]> granicus.if.org Git - procps-ng/commitdiff
top: highlight all inspect search string(s) when found
authorJim Warner <james.warner@comcast.net>
Mon, 3 Dec 2012 06:00:00 +0000 (00:00 -0600)
committerCraig Small <csmall@enc.com.au>
Sun, 9 Dec 2012 22:29:27 +0000 (09:29 +1100)
We have modeled the Inspect search provisions on those
provided by the 'less' pager. With this commit we take
the next step and provide for highlighting any strings
matched (and in view). Of course, top will continue to
adjust the beginning column so as to bring out-of-view
matches into view, while highlighting visible matches.

However, top won't emulate every 'less' behavior since
the following are seen as flaws in the user interface.

* when viewing true binary data, less makes no attempt
. to smooth the right margin by truncating unprintable
. symbols, thus creatng ragged unappealing right edges

* when viewing true binary data, less will always fail
. search requests regardless of surrounding characters

* less refuses to bring out-of-view found matches into
. view by adjusting the left-most column, if necessary

(now that we know a '.' + 2 spaces is squeezed to one)
(everything's perfectly justified, but it's just luck)

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

index e7483110524f255fc00d63f4234607f730bf1049..7ce5109aee36f5a78aaa84e194148a747fe2a87b 100644 (file)
--- a/top/top.c
+++ b/top/top.c
@@ -2356,6 +2356,8 @@ static  size_t    Insp_bufsz;     // allocated size of Insp_buf
 static  size_t    Insp_bufrd;     // bytes actually in Insp_buf
 static  char     *Insp_selname;   // the selected label, if anybody cares
 static  char     *Insp_selfmts;   // the selected path/command, ditto
+static  char     *Insp_findstr;   // the current active target for find/next
+static  int       Insp_findlen;   // the above's strlen()
 
 
         // Our 'make status line' macro
@@ -2462,12 +2464,42 @@ static void insp_do_pipe (char *fmts, int pid) {
 } // end: insp_do_pipe
 
 
+        /*
+         * This guy is a *Helper* function serving the following two masters:
+         *   insp_find_str() - find the next Insp_findstr match
+         *   insp_make_row() - highlight any Insp_findstr matches in-view
+         * If Insp_findstr is found in the designated row (after 'col'), he
+         * returns the offest from the row start, otherwise he returns a huge
+         * integer so traditional fencepost usage can be employed. */
+static inline int insp_find_ofs (int col, int row) {
+ #define begFS (int)(fnd - Insp_p[row])
+   char *p, *fnd = NULL;
+
+   if (Insp_findstr) {
+      // skip this row, if there's no chance of a match
+      if (memchr(Insp_p[row], Insp_findstr[0], INSP_RLEN(row))) {
+         for ( ; col < INSP_RLEN(row); col++) {
+            if (!*(p = Insp_p[row] + col))       // skip any empty strings
+               continue;
+            if ((fnd = STRSTR(p, Insp_findstr))) // with binary data, each
+               break;                            // row may have '\0'.  so
+            col += strlen(p);                    // our scans must be done
+         }                                       // in chunks, and we must
+         if (fnd && fnd < Insp_p[row + 1])       // guard against overrun!
+            return begFS;
+      }
+   }
+   return INT_MAX;
+ #undef begFS
+} // end: insp_find_ofs
+
+
         /*
          * This guy supports the inspect 'L' and '&' search provisions
-         * and returns the row and *optimal* col for viewing any match
+         * and returns the row and *optimal* column for viewing any match
          * ( we'll always opt for left column justification since any )
-         * ( preceeding control chars would consume an unknown amount ) */
-static void insp_find (int ch, int *col, int *row) {
+         * ( preceding ctrl chars appropriate an unpredictable amount ) */
+static void insp_find_str (int ch, int *col, int *row) {
  #define reDUX (found) ? N_txt(WORD_another_txt) : ""
    static char str[SCREENMAX];
    static int found;
@@ -2478,29 +2510,26 @@ static void insp_find (int ch, int *col, int *row) {
    }
    if (ch == 'L' || ch == '/') {
       strcpy(str, linein(N_txt(GET_find_str_txt)));
+      Insp_findstr = str;
+      Insp_findlen = strlen(str);
+      if (!Insp_findlen) Insp_findstr = NULL;
       found = 0;
    }
    if (str[0]) {
-      int i, xx, yy;
+      int xx, yy;
+
       INSP_BUSY;
       for (xx = *col, yy = *row; yy < Insp_nl; ) {
-            // let's skip this entire row, if there's no chance of a match
-         if (memchr(Insp_p[yy], str[0], INSP_RLEN(yy))) {
-            char *p, *fnd = NULL;
-            for (i = xx; i < INSP_RLEN(yy); i++) {
-               if (!*(p = Insp_p[yy] + i))         // skip any empty strings
-                  continue;
-               if ((fnd = STRSTR(p, str)))         // with binary data, each
-                  break;                           // row may have '\0'.  so
-               i += strlen(p);                     // our scans must be done
-            }                                      // in chunks, and we must
-            if (fnd && fnd < Insp_p[yy + 1]) {     // guard against overrun!
-               found = 1;
-               if (xx == *col) { ++xx; continue; } // matched where we were!
-               *row = yy;                          // ( tried to fool top? )
-               *col = (int)(fnd - Insp_p[yy]);
-               return;
+         xx = insp_find_ofs(xx, yy);
+         if (xx < INSP_RLEN(yy)) {
+            found = 1;
+            if (xx == *col) {     // matched where we were!
+               ++xx;              // ( was the user maybe )
+               continue;          // ( trying to fool us? )
             }
+            *col = xx;
+            *row = yy;
+            return;
          }
          xx = 0;
          ++yy;
@@ -2508,34 +2537,84 @@ static void insp_find (int ch, int *col, int *row) {
       show_msg(fmtmk(N_fmt(FIND_no_find_fmt), reDUX, str));
    }
  #undef reDUX
-} // end: insp_find
+} // end: insp_find_str
 
 
         /*
-         * This guy is an insp_view_this() *Helper* function responsible
-         * for positioning us in both the x/y axes within the current glob
-         * and displaying a page worth of damages.  Along the way, he makes
-         * sure that any control characters and/or unprintable characters
-         * use a less-like approach which distinguishes between two forms
-         * of representation:  ^C and <FF>.
-         *
-         * He also creates a customized status line based on the maximum
-         * number of digits for the current selection's position so it will
-         * hopefully serve to inform, not distract by being jumpy. */
-static inline void insp_show_pg (int col, int row, int max) {
+         * This guy is a *Helper* function responsible for positioning a
+         * single row in the current 'X axis', then displaying the results.
+         * Along the way, he makes sure control characters and/or unprintable
+         * characters display in a less-like fashion:
+         *    '^A'    for control chars
+         *    '<BC>'  for other unprintable stuff
+         * Those will be highlighted with the current windows's capclr_msg,
+         * while visible search matches display with capclr_hdr for emphasis.
+         * ( we hide ugly plumbing in macros to concentrate on the algorithm ) */
+static inline void insp_make_row (int col, int row) {
+ #define maxSZ ( Screen_cols - (to + 1) )
  #define capNO { if (hicap) { putp(Caps_off); hicap = 0; } }
+ #define mkFND { PUTT("%s%.*s%s", Curwin->capclr_hdr, maxSZ, Insp_findstr, Caps_off); \
+    fr += Insp_findlen -1; to += Insp_findlen; hicap = 0; }
+#ifndef INSP_JUSTNOT
+ #define mkCTL { int x = maxSZ; const char *p = fmtmk("^%c", uch + '@'); \
+    PUTT("%s%.*s", (!hicap) ? Curwin->capclr_msg : "", x, p); to += 2; hicap = 1; }
+ #define mkUNP { int x = maxSZ; const char *p = fmtmk("<%02X>", uch); \
+    PUTT("%s%.*s", (!hicap) ? Curwin->capclr_msg : "", x, p); to += 4; hicap = 1; }
+#else
  #define mkCTL { if ((to += 2) <= Screen_cols) \
     PUTT("%s^%c", (!hicap) ? Curwin->capclr_msg : "", uch + '@'); hicap = 1; }
  #define mkUNP { if ((to += 4) <= Screen_cols) \
     PUTT("%s<%02X>", (!hicap) ? Curwin->capclr_msg : "", uch); hicap = 1; }
- #define mkSTD { capNO; putchar(uch); to++; }
+#endif
+ #define mkSTD { capNO; if (++to <= Screen_cols) putchar(uch); }
+   char tline[SCREENMAX];
+   int fr, to, ofs;
+   int hicap = 0;
+
+   capNO;
+   if (col < INSP_RLEN(row))
+      memcpy(tline, Insp_p[row] + col, sizeof(tline));
+   else tline[0] = '\n';
+
+   for (fr = 0, to = 0, ofs = 0; to < Screen_cols -1; fr++) {
+      if (!ofs)
+         ofs = insp_find_ofs(col + fr, row);
+      if (col + fr < ofs) {
+         unsigned char uch = tline[fr];
+         if (uch == '\n')   break;     // a no show  (he,he)
+         if (uch > 126)     mkUNP      // show as: '<AB>'
+         else if (uch < 32) mkCTL      // show as:  '^C'
+         else               mkSTD      // a show off (he,he)
+      } else {              mkFND      // a big show (he,he)
+         ofs = 0;
+      }
+   }
+   capNO;
+   putp(Cap_clr_eol);
+
+ #undef maxSZ
+ #undef capNO
+ #undef mkFND
+ #undef mkCTL
+ #undef mkUNP
+ #undef mkSTD
+} // end: insp_make_row
+
+
+        /*
+         * This guy is an insp_view_choice() *Helper* function who displays
+         * a page worth of of the user's damages.  He also creates a status
+         * line based on maximum digits for the current selection's lines and
+         * hozizontal position (so it serves to inform, not distract, by
+         * otherwise being jumpy). */
+static inline void insp_show_pgs (int col, int row, int max) {
    char buf[SMLBUFSIZ];
    int r = snprintf(buf, sizeof(buf), "%d", Insp_nl);
    int c = snprintf(buf, sizeof(buf), "%d", col +Screen_cols);
    int l = row +1, ls = Insp_nl;;
-   int hicap = 0;
 
-   if (!Insp_bufrd) l = ls = 0;   // for a more honest representation
+   if (!Insp_bufrd)
+      l = ls = 0;
    snprintf(buf, sizeof(buf), N_fmt(YINSP_status_fmt)
       , Insp_selname
       , r, l, r, ls
@@ -2544,39 +2623,19 @@ static inline void insp_show_pg (int col, int row, int max) {
    INSP_MKSL(0, buf);
 
    for ( ; max && row < Insp_nl; row++) {
-      char tline[SCREENMAX];
-      int fr, to, len;
-
-      capNO;
       putp("\n");
-      len = INSP_RLEN(row);
-      if (col < len)
-         memcpy(tline, Insp_p[row] + col, sizeof(tline));
-      else tline[0] = '\n';
-
-      for (fr = 0, to = 0; fr < len && to < Screen_cols; fr++) {
-         unsigned char uch = tline[fr];
-         if (uch == '\n')   break;     // a no show  (he,he)
-         if (uch > 126)     mkUNP      // show as '<AB>'
-         else if (uch < 32) mkCTL      // show as '^C'
-         else               mkSTD      // a show off (he,he)
-      }
-      capNO;
-      putp(Cap_clr_eol);
+      insp_make_row(col, row);
       --max;
    }
-   if (max) putp(Cap_nl_clreos);
- #undef capNO
- #undef mkCTL
- #undef mkUNP
- #undef mkSTD
-} // end: insp_show_pg
+   if (max)
+      putp(Cap_nl_clreos);
+} // end: insp_show_pgs
 
 
         /*
          * This guy is responsible for displaying the Insp_buf contents and
          * managing all scrolling/locate requests until the user gives up. */
-static int insp_view_this (char *hdr) {
+static int insp_view_choice (char *hdr) {
 #ifdef INSP_SLIDE_1
  #define hzAMT  1
 #else
@@ -2594,7 +2653,7 @@ static int insp_view_this (char *hdr) {
       putp(Cap_home);
       putp(Cap_curs_hide);
       show_special(1, fmtmk(N_unq(INSP_hdrview_fmt), hdr));
-      insp_show_pg(curcol, curlin, maxLN);
+      insp_show_pgs(curcol, curlin, maxLN);
       /* fflush(stdin) didn't do the trick, so we'll just dip a little deeper
          lest repeated <Enter> keys produce immediate re-selection in caller */
       tcflush(STDIN_FILENO, TCIFLUSH);
@@ -2632,7 +2691,7 @@ static int insp_view_this (char *hdr) {
             break;
          case 'L': case '&': case '/': case 'n':
             putp(Cap_curs_norm);
-            insp_find(key, &curcol, &curlin);
+            insp_find_str(key, &curcol, &curlin);
             break;
          case '=':
             snprintf(buf, sizeof(buf), "%s", Insp_selfmts);
@@ -2644,8 +2703,9 @@ static int insp_view_this (char *hdr) {
             break;
       }
    }
+ #undef hzAMT
  #undef maxLN
-} // end: insp_view_this
+} // end: insp_view_choice
 
 
         /*
@@ -2673,7 +2733,7 @@ static struct I_struc Inspect;
          *   1) validating the passed pid (required, but not always used)
          *   2) presenting/establishing the target selection
          *   3) arranging to fill Insp_buf (via the Inspect.tab[?].func)
-         *   4) invoking insp_view_this() for viewing/scrolling/searching
+         *   4) invoking insp_view_choice for viewing/scrolling/searching
          *   5) cleaning up the dynamically acquired memory afterwards */
 static void inspection_utility (int pid) {
  #define mkSEL(dst) { for (i = 0; i < Inspect.total; i++) Inspect.tab[i].caps = "~1"; \
@@ -2730,7 +2790,7 @@ static void inspection_utility (int pid) {
             Insp_selname = Inspect.tab[sel].name;
             Insp_selfmts = Inspect.tab[sel].fmts;
             Inspect.tab[sel].func(Inspect.tab[sel].fmts, pid);
-            key = insp_view_this(head);
+            key = insp_view_choice(head);
             free(Insp_buf);
             free(Insp_p);
             break;
index 0f35a03a9a06fec9f1c197b79895d854404c2109..33399cc641d41bf8b9dcba655861b1fa051a0dcc 100644 (file)
--- a/top/top.h
+++ b/top/top.h
@@ -32,6 +32,7 @@
 //#define CASEUP_SUFIX            /* show time/mem/cnts suffix in upper case */
 //#define CPU_ZEROTICS            /* tolerate few tics when cpu off vs. idle */
 //#define EQUCOLHDRYES            /* yes, do equalize column header lengths  */
+//#define INSP_JUSTNOT            /* don't smooth unprintable right margins  */
 //#define INSP_OFFDEMO            /* disable demo screens, issue msg instead */
 //#define INSP_SAVEBUF            /* preserve 'Insp_buf' contents in a file  */
 //#define INSP_SLIDE_1            /* when scrolling left/right don't move 8  */
@@ -650,9 +651,11 @@ typedef struct WIN_t {
 #endif
 //atic void          insp_do_file (char *fmts, int pid);
 //atic void          insp_do_pipe (char *fmts, int pid);
-//atic void          insp_find (int ch, int *col, int *row);
-//atic inline void   insp_show_pg (int col, int row, int max);
-//atic int           insp_view_this (char *hdr);
+//atic inline int    insp_find_ofs (int col, int row);
+//atic void          insp_find_str (int ch, int *col, int *row);
+//atic inline void   insp_make_row (int col, int row);
+//atic void          insp_show_pgs (int col, int row, int max);
+//atic int           insp_view_choice (char *hdr);
 //atic void          inspection_utility (int pid);
 /*------  Startup routines  ----------------------------------------------*/
 //atic void          before (char *me);