restore sccsline
[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
e39b1d85 11static char *SccsId = "@(#)lex.c 1.15 %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;
128ec005
KS
72 if (name != mailname)
73 strcpy(mailname, name);
e5717bff
KS
74 mailsize = fsize(ibuf);
75 if ((otf = fopen(tempMesg, "w")) == NULL) {
76 perror(tempMesg);
77 exit(1);
78 }
79 if ((itf = fopen(tempMesg, "r")) == NULL) {
80 perror(tempMesg);
81 exit(1);
82 }
83 remove(tempMesg);
84 setptr(ibuf);
85 setmsize(msgCount);
86 fclose(ibuf);
87 for (i = SIGINT; i <= SIGQUIT; i++)
88 signal(i, sigs[i - SIGINT]);
4d931f76 89 shudann = 1;
335704ca 90 sawcom = 0;
e5717bff
KS
91 return(0);
92}
ac72cd41
KS
93
94/*
95 * Interpret user commands one by one. If standard input is not a tty,
96 * print no prompt.
97 */
98
99int *msgvec;
100
101commands()
102{
103 int prompt, firstsw, stop();
104 register int n;
105 char linebuf[LINESIZE];
106
ac72cd41
KS
107 if (rcvmode)
108 if (signal(SIGINT, SIG_IGN) == SIG_DFL)
109 signal(SIGINT, stop);
110 input = stdin;
111 prompt = 1;
112 if (!intty)
113 prompt = 0;
114 firstsw = 1;
115 for (;;) {
116 setexit();
117 if (firstsw > 0) {
118 firstsw = 0;
119 source1(mailrc);
120 if (!nosrc)
121 source1(MASTER);
122 }
123
124 /*
125 * How's this for obscure: after we
126 * finish sourcing for the first time,
127 * go off and print the headers!
128 */
129
4d931f76
KS
130 if (shudann && !sourcing) {
131 shudann = 0;
ac72cd41 132 if (rcvmode)
4d931f76 133 announce(edit);
ac72cd41
KS
134 }
135
136 /*
137 * Print the prompt, if needed. Clear out
138 * string space, and flush the output.
139 */
140
141 if (!rcvmode && !sourcing)
142 return;
73f94fab 143top:
ac72cd41
KS
144 if (prompt && !sourcing)
145 printf("_\r");
146 flush();
147 sreset();
148
149 /*
150 * Read a line of commands from the current input
151 * and handle end of file specially.
152 */
153
154 n = 0;
155 for (;;) {
156 if (readline(input, &linebuf[n]) <= 0) {
157 if (n != 0)
158 break;
159 if (sourcing) {
160 unstack();
161 goto more;
162 }
73f94fab
KS
163 if (value("ignoreeof") != NOSTR && prompt) {
164 printf("Use \"quit\" to quit.\n");
165 goto top;
166 }
ac72cd41
KS
167 if (!edit) {
168 signal(SIGINT, SIG_IGN);
169 return;
170 }
171 edstop();
172 return;
173 }
174 if ((n = strlen(linebuf)) == 0)
175 break;
176 n--;
177 if (linebuf[n] != '\\')
178 break;
179 linebuf[n++] = ' ';
180 }
2cd8e0a9 181 if (execute(linebuf, 0))
ac72cd41
KS
182 return;
183more: ;
184 }
185}
186
187/*
188 * Execute a single command. If the command executed
189 * is "quit," then return non-zero so that the caller
190 * will know to return back to main, if he cares.
2cd8e0a9 191 * Contxt is non-zero if called while composing mail.
ac72cd41
KS
192 */
193
2cd8e0a9 194execute(linebuf, contxt)
ac72cd41
KS
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;
c0132ef0 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
128ec005
KS
249 /*
250 * See if we should execute the command -- if a conditional
251 * we always execute it, otherwise, check the state of cond.
252 */
253
989e19a2 254 if ((com->c_argtype & F) == 0)
128ec005
KS
255 if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode)
256 return(0);
257
ac72cd41
KS
258 /*
259 * Special case so that quit causes a return to
260 * main, who will call the quit code directly.
261 * If we are in a source file, just unstack.
262 */
263
264 if (com->c_func == edstop && sourcing) {
265 unstack();
266 return(0);
267 }
268 if (!edit && com->c_func == edstop) {
269 signal(SIGINT, SIG_IGN);
270 return(1);
271 }
272
273 /*
274 * Process the arguments to the command, depending
275 * on the type he expects. Default to an error.
276 * If we are sourcing an interactive command, it's
277 * an error.
278 */
279
280 if (!rcvmode && (com->c_argtype & M) == 0) {
281 printf("May not execute \"%s\" while sending\n",
282 com->c_name);
2cd8e0a9
KS
283 if (sourcing)
284 unstack();
ac72cd41
KS
285 return(0);
286 }
287 if (sourcing && com->c_argtype & I) {
288 printf("May not execute \"%s\" while sourcing\n",
289 com->c_name);
290 unstack();
291 return(0);
292 }
e5717bff
KS
293 if (readonly && com->c_argtype & W) {
294 printf("May not execute \"%s\" -- message file is read only\n",
295 com->c_name);
296 if (sourcing)
297 unstack();
298 return(0);
299 }
2cd8e0a9
KS
300 if (contxt && com->c_argtype & R) {
301 printf("Cannot recursively invoke \"%s\"\n", com->c_name);
302 return(0);
303 }
ac72cd41 304 e = 1;
2cd8e0a9 305 switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
ac72cd41
KS
306 case MSGLIST:
307 /*
308 * A message list defaulting to nearest forward
309 * legal message.
310 */
311 if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
312 break;
313 if (c == 0) {
314 *msgvec = first(com->c_msgflag,
315 com->c_msgmask);
316 msgvec[1] = NULL;
317 }
318 if (*msgvec == NULL) {
319 printf("No applicable messages\n");
320 break;
321 }
322 e = (*com->c_func)(msgvec);
323 break;
324
325 case NDMLIST:
326 /*
327 * A message list with no defaults, but no error
328 * if none exist.
329 */
330 if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
331 break;
332 e = (*com->c_func)(msgvec);
333 break;
334
335 case STRLIST:
336 /*
337 * Just the straight string, with
338 * leading blanks removed.
339 */
340 while (any(*cp, " \t"))
341 cp++;
342 e = (*com->c_func)(cp);
343 break;
344
345 case RAWLIST:
346 /*
347 * A vector of strings, in shell style.
348 */
349 if ((c = getrawlist(cp, arglist)) < 0)
350 break;
351 if (c < com->c_minargs) {
352 printf("%s requires at least %d arg(s)\n",
353 com->c_name, com->c_minargs);
354 break;
355 }
356 if (c > com->c_maxargs) {
357 printf("%s takes no more than %d arg(s)\n",
358 com->c_name, com->c_maxargs);
359 break;
360 }
361 e = (*com->c_func)(arglist);
362 break;
363
364 case NOLIST:
365 /*
366 * Just the constant zero, for exiting,
367 * eg.
368 */
369 e = (*com->c_func)(0);
370 break;
371
372 default:
373 panic("Unknown argtype");
374 }
375
376 /*
377 * Exit the current source file on
378 * error.
379 */
380
381 if (e && sourcing)
382 unstack();
383 if (com->c_func == edstop)
384 return(1);
385 if (value("autoprint") != NOSTR && com->c_argtype & P)
e5717bff
KS
386 if ((dot->m_flag & MDELETED) == 0) {
387 muvec[0] = dot - &message[0] + 1;
388 muvec[1] = 0;
389 type(muvec);
390 }
128ec005 391 if (!sourcing && (com->c_argtype & T) == 0)
ac72cd41
KS
392 sawcom = 1;
393 return(0);
394}
395
e5717bff
KS
396/*
397 * Set the size of the message vector used to construct argument
398 * lists to message list functions.
399 */
400
401setmsize(sz)
402{
403
404 if (msgvec != (int *) 0)
405 cfree(msgvec);
406 msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec);
407}
408
ac72cd41
KS
409/*
410 * Find the correct command in the command table corresponding
411 * to the passed command "word"
412 */
413
414struct cmd *
415lex(word)
416 char word[];
417{
418 register struct cmd *cp;
419 extern struct cmd cmdtab[];
420
421 for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
422 if (isprefix(word, cp->c_name))
423 return(cp);
424 return(NONE);
425}
426
427/*
428 * Determine if as1 is a valid prefix of as2.
429 * Return true if yep.
430 */
431
432isprefix(as1, as2)
433 char *as1, *as2;
434{
435 register char *s1, *s2;
436
437 s1 = as1;
438 s2 = as2;
439 while (*s1++ == *s2)
440 if (*s2++ == '\0')
441 return(1);
442 return(*--s1 == '\0');
443}
444
445/*
446 * The following gets called on receipt of a rubout. This is
447 * to abort printout of a command, mainly.
448 * Dispatching here when command() is inactive crashes rcv.
449 * Close all open files except 0, 1, 2, and the temporary.
450 * The special call to getuserid() is needed so it won't get
451 * annoyed about losing its open file.
452 * Also, unstack all source files.
453 */
454
e39b1d85
KS
455int inithdr; /* am printing startup headers */
456
ac72cd41
KS
457stop()
458{
459 register FILE *fp;
460
461 noreset = 0;
462 signal(SIGINT, SIG_IGN);
e39b1d85
KS
463 if (!inithdr)
464 sawcom++;
465 inithdr = 0;
ac72cd41
KS
466 while (sourcing)
467 unstack();
468 getuserid((char *) -1);
469 for (fp = &_iob[0]; fp < &_iob[_NFILE]; fp++) {
470 if (fp == stdin || fp == stdout)
471 continue;
472 if (fp == itf || fp == otf)
473 continue;
474 if (fp == stderr)
475 continue;
e5717bff
KS
476 if (fp == pipef) {
477 pclose(pipef);
478 pipef = NULL;
479 continue;
480 }
ac72cd41
KS
481 fclose(fp);
482 }
483 if (image >= 0) {
484 close(image);
485 image = -1;
486 }
487 clrbuf(stdout);
488 printf("Interrupt\n");
489 signal(SIGINT, stop);
490 reset(0);
491}
492
493/*
494 * Announce the presence of the current Mail version,
495 * give the message count, and print a header listing.
496 */
497
498char *greeting = "Mail version 2.0 %s. Type ? for help.\n";
499
e5717bff 500announce(pr)
ac72cd41 501{
f3bfa857 502 int vec[2], mdot;
ac72cd41 503 extern char *version;
74745497
KS
504
505 mdot = newfileinfo();
506 vec[0] = mdot;
507 vec[1] = 0;
508 if (pr && value("quiet") == NOSTR)
509 printf(greeting, version);
510 dot = &message[mdot - 1];
e39b1d85
KS
511 if (msgCount > 0 && !noheader) {
512 inithdr++;
74745497 513 headers(vec);
e39b1d85
KS
514 inithdr = 0;
515 }
74745497
KS
516}
517
518/*
519 * Announce information about the file we are editing.
520 * Return a likely place to set dot.
521 */
522
523newfileinfo()
524{
ac72cd41 525 register struct message *mp;
74745497 526 register int u, n, mdot;
ac72cd41 527
c52f0860
KS
528 for (mp = &message[0]; mp < &message[msgCount]; mp++)
529 if (mp->m_flag & MNEW)
530 break;
f3bfa857
KS
531 if (mp >= &message[msgCount])
532 for (mp = &message[0]; mp < &message[msgCount]; mp++)
533 if ((mp->m_flag & MREAD) == 0)
534 break;
c52f0860 535 if (mp < &message[msgCount])
f3bfa857 536 mdot = mp - &message[0] + 1;
c52f0860 537 else
f3bfa857 538 mdot = 1;
128ec005
KS
539 for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
540 if (mp->m_flag & MNEW)
541 n++;
542 if ((mp->m_flag & MREAD) == 0)
543 u++;
544 }
74745497
KS
545 printf("\"%s\": ", mailname);
546 if (msgCount == 1)
547 printf("1 message");
548 else
549 printf("%d messages", msgCount);
128ec005
KS
550 if (n > 0)
551 printf(" %d new", n);
552 if (u-n > 0)
553 printf(" %d unread", u);
74745497
KS
554 if (readonly)
555 printf(" [Read only]");
e5717bff 556 printf("\n");
74745497 557 return(mdot);
ac72cd41
KS
558}
559
560strace() {}
561
562/*
563 * Print the current version number.
564 */
565
566pversion(e)
567{
568 printf(greeting, version);
569 return(0);
570}