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