vms.c backslash efs and long name fixes
[perl.git] / vms / munchconfig.c
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 1000
38 #define TOKENBUFFERSIZE 80
39
40 typedef struct {
41   char Tag[TOKENBUFFERSIZE];
42   char Value[512];
43 } Translate;
44
45 void tilde_sub(char [], Translate [], int);
46
47 int
48 main(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 substititions 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 anthing
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
352 void
353 tilde_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