"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.10/cmd-cnd.c" (25 Mar 2018, 15906 Bytes) of package /linux/misc/s-nail-14.9.10.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 last Fossies "Diffs" side-by-side code changes report: 14.9.7_vs_14.9.8.

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