1/* munchconfig.c
2
3   A very, very (very!) simple program to process a config_h.sh file on
4   non-unix systems.
5
6   usage:
7   munchconfig config.sh config_h.sh [-f file] [foo=bar [baz=xyzzy [...]]] >config.h
8
9   which is to say, it takes as its first parameter a config.sh (or
10   equivalent), as its second a config_h.sh (or equivalent), an optional file
11   containing tag=value pairs (one on each line), and an optional list of
12   tag=value pairs on the command line.
13
14   It spits the processed config.h out to STDOUT.
15
16   */
17
18#include <stdio.h>
19#include <errno.h>
20#include <stdlib.h>
21#include <string.h>
22#include <ctype.h>
23#include <unistd.h>
24
25/* The failure code to exit with */
26#ifndef EXIT_FAILURE
27#ifdef VMS
28#define EXIT_FAILURE 0
29#else
30#define EXIT_FAILURE -1
31#endif
32#endif
33
34/* The biggest line we can read in from a file */
35#define LINEBUFFERSIZE 1024
36#define NUMTILDESUBS 30
37#define NUMCONFIGSUBS 1500
38#define TOKENBUFFERSIZE 80
39
40typedef struct {
41  char Tag[TOKENBUFFERSIZE];
42  char Value[512];
43} Translate;
44
45void tilde_sub(char [], Translate [], int);
46
47int
48main(int argc, char *argv[])
49{
50  int c, i;
51  char *ifile = NULL;
52  char WorkString[LINEBUFFERSIZE];
53  FILE *ConfigSH, *Config_H, *Extra_Subs;
54  char LineBuffer[LINEBUFFERSIZE], *TempValue, *StartTilde, *EndTilde;
55  char SecondaryLineBuffer[LINEBUFFERSIZE], OutBuf[LINEBUFFERSIZE];
56  char TokenBuffer[TOKENBUFFERSIZE];
57  int LineBufferLength, TempLength, DummyVariable, LineBufferLoop;
58  int TokenBufferLoop, ConfigSubLoop, GotIt, OutBufPos;
59  Translate TildeSub[NUMTILDESUBS];    /* Holds the tilde (~FOO~) */
60                                       /* substitutions */
61  Translate ConfigSub[NUMCONFIGSUBS];  /* Holds the substitutions from */
62                                       /* config.sh */
63  int TildeSubCount = 0, ConfigSubCount = 0; /* # of tilde substitutions */
64                                             /* and config substitutions, */
65                                             /* respectively */
66  if (argc < 3) {
67    printf("Usage: munchconfig config.sh config_h.sh [-f file] [foo=bar [baz=xyzzy [...]]]\n");
68    exit(EXIT_FAILURE);
69  }
70
71  optind = 3;    /* skip config.sh and config_h.sh */
72  while ((c = getopt(argc, argv, "f:")) != -1) {
73      switch (c) {
74        case 'f':
75            ifile = optarg;
76            break;
77        case ':':
78            fprintf(stderr, "Option -%c requires an operand\n", optopt);
79            break;
80        case '?':
81            fprintf(stderr,"Unrecognised option: -%c\n", optopt);
82      }
83  }
84
85  /* First, open the input files */
86  if (NULL == (ConfigSH = fopen(argv[1], "r"))) {
87    printf("Error %i trying to open config.sh file %s\n", errno, argv[1]);
88    exit(EXIT_FAILURE);
89  }
90
91  if (NULL == (Config_H = fopen(argv[2], "r"))) {
92    printf("Error %i trying to open config_h.sh file %s\n", errno, argv[2]);
93    exit(EXIT_FAILURE);
94  }
95
96  if (ifile != NULL && NULL == (Extra_Subs = fopen(ifile, "r"))) {
97    printf("Error %i trying to open extra substitutions file %s\n", errno, ifile);
98    exit(EXIT_FAILURE);
99  }
100
101  /* Any tag/value pairs on the command line? */
102  if (argc > optind) {
103    for (i=optind; i < argc && argv[i]; i++) {
104      /* Local copy */
105      strcpy(WorkString, argv[i]);
106      /* Stick a NULL over the = */
107      TempValue = strchr(WorkString, '=');
108      *TempValue++ = '\0';
109
110      /* Copy the tag and value into the holding array */
111      strcpy(TildeSub[TildeSubCount].Tag, WorkString);
112      strcpy(TildeSub[TildeSubCount].Value, TempValue);
113      TildeSubCount++;
114    }
115  }
116
117  /* Now read in the tag/value pairs from the extra substitutions file, if any */
118  while(ifile && fgets(LineBuffer, LINEBUFFERSIZE - 1, Extra_Subs)) {
119    /* Force a trailing null, just in case */
120    LineBuffer[LINEBUFFERSIZE - 1] = '\0';
121    LineBufferLength = strlen(LineBuffer);
122
123    /* Chop trailing control characters */
124    while((LineBufferLength > 0) && (LineBuffer[LineBufferLength-1] < ' ')) {
125      LineBuffer[LineBufferLength - 1] = '\0';
126      LineBufferLength--;
127    }
128
129    /* If it's empty, then try again */
130    if (!*LineBuffer)
131      continue;
132
133    /* Local copy */
134    strcpy(WorkString, LineBuffer);
135    /* Stick a NULL over the = */
136    TempValue = strchr(WorkString, '=');
137    *TempValue++ = '\0';
138
139    /* Copy the tag and value into the holding array */
140    strcpy(TildeSub[TildeSubCount].Tag, WorkString);
141    strcpy(TildeSub[TildeSubCount].Value, TempValue);
142    TildeSubCount++;
143  }
144
145
146  /* Now read in the config.sh file. */
147  while(fgets(LineBuffer, LINEBUFFERSIZE - 1, ConfigSH)) {
148    /* Force a trailing null, just in case */
149    LineBuffer[LINEBUFFERSIZE - 1] = '\0';
150
151    LineBufferLength = strlen(LineBuffer);
152
153    /* Chop trailing control characters */
154    while((LineBufferLength > 0) && (LineBuffer[LineBufferLength-1] < ' ')) {
155      LineBuffer[LineBufferLength - 1] = '\0';
156      LineBufferLength--;
157    }
158
159    /* If it's empty, then try again */
160    if (!*LineBuffer)
161      continue;
162
163    /* If the line begins with a '#' or ' ', skip */
164    if ((LineBuffer[0] == ' ') || (LineBuffer[0] == '#'))
165      continue;
166
167    /* We've got something. Guess we need to actually handle it */
168    /* Do the tilde substitution */
169    tilde_sub(LineBuffer, TildeSub, TildeSubCount);
170
171    /* Stick a NULL over the = */
172    TempValue = strchr(LineBuffer, '=');
173    *TempValue++ = '\0';
174    /* And another over the leading ', which better be there */
175    *TempValue++ = '\0';
176
177    /* Check to see if there's a trailing ' or ". If not, add a newline to
178       the buffer and grab another line. */
179    TempLength = strlen(TempValue);
180    while ((TempValue[TempLength-1] != '\'') &&
181           (TempValue[TempLength-1] != '"'))  {
182      fgets(SecondaryLineBuffer, LINEBUFFERSIZE - 1, ConfigSH);
183      /* Force a trailing null, just in case */
184      SecondaryLineBuffer[LINEBUFFERSIZE - 1] = '\0';
185      /* Go substitute */
186      tilde_sub(SecondaryLineBuffer, TildeSub, TildeSubCount);
187      /* Tack a nweline on the end of our primary buffer */
188      strcat(TempValue, "\n");
189      /* Concat the new line we just read */
190      strcat(TempValue, SecondaryLineBuffer);
191
192      /* Refigure the length */
193      TempLength = strlen(TempValue);
194
195      /* Chop trailing control characters */
196      while((TempLength > 0) && (TempValue[TempLength-1] < ' ')) {
197        TempValue[TempLength - 1] = '\0';
198        TempLength--;
199      }
200    }
201
202    /* And finally one over the trailing ' */
203    TempValue[TempLength-1] = '\0';
204
205    /* Is there even anything left? */
206    if(*TempValue) {
207      /* Copy the tag over */
208      strcpy(ConfigSub[ConfigSubCount].Tag, LineBuffer);
209      /* Copy the value over */
210      strcpy(ConfigSub[ConfigSubCount].Value, TempValue);
211
212      /* Up the count */
213      ConfigSubCount++;
214
215    }
216  }
217
218  /* Okay, we've read in all the substitutions from our config.sh */
219  /* equivalent. Read in the config_h.sh equiv and start the substitution */
220
221  /* First, eat all the lines until we get to one with !GROK!THIS! in it */
222  while(!strstr(fgets(LineBuffer, LINEBUFFERSIZE, Config_H),
223                "!GROK!THIS!")) {
224
225    /* Dummy statement to shut up any compiler that'll whine about an empty */
226    /* loop */
227    DummyVariable++;
228  }
229
230  /* Right, we've read all the lines through the first one with !GROK!THIS! */
231  /* in it. That gets us through the beginning stuff. Now start in earnest */
232  /* with our translations, which run until we get to another !GROK!THIS! */
233  while(!strstr(fgets(LineBuffer, LINEBUFFERSIZE, Config_H),
234                "!GROK!THIS!")) {
235    /* Force a trailing null, just in case */
236    LineBuffer[LINEBUFFERSIZE - 1] = '\0';
237
238    /* Tilde Substitute */
239    tilde_sub(LineBuffer, TildeSub, TildeSubCount);
240
241    LineBufferLength = strlen(LineBuffer);
242
243    /* Chop trailing control characters */
244    while((LineBufferLength > 0) && (LineBuffer[LineBufferLength-1] < ' ')) {
245      LineBuffer[LineBufferLength - 1] = '\0';
246      LineBufferLength--;
247    }
248
249    OutBufPos = 0;
250    /* Right. Go looking for $s. */
251    for(LineBufferLoop = 0; LineBufferLoop < LineBufferLength;
252        LineBufferLoop++) {
253      /* Did we find one? */
254      if ('$' != LineBuffer[LineBufferLoop]) {
255        /* Nope, spit out the value */
256        OutBuf[OutBufPos++] = LineBuffer[LineBufferLoop];
257      } else {
258        /* Yes, we did. Is it escaped? */
259        if ((LineBufferLoop > 0) && ('\\' == LineBuffer[LineBufferLoop -
260                                                       1])) {
261          /* Yup. Spit it out */
262          OutBuf[OutBufPos++] = LineBuffer[LineBufferLoop];
263        } else {
264         /* Nope. Go grab us a token */
265          TokenBufferLoop = 0;
266          /* Advance to the next character in the input stream */
267          LineBufferLoop++;
268          while((LineBufferLoop < LineBufferLength) &&
269                ((isalnum(LineBuffer[LineBufferLoop]) || ('_' ==
270                                                          LineBuffer[LineBufferLoop])))) {
271            TokenBuffer[TokenBufferLoop] = LineBuffer[LineBufferLoop];
272            LineBufferLoop++;
273            TokenBufferLoop++;
274          }
275
276          /* Trailing null on the token buffer */
277          TokenBuffer[TokenBufferLoop] = '\0';
278
279          /* Back the line buffer pointer up one */
280          LineBufferLoop--;
281
282          /* Right, we're done grabbing a token. Check to make sure we got */
283          /* something */
284          if (TokenBufferLoop) {
285            /* Well, we do. Run through all the tokens we've got in the */
286            /* ConfigSub array and see if any match */
287            GotIt = 0;
288            for(ConfigSubLoop = 0; ConfigSubLoop < ConfigSubCount;
289                ConfigSubLoop++) {
290              if (!strcmp(TokenBuffer, ConfigSub[ConfigSubLoop].Tag)) {
291                char *cp = ConfigSub[ConfigSubLoop].Value;
292                GotIt = 1;
293                while (*cp) OutBuf[OutBufPos++] = *(cp++);
294                break;
295              }
296            }
297
298            /* Did we find something? If not, spit out what was in our */
299            /* buffer */
300            if (!GotIt) {
301              char *cp = TokenBuffer;
302              OutBuf[OutBufPos++] = '$';
303              while (*cp) OutBuf[OutBufPos++] = *(cp++);
304            }
305
306          } else {
307            /* Just a bare $. Spit it out */
308            OutBuf[OutBufPos++] = '$';
309          }
310        }
311      }
312    }
313
314    /* If we've created an #undef line, make sure we don't output anything
315     * after the "#undef FOO" besides comments.  We could do this as we
316     * go by recognizing the #undef as it goes by, and thus avoid another
317     * use of a fixed-length buffer, but this is simpler.
318     */
319    if (!strncmp(OutBuf,"#undef",6)) {
320      char *cp = OutBuf;
321      int i, incomment = 0;
322      LineBufferLoop = 0;
323      OutBuf[OutBufPos] = '\0';
324      for (i = 0; i <= 1; i++) {
325        while (!isspace(*cp)) LineBuffer[LineBufferLoop++] = *(cp++);
326        while ( isspace(*cp)) LineBuffer[LineBufferLoop++] = *(cp++);
327      }
328      while (*cp) {
329        while (isspace(*cp)) LineBuffer[LineBufferLoop++] = *(cp++);
330        if (!incomment && *cp == '/' && *(cp+1) == '*') incomment = 1;
331        while (*cp && !isspace(*cp)) {
332          if (incomment) LineBuffer[LineBufferLoop++] = *cp;
333          cp++;
334        }
335        if (incomment && *cp == '*' && *(cp+1) == '/') incomment = 0;
336      }
337      LineBuffer[LineBufferLoop] = '\0';
338      puts(LineBuffer);
339    }
340    else {
341      OutBuf[OutBufPos] = '\0';
342      puts(OutBuf);
343    }
344  }
345
346  /* Close the files */
347  fclose(ConfigSH);
348  fclose(Config_H);
349  if (ifile) fclose(Extra_Subs);
350}
351
352void
353tilde_sub(char LineBuffer[], Translate TildeSub[], int TildeSubCount)
354{
355  char TempBuffer[LINEBUFFERSIZE], TempTilde[TOKENBUFFERSIZE];
356  int TildeLoop, InTilde, CopiedBufferLength, TildeBufferLength, k, GotIt;
357  int TempLength;
358  InTilde = 0;
359  CopiedBufferLength = 0;
360  TildeBufferLength = 0;
361  TempLength = strlen(LineBuffer);
362
363  /* Grovel over our input looking for ~foo~ constructs */
364  for(TildeLoop = 0; TildeLoop < TempLength; TildeLoop++) {
365    /* Are we in a tilde? */
366    if (InTilde) {
367      /* Yup. Is the current character a tilde? */
368      if (LineBuffer[TildeLoop] == '~') {
369        /* Yup. That means we're ready to do a substitution */
370        InTilde = 0;
371        GotIt = 0;
372        /* Trailing null */
373        TempTilde[TildeBufferLength] = '\0';
374        for( k=0; k < TildeSubCount; k++) {
375          if (!strcmp(TildeSub[k].Tag, TempTilde)) {
376            GotIt = 1;
377            /* Tack on the trailing null to the main buffer */
378            TempBuffer[CopiedBufferLength] = '\0';
379            /* Copy the tilde substitution over */
380            strcat(TempBuffer, TildeSub[k].Value);
381            CopiedBufferLength = strlen(TempBuffer);
382          }
383        }
384
385        /* Did we find anything? */
386        if (GotIt == 0) {
387          /* Guess not. Copy the whole thing out verbatim */
388          TempBuffer[CopiedBufferLength] = '\0';
389          TempBuffer[CopiedBufferLength++] = '~';
390          TempBuffer[CopiedBufferLength] = '\0';
391          strcat(TempBuffer, TempTilde);
392          strcat(TempBuffer, "~");
393          CopiedBufferLength = strlen(TempBuffer);
394        }
395
396      } else {
397        /* 'Kay, not a tilde. Is it a word character? */
398        if (isalnum(LineBuffer[TildeLoop]) ||
399            (LineBuffer[TildeLoop] == '-')) {
400          TempTilde[TildeBufferLength++] = LineBuffer[TildeLoop];
401        } else {
402          /* No, it's not a tilde character. For shame! We've got a */
403          /* bogus token. Copy a ~ into the output buffer, then append */
404          /* whatever we've got in our token buffer */
405          TempBuffer[CopiedBufferLength++] = '~';
406          TempBuffer[CopiedBufferLength] = '\0';
407          TempTilde[TildeBufferLength] = '\0';
408          strcat(TempBuffer, TempTilde);
409          CopiedBufferLength += TildeBufferLength;
410          InTilde = 0;
411        }
412      }
413    } else {
414      /* We're not in a tilde. Do we want to be? */
415      if (LineBuffer[TildeLoop] == '~') {
416        /* Guess so */
417        InTilde = 1;
418        TildeBufferLength = 0;
419      } else {
420        /* Nope. Copy the character to the output buffer */
421        TempBuffer[CopiedBufferLength++] = LineBuffer[TildeLoop];
422      }
423    }
424  }
425
426  /* Out of the loop. First, double-check to see if there was anything */
427  /* pending. */
428  if (InTilde) {
429    /* bogus token. Copy a ~ into the output buffer, then append */
430    /* whatever we've got in our token buffer */
431    TempBuffer[CopiedBufferLength++] = '~';
432    TempBuffer[CopiedBufferLength] = '\0';
433    TempTilde[TildeBufferLength] = '\0';
434    strcat(TempBuffer, TempTilde);
435    CopiedBufferLength += TildeBufferLength;
436  } else {
437    /* Nope, nothing pensing. Tack on a \0 */
438    TempBuffer[CopiedBufferLength] = '\0';
439  }
440
441  /* Okay, we're done. Copy the temp buffer back into the line buffer */
442  strcpy(LineBuffer, TempBuffer);
443
444}
445
446