Blob Blame History Raw
/*
 * ins_once.c
 *  Quick and dirty program to insert '#pragma once' into the header files.
 *  1998/08     kmatsui
 *  2002/08     kmatsui
 *      Added -p, -o, -g option, removed -s option.
 *      Compile with -DPATH_DELIM='x' option, if the path-delimiter is any
 *          other than '/'.
 *  2004/11     kmatsui
 *      Changed '#pragma __once' to '#pragma once'.
 *  2006/07     kmatsui
 *      Removed -o option.
 *      Changed non-prototype declarations to prototype ones.
 */

#include    "stdio.h"
#include    "stdlib.h"
#include    "string.h"
#include    "ctype.h"
#include    "errno.h"

#ifndef PATH_DELIM
#define PATH_DELIM  '/'
#endif

#define TRUE    1
#define FALSE   0

#define IFNDEF      0x100
#define IF          0x101
#define PRAGMA      0x102
#define DEFINED     0x103
#define _ONCE       0x111

void    usage( void);
void    test_a_file( char *);
void    conv_a_file( char *);
void    insert_once( FILE *, FILE *, char *);
void    prepend_once( FILE *, FILE *);
int     look_directive( FILE *);
int     get_token( char **);
void    ins_once( FILE *);

int     test;
    /* Only test files whether beginning with #ifndef or #if !defined.  */
int     prepend;
/*
 * If TRUE, prepend '#pragma once' line to the file.
 * If FALSE, insert the line after the first #ifndef line.
 */
int     gcc;
/*
 * If TRUE, do not insert '#pragma once' line to "stddef.h".
 * This is the option for GCC family.
 */

int main( int argc, char **argv) {
    extern int      getopt( int, char **, char *);
    extern int      optind;
    extern char     *optarg;
    int     opt;
    char    *except[] = { "assert.h", "cassert", "cassert.h", NULL};
    char    *g_except[] = { "stddef.h", "stdio.h", "signal.h", "errno.h"
                    , NULL};
    char    *arg;
    char    **ep;

    if (argc < 2)
        usage();
    while (optind < argc
            && (opt = getopt( argc, argv, "gopt")) != EOF) {
        switch (opt) {
        case 't':
            test = TRUE;
            break;
        case 'p':
            prepend = TRUE;
            break;
        case 'g':
            gcc = TRUE;
            break;
        default:
            usage();
            break;
        }
    }
    argv += (optind - 1);
    while (*++argv) {
        if ((arg = strrchr( *argv, PATH_DELIM)) != NULL)
            arg++;
        else
            arg = *argv;
        for (ep = except; *ep; ep++) {
            if (strcmp( arg, *ep) == 0)
                goto skip;
        }
        if (gcc) {
            for (ep = g_except; *ep; ep++) {
                if (strcmp( arg, *ep) == 0)
                    goto skip;
            }
        }
        if (test)
            test_a_file( *argv);
        else
            conv_a_file( *argv);
        continue;
skip:   fprintf( stderr, "Skipped %s\n", *ep);
    }
    return 0;
}

void    usage( void)
{
    static char     *mes[] = {
   "ins_once: Insert '#pragma once' to header files except \"assert.h\"\n",
   "            and \"stddef.h\" and some others (for GCC).\n",
   "Usage: ins_once [-DPATH_DELIM=\\] [-t|-p|-g] [header1.h [header2.h [...]]]\n",
   "    -t : Only test files whether beginning with #ifndef or #if !defined.\n",
   "    -p : Prepend the line to the file\n",
   "        (default: insert after the first #ifndef line -- for GCC).\n",
   "    -g : Do not convert stddef.h, stdio.h, signal.h, errno.h.\n",
        NULL,
    };
    char    **mesp = mes;

    while( *mesp)
        fputs( *mesp++, stderr);
    if (errno) {
        fputs( strerror( errno), stderr);
        fputc( '\n', stderr);
    }
    exit( errno);
}

void    test_a_file( char *fname) {
/*
 * Only test the file whether it begins with #ifndef or #if !defined.
 */
    char    buf[ BUFSIZ];
    FILE    *fp_in;
    int     token;
    char    *cp;

    fp_in = fopen( fname, "r");
    if (fp_in == NULL)
        usage();

    while (fgets( buf, BUFSIZ, fp_in) != NULL) {
        cp = buf;
        if (get_token( &cp) == '#') {       /* The first directive  */
            if (((token = get_token( &cp)) != IFNDEF)   /* #ifndef  */
                    && (token != IF || get_token( &cp) != '!'
                        || get_token( &cp) != DEFINED)) {   /* #if ! defined*/
                fputs( fname, stderr);
                fputs( ": doesn't begin with #ifndef nor #if !defined\n",
                        stderr);
            }
            break;
        }
    }
    fclose( fp_in);
}

void    conv_a_file( char *fname) {
/*
 * Insert '#pragma once' line to seemingly apropriate place according
 * the command-line options.
 */
    char    *tmp = "tmp_once";
    FILE    *fp_in, *fp_out;

    if ((fp_in = fopen( fname, "r")) == NULL)
        usage();
    if ((fp_out = fopen( tmp, "w")) == NULL)
        usage();
    fprintf( stderr, "Converting %s\n", fname);

    if (prepend)
        prepend_once( fp_in, fp_out);
    else
        insert_once( fp_in, fp_out, fname);

    fclose( fp_in);
    fclose( fp_out);
    if (remove( fname) != 0 || rename( tmp, fname) != 0)
        usage();
}

void    insert_once( FILE *fp_in, FILE *fp_out, char *fname) {
/*
 * Insert '#pragma once' line after the first directive line, if the
 * directive is #ifndef or #if !defined, else append the line at the end
 * of the file.
 */
    char    buf[ BUFSIZ];
    int     token;
    int     no_ifndef = TRUE;
    char    *cp;

    while (fgets( buf, BUFSIZ, fp_in) != NULL) {
        fputs( buf, fp_out);
        cp = buf;
        if (get_token( &cp) == '#') {       /* The first directive  */
            if (((token = get_token( &cp)) == IFNDEF)   /* #ifndef  */
                    || (token == IF && get_token( &cp) == '!'
                        && get_token( &cp) == DEFINED)) {   /* #if ! defined*/
                no_ifndef = FALSE;
                if (! look_directive( fp_in))
                    ins_once( fp_out);
                /* Else already written in   */
            } else {                            /* Other directive  */
                fputs( fname, stderr);
                fputs( ": doesn't begin with #ifndef nor #if !defined\n",
                        stderr);
            }
            break;
        }
    }
    while (fgets( buf, BUFSIZ, fp_in) != NULL)
        fputs( buf, fp_out);
    if (no_ifndef)
        ins_once( fp_out);          /* Append the line to the file  */
}

void    prepend_once( FILE *fp_in, FILE *fp_out) {
/*
 * Prepend the '#pragma once' line at the top of the file.
 */
    char    buf[ BUFSIZ];

    if (! look_directive( fp_in))
        ins_once( fp_out);          /* Prepend the line to the file */

    while (fgets( buf, BUFSIZ, fp_in) != NULL)
        fputs( buf, fp_out);
}

int     look_directive( FILE *fp) {
/*
 * Look whether the next line is '#pragma once'.
 */
    char    buf[ BUFSIZ];
    long    pos;
    int     res = 0;
    int     token;
    char    *cp;

    pos = ftell( fp);
    cp = buf;
    if (fgets( buf, BUFSIZ, fp) && buf[ 0] == '\n'
            && fgets( buf, BUFSIZ, fp) && get_token( &cp) == '#'
            && get_token( &cp) == PRAGMA && get_token( &cp) == _ONCE)
        res = 1;
    fseek( fp, pos, SEEK_SET);
    return res;
}

int     get_token( char **cpp) {
/* Get the next 'token', without parsing comments, literals, etc.   */
    int     token;
    char    *cp = *cpp;

    while (*cp != '\n' && isspace( *cp))
        cp++;
    if (memcmp( cp, "ifndef", 6) == 0) {
        token = IFNDEF;
        cp += 6;
    } else if (memcmp( cp, "if", 2) == 0) {
        token = IF;
        cp += 2;
    } else if (memcmp( cp, "pragma", 6) == 0) {
        token = PRAGMA;
        cp += 6;
    } else if (memcmp( cp, "defined", 7) == 0) {
        token = DEFINED;
        cp += 7;
    } else if (memcmp( cp, "once", 6) == 0) {
        token = _ONCE;
        cp += 6;
    } else {
        token = *cp++;
    }
    *cpp = cp;
    return token;
}

void    ins_once( FILE *fp) {
    fputs( "\n#pragma once\n\n", fp);
}