1 /* $RCSfile: function.c,v $
3 -- last change: $Author: kz $ $Date: 2008-03-05 18:28:45 $
6 -- GNU style functions for dmake.
9 -- All GNU style functions understood by dmake are implemented in this
10 -- file. Currently the only such function is $(mktmp ...) which is
11 -- not part of GNU-make is an extension provided by dmake.
14 -- Dennis Vadura, dvadura@dmake.wticorp.com
17 -- http://dmake.wticorp.com/
20 -- Copyright (c) 1996,1997 by WTI Corp. All rights reserved.
22 -- This program is NOT free software; you can redistribute it and/or
23 -- modify it under the terms of the Software License Agreement Provided
24 -- in the file <distribution-root>/readme/license.txt.
27 -- Use cvs log to obtain detailed change logs.
32 static char *_exec_mktmp
ANSI((char *, char *, char *));
33 static char *_exec_subst
ANSI((char *, char *, char *));
34 static char *_exec_iseq
ANSI((char *, char *, char *, int));
35 static char *_exec_sort
ANSI((char *));
36 static char *_exec_echo
ANSI((char *));
37 static char *_exec_uniq
ANSI((char *));
38 static char *_exec_shell
ANSI((char *, int));
39 static char *_exec_call
ANSI((char *, char *));
40 static char *_exec_assign
ANSI((char *));
41 static char *_exec_foreach
ANSI((char *, char *, char *));
42 static char *_exec_andor
ANSI((char *, int));
43 static char *_exec_not
ANSI((char *));
44 static int _mystrcmp
ANSI((const DMPVOID
, const DMPVOID
));
50 Execute the function given by the value of args.
52 So far mktmp is the only valid function, anything else elicits and error
53 message. It is my hope to support the GNU style functions in this portion
54 of the code at some time in the future. */
60 char *mod2
= NIL(char);
62 char *res
= NIL(char);
64 /* This must succeed since the presence of ' ', \t or \n is what
65 * determines if this function is called in the first place.
66 * Unfortunately this prohibits the use of whitespaces in parameters
67 * for macro functions. */
68 /* ??? Using ScanToken to find the next ' ', \t or \n and discarding
69 * the returned, evaluated result is a misuse of that function. */
70 FREE(ScanToken(buf
, &args
, FALSE
));
71 fname
= DmSubStr(buf
, args
);
72 /* args points to the whitespace after the found token, this leads
73 * to leading whitespaces. */
75 args
= DmStrSpn(args
," \t"); /* strip whitespace before */
76 if( *args
) { /* ... and after value */
78 for(q
=args
+strlen(args
)-1; ((*q
== ' ')||(*q
== '\t')); q
--);
83 /* ??? Some function macros expect comma seperated parameters, but
84 * no decent parser is included. The desirable solution would be
85 * to parse fname for the correct number of parameters in fname
86 * when a function is recognized. We only count the parameters
87 * at the moment. Note "" is a valid parameter. */
88 if( (mod1
= strchr(fname
,',')) != NIL(char) ){
93 if( (mod2
= strchr(mod1
,',')) != NIL(char) ){
100 /* ??? At the moment only the leading part of fname compared if it
101 * matches a known function macro. For example assignXXX or even
102 * assign,,,, is also erroneously accepted. */
105 if(strncmp(fname
,"assign",6) == 0)
106 res
= _exec_assign(args
);
107 else if(strncmp(fname
,"and",3) == 0)
108 res
= _exec_andor(args
, TRUE
);
110 res
= _exec_call(fname
,args
);
114 if(strncmp(fname
,"eq",2) == 0)
116 res
= _exec_iseq(mod1
,mod2
,args
,TRUE
);
118 Fatal( "Two comma-seperated arguments expected in [%s].\n", buf
);
119 else if (strncmp(fname
,"echo",4) == 0)
120 res
= _exec_echo(args
);
122 res
= _exec_call(fname
,args
);
126 if(strncmp(fname
,"foreach",7) == 0)
128 res
= _exec_foreach(mod1
,mod2
,args
);
130 Fatal( "Two comma-seperated arguments expected in [%s].\n", buf
);
132 res
= _exec_call(fname
,args
);
136 if(strncmp(fname
,"mktmp",5) == 0)
138 res
= _exec_mktmp(mod1
,mod2
,args
);
140 Fatal( "Maximal two comma-seperated arguments expected in [%s].\n", buf
);
142 res
= _exec_call(fname
,args
);
146 if( strncmp(fname
,"null", 4) == 0 )
147 res
= _exec_iseq(mod1
,NIL(char),args
,TRUE
);
148 else if (strncmp(fname
,"nil",3) == 0 ) {
152 else if (strncmp(fname
,"not",3) == 0 )
153 res
= _exec_not(args
);
154 else if (strncmp(fname
,"normpath",8) == 0 ) {
155 char *eargs
= Expand(args
);
157 if( mod_count
== 0 ) {
158 res
= exec_normpath(eargs
);
160 else if( mod_count
== 1 ) {
161 char *para
= Expand(mod1
);
162 int tmpUseWinpath
= UseWinpath
;
164 if( !*para
|| strcmp(para
, "\"\"") == 0 ) {
169 res
= exec_normpath(eargs
);
170 UseWinpath
= tmpUseWinpath
;
174 Fatal( "One or no comma-seperated arguments expected in [%s].\n", buf
);
179 res
= _exec_call(fname
,args
);
183 if(strncmp(fname
,"!null",5) == 0)
184 res
= _exec_iseq(mod1
,NIL(char),args
,FALSE
);
185 else if(strncmp(fname
,"!eq",3) ==0)
187 res
= _exec_iseq(mod1
,mod2
,args
,FALSE
);
189 Fatal( "Two comma-seperated arguments expected in [%s].\n", buf
);
191 res
= _exec_call(fname
,args
);
195 if(strncmp(fname
,"or",2) == 0)
196 res
= _exec_andor(args
, FALSE
);
198 res
= _exec_call(fname
,args
);
202 if(strncmp(fname
,"sort",4) == 0)
203 res
= _exec_sort(args
);
204 else if(strncmp(fname
,"shell",5)==0)
205 if( mod_count
== 0 ) {
206 res
= _exec_shell(args
, FALSE
);
208 else if( mod_count
== 1 ) {
209 char *emod
= Expand(mod1
);
210 if(strncmp(emod
,"expand",7)==0)
211 res
= _exec_shell(args
, TRUE
);
213 Fatal( "Unknown argument [%s] to shell in [%s].\n", emod
, buf
);
217 Fatal( "One or no comma-seperated arguments expected in [%s].\n", buf
);
218 else if(strncmp(fname
,"strip",5)==0)
219 res
= Tokenize(Expand(args
)," ",'t',TRUE
);
220 else if(strncmp(fname
,"subst",5)==0) {
222 res
= _exec_subst(mod1
,mod2
,args
);
224 Fatal( "Two comma-seperated arguments expected in [%s].\n", buf
);
227 res
= _exec_call(fname
,args
);
231 if(strncmp(fname
,"uniq",4) == 0)
232 res
= _exec_uniq(args
);
234 res
= _exec_call(fname
,args
);
238 res
= _exec_call(fname
,args
);
241 if( res
== NIL(char) ) res
= DmStrDup("");
249 _exec_assign( macrostring
)
252 if ( !Parse_macro(macrostring
, M_MULTI
|M_FORCE
) ) {
253 Error( "Dynamic macro assignment failed, while making [%s]\n",
254 Current_target
? Current_target
->CE_NAME
: "NIL");
255 return(DmStrDup(""));
258 return(DmStrDup(LastMacName
));
266 return(DmStrDup(DmStrSpn(data
," \t")));
271 _exec_call( var
, list
)/*
272 =========================
273 Return the (recursively expanded) value of macro var. Expand list and
276 char *var
; /* Name of the macro (until first whitespace). */
277 char *list
; /* Rest data (after the whitespace). */
279 char *res
= NIL(char);
281 /* the argument part is expanded. */
284 /* Prepend '$(' and append ')' so that Expand will return the value
285 * of the 'var' macro. */
286 var
= DmStrJoin(DmStrJoin("$(",var
,-1,FALSE
),")",-1,TRUE
);
295 _exec_foreach( var
, list
, data
)
300 char *res
= NIL(char);
308 data
= DmStrSpn(data
," \t\n");
310 /* push previous macro definition and redefine. */
311 hp
= Def_macro(var
,"",M_MULTI
|M_NOEXPORT
|M_FORCE
|M_PUSH
);
313 while( *(s
=Get_token(&tk
, "", FALSE
)) != '\0' ) {
314 Def_macro(var
,s
,M_MULTI
|M_NOEXPORT
|M_FORCE
);
315 res
= DmStrAdd(res
,Expand(data
),TRUE
);
319 Pop_macro(hp
); /* Get back old macro definition. */
321 if(hp
->ht_value
) FREE(hp
->ht_value
);
331 _exec_mktmp( file
, text
, data
)
338 FILE *tmpfile
= NIL(FILE);
340 /* This is only a test of the recipe line so prevent the tempfile side
342 if( Suppress_temp_file
) return(NIL(char));
344 name
= Current_target
? Current_target
->CE_NAME
:"makefile text";
346 if( file
&& *file
) {
347 /* Expand the file parameter to mktmp if present. */
348 tmpname
= Expand(file
);
352 /* Only use umask if we are also using mkstemp - this basically
353 * avoids using the incompatible implementation from MSVC. */
356 /* Create tempfile with 600 permissions. */
360 if( (tmpfile
= fopen(tmpname
, "w")) == NIL(FILE) )
361 Open_temp_error( tmpname
, name
);
366 Def_macro("TMPFILE", tmpname
, M_EXPANDED
|M_MULTI
);
367 Link_temp( Current_target
, tmpfile
, tmpname
);
369 /* Don't free tmpname if it is used. It is stored in a FILELIST
370 * member in Link_temp() and freed by Unlink_temp_files(). */
376 /* If file expanded to a non empty value tmpfile is already opened,
377 * otherwise open it now. */
379 tmpfile
= Start_temp( "", Current_target
, &tmpname
);
381 /* If the text parameter is given return its expanded value
382 * instead of the used filename. */
383 if( !text
|| !*text
) {
384 /* tmpname is freed by Unlink_temp_files(). */
385 text
= DmStrDup(DO_WINPATH(tmpname
));
393 Append_line( data
, TRUE
, tmpfile
, name
, FALSE
, FALSE
);
394 Close_temp( Current_target
, tmpfile
);
402 _exec_iseq( lhs
, rhs
, data
, eq
)
408 char *l
= Expand(lhs
);
409 char *r
= Expand(rhs
);
410 char *i
= DmStrSpn(data
, " \t\n");
411 char *e
= strchr(i
, ' ');
412 char *res
= NIL(char);
413 int val
= strcmp(l
,r
);
415 if( (!val
&& eq
) || (val
&& !eq
) ) {
416 if( e
!= NIL(char) ) *e
= '\0';
419 else if( e
!= NIL(char) ) {
420 e
= DmStrSpn(e
," \t\n");
421 if( *e
) res
= Expand(e
);
434 char *res
= NIL(char);
435 char *data
= Expand(args
);
438 char *white
= " \t\n";
442 for(i
=0,p
=DmStrSpn(data
,white
);*p
;p
=DmStrSpn(DmStrPbrk(p
,white
),white
),i
++);
445 TALLOC(tokens
, i
, char *);
447 for( i
=0,p
=DmStrSpn(data
,white
); *p
; p
=DmStrSpn(p
,white
),i
++){
449 p
= DmStrPbrk(p
,white
);
450 if( *p
) *p
++ = '\0';
453 qsort( tokens
, i
, sizeof(char *), _mystrcmp
);
455 for( j
=0; j
<i
; j
++ ) res
= DmStrApp(res
, tokens
[j
]);
468 char *res
= NIL(char);
469 char *data
= Expand(args
);
473 char *white
= " \t\n";
479 for(i
=0,p
=DmStrSpn(data
,white
);*p
;p
=DmStrSpn(DmStrPbrk(p
,white
),white
),i
++);
482 TALLOC(tokens
, i
, char *);
483 TALLOC(tokens_after
, i
, char *);
485 for( i
=0,p
=DmStrSpn(data
,white
); *p
; p
=DmStrSpn(p
,white
),i
++){
487 p
= DmStrPbrk(p
,white
);
488 if( *p
) *p
++ = '\0';
491 qsort( tokens
, i
, sizeof(char *), _mystrcmp
);
493 for( j
=0; j
<i
; j
++ ) {
494 if (strcmp(tokens
[j
], last
) != 0) {
495 tokens_after
[k
++] = tokens
[j
];
500 for( j
=0; j
<k
; j
++ ) res
= DmStrApp(res
, tokens_after
[j
]);
514 return(strcmp(*((const char **)p
),*((const char **)q
)));
519 _exec_subst( pat
, subst
, data
)
527 subst
= Expand(subst
);
529 /* This implies FREE(Expand(data)) */
530 res
= Apply_edit( Expand(data
), pat
, subst
, TRUE
, FALSE
);
539 _exec_shell( data
, expand
)/*
540 =============================
541 Capture the stdout of an execuded command.
542 If expand is TRUE expand the result. */
546 extern char *tempnam();
550 FILE *old_stdout_redir
= stdout_redir
;
552 int wait
= Wait_for_completion
;
553 int old_is_exec_shell
= Is_exec_shell
;
554 CELLPTR old_Shell_exec_target
= Shell_exec_target
;
555 uint16 vflag
= Verbose
;
557 char *res
= NIL(char);
562 if( Suppress_temp_file
) return(NIL(char));
564 /* Set the temp CELL used for building prerequisite candidates to
565 * all zero so that we don't have to keep initializing all the
568 register char *s
= (char *) &cell
;
569 register int n
= sizeof(CELL
);
570 while( n
) { *s
++ = '\0'; n
--; }
572 rcp
.st_string
= DmStrSpn(data
, " \t+-%@");
573 rcp
.st_attr
= Rcp_attribute( data
);
574 rcp
.st_next
= NIL(STRING
);
575 cname
.ht_name
= "Shell escape";
576 cell
.ce_name
= &cname
;
577 cell
.ce_all
.cl_prq
= &cell
;
578 cell
.ce_all
.cl_next
= NIL(LINK
);
579 cell
.ce_all
.cl_flag
= 0;
580 cell
.ce_fname
= cname
.ht_name
;
581 cell
.ce_recipe
= &rcp
;
582 cell
.ce_flag
= F_TARGET
|F_RULES
;
583 /* Setting A_SILENT supresses the recipe output from Print_cmnd(). */
584 cell
.ce_attr
= A_PHONY
|A_SILENT
|A_SHELLESC
;
586 if( Measure
& M_TARGET
)
587 Do_profile_output( "s", M_TARGET
, &cell
);
589 /* Print the shell escape command. */
590 if( Verbose
& V_FORCEECHO
) {
591 printf( "%s: Executing shell macro: %s\n", Pname
, data
);
595 if( (stdout_redir
= Get_temp(&tmpnm
, "w+")) == NIL(FILE) )
596 Open_temp_error( tmpnm
, cname
.ht_name
);
598 bsize
= (Buffer_size
< BUFSIZ
)?BUFSIZ
:Buffer_size
;
599 buffer
= MALLOC(bsize
,char);
601 /* As this function redirects the output of stdout we have to make sure
602 * that only this single command is executed and all previous recipe lines
603 * that belong to the same target have finished. With Shell_exec_target and
604 * Wait_for_completion set this is realized. Current_target being NIL(CELL)
605 * outside of recipe lines makes sure that no waiting for previous recipe
606 * lines has to be done. */
607 Wait_for_completion
= TRUE
;
608 Is_exec_shell
= TRUE
;
609 Shell_exec_target
= Current_target
;
610 Verbose
&= V_LEAVE_TMP
;
613 /* The actual redirection happens in runargv(). */
614 Exec_commands( &cell
);
616 Unlink_temp_files( &cell
);
620 Wait_for_completion
= wait
;
621 Is_exec_shell
= old_is_exec_shell
;
622 Shell_exec_target
= old_Shell_exec_target
;
624 /* Now we have to read the temporary file, get the tokens and return them
626 rewind(stdout_redir
);
627 while( fgets(buffer
, bsize
, stdout_redir
) ) {
628 char *p
= strchr(buffer
, '\n');
631 res
= DmStrJoin(res
,buffer
,-1,TRUE
);
634 /* You might encounter '\r\n' on windows, handle it. */
635 if( p
> buffer
&& *(p
-1) == '\r')
637 res
= DmStrApp(res
,buffer
);
641 fclose(stdout_redir
);
646 stdout_redir
= old_stdout_redir
;
650 exp_res
= Expand(res
);
660 _exec_andor( args
, doand
)
666 char *white
= " \t\n";
669 args
= DmStrSpn(args
,white
);
671 p
=ScanToken(args
, &next
, TRUE
);
673 if (doand
? !*p
: *p
) {
681 while (*(args
=DmStrSpn(next
,white
)));
683 return(res
? DmStrDup("t") : DmStrDup(""));
691 char *white
= " \t\n";
692 char *p
=Expand(args
);
693 int res
= (*DmStrSpn(p
,white
) == '\0');
696 return(res
? DmStrDup("t") : DmStrDup(""));
701 exec_normpath( args
)/*
702 =======================
703 Normalize token-wise. The normalised filenames are returned in a new
704 string, the original string is not freed. Quoted tokens remain quoted
705 after the normalizaton. */
711 /* This honors .WINPATH . */
712 SET_TOKEN( &str
, args
);
714 while( *(s
= Get_token( &str
, "", FALSE
)) != '\0' ) {
715 if(str
.tk_quote
== 0) {
716 /* Add leading quote. */
717 res
= DmStrApp(res
, "\"");
718 res
= DmStrJoin(res
, DO_WINPATH(normalize_path(s
)), -1, TRUE
);
719 /* Append the trailing quote. */
720 res
= DmStrJoin(res
, "\"", 1, TRUE
);
722 res
= DmStrApp(res
, DO_WINPATH(normalize_path(s
)));