added global flag shudann which causes lex() to print out
[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
335704ca 11static char *SccsId = "@(#)lex.c 1.4 %G%";
e5717bff
KS
12
13/*
14 * Set up editing on the given file name.
15 * If isedit is true, we are considered to be editing the file,
16 * otherwise we are reading our mail which has signficance for
17 * mbox and so forth.
18 */
19
20setfile(name, isedit)
21 char *name;
22{
23 FILE *ibuf;
24 int i;
25 static int shudclob;
26 static char efile[128];
27 extern char tempMesg[];
28 int (*sigs[2])();
29
30 if ((ibuf = fopen(name, "r")) == NULL) {
31 if (isedit)
32 perror(name);
33 else
34 printf("No mail for %s\n", myname);
35 return(-1);
36 }
37
38 /*
39 * Looks like all will be well. We must now relinquish our
40 * hold on the current set of stuff. Must ignore signals
41 * while we are reading the new file, else we will ruin
42 * the message[] data structure.
43 */
44
45 for (i = SIGINT; i <= SIGQUIT; i++)
46 sigs[i - SIGINT] = signal(i, SIG_IGN);
47 if (shudclob) {
48 if (edit)
49 edstop();
50 else
51 quit();
52 }
53
54 /*
55 * Copy the messages into /tmp
56 * and set pointers.
57 */
58
59 readonly = 0;
60 if ((i = open(name, 1)) < 0)
61 readonly++;
62 else
63 close(i);
64 if (shudclob) {
65 fclose(itf);
66 fclose(otf);
67 }
68 shudclob = 1;
69 edit = isedit;
70 strncpy(efile, name, 128);
71 editfile = efile;
72 mailsize = fsize(ibuf);
73 if ((otf = fopen(tempMesg, "w")) == NULL) {
74 perror(tempMesg);
75 exit(1);
76 }
77 if ((itf = fopen(tempMesg, "r")) == NULL) {
78 perror(tempMesg);
79 exit(1);
80 }
81 remove(tempMesg);
82 setptr(ibuf);
83 setmsize(msgCount);
84 fclose(ibuf);
85 for (i = SIGINT; i <= SIGQUIT; i++)
86 signal(i, sigs[i - SIGINT]);
87 printf("%s: ", name);
88 announce(!edit);
335704ca 89 sawcom = 0;
e5717bff
KS
90 return(0);
91}
ac72cd41
KS
92
93/*
94 * Interpret user commands one by one. If standard input is not a tty,
95 * print no prompt.
96 */
97
98int *msgvec;
99
100commands()
101{
102 int prompt, firstsw, stop();
103 register int n;
104 char linebuf[LINESIZE];
105
ac72cd41
KS
106 if (rcvmode)
107 if (signal(SIGINT, SIG_IGN) == SIG_DFL)
108 signal(SIGINT, stop);
109 input = stdin;
110 prompt = 1;
111 if (!intty)
112 prompt = 0;
113 firstsw = 1;
114 for (;;) {
115 setexit();
116 if (firstsw > 0) {
117 firstsw = 0;
118 source1(mailrc);
119 if (!nosrc)
120 source1(MASTER);
121 }
122
123 /*
124 * How's this for obscure: after we
125 * finish sourcing for the first time,
126 * go off and print the headers!
127 */
128
e5717bff 129#ifdef CRAZYWOW
ac72cd41
KS
130 if (firstsw == 0 && !sourcing) {
131 firstsw = -1;
132 if (rcvmode)
e5717bff 133 announce(1);
ac72cd41 134 }
e5717bff 135#endif
ac72cd41
KS
136
137 /*
138 * Print the prompt, if needed. Clear out
139 * string space, and flush the output.
140 */
141
142 if (!rcvmode && !sourcing)
143 return;
73f94fab 144top:
ac72cd41
KS
145 if (prompt && !sourcing)
146 printf("_\r");
147 flush();
148 sreset();
149
150 /*
151 * Read a line of commands from the current input
152 * and handle end of file specially.
153 */
154
155 n = 0;
156 for (;;) {
157 if (readline(input, &linebuf[n]) <= 0) {
158 if (n != 0)
159 break;
160 if (sourcing) {
161 unstack();
162 goto more;
163 }
73f94fab
KS
164 if (value("ignoreeof") != NOSTR && prompt) {
165 printf("Use \"quit\" to quit.\n");
166 goto top;
167 }
ac72cd41
KS
168 if (!edit) {
169 signal(SIGINT, SIG_IGN);
170 return;
171 }
172 edstop();
173 return;
174 }
175 if ((n = strlen(linebuf)) == 0)
176 break;
177 n--;
178 if (linebuf[n] != '\\')
179 break;
180 linebuf[n++] = ' ';
181 }
182 if (execute(linebuf))
183 return;
184more: ;
185 }
186}
187
188/*
189 * Execute a single command. If the command executed
190 * is "quit," then return non-zero so that the caller
191 * will know to return back to main, if he cares.
192 */
193
194execute(linebuf)
195 char linebuf[];
196{
197 char word[LINESIZE];
198 char *arglist[MAXARGC];
199 struct cmd *com;
200 register char *cp, *cp2;
201 register int c;
e5717bff 202 int muvec[2];
ac72cd41
KS
203 int edstop(), e;
204
205 /*
206 * Strip the white space away from the beginning
207 * of the command, then scan out a word, which
208 * consists of anything except digits and white space.
209 *
210 * Handle ! escapes differently to get the correct
211 * lexical conventions.
212 */
213
214 cp = linebuf;
215 while (any(*cp, " \t"))
216 cp++;
217 if (*cp == '!') {
218 if (sourcing) {
219 printf("Can't \"!\" while sourcing\n");
220 unstack();
221 return(0);
222 }
223 shell(cp+1);
224 return(0);
225 }
226 cp2 = word;
e5717bff 227 while (*cp && !any(*cp, " \t0123456789$^./-+*'\""))
ac72cd41
KS
228 *cp2++ = *cp++;
229 *cp2 = '\0';
230
231 /*
232 * Look up the command; if not found, bitch.
233 * Normally, a blank command would map to the
234 * first command in the table; while sourcing,
235 * however, we ignore blank lines to eliminate
236 * confusion.
237 */
238
239 if (sourcing && equal(word, ""))
240 return(0);
241 com = lex(word);
242 if (com == NONE) {
243 printf("What?\n");
244 if (sourcing)
245 unstack();
246 return(0);
247 }
248
249 /*
250 * Special case so that quit causes a return to
251 * main, who will call the quit code directly.
252 * If we are in a source file, just unstack.
253 */
254
255 if (com->c_func == edstop && sourcing) {
256 unstack();
257 return(0);
258 }
259 if (!edit && com->c_func == edstop) {
260 signal(SIGINT, SIG_IGN);
261 return(1);
262 }
263
264 /*
265 * Process the arguments to the command, depending
266 * on the type he expects. Default to an error.
267 * If we are sourcing an interactive command, it's
268 * an error.
269 */
270
271 if (!rcvmode && (com->c_argtype & M) == 0) {
272 printf("May not execute \"%s\" while sending\n",
273 com->c_name);
274 unstack();
275 return(0);
276 }
277 if (sourcing && com->c_argtype & I) {
278 printf("May not execute \"%s\" while sourcing\n",
279 com->c_name);
280 unstack();
281 return(0);
282 }
e5717bff
KS
283 if (readonly && com->c_argtype & W) {
284 printf("May not execute \"%s\" -- message file is read only\n",
285 com->c_name);
286 if (sourcing)
287 unstack();
288 return(0);
289 }
ac72cd41 290 e = 1;
e5717bff 291 switch (com->c_argtype & ~(P|I|M|W)) {
ac72cd41
KS
292 case MSGLIST:
293 /*
294 * A message list defaulting to nearest forward
295 * legal message.
296 */
297 if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
298 break;
299 if (c == 0) {
300 *msgvec = first(com->c_msgflag,
301 com->c_msgmask);
302 msgvec[1] = NULL;
303 }
304 if (*msgvec == NULL) {
305 printf("No applicable messages\n");
306 break;
307 }
308 e = (*com->c_func)(msgvec);
309 break;
310
311 case NDMLIST:
312 /*
313 * A message list with no defaults, but no error
314 * if none exist.
315 */
316 if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
317 break;
318 e = (*com->c_func)(msgvec);
319 break;
320
321 case STRLIST:
322 /*
323 * Just the straight string, with
324 * leading blanks removed.
325 */
326 while (any(*cp, " \t"))
327 cp++;
328 e = (*com->c_func)(cp);
329 break;
330
331 case RAWLIST:
332 /*
333 * A vector of strings, in shell style.
334 */
335 if ((c = getrawlist(cp, arglist)) < 0)
336 break;
337 if (c < com->c_minargs) {
338 printf("%s requires at least %d arg(s)\n",
339 com->c_name, com->c_minargs);
340 break;
341 }
342 if (c > com->c_maxargs) {
343 printf("%s takes no more than %d arg(s)\n",
344 com->c_name, com->c_maxargs);
345 break;
346 }
347 e = (*com->c_func)(arglist);
348 break;
349
350 case NOLIST:
351 /*
352 * Just the constant zero, for exiting,
353 * eg.
354 */
355 e = (*com->c_func)(0);
356 break;
357
358 default:
359 panic("Unknown argtype");
360 }
361
362 /*
363 * Exit the current source file on
364 * error.
365 */
366
367 if (e && sourcing)
368 unstack();
369 if (com->c_func == edstop)
370 return(1);
371 if (value("autoprint") != NOSTR && com->c_argtype & P)
e5717bff
KS
372 if ((dot->m_flag & MDELETED) == 0) {
373 muvec[0] = dot - &message[0] + 1;
374 muvec[1] = 0;
375 type(muvec);
376 }
ac72cd41
KS
377 if (!sourcing)
378 sawcom = 1;
379 return(0);
380}
381
e5717bff
KS
382/*
383 * Set the size of the message vector used to construct argument
384 * lists to message list functions.
385 */
386
387setmsize(sz)
388{
389
390 if (msgvec != (int *) 0)
391 cfree(msgvec);
392 msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec);
393}
394
ac72cd41
KS
395/*
396 * Find the correct command in the command table corresponding
397 * to the passed command "word"
398 */
399
400struct cmd *
401lex(word)
402 char word[];
403{
404 register struct cmd *cp;
405 extern struct cmd cmdtab[];
406
407 for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
408 if (isprefix(word, cp->c_name))
409 return(cp);
410 return(NONE);
411}
412
413/*
414 * Determine if as1 is a valid prefix of as2.
415 * Return true if yep.
416 */
417
418isprefix(as1, as2)
419 char *as1, *as2;
420{
421 register char *s1, *s2;
422
423 s1 = as1;
424 s2 = as2;
425 while (*s1++ == *s2)
426 if (*s2++ == '\0')
427 return(1);
428 return(*--s1 == '\0');
429}
430
431/*
432 * The following gets called on receipt of a rubout. This is
433 * to abort printout of a command, mainly.
434 * Dispatching here when command() is inactive crashes rcv.
435 * Close all open files except 0, 1, 2, and the temporary.
436 * The special call to getuserid() is needed so it won't get
437 * annoyed about losing its open file.
438 * Also, unstack all source files.
439 */
440
441stop()
442{
443 register FILE *fp;
444
445 noreset = 0;
446 signal(SIGINT, SIG_IGN);
447 sawcom++;
448 while (sourcing)
449 unstack();
450 getuserid((char *) -1);
451 for (fp = &_iob[0]; fp < &_iob[_NFILE]; fp++) {
452 if (fp == stdin || fp == stdout)
453 continue;
454 if (fp == itf || fp == otf)
455 continue;
456 if (fp == stderr)
457 continue;
e5717bff
KS
458 if (fp == pipef) {
459 pclose(pipef);
460 pipef = NULL;
461 continue;
462 }
ac72cd41
KS
463 fclose(fp);
464 }
465 if (image >= 0) {
466 close(image);
467 image = -1;
468 }
469 clrbuf(stdout);
470 printf("Interrupt\n");
471 signal(SIGINT, stop);
472 reset(0);
473}
474
475/*
476 * Announce the presence of the current Mail version,
477 * give the message count, and print a header listing.
478 */
479
480char *greeting = "Mail version 2.0 %s. Type ? for help.\n";
481
e5717bff 482announce(pr)
ac72cd41
KS
483{
484 int vec[2];
485 extern char *version;
486 register struct message *mp;
487
488 if (value("hold") != NOSTR)
489 for (mp = &message[0]; mp < &message[msgCount]; mp++)
490 mp->m_flag |= MPRESERVE;
491 vec[0] = 1;
492 vec[1] = 0;
e5717bff 493 if (pr && value("quiet") == NOSTR)
ac72cd41
KS
494 printf(greeting, version);
495 if (msgCount == 1)
e5717bff 496 printf("1 message");
ac72cd41 497 else
e5717bff
KS
498 printf("%d messages", msgCount);
499 if (readonly)
500 printf(" [Read only]");
501 printf("\n");
ac72cd41
KS
502 headers(vec);
503}
504
505strace() {}
506
507/*
508 * Print the current version number.
509 */
510
511pversion(e)
512{
513 printf(greeting, version);
514 return(0);
515}