[project @ 1996-01-08 20:28:12 by partain]
[ghc-hetmet.git] / glafp-utils / msub / msub.c
1 /*
2         msub - Read file(s) and perform substitutions using values of
3                 variables defined in makefile.
4
5         Syntax: msub [-f makefile] [file ...]
6
7         Multiple -f options may be given.
8
9         27 Mar 1990     Paul DuBois     dubois@primate.wisc.edu
10
11         27 Mar 1990 V1.0.  Created.
12 */
13
14 # include       <stdio.h>
15 # include       <ctype.h>
16 # include       <string.h>
17
18 # define        true    (1)
19 # define        false   (0)
20
21
22 extern char     *calloc ();
23
24
25 char    *NewString();
26
27
28 typedef struct Def      Def;
29
30 struct Def
31 {
32         char    *var;           /* variable name */
33         char    *val;           /* variable value */
34         int     simple;         /* whether value has been expanded */
35         Def     *nextDef;       /* next definition in list */
36 };
37
38
39 char    *usage = "Usage: msub [ -f makefile ] file";
40 char    *makefile = NULL;
41 Def     *defList = NULL;        /* list of definitions */
42 int     nDefs = 0;              /* number of definitions */
43 int     changes;
44
45 main (argc, argv)
46 int     argc;
47 char    **argv;
48 {
49 FILE    *f;
50 Def     *dp;
51 int     i, pass;
52
53         --argc;
54         ++argv;
55
56         while (argc > 0 && argv[0][0] == '-')
57         {
58                 if (strcmp (argv[0], "-f") == 0)
59                 {
60                         if (argc < 2)
61                                 panic (usage);
62                         if ((f = fopen (makefile = argv[1], "r")) == NULL)
63                                 panic ("Can't open makefile");
64                         ReadMake (f);
65                         fclose (f);
66                         argc -= 2;
67                         argv += 2;
68                 }
69                 else
70                         panic (usage);  /* bad flag */
71         }
72
73         if (makefile == NULL)   /* no -f options given */
74         {
75                 if ((f = fopen ("makefile", "r")) == NULL)
76                 {
77                         if ((f = fopen ("Makefile", "r")) == NULL)
78                                 panic ("Can't open makefile");
79                 }
80                 ReadMake (f);
81                 fclose (f);
82         }
83
84         EncodeEscapes ();
85
86         /* determine which values need expanding */
87         for (dp = defList; dp != NULL; dp = dp->nextDef)
88                 dp->simple = !FindVarRef (dp->val, NULL, NULL, false);
89
90         /* expand values to eliminate embedded var references */
91         for (pass = 0; pass < nDefs; pass++)
92         {
93                 changes = 0;
94                 for (dp = defList; dp != NULL; dp = dp->nextDef)
95                 {
96                         if (!dp->simple)
97                                 Expand (dp);
98                 }
99                 if (changes == 0)       /* loop while values expand */
100                         break;
101         }
102
103         /* sanity check: shouldn't have to make more than nDefs passes */
104         if (pass >= nDefs)
105                 panic ("Too many expansion passes.  Something's wrong!");
106
107         DecodeEscapes ();
108
109         /* partain addition:
110             args of form "foo=bar" are taken to be defs to add in
111         */
112         while (argc > 0 && strchr(argv[0], '=') && isalpha(argv[0][0]))
113         {
114          char name[BUFSIZ], *np, *p;
115          int len;
116          p = argv[0];
117                 /* copied from below */
118                         np = name;
119                         while (isalnum (*p) || *p == '_')
120                                 *np++ = *p++;
121                         *np = '\0';
122                         while (isspace (*p))    /* skip whitespace */
123                                 ++p;
124                         if (*p++ == '=')        /* it's a definition */
125                         {
126                                 /* skip leading/trailing whitespace */
127                                 while (isspace (*p))
128                                         ++p;
129                                 len = strlen (p);
130                                 while (len > 0 && isspace (p[len-1]))
131                                         p[--len] = '\0';
132                                 AddDef (name, p, 1);
133                                 /* find var refs in value -- NUKED */
134                                 /* FindAllVarRefs (p); */
135                                 /* also NUKED: continue; */
136                         }
137                 /* end of: copied from below */
138                 --argc;
139                 ++argv;
140         }
141         /* end of partain addition */
142
143         /* read source file(s) and perform substitutions */
144         if (argc == 0)
145                 DoSub (stdin);
146         else while (argc > 0)
147         {
148                 if ((f = fopen (*argv, "r")) == NULL)
149                 {
150                         fprintf (stderr, "Can't open \"%s\".  ", *argv);
151                         panic ("Quitting.");
152                 }
153                 DoSub (f);
154                 fclose (f);
155                 --argc;
156                 ++argv;
157         }
158
159         exit (0);
160 }
161
162
163 /*
164         Read a makefile.
165 */
166
167 ReadMake (f)
168 FILE    *f;
169 {
170 char    input[BUFSIZ * 4], name[BUFSIZ], *p, *np;
171 int     i, len;
172
173         while (GetLine (f, input))      /* get line, check whether def'n */
174         {
175                 for (p = input; isspace (*p); p++) { /* nop */ }
176                 if (*p == '#' || *p == '\0')    /* comment or blank line */
177                         continue;
178                 if (isalpha (*p))       /* look for var name */
179                 {
180                         np = name;
181                         while (isalnum (*p) || *p == '_')
182                                 *np++ = *p++;
183                         *np = '\0';
184                         while (isspace (*p))    /* skip whitespace */
185                                 ++p;
186                         if (*p++ == '=')        /* it's a definition */
187                         {
188                                 /* skip leading/trailing whitespace */
189                                 while (isspace (*p))
190                                         ++p;
191                                 len = strlen (p);
192                                 while (len > 0 && isspace (p[len-1]))
193                                         p[--len] = '\0';
194                                 AddDef (name, p, 1);
195                                 /* find var refs in value */
196                                 FindAllVarRefs (p);
197                                 continue;
198                         }
199                 }
200                 /* not a definition; find var refs anywhere in line */
201                 FindAllVarRefs (input);
202         }
203 }
204
205
206 /*
207         Find definition by variable name.
208 */
209
210 Def *FindDefByVar (s)
211 char    *s;
212 {
213 Def     *dp;
214
215         for (dp = defList; dp != NULL; dp = dp->nextDef)
216         {
217                 if (strcmp (dp->var, s) == 0)
218                         return (dp);
219         }
220         return (NULL);
221 }
222
223
224 /*
225         Add a definition.  If the name hasn't been seen yet, create a new
226         definition on the list.  If the name has been seen, and replace is
227         non-zero, replace the current value with the new one.  (replace will
228         be zero if we're just adding a variable which is known by its being
229         referenced somewhere.)
230 */
231
232 AddDef (name, value, replace)
233 char    *name, *value;
234 int     replace;
235 {
236 Def     *dp;
237
238         if ((dp = FindDefByVar (name)) == NULL)
239         {
240                 if ((dp = (Def *) calloc (1, sizeof (Def))) == NULL)
241                         panic ("AddDef: out of memory");
242                 dp->var = NewString (name);
243                 dp->val = NewString (value);
244                 dp->simple = 0;         /* assume not */
245                 dp->nextDef = defList;
246                 defList = dp;
247                 ++nDefs;
248         }
249         else if (replace)
250         {
251                 free (dp->val);
252                 dp->val = NewString (value);
253         }
254 }
255
256
257 /*
258         Replace instances of '$$' with a single ^A.
259 */
260
261 EncodeEscapes ()
262 {
263 Def     *dp;
264 char    *p, *q;
265
266         for (dp = defList; dp != NULL; dp = dp->nextDef)
267         {
268                 for (p = q = dp->val; *p != '\0'; p++, q++)
269                 {
270                         *q = *p;
271                         if (*p == '$' && *(p+1) == '$')
272                         {
273                                 *q = '\01';
274                                 p++;
275                         }
276                 }
277                 *q = '\0';
278         }
279 }
280
281
282 /*
283         Replace instances of ^A with a '$'.
284 */
285
286 DecodeEscapes ()
287 {
288 Def     *dp;
289 char    *p;
290
291         for (dp = defList; dp != NULL; dp = dp->nextDef)
292         {
293                 for (p = dp->val; *p != '\0'; p++)
294                 {
295                         if (*p == '\01')
296                                 *p = '$';
297                 }
298         }
299 }
300
301
302 /*
303         Find variable reference in variable value.  begin is set to
304         the index of the '$' and end is set to the index of the closing
305         delimiter.  (If either is NULL, it is not set.)
306
307         recogEsc is non-zero (true) if '$$' is recognized as an escaped '$'
308         and skipped.  It will be true during initial searching for var refs
309         and while substituting in source files, false while expanding variable
310         values.
311
312         $v, ${}, ${1}, ${v), etc. are not accepted as valid and are ignored.
313 */
314
315 FindVarRef (s, beg, end, recogEsc)
316 char    *s;
317 int     *beg, *end, recogEsc;
318 {
319 int     i;
320 char    c, delim;
321
322         i = 0;
323         for (;;)
324         {
325                 while ((c = s[i]) != '\0')
326                 {
327                         if (c == '$')
328                         {
329                                 if ((c = s[i+1]) == '{' || c == '(')
330                                         break;
331                                 if (recogEsc && c == '$')       /* escaped $ */
332                                         ++i;
333                         }
334                         ++i;
335                 }
336                 if (c == '\0')
337                         return (0);     /* no reference */
338                 if (beg != (int *) NULL)
339                         *beg = i;
340                 if (s[++i] == '{')
341                         delim = '}';
342                 else
343                         delim = ')';
344                 ++i;
345                 if (!isalpha (s[i]))    /* must have at least one char, must */
346                         continue;       /* begin with letter */
347                 while ((c = s[++i]) != '\0')
348                 {
349                         if (c == delim) /* find matching delim */
350                         {
351                                 if (end != (int *) NULL)
352                                         *end = i;
353                                 return (1);
354                         }
355                         if (!isalnum (c) && c != '_')
356                                 break;
357                 }
358         }
359 }
360
361
362 FindAllVarRefs (s)
363 char    *s;
364 {
365 char    name[BUFSIZ];
366 int     begin, end;
367
368         while (FindVarRef (s, &begin, &end, true))
369         {
370                 /* add with empty value if unknown, */
371                 /* but don't replace value if known */
372                 strncpy (name, s + begin + 2, end - begin - 2);
373                 name[end-begin-2] = '\0';
374                 AddDef (name, "", 0);
375                 s += end + 1;
376         }
377 }
378
379
380 /*
381         Pull out a variable reference, skipping leading '$(' or '${' and
382         trailing ')' or '}'.
383 */
384
385 YankVarRef (dst, src, begin, end)
386 char    *dst, *src;
387 int     begin, end;
388 {
389         strncpy (dst, src + begin + 2, end - begin - 2);
390         dst[end - begin - 2] = '\0';
391 }
392
393
394 /*
395         Look for variable references in a variable value and expand them
396         when possible.  If a variable is referenced but never defined, it
397         disappears.  If a variable is referenced, but its value has not itself
398         been fully expanded, defer expansion of value until another pass, at
399         which time the referenced variable might then be expanded.  This
400         prevents infinite expansions on circular references.
401 */
402
403 Expand (dp)
404 Def     *dp;
405 {
406 Def     *dp2;
407 /* partain: made 4 * BUFSIZ
408 char    buf[BUFSIZ];
409 */
410 char    buf[4 * BUFSIZ];
411 int     begin, end, vn;
412
413         while (FindVarRef (dp->val, &begin, &end, false))
414         {
415                 YankVarRef (buf, dp->val, begin, end);
416                 if ((dp2 = FindDefByVar (buf)) == NULL)
417                 {
418                         fprintf (stderr, "Expand error: can't find %s, ", buf);
419                         panic ("quitting");
420                 }
421                 if (!dp2->simple)       /* can't expand, give up for now */
422                         break;
423                 dp->val[begin] = '\0';
424                 sprintf (buf, "%s%s%s", dp->val, dp2->val, &(dp->val)[end+1]);
425                 free (dp->val);
426                 dp->val = NewString (buf);
427                 ++changes;
428         }
429         dp->simple = !FindVarRef (dp->val, NULL, NULL, false);
430 }
431
432
433 /*
434         Read through file, performing substitutions for any variables
435         known from Makefile.  If a variable reference is found that is
436         for an unknown variable, leave it alone, as it may be a reference
437         to a real variable in a shell script.
438 */
439
440 DoSub (f)
441 FILE    *f;
442 {
443 Def     *dp;
444 char    input[BUFSIZ], var[BUFSIZ], *p;
445 int     begin, end;
446
447         while (fgets (input, sizeof (input), f) != NULL)
448         {
449                 p = input;
450                 while (FindVarRef (p, &begin, &end, true))
451                 {
452                         write (1, p, begin);    /* write prefix */
453                         YankVarRef (var, p, begin, end);
454                         /* if var is known from makefile, write */
455                         /* value, else just echo the reference */
456                         if ((dp = FindDefByVar (var)) != NULL)
457                                 write (1, dp->val, strlen (dp->val));
458                         else
459                                 write (1, p + begin, end - begin + 1);
460                         p += end + 1;
461                 }
462                 write (1, p, strlen (p));
463         }
464 }
465
466
467 /*
468         Get next line from Makefile (combines continuation lines into one).
469         No overflow checking, oops.
470 */
471
472 GetLine (f, lbuf)
473 FILE    *f;
474 char    *lbuf;
475 {
476 char    buf[BUFSIZ];
477 int     loop = 1, haveLine = 0, len;
478
479         *lbuf = '\0';
480         while (loop && fgets (buf, sizeof (buf), f) != NULL)
481         {
482                 loop = 0;
483                 haveLine = 1;
484                 len = strlen (buf);
485                 if (len > 0 && buf[len-1] == '\n')      /* trim newline */
486                         buf[--len] = '\0';
487                 if (len > 0 && buf[len-1] == '\\')      /* need continuation */
488                 {
489                         loop = 1;
490                         buf[--len] = ' ';
491                 }
492                 strcat (lbuf, buf);
493         }
494         return (haveLine);
495 }
496
497
498 /*
499         Allocate space for a string, copy the string into it, and return
500         a pointer to the copy.
501 */
502
503
504 char    *NewString (s)
505 char    *s;
506 {
507 char    *p;
508
509         if ((p = calloc (1, strlen (s) + 1)) == NULL)
510                 panic ("NewString: out of space");
511         return (strcpy (p, s));
512 }
513
514
515 panic (s)
516 char    *s;
517 {
518         fprintf (stderr, "%s\n", s);
519         exit (1);
520 }