summaryrefslogtreecommitdiff
path: root/gcc/fix-header.c
diff options
context:
space:
mode:
authorbothner <bothner@138bc75d-0d04-0410-961f-82ee72b054a4>1993-07-26 00:11:20 +0000
committerbothner <bothner@138bc75d-0d04-0410-961f-82ee72b054a4>1993-07-26 00:11:20 +0000
commit41f8c0bb2babab1eddb059f6fb78b8f0da65a822 (patch)
treed62b715f580e46dc24f6184c8c26da60ec50313d /gcc/fix-header.c
parenta0f2061cf6523e9ac06fdee65cda04727146904f (diff)
downloadgcc-41f8c0bb2babab1eddb059f6fb78b8f0da65a822.tar.gz
Initial revision
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@4986 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/fix-header.c')
-rw-r--r--gcc/fix-header.c674
1 files changed, 674 insertions, 0 deletions
diff --git a/gcc/fix-header.c b/gcc/fix-header.c
new file mode 100644
index 00000000000..4d3dfefb549
--- /dev/null
+++ b/gcc/fix-header.c
@@ -0,0 +1,674 @@
+/* patch-header.c - Make C header file suitable for C++.
+ Copyright (C) 1993 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This program massages a system include file (such as stdio.h),
+ into a form more conformant with ANSI/POSIX, and more suitable for C++:
+
+ * extern "C" { ... } braces are added (inside #ifndef __cplusplus),
+ if they seem to be needed. These prevcnt C++ compilers from name
+ mangling the functions inside the braces.
+
+ * If an old-style incomplete function declaration is seen (without
+ an argument list), and it is a "standard" function listed in
+ the file sys-protos.h (and with a non-empty argument list), then
+ the declaration is converted to a complete prototype by replacing
+ the empty parameter list with the argument lust from sys-protos.h.
+
+ * The program can be given a list of (names of) required standard
+ functions (such as fclose for stdio.h). If a reqquired function
+ is not seen in the input, then a prototype for it will be
+ written to the output.
+
+ * If all of the non-comment code of the original file is protected
+ against multiple inclusion:
+ #ifndef FOO
+ #define FOO
+ <body of include file>
+ #endif
+ then extra matter added to the include file is placed inside the <body>.
+
+ * If the input file is OK (nothing needs to be done);
+ the output file is not written (nor removed if it exists).
+
+ There are also some special actions that are done for certain
+ well-known standard include files:
+
+ * If argv[1] is "sys/stat.h", the Posix.1 macros
+ S_ISBLK, S_ISCHR, S_ISDIR, S_ISFIFO, S_ISLNK, S_ISREG are added if
+ they were missing, and the corresponding "traditional" S_IFxxx
+ macros were defined.
+
+ * If argv[1] is "errno.h", errno is declared if it was missing.
+
+ * TODO: The input file should be read complete into memory, because:
+ a) it needs to be scanned twice anyway, and
+ b) it would be nice to allow update in place.
+
+ Usage:
+ patch-header FOO.H INFILE.H OUTFILE.H REQUIRED_FUNCS <SCAN-FILE
+ where:
+ * FOO.H is the relative file name of the include file,
+ as it would be #include'd by a C file. (E.g. stdio.h)
+ * INFILE.H is a full pathname for the input file (e.g. /usr/include/stdio.h)
+ * OUTFILE.H is the full pathname for where to write the output file,
+ if anything needs to be done. (e.g. ./include/stdio.h)
+ * SCAN-FILE is the output of the scan-decls program.
+ * REQUIRED_FUNCS is a list of required function (e.g. fclose for stdio.h).
+
+ Written by Per Bothner <bothner@cygnus.com>, July 1993. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "obstack.h"
+#include "scan.h"
+
+extern char *strcpy();
+sstring buf;
+int verbose = 0;
+int partial_count = 0;
+int missing_extern_C_count = 0;
+int missing_extra_stuff = 0;
+
+#include "xsys-protos.h"
+
+/* Certain standard files get extra treatment */
+
+enum special_file
+{
+ no_special,
+ errno_special,
+ sys_stat_special
+};
+
+enum special_file special_file_handling = no_special;
+
+/* The following are only used when handling sys/stat.h */
+/* They are set if the corresponding macro has been seen. */
+int seen_S_IFBLK = 0, seen_S_ISBLK = 0;
+int seen_S_IFCHR = 0, seen_S_ISCHR = 0;
+int seen_S_IFDIR = 0, seen_S_ISDIR = 0;
+int seen_S_IFIFO = 0, seen_S_ISFIFO = 0;
+int seen_S_IFLNK = 0, seen_S_ISLNK = 0;
+int seen_S_IFREG = 0, seen_S_ISREG = 0;
+
+/* The following are only used when handling errno.h */
+int seen_errno = 0;
+
+/* Wrapper around free, to avoid prototype clashes. */
+
+void xfree (ptr)
+ char *ptr;
+{
+ free(ptr);
+}
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free xfree
+struct obstack scan_file_obstack;
+
+/* NOTE: If you edit this, also edit gen-protos.c !! */
+struct fn_decl *
+lookup_std_proto (name)
+ char *name;
+{
+ int i = hash(name) % HASH_SIZE;
+ int i0 = i;
+ for (;;)
+ {
+ struct fn_decl *fn;
+ if (hash_tab[i] == 0)
+ return NULL;
+ fn = &std_protos[hash_tab[i]];
+ if (strcmp (fn->fname, name) == 0)
+ return fn;
+ i = (i+1) % HASH_SIZE;
+ if (i == i0)
+ abort();
+ }
+}
+
+char *inc_filename;
+int inc_filename_length;
+char *progname = "patch-header";
+FILE *outf;
+sstring buf;
+sstring line;
+
+int lbrac_line, rbrac_line;
+
+char **required_functions;
+int required_unseen_count;
+
+int
+write_lbrac ()
+{
+ fprintf (outf, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n");
+
+ if (partial_count)
+ {
+ fprintf (outf, "#ifndef _PARAMS\n");
+ fprintf (outf, "#if defined(__STDC__) || defined(__cplusplus)\n");
+ fprintf (outf, "#define _PARAMS(ARGS) ARGS\n");
+ fprintf (outf, "#else\n");
+ fprintf (outf, "#define _PARAMS(ARGS) ()\n");
+ fprintf (outf, "#endif\n#endif /* _PARAMS */\n");
+ }
+}
+
+struct partial_proto
+{
+ struct partial_proto *next;
+ char *fname; /* name of function */
+ char *rtype; /* return type */
+ struct fn_decl *fn;
+ int line_seen;
+};
+
+struct partial_proto *partial_proto_list = NULL;
+
+struct partial_proto required_dummy_proto;
+#define REQUIRED(FN) ((FN)->partial == &required_dummy_proto)
+#define SET_REQUIRED(FN) ((FN)->partial = &required_dummy_proto)
+#define CLEAR_REQUIRED(FN) ((FN)->partial = 0)
+
+void
+read_scan_file (scan_file)
+ FILE *scan_file;
+{
+ char **rptr;
+ int i;
+ obstack_init(&scan_file_obstack);
+
+ for (;;)
+ {
+ struct partial_proto *partial;
+ struct fn_decl *fn;
+ int ch;
+ char *ptr, *fname, *extern_C, *rtype, *args, *file_seen, *line_seen;
+ line.ptr = line.base;
+ ch = read_upto (scan_file, &line, '\n');
+ if (ch == EOF)
+ break;
+
+ fname = line.base;
+ for (ptr = fname; *ptr != ';'; ) ptr++;
+ *ptr = 0;
+ extern_C = ptr + 1;
+ for (ptr = extern_C; *ptr != ';'; ) ptr++;
+ *ptr = 0;
+
+ if (*extern_C == 'X')
+ {
+ switch (special_file_handling)
+ {
+ case errno_special:
+ if (strcmp (fname, "errno") == 0) seen_errno++;
+ break;
+ }
+ continue;
+ }
+
+ if (*extern_C == 'M')
+ {
+ /* The original include file defines fname as a macro. */
+ fn = lookup_std_proto (fname);
+
+ /* Since fname is a macro, don't require a prototype for it. */
+ if (fn && REQUIRED (fn))
+ {
+ CLEAR_REQUIRED(fn);
+ required_unseen_count--;
+ }
+
+ switch (special_file_handling)
+ {
+ case errno_special:
+ if (strcmp (fname, "errno") == 0) seen_errno++;
+ break;
+ case sys_stat_special:
+ if (fname[0] == 'S' && fname[1] == '_')
+ {
+ if (strcmp (fname, "S_IFBLK") == 0) seen_S_IFBLK++;
+ else if (strcmp (fname, "S_ISBLK") == 0) seen_S_ISBLK++;
+ else if (strcmp (fname, "S_IFCHR") == 0) seen_S_IFCHR++;
+ else if (strcmp (fname, "S_ISCHR") == 0) seen_S_ISCHR++;
+ else if (strcmp (fname, "S_IFDIR") == 0) seen_S_IFDIR++;
+ else if (strcmp (fname, "S_ISDIR") == 0) seen_S_ISDIR++;
+ else if (strcmp (fname, "S_IFIFO") == 0) seen_S_IFIFO++;
+ else if (strcmp (fname, "S_ISFIFO") == 0) seen_S_ISFIFO++;
+ else if (strcmp (fname, "S_IFLNK") == 0) seen_S_IFLNK++;
+ else if (strcmp (fname, "S_ISLNK") == 0) seen_S_ISLNK++;
+ else if (strcmp (fname, "S_IFREG") == 0) seen_S_IFREG++;
+ else if (strcmp (fname, "S_ISREG") == 0) seen_S_ISREG++;
+ }
+ break;
+ }
+ continue;
+ }
+
+ rtype = ptr + 1;
+ for (ptr = rtype; *ptr != ';'; ) ptr++;
+ *ptr = 0;
+ args = ptr + 1;
+ for (ptr = args; *ptr != ';'; ) ptr++;
+ *ptr = 0;
+ file_seen = ptr + 1;
+ for (ptr = file_seen; *ptr != ';'; ) ptr++;
+ *ptr = 0;
+ line_seen = ptr + 1;
+ for (ptr = line_seen; *ptr != ';'; ) ptr++;
+ *ptr = 0;
+
+ if (extern_C[0] == 'f')
+ missing_extern_C_count++;
+
+ fn = lookup_std_proto (fname);
+
+ /* Remove the function from the list of required function. */
+ if (fn && REQUIRED (fn))
+ {
+ CLEAR_REQUIRED(fn);
+ required_unseen_count--;
+ }
+
+ /* If we have a full prototype, we're done. */
+ if (args[0] != '\0')
+ continue;
+
+ /* If the partial prototype was included from some other file,
+ we don't need to patch it up (in this run). */
+ i = strlen (file_seen);
+ if (i < inc_filename_length
+ || strcmp (inc_filename, file_seen + (i - inc_filename_length)) != 0)
+ continue;
+
+ if (fn == NULL)
+ continue;
+ if (fn->fname[0] == '\0' || strcmp(fn->fname, "void") == 0)
+ continue;
+
+ /* We only have a partial function declaration,
+ so remember that we have to add a complete prototype. */
+ partial_count++;
+ partial = (struct partial_proto*)
+ obstack_alloc (&scan_file_obstack, sizeof(struct partial_proto));
+ partial->fname
+ = obstack_copy0 (&scan_file_obstack, fname, strlen (fname));
+ partial->rtype
+ = obstack_copy0 (&scan_file_obstack, rtype, strlen (rtype));
+ partial->line_seen = atoi(line_seen);
+ partial->fn = fn;
+ fn->partial = partial;
+ partial->next = partial_proto_list;
+ partial_proto_list = partial;
+ if (verbose)
+ {
+ fprintf (stderr, "(%s: %s non-prototype function declaration.)\n",
+ inc_filename, fname);
+ }
+ }
+
+ if (missing_extern_C_count + required_unseen_count + partial_count
+ + missing_extra_stuff == 0)
+ {
+ if (verbose)
+ fprintf (stderr, "%s: OK, nothing needs to be done.\n", inc_filename);
+ exit (0);
+ }
+ if (required_unseen_count)
+ fprintf (stderr, "%s: %d missing function declarations.\n",
+ inc_filename, required_unseen_count);
+ if (partial_count)
+ fprintf (stderr, "%s: %d non-prototype function declarations.\n",
+ inc_filename, partial_count);
+ if (missing_extern_C_count)
+ fprintf (stderr, "%s: %d declarations not protected by extern \"C\".\n",
+ inc_filename, missing_extern_C_count);
+}
+
+write_rbrac ()
+{
+ struct fn_decl *fn;
+ char **rptr;
+ register struct partial_proto *partial;
+
+ if (required_unseen_count)
+ fprintf (outf, "#if defined(__STDC__) || defined(__cplusplus)\n");
+
+ /* Now we print out prototypes for those functions that we haven't seen. */
+ for (rptr = required_functions; *rptr; rptr++)
+ {
+ fn = lookup_std_proto (*rptr);
+ if (fn == NULL || !REQUIRED (fn))
+ continue;
+ fprintf (outf, "extern %s %s (%s);\n",
+ fn->rtype, fn->fname, fn->params);
+ }
+ if (required_unseen_count)
+ fprintf (outf,
+ "#endif /* defined(__STDC__) || defined(__cplusplus) */\n");
+
+ switch (special_file_handling)
+ {
+ case errno_special:
+ if (!seen_errno)
+ fprintf (outf, "extern int errno;\n");
+ break;
+ case sys_stat_special:
+ if (!seen_S_ISBLK && seen_S_IFBLK)
+ fprintf (outf,
+ "#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)\n");
+ if (!seen_S_ISCHR && seen_S_IFCHR)
+ fprintf (outf,
+ "#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)\n");
+ if (!seen_S_ISDIR && seen_S_IFDIR)
+ fprintf (outf,
+ "#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)\n");
+ if (!seen_S_ISFIFO && seen_S_IFIFO)
+ fprintf (outf,
+ "#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)\n");
+ if (!seen_S_ISLNK && seen_S_IFLNK)
+ fprintf (outf,
+ "#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)\n");
+ if (!seen_S_ISREG && seen_S_IFREG)
+ fprintf (outf,
+ "#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)\n");
+ break;
+ }
+
+
+ fprintf (outf, "#ifdef __cplusplus\n}\n#endif\n");
+}
+
+char *
+strdup (str)
+ char *str;
+{
+ return strcpy((char*)malloc (strlen (str) + 1), str);
+}
+
+/* Returns 1 iff the file is properly protected from multiple inclusion:
+ #ifndef PROTECT_NAME
+ #define PROTECT_NAME
+ #endif
+
+ */
+
+int
+check_protection (inf, ifndef_line, endif_line)
+ FILE *inf;
+ int *ifndef_line, *endif_line;
+{
+ int c;
+ int if_nesting = 1; /* Level of nesting of #if's */
+ char *protect_name = NULL; /* Identifier following initial #ifndef */
+ int define_seen = 0;
+
+ /* Skip initial white space (including comments). */
+ for (;; lineno++)
+ {
+ c = skip_spaces (inf, ' ');
+ if (c == EOF)
+ return 0;
+ if (c != '\n')
+ break;
+ }
+ if (c != '#')
+ return 0;
+ c = scan_ident (inf, &buf, skip_spaces (inf, ' '));
+ if (SSTRING_LENGTH(&buf) == 0 || strcmp (buf.base, "ifndef") != 0)
+ return 0;
+
+ /* So far so good: We've seen an initial #ifndef. */
+ *ifndef_line = lineno;
+ c = scan_ident (inf, &buf, skip_spaces (inf, c));
+ if (SSTRING_LENGTH(&buf) == 0 || c == EOF)
+ return 0;
+ protect_name = strdup (buf.base);
+
+ ungetc (c, inf);
+ c = read_upto (inf, &buf, '\n');
+ if (c == EOF)
+ return 0;
+ lineno++;
+
+ for (;;)
+ {
+ c = skip_spaces(inf, ' ');
+ if (c == EOF)
+ return 0;
+ if (c == '\n')
+ {
+ lineno++;
+ continue;
+ }
+ if (c != '#')
+ goto skip_to_eol;
+ c = scan_ident (inf, &buf, skip_spaces (inf, ' '));
+ if (SSTRING_LENGTH(&buf) == 0)
+ ;
+ else if (!strcmp (buf.base, "ifndef")
+ || !strcmp (buf.base, "ifdef") || !strcmp (buf.base, "if"))
+ {
+ if_nesting++;
+ }
+ else if (!strcmp (buf.base, "endif"))
+ {
+ if_nesting--;
+ if (if_nesting == 0)
+ break;
+ }
+ else if (!strcmp (buf.base, "else"))
+ {
+ if (if_nesting == 1)
+ return 0;
+ }
+ else if (!strcmp (buf.base, "define"))
+ {
+ if (if_nesting != 1)
+ goto skip_to_eol;
+ c = skip_spaces (inf, c);
+ c = scan_ident (inf, &buf, c);
+ if (buf.base[0] > 0 && strcmp(buf.base, protect_name) == 0)
+ define_seen = 1;
+ }
+ skip_to_eol:
+ for (;;)
+ {
+ if (c == '\n' || c == EOF)
+ break;
+ c = getc (inf);
+ }
+ if (c == EOF)
+ return 0;
+ lineno++;
+ }
+
+ if (!define_seen)
+ return 0;
+ *endif_line = lineno;
+ /* Skip final white space (including comments). */
+ for (;;)
+ {
+ c = skip_spaces (inf, ' ');
+ if (c == EOF)
+ break;
+ if (c != '\n')
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ FILE *inf;
+ int c;
+ int i, done;
+ char *cptr, *cptr0, **pptr;
+ int ifndef_line;
+ int endif_line;;
+
+
+ if (argv[0] && argv[0][0])
+ progname = argv[0];
+
+ if (argc < 4)
+ {
+ fprintf (stderr, "%s: Usage: foo.h infile.h outfile.h req_funcs <scan-file-name\n",
+ progname);
+ exit (-1);
+ }
+
+ inc_filename = argv[1];
+ inc_filename_length = strlen (inc_filename);
+ if (strcmp (inc_filename, "sys/stat.h") == 0)
+ special_file_handling = sys_stat_special;
+ else if (strcmp (inc_filename, "errno.h") == 0)
+ special_file_handling = errno_special, missing_extra_stuff++;
+
+ /* Calculate an upper bound of the number of function names in argv[4] */
+ for (i = 1, cptr = argv[4]; *cptr; cptr++)
+ if (*cptr == ' ') i++;
+ /* Find the list of prototypes required for this include file. */
+ required_functions = (char**)xmalloc((i+1) * sizeof(char*));
+ for (cptr = argv[4], cptr0 = cptr, pptr = required_functions, done = 0;
+ !done; cptr++)
+ {
+ done = *cptr == '\0';
+ if (*cptr == ' ' || done)
+ {
+ *cptr = '\0';
+ if (cptr > cptr0)
+ {
+ struct fn_decl *fn = lookup_std_proto(cptr0);
+ *pptr++ = cptr0;
+ if (fn == NULL)
+ fprintf (stderr, "Internal error: No prototype for %s\n",
+ cptr0);
+ else
+ SET_REQUIRED(fn);
+ }
+ cptr0 = cptr + 1;
+ }
+ }
+ required_unseen_count = pptr - required_functions;
+ *pptr = 0;
+
+ read_scan_file (stdin);
+
+ inf = fopen (argv[2], "r");
+ if (inf == NULL)
+ {
+ fprintf (stderr, "%s: Cannot open '%s' for reading -",
+ progname, argv[2]);
+ perror (NULL);
+ exit (-1);
+ }
+
+ outf = fopen (argv[3], "w");
+ if (outf == NULL)
+ {
+ fprintf (stderr, "%s: Cannot open '%s' for writing -",
+ progname, argv[3]);
+ perror (NULL);
+ exit (-1);
+ }
+
+ if (check_protection (inf, &ifndef_line, &endif_line))
+ {
+#if 0
+ fprintf(stderr, "#ifndef %s on line %d; #endif on line %d\n",
+ protect_name, ifndef_line, endif_line);
+#endif
+ lbrac_line = ifndef_line+1;
+ rbrac_line = endif_line;
+ }
+ else
+ {
+ lbrac_line = 1;
+ rbrac_line = -1;
+ }
+
+ fseek(inf, 0, 0);
+ lineno = 1;
+
+ for (;;)
+ {
+ if (lineno == lbrac_line)
+ write_lbrac ();
+ if (lineno == rbrac_line)
+ write_rbrac ();
+ for (;;)
+ {
+ struct fn_decl *fn;
+ c = getc (inf);
+ if (c == EOF)
+ break;
+ if (isalpha (c) || c == '_')
+ {
+ struct partial_proto *partial;
+ ungetc (c, inf);
+ if (get_token (inf, &buf) != IDENTIFIER_TOKEN)
+ abort ();
+ fputs (buf.base, outf);
+ fn = lookup_std_proto (buf.base);
+ /* We only want to edit the declaration matching the one
+ seen by scan-decls, as there can be multiple
+ declarations, selected by #ifdef __STDC__ or whatever. */
+ if (fn && fn->partial && fn->partial->line_seen == lineno)
+ {
+ c = skip_spaces (inf, ' ');
+ if (c == EOF)
+ break;
+ if (c == '(')
+ {
+ c = skip_spaces (inf, ' ');
+ if (c == ')')
+ {
+ fprintf (outf, " _PARAMS((%s))", fn->params);
+ }
+ else
+ {
+ putc ('(', outf);
+ ungetc (c, inf);
+ }
+ }
+ else
+ putc (c, outf);
+ }
+ }
+ else
+ putc (c, outf);
+ if (c == '\n')
+ break;
+ }
+ if (c == EOF)
+ break;
+ lineno++;
+ }
+ if (rbrac_line < 0)
+ write_rbrac ();
+
+ fclose (inf);
+ fclose (outf);
+
+ return 0;
+}