xref: /original-bsd/usr.bin/mail/lex.c (revision ba72ef4c)
1 #
2 
3 #include "rcv.h"
4 
5 /*
6  * Mail -- a mail program
7  *
8  * Lexical processing of commands.
9  */
10 
11 static char *SccsId = "@(#)lex.c	1.4 10/10/80";
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 
20 setfile(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 	sawcom = 0;
90 	return(0);
91 }
92 
93 /*
94  * Interpret user commands one by one.  If standard input is not a tty,
95  * print no prompt.
96  */
97 
98 int	*msgvec;
99 
100 commands()
101 {
102 	int prompt, firstsw, stop();
103 	register int n;
104 	char linebuf[LINESIZE];
105 
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 
129 #ifdef CRAZYWOW
130 		if (firstsw == 0 && !sourcing) {
131 			firstsw = -1;
132 			if (rcvmode)
133 				announce(1);
134 		}
135 #endif
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;
144 top:
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 				}
164 				if (value("ignoreeof") != NOSTR && prompt) {
165 					printf("Use \"quit\" to quit.\n");
166 					goto top;
167 				}
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;
184 more:		;
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 
194 execute(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;
202 	int muvec[2];
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;
227 	while (*cp && !any(*cp, " \t0123456789$^./-+*'\""))
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 
249 	/*
250 	 * Special case so that quit causes a return to
251 	 * main, who will call the quit code directly.
252 	 * If we are in a source file, just unstack.
253 	 */
254 
255 	if (com->c_func == edstop && sourcing) {
256 		unstack();
257 		return(0);
258 	}
259 	if (!edit && com->c_func == edstop) {
260 		signal(SIGINT, SIG_IGN);
261 		return(1);
262 	}
263 
264 	/*
265 	 * Process the arguments to the command, depending
266 	 * on the type he expects.  Default to an error.
267 	 * If we are sourcing an interactive command, it's
268 	 * an error.
269 	 */
270 
271 	if (!rcvmode && (com->c_argtype & M) == 0) {
272 		printf("May not execute \"%s\" while sending\n",
273 		    com->c_name);
274 		unstack();
275 		return(0);
276 	}
277 	if (sourcing && com->c_argtype & I) {
278 		printf("May not execute \"%s\" while sourcing\n",
279 		    com->c_name);
280 		unstack();
281 		return(0);
282 	}
283 	if (readonly && com->c_argtype & W) {
284 		printf("May not execute \"%s\" -- message file is read only\n",
285 		   com->c_name);
286 		if (sourcing)
287 			unstack();
288 		return(0);
289 	}
290 	e = 1;
291 	switch (com->c_argtype & ~(P|I|M|W)) {
292 	case MSGLIST:
293 		/*
294 		 * A message list defaulting to nearest forward
295 		 * legal message.
296 		 */
297 		if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
298 			break;
299 		if (c  == 0) {
300 			*msgvec = first(com->c_msgflag,
301 				com->c_msgmask);
302 			msgvec[1] = NULL;
303 		}
304 		if (*msgvec == NULL) {
305 			printf("No applicable messages\n");
306 			break;
307 		}
308 		e = (*com->c_func)(msgvec);
309 		break;
310 
311 	case NDMLIST:
312 		/*
313 		 * A message list with no defaults, but no error
314 		 * if none exist.
315 		 */
316 		if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
317 			break;
318 		e = (*com->c_func)(msgvec);
319 		break;
320 
321 	case STRLIST:
322 		/*
323 		 * Just the straight string, with
324 		 * leading blanks removed.
325 		 */
326 		while (any(*cp, " \t"))
327 			cp++;
328 		e = (*com->c_func)(cp);
329 		break;
330 
331 	case RAWLIST:
332 		/*
333 		 * A vector of strings, in shell style.
334 		 */
335 		if ((c = getrawlist(cp, arglist)) < 0)
336 			break;
337 		if (c < com->c_minargs) {
338 			printf("%s requires at least %d arg(s)\n",
339 				com->c_name, com->c_minargs);
340 			break;
341 		}
342 		if (c > com->c_maxargs) {
343 			printf("%s takes no more than %d arg(s)\n",
344 				com->c_name, com->c_maxargs);
345 			break;
346 		}
347 		e = (*com->c_func)(arglist);
348 		break;
349 
350 	case NOLIST:
351 		/*
352 		 * Just the constant zero, for exiting,
353 		 * eg.
354 		 */
355 		e = (*com->c_func)(0);
356 		break;
357 
358 	default:
359 		panic("Unknown argtype");
360 	}
361 
362 	/*
363 	 * Exit the current source file on
364 	 * error.
365 	 */
366 
367 	if (e && sourcing)
368 		unstack();
369 	if (com->c_func == edstop)
370 		return(1);
371 	if (value("autoprint") != NOSTR && com->c_argtype & P)
372 		if ((dot->m_flag & MDELETED) == 0) {
373 			muvec[0] = dot - &message[0] + 1;
374 			muvec[1] = 0;
375 			type(muvec);
376 		}
377 	if (!sourcing)
378 		sawcom = 1;
379 	return(0);
380 }
381 
382 /*
383  * Set the size of the message vector used to construct argument
384  * lists to message list functions.
385  */
386 
387 setmsize(sz)
388 {
389 
390 	if (msgvec != (int *) 0)
391 		cfree(msgvec);
392 	msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec);
393 }
394 
395 /*
396  * Find the correct command in the command table corresponding
397  * to the passed command "word"
398  */
399 
400 struct cmd *
401 lex(word)
402 	char word[];
403 {
404 	register struct cmd *cp;
405 	extern struct cmd cmdtab[];
406 
407 	for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
408 		if (isprefix(word, cp->c_name))
409 			return(cp);
410 	return(NONE);
411 }
412 
413 /*
414  * Determine if as1 is a valid prefix of as2.
415  * Return true if yep.
416  */
417 
418 isprefix(as1, as2)
419 	char *as1, *as2;
420 {
421 	register char *s1, *s2;
422 
423 	s1 = as1;
424 	s2 = as2;
425 	while (*s1++ == *s2)
426 		if (*s2++ == '\0')
427 			return(1);
428 	return(*--s1 == '\0');
429 }
430 
431 /*
432  * The following gets called on receipt of a rubout.  This is
433  * to abort printout of a command, mainly.
434  * Dispatching here when command() is inactive crashes rcv.
435  * Close all open files except 0, 1, 2, and the temporary.
436  * The special call to getuserid() is needed so it won't get
437  * annoyed about losing its open file.
438  * Also, unstack all source files.
439  */
440 
441 stop()
442 {
443 	register FILE *fp;
444 
445 	noreset = 0;
446 	signal(SIGINT, SIG_IGN);
447 	sawcom++;
448 	while (sourcing)
449 		unstack();
450 	getuserid((char *) -1);
451 	for (fp = &_iob[0]; fp < &_iob[_NFILE]; fp++) {
452 		if (fp == stdin || fp == stdout)
453 			continue;
454 		if (fp == itf || fp == otf)
455 			continue;
456 		if (fp == stderr)
457 			continue;
458 		if (fp == pipef) {
459 			pclose(pipef);
460 			pipef = NULL;
461 			continue;
462 		}
463 		fclose(fp);
464 	}
465 	if (image >= 0) {
466 		close(image);
467 		image = -1;
468 	}
469 	clrbuf(stdout);
470 	printf("Interrupt\n");
471 	signal(SIGINT, stop);
472 	reset(0);
473 }
474 
475 /*
476  * Announce the presence of the current Mail version,
477  * give the message count, and print a header listing.
478  */
479 
480 char	*greeting	= "Mail version 2.0 %s.  Type ? for help.\n";
481 
482 announce(pr)
483 {
484 	int vec[2];
485 	extern char *version;
486 	register struct message *mp;
487 
488 	if (value("hold") != NOSTR)
489 		for (mp = &message[0]; mp < &message[msgCount]; mp++)
490 			mp->m_flag |= MPRESERVE;
491 	vec[0] = 1;
492 	vec[1] = 0;
493 	if (pr && value("quiet") == NOSTR)
494 		printf(greeting, version);
495 	if (msgCount == 1)
496 		printf("1 message");
497 	else
498 		printf("%d messages", msgCount);
499 	if (readonly)
500 		printf(" [Read only]");
501 	printf("\n");
502 	headers(vec);
503 }
504 
505 strace() {}
506 
507 /*
508  * Print the current version number.
509  */
510 
511 pversion(e)
512 {
513 	printf(greeting, version);
514 	return(0);
515 }
516