1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ n_cmd_firstfit(): the table of commands + `help' and `list'.
3 *@ And n_cmd_arg_parse(), the (new) argument list parser. TODO this is
4 *@ TODO too stupid yet, however: it should fully support subcommands, too, so
5 *@ TODO that, e.g., "vexpr regex" arguments can be fully prepared by the
6 *@ TODO generic parser. But at least a bit.
7 *@ TODO See cmd-tab.h for sort and speedup TODOs.
9 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
11 /* Command table and getrawlist() also:
12 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
14 * Copyright (c) 1980, 1993
15 * The Regents of the University of California. All rights reserved.
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
20 * 1. Redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer.
22 * 2. Redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in the
24 * documentation and/or other materials provided with the distribution.
25 * 3. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 #define n_FILE cmd_tab
44 #ifndef HAVE_AMALGAMATION
48 /* Create a multiline info string about all known additional infos for lcp */
49 #ifdef HAVE_DOCSTRINGS
50 static char const *a_ctab_cmdinfo(struct n_cmd_desc
const *cdp
);
53 /* Print a list of all commands */
54 static int a_ctab_c_list(void *vp
);
56 static int a_ctab__pcmd_cmp(void const *s1
, void const *s2
);
58 /* `help' / `?' command */
59 static int a_ctab_c_help(void *vp
);
61 /* List of all commands; but first their n_cmd_arg_desc instances */
63 static struct n_cmd_desc
const a_ctab_ctable
[] = {
67 /* And a list of things which are special to the lexer in go.c, so that we can
68 * provide help and list them.
69 * This cross-file relationship is a bit unfortunate.. */
70 #ifdef HAVE_DOCSTRINGS
75 static struct n_cmd_desc
const a_ctab_ctable_plus
[] = {
76 { n_ns
, (int(*)(void*))-1, n_CMD_ARG_TYPE_STRING
, 0, 0, NULL
77 DS(N_("Comment command: ignore remaining (continuable) line")) },
78 { n_hy
, (int(*)(void*))-1, n_CMD_ARG_TYPE_WYSH
, 0, 0, NULL
79 DS(N_("Print out the preceding message")) }
83 #ifdef HAVE_DOCSTRINGS
85 a_ctab_cmdinfo(struct n_cmd_desc
const *cdp
){
86 struct n_string rvb
, *rv
;
90 rv
= n_string_creat_auto(&rvb
);
91 rv
= n_string_reserve(rv
, 80);
93 switch(cdp
->cd_caflags
& n_CMD_ARG_TYPE_MASK
){
94 case n_CMD_ARG_TYPE_MSGLIST
:
95 cp
= N_("message-list");
97 case n_CMD_ARG_TYPE_STRING
:
98 case n_CMD_ARG_TYPE_RAWDAT
:
99 cp
= N_("string data");
101 case n_CMD_ARG_TYPE_RAWLIST
:
102 cp
= N_("old-style quoting");
104 case n_CMD_ARG_TYPE_NDMLIST
:
105 cp
= N_("message-list (no default)");
107 case n_CMD_ARG_TYPE_WYRA
:
108 cp
= N_("`wysh' for sh(1)ell-style quoting");
110 case n_CMD_ARG_TYPE_WYSH
:
111 cp
= (cdp
->cd_minargs
== 0 && cdp
->cd_maxargs
== 0)
112 ? N_("sh(1)ell-style quoting (takes no arguments)")
113 : N_("sh(1)ell-style quoting");
116 case n_CMD_ARG_TYPE_ARG
:{
119 struct n_cmd_arg_desc
const *cadp
;
121 rv
= n_string_push_cp(rv
, _("argument tokens: "));
123 for(cadp
= cdp
->cd_cadp
, i
= 0; i
< cadp
->cad_no
; ++i
){
125 rv
= n_string_push_c(rv
, ',');
127 flags
= cadp
->cad_ent_flags
[i
][0];
128 if(flags
& n_CMD_ARG_DESC_OPTION
)
129 rv
= n_string_push_c(rv
, '[');
130 if(flags
& n_CMD_ARG_DESC_GREEDY
)
131 rv
= n_string_push_c(rv
, ':');
132 switch(flags
& n__CMD_ARG_DESC_TYPE_MASK
){
133 case n_CMD_ARG_DESC_STRING
:
134 rv
= n_string_push_cp(rv
, _("raw"));
137 case n_CMD_ARG_DESC_WYSH
:
138 rv
= n_string_push_cp(rv
, _("eval"));
141 if(flags
& n_CMD_ARG_DESC_GREEDY
)
142 rv
= n_string_push_c(rv
, ':');
143 if(flags
& n_CMD_ARG_DESC_OPTION
)
144 rv
= n_string_push_c(rv
, ']');
150 rv
= n_string_push_cp(rv
, V_(cp
));
152 /* Note: on updates, change the manual! */
153 if(cdp
->cd_caflags
& n_CMD_ARG_L
)
154 rv
= n_string_push_cp(rv
, _(" | `local'"));
155 if(cdp
->cd_caflags
& n_CMD_ARG_V
)
156 rv
= n_string_push_cp(rv
, _(" | `vput'"));
157 if(cdp
->cd_caflags
& n_CMD_ARG_EM
)
158 rv
= n_string_push_cp(rv
, _(" | *!*"));
160 if(cdp
->cd_caflags
& n_CMD_ARG_A
)
161 rv
= n_string_push_cp(rv
, _(" | needs box"));
162 if(cdp
->cd_caflags
& n_CMD_ARG_I
)
163 rv
= n_string_push_cp(rv
, _(" | ok: batch/interactive"));
164 if(cdp
->cd_caflags
& n_CMD_ARG_M
)
165 rv
= n_string_push_cp(rv
, _(" | ok: send mode"));
166 if(cdp
->cd_caflags
& n_CMD_ARG_R
)
167 rv
= n_string_push_cp(rv
, _(" | not ok: compose mode"));
168 if(cdp
->cd_caflags
& n_CMD_ARG_S
)
169 rv
= n_string_push_cp(rv
, _(" | not ok: startup"));
170 if(cdp
->cd_caflags
& n_CMD_ARG_X
)
171 rv
= n_string_push_cp(rv
, _(" | ok: subprocess"));
173 if(cdp
->cd_caflags
& n_CMD_ARG_G
)
174 rv
= n_string_push_cp(rv
, _(" | gabby"));
176 cp
= n_string_cp(rv
);
180 #endif /* HAVE_DOCSTRINGS */
183 a_ctab_c_list(void *vp
){
185 struct n_cmd_desc
const **cdpa
, *cdp
, **cdpa_curr
;
189 i
= n_NELEM(a_ctab_ctable
) + n_NELEM(a_ctab_ctable_plus
) +1;
190 cdpa
= n_autorec_alloc(sizeof(cdp
) * i
);
192 for(i
= 0; i
< n_NELEM(a_ctab_ctable
); ++i
)
193 cdpa
[i
] = &a_ctab_ctable
[i
];
194 for(l
= 0; l
< n_NELEM(a_ctab_ctable_plus
); ++i
, ++l
)
195 cdpa
[i
] = &a_ctab_ctable_plus
[l
];
198 if(*(void**)vp
== NULL
)
199 qsort(cdpa
, i
, sizeof(*cdpa
), &a_ctab__pcmd_cmp
);
201 if((fp
= Ftmp(NULL
, "list", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) == NULL
)
204 scrwid
= n_SCRNWIDTH_FOR_LISTS
;
206 fprintf(fp
, _("Commands are:\n"));
208 for(i
= 0, cdpa_curr
= cdpa
; (cdp
= *cdpa_curr
++) != NULL
;){
209 char const *pre
, *suf
;
211 if(cdp
->cd_func
== NULL
)
212 pre
= "[", suf
= "]";
216 #ifdef HAVE_DOCSTRINGS
217 if(n_poption
& n_PO_D_V
){
218 fprintf(fp
, "%s%s%s\n", pre
, cdp
->cd_name
, suf
);
220 fprintf(fp
, " : %s\n", V_(cdp
->cd_doc
));
222 fprintf(fp
, " : %s\n", a_ctab_cmdinfo(cdp
));
229 j
= strlen(cdp
->cd_name
);
233 if((i
+= j
+ 2) > scrwid
){
238 fprintf(fp
, (*cdpa_curr
!= NULL
? "%s%s%s, " : "%s%s%s\n"),
239 pre
, cdp
->cd_name
, suf
);
244 page_or_print(fp
, l
);
252 a_ctab__pcmd_cmp(void const *s1
, void const *s2
){
253 struct n_cmd_desc
const * const *cdpa1
, * const *cdpa2
;
259 rv
= strcmp((*cdpa1
)->cd_name
, (*cdpa2
)->cd_name
);
265 a_ctab_c_help(void *vp
){
270 /* Help for a single command? */
271 if((arg
= *(char const**)vp
) != NULL
){
272 struct n_cmd_desc
const *cdp
, *cdp_max
;
273 struct str
const *alias_exp
;
274 char const *alias_name
, *aepx
;
276 /* Aliases take precedence.
277 * Avoid self-recursion; since a commandalias can shadow a command of
278 * equal name allow one level of expansion to return an equal result:
279 * "commandalias q q;commandalias x q;x" should be "x->q->q->quit" */
281 while((aepx
= n_commandalias_exists(arg
, &alias_exp
)) != NULL
&&
282 (alias_name
== NULL
|| strcmp(alias_name
, aepx
))){
284 fprintf(n_stdout
, "%s -> ", arg
);
288 cdp_max
= &(cdp
= a_ctab_ctable
)[n_NELEM(a_ctab_ctable
)];
290 for(; cdp
< cdp_max
; ++cdp
){
291 if(is_prefix(arg
, cdp
->cd_name
)){
292 fputs(arg
, n_stdout
);
293 if(strcmp(arg
, cdp
->cd_name
))
294 fprintf(n_stdout
, " (%s)", cdp
->cd_name
);
298 #ifdef HAVE_DOCSTRINGS
299 fprintf(n_stdout
, ": %s", V_(cdp
->cd_doc
));
300 if(n_poption
& n_PO_D_V
)
301 fprintf(n_stdout
, "\n : %s", a_ctab_cmdinfo(cdp
));
303 putc('\n', n_stdout
);
308 if(cdp_max
== &a_ctab_ctable
[n_NELEM(a_ctab_ctable
)]){
310 a_ctab_ctable_plus
)[n_NELEM(a_ctab_ctable_plus
)];
314 if(alias_name
!= NULL
){
315 fprintf(n_stdout
, "%s\n", n_shexp_quote_cp(arg
, TRU1
));
318 n_err(_("Unknown command: `%s'\n"), arg
);
322 /* Very ugly, but take care for compiler supported string lengths :( */
323 #ifdef HAVE_UISTRINGS
324 fputs(n_progname
, n_stdout
);
326 " commands -- <msglist> denotes message specifications,\n"
327 "e.g., 1-5, :n or . (current, the \"dot\"), separated by spaces:\n"),
331 "type <msglist> type (`print') messages (honour `headerpick' etc.)\n"
332 "Type <msglist> like `type' but always show all headers\n"
333 "next goto and type next message\n"
334 "from <msglist> (search and) print header summary for the given list\n"
335 "headers header summary for messages surrounding \"dot\"\n"
336 "delete <msglist> delete messages (can be `undelete'd)\n"),
341 "save <msglist> folder append messages to folder and mark as saved\n"
342 "copy <msglist> folder like `save', but don't mark them (`move' moves)\n"
343 "write <msglist> file write message contents to file (prompts for parts)\n"
344 "Reply <msglist> reply to message senders only\n"
345 "reply <msglist> like `Reply', but address all recipients\n"
346 "Lreply <msglist> forced mailing list `reply' (see `mlist')\n"),
351 "mail <recipients> compose a mail for the given recipients\n"
352 "file folder change to another mailbox\n"
353 "File folder like `file', but open readonly\n"
354 "quit quit and apply changes to the current mailbox\n"
355 "xit or exit like `quit', but discard changes\n"
356 "!shell command shell escape\n"
357 "list [<anything>] all available commands [in search order]\n"),
359 #endif /* HAVE_UISTRINGS */
361 rv
= (ferror(n_stdout
) != 0);
369 n_cmd_isolate(char const *cmd
){
371 while(*cmd
!= '\0' &&
372 strchr("\\!~|? \t0123456789&%@$^.:/-+*'\",;(`", *cmd
) == NULL
)
375 return n_UNCONST(cmd
);
378 FL
struct n_cmd_desc
const *
379 n_cmd_firstfit(char const *cmd
){ /* TODO *hashtable*! linear list search!!! */
380 struct n_cmd_desc
const *cdp
;
383 for(cdp
= a_ctab_ctable
; cdp
< &a_ctab_ctable
[n_NELEM(a_ctab_ctable
)]; ++cdp
)
384 if(*cmd
== *cdp
->cd_name
&& cdp
->cd_func
!= NULL
&&
385 is_prefix(cmd
, cdp
->cd_name
))
393 FL
struct n_cmd_desc
const *
395 struct n_cmd_desc
const *cdp
;
398 cdp
= &a_ctab_ctable
[0];
404 n_cmd_arg_parse(struct n_cmd_arg_ctx
*cacp
){
405 struct n_cmd_arg ncap
, *lcap
;
406 struct str shin_orig
, shin
;
407 bool_t addca
, greedyjoin
;
409 size_t cad_idx
, parsed_args
;
410 struct n_cmd_arg_desc
const *cadp
;
413 assert(cacp
->cac_inlen
== 0 || cacp
->cac_indat
!= NULL
);
414 assert(cacp
->cac_desc
->cad_no
> 0);
417 bool_t opt_seen
= FAL0
;
419 for(cadp
= cacp
->cac_desc
, cad_idx
= 0;
420 cad_idx
< cadp
->cad_no
; ++cad_idx
){
421 assert(cadp
->cad_ent_flags
[cad_idx
][0] & n__CMD_ARG_DESC_TYPE_MASK
);
423 (cadp
->cad_ent_flags
[cad_idx
][0] & n_CMD_ARG_DESC_OPTION
));
424 if(cadp
->cad_ent_flags
[cad_idx
][0] & n_CMD_ARG_DESC_OPTION
)
426 assert(!(cadp
->cad_ent_flags
[cad_idx
][0] & n_CMD_ARG_DESC_GREEDY
) ||
427 cad_idx
+ 1 == cadp
->cad_no
);
432 shin
.s
= n_UNCONST(cacp
->cac_indat
); /* "logical" only */
433 shin
.l
= (cacp
->cac_inlen
== UIZ_MAX
? strlen(shin
.s
) : cacp
->cac_inlen
);
436 cacp
->cac_arg
= lcap
= NULL
;
442 for(cadp
= cacp
->cac_desc
, cad_idx
= 0; shin
.l
> 0 && cad_idx
< cadp
->cad_no
;
445 memset(&ncap
, 0, sizeof ncap
);
446 ncap
.ca_indat
= shin
.s
;
447 /* >ca_inline once we know */
448 memcpy(&ncap
.ca_ent_flags
[0], &cadp
->cad_ent_flags
[cad_idx
][0],
449 sizeof ncap
.ca_ent_flags
);
452 switch(ncap
.ca_ent_flags
[0] & n__CMD_ARG_DESC_TYPE_MASK
){
453 case n_CMD_ARG_DESC_STRING
:{ /* TODO \ escaping? additional type!? */
454 char /*const*/ *cp
= shin
.s
;
457 while(i
> 0 && blankspacechar(*cp
))
460 ncap
.ca_arg
.ca_str
.s
= cp
;
461 while(i
> 0 && !blankspacechar(*cp
))
463 ncap
.ca_arg
.ca_str
.s
= savestrbuf(ncap
.ca_arg
.ca_str
.s
,
464 ncap
.ca_arg
.ca_str
.l
= PTR2SIZE(cp
- ncap
.ca_arg
.ca_str
.s
));
466 while(i
> 0 && blankspacechar(*cp
))
468 ncap
.ca_inlen
= PTR2SIZE(cp
- ncap
.ca_indat
);
474 case n_CMD_ARG_DESC_WYSH
:{
475 struct n_string shou
, *shoup
;
476 enum n_shexp_state shs
;
479 if(cad_idx
== cadp
->cad_no
- 1 ||
480 (cadp
->cad_ent_flags
[cad_idx
+ 1][0] & n_CMD_ARG_DESC_OPTION
))
481 addflags
= n_SHEXP_PARSE_META_SEMICOLON
;
483 addflags
= n_SHEXP_PARSE_NONE
;
485 shoup
= n_string_creat_auto(&shou
);
487 shs
= n_shexp_parse_token((ncap
.ca_ent_flags
[1] | addflags
|
488 n_SHEXP_PARSE_TRIM_SPACE
| n_SHEXP_PARSE_LOG
),
490 (ncap
.ca_ent_flags
[0] & n_CMD_ARG_DESC_GREEDY
? &cookie
: NULL
));
491 ncap
.ca_inlen
= PTR2SIZE(shin
.s
- ncap
.ca_indat
);
492 if((shs
& (n_SHEXP_STATE_OUTPUT
| n_SHEXP_STATE_ERR_MASK
)) ==
493 n_SHEXP_STATE_OUTPUT
){
494 if((shs
& n_SHEXP_STATE_META_SEMICOLON
) && shou
.s_len
== 0)
496 ncap
.ca_arg
.ca_str
.s
= n_string_cp(shoup
);
497 ncap
.ca_arg
.ca_str
.l
= shou
.s_len
;
498 shoup
= n_string_drop_ownership(shoup
);
502 if(shs
& n_SHEXP_STATE_ERR_MASK
)
504 if((shs
& n_SHEXP_STATE_STOP
) &&
505 (ncap
.ca_ent_flags
[0] & n_CMD_ARG_DESC_HONOUR_STOP
)){
506 if(!(shs
& n_SHEXP_STATE_OUTPUT
))
509 }else if(!(shs
& n_SHEXP_STATE_OUTPUT
) && cad_idx
< cadp
->cad_no
&&
510 !(cadp
->cad_ent_flags
[cad_idx
][0] & n_CMD_ARG_DESC_OPTION
))
513 addca
= ((shs
& n_SHEXP_STATE_OUTPUT
) != NULL
);
519 if(greedyjoin
== TRU1
){ /* TODO speed this up! */
523 assert(lcap
!= NULL
);
524 i
= lcap
->ca_arg
.ca_str
.l
;
525 lcap
->ca_arg
.ca_str
.l
+= 1 + ncap
.ca_arg
.ca_str
.l
;
526 cp
= n_autorec_alloc(lcap
->ca_arg
.ca_str
.l
+1);
527 memcpy(cp
, lcap
->ca_arg
.ca_str
.s
, i
);
528 lcap
->ca_arg
.ca_str
.s
= cp
;
530 memcpy(&cp
[i
], ncap
.ca_arg
.ca_str
.s
, ncap
.ca_arg
.ca_str
.l
+1);
532 struct n_cmd_arg
*cap
;
534 cap
= n_autorec_alloc(sizeof *cap
);
535 memcpy(cap
, &ncap
, sizeof ncap
);
548 if((shin
.l
> 0 || cookie
!= NULL
) &&
549 (ncap
.ca_ent_flags
[0] & n_CMD_ARG_DESC_GREEDY
)){
551 greedyjoin
= ((ncap
.ca_ent_flags
[0] & n_CMD_ARG_DESC_GREEDY_JOIN
) &&
552 (ncap
.ca_ent_flags
[0] &
553 (n_CMD_ARG_DESC_STRING
| n_CMD_ARG_DESC_WYSH
)))
559 if(cad_idx
< cadp
->cad_no
&&
560 !(cadp
->cad_ent_flags
[cad_idx
][0] & n_CMD_ARG_DESC_OPTION
))
563 lcap
= (struct n_cmd_arg
*)-1;
566 return (lcap
!= NULL
);
571 for(i
= 0; (i
< cadp
->cad_no
&&
572 !(cadp
->cad_ent_flags
[i
][0] & n_CMD_ARG_DESC_OPTION
)); ++i
)
575 n_err(_("`%s': parsing stopped after %" PRIuZ
" arguments "
576 "(need %" PRIuZ
"%s)\n"
579 cadp
->cad_name
, parsed_args
, i
, (i
== cadp
->cad_no
? n_empty
: "+"),
580 (int)shin_orig
.l
, shin_orig
.s
,
581 (int)shin
.l
, shin
.s
);
588 n_cmd_arg_save_to_heap(struct n_cmd_arg_ctx
const *cacp
){
589 struct n_cmd_arg
*ncap
;
590 struct n_cmd_arg_ctx
*ncacp
;
592 struct n_cmd_arg
const *cap
;
596 /* For simplicity, save it all in once chunk, so that it can be thrown away
597 * with a simple n_free() from whoever is concerned */
599 for(cap
= cacp
->cac_arg
; cap
!= NULL
; cap
= cap
->ca_next
){
600 i
= cap
->ca_arg
.ca_str
.l
+1;
602 len
+= sizeof(*cap
) + i
;
604 if(cacp
->cac_vput
!= NULL
)
605 len
+= strlen(cacp
->cac_vput
) +1;
607 ncacp
= n_alloc(len
);
609 buf
= (char*)&ncacp
[1];
611 for(ncap
= NULL
, cap
= cacp
->cac_arg
; cap
!= NULL
; cap
= cap
->ca_next
){
615 DBG( memset(vp
, 0, sizeof *ncap
); )
622 ncap
->ca_next
= NULL
;
623 ncap
->ca_ent_flags
[0] = cap
->ca_ent_flags
[0];
624 ncap
->ca_ent_flags
[1] = cap
->ca_ent_flags
[1];
625 ncap
->ca_arg_flags
= cap
->ca_arg_flags
;
626 memcpy(ncap
->ca_arg
.ca_str
.s
= (char*)&ncap
[1], cap
->ca_arg
.ca_str
.s
,
627 (ncap
->ca_arg
.ca_str
.l
= i
= cap
->ca_arg
.ca_str
.l
) +1);
630 buf
+= sizeof(*ncap
) + i
;
633 if(cacp
->cac_vput
!= NULL
){
634 ncacp
->cac_vput
= buf
;
635 memcpy(buf
, cacp
->cac_vput
, strlen(cacp
->cac_vput
) +1);
637 ncacp
->cac_vput
= NULL
;
642 FL
struct n_cmd_arg_ctx
*
643 n_cmd_arg_restore_from_heap(void *vp
){
644 struct n_cmd_arg
*cap
, *ncap
;
645 struct n_cmd_arg_ctx
*cacp
, *rv
;
648 rv
= n_autorec_alloc(sizeof *rv
);
652 for(ncap
= NULL
, cap
= cacp
->cac_arg
; cap
!= NULL
; cap
= cap
->ca_next
){
653 vp
= n_autorec_alloc(sizeof(*ncap
) + cap
->ca_arg
.ca_str
.l
+1);
654 DBG( memset(vp
, 0, sizeof *ncap
); )
661 ncap
->ca_next
= NULL
;
662 ncap
->ca_ent_flags
[0] = cap
->ca_ent_flags
[0];
663 ncap
->ca_ent_flags
[1] = cap
->ca_ent_flags
[1];
664 ncap
->ca_arg_flags
= cap
->ca_arg_flags
;
665 memcpy(ncap
->ca_arg
.ca_str
.s
= (char*)&ncap
[1], cap
->ca_arg
.ca_str
.s
,
666 (ncap
->ca_arg
.ca_str
.l
= cap
->ca_arg
.ca_str
.l
) +1);
669 if(cacp
->cac_vput
!= NULL
)
670 rv
->cac_vput
= savestr(cacp
->cac_vput
);
676 getrawlist(bool_t wysh
, char **res_dat
, size_t res_size
,
677 char const *line
, size_t linesize
){
681 n_pstate
&= ~n_PS_ARGLIST_MASK
;
686 }else if(UICMP(z
, res_size
, >, INT_MAX
))
693 /* And assuming result won't grow input */
694 char c2
, c
, quotec
, *cp2
, *linebuf
;
696 linebuf
= n_lofi_alloc(linesize
);
699 for(; blankchar(*line
); ++line
)
704 if(UICMP(z
, res_no
, >=, res_size
)){
705 n_err(_("Too many input tokens for result storage\n"));
713 /* TODO v15: complete switch in order mirror known behaviour */
714 while((c
= *line
++) != '\0'){
720 if((c2
= *line
++) == quotec
)
725 }else if(c
== '"' || c
== '\''){
729 if((c2
= *line
++) != '\0')
733 }else if(blankchar(c
))
738 res_dat
[res_no
++] = savestrbuf(linebuf
, PTR2SIZE(cp2
- linebuf
));
743 n_lofi_free(linebuf
);
745 /* sh(1) compat mode. Prepare shell token-wise */
746 struct n_string store
;
750 n_string_creat_auto(&store
);
751 input
.s
= n_UNCONST(line
);
756 if(UICMP(z
, res_no
, >=, res_size
)){
757 n_err(_("Too many input tokens for result storage\n"));
763 enum n_shexp_state shs
;
765 if((shs
= n_shexp_parse_token((n_SHEXP_PARSE_LOG
|
766 (cookie
== NULL
? n_SHEXP_PARSE_TRIM_SPACE
: 0) |
767 /* TODO not here in old style n_SHEXP_PARSE_IFS_VAR |*/
768 n_SHEXP_PARSE_META_SEMICOLON
),
769 &store
, &input
, &cookie
)
770 ) & n_SHEXP_STATE_ERR_MASK
){
771 /* Simply ignore Unicode error, just keep the normalized \[Uu] */
772 if((shs
& n_SHEXP_STATE_ERR_MASK
) != n_SHEXP_STATE_ERR_UNICODE
){
778 if(shs
& n_SHEXP_STATE_OUTPUT
){
779 if(shs
& n_SHEXP_STATE_CONTROL
)
780 n_pstate
|= n_PS_WYSHLIST_SAW_CONTROL
;
782 res_dat
[res_no
++] = n_string_cp(&store
);
783 n_string_drop_ownership(&store
);
786 if(shs
& n_SHEXP_STATE_STOP
)
791 n_string_gut(&store
);
795 res_dat
[(size_t)res_no
] = NULL
;