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