4 -- GNU style functions for dmake.
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.
12 -- Dennis Vadura, dvadura@dmake.wticorp.com
15 -- http://dmake.wticorp.com/
18 -- Copyright (c) 1996,1997 by WTI Corp. All rights reserved.
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.
25 -- Use cvs log to obtain detailed change logs.
30 static char *_exec_mktmp
ANSI((char *, char *, char *));
31 static char *_exec_subst
ANSI((char *, char *, char *));
32 static char *_exec_iseq
ANSI((char *, char *, char *, int));
33 static char *_exec_sort
ANSI((char *));
34 static char *_exec_echo
ANSI((char *));
35 static char *_exec_uniq
ANSI((char *));
36 static char *_exec_shell
ANSI((char *, int));
37 static char *_exec_call
ANSI((char *, char *));
38 static char *_exec_assign
ANSI((char *));
39 static char *_exec_foreach
ANSI((char *, char *, char *));
40 static char *_exec_andor
ANSI((char *, int));
41 static char *_exec_not
ANSI((char *));
42 static int _mystrcmp
ANSI((const DMPVOID
, const DMPVOID
));
48 Execute the function given by the value of args.
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. */
58 char *mod2
= NIL(char);
60 char *res
= NIL(char);
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
));
69 fname
= DmSubStr(buf
, args
);
70 /* args points to the whitespace after the found token, this leads
71 * to leading whitespaces. */
73 args
= DmStrSpn(args
," \t"); /* strip whitespace before */
74 if( *args
) { /* ... and after value */
76 for(q
=args
+strlen(args
)-1; ((*q
== ' ')||(*q
== '\t')); q
--);
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) ){
91 if( (mod2
= strchr(mod1
,',')) != NIL(char) ){
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. */
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
, TRUE
);
108 res
= _exec_call(fname
,args
);
112 if(strncmp(fname
,"eq",2) == 0)
114 res
= _exec_iseq(mod1
,mod2
,args
,TRUE
);
116 Fatal( "Two comma-seperated arguments expected in [%s].\n", buf
);
117 else if (strncmp(fname
,"echo",4) == 0)
118 res
= _exec_echo(args
);
120 res
= _exec_call(fname
,args
);
124 if(strncmp(fname
,"foreach",7) == 0)
126 res
= _exec_foreach(mod1
,mod2
,args
);
128 Fatal( "Two comma-seperated arguments expected in [%s].\n", buf
);
130 res
= _exec_call(fname
,args
);
134 if(strncmp(fname
,"mktmp",5) == 0)
136 res
= _exec_mktmp(mod1
,mod2
,args
);
138 Fatal( "Maximal two comma-seperated arguments expected in [%s].\n", buf
);
140 res
= _exec_call(fname
,args
);
144 if( strncmp(fname
,"null", 4) == 0 )
145 res
= _exec_iseq(mod1
,NIL(char),args
,TRUE
);
146 else if (strncmp(fname
,"nil",3) == 0 ) {
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
);
155 if( mod_count
== 0 ) {
156 res
= exec_normpath(eargs
);
158 else if( mod_count
== 1 ) {
159 char *para
= Expand(mod1
);
160 int tmpUseWinpath
= UseWinpath
;
162 if( !*para
|| strcmp(para
, "\"\"") == 0 ) {
167 res
= exec_normpath(eargs
);
168 UseWinpath
= tmpUseWinpath
;
172 Fatal( "One or no comma-seperated arguments expected in [%s].\n", buf
);
177 res
= _exec_call(fname
,args
);
181 if(strncmp(fname
,"!null",5) == 0)
182 res
= _exec_iseq(mod1
,NIL(char),args
,FALSE
);
183 else if(strncmp(fname
,"!eq",3) ==0)
185 res
= _exec_iseq(mod1
,mod2
,args
,FALSE
);
187 Fatal( "Two comma-seperated arguments expected in [%s].\n", buf
);
189 res
= _exec_call(fname
,args
);
193 if(strncmp(fname
,"or",2) == 0)
194 res
= _exec_andor(args
, FALSE
);
196 res
= _exec_call(fname
,args
);
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
, FALSE
);
206 else if( mod_count
== 1 ) {
207 char *emod
= Expand(mod1
);
208 if(strncmp(emod
,"expand",7)==0)
209 res
= _exec_shell(args
, TRUE
);
211 Fatal( "Unknown argument [%s] to shell in [%s].\n", emod
, buf
);
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',TRUE
);
218 else if(strncmp(fname
,"subst",5)==0) {
220 res
= _exec_subst(mod1
,mod2
,args
);
222 Fatal( "Two comma-seperated arguments expected in [%s].\n", buf
);
225 res
= _exec_call(fname
,args
);
229 if(strncmp(fname
,"uniq",4) == 0)
230 res
= _exec_uniq(args
);
232 res
= _exec_call(fname
,args
);
236 res
= _exec_call(fname
,args
);
239 if( res
== NIL(char) ) res
= DmStrDup("");
247 _exec_assign( macrostring
)
250 if ( !Parse_macro(macrostring
, M_MULTI
|M_FORCE
) ) {
251 Error( "Dynamic macro assignment failed, while making [%s]\n",
252 Current_target
? Current_target
->CE_NAME
: "NIL");
253 return(DmStrDup(""));
256 return(DmStrDup(LastMacName
));
264 return(DmStrDup(DmStrSpn(data
," \t")));
269 _exec_call( var
, list
)/*
270 =========================
271 Return the (recursively expanded) value of macro var. Expand list and
274 char *var
; /* Name of the macro (until first whitespace). */
275 char *list
; /* Rest data (after the whitespace). */
277 char *res
= NIL(char);
279 /* the argument part is expanded. */
282 /* Prepend '$(' and append ')' so that Expand will return the value
283 * of the 'var' macro. */
284 var
= DmStrJoin(DmStrJoin("$(",var
,-1,FALSE
),")",-1,TRUE
);
293 _exec_foreach( var
, list
, data
)
298 char *res
= NIL(char);
306 data
= DmStrSpn(data
," \t\n");
308 /* push previous macro definition and redefine. */
309 hp
= Def_macro(var
,"",M_MULTI
|M_NOEXPORT
|M_FORCE
|M_PUSH
);
311 while( *(s
=Get_token(&tk
, "", FALSE
)) != '\0' ) {
312 Def_macro(var
,s
,M_MULTI
|M_NOEXPORT
|M_FORCE
);
313 res
= DmStrAdd(res
,Expand(data
),TRUE
);
317 Pop_macro(hp
); /* Get back old macro definition. */
319 if(hp
->ht_value
) FREE(hp
->ht_value
);
329 _exec_mktmp( file
, text
, data
)
336 FILE *tmpfile
= NIL(FILE);
338 /* This is only a test of the recipe line so prevent the tempfile side
340 if( Suppress_temp_file
) return(NIL(char));
342 name
= Current_target
? Current_target
->CE_NAME
:"makefile text";
344 if( file
&& *file
) {
345 /* Expand the file parameter to mktmp if present. */
346 tmpname
= Expand(file
);
350 /* Only use umask if we are also using mkstemp - this basically
351 * avoids using the incompatible implementation from MSVC. */
354 /* Create tempfile with 600 permissions. */
358 if( (tmpfile
= fopen(tmpname
, "w")) == NIL(FILE) )
359 Open_temp_error( tmpname
, name
);
364 Def_macro("TMPFILE", tmpname
, M_EXPANDED
|M_MULTI
);
365 Link_temp( Current_target
, tmpfile
, tmpname
);
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(). */
374 /* If file expanded to a non empty value tmpfile is already opened,
375 * otherwise open it now. */
377 tmpfile
= Start_temp( "", Current_target
, &tmpname
);
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
));
391 Append_line( data
, TRUE
, tmpfile
, name
, FALSE
, FALSE
);
392 Close_temp( Current_target
, tmpfile
);
400 _exec_iseq( lhs
, rhs
, data
, eq
)
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);
411 int val
= strcmp(l
,r
);
413 if( (!val
&& eq
) || (val
&& !eq
) ) {
414 if( e
!= NIL(char) ) *e
= '\0';
417 else if( e
!= NIL(char) ) {
418 e
= DmStrSpn(e
," \t\n");
419 if( *e
) res
= Expand(e
);
432 char *res
= NIL(char);
433 char *data
= Expand(args
);
436 char *white
= " \t\n";
440 for(i
=0,p
=DmStrSpn(data
,white
);*p
;p
=DmStrSpn(DmStrPbrk(p
,white
),white
),i
++);
443 TALLOC(tokens
, i
, char *);
445 for( i
=0,p
=DmStrSpn(data
,white
); *p
; p
=DmStrSpn(p
,white
),i
++){
447 p
= DmStrPbrk(p
,white
);
448 if( *p
) *p
++ = '\0';
451 qsort( tokens
, i
, sizeof(char *), _mystrcmp
);
453 for( j
=0; j
<i
; j
++ ) res
= DmStrApp(res
, tokens
[j
]);
466 char *res
= NIL(char);
467 char *data
= Expand(args
);
471 char *white
= " \t\n";
477 for(i
=0,p
=DmStrSpn(data
,white
);*p
;p
=DmStrSpn(DmStrPbrk(p
,white
),white
),i
++);
480 TALLOC(tokens
, i
, char *);
481 TALLOC(tokens_after
, i
, char *);
483 for( i
=0,p
=DmStrSpn(data
,white
); *p
; p
=DmStrSpn(p
,white
),i
++){
485 p
= DmStrPbrk(p
,white
);
486 if( *p
) *p
++ = '\0';
489 qsort( tokens
, i
, sizeof(char *), _mystrcmp
);
491 for( j
=0; j
<i
; j
++ ) {
492 if (strcmp(tokens
[j
], last
) != 0) {
493 tokens_after
[k
++] = tokens
[j
];
498 for( j
=0; j
<k
; j
++ ) res
= DmStrApp(res
, tokens_after
[j
]);
512 return(strcmp(*((const char **)p
),*((const char **)q
)));
517 _exec_subst( pat
, subst
, data
)
525 subst
= Expand(subst
);
527 /* This implies FREE(Expand(data)) */
528 res
= Apply_edit( Expand(data
), pat
, subst
, TRUE
, FALSE
);
537 _exec_shell( data
, expand
)/*
538 =============================
539 Capture the stdout of an execuded command.
540 If expand is TRUE expand the result. */
544 extern char *tempnam();
548 FILE *old_stdout_redir
= stdout_redir
;
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
;
555 char *res
= NIL(char);
560 if( Suppress_temp_file
) return(NIL(char));
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
566 register char *s
= (char *) &cell
;
567 register int n
= sizeof(CELL
);
568 while( n
) { *s
++ = '\0'; n
--; }
570 rcp
.st_string
= DmStrSpn(data
, " \t+-%@");
571 rcp
.st_attr
= Rcp_attribute( data
);
572 rcp
.st_next
= NIL(STRING
);
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
);
577 cell
.ce_all
.cl_flag
= 0;
578 cell
.ce_fname
= cname
.ht_name
;
579 cell
.ce_recipe
= &rcp
;
580 cell
.ce_flag
= F_TARGET
|F_RULES
;
581 /* Setting A_SILENT supresses the recipe output from Print_cmnd(). */
582 cell
.ce_attr
= A_PHONY
|A_SILENT
|A_SHELLESC
;
584 if( Measure
& M_TARGET
)
585 Do_profile_output( "s", M_TARGET
, &cell
);
587 /* Print the shell escape command. */
588 if( Verbose
& V_FORCEECHO
) {
589 printf( "%s: Executing shell macro: %s\n", Pname
, data
);
593 if( (stdout_redir
= Get_temp(&tmpnm
, "w+")) == NIL(FILE) )
594 Open_temp_error( tmpnm
, cname
.ht_name
);
596 bsize
= (Buffer_size
< BUFSIZ
)?BUFSIZ
:Buffer_size
;
597 buffer
= MALLOC(bsize
,char);
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
= TRUE
;
606 Is_exec_shell
= TRUE
;
607 Shell_exec_target
= Current_target
;
608 Verbose
&= V_LEAVE_TMP
;
611 /* The actual redirection happens in runargv(). */
612 Exec_commands( &cell
);
614 Unlink_temp_files( &cell
);
618 Wait_for_completion
= wait
;
619 Is_exec_shell
= old_is_exec_shell
;
620 Shell_exec_target
= old_Shell_exec_target
;
622 /* Now we have to read the temporary file, get the tokens and return them
624 rewind(stdout_redir
);
625 while( fgets(buffer
, bsize
, stdout_redir
) ) {
626 char *p
= strchr(buffer
, '\n');
629 res
= DmStrJoin(res
,buffer
,-1,TRUE
);
632 /* You might encounter '\r\n' on windows, handle it. */
633 if( p
> buffer
&& *(p
-1) == '\r')
635 res
= DmStrApp(res
,buffer
);
639 fclose(stdout_redir
);
644 stdout_redir
= old_stdout_redir
;
648 exp_res
= Expand(res
);
658 _exec_andor( args
, doand
)
664 char *white
= " \t\n";
667 args
= DmStrSpn(args
,white
);
669 p
=ScanToken(args
, &next
, TRUE
);
671 if (doand
? !*p
: *p
) {
679 while (*(args
=DmStrSpn(next
,white
)));
681 return(res
? DmStrDup("t") : DmStrDup(""));
689 char *white
= " \t\n";
690 char *p
=Expand(args
);
691 int res
= (*DmStrSpn(p
,white
) == '\0');
694 return(res
? DmStrDup("t") : DmStrDup(""));
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. */
709 /* This honors .WINPATH . */
710 SET_TOKEN( &str
, args
);
712 while( *(s
= Get_token( &str
, "", FALSE
)) != '\0' ) {
713 if(str
.tk_quote
== 0) {
714 /* Add leading quote. */
715 res
= DmStrApp(res
, "\"");
716 res
= DmStrJoin(res
, DO_WINPATH(normalize_path(s
)), -1, TRUE
);
717 /* Append the trailing quote. */
718 res
= DmStrJoin(res
, "\"", 1, TRUE
);
720 res
= DmStrApp(res
, DO_WINPATH(normalize_path(s
)));