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

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

unix.superglobalmegacorp.com

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