/***************************************************************************
 *                                                                         *
 *   CRNUL.C                                                               *
 *                                                                         *
 *   Copyright (c) 1996-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   Encodes and Decodes Network Virtual Terminal's CR-NUL pairs, per      *
 *   RFC 854, the Telnet RFC.                                              *
 *                                                                         *
 *                                        - R. Stein and C. Dunn 2/15/96   *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "crnul.h"

#define FILREV "$Revision: 3 $"

VOID
crndinit(                          /* initialize crndec() session          */
struct crnul *crnp)                /*   session control block              */
                                   /*   setmem(,,0) acceptable alternative */
{
     ASSERT(FALSE == 0);           /*   (i.e. equivalent to setmem(,,0))   */
     crnp->crlast=FALSE;
}

UINT                               /*   ret # of decoded bytes (< nbytes)  */
crndec(                            /* decode CR-NUL pairs, per RFC 854     */
struct crnul *crnp,                /*   session control block for crndec() */
CHAR *block,                       /*   encoded bytes                      */
UINT nbytes)                       /*   number of bytes in encoded form    */
                                   /*   Note:  crndec(), if it is called   */
                                   /*   at all, should be called *AFTER*   */
                                   /*   iacdec() (i.e. CR-NUL encoding is  */
                                   /*   a higher level than IAC encoding). */
                                   /*   Otherwise, suboption negotiation   */
                                   /*   fields could be corrupted.         */
{
     UINT crpos,stsrch;

     if (nbytes == 0) {
          return(0);
     }
     if (crnp->crlast                   /* last block end with a CR?       */
      && block[0] == '\0') {            /* and this one starts with a NUL? */
          movmem(block+1,block,nbytes-1);
          nbytes--;                     /* remove NUL                      */
     }
     crnp->crlast=FALSE;
     stsrch=0;
     while (stsrch < nbytes) {          /* anything left to search?        */
          crpos=stsrch+scanch(block+stsrch,'\r',nbytes-stsrch);
          if (crpos >= nbytes) {
               break;                   /* no more CR's                    */
          }
          stsrch=crpos+1;               /* skip the CR                     */
          if (stsrch < nbytes) {        /* anything after the CR?          */
               if (block[stsrch] == '\0') {  /* CR-NUL pair detected       */
                    if (stsrch < nbytes-1) {
                         movmem(block+stsrch+1,block+stsrch,nbytes-stsrch-1);
                    }
                    nbytes--;           /* remove the NUL after the CR     */
               }
          }
          else {                        /* block ends with CR              */
               crnp->crlast=TRUE;
               break;
          }
     }
     return(nbytes);
}

UINT                               /*   returns number of converted bytes  */
crnenc(                            /* encode CR-NUL pairs, per RFC 854 NVT */
CHAR *block,                       /*   bytes, converted in-place          */
UINT nbytes,                       /*   original number of bytes           */
UINT maxbytes)                     /*   max converted bytes (eg nbytes*2)  */
                                   /*   crenc() will never grow a block to */
                                   /*   more than double its origional     */
                                   /*   size.  Therefore, the ideal value  */
                                   /*   for maxbytes is nbytes*2.          */
                                   /*   Moreover, crnenc() combined with   */
                                   /*   iacenc() will never grow a block   */
                                   /*   to more than double it's           */
                                   /*   pre-encoded "raw" form.            */
                                   /*   Note:  crnenc(), if it is called   */
                                   /*   at all, should be called *BEFORE*  */
                                   /*   iacenc() (i.e. CR-NUL encoding is  */
                                   /*   a higher level than IAC encoding). */
                                   /*   There is no penalty for violating  */
                                   /*   this rule, it's just consistent    */
                                   /*   with the rule for crndec().        */
{
     UINT crpos,stsrch;

     stsrch=0;
     while (stsrch < nbytes) {          /* anything left to search?        */
          crpos=stsrch+scanch(block+stsrch,'\r',nbytes-stsrch);
          if (crpos >= nbytes) {
               break;                   /* no more CR's                    */
          }
          stsrch=crpos+1;               /* skip the CR                     */
          if (stsrch < nbytes) {        /* anything after the CR?          */
               if (block[stsrch] != '\n' && nbytes < maxbytes) {
                    movmem(block+stsrch,block+stsrch+1,nbytes-stsrch);
                    block[stsrch]='\0'; /* insert NUL after CR             */
                    nbytes++;
               }
               stsrch++;                /* skip what's NOW after the CR    */
          }
          else {                        /* block ends with CR              */
               if (nbytes < maxbytes) {
                    block[nbytes]='\0'; /* insert precautionary NUL,       */
                    nbytes++;           /* taking some liberty with RFC854 */
               }
               break;
          }
     }
     return(nbytes);
}

/*
     The above "precautionary NUL" is not addressed specifically by
     RFC854 but we assume that CR-NUL-LF will be translated by an NVT
     into CR-LF.  If we didn't take this liberty, 3 problems would arise:
     (1) crnenc() would need a session control block to remember if one
     block ended in a CR so it could test the first byte of the next
     block for LF or not; (2) crnenc()+iacenc() could MORE than double
     the buffer (turning the single byte FF into 00 FF FF for example);
     (3) a special routine would be needed to declare "end of connection"
     so that a trailing CR could by NUL-padded.
*/
