home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume30 / rc / part03 / input.c < prev    next >
C/C++ Source or Header  |  1992-05-30  |  8KB  |  374 lines

  1. /* input.c: i/o routines for files and pseudo-files (strings) */
  2.  
  3. #include <errno.h>
  4. #include <setjmp.h>
  5. #include "rc.h"
  6. #include "jbwrap.h"
  7.  
  8. /*
  9.    NB: character unget is supported for up to two characters, but NOT
  10.    in the case of EOF. Since EOF does not fit in a char, it is easiest
  11.    to support only one unget of EOF.
  12. */
  13.  
  14. typedef struct Input {
  15.     inputtype t;
  16.     char *ibuf;
  17.     int fd, index, read, lineno, last;
  18.     bool saved, eofread;
  19. } Input;
  20.  
  21. #define BUFSIZE ((SIZE_T) 256)
  22.  
  23. #ifdef READLINE
  24. extern char *readline(char *);
  25. extern void add_history(char *);
  26. static char *rlinebuf;
  27. #endif
  28.  
  29. char *prompt, *prompt2;
  30. bool rcrc;
  31.  
  32. static int dead(void);
  33. static int fdgchar(void);
  34. static int stringgchar(void);
  35. static void history(void);
  36. static void ugdead(int);
  37. static void pushcommon(void);
  38.  
  39. static char *inbuf;
  40. static SIZE_T istacksize, chars_out, chars_in;
  41. static bool eofread = FALSE, save_lineno = TRUE;
  42. static Input *istack, *itop;
  43. static char *histstr;
  44. static int histfd;
  45.  
  46. static int (*realgchar)(void);
  47. static void (*realugchar)(int);
  48.  
  49. int last;
  50.  
  51. extern int gchar() {
  52.     if (eofread) {
  53.         eofread = FALSE;
  54.         return last = EOF;
  55.     }
  56.     return (*realgchar)();
  57. }
  58.  
  59. extern void ugchar(int c) {
  60.     (*realugchar)(c);
  61. }
  62.  
  63. static int dead() {
  64.     return last = EOF;
  65. }
  66.  
  67. static void ugdead(int c) {
  68.     return;
  69. }
  70.  
  71. static void ugalive(int c) {
  72.     if (c == EOF)
  73.         eofread = TRUE;
  74.     else
  75.         inbuf[--chars_out] = c;
  76. }
  77.  
  78. /* get the next character from a string. */
  79.  
  80. static int stringgchar() {
  81.     return last = (inbuf[chars_out] == '\0' ? EOF : inbuf[chars_out++]);
  82. }
  83.  
  84. /* signal-safe readline wrapper */
  85.  
  86. #ifdef READLINE
  87. #ifndef SVSIGS
  88. static char *rc_readline(char *prompt) {
  89.     char *r;
  90.     interrupt_happened = FALSE;
  91.     if (!setjmp(slowbuf.j)) {
  92.         slow = TRUE;
  93.         if (!interrupt_happened)
  94.             r = readline(prompt);
  95.         else
  96.             r = NULL;
  97.     } else
  98.         r = NULL;
  99.     slow = FALSE;
  100.     if (r == NULL)
  101.         errno = EINTR;
  102.     SIGCHK;
  103.     return r;
  104. }
  105. #else
  106. #define rc_readline readline
  107. #endif /* SVSIGS */
  108. #endif /* READLINE */
  109.  
  110. /*
  111.    read a character from a file-descriptor. If GNU readline is defined, add a newline and doctor
  112.    the buffer to look like a regular fdgchar buffer.
  113. */
  114.  
  115. static int fdgchar() {
  116.     if (chars_out >= chars_in + 2) { /* has the buffer been exhausted? if so, replenish it */
  117.         while (1) {
  118. #ifdef READLINE
  119.             if (interactive && istack->fd == 0) {
  120.                 rlinebuf = readline(prompt);
  121.                 if (rlinebuf == NULL) {
  122.                     chars_in = 0;
  123.                 } else {
  124.                     if (*rlinebuf != '\0')
  125.                         add_history(rlinebuf);
  126.                     chars_in = strlen(rlinebuf) + 1;
  127.                     efree(inbuf);
  128.                     inbuf = ealloc(chars_in + 3);
  129.                     strcpy(inbuf+2, rlinebuf);
  130.                     strcat(inbuf+2, "\n");
  131.                 }
  132.             } else
  133. #endif
  134.                 {
  135.                 long /*ssize_t*/ r = rc_read(istack->fd, inbuf + 2, BUFSIZE);
  136.                 if (r < 0) {
  137.                     if (errno == EINTR)
  138.                         continue; /* Suppose it was interrupted by a signal */
  139.                     uerror("read");
  140.                     rc_exit(1);
  141.                 }
  142.                 chars_in = (SIZE_T) r;
  143.             }
  144.             break;
  145.         }
  146.         if (chars_in == 0)
  147.             return last = EOF;
  148.         chars_out = 2;
  149.         if (dashvee)
  150.             writeall(2, inbuf + 2, chars_in);
  151.         history();
  152.     }
  153.     return last = inbuf[chars_out++];
  154. }
  155.  
  156. /* set up the input stack, and put a "dead" input at the bottom, so that yyparse will always read eof */
  157.  
  158. extern void initinput() {
  159.     istack = itop = ealloc(istacksize = 256 * sizeof (Input));
  160.     istack->t = iFd;
  161.     istack->fd = -1;
  162.     realugchar = ugalive;
  163. }
  164.  
  165. /* push an input source onto the stack. set up a new input buffer, and set gchar() */
  166.  
  167. static void pushcommon() {
  168.     SIZE_T idiff;
  169.     istack->index = chars_out;
  170.     istack->read = chars_in;
  171.     istack->ibuf = inbuf;
  172.     istack->lineno = lineno;
  173.     istack->saved = save_lineno;
  174.     istack->last = last;
  175.     istack->eofread = eofread;
  176.     istack++;
  177.     idiff = istack - itop;
  178.     if (idiff >= istacksize / sizeof (Input)) {
  179.         itop = erealloc(itop, istacksize *= 2);
  180.         istack = itop + idiff;
  181.     }
  182.     realugchar = ugalive;
  183.     chars_out = 2;
  184.     chars_in = 0;
  185. }
  186.  
  187. extern void pushfd(int fd) {
  188.     pushcommon();
  189.     istack->t = iFd;
  190.     save_lineno = TRUE;
  191.     istack->fd = fd;
  192.     realgchar = fdgchar;
  193.     inbuf = ealloc(BUFSIZE + 2);
  194.     lineno = 1;
  195. }
  196.  
  197. extern void pushstring(char **a, bool save) {
  198.     pushcommon();
  199.     istack->t = iString;
  200.     save_lineno = save;
  201.     inbuf = mprint("..%A", a);
  202.     realgchar = stringgchar;
  203.     if (save_lineno)
  204.         lineno = 1;
  205.     else
  206.         --lineno;
  207. }
  208.  
  209. /* remove an input source from the stack. restore the right kind of getchar (string,fd) etc. */
  210.  
  211. extern void popinput() {
  212.     if (istack->t == iFd)
  213.         close(istack->fd);
  214.     efree(inbuf);
  215.     --istack;
  216.     realgchar = (istack->t == iString ? stringgchar : fdgchar);
  217.     if (istack->fd == -1) { /* top of input stack */
  218.         realgchar = dead;
  219.         realugchar = ugdead;
  220.     }
  221.     last = istack->last;
  222.     eofread = istack->eofread;
  223.     inbuf = istack->ibuf;
  224.     chars_out = istack->index;
  225.     chars_in = istack->read;
  226.     if (save_lineno)
  227.         lineno = istack->lineno;
  228.     else
  229.         lineno++;
  230.     save_lineno = istack->saved;
  231. }
  232.  
  233. /* flush input characters upto newline. Used by scanerror() */
  234.  
  235. extern void flushu() {
  236.     int c;
  237.     if (last == '\n' || last == EOF)
  238.         return;
  239.     while ((c = gchar()) != '\n' && c != EOF)
  240.         ; /* skip to newline */
  241.     if (c == EOF)
  242.         ugchar(c);
  243. }
  244.  
  245. /* the wrapper loop in rc: prompt for commands until EOF, calling yyparse and walk() */
  246.  
  247. extern Node *doit(bool execit) {
  248.     bool eof;
  249.     Jbwrap j;
  250.     Estack e1, e2;
  251.     Edata jerror;
  252.     if (dashen)
  253.         execit = FALSE;
  254.     setjmp(j.j);
  255.     jerror.jb = &j;
  256.     except(eError, jerror, &e1);
  257.     for (eof = FALSE; !eof;) {
  258.         Edata block;
  259.         block.b = newblock();
  260.         except(eArena, block, &e2);
  261.         SIGCHK;
  262.         if (dashell) {
  263.             char *fname[3];
  264.             fname[1] = concat(varlookup("home"), word("/.rcrc", NULL))->w;
  265.             fname[2] = NULL;
  266.             rcrc = TRUE;
  267.             dashell = FALSE;
  268.             b_dot(fname);
  269.         }
  270.         if (interactive) {
  271.             List *s;
  272.             if (!dashen && fnlookup("prompt") != NULL) {
  273.                 static char *arglist[] = { "prompt", NULL };
  274.                 funcall(arglist);
  275.             }
  276.             if ((s = varlookup("prompt")) != NULL) {
  277. #ifdef READLINE
  278.                 prompt = s->w;
  279. #else
  280.                 fprint(2, "%s", s->w);
  281. #endif
  282.                 prompt2 = (s->n == NULL ? "" : s->n->w);
  283.             }
  284.         }
  285.         inityy();
  286.         if (yyparse() == 1 && execit)
  287.             rc_raise(eError);
  288.         eof = (last == EOF); /* "last" can be clobbered during a walk() */
  289.         if (parsetree != NULL) {
  290.             if (execit)
  291.                 walk(parsetree, TRUE);
  292.             else if (dashex && dashen)
  293.                 fprint(2, "%T\n", parsetree);
  294.         }
  295.         unexcept(); /* eArena */
  296.     }
  297.     popinput();
  298.     unexcept(); /* eError */
  299.     return parsetree;
  300. }
  301.  
  302. /* parse a function imported from the environment */
  303.  
  304. extern Node *parseline(char *extdef) {
  305.     int i = interactive;
  306.     char *in[2];
  307.     Node *fun;
  308.     in[0] = extdef;
  309.     in[1] = NULL;
  310.     interactive = FALSE;
  311.     pushstring(in, TRUE);
  312.     fun = doit(FALSE);
  313.     interactive = i;
  314.     return fun;
  315. }
  316.  
  317. /* write last command out to a file. Check to see if $history has changed, also */
  318.  
  319. static void history() {
  320.     List *histlist;
  321.     SIZE_T a;
  322.     if (!interactive)
  323.         return;
  324.     if ((histlist = varlookup("history")) == NULL) {
  325.         if (histstr != NULL) {
  326.             efree(histstr);
  327.             close(histfd);
  328.             histstr = NULL;
  329.         }
  330.         return;
  331.     }
  332.     if (histstr == NULL || !streq(histstr, histlist->w)) { /* open new file */
  333.         if (histstr != NULL) {
  334.             efree(histstr);
  335.             close(histfd);
  336.         }
  337.         histstr = ecpy(histlist->w);
  338.         histfd = rc_open(histstr, rAppend);
  339.         if (histfd < 0) {
  340.             uerror(histstr);
  341.             efree(histstr);
  342.             histstr = NULL;
  343.             varrm("history", FALSE);
  344.         }
  345.     }
  346.     /*
  347.        Small unix hack: since read() reads only up to a newline
  348.        from a terminal, then presumably this write() will write at
  349.        most only one input line at a time.
  350.     */
  351.     for (a = 2; a < chars_in + 2; a++) { /* skip empty lines and comments in history. */
  352.         if (inbuf[a] == '#' || inbuf[a] == '\n')
  353.             return;
  354.         if (inbuf[a] != ' ' && inbuf[a] != '\t')
  355.             break;
  356.     }
  357.     writeall(histfd, inbuf + 2, chars_in);
  358. }
  359.  
  360. /* close file descriptors after a fork() */
  361.  
  362. extern void closefds() {
  363.     Input *i;
  364.     if (histstr != NULL) {            /* Close an open history file */
  365.         close(histfd);
  366.         histstr = NULL;            /* But prevent re-closing of the same file-descriptor */
  367.     }
  368.     for (i = istack; i != itop; --i)    /* close open scripts */
  369.         if (i->t == iFd && i->fd > 2) {
  370.             close(i->fd);
  371.             i->fd = -1;
  372.         }
  373. }
  374.