Add the "wordexp" shell built-in command.
[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.4 2005/04/19 05:18:19 cpressey 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         const char *name;       /* name of event (e.g. INIT) */
101         const char *routine;    /* name of routine called on event */
102         const 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, {NULL, 0, NULL, NULL}},
132         {"RESET", "reset", reset, {NULL, 0, NULL, NULL}},
133         {"SHELLPROC", "initshellproc", shellproc, {NULL, 0, NULL, NULL}},
134         {NULL, NULL, NULL, {NULL, 0, NULL, NULL}}
135 };
136
137
138 const char *curfile;                    /* current file */
139 int linno;                              /* current line */
140 const 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(const char *);
147 int match(const char *, const char *);
148 int gooddefine(const char *);
149 void doevent(struct event *, FILE *, const char *);
150 void doinclude(char *);
151 void dodecl(char *, FILE *);
152 void output(void);
153 void addstr(const char *, struct text *);
154 void addchar(int, struct text *);
155 void writetext(struct text *, FILE *);
156 FILE *ckfopen(const char *, const char *);
157 void *ckmalloc(int);
158 char *savestr(const char *);
159 void error(const 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         header_files[2] = "\"init.h\"";
171         for (ap = argv + 1 ; *ap ; ap++)
172                 readfile(*ap);
173         output();
174         rename(OUTTEMP, OUTFILE);
175         exit(0);
176 }
177
178
179 /*
180  * Parse an input file.
181  */
182
183 void
184 readfile(const char *fname)
185 {
186         FILE *fp;
187         char line[1024];
188         struct event *ep;
189
190         fp = ckfopen(fname, "r");
191         curfile = fname;
192         linno = 0;
193         amiddecls = 0;
194         while (fgets(line, sizeof line, fp) != NULL) {
195                 linno++;
196                 for (ep = event ; ep->name ; ep++) {
197                         if (line[0] == ep->name[0] && match(ep->name, line)) {
198                                 doevent(ep, fp, fname);
199                                 break;
200                         }
201                 }
202                 if (line[0] == 'I' && match("INCLUDE", line))
203                         doinclude(line);
204                 if (line[0] == 'M' && match("MKINIT", line))
205                         dodecl(line, fp);
206                 if (line[0] == '#' && gooddefine(line)) {
207                         char *cp;
208                         char line2[1024];
209                         static const char undef[] = "#undef ";
210
211                         strcpy(line2, line);
212                         memcpy(line2, undef, sizeof(undef) - 1);
213                         cp = line2 + sizeof(undef) - 1;
214                         while(*cp && (*cp == ' ' || *cp == '\t'))
215                                 cp++;
216                         while(*cp && *cp != ' ' && *cp != '\t' && *cp != '\n')
217                                 cp++;
218                         *cp++ = '\n'; *cp = '\0';
219                         addstr(line2, &defines);
220                         addstr(line, &defines);
221                 }
222         }
223         fclose(fp);
224 }
225
226
227 int
228 match(const char *name, const char *line)
229 {
230         const char *p, *q;
231
232         p = name, q = line;
233         while (*p) {
234                 if (*p++ != *q++)
235                         return 0;
236         }
237         if (*q != '{' && *q != ' ' && *q != '\t' && *q != '\n')
238                 return 0;
239         return 1;
240 }
241
242
243 int
244 gooddefine(const char *line)
245 {
246         const char *p;
247
248         if (! match("#define", line))
249                 return 0;                       /* not a define */
250         p = line + 7;
251         while (*p == ' ' || *p == '\t')
252                 p++;
253         while (*p != ' ' && *p != '\t') {
254                 if (*p == '(')
255                         return 0;               /* macro definition */
256                 p++;
257         }
258         while (*p != '\n' && *p != '\0')
259                 p++;
260         if (p[-1] == '\\')
261                 return 0;                       /* multi-line definition */
262         return 1;
263 }
264
265
266 void
267 doevent(struct event *ep, FILE *fp, const char *fname)
268 {
269         char line[1024];
270         int indent;
271         char *p;
272
273         sprintf(line, "\n      /* from %s: */\n", fname);
274         addstr(line, &ep->code);
275         addstr("      {\n", &ep->code);
276         for (;;) {
277                 linno++;
278                 if (fgets(line, sizeof line, fp) == NULL)
279                         error("Unexpected EOF");
280                 if (equal(line, "}\n"))
281                         break;
282                 indent = 6;
283                 for (p = line ; *p == '\t' ; p++)
284                         indent += 8;
285                 for ( ; *p == ' ' ; p++)
286                         indent++;
287                 if (*p == '\n' || *p == '#')
288                         indent = 0;
289                 while (indent >= 8) {
290                         addchar('\t', &ep->code);
291                         indent -= 8;
292                 }
293                 while (indent > 0) {
294                         addchar(' ', &ep->code);
295                         indent--;
296                 }
297                 addstr(p, &ep->code);
298         }
299         addstr("      }\n", &ep->code);
300 }
301
302
303 void
304 doinclude(char *line)
305 {
306         char *p;
307         char *name;
308         const char **pp;
309
310         for (p = line ; *p != '"' && *p != '<' && *p != '\0' ; p++);
311         if (*p == '\0')
312                 error("Expecting '\"' or '<'");
313         name = p;
314         while (*p != ' ' && *p != '\t' && *p != '\n')
315                 p++;
316         if (p[-1] != '"' && p[-1] != '>')
317                 error("Missing terminator");
318         *p = '\0';
319
320         /* name now contains the name of the include file */
321         for (pp = header_files ; *pp && ! equal(*pp, name) ; pp++);
322         if (*pp == NULL)
323                 *pp = savestr(name);
324 }
325
326
327 void
328 dodecl(char *line1, FILE *fp)
329 {
330         char line[1024];
331         char *p, *q;
332
333         if (strcmp(line1, "MKINIT\n") == 0) { /* start of struct/union decl */
334                 addchar('\n', &decls);
335                 do {
336                         linno++;
337                         if (fgets(line, sizeof line, fp) == NULL)
338                                 error("Unterminated structure declaration");
339                         addstr(line, &decls);
340                 } while (line[0] != '}');
341                 amiddecls = 0;
342         } else {
343                 if (! amiddecls)
344                         addchar('\n', &decls);
345                 q = NULL;
346                 for (p = line1 + 6 ; *p && strchr("=/\n", *p) == NULL; p++)
347                         continue;
348                 if (*p == '=') {                /* eliminate initialization */
349                         for (q = p ; *q && *q != ';' ; q++);
350                         if (*q == '\0')
351                                 q = NULL;
352                         else {
353                                 while (p[-1] == ' ')
354                                         p--;
355                                 *p = '\0';
356                         }
357                 }
358                 addstr("extern", &decls);
359                 addstr(line1 + 6, &decls);
360                 if (q != NULL)
361                         addstr(q, &decls);
362                 amiddecls = 1;
363         }
364 }
365
366
367
368 /*
369  * Write the output to the file OUTTEMP.
370  */
371
372 void
373 output(void)
374 {
375         FILE *fp;
376         const char * const *pp;
377         struct event *ep;
378
379         fp = ckfopen(OUTTEMP, "w");
380         fputs(writer, fp);
381         for (pp = header_files ; *pp ; pp++)
382                 fprintf(fp, "#include %s\n", *pp);
383         fputs("\n\n\n", fp);
384         writetext(&defines, fp);
385         fputs("\n\n", fp);
386         writetext(&decls, fp);
387         for (ep = event ; ep->name ; ep++) {
388                 fputs("\n\n\n", fp);
389                 fputs(ep->comment, fp);
390                 fprintf(fp, "\nvoid\n%s(void) {\n", ep->routine);
391                 writetext(&ep->code, fp);
392                 fprintf(fp, "}\n");
393         }
394         fclose(fp);
395 }
396
397
398 /*
399  * A text structure is simply a block of text that is kept in memory.
400  * Addstr appends a string to the text struct, and addchar appends a single
401  * character.
402  */
403
404 void
405 addstr(const char *s, struct text *text)
406 {
407         while (*s) {
408                 if (--text->nleft < 0)
409                         addchar(*s++, text);
410                 else
411                         *text->nextc++ = *s++;
412         }
413 }
414
415
416 void
417 addchar(int c, struct text *text)
418 {
419         struct block *bp;
420
421         if (--text->nleft < 0) {
422                 bp = ckmalloc(sizeof *bp);
423                 if (text->start == NULL)
424                         text->start = bp;
425                 else
426                         text->last->next = bp;
427                 text->last = bp;
428                 text->nextc = bp->text;
429                 text->nleft = BLOCKSIZE - 1;
430         }
431         *text->nextc++ = c;
432 }
433
434 /*
435  * Write the contents of a text structure to a file.
436  */
437 void
438 writetext(struct text *text, FILE *fp)
439 {
440         struct block *bp;
441
442         if (text->start != NULL) {
443                 for (bp = text->start ; bp != text->last ; bp = bp->next)
444                         fwrite(bp->text, sizeof (char), BLOCKSIZE, fp);
445                 fwrite(bp->text, sizeof (char), BLOCKSIZE - text->nleft, fp);
446         }
447 }
448
449 FILE *
450 ckfopen(const char *file, const char *mode)
451 {
452         FILE *fp;
453
454         if ((fp = fopen(file, mode)) == NULL) {
455                 fprintf(stderr, "Can't open %s: %s\n", file, strerror(errno));
456                 exit(2);
457         }
458         return fp;
459 }
460
461 void *
462 ckmalloc(int nbytes)
463 {
464         char *p;
465
466         if ((p = malloc(nbytes)) == NULL)
467                 error("Out of space");
468         return p;
469 }
470
471 char *
472 savestr(const char *s)
473 {
474         char *p;
475
476         p = ckmalloc(strlen(s) + 1);
477         strcpy(p, s);
478         return p;
479 }
480
481 void
482 error(const char *msg)
483 {
484         if (curfile != NULL)
485                 fprintf(stderr, "%s:%d: ", curfile, linno);
486         fprintf(stderr, "%s\n", msg);
487         exit(2);
488 }