master xplshn/aruu / cmd / extra / yap / getcomm.c
  1/* Copyright (c) 1985 Ceriel J.H. Jacobs */
  2
  3/*
  4 * Command reader, also executes shell escapes
  5 */
  6
  7#include "getcomm.h"
  8#include "assert.h"
  9#include "commands.h"
 10#include "display.h"
 11#include "getline.h"
 12#include "in_all.h"
 13#include "keys.h"
 14#include "machine.h"
 15#include "main.h"
 16#include "output.h"
 17#include "process.h"
 18#include "prompt.h"
 19#include "term.h"
 20#include <ctype.h>
 21
 22static int  killchar(int c);
 23static void sigquit(int signo);
 24
 25/*
 26 * Read a line from the terminal, doing line editing.
 27 * The parameter s contains the prompt for the line.
 28 */
 29
 30char *
 31readline(char *s)
 32{
 33  static char buf[80];
 34  char       *p = buf;
 35  int         ch;
 36  int         pos;
 37
 38  clrbline();
 39  putline(s);
 40  pos = strlen(s);
 41  while ((ch = getch()) != '\n' && ch != '\r') {
 42    if (ch == -1) {
 43      /*
 44       * Can only occur because of an interrupted read.
 45       */
 46      ch        = erasech;
 47      interrupt = 0;
 48    }
 49    if (ch == erasech) {
 50      /*
 51       * Erase last char
 52       */
 53      if (p == buf) {
 54        /*
 55         * There was none, so return
 56         */
 57        return (char *)0;
 58      }
 59      pos -= killchar(*--p);
 60      if (*p != '\\')
 61        continue;
 62    }
 63    if (ch == killch) {
 64      /*
 65       * Erase the whole line
 66       */
 67      if (!(p > buf && *(p - 1) == '\\')) {
 68        while (p > buf) {
 69          pos -= killchar(*--p);
 70        }
 71        continue;
 72      }
 73      pos -= killchar(*--p);
 74    }
 75    if (p > &buf[78] || pos >= COLS - 2) {
 76      /*
 77       * Line does not fit.
 78       * Simply refuse to make it any longer
 79       */
 80      pos -= killchar(*--p);
 81    }
 82    *p++ = ch;
 83    if (ch < ' ' || ch >= 0177) {
 84      fputch('^');
 85      pos++;
 86      ch ^= 0100;
 87    }
 88    fputch(ch);
 89    pos++;
 90  }
 91  fputch('\r');
 92  *p++ = '\0';
 93  flush();
 94  return buf;
 95}
 96
 97/*
 98 * Erase a character from the command line.
 99 */
100
101static int
102killchar(int c)
103{
104  backspace();
105  putch(' ');
106  backspace();
107  if (c < ' ' || c >= 0177) {
108    (void)killchar(' ');
109    return 2;
110  }
111  return 1;
112}
113
114/*
115 * Do a shell escape, after expanding '%' and '!'.
116 */
117
118void
119shellescape(char *p, int esc_char)
120{
121  char *p2;        /* walks through command */
122  int   id;        /* procid of child */
123  int   cnt;       /* prevent array bound errors */
124  int   lastc = 0; /* will contain the previous char */
125#ifdef SIGTSTP
126  void (*savetstp)(int);
127#endif
128  static char previous[256]; /* previous command */
129  char        comm[256];     /* space for command */
130  int         piped[2];
131
132  p2    = comm;
133  *p2++ = esc_char;
134  cnt   = 253;
135  while (*p) {
136    /*
137     * expand command
138     */
139    switch (*p++) {
140      case '!':
141        /*
142         * An unescaped ! expands to the previous
143         * command, but disappears if there is none
144         */
145        if (lastc != '\\') {
146          if (*previous) {
147            id = strlen(previous);
148            if ((cnt -= id) <= 0)
149              break;
150            (void)strcpy(p2, previous);
151            p2 += id;
152          }
153        } else {
154          *(p2 - 1) = '!';
155        }
156        continue;
157      case '%':
158        /*
159         * An unescaped % will expand to the current
160         * filename, but disappears is there is none
161         */
162        if (lastc != '\\') {
163          if (nopipe) {
164            id = strlen(currentfile);
165            if ((cnt -= id) <= 0)
166              break;
167            (void)strcpy(p2, currentfile);
168            p2 += id;
169          }
170        } else {
171          *(p2 - 1) = '%';
172        }
173        continue;
174      default:
175        lastc = *(p - 1);
176        if (cnt-- <= 0)
177          break;
178        *p2++ = lastc;
179        continue;
180    }
181    break;
182  }
183  clrbline();
184  *p2 = '\0';
185  if (!stupid) {
186    /*
187     * Display expanded command
188     */
189    cputline(comm);
190    putline("\r\n");
191  }
192  flush();
193  (void)strcpy(previous, comm + 1);
194  resettty();
195  if (esc_char == '|' && pipe(piped) < 0) {
196    error("Cannot create pipe");
197    return;
198  }
199  if ((id = fork()) < 0) {
200    error("Cannot fork");
201    return;
202  }
203  if (id == 0) {
204    if (esc_char == '|') {
205      close(piped[1]);
206      close(0);
207      fcntl(piped[0], F_DUPFD, 0);
208      close(piped[0]);
209    }
210    execl("/bin/sh", "sh", "-c", comm + 1, (char *)0);
211    exit(1);
212  }
213  (void)signal(SIGINT, SIG_IGN);
214  (void)signal(SIGQUIT, SIG_IGN);
215#ifdef SIGTSTP
216  if ((savetstp = signal(SIGTSTP, SIG_IGN)) != SIG_IGN) {
217    (void)signal(SIGTSTP, SIG_DFL);
218  }
219#endif
220  if (esc_char == '|') {
221    (void)close(piped[0]);
222    (void)signal(SIGPIPE, SIG_IGN);
223    wrt_fd(piped[1]);
224    (void)close(piped[1]);
225  }
226  while ((lastc = wait((int *)0)) != id && lastc >= 0) {
227    /*
228     * Wait for child, making sure it is the one we expected ...
229     */
230  }
231  (void)signal(SIGINT, catchdel);
232  (void)signal(SIGQUIT, sigquit);
233#ifdef SIGTSTP
234  (void)signal(SIGTSTP, savetstp);
235#endif
236  inittty();
237}
238
239static void
240sigquit(int signo)
241{
242  (void)signo;
243  quit();
244}
245
246/*
247 * Get all those commands ...
248 */
249
250int
251getcomm(long *plong)
252{
253  int   c;
254  long  count;
255  char *p;
256  int   i;
257  int   j;
258  char  buf[10];
259
260  for (;;) {
261    count = 0;
262    give_prompt();
263    while (isdigit((c = getch()))) {
264      count = count * 10 + (c - '0');
265    }
266    *plong = count;
267    p      = buf;
268    for (;;) {
269      if (c == -1) {
270        /*
271         * This should never happen, but it does,
272         * when the user gives a TSTP signal (^Z) or
273         * an interrupt while the program is trying
274         * to read a character from the terminal.
275         * In this case, the read is interrupted, so
276         * we end up here.
277         * Right, we will have to read again.
278         */
279        if (interrupt)
280          return 1;
281        break;
282      }
283      *p++ = c;
284      *p   = 0;
285      if ((i = match(buf, &j, currmap->k_mach)) > 0) {
286        /*
287         * The key sequence matched. We have a command
288         */
289        return j;
290      }
291      if (i == 0)
292        return 0;
293      /*
294       * We have a prefix of a command.
295       */
296      assert(i == FSM_ISPREFIX);
297      c = getch();
298    }
299  }
300  /* NOTREACHED */
301}