changed to print (continue) when continued after ^Z
[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
726c3356 11static char *SccsId = "@(#)lex.c 1.16 %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[];
726c3356 28 int (*sigs[3])();
e5717bff
KS
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
726c3356 45 sigsave(sigs, SIG_IGN);
e5717bff
KS
46 if (shudclob) {
47 if (edit)
48 edstop();
49 else
50 quit();
51 }
52
53 /*
54 * Copy the messages into /tmp
55 * and set pointers.
56 */
57
58 readonly = 0;
59 if ((i = open(name, 1)) < 0)
60 readonly++;
61 else
62 close(i);
63 if (shudclob) {
64 fclose(itf);
65 fclose(otf);
66 }
67 shudclob = 1;
68 edit = isedit;
69 strncpy(efile, name, 128);
70 editfile = efile;
128ec005
KS
71 if (name != mailname)
72 strcpy(mailname, name);
e5717bff
KS
73 mailsize = fsize(ibuf);
74 if ((otf = fopen(tempMesg, "w")) == NULL) {
75 perror(tempMesg);
76 exit(1);
77 }
78 if ((itf = fopen(tempMesg, "r")) == NULL) {
79 perror(tempMesg);
80 exit(1);
81 }
82 remove(tempMesg);
83 setptr(ibuf);
84 setmsize(msgCount);
85 fclose(ibuf);
726c3356 86 sigret(sigs);
4d931f76 87 shudann = 1;
335704ca 88 sawcom = 0;
e5717bff
KS
89 return(0);
90}
ac72cd41
KS
91
92/*
93 * Interpret user commands one by one. If standard input is not a tty,
94 * print no prompt.
95 */
96
97int *msgvec;
98
99commands()
100{
101 int prompt, firstsw, stop();
102 register int n;
103 char linebuf[LINESIZE];
726c3356 104 int hangup();
ac72cd41 105
726c3356
KS
106 if (rcvmode) {
107 if (sigset(SIGINT, SIG_IGN) != SIG_IGN)
108 sigset(SIGINT, stop);
109 if (sigset(SIGHUP, SIG_IGN) != SIG_IGN)
110 sigset(SIGHUP, hangup);
111 }
ac72cd41
KS
112 input = stdin;
113 prompt = 1;
114 if (!intty)
115 prompt = 0;
116 firstsw = 1;
117 for (;;) {
118 setexit();
119 if (firstsw > 0) {
120 firstsw = 0;
121 source1(mailrc);
122 if (!nosrc)
123 source1(MASTER);
124 }
125
126 /*
127 * How's this for obscure: after we
128 * finish sourcing for the first time,
129 * go off and print the headers!
130 */
131
4d931f76
KS
132 if (shudann && !sourcing) {
133 shudann = 0;
ac72cd41 134 if (rcvmode)
4d931f76 135 announce(edit);
ac72cd41
KS
136 }
137
138 /*
139 * Print the prompt, if needed. Clear out
140 * string space, and flush the output.
141 */
142
143 if (!rcvmode && !sourcing)
144 return;
73f94fab 145top:
ac72cd41
KS
146 if (prompt && !sourcing)
147 printf("_\r");
148 flush();
149 sreset();
150
151 /*
152 * Read a line of commands from the current input
153 * and handle end of file specially.
154 */
155
156 n = 0;
157 for (;;) {
158 if (readline(input, &linebuf[n]) <= 0) {
159 if (n != 0)
160 break;
161 if (sourcing) {
162 unstack();
163 goto more;
164 }
73f94fab
KS
165 if (value("ignoreeof") != NOSTR && prompt) {
166 printf("Use \"quit\" to quit.\n");
167 goto top;
168 }
726c3356 169 if (!edit)
ac72cd41 170 return;
ac72cd41
KS
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) {
726c3356 269 sigset(SIGINT, SIG_IGN);
ac72cd41
KS
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
726c3356
KS
396/*
397 * Branch here on hangup signal and simulate quit.
398 */
399hangup()
400{
401 int (*sigs[3])();
402
403 sigsave(sigs);
404 if (edit) {
405 if (setexit())
406 exit(0);
407 edstop();
408 }
409 else
410 quit();
411 exit(0);
412}
413
e5717bff
KS
414/*
415 * Set the size of the message vector used to construct argument
416 * lists to message list functions.
417 */
418
419setmsize(sz)
420{
421
422 if (msgvec != (int *) 0)
423 cfree(msgvec);
424 msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec);
425}
426
ac72cd41
KS
427/*
428 * Find the correct command in the command table corresponding
429 * to the passed command "word"
430 */
431
432struct cmd *
433lex(word)
434 char word[];
435{
436 register struct cmd *cp;
437 extern struct cmd cmdtab[];
438
439 for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
440 if (isprefix(word, cp->c_name))
441 return(cp);
442 return(NONE);
443}
444
445/*
446 * Determine if as1 is a valid prefix of as2.
447 * Return true if yep.
448 */
449
450isprefix(as1, as2)
451 char *as1, *as2;
452{
453 register char *s1, *s2;
454
455 s1 = as1;
456 s2 = as2;
457 while (*s1++ == *s2)
458 if (*s2++ == '\0')
459 return(1);
460 return(*--s1 == '\0');
461}
462
463/*
464 * The following gets called on receipt of a rubout. This is
465 * to abort printout of a command, mainly.
466 * Dispatching here when command() is inactive crashes rcv.
467 * Close all open files except 0, 1, 2, and the temporary.
468 * The special call to getuserid() is needed so it won't get
469 * annoyed about losing its open file.
470 * Also, unstack all source files.
471 */
472
e39b1d85
KS
473int inithdr; /* am printing startup headers */
474
726c3356 475stop(s)
ac72cd41
KS
476{
477 register FILE *fp;
478
479 noreset = 0;
e39b1d85
KS
480 if (!inithdr)
481 sawcom++;
482 inithdr = 0;
ac72cd41
KS
483 while (sourcing)
484 unstack();
485 getuserid((char *) -1);
486 for (fp = &_iob[0]; fp < &_iob[_NFILE]; fp++) {
487 if (fp == stdin || fp == stdout)
488 continue;
489 if (fp == itf || fp == otf)
490 continue;
491 if (fp == stderr)
492 continue;
e5717bff
KS
493 if (fp == pipef) {
494 pclose(pipef);
495 pipef = NULL;
496 continue;
497 }
ac72cd41
KS
498 fclose(fp);
499 }
500 if (image >= 0) {
501 close(image);
502 image = -1;
503 }
504 clrbuf(stdout);
505 printf("Interrupt\n");
726c3356 506 sigrelse(s);
ac72cd41
KS
507 reset(0);
508}
509
510/*
511 * Announce the presence of the current Mail version,
512 * give the message count, and print a header listing.
513 */
514
515char *greeting = "Mail version 2.0 %s. Type ? for help.\n";
516
e5717bff 517announce(pr)
ac72cd41 518{
f3bfa857 519 int vec[2], mdot;
ac72cd41 520 extern char *version;
74745497
KS
521
522 mdot = newfileinfo();
523 vec[0] = mdot;
524 vec[1] = 0;
525 if (pr && value("quiet") == NOSTR)
526 printf(greeting, version);
527 dot = &message[mdot - 1];
e39b1d85
KS
528 if (msgCount > 0 && !noheader) {
529 inithdr++;
74745497 530 headers(vec);
e39b1d85
KS
531 inithdr = 0;
532 }
74745497
KS
533}
534
535/*
536 * Announce information about the file we are editing.
537 * Return a likely place to set dot.
538 */
539
540newfileinfo()
541{
ac72cd41 542 register struct message *mp;
74745497 543 register int u, n, mdot;
ac72cd41 544
c52f0860
KS
545 for (mp = &message[0]; mp < &message[msgCount]; mp++)
546 if (mp->m_flag & MNEW)
547 break;
f3bfa857
KS
548 if (mp >= &message[msgCount])
549 for (mp = &message[0]; mp < &message[msgCount]; mp++)
550 if ((mp->m_flag & MREAD) == 0)
551 break;
c52f0860 552 if (mp < &message[msgCount])
f3bfa857 553 mdot = mp - &message[0] + 1;
c52f0860 554 else
f3bfa857 555 mdot = 1;
128ec005
KS
556 for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
557 if (mp->m_flag & MNEW)
558 n++;
559 if ((mp->m_flag & MREAD) == 0)
560 u++;
561 }
74745497
KS
562 printf("\"%s\": ", mailname);
563 if (msgCount == 1)
564 printf("1 message");
565 else
566 printf("%d messages", msgCount);
128ec005
KS
567 if (n > 0)
568 printf(" %d new", n);
569 if (u-n > 0)
570 printf(" %d unread", u);
74745497
KS
571 if (readonly)
572 printf(" [Read only]");
e5717bff 573 printf("\n");
74745497 574 return(mdot);
ac72cd41
KS
575}
576
577strace() {}
578
579/*
580 * Print the current version number.
581 */
582
583pversion(e)
584{
585 printf(greeting, version);
586 return(0);
587}