|
|
1.1 root 1: //
2: // nono
1.1.1.2 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.10! root 7: //
! 8: // 設定
! 9: //
! 10:
1.1 root 11: #include "config.h"
12: #include "mystring.h"
13:
1.1.1.10! root 14: // グローバル参照用
! 15: Config *gConfig;
1.1 root 16:
17: // コンストラクタ
18: Config::Config()
19: {
20: // 設定項目はすべてここで用意すること。
21: // ここで追加した順に表示されるので基本的にはアルファベット順に並べる
22: // (先頭のドットは考慮しなくていい)。
23: // ただし vmtype だけは特別なので最初に表示されたほうがいいだろう。
24:
25: Add("vmtype", "");
26:
27: // 以下概ねアルファベット順に並べる
28:
1.1.1.7 root 29: // VM 内の時計を同期する時間軸。
30: // [ real, virtual ]
31: Add("clock-sync", "real");
32:
1.1.1.10! root 33: // デバッガコンソール
! 34: Add("debugger-driver", "none");
! 35: Add("debugger-tcp-port", 0);
! 36: // デバッガコンソールの fallback は常に不可とする。hostcom.cpp 参照。
! 37: // "debugger-fallback"
1.1.1.7 root 38:
39: // MAC アドレス
1.1 root 40: Add("ethernet-macaddr", "auto");
1.1.1.7 root 41:
1.1.1.9 root 42: // キーボードの入力モード
43: Add("hostkbd-input", "char");
44:
1.1.1.10! root 45: // シリアルポートのホスト側設定
! 46: Add("hostcom-driver", "none");
! 47: Add("hostcom-fallback", 0);
! 48: Add("hostcom-tcp-port", 9998);
! 49:
1.1.1.7 root 50: // ネットワークのホスト側設定。
51: Add("hostnet-driver", "auto");
52: Add("hostnet-fallback", 0);
53: Add("hostnet-afpacket-ifname", "auto");
54: Add("hostnet-bpf-ifname", "auto");
55: Add("hostnet-tap-devpath", "auto");
56:
1.1.1.10! root 57: // キーボードの接続
! 58: Add("keyboard-connect", 1);
! 59:
1.1.1.7 root 60: // LUNA の DIPSW (1 が UP、0 が DOWN)
61: // 初期値は機種によって異なるため pio.cpp で設定している。
1.1.1.3 root 62: Add("luna-dipsw1");
63: Add("luna-dipsw2");
1.1.1.7 root 64:
1.1.1.8 root 65: // LUNA の RTC(MK48T02) がうるう年でない 1970 をエポックとして
66: // 設定しているので、そちらに合わせてうるう年を補正する
67: Add("luna-adjust-misused-epoch", 1);
68:
1.1.1.10! root 69: // LUNA ビデオボードプレーン数
! 70: Add("luna-video-plane", 1);
! 71:
1.1.1.8 root 72: // m88100 の別名ニーモニックを併記する。
73: Add(".m88k-altname", 0);
74:
1.1.1.7 root 75: // モニタウィンドウの更新頻度 [Hz]
1.1.1.3 root 76: Add("monitor-rate", 20);
1.1.1.7 root 77:
78: // MPU クロック (MHz 単位の実数、小数点以下は3桁まで)。
79: // 初期値は各 VM クラスで設定している。
1.1 root 80: Add("mpu-clock");
1.1.1.7 root 81:
82: // X68030 時の FPU の有無。
83: // LUNA-I では VM 初期化時に削除する。
1.1 root 84: Add("mpu-has-fpu", 0);
1.1.1.7 root 85:
86: // M88100 で疑似 STOP 状態をサポートする
87: Add("mpu-pseudo-stop", 1);
88:
89: // PROM ファイルパス。なければ内蔵 ROM 起動。
1.1.1.2 root 90: Add("prom-image", "");
1.1.1.7 root 91:
92: // PROM のデバッグオプション。
93: // PROM 中の SCSI デバッグビットを立てる。
1.1 root 94: Add(".prom-scsi-debug", 0);
1.1.1.7 root 95:
96: // RAM サイズ (MB 単位の整数)。
97: // 初期値は機種ごとの VM クラスで設定している。
1.1.1.2 root 98: Add("ram-size", 0);
1.1.1.7 root 99:
100: // RTC のデバッグ用オプション。
1.1 root 101: Add(".rtc-force-fixed", 0);
1.1.1.8 root 102: Add(".rtc-date");
103: Add(".rtc-time");
1.1.1.7 root 104:
1.1.1.10! root 105: // RTC の epoch。初期値は各 VM クラスで設定している。
! 106: Add("rtc-epoch-year");
! 107:
1.1.1.7 root 108: // ステータスパネルを表示する。
1.1.1.6 root 109: Add("show-statuspanel", 1);
1.1.1.7 root 110:
111: // SCSI 設定。
1.1 root 112: for (int id = 0; id < 8; id++) {
1.1.1.7 root 113: // 種別とイメージパス
1.1 root 114: Add(string_format("spc0-id%d-image", id));
1.1.1.7 root 115: // エミュレータ的に書き込みを禁止するか
1.1.1.8 root 116: Add(string_format("spc0-id%d-writeignore", id), 0);
117: // エミュレータ的に書き込みを禁止するか
118: // (後方互換、十分不要になった頃に削除する)
1.1 root 119: Add(string_format("spc0-id%d-writeprotect", id), 0);
1.1.1.7 root 120: // シークタイム [msec]
1.1.1.4 root 121: Add(string_format("spc0-id%d-seektime", id), 0);
1.1 root 122: }
123: }
124:
125: // デストラクタ
126: Config::~Config()
127: {
1.1.1.10! root 128: gConfig = NULL;
1.1 root 129: }
130:
131: // 項目を追加 (初期状態用)。値は空文字列。
132: bool
133: Config::Add(const std::string& key)
134: {
135: return Add(key, "");
136: }
137:
138: // 項目を追加 (初期状態用)。値は整数値を文字列にしたもの。
139: bool
140: Config::Add(const std::string& key, int value)
141: {
142: return Add(key, std::to_string(value));
143: }
144:
145: // 項目を追加 (初期状態用)
146: bool
147: Config::Add(const std::string& key, const std::string& value)
148: {
149: assert(FindItem(key) == NULL);
1.1.1.2 root 150: assert(fixed == false);
1.1 root 151:
152: list.emplace_back(key, value, ConfigItem::FromInitial, "");
153: return true;
154: }
155:
1.1.1.4 root 156: // 項目を削除
157: // key が見付からなくても何もしない。key は複数登録されていることはないはず。
158: void
159: Config::Delete(const std::string& key)
160: {
161: for (auto it = list.begin(); it != list.end(); ++it) {
162: auto item = *it;
163: if (key == item.key) {
164: list.erase(it);
165: return;
166: }
167: }
168: assert(FindItem(key) == NULL);
169: }
170:
1.1 root 171: // 設定ファイルの内容で上書きする
172: bool
173: Config::Update(const ConfigFile& file)
174: {
1.1.1.2 root 175: assert(fixed == false);
176:
1.1 root 177: for (const auto& pair : file.lines) {
178: const std::string& line = pair.first;
179: int lineno = pair.second;
180:
181: auto where = string_format("%s:%d", file.filepath.c_str(), lineno);
182: if (UpdateLine(line, ConfigItem::FromConfig, where) == false) {
183: return false;
184: }
185: }
186:
187: return true;
188: }
189:
190: // コマンドラインオプションで更新
191: bool
192: Config::Update(const std::vector<std::string>& options)
193: {
1.1.1.2 root 194: assert(fixed == false);
195:
1.1 root 196: for (const auto& line : options) {
197: if (UpdateLine(line, ConfigItem::FromOption, "") == false) {
198: return false;
199: }
200: }
201:
202: return true;
203: }
204:
205: // 更新1行分の共通処理。
1.1.1.10! root 206: // キーが見付からない場合、コマンドラインからならエラーとし、
! 207: // それ以外からの場合は警告のみで無視する。
1.1 root 208: // エラーが起きたらエラーメッセージを表示して false を返す。
209: bool
210: Config::UpdateLine(const std::string& line, ConfigItem::from_t from,
211: const std::string& where)
212: {
213: std::string key;
214: std::string val;
215:
216: // キーと値に分解
217: if (SplitKeyVal(&key, &val, line) == false) {
1.1.1.10! root 218: ConfigItem::PrintErr(from, where, "", line, "syntax error");
1.1 root 219: return false;
220: }
221:
1.1.1.10! root 222: // キーで検索
1.1 root 223: ConfigItem *itemptr = FindItem(key);
224: if (itemptr == NULL) {
1.1.1.10! root 225: // コマンドラインからの場合はエラーにする
! 226: if (from == ConfigItem::FromOption) {
! 227: ConfigItem::PrintErr(from, where, key, val, "unknown key");
! 228: return false;
! 229: }
! 230: // コマンドライン以外の場合は警告のみで無視する
! 231: ConfigItem::PrintErr(from, where, key, val, "unknown key, ignored");
1.1 root 232: return true;
233: }
234:
235: // 見付かったので更新
236: ConfigItem& item = *itemptr;
237: item.value = val;
238: item.from = from;
239: item.where = where;
240:
241: return true;
242: }
243:
244: // "key=val" 形式の行 line を分解するして *keyp, *valp に代入する。
245: // その際 key, val 前後の空白は取り除く。
246: // 失敗すれば false を返す。
247: /*static*/ bool
248: Config::SplitKeyVal(std::string *keyp, std::string *valp,
249: const std::string& line)
250: {
251: assert(keyp);
252: assert(valp);
253:
254: // キーと値に分解
255: auto pos = line.find('=');
256: if (pos == std::string::npos) {
257: return false;
258: }
259: auto key = line.substr(0, pos++);
260: auto val = line.substr(pos, line.size() - pos);
261:
1.1.1.8 root 262: *keyp = string_trim(key);
263: *valp = string_trim(val);
1.1 root 264:
265: return true;
266: }
267:
1.1.1.2 root 268: // 内容を表示する。
269: // all なら隠し設定も含めて表示。
1.1 root 270: void
1.1.1.2 root 271: Config::Show(bool all) const
1.1 root 272: {
1.1.1.2 root 273: assert(fixed);
274:
1.1 root 275: for (const auto& item : list) {
1.1.1.2 root 276: // '!' から始まるのは内部用変数なので隠す
277: if (!all && item.key[0] == '!') {
278: continue;
279: }
1.1 root 280: // ドットから始まるのは隠し設定で、初期値のままなら隠す
1.1.1.2 root 281: if (!all &&
282: item.key[0] == '.' && item.from == ConfigItem::FromInitial) {
1.1 root 283: continue;
284: }
285:
286: std::string buf = string_format("%s=\"%s\"",
287: item.key.c_str(), item.value.c_str());
288: if (buf.size() < 40) {
289: int n = ((40 - buf.size()) + 7) / 8;
290: buf.append(n, '\t');
291: } else {
292: buf.push_back('\t');
293: }
294:
295: std::string fromstr;
296: switch (item.from) {
297: case ConfigItem::FromInitial:
298: fromstr = "initial value";
299: break;
300: case ConfigItem::FromConfig:
301: fromstr = item.where;
302: break;
303: case ConfigItem::FromOption:
304: fromstr = "-V option";
305: break;
306: default:
307: __unreachable();
308: }
309:
310: printf("%s(%s)\n", buf.c_str(), fromstr.c_str());
311: }
312: }
313:
314: // key から ConfigItem を取得する。見付からなければ assert する。
315: const ConfigItem&
1.1.1.2 root 316: Config::Find(const std::string& key) const
1.1 root 317: {
318: for (const auto& item : list) {
319: if (key == item.key) {
320: return item;
321: }
322: }
1.1.1.10! root 323: PANIC("Config::Find() \"%s\" not found", key.c_str());
1.1.1.2 root 324: }
325:
326: // key のデフォルト値を差し替える。key が見付からなければ assert する。
327: void
328: Config::SetDefault(const std::string& key, int val)
329: {
330: std::string valstr = std::to_string(val);
331: SetDefault(key, valstr);
1.1 root 332: }
333:
1.1.1.2 root 334: // key のデフォルト値を差し替える。key が見付からなければ assert する。
335: void
336: Config::SetDefault(const std::string& key, const std::string& val)
1.1 root 337: {
1.1.1.2 root 338: assert(fixed == false);
339:
1.1 root 340: for (auto& item : list) {
341: if (key == item.key) {
1.1.1.2 root 342: item.SetDefault(val);
343: return;
1.1 root 344: }
345: }
1.1.1.2 root 346: assertmsg(0, "Config::SetDefault() \"%s\" not found\n", key.c_str());
1.1 root 347: }
348:
349: // key から ConfigItem を返す。見付からなければ NULL を返す。
350: // (こっちは内部で変更するために使う)
351: ConfigItem *
352: Config::FindItem(const std::string& key)
353: {
354: for (auto& item : list) {
355: if (key == item.key) {
356: return &item;
357: }
358: }
359: return NULL;
360: }
361:
1.1.1.2 root 362: // これ以降は書き込み禁止
363: void
364: Config::Fix()
365: {
366: assert(fixed == false);
367:
368: fixed = true;
369: }
370:
1.1 root 371:
372: //
373: // 設定項目
374: //
375:
1.1.1.10! root 376: // 値を整数値として取得してみる。
! 377: bool
! 378: ConfigItem::TryInt(int *val) const
! 379: {
! 380: char *endp;
! 381:
! 382: if (value.empty()) {
! 383: return false;
! 384: }
! 385:
! 386: errno = 0;
! 387: int r = strtol(value.c_str(), &endp, 10);
! 388: if (errno == ERANGE || *endp != '\0') {
! 389: return false;
! 390: }
! 391: *val = r;
! 392: return true;
! 393: }
! 394:
1.1 root 395: // デフォルト値を差し替える
396: void
397: ConfigItem::SetDefault(const std::string& val)
398: {
399: if (from == FromInitial) {
400: value = val;
401: }
402: }
403:
404: void
405: ConfigItem::Err() const
406: {
407: // 定型文
408: Err("invalid argument");
409: }
410:
411: void
412: ConfigItem::Err(const char *fmt, ...) const
413: {
414: char buf[1024];
415: va_list ap;
416:
417: va_start(ap, fmt);
418: vsnprintf(buf, sizeof(buf), fmt, ap);
419: va_end(ap);
420:
421: std::string msg(buf);
422: Err(msg);
423: }
424:
425: void
426: ConfigItem::Err(const std::string& msg) const
427: {
1.1.1.10! root 428: PrintErr(from, where, key, value, msg);
! 429: }
! 430:
! 431: /*static*/ void
! 432: ConfigItem::PrintErr(ConfigItem::from_t from, const std::string& where,
! 433: const std::string& key, const std::string& value, const std::string& msg)
! 434: {
1.1 root 435: std::string fromstr;
1.1.1.10! root 436: std::string keyvalue;
1.1 root 437:
438: switch (from) {
439: case FromInitial:
440: fromstr = "initial value:";
441: break;
442: case FromConfig:
443: fromstr = where + ":";
444: break;
445: case FromOption:
446: fromstr = "option -V";
447: break;
448: default:
449: __unreachable();
450: }
1.1.1.10! root 451:
! 452: if (key.empty()) {
! 453: keyvalue = value;
! 454: } else {
! 455: keyvalue = key + "=" + value;
! 456: }
! 457:
! 458: warnx("%s %s: %s", fromstr.c_str(), keyvalue.c_str(), msg.c_str());
1.1 root 459: }
460:
461:
462: //
463: // 設定ファイル
464: //
465:
466: // コンストラクタ
467: ConfigFile::ConfigFile(const std::string& filepath_)
468: {
469: filepath = filepath_;
470: }
471:
472: // デストラクタ
473: ConfigFile::~ConfigFile()
474: {
475: }
476:
477: // ファイルの読み込み
478: bool
479: ConfigFile::Load()
480: {
481: char buf[1024];
482: FILE *fp;
483:
484: fp = fopen(filepath.c_str(), "r");
485: if (fp == NULL) {
486: warn("%s", filepath.c_str());
487: return false;
488: }
489:
490: for (int line = 1; fgets(buf, sizeof(buf), fp) != NULL; line++) {
491: // 空行とコメント行を無視
492: rtrim(buf);
493: if (buf[0] == '#') {
494: buf[0] = '\0';
495: }
496: if (buf[0] == '\0') {
497: continue;
498: }
499:
500: // ここでは記憶するだけ
501: lines.emplace_back(buf, line);
502: }
503:
504: fclose(fp);
505: return true;
506: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.