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

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: 
1.1.1.12  root       43:        // DIPSW エイリアス
                     44:        Add("dipsw-autoboot", "", VMCap::LUNA | VMCap::NEWS);
                     45:        Add("dipsw-serial", "", VMCap::LUNA | VMCap::NEWS);
                     46: 
1.1.1.7   root       47:        // MAC アドレス
1.1.1.13  root       48:        Add("ethernet0-macaddr", "auto");
1.1.1.14  root       49:        Add("ethernet1-macaddr", "auto", VMCap::X68030);
1.1.1.13  root       50:        AddAlias("ethernet-macaddr", "ethernet0-macaddr");
1.1.1.12  root       51: 
1.1.1.14  root       52:        // 実行ファイル(-X)
                     53:        Add("exec-file");
                     54:        Add("exec-arg", "", VMCap::LUNA | VMCap::X68030);
                     55:        // RAMDISK (--initrd)
                     56:        Add("exec-initrd", "", VMCap::VIRT68K);
                     57: 
1.1.1.12  root       58:        // X68030 の拡張メモリサイズ
                     59:        Add("extram-size", "0", VMCap::X68030);
1.1.1.7   root       60: 
1.1.1.11  root       61:        // フロッピーディスク
                     62:        Add("fd-drive", 2, VMCap::X68030);
                     63:        for (int id = 0; id < FDCDevice::MAX_DRIVE; id++) {
                     64:                // 種別とイメージパス
1.1.1.15! root       65:                Add(string_format("fd%u-image", id), VMCap::X68030);
1.1.1.11  root       66:                // エミュレータ的に書き込みを禁止するか
1.1.1.15! root       67:                Add(string_format("fd%u-writeignore", id), 0, VMCap::X68030);
1.1.1.11  root       68:        }
                     69: 
                     70:        // X68030 時の FPU の有無。
                     71:        Add("fpu-type", "68881", VMCap::X68030);
1.1.1.9   root       72: 
1.1.1.12  root       73:        // 高速モード。
                     74:        Add("fast-mode", "0");
                     75: 
1.1.1.14  root       76:        // ホスト CPU アクセラレーション。
                     77:        Add("host-avx2", "auto");
1.1.1.15! root       78:        Add("host-neon", "auto");
        !            79: 
        !            80:        // CPU アフィニティ (解釈が複雑すぎるので隠しオプション)。
        !            81:        // "" … 指定しない
        !            82:        // auto … 予約。現状は "" と同じ。
        !            83:        // low:<list>  … Light スレッドを <list> の CPU に割り付ける。
        !            84:        // high:<list> … Heavy スレッドを <list> の CPU に割り付ける。
        !            85:        // perf:<list> … Heavy スレッドを <list> の CPU に、
        !            86:        //                Light スレッドをその反集合の CPU に割り付ける。
        !            87:        Add(".host-cpu-affinity", "auto");
1.1.1.14  root       88: 
1.1.1.10  root       89:        // シリアルポートのホスト側設定
                     90:        Add("hostcom-driver", "none");
                     91:        Add("hostcom-fallback", 0);
                     92:        Add("hostcom-tcp-port", 9998);
                     93: 
1.1.1.11  root       94:        // キーボードの入力モード
1.1.1.14  root       95:        Add("hostkbd-input", "char", VMCap::LUNA | VMCap::X68030);
1.1.1.11  root       96: 
1.1.1.7   root       97:        // ネットワークのホスト側設定。
1.1.1.14  root       98:        // (0 と 1 で -driver の初期値が違う)
                     99:        Add("hostnet0-driver",                  "auto");
                    100:        Add("hostnet0-fallback",                0);
                    101:        Add("hostnet0-afpacket-ifname", "auto");
                    102:        Add("hostnet0-bpf-ifname",              "auto");
                    103:        Add("hostnet0-tap-devpath",             "auto");
                    104: 
                    105:        Add("hostnet1-driver",                  "none", VMCap::X68030);
                    106:        Add("hostnet1-fallback",                0,              VMCap::X68030);
                    107:        Add("hostnet1-afpacket-ifname", "auto", VMCap::X68030);
                    108:        Add("hostnet1-bpf-ifname",              "auto", VMCap::X68030);
                    109:        Add("hostnet1-tap-devpath",             "auto", VMCap::X68030);
                    110: 
1.1.1.13  root      111:        AddAlias("hostnet-driver",                      "hostnet0-driver");
                    112:        AddAlias("hostnet-fallback",            "hostnet0-fallback");
                    113:        AddAlias("hostnet-afpacket-ifname",     "hostnet0-afpacket-ifname");
                    114:        AddAlias("hostnet-bpf-ifname",          "hostnet0-bpf-ifname");
                    115:        AddAlias("hostnet-tap-devpath",         "hostnet0-tap-devpath");
1.1.1.7   root      116: 
1.1.1.11  root      117:        // IPLROM1/2 ファイルパス。なければ内蔵 ROM。
                    118:        Add("iplrom1-image", "", VMCap::X68030);
                    119:        Add("iplrom2-image", "", VMCap::X68030);
                    120: 
1.1.1.10  root      121:        // キーボードの接続
1.1.1.14  root      122:        Add("keyboard-connect", 1, VMCap::LUNA | VMCap::X68030);
1.1.1.10  root      123: 
1.1.1.8   root      124:        // LUNA の RTC(MK48T02) がうるう年でない 1970 をエポックとして
                    125:        // 設定しているので、そちらに合わせてうるう年を補正する
1.1.1.11  root      126:        Add("luna-adjust-misused-epoch", 1, VMCap::LUNA);
                    127: 
                    128:        // LUNA の DIPSW (1 が UP、0 が DOWN)。
                    129:        // 初期値は機種によって異なるため pio.cpp で設定している。
                    130:        Add("luna-dipsw1", VMCap::LUNA);
                    131:        Add("luna-dipsw2", VMCap::LUNA);
1.1.1.8   root      132: 
1.1.1.10  root      133:        // LUNA ビデオボードプレーン数
1.1.1.12  root      134:        Add("luna-video-plane", 4, VMCap::LUNA);
1.1.1.10  root      135: 
1.1.1.8   root      136:        // m88100 の別名ニーモニックを併記する。
1.1.1.11  root      137:        Add(".m88k-altname", 0, VMCap::M88K);
1.1.1.8   root      138: 
1.1.1.15! root      139:        // m88100/88200 のバージョン。
        !           140:        // 初期値はコンストラクタ側で用意している。
        !           141:        Add("m88100-version", 0, VMCap::M88K);
        !           142:        Add("m88200-version", 0, VMCap::M88K);
        !           143: 
1.1.1.12  root      144:        // 画面サイズ。
                    145:        Add("mainview-scale", "1");
                    146: 
                    147:        // モニタウィンドウ(等)のフォントサイズ
                    148:        Add("monitor-fontsize", 12);
                    149: 
1.1.1.7   root      150:        // モニタウィンドウの更新頻度 [Hz]
1.1.1.3   root      151:        Add("monitor-rate", 20);
1.1.1.7   root      152: 
                    153:        // MPU クロック (MHz 単位の実数、小数点以下は3桁まで)。
                    154:        // 初期値は各 VM クラスで設定している。
1.1       root      155:        Add("mpu-clock");
1.1.1.7   root      156: 
                    157:        // M88100 で疑似 STOP 状態をサポートする
1.1.1.11  root      158:        Add("mpu-pseudo-stop", 1, VMCap::M88K);
1.1.1.7   root      159: 
1.1.1.13  root      160:        // Nereid
                    161:        Add("nereid0-enable", 0, VMCap::X68030);
                    162:        Add("nereid1-enable", 0, VMCap::X68030);
                    163: 
                    164:        // Nereid イーサネット
                    165:        Add("nereid0-net", 1, VMCap::X68030);
                    166:        Add("nereid1-net", 1, VMCap::X68030);
                    167: 
                    168:        // Nereid バンクメモリ容量 (0, 4, 16 もしくは -4, -16)
                    169:        Add("nereid0-ram-size", 16, VMCap::X68030);
                    170:        Add("nereid1-ram-size", 16, VMCap::X68030);
                    171: 
1.1.1.12  root      172:        // NEWS の DIPSW (1 がオン、0 がオフ)。
                    173:        Add("news-dipsw", "00001000", VMCap::NEWS);
                    174: 
                    175:        // NEWS の SIC を無視する。(暫定)
1.1.1.13  root      176:        // これは新旧で処置が変わるのでエイリアスにしていない。
1.1.1.12  root      177:        Add("xxx-news-sic-ignore", "0", VMCap::NEWS);
                    178:        Add("xxx-news-sci-ignore", "", VMCap::NEWS);
                    179: 
1.1.1.7   root      180:        // PROM ファイルパス。なければ内蔵 ROM 起動。
1.1.1.11  root      181:        Add("prom-image", "", VMCap::LUNA);
1.1.1.7   root      182: 
                    183:        // PROM のデバッグオプション。
                    184:        // PROM 中の SCSI デバッグビットを立てる。
1.1.1.11  root      185:        Add(".prom-scsi-debug", 0, VMCap::LUNA1);
1.1.1.7   root      186: 
                    187:        // RAM サイズ (MB 単位の整数)。
                    188:        // 初期値は機種ごとの VM クラスで設定している。
1.1.1.2   root      189:        Add("ram-size", 0);
1.1.1.7   root      190: 
                    191:        // RTC のデバッグ用オプション。
1.1       root      192:        Add(".rtc-force-fixed", 0);
1.1.1.8   root      193:        Add(".rtc-date");
                    194:        Add(".rtc-time");
1.1.1.7   root      195: 
1.1.1.10  root      196:        // RTC の epoch。初期値は各 VM クラスで設定している。
1.1.1.12  root      197:        Add("rtc-epoch-year", VMCap::LUNA | VMCap::NEWS);
1.1.1.10  root      198: 
1.1.1.7   root      199:        // ステータスパネルを表示する。
1.1.1.6   root      200:        Add("show-statuspanel", 1);
1.1.1.7   root      201: 
                    202:        // SCSI 設定。
1.1       root      203:        for (int id = 0; id < 8; id++) {
1.1.1.14  root      204:                auto scsicap = VMCap::LUNA | VMCap::X68030;
1.1.1.7   root      205:                // 種別とイメージパス
1.1.1.15! root      206:                Add(string_format("spc0-id%u-image", id), scsicap);
1.1.1.7   root      207:                // エミュレータ的に書き込みを禁止するか
1.1.1.15! root      208:                Add(string_format("spc0-id%u-writeignore", id), 0, scsicap);
1.1.1.8   root      209:                // エミュレータ的に書き込みを禁止するか
                    210:                // (後方互換、十分不要になった頃に削除する)
1.1.1.15! root      211:                Add(string_format("spc0-id%u-writeprotect", id), 0, scsicap);
1.1.1.7   root      212:                // シークタイム [msec]
1.1.1.15! root      213:                Add(string_format("spc0-id%u-seektime", id), 0, scsicap);
1.1       root      214:        }
1.1.1.11  root      215: 
                    216:        // SRAM の RAM 容量を同期するか。
                    217:        Add("sram-sync-ramsize", 1, VMCap::X68030);
1.1.1.14  root      218: 
                    219:        // virtio 設定。
                    220:        for (int id = 0; id < 8; id++) {
                    221:                auto viocap = VMCap::VIRT68K;
                    222:                // イメージパス
1.1.1.15! root      223:                Add(string_format("virtio-block%u-image", id), viocap);
1.1.1.14  root      224:                // エミュレータ的に書き込みを禁止するか
1.1.1.15! root      225:                Add(string_format("virtio-block%u-writeignore", id), 0, viocap);
1.1.1.14  root      226:        }
                    227: 
                    228:        // Windrv パス。
                    229:        Add("windrv-path", "", VMCap::X68030);
                    230: 
                    231:        // X68030 で FC2 ピンを切るか。
                    232:        Add("x68k-cut-fc2", 1, VMCap::X68030);
1.1       root      233: }
                    234: 
                    235: // デストラクタ
                    236: Config::~Config()
                    237: {
1.1.1.10  root      238:        gConfig = NULL;
1.1       root      239: }
                    240: 
                    241: // 項目を追加 (初期状態用)。値は空文字列。
                    242: bool
1.1.1.11  root      243: Config::Add(const std::string& key, VMCap vmcap)
1.1       root      244: {
1.1.1.11  root      245:        return Add(key, "", vmcap);
1.1       root      246: }
                    247: 
                    248: // 項目を追加 (初期状態用)。値は整数値を文字列にしたもの。
                    249: bool
1.1.1.11  root      250: Config::Add(const std::string& key, int value, VMCap vmcap)
1.1       root      251: {
1.1.1.11  root      252:        return Add(key, std::to_string(value), vmcap);
1.1       root      253: }
                    254: 
                    255: // 項目を追加 (初期状態用)
                    256: bool
1.1.1.11  root      257: Config::Add(const std::string& key, const std::string& value, VMCap vmcap)
1.1       root      258: {
                    259:        assert(FindItem(key) == NULL);
1.1.1.2   root      260:        assert(fixed == false);
1.1       root      261: 
1.1.1.11  root      262:        list.emplace_back(key, value, vmcap, ConfigItem::FromInitial, "");
1.1       root      263:        return true;
                    264: }
                    265: 
1.1.1.13  root      266: // エイリアスを追加
                    267: bool
                    268: Config::AddAlias(const std::string& alias, const std::string& key)
                    269: {
                    270:        // すでに同名のエイリアスがあれば、プログラムミス
                    271:        for (const auto& p : aliases) {
                    272:                if (alias == p.first) {
                    273:                        PANIC("aliases already has %s !", alias.c_str());
                    274:                }
                    275:        }
                    276: 
                    277:        aliases.emplace_back(alias, key);
                    278:        return true;
                    279: }
                    280: 
1.1.1.4   root      281: // 項目を削除
                    282: // key が見付からなくても何もしない。key は複数登録されていることはないはず。
                    283: void
                    284: Config::Delete(const std::string& key)
                    285: {
                    286:        for (auto it = list.begin(); it != list.end(); ++it) {
                    287:                auto item = *it;
                    288:                if (key == item.key) {
                    289:                        list.erase(it);
                    290:                        return;
                    291:                }
                    292:        }
                    293:        assert(FindItem(key) == NULL);
                    294: }
                    295: 
1.1       root      296: // 設定ファイルの内容で上書きする
                    297: bool
                    298: Config::Update(const ConfigFile& file)
                    299: {
1.1.1.2   root      300:        assert(fixed == false);
                    301: 
1.1       root      302:        for (const auto& pair : file.lines) {
                    303:                const std::string& line = pair.first;
1.1.1.15! root      304:                uint lineno = pair.second;
1.1       root      305: 
1.1.1.15! root      306:                auto where = string_format("%s:%u", file.filepath.c_str(), lineno);
1.1.1.14  root      307:                if (UpdateLine(line, file.from, where) == false) {
1.1       root      308:                        return false;
                    309:                }
                    310:        }
                    311: 
                    312:        return true;
                    313: }
                    314: 
1.1.1.15! root      315: #define UpdateLinePerf(str)    do {    \
        !           316:        if (UpdateLine(str, ConfigItem::FromPerf, where) == false) \
        !           317:                return false;   \
        !           318: } while (0)
        !           319: 
1.1       root      320: // コマンドラインオプションで更新
                    321: bool
1.1.1.12  root      322: Config::Update(const std::string& line, const std::string& optname)
1.1       root      323: {
1.1.1.2   root      324:        assert(fixed == false);
                    325: 
1.1.1.15! root      326:        // パフォーマンス測定用オプション --perf が指定されたら
        !           327:        // line="--perf" という非正規の書式で呼ばれるので、ここで展開する。
        !           328:        if (line == "--perf") {
        !           329:                // -V prom-image=PROM.DAT はパスの問題があるので、各自で追加指定
        !           330:                // する必要がある。
        !           331:                std::string where = "--perf -V";
        !           332:                UpdateLinePerf(".rtc-force-fixed=1");
        !           333:                UpdateLinePerf("clock-sync=virtual");
        !           334:                UpdateLinePerf("fast-mode=1");
        !           335:                UpdateLinePerf("fd0-writeignore=1");
        !           336:                UpdateLinePerf("fd1-writeignore=1");
        !           337:                UpdateLinePerf("fd2-writeignore=1");
        !           338:                UpdateLinePerf("fd3-writeignore=1");
        !           339:                UpdateLinePerf("ethernet-macaddr=02:00:00:00:00:01");
        !           340:                UpdateLinePerf("hostcom-driver=none");
        !           341:                UpdateLinePerf("hostnet-driver=none");
        !           342:                UpdateLinePerf("luna-dipsw1=11110111");
        !           343:                UpdateLinePerf("luna-video-plane=4");
        !           344:                UpdateLinePerf("spc0-id0-writeignore=1");
        !           345:                UpdateLinePerf("spc0-id1-writeignore=1");
        !           346:                UpdateLinePerf("spc0-id2-writeignore=1");
        !           347:                UpdateLinePerf("spc0-id3-writeignore=1");
        !           348:                UpdateLinePerf("spc0-id4-writeignore=1");
        !           349:                UpdateLinePerf("spc0-id5-writeignore=1");
        !           350:                UpdateLinePerf("spc0-id6-writeignore=1");
        !           351:                UpdateLinePerf("virtio-block0-writeignore=1");
        !           352:                UpdateLinePerf("virtio-block1-writeignore=1");
        !           353:                UpdateLinePerf("virtio-block2-writeignore=1");
        !           354:                UpdateLinePerf("virtio-block3-writeignore=1");
        !           355:                UpdateLinePerf("virtio-block4-writeignore=1");
        !           356:                UpdateLinePerf("virtio-block5-writeignore=1");
        !           357:                UpdateLinePerf("virtio-block6-writeignore=1");
        !           358:                return true;
        !           359:        } else {
        !           360:                return UpdateLine(line, ConfigItem::FromOption, optname);
        !           361:        }
1.1       root      362: }
                    363: 
                    364: // 更新1行分の共通処理。
1.1.1.10  root      365: // キーが見付からない場合、コマンドラインからならエラーとし、
                    366: // それ以外からの場合は警告のみで無視する。
1.1       root      367: // エラーが起きたらエラーメッセージを表示して false を返す。
                    368: bool
                    369: Config::UpdateLine(const std::string& line, ConfigItem::from_t from,
                    370:        const std::string& where)
                    371: {
                    372:        std::string key;
                    373:        std::string val;
                    374: 
                    375:        // キーと値に分解
                    376:        if (SplitKeyVal(&key, &val, line) == false) {
1.1.1.12  root      377:                ConfigItem::PrintErr(from, where, "", line, "Syntax error");
1.1       root      378:                return false;
                    379:        }
                    380: 
1.1.1.13  root      381:        // キーが別名なら正規名に置換
                    382:        key = GetCanonKey(key);
                    383: 
1.1.1.10  root      384:        // キーで検索
1.1       root      385:        ConfigItem *itemptr = FindItem(key);
                    386:        if (itemptr == NULL) {
1.1.1.14  root      387:                switch (from) {
                    388:                 default:
                    389:                 case ConfigItem::FromHome:
1.1.1.15! root      390:                 case ConfigItem::FromPerf:
1.1.1.14  root      391:                        // 共通設定ファイルなら黙って無視する。
                    392:                        // このファイルには他機種向けの設定も並んでいる可能性があるため
                    393:                        // 無視してくれないと困る。
                    394:                        return true;
                    395: 
                    396:                 case ConfigItem::FromConfig:
                    397:                        // VM 設定ファイルなら警告のみで無視する。
                    398:                        // このファイルに他機種向けの設定があるのはおかしいが
                    399:                        // 止めるほどではない。
                    400:                        ConfigItem::PrintErr(from, where, key, val, "Unknown key, ignored");
                    401:                        return true;
                    402: 
                    403:                 case ConfigItem::FromOption:
                    404:                        // コマンドラインからの場合はエラーにする。
1.1.1.12  root      405:                        ConfigItem::PrintErr(from, where, key, val, "Unknown key");
1.1.1.10  root      406:                        return false;
                    407:                }
1.1       root      408:        }
                    409: 
                    410:        // 見付かったので更新
                    411:        ConfigItem& item = *itemptr;
                    412:        item.value = val;
                    413:        item.from  = from;
                    414:        item.where = where;
                    415: 
                    416:        return true;
                    417: }
                    418: 
                    419: // "key=val" 形式の行 line を分解するして *keyp, *valp に代入する。
                    420: // その際 key, val 前後の空白は取り除く。
                    421: // 失敗すれば false を返す。
                    422: /*static*/ bool
                    423: Config::SplitKeyVal(std::string *keyp, std::string *valp,
                    424:        const std::string& line)
                    425: {
                    426:        assert(keyp);
                    427:        assert(valp);
                    428: 
                    429:        // キーと値に分解
                    430:        auto pos = line.find('=');
                    431:        if (pos == std::string::npos) {
                    432:                return false;
                    433:        }
                    434:        auto key = line.substr(0, pos++);
                    435:        auto val = line.substr(pos, line.size() - pos);
                    436: 
1.1.1.8   root      437:        *keyp = string_trim(key);
                    438:        *valp = string_trim(val);
1.1       root      439: 
                    440:        return true;
                    441: }
                    442: 
1.1.1.13  root      443: // キーがエイリアスなら正規名を返す。
                    444: // エイリアスでなければキーをそのまま返す。
                    445: const std::string&
                    446: Config::GetCanonKey(const std::string& key) const
                    447: {
                    448:        for (const auto& p : aliases) {
                    449:                const auto& alias = p.first;
                    450:                const auto& canon = p.second;
                    451: 
                    452:                if (key == alias) {
                    453:                        return canon;
                    454:                }
                    455:        }
                    456: 
                    457:        return key;
                    458: }
                    459: 
1.1.1.2   root      460: // 内容を表示する。
                    461: // all なら隠し設定も含めて表示。
1.1       root      462: void
1.1.1.2   root      463: Config::Show(bool all) const
1.1       root      464: {
1.1.1.2   root      465:        assert(fixed);
                    466: 
1.1       root      467:        for (const auto& item : list) {
1.1.1.2   root      468:                // '!' から始まるのは内部用変数なので隠す
                    469:                if (!all && item.key[0] == '!') {
                    470:                        continue;
                    471:                }
1.1       root      472:                // ドットから始まるのは隠し設定で、初期値のままなら隠す
1.1.1.2   root      473:                if (!all &&
                    474:                    item.key[0] == '.' && item.from == ConfigItem::FromInitial) {
1.1       root      475:                        continue;
                    476:                }
                    477: 
                    478:                std::string buf = string_format("%s=\"%s\"",
                    479:                        item.key.c_str(), item.value.c_str());
                    480:                if (buf.size() < 40) {
1.1.1.15! root      481:                        uint n = ((40 - buf.size()) + 7) / 8;
1.1       root      482:                        buf.append(n, '\t');
                    483:                } else {
                    484:                        buf.push_back('\t');
                    485:                }
                    486: 
                    487:                std::string fromstr;
                    488:                switch (item.from) {
                    489:                 case ConfigItem::FromInitial:
                    490:                        fromstr = "initial value";
                    491:                        break;
1.1.1.14  root      492:                 case ConfigItem::FromHome:
1.1       root      493:                 case ConfigItem::FromConfig:
                    494:                        fromstr = item.where;
                    495:                        break;
                    496:                 case ConfigItem::FromOption:
1.1.1.12  root      497:                        fromstr = "option " + item.where;
1.1       root      498:                        break;
1.1.1.15! root      499:                 case ConfigItem::FromPerf:
        !           500:                        fromstr = "option --perf";
        !           501:                        break;
1.1       root      502:                 default:
1.1.1.15! root      503:                        fromstr = string_format("corrupted item.from=%u for key=%s",
        !           504:                                (uint)item.from, item.key.c_str());
1.1.1.12  root      505:                        break;
1.1       root      506:                }
                    507: 
                    508:                printf("%s(%s)\n", buf.c_str(), fromstr.c_str());
                    509:        }
                    510: }
                    511: 
                    512: // key から ConfigItem を取得する。見付からなければ assert する。
                    513: const ConfigItem&
1.1.1.2   root      514: Config::Find(const std::string& key) const
1.1       root      515: {
                    516:        for (const auto& item : list) {
                    517:                if (key == item.key) {
                    518:                        return item;
                    519:                }
                    520:        }
1.1.1.10  root      521:        PANIC("Config::Find() \"%s\" not found", key.c_str());
1.1.1.2   root      522: }
                    523: 
                    524: // key のデフォルト値を差し替える。key が見付からなければ assert する。
                    525: void
                    526: Config::SetDefault(const std::string& key, int val)
                    527: {
                    528:        std::string valstr = std::to_string(val);
                    529:        SetDefault(key, valstr);
1.1       root      530: }
                    531: 
1.1.1.2   root      532: // key のデフォルト値を差し替える。key が見付からなければ assert する。
                    533: void
                    534: Config::SetDefault(const std::string& key, const std::string& val)
1.1       root      535: {
1.1.1.2   root      536:        assert(fixed == false);
                    537: 
1.1       root      538:        for (auto& item : list) {
                    539:                if (key == item.key) {
1.1.1.2   root      540:                        item.SetDefault(val);
                    541:                        return;
1.1       root      542:                }
                    543:        }
1.1.1.2   root      544:        assertmsg(0, "Config::SetDefault() \"%s\" not found\n", key.c_str());
1.1       root      545: }
                    546: 
                    547: // key から ConfigItem を返す。見付からなければ NULL を返す。
                    548: // (こっちは内部で変更するために使う)
                    549: ConfigItem *
                    550: Config::FindItem(const std::string& key)
                    551: {
                    552:        for (auto& item : list) {
                    553:                if (key == item.key) {
                    554:                        return &item;
                    555:                }
                    556:        }
                    557:        return NULL;
                    558: }
                    559: 
1.1.1.12  root      560: // これ以降は書き込み禁止。
                    561: bool
1.1.1.2   root      562: Config::Fix()
                    563: {
                    564:        assert(fixed == false);
                    565: 
1.1.1.11  root      566:        // ここで、この機種に不要な変数を削除する。
1.1.1.12  root      567:        // が、その前にその変数がユーザから指定されていればエラーにする。
1.1.1.11  root      568:        for (auto it = list.begin(); it != list.end(); ) {
                    569:                ConfigItem& item = *it;
                    570:                VMCap vmcap = item.GetVMCap();
                    571:                if (gMainApp.Has(vmcap) == false) {
1.1.1.12  root      572:                        // この機種用ではない
1.1.1.14  root      573:                        switch (item.from) {
                    574:                         case ConfigItem::FromConfig:
                    575:                         case ConfigItem::FromOption:
                    576:                                // 明示的に更新されていればエラー
1.1.1.12  root      577:                                item.Err("Not supported in this vmtype");
                    578:                                return false;
1.1.1.14  root      579:                         default:
                    580:                                break;
1.1.1.12  root      581:                        }
                    582: 
1.1.1.11  root      583:                        it = list.erase(it);
                    584:                } else {
                    585:                        ++it;
                    586:                }
                    587:        }
                    588: 
1.1.1.2   root      589:        fixed = true;
1.1.1.12  root      590:        return true;
1.1.1.2   root      591: }
                    592: 
1.1       root      593: 
                    594: //
                    595: // 設定項目
                    596: //
                    597: 
1.1.1.10  root      598: // 値を整数値として取得してみる。
                    599: bool
                    600: ConfigItem::TryInt(int *val) const
                    601: {
                    602:        char *endp;
                    603: 
                    604:        if (value.empty()) {
                    605:                return false;
                    606:        }
                    607: 
                    608:        errno = 0;
                    609:        int r = strtol(value.c_str(), &endp, 10);
                    610:        if (errno == ERANGE || *endp != '\0') {
                    611:                return false;
                    612:        }
                    613:        *val = r;
                    614:        return true;
                    615: }
                    616: 
1.1.1.12  root      617: // 値を double 値として取得してみる。
                    618: bool
                    619: ConfigItem::TryDouble(double *val) const
                    620: {
                    621:        char *endp;
                    622: 
                    623:        if (value.empty()) {
                    624:                return false;
                    625:        }
                    626: 
                    627:        errno = 0;
                    628:        double res = strtod(value.c_str(), &endp);
                    629:        if (errno) {
                    630:                return false;
                    631:        }
                    632:        *val = res;
                    633:        return true;
                    634: }
                    635: 
1.1.1.15! root      636: // b ^ e
        !           637: static inline int ipow(int b, int e)
        !           638: {
        !           639:        int rv = 1;
        !           640: #if 1
        !           641:        for (int i = 0; i < e; i++) {
        !           642:                rv *= b;
        !           643:        }
        !           644: #else
        !           645:        // e <= 3 とかだと上のほうが速そう。
        !           646:        for (; e != 0; e >>= 1) {
        !           647:                if ((e & 1)) {
        !           648:                        rv *= b;
        !           649:                }
        !           650:                b *= b;
        !           651:        }
        !           652: #endif
        !           653:        return rv;
        !           654: }
        !           655: 
        !           656: // 値を固定小数点値として取得してみる。
        !           657: bool
        !           658: ConfigItem::TryFixedDecimal(int *val, int digit) const
        !           659: {
        !           660:        char *endp;
        !           661:        bool neg = false;
        !           662: 
        !           663:        if (value.empty()) {
        !           664:                return false;
        !           665:        }
        !           666: 
        !           667:        errno = 0;
        !           668:        int r = strtol(value.c_str(), &endp, 10);
        !           669:        if (errno == ERANGE || !(*endp == '\0' || *endp == '.')) {
        !           670:                return false;
        !           671:        }
        !           672:        if (r < 0) {
        !           673:                r = -r;
        !           674:                neg = true;
        !           675:        }
        !           676:        r *= ipow(10, digit);
        !           677:        if (*endp == '.') {
        !           678:                char *p = endp + 1;
        !           679:                errno = 0;
        !           680:                int f = strtol(p, &endp, 10);
        !           681:                if (errno == ERANGE || *endp != '\0') {
        !           682:                        return false;
        !           683:                }
        !           684:                int n = endp - p;
        !           685:                if (n <= digit) {
        !           686:                        r += f * ipow(10, digit - n);
        !           687:                } else {
        !           688:                        r += f / ipow(10, n - digit) + (p[digit] >= '5');
        !           689:                }
        !           690:        }
        !           691:        *val = neg ? -r : r;
        !           692:        return true;
        !           693: }
        !           694: 
1.1       root      695: // デフォルト値を差し替える
                    696: void
                    697: ConfigItem::SetDefault(const std::string& val)
                    698: {
                    699:        if (from == FromInitial) {
                    700:                value = val;
                    701:        }
                    702: }
                    703: 
                    704: void
                    705: ConfigItem::Err() const
                    706: {
                    707:        // 定型文
1.1.1.12  root      708:        Err("Invalid argument");
1.1       root      709: }
                    710: 
                    711: void
                    712: ConfigItem::Err(const char *fmt, ...) const
                    713: {
                    714:        char buf[1024];
                    715:        va_list ap;
                    716: 
                    717:        va_start(ap, fmt);
                    718:        vsnprintf(buf, sizeof(buf), fmt, ap);
                    719:        va_end(ap);
                    720: 
                    721:        std::string msg(buf);
                    722:        Err(msg);
                    723: }
                    724: 
                    725: void
                    726: ConfigItem::Err(const std::string& msg) const
                    727: {
1.1.1.10  root      728:        PrintErr(from, where, key, value, msg);
                    729: }
                    730: 
1.1.1.12  root      731: // この item についてエラーを表示する。
                    732: // key=val の '=' がない場合のみ key を空、value を行全体として呼ぶ。
1.1.1.10  root      733: /*static*/ void
                    734: ConfigItem::PrintErr(ConfigItem::from_t from, const std::string& where,
                    735:        const std::string& key, const std::string& value, const std::string& msg)
                    736: {
1.1       root      737:        std::string fromstr;
                    738: 
                    739:        switch (from) {
                    740:         case FromInitial:
1.1.1.12  root      741:                // 初期値の場合 key が空なことは起きないはず。
                    742:                // というか初期値でエラーが起きることも普通はないはず。
                    743:                fromstr = "initial value: " + key + "=" + value;
1.1       root      744:                break;
1.1.1.12  root      745: 
1.1.1.14  root      746:         case FromHome:
1.1       root      747:         case FromConfig:
1.1.1.12  root      748:                // 設定ファイルでのエラー。where はファイル名と行番号。
                    749:                fromstr = where + ": ";
                    750:                if (key.empty() == false) {
                    751:                        fromstr += key;
1.1.1.13  root      752:                        fromstr += "=";
1.1.1.12  root      753:                }
                    754:                fromstr += value;
1.1       root      755:                break;
1.1.1.12  root      756: 
1.1       root      757:         case FromOption:
1.1.1.15! root      758:         case FromPerf:
1.1.1.12  root      759:                // コマンドラインオプションの場合は where は
                    760:                // "--fontsize" とか "-V" なので、
                    761:                // "-V" なら (key =) value を追加し、
                    762:                // それ以外なら value のみ追加がいいか。
                    763:                fromstr = "option " + where + " ";
1.1.1.14  root      764:                if (where.find("-V") != std::string::npos) {
1.1.1.12  root      765:                        if (key.empty() == false) {
                    766:                                fromstr += key;
                    767:                                fromstr += "=";
                    768:                        }
                    769:                }
                    770:                fromstr += value;
1.1       root      771:                break;
1.1.1.10  root      772: 
1.1.1.12  root      773:         default:
1.1.1.15! root      774:                fromstr = string_format("corrupted item.from=%u for key=%s",
        !           775:                        (uint)from, key.c_str());
1.1.1.12  root      776:                break;
1.1.1.10  root      777:        }
                    778: 
1.1.1.12  root      779:        warnx("%s: %s", fromstr.c_str(), msg.c_str());
1.1       root      780: }
                    781: 
                    782: 
                    783: //
                    784: // 設定ファイル
                    785: //
                    786: 
                    787: // コンストラクタ
1.1.1.14  root      788: ConfigFile::ConfigFile(const std::string& filepath_, ConfigItem::from_t from_)
1.1       root      789: {
                    790:        filepath = filepath_;
1.1.1.14  root      791:        from = from_;
1.1       root      792: }
                    793: 
                    794: // デストラクタ
                    795: ConfigFile::~ConfigFile()
                    796: {
                    797: }
                    798: 
1.1.1.12  root      799: // ファイルの読み込み。
                    800: // 成功すれば true を、失敗すればメッセージを出力して false を返す。
                    801: // ファイルがないのは正常とする (なければスルーするため)。
                    802: // ファイルがあって読めなければ失敗 (エラー終了したいので)。
1.1       root      803: bool
                    804: ConfigFile::Load()
                    805: {
                    806:        char buf[1024];
                    807:        FILE *fp;
                    808: 
1.1.1.15! root      809:        if (filepath.empty()) {
        !           810:                return true;
        !           811:        }
        !           812: 
1.1       root      813:        fp = fopen(filepath.c_str(), "r");
                    814:        if (fp == NULL) {
1.1.1.12  root      815:                if (errno == ENOENT) {
                    816:                        return true;
                    817:                }
1.1       root      818:                warn("%s", filepath.c_str());
                    819:                return false;
                    820:        }
                    821: 
                    822:        for (int line = 1; fgets(buf, sizeof(buf), fp) != NULL; line++) {
                    823:                // 空行とコメント行を無視
                    824:                rtrim(buf);
                    825:                if (buf[0] == '#') {
                    826:                        buf[0] = '\0';
                    827:                }
                    828:                if (buf[0] == '\0') {
                    829:                        continue;
                    830:                }
                    831: 
                    832:                // ここでは記憶するだけ
                    833:                lines.emplace_back(buf, line);
                    834:        }
                    835: 
                    836:        fclose(fp);
                    837:        return true;
                    838: }

unix.superglobalmegacorp.com

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