/* fix-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 prevent 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 #endif then extra matter added to the include file is placed inside the . * 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: fix-header FOO.H INFILE.H OUTFILE.H REQUIRED_FUNCS , July 1993. */ #include #include #include #include #ifndef O_RDONLY #define O_RDONLY 0 #endif #include "hconfig.h" #include "obstack.h" #include "scan.h" int verbose = 0; int partial_count = 0; int missing_extern_C_count = 0; int missing_errno = 0; #include "xsys-protos.h" char *inf_buffer; char *inf_limit; char *inf_ptr; /* 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; /* 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 = "fix-header"; FILE *outf; sstring buf; sstring line; int lbrac_line, rbrac_line; char **required_functions; int required_unseen_count; int write_lbrac () { if (missing_extern_C_count + required_unseen_count > 0) 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 recognized_macro (fname) char *fname; { /* The original include file defines fname as a macro. */ struct fn_decl *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) missing_errno = 0; 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++; } } } void recognized_extern (name, type) char *name; char *type; { switch (special_file_handling) { case errno_special: if (strcmp (name, "errno") == 0) missing_errno = 0; break; } } /* Called by scan_decls if it saw a function definition for a function named FNAME, with return type RTYPE, and argument list ARGS, in source file FILE_SEEN on line LINE_SEEN. KIND is 'I' for an inline function; 'F' if a normal function declaration preceded by 'extern "C"' (or nested inside 'extern "C"' braces); or 'f' for other function declarations. */ void recognized_function (fname, kind, rtype, args, file_seen, line_seen) char *fname; int kind; /* One of 'f' 'F' or 'I' */ char *rtype; char *args; char *file_seen; int line_seen; { struct partial_proto *partial; int i; struct fn_decl *fn; if (kind == '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') return; if (kind == 'I') /* don't edit inline function */ return; /* 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) return; if (fn == NULL) return; if (fn->params[0] == '\0' || strcmp (fn->params, "void") == 0) return; /* 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_alloc (&scan_file_obstack, strlen (fname) + 1); strcpy (partial->fname, fname); partial->rtype = obstack_alloc (&scan_file_obstack, strlen (rtype) + 1); strcpy (partial->rtype, rtype); partial->line_seen = 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); } } void read_scan_file (scan_file) FILE *scan_file; { char **rptr; obstack_init (&scan_file_obstack); scan_decls (scan_file); if (missing_extern_C_count + required_unseen_count + partial_count + missing_errno == 0) { if (verbose) fprintf (stderr, "%s: OK, nothing needs to be done.\n", inc_filename); exit (0); } if (!verbose) fprintf (stderr, "%s: fixing %s\n", progname, inc_filename); else { 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 (missing_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; } if (missing_extern_C_count + required_unseen_count > 0) fprintf (outf, "#ifdef __cplusplus\n}\n#endif\n"); } char * strdup (str) char *str; { char *copy = (char *) xmalloc (strlen (str) + 1); strcpy (copy, str); return copy; } /* Returns 1 iff the file is properly protected from multiple inclusion: #ifndef PROTECT_NAME #define PROTECT_NAME #endif */ #define INF_GET() (inf_ptr < inf_limit ? *(unsigned char*)inf_ptr++ : EOF) #define INF_UNGET(c) ((c)!=EOF && inf_ptr--) int inf_skip_spaces (c) int c; { for (;;) { if (c == ' ' || c == '\t') c = INF_GET (); else if (c == '/') { c = INF_GET (); if (c != '*') { INF_UNGET (c); return '/'; } c = INF_GET (); for (;;) { if (c == EOF) return EOF; else if (c != '*') { if (c == '\n') source_lineno++, lineno++; c = INF_GET (); } else if ((c = INF_GET ()) == '/') return INF_GET (); } } else break; } return c; } /* Read into STR from inf_buffer upto DELIM. */ int inf_read_upto (str, delim) sstring *str; int delim; { int ch; for (;;) { ch = INF_GET (); if (ch == EOF || ch == delim) break; SSTRING_PUT (str, ch); } MAKE_SSTRING_SPACE (str, 1); *str->ptr = 0; return ch; } int inf_scan_ident (s, c) register sstring *s; int c; { s->ptr = s->base; if (isalpha (c) || c == '_') { for (;;) { SSTRING_PUT (s, c); c = INF_GET (); if (c == EOF || !(isalnum (c) || c == '_')) break; } } MAKE_SSTRING_SPACE (s, 1); *s->ptr = 0; return c; } /* Returns 1 if the file is correctly protected against multiple inclusion, setting *ifndef_line to the line number of the initial #ifndef and setting *endif_line to the final #endif. Otherwise return 0. */ int check_protection (ifndef_line, endif_line) 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 = inf_skip_spaces (' '); if (c == EOF) return 0; if (c != '\n') break; } if (c != '#') return 0; c = inf_scan_ident (&buf, inf_skip_spaces (' ')); 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 = inf_scan_ident (&buf, inf_skip_spaces (c)); if (SSTRING_LENGTH (&buf) == 0 || c == EOF) return 0; protect_name = strdup (buf.base); INF_UNGET (c); c = inf_read_upto (&buf, '\n'); if (c == EOF) return 0; lineno++; for (;;) { c = inf_skip_spaces (' '); if (c == EOF) return 0; if (c == '\n') { lineno++; continue; } if (c != '#') goto skip_to_eol; c = inf_scan_ident (&buf, inf_skip_spaces (' ')); 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 = inf_skip_spaces (c); c = inf_scan_ident (&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 = INF_GET (); } if (c == EOF) return 0; lineno++; } if (!define_seen) return 0; *endif_line = lineno; /* Skip final white space (including comments). */ for (;;) { c = inf_skip_spaces (' '); if (c == EOF) break; if (c != '\n') return 0; } return 1; } int main (argc, argv) int argc; char **argv; { int inf_fd; struct stat sbuf; int c; int i, done; char *cptr, *cptr0, **pptr; int ifndef_line; int endif_line; long to_read; long int inf_size; if (argv[0] && argv[0][0]) { register char *p; progname = 0; for (p = argv[0]; *p; p++) if (*p == '/') progname = p; progname = progname ? progname+1 : argv[0]; } if (argc < 4) { fprintf (stderr, "%s: Usage: foo.h infile.h outfile.h req_funcs 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_fd = open (argv[2], O_RDONLY, 0666); if (inf_fd < 0) { fprintf (stderr, "%s: Cannot open '%s' for reading -", progname, argv[2]); perror (NULL); exit (-1); } if (fstat (inf_fd, &sbuf) < 0) { fprintf (stderr, "%s: Cannot get size of '%s' -", progname, argv[2]); perror (NULL); exit (-1); } inf_size = sbuf.st_size; inf_buffer = (char*) xmalloc (inf_size + 2); inf_buffer[inf_size] = '\n'; inf_buffer[inf_size + 1] = '\0'; inf_limit = inf_buffer + inf_size; inf_ptr = inf_buffer; to_read = inf_size; while (to_read > 0) { long i = read (inf_fd, inf_buffer + inf_size - to_read, to_read); if (i < 0) { fprintf (stderr, "%s: Failed to read '%s' -", progname, argv[2]); perror (NULL); exit (-1); } if (i == 0) { inf_size -= to_read; break; } to_read -= i; } close (inf_fd); /* If file doesn't end with '\n', add one. */ if (inf_limit > inf_buffer && inf_limit[-1] != '\n') inf_limit++; unlink (argv[3]); outf = fopen (argv[3], "w"); if (outf == NULL) { fprintf (stderr, "%s: Cannot open '%s' for writing -", progname, argv[3]); perror (NULL); exit (-1); } lineno = 1; if (check_protection (&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; } /* Reset input file. */ inf_ptr = inf_buffer; lineno = 1; for (;;) { if (lineno == lbrac_line) write_lbrac (); if (lineno == rbrac_line) write_rbrac (); for (;;) { struct fn_decl *fn; c = INF_GET (); if (c == EOF) break; if (isalpha (c) || c == '_') { struct partial_proto *partial; c = inf_scan_ident (&buf, c); INF_UNGET (c); 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 = inf_skip_spaces (' '); if (c == EOF) break; if (c == '(') { c = inf_skip_spaces (' '); if (c == ')') { fprintf (outf, " _PARAMS((%s))", fn->params); } else { putc ('(', outf); INF_UNGET (c); } } else fprintf (outf, " %c", c); } } else { putc (c, outf); if (c == '\n') break; } } if (c == EOF) break; lineno++; } if (rbrac_line < 0) write_rbrac (); fclose (outf); return 0; }