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