Move string dope from strings.c to the new memory.c..
[s-mailx.git] / lex.c
blob24bbffd2ac0f16cfeac1c4fa98c23ecbfc4202a0
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ (Lexical processing of) Commands, and the event loops.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE lex
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 struct cmd {
43 char const *name; /* Name of command */
44 int (*func)(void*); /* Implementor of command */
45 enum argtype argtype; /* Arglist type (see below) */
46 short msgflag; /* Required flags of msgs */
47 short msgmask; /* Relevant flags of msgs */
48 #ifdef HAVE_DOCSTRINGS
49 char const *doc; /* One line doc for command */
50 #endif
52 /* Yechh, can't initialize unions */
53 #define minargs msgflag /* Minimum argcount for RAWLIST */
54 #define maxargs msgmask /* Max argcount for RAWLIST */
56 struct cmd_ghost {
57 struct cmd_ghost *next;
58 struct str cmd; /* Data follows after .name */
59 char name[VFIELD_SIZE(0)];
62 static int _reset_on_stop; /* do a reset() if stopped */
63 static sighandler_type _oldpipe;
64 static struct cmd_ghost *_cmd_ghosts;
65 /* _cmd_tab[] after fun protos */
67 /* Isolate the command from the arguments */
68 static char * _lex_isolate(char const *comm);
70 /* Get first-fit, or NULL */
71 static struct cmd const * _lex(char const *comm);
73 /* Command ghost handling */
74 static int _c_ghost(void *v);
75 static int _c_unghost(void *v);
77 /* Print a list of all commands */
78 static int _c_list(void *v);
80 static int __pcmd_cmp(void const *s1, void const *s2);
82 /* `quit' command */
83 static int _c_quit(void *v);
85 /* Print the binaries compiled-in features */
86 static int _c_features(void *v);
88 /* Print the binaries version number */
89 static int _c_version(void *v);
91 /* When we wake up after ^Z, reprint the prompt */
92 static void stop(int s);
94 /* Branch here on hangup signal and simulate "exit" */
95 static void hangup(int s);
97 /* List of all commands, and list of commands which are specially treated
98 * and deduced in execute(), but we need a list for _c_list() and
99 * print_comm_docstr() */
100 #ifdef HAVE_DOCSTRINGS
101 # define DS(S) , S
102 #else
103 # define DS(S)
104 #endif
105 static struct cmd const _cmd_tab[] = {
106 #include "cmd_tab.h"
108 _special_cmd_tab[] = {
109 { "#", NULL, 0, 0, 0
110 DS(N_("\"Comment command\": ignore remaining (continuable) line")) },
111 { "-", NULL, 0, 0, 0
112 DS(N_("Print out the preceding message")) }
114 #undef DS
116 static char *
117 _lex_isolate(char const *comm)
119 NYD_ENTER;
120 while (*comm != '\0' &&
121 strchr("~|? \t0123456789&%@$^.:/-+*'\",;(`", *comm) == NULL)
122 ++comm;
123 NYD_LEAVE;
124 return UNCONST(comm);
127 static struct cmd const *
128 _lex(char const *comm) /* TODO **command hashtable**! linear list search!!! */
130 struct cmd const *cp, *cpmax;
131 NYD_ENTER;
133 for (cp = cpmax = _cmd_tab, cpmax += NELEM(_cmd_tab); cp != cpmax; ++cp)
134 if (*comm == *cp->name && is_prefix(comm, cp->name))
135 goto jleave;
136 cp = NULL;
137 jleave:
138 NYD_LEAVE;
139 return cp;
142 static int
143 _c_ghost(void *v)
145 char const **argv = v;
146 struct cmd_ghost *lcg, *cg;
147 size_t i, cl, nl;
148 char *cp;
149 NYD_ENTER;
151 /* Show the list? */
152 if (*argv == NULL) {
153 FILE *fp;
155 if ((fp = Ftmp(NULL, "ghost", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL)
156 fp = stdout;
157 for (i = 0, cg = _cmd_ghosts; cg != NULL; cg = cg->next)
158 fprintf(fp, "ghost %s \"%s\"\n", cg->name, string_quote(cg->cmd.s));
159 if (fp != stdout) {
160 page_or_print(fp, i);
161 Fclose(fp);
163 goto jleave;
166 /* Verify the ghost name is a valid one */
167 if (*argv[0] == '\0' || *_lex_isolate(argv[0]) != '\0') {
168 n_err(_("`ghost': can't canonicalize \"%s\"\n"), argv[0]);
169 v = NULL;
170 goto jleave;
173 /* Show command of single ghost? */
174 if (argv[1] == NULL) {
175 for (cg = _cmd_ghosts; cg != NULL; cg = cg->next)
176 if (!strcmp(argv[0], cg->name)) {
177 printf("ghost %s \"%s\"\n", cg->name, string_quote(cg->cmd.s));
178 goto jleave;
180 n_err(_("`ghost': no such alias: \"%s\"\n"), argv[0]);
181 v = NULL;
182 goto jleave;
185 /* Define command for ghost: verify command content */
186 for (cl = 0, i = 1; (cp = UNCONST(argv[i])) != NULL; ++i)
187 if (*cp != '\0')
188 cl += strlen(cp) + 1; /* SP or NUL */
189 if (cl == 0) {
190 n_err(_("`ghost': empty command arguments after \"%s\"\n"), argv[0]);
191 v = NULL;
192 goto jleave;
195 /* If the ghost already exists, recreate */
196 for (lcg = NULL, cg = _cmd_ghosts; cg != NULL; lcg = cg, cg = cg->next)
197 if (!strcmp(cg->name, argv[0])) {
198 if (lcg != NULL)
199 lcg->next = cg->next;
200 else
201 _cmd_ghosts = cg->next;
202 free(cg);
203 break;
206 nl = strlen(argv[0]) +1;
207 cg = smalloc(sizeof(*cg) - VFIELD_SIZEOF(struct cmd_ghost, name) + nl + cl);
208 cg->next = _cmd_ghosts;
209 _cmd_ghosts = cg;
210 memcpy(cg->name, argv[0], nl);
211 cp = cg->cmd.s = cg->name + nl;
212 cg->cmd.l = --cl;
213 while (*++argv != NULL) {
214 i = strlen(*argv);
215 if (i > 0) {
216 memcpy(cp, *argv, i);
217 cp += i;
218 *cp++ = ' ';
221 *--cp = '\0';
222 jleave:
223 NYD_LEAVE;
224 return (v == NULL);
227 static int
228 _c_unghost(void *v)
230 int rv = 0;
231 char const **argv = v, *cp;
232 struct cmd_ghost *lcg, *cg;
233 NYD_ENTER;
235 while ((cp = *argv++) != NULL) {
236 if (cp[0] == '*' && cp[1] == '\0') {
237 while ((cg = _cmd_ghosts) != NULL) {
238 _cmd_ghosts = cg->next;
239 free(cg);
241 } else {
242 for (lcg = NULL, cg = _cmd_ghosts; cg != NULL; lcg = cg, cg = cg->next)
243 if (!strcmp(cg->name, cp)) {
244 if (lcg != NULL)
245 lcg->next = cg->next;
246 else
247 _cmd_ghosts = cg->next;
248 free(cg);
249 goto jouter;
251 n_err(_("`unghost': no such alias: \"%s\"\n"), cp);
252 rv = 1;
253 jouter:
257 NYD_LEAVE;
258 return rv;
261 static int
262 __pcmd_cmp(void const *s1, void const *s2)
264 struct cmd const * const *c1 = s1, * const *c2 = s2;
265 int rv;
266 NYD_ENTER;
268 rv = strcmp((*c1)->name, (*c2)->name);
269 NYD_LEAVE;
270 return rv;
273 static int
274 _c_list(void *v)
276 struct cmd const **cpa, *cp, **cursor;
277 size_t i;
278 NYD_ENTER;
279 UNUSED(v);
281 i = NELEM(_cmd_tab) + NELEM(_special_cmd_tab) + 1;
282 cpa = ac_alloc(sizeof(cp) * i);
284 for (i = 0; i < NELEM(_cmd_tab); ++i)
285 cpa[i] = _cmd_tab + i;
287 size_t j;
289 for (j = 0; j < NELEM(_special_cmd_tab); ++i, ++j)
290 cpa[i] = _special_cmd_tab + j;
292 cpa[i] = NULL;
294 qsort(cpa, i, sizeof(cp), &__pcmd_cmp);
296 printf(_("Commands are:\n"));
297 for (i = 0, cursor = cpa; (cp = *cursor++) != NULL;) {
298 size_t j;
299 if (cp->func == &c_cmdnotsupp)
300 continue;
301 j = strlen(cp->name) + 2;
302 if ((i += j) > 72) {
303 i = j;
304 printf("\n");
306 printf((*cursor != NULL ? "%s, " : "%s\n"), cp->name);
309 ac_free(cpa);
310 NYD_LEAVE;
311 return 0;
314 static int
315 _c_quit(void *v)
317 int rv;
318 NYD_ENTER;
319 UNUSED(v);
321 /* If we are PS_SOURCING, then return 1 so evaluate() can handle it.
322 * Otherwise return -1 to abort command loop */
323 rv = (pstate & PS_SOURCING) ? 1 : -1;
324 NYD_LEAVE;
325 return rv;
328 static int
329 _c_features(void *v)
331 NYD_ENTER;
332 UNUSED(v);
333 printf(_("Features: %s\n"), ok_vlook(features));
334 NYD_LEAVE;
335 return 0;
338 static int
339 _c_version(void *v)
341 NYD_ENTER;
342 UNUSED(v);
343 printf(_("Version %s\n"), ok_vlook(version));
344 NYD_LEAVE;
345 return 0;
348 static void
349 stop(int s)
351 sighandler_type old_action;
352 sigset_t nset;
353 NYD_X; /* Signal handler */
355 old_action = safe_signal(s, SIG_DFL);
357 sigemptyset(&nset);
358 sigaddset(&nset, s);
359 sigprocmask(SIG_UNBLOCK, &nset, NULL);
360 n_raise(s);
361 sigprocmask(SIG_BLOCK, &nset, NULL);
362 safe_signal(s, old_action);
363 if (_reset_on_stop) {
364 _reset_on_stop = 0;
365 n_TERMCAP_RESUME(TRU1);
366 reset(0);
370 static void
371 hangup(int s)
373 NYD_X; /* Signal handler */
374 UNUSED(s);
375 /* nothing to do? */
376 exit(EXIT_ERR);
379 FL bool_t
380 commands(void)
382 struct eval_ctx ev;
383 int n;
384 bool_t volatile rv = TRU1;
385 NYD_ENTER;
387 if (!(pstate & PS_SOURCING)) {
388 if (safe_signal(SIGINT, SIG_IGN) != SIG_IGN)
389 safe_signal(SIGINT, onintr);
390 if (safe_signal(SIGHUP, SIG_IGN) != SIG_IGN)
391 safe_signal(SIGHUP, hangup);
392 /* TODO We do a lot of redundant signal handling, especially
393 * TODO with the command line editor(s); try to merge this */
394 safe_signal(SIGTSTP, stop);
395 safe_signal(SIGTTOU, stop);
396 safe_signal(SIGTTIN, stop);
398 _oldpipe = safe_signal(SIGPIPE, SIG_IGN);
399 safe_signal(SIGPIPE, _oldpipe);
401 memset(&ev, 0, sizeof ev);
403 setexit();
404 for (;;) {
405 char *temporary_orig_line; /* XXX eval_ctx.ev_line not yet constant */
407 n_COLOUR( n_colour_env_pop(TRU1); )
409 /* TODO Unless we have our signal manager (or however we do it) child
410 * TODO processes may have time slots where their execution isn't
411 * TODO protected by signal handlers (in between start and setup
412 * TODO completed). close_all_files() is only called from onintr()
413 * TODO so those may linger possibly forever */
414 if (!(pstate & PS_SOURCING))
415 close_all_files();
417 interrupts = 0;
418 handlerstacktop = NULL;
420 temporary_localopts_free(); /* XXX intermediate hack */
421 sreset((pstate & PS_SOURCING) != 0);
422 if (!(pstate & PS_SOURCING)) {
423 char *cp;
425 /* TODO Note: this buffer may contain a password. We should redefine
426 * TODO the code flow which has to do that */
427 if ((cp = termios_state.ts_linebuf) != NULL) {
428 termios_state.ts_linebuf = NULL;
429 termios_state.ts_linesize = 0;
430 free(cp); /* TODO pool give-back */
432 /* TODO Due to expand-on-tab of NCL the buffer may grow */
433 if (ev.ev_line.l > LINESIZE * 3) {
434 free(ev.ev_line.s); /* TODO pool! but what? */
435 ev.ev_line.s = NULL;
436 ev.ev_line.l = ev.ev_line_size = 0;
440 if (!(pstate & PS_SOURCING) && (options & OPT_INTERACTIVE)) {
441 char *cp;
443 cp = ok_vlook(newmail);
444 if ((options & OPT_TTYIN) && cp != NULL) {
445 struct stat st;
447 n = (cp != NULL && strcmp(cp, "nopoll"));
448 if ((mb.mb_type == MB_FILE && !stat(mailname, &st) &&
449 st.st_size > mailsize) ||
450 (mb.mb_type == MB_MAILDIR && n != 0)) {
451 size_t odot = PTR2SIZE(dot - message);
452 ui32_t odid = (pstate & PS_DID_PRINT_DOT);
454 if (setfile(mailname,
455 FEDIT_NEWMAIL |
456 ((mb.mb_perm & MB_DELE) ? 0 : FEDIT_RDONLY)) < 0) {
457 exit_status |= EXIT_ERR;
458 rv = FAL0;
459 break;
461 dot = message + odot;
462 pstate |= odid;
466 _reset_on_stop = 1;
467 exit_status = EXIT_OK;
470 /* Read a line of commands and handle end of file specially */
471 jreadline:
472 ev.ev_line.l = ev.ev_line_size;
473 n = readline_input(NULL, TRU1, &ev.ev_line.s, &ev.ev_line.l,
474 ev.ev_new_content);
475 ev.ev_line_size = (ui32_t)ev.ev_line.l;
476 ev.ev_line.l = (ui32_t)n;
477 _reset_on_stop = 0;
478 if (n < 0) {
479 /* EOF */
480 if (pstate & PS_LOADING)
481 break;
482 if (pstate & PS_SOURCING) {
483 unstack();
484 continue;
486 if ((options & OPT_INTERACTIVE) && ok_blook(ignoreeof)) {
487 printf(_("*ignoreeof* set, use `quit' to quit.\n"));
488 continue;
490 break;
493 temporary_orig_line = ((pstate & PS_SOURCING) ||
494 !(options & OPT_INTERACTIVE)) ? NULL
495 : savestrbuf(ev.ev_line.s, ev.ev_line.l);
496 pstate &= ~PS_HOOK_MASK;
497 if (evaluate(&ev)) {
498 if (pstate & PS_LOADING) /* TODO mess; join PS_EVAL_ERROR.. */
499 rv = FAL0;
500 break;
502 if ((options & OPT_BATCH_FLAG) && ok_blook(batch_exit_on_error)) {
503 if (exit_status != EXIT_OK)
504 break;
505 if ((pstate & (PS_IN_LOAD | PS_EVAL_ERROR)) == PS_EVAL_ERROR) {
506 exit_status = EXIT_ERR;
507 break;
510 if (!(pstate & PS_SOURCING) && (options & OPT_INTERACTIVE)) {
511 if (ev.ev_new_content != NULL)
512 goto jreadline;
513 /* That *can* happen since evaluate() unstack()s on error! */
514 if (temporary_orig_line != NULL)
515 n_tty_addhist(temporary_orig_line, !ev.ev_add_history);
519 if (ev.ev_line.s != NULL)
520 free(ev.ev_line.s);
521 if (pstate & PS_SOURCING)
522 sreset(FAL0);
523 NYD_LEAVE;
524 return rv;
527 FL int
528 execute(char *linebuf, size_t linesize) /* XXX LEGACY */
530 struct eval_ctx ev;
531 int rv;
532 NYD_ENTER;
534 memset(&ev, 0, sizeof ev);
535 ev.ev_line.s = linebuf;
536 ev.ev_line.l = linesize;
537 ev.ev_is_recursive = TRU1;
538 n_COLOUR( n_colour_env_push(); )
539 rv = evaluate(&ev);
540 n_COLOUR( n_colour_env_pop(FAL0); )
542 NYD_LEAVE;
543 return rv;
546 FL int
547 evaluate(struct eval_ctx *evp)
549 struct str line;
550 char _wordbuf[2], *arglist[MAXARGC], *cp, *word;
551 struct cmd_ghost *cg = NULL;
552 struct cmd const *com = NULL;
553 int muvec[2], c, e = 1;
554 NYD_ENTER;
556 line = evp->ev_line; /* XXX don't change original (buffer pointer) */
557 evp->ev_add_history = FAL0;
558 evp->ev_new_content = NULL;
560 /* Command ghosts that refer to shell commands or macro expansion restart */
561 jrestart:
563 /* Strip the white space away from the beginning of the command */
564 for (cp = line.s; whitechar(*cp); ++cp)
566 line.l -= PTR2SIZE(cp - line.s);
568 /* Ignore comments */
569 if (*cp == '#')
570 goto jleave0;
572 /* Handle ! differently to get the correct lexical conventions */
573 if (*cp == '!') {
574 if (pstate & PS_SOURCING) {
575 n_err(_("Can't `!' while `source'ing\n"));
576 goto jleave;
578 c_shell(++cp);
579 evp->ev_add_history = TRU1;
580 goto jleave0;
583 /* Isolate the actual command; since it may not necessarily be
584 * separated from the arguments (as in `p1') we need to duplicate it to
585 * be able to create a NUL terminated version.
586 * We must be aware of several special one letter commands here */
587 arglist[0] = cp;
588 if ((cp = _lex_isolate(cp)) == arglist[0] &&
589 (*cp == '|' || *cp == '~' || *cp == '?'))
590 ++cp;
591 c = (int)PTR2SIZE(cp - arglist[0]);
592 line.l -= c;
593 word = UICMP(z, c, <, sizeof _wordbuf) ? _wordbuf : salloc(c +1);
594 memcpy(word, arglist[0], c);
595 word[c] = '\0';
597 /* Look up the command; if not found, bitch.
598 * Normally, a blank command would map to the first command in the
599 * table; while PS_SOURCING, however, we ignore blank lines to eliminate
600 * confusion; act just the same for ghosts */
601 if (*word == '\0') {
602 if ((pstate & PS_SOURCING) || cg != NULL)
603 goto jleave0;
604 com = _cmd_tab + 0;
605 goto jexec;
608 /* If this is the first evaluation, check command ghosts */
609 if (cg == NULL) {
610 /* TODO relink list head, so it's sorted on usage over time?
611 * TODO in fact, there should be one hashmap over all commands and ghosts
612 * TODO so that the lookup could be made much more efficient than it is
613 * TODO now (two adjacent list searches! */
614 for (cg = _cmd_ghosts; cg != NULL; cg = cg->next)
615 if (!strcmp(word, cg->name)) {
616 if (line.l > 0) {
617 size_t i = cg->cmd.l;
618 line.s = salloc(i + line.l +1);
619 memcpy(line.s, cg->cmd.s, i);
620 memcpy(line.s + i, cp, line.l);
621 line.s[i += line.l] = '\0';
622 line.l = i;
623 } else {
624 line.s = cg->cmd.s;
625 line.l = cg->cmd.l;
627 goto jrestart;
631 if ((com = _lex(word)) == NULL || com->func == &c_cmdnotsupp) {
632 bool_t s = condstack_isskip();
633 if (!s || (options & OPT_D_V))
634 n_err(_("Unknown command%s: `%s'\n"),
635 (s ? _(" (conditionally ignored)") : ""), word);
636 if (s)
637 goto jleave0;
638 if (com != NULL) {
639 c_cmdnotsupp(NULL);
640 com = NULL;
642 goto jleave;
645 /* See if we should execute the command -- if a conditional we always
646 * execute it, otherwise, check the state of cond */
647 jexec:
648 if (!(com->argtype & ARG_F) && condstack_isskip())
649 goto jleave0;
651 /* Process the arguments to the command, depending on the type it expects,
652 * default to error. If we're PS_SOURCING an interactive command: error */
653 if ((options & OPT_SENDMODE) && !(com->argtype & ARG_M)) {
654 n_err(_("May not execute `%s' while sending\n"), com->name);
655 goto jleave;
657 if ((pstate & PS_SOURCING) && (com->argtype & ARG_I)) {
658 n_err(_("May not execute `%s' while `source'ing\n"), com->name);
659 goto jleave;
661 if (!(mb.mb_perm & MB_DELE) && (com->argtype & ARG_W)) {
662 n_err(_("May not execute `%s' -- message file is read only\n"),
663 com->name);
664 goto jleave;
666 if (evp->ev_is_recursive && (com->argtype & ARG_R)) {
667 n_err(_("Cannot recursively invoke `%s'\n"), com->name);
668 goto jleave;
670 if (mb.mb_type == MB_VOID && (com->argtype & ARG_A)) {
671 n_err(_("Cannot execute `%s' without active mailbox\n"), com->name);
672 goto jleave;
674 if (com->argtype & ARG_O)
675 OBSOLETE2(_("this command will be removed"), com->name);
677 if (com->argtype & ARG_V)
678 temporary_arg_v_store = NULL;
680 switch (com->argtype & ARG_ARGMASK) {
681 case ARG_MSGLIST:
682 /* Message list defaulting to nearest forward legal message */
683 if (n_msgvec == NULL)
684 goto je96;
685 if ((c = getmsglist(cp, n_msgvec, com->msgflag)) < 0)
686 break;
687 if (c == 0) {
688 *n_msgvec = first(com->msgflag, com->msgmask);
689 if (*n_msgvec != 0)
690 n_msgvec[1] = 0;
692 if (*n_msgvec == 0) {
693 if (!(pstate & PS_HOOK_MASK))
694 printf(_("No applicable messages\n"));
695 break;
697 e = (*com->func)(n_msgvec);
698 break;
700 case ARG_NDMLIST:
701 /* Message list with no defaults, but no error if none exist */
702 if (n_msgvec == NULL) {
703 je96:
704 n_err(_("Invalid use of \"message list\"\n"));
705 break;
707 if ((c = getmsglist(cp, n_msgvec, com->msgflag)) < 0)
708 break;
709 e = (*com->func)(n_msgvec);
710 break;
712 case ARG_STRLIST:
713 /* Just the straight string, with leading blanks removed */
714 while (whitechar(*cp))
715 ++cp;
716 e = (*com->func)(cp);
717 break;
719 case ARG_RAWLIST:
720 case ARG_ECHOLIST:
721 /* A vector of strings, in shell style */
722 if ((c = getrawlist(cp, line.l, arglist, NELEM(arglist),
723 ((com->argtype & ARG_ARGMASK) == ARG_ECHOLIST))) < 0)
724 break;
725 if (c < com->minargs) {
726 n_err(_("`%s' requires at least %d arg(s)\n"),
727 com->name, com->minargs);
728 break;
730 if (c > com->maxargs) {
731 n_err(_("`%s' takes no more than %d arg(s)\n"),
732 com->name, com->maxargs);
733 break;
735 e = (*com->func)(arglist);
736 break;
738 case ARG_NOLIST:
739 /* Just the constant zero, for exiting, eg. */
740 e = (*com->func)(0);
741 break;
743 default:
744 n_panic(_("Unknown argument type"));
747 if (e == 0 && (com->argtype & ARG_V) &&
748 (cp = temporary_arg_v_store) != NULL) {
749 temporary_arg_v_store = NULL;
750 evp->ev_new_content = cp;
751 goto jleave0;
753 if (!(com->argtype & ARG_H) && !(pstate & PS_MSGLIST_SAW_NO))
754 evp->ev_add_history = TRU1;
756 jleave:
757 /* Exit the current source file on error TODO what a mess! */
758 if (e == 0)
759 pstate &= ~PS_EVAL_ERROR;
760 else {
761 pstate |= PS_EVAL_ERROR;
762 if (e < 0 || (pstate & PS_LOADING)) {
763 e = 1;
764 goto jret;
766 if (pstate & PS_SOURCING)
767 unstack();
768 goto jret0;
770 if (com == NULL)
771 goto jret0;
772 if ((com->argtype & ARG_P) && ok_blook(autoprint))
773 if (visible(dot)) {
774 muvec[0] = (int)PTR2SIZE(dot - message + 1);
775 muvec[1] = 0;
776 c_type(muvec); /* TODO what if error? re-eval! */
778 if (!(pstate & (PS_SOURCING | PS_HOOK_MASK)) && !(com->argtype & ARG_T))
779 pstate |= PS_SAW_COMMAND;
780 jleave0:
781 pstate &= ~PS_EVAL_ERROR;
782 jret0:
783 e = 0;
784 jret:
785 NYD_LEAVE;
786 return e;
789 FL void
790 onintr(int s)
792 NYD_X; /* Signal handler */
794 if (handlerstacktop != NULL) {
795 handlerstacktop(s);
796 return;
798 safe_signal(SIGINT, onintr);
799 noreset = 0;
800 while (pstate & PS_SOURCING)
801 unstack();
803 termios_state_reset();
804 close_all_files();
806 if (image >= 0) {
807 close(image);
808 image = -1;
810 if (interrupts != 1)
811 n_err_sighdl(_("Interrupt\n"));
812 safe_signal(SIGPIPE, _oldpipe);
813 reset(0);
816 #ifdef HAVE_DOCSTRINGS
817 FL bool_t
818 print_comm_docstr(char const *comm)
820 struct cmd_ghost const *cg;
821 struct cmd const *cp, *cpmax;
822 bool_t rv = FAL0;
823 NYD_ENTER;
825 /* Ghosts take precedence */
826 for (cg = _cmd_ghosts; cg != NULL; cg = cg->next)
827 if (!strcmp(comm, cg->name)) {
828 printf("%s -> ", comm);
829 comm = cg->cmd.s;
830 break;
833 cpmax = (cp = _cmd_tab) + NELEM(_cmd_tab);
834 jredo:
835 for (; cp < cpmax; ++cp) {
836 if (!strcmp(comm, cp->name))
837 printf("%s: %s\n", comm, V_(cp->doc));
838 else if (is_prefix(comm, cp->name))
839 printf("%s (%s): %s\n", comm, cp->name, V_(cp->doc));
840 else
841 continue;
842 rv = TRU1;
843 break;
845 if (!rv && cpmax == _cmd_tab + NELEM(_cmd_tab)) {
846 cpmax = (cp = _special_cmd_tab) + NELEM(_special_cmd_tab);
847 goto jredo;
850 if (!rv && cg != NULL) {
851 printf("\"%s\"\n", comm);
852 rv = TRU1;
854 NYD_LEAVE;
855 return rv;
857 #endif /* HAVE_DOCSTRINGS */
859 /* s-it-mode */