home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume3 / dial / dial.c < prev    next >
Encoding:
C/C++ Source or Header  |  1986-11-30  |  20.6 KB  |  940 lines

  1. /* dial -- dial a remote computer using a state transition table
  2.  *
  3.  * Copyright (c) 1986, Oliver Laumann, Technical University of Berlin.
  4.  * Not derived from licensed software.
  5.  *
  6.  * Permission is granted to freely use, copy, modify, and redistribute
  7.  * this software, provided that no attempt is made to gain profit from it,
  8.  * the author is not construed to be liable for any results of using the
  9.  * software, alterations are clearly marked as such, and this notice is
  10.  * not modified.
  11.  */
  12.  
  13. #include <stdio.h>
  14. #include <pwd.h>
  15. #include <sys/ioctl.h>
  16. #include <sys/time.h>
  17. #include <sys/file.h>
  18. #include <sys/errno.h>
  19. #include <sys/signal.h>
  20. #include <sys/types.h>
  21. #include <sys/stat.h>
  22.  
  23. #define TIMEOUT     10
  24. #define DELAY       0
  25. #define BUFLEN      1024
  26. #define EXIT        ((struct state *)0)
  27. #define LOCKFN      "/usr/spool/uucp/LCK..%s"
  28. #define DIALLIB     "/usr/local/lib/dial"
  29. #define REMOTE      "/etc/remote"
  30.  
  31. #define FULL        0      /* match() return values */
  32. #define PART        1
  33. #define NONE        2
  34.  
  35. #define ISSPACE(x) (x == ' ' || x == '\t')
  36. #define ISOCT(x)   (x >= '0' && x <= '7')
  37. #define ISDEC(x)   (x >= '0' && x <= '9')
  38.  
  39. char *script;
  40. char *line;
  41. int escape = '~';
  42. char *prompt = "\r\n:";
  43. struct timeval timeout = {TIMEOUT, 0};
  44. struct timeval delay = {DELAY, 0};
  45. char obuf[BUFLEN];
  46. char *obp = obuf;
  47. char rbuf[BUFLEN];
  48. char *rbp = rbuf;
  49. int vflag;
  50. int pflag;
  51. int ttyf;
  52. struct sgttyb ottyb;
  53. struct sgttyb consb;
  54. struct tchars otc;
  55. struct ltchars oltc;
  56. short coflags;
  57. int ttydone;
  58. int consdone;
  59. char *myname;
  60. char **argp;
  61. int numargs;
  62. char lockfile[BUFLEN];
  63. int locked;
  64. int undef;
  65. char *dialdir;
  66. int baud = -1;
  67. int bdconst[] = {
  68.     B50, B75, B110, B134, B150, B200, B300, B600,
  69.     B1200, B1800, B2400, B4800, B9600, EXTA, EXTB
  70. };
  71. int bdnum[] = {
  72.     50, 75, 110, 134, 150, 200, 300, 600,
  73.     1200, 1800, 2400, 4800, 9600, 19200, 38400, 0
  74. };
  75.  
  76. #define S_STATE  1
  77.  
  78. struct state {
  79.     struct state *s_next;
  80.     char *s_string;
  81.     int s_len;
  82.     char *s_name;
  83.     char *s_output;
  84.     int s_outlen;
  85.     struct timeval s_timeout;
  86.     struct timeval s_delay;
  87.     int s_stat;
  88. } *stab;
  89.  
  90. extern errno;
  91. extern char **environ;
  92. char *malloc(), *alloc(), *memsav(), *getenv(), *tgetstr(), *fnexpand();
  93. struct state *enter(), *receive(), *next_state(), *user();
  94. struct passwd *getpwnam(), *getpwuid();
  95. long atol();
  96.  
  97. main (ac, av) char **av; {
  98.     register struct state *next;
  99.     register char *p;
  100.     int Exit();
  101.  
  102.     myname = ac < 1 ? "dial" : av[0];
  103.     if (ac < 1) {
  104. help:   err ("Use: %s [-v] [-p] [-lline] [script-file [args...]]\n", myname);
  105.     }
  106.     while (--ac) {
  107.     p = *++av;
  108.     if (*p == '-' && p[1] != '\0') {
  109.         if (*++p == 'v') {
  110.         ++vflag;
  111.         } else if (*p == 'p') {
  112.         ++pflag;
  113.         } else if (*p == 'l') {
  114.         line = ++p;
  115.         } else goto help;
  116.     } else if (!script) {
  117.         script = p;
  118.     } else {
  119.         argp = av;
  120.         numargs = ac;
  121.         break;
  122.     }
  123.     }
  124.     if (script) {
  125.     dialdir = getenv ("DIALDIR");
  126.     get_script ();
  127.     check_script ();
  128.     }
  129.     if (!line || line[0] == '\0')
  130.     err ("No tty line specified.\n");
  131.     signal (SIGHUP, Exit);
  132.     signal (SIGINT, Exit);
  133.     open_line ();
  134.     if (!stab)
  135.     say ("Connected.\n");
  136.     set_tty ();
  137.     next = (stab ? stab : user ());
  138.     while (next != EXIT)
  139.     next = enter (next);
  140.     if (vflag)
  141.     say ("State `exit' -- done.\n");
  142.     Exit (0);
  143. }
  144.  
  145. get_script () {
  146.     FILE *f = NULL;
  147.     char fn[BUFLEN], lbuf[BUFLEN], buf[BUFLEN];
  148.     register char *p, *s;
  149.     register struct state *sp, *tmp;
  150.     register c, delim, lno = 0;
  151.     struct stat st;
  152.  
  153.     if (!strcmp (script, "-")) {
  154.     f = stdin;
  155.     script = "stdin";
  156.     } else {
  157.     if (stat (script, &st) || (st.st_mode & S_IFMT) == S_IFDIR
  158.                    || (f = fopen (script, "r")) == NULL) {
  159.         if (dialdir) {
  160.         sprintf (fn, "%s/%s", dialdir, script);
  161.         f = fopen (fn, "r");
  162.         }
  163.         if (f == NULL) {
  164.         sprintf (fn, "%s/%s", DIALLIB, script);
  165.         if ((f = fopen (fn, "r")) == NULL)
  166.             err ("Cannot open `%s'.\n", script);
  167.         }
  168.     }
  169.     }
  170.     while (1) {
  171.     ++lno;
  172.     if (fgetstr (f, lbuf, BUFLEN-1) == EOF)
  173.         break;
  174.     if (!expand_args (lbuf, buf)) goto error;
  175.     for (p = buf; ISSPACE(*p); ++p) ;
  176.     if (*p == '#' || *p == '\0')
  177.         continue;
  178.     if (p == buf) {
  179.         if (getkey (p))
  180.         continue;
  181.     } else if (!stab) goto error;
  182.     tmp = (struct state *)alloc (sizeof (struct state));
  183.     tmp->s_stat = (p == buf ? S_STATE : 0);
  184.     for (s = p; *s && !ISSPACE(*s); ++s) ;
  185.     if (*s == '\0') goto error;
  186.     *s = '\0';
  187.     tmp->s_name = memsav (p, s-p+1);
  188.     for (++s; ISSPACE(*s); ++s) ;
  189.     if (delim = (*s == '"')) ++s;
  190.     for (p = s; *s && (delim ? *s != '"' : !ISSPACE(*s)); ++s) ;
  191.     if (delim) {
  192.         if (*s == '\0') goto error;
  193.         *s++ = '\0';
  194.     }
  195.     c = *s;
  196.     *s = '\0';
  197.     tmp->s_len = cook (p, p);
  198.     tmp->s_string = memsav (p, tmp->s_len);
  199.     tmp->s_outlen = 0;
  200.     tmp->s_timeout = timeout;
  201.     tmp->s_delay = delay;
  202.     if (c) {
  203.         for (++s; ISSPACE(*s); ++s) ;
  204.         if (*s) {
  205.         if (delim = (*s == '"')) ++s;
  206.         for (p = s; *s && (delim ? *s != '"' : !ISSPACE(*s)); ++s) ;
  207.         if (delim) {
  208.             if (*s == '\0') goto error;
  209.             *s++ = '\0';
  210.         }
  211.         *s = '\0';
  212.         if (tmp->s_stat & S_STATE) {
  213.             if (!set_par (tmp, p)) goto error;
  214.         } else {
  215.             tmp->s_outlen = cook (p, p);
  216.             tmp->s_output = memsav (p, tmp->s_outlen);
  217.         }
  218.         }
  219.     }
  220.     if (stab)
  221.         sp = sp->s_next = tmp;
  222.     else
  223.         stab = sp = tmp;
  224.     sp->s_next = 0;
  225.     }
  226.     fclose (f);
  227.     return;
  228. error:
  229.     err ("%s: syntax error in line %d.\n", script, lno);
  230. }
  231.  
  232. expand_args (from, to) char *from, *to; {
  233.     register char *p, *q, *t, *s = to, *endp = to+BUFLEN-1;
  234.     register n;
  235.     FILE *f;
  236.  
  237.     for (p = from; *p; ++p) {
  238.     if (s == endp)
  239.         break;
  240.     if (*p == '$') {
  241.         if (p > from && p[-1] == '\\') {
  242.         s[-1] = '$';    
  243.         } else if (ISDEC(p[1])) {
  244.         if (*++p - '0' <= numargs) {
  245.             for (q = argp[*p - '0']; s < endp && *q; *s++ = *q++)
  246.             ;
  247.         }
  248.         } else if (p[1] == '*') {
  249.         for (n = 0; n < numargs; ++n) {
  250.             for (q = argp[n]; s < endp && *q; *s++ = *q++)
  251.             ;
  252.             if (n < numargs-1 && s < endp)
  253.             *s++ = ' ';
  254.         }
  255.         ++p;
  256.         } else if (p[1] == '{') {
  257.         ++p;
  258.         for (q = ++p; *p && *p != '}'; ++p) ;
  259.         if (*p == '\0') return (0);
  260.         *p = '\0';
  261.         if (*q == '~' || *q == '/') {
  262.             t = fnexpand (q);
  263.             if ((f = fopen (t, "r")) == NULL)
  264.             err ("Cannot open `%s'.\n", t);
  265.             while ((n = getc (f)) != EOF && s < endp)
  266.             *s++ = n;
  267.             fclose (f);
  268.         } else if (t = getenv (q)) {
  269.             while (s < endp && *t)
  270.             *s++ = *t++;
  271.         }
  272.         } else *s++ = '$';
  273.     } else *s++ = *p;
  274.     }
  275.     *s = '\0';
  276.     return (1);
  277. }
  278.  
  279. char *fnexpand (s) char *s; {
  280.     static char buf[BUFLEN];
  281.     struct passwd *pw;
  282.     register char c, *p, *q = s+1;
  283.  
  284.     if (*s != '~')
  285.     return (s);
  286.     for (p = q; (c = *p) && c != '/'; ++p) ;
  287.     *p = '\0';
  288.     if (p == q) {
  289.     if (!(pw = getpwuid (getuid ())))
  290.         err ("Cannot get home directory.\n");
  291.     } else if (!(pw = getpwnam (q)))
  292.     err ("Unknown user: %s.\n", q);
  293.     if (c) {
  294.     sprintf (buf, "%s/%s", pw->pw_dir, ++p);
  295.     return (buf);
  296.     } else return (pw->pw_dir);
  297. }
  298.  
  299. set_par (sp, s) struct state *sp; char *s; {
  300.     register char *p;
  301.  
  302.     for (p = s; *p && *p != ','; ++p) ;
  303.     if (*p == ',')
  304.     *p++ = '\0';
  305.     if (*s) {
  306.     if (!isnum (s)) return (0);
  307.     set_time (&sp->s_timeout, s);
  308.     }
  309.     if (*p) {
  310.     if (!isnum (p)) return (0);
  311.     set_time (&sp->s_delay, p);
  312.     }
  313.     return (1);
  314. }
  315.  
  316. set_time (tp, s) struct timeval *tp; char *s; {
  317.     register char *p;
  318.     register long div;
  319.  
  320.     for (p = s; *p && *p != '.'; ++p) ;
  321.     if (*p == '.')
  322.     *p++ = '\0';
  323.     tp->tv_sec = atoi (s);
  324.     if (p) {
  325.     p[6] = '\0';
  326.     tp->tv_usec = atol (p);
  327.     div = 1;
  328.     while (*p) {
  329.         div *= 10;
  330.         ++p;
  331.     }
  332.     tp->tv_usec *= 1000000L / div;
  333.     } else tp->tv_usec = 0;
  334. }
  335.  
  336. getkey (s) char *s; {
  337.     register char *k, *p, *q;
  338.  
  339.     for (p = s; *p; ++p)
  340.     if (*p == '=' && !(p > s && p[-1] == '\\'))
  341.         break;
  342.     if (*p == '\0') return (0);
  343.     for (*p++ = '\0'; ISSPACE(*p); ++p) ;
  344.     for (q = p; *q && !ISSPACE(*q); ++q) ;
  345.     *q = '\0';
  346.     for (k = s; *k && !ISSPACE(*k); ++k) ;
  347.     *k = '\0';
  348.     if (!strcmp (s, "line")) {
  349.     if (*p == '\0')
  350.         err ("Bad value for keyword `line'.\n");
  351.     if (!line)
  352.         line = memsav (p, strlen (p)+1);
  353.     } else if (!strcmp (s, "timeout")) {
  354.     if (!isnum (p))
  355.         err ("Bad value for keyword `timeout'.\n");
  356.     set_time (&timeout, p);
  357.     } else if (!strcmp (s, "delay")) {
  358.     if (!isnum (p))
  359.         err ("Bad value for keyword `delay'.\n");
  360.     set_time (&delay, p);
  361.     } else if (!strcmp (s, "escape")) {
  362.     if (strlen (p) != 1)
  363.         err ("Bad value for keyword `escape'.\n");
  364.     escape = *p & 0177;
  365.     } else if (!strcmp (s, "prompt")) {
  366.     prompt = memsav (p, strlen (p)+1);
  367.     } else err ("Invalid keyword `%s'.\n", s);
  368.     return (1);
  369. }
  370.  
  371. check_script () {
  372.     register struct state *sp;
  373.  
  374.     for (sp = stab; sp; sp = sp->s_next) {
  375.     if (!(sp->s_stat & S_STATE)) continue;
  376.     if (!sp->s_next || (sp->s_next->s_stat & S_STATE))
  377.         err ("No transitions for state `%s'.\n", sp->s_name);
  378.     }
  379. }
  380.  
  381. struct state *enter (sp) struct state *sp; {
  382.     if (vflag)
  383.     say ("Entering state %s.\n", sp->s_name);
  384.     if (sp->s_delay.tv_sec || sp->s_delay.tv_usec) {
  385.     flushbuf ();
  386.     if (select (0, (int *)0, (int *)0, (int *)0, &sp->s_delay) == -1) {
  387.         perror ("select");
  388.         Exit (1);
  389.     }
  390.     }
  391.     sendstr (sp->s_string, sp->s_len);
  392.     return (receive (sp));
  393. }
  394.  
  395. sendstr (sbuf, len) char *sbuf; register len; {
  396.     char buf[BUFLEN], ibuf[BUFLEN];
  397.     register char *s, *p, *t;
  398.     register n;
  399.     char c;
  400.  
  401.     for (s = sbuf, p = buf; len && p < buf+BUFLEN; --len, ++p, ++s) {
  402.     if (*s == '\\') {
  403.         if (s[1] == '\\') {
  404.         *p = '\\'; ++s;
  405.         } else if (s[1] == '<' || s[1] == '?') {
  406.         ++s;
  407.         flushbuf ();
  408.         if (*s == '<') {
  409.             n = Read (0, ibuf, BUFLEN);
  410.             c = '\n';
  411.         } else {
  412.             set_cons ();
  413.             for (t = ibuf, n = 0; n < BUFLEN; ++n, ++t) {
  414.             Read (0, t, 1);
  415.             if (*t == '\r') break;
  416.             }
  417.             reset_cons ();
  418.             if (!pflag)
  419.             say ("\n");
  420.             c = '\r';
  421.         }
  422.         for (t = ibuf; n && *t != c && p < buf+BUFLEN; --n)
  423.             *p++ = *t++;
  424.         } else *p = '\\';
  425.     } else *p = *s;
  426.     }
  427.     if (vflag) {
  428.     say ("Sending \""); prstr (buf, p-buf); say ("\".\n");
  429.     }
  430.     if (p > buf)
  431.     Write (ttyf, buf, p-buf);
  432. }
  433.  
  434. fill (sp) struct state *sp; {
  435.     register i;
  436.     register char *p;
  437.     int rbits = (1 << ttyf);
  438.  
  439.     if (rbp == rbuf+BUFLEN)
  440.     err ("Pattern space overflow.\n");
  441.     flushbuf ();
  442.     i = select (ttyf+1, &rbits, (int *)0, (int *)0, &sp->s_timeout);
  443.     if (i == -1) {
  444.     perror ("select");
  445.     Exit (1);
  446.     }
  447.     if (i == 0) {
  448.     if (vflag)
  449.         say ("   Got time-out.\n");
  450.     return (0);
  451.     }
  452.     i = Read (ttyf, rbp, rbuf+BUFLEN-rbp);
  453.     p = rbp;
  454.     do {
  455.     *p++ &= 0177;
  456.     } while (--i);
  457.     if (vflag) {
  458.     say ("   Got \""); prstr (rbp, p-rbp); say ("\".\n");
  459.     }
  460.     if (pflag)
  461.     Write (1, rbp, p-rbp);
  462.     rbp = p;
  463.     return (1);
  464. }
  465.  
  466. shift (n) {
  467.     bcopy (rbuf+n, rbuf, rbp-rbuf-n);
  468.     rbp -=n;
  469. }
  470.  
  471. struct state *receive (sp) struct state *sp; {
  472.     register struct state *p, *fullp, *partp;
  473.     register r, mlen, dofill = 0;
  474.     int len;
  475.  
  476.     while (1) {
  477.     if ((rbp == rbuf || dofill) && !fill (sp)) {
  478.         rbp = rbuf;
  479.         for (p = sp->s_next; p && !(p->s_stat & S_STATE); p = p->s_next) {
  480.         if (p->s_len == 1 && p->s_string[0] == '#') {
  481.             output (p, (char *)0, 0);
  482.             return (next_state (p->s_name, 1));
  483.         }
  484.         }
  485.         return (EXIT);
  486.     }
  487.     dofill = 0;
  488.     fullp = partp = 0;
  489.     for (p = sp->s_next; p && !(p->s_stat & S_STATE); p = p->s_next) {
  490.         if (p->s_len == 1 && p->s_string[0] == '#')
  491.         continue;
  492.         r = match (rbuf, rbp-rbuf, p->s_string, p->s_len, &len);
  493.         if (r == FULL && !fullp) {
  494.         fullp = p;
  495.         mlen = len;
  496.         } else if (r == PART && !partp) {
  497.         partp = p;
  498.         }
  499.     }
  500.     if (!fullp && !partp) {
  501.         shift (1);
  502.         if (vflag)
  503.         say ("No match -- shift(1).\n");
  504.         continue;
  505.     }
  506.     if (fullp && !partp) {
  507.         if (fullp->s_len == 1 && fullp->s_string[0] == '*')
  508.         mlen = amatch (sp);
  509.         if (vflag)
  510.         say ("Full match <%s> -- shift(%d).\n", fullp->s_name, mlen);
  511.         output (fullp, rbuf, mlen);
  512.         shift (mlen);
  513.         return (next_state (fullp->s_name, 1));
  514.     }
  515.     if (vflag)
  516.         say ("Partial match <%s>.\n", partp->s_name);
  517.     dofill = 1;
  518.     }
  519. }
  520.  
  521. amatch (ap) struct state *ap; {
  522.     register struct state *sp;
  523.     register char *p;
  524.     int notused;
  525.  
  526.     for (p = rbuf; p < rbp; ++p) {
  527.     for (sp = ap->s_next; sp && !(sp->s_stat & S_STATE); sp = sp->s_next) {
  528.         if (sp->s_len == 1 &&
  529.             (sp->s_string[0] == '#' || sp->s_string[0] == '*'))
  530.         continue;
  531.         if (match (p, rbp-p, sp->s_string, sp->s_len, ¬used) != NONE)
  532.         goto done;
  533.     }
  534.     }
  535. done:
  536.     return (p-rbuf);
  537. }
  538.  
  539. match (s, slen, t, tlen, len) register char *s, *t; register slen, tlen;
  540.     int *len; {
  541.     register otlen = tlen, oslen = slen;
  542.  
  543.     if (tlen == 1 && t[0] == '*')
  544.     return (FULL);
  545.     while (tlen) {
  546.     if (!slen)
  547.         return (PART);
  548.     if (*t != '\\') {
  549.         if (*s != *t)
  550.         if (*t != '.' || (tlen < otlen && t[-1] == '\\'))
  551.             return (NONE);
  552.         ++s; --slen;
  553.     }
  554.     ++t; --tlen;
  555.     }
  556.     *len = oslen-slen;
  557.     return (FULL);
  558. }
  559.  
  560. struct state *next_state (s, noise) char *s; {
  561.     register struct state *sp;
  562.  
  563.     undef = 0;
  564.     if (!strcmp (s, "exit"))
  565.     return (EXIT);
  566.     if (!strcmp (s, "user")) {
  567.     return (user ());
  568.     }
  569.     for (sp = stab; sp; sp = sp->s_next)
  570.     if ((sp->s_stat & S_STATE) && !strcmp (sp->s_name, s))
  571.         return (sp);
  572.     ++undef;
  573.     if (noise)
  574.     err ("State `%s' not found in script.\n", s);
  575. #ifdef lint
  576.     return (sp);
  577. #endif
  578. }
  579.  
  580. struct state *user () {
  581.     char c, buf[BUFLEN];
  582.     register n, l, gotesc = 0;
  583.     int rbits;
  584.     register char *p, *s;
  585.     register struct state *sp;
  586.  
  587.     flushbuf ();
  588.     signal (SIGINT, SIG_IGN);
  589.     set_cons ();
  590.     while (1) {
  591.     rbits = (1 << 0) | (1 << ttyf);
  592.     n = select (ttyf+1, &rbits, (int *)0, (int *)0, (struct timeval *)0);
  593.     if (n == -1) {
  594.         perror ("select");
  595.         Exit (1);
  596.     }
  597.     if (rbits & (1 << 0)) {
  598.         if (gotesc) {
  599.         Read (0, &c, 1);
  600.         if (c == '.' || c == otc.t_eofc) {
  601.             reset_cons ();
  602.             say ("\nConnection closed.\n");
  603.             return (EXIT);
  604.         } else if (c == oltc.t_suspc) {
  605.             reset_cons ();
  606.             kill (getpid (), SIGTSTP);
  607.             set_cons ();
  608.         } else if (c == '!') {
  609.             reset_cons ();
  610.             say ("\n");
  611.             if (system ((s = getenv ("SHELL")) ? s : "/bin/sh") == 127)
  612.             say ("Cannot execute shell.\n");
  613.             else say ("\n!\n");
  614.             set_cons ();
  615.         } else if (c == '?') {
  616.             reset_cons ();
  617.             print_help ();
  618.             set_cons ();
  619.         } else if (c == ' ' || c == '\r') {
  620.             Write (1, prompt, strlen (prompt));
  621.             reset_cons ();
  622.             if ((n = Read (0, buf, BUFLEN)) > 1) {
  623.             buf[n-1] = '\0';
  624.             for (p = buf; ISSPACE(*p); ++p) ;
  625.             for (s = p; *s && !ISSPACE(*s); ++s) ;
  626.             *s = '\0';
  627.             if (s > p) {
  628.                 sp = next_state (p, 0);
  629.                 if (undef) {
  630.                 say ("`"); prstr (p, s-p);
  631.                 say ("' is undefined.\n");
  632.                 } else {
  633.                 signal (SIGINT, Exit);
  634.                 return (sp);
  635.                 }
  636.             }
  637.             }
  638.             set_cons ();
  639.         } else Write (ttyf, &c, 1);
  640.         gotesc = 0;
  641.         } else {
  642.         n = Read (0, buf, BUFLEN);
  643.         for (p = buf, l = n; l && *p != escape; ++p, --l) ;
  644.         if (l) {
  645.             if (p > buf)
  646.             Write (ttyf, buf, p-buf);
  647.             ++gotesc;
  648.             while (--l)
  649.             ioctl (0, TIOCSTI, ++p);
  650.         } else Write (ttyf, buf, n);
  651.         }
  652.     }
  653.     if (rbits & (1 << ttyf))
  654.         Write (1, buf, Read (ttyf, buf, BUFLEN));
  655.     }
  656. }
  657.  
  658. print_help () {
  659.     char e[3];
  660.  
  661.     if (escape >= ' ' && escape <= '~')
  662.     sprintf (e, "%c", escape);
  663.     else
  664.     sprintf (e, "^%c", escape ^ '@');
  665.     say ("\n%s.       close connection and terminate.\n", e);
  666.     say ("%s!       fork a shell.\n", e);
  667.     say ("%s^Z      suspend %s.\n", e, myname);
  668.     say ("%s<CR>\n", e);
  669.     say ("%s<SPACE> leave interactive mode and enter new state.\n", e);
  670. }
  671.  
  672. output (sp, buf, len) struct state *sp; char *buf; {
  673.     char tbuf[BUFLEN];
  674.     register char *s = sp->s_output, *p = tbuf;
  675.     register i, olen = sp->s_outlen;
  676.  
  677.     if (pflag || !olen)
  678.     return;
  679.     while (olen) {
  680.     if (p == tbuf+BUFLEN) break;
  681.     if (*s == '&' && !(s > sp->s_output && s[-1] == '\\')) {
  682.         for (i = 0; i < len && p < tbuf+BUFLEN; ++i)
  683.         *p++ = buf[i];
  684.     } else if (*s != '\\') *p++ = *s;
  685.     ++s;
  686.     --olen;
  687.     }
  688.     if (vflag) {
  689.     say ("Print \""); prstr (tbuf, p-tbuf); say ("\".\n");
  690.     } else {
  691.     outbuf (tbuf, p-tbuf);
  692.     }
  693. }
  694.  
  695. outbuf (buf, len) register char *buf; register len; {
  696.     register l;
  697.  
  698. again:
  699.     l = obuf + BUFLEN - obp;
  700.     if (l > len) l = len;
  701.     bcopy (buf, obp, l);
  702.     obp += l;
  703.     if (len -= l) {
  704.     flushbuf ();
  705.     buf += l;
  706.     goto again;
  707.     }
  708. }
  709.  
  710. flushbuf () {
  711.     if (obp > obuf) {
  712.     Write (1, obuf, obp-obuf);
  713.     obp = obuf;
  714.     }
  715. }
  716.  
  717. open_line () {
  718.     char fn[BUFLEN];
  719.     char tbuf[1024], cbuf[1024];
  720.     int i, f;
  721.     struct stat statbuf;
  722.     char **oldenv, *envp[2], *p, *pp = cbuf;
  723.  
  724.     sprintf (fn, line[0] == '/' ? "%s" : "/dev/%s", line);
  725.     if (stat (fn, &statbuf) == -1 && errno == ENOENT) {
  726.     sprintf (tbuf, "TERMCAP=%s", REMOTE);
  727.     envp[0] = memsav (tbuf, strlen (tbuf));
  728.     envp[1] = 0;
  729.     oldenv = environ;
  730.     environ = envp;
  731.     if ((i = tgetent (tbuf, line)) == -1)
  732.         err ("Cannot open `%s'.\n", REMOTE);
  733.     else if (i == 0)
  734.         err ("`%s': no such line or remote system.\n", line);
  735.     if ((p = tgetstr ("dv", &pp)) == 0)
  736.         err ("No `dv' capability for system `%s'.\n", line);
  737.     strcpy (fn, p);
  738.     if ((baud = tgetnum ("br")) != -1) {
  739.         for (i = 0; bdnum[i] && baud != bdnum[i]; ++i) ;
  740.         if (!bdnum[i])
  741.         err ("Invalid baud rate %d for system `%s'.\n", baud, line);
  742.         baud = bdconst[i];
  743.     }
  744.     environ = oldenv;
  745.     }
  746.     if (!strncmp (fn, "/dev/", 5)) {
  747.     sprintf (lockfile, LOCKFN, fn+5);
  748.     if ((f = open (lockfile, O_CREAT|O_EXCL, 0)) == -1) {
  749.         if (errno == EEXIST)
  750.         err ("`%s' is already in use.\n", line);
  751.         perror (lockfile);
  752.         Exit (1);
  753.     }
  754.     locked++;
  755.     close (f);
  756.     }
  757.     if ((ttyf = open (fn, O_RDWR)) == -1) {
  758.     perror (line);
  759.     Exit (1);
  760.     }
  761.     ioctl (ttyf, TIOCHPCL, (char *)0);   /* Hang up phone on last close. */
  762. }
  763.  
  764. set_cons () {
  765.     struct tchars tc;
  766.     struct ltchars ltc;
  767.  
  768.     ioctl (0, TIOCGETP, &consb);
  769.     coflags = consb.sg_flags;
  770.     consb.sg_flags &= ~ECHO;
  771.     consb.sg_flags &= ~CRMOD;
  772.     consb.sg_flags |= CBREAK;
  773.     ioctl (0, TIOCSETP, &consb);
  774.     ioctl (0, TIOCGETC, &tc);
  775.     otc = tc;
  776.     tc.t_intrc = tc.t_quitc = -1;
  777.     ioctl (0, TIOCSETC, &tc);
  778.     ioctl (0, TIOCGLTC, <c);
  779.     oltc = ltc;
  780.     ltc.t_suspc = ltc.t_dsuspc = ltc.t_lnextc = -1;
  781.     ioctl (0, TIOCSLTC, <c);
  782.     ++consdone;
  783. }
  784.  
  785. reset_cons () {
  786.     if (consdone) {
  787.     consb.sg_flags = coflags;
  788.     ioctl (0, TIOCSETP, &consb);
  789.     ioctl (0, TIOCSETC, &otc);
  790.     ioctl (0, TIOCSLTC, &oltc);
  791.     consdone = 0;
  792.     }
  793. }
  794.  
  795. set_tty () {
  796.     struct sgttyb ttyb;
  797.     ioctl (ttyf, TIOCGETP, &ttyb);
  798.     ottyb = ttyb;
  799.     ttyb.sg_flags &= ~ECHO;
  800.     ttyb.sg_flags &= ~CRMOD;
  801.     ttyb.sg_flags |= (RAW|TANDEM);
  802.     if (baud != -1)
  803.     ttyb.sg_ispeed = ttyb.sg_ospeed = baud;
  804.     ioctl (ttyf, TIOCSETP, &ttyb);
  805.     ++ttydone;
  806. }
  807.  
  808. reset_tty () {
  809.     if (ttydone)
  810.     ioctl (ttyf, TIOCSETP, &ottyb);
  811. }
  812.  
  813. Exit (c) {
  814.     flushbuf ();
  815.     reset_tty ();
  816.     reset_cons ();
  817.     if (locked) {
  818.     if (unlink (lockfile) == -1)
  819.         perror (lockfile);
  820.     }
  821.     exit (c);
  822. }
  823.  
  824. Write (f, buf, len) char *buf; {
  825.     if (write (f, buf, len) != len) {
  826.     if (errno == EIO)
  827.         err ("Lost line.\n");
  828.     else {
  829.         perror ("write");
  830.         Exit (1);
  831.     }
  832.     }
  833. }
  834.  
  835. Read (f, buf, len) char *buf; {
  836.     register n;
  837.     if ((n = read (f, buf, len)) == -1) {
  838.     if (errno == EIO)
  839.         err ("Lost line.\n");
  840.     else {
  841.         perror ("read");
  842.         Exit (1);
  843.     }
  844.     }
  845.     if (n == 0)
  846.     err ("EOF on tty.\n");
  847.     return (n);
  848. }
  849.  
  850. char *memsav (s, len) char *s; {
  851.     register char *p;
  852.     p = alloc (len);
  853.     bcopy (s, p, len);
  854.     return (p);
  855. }
  856.  
  857. char *alloc (n) {
  858.     register char *p;
  859.     if ((p = malloc (n)) == 0)
  860.     err ("Out of memory.\n");
  861.     return (p);
  862. }
  863.  
  864. cook (dst, src) char *dst, *src; {
  865.     register char *p, *t;
  866.     register n, c;
  867.  
  868.     for (p = dst, t = src; c = *t; ++t) {
  869.     if (c == '\\') {
  870.         if (ISOCT(t[1])) {
  871.         for (n = c = 0; n < 3; ++n) {
  872.             c = (c << 3) | (*++t - '0');
  873.             if (!ISOCT(t[1]))
  874.             break;
  875.         }
  876.         } else {
  877.         if (t[1] == 'r') {
  878.             c = '\r'; ++t;
  879.         } else if (t[1] == 'n') {
  880.             c = '\n'; ++t;
  881.         } else if (t[1] == '\0')
  882.             break;
  883.         }
  884.     }
  885.     *p = c; ++p;
  886.     }
  887.     return (p - dst);
  888. }
  889.  
  890. prstr (s, len) char *s; int len; {
  891.     register c, l;
  892.  
  893.     for (l = len; l; --l) {
  894.     if ((c = *s++) == '\r')
  895.         say ("\\r");
  896.     else if (c == '\n')
  897.         say ("\\n");
  898.     else if (c < ' ' || c > '~')
  899.         say ("\\%03o", c & 0377);
  900.     else say ("%c", c);
  901.     }
  902. }
  903.  
  904. fgetstr (f, buf, len) register FILE *f; char *buf; {
  905.     register c, n;
  906.     register char *p = buf;
  907.  
  908.     n = len;
  909.     while ((c = getc (f)) != EOF && c != '\n')
  910.     if (n) {
  911.         --n; *p++ = c;
  912.     }
  913.     *p = '\0';
  914.     return (c == EOF && p == buf ? EOF : p - buf);
  915. }
  916.  
  917. isnum (s) register char *s; {
  918.     register point = 0;
  919.  
  920.     if (*s == '\0') return (0);
  921.     for ( ; *s; ++s) {
  922.     if (*s == '.') {
  923.         if (point) return (0);
  924.         ++point;
  925.     } else if (!ISDEC(*s)) return (0);
  926.     }
  927.     return (1);
  928. }
  929.  
  930. /*VARARGS1*/
  931. say (fmt, p1, p2, p3, p4) char *fmt; {
  932.     fprintf (stdout, fmt, p1, p2, p3, p4);
  933. }
  934.  
  935. /*VARARGS1*/
  936. err (fmt, p1, p2, p3, p4) char *fmt; {
  937.     fprintf (stderr, fmt, p1, p2, p3, p4);
  938.     Exit (1);
  939. }
  940.