"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.7/cmd-cnd.c" (16 Feb 2018, 15908 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-cnd.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  *@ 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(boolify(cp, UIZ_MAX, -1)){
  120       case 0: rv = FAL0; break;
  121       case 1: rv = TRU1; break;
  122       default:
  123          emsg = N_("Expected a boolean");
  124          goto jesyn;
  125       }
  126       break;
  127    case 'R': case 'r':
  128       rv = !(n_psonce & n_PSO_SENDMODE);
  129       break;
  130    case 'S': case 's':
  131       rv = ((n_psonce & n_PSO_SENDMODE) != 0);
  132       break;
  133    case 'T': case 't':
  134       if(!asccasecmp(cp, "true")) /* Beware! */
  135          rv = TRU1;
  136       else
  137          rv = ((n_psonce & n_PSO_INTERACTIVE) != 0);
  138       break;
  139    case '$':{
  140       enum{
  141          a_NONE,
  142          a_ICASE = 1u<<0
  143       } flags = a_NONE;
  144 
  145       /* Look up the value in question, we need it anyway */
  146       if(*++cp == '{'){
  147          size_t i = strlen(cp);
  148 
  149          if(i > 0 && cp[i - 1] == '}')
  150             cp = savestrbuf(++cp, i -= 2);
  151          else
  152             goto jesyn;
  153       }
  154       if(noop)
  155          lhv = NULL;
  156       else
  157          lhv = n_var_vlook(cp, TRU1);
  158 
  159       /* Single argument, "implicit boolean" form? */
  160       if(argc == 1){
  161          rv = (lhv != NULL);
  162          break;
  163       }
  164       op = argv[1];
  165 
  166       /* Three argument comparison form required, check syntax */
  167       emsg = N_("unrecognized condition");
  168       if(argc == 2 || (c = op[0]) == '\0')
  169          goto jesyn;
  170 
  171       /* May be modifier */
  172       if(c == '@'){
  173          for(;;){
  174             c = *++op;
  175             if(c == 'i')
  176                flags |= a_ICASE;
  177             else
  178                break;
  179          }
  180          if(flags == a_NONE)
  181             flags = a_ICASE;
  182       }
  183 
  184       if(op[1] == '\0'){
  185          if(c != '<' && c != '>')
  186             goto jesyn;
  187       }else if(c != '-' && op[2] != '\0')
  188          goto jesyn;
  189       else if(c == '<' || c == '>'){
  190          if(op[1] != '=')
  191             goto jesyn;
  192       }else if(c == '=' || c == '!'){
  193          if(op[1] != '=' && op[1] != '%' && op[1] != '@'
  194 #ifdef HAVE_REGEX
  195                && op[1] != '~'
  196 #endif
  197          )
  198             goto jesyn;
  199       }else if(c == '-'){
  200          if(op[1] == '\0' || op[2] == '\0' || op[3] != '\0')
  201             goto jesyn;
  202          if(op[1] == 'e'){
  203             if(op[2] != 'q')
  204                goto jesyn;
  205          }else if(op[1] == 'g' || op[1] == 'l'){
  206             if(op[2] != 'e' && op[2] != 't')
  207                goto jesyn;
  208          }else if(op[1] == 'n'){
  209             if(op[2] != 'e')
  210                goto jesyn;
  211          }else
  212             goto jesyn;
  213       }else
  214          goto jesyn;
  215 
  216       /* The right hand side may also be a variable, more syntax checking */
  217       emsg = N_("invalid right hand side");
  218       if((rhv = argv[2]) == NULL /* Can't happen */)
  219          goto jesyn;
  220       if(*rhv == '$'){
  221          if(*++rhv == '\0')
  222             goto jesyn;
  223          else if(*rhv == '{'){
  224             size_t i = strlen(rhv);
  225 
  226             if(i > 0 && rhv[i - 1] == '}')
  227                rhv = savestrbuf(++rhv, i -= 2);
  228             else{
  229                cp = --rhv;
  230                goto jesyn;
  231             }
  232          }
  233          if(noop)
  234             rhv = NULL;
  235          else
  236             rhv = n_var_vlook(cp = rhv, TRU1);
  237       }
  238 
  239       /* A null value is treated as the empty string */
  240       emsg = NULL;
  241       if(lhv == NULL)
  242          lhv = n_UNCONST(n_empty);
  243       if(rhv == NULL)
  244          rhv = n_UNCONST(n_empty);
  245 
  246 #ifdef HAVE_REGEX
  247       if(op[1] == '~'){
  248          regex_t re;
  249          int s;
  250 
  251          if((s = regcomp(&re, rhv, REG_EXTENDED | REG_NOSUB |
  252                (flags & a_ICASE ? REG_ICASE : 0))) != 0){
  253             emsg = savecat(_("invalid regular expression: "),
  254                   n_regex_err_to_doc(NULL, s));
  255             goto jesyn_ntr;
  256          }
  257          if(!noop)
  258             rv = (regexec(&re, lhv, 0,NULL, 0) == REG_NOMATCH) ^ (c == '=');
  259          regfree(&re);
  260       }else
  261 #endif
  262             if(noop)
  263          break;
  264       else if(op[1] == '%' || op[1] == '@'){
  265          if(op[1] == '@')
  266             n_OBSOLETE("`if'++: \"=@\" and \"!@\" became \"=%\" and \"!%\"");
  267          rv = ((flags & a_ICASE ? asccasestr(lhv, rhv) : strstr(lhv, rhv)
  268                ) == NULL) ^ (c == '=');
  269       }else if(c == '-'){
  270          si64_t lhvi, rhvi;
  271 
  272          if(*lhv == '\0')
  273             lhv = n_0;
  274          if(*rhv == '\0')
  275             rhv = n_0;
  276          if((n_idec_si64_cp(&lhvi, lhv, 0, NULL
  277                   ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
  278                ) != n_IDEC_STATE_CONSUMED || (n_idec_si64_cp(&rhvi, rhv, 0, NULL
  279                   ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
  280                ) != n_IDEC_STATE_CONSUMED){
  281             emsg = N_("integer expression expected");
  282             goto jesyn;
  283          }
  284 
  285          lhvi -= rhvi;
  286          switch(op[1]){
  287          default:
  288          case 'e': rv = (lhvi == 0); break;
  289          case 'n': rv = (lhvi != 0); break;
  290          case 'l': rv = (op[2] == 't') ? lhvi < 0 : lhvi <= 0; break;
  291          case 'g': rv = (op[2] == 't') ? lhvi > 0 : lhvi >= 0; break;
  292          }
  293          break;
  294       }else{
  295          si32_t scmp;
  296 
  297          scmp = (flags & a_ICASE) ? asccasecmp(lhv, rhv) : strcmp(lhv, rhv);
  298          switch(c){
  299          default:
  300          case '=': rv = (scmp == 0); break;
  301          case '!': rv = (scmp != 0); break;
  302          case '<': rv = (op[1] == '\0') ? scmp < 0 : scmp <= 0; break;
  303          case '>': rv = (op[1] == '\0') ? scmp > 0 : scmp >= 0; break;
  304          }
  305       }
  306       }break;
  307    }
  308 
  309    if(noop && rv < 0)
  310       rv = TRU1;
  311 jleave:
  312    NYD2_LEAVE;
  313    return rv;
  314 }
  315 
  316 static si8_t
  317 a_ccnd_oif_group(struct a_ccnd_if_ctx *cicp, size_t level, bool_t noop){
  318    char const *emsg, *arg0, * const *argv, * const *argv_max_save;
  319    size_t i;
  320    char unary, c;
  321    enum{
  322       a_FIRST = 1<<0,
  323       a_END_OK = 1<<1,
  324       a_NEED_LIST = 1<<2,
  325 
  326       a_CANNOT_UNARY = a_NEED_LIST,
  327       a_CANNOT_OBRACK = a_NEED_LIST,
  328       a_CANNOT_CBRACK = a_FIRST,
  329       a_CANNOT_LIST = a_FIRST,
  330       a_CANNOT_COND = a_NEED_LIST
  331    } state;
  332    si8_t rv, xrv;
  333    NYD2_ENTER;
  334 
  335    rv = -1;
  336    state = a_FIRST;
  337    unary = '\0';
  338    emsg = NULL;
  339 
  340    for(;;){
  341       arg0 = *(argv = cicp->cic_argv);
  342       if(arg0 == NULL){
  343          if(!(state & a_END_OK)){
  344             emsg = N_("missing expression (premature end)");
  345             goto jesyn;
  346          }
  347          if(noop && rv < 0)
  348             rv = TRU1;
  349          break; /* goto jleave; */
  350       }
  351 
  352       switch((c = *arg0)){
  353       case '!':
  354          if(arg0[1] != '\0')
  355             goto jneed_cond;
  356 
  357          if(state & a_CANNOT_UNARY){
  358             emsg = N_("cannot use a unary operator here");
  359             goto jesyn;
  360          }
  361 
  362          unary = (unary != '\0') ? '\0' : c;
  363          state &= ~(a_FIRST | a_END_OK);
  364          cicp->cic_argv = ++argv;
  365          continue;
  366 
  367       case '[':
  368       case ']':
  369          if(arg0[1] != '\0')
  370             goto jneed_cond;
  371 
  372          if(c == '['){
  373             if(state & a_CANNOT_OBRACK){
  374                emsg = N_("cannot open a group here");
  375                goto jesyn;
  376             }
  377 
  378             cicp->cic_argv = ++argv;
  379             if((xrv = a_ccnd_oif_group(cicp, level + 1, noop)) < 0){
  380                rv = xrv;
  381                goto jleave;
  382             }else if(!noop)
  383                rv = (unary != '\0') ? !xrv : xrv;
  384 
  385             unary = '\0';
  386             state &= ~(a_FIRST | a_END_OK);
  387             state |= (level == 0 ? a_END_OK : 0) | a_NEED_LIST;
  388             continue;
  389          }else{
  390             if(state & a_CANNOT_CBRACK){
  391                emsg = N_("cannot use closing bracket here");
  392                goto jesyn;
  393             }
  394 
  395             if(level == 0){
  396                emsg = N_("no open groups to be closed here");
  397                goto jesyn;
  398             }
  399 
  400             cicp->cic_argv = ++argv;
  401             if(noop && rv < 0)
  402                rv = TRU1;
  403             goto jleave;/* break;break; */
  404          }
  405 
  406       case '|':
  407       case '&':
  408          if(c != arg0[1] || arg0[2] != '\0')
  409             goto jneed_cond;
  410 
  411          if(state & a_CANNOT_LIST){
  412             emsg = N_("cannot use a AND-OR list here");
  413             goto jesyn;
  414          }
  415 
  416          noop = ((c == '&') ^ (rv == TRU1));
  417          state &= ~(a_FIRST | a_END_OK | a_NEED_LIST);
  418          cicp->cic_argv = ++argv;
  419          continue;
  420 
  421       default:
  422 jneed_cond:
  423          if(state & a_CANNOT_COND){
  424             emsg = N_("cannot use a `if' condition here");
  425             goto jesyn;
  426          }
  427 
  428          for(i = 0;; ++i){
  429             if((arg0 = argv[i]) == NULL)
  430                break;
  431             c = *arg0;
  432             if(c == '!' && arg0[1] == '\0')
  433                break;
  434             if((c == '[' || c == ']') && arg0[1] == '\0')
  435                break;
  436             if((c == '&' || c == '|') && c == arg0[1] && arg0[2] == '\0')
  437                break;
  438          }
  439          if(i == 0){
  440             emsg = N_("empty conditional group");
  441             goto jesyn;
  442          }
  443 
  444          argv_max_save = cicp->cic_argv_max;
  445          cicp->cic_argv_max = argv + i;
  446          if((xrv = a_ccnd_oif_test(cicp, noop)) < 0){
  447             rv = xrv;
  448             goto jleave;
  449          }else if(!noop)
  450             rv = (unary != '\0') ? !xrv : xrv;
  451          cicp->cic_argv_max = argv_max_save;
  452 
  453          cicp->cic_argv = (argv += i);
  454          unary = '\0';
  455          state &= ~a_FIRST;
  456          state |= a_END_OK | a_NEED_LIST;
  457          break;
  458       }
  459    }
  460 
  461 jleave:
  462    NYD2_LEAVE;
  463    return rv;
  464 jesyn:
  465    if(emsg == NULL)
  466       emsg = N_("invalid grouping");
  467    a_ccnd_oif_error(cicp, V_(emsg), arg0);
  468    rv = -1;
  469    goto jleave;
  470 }
  471 
  472 static int
  473 a_ccnd_if(void *v, bool_t iselif){
  474    struct a_ccnd_if_ctx cic;
  475    char const * const *argv;
  476    size_t argc;
  477    si8_t xrv, rv;
  478    struct a_ccnd_if_node *cinp;
  479    NYD_ENTER;
  480 
  481    if(!iselif){
  482       cinp = smalloc(sizeof *cinp);
  483       cinp->cin_outer = n_go_data->gdc_ifcond;
  484    }else{
  485       cinp = n_go_data->gdc_ifcond;
  486       assert(cinp != NULL);
  487    }
  488    cinp->cin_error = FAL0;
  489    cinp->cin_noop = a_CCND_IF_ISSKIP();
  490    cinp->cin_go = TRU1;
  491    cinp->cin_else = FAL0;
  492    if(!iselif)
  493       n_go_data->gdc_ifcond = cinp;
  494 
  495    if(cinp->cin_noop){
  496       rv = 0;
  497       goto jleave;
  498    }
  499 
  500    /* For heaven's sake, support comments _today_ TODO wyshlist.. */
  501    for(argc = 0, argv = v; argv[argc] != NULL; ++argc)
  502       if(argv[argc][0] == '#'){
  503          char const **nav = salloc(sizeof(char*) * (argc + 1));
  504          size_t i;
  505 
  506          for(i = 0; i < argc; ++i)
  507             nav[i] = argv[i];
  508          nav[i] = NULL;
  509          argv = nav;
  510          break;
  511       }
  512    cic.cic_argv_base = cic.cic_argv = argv;
  513    cic.cic_argv_max = &argv[argc];
  514    xrv = a_ccnd_oif_group(&cic, 0, FAL0);
  515 
  516    if(xrv >= 0){
  517       cinp->cin_go = (bool_t)xrv;
  518       rv = 0;
  519    }else{
  520       cinp->cin_error = cinp->cin_noop = TRU1;
  521       rv = 1;
  522    }
  523 jleave:
  524    NYD_LEAVE;
  525    return rv;
  526 }
  527 
  528 FL int
  529 c_if(void *v){
  530    int rv;
  531    NYD_ENTER;
  532 
  533    rv = a_ccnd_if(v, FAL0);
  534    NYD_LEAVE;
  535    return rv;
  536 }
  537 
  538 FL int
  539 c_elif(void *v){
  540    struct a_ccnd_if_node *cinp;
  541    int rv;
  542    NYD_ENTER;
  543 
  544    if((cinp = n_go_data->gdc_ifcond) == NULL || cinp->cin_else){
  545       n_err(_("`elif' without a matching `if'\n"));
  546       rv = 1;
  547    }else if(!cinp->cin_error){
  548       cinp->cin_go = !cinp->cin_go; /* Cause right _IF_ISSKIP() result */
  549       rv = a_ccnd_if(v, TRU1);
  550    }else
  551       rv = 0;
  552    NYD_LEAVE;
  553    return rv;
  554 }
  555 
  556 FL int
  557 c_else(void *v){
  558    int rv;
  559    struct a_ccnd_if_node *cinp;
  560    NYD_ENTER;
  561    n_UNUSED(v);
  562 
  563    if((cinp = n_go_data->gdc_ifcond) == NULL || cinp->cin_else){
  564       n_err(_("`else' without a matching `if'\n"));
  565       rv = 1;
  566    }else{
  567       cinp->cin_else = TRU1;
  568       cinp->cin_go = !cinp->cin_go;
  569       rv = 0;
  570    }
  571    NYD_LEAVE;
  572    return rv;
  573 }
  574 
  575 FL int
  576 c_endif(void *v){
  577    int rv;
  578    struct a_ccnd_if_node *cinp;
  579    NYD_ENTER;
  580    n_UNUSED(v);
  581 
  582    if((cinp = n_go_data->gdc_ifcond) == NULL){
  583       n_err(_("`endif' without a matching `if'\n"));
  584       rv = 1;
  585    }else{
  586       n_go_data->gdc_ifcond = cinp->cin_outer;
  587       free(cinp);
  588       rv = 0;
  589    }
  590    NYD_LEAVE;
  591    return rv;
  592 }
  593 
  594 FL bool_t
  595 n_cnd_if_isskip(void){
  596    bool_t rv;
  597    NYD2_ENTER;
  598 
  599    rv = a_CCND_IF_ISSKIP();
  600    NYD2_LEAVE;
  601    return rv;
  602 }
  603 
  604 FL void
  605 n_cnd_if_stack_del(struct n_go_data_ctx *gdcp){
  606    struct a_ccnd_if_node *vp, *cinp;
  607    NYD2_ENTER;
  608 
  609    vp = gdcp->gdc_ifcond;
  610    gdcp->gdc_ifcond = NULL;
  611 
  612    while((cinp = vp) != NULL){
  613       vp = cinp->cin_outer;
  614       free(cinp);
  615    }
  616    NYD2_LEAVE;
  617 }
  618 
  619 /* s-it-mode */