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

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

unix.superglobalmegacorp.com

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