| File: | dmake/function.c |
| Location: | line 493, column 9 |
| Description: | Array access (from variable 'tokens_after') results in a null pointer dereference |
| 1 | /* | |||
| 2 | -- | |||
| 3 | -- SYNOPSIS | |||
| 4 | -- GNU style functions for dmake. | |||
| 5 | -- | |||
| 6 | -- DESCRIPTION | |||
| 7 | -- All GNU style functions understood by dmake are implemented in this | |||
| 8 | -- file. Currently the only such function is $(mktmp ...) which is | |||
| 9 | -- not part of GNU-make is an extension provided by dmake. | |||
| 10 | -- | |||
| 11 | -- AUTHOR | |||
| 12 | -- Dennis Vadura, dvadura@dmake.wticorp.com | |||
| 13 | -- | |||
| 14 | -- WWW | |||
| 15 | -- http://dmake.wticorp.com/ | |||
| 16 | -- | |||
| 17 | -- COPYRIGHT | |||
| 18 | -- Copyright (c) 1996,1997 by WTI Corp. All rights reserved. | |||
| 19 | -- | |||
| 20 | -- This program is NOT free software; you can redistribute it and/or | |||
| 21 | -- modify it under the terms of the Software License Agreement Provided | |||
| 22 | -- in the file <distribution-root>/readme/license.txt. | |||
| 23 | -- | |||
| 24 | -- LOG | |||
| 25 | -- Use cvs log to obtain detailed change logs. | |||
| 26 | */ | |||
| 27 | ||||
| 28 | #include "extern.h" | |||
| 29 | ||||
| 30 | static char *_exec_mktmp ANSI((char *, char *, char *))(char *, char *, char *); | |||
| 31 | static char *_exec_subst ANSI((char *, char *, char *))(char *, char *, char *); | |||
| 32 | static char *_exec_iseq ANSI((char *, char *, char *, int))(char *, char *, char *, int); | |||
| 33 | static char *_exec_sort ANSI((char *))(char *); | |||
| 34 | static char *_exec_echo ANSI((char *))(char *); | |||
| 35 | static char *_exec_uniq ANSI((char *))(char *); | |||
| 36 | static char *_exec_shell ANSI((char *, int))(char *, int); | |||
| 37 | static char *_exec_call ANSI((char *, char *))(char *, char *); | |||
| 38 | static char *_exec_assign ANSI((char *))(char *); | |||
| 39 | static char *_exec_foreach ANSI((char *, char *, char *))(char *, char *, char *); | |||
| 40 | static char *_exec_andor ANSI((char *, int))(char *, int); | |||
| 41 | static char *_exec_not ANSI((char *))(char *); | |||
| 42 | static int _mystrcmp ANSI((const DMPVOID, const DMPVOID))(const void *, const void *); | |||
| 43 | ||||
| 44 | ||||
| 45 | PUBLIC char * | |||
| 46 | Exec_function(buf)/* | |||
| 47 | ==================== | |||
| 48 | Execute the function given by the value of args. | |||
| 49 | ||||
| 50 | So far mktmp is the only valid function, anything else elicits and error | |||
| 51 | message. It is my hope to support the GNU style functions in this portion | |||
| 52 | of the code at some time in the future. */ | |||
| 53 | char *buf; | |||
| 54 | { | |||
| 55 | char *fname; | |||
| 56 | char *args; | |||
| 57 | char *mod1; | |||
| 58 | char *mod2 = NIL(char)((char*)((void*)0)); | |||
| 59 | int mod_count = 0; | |||
| 60 | char *res = NIL(char)((char*)((void*)0)); | |||
| 61 | ||||
| 62 | /* This must succeed since the presence of ' ', \t or \n is what | |||
| 63 | * determines if this function is called in the first place. | |||
| 64 | * Unfortunately this prohibits the use of whitespaces in parameters | |||
| 65 | * for macro functions. */ | |||
| 66 | /* ??? Using ScanToken to find the next ' ', \t or \n and discarding | |||
| 67 | * the returned, evaluated result is a misuse of that function. */ | |||
| 68 | FREE(ScanToken(buf, &args, FALSE))free((char*)(ScanToken(buf, &args, 0))); | |||
| 69 | fname = DmSubStr(buf, args); | |||
| 70 | /* args points to the whitespace after the found token, this leads | |||
| 71 | * to leading whitespaces. */ | |||
| 72 | if( *args ) { | |||
| ||||
| 73 | args = DmStrSpn(args," \t"); /* strip whitespace before */ | |||
| 74 | if( *args ) { /* ... and after value */ | |||
| 75 | char *q; | |||
| 76 | for(q=args+strlen(args)-1; ((*q == ' ')||(*q == '\t')); q--); | |||
| 77 | *++q = '\0'; | |||
| 78 | } | |||
| 79 | } | |||
| 80 | ||||
| 81 | /* ??? Some function macros expect comma seperated parameters, but | |||
| 82 | * no decent parser is included. The desirable solution would be | |||
| 83 | * to parse fname for the correct number of parameters in fname | |||
| 84 | * when a function is recognized. We only count the parameters | |||
| 85 | * at the moment. Note "" is a valid parameter. */ | |||
| 86 | if( (mod1 = strchr(fname,',')) != NIL(char)((char*)((void*)0)) ){ | |||
| 87 | *mod1 = '\0'; | |||
| 88 | mod1++; | |||
| 89 | mod_count++; | |||
| 90 | ||||
| 91 | if( (mod2 = strchr(mod1,',')) != NIL(char)((char*)((void*)0)) ){ | |||
| 92 | *mod2 = '\0'; | |||
| 93 | mod2++; | |||
| 94 | mod_count++; | |||
| 95 | } | |||
| 96 | } | |||
| 97 | ||||
| 98 | /* ??? At the moment only the leading part of fname compared if it | |||
| 99 | * matches a known function macro. For example assignXXX or even | |||
| 100 | * assign,,,, is also erroneously accepted. */ | |||
| 101 | switch( *fname ) { | |||
| 102 | case 'a': | |||
| 103 | if(strncmp(fname,"assign",6) == 0) | |||
| 104 | res = _exec_assign(args); | |||
| 105 | else if(strncmp(fname,"and",3) == 0) | |||
| 106 | res = _exec_andor(args, TRUE1); | |||
| 107 | else | |||
| 108 | res = _exec_call(fname,args); | |||
| 109 | break; | |||
| 110 | ||||
| 111 | case 'e': | |||
| 112 | if(strncmp(fname,"eq",2) == 0) | |||
| 113 | if( mod_count == 2 ) | |||
| 114 | res = _exec_iseq(mod1,mod2,args,TRUE1); | |||
| 115 | else | |||
| 116 | Fatal( "Two comma-seperated arguments expected in [%s].\n", buf ); | |||
| 117 | else if (strncmp(fname,"echo",4) == 0) | |||
| 118 | res = _exec_echo(args); | |||
| 119 | else | |||
| 120 | res = _exec_call(fname,args); | |||
| 121 | break; | |||
| 122 | ||||
| 123 | case 'f': | |||
| 124 | if(strncmp(fname,"foreach",7) == 0) | |||
| 125 | if( mod_count == 2 ) | |||
| 126 | res = _exec_foreach(mod1,mod2,args); | |||
| 127 | else | |||
| 128 | Fatal( "Two comma-seperated arguments expected in [%s].\n", buf ); | |||
| 129 | else | |||
| 130 | res = _exec_call(fname,args); | |||
| 131 | break; | |||
| 132 | ||||
| 133 | case 'm': | |||
| 134 | if(strncmp(fname,"mktmp",5) == 0) | |||
| 135 | if( mod_count < 3 ) | |||
| 136 | res = _exec_mktmp(mod1,mod2,args); | |||
| 137 | else | |||
| 138 | Fatal( "Maximal two comma-seperated arguments expected in [%s].\n", buf ); | |||
| 139 | else | |||
| 140 | res = _exec_call(fname,args); | |||
| 141 | break; | |||
| 142 | ||||
| 143 | case 'n': | |||
| 144 | if( strncmp(fname,"null", 4) == 0 ) | |||
| 145 | res = _exec_iseq(mod1,NIL(char)((char*)((void*)0)),args,TRUE1); | |||
| 146 | else if (strncmp(fname,"nil",3) == 0 ) { | |||
| 147 | FREE(Expand(args))free((char*)(Expand(args))); | |||
| 148 | res = DmStrDup(""); | |||
| 149 | } | |||
| 150 | else if (strncmp(fname,"not",3) == 0 ) | |||
| 151 | res = _exec_not(args); | |||
| 152 | else if (strncmp(fname,"normpath",8) == 0 ) { | |||
| 153 | char *eargs = Expand(args); | |||
| 154 | ||||
| 155 | if( mod_count == 0 ) { | |||
| 156 | res = exec_normpath(eargs); | |||
| 157 | } | |||
| 158 | else if( mod_count == 1 ) { | |||
| 159 | char *para = Expand(mod1); | |||
| 160 | int tmpUseWinpath = UseWinpath; | |||
| 161 | ||||
| 162 | if( !*para || strcmp(para, "\"\"") == 0 ) { | |||
| 163 | UseWinpath = FALSE0; | |||
| 164 | } else { | |||
| 165 | UseWinpath = TRUE1; | |||
| 166 | } | |||
| 167 | res = exec_normpath(eargs); | |||
| 168 | UseWinpath = tmpUseWinpath; | |||
| 169 | FREE(para)free((char*)(para)); | |||
| 170 | } | |||
| 171 | else | |||
| 172 | Fatal( "One or no comma-seperated arguments expected in [%s].\n", buf ); | |||
| 173 | ||||
| 174 | FREE(eargs)free((char*)(eargs)); | |||
| 175 | } | |||
| 176 | else | |||
| 177 | res = _exec_call(fname,args); | |||
| 178 | break; | |||
| 179 | ||||
| 180 | case '!': | |||
| 181 | if(strncmp(fname,"!null",5) == 0) | |||
| 182 | res = _exec_iseq(mod1,NIL(char)((char*)((void*)0)),args,FALSE0); | |||
| 183 | else if(strncmp(fname,"!eq",3) ==0) | |||
| 184 | if( mod_count == 2 ) | |||
| 185 | res = _exec_iseq(mod1,mod2,args,FALSE0); | |||
| 186 | else | |||
| 187 | Fatal( "Two comma-seperated arguments expected in [%s].\n", buf ); | |||
| 188 | else | |||
| 189 | res = _exec_call(fname,args); | |||
| 190 | break; | |||
| 191 | ||||
| 192 | case 'o': | |||
| 193 | if(strncmp(fname,"or",2) == 0) | |||
| 194 | res = _exec_andor(args, FALSE0); | |||
| 195 | else | |||
| 196 | res = _exec_call(fname,args); | |||
| 197 | break; | |||
| 198 | ||||
| 199 | case 's': | |||
| 200 | if(strncmp(fname,"sort",4) == 0) | |||
| 201 | res = _exec_sort(args); | |||
| 202 | else if(strncmp(fname,"shell",5)==0) | |||
| 203 | if( mod_count == 0 ) { | |||
| 204 | res = _exec_shell(args, FALSE0); | |||
| 205 | } | |||
| 206 | else if( mod_count == 1 ) { | |||
| 207 | char *emod = Expand(mod1); | |||
| 208 | if(strncmp(emod,"expand",7)==0) | |||
| 209 | res = _exec_shell(args, TRUE1); | |||
| 210 | else | |||
| 211 | Fatal( "Unknown argument [%s] to shell in [%s].\n", emod, buf ); | |||
| 212 | FREE(emod)free((char*)(emod)); | |||
| 213 | } | |||
| 214 | else | |||
| 215 | Fatal( "One or no comma-seperated arguments expected in [%s].\n", buf ); | |||
| 216 | else if(strncmp(fname,"strip",5)==0) | |||
| 217 | res = Tokenize(Expand(args)," ",'t',TRUE1); | |||
| 218 | else if(strncmp(fname,"subst",5)==0) { | |||
| 219 | if( mod_count == 2 ) | |||
| 220 | res = _exec_subst(mod1,mod2,args); | |||
| 221 | else | |||
| 222 | Fatal( "Two comma-seperated arguments expected in [%s].\n", buf ); | |||
| 223 | } | |||
| 224 | else | |||
| 225 | res = _exec_call(fname,args); | |||
| 226 | break; | |||
| 227 | ||||
| 228 | case 'u': | |||
| 229 | if(strncmp(fname,"uniq",4) == 0) | |||
| 230 | res = _exec_uniq(args); | |||
| 231 | else | |||
| 232 | res = _exec_call(fname,args); | |||
| 233 | break; | |||
| 234 | ||||
| 235 | default: | |||
| 236 | res = _exec_call(fname,args); | |||
| 237 | } | |||
| 238 | ||||
| 239 | if( res == NIL(char)((char*)((void*)0)) ) res = DmStrDup(""); | |||
| 240 | ||||
| 241 | FREE(fname)free((char*)(fname)); | |||
| 242 | return(res); | |||
| 243 | } | |||
| 244 | ||||
| 245 | ||||
| 246 | static char * | |||
| 247 | _exec_assign( macrostring ) | |||
| 248 | char *macrostring; | |||
| 249 | { | |||
| 250 | if ( !Parse_macro(macrostring, M_MULTI0x0004|M_FORCE0x0080) ) { | |||
| 251 | Error( "Dynamic macro assignment failed, while making [%s]\n", | |||
| 252 | Current_target ? Current_target->CE_NAMEce_name->ht_name : "NIL"); | |||
| 253 | return(DmStrDup("")); | |||
| 254 | } | |||
| 255 | ||||
| 256 | return(DmStrDup(LastMacName)); | |||
| 257 | } | |||
| 258 | ||||
| 259 | ||||
| 260 | static char * | |||
| 261 | _exec_echo(data) | |||
| 262 | char *data; | |||
| 263 | { | |||
| 264 | return(DmStrDup(DmStrSpn(data," \t"))); | |||
| 265 | } | |||
| 266 | ||||
| 267 | ||||
| 268 | static char * | |||
| 269 | _exec_call( var, list )/* | |||
| 270 | ========================= | |||
| 271 | Return the (recursively expanded) value of macro var. Expand list and | |||
| 272 | discard the result. | |||
| 273 | */ | |||
| 274 | char *var; /* Name of the macro (until first whitespace). */ | |||
| 275 | char *list; /* Rest data (after the whitespace). */ | |||
| 276 | { | |||
| 277 | char *res = NIL(char)((char*)((void*)0)); | |||
| 278 | ||||
| 279 | /* the argument part is expanded. */ | |||
| 280 | FREE(Expand(list))free((char*)(Expand(list))); | |||
| 281 | ||||
| 282 | /* Prepend '$(' and append ')' so that Expand will return the value | |||
| 283 | * of the 'var' macro. */ | |||
| 284 | var = DmStrJoin(DmStrJoin("$(",var,-1,FALSE0),")",-1,TRUE1); | |||
| 285 | res = Expand(var); | |||
| 286 | ||||
| 287 | FREE(var)free((char*)(var)); | |||
| 288 | return(res); | |||
| 289 | } | |||
| 290 | ||||
| 291 | ||||
| 292 | static char * | |||
| 293 | _exec_foreach( var, list, data ) | |||
| 294 | char *var; | |||
| 295 | char *list; | |||
| 296 | char *data; | |||
| 297 | { | |||
| 298 | char *res = NIL(char)((char*)((void*)0)); | |||
| 299 | char *s; | |||
| 300 | TKSTR tk; | |||
| 301 | HASHPTR hp; | |||
| 302 | ||||
| 303 | var = Expand(var); | |||
| 304 | list = Expand(list); | |||
| 305 | ||||
| 306 | data = DmStrSpn(data," \t\n"); | |||
| 307 | SET_TOKEN(&tk,list)(&tk)->tk_str = (list); (&tk)->tk_cchar = *(list ); (&tk)->tk_quote = 1;; | |||
| 308 | /* push previous macro definition and redefine. */ | |||
| 309 | hp = Def_macro(var,"",M_MULTI0x0004|M_NOEXPORT0x0040|M_FORCE0x0080|M_PUSH0x0100); | |||
| 310 | ||||
| 311 | while( *(s=Get_token(&tk, "", FALSE0)) != '\0' ) { | |||
| 312 | Def_macro(var,s,M_MULTI0x0004|M_NOEXPORT0x0040|M_FORCE0x0080); | |||
| 313 | res = DmStrAdd(res,Expand(data),TRUE1); | |||
| 314 | } | |||
| 315 | ||||
| 316 | CLEAR_TOKEN(&tk)*(&tk)->tk_str = (&tk)->tk_cchar; | |||
| 317 | Pop_macro(hp); /* Get back old macro definition. */ | |||
| 318 | FREE(hp->ht_name)free((char*)(hp->ht_name)); | |||
| 319 | if(hp->ht_value) FREE(hp->ht_value)free((char*)(hp->ht_value)); | |||
| 320 | FREE(hp)free((char*)(hp)); | |||
| 321 | FREE(var)free((char*)(var)); | |||
| 322 | FREE(list)free((char*)(list)); | |||
| 323 | ||||
| 324 | return(res); | |||
| 325 | } | |||
| 326 | ||||
| 327 | ||||
| 328 | static char * | |||
| 329 | _exec_mktmp( file, text, data ) | |||
| 330 | char *file; | |||
| 331 | char *text; | |||
| 332 | char *data; | |||
| 333 | { | |||
| 334 | char *tmpname; | |||
| 335 | char *name; | |||
| 336 | FILE *tmpfile = NIL(FILE)((FILE*)((void*)0)); | |||
| 337 | ||||
| 338 | /* This is only a test of the recipe line so prevent the tempfile side | |||
| 339 | * effects. */ | |||
| 340 | if( Suppress_temp_file ) return(NIL(char)((char*)((void*)0))); | |||
| 341 | ||||
| 342 | name = Current_target ? Current_target->CE_NAMEce_name->ht_name:"makefile text"; | |||
| 343 | ||||
| 344 | if( file && *file ) { | |||
| 345 | /* Expand the file parameter to mktmp if present. */ | |||
| 346 | tmpname = Expand(file); | |||
| 347 | ||||
| 348 | if( *tmpname ) { | |||
| 349 | #ifdef HAVE_MKSTEMP1 | |||
| 350 | /* Only use umask if we are also using mkstemp - this basically | |||
| 351 | * avoids using the incompatible implementation from MSVC. */ | |||
| 352 | mode_t mask; | |||
| 353 | ||||
| 354 | /* Create tempfile with 600 permissions. */ | |||
| 355 | mask = umask(0066); | |||
| 356 | #endif | |||
| 357 | ||||
| 358 | if( (tmpfile = fopen(tmpname, "w")) == NIL(FILE)((FILE*)((void*)0)) ) | |||
| 359 | Open_temp_error( tmpname, name ); | |||
| 360 | #ifdef HAVE_MKSTEMP1 | |||
| 361 | umask(mask); | |||
| 362 | #endif | |||
| 363 | ||||
| 364 | Def_macro("TMPFILE", tmpname, M_EXPANDED0x0008|M_MULTI0x0004); | |||
| 365 | Link_temp( Current_target, tmpfile, tmpname ); | |||
| 366 | ||||
| 367 | /* Don't free tmpname if it is used. It is stored in a FILELIST | |||
| 368 | * member in Link_temp() and freed by Unlink_temp_files(). */ | |||
| 369 | } | |||
| 370 | else | |||
| 371 | FREE(tmpname)free((char*)(tmpname)); | |||
| 372 | } | |||
| 373 | ||||
| 374 | /* If file expanded to a non empty value tmpfile is already opened, | |||
| 375 | * otherwise open it now. */ | |||
| 376 | if( !tmpfile ) | |||
| 377 | tmpfile = Start_temp( "", Current_target, &tmpname ); | |||
| 378 | ||||
| 379 | /* If the text parameter is given return its expanded value | |||
| 380 | * instead of the used filename. */ | |||
| 381 | if( !text || !*text ) { | |||
| 382 | /* tmpname is freed by Unlink_temp_files(). */ | |||
| 383 | text = DmStrDup(DO_WINPATH(tmpname)tmpname); | |||
| 384 | } | |||
| 385 | else { | |||
| 386 | text = Expand(text); | |||
| 387 | } | |||
| 388 | ||||
| 389 | data = Expand(data); | |||
| 390 | ||||
| 391 | Append_line( data, TRUE1, tmpfile, name, FALSE0, FALSE0 ); | |||
| 392 | Close_temp( Current_target, tmpfile ); | |||
| 393 | FREE(data)free((char*)(data)); | |||
| 394 | ||||
| 395 | return( text ); | |||
| 396 | } | |||
| 397 | ||||
| 398 | ||||
| 399 | static char * | |||
| 400 | _exec_iseq( lhs, rhs, data, eq ) | |||
| 401 | char *lhs; | |||
| 402 | char *rhs; | |||
| 403 | char *data; | |||
| 404 | int eq; | |||
| 405 | { | |||
| 406 | char *l = Expand(lhs); | |||
| 407 | char *r = Expand(rhs); | |||
| 408 | char *i = DmStrSpn(data, " \t\n"); | |||
| 409 | char *e = strchr(i, ' '); | |||
| 410 | char *res = NIL(char)((char*)((void*)0)); | |||
| 411 | int val = strcmp(l,r); | |||
| 412 | ||||
| 413 | if( (!val && eq) || (val && !eq) ) { | |||
| 414 | if( e != NIL(char)((char*)((void*)0)) ) *e = '\0'; | |||
| 415 | res = Expand(i); | |||
| 416 | } | |||
| 417 | else if( e != NIL(char)((char*)((void*)0)) ) { | |||
| 418 | e = DmStrSpn(e," \t\n"); | |||
| 419 | if( *e ) res = Expand(e); | |||
| 420 | } | |||
| 421 | ||||
| 422 | FREE(l)free((char*)(l)); | |||
| 423 | FREE(r)free((char*)(r)); | |||
| 424 | return(res); | |||
| 425 | } | |||
| 426 | ||||
| 427 | ||||
| 428 | static char * | |||
| 429 | _exec_sort( args ) | |||
| 430 | char *args; | |||
| 431 | { | |||
| 432 | char *res = NIL(char)((char*)((void*)0)); | |||
| 433 | char *data = Expand(args); | |||
| 434 | char **tokens; | |||
| 435 | char *p; | |||
| 436 | char *white = " \t\n"; | |||
| 437 | int j; | |||
| 438 | int i; | |||
| 439 | ||||
| 440 | for(i=0,p=DmStrSpn(data,white);*p;p=DmStrSpn(DmStrPbrk(p,white),white),i++); | |||
| 441 | ||||
| 442 | if( i != 0 ) { | |||
| 443 | TALLOC(tokens, i, char *)if ((tokens = (char **) calloc((unsigned int)(i), (size_t)sizeof (char *))) == (char **)0) {No_ram();}; | |||
| 444 | ||||
| 445 | for( i=0,p=DmStrSpn(data,white); *p; p=DmStrSpn(p,white),i++){ | |||
| 446 | tokens[i] = p; | |||
| 447 | p = DmStrPbrk(p,white); | |||
| 448 | if( *p ) *p++ = '\0'; | |||
| 449 | } | |||
| 450 | ||||
| 451 | qsort( tokens, i, sizeof(char *), _mystrcmp ); | |||
| 452 | ||||
| 453 | for( j=0; j<i; j++ ) res = DmStrApp(res, tokens[j]); | |||
| 454 | FREE(data)free((char*)(data)); | |||
| 455 | FREE(tokens)free((char*)(tokens)); | |||
| 456 | } | |||
| 457 | ||||
| 458 | return(res); | |||
| 459 | } | |||
| 460 | ||||
| 461 | ||||
| 462 | static char * | |||
| 463 | _exec_uniq( args ) | |||
| 464 | char *args; | |||
| 465 | { | |||
| 466 | char *res = NIL(char)((char*)((void*)0)); | |||
| 467 | char *data = Expand(args); | |||
| 468 | char **tokens; | |||
| 469 | char **tokens_after; | |||
| 470 | char *p; | |||
| 471 | char *white = " \t\n"; | |||
| 472 | int j; | |||
| 473 | int i; | |||
| 474 | char *last = ""; | |||
| 475 | int k = 0; | |||
| 476 | ||||
| 477 | for(i=0,p=DmStrSpn(data,white);*p;p=DmStrSpn(DmStrPbrk(p,white),white),i++); | |||
| 478 | ||||
| 479 | if( i != 0 ) { | |||
| 480 | TALLOC(tokens, i, char *)if ((tokens = (char **) calloc((unsigned int)(i), (size_t)sizeof (char *))) == (char **)0) {No_ram();}; | |||
| 481 | TALLOC(tokens_after, i, char *)if ((tokens_after = (char **) calloc((unsigned int)(i), (size_t )sizeof(char *))) == (char **)0) {No_ram();}; | |||
| 482 | ||||
| 483 | for( i=0,p=DmStrSpn(data,white); *p; p=DmStrSpn(p,white),i++){ | |||
| 484 | tokens[i] = p; | |||
| 485 | p = DmStrPbrk(p,white); | |||
| 486 | if( *p ) *p++ = '\0'; | |||
| 487 | } | |||
| 488 | ||||
| 489 | qsort( tokens, i, sizeof(char *), _mystrcmp ); | |||
| 490 | ||||
| 491 | for( j=0; j<i; j++ ) { | |||
| 492 | if (strcmp(tokens[j], last) != 0) { | |||
| 493 | tokens_after[k++] = tokens[j]; | |||
| ||||
| 494 | last = tokens[j]; | |||
| 495 | } | |||
| 496 | } | |||
| 497 | ||||
| 498 | for( j=0; j<k; j++ ) res = DmStrApp(res, tokens_after[j]); | |||
| 499 | FREE(tokens)free((char*)(tokens)); | |||
| 500 | FREE(tokens_after)free((char*)(tokens_after)); | |||
| 501 | } | |||
| 502 | ||||
| 503 | FREE(data)free((char*)(data)); | |||
| 504 | return(res); | |||
| 505 | } | |||
| 506 | ||||
| 507 | static int | |||
| 508 | _mystrcmp( p, q ) | |||
| 509 | const DMPVOIDvoid * p; | |||
| 510 | const DMPVOIDvoid * q; | |||
| 511 | { | |||
| 512 | return(strcmp(*((const char **)p),*((const char **)q))); | |||
| 513 | } | |||
| 514 | ||||
| 515 | ||||
| 516 | static char * | |||
| 517 | _exec_subst( pat, subst, data ) | |||
| 518 | char *pat; | |||
| 519 | char *subst; | |||
| 520 | char *data; | |||
| 521 | { | |||
| 522 | char *res; | |||
| 523 | ||||
| 524 | pat = Expand(pat); | |||
| 525 | subst = Expand(subst); | |||
| 526 | ||||
| 527 | /* This implies FREE(Expand(data)) */ | |||
| 528 | res = Apply_edit( Expand(data), pat, subst, TRUE1, FALSE0 ); | |||
| 529 | FREE(pat)free((char*)(pat)); | |||
| 530 | FREE(subst)free((char*)(subst)); | |||
| 531 | ||||
| 532 | return(res); | |||
| 533 | } | |||
| 534 | ||||
| 535 | ||||
| 536 | static char * | |||
| 537 | _exec_shell( data, expand )/* | |||
| 538 | ============================= | |||
| 539 | Capture the stdout of an execuded command. | |||
| 540 | If expand is TRUE expand the result. */ | |||
| 541 | char *data; | |||
| 542 | int expand; | |||
| 543 | { | |||
| 544 | extern char *tempnam(); | |||
| 545 | int bsize; | |||
| 546 | char *buffer; | |||
| 547 | char *tmpnm; | |||
| 548 | FILE *old_stdout_redir = stdout_redir; | |||
| 549 | ||||
| 550 | int wait = Wait_for_completion; | |||
| 551 | int old_is_exec_shell = Is_exec_shell; | |||
| 552 | CELLPTR old_Shell_exec_target = Shell_exec_target; | |||
| 553 | uint16 vflag = Verbose; | |||
| 554 | int tflag = Trace; | |||
| 555 | char *res = NIL(char)((char*)((void*)0)); | |||
| 556 | CELL cell; | |||
| 557 | STRING rcp; | |||
| 558 | HASH cname; | |||
| 559 | ||||
| 560 | if( Suppress_temp_file ) return(NIL(char)((char*)((void*)0))); | |||
| 561 | ||||
| 562 | /* Set the temp CELL used for building prerequisite candidates to | |||
| 563 | * all zero so that we don't have to keep initializing all the | |||
| 564 | * fields. */ | |||
| 565 | { | |||
| 566 | register char *s = (char *) &cell; | |||
| 567 | register int n = sizeof(CELL); | |||
| 568 | while( n ) { *s++ = '\0'; n--; } | |||
| 569 | } | |||
| 570 | rcp.st_string = DmStrSpn(data, " \t+-%@"); | |||
| 571 | rcp.st_attr = Rcp_attribute( data ); | |||
| 572 | rcp.st_next = NIL(STRING)((STRING*)((void*)0)); | |||
| 573 | cname.ht_name = "Shell escape"; | |||
| 574 | cell.ce_name = &cname; | |||
| 575 | cell.ce_all.cl_prq = &cell; | |||
| 576 | cell.ce_all.cl_next = NIL(LINK)((LINK*)((void*)0)); | |||
| 577 | cell.ce_all.cl_flag = 0; | |||
| 578 | cell.ce_fname = cname.ht_name; | |||
| 579 | cell.ce_recipe = &rcp; | |||
| 580 | cell.ce_flag = F_TARGET0x0008|F_RULES0x0010; | |||
| 581 | /* Setting A_SILENT supresses the recipe output from Print_cmnd(). */ | |||
| 582 | cell.ce_attr = A_PHONY0x04000|A_SILENT0x00002|A_SHELLESC0x40000000; | |||
| 583 | ||||
| 584 | if( Measure & M_TARGET0x01 ) | |||
| 585 | Do_profile_output( "s", M_TARGET0x01, &cell ); | |||
| 586 | ||||
| 587 | /* Print the shell escape command. */ | |||
| 588 | if( Verbose & V_FORCEECHO0x80 ) { | |||
| 589 | printf( "%s: Executing shell macro: %s\n", Pname, data ); | |||
| 590 | fflush(stdoutstdout); | |||
| 591 | } | |||
| 592 | ||||
| 593 | if( (stdout_redir = Get_temp(&tmpnm, "w+")) == NIL(FILE)((FILE*)((void*)0)) ) | |||
| 594 | Open_temp_error( tmpnm, cname.ht_name ); | |||
| 595 | ||||
| 596 | bsize = (Buffer_size < BUFSIZ8192)?BUFSIZ8192:Buffer_size; | |||
| 597 | buffer = MALLOC(bsize,char)(char*) malloc((unsigned int)(bsize)*(size_t)sizeof(char)); | |||
| 598 | ||||
| 599 | /* As this function redirects the output of stdout we have to make sure | |||
| 600 | * that only this single command is executed and all previous recipe lines | |||
| 601 | * that belong to the same target have finished. With Shell_exec_target and | |||
| 602 | * Wait_for_completion set this is realized. Current_target being NIL(CELL) | |||
| 603 | * outside of recipe lines makes sure that no waiting for previous recipe | |||
| 604 | * lines has to be done. */ | |||
| 605 | Wait_for_completion = TRUE1; | |||
| 606 | Is_exec_shell = TRUE1; | |||
| 607 | Shell_exec_target = Current_target; | |||
| 608 | Verbose &= V_LEAVE_TMP0x01; | |||
| 609 | Trace = FALSE0; | |||
| 610 | ||||
| 611 | /* The actual redirection happens in runargv(). */ | |||
| 612 | Exec_commands( &cell ); | |||
| 613 | ||||
| 614 | Unlink_temp_files( &cell ); | |||
| 615 | ||||
| 616 | Trace = tflag; | |||
| 617 | Verbose = vflag; | |||
| 618 | Wait_for_completion = wait; | |||
| 619 | Is_exec_shell = old_is_exec_shell; | |||
| 620 | Shell_exec_target = old_Shell_exec_target; | |||
| 621 | ||||
| 622 | /* Now we have to read the temporary file, get the tokens and return them | |||
| 623 | * as a string. */ | |||
| 624 | rewind(stdout_redir); | |||
| 625 | while( fgets(buffer, bsize, stdout_redir) ) { | |||
| 626 | char *p = strchr(buffer, '\n'); | |||
| 627 | ||||
| 628 | if( p == NIL(char)((char*)((void*)0)) ) | |||
| 629 | res = DmStrJoin(res,buffer,-1,TRUE1); | |||
| 630 | else { | |||
| 631 | *p = '\0'; | |||
| 632 | /* You might encounter '\r\n' on windows, handle it. */ | |||
| 633 | if( p > buffer && *(p-1) == '\r') | |||
| 634 | *(p-1) = '\0'; | |||
| 635 | res = DmStrApp(res,buffer); | |||
| 636 | } | |||
| 637 | } | |||
| 638 | ||||
| 639 | fclose(stdout_redir); | |||
| 640 | Remove_file(tmpnm); | |||
| 641 | FREE(tmpnm)free((char*)(tmpnm)); | |||
| 642 | FREE(buffer)free((char*)(buffer)); | |||
| 643 | ||||
| 644 | stdout_redir = old_stdout_redir; | |||
| 645 | ||||
| 646 | if ( expand ) { | |||
| 647 | char *exp_res; | |||
| 648 | exp_res = Expand(res); | |||
| 649 | FREE(res)free((char*)(res)); | |||
| 650 | res = exp_res; | |||
| 651 | } | |||
| 652 | ||||
| 653 | return(res); | |||
| 654 | } | |||
| 655 | ||||
| 656 | ||||
| 657 | static char * | |||
| 658 | _exec_andor( args, doand ) | |||
| 659 | char *args; | |||
| 660 | int doand; | |||
| 661 | { | |||
| 662 | char *next; | |||
| 663 | char *p; | |||
| 664 | char *white = " \t\n"; | |||
| 665 | int res=doand; | |||
| 666 | ||||
| 667 | args = DmStrSpn(args,white); | |||
| 668 | do { | |||
| 669 | p=ScanToken(args, &next, TRUE1); | |||
| 670 | ||||
| 671 | if (doand ? !*p : *p) { | |||
| 672 | res = !doand; | |||
| 673 | FREE(p)free((char*)(p)); | |||
| 674 | break; | |||
| 675 | } | |||
| 676 | ||||
| 677 | FREE(p)free((char*)(p)); | |||
| 678 | } | |||
| 679 | while (*(args=DmStrSpn(next,white))); | |||
| 680 | ||||
| 681 | return(res ? DmStrDup("t") : DmStrDup("")); | |||
| 682 | } | |||
| 683 | ||||
| 684 | ||||
| 685 | static char * | |||
| 686 | _exec_not( args ) | |||
| 687 | char *args; | |||
| 688 | { | |||
| 689 | char *white = " \t\n"; | |||
| 690 | char *p=Expand(args); | |||
| 691 | int res = (*DmStrSpn(p,white) == '\0'); | |||
| 692 | ||||
| 693 | FREE(p)free((char*)(p)); | |||
| 694 | return(res ? DmStrDup("t") : DmStrDup("")); | |||
| 695 | } | |||
| 696 | ||||
| 697 | ||||
| 698 | char * | |||
| 699 | exec_normpath( args )/* | |||
| 700 | ======================= | |||
| 701 | Normalize token-wise. The normalised filenames are returned in a new | |||
| 702 | string, the original string is not freed. Quoted tokens remain quoted | |||
| 703 | after the normalizaton. */ | |||
| 704 | char *args; | |||
| 705 | { | |||
| 706 | TKSTR str; | |||
| 707 | char *s, *res; | |||
| 708 | ||||
| 709 | /* This honors .WINPATH . */ | |||
| 710 | SET_TOKEN( &str, args )(&str)->tk_str = (args); (&str)->tk_cchar = *(args ); (&str)->tk_quote = 1;; | |||
| 711 | res = NIL(char)((char*)((void*)0)); | |||
| 712 | while( *(s = Get_token( &str, "", FALSE0 )) != '\0' ) { | |||
| 713 | if(str.tk_quote == 0) { | |||
| 714 | /* Add leading quote. */ | |||
| 715 | res = DmStrApp(res, "\""); | |||
| 716 | res = DmStrJoin(res, DO_WINPATH(normalize_path(s))normalize_path(s), -1, TRUE1); | |||
| 717 | /* Append the trailing quote. */ | |||
| 718 | res = DmStrJoin(res, "\"", 1, TRUE1); | |||
| 719 | } else { | |||
| 720 | res = DmStrApp(res, DO_WINPATH(normalize_path(s))normalize_path(s)); | |||
| 721 | } | |||
| 722 | } | |||
| 723 | return res; | |||
| 724 | } |