/***************************************************************************
 *                                                                         *
 *   TNO.C                                                                 *
 *                                                                         *
 *   Copyright (c) 1994-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   General-purpose Telnet option decoding.                               *
 *                                                                         *
 *                                        - RNStein  5/31/94               *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "tno.h"

#define FILREV "$Revision: 7 $"

CHAR *tntcmdlist[]={               /* telnet command characters (see TNO.H)*/
     "EOR",
     "SE",
     "NOP",
     "DMARK",
     "BRK",
     "IP",
     "AO",
     "AYT",
     "EC",
     "EL",
     "GA",
     "SB",
     "WILL",
     "WONT",
     "DO",
     "DONT",
     "IAC",
};

#ifndef UNIX
#if (sizeof(tntcmdlist)/sizeof(char *)) != (TNTCMD_LAST-TNTCMD_FIRST)+1
#error Telnet command list out of sync in TNO.H and TNO.C
#endif
#endif

CHAR *tntoptlist[]={               /* telnet option name list (see TNO.H)  */
     "BINARY",
     "ECHO",
     "RCP",
     "SUPPRESS GO AHEAD",
     "NAME",
     "STATUS",
     "TIMING MARK",
     "RCTE",
     "NAOL",
     "NAOP",
     "NAOCRD",
     "NAOHTS",
     "NAOHTD",
     "NAOFFD",
     "NAOVTS",
     "NAOVTD",
     "NAOLFD",
     "EXTEND ASCII",
     "LOGOUT",
     "BYTE MACRO",
     "DATA ENTRY TERMINAL",
     "SUPDUP",
     "SUPDUP OUTPUT",
     "SEND LOCATION",
     "TERMINAL TYPE",
     "END OF RECORD",
};

#ifndef UNIX
#if (sizeof(tntoptlist)/sizeof(char *)) != (TNTOPT_LAST-TNTOPT_FIRST)+1
#error Telnet option list out of sync in TNO.H and TNO.C
#endif
#endif

struct tnoscb *tnoscb;        /* global variable for tnoini() and tnohose()*/

VOID
tnoini(                       /* initialize Telnet Option session (tnoscb) */
INT maxstg,                   /* max bytes to accumulate in stg[] (0-255)  */
INT (*tnoxit)(                /* Telnet option exit point handler          */
     INT code,                /* see TNCXXXX codes in TNO.H                */
     INT parm))               /* see TNCXXXX codes in TNO.H                */
{
     tnoscb->maxstg=maxstg;
     tnoscb->tnoxit=tnoxit;
     tnoscb->state=TNOQSC;
     tnoscb->flags=0;
}

int
tnohose(                      /* Telnet Option byte hose (tnoscb implicit) */
CHAR ch)                      /* incoming character from other NVT party   */
                              /* returns 00-FF character or -1=nothing yet */
                              /* may call your exit point as a side effect */
                              /* Note:  whatever context you need for your */
                              /* exit points, be sure to set up before you */
                              /* call tnohose().                           */
{
     INT rc=-1;

     switch (tnoscb->state) {
     default:                 /* (recovery precaution)                     */
          tnoscb->state=TNOQSC;
     case TNOQSC:             /* quiescent (searching for an IAC)          */
          if (ch == IAC) {
               tnoscb->state=TNOIAC;
          }
          else {
               rc=ch;
          }
          break;
     case TNOIAC:             /* received IAC                              */
          switch (ch) {
          case WILL:
          case WONT:
          case DO:
          case DONT:
          case SB:
               tnoscb->state=ch;
               break;
          case IAC:
               tnoscb->state=TNOQSC;
               rc=IAC;
               break;
          default:
               tnoscb->state=TNOQSC;
               rc=tnoscb->tnoxit(TNCOTHR,ch);
               break;
          }
          break;
     case TNOWIL:             /* received IAC,WILL                         */
     case TNOWNT:             /* received IAC,WONT                         */
     case TNODOO:             /* received IAC,DO                           */
     case TNODNT:             /* received IAC,DONT                         */
          tnoscb->opid=ch;
          rc=tnoscb->tnoxit((tnoscb->state+1)&0xFE,(tnoscb->state&1));
          tnoscb->state=TNOQSC;
          break;
     case TNOISB:             /* received IAC,SB                           */
          tnoscb->opid=ch;
          tnoscb->state=TNOISO;
          tnoscb->stgcnt=0;
          break;
     case TNOISO:             /* received IAC,SB,OPT,...                   */
          if (ch == IAC) {
               tnoscb->state=TNOSOI;
          }
          else {
               if (tnoscb->stgcnt < tnoscb->maxstg-1) {
                    tnoscb->stg[tnoscb->stgcnt++]=ch;
               }
          }
          break;
     case TNOSOI:             /* recd IAC,SB,OPT,...,IAC (expect SE or IAC)*/
          switch (ch) {
          case SE:
               tnoscb->state=TNOQSC;
               if (tnoscb->stgcnt < tnoscb->maxstg) {
                    tnoscb->stg[tnoscb->stgcnt]='\0';
               }
               rc=tnoscb->tnoxit(TNCSBSE,0);
               break;
          default:
               if (tnoscb->stgcnt < tnoscb->maxstg-1) {
                    tnoscb->stg[tnoscb->stgcnt++]=IAC;
               }
               tnoscb->state=TNOISO;
               break;
          }
          break;
     }
     return(rc);
}

INT
tnoyes(                            /* prefer YES resp to TNCWIWO or TNCDODO*/
INT mask)                          /* flag mask, application-specific      */
                                   /* returns 1=can, 0=can't               */
                                   /* meaning of app-specific flag bit:    */
                                   /* 1=yes proposed and accepted (either  */
                                   /*   peer proposed and we accepted, or  */
                                   /*   we proposed and peer accepted)     */
                                   /* 0=no (either we proposed and peer    */
                                   /*   denied, or peer refused and we     */
                                   /*   accepted)                          */
{
     INT old,curr;

     old=((tnoscb->flags&mask) != 0);
     curr=((tnoscb->state&1) != 0);
     if (old != curr) {
          tnoscb->tnoxit(TNCSEND,((tnoscb->state+1)^2)-1);
          tnoscb->flags^=mask;     /* flag means:  peer told yes/no        */
     }
     return(curr);
}

VOID
tnono(                             /* demand NO resp to TNCWIWO or TNCDODO */
INT mask)                          /* 0=ignore WONTs/DONTs, mask=ack first */
                                   /* one only, always rejects WILLs/DOs   */
                                   /* meaning of app-specific flag bit:    */
                                   /* 1=peer has been told no (either we   */
                                   /*   rejected his proposal, or accepted */
                                   /*   peer's refusal)                    */
                                   /* 0=peer hasn't been notified yet of   */
                                   /*   our refusal/denial                 */
{
     if ((tnoscb->state&1) || (mask != 0 && !(tnoscb->flags&mask))) {
          tnoscb->tnoxit(TNCSEND,((tnoscb->state+1)^2)&~1);
          tnoscb->flags|=mask;     /* flag means:  peer's no answered/not  */
     }
}
