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