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

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());
1.1.1.6   root       26:        p("-c <path>           vm directory or configuration file");
1.1.1.2   root       27:        p("-f                  fast mode");
                     28:        if (IsGUI())
1.1.1.4   root       29:                p("--fontsize <height> fontsize in monitors {12,16} (default:12)");
1.1.1.2   root       30:        p("-h                  show brief help message");
                     31:        p("--help              show all help message including for developers");
                     32:        if (IsGUI())
                     33:                p("-s,--scale <scale>  screen scale, default 1.0");
                     34:        p("--show-config       show configuration variables");
1.1.1.8   root       35:        p("--show-hostnet      show list of hostnet drivers");
1.1.1.2   root       36:        p("-v                  show version");
                     37:        p("-V <key>=<val>      overwrite config option");
1.1.1.9   root       38:        p("-X <file> [arg..]   load and execute host binary (a.out or ELF)");
1.1.1.2   root       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");
1.1.1.10! root       47:                p("-H                  human68k console emulation");
1.1.1.2   root       48:                p("-L <name>=<lv>[,..] set loglevel (-Lhelp displays names)");
                     49:                p("-M <name>[,..]      monitors to display at startup (-Mhelp displays names)");
                     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.9   root      110:        // VM 種別文字列から vmtype を決定
                    111:        const ConfigItem& item = gConfig->Find("vmtype");
                    112:        std::string vmstr = string_tolower(item.AsString());
                    113:        if (vmstr == "x68030") {
                    114:                vmtype = VMTYPE_X68030;
                    115:        } else if (vmstr == "luna") {
                    116:                vmtype = VMTYPE_LUNA1;
                    117:        } else if (vmstr == "luna88k") {
                    118:                vmtype = VMTYPE_LUNA88K;
1.1.1.2   root      119:        } else {
1.1.1.9   root      120:                if (item.GetFrom() == ConfigItem::FromInitial) {
                    121:                        // 未指定の時
                    122:                        warnx("vmtype must be specified");
1.1.1.2   root      123:                } else {
1.1.1.9   root      124:                        // ユーザ由来の時
                    125:                        item.Err("invalid vmtype");
1.1.1.2   root      126:                }
1.1.1.9   root      127:                return EXIT_FAILURE;
1.1.1.2   root      128:        }
1.1       root      129: 
1.1.1.8   root      130:        // 2. VM 作成
1.1.1.2   root      131:        switch (vmtype) {
                    132:         case VMTYPE_X68030:
                    133:                gVM.reset(new VM_X68030());
                    134:                break;
                    135: 
1.1.1.3   root      136:         case VMTYPE_LUNA1:
1.1.1.6   root      137:                gVM.reset(new VM_LUNA1());
1.1.1.2   root      138:                break;
                    139: 
                    140:         case VMTYPE_LUNA88K:
                    141:                gVM.reset(new VM_LUNA88K());
                    142:                break;
1.1       root      143: 
1.1.1.2   root      144:         default:
                    145:                __unreachable();
                    146:        }
                    147: 
1.1.1.8   root      148:        // 3. 動的なコンストラクション
1.1.1.5   root      149:        if (!gVM->Create()) {
1.1.1.7   root      150:                gVM.reset();
1.1.1.5   root      151:                return EXIT_FAILURE;
                    152:        }
                    153: 
1.1.1.8   root      154:        // デバッガ (ここでもモニタを登録する)
                    155:        debugger_create();
                    156: 
1.1.1.5   root      157:        // 設定内容を表示して終了
                    158:        // (VM コンストラクタで変数の増減があるのでそれより後、
                    159:        // Create() でも SCSI パラメータを減らすので、それより後)
1.1.1.3   root      160:        gConfig->Fix();
1.1.1.2   root      161:        if (show_config) {
1.1.1.3   root      162:                gConfig->Show(show_config - 1);
1.1.1.7   root      163:                gVM.reset();
1.1.1.5   root      164:                return EXIT_SUCCESS;
1.1.1.2   root      165:        }
                    166: 
1.1.1.8   root      167:        // 4. ログレベルを設定。コンストラクト後すぐに行う。
1.1.1.2   root      168:        // -L help もここで処理。
                    169:        if (!ParseLogopt()) {
1.1.1.7   root      170:                gVM.reset();
1.1.1.5   root      171:                return EXIT_FAILURE;
1.1.1.2   root      172:        }
                    173: 
1.1.1.8   root      174:        // 5. ホストネットワークドライバの作成。
                    175:        //
                    176:        // ホストネットワークドライバを作成する時にはログ表示が欲しいが、通常の
                    177:        // フローではオブジェクトが出揃った後でログレベルを割り当てるので、
                    178:        // にわたま問題になってしまう。そこでこれだけ特別対応する。
                    179:        // 手順 3. のコンストラクションで EthernetDevice が NetDriverSelector を
                    180:        // 作成し、手順 4. で通常通りにログレベルの割り当てを受ける。このログを
                    181:        // 使ってここ(手順5)で実際の NetDriver を作成し、同時にログレベルも引き
                    182:        // 継ぐことにする。
                    183:        if ((bool)gNetDriverSelector) {
                    184:                // 実際の NetDriver を作成
                    185:                NetDriver *newdriver = gNetDriverSelector->InitDriver();
                    186:                if (newdriver == NULL) {
                    187:                        gVM.reset();
                    188:                        return EXIT_FAILURE;
                    189:                }
                    190:                // 成功すればセット
                    191:                gNetDriver.reset(newdriver);
                    192: 
                    193:                // NetDriverSelector はこれ以降まったく使わないがとりあえず放置。
                    194:        }
                    195: 
1.1.1.5   root      196:        return PASS;
1.1.1.2   root      197: }
                    198: 
1.1.1.7   root      199: // VM の初期化、ステージ2。スレッド生成を伴う。
                    200: // Init1() で引数を受け付けてから Init2() でデバッガスレッドなどを起動するが、
1.1.1.2   root      201: // -Mhelp とかが指定された場合はここでスレッド開始する前にプロセスを終了する
1.1.1.7   root      202: // 必要があるので分けてある。wxapp.cpp も参照。
1.1.1.2   root      203: bool
1.1.1.7   root      204: MainApp::Init2()
1.1.1.2   root      205: {
                    206:        // VM 初期化。
                    207:        // これ以降はスレッドを開始したかもしれないので false を返す際には
                    208:        // VM をデストラクトすること。
                    209:        if (!gVM->Init()) {
                    210:                gVM.reset();
                    211:                return false;
                    212:        }
                    213:        // デバッガは VM オブジェクトリストに入っていない
                    214:        debugger_init();
                    215: 
                    216:        // 起動時設定の適用
                    217:        if (!gVM->Apply()) {
                    218:                gVM.reset();
                    219:                return false;
                    220:        }
                    221: 
                    222:        return true;
                    223: }
                    224: 
                    225: // コマンドライン引数を処理する。
                    226: // 知らない引数とかがあればこちらで usage を表示して false を返す。
                    227: bool
                    228: MainApp::ParseOpt(int ac, char *av[])
                    229: {
1.1.1.6   root      230:        struct stat st;
                    231:        const char *cpath;
1.1.1.2   root      232:        int b;
                    233:        int c;
                    234: 
                    235:        fontsize = 12;
                    236:        screen_scale = 1.0;
1.1.1.6   root      237:        cpath = ".";
1.1.1.2   root      238: 
1.1.1.9   root      239:        while ((c = getopt_long(ac, av, "b:B:c:CdDfhHL:M:s:vV:X:",
1.1.1.2   root      240:                                longopts, NULL)) != -1) {
                    241:                switch (c) {
                    242:                 case 'b':
1.1.1.4   root      243:                        debug_breakaddr.push_back(optarg);
1.1.1.2   root      244:                        break;
                    245: 
                    246:                 case 'B':
                    247:                        b = atoi(optarg);
                    248:                        if (b < 2 || b > 6) {
                    249:                                fprintf(stderr, "-B <benchmark_mode>: 2..6\n");
                    250:                                return false;
                    251:                        }
                    252:                        benchmark_mode = b;
                    253:                        break;
                    254: 
                    255:                 case 'c':
1.1.1.6   root      256:                        cpath = optarg;
                    257:                        // 空文字列なら再び初期値に
                    258:                        if (cpath[0] == '\0') {
                    259:                                cpath = ".";
                    260:                        }
1.1.1.2   root      261:                        break;
                    262: 
                    263:                 case 'C':
                    264:                        log_to_console = true;
                    265:                        break;
                    266: 
                    267:                 case 'd':
                    268:                        debug_on_start = true;
                    269:                        break;
                    270: 
                    271:                 case 'D':
                    272:                        debug_on_console = true;
                    273:                        break;
                    274: 
                    275:                 case 'f':
                    276:                        fast_mode = true;
                    277:                        break;
                    278: 
1.1.1.9   root      279:                 case 'H':
                    280:                        human_mode = true;
                    281:                        break;
                    282: 
1.1.1.2   root      283:                 case 'L':
                    284:                        if (logopt[0] != '\0') {
                    285:                                strlcat(logopt, ",", sizeof(logopt));
                    286:                        }
                    287:                        strlcat(logopt, optarg, sizeof(logopt));
                    288:                        break;
                    289: 
                    290:                 case 'M':
                    291:                        if (!monitor_opt.empty()) {
                    292:                                monitor_opt += ",";
                    293:                        }
                    294:                        monitor_opt += optarg;
                    295:                        break;
                    296: 
                    297:                 case 's':
                    298:                        if (IsGUI()) {
                    299:                                char *end;
                    300:                                errno = 0;
                    301:                                screen_scale = strtod(optarg, &end);
                    302:                                if (end == optarg || end[0] != '\0' || errno == ERANGE) {
                    303:                                        warnx("-s: invalid argument");
                    304:                                        return false;
                    305:                                }
                    306:                                // 上限は適当
                    307:                                if (screen_scale <= 0.0 || screen_scale >= 10.0) {
                    308:                                        warnx("-s: invalid scale");
                    309:                                        return false;
                    310:                                }
                    311:                        }
                    312:                        break;
                    313: 
                    314:                 case 'X':
1.1.1.9   root      315:                        exec_file = optarg;
1.1.1.2   root      316:                        for (int i = optind; i < ac; i++) {
                    317:                                if (i != optind) {
1.1.1.9   root      318:                                        exec_arg += " ";
1.1.1.2   root      319:                                }
1.1.1.9   root      320:                                exec_arg += av[i];
1.1.1.2   root      321:                        }
                    322:                        optind = ac;
                    323:                        break;
                    324: 
                    325:                 case 'v':
                    326:                        ShowVersion();
                    327:                        exit(0);
                    328: 
                    329:                 case 'V':
                    330:                        config_options.push_back(optarg);
                    331:                        break;
                    332: 
                    333:                 case OPT_fontsize:
                    334:                        fontsize = atoi(optarg);
                    335:                        break;
                    336: 
                    337:                 case OPT_help:
                    338:                        ShowHelp(true);
                    339:                        return false;
                    340: 
                    341:                 case OPT_show_config:
1.1.1.3   root      342:                        show_config = 1;
                    343:                        break;
                    344: 
                    345:                 case OPT_show_config_all:
                    346:                        show_config = 2;
1.1.1.2   root      347:                        break;
                    348: 
1.1.1.8   root      349:                 case OPT_show_hostnet:
                    350:                        ShowHostnet();
                    351:                        return false;
                    352: 
1.1.1.2   root      353:                 case 'h':
                    354:                 default:
                    355:                        ShowHelp(false);
                    356:                        return false;
                    357:                }
                    358:        }
                    359: 
1.1.1.6   root      360:        // 引数がディレクトリなら、それを VM ディレクトリとし、その中の
                    361:        // nono.cfg を設定ファイルとする。
                    362:        // 引数がファイルなら、それを設定ファイルとし、そのファイルがある
                    363:        // ディレクトリを VM ディレクトリとする。
                    364:        // -c DIR       => vmdir = DIR, vmfile = DIR/nono.cfg
                    365:        // -c DIR/FILE  => vmdir = DIR, vmfile = FILE
                    366:        if (stat(cpath, &st) < 0) {
                    367:                warn("stat %s", cpath);
                    368:                return false;
1.1.1.2   root      369:        }
1.1.1.6   root      370:        if (S_ISDIR(st.st_mode)) {
                    371:                vmdir = std::string(cpath);
                    372:                if (vmdir.back() != '/') {
                    373:                        vmdir += '/';
                    374:                }
                    375:                vmfile = vmdir + "nono.cfg";
                    376:        } else if (S_ISREG(st.st_mode)) {
                    377:                vmfile = std::string(cpath);
                    378:                auto pos = vmfile.rfind('/');
                    379:                if (pos != std::string::npos) {
                    380:                        vmdir = vmfile.substr(0, pos + 1);
                    381:                } else {
                    382:                        vmdir = "./";
                    383:                }
                    384:        } else {
                    385:                warnx("-c %s: path must be file or directory", cpath);
                    386:                return false;
1.1.1.2   root      387:        }
                    388: 
                    389:        // CLI 版で -Mhelp つけても黙って起動するのはさすがにどうかと思う。
                    390:        // とは言え GUI 版のコマンドラインを CLI 版用に書き換えた時に -M を
                    391:        // 消して回らないと起動できないのも面倒なので -M オプション自体は
                    392:        // 無意味だけど許容する。うーん。
                    393:        if (IsCLI()) {
                    394:                if (monitor_opt == "help") {
                    395:                        warnx("-Mhelp is not available on CLI");
                    396:                        return false;
                    397:                }
                    398:                if (!monitor_opt.empty()) {
                    399:                        warnx("-M option is ignored on CLI");
                    400:                        return true;
                    401:                }
                    402:        }
                    403: 
1.1.1.9   root      404:        // Human モードでは実行ファイル名が必要
                    405:        if (human_mode) {
                    406:                if (exec_file == NULL) {
                    407:                        warnx("-H option needs -X");
                    408:                        return false;
                    409:                }
                    410:        }
                    411: 
1.1.1.2   root      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.