home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / mm / mm-ccmd-0.91-20031009.tar.gz / mm-ccmd-0.91-20031009.tar / work / ccmd / ccmdmd.c < prev    next >
C/C++ Source or Header  |  2002-09-17  |  28KB  |  1,041 lines

  1. /*
  2.  Copyright (c) 1986, 2002 by The Trustees of Columbia University in
  3.  the City of New York.  Permission is granted to any individual or
  4.  institution to use, copy, or redistribute this software so long as it
  5.  is not sold for profit, provided this copyright notice is retained.
  6.  
  7.  Author: Andrew Lowry
  8. */
  9.  
  10. /* Machine dependent code for Unix systems -- Use preprocessor
  11. ** conditionals for peculiarities of particular systems, but
  12. ** PLEASE -- Avoid nesting preprocessor conditionals!
  13. **/
  14.  
  15. #include "ccmdlib.h"
  16. #include "cmfncs.h"            /* and internal symbols */
  17.  
  18. #include <string.h>            /* For index etc */
  19. #include <errno.h>            /* For errno */
  20.  
  21. /* Terminal driver defs */
  22.  
  23. #if HAVE_TERMIOS
  24. # include <termios.h>
  25. #elif HAVE_TERMIO
  26. # include <sys/termio.h>
  27. #else
  28. # include <sgtty.h>
  29. #endif
  30.  
  31. /* Signal handling defs, very system-dependent */
  32.  
  33. #include <signal.h>
  34.  
  35. /*
  36.  * On some systems, signal handlers are defined as returning (void).
  37.  */
  38. #if CCMD_OS_BSD44
  39. #  define ccmdsigval_t sig_t
  40. #  define SIG void
  41. #elif CCMD_OS_LINUX
  42.     typedef void (*ccmdsigval_t)(int);
  43. #  define SIG void
  44. #elif HAVE_VOIDSIG
  45.     typedef void (*ccmdsigval_t)();
  46. #  define SIG void
  47. #else
  48.     typedef int  (*ccmdsigval_t)();
  49. #  define SIG int
  50. #endif
  51.  
  52. /*
  53.  * Must now allow for signal sets
  54.  */
  55. #if HAVE_SIGSETS
  56. #  define ccmdsigset_t sigset_t
  57. #  define ccmdsigemptyset(set) sigemptyset(set)
  58. #  define ccmdsigfillset(set)  sigfillset(set)
  59. #  define ccmdsigaddset(set,s) sigaddset(set,s)
  60. #  define ccmdsigdelset(set,s) sigdelset(set,s)
  61. #  define ccmdsigismember(set,s) sigismember(set,s)
  62. #else
  63. #  ifndef sigmask
  64. #    define sigmask(m) (1 << ((m) - 1))
  65. #  endif
  66. #  define ccmdsigset_t unsigned long
  67. #  define ccmdsigemptyset(set) (*(set) = 0)
  68. #  define ccmdsigfillset(set)  (*(set) = ~0L)
  69. #  define ccmdsigaddset(set,s) (*(set) |= sigmask(s))
  70. #  define ccmdsigdelset(set,s) (*(set) &= ~sigmask(s))
  71. #  define ccmdsigismember(set,s) (*(set) & sigmask(s))
  72. #endif
  73.  
  74.  
  75. /* Local forward decls */
  76.  
  77. static char *strindex(char *src, char *str);
  78. static char *gttype(int fd);
  79. static raw(int fd);
  80. static unraw(int fd);
  81. PASSEDSTATIC int outc(int c);
  82. static void intson(void);
  83. static void intsoff(void);
  84.  
  85.  
  86. /* cmrpjmp
  87. **
  88. ** Purpose:
  89. **   Automatic reparse handler, installed via the cmsetrp macro from
  90. **   ccmd.h.  If this handler is installed in a CSB for which a reparse
  91. **   is needed, it will perform a longjmp to restart execution at the
  92. **   point following the installing cmsetrp invocation.  This point
  93. **   should be following the call to cmini that set up parsing for
  94. **   the current command line, and before the comnd call for the first
  95. **   field in the command.
  96. **
  97. ** Input arguments: None
  98. ** Output arguments: None
  99. ** Returns: Nothing
  100. **/
  101.  
  102. jmp_buf cmrpjb;            /* global jump buffer for autoreparse */
  103.  
  104. cmrpjmp()
  105. {
  106.   longjmp(cmrpjb,1);        /* do the jump */
  107.   return(CMxNOAR);        /* if it returns, it failed */
  108. }
  109.  
  110.  
  111.  
  112. /* cmerjmp, cmerjnp
  113. **
  114. ** Purpose:
  115. **   Automatic parse error handler, much like the automatic reparse
  116. **   handler described above.  The cmseter macro should be invoked
  117. **   just prior to issuing a prompt.  When a parsing error
  118. **   subsequently occurs (that is, the parse function is about to
  119. **   return anything but CMxOK), cmperr will be called to print the
  120. **   error, and then execution will jump back to the site of the macro
  121. **   invocation.  When the automatic error handler is installed, the
  122. **   user program can ignore the codes returned by parse, since they
  123. **   will always be CMxOK.  CSB field _cmerr may be examined to see
  124. **   whether the the prior command line was terminated by an error,
  125. **   and if so, which error.
  126. **
  127. **   Note: Reparse situations will be handled by the error handler if
  128. **   no reparse handler has been installed.
  129. **
  130. **   Cmerjnp is the same as cmerjmp, except that the error message is
  131. **   not printed.
  132. **
  133. ** Input arguments: None.
  134. ** Output arguments: None.
  135. ** Returns: Nothing.
  136. **/
  137.  
  138. jmp_buf cmerjb;                /* global jump buffer */
  139.  
  140. cmerjmp(ret,str,flags)
  141. int ret;                /* code that triggered the handler */
  142. char *str;
  143. int flags;
  144. {
  145.   if (str) return(cmermsg(str,flags));
  146.   cmperr(ret,flags);                /* issue error message */
  147.   longjmp(cmerjb,1);            /* take the jump */
  148.   return(CMxNOAE);            /* failed */
  149. }
  150.  
  151. cmermsg(str,flags)
  152. char *str;                /* error msg */
  153. int flags;
  154. {
  155.   cmpemsg(str,flags);            /* issue error message */
  156.   longjmp(cmerjb,1);            /* take the jump */
  157.   return(CMxNOAE);            /* failed */
  158. }
  159.  
  160. cmerjnp(ret)
  161. int ret;                /* code that triggered the handler */
  162. {
  163.   longjmp(cmerjb,1);            /* take the jump */
  164.   return(CMxNOAE);            /* failed */
  165. }
  166.  
  167.  
  168. /* KLH: This appears to help implement cmsetbl() but is only
  169.    defined for BSD and is never used by MM.
  170. */
  171.  
  172. #ifdef CCMD_OS_BSD
  173. /*
  174.  * if handling nonblocking I/O, and a EWOULDBLOCK error comes up
  175.  * Then jump to the point set with a cmsetbl() call.
  176.  * The user then could wait for input.
  177.  */
  178.  
  179. jmp_buf cmbljb;                /* global jump buffer */
  180.  
  181. cmbljmp(ret)
  182. int ret;                /* code that triggered the handler */
  183. {
  184.   if (errno != EWOULDBLOCK)
  185.       return(ret);
  186.   longjmp(cmbljb,1);            /* take the jump */
  187.   return(CMxNOAE);            /* failed */
  188. }
  189. #endif
  190.  
  191.  
  192. /*
  193. ** Machine-dependent IO routines...  Generally, a file descriptor is
  194. ** supplied as an argument to the calls.
  195. **/
  196.  
  197. static int autowr;            /* TRUE if automatic wrap at eol */
  198. static int li;                /* lines on screen */
  199. static char tcapent[1024];        /* complete termcap entry */
  200. static char tcarea[100];        /* decoded termcap entries */
  201. static char *nl = "\n";
  202. static char *cr = "\r";
  203. static char *cl,*ce,*up;        /* pointers to decoded entries */
  204. static char *le,*nd,*down;
  205.  
  206. char *tgetstr();            /* termlib routine returns string */
  207.  
  208. /* cmgetc - get a character from the input source.  Return standard return
  209.  * code. 
  210. */
  211.  
  212. int cmgetc(c,fd)
  213. int *c;                    /* pointer where char is placed */
  214. FILE * fd;                /* input file descriptor */
  215. {
  216.   int cc;                /* value from read */
  217.  
  218.   if (cmcsb._cmoj != NULL)
  219.     cmflsh(cmcsb._cmoj);        /* flush pending output */
  220.   if (fd == NULL)            /* no file descriptor.  EOF */
  221.     return(CMxEOF);
  222. #if 0 /* KLH: yeech.  What to replace with? */
  223.   if (fd->_cnt == 0 && (cmcsb._cmflg & CM_ITTY))
  224.     cmselect(fileno(fd));
  225. #endif
  226.   *c = cc = getc(fd);
  227.   if (cc == EOF)
  228.     return(CMxEOF);            /* end of file */
  229.   else
  230.     return(CMxOK);            /* good read */
  231. }
  232.  
  233. /* cmputc - Output a single character to the terminal */
  234.  
  235. cmputc(c,fd)
  236. char c;                    /* char to output */
  237. FILE * fd;                /* output filedesc */
  238. {
  239.  
  240.   if (fd != NULL) {
  241.     putc(c,fd);
  242.     if (c == '\n') 
  243.       cmflsh(fd);
  244.   }
  245. }
  246.  
  247. /* cmputs - Output null-terminated string to the terminal */
  248.  
  249. cmputs(s,fd)
  250. char *s;                /* output string */
  251. FILE *fd;                /* output filedesc */
  252. {
  253.   while(*s != '\0')
  254.     cmputc(*s++,fd);
  255. }
  256.  
  257. /* cmcr - Move to the beginning of the current line */
  258.  
  259. cmcr(fd)
  260. FILE * fd;                /* output filedesc */
  261. {
  262.   cmputs(cr,fd);            /* use term specific sequence */
  263. }
  264.  
  265. /* cmnl - Output a newline sequence to the comman stream */
  266.  
  267. cmnl(fd)
  268. FILE *fd;                /* output filedesc */
  269. {
  270.   cmputs(nl,fd);            /* use term-specific sequence */
  271. }
  272.  
  273. /* cmflsh - flush output on fd */
  274. cmflsh(fd)
  275. FILE *fd;
  276. {
  277.   if (fd != NULL)
  278.     fflush(fd);
  279. }
  280.  
  281. /* cmwrap - Make sure that cursor wraps when it is required */
  282.  
  283. cmwrap(fd)
  284. FILE * fd;                /* output filedesc */
  285. {
  286.   if (!autowr)
  287.     cmnl(fd);                /* newline if not automatic */
  288. }
  289.  
  290. /* cmcls - Clear the screen.  Current IOJ value in the CSB is used for
  291. ** character output.  Only invoked if that IOJ is for a CRT terminal.
  292. ** Returns TRUE iff the operation succeeds.
  293. **/
  294.  
  295. int
  296. cmcls()
  297. {
  298.   if (cl == NULL)
  299.     return(FALSE);            /* no clear screen sequence */
  300.   else {
  301.     tputs(cl,li,outc);            /* output the clear sequence */
  302.     if (cmcsb._cmoj)
  303.     fflush(cmcsb._cmoj);
  304.     return(TRUE);
  305.   }
  306. }
  307.  
  308. /* cmceol - Clear to end of line.  Current IOJ value in the CSB is
  309. ** used for character output.  Only invoked if that IOJ is for a CRT
  310. ** terminal.  Returns TRUE iff the operation succeeds.
  311. **/
  312.  
  313. cmceol()
  314. {
  315.   if (ce == NULL)
  316.     return(FALSE);            /* no ceol sequence */
  317.   else {
  318.     tputs(ce,1,outc);            /* else do the operation */
  319.     return(TRUE);
  320.   }
  321. }
  322.  
  323. /* cmupl - Moves up on line in the display without changing column
  324. ** position.  Should not wrap to bottom of screen or cause destructive
  325. ** downward scrolling.  Current IOJ value in the CSB is used for
  326. ** character output.  Only invoked if that IOJ is for a CRT terminal.
  327. ** Returns TRUE if the operation succeeds.
  328. */
  329.  
  330. cmupl()
  331. {
  332.   if (up == NULL)
  333.     return(FALSE);            /* no upline sequence */
  334.   else {
  335.     tputs(up,1,outc);            /* else do the operation */
  336.     return(TRUE);
  337.   }
  338. }
  339.  
  340. cmdownl()
  341. {
  342.   int oldcrmod;
  343.   if (down == NULL)
  344.     return(FALSE);            /* no upline sequence */
  345.   else {
  346.     if (cmcsb._cmoj) {
  347.     oldcrmod = crmod(FALSE);    
  348.     tputs(down,1,outc);            /* else do the operation */
  349.     if (cmcsb._cmoj)
  350.         fflush(cmcsb._cmoj);
  351.     crmod(oldcrmod);
  352.     return(TRUE);
  353.     }
  354.   }
  355. }
  356.  
  357. cmleft()
  358. {
  359.   if (le == NULL)
  360.     return(FALSE);            /* no move left sequence */
  361.   else {
  362.     tputs(le,1,outc);            /* else do the operation */
  363.     cmcsb._cmcol--;
  364.     return(TRUE);
  365.   }
  366. }
  367.  
  368. cmright()
  369. {
  370.   if (nd == NULL)
  371.     return(FALSE);            /* no move right sequence */
  372.   else {
  373.     tputs(nd,1,outc);            /* else do the operation */
  374.     cmcsb._cmcol++;
  375.     return(TRUE);
  376.   }
  377. }
  378.  
  379. /* cmcpos - Returns the current column position on the display.
  380. ** We just assume the ccmd package is correct, since there's
  381. ** no facility in termlib for extracting column position, and
  382. ** the price for never knowing the cursor position is too high
  383. ** (really ugly user interface due to many blank lines).
  384. **/
  385.  
  386. int
  387. cmcpos()
  388. {
  389.   return(cmcsb._cmcol);
  390. }
  391.  
  392. /* cmflush - Flush all pending input on the input source */
  393.  
  394. cmflush(fd)
  395. FILE *fd;
  396. {
  397.   if (fd != NULL) {
  398.     if (isatty(fileno(fd))) {        /* if it's a terminal */
  399. #if HAVE_TERMIOS
  400.       tcflush(fileno(fd), TCIOFLUSH);
  401. #else
  402. # if HAVE_TERMIO && !defined(TIOCFLUSH)
  403. #  define TIOCFLUSH TCFLSH
  404. # endif
  405.       ioctl(fileno(fd),TIOCFLUSH,NULL);    /* flush input and output */
  406. #endif
  407.     }
  408. #if 0 /* KLH: yeech!!! */
  409.     fd->_cnt = 0;            /* flush stdio IOBUF. */
  410. #elif HAVE_FPURGE
  411.     fpurge(fd);                /* Reset stdio buffer */
  412. #endif
  413.   }
  414. }
  415.  
  416. /* cmtset - Initialize the source terminal, as currently set in the
  417. ** CSB.  If the file is a terminal, a termcap entry is obtained and
  418. ** examined to see whether or not it is a hardcopy terminal.  If not,
  419. ** various control strings are read from the termcap entry and saved
  420. ** for screen operations.  In any case, terminals are placed in cbreak
  421. ** mode without echoing, and INT and TSTP signals are caught to
  422. ** prevent the terminal remaining in a funny state upon exit, and to
  423. ** place it back into the required state upon continuation.
  424. **/
  425.  
  426. cmtset()
  427. {
  428.   int ofd, ifd;
  429.   char *areap = tcarea;            /* pointer to termcap decoding area */
  430.   char *ttype;                /* terminal type */
  431.   int tret;
  432.  
  433.   if (cmcsb._cmoj != NULL)
  434.     ofd = fileno(cmcsb._cmoj);        /* input file descriptor */
  435.   if (cmcsb._cmij != NULL)
  436.     ifd = fileno(cmcsb._cmij);        /* output file designator */
  437.  
  438.   cmcsb._cmflg &= ~CM_TTY;
  439.   if (cmcsb._cmoj != NULL && isatty(ofd)) { /* check if it is a terminal */
  440.     cmcsb._cmflg |= CM_OTTY;        /* yup */
  441.     ttype = gttype(ofd);        /* get the terminal type name */
  442.     if (tcapent[0] == '\0')
  443.       tret = tgetent(tcapent,ttype);    /* get termcap entry */
  444.     else
  445.       tret = 1;
  446.     if (tret != 1) {
  447.       cmcsb._cmflg2 &= ~CM_CRT;    /* no luck... assume hardcopy */
  448.       cmcsb._cmcmx = 79;        /* use default max column */
  449.       cmcsb._cmwrp = 79;        /* and wrap column */
  450.       nl = "\n";            /* and set default newline */
  451.       cr = "\r";            /* and return sequences */
  452.     }
  453.     else if (cmcsb._cmoj != NULL) {
  454.       if (tgetflag("hc"))         /* hardcopy indicated? */
  455.     cmcsb._cmflg2 &= ~CM_CRT;    /* yup, note it */
  456.       else {
  457.     cmcsb._cmflg2 |= CM_CRT;    /* else flag a crt */
  458.       }
  459.       cmtsize();            /* setup term size & termcap vars */
  460.       cmcsb._cmwrp = cmcsb._cmcmx;    /* set up autowrap column */
  461.     }
  462.     if (cmcsb._cmij != NULL && isatty(ifd)) {
  463.       cmcsb._cmflg |= CM_ITTY;        /* yup */
  464.       raw(ifd);                /* set up the terminal properly */
  465.       intson();                /* install our interrupt handlers */
  466.     }
  467.   }
  468.   else {
  469.     if (cmcsb._cmij != NULL && isatty(ifd)) {
  470.       raw(ifd);                /* set up the terminal properly */
  471.       intson();                /* install our interrupt handlers */
  472.     }
  473.     else {
  474.       cmcsb._cmflg &= ~CM_TTY;        /* not a tty, so not a crt either */
  475.       cmcsb._cmflg2 &= ~CM_CRT;
  476.       cmcsb._cmcmx = 79;        /* and just use default width */
  477.     }
  478.   }
  479. }
  480.  
  481. /* cmtend - Clean up after prior input source */
  482.  
  483. cmtend()
  484. {
  485.   int fd;
  486.  
  487.   if (cmcsb._cmij != NULL) {
  488.     fd = fileno(cmcsb._cmij);        /* file desc to shut down */
  489.  
  490.     if (cmcsb._cmflg & CM_TTY) {
  491.       unraw(fd);            /* reset former tty params */
  492.       intsoff();            /* remove our interrupt handlers */
  493.     }
  494.   }
  495. }
  496.  
  497. /*
  498.  * cmtsize()
  499.  * set up terminal size.  try using TIOCGWINSZ if possible.  If not set,
  500.  * use termcap entry.  If ioctl() succeeds, fix up termcap entry, so inferior
  501.  * processes inherit correctly.
  502.  */
  503. cmtsize()
  504. {
  505.   int li = -1, co = -1; 
  506. #ifdef TIOCGWINSZ
  507.   struct winsize w;
  508.  
  509.   if (ioctl(fileno(cmcsb._cmoj), TIOCGWINSZ, &w) == 0) {
  510.     if (w.ws_col > 0 && cmcsb._cmcmx != w.ws_col) {
  511.       co = cmcsb._cmcmx = w.ws_col;
  512.     }
  513.     if (w.ws_row > 0 && cmcsb._cmrmx != w.ws_row) {
  514.       li = cmcsb._cmrmx = w.ws_row;
  515.     }
  516.   }
  517. #else /* TIOCGWINSZ */
  518. #ifdef TIOCGSIZE
  519.   struct ttysize t;
  520.  
  521.   if (ioctl(fileno(cmcsb._cmoj), TIOCGSIZE, &t) == 0) {
  522.     if (t.ts_col > 0 && cmcsb._cmcmx != t.ts_col) {
  523.       co = cmcsb._cmcmx = t.ts_col;
  524.     }
  525.     if (t.ts_row > 0 && cmcsb._cmrmx != t.ts_row) {
  526.       li = cmcsb._cmrmx = t.ts_row;
  527.     }
  528.   }
  529. #endif /* TIOCGSIZE */
  530. #endif /* !TIOCGWINSZ */
  531.   if (co <= 0)
  532.     cmcsb._cmcmx = tgetnum("co");    /* get col count */
  533.   if (cmcsb._cmcmx <= 0)
  534.     cmcsb._cmcmx = 79;            /* use default if not given */
  535.   else
  536.     cmcsb._cmcmx--;            /* else drop to max col position */
  537.   if (li <= 0)                /*  */
  538.     cmcsb._cmrmx = tgetnum("li");    /* get row count */
  539.   if (cmcsb._cmrmx == -1)
  540.     cmcsb._cmrmx = 24;            /* or use default if necesary */
  541.   li = --cmcsb._cmrmx;
  542.   tc_setsize(cmcsb._cmrmx+1,cmcsb._cmcmx+1);
  543. }
  544.  
  545. /*
  546.  * set the "li" and "co" entries in the termcap entry
  547.  */
  548. tc_setsize(li, co) {
  549.   char buf[1024], *p1 = NULL, *p2=NULL, *p3=NULL, *p4=NULL;
  550.  
  551.   p1 = strindex(tcapent,"co#");        /* find num cols */
  552.   if (p1) {
  553.     p2 = index(p1+1,':');        /* and the end of it. */
  554.   }
  555.   p3 = strindex(tcapent,"li#");        /* find num lines */
  556.   if (p3) {
  557.     p4 = index(p3+1,':');        /* and the end of it */
  558.   }
  559.   if (p3)
  560.     *p3 = '\0';
  561.   if (p1) 
  562.     *p1 = '\0';
  563.   if (p2) 
  564.     *(p2++) == '\0';
  565.   else p2 = "";
  566.   if (p4) 
  567.     *(p4++) == '\0';
  568.   else
  569.     p4 = "";
  570.                     /* build new termcap string */
  571.   sprintf(buf,"%sco#%d:%sli#%d:%s", tcapent, co, p2, li, p4);
  572.   strcpy(tcapent, buf);
  573.   setenv("TERMCAP", tcapent, TRUE);    /* put it in the environment. */
  574.   tc_setents(tcarea);
  575. }  
  576.  
  577. tc_setents(areap) 
  578. char *areap;
  579. {
  580.   if (cmcsb._cmflg2 & CM_CRT) {
  581.     cl = tgetstr("cl",&areap);        /* get clear screen sequence */
  582.     ce = tgetstr("ce",&areap);        /* and clear end-of-line */
  583.     up = tgetstr("up",&areap);        /* and upline sequence */
  584.     le = tgetstr("le",&areap);        /* move left */
  585.     nd = tgetstr("nd",&areap);        /* move right */
  586.     down = tgetstr("do",&areap);    /* move down */
  587.     if (le == NULL)
  588.         le = "\b";            /* default if not specified */
  589.   }
  590.   nl = tgetstr("nl",&areap);        /* alwasy get newline sequence */
  591.   if (nl == NULL)
  592.     nl = "\n";                /* default if not specified */
  593.   cr = tgetstr("cr",&areap);        /* get return sequence */
  594.   if (cr == NULL)
  595.     cr = "\r";                /* or set default */
  596.   autowr = tgetflag("am");        /* check for autowrap */
  597.   li = cmcsb._cmrmx;
  598. }
  599.  
  600. /*
  601.  * search for an embedded string
  602.  */
  603. static char *
  604. strindex(char *src, char *str)
  605. {
  606.   char *cp = src, *cp1;
  607.  
  608.   while(cp1 = index(cp, *str)) {
  609.     if (strncmp(cp1, str, strlen(str)) == 0) {
  610.       return(cp1);
  611.     }
  612.     cp = cp1+1;
  613.   }
  614.   return(NULL);
  615. }
  616.  
  617. /* gttype - Return terminal type name for given tty file descriptor 
  618. ** Auxiliary routine for cmtset
  619. **/
  620.  
  621. static char *
  622. gttype(int fd)
  623. {
  624.   char *type;                /* type name of terminal */
  625.   char *name;                /* terminal name */
  626.   char *cname;                /* controlling terminal name */
  627.   int cttyfd;                /* controlling terminal file desc */
  628.   int ctty;                /* TRUE if fd is controlling tty */
  629.   FILE *typedb;                /* stream for ttytype database */
  630.   static char typelin[80];        /* line from ttytype database */
  631.   char *typecp;                /* pointer into type db entry */
  632.   extern char *ttyname(), *getenv();
  633.  
  634.   cttyfd = open("/dev/tty",O_RDWR,0);    /* open the controlling tty */
  635.   if (cttyfd < 0)
  636.     ctty = FALSE;            /* bad open - assume not ctty */
  637.   else {
  638.     name = ttyname(fd);            /* get the terminal name */
  639.     cname = ttyname(cttyfd);        /* and controlling tty name */
  640.     if (strcmp(name,cname) == 0)        /* same? */
  641.       ctty = TRUE;            /* yup, it is ctrl tty */
  642.     else
  643.       ctty = FALSE;            /* nope, some other tty */
  644.     close(cttyfd);            /* no more use for this */
  645.   }
  646.                     /*  */
  647.   if (ctty) {                /* controlling terminal? */
  648.     type = getenv("TERM");        /* yup, use environment var */
  649.     if (type != NULL)
  650.       return(type);            /* give it back if successful */
  651.   }
  652.  
  653.   name += 5;                /* skip the "/dev/" prefix */
  654.   typedb = fopen("/etc/ttytype","r");     /* open type database */
  655.   if (typedb == NULL)
  656.     return("unknown");            /* give up if bad open */
  657.   while (fgets(typelin,80,typedb) != NULL) { /* scan the database */
  658.     typecp = typelin;
  659.     while ((*typecp++) != SPACE);    /* scan for space in entry */
  660.     *(typecp-1) = NULCHAR;        /* change it to null */
  661.     
  662.     if (strcmp(name,typecp) == 0) {    /* this our entry? */
  663.       fclose(typedb);            /* yup, shut database */
  664.       return(typelin);            /* and return type name */
  665.     }
  666.   }
  667.   fclose(typedb);            /* not found... close database */
  668.   return("unknown");            /* and give default */
  669. }
  670.  
  671. /* auxiliary routines to take terminals into and out of raw mode */
  672.  
  673. #if HAVE_TERMIOS
  674. static struct termios ttyblk, ttysav;
  675.  
  676. #elif HAVE_TERMIO
  677. static struct termio ttyblk, ttysav;
  678.  
  679. #elif HAVE_TERM_BSD
  680. static struct sgttyb ttyblk, ttysav;    /* tty parameter blocks */
  681. static struct ltchars ltc,ltcsav;    /* local special chars for new  */
  682. #endif
  683.  
  684. /* raw - put the terminal into raw (actually cbreak) mode, turn off
  685. ** echoing and output translations, and extract the output speed for
  686. ** the termcap library.  On BSD unix systems, literal-next processing
  687. ** is also disabled.
  688. **/
  689.  
  690. static
  691. raw(int fd)
  692. {
  693. #if HAVE_TERMIOS
  694.   tcgetattr(fd, &ttysav);
  695.   ttyblk = ttysav;
  696.   ttyblk.c_lflag &= ~(ICANON|ECHO);    /* Make raw-ish */
  697.   ttyblk.c_cc[VINTR] = 003;        /* interrupt char is control-c */
  698.   ttyblk.c_cc[VMIN] = 1;
  699.   ttyblk.c_cc[VTIME] = 1;
  700.   tcsetattr(fd, TCSADRAIN, &ttyblk);
  701.  
  702. #elif HAVE_TERMIO
  703.   /* KLH: Note that the c_cc index values have CHANGED since the original
  704.   ** SYSV implementation.  Now using symbols, but original values left
  705.   ** in comments.
  706.   */
  707.   ioctl(fd, TCGETA, &ttysav);
  708.   ttyblk = ttysav;
  709.   ttyblk.c_lflag &= ~(ICANON|ECHO);
  710.   ttyblk.c_cc[VINTR/*0*/] = 003;    /* interrupt char is control-c */
  711.   ttyblk.c_cc[VMIN/*4*/] = 1;
  712.   ttyblk.c_cc[VTIME/*5*/] = 1;
  713.   ioctl(fd,TCSETAW,&ttyblk);        /* set new modes . */
  714.  
  715. #elif HAVE_TERM_BSD
  716.   extern short ospeed;            /* declared in termlib */
  717.  
  718.   ioctl(fd,TIOCGETP,&ttysav);        /* get original parameters */
  719.   ttyblk = ttysav;            /* copy into new parameter block */
  720.   ospeed = ttysav.sg_ospeed;        /* save output speed for termlib */
  721.   ttyblk.sg_flags &= ~(RAW | ECHO | LCASE); /* no echoing or xlates */
  722.   ttyblk.sg_flags |= CBREAK;        /* single character reads */
  723.   ioctl(fd,TIOCSETN,&ttyblk);        /* set params, leave typeahead */
  724.   ioctl(fd,TIOCGLTC,<c);        /* get current local special chars */
  725.   ltcsav = ltc;                /* copy it for later restore */
  726.   ltc.t_lnextc = -1;            /* disable literal-next */
  727.   ioctl(fd,TIOCSLTC,<c);        /* set the new chars in place */
  728. #else
  729. # --ERROR-- no code for raw()
  730. #endif
  731. }
  732.  
  733. /* unraw - restore the tty modes in effect before raw was performed. */
  734.  
  735. static
  736. unraw(int fd)
  737. {
  738. #if HAVE_TERMIOS
  739.   tcsetattr(fd, TCSADRAIN, &ttysav);    /* put back saved params */
  740. #elif HAVE_TERMIO
  741.   ioctl(fd,TCSETAW, &ttysav);        /* put back saved params */
  742. #elif HAVE_TERM_BSD
  743.   ioctl(fd,TIOCSETN,&ttysav);        /* put back saved params */
  744.   ioctl(fd,TIOCSLTC,<csav);        /* restore local special chars */
  745. #else
  746.     --ERROR-- no code for unraw()
  747. #endif
  748. }
  749.  
  750. /* outc - aux routine to be passed to termlib routines - output one char */
  751.  
  752. PASSEDSTATIC int
  753. outc(int c)
  754. {
  755.   FILE *fd = cmcsb._cmoj;        /* get output filedesc */
  756.  
  757.   if (fd != NULL)
  758.     putc(c,fd);                /* do the write */
  759. }
  760.  
  761. /* intson - Install our interrupt handlers for INT and STOP, so
  762. ** any terminal settings we have installed will be undone before
  763. ** the program exits.  Any handlers that are already installed
  764. ** are left in place.  Those handlers should call cmdone if
  765. ** they expect to exit with the terminal set correctly.
  766. **/
  767.  
  768. #ifdef V7                /* used improve calls in version 7 */
  769. #define signal sigsys
  770. #endif
  771.  
  772. /* Implement our own version of signal().
  773.  * Needed because systems vary in how they implement this.
  774.  * NetBSD/FreeBSD: SA_RESTART on, SA_RESETHAND off
  775.  *      Solaris: SA_RESTART off, SA_RESETHAND on
  776.  * Ugh!!!
  777.  * MM was written assuming the BSD settings.
  778.  */
  779. static ccmdsigval_t
  780. ccmdsignal(int sig, ccmdsigval_t func)
  781. {
  782. #if HAVE_SIGSETS
  783.   struct sigaction act, oact;
  784.  
  785.   act.sa_handler = func;
  786.   act.sa_flags = SA_RESTART;
  787.   sigemptyset(&act.sa_mask);
  788.   sigaddset(&act.sa_mask, sig);/* Suspend this sig during handler */
  789.   if (sigaction(sig, &act, &oact) == 0)
  790.     return oact.sa_handler;
  791.   return (ccmdsigval_t)-1;
  792. #else
  793.   return (ccmdsigval_t) signal(sig, func);
  794. #endif
  795. }
  796.  
  797. static void
  798. intson(void)
  799. {
  800.   static SIG sighand();            /* forward decl of our handler */
  801.   ccmdsigset_t oldmask;            /* sig mask prior to our diddling */
  802.   ccmdsigset_t sigs;            /* New sig mask */
  803.   ccmdsigval_t oldhand;            /* old handler */
  804.  
  805.   ccmdsigemptyset(&sigs);
  806. #if defined(SIGTSTP) || defined(SIGWINCH)
  807.     (void) ccmdsigaddset(&sigs, SIGINT);
  808. # ifdef SIGTSTP
  809.     (void) ccmdsigaddset(&sigs, SIGTSTP);
  810. # endif
  811. # ifdef SIGWINCH
  812.     (void) ccmdsigaddset(&sigs, SIGWINCH);
  813. #endif
  814. # if HAVE_SIGSETS
  815.     (void) sigprocmask(SIG_BLOCK, &sigs, &oldmask);
  816. # else
  817.     oldmask = sigblock(sigs);
  818. # endif
  819. #endif
  820.  
  821.   oldhand = ccmdsignal(SIGINT, (ccmdsigval_t)sighand);    /* install our handler, get prior */
  822.   if (oldhand != SIG_DFL)        /* did they have something? */
  823.     ccmdsignal(SIGINT,oldhand);        /* yup, leave it there */
  824. #ifdef SIGTSTP
  825.   oldhand = ccmdsignal(SIGTSTP, (ccmdsigval_t)sighand);    /* install ours for TSTP too */
  826.   if (oldhand != SIG_DFL)
  827.     ccmdsignal(SIGTSTP,oldhand);        /* but leave theirs intact */
  828. #endif /* SIGTSTP */
  829.  
  830. #ifdef SIGWINCH
  831.   oldhand = ccmdsignal(SIGWINCH, (ccmdsigval_t)sighand); /* install ours for WINCH too */
  832.   if (oldhand != SIG_DFL)
  833.     ccmdsignal(SIGWINCH,oldhand);        /* but leave theirs intact */
  834. #endif /* SIGWINCH */
  835.  
  836.     /* now unblock the signals */
  837. #if defined(SIGTSTP) || defined(SIGWINCH)
  838. # if HAVE_SIGSETS
  839.     (void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)NULL);
  840. # else
  841.     sigsetmask(oldmask);
  842. # endif
  843. #endif
  844. }
  845.  
  846.  
  847. /* intsoff - Remove our interrupt handlers.  If we remove something
  848. ** that isn't ours, put it back.
  849. **/
  850.  
  851. static void
  852. intsoff(void)
  853. {
  854.   static SIG sighand();            /* forward decl of our handler */
  855.   ccmdsigset_t oldmask;            /* sig mask prior to our diddling */
  856.   ccmdsigset_t sigs;            /* New sig mask */
  857.   ccmdsigval_t oldhand;            /* prior handler for a signal */
  858.  
  859.   ccmdsigemptyset(&sigs);
  860. #if defined(SIGTSTP) || defined(SIGWINCH)
  861.     (void) ccmdsigaddset(&sigs, SIGINT);
  862. # ifdef SIGTSTP
  863.     (void) ccmdsigaddset(&sigs, SIGTSTP);
  864. # endif
  865. # ifdef SIGWINCH
  866.     (void) ccmdsigaddset(&sigs, SIGWINCH);
  867. #endif
  868. # if HAVE_SIGSETS
  869.     (void) sigprocmask(SIG_BLOCK, &sigs, &oldmask);
  870. # else
  871.     oldmask = sigblock(sigs);
  872. # endif
  873. #endif
  874.  
  875.   oldhand = ccmdsignal(SIGINT,SIG_DFL);    /* remove INT handler */
  876.   if (oldhand != (ccmdsigval_t)sighand)
  877.     ccmdsignal(SIGINT,oldhand);        /* replace if not ours */
  878.  
  879. #ifdef SIGTSTP
  880.   oldhand = ccmdsignal(SIGTSTP,SIG_DFL);    /* remove TSTP handler */
  881.   if (oldhand != (ccmdsigval_t)sighand)
  882.     ccmdsignal(SIGTSTP,oldhand);        /* replace if not ours */
  883. #endif
  884.  
  885. #ifdef SIGWINCH
  886.   oldhand = ccmdsignal(SIGWINCH,SIG_DFL);    /* remove TSTP handler */
  887.   if (oldhand != (ccmdsigval_t)sighand)
  888.     ccmdsignal(SIGWINCH,oldhand);        /* replace if not ours */
  889. #endif
  890.  
  891.     /* now unblock the signals */
  892. #if defined(SIGTSTP) || defined(SIGWINCH)
  893. # if HAVE_SIGSETS
  894.     (void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)NULL);
  895. # else
  896.     sigsetmask(oldmask);
  897. # endif
  898. #endif
  899. }
  900.  
  901.  
  902. /* sighand - Handler for INT and TSTP signals.  We first fix
  903. ** the terminal to its normal settings, then remove our handler
  904. ** and generate whichever signal invoked us to get the default
  905. ** action.  If the program is continued, the terminal is adjusted
  906. ** again, and our handler is reinstalled.  (This should only happen
  907. ** with TSTP signals).
  908. **/
  909.  
  910. static SIG
  911. sighand(sig,code,scp)
  912. int sig,code;                /* sig is all we care about */
  913. struct sigcontext *scp;
  914. {
  915.   ccmdsigset_t oldmask;        /* prior interrupt mask */
  916.   ccmdsigset_t sigs;        /* New sig mask */
  917.  
  918. #if !HAVE_UNISTD
  919.   long getpid();            /* pids are long */
  920. #endif
  921.  
  922.   switch (sig) {
  923. #ifdef SIGWINCH
  924.   case SIGWINCH:
  925.     cmtsize();
  926.     break;
  927. #endif
  928.  
  929.   default:
  930.     cmtend();                /* fix the terminal */
  931. # if HAVE_SIGSETS
  932.     ccmdsigemptyset(&sigs);        /* Set no signals blocked */
  933.     (void) sigprocmask(SIG_SETMASK, &sigs, &oldmask);
  934. # else
  935.     oldmask = sigsetmask(0);
  936. # endif
  937.  
  938. #ifdef SIGTSTP
  939.     if (sig == SIGTSTP)
  940.       cmnl(stdout);            /* move to new line for looks */
  941. #endif /* SIGTSTP */
  942.     kill(getpid(),sig);            /* get the default action */
  943.     cmtset();                /* redo the terminal if continued */
  944.  
  945. # if HAVE_SIGSETS
  946.     (void) sigprocmask(SIG_SETMASK, &oldmask, NULL);
  947. # else
  948.     sigsetmask(oldmask);        /* set mask back to before */
  949. #endif
  950.   }
  951. }
  952.  
  953.  
  954.  
  955. /* ctty, cttycls - Ctty opens the controlling terminal and returns a
  956. ** file descriptor for it.  After the first call, it just returns the
  957. ** file descriptor opened previously.  Cttycls closes the file
  958. ** descriptor opened by ctty, after which another call to ctty will
  959. ** open another one.
  960. **/
  961.  
  962. static int ttyfd = -1;
  963.  
  964. static int
  965. ctty()
  966. {
  967.   if (ttyfd == -1)
  968.     ttyfd = open("/dev/tty",O_RDWR,0);
  969.   return(ttyfd);
  970. }
  971.  
  972. static
  973. cttycls()
  974. {
  975.   if (ttyfd != -1)
  976.     close(ttyfd);
  977.   ttyfd = -1;
  978. }
  979.  
  980. /* KLH: Only used to disable NL->CR+NL output conversion during
  981.    a cursor-move-down sequence (ie cmdownl())
  982. */
  983. crmod(val)
  984. int val;
  985. {
  986.   int oldval;
  987.   int fd;
  988.  
  989.   if (cmcsb._cmoj == NULL) 
  990.       return(-1);
  991.   fd = fileno(cmcsb._cmoj);
  992.  
  993. #if HAVE_TERMIOS
  994.   {
  995.   struct termios tblk;            /* get original parameters */
  996.   tcgetattr(fd, &tblk);
  997.   oldval = tblk.c_oflag & ONLCR;
  998.   if (val)
  999.       tblk.c_oflag |= ONLCR;        /* Turn on NL to CR-NL (old CRMOD) */
  1000.   else
  1001.       tblk.c_oflag &= ~ONLCR;        /* Turn off NL to CR-NL (old CRMOD) */
  1002.   tcsetattr(fd, TCSANOW , &tblk);    /* set params, leave typeahead */
  1003.   }
  1004. #elif defined(TIOCSETN)        /* For either HAVE_TERMIO or HAVE_TERM_BSD */
  1005.   {
  1006.   struct sgttyb ttyblk;
  1007.  
  1008.   ioctl(fd,TIOCGETP,&ttyblk);        /* get original parameters */
  1009.  
  1010.   oldval = ttyblk.sg_flags & CRMOD;
  1011.   if (val)
  1012.       ttyblk.sg_flags |= CRMOD;        /* turn on CRMOD */
  1013.   else
  1014.       ttyblk.sg_flags &= ~CRMOD;    /* turn off CRMOD */
  1015.   ioctl(fd,TIOCSETN,&ttyblk);        /* set params, leave typeahead */
  1016.   }
  1017. #else
  1018.   oldval = 0;
  1019. #endif
  1020.   return(oldval);
  1021. }
  1022.  
  1023. cmselect(fd)
  1024. int fd;
  1025. {
  1026. #ifdef FD_SETSIZE
  1027.   fd_set rfds, efds;
  1028.   int r;
  1029.   
  1030.   while(1) {
  1031.     FD_ZERO(&rfds);
  1032.     FD_ZERO(&efds);
  1033.     FD_SET(fd, &rfds);
  1034.     FD_SET(fd, &efds);
  1035.     r = select(FD_SETSIZE, &rfds, NULL, &efds, NULL);
  1036.     if (r >= 0 || errno != EINTR)
  1037.     return;
  1038.   }
  1039. #endif
  1040. }
  1041.