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