LCOV - code coverage report
Current view: top level - libreoffice/dmake - getinp.c (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 280 327 85.6 %
Date: 2012-12-17 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* RCS  $Id: getinp.c,v 1.10 2007-10-15 15:39:23 ihi Exp $
       2             : --
       3             : -- SYNOPSIS
       4             : --      Handle reading of input.
       5             : --
       6             : -- DESCRIPTION
       7             : --  The code in this file reads the input from the specified stream
       8             : --  into the provided buffer of size Buffer_size.  In doing so it deletes
       9             : --  comments.  Comments are delimited by the #, and
      10             : --  <nl> character sequences.  An exception is \# which
      11             : --  is replaced by # in the input.  Line continuations are signalled
      12             : --  at the end of a line and are recognized inside comments.
      13             : --  The line continuation is always  <\><nl>.
      14             : --
      15             : --  If the file to read is NIL(FILE) then the Get_line routine returns the
      16             : --  next rule from the builtin rule table (Rule_tab from ruletab.c) if
      17             : --  there is one.
      18             : --
      19             : -- AUTHOR
      20             : --      Dennis Vadura, dvadura@dmake.wticorp.com
      21             : --
      22             : -- WWW
      23             : --      http://dmake.wticorp.com/
      24             : --
      25             : -- COPYRIGHT
      26             : --      Copyright (c) 1996,1997 by WTI Corp.  All rights reserved.
      27             : --
      28             : --      This program is NOT free software; you can redistribute it and/or
      29             : --      modify it under the terms of the Software License Agreement Provided
      30             : --      in the file <distribution-root>/readme/license.txt.
      31             : --
      32             : -- LOG
      33             : --      Use cvs log to obtain detailed change logs.
      34             : */
      35             : 
      36             : #include "extern.h"
      37             : 
      38             : #define IS_WHITE(A)  ((A == ' ') || (A == '\t') || (A == '\n') || (A == '\r'))
      39             : #define SCAN_WHITE(A) \
      40             :     while( IS_WHITE(*A) ) A++;
      41             : 
      42             : static  int _is_conditional ANSI((char*));
      43             : static  int _handle_conditional ANSI((int, TKSTRPTR));
      44             : 
      45             : static int  rule_ind = 0;   /* index of rule when reading Rule_tab   */
      46             : static int  skip = FALSE;   /* if true the skip input        */
      47             : 
      48             : int partcomp( char* lhs, int opcode );
      49             : int parse_complex_expression( char *expr, char **expr_end, int opcode );
      50             : 
      51             : 
      52             : PUBLIC int
      53      474522 : Get_line( buf, fil )/*
      54             : ======================
      55             :    Read a line of input from the file stripping off comments.  The routine
      56             :    returns TRUE if EOF. If fil equals NIL(FILE) then the next line from
      57             :    *Rule_tab[] is used.  Rule_tab is either the buildin rule table or points
      58             :    to the current environment (used by ReadEnvironment()).
      59             :    The function returns TRUE if the input file/buffer was read to the end
      60             :    and FALSE otherwise. */
      61             : char *buf;
      62             : FILE *fil;
      63             : {
      64             :    extern   char **Rule_tab;
      65             :    register char *p;
      66             :    register char *c;
      67             :    char      *q;
      68             :    char          *buf_org;
      69             :    static   int  ignore = FALSE;
      70      474522 :    int       cont   = FALSE;
      71      474522 :    int       pos    = 0;
      72      474522 :    int       res = 0;
      73      474522 :    register char *tmp = NIL(char);
      74             : 
      75             :    DB_ENTER( "Get_line" );
      76             : 
      77      474522 :    if( Skip_to_eof ) {
      78           0 :       Skip_to_eof = FALSE;
      79           0 :       rule_ind    = 0;
      80             : 
      81           0 :       if( Verbose & V_MAKE )
      82           0 :      Warning("Ignoring remainder of file %s", Filename());
      83             : 
      84           0 :       DB_RETURN(TRUE);
      85             :    }
      86             : 
      87      474522 :    if( fil == NIL(FILE) ) {
      88             :       /* Reading the internal rule table.  Set rule_ind to zero after the
      89             :        * last entry so that ReadEnvironment() works as expected every time. */
      90             : 
      91      271952 :       while( (p = Rule_tab[ rule_ind++ ]) != NIL(char) ) {
      92             :      /* The last test in this if *p != '~', handles the environment
      93             :       * passing conventions used by MKS to pass arguments.  We want to
      94             :       * skip those environment entries. Also CYGWIN likes to export '!'
      95             :       * prefixed environment variables that cause severe pain, axe them too.
      96             :       * And finally it is possible to do "env 'GGG HHH'='some value' bash"
      97             :       * which causes that there are env variables with spaces in the name
      98             :       * defined which causes dmake to malfunction too */
      99      135608 :      char *equal = strchr(p,'=');
     100      135608 :      char *space = strchr(p,' ');
     101      135608 :      if( !Readenv || (Readenv && (equal != NIL(char)) && (space == NIL(char) || space > equal) && *p!='~' && *p!='!')){
     102      135608 :         strcpy( buf, p );
     103             : 
     104             :         DB_PRINT( "io", ("Returning [%s]", buf) );
     105      135608 :         DB_RETURN( FALSE );
     106             :      }
     107             :       }
     108             : 
     109         368 :       rule_ind = 0;
     110             : 
     111             :       DB_PRINT( "io", ("Done Ruletab") );
     112         368 :       DB_RETURN( TRUE );
     113             :    }
     114             : 
     115      338546 :    buf_org = buf;
     116             : 
     117             : do_again:
     118             :    do {
     119     1132042 :       p = buf+pos;
     120             :       /* fgets() reads at most one less than Buffer_size-pos characters. */
     121     1132042 :       if(feof( fil ) || (fgets( p, Buffer_size-pos, fil ) == NIL(char)))
     122        4592 :      DB_RETURN( TRUE );
     123             : 
     124             : #ifdef _MPW
     125             :      if ( p[0] == 10 && p[1] == COMMENT_CHAR)
     126             :        p[0] = ' ';
     127             : #endif
     128             : 
     129     1127450 :       Line_number++;
     130             : 
     131             :       /* Set q to the last char in p before the \n\0. */
     132     1127450 :       q = p+strlen(p)-2;
     133     1127450 :       if( q >= p ) {    /* Only check for special cases if p points
     134             :              * to a non-empty line. */
     135             : 
     136             :      /* ignore each RETURN at the end of a line before any further
     137             :       * processing */
     138      979442 :      if( q[0] == '\r' && q[1] == '\n' ) {
     139           0 :         q[0] = '\n';
     140           0 :         q[1] = '\0';
     141           0 :         q--;
     142             :      }
     143             :      /* you also have to deal with END_OF_FILE chars to process raw
     144             :       * DOS-Files. Normally they are the last chars in file, but after
     145             :       * working on these file with vi, there is an additional NEWLINE
     146             :       * after the last END_OF_FILE. So if the second last char in the
     147             :       * actual line is END_OF_FILE, you can skip the last char. Then
     148             :       * you can search the line back until you find no more END_OF_FILE
     149             :       * and nuke each you found by string termination. */
     150      979442 :      if( q[0] == '\032' )
     151           0 :         q--;
     152     1958884 :      while( q[1] == '\032' ) {
     153           0 :         q[1] = '\0';
     154           0 :         q--;
     155             :      }
     156             : 
     157             :      /* ignore input if ignore flag set and line ends in a continuation
     158             :         character. */
     159             : 
     160      979442 :      if( ignore ) {
     161          10 :         if( q[0] != CONTINUATION_CHAR || q[1] != '\n' )  ignore = FALSE;
     162          10 :         *p = '\0';
     163          10 :         continue;
     164             :      }
     165             : 
     166             :      /* If a comment is found the line does not end in \n anymore. */
     167      979432 :      c = Do_comment(p, &q, Group || (*buf == '\t') || (Notabs && *buf ==' '));
     168             : 
     169             :      /* Does the end of the line end in a continuation sequence? */
     170             : 
     171      979432 :      if( (q[0] == CONTINUATION_CHAR) && (q[1] == '\n')) {
     172             :      /* If the continuation was at the end of a comment then ignore the
     173             :       * next input line, (or lines until we get one ending in just <nl>)
     174             :       * else it's a continuation, so build the input line from several
     175             :       * text lines on input.  The maximum size of this is governened by
     176             :       * Buffer_size */
     177      169020 :         if( q != p && q[-1] == CONTINUATION_CHAR ) {
     178         184 :            size_t len = strlen(q+1)+1;
     179         184 :            memmove( q, q+1, len );
     180         184 :            q--;
     181         184 :            cont = FALSE;
     182             :         }
     183       84234 :         else if( c != NIL(char) )
     184           2 :            ignore = TRUE;
     185             :         else
     186       84232 :            cont   = TRUE;   /* Keep the \<nl>. */
     187             :      }
     188             :      else {
     189      895014 :         cont = FALSE;
     190             :      }
     191             : 
     192      979432 :      q = ( c == NIL(char) ) ? q+2 : c;
     193             :       }
     194             :       else {  /* empty line or "" */
     195      148008 :      cont = FALSE;
     196      148008 :      ignore = FALSE;
     197      148008 :      q = p+strlen(p);  /* strlen(p) is 1 or 0 */
     198             :       }
     199             : 
     200     1127440 :       pos += q-p;
     201             :    }
     202     1127450 :    while( (cont || !*buf) && (pos < Buffer_size-1) );
     203             : 
     204      878182 :    if( pos >= Buffer_size-1 )
     205           0 :       Fatal( "Input line too long, increase MAXLINELENGTH" );
     206             : 
     207             :    /* Lines that had comments don't contain \n anymore. */
     208             :    /* ??? Continued lines that are followed by an empty or comment only
     209             :     * line will end in \<nl>. */
     210      878182 :    if( (q > p) && (buf[ pos-1 ] == '\n') )
     211      814960 :       buf[ --pos ] = '\0';   /* Remove the final \n. */
     212             : 
     213             :    /* STUPID AUGMAKE uses "include" at the start of a line as
     214             :     * a signal to include a new file, so let's look for it.
     215             :     * if we see it replace it by .INCLUDE: and stick this back
     216             :     * into the buffer. We also allow GNU make if[n]eq/else/endif.
     217             :     *
     218             :     * These substitutions are made only if we are not parsing a group
     219             :     * recipe. */
     220      878182 :    if( (p = DmStrSpn(buf, " \t\r\n")) == NIL(char) )
     221           0 :       p = buf;
     222             : 
     223      878182 :    if (!Group) {
     224      878182 :       if( !strncmp( "include", p, 7 ) &&
     225           0 :           (p[7] == ' ' || p[7] == '\t') )
     226           0 :          tmp = DmStrJoin( ".INCLUDE:", p+7, -1, FALSE );
     227      878182 :       else if( !strncmp( "ifeq", p, 4 ) &&
     228           0 :                (p[4] == ' ' || p[4] == '\t') )
     229           0 :          tmp = DmStrJoin( ".IFEQ", p+4, -1, FALSE );
     230      878182 :       else if( !strncmp( "ifneq", p, 5 ) &&
     231           0 :                (p[5] == ' ' || p[5] == '\t') )
     232           0 :          tmp = DmStrJoin( ".IFNEQ", p+5, -1, FALSE );
     233      878182 :       else if( !strncmp( "elif", p, 4 ) &&
     234           0 :                (p[4] == ' ' || p[4] == '\t') )
     235           0 :          tmp = DmStrJoin( ".ELIF", p+4, -1, FALSE );
     236      878182 :       else if( !strncmp( "else", p, 4 ) &&
     237           0 :                (p[4] == ' ' || p[4] == '\t' || p[4] == '\0') )
     238           0 :          tmp = DmStrJoin( ".ELSE", p+4, -1, FALSE );
     239      878182 :       else if( !strncmp( "endif", p, 5 ) &&
     240           0 :                (p[5] == ' ' || p[5] == '\t' || p[5] == '\0') )
     241           0 :          tmp = DmStrJoin( ".END", p+5, -1, FALSE );
     242             :    }
     243             : 
     244      878182 :    if( tmp != NIL(char)) {
     245           0 :      strcpy( buf, tmp );
     246           0 :      FREE( tmp );
     247           0 :      tmp = NIL(char);
     248             :    }
     249             : 
     250             :    /* Now that we have the next line of input to make, we should check to
     251             :     * see if it is a conditional expression.  If it is then process it,
     252             :     * otherwise pass it on to the parser. */
     253             : 
     254      878182 :    if( *(p = DmStrSpn(buf, " \t\r\n")) == CONDSTART ) {
     255             :       TKSTR token;
     256             : 
     257      329978 :       SET_TOKEN( &token, p );
     258             : 
     259      329978 :       p = Get_token( &token, "", FALSE );
     260             : 
     261      329978 :       if( (res = _is_conditional(p)) != 0 ) /* ignore non-control special */
     262             :       {                     /* targets            */
     263      306000 :      res  = _handle_conditional( res, &token );
     264      306000 :      skip = TRUE;
     265             :       }
     266             :       else {
     267       23978 :      CLEAR_TOKEN( &token );
     268       23978 :      res  = TRUE;
     269             :       }
     270             :    }
     271             : 
     272      878182 :    if( skip ) {
     273      544228 :       buf  = buf_org;       /* ignore line just read in */
     274      544228 :       pos  = 0;
     275      544228 :       skip = res;
     276      544228 :       goto do_again;
     277             :    }
     278             : 
     279             :    DB_PRINT( "io", ("Returning [%s]", buf) );
     280      333954 :    DB_RETURN( FALSE );
     281             : }
     282             : 
     283             : 
     284             : PUBLIC char *
     285      979432 : Do_comment(str, pend, keep)/*
     286             : =============================
     287             :    Search the input string looking for comment chars.  If it contains
     288             :    comment chars then NUKE the remainder of the line, if the comment
     289             :    char is preceeded by \ then shift the remainder of the line left
     290             :    by one char. */
     291             : char *str;
     292             : char **pend;
     293             : int  keep;
     294             : {
     295      979432 :    char *c = str;
     296             : 
     297     1959232 :    while( (c = strchr(c, COMMENT_CHAR)) != NIL(char) ) {
     298      231600 :       if( Comment || State == NORMAL_SCAN )
     299      183192 :      if( c != str && c[-1] == ESCAPE_CHAR ) {
     300         368 :         size_t len = strlen(c)+1;
     301         368 :         memmove( c-1, c, len );  /* copy it left, due to \# */
     302         368 :         if( pend ) (*pend)--;    /* shift tail pointer left */
     303             :      }
     304             :      else {
     305             :         /* Check/execute if shebang command is present. */
     306      182088 :         if(    !No_exec
     307      182088 :         && c == str
     308      144430 :             && c[1] == '!'
     309           0 :             && Line_number == 1
     310           0 :         && Nestlevel() == 1 ) {
     311             :            char *cmnd;
     312             : 
     313           0 :            cmnd = Expand(c+2);
     314           0 :            cmnd[strlen(cmnd)-1] = '\0'; /* strip last newline */
     315           0 :            Current_target = Root;
     316             : #if defined(MSDOS)
     317             :            Swap_on_exec = TRUE;
     318             : #endif
     319           0 :            Wait_for_completion = TRUE;
     320           0 :            Do_cmnd(&cmnd, FALSE, TRUE, Current_target, A_DEFAULT, TRUE);
     321             : #if defined(MSDOS)
     322             :            Swap_on_exec = FALSE;
     323             : #endif
     324           0 :            Wait_for_completion = FALSE;
     325           0 :            FREE(cmnd);
     326             :         }
     327             : 
     328      182088 :         *c = '\0';               /* a true comment so break */
     329      182088 :         break;
     330             :      }
     331             :       else {
     332       49144 :          if( keep )
     333        2984 :         c = NIL(char);
     334             :      else
     335       46160 :         *c = '\0';
     336             : 
     337       49144 :      break;
     338             :       }
     339             :    }
     340             : 
     341      979432 :    return(c);
     342             : }
     343             : 
     344             : 
     345             : PUBLIC char *
     346     1649488 : Get_token( string, brk, anchor )/*
     347             : ==================================
     348             :     Return the next token in string.
     349             : 
     350             :     Returns empty string when no more tokens in string.
     351             :     brk is a list of chars that also cause breaks in addition to space and
     352             :     tab, but are themselves returned as tokens.  if brk is NULL then the
     353             :     remainder of the line is returned as a single token.
     354             : 
     355             :     'anchor' if 1, says break on chars in the brk list, but only if
     356             :     the entire token begins with the first char of the brk list, if
     357             :     0 then any char of brk will cause a break to occurr.
     358             : 
     359             :     If 'anchor' is 2, then break only seeing the first char in the break
     360             :     list allowing only chars in the break list to form the prefix. */
     361             : 
     362             : TKSTRPTR  string;
     363             : char      *brk;
     364             : int   anchor;
     365             : {
     366             :    register char *s;
     367     1649488 :    register char *curp = 0;
     368             :    register char *t;
     369     1649488 :    int           done = FALSE;
     370             :    char          space[100];
     371             : 
     372             :    DB_ENTER( "Get_token" );
     373             : 
     374     1649488 :    s  = string->tk_str;           /* Get string parameters  */
     375     1649488 :    *s = string->tk_cchar;         /* ... and strip leading w/s  */
     376             : 
     377     1649488 :    SCAN_WHITE( s );
     378             : 
     379             :    DB_PRINT( "tok", ("What's left [%s]", s) );
     380             : 
     381     1649488 :    if( !*s ) {
     382             :       DB_PRINT( "tok", ("Returning NULL token") );
     383      173606 :       DB_RETURN( "" );
     384             :    }
     385             : 
     386             : 
     387             :    /* Build the space list.  space contains all those chars that may possibly
     388             :     * cause breaks.  This includes the brk list as well as white space. */
     389             : 
     390     1475882 :    if( brk != NIL(char) ) {
     391     1138344 :       strcpy( space, " \t\r\n" );
     392     1138344 :       strcat( space, brk   );
     393             :    }
     394             :    else {
     395      337538 :       space[0] = 0xff;            /* a char we know will not show up      */
     396      337538 :       space[1] = 0;
     397             :    }
     398             : 
     399             : 
     400             :    /* Handle processing of quoted tokens.  Note that this is disabled if
     401             :     * brk is equal to NIL */
     402             : 
     403     2953324 :    while( *s == '\"' && ((brk != NIL(char)) || !string->tk_quote) ) {
     404        3120 :       s++;
     405        3120 :       if( string->tk_quote ) {
     406        1560 :      curp = s-1;
     407        1560 :      do { curp = strchr( curp+1, '\"' ); }
     408        1560 :      while( (curp != NIL(char)) && (*(curp+1) == '\"'));
     409             : 
     410        1560 :          if( curp == NIL(char) ) Fatal( "Unmatched quote in token" );
     411        1560 :      string->tk_quote = !string->tk_quote;
     412             : 
     413             :      /* Check for "" case, and if found ignore it */
     414        1560 :      if( curp == s ) continue;
     415        1560 :      goto found_token;
     416             :       }
     417             :       else
     418        1560 :      SCAN_WHITE( s );
     419             : 
     420        1560 :       string->tk_quote = !string->tk_quote;
     421             :    }
     422             : 
     423             : 
     424             :    /* Check for a token break character at the beginning of the token.
     425             :     * If found return the next set of break chars as a token. */
     426             : 
     427     1474322 :    if( anchor == 2 && brk != NIL(char) ) {
     428      299298 :       curp = s;
     429      299298 :       while( *curp && (strchr(brk,*curp)!=NIL(char)) && (*curp!=*brk) ) curp++;
     430      299298 :       done = (*brk == *curp++);
     431             :    }
     432     1175024 :    else if( (brk != NIL(char)) && (strchr( brk, *s ) != NIL(char)) ) {
     433       26650 :       curp = DmStrSpn( s, brk );
     434       53300 :       done = (anchor == 0) ? TRUE :
     435       26650 :          ((anchor == 1)?(*s == *brk) : (*brk == curp[-1]));
     436             :    }
     437             : 
     438             : 
     439             :    /* Scan for the next token in the list and return it less the break char
     440             :     * that was used to terminate the token.  It will possibly be returned in
     441             :     * the next call to Get_token */
     442             : 
     443     1474322 :    if( !done ) {
     444     1174668 :       SCAN_WHITE( s );
     445             : 
     446     1174668 :       t = s;
     447             :       do {
     448     1365102 :      done = TRUE;
     449     1365102 :      curp = DmStrPbrk(t, space);
     450             : 
     451     1365102 :      if( anchor && *curp && !IS_WHITE( *curp ) )
     452      194366 :         if( ((anchor == 1)?*curp:DmStrSpn(curp,brk)[-1]) != *brk ) {
     453      190434 :            t++;
     454      190434 :            done = FALSE;
     455             :         }
     456             :       }
     457     1365102 :       while( !done );
     458             : 
     459     1174668 :       if( (curp == s) && (strchr(brk, *curp) != NIL(char)) ) curp++;
     460             :    }
     461             : 
     462             : found_token:
     463     1475882 :    string->tk_str   = curp;
     464     1475882 :    string->tk_cchar = *curp;
     465     1475882 :    *curp = '\0';
     466             : 
     467             :    DB_PRINT( "tok", ("Returning [%s]", s) );
     468     1475882 :    DB_RETURN( s );
     469             : }
     470             : 
     471             : 
     472             : static int
     473      329978 : _is_conditional( tg )/*
     474             : =======================
     475             :     Look at tg and return it's value if it is a conditional identifier
     476             :     otherwise return 0. */
     477             : char *tg;
     478             : {
     479             :    DB_ENTER( "_is_conditional" );
     480             : 
     481      329978 :    tg++;
     482      329978 :    switch( *tg )
     483             :    {
     484             :       case 'I':
     485      154304 :          if( !strcmp( tg, "IF" ))         DB_RETURN( ST_IF    );
     486       16886 :          else if( !strcmp( tg, "IFEQ" ))  DB_RETURN( ST_IFEQ  );
     487       16886 :          else if( !strcmp( tg, "IFNEQ" )) DB_RETURN( ST_IFNEQ );
     488       16886 :          break;
     489             : 
     490             :       case 'E':
     491      172444 :          if( !strcmp( tg, "END" ))        DB_RETURN( ST_END  );
     492      171046 :          else if( !strcmp( tg, "ENDIF")) DB_RETURN( ST_END  );
     493       35026 :          else if( !strcmp( tg, "ELSE" )) DB_RETURN( ST_ELSE );
     494        4656 :          else if( !strcmp( tg, "ELIF" )) DB_RETURN( ST_ELIF );
     495        3862 :          break;
     496             :    }
     497             : 
     498       23978 :    DB_RETURN( 0 );
     499             : }
     500             : 
     501             : 
     502             : 
     503             : #define SEEN_END  0x00
     504             : #define SEEN_IF   0x01
     505             : #define SEEN_ELSE 0x02
     506             : #define SEEN_ELIF 0x04
     507             : 
     508             : #define ACCEPT_IF   0x10
     509             : #define ACCEPT_ELIF 0x20
     510             : 
     511             : static int
     512      306000 : _handle_conditional( opcode, tg )
     513             :     int      opcode;
     514             :     TKSTRPTR tg;
     515             : {
     516             :    static short action[MAX_COND_DEPTH];
     517             :    static char      ifcntl[MAX_COND_DEPTH];
     518             :    char         *cst;
     519             :    char         *lhs, *expr, *expr_end;
     520             :    char         *lop;
     521             :    int          result;
     522             : 
     523             :    DB_ENTER( "_handle_conditional" );
     524             : 
     525      306000 :    switch( opcode ) {
     526             :       case ST_ELIF:
     527         794 :          if( !(ifcntl[Nest_level] & SEEN_IF) || (ifcntl[Nest_level]&SEEN_ELSE) )
     528           0 :         Fatal(".ELIF without a preceeding .IF" );
     529             :      /*FALLTHRU*/
     530             : 
     531             :       case ST_IF:
     532             :       case ST_IFEQ:
     533             :       case ST_IFNEQ:
     534      138212 :      if( opcode != ST_ELIF && (Nest_level+1) == MAX_COND_DEPTH )
     535           0 :         Fatal( ".IF .ELSE ... .END nesting too deep" );
     536             : 
     537      138212 :      If_expand = TRUE;
     538      138212 :      expr = Expand( Get_token( tg, NIL(char), FALSE ));
     539      138212 :      If_expand = FALSE;
     540             : 
     541             :      /* Remove CONTINUATION_CHAR<nl> and replace with "  " so that line
     542             :       * continuations are recognized as whitespace. */
     543      138400 :      for( cst=strchr(expr,CONTINUATION_CHAR); cst != NIL(char); cst=strchr(cst,CONTINUATION_CHAR) )
     544         188 :         if( cst[1] == '\n' ) {
     545         188 :            *cst = ' ';
     546         188 :            cst[1] = ' ';
     547             :         }
     548             :         else
     549           0 :            cst++;
     550             : 
     551      138212 :      lhs = expr;
     552      138212 :      SCAN_WHITE( lhs );
     553             : 
     554             :      /* Parse the expression and get its logical result */
     555      138212 :      if ( ((lop = DmStrStr(lhs, "||" )) !=  NIL(char)) || ((lop = DmStrStr(lhs, "&&" )) !=  NIL(char)) )
     556        8856 :         result = parse_complex_expression( lhs, &expr_end, opcode );
     557             :      else
     558      129356 :         result = partcomp( lhs, opcode );
     559             : 
     560      138212 :      if( expr != NIL(char) ) FREE( expr );
     561             : 
     562      138212 :      if( opcode != ST_ELIF ) {
     563      137418 :         Nest_level++;
     564      137418 :         action[Nest_level] = 1;
     565             :      }
     566      138212 :      ifcntl[Nest_level] |= (opcode==ST_ELIF)?SEEN_ELIF:SEEN_IF;
     567             : 
     568      138212 :      if( result ) {
     569       40128 :         if( !(ifcntl[Nest_level] & (ACCEPT_IF|ACCEPT_ELIF)) ) {
     570       40126 :            action[ Nest_level ] = action[ Nest_level-1 ];
     571       40126 :            ifcntl[Nest_level] |= (opcode==ST_ELIF)?ACCEPT_ELIF:ACCEPT_IF;
     572             :         }
     573             :         else
     574           2 :            action[Nest_level] = 1;
     575             :      }
     576             :      else
     577       98084 :         action[Nest_level] = 1;
     578      138212 :      break;
     579             : 
     580             :       case ST_ELSE:
     581       30370 :      if( Nest_level <= 0 ) Fatal( ".ELSE without .IF" );
     582       30370 :      if( ifcntl[Nest_level] & SEEN_ELSE )
     583           0 :         Fatal( "Missing .IF or .ELIF before .ELSE" );
     584             : 
     585       30370 :      if( ifcntl[Nest_level] & (ACCEPT_IF|ACCEPT_ELIF) )
     586       14492 :         action[Nest_level] = 1;
     587       15878 :      else if( action[ Nest_level-1 ] != 1 )
     588       10054 :         action[ Nest_level ] ^= 0x1;    /* flip between 0 and 1 */
     589             : 
     590       30370 :      ifcntl[Nest_level] |= SEEN_ELSE;
     591       30370 :      break;
     592             : 
     593             :       case ST_END:
     594      137418 :      ifcntl[Nest_level] = SEEN_END;
     595      137418 :      Nest_level--;
     596      137418 :      if( Nest_level < 0 ) Fatal( "Unmatched .END[IF]" );
     597      137418 :      break;
     598             :    }
     599             : 
     600      306000 :    DB_RETURN( action[ Nest_level ] );
     601             : }
     602             : 
     603             : /* uncomment to turn on expression debug statements */
     604             : /*#define PARSE_DEBUG */
     605             : #define PARSE_SKIP_WHITE(A)     while( *A && ((*A==' ') || (*A=='\t')) )  A++;
     606             : 
     607             : #define OP_NONE 0
     608             : #define OP_AND  1
     609             : #define OP_OR   2
     610             : 
     611             : static int n = 1;
     612             : 
     613        9414 : int parse_complex_expression( char *expr, char **expr_end, int opcode )
     614             : {
     615        9414 :     char *p = expr;
     616        9414 :     char *term_start = p;
     617             :     char *term_end;
     618             :     int  local_term;
     619             :     char *part;
     620        9414 :     int  term_result = FALSE;
     621        9414 :     int  final_result = TRUE;
     622             :     unsigned int   term_len;
     623        9414 :     unsigned int   last_op = OP_NONE;
     624             : 
     625             :     #ifdef PARSE_DEBUG
     626             :         printf( "%d: parse_complex_expression( %s ): Opcode: %d\n", n, expr, opcode );
     627             :     #endif
     628             : 
     629             :     while ( 1 )
     630             :     {
     631             :         /* A new sub-expression */
     632      358752 :         local_term = TRUE;
     633      358752 :         if ( *p == '(' )
     634             :         {
     635         558 :             n++;
     636         558 :             term_result = parse_complex_expression( p+1, &p, opcode );
     637         558 :             n--;
     638         558 :             PARSE_SKIP_WHITE( p );
     639         558 :             term_start = p;
     640         558 :             term_end = p;
     641         558 :             local_term = FALSE;
     642             :         }
     643             :         else
     644      358194 :             term_end = p;
     645             : 
     646             :         /* Lets do an operation!! */
     647      358752 :         if ( !(*p)                                  /* at the end of the entire line */
     648      349896 :             || ((*p == '&') && (*(p+1) && (*(p+1)=='&')))   /* found an && */
     649      341528 :             || ((*p == '|') && (*(p+1) && (*(p+1)=='|')))   /* found an || */
     650      334788 :             || (*p == ')') )                            /* at the end of our term */
     651             :         {
     652             :             /* Grab the sub-expression if we parsed it.  Otherwise,
     653             :              * it was a () subexpression and we don't need to evaluate
     654             :              * it since that was already done.
     655             :              */
     656       24522 :             if ( local_term == TRUE )
     657             :             {
     658             :                 /* Back up 1 to the end of the actual term */
     659       23964 :                 term_end--;
     660             : 
     661             :                 /* Evaluate the term */
     662       23964 :                 PARSE_SKIP_WHITE( term_start );
     663       23964 :                 term_len = term_end - term_start + 1;
     664       23964 :                 part = MALLOC( term_len + 1, char );
     665       23964 :                 strncpy( part, term_start, term_len );
     666       23964 :                 *(part+term_len) = '\0';
     667             :                 #ifdef PARSE_DEBUG
     668             :                     printf( "%d:   evaling '%s'\n", n, part );
     669             :                 #endif
     670       23964 :                 term_result = partcomp( part, opcode );
     671             :                 #ifdef PARSE_DEBUG
     672             :                     printf( "%d:   evaled, result %d\n", n, term_result );
     673             :                 #endif
     674       23964 :                 FREE( part );
     675             :             }
     676             : 
     677             :             /* Do the actual logical operation using the _preceding_
     678             :              * logical operator, NOT the one we just found.
     679             :              */
     680       24522 :             if ( last_op == OP_AND )
     681        8368 :                 final_result = final_result && term_result;
     682       16154 :             else if ( last_op == OP_OR )
     683        6740 :                 final_result = final_result || term_result;
     684             :             else
     685        9414 :                 final_result = term_result;
     686             :             #ifdef PARSE_DEBUG
     687             :                 printf( "%d:   final_result:%d\n", n, final_result );
     688             :             #endif
     689             : 
     690             :             /* If we're not at the end of the line, just keep going */
     691       39630 :             if ( *p )
     692             :             {
     693             :                 /* Recognize the operator we just found above */
     694       15666 :                 if ( *p == '&' )
     695        8368 :                     last_op = OP_AND;
     696        7298 :                 else if ( *p == '|' )
     697        6740 :                     last_op = OP_OR;
     698       15666 :                 if ( *p != ')' )
     699       15108 :                     p += 2;
     700             : 
     701             :                 /* Get the start of the next term */
     702       15666 :                 PARSE_SKIP_WHITE( p );
     703       15666 :                 term_start = p;
     704             : 
     705             :                 /* If this is the close of a term, we are done and return
     706             :                 * to our caller.
     707             :                 */
     708       15666 :                 if ( *p == ')' )
     709             :                 {
     710         558 :                     p++;
     711         558 :                     break;
     712             :                 }
     713             :             }
     714        8856 :             else break;     /* At end of line, all done */
     715             :         }
     716      334230 :         else if ( local_term == TRUE )  p++;        /* Advance to next char in expression */
     717      349338 :     }
     718        9414 :     *expr_end = p;
     719             : 
     720             :     #ifdef PARSE_DEBUG
     721             :         printf( "%d:   done, returning '%s', result %d\n", n, *expr_end, final_result );
     722             :     #endif
     723        9414 :     return( final_result );
     724             : }
     725             : 
     726             : 
     727      153320 : int partcomp( char* lhs, int opcode )
     728             : {
     729             : 
     730      153320 :     char    *tok, *rhs, *op = 0;
     731             :     int     result, opsind;
     732      153320 :     const int localopscount=4;
     733      153320 :     char*   localops[] = { "==", "!=", "<=", ">=" };
     734             : 
     735             : #define EQUAL           0
     736             : #define NOTEQUAL        1
     737             : #define LESS_EQUAL      2
     738             : #define GREATER_EQUAL   3
     739             : 
     740             :     #ifdef PARSE_DEBUG
     741             :         printf( "eval: %s\n", lhs);
     742             :     #endif
     743             : 
     744      153320 :     opsind = 0;
     745      153320 :     if( opcode == ST_IFEQ || opcode == ST_IFNEQ )
     746             :     {
     747             :         /* IF[N]EQ syntax is:  .IF[N]EQ <1> <2>
     748             :          * Here, step over first argument and get to <2> if it exists.
     749             :          */
     750           0 :         for( op = lhs; ((*op)&&(*op != ' ')&&(*op != '\t')); op++ );
     751           0 :         if( *op )       op++;           /* position op at start of <2> */
     752           0 :         else            op = NIL(char); /* only 1 argument given */
     753             :     }
     754             :     else
     755             :     {
     756             :         /* Find which logical operator we are to use for this expression,
     757             :          * and jump to it */
     758      390512 :         while ( (opsind < localopscount) && ((op = DmStrStr(lhs, localops[opsind])) == NIL(char)) )
     759       83872 :             opsind++;
     760             : 
     761             :         #ifdef PARSE_DEBUG
     762             :             printf("  found op %d: %s\n", opsind, localops[opsind]);
     763             :         #endif
     764             :      }
     765             : 
     766             :     /* If the opcode was IFEQ or IFNEQ and only 1 argument was given,
     767             :      * or an unknown logical operator was encountered,
     768             :      * return false if argument is empty string, true if !empty
     769             :      */
     770      153320 :     if( op == NIL(char) )
     771           0 :         result = (*lhs != '\0');
     772             :     else
     773             :      {
     774             :         /* Make both characters of the operation the same, replacing the = in op[1]
     775             :          * Its easier to deal with this way???
     776             :          */
     777      153320 :         if( opcode != ST_IFEQ && opcode != ST_IFNEQ )
     778      153320 :             op[1] = op[0];
     779             : 
     780             :         #ifdef PARSE_DEBUG
     781             :             printf("  op:%s\n", op);
     782             :         #endif
     783             : 
     784             :         /* Isolate the left half of the expression */
     785      153320 :         if( lhs != op )
     786             :         {
     787      153136 :             for( tok = op-1; (tok != lhs) && ((*tok == ' ')||(*tok == '\t')); tok-- );
     788      153136 :             tok[1] = '\0';
     789             :         }
     790             :         else
     791         184 :             lhs = NIL(char); /* Left hand side is empty. */
     792             : 
     793             :         /* Jump over the operation so we can grab the right half of the expression */
     794      153320 :         if( opcode == ST_IFEQ || opcode == ST_IFNEQ )
     795           0 :             op--;
     796             :         else
     797      153320 :             op++;
     798             : 
     799             :         /* Isolate the right half of the expression */
     800      153320 :         rhs = DmStrSpn( op+1, " \t" );
     801      153320 :         if( !*rhs ) rhs = NIL(char);
     802             : 
     803             :         #ifdef PARSE_DEBUG
     804             :             printf("  lhs:%s, rhs:%s\n", lhs, rhs);
     805             :         #endif
     806             : 
     807             :         /* Do the actual logical operation on the expression */
     808      153320 :         if ( opsind > NOTEQUAL )
     809             :         {
     810        2384 :             switch( opsind )
     811             :             {
     812             :                 case LESS_EQUAL:
     813             :                 case GREATER_EQUAL:
     814             :                     /* Ignore quotes around the arguments */
     815        2384 :                     if ( lhs && lhs[0] == '"' ) lhs++;
     816        2384 :                     if ( rhs && rhs[0] == '"' ) rhs++;
     817             : 
     818             :                     /* Empty strings evaluate to zero. */
     819        2384 :                     int lint = lhs ? atoi( lhs ) : 0;
     820        2384 :                     int rint = rhs ? atoi( rhs ) : 0;
     821        2384 :                     result = ( lint >= rint ) ? TRUE : FALSE;
     822        2384 :                     if ( opsind == LESS_EQUAL && lint != rint )
     823         736 :                         result = !result;
     824        2384 :                     break;
     825             :                 default:
     826           0 :                     result = FALSE;
     827             :             }
     828             :         }
     829             :         else
     830             :         {
     831             :             /* Use a simple string compare to determine equality */
     832      150936 :             if( (rhs == NIL(char)) || (lhs == NIL(char)) )
     833         184 :                 result = (rhs == lhs) ? TRUE : FALSE;
     834             :             else
     835             :             {
     836             :                 /* String off whitespace at the end of the right half of the expression */
     837      150752 :                 tok = rhs + strlen( rhs );
     838      150752 :                 for( tok=tok-1; (tok != lhs) && ((*tok == ' ')||(*tok == '\t')); tok--);
     839      150752 :                 tok[1] = '\0';
     840             : 
     841      150752 :                   result = (strcmp( lhs, rhs ) == 0) ? TRUE : FALSE;
     842             :             }
     843             : 
     844      150936 :             if( *op == '!' || opcode == ST_IFNEQ ) result = !result;
     845             :         }
     846             :     }
     847             : 
     848             :     #ifdef PARSE_DEBUG
     849             :         printf("partresult %d\n\n",result);
     850             :     #endif
     851      153320 :     return result;
     852             : }
     853             : 

Generated by: LCOV version 1.10