/* @(#)make.c 1.136 07/03/07 Copyright 1985, 87, 88, 91, 1995-2007 J. Schilling */ #ifndef lint static char sccsid[] = "@(#)make.c 1.136 07/03/07 Copyright 1985, 87, 88, 91, 1995-2007 J. Schilling"; #endif /* * Make program * * Copyright (c) 1985, 87, 88, 91, 1995-2007 by J. Schilling */ /* * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * See the file CDDL.Schily.txt in this distribution for details. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file CDDL.Schily.txt from this distribution. */ #include #include #include #include #include #include #include #include #include #include #include #include "make.h" #include #include #include #include #if defined(__EMX__) || defined(__DJGPP__) #include #endif #include #include char make_version[] = "1.2a41"; #ifdef _FASCII LOCAL void setup_env __PR((void)); #endif EXPORT void usage __PR((int exitcode)); LOCAL void initmakefiles __PR((void)); LOCAL int addmakefile __PR((char *name)); LOCAL void read_defs __PR((void)); LOCAL void read_makefiles __PR((void)); EXPORT void setup_dotvars __PR((void)); LOCAL void setup_vars __PR((void)); LOCAL void setup_MAKE __PR((char *name)); EXPORT char *searchtype __PR((int mode)); LOCAL void printdirs __PR((void)); LOCAL int addcommandline __PR((char * name)); LOCAL void read_cmdline __PR((void)); EXPORT void doexport __PR((char *)); LOCAL void read_environ __PR((void)); EXPORT int main __PR((int ac, char ** av)); LOCAL void check_old_makefiles __PR((void)); LOCAL void getmakeflags __PR((void)); LOCAL void read_makemacs __PR((void)); LOCAL char *nextmakemac __PR((char *s)); LOCAL BOOL read_mac __PR((char *mf)); LOCAL void setmakeflags __PR((void)); LOCAL char *stripmacros __PR((char *macbase, char *new)); LOCAL void setmakeenv __PR((char *envbase, char *envp)); EXPORT int docmd __PR((char * cmd, obj_t * obj)); EXPORT BOOL move_tgt __PR((obj_t * from)); LOCAL int copy_file __PR((char * from, char * objname)); EXPORT BOOL touch_file __PR((char * name)); LOCAL date_t gcurtime __PR((void)); LOCAL date_t gnewtime __PR((void)); EXPORT date_t gftime __PR((char * file)); LOCAL BOOL isdir __PR((char * file)); EXPORT Llong gfileid __PR((char * file)); EXPORT char *prtime __PR((date_t date)); LOCAL void handler __PR((int signo)); LOCAL void exhandler __PR((int excode, void *arg)); EXPORT char *curwdir __PR((void)); LOCAL char *getdefaultsfile __PR((void)); LOCAL char *searchfileinpath __PR((char *name)); LOCAL char *searchonefile __PR((char *name, char *nbuf, char *np, char *ep)); #ifndef HAVE_PUTENV EXPORT int putenv __PR((const char *new)); #endif #if defined(__DJGPP__) LOCAL char *strbs2s __PR((char *s)); #endif BOOL posixmode = FALSE; /* We found a .POSIX target */ BOOL Eflag = FALSE; /* -e Environment overrides vars*/ BOOL Iflag = FALSE; /* -i Ignore command errors */ BOOL Kflag = FALSE; /* -k Continue on unrelated tgts */ BOOL Stopflag = FALSE; /* -S Stop on make errors */ BOOL NSflag = FALSE; /* -N Ignore no Source on dep. */ BOOL Nflag = FALSE; /* -n Only show what to do */ BOOL Qflag = FALSE; /* -q If up to date exit (0) */ BOOL Rflag = FALSE; /* -r Turn off internal rules */ BOOL Sflag = FALSE; /* -s Be silent */ BOOL Tflag = FALSE; /* -t Touch objects */ int Mlevel = 0; /* MAKE_LEVEL from environment */ int Debug = 0; /* -d Print reason for rebuild */ int XDebug = 0; /* -xd Print extended debug info*/ BOOL Prdep = FALSE; /* -xM Print include dependency */ BOOL Probj = FALSE; /* -probj Print object tree */ BOOL Print = FALSE; /* -p Print macro/target definitions*/ int Dmake = 0; /* -D Display makefile */ BOOL help = FALSE; /* -help Show Usage */ BOOL pversion = FALSE; /* -version Show version string */ BOOL NoWarn = FALSE; /* -w No warnings */ BOOL DoWarn = FALSE; /* -W Print extra warnings */ char Makeflags[] = "MAKEFLAGS"; char Make_Flags[] = "MAKE_FLAGS"; char Make_Macs[] = "MAKE_MACS"; char Make_Level[] = "MAKE_LEVEL"; char Envdefs[] = "Environment defs"; char Makedefs[] = "Internal Makefile"; char Ldefaults[] = "defaults.smk"; #ifdef SVR4 char Defaults[] = "/opt/schily/lib/defaults.smk"; #else char Defaults[] = "/usr/bert/lib/defaults.smk"; #endif #define MAKEFILECOUNT 32 /* Max number of Makefiles */ char SMakefile[] = "SMakefile"; /* smake's default Makefile */ char Makefile[] = "Makefile"; /* Primary default Makefile */ char makefile[] = "makefile"; /* Secondary default Makefile */ char **MakeFileNames; /* To hold all Makefilenames */ int Mfileindex; /* Current Makefile index */ int Mfilesize; /* Size of Makefile array */ int Mfilecount; /* Number of Makefiles found+2 */ char CmdLMac[] = "Command Line Macro"; /* Makefile Name for .. */ char **CmdLDefs; /* To hold all Cmdline Macros */ int Cmdlinecount; /* Number of Cmdline Macros */ int Cmdlinesize; /* Size of Cmdline Macro array */ char *MFCmdline; /* Pointer to Cmdl. Macs fr. env*/ int Mflags; char *ObjDir; /* .OBJDIR: pathname Target destination dir */ int ObjDirlen; /* strlen(.OBJDIR) */ int ObjSearch = SALL; /* .OBJSEARCH: searchtype for explicit rules*/ list_t *SearchList; /* .SEARCHLIST: list of src/obj dir pairs */ list_t *Suffixes; /* .SUFFIXES: list of suffixes (POSIX) */ BOOL SSuffrules; obj_t *Init; /* .INIT: command to execute at startup */ obj_t *Done; /* .DONE: command do execute on success */ obj_t *Failed; /* .FAILED: command to execute on failure */ obj_t *IncludeFailed; /* .INCLUDE_FAILED: cmd to exec if missing */ obj_t *Deflt; /* .DEFAULT: command to execute if no rule */ obj_t *Precious; /* .PRECIOUS: list of targets not to remove */ obj_t *Phony; /* .PHONY: list of false targets, no check */ obj_t *curtarget; /* current target of actually running cmd */ date_t curtime; /* Current time */ date_t newtime; /* Special time newer than all */ /* * POSIX requieres commands to be executed via 'sh -c command' which is * wrong as it causes make not to stop on errors if called with complex * commands (e.g. commands that contain a ';'). * * We used to call /bin/sh -ce 'cmd' in former times which was correct, * but we got (unfortunately undocumented) problems on QNX and we changed * it to sh -c 'cmd' on 00/11/19. In hope that newer versions of QNX have * no problems with sh -ce, we changed it back on May 9th 2004. * * XXX Switching between both variants via .POSIX does not look like a good * XXX idea. We rather try to send a bug report against the POSIX standard. */ #define POSIX_SHELL_CEFLAG "-c" /* Does not work correctly */ #define SHELL_CEFLAG "-ce" /* Needed for correct behavior */ #define SHELL_CFLAG "-c" /* Used with make -i */ LOCAL char ceflag[] = SHELL_CEFLAG; /* Used by default for cmds */ LOCAL char cflag[] = SHELL_CFLAG; /* Used with make -i */ #define MINSECS 60 #define HOURSECS (MINSECS*60) #define DAYSECS (HOURSECS*24) #define MONTHSECS (DAYSECS*30) #define YEARSECS (DAYSECS*365) char Nullstr[] = ""; char slash[] = PATH_DELIM_STR; int slashlen = sizeof (PATH_DELIM_STR) -1; /* strlen(slash) */ LOCAL BOOL xpatrules; /* Have non local pattern rules in Makefile */ EXPORT int xssrules; /* Have non local simple suffix rules */ #ifdef _FASCII /* Mark Williams C */ char *_stksize = (char *) 8192; /* * Old and probably outdated setup that was needed in 1986 to make * 'smake' run on a ATARI ST using the Marc Williams C development tools. */ LOCAL void setup_env() { register char *ep; extern char *getenv(); if ((ep = getenv("PATH")) == (char *) NULL || *ep == '\0') putenv("PATH=.bin,,\\bin,\\lib"); if ((ep = getenv("SUFF")) == (char *) NULL || *ep == '\0') putenv("SUFF=,.prg,.tos,.ttp"); if ((ep = getenv("LIBPATH")) == (char *) NULL || *ep == '\0') putenv("LIBPATH=\\lib,\\bin"); if ((ep = getenv("TMPDIR")) == (char *) NULL || *ep == '\0') putenv("TMPDIR=\\tmp"); if ((ep = getenv("INCDIR")) == (char *) NULL || *ep == '\0') putenv("INCDIR=\\include"); } #endif EXPORT void usage(exitcode) int exitcode; { error("Usage: smake [options] [target...] [macro=value...]\n"); error("Options:\n"); error(" -e Environment overrides variables in Makefile.\n"); error(" -i Ignore errors from commands.\n"); error(" -k Ignore target errors, continue on unrelated targets.\n"); error(" -N Continue if no source for nonexistent dependencies found.\n"); error(" -n Don't make - only say what to do.\n"); error(" -p Print all macro and target definitions.\n"); error(" -q Question mode. Exit code is 0 if target is up to date.\n"); error(" -r Turn off internal rules.\n"); error(" -s Be silent.\n"); error(" -t Touch Objects instead of executing defined commands.\n"); error(" -w Don't print warning Messages.\n"); error(" -W Print extra (debug) warning Messages.\n"); error(" -D Display Makefiles as read in.\n"); error(" -DD Display Makefiles/Rules as read in.\n"); error(" -d Print reason why a target has to be rebuilt.\n"); error(" -dd Debug dependency check.\n"); error(" -xM Print include dependency.\n"); error(" -xd Print extended debug info.\n"); error(" -probj Print object tree.\n"); error(" -help Print this help.\n"); error(" -version Print version number.\n"); error(" -posix Force POSIX behaviour.\n"); error(" mf=makefilename | -f makefilename\n"); error("More than -f makefile option may be specified.\n"); exit(exitcode); } LOCAL void initmakefiles() { /* * Mfilecount als Index fuer MakeFileNames[] bei addmakefile() * Mfileindex als globale Index Variable fuer den Parser * * This code needs to be kept in sync with the MF_IDX_* #defines * in make.h */ Mfilecount = 0; addmakefile(Makedefs); /* Implicit rules */ addmakefile(Envdefs); /* Environment strings */ addmakefile(Makefile); /* Default make file */ Mfilecount--; /* -f name overwrites Makefile */ } /* * Add a new makefile to the list of makefiles. * Called by getargs() if a -f option was found. */ LOCAL int addmakefile(name) char *name; { if (MakeFileNames == NULL) { /* * Use a default size of 4 as we usually have 4 Makefiles. */ Mfilesize = 4; MakeFileNames = malloc(Mfilesize * sizeof (char *)); } else if (Mfilesize <= (Mfilecount+1)) { /* One spare for CmdLMac */ Mfilesize += 4; MakeFileNames = realloc(MakeFileNames, Mfilesize * sizeof (char *)); } if (MakeFileNames == NULL) comerr("No memory for Makefiles.\n"); MakeFileNames[Mfilecount++] = name; return (1); } /* * Read the default rules - either compiled in or from file. */ LOCAL void read_defs() { int MFsave = Mfileindex; char *deflts; extern char implicit_rules[]; /* Default rules compiled into make */ Dmake--; Mfileindex = MF_IDX_IMPICIT; /* Index 0 Implicit Rules */ if (gftime(Ldefaults) != 0) { MakeFileNames[0] = Ldefaults; readfile(Ldefaults, TRUE); #ifdef DEFAULTS_PATH_SEARCH_FIRST } else if ((deflts = getdefaultsfile()) != NULL) { MakeFileNames[0] = deflts; readfile(deflts, TRUE); #endif #ifdef DEFAULTS_PATH /* This is the install path */ } else if (gftime(DEFAULTS_PATH) != 0) { MakeFileNames[0] = DEFAULTS_PATH; readfile(DEFAULTS_PATH, TRUE); #endif } else if (gftime(Defaults) != 0) { MakeFileNames[0] = Defaults; readfile(Defaults, TRUE); #ifndef DEFAULTS_PATH_SEARCH_FIRST } else if ((deflts = getdefaultsfile()) != NULL) { MakeFileNames[0] = deflts; readfile(deflts, TRUE); #endif } else { readstring(implicit_rules, Makedefs); } Dmake++; Mfileindex = MFsave; } /* * Read in all external makefiles. Use either the names from command line * or look for the default names "Makefile" and "makefile". */ LOCAL void read_makefiles() { int MFsave = Mfileindex; patr_t *oPatrules = Patrules; patr_t **opattail = pattail; Patrules = 0; pattail = &Patrules; xssrules = 0; Mfileindex = MF_IDX_MAKEFILE; /* Index 2 Default Makefile */ if (Mfilecount == MF_IDX_MAKEFILE) { /* * First look for "SMakefile", * then for "Makefile", then for "makefile" */ if (gftime(SMakefile) != 0) { /* "SMakefile" */ Mfilecount++; MakeFileNames[2] = SMakefile; } else if (gftime(Makefile) != 0) { /* "Makefile" */ Mfilecount++; MakeFileNames[2] = Makefile; } else if (gftime(makefile) != 0) { /* "makefile" */ Mfilecount++; MakeFileNames[2] = makefile; } } while (Mfileindex < Mfilecount) { readfile(MakeFileNames[Mfileindex], TRUE); Mfileindex++; } /* * Check for external pattern rule definitions. */ xpatrules = Patrules != NULL; /* * The pattern rules which are defined in the external makefiles * must superseede the pattern rules from the internal rules. * Concat the pattern rules found in internal rules to the end of * the patern rules list from external makefiles. */ *pattail = oPatrules; pattail = opattail; Mfileindex = MFsave; } /* * Setup special variables. * * NOTE: Must be reentrant because it may be called more than once * if the "include" directive is used. * As there is (currently) no way to delete an object * it is OK not to do anything special if objlook() returns NULL. */ EXPORT void setup_dotvars() { obj_t *obj; list_t *l; char *name; obj = objlook(".OBJDIR", FALSE); if (obj != NULL && obj->o_type != ':') /* Must be a special target */ obj = NULL; if (obj != NULL) { if (obj->o_list && (ObjDir = obj->o_list->l_obj->o_name)) { if (gfileid(ObjDir) == gfileid(".")) { /* * Do not allow moving targets to themselves. */ ObjDir = NULL; } ObjDirlen = strlen(ObjDir); } } #ifdef no_longer_needed /* Has been moved to dynmac expansion */ /* * XXX We cannot do this here as we are called more than once and * XXX we like to allow $O to be overwritten from Makefiles that * XXX do not know about smake's special features. */ /* * Create special variable $O -> ObjDir */ define_var("O", ObjDir ? ObjDir : "."); } #endif obj = objlook(".OBJSEARCH", FALSE); if (obj != NULL && obj->o_type != ':') /* Must be a special target */ obj = NULL; if (obj != NULL) { if ((l = obj->o_list) != NULL) { name = l->l_obj->o_name; if (streql("src", name)) { ObjSearch = SSRC; } else if (streql("obj", name)) { ObjSearch = SOBJ; } else if (streql("all", name)) { ObjSearch = SALL; } else { /* * This is the default. */ ObjSearch = SALL; } } } obj = objlook(".SEARCHLIST", FALSE); if (obj != NULL && obj->o_type != ':') /* Must be a special target */ obj = NULL; if (obj != NULL) { SearchList = obj->o_list; } else if ((obj = objlook("VPATH", FALSE)) != NULL && obj->o_type == '=') { SearchList = cvtvpath(obj->o_list); } if (objlook(".IGNORE", FALSE)) Iflag = TRUE; if (objlook(".SILENT", FALSE)) Sflag = TRUE; Init = objlook(".INIT", FALSE); Done = objlook(".DONE", FALSE); Failed = objlook(".FAILED", FALSE); IncludeFailed = objlook(".INCLUDE_FAILED", FALSE); if (IncludeFailed != NULL && IncludeFailed->o_cmd == NULL) IncludeFailed = NULL; Deflt = objlook(".DEFAULT", FALSE); Precious = objlook(".PRECIOUS", FALSE); Phony = objlook(".PHONY", FALSE); if (objlook(".POSIX", FALSE)) posixmode = TRUE; obj = objlook(".SUFFIXES", FALSE); if (obj != NULL && obj->o_type != ':') /* Must be a special target */ obj = NULL; if (obj != NULL) Suffixes = obj->o_list; if (Debug > 1 && Suffixes != (list_t *) NULL) { register list_t *p; error(".SUFFIXES :\t"); for (p = Suffixes; p; p = p->l_next) error("%s ", p->l_obj->o_name); error("\n"); } SSuffrules = check_ssufftab(); } /* * Set up some known special macros. */ LOCAL void setup_vars() { int i; char make_level[64]; char *p; define_var("$", "$"); /* Really needed ? */ define_var("MAKE_NAME", "smake"); /* Needed to identify syntax */ define_var("MAKE_VERSION", make_version); /* Version dependant files? */ if ((p = getenv(Make_Level)) != NULL) { p = astoi(p, &i); if (*p == '\0') Mlevel = i; } snprintf(make_level, sizeof (make_level), "%d", Mlevel+1); define_var(Make_Level, make_level); doexport(Make_Level); } /* * Set up the special macro $(MAKE). * If we were called with an absolute PATH or without any '/', use argv[0], * else compute the absolute PATH by prepending working dir to argv[0]. */ LOCAL void setup_MAKE(name) char *name; { char wd[MAXPATHNAME + 1]; int len; /* * If argv[0] starts with a slash or contains no slash, * or on DOS like OS starts with MS-DOS drive letter, * it is useful as $(MAKE). */ #ifdef HAVE_DOS_DRIVELETTER if (name[0] == SLASH || strchr(name, SLASH) == NULL || name[1] == ':') { #else if (name[0] == SLASH || strchr(name, SLASH) == NULL) { #endif define_var("MAKE", name); } else { /* * Compute abs pathname for $(MAKE) */ strncpy(wd, curwdir(), sizeof (wd)); wd[sizeof (wd)-1] = '\0'; len = strlen(wd); if ((strlen(name) + len + 2) < sizeof (wd)) { strcat(wd, PATH_DELIM_STR); strcat(wd, name); } define_var("MAKE", wd); } } /* * Transfer object search types into human readable names. */ EXPORT char * searchtype(mode) int mode; { if (mode == SSRC) return ("src"); if (mode == SOBJ) return ("obj"); if (mode == SALL) return ("all"); return ("invalid Object search mode"); } /* * Print some 'smake' special macros: * .OBJDIR, .SEARCHLIST and .OBJSEARCH */ LOCAL void printdirs() { if (ObjDir != NULL) error(".OBJDIR :\t%s\n", ObjDir); error(".OBJSEARCH :\t%s\n", searchtype(ObjSearch)); if (SearchList != (list_t *) NULL) { register list_t *p; error(".SEARCHLIST :\t"); for (p = SearchList; p; p = p->l_next) error("%s ", p->l_obj->o_name); error("\n"); } } /* * Add a command line macro to our list. * This is called by getargs(). */ LOCAL int addcommandline(name) char *name; { if (Debug > 1) error("got_it: %s\n", name); /* UNIX make: ":;=$\n\t" */ if (!strchr(name, '=')) return (NOTAFILE); /* Tell getargs that this may be a flag */ if (CmdLDefs == NULL) { Cmdlinesize = 8; CmdLDefs = malloc(Cmdlinesize * sizeof (char *)); } else if (Cmdlinesize <= Cmdlinecount) { Cmdlinesize += 8; CmdLDefs = realloc(CmdLDefs, Cmdlinesize * sizeof (char *)); } if (CmdLDefs == NULL) comerr("No memory for Commandline Macros.\n"); CmdLDefs[Cmdlinecount++] = name; return (1); } /* * Read in and parse all command line macro definitions from list. */ LOCAL void read_cmdline() { int MFsave = Mfileindex; register int i; if (Cmdlinecount == 0) return; /* * Register the command line macros past the last makefile */ Mfileindex = Mfilecount; if (Mfileindex == MF_IDX_MAKEFILE) Mfileindex = MF_IDX_MAKEFILE + 1; MakeFileNames[Mfileindex] = CmdLMac; Mflags |= F_READONLY; for (i = 0; i < Cmdlinecount; i++) { readstring(CmdLDefs[i], CmdLMac); putenv(CmdLDefs[i]); } Mflags &= ~F_READONLY; Mfileindex = MFsave; } /* * Export a macro into the environment. * This is mainly done by the "export" directive inside a makefile. */ EXPORT void doexport(oname) char *oname; { obj_t *obj; list_t *l; char *name; int len; obj = objlook(oname, FALSE); if (obj != NULL && basetype(obj->o_type) != '=') /* Must be a macro type target */ obj = NULL; if (obj != NULL) { if ((l = obj->o_list) != NULL) { char *xname; len = strlen(oname)+1; /* Env name + '=' */ while (l && l->l_obj->o_name) { xname = l->l_obj->o_name; if (gbuf != NULL) xname = substitute(xname, NullObj, 0, 0); len += strlen(xname)+1; l = l->l_next; } name = malloc(len); if (name == NULL) comerr("Cannot alloc memory for env.\n"); name[0] = '\0'; l = obj->o_list; strcat(name, oname); strcat(name, "="); while (l && l->l_obj->o_name) { xname = l->l_obj->o_name; if (gbuf != NULL) xname = substitute(xname, NullObj, 0, 0); strcat(name, xname); if (l->l_next == NULL) break; strcat(name, " "); l = l->l_next; } putenv(name); } } } /* * Read in and parse all environment vars to make them make macros. */ LOCAL void read_environ() { int MFsave = Mfileindex; register char **env; extern char **environ; char *ev; char *p; Dmake -= 2; Mfileindex = MF_IDX_ENVIRON; /* Index 1 Environment vars */ if (Eflag) Mflags |= F_READONLY; mfname = Envdefs; for (env = environ; *env; env++) { ev = *env; p = strchr(ev, EQUAL); if (p == NULL) continue; if (strncmp(ev, "SHELL=", 6) == 0) continue; /* Never import SHELL */ *p = '\0'; define_var(ev, &p[1]); *p = EQUAL; } mfname = NULL; Mflags &= ~F_READONLY; Dmake += 2; Mfileindex = MFsave; } EXPORT int main(ac, av) int ac; char *av[]; { int failures = 0; int i; int cac = ac; char * const *cav = av; static char options[] = "help,version,posix,e,i,k,n,N,p,q,r,s,S,t,w,W,d+,D+,xM,xd+,probj,mf&,f&,&"; save_args(ac, av); #ifdef __DJGPP__ set_progname("smake"); /* We may have strange av[0] on DJGPP */ #endif #ifdef HAVE_GETPID getpid(); /* Give some info for truss(1) users */ #endif #ifdef HAVE_GETPGRP getpgrp(); /* Give some info for truss(1) users */ #endif #ifdef _FASCII /* Mark Williams C */ stderr->_ff &= ~_FSTBUF; /* setbuf was called ??? */ setup_env(); #endif getmakeflags(); /* Default options from MAKEFLAGS= */ initmakefiles(); /* Set up MakeFileNames[] array */ cac--; cav++; if (getallargs(&cac, &cav, options, &help, &pversion, &posixmode, &Eflag, &Iflag, &Kflag, &Nflag, &NSflag, &Print, &Qflag, &Rflag, &Sflag, &Stopflag, &Tflag, &NoWarn, &DoWarn, &Debug, &Dmake, &Prdep, &XDebug, &Probj, addmakefile, NULL, addmakefile, NULL, addcommandline, NULL) < 0) { errmsgno(EX_BAD, "Bad flag: %s.\n", cav[0]); usage(EX_BAD); } if (help) usage(0); if (pversion) { printf("Smake release %s (%s-%s-%s) Copyright (C) 1985, 87, 88, 91, 1995-2007 Jörg Schilling\n", make_version, HOST_CPU, HOST_VENDOR, HOST_OS); exit(0); } /* * XXX Is this the right place to set the options and cmd line macros * XXX to the exported environment? * XXX Later in read_makemacs() we may find that MAKEFLAGS= may contain * XXX garbage that has been propagated to MFCmdline. * For this reason, we call read_makemacs() before, let it parse only * and kill any unwanted content from MFCmdline. */ if (NullObj == 0) /* First make sure we may expand vars */ NullObj = objlook(Nullstr, TRUE); read_makemacs(); /* With gbuf == NULL, this is parse only */ setmakeflags(); if (Qflag) { Sflag = TRUE; Nflag = TRUE; } if (Stopflag) { Kflag = FALSE; } if (Debug > 0) error("MAKEFLAGS value: '%s'\n", getenv(Makeflags)); /* * XXX Reihenfolge bei UNIX make beachten!!! */ if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, handler); #ifdef SIGQUIT if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) signal(SIGQUIT, handler); #endif #ifdef SIGHUP if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, handler); #endif if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, handler); curtime = gcurtime(); newtime = gnewtime(); initchars(); initgbuf(NAMEMAX); /* Now the Makefile parser becomes usable */ /* * The functions read_cmdline() and read_makemacs() are setting * the F_READONLY flag in all objects. The function read_environ() * sets F_READONLY if the -e option was specified. * This differs from the description in POSIX for 'make' but it * is the only way to allow 'include' directives to work as expected. * For the same reason, we are reading the macros in the opposite * order as described in POSIX. */ read_cmdline(); /* First read cmd line macros */ read_makemacs(); /* then the inherited cmd line macros */ if (!Rflag) read_defs(); /* read "defaults.smk" */ setup_MAKE(av[0]); /* Set up $(MAKE) */ setup_vars(); /* Set up some known special macros */ setup_arch(); /* Set up srch specific macros */ read_environ(); /* Sets F_READONLY if -e flag is present*/ if (Debug > 0 && Mlevel > 0) error("Starting '%s'[%d] in directory '%s'\n", av[0], Mlevel, curwdir()); /* * Clear default target, then look again in makefiles */ default_tgt = NULL; read_makefiles(); /* * Let all objects created later seem to bee in * last Makefile or in implicit rules if no * Makefile is present. */ Mfileindex = Mfilecount - 1; if (Mfileindex < MF_IDX_MAKEFILE) Mfileindex = MF_IDX_IMPICIT; setup_dotvars(); if (!Rflag) check_old_makefiles(); if (Probj) /* -probj Flag */ printtree(); if (Print) { /* -p Flag */ prtree(); exit(0); /* XXX Really exit() here for make -p -f /dev/null ??? */ /* XXX Posix requires make -p -f /dev/null 2>/dev/null */ /* XXX to print the internal macros */ } on_comerr(exhandler, av[0]); if (Debug > 0) printdirs(); /* .OBJDIR .OBJSEARCH .SEARCHLIST */ makeincs(); /* Re-make included files */ omake(Init, TRUE); cac = ac; cav = av; cac--; cav++; for (i = 0; getfiles(&cac, &cav, options); cac--, cav++, i++) if (!domake(cav[0])) failures++; if (i == 0 && !domake((char *) NULL)) failures++; if (failures && Failed) { omake(Failed, TRUE); } else { omake(Done, TRUE); } #ifdef DEBUG prmem(); #endif if (failures > 0) comexit(1); comexit(0); return (0); /* keep lint happy :-) */ } /* * Check for old makefile systems without patten matching rules. * * Old makefile systems only define simple suffix rules. Now that newer smake * releases support POSIX suffix rules, the makefile system will only work * if the makefile system uses pattern matching rules. * We may remove this function in 2005. */ LOCAL void check_old_makefiles() { obj_t *o; if (Suffixes == NULL) /* No/Empty .SUFFIXES, no compat probelems */ return; if (xssrules == 0) /* Makefile did not define simple suff rule*/ return; if (xpatrules) /* New makefiles define pattern rules */ return; /* * All makefiles from the Schily (SING) makefile system define * _UNIQ=.XxZzy- */ if ((o = objlook("_UNIQ", FALSE)) == NULL) return; if (!streql(".XxZzy-", o->o_list->l_obj->o_name)) return; errmsgno(EX_BAD, "WARNING: this project uses an old version of the makefile system.\n"); comerrno(EX_BAD, "Update the makefiles or call 'smake -r'.\n"); } /* * Read in and parse the makeflags we got from the MAKEFLAGS= environment var. * MAKEFLAGS= may be: * * - "et..." Only option letters. * No space and no macro definitions. * - "NAME=value..." Only a list of macro definitions (like a * make command line). * - "-et..." Options as they appear on the command line. * Space and multiple '-' are allowed. * "-et -- NAME=value..." A complete make command line (except * -f filename options). */ LOCAL void getmakeflags() { int MFsave = Mfileindex; char *mf = getenv(Makeflags); if (!mf || *mf == '\0') /* No MAKEFLAGS= or empty MAKEFLAGS= */ return; Mfileindex = MF_IDX_ENVIRON; /* Index 1 Environment vars */ if (*mf != '-') { /* Only macros if does not start with '-'*/ char *p = nextmakemac(mf); /* Next unescaped ' ' */ char *eql = strchr(mf, '='); /* * Be gracious to non POSIX make programs like 'GNUmake' which * may have "e -- MAKE=smake" in the MAKEFLAGS environment. * The correct string would rather be "-e -- MAKE=smake". */ if (eql != NULL && (p == NULL || eql < p)) { /* * No options at all, only cmdline macros. */ MFCmdline = mf; goto out; /* Allow debug prints */ } } while (*mf) { switch (*mf) { case ' ': break; /* Ignore blanks */ case '-': /* look for " -- " separator */ if (mf[1] == '-') { if (mf[2] != ' ') { char *p = nextmakemac(mf); errmsgno(EX_BAD, "Found illegal option '%s' in MAKEFLAGS.\n", mf); if (p != NULL) { size_t d = p - mf; if (d > 50) d = 50; errmsgno(EX_BAD, "Skipping illegal option '%.*s'.\n", (int)d, mf); mf = p; break; } else { errmsgno(EX_BAD, "Ignoring illegal option '%s'.\n", mf); } goto out; /* Allow debug prints */ } MFCmdline = &mf[3]; goto out; /* Allow debug prints */ } break; /* Ignore single '-' */ case 'D': /* Display makefile */ Dmake++; break; case 'd': /* Debug */ Debug++; break; case 'X': /* XDebug */ XDebug++; break; case 'e': /* Environment overrides vars */ Eflag = TRUE; break; case 'i': /* Ignore errors from cmds */ Iflag = TRUE; break; case 'k': /* Ignore target errors */ Kflag = TRUE; break; case 'N': /* Ignore no Source on dep. */ NSflag = TRUE; break; case 'n': /* Do not exec any commands */ Nflag = TRUE; break; case 'p': /* Print macros/targets */ Print = TRUE; break; case 'q': /* Question */ Qflag = TRUE; break; case 'r': /* Turn off internal Rules */ Rflag = TRUE; break; case 's': /* Silent */ Sflag = TRUE; break; case 'S': /* Stop on error (opposite of -k) */ Stopflag = TRUE; break; case 't': /* Touch */ Tflag = TRUE; break; case 'W': /* Extra Warnings */ DoWarn = TRUE; break; case 'w': /* No Warnings */ NoWarn = TRUE; break; case 'Z': /* Print includes */ Prdep = TRUE; break; } mf++; } out: /* * As this is called before we call getargs(), Debug may only be true * if the 'd' option is present in the MAKEFLAGS environment. */ if (Debug > 0) error("Read MAKEFLAGS: '%s'\n", getenv(Makeflags)); Mfileindex = MFsave; } /* * Parse a list of macro=value command line macros from the * MAKEFLAGS= environment and set up the macro in the make tree. * If gbuf is NULL, read_mac() is parse only. */ LOCAL void read_makemacs() { register char *mf = MFCmdline; register char *p; int MFsave = Mfileindex; if (mf == NULL) return; Mfileindex = MF_IDX_ENVIRON; /* Index 1 Environment vars */ while (*mf) { p = nextmakemac(mf); if (p == NULL) { /* No other macro def follows */ if (!read_mac(mf)) *mf = '\0'; break; } else { /* Need to temporarily null terminate */ *p = '\0'; if (!read_mac(mf)) { strcpy(mf, &p[1]); } else { *p = ' '; mf = &p[1]; } } } Mfileindex = MFsave; } /* * Find next un-escaped blank (' ') which is a separator * for a list of macro=value items. */ LOCAL char * nextmakemac(s) char *s; { while (*s) { if (*s == '\\') { /* escaped character */ if (*++s == '\0') return (NULL); } else if (*s == ' ') { /* un-escaped space */ return (s); } s++; } return (NULL); } /* * Remove the escapes that have been introduced before the name=value * lists are put together into the MAKEFLAGS= environment. * Then parse the result as a string. * If gbuf is NULL, read_mac() is parse only. */ LOCAL BOOL read_mac(mf) char *mf; { char macdef[NAMEMAX*2+1]; char *p; p = macdef; while (*mf) { if (p >= &macdef[NAMEMAX*2]) break; /* * Only remove those escape sequences, that we created. * This is "\\" and "\ ". */ if (mf[0] == '\\' && (mf[1] == '\\' || mf[1] == ' ')) mf++; *p++ = *mf++; } *p = '\0'; if (macdef[0] == '\0') /* Ignore empty definition */ return (TRUE); if (strchr(macdef, '=') == NULL) { /* Check if it is a macro def*/ errmsgno(EX_BAD, "Found illegal macro definition '%s' in MAKEFLAGS.\n", macdef); return (FALSE); } if (gbuf == NULL) /* Parse only and kill */ return (TRUE); /* unwanted content */ Mflags |= F_READONLY; readstring(macdef, Makeflags); Mflags &= ~F_READONLY; return (TRUE); } /* * Prepare the MAKEFLAGS= environment for export. */ LOCAL void setmakeflags() { /* * MAKEFLAGS=- 12 bytes incl '\0' * 3 x 8 bytes= 24 bytes * 12 flags 12 bytes * '-- ' 3 bytes * ===================== * 51 bytes */ #define MAKEENV_SIZE_STATIC 64 static char makeenv[MAKEENV_SIZE_STATIC]; char *p; int i; p = strcatl(makeenv, Makeflags, (char *)NULL); *p++ = '='; *p++ = '-'; /* Posix make includes '-' */ /* "MAKEFLAGS=-" 12 incl. '\0' */ i = Dmake; /* Display makefile */ if (i > 8) i = 8; while (--i >= 0) *p++ = 'D'; i = Debug; /* Debug */ if (i > 8) i = 8; while (--i >= 0) *p++ = 'd'; i = XDebug; /* XDebug */ if (i > 8) i = 8; while (--i >= 0) *p++ = 'X'; if (Eflag) /* Environment overrides vars */ *p++ = 'e'; if (Iflag) /* Ignore errors from cmds */ *p++ = 'i'; if (Kflag) /* Ignore target errors */ *p++ = 'k'; if (NSflag) /* Ignore no Source on dep. */ *p++ = 'N'; if (Nflag) /* Do not exec any commands */ *p++ = 'n'; if (Print) /* Print macros/targets */ *p++ = 'p'; if (Qflag) /* Question */ *p++ = 'q'; if (Rflag) /* Turn off internal Rules */ *p++ = 'r'; if (Sflag) /* Silent */ *p++ = 's'; if (Stopflag) /* Stop on error (opposite of -k) */ *p++ = 'S'; if (Tflag) /* Touch */ *p++ = 't'; if (DoWarn) /* Extra Warnings */ *p++ = 'W'; if (NoWarn) /* No Warnings */ *p++ = 'w'; if (Prdep) /* Print includes */ *p++ = 'Z'; if (p - makeenv == 11) /* Empty flags, remove '-' */ --p; *p = '\0'; define_var(Make_Flags, &makeenv[10]); /* MAKE_FLAGS= ... */ doexport(Make_Flags); setmakeenv(makeenv, p); /* Add cmdline macs */ } /* * Strip out macro defs inherited from MAKELAGS, that will be overwritten * by command line macro defs. * Return new write poiner at end of string. */ LOCAL char * stripmacros(macbase, new) char *macbase; char *new; { char *p = strchr(new, '='); char *p2; if (p == NULL) /* Paranoia */ return (macbase + strlen(macbase)); do { p2 = nextmakemac(macbase); /* Find next macro delim */ if (strncmp(macbase, new, p - new) == 0) { /* * Got a match, need to remove this entry. */ if (p2 == NULL) { /* This is the only, zap out */ *macbase = '\0'; } else { /* Copy rest over current */ strcpy(macbase, &p2[1]); } } else if (p2) { /* Continue with next extry */ macbase = &p2[1]; } } while (p2); return (macbase + strlen(macbase)); } /* * Add the actual command line macro definitions to the MAKEFLAGS= string * and then putenv() the result. */ LOCAL void setmakeenv(envbase, envp) char *envbase; char *envp; { register int i; register int l; register int len = 0; register char *p; register char *macbase; if (Cmdlinecount == 0 && (MFCmdline == 0 || *MFCmdline == '\0')) { /* * No command line macros and no inherited command line * macros from MAKEFLAGS, so just call putenv() and return. */ putenv(envbase); return; } if ((envp - envbase) > 10) { /* envbase[] is currently not empty */ strcpy(envp, " -- "); /* we need a separator to the flags */ envp += 4; *envp = '\0'; } if (MFCmdline) /* Add one for '\0' or ' ' at end */ len = strlen(MFCmdline) + 1; for (i = 0; i < Cmdlinecount; i++) { p = CmdLDefs[i]; while (*p) { if (*p == '\\' || *p == ' ') len++; len++; p++; } len += 1; /* Add one for '\0' or ' ' at end */ } l = strlen(envbase) + len + 1; /* Add one (see stripmacro comment) */ if (l > MAKEENV_SIZE_STATIC) { p = malloc(l); strcpy(p, envbase); envp = p + (envp - envbase); envbase = p; } macbase = envp; if (MFCmdline) { for (p = MFCmdline; *p; ) *envp++ = *p++; *envp++ = ' '; } *envp = '\0'; for (i = 0; i < Cmdlinecount; i++) { p = CmdLDefs[i]; envp = stripmacros(macbase, p); while (*p) { if (*p == '\\' || *p == ' ') *envp++ = '\\'; *envp++ = *p++; } *envp++ = ' '; *envp = '\0'; /* Needed for stripmacros */ } /* But overshoots by one */ *--envp = '\0'; putenv(envbase); define_var(Make_Macs, macbase); /* MAKE_MACS= ... */ doexport(Make_Macs); } #define iswhite(c) ((c) == ' ' || (c) == '\t') #if defined(__linux__) || defined(__linux) || defined(SHELL_IS_BASH) /* * Needed to handle the Linux bash signal bug. */ int lpid; #endif /* * Execute or print a command line. * Return exit code from command line. */ EXPORT int docmd(cmd, obj) register char *cmd; obj_t *obj; { int code; int pid = 0; int retries; int Exit; int Silent = Sflag; int NoError = Iflag; int NoExec = Nflag; BOOL foundplus = FALSE; obj_t *shello; char *shell = NULL; while (iswhite(*cmd)) cmd++; /* * '-' Ignore error, '@' Silent, '+' Always execute * '?' No command dependency checking, '!' Force command dependency checking */ for (code = 0; code < 5 && (*cmd == '@' || *cmd == '-' || *cmd == '+' || *cmd == '?' || *cmd == '!'); code++, cmd++) { if (*cmd == '@') Silent = TRUE; if (*cmd == '-') NoError = TRUE; if (*cmd == '+') { NoExec = FALSE; foundplus = TRUE; } if (*cmd == '?') { /* EMPTY */ ; /* XXX To be defined !!! */ } if (*cmd == '!') { /* EMPTY */ ; /* XXX To be defined !!! */ } } if (foundplus) Silent = FALSE; if (!Silent || NoExec || Debug > 0) { /* error("...%s\n", cmd);*/ printf("%s%s\n", posixmode?"\t":"...", cmd); /* POSIX !!! */ } if (NoExec && !found_make) return (0); curtarget = obj; shello = objlook("SHELL", FALSE); if (shello != NULL && basetype(shello->o_type) == EQUAL && shello->o_list != NULL) shell = shello->o_list->l_obj->o_name; if (shell == NULL || *shell == '\0') { shello = NULL; shell = "/bin/sh"; } #if !defined(USE_SYSTEM) && /* XXX else system() ??? */ \ ((defined(HAVE_FORK) && defined HAVE_EXECL) || defined(JOS)) #if defined(__EMX__) || defined(__DJGPP__) #ifdef __EMX__ pid = spawnl(P_NOWAIT, shell, filename(shell), NoError ? cflag:ceflag, cmd, (char *)NULL); if (pid < 0) comerr("Can't spawn %s.\n", shell); #else { /* * exec '/bin/sh' does not work under DJGPP. * Strings like 'c:\djgpp\bin\sh.exe' or '/dev/c/djgpp/bin/sh.exe' * must be used instead. * * Notes: c:/djgpp/share/config.site defines 'SHELL' with the required string. * * Using system("sh -ce 'cmd'") and spawn("command.com /c sh -ce 'cmd'") * cause GPF's (not enough memmory?) * * Temporary solution: Use DJGPP_SH envp. var. (must be set manually) */ static char *shellname = NULL; if (shello != NULL) /* On DJGPP too? */ shellname = shell; if (shellname == NULL) shellname = getenv("DJGPP_SH"); /* Backward compat */ if (shellname == NULL) shellname = searchfileinpath("bin/sh.exe"); /* alloc() */ if (shellname == NULL) shellname = searchfileinpath("sh.exe"); /* alloc() */ if (shellname == NULL) comerr("Can't find sh.exe.\n"); Exit = spawnl(P_WAIT, shellname, filename(shellname), NoError ? cflag:ceflag, cmd, (char *)NULL); if (Exit) { /* TODO: DOS error code to UNIX error code */ Exit = 0xFF<<8; } } #endif #else /* * Do several tries to fork child to allow working on loaded systems. */ for (retries = 0; retries < 10; retries++) { pid = fork(); if (pid >= 0) break; sleep(1L); /* Wait for resources to become free.*/ } if (pid < 0) comerr("Can't fork.\n"); if (pid == 0) { /* Child process: do the work. */ /* * We used to call /bin/sh -ce 'cmd' but we get problems on QNX * and UNIX-98 requests that the command shall be called as in * system() which means /bin/sh -c 'cmd'. */ execl(shell, filename(shell), NoError ? cflag:ceflag, cmd, (char *)NULL); comerr("Can't exec %s.\n", shell); } #endif /* __EMX__ || __DJGPP__ */ #if defined(__linux__) || defined(__linux) || defined(SHELL_IS_BASH) /* * Needed to handle the Linux bash signal bug. */ lpid = pid; #endif /* Parent process: wait for child. */ #ifndef __DJGPP__ if ((code = wait((WAIT_T *)&Exit)) != pid) comerrno(Exit, "code %d waiting for %s.\n", geterrno(), shell); #endif #else /* Use system() */ Exit = system(cmd); #endif if (Exit) { errmsgno(Exit>>8, "*** Code %d from command line for target '%s'%s.\n", Exit>>8, obj->o_name, NoError?" (ignored)":""); if (Silent && Debug <= 0) { errmsgno(EX_BAD, "The following command caused the error:\n"); error("%s\n", cmd); } } curtarget = (obj_t *)NULL; if (NoError) return (0); return (Exit); } #ifdef tos # include "osbind.h" #endif /* * Move a target file to ObjDir. */ EXPORT BOOL move_tgt(from) register obj_t *from; { date_t fromtime; int code; char _objname[TYPICAL_NAMEMAX]; char *objname = NULL; BOOL ret = TRUE; /* * Move only if: * objdir to corresponding srcdir exists * target is known in Makefile */ if ((ObjDir == NULL && from->o_level == OBJLEVEL) || from->o_level < OBJLEVEL) return (TRUE); fromtime = gftime(from->o_name); if (fromtime == 0) /* Nothing to move found */ return (TRUE); if (strchr(from->o_name, SLASH)) /* Only move from current directory */ return (TRUE); if (Debug > 3) error("move: from->o_level: %d\n", from->o_level); if ((objname = build_path(from->o_level, from->o_name, from->o_namelen, _objname, sizeof (_objname))) == NULL) return (FALSE); if (!Sflag || Nflag) printf("%smove %s %s\n", posixmode?"\t":"...", from->o_name, objname); if (Nflag) { ret = TRUE; goto out; } if ((from->o_name == objname) || (gfileid(from->o_name) == gfileid(objname))) { errmsgno(EX_BAD, "Will not move '%s' to itself.\n", from->o_name); ret = TRUE; goto out; } # ifdef tos unlink(objname); if ((code = Frename(0, from->o_name, objname)) < 0) { if (code == EXDEV) { code = copy_file(from->o_name, objname); if (unlink(from->o_name) < 0) errmsg("Can't remove old name '%s'.\n", from->o_name); } else { errmsgno(-code, "Can't rename '%s' to '%s'.\n", from->o_name, objname); } } if (code < 0) { ret = FALSE; goto out; } # else if ((code = rename(from->o_name, objname)) < 0) { if (geterrno() == EXDEV) { if (rmdir(objname) < 0 && geterrno() == ENOTDIR) unlink(objname); code = copy_file(from->o_name, objname); if (unlink(from->o_name) < 0) errmsg("Can't remove old name '%s'.\n", from->o_name); } else { errmsg("Can't rename '%s' to '%s'.\n", from->o_name, objname); } } if (code < 0) { ret = FALSE; goto out; } #endif /* tos */ out: if (objname != NULL && objname != from->o_name && objname != _objname) free(objname); return (ret); } /* * Copy File if we cannot rename the file. */ #ifdef tos LOCAL int copy_file(from, objname) char *from; char *objname; { int fin; int fout; int cnt = -1; if ((fin = open(from, 0)) < 0) errmsg("Can't open '%s'.\n", from); else { if ((fout = creat(objname, 0666)) < 0) errmsg("Can't create '%s'.\n", objname); else { while ((cnt = read(fin, gbuf, gbufsize)) > 0) if (write(fout, gbuf, cnt) != cnt) { errmsg("Write error on '%s'.\n", objname); cnt = -1; break; } close(fout); } close(fin); } return (cnt); } #else LOCAL int copy_file(from, objname) char *from; char *objname; { FILE *fin; FILE *fout; int cnt = -1; if ((fin = fileopen(from, "rub")) == 0) errmsg("Can't open '%s'.\n", from); else { if ((fout = fileopen(objname, "wtcub")) == 0) errmsg("Can't create '%s'.\n", objname); else { while ((cnt = fileread(fin, gbuf, gbufsize)) > 0) filewrite(fout, gbuf, cnt); fclose(fout); } fclose(fin); } return (cnt); } #endif /* * This function behaves similar to the UNIX 'touch' command. */ EXPORT BOOL touch_file(name) char *name; { FILE *f; char _objname[TYPICAL_NAMEMAX]; char *objname = _objname; size_t objlen = sizeof (_objname); char *np = NULL; #ifndef HAVE_UTIME char c; #endif again: if (snprintf(objname, objlen, "%s%s%s", ObjDir, slash, filename(name)) >= objlen) { objlen = strlen(filename(name)) + ObjDirlen + slashlen + 1; objname = np = __realloc(np, objlen, "touch path"); goto again; } #ifdef __is_this_ok__ if (!gftime(objname)) snprintf(_objname, sizeof (_objname), name); #endif if (!Sflag) printf("%stouch %s\n", posixmode?"\t":"...", objname); if (Nflag) return (TRUE); if ((f = fileopen(objname, "rwcub")) != (FILE *)NULL) { #ifdef HAVE_UTIME utime(objname, NULL); #else c = getc(f); fileseek(f, (off_t)0); putc(c, f); #endif fclose(f); if (np) free(np); return (TRUE); } if (np) free(np); return (FALSE); } # include # define STATBUF struct stat /* * Get current time. */ LOCAL date_t gcurtime() { return ((date_t) time((time_t *) 0)); } /* * Get a time that is in the future (as far as possible). */ LOCAL date_t gnewtime() { time_t t = curtime; time_t a = YEARSECS; int i = 0; while ((a * 2) > a) { a *= 2; if (++i > 100) break; } i = 0; while (a > 0) { while ((t + a) > t) { t += a; if (++i > 100) break; } if (i > 1000) break; a /= 2; } while (t == NOTIME || t == BADTIME || t == RECURSETIME || t == PHONYTIME) { t--; } /*printf("i: %d, %lu, %lx, %s\n", i, t, t, prtime(-3));*/ return (t); } /* * Get the time of last modification for a file. * Cannot call it gmtime() */ EXPORT date_t gftime(file) char *file; { STATBUF stbuf; char this_time[32]; char cur_time[32]; stbuf.st_mtime = NOTIME; if (stat(file, &stbuf) < 0) { /* * GNU libc.6 destroys st_mtime */ stbuf.st_mtime = NOTIME; } else { register time_t t; /* * Make sure that the time for an existing file is not * in the list of special time stamps. */ t = stbuf.st_mtime; while (t == NOTIME || t == BADTIME || t == RECURSETIME || t == PHONYTIME) { t++; } stbuf.st_mtime = t; } if (Debug > 3) error("gftime(%s) = %s\n", file, prtime(stbuf.st_mtime)); if (stbuf.st_mtime > (curtime +5)) { date_t xcurtime; xcurtime = gcurtime(); if (stbuf.st_mtime <= (xcurtime +5)) { curtime = xcurtime; return (stbuf.st_mtime); } strncpy(this_time, prtime(stbuf.st_mtime), sizeof (this_time)); this_time[sizeof (this_time)-1] = '\0'; strncpy(cur_time, prtime(curtime), sizeof (cur_time)); cur_time[sizeof (cur_time)-1] = '\0'; errmsgno(EX_BAD, "WARNING: '%s' has modification time in the future (%s > %s).\n", file, this_time, cur_time); } return (stbuf.st_mtime); } /* * Check if file is a directory. */ LOCAL BOOL isdir(file) char *file; { STATBUF stbuf; if (stat(file, &stbuf) < 0) return (TRUE); if ((stbuf.st_mode&S_IFMT) == S_IFDIR) return (TRUE); return (FALSE); } /* * Get a unique number for a file to prevent moving targets to themselves. * XXX inode number is now long !!! */ EXPORT Llong gfileid(file) char *file; { STATBUF stbuf; Llong result; /* * Setup a unique default. In case stat() will fail. */ stbuf.st_ino = (ino_t) (long) file; stbuf.st_dev = 0; if (stat(file, &stbuf) < 0) { /* * GNU libc.6 destroys st_mtime * Paranoia .... we fall back here too. */ stbuf.st_ino = (ino_t) (long) file; stbuf.st_dev = 0; } #if SIZEOF_LLONG > 4 result = stbuf.st_dev; result <<= 32; result |= stbuf.st_ino; #else result = stbuf.st_dev; result <<= 16; result |= stbuf.st_ino; #endif if (Debug > 3) error("gfileid: %s %lld\n", file, result); return (result); } /* * Transfer UNIX time stamps into human readable names. * Take care of our "special timestamps". */ EXPORT char * prtime(date) date_t date; { char *s; if (date == (date_t)0) return ("File does not exist"); if (date == BADTIME) return ("File could not be made"); if (date == RECURSETIME) return ("Recursive dependencies"); if (date == PHONYTIME) return ("File is phony"); if (date == newtime) return ("Younger than any file"); s = ctime((const time_t *)&date); s[strlen(s)-1] = '\0'; return (s); } /* * Our general signal handler. Does some needed clean up and includes * workarounds for buggy OS like Linux. */ LOCAL void handler(signo) int signo; { char *name; signal(signo, handler); errmsgno(EX_BAD, "Got signal %d\n", signo); if (!curtarget) goto out; errmsgno(EX_BAD, "Current target is: %s precious: %d phony: %d\n", curtarget->o_name, isprecious(curtarget), isprecious(curtarget)); /* while(wait(0) >= 0) */ /* ;*/ /* * Keine Bibliotheken * Kein -t, -q etc. */ if (isprecious(curtarget)) goto out; if (isphony(curtarget)) goto out; name = curtarget->o_name; if (isdir(name)) { error("*** %s not removed.\n", name); goto out; } if (unlink(name) >= 0) { error("*** %s removed.\n", name); } else { errmsg("*** %s could not be removed.\n", name); } /* * Test ob ObjDir/name existiert und neuer als vorher ist. */ out: #if defined(__linux__) || defined(__linux) || defined(SHELL_IS_BASH) /* * Linux signal handling is broken. This is caused by a bug in 'bash'. * Bash does jobcontrol even if called as "sh -ce 'command'". * This is illegal. Only the foregound (make) process and with some * bash versions the descendant 'make' processes are killed. * The following code tries to kill the others too. */ signal(signo, SIG_IGN); /* Make us immune to death ;-) */ /* * First shoot everyone into the foot. */ kill(lpid, signo); /* Kill bash that is our child */ kill(-lpid, signo); /* Kill possible bash children */ kill(-getpgrp(), signo); /* Kill our process group */ /* * Now shoot everyone into the head. */ kill(lpid, SIGKILL); /* Kill bash that is our child */ kill(-lpid, SIGKILL); /* Kill possible bash children */ kill(-getpgrp(), SIGKILL); /* Kill our process group */ #endif comexit(signo); } LOCAL void exhandler(excode, arg) int excode; void *arg; { /* * Ausgabe wenn: * * - excode != 0 && Mlevel > 0 * - Debug > 0 && Mlevel > 0 */ if ((Debug <= 1 && excode == 0) || Mlevel <= 0) return; errmsgno(EX_BAD, "Leaving '%s'[%d] from directory '%s'\n", (char *)arg, Mlevel, curwdir()); if (default_tgt != NULL) errmsgno(EX_BAD, "Default commandline target: '%s'\n", default_tgt->o_name); errmsgno(EX_BAD, "Doing exit(%d)\n", excode); } /* * Return current working directory in an allocated string. */ EXPORT char * curwdir() { static char *wdir; char wd[MAXPATHNAME + 1]; if (wdir != NULL) return (wdir); if (getcwd(wd, MAXPATHNAME) == NULL) { wd[0] = '/'; wd[1] = '\0'; } wdir = malloc(strlen(wd)+1); if (wdir == NULL) comerr("Cannot malloc working dir.\n"); strcpy(wdir, wd); return (wdir); } /* * Search for the defaults.smk file in the PATH of the user. * Assume that the file is ... bin/../lib/defaults.smk */ LOCAL char * getdefaultsfile() { return (searchfileinpath("lib/defaults.smk")); } LOCAL char * searchfileinpath(name) char *name; { char *path = getenv("PATH"); char pbuf[NAMEMAX]; char *nbuf = pbuf; char *np; int nlen = strlen(name); char *pn = get_progname(); if (strchr(pn, '/') != NULL) { strncpy(nbuf, pn, sizeof (pbuf)); nbuf[sizeof (pbuf) - 1] = '\0'; np = nbuf + strlen(nbuf); while (np > nbuf && np[-1] != '/') *--np = '\0'; pn = &nbuf[sizeof (pbuf) - 1]; if ((np = searchonefile(name, nbuf, np, pn)) != NULL) return (np); } if (path == NULL) return (NULL); #ifdef __DJGPP__ strbs2s(path); /* PATH under DJGPP can contain both slashes */ #endif pn = &nbuf[sizeof (pbuf) - 1]; for (;;) { np = nbuf; while (*path != PATH_ENV_DELIM && *path != '\0' && np < &nbuf[sizeof (pbuf) - nlen]) *np++ = *path++; *np = '\0'; if ((np = searchonefile(name, nbuf, np, pn)) != NULL) return (np); if (*path == '\0') break; path++; } return (NULL); } LOCAL char * searchonefile(name, nbuf, np, ep) register char *name; register char *nbuf; register char *np; register char *ep; { while (np > nbuf && np[-1] == '/') *--np = '\0'; if (np >= &nbuf[4] && streql(&np[-4], "/bin")) np = &np[-4]; *np++ = '/'; *np = '\0'; strncpy(np, name, ep - np); *ep = '\0'; if (gftime(nbuf)) { np = malloc(strlen(nbuf)+1); if (np == NULL) return (NULL); strcpy(np, nbuf); return (np); } return (NULL); } #ifdef __DJGPP__ LOCAL char * strbs2s(s) char *s; { char *tmp = s; if (tmp) { while (*tmp) { if (*tmp == '\\') *tmp = '/'; tmp++; } } return (s); } #endif #ifndef HAVE_PUTENV EXPORT int putenv __PR((const char *new)); LOCAL int ev_find __PR((const char *s)); extern char **environ; /* The environment array */ LOCAL BOOL ealloc = FALSE; /* TRUE if environ is already allocated */ /* * Our local putenv implementation for systems that don't have it. */ EXPORT int putenv(new) const char *new; { char **newenv; register int idx; if ((idx = ev_find(new)) >= 0) { /* * An old entry with the same name exists, replace it. */ environ[idx] = (char *)new; } else { /* * If idx is < 0, we need to expand environ for the new entry. * In this case -idx is the inverted size of the old environ. */ idx = -idx + 1; /* Add space for new entry */ if (ealloc) { /* * environ is allocated, expand with realloc */ newenv = (char **)realloc(environ, idx*sizeof (char *)); if (newenv == NULL) return (-1); } else { /* * environ is orig space, copy to malloc'ed space */ ealloc = TRUE; newenv = (char **)malloc(idx*sizeof (char *)); if (newenv == NULL) return (-1); (void) movebytes((char *)environ, (char *)newenv, (int)(idx*sizeof (char *))); } environ = newenv; environ[idx-2] = (char *)new; environ[idx-1] = NULL; } return (0); } /* * Check if arg of form name=value is part of environ. * Return index on success and -environ_size if not. */ LOCAL int ev_find(s) register const char *s; { register int i = 0; register const char *ep; register const char *s2; for (i = 0; environ[i] != NULL; i++) { /* * Find string in environment entry. */ for (ep = environ[i], s2 = s; *ep == *s2++; ep++) { if (*ep == '=') return (i); } } return (-(++i)); } #endif /* HAVE_PUTENV */