/********************************************************************* util.cc 小道具集 Haruhiko Okumura *********************************************************************/ #include #include #include #include #include #include #include /* errno */ #include #include #include /* fcntl() */ #include /* htonl(), ntohl() */ #include "util.h" using namespace std; /** 文字列型に変換 */ #if 0 template string to_str(T x, int len=0, char fillchar=0) { ostringstream os; if (len != 0) os.width(len); if (fillchar != 0) os.fill(fillchar); os << x; return os.str(); } #endif string to_str(int x) { char s[20]; for (int i = 0; i < 20; i++) s[i] = 0; // precaution for buggy library sprintf(s, "%d", x); return string(s); } /** 現在日を "2000-01-23" の形の文字列として返す */ string datestr() { time_t t = time(0); struct tm* lt = localtime(&t); char s[11]; strftime(s, sizeof(s), "%Y-%m-%d", lt); return string(s); } /** 現在時刻を "2000-01-23 12:34:56" の形の文字列として返す */ string timestr() { time_t t = time(0); struct tm* lt = localtime(&t); char s[20]; strftime(s, sizeof(s), "%Y-%m-%d %X", lt); return string(s); } /** 環境変数を取得。例: environment("REMOTE_ADDR") */ string environment(const char* env) { if (env) { char* p = getenv(env); if (p != 0) return p; } return ""; } /** 全環境変数を取得 */ extern "C" char** environ; /* にない場合に備えて */ map environment() { map m; for (int i = 0; environ[i]; i++) { string s; const char* p = environ[i]; while (*p && *p != '=') s += *p++; if (*p == '=') { string t; while (*++p) t += *p; m[s] = t; } } return m; } /** メッセージを HTML 形式で出力 */ void put_html(const string& title, const string& msg, const bool nocache /* = false */) { cout << "Content-type: text/html\n\n" "\n" "\n"; if (nocache) cout << "\n"; cout << "\n" "" << title << "\n" "\n" "\n" "

" << title << "

\n" "

" << msg << "\n" "


\n" "

ブラウザの[戻る]ボタンで戻ってください。\n" "\n"; } /** エラーメッセージを表示して終了 */ void error_html(const string& msg) { put_html("エラー", msg); exit(0); } /** & < > " を & < > " に直す */ string to_html(const string& s) { string t; for (string::const_iterator p = s.begin(); p != s.end(); ++p) { if (*p == '&') t += "&"; else if (*p == '<') t += "<"; else if (*p == '>') t += ">"; else if (*p == '"') t += """; else t += *p; } return t; } #if 0 /** 上と同じだが ... だけ許す */ string to_html_a(string& s) { string t; int level = 0; for (size_t i = 0; i < s.length(); ++i) { if (s[i] == '<') { if (level == 0 && i+2 < s.length() && std::toupper(s[i+1]) == 'A' && std::isspace(s[i+2])) { level = 1; t += s[i++]; t += s[i++]; t += s[i]; } else if (level == 3 && i+3 < s.length() && s[i+1] == '/' && std::toupper(s[i+2]) == 'A' && s[i+3] == '>') { level = 0; t += s[i++]; t += s[i++]; t += s[i++]; t += s[i]; } else { t += "<"; } } else if (s[i] == '"') { if (level == 1) level = 2; else if (level == 2) level = 1; t += s[i]; } else if (s[i] == '>') { if (level == 1) { t += s[i]; level = 3; } else if (level == 2) { t += "\">"; level = 3; } else { t += ">"; } } else if (s[i] == '&') { if (level == 1 || level == 2) t += '&'; else t += "&"; } else { t += s[i]; } } if (level == 1) t += ">"; else if (level == 2) t += "\">"; else if (level == 3) t += ""; return t; } #endif /** & < > を & < > に直すが ... だけ許す */ string to_html_a(string& s) { string t; int level = 0; for (size_t i = 0; i < s.length(); ++i) { if (s[i] == '<') { if (level == 0 && i+14 < s.length() && s[i+1] == 'a' && s[i+2] == ' ' && s[i+3] == 'h' && s[i+4] == 'r' && s[i+5] == 'e' && s[i+6] == 'f' && s[i+7] == '=' && s[i+8] == '"' && !isspace(s[i+9]) && (tolower(s[i+ 9]) != 'j' || tolower(s[i+10]) != 'a' || tolower(s[i+11]) != 'v' || tolower(s[i+12]) != 'a' || tolower(s[i+13]) != 's' || tolower(s[i+14]) != 'c')) { level = 1; t += "') { level = 0; t += ""; i += 3; } else { t += "<"; } } else if (s[i] == '"') { if (level == 1) { if (i+1 < s.length() && s[i+1] == '>') { level = 2; t += "\">"; i += 1; } else { t += """; } } else { t += "\""; } } else if (s[i] == '>') { t += ">"; } else if (s[i] == '&') { t += "&"; } else { t += s[i]; } } if (level == 1) t += "\">"; else if (level == 2) t += ""; return t; } /** 正しいEUC文字列でない場合は修正する */ int check_euc(string& s) { bool visible = false; bool good = true; int euccount = 0; for (string::iterator p = s.begin(); p != s.end(); ++p) { int c = (unsigned char)(*p); if ((c >= 0x20 && c <= 0x7e) || c == '\t' || c == '\n') { if (c > 0x20) visible = true; } else if (c >= 0x80 && p+1 != s.end()) { int d = (unsigned char)(*++p); if (c >= 0xa1 && c <= 0xfe && d >= 0xa1 && d <= 0xfe) { if (c != 0xa1 || d != 0xa1) { visible = true; euccount++; } } else if (c == 0x8e && d >= 0xa0 && d <= 0xdf) { visible = true; } else if ((d >= 0x20 && d <= 0x7e) || d == '\t' || d == '\n') { *(p-1) = ' '; if (d > 0x20) visible = true; good = false; } else { *(p-1) = *p = ' '; good = false; } } else { *p = ' '; good = false; } } if (! visible) s = ""; if (! good) return 0; return euccount; } /** ファイルにアペンド */ bool append(const string& file, const string& s) { if (file.empty() || s.empty()) return false; // Use low-level I/O for atomicity int fd = open(file.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); if (fd < 0) return false; if (write(fd, s.c_str(), s.length()) != (int)s.length()) return false; if (close(fd) < 0) return false; return true; } /** ロックファイルクラス */ LockFile::LockFile(const string& filename) { fd = open(filename.c_str(), O_RDWR | O_CREAT, 0666); if (fd < 0) { count = -1; return; } struct flock lock; lock.l_type = F_WRLCK; lock.l_start = 0; lock.l_whence = SEEK_SET; lock.l_len = 0; // ロック対象はファイル全体 // ↓ 他のプログラムがロックしている間待つ int r; while ((r = fcntl(fd, F_SETLKW, &lock)) < 0 && errno == EINTR) continue; if (r < 0) { // ロックできなかった close(fd); count = fd = -1; return; } if (read(fd, &count, sizeof(count)) == sizeof(count)) { count = ntohl(count); } else { count = 0; } } void LockFile::setcount(int n) { if (fd < 0) return; count = n; n = htonl(n); lseek(fd, 0, SEEK_SET); write(fd, &n, sizeof(n)); } LockFile::~LockFile() { close(fd); // close() するとロックが解除される } /** GET や PUT で得た文字列を通常の文字列に変換する */ void decode_query(string& s) { string t; for (string::const_iterator p = s.begin(); p != s.end(); p++) { if (*p == '%') { if (++p == s.end()) break; int d = *p; if (++p == s.end()) break; int e = *p; d = (d >= 'A') ? (d & 0xdf) - 'A' + 10 : d - '0'; e = (e >= 'A') ? (e & 0xdf) - 'A' + 10 : e - '0'; if (d >= 0 && d < 16 && e >= 0 && e < 16) t += char(16 * d + e); } else if (*p == '+') { t += ' '; } else if (*p == '&') { t += '\n'; } else { t += *p; } } s = t; } /** 上の逆。危ない文字を % で始まる16進に変換する */ string encode_query(const string& s) { const char tbl[] = "0123456789ABCDEF"; string t; for (string::const_iterator p = s.begin(); p != s.end(); p++) { if (*p == ' ') { t += '+'; } else if (isalnum(*p)) { t += *p; } else { t += '%'; t += tbl[(unsigned char)(*p) >> 4]; t += tbl[(unsigned char)(*p) & 15]; } } return t; } /** POST された内容をパースする */ void parse_post(map& post, istream& in /* = cin */) { while (in) { string s, t; int c; while ((c = in.get()) != EOF && c != '=') s += char(c); if (c == EOF) break; while ((c = in.get()) != EOF && c != '&') { if (c == '+') { t += ' '; } else if (c == '%') { int d = in.get(); int e = in.get(); d = (d >= 'A') ? (d & 0xdf) - 'A' + 10 : d - '0'; e = (e >= 'A') ? (e & 0xdf) - 'A' + 10 : e - '0'; if (d >= 0 && d < 16 && e >= 0 && e < 16) { int x = 16 * d + e; if (x == '\n' || x == '\t' || x >= ' ') t += char(x); } else { t += char(c); t += char(d); t += char(e); } } else if (c >= ' ') { t += char(c); } } post[s] = t; } } /** GET した内容をパースする */ void parse_get(map& post) { char* p = getenv("QUERY_STRING"); if (p == 0) return; istringstream in(p); parse_post(post, in); } /** start から始まる文字列の頭の部分を target と比較 */ inline bool isprefix(const char* start, const char* target) { while (*start) { if (*start != *target) break; ++start; ++target; } return *target == 0; } /** 上と同じだが tolower してから比較 */ inline bool isprefix_lower(const char* start, const char* target) { while (*start) { if (tolower(*start) != *target) break; ++start; ++target; } return *target == 0; } /** BASE64 の変換表。64 は使われない文字 */ const byte mimetbl[256] = { 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 0, 64, 64, 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 }; /** BASE64 を復号 */ string demime(const string& s) { string t; const char* p = s.c_str(); while (*p) { if (isprefix_lower(p, "=?iso-2022-jp?b?")) { p += 16; unsigned int c = 1; while (true) { int k = mimetbl[byte(*p++)]; if (k == 64) break; c = (c << 6) | k; if (c & 0x1000000) { c &= 0xffffff; if (c >> 16) t += char(c >> 16); c &= 0xffff; if (c >> 8) t += char(c >> 8); c &= 0xff; if (c) t += char(c); c = 1; } } if (*p == '?') p++; if (*p == '=') p++; if (isspace(*p)) { t += ' '; do { p++; } while (isspace(*p)); } } else { t += *p++; } } return t; } /** BASE64 にする */ string enmime(const string& s) { const char* B = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; string t = "=?iso-2022-jp?B?"; string::const_iterator p = s.begin(); while (p != s.end()) { t += B[byte(*p) >> 2]; int c = (byte(*p) & 3) << 4; if (++p == s.end()) { t += B[c]; t += "=="; } else { t += B[c | (byte(*p) >> 4)]; c = (byte(*p) & 15) << 2; if (++p == s.end()) { t += B[c]; t += "="; } else { t += B[c | (byte(*p) >> 6)]; t += B[byte(*p) & 63]; ++p; } } } t += "?="; return t; } /** シフト JIS の1文字を JIS に変換する */ void sjis_to_jis(int& ph, int& pl) { if (ph <= 0x9f) { if (pl < 0x9f) ph = (ph << 1) - 0xe1; else ph = (ph << 1) - 0xe0; } else { if (pl < 0x9f) ph = (ph << 1) - 0x161; else ph = (ph << 1) - 0x160; } if (pl < 0x7f) pl -= 0x1f; else if (pl < 0x9f) pl -= 0x20; else pl -= 0x7e; } /** iso-2022-jp の文字列を EUC に変換する */ string jis2euc(const string& s) { enum {NORMAL, KANJI, HANKANA} mode = NORMAL; string t; string::const_iterator p = s.begin(); while (p != s.end()) { if (*p == 0x1b) { if (++p == s.end()) break; if (*p == '$') { if (++p == s.end()) break; if (*p == '@' || *p == 'B') mode = KANJI; } else if (*p == '(') { if (++p == s.end()) break; if (*p == 'B' || *p == 'J') mode = NORMAL; else if (*p == 'I') mode = HANKANA; } } else if (*p == 0x0e) { mode = HANKANA; } else if (*p == 0x0f) { mode = NORMAL; } else if (mode == KANJI) { if (*p < 0x21 || *p > 0x7e) { mode = NORMAL; continue; } if (++p == s.end()) break; if (*p < 0x21 || *p > 0x7e) { mode = NORMAL; continue; } t += *(p-1) | 0x80; t += *p | 0x80; } else if (mode == HANKANA) { if (*p < 0x20 || *p > 0x5f) { mode = NORMAL; continue; } t += 0x8e; t += *p | 0x80; } else if ((*p >= ' ' && *p <= 0x7e) || *p == '\n' || *p == '\t' || *p == 0x1c) { t += *p; } else { int c = byte(*p); if (issjis1(c)) { if (++p == s.end()) break; int d = byte(*p); if (issjis2(d)) { sjis_to_jis(c, d); t += char(c); t += char(d); } } } ++p; } return t; } /** EUC の文字列を iso-2022-jp に変換する */ string euc2jis(const string& s) { enum {NORMAL, KANJI, HANKANA} mode = NORMAL; string t; string::const_iterator p = s.begin(); while (p != s.end()) { int c = byte(*p++); if (c < 0x80) { if (mode != NORMAL) { mode = NORMAL; t += "\x1b(B"; } } else if (c == 0x8e) { if (mode != HANKANA) { mode = HANKANA; t += "\x1b(I"; } if (p == s.end()) break; c = byte(*p++); } else { if (mode != KANJI) { mode = KANJI; t += "\x1b$B"; } } t += char(c & 0x7f); } if (mode != NORMAL) t += "\x1b(B"; return t; }