From 69f439eff387a6ecb52734e400b297a3c85f2285 Mon Sep 17 00:00:00 2001 From: Daniel Lange Date: Tue, 21 Sep 2021 08:35:19 +0200 Subject: New upstream version 3.1.0 --- CommandLine.c | 400 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 400 insertions(+) create mode 100644 CommandLine.c (limited to 'CommandLine.c') diff --git a/CommandLine.c b/CommandLine.c new file mode 100644 index 0000000..f0d2255 --- /dev/null +++ b/CommandLine.c @@ -0,0 +1,400 @@ +/* +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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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" + "-M --no-mouse Disable the mouse\n" + "-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", name); + Platform_longOptionsUsage(name); + printf("\n" + "Long options may be passed with a single dash.\n\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; + bool enableMouse; + bool treeView; + bool allowUnicode; + bool highlightChanges; + int highlightDelaySecs; + bool readonly; +} CommandLineSettings; + +static CommandLineSettings parseArguments(const char* program, int argc, char** argv) { + + CommandLineSettings flags = { + .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, + .enableMouse = true, + .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); + exit(0); + case 'V': + printVersionFlag(program); + exit(0); + 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); + } + exit(0); + } + 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); + exit(1); + } + 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); + exit(1); + } + 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))) { + fprintf(stderr, "Error: invalid user \"%s\".\n", username); + exit(1); + } + 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); + exit(1); + } + } + flags.highlightChanges = true; + break; + } + case 128: + flags.readonly = true; + break; + + default: + if (Platform_getLongOption(opt, argc, argv) == false) + exit(1); + break; + } + } + return flags; +} + +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, ""); + + CommandLineSettings flags = parseArguments(name, argc, argv); + + if (flags.readonly) + Settings_enableReadonly(); + + Platform_init(); + + 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->treeView = true; + if (flags.highlightChanges) + settings->highlightChanges = true; + if (flags.highlightDelaySecs != -1) + settings->highlightDelaySecs = flags.highlightDelaySecs; + if (flags.sortKey > 0) { + // -t -s means "tree sorted by key" + // -s means "list sorted by key" (previous existing behavior) + if (!flags.treeView) { + settings->treeView = false; + } + Settings_setSortKey(settings, flags.sortKey); + } + + CRT_init(settings, flags.allowUnicode); + + MainPanel* panel = MainPanel_new(); + ProcessList_setPanel(pl, (Panel*) panel); + + MainPanel_updateTreeFunctions(panel, settings->treeView); + + State state = { + .settings = settings, + .ut = ut, + .pl = pl, + .mainPanel = panel, + .header = header, + .pauseProcessUpdate = false, + .hideProcessSelection = 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->allBranchesCollapsed) + ProcessList_collapseAllBranches(pl); + + ScreenManager_run(scr, NULL, NULL); + + attron(CRT_colors[RESET_COLOR]); + mvhline(LINES - 1, 0, ' ', COLS); + attroff(CRT_colors[RESET_COLOR]); + refresh(); + + 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; +} -- cgit v1.2.3