1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Program input of all sorts, input lexing, event loops, command evaluation.
3 *@ TODO n_PS_ROBOT requires yet n_PS_SOURCING, which REALLY sucks.
5 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
6 * Copyright (c) 2012 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 #ifndef HAVE_AMALGAMATION
45 a_GO_FREE
= 1u<<0, /* Structure was allocated, n_free() it */
46 a_GO_PIPE
= 1u<<1, /* Open on a pipe */
47 a_GO_FILE
= 1u<<2, /* Loading or sourcing a file */
48 a_GO_MACRO
= 1u<<3, /* Running a macro */
49 a_GO_MACRO_FREE_DATA
= 1u<<4, /* Lines are allocated, n_free() once done */
50 /* TODO For simplicity this is yet _MACRO plus specialization overlay
51 * TODO (_X_OPTION, _CMD) -- these should be types on their own! */
52 a_GO_MACRO_X_OPTION
= 1u<<5, /* Macro indeed command line -X option */
53 a_GO_MACRO_CMD
= 1u<<6, /* Macro indeed single-line: ~:COMMAND */
54 /* TODO a_GO_SPLICE: the right way to support *on-compose-splice(-shell)?*
55 * TODO would be a command_loop object that emits an on_read_line event, and
56 * TODO have a special handler for the compose mode; with that, then,
57 * TODO _event_loop() would not call _evaluate() but CTX->on_read_line,
58 * TODO and _evaluate() would be the standard impl.,
59 * TODO whereas the COMMAND ESCAPE switch in collect.c would be another one.
60 * TODO With this generic accmacvar.c:temporary_compose_mode_hook_call()
61 * TODO could be dropped, and n_go_macro() could become extended,
62 * TODO and/or we would add a n_go_anything(), which would allow special
63 * TODO input handlers, special I/O input and output, special `localopts'
64 * TODO etc., to be glued to the new execution context. And all I/O all
65 * TODO over this software should not use stdin/stdout, but CTX->in/out.
66 * TODO The pstate must be a property of the current execution context, too.
67 * TODO This not today. :( For now we invent a special SPLICE execution
68 * TODO context overlay that at least allows to temporarily modify the
69 * TODO global pstate, and the global stdin and stdout pointers. HACK!
70 * TODO This splice thing is very special and has to go again. HACK!!
71 * TODO a_go_input() will drop it once it sees EOF (HACK!), but care for
72 * TODO jumps must be taken by splice creators. HACK!!! But works. ;} */
74 /* If it is none of those, it must be the outermost, the global one */
75 a_GO_TYPE_MASK
= a_GO_PIPE
| a_GO_FILE
| a_GO_MACRO
|
76 /* a_GO_MACRO_X_OPTION | a_GO_MACRO_CMD | */ a_GO_SPLICE
,
78 a_GO_FORCE_EOF
= 1u<<8, /* go_input() shall return EOF next */
81 a_GO_SUPER_MACRO
= 1u<<16, /* *Not* inheriting n_PS_SOURCING state */
82 /* This context has inherited the memory pool from its parent.
83 * In practice only used for resource file loading and -X args, which enter
84 * a top level n_go_main_loop() and should (re)use the in practice already
85 * allocated memory pool of the global context */
86 a_GO_MEMPOOL_INHERITED
= 1u<<17,
88 /* `xcall' optimization barrier: n_go_macro() has been finished with
89 * a `xcall' request, and `xcall' set this in the parent a_go_input of the
90 * said n_go_macro() to indicate a barrier: we teardown the a_go_input of
91 * the n_go_macro() away after leaving its _event_loop(), but then,
92 * back in n_go_macro(), that enters a for(;;) loop that directly calls
93 * c_call() -- our `xcall' stack avoidance optimization --, yet this call
94 * will itself end up in a new n_go_macro(), and if that again ends up with
95 * `xcall' this should teardown and leave its own n_go_macro(), unrolling the
96 * stack "up to the barrier level", but which effectively still is the
97 * n_go_macro() that lost its a_go_input and is looping the `xcall'
98 * optimization loop. If no `xcall' is desired that loop is simply left and
99 * the _event_loop() of the outer a_go_ctx will perform a loop tick and
100 * clear this bit again OR become teardown itself */
101 a_GO_XCALL_LOOP
= 1u<<24, /* `xcall' optimization barrier level */
102 a_GO_XCALL_LOOP_ERROR
= 1u<<25, /* .. state machine error transporter */
103 a_GO_XCALL_LOOP_MASK
= a_GO_XCALL_LOOP
| a_GO_XCALL_LOOP_ERROR
106 enum a_go_cleanup_mode
{
107 a_GO_CLEANUP_UNWIND
= 1u<<0, /* Teardown all contexts except outermost */
108 a_GO_CLEANUP_TEARDOWN
= 1u<<1, /* Teardown current context */
109 a_GO_CLEANUP_LOOPTICK
= 1u<<2, /* Normal looptick cleanup */
110 a_GO_CLEANUP_MODE_MASK
= n_BITENUM_MASK(0, 2),
112 a_GO_CLEANUP_ERROR
= 1u<<8, /* Error occurred on level */
113 a_GO_CLEANUP_SIGINT
= 1u<<9, /* Interrupt signal received */
114 a_GO_CLEANUP_HOLDALLSIGS
= 1u<<10 /* hold_all_sigs() active TODO */
117 enum a_go_hist_flags
{
119 a_GO_HIST_ADD
= 1u<<0,
120 a_GO_HIST_GABBY
= 1u<<1,
121 a_GO_HIST_INIT
= 1u<<2
124 struct a_go_eval_ctx
{
125 struct str gec_line
; /* The terminated data to _evaluate() */
126 ui32_t gec_line_size
; /* May be used to store line memory size */
127 bool_t gec_ever_seen
; /* Has ever been used (main_loop() only) */
129 ui8_t gec_hist_flags
; /* enum a_go_hist_flags */
130 char const *gec_hist_cmd
; /* If a_GO_HIST_ADD only, cmd and args */
131 char const *gec_hist_args
;
134 struct a_go_input_inject
{
135 struct a_go_input_inject
*gii_next
;
138 bool_t gii_no_history
;
139 char gii_dat
[n_VFIELD_SIZE(6)];
143 struct a_go_ctx
*gc_outer
;
144 sigset_t gc_osigmask
;
145 ui32_t gc_flags
; /* enum a_go_flags */
146 ui32_t gc_loff
; /* Pseudo (macro): index in .gc_lines */
147 char **gc_lines
; /* Pseudo content, lines unfolded */
148 FILE *gc_file
; /* File we were in, if applicable */
149 struct a_go_input_inject
*gc_inject
; /* To be consumed first */
150 void (*gc_on_finalize
)(void *);
151 void *gc_finalize_arg
;
152 sigjmp_buf gc_eloop_jmp
; /* TODO one day... for _event_loop() */
153 /* SPLICE hacks: saved stdin/stdout, saved pstate */
154 FILE *gc_splice_stdin
;
155 FILE *gc_splice_stdout
;
156 ui32_t gc_splice_psonce
;
157 ui8_t gc_splice__dummy
[4];
158 struct n_go_data_ctx gc_data
;
159 char gc_name
[n_VFIELD_SIZE(0)]; /* Name of file or macro */
162 static sighandler_type a_go_oldpipe
;
163 /* a_go_cmd_tab[] after fun protos */
165 /* Our current execution context, and the buffer backing the outermost level */
166 static struct a_go_ctx
*a_go_ctx
;
168 #define a_GO_MAINCTX_NAME "Main event loop"
171 char uf
[n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
) +
172 sizeof(a_GO_MAINCTX_NAME
)];
175 /* `xcall' stack-avoidance bypass optimization. This actually is
176 * a n_cmd_arg_save_to_heap() buffer with n_cmd_arg_ctx.cac_indat misused to
177 * point to the a_go_ctx to unroll up to */
178 static void *a_go_xcall
;
180 static sigjmp_buf a_go_srbuf
; /* TODO GET RID */
182 /* n_PS_STATE_PENDMASK requires some actions */
183 static void a_go_update_pstate(void);
185 /* Evaluate a single command */
186 static bool_t
a_go_evaluate(struct a_go_eval_ctx
*gecp
);
188 /* Branch here on hangup signal and simulate "exit" */
189 static void a_go_hangup(int s
);
191 /* The following gets called on receipt of an interrupt */
192 static void a_go_onintr(int s
);
194 /* Cleanup current execution context, update the program state.
195 * If _CLEANUP_ERROR is set then we don't alert and error out if the stack
196 * doesn't exist at all, unless _CLEANUP_HOLDALLSIGS we hold_all_sigs() */
197 static void a_go_cleanup(enum a_go_cleanup_mode gcm
);
199 /* `source' and `source_if' (if silent_open_error: no pipes allowed, then).
200 * Returns FAL0 if file is somehow not usable (unless silent_open_error) or
201 * upon evaluation error, and TRU1 on success */
202 static bool_t
a_go_file(char const *file
, bool_t silent_open_error
);
204 /* System resource file load()ing or -X command line option array traversal */
205 static bool_t
a_go_load(struct a_go_ctx
*gcp
);
207 /* A simplified command loop for recursed state machines */
208 static bool_t
a_go_event_loop(struct a_go_ctx
*gcp
, enum n_go_input_flags gif
);
211 a_go_update_pstate(void){
215 act
= ((n_pstate
& n_PS_SIGWINCH_PEND
) != 0);
216 n_pstate
&= ~n_PS_PSTATE_PENDMASK
;
221 snprintf(buf
, sizeof buf
, "%d", n_scrnwidth
);
222 ok_vset(COLUMNS
, buf
);
223 snprintf(buf
, sizeof buf
, "%d", n_scrnheight
);
230 a_go_evaluate(struct a_go_eval_ctx
*gecp
){
231 /* xxx old style(9), but also old code */
232 /* TODO a_go_evaluate() should be splitted in multiple subfunctions,
233 * TODO `eval' should be a prefix, etc., a Ctx should be passed along */
235 struct n_string s
, *sp
;
236 struct str
const *alias_exp
;
237 char _wordbuf
[2], **arglist_base
/*[n_MAXARGC]*/, **arglist
, *cp
, *word
;
238 char const *alias_name
;
239 struct n_cmd_desc
const *cdp
;
240 si32_t nerrn
, nexn
; /* TODO n_pstate_ex_no -> si64_t! */
244 a_ALIAS_MASK
= n_BITENUM_MASK(0, 2), /* Alias recursion counter bits */
245 a_NOPREFIX
= 1u<<4, /* Modifier prefix not allowed right now */
246 a_NOALIAS
= 1u<<5, /* "No alias!" expansion modifier */
247 /* New modifier prefixes must be reflected in a_go_c_alias()! */
248 a_IGNERR
= 1u<<6, /* ignerr modifier prefix */
249 a_WYSH
= 1u<<7, /* XXX v15+ drop wysh modifier prefix */
250 a_VPUT
= 1u<<8, /* vput modifier prefix */
251 a_MODE_MASK
= n_BITENUM_MASK(5, 8),
252 a_NO_ERRNO
= 1u<<16 /* Don't set n_pstate_err_no */
262 n_UNINIT(alias_exp
, NULL
);
264 arglist_base
= n_autorec_alloc(sizeof(*arglist_base
) * n_MAXARGC
);
265 line
= gecp
->gec_line
; /* TODO const-ify original (buffer)! */
266 assert(line
.s
[line
.l
] == '\0');
268 if(line
.l
> 0 && spacechar(line
.s
[0]))
269 gecp
->gec_hist_flags
= a_GO_HIST_NONE
;
270 else if(gecp
->gec_hist_flags
& a_GO_HIST_ADD
)
271 gecp
->gec_hist_cmd
= gecp
->gec_hist_args
= NULL
;
274 /* Aliases that refer to shell commands or macro expansion restart */
276 if(n_str_trim_ifs(&line
, TRU1
)->l
== 0){
278 gecp
->gec_hist_flags
= a_GO_HIST_NONE
;
281 (cp
= line
.s
)[line
.l
] = '\0';
283 /* No-expansion modifier? */
284 if(!(flags
& a_NOPREFIX
) && *cp
== '\\'){
290 /* Note: adding more special treatments must be reflected in the `help' etc.
291 * output in cmd-tab.c! */
293 /* Ignore null commands (comments) */
295 gecp
->gec_hist_flags
= a_GO_HIST_NONE
;
299 /* Handle ! differently to get the correct lexical conventions */
302 /* Isolate the actual command; since it may not necessarily be
303 * separated from the arguments (as in `p1') we need to duplicate it to
304 * be able to create a NUL terminated version.
305 * We must be aware of several special one letter commands here */
306 else if((cp
= n_UNCONST(n_cmd_isolate(cp
))) == line
.s
&&
307 (*cp
== '|' || *cp
== '?'))
309 c
= (int)PTR2SIZE(cp
- line
.s
);
310 word
= UICMP(z
, c
, <, sizeof _wordbuf
) ? _wordbuf
: n_autorec_alloc(c
+1);
311 memcpy(word
, arglist
[0] = line
.s
, c
);
316 /* It may be a modifier.
317 * Note: adding modifiers must be reflected in commandalias handling code */
318 if(c
== sizeof("ignerr") -1 && !asccasecmp(word
, "ignerr")){
319 flags
|= a_NOPREFIX
| a_IGNERR
;
321 }else if(c
== sizeof("wysh") -1 && !asccasecmp(word
, "wysh")){
322 flags
|= a_NOPREFIX
| a_WYSH
;
324 }else if(c
== sizeof("vput") -1 && !asccasecmp(word
, "vput")){
325 flags
|= a_NOPREFIX
| a_VPUT
;
329 /* We need to trim for a possible history entry, but do it anyway and insert
330 * a space for argument separation in case of alias expansion. Also, do
331 * terminate again because nothing prevents aliases from introducing WS */
332 n_str_trim_ifs(&line
, TRU1
);
333 line
.s
[line
.l
] = '\0';
335 /* Lengthy history entry setup, possibly even redundant. But having
336 * normalized history entries is a good thing, and this is maybe still
337 * cheaper than parsing a StrList of words per se */
338 if((gecp
->gec_hist_flags
& (a_GO_HIST_ADD
| a_GO_HIST_INIT
)
341 sp
= n_string_creat_auto(&s
);
342 sp
= n_string_assign_buf(sp
, line
.s
, line
.l
);
343 gecp
->gec_hist_args
= n_string_cp(sp
);
346 sp
= n_string_creat_auto(&s
);
347 sp
= n_string_reserve(sp
, 32);
349 if(flags
& a_NOALIAS
)
350 sp
= n_string_push_c(sp
, '\\');
352 sp
= n_string_push_buf(sp
, "ignerr ", sizeof("ignerr ") -1);
354 sp
= n_string_push_buf(sp
, "wysh ", sizeof("wysh ") -1);
356 sp
= n_string_push_buf(sp
, "vput ", sizeof("vput ") -1);
357 gecp
->gec_hist_flags
= a_GO_HIST_ADD
| a_GO_HIST_INIT
;
360 /* Look up the command; if not found, bitch.
361 * Normally, a blank command would map to the first command in the
362 * table; while n_PS_SOURCING, however, we ignore blank lines to eliminate
363 * confusion; act just the same for aliases */
366 if((n_pstate
& n_PS_ROBOT
) || !(n_psonce
& n_PSO_INTERACTIVE
) ||
368 gecp
->gec_hist_flags
= a_GO_HIST_NONE
;
371 cdp
= n_cmd_default();
375 if(!(flags
& a_NOALIAS
) && (flags
& a_ALIAS_MASK
) != a_ALIAS_MASK
){
378 expcnt
= (flags
& a_ALIAS_MASK
);
380 flags
= (flags
& ~(a_ALIAS_MASK
| a_NOPREFIX
)) | expcnt
;
382 /* Avoid self-recursion; yes, the user could use \ no-expansion, but.. */
383 if(alias_name
!= NULL
&& !strcmp(word
, alias_name
)){
384 if(n_poption
& n_PO_D_V
)
385 n_err(_("Actively avoiding self-recursion of `commandalias': %s\n"),
387 }else if((alias_name
= n_commandalias_exists(word
, &alias_exp
)) != NULL
){
391 sp
= n_string_push_cp(sp
, word
);
392 gecp
->gec_hist_cmd
= n_string_cp(sp
);
396 /* And join arguments onto alias expansion */
400 line
.s
= n_autorec_alloc(i
+ 1 + line
.l
+1);
401 memcpy(line
.s
, alias_exp
->s
, i
);
404 memcpy(&line
.s
[i
], cp
, line
.l
);
406 line
.s
[i
+= line
.l
] = '\0';
412 if((cdp
= n_cmd_firstfit(word
)) == NULL
){
415 if(!(doskip
= n_cnd_if_isskip()) || (n_poption
& n_PO_D_V
))
416 n_err(_("Unknown command%s: `%s'\n"),
417 (doskip
? _(" (ignored due to `if' condition)") : n_empty
),
419 gecp
->gec_hist_flags
= a_GO_HIST_NONE
;
426 /* See if we should execute the command -- if a conditional we always
427 * execute it, otherwise, check the state of cond */
429 if(!(cdp
->cd_caflags
& n_CMD_ARG_F
) && n_cnd_if_isskip())
433 sp
= n_string_push_cp(sp
, cdp
->cd_name
);
434 gecp
->gec_hist_cmd
= n_string_cp(sp
);
440 /* Process the arguments to the command, depending on the type it expects */
441 if((cdp
->cd_caflags
& n_CMD_ARG_I
) && !(n_psonce
& n_PSO_INTERACTIVE
) &&
442 !(n_poption
& n_PO_BATCH_FLAG
)){
443 n_err(_("May not execute `%s' unless interactive or in batch mode\n"),
447 if(!(cdp
->cd_caflags
& n_CMD_ARG_M
) && (n_psonce
& n_PSO_SENDMODE
)){
448 n_err(_("May not execute `%s' while sending\n"), cdp
->cd_name
);
451 if(cdp
->cd_caflags
& n_CMD_ARG_R
){
452 if(n_pstate
& n_PS_COMPOSE_MODE
){
453 /* TODO n_PS_COMPOSE_MODE: should allow `reply': ~:reply! */
454 n_err(_("Cannot invoke `%s' when in compose mode\n"), cdp
->cd_name
);
457 /* TODO Nothing should prevent n_CMD_ARG_R in conjunction with
458 * TODO n_PS_ROBOT|_SOURCING; see a.._may_yield_control()! */
459 if(n_pstate
& (n_PS_ROBOT
| n_PS_SOURCING
)){
460 n_err(_("Cannot invoke `%s' from a macro or during file inclusion\n"),
465 if((cdp
->cd_caflags
& n_CMD_ARG_S
) && !(n_psonce
& n_PSO_STARTED
)){
466 n_err(_("May not execute `%s' during startup\n"), cdp
->cd_name
);
469 if(!(cdp
->cd_caflags
& n_CMD_ARG_X
) && (n_pstate
& n_PS_COMPOSE_FORKHOOK
)){
470 n_err(_("Cannot invoke `%s' from a hook running in a child process\n"),
475 if((cdp
->cd_caflags
& n_CMD_ARG_A
) && mb
.mb_type
== MB_VOID
){
476 n_err(_("Cannot execute `%s' without active mailbox\n"), cdp
->cd_name
);
479 if((cdp
->cd_caflags
& n_CMD_ARG_W
) && !(mb
.mb_perm
& MB_DELE
)){
480 n_err(_("May not execute `%s' -- message file is read only\n"),
485 if(cdp
->cd_caflags
& n_CMD_ARG_O
)
486 n_OBSOLETE2(_("this command will be removed"), cdp
->cd_name
);
488 /* TODO v15: strip n_PS_ARGLIST_MASK off, just in case the actual command
489 * TODO doesn't use any of those list commands which strip this mask,
490 * TODO and for now we misuse bits for checking relation to history;
491 * TODO argument state should be property of a per-command carrier instead */
492 n_pstate
&= ~n_PS_ARGLIST_MASK
;
494 if((flags
& a_WYSH
) &&
495 (cdp
->cd_caflags
& n_CMD_ARG_TYPE_MASK
) != n_CMD_ARG_TYPE_WYRA
){
496 n_err(_("`wysh' prefix does not affect `%s'\n"), cdp
->cd_name
);
501 if(cdp
->cd_caflags
& n_CMD_ARG_V
){
504 emsg
= line
.s
; /* xxx Cannot pass &char* as char const**, so no cp */
505 arglist
[0] = n_shexp_parse_token_cp((n_SHEXP_PARSE_TRIM_SPACE
|
506 n_SHEXP_PARSE_TRIM_IFSSPACE
| n_SHEXP_PARSE_LOG
|
507 n_SHEXP_PARSE_META_KEEP
), &emsg
);
508 line
.l
-= PTR2SIZE(emsg
- line
.s
);
509 line
.s
= cp
= n_UNCONST(emsg
);
511 emsg
= N_("could not parse input token");
512 else if(!n_shexp_is_valid_varname(arglist
[0]))
513 emsg
= N_("not a valid variable name");
514 else if(!n_var_is_user_writable(arglist
[0]))
515 emsg
= N_("either not a user writable, or a boolean variable");
519 n_err("`%s': vput: %s: %s\n",
520 cdp
->cd_name
, V_(emsg
), n_shexp_quote_cp(arglist
[0], FAL0
));
521 nerrn
= n_ERR_NOTSUP
;
525 n_pstate
|= n_PS_ARGMOD_VPUT
; /* TODO YET useless since stripped later
526 * TODO on in getrawlist() etc., i.e., the argument vector producers,
527 * TODO therefore yet needs to be set again based on flags&a_VPUT! */
529 n_err(_("`vput' prefix does not affect `%s'\n"), cdp
->cd_name
);
534 switch(cdp
->cd_caflags
& n_CMD_ARG_TYPE_MASK
){
535 case n_CMD_ARG_TYPE_MSGLIST
:
536 /* Message list defaulting to nearest forward legal message */
539 if((c
= getmsglist(line
.s
, n_msgvec
, cdp
->cd_msgflag
)) < 0){
545 if((n_msgvec
[0] = first(cdp
->cd_msgflag
, cdp
->cd_msgmask
)) != 0)
548 if(n_msgvec
[0] == 0){
550 if(!(n_pstate
& n_PS_HOOK_MASK
))
551 fprintf(n_stdout
, _("No applicable messages\n"));
556 rv
= (*cdp
->cd_func
)(n_msgvec
);
559 case n_CMD_ARG_TYPE_NDMLIST
:
560 /* Message list with no defaults, but no error if none exist */
563 if((c
= getmsglist(line
.s
, n_msgvec
, cdp
->cd_msgflag
)) < 0){
568 rv
= (*cdp
->cd_func
)(n_msgvec
);
571 case n_CMD_ARG_TYPE_STRING
:
572 /* Just the straight string, old style, with leading blanks removed */
573 for(cp
= line
.s
; spacechar(*cp
);)
575 rv
= (*cdp
->cd_func
)(cp
);
577 case n_CMD_ARG_TYPE_RAWDAT
:
578 /* Just the straight string, placed in argv[] */
581 rv
= (*cdp
->cd_func
)(arglist_base
);
584 case n_CMD_ARG_TYPE_WYSH
:
588 case n_CMD_ARG_TYPE_WYRA
:
589 c
= (flags
& a_WYSH
) ? 1 : 0;
591 case n_CMD_ARG_TYPE_RAWLIST
:
595 if((c
= getrawlist((c
!= 0), arglist
,
596 n_MAXARGC
- PTR2SIZE(arglist
- arglist_base
), line
.s
, line
.l
)) < 0){
597 n_err(_("Invalid argument list\n"));
602 if(c
< cdp
->cd_minargs
){
603 n_err(_("`%s' requires at least %u arg(s)\n"),
604 cdp
->cd_name
, (ui32_t
)cdp
->cd_minargs
);
609 if(c
> cdp
->cd_maxargs
){
610 n_err(_("`%s' takes no more than %u arg(s)\n"),
611 cdp
->cd_name
, (ui32_t
)cdp
->cd_maxargs
);
618 n_pstate
|= n_PS_ARGMOD_VPUT
;
620 rv
= (*cdp
->cd_func
)(arglist_base
);
621 if(a_go_xcall
!= NULL
)
625 case n_CMD_ARG_TYPE_ARG
:{
626 /* TODO The _ARG_TYPE_ARG is preliminary, in the end we should have a
627 * TODO per command-ctx carrier that also has slots for it arguments,
628 * TODO and that should be passed along all the way. No more arglists
630 struct n_cmd_arg_ctx cac
;
632 cac
.cac_desc
= cdp
->cd_cadp
;
633 cac
.cac_indat
= line
.s
;
634 cac
.cac_inlen
= line
.l
;
635 if(!n_cmd_arg_parse(&cac
)){
641 cac
.cac_vput
= *arglist_base
;
642 n_pstate
|= n_PS_ARGMOD_VPUT
;
646 rv
= (*cdp
->cd_func
)(&cac
);
647 if(a_go_xcall
!= NULL
)
652 DBG( n_panic(_("Implementation error: unknown argument type: %d"),
653 cdp
->cd_caflags
& n_CMD_ARG_TYPE_MASK
); )
654 nerrn
= n_ERR_NOTOBACCO
;
659 if(gecp
->gec_hist_flags
& a_GO_HIST_ADD
){
660 if(cdp
->cd_caflags
& n_CMD_ARG_H
)
661 gecp
->gec_hist_flags
= a_GO_HIST_NONE
;
662 else if((cdp
->cd_caflags
& n_CMD_ARG_G
) ||
663 (n_pstate
& n_PS_MSGLIST_GABBY
))
664 gecp
->gec_hist_flags
|= a_GO_HIST_GABBY
;
668 if(!(flags
& a_NO_ERRNO
)){
669 if(cdp
->cd_caflags
& n_CMD_ARG_EM
)
671 else if((nerrn
= n_err_no
) == 0)
675 }else if(cdp
->cd_caflags
& n_CMD_ARG_EM
)
682 if(flags
& a_IGNERR
){
683 n_pstate
&= ~n_PS_ERR_EXIT_MASK
;
684 n_exit_status
= n_EXIT_OK
;
688 if((bo
= ok_blook(batch_exit_on_error
))){
689 n_OBSOLETE(_("please use *errexit*, not *batch-exit-on-error*"));
690 if(!(n_poption
& n_PO_BATCH_FLAG
))
693 if(ok_blook(errexit
) || bo
) /* TODO v15: drop bo */
694 n_pstate
|= n_PS_ERR_QUIT
;
695 else if(ok_blook(posix
)){
696 if(n_psonce
& n_PSO_STARTED
)
698 else if(!(n_psonce
& n_PSO_INTERACTIVE
))
699 n_pstate
|= n_PS_ERR_XIT
;
704 if(n_exit_status
== n_EXIT_OK
)
705 n_exit_status
= n_EXIT_ERR
;
706 if((n_poption
& n_PO_D_V
) &&
707 !(n_psonce
& (n_PSO_INTERACTIVE
| n_PSO_STARTED
)))
708 n_alert(_("Non-interactive, bailing out due to errors "
709 "in startup load phase"));
716 if((cdp
->cd_caflags
& n_CMD_ARG_P
) && ok_blook(autoprint
))
718 n_go_input_inject(n_GO_INPUT_INJECT_COMMIT
, "\\type",
719 sizeof("\\type") -1);
721 if(!(n_pstate
& (n_PS_SOURCING
| n_PS_HOOK_MASK
)) &&
722 !(cdp
->cd_caflags
& n_CMD_ARG_T
))
723 n_pstate
|= n_PS_SAW_COMMAND
;
727 if(!(flags
& a_NO_ERRNO
))
728 n_pstate_err_no
= nerrn
;
729 n_pstate_ex_no
= nexn
;
736 NYD_X
; /* Signal handler */
743 a_go_onintr(int s
){ /* TODO block signals while acting */
744 NYD_X
; /* Signal handler */
747 safe_signal(SIGINT
, a_go_onintr
);
749 termios_state_reset();
751 a_go_cleanup(a_GO_CLEANUP_UNWIND
| /* XXX FAKE */a_GO_CLEANUP_HOLDALLSIGS
);
754 n_err_sighdl(_("Interrupt\n"));
755 safe_signal(SIGPIPE
, a_go_oldpipe
);
756 siglongjmp(a_go_srbuf
, 0); /* FIXME get rid */
760 a_go_cleanup(enum a_go_cleanup_mode gcm
){
761 /* Signals blocked */
762 struct a_go_ctx
*gcp
;
765 if(!(gcm
& a_GO_CLEANUP_HOLDALLSIGS
))
770 /* Free input injections of this level first */
771 if(!(gcm
& a_GO_CLEANUP_LOOPTICK
)){
772 struct a_go_input_inject
**giipp
, *giip
;
774 for(giipp
= &gcp
->gc_inject
; (giip
= *giipp
) != NULL
;){
775 *giipp
= giip
->gii_next
;
780 /* Cleanup non-crucial external stuff */
782 if(gcp
->gc_data
.gdc_colour
!= NULL
)
783 n_colour_stack_del(NULL
);
786 /* Work the actual context (according to cleanup mode) */
787 if(gcp
->gc_outer
== NULL
){
788 if(gcm
& (a_GO_CLEANUP_UNWIND
| a_GO_CLEANUP_SIGINT
)){
789 if(a_go_xcall
!= NULL
){
793 gcp
->gc_flags
&= ~a_GO_XCALL_LOOP_MASK
;
794 n_pstate
&= ~n_PS_ERR_EXIT_MASK
;
797 if(!(n_pstate
& n_PS_SOURCING
))
803 n_pstate
&= ~(n_PS_SOURCING
| n_PS_ROBOT
);
804 assert(a_go_xcall
== NULL
);
805 assert(!(gcp
->gc_flags
& a_GO_XCALL_LOOP_MASK
));
806 assert(gcp
->gc_on_finalize
== NULL
);
807 assert(gcp
->gc_data
.gdc_colour
== NULL
);
809 }else if(gcm
& a_GO_CLEANUP_LOOPTICK
){
812 }else if(gcp
->gc_flags
& a_GO_SPLICE
){ /* TODO Temporary hack */
813 n_stdin
= gcp
->gc_splice_stdin
;
814 n_stdout
= gcp
->gc_splice_stdout
;
815 n_psonce
= gcp
->gc_splice_psonce
;
819 /* Cleanup crucial external stuff */
820 if(gcp
->gc_data
.gdc_ifcond
!= NULL
){
821 n_cnd_if_stack_del(gcp
->gc_data
.gdc_ifcond
);
822 if(!(gcm
& (a_GO_CLEANUP_ERROR
| a_GO_CLEANUP_SIGINT
)) &&
823 !(gcp
->gc_flags
& a_GO_FORCE_EOF
) && a_go_xcall
== NULL
){
824 n_err(_("Unmatched `if' at end of %s %s\n"),
825 ((gcp
->gc_flags
& a_GO_MACRO
826 ? (gcp
->gc_flags
& a_GO_MACRO_CMD
? _("command") : _("macro"))
827 : _("`source'd file"))),
829 gcm
|= a_GO_CLEANUP_ERROR
;
833 /* Teardown context */
834 if(gcp
->gc_flags
& a_GO_MACRO
){
835 if(gcp
->gc_flags
& a_GO_MACRO_FREE_DATA
){
838 while(*(lp
= &gcp
->gc_lines
[gcp
->gc_loff
]) != NULL
){
842 /* Part of gcp's memory chunk, then */
843 if(!(gcp
->gc_flags
& a_GO_MACRO_CMD
))
844 n_free(gcp
->gc_lines
);
846 }else if(gcp
->gc_flags
& a_GO_PIPE
)
847 /* XXX command manager should -TERM then -KILL instead of hoping
848 * XXX for exit of provider due to n_ERR_PIPE / SIGPIPE */
849 Pclose(gcp
->gc_file
, TRU1
);
850 else if(gcp
->gc_flags
& a_GO_FILE
)
851 Fclose(gcp
->gc_file
);
854 if(!(gcp
->gc_flags
& a_GO_MEMPOOL_INHERITED
)){
855 if(gcp
->gc_data
.gdc_mempool
!= NULL
)
856 n_memory_pool_pop(NULL
);
860 n_go_data
= &(a_go_ctx
= gcp
->gc_outer
)->gc_data
;
861 if((a_go_ctx
->gc_flags
& (a_GO_MACRO
| a_GO_SUPER_MACRO
)) ==
862 (a_GO_MACRO
| a_GO_SUPER_MACRO
)){
863 n_pstate
&= ~n_PS_SOURCING
;
864 assert(n_pstate
& n_PS_ROBOT
);
865 }else if(!(a_go_ctx
->gc_flags
& a_GO_TYPE_MASK
))
866 n_pstate
&= ~(n_PS_SOURCING
| n_PS_ROBOT
);
868 assert(n_pstate
& n_PS_ROBOT
);
870 if(gcp
->gc_on_finalize
!= NULL
)
871 (*gcp
->gc_on_finalize
)(gcp
->gc_finalize_arg
);
873 if(gcm
& a_GO_CLEANUP_ERROR
){
874 if(a_go_ctx
->gc_flags
& a_GO_XCALL_LOOP
)
875 a_go_ctx
->gc_flags
|= a_GO_XCALL_LOOP_ERROR
;
879 if(gcp
->gc_flags
& a_GO_FREE
)
882 if(n_UNLIKELY((gcm
& a_GO_CLEANUP_UNWIND
) && gcp
!= a_go_ctx
))
887 if(!(gcm
& a_GO_CLEANUP_HOLDALLSIGS
))
892 /* With *posix* we follow what POSIX says:
893 * Any errors in the start-up file shall either cause mailx to
894 * terminate with a diagnostic message and a non-zero status or to
895 * continue after writing a diagnostic message, ignoring the
896 * remainder of the lines in the start-up file
897 * Print the diagnostic only for the outermost resource unless the user
898 * is debugging or in verbose mode */
899 if((n_poption
& n_PO_D_V
) ||
900 (!(n_psonce
& n_PSO_STARTED
) &&
901 !(gcp
->gc_flags
& (a_GO_SPLICE
| a_GO_MACRO
)) &&
902 !(gcp
->gc_outer
->gc_flags
& a_GO_TYPE_MASK
)))
903 /* I18N: file inclusion, macro etc. evaluation has been stopped */
904 n_alert(_("Stopped %s %s due to errors%s"),
905 (n_psonce
& n_PSO_STARTED
906 ? (gcp
->gc_flags
& a_GO_SPLICE
? _("spliced in program")
907 : (gcp
->gc_flags
& a_GO_MACRO
908 ? (gcp
->gc_flags
& a_GO_MACRO_CMD
909 ? _("evaluating command") : _("evaluating macro"))
910 : (gcp
->gc_flags
& a_GO_PIPE
911 ? _("executing `source'd pipe")
912 : (gcp
->gc_flags
& a_GO_FILE
913 ? _("loading `source'd file") : _(a_GO_MAINCTX_NAME
))))
915 : (gcp
->gc_flags
& a_GO_MACRO
916 ? (gcp
->gc_flags
& a_GO_MACRO_X_OPTION
917 ? _("evaluating command line") : _("evaluating macro"))
918 : _("loading initialization resource"))),
920 (n_poption
& n_PO_DEBUG
? n_empty
: _(" (enable *debug* for trace)")));
925 a_go_file(char const *file
, bool_t silent_open_error
){
926 struct a_go_ctx
*gcp
;
936 /* Being a command argument file is space-trimmed *//* TODO v15 with
937 * TODO WYRALIST this is no longer necessary true, and for that we
938 * TODO don't set _PARSE_TRIM_SPACE because we cannot! -> cmd-tab.h!! */
940 ((ispipe
= (!silent_open_error
&& (nlen
= strlen(file
)) > 0 &&
941 file
[--nlen
] == '|')))
944 if(!silent_open_error
){
945 for(nlen
= strlen(file
); nlen
> 0;){
951 nbuf
= savestrbuf(file
, nlen
);
961 if((fip
= Popen(nbuf
/* #if 0 above = savestrbuf(file, nlen)*/, "r",
962 ok_vlook(SHELL
), NULL
, n_CHILD_FD_NULL
)) == NULL
)
964 }else if((nbuf
= fexpand(file
, FEXP_LOCAL
)) == NULL
)
966 else if((fip
= Fopen(nbuf
, "r")) == NULL
){
968 if(!silent_open_error
|| (n_poption
& n_PO_D_V
))
970 if(silent_open_error
)
975 sigprocmask(SIG_BLOCK
, NULL
, &osigmask
);
977 gcp
= n_alloc(n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
) +
978 (nlen
= strlen(nbuf
) +1));
979 memset(gcp
, 0, n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
));
983 gcp
->gc_outer
= a_go_ctx
;
984 gcp
->gc_osigmask
= osigmask
;
986 gcp
->gc_flags
= (ispipe
? a_GO_FREE
| a_GO_PIPE
: a_GO_FREE
| a_GO_FILE
) |
987 (a_go_ctx
->gc_flags
& a_GO_SUPER_MACRO
? a_GO_SUPER_MACRO
: 0);
988 memcpy(gcp
->gc_name
, nbuf
, nlen
);
991 n_go_data
= &gcp
->gc_data
;
992 n_pstate
|= n_PS_SOURCING
| n_PS_ROBOT
;
993 if(!a_go_event_loop(gcp
, n_GO_INPUT_NONE
| n_GO_INPUT_NL_ESC
))
997 return (fip
!= NULL
);
1001 a_go_load(struct a_go_ctx
*gcp
){
1004 assert(!(n_psonce
& n_PSO_STARTED
));
1005 assert(!(a_go_ctx
->gc_flags
& a_GO_TYPE_MASK
));
1007 gcp
->gc_flags
|= a_GO_MEMPOOL_INHERITED
;
1008 gcp
->gc_data
.gdc_mempool
= n_go_data
->gdc_mempool
;
1013 * Any errors in the start-up file shall either cause mailx to terminate
1014 * with a diagnostic message and a non-zero status or to continue after
1015 * writing a diagnostic message, ignoring the remainder of the lines in
1016 * the start-up file. */
1017 gcp
->gc_outer
= a_go_ctx
;
1019 n_go_data
= &gcp
->gc_data
;
1020 /* FIXME won't work for now (n_PS_ROBOT needs n_PS_SOURCING sofar)
1021 n_pstate |= n_PS_ROBOT |
1022 (gcp->gc_flags & a_GO_MACRO_X_OPTION ? 0 : n_PS_SOURCING);
1024 n_pstate
|= n_PS_ROBOT
| n_PS_SOURCING
;
1030 return (((n_psonce
& n_PSO_EXIT_MASK
) |
1031 (n_pstate
& n_PS_ERR_EXIT_MASK
)) == 0);
1035 a_go__eloopint(int sig
){ /* TODO one day, we don't need it no more */
1036 NYD_X
; /* Signal handler */
1038 siglongjmp(a_go_ctx
->gc_eloop_jmp
, 1);
1042 a_go_event_loop(struct a_go_ctx
*gcp
, enum n_go_input_flags gif
){
1043 sighandler_type soldhdl
;
1044 struct a_go_eval_ctx gec
;
1045 enum {a_RETOK
= TRU1
, a_TICKED
= 1<<1} volatile f
;
1046 volatile int hadint
; /* TODO get rid of shitty signal stuff (see signal.c) */
1050 memset(&gec
, 0, sizeof gec
);
1051 osigmask
= gcp
->gc_osigmask
;
1055 if((soldhdl
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
){
1056 safe_signal(SIGINT
, &a_go__eloopint
);
1057 if(sigsetjmp(gcp
->gc_eloop_jmp
, 1)){
1061 gcp
->gc_flags
&= ~a_GO_XCALL_LOOP_MASK
;
1066 for(;; f
|= a_TICKED
){
1072 /* Read a line of commands and handle end of file specially */
1073 gec
.gec_line
.l
= gec
.gec_line_size
;
1075 n
= n_go_input(gif
, NULL
, &gec
.gec_line
.s
, &gec
.gec_line
.l
, NULL
, NULL
);
1077 gec
.gec_line_size
= (ui32_t
)gec
.gec_line
.l
;
1078 gec
.gec_line
.l
= (ui32_t
)n
;
1084 assert(gec
.gec_hist_flags
== a_GO_HIST_NONE
);
1085 if(!a_go_evaluate(&gec
))
1089 if(!(f
& a_RETOK
) || a_go_xcall
!= NULL
||
1090 (n_psonce
& n_PSO_EXIT_MASK
) || (n_pstate
& n_PS_ERR_EXIT_MASK
))
1094 jjump
: /* TODO Should be _CLEANUP_UNWIND not _TEARDOWN on signal if DOABLE! */
1095 a_go_cleanup(a_GO_CLEANUP_TEARDOWN
|
1096 (f
& a_RETOK
? 0 : a_GO_CLEANUP_ERROR
) |
1097 (hadint
? a_GO_CLEANUP_SIGINT
: 0) | a_GO_CLEANUP_HOLDALLSIGS
);
1099 if(gec
.gec_line
.s
!= NULL
)
1100 n_free(gec
.gec_line
.s
);
1102 if(soldhdl
!= SIG_IGN
)
1103 safe_signal(SIGINT
, soldhdl
);
1107 sigprocmask(SIG_SETMASK
, &osigmask
, NULL
);
1110 return (f
& a_RETOK
);
1115 struct a_go_ctx
*gcp
;
1118 assert(n_stdin
!= NULL
);
1120 gcp
= (void*)a_go__mainctx_b
.uf
;
1121 DBGOR( memset(gcp
, 0, n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
)),
1122 memset(&gcp
->gc_data
, 0, sizeof gcp
->gc_data
) );
1123 gcp
->gc_file
= n_stdin
;
1124 memcpy(gcp
->gc_name
, a_GO_MAINCTX_NAME
, sizeof(a_GO_MAINCTX_NAME
));
1126 n_go_data
= &gcp
->gc_data
;
1128 n_child_manager_start();
1133 n_go_main_loop(void){ /* FIXME */
1134 struct a_go_eval_ctx gec
;
1141 if (!(n_pstate
& n_PS_SOURCING
)) {
1142 if (safe_signal(SIGINT
, SIG_IGN
) != SIG_IGN
)
1143 safe_signal(SIGINT
, &a_go_onintr
);
1144 if (safe_signal(SIGHUP
, SIG_IGN
) != SIG_IGN
)
1145 safe_signal(SIGHUP
, &a_go_hangup
);
1147 a_go_oldpipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1148 safe_signal(SIGPIPE
, a_go_oldpipe
);
1150 memset(&gec
, 0, sizeof gec
);
1152 (void)sigsetjmp(a_go_srbuf
, 1); /* FIXME get rid */
1155 for (eofcnt
= 0;; gec
.gec_ever_seen
= TRU1
) {
1158 if(gec
.gec_ever_seen
)
1159 a_go_cleanup(a_GO_CLEANUP_LOOPTICK
| a_GO_CLEANUP_HOLDALLSIGS
);
1161 if (!(n_pstate
& n_PS_SOURCING
)) {
1164 /* TODO Note: this buffer may contain a password. We should redefine
1165 * TODO the code flow which has to do that */
1166 if ((cp
= termios_state
.ts_linebuf
) != NULL
) {
1167 termios_state
.ts_linebuf
= NULL
;
1168 termios_state
.ts_linesize
= 0;
1169 n_free(cp
); /* TODO pool give-back */
1171 if (gec
.gec_line
.l
> LINESIZE
* 3) {
1172 n_free(gec
.gec_line
.s
);
1173 gec
.gec_line
.s
= NULL
;
1174 gec
.gec_line
.l
= gec
.gec_line_size
= 0;
1178 if (!(n_pstate
& n_PS_SOURCING
) && (n_psonce
& n_PSO_INTERACTIVE
)) {
1181 if ((cp
= ok_vlook(newmail
)) != NULL
) {
1184 /* FIXME TEST WITH NOPOLL ETC. !!! */
1185 n
= (cp
!= NULL
&& strcmp(cp
, "nopoll"));
1186 if ((mb
.mb_type
== MB_FILE
&& !stat(mailname
, &st
) &&
1187 st
.st_size
> mailsize
) ||
1188 (mb
.mb_type
== MB_MAILDIR
&& n
!= 0)) {
1189 size_t odot
= PTR2SIZE(dot
- message
);
1190 ui32_t odid
= (n_pstate
& n_PS_DID_PRINT_DOT
);
1194 i
= setfile(mailname
,
1196 ((mb
.mb_perm
& MB_DELE
) ? 0 : FEDIT_RDONLY
));
1199 n_exit_status
|= n_EXIT_ERR
;
1203 dot
= &message
[odot
];
1208 n_exit_status
= n_EXIT_OK
;
1211 /* Read a line of commands and handle end of file specially */
1212 gec
.gec_line
.l
= gec
.gec_line_size
;
1216 histadd
= (!(n_pstate
& n_PS_SOURCING
) &&
1217 (n_psonce
& n_PSO_INTERACTIVE
));
1219 n
= n_go_input(n_GO_INPUT_CTX_DEFAULT
| n_GO_INPUT_NL_ESC
, NULL
,
1220 &gec
.gec_line
.s
, &gec
.gec_line
.l
, NULL
, &histadd
);
1223 gec
.gec_hist_flags
= histadd
? a_GO_HIST_ADD
: a_GO_HIST_NONE
;
1225 gec
.gec_line_size
= (ui32_t
)gec
.gec_line
.l
;
1226 gec
.gec_line
.l
= (ui32_t
)n
;
1229 if (!(n_pstate
& n_PS_ROBOT
) &&
1230 (n_psonce
& n_PSO_INTERACTIVE
) && ok_blook(ignoreeof
) &&
1232 fprintf(n_stdout
, _("*ignoreeof* set, use `quit' to quit.\n"));
1233 n_go_input_clearerr();
1239 n_pstate
&= ~n_PS_HOOK_MASK
;
1241 rv
= a_go_evaluate(&gec
);
1244 if(gec
.gec_hist_flags
& a_GO_HIST_ADD
){
1245 char const *cc
, *ca
;
1247 cc
= gec
.gec_hist_cmd
;
1248 ca
= gec
.gec_hist_args
;
1249 if(cc
!= NULL
&& ca
!= NULL
)
1250 cc
= savecatsep(cc
, ' ', ca
);
1253 n_tty_addhist(cc
, ((gec
.gec_hist_flags
& a_GO_HIST_GABBY
) != 0));
1256 switch(n_pstate
& n_PS_ERR_EXIT_MASK
){
1257 case n_PS_ERR_XIT
: n_psonce
|= n_PSO_XIT
; break;
1258 case n_PS_ERR_QUIT
: n_psonce
|= n_PSO_QUIT
; break;
1261 if(n_psonce
& n_PSO_EXIT_MASK
)
1268 a_go_cleanup(a_GO_CLEANUP_TEARDOWN
| a_GO_CLEANUP_HOLDALLSIGS
|
1269 (rv
? 0 : a_GO_CLEANUP_ERROR
));
1271 if (gec
.gec_line
.s
!= NULL
)
1272 n_free(gec
.gec_line
.s
);
1280 n_go_input_clearerr(void){
1286 if(!(a_go_ctx
->gc_flags
& (a_GO_FORCE_EOF
|
1287 a_GO_PIPE
| a_GO_MACRO
| a_GO_SPLICE
)))
1288 fp
= a_go_ctx
->gc_file
;
1291 a_go_ctx
->gc_flags
&= ~a_GO_IS_EOF
;
1298 n_go_input_force_eof(void){
1300 a_go_ctx
->gc_flags
|= a_GO_FORCE_EOF
;
1305 n_go_input_is_eof(void){
1309 rv
= ((a_go_ctx
->gc_flags
& a_GO_IS_EOF
) != 0);
1315 n_go_input_inject(enum n_go_input_inject_flags giif
, char const *buf
,
1321 if(UIZ_MAX
- n_VSTRUCT_SIZEOF(struct a_go_input_inject
, gii_dat
) -1 > len
&&
1323 struct a_go_input_inject
*giip
, **giipp
;
1327 giip
= n_alloc(n_VSTRUCT_SIZEOF(struct a_go_input_inject
, gii_dat
1329 giipp
= &a_go_ctx
->gc_inject
;
1330 giip
->gii_next
= *giipp
;
1331 giip
->gii_commit
= ((giif
& n_GO_INPUT_INJECT_COMMIT
) != 0);
1332 giip
->gii_no_history
= ((giif
& n_GO_INPUT_INJECT_HISTORY
) == 0);
1333 memcpy(&giip
->gii_dat
[0], buf
, len
);
1334 giip
->gii_dat
[giip
->gii_len
= len
] = '\0';
1343 (n_go_input
)(enum n_go_input_flags gif
, char const *prompt
, char **linebuf
,
1344 size_t *linesize
, char const *string
, bool_t
*histok_or_null
1345 n_MEMORY_DEBUG_ARGS
){
1346 /* TODO readline: linebuf pool!; n_go_input should return si64_t */
1347 struct n_string xprompt
;
1349 bool_t doprompt
, dotty
;
1351 struct a_go_input_inject
*giip
;
1356 if(!(gif
& n_GO_INPUT_HOLDALLSIGS
))
1361 if(a_go_ctx
->gc_flags
& a_GO_FORCE_EOF
){
1362 a_go_ctx
->gc_flags
|= a_GO_IS_EOF
;
1367 if(gif
& n_GO_INPUT_FORCE_STDIN
)
1370 /* Special case macro mode: never need to prompt, lines have always been
1371 * unfolded already */
1372 if(a_go_ctx
->gc_flags
& a_GO_MACRO
){
1373 if(*linebuf
!= NULL
)
1376 /* Injection in progress? Don't care about the autocommit state here */
1377 if((giip
= a_go_ctx
->gc_inject
) != NULL
){
1378 a_go_ctx
->gc_inject
= giip
->gii_next
;
1380 /* Simply "reuse" allocation, copy string to front of it */
1382 histok
= !giip
->gii_no_history
;
1383 *linesize
= giip
->gii_len
;
1384 *linebuf
= (char*)giip
;
1385 memmove(*linebuf
, giip
->gii_dat
, giip
->gii_len
+1);
1386 iftype
= "INJECTION";
1388 if((*linebuf
= a_go_ctx
->gc_lines
[a_go_ctx
->gc_loff
]) == NULL
){
1390 a_go_ctx
->gc_flags
|= a_GO_IS_EOF
;
1395 ++a_go_ctx
->gc_loff
;
1396 *linesize
= strlen(*linebuf
);
1397 if(!(a_go_ctx
->gc_flags
& a_GO_MACRO_FREE_DATA
))
1398 *linebuf
= sbufdup(*linebuf
, *linesize
);
1400 iftype
= (a_go_ctx
->gc_flags
& a_GO_MACRO_X_OPTION
)
1402 : (a_go_ctx
->gc_flags
& a_GO_MACRO_CMD
) ? "CMD" : "MACRO";
1406 n_pstate
|= n_PS_READLINE_NL
;
1409 /* Injection in progress? */
1410 struct a_go_input_inject
**giipp
;
1412 giipp
= &a_go_ctx
->gc_inject
;
1414 if((giip
= *giipp
) != NULL
){
1415 *giipp
= giip
->gii_next
;
1417 if(giip
->gii_commit
){
1418 if(*linebuf
!= NULL
)
1420 goto jinject
; /* (above) */
1422 string
= savestrbuf(giip
->gii_dat
, giip
->gii_len
);
1429 n_pstate
&= ~n_PS_READLINE_NL
;
1430 iftype
= (!(n_psonce
& n_PSO_STARTED
) ? "LOAD"
1431 : (n_pstate
& n_PS_SOURCING
) ? "SOURCE" : "READ");
1432 doprompt
= ((n_psonce
& (n_PSO_INTERACTIVE
| n_PSO_STARTED
)) ==
1433 (n_PSO_INTERACTIVE
| n_PSO_STARTED
) && !(n_pstate
& n_PS_ROBOT
));
1434 dotty
= (doprompt
&& !ok_blook(line_editor_disable
));
1436 gif
|= n_GO_INPUT_PROMPT_NONE
;
1439 n_string_creat_auto(&xprompt
);
1441 gif
|= n_GO_INPUT_PROMPT_EVAL
;
1444 /* Ensure stdout is flushed first anyway (partial lines, maybe?) */
1445 if(!dotty
&& (gif
& n_GO_INPUT_PROMPT_NONE
))
1448 ifile
= (gif
& n_GO_INPUT_FORCE_STDIN
) ? n_stdin
: a_go_ctx
->gc_file
;
1450 assert((n_pstate
& n_PS_COMPOSE_FORKHOOK
) &&
1451 (a_go_ctx
->gc_flags
& a_GO_MACRO
));
1455 for(nold
= n
= 0;;){
1457 assert(ifile
== n_stdin
);
1458 if(string
!= NULL
&& (n
= (int)strlen(string
)) > 0){
1462 *linesize
= (size_t)n
+ LINESIZE
+1;
1463 *linebuf
= (n_realloc
)(*linebuf
, *linesize n_MEMORY_DEBUG_ARGSCALL
);
1464 memcpy(*linebuf
, string
, (size_t)n
+1);
1470 n
= (n_tty_readline
)(gif
, prompt
, linebuf
, linesize
, n
, histok_or_null
1471 n_MEMORY_DEBUG_ARGSCALL
);
1475 if(!(gif
& n_GO_INPUT_PROMPT_NONE
))
1476 n_tty_create_prompt(&xprompt
, prompt
, gif
);
1480 if(!(gif
& n_GO_INPUT_PROMPT_NONE
) && xprompt
.s_len
> 0){
1481 fwrite(xprompt
.s_dat
, 1, xprompt
.s_len
, n_stdout
);
1485 n
= (readline_restart
)(ifile
, linebuf
, linesize
, n
1486 n_MEMORY_DEBUG_ARGSCALL
);
1490 if(n
< 0 && feof(ifile
))
1491 a_go_ctx
->gc_flags
|= a_GO_IS_EOF
;
1493 if(n
> 0 && nold
> 0){
1498 cp
= &(*linebuf
)[nold
];
1499 while(spacechar(*cp
) && n
- i
>= nold
)
1502 memmove(&(*linebuf
)[nold
], cp
, n
- nold
- i
);
1504 (*linebuf
)[n
] = '\0';
1513 * An unquoted <backslash> at the end of a command line shall
1514 * be discarded and the next line shall continue the command */
1515 if(!(gif
& n_GO_INPUT_NL_ESC
) || (*linebuf
)[n
- 1] != '\\'){
1517 n_pstate
|= n_PS_READLINE_NL
;
1520 /* Definitely outside of quotes, thus the quoting rules are so that an
1521 * uneven number of successive reverse solidus at EOL is a continuation */
1525 for(j
= 1, i
= (size_t)n
- 1; i
-- > 0; ++j
)
1526 if((*linebuf
)[i
] != '\\')
1531 (*linebuf
)[nold
= --n
] = '\0';
1532 gif
|= n_GO_INPUT_NL_FOLLOW
;
1537 (*linebuf
)[*linesize
= n
] = '\0';
1540 if(n_poption
& n_PO_D_VV
)
1541 n_err(_("%s %d bytes <%s>\n"), iftype
, n
, *linebuf
);
1543 if (n_pstate
& n_PS_PSTATE_PENDMASK
)
1544 a_go_update_pstate();
1546 /* TODO We need to special case a_GO_SPLICE, since that is not managed by us
1547 * TODO but only established from the outside and we need to drop this
1548 * TODO overlay context somehow */
1549 if(n
< 0 && (a_go_ctx
->gc_flags
& a_GO_SPLICE
))
1550 a_go_cleanup(a_GO_CLEANUP_TEARDOWN
| a_GO_CLEANUP_HOLDALLSIGS
);
1552 if(histok_or_null
!= NULL
&& !histok
)
1553 *histok_or_null
= FAL0
;
1555 if(!(gif
& n_GO_INPUT_HOLDALLSIGS
))
1562 n_go_input_cp(enum n_go_input_flags gif
, char const *prompt
,
1563 char const *string
){
1567 char *linebuf
, * volatile rv
;
1575 n_SIGMAN_ENTER_SWITCH(&sm
, n_SIGMAN_ALL
){
1583 n
= n_go_input(gif
, prompt
, &linebuf
, &linesize
, string
, &histadd
);
1584 if(n
> 0 && *(rv
= savestrbuf(linebuf
, (size_t)n
)) != '\0' &&
1585 (gif
& n_GO_INPUT_HIST_ADD
) && (n_psonce
& n_PSO_INTERACTIVE
) &&
1587 n_tty_addhist(rv
, ((gif
& n_GO_INPUT_HIST_GABBY
) != 0));
1589 n_sigman_cleanup_ping(&sm
);
1594 n_sigman_leave(&sm
, n_SIGMAN_VIPSIGS_NTTYOUT
);
1599 n_go_load(char const *name
){
1600 struct a_go_ctx
*gcp
;
1608 if(name
== NULL
|| *name
== '\0')
1610 else if((fip
= Fopen(name
, "r")) == NULL
){
1611 if(n_poption
& n_PO_D_V
)
1612 n_err(_("No such file to load: %s\n"), n_shexp_quote_cp(name
, FAL0
));
1616 i
= strlen(name
) +1;
1617 gcp
= n_alloc(n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
) + i
);
1618 memset(gcp
, 0, n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
));
1621 gcp
->gc_flags
= a_GO_FREE
| a_GO_FILE
;
1622 memcpy(gcp
->gc_name
, name
, i
);
1624 if(n_poption
& n_PO_D_V
)
1625 n_err(_("Loading %s\n"), n_shexp_quote_cp(gcp
->gc_name
, FAL0
));
1626 rv
= a_go_load(gcp
);
1633 n_go_Xargs(char const **lines
, size_t cnt
){
1634 static char const name
[] = "-X";
1639 char uf
[n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
) + sizeof(name
)];
1641 char const *srcp
, *xsrcp
;
1643 size_t imax
, i
, len
;
1644 struct a_go_ctx
*gcp
;
1648 memset(gcp
, 0, n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
));
1650 gcp
->gc_flags
= a_GO_MACRO
| a_GO_MACRO_X_OPTION
|
1651 a_GO_SUPER_MACRO
| a_GO_MACRO_FREE_DATA
;
1652 memcpy(gcp
->gc_name
, name
, sizeof name
);
1654 /* The problem being that we want to support reverse solidus newline
1655 * escaping also within multiline -X, i.e., POSIX says:
1656 * An unquoted <backslash> at the end of a command line shall
1657 * be discarded and the next line shall continue the command
1658 * Therefore instead of "gcp->gc_lines = n_UNCONST(lines)", duplicate the
1659 * entire lines array and set _MACRO_FREE_DATA */
1661 gcp
->gc_lines
= n_alloc(sizeof(*gcp
->gc_lines
) * imax
);
1663 /* For each of the input lines.. */
1664 for(i
= len
= 0, cp
= NULL
; cnt
> 0;){
1668 if((j
= strlen(srcp
= *lines
)) == 0){
1673 /* Separate one line from a possible multiline input string */
1674 if((xsrcp
= memchr(srcp
, '\n', j
)) != NULL
){
1676 j
= PTR2SIZE(xsrcp
- srcp
);
1680 /* The (separated) string may itself indicate soft newline escaping */
1681 if((keep
= (srcp
[j
- 1] == '\\'))){
1684 /* Need an uneven number of reverse solidus */
1685 for(xk
= 1, xj
= j
- 1; xj
-- > 0; ++xk
)
1686 if(srcp
[xj
] != '\\')
1694 /* Strip any leading WS from follow lines, then */
1696 while(j
> 0 && spacechar(*srcp
))
1700 if(i
+ 2 >= imax
){ /* TODO need a vector (main.c, here, ++) */
1702 gcp
->gc_lines
= n_realloc(gcp
->gc_lines
, sizeof(*gcp
->gc_lines
) *
1705 gcp
->gc_lines
[i
] = cp
= n_realloc(cp
, len
+ j
+1);
1706 memcpy(&cp
[len
], srcp
, j
);
1707 cp
[len
+= j
] = '\0';
1716 assert(i
+ 1 < imax
);
1717 gcp
->gc_lines
[i
++] = cp
;
1719 gcp
->gc_lines
[i
] = NULL
;
1721 b
.rv
= a_go_load(gcp
);
1731 rv
= (a_go_file(*(char**)v
, FAL0
) == TRU1
) ? 0 : 1;
1737 c_source_if(void *v
){ /* XXX obsolete?, support file tests in `if' etc.! */
1741 rv
= (a_go_file(*(char**)v
, TRU1
) == TRU1
) ? 0 : 1;
1747 n_go_macro(enum n_go_input_flags gif
, char const *name
, char **lines
,
1748 void (*on_finalize
)(void*), void *finalize_arg
){
1749 struct a_go_ctx
*gcp
;
1755 sigprocmask(SIG_BLOCK
, NULL
, &osigmask
);
1757 gcp
= n_alloc(n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
) +
1758 (i
= strlen(name
) +1));
1759 memset(gcp
, 0, n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
));
1763 gcp
->gc_outer
= a_go_ctx
;
1764 gcp
->gc_osigmask
= osigmask
;
1765 gcp
->gc_flags
= a_GO_FREE
| a_GO_MACRO
| a_GO_MACRO_FREE_DATA
|
1766 ((!(a_go_ctx
->gc_flags
& a_GO_TYPE_MASK
) ||
1767 (a_go_ctx
->gc_flags
& a_GO_SUPER_MACRO
)) ? a_GO_SUPER_MACRO
: 0);
1768 gcp
->gc_lines
= lines
;
1769 gcp
->gc_on_finalize
= on_finalize
;
1770 gcp
->gc_finalize_arg
= finalize_arg
;
1771 memcpy(gcp
->gc_name
, name
, i
);
1774 n_go_data
= &gcp
->gc_data
;
1775 n_pstate
|= n_PS_ROBOT
;
1776 rv
= a_go_event_loop(gcp
, gif
);
1778 /* Shall this enter a `xcall' stack avoidance optimization (loop)? */
1779 if(a_go_xcall
!= NULL
){
1781 struct n_cmd_arg_ctx
*cacp
;
1783 if(a_go_xcall
== (void*)-1)
1785 else if(((void const*)(cacp
= a_go_xcall
)->cac_indat
) == gcp
){
1786 /* Indicate that "our" (ex-) parent now hosts xcall optimization */
1787 a_go_ctx
->gc_flags
|= a_GO_XCALL_LOOP
;
1788 while(a_go_xcall
!= NULL
){
1791 a_go_ctx
->gc_flags
&= ~a_GO_XCALL_LOOP_ERROR
;
1795 cacp
= n_cmd_arg_restore_from_heap(vp
);
1802 rv
= ((a_go_ctx
->gc_flags
& a_GO_XCALL_LOOP_ERROR
) != 0);
1803 a_go_ctx
->gc_flags
&= ~a_GO_XCALL_LOOP_MASK
;
1811 n_go_command(enum n_go_input_flags gif
, char const *cmd
){
1812 struct a_go_ctx
*gcp
;
1818 sigprocmask(SIG_BLOCK
, NULL
, &osigmask
);
1822 gcp
= n_alloc(n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
) +
1823 ial
+ 2*sizeof(char*));
1824 memset(gcp
, 0, n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
));
1828 gcp
->gc_outer
= a_go_ctx
;
1829 gcp
->gc_osigmask
= osigmask
;
1830 gcp
->gc_flags
= a_GO_FREE
| a_GO_MACRO
| a_GO_MACRO_CMD
|
1831 ((!(a_go_ctx
->gc_flags
& a_GO_TYPE_MASK
) ||
1832 (a_go_ctx
->gc_flags
& a_GO_SUPER_MACRO
)) ? a_GO_SUPER_MACRO
: 0);
1833 gcp
->gc_lines
= (void*)&gcp
->gc_name
[ial
];
1834 memcpy(gcp
->gc_lines
[0] = &gcp
->gc_name
[0], cmd
, i
);
1835 gcp
->gc_lines
[1] = NULL
;
1838 n_go_data
= &gcp
->gc_data
;
1839 n_pstate
|= n_PS_ROBOT
;
1840 rv
= a_go_event_loop(gcp
, gif
);
1846 n_go_splice_hack(char const *cmd
, FILE *new_stdin
, FILE *new_stdout
,
1847 ui32_t new_psonce
, void (*on_finalize
)(void*), void *finalize_arg
){
1848 struct a_go_ctx
*gcp
;
1853 sigprocmask(SIG_BLOCK
, NULL
, &osigmask
);
1855 gcp
= n_alloc(n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
) +
1856 (i
= strlen(cmd
) +1));
1857 memset(gcp
, 0, n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
));
1861 gcp
->gc_outer
= a_go_ctx
;
1862 gcp
->gc_osigmask
= osigmask
;
1863 gcp
->gc_file
= new_stdin
;
1864 gcp
->gc_flags
= a_GO_FREE
| a_GO_SPLICE
;
1865 gcp
->gc_on_finalize
= on_finalize
;
1866 gcp
->gc_finalize_arg
= finalize_arg
;
1867 gcp
->gc_splice_stdin
= n_stdin
;
1868 gcp
->gc_splice_stdout
= n_stdout
;
1869 gcp
->gc_splice_psonce
= n_psonce
;
1870 memcpy(gcp
->gc_name
, cmd
, i
);
1872 n_stdin
= new_stdin
;
1873 n_stdout
= new_stdout
;
1874 n_psonce
= new_psonce
;
1876 n_pstate
|= n_PS_ROBOT
;
1883 n_go_splice_hack_remove_after_jump(void){
1884 a_go_cleanup(a_GO_CLEANUP_TEARDOWN
);
1888 n_go_may_yield_control(void){ /* TODO this is a terrible hack */
1889 struct a_go_ctx
*gcp
;
1895 /* Only when startup completed */
1896 if(!(n_psonce
& n_PSO_STARTED
))
1898 /* Only interactive or batch mode (assuming that is ok) */
1899 if(!(n_psonce
& n_PSO_INTERACTIVE
) && !(n_poption
& n_PO_BATCH_FLAG
))
1902 /* Not when running any hook */
1903 if(n_pstate
& n_PS_HOOK_MASK
)
1906 /* Traverse up the stack:
1907 * . not when controlled by a child process
1908 * TODO . not when there are pipes involved, we neither handle job control,
1909 * TODO nor process groups, that is, controlling terminal acceptably
1910 * . not when sourcing a file */
1911 for(gcp
= a_go_ctx
; gcp
!= NULL
; gcp
= gcp
->gc_outer
){
1912 if(gcp
->gc_flags
& (a_GO_PIPE
| a_GO_FILE
| a_GO_SPLICE
))
1924 /* TODO HACK! `eval' should be nothing else but a command prefix, evaluate
1925 * TODO ARGV with shell rules, but if that is not possible then simply
1926 * TODO adjust argv/argc of "the CmdCtx" that we will have "exec" real cmd */
1927 struct a_go_eval_ctx gec
;
1928 struct n_string s_b
, *sp
;
1930 char const **argv
, *cp
;
1935 for(j
= i
= 0; (cp
= argv
[i
]) != NULL
; ++i
)
1938 sp
= n_string_creat_auto(&s_b
);
1939 sp
= n_string_reserve(sp
, j
);
1941 for(i
= 0; (cp
= argv
[i
]) != NULL
; ++i
){
1943 sp
= n_string_push_c(sp
, ' ');
1944 sp
= n_string_push_cp(sp
, cp
);
1947 memset(&gec
, 0, sizeof gec
);
1948 gec
.gec_line
.s
= n_string_cp(sp
);
1949 gec
.gec_line
.l
= sp
->s_len
;
1950 (void)/* XXX */a_go_evaluate(&gec
);
1952 return (a_go_xcall
!= NULL
? 0 : n_pstate_ex_no
);
1958 struct a_go_ctx
*gcp
;
1961 /* The context can only be a macro context, except that possibly a single
1962 * level of `eval' (TODO: yet) was used to double-expand our arguments */
1963 if((gcp
= a_go_ctx
)->gc_flags
& a_GO_MACRO_CMD
)
1964 gcp
= gcp
->gc_outer
;
1965 if((gcp
->gc_flags
& (a_GO_MACRO
| a_GO_MACRO_CMD
)) != a_GO_MACRO
)
1968 /* Try to roll up the stack as much as possible.
1969 * See a_GO_XCALL_LOOP flag description for more */
1970 if(gcp
->gc_outer
!= NULL
){
1971 if(gcp
->gc_outer
->gc_flags
& a_GO_XCALL_LOOP
)
1972 gcp
= gcp
->gc_outer
;
1974 /* Otherwise this macro is invoked from the top level, in which case we
1975 * silently act as if we were `call'... */
1977 /* ...which means we must ensure the rest of the macro that was us
1978 * doesn't become evaluated! */
1979 a_go_xcall
= (void*)-1;
1984 struct n_cmd_arg_ctx
*cacp
;
1986 cacp
= n_cmd_arg_save_to_heap(vp
);
1987 cacp
->cac_indat
= (char*)gcp
;
1995 n_err(_("`xcall': can only be used inside a macro\n"));
1996 n_pstate_err_no
= n_ERR_INVAL
;
2005 n_psonce
|= n_PSO_XIT
;
2014 n_psonce
|= n_PSO_QUIT
;