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