1 |
|
|
/* $OpenBSD: lex.c,v 1.39 2015/10/16 17:56:07 mmcc Exp $ */ |
2 |
|
|
/* $NetBSD: lex.c,v 1.10 1997/05/17 19:55:13 pk Exp $ */ |
3 |
|
|
|
4 |
|
|
/* |
5 |
|
|
* Copyright (c) 1980, 1993 |
6 |
|
|
* The Regents of the University of California. All rights reserved. |
7 |
|
|
* |
8 |
|
|
* Redistribution and use in source and binary forms, with or without |
9 |
|
|
* modification, are permitted provided that the following conditions |
10 |
|
|
* are met: |
11 |
|
|
* 1. Redistributions of source code must retain the above copyright |
12 |
|
|
* notice, this list of conditions and the following disclaimer. |
13 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
14 |
|
|
* notice, this list of conditions and the following disclaimer in the |
15 |
|
|
* documentation and/or other materials provided with the distribution. |
16 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
17 |
|
|
* may be used to endorse or promote products derived from this software |
18 |
|
|
* without specific prior written permission. |
19 |
|
|
* |
20 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
21 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
22 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
23 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
24 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
25 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
26 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
27 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
28 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
29 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
30 |
|
|
* SUCH DAMAGE. |
31 |
|
|
*/ |
32 |
|
|
|
33 |
|
|
#include "rcv.h" |
34 |
|
|
#include <errno.h> |
35 |
|
|
#include <fcntl.h> |
36 |
|
|
#include "extern.h" |
37 |
|
|
|
38 |
|
|
/* |
39 |
|
|
* Mail -- a mail program |
40 |
|
|
* |
41 |
|
|
* Lexical processing of commands. |
42 |
|
|
*/ |
43 |
|
|
|
44 |
|
|
char *prompt = "& "; |
45 |
|
|
|
46 |
|
|
const struct cmd *com; /* command we are running */ |
47 |
|
|
|
48 |
|
|
/* |
49 |
|
|
* Set up editing on the given file name. |
50 |
|
|
* If the first character of name is %, we are considered to be |
51 |
|
|
* editing the file, otherwise we are reading our mail which has |
52 |
|
|
* signficance for mbox and so forth. |
53 |
|
|
*/ |
54 |
|
|
int |
55 |
|
|
setfile(char *name) |
56 |
|
|
{ |
57 |
|
|
FILE *ibuf; |
58 |
|
|
int i, fd; |
59 |
|
|
struct stat stb; |
60 |
|
|
char isedit = *name != '%'; |
61 |
|
|
char *who = name[1] ? name + 1 : myname; |
62 |
|
|
char tempname[PATHSIZE]; |
63 |
|
|
static int shudclob; |
64 |
|
|
|
65 |
|
|
if ((name = expand(name)) == NULL) |
66 |
|
|
return(-1); |
67 |
|
|
|
68 |
|
|
if ((ibuf = Fopen(name, "r")) == NULL) { |
69 |
|
|
if (!isedit && errno == ENOENT) |
70 |
|
|
goto nomail; |
71 |
|
|
warn("%s", name); |
72 |
|
|
return(-1); |
73 |
|
|
} |
74 |
|
|
|
75 |
|
|
if (fstat(fileno(ibuf), &stb) < 0) { |
76 |
|
|
warn("fstat"); |
77 |
|
|
(void)Fclose(ibuf); |
78 |
|
|
return(-1); |
79 |
|
|
} |
80 |
|
|
|
81 |
|
|
switch (stb.st_mode & S_IFMT) { |
82 |
|
|
case S_IFDIR: |
83 |
|
|
(void)Fclose(ibuf); |
84 |
|
|
warnc(EISDIR, "%s", name); |
85 |
|
|
return(-1); |
86 |
|
|
|
87 |
|
|
case S_IFREG: |
88 |
|
|
break; |
89 |
|
|
|
90 |
|
|
default: |
91 |
|
|
(void)Fclose(ibuf); |
92 |
|
|
warnc(EINVAL, "%s", name); |
93 |
|
|
return(-1); |
94 |
|
|
} |
95 |
|
|
|
96 |
|
|
/* |
97 |
|
|
* Looks like all will be well. We must now relinquish our |
98 |
|
|
* hold on the current set of stuff. Must hold signals |
99 |
|
|
* while we are reading the new file, else we will ruin |
100 |
|
|
* the message[] data structure. |
101 |
|
|
*/ |
102 |
|
|
holdsigs(); |
103 |
|
|
if (shudclob) |
104 |
|
|
quit(); |
105 |
|
|
|
106 |
|
|
/* |
107 |
|
|
* Copy the messages into /tmp |
108 |
|
|
* and set pointers. |
109 |
|
|
*/ |
110 |
|
|
readonly = 0; |
111 |
|
|
if ((i = open(name, O_WRONLY, 0)) < 0) |
112 |
|
|
readonly++; |
113 |
|
|
else |
114 |
|
|
(void)close(i); |
115 |
|
|
if (shudclob) { |
116 |
|
|
(void)fclose(itf); |
117 |
|
|
(void)fclose(otf); |
118 |
|
|
} |
119 |
|
|
shudclob = 1; |
120 |
|
|
edit = isedit; |
121 |
|
|
strlcpy(prevfile, mailname, PATHSIZE); |
122 |
|
|
if (name != mailname) |
123 |
|
|
strlcpy(mailname, name, sizeof(mailname)); |
124 |
|
|
mailsize = fsize(ibuf); |
125 |
|
|
(void)snprintf(tempname, sizeof(tempname), |
126 |
|
|
"%s/mail.RxXXXXXXXXXX", tmpdir); |
127 |
|
|
if ((fd = mkostemp(tempname, O_CLOEXEC)) == -1 || |
128 |
|
|
(otf = fdopen(fd, "w")) == NULL) |
129 |
|
|
err(1, "%s", tempname); |
130 |
|
|
if ((itf = fopen(tempname, "re")) == NULL) |
131 |
|
|
err(1, "%s", tempname); |
132 |
|
|
(void)rm(tempname); |
133 |
|
|
setptr(ibuf, (off_t)0); |
134 |
|
|
setmsize(msgCount); |
135 |
|
|
/* |
136 |
|
|
* New mail may have arrived while we were reading |
137 |
|
|
* the mail file, so reset mailsize to be where |
138 |
|
|
* we really are in the file... |
139 |
|
|
*/ |
140 |
|
|
mailsize = ftell(ibuf); |
141 |
|
|
(void)Fclose(ibuf); |
142 |
|
|
relsesigs(); |
143 |
|
|
sawcom = 0; |
144 |
|
|
if (!edit && msgCount == 0) { |
145 |
|
|
nomail: |
146 |
|
|
fprintf(stderr, "No mail for %s\n", who); |
147 |
|
|
return(-1); |
148 |
|
|
} |
149 |
|
|
return(0); |
150 |
|
|
} |
151 |
|
|
|
152 |
|
|
/* |
153 |
|
|
* Incorporate any new mail that has arrived since we first |
154 |
|
|
* started reading mail. |
155 |
|
|
*/ |
156 |
|
|
int |
157 |
|
|
incfile(void) |
158 |
|
|
{ |
159 |
|
|
int newsize; |
160 |
|
|
int omsgCount = msgCount; |
161 |
|
|
FILE *ibuf; |
162 |
|
|
|
163 |
|
|
ibuf = Fopen(mailname, "r"); |
164 |
|
|
if (ibuf == NULL) |
165 |
|
|
return(-1); |
166 |
|
|
holdsigs(); |
167 |
|
|
if (!spool_lock()) { |
168 |
|
|
(void)Fclose(ibuf); |
169 |
|
|
relsesigs(); |
170 |
|
|
return(-1); |
171 |
|
|
} |
172 |
|
|
newsize = fsize(ibuf); |
173 |
|
|
/* make sure mail box has grown and is non-empty */ |
174 |
|
|
if (newsize == 0 || newsize <= mailsize) { |
175 |
|
|
(void)Fclose(ibuf); |
176 |
|
|
spool_unlock(); |
177 |
|
|
relsesigs(); |
178 |
|
|
return(newsize == mailsize ? 0 : -1); |
179 |
|
|
} |
180 |
|
|
setptr(ibuf, mailsize); |
181 |
|
|
setmsize(msgCount); |
182 |
|
|
mailsize = ftell(ibuf); |
183 |
|
|
(void)Fclose(ibuf); |
184 |
|
|
spool_unlock(); |
185 |
|
|
relsesigs(); |
186 |
|
|
return(msgCount - omsgCount); |
187 |
|
|
} |
188 |
|
|
|
189 |
|
|
|
190 |
|
|
int *msgvec; |
191 |
|
|
int reset_on_stop; /* reset prompt if stopped */ |
192 |
|
|
|
193 |
|
|
/* |
194 |
|
|
* Interpret user commands one by one. If standard input is not a tty, |
195 |
|
|
* print no prompt. |
196 |
|
|
*/ |
197 |
|
|
void |
198 |
|
|
commands(void) |
199 |
|
|
{ |
200 |
|
|
int n, sig, *sigp; |
201 |
|
|
int eofloop = 0; |
202 |
|
|
char linebuf[LINESIZE]; |
203 |
|
|
|
204 |
|
|
prompt: |
205 |
|
|
for (;;) { |
206 |
|
|
/* |
207 |
|
|
* Print the prompt, if needed. Clear out |
208 |
|
|
* string space, and flush the output. |
209 |
|
|
*/ |
210 |
|
|
if (!sourcing && value("interactive") != NULL) { |
211 |
|
|
if ((value("autoinc") != NULL) && (incfile() > 0)) |
212 |
|
|
puts("New mail has arrived."); |
213 |
|
|
reset_on_stop = 1; |
214 |
|
|
printf("%s", prompt); |
215 |
|
|
} |
216 |
|
|
fflush(stdout); |
217 |
|
|
sreset(); |
218 |
|
|
/* |
219 |
|
|
* Read a line of commands from the current input |
220 |
|
|
* and handle end of file specially. |
221 |
|
|
*/ |
222 |
|
|
n = 0; |
223 |
|
|
sig = 0; |
224 |
|
|
sigp = sourcing ? NULL : &sig; |
225 |
|
|
for (;;) { |
226 |
|
|
if (readline(input, &linebuf[n], LINESIZE - n, sigp) < 0) { |
227 |
|
|
if (sig) { |
228 |
|
|
if (sig == SIGINT) |
229 |
|
|
dointr(); |
230 |
|
|
else if (sig == SIGHUP) |
231 |
|
|
/* nothing to do? */ |
232 |
|
|
exit(1); |
233 |
|
|
else { |
234 |
|
|
/* Stopped by job control */ |
235 |
|
|
(void)kill(0, sig); |
236 |
|
|
if (reset_on_stop) |
237 |
|
|
reset_on_stop = 0; |
238 |
|
|
} |
239 |
|
|
goto prompt; |
240 |
|
|
} |
241 |
|
|
if (n == 0) |
242 |
|
|
n = -1; |
243 |
|
|
break; |
244 |
|
|
} |
245 |
|
|
if ((n = strlen(linebuf)) == 0) |
246 |
|
|
break; |
247 |
|
|
n--; |
248 |
|
|
if (linebuf[n] != '\\') |
249 |
|
|
break; |
250 |
|
|
linebuf[n++] = ' '; |
251 |
|
|
} |
252 |
|
|
reset_on_stop = 0; |
253 |
|
|
if (n < 0) { |
254 |
|
|
/* eof */ |
255 |
|
|
if (loading) |
256 |
|
|
break; |
257 |
|
|
if (sourcing) { |
258 |
|
|
unstack(); |
259 |
|
|
continue; |
260 |
|
|
} |
261 |
|
|
if (value("interactive") != NULL && |
262 |
|
|
value("ignoreeof") != NULL && |
263 |
|
|
++eofloop < 25) { |
264 |
|
|
puts("Use \"quit\" to quit."); |
265 |
|
|
continue; |
266 |
|
|
} |
267 |
|
|
break; |
268 |
|
|
} |
269 |
|
|
eofloop = 0; |
270 |
|
|
if (execute(linebuf, 0)) |
271 |
|
|
break; |
272 |
|
|
} |
273 |
|
|
} |
274 |
|
|
|
275 |
|
|
/* |
276 |
|
|
* Execute a single command. |
277 |
|
|
* Command functions return 0 for success, 1 for error, and -1 |
278 |
|
|
* for abort. A 1 or -1 aborts a load or source. A -1 aborts |
279 |
|
|
* the interactive command loop. |
280 |
|
|
* Contxt is non-zero if called while composing mail. |
281 |
|
|
*/ |
282 |
|
|
int |
283 |
|
|
execute(char *linebuf, int contxt) |
284 |
|
|
{ |
285 |
|
|
char word[LINESIZE]; |
286 |
|
|
char *arglist[MAXARGC]; |
287 |
|
|
char *cp, *cp2; |
288 |
|
|
int c, muvec[2]; |
289 |
|
|
int e = 1; |
290 |
|
|
|
291 |
|
|
com = NULL; |
292 |
|
|
|
293 |
|
|
/* |
294 |
|
|
* Strip the white space away from the beginning |
295 |
|
|
* of the command, then scan out a word, which |
296 |
|
|
* consists of anything except digits and white space. |
297 |
|
|
* |
298 |
|
|
* Handle ! escapes differently to get the correct |
299 |
|
|
* lexical conventions. |
300 |
|
|
*/ |
301 |
|
|
for (cp = linebuf; isspace((unsigned char)*cp); cp++) |
302 |
|
|
; |
303 |
|
|
if (*cp == '!') { |
304 |
|
|
if (sourcing) { |
305 |
|
|
puts("Can't \"!\" while sourcing"); |
306 |
|
|
goto out; |
307 |
|
|
} |
308 |
|
|
shell(cp+1); |
309 |
|
|
return(0); |
310 |
|
|
} |
311 |
|
|
cp2 = word; |
312 |
|
|
while (*cp && |
313 |
|
|
strchr(" \t0123456789$^.:/-+*'\"", (unsigned char)*cp) == NULL) |
314 |
|
|
*cp2++ = *cp++; |
315 |
|
|
*cp2 = '\0'; |
316 |
|
|
|
317 |
|
|
/* |
318 |
|
|
* Look up the command; if not found, bitch. |
319 |
|
|
* Normally, a blank command would map to the |
320 |
|
|
* first command in the table; while sourcing, |
321 |
|
|
* however, we ignore blank lines to eliminate |
322 |
|
|
* confusion. |
323 |
|
|
*/ |
324 |
|
|
if (sourcing && *word == '\0') |
325 |
|
|
return(0); |
326 |
|
|
com = lex(word); |
327 |
|
|
if (com == NULL) { |
328 |
|
|
printf("Unknown command: \"%s\"\n", word); |
329 |
|
|
goto out; |
330 |
|
|
} |
331 |
|
|
|
332 |
|
|
/* |
333 |
|
|
* See if we should execute the command -- if a conditional |
334 |
|
|
* we always execute it, otherwise, check the state of cond. |
335 |
|
|
*/ |
336 |
|
|
if ((com->c_argtype & F) == 0) |
337 |
|
|
if ((cond == CRCV && !rcvmode) || (cond == CSEND && rcvmode)) |
338 |
|
|
return(0); |
339 |
|
|
|
340 |
|
|
/* |
341 |
|
|
* Process the arguments to the command, depending |
342 |
|
|
* on the type he expects. Default to an error. |
343 |
|
|
* If we are sourcing an interactive command, it's |
344 |
|
|
* an error. |
345 |
|
|
*/ |
346 |
|
|
if (!rcvmode && (com->c_argtype & M) == 0) { |
347 |
|
|
printf("May not execute \"%s\" while sending\n", |
348 |
|
|
com->c_name); |
349 |
|
|
goto out; |
350 |
|
|
} |
351 |
|
|
if (sourcing && com->c_argtype & I) { |
352 |
|
|
printf("May not execute \"%s\" while sourcing\n", |
353 |
|
|
com->c_name); |
354 |
|
|
goto out; |
355 |
|
|
} |
356 |
|
|
if (readonly && com->c_argtype & W) { |
357 |
|
|
printf("May not execute \"%s\" -- message file is read only\n", |
358 |
|
|
com->c_name); |
359 |
|
|
goto out; |
360 |
|
|
} |
361 |
|
|
if (contxt && com->c_argtype & R) { |
362 |
|
|
printf("Cannot recursively invoke \"%s\"\n", com->c_name); |
363 |
|
|
goto out; |
364 |
|
|
} |
365 |
|
|
switch (com->c_argtype & ~(F|P|I|M|T|W|R)) { |
366 |
|
|
case MSGLIST|STRLIST: |
367 |
|
|
/* |
368 |
|
|
* A message list defaulting to nearest forward |
369 |
|
|
* legal message. |
370 |
|
|
*/ |
371 |
|
|
if (msgvec == 0) { |
372 |
|
|
puts("Illegal use of \"message list\""); |
373 |
|
|
break; |
374 |
|
|
} |
375 |
|
|
/* |
376 |
|
|
* remove leading blanks. |
377 |
|
|
*/ |
378 |
|
|
while (isspace((unsigned char)*cp)) |
379 |
|
|
cp++; |
380 |
|
|
|
381 |
|
|
if (isdigit((unsigned char)*cp) || *cp == ':') { |
382 |
|
|
if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0) |
383 |
|
|
break; |
384 |
|
|
/* position to next space - past the message list */ |
385 |
|
|
while (!isspace((unsigned char)*cp)) |
386 |
|
|
cp++; |
387 |
|
|
/* position to next non-space */ |
388 |
|
|
while (isspace((unsigned char)*cp)) |
389 |
|
|
cp++; |
390 |
|
|
} else { |
391 |
|
|
c = 0; /* no message list */ |
392 |
|
|
} |
393 |
|
|
|
394 |
|
|
if (c == 0) { |
395 |
|
|
*msgvec = first(com->c_msgflag, |
396 |
|
|
com->c_msgmask); |
397 |
|
|
msgvec[1] = 0; |
398 |
|
|
} |
399 |
|
|
if (*msgvec == 0) { |
400 |
|
|
puts("No applicable messages"); |
401 |
|
|
break; |
402 |
|
|
} |
403 |
|
|
/* |
404 |
|
|
* Just the straight string, with |
405 |
|
|
* leading blanks removed. |
406 |
|
|
*/ |
407 |
|
|
while (isspace((unsigned char)*cp)) |
408 |
|
|
cp++; |
409 |
|
|
|
410 |
|
|
e = (*com->c_func2)(msgvec, cp); |
411 |
|
|
break; |
412 |
|
|
|
413 |
|
|
case MSGLIST: |
414 |
|
|
/* |
415 |
|
|
* A message list defaulting to nearest forward |
416 |
|
|
* legal message. |
417 |
|
|
*/ |
418 |
|
|
if (msgvec == NULL) { |
419 |
|
|
puts("Illegal use of \"message list\""); |
420 |
|
|
break; |
421 |
|
|
} |
422 |
|
|
if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0) |
423 |
|
|
break; |
424 |
|
|
if (c == 0) { |
425 |
|
|
*msgvec = first(com->c_msgflag, |
426 |
|
|
com->c_msgmask); |
427 |
|
|
msgvec[1] = 0; |
428 |
|
|
} |
429 |
|
|
if (*msgvec == 0) { |
430 |
|
|
puts("No applicable messages"); |
431 |
|
|
break; |
432 |
|
|
} |
433 |
|
|
e = (*com->c_func)(msgvec); |
434 |
|
|
break; |
435 |
|
|
|
436 |
|
|
case NDMLIST: |
437 |
|
|
/* |
438 |
|
|
* A message list with no defaults, but no error |
439 |
|
|
* if none exist. |
440 |
|
|
*/ |
441 |
|
|
if (msgvec == 0) { |
442 |
|
|
puts("Illegal use of \"message list\""); |
443 |
|
|
break; |
444 |
|
|
} |
445 |
|
|
if (getmsglist(cp, msgvec, com->c_msgflag) < 0) |
446 |
|
|
break; |
447 |
|
|
e = (*com->c_func)(msgvec); |
448 |
|
|
break; |
449 |
|
|
|
450 |
|
|
case STRLIST: |
451 |
|
|
/* |
452 |
|
|
* Just the straight string, with |
453 |
|
|
* leading blanks removed. |
454 |
|
|
*/ |
455 |
|
|
while (isspace((unsigned char)*cp)) |
456 |
|
|
cp++; |
457 |
|
|
e = (*com->c_func)(cp); |
458 |
|
|
break; |
459 |
|
|
|
460 |
|
|
case RAWLIST: |
461 |
|
|
/* |
462 |
|
|
* A vector of strings, in shell style. |
463 |
|
|
*/ |
464 |
|
|
if ((c = getrawlist(cp, arglist, |
465 |
|
|
sizeof(arglist) / sizeof(*arglist))) < 0) |
466 |
|
|
break; |
467 |
|
|
if (c < com->c_minargs) { |
468 |
|
|
printf("%s requires at least %d arg(s)\n", |
469 |
|
|
com->c_name, com->c_minargs); |
470 |
|
|
break; |
471 |
|
|
} |
472 |
|
|
if (c > com->c_maxargs) { |
473 |
|
|
printf("%s takes no more than %d arg(s)\n", |
474 |
|
|
com->c_name, com->c_maxargs); |
475 |
|
|
break; |
476 |
|
|
} |
477 |
|
|
e = (*com->c_func)(arglist); |
478 |
|
|
break; |
479 |
|
|
|
480 |
|
|
case NOLIST: |
481 |
|
|
/* |
482 |
|
|
* Just the constant zero, for exiting, |
483 |
|
|
* eg. |
484 |
|
|
*/ |
485 |
|
|
e = (*com->c_func)(0); |
486 |
|
|
break; |
487 |
|
|
|
488 |
|
|
default: |
489 |
|
|
errx(1, "Unknown argtype"); |
490 |
|
|
} |
491 |
|
|
|
492 |
|
|
out: |
493 |
|
|
/* |
494 |
|
|
* Exit the current source file on |
495 |
|
|
* error. |
496 |
|
|
*/ |
497 |
|
|
if (e) { |
498 |
|
|
if (e < 0) |
499 |
|
|
return(1); |
500 |
|
|
if (loading) |
501 |
|
|
return(1); |
502 |
|
|
if (sourcing) |
503 |
|
|
unstack(); |
504 |
|
|
return(0); |
505 |
|
|
} |
506 |
|
|
if (com == NULL) |
507 |
|
|
return(0); |
508 |
|
|
if (value("autoprint") != NULL && com->c_argtype & P) |
509 |
|
|
if ((dot->m_flag & MDELETED) == 0) { |
510 |
|
|
muvec[0] = dot - &message[0] + 1; |
511 |
|
|
muvec[1] = 0; |
512 |
|
|
type(muvec); |
513 |
|
|
} |
514 |
|
|
if (!sourcing && (com->c_argtype & T) == 0) |
515 |
|
|
sawcom = 1; |
516 |
|
|
return(0); |
517 |
|
|
} |
518 |
|
|
|
519 |
|
|
/* |
520 |
|
|
* Set the size of the message vector used to construct argument |
521 |
|
|
* lists to message list functions. |
522 |
|
|
*/ |
523 |
|
|
void |
524 |
|
|
setmsize(int n) |
525 |
|
|
{ |
526 |
|
|
int *msgvec2; |
527 |
|
|
size_t msize; |
528 |
|
|
|
529 |
|
|
msize = (n + 1) * sizeof(*msgvec); |
530 |
|
|
if ((msgvec2 = realloc(msgvec, msize)) == NULL) |
531 |
|
|
err(1, "realloc"); |
532 |
|
|
msgvec = msgvec2; |
533 |
|
|
memset(msgvec, 0, msize); |
534 |
|
|
} |
535 |
|
|
|
536 |
|
|
/* |
537 |
|
|
* Find the correct command in the command table corresponding |
538 |
|
|
* to the passed command "word" |
539 |
|
|
*/ |
540 |
|
|
|
541 |
|
|
const struct cmd * |
542 |
|
|
lex(char *word) |
543 |
|
|
{ |
544 |
|
|
extern const struct cmd cmdtab[]; |
545 |
|
|
const struct cmd *cp; |
546 |
|
|
|
547 |
|
|
if (word[0] == '#') |
548 |
|
|
word = "#"; |
549 |
|
|
for (cp = &cmdtab[0]; cp->c_name != NULL; cp++) |
550 |
|
|
if (isprefix(word, cp->c_name)) |
551 |
|
|
return(cp); |
552 |
|
|
return(NULL); |
553 |
|
|
} |
554 |
|
|
|
555 |
|
|
/* |
556 |
|
|
* Determine if as1 is a valid prefix of as2. |
557 |
|
|
* Return true if yep. |
558 |
|
|
*/ |
559 |
|
|
int |
560 |
|
|
isprefix(char *as1, char *as2) |
561 |
|
|
{ |
562 |
|
|
char *s1, *s2; |
563 |
|
|
|
564 |
|
|
s1 = as1; |
565 |
|
|
s2 = as2; |
566 |
|
|
while (*s1++ == *s2) |
567 |
|
|
if (*s2++ == '\0') |
568 |
|
|
return(1); |
569 |
|
|
return(*--s1 == '\0'); |
570 |
|
|
} |
571 |
|
|
|
572 |
|
|
/* |
573 |
|
|
* The following gets called on receipt of an interrupt. This is |
574 |
|
|
* to abort printout of a command, mainly. |
575 |
|
|
* Dispatching here when command() is inactive crashes rcv. |
576 |
|
|
* Close all open files except 0, 1, 2, and the temporary. |
577 |
|
|
* Also, unstack all source files. |
578 |
|
|
*/ |
579 |
|
|
int inithdr; /* am printing startup headers */ |
580 |
|
|
|
581 |
|
|
void |
582 |
|
|
dointr(void) |
583 |
|
|
{ |
584 |
|
|
|
585 |
|
|
noreset = 0; |
586 |
|
|
if (!inithdr) |
587 |
|
|
sawcom++; |
588 |
|
|
inithdr = 0; |
589 |
|
|
while (sourcing) |
590 |
|
|
unstack(); |
591 |
|
|
|
592 |
|
|
close_all_files(); |
593 |
|
|
|
594 |
|
|
if (image >= 0) { |
595 |
|
|
(void)close(image); |
596 |
|
|
image = -1; |
597 |
|
|
} |
598 |
|
|
fputs("Interrupt\n", stderr); |
599 |
|
|
} |
600 |
|
|
|
601 |
|
|
/* |
602 |
|
|
* Announce the presence of the current Mail version, |
603 |
|
|
* give the message count, and print a header listing. |
604 |
|
|
*/ |
605 |
|
|
void |
606 |
|
|
announce(void) |
607 |
|
|
{ |
608 |
|
|
int vec[2], mdot; |
609 |
|
|
|
610 |
|
|
mdot = newfileinfo(0); |
611 |
|
|
vec[0] = mdot; |
612 |
|
|
vec[1] = 0; |
613 |
|
|
dot = &message[mdot - 1]; |
614 |
|
|
if (msgCount > 0 && value("noheader") == NULL) { |
615 |
|
|
inithdr++; |
616 |
|
|
headers(vec); |
617 |
|
|
inithdr = 0; |
618 |
|
|
} |
619 |
|
|
} |
620 |
|
|
|
621 |
|
|
/* |
622 |
|
|
* Announce information about the file we are editing. |
623 |
|
|
* Return a likely place to set dot. |
624 |
|
|
*/ |
625 |
|
|
int |
626 |
|
|
newfileinfo(int omsgCount) |
627 |
|
|
{ |
628 |
|
|
struct message *mp; |
629 |
|
|
int u, n, mdot, d, s; |
630 |
|
|
char fname[PATHSIZE], zname[PATHSIZE], *ename; |
631 |
|
|
|
632 |
|
|
for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++) |
633 |
|
|
if (mp->m_flag & MNEW) |
634 |
|
|
break; |
635 |
|
|
if (mp >= &message[msgCount]) |
636 |
|
|
for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++) |
637 |
|
|
if ((mp->m_flag & MREAD) == 0) |
638 |
|
|
break; |
639 |
|
|
if (mp < &message[msgCount]) |
640 |
|
|
mdot = mp - &message[0] + 1; |
641 |
|
|
else |
642 |
|
|
mdot = omsgCount + 1; |
643 |
|
|
s = d = 0; |
644 |
|
|
for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) { |
645 |
|
|
if (mp->m_flag & MNEW) |
646 |
|
|
n++; |
647 |
|
|
if ((mp->m_flag & MREAD) == 0) |
648 |
|
|
u++; |
649 |
|
|
if (mp->m_flag & MDELETED) |
650 |
|
|
d++; |
651 |
|
|
if (mp->m_flag & MSAVED) |
652 |
|
|
s++; |
653 |
|
|
} |
654 |
|
|
ename = mailname; |
655 |
|
|
if (getfold(fname, sizeof(fname)) >= 0) { |
656 |
|
|
strlcat(fname, "/", sizeof(fname)); |
657 |
|
|
if (strncmp(fname, mailname, strlen(fname)) == 0) { |
658 |
|
|
(void)snprintf(zname, sizeof(zname), "+%s", |
659 |
|
|
mailname + strlen(fname)); |
660 |
|
|
ename = zname; |
661 |
|
|
} |
662 |
|
|
} |
663 |
|
|
printf("\"%s\": ", ename); |
664 |
|
|
if (msgCount == 1) |
665 |
|
|
fputs("1 message", stdout); |
666 |
|
|
else |
667 |
|
|
printf("%d messages", msgCount); |
668 |
|
|
if (n > 0) |
669 |
|
|
printf(" %d new", n); |
670 |
|
|
if (u-n > 0) |
671 |
|
|
printf(" %d unread", u); |
672 |
|
|
if (d > 0) |
673 |
|
|
printf(" %d deleted", d); |
674 |
|
|
if (s > 0) |
675 |
|
|
printf(" %d saved", s); |
676 |
|
|
if (readonly) |
677 |
|
|
fputs(" [Read only]", stdout); |
678 |
|
|
putchar('\n'); |
679 |
|
|
return(mdot); |
680 |
|
|
} |
681 |
|
|
|
682 |
|
|
/* |
683 |
|
|
* Print the current version number. |
684 |
|
|
*/ |
685 |
|
|
/*ARGSUSED*/ |
686 |
|
|
int |
687 |
|
|
pversion(void *v) |
688 |
|
|
{ |
689 |
|
|
extern const char version[]; |
690 |
|
|
|
691 |
|
|
printf("Version %s\n", version); |
692 |
|
|
return(0); |
693 |
|
|
} |
694 |
|
|
|
695 |
|
|
/* |
696 |
|
|
* Load a file of user definitions. |
697 |
|
|
*/ |
698 |
|
|
void |
699 |
|
|
load(char *name) |
700 |
|
|
{ |
701 |
|
|
FILE *in, *oldin; |
702 |
|
|
|
703 |
|
|
if ((in = Fopen(name, "r")) == NULL) |
704 |
|
|
return; |
705 |
|
|
oldin = input; |
706 |
|
|
input = in; |
707 |
|
|
loading = 1; |
708 |
|
|
sourcing = 1; |
709 |
|
|
commands(); |
710 |
|
|
loading = 0; |
711 |
|
|
sourcing = 0; |
712 |
|
|
input = oldin; |
713 |
|
|
(void)Fclose(in); |
714 |
|
|
} |