[comp.os.minix] cppmake - Part 2 of 2

cechew@bruce.OZ (Earl Chew) (11/09/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 2)."
# Contents:  cppmake.c
# Wrapped by cechew@bruce on Thu Nov  9 20:07:23 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'cppmake.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cppmake.c'\"
else
echo shar: Extracting \"'cppmake.c'\" \(26995 characters\)
sed "s/^X//" >'cppmake.c' <<'END_OF_FILE'
X/*				C P P M A K E
X *
X *			Author: C. E. Chew
X *			Date:   October 1989
X *
X * (C) Copyright C E Chew
X *
X * Feel free to copy, use and distribute this software provided:
X *
X *	1. you do not pretend that you wrote it
X *	2. you leave this copyright notice intact.
X *
X * Patchlevel 1.5
X *
X * Edit History:
X * 03-Nov-1989	Oops mixed up WSTOPSIG and WTERMSIG. Patches from Bruce
X *		Evans about running in background and add pid_t for
X *		_MINIX. Scan CPPMAKECPP and CPPMAKEMAKE to break up
X *		into word sized morsels. Leave comments intact.
X * 01-Nov-1989	Don't use @$ since some systems use $ as alphabetic.
X *		More problems with getline. Add CPPOPT. Add -M and -C.
X * 31-Oct-1989	Quote with printable characters.
X * 23-Oct-1989	Fixed getline() looping bug. Fix single argument problem.
X */
X
X#include <ctype.h>
X#include <stdio.h>
X#include <signal.h>
X
X#ifndef	_MSDOS
X#include <sys/types.h>
X#endif
X
X#ifdef	_BSD
X#include <strings.h>
X#else
X#include <string.h>
X#endif
X
X#ifdef	_BSD
X#include <sys/wait.h>
X#endif
X
X#ifdef	_MSDOS
X#include <process.h>
X#endif
X
X/* Varargs handling */
X
X#ifdef		__STDC__
X# include <stdarg.h>
X# define VA_START(n,l)	va_start(n,l)
X# define VA_ALIST	...
X# define VA_LIST	va_list
X# define VA_END(n)	va_end(n)
X# define VA_ARG(n,t)	va_arg(n,t)
X# define VA_DCL
X#else
X# include <varargs.h>
X# define VA_START(n,l)	va_start(n)
X# define VA_ALIST	va_alist
X# define VA_LIST	va_list
X# define VA_END(n)	va_end(n)
X# define VA_ARG(n,t)	va_arg(n,t)
X# define VA_DCL		va_dcl
X#endif
X
X/* Function prototypes */
X
X#ifdef	__STDC__
X#define	P(x)		x
X#define T(x,y)		x
X#define D(x)
X#else
X#define P(x)		()
X#define T(x,y)		y
X#define D(x)		x;
X#endif
X
X/* Local definitions */
X
X#define DEFINE		"-D"		/* define */
X#define INCLUDE		"-I"		/* include */
X#define UNDEF		"-U"		/* undefine */
X
X#define INCFILE		".i"		/* extension for include file */
X
X#define MAXARGS		20		/* maximum args in process descriptor */
X
X#define GETLINELENGTH	80		/* initial allocation for getline */
X#define GETLINEINC	20		/* increment for getline */
X
X#define CATHEADERWIDTH	60		/* header width for cat */
X
X#define CPPINPUT	"/tmp/cppiXXXXXX"/* cpp input */
X#define CPPOUTPUT	"/tmp/cppoXXXXXX"/* cpp output */
X#define MAKEFILE	"/tmp/makeXXXXXX"/* make file */
X
X/* System deficiencies */
X
X#ifdef	_BSD
Xtypedef	int pid_t;			/* process id */
Xtypedef union wait wait_t;		/* wait return structure */
X#endif
X
X#ifdef	_MINIX
Xtypedef	int pid_t;			/* process id */
Xtypedef int wait_t;			/* wait return structure */
X#endif
X
X#ifdef	INTSIGNAL
Xtypedef	int signal_t;			/* signal handler return */
X#else
Xtypedef void signal_t;			/* signal handler return */
X#endif
X
X#ifdef	CHARMALLOC
Xtypedef char *malloc_t;			/* malloc return type */
Xtypedef int free_t;			/* free return type */
X#else
Xtypedef void *malloc_t;			/* malloc return type */
Xtypedef void free_t;			/* free return type */
X#endif
X
X#ifndef	_MSDOS
X
X#ifndef	WIFSTOPPED
X#define WIFSTOPPED(x)	((*((unsigned int *) (&(x))) & 0xff) == 0x7f)
X#endif
X
X#ifndef	WIFEXITED
X#define WIFEXITED(x)	((*((unsigned int *) (&(x))) & 0xff) == 0)
X#endif
X
X#ifndef	WIFSIGNALED
X#define WIFSIGNALED(x)	((*((unsigned int *) (&(x))) - 1 & 0xffff) <  0xff)
X#endif
X
X#ifndef	WSTOPSIG
X#define WSTOPSIG(x)	((*((unsigned int *) (&(x))) >> 8) & 0xff)
X#endif
X
X#ifndef	WEXITSTATUS
X#define WEXITSTATUS(x)	((*((unsigned int *) (&(x))) >> 8) & 0xff)
X#endif
X
X#ifndef	WTERMSIG
X#define WTERMSIG(x)	(*((unsigned int *) (&(x))) & 0x7f)
X#endif
X
X#endif
X
X#ifdef	_BSD
X#define strrchr rindex
X#endif
X
X/* Library function prototypes */
X
Xmalloc_t malloc P((unsigned int));	/* raw malloc */
Xmalloc_t realloc P((malloc_t, unsigned int));/* raw realloc */
Xfree_t free P((void *));		/* raw free */
Xsignal_t (*signal P((int, signal_t (*)(int)))) P((int)); /* signal handler */
Xchar *mktemp P((char *));		/* make a temporary filename */
Xint unlink P((char *));			/* remove file */
Xint dup P((int));			/* duplicate handle */
Xint dup2 P((int, int));			/* duplicate handle */
Xint close P((int));			/* close handle */
X
X#ifndef		_MSDOS
Xint wait P((wait_t *));			/* wait for child */
Xpid_t wait P((int *));			/* wait for child */
Xpid_t fork P((void));			/* fork a child */
Xint execvp P((char *, char **));	/* execute process */
X#endif
X
Xint getopt P((int, char **, char *));	/* get options */
Xchar *getenv P((char *));		/* get environment */
X
X/* Library externals */
X
Xextern char *optarg;			/* option argument */
Xextern int opterr;			/* error processing */
Xextern int optind;			/* argc index */
X
X/* Local types */
X
Xtypedef struct process {
X  int argc;
X  char *argv[MAXARGS+1];
X} PROCESS;
X
X/* Local function prototypes */
X
Xchar *getline P((FILE *));		/* read a line */
XFILE *ftemp P((char *, char **));	/* create a temporary file */
Xchar *smalloc P((unsigned int));	/* safe malloc */
Xchar *srealloc P((char *, unsigned int));/* safe realloc */
Xchar *stringdup P((char *));		/* duplicate a string */
Xint procreate P((PROCESS *, FILE *, FILE *)); /* doctors and nurses */
Xvoid appendarg P((PROCESS *, ...));	/* append an argument */
Xvoid appendlist P((PROCESS *, char **));/* append an argument vector */
Xvoid setarg P((PROCESS *, char *));	/* set arguments */
Xvoid cpppreamble P((FILE *));		/* insert cpp preamble */
Xvoid cppmaketocpp P((FILE *, FILE *));	/* cppmake to cpp */
Xvoid cpptomake P((FILE *, FILE *));	/* cpp to make */
Xsignal_t wrapup P((int));		/* signal wrapup */
Xvoid purge P((void));			/* purge temporary files */
Xvoid done P((int));			/* purge then exit */
Xvoid cat P((char *, FILE *, FILE *));	/* dump a file */
Xvoid dsync P((FILE *));			/* sync a stream */
X
X/* Process descriptors */
X
XPROCESS cpp =  {1, {"/lib/cpp", 0}};
XPROCESS make = {1, {"make",     0}};
XPROCESS cppargs  = {0, {0}};
XPROCESS makeargs = {0, {0}};
X
X/* File names */
X
Xchar *CppInputName    = 0;		/* name of cpp input */
Xchar *CppOutputName   = 0;		/* name of cpp output */
Xchar *MakefileName    = 0;		/* name of makefile */
Xchar *CppMakefileName = 0;		/* name of cppmake file */
Xchar *CppMakeMake     = 0;		/* name of make program */
Xchar *CppMakeCpp      = 0;		/* name of cpp program */
X
X/* Option switches  */
X
Xchar verbose     = 0;			/* verbose */
Xchar nomake      = 0;			/* don't start make */
Xchar compileonly = 0;			/* compile only */
X
X/* Main program */
X
Xint main(T(int argc, argc), T(char *argv[], argv))
X
XD(int argc)				/* number of arguments */
XD(char *argv[])				/* argument vector */
X
X{
X  FILE *CppMakefile;			/* cppmake file */
X  FILE *CppInput;			/* cpp input */
X  FILE *CppOutput;			/* cpp output */
X  FILE *Makefile;			/* makefile */
X  int sw;				/* option switch */
X  int status;				/* exit status */
X  char *env;				/* environment */
X  char *DefineArg;			/* -D argument */
X  char *UndefArg;			/* -U argument */
X  char *IncludeArg;			/* -I argument */
X  char *OutfileName;			/* name of output file */
X  int lastopt;				/* last option */
X
X/* Cleanup */
X#ifdef	SIGINT
X  if (signal(SIGINT,  SIG_IGN) != SIG_IGN)
X    (void) signal(SIGINT,  wrapup);
X#endif
X#ifdef	SIGHUP
X  if (signal(SIGHUP,  SIG_IGN) != SIG_IGN)
X    (void) signal(SIGHUP,  wrapup);
X#endif
X#ifdef	SIGQUIT
X  if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
X    (void) signal(SIGQUIT, wrapup);
X#endif
X
X/* Check for environment */
X  if ((env = getenv("CPPMAKEMAKE")) != 0)
X    CppMakeMake = stringdup(env);
X  if ((env = getenv("CPPMAKECPP")) != 0)
X    CppMakeCpp = stringdup(env);
X
X/* Do options */
X  lastopt = 1;
X  sw = opterr = 0;
X  OutfileName = 0;
X  while (sw != '?' && (sw = getopt(argc, argv, "o:f:D:I:U:M:C:cv")) != EOF) {
X    switch (sw) {
X    case 'v':
X      verbose = 1;
X      break;
X
X    case 'c':
X      compileonly = 1;
X      break;
X
X    case 'f':
X      CppMakefileName = optarg;
X      break;
X
X    case 'M':
X      CppMakeMake = optarg;
X      break;
X
X    case 'C':
X      CppMakeCpp = optarg;
X      break;
X
X    case 'D':
X      DefineArg = smalloc((unsigned int) (strlen(optarg) + sizeof(DEFINE)));
X      (void) strcpy(DefineArg, DEFINE);
X      (void) strcat(DefineArg, optarg);
X      appendarg(&cppargs, DefineArg, (char *) 0);
X      break;
X
X    case 'I':
X      IncludeArg = smalloc((unsigned int) (strlen(optarg) + sizeof(INCLUDE)));
X      (void) strcpy(IncludeArg, INCLUDE);
X      (void) strcat(IncludeArg, optarg);
X      appendarg(&cppargs, IncludeArg, (char *) 0);
X      break;
X
X    case 'U':
X      UndefArg = smalloc((unsigned int) (strlen(optarg) + sizeof(UNDEF)));
X      (void) strcpy(UndefArg, UNDEF);
X      (void) strcat(UndefArg, optarg);
X      appendarg(&cppargs, UndefArg, (char *) 0);
X      break;
X
X    case 'o':
X      OutfileName = optarg;
X      break;
X
X    case '?':
X      break;
X    }
X
X    if (sw != '?')
X      lastopt = optind;
X  }
X
X/* Move argument indicator back to last argument */
X  if (optind < argc)
X    lastopt = optind;
X
X/* Set the process names */
X  if (CppMakeMake != 0)
X    setarg(&make, CppMakeMake);
X  if (CppMakeCpp != 0)
X    setarg(&cpp, CppMakeCpp);
X
X/* Add cpp and make options */
X#ifdef CPPOPT
X  appendarg(&cpp, CPPOPT, (char *) 0);
X#endif
X  appendlist(&cpp,  &cppargs.argv[0]);
X  appendlist(&make, &makeargs.argv[0]);
X
X/* Set the name of the makefile and the name of the cppmake file */
X  if (! compileonly) {
X
X/* Makefile name */
X    if (OutfileName != 0) {
X      nomake       = 1;
X      MakefileName = OutfileName;
X    }
X
X/* Cppmake file name */
X    if (CppMakefileName != 0)
X      CppMakefile = fopen(CppMakefileName, "r");
X    else {
X      CppMakefileName = "Makefile.cpp";
X      if ((CppMakefile = fopen(CppMakefileName, "r")) == 0) {
X	CppMakefileName = "makefile.cpp";
X	CppMakefile = fopen(CppMakefileName, "r");
X      }
X    }
X  }
X
X/* Set the name of the cppmake file and the output file */
X  else {
X    if (lastopt == argc && CppMakefileName == 0) {
X      (void) fputs("No cppmake file specified.\n", stderr);
X      exit(1);
X    }
X    else if (CppMakefileName == 0 && lastopt < argc-1 ||
X	     CppMakefileName != 0 && lastopt < argc) {
X      (void) fputs("Too many cppmake files specified.\n", stderr);
X      exit(1);
X    }
X
X    if (CppMakefileName == 0) {
X      CppMakefileName = argv[lastopt];
X      lastopt = argc;
X    }
X
X    CppMakefile = fopen(CppMakefileName, "r");
X
X    if (OutfileName != 0)
X      CppInputName = OutfileName;
X    else {
X      if ((OutfileName = strrchr(CppMakefileName, '.')) == 0) {
X	(void) fprintf(stderr, "Cannot make output file name from %s.\n",
X		       CppMakefileName);
X        exit(1);
X      }
X      sw = OutfileName - CppMakefileName;
X      CppInputName = smalloc((unsigned int) (sw+sizeof(INCFILE)));
X      (void) strncpy(CppInputName, CppMakefileName, sw);
X      (void) strcpy(CppInputName+sw, INCFILE);
X    }
X  }
X
X/* Check that the cppmake file is ok */
X  if (CppMakefile == 0) {
X    (void) fputs("Cannot open makefile\n", stderr);
X    exit(1);
X  }
X
X/* Create the temporary files */
X  if (compileonly) {
X    if ((CppInput = fopen(CppInputName, "w")) == 0) {
X      (void) fprintf(stderr, "Cannot open output file %s.\n", CppInputName);
X      exit(1);
X    }
X  }
X  else {
X
X/* Open the temporary files */
X    CppInput  = ftemp(CPPINPUT,  &CppInputName);
X    CppOutput = ftemp(CPPOUTPUT, &CppOutputName);
X    if (MakefileName == 0)
X      Makefile = ftemp(MAKEFILE, &MakefileName);
X    else if ((Makefile = fopen(MakefileName, "w")) == 0) {
X      (void) fprintf(stderr, "Cannot open makefile %s.\n", MakefileName);
X      done(1);
X    }
X
X/* Insert the cpp preamble */
X    cpppreamble(CppInput);
X  }
X
X/* Convert cppmake file for cpp input */
X  cppmaketocpp(CppMakefile, CppInput);
X  dsync(CppInput);
X
X/* Compile only so don't erase output file */
X  if (compileonly)
X    CppInputName = 0;
X
X  else {
X
X/* Run cpp on the cpp input file */
X    if (verbose)
X      cat("Preprocessor Input", CppInput, stderr);
X    if ((status = procreate(&cpp, CppInput, CppOutput)) != 0)
X      done(status);
X    dsync(CppOutput);
X
X/* Convert cpp output to makefile */
X    if (verbose)
X      cat("Preprocessor Output", CppOutput, stderr);
X    cpptomake(CppOutput, Makefile);
X    dsync(Makefile);
X
X/* Run make on the makefile */
X    if (verbose)
X      cat("Makefile Input", Makefile, stderr);
X    if (nomake) {
X      (void) fclose(Makefile);
X      MakefileName = 0;
X    }
X    else {
X      appendarg(&make, "-f", MakefileName, (char *) 0);
X      for (; lastopt < argc; lastopt++)
X	appendarg(&make, argv[lastopt], (char *) 0);
X
X      if ((status = procreate(&make, stdin, stdout)) != 0)
X	done(status);
X    }
X  }
X
X/* Complete */
X  done(0);
X
X/* Don't trust return from main on some machines */
X  return 0;
X}
X
X/* Insert cpp preamble
X *
X * Insert a preamble into the cpp input file. This preamble will
X * provide the names of the modules that make up the cppmake file.
X */
X
Xvoid cpppreamble(f)
X
XFILE *f;				/* cpp input file */
X
X{
X  (void) fprintf(f, "#define CPPMAKEMAKE %s\n",     make.argv[0]);
X  (void) fprintf(f, "#define CPPMAKECPP %s\n",      cpp.argv[0]);
X  (void) fprintf(f, "#define CPPMAKEFILE %s\n",     CppMakefileName);
X  (void) fprintf(f, "#define CPPMAKEMAKEFILE %s\n", MakefileName);
X}
X
X/* Change an cppmake file into a cpp input file
X *
X * A cppmake file is changed into a makefile. The following transformations
X * are made:
X *
X *	1. Preprocessor lines are retained
X *	2. Comments are deleted
X *	3. @ is replaced by @@ except in cpp commands
X *	4. ' ' is replaced by @% except in cpp commands
X *	5. \t is replaced by @^ except in cpp commands
X *	6. \n is replaced by @! except in cpp commands
X *	7. # is replaced by @& except in cpp commands
X *	8. Empty lines have @* inserted.
X *	9. Leading whitespace in macros definitions are removed
X */
X
Xvoid cppmaketocpp(T(FILE *cppmake, cppmake), T(FILE *cpp, cpp))
X
XD(FILE *cppmake)			/* cppmake file */
XD(FILE *cpp)				/* cpp file */
X
X{
X  char *lp;				/* line pointer */
X  char *token;				/* preprocessor token */
X  int toklen;				/* length of token */
X  char *p, *q;				/* scanner */
X  char *qf;				/* quote from here */
X  char *rp;				/* replacement for quote */
X
X  for (; (lp = getline(cppmake)) != 0; ) {
X
X/* Remove makefile comments */
X    for (qf = p = lp; isspace(*p); p++)
X      ;
X
X/* Comment or cpp command */
X    if (*p++ == '#') {
X      qf = lp = p-1;
X
X/* Join lines by removing quoted newlines */
X      for (q = p = lp; *p; p++) {
X	if (p[0] != '\\' || p[1] != '\n')
X	  *q++ = *p;
X	else
X	  p++;
X      }
X      *q = 0;
X
X/* Scan for command */
X      for (p = lp + 1; isspace(*p); p++)
X	;
X      token = p;
X
X      for (; isalpha(*p); p++)
X	;
X
X      toklen = p - token;
X
X      if (toklen != 0) {
X
X/* Define macros */
X	if (strncmp("define", token, toklen) == 0) {
X
X/* Quote spaces and tabs within macros */
X	  for (; isspace(*p); p++)
X	    ;
X	  for (; isalnum(*p) || *p == '_'; p++)
X	    ;
X	  if (*p == '(') {
X	    for (; *p && *p != ')'; p++)
X	      ;
X	    if (*p == ')')
X	      p++;
X	  }
X	  else if (isspace(*p))
X	    *p++ = ' ';
X
X/* Note start of definition */
X	  q = p;
X
X/* Quote characters in macro definition only */
X	  qf = p;
X
X/* Kill leading spaces in macro definition */
X	  if (isspace(*p)) {
X	    for (; isspace(*p); p++)
X	      ;
X	    for (; *p; )
X	      *q++ = *p++;
X	    *q = 0;
X	  }
X	}
X
X/* Other cpp directives are left alone */
X	else if (strncmp("undef",   token, toklen) == 0 ||
X	         strncmp("include", token, toklen) == 0 ||
X	         strncmp("line",    token, toklen) == 0 ||
X	         strncmp("endif",   token, toklen) == 0 ||
X	         strncmp("if",      token, toklen) == 0 ||
X	         strncmp("else",    token, toklen) == 0 ||
X	         strncmp("elsif",   token, toklen) == 0 ||
X	         strncmp("ifndef",  token, toklen) == 0 ||
X	         strncmp("ifdef",   token, toklen) == 0)
X	  qf = lp + strlen(lp);
X      }
X    }
X
X/* Quote blank lines */
X    if (*lp == 0)
X      (void) fputs("@*\n", cpp);
X
X    else {
X/* Output the current line and */
X      for (q = lp, p = qf; *p; p++) {
X	switch (*p) {
X	case '@':  rp = "@@"; break;
X	case ' ':  rp = "@%"; break;
X	case '\t': rp = "@^"; break;
X	case '\n': rp = "@!"; break;
X	case '#':  rp = "@&"; break;
X	default  : rp = 0;    break;
X	}
X	if (rp != 0) {
X	  (void) fwrite(q, sizeof(char), p-q, cpp);
X	  (void) fputs(rp, cpp);
X	  q = p + 1;
X	}
X      }
X      if (q < p)
X	(void) fwrite(q, sizeof(char), p-q, cpp);
X      (void) putc('\n', cpp);
X    }
X  }
X}
X
X/* Change a cpp output file into a makefile
X *
X * Read the cpp output file and make the following transformations:
X *
X *	1. Leading spaces are removed
X *	2. Lines beginning with # are discarded
X *	3. @@ is converted to @
X *	4. @% is converted to ' '
X *	5. @^ is converted to \t
X *	6. @! is converted to \n
X *	7. @& is converted to #
X *	8. @* is removed
X *	9. ## and surrounding whitespace is removed
X *     10. @@ in the original source is converted to \n
X *     11. Trailing whitespace is removed
X *     12. Blank lines are removed
X */
X
Xvoid cpptomake(T(FILE *cpp, cpp), T(FILE *make, make))
X
XD(FILE *cpp)				/* cpp output */
XD(FILE *make)				/* makefile */
X
X{
X  char *lp;				/* line */
X  char *p, *q;				/* line scanners */
X
X  for (; (lp = getline(cpp)) != 0; ) {
X
X/* Ignore cpp line control and empty lines */
X    if (*lp == '#' || *lp == 0)
X      continue;
X
X/* Skip leading whitespace */
X    for (; isspace(*lp); lp++)
X      ;
X
X/* Replace quoted characters */
X    for (q = p = lp; *p; p++) {
X      if (p[0] != '@')
X	*q++ = *p;
X      else {
X	switch (p[1]) {
X	default:  *q++ = p[0]; *q++ = p[1]; break;
X	case '@': *q++ = '@';  break;
X	case '%': *q++ = ' ';  break;
X	case '^': *q++ = '\t'; break;
X	case '!': *q++ = '\n'; break;
X	case '&': *q++ = '#';  break;
X	case '*':              break;
X	}
X	p++;
X      }
X    }
X    *q = 0;
X
X/* Scan original source */
X    for (q = p = lp; *p; p++) {
X
X/* Concatenate adjacent symbols */
X      if (p[0] == '#' && p[1] == '#') {
X	for (; q > lp; ) {
X	  if (! isspace(*--q)) {
X	    q++;
X	    break;
X	  }
X	}
X	for (p += 2; isspace(*p); p++)
X	  ;
X	p--;
X      }
X
X/* Visible newline */
X      else if (p[0] == '@' && p[1] == '@') {
X	for (; q > lp; ) {
X	  if (! isspace(*--q)) {
X	    q++;
X	    break;
X	  }
X	}
X	*q++ = '\n';
X	p++;
X      }
X
X/* Default is to leave it alone */
X      else
X	*q++ = *p;
X    }
X
X/* Eat trailing whitespace */
X    for (; q > lp; ) {
X      if (! isspace(*--q)) {
X	q++;
X	break;
X      }
X    }
X    *q = 0;
X
X/* Output the line */
X    (void) fputs(lp, make);
X    (void) putc('\n', make);
X  }
X}
X
X/* Get a line
X *
X * Read a line from a stream. Return a null terminated string
X * containing the line. Return null on end of file. A dynamically
X * allocated static buffer is used to hold the line. Lines with
X * continuation marks are joined with the continuation mark and
X * the \n left intact.
X */
X
Xchar *getline(T(FILE *f, f))
X
XD(FILE *f)				/* stream */
X
X{
X  static char *buf;			/* line buffer */
X  static int buflen = 0;		/* size of buffer */
X  char *bp;				/* buffer pointer */
X  int size;				/* size of buffer */
X
X  if (buflen == 0) {
X    buflen = GETLINELENGTH * sizeof(char);
X    buf = smalloc((unsigned int) buflen);
X  }
X  for (*(bp = buf) = 0; ; ) {
X    if (fgets(bp, buflen - (bp - buf), f) == 0)
X      break;
X    bp += (size = strlen(bp));
X    if (bp[-1] == '\n') {
X      if (bp - buf == 1 || bp[-2] != '\\') {
X	bp[-1] = 0;
X	break;
X      }
X    }
X    if (bp - buf + 1 >= buflen) {
X      buflen += GETLINEINC * sizeof(char);
X      size = bp - buf;
X      buf = srealloc(buf, (unsigned int) buflen);
X      bp = buf + size;
X    }
X  }
X  return bp == buf ? 0 : buf;
X}
X
X/* Duplicate a string
X *
X * Return a copy of the string. The copy is made in space malloc'ed
X * for the string.
X */
X
Xchar *stringdup(T(char *s, s))
X
XD(char *s)				/* string */
X
X{
X  return strcpy(smalloc((unsigned int) (strlen(s)+1)), s);
X}
X
X/* Safe malloc
X *
X * Malloc with error checking.
X */
X
Xchar *smalloc(T(unsigned int n, n))
X
XD(unsigned int n)			/* size of malloc */
X
X{
X  char *p;				/* area */
X
X  if ((p = (char *) malloc(n)) == 0) {
X    (void) fputs("Insufficient memory\n", stderr);
X    done(1);
X  }
X  return p;
X}
X
X/* Safe realloc
X *
X * Realloc and check for error.
X */
X
Xchar *srealloc(T(char *p, p), T(unsigned int n, n))
X
XD(char *p)				/* area */
XD(unsigned int n)			/* size */
X
X{
X  if ((p = (char *) realloc((malloc_t) p, n)) == 0) {
X    (void) fputs("Insufficient memory\n", stderr);
X    done(1);
X  }
X  return p;
X}
X
X/* Open a temporary file
X *
X * A temporary file is opened using the given template. The
X * function returns a stream pointer to the temporary file.
X */
X
XFILE *ftemp(T(char *template, template), T(char **name, name))
X
XD(char *template)			/* name template */
XD(char **name)				/* real name */
X
X{
X  FILE *fp;				/* file */
X
X  for (;;) {
X    (void) mktemp(*name = stringdup(template));
X
X    if ((fp = fopen(*name, "w+")) != NULL)
X      return fp;
X    free(*name);
X  }
X}
X
X/* Start a child
X *
X * Start a child process with the specified argument list and
X * the named streams as stdin and stdout.
X */
X
Xint procreate(T(PROCESS *child, child), T(FILE *in, in), T(FILE *out, out))
X
XD(PROCESS *child)			/* arguments */
XD(FILE *in)				/* input */
XD(FILE *out)				/* output */
X
X{
X  int i;				/* argv index */
X#ifdef	_MSDOS
X  int Stdin;				/* saved stdin */
X  int Stdout;				/* saved stdout */
X  int status;				/* return status */
X#else
X  pid_t pid;				/* child id */
X  wait_t status;			/* child exit status */
X#endif
X
X  if (verbose) {
X    (void) fprintf(stderr, "Spawning child: ");
X    for (i = 0; i < child->argc; i++)
X      (void) fprintf(stderr, "%s ", child->argv[i]);
X    (void) putc('\n', stderr);
X  }
X
X#ifdef	_MSDOS
X
X  if (fileno(in) != 0) {
X    if ((Stdin = dup(0)) < 0) {
X      (void) fputs("Cannot save stdin\n", stderr);
X      done(1);
X    }
X    if (dup2(fileno(in), 0) < 0) {
X      (void) fputs("Cannot set stdin\n", stderr);
X      done(1);
X    }
X  }
X
X  if (fileno(out) != 1) {
X    if ((Stdout = dup(1)) < 0) {
X      (void) fputs("Cannot save stdout\n", stderr);
X      done(1);
X    }
X    if (dup2(fileno(out), 1) < 0) {
X      (void) fputs("Cannot set stdout\n", stderr);
X      done(1);
X    }
X  }
X
X  status = spawnvp(P_WAIT, child->argv[0], child->argv);
X
X  if (fileno(in) != 0) {
X    if (dup2(Stdin, 0) < 0) {
X      (void) fputs("Cannot restore stdin\n", stderr);
X      done(1);
X    }
X    (void) close(Stdin);
X  }
X
X  if (fileno(out) != 1) {
X    if (dup2(Stdout, 1) < 0) {
X      (void) fputs("Cannot restore stdout\n", stderr);
X      done(1);
X    }
X    (void) close(Stdout);
X  }
X
X  if (status < 0) {
X    (void) fprintf(stderr, "Cannot spawn %s.\n", child->argv[0]);
X    return 1;
X  }
X
X  return status;
X
X#else
X  switch (pid = fork()) {
X  case -1:
X    (void) fprintf(stderr, "Cannot fork %s.\n", child->argv[0]);
X    done(1);
X
X  case 0:
X#ifdef	SIGINT
X    if (signal(SIGINT,  SIG_IGN) != SIG_IGN)
X      (void) signal(SIGINT,  SIG_DFL);
X#endif
X#ifdef	SIGHUP
X    if (signal(SIGHUP,  SIG_IGN) != SIG_IGN)
X      (void) signal(SIGHUP,  SIG_DFL);
X#endif
X#ifdef	SIGQUIT
X    if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
X      (void) signal(SIGQUIT, SIG_DFL);
X#endif
X
X    if ((fileno(in)  != 0 && dup2(fileno(in),  0) < 0) ||
X        (fileno(out) != 1 && dup2(fileno(out), 1) < 0)) {
X      (void) fputs("Cannot set stdin and stdout\n", stderr);
X      exit(1);
X    }
X    execvp(child->argv[0], child->argv);
X    (void) fprintf(stderr, "Cannot exec %s.\n", child->argv[0]);
X    exit(1);
X
X  default:
X    do ;
X    while (pid != wait(&status) || WIFSTOPPED(status));
X
X    if (WIFSIGNALED(status)) {
X      (void) fprintf(stderr, "%s killed by signal %d.\n",
X		     child->argv[0], WTERMSIG(status));
X      return 1;
X    }
X
X    return WEXITSTATUS(status);
X  }
X#endif
X}
X
X/* Append arguments to the process
X *
X * Add null terminated list of arguments to the list of arguments
X * for the process.
X */
X
Xvoid appendarg(T(PROCESS *p, p), VA_ALIST)
X
XD(PROCESS *p)				/* process */
XVA_DCL
X
X{
X  VA_LIST ap;				/* point at argument list */
X  char *q;				/* point at argument */
X
X  VA_START(ap, p);
X
X  for (; (q = VA_ARG(ap, char *)) != 0; ) {
X    if (p->argc >= MAXARGS) {
X      (void) fprintf(stderr, "Too many arguments for %s.\n", p->argv[0]);
X      done(1);
X    }
X    p->argv[p->argc++] = q;
X    p->argv[p->argc]   = 0;
X  }
X
X  VA_END(ap);
X}
X
X/* Set the arguments for a process
X *
X * This resets the process argument list and sets it to the
X * one named in the parameter list. The parameter list will
X * be scanned and separated into space separated words. Note
X * that the string will be written into.
X */
X
Xvoid setarg(T(PROCESS *p, p), T(char *s, s))
X
XD(PROCESS *p)				/* process */
XD(char *s)				/* arguments */
X
X{
X  char *a;				/* point at argument */
X
X  p->argc    = 0;
X  p->argv[0] = 0;
X
X  for (;;) {
X    for (; isspace(*s); s++)
X      ;
X    if (*s == 0)
X      break;
X    for (a = s; *s != 0 && !isspace(*s); s++)
X      ;
X    if (*s != 0)
X      *s++ = 0;
X    appendarg(p, a, (char *) 0);
X  }
X}
X
X/* Append an argument vector to the list of arguments
X *
X * The vector of arguments is appended to the list of
X * arguments for the process.
X */
X
Xvoid appendlist(T(PROCESS *p, p), T(char **argv, argv))
X
XD(PROCESS *p)				/* process descriptor */
XD(char **argv)				/* argument vector */
X
X{
X  for (; *argv != 0; argv++)
X    appendarg(p, *argv, (char *) 0);
X}
X
X/* Wrap up with exit status
X *
X * Delete all temporary files and exit with the specified status.
X */
X
Xvoid done(T(int status, status))
X
XD(int status)				/* exit status */
X
X{
X  purge();
X  exit(status);
X}
X
X/* Wrap up
X *
X * Delete all temporary files and exit with error status.
X */
X
Xsignal_t wrapup(T(int sig, sig))
X
XD(int sig)				/* signal */
X
X{
X  sig = sig;
X  purge();
X  exit(1);
X}
X
X/* Remove all temporary files
X *
X * Check for temporary files and remove them.
X */
X
Xvoid purge(T(void,))
X
X{
X  if (CppInputName != 0)
X    (void) unlink(CppInputName);
X  if (CppOutputName != 0)
X    (void) unlink(CppOutputName);
X  if (MakefileName != 0)
X    (void) unlink(MakefileName);
X}
X
X/* Cat a file
X *
X * Copy the named stream to the named output stream. On completion
X * the input stream is rewound.
X */
X
Xvoid cat(T(char *name, name), T(FILE *in, in), T(FILE *out, out))
X
XD(char *name)					/* heading to use */
XD(FILE *in)					/* input stream */
XD(FILE *out)				/* output stream */
X
X{
X  int ch;				/* copy */
X  int len;				/* length of name */
X  int left;				/* left of header */
X  int right;				/* right of header */
X  int lastch;				/* last character */
X
X  len = strlen(name);
X  left = (CATHEADERWIDTH - len) / 2;
X  right = CATHEADERWIDTH - len - left;
X
X  for (len = left; len--; (void) putc('-', out))
X    ;
X  (void) fprintf(out, " %s ", name);
X  for (len = right; len--; (void) putc('-', out))
X    ;
X  (void) putc('\n', out);
X
X  for (lastch = '\n'; (ch = getc(in)) != EOF; lastch = ch)
X    (void) putc(ch, out);
X  
X  if (lastch != '\n')
X    (void) putc('\n', out);
X
X  for (len = left; len--; (void) putc('+', out))
X    ;
X  (void) fprintf(out, " %s ", name);
X  for (len = right; len--; (void) putc('+', out))
X    ;
X  (void) putc('\n', out);
X
X  rewind(in);
X}
X
X/* Synchronise disk data blocks and directory
X *
X * Make sure that the data in the stream has been written to
X * the disk and that the directory has been updated.
X */
X
Xvoid dsync(T(FILE *f, f))
X
XD(FILE *f)				/* stream */
X
X{
X#ifdef	_MSDOS
X  int sf;				/* save descriptor */
X#endif
X
X  (void) fflush(f);
X  rewind(f);
X
X#ifdef	_MSDOS
X  if ((sf = dup(fileno(f))) < 0) {
X    (void) fputs("Cannot sync file.\n", stderr);
X    done(1);
X  }
X  (void) close(sf);
X#endif
X}
END_OF_FILE
if test 26995 -ne `wc -c <'cppmake.c'`; then
    echo shar: \"'cppmake.c'\" unpacked with wrong size!
fi
# end of 'cppmake.c'
fi
echo shar: End of archive 2 \(of 2\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Earl Chew, Dept of Computer Science, Monash University, Australia 3168
ARPA: cechew%bruce.cs.monash.oz.au@uunet.uu.net  ACS : cechew@bruce.oz
----------------------------------------------------------------------