|
|
1.1 ! root 1: // ! 2: // nono ! 3: // Copyright (C) 2018 [email protected] ! 4: // ! 5: ! 6: #include "config.h" ! 7: #include "mystring.h" ! 8: ! 9: std::unique_ptr<Config> gConfig; ! 10: ! 11: // ! 12: // 設定クラス ! 13: // ! 14: ! 15: // コンストラクタ ! 16: Config::Config() ! 17: { ! 18: // 設定項目はすべてここで用意すること。 ! 19: // ここで追加した順に表示されるので基本的にはアルファベット順に並べる ! 20: // (先頭のドットは考慮しなくていい)。 ! 21: // ただし vmtype だけは特別なので最初に表示されたほうがいいだろう。 ! 22: ! 23: Add("vmtype", ""); ! 24: ! 25: // 以下概ねアルファベット順に並べる ! 26: ! 27: // デバッガの TCP 待ち受けポート ! 28: Add("debugger-port", 9999); ! 29: Add("ethernet-hostdriver", "none"); ! 30: Add("ethernet-macaddr", "auto"); ! 31: // LUNA の DIPSW (1 が UP、0 が DOWN) ! 32: Add("luna-dipsw1", "11110111"); ! 33: Add("luna-dipsw2", "11111111"); ! 34: // MPU クロック (MHz 単位の実数、小数点以下は3桁まで)。 ! 35: // 初期値は機種ごとに異なるためここでは何もせず ! 36: // Scheduler で初期値を代入している。 ! 37: Add("mpu-clock"); ! 38: Add("mpu-has-fpu", 0); ! 39: Add(".prom-scsi-debug", 0); ! 40: Add("prom-use-rom", 0); ! 41: Add(".rtc-force-fixed", 0); ! 42: for (int id = 0; id < 8; id++) { ! 43: Add(string_format("spc0-id%d-image", id)); ! 44: Add(string_format("spc0-id%d-writeprotect", id), 0); ! 45: } ! 46: } ! 47: ! 48: // デストラクタ ! 49: Config::~Config() ! 50: { ! 51: } ! 52: ! 53: // 項目を追加 (初期状態用)。値は空文字列。 ! 54: bool ! 55: Config::Add(const std::string& key) ! 56: { ! 57: return Add(key, ""); ! 58: } ! 59: ! 60: // 項目を追加 (初期状態用)。値は整数値を文字列にしたもの。 ! 61: bool ! 62: Config::Add(const std::string& key, int value) ! 63: { ! 64: return Add(key, std::to_string(value)); ! 65: } ! 66: ! 67: // 項目を追加 (初期状態用) ! 68: bool ! 69: Config::Add(const std::string& key, const std::string& value) ! 70: { ! 71: assert(FindItem(key) == NULL); ! 72: ! 73: list.emplace_back(key, value, ConfigItem::FromInitial, ""); ! 74: return true; ! 75: } ! 76: ! 77: // 設定ファイルの内容で上書きする ! 78: bool ! 79: Config::Update(const ConfigFile& file) ! 80: { ! 81: for (const auto& pair : file.lines) { ! 82: const std::string& line = pair.first; ! 83: int lineno = pair.second; ! 84: ! 85: auto where = string_format("%s:%d", file.filepath.c_str(), lineno); ! 86: if (UpdateLine(line, ConfigItem::FromConfig, where) == false) { ! 87: return false; ! 88: } ! 89: } ! 90: ! 91: return true; ! 92: } ! 93: ! 94: // コマンドラインオプションで更新 ! 95: bool ! 96: Config::Update(const std::vector<std::string>& options) ! 97: { ! 98: for (const auto& line : options) { ! 99: if (UpdateLine(line, ConfigItem::FromOption, "") == false) { ! 100: return false; ! 101: } ! 102: } ! 103: ! 104: return true; ! 105: } ! 106: ! 107: // 更新1行分の共通処理。 ! 108: // キーが見付からないのはエラーとはしない(?)。 ! 109: // エラーが起きたらエラーメッセージを表示して false を返す。 ! 110: bool ! 111: Config::UpdateLine(const std::string& line, ConfigItem::from_t from, ! 112: const std::string& where) ! 113: { ! 114: std::string key; ! 115: std::string val; ! 116: ! 117: // キーと値に分解 ! 118: if (SplitKeyVal(&key, &val, line) == false) { ! 119: std::string fromstr; ! 120: if (from == ConfigItem::FromOption) { ! 121: fromstr = "-V " + line; ! 122: } else { ! 123: fromstr = where; ! 124: } ! 125: warnx("%s: syntax error", fromstr.c_str()); ! 126: return false; ! 127: } ! 128: ! 129: // キーで検索 (なければ無視?) ! 130: ConfigItem *itemptr = FindItem(key); ! 131: if (itemptr == NULL) { ! 132: return true; ! 133: } ! 134: ! 135: // 見付かったので更新 ! 136: ConfigItem& item = *itemptr; ! 137: item.value = val; ! 138: item.from = from; ! 139: item.where = where; ! 140: ! 141: return true; ! 142: } ! 143: ! 144: // "key=val" 形式の行 line を分解するして *keyp, *valp に代入する。 ! 145: // その際 key, val 前後の空白は取り除く。 ! 146: // 失敗すれば false を返す。 ! 147: /*static*/ bool ! 148: Config::SplitKeyVal(std::string *keyp, std::string *valp, ! 149: const std::string& line) ! 150: { ! 151: assert(keyp); ! 152: assert(valp); ! 153: ! 154: // キーと値に分解 ! 155: auto pos = line.find('='); ! 156: if (pos == std::string::npos) { ! 157: return false; ! 158: } ! 159: auto key = line.substr(0, pos++); ! 160: auto val = line.substr(pos, line.size() - pos); ! 161: ! 162: string_rtrim(key); ! 163: string_rtrim(val); ! 164: *keyp = string_ltrim(key); ! 165: *valp = string_ltrim(val); ! 166: ! 167: return true; ! 168: } ! 169: ! 170: // 内容を表示する ! 171: void ! 172: Config::Show() const ! 173: { ! 174: for (const auto& item : list) { ! 175: // ドットから始まるのは隠し設定で、初期値のままなら隠す ! 176: if (item.key[0] == '.' && item.from == ConfigItem::FromInitial) { ! 177: continue; ! 178: } ! 179: ! 180: std::string buf = string_format("%s=\"%s\"", ! 181: item.key.c_str(), item.value.c_str()); ! 182: if (buf.size() < 40) { ! 183: int n = ((40 - buf.size()) + 7) / 8; ! 184: buf.append(n, '\t'); ! 185: } else { ! 186: buf.push_back('\t'); ! 187: } ! 188: ! 189: std::string fromstr; ! 190: switch (item.from) { ! 191: case ConfigItem::FromInitial: ! 192: fromstr = "initial value"; ! 193: break; ! 194: case ConfigItem::FromConfig: ! 195: fromstr = item.where; ! 196: break; ! 197: case ConfigItem::FromOption: ! 198: fromstr = "-V option"; ! 199: break; ! 200: default: ! 201: __unreachable(); ! 202: } ! 203: ! 204: printf("%s(%s)\n", buf.c_str(), fromstr.c_str()); ! 205: } ! 206: } ! 207: ! 208: // key から ConfigItem を取得する。見付からなければ assert する。 ! 209: const ConfigItem& ! 210: Config::Get(const std::string& key) const ! 211: { ! 212: for (const auto& item : list) { ! 213: if (key == item.key) { ! 214: return item; ! 215: } ! 216: } ! 217: assertmsg(0, "Config::Get() \"%s\" not found\n", key.c_str()); ! 218: } ! 219: ! 220: // key から ConfigItem (編集可能) を取得する。見付からなければ assert する。 ! 221: ConfigItem& ! 222: Config::Get(const std::string& key) ! 223: { ! 224: for (auto& item : list) { ! 225: if (key == item.key) { ! 226: return item; ! 227: } ! 228: } ! 229: assertmsg(0, "Config::Get() \"%s\" not found\n", key.c_str()); ! 230: } ! 231: ! 232: // key から ConfigItem を返す。見付からなければ NULL を返す。 ! 233: // (こっちは内部で変更するために使う) ! 234: ConfigItem * ! 235: Config::FindItem(const std::string& key) ! 236: { ! 237: for (auto& item : list) { ! 238: if (key == item.key) { ! 239: return &item; ! 240: } ! 241: } ! 242: return NULL; ! 243: } ! 244: ! 245: ! 246: // ! 247: // 設定項目 ! 248: // ! 249: ! 250: // デフォルト値を差し替える ! 251: void ! 252: ConfigItem::SetDefault(int val) ! 253: { ! 254: std::string valstr = std::to_string(val); ! 255: SetDefault(valstr); ! 256: } ! 257: ! 258: // デフォルト値を差し替える ! 259: void ! 260: ConfigItem::SetDefault(const std::string& val) ! 261: { ! 262: if (from == FromInitial) { ! 263: value = val; ! 264: } ! 265: } ! 266: ! 267: void ! 268: ConfigItem::Err() const ! 269: { ! 270: // 定型文 ! 271: Err("invalid argument"); ! 272: } ! 273: ! 274: void ! 275: ConfigItem::Err(const char *fmt, ...) const ! 276: { ! 277: char buf[1024]; ! 278: va_list ap; ! 279: ! 280: va_start(ap, fmt); ! 281: vsnprintf(buf, sizeof(buf), fmt, ap); ! 282: va_end(ap); ! 283: ! 284: std::string msg(buf); ! 285: Err(msg); ! 286: } ! 287: ! 288: void ! 289: ConfigItem::Err(const std::string& msg) const ! 290: { ! 291: std::string fromstr; ! 292: ! 293: switch (from) { ! 294: case FromInitial: ! 295: fromstr = "initial value:"; ! 296: break; ! 297: case FromConfig: ! 298: fromstr = where + ":"; ! 299: break; ! 300: case FromOption: ! 301: fromstr = "option -V"; ! 302: break; ! 303: default: ! 304: __unreachable(); ! 305: } ! 306: warnx("%s %s=%s: %s", ! 307: fromstr.c_str(), key.c_str(), value.c_str(), msg.c_str()); ! 308: } ! 309: ! 310: ! 311: // ! 312: // 設定ファイル ! 313: // ! 314: ! 315: // コンストラクタ ! 316: ConfigFile::ConfigFile(const std::string& filepath_) ! 317: { ! 318: filepath = filepath_; ! 319: } ! 320: ! 321: // デストラクタ ! 322: ConfigFile::~ConfigFile() ! 323: { ! 324: } ! 325: ! 326: // ファイルの読み込み ! 327: bool ! 328: ConfigFile::Load() ! 329: { ! 330: char buf[1024]; ! 331: FILE *fp; ! 332: ! 333: fp = fopen(filepath.c_str(), "r"); ! 334: if (fp == NULL) { ! 335: warn("%s", filepath.c_str()); ! 336: return false; ! 337: } ! 338: ! 339: for (int line = 1; fgets(buf, sizeof(buf), fp) != NULL; line++) { ! 340: // 空行とコメント行を無視 ! 341: rtrim(buf); ! 342: if (buf[0] == '#') { ! 343: buf[0] = '\0'; ! 344: } ! 345: if (buf[0] == '\0') { ! 346: continue; ! 347: } ! 348: ! 349: // ここでは記憶するだけ ! 350: lines.emplace_back(buf, line); ! 351: } ! 352: ! 353: fclose(fp); ! 354: return true; ! 355: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.