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