diff --git a/CommandLine.c b/CommandLine.c index 6e52f7a54..59448b2b3 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -54,6 +54,7 @@ static void printHelpFlag(const char* name) { "-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" + "-S --state=STATESCHARS Show only the states matching the given states\n" "-h --help Print this help screen\n" "-H --highlight-changes[=DELAY] Highlight new and old processes\n", name); #ifdef HAVE_GETMOUSE @@ -78,6 +79,7 @@ static void printHelpFlag(const char* name) { typedef struct CommandLineSettings_ { Hashtable* pidMatchList; char* commFilter; + char* stateFilter; uid_t userId; int sortKey; int delay; @@ -98,6 +100,7 @@ static CommandLineStatus parseArguments(int argc, char** argv, CommandLineSettin *flags = (CommandLineSettings) { .pidMatchList = NULL, .commFilter = NULL, + .stateFilter = NULL, .userId = (uid_t)-1, // -1 is guaranteed to be an invalid uid_t (see setreuid(2)) .sortKey = 0, .delay = -1, @@ -128,6 +131,7 @@ static CommandLineStatus parseArguments(int argc, char** argv, CommandLineSettin {"tree", no_argument, 0, 't'}, {"pid", required_argument, 0, 'p'}, {"filter", required_argument, 0, 'F'}, + {"state", required_argument, 0, 'S'}, {"highlight-changes", optional_argument, 0, 'H'}, {"readonly", no_argument, 0, 128}, PLATFORM_LONG_OPTIONS @@ -136,7 +140,7 @@ static CommandLineStatus parseArguments(int argc, char** argv, CommandLineSettin int opt, opti = 0; /* Parse arguments */ - while ((opt = getopt_long(argc, argv, "hVMCs:td:n:u::Up:F:H::", long_opts, &opti))) { + while ((opt = getopt_long(argc, argv, "hVMCs:td:n:u::Up:F:H::S:", long_opts, &opti))) { if (opt == EOF) break; @@ -255,6 +259,36 @@ static CommandLineStatus parseArguments(int argc, char** argv, CommandLineSettin } free_and_xStrdup(&flags->commFilter, optarg); break; + + case 'S': + assert(optarg); + if (optarg[0] == '\0') { + fprintf(stderr, "Error: state filter cannot be empty.\n"); + return STATUS_ERROR_EXIT; + } + + bool valid_states[256] = { false }; + for (ProcessState s = UNKNOWN; s <= SLEEPING; s++) { + char c = Process_stateChar(s); + valid_states[(int)c] = true; + } + + bool valid_arg = true; + for (char* c = optarg; *c != '\0' && valid_arg; c++) { + if (*c == ',' || isspace(*c)) + continue; + + valid_arg &= valid_states[(int)*c]; + } + + if (!valid_arg) { + fprintf(stderr, "Error: invalid state filter value \"%s\".\n", optarg); + return STATUS_ERROR_EXIT; + } + + free_and_xStrdup(&flags->stateFilter, optarg); + break; + case 'H': { const char* delay = optarg; if (!delay && optind < argc && argv[optind] != NULL && @@ -382,6 +416,9 @@ int CommandLine_run(int argc, char** argv) { .hideSelection = false, .hideMeters = false, }; + if (flags.stateFilter) { + free_and_xStrdup(&settings->stateFilter, flags.stateFilter); + } MainPanel_setState(panel, &state); if (flags.commFilter) diff --git a/Process.c b/Process.c index 39cb92c89..f1db43e2f 100644 --- a/Process.c +++ b/Process.c @@ -536,7 +536,7 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin } } -static inline char processStateChar(ProcessState state) { +char Process_stateChar(ProcessState state) { switch (state) { case UNKNOWN: return '?'; case RUNNABLE: return 'U'; @@ -716,7 +716,7 @@ void Process_writeField(const Process* this, RichString* str, RowField field) { case SESSION: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->session); break; case STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break; case STATE: - xSnprintf(buffer, n, "%c ", processStateChar(this->state)); + xSnprintf(buffer, n, "%c ", Process_stateChar(this->state)); switch (this->state) { case RUNNABLE: case RUNNING: @@ -860,6 +860,14 @@ static bool Process_matchesFilter(const Process* this, const Table* table) { if (pt->pidMatchList && !Hashtable_get(pt->pidMatchList, Process_getThreadGroup(this))) return true; + const char* stateFilter = host->settings->stateFilter; + if (stateFilter) { + char stateChar = Process_stateChar(this->state); + if (!strchr(stateFilter, stateChar)) { + return true; + } + } + return false; } diff --git a/Process.h b/Process.h index 38e2711f6..61f79cfa8 100644 --- a/Process.h +++ b/Process.h @@ -37,7 +37,7 @@ typedef enum Tristate_ { /* Core process states (shared by platforms) * NOTE: The enum has an ordering that is important! - * See processStateChar in process.c for ProcessSate -> letter mapping */ + * See Process_stateChar in process.c for ProcessSate -> letter mapping */ typedef enum ProcessState_ { UNKNOWN = 1, RUNNABLE, @@ -335,6 +335,8 @@ void Process_makeCommandStr(Process* this, const struct Settings_ *settings); void Process_writeCommand(const Process* this, int attr, int baseAttr, RichString* str); +char Process_stateChar(ProcessState state); + void Process_updateCPUFieldWidths(float percentage); #endif diff --git a/Settings.c b/Settings.c index ae5402fba..7dea1afa4 100644 --- a/Settings.c +++ b/Settings.c @@ -49,6 +49,7 @@ static void Settings_deleteScreens(Settings* this) { } void Settings_delete(Settings* this) { + free(this->stateFilter); free(this->filename); free(this->initialFilename); Settings_deleteColumns(this); diff --git a/Settings.h b/Settings.h index 01e808e86..13aca4691 100644 --- a/Settings.h +++ b/Settings.h @@ -109,6 +109,7 @@ typedef struct Settings_ { #ifdef HAVE_LIBHWLOC bool topologyAffinity; #endif + char* stateFilter; bool changed; uint64_t lastUpdate;