■ 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(">", fout); break;
case '<': fputs("<", fout); break;
case '&': fputs("&", fout); break;
case '\"': fputs(""", 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("&", fout);
ungetnext(chbuf);
divide = 0;
return;
}
else
{
fprintf(fout, "<font color=\"%s\">&", 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;
}