Last Update: Dec.2,2006

C Source Library

「LSI C-86 Ver.3.30c 試食版」、「Borland C++ 5.5」、「Microsoft Visual C++ 6.0」でコンパイル出来ます。

■ C/C++ ソースを HTML 化する

/*
 *  c2htmlx.c : C/C++ ソースを HTML 化する
 *
 *      Copyright (c) 2001, 2005, 2006 AOK. 
 *  
 *      2001/10/05 - 初版
 *      2001/10/14 - 2 版
 *      2005/10/19 - 3 版
 *      2006/11/03 - 4 版(css)
 *      2006/11/29 - 5 版
 *      2006/12/02 - 6 版(C++ 拡張)
 *
 *      使い方: C2HTMLX ファイル名
 *  
 *  出力される HTML ファイル名は内部で生成される(.c → .htm)
 *  同名のファイルがあると上書きされてしまう(^-^;)
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef LSI_C /* Borland C++ 5.5 */
    #include <mbctype.h>
    #include <mbstring.h>
    #define chkctype(x, y)  _mbbtype(x, y)
    #define CT_ANK          _MBC_SINGLE
    #define CT_KJ1          _MBC_LEAD
    #define CT_KJ2          _MBC_TRAIL
#else /* LSI C-86 */
    #include <jctype.h>
#endif

int tabw = 4;   /* タブの幅 */
const char *tag_com = "<span class='color_comment'>";
const char *tag_key = "<span class='color_keyword'>";
const char *tag_str = "<span class='color_string'>";
const char *tag_end = "</span>";

int col = -1, error = 0;
char chbuf;
FILE *fin, *fout;

typedef int (*FPTR)(const void*, const void*);

#define BUFLEN  127
#define NUM_OF_ARR(x)   (sizeof(x)/sizeof(x[0]))
    
const char key[][17] = {  /* 昇順に並べておく */
    "#define","#elif","#else","#endif","#error",
    "#if","#ifdef","#ifndef","#import","#include",
    "#line","#pragma","#undef","#using","auto",
    "bool","break","case","catch","char",
    "class","const","const_cast","continue","default",
    "delete","do","double","dynamic_cast","else",
    "enum","explicit","export","extern","false",
    "float","for","friend","goto","if",
    "inline","int","long","mutable","namespace",
    "new","operator","private","protected","public",
    "register","reinterpret_cast","return","short","signed",
    "sizeof","static","static_cast","struct","switch",
    "template","this","throw","true","try",
    "typedef","typeid","typename","union","unsigned",
    "using","virtual","void","volatile","wchar_t",
    "while" };

/* 1文字取ってくる */
int getnext(void)
{
    int n;
    if ((n = getc(fin)) == EOF)
    {
        error++;
        return 0;
    }
    chbuf = (char)n;
    col++;
    return 1;
}

/* 1文字戻す */
int ungetnext(char c)
{
    if (ungetc(c, fin) == EOF)
    {
        error++;
        return 0;
    }
    if (--col < 0) col = tabw - 1;
    return 1;
}

/* 予約語かどうか(2分探索) */
int check_keyword(char *c)
{
    return (bsearch(c, key, NUM_OF_ARR(key),
                    sizeof(key[0]), (FPTR)strcmp) != NULL);
}

/* バッファへ1文字入れる */
void input_buffer(char buf[], char c)
{
    int len;
    len = strlen(buf);
    buf[len] = c;
    buf[len+1] = '\0';
}

/* タブをスペースに変換 */
void fillspace(void)
{
    int num;
    num = tabw - col % tabw;
    /* printf("(%2ld,%2ld) ", col, num); */
    while (num-- > 0) putc(' ', fout);
    col = -1;
}

/* 標準の書き出し */
void normal(void)
{
    switch (chbuf)
    {
    case '\t': fillspace(); break;
    case '>': fputs("&gt;", fout); break; 
    case '<': fputs("&lt;", fout); break; 
    case '&': fputs("&amp;", fout); break;
    case '\"': fputs("&quot;", fout); break;
    case '\n': col = -1;
    default: putc(chbuf, fout); break;
    }
}

/* コメント文 */
void comment(void)
{
    fprintf(fout, "%s/*", tag_com);
    while (getnext())
    {
        if (chbuf == '*')
        {
            putc(chbuf, fout);
            if (!getnext()) return;
            if (chbuf == '/')
            {
                fprintf(fout, "/%s", tag_end);
                return;
            }
            else if (chbuf == '*')
                ungetnext(chbuf);
            else
                normal();
        }
        else normal();
    }
}

void comment_cpp(void)
{
    fprintf(fout, "%s//", tag_com);
    while (getnext())
    {
        if (chbuf == '\n')
        {
            fprintf(fout, "%s", tag_end);
            normal();
            return;
        }
        else normal();
    }
}

/* 文字列定数 */
void string(void)
{
    int escape = 0, mode = CT_ANK;
    fprintf(fout, "%s&quot;", tag_str);
    while (getnext())
    {
        mode = chkctype(chbuf, mode);
        if (mode == CT_KJ1 || mode == CT_KJ2)
        {
            escape = 0;
            putc(chbuf, fout);
        }
        else
        {
            if (escape == 0 && chbuf == '\"')
            {
                fprintf(fout, "&quot;%s", tag_end);
                return;
            }
            else if (chbuf == '\\')
            {
                escape = (escape == 0 ? 1 : 0);
                putc(chbuf, fout);
            }
            else
            {
                escape = 0;
                normal();
            }
        }
    }
}

/* 文字定数 */
void character(void)
{
    int escape = 0;
    fprintf(fout, "%s'", tag_str);
    while (getnext())
    {
        if (escape == 0 && chbuf == '\'')
        {
            fprintf(fout, "'%s", tag_end);
            return;
        }
        else if (chbuf == '\\')
        {
            escape = (escape == 0 ? 1 : 0);
            putc(chbuf, fout);
        }
        else
        {
            escape = 0;
            normal();
        }
    }
}

/* 予約語を探す */
void symbol(void)
{
    char buf[BUFLEN+1];
    buf[0] = '\0';
    input_buffer(buf, chbuf);
    if (!getnext())
    {
        fputs(buf, fout);
        return;
    }
    while (isalnum(chbuf) || chbuf == '_' || chbuf=='.')
                        /* '.' は構造体や float.h 対策 */
    {
        input_buffer(buf, chbuf);
        if (!getnext()) break;
    }
    if (check_keyword(buf))
    {
        fprintf(fout, "%s%s%s", tag_key, buf, tag_end);
        /* printf("%s, ", buf); */
    }
    else fputs(buf, fout);  
    if (!error) ungetnext(chbuf);
}

/* 最初の文字を判定して処理を分岐する */
void getsym(void)
{
    if (!getnext()) return;
    if (chbuf == '/')
    {
        if (!getnext())
        {
            putc('/', fout);
            return;
        }
        if (chbuf == '*')
            comment();
        else if (chbuf == '/')
            comment_cpp();
        else
        {
            putc('/', fout);
            ungetnext(chbuf);
        }
    }
    else if (chbuf == '\"') string();
    else if (chbuf == '\'') character();
    else if (isalnum(chbuf) || chbuf == '_' || chbuf == '#') symbol();
    else normal();
}

/* 拡張子の付け替え */
int change_ext(char *fname)
{
    char *p = strrchr(strlwr(fname), '.');
    if (p != NULL && (strcmp(p, ".c") == 0 ||
        strcmp(p, ".cpp") == 0 || strcmp(p, ".h") == 0))
    {
        strcpy(p, ".htm");
        return 1;
    }
    puts("入力ファイルは C/C++ ソースではありません.");
    return 0;
}

/* プログラムのエントリポイント */
int main(int argc, char *argv[])
{
    char fname[FILENAME_MAX+1];
    
    /* コマンドライン引き数のチェック */
    if (argc >= 2)
    {
        strncpy(fname, argv[1], FILENAME_MAX);
        fname[FILENAME_MAX] = '\0';
    }
    else
    {
        puts("パラメータが足りません.\n\n使い方: C2HTMLX ファイル名");
        exit(EXIT_FAILURE);
    }

    /* 入出力ファイルをオープン */
    printf("入力ファイル名: %s\n", fname);
    if ((fin = fopen(fname, "r")) == NULL)
    {
        perror("入力ファイルをオープン出来ません.\n");
        exit(EXIT_FAILURE);
    }

    if (change_ext(fname) == 0) /* 拡張子の付け替え */
    {
        fclose(fin);
        exit(EXIT_FAILURE);
    }

    printf("出力ファイル名: %s\n", fname);
    if ((fout = fopen(fname, "w")) == NULL)
    {
        perror("出力ファイルをオープン出来ません.\n");
        fclose(fin);
        exit(EXIT_FAILURE);
    }

    /* 変換実行 */
    fputs("<html>\n<head>\n", fout);
    fputs("<meta http-equiv=\"Content-Type\"\n", fout);
    fputs("content=\"text/html;charset=Shift_JIS\">\n", fout);
    fputs("<meta name=\"GENERATOR\" content=\"C2HTMLX (c)2001, 2006 AOK\">\n", fout);
    fprintf(fout, "<title>%s</title>\n", argv[1]);
    fputs("<style type=\"text/css\">\n", fout);
    fputs("<!--\n", fout);
    fputs("span.color_comment { color:#c00000; }\n", fout);
    fputs("span.color_string { color:teal; }\n", fout);
    fputs("span.color_keyword { color:blue; }\n", fout);
    fputs("-->\n", fout);
    fputs("</style>\n", fout);
    fputs("</head>\n<body>\n<pre>", fout);
    while (!error) getsym(); /* 変換の本体 */
    fputs("</pre>\n</body>\n</html>", fout);

    /* 入出力ファイルをクローズ */
    fclose(fin);
    fclose(fout);
    
    puts("\n*** 変換に成功しました.");

    return 0;
}