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