Last Update: Dec.2,2006

C Source Library

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

■ Perl ソースを HTML 化する

/*
 *  pl2html.c : Perl ソースを HTML 化する
 *
 *      Copyright (c) 2001 AOK. 
 *  
 *      2001/10/18 - 4版
 *
 *      使い方: PL2HTML ファイル名
 *  
 *  出力されるHTMLファイル名は内部で生成される(.pl → .htm)
 *  同名のファイルがあると上書きされてしまう(^-^;)
 *
 */

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

#ifndef LSI_C /* Borland C++ 5.5 */
    #include <windows.h>
    #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

/* HTMLのカラーを指定 */
char color_kwd[65] = "blue";
char color_com[65] = "gray";
char color_str[65] = "teal";
char color_reg[65] = "fuchsia";
char color_sub[65] = "red";

int tabw = 8;   /* タブの幅 */
int col = -1, error = 0, line = 0, divide = 0;
char chbuf;
FILE *fin, *fout;

typedef int (*FPTR)(const void*, const void*);
void getsym(void);
void regexp(int n, char c);

#define BUFLEN          1024
#define NUM_OF_ARR(x)   (sizeof(x)/sizeof(x[0]))
#define isword(x)       (isalnum(x)||x=='_')
#define isname(x)       (isalnum(x)||x=='_'||x=='\"'||x=='\'')

char herebuf[BUFLEN+1] = { '\0' };
int hereline = 0, hereflag = 0;
    
/* 昇順に並べられたキーワード */
const char key[][16] = {
    "accept","alarm","atan2","bind","binmode","caller","chdir","chmod",
    "chop","chown","chroot","close","closedir","connect","cos","crypt",
    "dbmclose","dbmopen","defined","delete","die","do","dump","each",
    "else","elsif","endgrent","endhostent","endnetent","endprotoent",
    "endpwent","endservent","eof","eval","exec","exit","exp","fcntl",
    "fileno","flock","for","foreach","fork","getc","getgrent",
    "getgrgid","getgrnam","gethostbyaddr","gethostbyname","gethostent",
    "getlogin","getnetbyname","getnetent","getpeername","getpgrp",
    "getppid","getpriority","getprotobyname","getprotoent","getpwent",
    "getpwnam","getpwuid","getservbyname","getservent","getsockname",
    "getsockopt","gmtime","goto","grep","hex","if","index","int",
    "ioctl","join","keys","kill","last","length","link","listen",
    "local","localtime","log","lstat","mkdir","msgctl","msgget",
    "msgrcv","msgsnd","next","oct","open","opendir","ord","pack",
    "package","pipe","pop","print","printf","push","rand","read",
    "readdir","readlink","recv","redo","rename","require","reset",
    "return","reverse","rewinddir","rindex","rmdir","scalar","seek",
    "seekdir","select","semctl","semget","semop","send","setgrent",
    "setnetent","setpgrp","setpriority","setprotoent","setpwent",
    "setservent","setsockopt","shift","shmctl","shmget","shmread",
    "shmwrite","shutdown","sin","sleep","socket","socketpair","sort",
    "splice","split","sprintf","sqrt","srand","stat","study","sub",
    "substr","symlink","syscall","sysread","system","syswrite","tell",
    "telldir","time","times","truncate","umask","undef","unless",
    "unlink","unpack","unshift","utime","values","vec","wait",
    "waitpid","wantarray","warn","while","write",
};

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

/* 1文字戻す */
int ungetnext(char c)
{
    if (ungetc(c, fin) == EOF)
    {
        error++;
        return 0;
    }
    if (--col < 0) col = tabw - 1;
    if (c == '\n') line--;
    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(); col = -1; 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, "<font color=\"%s\">#", color_com);
    while (getnext())
    {
        if (chbuf == '\n')
        {
            fputs("</font>", fout);
            normal();
            break;
        }
        else normal();
    }
}

/* 文字列定数 */
void string(char d)
{
    int escape = 0, mode = CT_ANK;
    fprintf(fout, "<font color=\"%s\">%c", color_str, d);
    while (getnext())
    {
        mode = chkctype(chbuf, mode);
        if (mode == CT_KJ1 || mode == CT_KJ2)
        {
            escape = 0;
            putc(chbuf, fout);
        }
        else
        {
            if (escape == 0 && chbuf == d)
            {
                fprintf(fout, "%c</font>", d);
                break;
            }
            else if (chbuf == '\\')
            {
                escape = (escape == 0 ? 1 : 0);
                putc(chbuf, fout);
            }
            else
            {
                escape = 0;
                normal();
            }
        }
    }
    divide = 1;
}

/* 予約語を探す */
void symbol(void)
{
    char buf[BUFLEN+1] = { '\0' };
    input_buffer(buf, chbuf);
    if (!getnext())
    {
        fputs(buf, fout);
        return;
    }
    while (isword(chbuf) || chbuf=='\'') 
    {
        input_buffer(buf, chbuf);
        if (!getnext()) break;
    }
    if (check_keyword(buf))
    {
        fprintf(fout, "<font color=\"%s\">%s</font>", color_kwd, buf);
    }
    else if (strcmp(buf, "m") == 0 || strcmp(buf, "q") == 0 ||
             strcmp(buf, "qq") == 0 || strcmp(buf, "qx") == 0)
    {
        if (chbuf != ' ' && chbuf != '\t' && chbuf != '\n')
        {
            fprintf(fout, "<font color=\"%s\">%s</font>", color_kwd, buf);
            regexp(1, chbuf);
            return;
        }
        else fputs(buf, fout);
    }
    else if (strcmp(buf, "s") == 0 || strcmp(buf, "y") == 0 || strcmp(buf, "tr") == 0)
    {
        if (chbuf != ' ' && chbuf != '\t' && chbuf != '\n')
        {
            fprintf(fout, "<font color=\"%s\">%s</font>", color_kwd, buf);
            regexp(2, chbuf);
            return;
        }
        else fputs(buf, fout);
    }
    else fputs(buf, fout);  
    if (!error) ungetnext(chbuf);
    divide = 0;
}

/* here-is 構文の開始 */
void here_start(void)
{
    if (!getnext()) return;
    if (isspace(chbuf))
    {
        normal();
        if (!getnext()) return;
        if (chbuf == 'x') {
            if (!getnext()) return;
            if (isspace(chbuf))
            {
                hereline = line + 1;
                input_buffer(herebuf, 'x');
                fputc('x', fout);
                normal();
                hereflag = 1;
                return;
            }
            else
            {
                ungetnext(chbuf);
                ungetnext('x');
            }
        }
        else ungetnext(chbuf);
        divide = 0;
        hereflag = 0;
        return;
    }
    if (isname(chbuf))
    {
        fprintf(fout, "<font color=\"%s\">", color_str);
        input_buffer(herebuf, chbuf);
        while (getnext() && isname(chbuf)) input_buffer(herebuf, chbuf);
        fprintf(fout, "%s</font>", herebuf);
        hereline = line + 1;
        if (herebuf[0] == '\'' || herebuf[0] == '\"')
        {
            strcpy(herebuf, herebuf+1);
            herebuf[strlen(herebuf)-1] = '\0';
        }
        if (!error) ungetnext(chbuf);       
    }
    else
    {
        ungetnext(chbuf);
        divide = 0;
    }
    hereflag = 0;
}

/* here-is 構文の終了 */
void here_end(void)
{
    char buf[BUFLEN+1] = { '\0' };
    fprintf(fout, "<font color=\"%s\">", color_str);
    while (getnext())
    {
        normal();
        if (chbuf == '\n')
        {
            if ((strcmp(herebuf, "x") == 0 && hereflag == 1 && buf[0] == '\0') ||
                strcmp(herebuf, buf) == 0 && hereflag == 0)
            {
                fputs("</font>", fout);
                herebuf[0] = '\0';
                break;
            }
            else buf[0] = '\0';
        }
        else input_buffer(buf, chbuf);
    }
    divide = 0;
}

/* 数値定数 */
void number(void)
{
    normal();
    while (getnext())
    {
        if (isalnum(chbuf) || chbuf == '.')
        {
            normal();
            continue;
        }
        else
        {
            ungetnext(chbuf);
            break;
        }
    }
    divide = 1;
}

/* 変数 */
void variable(void)
{
    normal();
    if (!getnext()) return;
    normal();
    while (getnext())
    {
        if (isword(chbuf) || chbuf == ':')
        {
            normal();
            continue;
        }
        else if (chbuf == '[')
        {
            normal();
            while (getnext())
            {
                if (chbuf == ']')
                {
                    normal();
                    break;
                }
                else
                {
                    ungetnext(chbuf);
                    getsym();
                }
            }
        }
        else if (chbuf == '{')
        {
            normal();
            while (getnext())
            {
                if (chbuf == '}')
                {
                    normal();
                    break;
                }
                else
                {
                    ungetnext(chbuf);
                    getsym();
                }
            }
        }
        else
        {
            ungetnext(chbuf);
            break;
        }
    }
    divide = 1;
}

/* サブルーチンコール */
void subroutine(void)
{
    if (!getnext()) { normal(); return; }
    if (!isalpha(chbuf) && chbuf != '_')
    {
        fputs("&amp;", fout);
        ungetnext(chbuf);
        divide = 0;
        return;
    }
    else
    {
        fprintf(fout, "<font color=\"%s\">&amp;", color_sub);
        normal();
    }
    while (getnext())
    {
        if (isword(chbuf) || chbuf == '\'' || chbuf == ':')
        {
            normal();
            continue;
        }
        else if (chbuf == '(')
        {
            fputs("</font>", fout);
            normal();
            while (getnext())
            {
                if (chbuf == ')')
                {
                    normal();
                    break;
                }
                else
                {
                    ungetnext(chbuf);
                    getsym();
                }
            }
        }
        else
        {
            fputs("</font>", fout);
            ungetnext(chbuf);
            break;
        }
    }
    divide = 1;
}

/* 正規表現パターン */
void regexp(int n, char c)
{
    int escape = 0, mode = CT_ANK;
    fprintf(fout, "<font color=\"%s\">%c", color_reg, c);
    while (getnext())
    {
        mode = chkctype(chbuf, mode);
        if (mode == CT_KJ1 || mode == CT_KJ2)
        {
            escape = 0;
            putc(chbuf, fout);
        }
        else
        {
            if (escape == 0 && chbuf == c)
            {
                if (n == 1)
                {
                    fprintf(fout, "%c</font>", c);
                    while (getnext())
                    {
                        if (isalpha(chbuf))
                        {
                            normal();
                            continue;
                        }
                        else
                        {
                            ungetnext(chbuf);
                            break;
                        }
                    }
                    break;
                }
                else if (n == 2)
                {
                    n = 1;
                    putc(chbuf, fout);
                }
            }
            else if (chbuf == '\\')
            {
                escape = (escape == 0 ? 1 : 0);
                putc(chbuf, fout);
            }
            else
            {
                escape = 0;
                normal();
            }
        }
    }
    divide = 1;
}

/* 最初の文字を判定して処理を分岐する */
void getsym(void)
{
    if (!getnext()) return;
    if (herebuf[0] != '\0' && line == hereline)
    {
        normal();
        here_end();
        return;
    }
    if (chbuf == '#') comment();
    else if (isdigit(chbuf)) number();
    else if (chbuf == '$' || chbuf == '@' || chbuf == '%' ||
        chbuf == '*' || chbuf == '\\') variable();
    else if (chbuf == '&') subroutine();
    else if (chbuf == '\"') string('\"');
    else if (chbuf == '\'') string('\'');
    else if (chbuf == '/')
    {
        if (divide) normal();
        else regexp(1, chbuf);
    }
    else if (chbuf == '<')
    {
        normal();
        if (!getnext()) return;
        if (chbuf == '<')
        {
            normal();
            here_start();
        }
        else ungetnext(chbuf);
    }
    else if (isalpha(chbuf)) symbol();
    else if (chbuf == ' ' || chbuf == '\t') normal();
    else if (chbuf == ')' || chbuf == ']')
    {
        normal();
        divide = 1;
    }
    else
    {
        normal();
        divide = 0;
    }
}

/* 拡張子の付け替え */
int change_ext(char *fname)
{
    int len;
    char *p;
    len = strlen(strlwr(fname));
    p = strrchr(fname, '.');
    if (len >= 4 && len <= FILENAME_MAX-1 && p != NULL)
    {
        if (strcmp(p, ".pl") == 0 || strcmp(p, ".cgi") == 0) strcpy(p, ".htm");
        else
        {
            puts("入力ファイルはPerlソースではありません.");
            return 0;
        }
    }
    else
    {
        puts("拡張子の付け替えが出来ません.");
        return 0;
    }
    return 1;
}

/* プログラムのエントリポイント */
int main(int argc, char *argv[])
{
    char fname[FILENAME_MAX+1];

#ifndef LSI_C /* Borland C++ 5.5 */
    char ini[FILENAME_MAX+1], *p;
    const char *app = "Settings";
    strcpy(ini, argv[0]);
    p = strrchr(ini, '.');
    strcpy(p, ".ini");
#endif

    /* コマンドライン引き数のチェック */
    if (argc >= 2)
    {
        strncpy(fname, argv[1], FILENAME_MAX);
        fname[FILENAME_MAX] = '\0';
    }
    else
    {
        puts("パラメータが足りません.\n\n使い方: PL2HTML ファイル名");
        exit(EXIT_FAILURE);
    }
    
#ifndef LSI_C /* Borland C++ 5.5 */
    /* pl2html.ini ファイルを作成・編集することでカスタマイズが可能 */
    tabw = GetPrivateProfileInt(app,"tab", 8, ini);
    GetPrivateProfileString(app, "kwd", "blue", color_kwd, 64, ini);
    GetPrivateProfileString(app, "com", "gray", color_com, 64, ini);
    GetPrivateProfileString(app, "str", "teal", color_str, 64, ini);
    GetPrivateProfileString(app, "reg", "fuchsia", color_reg, 64, ini);
    GetPrivateProfileString(app, "sub", "red", color_sub, 64, ini);
#endif

    /* 入出力ファイルをオープン */
    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\" content=\"text/html;charset=Shift_JIS\">\n", fout);
    fputs("<meta name=\"GENERATOR\" content=\"PL2HTML (c)2001 AOK\">\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;
}