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

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

unix.superglobalmegacorp.com

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