home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume20 / rc / part02 / input.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-05-22  |  6.8 KB  |  350 lines

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