/* CTERM1.C by Donald W. Smith. CIS 76515,3406. * A minimal terminal emulator to demonstrate the use of state * machine driven communications protocols using the C language. * Use makect1. to compile. */ #define VERSION fputs("\n\t CTERM 1.11: 4/26/89 DWS\n\n",stdout) #define BUFLEN 200 #define LINELEN 80 /* Max user input length. Lots of slack */ #include #include /* for Turbo C is... functions */ #include #include /* For system() call */ #include /* Ctrl-C and Ctrl-Break handling */ #include #include /* For system() call */ #include "cterm.h" /* defines for cterm series */ #include "commn.h" /* defines shared by myint and cterm */ #include "getargs.h" /* for getargs access */ #define CMDDIM(x) (sizeof(x)/sizeof(x[0])) /* --------- GLOBALS ------------------ */ enum modes mode = M_Cmd; /* Term, Config, etc. */ char inbuf[BUFLEN+1]; char outbuf[LINELEN+1]; /* Used for short output */ S_INIT cur_config = {1200}; /* Current port config */ int comport = 1; /* Current comm port number */ int bbsmode = 0; /* BBS (8,1,N) or T16 (7,1,E) */ int eschar = ESC; /* keyboard escape character */ FILE *cap_fptr; /* file ptr for capture file */ static union REGS rg; /* used for keyfun() */ /* ---------- External variables -------------- */ extern unsigned _heaplen = 4096; /* TurboC 1.5 and above */ /* ---------- External routines ----------- */ /* -- From myint -- */ extern void Config_Comm( int port, S_INIT conf ); extern S_INIT Get_Conf( int port ); extern int incomm(); extern int Inst_IH(void interrupt (*faddr)(), int comnum); extern int Remove_IH(); extern int writecomm(unsigned char *buf, int len); extern int xmit_break(); /* -- from xmutil -- */ extern int read_comm(int *num, char *buf, int wait); /* -- from object only files -- */ extern int getargs( int, char**, ARG *, int, int (*usage)() ); ARG Argtab[] = { { 'b', BOOLEAN, &bbsmode, "BBS mode (8,1,N) vs. T16)" }, { 'c', INTVAR, &comport, "1 = COM1, 2 = COM2" }, { 'e', INTVAR, &eschar, "Escape char (0x..)" }, { 's', INTVAR, (int *)&cur_config.speed, "speed in bps" } }; /* ----------- fgetsnn --------------------- * Does: gets one line from file, replacing \n with NULL terminator. * Returns: the number of characters stored, or EOF. * NOTE: If return is (size - 1), no \n was found before size * was reached. User may choose to eat or read the * rest with getchar(). */ int fgetsnn(FILE *fp, char *s, int size) { register int i; int c; for (i = 0; (i < size-1) && (c = fgetc(fp)) != EOF && (c != '\n') ; ++i) s[i] = c; s[i] = '\0'; if (c == EOF) return(EOF); return(i); } /* ----- capture_sw: Enables saving sessions to (PC) disc. ----- * Returns: 0 O.K. 1 if open fails, 2 if ESC hit. * First call: Asks for data file name (and remembers it). * Second call: Assumes you want to close it. * Third call: Offers last file name to re-open, or key in new. */ int capture_sw() { static char cap_fname[NAMESIZE + 1] = "capture.fil"; char cap_temp[NAMESIZE + 1]; static int cap_sw = 0; /* capture on/off switch */ if (cap_sw == 0) { /* Open the file for capture */ fprintf(stdout,"\n Capture to file <%s> or : ",cap_fname); fgetsnn (stdin, cap_temp, NAMESIZE ); if (cap_temp[0] == eschar) return(2); if (cap_temp[0] != '\0') strncpy(cap_fname, cap_temp, NAMESIZE); if ( (cap_fptr = fopen (cap_fname, "a+t")) == NULL ) { printf("\n Cannot open %s. Try again.\n", cap_fname); return(1); } cap_sw = 1; } else { /* we are already capturing. Close and get out */ fclose( cap_fptr ); cap_sw = 0; } return(0); } /* ----- keyfun: Use to call BIOS keyboard input services ------ * Use to call BIOS keyboard input services * Use instead of keypressed and bioskey to prevent DOS ^C hassles. * Pass: 1 - test for keyhit. * 0 - get extended char and scan code. * Modeled after Stevens 2/89 DDJ p. 140. */ int keyfun(int serv) { rg.h.ah = serv; int86(BIOS_KEY, &rg, &rg); if (serv == KEYHIT) return ((rg.x.flags & 0x40) == 0); else return (rg.x.ax); } /* --------- term() ----------------------------- * Emulates a dumb terminal. Scaffolding for xmodem routines. */ void term() { register int i; int keyin; /* Key = scan code + ASCII val */ char gochar; int redd; int ret_code, wait_ret; char *tail = inbuf; /* for tail of input buffer */ static int cap_flag = 0; /* Is capture turned on now? */ while (mode == M_Term) { redd = BUFLEN / 2; /* Go for half at a time. */ ret_code = read_comm (&redd, inbuf, 10); /* 10 msecs */ if ( (ret_code != 0) && (ret_code != TIMEOUT) ) fprintf (stderr, "read_comm error %x\n", ret_code ); if ( redd > 0 ) { /* Reading was productive */ tail[redd] = 0; /* plant a null */ for ( i = 0; i < redd; i++) { /* check for specials */ if ( isprint( gochar = inbuf[i] & 0x7F) || /* zero hi */ ( isspace(gochar) ) || /* CR,LF.. */ gochar == BS ) { putch(gochar); if (cap_flag) fputc(gochar, cap_fptr); } /* printable test */ } /* for loop */ } /* end if reading was productive */ if ( keyfun(KEYHIT) ) { keyin = keyfun(READNEXT); /* Retrieve Scan Code + Key */ gochar = keyin & 0xFF; /* truncates */ if (gochar == 0) { /* Function key or a special */ switch (keyin) { case PGUP: mode = M_XSend; xmodem_send(); mode = M_Term; break; case PGDN: mode = M_XRecv; xmodem_recv(); mode = M_Term; break; case CBRK: xmit_break(); break; case INS: if (capture_sw() == 0) cap_flag = !cap_flag; default: break; } } else { /* Some plain old ascii character came in */ if ( gochar != eschar ) { outbuf[0] = gochar; writecomm(outbuf, 1); } else /* ESCAPE entered */ mode = M_Cmd; /* leave terminal mode */ } } /* end if keypressed */ } /* while mode = M_Term */ return; } /* ------- off_to_dos --------------------------------- * Prompts for command to pass to dos * Pass: Nothing */ void off_to_dos() { char buf[LINELEN]; fputs("\nInput DOS Command (CR returns to menu)\nDOS>",stdout); while ( fgetsnn(stdin, buf, LINELEN) ) { /* > 1 means got 1 */ system(buf); fputs("\nDOS>",stdout); } } /* ------- config --------------------------------- * Prompts for new configuration (speed or default). * Pass: Nothing */ void config() { S_INIT work; char *cptr; char buf[LINELEN]; unsigned inval; int inlen; int i = 0; work = Get_Conf(comport); /* a struct assignment */ fputs("\n Current config shows:\n", stdout); printf("%5u, parity %s, %s stops, %d bits/char.\n", work.speed, PARITY_LIST[work.ubits.lbits.parity], STOP_LIST[work.ubits.lbits.two_stops], work.ubits.lbits.wlen + 5); fputs("0 = T16 ( 7, 1, Even )\n", stdout); fputs("1 = BBS ( 8, 1, None )\n", stdout); fputs("other = new speed value\n", stdout); inlen = fgetsnn(stdin, buf, LINELEN); /* Got one parameter */ if (inlen > 0) { inval = atoi(buf); if (inval == 0) { work.ubits.lctrl = sev1even; /* 7,1,EVEN */ bbsmode = 0; } if (inval == 1) { work.ubits.lctrl = ate1none; /* 8,1,NONE */ bbsmode = 1; } if (inval > 1) { while ( SPEED_VALS[i] && SPEED_VALS[i] != inval ) i++; if (SPEED_VALS[i] == 0) printf("\n Speed %d unavailable.\n",inval); else { /* found a valid new speed */ work.speed = inval; } } Config_Comm(comport, work); cur_config = work; /* Publish the results */ } else fputs("\n Exiting Config mode.\n",stdout); if ( inlen == (LINELEN - 1) ) while (fgetc(stdin) != EOF) /* Purge garbage in buffer */ ; mode = M_Cmd; } /* -------- prompt_wait -------------- * Pass: A pointer to the prompt string to be output. * A pointer to the command array. * The number of elements in the array. * The default (or help) index. * Returns: The (int) index number of the command entered. * NOTE: May need to be modified to remove command and return * the remainder of the command line. */ prompt_wait ( char *prompt, char *cmds[], int n, int help ) { char buffer[LINELEN]; /* used by fgetsnn */ int i = 0; while (i == 0) { /* Don't bite on CR only input */ printf("\n%s",prompt); i = fgetsnn(stdin, buffer, LINELEN ); } if ( i == (LINELEN - 1) ) while (fgetc(stdin) != EOF) /* Purge garbage in buffer */ ; for ( i = 0 ; i < n ; i++ ) if ( *cmds[i] == toupper(buffer[0]) ) /* Match first char */ return(i); return (help); /* not found... return help default */ } /* ----- main_help: Shows canned help message --------------- */ void main_help() { static char *details[] = { "Config : Set up comm parameters.\n", "Dos, : Calls DOS with commands.\n", "Help, : See this help info.\n", "Quit : Exit program.\n", "Rcvx, : Receive file using Xmodem.\n", "Sendx, : Send file using Xmodem.\n", "Term, : Dumb terminal mode.\n" }; register int i; fputs("\n Valid commands are:\n",stdout); for (i = 0 ; i < CMDDIM(details) ; i++) printf("%s",details[i]); } /* ----- main_menu: Prompts for input, dispatches if valid -- */ void main_menu() { static char *prompt = "CTERM>"; static char *maincmds[] = { "CONFIG", "DOS", "HELP", /* used for default below (index 2 ) */ "QUIT", "RCVX", "SENDX", "TERM" }; while (mode == M_Cmd) { switch ( prompt_wait(prompt, maincmds, CMDDIM(maincmds), 2 )) { case 0: mode = M_Config; config(); break; case 1: off_to_dos(); break; case 2: main_help(); break; case 3: printf("\n *** Closing Comm port %d.", comport); close_comm(); exit(1); case 4: mode = M_XRecv; xmodem_recv(); fputs("\nReturned from xmodem recv!\n",stdout); break; case 5: mode = M_XSend; xmodem_send(); fputs("\nReturned from xmodem send!\n",stdout); break; case 6: mode = M_Term; eat_noise(); term(); break; default: main_help(); } /* end switch */ } /* end while */ } /* -------- Catch_23 ----------------------------- * Does: Traps Ctrl-C and Ctrl-Break keys during config and user I/O. * Should not be invoked if we are in terminal mode. */ void Catch_23() { signal(SIGINT, Catch_23); /* Re-install self each time */ return; } /* -------- Catch_24 ----------------------------- * Does: Traps Disk and other hardware errors such as Abort, Retry, Fail? */ int Catch_24(int errval, int ax) { char msg[25]; int drive; if (ax < 0) { /* device error */ bdosptr( 0x09, "device error$", 0); hardretn(-1); } drive = (ax & 0x00FF); sprintf(msg, "disk error %d on drive %c $", errval, 'A' + drive); bdosptr( 0x09, msg, 0); hardretn(2); } /* ----- usage: Give user quick help before exit. ----------- */ usage() { printf("\n Defaults: T16, 1200 bps, 8,1,NONE, COM1, ESC.\n"); } /* ----- main: Gets the ball rolling! ---------------------- */ main( int argc, char *argv[] ) { int error; VERSION; signal(SIGINT, Catch_23); harderr(Catch_24); argc = getargs( argc, argv, Argtab, CMDDIM(Argtab), usage ); error = init_comm(comport, bbsmode); if (error != 0) { fprintf(stderr,"\n *** Comm Port %d Init FAILED!",comport); return(2); } fprintf(stderr,"\n *** Comm Port %d Init O.K. *** ", comport); main_menu(); return(1); }