File: | dmake/function.c |
Location: | line 451, column 7 |
Description: | Null pointer passed as an argument to a 'nonnull' parameter |
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 | } |