+++ /dev/null
-/*
- msub - Read file(s) and perform substitutions using values of
- variables defined in makefile.
-
- Syntax: msub [-f makefile] [file ...]
-
- Multiple -f options may be given.
-
- 27 Mar 1990 Paul DuBois dubois@primate.wisc.edu
-
- 27 Mar 1990 V1.0. Created.
-*/
-
-# include <stdio.h>
-# include <ctype.h>
-# include <string.h>
-
-# define true (1)
-# define false (0)
-
-
-extern char *calloc ();
-
-
-char *NewString();
-
-
-typedef struct Def Def;
-
-struct Def
-{
- char *var; /* variable name */
- char *val; /* variable value */
- int simple; /* whether value has been expanded */
- Def *nextDef; /* next definition in list */
-};
-
-
-char *usage = "Usage: msub [ -f makefile ] file";
-char *makefile = NULL;
-Def *defList = NULL; /* list of definitions */
-int nDefs = 0; /* number of definitions */
-int changes;
-
-main (argc, argv)
-int argc;
-char **argv;
-{
-FILE *f;
-Def *dp;
-int i, pass;
-
- --argc;
- ++argv;
-
- while (argc > 0 && argv[0][0] == '-')
- {
- if (strcmp (argv[0], "-f") == 0)
- {
- if (argc < 2)
- panic (usage);
- if ((f = fopen (makefile = argv[1], "r")) == NULL)
- panic ("Can't open makefile");
- ReadMake (f);
- fclose (f);
- argc -= 2;
- argv += 2;
- }
- else
- panic (usage); /* bad flag */
- }
-
- if (makefile == NULL) /* no -f options given */
- {
- if ((f = fopen ("makefile", "r")) == NULL)
- {
- if ((f = fopen ("Makefile", "r")) == NULL)
- panic ("Can't open makefile");
- }
- ReadMake (f);
- fclose (f);
- }
-
- EncodeEscapes ();
-
- /* determine which values need expanding */
- for (dp = defList; dp != NULL; dp = dp->nextDef)
- dp->simple = !FindVarRef (dp->val, NULL, NULL, false);
-
- /* expand values to eliminate embedded var references */
- for (pass = 0; pass < nDefs; pass++)
- {
- changes = 0;
- for (dp = defList; dp != NULL; dp = dp->nextDef)
- {
- if (!dp->simple)
- Expand (dp);
- }
- if (changes == 0) /* loop while values expand */
- break;
- }
-
- /* sanity check: shouldn't have to make more than nDefs passes */
- if (pass >= nDefs)
- panic ("Too many expansion passes. Something's wrong!");
-
- DecodeEscapes ();
-
- /* partain addition:
- args of form "foo=bar" are taken to be defs to add in
- */
- while (argc > 0 && strchr(argv[0], '=') && isalpha(argv[0][0]))
- {
- char name[BUFSIZ], *np, *p;
- int len;
- p = argv[0];
- /* copied from below */
- np = name;
- while (isalnum (*p) || *p == '_')
- *np++ = *p++;
- *np = '\0';
- while (isspace (*p)) /* skip whitespace */
- ++p;
- if (*p++ == '=') /* it's a definition */
- {
- /* skip leading/trailing whitespace */
- while (isspace (*p))
- ++p;
- len = strlen (p);
- while (len > 0 && isspace (p[len-1]))
- p[--len] = '\0';
- AddDef (name, p, 1);
- /* find var refs in value -- NUKED */
- /* FindAllVarRefs (p); */
- /* also NUKED: continue; */
- }
- /* end of: copied from below */
- --argc;
- ++argv;
- }
- /* end of partain addition */
-
- /* read source file(s) and perform substitutions */
- if (argc == 0)
- DoSub (stdin);
- else while (argc > 0)
- {
- if ((f = fopen (*argv, "r")) == NULL)
- {
- fprintf (stderr, "Can't open \"%s\". ", *argv);
- panic ("Quitting.");
- }
- DoSub (f);
- fclose (f);
- --argc;
- ++argv;
- }
-
- exit (0);
-}
-
-
-/*
- Read a makefile.
-*/
-
-ReadMake (f)
-FILE *f;
-{
-char input[BUFSIZ * 4], name[BUFSIZ], *p, *np;
-int i, len;
-
- while (GetLine (f, input)) /* get line, check whether def'n */
- {
- for (p = input; isspace (*p); p++) { /* nop */ }
- if (*p == '#' || *p == '\0') /* comment or blank line */
- continue;
- if (isalpha (*p)) /* look for var name */
- {
- np = name;
- while (isalnum (*p) || *p == '_')
- *np++ = *p++;
- *np = '\0';
- while (isspace (*p)) /* skip whitespace */
- ++p;
- if (*p++ == '=') /* it's a definition */
- {
- /* skip leading/trailing whitespace */
- while (isspace (*p))
- ++p;
- len = strlen (p);
- while (len > 0 && isspace (p[len-1]))
- p[--len] = '\0';
- AddDef (name, p, 1);
- /* find var refs in value */
- FindAllVarRefs (p);
- continue;
- }
- }
- /* not a definition; find var refs anywhere in line */
- FindAllVarRefs (input);
- }
-}
-
-
-/*
- Find definition by variable name.
-*/
-
-Def *FindDefByVar (s)
-char *s;
-{
-Def *dp;
-
- for (dp = defList; dp != NULL; dp = dp->nextDef)
- {
- if (strcmp (dp->var, s) == 0)
- return (dp);
- }
- return (NULL);
-}
-
-
-/*
- Add a definition. If the name hasn't been seen yet, create a new
- definition on the list. If the name has been seen, and replace is
- non-zero, replace the current value with the new one. (replace will
- be zero if we're just adding a variable which is known by its being
- referenced somewhere.)
-*/
-
-AddDef (name, value, replace)
-char *name, *value;
-int replace;
-{
-Def *dp;
-
- if ((dp = FindDefByVar (name)) == NULL)
- {
- if ((dp = (Def *) calloc (1, sizeof (Def))) == NULL)
- panic ("AddDef: out of memory");
- dp->var = NewString (name);
- dp->val = NewString (value);
- dp->simple = 0; /* assume not */
- dp->nextDef = defList;
- defList = dp;
- ++nDefs;
- }
- else if (replace)
- {
- free (dp->val);
- dp->val = NewString (value);
- }
-}
-
-
-/*
- Replace instances of '$$' with a single ^A.
-*/
-
-EncodeEscapes ()
-{
-Def *dp;
-char *p, *q;
-
- for (dp = defList; dp != NULL; dp = dp->nextDef)
- {
- for (p = q = dp->val; *p != '\0'; p++, q++)
- {
- *q = *p;
- if (*p == '$' && *(p+1) == '$')
- {
- *q = '\01';
- p++;
- }
- }
- *q = '\0';
- }
-}
-
-
-/*
- Replace instances of ^A with a '$'.
-*/
-
-DecodeEscapes ()
-{
-Def *dp;
-char *p;
-
- for (dp = defList; dp != NULL; dp = dp->nextDef)
- {
- for (p = dp->val; *p != '\0'; p++)
- {
- if (*p == '\01')
- *p = '$';
- }
- }
-}
-
-
-/*
- Find variable reference in variable value. begin is set to
- the index of the '$' and end is set to the index of the closing
- delimiter. (If either is NULL, it is not set.)
-
- recogEsc is non-zero (true) if '$$' is recognized as an escaped '$'
- and skipped. It will be true during initial searching for var refs
- and while substituting in source files, false while expanding variable
- values.
-
- $v, ${}, ${1}, ${v), etc. are not accepted as valid and are ignored.
-*/
-
-FindVarRef (s, beg, end, recogEsc)
-char *s;
-int *beg, *end, recogEsc;
-{
-int i;
-char c, delim;
-
- i = 0;
- for (;;)
- {
- while ((c = s[i]) != '\0')
- {
- if (c == '$')
- {
- if ((c = s[i+1]) == '{' || c == '(')
- break;
- if (recogEsc && c == '$') /* escaped $ */
- ++i;
- }
- ++i;
- }
- if (c == '\0')
- return (0); /* no reference */
- if (beg != (int *) NULL)
- *beg = i;
- if (s[++i] == '{')
- delim = '}';
- else
- delim = ')';
- ++i;
- if (!isalpha (s[i])) /* must have at least one char, must */
- continue; /* begin with letter */
- while ((c = s[++i]) != '\0')
- {
- if (c == delim) /* find matching delim */
- {
- if (end != (int *) NULL)
- *end = i;
- return (1);
- }
- if (!isalnum (c) && c != '_')
- break;
- }
- }
-}
-
-
-FindAllVarRefs (s)
-char *s;
-{
-char name[BUFSIZ];
-int begin, end;
-
- while (FindVarRef (s, &begin, &end, true))
- {
- /* add with empty value if unknown, */
- /* but don't replace value if known */
- strncpy (name, s + begin + 2, end - begin - 2);
- name[end-begin-2] = '\0';
- AddDef (name, "", 0);
- s += end + 1;
- }
-}
-
-
-/*
- Pull out a variable reference, skipping leading '$(' or '${' and
- trailing ')' or '}'.
-*/
-
-YankVarRef (dst, src, begin, end)
-char *dst, *src;
-int begin, end;
-{
- strncpy (dst, src + begin + 2, end - begin - 2);
- dst[end - begin - 2] = '\0';
-}
-
-
-/*
- Look for variable references in a variable value and expand them
- when possible. If a variable is referenced but never defined, it
- disappears. If a variable is referenced, but its value has not itself
- been fully expanded, defer expansion of value until another pass, at
- which time the referenced variable might then be expanded. This
- prevents infinite expansions on circular references.
-*/
-
-Expand (dp)
-Def *dp;
-{
-Def *dp2;
-/* partain: made 4 * BUFSIZ
-char buf[BUFSIZ];
-*/
-char buf[4 * BUFSIZ];
-int begin, end, vn;
-
- while (FindVarRef (dp->val, &begin, &end, false))
- {
- YankVarRef (buf, dp->val, begin, end);
- if ((dp2 = FindDefByVar (buf)) == NULL)
- {
- fprintf (stderr, "Expand error: can't find %s, ", buf);
- panic ("quitting");
- }
- if (!dp2->simple) /* can't expand, give up for now */
- break;
- dp->val[begin] = '\0';
- sprintf (buf, "%s%s%s", dp->val, dp2->val, &(dp->val)[end+1]);
- free (dp->val);
- dp->val = NewString (buf);
- ++changes;
- }
- dp->simple = !FindVarRef (dp->val, NULL, NULL, false);
-}
-
-
-/*
- Read through file, performing substitutions for any variables
- known from Makefile. If a variable reference is found that is
- for an unknown variable, leave it alone, as it may be a reference
- to a real variable in a shell script.
-*/
-
-DoSub (f)
-FILE *f;
-{
-Def *dp;
-char input[BUFSIZ], var[BUFSIZ], *p;
-int begin, end;
-
- while (fgets (input, sizeof (input), f) != NULL)
- {
- p = input;
- while (FindVarRef (p, &begin, &end, true))
- {
- write (1, p, begin); /* write prefix */
- YankVarRef (var, p, begin, end);
- /* if var is known from makefile, write */
- /* value, else just echo the reference */
- if ((dp = FindDefByVar (var)) != NULL)
- write (1, dp->val, strlen (dp->val));
- else
- write (1, p + begin, end - begin + 1);
- p += end + 1;
- }
- write (1, p, strlen (p));
- }
-}
-
-
-/*
- Get next line from Makefile (combines continuation lines into one).
- No overflow checking, oops.
-*/
-
-GetLine (f, lbuf)
-FILE *f;
-char *lbuf;
-{
-char buf[BUFSIZ];
-int loop = 1, haveLine = 0, len;
-
- *lbuf = '\0';
- while (loop && fgets (buf, sizeof (buf), f) != NULL)
- {
- loop = 0;
- haveLine = 1;
- len = strlen (buf);
- if (len > 0 && buf[len-1] == '\n') /* trim newline */
- buf[--len] = '\0';
- if (len > 0 && buf[len-1] == '\\') /* need continuation */
- {
- loop = 1;
- buf[--len] = ' ';
- }
- strcat (lbuf, buf);
- }
- return (haveLine);
-}
-
-
-/*
- Allocate space for a string, copy the string into it, and return
- a pointer to the copy.
-*/
-
-
-char *NewString (s)
-char *s;
-{
-char *p;
-
- if ((p = calloc (1, strlen (s) + 1)) == NULL)
- panic ("NewString: out of space");
- return (strcpy (p, s));
-}
-
-
-panic (s)
-char *s;
-{
- fprintf (stderr, "%s\n", s);
- exit (1);
-}
+++ /dev/null
-.TH MSUB 1
-.SH NAME
-msub \- substitute \fImake\fR variables into template to produce script
-.SH SYNOPSIS
-.B msub
-[
-.B \-e
-] [
-.B \-f
-.I file
-] [
-.BI \+R str
-] [
-.BI \-R str
-] [
-.IB variable = value
-] [
-.I file
-\&...
-]
-.SH DESCRIPTION
-Makefiles often contain project configuration information specified as
-.I make
-variables, and it is often desirable to make use of that information in other
-files.
-.I msub
-allows targets to be produced easily
-from templates that contain references to those variables.
-.I msub
-is particularly useful in software projects that use
-.IR imake
-because virtually all configuration parameters are written into Makefiles
-and are thus available for propagation into other files.
-.PP
-First
-.I msub
-reads the
-.I Makefile
-and finds all variable definition lines
-of the form ``\fIvar\fR = \fIvalue\fR''.
-Then
-.I msub
-reads any files named on the command line (or the standard input
-if none), looks for references to those variables, and replaces the
-references with the corresponding variable values.
-References to undefined variables are replaced by the empty string.
-The result is written to the standard output.
-.PP
-.I msub
-takes the following options:
-.TP
-.B \-e
-Environment variable values override assignments within Makefiles.
-Normally assignments within Makefiles override environment variables.
-.TP
-.BI \-f \ file
-By default, variable values are extracted from
-.I Makefile
-(or
-.I makefile
-if
-.I Makefile
-is missing) in the current directory.
-If the
-.B \-f
-.I file
-option is given, variable values are extracted from
-.I file
-instead.
-Multiple
-.B \-f
-options may be specified.
-(Note:
-.I make
-recognizes
-.B \-f\ \-
-to mean the
-.I Makefile
-should be read from
-.IR stdin .
-.I msub
-doesn't, because template input may come from
-.IR stdin .)
-.TP
-.BI +R str
-.TP
-.BI \-R str
-The default variable reference indicators within templates
-are the same as in Makefiles, i.e.,
-``$('' and ``)'', and ``${'' and ``}''.
-These can be changed with the
-.B +R
-and
-.B \-R
-options, which must be specified in pairs.
-.B +R
-specifies the string that initiates a variable reference and
-.B \-R
-specifies the string that terminates it.
-Multiple pairs of reference indicators may be given.
-.TP
-.IB variable = value
-Variable definition.
-This definition overrides any regular definition for the specified
-variable within the makefile itself or in the environment.
-.SH EXAMPLES
-Suppose you want to produce for a program a help file that indicates the local
-e-mail address to which questions may be directed.
-If this address is specified as the variable EMAILHELPADDR in the
-.IR Makefile ,
-you can write a template
-.I helpfile.msub
-like this:
-.in +.5i
-.nf
-.sp .5v
-\&.\|.\|.\|\fIstuff\fP\|.\|.\|.
-Please direct questions to ${EMAILHELPADDR}.
-\&.\|.\|.\|\fImore stuff\fP\|.\|.\|.
-.sp .5v
-.fi
-.in
-Process the template to produce the help file like this:
-.in +.5i
-.nf
-.sp .5v
-msub helpfile.msub > helpfile
-.sp .5v
-.fi
-.in
-If the
-.I Makefile
-contains the following lines:
-.in +.5i
-.nf
-.sp .5v
-EMAILHELPADDR = postmaster@$(DOMAIN)
-DOMAIN = primate.wisc.edu
-.sp .5v
-.fi
-.in
-then
-.I helpfile
-will look like this:
-.in +.5i
-.nf
-.sp .5v
-\&.\|.\|.\|\fIstuff\fP\|.\|.\|.
-Please direct questions to postmaster@primate.wisc.edu.
-\&.\|.\|.\|\fImore stuff\fP\|.\|.\|.
-.sp .5v
-.fi
-.in
-.I msub
-can produce executable scripts, too.
-If the processor for which you're producing
-the script allows variable references, make sure you refer to
-.I make
-variables in the template in a different way than the processor refers to
-variables.
-For instance, ``${\fIvar\fP}'' is ambiguous in a shell script template \- is
-it a reference to a shell variable that you want
-.I msub
-to leave alone, or to a
-.I make
-variable for which you want
-.I msub
-to substitute the variable's value?
-To resolve the ambiguity, you can refer to shell variables as ``$\fIvar\fP''
-(which the shell recognizes, but
-.I msub
-doesn't), or you can use different indicators than the defaults to refer to
-.I make
-variables in the template.
-For example, you could use ``@<'' and ``>@'' and refer to variables as
-``@<\fIvar\fP>@''.
-Suppose you have a simple shell script to print your home directory pathname.
-You might write it like this:
-.in +.5i
-.nf
-.sp .5v
-#!@<SHELL>@
-echo ${HOME}
-.sp .5v
-.fi
-.in
-Then you can process the template like this (note that you must quote the
-arguments because ``<'' and ``>'' are special to the shell):
-.in +.5i
-.nf
-.sp .5v
-msub +R"@<" \-R">@" template > script
-.sp .5v
-.fi
-.in
-The
-.B +R/\-R
-arguments cause
-.I msub
-to recognize ``@<SHELL>@'' but not ``${HOME}''.
-The result looks like this:
-.in +.5i
-.nf
-.sp .5v
-#!/bin/sh
-echo ${HOME}
-.sp .5v
-.fi
-.in
-.SH "WHO-TO-BLAME"
-Paul DuBois, dubois@primate.wisc.edu
-.SH "BUGS AND RESTRICTIONS"
-CC, YACC, etc., have implicit values in
-.I make
-even if they're not defined in the
-.IR Makefile ,
-which isn't true for
-.IR msub .
-However, when
-.I msub
-is used with
-.IR imake ,
-this isn't normally a problem since
-.IR imake -generated
-Makefiles tend to contain explicit definitions for every parameter in the
-known universe.
-.PP
-.I msub
-is more restrictive in the variable names it recognizes and
-performs substitutions on than
-.IR make .
-.I make
-allows single character names to be referenced as ``$\fIc\fP'', where
-.I c
-is a letter;
-.I msub
-doesn't.
-.I msub
-also ignores
-.I make
-variables like
-``$*'' and ``$@''.
-.PP
-Recursive references in
-.I Makefile
-variables aren't expanded.
-If a
-.I Makefile
-contains the definition ``X = ${X}'', X won't be expanded.
-Indirectly recursive references aren't expanded, either.
-If a
-.I Makefile
-contains the definitions ``X = ${Y}'' and ``Y = ${X}'',
-neither X nor Y will be expanded.
-(These actually aren't bugs in
-.IR msub ,
-they're problems in the
-.IR Makefile .)
-.PP
-A comment at the end of a variable assignment line become part of the
-variable's value.
-If
-.I Makefile
-contains the following line:
-.in +.5i
-.nf
-.sp .5v
-SHELL = /bin/shell # default shell
-.sp .5v
-.fi
-.in
-then
-.I msub
-takes the value of SHELL to be ``/bin/shell # default shell''.
+++ /dev/null
-.SH
-Theory of Operation
-.LP
-.I msub
-reads the makefile in the current directory (or the file named after the
-.B \-f
-flag) and finds all variable definitions and references.
-Definitions are lines of the form ``\fIname\fR = \fIvalue\fR''.
-References are instances of ``$(\fIvar\fR)'' or ``${\fIvar\fR}'', either in
-the value part of definition lines, or any part of dependency or command lines.
-Then variable values are examined to eliminate references to other variables
-by replacing the referenced variables with their values.
-Variables which are referenced but never defined have a value of the empty
-string.
-.LP
-Then
-.I msub
-reads any files named on the command line (or
-.I stdin
-if none) and looks for instances of ``$(\fIvar\fR)'' or ``${\fIvar\fR}'',
-where
-.I var
-is known from the makefile, and substitutes the value of
-.I var .
-.SH
-Purpose
-.LP
-The motivation for
-.I msub
-is that project configuration information is often contained in makefiles
-and it is often desirable to make use of that information.
-.I msub
-allows files to be produced from templates that contain references
-to makefile variables.
-In this sense it is similar to the way that the C preprocessor
-.I cpp
-uses #define statements in header files; the
-makefile is the implicit ``header'' file and definition lines are the
-``#define'' statements.
-.LP
-Although it is possible to use
-.I cpp
-to perform substitutions on templates, a disadvantage of
-.I cpp
-is that it modifies its source in other ways (eg., lines beginning with
-``#'' are special and comments (``/* ... */'') are deleted.
-Also, it is necessary to pass a -D flag for every symbol to be substituted.
-Similarly, for C programs it can be simpler to use
-.I msub
-to produce a single configuration header file from a template
-than to pass a lot of -D flags to each invocation of
-.I cc .
-.LP
-.I msub
-performs no textual modification besides substitution of references
-to makefile variables; it leaves everything else alone.
-In particular,
-references to variables not known from the makefile are left untouched.
-This allows
-.I msub
-to be used to produce shell scripts that use variables of
-their own.
-.LP
-.I msub
-is particularly useful in software projects that use
-.I imake .
-Virtually all configuration information is propagated into makefiles
-thus produced, so it is available for propagation into other files using
-.I msub .
-.SH
-Problems
-.LP
-If
-.I msub
-is used to produce shell scripts, it is unwise for the script to use names
-for its own variables that are the same as those in the makefile.
-The problem can be avoided entirely if, for instance, variables in makefiles
-are all uppercase and the scripts' own variables are lowercase or mixed case.
-.LP
-Circular references in makefile variables are not expanded.
-If a makefile contains the definitions ``X = ${Y}'' and ``Y = ${X}'',
-neither X nor Y will be expanded.
-But that is really a problem with the makefile, not
-.I msub .
-.LP
-.I make
-recognizes
-.B \-f\ \-
-to mean the makefile should be read from
-.I stdin .
-.I msub
-does not.
-.LP
-CC, YACC, etc., have implicit values in
-.I make
-even if not defined in the makefile.
-Not true in
-.I msub .
-.LP
-.I msub
-is more restrictive in the variable names that it recognizes and
-performs substitutions on.
-Names that do not begin with a letter and
-that contain any characters besides letters, digits and underscores are
-ignored.
-This means that variable names such as ``${1}'' are not recognized;
-the reason for this is that shell scripts often use such variables and
-they should be left untouched.
-Also,
-``$\fIx\fR'' is legal in
-.I make
-when
-.I x
-is a single character, but
-.I msub
-ignores such references.
-.i msub
-.LP
-.I make
-allows variables to be defined on the command line.
-.I msub
-does not.
-.LP
-If you try to be tricky with your variable references, e.g., by using
-recursive references, you may get burned.
-.LP
-There is no way to prevent substitution, in a source file, of a reference to
-a variable known from the makefile.