nail.h: add n_BITENUM_MASK(LO,HI)
[s-mailx.git] / lex_input.c
blob95822c40debbf1a7f32e02526869c89d6af4b707
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Command input, lexing and evaluation, resource file loading and `source'ing.
3 *@ TODO PS_ROBOT requires yet PS_SOURCING, which REALLY sucks.
4 *@ TODO Commands and ghosts deserve a hashmap. Or so.
6 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
7 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
8 */
9 /*
10 * Copyright (c) 1980, 1993
11 * The Regents of the University of California. All rights reserved.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
37 #undef n_FILE
38 #define n_FILE lex_input
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 enum a_lex_input_flags{
45 a_LEX_NONE,
46 a_LEX_FREE = 1<<0, /* Structure was allocated, free() it */
47 a_LEX_PIPE = 1<<1, /* Open on a pipe */
48 a_LEX_MACRO = 1<<2, /* Running a macro */
49 a_LEX_MACRO_FREE_DATA = 1<<3, /* Lines are allocated, free(3) once done */
50 a_LEX_MACRO_X_OPTION = 1<<4, /* Macro indeed command line -X option */
51 a_LEX_MACRO_CMD = 1<<5, /* Macro indeed single-line: ~:COMMAND */
53 a_LEX_SUPER_MACRO = 1<<16 /* *Not* inheriting PS_SOURCING state */
56 struct a_lex_cmd{
57 char const *lc_name; /* Name of command */
58 int (*lc_func)(void*); /* Implementor of command */
59 enum argtype lc_argtype; /* Arglist type (see below) */
60 si16_t lc_msgflag; /* Required flags of msgs */
61 si16_t lc_msgmask; /* Relevant flags of msgs */
62 #ifdef HAVE_DOCSTRINGS
63 char const *lc_doc; /* One line doc for command */
64 #endif
66 /* Yechh, can't initialize unions */
67 #define lc_minargs lc_msgflag /* Minimum argcount for RAWLIST */
68 #define lc_maxargs lc_msgmask /* Max argcount for RAWLIST */
70 struct a_lex_ghost{
71 struct a_lex_ghost *lg_next;
72 struct str lg_cmd; /* Data follows after .lg_name */
73 char lg_name[VFIELD_SIZE(0)];
76 struct a_lex_eval_ctx{
77 struct str le_line; /* The terminated data to _evaluate() */
78 ui32_t le_line_size; /* May be used to store line memory size */
79 bool_t le_is_recursive; /* Evaluation in evaluation? (collect ~:) */
80 ui8_t __dummy[3];
81 bool_t le_add_history; /* Enter (final) command in history? */
82 char const *le_new_content; /* History: reenter line, start with this */
85 struct a_lex_input_stack{
86 struct a_lex_input_stack *li_outer;
87 FILE *li_file; /* File we were in */
88 void *li_cond; /* Saved state of conditional stack */
89 ui32_t li_flags; /* enum a_lex_input_flags */
90 ui32_t li_loff; /* Pseudo (macro): index in .li_lines */
91 char **li_lines; /* Pseudo content, lines unfolded */
92 char li_name[VFIELD_SIZE(0)]; /* Name of file or macro */
95 static bool_t a_lex_reset_on_stop; /* do a reset() if stopped */
96 static sighandler_type a_lex_oldpipe;
97 static struct a_lex_ghost *a_lex_ghosts;
98 /* a_lex_cmd_tab[] after fun protos */
100 /* */
101 static struct a_lex_input_stack *a_lex_input;
103 /* Isolate the command from the arguments */
104 static char *a_lex_isolate(char const *comm);
106 /* Command ghost handling */
107 static int a_lex_c_ghost(void *v);
108 static int a_lex_c_unghost(void *v);
110 /* Print a list of all commands */
111 static int a_lex_c_list(void *v);
113 static int a_lex__pcmd_cmp(void const *s1, void const *s2);
115 /* `quit' command */
116 static int a_lex_c_quit(void *v);
118 /* Print the binaries compiled-in features */
119 static int a_lex_c_features(void *v);
121 /* Print the binaries version number */
122 static int a_lex_c_version(void *v);
124 /* PS_STATE_PENDMASK requires some actions */
125 static void a_lex_update_pstate(void);
127 /* Evaluate a single command.
128 * .le_add_history and .le_new_content will be updated upon success.
129 * Command functions return 0 for success, 1 for error, and -1 for abort.
130 * 1 or -1 aborts a load or source, a -1 aborts the interactive command loop */
131 static int a_lex_evaluate(struct a_lex_eval_ctx *evp);
133 /* Get first-fit, or NULL */
134 static struct a_lex_cmd const *a_lex__firstfit(char const *comm);
136 /* When we wake up after ^Z, reprint the prompt */
137 static void a_lex_stop(int s);
139 /* Branch here on hangup signal and simulate "exit" */
140 static void a_lex_hangup(int s);
142 /* The following gets called on receipt of an interrupt. Close all open files
143 * except 0, 1, 2, and the temporary. Also, unstack all source files */
144 static void a_lex_onintr(int s);
146 /* Pop the current input back to the previous level. Update the program state.
147 * If the argument is TRUM1 then we don't alert and error out if the stack
148 * doesn't exist at all */
149 static void a_lex_unstack(bool_t eval_error);
151 /* `source' and `source_if' (if silent_error: no pipes allowed, then) */
152 static bool_t a_lex_source_file(char const *file, bool_t silent_error);
154 /* System resource file load()ing or -X command line option array traversal */
155 static bool_t a_lex_load(struct a_lex_input_stack *lip);
157 /* A simplified command loop for recursed state machines */
158 static bool_t a_commands_recursive(void);
160 /* List of all commands, and list of commands which are specially treated
161 * and deduced in _evaluate(), but we need a list for _c_list() and
162 * print_comm_docstr() */
163 #ifdef HAVE_DOCSTRINGS
164 # define DS(S) , S
165 #else
166 # define DS(S)
167 #endif
168 static struct a_lex_cmd const a_lex_cmd_tab[] = {
169 #include "cmd_tab.h"
171 a_lex_special_cmd_tab[] = {
172 { "#", NULL, 0, 0, 0
173 DS(N_("\"Comment command\": ignore remaining (continuable) line")) },
174 { "-", NULL, 0, 0, 0
175 DS(N_("Print out the preceding message")) }
177 #undef DS
179 static char *
180 a_lex_isolate(char const *comm){
181 NYD2_ENTER;
182 while(*comm != '\0' &&
183 strchr("~|? \t0123456789&%@$^.:/-+*'\",;(`", *comm) == NULL)
184 ++comm;
185 NYD2_LEAVE;
186 return UNCONST(comm);
189 static int
190 a_lex_c_ghost(void *v){
191 struct a_lex_ghost *lgp, *gp;
192 size_t i, cl, nl;
193 char *cp;
194 char const **argv;
195 NYD_ENTER;
197 argv = v;
199 /* Show the list? */
200 if(*argv == NULL){
201 FILE *fp;
203 if((fp = Ftmp(NULL, "ghost", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL)
204 fp = stdout;
206 for(i = 0, gp = a_lex_ghosts; gp != NULL; gp = gp->lg_next)
207 fprintf(fp, "ghost %s \"%s\"\n",
208 gp->lg_name, string_quote(gp->lg_cmd.s));
210 if(fp != stdout){
211 page_or_print(fp, i);
212 Fclose(fp);
214 goto jleave;
217 /* Verify the ghost name is a valid one */
218 if(*argv[0] == '\0' || *a_lex_isolate(argv[0]) != '\0'){
219 n_err(_("`ghost': can't canonicalize \"%s\"\n"), argv[0]);
220 v = NULL;
221 goto jleave;
224 /* Show command of single ghost? */
225 if(argv[1] == NULL){
226 for(gp = a_lex_ghosts; gp != NULL; gp = gp->lg_next)
227 if(!strcmp(argv[0], gp->lg_name)){
228 printf("ghost %s \"%s\"\n",
229 gp->lg_name, string_quote(gp->lg_cmd.s));
230 goto jleave;
232 n_err(_("`ghost': no such alias: \"%s\"\n"), argv[0]);
233 v = NULL;
234 goto jleave;
237 /* Define command for ghost: verify command content */
238 for(cl = 0, i = 1; (cp = UNCONST(argv[i])) != NULL; ++i)
239 if(*cp != '\0')
240 cl += strlen(cp) +1; /* SP or NUL */
241 if(cl == 0){
242 n_err(_("`ghost': empty command arguments after \"%s\"\n"), argv[0]);
243 v = NULL;
244 goto jleave;
247 /* If the ghost already exists, recreate */
248 for(lgp = NULL, gp = a_lex_ghosts; gp != NULL; lgp = gp, gp = gp->lg_next)
249 if(!strcmp(gp->lg_name, argv[0])){
250 if(lgp != NULL)
251 lgp->lg_next = gp->lg_next;
252 else
253 a_lex_ghosts = gp->lg_next;
254 free(gp);
255 break;
258 nl = strlen(argv[0]) +1;
259 gp = smalloc(sizeof(*gp) - VFIELD_SIZEOF(struct a_lex_ghost, lg_name) +
260 nl + cl);
261 gp->lg_next = a_lex_ghosts;
262 a_lex_ghosts = gp;
263 memcpy(gp->lg_name, argv[0], nl);
264 cp = gp->lg_cmd.s = gp->lg_name + nl;
265 gp->lg_cmd.l = --cl;
267 while(*++argv != NULL)
268 if((i = strlen(*argv)) > 0){
269 memcpy(cp, *argv, i);
270 cp += i;
271 *cp++ = ' ';
273 *--cp = '\0';
274 jleave:
275 NYD_LEAVE;
276 return v == NULL;
279 static int
280 a_lex_c_unghost(void *v){
281 struct a_lex_ghost *lgp, *gp;
282 char const **argv, *cp;
283 int rv;
284 NYD_ENTER;
286 rv = 0;
287 argv = v;
289 while((cp = *argv++) != NULL){
290 if(cp[0] == '*' && cp[1] == '\0'){
291 while((gp = a_lex_ghosts) != NULL){
292 a_lex_ghosts = gp->lg_next;
293 free(gp);
295 }else{
296 for(lgp = NULL, gp = a_lex_ghosts; gp != NULL;
297 lgp = gp, gp = gp->lg_next)
298 if(!strcmp(gp->lg_name, cp)){
299 if(lgp != NULL)
300 lgp->lg_next = gp->lg_next;
301 else
302 a_lex_ghosts = gp->lg_next;
303 free(gp);
304 goto jouter;
306 n_err(_("`unghost': no such alias: \"%s\"\n"), cp);
307 rv = 1;
308 jouter: ;
311 NYD_LEAVE;
312 return rv;
315 static int
316 a_lex_c_list(void *v){
317 struct a_lex_cmd const **cpa, *cp, **cursor;
318 size_t i;
319 NYD_ENTER;
321 i = NELEM(a_lex_cmd_tab) + NELEM(a_lex_special_cmd_tab) +1;
322 cpa = salloc(sizeof(cp) * i);
324 for(i = 0; i < NELEM(a_lex_cmd_tab); ++i)
325 cpa[i] = &a_lex_cmd_tab[i];
326 /* C99 */{
327 size_t j;
329 for(j = 0; j < NELEM(a_lex_special_cmd_tab); ++i, ++j)
330 cpa[i] = &a_lex_special_cmd_tab[j];
332 cpa[i] = NULL;
334 /* C99 */{
335 char const *xcp = v;
337 if(*xcp == '\0')
338 qsort(cpa, i, sizeof(xcp), &a_lex__pcmd_cmp);
341 printf(_("Commands are:\n"));
342 for(i = 0, cursor = cpa; (cp = *cursor++) != NULL;){
343 size_t j;
345 if(cp->lc_func == &c_cmdnotsupp)
346 continue;
347 j = strlen(cp->lc_name) + 2;
348 if((i += j) > 72){
349 i = j;
350 printf("\n");
352 printf((*cursor != NULL ? "%s, " : "%s\n"), cp->lc_name);
354 NYD_LEAVE;
355 return 0;
358 static int
359 a_lex__pcmd_cmp(void const *s1, void const *s2){
360 struct a_lex_cmd const * const *cp1, * const *cp2;
361 int rv;
362 NYD2_ENTER;
364 cp1 = s1;
365 cp2 = s2;
366 rv = strcmp((*cp1)->lc_name, (*cp2)->lc_name);
367 NYD2_LEAVE;
368 return rv;
371 static int
372 a_lex_c_quit(void *v){
373 NYD_ENTER;
374 UNUSED(v);
376 /* If we are PS_SOURCING, then return 1 so _evaluate() can handle it.
377 * Otherwise return -1 to abort command loop */
378 pstate |= PS_EXIT;
379 NYD_LEAVE;
380 return 0;
383 static int
384 a_lex_c_features(void *v){
385 NYD_ENTER;
387 UNUSED(v);
389 printf(_("Features: %s\n"), ok_vlook(features));
390 NYD_LEAVE;
391 return 0;
394 static int
395 a_lex_c_version(void *v){
396 NYD_ENTER;
398 UNUSED(v);
400 printf(_("Version %s\n"), ok_vlook(version));
401 NYD_LEAVE;
402 return 0;
405 static void
406 a_lex_update_pstate(void){
407 NYD_ENTER;
409 if(pstate & PS_SIGWINCH_PEND){
410 char buf[32];
412 snprintf(buf, sizeof buf, "%d", scrnwidth);
413 ok_vset(COLUMNS, buf);
414 snprintf(buf, sizeof buf, "%d", scrnheight);
415 ok_vset(LINES, buf);
418 pstate &= ~PS_PSTATE_PENDMASK;
419 NYD_LEAVE;
422 static int
423 a_lex_evaluate(struct a_lex_eval_ctx *evp){
424 /* xxx old style(9), but also old code */
425 struct str line;
426 char _wordbuf[2], *arglist[MAXARGC], *cp, *word;
427 struct a_lex_ghost *gp;
428 struct a_lex_cmd const *cmd;
429 int c, e;
430 NYD_ENTER;
432 e = 1;
433 cmd = NULL;
434 gp = NULL;
435 line = evp->le_line; /* XXX don't change original (buffer pointer) */
436 assert(line.s[line.l] == '\0');
437 evp->le_add_history = FAL0;
438 evp->le_new_content = NULL;
440 /* Command ghosts that refer to shell commands or macro expansion restart */
441 jrestart:
443 /* Strip the white space away from end and beginning of command */
444 if(line.l > 0){
445 size_t i = line.l;
447 for(cp = &line.s[i -1]; spacechar(*cp); --cp)
448 --i;
449 line.l = i;
451 for(cp = line.s; spacechar(*cp); ++cp)
453 line.l -= PTR2SIZE(cp - line.s);
455 /* Ignore null commands (comments) */
456 if(*cp == '#')
457 goto jleave0;
459 /* Handle ! differently to get the correct lexical conventions */
460 arglist[0] = cp;
461 if(*cp == '!')
462 ++cp;
463 /* Isolate the actual command; since it may not necessarily be
464 * separated from the arguments (as in `p1') we need to duplicate it to
465 * be able to create a NUL terminated version.
466 * We must be aware of several special one letter commands here */
467 else if((cp = a_lex_isolate(cp)) == arglist[0] &&
468 (*cp == '|' || *cp == '~' || *cp == '?'))
469 ++cp;
470 c = (int)PTR2SIZE(cp - arglist[0]);
471 line.l -= c;
472 word = UICMP(z, c, <, sizeof _wordbuf) ? _wordbuf : salloc(c +1);
473 memcpy(word, arglist[0], c);
474 word[c] = '\0';
476 /* Look up the command; if not found, bitch.
477 * Normally, a blank command would map to the first command in the
478 * table; while PS_SOURCING, however, we ignore blank lines to eliminate
479 * confusion; act just the same for ghosts */
480 if(*word == '\0'){
481 if((pstate & PS_ROBOT) || gp != NULL)
482 goto jleave0;
483 cmd = a_lex_cmd_tab + 0;
484 goto jexec;
487 /* If this is the first evaluation, check command ghosts */
488 if(gp == NULL){
489 /* TODO relink list head, so it's sorted on usage over time?
490 * TODO in fact, there should be one hashmap over all commands and ghosts
491 * TODO so that the lookup could be made much more efficient than it is
492 * TODO now (two adjacent list searches! */
493 for(gp = a_lex_ghosts; gp != NULL; gp = gp->lg_next)
494 if(!strcmp(word, gp->lg_name)){
495 if(line.l > 0){
496 size_t i;
498 i = gp->lg_cmd.l;
499 line.s = salloc(i + line.l +1);
500 memcpy(line.s, gp->lg_cmd.s, i);
501 memcpy(line.s + i, cp, line.l);
502 line.s[i += line.l] = '\0';
503 line.l = i;
504 }else{
505 line.s = gp->lg_cmd.s;
506 line.l = gp->lg_cmd.l;
508 goto jrestart;
512 if((cmd = a_lex__firstfit(word)) == NULL || cmd->lc_func == &c_cmdnotsupp){
513 bool_t s;
515 if(!(s = condstack_isskip()) || (options & OPT_D_V))
516 n_err(_("Unknown command%s: `%s'\n"),
517 (s ? _(" (ignored due to `if' condition)") : ""), word);
518 if(s)
519 goto jleave0;
520 if(cmd != NULL){
521 c_cmdnotsupp(NULL);
522 cmd = NULL;
524 goto jleave;
527 /* See if we should execute the command -- if a conditional we always
528 * execute it, otherwise, check the state of cond */
529 jexec:
530 if(!(cmd->lc_argtype & ARG_F) && condstack_isskip())
531 goto jleave0;
533 /* Process the arguments to the command, depending on the type it expects */
534 if(!(cmd->lc_argtype & ARG_M) && (options & OPT_SENDMODE)){
535 n_err(_("May not execute `%s' while sending\n"), cmd->lc_name);
536 goto jleave;
538 if((cmd->lc_argtype & ARG_S) && !(pstate & PS_STARTED)){
539 n_err(_("May not execute `%s' during startup\n"), cmd->lc_name);
540 goto jleave;
542 if((cmd->lc_argtype & ARG_I) &&
543 !(options & (OPT_INTERACTIVE | OPT_BATCH_FLAG))){
544 n_err(_("May not execute `%s' unless interactive or in batch mode\n"),
545 cmd->lc_name);
546 goto jleave;
548 if((cmd->lc_argtype & ARG_R) && (pstate & PS_RECURSED)){
549 n_err(_("Cannot invoke `%s' when in recursed mode (e.g., composing)\n"),
550 cmd->lc_name);
551 goto jleave;
554 if((cmd->lc_argtype & ARG_W) && !(mb.mb_perm & MB_DELE)){
555 n_err(_("May not execute `%s' -- message file is read only\n"),
556 cmd->lc_name);
557 goto jleave;
559 if((cmd->lc_argtype & ARG_A) && mb.mb_type == MB_VOID){
560 n_err(_("Cannot execute `%s' without active mailbox\n"), cmd->lc_name);
561 goto jleave;
564 if(cmd->lc_argtype & ARG_O)
565 OBSOLETE2(_("this command will be removed"), cmd->lc_name);
566 if(cmd->lc_argtype & ARG_V)
567 temporary_arg_v_store = NULL;
569 switch(cmd->lc_argtype & ARG_ARGMASK){
570 case ARG_MSGLIST:
571 /* Message list defaulting to nearest forward legal message */
572 if(n_msgvec == NULL)
573 goto je96;
574 if((c = getmsglist(cp, n_msgvec, cmd->lc_msgflag)) < 0)
575 break;
576 if(c == 0){
577 if((n_msgvec[0] = first(cmd->lc_msgflag, cmd->lc_msgmask)) != 0)
578 n_msgvec[1] = 0;
580 if(n_msgvec[0] == 0){
581 if(!(pstate & PS_HOOK_MASK))
582 printf(_("No applicable messages\n"));
583 break;
585 e = (*cmd->lc_func)(n_msgvec);
586 break;
588 case ARG_NDMLIST:
589 /* Message list with no defaults, but no error if none exist */
590 if(n_msgvec == NULL){
591 je96:
592 n_err(_("Invalid use of \"message list\"\n"));
593 break;
595 if((c = getmsglist(cp, n_msgvec, cmd->lc_msgflag)) < 0)
596 break;
597 e = (*cmd->lc_func)(n_msgvec);
598 break;
600 case ARG_STRLIST:
601 /* Just the straight string, with leading blanks removed */
602 while(whitechar(*cp))
603 ++cp;
604 e = (*cmd->lc_func)(cp);
605 break;
607 case ARG_RAWLIST:
608 case ARG_ECHOLIST:
609 /* A vector of strings, in shell style */
610 if((c = getrawlist(cp, line.l, arglist, NELEM(arglist),
611 ((cmd->lc_argtype & ARG_ARGMASK) == ARG_ECHOLIST))) < 0)
612 break;
613 if(c < cmd->lc_minargs){
614 n_err(_("`%s' requires at least %d arg(s)\n"),
615 cmd->lc_name, cmd->lc_minargs);
616 break;
618 #undef lc_minargs
619 if(c > cmd->lc_maxargs){
620 n_err(_("`%s' takes no more than %d arg(s)\n"),
621 cmd->lc_name, cmd->lc_maxargs);
622 break;
624 #undef lc_maxargs
625 e = (*cmd->lc_func)(arglist);
626 break;
628 case ARG_NOLIST:
629 /* Just the constant zero, for exiting, eg. */
630 e = (*cmd->lc_func)(0);
631 break;
633 default:
634 DBG( n_panic(_("Implementation error: unknown argument type")); )
635 goto jleave0;
638 if(e == 0 && (cmd->lc_argtype & ARG_V) &&
639 (cp = temporary_arg_v_store) != NULL){
640 temporary_arg_v_store = NULL;
641 evp->le_new_content = cp;
642 goto jleave0;
644 if(!(cmd->lc_argtype & ARG_H) && !(pstate & PS_MSGLIST_SAW_NO))
645 evp->le_add_history = TRU1;
647 jleave:
648 /* Exit the current source file on error TODO what a mess! */
649 if(e == 0)
650 pstate &= ~PS_EVAL_ERROR;
651 else{
652 pstate |= PS_EVAL_ERROR;
653 if(e < 0 || (pstate & PS_ROBOT)){ /* FIXME */
654 e = 1;
655 goto jret;
657 goto jret0;
660 if(cmd == NULL)
661 goto jret0;
662 if((cmd->lc_argtype & ARG_P) && ok_blook(autoprint))
663 if(visible(dot)){
664 line.s = savestr("type");
665 line.l = sizeof("type") -1;
666 gp = (struct a_lex_ghost*)-1; /* Avoid `ghost' interpretation */
667 goto jrestart;
670 if(!(pstate & (PS_SOURCING | PS_HOOK_MASK)) && !(cmd->lc_argtype & ARG_T))
671 pstate |= PS_SAW_COMMAND;
672 jleave0:
673 pstate &= ~PS_EVAL_ERROR;
674 jret0:
675 e = 0;
676 jret:
678 fprintf(stderr, "a_lex_evaluate returns %d for <%s>\n",e,line.s);
680 NYD_LEAVE;
681 return e;
684 static struct a_lex_cmd const *
685 a_lex__firstfit(char const *comm){ /* TODO *hashtable*! linear list search!!! */
686 struct a_lex_cmd const *cp;
687 NYD2_ENTER;
689 for(cp = a_lex_cmd_tab; PTRCMP(cp, <, &a_lex_cmd_tab[NELEM(a_lex_cmd_tab)]);
690 ++cp)
691 if(*comm == *cp->lc_name && is_prefix(comm, cp->lc_name))
692 goto jleave;
693 cp = NULL;
694 jleave:
695 NYD2_LEAVE;
696 return cp;
699 static void
700 a_lex_stop(int s){
701 sighandler_type old_action;
702 sigset_t nset;
703 NYD_X; /* Signal handler */
705 old_action = safe_signal(s, SIG_DFL);
707 sigemptyset(&nset);
708 sigaddset(&nset, s);
709 sigprocmask(SIG_UNBLOCK, &nset, NULL);
710 n_raise(s);
711 sigprocmask(SIG_BLOCK, &nset, NULL);
713 safe_signal(s, old_action);
715 if(a_lex_reset_on_stop){
716 a_lex_reset_on_stop = 0;
717 n_TERMCAP_RESUME(TRU1);
718 siglongjmp(srbuf, 0); /* FIXME get rid */
722 static void
723 a_lex_hangup(int s){
724 NYD_X; /* Signal handler */
725 UNUSED(s);
726 /* nothing to do? */
727 exit(EXIT_ERR);
730 static void
731 a_lex_onintr(int s){
732 NYD_X; /* Signal handler */
734 safe_signal(SIGINT, a_lex_onintr);
735 noreset = 0;
736 a_lex_unstack(TRUM1);
738 termios_state_reset();
739 close_all_files(); /* FIXME .. to outer level ONLU! */
741 if(image >= 0){
742 close(image);
743 image = -1;
745 if(interrupts != 1)
746 n_err_sighdl(_("Interrupt\n"));
747 safe_signal(SIGPIPE, a_lex_oldpipe);
748 siglongjmp(srbuf, 0); /* FIXME get rid */
751 static void
752 a_lex_unstack(bool_t eval_error){
753 struct a_lex_input_stack *lip;
754 NYD_ENTER;
756 if((lip = a_lex_input) == NULL){
757 /* If called from a_lex_onintr(), be silent FIXME */
758 pstate &= ~(PS_SOURCING | PS_ROBOT);
759 if(eval_error == TRUM1 || !(pstate & PS_STARTED))
760 goto jleave;
761 goto jerr;
764 if(lip->li_flags & a_LEX_MACRO){
765 if(lip->li_flags & a_LEX_MACRO_FREE_DATA){
766 char **lp;
768 while(*(lp = &lip->li_lines[lip->li_loff]) != NULL){
769 free(*lp);
770 ++lip->li_loff;
772 /* Part of lip's memory chunk, then */
773 if(!(lip->li_flags & a_LEX_MACRO_CMD))
774 free(lip->li_lines);
776 }else{
777 if(lip->li_flags & a_LEX_PIPE)
778 /* XXX command manager should -TERM then -KILL instead of hoping
779 * XXX for exit of provider due to EPIPE / SIGPIPE */
780 Pclose(lip->li_file, TRU1);
781 else
782 Fclose(lip->li_file);
785 if(!condstack_take(lip->li_cond)){
786 n_err(_("Unmatched `if' at end of %s \"%s\"\n"),
787 ((lip->li_flags & a_LEX_MACRO
788 ? (lip->li_flags & a_LEX_MACRO_CMD ? _("command") : _("macro"))
789 : _("`source'd file"))),
790 lip->li_name);
791 eval_error = TRU1;
794 if((a_lex_input = lip->li_outer) == NULL){
795 pstate &= ~(PS_SOURCING | PS_ROBOT);
796 }else{
797 if((a_lex_input->li_flags & (a_LEX_MACRO | a_LEX_SUPER_MACRO)) ==
798 (a_LEX_MACRO | a_LEX_SUPER_MACRO))
799 pstate &= ~PS_SOURCING;
800 assert(pstate & PS_ROBOT);
803 if(eval_error)
804 goto jerr;
805 if(lip->li_flags & a_LEX_FREE)
806 free(lip);
807 jleave:
808 if(UNLIKELY(a_lex_input != NULL && eval_error == TRUM1))
809 a_lex_unstack(TRUM1);
810 NYD_LEAVE;
811 return;
813 jerr:
814 if(lip != NULL){
815 if(options & OPT_D_V)
816 n_alert(_("Stopped %s \"%s\" due to errors%s"),
817 (pstate & PS_STARTED
818 ? (lip->li_flags & a_LEX_MACRO
819 ? (lip->li_flags & a_LEX_MACRO_CMD
820 ? _("evaluating command") : _("evaluating macro"))
821 : (lip->li_flags & a_LEX_PIPE
822 ? _("executing `source'd pipe")
823 : _("loading `source'd file")))
824 : (lip->li_flags & a_LEX_MACRO
825 ? (lip->li_flags & a_LEX_MACRO_X_OPTION
826 ? _("evaluating command line") : _("evaluating macro"))
827 : _("loading initialization resource"))),
828 lip->li_name,
829 (options & OPT_DEBUG ? "" : _(" (enable *debug* for trace)")));
830 if(lip->li_flags & a_LEX_FREE)
831 free(lip);
834 if(!(options & OPT_INTERACTIVE) && !(pstate & PS_STARTED)){
835 if(options & OPT_D_V)
836 n_alert(_("Non-interactive, bailing out due to errors "
837 "in startup load phase"));
838 exit(EXIT_ERR);
840 goto jleave;
843 static bool_t
844 a_lex_source_file(char const *file, bool_t silent_error){
845 struct a_lex_input_stack *lip;
846 size_t nlen;
847 char *nbuf;
848 bool_t ispipe;
849 FILE *fip;
850 NYD_ENTER;
852 fip = NULL;
854 /* Being a command argument file is space-trimmed */
855 if((ispipe = (!silent_error && (nlen = strlen(file)) > 0 &&
856 file[--nlen] == '|'))){
857 if((fip = Popen(nbuf = savestrbuf(file, nlen), "r",
858 ok_vlook(SHELL), NULL, COMMAND_FD_NULL)) == NULL){
859 if(!silent_error || (options & OPT_D_V))
860 n_perr(nbuf, 0);
861 goto jleave;
863 }else if((nbuf = fexpand(file, FEXP_LOCAL)) == NULL)
864 goto jleave;
865 else if((fip = Fopen(nbuf, "r")) == NULL){
866 if(!silent_error || (options & OPT_D_V))
867 n_perr(nbuf, 0);
868 goto jleave;
871 lip = smalloc(sizeof(*lip) -
872 VFIELD_SIZEOF(struct a_lex_input_stack, li_name) +
873 (nlen = strlen(nbuf) +1));
874 lip->li_outer = a_lex_input;
875 lip->li_file = fip;
876 lip->li_cond = condstack_release();
877 lip->li_flags = (ispipe ? a_LEX_PIPE : a_LEX_NONE) |
878 (a_lex_input != NULL && (a_lex_input->li_flags & a_LEX_SUPER_MACRO)
879 ? a_LEX_SUPER_MACRO : 0);
880 memcpy(lip->li_name, nbuf, nlen);
882 pstate |= PS_SOURCING | PS_ROBOT;
883 a_lex_input = lip;
884 a_commands_recursive();
885 /* FIXME return TRUM1 if file can't be opened, FAL0 on eval error */
886 jleave:
887 NYD_LEAVE;
888 return silent_error ? TRU1 : (fip != NULL);
891 static bool_t
892 a_lex_load(struct a_lex_input_stack *lip){
893 bool_t rv;
894 NYD2_ENTER;
896 assert(!(pstate & PS_STARTED));
897 assert(a_lex_input == NULL);
899 /* POSIX:
900 * Any errors in the start-up file shall either cause mailx to terminate
901 * with a diagnostic message and a non-zero status or to continue after
902 * writing a diagnostic message, ignoring the remainder of the lines in
903 * the start-up file. */
904 lip->li_cond = condstack_release();
906 /* FIXME won't work for now (PS_ROBOT needs PS_SOURCING anyway)
907 pstate |= PS_ROBOT |
908 (lip->li_flags & a_LEX_MACRO_X_OPTION ? 0 : PS_SOURCING);
910 pstate |= PS_ROBOT | PS_SOURCING;
911 if(options & OPT_D_V)
912 n_err(_("Loading \"%s\"\n"), lip->li_name);
913 a_lex_input = lip;
914 if(!(rv = n_commands())){
915 if(!(options & OPT_INTERACTIVE)){
916 if(options & OPT_D_V)
917 n_alert(_("Non-interactive program mode, forced exit"));
918 exit(EXIT_ERR);
921 /* PS_EXIT handled by callers */
922 NYD2_LEAVE;
923 return rv;
926 static bool_t
927 a_commands_recursive(void){
928 struct a_lex_eval_ctx ev;
929 bool_t rv;
930 NYD2_ENTER;
932 memset(&ev, 0, sizeof ev);
934 /* FIXME sigkondom */
935 n_COLOUR( n_colour_env_push(); )
936 rv = TRU1;
937 for(;;){
938 int n;
940 /* Read a line of commands and handle end of file specially */
941 ev.le_line.l = ev.le_line_size;
942 n = n_lex_input(NULL, TRU1, &ev.le_line.s, &ev.le_line.l,
943 ev.le_new_content);
944 ev.le_line_size = (ui32_t)ev.le_line.l;
945 ev.le_line.l = (ui32_t)n;
947 if(n < 0)
948 break;
950 if(a_lex_evaluate(&ev)){
951 rv = FAL0;
952 break;
955 if((options & OPT_BATCH_FLAG) && ok_blook(batch_exit_on_error)){
956 if(exit_status != EXIT_OK)
957 break;
960 a_lex_unstack(!rv);
961 n_COLOUR( n_colour_env_pop(FAL0); )
963 if(ev.le_line.s != NULL)
964 free(ev.le_line.s);
965 NYD2_LEAVE;
966 return rv;
969 #ifdef HAVE_DOCSTRINGS
970 FL bool_t
971 n_print_comm_docstr(char const *comm){
972 struct a_lex_ghost const *gp;
973 struct a_lex_cmd const *cp, *cpmax;
974 bool_t rv = FAL0;
975 NYD_ENTER;
977 /* Ghosts take precedence */
978 for(gp = a_lex_ghosts; gp != NULL; gp = gp->lg_next)
979 if(!strcmp(comm, gp->lg_name)){
980 printf("%s -> ", comm);
981 comm = gp->lg_cmd.s;
982 break;
985 cpmax = &(cp = a_lex_cmd_tab)[NELEM(a_lex_cmd_tab)];
986 jredo:
987 for(; PTRCMP(cp, <, cpmax); ++cp){
988 if(!strcmp(comm, cp->lc_name))
989 printf("%s: %s\n", comm, V_(cp->lc_doc));
990 else if(is_prefix(comm, cp->lc_name))
991 printf("%s (%s): %s\n", comm, cp->lc_name, V_(cp->lc_doc));
992 else
993 continue;
994 rv = TRU1;
995 break;
997 if(!rv && PTRCMP(cpmax, ==, &a_lex_cmd_tab[NELEM(a_lex_cmd_tab)])){
998 cpmax = &(cp = a_lex_special_cmd_tab)[NELEM(a_lex_special_cmd_tab)];
999 goto jredo;
1002 if(!rv && gp != NULL){
1003 printf("\"%s\"\n", comm);
1004 rv = TRU1;
1006 NYD_LEAVE;
1007 return rv;
1009 #endif /* HAVE_DOCSTRINGS */
1011 FL bool_t
1012 n_commands(void){ /* FIXME */
1013 struct a_lex_eval_ctx ev;
1014 int n;
1015 bool_t volatile rv = TRU1;
1016 NYD_ENTER;
1018 if (!(pstate & PS_SOURCING)) {
1019 if (safe_signal(SIGINT, SIG_IGN) != SIG_IGN)
1020 safe_signal(SIGINT, &a_lex_onintr);
1021 if (safe_signal(SIGHUP, SIG_IGN) != SIG_IGN)
1022 safe_signal(SIGHUP, &a_lex_hangup);
1023 /* TODO We do a lot of redundant signal handling, especially
1024 * TODO with the command line editor(s); try to merge this */
1025 safe_signal(SIGTSTP, &a_lex_stop);
1026 safe_signal(SIGTTOU, &a_lex_stop);
1027 safe_signal(SIGTTIN, &a_lex_stop);
1029 a_lex_oldpipe = safe_signal(SIGPIPE, SIG_IGN);
1030 safe_signal(SIGPIPE, a_lex_oldpipe);
1032 memset(&ev, 0, sizeof ev);
1034 (void)sigsetjmp(srbuf, 1); /* FIXME get rid */
1035 for (;;) {
1036 char *temporary_orig_line; /* XXX eval_ctx.le_line not yet constant */
1038 n_COLOUR( n_colour_env_pop(TRU1); )
1040 /* TODO Unless we have our signal manager (or however we do it) child
1041 * TODO processes may have time slots where their execution isn't
1042 * TODO protected by signal handlers (in between start and setup
1043 * TODO completed). close_all_files() is only called from onintr()
1044 * TODO so those may linger possibly forever */
1045 if(!(pstate & PS_SOURCING))
1046 close_all_files();
1048 interrupts = 0;
1050 temporary_localopts_free(); /* XXX intermediate hack */
1051 sreset((pstate & PS_SOURCING) != 0);
1052 if (!(pstate & PS_SOURCING)) {
1053 char *cp;
1055 /* TODO Note: this buffer may contain a password. We should redefine
1056 * TODO the code flow which has to do that */
1057 if ((cp = termios_state.ts_linebuf) != NULL) {
1058 termios_state.ts_linebuf = NULL;
1059 termios_state.ts_linesize = 0;
1060 free(cp); /* TODO pool give-back */
1062 /* TODO Due to expand-on-tab of NCL the buffer may grow */
1063 if (ev.le_line.l > LINESIZE * 3) {
1064 free(ev.le_line.s); /* TODO pool! but what? */
1065 ev.le_line.s = NULL;
1066 ev.le_line.l = ev.le_line_size = 0;
1070 if (!(pstate & PS_SOURCING) && (options & OPT_INTERACTIVE)) {
1071 char *cp;
1073 cp = ok_vlook(newmail);
1074 if ((options & OPT_TTYIN) && cp != NULL) {
1075 struct stat st;
1077 /* FIXME TEST WITH NOPOLL ETC. !!! */
1078 n = (cp != NULL && strcmp(cp, "nopoll"));
1079 if ((mb.mb_type == MB_FILE && !stat(mailname, &st) &&
1080 st.st_size > mailsize) ||
1081 (mb.mb_type == MB_MAILDIR && n != 0)) {
1082 size_t odot = PTR2SIZE(dot - message);
1083 ui32_t odid = (pstate & PS_DID_PRINT_DOT);
1085 if (setfile(mailname,
1086 FEDIT_NEWMAIL |
1087 ((mb.mb_perm & MB_DELE) ? 0 : FEDIT_RDONLY)) < 0) {
1088 exit_status |= EXIT_ERR;
1089 rv = FAL0;
1090 break;
1092 dot = message + odot;
1093 pstate |= odid;
1097 a_lex_reset_on_stop = TRU1;
1098 exit_status = EXIT_OK;
1101 /* Read a line of commands and handle end of file specially */
1102 jreadline:
1103 ev.le_line.l = ev.le_line_size;
1104 n = n_lex_input(NULL, TRU1, &ev.le_line.s, &ev.le_line.l,
1105 ev.le_new_content);
1106 ev.le_line_size = (ui32_t)ev.le_line.l;
1107 ev.le_line.l = (ui32_t)n;
1108 a_lex_reset_on_stop = FAL0;
1110 if (n < 0) {
1111 /* FIXME did unstack() when PS_SOURCING, only break with PS_LOADING*/
1112 if (!(pstate & PS_ROBOT) &&
1113 (options & OPT_INTERACTIVE) && ok_blook(ignoreeof)) {
1114 printf(_("*ignoreeof* set, use `quit' to quit.\n"));
1115 n_msleep(500, FAL0);
1116 continue;
1118 break;
1121 temporary_orig_line = ((pstate & PS_SOURCING) ||
1122 !(options & OPT_INTERACTIVE)) ? NULL
1123 : savestrbuf(ev.le_line.s, ev.le_line.l);
1124 pstate &= ~PS_HOOK_MASK;
1125 if (a_lex_evaluate(&ev)) {
1126 if (!(pstate & PS_STARTED)) /* TODO mess; join PS_EVAL_ERROR.. */
1127 rv = FAL0;
1128 break;
1131 if ((options & OPT_BATCH_FLAG) && ok_blook(batch_exit_on_error)) {
1132 if (exit_status != EXIT_OK)
1133 break;
1134 if ((pstate & (PS_SOURCING | PS_EVAL_ERROR)) == PS_EVAL_ERROR) {
1135 exit_status = EXIT_ERR;
1136 break;
1139 if (!(pstate & PS_SOURCING) && (options & OPT_INTERACTIVE)) {
1140 if (ev.le_new_content != NULL)
1141 goto jreadline;
1142 /* *Can* happen since _evaluate() n_unstack()s on error! XXX no more */
1143 if (temporary_orig_line != NULL)
1144 n_tty_addhist(temporary_orig_line, !ev.le_add_history);
1147 if(pstate & PS_EXIT)
1148 break;
1150 a_lex_unstack(!rv);
1152 if (ev.le_line.s != NULL)
1153 free(ev.le_line.s);
1154 if (pstate & PS_SOURCING)
1155 sreset(FAL0);
1156 NYD_LEAVE;
1157 return rv;
1160 FL int
1161 (n_lex_input)(char const *prompt, bool_t nl_escape, char **linebuf,
1162 size_t *linesize, char const *string SMALLOC_DEBUG_ARGS){
1163 /* TODO readline: linebuf pool! */
1164 bool_t doprompt, dotty;
1165 int n, nold;
1166 FILE *ifile;
1167 NYD2_ENTER;
1169 /* Special case macro mode: never need to prompt, lines have always been
1170 * unfolded already */
1171 if(a_lex_input != NULL && (a_lex_input->li_flags & a_LEX_MACRO)){
1172 char **lp = &a_lex_input->li_lines[a_lex_input->li_loff];
1174 if(*linebuf != NULL)
1175 free(*linebuf);
1177 if((*linebuf = *lp) == NULL){
1178 *linesize = 0;
1179 n = -1;
1180 }else{
1181 ++a_lex_input->li_loff;
1182 n = (int)(*linesize = strlen(*linebuf));
1184 if(!(a_lex_input->li_flags & a_LEX_MACRO_FREE_DATA))
1185 *linebuf = sbufdup(*linebuf, *linesize);
1187 if(options & OPT_D_VV)
1188 n_err(_("%s %d bytes <%.*s>\n"),
1189 (a_lex_input->li_flags & a_LEX_MACRO_X_OPTION
1190 ? _("-X option") : _("MACRO")),
1191 n, n, *linebuf);
1193 goto jleave;
1196 doprompt = ((pstate & (PS_STARTED | PS_ROBOT)) == PS_STARTED &&
1197 (options & OPT_INTERACTIVE));
1198 dotty = (doprompt && !ok_blook(line_editor_disable));
1199 if(!doprompt)
1200 prompt = NULL;
1201 else if(prompt == NULL)
1202 prompt = getprompt();
1204 /* Ensure stdout is flushed first anyway */
1205 if(!dotty && prompt == NULL)
1206 fflush(stdout);
1208 ifile = (a_lex_input != NULL) ? a_lex_input->li_file : stdin;
1209 assert(ifile != NULL);
1211 for(nold = n = 0;;){
1212 if(dotty){
1213 assert(ifile == stdin);
1214 if(string != NULL && (n = (int)strlen(string)) > 0){
1215 if(*linesize > 0)
1216 *linesize += n +1;
1217 else
1218 *linesize = (size_t)n + LINESIZE +1;
1219 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
1220 memcpy(*linebuf, string, (size_t)n +1);
1222 string = NULL;
1223 /* TODO if nold>0, don't redisplay the entire line!
1224 * TODO needs complete redesign ... */
1225 n = (n_tty_readline)(prompt, linebuf, linesize, n
1226 SMALLOC_DEBUG_ARGSCALL);
1227 }else{
1228 if(prompt != NULL) {
1229 if(*prompt != '\0')
1230 fputs(prompt, stdout);
1231 fflush(stdout);
1234 n = (readline_restart)(ifile, linebuf, linesize, n
1235 SMALLOC_DEBUG_ARGSCALL);
1237 if(n > 0 && nold > 0){
1238 int i = 0;
1239 char const *cp = *linebuf + nold;
1241 while(blankspacechar(*cp) && nold + i < n)
1242 ++cp, ++i;
1243 if(i > 0){
1244 memmove(*linebuf + nold, cp, n - nold - i);
1245 n -= i;
1246 (*linebuf)[n] = '\0';
1251 if(n <= 0)
1252 break;
1254 /* POSIX says:
1255 * An unquoted <backslash> at the end of a command line shall
1256 * be discarded and the next line shall continue the command */
1257 if(!nl_escape || (*linebuf)[n - 1] != '\\')
1258 break;
1259 /* Definitely outside of quotes, thus the quoting rules are so that an
1260 * uneven number of successive backslashs at EOL is a continuation */
1261 if(n > 1){
1262 size_t i, j;
1264 for(j = 1, i = (size_t)n - 1; i-- > 0; ++j)
1265 if((*linebuf)[i] != '\\')
1266 break;
1267 if(!(j & 1))
1268 break;
1270 (*linebuf)[nold = --n] = '\0';
1271 if(prompt != NULL && *prompt != '\0')
1272 prompt = ".. "; /* XXX PS2 .. */
1275 if(n >= 0){
1276 (*linebuf)[*linesize = n] = '\0';
1278 if(options & OPT_D_VV)
1279 n_err(_("%s %d bytes <%.*s>\n"),
1280 (!(pstate & PS_STARTED) ? "LOAD"
1281 : (pstate & PS_SOURCING ? "SOURCE" : "READ")),
1282 n, n, *linebuf);
1284 jleave:
1285 if (pstate & PS_PSTATE_PENDMASK)
1286 a_lex_update_pstate();
1288 NYD2_LEAVE;
1289 return n;
1292 FL char *
1293 n_lex_input_cp_addhist(char const *prompt, char const *string, bool_t isgabby){
1294 /* FIXME n_lex_input_cp_addhist(): leaks on sigjmp without linepool */
1295 size_t linesize;
1296 char *linebuf, *rv;
1297 int n;
1298 NYD2_ENTER;
1300 linesize = 0;
1301 linebuf = NULL;
1302 rv = NULL;
1304 n = n_lex_input(prompt, TRU1, &linebuf, &linesize, string);
1305 if(n > 0 && *(rv = savestrbuf(linebuf, (size_t)n)) != '\0' &&
1306 (options & OPT_INTERACTIVE))
1307 n_tty_addhist(rv, isgabby);
1309 if(linebuf != NULL)
1310 free(linebuf);
1311 NYD2_LEAVE;
1312 return rv;
1315 FL void
1316 n_load(char const *name){
1317 struct a_lex_input_stack *lip;
1318 size_t i;
1319 FILE *fip;
1320 NYD_ENTER;
1322 if(name == NULL || *name == '\0' || (fip = Fopen(name, "r")) == NULL)
1323 goto jleave;
1325 i = strlen(name) +1;
1326 lip = scalloc(1, sizeof(*lip) -
1327 VFIELD_SIZEOF(struct a_lex_input_stack, li_name) + i);
1328 lip->li_file = fip;
1329 lip->li_flags = a_LEX_FREE;
1330 memcpy(lip->li_name, name, i);
1332 a_lex_load(lip);
1333 pstate &= ~PS_EXIT;
1334 jleave:
1335 NYD_LEAVE;
1338 FL void
1339 n_load_Xargs(char const **lines){
1340 static char const name[] = "-X";
1342 ui8_t buf[sizeof(struct a_lex_input_stack) + sizeof name];
1343 struct a_lex_input_stack *lip;
1344 NYD_ENTER;
1346 memset(buf, 0, sizeof buf);
1347 lip = (void*)buf;
1348 lip->li_flags = a_LEX_MACRO | a_LEX_MACRO_X_OPTION | a_LEX_SUPER_MACRO;
1349 lip->li_lines = UNCONST(lines);
1350 memcpy(lip->li_name, name, sizeof name);
1352 a_lex_load(lip);
1353 if(pstate & PS_EXIT)
1354 exit(EXIT_OK);
1355 NYD_LEAVE;
1358 FL int
1359 c_source(void *v){
1360 int rv;
1361 NYD_ENTER;
1363 rv = (a_lex_source_file(*(char**)v, FAL0) == TRU1) ? 0 : 1;
1364 NYD_LEAVE;
1365 return rv;
1368 FL int
1369 c_source_if(void *v){ /* XXX obsolete?, support file tests in `if' etc.! */
1370 int rv;
1371 NYD_ENTER;
1373 rv = (a_lex_source_file(*(char**)v, TRU1) != FAL0) ? 0 : 1;
1374 NYD_LEAVE;
1375 return rv;
1378 FL bool_t
1379 n_source_macro(char const *name, char **lines){
1380 struct a_lex_input_stack *lip;
1381 size_t i;
1382 int rv;
1383 NYD_ENTER;
1385 lip = smalloc(sizeof(*lip) -
1386 VFIELD_SIZEOF(struct a_lex_input_stack, li_name) +
1387 (i = strlen(name) +1));
1388 lip->li_outer = a_lex_input;
1389 lip->li_file = NULL;
1390 lip->li_cond = condstack_release();
1391 lip->li_flags = a_LEX_FREE | a_LEX_MACRO | a_LEX_MACRO_FREE_DATA |
1392 (a_lex_input == NULL || (a_lex_input->li_flags & a_LEX_SUPER_MACRO)
1393 ? a_LEX_SUPER_MACRO : 0);
1394 lip->li_loff = 0;
1395 lip->li_lines = lines;
1396 memcpy(lip->li_name, name, i);
1398 pstate |= PS_ROBOT;
1399 a_lex_input = lip;
1400 rv = a_commands_recursive();
1401 NYD_LEAVE;
1402 return rv;
1405 FL bool_t
1406 n_source_command(char const *cmd){
1407 struct a_lex_input_stack *lip;
1408 size_t i, ial;
1409 bool_t rv;
1410 NYD_ENTER;
1412 i = strlen(cmd);
1413 cmd = sbufdup(cmd, i++);
1414 ial = n_ALIGN(i);
1416 lip = smalloc(sizeof(*lip) -
1417 VFIELD_SIZEOF(struct a_lex_input_stack, li_name) +
1418 ial + 2*sizeof(char*));
1419 lip->li_outer = a_lex_input;
1420 lip->li_file = NULL;
1421 lip->li_cond = condstack_release();
1422 lip->li_flags = a_LEX_FREE | a_LEX_MACRO | a_LEX_MACRO_FREE_DATA |
1423 a_LEX_MACRO_CMD |
1424 (a_lex_input == NULL || (a_lex_input->li_flags & a_LEX_SUPER_MACRO)
1425 ? a_LEX_SUPER_MACRO : 0);
1426 lip->li_loff = 0;
1427 lip->li_lines = (void*)(lip->li_name + ial);
1428 lip->li_lines[0] = UNCONST(cmd); /* dup'ed above */
1429 lip->li_lines[1] = NULL;
1430 memcpy(lip->li_name, cmd, i);
1432 pstate |= PS_ROBOT;
1433 a_lex_input = lip;
1434 rv = a_commands_recursive();
1435 NYD_LEAVE;
1436 return rv;
1439 FL bool_t
1440 n_source_may_yield_control(void){
1441 return ((options & OPT_INTERACTIVE) &&
1442 (pstate & PS_STARTED) &&
1443 (!(pstate & PS_ROBOT) || (pstate & PS_RECURSED)) && /* Ok for ~: */
1444 (a_lex_input == NULL || a_lex_input->li_outer == NULL));
1447 /* s-it-mode */