Initial import from FreeBSD RELENG_4:
[games.git] / bin / sh / mkinit.c
1 /*-
2  * Copyright (c) 1991, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Kenneth Almquist.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #ifndef lint
38 static char const copyright[] =
39 "@(#) Copyright (c) 1991, 1993\n\
40         The Regents of the University of California.  All rights reserved.\n";
41 #endif /* not lint */
42
43 #ifndef lint
44 #if 0
45 static char sccsid[] = "@(#)mkinit.c    8.2 (Berkeley) 5/4/95";
46 #endif
47 #endif /* not lint */
48 #include <sys/cdefs.h>
49 __FBSDID("$FreeBSD: src/bin/sh/mkinit.c,v 1.14.2.1 2002/07/19 04:38:51 tjr Exp $");
50
51 /*
52  * This program scans all the source files for code to handle various
53  * special events and combines this code into one file.  This (allegedly)
54  * improves the structure of the program since there is no need for
55  * anyone outside of a module to know that that module performs special
56  * operations on particular events.
57  *
58  * Usage:  mkinit sourcefile...
59  */
60
61
62 #include <sys/cdefs.h>
63 #include <sys/types.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <fcntl.h>
68 #include <unistd.h>
69 #include <errno.h>
70
71
72 /*
73  * OUTFILE is the name of the output file.  Output is initially written
74  * to the file OUTTEMP, which is then moved to OUTFILE.
75  */
76
77 #define OUTFILE "init.c"
78 #define OUTTEMP "init.c.new"
79
80
81 /*
82  * A text structure is basicly just a string that grows as more characters
83  * are added onto the end of it.  It is implemented as a linked list of
84  * blocks of characters.  The routines addstr and addchar append a string
85  * or a single character, respectively, to a text structure.  Writetext
86  * writes the contents of a text structure to a file.
87  */
88
89 #define BLOCKSIZE 512
90
91 struct text {
92         char *nextc;
93         int nleft;
94         struct block *start;
95         struct block *last;
96 };
97
98 struct block {
99         struct block *next;
100         char text[BLOCKSIZE];
101 };
102
103
104 /*
105  * There is one event structure for each event that mkinit handles.
106  */
107
108 struct event {
109         char *name;             /* name of event (e.g. INIT) */
110         char *routine;          /* name of routine called on event */
111         char *comment;          /* comment describing routine */
112         struct text code;       /* code for handling event */
113 };
114
115
116 char writer[] = "\
117 /*\n\
118  * This file was generated by the mkinit program.\n\
119  */\n\
120 \n";
121
122 char init[] = "\
123 /*\n\
124  * Initialization code.\n\
125  */\n";
126
127 char reset[] = "\
128 /*\n\
129  * This routine is called when an error or an interrupt occurs in an\n\
130  * interactive shell and control is returned to the main command loop.\n\
131  */\n";
132
133 char shellproc[] = "\
134 /*\n\
135  * This routine is called to initialize the shell to run a shell procedure.\n\
136  */\n";
137
138
139 struct event event[] = {
140         {"INIT", "init", init},
141         {"RESET", "reset", reset},
142         {"SHELLPROC", "initshellproc", shellproc},
143         {NULL, NULL}
144 };
145
146
147 char *curfile;                          /* current file */
148 int linno;                              /* current line */
149 char *header_files[200];                /* list of header files */
150 struct text defines;                    /* #define statements */
151 struct text decls;                      /* declarations */
152 int amiddecls;                          /* for formatting */
153
154
155 void readfile(char *);
156 int match(char *, char *);
157 int gooddefine(char *);
158 void doevent(struct event *, FILE *, char *);
159 void doinclude(char *);
160 void dodecl(char *, FILE *);
161 void output(void);
162 void addstr(char *, struct text *);
163 void addchar(int, struct text *);
164 void writetext(struct text *, FILE *);
165 FILE *ckfopen(char *, char *);
166 void *ckmalloc(int);
167 char *savestr(char *);
168 void error(char *);
169
170 #define equal(s1, s2)   (strcmp(s1, s2) == 0)
171
172 int
173 main(int argc __unused, char *argv[])
174 {
175         char **ap;
176
177         header_files[0] = "\"shell.h\"";
178         header_files[1] = "\"mystring.h\"";
179         for (ap = argv + 1 ; *ap ; ap++)
180                 readfile(*ap);
181         output();
182         rename(OUTTEMP, OUTFILE);
183         exit(0);
184 }
185
186
187 /*
188  * Parse an input file.
189  */
190
191 void
192 readfile(char *fname)
193 {
194         FILE *fp;
195         char line[1024];
196         struct event *ep;
197
198         fp = ckfopen(fname, "r");
199         curfile = fname;
200         linno = 0;
201         amiddecls = 0;
202         while (fgets(line, sizeof line, fp) != NULL) {
203                 linno++;
204                 for (ep = event ; ep->name ; ep++) {
205                         if (line[0] == ep->name[0] && match(ep->name, line)) {
206                                 doevent(ep, fp, fname);
207                                 break;
208                         }
209                 }
210                 if (line[0] == 'I' && match("INCLUDE", line))
211                         doinclude(line);
212                 if (line[0] == 'M' && match("MKINIT", line))
213                         dodecl(line, fp);
214                 if (line[0] == '#' && gooddefine(line)) {
215                         char *cp;
216                         char line2[1024];
217                         static const char undef[] = "#undef ";
218
219                         strcpy(line2, line);
220                         memcpy(line2, undef, sizeof(undef) - 1);
221                         cp = line2 + sizeof(undef) - 1;
222                         while(*cp && (*cp == ' ' || *cp == '\t'))
223                                 cp++;
224                         while(*cp && *cp != ' ' && *cp != '\t' && *cp != '\n')
225                                 cp++;
226                         *cp++ = '\n'; *cp = '\0';
227                         addstr(line2, &defines);
228                         addstr(line, &defines);
229                 }
230         }
231         fclose(fp);
232 }
233
234
235 int
236 match(char *name, char *line)
237 {
238         char *p, *q;
239
240         p = name, q = line;
241         while (*p) {
242                 if (*p++ != *q++)
243                         return 0;
244         }
245         if (*q != '{' && *q != ' ' && *q != '\t' && *q != '\n')
246                 return 0;
247         return 1;
248 }
249
250
251 int
252 gooddefine(char *line)
253 {
254         char *p;
255
256         if (! match("#define", line))
257                 return 0;                       /* not a define */
258         p = line + 7;
259         while (*p == ' ' || *p == '\t')
260                 p++;
261         while (*p != ' ' && *p != '\t') {
262                 if (*p == '(')
263                         return 0;               /* macro definition */
264                 p++;
265         }
266         while (*p != '\n' && *p != '\0')
267                 p++;
268         if (p[-1] == '\\')
269                 return 0;                       /* multi-line definition */
270         return 1;
271 }
272
273
274 void
275 doevent(struct event *ep, FILE *fp, char *fname)
276 {
277         char line[1024];
278         int indent;
279         char *p;
280
281         sprintf(line, "\n      /* from %s: */\n", fname);
282         addstr(line, &ep->code);
283         addstr("      {\n", &ep->code);
284         for (;;) {
285                 linno++;
286                 if (fgets(line, sizeof line, fp) == NULL)
287                         error("Unexpected EOF");
288                 if (equal(line, "}\n"))
289                         break;
290                 indent = 6;
291                 for (p = line ; *p == '\t' ; p++)
292                         indent += 8;
293                 for ( ; *p == ' ' ; p++)
294                         indent++;
295                 if (*p == '\n' || *p == '#')
296                         indent = 0;
297                 while (indent >= 8) {
298                         addchar('\t', &ep->code);
299                         indent -= 8;
300                 }
301                 while (indent > 0) {
302                         addchar(' ', &ep->code);
303                         indent--;
304                 }
305                 addstr(p, &ep->code);
306         }
307         addstr("      }\n", &ep->code);
308 }
309
310
311 void
312 doinclude(char *line)
313 {
314         char *p;
315         char *name;
316         char **pp;
317
318         for (p = line ; *p != '"' && *p != '<' && *p != '\0' ; p++);
319         if (*p == '\0')
320                 error("Expecting '\"' or '<'");
321         name = p;
322         while (*p != ' ' && *p != '\t' && *p != '\n')
323                 p++;
324         if (p[-1] != '"' && p[-1] != '>')
325                 error("Missing terminator");
326         *p = '\0';
327
328         /* name now contains the name of the include file */
329         for (pp = header_files ; *pp && ! equal(*pp, name) ; pp++);
330         if (*pp == NULL)
331                 *pp = savestr(name);
332 }
333
334
335 void
336 dodecl(char *line1, FILE *fp)
337 {
338         char line[1024];
339         char *p, *q;
340
341         if (strcmp(line1, "MKINIT\n") == 0) { /* start of struct/union decl */
342                 addchar('\n', &decls);
343                 do {
344                         linno++;
345                         if (fgets(line, sizeof line, fp) == NULL)
346                                 error("Unterminated structure declaration");
347                         addstr(line, &decls);
348                 } while (line[0] != '}');
349                 amiddecls = 0;
350         } else {
351                 if (! amiddecls)
352                         addchar('\n', &decls);
353                 q = NULL;
354                 for (p = line1 + 6 ; *p && strchr("=/\n", *p) == NULL; p++)
355                         continue;
356                 if (*p == '=') {                /* eliminate initialization */
357                         for (q = p ; *q && *q != ';' ; q++);
358                         if (*q == '\0')
359                                 q = NULL;
360                         else {
361                                 while (p[-1] == ' ')
362                                         p--;
363                                 *p = '\0';
364                         }
365                 }
366                 addstr("extern", &decls);
367                 addstr(line1 + 6, &decls);
368                 if (q != NULL)
369                         addstr(q, &decls);
370                 amiddecls = 1;
371         }
372 }
373
374
375
376 /*
377  * Write the output to the file OUTTEMP.
378  */
379
380 void
381 output(void)
382 {
383         FILE *fp;
384         char **pp;
385         struct event *ep;
386
387         fp = ckfopen(OUTTEMP, "w");
388         fputs(writer, fp);
389         for (pp = header_files ; *pp ; pp++)
390                 fprintf(fp, "#include %s\n", *pp);
391         fputs("\n\n\n", fp);
392         writetext(&defines, fp);
393         fputs("\n\n", fp);
394         writetext(&decls, fp);
395         for (ep = event ; ep->name ; ep++) {
396                 fputs("\n\n\n", fp);
397                 fputs(ep->comment, fp);
398                 fprintf(fp, "\nvoid\n%s() {\n", ep->routine);
399                 writetext(&ep->code, fp);
400                 fprintf(fp, "}\n");
401         }
402         fclose(fp);
403 }
404
405
406 /*
407  * A text structure is simply a block of text that is kept in memory.
408  * Addstr appends a string to the text struct, and addchar appends a single
409  * character.
410  */
411
412 void
413 addstr(char *s, struct text *text)
414 {
415         while (*s) {
416                 if (--text->nleft < 0)
417                         addchar(*s++, text);
418                 else
419                         *text->nextc++ = *s++;
420         }
421 }
422
423
424 void
425 addchar(int c, struct text *text)
426 {
427         struct block *bp;
428
429         if (--text->nleft < 0) {
430                 bp = ckmalloc(sizeof *bp);
431                 if (text->start == NULL)
432                         text->start = bp;
433                 else
434                         text->last->next = bp;
435                 text->last = bp;
436                 text->nextc = bp->text;
437                 text->nleft = BLOCKSIZE - 1;
438         }
439         *text->nextc++ = c;
440 }
441
442 /*
443  * Write the contents of a text structure to a file.
444  */
445 void
446 writetext(struct text *text, FILE *fp)
447 {
448         struct block *bp;
449
450         if (text->start != NULL) {
451                 for (bp = text->start ; bp != text->last ; bp = bp->next)
452                         fwrite(bp->text, sizeof (char), BLOCKSIZE, fp);
453                 fwrite(bp->text, sizeof (char), BLOCKSIZE - text->nleft, fp);
454         }
455 }
456
457 FILE *
458 ckfopen(char *file, char *mode)
459 {
460         FILE *fp;
461
462         if ((fp = fopen(file, mode)) == NULL) {
463                 fprintf(stderr, "Can't open %s: %s\n", file, strerror(errno));
464                 exit(2);
465         }
466         return fp;
467 }
468
469 void *
470 ckmalloc(int nbytes)
471 {
472         char *p;
473
474         if ((p = malloc(nbytes)) == NULL)
475                 error("Out of space");
476         return p;
477 }
478
479 char *
480 savestr(char *s)
481 {
482         char *p;
483
484         p = ckmalloc(strlen(s) + 1);
485         strcpy(p, s);
486         return p;
487 }
488
489 void
490 error(char *msg)
491 {
492         if (curfile != NULL)
493                 fprintf(stderr, "%s:%d: ", curfile, linno);
494         fprintf(stderr, "%s\n", msg);
495         exit(2);
496 }