|
|
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:
7: #include "config.h"
8: #include "mystring.h"
9:
10: std::unique_ptr<Config> gConfig;
11:
12: //
13: // 設定クラス
14: //
15:
16: // コンストラクタ
17: Config::Config()
18: {
19: // 設定項目はすべてここで用意すること。
20: // ここで追加した順に表示されるので基本的にはアルファベット順に並べる
21: // (先頭のドットは考慮しなくていい)。
22: // ただし vmtype だけは特別なので最初に表示されたほうがいいだろう。
23:
24: Add("vmtype", "");
25:
26: // 以下概ねアルファベット順に並べる
27:
1.1.1.7 root 28: // VM 内の時計を同期する時間軸。
29: // [ real, virtual ]
30: Add("clock-sync", "real");
31:
32: // デバッガの TCP 待ち受けポート
1.1.1.4 root 33: Add("debugger-port", 0);
1.1.1.7 root 34:
35: // MAC アドレス
1.1 root 36: Add("ethernet-macaddr", "auto");
1.1.1.7 root 37:
1.1.1.9 ! root 38: // キーボードの入力モード
! 39: Add("hostkbd-input", "char");
! 40:
1.1.1.7 root 41: // ネットワークのホスト側設定。
42: Add("hostnet-driver", "auto");
43: Add("hostnet-fallback", 0);
44: Add("hostnet-afpacket-ifname", "auto");
45: Add("hostnet-bpf-ifname", "auto");
46: Add("hostnet-tap-devpath", "auto");
47:
48: // LUNA の DIPSW (1 が UP、0 が DOWN)
49: // 初期値は機種によって異なるため pio.cpp で設定している。
1.1.1.3 root 50: Add("luna-dipsw1");
51: Add("luna-dipsw2");
1.1.1.7 root 52:
1.1.1.8 root 53: // LUNA の RTC(MK48T02) がうるう年でない 1970 をエポックとして
54: // 設定しているので、そちらに合わせてうるう年を補正する
55: Add("luna-adjust-misused-epoch", 1);
56:
57: // m88100 の別名ニーモニックを併記する。
58: Add(".m88k-altname", 0);
59:
1.1.1.7 root 60: // モニタウィンドウの更新頻度 [Hz]
1.1.1.3 root 61: Add("monitor-rate", 20);
1.1.1.7 root 62:
63: // MPU クロック (MHz 単位の実数、小数点以下は3桁まで)。
64: // 初期値は各 VM クラスで設定している。
1.1 root 65: Add("mpu-clock");
1.1.1.7 root 66:
67: // X68030 時の FPU の有無。
68: // LUNA-I では VM 初期化時に削除する。
1.1 root 69: Add("mpu-has-fpu", 0);
1.1.1.7 root 70:
71: // M88100 で疑似 STOP 状態をサポートする
72: Add("mpu-pseudo-stop", 1);
73:
74: // PROM ファイルパス。なければ内蔵 ROM 起動。
1.1.1.2 root 75: Add("prom-image", "");
1.1.1.7 root 76:
77: // PROM のデバッグオプション。
78: // PROM 中の SCSI デバッグビットを立てる。
1.1 root 79: Add(".prom-scsi-debug", 0);
1.1.1.7 root 80:
81: // RAM サイズ (MB 単位の整数)。
82: // 初期値は機種ごとの VM クラスで設定している。
1.1.1.2 root 83: Add("ram-size", 0);
1.1.1.7 root 84:
85: // RTC のデバッグ用オプション。
1.1 root 86: Add(".rtc-force-fixed", 0);
1.1.1.8 root 87: Add(".rtc-date");
88: Add(".rtc-time");
1.1.1.7 root 89:
90: // ステータスパネルを表示する。
1.1.1.6 root 91: Add("show-statuspanel", 1);
1.1.1.7 root 92:
93: // SCSI 設定。
1.1 root 94: for (int id = 0; id < 8; id++) {
1.1.1.7 root 95: // 種別とイメージパス
1.1 root 96: Add(string_format("spc0-id%d-image", id));
1.1.1.7 root 97: // エミュレータ的に書き込みを禁止するか
1.1.1.8 root 98: Add(string_format("spc0-id%d-writeignore", id), 0);
99: // エミュレータ的に書き込みを禁止するか
100: // (後方互換、十分不要になった頃に削除する)
1.1 root 101: Add(string_format("spc0-id%d-writeprotect", id), 0);
1.1.1.7 root 102: // シークタイム [msec]
1.1.1.4 root 103: Add(string_format("spc0-id%d-seektime", id), 0);
1.1 root 104: }
105: }
106:
107: // デストラクタ
108: Config::~Config()
109: {
110: }
111:
112: // 項目を追加 (初期状態用)。値は空文字列。
113: bool
114: Config::Add(const std::string& key)
115: {
116: return Add(key, "");
117: }
118:
119: // 項目を追加 (初期状態用)。値は整数値を文字列にしたもの。
120: bool
121: Config::Add(const std::string& key, int value)
122: {
123: return Add(key, std::to_string(value));
124: }
125:
126: // 項目を追加 (初期状態用)
127: bool
128: Config::Add(const std::string& key, const std::string& value)
129: {
130: assert(FindItem(key) == NULL);
1.1.1.2 root 131: assert(fixed == false);
1.1 root 132:
133: list.emplace_back(key, value, ConfigItem::FromInitial, "");
134: return true;
135: }
136:
1.1.1.4 root 137: // 項目を削除
138: // key が見付からなくても何もしない。key は複数登録されていることはないはず。
139: void
140: Config::Delete(const std::string& key)
141: {
142: for (auto it = list.begin(); it != list.end(); ++it) {
143: auto item = *it;
144: if (key == item.key) {
145: list.erase(it);
146: return;
147: }
148: }
149: assert(FindItem(key) == NULL);
150: }
151:
1.1 root 152: // 設定ファイルの内容で上書きする
153: bool
154: Config::Update(const ConfigFile& file)
155: {
1.1.1.2 root 156: assert(fixed == false);
157:
1.1 root 158: for (const auto& pair : file.lines) {
159: const std::string& line = pair.first;
160: int lineno = pair.second;
161:
162: auto where = string_format("%s:%d", file.filepath.c_str(), lineno);
163: if (UpdateLine(line, ConfigItem::FromConfig, where) == false) {
164: return false;
165: }
166: }
167:
168: return true;
169: }
170:
171: // コマンドラインオプションで更新
172: bool
173: Config::Update(const std::vector<std::string>& options)
174: {
1.1.1.2 root 175: assert(fixed == false);
176:
1.1 root 177: for (const auto& line : options) {
178: if (UpdateLine(line, ConfigItem::FromOption, "") == false) {
179: return false;
180: }
181: }
182:
183: return true;
184: }
185:
186: // 更新1行分の共通処理。
187: // キーが見付からないのはエラーとはしない(?)。
188: // エラーが起きたらエラーメッセージを表示して false を返す。
189: bool
190: Config::UpdateLine(const std::string& line, ConfigItem::from_t from,
191: const std::string& where)
192: {
193: std::string key;
194: std::string val;
195:
196: // キーと値に分解
197: if (SplitKeyVal(&key, &val, line) == false) {
198: std::string fromstr;
199: if (from == ConfigItem::FromOption) {
200: fromstr = "-V " + line;
201: } else {
202: fromstr = where;
203: }
204: warnx("%s: syntax error", fromstr.c_str());
205: return false;
206: }
207:
208: // キーで検索 (なければ無視?)
209: ConfigItem *itemptr = FindItem(key);
210: if (itemptr == NULL) {
211: return true;
212: }
213:
214: // 見付かったので更新
215: ConfigItem& item = *itemptr;
216: item.value = val;
217: item.from = from;
218: item.where = where;
219:
220: return true;
221: }
222:
223: // "key=val" 形式の行 line を分解するして *keyp, *valp に代入する。
224: // その際 key, val 前後の空白は取り除く。
225: // 失敗すれば false を返す。
226: /*static*/ bool
227: Config::SplitKeyVal(std::string *keyp, std::string *valp,
228: const std::string& line)
229: {
230: assert(keyp);
231: assert(valp);
232:
233: // キーと値に分解
234: auto pos = line.find('=');
235: if (pos == std::string::npos) {
236: return false;
237: }
238: auto key = line.substr(0, pos++);
239: auto val = line.substr(pos, line.size() - pos);
240:
1.1.1.8 root 241: *keyp = string_trim(key);
242: *valp = string_trim(val);
1.1 root 243:
244: return true;
245: }
246:
1.1.1.2 root 247: // 内容を表示する。
248: // all なら隠し設定も含めて表示。
1.1 root 249: void
1.1.1.2 root 250: Config::Show(bool all) const
1.1 root 251: {
1.1.1.2 root 252: assert(fixed);
253:
1.1 root 254: for (const auto& item : list) {
1.1.1.2 root 255: // '!' から始まるのは内部用変数なので隠す
256: if (!all && item.key[0] == '!') {
257: continue;
258: }
1.1 root 259: // ドットから始まるのは隠し設定で、初期値のままなら隠す
1.1.1.2 root 260: if (!all &&
261: item.key[0] == '.' && item.from == ConfigItem::FromInitial) {
1.1 root 262: continue;
263: }
264:
265: std::string buf = string_format("%s=\"%s\"",
266: item.key.c_str(), item.value.c_str());
267: if (buf.size() < 40) {
268: int n = ((40 - buf.size()) + 7) / 8;
269: buf.append(n, '\t');
270: } else {
271: buf.push_back('\t');
272: }
273:
274: std::string fromstr;
275: switch (item.from) {
276: case ConfigItem::FromInitial:
277: fromstr = "initial value";
278: break;
279: case ConfigItem::FromConfig:
280: fromstr = item.where;
281: break;
282: case ConfigItem::FromOption:
283: fromstr = "-V option";
284: break;
285: default:
286: __unreachable();
287: }
288:
289: printf("%s(%s)\n", buf.c_str(), fromstr.c_str());
290: }
291: }
292:
293: // key から ConfigItem を取得する。見付からなければ assert する。
294: const ConfigItem&
1.1.1.2 root 295: Config::Find(const std::string& key) const
1.1 root 296: {
297: for (const auto& item : list) {
298: if (key == item.key) {
299: return item;
300: }
301: }
1.1.1.2 root 302: assertmsg(0, "Config::Find() \"%s\" not found\n", key.c_str());
303: }
304:
305: // key のデフォルト値を差し替える。key が見付からなければ assert する。
306: void
307: Config::SetDefault(const std::string& key, int val)
308: {
309: std::string valstr = std::to_string(val);
310: SetDefault(key, valstr);
1.1 root 311: }
312:
1.1.1.2 root 313: // key のデフォルト値を差し替える。key が見付からなければ assert する。
314: void
315: Config::SetDefault(const std::string& key, const std::string& val)
1.1 root 316: {
1.1.1.2 root 317: assert(fixed == false);
318:
1.1 root 319: for (auto& item : list) {
320: if (key == item.key) {
1.1.1.2 root 321: item.SetDefault(val);
322: return;
1.1 root 323: }
324: }
1.1.1.2 root 325: assertmsg(0, "Config::SetDefault() \"%s\" not found\n", key.c_str());
1.1 root 326: }
327:
328: // key から ConfigItem を返す。見付からなければ NULL を返す。
329: // (こっちは内部で変更するために使う)
330: ConfigItem *
331: Config::FindItem(const std::string& key)
332: {
333: for (auto& item : list) {
334: if (key == item.key) {
335: return &item;
336: }
337: }
338: return NULL;
339: }
340:
1.1.1.2 root 341: // これ以降は書き込み禁止
342: void
343: Config::Fix()
344: {
345: assert(fixed == false);
346:
347: fixed = true;
348: }
349:
1.1 root 350:
351: //
352: // 設定項目
353: //
354:
355: // デフォルト値を差し替える
356: void
357: ConfigItem::SetDefault(const std::string& val)
358: {
359: if (from == FromInitial) {
360: value = val;
361: }
362: }
363:
364: void
365: ConfigItem::Err() const
366: {
367: // 定型文
368: Err("invalid argument");
369: }
370:
371: void
372: ConfigItem::Err(const char *fmt, ...) const
373: {
374: char buf[1024];
375: va_list ap;
376:
377: va_start(ap, fmt);
378: vsnprintf(buf, sizeof(buf), fmt, ap);
379: va_end(ap);
380:
381: std::string msg(buf);
382: Err(msg);
383: }
384:
385: void
386: ConfigItem::Err(const std::string& msg) const
387: {
388: std::string fromstr;
389:
390: switch (from) {
391: case FromInitial:
392: fromstr = "initial value:";
393: break;
394: case FromConfig:
395: fromstr = where + ":";
396: break;
397: case FromOption:
398: fromstr = "option -V";
399: break;
400: default:
401: __unreachable();
402: }
403: warnx("%s %s=%s: %s",
404: fromstr.c_str(), key.c_str(), value.c_str(), msg.c_str());
405: }
406:
407:
408: //
409: // 設定ファイル
410: //
411:
412: // コンストラクタ
413: ConfigFile::ConfigFile(const std::string& filepath_)
414: {
415: filepath = filepath_;
416: }
417:
418: // デストラクタ
419: ConfigFile::~ConfigFile()
420: {
421: }
422:
423: // ファイルの読み込み
424: bool
425: ConfigFile::Load()
426: {
427: char buf[1024];
428: FILE *fp;
429:
430: fp = fopen(filepath.c_str(), "r");
431: if (fp == NULL) {
432: warn("%s", filepath.c_str());
433: return false;
434: }
435:
436: for (int line = 1; fgets(buf, sizeof(buf), fp) != NULL; line++) {
437: // 空行とコメント行を無視
438: rtrim(buf);
439: if (buf[0] == '#') {
440: buf[0] = '\0';
441: }
442: if (buf[0] == '\0') {
443: continue;
444: }
445:
446: // ここでは記憶するだけ
447: lines.emplace_back(buf, line);
448: }
449:
450: fclose(fp);
451: return true;
452: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.