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

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

unix.superglobalmegacorp.com

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