source: trunk/emx/src/emxcat/emxcat.c@ 3669

Last change on this file since 3669 was 494, checked in by zap, 22 years ago

See ChangeLog.

  • Property cvs2svn:cvs-rev set to 1.5
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 13.3 KB
Line 
1/* emxcat.c -- Concatenate source files
2 Copyright (c) 1992-1998 Eberhard Mattes
3
4This file is part of emxcat.
5
6emxcat is free software; you can redistribute it and/or modify it
7under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11emxcat is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with emxcat; see the file COPYING. If not, write to
18the Free Software Foundation, 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA. */
20
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <ctype.h>
26
27#define NORETURN volatile
28
29#define FALSE 0
30#define TRUE 1
31
32typedef struct list
33{
34 struct list *next;
35 char *name;
36} list;
37
38typedef struct edge
39{
40 struct edge *next;
41 struct vertex *v;
42} edge;
43
44typedef struct vertex
45{
46 struct vertex *next;
47 edge *desc;
48 char *name;
49 int mark;
50 int number;
51 int descs;
52} vertex;
53
54static vertex *includes = NULL;
55static vertex **includes_add = &includes;
56static vertex *prev_include = NULL;
57static list *selects = NULL;
58static list **selects_add = &selects;
59static list *consts = NULL;
60static list **consts_add = &consts;
61static list *defines;
62static list **defines_add;
63static list *predefs = NULL;
64static list **predefs_add = &predefs;
65static char *output_fname;
66static FILE *output_file;
67static int rc = 0;
68static int define_warning;
69static int sys_emx_h = FALSE;
70
71
72/* Prototypes. */
73
74static void NORETURN usage (void);
75static void *xmalloc (size_t n);
76static char *xstrdup (const char *s);
77int main (int argc, char *argv[]);
78
79
80/* Tell the user how to invoke this program. */
81
82static void NORETURN usage (void)
83{
84 fprintf (stderr, "emxcat " VERSION INNOTEK_VERSION " -- "
85 "Copyright (c) 1992-1995 by Eberhard Mattes\n\n");
86 fprintf (stderr, "Usage: emxcat [-D<symbol>]... -o <output_file> <input_file>...\n");
87 exit (1);
88}
89
90
91/* Allocate a block of memory. Abort if out of memory. */
92
93static void *xmalloc (size_t n)
94{
95 void *p;
96
97 p = malloc (n);
98 if (p == NULL)
99 {
100 fprintf (stderr, "emxcat: out of memory\n");
101 exit (2);
102 }
103 return p;
104}
105
106
107/* Create on the heap a duplicate of the string S. Abort if out of
108 memory. */
109
110static char *xstrdup (const char *s)
111{
112 char *p;
113
114 p = xmalloc (strlen (s) + 1);
115 strcpy (p, s);
116 return p;
117}
118
119
120/* Add a string to a linked to list of strings. */
121
122static void add_list (list ***dst, const char *src)
123{
124 list *p;
125
126 p = xmalloc (sizeof (*p));
127 p->next = NULL;
128 p->name = (char *)src;
129 **dst = p;
130 *dst = &p->next;
131}
132
133
134/* Handle an #include directive. */
135
136static int do_include (const char *str, int pass, const char *fname)
137{
138 char delim;
139 char *name;
140 int len;
141 const char *start, *s;
142 vertex *p, *v;
143 edge *e;
144
145 if (pass == 1 && defines != NULL && !define_warning)
146 {
147 fprintf (stderr, "emxcat: #define used before #include in %s\n",
148 fname);
149 define_warning = TRUE;
150 rc = 1;
151 }
152 s = str;
153 if (!isspace ((unsigned char)*s))
154 return TRUE;
155 while (isspace ((unsigned char)*s))
156 ++s;
157 if (*s == '"')
158 delim = '"';
159 else if (*s == '<')
160 delim = '>';
161 else
162 return TRUE;
163 start = s;
164 ++s;
165 while (*s != 0 && *s != delim)
166 ++s;
167 if (*s != delim)
168 return TRUE;
169 if (pass == 1)
170 return FALSE;
171 ++s;
172 len = s - start;
173 name = alloca (len+1);
174 memcpy (name, start, len);
175 name[len] = 0;
176 if (strcmp (name, "<sys/emx.h>") == 0)
177 {
178 sys_emx_h = TRUE;
179 return FALSE;
180 }
181 v = NULL;
182 for (p = includes; p != NULL; p = p->next)
183 if (strcmp (name, p->name) == 0)
184 {
185 v = p;
186 break;
187 }
188 if (v == NULL)
189 {
190 v = xmalloc (sizeof (*v));
191 v->next = NULL;
192 v->name = xstrdup (name);
193 v->desc = NULL;
194 v->mark = 0;
195 v->descs = 0;
196 *includes_add = v;
197 includes_add = &v->next;
198 }
199 if (prev_include != NULL)
200 {
201 for (e = v->desc; e != NULL; e = e->next)
202 if (e->v == prev_include)
203 break;
204 if (e == NULL)
205 {
206 e = xmalloc (sizeof (*e));
207 e->next = v->desc;
208 v->desc = e;
209 e->v = prev_include;
210 }
211 }
212 prev_include = v;
213 return FALSE;
214}
215
216
217/* Handle a #define directive. */
218
219static int do_define (const char *str, int pass)
220{
221 char *name;
222 int len;
223 const char *start, *s;
224 list *p;
225
226 s = str;
227 if (!isspace ((unsigned char)*s))
228 return TRUE;
229 while (isspace ((unsigned char)*s))
230 ++s;
231 start = s;
232 while (*s == '_' || isalnum ((unsigned char)*s))
233 ++s;
234 len = s - start;
235 name = alloca (len+1);
236 memcpy (name, start, len);
237 name[len] = 0;
238 while (isspace ((unsigned char)*s))
239 ++s;
240 if (strncmp (s, "/*", 2) == 0)
241 s = strchr (s, 0);
242 if (strncmp (name, "INCL_", 5) == 0 && *s == 0)
243 {
244 if (pass == 1)
245 return FALSE;
246 for (p = selects; p != NULL; p = p->next)
247 if (strcmp (p->name, name) == 0)
248 return FALSE;
249 add_list (&selects_add, xstrdup (name));
250 return FALSE;
251 }
252 else if (pass == 0)
253 return FALSE;
254 else
255 {
256 add_list (&defines_add, xstrdup (name));
257 return TRUE;
258 }
259}
260
261
262/* Handle a CONST_ definition encountered in an .s file. */
263
264static int do_const (const char *str, int pass)
265{
266 const char *s;
267 char *name;
268 int len;
269 list *p;
270
271 if (pass == 0)
272 return FALSE;
273 for (s = str; *s == '_' || isalnum ((unsigned char)*s); ++s)
274 ;
275 len = s - str;
276 name = alloca (len+1);
277 memcpy (name, str, len);
278 name[len] = 0;
279 for (p = consts; p != NULL; p = p->next)
280 if (strcmp (name, p->name) == 0)
281 return FALSE;
282 add_list (&consts_add, xstrdup (name));
283 return TRUE;
284}
285
286
287/* Clear all marks of the dag of header files. */
288
289static void unmark (void)
290{
291 vertex *p;
292
293 for (p = includes; p != NULL; p = p->next)
294 p->mark = 0;
295}
296
297
298/* Number the vertices of the dag of header files. */
299
300static int vn;
301
302static void number_2 (vertex *v)
303{
304 edge *e;
305
306 v->mark = 1;
307 v->number = vn++;
308 v->descs = 0;
309 for (e = v->desc; e != NULL; e = e->next)
310 if (!e->v->mark)
311 {
312 number_2 (e->v);
313 v->descs += 1 + e->v->descs;
314 }
315}
316
317/* Helper function for checking for cycles the dag of header files. */
318
319static void cycles_2 (vertex *v, const char *fname)
320{
321 edge *e;
322 vertex *w;
323
324 v->mark = 1;
325 for (e = v->desc; e != NULL; e = e->next)
326 {
327 w = e->v;
328 if (w->mark)
329 {
330 if (v->number >= w->number && v->number <= w->number + w->descs)
331 {
332 fprintf (stderr, "emxcat: %s: inconsistent order of %s and %s\n",
333 fname, v->name, w->name);
334 rc = 1;
335 }
336 }
337 else
338 cycles_2 (w, fname);
339 }
340}
341
342/* Check for cycles the dag of header files. */
343
344static void cycles (const char *fname)
345{
346 vertex *p;
347
348 unmark ();
349 vn = 1;
350 for (p = includes; p != NULL; p = p->next)
351 if (!p->mark)
352 number_2 (p);
353 unmark ();
354 for (p = includes; p != NULL; p = p->next)
355 if (!p->mark)
356 cycles_2 (p, fname);
357}
358
359
360/* Read the input file named FNAME. If PASS is zero, collect
361 information. If PASS is one, copy the file contents to the output
362 file. */
363
364static void read_file (const char *fname, int pass)
365{
366 FILE *f;
367 char *s;
368 list *p, *q;
369 enum {TYPE_UNKNOWN, TYPE_C, TYPE_S} type;
370 char line[1024];
371 int copy;
372
373 /* Determine the type of the file (C or assembler) from the file
374 name suffix. */
375
376 type = TYPE_UNKNOWN;
377 s = _getext (fname);
378 if (s != NULL)
379 {
380 ++s;
381 if (stricmp (s, "c") == 0 || stricmp (s, "cc") == 0
382 || stricmp (s, "cpp") == 0 || stricmp (s, "cxx") == 0)
383 type = TYPE_C;
384 else if (stricmp (s, "s") == 0)
385 type = TYPE_S;
386 }
387
388 /* Abort if the file type is not known. */
389
390 if (type == TYPE_UNKNOWN)
391 {
392 fprintf (stderr, "emxcat: unknown file type (%s)\n", fname);
393 exit (1);
394 }
395
396 /* Open the file. */
397
398 f = fopen (fname, "rt");
399 if (f == NULL)
400 {
401 perror (fname);
402 exit (2);
403 }
404
405 /* Initialize file-local variables. */
406
407 defines = NULL; defines_add = &defines; prev_include = NULL;
408 define_warning = FALSE;
409
410 /* Read lines until hitting the end of the file. */
411
412 while (fgets (line, sizeof (line), f) != NULL)
413 {
414 /* Lines starting with #include and #define are handled
415 specially. In assembler files, lines starting with CONST_
416 are handled specially. In the second pass, all other lines
417 are copied verbatim to the output file. */
418
419 if (memcmp (line, "#include", 8) == 0)
420 copy = do_include (line+8, pass, fname);
421 else if (type == TYPE_S && memcmp (line, "CONST_", 6) == 0)
422 copy = do_const (line, pass);
423 else if (memcmp (line, "#define", 7) == 0)
424 copy = do_define (line+7, pass);
425 else
426 copy = TRUE;
427
428 /* Write the line to the output file if we are in the second
429 pass and the line is to be copied. */
430
431 if (copy && pass == 1)
432 {
433 if (fputs (line, output_file) == EOF)
434 break;
435 }
436 }
437
438 /* Check the input stream for I/O errors, then close it. */
439
440 if (ferror (f))
441 {
442 perror (fname);
443 exit (2);
444 }
445 fclose (f);
446
447 /* Undefine all symbols #defined in this file. */
448
449 if (defines != NULL)
450 {
451 fputc ('\n', output_file);
452 for (p = defines; p != NULL; p = q)
453 {
454 q = p->next;
455 fprintf (output_file, "#undef %s\n", p->name);
456 free (p->name);
457 free (p);
458 }
459 }
460
461 /* Check for cycles the dag of header files. */
462
463 if (pass == 0)
464 cycles (fname);
465
466 /* In the second pass, write an empty line to the output file, and
467 check for I/O errors. */
468
469 if (pass == 1)
470 {
471 fputc ('\n', output_file);
472 if (ferror (output_file))
473 {
474 perror ("emxcat output file");
475 exit (2);
476 }
477 }
478}
479
480
481/* Write #define directives for predefined symbols. */
482
483static void insert_predefs (void)
484{
485 list *p;
486
487 for (p = predefs; p != NULL; p = p->next)
488 fprintf (output_file, "#define %s\n", p->name);
489}
490
491
492/* Insert #define directives which have been used before #include
493 directives. */
494
495static void insert_selects (void)
496{
497 list *p;
498
499 for (p = selects; p != NULL; p = p->next)
500 fprintf (output_file, "#define %s\n", p->name);
501}
502
503
504/* Topological sort on the dag of #include directives, writing them to
505 the output file. */
506
507static void insert_includes_2 (vertex *v)
508{
509 edge *e;
510
511 v->mark = 1;
512 for (e = v->desc; e != NULL; e = e->next)
513 if (!e->v->mark)
514 insert_includes_2 (e->v);
515 fprintf (output_file, "#include %s\n", v->name);
516}
517
518
519/* Compare two argv[] entries by name. This function is passed to
520 qsort() for sorting the input files by name. */
521
522int cmp_fname (const void *p1, const void *p2)
523{
524 /* Disregard case when comparing file names, to get same result on
525 case-preserving file systems (HPFS) and upper-casing file systems
526 (FAT). */
527
528 return strcasecmp (*(const char * const *)p1,
529 *(const char * const *)p2);
530}
531
532
533/* Topologically sort #include directives and write them to the output
534 file. <sys/emx.h> always comes first. */
535
536static void insert_includes (void)
537{
538 vertex *p;
539
540 if (sys_emx_h)
541 fputs ("#include <sys/emx.h>\n", output_file);
542 unmark ();
543 for (p = includes; p != NULL; p = p->next)
544 if (!p->mark)
545 insert_includes_2 (p);
546}
547
548
549/* The main code of emxcat. */
550
551int main (int argc, char *argv[])
552{
553 int i, j, pass;
554
555 /* Expand response files and wildcards. */
556
557 _response (&argc, &argv);
558 _wildcard (&argc, &argv);
559
560 /* Handle -D options. */
561
562 i = 1;
563 while (i < argc && strncmp (argv[i], "-D", 2) == 0 && strlen (argv[i]) > 2)
564 {
565 add_list (&predefs_add, xstrdup (argv[i] + 2));
566 ++i;
567 }
568
569 /* 3 or more arguments must be left: -o <output_file> <input_file> */
570
571 if (argc - i < 3)
572 usage ();
573 if (strcmp (argv[i], "-o") != 0)
574 usage ();
575 ++i;
576 output_fname = argv[i++];
577
578 /* Open the output file. */
579
580 output_file = fopen (output_fname, "wt");
581 if (output_file == NULL)
582 {
583 perror (output_fname);
584 exit (2);
585 }
586
587 /* Sort the input files by name. This is for getting predictable
588 results when using wildcards. Note that _wildcard() yields files
589 sorted by name on HPFS partitions. On FAT partitions, however,
590 the files are not in a particular order. */
591
592 qsort (argv + i, argc - i, sizeof (*argv), cmp_fname);
593
594 /* Make two passes over all the files. The first pass collects
595 information, the second pass writes the output file. */
596
597 for (pass = 0; pass < 2; ++pass)
598 {
599 if (pass == 1)
600 {
601 /* At the beginning of the second pass, write #define and
602 #include directives. */
603
604 insert_predefs ();
605 insert_selects ();
606 insert_includes ();
607 }
608
609 /* Read each input file given on the command line, provided its
610 name is not identical to the name of the output file. */
611
612 for (j = i; j < argc; ++j)
613 if (strcasecmp (output_fname, argv[j]) != 0)
614 read_file (argv[j], pass);
615 }
616
617 /* Close the output file. */
618
619 if (fflush (output_file) != 0 || fclose (output_file) != 0)
620 {
621 perror (output_fname);
622 exit (2);
623 }
624
625 /* Done. */
626
627 return rc;
628}
Note: See TracBrowser for help on using the repository browser.