
/* "makeproto"  Copyright 1989, 1990, 1991, 1992, 1993 Free Software Foundation */


/* Program to scan old-style source files and make prototypes */



#include <stdio.h>
#include <ctype.h>
#include <time.h>

#ifdef M_XENIX
# define BSD 0
#endif

#ifdef FILE       /* a #define in BSD, a typedef in SYSV (hp-ux, at least) */
# ifndef BSD
#  define BSD 1
# endif
#endif

#ifdef BSD
# if !BSD
#  undef BSD
# endif
#endif


#ifdef BSD
# include <strings.h>
#else
# include <string.h>
#endif



#define isidchar(x)    (isalnum(x) || (x) == '_')

#define dprintf        if (!debug) ; else printf

#define MAXARGS        16



int verbose, debug, incomment;


struct warnstruct {
    char *bad, *good;
} warntypes[] = {
    { "char",             "int" },
    { "signed char",      "int" },
    { "unsigned char",    "int" },
    { "short",            "int" },
    { "signed short",     "int" },
    { "unsigned short",   "int" },
    { "boolean",          "int" },
    { "Boolean",          "int" },
    { "float",            "double" },
    { NULL, NULL }
} ;



int readline(buf, inf)
char *buf;
FILE *inf;
{
    char *cp, *cp2;
    int spflag;

    for (;;) {
        if (fgets(buf, 1000, inf)) {
            cp = buf;
            cp2 = buf;
            spflag = 0;
            while (*cp) {
                if (incomment) {
                    if (cp[0] == '*' && cp[1] == '/') {
                        incomment = 0;
                        cp += 2;
                    } else
                        cp++;
                    spflag = 1;
                } else {
                    if (cp[0] == '/' && cp[1] == '*') {
                        incomment = 1;
                        cp += 2;
                    } else if (isspace(*cp)) {
                        spflag = 1;
                        cp++;
                    } else {
                        if (spflag)
                            *cp2++ = ' ';
                        *cp2++ = *cp++;
                        spflag = 0;
                    }
                }
            }
            *cp2 = 0;
            if (!*buf)
                continue;
            if (verbose)
                printf("\217%s\210\n", buf);
            return 1;
        } else
            strcpy(buf, "\001");
            return 0;
    }
}




int strbeginsword(s1, s2)
register char *s1, *s2;
{
    while (*s2 && *s1 == *s2)
        s1++, s2++;
    return (!*s2 && !isidchar(*s1));
}




void usage()
{
    fprintf(stderr, "usage:  makeproto [options] [infile ...] [-o outfile]]\n");
    fprintf(stderr, "           -tnnn   Tab to nnn after type name [default 15]\n");
    fprintf(stderr, "           -annn   Tab to nnn before arguments [default 30]\n");
    fprintf(stderr, "           -s0     Omit functions declared static\n");
    fprintf(stderr, "           -s1     Omit functions not declared static\n");
    fprintf(stderr, "           -x      Add \"extern\" keyword (-X => \"Extern\")\n");
    fprintf(stderr, "           -n      Include argument names in prototypes\n");
    fprintf(stderr, "           -m      Use PP/PV macro notation\n");
    exit(1);
}




#define bounce(msg)   do {  if (verbose) printf("Bounced: %s\n", msg); if (stupid) goto Lbounce;  } while (0)





int main(argc, argv)
int argc;
char **argv;
{
    FILE *inf, *outf;
    char outfname[256];
    char buf[1000], ifdefname[256];
    char ftype[256], fname[80], dtype[256], decl[256], dname[80], temp[256];
    char argdecls[MAXARGS][256], argnames[MAXARGS][80];
    char *cp, *cp2, *cp3;
    int i, j, pos, len, thistab, numstars, whichf, nargs, incomment, errors = 0;
    long li;
    int typetab = 15, argtab = 30, width = 80, usenames = 0, usemacros = 0;
    int useextern = 0, staticness = -1, hasheader = 0, useifdefs = 0;
    int stupid = 1, firstdecl;

    errors = 0;
    verbose = 0;
    debug = 0;
    *outfname = 0;
    while (argc > 1 && argv[1][0] == '-') {
        if (argv[1][1] == 't') {
            typetab = atoi(argv[1] + 2);
        } else if (argv[1][1] == 'a') {
            argtab = atoi(argv[1] + 2);
        } else if (argv[1][1] == 'w') {
            width = atoi(argv[1] + 2);
        } else if (argv[1][1] == 's') {
            staticness = atoi(argv[1] + 2);
        } else if (argv[1][1] == 'v') {
            verbose = 1;
        } else if (argv[1][1] == 'D') {
            debug = 1;
        } else if (argv[1][1] == 'x') {
            useextern = 1;
        } else if (argv[1][1] == 'X') {
            useextern = 2;
        } else if (argv[1][1] == 'n') {
            usenames = 1;
        } else if (argv[1][1] == 'm') {
            usemacros = 1;
        } else if (argv[1][1] == 'h') {
            hasheader = 1;
        } else if (argv[1][1] == 'i') {
            useifdefs = 1;
        } else if (argv[1][1] == 'o' && argc > 2) {
            strcpy(outfname, argv[2]);
            argc--, argv++;
        } else {
            usage();
        }
        argc--, argv++;
    }
    if (argc > 2 && !strcmp(argv[argc-2], "-o")) {
        strcpy(outfname, argv[argc-1]);
        argc -= 2;
    }
    if (*outfname) {
        outf = fopen(outfname, "w");
        if (!outf) {
            perror(outfname);
            exit(1);
        }
    } else
        outf = stdout;
    if (hasheader) {
        time(&li);
        cp = ctime(&li);
        cp[24] = 0;
        fprintf(outf, "\n/* Declarations created by \"makeproto\" on %s */\n", cp);
        fprintf(outf, "\n\n");
    }
    incomment = 0;
    for (whichf = 1; whichf < argc + (argc < 2); whichf++) {
        if (whichf >= argc || !strcmp(argv[whichf], "-")) {
            inf = stdin;
        } else {
            inf = fopen(argv[whichf], "r");
            if (!inf) {
                perror(argv[whichf]);
                fprintf(outf, "\n/* Unable to open file %s */\n", argv[whichf]);
                errors++;
                continue;
            }
        }
        firstdecl = 1;
        while (readline(buf, inf)) {
            if (!isidchar(*buf))
                continue;
            cp = buf;
            cp2 = ftype;
            numstars = 0;
            while (isspace(*cp) || isidchar(*cp))
                *cp2++ = *cp++;
            if (*cp == '*') {
                while (*cp == '*' || isspace(*cp)) {
                    if (*cp == '*')
                        numstars++;
                    cp++;
                }
            } else {
                while (cp > buf && isspace(cp[-1])) cp--, cp2--;
                while (cp > buf && isidchar(cp[-1])) cp--, cp2--;
            }
            while (cp2 > ftype && isspace(cp2[-1])) cp2--;
            *cp2 = 0;
            if (!*ftype)
                strcpy(ftype, "int");
            dprintf("numstars is %d\n", numstars);   /***/
            dprintf("ftype is %s\n", ftype);     /***/
            dprintf("cp after ftype is %s\n", cp);     /***/
            if (strbeginsword(ftype, "static") || strbeginsword(ftype, "Static")) {
                if (staticness == 0)
                    bounce("Function is static");
            } else {
                if (staticness == 1)
                    bounce("Function is not static");
                if (useextern &&
                     !strbeginsword(ftype, "extern") && !strbeginsword(ftype, "Extern")) {
                    sprintf(temp, useextern == 2 ? "Extern %s" : "extern %s", ftype);
                    strcpy(ftype, temp);
                }
            }
            while (isspace(*cp)) cp++;
            if (!*cp) {
                readline(buf, inf);
                cp = buf;
            }
            dprintf("cp before fname is %s\n", cp);     /***/
            if (!isidchar(*cp))
                bounce("No function name");
            cp2 = fname;
            while (isidchar(*cp))
                *cp2++= *cp++;
            *cp2 = 0;
            dprintf("fname is %s\n", fname);     /***/
            dprintf("cp after fname is %s\n", cp);     /***/
            while (isspace(*cp)) cp++;
            if (*cp++ != '(')
                bounce("No function '('");
            nargs = 0;
            if (!*cp) {
                readline(buf, inf);
                cp = buf;
            }
            while (isspace(*cp)) cp++;
            while (*cp != ')') {
                if (!isidchar(*cp))
                    bounce("Missing argument name");
                if (nargs >= MAXARGS)
                    bounce("Too many arguments");
                cp2 = argnames[nargs];
                argdecls[nargs][0] = 0;
                nargs++;
                while (isidchar(*cp))
                    *cp2++ = *cp++;
                *cp2 = 0;
                dprintf("Argument %d is named %s\n", nargs-1, argnames[nargs-1]);    /***/
                while (isspace(*cp)) cp++;
                if (*cp == ',') {
                    cp++;
                    if (!*cp) {
                        readline(buf, inf);
                        cp = buf;
                    }
                    while (isspace(*cp)) cp++;
                } else if (*cp != ')')
                    bounce("Missing function ')'");
            }
            if (cp[1])
                bounce("Characters after function ')'");
            readline(buf, inf);
            cp = buf;
            for (;;) {
                while (isspace(*cp)) cp++;
                if (isidchar(*cp)) {
                    cp2 = dtype;
                    if (strbeginsword(cp, "register")) {
                        cp += 8;
                        while (isspace(*cp)) cp++;
                    }
                    while (isspace(*cp) || isidchar(*cp))
                        *cp2++ = *cp++;
                    if (*cp == ',' || *cp == ';' || *cp == '[') {
                        while (cp2 > dtype && isspace(cp2[-1])) cp--, cp2--;
                        while (cp2 > dtype && isidchar(cp2[-1])) cp--, cp2--;
                    } else if (*cp != '(' && *cp != '*')
                        bounce("Strange character in arg decl");
                    while (cp2 > dtype && isspace(cp2[-1])) cp2--;
                    *cp2 = 0;
                    if (!*dtype)
                        bounce("Empty argument type");
                    for (;;) {
                        cp2 = decl;
                        cp3 = dname;
                        while (*cp == '*' || *cp == '(' || isspace(*cp))
                            *cp2++ = *cp++;
                        if (!isidchar(*cp))
                            bounce("Missing arg decl name");
                        while (isidchar(*cp)) {
                            if (usenames)
                                *cp2++ = *cp;
                            *cp3++ = *cp++;
                        }
                        if (!usenames) {
                            while (cp2 > decl && isspace(cp2[-1])) cp2--;
                            while (isspace(*cp)) cp++;
                        }
                        i = 0;
                        while (*cp && *cp != ';' && (*cp != ',' || i > 0)) {
                            if (*cp == '(' || *cp == '[') i++;
                            if (*cp == ')' || *cp == ']') i--;
                            *cp2++ = *cp++;
                        }
                        *cp2 = 0;
                        *cp3 = 0;
                        dprintf("Argument %s is %s\n", dname, decl);     /***/
                        if (i > 0)
                            bounce("Unbalanced parens in arg decl");
                        if (!*cp)
                            bounce("Missing ';' or ',' in arg decl");
                        for (i = 0; i < nargs && strcmp(argnames[i], dname); i++) ;
                        if (i >= nargs)
                            bounce("Arg decl name not in argument list");
                        if (*decl)
                            sprintf(argdecls[i], "%s %s", dtype, decl);
                        else
                            strcpy(argdecls[i], dtype);
                        if (*cp == ',') {
                            cp++;
                            if (!*cp) {
                                readline(buf, inf);
                                cp = buf;
                            }
                            while (isspace(*cp)) cp++;
                        } else
                            break;
                    }
                    cp++;
                    if (!*cp) {
                        readline(buf, inf);
                        cp = buf;
                    }
                } else
                    break;
            }
            if (*cp != '{')
                bounce("Missing function '{'");
            if (firstdecl) {
                firstdecl = 0;
                if (argc > 2)
                    fprintf(outf, "\n/* Declarations from %s */\n", argv[whichf]);
                if (useifdefs && inf != stdin) {
                    strcpy(ifdefname, argv[whichf]);
		    cp = ifdefname;
		    for (cp2 = ifdefname; *cp2; ) {
			if (*cp2++ == '/')
			    cp = cp2;
		    }
                    for (cp2 = ifdefname; *cp; cp++, cp2++) {
		        if (islower(*cp))
			    *cp2 = toupper(*cp);
                        else if (isalnum(*cp))
                            *cp2 = *cp;
                        else
                            *cp2 = '_';
                    }
                    fprintf(outf, "#ifdef PROTO_%s\n", ifdefname);
                }
            }
            for (i = 0; i < nargs; i++) {
                if (!argdecls[i][0])
                    sprintf(argdecls[i], "int %s", argnames[i]);
                for (j = 0; warntypes[j].bad &&
                            !strbeginsword(argdecls[i], warntypes[j].bad); j++) ;
                if (warntypes[j].bad) {
                    cp = argdecls[i];
                    while (isspace(*cp) || isidchar(*cp)) cp++;
                    if (!*cp) {     /* not, e.g., "char *" */
                        sprintf(temp, "%s%s", warntypes[j].good,
                                              argdecls[i] + strlen(warntypes[j].bad));
                        strcpy(argdecls[i], temp);
                        fprintf(stderr, "Warning: Argument %s of %s has type %s\n",
                                        argnames[i], fname, warntypes[j].bad);
                    }
                }
            }
            if (verbose && outf != stdout)
                printf("Found declaration for %s\n", fname);
            fprintf(outf, "%s", ftype);
            pos = strlen(ftype) + numstars;
            do {
                putc(' ', outf);
                pos++;
            } while (pos < typetab);
            for (i = 1; i <= numstars; i++)
                putc('*', outf);
            fprintf(outf, "%s", fname);
            pos += strlen(fname);
            do {
                putc(' ', outf);
                pos++;
            } while (pos < argtab);
            if (nargs == 0) {
                if (usemacros)
                    fprintf(outf, "PV();");
                else
                    fprintf(outf, "(void);");
            } else {
                if (usemacros)
                    fprintf(outf, "PP( ("), pos += 5;
                else
                    fprintf(outf, "("), pos++;
                thistab = pos;
                for (i = 0; i < nargs; i++) {
                    len = strlen(argdecls[i]);
                    if (i > 0) {
                        putc(',', outf);
                        pos++;
                        if (pos > thistab && pos + len >= width) {
                            putc('\n', outf);
                            for (j = 1; j <= thistab; j++)
                                putc(' ', outf);
                            pos = thistab;
                        } else {
                            putc(' ', outf);
                            pos++;
                        }
                    }
                    fprintf(outf, "%s", argdecls[i]);
                    pos += len;
                }
                if (usemacros)
                    fprintf(outf, ") );");
                else
                    fprintf(outf, ");");
            }
            putc('\n', outf);
Lbounce: ;
        }
        if (inf != stdin) {
            if (useifdefs && !firstdecl)
                fprintf(outf, "#endif /*PROTO_%s*/\n", ifdefname);
            fclose(inf);
        }
    }
    if (hasheader) {
        fprintf(outf, "\n\n/* End. */\n\n");
    }
    if (outf != stdout)
        fclose(outf);
    if (errors)
        exit(1);
    else
        exit(0);
}



/* End. */



