|
|
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"
1.1.1.11 root 12: #include "fdc.h"
1.1 root 13: #include "mystring.h"
14:
1.1.1.10 root 15: // グローバル参照用
16: Config *gConfig;
1.1 root 17:
18: // コンストラクタ
19: Config::Config()
20: {
21: // 設定項目はすべてここで用意すること。
22: // ここで追加した順に表示されるので基本的にはアルファベット順に並べる
23: // (先頭のドットは考慮しなくていい)。
24: // ただし vmtype だけは特別なので最初に表示されたほうがいいだろう。
25:
26: Add("vmtype", "");
27:
28: // 以下概ねアルファベット順に並べる
29:
1.1.1.11 root 30: // X68030 CGROM ファイルパス。なければ内蔵 CGROM。
31: Add("cgrom-image", "", VMCap::X68030);
32:
1.1.1.7 root 33: // VM 内の時計を同期する時間軸。
1.1.1.18! root 34: // "real" か "virtual"。初期値は各 VM クラスで設定している。
! 35: Add("clock-sync");
1.1.1.7 root 36:
1.1.1.10 root 37: // デバッガコンソール
38: Add("debugger-driver", "none");
39: Add("debugger-tcp-port", 0);
40: // デバッガコンソールの fallback は常に不可とする。hostcom.cpp 参照。
41: // "debugger-fallback"
1.1.1.7 root 42:
1.1.1.12 root 43: // DIPSW エイリアス
44: Add("dipsw-autoboot", "", VMCap::LUNA | VMCap::NEWS);
45: Add("dipsw-serial", "", VMCap::LUNA | VMCap::NEWS);
46:
1.1.1.7 root 47: // MAC アドレス
1.1.1.13 root 48: Add("ethernet0-macaddr", "auto");
1.1.1.14 root 49: Add("ethernet1-macaddr", "auto", VMCap::X68030);
1.1.1.13 root 50: AddAlias("ethernet-macaddr", "ethernet0-macaddr");
1.1.1.12 root 51:
1.1.1.14 root 52: // 実行ファイル(-X)
53: Add("exec-file");
54: Add("exec-arg", "", VMCap::LUNA | VMCap::X68030);
55: // RAMDISK (--initrd)
56: Add("exec-initrd", "", VMCap::VIRT68K);
57:
1.1.1.16 root 58: // virt-m68k のブートパラメータ文字列。BootInfo の BI_COMMANDLINE で、
59: // NetBSD/virt68k ではルートデバイスの決定に使っている。
60: Add("exec-bootparam", "", VMCap::VIRT68K);
61:
1.1.1.12 root 62: // X68030 の拡張メモリサイズ
63: Add("extram-size", "0", VMCap::X68030);
1.1.1.7 root 64:
1.1.1.11 root 65: // フロッピーディスク
66: Add("fd-drive", 2, VMCap::X68030);
67: for (int id = 0; id < FDCDevice::MAX_DRIVE; id++) {
68: // 種別とイメージパス
1.1.1.15 root 69: Add(string_format("fd%u-image", id), VMCap::X68030);
1.1.1.11 root 70: // エミュレータ的に書き込みを禁止するか
1.1.1.15 root 71: Add(string_format("fd%u-writeignore", id), 0, VMCap::X68030);
1.1.1.11 root 72: }
73:
1.1.1.16 root 74: // 68030 の FPU の有無。
75: Add("fpu-type", "68881", VMCap::VIRT68K | VMCap::X68030);
1.1.1.9 root 76:
1.1.1.12 root 77: // 高速モード。
78: Add("fast-mode", "0");
79:
1.1.1.14 root 80: // ホスト CPU アクセラレーション。
81: Add("host-avx2", "auto");
1.1.1.15 root 82: Add("host-neon", "auto");
83:
84: // CPU アフィニティ (解釈が複雑すぎるので隠しオプション)。
85: // "" … 指定しない
86: // auto … 予約。現状は "" と同じ。
87: // low:<list> … Light スレッドを <list> の CPU に割り付ける。
88: // high:<list> … Heavy スレッドを <list> の CPU に割り付ける。
89: // perf:<list> … Heavy スレッドを <list> の CPU に、
90: // Light スレッドをその反集合の CPU に割り付ける。
91: Add(".host-cpu-affinity", "auto");
1.1.1.14 root 92:
1.1.1.10 root 93: // シリアルポートのホスト側設定
94: Add("hostcom-driver", "none");
95: Add("hostcom-fallback", 0);
96: Add("hostcom-tcp-port", 9998);
97:
1.1.1.11 root 98: // キーボードの入力モード
1.1.1.14 root 99: Add("hostkbd-input", "char", VMCap::LUNA | VMCap::X68030);
1.1.1.11 root 100:
1.1.1.7 root 101: // ネットワークのホスト側設定。
1.1.1.14 root 102: // (0 と 1 で -driver の初期値が違う)
103: Add("hostnet0-driver", "auto");
104: Add("hostnet0-fallback", 0);
105: Add("hostnet0-afpacket-ifname", "auto");
106: Add("hostnet0-bpf-ifname", "auto");
107: Add("hostnet0-tap-devpath", "auto");
1.1.1.17 root 108: Add("hostnet0-usermode-hostfwd","");
109: Add("hostnet0-usermode-net", "10.8.0.0/24");
110: Add("hostnet0-usermode-net6", "fd08::/64");
1.1.1.14 root 111:
112: Add("hostnet1-driver", "none", VMCap::X68030);
113: Add("hostnet1-fallback", 0, VMCap::X68030);
114: Add("hostnet1-afpacket-ifname", "auto", VMCap::X68030);
115: Add("hostnet1-bpf-ifname", "auto", VMCap::X68030);
116: Add("hostnet1-tap-devpath", "auto", VMCap::X68030);
1.1.1.17 root 117: Add("hostnet1-usermode-hostfwd","", VMCap::X68030);
118: Add("hostnet1-usermode-net", "10.8.0.0/24", VMCap::X68030);
119: Add("hostnet1-usermode-net6", "fd08::/64", VMCap::X68030);
1.1.1.14 root 120:
1.1.1.13 root 121: AddAlias("hostnet-driver", "hostnet0-driver");
122: AddAlias("hostnet-fallback", "hostnet0-fallback");
123: AddAlias("hostnet-afpacket-ifname", "hostnet0-afpacket-ifname");
124: AddAlias("hostnet-bpf-ifname", "hostnet0-bpf-ifname");
125: AddAlias("hostnet-tap-devpath", "hostnet0-tap-devpath");
1.1.1.17 root 126: AddAlias("hostnet-usermode-hostfwd","hostnet0-usermode-hostfwd");
127: AddAlias("hostnet-usermode-net", "hostnet0-usermode-net");
128: AddAlias("hostnet-usermode-net6", "hostnet0-usermode-net6");
1.1.1.7 root 129:
1.1.1.11 root 130: // IPLROM1/2 ファイルパス。なければ内蔵 ROM。
131: Add("iplrom1-image", "", VMCap::X68030);
132: Add("iplrom2-image", "", VMCap::X68030);
133:
1.1.1.10 root 134: // キーボードの接続
1.1.1.14 root 135: Add("keyboard-connect", 1, VMCap::LUNA | VMCap::X68030);
1.1.1.10 root 136:
1.1.1.8 root 137: // LUNA の RTC(MK48T02) がうるう年でない 1970 をエポックとして
138: // 設定しているので、そちらに合わせてうるう年を補正する
1.1.1.11 root 139: Add("luna-adjust-misused-epoch", 1, VMCap::LUNA);
140:
141: // LUNA の DIPSW (1 が UP、0 が DOWN)。
142: // 初期値は機種によって異なるため pio.cpp で設定している。
143: Add("luna-dipsw1", VMCap::LUNA);
144: Add("luna-dipsw2", VMCap::LUNA);
1.1.1.8 root 145:
1.1.1.10 root 146: // LUNA ビデオボードプレーン数
1.1.1.12 root 147: Add("luna-video-plane", 4, VMCap::LUNA);
1.1.1.10 root 148:
1.1.1.8 root 149: // m88100 の別名ニーモニックを併記する。
1.1.1.11 root 150: Add(".m88k-altname", 0, VMCap::M88K);
1.1.1.8 root 151:
1.1.1.15 root 152: // m88100/88200 のバージョン。
153: // 初期値はコンストラクタ側で用意している。
154: Add("m88100-version", 0, VMCap::M88K);
155: Add("m88200-version", 0, VMCap::M88K);
156:
1.1.1.12 root 157: // 画面サイズ。
158: Add("mainview-scale", "1");
159:
160: // モニタウィンドウ(等)のフォントサイズ
161: Add("monitor-fontsize", 12);
162:
1.1.1.7 root 163: // モニタウィンドウの更新頻度 [Hz]
1.1.1.3 root 164: Add("monitor-rate", 20);
1.1.1.7 root 165:
166: // MPU クロック (MHz 単位の実数、小数点以下は3桁まで)。
167: // 初期値は各 VM クラスで設定している。
1.1 root 168: Add("mpu-clock");
1.1.1.7 root 169:
170: // M88100 で疑似 STOP 状態をサポートする
1.1.1.11 root 171: Add("mpu-pseudo-stop", 1, VMCap::M88K);
1.1.1.7 root 172:
1.1.1.16 root 173: // MPU 種別。現状 m68k のみ。初期値は機種依存。
174: Add("mpu-type", "", VMCap::M68K);
175:
1.1.1.13 root 176: // Nereid
177: Add("nereid0-enable", 0, VMCap::X68030);
178: Add("nereid1-enable", 0, VMCap::X68030);
179:
180: // Nereid イーサネット
181: Add("nereid0-net", 1, VMCap::X68030);
182: Add("nereid1-net", 1, VMCap::X68030);
183:
184: // Nereid バンクメモリ容量 (0, 4, 16 もしくは -4, -16)
185: Add("nereid0-ram-size", 16, VMCap::X68030);
186: Add("nereid1-ram-size", 16, VMCap::X68030);
187:
1.1.1.12 root 188: // NEWS の DIPSW (1 がオン、0 がオフ)。
189: Add("news-dipsw", "00001000", VMCap::NEWS);
190:
191: // NEWS の SIC を無視する。(暫定)
1.1.1.13 root 192: // これは新旧で処置が変わるのでエイリアスにしていない。
1.1.1.12 root 193: Add("xxx-news-sic-ignore", "0", VMCap::NEWS);
194: Add("xxx-news-sci-ignore", "", VMCap::NEWS);
195:
1.1.1.7 root 196: // PROM ファイルパス。なければ内蔵 ROM 起動。
1.1.1.11 root 197: Add("prom-image", "", VMCap::LUNA);
1.1.1.7 root 198:
199: // PROM のデバッグオプション。
200: // PROM 中の SCSI デバッグビットを立てる。
1.1.1.11 root 201: Add(".prom-scsi-debug", 0, VMCap::LUNA1);
1.1.1.7 root 202:
203: // RAM サイズ (MB 単位の整数)。
204: // 初期値は機種ごとの VM クラスで設定している。
1.1.1.2 root 205: Add("ram-size", 0);
1.1.1.7 root 206:
207: // RTC のデバッグ用オプション。
1.1 root 208: Add(".rtc-force-fixed", 0);
1.1.1.8 root 209: Add(".rtc-date");
210: Add(".rtc-time");
1.1.1.7 root 211:
1.1.1.10 root 212: // RTC の epoch。初期値は各 VM クラスで設定している。
1.1.1.12 root 213: Add("rtc-epoch-year", VMCap::LUNA | VMCap::NEWS);
1.1.1.10 root 214:
1.1.1.7 root 215: // ステータスパネルを表示する。
1.1.1.6 root 216: Add("show-statuspanel", 1);
1.1.1.7 root 217:
218: // SCSI 設定。
1.1 root 219: for (int id = 0; id < 8; id++) {
1.1.1.14 root 220: auto scsicap = VMCap::LUNA | VMCap::X68030;
1.1.1.7 root 221: // 種別とイメージパス
1.1.1.15 root 222: Add(string_format("spc0-id%u-image", id), scsicap);
1.1.1.7 root 223: // エミュレータ的に書き込みを禁止するか
1.1.1.15 root 224: Add(string_format("spc0-id%u-writeignore", id), 0, scsicap);
1.1.1.7 root 225: // シークタイム [msec]
1.1.1.15 root 226: Add(string_format("spc0-id%u-seektime", id), 0, scsicap);
1.1 root 227: }
1.1.1.11 root 228:
229: // SRAM の RAM 容量を同期するか。
230: Add("sram-sync-ramsize", 1, VMCap::X68030);
1.1.1.14 root 231:
232: // virtio 設定。
1.1.1.18! root 233: auto viocap = VMCap::VIRT68K;
1.1.1.14 root 234: for (int id = 0; id < 8; id++) {
235: // イメージパス
1.1.1.15 root 236: Add(string_format("virtio-block%u-image", id), viocap);
1.1.1.14 root 237: // エミュレータ的に書き込みを禁止するか
1.1.1.15 root 238: Add(string_format("virtio-block%u-writeignore", id), 0, viocap);
1.1.1.14 root 239: }
1.1.1.18! root 240: for (int id = 0; id < 8; id++) {
! 241: // 種別とイメージパス
! 242: Add(string_format("virtio-scsi-id%u-image", id), viocap);
! 243: // エミュレータ的に書き込みを禁止するか
! 244: Add(string_format("virtio-scsi-id%u-writeignore", id), 0, viocap);
! 245: }
1.1.1.14 root 246:
247: // Windrv パス。
248: Add("windrv-path", "", VMCap::X68030);
249:
250: // X68030 で FC2 ピンを切るか。
251: Add("x68k-cut-fc2", 1, VMCap::X68030);
1.1.1.16 root 252:
253: // XP プロセッサのクロック (MHz 単位の実数、小数点以下は3桁まで)。
254: Add("xp-clock", "6.144", VMCap::LUNA);
1.1 root 255: }
256:
257: // デストラクタ
258: Config::~Config()
259: {
1.1.1.10 root 260: gConfig = NULL;
1.1 root 261: }
262:
263: // 項目を追加 (初期状態用)。値は空文字列。
264: bool
1.1.1.11 root 265: Config::Add(const std::string& key, VMCap vmcap)
1.1 root 266: {
1.1.1.11 root 267: return Add(key, "", vmcap);
1.1 root 268: }
269:
270: // 項目を追加 (初期状態用)。値は整数値を文字列にしたもの。
271: bool
1.1.1.11 root 272: Config::Add(const std::string& key, int value, VMCap vmcap)
1.1 root 273: {
1.1.1.11 root 274: return Add(key, std::to_string(value), vmcap);
1.1 root 275: }
276:
277: // 項目を追加 (初期状態用)
278: bool
1.1.1.11 root 279: Config::Add(const std::string& key, const std::string& value, VMCap vmcap)
1.1 root 280: {
281: assert(FindItem(key) == NULL);
1.1.1.2 root 282: assert(fixed == false);
1.1 root 283:
1.1.1.11 root 284: list.emplace_back(key, value, vmcap, ConfigItem::FromInitial, "");
1.1 root 285: return true;
286: }
287:
1.1.1.13 root 288: // エイリアスを追加
289: bool
290: Config::AddAlias(const std::string& alias, const std::string& key)
291: {
292: // すでに同名のエイリアスがあれば、プログラムミス
293: for (const auto& p : aliases) {
294: if (alias == p.first) {
295: PANIC("aliases already has %s !", alias.c_str());
296: }
297: }
298:
299: aliases.emplace_back(alias, key);
300: return true;
301: }
302:
1.1.1.4 root 303: // 項目を削除
304: // key が見付からなくても何もしない。key は複数登録されていることはないはず。
305: void
306: Config::Delete(const std::string& key)
307: {
308: for (auto it = list.begin(); it != list.end(); ++it) {
309: auto item = *it;
310: if (key == item.key) {
311: list.erase(it);
312: return;
313: }
314: }
315: assert(FindItem(key) == NULL);
316: }
317:
1.1 root 318: // 設定ファイルの内容で上書きする
319: bool
320: Config::Update(const ConfigFile& file)
321: {
1.1.1.2 root 322: assert(fixed == false);
323:
1.1 root 324: for (const auto& pair : file.lines) {
325: const std::string& line = pair.first;
1.1.1.15 root 326: uint lineno = pair.second;
1.1 root 327:
1.1.1.15 root 328: auto where = string_format("%s:%u", file.filepath.c_str(), lineno);
1.1.1.14 root 329: if (UpdateLine(line, file.from, where) == false) {
1.1 root 330: return false;
331: }
332: }
333:
334: return true;
335: }
336:
1.1.1.15 root 337: #define UpdateLinePerf(str) do { \
338: if (UpdateLine(str, ConfigItem::FromPerf, where) == false) \
339: return false; \
340: } while (0)
341:
1.1 root 342: // コマンドラインオプションで更新
343: bool
1.1.1.12 root 344: Config::Update(const std::string& line, const std::string& optname)
1.1 root 345: {
1.1.1.2 root 346: assert(fixed == false);
347:
1.1.1.15 root 348: // パフォーマンス測定用オプション --perf が指定されたら
349: // line="--perf" という非正規の書式で呼ばれるので、ここで展開する。
350: if (line == "--perf") {
351: // -V prom-image=PROM.DAT はパスの問題があるので、各自で追加指定
352: // する必要がある。
353: std::string where = "--perf -V";
354: UpdateLinePerf(".rtc-force-fixed=1");
355: UpdateLinePerf("clock-sync=virtual");
356: UpdateLinePerf("fast-mode=1");
357: UpdateLinePerf("fd0-writeignore=1");
358: UpdateLinePerf("fd1-writeignore=1");
359: UpdateLinePerf("fd2-writeignore=1");
360: UpdateLinePerf("fd3-writeignore=1");
361: UpdateLinePerf("ethernet-macaddr=02:00:00:00:00:01");
362: UpdateLinePerf("hostcom-driver=none");
363: UpdateLinePerf("hostnet-driver=none");
364: UpdateLinePerf("luna-dipsw1=11110111");
365: UpdateLinePerf("luna-video-plane=4");
366: UpdateLinePerf("spc0-id0-writeignore=1");
367: UpdateLinePerf("spc0-id1-writeignore=1");
368: UpdateLinePerf("spc0-id2-writeignore=1");
369: UpdateLinePerf("spc0-id3-writeignore=1");
370: UpdateLinePerf("spc0-id4-writeignore=1");
371: UpdateLinePerf("spc0-id5-writeignore=1");
372: UpdateLinePerf("spc0-id6-writeignore=1");
373: UpdateLinePerf("virtio-block0-writeignore=1");
374: UpdateLinePerf("virtio-block1-writeignore=1");
375: UpdateLinePerf("virtio-block2-writeignore=1");
376: UpdateLinePerf("virtio-block3-writeignore=1");
377: UpdateLinePerf("virtio-block4-writeignore=1");
378: UpdateLinePerf("virtio-block5-writeignore=1");
379: UpdateLinePerf("virtio-block6-writeignore=1");
380: return true;
381: } else {
382: return UpdateLine(line, ConfigItem::FromOption, optname);
383: }
1.1 root 384: }
385:
386: // 更新1行分の共通処理。
1.1.1.18! root 387: // キーが見付からない場合は from によって動作が異なる。
! 388: // o 共通設定ファイルでなら、何もせず true を返す。
! 389: // o VM 設定ファイルでなら、エラーメッセージを表示するが true を返す。
! 390: // o コマンドラインでなら、エラーメッセージを表示して false を返す。
! 391: // 戻り値 false は実行中止を示す。
1.1 root 392: bool
393: Config::UpdateLine(const std::string& line, ConfigItem::from_t from,
394: const std::string& where)
395: {
396: std::string key;
397: std::string val;
398:
399: // キーと値に分解
400: if (SplitKeyVal(&key, &val, line) == false) {
1.1.1.12 root 401: ConfigItem::PrintErr(from, where, "", line, "Syntax error");
1.1 root 402: return false;
403: }
404:
1.1.1.13 root 405: // キーが別名なら正規名に置換
406: key = GetCanonKey(key);
407:
1.1.1.10 root 408: // キーで検索
1.1 root 409: ConfigItem *itemptr = FindItem(key);
410: if (itemptr == NULL) {
1.1.1.14 root 411: switch (from) {
412: default:
413: case ConfigItem::FromHome:
1.1.1.15 root 414: case ConfigItem::FromPerf:
1.1.1.14 root 415: // 共通設定ファイルなら黙って無視する。
416: // このファイルには他機種向けの設定も並んでいる可能性があるため
417: // 無視してくれないと困る。
418: return true;
419:
420: case ConfigItem::FromConfig:
421: // VM 設定ファイルなら警告のみで無視する。
422: // このファイルに他機種向けの設定があるのはおかしいが
423: // 止めるほどではない。
424: ConfigItem::PrintErr(from, where, key, val, "Unknown key, ignored");
425: return true;
426:
427: case ConfigItem::FromOption:
428: // コマンドラインからの場合はエラーにする。
1.1.1.12 root 429: ConfigItem::PrintErr(from, where, key, val, "Unknown key");
1.1.1.10 root 430: return false;
431: }
1.1 root 432: }
433:
434: // 見付かったので更新
435: ConfigItem& item = *itemptr;
436: item.value = val;
437: item.from = from;
438: item.where = where;
439:
440: return true;
441: }
442:
443: // "key=val" 形式の行 line を分解するして *keyp, *valp に代入する。
444: // その際 key, val 前後の空白は取り除く。
445: // 失敗すれば false を返す。
446: /*static*/ bool
447: Config::SplitKeyVal(std::string *keyp, std::string *valp,
448: const std::string& line)
449: {
450: assert(keyp);
451: assert(valp);
452:
453: // キーと値に分解
454: auto pos = line.find('=');
455: if (pos == std::string::npos) {
456: return false;
457: }
458: auto key = line.substr(0, pos++);
459: auto val = line.substr(pos, line.size() - pos);
460:
1.1.1.8 root 461: *keyp = string_trim(key);
462: *valp = string_trim(val);
1.1 root 463:
464: return true;
465: }
466:
1.1.1.13 root 467: // キーがエイリアスなら正規名を返す。
468: // エイリアスでなければキーをそのまま返す。
469: const std::string&
470: Config::GetCanonKey(const std::string& key) const
471: {
472: for (const auto& p : aliases) {
473: const auto& alias = p.first;
474: const auto& canon = p.second;
475:
476: if (key == alias) {
477: return canon;
478: }
479: }
480:
481: return key;
482: }
483:
1.1.1.2 root 484: // 内容を表示する。
485: // all なら隠し設定も含めて表示。
1.1 root 486: void
1.1.1.2 root 487: Config::Show(bool all) const
1.1 root 488: {
1.1.1.2 root 489: assert(fixed);
490:
1.1 root 491: for (const auto& item : list) {
1.1.1.2 root 492: // '!' から始まるのは内部用変数なので隠す
493: if (!all && item.key[0] == '!') {
494: continue;
495: }
1.1 root 496: // ドットから始まるのは隠し設定で、初期値のままなら隠す
1.1.1.2 root 497: if (!all &&
498: item.key[0] == '.' && item.from == ConfigItem::FromInitial) {
1.1 root 499: continue;
500: }
501:
502: std::string buf = string_format("%s=\"%s\"",
503: item.key.c_str(), item.value.c_str());
504: if (buf.size() < 40) {
1.1.1.15 root 505: uint n = ((40 - buf.size()) + 7) / 8;
1.1 root 506: buf.append(n, '\t');
507: } else {
508: buf.push_back('\t');
509: }
510:
511: std::string fromstr;
512: switch (item.from) {
513: case ConfigItem::FromInitial:
514: fromstr = "initial value";
515: break;
1.1.1.14 root 516: case ConfigItem::FromHome:
1.1 root 517: case ConfigItem::FromConfig:
518: fromstr = item.where;
519: break;
520: case ConfigItem::FromOption:
1.1.1.12 root 521: fromstr = "option " + item.where;
1.1 root 522: break;
1.1.1.15 root 523: case ConfigItem::FromPerf:
524: fromstr = "option --perf";
525: break;
1.1 root 526: default:
1.1.1.15 root 527: fromstr = string_format("corrupted item.from=%u for key=%s",
528: (uint)item.from, item.key.c_str());
1.1.1.12 root 529: break;
1.1 root 530: }
531:
532: printf("%s(%s)\n", buf.c_str(), fromstr.c_str());
533: }
534: }
535:
536: // key から ConfigItem を取得する。見付からなければ assert する。
537: const ConfigItem&
1.1.1.2 root 538: Config::Find(const std::string& key) const
1.1 root 539: {
540: for (const auto& item : list) {
541: if (key == item.key) {
542: return item;
543: }
544: }
1.1.1.10 root 545: PANIC("Config::Find() \"%s\" not found", key.c_str());
1.1.1.2 root 546: }
547:
548: // key のデフォルト値を差し替える。key が見付からなければ assert する。
549: void
550: Config::SetDefault(const std::string& key, int val)
551: {
552: std::string valstr = std::to_string(val);
553: SetDefault(key, valstr);
1.1 root 554: }
555:
1.1.1.2 root 556: // key のデフォルト値を差し替える。key が見付からなければ assert する。
557: void
558: Config::SetDefault(const std::string& key, const std::string& val)
1.1 root 559: {
1.1.1.2 root 560: assert(fixed == false);
561:
1.1 root 562: for (auto& item : list) {
563: if (key == item.key) {
1.1.1.2 root 564: item.SetDefault(val);
565: return;
1.1 root 566: }
567: }
1.1.1.2 root 568: assertmsg(0, "Config::SetDefault() \"%s\" not found\n", key.c_str());
1.1 root 569: }
570:
571: // key から ConfigItem を返す。見付からなければ NULL を返す。
572: // (こっちは内部で変更するために使う)
573: ConfigItem *
574: Config::FindItem(const std::string& key)
575: {
576: for (auto& item : list) {
577: if (key == item.key) {
578: return &item;
579: }
580: }
581: return NULL;
582: }
583:
1.1.1.12 root 584: // これ以降は書き込み禁止。
585: bool
1.1.1.2 root 586: Config::Fix()
587: {
588: assert(fixed == false);
589:
1.1.1.11 root 590: // ここで、この機種に不要な変数を削除する。
1.1.1.18! root 591: // その変数がユーザから指定されていればエラーにする。
1.1.1.11 root 592: for (auto it = list.begin(); it != list.end(); ) {
593: ConfigItem& item = *it;
594: VMCap vmcap = item.GetVMCap();
595: if (gMainApp.Has(vmcap) == false) {
1.1.1.12 root 596: // この機種用ではない
1.1.1.14 root 597: switch (item.from) {
598: case ConfigItem::FromConfig:
599: case ConfigItem::FromOption:
600: // 明示的に更新されていればエラー
1.1.1.12 root 601: item.Err("Not supported in this vmtype");
602: return false;
1.1.1.14 root 603: default:
604: break;
1.1.1.12 root 605: }
606:
1.1.1.11 root 607: it = list.erase(it);
608: } else {
609: ++it;
610: }
611: }
612:
1.1.1.2 root 613: fixed = true;
1.1.1.12 root 614: return true;
1.1.1.2 root 615: }
616:
1.1 root 617:
618: //
619: // 設定項目
620: //
621:
1.1.1.10 root 622: // 値を整数値として取得してみる。
623: bool
624: ConfigItem::TryInt(int *val) const
625: {
626: char *endp;
627:
628: if (value.empty()) {
629: return false;
630: }
631:
632: errno = 0;
633: int r = strtol(value.c_str(), &endp, 10);
634: if (errno == ERANGE || *endp != '\0') {
635: return false;
636: }
637: *val = r;
638: return true;
639: }
640:
1.1.1.12 root 641: // 値を double 値として取得してみる。
642: bool
643: ConfigItem::TryDouble(double *val) const
644: {
645: char *endp;
646:
647: if (value.empty()) {
648: return false;
649: }
650:
651: errno = 0;
652: double res = strtod(value.c_str(), &endp);
653: if (errno) {
654: return false;
655: }
656: *val = res;
657: return true;
658: }
659:
1.1.1.16 root 660: // b を e 乗した整数を返す。
661: static inline int
662: ipow(int b, int e)
1.1.1.15 root 663: {
664: int rv = 1;
1.1.1.16 root 665:
1.1.1.15 root 666: for (int i = 0; i < e; i++) {
667: rv *= b;
668: }
669: return rv;
670: }
671:
672: // 値を固定小数点値として取得してみる。
1.1.1.16 root 673: // digit は小数点以下の桁数で、それ以下は四捨五入する。
674: // 値が "1.2345" で digit=3 なら *val = 1235 を返す。
1.1.1.15 root 675: bool
676: ConfigItem::TryFixedDecimal(int *val, int digit) const
677: {
678: char *endp;
679: bool neg = false;
680:
681: if (value.empty()) {
682: return false;
683: }
684:
685: errno = 0;
686: int r = strtol(value.c_str(), &endp, 10);
687: if (errno == ERANGE || !(*endp == '\0' || *endp == '.')) {
688: return false;
689: }
690: if (r < 0) {
691: r = -r;
692: neg = true;
693: }
694: r *= ipow(10, digit);
695: if (*endp == '.') {
696: char *p = endp + 1;
697: errno = 0;
698: int f = strtol(p, &endp, 10);
699: if (errno == ERANGE || *endp != '\0') {
700: return false;
701: }
702: int n = endp - p;
703: if (n <= digit) {
704: r += f * ipow(10, digit - n);
705: } else {
706: r += f / ipow(10, n - digit) + (p[digit] >= '5');
707: }
708: }
709: *val = neg ? -r : r;
710: return true;
711: }
712:
1.1 root 713: // デフォルト値を差し替える
714: void
715: ConfigItem::SetDefault(const std::string& val)
716: {
717: if (from == FromInitial) {
718: value = val;
719: }
720: }
721:
722: void
723: ConfigItem::Err() const
724: {
725: // 定型文
1.1.1.12 root 726: Err("Invalid argument");
1.1 root 727: }
728:
729: void
730: ConfigItem::Err(const char *fmt, ...) const
731: {
732: char buf[1024];
733: va_list ap;
734:
735: va_start(ap, fmt);
736: vsnprintf(buf, sizeof(buf), fmt, ap);
737: va_end(ap);
738:
739: std::string msg(buf);
740: Err(msg);
741: }
742:
743: void
744: ConfigItem::Err(const std::string& msg) const
745: {
1.1.1.10 root 746: PrintErr(from, where, key, value, msg);
747: }
748:
1.1.1.12 root 749: // この item についてエラーを表示する。
750: // key=val の '=' がない場合のみ key を空、value を行全体として呼ぶ。
1.1.1.10 root 751: /*static*/ void
752: ConfigItem::PrintErr(ConfigItem::from_t from, const std::string& where,
753: const std::string& key, const std::string& value, const std::string& msg)
754: {
1.1 root 755: std::string fromstr;
756:
757: switch (from) {
758: case FromInitial:
1.1.1.12 root 759: // 初期値の場合 key が空なことは起きないはず。
760: // というか初期値でエラーが起きることも普通はないはず。
761: fromstr = "initial value: " + key + "=" + value;
1.1 root 762: break;
1.1.1.12 root 763:
1.1.1.14 root 764: case FromHome:
1.1 root 765: case FromConfig:
1.1.1.12 root 766: // 設定ファイルでのエラー。where はファイル名と行番号。
767: fromstr = where + ": ";
768: if (key.empty() == false) {
769: fromstr += key;
1.1.1.13 root 770: fromstr += "=";
1.1.1.12 root 771: }
772: fromstr += value;
1.1 root 773: break;
1.1.1.12 root 774:
1.1 root 775: case FromOption:
1.1.1.15 root 776: case FromPerf:
1.1.1.12 root 777: // コマンドラインオプションの場合は where は
778: // "--fontsize" とか "-V" なので、
779: // "-V" なら (key =) value を追加し、
780: // それ以外なら value のみ追加がいいか。
781: fromstr = "option " + where + " ";
1.1.1.14 root 782: if (where.find("-V") != std::string::npos) {
1.1.1.12 root 783: if (key.empty() == false) {
784: fromstr += key;
785: fromstr += "=";
786: }
787: }
788: fromstr += value;
1.1 root 789: break;
1.1.1.10 root 790:
1.1.1.12 root 791: default:
1.1.1.15 root 792: fromstr = string_format("corrupted item.from=%u for key=%s",
793: (uint)from, key.c_str());
1.1.1.12 root 794: break;
1.1.1.10 root 795: }
796:
1.1.1.12 root 797: warnx("%s: %s", fromstr.c_str(), msg.c_str());
1.1 root 798: }
799:
800:
801: //
802: // 設定ファイル
803: //
804:
805: // コンストラクタ
1.1.1.14 root 806: ConfigFile::ConfigFile(const std::string& filepath_, ConfigItem::from_t from_)
1.1 root 807: {
808: filepath = filepath_;
1.1.1.14 root 809: from = from_;
1.1 root 810: }
811:
812: // デストラクタ
813: ConfigFile::~ConfigFile()
814: {
815: }
816:
1.1.1.12 root 817: // ファイルの読み込み。
818: // 成功すれば true を、失敗すればメッセージを出力して false を返す。
819: // ファイルがないのは正常とする (なければスルーするため)。
820: // ファイルがあって読めなければ失敗 (エラー終了したいので)。
1.1 root 821: bool
822: ConfigFile::Load()
823: {
824: char buf[1024];
825: FILE *fp;
826:
1.1.1.15 root 827: if (filepath.empty()) {
828: return true;
829: }
830:
1.1 root 831: fp = fopen(filepath.c_str(), "r");
832: if (fp == NULL) {
1.1.1.12 root 833: if (errno == ENOENT) {
834: return true;
835: }
1.1 root 836: warn("%s", filepath.c_str());
837: return false;
838: }
839:
840: for (int line = 1; fgets(buf, sizeof(buf), fp) != NULL; line++) {
841: // 空行とコメント行を無視
842: rtrim(buf);
843: if (buf[0] == '#') {
844: buf[0] = '\0';
845: }
846: if (buf[0] == '\0') {
847: continue;
848: }
849:
850: // ここでは記憶するだけ
851: lines.emplace_back(buf, line);
852: }
853:
854: fclose(fp);
855: return true;
856: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.