Freed storage of old message structure to avoid storage leak
[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
73f94fab 11static char *SccsId = "@(#)lex.c 1.2 %G%";
ac72cd41
KS
12
13/*
14 * Interpret user commands one by one. If standard input is not a tty,
15 * print no prompt.
16 */
17
18int *msgvec;
19
20commands()
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;
73f94fab 63top:
ac72cd41
KS
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 }
73f94fab
KS
83 if (value("ignoreeof") != NOSTR && prompt) {
84 printf("Use \"quit\" to quit.\n");
85 goto top;
86 }
ac72cd41
KS
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;
103more: ;
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
113execute(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
295struct cmd *
296lex(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
313isprefix(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
336stop()
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
370char *greeting = "Mail version 2.0 %s. Type ? for help.\n";
371
372announce()
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
392strace() {}
393
394/*
395 * Print the current version number.
396 */
397
398pversion(e)
399{
400 printf(greeting, version);
401 return(0);
402}