diff --git a/sysutils/htop/Makefile b/sysutils/htop/Makefile index f409ba4563c1..e6a2dbdc89e7 100644 --- a/sysutils/htop/Makefile +++ b/sysutils/htop/Makefile @@ -14,6 +14,9 @@ LICENSE_FILE= ${WRKSRC}/COPYING USES= autoreconf compiler:c11 libtool localbase ncurses pkgconfig \ python:build shebangfix +BUILD_DEPENDS+= libgnuregex>=6.0:devel/libgnuregex +RUN_DEPENDS+= ${BUILD_DEPENDS} + USE_GITHUB= yes GH_ACCOUNT= htop-dev diff --git a/sysutils/htop/files/patch-Action.c b/sysutils/htop/files/patch-Action.c new file mode 100644 index 000000000000..72c144dd63e8 --- /dev/null +++ b/sysutils/htop/files/patch-Action.c @@ -0,0 +1,14 @@ +--- Action.c.orig 2020-12-22 06:39:42 UTC ++++ Action.c +@@ -238,9 +238,8 @@ static Htop_Reaction actionToggleTreeView(State* st) { + } + + static Htop_Reaction actionIncFilter(State* st) { +- IncSet* inc = ((MainPanel*)st->panel)->inc; +- IncSet_activate(inc, INC_FILTER, st->panel); +- st->pl->incFilter = IncSet_filter(inc); ++ st->pl->incSet = ((MainPanel*)st->panel)->inc; ++ IncSet_activate(st->pl->incSet, INC_FILTER, st->panel); + return HTOP_REFRESH | HTOP_KEEP_FOLLOWING; + } + diff --git a/sysutils/htop/files/patch-IncSet.c b/sysutils/htop/files/patch-IncSet.c new file mode 100644 index 000000000000..6a0445f2d672 --- /dev/null +++ b/sysutils/htop/files/patch-IncSet.c @@ -0,0 +1,176 @@ +--- IncSet.c.orig 2020-12-22 06:39:42 UTC ++++ IncSet.c +@@ -12,6 +12,7 @@ in the source distribution for its full text. + #include + #include + #include ++#include + + #include "CRT.h" + #include "ListItem.h" +@@ -39,9 +40,11 @@ static inline void IncMode_initSearch(IncMode* search) + search->isFilter = false; + } + +-static const char* const filterFunctions[] = {"Done ", "Clear ", " Filter: ", NULL}; +-static const char* const filterKeys[] = {"Enter", "Esc", " "}; +-static const int filterEvents[] = {13, 27, ERR}; ++static const char* const filterFunctions[] = {"Done ", "Clear ", "!Regexp ", "Filter Out ", " Filter: ", NULL}; ++static const char* const filterKeys[] = {"Enter", "Esc", "F5", "F6", " "}; ++static const char* regexLabels[] = {"Regexp ", "!Regexp "}; ++static const char* excludeLabels[] = {"Filter Out ", "Filter In "}; ++static const int filterEvents[] = {13, 27, KEY_F(5), KEY_F(6), ERR}; + + static inline void IncMode_initFilter(IncMode* filter) { + memset(filter, 0, sizeof(IncMode)); +@@ -61,24 +64,79 @@ IncSet* IncSet_new(FunctionBar* bar) { + this->defaultBar = bar; + this->filtering = false; + this->found = false; ++ this->rawFilter = NULL; ++ this->regex = true; ++ this->exclude = false; ++ this->regexAlloc = false; + return this; + } + ++static void freeFilter(IncSet* this); ++ + void IncSet_delete(IncSet* this) { + IncMode_done(&(this->modes[0])); + IncMode_done(&(this->modes[1])); ++ freeFilter(this); + free(this); + } + ++ ++static void updateBarLabels(IncSet* this) { ++ if (this->active == &(this->modes[INC_FILTER])) { ++ IncMode* filterMode = &(this->modes[INC_FILTER]); ++ FunctionBar_setLabel(filterMode->bar, KEY_F(5), regexLabels[this->regex ? 1 : 0]); ++ FunctionBar_setLabel(filterMode->bar, KEY_F(6), excludeLabels[this->exclude ? 1 : 0]); ++ } ++} ++ ++static bool testWithFilter(IncSet* this, const char* testSubject) { ++ if (!this || !this->filtering) { ++ return true; ++ } ++ if (this->regexAlloc) { ++ return regexec(&this->regexFilter, testSubject, 0, NULL, 0) ? this->exclude : !this->exclude; ++ } ++ return String_contains_i(testSubject, this->rawFilter ? this->rawFilter : "" ) ? !this->exclude : this->exclude; ++} ++ ++bool IncSet_filterTest(IncSet* this, const char* str) { ++ return !this || !this->filtering || testWithFilter(this, str); ++} ++ ++static void freeFilter(IncSet* this) { ++ if (this->regexAlloc) { ++ regfree(&this->regexFilter); ++ this->regexAlloc = false; ++ } ++ if (this->rawFilter) { ++ free(this->rawFilter); ++ this->rawFilter = NULL; ++ } ++} ++ ++static void compileFilter(IncSet* this, const char* filterStr) { ++ if (this->rawFilter) { ++ freeFilter(this); ++ } ++ if (filterStr) { ++ this->rawFilter = xStrdup(filterStr); ++ if (this->regex) { ++ int result = regcomp(&this->regexFilter, this->rawFilter, REG_EXTENDED|REG_NOSUB|REG_ICASE); ++ if (!result) { ++ this->regexAlloc = true; ++ } ++ } ++ } ++} ++ + static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) { + Object* selected = Panel_getSelected(panel); + Panel_prune(panel); + if (this->filtering) { + int n = 0; +- const char* incFilter = this->modes[INC_FILTER].buffer; + for (int i = 0; i < Vector_size(lines); i++) { + ListItem* line = (ListItem*)Vector_get(lines, i); +- if (String_contains_i(line->value, incFilter)) { ++ if (testWithFilter(this, line->value)) { + Panel_add(panel, (Object*)line); + if (selected == (Object*)line) { + Panel_setSelected(panel, n); +@@ -98,10 +156,10 @@ static void updateWeakPanel(IncSet* this, Panel* panel + } + } + +-static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) { ++static bool search(IncSet* this, Panel* panel, IncMode_GetPanelValue getPanelValue) { + int size = Panel_size(panel); + for (int i = 0; i < size; i++) { +- if (String_contains_i(getPanelValue(panel, i), mode->buffer)) { ++ if (testWithFilter(this, getPanelValue(panel, i))) { + Panel_setSelected(panel, i); + return true; + } +@@ -155,6 +213,18 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* pan + + IncMode_find(mode, panel, getPanelValue, 1); + doSearch = false; ++ } else if (ch == KEY_F(5)) { ++ if (mode->isFilter) { ++ filterChanged = true; ++ this->regex = !this->regex; ++ updateBarLabels(this); ++ } ++ } else if (ch == KEY_F(6)) { ++ if (mode->isFilter) { ++ filterChanged = true; ++ this->exclude = !this->exclude; ++ updateBarLabels(this); ++ } + } else if (0 < ch && ch < 255 && isprint((unsigned char)ch)) { + if (mode->index < INCMODE_MAX) { + mode->buffer[mode->index] = ch; +@@ -175,6 +245,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* pan + filterChanged = true; + if (mode->index == 0) { + this->filtering = false; ++ freeFilter(this); + IncMode_reset(mode); + } + } +@@ -188,6 +259,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* pan + filterChanged = true; + if (ch == 27) { + this->filtering = false; ++ freeFilter(this); + IncMode_reset(mode); + } + } else { +@@ -199,8 +271,11 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* pan + Panel_setDefaultBar(panel); + doSearch = false; + } ++ if (filterChanged) { ++ compileFilter(this, mode->buffer); ++ } + if (doSearch) { +- this->found = search(mode, panel, getPanelValue); ++ this->found = search(this, panel, getPanelValue); + } + if (filterChanged && lines) { + updateWeakPanel(this, panel, lines); +@@ -215,6 +290,7 @@ const char* IncSet_getListItemValue(Panel* panel, int + + void IncSet_activate(IncSet* this, IncType type, Panel* panel) { + this->active = &(this->modes[type]); ++ updateBarLabels(this); + panel->currentBar = this->active->bar; + } + diff --git a/sysutils/htop/files/patch-IncSet.h b/sysutils/htop/files/patch-IncSet.h new file mode 100644 index 000000000000..cc151b4d44a2 --- /dev/null +++ b/sysutils/htop/files/patch-IncSet.h @@ -0,0 +1,31 @@ +--- IncSet.h.orig 2020-12-22 06:39:42 UTC ++++ IncSet.h +@@ -9,6 +9,7 @@ in the source distribution for its full text. + + #include + #include ++#include + + #include "FunctionBar.h" + #include "Panel.h" +@@ -34,6 +35,11 @@ typedef struct IncSet_ { + FunctionBar* defaultBar; + bool filtering; + bool found; ++ bool regex; ++ bool exclude; ++ bool regexAlloc; ++ char* rawFilter; ++ regex_t regexFilter; + } IncSet; + + static inline const char* IncSet_filter(const IncSet* this) { +@@ -51,6 +57,8 @@ void IncSet_delete(IncSet* this); + bool IncSet_next(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue); + + bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue); ++ ++bool IncSet_filterTest(IncSet* this, const char* str); + + bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines); + diff --git a/sysutils/htop/files/patch-InfoScreen.c b/sysutils/htop/files/patch-InfoScreen.c new file mode 100644 index 000000000000..fb6336049aa6 --- /dev/null +++ b/sysutils/htop/files/patch-InfoScreen.c @@ -0,0 +1,21 @@ +--- InfoScreen.c.orig 2020-12-22 06:39:42 UTC ++++ InfoScreen.c +@@ -63,8 +63,7 @@ void InfoScreen_drawTitled(InfoScreen* this, const cha + + void InfoScreen_addLine(InfoScreen* this, const char* line) { + Vector_add(this->lines, (Object*) ListItem_new(line, 0)); +- const char* incFilter = IncSet_filter(this->inc); +- if (!incFilter || String_contains_i(line, incFilter)) { ++ if (IncSet_filterTest(this->inc, line)) { + Panel_add(this->display, Vector_get(this->lines, Vector_size(this->lines) - 1)); + } + } +@@ -73,7 +72,7 @@ void InfoScreen_appendLine(InfoScreen* this, const cha + ListItem* last = (ListItem*)Vector_get(this->lines, Vector_size(this->lines) - 1); + ListItem_append(last, line); + const char* incFilter = IncSet_filter(this->inc); +- if (incFilter && Panel_get(this->display, Panel_size(this->display) - 1) != (Object*)last && String_contains_i(line, incFilter)) { ++ if (incFilter && Panel_get(this->display, Panel_size(this->display) - 1) != (Object*)last && IncSet_filterTest(this->inc, line)) { + Panel_add(this->display, (Object*)last); + } + } diff --git a/sysutils/htop/files/patch-MainPanel.c b/sysutils/htop/files/patch-MainPanel.c new file mode 100644 index 000000000000..a6b99ccfb3e7 --- /dev/null +++ b/sysutils/htop/files/patch-MainPanel.c @@ -0,0 +1,11 @@ +--- MainPanel.c.orig 2020-12-22 06:39:42 UTC ++++ MainPanel.c +@@ -74,7 +74,7 @@ static HandlerResult MainPanel_eventHandler(Panel* sup + } else if (ch != ERR && this->inc->active) { + bool filterChanged = IncSet_handleKey(this->inc, ch, super, (IncMode_GetPanelValue) MainPanel_getValue, NULL); + if (filterChanged) { +- this->state->pl->incFilter = IncSet_filter(this->inc); ++ this->state->pl->incSet = this->inc; + reaction = HTOP_REFRESH | HTOP_REDRAW_BAR; + } + if (this->inc->found) { diff --git a/sysutils/htop/files/patch-ProcessList.c b/sysutils/htop/files/patch-ProcessList.c new file mode 100644 index 000000000000..16041507a4d0 --- /dev/null +++ b/sysutils/htop/files/patch-ProcessList.c @@ -0,0 +1,28 @@ +--- ProcessList.c.orig 2020-12-22 06:39:42 UTC ++++ ProcessList.c +@@ -6,6 +6,7 @@ in the source distribution for its full text. + */ + + #include "ProcessList.h" ++#include "IncSet.h" + + #include + #include +@@ -483,8 +484,6 @@ void ProcessList_expandTree(ProcessList* this) { + } + + void ProcessList_rebuildPanel(ProcessList* this) { +- const char* incFilter = this->incFilter; +- + int currPos = Panel_getSelectedIndex(this->panel); + pid_t currPid = this->following != -1 ? this->following : 0; + int currScrollV = this->panel->scrollV; +@@ -497,7 +496,7 @@ void ProcessList_rebuildPanel(ProcessList* this) { + + if ( (!p->show) + || (this->userId != (uid_t) -1 && (p->st_uid != this->userId)) +- || (incFilter && !(String_contains_i(Process_getCommand(p), incFilter))) ++ || !IncSet_filterTest(this->incSet, p->comm) + || (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) ) + continue; + diff --git a/sysutils/htop/files/patch-ProcessList.h b/sysutils/htop/files/patch-ProcessList.h new file mode 100644 index 000000000000..23c71c73411e --- /dev/null +++ b/sysutils/htop/files/patch-ProcessList.h @@ -0,0 +1,19 @@ +--- ProcessList.h.orig 2020-12-22 06:39:42 UTC ++++ ProcessList.h +@@ -18,6 +18,7 @@ in the source distribution for its full text. + #include "Process.h" + #include "RichString.h" + #include "Settings.h" ++#include "IncSet.h" + #include "UsersTable.h" + #include "Vector.h" + +@@ -48,7 +49,7 @@ typedef struct ProcessList_ { + Panel* panel; + int following; + uid_t userId; +- const char* incFilter; ++ IncSet* incSet; + Hashtable* pidMatchList; + + #ifdef HAVE_LIBHWLOC diff --git a/sysutils/htop/files/patch-htop.1.in b/sysutils/htop/files/patch-htop.1.in new file mode 100644 index 000000000000..6730532c56e9 --- /dev/null +++ b/sysutils/htop/files/patch-htop.1.in @@ -0,0 +1,14 @@ +--- htop.1.in.orig 2020-12-22 06:39:42 UTC ++++ htop.1.in +@@ -143,7 +143,10 @@ bindings take precedence. + .B F4, \\\\ + Incremental process filtering: type in part of a process command line and + only processes whose names match will be shown. To cancel filtering, +-enter the Filter option again and press Esc. ++enter the Filter option again and press Esc. F5 will toggle between a ++GNU Regular Expression and plain string match, both kinds are ++case insensitive. F6 will toggle between showing processes whose names do ++match and don't match. + .TP + .B F5, t + Tree view: organize processes by parenthood, and layout the relations diff --git a/sysutils/htop/files/patch-htop.c b/sysutils/htop/files/patch-htop.c new file mode 100644 index 000000000000..0b00f93ec878 --- /dev/null +++ b/sysutils/htop/files/patch-htop.c @@ -0,0 +1,11 @@ +--- htop.c.orig 2020-12-22 06:39:42 UTC ++++ htop.c +@@ -254,7 +254,7 @@ static void setCommFilter(State* state, char** commFil + buffer[maxlen] = 0; + inc->modes[INC_FILTER].index = strlen(buffer); + inc->filtering = true; +- pl->incFilter = IncSet_filter(inc); ++ pl->incSet = ((MainPanel*)state->panel)->inc; + + free(*commFilter); + *commFilter = NULL;