diff --git a/DisplayOptionsPanel.c b/DisplayOptionsPanel.c index 7e0b7e4aa..3e2cf3a24 100644 --- a/DisplayOptionsPanel.c +++ b/DisplayOptionsPanel.c @@ -45,6 +45,7 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) { } NumberItem* numItem = (OptionItem_kind(selected) == OPTION_ITEM_NUMBER) ? (NumberItem*)selected : NULL; + StringItem* strItem = (OptionItem_kind(selected) == OPTION_ITEM_STRING) ? (StringItem*)selected : NULL; /* Helper: position the hardware cursor right after the edit buffer. * +1 on Y for the panel header row; +1 on X for the leading '[' bracket. */ @@ -54,8 +55,19 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) { super->cursorOn = true; \ } while (0) + #define SET_STR_CURSOR() do { \ + super->cursorY = super->y + 1 + (super->selected - super->scrollV); \ + super->cursorX = super->x + 1 + (int)LineEditor_getCursor(&strItem->editor); \ + super->cursorOn = true; \ + } while (0) + switch (ch) { case 27: /* Escape: cancel editing */ + if (numItem && numItem->editing) { + NumberItem_cancelEditing(numItem); + super->cursorOn = false; + return HANDLED; + } if (numItem && numItem->editing) { NumberItem_cancelEditing(numItem); super->cursorOn = false; @@ -64,6 +76,11 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) { break; case KEY_BACKSPACE: case KEY_DEL_MAC: + if (strItem && strItem->editing) { + LineEditor_handleKey(&strItem->editor, KEY_BACKSPACE); + SET_STR_CURSOR(); + return HANDLED; + } if (numItem) { if (!numItem->editing) { NumberItem_startEditingFromValue(numItem); @@ -76,6 +93,19 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) { case '\n': case '\r': case KEY_ENTER: + if (strItem && strItem->editing) { + StringItem_applyEditing(strItem); + super->cursorOn = false; + settingsChanged = true; + result = HANDLED; + break; + } + if (strItem && !strItem->editing) { + StringItem_startEditing(strItem); + SET_STR_CURSOR(); + result = HANDLED; + break; + } if (numItem && numItem->editing) { if (NumberItem_applyEditing(numItem)) { settingsChanged = true; @@ -219,6 +249,12 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) { SET_EDIT_CURSOR(); return HANDLED; } + } else if (strItem) { + if (strItem->editing) { + LineEditor_handleKey(&strItem->editor, ch); + SET_STR_CURSOR(); + return HANDLED; + } } break; } @@ -282,6 +318,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* Panel_add(super, (Object*) CheckItem_newByRef("Highlight program \"basename\"", &(settings->highlightBaseName))); Panel_add(super, (Object*) CheckItem_newByRef("Highlight out-dated/removed programs (red) / libraries (yellow)", &(settings->highlightDeletedExe))); Panel_add(super, (Object*) CheckItem_newByRef("Shadow distribution path prefixes", &(settings->shadowDistPathPrefix))); + Panel_add(super, (Object*) StringItem_newByRef("- Custom path prefixes (colon-separated)", &(settings->distPathPrefixes), NULL)); Panel_add(super, (Object*) CheckItem_newByRef("Merge exe, comm and cmdline in Command", &(settings->showMergedCommand))); Panel_add(super, (Object*) CheckItem_newByRef("- Try to find comm in cmdline (when Command is merged)", &(settings->findCommInCmdline))); Panel_add(super, (Object*) CheckItem_newByRef("- Try to strip exe from cmdline (when Command is merged)", &(settings->stripExeFromCmdline))); diff --git a/OptionItem.c b/OptionItem.c index f0595092c..188c17874 100644 --- a/OptionItem.c +++ b/OptionItem.c @@ -76,6 +76,27 @@ static void NumberItem_display(const Object* cast, RichString* out) { RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->super.text); } +static void StringItem_display(const Object* cast, RichString* out) { + const StringItem* this = (const StringItem*)cast; + int labelAttr = CRT_colors[CHECK_TEXT]; + int boxAttr = CRT_colors[CHECK_BOX]; + int valAttr = this->valid ? CRT_colors[CHECK_MARK] : CRT_colors[FAILED_READ]; + + RichString_writeAscii(out, boxAttr, "["); + if (this->editing) { + RichString_appendAscii(out, valAttr, this->editor.buffer); + } else { + const char* val = (this->ref && *this->ref) ? *this->ref : NULL; + if (val) { + RichString_appendAscii(out, valAttr, val); + } else { + RichString_appendAscii(out, CRT_colors[PROCESS_SHADOW], "(empty)"); + } + } + RichString_appendAscii(out, boxAttr, "] "); + RichString_appendWide(out, labelAttr, this->super.text); +} + const OptionItemClass OptionItem_class = { .super = { .extends = Class(Object), @@ -112,6 +133,15 @@ const OptionItemClass NumberItem_class = { .kind = OPTION_ITEM_NUMBER }; +const OptionItemClass StringItem_class = { + .super = { + .extends = Class(OptionItem), + .delete = OptionItem_delete, + .display = StringItem_display + }, + .kind = OPTION_ITEM_STRING +}; + TextItem* TextItem_new(const char* text) { TextItem* this = AllocThis(TextItem); this->super.text = xStrdup(text); @@ -323,3 +353,40 @@ void NumberItem_deleteChar(NumberItem* this) { this->editBuffer[this->editLen] = '\0'; } } +StringItem* StringItem_newByRef(const char* text, char** ref, bool (*validate)(const char* text)) { + StringItem* this = AllocThis(StringItem); + this->super.text = xStrdup(text); + this->ref = ref; + this->editing = false; + this->valid = true; + this->validate = validate; + LineEditor_init(&this->editor); + if (ref && *ref) + LineEditor_setText(&this->editor, *ref); + return this; +} + +void StringItem_startEditing(StringItem* this) { + this->editing = true; + LineEditor_setText(&this->editor, (this->ref && *this->ref) ? *this->ref : ""); +} + +void StringItem_cancelEditing(StringItem* this) { + this->editing = false; + LineEditor_setText(&this->editor, (this->ref && *this->ref) ? *this->ref : ""); +} + +bool StringItem_applyEditing(StringItem* this) { + this->editing = false; + const char* text = this->editor.buffer; + if (this->validate && !this->validate(text)) { + this->valid = false; + return false; + } + this->valid = true; + if (this->ref) { + free(*this->ref); + *this->ref = (*text != '\0') ? xStrdup(text) : NULL; + } + return true; +} diff --git a/OptionItem.h b/OptionItem.h index 02937327d..24d336696 100644 --- a/OptionItem.h +++ b/OptionItem.h @@ -9,6 +9,7 @@ in the source distribution for its full text. #include +#include "LineEditor.h" #include "Object.h" #define NUMBERITEM_EDIT_MAX 10 @@ -17,6 +18,7 @@ enum OptionItemType { OPTION_ITEM_TEXT, OPTION_ITEM_CHECK, OPTION_ITEM_NUMBER, + OPTION_ITEM_STRING, }; typedef struct OptionItemClass_ { @@ -62,10 +64,20 @@ typedef struct NumberItem_ { int savedValue; } NumberItem; +typedef struct StringItem_ { + OptionItem super; + char** ref; + bool editing; + bool valid; + LineEditor editor; + bool (*validate)(const char* text); +} StringItem; + extern const OptionItemClass OptionItem_class; extern const OptionItemClass TextItem_class; extern const OptionItemClass CheckItem_class; extern const OptionItemClass NumberItem_class; +extern const OptionItemClass StringItem_class; TextItem* TextItem_new(const char* text); @@ -88,4 +100,9 @@ bool NumberItem_applyEditing(NumberItem* this); bool NumberItem_addChar(NumberItem* this, char c); void NumberItem_deleteChar(NumberItem* this); +StringItem* StringItem_newByRef(const char* text, char** ref, bool (*validate)(const char* text)); +void StringItem_startEditing(StringItem* this); +void StringItem_cancelEditing(StringItem* this); +bool StringItem_applyEditing(StringItem* this); + #endif diff --git a/Process.c b/Process.c index f68c305e0..8a1e14482 100644 --- a/Process.c +++ b/Process.c @@ -174,6 +174,28 @@ static inline char* stpcpyWithNewlineConversion(char* dstStr, const char* srcStr return dstStr; } +static size_t findDistPrefixLength(const char* str, const Settings* settings) { + static const char* const builtinPrefixes = + "/bin/:/sbin/:/lib/:/lib32/:/lib64/:/libx32/:" + "/usr/bin/:/usr/sbin/:/usr/lib/:/usr/lib32/:/usr/lib64/:/usr/libx32/:" + "/usr/libexec/:/usr/local/bin/:/usr/local/lib/:/usr/local/sbin/:" + "/nix/store/:/run/current-system/"; + + const char* list = (settings->distPathPrefixes && settings->distPathPrefixes[0] != '\0') + ? settings->distPathPrefixes + : builtinPrefixes; + + const char* token = list; + while (*token) { + const char* end = String_strchrnul(token, ':'); + size_t len = end - token; + if (strncmp(str, token, len) == 0) + return len; + token = *end ? end + 1 : end; + } + return 0; +} + /* * This function makes the merged Command string. It also stores the offsets of the * basename, comm w.r.t the merged Command string - these offsets will be used by @@ -256,51 +278,10 @@ void Process_makeCommandStr(Process* this, const Settings* settings) { #define CHECK_AND_MARK_DIST_PATH_PREFIXES(str_) \ do { \ - if ((str_)[0] != '/') { \ - break; \ - } \ - switch ((str_)[1]) { \ - case 'b': \ - CHECK_AND_MARK(str_, "/bin/"); \ - break; \ - case 'l': \ - CHECK_AND_MARK(str_, "/lib/"); \ - CHECK_AND_MARK(str_, "/lib32/"); \ - CHECK_AND_MARK(str_, "/lib64/"); \ - CHECK_AND_MARK(str_, "/libx32/"); \ - break; \ - case 's': \ - CHECK_AND_MARK(str_, "/sbin/"); \ - break; \ - case 'u': \ - if (String_startsWith(str_, "/usr/")) { \ - switch ((str_)[5]) { \ - case 'b': \ - CHECK_AND_MARK(str_, "/usr/bin/"); \ - break; \ - case 'l': \ - CHECK_AND_MARK(str_, "/usr/libexec/"); \ - CHECK_AND_MARK(str_, "/usr/lib/"); \ - CHECK_AND_MARK(str_, "/usr/lib32/"); \ - CHECK_AND_MARK(str_, "/usr/lib64/"); \ - CHECK_AND_MARK(str_, "/usr/libx32/"); \ - \ - CHECK_AND_MARK(str_, "/usr/local/bin/"); \ - CHECK_AND_MARK(str_, "/usr/local/lib/"); \ - CHECK_AND_MARK(str_, "/usr/local/sbin/"); \ - break; \ - case 's': \ - CHECK_AND_MARK(str_, "/usr/sbin/"); \ - break; \ - } \ - } \ - break; \ - case 'n': \ - CHECK_AND_MARK(str_, "/nix/store/"); \ - break; \ - case 'r': \ - CHECK_AND_MARK(str_, "/run/current-system/"); \ - break; \ + size_t plen = findDistPrefixLength(str_, settings); \ + if (plen > 0) { \ + WRITE_HIGHLIGHT(0, plen, CRT_colors[PROCESS_SHADOW], \ + CMDLINE_HIGHLIGHT_FLAG_PREFIXDIR); \ } \ } while (0) diff --git a/Settings.c b/Settings.c index e1cd0804d..c656a2ee2 100644 --- a/Settings.c +++ b/Settings.c @@ -51,6 +51,7 @@ static void Settings_deleteScreens(Settings* this) { void Settings_delete(Settings* this) { free(this->filename); free(this->initialFilename); + free(this->distPathPrefixes); Settings_deleteColumns(this); Settings_deleteScreens(this); free(this); @@ -440,6 +441,8 @@ static bool Settings_read(Settings* this, const char* fileName, const Machine* h this->highlightDeletedExe = atoi(option[1]); } else if (String_eq(option[0], "shadow_distribution_path_prefix")) { this->shadowDistPathPrefix = atoi(option[1]); + } else if (String_eq(option[0], "dist_path_prefixes")) { + free_and_xStrdup(&this->distPathPrefixes, option[1]); } else if (String_eq(option[0], "highlight_megabytes")) { this->highlightMegabytes = atoi(option[1]); } else if (String_eq(option[0], "highlight_threads")) { @@ -696,6 +699,8 @@ int Settings_write(const Settings* this, bool onCrash) { printSettingInteger("highlight_base_name", this->highlightBaseName); printSettingInteger("highlight_deleted_exe", this->highlightDeletedExe); printSettingInteger("shadow_distribution_path_prefix", this->shadowDistPathPrefix); + if (this->distPathPrefixes && this->distPathPrefixes[0] != '\0') + printSettingString("dist_path_prefixes", this->distPathPrefixes); printSettingInteger("highlight_megabytes", this->highlightMegabytes); printSettingInteger("highlight_threads", this->highlightThreads); printSettingInteger("highlight_changes", this->highlightChanges); diff --git a/Settings.h b/Settings.h index b881b0e4a..21d30ad95 100644 --- a/Settings.h +++ b/Settings.h @@ -57,6 +57,7 @@ typedef struct ScreenSettings_ { typedef struct Settings_ { char* filename; char* initialFilename; + char* distPathPrefixes; bool writeConfig; /* whether to write the current settings on exit */ int config_version; HeaderLayout hLayout;