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