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}