]> granicus.if.org Git - procps-ng/commitdiff
top: enable other filtering via inclusion or exclusion
authorJim Warner <james.warner@comcast.net>
Sun, 3 Mar 2013 06:00:00 +0000 (00:00 -0600)
committerJaromir Capik <jcapik@redhat.com>
Mon, 4 Mar 2013 17:46:34 +0000 (18:46 +0100)
This change represents the extension of user filtering
based on inclusion or exclusion. However where 'U'/'u'
filtering provides an either/or choice, this extension
offers multiple choices applicable to multiple fields.

The 'inclusion' and 'exclusion' criteria can be freely
combined making a powerful tool to fine tune a display
and avoid clutter associated with uninteresting tasks.

I'm convinced it offers unimagined future flexibility!

(everything is perfectly justified plus right margins)
(are completely filled, but of course it must be luck)

Reference(s):
http://www.freelists.org/post/procps/top-over-the-top,22
http://www.freelists.org/post/procps/top-over-the-top,8

Signed-off-by: Jim Warner <james.warner@comcast.net>
top/top.c
top/top.h
top/top_nls.c
top/top_nls.h

index c8ce514739c87db02255b07c308d5c62dc4c1575..3c952834e7d8ce713f193873d970a4600f7a8218 100644 (file)
--- a/top/top.c
+++ b/top/top.c
@@ -1298,6 +1298,59 @@ static inline const char *hex_make (KLONG num, int noz) {
 } // end: hex_make
 
 
+        /*
+         * The sructure hung from a WIN_t when 'other' filtering is active. */
+struct osel_s {
+   int   flg;                                  // include == 1, exclude == 0
+   FLG_t enu;                                  // field (procflag) to filter
+   char *val;                                  // value included or excluded
+   char *(*cmp)(const char *, const char *);   // string comparison function
+   char *raw;                                  // raw user input (dup check)
+   struct osel_s *nxt;                         // the next criteria or NULL.
+};
+
+
+        /*
+         * A function to turn off any 'other' filtering in the given window */
+static void osel_clear (WIN_t *q) {
+   struct osel_s *osel = q->osel_1st;
+
+   while (osel) {
+      struct osel_s *nxt = osel->nxt;
+      free(osel->val);
+      free(osel->raw);
+      free(osel);
+      osel = nxt;
+   }
+   q->osel_tot = 0;
+   q->osel_1st = NULL;
+   free (q->osel_prt);
+   q->osel_prt = NULL;
+#ifndef USE_X_COLHDR
+   OFFw(Curwin, NOHISEL_xxx);
+#endif
+} // end: osel_clear
+
+
+        /*
+         * Determine if there's a matching value among the 'other' criteria
+         * in a given window -- it's called from only one place, and likely
+         * inlined even without the directive */
+static inline int osel_matched (const WIN_t *q, FLG_t enu, const char *str) {
+   struct osel_s *osel = q->osel_1st;
+
+   while (osel) {
+      if (osel->enu == enu) {
+         char *p = osel->cmp(str, osel->val);
+         if (p && !osel->flg) return 0;
+         if (!p && osel->flg) return 0;
+      }
+      osel = osel->nxt;
+   }
+   return 1;
+} // end: osel_matched
+
+
         /*
          * Validate the passed string as a user name or number,
          * and/or update the window's 'u/U' selection stuff. */
@@ -3299,7 +3352,7 @@ static void configs_read (void) {
                break;
          }
 #ifndef USE_X_COLHDR
-         OFFw(w, NOHIFND_xxx);
+         OFFw(w, NOHIFND_xxx | NOHISEL_xxx);
 #endif
       } // end: for (GROUPSMAX)
 
@@ -3475,7 +3528,7 @@ static void parse_args (char **args) {
                for (i = 0; i < P_MAXPFLGS; i++)
                   if (!STRCMP(cp, N_col(i))) break;
                if (i == P_MAXPFLGS)
-                  error_exit(fmtmk(N_fmt(XTRA_sortopt_fmt), cp));
+                  error_exit(fmtmk(N_fmt(XTRA_badflds_fmt), cp));
                OFFw(Curwin, Show_FOREST);
                Curwin->rc.sortindx = i;
                cp += strlen(cp);
@@ -3636,6 +3689,7 @@ static void win_reset (WIN_t *q) {
          q->rc.maxtasks = q->usrseltyp = q->begpflg = q->begtask = 0;
 #endif
          Monpidsidx = 0;
+         osel_clear(q);
 } // end: win_reset
 
 
@@ -3935,7 +3989,8 @@ static void find_string (int ch) {
    if (Curwin->findstr[0]) {
       SETw(Curwin, INFINDS_xxx);
       for (i = Curwin->begtask; i < Frame_maxtask; i++) {
-         if (-1 < find_ofs(Curwin, task_show(Curwin, Curwin->ppt[i]))) {
+         const char *row = task_show(Curwin, Curwin->ppt[i]);
+         if (*row && -1 < find_ofs(Curwin, row)) {
             found = 1;
             if (i == Curwin->begtask) continue;
             Curwin->begtask = i;
@@ -3995,6 +4050,66 @@ signify_that:
 } // end: help_view
 
 
+static void other_selection (int ch) {
+   char *(*cmp)(const char *, const char *);
+   char raw[MEDBUFSIZ], *glob, *pval;
+   struct osel_s *osel;
+   const char *typ;
+   int flg, enu;
+
+   if (ch == 'o') {
+      typ   = N_txt(OSEL_casenot_txt);
+      cmp   = strcasestr;
+   } else {
+      typ   = N_txt(OSEL_caseyes_txt);
+      cmp   = strstr;
+   }
+   glob = ioline(fmtmk(N_fmt(OSEL_prompts_fmt), Curwin->osel_tot + 1, typ));
+   if (!snprintf(raw, sizeof(raw), "%s", glob)) return;
+   for (osel = Curwin->osel_1st; osel; ) {
+      if (!strcmp(osel->raw, glob)) {          // #1: is criteria duplicate?
+         show_msg(N_txt(OSEL_errdups_txt));
+         return;
+      }
+      osel = osel->nxt;
+   }
+   if (*glob != '!') flg = 1;                  // #2: is it include/exclude?
+   else { ++glob; flg = 0; }
+   if (!(pval = strchr(glob, ':'))) {          // #3: do we see a delimiter?
+      show_msg(fmtmk(N_fmt(OSEL_errdelm_fmt)
+         , flg ? N_txt(WORD_include_txt) : N_txt(WORD_exclude_txt)));
+      return;
+   }
+   *(pval++) = '\0';
+   for (enu = 0; enu < P_MAXPFLGS; enu++)      // #4: is this a valid field?
+      if (!STRCMP(N_col(enu), glob)) break;
+   if (enu == P_MAXPFLGS) {
+      show_msg(fmtmk(N_fmt(XTRA_badflds_fmt), glob));
+      return;
+   }
+   if (!(*pval)) {                             // #5: did we get some value?
+      show_msg(fmtmk(N_fmt(OSEL_errvalu_fmt)
+         , flg ? N_txt(WORD_include_txt) : N_txt(WORD_exclude_txt)));
+      return;
+   }
+   osel = alloc_c(sizeof(struct osel_s));
+   osel->flg = flg;
+   osel->enu = enu;
+   osel->val = alloc_s(pval);
+   osel->cmp = cmp;
+   osel->raw = alloc_s(raw);
+   osel->nxt = Curwin->osel_1st;
+   Curwin->osel_1st = osel;
+   Curwin->osel_tot += 1;
+   if (!Curwin->osel_prt) Curwin->osel_prt = alloc_c(strlen(raw) + 3);
+   else Curwin->osel_prt = alloc_r(Curwin->osel_prt, strlen(Curwin->osel_prt) + strlen(raw) + 6);
+   strcat(Curwin->osel_prt, fmtmk("%s'%s'", (Curwin->osel_tot > 1) ? " + " : "", raw));
+#ifndef USE_X_COLHDR
+   SETw(Curwin, NOHISEL_xxx);
+#endif
+} // end: other_selection
+
+
 static void write_rcfile (void) {
    FILE *fp;
    int i;
@@ -4287,6 +4402,10 @@ static void keys_task (int ch) {
                ? N_txt(ON_word_only_txt) : N_txt(OFF_one_word_txt)));
          }
          break;
+      case 'O':
+      case 'o':
+         if (VIZCHKw(w)) other_selection(ch);
+         break;
       case 'U':
       case 'u':
          if (VIZCHKw(w)) {
@@ -4333,6 +4452,11 @@ static void keys_task (int ch) {
             capsmk(w);
          }
          break;
+      case kbd_CtrlO:
+         if (VIZCHKw(w))
+            ioline(fmtmk(N_fmt(OSEL_statlin_fmt)
+               , w->osel_prt ? w->osel_prt : N_txt(WORD_noneone_txt)));
+         break;
       default:                    // keep gcc happy
          break;
    }
@@ -4357,11 +4481,7 @@ static void keys_window (int ch) {
          break;
       case '&':
       case 'L':
-         if (VIZCHKw(w)) {             // ( next 2 are strictly for the UI )
-            SETw(w, Show_IDLEPS);      // make sure we're showing idle tasks
-            w->usrseltyp = 0;          // make sure we're not user filtering
-            find_string(ch);           // ( we'll search entire ppt anyway )
-         }
+         if (VIZCHKw(w)) find_string(ch);
          break;
       case 'A':
          Rc.mode_altscr = !Rc.mode_altscr;
@@ -4615,8 +4735,9 @@ static void do_key (int ch) {
       { keys_summary,
          { '1', 'C', 'l', 'm', 't', '\0' } },
       { keys_task,
-         { '#', '<', '>', 'b', 'c', 'i', 'J', 'j', 'n', 'R', 'S'
-         , 'U', 'u', 'V', 'x', 'y', 'z', '\0' } },
+         { '#', '<', '>', 'b', 'c', 'i', 'J', 'j', 'n', 'O', 'o'
+         , 'R', 'S', 'U', 'u', 'V', 'x', 'y', 'z'
+         , kbd_CtrlO, '\0' } },
       { keys_window,
          { '+', '-', '=', '_', '&', 'A', 'a', 'G', 'L', 'w'
          , kbd_UP, kbd_DOWN, kbd_LEFT, kbd_RIGHT, kbd_PGUP, kbd_PGDN
@@ -4852,7 +4973,7 @@ static const char *task_show (const WIN_t *q, const proc_t *p) {
          case X_XON:
          case X_XOF:
             cp = NULL;
-            if (!CHKw(q, INFINDS_xxx | NOHIFND_xxx)) {
+            if (!CHKw(q, INFINDS_xxx | NOHIFND_xxx | NOHISEL_xxx)) {
                /* treat running tasks specially - entire row may get highlighted
                   so we needn't turn it on and we MUST NOT turn it off */
                if (!('R' == p->state && CHKw(q, Show_HIROWS)))
@@ -5021,9 +5142,10 @@ static const char *task_show (const WIN_t *q, const proc_t *p) {
 
       } // end: switch 'procflag'
 
-      if (cp)
+      if (cp) {
+         if (q->osel_tot && !osel_matched(q, i, cp)) return "";
          rp = scat(rp, cp);
-
+      }
       #undef S
       #undef W
       #undef Js
@@ -5090,16 +5212,15 @@ static int window_show (WIN_t *q, int wmax) {
       checking some stuff with each iteration and check it just once... */
    if (CHKw(q, Show_IDLEPS) && !q->usrseltyp)
       while (i < Frame_maxtask && lwin < wmax) {
-         task_show(q, q->ppt[i++]);
-         ++lwin;
+         if (*task_show(q, q->ppt[i++]))
+            ++lwin;
       }
    else
       while (i < Frame_maxtask && lwin < wmax) {
          if ((CHKw(q, Show_IDLEPS) || isBUSY(q->ppt[i]))
-         && user_matched(q, q->ppt[i])) {
-            task_show(q, q->ppt[i]);
+         && user_matched(q, q->ppt[i])
+         && *task_show(q, q->ppt[i++]))
             ++lwin;
-         }
          ++i;
       }
 
index da237ef043b173e51461acd4dc9f32ed234fd6c7..4de786a0f01a41a2d3c6fabd16c0c569994c8f6f 100644 (file)
--- a/top/top.h
+++ b/top/top.h
 #define linux_version_code LINUX_VERSION(2,5,43)
 #endif
 
-#ifdef STRINGCASENO
    // pretend as if #define _GNU_SOURCE
 char *strcasestr(const char *haystack, const char *needle);
+
+#ifdef STRINGCASENO
 #define STRSTR  strcasestr
 #define STRCMP  strcasecmp
 #else
@@ -153,6 +154,7 @@ char *strcasestr(const char *haystack, const char *needle);
 #define kbd_BKSP   137
 #define kbd_INS    138
 #define kbd_DEL    139
+#define kbd_CtrlO  '\017'
 
         /* Special value in Pseudo_row to force an additional procs refresh
            -- used at startup and for task/thread mode transitions */
@@ -307,7 +309,8 @@ typedef struct CPU_t {
 #define INFINDS_xxx  0x010000     // build rows for find_string, not display
 #define EQUWINS_xxx  0x000001     // rebalance all wins & tasks (off i,n,u/U)
 #ifndef USE_X_COLHDR
-#define NOHIFND_xxx  0x100000     // must restrict Show_HICOLS temporarily
+#define NOHISEL_xxx  0x200000     // restrict Show_HICOLS for osel temporarily
+#define NOHIFND_xxx  0x100000     // restrict Show_HICOLS for find temporarily
 #endif
 
         // Default flags if there's no rcfile to provide user customizations
@@ -389,6 +392,9 @@ typedef struct WIN_t {
           columnhdr [SCREENMAX],       // column headings for procflgs
 #endif
          *captab [CAPTABMAX];          // captab needed by show_special()
+   struct osel_s *osel_1st;            // other selection criteria anchor
+   int    osel_tot;                    // total of other selection criteria
+   char  *osel_prt;                    // other stuff printable as status line
    char  *findstr;                     // window's current/active search string
    int    findlen;                     // above's strlen, without call overhead
    proc_t **ppt;                       // this window's proc_t ptr array
@@ -655,6 +661,8 @@ typedef struct WIN_t {
 //atic float         get_float (const char *prompt);
 //atic int           get_int (const char *prompt);
 //atic inline const char *hex_make (KLONG num, int noz);
+//atic void          osel_clear (WIN_t *q);
+//atic inline int    osel_matched (const WIN_t *q, FLG_t enu, const char *str);
 //atic const char   *user_certify (WIN_t *q, const char *str, char typ);
 //atic inline int    user_matched (const WIN_t *q, const proc_t *p);
 /*------  Basic Formatting support  --------------------------------------*/
@@ -719,6 +727,7 @@ typedef struct WIN_t {
 //atic inline int    find_ofs (const WIN_t *q, const char *buf);
 //atic void          find_string (int ch);
 //atic void          help_view (void);
+//atic void          other_selection (int ch);
 //atic void          write_rcfile (void);
 /*------  Interactive Input Secondary support (do_key helpers)  ----------*/
 //atic void          keys_global (int ch);
index 78d19e2d36e3242bc398a2714a9342bc5c613666..d647e69120d8bfa471ae41a44b69fab0b7b61def 100644 (file)
@@ -379,7 +379,7 @@ static void build_norm_nlstab (void) {
    Norm_nlstab[FIND_no_find_fmt] = _("%s\"%s\" not found");
    Norm_nlstab[XTRA_fixwide_fmt] = _("width incr is %d, change to (0 default, -1 auto)");
    Norm_nlstab[XTRA_warncfg_txt] = _("Overwrite existing obsolete/corrupted rcfile?");
-   Norm_nlstab[XTRA_sortopt_fmt] = _("unrecognized field name '%s'");
+   Norm_nlstab[XTRA_badflds_fmt] = _("unrecognized field name '%s'");
    Norm_nlstab[XTRA_winsize_txt] = _("even using field names only, window is now too small");
 #ifndef INSP_OFFDEMO
    Norm_nlstab[YINSP_demo01_txt] = _("Open Files");
@@ -429,6 +429,18 @@ static void build_norm_nlstab (void) {
    Norm_nlstab[YINSP_rcfile_fmt] = _("could not parse rcfile inspect entry %d");
    Norm_nlstab[YINSP_status_fmt] = _("%s: %*d-%-*d lines, %*d-%*d columns, %lu bytes read");
    Norm_nlstab[YINSP_workin_txt] = _("patience please, working...");
+/* Translation Hint: Below are 2 abbreviations which can be as long as needed:
+   .                 FLD = FIELD, VAL = VALUE */
+   Norm_nlstab[OSEL_prompts_fmt] = _("add filter #%d (%s) as: [!]FLD:VAL");
+   Norm_nlstab[OSEL_casenot_txt] = _("ignoring case");
+   Norm_nlstab[OSEL_caseyes_txt] = _("case sensitive");
+   Norm_nlstab[OSEL_errdups_txt] = _("duplicate filter was ignored");
+   Norm_nlstab[OSEL_errdelm_fmt] = _("'%s' filter delimiter is missing");
+   Norm_nlstab[OSEL_errvalu_fmt] = _("'%s' filter value is missing");
+   Norm_nlstab[WORD_include_txt] = _("include");
+   Norm_nlstab[WORD_exclude_txt] = _("exclude");
+   Norm_nlstab[OSEL_statlin_fmt] = _("<Enter> to resume, filters = %s");
+   Norm_nlstab[WORD_noneone_txt] = _("none");
 }
 
 
@@ -476,8 +488,8 @@ static void build_uniq_nlstab (void) {
       "  c,i,S,j . Toggle: '~1c~2' Cmd name/line; '~1i~2' Idle; '~1S~2' Time; '~1j~2' Str justify\n"
       "  x~5,~1y~5     . Toggle highlights: '~1x~2' sort field; '~1y~2' running tasks\n"
       "  z~5,~1b~5     . Toggle: '~1z~2' color/mono; '~1b~2' bold/reverse (only if 'x' or 'y')\n"
-      "  u,U     . Filter by: '~1u~2' effective user; '~1U~2' effective/file/real/saved user\n"
-      "  n or #  . Set maximum tasks displayed\n"
+      "  u,U,o,O . Filter by: '~1u~2'/'~1U~2' effective/any user; '~1o~2'/'~1O~2' other criteria\n"
+      "  n,#,^O  . Set: '~1n~2'/'~1#~2' max tasks displayed; Show: ~1Ctrl~2+'~1O~2' other filter(s)\n"
       "  C,...   . Toggle scroll coordinates msg for: ~1up~2,~1down~2,~1left~2,~1right~2,~1home~2,~1end~2\n"
       "\n"
       "%s"
index 55daafcb37d75de27e14edeb024e1ff9f109cd33..ad609a786ba90a45af04de1cafdf3a1e2114e0e4 100644 (file)
@@ -76,11 +76,14 @@ enum norm_nls {
    GET_sigs_num_fmt, GET_user_ids_txt, HELP_cmdline_fmt, HILIGHT_cant_txt,
    IRIX_curmode_fmt, LIMIT_exceed_fmt, MISSING_args_fmt, NAME_windows_fmt,
    NOT_onsecure_txt, NOT_smp_cpus_txt, OFF_one_word_txt, ON_word_only_txt,
-   RC_bad_entry_fmt, RC_bad_files_fmt, SCROLL_coord_fmt, SELECT_clash_txt,
-   THREADS_show_fmt, TIME_accumed_fmt, UNKNOWN_cmds_txt, UNKNOWN_opts_fmt,
-   USAGE_abbrev_txt, WORD_allcpus_txt, WORD_another_txt, WORD_eachcpu_fmt,
-   WORD_process_txt, WORD_threads_txt, WRITE_rcfile_fmt, WRONG_switch_fmt,
-   XTRA_fixwide_fmt, XTRA_sortopt_fmt, XTRA_warncfg_txt, XTRA_winsize_txt,
+   OSEL_casenot_txt, OSEL_caseyes_txt, OSEL_errdelm_fmt, OSEL_errdups_txt,
+   OSEL_errvalu_fmt, OSEL_prompts_fmt, OSEL_statlin_fmt, RC_bad_entry_fmt,
+   RC_bad_files_fmt, SCROLL_coord_fmt, SELECT_clash_txt, THREADS_show_fmt,
+   TIME_accumed_fmt, UNKNOWN_cmds_txt, UNKNOWN_opts_fmt, USAGE_abbrev_txt,
+   WORD_allcpus_txt, WORD_another_txt, WORD_eachcpu_fmt, WORD_exclude_txt,
+   WORD_include_txt, WORD_noneone_txt, WORD_process_txt, WORD_threads_txt,
+   WRITE_rcfile_fmt, WRONG_switch_fmt, XTRA_badflds_fmt, XTRA_fixwide_fmt,
+   XTRA_warncfg_txt, XTRA_winsize_txt,
 #ifndef INSP_OFFDEMO
    YINSP_demo01_txt, YINSP_demo02_txt, YINSP_demo03_txt, YINSP_deqfmt_txt,
    YINSP_deqtyp_txt, YINSP_dstory_txt,