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