Annotation of nono/lib/mainapp.cpp, revision 1.1.1.8

1.1       root        1: //
                      2: // nono
1.1.1.3   root        3: // Copyright (C) 2020 nono project
                      4: // Licensed under nono-license.txt
1.1       root        5: //
                      6: 
                      7: #include "mainapp.h"
1.1.1.2   root        8: #include "mystring.h"
1.1.1.8 ! root        9: #include "netdriver_selector.h"
1.1.1.2   root       10: #include <getopt.h>
                     11: #include <sys/stat.h>
1.1.1.8 ! root       12: #include <algorithm>
1.1.1.2   root       13: 
                     14: // グローバルインスタンス
                     15: MainApp gMainApp;
                     16: 
                     17: // ヘルプメッセージ
                     18: void
                     19: MainApp::ShowHelp(bool all) const
                     20: {
                     21:        ShowVersion();
                     22: 
                     23: #define p(msg) printf(" " msg "\n")
                     24: 
                     25:        printf("usage: %s [<options>]\n", getprogname());
                     26:        p("-A <file>           load and execute host binary (a.out or ELF)");
1.1.1.6   root       27:        p("-c <path>           vm directory or configuration file");
1.1.1.2   root       28:        p("-f                  fast mode");
                     29:        if (IsGUI())
1.1.1.4   root       30:                p("--fontsize <height> fontsize in monitors {12,16} (default:12)");
1.1.1.2   root       31:        p("-h                  show brief help message");
                     32:        p("--help              show all help message including for developers");
                     33:        if (IsGUI())
                     34:                p("-s,--scale <scale>  screen scale, default 1.0");
                     35:        p("--show-config       show configuration variables");
1.1.1.8 ! root       36:        p("--show-hostnet      show list of hostnet drivers");
1.1.1.2   root       37:        p("-v                  show version");
                     38:        p("-V <key>=<val>      overwrite config option");
                     39: 
                     40:        if (all) {
                     41:                printf("\n(options for developers)\n");
1.1.1.4   root       42:                p("-b <addr>[,<skip>]  set breakpoint");
1.1.1.2   root       43:                p("-B <rom#>           benchmark mode");
                     44:                p("-C                  output log to console");
                     45:                p("-d                  debugger prompt on startup");
                     46:                p("-D                  debugger on console");
                     47:                p("-L <name>=<lv>[,..] set loglevel (-Lhelp displays names)");
                     48:                p("-M <name>[,..]      monitors to display at startup (-Mhelp displays names)");
                     49:                p("-X <human68k executable> human68k console emulation");
                     50:        }
                     51: }
                     52: 
                     53: // long option は enum と struct option をアルファベット順に並べる。
                     54: // enum は getopt() の1文字のオプションと衝突しなければいいので適当に
                     55: // 0x80 から始めておく。
                     56: enum {
                     57:        OPT_fontsize = 0x80,
                     58:        OPT_help,
                     59:        OPT_show_config,
1.1.1.3   root       60:        OPT_show_config_all,
1.1.1.8 ! root       61:        OPT_show_hostnet,
1.1.1.2   root       62: };
                     63: static struct option longopts[] = {
                     64:        { "fontsize",           required_argument,      NULL,   OPT_fontsize },
                     65:        { "help",                       no_argument,            NULL,   OPT_help },
                     66:        { "show-config",        no_argument,            NULL,   OPT_show_config },
1.1.1.3   root       67:        // --show-config-all は開発用なのでヘルプには載せない
                     68:        { "show-config-all",no_argument,                NULL,   OPT_show_config_all },
1.1.1.8 ! root       69:        { "show-hostnet",       no_argument,            NULL,   OPT_show_hostnet },
1.1.1.2   root       70:        { NULL,                         0,                                      NULL,   0 },
                     71: };
                     72: 
1.1.1.7   root       73: // VM の初期化、ステージ1。
                     74: // VM 作成と設定確定あたりまで。スレッド生成を伴わないもの。
1.1.1.2   root       75: // 所々 CLI と GUI で処理が違う。
1.1.1.5   root       76: // 戻り値は以下のいずれか。
                     77: //  MainApp::PASS .. 実行を継続(通常パス)
                     78: //  EXIT_SUCCESS / EXIT_FAILURE .. この終了コードでアプリケーションを終了
                     79: int
1.1.1.7   root       80: MainApp::Init1(bool is_cli_, int ac, char *av[])
1.1.1.2   root       81: {
                     82:        is_cli = is_cli_;
                     83: 
                     84:        if (!ParseOpt(ac, av)) {
1.1.1.5   root       85:                return EXIT_FAILURE;
1.1.1.2   root       86:        }
1.1       root       87: 
1.1.1.2   root       88:        // ログ機構は引数処理後なるはや
                     89:        // CLI ならログは常に標準出力へ(も)出力。
                     90:        // GUI なら -C で指定。
1.1.1.3   root       91:        gLogger.UseStdout(IsCLI() ? true : log_to_console);
1.1.1.2   root       92: 
                     93:        // 設定を作成。コンストラクタで初期値を用意
                     94:        gConfig.reset(new Config());
                     95: 
1.1.1.6   root       96:        // 引数で指定されたディレクトリの設定ファイルがあれば読み込んで設定を更新
                     97:        ConfigFile file(vmfile);
1.1.1.2   root       98:        if (file.Load() == false) {
1.1.1.5   root       99:                return EXIT_FAILURE;
1.1.1.2   root      100:        }
                    101:        if (gConfig->Update(file) == false) {
1.1.1.5   root      102:                return EXIT_FAILURE;
1.1.1.2   root      103:        }
                    104:        // 最後にコマンドライン引数で更新
                    105:        if (gConfig->Update(config_options) == false) {
1.1.1.5   root      106:                return EXIT_FAILURE;
1.1.1.2   root      107:        }
1.1       root      108: 
1.1.1.8 ! root      109:        // 1. VM 種別を決定
1.1.1.2   root      110:        if (IsCLI() && human68k_file) {
                    111:                vmtype = VMTYPE_RXZ;
                    112:        } else {
                    113:                // VM 種別文字列から vmtype を決定
1.1.1.3   root      114:                const ConfigItem& item = gConfig->Find("vmtype");
1.1.1.2   root      115:                std::string vmstr = string_tolower(item.AsString());
                    116:                if (vmstr == "x68030") {
                    117:                        vmtype = VMTYPE_X68030;
                    118:                } else if (vmstr == "luna") {
1.1.1.3   root      119:                        vmtype = VMTYPE_LUNA1;
1.1.1.2   root      120:                } else if (vmstr == "luna88k") {
                    121:                        vmtype = VMTYPE_LUNA88K;
                    122:                } else {
                    123:                        if (item.GetFrom() == ConfigItem::FromInitial) {
                    124:                                // 未指定の時
                    125:                                warnx("vmtype must be specified");
                    126:                        } else {
                    127:                                // ユーザ由来の時
                    128:                                item.Err("invalid vmtype");
                    129:                        }
1.1.1.5   root      130:                        return EXIT_FAILURE;
1.1.1.2   root      131:                }
                    132:        }
1.1       root      133: 
1.1.1.8 ! root      134:        // 2. VM 作成
1.1.1.2   root      135:        switch (vmtype) {
                    136:         case VMTYPE_X68030:
                    137:                gVM.reset(new VM_X68030());
                    138:                break;
                    139: 
1.1.1.3   root      140:         case VMTYPE_LUNA1:
1.1.1.6   root      141:                gVM.reset(new VM_LUNA1());
1.1.1.2   root      142:                break;
                    143: 
                    144:         case VMTYPE_RXZ:
                    145:                gVM.reset(new VM_RXZ());
                    146:                break;
                    147: 
                    148:         case VMTYPE_LUNA88K:
                    149:                gVM.reset(new VM_LUNA88K());
                    150:                break;
1.1       root      151: 
1.1.1.2   root      152:         default:
                    153:                __unreachable();
                    154:        }
                    155: 
1.1.1.8 ! root      156:        // 3. 動的なコンストラクション
1.1.1.5   root      157:        if (!gVM->Create()) {
1.1.1.7   root      158:                gVM.reset();
1.1.1.5   root      159:                return EXIT_FAILURE;
                    160:        }
                    161: 
1.1.1.8 ! root      162:        // デバッガ (ここでもモニタを登録する)
        !           163:        debugger_create();
        !           164: 
1.1.1.5   root      165:        // 設定内容を表示して終了
                    166:        // (VM コンストラクタで変数の増減があるのでそれより後、
                    167:        // Create() でも SCSI パラメータを減らすので、それより後)
1.1.1.3   root      168:        gConfig->Fix();
1.1.1.2   root      169:        if (show_config) {
1.1.1.3   root      170:                gConfig->Show(show_config - 1);
1.1.1.7   root      171:                gVM.reset();
1.1.1.5   root      172:                return EXIT_SUCCESS;
1.1.1.2   root      173:        }
                    174: 
1.1.1.8 ! root      175:        // 4. ログレベルを設定。コンストラクト後すぐに行う。
1.1.1.2   root      176:        // -L help もここで処理。
                    177:        if (!ParseLogopt()) {
1.1.1.7   root      178:                gVM.reset();
1.1.1.5   root      179:                return EXIT_FAILURE;
1.1.1.2   root      180:        }
                    181: 
1.1.1.8 ! root      182:        // 5. ホストネットワークドライバの作成。
        !           183:        //
        !           184:        // ホストネットワークドライバを作成する時にはログ表示が欲しいが、通常の
        !           185:        // フローではオブジェクトが出揃った後でログレベルを割り当てるので、
        !           186:        // にわたま問題になってしまう。そこでこれだけ特別対応する。
        !           187:        // 手順 3. のコンストラクションで EthernetDevice が NetDriverSelector を
        !           188:        // 作成し、手順 4. で通常通りにログレベルの割り当てを受ける。このログを
        !           189:        // 使ってここ(手順5)で実際の NetDriver を作成し、同時にログレベルも引き
        !           190:        // 継ぐことにする。
        !           191:        if ((bool)gNetDriverSelector) {
        !           192:                // 実際の NetDriver を作成
        !           193:                NetDriver *newdriver = gNetDriverSelector->InitDriver();
        !           194:                if (newdriver == NULL) {
        !           195:                        gVM.reset();
        !           196:                        return EXIT_FAILURE;
        !           197:                }
        !           198:                // 成功すればセット
        !           199:                gNetDriver.reset(newdriver);
        !           200: 
        !           201:                // NetDriverSelector はこれ以降まったく使わないがとりあえず放置。
        !           202:        }
        !           203: 
1.1.1.5   root      204:        return PASS;
1.1.1.2   root      205: }
                    206: 
1.1.1.7   root      207: // VM の初期化、ステージ2。スレッド生成を伴う。
                    208: // Init1() で引数を受け付けてから Init2() でデバッガスレッドなどを起動するが、
1.1.1.2   root      209: // -Mhelp とかが指定された場合はここでスレッド開始する前にプロセスを終了する
1.1.1.7   root      210: // 必要があるので分けてある。wxapp.cpp も参照。
1.1.1.2   root      211: bool
1.1.1.7   root      212: MainApp::Init2()
1.1.1.2   root      213: {
                    214:        // VM 初期化。
                    215:        // これ以降はスレッドを開始したかもしれないので false を返す際には
                    216:        // VM をデストラクトすること。
                    217:        if (!gVM->Init()) {
                    218:                gVM.reset();
                    219:                return false;
                    220:        }
                    221:        // デバッガは VM オブジェクトリストに入っていない
                    222:        debugger_init();
                    223: 
                    224:        // 起動時設定の適用
                    225:        if (!gVM->Apply()) {
                    226:                gVM.reset();
                    227:                return false;
                    228:        }
                    229: 
                    230:        return true;
                    231: }
                    232: 
                    233: // コマンドライン引数を処理する。
                    234: // 知らない引数とかがあればこちらで usage を表示して false を返す。
                    235: bool
                    236: MainApp::ParseOpt(int ac, char *av[])
                    237: {
1.1.1.6   root      238:        struct stat st;
                    239:        const char *cpath;
1.1.1.2   root      240:        int b;
                    241:        int c;
                    242: 
                    243:        fontsize = 12;
                    244:        screen_scale = 1.0;
1.1.1.6   root      245:        cpath = ".";
1.1.1.2   root      246: 
                    247:        while ((c = getopt_long(ac, av, "A:b:B:c:CdDfhL:M:s:vV:X:",
                    248:                                longopts, NULL)) != -1) {
                    249:                switch (c) {
                    250:                 case 'A':
                    251:                        host_file = optarg;
                    252:                        break;
                    253: 
                    254:                 case 'b':
1.1.1.4   root      255:                        debug_breakaddr.push_back(optarg);
1.1.1.2   root      256:                        break;
                    257: 
                    258:                 case 'B':
                    259:                        b = atoi(optarg);
                    260:                        if (b < 2 || b > 6) {
                    261:                                fprintf(stderr, "-B <benchmark_mode>: 2..6\n");
                    262:                                return false;
                    263:                        }
                    264:                        benchmark_mode = b;
                    265:                        break;
                    266: 
                    267:                 case 'c':
1.1.1.6   root      268:                        cpath = optarg;
                    269:                        // 空文字列なら再び初期値に
                    270:                        if (cpath[0] == '\0') {
                    271:                                cpath = ".";
                    272:                        }
1.1.1.2   root      273:                        break;
                    274: 
                    275:                 case 'C':
                    276:                        log_to_console = true;
                    277:                        break;
                    278: 
                    279:                 case 'd':
                    280:                        debug_on_start = true;
                    281:                        break;
                    282: 
                    283:                 case 'D':
                    284:                        debug_on_console = true;
                    285:                        break;
                    286: 
                    287:                 case 'f':
                    288:                        fast_mode = true;
                    289:                        break;
                    290: 
                    291:                 case 'L':
                    292:                        if (logopt[0] != '\0') {
                    293:                                strlcat(logopt, ",", sizeof(logopt));
                    294:                        }
                    295:                        strlcat(logopt, optarg, sizeof(logopt));
                    296:                        break;
                    297: 
                    298:                 case 'M':
                    299:                        if (!monitor_opt.empty()) {
                    300:                                monitor_opt += ",";
                    301:                        }
                    302:                        monitor_opt += optarg;
                    303:                        break;
                    304: 
                    305:                 case 's':
                    306:                        if (IsGUI()) {
                    307:                                char *end;
                    308:                                errno = 0;
                    309:                                screen_scale = strtod(optarg, &end);
                    310:                                if (end == optarg || end[0] != '\0' || errno == ERANGE) {
                    311:                                        warnx("-s: invalid argument");
                    312:                                        return false;
                    313:                                }
                    314:                                // 上限は適当
                    315:                                if (screen_scale <= 0.0 || screen_scale >= 10.0) {
                    316:                                        warnx("-s: invalid scale");
                    317:                                        return false;
                    318:                                }
                    319:                        }
                    320:                        break;
                    321: 
                    322:                 case 'X':
                    323:                        human68k_file = optarg;
                    324:                        for (int i = optind; i < ac; i++) {
                    325:                                if (i != optind) {
                    326:                                        strlcat(human68k_arg, " ", sizeof(human68k_arg));
                    327:                                }
                    328:                                strlcat(human68k_arg, av[i], sizeof(human68k_arg));
                    329:                        }
                    330:                        optind = ac;
                    331:                        break;
                    332: 
                    333:                 case 'v':
                    334:                        ShowVersion();
                    335:                        exit(0);
                    336: 
                    337:                 case 'V':
                    338:                        config_options.push_back(optarg);
                    339:                        break;
                    340: 
                    341:                 case OPT_fontsize:
                    342:                        fontsize = atoi(optarg);
                    343:                        break;
                    344: 
                    345:                 case OPT_help:
                    346:                        ShowHelp(true);
                    347:                        return false;
                    348: 
                    349:                 case OPT_show_config:
1.1.1.3   root      350:                        show_config = 1;
                    351:                        break;
                    352: 
                    353:                 case OPT_show_config_all:
                    354:                        show_config = 2;
1.1.1.2   root      355:                        break;
                    356: 
1.1.1.8 ! root      357:                 case OPT_show_hostnet:
        !           358:                        ShowHostnet();
        !           359:                        return false;
        !           360: 
1.1.1.2   root      361:                 case 'h':
                    362:                 default:
                    363:                        ShowHelp(false);
                    364:                        return false;
                    365:                }
                    366:        }
                    367: 
1.1.1.6   root      368:        // 引数がディレクトリなら、それを VM ディレクトリとし、その中の
                    369:        // nono.cfg を設定ファイルとする。
                    370:        // 引数がファイルなら、それを設定ファイルとし、そのファイルがある
                    371:        // ディレクトリを VM ディレクトリとする。
                    372:        // -c DIR       => vmdir = DIR, vmfile = DIR/nono.cfg
                    373:        // -c DIR/FILE  => vmdir = DIR, vmfile = FILE
                    374:        if (stat(cpath, &st) < 0) {
                    375:                warn("stat %s", cpath);
                    376:                return false;
1.1.1.2   root      377:        }
1.1.1.6   root      378:        if (S_ISDIR(st.st_mode)) {
                    379:                vmdir = std::string(cpath);
                    380:                if (vmdir.back() != '/') {
                    381:                        vmdir += '/';
                    382:                }
                    383:                vmfile = vmdir + "nono.cfg";
                    384:        } else if (S_ISREG(st.st_mode)) {
                    385:                vmfile = std::string(cpath);
                    386:                auto pos = vmfile.rfind('/');
                    387:                if (pos != std::string::npos) {
                    388:                        vmdir = vmfile.substr(0, pos + 1);
                    389:                } else {
                    390:                        vmdir = "./";
                    391:                }
                    392:        } else {
                    393:                warnx("-c %s: path must be file or directory", cpath);
                    394:                return false;
1.1.1.2   root      395:        }
                    396: 
                    397:        // CLI 版で -Mhelp つけても黙って起動するのはさすがにどうかと思う。
                    398:        // とは言え GUI 版のコマンドラインを CLI 版用に書き換えた時に -M を
                    399:        // 消して回らないと起動できないのも面倒なので -M オプション自体は
                    400:        // 無意味だけど許容する。うーん。
                    401:        if (IsCLI()) {
                    402:                if (monitor_opt == "help") {
                    403:                        warnx("-Mhelp is not available on CLI");
                    404:                        return false;
                    405:                }
                    406:                if (!monitor_opt.empty()) {
                    407:                        warnx("-M option is ignored on CLI");
                    408:                        return true;
                    409:                }
                    410:        }
                    411: 
                    412:        return true;
                    413: }
                    414: 
                    415: // バージョンを表示
                    416: void
                    417: MainApp::ShowVersion() const
                    418: {
                    419:        // ここは実行ファイル名によらず nono にする
                    420:        fprintf(stderr, "nono version %d.%d.%d (%s)\n",
                    421:                NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, NONO_DATE);
                    422: }
                    423: 
1.1.1.8 ! root      424: // ログレベル指定文字列を処理する。
        !           425: // str はログレベル指定文字列を ',' で連結した "foo=1,bar=2" 形式の文字列で、
        !           426: // これを分解してそれぞれ担当するオブジェクトのログレベルにセットする。
        !           427: // "help" があれば設定は行わず一覧を表示。
1.1       root      428: bool
1.1.1.2   root      429: MainApp::ParseLogopt()
1.1       root      430: {
1.1.1.8 ! root      431:        std::vector<std::string> items;
        !           432: 
        !           433:        // 分解して..
        !           434:        items = string_split(logopt, ',');
        !           435: 
        !           436:        // "help" があればヘルプを表示して終了
        !           437:        for (const auto& item : items) {
        !           438:                if (item == "help") {
        !           439:                        std::vector<std::string> list = GetLogNames();
        !           440:                        // less したいだろうから stderr ではなく stdout に出力する
        !           441:                        for (const auto& name : list) {
        !           442:                                printf(" %s\n", name.c_str());
        !           443:                        }
        !           444:                        return false;
1.1       root      445:                }
1.1.1.8 ! root      446:        }
        !           447: 
        !           448:        // ログレベルを設定
        !           449:        std::string errmsg;
        !           450:        if (SetLogopt(items, &errmsg) == false) {
        !           451:                warnx("%s", errmsg.c_str());
1.1       root      452:                return false;
                    453:        }
                    454: 
1.1.1.8 ! root      455:        return true;
        !           456: }
1.1       root      457: 
1.1.1.8 ! root      458: // ログレベルを設定する。
        !           459: // ログレベル指定文字列を 1つずつに分解したリスト items を処理する。
        !           460: // "help" があるケースはここに来るまでに処理してあるので、ここには来ない。
        !           461: // 成功なら何も表示せず true を返す。失敗なら *errmsg にエラーメッセージを
        !           462: // 格納して false を返す。
        !           463: // MainApp 内と Debugger からも呼ばれる。
        !           464: /*static*/ bool
        !           465: MainApp::SetLogopt(const std::vector<std::string>& items, std::string *errmsg)
        !           466: {
        !           467:        for (const auto& item : items) {
        !           468:                if (SetLogopt1(item.c_str(), errmsg) == false) {
        !           469:                        return false;
        !           470:                }
        !           471:        }
        !           472:        if (0) {        // デバッグ用
        !           473:                for (const auto o : gObjects) {
        !           474:                        printf("%-16s %d\n", o->GetName().c_str(), o->loglevel);
1.1       root      475:                }
1.1.1.8 ! root      476:        }
        !           477:        return true;
        !           478: }
        !           479: 
        !           480: // "logname[=loglevel]" 形式をパースしてオブジェクトにログレベルを設定する。
        !           481: // loglevel は省略なら 1 とする。
        !           482: // logname が "all" なら全オブジェクトにセットする。
        !           483: // そうでない場合は case ignore で完全一致するか前方一致で1つに確定すれば、
        !           484: // そのオブジェクトにログレベルを設定して true を返す。
        !           485: // 見付からないか候補が複数ある場合はエラーメッセージを *errmsg に出力して
        !           486: // false を返す。
        !           487: // この関数は (このすぐ上の SetLogopt() を経由して)
        !           488: // MainApp と Debugger から呼ばれることに注意。
        !           489: /*static*/ bool
        !           490: MainApp::SetLogopt1(const std::string& item, std::string *errmsg)
        !           491: {
        !           492:        std::string name;
        !           493:        const char *v;
        !           494:        int level;
        !           495: 
        !           496:        v = strchr(item.c_str(), '=');
        !           497:        if (v) {
        !           498:                name = std::string(item.c_str(), v - item.c_str());
        !           499:                level = atoi(++v);
        !           500:        } else {
        !           501:                name = item;
        !           502:                level = 1;
        !           503:        }
        !           504:        // ここで name は変数名、level は値(省略されたら1)
        !           505: 
        !           506:        if (name.empty()) {
        !           507:                *errmsg = "logname must be specified";
        !           508:                return false;
        !           509:        }
1.1       root      510: 
1.1.1.8 ! root      511:        // "all" なら全部にセット
        !           512:        if (name == "all") {
        !           513:                for (auto obj : gObjects) {
        !           514:                        if (obj->GetName().empty() == false) {
        !           515:                                obj->loglevel = level;
        !           516:                        }
1.1       root      517:                }
1.1.1.8 ! root      518:                return true;
        !           519:        }
        !           520: 
        !           521:        std::vector<Object *> found;                    // 候補オブジェクト
        !           522:        std::vector<std::string> candidates;    // 候補名
1.1       root      523: 
1.1.1.8 ! root      524:        // 前方一致する名前(とそのオブジェクト)を列挙する
        !           525:        for (auto *obj : gObjects) {
        !           526:                const std::vector<std::string>& aliases = obj->GetAliases();
        !           527: 
        !           528:                for (const auto& a : aliases) {
        !           529:                        // 完全一致したら即採用
        !           530:                        // (前方一致しつつ完全一致したらこの時点で採用して打ち切るため)
        !           531:                        if (strcasecmp(a.c_str(), name.c_str()) == 0) {
        !           532:                                obj->loglevel = level;
        !           533:                                return true;
1.1       root      534:                        }
1.1.1.8 ! root      535: 
        !           536:                        // 前方一致したら覚えておく
        !           537:                        if (strncasecmp(a.c_str(), name.c_str(), name.size()) == 0) {
        !           538:                                candidates.emplace_back(a);
        !           539:                                found.push_back(obj);
1.1       root      540:                        }
1.1.1.8 ! root      541:                }
        !           542:        }
        !           543: 
        !           544:        // 見付からない場合はエラー
        !           545:        if (found.empty()) {
        !           546:                *errmsg = string_format("Unknown logname \"%s\"", name.c_str());
        !           547:                return false;
        !           548:        }
        !           549: 
        !           550:        // 1つだけなら確定
        !           551:        if (found.size() == 1) {
        !           552:                Object *obj = found[0];
        !           553:                obj->loglevel = level;
        !           554:                return true;
        !           555:        }
        !           556: 
        !           557:        // 複数あれば候補文字列を作成
        !           558:        *errmsg = string_format("Ambiguous logname \"%s\": candidates are",
        !           559:                name.c_str());
        !           560:        for (const auto& cand : candidates) {
        !           561:                *errmsg += string_format(" \"%s\"", cand.c_str());
        !           562:        }
        !           563:        return false;
        !           564: }
        !           565: 
        !           566: // ログ名の一覧を取得する。
        !           567: // MainApp と Debugger から呼ばれる。
        !           568: /*static*/ std::vector<std::string>
        !           569: MainApp::GetLogNames()
        !           570: {
        !           571:        std::vector<Object *> sortobj;
        !           572: 
        !           573:        // エイリアスを持つオブジェクトだけ抜き出す
        !           574:        for (const auto& obj : gObjects) {
        !           575:                if (obj->GetAliases().empty() == false) {
        !           576:                        sortobj.emplace_back(obj);
        !           577:                }
        !           578:        }
        !           579: 
        !           580:        // aliases の一語目で sortobj をソートする
        !           581:        std::sort(sortobj.begin(), sortobj.end(),
        !           582:                [](const auto& a, const auto *b) {
        !           583:                        const auto& sa = a->GetAliases()[0];
        !           584:                        const auto& sb = b->GetAliases()[0];
        !           585:                        return strcmp(sa.c_str(), sb.c_str()) < 0;
        !           586:                }
        !           587:        );
        !           588: 
        !           589:        std::vector<std::string> list;
        !           590:        for (const auto *obj : sortobj) {
        !           591:                const auto& aliases = obj->GetAliases();
        !           592:                std::string str;
        !           593: 
        !           594:                str += aliases[0];
        !           595:                if (aliases.size() > 1) {
        !           596:                        str += " (alias:";
        !           597:                        for (int i = 1, sz = aliases.size(); i < sz; i++) {
        !           598:                                str += ' ';
        !           599:                                str += aliases[i];
1.1       root      600:                        }
1.1.1.8 ! root      601:                        str += ')';
1.1.1.6   root      602:                }
1.1.1.8 ! root      603:                list.emplace_back(str);
1.1       root      604:        }
1.1.1.8 ! root      605:        list.emplace_back("all");
1.1       root      606: 
1.1.1.8 ! root      607:        return list;
1.1       root      608: }
1.1.1.2   root      609: 
                    610: // 関連するファイルのパスを取得。
1.1.1.3   root      611: // 1. name がファイル名のみなら、VM ディレクトリとその親ディレクトリを検索。
                    612: //    ファイルが存在すればその時点で戻り値とする。ファイルが存在したけど
                    613: //    何らか不都合があったとしても (例えばファイルサイズが 0 だとか
                    614: //    パーミッションが足りないとか) 次を試すとかはしない。
                    615: // 2. name が '~' から始まっていればホームディレクトリに展開。
                    616: // 3. name が相対パスなら VM ディレクトリからの相対パスとする。
                    617: // 4. name は絶対パスのはずなので、そのまま使用する。
1.1.1.2   root      618: std::string
                    619: MainApp::SearchFile(const std::string& name) const
                    620: {
                    621:        std::string path;
                    622:        struct stat st;
                    623: 
1.1.1.3   root      624:        // 1. パス区切りを含んでなければ、ファイル名のみ
                    625:        if (name.find('/') == std::string::npos) {
                    626:                // 1a. VM ディレクトリ
                    627:                path = GetVMDir() + name;
                    628:                if (stat(path.c_str(), &st) == 0) {
                    629:                        return path;
                    630:                }
                    631: 
                    632:                // 2. 親ディレクトリ
                    633:                path = GetVMDir() + "../" + name;
                    634:                if (stat(path.c_str(), &st) == 0) {
                    635:                        return path;
                    636:                }
                    637: 
                    638:                // どちらもなければエラー
                    639:                return "";
                    640:        }
1.1.1.2   root      641: 
1.1.1.3   root      642:        // 2. 先頭の '~' を $HOME に展開
                    643:        path = name;
                    644:        if (path[0] == '~' && path[1] == '/') {
                    645:                const char *home = getenv("HOME");
                    646:                if (home == NULL) {
                    647:                        home = "";
                    648:                }
                    649:                path = string_format("%s%s", home, path.c_str() + 1);
1.1.1.2   root      650:        }
                    651: 
1.1.1.3   root      652:        // 3. 相対パスなら、VM ディレクトリからの相対
                    653:        if (path[0] != '/') {
                    654:                path = GetVMDir() + path;
1.1.1.2   root      655:        }
                    656: 
1.1.1.3   root      657:        return path;
                    658: }
                    659: 
                    660: // 現在の VM が指定の feature を持っているか?
                    661: bool
                    662: MainApp::HasVMFeature(vmf_t feature) const
                    663: {
                    664:        uint32 vmf = 1 << GetVMType();
                    665:        return (vmf & feature) != 0;
1.1.1.2   root      666: }
1.1.1.8 ! root      667: 
        !           668: // コンパイルされている host netdriver の一覧を表示。
        !           669: void
        !           670: MainApp::ShowHostnet() const
        !           671: {
        !           672:        auto list = NetDriverSelector::GetDrivers();
        !           673:        for (const auto& name : list) {
        !           674:                printf(" %s\n", name.c_str());
        !           675:        }
        !           676: }

unix.superglobalmegacorp.com

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