diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:00:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:00:29 +0000 |
commit | 52e25213825024b8bb446eb26b03bedc9d5c2103 (patch) | |
tree | da70bf44b2423f6f8e9a070c063fed79d190b489 /CommandLine.c | |
parent | Initial commit. (diff) | |
download | htop-52e25213825024b8bb446eb26b03bedc9d5c2103.tar.xz htop-52e25213825024b8bb446eb26b03bedc9d5c2103.zip |
Adding upstream version 3.2.2.upstream/3.2.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | CommandLine.c | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/CommandLine.c b/CommandLine.c new file mode 100644 index 0000000..682e054 --- /dev/null +++ b/CommandLine.c @@ -0,0 +1,425 @@ +/* +htop - CommandLine.c +(C) 2004-2011 Hisham H. Muhammad +(C) 2020-2021 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include "CommandLine.h" + +#include <assert.h> +#include <ctype.h> +#include <getopt.h> +#include <locale.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "Action.h" +#include "CRT.h" +#include "DynamicColumn.h" +#include "DynamicMeter.h" +#include "Hashtable.h" +#include "Header.h" +#include "IncSet.h" +#include "MainPanel.h" +#include "MetersPanel.h" +#include "Panel.h" +#include "Platform.h" +#include "Process.h" +#include "ProcessList.h" +#include "ProvideCurses.h" +#include "ScreenManager.h" +#include "Settings.h" +#include "UsersTable.h" +#include "XUtils.h" + + +static void printVersionFlag(const char* name) { + printf("%s " VERSION "\n", name); +} + +static void printHelpFlag(const char* name) { + printf("%s " VERSION "\n" + COPYRIGHT "\n" + "Released under the GNU GPLv2+.\n\n" + "-C --no-color Use a monochrome color scheme\n" + "-d --delay=DELAY Set the delay between updates, in tenths of seconds\n" + "-F --filter=FILTER Show only the commands matching the given filter\n" + "-h --help Print this help screen\n" + "-H --highlight-changes[=DELAY] Highlight new and old processes\n", name); +#ifdef HAVE_GETMOUSE + printf("-M --no-mouse Disable the mouse\n"); +#endif + printf("-p --pid=PID[,PID,PID...] Show only the given PIDs\n" + " --readonly Disable all system and process changing features\n" + "-s --sort-key=COLUMN Sort by COLUMN in list view (try --sort-key=help for a list)\n" + "-t --tree Show the tree view (can be combined with -s)\n" + "-u --user[=USERNAME] Show only processes for a given user (or $USER)\n" + "-U --no-unicode Do not use unicode but plain ASCII\n" + "-V --version Print version info\n"); + Platform_longOptionsUsage(name); + printf("\n" + "Press F1 inside %s for online help.\n" + "See 'man %s' for more information.\n", name, name); +} + +// ---------------------------------------- + +typedef struct CommandLineSettings_ { + Hashtable* pidMatchList; + char* commFilter; + uid_t userId; + int sortKey; + int delay; + bool useColors; +#ifdef HAVE_GETMOUSE + bool enableMouse; +#endif + bool treeView; + bool allowUnicode; + bool highlightChanges; + int highlightDelaySecs; + bool readonly; +} CommandLineSettings; + +static CommandLineStatus parseArguments(const char* program, int argc, char** argv, CommandLineSettings* flags) { + + *flags = (CommandLineSettings) { + .pidMatchList = NULL, + .commFilter = NULL, + .userId = (uid_t)-1, // -1 is guaranteed to be an invalid uid_t (see setreuid(2)) + .sortKey = 0, + .delay = -1, + .useColors = true, +#ifdef HAVE_GETMOUSE + .enableMouse = true, +#endif + .treeView = false, + .allowUnicode = true, + .highlightChanges = false, + .highlightDelaySecs = -1, + .readonly = false, + }; + + const struct option long_opts[] = + { + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + {"delay", required_argument, 0, 'd'}, + {"sort-key", required_argument, 0, 's'}, + {"user", optional_argument, 0, 'u'}, + {"no-color", no_argument, 0, 'C'}, + {"no-colour", no_argument, 0, 'C'}, + {"no-mouse", no_argument, 0, 'M'}, + {"no-unicode", no_argument, 0, 'U'}, + {"tree", no_argument, 0, 't'}, + {"pid", required_argument, 0, 'p'}, + {"filter", required_argument, 0, 'F'}, + {"highlight-changes", optional_argument, 0, 'H'}, + {"readonly", no_argument, 0, 128}, + PLATFORM_LONG_OPTIONS + {0, 0, 0, 0} + }; + + int opt, opti = 0; + /* Parse arguments */ + while ((opt = getopt_long(argc, argv, "hVMCs:td:u::Up:F:H::", long_opts, &opti))) { + if (opt == EOF) + break; + switch (opt) { + case 'h': + printHelpFlag(program); + return STATUS_OK_EXIT; + case 'V': + printVersionFlag(program); + return STATUS_OK_EXIT; + case 's': + assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */ + if (String_eq(optarg, "help")) { + for (int j = 1; j < LAST_PROCESSFIELD; j++) { + const char* name = Process_fields[j].name; + const char* description = Process_fields[j].description; + if (name) + printf("%19s %s\n", name, description); + } + return STATUS_OK_EXIT; + } + flags->sortKey = 0; + for (int j = 1; j < LAST_PROCESSFIELD; j++) { + if (Process_fields[j].name == NULL) + continue; + if (String_eq(optarg, Process_fields[j].name)) { + flags->sortKey = j; + break; + } + } + if (flags->sortKey == 0) { + fprintf(stderr, "Error: invalid column \"%s\".\n", optarg); + return STATUS_ERROR_EXIT; + } + break; + case 'd': + if (sscanf(optarg, "%16d", &(flags->delay)) == 1) { + if (flags->delay < 1) + flags->delay = 1; + if (flags->delay > 100) + flags->delay = 100; + } else { + fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg); + return STATUS_ERROR_EXIT; + } + break; + case 'u': + { + const char* username = optarg; + if (!username && optind < argc && argv[optind] != NULL && + (argv[optind][0] != '\0' && argv[optind][0] != '-')) { + username = argv[optind++]; + } + + if (!username) { + flags->userId = geteuid(); + } else if (!Action_setUserOnly(username, &(flags->userId))) { + for (const char* itr = username; *itr; ++itr) + if (!isdigit((unsigned char)*itr)) { + fprintf(stderr, "Error: invalid user \"%s\".\n", username); + return STATUS_ERROR_EXIT; + } + flags->userId = atol(username); + } + break; + } + case 'C': + flags->useColors = false; + break; + case 'M': +#ifdef HAVE_GETMOUSE + flags->enableMouse = false; +#endif + break; + case 'U': + flags->allowUnicode = false; + break; + case 't': + flags->treeView = true; + break; + case 'p': { + assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */ + char* argCopy = xStrdup(optarg); + char* saveptr; + const char* pid = strtok_r(argCopy, ",", &saveptr); + + if (!flags->pidMatchList) { + flags->pidMatchList = Hashtable_new(8, false); + } + + while (pid) { + unsigned int num_pid = atoi(pid); + // deepcode ignore CastIntegerToAddress: we just want a non-NULL pointer here + Hashtable_put(flags->pidMatchList, num_pid, (void*) 1); + pid = strtok_r(NULL, ",", &saveptr); + } + free(argCopy); + + break; + } + case 'F': { + assert(optarg); + free_and_xStrdup(&flags->commFilter, optarg); + break; + } + case 'H': { + const char* delay = optarg; + if (!delay && optind < argc && argv[optind] != NULL && + (argv[optind][0] != '\0' && argv[optind][0] != '-')) { + delay = argv[optind++]; + } + if (delay) { + if (sscanf(delay, "%16d", &(flags->highlightDelaySecs)) == 1) { + if (flags->highlightDelaySecs < 1) + flags->highlightDelaySecs = 1; + } else { + fprintf(stderr, "Error: invalid highlight delay value \"%s\".\n", delay); + return STATUS_ERROR_EXIT; + } + } + flags->highlightChanges = true; + break; + } + case 128: + flags->readonly = true; + break; + + default: { + CommandLineStatus status; + if ((status = Platform_getLongOption(opt, argc, argv)) != STATUS_OK) + return status; + break; + } + } + } + + if (optind < argc) { + fprintf(stderr, "Error: unsupported non-option ARGV-elements:"); + while (optind < argc) + fprintf(stderr, " %s", argv[optind++]); + fprintf(stderr, "\n"); + return STATUS_ERROR_EXIT; + } + + return STATUS_OK; +} + +static void CommandLine_delay(ProcessList* pl, unsigned long millisec) { + struct timespec req = { + .tv_sec = 0, + .tv_nsec = millisec * 1000000L + }; + while (nanosleep(&req, &req) == -1) + continue; + Platform_gettime_realtime(&pl->realtime, &pl->realtimeMs); +} + +static void setCommFilter(State* state, char** commFilter) { + ProcessList* pl = state->pl; + IncSet* inc = state->mainPanel->inc; + + IncSet_setFilter(inc, *commFilter); + pl->incFilter = IncSet_filter(inc); + + free(*commFilter); + *commFilter = NULL; +} + +int CommandLine_run(const char* name, int argc, char** argv) { + + /* initialize locale */ + const char* lc_ctype; + if ((lc_ctype = getenv("LC_CTYPE")) || (lc_ctype = getenv("LC_ALL"))) + setlocale(LC_CTYPE, lc_ctype); + else + setlocale(LC_CTYPE, ""); + + CommandLineStatus status = STATUS_OK; + CommandLineSettings flags = { 0 }; + + if ((status = parseArguments(name, argc, argv, &flags)) != STATUS_OK) + return status != STATUS_OK_EXIT ? 1 : 0; + + if (flags.readonly) + Settings_enableReadonly(); + + if (!Platform_init()) + return 1; + + Process_setupColumnWidths(); + + UsersTable* ut = UsersTable_new(); + Hashtable* dc = DynamicColumns_new(); + Hashtable* dm = DynamicMeters_new(); + if (!dc) + dc = Hashtable_new(0, true); + + ProcessList* pl = ProcessList_new(ut, dm, dc, flags.pidMatchList, flags.userId); + + Settings* settings = Settings_new(pl->activeCPUs, dc); + pl->settings = settings; + + Header* header = Header_new(pl, settings, 2); + + Header_populateFromSettings(header); + + if (flags.delay != -1) + settings->delay = flags.delay; + if (!flags.useColors) + settings->colorScheme = COLORSCHEME_MONOCHROME; +#ifdef HAVE_GETMOUSE + if (!flags.enableMouse) + settings->enableMouse = false; +#endif + if (flags.treeView) + settings->ss->treeView = true; + if (flags.highlightChanges) + settings->highlightChanges = true; + if (flags.highlightDelaySecs != -1) + settings->highlightDelaySecs = flags.highlightDelaySecs; + if (flags.sortKey > 0) { + // -t -s <key> means "tree sorted by key" + // -s <key> means "list sorted by key" (previous existing behavior) + if (!flags.treeView) { + settings->ss->treeView = false; + } + ScreenSettings_setSortKey(settings->ss, flags.sortKey); + } + + CRT_init(settings, flags.allowUnicode); + + MainPanel* panel = MainPanel_new(); + ProcessList_setPanel(pl, (Panel*) panel); + + MainPanel_updateLabels(panel, settings->ss->treeView, flags.commFilter); + + State state = { + .settings = settings, + .ut = ut, + .pl = pl, + .mainPanel = panel, + .header = header, + .pauseProcessUpdate = false, + .hideProcessSelection = false, + .hideMeters = false, + }; + + MainPanel_setState(panel, &state); + if (flags.commFilter) + setCommFilter(&state, &(flags.commFilter)); + + ScreenManager* scr = ScreenManager_new(header, settings, &state, true); + ScreenManager_add(scr, (Panel*) panel, -1); + + ProcessList_scan(pl, false); + CommandLine_delay(pl, 75); + ProcessList_scan(pl, false); + + if (settings->ss->allBranchesCollapsed) + ProcessList_collapseAllBranches(pl); + + ScreenManager_run(scr, NULL, NULL, NULL); + + Platform_done(); + + CRT_done(); + + if (settings->changed) { + int r = Settings_write(settings, false); + if (r < 0) + fprintf(stderr, "Can not save configuration to %s: %s\n", settings->filename, strerror(-r)); + } + + Header_delete(header); + ProcessList_delete(pl); + + ScreenManager_delete(scr); + MetersPanel_cleanup(); + + UsersTable_delete(ut); + + if (flags.pidMatchList) + Hashtable_delete(flags.pidMatchList); + + CRT_resetSignalHandlers(); + + /* Delete these last, since they can get accessed in the crash handler */ + Settings_delete(settings); + DynamicColumns_delete(dc); + DynamicMeters_delete(dm); + + return 0; +} |