Annotation of nono/lib/config.cpp, revision 1.1.1.9

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.