xref: /original-bsd/usr.bin/mail/lex.c (revision 552e81d8)
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.2 10/08/80";
12 
13 /*
14  * Interpret user commands one by one.  If standard input is not a tty,
15  * print no prompt.
16  */
17 
18 int	*msgvec;
19 
20 commands()
21 {
22 	int prompt, firstsw, stop();
23 	register int n;
24 	char linebuf[LINESIZE];
25 
26 	msgvec = (int *) calloc((unsigned) (msgCount + 1), sizeof *msgvec);
27 	if (rcvmode)
28 		if (signal(SIGINT, SIG_IGN) == SIG_DFL)
29 			signal(SIGINT, stop);
30 	input = stdin;
31 	prompt = 1;
32 	if (!intty)
33 		prompt = 0;
34 	firstsw = 1;
35 	for (;;) {
36 		setexit();
37 		if (firstsw > 0) {
38 			firstsw = 0;
39 			source1(mailrc);
40 			if (!nosrc)
41 				source1(MASTER);
42 		}
43 
44 		/*
45 		 * How's this for obscure:  after we
46 		 * finish sourcing for the first time,
47 		 * go off and print the headers!
48 		 */
49 
50 		if (firstsw == 0 && !sourcing) {
51 			firstsw = -1;
52 			if (rcvmode)
53 				announce();
54 		}
55 
56 		/*
57 		 * Print the prompt, if needed.  Clear out
58 		 * string space, and flush the output.
59 		 */
60 
61 		if (!rcvmode && !sourcing)
62 			return;
63 top:
64 		if (prompt && !sourcing)
65 			printf("_\r");
66 		flush();
67 		sreset();
68 
69 		/*
70 		 * Read a line of commands from the current input
71 		 * and handle end of file specially.
72 		 */
73 
74 		n = 0;
75 		for (;;) {
76 			if (readline(input, &linebuf[n]) <= 0) {
77 				if (n != 0)
78 					break;
79 				if (sourcing) {
80 					unstack();
81 					goto more;
82 				}
83 				if (value("ignoreeof") != NOSTR && prompt) {
84 					printf("Use \"quit\" to quit.\n");
85 					goto top;
86 				}
87 				if (!edit) {
88 					signal(SIGINT, SIG_IGN);
89 					return;
90 				}
91 				edstop();
92 				return;
93 			}
94 			if ((n = strlen(linebuf)) == 0)
95 				break;
96 			n--;
97 			if (linebuf[n] != '\\')
98 				break;
99 			linebuf[n++] = ' ';
100 		}
101 		if (execute(linebuf))
102 			return;
103 more:		;
104 	}
105 }
106 
107 /*
108  * Execute a single command.  If the command executed
109  * is "quit," then return non-zero so that the caller
110  * will know to return back to main, if he cares.
111  */
112 
113 execute(linebuf)
114 	char linebuf[];
115 {
116 	char word[LINESIZE];
117 	char *arglist[MAXARGC];
118 	struct cmd *com;
119 	register char *cp, *cp2;
120 	register int c;
121 	int edstop(), e;
122 
123 	/*
124 	 * Strip the white space away from the beginning
125 	 * of the command, then scan out a word, which
126 	 * consists of anything except digits and white space.
127 	 *
128 	 * Handle ! escapes differently to get the correct
129 	 * lexical conventions.
130 	 */
131 
132 	cp = linebuf;
133 	while (any(*cp, " \t"))
134 		cp++;
135 	if (*cp == '!') {
136 		if (sourcing) {
137 			printf("Can't \"!\" while sourcing\n");
138 			unstack();
139 			return(0);
140 		}
141 		shell(cp+1);
142 		return(0);
143 	}
144 	cp2 = word;
145 	while (*cp && !any(*cp, " \t0123456789$^.-+*'\""))
146 		*cp2++ = *cp++;
147 	*cp2 = '\0';
148 
149 	/*
150 	 * Look up the command; if not found, bitch.
151 	 * Normally, a blank command would map to the
152 	 * first command in the table; while sourcing,
153 	 * however, we ignore blank lines to eliminate
154 	 * confusion.
155 	 */
156 
157 	if (sourcing && equal(word, ""))
158 		return(0);
159 	com = lex(word);
160 	if (com == NONE) {
161 		printf("What?\n");
162 		if (sourcing)
163 			unstack();
164 		return(0);
165 	}
166 
167 	/*
168 	 * Special case so that quit causes a return to
169 	 * main, who will call the quit code directly.
170 	 * If we are in a source file, just unstack.
171 	 */
172 
173 	if (com->c_func == edstop && sourcing) {
174 		unstack();
175 		return(0);
176 	}
177 	if (!edit && com->c_func == edstop) {
178 		signal(SIGINT, SIG_IGN);
179 		return(1);
180 	}
181 
182 	/*
183 	 * Process the arguments to the command, depending
184 	 * on the type he expects.  Default to an error.
185 	 * If we are sourcing an interactive command, it's
186 	 * an error.
187 	 */
188 
189 	if (!rcvmode && (com->c_argtype & M) == 0) {
190 		printf("May not execute \"%s\" while sending\n",
191 		    com->c_name);
192 		unstack();
193 		return(0);
194 	}
195 	if (sourcing && com->c_argtype & I) {
196 		printf("May not execute \"%s\" while sourcing\n",
197 		    com->c_name);
198 		unstack();
199 		return(0);
200 	}
201 	e = 1;
202 	switch (com->c_argtype & ~(P|I|M)) {
203 	case MSGLIST:
204 		/*
205 		 * A message list defaulting to nearest forward
206 		 * legal message.
207 		 */
208 		if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
209 			break;
210 		if (c  == 0) {
211 			*msgvec = first(com->c_msgflag,
212 				com->c_msgmask);
213 			msgvec[1] = NULL;
214 		}
215 		if (*msgvec == NULL) {
216 			printf("No applicable messages\n");
217 			break;
218 		}
219 		e = (*com->c_func)(msgvec);
220 		break;
221 
222 	case NDMLIST:
223 		/*
224 		 * A message list with no defaults, but no error
225 		 * if none exist.
226 		 */
227 		if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
228 			break;
229 		e = (*com->c_func)(msgvec);
230 		break;
231 
232 	case STRLIST:
233 		/*
234 		 * Just the straight string, with
235 		 * leading blanks removed.
236 		 */
237 		while (any(*cp, " \t"))
238 			cp++;
239 		e = (*com->c_func)(cp);
240 		break;
241 
242 	case RAWLIST:
243 		/*
244 		 * A vector of strings, in shell style.
245 		 */
246 		if ((c = getrawlist(cp, arglist)) < 0)
247 			break;
248 		if (c < com->c_minargs) {
249 			printf("%s requires at least %d arg(s)\n",
250 				com->c_name, com->c_minargs);
251 			break;
252 		}
253 		if (c > com->c_maxargs) {
254 			printf("%s takes no more than %d arg(s)\n",
255 				com->c_name, com->c_maxargs);
256 			break;
257 		}
258 		e = (*com->c_func)(arglist);
259 		break;
260 
261 	case NOLIST:
262 		/*
263 		 * Just the constant zero, for exiting,
264 		 * eg.
265 		 */
266 		e = (*com->c_func)(0);
267 		break;
268 
269 	default:
270 		panic("Unknown argtype");
271 	}
272 
273 	/*
274 	 * Exit the current source file on
275 	 * error.
276 	 */
277 
278 	if (e && sourcing)
279 		unstack();
280 	if (com->c_func == edstop)
281 		return(1);
282 	if (value("autoprint") != NOSTR && com->c_argtype & P)
283 		if ((dot->m_flag & MDELETED) == 0)
284 			print(dot);
285 	if (!sourcing)
286 		sawcom = 1;
287 	return(0);
288 }
289 
290 /*
291  * Find the correct command in the command table corresponding
292  * to the passed command "word"
293  */
294 
295 struct cmd *
296 lex(word)
297 	char word[];
298 {
299 	register struct cmd *cp;
300 	extern struct cmd cmdtab[];
301 
302 	for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
303 		if (isprefix(word, cp->c_name))
304 			return(cp);
305 	return(NONE);
306 }
307 
308 /*
309  * Determine if as1 is a valid prefix of as2.
310  * Return true if yep.
311  */
312 
313 isprefix(as1, as2)
314 	char *as1, *as2;
315 {
316 	register char *s1, *s2;
317 
318 	s1 = as1;
319 	s2 = as2;
320 	while (*s1++ == *s2)
321 		if (*s2++ == '\0')
322 			return(1);
323 	return(*--s1 == '\0');
324 }
325 
326 /*
327  * The following gets called on receipt of a rubout.  This is
328  * to abort printout of a command, mainly.
329  * Dispatching here when command() is inactive crashes rcv.
330  * Close all open files except 0, 1, 2, and the temporary.
331  * The special call to getuserid() is needed so it won't get
332  * annoyed about losing its open file.
333  * Also, unstack all source files.
334  */
335 
336 stop()
337 {
338 	register FILE *fp;
339 
340 	noreset = 0;
341 	signal(SIGINT, SIG_IGN);
342 	sawcom++;
343 	while (sourcing)
344 		unstack();
345 	getuserid((char *) -1);
346 	for (fp = &_iob[0]; fp < &_iob[_NFILE]; fp++) {
347 		if (fp == stdin || fp == stdout)
348 			continue;
349 		if (fp == itf || fp == otf)
350 			continue;
351 		if (fp == stderr)
352 			continue;
353 		fclose(fp);
354 	}
355 	if (image >= 0) {
356 		close(image);
357 		image = -1;
358 	}
359 	clrbuf(stdout);
360 	printf("Interrupt\n");
361 	signal(SIGINT, stop);
362 	reset(0);
363 }
364 
365 /*
366  * Announce the presence of the current Mail version,
367  * give the message count, and print a header listing.
368  */
369 
370 char	*greeting	= "Mail version 2.0 %s.  Type ? for help.\n";
371 
372 announce()
373 {
374 	int vec[2];
375 	extern char *version;
376 	register struct message *mp;
377 
378 	if (value("hold") != NOSTR)
379 		for (mp = &message[0]; mp < &message[msgCount]; mp++)
380 			mp->m_flag |= MPRESERVE;
381 	vec[0] = 1;
382 	vec[1] = 0;
383 	if (value("quiet") == NOSTR)
384 		printf(greeting, version);
385 	if (msgCount == 1)
386 		printf("1 message:\n");
387 	else
388 		printf("%d messages:\n", msgCount);
389 	headers(vec);
390 }
391 
392 strace() {}
393 
394 /*
395  * Print the current version number.
396  */
397 
398 pversion(e)
399 {
400 	printf(greeting, version);
401 	return(0);
402 }
403