"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.7/cmd-tab.c" (16 Feb 2018, 23956 Bytes) of package /linux/misc/s-nail-14.9.7.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "cmd-tab.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.6_vs_14.9.7.

    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.
    8  *
    9  * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
   10  */
   11 /* Command table and getrawlist() also:
   12  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
   13  *
   14  * Copyright (c) 1980, 1993
   15  *      The Regents of the University of California.  All rights reserved.
   16  *
   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.
   28  *
   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.
   40  */
   41 #undef n_FILE
   42 #define n_FILE cmd_tab
   43 
   44 #ifndef HAVE_AMALGAMATION
   45 # include "nail.h"
   46 #endif
   47 
   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
   52 
   53 /* Print a list of all commands */
   54 static int a_ctab_c_list(void *vp);
   55 
   56 static int a_ctab__pcmd_cmp(void const *s1, void const *s2);
   57 
   58 /* `help' / `?' command */
   59 static int a_ctab_c_help(void *vp);
   60 
   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"
   65 };
   66 
   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    { "-", (int(*)(void*))-1, n_CMD_ARG_TYPE_WYSH, 0, 0, NULL
   79       DS(N_("Print out the preceding message")) }
   80 };
   81 #undef DS
   82 
   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;
   89 
   90    rv = n_string_creat_auto(&rvb);
   91    rv = n_string_reserve(rv, 80);
   92 
   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;
  120 
  121       rv = n_string_push_cp(rv, _("argument tokens: "));
  122 
  123       for(cadp = cdp->cd_cadp, i = 0; i < cadp->cad_no; ++i){
  124          if(i != 0)
  125             rv = n_string_push_c(rv, ',');
  126 
  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;
  140          }
  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, ']');
  145       }
  146       cp = NULL;
  147       }break;
  148    }
  149    if(cp != NULL)
  150       rv = n_string_push_cp(rv, V_(cp));
  151 
  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, _(" | *!*"));
  159 
  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"));
  172 
  173    if(cdp->cd_caflags & n_CMD_ARG_G)
  174       rv = n_string_push_cp(rv, _(" | gabby"));
  175 
  176    cp = n_string_cp(rv);
  177    NYD2_LEAVE;
  178    return cp;
  179 }
  180 #endif /* HAVE_DOCSTRINGS */
  181 
  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;
  188 
  189    i = n_NELEM(a_ctab_ctable) + n_NELEM(a_ctab_ctable_plus) +1;
  190    cdpa = n_autorec_alloc(sizeof(cdp) * i);
  191 
  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;
  197 
  198    if(*(void**)vp == NULL)
  199       qsort(cdpa, i, sizeof(*cdpa), &a_ctab__pcmd_cmp);
  200 
  201    if((fp = Ftmp(NULL, "list", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL)
  202       fp = n_stdout;
  203 
  204    scrwid = n_SCRNWIDTH_FOR_LISTS;
  205 
  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;
  210 
  211       if(cdp->cd_func == NULL)
  212          pre = "[", suf = "]";
  213       else
  214          pre = suf = n_empty;
  215 
  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
  226            {
  227          size_t j;
  228 
  229          j = strlen(cdp->cd_name);
  230          if(*pre != '\0')
  231             j += 2;
  232 
  233          if((i += j + 2) > scrwid){
  234             i = j;
  235             fprintf(fp, "\n");
  236             ++l;
  237          }
  238          fprintf(fp, (*cdpa_curr != NULL ? "%s%s%s, " : "%s%s%s\n"),
  239             pre, cdp->cd_name, suf);
  240       }
  241    }
  242 
  243    if(fp != n_stdout){
  244       page_or_print(fp, l);
  245       Fclose(fp);
  246    }
  247    NYD_LEAVE;
  248    return 0;
  249 }
  250 
  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;
  256 
  257    cdpa1 = s1;
  258    cdpa2 = s2;
  259    rv = strcmp((*cdpa1)->cd_name, (*cdpa2)->cd_name);
  260    NYD2_LEAVE;
  261    return rv;
  262 }
  263 
  264 static int
  265 a_ctab_c_help(void *vp){
  266    int rv;
  267    char const *arg;
  268    NYD_ENTER;
  269 
  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;
  275 
  276       /* Aliases take precedence */
  277       if((alias_name = n_commandalias_exists(arg, &alias_exp)) != NULL){
  278          fprintf(n_stdout, "%s -> ", arg);
  279          arg = alias_exp->s;
  280       }
  281 
  282       cdp_max = &(cdp = a_ctab_ctable)[n_NELEM(a_ctab_ctable)];
  283 jredo:
  284       for(; cdp < cdp_max; ++cdp){
  285          if(is_prefix(arg, cdp->cd_name)){
  286             fputs(arg, n_stdout);
  287             if(strcmp(arg, cdp->cd_name))
  288                fprintf(n_stdout, " (%s)", cdp->cd_name);
  289          }else
  290             continue;
  291 
  292 #ifdef HAVE_DOCSTRINGS
  293          fprintf(n_stdout, ": %s", V_(cdp->cd_doc));
  294          if(n_poption & n_PO_D_V)
  295             fprintf(n_stdout, "\n  : %s", a_ctab_cmdinfo(cdp));
  296 #endif
  297          putc('\n', n_stdout);
  298          rv = 0;
  299          goto jleave;
  300       }
  301 
  302       if(cdp_max == &a_ctab_ctable[n_NELEM(a_ctab_ctable)]){
  303          cdp_max = &(cdp =
  304                a_ctab_ctable_plus)[n_NELEM(a_ctab_ctable_plus)];
  305          goto jredo;
  306       }
  307 
  308       if(alias_name != NULL){
  309          fprintf(n_stdout, "%s\n", n_shexp_quote_cp(arg, TRU1));
  310          rv = 0;
  311       }else{
  312          n_err(_("Unknown command: `%s'\n"), arg);
  313          rv = 1;
  314       }
  315    }else{
  316       /* Very ugly, but take care for compiler supported string lengths :( */
  317       fputs(n_progname, n_stdout);
  318       fputs(_(
  319          " commands -- <msglist> denotes message specifications,\n"
  320          "e.g., 1-5, :n or . (current, the \"dot\"), separated by spaces:\n"),
  321          n_stdout);
  322       fputs(_(
  323 "\n"
  324 "type <msglist>         type (`print') messages (honour `headerpick' etc.)\n"
  325 "Type <msglist>         like `type' but always show all headers\n"
  326 "next                   goto and type next message\n"
  327 "from <msglist>         (search and) print header summary for the given list\n"
  328 "headers                header summary for messages surrounding \"dot\"\n"
  329 "delete <msglist>       delete messages (can be `undelete'd)\n"),
  330          n_stdout);
  331 
  332       fputs(_(
  333 "\n"
  334 "save <msglist> folder  append messages to folder and mark as saved\n"
  335 "copy <msglist> folder  like `save', but don't mark them (`move' moves)\n"
  336 "write <msglist> file   write message contents to file (prompts for parts)\n"
  337 "Reply <msglist>        reply to message senders only\n"
  338 "reply <msglist>        like `Reply', but address all recipients\n"
  339 "Lreply <msglist>       forced mailing-list `reply' (see `mlist')\n"),
  340          n_stdout);
  341 
  342       fputs(_(
  343 "\n"
  344 "mail <recipients>      compose a mail for the given recipients\n"
  345 "file folder            change to another mailbox\n"
  346 "File folder            like `file', but open readonly\n"
  347 "quit                   quit and apply changes to the current mailbox\n"
  348 "xit or exit            like `quit', but discard changes\n"
  349 "!shell command         shell escape\n"
  350 "list [<anything>]      all available commands [in search order]\n"),
  351          n_stdout);
  352 
  353       rv = (ferror(n_stdout) != 0);
  354    }
  355 jleave:
  356    NYD_LEAVE;
  357    return rv;
  358 }
  359 
  360 FL char const *
  361 n_cmd_isolate(char const *cmd){
  362    NYD2_ENTER;
  363    while(*cmd != '\0' &&
  364          strchr("\\!~|? \t0123456789&%@$^.:/-+*'\",;(`", *cmd) == NULL)
  365       ++cmd;
  366    NYD2_LEAVE;
  367    return n_UNCONST(cmd);
  368 }
  369 
  370 FL struct n_cmd_desc const *
  371 n_cmd_firstfit(char const *cmd){ /* TODO *hashtable*! linear list search!!! */
  372    struct n_cmd_desc const *cdp;
  373    NYD2_ENTER;
  374 
  375    for(cdp = a_ctab_ctable; cdp < &a_ctab_ctable[n_NELEM(a_ctab_ctable)]; ++cdp)
  376       if(*cmd == *cdp->cd_name && cdp->cd_func != NULL &&
  377             is_prefix(cmd, cdp->cd_name))
  378          goto jleave;
  379    cdp = NULL;
  380 jleave:
  381    NYD2_LEAVE;
  382    return cdp;
  383 }
  384 
  385 FL struct n_cmd_desc const *
  386 n_cmd_default(void){
  387    struct n_cmd_desc const *cdp;
  388    NYD2_ENTER;
  389 
  390    cdp = &a_ctab_ctable[0];
  391    NYD2_LEAVE;
  392    return cdp;
  393 }
  394 
  395 FL bool_t
  396 n_cmd_arg_parse(struct n_cmd_arg_ctx *cacp){
  397    struct n_cmd_arg ncap, *lcap;
  398    struct str shin_orig, shin;
  399    bool_t addca, greedyjoin;
  400    void const *cookie;
  401    size_t cad_idx, parsed_args;
  402    struct n_cmd_arg_desc const *cadp;
  403    NYD_ENTER;
  404 
  405    assert(cacp->cac_inlen == 0 || cacp->cac_indat != NULL);
  406    assert(cacp->cac_desc->cad_no > 0);
  407 #ifdef HAVE_DEBUG
  408    /* C99 */{
  409       bool_t opt_seen = FAL0;
  410 
  411       for(cadp = cacp->cac_desc, cad_idx = 0;
  412             cad_idx < cadp->cad_no; ++cad_idx){
  413          assert(cadp->cad_ent_flags[cad_idx][0] & n__CMD_ARG_DESC_TYPE_MASK);
  414          assert(!opt_seen ||
  415             (cadp->cad_ent_flags[cad_idx][0] & n_CMD_ARG_DESC_OPTION));
  416          if(cadp->cad_ent_flags[cad_idx][0] & n_CMD_ARG_DESC_OPTION)
  417             opt_seen = TRU1;
  418          assert(!(cadp->cad_ent_flags[cad_idx][0] & n_CMD_ARG_DESC_GREEDY) ||
  419             cad_idx + 1 == cadp->cad_no);
  420       }
  421    }
  422 #endif
  423 
  424    shin.s = n_UNCONST(cacp->cac_indat); /* "logical" only */
  425    shin.l = (cacp->cac_inlen == UIZ_MAX ? strlen(shin.s) : cacp->cac_inlen);
  426    shin_orig = shin;
  427    cacp->cac_no = 0;
  428    cacp->cac_arg = lcap = NULL;
  429 
  430    cookie = NULL;
  431    parsed_args = 0;
  432    greedyjoin = FAL0;
  433 
  434    for(cadp = cacp->cac_desc, cad_idx = 0; shin.l > 0 && cad_idx < cadp->cad_no;
  435          ++cad_idx){
  436 jredo:
  437       memset(&ncap, 0, sizeof ncap);
  438       ncap.ca_indat = shin.s;
  439       /* >ca_inline once we know */
  440       memcpy(&ncap.ca_ent_flags[0], &cadp->cad_ent_flags[cad_idx][0],
  441          sizeof ncap.ca_ent_flags);
  442       addca = FAL0;
  443 
  444       switch(ncap.ca_ent_flags[0] & n__CMD_ARG_DESC_TYPE_MASK){
  445       case n_CMD_ARG_DESC_STRING:{ /* TODO \ escaping? additional type!? */
  446          char /*const*/ *cp = shin.s;
  447          size_t i = shin.l;
  448 
  449          while(i > 0 && blankspacechar(*cp))
  450             ++cp, --i;
  451 
  452          ncap.ca_arg.ca_str.s = cp;
  453          while(i > 0 && !blankspacechar(*cp))
  454             ++cp, --i;
  455          ncap.ca_arg.ca_str.s = savestrbuf(ncap.ca_arg.ca_str.s,
  456                ncap.ca_arg.ca_str.l = PTR2SIZE(cp - ncap.ca_arg.ca_str.s));
  457 
  458          while(i > 0 && blankspacechar(*cp))
  459             ++cp, --i;
  460          ncap.ca_inlen = PTR2SIZE(cp - ncap.ca_indat);
  461          shin.s = cp;
  462          shin.l = i;
  463          addca = TRU1;
  464          }break;
  465       default:
  466       case n_CMD_ARG_DESC_WYSH:{
  467          struct n_string shou, *shoup;
  468          enum n_shexp_state shs;
  469          ui32_t addflags;
  470 
  471          if(cad_idx == cadp->cad_no - 1 ||
  472                (cadp->cad_ent_flags[cad_idx + 1][0] & n_CMD_ARG_DESC_OPTION))
  473             addflags = n_SHEXP_PARSE_META_SEMICOLON;
  474          else
  475             addflags = n_SHEXP_PARSE_NONE;
  476 
  477          shoup = n_string_creat_auto(&shou);
  478          ncap.ca_arg_flags =
  479          shs = n_shexp_parse_token((ncap.ca_ent_flags[1] | addflags |
  480                   n_SHEXP_PARSE_TRIM_SPACE | n_SHEXP_PARSE_LOG),
  481                shoup, &shin,
  482                (ncap.ca_ent_flags[0] & n_CMD_ARG_DESC_GREEDY ? &cookie : NULL));
  483          ncap.ca_inlen = PTR2SIZE(shin.s - ncap.ca_indat);
  484          if((shs & (n_SHEXP_STATE_OUTPUT | n_SHEXP_STATE_ERR_MASK)) ==
  485                n_SHEXP_STATE_OUTPUT){
  486             if((shs & n_SHEXP_STATE_META_SEMICOLON) && shou.s_len == 0)
  487                break;
  488             ncap.ca_arg.ca_str.s = n_string_cp(shoup);
  489             ncap.ca_arg.ca_str.l = shou.s_len;
  490             shoup = n_string_drop_ownership(shoup);
  491          }
  492          n_string_gut(shoup);
  493 
  494          if(shs & n_SHEXP_STATE_ERR_MASK)
  495             goto jerr;
  496          if((shs & n_SHEXP_STATE_STOP) &&
  497                (ncap.ca_ent_flags[0] & n_CMD_ARG_DESC_HONOUR_STOP)){
  498             if(!(shs & n_SHEXP_STATE_OUTPUT))
  499                goto jleave;
  500             addca = TRUM1;
  501          }else
  502             addca = TRU1;
  503          }break;
  504       }
  505       ++parsed_args;
  506 
  507       if(addca){
  508          if(greedyjoin == TRU1){ /* TODO speed this up! */
  509             char *cp;
  510             size_t i;
  511 
  512             assert(lcap != NULL);
  513             i = lcap->ca_arg.ca_str.l;
  514             lcap->ca_arg.ca_str.l += 1 + ncap.ca_arg.ca_str.l;
  515             cp = salloc(lcap->ca_arg.ca_str.l +1);
  516             memcpy(cp, lcap->ca_arg.ca_str.s, i);
  517             lcap->ca_arg.ca_str.s = cp;
  518             cp[i++] = ' ';
  519             memcpy(&cp[i], ncap.ca_arg.ca_str.s, ncap.ca_arg.ca_str.l +1);
  520          }else{
  521             struct n_cmd_arg *cap;
  522 
  523             cap = salloc(sizeof *cap);
  524             memcpy(cap, &ncap, sizeof ncap);
  525             if(lcap == NULL)
  526                cacp->cac_arg = cap;
  527             else
  528                lcap->ca_next = cap;
  529             lcap = cap;
  530             ++cacp->cac_no;
  531          }
  532 
  533          if(addca == TRUM1)
  534             goto jleave;
  535       }
  536 
  537       if((shin.l > 0 || cookie != NULL) &&
  538             (ncap.ca_ent_flags[0] & n_CMD_ARG_DESC_GREEDY)){
  539          if(!greedyjoin)
  540             greedyjoin = ((ncap.ca_ent_flags[0] & n_CMD_ARG_DESC_GREEDY_JOIN) &&
  541                      (ncap.ca_ent_flags[0] &
  542                         (n_CMD_ARG_DESC_STRING | n_CMD_ARG_DESC_WYSH)))
  543                   ? TRU1 : TRUM1;
  544          goto jredo;
  545       }
  546    }
  547 
  548    if(cad_idx < cadp->cad_no &&
  549          !(cadp->cad_ent_flags[cad_idx][0] & n_CMD_ARG_DESC_OPTION))
  550       goto jerr;
  551 
  552    lcap = (struct n_cmd_arg*)-1;
  553 jleave:
  554    NYD_LEAVE;
  555    return (lcap != NULL);
  556 
  557 jerr:{
  558       size_t i;
  559 
  560       for(i = 0; (i < cadp->cad_no &&
  561             !(cadp->cad_ent_flags[i][0] & n_CMD_ARG_DESC_OPTION)); ++i)
  562          ;
  563 
  564       n_err(_("`%s': parsing stopped after %" PRIuZ " arguments "
  565             "(need %" PRIuZ "%s)\n"
  566             "     Input: %.*s\n"
  567             "   Stopped: %.*s\n"),
  568          cadp->cad_name, parsed_args, i, (i == cadp->cad_no ? n_empty : "+"),
  569          (int)shin_orig.l, shin_orig.s,
  570          (int)shin.l, shin.s);
  571    }
  572    lcap = NULL;
  573    goto jleave;
  574 }
  575 
  576 FL void *
  577 n_cmd_arg_save_to_heap(struct n_cmd_arg_ctx const *cacp){
  578    struct n_cmd_arg *ncap;
  579    struct n_cmd_arg_ctx *ncacp;
  580    char *buf;
  581    struct n_cmd_arg const *cap;
  582    size_t len, i;
  583    NYD2_ENTER;
  584 
  585    /* For simplicity, save it all in once chunk, so that it can be thrown away
  586     * with a simple n_free() from whoever is concerned */
  587    len = sizeof *cacp;
  588    for(cap = cacp->cac_arg; cap != NULL; cap = cap->ca_next){
  589       i = cap->ca_arg.ca_str.l +1;
  590       i = n_ALIGN(i);
  591       len += sizeof(*cap) + i;
  592    }
  593    if(cacp->cac_vput != NULL)
  594       len += strlen(cacp->cac_vput) +1;
  595 
  596    ncacp = n_alloc(len);
  597    *ncacp = *cacp;
  598    buf = (char*)&ncacp[1];
  599 
  600    for(ncap = NULL, cap = cacp->cac_arg; cap != NULL; cap = cap->ca_next){
  601       void *vp;
  602 
  603       vp = buf;
  604       DBG( memset(vp, 0, sizeof *ncap); )
  605 
  606       if(ncap == NULL)
  607          ncacp->cac_arg = vp;
  608       else
  609          ncap->ca_next = vp;
  610       ncap = vp;
  611       ncap->ca_next = NULL;
  612       ncap->ca_ent_flags[0] = cap->ca_ent_flags[0];
  613       ncap->ca_ent_flags[1] = cap->ca_ent_flags[1];
  614       ncap->ca_arg_flags = cap->ca_arg_flags;
  615       memcpy(ncap->ca_arg.ca_str.s = (char*)&ncap[1], cap->ca_arg.ca_str.s,
  616             (ncap->ca_arg.ca_str.l = i = cap->ca_arg.ca_str.l) +1);
  617 
  618       i = n_ALIGN(i);
  619       buf += sizeof(*ncap) + i;
  620    }
  621 
  622    if(cacp->cac_vput != NULL){
  623       ncacp->cac_vput = buf;
  624       memcpy(buf, cacp->cac_vput, strlen(cacp->cac_vput) +1);
  625    }else
  626       ncacp->cac_vput = NULL;
  627    NYD2_LEAVE;
  628    return ncacp;
  629 }
  630 
  631 FL struct n_cmd_arg_ctx *
  632 n_cmd_arg_restore_from_heap(void *vp){
  633    struct n_cmd_arg *cap, *ncap;
  634    struct n_cmd_arg_ctx *cacp, *rv;
  635    NYD2_ENTER;
  636 
  637    rv = n_autorec_alloc(sizeof *rv);
  638    cacp = vp;
  639    *rv = *cacp;
  640 
  641    for(ncap = NULL, cap = cacp->cac_arg; cap != NULL; cap = cap->ca_next){
  642       vp = n_autorec_alloc(sizeof(*ncap) + cap->ca_arg.ca_str.l +1);
  643       DBG( memset(vp, 0, sizeof *ncap); )
  644 
  645       if(ncap == NULL)
  646          rv->cac_arg = vp;
  647       else
  648          ncap->ca_next = vp;
  649       ncap = vp;
  650       ncap->ca_next = NULL;
  651       ncap->ca_ent_flags[0] = cap->ca_ent_flags[0];
  652       ncap->ca_ent_flags[1] = cap->ca_ent_flags[1];
  653       ncap->ca_arg_flags = cap->ca_arg_flags;
  654       memcpy(ncap->ca_arg.ca_str.s = (char*)&ncap[1], cap->ca_arg.ca_str.s,
  655             (ncap->ca_arg.ca_str.l = cap->ca_arg.ca_str.l) +1);
  656    }
  657 
  658    if(cacp->cac_vput != NULL)
  659       rv->cac_vput = savestr(cacp->cac_vput);
  660    NYD2_LEAVE;
  661    return rv;
  662 }
  663 
  664 FL int
  665 getrawlist(bool_t wysh, char **res_dat, size_t res_size,
  666       char const *line, size_t linesize){
  667    int res_no;
  668    NYD_ENTER;
  669 
  670    n_pstate &= ~n_PS_ARGLIST_MASK;
  671 
  672    if(res_size == 0){
  673       res_no = -1;
  674       goto jleave;
  675    }else if(UICMP(z, res_size, >, INT_MAX))
  676       res_size = INT_MAX;
  677    else
  678       --res_size;
  679    res_no = 0;
  680 
  681    if(!wysh){
  682       /* And assuming result won't grow input */
  683       char c2, c, quotec, *cp2, *linebuf;
  684 
  685       linebuf = n_lofi_alloc(linesize);
  686 
  687       for(;;){
  688          for(; blankchar(*line); ++line)
  689             ;
  690          if(*line == '\0')
  691             break;
  692 
  693          if(UICMP(z, res_no, >=, res_size)){
  694             n_err(_("Too many input tokens for result storage\n"));
  695             res_no = -1;
  696             break;
  697          }
  698 
  699          cp2 = linebuf;
  700          quotec = '\0';
  701 
  702          /* TODO v15: complete switch in order mirror known behaviour */
  703          while((c = *line++) != '\0'){
  704             if(quotec != '\0'){
  705                if(c == quotec){
  706                   quotec = '\0';
  707                   continue;
  708                }else if(c == '\\'){
  709                   if((c2 = *line++) == quotec)
  710                      c = c2;
  711                   else
  712                      --line;
  713                }
  714             }else if(c == '"' || c == '\''){
  715                quotec = c;
  716                continue;
  717             }else if(c == '\\'){
  718                if((c2 = *line++) != '\0')
  719                   c = c2;
  720                else
  721                   --line;
  722             }else if(blankchar(c))
  723                break;
  724             *cp2++ = c;
  725          }
  726 
  727          res_dat[res_no++] = savestrbuf(linebuf, PTR2SIZE(cp2 - linebuf));
  728          if(c == '\0')
  729             break;
  730       }
  731 
  732       n_lofi_free(linebuf);
  733    }else{
  734       /* sh(1) compat mode.  Prepare shell token-wise */
  735       struct n_string store;
  736       struct str input;
  737       void const *cookie;
  738 
  739       n_string_creat_auto(&store);
  740       input.s = n_UNCONST(line);
  741       input.l = linesize;
  742       cookie = NULL;
  743 
  744       for(;;){
  745          if(UICMP(z, res_no, >=, res_size)){
  746             n_err(_("Too many input tokens for result storage\n"));
  747             res_no = -1;
  748             break;
  749          }
  750 
  751          /* C99 */{
  752             enum n_shexp_state shs;
  753 
  754             if((shs = n_shexp_parse_token((n_SHEXP_PARSE_LOG |
  755                         (cookie == NULL ? n_SHEXP_PARSE_TRIM_SPACE : 0) |
  756                         /* TODO not here in old style n_SHEXP_PARSE_IFS_VAR |*/
  757                         n_SHEXP_PARSE_META_SEMICOLON),
  758                      &store, &input, &cookie)
  759                   ) & n_SHEXP_STATE_ERR_MASK){
  760                /* Simply ignore Unicode error, just keep the normalized \[Uu] */
  761                if((shs & n_SHEXP_STATE_ERR_MASK) != n_SHEXP_STATE_ERR_UNICODE){
  762                   res_no = -1;
  763                   break;
  764                }
  765             }
  766 
  767             if(shs & n_SHEXP_STATE_OUTPUT){
  768                if(shs & n_SHEXP_STATE_CONTROL)
  769                   n_pstate |= n_PS_WYSHLIST_SAW_CONTROL;
  770 
  771                res_dat[res_no++] = n_string_cp(&store);
  772                n_string_drop_ownership(&store);
  773             }
  774 
  775             if(shs & n_SHEXP_STATE_STOP)
  776                break;
  777          }
  778       }
  779 
  780       n_string_gut(&store);
  781    }
  782 
  783    if(res_no >= 0)
  784       res_dat[(size_t)res_no] = NULL;
  785 jleave:
  786    NYD_LEAVE;
  787    return res_no;
  788 }
  789 
  790 /* s-it-mode */