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