//----------------------------------------------------------------------------------------------------
// cpp2html.cpp: c/c++ source file to HTML source converter
//
// USAGE: cpp2html srcfile [-disp] [-plain] [-out htmlfile] [-t n] [-no]
//  option:
//    -disp   output to display (default _cpp.html file).
//    -plain  do not display HTML header and footer.
//    -t n    tab order (insert n spaces instead of a tab
//    -out htmlfile output filename
//----------------------------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <ctype.h>
#include "cpp2html.h"

//----------------------------------------------------------------------------------------------------
// C/C++, HTML 用キーワード
//----------------------------------------------------------------------------------------------------
static const char* c2hprog = "cpp2html v1.0";

// 特殊領域の判定[通常,文字列,コメント]
enum STR_STATE {
    STR_NORMAL      = 0,
    STR_CHAR        = 1,
    STR_STRING      = 2,
    STR_CCOMMENT    = 3,
    STR_CPPCOMMENT  = 4
};
static const char* html_sym[] = {
    "&amp;",
    "&lt;",
    "&gt;",
    "&quot;",
    0
};
static const char* cpp_pp[]  = {
    "#if",
    "#ifdef",
    "#ifndef",
    "#endif",
    "#else",
    "#elif",
    "#undef",
    "#define",
    "#pragma",
    0
};
static const char* cpp_kwd[] = {
    "and",
    "and_eq",
    "auto",
    "bitand",
    "bitor",
    "bool",
    "break",
    "case",
    "catch",
    "char",
    "class",
    "compl",
    "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",
    "not",
    "not_eq",
    "operator",
    "or",
    "or_eq",
    "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",
    "xor",
    "xor_eq",
    0
};
//----------------------------------------------------------------------------------------------------
// main 関数として使用時
//----------------------------------------------------------------------------------------------------
#if (USE_LIB==0)
int main(int argc, char* argv[])
{
    Cpp2Html* c2h = new Cpp2Html(argc,argv);
    if ( !c2h->ReadCode() ) return 0;
    if ( c2h->Convert() ) c2h->WriteCode();
    delete c2h;
    return 0;
}
#endif

//----------------------------------------------------------------------------------------------------
// 一般関数
//----------------------------------------------------------------------------------------------------
// パス名の分割
void split_path(char* fpath, char* dir, char* file, char* ext)
{
    int i,len = strlen(fpath);
    // file and ext
    for ( i=len-1; i>=0; i-- ) if ( fpath[i] == '.' ) break;
    strncpy(ext,fpath+i,len-i);
    strncpy(file,fpath,i);
    ext[len-i] = '\0';
    file[i] = '\0';
    // dir
    len = i;
    for ( i=len-1; i>=0; i-- ) if ( file[i] == '/' ) break;
    i++;
    strncpy(dir,file,i);
    strncpy(file,file+i,len-i);
    dir[i] = '\0';
    file[len-i] = '\0';
}
// ファイル中の文字数[タブ数,行数]のカウント
void filestr_count(char* fname, int& sum_sc, int& sum_lc, int& sum_tc)
{
    FILE* fp;
    int rsc;
    char buf[BUFSIZ];
    memset(buf,0,BUFSIZ);
    sum_sc = sum_lc = sum_tc = 0;
    if ( (fp=fopen(fname,"r")) == NULL ) {
        fprintf(stderr,"read file[%s]: No such file or directory\n",fname);
        return;
    }
    while ( (rsc=fread(buf,sizeof(char),BUFSIZ,fp)) ) {
        for ( int i=0; i<rsc; i++ ) {
            if ( buf[i] == '\n' ) sum_lc ++;
            if ( buf[i] == 't' )  sum_tc ++;
        }
        sum_sc += rsc;
    }
    sum_sc += rsc;
    fclose(fp);
}
// 配列中の文字数[タブ数,行数]のカウント
void str_count(char* code, int& sum_sc, int& sum_lc, int& sum_tc)
{
     sum_sc = sum_lc = sum_tc = 0;
    sum_sc = strlen(code);
    for ( int i=0; i<sum_sc; i++ ) {
        if ( code[i] == '\n' ) sum_lc ++;
        if ( code[i] == 't' )  sum_tc ++;
    }
}
// 入力ファイルから文字列の取得
int read_string(char* fname, int read_sc, char* inbuf)
{
    FILE* fp;
    int rsc;
    if ( (fp=fopen(fname,"r")) == NULL ) {
        perror("read file open");
        return 0;
    }
    rsc = fread(inbuf,sizeof(char),read_sc,fp);
    if ( rsc != read_sc ) perror("read size");
    fclose(fp);
    return 1;
}
// qsort 用(昇順)
int kwd_compare(const void *x, const void *y) {
    return (strlen(*(char **)y) - strlen(*(char **)x));
}
// キーワードの検索
int kwd_sch(char* code, int str_len, int ki, const char* kwd)
{
    int i,klen = strlen(kwd);
    if ( ki+klen > str_len ) return 0;
    for ( i=0; i<klen; i++ ) {
        if ( code[ki+i] != kwd[i]) break;
    }
    if ( i == klen ) return 1;
    return 0;
}
// キーワードの検索[大文字・小文字を区別しない]
int Kwd_sch(char* code, int str_len, int ki, char* kwd)
{
    int i,klen = strlen(kwd);
    if ( ki+klen > str_len ) return 0;
    for ( i=0; i<klen; i++ ) {
        if ( (code[ki+i]) != kwd[i] && (code[ki+i]|32) != kwd[i] ) break;
    }
    if ( i == klen ) return 1;
    return 0;
}
// 1行中から目的の文字を検索
int line_schchar(char* c_code, int str_len, int ki, char fc)
{
    int i;
    unsigned char c;
    for ( i=0; ki+i<str_len; i++ ) {
        c = c_code[ki+i];
        if ( c == fc )   break;
        if ( c == '\n' ) return -1;
    }
    return i;
}
// 1行中から目的の文字列を検索
int line_schstr(char* c_code, int str_len, int ki, const char* kwd)
{
    for ( int i=0; ki+i<str_len; i++ ) {
        if ( kwd_sch(c_code,str_len,ki+i,kwd) ) return i;
        if ( c_code[ki+i] == '\n' ) return -1;
    }
    return -1;
}

//----------------------------------------------------------------------------------------------------
//
// Cpp2Html Class:
//   convert C/C++ source to HTML document
//
//----------------------------------------------------------------------------------------------------
// コマンドヘルプ
void Cpp2Html::usage()
{
    fprintf(stderr,"USAGE: %s srcfile [-disp] [-plain] [-out htmlfile] [-t n] [-no]\n",cmdopt.cmd);
    fprintf(stderr,"option:\n");
    fprintf(stderr,"\t-disp \toutput to display (default _cpp.html file).\n");
    fprintf(stderr,"\t-plain\tdo not display HTML header and footer.\n");
    fprintf(stderr,"\t-t n  \ttab order (insert n spaces instead of a tab)\n");
    fprintf(stderr,"\t-out htmlfile\toutput filename\n");
    fprintf(stderr,"\t-no\tdisplay line number\n");
}

// プログラムの引数の読取[FALSE:失敗,TRUE:正常]
int Cpp2Html::ReadArg(int argc, char* argv[], CSrcOption& cmdopt)
{
    // 引数の解釈
    strcpy(cmdopt.cmd,argv[0]);
#ifdef __DEBUG__
    strcpy(cmdopt.fIn,"cpp2html.cpp");
#else
    if ( argc == 1 ) { return FALSE; }
#endif
    for ( int i=1; i<argc; i++ ) {
        if ( argv[i][0] == '-' ) {
            if ( !strcmp(argv[i], "-out") ) {
                if ( ++i >= argc ) return FALSE;
                strcpy(cmdopt.fOut,argv[i]);
            } else if ( !strcmp(argv[i], "-plain") ) {
                if ( i >= argc )   return FALSE;
                cmdopt.Hfmt   = FMT_PLAIN;
            } else if ( !strcmp(argv[i], "-disp") ) {
                if ( i >= argc )   return FALSE;
                cmdopt.Hdst   = DST_DISP;
            } else if ( !strcmp(argv[i], "-t") ) {
                if ( ++i >= argc ) return FALSE;
                cmdopt.t2s    = atoi(argv[i]);
            } else if ( !strcmp(argv[i], "-no") ) {
                if ( i >= argc )   return FALSE;
                cmdopt.swLine = SW_LINE;
            } else {
                fprintf(stderr,"unrecognized option '%s'\n",argv[i]);
                return FALSE;
            }
        } else {
            strcpy(cmdopt.fIn,argv[i]);
        }
    }
    return TRUE;
}

// コンストラクタ: コマンド引数より変換のオプションの読込み
Cpp2Html::Cpp2Html(int argc, char* argv[])
{
    Run = ReadArg(argc,argv,cmdopt);
    if ( Run == FALSE ) { usage(); return; }
    InitClass();
}

// コンストラクタ: オプションの受け渡し
Cpp2Html::Cpp2Html(CSrcOption opt)
{
    Run = TRUE;
    cmdopt = opt;
    InitClass();
}

// コンストラクタ: 全引数の指定
Cpp2Html::Cpp2Html(int Hfmt, int t2s, int swLine)
{
    Run           = TRUE;
    cmdopt.Hfmt   = Hfmt;
    cmdopt.t2s    = t2s;
    cmdopt.swLine = swLine;
    InitClass();
}
// デストラクタ
Cpp2Html::~Cpp2Html()
{
    if ( Run ) {
        if ( Ccode != NULL ) free(Ccode);
        if ( Hcode != NULL ) free(Hcode);
    }
}

// スタイルの変更
void Cpp2Html::SetBodyColor(int color, int bgcolor)
{
    colopt.setBodyColor(color,bgcolor);
}

void Cpp2Html::SetFontFormat(FONT_TYPE type, int color, int weight, int style)
{
    colopt.setFontFormat(type,color,weight,style);
}

// クラスデータの初期化
void Cpp2Html::InitClass()
{
    // 出力ファイル名の設定
    char dir[128],file[128],ext[10];
    if ( strcmp(cmdopt.fIn,std_in) ) {
        split_path(cmdopt.fIn,dir,file,ext);
        sprintf(DocTitle,"%s%s",file,ext);
        if ( !strcmp(cmdopt.fOut,std_out) ) {
            if ( cmdopt.Hdst == DST_FILE ) {
                ext[0] = '_';
                sprintf(cmdopt.fOut,"%s%s.html",file,ext);
            }
        }
    } else {
        sprintf(DocTitle,"cpp2html");
    }
    // タブの変換配列の準備
    memset(tabstr,0,sizeof(char)*256);
    for ( int i=0; i<cmdopt.t2s; i++ ) strcat(tabstr,"&nbsp;");
    // キーワードのソート
    SortKwd();
    // 変数初期化
    Ccode = Hcode = NULL;
    OFP = NULL;
    // フッタコードの生成
    sprintf(FootCode,"</body>\n</html>\n");
}

// C/C++キーワードのソート[昇順に並べ替え]
void Cpp2Html::SortKwd()
{
    int ki;
    for ( ki=0; cpp_pp[ki] != 0; ki++ ) pp_table[ki] = (char*)cpp_pp[ki];
    qsort(pp_table,ki,sizeof(char*),kwd_compare);
    for ( int i=0; i<ki; i++ ) pp_len[i] = strlen(pp_table[i]);
    pp_len[ki] = 0;
    
    for ( ki=0; cpp_kwd[ki] != 0; ki++ ) kwd_table[ki] = (char*)cpp_kwd[ki];
    qsort(kwd_table,ki,sizeof(char*),kwd_compare);
    for ( int i=0; i<ki; i++ ) kwd_len[i] = strlen(kwd_table[i]);
    kwd_len[ki] = 0;
}

// ソースコードの読込み[配列]
void Cpp2Html::SetCode(char* src)
{
    // 入力ファイルの文字数取得
    lineSum = tabSum = fcLen = 0;
    str_count(src,fcLen,lineSum,tabSum);
    // 文字配列の確保
    Ccode = (char*)calloc(fcLen+1,sizeof(char));
    strcpy(Ccode,src);
}

// ソースコードの読込み[ファイル]
int Cpp2Html::ReadCode()
{
    if ( Run == FALSE ) return FALSE;
    // 入力ファイルの文字数取得
    lineSum = tabSum = fcLen = 0;
    filestr_count(cmdopt.fIn,fcLen,lineSum,tabSum);
    if ( fcLen == 0 ) {
        return FALSE;
    }
    // 文字配列の確保
    Ccode = (char*)calloc(fcLen+1,sizeof(char));
    // 入力ファイルから文字列の取得
    if ( !read_string(cmdopt.fIn,fcLen,Ccode) ) {
        fprintf(stderr,"input file reading error.\n");
        return FALSE;
    }
    return TRUE;
}

// 指定されたファイルからの読込み
int Cpp2Html::ReadFile(char* fname)
{
    if ( Run == FALSE ) return FALSE;
    // 入力ファイルの設定
    strcpy(cmdopt.fIn,fname);
    // 出力ファイルの設定
    char dir[128],file[128],ext[10];
    split_path(cmdopt.fIn,dir,file,ext);
    sprintf(DocTitle,"%s%s",file,ext);
    ext[0] = '_';
    sprintf(cmdopt.fOut,"%s%s.html",file,ext);
    // 入力ファイルの文字数取得
    lineSum = tabSum = fcLen = 0;
    filestr_count(cmdopt.fIn,fcLen,lineSum,tabSum);
    if ( fcLen == 0 ) return FALSE;
    // 文字配列の確保
    Ccode = (char*)calloc(fcLen+1,sizeof(char));
    // 入力ファイルから文字列の取得
    if ( !read_string(cmdopt.fIn,fcLen,Ccode) ) {
        fprintf(stderr,"input file reading error.\n");
        return FALSE;
    }
    return TRUE;
}

// 変換実行
int Cpp2Html::Convert()
{
    if ( Run == FALSE ) return FALSE;

    // ヘッダコードの生成
    sprintf(HeadCode,
            "<html>\n"
            "<head>\n"
            "<meta http-equiv=\"Content-Type\" content=\"text/html\">\n"
            "<meta http-equiv=\"Content-Style-Type\" content=\"text/css\">\n"
            "<title>%s</title>\n"
            "<style type=\"text/css\">\n"
            "<!--\n"
            "body {%s}\n"
            "span.comment   {%s}\n"
            "span.ditective {%s}\n"
            "span.function  {%s}\n"
            "span.keyword   {%s}\n"
            "span.string    {%s}\n"
            "span.linenum   {%s}\n"
            "-->\n"
            "</style>\n"
            "</head>\n"
            "<body>\n",
            DocTitle,
            colopt.getBodyFmt(),
            colopt.getCommentFmt(),colopt.getDitectiveFmt(),colopt.getFunctionFmt(),
            colopt.getCommandFmt(),colopt.getStringFmt(),colopt.getLinenumFmt());
    
    // * 書き出し用HTML文字列の確保
    // タブ→スペース(&nbsp;):
    // "&nbsp:":6[byte] * t2s(=4) = 6*4=24[byte]
    // sum = 24 * n [byte]
    int tabmem = 6*cmdopt.t2s*tabSum;
    // 行番号:
    // "<span class=\"linenum\">n:&nbsp;</span>" 38[byte]〜
    // sum = 38 * n [byte]
    int linemem = 0;
    if ( cmdopt.swLine ) {
        linemem = 38 * (lineSum+1);
        if ( lineSum < 100 ) linefmt = 2;
        else if ( lineSum < 1000 ) linefmt = 3;
        else linefmt = 4;
    }
    int buflen = FMINSIZ+fcLen*3+tabmem+linemem;
    if ( !Initbuf(buflen) ) {
        fprintf(stderr,"cannot alloc output buffer.\n");
        return FALSE;
    }
    // convert: c/c++ source file to HTML source
    Cpp2html();
    return TRUE;
}

// HTMLコードの長さ
int Cpp2Html::CodeLen()
{
    int len = 0;
    if ( !cmdopt.Hfmt ) len += strlen(HeadCode);
    len += strlen(Hcode);
    if ( !cmdopt.Hfmt ) len += strlen(FootCode);
    return len;
}

// HTMLコードのコピー
void Cpp2Html::GetCode(char* dst)
{
    // ヘッダの出力
    if ( !cmdopt.Hfmt ) strcat(dst,HeadCode);
    // コードの出力
    strcat(dst,Hcode);
    // フッタの出力
    if ( !cmdopt.Hfmt ) strcat(dst,FootCode);
}

// ファイル出力
void Cpp2Html::WriteCode()
{
    if ( cmdopt.Hdst == DST_FILE ) {
        if ( !strcmp(cmdopt.fOut,std_out) ) {
            fprintf(stderr,"**%s\n\toutput file is not detected.\n",c2hprog);
            return;
        }
        fprintf(stderr,"**%s\n\t%s -> %s\n",c2hprog,cmdopt.fIn,cmdopt.fOut);
    }
    
    // * 出力設定
    OFP = stdout;
    if ( !cmdopt.Hdst ) {
        if ( (OFP=fopen(cmdopt.fOut,"w")) == NULL ) {
            fprintf(stderr,"output file[%s]: cannot writable.\n",cmdopt.fOut);
            return;
        }
    }
    
    // * コードの生成・出力
    // ヘッダの出力
    if ( !cmdopt.Hfmt ) Html_headout();
    // コードの出力
    Html_codeout();
    // フッタの出力
    if ( !cmdopt.Hfmt ) Html_footout();
    
    // 後片付け
    fclose(OFP);
}

// ソース,HTML コードの解放
void Cpp2Html::FreeCode()
{
    if ( Run ) {
        if ( Ccode != NULL ) free(Ccode); Ccode = NULL;
        if ( Hcode != NULL ) free(Hcode); Hcode = NULL;
    }
}

// ヘッダ
void Cpp2Html::Html_headout()
{
    fprintf(OFP,"%s",HeadCode);
    fflush(OFP);
}

// フッタ
void Cpp2Html::Html_footout()
{
    fprintf(OFP,"%s",FootCode);
    fflush(OFP);
}

// C/C++ source
void Cpp2Html::Html_codeout()
{
    fwrite(Hcode,sizeof(char),strlen(Hcode),OFP);
}

// HTML 出力バッファの初期化
int Cpp2Html::Initbuf(int buflen)
{
    hci = 0;
    //linenum = 1;
    Hcode = (char*)calloc(buflen,sizeof(char));
    memset(Hcode,0,buflen);
    if ( Hcode == NULL ) return 0;
    return 1;
}

// 出力バッファへ行番号を挿入
void Cpp2Html::PushLine()
{
    char linebuf[50];
    char fmt[10];
    switch ( linefmt ) {
    case 2:  sprintf(fmt,"%%02d:<br>"); break;
    case 3:  sprintf(fmt,"%%03d:<br>"); break;
    case 4:  sprintf(fmt,"%%04d:<br>"); break;
    default: sprintf(fmt,"%%d<br>");    break;
    }

    strpush("<table border=\"0\">\n<tr>\n<td>\n<pre><span class=\"linenum\">");
    for ( int i=0; i<lineSum; i++ ) {
        sprintf(linebuf,fmt,i+1);
        strpush(linebuf);
    }
    strpush("</span></pre>\n</td>\n<td>\n");
}

// 出力バッファへ文字列の追加
void Cpp2Html::strpush(const char* buf)
{
    for ( unsigned i=0; i<strlen(buf); i++ ) Hcode[hci++] = buf[i];
}

// 出力バッファへ文字の追加
void Cpp2Html::charpush(const char buf)
{
    Hcode[hci++] = buf;
}

// HTML記号かどうか
int Cpp2Html::htmlsym_sch(char c)
{
    if ( c == '&' ) return 0;
    if ( c == '<' ) return 1;
    if ( c == '>' ) return 2;
    //if ( c == '\"') return 3;
    return -1;
}

// HTML記号の検索
char* Cpp2Html::get_htmlsym(int kn)
{
    return (char*)html_sym[kn];
}

// 特殊領域の判定[通常,文字列,コメント]
int Cpp2Html::ch_strstate(unsigned char pre_c, unsigned char now_c, unsigned char nxt_c)
{
    if ( pre_c != '\\' && now_c == '\'' ) return STR_CHAR;
    if ( pre_c != '\\' && now_c == '\"' ) return STR_STRING;
    if ( now_c == '/'  && nxt_c == '*' )  return STR_CCOMMENT;
    if ( now_c == '/'  && nxt_c == '/' )  return STR_CPPCOMMENT;
    return STR_NORMAL;
}

// 特殊領域の置き換え[通常,文字列,コメント]
int Cpp2Html::rep_strarea(int kstat, char* c_code, int iPos)
{
    int reti;
    switch ( kstat ) {
    case STR_CHAR:
        strpush("<span class=\"string\">\'");
        reti = rep_area(c_code,iPos,"\'");
        strpush("\'");
        break;
    case STR_STRING:
        strpush("<span class=\"string\">\"");
        reti = rep_area(c_code,iPos,"\"");
        strpush("\"");
        break;
    case STR_CCOMMENT:
        strpush("<span class=\"comment\">/*");
        reti = rep_area(c_code,iPos+1,"*/");
        strpush("*/");
        reti += 2;
        break;
    case STR_CPPCOMMENT:
        strpush("<span class=\"comment\">//");
        reti = rep_area(c_code,iPos+1,"\n");
        // [\r\n],[\r] の際に問題あり!?
        break;
    }
    strpush("</span>");
    return reti;
}

// 指定範囲の置き換え
int Cpp2Html::rep_area(char* c_code, int iPos, const char* estr)
{
    int i,hkey,iStart,fopt;
    unsigned char c,pre_c;
    fopt = (estr[0] == '\'' ) ? 1 : 0;
    // 文字列範囲取得
    i = iPos;
    iStart = 1;
    while ( iStart && i++ < fcLen ) {
        pre_c = c_code[i-1];
        c = c_code[i];
        if ( c == '\t' ) { strpush(tabstr); continue; }
        if ( kwd_sch(c_code,fcLen,i,"\\\\") ) { strpush("\\\\"); i++; continue; }
        if ( kwd_sch(c_code,fcLen,i,"\\\'") ) { strpush("\\\'"); i++; continue; }
        if ( kwd_sch(c_code,fcLen,i,"\\\"") ) { strpush("\\\""); i++; continue; }
        hkey = htmlsym_sch(c);
        if ( hkey >= 0 ) {
            strpush(get_htmlsym(hkey));
        } else {
            if ( kwd_sch(c_code,fcLen,i,estr) ) iStart = 0;
            else charpush(c);
        }
    }
    return (i-iPos);
}

// プリプロフェッサの判定
int Cpp2Html::ch_ditective(char* c_code, int iPos, unsigned char pre_c)
{
    int klen,kend;
    for ( int ki=0; pp_len[ki] != 0; ki++ ) {
        klen = pp_len[ki];
        if ( iPos+klen >= fcLen ) continue;
        kend = c_code[iPos+klen];
        if ( kwd_sch(c_code,fcLen,iPos,(char*)pp_table[ki]) && !isalpha(pre_c) && !isalpha(kend) ) {
            return ki;
        }
    }
    return -1;
}

// 関数の判定
int Cpp2Html::ch_function(char* c_code, int iPos, unsigned char pre_c)
{
    int kp;
    unsigned char c = c_code[iPos];
    if ( pre_c == '\n' ) {
        if ( isalpha(c) ) {
            kp = line_schchar(Ccode,fcLen,iPos,'(');
            if ( kp > 0 ) return 1;
        }
        return 0;
    }
    return 0;
}

// 関数の置き換え
int Cpp2Html::rep_function(char* c_code, int iPos, unsigned char pre_c)
{
    int i=iPos,kno,kp;
    unsigned char c;
    if ( (kno = ch_keyword(c_code,i,pre_c)) >= 0 ) {
        strpush("<span class=\"keyword\">");
        strpush(kwd_table[kno]);
        strpush("</span>");
        i += strlen(kwd_table[kno]);
    }
    c = c_code[i];
    if ( c == '*' ) { charpush('*'); i++; c = c_code[i]; }
    if ( c == '*' ) { charpush('*'); i++; c = c_code[i]; }
    // class
    kp = line_schstr(Ccode,fcLen,i,"::");
    if ( kp >= 0 ) {
        memset(repst,0,sizeof(char)*256);
        strpush("<span class=\"ditective\">");
        strncpy(repst,Ccode+i,kp);
        strpush(repst);
        strpush("</span>::");
        i += kp+2;
    }
    // function
    kp = line_schchar(Ccode,fcLen,i,'(');
    if ( kp >= 0 ) {
        memset(repst,0,sizeof(char)*256);
        strpush("<span class=\"function\">");
        strncpy(repst,Ccode+i,kp);
        strpush(repst);
        strpush("</span>");
        i += kp-1;
    }
    return (i-iPos);
}

// キーワードの判定
int Cpp2Html::ch_keyword(char* c_code, int iPos, unsigned char pre_c)
{
    int klen,kend;
    for ( int ki=0; kwd_len[ki] != 0; ki++ ) {
        klen = kwd_len[ki];
        if ( iPos+klen >= fcLen ) continue;
        kend = c_code[iPos+klen];
        if ( kwd_sch(c_code,fcLen,iPos,(char*)kwd_table[ki]) && !isalpha(pre_c) && !isalpha(kend) ) {
            return ki;
        }
    }
    return -1;
}

// Convert: c/c++ source file to HTML source
void Cpp2Html::Cpp2html()
{
    int kno,kp;
    int hkey=0,kstat=0;
    unsigned char c,nxt_c,pre_c=' ';
    
    if ( cmdopt.swLine ) PushLine();
    strpush("<pre>");

    // キーワード、コメント、文字列の検索
    for ( int i=0; i<fcLen; pre_c=Ccode[i++] ) {
        c     = Ccode[i];
        nxt_c = (i<fcLen-1) ? Ccode[i+1] : 0;
        
        // タブの変換
        if ( c == '\t' ) { strpush(tabstr); continue; }
        // HTML記号への変換
        if ( (hkey = htmlsym_sch(c)) >= 0 ) {
            strpush(get_htmlsym(hkey)); continue;
        }
        // 特殊領域の置き換え[通常,文字列,コメント]
        if ( (kstat = ch_strstate(pre_c,c,nxt_c)) != 0 ) {
            i += rep_strarea(kstat,Ccode,i);
            continue;
        }
        // プリプロセッサの検索・置換
        if ( (kno = ch_ditective(Ccode,i,pre_c)) >= 0 ) {
            strpush("<span class=\"ditective\">");
            strpush(pp_table[kno]);
            strpush("</span>");
            i += pp_len[kno]-1;
            continue;
        }
        // 関数の置換
        if ( ch_function(Ccode,i,pre_c) ) {
            i += rep_function(Ccode,i,pre_c);
            continue;
        }
        // キーワードの置換
        if ( (kno = ch_keyword(Ccode,i,pre_c)) >= 0 ) {
            strpush("<span class=\"keyword\">");
            strpush(kwd_table[kno]);
            strpush("</span>");
            i += kwd_len[kno]-1;
            continue;
        }
        // インクルード文の置換
        if ( kwd_sch(Ccode,fcLen,i,"#include") ) {
            int fopt = 0;
            strpush("<span class=\"ditective\">#include</span>");
            i += 8;
            if ( line_schchar(Ccode,fcLen,i,'<') >= 0 ) fopt = 1;
            kp = line_schchar(Ccode,fcLen,i,(fopt) ? '<' : '\"');
            if ( kp >= 0 ) {
                memset(repst,0,sizeof(char)*256);
                strncpy(repst,Ccode+i,kp);
                strpush(repst);
                i += kp+1;
            }
            if ( fopt ) {
                strpush("<span class=\"keyword\">&lt;");
                kp = line_schchar(Ccode,fcLen,i,'>');
            } else {
                strpush("<span class=\"string\">\"");
                kp = line_schchar(Ccode,fcLen,i,'\"');
            }
            if ( kp >= 0 ) {
                memset(repst,0,sizeof(char)*256);
                strncpy(repst,Ccode+i,kp);
                strpush(repst);
                i += kp+1;
                strpush((fopt) ? "&gt;" : "\"");
            }
            c = Ccode[i];
            strpush("</span>");
        }
        // 文字列のコピー
        charpush(c);
    }

    if ( cmdopt.swLine ) {
        strpush("</td>\n</tr>\n</table>\n");
    }
    strpush("</pre>\n");
    charpush('\0');
}
inserted by FC2 system