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