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 - _PS_ERR_EXIT_* and _PSO_EXIT_* mixup is a mess: TERRIBLE!
4 *@ TODO - hold_all_sigs() most often on, especially robot mode: TERRIBLE!
5 *@ TODO - n_PS_ROBOT requires yet n_PS_SOURCING, which REALLY sucks.
6 *@ TODO - go_input(): with IO::Device we could have CStringListDevice, for
7 *@ TODO example to handle injections, and also `readctl' channels!
8 *@ TODO (Including sh(1)ell HERE strings and such.)
10 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
11 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
12 * SPDX-License-Identifier: BSD-3-Clause TODO ISC
15 * Copyright (c) 1980, 1993
16 * The Regents of the University of California. All rights reserved.
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions
21 * 1. Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
23 * 2. Redistributions in binary form must reproduce the above copyright
24 * notice, this list of conditions and the following disclaimer in the
25 * documentation and/or other materials provided with the distribution.
26 * 3. Neither the name of the University nor the names of its contributors
27 * may be used to endorse or promote products derived from this software
28 * without specific prior written permission.
30 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45 #ifndef HAVE_AMALGAMATION
51 a_GO_FREE
= 1u<<0, /* Structure was allocated, n_free() it */
52 a_GO_PIPE
= 1u<<1, /* Open on a pipe */
53 a_GO_FILE
= 1u<<2, /* Loading or sourcing a file */
54 a_GO_MACRO
= 1u<<3, /* Running a macro */
55 a_GO_MACRO_FREE_DATA
= 1u<<4, /* Lines are allocated, n_free() once done */
56 /* TODO For simplicity this is yet _MACRO plus specialization overlay
57 * TODO (_X_OPTION, _CMD) -- these should be types on their own! */
58 a_GO_MACRO_X_OPTION
= 1u<<5, /* Macro indeed command line -X option */
59 a_GO_MACRO_CMD
= 1u<<6, /* Macro indeed single-line: ~:COMMAND */
60 /* TODO a_GO_SPLICE: the right way to support *on-compose-splice(-shell)?*
61 * TODO would be a command_loop object that emits an on_read_line event, and
62 * TODO have a special handler for the compose mode; with that, then,
63 * TODO _event_loop() would not call _evaluate() but CTX->on_read_line,
64 * TODO and _evaluate() would be the standard impl.,
65 * TODO whereas the COMMAND ESCAPE switch in collect.c would be another one.
66 * TODO With this generic accmacvar.c:temporary_compose_mode_hook_call()
67 * TODO could be dropped, and n_go_macro() could become extended,
68 * TODO and/or we would add a n_go_anything(), which would allow special
69 * TODO input handlers, special I/O input and output, special `localopts'
70 * TODO etc., to be glued to the new execution context. And all I/O all
71 * TODO over this software should not use stdin/stdout, but CTX->in/out.
72 * TODO The pstate must be a property of the current execution context, too.
73 * TODO This not today. :( For now we invent a special SPLICE execution
74 * TODO context overlay that at least allows to temporarily modify the
75 * TODO global pstate, and the global stdin and stdout pointers. HACK!
76 * TODO This splice thing is very special and has to go again. HACK!!
77 * TODO a_go_input() will drop it once it sees EOF (HACK!), but care for
78 * TODO jumps must be taken by splice creators. HACK!!! But works. ;} */
80 /* If it is none of those, it must be the outermost, the global one */
81 a_GO_TYPE_MASK
= a_GO_PIPE
| a_GO_FILE
| a_GO_MACRO
|
82 /* a_GO_MACRO_X_OPTION | a_GO_MACRO_CMD | */ a_GO_SPLICE
,
84 a_GO_FORCE_EOF
= 1u<<8, /* go_input() shall return EOF next */
87 a_GO_SUPER_MACRO
= 1u<<16, /* *Not* inheriting n_PS_SOURCING state */
88 /* This context has inherited the memory pool from its parent.
89 * In practice only used for resource file loading and -X args, which enter
90 * a top level n_go_main_loop() and should (re)use the in practice already
91 * allocated memory pool of the global context */
92 a_GO_MEMPOOL_INHERITED
= 1u<<17,
94 /* This context has inherited the entire data context from its parent */
95 a_GO_DATACTX_INHERITED
= 1u<<18,
97 a_GO_XCALL_IS_CALL
= 1u<<24, /* n_GO_INPUT_NO_XCALL */
98 /* `xcall' optimization barrier: n_go_macro() has been finished with
99 * a `xcall' request, and `xcall' set this in the parent a_go_input of the
100 * said n_go_macro() to indicate a barrier: we teardown the a_go_input of
101 * the n_go_macro() away after leaving its _event_loop(), but then,
102 * back in n_go_macro(), that enters a for(;;) loop that directly calls
103 * c_call() -- our `xcall' stack avoidance optimization --, yet this call
104 * will itself end up in a new n_go_macro(), and if that again ends up with
105 * `xcall' this should teardown and leave its own n_go_macro(), unrolling the
106 * stack "up to the barrier level", but which effectively still is the
107 * n_go_macro() that lost its a_go_input and is looping the `xcall'
108 * optimization loop. If no `xcall' is desired that loop is simply left and
109 * the _event_loop() of the outer a_go_ctx will perform a loop tick and
110 * clear this bit again OR become teardown itself */
111 a_GO_XCALL_LOOP
= 1u<<25, /* `xcall' optimization barrier level */
112 a_GO_XCALL_LOOP_ERROR
= 1u<<26, /* .. state machine error transporter */
113 a_GO_XCALL_LOOP_MASK
= a_GO_XCALL_LOOP
| a_GO_XCALL_LOOP_ERROR
116 enum a_go_cleanup_mode
{
117 a_GO_CLEANUP_UNWIND
= 1u<<0, /* Teardown all contexts except outermost */
118 a_GO_CLEANUP_TEARDOWN
= 1u<<1, /* Teardown current context */
119 a_GO_CLEANUP_LOOPTICK
= 1u<<2, /* Normal looptick cleanup */
120 a_GO_CLEANUP_MODE_MASK
= n_BITENUM_MASK(0, 2),
122 a_GO_CLEANUP_ERROR
= 1u<<8, /* Error occurred on level */
123 a_GO_CLEANUP_SIGINT
= 1u<<9, /* Interrupt signal received */
124 a_GO_CLEANUP_HOLDALLSIGS
= 1u<<10 /* hold_all_sigs() active TODO */
127 enum a_go_hist_flags
{
129 a_GO_HIST_ADD
= 1u<<0,
130 a_GO_HIST_GABBY
= 1u<<1,
131 a_GO_HIST_INIT
= 1u<<2
134 struct a_go_eval_ctx
{
135 struct str gec_line
; /* The terminated data to _evaluate() */
136 ui32_t gec_line_size
; /* May be used to store line memory size */
137 bool_t gec_ever_seen
; /* Has ever been used (main_loop() only) */
139 ui8_t gec_hist_flags
; /* enum a_go_hist_flags */
140 char const *gec_hist_cmd
; /* If a_GO_HIST_ADD only, cmd and args */
141 char const *gec_hist_args
;
144 struct a_go_input_inject
{
145 struct a_go_input_inject
*gii_next
;
148 bool_t gii_no_history
;
149 char gii_dat
[n_VFIELD_SIZE(6)];
153 struct a_go_ctx
*gc_outer
;
154 sigset_t gc_osigmask
;
155 ui32_t gc_flags
; /* enum a_go_flags */
156 ui32_t gc_loff
; /* Pseudo (macro): index in .gc_lines */
157 char **gc_lines
; /* Pseudo content, lines unfolded */
158 FILE *gc_file
; /* File we were in, if applicable */
159 struct a_go_input_inject
*gc_inject
; /* To be consumed first */
160 void (*gc_on_finalize
)(void *);
161 void *gc_finalize_arg
;
162 sigjmp_buf gc_eloop_jmp
; /* TODO one day... for _event_loop() */
163 /* SPLICE hacks: saved stdin/stdout, saved pstate */
164 FILE *gc_splice_stdin
;
165 FILE *gc_splice_stdout
;
166 ui32_t gc_splice_psonce
;
167 ui8_t gc_splice__dummy
[4];
168 struct n_go_data_ctx gc_data
;
169 char gc_name
[n_VFIELD_SIZE(0)]; /* Name of file or macro */
172 struct a_go_readctl_ctx
{ /* TODO localize readctl_read_overlay: OnForkEvent! */
173 struct a_go_readctl_ctx
*grc_last
;
174 struct a_go_readctl_ctx
*grc_next
;
175 char const *grc_expand
; /* If filename based, expanded string */
177 si32_t grc_fd
; /* Based upon file-descriptor */
178 char grc_name
[n_VFIELD_SIZE(4)]; /* User input for identification purposes */
181 static sighandler_type a_go_oldpipe
;
182 /* a_go_cmd_tab[] after fun protos */
184 /* Our current execution context, and the buffer backing the outermost level */
185 static struct a_go_ctx
*a_go_ctx
;
187 #define a_GO_MAINCTX_NAME "Main event loop"
190 char uf
[n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
) +
191 sizeof(a_GO_MAINCTX_NAME
)];
194 /* `xcall' stack-avoidance bypass optimization. This actually is
195 * a n_cmd_arg_save_to_heap() buffer with n_cmd_arg_ctx.cac_indat misused to
196 * point to the a_go_ctx to unroll up to */
197 static void *a_go_xcall
;
199 static sigjmp_buf a_go_srbuf
; /* TODO GET RID */
201 /* n_PS_STATE_PENDMASK requires some actions */
202 static void a_go_update_pstate(void);
204 /* Evaluate a single command */
205 static bool_t
a_go_evaluate(struct a_go_eval_ctx
*gecp
);
207 /* Branch here on hangup signal and simulate "exit" */
208 static void a_go_hangup(int s
);
210 /* The following gets called on receipt of an interrupt */
211 static void a_go_onintr(int s
);
213 /* Cleanup current execution context, update the program state.
214 * If _CLEANUP_ERROR is set then we don't alert and error out if the stack
215 * doesn't exist at all, unless _CLEANUP_HOLDALLSIGS we hold_all_sigs() */
216 static void a_go_cleanup(enum a_go_cleanup_mode gcm
);
218 /* `source' and `source_if' (if silent_open_error: no pipes allowed, then).
219 * Returns FAL0 if file is somehow not usable (unless silent_open_error) or
220 * upon evaluation error, and TRU1 on success */
221 static bool_t
a_go_file(char const *file
, bool_t silent_open_error
);
223 /* System resource file load()ing or -X command line option array traversal */
224 static bool_t
a_go_load(struct a_go_ctx
*gcp
);
226 /* A simplified command loop for recursed state machines */
227 static bool_t
a_go_event_loop(struct a_go_ctx
*gcp
, enum n_go_input_flags gif
);
230 a_go_update_pstate(void){
234 act
= ((n_pstate
& n_PS_SIGWINCH_PEND
) != 0);
235 n_pstate
&= ~n_PS_PSTATE_PENDMASK
;
240 snprintf(buf
, sizeof buf
, "%d", n_scrnwidth
);
241 ok_vset(COLUMNS
, buf
);
242 snprintf(buf
, sizeof buf
, "%d", n_scrnheight
);
249 a_go_evaluate(struct a_go_eval_ctx
*gecp
){
250 /* xxx old style(9), but also old code */
251 /* TODO a_go_evaluate() should be splitted in multiple subfunctions,
252 * TODO `eval' should be a prefix, etc., a Ctx should be passed along */
254 struct n_string s
, *sp
;
255 struct str
const *alias_exp
;
256 char _wordbuf
[2], *argv_stack
[3], **argv_base
, **argvp
, *vput
, *cp
, *word
;
257 char const *alias_name
;
258 struct n_cmd_desc
const *cdp
;
259 si32_t nerrn
, nexn
; /* TODO n_pstate_ex_no -> si64_t! */
263 a_ALIAS_MASK
= n_BITENUM_MASK(0, 2), /* Alias recursion counter bits */
264 a_NOPREFIX
= 1u<<4, /* Modifier prefix not allowed right now */
265 a_NOALIAS
= 1u<<5, /* "No alias!" expansion modifier */
266 a_IGNERR
= 1u<<6, /* ignerr modifier prefix */
267 a_LOCAL
= 1u<<7, /* local modifier prefix */
268 a_SCOPE
= 1u<<8, /* TODO scope modifier prefix */
269 a_U
= 1u<<9, /* TODO UTF-8 modifier prefix */
270 a_VPUT
= 1u<<10, /* vput modifier prefix */
271 a_WYSH
= 1u<<11, /* XXX v15+ drop wysh modifier prefix */
272 a_MODE_MASK
= n_BITENUM_MASK(5, 11),
273 a_NO_ERRNO
= 1u<<16 /* Don't set n_pstate_err_no */
277 if(!(n_psonce
& n_PSO_EXIT_MASK
) && !(n_pstate
& n_PS_ERR_EXIT_MASK
))
278 n_exit_status
= n_EXIT_OK
;
287 n_UNINIT(alias_exp
, NULL
);
288 line
= gecp
->gec_line
; /* TODO const-ify original (buffer)! */
289 assert(line
.s
[line
.l
] == '\0');
291 if(line
.l
> 0 && spacechar(line
.s
[0]))
292 gecp
->gec_hist_flags
= a_GO_HIST_NONE
;
293 else if(gecp
->gec_hist_flags
& a_GO_HIST_ADD
)
294 gecp
->gec_hist_cmd
= gecp
->gec_hist_args
= NULL
;
297 /* Aliases that refer to shell commands or macro expansion restart */
299 if(n_str_trim_ifs(&line
, TRU1
)->l
== 0){
301 gecp
->gec_hist_flags
= a_GO_HIST_NONE
;
304 (cp
= line
.s
)[line
.l
] = '\0';
306 /* No-expansion modifier? */
307 if(!(flags
& a_NOPREFIX
) && *cp
== '\\'){
313 /* Note: adding more special treatments must be reflected in the `help' etc.
314 * output in cmd-tab.c! */
316 /* Ignore null commands (comments) */
318 gecp
->gec_hist_flags
= a_GO_HIST_NONE
;
322 /* Handle ! differently to get the correct lexical conventions */
325 /* Isolate the actual command; since it may not necessarily be
326 * separated from the arguments (as in `p1') we need to duplicate it to
327 * be able to create a NUL terminated version.
328 * We must be aware of several special one letter commands here */
329 else if((cp
= n_UNCONST(n_cmd_isolate(cp
))) == line
.s
&&
330 (*cp
== '|' || *cp
== '?'))
332 c
= (int)PTR2SIZE(cp
- line
.s
);
333 word
= UICMP(z
, c
, <, sizeof _wordbuf
) ? _wordbuf
: n_autorec_alloc(c
+1);
334 memcpy(word
, line
.s
, c
);
339 /* It may be a modifier.
340 * NOTE: changing modifiers must be reflected in `commandalias' handling
341 * code as well as in the manual (of course)! */
345 case sizeof("ignerr") -1:
346 if(!asccasecmp(word
, "ignerr")){
347 flags
|= a_NOPREFIX
| a_IGNERR
;
351 /*case sizeof("scope") -1:*/
352 case sizeof("local") -1:
353 if(!asccasecmp(word
, "local")){
354 flags
|= a_NOPREFIX
| a_LOCAL
;
356 }else if(!asccasecmp(word
, "scope")){
357 /* This will be an extended per-command `localopts' */
358 n_err(_("Ignoring yet unused `scope' command modifier!"));
359 flags
|= a_NOPREFIX
| a_SCOPE
;
364 if(!asccasecmp(word
, "u")){
365 n_err(_("Ignoring yet unused `u' command modifier!"));
366 flags
|= a_NOPREFIX
| a_U
;
370 /*case sizeof("vput") -1:*/
371 case sizeof("wysh") -1:
372 if(!asccasecmp(word
, "wysh")){
373 flags
|= a_NOPREFIX
| a_WYSH
;
375 }else if(!asccasecmp(word
, "vput")){
376 flags
|= a_NOPREFIX
| a_VPUT
;
382 /* We need to trim for a possible history entry, but do it anyway and insert
383 * a space for argument separation in case of alias expansion. Also, do
384 * terminate again because nothing prevents aliases from introducing WS */
385 n_str_trim_ifs(&line
, TRU1
);
386 line
.s
[line
.l
] = '\0';
388 /* Lengthy history entry setup, possibly even redundant. But having
389 * normalized history entries is a good thing, and this is maybe still
390 * cheaper than parsing a StrList of words per se */
391 if((gecp
->gec_hist_flags
& (a_GO_HIST_ADD
| a_GO_HIST_INIT
)
394 sp
= n_string_creat_auto(&s
);
395 sp
= n_string_assign_buf(sp
, line
.s
, line
.l
);
396 gecp
->gec_hist_args
= n_string_cp(sp
);
399 sp
= n_string_creat_auto(&s
);
400 sp
= n_string_reserve(sp
, 32);
402 if(flags
& a_NOALIAS
)
403 sp
= n_string_push_c(sp
, '\\');
405 sp
= n_string_push_buf(sp
, "ignerr ", sizeof("ignerr ") -1);
407 sp
= n_string_push_buf(sp
, "wysh ", sizeof("wysh ") -1);
409 sp
= n_string_push_buf(sp
, "vput ", sizeof("vput ") -1);
410 gecp
->gec_hist_flags
= a_GO_HIST_ADD
| a_GO_HIST_INIT
;
413 /* Look up the command; if not found, bitch.
414 * Normally, a blank command would map to the first command in the
415 * table; while n_PS_SOURCING, however, we ignore blank lines to eliminate
416 * confusion; act just the same for aliases */
419 if((n_pstate
& n_PS_ROBOT
) || !(n_psonce
& n_PSO_INTERACTIVE
) ||
421 gecp
->gec_hist_flags
= a_GO_HIST_NONE
;
424 cdp
= n_cmd_default();
428 if(!(flags
& a_NOALIAS
) && (flags
& a_ALIAS_MASK
) != a_ALIAS_MASK
){
431 expcnt
= (flags
& a_ALIAS_MASK
);
433 flags
= (flags
& ~(a_ALIAS_MASK
| a_NOPREFIX
)) | expcnt
;
435 /* Avoid self-recursion; since a commandalias can shadow a command of
436 * equal name allow one level of expansion to return an equal result:
437 * "commandalias q q;commandalias x q;x" should be "x->q->q->quit".
438 * P.S.: should also work for "help x" ... */
439 if(alias_name
!= NULL
&& !strcmp(word
, alias_name
))
442 if((alias_name
= n_commandalias_exists(word
, &alias_exp
)) != NULL
){
446 sp
= n_string_push_cp(sp
, word
);
447 gecp
->gec_hist_cmd
= n_string_cp(sp
);
451 /* And join arguments onto alias expansion */
455 line
.s
= n_autorec_alloc(i
+ 1 + line
.l
+1);
456 memcpy(line
.s
, alias_exp
->s
, i
);
459 memcpy(&line
.s
[i
], cp
, line
.l
);
461 line
.s
[i
+= line
.l
] = '\0';
467 if((cdp
= n_cmd_firstfit(word
)) == NULL
){
470 if(!(doskip
= n_cnd_if_isskip()) || (n_poption
& n_PO_D_V
))
471 n_err(_("Unknown command%s: `%s'\n"),
472 (doskip
? _(" (ignored due to `if' condition)") : n_empty
),
474 gecp
->gec_hist_flags
= a_GO_HIST_NONE
;
481 /* See if we should execute the command -- if a conditional we always
482 * execute it, otherwise, check the state of cond */
484 if(!(cdp
->cd_caflags
& n_CMD_ARG_F
) && n_cnd_if_isskip()){
485 gecp
->gec_hist_flags
= a_GO_HIST_NONE
;
490 sp
= n_string_push_cp(sp
, cdp
->cd_name
);
491 gecp
->gec_hist_cmd
= n_string_cp(sp
);
497 /* Process the arguments to the command, depending on the type it expects */
498 if((cdp
->cd_caflags
& n_CMD_ARG_I
) && !(n_psonce
& n_PSO_INTERACTIVE
) &&
499 !(n_poption
& n_PO_BATCH_FLAG
)){
500 n_err(_("May not execute `%s' unless interactive or in batch mode\n"),
504 if(!(cdp
->cd_caflags
& n_CMD_ARG_M
) && (n_psonce
& n_PSO_SENDMODE
)){
505 n_err(_("May not execute `%s' while sending\n"), cdp
->cd_name
);
508 if(cdp
->cd_caflags
& n_CMD_ARG_R
){
509 if(n_pstate
& n_PS_COMPOSE_MODE
){
510 /* TODO n_PS_COMPOSE_MODE: should allow `reply': ~:reply! */
511 n_err(_("Cannot invoke `%s' when in compose mode\n"), cdp
->cd_name
);
514 /* TODO Nothing should prevent n_CMD_ARG_R in conjunction with
515 * TODO n_PS_ROBOT|_SOURCING; see a.._may_yield_control()! */
516 if(n_pstate
& (n_PS_ROBOT
| n_PS_SOURCING
) && !n_go_may_yield_control()){
517 n_err(_("Cannot invoke `%s' in this program state\n"),
522 if((cdp
->cd_caflags
& n_CMD_ARG_S
) && !(n_psonce
& n_PSO_STARTED_CONFIG
)){
523 n_err(_("May not execute `%s' during startup\n"), cdp
->cd_name
);
526 if(!(cdp
->cd_caflags
& n_CMD_ARG_X
) && (n_pstate
& n_PS_COMPOSE_FORKHOOK
)){
527 n_err(_("Cannot invoke `%s' from a hook running in a child process\n"),
532 if((cdp
->cd_caflags
& n_CMD_ARG_A
) && mb
.mb_type
== MB_VOID
){
533 n_err(_("Cannot execute `%s' without active mailbox\n"), cdp
->cd_name
);
536 if((cdp
->cd_caflags
& n_CMD_ARG_W
) && !(mb
.mb_perm
& MB_DELE
)){
537 n_err(_("May not execute `%s' -- message file is read only\n"),
542 if(cdp
->cd_caflags
& n_CMD_ARG_O
)
543 n_OBSOLETE2(_("this command will be removed"), cdp
->cd_name
);
545 /* TODO v15: strip n_PS_ARGLIST_MASK off, just in case the actual command
546 * TODO doesn't use any of those list commands which strip this mask,
547 * TODO and for now we misuse bits for checking relation to history;
548 * TODO argument state should be property of a per-command carrier instead */
549 n_pstate
&= ~n_PS_ARGLIST_MASK
;
551 if((flags
& a_WYSH
) &&
552 (cdp
->cd_caflags
& n_CMD_ARG_TYPE_MASK
) != n_CMD_ARG_TYPE_WYRA
){
553 n_err(_("`wysh' command modifier does not affect `%s'\n"), cdp
->cd_name
);
558 if(!(cdp
->cd_caflags
& n_CMD_ARG_L
)){
559 n_err(_("`local' command modifier does not affect `%s'\n"),
564 n_pstate
|= n_PS_ARGMOD_LOCAL
; /* TODO YET useless since stripped later
565 * TODO on in getrawlist() etc., i.e., the argument vector producers,
566 * TODO therefore yet needs to be set again based on flags&a_LOCAL! */
570 if(cdp
->cd_caflags
& n_CMD_ARG_V
){
573 emsg
= line
.s
; /* xxx Cannot pass &char* as char const**, so no cp */
574 vput
= n_shexp_parse_token_cp((n_SHEXP_PARSE_TRIM_SPACE
|
575 n_SHEXP_PARSE_TRIM_IFSSPACE
| n_SHEXP_PARSE_LOG
|
576 n_SHEXP_PARSE_META_SEMICOLON
| n_SHEXP_PARSE_META_KEEP
), &emsg
);
577 line
.l
-= PTR2SIZE(emsg
- line
.s
);
578 line
.s
= cp
= n_UNCONST(emsg
);
580 emsg
= N_("could not parse input token");
581 else if(!n_shexp_is_valid_varname(vput
))
582 emsg
= N_("not a valid variable name");
583 else if(!n_var_is_user_writable(vput
))
584 emsg
= N_("either not a user writable, or a boolean variable");
588 n_err("`%s': vput: %s: %s\n",
589 cdp
->cd_name
, V_(emsg
), n_shexp_quote_cp(vput
, FAL0
));
590 nerrn
= n_ERR_NOTSUP
;
594 n_pstate
|= n_PS_ARGMOD_VPUT
; /* TODO YET useless since stripped later
595 * TODO on in getrawlist() etc., i.e., the argument vector producers,
596 * TODO therefore yet needs to be set again based on flags&a_VPUT! */
598 n_err(_("`vput' prefix does not affect `%s'\n"), cdp
->cd_name
);
603 switch(cdp
->cd_caflags
& n_CMD_ARG_TYPE_MASK
){
604 case n_CMD_ARG_TYPE_MSGLIST
:
605 /* Message list defaulting to nearest forward legal message */
608 if((c
= n_getmsglist(line
.s
, n_msgvec
, cdp
->cd_msgflag
, NULL
)) < 0){
614 if((n_msgvec
[0] = first(cdp
->cd_msgflag
, cdp
->cd_msgmask
)) != 0)
618 if(!(n_pstate
& (n_PS_HOOK_MASK
| n_PS_ROBOT
)) ||
619 (n_poption
& n_PO_D_V
))
620 fprintf(n_stdout
, _("No applicable messages\n"));
630 mvp
= n_autorec_calloc(c
+1, sizeof *mvp
);
632 mvp
[c
] = n_msgvec
[c
];
633 if(!(flags
& a_NO_ERRNO
) && !(cdp
->cd_caflags
& n_CMD_ARG_EM
)) /*XXX*/
635 rv
= (*cdp
->cd_func
)(mvp
);
639 case n_CMD_ARG_TYPE_NDMLIST
:
640 /* Message list with no defaults, but no error if none exist */
643 if((c
= n_getmsglist(line
.s
, n_msgvec
, cdp
->cd_msgflag
, NULL
)) < 0){
650 case n_CMD_ARG_TYPE_STRING
:
651 /* Just the straight string, old style, with leading blanks removed */
652 for(cp
= line
.s
; spacechar(*cp
);)
654 if(!(flags
& a_NO_ERRNO
) && !(cdp
->cd_caflags
& n_CMD_ARG_EM
)) /* XXX */
656 rv
= (*cdp
->cd_func
)(cp
);
659 case n_CMD_ARG_TYPE_RAWDAT
:
660 /* Just the straight string, placed in argv[] */
666 if(!(flags
& a_NO_ERRNO
) && !(cdp
->cd_caflags
& n_CMD_ARG_EM
)) /* XXX */
668 rv
= (*cdp
->cd_func
)(argv_stack
);
671 case n_CMD_ARG_TYPE_WYSH
:
675 case n_CMD_ARG_TYPE_WYRA
:
676 c
= (flags
& a_WYSH
) ? 1 : 0;
678 case n_CMD_ARG_TYPE_RAWLIST
:
682 argvp
= argv_base
= n_autorec_alloc(sizeof(*argv_base
) * n_MAXARGC
);
685 if((c
= getrawlist((c
!= 0), argvp
,
686 (n_MAXARGC
- ((flags
& a_VPUT
) != 0)), line
.s
, line
.l
)) < 0){
687 n_err(_("Invalid argument list\n"));
692 if(UICMP(32, c
, <, cdp
->cd_minargs
)){
693 n_err(_("`%s' requires at least %u arg(s)\n"),
694 cdp
->cd_name
, (ui32_t
)cdp
->cd_minargs
);
699 if(UICMP(32, c
, >, cdp
->cd_maxargs
)){
700 n_err(_("`%s' takes no more than %u arg(s)\n"),
701 cdp
->cd_name
, (ui32_t
)cdp
->cd_maxargs
);
708 n_pstate
|= n_PS_ARGMOD_LOCAL
;
710 n_pstate
|= n_PS_ARGMOD_VPUT
; /* TODO due to getrawlist(), as above */
712 if(!(flags
& a_NO_ERRNO
) && !(cdp
->cd_caflags
& n_CMD_ARG_EM
)) /* XXX */
714 rv
= (*cdp
->cd_func
)(argv_base
);
715 if(a_go_xcall
!= NULL
)
719 case n_CMD_ARG_TYPE_ARG
:{
720 /* TODO The _ARG_TYPE_ARG is preliminary, in the end we should have a
721 * TODO per command-ctx carrier that also has slots for it arguments,
722 * TODO and that should be passed along all the way. No more arglists
724 struct n_cmd_arg_ctx cac
;
726 cac
.cac_desc
= cdp
->cd_cadp
;
727 cac
.cac_indat
= line
.s
;
728 cac
.cac_inlen
= line
.l
;
729 cac
.cac_msgflag
= cdp
->cd_msgflag
;
730 cac
.cac_msgmask
= cdp
->cd_msgmask
;
731 if(!n_cmd_arg_parse(&cac
)){
738 /* Global "hack" not used: n_pstate |= n_PS_ARGMOD_VPUT; */
742 if(!(flags
& a_NO_ERRNO
) && !(cdp
->cd_caflags
& n_CMD_ARG_EM
)) /* XXX */
744 rv
= (*cdp
->cd_func
)(&cac
);
745 if(a_go_xcall
!= NULL
)
750 DBG( n_panic(_("Implementation error: unknown argument type: %d"),
751 cdp
->cd_caflags
& n_CMD_ARG_TYPE_MASK
); )
752 nerrn
= n_ERR_NOTOBACCO
;
757 if(gecp
->gec_hist_flags
& a_GO_HIST_ADD
){
758 if(cdp
->cd_caflags
& n_CMD_ARG_H
)
759 gecp
->gec_hist_flags
= a_GO_HIST_NONE
;
760 else if((cdp
->cd_caflags
& n_CMD_ARG_G
) ||
761 (n_pstate
& n_PS_MSGLIST_GABBY
))
762 gecp
->gec_hist_flags
|= a_GO_HIST_GABBY
;
766 if(!(flags
& a_NO_ERRNO
)){
767 if(cdp
->cd_caflags
& n_CMD_ARG_EM
)
769 else if((nerrn
= n_err_no
) == 0)
772 flags ^= a_NO_ERRNO;*/
773 }else if(cdp
->cd_caflags
& n_CMD_ARG_EM
)
780 if(flags
& a_IGNERR
){
781 if(!(n_psonce
& n_PSO_EXIT_MASK
) && !(n_pstate
& n_PS_ERR_EXIT_MASK
))
782 n_exit_status
= n_EXIT_OK
;
783 n_pstate
&= ~n_PS_ERR_EXIT_MASK
;
787 if((bo
= ok_blook(batch_exit_on_error
))){
788 n_OBSOLETE(_("please use *errexit*, not *batch-exit-on-error*"));
789 if(!(n_poption
& n_PO_BATCH_FLAG
))
792 if(ok_blook(errexit
) || bo
) /* TODO v15: drop bo */
793 n_pstate
|= n_PS_ERR_QUIT
;
794 else if(ok_blook(posix
)){
795 if(n_psonce
& n_PSO_STARTED
)
797 else if(!(n_psonce
& n_PSO_INTERACTIVE
))
798 n_pstate
|= n_PS_ERR_XIT
;
803 if(n_exit_status
== n_EXIT_OK
)
804 n_exit_status
= n_EXIT_ERR
;
805 if((n_poption
& n_PO_D_V
) &&
806 !(n_psonce
& (n_PSO_INTERACTIVE
| n_PSO_STARTED
)))
807 n_alert(_("Non-interactive, bailing out due to errors "
808 "in startup load phase"));
815 if((cdp
->cd_caflags
& n_CMD_ARG_P
) && ok_blook(autoprint
))
817 n_go_input_inject(n_GO_INPUT_INJECT_COMMIT
, "\\type",
818 sizeof("\\type") -1);
820 if(!(n_pstate
& (n_PS_SOURCING
| n_PS_HOOK_MASK
)) &&
821 !(cdp
->cd_caflags
& n_CMD_ARG_T
))
822 n_pstate
|= n_PS_SAW_COMMAND
;
826 if(!(flags
& a_NO_ERRNO
))
827 n_pstate_err_no
= nerrn
;
828 n_pstate_ex_no
= nexn
;
835 NYD_X
; /* Signal handler */
842 FL
void n_go_onintr_for_imap(void){a_go_onintr(0);}
845 a_go_onintr(int s
){ /* TODO block signals while acting */
846 NYD_X
; /* Signal handler */
849 safe_signal(SIGINT
, a_go_onintr
);
851 termios_state_reset();
853 a_go_cleanup(a_GO_CLEANUP_UNWIND
| /* XXX FAKE */a_GO_CLEANUP_HOLDALLSIGS
);
856 n_err_sighdl(_("Interrupt\n"));
857 safe_signal(SIGPIPE
, a_go_oldpipe
);
858 siglongjmp(a_go_srbuf
, 0); /* FIXME get rid */
862 a_go_cleanup(enum a_go_cleanup_mode gcm
){
863 /* Signals blocked */
864 struct a_go_ctx
*gcp
;
867 if(!(gcm
& a_GO_CLEANUP_HOLDALLSIGS
))
872 /* Free input injections of this level first */
873 if(!(gcm
& a_GO_CLEANUP_LOOPTICK
)){
874 struct a_go_input_inject
**giipp
, *giip
;
876 for(giipp
= &gcp
->gc_inject
; (giip
= *giipp
) != NULL
;){
877 *giipp
= giip
->gii_next
;
882 /* Cleanup non-crucial external stuff */
884 if(gcp
->gc_data
.gdc_colour
!= NULL
)
885 n_colour_stack_del(&gcp
->gc_data
);
888 /* Work the actual context (according to cleanup mode) */
889 if(gcp
->gc_outer
== NULL
){
890 if(gcm
& (a_GO_CLEANUP_UNWIND
| a_GO_CLEANUP_SIGINT
)){
891 if(a_go_xcall
!= NULL
){
895 gcp
->gc_flags
&= ~a_GO_XCALL_LOOP_MASK
;
896 n_pstate
&= ~n_PS_ERR_EXIT_MASK
;
899 if(!(n_pstate
& n_PS_SOURCING
))
905 n_pstate
&= ~(n_PS_SOURCING
| n_PS_ROBOT
);
906 assert(a_go_xcall
== NULL
);
907 assert(!(gcp
->gc_flags
& a_GO_XCALL_LOOP_MASK
));
908 assert(gcp
->gc_on_finalize
== NULL
);
909 n_COLOUR( assert(gcp
->gc_data
.gdc_colour
== NULL
); )
911 }else if(gcm
& a_GO_CLEANUP_LOOPTICK
){
914 }else if(gcp
->gc_flags
& a_GO_SPLICE
){ /* TODO Temporary hack */
915 n_stdin
= gcp
->gc_splice_stdin
;
916 n_stdout
= gcp
->gc_splice_stdout
;
917 n_psonce
= gcp
->gc_splice_psonce
;
921 /* Cleanup crucial external stuff */
922 if(gcp
->gc_data
.gdc_ifcond
!= NULL
){
923 n_cnd_if_stack_del(&gcp
->gc_data
);
924 if(!(gcm
& (a_GO_CLEANUP_ERROR
| a_GO_CLEANUP_SIGINT
)) &&
925 !(gcp
->gc_flags
& a_GO_FORCE_EOF
) && a_go_xcall
== NULL
&&
926 !(n_psonce
& n_PSO_EXIT_MASK
)){
927 n_err(_("Unmatched `if' at end of %s %s\n"),
928 ((gcp
->gc_flags
& a_GO_MACRO
929 ? (gcp
->gc_flags
& a_GO_MACRO_CMD
? _("command") : _("macro"))
930 : _("`source'd file"))),
932 gcm
|= a_GO_CLEANUP_ERROR
;
936 /* Teardown context */
937 if(gcp
->gc_flags
& a_GO_MACRO
){
938 if(gcp
->gc_flags
& a_GO_MACRO_FREE_DATA
){
941 while(*(lp
= &gcp
->gc_lines
[gcp
->gc_loff
]) != NULL
){
945 /* Part of gcp's memory chunk, then */
946 if(!(gcp
->gc_flags
& a_GO_MACRO_CMD
))
947 n_free(gcp
->gc_lines
);
949 }else if(gcp
->gc_flags
& a_GO_PIPE
)
950 /* XXX command manager should -TERM then -KILL instead of hoping
951 * XXX for exit of provider due to n_ERR_PIPE / SIGPIPE */
952 Pclose(gcp
->gc_file
, TRU1
);
953 else if(gcp
->gc_flags
& a_GO_FILE
)
954 Fclose(gcp
->gc_file
);
956 if(!(gcp
->gc_flags
& a_GO_MEMPOOL_INHERITED
)){
957 if(gcp
->gc_data
.gdc_mempool
!= NULL
)
958 n_memory_pool_pop(NULL
, TRU1
);
963 /* Update a_go_ctx and n_go_data, n_pstate ... */
964 a_go_ctx
= gcp
->gc_outer
;
965 assert(a_go_ctx
!= NULL
);
969 for(x
= a_go_ctx
; x
->gc_flags
& a_GO_DATACTX_INHERITED
;){
973 n_go_data
= &x
->gc_data
;
976 if((a_go_ctx
->gc_flags
& (a_GO_MACRO
| a_GO_SUPER_MACRO
)) ==
977 (a_GO_MACRO
| a_GO_SUPER_MACRO
)){
978 n_pstate
&= ~n_PS_SOURCING
;
979 assert(n_pstate
& n_PS_ROBOT
);
980 }else if(!(a_go_ctx
->gc_flags
& a_GO_TYPE_MASK
))
981 n_pstate
&= ~(n_PS_SOURCING
| n_PS_ROBOT
);
983 assert(n_pstate
& n_PS_ROBOT
);
985 if(gcp
->gc_on_finalize
!= NULL
)
986 (*gcp
->gc_on_finalize
)(gcp
->gc_finalize_arg
);
988 if(gcm
& a_GO_CLEANUP_ERROR
){
989 if(a_go_ctx
->gc_flags
& a_GO_XCALL_LOOP
)
990 a_go_ctx
->gc_flags
|= a_GO_XCALL_LOOP_ERROR
;
994 if(gcp
->gc_flags
& a_GO_FREE
)
997 if(n_UNLIKELY((gcm
& a_GO_CLEANUP_UNWIND
) && gcp
!= a_go_ctx
))
1002 if(!(gcm
& a_GO_CLEANUP_HOLDALLSIGS
))
1007 /* With *posix* we follow what POSIX says:
1008 * Any errors in the start-up file shall either cause mailx to
1009 * terminate with a diagnostic message and a non-zero status or to
1010 * continue after writing a diagnostic message, ignoring the
1011 * remainder of the lines in the start-up file
1012 * Print the diagnostic only for the outermost resource unless the user
1013 * is debugging or in verbose mode */
1014 if((n_poption
& n_PO_D_V
) ||
1015 (!(n_psonce
& n_PSO_STARTED
) &&
1016 !(gcp
->gc_flags
& (a_GO_SPLICE
| a_GO_MACRO
)) &&
1017 !(gcp
->gc_outer
->gc_flags
& a_GO_TYPE_MASK
)))
1018 /* I18N: file inclusion, macro etc. evaluation has been stopped */
1019 n_alert(_("Stopped %s %s due to errors%s"),
1020 (n_psonce
& n_PSO_STARTED
1021 ? (gcp
->gc_flags
& a_GO_SPLICE
? _("spliced in program")
1022 : (gcp
->gc_flags
& a_GO_MACRO
1023 ? (gcp
->gc_flags
& a_GO_MACRO_CMD
1024 ? _("evaluating command") : _("evaluating macro"))
1025 : (gcp
->gc_flags
& a_GO_PIPE
1026 ? _("executing `source'd pipe")
1027 : (gcp
->gc_flags
& a_GO_FILE
1028 ? _("loading `source'd file") : _(a_GO_MAINCTX_NAME
))))
1030 : (gcp
->gc_flags
& a_GO_MACRO
1031 ? (gcp
->gc_flags
& a_GO_MACRO_X_OPTION
1032 ? _("evaluating command line") : _("evaluating macro"))
1033 : _("loading initialization resource"))),
1034 n_shexp_quote_cp(gcp
->gc_name
, FAL0
),
1035 (n_poption
& n_PO_DEBUG
? n_empty
: _(" (enable *debug* for trace)")));
1040 a_go_file(char const *file
, bool_t silent_open_error
){
1041 struct a_go_ctx
*gcp
;
1051 /* Being a command argument file is space-trimmed *//* TODO v15 with
1052 * TODO WYRALIST this is no longer necessary true, and for that we
1053 * TODO don't set _PARSE_TRIM_SPACE because we cannot! -> cmd-tab.h!! */
1055 ((ispipe
= (!silent_open_error
&& (nlen
= strlen(file
)) > 0 &&
1056 file
[--nlen
] == '|')))
1059 if(!silent_open_error
){
1060 for(nlen
= strlen(file
); nlen
> 0;){
1066 nbuf
= savestrbuf(file
, nlen
);
1076 if((fip
= Popen(nbuf
/* #if 0 above = savestrbuf(file, nlen)*/, "r",
1077 ok_vlook(SHELL
), NULL
, n_CHILD_FD_NULL
)) == NULL
)
1079 }else if((nbuf
= fexpand(file
, FEXP_LOCAL
| FEXP_NVAR
)) == NULL
)
1081 else if((fip
= Fopen(nbuf
, "r")) == NULL
){
1083 if(!silent_open_error
|| (n_poption
& n_PO_D_V
))
1085 if(silent_open_error
)
1090 sigprocmask(SIG_BLOCK
, NULL
, &osigmask
);
1092 gcp
= n_alloc(n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
) +
1093 (nlen
= strlen(nbuf
) +1));
1094 memset(gcp
, 0, n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
));
1098 gcp
->gc_outer
= a_go_ctx
;
1099 gcp
->gc_osigmask
= osigmask
;
1101 gcp
->gc_flags
= (ispipe
? a_GO_FREE
| a_GO_PIPE
: a_GO_FREE
| a_GO_FILE
) |
1102 (a_go_ctx
->gc_flags
& a_GO_SUPER_MACRO
? a_GO_SUPER_MACRO
: 0);
1103 memcpy(gcp
->gc_name
, nbuf
, nlen
);
1106 n_go_data
= &gcp
->gc_data
;
1107 n_pstate
|= n_PS_SOURCING
| n_PS_ROBOT
;
1108 if(!a_go_event_loop(gcp
, n_GO_INPUT_NONE
| n_GO_INPUT_NL_ESC
))
1112 return (fip
!= NULL
);
1116 a_go_load(struct a_go_ctx
*gcp
){
1119 assert(!(n_psonce
& n_PSO_STARTED
));
1120 assert(!(a_go_ctx
->gc_flags
& a_GO_TYPE_MASK
));
1122 gcp
->gc_flags
|= a_GO_MEMPOOL_INHERITED
;
1123 gcp
->gc_data
.gdc_mempool
= n_go_data
->gdc_mempool
;
1128 * Any errors in the start-up file shall either cause mailx to terminate
1129 * with a diagnostic message and a non-zero status or to continue after
1130 * writing a diagnostic message, ignoring the remainder of the lines in
1131 * the start-up file. */
1132 gcp
->gc_outer
= a_go_ctx
;
1134 n_go_data
= &gcp
->gc_data
;
1135 /* FIXME won't work for now (n_PS_ROBOT needs n_PS_SOURCING sofar)
1136 n_pstate |= n_PS_ROBOT |
1137 (gcp->gc_flags & a_GO_MACRO_X_OPTION ? 0 : n_PS_SOURCING);
1139 n_pstate
|= n_PS_ROBOT
| n_PS_SOURCING
;
1145 return (((n_psonce
& n_PSO_EXIT_MASK
) |
1146 (n_pstate
& n_PS_ERR_EXIT_MASK
)) == 0);
1150 a_go__eloopint(int sig
){ /* TODO one day, we don't need it no more */
1151 NYD_X
; /* Signal handler */
1153 siglongjmp(a_go_ctx
->gc_eloop_jmp
, 1);
1157 a_go_event_loop(struct a_go_ctx
*gcp
, enum n_go_input_flags gif
){
1158 sighandler_type soldhdl
;
1159 struct a_go_eval_ctx gec
;
1160 enum {a_RETOK
= TRU1
, a_TICKED
= 1<<1} volatile f
;
1161 volatile int hadint
; /* TODO get rid of shitty signal stuff (see signal.c) */
1165 memset(&gec
, 0, sizeof gec
);
1166 osigmask
= gcp
->gc_osigmask
;
1170 if((soldhdl
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
){
1171 safe_signal(SIGINT
, &a_go__eloopint
);
1172 if(sigsetjmp(gcp
->gc_eloop_jmp
, 1)){
1176 gcp
->gc_flags
&= ~a_GO_XCALL_LOOP_MASK
;
1181 for(;; f
|= a_TICKED
){
1187 /* Read a line of commands and handle end of file specially */
1188 gec
.gec_line
.l
= gec
.gec_line_size
;
1190 n
= n_go_input(gif
, NULL
, &gec
.gec_line
.s
, &gec
.gec_line
.l
, NULL
, NULL
);
1192 gec
.gec_line_size
= (ui32_t
)gec
.gec_line
.l
;
1193 gec
.gec_line
.l
= (ui32_t
)n
;
1199 assert(gec
.gec_hist_flags
== a_GO_HIST_NONE
);
1200 if(!a_go_evaluate(&gec
))
1204 if(!(f
& a_RETOK
) || a_go_xcall
!= NULL
||
1205 (n_psonce
& n_PSO_EXIT_MASK
) || (n_pstate
& n_PS_ERR_EXIT_MASK
))
1209 jjump
: /* TODO Should be _CLEANUP_UNWIND not _TEARDOWN on signal if DOABLE! */
1210 a_go_cleanup(a_GO_CLEANUP_TEARDOWN
|
1211 (f
& a_RETOK
? 0 : a_GO_CLEANUP_ERROR
) |
1212 (hadint
? a_GO_CLEANUP_SIGINT
: 0) | a_GO_CLEANUP_HOLDALLSIGS
);
1214 if(gec
.gec_line
.s
!= NULL
)
1215 n_free(gec
.gec_line
.s
);
1217 if(soldhdl
!= SIG_IGN
)
1218 safe_signal(SIGINT
, soldhdl
);
1222 sigprocmask(SIG_SETMASK
, &osigmask
, NULL
);
1225 return (f
& a_RETOK
);
1230 struct a_go_ctx
*gcp
;
1233 assert(n_stdin
!= NULL
);
1235 gcp
= (void*)a_go__mainctx_b
.uf
;
1236 DBGOR( memset(gcp
, 0, n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
)),
1237 memset(&gcp
->gc_data
, 0, sizeof gcp
->gc_data
) );
1238 gcp
->gc_file
= n_stdin
;
1239 memcpy(gcp
->gc_name
, a_GO_MAINCTX_NAME
, sizeof(a_GO_MAINCTX_NAME
));
1241 n_go_data
= &gcp
->gc_data
;
1243 n_child_manager_start();
1248 n_go_main_loop(void){ /* FIXME */
1249 struct a_go_eval_ctx gec
;
1256 if (!(n_pstate
& n_PS_SOURCING
)) {
1257 if (safe_signal(SIGINT
, SIG_IGN
) != SIG_IGN
)
1258 safe_signal(SIGINT
, &a_go_onintr
);
1259 if (safe_signal(SIGHUP
, SIG_IGN
) != SIG_IGN
)
1260 safe_signal(SIGHUP
, &a_go_hangup
);
1262 a_go_oldpipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1263 safe_signal(SIGPIPE
, a_go_oldpipe
);
1265 memset(&gec
, 0, sizeof gec
);
1267 (void)sigsetjmp(a_go_srbuf
, 1); /* FIXME get rid */
1270 for (eofcnt
= 0;; gec
.gec_ever_seen
= TRU1
) {
1273 if(gec
.gec_ever_seen
)
1274 a_go_cleanup(a_GO_CLEANUP_LOOPTICK
| a_GO_CLEANUP_HOLDALLSIGS
);
1276 if (!(n_pstate
& n_PS_SOURCING
)) {
1279 /* TODO Note: this buffer may contain a password. We should redefine
1280 * TODO the code flow which has to do that */
1281 if ((cp
= termios_state
.ts_linebuf
) != NULL
) {
1282 termios_state
.ts_linebuf
= NULL
;
1283 termios_state
.ts_linesize
= 0;
1284 n_free(cp
); /* TODO pool give-back */
1286 if (gec
.gec_line
.l
> LINESIZE
* 3) {
1287 n_free(gec
.gec_line
.s
);
1288 gec
.gec_line
.s
= NULL
;
1289 gec
.gec_line
.l
= gec
.gec_line_size
= 0;
1293 if (!(n_pstate
& n_PS_SOURCING
) && (n_psonce
& n_PSO_INTERACTIVE
)) {
1296 if ((cp
= ok_vlook(newmail
)) != NULL
) { /* TODO bla */
1299 if(mb
.mb_type
== MB_FILE
){
1300 if(!stat(mailname
, &st
) && st
.st_size
> mailsize
)
1301 #if defined HAVE_MAILDIR || defined HAVE_IMAP
1308 odot
= PTR2SIZE(dot
- message
);
1309 odid
= (n_pstate
& n_PS_DID_PRINT_DOT
);
1312 n
= setfile(mailname
,
1314 ((mb
.mb_perm
& MB_DELE
) ? 0 : FEDIT_RDONLY
)));
1318 n_exit_status
|= n_EXIT_ERR
;
1323 if(mb
.mb_type
!= MB_IMAP
){
1325 dot
= &message
[odot
];
1332 #if defined HAVE_MAILDIR || defined HAVE_IMAP
1333 n
= (cp
!= NULL
&& strcmp(cp
, "nopoll"));
1337 if(mb
.mb_type
== MB_MAILDIR
){
1343 if(mb
.mb_type
== MB_IMAP
){
1345 n
= (cp
!= NULL
&& strcmp(cp
, "noimap"));
1347 if(imap_newmail(n
) > (cp
== NULL
))
1355 /* Read a line of commands and handle end of file specially */
1356 gec
.gec_line
.l
= gec
.gec_line_size
;
1360 histadd
= (!(n_pstate
& n_PS_SOURCING
) &&
1361 (n_psonce
& n_PSO_INTERACTIVE
));
1363 n
= n_go_input(n_GO_INPUT_CTX_DEFAULT
| n_GO_INPUT_NL_ESC
, NULL
,
1364 &gec
.gec_line
.s
, &gec
.gec_line
.l
, NULL
, &histadd
);
1367 gec
.gec_hist_flags
= histadd
? a_GO_HIST_ADD
: a_GO_HIST_NONE
;
1369 gec
.gec_line_size
= (ui32_t
)gec
.gec_line
.l
;
1370 gec
.gec_line
.l
= (ui32_t
)n
;
1373 if (!(n_pstate
& n_PS_ROBOT
) &&
1374 (n_psonce
& n_PSO_INTERACTIVE
) && ok_blook(ignoreeof
) &&
1376 fprintf(n_stdout
, _("*ignoreeof* set, use `quit' to quit.\n"));
1377 n_go_input_clearerr();
1383 n_pstate
&= ~n_PS_HOOK_MASK
;
1385 rv
= a_go_evaluate(&gec
);
1388 if(gec
.gec_hist_flags
& a_GO_HIST_ADD
){
1389 char const *cc
, *ca
;
1391 cc
= gec
.gec_hist_cmd
;
1392 ca
= gec
.gec_hist_args
;
1393 if(cc
!= NULL
&& ca
!= NULL
)
1394 cc
= savecatsep(cc
, ' ', ca
);
1398 n_tty_addhist(cc
, (n_GO_INPUT_CTX_DEFAULT
|
1399 (gec
.gec_hist_flags
& a_GO_HIST_GABBY
? n_GO_INPUT_HIST_GABBY
1400 : n_GO_INPUT_NONE
)));
1403 switch(n_pstate
& n_PS_ERR_EXIT_MASK
){
1404 case n_PS_ERR_XIT
: n_psonce
|= n_PSO_XIT
; break;
1405 case n_PS_ERR_QUIT
: n_psonce
|= n_PSO_QUIT
; break;
1408 if(n_psonce
& n_PSO_EXIT_MASK
)
1415 a_go_cleanup(a_GO_CLEANUP_TEARDOWN
| a_GO_CLEANUP_HOLDALLSIGS
|
1416 (rv
? 0 : a_GO_CLEANUP_ERROR
));
1418 if (gec
.gec_line
.s
!= NULL
)
1419 n_free(gec
.gec_line
.s
);
1427 n_go_input_clearerr(void){
1433 if(!(a_go_ctx
->gc_flags
& (a_GO_FORCE_EOF
|
1434 a_GO_PIPE
| a_GO_MACRO
| a_GO_SPLICE
)))
1435 fp
= a_go_ctx
->gc_file
;
1438 a_go_ctx
->gc_flags
&= ~a_GO_IS_EOF
;
1445 n_go_input_force_eof(void){
1447 a_go_ctx
->gc_flags
|= a_GO_FORCE_EOF
;
1452 n_go_input_is_eof(void){
1456 rv
= ((a_go_ctx
->gc_flags
& a_GO_IS_EOF
) != 0);
1462 n_go_input_inject(enum n_go_input_inject_flags giif
, char const *buf
,
1468 if(UIZ_MAX
- n_VSTRUCT_SIZEOF(struct a_go_input_inject
, gii_dat
) -1 > len
&&
1470 struct a_go_input_inject
*giip
, **giipp
;
1474 giip
= n_alloc(n_VSTRUCT_SIZEOF(struct a_go_input_inject
, gii_dat
1476 giipp
= &a_go_ctx
->gc_inject
;
1477 giip
->gii_next
= *giipp
;
1478 giip
->gii_commit
= ((giif
& n_GO_INPUT_INJECT_COMMIT
) != 0);
1479 giip
->gii_no_history
= ((giif
& n_GO_INPUT_INJECT_HISTORY
) == 0);
1480 memcpy(&giip
->gii_dat
[0], buf
, len
);
1481 giip
->gii_dat
[giip
->gii_len
= len
] = '\0';
1490 (n_go_input
)(enum n_go_input_flags gif
, char const *prompt
, char **linebuf
,
1491 size_t *linesize
, char const *string
, bool_t
*histok_or_null
1492 n_MEMORY_DEBUG_ARGS
){
1493 /* TODO readline: linebuf pool!; n_go_input should return si64_t.
1494 * TODO This thing should be replaced by a(n) (stack of) event generator(s)
1495 * TODO and consumed by OnLineCompletedEvent listeners */
1496 struct n_string xprompt
;
1499 struct a_go_input_inject
*giip
;
1504 a_USE_PROMPT
= 1u<<1,
1506 a_DIGMSG_OVERLAY
= 1u<<16
1510 if(!(gif
& n_GO_INPUT_HOLDALLSIGS
))
1515 if(a_go_ctx
->gc_flags
& a_GO_FORCE_EOF
){
1516 a_go_ctx
->gc_flags
|= a_GO_IS_EOF
;
1521 if(gif
& n_GO_INPUT_FORCE_STDIN
)
1524 /* Special case macro mode: never need to prompt, lines have always been
1525 * unfolded already */
1526 if(a_go_ctx
->gc_flags
& a_GO_MACRO
){
1527 if(*linebuf
!= NULL
)
1530 /* Injection in progress? Don't care about the autocommit state here */
1531 if((giip
= a_go_ctx
->gc_inject
) != NULL
){
1532 a_go_ctx
->gc_inject
= giip
->gii_next
;
1534 /* Simply "reuse" allocation, copy string to front of it */
1536 *linesize
= giip
->gii_len
;
1537 *linebuf
= (char*)giip
;
1538 memmove(*linebuf
, giip
->gii_dat
, giip
->gii_len
+1);
1539 iftype
= "INJECTION";
1541 if((*linebuf
= a_go_ctx
->gc_lines
[a_go_ctx
->gc_loff
]) == NULL
){
1543 a_go_ctx
->gc_flags
|= a_GO_IS_EOF
;
1548 ++a_go_ctx
->gc_loff
;
1549 *linesize
= strlen(*linebuf
);
1550 if(!(a_go_ctx
->gc_flags
& a_GO_MACRO_FREE_DATA
))
1551 *linebuf
= sbufdup(*linebuf
, *linesize
);
1553 iftype
= (a_go_ctx
->gc_flags
& a_GO_MACRO_X_OPTION
)
1555 : (a_go_ctx
->gc_flags
& a_GO_MACRO_CMD
) ? "CMD" : "MACRO";
1558 n_pstate
|= n_PS_READLINE_NL
;
1561 /* Injection in progress? */
1562 struct a_go_input_inject
**giipp
;
1564 giipp
= &a_go_ctx
->gc_inject
;
1566 if((giip
= *giipp
) != NULL
){
1567 *giipp
= giip
->gii_next
;
1569 if(giip
->gii_commit
){
1570 if(*linebuf
!= NULL
)
1572 if(!giip
->gii_no_history
)
1574 goto jinject
; /* (above) */
1576 string
= savestrbuf(giip
->gii_dat
, giip
->gii_len
);
1583 n_pstate
&= ~n_PS_READLINE_NL
;
1584 iftype
= (!(n_psonce
& n_PSO_STARTED
) ? "LOAD"
1585 : (n_pstate
& n_PS_SOURCING
) ? "SOURCE" : "READ");
1586 if(!(n_pstate
& n_PS_ROBOT
) &&
1587 (n_psonce
& (n_PSO_INTERACTIVE
| n_PSO_STARTED
)) ==
1588 (n_PSO_INTERACTIVE
| n_PSO_STARTED
))
1590 if(!(f
& a_HISTOK
) || (gif
& n_GO_INPUT_FORCE_STDIN
))
1591 gif
|= n_GO_INPUT_PROMPT_NONE
;
1594 if(!ok_blook(line_editor_disable
))
1597 (void)n_string_creat_auto(&xprompt
);
1599 gif
|= n_GO_INPUT_PROMPT_EVAL
;
1602 /* Ensure stdout is flushed first anyway (partial lines, maybe?) */
1603 if((gif
& n_GO_INPUT_PROMPT_NONE
) && !(f
& a_USE_MLE
))
1606 if(gif
& n_GO_INPUT_FORCE_STDIN
){
1607 struct a_go_readctl_ctx
*grcp
;
1608 struct n_dig_msg_ctx
*dmcp
;
1610 if((dmcp
= n_digmsg_read_overlay
) != NULL
){
1611 ifile
= dmcp
->dmc_fp
;
1612 f
|= a_DIGMSG_OVERLAY
;
1613 }else if((grcp
= n_readctl_read_overlay
) == NULL
||
1614 (ifile
= grcp
->grc_fp
) == NULL
)
1617 ifile
= a_go_ctx
->gc_file
;
1619 assert((n_pstate
& n_PS_COMPOSE_FORKHOOK
) &&
1620 (a_go_ctx
->gc_flags
& a_GO_MACRO
));
1624 for(nold
= n
= 0;;){
1626 assert(ifile
== n_stdin
);
1627 if(string
!= NULL
&& (n
= (int)strlen(string
)) > 0){
1631 *linesize
= (size_t)n
+ LINESIZE
+1;
1632 *linebuf
= (n_realloc
)(*linebuf
, *linesize n_MEMORY_DEBUG_ARGSCALL
);
1633 memcpy(*linebuf
, string
, (size_t)n
+1);
1639 n
= (n_tty_readline
)(gif
, prompt
, linebuf
, linesize
, n
, histok_or_null
1640 n_MEMORY_DEBUG_ARGSCALL
);
1644 if(n
< 0 && !ferror(ifile
)) /* EOF never i guess */
1645 a_go_ctx
->gc_flags
|= a_GO_IS_EOF
;
1647 if(!(gif
& n_GO_INPUT_PROMPT_NONE
))
1648 n_tty_create_prompt(&xprompt
, prompt
, gif
);
1652 if(!(gif
& n_GO_INPUT_PROMPT_NONE
) && xprompt
.s_len
> 0){
1653 fwrite(xprompt
.s_dat
, 1, xprompt
.s_len
, n_stdout
);
1657 n
= (readline_restart
)(ifile
, linebuf
, linesize
, n
1658 n_MEMORY_DEBUG_ARGSCALL
);
1662 if(n
< 0 && !ferror(ifile
))
1663 a_go_ctx
->gc_flags
|= a_GO_IS_EOF
;
1665 if(n
> 0 && nold
> 0){
1670 cp
= &(*linebuf
)[nold
];
1671 while(spacechar(*cp
) && n
- i
>= nold
)
1674 memmove(&(*linebuf
)[nold
], cp
, n
- nold
- i
);
1676 (*linebuf
)[n
] = '\0';
1685 * TODO This does not take care for current shell quote mode!
1686 * TODO Thus "echo '\<NEWLINE HERE> bla' will never work
1687 * An unquoted <backslash> at the end of a command line shall
1688 * be discarded and the next line shall continue the command */
1689 if(!(gif
& n_GO_INPUT_NL_ESC
) || (*linebuf
)[n
- 1] != '\\')
1692 /* Definitely outside of quotes, thus the quoting rules are so that an
1693 * uneven number of successive reverse solidus at EOL is a continuation */
1697 for(j
= 1, i
= (size_t)n
- 1; i
-- > 0; ++j
)
1698 if((*linebuf
)[i
] != '\\')
1703 (*linebuf
)[nold
= --n
] = '\0';
1704 gif
|= n_GO_INPUT_NL_FOLLOW
;
1710 n_pstate
|= n_PS_READLINE_NL
;
1711 (*linebuf
)[*linesize
= n
] = '\0';
1714 if(n_poption
& n_PO_D_VV
)
1715 n_err(_("%s %d bytes <%s>\n"), iftype
, n
, *linebuf
);
1717 if (n_pstate
& n_PS_PSTATE_PENDMASK
)
1718 a_go_update_pstate();
1720 /* TODO We need to special case a_GO_SPLICE, since that is not managed by us
1721 * TODO but only established from the outside and we need to drop this
1722 * TODO overlay context somehow; ditto DIGMSG_OVERLAY */
1724 if(f
& a_DIGMSG_OVERLAY
)
1725 n_digmsg_read_overlay
= NULL
;
1726 if(a_go_ctx
->gc_flags
& a_GO_SPLICE
)
1727 a_go_cleanup(a_GO_CLEANUP_TEARDOWN
| a_GO_CLEANUP_HOLDALLSIGS
);
1730 if(histok_or_null
!= NULL
&& !(f
& a_HISTOK
))
1731 *histok_or_null
= FAL0
;
1733 if(!(gif
& n_GO_INPUT_HOLDALLSIGS
))
1740 n_go_input_cp(enum n_go_input_flags gif
, char const *prompt
,
1741 char const *string
){
1745 char *linebuf
, * volatile rv
;
1753 n_SIGMAN_ENTER_SWITCH(&sm
, n_SIGMAN_ALL
){
1761 n
= n_go_input(gif
, prompt
, &linebuf
, &linesize
, string
, &histadd
);
1762 if(n
> 0 && *(rv
= savestrbuf(linebuf
, (size_t)n
)) != '\0' &&
1763 (gif
& n_GO_INPUT_HIST_ADD
) && (n_psonce
& n_PSO_INTERACTIVE
) &&
1765 n_tty_addhist(rv
, gif
);
1767 n_sigman_cleanup_ping(&sm
);
1772 n_sigman_leave(&sm
, n_SIGMAN_VIPSIGS_NTTYOUT
);
1777 n_go_load(char const *name
){
1778 struct a_go_ctx
*gcp
;
1786 if(name
== NULL
|| *name
== '\0')
1788 else if((fip
= Fopen(name
, "r")) == NULL
){
1789 if(n_poption
& n_PO_D_V
)
1790 n_err(_("No such file to load: %s\n"), n_shexp_quote_cp(name
, FAL0
));
1794 i
= strlen(name
) +1;
1795 gcp
= n_alloc(n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
) + i
);
1796 memset(gcp
, 0, n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
));
1799 gcp
->gc_flags
= a_GO_FREE
| a_GO_FILE
;
1800 memcpy(gcp
->gc_name
, name
, i
);
1802 if(n_poption
& n_PO_D_V
)
1803 n_err(_("Loading %s\n"), n_shexp_quote_cp(gcp
->gc_name
, FAL0
));
1804 rv
= a_go_load(gcp
);
1811 n_go_Xargs(char const **lines
, size_t cnt
){
1812 static char const name
[] = "-X";
1817 char uf
[n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
) + sizeof(name
)];
1819 char const *srcp
, *xsrcp
;
1821 size_t imax
, i
, len
;
1822 struct a_go_ctx
*gcp
;
1826 memset(gcp
, 0, n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
));
1828 gcp
->gc_flags
= a_GO_MACRO
| a_GO_MACRO_X_OPTION
|
1829 a_GO_SUPER_MACRO
| a_GO_MACRO_FREE_DATA
;
1830 memcpy(gcp
->gc_name
, name
, sizeof name
);
1832 /* The problem being that we want to support reverse solidus newline
1833 * escaping also within multiline -X, i.e., POSIX says:
1834 * An unquoted <backslash> at the end of a command line shall
1835 * be discarded and the next line shall continue the command
1836 * Therefore instead of "gcp->gc_lines = n_UNCONST(lines)", duplicate the
1837 * entire lines array and set _MACRO_FREE_DATA */
1839 gcp
->gc_lines
= n_alloc(sizeof(*gcp
->gc_lines
) * imax
);
1841 /* For each of the input lines.. */
1842 for(i
= len
= 0, cp
= NULL
; cnt
> 0;){
1846 if((j
= strlen(srcp
= *lines
)) == 0){
1851 /* Separate one line from a possible multiline input string */
1852 if((xsrcp
= memchr(srcp
, '\n', j
)) != NULL
){
1854 j
= PTR2SIZE(xsrcp
- srcp
);
1858 /* The (separated) string may itself indicate soft newline escaping */
1859 if((keep
= (srcp
[j
- 1] == '\\'))){
1862 /* Need an uneven number of reverse solidus */
1863 for(xk
= 1, xj
= j
- 1; xj
-- > 0; ++xk
)
1864 if(srcp
[xj
] != '\\')
1872 /* Strip any leading WS from follow lines, then */
1874 while(j
> 0 && spacechar(*srcp
))
1878 if(i
+ 2 >= imax
){ /* TODO need a vector (main.c, here, ++) */
1880 gcp
->gc_lines
= n_realloc(gcp
->gc_lines
, sizeof(*gcp
->gc_lines
) *
1883 gcp
->gc_lines
[i
] = cp
= n_realloc(cp
, len
+ j
+1);
1884 memcpy(&cp
[len
], srcp
, j
);
1885 cp
[len
+= j
] = '\0';
1894 assert(i
+ 1 < imax
);
1895 gcp
->gc_lines
[i
++] = cp
;
1897 gcp
->gc_lines
[i
] = NULL
;
1899 b
.rv
= a_go_load(gcp
);
1909 rv
= (a_go_file(*(char**)v
, FAL0
) == TRU1
) ? 0 : 1;
1915 c_source_if(void *v
){ /* XXX obsolete?, support file tests in `if' etc.! */
1919 rv
= (a_go_file(*(char**)v
, TRU1
) == TRU1
) ? 0 : 1;
1925 n_go_macro(enum n_go_input_flags gif
, char const *name
, char **lines
,
1926 void (*on_finalize
)(void*), void *finalize_arg
){
1927 struct a_go_ctx
*gcp
;
1933 sigprocmask(SIG_BLOCK
, NULL
, &osigmask
);
1935 gcp
= n_alloc(n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
) +
1936 (i
= strlen(name
) +1));
1937 memset(gcp
, 0, n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
));
1941 gcp
->gc_outer
= a_go_ctx
;
1942 gcp
->gc_osigmask
= osigmask
;
1943 gcp
->gc_flags
= a_GO_FREE
| a_GO_MACRO
| a_GO_MACRO_FREE_DATA
|
1944 ((!(a_go_ctx
->gc_flags
& a_GO_TYPE_MASK
) ||
1945 (a_go_ctx
->gc_flags
& a_GO_SUPER_MACRO
)) ? a_GO_SUPER_MACRO
: 0) |
1946 ((gif
& n_GO_INPUT_NO_XCALL
) ? a_GO_XCALL_IS_CALL
: 0);
1947 gcp
->gc_lines
= lines
;
1948 gcp
->gc_on_finalize
= on_finalize
;
1949 gcp
->gc_finalize_arg
= finalize_arg
;
1950 memcpy(gcp
->gc_name
, name
, i
);
1953 n_go_data
= &gcp
->gc_data
;
1954 n_pstate
|= n_PS_ROBOT
;
1955 rv
= a_go_event_loop(gcp
, gif
);
1957 /* Shall this enter a `xcall' stack avoidance optimization (loop)? */
1958 if(a_go_xcall
!= NULL
){
1960 struct n_cmd_arg_ctx
*cacp
;
1962 if(a_go_xcall
== (void*)-1)
1964 else if(((void const*)(cacp
= a_go_xcall
)->cac_indat
) == gcp
){
1965 /* Indicate that "our" (ex-) parent now hosts xcall optimization */
1966 a_go_ctx
->gc_flags
|= a_GO_XCALL_LOOP
;
1967 while(a_go_xcall
!= NULL
){
1970 a_go_ctx
->gc_flags
&= ~a_GO_XCALL_LOOP_ERROR
;
1974 cacp
= n_cmd_arg_restore_from_heap(vp
);
1981 rv
= ((a_go_ctx
->gc_flags
& a_GO_XCALL_LOOP_ERROR
) == 0);
1982 a_go_ctx
->gc_flags
&= ~a_GO_XCALL_LOOP_MASK
;
1990 n_go_command(enum n_go_input_flags gif
, char const *cmd
){
1991 struct a_go_ctx
*gcp
;
1997 sigprocmask(SIG_BLOCK
, NULL
, &osigmask
);
2001 gcp
= n_alloc(n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
) +
2002 ial
+ 2*sizeof(char*));
2003 memset(gcp
, 0, n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
));
2007 gcp
->gc_outer
= a_go_ctx
;
2008 gcp
->gc_osigmask
= osigmask
;
2009 gcp
->gc_flags
= a_GO_FREE
| a_GO_MACRO
| a_GO_MACRO_CMD
|
2010 ((!(a_go_ctx
->gc_flags
& a_GO_TYPE_MASK
) ||
2011 (a_go_ctx
->gc_flags
& a_GO_SUPER_MACRO
)) ? a_GO_SUPER_MACRO
: 0);
2012 gcp
->gc_lines
= (void*)&gcp
->gc_name
[ial
];
2013 memcpy(gcp
->gc_lines
[0] = &gcp
->gc_name
[0], cmd
, i
);
2014 gcp
->gc_lines
[1] = NULL
;
2017 n_go_data
= &gcp
->gc_data
;
2018 n_pstate
|= n_PS_ROBOT
;
2019 rv
= a_go_event_loop(gcp
, gif
);
2025 n_go_splice_hack(char const *cmd
, FILE *new_stdin
, FILE *new_stdout
,
2026 ui32_t new_psonce
, void (*on_finalize
)(void*), void *finalize_arg
){
2027 struct a_go_ctx
*gcp
;
2032 sigprocmask(SIG_BLOCK
, NULL
, &osigmask
);
2034 gcp
= n_alloc(n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
) +
2035 (i
= strlen(cmd
) +1));
2036 memset(gcp
, 0, n_VSTRUCT_SIZEOF(struct a_go_ctx
, gc_name
));
2040 gcp
->gc_outer
= a_go_ctx
;
2041 gcp
->gc_osigmask
= osigmask
;
2042 gcp
->gc_file
= new_stdin
;
2043 gcp
->gc_flags
= a_GO_FREE
| a_GO_SPLICE
| a_GO_DATACTX_INHERITED
;
2044 gcp
->gc_on_finalize
= on_finalize
;
2045 gcp
->gc_finalize_arg
= finalize_arg
;
2046 gcp
->gc_splice_stdin
= n_stdin
;
2047 gcp
->gc_splice_stdout
= n_stdout
;
2048 gcp
->gc_splice_psonce
= n_psonce
;
2049 memcpy(gcp
->gc_name
, cmd
, i
);
2051 n_stdin
= new_stdin
;
2052 n_stdout
= new_stdout
;
2053 n_psonce
= new_psonce
;
2055 /* Do NOT touch n_go_data! */
2056 n_pstate
|= n_PS_ROBOT
;
2063 n_go_splice_hack_remove_after_jump(void){
2064 a_go_cleanup(a_GO_CLEANUP_TEARDOWN
);
2068 n_go_may_yield_control(void){ /* TODO this is a terrible hack */
2069 struct a_go_ctx
*gcp
;
2075 /* Only when startup completed */
2076 if(!(n_psonce
& n_PSO_STARTED
))
2078 /* Only interactive or batch mode (assuming that is ok) */
2079 if(!(n_psonce
& n_PSO_INTERACTIVE
) && !(n_poption
& n_PO_BATCH_FLAG
))
2082 /* Not when running any hook */
2083 if(n_pstate
& n_PS_HOOK_MASK
)
2086 /* Traverse up the stack:
2087 * . not when controlled by a child process
2088 * TODO . not when there are pipes involved, we neither handle job control,
2089 * TODO nor process groups, that is, controlling terminal acceptably
2090 * . not when sourcing a file */
2091 for(gcp
= a_go_ctx
; gcp
!= NULL
; gcp
= gcp
->gc_outer
){
2092 if(gcp
->gc_flags
& (a_GO_PIPE
| a_GO_FILE
| a_GO_SPLICE
))
2104 /* TODO HACK! `eval' should be nothing else but a command prefix, evaluate
2105 * TODO ARGV with shell rules, but if that is not possible then simply
2106 * TODO adjust argv/argc of "the CmdCtx" that we will have "exec" real cmd */
2107 struct a_go_eval_ctx gec
;
2108 struct n_string s_b
, *sp
;
2110 char const **argv
, *cp
;
2115 for(j
= i
= 0; (cp
= argv
[i
]) != NULL
; ++i
)
2118 sp
= n_string_creat_auto(&s_b
);
2119 sp
= n_string_reserve(sp
, j
);
2121 for(i
= 0; (cp
= argv
[i
]) != NULL
; ++i
){
2123 sp
= n_string_push_c(sp
, ' ');
2124 sp
= n_string_push_cp(sp
, cp
);
2127 memset(&gec
, 0, sizeof gec
);
2128 gec
.gec_line
.s
= n_string_cp(sp
);
2129 gec
.gec_line
.l
= sp
->s_len
;
2130 if(n_poption
& n_PO_D_VV
)
2131 n_err(_("EVAL %" PRIuZ
" bytes <%s>\n"), gec
.gec_line
.l
, gec
.gec_line
.s
);
2132 (void)/* XXX */a_go_evaluate(&gec
);
2134 return (a_go_xcall
!= NULL
? 0 : n_pstate_ex_no
);
2140 struct a_go_ctx
*gcp
;
2143 /* The context can only be a macro context, except that possibly a single
2144 * level of `eval' (TODO: yet) was used to double-expand our arguments */
2145 if((gcp
= a_go_ctx
)->gc_flags
& a_GO_MACRO_CMD
)
2146 gcp
= gcp
->gc_outer
;
2147 if((gcp
->gc_flags
& (a_GO_MACRO
| a_GO_MACRO_X_OPTION
| a_GO_MACRO_CMD
)
2149 if(n_poption
& n_PO_D_V
)
2150 n_err(_("`xcall': can only be used inside a macro, using `call'\n"));
2155 /* Try to roll up the stack as much as possible.
2156 * See a_GO_XCALL_LOOP flag description for more */
2157 if(!(gcp
->gc_flags
& a_GO_XCALL_IS_CALL
) && gcp
->gc_outer
!= NULL
){
2158 if(gcp
->gc_outer
->gc_flags
& a_GO_XCALL_LOOP
)
2159 gcp
= gcp
->gc_outer
;
2161 /* Otherwise this macro is "invoked from the top level", in which case we
2162 * silently act as if we were `call'... */
2164 /* ...which means we must ensure the rest of the macro that was us
2165 * doesn't become evaluated! */
2166 a_go_xcall
= (void*)-1;
2171 struct n_cmd_arg_ctx
*cacp
;
2173 cacp
= n_cmd_arg_save_to_heap(vp
);
2174 cacp
->cac_indat
= (char*)gcp
;
2188 if(*(argv
= vp
) != NULL
&& (n_idec_si32_cp(&n_exit_status
, *argv
, 0, NULL
) &
2189 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
2190 ) != n_IDEC_STATE_CONSUMED
)
2191 n_exit_status
|= n_EXIT_ERR
;
2193 if(n_pstate
& n_PS_COMPOSE_FORKHOOK
){ /* TODO sic */
2195 _exit(n_exit_status
);
2196 }else if(n_pstate
& n_PS_COMPOSE_MODE
) /* XXX really.. */
2197 n_err(_("`exit' delayed until compose mode is left\n")); /* XXX ..log? */
2198 n_psonce
|= n_PSO_XIT
;
2208 if(*(argv
= vp
) != NULL
&& (n_idec_si32_cp(&n_exit_status
, *argv
, 0, NULL
) &
2209 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
2210 ) != n_IDEC_STATE_CONSUMED
)
2211 n_exit_status
|= n_EXIT_ERR
;
2213 if(n_pstate
& n_PS_COMPOSE_FORKHOOK
){ /* TODO sic */
2215 _exit(n_exit_status
);
2216 }else if(n_pstate
& n_PS_COMPOSE_MODE
) /* XXX really.. */
2217 n_err(_("`exit' delayed until compose mode is left\n")); /* XXX ..log? */
2218 n_psonce
|= n_PSO_QUIT
;
2224 c_readctl(void *vp
){
2225 /* TODO We would need OnForkEvent and then simply remove some internal
2226 * TODO management; we don't have this, therefore we need global
2227 * TODO n_readctl_read_overlay to be accessible via =NULL, and to make that
2228 * TODO work in turn we need an instance for default STDIN! Sigh. */
2231 ui8_t buf
[n_VSTRUCT_SIZEOF(struct a_go_readctl_ctx
, grc_name
)+1 +1];
2233 static struct a_go_readctl_ctx
*a_stdin
;
2235 struct a_go_readctl_ctx
*grcp
;
2244 struct n_cmd_arg
*cap
;
2245 struct n_cmd_arg_ctx
*cacp
;
2248 if(a_stdin
== NULL
){
2249 a_stdin
= (struct a_go_readctl_ctx
*)(void*)a
.buf
;
2250 a_stdin
->grc_name
[0] = '-';
2251 n_readctl_read_overlay
= a_stdin
;
2254 n_pstate_err_no
= n_ERR_NONE
;
2256 cap
= cacp
->cac_arg
;
2258 if(cacp
->cac_no
== 0 || is_asccaseprefix(cap
->ca_arg
.ca_str
.s
, "show"))
2260 else if(is_asccaseprefix(cap
->ca_arg
.ca_str
.s
, "set"))
2262 else if(is_asccaseprefix(cap
->ca_arg
.ca_str
.s
, "create"))
2264 else if(is_asccaseprefix(cap
->ca_arg
.ca_str
.s
, "remove"))
2267 emsg
= N_("`readctl': invalid subcommand: %s\n");
2271 if(cacp
->cac_no
== 1){ /* TODO better option parser <> subcommand */
2272 n_err(_("`readctl': %s: requires argument\n"), cap
->ca_arg
.ca_str
.s
);
2277 /* - is special TODO unfortunately also regarding storage */
2278 if(cap
->ca_arg
.ca_str
.l
== 1 && *cap
->ca_arg
.ca_str
.s
== '-'){
2279 if(f
& (a_CREATE
| a_REMOVE
)){
2280 n_err(_("`readctl': cannot create nor remove -\n"));
2283 n_readctl_read_overlay
= a_stdin
;
2287 /* Try to find a yet existing instance */
2288 if((grcp
= n_readctl_read_overlay
) != NULL
){
2289 for(; grcp
!= NULL
; grcp
= grcp
->grc_next
)
2290 if(!strcmp(grcp
->grc_name
, cap
->ca_arg
.ca_str
.s
))
2292 for(grcp
= n_readctl_read_overlay
; (grcp
= grcp
->grc_last
) != NULL
;)
2293 if(!strcmp(grcp
->grc_name
, cap
->ca_arg
.ca_str
.s
))
2297 if(f
& (a_SET
| a_REMOVE
)){
2298 emsg
= N_("`readctl': no such channel: %s\n");
2304 n_readctl_read_overlay
= grcp
;
2305 else if(f
& a_REMOVE
){
2306 if(n_readctl_read_overlay
== grcp
)
2307 n_readctl_read_overlay
= a_stdin
;
2309 if(grcp
->grc_last
!= NULL
)
2310 grcp
->grc_last
->grc_next
= grcp
->grc_next
;
2311 if(grcp
->grc_next
!= NULL
)
2312 grcp
->grc_next
->grc_last
= grcp
->grc_last
;
2313 fclose(grcp
->grc_fp
);
2321 n_err(_("`readctl': channel already exists: %s\n"), /* TODO reopen */
2322 n_shexp_quote_cp(cap
->ca_arg
.ca_str
.s
, FAL0
));
2323 n_pstate_err_no
= n_ERR_EXIST
;
2328 if((n_idec_si32_cp(&fd
, cap
->ca_arg
.ca_str
.s
, 0, NULL
2329 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
2330 ) != n_IDEC_STATE_CONSUMED
){
2331 if((emsg
= fexpand(cap
->ca_arg
.ca_str
.s
, FEXP_LOCAL
| FEXP_NVAR
)
2333 emsg
= N_("`readctl': cannot expand filename %s\n");
2337 elen
= strlen(emsg
);
2338 fp
= safe_fopen(emsg
, "r", NULL
);
2339 }else if(fd
== STDIN_FILENO
|| fd
== STDOUT_FILENO
||
2340 fd
== STDERR_FILENO
){
2341 n_err(_("`readctl': create: standard descriptors are not allowed\n"));
2348 fp
= fdopen(fd
, "r");
2354 if((i
= UIZ_MAX
- elen
) <= cap
->ca_arg
.ca_str
.l
||
2355 (i
-= cap
->ca_arg
.ca_str
.l
) <=
2356 n_VSTRUCT_SIZEOF(struct a_go_readctl_ctx
, grc_name
) +2){
2357 n_err(_("`readctl': failed to create storage for %s\n"),
2358 cap
->ca_arg
.ca_str
.s
);
2359 n_pstate_err_no
= n_ERR_OVERFLOW
;
2364 grcp
= n_alloc(n_VSTRUCT_SIZEOF(struct a_go_readctl_ctx
, grc_name
) +
2365 cap
->ca_arg
.ca_str
.l
+1 + elen
+1);
2366 grcp
->grc_last
= NULL
;
2367 if((grcp
->grc_next
= n_readctl_read_overlay
) != NULL
)
2368 grcp
->grc_next
->grc_last
= grcp
;
2369 n_readctl_read_overlay
= grcp
;
2372 memcpy(grcp
->grc_name
, cap
->ca_arg
.ca_str
.s
, cap
->ca_arg
.ca_str
.l
+1);
2374 grcp
->grc_expand
= NULL
;
2378 grcp
->grc_expand
= cp
= &grcp
->grc_name
[cap
->ca_arg
.ca_str
.l
+1];
2379 memcpy(cp
, emsg
, ++elen
);
2382 emsg
= N_("`readctl': failed to create file for %s\n");
2389 return (f
& a_ERR
) ? 1 : 0;
2391 n_err(V_(emsg
), n_shexp_quote_cp(cap
->ca_arg
.ca_str
.s
, FAL0
));
2393 n_pstate_err_no
= n_ERR_INVAL
;
2398 if((grcp
= n_readctl_read_overlay
) == NULL
)
2399 fprintf(n_stdout
, _("`readctl': no channels registered\n"));
2401 while(grcp
->grc_last
!= NULL
)
2402 grcp
= grcp
->grc_last
;
2404 fprintf(n_stdout
, _("`readctl': registered channels:\n"));
2405 for(; grcp
!= NULL
; grcp
= grcp
->grc_next
)
2406 fprintf(n_stdout
, _("%c%s %s%s%s%s\n"),
2407 (grcp
== n_readctl_read_overlay
? '*' : ' '),
2408 (grcp
->grc_fd
!= -1 ? _("descriptor") : _("name")),
2409 n_shexp_quote_cp(grcp
->grc_name
, FAL0
),
2410 (grcp
->grc_expand
!= NULL
? " (" : n_empty
),
2411 (grcp
->grc_expand
!= NULL
? grcp
->grc_expand
: n_empty
),
2412 (grcp
->grc_expand
!= NULL
? ")" : n_empty
));