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