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