home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / expect / expect-4.7 / lib_exp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-07  |  14.8 KB  |  639 lines

  1. /* lib_exp.c - top-level functions in the expect C library, libexpect.a
  2.  
  3. Written by: Don Libes, libes@cme.nist.gov, NIST, 12/3/90
  4.  
  5. Design and implementation of this program was paid for by U.S. tax
  6. dollars.  Therefore it is public domain.  However, the author and NIST
  7. would appreciate credit if this program or parts of it are used.
  8. */
  9.  
  10. #include "exp_conf.h"
  11. #include <stdio.h>
  12. #include <sys/types.h>
  13. #include <sys/ioctl.h>
  14. #ifdef HAVE_SYS_FCNTL_H
  15. #  include <sys/fcntl.h>
  16. #else
  17. #  include <fcntl.h>
  18. #endif
  19. #include <signal.h>
  20. /*#include <memory.h> - deprecated - ANSI C moves them into string.h */
  21. #include "string.h"
  22. #include <varargs.h>
  23. #include <errno.h>
  24. #include "exp_rename.h"
  25. #define EXP_DEFINE_FNS
  26. #include "expect.h"
  27. #include "exp_printify.h"
  28.  
  29. extern char *tclRegexpError;    /* declared in tclInt.h */
  30.  
  31. extern char *sys_errlist[];
  32. extern int errno;
  33.  
  34. #ifdef NO_MEMCPY
  35. #define memcpy(x,y,len) bcopy(y,x,len)
  36. #endif
  37.  
  38. /* avoid colliding with Tcl's stdlib.h */
  39. #ifndef _STDLIB
  40. char *malloc();
  41. void exit();
  42. #endif
  43.  
  44. #ifndef TRUE
  45. #define TRUE 1
  46. #define FALSE 0
  47. #endif
  48.  
  49. #define EXP_MATCH_MAX    2000
  50. /* public */
  51. char *exp_match = 0;
  52. int exp_match_max = EXP_MATCH_MAX;    /* bytes */
  53. int exp_timeout = 10;            /* seconds */
  54. int exp_autoallocpty = TRUE;        /* if TRUE, we do allocation */
  55. int exp_pty[2];                /* master is [0], slave is [1] */
  56. int exp_pid;
  57. char *exp_stty_init = 0;        /* initial stty args */
  58. int exp_ttycopy = TRUE;            /* copy tty parms from /dev/tty */
  59. int exp_ttyinit = TRUE;            /* set tty parms to sane state */
  60. int exp_disconnected = FALSE;        /* not disc. from controlling tty */
  61.  
  62. jmp_buf exp_readenv;        /* for interruptable read() */
  63. int exp_reading = FALSE;    /* whether we can longjmp or not */
  64.  
  65. extern FILE *debugfile;
  66. extern FILE *logfile;
  67. int logfile_all = FALSE;
  68. int loguser = FALSE;
  69.  
  70. void debuglog();
  71. int getptymaster();
  72. int getptyslave();
  73. int Exp_StringMatch();
  74.  
  75. #define sysreturn(x)    return(errno = x, -1)
  76.  
  77. void init_pty();
  78.  
  79. /* returns fd of master side of pty */
  80. int
  81. exp_spawnv(file,argv)
  82. char *file;
  83. char *argv[];    /* some compiler complains about **argv? */
  84. {
  85.     int ttyfd;
  86.  
  87.     static int first_time = TRUE;
  88.  
  89.     if (first_time) {
  90.         first_time = FALSE;
  91.         init_pty();
  92.     }
  93.  
  94.     if (!file || !argv) sysreturn(EINVAL);
  95.     if (!argv[0] || strcmp(file,argv[0])) {
  96.         debuglog("expect: warning: file (%s) != argv[0] (%s)\n",
  97.             file,
  98.             argv[0]?argv[0]:"");
  99.     }
  100.  
  101.     if (exp_autoallocpty) {
  102.         if (0 > (exp_pty[0] = getptymaster())) sysreturn(ENODEV);
  103.     }
  104.     fcntl(exp_pty[0],F_SETFD,1);    /* close on exec */
  105.     if ((exp_pid = fork()) == -1) return(-1);
  106.     if (exp_pid) {
  107.         /* parent */
  108.         if (!exp_autoallocpty) close(exp_pty[1]);
  109.         return(exp_pty[0]);
  110.     }
  111.  
  112.     /* child process - do not return from here!  all errors must exit() */
  113.  
  114. #ifdef POSIX
  115.     setsid();
  116. #else
  117. #ifdef SYSV3
  118.     setpgrp();
  119. #else /* !SYSV3 */
  120. #ifdef MIPS_BSD
  121.     /* required on BSD side of MIPS OS <jmsellen@watdragon.waterloo.edu> */
  122. #    include <sysv/sys.s>
  123.     syscall(SYS_setpgrp);
  124. #endif
  125.     /* if (exp_disconnected) */
  126.     setpgrp(0,0);/* make a new pgrp leader */
  127.     ttyfd = open("/dev/tty", O_RDWR);
  128.     if (ttyfd >= 0) {
  129.         (void) ioctl(ttyfd, TIOCNOTTY, (char *)0);
  130.         (void) close(ttyfd);
  131.     }
  132. #endif /* SYSV3 */
  133. #endif /* POSIX */
  134.     if (exp_autoallocpty) {
  135.         close(0);
  136.         close(1);
  137.         /* leave 2 around awhile for stderr-related stuff */
  138.  
  139.         /* since we closed fd 0, open of pty slave must return fd 0 */
  140.         if (0 > (exp_pty[1] = getptyslave(exp_ttycopy,exp_ttyinit,
  141.                         exp_stty_init))) {
  142.         fprintf(stderr,"open(slave pty): %s\n",sys_errlist[errno]);
  143.         exit(-1);
  144.         }
  145.         /* sanity check */
  146.         if (exp_pty[1] != 0) {
  147.         fprintf(stderr,"getptyslave: slave = %d but expected 0\n",
  148.                                 exp_pty[1]);
  149.         exit(-1);
  150.         }
  151.     } else {
  152.         if (exp_pty[1] != 0) {
  153.             close(0);
  154.             fcntl(exp_pty[1],F_DUPFD,0);
  155.         }
  156.         close(1);
  157.         fcntl(0,F_DUPFD,1);
  158.         close(exp_pty[1]);
  159.     }
  160.  
  161.     /* by now, fd 0 and 1 point to slave pty, so fix 2 */
  162.     close(2);
  163.     fcntl(0,F_DUPFD,2);    /* duplicate 0 onto 2 */
  164.  
  165.     /* (possibly multiple) masters are closed automatically due to */
  166.     /* earlier fcntl(,,CLOSE_ON_EXEC); */
  167.  
  168.         (void) execvp(file,argv);
  169.     /* Unfortunately, by now we've closed fd's to stderr, logfile and
  170.         debugfile.
  171.        The only reasonable thing to do is to send back the error as
  172.        part of the program output.  This will be picked up in an
  173.        expect or interact command.
  174.     */
  175.     fprintf(stderr,"execvp(%s): %s\n",file,sys_errlist[errno]);
  176.     exit(-1);
  177.     /*NOTREACHED*/
  178. }
  179.  
  180. /* returns fd of master side of pty */
  181. /*VARARGS*/
  182. int
  183. exp_spawnl(va_alist)
  184. va_dcl
  185. {
  186.     va_list args;
  187.     int i;
  188.     char *arg, **argv;
  189.  
  190.     va_start(args);
  191.     for (i=0;;i++) {
  192.         arg = va_arg(args,char *);
  193.         if (!arg) break;
  194.     }
  195.     va_end(args);
  196.     if (i == 0) sysreturn(EINVAL);
  197.     if (!(argv = (char **)malloc((i+1)*sizeof(char *)))) sysreturn(ENOMEM);
  198.     va_start(args);
  199.     for (i=0;;i++) {
  200.         argv[i] = va_arg(args,char *);
  201.         if (!argv[i]) break;
  202.     }
  203.     i = exp_spawnv(argv[0],argv+1);
  204.     free((char *)argv);
  205.     return(i);
  206. }
  207.  
  208. /* remove nulls from s.  Initially, the number of chars in s is c, */
  209. /* not strlen(s).  This count does not include the trailing null. */
  210. /* returns number of nulls removed. */
  211. static int
  212. rm_nulls(s,c)
  213. char *s;
  214. int c;
  215. {
  216.     char *s2 = s;    /* points to place in original string to put */
  217.             /* next non-null character */
  218.     int count = 0;
  219.     int i;
  220.  
  221.     for (i=0;i<c;i++,s++) {
  222.         if (0 == *s) {
  223.             count++;
  224.             continue;
  225.         }
  226.         if (count) *s2 = *s;
  227.         s2++;
  228.     }
  229.     return(count);
  230. }
  231.  
  232. static int i_read_errno;/* place to save errno, if i_read() == -1, so it
  233.                doesn't get overwritten before we get to read it */
  234.  
  235. /*ARGSUSED*/
  236. static void
  237. sigalarm_handler(n)
  238. int n;            /* signal number, unused by us */
  239. {
  240. #ifdef REARM_SIG
  241.     signal(SIGALRM,sigalarm_handler);
  242. #endif
  243.  
  244.     longjmp(exp_readenv,1);
  245. }
  246.  
  247. /* interruptable read */
  248. static int
  249. i_read(fd,fp,buffer,length,timeout)
  250. int fd;
  251. FILE *fp;
  252. char *buffer;
  253. int length;
  254. int timeout;
  255. {
  256.     int cc = -2;
  257.  
  258.     /* since setjmp insists on returning 1 upon longjmp(,0), */
  259.     /* longjmp(,2) instead. */
  260.  
  261.     alarm(timeout);
  262.  
  263.     /* restart read if setjmp returns 0 (first time) or 2. */
  264.     /* abort if setjmp returns 1. */
  265.     if (1 != setjmp(exp_readenv)) {
  266.         exp_reading = TRUE;
  267.         if (fd != -1) cc = read(fd,buffer,length);
  268.         else {
  269.             int c;
  270.             c = getc(fp);
  271.             if (c == EOF) {
  272.                 if (feof(fp)) cc = 0;
  273.                 else cc = -1;
  274.             } else {
  275.                 buffer[0] = c;
  276.                 cc = 1;
  277.             }
  278.         }
  279. #if 0
  280.         /* can't get fread to return early! */
  281.         else {
  282.             if (!(cc = fread(buffer,1,length,fp))) {
  283.                 if (ferror(fp)) cc = -1;
  284.             }
  285.         }
  286. #endif
  287.         i_read_errno = errno;    /* errno can be overwritten by the */
  288.                     /* time we return */
  289.     }
  290.     exp_reading = FALSE;
  291.  
  292.     alarm(0);
  293.     return(cc);
  294. }
  295.  
  296. static unsigned int bufsiz = 2*EXP_MATCH_MAX;
  297.  
  298. /* I tried really hard to make the following two functions share the code */
  299. /* that makes the ecase array, but I kept running into a brick wall when */
  300. /* passing var args into the funcs and then again into a make_cases func */
  301. /* I would very much appreciate it if someone showed me how to do it right */
  302.  
  303. /* takes triplets of args, with a final "exp_last" arg */
  304. /* triplets are type, pattern, and then int to return */
  305. /* returns negative value if error (or EOF/timeout) occurs */
  306. /* some negative values can also have an associated errno */
  307.  
  308. static int
  309. expectv(fd,fp,ecases)
  310. int fd;
  311. FILE *fp;
  312. struct exp_case *ecases;
  313. {
  314.     int cc = 0;    /* number of chars returned in a single read */
  315.     int rc = 0;    /* number of chars in exp_match[] */
  316.     int oldrc;
  317.  
  318.     unsigned int new_siz;    /* this is just match_max*2 for efficiency */
  319.     char *new_buf;
  320.     extern char *realloc();
  321.     struct exp_case *ec;    /* points to current ecase */
  322.  
  323.     time_t current_time;        /* current time (when we last looked)*/
  324.     time_t end_time;        /* future time at which to give up */
  325.  
  326.     static int first_time = TRUE;
  327.  
  328.     if (first_time) {
  329.         first_time = FALSE;
  330.         if (!(exp_match = malloc((unsigned)(bufsiz+1))))
  331.             sysreturn(ENOMEM);
  332.     }
  333.  
  334.     if (!ecases) sysreturn(EINVAL);
  335.  
  336.     /* compile if necessary */
  337.     for (ec=ecases;ec->type != exp_end;ec++) {
  338.         if ((ec->type == exp_regexp) && !ec->re) {
  339.             tclRegexpError = 0;
  340.             if (!(ec->re = regcomp(ec->pattern))) {
  341.                 fprintf(stderr,"regular expression %s is bad: %s",ec->pattern,tclRegexpError);
  342.                 sysreturn(EINVAL);
  343.               }
  344.           }
  345.     }
  346.  
  347.     /* get the latest buffer size.  Double the user input for two */
  348.     /* reasons.  1) Need twice the space in case the match */
  349.     /* straddles two bufferfuls, 2) easier to hack the division by */
  350.     /* two when shifting the buffers later on */
  351.     if (bufsiz != (new_siz = 2*exp_match_max)) {
  352.         if (0 == (new_buf = realloc(exp_match,new_siz+1)))
  353.             sysreturn(ENOMEM);
  354.         bufsiz = new_siz;
  355.         exp_match = new_buf;
  356.     }
  357.  
  358.     exp_match[0] = '\0';
  359.     signal(SIGALRM,sigalarm_handler);
  360.  
  361.     time(¤t_time);
  362.     /* if user sets timeout to 0 (i.e. poll), do the next best */
  363.     /* thing: wait only one second.  Eventually polling should be */
  364.     /* implemented right, but I don't consider it high priority */
  365.     /* at the moment, especially cause select command can do it */
  366.     end_time = current_time + ((exp_timeout == 0)?1:exp_timeout);
  367.  
  368.     for (;;) {
  369.         /* when buffer fills, copy second half over first and */
  370.         /* continue, so we can do matches over multiple buffers */
  371.         if (rc == bufsiz) {
  372.             memcpy(exp_match,&exp_match[bufsiz/2],bufsiz/2);
  373.             rc = bufsiz/2;
  374.         }
  375.  
  376.         cc = i_read(fd,fp,&exp_match[rc],bufsiz-rc,
  377.             end_time-current_time);
  378.  
  379.         if (cc == 0) return(EXP_EOF);        /* normal EOF */
  380.         else if (cc == -1) {            /* abnormal EOF */
  381.             /* ptys produce EIO upon EOF - sigh */
  382.             if (i_read_errno == EIO) {
  383.                 /* convert to EOF indication */
  384.                 return(EXP_EOF);
  385.             }
  386.             sysreturn(i_read_errno);
  387.         } else if (cc == -2) return(EXP_TIMEOUT);
  388.  
  389.         oldrc = rc;
  390.         rc += cc;
  391.  
  392.         if (logfile_all || (loguser && logfile)) {
  393.             fwrite(&exp_match[oldrc],1,cc,logfile);
  394.         }
  395.         if (loguser) fwrite(&exp_match[oldrc],1,cc,stdout);
  396.         if (debugfile) fwrite(&exp_match[oldrc],1,cc,debugfile);
  397.  
  398.         /* if we wrote to any logs, flush them */
  399.         if (debugfile) fflush(debugfile);
  400.         if (loguser) {
  401.             fflush(stdout);
  402.             if (logfile) fflush(logfile);
  403.         }
  404.  
  405.         /* remove nulls from input, so we can use C-style strings */
  406.         /* doing it here lets them be sent to the screen, just */
  407.         /*  in case they are involved in formatting operations */
  408.         rc -= rm_nulls(&exp_match[oldrc],cc);
  409.         /* cc should be decremented as well, but since it will not */
  410.         /* be used before being set again, there is no need */
  411.         exp_match[rc] = '\0';
  412.  
  413.         debuglog("expect: does {%s} match ",exp_printify(exp_match));
  414.         /* pattern supplied */
  415.         for (ec=ecases;ec->type != exp_end;ec++) {
  416.             int rc;
  417.             rc = 0;
  418.  
  419.             debuglog("{%s}? ",exp_printify(ec->pattern));
  420.             if (ec->type == exp_glob) {
  421.                 int offset;
  422.                 if (-1 != (Exp_StringMatch(exp_match,ec->pattern,&offset))) {
  423.                     rc = 1;
  424.                 }
  425.             } else {
  426.                 tclRegexpError = 0;
  427.                 if (regexec(ec->re,exp_match)) {
  428.                     rc = 1;
  429.                 } else if (tclRegexpError) {
  430.                         fprintf(stderr,"r.e. match (pattern %s) failed: %s",ec->pattern,tclRegexpError);
  431.                 }
  432.  
  433.             }
  434.             if (rc) {
  435.                 debuglog("yes\nexp_match is {%s}\n",
  436.                         exp_printify(exp_match));
  437.                 return(ec->value);
  438.             } else debuglog("no\n");
  439.         }
  440.     }
  441. }
  442.  
  443. int
  444. exp_fexpectv(fp,ecases)
  445. FILE *fp;
  446. struct exp_case *ecases;
  447. {
  448.     return(expectv(-1,fp,ecases));
  449. }
  450.  
  451. int
  452. exp_expectv(fd,ecases)
  453. int fd;
  454. struct exp_case *ecases;
  455. {
  456.     return(expectv(fd,(FILE *)0,ecases));
  457. }
  458.  
  459. /*VARARGS*/
  460. int
  461. exp_expectl(va_alist)
  462. va_dcl
  463. {
  464.     va_list args;
  465.     int fd;
  466.     struct exp_case *ec, *ecases;
  467.     int i;
  468.     enum exp_type type;
  469.  
  470.     va_start(args);
  471.     fd = va_arg(args,int);
  472.     /* first just count the arg sets */
  473.     for (i=0;;i++) {
  474.         type = va_arg(args,enum exp_type);
  475.         if (type == exp_end) break;
  476.  
  477.         if (type < 0 || type >= exp_bogus) {
  478.             fprintf(stderr,"bad type (set %d) in exp_expectl\n",i);
  479.             sysreturn(EINVAL);
  480.         }
  481.  
  482.         va_arg(args,char *);        /* COMPUTED BUT NOT USED */
  483.         if (type == exp_compiled) {
  484.             va_arg(args,regexp *);    /* COMPUTED BUT NOT USED */
  485.         }
  486.         va_arg(args,int);        /* COMPUTED BUT NOT USED*/
  487.     }
  488.     va_end(args);
  489.  
  490.     if (!(ecases = (struct exp_case *)
  491.                 malloc((1+i)*sizeof(struct exp_case))))
  492.         sysreturn(ENOMEM);
  493.  
  494.     /* now set up the actual cases */
  495.     va_start(args);
  496.     va_arg(args,int);        /*COMPUTED BUT NOT USED*/
  497.     for (ec=ecases;;ec++) {
  498.         ec->type = va_arg(args,int);
  499.         if (ec->type == exp_end) break;
  500.         ec->pattern = va_arg(args,char *);
  501.         if (ec->type == exp_compiled) {
  502.             ec->re = va_arg(args,regexp *);
  503.         } else {
  504.             ec->re = 0;
  505.         }
  506.         ec->value = va_arg(args,int);
  507.     }
  508.     va_end(args);
  509.     i = expectv(fd,(FILE *)0,ecases);
  510.  
  511.     for (ec=ecases;ec->type != exp_end;ec++) {
  512.         /* free only if regexp and we compiled it for user */
  513.         if (ec->type == exp_regexp) {
  514.             free((char *)ec->re);
  515.         }
  516.     }
  517.     free((char *)ecases);
  518.     return(i);
  519. }
  520.  
  521. int
  522. exp_fexpectl(va_alist)
  523. va_dcl
  524. {
  525.     va_list args;
  526.     FILE *fp;
  527.     struct exp_case *ec, *ecases;
  528.     int i;
  529.     enum exp_type type;
  530.  
  531.     va_start(args);
  532.     fp = va_arg(args,FILE *);
  533.     /* first just count the arg-pairs */
  534.     for (i=0;;i++) {
  535.         type = va_arg(args,enum exp_type);
  536.         if (type == exp_end) break;
  537.  
  538.         if (type < 0 || type >= exp_bogus) {
  539.             fprintf(stderr,"bad type (set %d) in exp_expectl\n",i);
  540.             sysreturn(EINVAL);
  541.         }
  542.  
  543.         va_arg(args,char *);        /* COMPUTED BUT NOT USED */
  544.         if (type == exp_compiled) {
  545.             va_arg(args,regexp *);    /* COMPUTED BUT NOT USED */
  546.         }
  547.         va_arg(args,int);        /* COMPUTED BUT NOT USED*/
  548.     }
  549.     va_end(args);
  550.  
  551.     if (!(ecases = (struct exp_case *)
  552.                     malloc((1+i)*sizeof(struct exp_case))))
  553.         sysreturn(ENOMEM);
  554.  
  555.     va_start(args);
  556.     va_arg(args,FILE *);        /*COMPUTED, BUT NOT USED*/
  557.     for (ec=ecases;;ec++) {
  558.         ec->type = va_arg(args,int);
  559.         if (ec->type == exp_end) break;
  560.         ec->pattern = va_arg(args,char *);
  561.         if (ec->type == exp_compiled) {
  562.             ec->re = va_arg(args,regexp *);
  563.         } else {
  564.             ec->re = 0;
  565.         }
  566.         ec->value = va_arg(args,int);
  567.     }
  568.     va_end(args);
  569.     i = expectv(-1,fp,ecases);
  570.  
  571.     for (ec=ecases;ec->type != exp_end;ec++) {
  572.         /* free only if regexp and we compiled it for user */
  573.         if (ec->type == exp_regexp) {
  574.             free((char *)ec->re);
  575.         }
  576.     }
  577.     free((char *)ecases);
  578.     return(i);
  579. }
  580.  
  581. /* like popen(3) but works in both directions */
  582. FILE *
  583. exp_popen(program)
  584. char *program;
  585. {
  586.     FILE *fp;
  587.     int ec;
  588.  
  589.     if (0 > (ec = exp_spawnl("sh","sh","-c",program,(char *)0))) return(0);
  590.     if (!(fp = fdopen(ec,"r+"))) return(0);
  591.     setbuf(fp,(char *)0);
  592.     return(fp);
  593. }
  594.  
  595. int
  596. exp_disconnect()
  597. {
  598.     int ttyfd;
  599.  
  600. #ifndef EALREADY
  601. #define EALREADY 37
  602. #endif
  603.  
  604.     /* presumably, no stderr, so don't bother with error message */
  605.     if (exp_disconnected) sysreturn(EALREADY);
  606.     exp_disconnected = TRUE;
  607.  
  608.     freopen("/dev/null","r",stdin);
  609.     freopen("/dev/null","w",stdout);
  610.     freopen("/dev/null","w",stderr);
  611.  
  612. #ifdef POSIX
  613.     setsid();
  614. #else
  615. #ifdef SYSV3
  616.     /* put process in our own pgrp, and lose controlling terminal */
  617.     setpgrp();
  618.     signal(SIGHUP,SIG_IGN);
  619.     if (fork()) exit(0);    /* first child exits (as per Stevens, */
  620.     /* UNIX Network Programming, p. 79-80) */
  621.     /* second child process continues as daemon */
  622. #else /* !SYSV3 */
  623. #ifdef MIPS_BSD
  624.     /* required on BSD side of MIPS OS <jmsellen@watdragon.waterloo.edu> */
  625. #    include <sysv/sys.s>
  626.     syscall(SYS_setpgrp);
  627. #endif
  628.     setpgrp(0,getpid());    /* put process in our own pgrp */
  629.     ttyfd = open("/dev/tty", O_RDWR);
  630.     if (ttyfd >= 0) {
  631.         /* zap controlling terminal if we had one */
  632.         (void) ioctl(ttyfd, TIOCNOTTY, (char *)0);
  633.         (void) close(ttyfd);
  634.     }
  635. #endif /* SYSV3 */
  636. #endif /* POSIX */
  637.     return(0);
  638. }
  639.