/* 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 #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: patch-header FOO.H INFILE.H OUTFILE.H REQUIRED_FUNCS , July 1993. */ #include #include #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, *kind, *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; kind = ptr + 1; for (ptr = kind; *ptr != ';'; ) ptr++; *ptr = 0; if (*kind == 'X') { switch (special_file_handling) { case errno_special: if (strcmp (fname, "errno") == 0) seen_errno++; break; } continue; } if (*kind == '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 (kind[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 (kind[0] == 'I') /* don't edit inline function */ 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->params[0] == '\0' || strcmp(fn->params, "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_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 = 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 (!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 (!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 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 fprintf (outf, " %c", c); } } 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; }