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

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

unix.superglobalmegacorp.com

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