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