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

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

unix.superglobalmegacorp.com

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