LCOV - code coverage report
Current view: top level - libreoffice/dmake - expand.c (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 275 459 59.9 %
Date: 2012-12-17 Functions: 8 10 80.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             : --
       3             : -- SYNOPSIS
       4             : --      Macro expansion code.
       5             : --
       6             : -- DESCRIPTION
       7             : --
       8             : --  This routine handles all the necessary junk that deals with macro
       9             : --  expansion.  It understands the following syntax.  If a macro is
      10             : --  not defined it expands to NULL, and {} are synonyms for ().
      11             : --
      12             : --      $$      - expands to $
      13             : --      {{      - expands to {
      14             : --          }}      - expands to }
      15             : --      $A      - expands to whatever the macro A is defined as
      16             : --      $(AA)   - expands to whatever the macro AA is defined as
      17             : --      $($(A)) - represents macro indirection
      18             : --      <+...+> - get mapped to $(mktmp ...)
      19             : --
      20             : --        following macro is recognized
      21             : --
      22             : --                string1{ token_list }string2
      23             : --
      24             : --        and expands to string1 prepended to each element of token_list and
      25             : --        string2 appended to each of the resulting tokens from the first
      26             : --        operation.  If string2 is of the form above then the result is
      27             : --        the cross product of the specified (possibly modified) token_lists.
      28             : --
      29             : --        The folowing macro modifiers are defined and expanded:
      30             : --
      31             : --               $(macro:modifier_list:modifier_list:...)
      32             : --
      33             : --        where modifier_list a combination of:
      34             : --
      35             : --               D or d      - Directory portion of token including separator
      36             : --               F or f      - File portion of token including suffix
      37             : --               B or b      - basename portion of token not including suffix
      38             : --               E or e      - Suffix portion of name
      39             : --               L or l      - translate to lower case
      40             : --               U or u      - translate to upper case
      41             : --               I or i      - return inferred names
      42             : --               N or n      - return normalized paths
      43             : --               1           - return the first white space separated token
      44             : --
      45             : --    or a single one of:
      46             : --               M or m      - map escape codes
      47             : --               S or s      - pattern substitution (simple)
      48             : --               T or t      - for tokenization
      49             : --               ^           - prepend a prefix to each token
      50             : --               +           - append a suffix to each token
      51             : --
      52             : --        NOTE:  Modifiers are applied once the macro value has been found.
      53             : --               Thus the construct $($(test):s/joe/mary/) is defined and
      54             : --               modifies the value of $($(test))
      55             : --
      56             : --         Also the construct $(m:d:f) is not the same as $(m:df)
      57             : --         the first applies d to the value of $(m) and then
      58             : --         applies f to the value of that whereas the second form
      59             : --         applies df to the value of $(m).
      60             : --
      61             : -- AUTHOR
      62             : --      Dennis Vadura, dvadura@dmake.wticorp.com
      63             : --
      64             : -- WWW
      65             : --      http://dmake.wticorp.com/
      66             : --
      67             : -- COPYRIGHT
      68             : --      Copyright (c) 1996,1997 by WTI Corp.  All rights reserved.
      69             : --
      70             : --      This program is NOT free software; you can redistribute it and/or
      71             : --      modify it under the terms of the Software License Agreement Provided
      72             : --      in the file <distribution-root>/readme/license.txt.
      73             : --
      74             : -- LOG
      75             : --      Use cvs log to obtain detailed change logs.
      76             : */
      77             : 
      78             : #include "extern.h"
      79             : 
      80             : /* Microsoft BRAINDAMAGE ALERT!!!!
      81             :  * This #ifdef is here only to satisfy stupid bugs in MSC5.0 and MSC5.1
      82             :  * it isn't needed for anything else.  It turns loop optimization off. */
      83             : #if defined(_MSV_VER) && _MSC_VER < 600
      84             : #include "optoff.h"
      85             : #endif
      86             : 
      87             : static  char*   _scan_macro ANSI((char*, char**, int));
      88             : static  char*   _scan_brace ANSI((char*, char**, int*));
      89             : static  char*   _cross_prod ANSI((char*, char*));
      90             : 
      91             : #if !defined(__GNUC__) && !defined(__IBMC__)
      92             : static  char*   _scan_ballanced_parens ANSI((char*, char));
      93             : #else
      94             : static  char*   _scan_ballanced_parens ANSI((char*, int));
      95             : #endif
      96             : 
      97             : 
      98             : PUBLIC char *
      99      974782 : Expand( src )/*
     100             : ===============
     101             :       This is the driver routine for the expansion, it identifies non-white
     102             :       space tokens and gets the ScanToken routine to figure out if they should
     103             :       be treated in a special way. */
     104             : 
     105             : char *src;                    /* pointer to source string  */
     106             : {
     107             :    char  *tmp;            /* pointer to temporary str  */
     108             :    char  *res;                /* pointer to result string  */
     109             :    char  *start;              /* pointer to start of token */
     110             : 
     111             :    DB_ENTER( "Expand" );
     112             :    DB_PRINT( "exp", ("Expanding [%s]", src) );
     113             : 
     114      974782 :    res = DmStrDup( "" );
     115      974782 :    if( src == NIL(char) ) DB_RETURN( res );
     116             : 
     117     3155880 :    while( *src ) {
     118             :       char *ks, *ke;
     119             : 
     120             :       /* Here we find the next non white space token in the string
     121             :        * and find it's end, with respect to non-significant white space. */
     122             : 
     123             : #if !defined( _MPW) && !defined(__EMX__)
     124     1211612 :       start = DmStrSpn( src, " \t\n" );
     125             : #else
     126             :       start = DmStrSpn( src, " \t\r\n" );
     127             : #endif
     128             : 
     129     1211612 :       res   = DmStrJoin( res, src, start-src, TRUE );
     130     1211612 :       if( !(*start) ) break;
     131             : 
     132             :       /* START <+...+> KLUDGE */
     133     1209436 :       if(   (ks=DmStrStr(start,"<+")) != NIL(char)
     134           0 :             && (ke=DmStrStr(ks,"+>")) != NIL(char) ) {
     135             :         char *t1, *t2;
     136             : 
     137           0 :         res = DmStrJoin( res, t2=Expand(t1=DmSubStr(start,ks)), -1, TRUE);
     138           0 :         FREE(t1); FREE(t2);
     139             : 
     140           0 :         t1 = DmSubStr(ks+2, ke+1); t1[ke-ks-2] = ')';
     141           0 :         t2 = DmStrJoin( "$(mktmp ", t1, -1,FALSE);
     142           0 :         FREE(t1);
     143           0 :         res = DmStrJoin( res, t1=Expand(t2), -1, TRUE);
     144           0 :         FREE(t1); FREE(t2);
     145           0 :         src = ke+2;
     146             :       }
     147             :       /* END <+...+> KLUDGE */
     148             :       else {
     149     1209436 :         res   = DmStrJoin( res, tmp = ScanToken(start,&src,TRUE), -1, TRUE );
     150     1209436 :         FREE( tmp );
     151             :       }
     152             :    }
     153             : 
     154             :    DB_PRINT( "exp", ("Returning [%s]", res) );
     155      973222 :    DB_RETURN( res );
     156             : }
     157             : 
     158             : 
     159             : PUBLIC char *
     160       64784 : Apply_edit( src, pat, subst, fr, anchor )/*
     161             : ===========================================
     162             :    Take the src string and apply the pattern substitution.  ie. look for
     163             :    occurrences of pat in src and replace each occurrence with subst.  This is
     164             :    NOT a regular expressions pattern substitution, it's just not worth it.
     165             : 
     166             :    if anchor == TRUE then the src pattern match must be at the end of a token.
     167             :    ie. this is for SYSV compatibility and is only used for substitutions of
     168             :    the caused by $(macro:pat=sub).  So if src = "fre.o.k june.o" then
     169             :    $(src:.o=.a) results in "fre.o.k june.a", and $(src:s/.o/.a) results in
     170             :    "fre.a.k june.a" */
     171             : 
     172             : char *src;          /* the source string */
     173             : char *pat;          /* pattern to find   */
     174             : char *subst;            /* substitute string */
     175             : int   fr;           /* if TRUE free src  */
     176             : int   anchor;           /* if TRUE anchor    */
     177             : {
     178             :    char *res;
     179             :    char *p;
     180             :    char *s;
     181             :    int   l;
     182             : 
     183             :    DB_ENTER( "Apply_edit" );
     184             : 
     185             :    /* do nothing if pat is NULL or pat and subst are equal */
     186       64784 :    if( !*pat || !strcmp(pat,subst) ) DB_RETURN( src );
     187             : 
     188             :    DB_PRINT( "mod", ("Source str:  [%s]", src) );
     189             :    DB_PRINT( "mod", ("Replacing [%s], with [%s]", pat, subst) );
     190             : 
     191             :    /* FIXME: This routine is used frequently and has room for optimizations */
     192       45554 :    s   = src;
     193       45554 :    l   = strlen( pat );
     194       45554 :    if( (p = DmStrStr( s, pat )) != NIL(char) ) {
     195       41484 :       res = DmStrDup( "" );
     196             :       do {
     197       41526 :      if( anchor )
     198           0 :         if( !*(p+l) || (strchr(" \t", *(p+l)) != NIL(char)) )
     199           0 :            res = DmStrJoin( DmStrJoin(res,s,p-s,TRUE), subst, -1, TRUE );
     200             :         else
     201           0 :            res = DmStrJoin( res, s, p+l-s, TRUE );
     202             :      else
     203       41526 :         res = DmStrJoin( DmStrJoin(res,s,p-s,TRUE), subst, -1, TRUE );
     204             : 
     205       41526 :      s   = p + l;
     206             :       }
     207       41526 :       while( (p = DmStrStr( s, pat )) != NIL(char) );
     208             : 
     209       41484 :       res = DmStrJoin( res, s, -1, TRUE );
     210       41484 :       if( fr ) FREE( src );
     211             :    }
     212             :    else
     213        4070 :       res = src;
     214             : 
     215             : 
     216             :    DB_PRINT( "mod", ("Result [%s]", res) );
     217       45554 :    DB_RETURN( res );
     218             : }
     219             : 
     220             : 
     221             : PUBLIC void
     222           0 : Map_esc( tok )/*
     223             : ================
     224             :    Map an escape sequence and replace it by it's corresponding character
     225             :    value.  It is assumed that tok points at the initial \, the esc
     226             :    sequence in the original string is replaced and the value of tok
     227             :    is not modified. */
     228             : char *tok;
     229             : {
     230           0 :    if( strchr( "\"\\vantbrf01234567", tok[1] ) ) {
     231             :       size_t len;
     232           0 :       switch( tok[1] ) {
     233           0 :      case 'a' : *tok = 0x07; break;
     234           0 :      case 'b' : *tok = '\b'; break;
     235           0 :      case 'f' : *tok = '\f'; break;
     236           0 :      case 'n' : *tok = '\n'; break;
     237           0 :      case 'r' : *tok = '\r'; break;
     238           0 :      case 't' : *tok = '\t'; break;
     239           0 :      case 'v' : *tok = 0x0b; break;
     240           0 :      case '\\': *tok = '\\'; break;
     241           0 :      case '\"': *tok = '\"'; break;
     242             : 
     243             :      default: {
     244           0 :         register int i = 0;
     245           0 :         register int j = 0;
     246           0 :         for( ; i<2 && isdigit(tok[2]); i++ ) {
     247           0 :            j = (j << 3) + (tok[1] - '0');
     248           0 :            len = strlen(tok+2)+1;
     249           0 :            memmove( tok+1, tok+2, len );
     250             :         }
     251           0 :         j = (j << 3) + (tok[1] - '0');
     252           0 :         *tok = j;
     253             :      }
     254             :       }
     255           0 :       len = strlen(tok+2)+1;
     256           0 :       memmove( tok+1, tok+2, len );
     257             :    }
     258           0 : }
     259             : 
     260             : 
     261             : PUBLIC char*
     262       44922 : Apply_modifiers( mod, src )/*
     263             : =============================
     264             :    This routine applies the appropriate modifiers to the string src
     265             :    and returns the proper result string */
     266             : 
     267             : int  mod;
     268             : char *src;
     269             : {
     270             :    char    *s;
     271             :    char    *e;
     272             :    char    *res;
     273             :    TKSTR   str;
     274             : 
     275             :    DB_ENTER( "Apply_modifiers" );
     276             : 
     277       44922 :    if ( mod & INFNAME_FLAG ) {
     278           0 :       SET_TOKEN( &str, src );
     279           0 :       e = NIL(char);
     280             : 
     281           0 :       while( *(s = Get_token( &str, "", FALSE )) != '\0' ) {
     282             :      HASHPTR hp;
     283             : 
     284           0 :      if ( (hp = Get_name(normalize_path(s), Defs, FALSE)) != NIL(HASH)
     285           0 :        && hp->CP_OWNR
     286           0 :        && hp->CP_OWNR->ce_fname
     287             :      ) {
     288           0 :         res = hp->CP_OWNR->ce_fname;
     289             :      }
     290             :      else
     291           0 :         res = s;
     292             : 
     293           0 :      if(str.tk_quote == 0) {
     294             :         /* Add leading quote. */
     295           0 :         e = DmStrApp(e, "\"");
     296           0 :         e = DmStrJoin(e, res, -1, TRUE);
     297             :         /* Append the trailing quote. */
     298           0 :         e = DmStrJoin(e, "\"", 1, TRUE);
     299             :      } else {
     300           0 :         e = DmStrApp(e, res);
     301             :      }
     302             : 
     303             :       }
     304             : 
     305           0 :       FREE(src);
     306           0 :       src = e;
     307           0 :       mod &= ~INFNAME_FLAG;
     308             :    }
     309             : 
     310       44922 :    if ( mod & NORMPATH_FLAG ) {
     311           0 :       e = exec_normpath(src);
     312             : 
     313           0 :       FREE(src);
     314           0 :       src = e;
     315           0 :       mod &= ~NORMPATH_FLAG;
     316             :    }
     317             : 
     318       44922 :    if(mod & (TOLOWER_FLAG|TOUPPER_FLAG) ) {
     319             :       int lower;
     320           0 :       lower = mod & TOLOWER_FLAG;
     321             : 
     322           0 :       for (s=src; *s; s++)
     323           0 :      if ( isalpha(*s) )
     324           0 :         *s = ((lower) ? tolower(*s) : toupper(*s));
     325             : 
     326           0 :       mod &= ~(TOLOWER_FLAG|TOUPPER_FLAG);
     327             :    }
     328             : 
     329       44922 :    if (mod & JUST_FIRST_FLAG) {
     330           0 :       SET_TOKEN(&str, src);
     331           0 :       if ((s = Get_token(&str,"",FALSE)) != '\0') {
     332             :      /* Recycle the quote at the beginning. */
     333           0 :      if(str.tk_quote == 0) {
     334           0 :         s--;
     335             :      }
     336           0 :          e = DmStrDup(s);
     337             :      /* Add trailing quote. */
     338           0 :      if(str.tk_quote == 0) {
     339           0 :         e = DmStrJoin(e, "\"", 1, TRUE);
     340             :      }
     341             : 
     342           0 :          CLEAR_TOKEN(&str);
     343           0 :          FREE(src);
     344           0 :          src = e;
     345             :       }
     346             :       else {
     347           0 :          CLEAR_TOKEN(&str);
     348             :       }
     349           0 :       mod &= ~JUST_FIRST_FLAG;
     350             :    }
     351             : 
     352       44922 :    if( !mod || mod == (SUFFIX_FLAG | DIRECTORY_FLAG | FILE_FLAG) )
     353           0 :       DB_RETURN( src );
     354             : 
     355       44922 :    SET_TOKEN( &str, src );
     356             :    DB_PRINT( "mod", ("Source string [%s]", src) );
     357       44922 :    res = DmStrDup("");
     358             : 
     359      134768 :    while( *(s = Get_token( &str, "", FALSE )) != '\0' ) {
     360       44924 :       char *tokstart = s;
     361             : 
     362             :       /* search for the directory portion of the filename.  If the
     363             :        * DIRECTORY_FLAG is set, then we want to keep the directory portion
     364             :        * othewise throw it away and blank out to the end of the token */
     365             : 
     366       44924 :       if( (e = Basename(s)) != s) {
     367       22532 :      if( !(mod & DIRECTORY_FLAG) ) {
     368             :         /* Move the basename to the start. */
     369       17998 :         size_t len = strlen(e)+1;
     370       17998 :         memmove(s, e, len);
     371             :      }
     372             :      else
     373        4534 :         s = e;
     374             :       }
     375             :       /* s now points to the start of the basename. */
     376             : 
     377             : 
     378             :       /* search for the suffix, if there is none, treat it as a NULL suffix.
     379             :        * if no file name treat it as a NULL file name.  same copy op as
     380             :        * for directory case above */
     381             : 
     382       44924 :       e = strrchr( s, '.' );            /* NULL suffix if e=0 */
     383       44924 :       if( e == NIL(char) ) e = s+strlen(s);
     384             : 
     385       44924 :       if( !(mod & FILE_FLAG) ) {
     386             :      /* Move the suffix to the start. */
     387       22406 :      size_t len = strlen(e)+1;
     388       22406 :          memmove(s, e, len);
     389             :       }
     390             :       else
     391       22518 :      s = e;
     392             : 
     393             :       /* s now points to the start of the suffix. */
     394             : 
     395             : 
     396             :       /* The last and final part.  This is the suffix case, if we don't want
     397             :        * it then just erase it. */
     398             : 
     399       44924 :       if( s != NIL(char) )
     400       44924 :      if( !(mod & SUFFIX_FLAG) && s != str.tk_str )
     401       22474 :         *s = '\0';
     402             : 
     403             : 
     404             :       /* only keep non-empty tokens. (This also discards empty quoted ""
     405             :        * tokens.) */
     406       44924 :       if( strlen(tokstart) ) {
     407             :      /* Recycle the quote at the beginning. */
     408       44924 :      if(str.tk_quote == 0) {
     409           0 :         tokstart--;
     410             :      }
     411       44924 :      res = DmStrApp(res, tokstart);
     412             :      /* Add trailing quote. */
     413       44924 :      if(str.tk_quote == 0) {
     414           0 :         res = DmStrJoin(res, "\"", 1, TRUE);
     415             :      }
     416             :       }
     417             :    }
     418             : 
     419       44922 :    FREE(src);
     420       44922 :    src = res;
     421             : 
     422             : 
     423             :    DB_PRINT( "mod", ("Result string [%s]", src) );
     424       44922 :    DB_RETURN( src );
     425             : }
     426             : 
     427             : 
     428             : PUBLIC char*
     429       20114 : Tokenize( src, separator, op, mapesc )/*
     430             : ========================================
     431             :     Tokenize the input of src and join each token found together with
     432             :     the next token separated by the separator string.
     433             : 
     434             :     When doing the tokenization, <sp>, <tab>, <nl>, and \<nl> all
     435             :     constitute white space. */
     436             : 
     437             : char *src;
     438             : char *separator;
     439             : char op;
     440             : int  mapesc;
     441             : {
     442             :    TKSTR    tokens;
     443             :    char     *tok;
     444             :    char     *res;
     445       20114 :    int      first = (op == 't' || op == 'T');
     446             : 
     447             :    DB_ENTER( "Tokenize" );
     448             : 
     449             :    /* map the escape codes in the separator string first */
     450       20114 :    if ( mapesc )
     451        1108 :       for(tok=separator; (tok = strchr(tok,ESCAPE_CHAR)) != NIL(char); tok++)
     452           0 :      Map_esc( tok );
     453             : 
     454             :    DB_PRINT( "exp", ("Separator [%s]", separator) );
     455             : 
     456             :    /* By default we return an empty string */
     457       20114 :    res = DmStrDup( "" );
     458             : 
     459             :    /* Build the token list */
     460       20114 :    SET_TOKEN( &tokens, src );
     461       61544 :    while( *(tok = Get_token( &tokens, "", FALSE )) != '\0' ) {
     462             :       char *x;
     463             : 
     464       21316 :       if( first ) {
     465         248 :      FREE( res );
     466         248 :      res   = DmStrDup( tok );
     467         248 :      first = FALSE;
     468             :       }
     469       21068 :       else if (op == '^') {
     470       20660 :      res = DmStrAdd(res, DmStrJoin(separator, tok, -1, FALSE), TRUE);
     471             :       }
     472         408 :       else if (op == '+') {
     473         408 :      res = DmStrAdd(res, DmStrJoin(tok, separator, -1, FALSE), TRUE);
     474             :       }
     475             :       else {
     476           0 :      res = DmStrJoin(res, x =DmStrJoin(separator, tok, -1, FALSE),
     477             :             -1, TRUE);
     478           0 :      FREE( x );
     479             :       }
     480             : 
     481             :       DB_PRINT( "exp", ("Tokenizing [%s] --> [%s]", tok, res) );
     482             :    }
     483             : 
     484       20114 :    FREE( src );
     485       20114 :    DB_RETURN( res );
     486             : }
     487             : 
     488             : 
     489             : static char*
     490           0 : _scan_ballanced_parens(p, delim)
     491             : char *p;
     492             : char delim;
     493             : {
     494           0 :    int pcount = 0;
     495           0 :    int bcount = 0;
     496             : 
     497           0 :    if ( p ) {
     498             :       do {
     499           0 :      if (delim)
     500           0 :         if( !(bcount || pcount) && *p == delim) {
     501           0 :            return(p);
     502             :         }
     503             : 
     504           0 :      if ( *p == '(' ) pcount++;
     505           0 :      else if ( *p == '{' ) bcount++;
     506           0 :      else if ( *p == ')' && pcount ) pcount--;
     507           0 :      else if ( *p == '}' && bcount ) bcount--;
     508             : 
     509           0 :      p++;
     510             :       }
     511           0 :       while (*p && (pcount || bcount || delim));
     512             :    }
     513             : 
     514           0 :    return(p);
     515             : }
     516             : 
     517             : 
     518             : PUBLIC char*
     519     1239028 : ScanToken( s, ps, doexpand )/*
     520             : ==============================
     521             :       This routine scans the token characters one at a time and identifies
     522             :       macros starting with $( and ${ and calls _scan_macro to expand their
     523             :       value.   the string1{ token_list }string2 expansion is also handled.
     524             :       In this case a temporary result is maintained so that we can take it's
     525             :       cross product with any other token_lists that may possibly appear. */
     526             : 
     527             : char *s;        /* pointer to start of src string */
     528             : char **ps;      /* pointer to start pointer   */
     529             : int  doexpand;
     530             : {
     531             :    char *res;                 /* pointer to result          */
     532             :    char *start;               /* pointer to start of prefix */
     533     1239028 :    int  crossproduct = 0;     /* if 1 then computing X-prod */
     534             : 
     535     1239028 :    start = s;
     536     1239028 :    res   = DmStrDup( "" );
     537             :    while( 1 ) {
     538    10390092 :       switch( *s ) {
     539             :          /* Termination, We halt at seeing a space or a tab or end of string.
     540             :           * We return the value of the result with any new macro's we scanned
     541             :           * or if we were computing cross_products then we return the new
     542             :           * cross_product.
     543             :           * NOTE:  Once we start computing cross products it is impossible to
     544             :           *        stop.  ie. the semantics are such that once a {} pair is
     545             :           *        seen we compute cross products until termination. */
     546             : 
     547             :          case ' ':
     548             :          case '\t':
     549             :      case '\n':
     550             :      case '\r':
     551             :          case '\0':
     552             :      {
     553             :         char *tmp;
     554             : 
     555     1239028 :         *ps = s;
     556     1239028 :         if( !crossproduct )
     557     1220022 :            tmp = DmStrJoin( res, start, (s-start), TRUE );
     558             :         else
     559             :         {
     560       19006 :            tmp = DmSubStr( start, s );
     561       19006 :            tmp = _cross_prod( res, tmp );
     562             :         }
     563     2478056 :         return( tmp );
     564             :      }
     565             : 
     566             :          case '$':
     567             :          case '{':
     568             :      {
     569             :         /* Handle if it's a macro or if it's a {} construct.
     570             :          * The results of a macro expansion are handled differently based
     571             :          * on whether we have seen a {} beforehand. */
     572             : 
     573             :         char *tmp;
     574      960944 :         tmp = DmSubStr( start, s );          /* save the prefix */
     575             : 
     576      960944 :         if( *s == '$' ) {
     577      941740 :            start = _scan_macro( s+1, &s, doexpand );
     578             : 
     579      941740 :            if( crossproduct ) {
     580         388 :           res = _cross_prod( res, DmStrJoin( tmp, start, -1, TRUE ) );
     581             :            }
     582             :            else {
     583      941352 :           res = DmStrJoin(res,tmp=DmStrJoin(tmp,start,-1,TRUE),-1,TRUE);
     584      941352 :           FREE( tmp );
     585             :            }
     586      941740 :            FREE( start );
     587             :         }
     588       19204 :         else if( strchr("{ \t",s[1]) == NIL(char) ){
     589             :            int ok;
     590       19020 :            start = _scan_brace( s+1, &s, &ok );
     591             : 
     592       19020 :            if( ok ) {
     593       19020 :           if ( crossproduct ) {
     594          14 :              res = _cross_prod(res,_cross_prod(tmp,start));
     595             :           }
     596             :           else {
     597             :              char *freeres;
     598       19006 :              res = Tokenize(start,
     599             :                     freeres=DmStrJoin(res,tmp,-1,TRUE),
     600             :                     '^', FALSE);
     601       19006 :              FREE(freeres);
     602       19006 :              FREE(tmp);
     603             :           }
     604       19020 :           crossproduct = TRUE;
     605             :            }
     606             :            else {
     607           0 :           res =DmStrJoin(res,tmp=DmStrJoin(tmp,start,-1,TRUE),-1,TRUE);
     608           0 :           FREE( start );
     609           0 :           FREE( tmp   );
     610             :            }
     611             :         }
     612             :         else {    /* handle the {{ case */
     613         184 :            res = DmStrJoin( res, start, (s-start+1), TRUE );
     614         184 :            s  += (s[1]=='{')?2:1;
     615         184 :            FREE( tmp );
     616             :         }
     617             : 
     618      960944 :         start = s;
     619             :      }
     620      960944 :      break;
     621             : 
     622             :      case '}':
     623         184 :         if( s[1] != '}' ) {
     624             :            /* error malformed macro expansion */
     625           0 :            s++;
     626             :         }
     627             :         else {    /* handle the }} case */
     628         184 :            res = DmStrJoin( res, start, (s-start+1), TRUE );
     629         184 :            s += 2;
     630         184 :            start = s;
     631             :         }
     632         184 :         break;
     633             : 
     634     8189936 :          default: s++;
     635             :       }
     636     9151064 :    }
     637             : }
     638             : 
     639             : 
     640             : static char*
     641      941740 : _scan_macro( s, ps, doexpand )/*
     642             : ================================
     643             :     This routine scans a macro use and expands it to the value.  It
     644             :     returns the macro's expanded value and modifies the pointer into the
     645             :     src string to point at the first character after the macro use.
     646             :     The types of uses recognized are:
     647             : 
     648             :         $$ and $<sp>    - expands to $
     649             :         $(name)     - expands to value of name
     650             :         ${name}     - same as above
     651             :         $($(name))  - recurses on macro names (any level)
     652             :     and
     653             :         $(func[,args ...] [data])
     654             :     and
     655             :             $(name:modifier_list:modifier_list:...)
     656             : 
     657             :     see comment for Expand for description of valid modifiers.
     658             : 
     659             :     NOTE that once a macro name bounded by ( or { is found only
     660             :     the appropriate terminator (ie. ( or } is searched for. */
     661             : 
     662             : char *s;        /* pointer to start of src string   */
     663             : char **ps;      /* pointer to start pointer     */
     664             : int  doexpand;          /* If TRUE enables macro expansion  */
     665             : {
     666             :    char sdelim;         /* start of macro delimiter         */
     667             :    char edelim;         /* corresponding end macro delim    */
     668             :    char *start;         /* start of prefix                  */
     669             :    char *macro_name;    /* temporary macro name             */
     670             :    char *recurse_name;  /* recursive macro name             */
     671             :    char *result;    /* result for macro expansion       */
     672      941740 :    int  bflag = 0;      /* brace flag, ==0 => $A type macro */
     673      941740 :    int  done  = 0;      /* != 0 => done macro search        */
     674      941740 :    int  lev   = 0;      /* brace level                      */
     675      941740 :    int  mflag = 0;      /* != 0 => modifiers present in mac */
     676      941740 :    int  fflag = 0;  /* != 0 => GNU style function       */
     677             :    HASHPTR hp;      /* hash table pointer for macros    */
     678             : 
     679             :    DB_ENTER( "_scan_macro" );
     680             : 
     681             :    /* Check for $ at end of line, or $ followed by white space */
     682             :    /* FIXME: Shouldn't a single '$' be an error? */
     683      941740 :    if( !*s || strchr(" \t", *s) != NIL(char)) {
     684           0 :       *ps = s;
     685           0 :       DB_RETURN( DmStrDup("") );
     686             :    }
     687             : 
     688      941740 :    if( *s == '$' ) {    /* Take care of the simple $$ case. */
     689       19486 :       *ps = s+1;
     690       19486 :       DB_RETURN( DmStrDup("$") );
     691             :    }
     692             : 
     693      922254 :    sdelim = *s;         /* set and remember start/end delim */
     694      922254 :    if( sdelim == '(' )
     695      737198 :       edelim = ')';
     696             :    else
     697      185056 :       edelim = '}';
     698             : 
     699      922254 :    start = s;           /* build up macro name, find its end */
     700    10525132 :    while( !done ) {
     701     8680624 :       switch( *s ) {
     702             :          case '(':              /* open macro brace */
     703             :          case '{':
     704      833356 :         if( *s == sdelim ) {
     705      833346 :            lev++;
     706      833346 :            bflag++;
     707             :         }
     708      833356 :         break;
     709             : 
     710             :          case ':':                              /* halt at modifier */
     711       37048 :             if( lev == 1 && !fflag && doexpand ) {
     712       31112 :                done = TRUE;
     713       31112 :                mflag = 1;
     714             :             }
     715        5936 :             else if( !lev )                     /* must be $: */
     716           0 :                Fatal( "Syntax error in macro [$%s]. A colon [:] cannot be a macro name.\n", start );
     717             : 
     718             :             /* continue if a colon is found but lev > 1 */
     719       37048 :             break;
     720             : 
     721             :      case '\n':                             /* Not possible because of the
     722             :                                                  * following case. */
     723           0 :         Fatal( "DEBUG: No standalone '\n' [%s].\n", start );
     724           0 :         break;
     725             : 
     726             :      case '\\':                             /* Transform \<nl> -> ' '. */
     727           0 :        if( s[1] != '\n' ) {
     728           0 :          done = !lev;
     729           0 :          break;
     730             :        } else {
     731             :          size_t len;
     732           0 :          s[1] = ' ';
     733           0 :          len = strlen(s+1)+1;
     734           0 :          memmove( s, s+1, len );
     735             :        }
     736             :        /*FALLTHRU*/
     737             :      case ' ':
     738             :      case '\t':
     739       42352 :         if ( lev == 1 ) fflag = 1;
     740       42352 :         break;
     741             : 
     742             :      case '\0':             /* check for null */
     743           0 :        *ps = s;
     744           0 :        done = TRUE;
     745           0 :        if( lev ) {              /* catch $( or ${ without closing bracket */
     746           0 :          Fatal( "Syntax error in macro [$%s]. The closing bracket [%c] is missing.\n", start, edelim );
     747             :        } else
     748           0 :          Fatal( "DEBUG: This cannot occur! [%s].\n", start );
     749           0 :        break;
     750             : 
     751             :       case ')':             /* close macro brace */
     752             :       case '}':
     753      802244 :         if( !lev )      /* A closing bracket without an .. */
     754           0 :           Fatal("Syntax error in macro [$%s]. Closing bracket [%c] cannot be a macro name.\n", start, *s );
     755      802244 :         else if( *s == edelim ) --lev;
     756             :       /*FALLTHRU*/
     757             : 
     758             :          default:       /* Done when lev == 0. This means either no */
     759     7767868 :         done = !lev;    /* opening bracket (single letter macro) or */
     760             :                 /* a fully enclosed $(..) or ${..} macro    */
     761             :                 /* was found. */
     762             :       }
     763     8680624 :       s++;
     764             :    }
     765             : 
     766             :    /* Check if this is a $A type macro.  If so then we have to
     767             :     * handle it a little differently. */
     768      922254 :    if( bflag )
     769      737198 :      macro_name = DmSubStr( start+1, s-1 );
     770             :    else
     771      185056 :      macro_name = DmSubStr( start, s );
     772             : 
     773             :    /* If we don't have to expand the macro we're done. */
     774      922254 :    if (!doexpand) {
     775       46764 :      *ps = s;
     776       46764 :      DB_RETURN(macro_name);
     777             :    }
     778             : 
     779             :    /* Check to see if the macro name contains spaces, if so then treat it
     780             :     * as a GNU style function invocation and call the function mapper to
     781             :     * deal with it.  We do not call the function expander if the function
     782             :     * invocation begins with a '$' */
     783      875490 :    if( fflag && *macro_name != '$' ) {
     784       29592 :      result = Exec_function(macro_name);
     785             :    }
     786             :    else {
     787             :      /* Check if the macro is a recursive macro name, if so then
     788             :       * EXPAND the name before expanding the value */
     789      845898 :      if( strchr( macro_name, '$' ) != NIL(char) ) {
     790        2486 :        recurse_name = Expand( macro_name );
     791        2486 :        FREE( macro_name );
     792        2486 :        macro_name = recurse_name;
     793             :      }
     794             : 
     795             :      /* Code to do value expansion goes here, NOTE:  macros whose assign bit
     796             :         is one have been evaluated and assigned, they contain no further
     797             :         expansions and thus do not need their values expanded again. */
     798             : 
     799      845898 :      if( (hp = GET_MACRO( macro_name )) != NIL(HASH) ) {
     800      765856 :        if( hp->ht_flag & M_MARK )
     801           0 :          Fatal( "Detected circular macro [%s]", hp->ht_name );
     802             : 
     803      765856 :        if( !(hp->ht_flag & M_EXPANDED) ) {
     804      287028 :          hp->ht_flag |= M_MARK;
     805      287028 :          result = Expand( hp->ht_value );
     806      287028 :          hp->ht_flag ^= M_MARK;
     807             :        }
     808      478828 :        else if( hp->ht_value != NIL(char) )
     809      393278 :          result = DmStrDup( hp->ht_value );
     810             :        else
     811       85550 :          result = DmStrDup( "" );
     812             : 
     813             :      }
     814             :      else {
     815             :        /* The use of an undefined macro implicitly defines it but
     816             :         * leaves its value to NIL(char). */
     817       80042 :        hp = Def_macro( macro_name, NIL(char), M_EXPANDED );
     818             :        /* Setting M_INIT assures that this macro is treated unset like
     819             :         * default internal macros. (Necessary for *= and *:=) */
     820       80042 :        hp->ht_flag |= M_INIT;
     821             : 
     822       80042 :        result = DmStrDup( "" );
     823             :      }
     824             :      /* Mark macros as used only if we are not expanding them for
     825             :       * the purpose of a .IF test, so we can warn about redef after use*/
     826      845898 :      if( !If_expand ) hp->ht_flag |= M_USED;
     827             : 
     828             :    }
     829             : 
     830      875490 :    if( mflag ) {
     831             :      char separator;
     832       31112 :      int  modifier_list = 0;
     833       31112 :      int  aug_mod       = FALSE;
     834             :      char *pat1;
     835             :      char *pat2;
     836             :      char *p;
     837             : 
     838             :      /* We are inside of a macro expansion. The "build up macro name,
     839             :       * find its while loop above should have caught all \<nl> and
     840             :       * converted them to a real space. Let's verify this. */
     841      371882 :      for( p=s; *p && *p != edelim && *p; p++ ) {
     842      340770 :        if( p[0] == '\\' && p[1] == '\n' ) {
     843             :          size_t len;
     844           0 :          p[1] = ' ';
     845           0 :          len = strlen(p+1)+1;
     846           0 :          memmove( p, p+1, len );
     847             :        }
     848             :      }
     849       31112 :      if( !*p )
     850           0 :        Fatal( "Syntax error in macro modifier pattern [$%s]. The closing bracket [%c] is missing.\n", start, edelim );
     851             : 
     852             :      /* Yet another brain damaged AUGMAKE kludge.  We should accept the
     853             :       * AUGMAKE bullshit of $(f:pat=sub) form of macro expansion.  In
     854             :       * order to do this we will forgo the normal processing if the
     855             :       * AUGMAKE solution pans out, otherwise we will try to process the
     856             :       * modifiers ala dmake.
     857             :       *
     858             :       * So we look for = in modifier string.
     859             :       * If found we process it and not do the normal stuff */
     860             : 
     861       31112 :      for( p=s; *p && *p != '=' && *p != edelim; p++ );
     862             : 
     863       31112 :      if( *p == '=' ) {
     864             :        char *tmp;
     865             : 
     866           0 :        pat1 = Expand(tmp = DmSubStr(s,p)); FREE(tmp);
     867           0 :        s = p+1;
     868           0 :        p = _scan_ballanced_parens(s+1, edelim);
     869             : 
     870           0 :        if ( !*p ) {
     871           0 :          Fatal( "Incomplete macro expression [%s]", s );
     872           0 :          p = s+1;
     873             :        }
     874           0 :        pat2 = Expand(tmp = DmSubStr(s,p)); FREE(tmp);
     875             : 
     876           0 :        result = Apply_edit( result, pat1, pat2, TRUE, TRUE );
     877           0 :        FREE( pat1 );
     878           0 :        FREE( pat2 );
     879           0 :        s = p;
     880           0 :        aug_mod = TRUE;
     881             :      }
     882             : 
     883       31112 :      if( !aug_mod )
     884      183108 :        while( *s && *s != edelim ) {        /* while not at end of macro */
     885             :          char switch_char;
     886             : 
     887      120884 :          switch( switch_char = *s++ ) {
     888           0 :          case '1': modifier_list |= JUST_FIRST_FLAG;         break;
     889             : 
     890             :          case 'b':
     891       17940 :          case 'B': modifier_list |= FILE_FLAG;         break;
     892             : 
     893             :          case 'd':
     894        4534 :          case 'D': modifier_list |= DIRECTORY_FLAG;        break;
     895             : 
     896             :          case 'f':
     897        4570 :          case 'F': modifier_list |= FILE_FLAG | SUFFIX_FLAG; break;
     898             : 
     899             :          case 'e':
     900       17878 :          case 'E': modifier_list |= SUFFIX_FLAG; break;
     901             : 
     902             :          case 'l':
     903           0 :          case 'L': modifier_list |= TOLOWER_FLAG; break;
     904             : 
     905             :          case 'i':
     906           0 :          case 'I': modifier_list |= INFNAME_FLAG; break;
     907             : 
     908             :          case 'u':
     909           0 :          case 'U': modifier_list |= TOUPPER_FLAG; break;
     910             : 
     911             :          case 'm':
     912             :          case 'M':
     913           0 :            if( modifier_list || ( (*s != edelim) && (*s != ':') ) ) {
     914           0 :              Warning( "Map escape modifier must appear alone, ignored");
     915           0 :              modifier_list = 0;
     916             :            }
     917             :            else {
     918             :              /* map the escape codes in the separator string first */
     919           0 :              for(p=result; (p = strchr(p,ESCAPE_CHAR)) != NIL(char); p++)
     920           0 :                Map_esc( p );
     921             :            }
     922             :          /* find the end of the macro spec, or the start of a new
     923             :           * modifier list for further processing of the result */
     924             : 
     925           0 :          for( ; (*s != edelim) && (*s != ':') && *s; s++ );
     926           0 :          if( !*s )
     927           0 :            Fatal( "Syntax error in macro. [$%s].\n", start );
     928           0 :          if( *s == ':' ) s++;
     929           0 :          break;
     930             : 
     931             :          case 'n':
     932           0 :          case 'N': modifier_list |= NORMPATH_FLAG; break;
     933             : 
     934             :          case 'S':
     935             :          case 's':
     936       39872 :            if( modifier_list ) {
     937           0 :              Warning( "Edit modifier must appear alone, ignored");
     938           0 :              modifier_list = 0;
     939             :            }
     940             :            else {
     941       39872 :              separator = *s++;
     942       39872 :              for( p=s; *p != separator && *p; p++ );
     943             : 
     944       39872 :              if( !*p )
     945           0 :                Fatal( "Syntax error in subst macro. [$%s].\n", start );
     946             :              else {
     947             :                char *t1, *t2;
     948       39872 :                pat1 = DmSubStr( s, p );
     949       39872 :                for(s=p=p+1; (*p != separator) && *p; p++ );
     950             :                /* Before the parsing fixes in iz36027 the :s macro modifier
     951             :                 * erroneously worked with patterns with missing pattern
     952             :                 * separator, i.e. $(XXX:s#pat#sub). This is an error because
     953             :                 * it prohibits the use of following macro modifiers.
     954             :                 * I.e. $(XXX:s#pat#sub:u) falsely replaces with "sub:u".
     955             :                 * ??? Remove this special case once OOo compiles without
     956             :                 * any of this warnings. */
     957       39872 :                if( !*p ) {
     958           0 :                  if( *(p-1) == edelim ) {
     959           0 :                    p--;
     960           0 :                    Warning( "Syntax error in subst macro. Bracket found, but third delimiter [%c] missing in [$%s].\n", separator, start );
     961             :                  }
     962             :                  else {
     963           0 :                    Fatal( "Syntax error in subst macro. Third delimiter [%c] missing in [$%s].\n", separator, start );
     964             :                  }
     965             :                }
     966       39872 :                pat2 = DmSubStr( s, p );
     967       39872 :                t1 = Expand(pat1); FREE(pat1);
     968       39872 :                t2 = Expand(pat2); FREE(pat2);
     969       39872 :                result = Apply_edit( result, t1, t2, TRUE, FALSE );
     970       39872 :                FREE( t1 );
     971       39872 :                FREE( t2 );
     972             :              }
     973       39872 :              s = p;
     974             :            }
     975             :          /* find the end of the macro spec, or the start of a new
     976             :           * modifier list for further processing of the result */
     977             : 
     978       39872 :          for( ; (*s != edelim) && (*s != ':') && *s; s++ );
     979       39872 :          if( !*s )
     980           0 :            Fatal( "Syntax error in macro. [$%s].\n", start );
     981       39872 :          if( *s == ':' ) s++;
     982       39872 :          break;
     983             : 
     984             :          case 'T':
     985             :          case 't':
     986             :          case '^':
     987             :          case '+':
     988         328 :            if( modifier_list ) {
     989           0 :              Warning( "Tokenize modifier must appear alone, ignored");
     990           0 :              modifier_list = 0;
     991             :            }
     992             :            else {
     993         328 :              separator = *s++;
     994             : 
     995         328 :              if( separator == '$' ) {
     996           0 :                p = _scan_ballanced_parens(s,'\0');
     997             : 
     998           0 :                if ( *p ) {
     999             :                  char *tmp;
    1000           0 :                  pat1 = Expand(tmp = DmSubStr(s-1,p));
    1001           0 :                  FREE(tmp);
    1002           0 :                  result = Tokenize(result, pat1, switch_char, TRUE);
    1003           0 :                  FREE(pat1);
    1004             :                }
    1005             :                else {
    1006           0 :                  Warning( "Incomplete macro expression [%s]", s );
    1007             :                }
    1008           0 :                s = p;
    1009             :              }
    1010         328 :              else if ( separator == '\"' ) {
    1011             :                /* we change the semantics to allow $(v:t")") */
    1012        3104 :                for (p = s; *p && *p != separator; p++)
    1013        2776 :                  if (*p == '\\')
    1014           0 :                    if (p[1] == '\\' || p[1] == '"')
    1015           0 :                      p++;
    1016             : 
    1017         328 :                if( *p == 0 )
    1018           0 :                  Fatal( "Unterminated separator string" );
    1019             :                else {
    1020         328 :                  pat1 = DmSubStr( s, p );
    1021         328 :                  result = Tokenize( result, pat1, switch_char, TRUE);
    1022         328 :                  FREE( pat1 );
    1023             :                }
    1024         328 :                s = p;
    1025             :              }
    1026             :              else {
    1027           0 :                Warning(
    1028             :                        "Separator must be a quoted string or macro expression");
    1029             :              }
    1030             : 
    1031             :              /* find the end of the macro spec, or the start of a new
    1032             :               * modifier list for further processing of the result */
    1033             : 
    1034         328 :              for( ; (*s != edelim) && (*s != ':'); s++ );
    1035         328 :              if( *s == ':' ) s++;
    1036             :            }
    1037         328 :          break;
    1038             : 
    1039             :          case ':':
    1040       35762 :            if( modifier_list ) {
    1041       35762 :              result = Apply_modifiers( modifier_list, result );
    1042       35762 :              modifier_list = 0;
    1043             :            }
    1044       35762 :            break;
    1045             : 
    1046             :          default:
    1047           0 :            Warning( "Illegal modifier in macro, ignored" );
    1048           0 :            break;
    1049             :          }
    1050             :        }
    1051             : 
    1052       31112 :      if( modifier_list ) /* apply modifier */
    1053        9160 :        result = Apply_modifiers( modifier_list, result );
    1054             : 
    1055       31112 :      s++;
    1056             :    }
    1057             : 
    1058      875490 :    *ps = s;
    1059      875490 :    FREE( macro_name );
    1060      875490 :    DB_RETURN( result );
    1061             : }
    1062             : 
    1063             : 
    1064             : static char*
    1065       19020 : _scan_brace( s, ps, flag )/*
    1066             : ============================
    1067             :       This routine scans for { token_list } pairs.  It expands the value of
    1068             :       token_list by calling Expand on it.  Token_list may be anything at all.
    1069             :       Note that the routine count's ballanced parentheses.  This means you
    1070             :       cannot have something like { fred { joe }, if that is what you really
    1071             :       need the write it as { fred {{ joe }, flag is set to 1 if all ok
    1072             :       and to 0 if the braces were unballanced. */
    1073             : 
    1074             : char *s;
    1075             : char **ps;
    1076             : int  *flag;
    1077             : {
    1078             :   char *t;
    1079             :   char *start;
    1080             :   char *res;
    1081       19020 :   int  lev  = 1;
    1082       19020 :   int  done = 0;
    1083             : 
    1084             :   DB_ENTER( "_scan_brace" );
    1085             : 
    1086       19020 :   start = s;
    1087      886158 :   while( !done )
    1088      848118 :     switch( *s++ ) {
    1089             :     case '{':
    1090           0 :       if( *s == '{' ) break;              /* ignore {{ */
    1091           0 :       lev++;
    1092           0 :       break;
    1093             : 
    1094             :     case '}':
    1095       19020 :       if( *s == '}' ) break;              /* ignore }} */
    1096       19020 :       if( lev )
    1097       19020 :         if( --lev == 0 ) done = TRUE;
    1098       19020 :       break;
    1099             : 
    1100             :     case '$':
    1101       75226 :       if( *s == '{' || *s == '}' ) {
    1102           0 :         if( (t = strchr(s,'}')) != NIL(char) )
    1103           0 :           s = t;
    1104           0 :         s++;
    1105             :       }
    1106       75226 :       break;
    1107             : 
    1108             :     case '\0':
    1109           0 :       if( lev ) {
    1110           0 :         done = TRUE;
    1111           0 :         s--;
    1112             :         /* error malformed macro expansion */
    1113             :       }
    1114           0 :       break;
    1115             :     }
    1116             : 
    1117       19020 :   start = DmSubStr( start, (lev) ? s : s-1 );
    1118             : 
    1119       19020 :   if( lev ) {
    1120             :     /* Braces were not ballanced so just return the string.
    1121             :      * Do not expand it. */
    1122             : 
    1123           0 :     res   = DmStrJoin( "{", start, -1, FALSE );
    1124           0 :     *flag = 0;
    1125             :   }
    1126             :   else {
    1127       19020 :     *flag = 1;
    1128       19020 :     res   = Expand( start );
    1129             : 
    1130       19020 :     if( (t = DmStrSpn( res, " \t" )) != res ) {
    1131           0 :       size_t len = strlen(t)+1;
    1132           0 :       memmove( res, t, len );
    1133             :     }
    1134             :   }
    1135             : 
    1136       19020 :   FREE( start );       /* this is ok! start is assigned a DmSubStr above */
    1137       19020 :   *ps = s;
    1138             : 
    1139       19020 :   DB_RETURN( res );
    1140             : }
    1141             : 
    1142             : 
    1143             : static char*
    1144       19422 : _cross_prod( x, y )/*
    1145             : =====================
    1146             :       Given two strings x and y compute the cross-product of the tokens found
    1147             :       in each string.  ie. if x = "a b" and y = "c d" return "ac ad bc bd".
    1148             : 
    1149             :          NOTE:  buf will continue to grow until it is big enough to handle
    1150             :                 all cross product requests.  It is never freed!  (maybe I
    1151             :             will fix this someday) */
    1152             : 
    1153             : char *x;
    1154             : char *y;
    1155             : {
    1156             :   static char *buf = NULL;
    1157             :   static int  buf_siz = 0;
    1158             :   char *brkx;
    1159             :   char *brky;
    1160             :   char *cy;
    1161             :   char *cx;
    1162             :   char *res;
    1163             :   int  i;
    1164             : 
    1165       19422 :   if( *x && *y ) {
    1166         604 :     res = DmStrDup( "" ); cx = x;
    1167        2756 :     while( *cx ) {
    1168        1548 :       cy = y;
    1169        1548 :       brkx = DmStrPbrk( cx, " \t\n" );
    1170        1548 :       if( (brkx-cx == 2) && *cx == '\"' && *(cx+1) == '\"' ) cx = brkx;
    1171             : 
    1172        5082 :       while( *cy ) {
    1173        1986 :         brky = DmStrPbrk( cy, " \t\n" );
    1174        1986 :         if( (brky-cy == 2) && *cy == '\"' && *(cy+1) == '\"' ) cy = brky;
    1175        1986 :         i    = brkx-cx + brky-cy + 2;
    1176             : 
    1177        1986 :         if( i > buf_siz ) {     /* grow buf to the correct size */
    1178         470 :           if( buf != NIL(char) ) FREE( buf );
    1179         470 :           if( (buf = MALLOC( i, char )) == NIL(char))  No_ram();
    1180         470 :           buf_siz = i;
    1181             :         }
    1182             : 
    1183        1986 :         strncpy( buf, cx, (i = brkx-cx) );
    1184        1986 :         buf[i] = '\0';
    1185        1986 :         if (brky-cy > 0) strncat( buf, cy, brky-cy );
    1186        1986 :         buf[i+(brky-cy)] = '\0';
    1187        1986 :         strcat( buf, " " );
    1188        1986 :         res = DmStrJoin( res, buf, -1, TRUE );
    1189        1986 :         cy = DmStrSpn( brky, " \t\n" );
    1190             :       }
    1191        1548 :       cx = DmStrSpn( brkx, " \t\n" );
    1192             :     }
    1193             : 
    1194         604 :     FREE( x );
    1195         604 :     res[ strlen(res)-1 ] = '\0';
    1196             :   }
    1197             :   else
    1198       18818 :     res = DmStrJoin( x, y, -1, TRUE );
    1199             : 
    1200       19422 :   FREE( y );
    1201       19422 :   return( res );
    1202             : }

Generated by: LCOV version 1.10