summaryrefslogtreecommitdiff
path: root/dmake/sysintf.c
diff options
context:
space:
mode:
Diffstat (limited to 'dmake/sysintf.c')
-rw-r--r--dmake/sysintf.c1157
1 files changed, 1157 insertions, 0 deletions
diff --git a/dmake/sysintf.c b/dmake/sysintf.c
new file mode 100644
index 000000000000..d7b482a3e405
--- /dev/null
+++ b/dmake/sysintf.c
@@ -0,0 +1,1157 @@
+/* RCS $Id: sysintf.c,v 1.13 2008-03-05 18:30:58 kz Exp $
+--
+-- SYNOPSIS
+-- System independent interface
+--
+-- DESCRIPTION
+-- These are the routines constituting the system interface.
+-- The system is taken to be essentially POSIX conformant.
+-- The original code was extensively revised by T J Thompson at MKS,
+-- and the library cacheing was added by Eric Gisin at MKS. I then
+-- revised the code yet again, to improve the lib cacheing, and to
+-- make it more portable.
+--
+-- The following is a list of routines that are required by this file
+-- in order to work. These routines are provided as functions by the
+-- standard C lib of the target system or as #defines in system/sysintf.h
+-- or via appropriate C code in the system/ directory for the given
+-- system.
+--
+-- The first group must be provided by a file in the system/ directory
+-- the second group is ideally provided by the C lib. However, there
+-- are instances where the C lib implementation of the specified routine
+-- does not exist, or is incorrect. In these instances the routine
+-- must be provided by the the user in the system/ directory of dmake.
+-- (For example, the bsd/ dir contains code for putenv(), and tempnam())
+--
+-- DMAKE SPECIFIC:
+-- seek_arch()
+-- touch_arch()
+-- void_lcache()
+-- runargv()
+-- DMSTAT()
+-- Remove_prq()
+--
+-- C-LIB SPECIFIC: (should be present in your C-lib)
+-- utime()
+-- time()
+-- getenv()
+-- putenv()
+-- getcwd()
+-- signal()
+-- chdir()
+-- tempnam()
+--
+-- 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"
+
+/* The following definition controls the use of GetModuleFileName() */
+#if defined(_MSC_VER) || defined(__MINGW32__)
+# define HAVE_GETMODULEFILENAMEFUNC 1
+
+/* this is needed for the _ftime call below. Only needed here. */
+# include <sys/timeb.h>
+#endif
+
+/* for cygwin_conv_to_posix_path() in Prolog() and for cygdospath()*/
+#if __CYGWIN__
+# include <sys/cygwin.h>
+#endif
+
+#include "sysintf.h"
+#if HAVE_ERRNO_H
+# include <errno.h>
+#else
+ extern int errno;
+#endif
+
+/*
+** Tries to stat the file name. Returns 0 if the file
+** does not exist. Note that if lib is not null it tries to stat
+** the name found inside lib.
+**
+** If member is NOT nil then look for the library object which defines the
+** symbol given by name. If found DmStrDup the name and return make the
+** pointer pointed at by sym point at it. Not handled for now!
+*/
+static time_t
+really_dostat(name, buf)
+char *name;
+struct stat *buf;
+{
+ return( ( DMSTAT(name,buf)==-1
+ || (STOBOOL(Augmake) && (buf->st_mode & S_IFDIR)))
+ ? (time_t)0L
+ : (time_t) buf->st_mtime
+ );
+}
+
+
+PUBLIC time_t
+Do_stat(name, lib, member, force)
+char *name;
+char *lib;
+char **member;
+int force;
+{
+ struct stat buf;
+ time_t seek_arch();
+
+ if( member != NIL(char *) )
+ Fatal("Library symbol names not supported");
+
+ buf.st_mtime = (time_t)0L;
+ if( lib != NIL(char) )
+ return( seek_arch(Basename(name), lib) );
+ else if( strlen(Basename(name)) > NameMax ) {
+ Warning( "Filename [%s] longer than value of NAMEMAX [%d].\n\
+ Assume unix time 0.\n", Basename(name), NameMax );
+ return((time_t)0L);
+ }
+ else if( STOBOOL(UseDirCache) )
+ return(CacheStat(name,force));
+ else
+ return(really_dostat(name,&buf));
+}
+
+
+/* Touch existing file to force modify time to present.
+ */
+PUBLIC int
+Do_touch(name, lib, member)
+char *name;
+char *lib;
+char **member;
+{
+ if( member != NIL(char *) )
+ Fatal("Library symbol names not supported");
+
+ if (lib != NIL(char))
+ return( touch_arch(Basename(name), lib) );
+ else if( strlen(Basename(name)) > NameMax ) {
+ Warning( "Filename [%s] longer than value of NAMEMAX [%d].\n\
+ File timestamp not updated to present time.\n", Basename(name), NameMax );
+ return(-1);
+ }
+ else
+#ifdef HAVE_UTIME_NULL
+ return( utime(name, NULL) );
+#else
+# error "Utime NULL not supported"
+#endif
+}
+
+
+
+PUBLIC void
+Void_lib_cache( lib_name, member_name )/*
+=========================================
+ Void the library cache for lib lib_name, and member member_name. */
+char *lib_name;
+char *member_name;
+{
+ VOID_LCACHE( lib_name, member_name );
+}
+
+
+
+/*
+** return the current time
+*/
+PUBLIC time_t
+Do_time()
+{
+ return (time( NIL(time_t) ));
+}
+
+
+
+/*
+** Print profiling information
+*/
+PUBLIC void
+Do_profile_output( text, mtype, target )
+char *text;
+uint16 mtype;
+CELLPTR target;
+{
+
+ time_t time_sec;
+ uint32 time_msec;
+ char *tstrg;
+ char *tname;
+
+#ifdef HAVE_GETTIMEOFDAY
+ struct timeval timebuffer;
+ gettimeofday(&timebuffer, NULL);
+ time_sec = timebuffer.tv_sec;
+ time_msec = timebuffer.tv_usec/1000;
+#else
+#if defined(_MSC_VER) || defined(__MINGW32__)
+ struct _timeb timebuffer;
+ _ftime( &timebuffer );
+ time_sec = timebuffer.time;
+ time_msec = timebuffer.millitm;
+# else
+ time_sec = time( NIL(time_t) );
+ time_msec = 0;
+# endif
+#endif
+
+ tname = target->CE_NAME;
+ if( mtype & M_TARGET ) {
+ tstrg = "target";
+ /* Don't print special targets .TARGETS and .ROOT */
+ if( tname[0] == '.' && (strcmp(".TARGETS", tname) == 0 || \
+ strcmp(".ROOT", tname) == 0) ) {
+ return;
+ }
+ } else {
+ tstrg = "recipe";
+ }
+
+ /* Don't print shell escape targets if not especially requested. */
+ if( (target->ce_attr & A_SHELLESC) && !(Measure & M_SHELLESC) ) {
+ return;
+ }
+
+ /* Print absolute path if requested. */
+ if( !(target->ce_attr & A_SHELLESC) && (Measure & M_ABSPATH) ) {
+ printf("%s %s %lu.%.3u %s%s%s\n",text, tstrg, time_sec, time_msec, Pwd, DirSepStr, tname);
+ } else {
+ printf("%s %s %lu.%.3u %s\n",text, tstrg, time_sec, time_msec, tname);
+ }
+}
+
+
+
+PUBLIC int
+Do_cmnd(cmd, group, do_it, target, cmnd_attr, last)/*
+=====================================================
+ Execute the string passed in as a command and return
+ the return code. The command line arguments are
+ assumed to be separated by spaces or tabs. The first
+ such argument is assumed to be the command.
+
+ If group is true then this is a group of commands to be fed to the
+ the shell as a single unit. In this case cmd is of the form
+ "file" indicating the file that should be read by the shell
+ in order to execute the command group.
+
+ If Wait_for_completion is TRUE add the A_WFC attribute to the new
+ process.
+*/
+char **cmd; /* Simulate a reference to *cmd. */
+int group; /* if set cmd contains the filename of a (group-)shell
+ * script. */
+int do_it; /* Only execute cmd if not set to null. */
+CELLPTR target;
+t_attr cmnd_attr; /* Attributes for current cmnd. */
+int last; /* Last recipe line in target. */
+{
+ int i;
+
+ DB_ENTER( "Do_cmnd" );
+
+ if( !do_it ) {
+ if( last && !Doing_bang ) {
+ /* Don't execute, just update the target when using '-t'
+ * switch. */
+ Update_time_stamp( target );
+ }
+ DB_RETURN( 0 );
+ }
+
+ /* Stop making the rest of the recipies for this target if an error occured
+ * but the Continue (-k) flag is set to build as much as possible. */
+ if ( target->ce_attr & A_ERROR ) {
+ if ( last ) {
+ Update_time_stamp( target );
+ }
+ DB_RETURN( 0 );
+ }
+
+ if( Max_proc == 1 ) Wait_for_completion = TRUE;
+
+ /* Tell runargv() to wait if needed. */
+ if( Wait_for_completion ) cmnd_attr |= A_WFC;
+
+ /* remove leading whitespace - This should never trigger! */
+ if( iswhite(**cmd) ) {
+ char *p;
+ if( (p = DmStrSpn(*cmd," \t") ) != *cmd )
+ strcpy(*cmd,p);
+ }
+
+ /* set shell if shell metas are found */
+ if( (cmnd_attr & A_SHELL) || group || (*DmStrPbrk(*cmd, Shell_metas)!='\0') )
+ cmnd_attr |= A_SHELL; /* If group is TRUE this doesn't hurt. */
+
+ /* runargv() returns either 0 or 1, 0 ==> command executed, and
+ * we waited for it to return, 1 ==> command started and is still
+ * running. */
+ i = runargv(target, group, last, cmnd_attr, cmd);
+
+ DB_RETURN( i );
+}
+
+
+#define MINARGV 64
+
+PUBLIC char **
+Pack_argv( group, shell, cmd )/*
+================================
+ Take a command and pack it into an argument vector to be executed.
+ If group is true cmd holds the group script file.
+*/
+int group;
+int shell;
+char **cmd; /* Simulate a reference to *cmd. */
+{
+ static char **av = NIL(char *);
+ static int avs = 0;
+ int i = 0;
+ char *s; /* Temporary string pointer. */
+
+ if( av == NIL(char *) ) {
+ TALLOC(av, MINARGV, char*);
+ avs = MINARGV;
+ }
+ av[0] = NIL(char);
+
+ if (**cmd) {
+ if( shell||group ) {
+ char* sh = group ? GShell : Shell;
+
+ if( sh != NIL(char) ) {
+ av[i++] = sh;
+ if( (av[i] = (group?GShell_flags:Shell_flags)) != NIL(char) ) i++;
+
+ if( shell && Shell_quote && *Shell_quote ) {
+ /* Enclose the shell command with SHELLCMDQUOTE. */
+ s = DmStrJoin(Shell_quote, *cmd, -1, FALSE);
+ FREE(*cmd);
+ *cmd = DmStrJoin(s, Shell_quote, -1, TRUE);
+ }
+ av[i++] = *cmd;
+
+#if defined(USE_CREATEPROCESS)
+ /* CreateProcess() needs one long command line. */
+ av[0] = DmStrAdd(av[0], av[1], FALSE);
+ av[1] = NIL(char);
+ /* i == 3 means Shell_flags are given. */
+ if( i == 3 ) {
+ s = av[0];
+ av[0] = DmStrAdd(s, av[2], FALSE);
+ FREE(s);
+ av[2] = NIL(char);
+ }
+ /* The final free of cmd will free the concated command line. */
+ FREE(*cmd);
+ *cmd = av[0];
+#endif
+ av[i] = NIL(char);
+ }
+ else
+ Fatal("%sSHELL macro not defined", group?"GROUP":"");
+ }
+ else {
+ char *tcmd = *cmd;
+
+#if defined(USE_CREATEPROCESS)
+ /* CreateProcess() needs one long command line, fill *cmd
+ * into av[0]. */
+ while( iswhite(*tcmd) ) ++tcmd;
+ if( *tcmd ) av[i++] = tcmd;
+#else
+ /* All other exec/spawn functions need the parameters separated
+ * in the argument vector. */
+ do {
+ /* Fill *cmd into av[]. Whitespace is converted into '\0' to
+ * terminate each av[] member. */
+ while( iswhite(*tcmd) ) ++tcmd;
+ if( *tcmd ) av[i++] = tcmd;
+
+ while( *tcmd != '\0' && !iswhite(*tcmd) ) ++tcmd;
+ if( *tcmd ) *tcmd++ = '\0';
+
+ /* dynamically increase av size. */
+ if( i == avs ) {
+ avs += MINARGV;
+ av = (char **) realloc( av, avs*sizeof(char *) );
+ }
+ } while( *tcmd );
+#endif
+
+ av[i] = NIL(char);
+ }
+ }
+
+ return(av);
+}
+
+
+/*
+** Return the value of ename from the environment
+** if ename is not defined in the environment then
+** NIL(char) should be returned
+*/
+PUBLIC char *
+Read_env_string(ename)
+char *ename;
+{
+ return( getenv(ename) );
+}
+
+
+
+/*
+** Set the value of the environment string ename to value.
+** Returns 0 if success, non-zero if failure
+*/
+PUBLIC int
+Write_env_string(ename, value)
+char *ename;
+char *value;
+{
+ char* p;
+ char* envstr = DmStrAdd(ename, value, FALSE);
+
+ p = envstr+strlen(ename); /* Don't change this code, DmStrAdd does not */
+ *p++ = '='; /* add the space if *value is 0, it does */
+ if( !*value ) *p = '\0'; /* allocate enough memory for one though. */
+
+ return( putenv(envstr) );
+}
+
+
+
+PUBLIC void
+ReadEnvironment()
+{
+ extern char **Rule_tab;
+#if !defined(_MSC_VER)
+#if defined(__BORLANDC__) && __BORLANDC__ >= 0x500
+ extern char ** _RTLENTRY _EXPDATA environ;
+#else
+ extern char **environ;
+#endif
+#endif
+ char **rsave;
+
+#if !defined(__ZTC__) && !defined(_MPW)
+# define make_env()
+# define free_env()
+#else
+ void make_env();
+ void free_env();
+#endif
+
+ make_env();
+
+ rsave = Rule_tab;
+ Rule_tab = environ;
+ Readenv = TRUE;
+
+ Parse( NIL(FILE) );
+
+ Readenv = FALSE;
+ Rule_tab = rsave;
+
+ free_env();
+}
+
+
+
+/*
+** All we have to catch is SIGINT
+*/
+PUBLIC void
+Catch_signals(fn)
+void (*fn)(int);
+{
+ /* FIXME: Check this and add error handling. */
+ if( (void (*)(int)) signal(SIGINT, SIG_IGN) != (void (*)(int))SIG_IGN )
+ signal( SIGINT, fn );
+ if( (void (*)(int)) signal(SIGQUIT, SIG_IGN) != (void (*)(int))SIG_IGN )
+ signal( SIGQUIT, fn );
+}
+
+
+
+/*
+** Clear any previously set signals
+*/
+PUBLIC void
+Clear_signals()
+{
+ if( (void (*)())signal(SIGINT, SIG_IGN) != (void (*)())SIG_IGN )
+ signal( SIGINT, SIG_DFL );
+ if( (void (*)())signal(SIGQUIT, SIG_IGN) != (void (*)())SIG_IGN )
+ signal( SIGQUIT, SIG_DFL );
+}
+
+
+
+/*
+** Set program name
+*/
+PUBLIC void
+Prolog(argc, argv)
+int argc;
+char* argv[];
+{
+ Pname = (argc == 0) ? DEF_MAKE_PNAME : argv[0];
+
+ /* Only some native Windows compilers provide this functionality. */
+#ifdef HAVE_GETMODULEFILENAMEFUNC
+ if( (AbsPname = MALLOC( PATH_MAX, char)) == NIL(char) ) No_ram();
+ GetModuleFileName(NULL, AbsPname, PATH_MAX*sizeof(char));
+#else
+ AbsPname = "";
+#endif
+
+#if __CYGWIN__
+ /* Get the drive letter prefix used by cygwin. */
+ if ( (CygDrvPre = MALLOC( PATH_MAX, char)) == NIL(char) )
+ No_ram();
+ else {
+ int err = cygwin_conv_to_posix_path("c:", CygDrvPre);
+ if (err)
+ Fatal( "error converting \"%s\" - %s\n",
+ CygDrvPre, strerror (errno));
+ if( (CygDrvPreLen = strlen(CygDrvPre)) == 2 ) {
+ /* No prefix */
+ *CygDrvPre = '\0';
+ CygDrvPreLen = 0;
+ } else {
+ /* Cut away the directory letter. */
+ CygDrvPre[CygDrvPreLen-2] = '\0';
+ /* Cut away the leading '/'. We don't free the pointer, i.e. choose
+ * the easy way. */
+ CygDrvPre++;
+ CygDrvPreLen -= 3;
+ }
+ }
+#endif
+
+ /* DirSepStr is used from Clean_path() in Def_cell(). Set it preliminary
+ * here, it will be redefined later in Create_macro_vars() in imacs.c. */
+ DirSepStr = "/";
+
+ Root = Def_cell( ".ROOT" );
+ Targets = Def_cell( ".TARGETS" );
+ Add_prerequisite(Root, Targets, FALSE, FALSE);
+
+ Targets->ce_flag = Root->ce_flag = F_RULES|F_TARGET|F_STAT;
+ Targets->ce_attr = Root->ce_attr = A_NOSTATE|A_PHONY;
+
+ Root->ce_flag |= F_MAGIC;
+ Root->ce_attr |= A_SEQ;
+
+ tzset();
+}
+
+
+
+/*
+** Do any clean up for exit.
+*/
+PUBLIC void
+Epilog(ret_code)
+int ret_code;
+{
+ Write_state();
+ Unlink_temp_files(Root);
+ Hook_std_writes(NIL(char)); /* For MSDOS tee (-F option) */
+ exit( ret_code );
+}
+
+
+
+/*
+** Use the built-in functions of the operating system to get the current
+** working directory.
+*/
+PUBLIC char *
+Get_current_dir()
+{
+ static char buf[PATH_MAX+2];
+
+ if( !getcwd(buf, sizeof(buf)) )
+ Fatal("Internal Error: Error when calling getcwd()!");
+
+#ifdef __EMX__
+ char *slash;
+ slash = buf;
+ while( (slash=strchr(slash,'/')) )
+ *slash = '\\';
+#endif
+
+ return buf;
+}
+
+
+
+/*
+** change working directory
+*/
+PUBLIC int
+Set_dir(path)
+char* path;
+{
+ return( chdir(path) );
+}
+
+
+
+/*
+** return switch char
+*/
+PUBLIC char
+Get_switch_char()
+{
+ return( getswitchar() );
+}
+
+
+int Create_temp(tmpdir, path)/*
+===============================
+ Create a temporary file and open with exclusive access
+ Path is updated with the filename and the file descriptor
+ is returned. Note that the new name should be freed when
+ the file is removed.
+*/
+char *tmpdir;
+char **path;
+{
+ int fd; /* file descriptor */
+
+#if defined(HAVE_MKSTEMP)
+ mode_t mask;
+
+ *path = DmStrJoin( tmpdir, DirSepStr, -1, FALSE);
+ *path = DmStrJoin( *path, "mkXXXXXX", -1, TRUE );
+
+ mask = umask(0066);
+ fd = mkstemp( *path );
+ umask(mask);
+
+#elif defined(HAVE_TEMPNAM)
+ char pidbuff[32];
+#if _MSC_VER >= 1300
+ /* Create more unique filename for .NET2003 and newer. */
+ long npid;
+ long nticks;
+
+ npid = _getpid();
+ nticks = GetTickCount() & 0xfff;
+ sprintf(pidbuff,"mk%d_%d_",npid,nticks);
+#else
+ sprintf(pidbuff,"mk");
+#endif
+ *path = tempnam(tmpdir, pidbuff);
+ fd = open(*path, O_CREAT | O_EXCL | O_TRUNC | O_RDWR, 0600);
+#else
+
+#error mkstemp() or tempnam() is needed
+
+#endif
+
+ return fd;
+}
+
+
+PUBLIC FILE*
+Get_temp(path, mode)/*
+======================
+ Generate a temporary file name and open the file for writing.
+ If a name cannot be generated or the file cannot be opened
+ return -1, else return the fileno of the open file.
+ and update the source file pointer to point at the new file name.
+ Note that the new name should be freed when the file is removed.
+ The file stream is opened with the given mode.
+*/
+char **path;
+char *mode;
+{
+ int fd;
+ FILE *fp;
+ char *tmpdir;
+ int tries = 20;
+
+ tmpdir = Read_env_string( "TMPDIR" );
+ if( tmpdir == NIL(char) )
+ tmpdir = "/tmp";
+
+ while( --tries )
+ {
+ /* This sets path to the name of the created temp file. */
+ if( (fd = Create_temp(tmpdir, path)) != -1)
+ break;
+
+ free(*path); /* free var if creating temp failed. */
+ }
+
+ if( fd != -1)
+ {
+ Def_macro( "TMPFILE", DO_WINPATH(*path), M_MULTI|M_EXPANDED );
+ /* associate stream with file descriptor */
+ fp = fdopen(fd, mode);
+ }
+ else
+ fp = NIL(FILE);
+
+ return fp;
+}
+
+
+PUBLIC FILE *
+Start_temp( suffix, cp, fname )/*
+=================================
+ Open a new temporary file and set it up for writing. The file is linked
+ to cp and will be removed if once the target is finished.
+ If a suffix for the temporary files is requested two temporary files are
+ created. This is done because the routines that create a save temporary
+ file do not provide a definable suffix. The first (provided by Get_temp())
+ is save and unique and the second file is generated by adding the desired
+ suffix the the first temporary file. The extra file is also linked to cp
+ so that it gets removed later.
+ The function returns the FILE pointer to the temporary file (with suffix
+ if specified) and leaves the file name in *fname.
+*/
+char *suffix;
+CELLPTR cp;
+char **fname;
+{
+ FILE *fp, *fp2;
+ char *tmpname;
+ char *name;
+ char *fname_suff;
+
+ name = (cp != NIL(CELL))?cp->CE_NAME:"makefile text";
+
+ /* This sets tmpname to the name that was used. */
+ if( (fp = Get_temp(&tmpname, "w")) == NIL(FILE) )
+ Open_temp_error( tmpname, name );
+
+ /* Don't free tmpname, it's stored in a FILELIST member in Link_temp(). */
+ Link_temp( cp, fp, tmpname );
+ *fname = tmpname;
+
+ /* As Get_temp() doesn't provide a definable suffix (anymore) we create an
+ * additional temporary file with that suffix. */
+ if ( suffix && *suffix ) {
+
+#ifdef HAVE_MKSTEMP
+ /* Only use umask if we are also using mkstemp - this basically
+ * avoids using the incompatible implementation from MSVC. */
+ mode_t mask;
+
+ mask = umask(0066);
+#endif
+
+ fname_suff = DmStrJoin( tmpname, suffix, -1, FALSE );
+
+ /* Overwrite macro, Get_temp didn't know of the suffix. */
+ Def_macro( "TMPFILE", DO_WINPATH(fname_suff), M_MULTI|M_EXPANDED );
+
+ if( (fp2 = fopen(fname_suff, "w" )) == NIL(FILE) )
+ Open_temp_error( fname_suff, name );
+#ifdef HAVE_MKSTEMP
+ umask(mask);
+#endif
+
+ /* Don't free fname_suff. */
+ Link_temp( cp, fp2, fname_suff );
+ fp = fp2;
+ *fname = fname_suff;
+ }
+
+ return( fp );
+}
+
+
+/*
+** Issue an error on failing to open a temporary file
+*/
+PUBLIC void
+Open_temp_error( tmpname, name )
+char *tmpname;
+char *name;
+{
+ Fatal("Cannot open temp file `%s' while processing `%s'", tmpname, name );
+}
+
+
+/*
+** Link a temp file onto the list of files.
+*/
+PUBLIC void
+Link_temp( cp, fp, fname )
+CELLPTR cp;
+FILE *fp;
+char *fname;
+{
+ FILELISTPTR new;
+
+ if( cp == NIL(CELL) ) cp = Root;
+
+ TALLOC( new, 1, FILELIST );
+
+ new->fl_next = cp->ce_files;
+ new->fl_name = fname;
+ new->fl_file = fp; /* indicates temp file is open */
+
+ cp->ce_files = new;
+}
+
+
+/*
+** Close a previously used temporary file.
+*/
+PUBLIC void
+Close_temp(cp, file)
+CELLPTR cp;
+FILE *file;
+{
+ FILELISTPTR fl;
+ if( cp == NIL(CELL) ) cp = Root;
+
+ for( fl=cp->ce_files; fl && fl->fl_file != file; fl=fl->fl_next );
+ if( fl ) {
+ fl->fl_file = NIL(FILE);
+ fclose(file);
+ }
+}
+
+
+/*
+** Clean-up, and close all temporary files associated with a target.
+*/
+PUBLIC void
+Unlink_temp_files( cp )/*
+==========================
+ Unlink the tempfiles if any exist. Make sure you close the files first
+ though. This ensures that under DOS there is no disk space lost. */
+CELLPTR cp;
+{
+ FILELISTPTR cur, next;
+
+ if( cp == NIL(CELL) || cp->ce_files == NIL(FILELIST) ) return;
+
+ for( cur=cp->ce_files; cur != NIL(FILELIST); cur=next ) {
+ next = cur->fl_next;
+
+ if( cur->fl_file ) fclose( cur->fl_file );
+
+ if( Verbose & V_LEAVE_TMP )
+ fprintf( stderr, "%s: Left temp file [%s]\n", Pname, cur->fl_name );
+ else
+ (void) Remove_file( cur->fl_name );
+
+ FREE(cur->fl_name);
+ FREE(cur);
+ }
+
+ cp->ce_files = NIL(FILELIST);
+}
+
+
+PUBLIC void
+Handle_result(status, ignore, abort_flg, target)/*
+==================================================
+ Handle return value of recipe.
+*/
+int status;
+int ignore;
+int abort_flg;
+CELLPTR target;
+{
+ status = ((status&0xff)==0 ? status>>8 /* return from exit() */
+ : (status & 0xff)==SIGTERM ? -1 /* terminated from SIGTERM */
+ : (status & 0x7f)+128); /* terminated from signal
+ * ( =status-128 ) */
+
+ if( status ) {
+ if( !abort_flg ) {
+ char buf[512];
+
+ sprintf(buf, "%s: Error code %d, while making '%s'",
+ Pname, status, target->ce_fname );
+
+ if( ignore || Continue ) {
+ if (!(Glob_attr & A_SILENT)) {
+ strcat(buf, " (Ignored" );
+
+ if ( Continue ) {
+ /* Continue after error if '-k' was used. */
+ strcat(buf,",Continuing");
+ target->ce_attr |= A_ERROR;
+ }
+ strcat(buf,")");
+ if (Verbose)
+ fprintf(stderr, "%s\n", buf);
+ }
+
+ if( target->ce_attr & A_ERRREMOVE
+ && Remove_file( target->ce_fname ) == 0
+ && !(Glob_attr & A_SILENT))
+ fprintf(stderr,"%s: '%s' removed.\n", Pname, target->ce_fname);
+ }
+ else {
+ fprintf(stderr, "%s\n",buf);
+
+ if(!(target->ce_attr & A_PRECIOUS)||(target->ce_attr & A_ERRREMOVE))
+ if( Remove_file( target->ce_fname ) == 0 )
+ fprintf(stderr,"%s: '%s' removed.\n", Pname,
+ target->ce_fname);
+
+ Quit(0);
+ }
+ }
+ else if(!(target->ce_attr & A_PRECIOUS)||(target->ce_attr & A_ERRREMOVE))
+ Remove_file( target->ce_fname );
+ }
+}
+
+
+PUBLIC void
+Update_time_stamp( cp )/*
+=========================
+ Update the time stamp of cp and scan the list of its prerequisites for
+ files being marked as removable (ie. an inferred intermediate node).
+ Remove them if there are any. */
+CELLPTR cp;
+{
+ HASHPTR hp;
+ LINKPTR dp;
+ CELLPTR tcp;
+ time_t mintime;
+ int phony = ((cp->ce_attr&A_PHONY) != 0);
+
+ for(dp=CeMeToo(cp); dp; dp=dp->cl_next) {
+ tcp=dp->cl_prq;
+ /* When calling Make() on this target ce_time was set to the minimal
+ * required time the target should have after building, i.e. the time
+ * stamp of the newest prerequisite or 1L if there is no
+ * prerequisite. */
+ mintime = tcp->ce_time;
+
+ if( tcp->ce_attr & A_LIBRARY )
+ Void_lib_cache( tcp->ce_fname, NIL(char) );
+ else if( !Touch && (tcp->ce_attr & A_LIBRARYM) )
+ Void_lib_cache( tcp->ce_lib, tcp->ce_fname );
+
+ /* phony targets are treated as if they were recently made
+ * and get the current time assigned. */
+ if( phony ) {
+ tcp->ce_time = Do_time();
+ }
+ else if (Trace) {
+ tcp->ce_time = Do_time();
+ }
+ else {
+ Stat_target(tcp, -1, TRUE);
+
+ if( tcp->ce_time == (time_t) 0L ) {
+ /* If the target does not exist after building set its
+ * time stamp depending if it has recipes or not. Virtual
+ * Targets (without recipes) get the newest time stamp of
+ * its prerequisites assigned. (This was conveniently stored
+ * in mintime.)
+ * Targets with recipes are treated as if they were recently
+ * made and get the current time assigned. */
+ if( cp->ce_recipe == NIL(STRING) && mintime > 1 ) {
+ tcp->ce_time = mintime;
+ }
+ else {
+ tcp->ce_time = Do_time();
+ }
+ }
+ else {
+ /* The target exist. If the target does not have recipe
+ * lines use the newest time stamp of either the target or
+ * the newest time stamp of its prerequisites and issue
+ * a warning. */
+ if( cp->ce_recipe == NIL(STRING) ) {
+ time_t newtime = ( mintime > 1 ? mintime : Do_time() );
+
+ if( !(tcp->ce_attr & A_SILENT) )
+ Warning( "Found file corresponding to virtual target [%s].",
+ tcp->CE_NAME );
+
+ if( newtime > tcp->ce_time )
+ tcp->ce_time = mintime;
+ }
+ }
+ }
+
+ if( Trace ) {
+ tcp->ce_flag |= F_STAT; /* pretend we stated ok */
+ }
+
+ if( Verbose & V_MAKE )
+ printf( "%s: <<<< Set [%s] time stamp to %lu\n",
+ Pname, tcp->CE_NAME, tcp->ce_time );
+
+ if( Measure & M_TARGET )
+ Do_profile_output( "e", M_TARGET, tcp );
+
+ /* At this point cp->ce_time is updated to either the actual file
+ * time or the current time. */
+ DB_PRINT( "make", ("time stamp: %ld, required mintime: %ld",
+ cp->ce_time, mintime) );
+ if( tcp->ce_time < mintime && !(tcp->ce_attr & A_SILENT) ) {
+ Warning( "Target [%s] was made but the time stamp has not been updated.",
+ tcp->CE_NAME );
+ }
+
+ /* The target was made, remove the temp files now. */
+ Unlink_temp_files( tcp );
+ tcp->ce_flag |= F_MADE;
+ tcp->ce_attr |= A_UPDATED;
+ }
+
+ /* Scan the list of prerequisites and if we find one that is
+ * marked as being removable, (ie. an inferred intermediate node)
+ * then remove it. We remove a prerequisite by running the recipe
+ * associated with the special target .REMOVE.
+ * Typically .REMOVE is defined in the startup file as:
+ * .REMOVE :; $(RM) $<
+ * with $< being the list of prerequisites specified in the current
+ * target. (Make() sets $< .) */
+
+ /* Make sure we don't try to remove prerequisites for the .REMOVE
+ * target. */
+ if( strcmp(cp->CE_NAME,".REMOVE") != 0 &&
+ (hp = Get_name(".REMOVE", Defs, FALSE)) != NIL(HASH) ) {
+ register LINKPTR dp;
+ int flag = FALSE;
+ int rem;
+ t_attr attr;
+
+ tcp = hp->CP_OWNR;
+
+ /* The .REMOVE target is re-used. Remove old prerequisites. */
+ tcp->ce_flag |= F_TARGET;
+ Clear_prerequisites( tcp );
+
+ for(dp=cp->ce_prq; dp != NIL(LINK); dp=dp->cl_next) {
+ register CELLPTR prq = dp->cl_prq;
+
+ attr = Glob_attr | prq->ce_attr;
+ /* We seem to have problems here that F_MULTI subtargets get removed
+ * that even though they are still needed because the A_PRECIOUS
+ * was not propagated correctly. Solution: Don't remove subtargets, the
+ * master target will be removed if is not needed. */
+ rem = (prq->ce_flag & F_REMOVE) &&
+ (prq->ce_flag & F_MADE ) &&
+ !(prq->ce_count ) && /* Don't remove F_MULTI subtargets. */
+ !(prq->ce_attr & A_PHONY) &&
+ !(attr & A_PRECIOUS);
+
+ /* remove if rem is != 0 */
+ if(rem) {
+ LINKPTR tdp;
+
+ /* Add the target plus all that are linked to it with the .UPDATEALL
+ * attribute. */
+ for(tdp=CeMeToo(prq); tdp; tdp=tdp->cl_next) {
+ CELLPTR tmpcell=tdp->cl_prq;
+
+ (Add_prerequisite(tcp,tmpcell,FALSE,FALSE))->cl_flag|=F_TARGET;
+ tmpcell->ce_flag &= ~F_REMOVE;
+ }
+ flag = TRUE;
+ }
+ }
+
+ if( flag ) {
+ int sv_force = Force;
+
+ Force = FALSE;
+ Remove_prq( tcp );
+ Force = sv_force;
+
+ for(dp=tcp->ce_prq; dp != NIL(LINK); dp=dp->cl_next) {
+ register CELLPTR prq = dp->cl_prq;
+
+ prq->ce_flag &= ~(F_MADE|F_VISITED|F_STAT);
+ prq->ce_flag |= F_REMOVE;
+ prq->ce_time = (time_t)0L;
+ }
+ }
+ }
+}
+
+
+PUBLIC int
+Remove_file( name )
+char *name;
+{
+ struct stat buf;
+
+ if( stat(name, &buf) != 0 )
+ return 1;
+ if( (buf.st_mode & S_IFMT) == S_IFDIR )
+ return 1;
+ return(unlink(name));
+}
+
+
+#if defined(__CYGWIN__)
+char *
+cygdospath(char *src, int winpath)/*
+====================================
+ Convert to DOS path if winpath is true. The returned pointer is
+ either the original pointer or a pointer to a static buffer.
+*/
+{
+ static char *buf = NIL(char);
+
+ if ( !buf && ( (buf = MALLOC( PATH_MAX, char)) == NIL(char) ) )
+ No_ram();
+
+ DB_PRINT( "cygdospath", ("converting [%s] with winpath [%d]", src, winpath ) );
+
+ /* Return immediately on NULL pointer or when .WINPATH is
+ * not set. */
+ if( !src || !winpath )
+ return src;
+
+ if( *src && src[0] == '/' ) {
+ char *tmp;
+ int err = cygwin_conv_to_win32_path(src, buf);
+ if (err)
+ Fatal( "error converting \"%s\" - %s\n",
+ src, strerror (errno));
+
+ tmp = buf;
+ while ((tmp = strchr (tmp, '\\')) != NULL) {
+ *tmp = '/';
+ tmp++;
+ }
+
+ return buf;
+ }
+ else
+ return src;
+}
+#endif