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}