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