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