date and time created 80/10/08 18:55:05 by bill
[unix-history] / usr / src / usr.bin / mail / lex.c
CommitLineData
ac72cd41
KS
1#
2
3#include "rcv.h"
4
5/*
6 * Mail -- a mail program
7 *
8 * Lexical processing of commands.
9 */
10
11static char *SccsId = "@(#)lex.c 1.1 %G%";
12
13/*
14 * Interpret user commands one by one. If standard input is not a tty,
15 * print no prompt.
16 */
17
18int *msgvec;
19
20commands()
21{
22 int prompt, firstsw, stop();
23 register int n;
24 char linebuf[LINESIZE];
25
26 msgvec = (int *) calloc((unsigned) (msgCount + 1), sizeof *msgvec);
27 if (rcvmode)
28 if (signal(SIGINT, SIG_IGN) == SIG_DFL)
29 signal(SIGINT, stop);
30 input = stdin;
31 prompt = 1;
32 if (!intty)
33 prompt = 0;
34 firstsw = 1;
35 for (;;) {
36 setexit();
37 if (firstsw > 0) {
38 firstsw = 0;
39 source1(mailrc);
40 if (!nosrc)
41 source1(MASTER);
42 }
43
44 /*
45 * How's this for obscure: after we
46 * finish sourcing for the first time,
47 * go off and print the headers!
48 */
49
50 if (firstsw == 0 && !sourcing) {
51 firstsw = -1;
52 if (rcvmode)
53 announce();
54 }
55
56 /*
57 * Print the prompt, if needed. Clear out
58 * string space, and flush the output.
59 */
60
61 if (!rcvmode && !sourcing)
62 return;
63 if (prompt && !sourcing)
64 printf("_\r");
65 flush();
66 sreset();
67
68 /*
69 * Read a line of commands from the current input
70 * and handle end of file specially.
71 */
72
73 n = 0;
74 for (;;) {
75 if (readline(input, &linebuf[n]) <= 0) {
76 if (n != 0)
77 break;
78 if (sourcing) {
79 unstack();
80 goto more;
81 }
82 if (!edit) {
83 signal(SIGINT, SIG_IGN);
84 return;
85 }
86 edstop();
87 return;
88 }
89 if ((n = strlen(linebuf)) == 0)
90 break;
91 n--;
92 if (linebuf[n] != '\\')
93 break;
94 linebuf[n++] = ' ';
95 }
96 if (execute(linebuf))
97 return;
98more: ;
99 }
100}
101
102/*
103 * Execute a single command. If the command executed
104 * is "quit," then return non-zero so that the caller
105 * will know to return back to main, if he cares.
106 */
107
108execute(linebuf)
109 char linebuf[];
110{
111 char word[LINESIZE];
112 char *arglist[MAXARGC];
113 struct cmd *com;
114 register char *cp, *cp2;
115 register int c;
116 int edstop(), e;
117
118 /*
119 * Strip the white space away from the beginning
120 * of the command, then scan out a word, which
121 * consists of anything except digits and white space.
122 *
123 * Handle ! escapes differently to get the correct
124 * lexical conventions.
125 */
126
127 cp = linebuf;
128 while (any(*cp, " \t"))
129 cp++;
130 if (*cp == '!') {
131 if (sourcing) {
132 printf("Can't \"!\" while sourcing\n");
133 unstack();
134 return(0);
135 }
136 shell(cp+1);
137 return(0);
138 }
139 cp2 = word;
140 while (*cp && !any(*cp, " \t0123456789$^.-+*'\""))
141 *cp2++ = *cp++;
142 *cp2 = '\0';
143
144 /*
145 * Look up the command; if not found, bitch.
146 * Normally, a blank command would map to the
147 * first command in the table; while sourcing,
148 * however, we ignore blank lines to eliminate
149 * confusion.
150 */
151
152 if (sourcing && equal(word, ""))
153 return(0);
154 com = lex(word);
155 if (com == NONE) {
156 printf("What?\n");
157 if (sourcing)
158 unstack();
159 return(0);
160 }
161
162 /*
163 * Special case so that quit causes a return to
164 * main, who will call the quit code directly.
165 * If we are in a source file, just unstack.
166 */
167
168 if (com->c_func == edstop && sourcing) {
169 unstack();
170 return(0);
171 }
172 if (!edit && com->c_func == edstop) {
173 signal(SIGINT, SIG_IGN);
174 return(1);
175 }
176
177 /*
178 * Process the arguments to the command, depending
179 * on the type he expects. Default to an error.
180 * If we are sourcing an interactive command, it's
181 * an error.
182 */
183
184 if (!rcvmode && (com->c_argtype & M) == 0) {
185 printf("May not execute \"%s\" while sending\n",
186 com->c_name);
187 unstack();
188 return(0);
189 }
190 if (sourcing && com->c_argtype & I) {
191 printf("May not execute \"%s\" while sourcing\n",
192 com->c_name);
193 unstack();
194 return(0);
195 }
196 e = 1;
197 switch (com->c_argtype & ~(P|I|M)) {
198 case MSGLIST:
199 /*
200 * A message list defaulting to nearest forward
201 * legal message.
202 */
203 if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
204 break;
205 if (c == 0) {
206 *msgvec = first(com->c_msgflag,
207 com->c_msgmask);
208 msgvec[1] = NULL;
209 }
210 if (*msgvec == NULL) {
211 printf("No applicable messages\n");
212 break;
213 }
214 e = (*com->c_func)(msgvec);
215 break;
216
217 case NDMLIST:
218 /*
219 * A message list with no defaults, but no error
220 * if none exist.
221 */
222 if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
223 break;
224 e = (*com->c_func)(msgvec);
225 break;
226
227 case STRLIST:
228 /*
229 * Just the straight string, with
230 * leading blanks removed.
231 */
232 while (any(*cp, " \t"))
233 cp++;
234 e = (*com->c_func)(cp);
235 break;
236
237 case RAWLIST:
238 /*
239 * A vector of strings, in shell style.
240 */
241 if ((c = getrawlist(cp, arglist)) < 0)
242 break;
243 if (c < com->c_minargs) {
244 printf("%s requires at least %d arg(s)\n",
245 com->c_name, com->c_minargs);
246 break;
247 }
248 if (c > com->c_maxargs) {
249 printf("%s takes no more than %d arg(s)\n",
250 com->c_name, com->c_maxargs);
251 break;
252 }
253 e = (*com->c_func)(arglist);
254 break;
255
256 case NOLIST:
257 /*
258 * Just the constant zero, for exiting,
259 * eg.
260 */
261 e = (*com->c_func)(0);
262 break;
263
264 default:
265 panic("Unknown argtype");
266 }
267
268 /*
269 * Exit the current source file on
270 * error.
271 */
272
273 if (e && sourcing)
274 unstack();
275 if (com->c_func == edstop)
276 return(1);
277 if (value("autoprint") != NOSTR && com->c_argtype & P)
278 if ((dot->m_flag & MDELETED) == 0)
279 print(dot);
280 if (!sourcing)
281 sawcom = 1;
282 return(0);
283}
284
285/*
286 * Find the correct command in the command table corresponding
287 * to the passed command "word"
288 */
289
290struct cmd *
291lex(word)
292 char word[];
293{
294 register struct cmd *cp;
295 extern struct cmd cmdtab[];
296
297 for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
298 if (isprefix(word, cp->c_name))
299 return(cp);
300 return(NONE);
301}
302
303/*
304 * Determine if as1 is a valid prefix of as2.
305 * Return true if yep.
306 */
307
308isprefix(as1, as2)
309 char *as1, *as2;
310{
311 register char *s1, *s2;
312
313 s1 = as1;
314 s2 = as2;
315 while (*s1++ == *s2)
316 if (*s2++ == '\0')
317 return(1);
318 return(*--s1 == '\0');
319}
320
321/*
322 * The following gets called on receipt of a rubout. This is
323 * to abort printout of a command, mainly.
324 * Dispatching here when command() is inactive crashes rcv.
325 * Close all open files except 0, 1, 2, and the temporary.
326 * The special call to getuserid() is needed so it won't get
327 * annoyed about losing its open file.
328 * Also, unstack all source files.
329 */
330
331stop()
332{
333 register FILE *fp;
334
335 noreset = 0;
336 signal(SIGINT, SIG_IGN);
337 sawcom++;
338 while (sourcing)
339 unstack();
340 getuserid((char *) -1);
341 for (fp = &_iob[0]; fp < &_iob[_NFILE]; fp++) {
342 if (fp == stdin || fp == stdout)
343 continue;
344 if (fp == itf || fp == otf)
345 continue;
346 if (fp == stderr)
347 continue;
348 fclose(fp);
349 }
350 if (image >= 0) {
351 close(image);
352 image = -1;
353 }
354 clrbuf(stdout);
355 printf("Interrupt\n");
356 signal(SIGINT, stop);
357 reset(0);
358}
359
360/*
361 * Announce the presence of the current Mail version,
362 * give the message count, and print a header listing.
363 */
364
365char *greeting = "Mail version 2.0 %s. Type ? for help.\n";
366
367announce()
368{
369 int vec[2];
370 extern char *version;
371 register struct message *mp;
372
373 if (value("hold") != NOSTR)
374 for (mp = &message[0]; mp < &message[msgCount]; mp++)
375 mp->m_flag |= MPRESERVE;
376 vec[0] = 1;
377 vec[1] = 0;
378 if (value("quiet") == NOSTR)
379 printf(greeting, version);
380 if (msgCount == 1)
381 printf("1 message:\n");
382 else
383 printf("%d messages:\n", msgCount);
384 headers(vec);
385}
386
387strace() {}
388
389/*
390 * Print the current version number.
391 */
392
393pversion(e)
394{
395 printf(greeting, version);
396 return(0);
397}