lex.c revision 1.30 1 1.28 christos /* $NetBSD: lex.c,v 1.30 2006/11/28 18:45:32 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.28 christos __RCSID("$NetBSD: lex.c,v 1.30 2006/11/28 18:45:32 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.1 cgd if ((i = open(name, 1)) < 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.30 christos else if (value(ENAME_PAGER_OFF) == NULL && c_pipe & C_PIPE_PAGER) {
372 1.30 christos const char *pager;
373 1.30 christos pager = value(ENAME_PAGER);
374 1.30 christos if (pager == NULL || *pager == '\0')
375 1.30 christos pager = _PATH_MORE;
376 1.30 christos
377 1.30 christos if ((fout = Popen(pager, "w")) == NULL) {
378 1.30 christos warn("Popen: %s", pager);
379 1.30 christos return -1;
380 1.30 christos }
381 1.30 christos }
382 1.30 christos
383 1.30 christos if (fout) {
384 1.30 christos (void)signal(SIGPIPE, brokpipe);
385 1.30 christos (void)fflush(stdout);
386 1.30 christos if ((oldfd1 = dup(1)) == -1)
387 1.30 christos err(EXIT_FAILURE, "dup failed");
388 1.30 christos if (dup2(fileno(fout), 1) == -1)
389 1.30 christos err(EXIT_FAILURE, "dup2 failed");
390 1.30 christos fp_stop = last_file;
391 1.30 christos }
392 1.30 christos return 0;
393 1.30 christos }
394 1.30 christos
395 1.30 christos /*
396 1.30 christos * This will close any piping started by setup_piping().
397 1.30 christos */
398 1.30 christos static void
399 1.30 christos close_piping(void)
400 1.30 christos {
401 1.30 christos if (oldfd1 != -1) {
402 1.30 christos (void)fflush(stdout);
403 1.30 christos if (fileno(stdout) != oldfd1 && dup2(oldfd1, 1) == -1)
404 1.30 christos err(EXIT_FAILURE, "dup2 failed");
405 1.30 christos
406 1.30 christos (void)signal(SIGPIPE, SIG_IGN);
407 1.30 christos close_top_files(fp_stop);
408 1.30 christos fp_stop = NULL;
409 1.30 christos (void)close(oldfd1);
410 1.30 christos oldfd1 = -1;
411 1.30 christos (void)signal(SIGPIPE, SIG_DFL);
412 1.1 cgd }
413 1.1 cgd }
414 1.1 cgd
415 1.1 cgd /*
416 1.30 christos * Determine if as1 is a valid prefix of as2.
417 1.30 christos * Return true if yep.
418 1.30 christos */
419 1.30 christos static int
420 1.30 christos isprefix(char *as1, const char *as2)
421 1.30 christos {
422 1.30 christos char *s1;
423 1.30 christos const char *s2;
424 1.30 christos
425 1.30 christos s1 = as1;
426 1.30 christos s2 = as2;
427 1.30 christos while (*s1++ == *s2)
428 1.30 christos if (*s2++ == '\0')
429 1.30 christos return 1;
430 1.30 christos return *--s1 == '\0';
431 1.30 christos }
432 1.30 christos
433 1.30 christos /*
434 1.30 christos * Find the correct command in the command table corresponding
435 1.30 christos * to the passed command "word"
436 1.30 christos */
437 1.30 christos PUBLIC const struct cmd *
438 1.30 christos lex(char word[])
439 1.30 christos {
440 1.30 christos const struct cmd *cp;
441 1.30 christos
442 1.30 christos for (cp = &cmdtab[0]; cp->c_name != NULL; cp++)
443 1.30 christos if (isprefix(word, cp->c_name))
444 1.30 christos return cp;
445 1.30 christos return NULL;
446 1.30 christos }
447 1.30 christos
448 1.30 christos PUBLIC char *
449 1.30 christos get_cmdname(char *buf)
450 1.30 christos {
451 1.30 christos char *cp;
452 1.30 christos char *cmd;
453 1.30 christos size_t len;
454 1.30 christos
455 1.30 christos for (cp = buf; *cp; cp++)
456 1.30 christos if (strchr(" \t0123456789$^.:/-+*'\">|", *cp) != NULL)
457 1.30 christos break;
458 1.30 christos len = cp - buf + 1;
459 1.30 christos cmd = salloc(len);
460 1.30 christos (void)strlcpy(cmd, buf, len);
461 1.30 christos return cmd;
462 1.30 christos }
463 1.30 christos
464 1.30 christos /*
465 1.1 cgd * Execute a single command.
466 1.1 cgd * Command functions return 0 for success, 1 for error, and -1
467 1.1 cgd * for abort. A 1 or -1 aborts a load or source. A -1 aborts
468 1.1 cgd * the interactive command loop.
469 1.1 cgd * Contxt is non-zero if called while composing mail.
470 1.1 cgd */
471 1.30 christos PUBLIC int
472 1.17 wiz execute(char linebuf[], int contxt)
473 1.1 cgd {
474 1.30 christos char *word;
475 1.1 cgd char *arglist[MAXARGC];
476 1.7 christos const struct cmd *com = NULL;
477 1.30 christos char *volatile cp;
478 1.11 lukem int c;
479 1.1 cgd int muvec[2];
480 1.1 cgd int e = 1;
481 1.1 cgd
482 1.1 cgd /*
483 1.1 cgd * Strip the white space away from the beginning
484 1.1 cgd * of the command, then scan out a word, which
485 1.1 cgd * consists of anything except digits and white space.
486 1.1 cgd *
487 1.1 cgd * Handle ! escapes differently to get the correct
488 1.1 cgd * lexical conventions.
489 1.1 cgd */
490 1.1 cgd
491 1.30 christos cp = skip_blank(linebuf);
492 1.1 cgd if (*cp == '!') {
493 1.1 cgd if (sourcing) {
494 1.25 christos (void)printf("Can't \"!\" while sourcing\n");
495 1.1 cgd goto out;
496 1.1 cgd }
497 1.28 christos (void)shell(cp + 1);
498 1.30 christos return 0;
499 1.1 cgd }
500 1.30 christos
501 1.30 christos word = get_cmdname(cp);
502 1.30 christos cp += strlen(word);
503 1.1 cgd
504 1.1 cgd /*
505 1.1 cgd * Look up the command; if not found, bitch.
506 1.1 cgd * Normally, a blank command would map to the
507 1.1 cgd * first command in the table; while sourcing,
508 1.1 cgd * however, we ignore blank lines to eliminate
509 1.1 cgd * confusion.
510 1.1 cgd */
511 1.1 cgd
512 1.1 cgd if (sourcing && *word == '\0')
513 1.30 christos return 0;
514 1.1 cgd com = lex(word);
515 1.19 wiz if (com == NULL) {
516 1.25 christos (void)printf("Unknown command: \"%s\"\n", word);
517 1.1 cgd goto out;
518 1.1 cgd }
519 1.1 cgd
520 1.1 cgd /*
521 1.1 cgd * See if we should execute the command -- if a conditional
522 1.1 cgd * we always execute it, otherwise, check the state of cond.
523 1.1 cgd */
524 1.1 cgd
525 1.30 christos if ((com->c_argtype & F) == 0 && (cond & CSKIP))
526 1.30 christos return 0;
527 1.1 cgd
528 1.1 cgd /*
529 1.1 cgd * Process the arguments to the command, depending
530 1.1 cgd * on the type he expects. Default to an error.
531 1.1 cgd * If we are sourcing an interactive command, it's
532 1.1 cgd * an error.
533 1.1 cgd */
534 1.1 cgd
535 1.30 christos if (mailmode == mm_sending && (com->c_argtype & M) == 0) {
536 1.25 christos (void)printf("May not execute \"%s\" while sending\n",
537 1.1 cgd com->c_name);
538 1.1 cgd goto out;
539 1.1 cgd }
540 1.1 cgd if (sourcing && com->c_argtype & I) {
541 1.25 christos (void)printf("May not execute \"%s\" while sourcing\n",
542 1.1 cgd com->c_name);
543 1.1 cgd goto out;
544 1.1 cgd }
545 1.1 cgd if (readonly && com->c_argtype & W) {
546 1.25 christos (void)printf("May not execute \"%s\" -- message file is read only\n",
547 1.1 cgd com->c_name);
548 1.1 cgd goto out;
549 1.1 cgd }
550 1.1 cgd if (contxt && com->c_argtype & R) {
551 1.25 christos (void)printf("Cannot recursively invoke \"%s\"\n", com->c_name);
552 1.1 cgd goto out;
553 1.1 cgd }
554 1.30 christos
555 1.30 christos if (!sourcing && com->c_pipe && value(ENAME_INTERACTIVE) != NULL) {
556 1.30 christos if (setjmp(pipestop))
557 1.30 christos goto out;
558 1.30 christos
559 1.30 christos if (setup_piping(cp, com->c_pipe) == -1)
560 1.30 christos goto out;
561 1.30 christos }
562 1.30 christos switch (com->c_argtype & ARGTYPE_MASK) {
563 1.1 cgd case MSGLIST:
564 1.1 cgd /*
565 1.1 cgd * A message list defaulting to nearest forward
566 1.1 cgd * legal message.
567 1.1 cgd */
568 1.1 cgd if (msgvec == 0) {
569 1.25 christos (void)printf("Illegal use of \"message list\"\n");
570 1.1 cgd break;
571 1.1 cgd }
572 1.1 cgd if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
573 1.1 cgd break;
574 1.1 cgd if (c == 0) {
575 1.30 christos *msgvec = first(com->c_msgflag, com->c_msgmask);
576 1.10 pk msgvec[1] = 0;
577 1.1 cgd }
578 1.10 pk if (*msgvec == 0) {
579 1.25 christos (void)printf("No applicable messages\n");
580 1.1 cgd break;
581 1.1 cgd }
582 1.1 cgd e = (*com->c_func)(msgvec);
583 1.1 cgd break;
584 1.1 cgd
585 1.1 cgd case NDMLIST:
586 1.1 cgd /*
587 1.1 cgd * A message list with no defaults, but no error
588 1.1 cgd * if none exist.
589 1.1 cgd */
590 1.1 cgd if (msgvec == 0) {
591 1.25 christos (void)printf("Illegal use of \"message list\"\n");
592 1.1 cgd break;
593 1.1 cgd }
594 1.1 cgd if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
595 1.1 cgd break;
596 1.1 cgd e = (*com->c_func)(msgvec);
597 1.1 cgd break;
598 1.1 cgd
599 1.1 cgd case STRLIST:
600 1.1 cgd /*
601 1.1 cgd * Just the straight string, with
602 1.1 cgd * leading blanks removed.
603 1.1 cgd */
604 1.12 christos while (isspace((unsigned char)*cp))
605 1.1 cgd cp++;
606 1.1 cgd e = (*com->c_func)(cp);
607 1.1 cgd break;
608 1.1 cgd
609 1.1 cgd case RAWLIST:
610 1.1 cgd /*
611 1.1 cgd * A vector of strings, in shell style.
612 1.1 cgd */
613 1.1 cgd if ((c = getrawlist(cp, arglist,
614 1.1 cgd sizeof arglist / sizeof *arglist)) < 0)
615 1.1 cgd break;
616 1.1 cgd if (c < com->c_minargs) {
617 1.25 christos (void)printf("%s requires at least %d arg(s)\n",
618 1.1 cgd com->c_name, com->c_minargs);
619 1.1 cgd break;
620 1.1 cgd }
621 1.1 cgd if (c > com->c_maxargs) {
622 1.25 christos (void)printf("%s takes no more than %d arg(s)\n",
623 1.1 cgd com->c_name, com->c_maxargs);
624 1.1 cgd break;
625 1.1 cgd }
626 1.1 cgd e = (*com->c_func)(arglist);
627 1.1 cgd break;
628 1.1 cgd
629 1.1 cgd case NOLIST:
630 1.1 cgd /*
631 1.1 cgd * Just the constant zero, for exiting,
632 1.1 cgd * eg.
633 1.1 cgd */
634 1.1 cgd e = (*com->c_func)(0);
635 1.1 cgd break;
636 1.1 cgd
637 1.1 cgd default:
638 1.11 lukem errx(1, "Unknown argtype");
639 1.1 cgd }
640 1.1 cgd
641 1.1 cgd out:
642 1.30 christos close_piping();
643 1.30 christos
644 1.1 cgd /*
645 1.1 cgd * Exit the current source file on
646 1.1 cgd * error.
647 1.1 cgd */
648 1.1 cgd if (e) {
649 1.1 cgd if (e < 0)
650 1.1 cgd return 1;
651 1.1 cgd if (loading)
652 1.1 cgd return 1;
653 1.1 cgd if (sourcing)
654 1.25 christos (void)unstack();
655 1.1 cgd return 0;
656 1.1 cgd }
657 1.7 christos if (com == NULL)
658 1.30 christos return 0;
659 1.30 christos if (value(ENAME_AUTOPRINT) != NULL && com->c_argtype & P)
660 1.1 cgd if ((dot->m_flag & MDELETED) == 0) {
661 1.30 christos muvec[0] = get_msgnum(dot);
662 1.1 cgd muvec[1] = 0;
663 1.25 christos (void)type(muvec);
664 1.1 cgd }
665 1.1 cgd if (!sourcing && (com->c_argtype & T) == 0)
666 1.1 cgd sawcom = 1;
667 1.30 christos return 0;
668 1.1 cgd }
669 1.1 cgd
670 1.1 cgd
671 1.1 cgd /*
672 1.1 cgd * The following gets called on receipt of an interrupt. This is
673 1.1 cgd * to abort printout of a command, mainly.
674 1.1 cgd * Dispatching here when command() is inactive crashes rcv.
675 1.1 cgd * Close all open files except 0, 1, 2, and the temporary.
676 1.1 cgd * Also, unstack all source files.
677 1.1 cgd */
678 1.30 christos static int inithdr; /* am printing startup headers */
679 1.1 cgd
680 1.1 cgd /*ARGSUSED*/
681 1.30 christos static void
682 1.28 christos intr(int s __unused)
683 1.1 cgd {
684 1.1 cgd noreset = 0;
685 1.1 cgd if (!inithdr)
686 1.1 cgd sawcom++;
687 1.1 cgd inithdr = 0;
688 1.1 cgd while (sourcing)
689 1.25 christos (void)unstack();
690 1.1 cgd
691 1.30 christos close_piping();
692 1.1 cgd close_all_files();
693 1.1 cgd
694 1.1 cgd if (image >= 0) {
695 1.25 christos (void)close(image);
696 1.1 cgd image = -1;
697 1.1 cgd }
698 1.25 christos (void)fprintf(stderr, "Interrupt\n");
699 1.1 cgd reset(0);
700 1.1 cgd }
701 1.1 cgd
702 1.1 cgd /*
703 1.1 cgd * Branch here on hangup signal and simulate "exit".
704 1.1 cgd */
705 1.1 cgd /*ARGSUSED*/
706 1.30 christos static void
707 1.28 christos hangup(int s __unused)
708 1.1 cgd {
709 1.1 cgd /* nothing to do? */
710 1.1 cgd exit(1);
711 1.1 cgd }
712 1.1 cgd
713 1.1 cgd /*
714 1.30 christos * Interpret user commands one by one. If standard input is not a tty,
715 1.30 christos * print no prompt.
716 1.1 cgd */
717 1.30 christos PUBLIC void
718 1.30 christos commands(void)
719 1.1 cgd {
720 1.30 christos int n;
721 1.30 christos char linebuf[LINESIZE];
722 1.30 christos int eofloop;
723 1.30 christos
724 1.30 christos if (!sourcing) {
725 1.30 christos if (signal(SIGINT, SIG_IGN) != SIG_IGN)
726 1.30 christos (void)signal(SIGINT, intr);
727 1.30 christos if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
728 1.30 christos (void)signal(SIGHUP, hangup);
729 1.30 christos (void)signal(SIGTSTP, stop);
730 1.30 christos (void)signal(SIGTTOU, stop);
731 1.30 christos (void)signal(SIGTTIN, stop);
732 1.30 christos }
733 1.30 christos setexit(); /* defined as (void)setjmp(srbuf) in def.h */
734 1.30 christos eofloop = 0; /* initialize this after a possible longjmp */
735 1.30 christos for (;;) {
736 1.30 christos (void)fflush(stdout);
737 1.30 christos sreset();
738 1.30 christos /*
739 1.30 christos * Print the prompt, if needed. Clear out
740 1.30 christos * string space, and flush the output.
741 1.30 christos */
742 1.30 christos if (!sourcing && value(ENAME_INTERACTIVE) != NULL) {
743 1.30 christos if ((prompt = value(ENAME_PROMPT)) == NULL)
744 1.30 christos prompt = DEFAULT_PROMPT;
745 1.30 christos prompt = smsgprintf(prompt, dot);
746 1.30 christos if ((value(ENAME_AUTOINC) != NULL) && (incfile() > 0))
747 1.30 christos (void)printf("New mail has arrived.\n");
748 1.30 christos reset_on_stop = 1;
749 1.30 christos #ifndef USE_EDITLINE
750 1.30 christos (void)printf("%s", prompt);
751 1.30 christos #endif
752 1.30 christos }
753 1.30 christos /*
754 1.30 christos * Read a line of commands from the current input
755 1.30 christos * and handle end of file specially.
756 1.30 christos */
757 1.30 christos n = 0;
758 1.30 christos for (;;) {
759 1.30 christos #ifdef USE_EDITLINE
760 1.30 christos if (!sourcing) {
761 1.30 christos char *line;
762 1.30 christos if ((line = my_gets(&elm.command, prompt, NULL)) == NULL) {
763 1.30 christos if (n == 0)
764 1.30 christos n = -1;
765 1.30 christos break;
766 1.30 christos }
767 1.30 christos (void)strlcpy(linebuf, line, sizeof(linebuf));
768 1.30 christos setscreensize(); /* so we can resize a window */
769 1.30 christos }
770 1.30 christos else {
771 1.30 christos if (mail_readline(input, &linebuf[n], LINESIZE - n) < 0) {
772 1.30 christos if (n == 0)
773 1.30 christos n = -1;
774 1.30 christos break;
775 1.30 christos }
776 1.30 christos }
777 1.30 christos #else /* USE_EDITLINE */
778 1.30 christos if (mail_readline(input, &linebuf[n], LINESIZE - n) < 0) {
779 1.30 christos if (n == 0)
780 1.30 christos n = -1;
781 1.30 christos break;
782 1.30 christos }
783 1.30 christos #endif /* USE_EDITLINE */
784 1.1 cgd
785 1.30 christos if (sourcing) { /* allow comments in source files */
786 1.30 christos char *ptr;
787 1.30 christos if ((ptr = comment_char(linebuf)) != NULL)
788 1.30 christos *ptr = '\0';
789 1.30 christos }
790 1.30 christos if ((n = strlen(linebuf)) == 0)
791 1.30 christos break;
792 1.30 christos n--;
793 1.30 christos if (linebuf[n] != '\\')
794 1.30 christos break;
795 1.30 christos linebuf[n++] = ' ';
796 1.30 christos }
797 1.30 christos reset_on_stop = 0;
798 1.30 christos if (n < 0) {
799 1.30 christos /* eof */
800 1.30 christos if (loading)
801 1.30 christos break;
802 1.30 christos if (sourcing) {
803 1.30 christos (void)unstack();
804 1.30 christos continue;
805 1.30 christos }
806 1.30 christos #ifdef USE_EDITLINE
807 1.30 christos {
808 1.30 christos char *p;
809 1.30 christos if (value(ENAME_INTERACTIVE) != NULL &&
810 1.30 christos (p = value(ENAME_IGNOREEOF)) != NULL &&
811 1.30 christos ++eofloop < (*p == '\0' ? 25 : atoi(p))) {
812 1.30 christos (void)printf("Use \"quit\" to quit.\n");
813 1.30 christos continue;
814 1.30 christos }
815 1.30 christos }
816 1.30 christos #else
817 1.30 christos if (value(ENAME_INTERACTIVE) != NULL &&
818 1.30 christos value(ENAME_IGNOREEOF) != NULL &&
819 1.30 christos ++eofloop < 25) {
820 1.30 christos (void)printf("Use \"quit\" to quit.\n");
821 1.30 christos continue;
822 1.30 christos }
823 1.30 christos #endif
824 1.30 christos break;
825 1.30 christos }
826 1.30 christos eofloop = 0;
827 1.30 christos if (execute(linebuf, 0))
828 1.30 christos break;
829 1.1 cgd }
830 1.1 cgd }
831 1.1 cgd
832 1.1 cgd /*
833 1.1 cgd * Announce information about the file we are editing.
834 1.1 cgd * Return a likely place to set dot.
835 1.1 cgd */
836 1.30 christos PUBLIC int
837 1.17 wiz newfileinfo(int omsgCount)
838 1.1 cgd {
839 1.11 lukem struct message *mp;
840 1.30 christos int d, n, s, t, u, mdot;
841 1.30 christos char fname[PATHSIZE];
842 1.30 christos char *ename;
843 1.1 cgd
844 1.30 christos /*
845 1.30 christos * Figure out where to set the 'dot'. Use the first new or
846 1.30 christos * unread message.
847 1.30 christos */
848 1.30 christos for (mp = get_abs_message(omsgCount + 1); mp;
849 1.30 christos mp = next_abs_message(mp))
850 1.1 cgd if (mp->m_flag & MNEW)
851 1.1 cgd break;
852 1.30 christos
853 1.30 christos if (mp == NULL)
854 1.30 christos for (mp = get_abs_message(omsgCount + 1); mp;
855 1.30 christos mp = next_abs_message(mp))
856 1.1 cgd if ((mp->m_flag & MREAD) == 0)
857 1.1 cgd break;
858 1.30 christos if (mp != NULL)
859 1.30 christos mdot = get_msgnum(mp);
860 1.1 cgd else
861 1.8 tls mdot = omsgCount + 1;
862 1.30 christos #ifdef THREAD_SUPPORT
863 1.30 christos /*
864 1.30 christos * See if the message is in the current thread.
865 1.30 christos */
866 1.30 christos if (mp != NULL && get_message(1) != NULL && get_message(mdot) != mp)
867 1.30 christos mdot = 0;
868 1.30 christos #endif
869 1.30 christos /*
870 1.30 christos * Scan the message array counting the new, unread, deleted,
871 1.30 christos * and saved messages.
872 1.30 christos */
873 1.30 christos d = n = s = t = u = 0;
874 1.30 christos for (mp = get_abs_message(1); mp; mp = next_abs_message(mp)) {
875 1.1 cgd if (mp->m_flag & MNEW)
876 1.1 cgd n++;
877 1.1 cgd if ((mp->m_flag & MREAD) == 0)
878 1.1 cgd u++;
879 1.1 cgd if (mp->m_flag & MDELETED)
880 1.1 cgd d++;
881 1.1 cgd if (mp->m_flag & MSAVED)
882 1.1 cgd s++;
883 1.30 christos if (mp->m_flag & MTAGGED)
884 1.30 christos t++;
885 1.1 cgd }
886 1.1 cgd ename = mailname;
887 1.30 christos if (getfold(fname, sizeof(fname)) >= 0) {
888 1.30 christos char zname[PATHSIZE];
889 1.30 christos size_t l;
890 1.9 mikel l = strlen(fname);
891 1.30 christos if (l < sizeof(fname) - 1)
892 1.9 mikel fname[l++] = '/';
893 1.9 mikel if (strncmp(fname, mailname, l) == 0) {
894 1.30 christos (void)snprintf(zname, sizeof(zname), "+%s",
895 1.9 mikel mailname + l);
896 1.1 cgd ename = zname;
897 1.1 cgd }
898 1.1 cgd }
899 1.30 christos /*
900 1.30 christos * Display the statistics.
901 1.30 christos */
902 1.25 christos (void)printf("\"%s\": ", ename);
903 1.30 christos {
904 1.30 christos int cnt = get_abs_msgCount();
905 1.30 christos (void)printf("%d message%s", cnt, cnt == 1 ? "" : "s");
906 1.30 christos }
907 1.1 cgd if (n > 0)
908 1.25 christos (void)printf(" %d new", n);
909 1.1 cgd if (u-n > 0)
910 1.25 christos (void)printf(" %d unread", u);
911 1.30 christos if (t > 0)
912 1.30 christos (void)printf(" %d tagged", t);
913 1.1 cgd if (d > 0)
914 1.25 christos (void)printf(" %d deleted", d);
915 1.1 cgd if (s > 0)
916 1.25 christos (void)printf(" %d saved", s);
917 1.1 cgd if (readonly)
918 1.25 christos (void)printf(" [Read only]");
919 1.25 christos (void)printf("\n");
920 1.30 christos
921 1.30 christos return mdot;
922 1.30 christos }
923 1.30 christos
924 1.30 christos /*
925 1.30 christos * Announce the presence of the current Mail version,
926 1.30 christos * give the message count, and print a header listing.
927 1.30 christos */
928 1.30 christos PUBLIC void
929 1.30 christos announce(void)
930 1.30 christos {
931 1.30 christos int vec[2], mdot;
932 1.30 christos
933 1.30 christos mdot = newfileinfo(0);
934 1.30 christos vec[0] = mdot;
935 1.30 christos vec[1] = 0;
936 1.30 christos if ((dot = get_message(mdot)) == NULL)
937 1.30 christos dot = get_abs_message(1); /* make sure we get something! */
938 1.30 christos if (get_abs_msgCount() > 0 && value(ENAME_NOHEADER) == NULL) {
939 1.30 christos inithdr++;
940 1.30 christos (void)headers(vec);
941 1.30 christos inithdr = 0;
942 1.30 christos }
943 1.1 cgd }
944 1.1 cgd
945 1.1 cgd /*
946 1.1 cgd * Print the current version number.
947 1.1 cgd */
948 1.1 cgd
949 1.1 cgd /*ARGSUSED*/
950 1.30 christos PUBLIC int
951 1.28 christos pversion(void *v __unused)
952 1.1 cgd {
953 1.25 christos (void)printf("Version %s\n", version);
954 1.30 christos return 0;
955 1.1 cgd }
956 1.1 cgd
957 1.1 cgd /*
958 1.1 cgd * Load a file of user definitions.
959 1.1 cgd */
960 1.30 christos PUBLIC void
961 1.24 christos load(const char *name)
962 1.1 cgd {
963 1.11 lukem FILE *in, *oldin;
964 1.1 cgd
965 1.1 cgd if ((in = Fopen(name, "r")) == NULL)
966 1.1 cgd return;
967 1.1 cgd oldin = input;
968 1.1 cgd input = in;
969 1.1 cgd loading = 1;
970 1.1 cgd sourcing = 1;
971 1.1 cgd commands();
972 1.1 cgd loading = 0;
973 1.1 cgd sourcing = 0;
974 1.1 cgd input = oldin;
975 1.25 christos (void)Fclose(in);
976 1.1 cgd }
977