added ? as a statement terminating character
[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
128ec005 11static char *SccsId = "@(#)lex.c 1.8 %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]);
89 printf("%s: ", name);
4d931f76 90 shudann = 1;
335704ca 91 sawcom = 0;
e5717bff
KS
92 return(0);
93}
ac72cd41
KS
94
95/*
96 * Interpret user commands one by one. If standard input is not a tty,
97 * print no prompt.
98 */
99
100int *msgvec;
101
102commands()
103{
104 int prompt, firstsw, stop();
105 register int n;
106 char linebuf[LINESIZE];
107
ac72cd41
KS
108 if (rcvmode)
109 if (signal(SIGINT, SIG_IGN) == SIG_DFL)
110 signal(SIGINT, stop);
111 input = stdin;
112 prompt = 1;
113 if (!intty)
114 prompt = 0;
115 firstsw = 1;
116 for (;;) {
117 setexit();
118 if (firstsw > 0) {
119 firstsw = 0;
120 source1(mailrc);
121 if (!nosrc)
122 source1(MASTER);
123 }
124
125 /*
126 * How's this for obscure: after we
127 * finish sourcing for the first time,
128 * go off and print the headers!
129 */
130
4d931f76
KS
131 if (shudann && !sourcing) {
132 shudann = 0;
ac72cd41 133 if (rcvmode)
4d931f76 134 announce(edit);
ac72cd41
KS
135 }
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
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
254 if ((com->c_argtype & C) == 0)
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);
283 unstack();
284 return(0);
285 }
286 if (sourcing && com->c_argtype & I) {
287 printf("May not execute \"%s\" while sourcing\n",
288 com->c_name);
289 unstack();
290 return(0);
291 }
e5717bff
KS
292 if (readonly && com->c_argtype & W) {
293 printf("May not execute \"%s\" -- message file is read only\n",
294 com->c_name);
295 if (sourcing)
296 unstack();
297 return(0);
298 }
ac72cd41 299 e = 1;
128ec005 300 switch (com->c_argtype & ~(C|P|I|M|T|W)) {
ac72cd41
KS
301 case MSGLIST:
302 /*
303 * A message list defaulting to nearest forward
304 * legal message.
305 */
306 if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
307 break;
308 if (c == 0) {
309 *msgvec = first(com->c_msgflag,
310 com->c_msgmask);
311 msgvec[1] = NULL;
312 }
313 if (*msgvec == NULL) {
314 printf("No applicable messages\n");
315 break;
316 }
317 e = (*com->c_func)(msgvec);
318 break;
319
320 case NDMLIST:
321 /*
322 * A message list with no defaults, but no error
323 * if none exist.
324 */
325 if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
326 break;
327 e = (*com->c_func)(msgvec);
328 break;
329
330 case STRLIST:
331 /*
332 * Just the straight string, with
333 * leading blanks removed.
334 */
335 while (any(*cp, " \t"))
336 cp++;
337 e = (*com->c_func)(cp);
338 break;
339
340 case RAWLIST:
341 /*
342 * A vector of strings, in shell style.
343 */
344 if ((c = getrawlist(cp, arglist)) < 0)
345 break;
346 if (c < com->c_minargs) {
347 printf("%s requires at least %d arg(s)\n",
348 com->c_name, com->c_minargs);
349 break;
350 }
351 if (c > com->c_maxargs) {
352 printf("%s takes no more than %d arg(s)\n",
353 com->c_name, com->c_maxargs);
354 break;
355 }
356 e = (*com->c_func)(arglist);
357 break;
358
359 case NOLIST:
360 /*
361 * Just the constant zero, for exiting,
362 * eg.
363 */
364 e = (*com->c_func)(0);
365 break;
366
367 default:
368 panic("Unknown argtype");
369 }
370
371 /*
372 * Exit the current source file on
373 * error.
374 */
375
376 if (e && sourcing)
377 unstack();
378 if (com->c_func == edstop)
379 return(1);
380 if (value("autoprint") != NOSTR && com->c_argtype & P)
e5717bff
KS
381 if ((dot->m_flag & MDELETED) == 0) {
382 muvec[0] = dot - &message[0] + 1;
383 muvec[1] = 0;
384 type(muvec);
385 }
128ec005 386 if (!sourcing && (com->c_argtype & T) == 0)
ac72cd41
KS
387 sawcom = 1;
388 return(0);
389}
390
e5717bff
KS
391/*
392 * Set the size of the message vector used to construct argument
393 * lists to message list functions.
394 */
395
396setmsize(sz)
397{
398
399 if (msgvec != (int *) 0)
400 cfree(msgvec);
401 msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec);
402}
403
ac72cd41
KS
404/*
405 * Find the correct command in the command table corresponding
406 * to the passed command "word"
407 */
408
409struct cmd *
410lex(word)
411 char word[];
412{
413 register struct cmd *cp;
414 extern struct cmd cmdtab[];
415
416 for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
417 if (isprefix(word, cp->c_name))
418 return(cp);
419 return(NONE);
420}
421
422/*
423 * Determine if as1 is a valid prefix of as2.
424 * Return true if yep.
425 */
426
427isprefix(as1, as2)
428 char *as1, *as2;
429{
430 register char *s1, *s2;
431
432 s1 = as1;
433 s2 = as2;
434 while (*s1++ == *s2)
435 if (*s2++ == '\0')
436 return(1);
437 return(*--s1 == '\0');
438}
439
440/*
441 * The following gets called on receipt of a rubout. This is
442 * to abort printout of a command, mainly.
443 * Dispatching here when command() is inactive crashes rcv.
444 * Close all open files except 0, 1, 2, and the temporary.
445 * The special call to getuserid() is needed so it won't get
446 * annoyed about losing its open file.
447 * Also, unstack all source files.
448 */
449
450stop()
451{
452 register FILE *fp;
453
454 noreset = 0;
455 signal(SIGINT, SIG_IGN);
456 sawcom++;
457 while (sourcing)
458 unstack();
459 getuserid((char *) -1);
460 for (fp = &_iob[0]; fp < &_iob[_NFILE]; fp++) {
461 if (fp == stdin || fp == stdout)
462 continue;
463 if (fp == itf || fp == otf)
464 continue;
465 if (fp == stderr)
466 continue;
e5717bff
KS
467 if (fp == pipef) {
468 pclose(pipef);
469 pipef = NULL;
470 continue;
471 }
ac72cd41
KS
472 fclose(fp);
473 }
474 if (image >= 0) {
475 close(image);
476 image = -1;
477 }
478 clrbuf(stdout);
479 printf("Interrupt\n");
480 signal(SIGINT, stop);
481 reset(0);
482}
483
484/*
485 * Announce the presence of the current Mail version,
486 * give the message count, and print a header listing.
487 */
488
489char *greeting = "Mail version 2.0 %s. Type ? for help.\n";
490
e5717bff 491announce(pr)
ac72cd41 492{
f3bfa857 493 int vec[2], mdot;
ac72cd41
KS
494 extern char *version;
495 register struct message *mp;
128ec005 496 register int u, n;
ac72cd41 497
c52f0860
KS
498 for (mp = &message[0]; mp < &message[msgCount]; mp++)
499 if (mp->m_flag & MNEW)
500 break;
f3bfa857
KS
501 if (mp >= &message[msgCount])
502 for (mp = &message[0]; mp < &message[msgCount]; mp++)
503 if ((mp->m_flag & MREAD) == 0)
504 break;
c52f0860 505 if (mp < &message[msgCount])
f3bfa857 506 mdot = mp - &message[0] + 1;
c52f0860 507 else
f3bfa857
KS
508 mdot = 1;
509 vec[0] = mdot;
ac72cd41 510 vec[1] = 0;
e5717bff 511 if (pr && value("quiet") == NOSTR)
ac72cd41
KS
512 printf(greeting, version);
513 if (msgCount == 1)
e5717bff 514 printf("1 message");
ac72cd41 515 else
e5717bff
KS
516 printf("%d messages", msgCount);
517 if (readonly)
518 printf(" [Read only]");
128ec005
KS
519 for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
520 if (mp->m_flag & MNEW)
521 n++;
522 if ((mp->m_flag & MREAD) == 0)
523 u++;
524 }
525 if (n > 0)
526 printf(" %d new", n);
527 if (u-n > 0)
528 printf(" %d unread", u);
e5717bff 529 printf("\n");
ac72cd41 530 headers(vec);
f3bfa857 531 dot = &message[mdot - 1];
ac72cd41
KS
532}
533
534strace() {}
535
536/*
537 * Print the current version number.
538 */
539
540pversion(e)
541{
542 printf(greeting, version);
543 return(0);
544}