Version 4.0.0.1, tag libreoffice-4.0.0.1
[LibreOffice.git] / dmake / function.c
blob0fcc68861b2e8dd4583b034a8def21f284ae0548
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.
11 -- AUTHOR
12 -- Dennis Vadura, dvadura@dmake.wticorp.com
14 -- WWW
15 -- http://dmake.wticorp.com/
17 -- COPYRIGHT
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.
24 -- LOG
25 -- Use cvs log to obtain detailed change logs.
28 #include "extern.h"
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));
45 PUBLIC char *
46 Exec_function(buf)/*
47 ====================
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. */
53 char *buf;
55 char *fname;
56 char *args;
57 char *mod1;
58 char *mod2 = NIL(char);
59 int mod_count = 0;
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. */
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';
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) ){
87 *mod1 = '\0';
88 mod1++;
89 mod_count++;
91 if( (mod2 = strchr(mod1,',')) != NIL(char) ){
92 *mod2 = '\0';
93 mod2++;
94 mod_count++;
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, TRUE);
107 else
108 res = _exec_call(fname,args);
109 break;
111 case 'e':
112 if(strncmp(fname,"eq",2) == 0)
113 if( mod_count == 2 )
114 res = _exec_iseq(mod1,mod2,args,TRUE);
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;
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;
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;
143 case 'n':
144 if( strncmp(fname,"null", 4) == 0 )
145 res = _exec_iseq(mod1,NIL(char),args,TRUE);
146 else if (strncmp(fname,"nil",3) == 0 ) {
147 FREE(Expand(args));
148 res = DmStrDup("");
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 ) {
163 UseWinpath = FALSE;
164 } else {
165 UseWinpath = TRUE;
167 res = exec_normpath(eargs);
168 UseWinpath = tmpUseWinpath;
169 FREE(para);
171 else
172 Fatal( "One or no comma-seperated arguments expected in [%s].\n", buf );
174 FREE(eargs);
176 else
177 res = _exec_call(fname,args);
178 break;
180 case '!':
181 if(strncmp(fname,"!null",5) == 0)
182 res = _exec_iseq(mod1,NIL(char),args,FALSE);
183 else if(strncmp(fname,"!eq",3) ==0)
184 if( mod_count == 2 )
185 res = _exec_iseq(mod1,mod2,args,FALSE);
186 else
187 Fatal( "Two comma-seperated arguments expected in [%s].\n", buf );
188 else
189 res = _exec_call(fname,args);
190 break;
192 case 'o':
193 if(strncmp(fname,"or",2) == 0)
194 res = _exec_andor(args, FALSE);
195 else
196 res = _exec_call(fname,args);
197 break;
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, 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);
210 else
211 Fatal( "Unknown argument [%s] to shell in [%s].\n", emod, buf );
212 FREE(emod);
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',TRUE);
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 );
224 else
225 res = _exec_call(fname,args);
226 break;
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;
235 default:
236 res = _exec_call(fname,args);
239 if( res == NIL(char) ) res = DmStrDup("");
241 FREE(fname);
242 return(res);
246 static char *
247 _exec_assign( macrostring )
248 char *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));
260 static char *
261 _exec_echo(data)
262 char *data;
264 return(DmStrDup(DmStrSpn(data," \t")));
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.
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. */
280 FREE(Expand(list));
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);
285 res = Expand(var);
287 FREE(var);
288 return(res);
292 static char *
293 _exec_foreach( var, list, data )
294 char *var;
295 char *list;
296 char *data;
298 char *res = NIL(char);
299 char *s;
300 TKSTR tk;
301 HASHPTR hp;
303 var = Expand(var);
304 list = Expand(list);
306 data = DmStrSpn(data," \t\n");
307 SET_TOKEN(&tk,list);
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);
316 CLEAR_TOKEN(&tk);
317 Pop_macro(hp); /* Get back old macro definition. */
318 FREE(hp->ht_name);
319 if(hp->ht_value) FREE(hp->ht_value);
320 FREE(hp);
321 FREE(var);
322 FREE(list);
324 return(res);
328 static char *
329 _exec_mktmp( file, text, data )
330 char *file;
331 char *text;
332 char *data;
334 char *tmpname;
335 char *name;
336 FILE *tmpfile = NIL(FILE);
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));
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);
348 if( *tmpname ) {
349 #ifdef HAVE_MKSTEMP
350 /* Only use umask if we are also using mkstemp - this basically
351 * avoids using the incompatible implementation from MSVC. */
352 mode_t mask;
354 /* Create tempfile with 600 permissions. */
355 mask = umask(0066);
356 #endif
358 if( (tmpfile = fopen(tmpname, "w")) == NIL(FILE) )
359 Open_temp_error( tmpname, name );
360 #ifdef HAVE_MKSTEMP
361 umask(mask);
362 #endif
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(). */
370 else
371 FREE(tmpname);
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 );
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));
385 else {
386 text = Expand(text);
389 data = Expand(data);
391 Append_line( data, TRUE, tmpfile, name, FALSE, FALSE );
392 Close_temp( Current_target, tmpfile );
393 FREE(data);
395 return( text );
399 static char *
400 _exec_iseq( lhs, rhs, data, eq )
401 char *lhs;
402 char *rhs;
403 char *data;
404 int 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';
415 res = Expand(i);
417 else if( e != NIL(char) ) {
418 e = DmStrSpn(e," \t\n");
419 if( *e ) res = Expand(e);
422 FREE(l);
423 FREE(r);
424 return(res);
428 static char *
429 _exec_sort( args )
430 char *args;
432 char *res = NIL(char);
433 char *data = Expand(args);
434 char **tokens;
435 char *p;
436 char *white = " \t\n";
437 int j;
438 int i;
440 for(i=0,p=DmStrSpn(data,white);*p;p=DmStrSpn(DmStrPbrk(p,white),white),i++);
442 if( i != 0 ) {
443 TALLOC(tokens, i, char *);
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';
451 qsort( tokens, i, sizeof(char *), _mystrcmp );
453 for( j=0; j<i; j++ ) res = DmStrApp(res, tokens[j]);
454 FREE(data);
455 FREE(tokens);
458 return(res);
462 static char *
463 _exec_uniq( args )
464 char *args;
466 char *res = NIL(char);
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;
477 for(i=0,p=DmStrSpn(data,white);*p;p=DmStrSpn(DmStrPbrk(p,white),white),i++);
479 if( i != 0 ) {
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++){
484 tokens[i] = p;
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];
494 last = tokens[j];
498 for( j=0; j<k; j++ ) res = DmStrApp(res, tokens_after[j]);
499 FREE(tokens);
500 FREE(tokens_after);
503 FREE(data);
504 return(res);
507 static int
508 _mystrcmp( p, q )
509 const DMPVOID p;
510 const DMPVOID q;
512 return(strcmp(*((const char **)p),*((const char **)q)));
516 static char *
517 _exec_subst( pat, subst, data )
518 char *pat;
519 char *subst;
520 char *data;
522 char *res;
524 pat = Expand(pat);
525 subst = Expand(subst);
527 /* This implies FREE(Expand(data)) */
528 res = Apply_edit( Expand(data), pat, subst, TRUE, FALSE );
529 FREE(pat);
530 FREE(subst);
532 return(res);
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;
544 extern char *tempnam();
545 int bsize;
546 char *buffer;
547 char *tmpnm;
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;
554 int tflag = Trace;
555 char *res = NIL(char);
556 CELL cell;
557 STRING rcp;
558 HASH cname;
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
564 * fields. */
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 );
590 fflush(stdout);
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;
609 Trace = FALSE;
611 /* The actual redirection happens in runargv(). */
612 Exec_commands( &cell );
614 Unlink_temp_files( &cell );
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;
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');
628 if( p == NIL(char) )
629 res = DmStrJoin(res,buffer,-1,TRUE);
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);
639 fclose(stdout_redir);
640 Remove_file(tmpnm);
641 FREE(tmpnm);
642 FREE(buffer);
644 stdout_redir = old_stdout_redir;
646 if ( expand ) {
647 char *exp_res;
648 exp_res = Expand(res);
649 FREE(res);
650 res = exp_res;
653 return(res);
657 static char *
658 _exec_andor( args, doand )
659 char *args;
660 int doand;
662 char *next;
663 char *p;
664 char *white = " \t\n";
665 int res=doand;
667 args = DmStrSpn(args,white);
668 do {
669 p=ScanToken(args, &next, TRUE);
671 if (doand ? !*p : *p) {
672 res = !doand;
673 FREE(p);
674 break;
677 FREE(p);
679 while (*(args=DmStrSpn(next,white)));
681 return(res ? DmStrDup("t") : DmStrDup(""));
685 static char *
686 _exec_not( args )
687 char *args;
689 char *white = " \t\n";
690 char *p=Expand(args);
691 int res = (*DmStrSpn(p,white) == '\0');
693 FREE(p);
694 return(res ? DmStrDup("t") : DmStrDup(""));
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;
706 TKSTR str;
707 char *s, *res;
709 /* This honors .WINPATH . */
710 SET_TOKEN( &str, args );
711 res = NIL(char);
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);
719 } else {
720 res = DmStrApp(res, DO_WINPATH(normalize_path(s)));
723 return res;