announce now looks for the first unread message if there
[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
f3bfa857 11static char *SccsId = "@(#)lex.c 1.7 %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;
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);
85 for (i = SIGINT; i <= SIGQUIT; i++)
86 signal(i, sigs[i - SIGINT]);
87 printf("%s: ", name);
4d931f76 88 shudann = 1;
335704ca 89 sawcom = 0;
e5717bff
KS
90 return(0);
91}
ac72cd41
KS
92
93/*
94 * Interpret user commands one by one. If standard input is not a tty,
95 * print no prompt.
96 */
97
98int *msgvec;
99
100commands()
101{
102 int prompt, firstsw, stop();
103 register int n;
104 char linebuf[LINESIZE];
105
ac72cd41
KS
106 if (rcvmode)
107 if (signal(SIGINT, SIG_IGN) == SIG_DFL)
108 signal(SIGINT, stop);
109 input = stdin;
110 prompt = 1;
111 if (!intty)
112 prompt = 0;
113 firstsw = 1;
114 for (;;) {
115 setexit();
116 if (firstsw > 0) {
117 firstsw = 0;
118 source1(mailrc);
119 if (!nosrc)
120 source1(MASTER);
121 }
122
123 /*
124 * How's this for obscure: after we
125 * finish sourcing for the first time,
126 * go off and print the headers!
127 */
128
4d931f76
KS
129 if (shudann && !sourcing) {
130 shudann = 0;
ac72cd41 131 if (rcvmode)
4d931f76 132 announce(edit);
ac72cd41
KS
133 }
134
135 /*
136 * Print the prompt, if needed. Clear out
137 * string space, and flush the output.
138 */
139
140 if (!rcvmode && !sourcing)
141 return;
73f94fab 142top:
ac72cd41
KS
143 if (prompt && !sourcing)
144 printf("_\r");
145 flush();
146 sreset();
147
148 /*
149 * Read a line of commands from the current input
150 * and handle end of file specially.
151 */
152
153 n = 0;
154 for (;;) {
155 if (readline(input, &linebuf[n]) <= 0) {
156 if (n != 0)
157 break;
158 if (sourcing) {
159 unstack();
160 goto more;
161 }
73f94fab
KS
162 if (value("ignoreeof") != NOSTR && prompt) {
163 printf("Use \"quit\" to quit.\n");
164 goto top;
165 }
ac72cd41
KS
166 if (!edit) {
167 signal(SIGINT, SIG_IGN);
168 return;
169 }
170 edstop();
171 return;
172 }
173 if ((n = strlen(linebuf)) == 0)
174 break;
175 n--;
176 if (linebuf[n] != '\\')
177 break;
178 linebuf[n++] = ' ';
179 }
180 if (execute(linebuf))
181 return;
182more: ;
183 }
184}
185
186/*
187 * Execute a single command. If the command executed
188 * is "quit," then return non-zero so that the caller
189 * will know to return back to main, if he cares.
190 */
191
192execute(linebuf)
193 char linebuf[];
194{
195 char word[LINESIZE];
196 char *arglist[MAXARGC];
197 struct cmd *com;
198 register char *cp, *cp2;
199 register int c;
e5717bff 200 int muvec[2];
ac72cd41
KS
201 int edstop(), e;
202
203 /*
204 * Strip the white space away from the beginning
205 * of the command, then scan out a word, which
206 * consists of anything except digits and white space.
207 *
208 * Handle ! escapes differently to get the correct
209 * lexical conventions.
210 */
211
212 cp = linebuf;
213 while (any(*cp, " \t"))
214 cp++;
215 if (*cp == '!') {
216 if (sourcing) {
217 printf("Can't \"!\" while sourcing\n");
218 unstack();
219 return(0);
220 }
221 shell(cp+1);
222 return(0);
223 }
224 cp2 = word;
e5717bff 225 while (*cp && !any(*cp, " \t0123456789$^./-+*'\""))
ac72cd41
KS
226 *cp2++ = *cp++;
227 *cp2 = '\0';
228
229 /*
230 * Look up the command; if not found, bitch.
231 * Normally, a blank command would map to the
232 * first command in the table; while sourcing,
233 * however, we ignore blank lines to eliminate
234 * confusion.
235 */
236
237 if (sourcing && equal(word, ""))
238 return(0);
239 com = lex(word);
240 if (com == NONE) {
241 printf("What?\n");
242 if (sourcing)
243 unstack();
244 return(0);
245 }
246
247 /*
248 * Special case so that quit causes a return to
249 * main, who will call the quit code directly.
250 * If we are in a source file, just unstack.
251 */
252
253 if (com->c_func == edstop && sourcing) {
254 unstack();
255 return(0);
256 }
257 if (!edit && com->c_func == edstop) {
258 signal(SIGINT, SIG_IGN);
259 return(1);
260 }
261
262 /*
263 * Process the arguments to the command, depending
264 * on the type he expects. Default to an error.
265 * If we are sourcing an interactive command, it's
266 * an error.
267 */
268
269 if (!rcvmode && (com->c_argtype & M) == 0) {
270 printf("May not execute \"%s\" while sending\n",
271 com->c_name);
272 unstack();
273 return(0);
274 }
275 if (sourcing && com->c_argtype & I) {
276 printf("May not execute \"%s\" while sourcing\n",
277 com->c_name);
278 unstack();
279 return(0);
280 }
e5717bff
KS
281 if (readonly && com->c_argtype & W) {
282 printf("May not execute \"%s\" -- message file is read only\n",
283 com->c_name);
284 if (sourcing)
285 unstack();
286 return(0);
287 }
ac72cd41 288 e = 1;
e5717bff 289 switch (com->c_argtype & ~(P|I|M|W)) {
ac72cd41
KS
290 case MSGLIST:
291 /*
292 * A message list defaulting to nearest forward
293 * legal message.
294 */
295 if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
296 break;
297 if (c == 0) {
298 *msgvec = first(com->c_msgflag,
299 com->c_msgmask);
300 msgvec[1] = NULL;
301 }
302 if (*msgvec == NULL) {
303 printf("No applicable messages\n");
304 break;
305 }
306 e = (*com->c_func)(msgvec);
307 break;
308
309 case NDMLIST:
310 /*
311 * A message list with no defaults, but no error
312 * if none exist.
313 */
314 if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
315 break;
316 e = (*com->c_func)(msgvec);
317 break;
318
319 case STRLIST:
320 /*
321 * Just the straight string, with
322 * leading blanks removed.
323 */
324 while (any(*cp, " \t"))
325 cp++;
326 e = (*com->c_func)(cp);
327 break;
328
329 case RAWLIST:
330 /*
331 * A vector of strings, in shell style.
332 */
333 if ((c = getrawlist(cp, arglist)) < 0)
334 break;
335 if (c < com->c_minargs) {
336 printf("%s requires at least %d arg(s)\n",
337 com->c_name, com->c_minargs);
338 break;
339 }
340 if (c > com->c_maxargs) {
341 printf("%s takes no more than %d arg(s)\n",
342 com->c_name, com->c_maxargs);
343 break;
344 }
345 e = (*com->c_func)(arglist);
346 break;
347
348 case NOLIST:
349 /*
350 * Just the constant zero, for exiting,
351 * eg.
352 */
353 e = (*com->c_func)(0);
354 break;
355
356 default:
357 panic("Unknown argtype");
358 }
359
360 /*
361 * Exit the current source file on
362 * error.
363 */
364
365 if (e && sourcing)
366 unstack();
367 if (com->c_func == edstop)
368 return(1);
369 if (value("autoprint") != NOSTR && com->c_argtype & P)
e5717bff
KS
370 if ((dot->m_flag & MDELETED) == 0) {
371 muvec[0] = dot - &message[0] + 1;
372 muvec[1] = 0;
373 type(muvec);
374 }
ac72cd41
KS
375 if (!sourcing)
376 sawcom = 1;
377 return(0);
378}
379
e5717bff
KS
380/*
381 * Set the size of the message vector used to construct argument
382 * lists to message list functions.
383 */
384
385setmsize(sz)
386{
387
388 if (msgvec != (int *) 0)
389 cfree(msgvec);
390 msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec);
391}
392
ac72cd41
KS
393/*
394 * Find the correct command in the command table corresponding
395 * to the passed command "word"
396 */
397
398struct cmd *
399lex(word)
400 char word[];
401{
402 register struct cmd *cp;
403 extern struct cmd cmdtab[];
404
405 for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
406 if (isprefix(word, cp->c_name))
407 return(cp);
408 return(NONE);
409}
410
411/*
412 * Determine if as1 is a valid prefix of as2.
413 * Return true if yep.
414 */
415
416isprefix(as1, as2)
417 char *as1, *as2;
418{
419 register char *s1, *s2;
420
421 s1 = as1;
422 s2 = as2;
423 while (*s1++ == *s2)
424 if (*s2++ == '\0')
425 return(1);
426 return(*--s1 == '\0');
427}
428
429/*
430 * The following gets called on receipt of a rubout. This is
431 * to abort printout of a command, mainly.
432 * Dispatching here when command() is inactive crashes rcv.
433 * Close all open files except 0, 1, 2, and the temporary.
434 * The special call to getuserid() is needed so it won't get
435 * annoyed about losing its open file.
436 * Also, unstack all source files.
437 */
438
439stop()
440{
441 register FILE *fp;
442
443 noreset = 0;
444 signal(SIGINT, SIG_IGN);
445 sawcom++;
446 while (sourcing)
447 unstack();
448 getuserid((char *) -1);
449 for (fp = &_iob[0]; fp < &_iob[_NFILE]; fp++) {
450 if (fp == stdin || fp == stdout)
451 continue;
452 if (fp == itf || fp == otf)
453 continue;
454 if (fp == stderr)
455 continue;
e5717bff
KS
456 if (fp == pipef) {
457 pclose(pipef);
458 pipef = NULL;
459 continue;
460 }
ac72cd41
KS
461 fclose(fp);
462 }
463 if (image >= 0) {
464 close(image);
465 image = -1;
466 }
467 clrbuf(stdout);
468 printf("Interrupt\n");
469 signal(SIGINT, stop);
470 reset(0);
471}
472
473/*
474 * Announce the presence of the current Mail version,
475 * give the message count, and print a header listing.
476 */
477
478char *greeting = "Mail version 2.0 %s. Type ? for help.\n";
479
e5717bff 480announce(pr)
ac72cd41 481{
f3bfa857 482 int vec[2], mdot;
ac72cd41
KS
483 extern char *version;
484 register struct message *mp;
485
c52f0860
KS
486 for (mp = &message[0]; mp < &message[msgCount]; mp++)
487 if (mp->m_flag & MNEW)
488 break;
f3bfa857
KS
489 if (mp >= &message[msgCount])
490 for (mp = &message[0]; mp < &message[msgCount]; mp++)
491 if ((mp->m_flag & MREAD) == 0)
492 break;
c52f0860 493 if (mp < &message[msgCount])
f3bfa857 494 mdot = mp - &message[0] + 1;
c52f0860 495 else
f3bfa857
KS
496 mdot = 1;
497 vec[0] = mdot;
ac72cd41 498 vec[1] = 0;
e5717bff 499 if (pr && value("quiet") == NOSTR)
ac72cd41
KS
500 printf(greeting, version);
501 if (msgCount == 1)
e5717bff 502 printf("1 message");
ac72cd41 503 else
e5717bff
KS
504 printf("%d messages", msgCount);
505 if (readonly)
506 printf(" [Read only]");
507 printf("\n");
ac72cd41 508 headers(vec);
f3bfa857 509 dot = &message[mdot - 1];
ac72cd41
KS
510}
511
512strace() {}
513
514/*
515 * Print the current version number.
516 */
517
518pversion(e)
519{
520 printf(greeting, version);
521 return(0);
522}