| 1 |
/*! |
| 2 |
\file |
| 3 |
\brief 属性情報の読み出しと変更 |
| 4 |
|
| 5 |
\author Satofumi KAMIMURA |
| 6 |
|
| 7 |
$Id$ |
| 8 |
|
| 9 |
\todo 余力あらば umask を適切に設定する |
| 10 |
\todo getline() と eof() を併用しないよう、他の実装を変更していく |
| 11 |
*/ |
| 12 |
|
| 13 |
#include "AccessProperty.h" |
| 14 |
#include "DetectOS.h" |
| 15 |
#include <sys/stat.h> |
| 16 |
#include <string> |
| 17 |
#include <fstream> |
| 18 |
#include <map> |
| 19 |
#include <boost/xpressive/xpressive.hpp> |
| 20 |
#ifdef WINDOWS_OS |
| 21 |
#include <windows.h> |
| 22 |
#endif |
| 23 |
|
| 24 |
using namespace beego; |
| 25 |
using namespace boost::xpressive; |
| 26 |
|
| 27 |
|
| 28 |
struct AccessProperty::pImpl { |
| 29 |
std::string error_message_; //!< エラー状態 |
| 30 |
bool read_; //!< 読み込み済みの場合 true |
| 31 |
time_t ctime_; //!< 読み出し時の時刻 |
| 32 |
std::string path_; //!< 読み出したファイルパス |
| 33 |
typedef std::map<std::string,std::string> PropertyMap; |
| 34 |
PropertyMap properties_; |
| 35 |
|
| 36 |
pImpl(const char* fname) |
| 37 |
: error_message_("no error."), read_(false), ctime_(0), path_(fname) { |
| 38 |
} |
| 39 |
|
| 40 |
time_t getChangeTime(void) { |
| 41 |
|
| 42 |
struct stat buf; |
| 43 |
if (stat(path_.c_str(), &buf) < 0) { |
| 44 |
return 0; |
| 45 |
} |
| 46 |
return buf.st_ctime; |
| 47 |
} |
| 48 |
|
| 49 |
bool readfile(void) { |
| 50 |
|
| 51 |
// 読み出し処理 |
| 52 |
// - 先頭が '#' で始まる場合は読み飛ばす |
| 53 |
// - 「key = value」において、value 以降は読み飛ばす |
| 54 |
|
| 55 |
std::ifstream fin(path_.c_str()); |
| 56 |
if (! fin.is_open()) { |
| 57 |
return false; |
| 58 |
} |
| 59 |
|
| 60 |
// 要素の読み出し |
| 61 |
sregex pattern = sregex::compile("^(.+?)\\s*=\\s*\"(.+?)\"\\s*$"); |
| 62 |
std::string line; |
| 63 |
while (getline(fin, line)) { |
| 64 |
if (line.empty() || line[0] == '#') { |
| 65 |
continue; |
| 66 |
} |
| 67 |
|
| 68 |
smatch match; |
| 69 |
if (regex_search(line, match, pattern)) { |
| 70 |
// 格納 |
| 71 |
properties_[match[1]] = match[2]; |
| 72 |
} |
| 73 |
} |
| 74 |
|
| 75 |
read_ = true; |
| 76 |
ctime_ = getChangeTime(); |
| 77 |
return true; |
| 78 |
} |
| 79 |
|
| 80 |
bool save(void) { |
| 81 |
|
| 82 |
// !!! readfile() と重複が多いが、仕方ないよなぁ... |
| 83 |
|
| 84 |
// 出力ファイルの生成 |
| 85 |
#if defined(WINDOWS_OS) |
| 86 |
const char* temp_name = _tempnam(NULL, "access_property_"); |
| 87 |
#else |
| 88 |
char temp_name[] = "access_property_XXXXXX"; |
| 89 |
mkstemp(temp_name); |
| 90 |
#endif |
| 91 |
std::ofstream fout(temp_name); |
| 92 |
|
| 93 |
// 既存ファイルの読み出し |
| 94 |
std::ifstream fin(path_.c_str()); |
| 95 |
if (! fin.is_open()) { |
| 96 |
return false; |
| 97 |
} |
| 98 |
|
| 99 |
// 要素の読み出しと置換 |
| 100 |
sregex pattern = sregex::compile("^(.+?)\\s*=\\s*\"?(.+?)\"?\\s*$"); |
| 101 |
std::string line; |
| 102 |
while (getline(fin, line)) { |
| 103 |
smatch match; |
| 104 |
if (regex_search(line, match, pattern)) { |
| 105 |
|
| 106 |
// 格納されているキーワードなら、置き換える |
| 107 |
const std::string& key = match[1]; |
| 108 |
PropertyMap::iterator p = properties_.find(key); |
| 109 |
if (p != properties_.end()) { |
| 110 |
fout << key << " = \"" << p->second << '"' << std::endl; |
| 111 |
|
| 112 |
// キーの登録削除 |
| 113 |
properties_.erase(key); |
| 114 |
|
| 115 |
continue; |
| 116 |
} |
| 117 |
} |
| 118 |
fout << line << std::endl; |
| 119 |
} |
| 120 |
|
| 121 |
// 未保存の項目を保存する |
| 122 |
for (PropertyMap::iterator it = properties_.begin(); |
| 123 |
it != properties_.end(); ++it) { |
| 124 |
fout << it->first << " = " << it->second << std::endl; |
| 125 |
} |
| 126 |
|
| 127 |
// ファイルを置換する |
| 128 |
fin.close(); |
| 129 |
fout.close(); |
| 130 |
#if defined(WINDOWS_OS) |
| 131 |
// !!! RemoveFile() を使うべき |
| 132 |
DeleteFileA(path_.c_str()); |
| 133 |
#endif |
| 134 |
return (rename(temp_name, path_.c_str()) == 0) ? true : false; |
| 135 |
} |
| 136 |
}; |
| 137 |
|
| 138 |
|
| 139 |
AccessProperty::AccessProperty(const char* fname) : pimpl(new pImpl(fname)) { |
| 140 |
pimpl->readfile(); |
| 141 |
} |
| 142 |
|
| 143 |
|
| 144 |
AccessProperty::~AccessProperty(void) { |
| 145 |
} |
| 146 |
|
| 147 |
|
| 148 |
const char* AccessProperty::what(void) { |
| 149 |
return pimpl->error_message_.c_str(); |
| 150 |
} |
| 151 |
|
| 152 |
|
| 153 |
#if 0 |
| 154 |
void AccessProperty::operator=(const char* value) { |
| 155 |
pimpl->value_ = value; |
| 156 |
} |
| 157 |
|
| 158 |
|
| 159 |
AccessProperty& AccessProperty::operator[](const char* key) { |
| 160 |
|
| 161 |
pimpl->properties_[key] = pimpl->value_; |
| 162 |
fprintf(stderr, " [%s] = %s\n", key, pimpl->value_.c_str()); |
| 163 |
|
| 164 |
return *this; |
| 165 |
} |
| 166 |
#endif |
| 167 |
|
| 168 |
|
| 169 |
std::string& AccessProperty::operator[](const char* key) const { |
| 170 |
return pimpl->properties_[key]; |
| 171 |
} |
| 172 |
|
| 173 |
|
| 174 |
bool AccessProperty::reload(void) { |
| 175 |
if (! pimpl->read_) { |
| 176 |
// !!! error_message_ を更新 |
| 177 |
return false; |
| 178 |
} |
| 179 |
|
| 180 |
return pimpl->readfile(); |
| 181 |
} |
| 182 |
|
| 183 |
|
| 184 |
bool AccessProperty::save(void) { |
| 185 |
if (! pimpl->read_) { |
| 186 |
// !!! error_message_ を更新 |
| 187 |
return false; |
| 188 |
} |
| 189 |
|
| 190 |
return pimpl->save(); |
| 191 |
} |
| 192 |
|
| 193 |
|
| 194 |
bool AccessProperty::isChangedByOther(void) { |
| 195 |
if (! pimpl->read_) { |
| 196 |
// !!! error_message_ を更新 |
| 197 |
return false; |
| 198 |
} |
| 199 |
|
| 200 |
// 最終時刻を読み出し、読み込んだ時刻と違っていたら、true を返す |
| 201 |
time_t now_ctime = pimpl->getChangeTime(); |
| 202 |
return (now_ctime != pimpl->ctime_) ? true : false; |
| 203 |
} |