From 5679e14e2655fd26d2d4bd167e5daa92784f5c3c Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Fri, 23 Jan 2026 21:28:22 -0700 Subject: [PATCH] Switch to getopt for command line parsing Fixes #144 --- src/App.cpp | 44 ++------------- src/App.h | 4 -- src/main.cpp | 149 ++++++++++++++++++++++++++++++++------------------- 3 files changed, 98 insertions(+), 99 deletions(-) diff --git a/src/App.cpp b/src/App.cpp index 88768d6..db89ef8 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -45,8 +45,7 @@ App::App() fLastActiveWindow(nullptr), fAppPreferencesWindow(nullptr), fFindWindow(nullptr), - fPreferences(nullptr), - fSuppressInitialWindow(false) + fPreferences(nullptr) { } @@ -197,44 +196,12 @@ App::QuitRequested() } -void -App::ReadyToRun() -{ - if(CountWindows() == 0 && fSuppressInitialWindow == false) { - PostMessage(WINDOW_NEW); - } -} - - -void -App::ArgvReceived(int32 argc, char** argv) -{ - BMessage* message = CurrentMessage(); - BString cwd = message->GetString("cwd", "~"); - std::unique_ptr windowStack; - for(int32 i = 1; i < argc; ++i) { - std::string arg = argv[i]; - // FIXME: this should be handled in main.cpp probably - if(arg == "-w" || arg == "--wait" || arg == "-h" || arg == "--help") - continue; - int32 line, column; - std::string filename = ParseFileArgument(argv[i], &line, &column); - if(filename.find('/') != 0) { - BPath absolute(cwd.String(), filename.c_str(), true); - filename = absolute.Path(); - } - entry_ref ref; - BEntry(filename.c_str()).GetRef(&ref); - _ActivateOrCreateWindow(message, ref, line, column, windowStack); - } -} - - void App::RefsReceived(BMessage* message) { int32 count; if(message->GetInfo("refs", nullptr, &count) != B_OK) { + PostMessage(WINDOW_NEW); return; } @@ -248,8 +215,8 @@ App::RefsReceived(BMessage* message) trackerMessage.AddRef("refs", &ref); continue; } - const int32 line = message->GetInt32("be:line", -1); - const int32 column = message->GetInt32("be:column", -1); + const int32 line = message->GetInt32("be:line", i, -1); + const int32 column = message->GetInt32("be:column", i, -1); _ActivateOrCreateWindow(message, ref, line, column, windowStack); } } @@ -263,9 +230,6 @@ void App::MessageReceived(BMessage* message) { switch(message->what) { - case SUPPRESS_INITIAL_WINDOW: { - fSuppressInitialWindow = true; - } break; case ACTIVATE_WINDOW: { BWindow* window = nullptr; if(message->FindPointer("window", (void**) &window) == B_OK && window != nullptr) { diff --git a/src/App.h b/src/App.h index f15ccfe..e2c0586 100644 --- a/src/App.h +++ b/src/App.h @@ -24,7 +24,6 @@ class Styler; enum { - SUPPRESS_INITIAL_WINDOW = 'Siwn', WINDOW_NEW_WITH_QUIT_REPLY = 'NWwn', ACTIVATE_WINDOW = 'actw' }; @@ -42,8 +41,6 @@ class App : public BApplication { void AboutRequested(); bool QuitRequested(); - void ReadyToRun(); - void ArgvReceived(int32 argc, char** argv); void RefsReceived(BMessage* message); void MessageReceived(BMessage* message); @@ -67,7 +64,6 @@ class App : public BApplication { Styler* fStyler; BPath fPreferencesFile; - bool fSuppressInitialWindow; }; diff --git a/src/main.cpp b/src/main.cpp index c9feffe..ddeedb6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,75 +5,114 @@ #include "App.h" -#include - #include +#include +#include + #include "Utils.h" +void +_PrintUsage() +{ + std::cerr + << "Usage: Koder [options] file..." << std::endl + << "Options:" << std::endl + << " -h, --help\t\tPrints this message." << std::endl + << " -w, --wait\t\tWait for the window to quit before returning." << std::endl + << "\t\t\tOpening in window stacks is not supported in this mode." << std::endl + << "\t\t\tCurrently accepts only one filename." << std::endl; +} + + int main(int argc, char** argv) { - std::string arg1 = argc > 1 ? argv[1] : ""; - if(argc > 1 && (arg1 == "-h" || arg1 == "--help")) { - fprintf(stderr, "Usage: Koder [options] file...\n"); - fprintf(stderr, "Options:\n"); - fprintf(stderr, " -h, --help\t\tPrints this message.\n"); - fprintf(stderr, " -w, --wait\t\tWait for the window to quit before " - "returning.\n" - "\t\t\tOpening in window stacks is not supported in this mode.\n" - "\t\t\tCurrently accepts only one filename.\n"); + int option_index = 0; + struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"wait", no_argument, 0, 'w'}, + {"launch", no_argument, 0, 0}, + {0, 0, 0, 0}}; + + bool waitForExit = false; + int c; + while((c = getopt_long(argc, argv, "hw", long_options, &option_index)) != -1) { + switch(c) { + case 'w': { + waitForExit = true; + } break; + case 'h': { + _PrintUsage(); + return 1; + } break; + case 0: { + if(strcmp(long_options[option_index].name, "launch") != 0) { + return 1; + } + App* app = new App(); + app->Init(); + app->Run(); + delete app; + return 0; + } break; + default: + _PrintUsage(); + return 1; + break; + } + } + + if(waitForExit && argc - optind > 1) { + std::cerr << "Error: Only one filename allowed when launching in --wait mode." << std::endl; return 1; } - if(argc > 1 && (arg1 == "-w" || arg1 == "--wait")) { - if(argc > 3) { - fprintf(stderr, "Koder accepts only one filename when launching " - "in --wait mode.\n"); + BMessage windowMessage(waitForExit ? (system_message_code) WINDOW_NEW_WITH_QUIT_REPLY : B_REFS_RECEIVED); + while(optind < argc) { + int32 line, column; + BPath filePath(ParseFileArgument(argv[optind++], &line, &column).c_str(), nullptr, true); + if(filePath.InitCheck() != B_OK) { + std::cerr << "Error: Invalid file path specified." << std::endl; return 1; } - BRoster roster; - team_id team = roster.TeamFor(gAppMime); - if(team == B_ERROR) { - BMessage* suppressMessage = new BMessage(SUPPRESS_INITIAL_WINDOW); - status_t status = roster.Launch(gAppMime, suppressMessage, &team); - delete suppressMessage; - if(status != B_OK && status != B_ALREADY_RUNNING) { - fprintf(stderr, "An issue occured while trying to launch Koder.\n"); - return 1; - } + + entry_ref ref; + BEntry entry(filePath.Path(), true); + if(entry.InitCheck() != B_OK || (entry.Exists() == true && entry.IsFile() == false)) { + std::cerr << "Error: Specified path is not a regular file." << std::endl; + return 1; } - BMessage windowMessage(WINDOW_NEW_WITH_QUIT_REPLY); - // parse filename if any - // TODO: support -- for piping - if(argc > 2) { - int32 line, column; - std::string filename = ParseFileArgument(argv[2], &line, &column); - if(filename.find('/') != 0) { - BPath absolute(".", filename.c_str(), true); - filename = absolute.Path(); - } - entry_ref ref; - BEntry(filename.c_str()).GetRef(&ref); - windowMessage.AddRef("refs", &ref); - if(line != -1) { - windowMessage.AddInt32("be:line", line); - } - if(column != -1) { - windowMessage.AddInt32("be:column", column); - } + if(entry.GetRef(&ref) != B_OK) { + std::cerr << "Error: Unable to get entry_ref for path." << std::endl; + return 1; } - BMessenger messenger(gAppMime, team); - BMessage reply; - messenger.SendMessage(&windowMessage, &reply); - return 0; - } else { - App* app = new App(); - app->Init(); - app->Run(); - delete app; - - return 0; + windowMessage.AddRef("refs", &ref); + // always add column and line so that the count is the same as the number of refs + windowMessage.AddInt32("be:column", column == -1 ? 0 : column); + windowMessage.AddInt32("be:line", line == -1 ? 0 : line); + } + + BEntry entry(argv[0], true); + entry_ref ref; + if(entry.InitCheck() != B_OK || entry.GetRef(&ref) != B_OK) { + std::cerr << "Error: Unable to determing Koder application path" << std::endl; + return 1; + } + + const char* args[] = { "--launch", nullptr }; + team_id team = -1; + // use the entry_ref version of Launch() to make sure B_SINGLE_LAUNCH works correctly + status_t status = be_roster->Launch(&ref, 1, args, &team); + if(status != B_OK && status != B_ALREADY_RUNNING) { + std::cerr << "An error occurred while trying to launch Koder." << std::endl; + return 1; } + + BMessenger messenger(gAppMime, team); + BMessage reply; + messenger.SendMessage(&windowMessage, &reply); + + return 0; }