lex.c revision 1.33.4.2 1 1.33.4.2 matt /* $NetBSD: lex.c,v 1.33.4.2 2008/01/09 02:00:46 matt 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.4.2 matt __RCSID("$NetBSD: lex.c,v 1.33.4.2 2008/01/09 02:00:46 matt 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.33.4.1 matt 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.33.4.2 matt int rval;
196 1.8 tls
197 1.30 christos omsgCount = get_abs_msgCount();
198 1.30 christos
199 1.8 tls ibuf = Fopen(mailname, "r");
200 1.8 tls if (ibuf == NULL)
201 1.8 tls return -1;
202 1.8 tls holdsigs();
203 1.8 tls newsize = fsize(ibuf);
204 1.33.4.2 matt if (newsize == 0 || /* mail box is now empty??? */
205 1.33.4.2 matt newsize < mailsize) { /* mail box has shrunk??? */
206 1.33.4.2 matt rval = -1;
207 1.33.4.2 matt goto done;
208 1.33.4.2 matt }
209 1.33.4.2 matt if (newsize == mailsize) {
210 1.33.4.2 matt rval = 0; /* no new mail */
211 1.33.4.2 matt goto done;
212 1.33.4.2 matt }
213 1.30 christos setptr(ibuf, mailsize); /* read in new mail */
214 1.30 christos setmsize(get_abs_msgCount()); /* get the new message count */
215 1.8 tls mailsize = ftell(ibuf);
216 1.33.4.2 matt rval = get_abs_msgCount() - omsgCount;
217 1.33.4.2 matt done:
218 1.25 christos (void)Fclose(ibuf);
219 1.8 tls relsesigs();
220 1.33.4.2 matt return rval;
221 1.8 tls }
222 1.8 tls
223 1.29 christos /*
224 1.29 christos * Return a pointer to the comment character, respecting quoting as
225 1.29 christos * done in getrawlist(). The comment character is ignored inside
226 1.29 christos * quotes.
227 1.29 christos */
228 1.29 christos static char *
229 1.29 christos comment_char(char *line)
230 1.29 christos {
231 1.29 christos char *p;
232 1.29 christos char quotec;
233 1.29 christos quotec = '\0';
234 1.29 christos for (p = line; *p; p++) {
235 1.29 christos if (quotec != '\0') {
236 1.29 christos if (*p == quotec)
237 1.29 christos quotec = '\0';
238 1.29 christos }
239 1.29 christos else if (*p == '"' || *p == '\'')
240 1.29 christos quotec = *p;
241 1.29 christos else if (*p == COMMENT_CHAR)
242 1.29 christos return p;
243 1.29 christos }
244 1.29 christos return NULL;
245 1.29 christos }
246 1.29 christos
247 1.30 christos /*
248 1.30 christos * When we wake up after ^Z, reprint the prompt.
249 1.30 christos */
250 1.30 christos static void
251 1.30 christos stop(int s)
252 1.30 christos {
253 1.30 christos sig_t old_action = signal(s, SIG_DFL);
254 1.30 christos sigset_t nset;
255 1.30 christos
256 1.30 christos (void)sigemptyset(&nset);
257 1.30 christos (void)sigaddset(&nset, s);
258 1.30 christos (void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
259 1.30 christos (void)kill(0, s);
260 1.30 christos (void)sigprocmask(SIG_BLOCK, &nset, NULL);
261 1.30 christos (void)signal(s, old_action);
262 1.30 christos if (reset_on_stop) {
263 1.30 christos reset_on_stop = 0;
264 1.30 christos reset(0);
265 1.30 christos }
266 1.30 christos }
267 1.30 christos
268 1.29 christos
269 1.1 cgd
270 1.1 cgd /*
271 1.30 christos * Signal handler is hooked by setup_piping().
272 1.30 christos * Respond to a broken pipe signal --
273 1.30 christos * probably caused by quitting more.
274 1.1 cgd */
275 1.30 christos static jmp_buf pipestop;
276 1.30 christos
277 1.30 christos /*ARGSUSED*/
278 1.30 christos static void
279 1.30 christos brokpipe(int signo __unused)
280 1.1 cgd {
281 1.30 christos longjmp(pipestop, 1);
282 1.30 christos }
283 1.1 cgd
284 1.30 christos /*
285 1.30 christos * Check the command line for any requested piping or redirection,
286 1.30 christos * depending on the value of 'c'. If "enable-pipes" is set, search
287 1.30 christos * the command line (cp) for the first occurrence of the character 'c'
288 1.30 christos * that is not in a quote or (parenthese) group.
289 1.30 christos */
290 1.30 christos PUBLIC char *
291 1.30 christos shellpr(char *cp)
292 1.30 christos {
293 1.30 christos int quotec;
294 1.30 christos int level;
295 1.30 christos
296 1.30 christos if (cp == NULL || value(ENAME_ENABLE_PIPES) == NULL)
297 1.30 christos return NULL;
298 1.30 christos
299 1.30 christos level = 0;
300 1.30 christos quotec = 0;
301 1.30 christos for (/*EMPTY*/; *cp != '\0'; cp++) {
302 1.30 christos if (quotec) {
303 1.30 christos if (*cp == quotec)
304 1.30 christos quotec = 0;
305 1.30 christos if (*cp == '\\' &&
306 1.30 christos (cp[1] == quotec || cp[1] == '\\'))
307 1.30 christos cp++;
308 1.30 christos }
309 1.30 christos else {
310 1.30 christos switch (*cp) {
311 1.30 christos case '|':
312 1.30 christos case '>':
313 1.30 christos if (level == 0)
314 1.30 christos return cp;
315 1.30 christos break;
316 1.30 christos case '(':
317 1.30 christos level++;
318 1.1 cgd break;
319 1.30 christos case ')':
320 1.30 christos level--;
321 1.1 cgd break;
322 1.30 christos case '"':
323 1.30 christos case '\'':
324 1.30 christos quotec = *cp;
325 1.1 cgd break;
326 1.30 christos default:
327 1.1 cgd break;
328 1.1 cgd }
329 1.30 christos }
330 1.30 christos }
331 1.30 christos return NULL;
332 1.30 christos }
333 1.30 christos
334 1.30 christos /*
335 1.30 christos * Setup any pipe or redirection that the command line indicates.
336 1.30 christos * If none, then setup the pager unless "pager-off" is defined.
337 1.30 christos */
338 1.30 christos static FILE *fp_stop = NULL;
339 1.30 christos static int oldfd1 = -1;
340 1.30 christos static int
341 1.30 christos setup_piping(char *cmdline, int c_pipe)
342 1.30 christos {
343 1.30 christos FILE *fout;
344 1.30 christos FILE *last_file;
345 1.30 christos char *cp;
346 1.30 christos
347 1.30 christos last_file = last_registered_file(0);
348 1.30 christos
349 1.30 christos fout = NULL;
350 1.30 christos if ((cp = shellpr(cmdline)) != NULL) {
351 1.30 christos char c;
352 1.30 christos c = *cp;
353 1.30 christos *cp = '\0';
354 1.30 christos cp++;
355 1.30 christos
356 1.30 christos if (c == '|') {
357 1.30 christos if ((fout = Popen(cp, "w")) == NULL) {
358 1.30 christos warn("Popen: %s", cp);
359 1.30 christos return -1;
360 1.26 christos }
361 1.30 christos }
362 1.30 christos else {
363 1.30 christos const char *mode;
364 1.30 christos assert(c == '>');
365 1.30 christos mode = *cp == '>' ? "a" : "w";
366 1.30 christos if (*cp == '>')
367 1.30 christos cp++;
368 1.30 christos
369 1.33.4.1 matt cp = skip_WSP(cp);
370 1.30 christos if ((fout = Fopen(cp, mode)) == NULL) {
371 1.30 christos warn("Fopen: %s", cp);
372 1.30 christos return -1;
373 1.1 cgd }
374 1.1 cgd }
375 1.30 christos
376 1.30 christos }
377 1.31 christos else if (value(ENAME_PAGER_OFF) == NULL && (c_pipe & C_PIPE_PAGER ||
378 1.31 christos (c_pipe & C_PIPE_CRT && value(ENAME_CRT) != NULL))) {
379 1.30 christos const char *pager;
380 1.30 christos pager = value(ENAME_PAGER);
381 1.30 christos if (pager == NULL || *pager == '\0')
382 1.30 christos pager = _PATH_MORE;
383 1.30 christos
384 1.30 christos if ((fout = Popen(pager, "w")) == NULL) {
385 1.30 christos warn("Popen: %s", pager);
386 1.30 christos return -1;
387 1.30 christos }
388 1.30 christos }
389 1.30 christos
390 1.30 christos if (fout) {
391 1.30 christos (void)signal(SIGPIPE, brokpipe);
392 1.30 christos (void)fflush(stdout);
393 1.33 christos if ((oldfd1 = dup(STDOUT_FILENO)) == -1)
394 1.30 christos err(EXIT_FAILURE, "dup failed");
395 1.33 christos if (dup2(fileno(fout), STDOUT_FILENO) == -1)
396 1.30 christos err(EXIT_FAILURE, "dup2 failed");
397 1.30 christos fp_stop = last_file;
398 1.30 christos }
399 1.30 christos return 0;
400 1.30 christos }
401 1.30 christos
402 1.30 christos /*
403 1.30 christos * This will close any piping started by setup_piping().
404 1.30 christos */
405 1.30 christos static void
406 1.30 christos close_piping(void)
407 1.30 christos {
408 1.30 christos if (oldfd1 != -1) {
409 1.30 christos (void)fflush(stdout);
410 1.33 christos if (fileno(stdout) != oldfd1 &&
411 1.33 christos dup2(oldfd1, STDOUT_FILENO) == -1)
412 1.30 christos err(EXIT_FAILURE, "dup2 failed");
413 1.30 christos
414 1.30 christos (void)signal(SIGPIPE, SIG_IGN);
415 1.30 christos close_top_files(fp_stop);
416 1.30 christos fp_stop = NULL;
417 1.30 christos (void)close(oldfd1);
418 1.30 christos oldfd1 = -1;
419 1.30 christos (void)signal(SIGPIPE, SIG_DFL);
420 1.1 cgd }
421 1.1 cgd }
422 1.1 cgd
423 1.1 cgd /*
424 1.30 christos * Determine if as1 is a valid prefix of as2.
425 1.30 christos * Return true if yep.
426 1.30 christos */
427 1.30 christos static int
428 1.30 christos isprefix(char *as1, const char *as2)
429 1.30 christos {
430 1.30 christos char *s1;
431 1.30 christos const char *s2;
432 1.30 christos
433 1.30 christos s1 = as1;
434 1.30 christos s2 = as2;
435 1.30 christos while (*s1++ == *s2)
436 1.30 christos if (*s2++ == '\0')
437 1.30 christos return 1;
438 1.30 christos return *--s1 == '\0';
439 1.30 christos }
440 1.30 christos
441 1.30 christos /*
442 1.30 christos * Find the correct command in the command table corresponding
443 1.30 christos * to the passed command "word"
444 1.30 christos */
445 1.30 christos PUBLIC const struct cmd *
446 1.30 christos lex(char word[])
447 1.30 christos {
448 1.30 christos const struct cmd *cp;
449 1.30 christos
450 1.30 christos for (cp = &cmdtab[0]; cp->c_name != NULL; cp++)
451 1.30 christos if (isprefix(word, cp->c_name))
452 1.30 christos return cp;
453 1.30 christos return NULL;
454 1.30 christos }
455 1.30 christos
456 1.30 christos PUBLIC char *
457 1.30 christos get_cmdname(char *buf)
458 1.30 christos {
459 1.30 christos char *cp;
460 1.30 christos char *cmd;
461 1.30 christos size_t len;
462 1.30 christos
463 1.30 christos for (cp = buf; *cp; cp++)
464 1.30 christos if (strchr(" \t0123456789$^.:/-+*'\">|", *cp) != NULL)
465 1.30 christos break;
466 1.33 christos /* XXX - Don't miss the pipe command! */
467 1.33 christos if (cp == buf && *cp == '|')
468 1.33 christos cp++;
469 1.30 christos len = cp - buf + 1;
470 1.30 christos cmd = salloc(len);
471 1.30 christos (void)strlcpy(cmd, buf, len);
472 1.30 christos return cmd;
473 1.30 christos }
474 1.30 christos
475 1.30 christos /*
476 1.1 cgd * Execute a single command.
477 1.1 cgd * Command functions return 0 for success, 1 for error, and -1
478 1.1 cgd * for abort. A 1 or -1 aborts a load or source. A -1 aborts
479 1.1 cgd * the interactive command loop.
480 1.32 christos * execute_contxt_e is in extern.h.
481 1.1 cgd */
482 1.30 christos PUBLIC int
483 1.32 christos execute(char linebuf[], enum execute_contxt_e contxt)
484 1.1 cgd {
485 1.30 christos char *word;
486 1.1 cgd char *arglist[MAXARGC];
487 1.7 christos const struct cmd *com = NULL;
488 1.30 christos char *volatile cp;
489 1.11 lukem int c;
490 1.1 cgd int e = 1;
491 1.1 cgd
492 1.1 cgd /*
493 1.1 cgd * Strip the white space away from the beginning
494 1.1 cgd * of the command, then scan out a word, which
495 1.1 cgd * consists of anything except digits and white space.
496 1.1 cgd *
497 1.1 cgd * Handle ! escapes differently to get the correct
498 1.1 cgd * lexical conventions.
499 1.1 cgd */
500 1.1 cgd
501 1.33.4.1 matt cp = skip_space(linebuf);
502 1.1 cgd if (*cp == '!') {
503 1.1 cgd if (sourcing) {
504 1.25 christos (void)printf("Can't \"!\" while sourcing\n");
505 1.1 cgd goto out;
506 1.1 cgd }
507 1.28 christos (void)shell(cp + 1);
508 1.30 christos return 0;
509 1.1 cgd }
510 1.30 christos
511 1.30 christos word = get_cmdname(cp);
512 1.30 christos cp += strlen(word);
513 1.1 cgd
514 1.1 cgd /*
515 1.1 cgd * Look up the command; if not found, bitch.
516 1.1 cgd * Normally, a blank command would map to the
517 1.1 cgd * first command in the table; while sourcing,
518 1.1 cgd * however, we ignore blank lines to eliminate
519 1.1 cgd * confusion.
520 1.1 cgd */
521 1.1 cgd
522 1.1 cgd if (sourcing && *word == '\0')
523 1.30 christos return 0;
524 1.1 cgd com = lex(word);
525 1.19 wiz if (com == NULL) {
526 1.25 christos (void)printf("Unknown command: \"%s\"\n", word);
527 1.1 cgd goto out;
528 1.1 cgd }
529 1.1 cgd
530 1.1 cgd /*
531 1.1 cgd * See if we should execute the command -- if a conditional
532 1.1 cgd * we always execute it, otherwise, check the state of cond.
533 1.1 cgd */
534 1.1 cgd
535 1.30 christos if ((com->c_argtype & F) == 0 && (cond & CSKIP))
536 1.30 christos return 0;
537 1.1 cgd
538 1.1 cgd /*
539 1.1 cgd * Process the arguments to the command, depending
540 1.1 cgd * on the type he expects. Default to an error.
541 1.1 cgd * If we are sourcing an interactive command, it's
542 1.1 cgd * an error.
543 1.1 cgd */
544 1.1 cgd
545 1.30 christos if (mailmode == mm_sending && (com->c_argtype & M) == 0) {
546 1.25 christos (void)printf("May not execute \"%s\" while sending\n",
547 1.1 cgd com->c_name);
548 1.1 cgd goto out;
549 1.1 cgd }
550 1.1 cgd if (sourcing && com->c_argtype & I) {
551 1.25 christos (void)printf("May not execute \"%s\" while sourcing\n",
552 1.1 cgd com->c_name);
553 1.1 cgd goto out;
554 1.1 cgd }
555 1.1 cgd if (readonly && com->c_argtype & W) {
556 1.25 christos (void)printf("May not execute \"%s\" -- message file is read only\n",
557 1.1 cgd com->c_name);
558 1.1 cgd goto out;
559 1.1 cgd }
560 1.32 christos if (contxt == ec_composing && com->c_argtype & R) {
561 1.25 christos (void)printf("Cannot recursively invoke \"%s\"\n", com->c_name);
562 1.1 cgd goto out;
563 1.1 cgd }
564 1.30 christos
565 1.30 christos if (!sourcing && com->c_pipe && value(ENAME_INTERACTIVE) != NULL) {
566 1.30 christos if (setjmp(pipestop))
567 1.30 christos goto out;
568 1.30 christos
569 1.30 christos if (setup_piping(cp, com->c_pipe) == -1)
570 1.30 christos goto out;
571 1.30 christos }
572 1.30 christos switch (com->c_argtype & ARGTYPE_MASK) {
573 1.1 cgd case MSGLIST:
574 1.1 cgd /*
575 1.1 cgd * A message list defaulting to nearest forward
576 1.1 cgd * legal message.
577 1.1 cgd */
578 1.1 cgd if (msgvec == 0) {
579 1.25 christos (void)printf("Illegal use of \"message list\"\n");
580 1.1 cgd break;
581 1.1 cgd }
582 1.1 cgd if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
583 1.1 cgd break;
584 1.1 cgd if (c == 0) {
585 1.30 christos *msgvec = first(com->c_msgflag, com->c_msgmask);
586 1.10 pk msgvec[1] = 0;
587 1.1 cgd }
588 1.10 pk if (*msgvec == 0) {
589 1.25 christos (void)printf("No applicable messages\n");
590 1.1 cgd break;
591 1.1 cgd }
592 1.1 cgd e = (*com->c_func)(msgvec);
593 1.1 cgd break;
594 1.1 cgd
595 1.1 cgd case NDMLIST:
596 1.1 cgd /*
597 1.1 cgd * A message list with no defaults, but no error
598 1.1 cgd * if none exist.
599 1.1 cgd */
600 1.1 cgd if (msgvec == 0) {
601 1.25 christos (void)printf("Illegal use of \"message list\"\n");
602 1.1 cgd break;
603 1.1 cgd }
604 1.1 cgd if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
605 1.1 cgd break;
606 1.1 cgd e = (*com->c_func)(msgvec);
607 1.1 cgd break;
608 1.1 cgd
609 1.1 cgd case STRLIST:
610 1.1 cgd /*
611 1.1 cgd * Just the straight string, with
612 1.1 cgd * leading blanks removed.
613 1.1 cgd */
614 1.33.4.1 matt cp = skip_space(cp);
615 1.1 cgd e = (*com->c_func)(cp);
616 1.1 cgd break;
617 1.1 cgd
618 1.1 cgd case RAWLIST:
619 1.1 cgd /*
620 1.1 cgd * A vector of strings, in shell style.
621 1.1 cgd */
622 1.1 cgd if ((c = getrawlist(cp, arglist,
623 1.33.4.1 matt sizeof(arglist) / sizeof(*arglist))) < 0)
624 1.1 cgd break;
625 1.1 cgd if (c < com->c_minargs) {
626 1.25 christos (void)printf("%s requires at least %d arg(s)\n",
627 1.1 cgd com->c_name, com->c_minargs);
628 1.1 cgd break;
629 1.1 cgd }
630 1.1 cgd if (c > com->c_maxargs) {
631 1.25 christos (void)printf("%s takes no more than %d arg(s)\n",
632 1.1 cgd com->c_name, com->c_maxargs);
633 1.1 cgd break;
634 1.1 cgd }
635 1.1 cgd e = (*com->c_func)(arglist);
636 1.1 cgd break;
637 1.1 cgd
638 1.1 cgd case NOLIST:
639 1.1 cgd /*
640 1.1 cgd * Just the constant zero, for exiting,
641 1.1 cgd * eg.
642 1.1 cgd */
643 1.1 cgd e = (*com->c_func)(0);
644 1.1 cgd break;
645 1.1 cgd
646 1.1 cgd default:
647 1.11 lukem errx(1, "Unknown argtype");
648 1.1 cgd }
649 1.1 cgd
650 1.1 cgd out:
651 1.30 christos close_piping();
652 1.30 christos
653 1.1 cgd /*
654 1.1 cgd * Exit the current source file on
655 1.1 cgd * error.
656 1.1 cgd */
657 1.1 cgd if (e) {
658 1.1 cgd if (e < 0)
659 1.1 cgd return 1;
660 1.1 cgd if (loading)
661 1.1 cgd return 1;
662 1.1 cgd if (sourcing)
663 1.25 christos (void)unstack();
664 1.1 cgd return 0;
665 1.1 cgd }
666 1.7 christos if (com == NULL)
667 1.30 christos return 0;
668 1.32 christos if (contxt != ec_autoprint && com->c_argtype & P &&
669 1.32 christos value(ENAME_AUTOPRINT) != NULL && (dot->m_flag & MDELETED) == 0)
670 1.32 christos (void)execute(__UNCONST("print ."), ec_autoprint);
671 1.1 cgd if (!sourcing && (com->c_argtype & T) == 0)
672 1.1 cgd sawcom = 1;
673 1.30 christos return 0;
674 1.1 cgd }
675 1.1 cgd
676 1.1 cgd
677 1.1 cgd /*
678 1.1 cgd * The following gets called on receipt of an interrupt. This is
679 1.1 cgd * to abort printout of a command, mainly.
680 1.1 cgd * Dispatching here when command() is inactive crashes rcv.
681 1.1 cgd * Close all open files except 0, 1, 2, and the temporary.
682 1.1 cgd * Also, unstack all source files.
683 1.1 cgd */
684 1.30 christos static int inithdr; /* am printing startup headers */
685 1.1 cgd
686 1.1 cgd /*ARGSUSED*/
687 1.30 christos static void
688 1.28 christos intr(int s __unused)
689 1.1 cgd {
690 1.1 cgd noreset = 0;
691 1.1 cgd if (!inithdr)
692 1.1 cgd sawcom++;
693 1.1 cgd inithdr = 0;
694 1.1 cgd while (sourcing)
695 1.25 christos (void)unstack();
696 1.1 cgd
697 1.30 christos close_piping();
698 1.1 cgd close_all_files();
699 1.1 cgd
700 1.1 cgd if (image >= 0) {
701 1.25 christos (void)close(image);
702 1.1 cgd image = -1;
703 1.1 cgd }
704 1.25 christos (void)fprintf(stderr, "Interrupt\n");
705 1.1 cgd reset(0);
706 1.1 cgd }
707 1.1 cgd
708 1.1 cgd /*
709 1.1 cgd * Branch here on hangup signal and simulate "exit".
710 1.1 cgd */
711 1.1 cgd /*ARGSUSED*/
712 1.30 christos static void
713 1.28 christos hangup(int s __unused)
714 1.1 cgd {
715 1.1 cgd /* nothing to do? */
716 1.1 cgd exit(1);
717 1.1 cgd }
718 1.1 cgd
719 1.1 cgd /*
720 1.30 christos * Interpret user commands one by one. If standard input is not a tty,
721 1.30 christos * print no prompt.
722 1.1 cgd */
723 1.30 christos PUBLIC void
724 1.30 christos commands(void)
725 1.1 cgd {
726 1.30 christos int n;
727 1.30 christos char linebuf[LINESIZE];
728 1.30 christos int eofloop;
729 1.30 christos
730 1.30 christos if (!sourcing) {
731 1.30 christos if (signal(SIGINT, SIG_IGN) != SIG_IGN)
732 1.30 christos (void)signal(SIGINT, intr);
733 1.30 christos if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
734 1.30 christos (void)signal(SIGHUP, hangup);
735 1.30 christos (void)signal(SIGTSTP, stop);
736 1.30 christos (void)signal(SIGTTOU, stop);
737 1.30 christos (void)signal(SIGTTIN, stop);
738 1.30 christos }
739 1.30 christos setexit(); /* defined as (void)setjmp(srbuf) in def.h */
740 1.30 christos eofloop = 0; /* initialize this after a possible longjmp */
741 1.30 christos for (;;) {
742 1.30 christos (void)fflush(stdout);
743 1.30 christos sreset();
744 1.30 christos /*
745 1.30 christos * Print the prompt, if needed. Clear out
746 1.30 christos * string space, and flush the output.
747 1.30 christos */
748 1.30 christos if (!sourcing && value(ENAME_INTERACTIVE) != NULL) {
749 1.30 christos if ((prompt = value(ENAME_PROMPT)) == NULL)
750 1.30 christos prompt = DEFAULT_PROMPT;
751 1.30 christos prompt = smsgprintf(prompt, dot);
752 1.30 christos if ((value(ENAME_AUTOINC) != NULL) && (incfile() > 0))
753 1.30 christos (void)printf("New mail has arrived.\n");
754 1.30 christos reset_on_stop = 1;
755 1.30 christos #ifndef USE_EDITLINE
756 1.30 christos (void)printf("%s", prompt);
757 1.30 christos #endif
758 1.30 christos }
759 1.30 christos /*
760 1.30 christos * Read a line of commands from the current input
761 1.30 christos * and handle end of file specially.
762 1.30 christos */
763 1.30 christos n = 0;
764 1.30 christos for (;;) {
765 1.30 christos #ifdef USE_EDITLINE
766 1.30 christos if (!sourcing) {
767 1.30 christos char *line;
768 1.30 christos if ((line = my_gets(&elm.command, prompt, NULL)) == NULL) {
769 1.30 christos if (n == 0)
770 1.30 christos n = -1;
771 1.30 christos break;
772 1.30 christos }
773 1.30 christos (void)strlcpy(linebuf, line, sizeof(linebuf));
774 1.30 christos setscreensize(); /* so we can resize a window */
775 1.30 christos }
776 1.30 christos else {
777 1.30 christos if (mail_readline(input, &linebuf[n], LINESIZE - n) < 0) {
778 1.30 christos if (n == 0)
779 1.30 christos n = -1;
780 1.30 christos break;
781 1.30 christos }
782 1.30 christos }
783 1.30 christos #else /* USE_EDITLINE */
784 1.30 christos if (mail_readline(input, &linebuf[n], LINESIZE - n) < 0) {
785 1.30 christos if (n == 0)
786 1.30 christos n = -1;
787 1.30 christos break;
788 1.30 christos }
789 1.30 christos #endif /* USE_EDITLINE */
790 1.1 cgd
791 1.30 christos if (sourcing) { /* allow comments in source files */
792 1.30 christos char *ptr;
793 1.30 christos if ((ptr = comment_char(linebuf)) != NULL)
794 1.30 christos *ptr = '\0';
795 1.30 christos }
796 1.30 christos if ((n = strlen(linebuf)) == 0)
797 1.30 christos break;
798 1.30 christos n--;
799 1.30 christos if (linebuf[n] != '\\')
800 1.30 christos break;
801 1.30 christos linebuf[n++] = ' ';
802 1.30 christos }
803 1.30 christos reset_on_stop = 0;
804 1.30 christos if (n < 0) {
805 1.30 christos /* eof */
806 1.30 christos if (loading)
807 1.30 christos break;
808 1.30 christos if (sourcing) {
809 1.30 christos (void)unstack();
810 1.30 christos continue;
811 1.30 christos }
812 1.30 christos #ifdef USE_EDITLINE
813 1.30 christos {
814 1.30 christos char *p;
815 1.30 christos if (value(ENAME_INTERACTIVE) != NULL &&
816 1.30 christos (p = value(ENAME_IGNOREEOF)) != NULL &&
817 1.30 christos ++eofloop < (*p == '\0' ? 25 : atoi(p))) {
818 1.30 christos (void)printf("Use \"quit\" to quit.\n");
819 1.30 christos continue;
820 1.30 christos }
821 1.30 christos }
822 1.30 christos #else
823 1.30 christos if (value(ENAME_INTERACTIVE) != NULL &&
824 1.30 christos value(ENAME_IGNOREEOF) != NULL &&
825 1.30 christos ++eofloop < 25) {
826 1.30 christos (void)printf("Use \"quit\" to quit.\n");
827 1.30 christos continue;
828 1.30 christos }
829 1.30 christos #endif
830 1.30 christos break;
831 1.30 christos }
832 1.30 christos eofloop = 0;
833 1.32 christos if (execute(linebuf, ec_normal))
834 1.30 christos break;
835 1.1 cgd }
836 1.1 cgd }
837 1.1 cgd
838 1.1 cgd /*
839 1.1 cgd * Announce information about the file we are editing.
840 1.1 cgd * Return a likely place to set dot.
841 1.1 cgd */
842 1.30 christos PUBLIC int
843 1.17 wiz newfileinfo(int omsgCount)
844 1.1 cgd {
845 1.11 lukem struct message *mp;
846 1.30 christos int d, n, s, t, u, mdot;
847 1.30 christos char fname[PATHSIZE];
848 1.30 christos char *ename;
849 1.1 cgd
850 1.30 christos /*
851 1.30 christos * Figure out where to set the 'dot'. Use the first new or
852 1.30 christos * unread message.
853 1.30 christos */
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 & MNEW)
857 1.1 cgd break;
858 1.30 christos
859 1.30 christos if (mp == NULL)
860 1.30 christos for (mp = get_abs_message(omsgCount + 1); mp;
861 1.30 christos mp = next_abs_message(mp))
862 1.1 cgd if ((mp->m_flag & MREAD) == 0)
863 1.1 cgd break;
864 1.30 christos if (mp != NULL)
865 1.30 christos mdot = get_msgnum(mp);
866 1.1 cgd else
867 1.8 tls mdot = omsgCount + 1;
868 1.30 christos #ifdef THREAD_SUPPORT
869 1.30 christos /*
870 1.30 christos * See if the message is in the current thread.
871 1.30 christos */
872 1.30 christos if (mp != NULL && get_message(1) != NULL && get_message(mdot) != mp)
873 1.30 christos mdot = 0;
874 1.30 christos #endif
875 1.30 christos /*
876 1.30 christos * Scan the message array counting the new, unread, deleted,
877 1.30 christos * and saved messages.
878 1.30 christos */
879 1.30 christos d = n = s = t = u = 0;
880 1.30 christos for (mp = get_abs_message(1); mp; mp = next_abs_message(mp)) {
881 1.1 cgd if (mp->m_flag & MNEW)
882 1.1 cgd n++;
883 1.1 cgd if ((mp->m_flag & MREAD) == 0)
884 1.1 cgd u++;
885 1.1 cgd if (mp->m_flag & MDELETED)
886 1.1 cgd d++;
887 1.1 cgd if (mp->m_flag & MSAVED)
888 1.1 cgd s++;
889 1.30 christos if (mp->m_flag & MTAGGED)
890 1.30 christos t++;
891 1.1 cgd }
892 1.1 cgd ename = mailname;
893 1.30 christos if (getfold(fname, sizeof(fname)) >= 0) {
894 1.30 christos char zname[PATHSIZE];
895 1.30 christos size_t l;
896 1.9 mikel l = strlen(fname);
897 1.30 christos if (l < sizeof(fname) - 1)
898 1.9 mikel fname[l++] = '/';
899 1.9 mikel if (strncmp(fname, mailname, l) == 0) {
900 1.30 christos (void)snprintf(zname, sizeof(zname), "+%s",
901 1.9 mikel mailname + l);
902 1.1 cgd ename = zname;
903 1.1 cgd }
904 1.1 cgd }
905 1.30 christos /*
906 1.30 christos * Display the statistics.
907 1.30 christos */
908 1.25 christos (void)printf("\"%s\": ", ename);
909 1.30 christos {
910 1.30 christos int cnt = get_abs_msgCount();
911 1.30 christos (void)printf("%d message%s", cnt, cnt == 1 ? "" : "s");
912 1.30 christos }
913 1.1 cgd if (n > 0)
914 1.25 christos (void)printf(" %d new", n);
915 1.1 cgd if (u-n > 0)
916 1.25 christos (void)printf(" %d unread", u);
917 1.30 christos if (t > 0)
918 1.30 christos (void)printf(" %d tagged", t);
919 1.1 cgd if (d > 0)
920 1.25 christos (void)printf(" %d deleted", d);
921 1.1 cgd if (s > 0)
922 1.25 christos (void)printf(" %d saved", s);
923 1.1 cgd if (readonly)
924 1.25 christos (void)printf(" [Read only]");
925 1.25 christos (void)printf("\n");
926 1.30 christos
927 1.30 christos return mdot;
928 1.30 christos }
929 1.30 christos
930 1.30 christos /*
931 1.30 christos * Announce the presence of the current Mail version,
932 1.30 christos * give the message count, and print a header listing.
933 1.30 christos */
934 1.30 christos PUBLIC void
935 1.30 christos announce(void)
936 1.30 christos {
937 1.30 christos int vec[2], mdot;
938 1.30 christos
939 1.30 christos mdot = newfileinfo(0);
940 1.30 christos vec[0] = mdot;
941 1.30 christos vec[1] = 0;
942 1.30 christos if ((dot = get_message(mdot)) == NULL)
943 1.30 christos dot = get_abs_message(1); /* make sure we get something! */
944 1.30 christos if (get_abs_msgCount() > 0 && value(ENAME_NOHEADER) == NULL) {
945 1.30 christos inithdr++;
946 1.30 christos (void)headers(vec);
947 1.30 christos inithdr = 0;
948 1.30 christos }
949 1.1 cgd }
950 1.1 cgd
951 1.1 cgd /*
952 1.1 cgd * Print the current version number.
953 1.1 cgd */
954 1.1 cgd
955 1.1 cgd /*ARGSUSED*/
956 1.30 christos PUBLIC int
957 1.28 christos pversion(void *v __unused)
958 1.1 cgd {
959 1.25 christos (void)printf("Version %s\n", version);
960 1.30 christos return 0;
961 1.1 cgd }
962 1.1 cgd
963 1.1 cgd /*
964 1.1 cgd * Load a file of user definitions.
965 1.1 cgd */
966 1.30 christos PUBLIC void
967 1.24 christos load(const char *name)
968 1.1 cgd {
969 1.11 lukem FILE *in, *oldin;
970 1.1 cgd
971 1.1 cgd if ((in = Fopen(name, "r")) == NULL)
972 1.1 cgd return;
973 1.1 cgd oldin = input;
974 1.1 cgd input = in;
975 1.1 cgd loading = 1;
976 1.1 cgd sourcing = 1;
977 1.1 cgd commands();
978 1.1 cgd loading = 0;
979 1.1 cgd sourcing = 0;
980 1.1 cgd input = oldin;
981 1.25 christos (void)Fclose(in);
982 1.1 cgd }
983