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

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

unix.superglobalmegacorp.com

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