"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/cmd-cnd.c" (8 Aug 2018, 15951 Bytes) of package /linux/misc/s-nail-14.9.11.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-cnd.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.10_vs_14.9.11.

    1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
    2  *@ Commands: conditional constructs.
    3  *
    4  * Copyright (c) 2014 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
    5  * SPDX-License-Identifier: ISC
    6  *
    7  * Permission to use, copy, modify, and/or distribute this software for any
    8  * purpose with or without fee is hereby granted, provided that the above
    9  * copyright notice and this permission notice appear in all copies.
   10  *
   11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   18  */
   19 #undef n_FILE
   20 #define n_FILE cmd_cnd
   21 
   22 #ifndef HAVE_AMALGAMATION
   23 # include "nail.h"
   24 #endif
   25 
   26 #define a_CCND_IF_ISSKIP() \
   27    (n_go_data->gdc_ifcond != NULL &&\
   28       (((struct a_ccnd_if_node*)n_go_data->gdc_ifcond)->cin_noop ||\
   29        !((struct a_ccnd_if_node*)n_go_data->gdc_ifcond)->cin_go))
   30 
   31 struct a_ccnd_if_node{
   32    struct a_ccnd_if_node *cin_outer;
   33    bool_t cin_error;    /* Bad expression, skip entire if..endif */
   34    bool_t cin_noop;     /* Outer stack !cin_go, entirely no-op */
   35    bool_t cin_go;       /* Green light */
   36    bool_t cin_else;     /* In `else' clause */
   37    ui8_t cin__dummy[4];
   38 };
   39 
   40 struct a_ccnd_if_ctx{
   41    char const * const *cic_argv_base;
   42    char const * const *cic_argv_max;   /* BUT: .cic_argv MUST be terminated! */
   43    char const * const *cic_argv;
   44 };
   45 
   46 /* */
   47 static void a_ccnd_oif_error(struct a_ccnd_if_ctx const *cicp,
   48                char const *msg_or_null, char const *nearby_or_null);
   49 
   50 /* noop and (1) don't work for real, only syntax-check and
   51  * (2) non-error return is ignored */
   52 static si8_t a_ccnd_oif_test(struct a_ccnd_if_ctx *cicp, bool_t noop);
   53 static si8_t a_ccnd_oif_group(struct a_ccnd_if_ctx *cicp, size_t level,
   54                bool_t noop);
   55 
   56 /* Shared `if' / `elif' implementation */
   57 static int a_ccnd_if(void *v, bool_t iselif);
   58 
   59 static void
   60 a_ccnd_oif_error(struct a_ccnd_if_ctx const *cicp, char const *msg_or_null,
   61       char const *nearby_or_null){
   62    struct str s;
   63    NYD2_ENTER;
   64 
   65    if(msg_or_null == NULL)
   66       msg_or_null = _("invalid expression syntax");
   67 
   68    if(nearby_or_null != NULL)
   69       n_err(_("`if' conditional: %s -- near: %s\n"),
   70          msg_or_null, nearby_or_null);
   71    else
   72       n_err(_("`if' conditional: %s\n"), msg_or_null);
   73 
   74    if((n_psonce & n_PSO_INTERACTIVE) || (n_poption & n_PO_D_V)){
   75       str_concat_cpa(&s, cicp->cic_argv_base,
   76          (*cicp->cic_argv_base != NULL ? " " : n_empty));
   77       n_err(_("   Expression: %s\n"), s.s);
   78 
   79       str_concat_cpa(&s, cicp->cic_argv,
   80          (*cicp->cic_argv != NULL ? " " : n_empty));
   81       n_err(_("   Stopped at: %s\n"), s.s);
   82    }
   83    NYD2_LEAVE;
   84 }
   85 
   86 static si8_t
   87 a_ccnd_oif_test(struct a_ccnd_if_ctx *cicp, bool_t noop){
   88    char const *emsg, * const *argv, *cp, *lhv, *op, *rhv;
   89    size_t argc;
   90    char c;
   91    si8_t rv;
   92    NYD2_ENTER;
   93 
   94    rv = -1;
   95    emsg = NULL;
   96    argv = cicp->cic_argv;
   97    argc = PTR2SIZE(cicp->cic_argv_max - cicp->cic_argv);
   98    cp = argv[0];
   99 
  100    if(*cp != '$'){
  101       if(argc > 1)
  102          goto jesyn;
  103    }else if(cp[1] == '\0')
  104       goto jesyn;
  105    else if(argc > 3){
  106 #ifdef HAVE_REGEX
  107 jesyn_ntr:
  108 #endif
  109       if(0){
  110 jesyn:
  111          if(emsg != NULL)
  112             emsg = V_(emsg);
  113       }
  114       a_ccnd_oif_error(cicp, emsg, cp);
  115       goto jleave;
  116    }
  117 
  118    switch(*cp){
  119    default:
  120       switch((rv = n_boolify(cp, UIZ_MAX, TRUM1))){
  121       case FAL0:
  122       case TRU1:
  123          break;
  124       default:
  125          emsg = N_("Expected a boolean");
  126          goto jesyn;
  127       }
  128       break;
  129    case 'R': case 'r':
  130       rv = !(n_psonce & n_PSO_SENDMODE);
  131       break;
  132    case 'S': case 's':
  133       rv = ((n_psonce & n_PSO_SENDMODE) != 0);
  134       break;
  135    case 'T': case 't':
  136       if(!asccasecmp(cp, "true")) /* Beware! */
  137          rv = TRU1;
  138       else
  139          rv = ((n_psonce & n_PSO_INTERACTIVE) != 0);
  140       break;
  141    case '$':{
  142       enum{
  143          a_NONE,
  144          a_ICASE = 1u<<0
  145       } flags = a_NONE;
  146 
  147       /* Look up the value in question, we need it anyway */
  148       if(*++cp == '{'){
  149          size_t i = strlen(cp);
  150 
  151          if(i > 0 && cp[i - 1] == '}')
  152             cp = savestrbuf(++cp, i -= 2);
  153          else
  154             goto jesyn;
  155       }
  156       if(noop)
  157          lhv = NULL;
  158       else
  159          lhv = n_var_vlook(cp, TRU1);
  160 
  161       /* Single argument, "implicit boolean" form? */
  162       if(argc == 1){
  163          rv = (lhv != NULL);
  164          break;
  165       }
  166       op = argv[1];
  167 
  168       /* Three argument comparison form required, check syntax */
  169       emsg = N_("unrecognized condition");
  170       if(argc == 2 || (c = op[0]) == '\0')
  171          goto jesyn;
  172 
  173       /* May be modifier */
  174       if(c == '@'){
  175          for(;;){
  176             c = *++op;
  177             if(c == 'i')
  178                flags |= a_ICASE;
  179             else
  180                break;
  181          }
  182          if(flags == a_NONE)
  183             flags = a_ICASE;
  184       }
  185 
  186       if(op[1] == '\0'){
  187          if(c != '<' && c != '>')
  188             goto jesyn;
  189       }else if(c != '-' && op[2] != '\0')
  190          goto jesyn;
  191       else if(c == '<' || c == '>'){
  192          if(op[1] != '=')
  193             goto jesyn;
  194       }else if(c == '=' || c == '!'){
  195          if(op[1] != '=' && op[1] != '%' && op[1] != '@'
  196 #ifdef HAVE_REGEX
  197                && op[1] != '~'
  198 #endif
  199          )
  200             goto jesyn;
  201       }else if(c == '-'){
  202          if(op[1] == '\0' || op[2] == '\0' || op[3] != '\0')
  203             goto jesyn;
  204          if(op[1] == 'e'){
  205             if(op[2] != 'q')
  206                goto jesyn;
  207          }else if(op[1] == 'g' || op[1] == 'l'){
  208             if(op[2] != 'e' && op[2] != 't')
  209                goto jesyn;
  210          }else if(op[1] == 'n'){
  211             if(op[2] != 'e')
  212                goto jesyn;
  213          }else
  214             goto jesyn;
  215       }else
  216          goto jesyn;
  217 
  218       /* The right hand side may also be a variable, more syntax checking */
  219       emsg = N_("invalid right hand side");
  220       if((rhv = argv[2]) == NULL /* Can't happen */)
  221          goto jesyn;
  222       if(*rhv == '$'){
  223          if(*++rhv == '\0')
  224             goto jesyn;
  225          else if(*rhv == '{'){
  226             size_t i = strlen(rhv);
  227 
  228             if(i > 0 && rhv[i - 1] == '}')
  229                rhv = savestrbuf(++rhv, i -= 2);
  230             else{
  231                cp = --rhv;
  232                goto jesyn;
  233             }
  234          }
  235          if(noop)
  236             rhv = NULL;
  237          else
  238             rhv = n_var_vlook(cp = rhv, TRU1);
  239       }
  240 
  241       /* A null value is treated as the empty string */
  242       emsg = NULL;
  243       if(lhv == NULL)
  244          lhv = n_UNCONST(n_empty);
  245       if(rhv == NULL)
  246          rhv = n_UNCONST(n_empty);
  247 
  248 #ifdef HAVE_REGEX
  249       if(op[1] == '~'){
  250          regex_t re;
  251          int s;
  252 
  253          if((s = regcomp(&re, rhv, REG_EXTENDED | REG_NOSUB |
  254                (flags & a_ICASE ? REG_ICASE : 0))) != 0){
  255             emsg = savecat(_("invalid regular expression: "),
  256                   n_regex_err_to_doc(NULL, s));
  257             goto jesyn_ntr;
  258          }
  259          if(!noop)
  260             rv = (regexec(&re, lhv, 0,NULL, 0) == REG_NOMATCH) ^ (c == '=');
  261          regfree(&re);
  262       }else
  263 #endif
  264             if(noop)
  265          break;
  266       else if(op[1] == '%' || op[1] == '@'){
  267          if(op[1] == '@')
  268             n_OBSOLETE("`if'++: \"=@\" and \"!@\" became \"=%\" and \"!%\"");
  269          rv = ((flags & a_ICASE ? asccasestr(lhv, rhv) : strstr(lhv, rhv)
  270                ) == NULL) ^ (c == '=');
  271       }else if(c == '-'){
  272          si64_t lhvi, rhvi;
  273 
  274          if(*lhv == '\0')
  275             lhv = n_0;
  276          if(*rhv == '\0')
  277             rhv = n_0;
  278          if((n_idec_si64_cp(&lhvi, lhv, 0, NULL
  279                   ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
  280                ) != n_IDEC_STATE_CONSUMED || (n_idec_si64_cp(&rhvi, rhv, 0, NULL
  281                   ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
  282                ) != n_IDEC_STATE_CONSUMED){
  283             emsg = N_("integer expression expected");
  284             goto jesyn;
  285          }
  286 
  287          lhvi -= rhvi;
  288          switch(op[1]){
  289          default:
  290          case 'e': rv = (lhvi == 0); break;
  291          case 'n': rv = (lhvi != 0); break;
  292          case 'l': rv = (op[2] == 't') ? lhvi < 0 : lhvi <= 0; break;
  293          case 'g': rv = (op[2] == 't') ? lhvi > 0 : lhvi >= 0; break;
  294          }
  295          break;
  296       }else{
  297          si32_t scmp;
  298 
  299          scmp = (flags & a_ICASE) ? asccasecmp(lhv, rhv) : strcmp(lhv, rhv);
  300          switch(c){
  301          default:
  302          case '=': rv = (scmp == 0); break;
  303          case '!': rv = (scmp != 0); break;
  304          case '<': rv = (op[1] == '\0') ? scmp < 0 : scmp <= 0; break;
  305          case '>': rv = (op[1] == '\0') ? scmp > 0 : scmp >= 0; break;
  306          }
  307       }
  308       }break;
  309    }
  310 
  311    if(noop && rv < 0)
  312       rv = TRU1;
  313 jleave:
  314    NYD2_LEAVE;
  315    return rv;
  316 }
  317 
  318 static si8_t
  319 a_ccnd_oif_group(struct a_ccnd_if_ctx *cicp, size_t level, bool_t noop){
  320    char const *emsg, *arg0, * const *argv, * const *argv_max_save;
  321    size_t i;
  322    char unary, c;
  323    enum{
  324       a_FIRST = 1<<0,
  325       a_END_OK = 1<<1,
  326       a_NEED_LIST = 1<<2,
  327 
  328       a_CANNOT_UNARY = a_NEED_LIST,
  329       a_CANNOT_OBRACK = a_NEED_LIST,
  330       a_CANNOT_CBRACK = a_FIRST,
  331       a_CANNOT_LIST = a_FIRST,
  332       a_CANNOT_COND = a_NEED_LIST
  333    } state;
  334    si8_t rv, xrv;
  335    NYD2_ENTER;
  336 
  337    rv = -1;
  338    state = a_FIRST;
  339    unary = '\0';
  340    emsg = NULL;
  341 
  342    for(;;){
  343       arg0 = *(argv = cicp->cic_argv);
  344       if(arg0 == NULL){
  345          if(!(state & a_END_OK)){
  346             emsg = N_("missing expression (premature end)");
  347             goto jesyn;
  348          }
  349          if(noop && rv < 0)
  350             rv = TRU1;
  351          break; /* goto jleave; */
  352       }
  353 
  354       switch((c = *arg0)){
  355       case '!':
  356          if(arg0[1] != '\0')
  357             goto jneed_cond;
  358 
  359          if(state & a_CANNOT_UNARY){
  360             emsg = N_("cannot use a unary operator here");
  361             goto jesyn;
  362          }
  363 
  364          unary = (unary != '\0') ? '\0' : c;
  365          state &= ~(a_FIRST | a_END_OK);
  366          cicp->cic_argv = ++argv;
  367          continue;
  368 
  369       case '[':
  370       case ']':
  371          if(arg0[1] != '\0')
  372             goto jneed_cond;
  373 
  374          if(c == '['){
  375             if(state & a_CANNOT_OBRACK){
  376                emsg = N_("cannot open a group here");
  377                goto jesyn;
  378             }
  379 
  380             cicp->cic_argv = ++argv;
  381             if((xrv = a_ccnd_oif_group(cicp, level + 1, noop)) < 0){
  382                rv = xrv;
  383                goto jleave;
  384             }else if(!noop)
  385                rv = (unary != '\0') ? !xrv : xrv;
  386 
  387             unary = '\0';
  388             state &= ~(a_FIRST | a_END_OK);
  389             state |= (level == 0 ? a_END_OK : 0) | a_NEED_LIST;
  390             continue;
  391          }else{
  392             if(state & a_CANNOT_CBRACK){
  393                emsg = N_("cannot use closing bracket here");
  394                goto jesyn;
  395             }
  396 
  397             if(level == 0){
  398                emsg = N_("no open groups to be closed here");
  399                goto jesyn;
  400             }
  401 
  402             cicp->cic_argv = ++argv;
  403             if(noop && rv < 0)
  404                rv = TRU1;
  405             goto jleave;/* break;break; */
  406          }
  407 
  408       case '|':
  409       case '&':
  410          if(c != arg0[1] || arg0[2] != '\0')
  411             goto jneed_cond;
  412 
  413          if(state & a_CANNOT_LIST){
  414             emsg = N_("cannot use a AND-OR list here");
  415             goto jesyn;
  416          }
  417 
  418          noop = ((c == '&') ^ (rv == TRU1));
  419          state &= ~(a_FIRST | a_END_OK | a_NEED_LIST);
  420          cicp->cic_argv = ++argv;
  421          continue;
  422 
  423       default:
  424 jneed_cond:
  425          if(state & a_CANNOT_COND){
  426             emsg = N_("cannot use a `if' condition here");
  427             goto jesyn;
  428          }
  429 
  430          for(i = 0;; ++i){
  431             if((arg0 = argv[i]) == NULL)
  432                break;
  433             c = *arg0;
  434             if(c == '!' && arg0[1] == '\0')
  435                break;
  436             if((c == '[' || c == ']') && arg0[1] == '\0')
  437                break;
  438             if((c == '&' || c == '|') && c == arg0[1] && arg0[2] == '\0')
  439                break;
  440          }
  441          if(i == 0){
  442             emsg = N_("empty conditional group");
  443             goto jesyn;
  444          }
  445 
  446          argv_max_save = cicp->cic_argv_max;
  447          cicp->cic_argv_max = argv + i;
  448          if((xrv = a_ccnd_oif_test(cicp, noop)) < 0){
  449             rv = xrv;
  450             goto jleave;
  451          }else if(!noop)
  452             rv = (unary != '\0') ? !xrv : xrv;
  453          cicp->cic_argv_max = argv_max_save;
  454 
  455          cicp->cic_argv = (argv += i);
  456          unary = '\0';
  457          state &= ~a_FIRST;
  458          state |= a_END_OK | a_NEED_LIST;
  459          break;
  460       }
  461    }
  462 
  463 jleave:
  464    NYD2_LEAVE;
  465    return rv;
  466 jesyn:
  467    if(emsg == NULL)
  468       emsg = N_("invalid grouping");
  469    a_ccnd_oif_error(cicp, V_(emsg), arg0);
  470    rv = -1;
  471    goto jleave;
  472 }
  473 
  474 static int
  475 a_ccnd_if(void *v, bool_t iselif){
  476    struct a_ccnd_if_ctx cic;
  477    char const * const *argv;
  478    size_t argc;
  479    si8_t xrv, rv;
  480    struct a_ccnd_if_node *cinp;
  481    NYD_ENTER;
  482 
  483    if(!iselif){
  484       cinp = n_alloc(sizeof *cinp);
  485       cinp->cin_outer = n_go_data->gdc_ifcond;
  486    }else{
  487       cinp = n_go_data->gdc_ifcond;
  488       assert(cinp != NULL);
  489    }
  490    cinp->cin_error = FAL0;
  491    cinp->cin_noop = a_CCND_IF_ISSKIP();
  492    cinp->cin_go = TRU1;
  493    cinp->cin_else = FAL0;
  494    if(!iselif)
  495       n_go_data->gdc_ifcond = cinp;
  496 
  497    if(cinp->cin_noop){
  498       rv = 0;
  499       goto jleave;
  500    }
  501 
  502    /* For heaven's sake, support comments _today_ TODO wyshlist.. */
  503    for(argc = 0, argv = v; argv[argc] != NULL; ++argc)
  504       if(argv[argc][0] == '#'){
  505          char const **nav = n_autorec_alloc(sizeof(char*) * (argc + 1));
  506          size_t i;
  507 
  508          for(i = 0; i < argc; ++i)
  509             nav[i] = argv[i];
  510          nav[i] = NULL;
  511          argv = nav;
  512          break;
  513       }
  514    cic.cic_argv_base = cic.cic_argv = argv;
  515    cic.cic_argv_max = &argv[argc];
  516    xrv = a_ccnd_oif_group(&cic, 0, FAL0);
  517 
  518    if(xrv >= 0){
  519       cinp->cin_go = (bool_t)xrv;
  520       rv = 0;
  521    }else{
  522       cinp->cin_error = cinp->cin_noop = TRU1;
  523       rv = 1;
  524    }
  525 jleave:
  526    NYD_LEAVE;
  527    return rv;
  528 }
  529 
  530 FL int
  531 c_if(void *v){
  532    int rv;
  533    NYD_ENTER;
  534 
  535    rv = a_ccnd_if(v, FAL0);
  536    NYD_LEAVE;
  537    return rv;
  538 }
  539 
  540 FL int
  541 c_elif(void *v){
  542    struct a_ccnd_if_node *cinp;
  543    int rv;
  544    NYD_ENTER;
  545 
  546    if((cinp = n_go_data->gdc_ifcond) == NULL || cinp->cin_else){
  547       n_err(_("`elif' without a matching `if'\n"));
  548       rv = 1;
  549    }else if(!cinp->cin_error){
  550       cinp->cin_go = !cinp->cin_go; /* Cause right _IF_ISSKIP() result */
  551       rv = a_ccnd_if(v, TRU1);
  552    }else
  553       rv = 0;
  554    NYD_LEAVE;
  555    return rv;
  556 }
  557 
  558 FL int
  559 c_else(void *v){
  560    int rv;
  561    struct a_ccnd_if_node *cinp;
  562    NYD_ENTER;
  563    n_UNUSED(v);
  564 
  565    if((cinp = n_go_data->gdc_ifcond) == NULL || cinp->cin_else){
  566       n_err(_("`else' without a matching `if'\n"));
  567       rv = 1;
  568    }else{
  569       cinp->cin_else = TRU1;
  570       cinp->cin_go = !cinp->cin_go;
  571       rv = 0;
  572    }
  573    NYD_LEAVE;
  574    return rv;
  575 }
  576 
  577 FL int
  578 c_endif(void *v){
  579    int rv;
  580    struct a_ccnd_if_node *cinp;
  581    NYD_ENTER;
  582    n_UNUSED(v);
  583 
  584    if((cinp = n_go_data->gdc_ifcond) == NULL){
  585       n_err(_("`endif' without a matching `if'\n"));
  586       rv = 1;
  587    }else{
  588       n_go_data->gdc_ifcond = cinp->cin_outer;
  589       n_free(cinp);
  590       rv = 0;
  591    }
  592    NYD_LEAVE;
  593    return rv;
  594 }
  595 
  596 FL bool_t
  597 n_cnd_if_isskip(void){
  598    bool_t rv;
  599    NYD2_ENTER;
  600 
  601    rv = a_CCND_IF_ISSKIP();
  602    NYD2_LEAVE;
  603    return rv;
  604 }
  605 
  606 FL void
  607 n_cnd_if_stack_del(struct n_go_data_ctx *gdcp){
  608    struct a_ccnd_if_node *vp, *cinp;
  609    NYD2_ENTER;
  610 
  611    vp = gdcp->gdc_ifcond;
  612    gdcp->gdc_ifcond = NULL;
  613 
  614    while((cinp = vp) != NULL){
  615       vp = cinp->cin_outer;
  616       n_free(cinp);
  617    }
  618    NYD2_LEAVE;
  619 }
  620 
  621 /* s-it-mode */