diff options
Diffstat (limited to 'imake.c')
-rw-r--r-- | imake.c | 1588 |
1 files changed, 1588 insertions, 0 deletions
@@ -0,0 +1,1588 @@ +/* $Id */ + +/*************************************************************************** + * * + * Porting Note * + * * + * Add the value of BOOTSTRAPCFLAGS to the cpp_argv table so that it will * + * be passed to the template file. * + * * + ***************************************************************************/ + +/* + * +Copyright (c) 1985, 1986, 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + * + * Original Author: + * Todd Brunhoff + * Tektronix, inc. + * While a guest engineer at Project Athena, MIT + * + * imake: the include-make program. + * + * Usage: imake [-Idir] [-Ddefine] [-T template] [-f imakefile ] [-C Imakefile.c ] [-s] [-e] [-v] [make flags] + * + * Imake takes a template file (Imake.tmpl) and a prototype (Imakefile) + * and runs cpp on them producing a Makefile. It then optionally runs make + * on the Makefile. + * Options: + * -D define. Same as cpp -D argument. + * -I Include directory. Same as cpp -I argument. + * -T template. Designate a template other + * than Imake.tmpl + * -f specify the Imakefile file + * -C specify the name to use instead of Imakefile.c + * -s[F] show. Show the produced makefile on the standard + * output. Make is not run is this case. If a file + * argument is provided, the output is placed there. + * -e[F] execute instead of show; optionally name Makefile F + * -v verbose. Show the make command line executed. + * + * Environment variables: + * + * IMAKEINCLUDE Include directory to use in addition to "." + * IMAKECPP Cpp to use instead of /lib/cpp + * IMAKEMAKE make program to use other than what is + * found by searching the $PATH variable. + * Other features: + * imake reads the entire cpp output into memory and then scans it + * for occurences of "@@". If it encounters them, it replaces it with + * a newline. It also trims any trailing white space on output lines + * (because make gets upset at them). This helps when cpp expands + * multi-line macros but you want them to appear on multiple lines. + * It also changes occurences of "XCOMM" to "#", to avoid problems + * with treating commands as invalid preprocessor commands. + * + * The macros MAKEFILE and MAKE are provided as macros + * to make. MAKEFILE is set to imake's makefile (not the constructed, + * preprocessed one) and MAKE is set to argv[0], i.e. the name of + * the imake program. + * + * Theory of operation: + * 1. Determine the name of the imakefile from the command line (-f) + * or from the content of the current directory (Imakefile or imakefile). + * Call this <imakefile>. This gets added to the arguments for + * make as MAKEFILE=<imakefile>. + * 2. Determine the name of the template from the command line (-T) + * or the default, Imake.tmpl. Call this <template> + * 3. Determine the name of the imakeCfile from the command line (-C) + * or the default, Imakefile.c. Call this <imakeCfile> + * 4. Store lines of input into <imakeCfile>: + * - A c-style comment header (see ImakefileCHeader below), used + * to recognize temporary files generated by imake. + * - If DEFAULT_OS_NAME is defined, format the utsname struct and + * call the result <defaultOsName>. Add: + * #define DefaultOSName <defaultOsName> + * - If DEFAULT_OS_MAJOR_REV is defined, format the utsname struct + * and call the result <defaultOsMajorVersion>. Add: + * #define DefaultOSMajorVersion <defaultOsMajorVersion> + * - If DEFAULT_OS_MINOR_REV is defined, format the utsname struct + * and call the result <defaultOsMinorVersion>. Add: + * #define DefaultOSMinorVersion <defaultOsMinorVersion> + * - If DEFAULT_OS_TEENY_REV is defined, format the utsname struct + * and call the result <defaultOsTeenyVersion>. Add: + * #define DefaultOSTeenyVersion <defaultOsTeenyVersion> + * - If the file "localdefines" is readable in the current + * directory, print a warning message to stderr and add: + * #define IMAKE_LOCAL_DEFINES "localdefines" + * #include IMAKE_LOCAL_DEFINES + * - If the file "admindefines" is readable in the current + * directory, print a warning message to stderr and add: + * #define IMAKE_ADMIN_DEFINES "admindefines" + * #include IMAKE_ADMIN_DEFINES + * - The following lines: + * #define INCLUDE_IMAKEFILE < <imakefile> > + * #define IMAKE_TEMPLATE " <template> " + * #include IMAKE_TEMPLATE + * - If the file "adminmacros" is readable in the current + * directory, print a warning message to stderr and add: + * #define IMAKE_ADMIN_MACROS "adminmacros" + * #include IMAKE_ADMIN_MACROS + * - If the file "localmacros" is readable in the current + * directory, print a warning message to stderr and add: + * #define IMAKE_LOCAL_MACROS "localmacros" + * #include IMAKE_LOCAL_MACROS + * 5. Start up cpp and provide it with this file. + * Note that the define for INCLUDE_IMAKEFILE is intended for + * use in the template file. This implies that the imake is + * useless unless the template file contains at least the line + * #include INCLUDE_IMAKEFILE + * 6. Gather the output from cpp, and clean it up, expanding @@ to + * newlines, stripping trailing white space, cpp control lines, + * and extra blank lines, and changing XCOMM to #. This cleaned + * output is placed in a new file, default "Makefile", but can + * be specified with -s or -e options. + * 7. Optionally start up make on the resulting file. + * + * The design of the template makefile should therefore be: + * <set global macros like CFLAGS, etc.> + * <include machine dependent additions> + * #include INCLUDE_IMAKEFILE + * <add any global targets like 'clean' and long dependencies> + */ +#include <stdio.h> +#include <ctype.h> +#include "Xosdefs.h" +#ifdef WIN32 +# include "Xw32defs.h" +#endif +#ifndef X_NOT_POSIX +# ifndef _POSIX_SOURCE +# define _POSIX_SOURCE +# endif +#endif +#include <sys/types.h> +#include <fcntl.h> +#ifdef X_NOT_POSIX +# ifndef WIN32 +# include <sys/file.h> +# endif +#else +# include <unistd.h> +#endif +#if defined(X_NOT_POSIX) || defined(_POSIX_SOURCE) +# include <signal.h> +#else +# define _POSIX_SOURCE +# include <signal.h> +# undef _POSIX_SOURCE +#endif +#include <sys/stat.h> +#ifndef X_NOT_POSIX +# ifdef _POSIX_SOURCE +# include <sys/wait.h> +# else +# define _POSIX_SOURCE +# include <sys/wait.h> +# undef _POSIX_SOURCE +# endif +# define waitCode(w) WEXITSTATUS(w) +# define waitSig(w) WTERMSIG(w) +typedef int waitType; +#else /* X_NOT_POSIX */ +# ifdef SYSV +# define waitCode(w) (((w) >> 8) & 0x7f) +# define waitSig(w) ((w) & 0xff) +typedef int waitType; +# else /* SYSV */ +# ifdef WIN32 +# include <process.h> +typedef int waitType; +# else +# include <sys/wait.h> +# define waitCode(w) ((w).w_T.w_Retcode) +# define waitSig(w) ((w).w_T.w_Termsig) +typedef union wait waitType; +# endif +# endif +# ifndef WIFSIGNALED +# define WIFSIGNALED(w) waitSig(w) +# endif +# ifndef WIFEXITED +# define WIFEXITED(w) waitCode(w) +# endif +#endif /* X_NOT_POSIX */ +#ifndef X_NOT_STDC_ENV +# include <stdlib.h> +#else +char *malloc(), *realloc(); +void exit(); +#endif +#if defined(macII) && !defined(__STDC__) /* stdlib.h fails to define these */ +char *malloc(), *realloc(); +#endif /* macII */ +#ifdef X_NOT_STDC_ENV +extern char *getenv(); +#endif +#include <errno.h> +#ifdef X_NOT_STDC_ENV +extern int errno; +#endif +#ifndef WIN32 +#include <sys/utsname.h> +#else +#include <windows.h> +#endif +#ifndef SYS_NMLN +# ifdef _SYS_NMLN +# define SYS_NMLN _SYS_NMLN +# else +# define SYS_NMLN 257 +# endif +#endif +#ifdef linux +#include <limits.h> +#endif +/* + * is strstr() in <strings.h> on X_NOT_STDC_ENV? + * are there any X_NOT_STDC_ENV machines left in the world? + */ +#include <string.h> +#include "imakemdep.h" + +/* + * This define of strerror is copied from (and should be identical to) + * Xos.h, which we don't want to include here for bootstrapping reasons. + */ +#if defined(X_NOT_STDC_ENV) || (defined(sun) && !defined(SVR4)) || defined(macII) +# ifndef strerror +extern char *sys_errlist[]; +extern int sys_nerr; +# define strerror(n) \ + (((n) >= 0 && (n) < sys_nerr) ? sys_errlist[n] : "unknown error") +# endif +#endif + +#define TRUE 1 +#define FALSE 0 + +#ifdef FIXUP_CPP_WHITESPACE +int InRule = FALSE; +# ifdef INLINE_SYNTAX +int InInline = 0; +# endif +#endif +#ifdef MAGIC_MAKE_VARS +int xvariable = 0; +int xvariables[10]; +#endif + +/* + * Some versions of cpp reduce all tabs in macro expansion to a single + * space. In addition, the escaped newline may be replaced with a + * space instead of being deleted. Blech. + */ +#ifdef FIXUP_CPP_WHITESPACE +void KludgeOutputLine(), KludgeResetRule(); +#else +# define KludgeOutputLine(arg) +# define KludgeResetRule() +#endif + +typedef unsigned char boolean; + +#ifdef USE_CC_E +# ifndef DEFAULT_CC +# define DEFAULT_CC "cc" +# endif +#else +# ifndef DEFAULT_CPP +# ifdef CPP_PROGRAM +# define DEFAULT_CPP CPP_PROGRAM +# else +# define DEFAULT_CPP "/lib/cpp" +# endif +# endif +#endif + +char *cpp = NULL; + +char *tmpMakefile = "/tmp/Imf.XXXXXX"; +char *tmpImakefile = "/tmp/IIf.XXXXXX"; +char *make_argv[ ARGUMENTS ] = { +#ifdef WIN32 + "nmake" +#else + "make" +#endif +}; + +int make_argindex; +int cpp_argindex; +char *Imakefile = NULL; +char *Makefile = "Makefile"; +char *Template = "Imake.tmpl"; +char *ImakefileC = "Imakefile.c"; +boolean haveImakefileC = FALSE; +char *cleanedImakefile = NULL; +char *program; +char *FindImakefile(); +char *ReadLine(); +char *CleanCppInput(); +char *Strdup(); +char *Emalloc(); +void LogFatalI(), LogFatal(), LogMsg(); + +void showit(); +void wrapup(); +void init(); +void AddMakeArg(); +void AddCppArg(); +void SetOpts(); +void CheckImakefileC(); +void cppit(); +void makeit(); +void CleanCppOutput(); +boolean isempty(); +void writetmpfile(); + +boolean verbose = FALSE; +boolean show = TRUE; + +int +main(argc, argv) + int argc; + char **argv; +{ + FILE *tmpfd; + char makeMacro[ BUFSIZ ]; + char makefileMacro[ BUFSIZ ]; + + program = argv[0]; + init(); + SetOpts(argc, argv); + + Imakefile = FindImakefile(Imakefile); + CheckImakefileC(ImakefileC); + if (Makefile) + tmpMakefile = Makefile; + else { + tmpMakefile = Strdup(tmpMakefile); + (void) mktemp(tmpMakefile); + } + AddMakeArg("-f"); + AddMakeArg( tmpMakefile ); + sprintf(makeMacro, "MAKE=%s", program); + AddMakeArg( makeMacro ); + sprintf(makefileMacro, "MAKEFILE=%s", Imakefile); + AddMakeArg( makefileMacro ); + + if ((tmpfd = fopen(tmpMakefile, "w+")) == NULL) + LogFatal("Cannot create temporary file %s.", tmpMakefile); + + cleanedImakefile = CleanCppInput(Imakefile); + cppit(cleanedImakefile, Template, ImakefileC, tmpfd, tmpMakefile); + + if (show) { + if (Makefile == NULL) + showit(tmpfd); + } else + makeit(); + wrapup(); + exit(0); +} + +void +showit(fd) + FILE *fd; +{ + char buf[ BUFSIZ ]; + int red; + + fseek(fd, 0, 0); + while ((red = fread(buf, 1, BUFSIZ, fd)) > 0) + writetmpfile(stdout, buf, red, "stdout"); + if (red < 0) + LogFatal("Cannot read %s.", tmpMakefile); +} + +void +wrapup() +{ + if (tmpMakefile != Makefile) + unlink(tmpMakefile); + if (cleanedImakefile && cleanedImakefile != Imakefile) + unlink(cleanedImakefile); + if (haveImakefileC) + unlink(ImakefileC); +} + +#ifdef SIGNALRETURNSINT +int +#else +void +#endif +catch(sig) + int sig; +{ + errno = 0; + LogFatalI("Signal %d.", sig); +} + +/* + * Initialize some variables. + */ +void +init() +{ + register char *p; + + make_argindex=0; + while (make_argv[ make_argindex ] != NULL) + make_argindex++; + cpp_argindex = 0; + while (cpp_argv[ cpp_argindex ] != NULL) + cpp_argindex++; + + /* + * See if the standard include directory is different than + * the default. Or if cpp is not the default. Or if the make + * found by the PATH variable is not the default. + */ + if ((p = getenv("IMAKEINCLUDE"))) { + if (*p != '-' || *(p+1) != 'I') + LogFatal("Environment var IMAKEINCLUDE %s", + "must begin with -I"); + AddCppArg(p); + for (; *p; p++) + if (*p == ' ') { + *p++ = '\0'; + AddCppArg(p); + } + } + if ((p = getenv("IMAKECPP"))) + cpp = p; + if ((p = getenv("IMAKEMAKE"))) + make_argv[0] = p; + + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, catch); +} + +void +AddMakeArg(arg) + char *arg; +{ + errno = 0; + if (make_argindex >= ARGUMENTS-1) + LogFatal("Out of internal storage.", ""); + make_argv[ make_argindex++ ] = arg; + make_argv[ make_argindex ] = NULL; +} + +void +AddCppArg(arg) + char *arg; +{ + errno = 0; + if (cpp_argindex >= ARGUMENTS-1) + LogFatal("Out of internal storage.", ""); + cpp_argv[ cpp_argindex++ ] = arg; + cpp_argv[ cpp_argindex ] = NULL; +} + +void +SetOpts(argc, argv) + int argc; + char **argv; +{ + errno = 0; + /* + * Now gather the arguments for make + */ + for(argc--, argv++; argc; argc--, argv++) { + /* + * We intercept these flags. + */ + if (argv[0][0] == '-') { + if (argv[0][1] == 'D') { + AddCppArg(argv[0]); + } else if (argv[0][1] == 'I') { + AddCppArg(argv[0]); + } else if (argv[0][1] == 'f') { + if (argv[0][2]) + Imakefile = argv[0]+2; + else { + argc--, argv++; + if (! argc) + LogFatal("No description arg after -f flag", ""); + Imakefile = argv[0]; + } + } else if (argv[0][1] == 's') { + if (argv[0][2]) + Makefile = ((argv[0][2] == '-') && !argv[0][3]) ? + NULL : argv[0]+2; + else { + argc--, argv++; + if (!argc) + LogFatal("No description arg after -s flag", ""); + Makefile = ((argv[0][0] == '-') && !argv[0][1]) ? + NULL : argv[0]; + } + show = TRUE; + } else if (argv[0][1] == 'e') { + Makefile = (argv[0][2] ? argv[0]+2 : NULL); + show = FALSE; + } else if (argv[0][1] == 'T') { + if (argv[0][2]) + Template = argv[0]+2; + else { + argc--, argv++; + if (! argc) + LogFatal("No description arg after -T flag", ""); + Template = argv[0]; + } + } else if (argv[0][1] == 'C') { + if (argv[0][2]) + ImakefileC = argv[0]+2; + else { + argc--, argv++; + if (! argc) + LogFatal("No imakeCfile arg after -C flag", ""); + ImakefileC = argv[0]; + } + } else if (argv[0][1] == 'v') { + verbose = TRUE; + } else + AddMakeArg(argv[0]); + } else + AddMakeArg(argv[0]); + } +#ifdef USE_CC_E + if (!cpp) + { + AddCppArg("-E"); + cpp = DEFAULT_CC; + } +#else + if (!cpp) + cpp = DEFAULT_CPP; +#endif + cpp_argv[0] = cpp; + AddCppArg(ImakefileC); +} + +char * +FindImakefile(Imakefile) + char *Imakefile; +{ + if (Imakefile) { + if (access(Imakefile, R_OK) < 0) + LogFatal("Cannot find %s.", Imakefile); + } else { + if (access("Imakefile", R_OK) < 0) + if (access("imakefile", R_OK) < 0) + LogFatal("No description file.", ""); + else + Imakefile = "imakefile"; + else + Imakefile = "Imakefile"; + } + return(Imakefile); +} + +void +LogFatalI(s, i) + char *s; + int i; +{ + /*NOSTRICT*/ + LogFatal(s, (char *)i); +} + +void +LogFatal(x0,x1) + char *x0, *x1; +{ + static boolean entered = FALSE; + + if (entered) + return; + entered = TRUE; + + LogMsg(x0, x1); + fprintf(stderr, " Stop.\n"); + wrapup(); + exit(1); +} + +void +LogMsg(x0,x1) + char *x0, *x1; +{ + int error_number = errno; + + if (error_number) { + fprintf(stderr, "%s: ", program); + fprintf(stderr, "%s\n", strerror(error_number)); + } + fprintf(stderr, "%s: ", program); + fprintf(stderr, x0, x1); + fprintf(stderr, "\n"); +} + +void +showargs(argv) + char **argv; +{ + for (; *argv; argv++) + fprintf(stderr, "%s ", *argv); + fprintf(stderr, "\n"); +} + +#define ImakefileCHeader "/* imake - temporary file */" + +void +CheckImakefileC(masterc) + char *masterc; +{ + char mkcbuf[1024]; + FILE *inFile; + + if (access(masterc, F_OK) == 0) { + inFile = fopen(masterc, "r"); + if (inFile == NULL) + LogFatal("Refuse to overwrite: %s", masterc); + if ((fgets(mkcbuf, sizeof(mkcbuf), inFile) && + strncmp(mkcbuf, ImakefileCHeader, + sizeof(ImakefileCHeader)-1))) + { + fclose(inFile); + LogFatal("Refuse to overwrite: %s", masterc); + } + fclose(inFile); + } +} + +#define LocalDefineFmt "#define %s \"%s\"\n" +#define IncludeFmt "#include %s\n" +#define ImakeDefSym "INCLUDE_IMAKEFILE" +#define ImakeTmplSym "IMAKE_TEMPLATE" +#define OverrideWarning "Warning: local file \"%s\" overrides global macros." + +boolean +optional_include(inFile, defsym, fname) + FILE *inFile; + char *defsym; + char *fname; +{ + errno = 0; + if (access(fname, R_OK) == 0) { + LogMsg(OverrideWarning, fname); + return (fprintf(inFile, LocalDefineFmt, defsym, fname) < 0 || + fprintf(inFile, IncludeFmt, defsym) < 0); + } + return FALSE; +} + +void +doit(outfd, cmd, argv) + FILE *outfd; + char *cmd; + char **argv; +{ + int pid; + waitType status; + + /* + * Fork and exec the command. + */ +#ifdef WIN32 + if (outfd) + dup2(fileno(outfd), 1); + status = _spawnvp(_P_WAIT, cmd, argv); + if (status < 0) + LogFatal("Cannot spawn %s.", cmd); + if (status > 0) + LogFatalI("Exit code %d.", status); +#else + pid = fork(); + if (pid < 0) + LogFatal("Cannot fork.", ""); + if (pid) { /* parent... simply wait */ + while (wait(&status) > 0) { + errno = 0; + if (WIFSIGNALED(status)) + LogFatalI("Signal %d.", waitSig(status)); + if (WIFEXITED(status) && waitCode(status)) + LogFatalI("Exit code %d.", waitCode(status)); + } + } + else { /* child... dup and exec cmd */ + if (verbose) + showargs(argv); + if (outfd) + dup2(fileno(outfd), 1); + execvp(cmd, argv); + LogFatal("Cannot exec %s.", cmd); + } +#endif +} + +#ifndef WIN32 +static void +parse_utsname(name, fmt, result, msg) + struct utsname *name; + char *fmt; + char *result; + char *msg; +{ + char buf[SYS_NMLN * 5 + 1]; + char *ptr = buf; + int arg; + + /* Assemble all the pieces into a buffer. */ + for (arg = 0; fmt[arg] != ' '; arg++) + { + /* Our buffer is only guaranteed to hold 5 arguments. */ + if (arg >= 5) + LogFatal(msg, fmt); + + switch (fmt[arg]) + { + case 's': + if (arg > 0) + *ptr++ = ' '; + strcpy(ptr, name->sysname); + ptr += strlen(ptr); + break; + + case 'n': + if (arg > 0) + *ptr++ = ' '; + strcpy(ptr, name->nodename); + ptr += strlen(ptr); + break; + + case 'r': + if (arg > 0) + *ptr++ = ' '; + strcpy(ptr, name->release); + ptr += strlen(ptr); + break; + + case 'v': + if (arg > 0) + *ptr++ = ' '; + strcpy(ptr, name->version); + ptr += strlen(ptr); + break; + + case 'm': + if (arg > 0) + *ptr++ = ' '; + strcpy(ptr, name->machine); + ptr += strlen(ptr); + break; + + default: + LogFatal(msg, fmt); + } + } + + /* Just in case... */ + if (strlen(buf) >= sizeof(buf)) + LogFatal("Buffer overflow parsing uname.", ""); + + /* Parse the buffer. The sscanf() return value is rarely correct. */ + *result = '\0'; + (void) sscanf(buf, fmt + arg + 1, result); +} + +/* Trim leading 0's and periods from version names. The 0's cause + the number to be interpreted as octal numbers. Some version strings + have the potential for different numbers of .'s in them. + */ + +static char * +trim_version(p) + char *p; +{ + + if (p != 0 && *p != '\0') + { + while ((*p == '0' || *p == '.') && *(p + 1) != '\0') + ++p; + } + return (p); +} +#endif + +#ifdef linux +static void get_distrib(inFile) + FILE* inFile; +{ + struct stat sb; + + static char* yast = "/sbin/YaST"; + static char* redhat = "/etc/redhat-release"; + + fprintf (inFile, "%s\n", "#define LinuxUnknown 0"); + fprintf (inFile, "%s\n", "#define LinuxSuSE 1"); + fprintf (inFile, "%s\n", "#define LinuxCaldera 2"); + fprintf (inFile, "%s\n", "#define LinuxCraftworks 3"); + fprintf (inFile, "%s\n", "#define LinuxDebian 4"); + fprintf (inFile, "%s\n", "#define LinuxInfoMagic 5"); + fprintf (inFile, "%s\n", "#define LinuxKheops 6"); + fprintf (inFile, "%s\n", "#define LinuxPro 7"); + fprintf (inFile, "%s\n", "#define LinuxRedHat 8"); + fprintf (inFile, "%s\n", "#define LinuxSlackware 9"); + fprintf (inFile, "%s\n", "#define LinuxTurbo 10"); + fprintf (inFile, "%s\n", "#define LinuxWare 11"); + fprintf (inFile, "%s\n", "#define LinuxYggdrasil 12"); + + if (lstat (yast, &sb) == 0) { + fprintf (inFile, "%s\n", "#define DefaultLinuxDistribution LinuxSuSE"); + return; + } + if (lstat (redhat, &sb) == 0) { + fprintf (inFile, "%s\n", "#define DefaultLinuxDistribution LinuxRedHat"); + return; + } + /* what's the definitive way to tell what any particular distribution is? */ + + fprintf (inFile, "%s\n", "#define DefaultLinuxDistribution LinuxUnknown"); + /* would like to know what version of the distribution it is */ +} + +static const char *libc_c= +"#include <stdio.h>\n" +"#include <ctype.h>\n" +"\n" +"#if 0\n" +"#pragma weak gnu_get_libc_version\n" +"#pragma weak __libc_version\n" +"#pragma weak __linux_C_lib_version\n" +"#else\n" +"asm (\".weak gnu_get_libc_version\");\n" +"asm (\".weak __libc_version\");\n" +"asm (\".weak __linux_C_lib_version\");\n" +"#endif\n" +"\n" +"extern const char * gnu_get_libc_version (void);\n" +"extern const char * __linux_C_lib_version;\n" +"extern const char __libc_version [];\n" +"\n" +"int\n" +"main ()\n" +"{\n" +" int libcmajor = 0, libcminor = 0, libcteeny = 0;\n" +"\n" +" if (((&__linux_C_lib_version != 0)\n" +" && ((&__libc_version != 0) || (gnu_get_libc_version != 0)))\n" +" || (!(&__linux_C_lib_version != 0) && !(&__libc_version != 0)\n" +" && !(gnu_get_libc_version != 0)))\n" +" {\n" +" libcmajor = 0;\n" +" libcminor = 0;\n" +" libcteeny = 0;\n" +" }\n" +" else\n" +" {\n" +" const char * ptr;\n" +" int glibcmajor = 0;\n" +"\n" +" if (gnu_get_libc_version != 0)\n" +" {\n" +" ptr = gnu_get_libc_version ();\n" +" glibcmajor = 4;\n" +" }\n" +" else if (&__libc_version != 0)\n" +" {\n" +" ptr = __libc_version;\n" +" glibcmajor = 4;\n" +" }\n" +" else\n" +" ptr = __linux_C_lib_version;\n" +"\n" +" while (!isdigit (*ptr))\n" +" ptr++;\n" +"\n" +" sscanf (ptr, \"%d.%d.%d\", &libcmajor, &libcminor, &libcteeny);\n" +" libcmajor += glibcmajor;\n" +" }\n" +"\n" +" printf(\"#define DefaultLinuxCLibMajorVersion %d\\n\", libcmajor);\n" +" printf(\"#define DefaultLinuxCLibMinorVersion %d\\n\", libcminor);\n" +" printf(\"#define DefaultLinuxCLibTeenyVersion %d\\n\", libcteeny);\n" +"\n" +" return 0;\n" +"}\n" +; + +static void get_libc_version(inFile) + FILE* inFile; +{ + static char* libcso = "/usr/lib/libc.so"; + struct stat sb; + char buf[PATH_MAX]; + char* ptr; + int libcmajor, libcminor, libcteeny; + + if (lstat (libcso, &sb) == 0) { + if (S_ISLNK (sb.st_mode)) { + /* + * /usr/lib/libc.so is a symlink -- this is libc 5.x + * we can do this the quick way + */ + if (readlink (libcso, buf, PATH_MAX) >= 0) { + for (ptr = buf; *ptr && !isdigit (*ptr); ptr++); + (void) sscanf (ptr, "%d.%d.%d", &libcmajor, &libcminor, &libcteeny); + fprintf(inFile, "#define DefaultLinuxCLibMajorVersion %d\n", libcmajor); + fprintf(inFile, "#define DefaultLinuxCLibMinorVersion %d\n", libcminor); + fprintf(inFile, "#define DefaultLinuxCLibTeenyVersion %d\n", libcteeny); + } + } else { + /* + * /usr/lib/libc.so is NOT a symlink -- this is libc 6.x / glibc 2.x + * now we have to figure this out the hard way. + */ + char *aout = tmpnam (NULL); + FILE *fp; + const char *format = "%s -o %s -x c -"; + char *cc; + int len; + char *command; + + cc = getenv ("CC"); + if (cc == NULL) + cc = "gcc"; + len = strlen (aout) + strlen (format) + strlen (cc); + if (len < 128) len = 128; + command = alloca (len); + + if (snprintf (command , len, format, cc, aout) == len) + abort (); + + fp = popen (command, "w"); + if (fp == NULL || fprintf (fp, "%s\n", libc_c) < 0 || pclose (fp) != 0) + abort (); + + fp = popen (aout, "r"); + if (fp == NULL) + abort (); + + while (fgets (command, len, fp)) + fprintf (inFile, command); + + len = pclose (fp); + remove (aout); + if (len) + abort (); + } + } +} + +static void get_ld_version(inFile) + FILE* inFile; +{ + FILE* ldprog = popen ("ld -v", "r"); + char c; + int ldmajor, ldminor; + + if (ldprog) { + do { + c = fgetc (ldprog); + } while (c != EOF && !isdigit (c)); + ungetc (c, ldprog); + (void) fscanf (ldprog, "%d.%d", &ldmajor, &ldminor); + fprintf(inFile, "#define DefaultLinuxBinUtilsMajorVersion %d\n", + ldmajor * 10 + ldminor); + pclose (ldprog); + } +} +#endif + +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +#if defined(sun) && defined(__SVR4) +static void get_sun_compiler_versions (inFile) + FILE* inFile; +{ + char buf[PATH_MAX]; + char cmd[PATH_MAX]; + static char* sunpro_cc = "/opt/SUNWspro/bin/cc"; + static char* sunpro_CC = "/opt/SUNWspro/bin/CC"; + int cmajor, cminor; + char* vptr; + struct stat sb; + FILE* ccproc; + + if (lstat (sunpro_cc, &sb) == 0) { + strcpy (cmd, sunpro_cc); + strcat (cmd, " -V 2>&1"); + if ((ccproc = popen (cmd, "r")) != NULL) { + if (fgets (buf, PATH_MAX, ccproc) != NULL) { + vptr = strrchr (buf, 'C'); + for (; !isdigit(*vptr); vptr++); + (void) sscanf (vptr, "%d.%d", &cmajor, &cminor); + fprintf (inFile, + "#define DefaultSunProCCompilerMajorVersion %d\n", + cmajor); + fprintf (inFile, + "#define DefaultSunProCCompilerMinorVersion %d\n", + cminor); + } + while (fgets (buf, PATH_MAX, ccproc) != NULL) {}; + pclose (ccproc); + } + } + if (lstat (sunpro_CC, &sb) == 0) { + strcpy (cmd, sunpro_CC); + strcat (cmd, " -V 2>&1"); + if ((ccproc = popen (cmd, "r")) != NULL) { + if (fgets (buf, PATH_MAX, ccproc) != NULL) { + vptr = strrchr (buf, 'C'); + for (; !isdigit(*vptr); vptr++); + (void) sscanf (vptr, "%d.%d", &cmajor, &cminor); + fprintf (inFile, + "#define DefaultSunProCplusplusCompilerMajorVersion %d\n", + cmajor); + fprintf (inFile, + "#define DefaultSunProCplusplusCompilerMinorVersion %d\n", + cminor); + } + while (fgets (buf, PATH_MAX, ccproc) != NULL) {}; + pclose (ccproc); + } + } +} +#endif + +static void get_gcc_incdir(inFile) + FILE* inFile; +{ + static char* gcc_path[] = { +#ifdef linux + "/usr/bin/cc", /* for Linux PostIncDir */ +#endif + "/usr/local/bin/gcc", + "/opt/gnu/bin/gcc" + }; + struct stat sb; + int i; + FILE* gccproc; + char buf[PATH_MAX]; + char cmd[PATH_MAX]; + char* ptr; + + buf[0] = '\0'; + for (i = 0; i < sizeof gcc_path / sizeof gcc_path[0]; i++) { + if (lstat (gcc_path[i], &sb) == 0) { + strcpy (cmd, gcc_path[i]); + strcat (cmd, " --print-libgcc-file-name"); + if ((gccproc = popen (cmd, "r")) != NULL) { + if (fgets (buf, PATH_MAX, gccproc) != NULL) { + ptr = strstr (buf, "libgcc.a"); + if (ptr) strcpy (ptr, "include"); + } + (void) pclose (gccproc); + break; + } + } + } + if (buf[0]) + fprintf (inFile, "#define DefaultGccIncludeDir %s\n", buf); +} + +boolean +define_os_defaults(inFile) + FILE *inFile; +{ +#ifndef WIN32 +#if (defined(DEFAULT_OS_NAME) || defined(DEFAULT_OS_MAJOR_REV) || \ + defined(DEFAULT_OS_MINOR_REV) || defined(DEFAUL_OS_TEENY_REV)) + struct utsname name; + char buf[SYS_NMLN * 5 + 1]; + + /* Obtain the system information. */ + if (uname(&name) < 0) + LogFatal("Cannot invoke uname", ""); + +# ifdef DEFAULT_OS_NAME + parse_utsname(&name, DEFAULT_OS_NAME, buf, + "Bad DEFAULT_OS_NAME syntax %s"); + if (buf[0] != '\0') + fprintf(inFile, "#define DefaultOSName %s\n", buf); +# endif + +# ifdef DEFAULT_OS_MAJOR_REV + parse_utsname(&name, DEFAULT_OS_MAJOR_REV, buf, + "Bad DEFAULT_OS_MAJOR_REV syntax %s"); + fprintf(inFile, "#define DefaultOSMajorVersion %s\n", + *buf ? trim_version(buf) : "0"); +# endif + +# ifdef DEFAULT_OS_MINOR_REV + parse_utsname(&name, DEFAULT_OS_MINOR_REV, buf, + "Bad DEFAULT_OS_MINOR_REV syntax %s"); + fprintf(inFile, "#define DefaultOSMinorVersion %s\n", + *buf ? trim_version(buf) : "0"); +# endif + +# ifdef DEFAULT_OS_TEENY_REV + parse_utsname(&name, DEFAULT_OS_TEENY_REV, buf, + "Bad DEFAULT_OS_TEENY_REV syntax %s"); + fprintf(inFile, "#define DefaultOSTeenyVersion %s\n", + *buf ? trim_version(buf) : "0"); +# endif +#endif +#ifdef linux + get_distrib (inFile); + get_libc_version (inFile); + get_ld_version(inFile); +#endif + get_gcc_incdir(inFile); +#if defined (sun) && defined(SVR4) + get_sun_compiler_versions (inFile); +#endif +#else /* WIN32 */ + OSVERSIONINFO osvi; + static char* os_names[] = { "Win32s", "Windows 95", "Windows NT" }; + + memset(&osvi, 0, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); + GetVersionEx (&osvi); + + fprintf (inFile, "#define DefaultOSName Microsoft %s\n", + os_names[osvi.dwPlatformId]); + + fprintf(inFile, "#define DefaultOSMajorVersion %d\n", osvi.dwMajorVersion); + fprintf(inFile, "#define DefaultOSMinorVersion %d\n", osvi.dwMinorVersion); + fprintf(inFile, "#define DefaultOSTeenyVersion %d\n", + osvi.dwBuildNumber & 0xFFFF); +#endif /* WIN32 */ + return FALSE; +} + +void +cppit(imakefile, template, masterc, outfd, outfname) + char *imakefile; + char *template; + char *masterc; + FILE *outfd; + char *outfname; +{ + FILE *inFile; + + haveImakefileC = TRUE; + inFile = fopen(masterc, "w"); + if (inFile == NULL) + LogFatal("Cannot open %s for output.", masterc); + if (fprintf(inFile, "%s\n", ImakefileCHeader) < 0 || + define_os_defaults(inFile) || + optional_include(inFile, "IMAKE_LOCAL_DEFINES", "localdefines") || + optional_include(inFile, "IMAKE_ADMIN_DEFINES", "admindefines") || + fprintf(inFile, "#define %s <%s>\n", ImakeDefSym, imakefile) < 0 || + fprintf(inFile, LocalDefineFmt, ImakeTmplSym, template) < 0 || + fprintf(inFile, IncludeFmt, ImakeTmplSym) < 0 || + optional_include(inFile, "IMAKE_ADMIN_MACROS", "adminmacros") || + optional_include(inFile, "IMAKE_LOCAL_MACROS", "localmacros") || + fflush(inFile) || + fclose(inFile)) + LogFatal("Cannot write to %s.", masterc); + /* + * Fork and exec cpp + */ + doit(outfd, cpp, cpp_argv); + CleanCppOutput(outfd, outfname); +} + +void +makeit() +{ + doit(NULL, make_argv[0], make_argv); +} + +char * +CleanCppInput(imakefile) + char *imakefile; +{ + FILE *outFile = NULL; + FILE *inFile; + char *buf, /* buffer for file content */ + *pbuf, /* walking pointer to buf */ + *punwritten, /* pointer to unwritten portion of buf */ + *ptoken, /* pointer to # token */ + *pend, /* pointer to end of # token */ + savec; /* temporary character holder */ + int count; + struct stat st; + + /* + * grab the entire file. + */ + if (!(inFile = fopen(imakefile, "r"))) + LogFatal("Cannot open %s for input.", imakefile); + if (fstat(fileno(inFile), &st) < 0) + LogFatal("Cannot stat %s for size.", imakefile); + buf = Emalloc((int)st.st_size+3); + count = fread(buf + 2, 1, st.st_size, inFile); + if (count == 0 && st.st_size != 0) + LogFatal("Cannot read %s:", imakefile); + fclose(inFile); + buf[0] = '\n'; + buf[1] = '\n'; + buf[count + 2] = '\0'; + + punwritten = pbuf = buf + 2; + while (*pbuf) { + /* for compatibility, replace make comments for cpp */ + if (*pbuf == '#' && pbuf[-1] == '\n' && pbuf[-2] != '\\') { + ptoken = pbuf+1; + while (*ptoken == ' ' || *ptoken == '\t') + ptoken++; + pend = ptoken; + while (*pend && *pend != ' ' && *pend != '\t' && *pend != '\n') + pend++; + savec = *pend; + *pend = '\0'; + if (strcmp(ptoken, "define") && + strcmp(ptoken, "if") && + strcmp(ptoken, "ifdef") && + strcmp(ptoken, "ifndef") && + strcmp(ptoken, "include") && + strcmp(ptoken, "line") && + strcmp(ptoken, "else") && + strcmp(ptoken, "elif") && + strcmp(ptoken, "endif") && + strcmp(ptoken, "error") && + strcmp(ptoken, "pragma") && + strcmp(ptoken, "undef")) { + if (outFile == NULL) { + tmpImakefile = Strdup(tmpImakefile); + (void) mktemp(tmpImakefile); + outFile = fopen(tmpImakefile, "w"); + if (outFile == NULL) + LogFatal("Cannot open %s for write.", + tmpImakefile); + } + writetmpfile(outFile, punwritten, pbuf-punwritten, + tmpImakefile); + if (ptoken > pbuf + 1) + writetmpfile(outFile, "XCOMM", 5, tmpImakefile); + else + writetmpfile(outFile, "XCOMM ", 6, tmpImakefile); + punwritten = pbuf + 1; + } + *pend = savec; + } + pbuf++; + } + if (outFile) { + writetmpfile(outFile, punwritten, pbuf-punwritten, tmpImakefile); + fclose(outFile); + return tmpImakefile; + } + + return(imakefile); +} + +void +CleanCppOutput(tmpfd, tmpfname) + FILE *tmpfd; + char *tmpfname; +{ + char *input; + int blankline = 0; + + while((input = ReadLine(tmpfd, tmpfname))) { + if (isempty(input)) { + if (blankline++) + continue; + KludgeResetRule(); + } else { + blankline = 0; + KludgeOutputLine(&input); + writetmpfile(tmpfd, input, strlen(input), tmpfname); + } + writetmpfile(tmpfd, "\n", 1, tmpfname); + } + fflush(tmpfd); +#ifdef NFS_STDOUT_BUG + /* + * On some systems, NFS seems to leave a large number of nulls at + * the end of the file. Ralph Swick says that this kludge makes the + * problem go away. + */ + ftruncate (fileno(tmpfd), (off_t)ftell(tmpfd)); +#endif +} + +/* + * Determine if a line has nothing in it. As a side effect, we trim white + * space from the end of the line. Cpp magic cookies are also thrown away. + * "XCOMM" token is transformed to "#". + */ +boolean +isempty(line) + register char *line; +{ + register char *pend; + + /* + * Check for lines of the form + * # n "... + * or + * # line n "... + */ + if (*line == '#') { + pend = line+1; + if (*pend == ' ') + pend++; + if (*pend == 'l' && pend[1] == 'i' && pend[2] == 'n' && + pend[3] == 'e' && pend[4] == ' ') + pend += 5; + if (isdigit(*pend)) { + do { + pend++; + } while (isdigit(*pend)); + if (*pend == '\n' || *pend == '\0') + return(TRUE); + if (*pend++ == ' ' && *pend == '"') + return(TRUE); + } + while (*pend) + pend++; + } else { + for (pend = line; *pend; pend++) { + if (*pend == 'X' && pend[1] == 'C' && pend[2] == 'O' && + pend[3] == 'M' && pend[4] == 'M' && + (pend == line || pend[-1] == ' ' || pend[-1] == '\t') && + (pend[5] == ' ' || pend[5] == '\t' || pend[5] == '\0')) + { + *pend = '#'; + strcpy(pend+1, pend+5); + } +#ifdef MAGIC_MAKE_VARS + if (*pend == 'X' && pend[1] == 'V' && pend[2] == 'A' && + pend[3] == 'R') + { + char varbuf[5]; + int i; + + if (pend[4] == 'd' && pend[5] == 'e' && pend[6] == 'f' && + pend[7] >= '0' && pend[7] <= '9') + { + i = pend[7] - '0'; + sprintf(varbuf, "%0.4d", xvariable); + strncpy(pend+4, varbuf, 4); + xvariables[i] = xvariable; + xvariable = (xvariable + 1) % 10000; + } + else if (pend[4] == 'u' && pend[5] == 's' && + pend[6] == 'e' && pend[7] >= '0' && + pend[7] <= '9') + { + i = pend[7] - '0'; + sprintf(varbuf, "%0.4d", xvariables[i]); + strncpy(pend+4, varbuf, 4); + } + } +#endif + } + } + while (--pend >= line && (*pend == ' ' || *pend == '\t')) ; + pend[1] = '\0'; + return (*line == '\0'); +} + +/*ARGSUSED*/ +char * +ReadLine(tmpfd, tmpfname) + FILE *tmpfd; + char *tmpfname; +{ + static boolean initialized = FALSE; + static char *buf, *pline, *end; + register char *p1, *p2; + + if (! initialized) { +#ifdef WIN32 + FILE *fp = tmpfd; +#endif + int total_red; + struct stat st; + + /* + * Slurp it all up. + */ + fseek(tmpfd, 0, 0); + if (fstat(fileno(tmpfd), &st) < 0) + LogFatal("cannot stat %s for size", tmpMakefile); + pline = buf = Emalloc((int)st.st_size+1); + total_red = fread(buf, 1, st.st_size, tmpfd); + if (total_red == 0 && st.st_size != 0) + LogFatal("cannot read %s", tmpMakefile); + end = buf + total_red; + *end = '\0'; + fseek(tmpfd, 0, 0); +#if defined(SYSV) || defined(WIN32) + tmpfd = freopen(tmpfname, "w+", tmpfd); +#ifdef WIN32 + if (! tmpfd) /* if failed try again */ + tmpfd = freopen(tmpfname, "w+", fp); +#endif + if (! tmpfd) + LogFatal("cannot reopen %s\n", tmpfname); +#else /* !SYSV */ + ftruncate(fileno(tmpfd), (off_t) 0); +#endif /* !SYSV */ + initialized = TRUE; + fprintf (tmpfd, "# Makefile generated by imake - do not edit!\n"); + fprintf (tmpfd, "# %s\n", + "$Xorg: imake.c,v 1.6 2001/02/09 02:03:15 xorgcvs Exp $"); + } + + for (p1 = pline; p1 < end; p1++) { + if (*p1 == '@' && *(p1+1) == '@' + /* ignore ClearCase version-extended pathnames */ + && !(p1 != pline && !isspace(*(p1-1)) && *(p1+2) == '/')) + { /* soft EOL */ + *p1++ = '\0'; + p1++; /* skip over second @ */ + break; + } + else if (*p1 == '\n') { /* real EOL */ +#ifdef WIN32 + if (p1 > pline && p1[-1] == '\r') + p1[-1] = '\0'; +#endif + *p1++ = '\0'; + break; + } + } + + /* + * return NULL at the end of the file. + */ + p2 = (pline == p1 ? NULL : pline); + pline = p1; + return(p2); +} + +void +writetmpfile(fd, buf, cnt, fname) + FILE *fd; + int cnt; + char *buf; + char *fname; +{ + if (fwrite(buf, sizeof(char), cnt, fd) == -1) + LogFatal("Cannot write to %s.", fname); +} + +char * +Emalloc(size) + int size; +{ + char *p; + + if ((p = malloc(size)) == NULL) + LogFatalI("Cannot allocate %d bytes", size); + return(p); +} + +#ifdef FIXUP_CPP_WHITESPACE +void +KludgeOutputLine(pline) + char **pline; +{ + char *p = *pline; + char quotechar = '\0'; + + switch (*p) { + case '#': /*Comment - ignore*/ + break; + case '\t': /*Already tabbed - ignore it*/ + break; + case ' ': /*May need a tab*/ + default: +# ifdef INLINE_SYNTAX + if (*p == '<' && p[1] == '<') { /* inline file close */ + InInline--; + InRule = TRUE; + break; + } +# endif + /* + * The following cases should not be treated as beginning of + * rules: + * variable := name (GNU make) + * variable = .*:.* (':' should be allowed as value) + * sed 's:/a:/b:' (: used in quoted values) + */ + for (; *p; p++) { + if (quotechar) { + if (quotechar == '\\' || + (*p == quotechar && +# ifdef WIN32 + quotechar != ')' && +# endif + p[-1] != '\\')) + quotechar = '\0'; + continue; + } + switch (*p) { + case '\\': + case '"': + case '\'': + quotechar = *p; + break; + case '(': + quotechar = ')'; + break; + case '{': + quotechar = '}'; + break; + case '[': + quotechar = ']'; + break; + case '=': +# ifdef REMOVE_CPP_LEADSPACE + if (!InRule && **pline == ' ') { + while (**pline == ' ') + (*pline)++; + } +# endif + goto breakfor; +# ifdef INLINE_SYNTAX + case '<': + if (p[1] == '<') /* inline file start */ + InInline++; + break; +# endif + case ':': + if (p[1] == '=') + goto breakfor; + while (**pline == ' ') + (*pline)++; + InRule = TRUE; + return; + } + } +breakfor: + if (InRule && **pline == ' ') + **pline = '\t'; + break; + } +} + +void +KludgeResetRule() +{ + InRule = FALSE; +} +#endif /* FIXUP_CPP_WHITESPACE */ + +char * +Strdup(cp) + register char *cp; +{ + register char *new = Emalloc(strlen(cp) + 1); + + strcpy(new, cp); + return new; +} |