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