lex.c revision 1.33 1 1.33 christos /* $NetBSD: lex.c,v 1.33 2007/01/03 00:39:16 christos Exp $ */
2 1.7 christos
3 1.1 cgd /*
4 1.4 deraadt * Copyright (c) 1980, 1993
5 1.4 deraadt * The Regents of the University of California. All rights reserved.
6 1.1 cgd *
7 1.1 cgd * Redistribution and use in source and binary forms, with or without
8 1.1 cgd * modification, are permitted provided that the following conditions
9 1.1 cgd * are met:
10 1.1 cgd * 1. Redistributions of source code must retain the above copyright
11 1.1 cgd * notice, this list of conditions and the following disclaimer.
12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 cgd * notice, this list of conditions and the following disclaimer in the
14 1.1 cgd * documentation and/or other materials provided with the distribution.
15 1.23 agc * 3. Neither the name of the University nor the names of its contributors
16 1.1 cgd * may be used to endorse or promote products derived from this software
17 1.1 cgd * without specific prior written permission.
18 1.1 cgd *
19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 1.1 cgd * SUCH DAMAGE.
30 1.1 cgd */
31 1.1 cgd
32 1.11 lukem #include <sys/cdefs.h>
33 1.1 cgd #ifndef lint
34 1.7 christos #if 0
35 1.8 tls static char sccsid[] = "@(#)lex.c 8.2 (Berkeley) 4/20/95";
36 1.7 christos #else
37 1.33 christos __RCSID("$NetBSD: lex.c,v 1.33 2007/01/03 00:39:16 christos Exp $");
38 1.7 christos #endif
39 1.1 cgd #endif /* not lint */
40 1.1 cgd
41 1.30 christos #include <assert.h>
42 1.30 christos
43 1.1 cgd #include "rcv.h"
44 1.28 christos #include <util.h>
45 1.4 deraadt #include "extern.h"
46 1.28 christos #ifdef USE_EDITLINE
47 1.26 christos #include "complete.h"
48 1.26 christos #endif
49 1.30 christos #include "format.h"
50 1.30 christos #include "thread.h"
51 1.26 christos
52 1.1 cgd /*
53 1.1 cgd * Mail -- a mail program
54 1.1 cgd *
55 1.1 cgd * Lexical processing of commands.
56 1.1 cgd */
57 1.1 cgd
58 1.30 christos static const char *prompt = DEFAULT_PROMPT;
59 1.30 christos static int *msgvec;
60 1.30 christos static int reset_on_stop; /* do a reset() if stopped */
61 1.30 christos
62 1.30 christos
63 1.30 christos /*
64 1.30 christos * Set the size of the message vector used to construct argument
65 1.30 christos * lists to message list functions.
66 1.30 christos */
67 1.30 christos static void
68 1.30 christos setmsize(int sz)
69 1.30 christos {
70 1.30 christos if (msgvec != 0)
71 1.30 christos free(msgvec);
72 1.30 christos msgvec = ecalloc((size_t) (sz + 1), sizeof *msgvec);
73 1.30 christos }
74 1.1 cgd
75 1.1 cgd /*
76 1.1 cgd * Set up editing on the given file name.
77 1.1 cgd * If the first character of name is %, we are considered to be
78 1.1 cgd * editing the file, otherwise we are reading our mail which has
79 1.1 cgd * signficance for mbox and so forth.
80 1.1 cgd */
81 1.30 christos PUBLIC int
82 1.24 christos setfile(const char *name)
83 1.1 cgd {
84 1.1 cgd FILE *ibuf;
85 1.20 wiz int i, fd;
86 1.1 cgd struct stat stb;
87 1.30 christos char isedit = *name != '%' || getuserid(myname) != (int)getuid();
88 1.24 christos const char *who = name[1] ? name + 1 : myname;
89 1.1 cgd static int shudclob;
90 1.20 wiz char tempname[PATHSIZE];
91 1.1 cgd
92 1.18 wiz if ((name = expand(name)) == NULL)
93 1.1 cgd return -1;
94 1.1 cgd
95 1.1 cgd if ((ibuf = Fopen(name, "r")) == NULL) {
96 1.1 cgd if (!isedit && errno == ENOENT)
97 1.1 cgd goto nomail;
98 1.22 wiz warn("%s", name);
99 1.30 christos return -1;
100 1.1 cgd }
101 1.1 cgd
102 1.1 cgd if (fstat(fileno(ibuf), &stb) < 0) {
103 1.22 wiz warn("fstat");
104 1.25 christos (void)Fclose(ibuf);
105 1.30 christos return -1;
106 1.1 cgd }
107 1.1 cgd
108 1.1 cgd switch (stb.st_mode & S_IFMT) {
109 1.1 cgd case S_IFDIR:
110 1.25 christos (void)Fclose(ibuf);
111 1.1 cgd errno = EISDIR;
112 1.22 wiz warn("%s", name);
113 1.30 christos return -1;
114 1.1 cgd
115 1.1 cgd case S_IFREG:
116 1.1 cgd break;
117 1.1 cgd
118 1.1 cgd default:
119 1.25 christos (void)Fclose(ibuf);
120 1.1 cgd errno = EINVAL;
121 1.22 wiz warn("%s", name);
122 1.30 christos return -1;
123 1.1 cgd }
124 1.1 cgd
125 1.1 cgd /*
126 1.1 cgd * Looks like all will be well. We must now relinquish our
127 1.1 cgd * hold on the current set of stuff. Must hold signals
128 1.1 cgd * while we are reading the new file, else we will ruin
129 1.1 cgd * the message[] data structure.
130 1.1 cgd */
131 1.1 cgd
132 1.1 cgd holdsigs();
133 1.1 cgd if (shudclob)
134 1.1 cgd quit();
135 1.1 cgd
136 1.1 cgd /*
137 1.1 cgd * Copy the messages into /tmp
138 1.1 cgd * and set pointers.
139 1.1 cgd */
140 1.1 cgd
141 1.1 cgd readonly = 0;
142 1.33 christos if ((i = open(name, O_WRONLY)) < 0)
143 1.1 cgd readonly++;
144 1.1 cgd else
145 1.25 christos (void)close(i);
146 1.1 cgd if (shudclob) {
147 1.25 christos (void)fclose(itf);
148 1.25 christos (void)fclose(otf);
149 1.1 cgd }
150 1.1 cgd shudclob = 1;
151 1.1 cgd edit = isedit;
152 1.25 christos (void)strcpy(prevfile, mailname);
153 1.1 cgd if (name != mailname)
154 1.25 christos (void)strcpy(mailname, name);
155 1.1 cgd mailsize = fsize(ibuf);
156 1.20 wiz (void)snprintf(tempname, sizeof(tempname),
157 1.20 wiz "%s/mail.RxXXXXXXXXXX", tmpdir);
158 1.20 wiz if ((fd = mkstemp(tempname)) == -1 ||
159 1.20 wiz (otf = fdopen(fd, "w")) == NULL)
160 1.20 wiz err(1, "%s", tempname);
161 1.28 christos (void)fcntl(fileno(otf), F_SETFD, FD_CLOEXEC);
162 1.20 wiz if ((itf = fopen(tempname, "r")) == NULL)
163 1.20 wiz err(1, "%s", tempname);
164 1.28 christos (void)fcntl(fileno(itf), F_SETFD, FD_CLOEXEC);
165 1.25 christos (void)rm(tempname);
166 1.25 christos setptr(ibuf, (off_t)0);
167 1.30 christos setmsize(get_abs_msgCount());
168 1.8 tls /*
169 1.9 mikel * New mail may have arrived while we were reading
170 1.9 mikel * the mail file, so reset mailsize to be where
171 1.8 tls * we really are in the file...
172 1.8 tls */
173 1.8 tls mailsize = ftell(ibuf);
174 1.25 christos (void)Fclose(ibuf);
175 1.1 cgd relsesigs();
176 1.1 cgd sawcom = 0;
177 1.30 christos if (!edit && get_abs_msgCount() == 0) {
178 1.1 cgd nomail:
179 1.25 christos (void)fprintf(stderr, "No mail for %s\n", who);
180 1.1 cgd return -1;
181 1.1 cgd }
182 1.30 christos return 0;
183 1.1 cgd }
184 1.1 cgd
185 1.8 tls /*
186 1.8 tls * Incorporate any new mail that has arrived since we first
187 1.8 tls * started reading mail.
188 1.8 tls */
189 1.30 christos PUBLIC int
190 1.17 wiz incfile(void)
191 1.8 tls {
192 1.25 christos off_t newsize;
193 1.30 christos int omsgCount;
194 1.8 tls FILE *ibuf;
195 1.8 tls
196 1.30 christos omsgCount = get_abs_msgCount();
197 1.30 christos
198 1.8 tls ibuf = Fopen(mailname, "r");
199 1.8 tls if (ibuf == NULL)
200 1.8 tls return -1;
201 1.8 tls holdsigs();
202 1.8 tls newsize = fsize(ibuf);
203 1.8 tls if (newsize == 0)
204 1.9 mikel return -1; /* mail box is now empty??? */
205 1.8 tls if (newsize < mailsize)
206 1.8 tls return -1; /* mail box has shrunk??? */
207 1.8 tls if (newsize == mailsize)
208 1.8 tls return 0; /* no new mail */
209 1.30 christos setptr(ibuf, mailsize); /* read in new mail */
210 1.30 christos setmsize(get_abs_msgCount()); /* get the new message count */
211 1.8 tls mailsize = ftell(ibuf);
212 1.25 christos (void)Fclose(ibuf);
213 1.8 tls relsesigs();
214 1.30 christos return get_abs_msgCount() - omsgCount;
215 1.8 tls }
216 1.8 tls
217 1.29 christos /*
218 1.29 christos * Return a pointer to the comment character, respecting quoting as
219 1.29 christos * done in getrawlist(). The comment character is ignored inside
220 1.29 christos * quotes.
221 1.29 christos */
222 1.29 christos static char *
223 1.29 christos comment_char(char *line)
224 1.29 christos {
225 1.29 christos char *p;
226 1.29 christos char quotec;
227 1.29 christos quotec = '\0';
228 1.29 christos for (p = line; *p; p++) {
229 1.29 christos if (quotec != '\0') {
230 1.29 christos if (*p == quotec)
231 1.29 christos quotec = '\0';
232 1.29 christos }
233 1.29 christos else if (*p == '"' || *p == '\'')
234 1.29 christos quotec = *p;
235 1.29 christos else if (*p == COMMENT_CHAR)
236 1.29 christos return p;
237 1.29 christos }
238 1.29 christos return NULL;
239 1.29 christos }
240 1.29 christos
241 1.30 christos /*
242 1.30 christos * When we wake up after ^Z, reprint the prompt.
243 1.30 christos */
244 1.30 christos static void
245 1.30 christos stop(int s)
246 1.30 christos {
247 1.30 christos sig_t old_action = signal(s, SIG_DFL);
248 1.30 christos sigset_t nset;
249 1.30 christos
250 1.30 christos (void)sigemptyset(&nset);
251 1.30 christos (void)sigaddset(&nset, s);
252 1.30 christos (void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
253 1.30 christos (void)kill(0, s);
254 1.30 christos (void)sigprocmask(SIG_BLOCK, &nset, NULL);
255 1.30 christos (void)signal(s, old_action);
256 1.30 christos if (reset_on_stop) {
257 1.30 christos reset_on_stop = 0;
258 1.30 christos reset(0);
259 1.30 christos }
260 1.30 christos }
261 1.30 christos
262 1.29 christos
263 1.1 cgd
264 1.1 cgd /*
265 1.30 christos * Signal handler is hooked by setup_piping().
266 1.30 christos * Respond to a broken pipe signal --
267 1.30 christos * probably caused by quitting more.
268 1.1 cgd */
269 1.30 christos static jmp_buf pipestop;
270 1.30 christos
271 1.30 christos /*ARGSUSED*/
272 1.30 christos static void
273 1.30 christos brokpipe(int signo __unused)
274 1.1 cgd {
275 1.30 christos longjmp(pipestop, 1);
276 1.30 christos }
277 1.1 cgd
278 1.30 christos /*
279 1.30 christos * Check the command line for any requested piping or redirection,
280 1.30 christos * depending on the value of 'c'. If "enable-pipes" is set, search
281 1.30 christos * the command line (cp) for the first occurrence of the character 'c'
282 1.30 christos * that is not in a quote or (parenthese) group.
283 1.30 christos */
284 1.30 christos PUBLIC char *
285 1.30 christos shellpr(char *cp)
286 1.30 christos {
287 1.30 christos int quotec;
288 1.30 christos int level;
289 1.30 christos
290 1.30 christos if (cp == NULL || value(ENAME_ENABLE_PIPES) == NULL)
291 1.30 christos return NULL;
292 1.30 christos
293 1.30 christos level = 0;
294 1.30 christos quotec = 0;
295 1.30 christos for (/*EMPTY*/; *cp != '\0'; cp++) {
296 1.30 christos if (quotec) {
297 1.30 christos if (*cp == quotec)
298 1.30 christos quotec = 0;
299 1.30 christos if (*cp == '\\' &&
300 1.30 christos (cp[1] == quotec || cp[1] == '\\'))
301 1.30 christos cp++;
302 1.30 christos }
303 1.30 christos else {
304 1.30 christos switch (*cp) {
305 1.30 christos case '|':
306 1.30 christos case '>':
307 1.30 christos if (level == 0)
308 1.30 christos return cp;
309 1.30 christos break;
310 1.30 christos case '(':
311 1.30 christos level++;
312 1.1 cgd break;
313 1.30 christos case ')':
314 1.30 christos level--;
315 1.1 cgd break;
316 1.30 christos case '"':
317 1.30 christos case '\'':
318 1.30 christos quotec = *cp;
319 1.1 cgd break;
320 1.30 christos default:
321 1.1 cgd break;
322 1.1 cgd }
323 1.30 christos }
324 1.30 christos }
325 1.30 christos return NULL;
326 1.30 christos }
327 1.30 christos
328 1.30 christos /*
329 1.30 christos * Setup any pipe or redirection that the command line indicates.
330 1.30 christos * If none, then setup the pager unless "pager-off" is defined.
331 1.30 christos */
332 1.30 christos static FILE *fp_stop = NULL;
333 1.30 christos static int oldfd1 = -1;
334 1.30 christos static int
335 1.30 christos setup_piping(char *cmdline, int c_pipe)
336 1.30 christos {
337 1.30 christos FILE *fout;
338 1.30 christos FILE *last_file;
339 1.30 christos char *cp;
340 1.30 christos
341 1.30 christos last_file = last_registered_file(0);
342 1.30 christos
343 1.30 christos fout = NULL;
344 1.30 christos if ((cp = shellpr(cmdline)) != NULL) {
345 1.30 christos char c;
346 1.30 christos c = *cp;
347 1.30 christos *cp = '\0';
348 1.30 christos cp++;
349 1.30 christos
350 1.30 christos if (c == '|') {
351 1.30 christos if ((fout = Popen(cp, "w")) == NULL) {
352 1.30 christos warn("Popen: %s", cp);
353 1.30 christos return -1;
354 1.26 christos }
355 1.30 christos }
356 1.30 christos else {
357 1.30 christos const char *mode;
358 1.30 christos assert(c == '>');
359 1.30 christos mode = *cp == '>' ? "a" : "w";
360 1.30 christos if (*cp == '>')
361 1.30 christos cp++;
362 1.30 christos
363 1.30 christos cp = skip_blank(cp);
364 1.30 christos if ((fout = Fopen(cp, mode)) == NULL) {
365 1.30 christos warn("Fopen: %s", cp);
366 1.30 christos return -1;
367 1.1 cgd }
368 1.1 cgd }
369 1.30 christos
370 1.30 christos }
371 1.31 christos else if (value(ENAME_PAGER_OFF) == NULL && (c_pipe & C_PIPE_PAGER ||
372 1.31 christos (c_pipe & C_PIPE_CRT && value(ENAME_CRT) != NULL))) {
373 1.30 christos const char *pager;
374 1.30 christos pager = value(ENAME_PAGER);
375 1.30 christos if (pager == NULL || *pager == '\0')
376 1.30 christos pager = _PATH_MORE;
377 1.30 christos
378 1.30 christos if ((fout = Popen(pager, "w")) == NULL) {
379 1.30 christos warn("Popen: %s", pager);
380 1.30 christos return -1;
381 1.30 christos }
382 1.30 christos }
383 1.30 christos
384 1.30 christos if (fout) {
385 1.30 christos (void)signal(SIGPIPE, brokpipe);
386 1.30 christos (void)fflush(stdout);
387 1.33 christos if ((oldfd1 = dup(STDOUT_FILENO)) == -1)
388 1.30 christos err(EXIT_FAILURE, "dup failed");
389 1.33 christos if (dup2(fileno(fout), STDOUT_FILENO) == -1)
390 1.30 christos err(EXIT_FAILURE, "dup2 failed");
391 1.30 christos fp_stop = last_file;
392 1.30 christos }
393 1.30 christos return 0;
394 1.30 christos }
395 1.30 christos
396 1.30 christos /*
397 1.30 christos * This will close any piping started by setup_piping().
398 1.30 christos */
399 1.30 christos static void
400 1.30 christos close_piping(void)
401 1.30 christos {
402 1.30 christos if (oldfd1 != -1) {
403 1.30 christos (void)fflush(stdout);
404 1.33 christos if (fileno(stdout) != oldfd1 &&
405 1.33 christos dup2(oldfd1, STDOUT_FILENO) == -1)
406 1.30 christos err(EXIT_FAILURE, "dup2 failed");
407 1.30 christos
408 1.30 christos (void)signal(SIGPIPE, SIG_IGN);
409 1.30 christos close_top_files(fp_stop);
410 1.30 christos fp_stop = NULL;
411 1.30 christos (void)close(oldfd1);
412 1.30 christos oldfd1 = -1;
413 1.30 christos (void)signal(SIGPIPE, SIG_DFL);
414 1.1 cgd }
415 1.1 cgd }
416 1.1 cgd
417 1.1 cgd /*
418 1.30 christos * Determine if as1 is a valid prefix of as2.
419 1.30 christos * Return true if yep.
420 1.30 christos */
421 1.30 christos static int
422 1.30 christos isprefix(char *as1, const char *as2)
423 1.30 christos {
424 1.30 christos char *s1;
425 1.30 christos const char *s2;
426 1.30 christos
427 1.30 christos s1 = as1;
428 1.30 christos s2 = as2;
429 1.30 christos while (*s1++ == *s2)
430 1.30 christos if (*s2++ == '\0')
431 1.30 christos return 1;
432 1.30 christos return *--s1 == '\0';
433 1.30 christos }
434 1.30 christos
435 1.30 christos /*
436 1.30 christos * Find the correct command in the command table corresponding
437 1.30 christos * to the passed command "word"
438 1.30 christos */
439 1.30 christos PUBLIC const struct cmd *
440 1.30 christos lex(char word[])
441 1.30 christos {
442 1.30 christos const struct cmd *cp;
443 1.30 christos
444 1.30 christos for (cp = &cmdtab[0]; cp->c_name != NULL; cp++)
445 1.30 christos if (isprefix(word, cp->c_name))
446 1.30 christos return cp;
447 1.30 christos return NULL;
448 1.30 christos }
449 1.30 christos
450 1.30 christos PUBLIC char *
451 1.30 christos get_cmdname(char *buf)
452 1.30 christos {
453 1.30 christos char *cp;
454 1.30 christos char *cmd;
455 1.30 christos size_t len;
456 1.30 christos
457 1.30 christos for (cp = buf; *cp; cp++)
458 1.30 christos if (strchr(" \t0123456789$^.:/-+*'\">|", *cp) != NULL)
459 1.30 christos break;
460 1.33 christos /* XXX - Don't miss the pipe command! */
461 1.33 christos if (cp == buf && *cp == '|')
462 1.33 christos cp++;
463 1.30 christos len = cp - buf + 1;
464 1.30 christos cmd = salloc(len);
465 1.30 christos (void)strlcpy(cmd, buf, len);
466 1.30 christos return cmd;
467 1.30 christos }
468 1.30 christos
469 1.30 christos /*
470 1.1 cgd * Execute a single command.
471 1.1 cgd * Command functions return 0 for success, 1 for error, and -1
472 1.1 cgd * for abort. A 1 or -1 aborts a load or source. A -1 aborts
473 1.1 cgd * the interactive command loop.
474 1.32 christos * execute_contxt_e is in extern.h.
475 1.1 cgd */
476 1.30 christos PUBLIC int
477 1.32 christos execute(char linebuf[], enum execute_contxt_e contxt)
478 1.1 cgd {
479 1.30 christos char *word;
480 1.1 cgd char *arglist[MAXARGC];
481 1.7 christos const struct cmd *com = NULL;
482 1.30 christos char *volatile cp;
483 1.11 lukem int c;
484 1.1 cgd int e = 1;
485 1.1 cgd
486 1.1 cgd /*
487 1.1 cgd * Strip the white space away from the beginning
488 1.1 cgd * of the command, then scan out a word, which
489 1.1 cgd * consists of anything except digits and white space.
490 1.1 cgd *
491 1.1 cgd * Handle ! escapes differently to get the correct
492 1.1 cgd * lexical conventions.
493 1.1 cgd */
494 1.1 cgd
495 1.30 christos cp = skip_blank(linebuf);
496 1.1 cgd if (*cp == '!') {
497 1.1 cgd if (sourcing) {
498 1.25 christos (void)printf("Can't \"!\" while sourcing\n");
499 1.1 cgd goto out;
500 1.1 cgd }
501 1.28 christos (void)shell(cp + 1);
502 1.30 christos return 0;
503 1.1 cgd }
504 1.30 christos
505 1.30 christos word = get_cmdname(cp);
506 1.30 christos cp += strlen(word);
507 1.1 cgd
508 1.1 cgd /*
509 1.1 cgd * Look up the command; if not found, bitch.
510 1.1 cgd * Normally, a blank command would map to the
511 1.1 cgd * first command in the table; while sourcing,
512 1.1 cgd * however, we ignore blank lines to eliminate
513 1.1 cgd * confusion.
514 1.1 cgd */
515 1.1 cgd
516 1.1 cgd if (sourcing && *word == '\0')
517 1.30 christos return 0;
518 1.1 cgd com = lex(word);
519 1.19 wiz if (com == NULL) {
520 1.25 christos (void)printf("Unknown command: \"%s\"\n", word);
521 1.1 cgd goto out;
522 1.1 cgd }
523 1.1 cgd
524 1.1 cgd /*
525 1.1 cgd * See if we should execute the command -- if a conditional
526 1.1 cgd * we always execute it, otherwise, check the state of cond.
527 1.1 cgd */
528 1.1 cgd
529 1.30 christos if ((com->c_argtype & F) == 0 && (cond & CSKIP))
530 1.30 christos return 0;
531 1.1 cgd
532 1.1 cgd /*
533 1.1 cgd * Process the arguments to the command, depending
534 1.1 cgd * on the type he expects. Default to an error.
535 1.1 cgd * If we are sourcing an interactive command, it's
536 1.1 cgd * an error.
537 1.1 cgd */
538 1.1 cgd
539 1.30 christos if (mailmode == mm_sending && (com->c_argtype & M) == 0) {
540 1.25 christos (void)printf("May not execute \"%s\" while sending\n",
541 1.1 cgd com->c_name);
542 1.1 cgd goto out;
543 1.1 cgd }
544 1.1 cgd if (sourcing && com->c_argtype & I) {
545 1.25 christos (void)printf("May not execute \"%s\" while sourcing\n",
546 1.1 cgd com->c_name);
547 1.1 cgd goto out;
548 1.1 cgd }
549 1.1 cgd if (readonly && com->c_argtype & W) {
550 1.25 christos (void)printf("May not execute \"%s\" -- message file is read only\n",
551 1.1 cgd com->c_name);
552 1.1 cgd goto out;
553 1.1 cgd }
554 1.32 christos if (contxt == ec_composing && com->c_argtype & R) {
555 1.25 christos (void)printf("Cannot recursively invoke \"%s\"\n", com->c_name);
556 1.1 cgd goto out;
557 1.1 cgd }
558 1.30 christos
559 1.30 christos if (!sourcing && com->c_pipe && value(ENAME_INTERACTIVE) != NULL) {
560 1.30 christos if (setjmp(pipestop))
561 1.30 christos goto out;
562 1.30 christos
563 1.30 christos if (setup_piping(cp, com->c_pipe) == -1)
564 1.30 christos goto out;
565 1.30 christos }
566 1.30 christos switch (com->c_argtype & ARGTYPE_MASK) {
567 1.1 cgd case MSGLIST:
568 1.1 cgd /*
569 1.1 cgd * A message list defaulting to nearest forward
570 1.1 cgd * legal message.
571 1.1 cgd */
572 1.1 cgd if (msgvec == 0) {
573 1.25 christos (void)printf("Illegal use of \"message list\"\n");
574 1.1 cgd break;
575 1.1 cgd }
576 1.1 cgd if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
577 1.1 cgd break;
578 1.1 cgd if (c == 0) {
579 1.30 christos *msgvec = first(com->c_msgflag, com->c_msgmask);
580 1.10 pk msgvec[1] = 0;
581 1.1 cgd }
582 1.10 pk if (*msgvec == 0) {
583 1.25 christos (void)printf("No applicable messages\n");
584 1.1 cgd break;
585 1.1 cgd }
586 1.1 cgd e = (*com->c_func)(msgvec);
587 1.1 cgd break;
588 1.1 cgd
589 1.1 cgd case NDMLIST:
590 1.1 cgd /*
591 1.1 cgd * A message list with no defaults, but no error
592 1.1 cgd * if none exist.
593 1.1 cgd */
594 1.1 cgd if (msgvec == 0) {
595 1.25 christos (void)printf("Illegal use of \"message list\"\n");
596 1.1 cgd break;
597 1.1 cgd }
598 1.1 cgd if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
599 1.1 cgd break;
600 1.1 cgd e = (*com->c_func)(msgvec);
601 1.1 cgd break;
602 1.1 cgd
603 1.1 cgd case STRLIST:
604 1.1 cgd /*
605 1.1 cgd * Just the straight string, with
606 1.1 cgd * leading blanks removed.
607 1.1 cgd */
608 1.12 christos while (isspace((unsigned char)*cp))
609 1.1 cgd cp++;
610 1.1 cgd e = (*com->c_func)(cp);
611 1.1 cgd break;
612 1.1 cgd
613 1.1 cgd case RAWLIST:
614 1.1 cgd /*
615 1.1 cgd * A vector of strings, in shell style.
616 1.1 cgd */
617 1.1 cgd if ((c = getrawlist(cp, arglist,
618 1.1 cgd sizeof arglist / sizeof *arglist)) < 0)
619 1.1 cgd break;
620 1.1 cgd if (c < com->c_minargs) {
621 1.25 christos (void)printf("%s requires at least %d arg(s)\n",
622 1.1 cgd com->c_name, com->c_minargs);
623 1.1 cgd break;
624 1.1 cgd }
625 1.1 cgd if (c > com->c_maxargs) {
626 1.25 christos (void)printf("%s takes no more than %d arg(s)\n",
627 1.1 cgd com->c_name, com->c_maxargs);
628 1.1 cgd break;
629 1.1 cgd }
630 1.1 cgd e = (*com->c_func)(arglist);
631 1.1 cgd break;
632 1.1 cgd
633 1.1 cgd case NOLIST:
634 1.1 cgd /*
635 1.1 cgd * Just the constant zero, for exiting,
636 1.1 cgd * eg.
637 1.1 cgd */
638 1.1 cgd e = (*com->c_func)(0);
639 1.1 cgd break;
640 1.1 cgd
641 1.1 cgd default:
642 1.11 lukem errx(1, "Unknown argtype");
643 1.1 cgd }
644 1.1 cgd
645 1.1 cgd out:
646 1.30 christos close_piping();
647 1.30 christos
648 1.1 cgd /*
649 1.1 cgd * Exit the current source file on
650 1.1 cgd * error.
651 1.1 cgd */
652 1.1 cgd if (e) {
653 1.1 cgd if (e < 0)
654 1.1 cgd return 1;
655 1.1 cgd if (loading)
656 1.1 cgd return 1;
657 1.1 cgd if (sourcing)
658 1.25 christos (void)unstack();
659 1.1 cgd return 0;
660 1.1 cgd }
661 1.7 christos if (com == NULL)
662 1.30 christos return 0;
663 1.32 christos if (contxt != ec_autoprint && com->c_argtype & P &&
664 1.32 christos value(ENAME_AUTOPRINT) != NULL && (dot->m_flag & MDELETED) == 0)
665 1.32 christos (void)execute(__UNCONST("print ."), ec_autoprint);
666 1.1 cgd if (!sourcing && (com->c_argtype & T) == 0)
667 1.1 cgd sawcom = 1;
668 1.30 christos return 0;
669 1.1 cgd }
670 1.1 cgd
671 1.1 cgd
672 1.1 cgd /*
673 1.1 cgd * The following gets called on receipt of an interrupt. This is
674 1.1 cgd * to abort printout of a command, mainly.
675 1.1 cgd * Dispatching here when command() is inactive crashes rcv.
676 1.1 cgd * Close all open files except 0, 1, 2, and the temporary.
677 1.1 cgd * Also, unstack all source files.
678 1.1 cgd */
679 1.30 christos static int inithdr; /* am printing startup headers */
680 1.1 cgd
681 1.1 cgd /*ARGSUSED*/
682 1.30 christos static void
683 1.28 christos intr(int s __unused)
684 1.1 cgd {
685 1.1 cgd noreset = 0;
686 1.1 cgd if (!inithdr)
687 1.1 cgd sawcom++;
688 1.1 cgd inithdr = 0;
689 1.1 cgd while (sourcing)
690 1.25 christos (void)unstack();
691 1.1 cgd
692 1.30 christos close_piping();
693 1.1 cgd close_all_files();
694 1.1 cgd
695 1.1 cgd if (image >= 0) {
696 1.25 christos (void)close(image);
697 1.1 cgd image = -1;
698 1.1 cgd }
699 1.25 christos (void)fprintf(stderr, "Interrupt\n");
700 1.1 cgd reset(0);
701 1.1 cgd }
702 1.1 cgd
703 1.1 cgd /*
704 1.1 cgd * Branch here on hangup signal and simulate "exit".
705 1.1 cgd */
706 1.1 cgd /*ARGSUSED*/
707 1.30 christos static void
708 1.28 christos hangup(int s __unused)
709 1.1 cgd {
710 1.1 cgd /* nothing to do? */
711 1.1 cgd exit(1);
712 1.1 cgd }
713 1.1 cgd
714 1.1 cgd /*
715 1.30 christos * Interpret user commands one by one. If standard input is not a tty,
716 1.30 christos * print no prompt.
717 1.1 cgd */
718 1.30 christos PUBLIC void
719 1.30 christos commands(void)
720 1.1 cgd {
721 1.30 christos int n;
722 1.30 christos char linebuf[LINESIZE];
723 1.30 christos int eofloop;
724 1.30 christos
725 1.30 christos if (!sourcing) {
726 1.30 christos if (signal(SIGINT, SIG_IGN) != SIG_IGN)
727 1.30 christos (void)signal(SIGINT, intr);
728 1.30 christos if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
729 1.30 christos (void)signal(SIGHUP, hangup);
730 1.30 christos (void)signal(SIGTSTP, stop);
731 1.30 christos (void)signal(SIGTTOU, stop);
732 1.30 christos (void)signal(SIGTTIN, stop);
733 1.30 christos }
734 1.30 christos setexit(); /* defined as (void)setjmp(srbuf) in def.h */
735 1.30 christos eofloop = 0; /* initialize this after a possible longjmp */
736 1.30 christos for (;;) {
737 1.30 christos (void)fflush(stdout);
738 1.30 christos sreset();
739 1.30 christos /*
740 1.30 christos * Print the prompt, if needed. Clear out
741 1.30 christos * string space, and flush the output.
742 1.30 christos */
743 1.30 christos if (!sourcing && value(ENAME_INTERACTIVE) != NULL) {
744 1.30 christos if ((prompt = value(ENAME_PROMPT)) == NULL)
745 1.30 christos prompt = DEFAULT_PROMPT;
746 1.30 christos prompt = smsgprintf(prompt, dot);
747 1.30 christos if ((value(ENAME_AUTOINC) != NULL) && (incfile() > 0))
748 1.30 christos (void)printf("New mail has arrived.\n");
749 1.30 christos reset_on_stop = 1;
750 1.30 christos #ifndef USE_EDITLINE
751 1.30 christos (void)printf("%s", prompt);
752 1.30 christos #endif
753 1.30 christos }
754 1.30 christos /*
755 1.30 christos * Read a line of commands from the current input
756 1.30 christos * and handle end of file specially.
757 1.30 christos */
758 1.30 christos n = 0;
759 1.30 christos for (;;) {
760 1.30 christos #ifdef USE_EDITLINE
761 1.30 christos if (!sourcing) {
762 1.30 christos char *line;
763 1.30 christos if ((line = my_gets(&elm.command, prompt, NULL)) == NULL) {
764 1.30 christos if (n == 0)
765 1.30 christos n = -1;
766 1.30 christos break;
767 1.30 christos }
768 1.30 christos (void)strlcpy(linebuf, line, sizeof(linebuf));
769 1.30 christos setscreensize(); /* so we can resize a window */
770 1.30 christos }
771 1.30 christos else {
772 1.30 christos if (mail_readline(input, &linebuf[n], LINESIZE - n) < 0) {
773 1.30 christos if (n == 0)
774 1.30 christos n = -1;
775 1.30 christos break;
776 1.30 christos }
777 1.30 christos }
778 1.30 christos #else /* USE_EDITLINE */
779 1.30 christos if (mail_readline(input, &linebuf[n], LINESIZE - n) < 0) {
780 1.30 christos if (n == 0)
781 1.30 christos n = -1;
782 1.30 christos break;
783 1.30 christos }
784 1.30 christos #endif /* USE_EDITLINE */
785 1.1 cgd
786 1.30 christos if (sourcing) { /* allow comments in source files */
787 1.30 christos char *ptr;
788 1.30 christos if ((ptr = comment_char(linebuf)) != NULL)
789 1.30 christos *ptr = '\0';
790 1.30 christos }
791 1.30 christos if ((n = strlen(linebuf)) == 0)
792 1.30 christos break;
793 1.30 christos n--;
794 1.30 christos if (linebuf[n] != '\\')
795 1.30 christos break;
796 1.30 christos linebuf[n++] = ' ';
797 1.30 christos }
798 1.30 christos reset_on_stop = 0;
799 1.30 christos if (n < 0) {
800 1.30 christos /* eof */
801 1.30 christos if (loading)
802 1.30 christos break;
803 1.30 christos if (sourcing) {
804 1.30 christos (void)unstack();
805 1.30 christos continue;
806 1.30 christos }
807 1.30 christos #ifdef USE_EDITLINE
808 1.30 christos {
809 1.30 christos char *p;
810 1.30 christos if (value(ENAME_INTERACTIVE) != NULL &&
811 1.30 christos (p = value(ENAME_IGNOREEOF)) != NULL &&
812 1.30 christos ++eofloop < (*p == '\0' ? 25 : atoi(p))) {
813 1.30 christos (void)printf("Use \"quit\" to quit.\n");
814 1.30 christos continue;
815 1.30 christos }
816 1.30 christos }
817 1.30 christos #else
818 1.30 christos if (value(ENAME_INTERACTIVE) != NULL &&
819 1.30 christos value(ENAME_IGNOREEOF) != NULL &&
820 1.30 christos ++eofloop < 25) {
821 1.30 christos (void)printf("Use \"quit\" to quit.\n");
822 1.30 christos continue;
823 1.30 christos }
824 1.30 christos #endif
825 1.30 christos break;
826 1.30 christos }
827 1.30 christos eofloop = 0;
828 1.32 christos if (execute(linebuf, ec_normal))
829 1.30 christos break;
830 1.1 cgd }
831 1.1 cgd }
832 1.1 cgd
833 1.1 cgd /*
834 1.1 cgd * Announce information about the file we are editing.
835 1.1 cgd * Return a likely place to set dot.
836 1.1 cgd */
837 1.30 christos PUBLIC int
838 1.17 wiz newfileinfo(int omsgCount)
839 1.1 cgd {
840 1.11 lukem struct message *mp;
841 1.30 christos int d, n, s, t, u, mdot;
842 1.30 christos char fname[PATHSIZE];
843 1.30 christos char *ename;
844 1.1 cgd
845 1.30 christos /*
846 1.30 christos * Figure out where to set the 'dot'. Use the first new or
847 1.30 christos * unread message.
848 1.30 christos */
849 1.30 christos for (mp = get_abs_message(omsgCount + 1); mp;
850 1.30 christos mp = next_abs_message(mp))
851 1.1 cgd if (mp->m_flag & MNEW)
852 1.1 cgd break;
853 1.30 christos
854 1.30 christos if (mp == NULL)
855 1.30 christos for (mp = get_abs_message(omsgCount + 1); mp;
856 1.30 christos mp = next_abs_message(mp))
857 1.1 cgd if ((mp->m_flag & MREAD) == 0)
858 1.1 cgd break;
859 1.30 christos if (mp != NULL)
860 1.30 christos mdot = get_msgnum(mp);
861 1.1 cgd else
862 1.8 tls mdot = omsgCount + 1;
863 1.30 christos #ifdef THREAD_SUPPORT
864 1.30 christos /*
865 1.30 christos * See if the message is in the current thread.
866 1.30 christos */
867 1.30 christos if (mp != NULL && get_message(1) != NULL && get_message(mdot) != mp)
868 1.30 christos mdot = 0;
869 1.30 christos #endif
870 1.30 christos /*
871 1.30 christos * Scan the message array counting the new, unread, deleted,
872 1.30 christos * and saved messages.
873 1.30 christos */
874 1.30 christos d = n = s = t = u = 0;
875 1.30 christos for (mp = get_abs_message(1); mp; mp = next_abs_message(mp)) {
876 1.1 cgd if (mp->m_flag & MNEW)
877 1.1 cgd n++;
878 1.1 cgd if ((mp->m_flag & MREAD) == 0)
879 1.1 cgd u++;
880 1.1 cgd if (mp->m_flag & MDELETED)
881 1.1 cgd d++;
882 1.1 cgd if (mp->m_flag & MSAVED)
883 1.1 cgd s++;
884 1.30 christos if (mp->m_flag & MTAGGED)
885 1.30 christos t++;
886 1.1 cgd }
887 1.1 cgd ename = mailname;
888 1.30 christos if (getfold(fname, sizeof(fname)) >= 0) {
889 1.30 christos char zname[PATHSIZE];
890 1.30 christos size_t l;
891 1.9 mikel l = strlen(fname);
892 1.30 christos if (l < sizeof(fname) - 1)
893 1.9 mikel fname[l++] = '/';
894 1.9 mikel if (strncmp(fname, mailname, l) == 0) {
895 1.30 christos (void)snprintf(zname, sizeof(zname), "+%s",
896 1.9 mikel mailname + l);
897 1.1 cgd ename = zname;
898 1.1 cgd }
899 1.1 cgd }
900 1.30 christos /*
901 1.30 christos * Display the statistics.
902 1.30 christos */
903 1.25 christos (void)printf("\"%s\": ", ename);
904 1.30 christos {
905 1.30 christos int cnt = get_abs_msgCount();
906 1.30 christos (void)printf("%d message%s", cnt, cnt == 1 ? "" : "s");
907 1.30 christos }
908 1.1 cgd if (n > 0)
909 1.25 christos (void)printf(" %d new", n);
910 1.1 cgd if (u-n > 0)
911 1.25 christos (void)printf(" %d unread", u);
912 1.30 christos if (t > 0)
913 1.30 christos (void)printf(" %d tagged", t);
914 1.1 cgd if (d > 0)
915 1.25 christos (void)printf(" %d deleted", d);
916 1.1 cgd if (s > 0)
917 1.25 christos (void)printf(" %d saved", s);
918 1.1 cgd if (readonly)
919 1.25 christos (void)printf(" [Read only]");
920 1.25 christos (void)printf("\n");
921 1.30 christos
922 1.30 christos return mdot;
923 1.30 christos }
924 1.30 christos
925 1.30 christos /*
926 1.30 christos * Announce the presence of the current Mail version,
927 1.30 christos * give the message count, and print a header listing.
928 1.30 christos */
929 1.30 christos PUBLIC void
930 1.30 christos announce(void)
931 1.30 christos {
932 1.30 christos int vec[2], mdot;
933 1.30 christos
934 1.30 christos mdot = newfileinfo(0);
935 1.30 christos vec[0] = mdot;
936 1.30 christos vec[1] = 0;
937 1.30 christos if ((dot = get_message(mdot)) == NULL)
938 1.30 christos dot = get_abs_message(1); /* make sure we get something! */
939 1.30 christos if (get_abs_msgCount() > 0 && value(ENAME_NOHEADER) == NULL) {
940 1.30 christos inithdr++;
941 1.30 christos (void)headers(vec);
942 1.30 christos inithdr = 0;
943 1.30 christos }
944 1.1 cgd }
945 1.1 cgd
946 1.1 cgd /*
947 1.1 cgd * Print the current version number.
948 1.1 cgd */
949 1.1 cgd
950 1.1 cgd /*ARGSUSED*/
951 1.30 christos PUBLIC int
952 1.28 christos pversion(void *v __unused)
953 1.1 cgd {
954 1.25 christos (void)printf("Version %s\n", version);
955 1.30 christos return 0;
956 1.1 cgd }
957 1.1 cgd
958 1.1 cgd /*
959 1.1 cgd * Load a file of user definitions.
960 1.1 cgd */
961 1.30 christos PUBLIC void
962 1.24 christos load(const char *name)
963 1.1 cgd {
964 1.11 lukem FILE *in, *oldin;
965 1.1 cgd
966 1.1 cgd if ((in = Fopen(name, "r")) == NULL)
967 1.1 cgd return;
968 1.1 cgd oldin = input;
969 1.1 cgd input = in;
970 1.1 cgd loading = 1;
971 1.1 cgd sourcing = 1;
972 1.1 cgd commands();
973 1.1 cgd loading = 0;
974 1.1 cgd sourcing = 0;
975 1.1 cgd input = oldin;
976 1.25 christos (void)Fclose(in);
977 1.1 cgd }
978