305191454d
2006/03/13 23:21:27 vq 1.7.6.10: #i61390# Make dmake (gcc) build -Wall warning free. 2006/02/01 23:28:55 vq 1.7.6.9: #i60948# Add -m option family to generate timing information for targets and/or recipes. (Autotools files were regenerated.) 2005/10/25 17:19:43 vq 1.7.6.8: #i53148# Restore old behaviour for non-*NIX dmake versions. 2005/10/11 17:39:38 vq 1.7.6.7: #i54938# Fix problem when building infered .INCLUDE makefiles and doing parallel builds. 2005/09/05 20:56:45 vq 1.7.6.6: #i53148# Move redirection of stdout from parent to child and avoid capturing spurious output from other process queues. 2005/09/05 17:23:44 vq 1.7.6.5: #i53148# Additional patch to make sure that the shell escapes is executed after all previous recipe lines from the same target have finished. 2005/09/04 19:38:14 vq 1.7.6.4: #i53148# Fix $(shell ...) handling for parallel builds with MAXPROCESS > 1. This certainly includes the -P# switch with # > 1. 2005/06/01 23:48:57 vq 1.7.6.3: #i50091# Echo shell function macro commands. 2004/11/25 16:09:31 vq 1.7.6.2: #i36027# Fix MSVC compilation problem. 2004/11/21 17:40:17 vq 1.7.6.1: #i36027# Improve parsing for dmake function macros.
679 lines
17 KiB
C
679 lines
17 KiB
C
/* $RCSfile: function.c,v $
|
|
-- $Revision: 1.8 $
|
|
-- last change: $Author: hr $ $Date: 2006-04-20 12:00:12 $
|
|
--
|
|
-- SYNOPSIS
|
|
-- GNU style functions for dmake.
|
|
--
|
|
-- DESCRIPTION
|
|
-- All GNU style functions understood by dmake are implemented in this
|
|
-- file. Currently the only such function is $(mktmp ...) which is
|
|
-- not part of GNU-make is an extension provided by dmake.
|
|
--
|
|
-- AUTHOR
|
|
-- Dennis Vadura, dvadura@dmake.wticorp.com
|
|
--
|
|
-- WWW
|
|
-- http://dmake.wticorp.com/
|
|
--
|
|
-- COPYRIGHT
|
|
-- Copyright (c) 1996,1997 by WTI Corp. All rights reserved.
|
|
--
|
|
-- This program is NOT free software; you can redistribute it and/or
|
|
-- modify it under the terms of the Software License Agreement Provided
|
|
-- in the file <distribution-root>/readme/license.txt.
|
|
--
|
|
-- LOG
|
|
-- Use cvs log to obtain detailed change logs.
|
|
*/
|
|
|
|
#include "extern.h"
|
|
|
|
static char *_exec_mktmp ANSI((char *, char *, char *));
|
|
static char *_exec_subst ANSI((char *, char *, char *));
|
|
static char *_exec_iseq ANSI((char *, char *, char *, int));
|
|
static char *_exec_sort ANSI((char *));
|
|
static char *_exec_echo ANSI((char *));
|
|
static char *_exec_uniq ANSI((char *));
|
|
static char *_exec_shell ANSI((char *, char *));
|
|
static char *_exec_call ANSI((char *, char *));
|
|
static char *_exec_assign ANSI((char *));
|
|
static char *_exec_foreach ANSI((char *, char *, char *));
|
|
static char *_exec_andor ANSI((char *, int));
|
|
static char *_exec_not ANSI((char *));
|
|
static int _mystrcmp ANSI((const PVOID, const PVOID));
|
|
|
|
|
|
PUBLIC char *
|
|
Exec_function(buf)/*
|
|
====================
|
|
Execute the function given by the value of args.
|
|
|
|
So far mktmp is the only valid function, anything else elicits and error
|
|
message. It is my hope to support the GNU style functions in this portion
|
|
of the code at some time in the future. */
|
|
char *buf;
|
|
{
|
|
char *fname;
|
|
char *args;
|
|
char *mod1;
|
|
char *mod2 = NIL(char);
|
|
int mod_count = 0;
|
|
char *res = NIL(char);
|
|
|
|
/* This must succeed since the presence of ' ', \t or \n is what
|
|
* determines if this function is called in the first place.
|
|
* Unfortunately this prohibits the use of whitespaces in parameters
|
|
* for macro functions. */
|
|
/* ??? Using ScanToken to find the next ' ', \t or \n and discarding
|
|
* the returned, evaluated result is a misuse of that function. */
|
|
FREE(ScanToken(buf, &args, FALSE));
|
|
fname = DmSubStr(buf, args);
|
|
/* args points to the whitespace after the found token, this leads
|
|
* to leading whitespaces. */
|
|
if( *args ) {
|
|
args = DmStrSpn(args," \t"); /* strip whitespace before */
|
|
if( *args ) { /* ... and after value */
|
|
char *q;
|
|
for(q=args+strlen(args)-1; ((*q == ' ')||(*q == '\t')); q--);
|
|
*++q = '\0';
|
|
}
|
|
}
|
|
|
|
/* ??? Some function macros expect comma seperated parameters, but
|
|
* no decent parser is included. The desirable solution would be
|
|
* to parse fname for the correct number of parameters in fname
|
|
* when a function is recognized. We only count the parameters
|
|
* at the moment. Note "" is a valid parameter. */
|
|
if( (mod1 = strchr(fname,',')) != NIL(char) ){
|
|
*mod1 = '\0';
|
|
mod1++;
|
|
mod_count++;
|
|
|
|
if( (mod2 = strchr(mod1,',')) != NIL(char) ){
|
|
*mod2 = '\0';
|
|
mod2++;
|
|
mod_count++;
|
|
}
|
|
}
|
|
|
|
/* ??? At the moment only the leading part of fname compared if it
|
|
* matches a known function macro. For example assignXXX or even
|
|
* assign,,,, is also erroneously accepted. */
|
|
switch( *fname ) {
|
|
case 'a':
|
|
if(strncmp(fname,"assign",6) == 0)
|
|
res = _exec_assign(args);
|
|
else if(strncmp(fname,"and",3) == 0)
|
|
res = _exec_andor(args, TRUE);
|
|
else
|
|
res = _exec_call(fname,args);
|
|
break;
|
|
|
|
case 'e':
|
|
if(strncmp(fname,"eq",2) == 0)
|
|
if( mod_count == 2 )
|
|
res = _exec_iseq(mod1,mod2,args,TRUE);
|
|
else
|
|
Fatal( "Two comma-seperated arguments expected in [%s].\n", buf );
|
|
else if (strncmp(fname,"echo",4) == 0)
|
|
res = _exec_echo(args);
|
|
else
|
|
res = _exec_call(fname,args);
|
|
break;
|
|
|
|
case 'f':
|
|
if(strncmp(fname,"foreach",7) == 0)
|
|
if( mod_count == 2 )
|
|
res = _exec_foreach(mod1,mod2,args);
|
|
else
|
|
Fatal( "Two comma-seperated arguments expected in [%s].\n", buf );
|
|
else
|
|
res = _exec_call(fname,args);
|
|
break;
|
|
|
|
case 'm':
|
|
if(strncmp(fname,"mktmp",5) == 0)
|
|
if( mod_count < 3 )
|
|
res = _exec_mktmp(mod1,mod2,args);
|
|
else
|
|
Fatal( "Maximal two comma-seperated arguments expected in [%s].\n", buf );
|
|
else
|
|
res = _exec_call(fname,args);
|
|
break;
|
|
|
|
case 'n':
|
|
if( strncmp(fname,"null", 4) == 0 )
|
|
res = _exec_iseq(mod1,NIL(char),args,TRUE);
|
|
else if (strncmp(fname,"nil",3) == 0 )
|
|
res = DmStrDup("");
|
|
else if (strncmp(fname,"not",3) == 0 )
|
|
res = _exec_not(args);
|
|
else
|
|
res = _exec_call(fname,args);
|
|
break;
|
|
|
|
case '!':
|
|
if(strncmp(fname,"!null",5) == 0)
|
|
res = _exec_iseq(mod1,NIL(char),args,FALSE);
|
|
else if(strncmp(fname,"!eq",3) ==0)
|
|
if( mod_count == 2 )
|
|
res = _exec_iseq(mod1,mod2,args,FALSE);
|
|
else
|
|
Fatal( "Two comma-seperated arguments expected in [%s].\n", buf );
|
|
else
|
|
res = _exec_call(fname,args);
|
|
break;
|
|
|
|
case 'o':
|
|
if(strncmp(fname,"or",2) == 0)
|
|
res = _exec_andor(args, FALSE);
|
|
else
|
|
res = _exec_call(fname,args);
|
|
break;
|
|
|
|
case 's':
|
|
if(strncmp(fname,"sort",4) == 0)
|
|
res = _exec_sort(args);
|
|
else if(strncmp(fname,"shell",5)==0)
|
|
res = _exec_shell(args,mod1);
|
|
else if(strncmp(fname,"strip",5)==0)
|
|
res = Tokenize(Expand(args)," ",'t',TRUE);
|
|
else if(strncmp(fname,"subst",5)==0) {
|
|
if( mod_count == 2 )
|
|
res = _exec_subst(mod1,mod2,args);
|
|
else
|
|
Fatal( "Two comma-seperated arguments expected in [%s].\n", buf );
|
|
}
|
|
else
|
|
res = _exec_call(fname,args);
|
|
break;
|
|
|
|
case 'u':
|
|
if(strncmp(fname,"uniq",4) == 0)
|
|
res = _exec_uniq(args);
|
|
else
|
|
res = _exec_call(fname,args);
|
|
break;
|
|
|
|
default:
|
|
res = _exec_call(fname,args);
|
|
}
|
|
|
|
if( res == NIL(char) ) res = DmStrDup("");
|
|
|
|
FREE(fname);
|
|
return(res);
|
|
}
|
|
|
|
|
|
static char *
|
|
_exec_assign( macrostring )
|
|
char *macrostring;
|
|
{
|
|
if ( !Parse_macro(macrostring, M_MULTI|M_FORCE) ) {
|
|
Error( "Dynamic macro assignment failed, while making [%s]\n",
|
|
Current_target ? Current_target->CE_NAME : "NIL");
|
|
return(DmStrDup(""));
|
|
}
|
|
|
|
return(DmStrDup(LastMacName));
|
|
}
|
|
|
|
|
|
static char *
|
|
_exec_echo(data)
|
|
char *data;
|
|
{
|
|
return(DmStrDup(DmStrSpn(data," \t")));
|
|
}
|
|
|
|
|
|
static char *
|
|
_exec_call( var, list )
|
|
char *var;
|
|
char *list;
|
|
{
|
|
char *res = NIL(char);
|
|
char *s;
|
|
TKSTR tk;
|
|
int i=0;
|
|
|
|
list = Expand(list);
|
|
|
|
SET_TOKEN(&tk,list);
|
|
while( *(s=Get_token(&tk, "", FALSE)) != '\0' ) {
|
|
char buf[40];
|
|
|
|
sprintf(buf, "%d", i++);
|
|
Def_macro(buf,s,M_MULTI|M_NOEXPORT|M_FORCE|M_PUSH);
|
|
}
|
|
CLEAR_TOKEN(&tk);
|
|
|
|
var = DmStrJoin(DmStrJoin("$(",var,-1,FALSE),")",-1,TRUE);
|
|
res = Expand(var);
|
|
|
|
i=0;
|
|
SET_TOKEN(&tk,list);
|
|
while( *(s=Get_token(&tk, "", FALSE)) != '\0' ) {
|
|
HASHPTR hp;
|
|
char buf[40];
|
|
|
|
sprintf(buf, "%d", i++);
|
|
hp = GET_MACRO(buf);
|
|
Pop_macro(hp);
|
|
FREE(hp->ht_name);
|
|
if(hp->ht_value) FREE(hp->ht_value);
|
|
FREE(hp);
|
|
}
|
|
CLEAR_TOKEN(&tk);
|
|
|
|
FREE(var);
|
|
FREE(list);
|
|
return(res);
|
|
}
|
|
|
|
|
|
static char *
|
|
_exec_foreach( var, list, data )
|
|
char *var;
|
|
char *list;
|
|
char *data;
|
|
{
|
|
char *res = NIL(char);
|
|
char *s;
|
|
TKSTR tk;
|
|
HASHPTR hp;
|
|
|
|
var = Expand(var);
|
|
list = Expand(list);
|
|
|
|
data = DmStrSpn(data," \t\n");
|
|
SET_TOKEN(&tk,list);
|
|
hp = Def_macro(var,"",M_MULTI|M_NOEXPORT|M_FORCE|M_PUSH);
|
|
|
|
while( *(s=Get_token(&tk, "", FALSE)) != '\0' ) {
|
|
Def_macro(var,s,M_MULTI|M_NOEXPORT|M_FORCE);
|
|
res = DmStrAdd(res,Expand(data),TRUE);
|
|
}
|
|
|
|
CLEAR_TOKEN(&tk);
|
|
Pop_macro(hp);
|
|
FREE(hp->ht_name);
|
|
if(hp->ht_value) FREE(hp->ht_value);
|
|
FREE(hp);
|
|
FREE(var);
|
|
FREE(list);
|
|
|
|
return(res);
|
|
}
|
|
|
|
|
|
static char *
|
|
_exec_mktmp( file, text, data )
|
|
char *file;
|
|
char *text;
|
|
char *data;
|
|
{
|
|
register char *p;
|
|
char *tmpname;
|
|
char *name;
|
|
FILE *tmpfile = NIL(FILE);
|
|
|
|
/* This is only a test of the recipe line so prevent the tempfile side
|
|
* effects. */
|
|
if( Suppress_temp_file ) return(NIL(char));
|
|
|
|
name = Current_target ? Current_target->CE_NAME:"makefile text";
|
|
|
|
if( file && *file ) {
|
|
/* This call to Get_temp sets TMPFILE for subsequent expansion of file.
|
|
* The contents file variable passed may include TMPFILE to be expanded. */
|
|
/* Using TMPFILE as an argument to mktmp is no longer supported because it is not
|
|
* safe to create a random filename and assume the file does not exist. Howver,
|
|
* we still allow Expand() to do its job for fixed filenames */
|
|
/* char *newtmp;
|
|
* Get_temp( &newtmp, "", FALSE ); FREE(newtmp); */
|
|
tmpname = Expand(file);
|
|
|
|
if( *tmpname ) {
|
|
if( (tmpfile = fopen(tmpname, "w")) == NIL(FILE) )
|
|
Open_temp_error( tmpname, name );
|
|
|
|
Def_macro("TMPFILE", tmpname, M_EXPANDED|M_MULTI);
|
|
Link_temp( Current_target, tmpfile, tmpname );
|
|
}
|
|
else
|
|
FREE(tmpname);
|
|
}
|
|
|
|
if( !tmpfile )
|
|
tmpfile = Start_temp( "", Current_target, &tmpname );
|
|
|
|
if( !text || !*text ) text = tmpname;
|
|
data = Expand(DmStrSpn(data, " \t\n"));
|
|
|
|
for(p=strchr(data,'\n'); p; p=strchr(p,'\n')) {
|
|
char *q = DmStrSpn(++p," \t");
|
|
strcpy(p,q);
|
|
}
|
|
|
|
/* do not map escape sequences while writing a tmpfile */
|
|
/* Append_line( data, FALSE, tmpfile, name, FALSE, TRUE ); */
|
|
Append_line( data, TRUE, tmpfile, name, FALSE, FALSE );
|
|
Close_temp( Current_target, tmpfile );
|
|
FREE(data);
|
|
|
|
return( Expand(text) );
|
|
}
|
|
|
|
|
|
static char *
|
|
_exec_iseq( lhs, rhs, data, eq )
|
|
char *lhs;
|
|
char *rhs;
|
|
char *data;
|
|
int eq;
|
|
{
|
|
char *l = Expand(lhs);
|
|
char *r = Expand(rhs);
|
|
char *i = DmStrSpn(data, " \t\n");
|
|
char *e = strchr(i, ' ');
|
|
char *res = NIL(char);
|
|
int val = strcmp(l,r);
|
|
|
|
if( (!val && eq) || (val && !eq) ) {
|
|
if( e != NIL(char) ) *e = '\0';
|
|
res = Expand(i);
|
|
}
|
|
else if( e != NIL(char) ) {
|
|
e = DmStrSpn(e," \t\n");
|
|
if( *e ) res = Expand(e);
|
|
}
|
|
|
|
FREE(l);
|
|
FREE(r);
|
|
return(res);
|
|
}
|
|
|
|
|
|
static char *
|
|
_exec_sort( args )
|
|
char *args;
|
|
{
|
|
char *res = NIL(char);
|
|
char *data = Expand(args);
|
|
char **tokens;
|
|
char *p;
|
|
char *white = " \t\n";
|
|
int j;
|
|
int i;
|
|
|
|
for(i=0,p=DmStrSpn(data,white);*p;p=DmStrSpn(DmStrPbrk(p,white),white),i++);
|
|
|
|
if( i != 0 ) {
|
|
TALLOC(tokens, i, char *);
|
|
|
|
for( i=0,p=DmStrSpn(data,white); *p; p=DmStrSpn(p,white),i++){
|
|
tokens[i] = p;
|
|
p = DmStrPbrk(p,white);
|
|
if( *p ) *p++ = '\0';
|
|
}
|
|
|
|
qsort( tokens, i, sizeof(char *), _mystrcmp );
|
|
|
|
for( j=0; j<i; j++ ) res = DmStrApp(res, tokens[j]);
|
|
FREE(data);
|
|
FREE(tokens);
|
|
}
|
|
|
|
return(res);
|
|
}
|
|
|
|
|
|
static char *
|
|
_exec_uniq( args )
|
|
char *args;
|
|
{
|
|
char *res = NIL(char);
|
|
char *data = Expand(args);
|
|
char **tokens;
|
|
char **tokens_after;
|
|
char *p;
|
|
char *white = " \t\n";
|
|
int j;
|
|
int i;
|
|
char *last = "";
|
|
int k = 0;
|
|
|
|
for(i=0,p=DmStrSpn(data,white);*p;p=DmStrSpn(DmStrPbrk(p,white),white),i++);
|
|
|
|
if( i != 0 ) {
|
|
TALLOC(tokens, i, char *);
|
|
TALLOC(tokens_after, i, char *);
|
|
|
|
for( i=0,p=DmStrSpn(data,white); *p; p=DmStrSpn(p,white),i++){
|
|
tokens[i] = p;
|
|
p = DmStrPbrk(p,white);
|
|
if( *p ) *p++ = '\0';
|
|
}
|
|
|
|
qsort( tokens, i, sizeof(char *), _mystrcmp );
|
|
|
|
for( j=0; j<i; j++ ) {
|
|
if (strcmp(tokens[j], last) != 0) {
|
|
tokens_after[k++] = tokens[j];
|
|
last = tokens[j];
|
|
}
|
|
}
|
|
|
|
for( j=0; j<k; j++ ) res = DmStrApp(res, tokens_after[j]);
|
|
FREE(data);
|
|
FREE(tokens);
|
|
FREE(tokens_after);
|
|
}
|
|
|
|
return(res);
|
|
}
|
|
|
|
static int
|
|
_mystrcmp( p, q )
|
|
const PVOID p;
|
|
const PVOID q;
|
|
{
|
|
return(strcmp(*((const char **)p),*((const char **)q)));
|
|
}
|
|
|
|
|
|
static char *
|
|
_exec_subst( pat, subst, data )
|
|
char *pat;
|
|
char *subst;
|
|
char *data;
|
|
{
|
|
char *res;
|
|
|
|
pat = Expand(pat);
|
|
subst = Expand(subst);
|
|
|
|
/* This implies FREE(Expand(data)) */
|
|
res = Apply_edit( Expand(data), pat, subst, TRUE, FALSE );
|
|
FREE(pat);
|
|
FREE(subst);
|
|
|
|
return(res);
|
|
}
|
|
|
|
|
|
static char *
|
|
_exec_shell( data, mod1 )/*
|
|
===========================
|
|
Capture the stdout of an execuded command. */
|
|
char *data;
|
|
char *mod1;
|
|
{
|
|
extern char *tempnam();
|
|
int bsize;
|
|
char *buffer;
|
|
char *tmpnm;
|
|
FILE *old_stdout_redir = stdout_redir;
|
|
#if !defined(USE_SANE_EXEC_SHELL_REDIR)
|
|
int old_stdout;
|
|
#endif
|
|
|
|
int wait = Wait_for_completion;
|
|
int old_is_exec_shell = Is_exec_shell;
|
|
CELLPTR old_Shell_exec_target = Shell_exec_target;
|
|
uint16 vflag = Verbose;
|
|
int tflag = Trace;
|
|
char *res = NIL(char);
|
|
CELL cell;
|
|
STRING rcp;
|
|
HASH cname;
|
|
|
|
if( Suppress_temp_file ) return(NIL(char));
|
|
|
|
/* Set the temp CELL used for building prerequisite candidates to
|
|
* all zero so that we don't have to keep initializing all the
|
|
* fields. */
|
|
{
|
|
register char *s = (char *) &cell;
|
|
register int n = sizeof(CELL);
|
|
while( n ) { *s++ = '\0'; n--; }
|
|
}
|
|
rcp.st_string = DmStrSpn(data, " \t+-%@");
|
|
rcp.st_attr = Rcp_attribute( data );
|
|
rcp.st_next = NIL(STRING);
|
|
cname.ht_name = "Shell escape";
|
|
cell.ce_name = &cname;
|
|
cell.ce_all.cl_prq = &cell;
|
|
cell.ce_all.cl_next = NIL(LINK);
|
|
cell.ce_all.cl_flag = 0;
|
|
cell.ce_fname = cname.ht_name;
|
|
cell.ce_recipe = &rcp;
|
|
cell.ce_flag = F_TARGET|F_RULES;
|
|
/* Setting A_SILENT supresses the recipe output from Print_cmnd(). */
|
|
cell.ce_attr = A_PHONY|A_SILENT|A_SHELLESC;
|
|
|
|
if( Measure & M_TARGET )
|
|
Do_profile_output( "s", M_TARGET, &cell );
|
|
|
|
/* Print the shell escape command. */
|
|
if( !(rcp.st_attr & A_SILENT) ) {
|
|
printf( "%s: Executing shell macro: %s\n", Pname, data );
|
|
fflush(stdout);
|
|
}
|
|
|
|
if( (stdout_redir = Get_temp(&tmpnm, "", "w+")) == NIL(FILE) )
|
|
Open_temp_error( tmpnm, cname.ht_name );
|
|
|
|
bsize = (Buffer_size < BUFSIZ)?BUFSIZ:Buffer_size;
|
|
buffer = MALLOC(bsize,char);
|
|
|
|
/* As this function redirects the output of stdout we have to make sure
|
|
* that only this single command is executed and all previous recipe lines
|
|
* that belong to the same target have finished. With Shell_exec_target and
|
|
* Wait_for_completion set this is realized. Current_target being NIL(CELL)
|
|
* outside of recipe lines makes sure that no waiting for previous recipe
|
|
* lines has to be done. */
|
|
Wait_for_completion = TRUE;
|
|
Is_exec_shell = TRUE;
|
|
Shell_exec_target = Current_target;
|
|
Verbose &= V_LEAVE_TMP;
|
|
Trace = FALSE;
|
|
|
|
/* Only unix type builds define the redirection of stdout in the forked
|
|
* child process. Other OSs have to do it here, in the parent process.
|
|
* Note! This is not save for parallel builds, see #iz53148#, but parallel
|
|
* builds are not supported for non-unix like builds anyway. */
|
|
#if !defined(USE_SANE_EXEC_SHELL_REDIR)
|
|
old_stdout = dup(1);
|
|
close(1);
|
|
dup( fileno(stdout_redir) );
|
|
#endif
|
|
Exec_commands( &cell );
|
|
#if !defined(USE_SANE_EXEC_SHELL_REDIR)
|
|
close(1);
|
|
dup(old_stdout);
|
|
#endif
|
|
Unlink_temp_files( &cell );
|
|
|
|
Trace = tflag;
|
|
Verbose = vflag;
|
|
Wait_for_completion = wait;
|
|
Is_exec_shell = old_is_exec_shell;
|
|
Shell_exec_target = old_Shell_exec_target;
|
|
|
|
/* Now we have to read the temporary file, get the tokens and return them
|
|
* as a string. */
|
|
rewind(stdout_redir);
|
|
while( fgets(buffer, bsize, stdout_redir) ) {
|
|
char *p = strchr(buffer, '\n');
|
|
|
|
if( p == NIL(char) )
|
|
res = DmStrJoin(res,buffer,-1,TRUE);
|
|
else {
|
|
*p = '\0';
|
|
/* You might encounter '\r\n' on windows, handle it. */
|
|
if( p > buffer && *(p-1) == '\r')
|
|
*(p-1) = '\0';
|
|
res = DmStrApp(res,buffer);
|
|
}
|
|
}
|
|
|
|
fclose(stdout_redir);
|
|
Remove_file(tmpnm);
|
|
FREE(tmpnm);
|
|
FREE(buffer);
|
|
|
|
stdout_redir = old_stdout_redir;
|
|
|
|
if ( mod1 ) {
|
|
mod1 = Expand(res);
|
|
FREE(res);
|
|
res = mod1;
|
|
}
|
|
|
|
return(res);
|
|
}
|
|
|
|
|
|
static char *
|
|
_exec_andor( args, doand )
|
|
char *args;
|
|
int doand;
|
|
{
|
|
char *next;
|
|
char *p;
|
|
char *white = " \t\n";
|
|
int res=doand;
|
|
|
|
args = DmStrSpn(args,white);
|
|
do {
|
|
p=ScanToken(args, &next, TRUE);
|
|
|
|
if (doand ? !*p : *p) {
|
|
res = !doand;
|
|
FREE(p);
|
|
break;
|
|
}
|
|
|
|
FREE(p);
|
|
}
|
|
while (*(args=DmStrSpn(next,white)));
|
|
|
|
return(res ? DmStrDup("t") : DmStrDup(""));
|
|
}
|
|
|
|
|
|
static char *
|
|
_exec_not( args )
|
|
char *args;
|
|
{
|
|
char *white = " \t\n";
|
|
char *p=Expand(args);
|
|
int res = (*DmStrSpn(p,white) == '\0');
|
|
|
|
FREE(p);
|
|
return(res ? DmStrDup("t") : DmStrDup(""));
|
|
}
|