update dev300-m58
[ooovba.git] / dmake / function.c
blobcf6937e02a4aa574b2964dc0217e90ce5f44d96a
1 /* $RCSfile: function.c,v $
2 -- $Revision: 1.12 $
3 -- last change: $Author: kz $ $Date: 2008-03-05 18:28:45 $
4 --
5 -- SYNOPSIS
6 -- GNU style functions for dmake.
7 --
8 -- DESCRIPTION
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.
13 -- AUTHOR
14 -- Dennis Vadura, dvadura@dmake.wticorp.com
16 -- WWW
17 -- http://dmake.wticorp.com/
19 -- COPYRIGHT
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.
26 -- LOG
27 -- Use cvs log to obtain detailed change logs.
30 #include "extern.h"
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));
47 PUBLIC char *
48 Exec_function(buf)/*
49 ====================
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. */
55 char *buf;
57 char *fname;
58 char *args;
59 char *mod1;
60 char *mod2 = NIL(char);
61 int mod_count = 0;
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. */
74 if( *args ) {
75 args = DmStrSpn(args," \t"); /* strip whitespace before */
76 if( *args ) { /* ... and after value */
77 char *q;
78 for(q=args+strlen(args)-1; ((*q == ' ')||(*q == '\t')); q--);
79 *++q = '\0';
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) ){
89 *mod1 = '\0';
90 mod1++;
91 mod_count++;
93 if( (mod2 = strchr(mod1,',')) != NIL(char) ){
94 *mod2 = '\0';
95 mod2++;
96 mod_count++;
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. */
103 switch( *fname ) {
104 case 'a':
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);
109 else
110 res = _exec_call(fname,args);
111 break;
113 case 'e':
114 if(strncmp(fname,"eq",2) == 0)
115 if( mod_count == 2 )
116 res = _exec_iseq(mod1,mod2,args,TRUE);
117 else
118 Fatal( "Two comma-seperated arguments expected in [%s].\n", buf );
119 else if (strncmp(fname,"echo",4) == 0)
120 res = _exec_echo(args);
121 else
122 res = _exec_call(fname,args);
123 break;
125 case 'f':
126 if(strncmp(fname,"foreach",7) == 0)
127 if( mod_count == 2 )
128 res = _exec_foreach(mod1,mod2,args);
129 else
130 Fatal( "Two comma-seperated arguments expected in [%s].\n", buf );
131 else
132 res = _exec_call(fname,args);
133 break;
135 case 'm':
136 if(strncmp(fname,"mktmp",5) == 0)
137 if( mod_count < 3 )
138 res = _exec_mktmp(mod1,mod2,args);
139 else
140 Fatal( "Maximal two comma-seperated arguments expected in [%s].\n", buf );
141 else
142 res = _exec_call(fname,args);
143 break;
145 case 'n':
146 if( strncmp(fname,"null", 4) == 0 )
147 res = _exec_iseq(mod1,NIL(char),args,TRUE);
148 else if (strncmp(fname,"nil",3) == 0 ) {
149 FREE(Expand(args));
150 res = DmStrDup("");
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 ) {
165 UseWinpath = FALSE;
166 } else {
167 UseWinpath = TRUE;
169 res = exec_normpath(eargs);
170 UseWinpath = tmpUseWinpath;
171 FREE(para);
173 else
174 Fatal( "One or no comma-seperated arguments expected in [%s].\n", buf );
176 FREE(eargs);
178 else
179 res = _exec_call(fname,args);
180 break;
182 case '!':
183 if(strncmp(fname,"!null",5) == 0)
184 res = _exec_iseq(mod1,NIL(char),args,FALSE);
185 else if(strncmp(fname,"!eq",3) ==0)
186 if( mod_count == 2 )
187 res = _exec_iseq(mod1,mod2,args,FALSE);
188 else
189 Fatal( "Two comma-seperated arguments expected in [%s].\n", buf );
190 else
191 res = _exec_call(fname,args);
192 break;
194 case 'o':
195 if(strncmp(fname,"or",2) == 0)
196 res = _exec_andor(args, FALSE);
197 else
198 res = _exec_call(fname,args);
199 break;
201 case 's':
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);
212 else
213 Fatal( "Unknown argument [%s] to shell in [%s].\n", emod, buf );
214 FREE(emod);
216 else
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) {
221 if( mod_count == 2 )
222 res = _exec_subst(mod1,mod2,args);
223 else
224 Fatal( "Two comma-seperated arguments expected in [%s].\n", buf );
226 else
227 res = _exec_call(fname,args);
228 break;
230 case 'u':
231 if(strncmp(fname,"uniq",4) == 0)
232 res = _exec_uniq(args);
233 else
234 res = _exec_call(fname,args);
235 break;
237 default:
238 res = _exec_call(fname,args);
241 if( res == NIL(char) ) res = DmStrDup("");
243 FREE(fname);
244 return(res);
248 static char *
249 _exec_assign( macrostring )
250 char *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));
262 static char *
263 _exec_echo(data)
264 char *data;
266 return(DmStrDup(DmStrSpn(data," \t")));
270 static char *
271 _exec_call( var, list )/*
272 =========================
273 Return the (recursively expanded) value of macro var. Expand list and
274 discard the result.
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. */
282 FREE(Expand(list));
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);
287 res = Expand(var);
289 FREE(var);
290 return(res);
294 static char *
295 _exec_foreach( var, list, data )
296 char *var;
297 char *list;
298 char *data;
300 char *res = NIL(char);
301 char *s;
302 TKSTR tk;
303 HASHPTR hp;
305 var = Expand(var);
306 list = Expand(list);
308 data = DmStrSpn(data," \t\n");
309 SET_TOKEN(&tk,list);
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);
318 CLEAR_TOKEN(&tk);
319 Pop_macro(hp); /* Get back old macro definition. */
320 FREE(hp->ht_name);
321 if(hp->ht_value) FREE(hp->ht_value);
322 FREE(hp);
323 FREE(var);
324 FREE(list);
326 return(res);
330 static char *
331 _exec_mktmp( file, text, data )
332 char *file;
333 char *text;
334 char *data;
336 char *tmpname;
337 char *name;
338 FILE *tmpfile = NIL(FILE);
340 /* This is only a test of the recipe line so prevent the tempfile side
341 * effects. */
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);
350 if( *tmpname ) {
351 #ifdef HAVE_MKSTEMP
352 /* Only use umask if we are also using mkstemp - this basically
353 * avoids using the incompatible implementation from MSVC. */
354 mode_t mask;
356 /* Create tempfile with 600 permissions. */
357 mask = umask(0066);
358 #endif
360 if( (tmpfile = fopen(tmpname, "w")) == NIL(FILE) )
361 Open_temp_error( tmpname, name );
362 #ifdef HAVE_MKSTEMP
363 umask(mask);
364 #endif
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(). */
372 else
373 FREE(tmpname);
376 /* If file expanded to a non empty value tmpfile is already opened,
377 * otherwise open it now. */
378 if( !tmpfile )
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));
387 else {
388 text = Expand(text);
391 data = Expand(data);
393 Append_line( data, TRUE, tmpfile, name, FALSE, FALSE );
394 Close_temp( Current_target, tmpfile );
395 FREE(data);
397 return( text );
401 static char *
402 _exec_iseq( lhs, rhs, data, eq )
403 char *lhs;
404 char *rhs;
405 char *data;
406 int 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';
417 res = Expand(i);
419 else if( e != NIL(char) ) {
420 e = DmStrSpn(e," \t\n");
421 if( *e ) res = Expand(e);
424 FREE(l);
425 FREE(r);
426 return(res);
430 static char *
431 _exec_sort( args )
432 char *args;
434 char *res = NIL(char);
435 char *data = Expand(args);
436 char **tokens;
437 char *p;
438 char *white = " \t\n";
439 int j;
440 int i;
442 for(i=0,p=DmStrSpn(data,white);*p;p=DmStrSpn(DmStrPbrk(p,white),white),i++);
444 if( i != 0 ) {
445 TALLOC(tokens, i, char *);
447 for( i=0,p=DmStrSpn(data,white); *p; p=DmStrSpn(p,white),i++){
448 tokens[i] = p;
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]);
456 FREE(data);
457 FREE(tokens);
460 return(res);
464 static char *
465 _exec_uniq( args )
466 char *args;
468 char *res = NIL(char);
469 char *data = Expand(args);
470 char **tokens;
471 char **tokens_after;
472 char *p;
473 char *white = " \t\n";
474 int j;
475 int i;
476 char *last = "";
477 int k = 0;
479 for(i=0,p=DmStrSpn(data,white);*p;p=DmStrSpn(DmStrPbrk(p,white),white),i++);
481 if( i != 0 ) {
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++){
486 tokens[i] = p;
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];
496 last = tokens[j];
500 for( j=0; j<k; j++ ) res = DmStrApp(res, tokens_after[j]);
501 FREE(data);
502 FREE(tokens);
503 FREE(tokens_after);
506 return(res);
509 static int
510 _mystrcmp( p, q )
511 const DMPVOID p;
512 const DMPVOID q;
514 return(strcmp(*((const char **)p),*((const char **)q)));
518 static char *
519 _exec_subst( pat, subst, data )
520 char *pat;
521 char *subst;
522 char *data;
524 char *res;
526 pat = Expand(pat);
527 subst = Expand(subst);
529 /* This implies FREE(Expand(data)) */
530 res = Apply_edit( Expand(data), pat, subst, TRUE, FALSE );
531 FREE(pat);
532 FREE(subst);
534 return(res);
538 static char *
539 _exec_shell( data, expand )/*
540 =============================
541 Capture the stdout of an execuded command.
542 If expand is TRUE expand the result. */
543 char *data;
544 int expand;
546 extern char *tempnam();
547 int bsize;
548 char *buffer;
549 char *tmpnm;
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;
556 int tflag = Trace;
557 char *res = NIL(char);
558 CELL cell;
559 STRING rcp;
560 HASH cname;
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
566 * fields. */
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( !(rcp.st_attr & A_SILENT) ) {
591 printf( "%s: Executing shell macro: %s\n", Pname, data );
592 fflush(stdout);
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;
611 Trace = FALSE;
613 /* The actual redirection happens in runargv(). */
614 Exec_commands( &cell );
616 Unlink_temp_files( &cell );
618 Trace = tflag;
619 Verbose = vflag;
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
625 * as a string. */
626 rewind(stdout_redir);
627 while( fgets(buffer, bsize, stdout_redir) ) {
628 char *p = strchr(buffer, '\n');
630 if( p == NIL(char) )
631 res = DmStrJoin(res,buffer,-1,TRUE);
632 else {
633 *p = '\0';
634 /* You might encounter '\r\n' on windows, handle it. */
635 if( p > buffer && *(p-1) == '\r')
636 *(p-1) = '\0';
637 res = DmStrApp(res,buffer);
641 fclose(stdout_redir);
642 Remove_file(tmpnm);
643 FREE(tmpnm);
644 FREE(buffer);
646 stdout_redir = old_stdout_redir;
648 if ( expand ) {
649 char *exp_res;
650 exp_res = Expand(res);
651 FREE(res);
652 res = exp_res;
655 return(res);
659 static char *
660 _exec_andor( args, doand )
661 char *args;
662 int doand;
664 char *next;
665 char *p;
666 char *white = " \t\n";
667 int res=doand;
669 args = DmStrSpn(args,white);
670 do {
671 p=ScanToken(args, &next, TRUE);
673 if (doand ? !*p : *p) {
674 res = !doand;
675 FREE(p);
676 break;
679 FREE(p);
681 while (*(args=DmStrSpn(next,white)));
683 return(res ? DmStrDup("t") : DmStrDup(""));
687 static char *
688 _exec_not( args )
689 char *args;
691 char *white = " \t\n";
692 char *p=Expand(args);
693 int res = (*DmStrSpn(p,white) == '\0');
695 FREE(p);
696 return(res ? DmStrDup("t") : DmStrDup(""));
700 char *
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. */
706 char *args;
708 TKSTR str;
709 char *s, *res;
711 /* This honors .WINPATH . */
712 SET_TOKEN( &str, args );
713 res = NIL(char);
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);
721 } else {
722 res = DmStrApp(res, DO_WINPATH(normalize_path(s)));
725 return res;