cc-test.sh: reorder a bit more logical (what i hope)
[s-mailx.git] / cmd-tab.c
blob371fe7fbd29f00ed3de3a08f101d4afaee2484aa
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
19 * are met:
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
39 * SUCH DAMAGE.
41 #undef n_FILE
42 #define n_FILE cmd_tab
44 #ifndef HAVE_AMALGAMATION
45 # include "nail.h"
46 #endif
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);
51 #endif
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 */
62 #include "cmd-tab.h"
63 static struct n_cmd_desc const a_ctab_ctable[] = {
64 #include "cmd-tab.h"
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
71 # define DS(S) , S
72 #else
73 # define DS(S)
74 #endif
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")) }
81 #undef DS
83 #ifdef HAVE_DOCSTRINGS
84 static char const *
85 a_ctab_cmdinfo(struct n_cmd_desc const *cdp){
86 struct n_string rvb, *rv;
87 char const *cp;
88 NYD2_ENTER;
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");
96 break;
97 case n_CMD_ARG_TYPE_STRING:
98 case n_CMD_ARG_TYPE_RAWDAT:
99 cp = N_("string data");
100 break;
101 case n_CMD_ARG_TYPE_RAWLIST:
102 cp = N_("old-style quoting");
103 break;
104 case n_CMD_ARG_TYPE_NDMLIST:
105 cp = N_("message-list (no default)");
106 break;
107 case n_CMD_ARG_TYPE_WYRA:
108 cp = N_("`wysh' for sh(1)ell-style quoting");
109 break;
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");
114 break;
115 default:
116 case n_CMD_ARG_TYPE_ARG:{
117 ui32_t flags;
118 size_t i;
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){
124 if(i != 0)
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"));
135 break;
136 default:
137 case n_CMD_ARG_DESC_WYSH:
138 rv = n_string_push_cp(rv, _("eval"));
139 break;
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, ']');
146 cp = NULL;
147 }break;
149 if(cp != NULL)
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);
177 NYD2_LEAVE;
178 return cp;
180 #endif /* HAVE_DOCSTRINGS */
182 static int
183 a_ctab_c_list(void *vp){
184 FILE *fp;
185 struct n_cmd_desc const **cdpa, *cdp, **cdpa_curr;
186 size_t i, l, scrwid;
187 NYD_ENTER;
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];
196 cdpa[i] = NULL;
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)
202 fp = n_stdout;
204 scrwid = n_SCRNWIDTH_FOR_LISTS;
206 fprintf(fp, _("Commands are:\n"));
207 l = 1;
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 = "]";
213 else
214 pre = suf = n_empty;
216 #ifdef HAVE_DOCSTRINGS
217 if(n_poption & n_PO_D_V){
218 fprintf(fp, "%s%s%s\n", pre, cdp->cd_name, suf);
219 ++l;
220 fprintf(fp, " : %s\n", V_(cdp->cd_doc));
221 ++l;
222 fprintf(fp, " : %s\n", a_ctab_cmdinfo(cdp));
223 ++l;
224 }else
225 #endif
227 size_t j;
229 j = strlen(cdp->cd_name);
230 if(*pre != '\0')
231 j += 2;
233 if((i += j + 2) > scrwid){
234 i = j;
235 fprintf(fp, "\n");
236 ++l;
238 fprintf(fp, (*cdpa_curr != NULL ? "%s%s%s, " : "%s%s%s\n"),
239 pre, cdp->cd_name, suf);
243 if(fp != n_stdout){
244 page_or_print(fp, l);
245 Fclose(fp);
247 NYD_LEAVE;
248 return 0;
251 static int
252 a_ctab__pcmd_cmp(void const *s1, void const *s2){
253 struct n_cmd_desc const * const *cdpa1, * const *cdpa2;
254 int rv;
255 NYD2_ENTER;
257 cdpa1 = s1;
258 cdpa2 = s2;
259 rv = strcmp((*cdpa1)->cd_name, (*cdpa2)->cd_name);
260 NYD2_LEAVE;
261 return rv;
264 static int
265 a_ctab_c_help(void *vp){
266 int rv;
267 char const *arg;
268 NYD_ENTER;
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" */
280 alias_name = NULL;
281 while((aepx = n_commandalias_exists(arg, &alias_exp)) != NULL &&
282 (alias_name == NULL || strcmp(alias_name, aepx))){
283 alias_name = aepx;
284 fprintf(n_stdout, "%s -> ", arg);
285 arg = alias_exp->s;
288 cdp_max = &(cdp = a_ctab_ctable)[n_NELEM(a_ctab_ctable)];
289 jredo:
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);
295 }else
296 continue;
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));
302 #endif
303 putc('\n', n_stdout);
304 rv = 0;
305 goto jleave;
308 if(cdp_max == &a_ctab_ctable[n_NELEM(a_ctab_ctable)]){
309 cdp_max = &(cdp =
310 a_ctab_ctable_plus)[n_NELEM(a_ctab_ctable_plus)];
311 goto jredo;
314 if(alias_name != NULL){
315 fprintf(n_stdout, "%s\n", n_shexp_quote_cp(arg, TRU1));
316 rv = 0;
317 }else{
318 n_err(_("Unknown command: `%s'\n"), arg);
319 rv = 1;
321 }else{
322 /* Very ugly, but take care for compiler supported string lengths :( */
323 #ifdef HAVE_UISTRINGS
324 fputs(n_progname, n_stdout);
325 fputs(_(
326 " commands -- <msglist> denotes message specifications,\n"
327 "e.g., 1-5, :n or . (current, the \"dot\"), separated by spaces:\n"),
328 n_stdout);
329 fputs(_(
330 "\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"),
337 n_stdout);
339 fputs(_(
340 "\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"),
347 n_stdout);
349 fputs(_(
350 "\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"),
358 n_stdout);
359 #endif /* HAVE_UISTRINGS */
361 rv = (ferror(n_stdout) != 0);
363 jleave:
364 NYD_LEAVE;
365 return rv;
368 FL char const *
369 n_cmd_isolate(char const *cmd){
370 NYD2_ENTER;
371 while(*cmd != '\0' &&
372 strchr("\\!~|? \t0123456789&%@$^.:/-+*'\",;(`", *cmd) == NULL)
373 ++cmd;
374 NYD2_LEAVE;
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;
381 NYD2_ENTER;
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))
386 goto jleave;
387 cdp = NULL;
388 jleave:
389 NYD2_LEAVE;
390 return cdp;
393 FL struct n_cmd_desc const *
394 n_cmd_default(void){
395 struct n_cmd_desc const *cdp;
396 NYD2_ENTER;
398 cdp = &a_ctab_ctable[0];
399 NYD2_LEAVE;
400 return cdp;
403 FL bool_t
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;
408 void const *cookie;
409 size_t cad_idx, parsed_args;
410 struct n_cmd_arg_desc const *cadp;
411 NYD_ENTER;
413 assert(cacp->cac_inlen == 0 || cacp->cac_indat != NULL);
414 assert(cacp->cac_desc->cad_no > 0);
415 #ifdef HAVE_DEBUG
416 /* C99 */{
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);
422 assert(!opt_seen ||
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)
425 opt_seen = TRU1;
426 assert(!(cadp->cad_ent_flags[cad_idx][0] & n_CMD_ARG_DESC_GREEDY) ||
427 cad_idx + 1 == cadp->cad_no);
430 #endif
432 shin.s = n_UNCONST(cacp->cac_indat); /* "logical" only */
433 shin.l = (cacp->cac_inlen == UIZ_MAX ? strlen(shin.s) : cacp->cac_inlen);
434 shin_orig = shin;
435 cacp->cac_no = 0;
436 cacp->cac_arg = lcap = NULL;
438 cookie = NULL;
439 parsed_args = 0;
440 greedyjoin = FAL0;
442 for(cadp = cacp->cac_desc, cad_idx = 0; shin.l > 0 && cad_idx < cadp->cad_no;
443 ++cad_idx){
444 jredo:
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);
450 addca = FAL0;
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;
455 size_t i = shin.l;
457 while(i > 0 && blankspacechar(*cp))
458 ++cp, --i;
460 ncap.ca_arg.ca_str.s = cp;
461 while(i > 0 && !blankspacechar(*cp))
462 ++cp, --i;
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))
467 ++cp, --i;
468 ncap.ca_inlen = PTR2SIZE(cp - ncap.ca_indat);
469 shin.s = cp;
470 shin.l = i;
471 addca = TRU1;
472 }break;
473 default:
474 case n_CMD_ARG_DESC_WYSH:{
475 struct n_string shou, *shoup;
476 enum n_shexp_state shs;
477 ui32_t addflags;
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;
482 else
483 addflags = n_SHEXP_PARSE_NONE;
485 shoup = n_string_creat_auto(&shou);
486 ncap.ca_arg_flags =
487 shs = n_shexp_parse_token((ncap.ca_ent_flags[1] | addflags |
488 n_SHEXP_PARSE_TRIM_SPACE | n_SHEXP_PARSE_LOG),
489 shoup, &shin,
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)
495 break;
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);
500 n_string_gut(shoup);
502 if(shs & n_SHEXP_STATE_ERR_MASK)
503 goto jerr;
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))
507 goto jleave;
508 addca = TRUM1;
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))
511 goto jerr;
512 else
513 addca = ((shs & n_SHEXP_STATE_OUTPUT) != NULL);
514 }break;
516 ++parsed_args;
518 if(addca){
519 if(greedyjoin == TRU1){ /* TODO speed this up! */
520 char *cp;
521 size_t i;
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;
529 cp[i++] = ' ';
530 memcpy(&cp[i], ncap.ca_arg.ca_str.s, ncap.ca_arg.ca_str.l +1);
531 }else{
532 struct n_cmd_arg *cap;
534 cap = n_autorec_alloc(sizeof *cap);
535 memcpy(cap, &ncap, sizeof ncap);
536 if(lcap == NULL)
537 cacp->cac_arg = cap;
538 else
539 lcap->ca_next = cap;
540 lcap = cap;
541 ++cacp->cac_no;
544 if(addca == TRUM1)
545 goto jleave;
548 if((shin.l > 0 || cookie != NULL) &&
549 (ncap.ca_ent_flags[0] & n_CMD_ARG_DESC_GREEDY)){
550 if(!greedyjoin)
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)))
554 ? TRU1 : TRUM1;
555 goto jredo;
559 if(cad_idx < cadp->cad_no &&
560 !(cadp->cad_ent_flags[cad_idx][0] & n_CMD_ARG_DESC_OPTION))
561 goto jerr;
563 lcap = (struct n_cmd_arg*)-1;
564 jleave:
565 NYD_LEAVE;
566 return (lcap != NULL);
568 jerr:{
569 size_t i;
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"
577 " Input: %.*s\n"
578 " Stopped: %.*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);
583 lcap = NULL;
584 goto jleave;
587 FL void *
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;
591 char *buf;
592 struct n_cmd_arg const *cap;
593 size_t len, i;
594 NYD2_ENTER;
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 */
598 len = sizeof *cacp;
599 for(cap = cacp->cac_arg; cap != NULL; cap = cap->ca_next){
600 i = cap->ca_arg.ca_str.l +1;
601 i = n_ALIGN(i);
602 len += sizeof(*cap) + i;
604 if(cacp->cac_vput != NULL)
605 len += strlen(cacp->cac_vput) +1;
607 ncacp = n_alloc(len);
608 *ncacp = *cacp;
609 buf = (char*)&ncacp[1];
611 for(ncap = NULL, cap = cacp->cac_arg; cap != NULL; cap = cap->ca_next){
612 void *vp;
614 vp = buf;
615 DBG( memset(vp, 0, sizeof *ncap); )
617 if(ncap == NULL)
618 ncacp->cac_arg = vp;
619 else
620 ncap->ca_next = vp;
621 ncap = vp;
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);
629 i = n_ALIGN(i);
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);
636 }else
637 ncacp->cac_vput = NULL;
638 NYD2_LEAVE;
639 return ncacp;
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;
646 NYD2_ENTER;
648 rv = n_autorec_alloc(sizeof *rv);
649 cacp = vp;
650 *rv = *cacp;
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); )
656 if(ncap == NULL)
657 rv->cac_arg = vp;
658 else
659 ncap->ca_next = vp;
660 ncap = vp;
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);
671 NYD2_LEAVE;
672 return rv;
675 FL int
676 getrawlist(bool_t wysh, char **res_dat, size_t res_size,
677 char const *line, size_t linesize){
678 int res_no;
679 NYD_ENTER;
681 n_pstate &= ~n_PS_ARGLIST_MASK;
683 if(res_size == 0){
684 res_no = -1;
685 goto jleave;
686 }else if(UICMP(z, res_size, >, INT_MAX))
687 res_size = INT_MAX;
688 else
689 --res_size;
690 res_no = 0;
692 if(!wysh){
693 /* And assuming result won't grow input */
694 char c2, c, quotec, *cp2, *linebuf;
696 linebuf = n_lofi_alloc(linesize);
698 for(;;){
699 for(; blankchar(*line); ++line)
701 if(*line == '\0')
702 break;
704 if(UICMP(z, res_no, >=, res_size)){
705 n_err(_("Too many input tokens for result storage\n"));
706 res_no = -1;
707 break;
710 cp2 = linebuf;
711 quotec = '\0';
713 /* TODO v15: complete switch in order mirror known behaviour */
714 while((c = *line++) != '\0'){
715 if(quotec != '\0'){
716 if(c == quotec){
717 quotec = '\0';
718 continue;
719 }else if(c == '\\'){
720 if((c2 = *line++) == quotec)
721 c = c2;
722 else
723 --line;
725 }else if(c == '"' || c == '\''){
726 quotec = c;
727 continue;
728 }else if(c == '\\'){
729 if((c2 = *line++) != '\0')
730 c = c2;
731 else
732 --line;
733 }else if(blankchar(c))
734 break;
735 *cp2++ = c;
738 res_dat[res_no++] = savestrbuf(linebuf, PTR2SIZE(cp2 - linebuf));
739 if(c == '\0')
740 break;
743 n_lofi_free(linebuf);
744 }else{
745 /* sh(1) compat mode. Prepare shell token-wise */
746 struct n_string store;
747 struct str input;
748 void const *cookie;
750 n_string_creat_auto(&store);
751 input.s = n_UNCONST(line);
752 input.l = linesize;
753 cookie = NULL;
755 for(;;){
756 if(UICMP(z, res_no, >=, res_size)){
757 n_err(_("Too many input tokens for result storage\n"));
758 res_no = -1;
759 break;
762 /* C99 */{
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){
773 res_no = -1;
774 break;
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)
787 break;
791 n_string_gut(&store);
794 if(res_no >= 0)
795 res_dat[(size_t)res_no] = NULL;
796 jleave:
797 NYD_LEAVE;
798 return res_no;
801 /* s-it-mode */