diff --git a/bin/ps/extern.h b/bin/ps/extern.h --- a/bin/ps/extern.h +++ b/bin/ps/extern.h @@ -40,6 +40,7 @@ extern time_t now; extern int showthreads, sumrusage, termwidth; extern struct velisthead varlist; +extern const size_t known_keywords_nb; __BEGIN_DECLS char *arguments(KINFO *, VARENT *); @@ -54,6 +55,7 @@ const char *fmt_argv(char **, char *, char *, size_t); double getpcpu(const KINFO *); char *jailname(KINFO *, VARENT *); +size_t keyword_index(const VAR *); char *kvar(KINFO *, VARENT *); char *label(KINFO *, VARENT *); char *loginclass(KINFO *, VARENT *); diff --git a/bin/ps/keyword.c b/bin/ps/keyword.c --- a/bin/ps/keyword.c +++ b/bin/ps/keyword.c @@ -36,10 +36,12 @@ #include #include +#include #include #include #include #include + #include #include "ps.h" @@ -224,6 +226,17 @@ USHORT, "x"}, }; +const size_t known_keywords_nb = nitems(var); + +size_t +keyword_index(const VAR *const v) +{ + const size_t idx = v - var; + + assert(idx < known_keywords_nb); + return (idx); +} + void showkey(void) { @@ -280,16 +293,6 @@ } if (cp == NULL || !(v = findvar(cp, var_list, user, &hp))) continue; - if (!user) { - /* - * If the user is NOT adding this field manually, - * get on with our lives if this VAR is already - * represented in the list. - */ - vent = find_varentry(v->name); - if (vent != NULL) - continue; - } if ((vent = malloc(sizeof(struct varent))) == NULL) xo_errx(1, "malloc failed"); vent->header = v->header; @@ -300,6 +303,7 @@ } vent->width = strlen(vent->header); vent->var = v; + vent->flags = user ? VE_KEEP : 0; STAILQ_INSERT_TAIL(var_list, vent, next_ve); } free(tempstr1); @@ -322,7 +326,7 @@ *hp++ = '\0'; key.name = p; - v = bsearch(&key, var, nitems(var), sizeof(VAR), vcmp); + v = bsearch(&key, var, known_keywords_nb, sizeof(VAR), vcmp); if (v && v->alias) { /* diff --git a/bin/ps/ps.h b/bin/ps/ps.h --- a/bin/ps/ps.h +++ b/bin/ps/ps.h @@ -58,7 +58,9 @@ STAILQ_ENTRY(varent) next_ve; const char *header; const struct var *var; - u_int width; + u_int width; +#define VE_KEEP (1 << 0) + uint16_t flags; } VARENT; STAILQ_HEAD(velisthead, varent); diff --git a/bin/ps/ps.c b/bin/ps/ps.c --- a/bin/ps/ps.c +++ b/bin/ps/ps.c @@ -82,6 +82,19 @@ int termwidth; /* Width of the screen (0 == infinity). */ int showthreads; /* will threads be shown? */ +struct keyword_info { + /* + * Whether there is (at least) one column referencing this keyword that + * must be kept. + */ +#define KWI_HAS_MUST_KEEP_COLUMN (1 << 0) + /* + * Whether a column with such a keyword has been seen. + */ +#define KWI_SEEN (1 << 1) + u_int flags; +}; + struct velisthead varlist = STAILQ_HEAD_INITIALIZER(varlist); static kvm_t *kd; @@ -129,7 +142,8 @@ static char *kludge_oldps_options(const char *, char *, const char *); static int pscomp(const void *, const void *); static void saveuser(KINFO *); -static void scanvars(void); +static void scan_vars(struct keyword_info *); +static void remove_redundant_columns(struct keyword_info *); static void pidmax_init(void); static void usage(void); @@ -166,6 +180,7 @@ char fmtbuf[_POSIX2_LINE_MAX]; enum { NONE = 0, UP = 1, DOWN = 2, BOTH = 1 | 2 } directions = NONE; struct { int traversed; int initial; } pid_count; + struct keyword_info *keywords_info; (void) setlocale(LC_ALL, ""); time(&now); /* Used by routines in print.c. */ @@ -455,6 +470,22 @@ if (!_fmt) parsefmt(dfmt, &varlist, 0); + keywords_info = calloc(known_keywords_nb, sizeof(struct keyword_info)); + if (keywords_info == NULL) + xo_errx(1, "malloc failed"); + /* + * Scan requested variables, noting which structures are needed and + * which keywords are specified. + */ + scan_vars(keywords_info); + /* + * Remove redundant columns from "canned" displays (see the callee's + * herald comment for more details). + */ + remove_redundant_columns(keywords_info); + free(keywords_info); + keywords_info = NULL; + if (!all && nselectors == 0) { uidlist.l.ptr = malloc(sizeof(uid_t)); if (uidlist.l.ptr == NULL) @@ -464,12 +495,6 @@ *uidlist.l.uids = getuid(); } - /* - * scan requested variables, noting what structures are needed, - * and adjusting header widths as appropriate. - */ - scanvars(); - /* * Get process list. If the user requested just one selector- * option, then kvm_getprocs can be asked to return just those @@ -1189,7 +1214,7 @@ } static void -scanvars(void) +scan_vars(struct keyword_info *const keywords_info) { struct varent *vent; const VAR *v; @@ -1200,6 +1225,47 @@ needuser = 1; if (v->flag & COMM) needcomm = 1; + if ((vent->flags & VE_KEEP) != 0) + keywords_info[keyword_index(v)].flags |= + KWI_HAS_MUST_KEEP_COLUMN; + } +} + +/* + * For each explicitly requested keyword, remove all the same keywords + * from "canned" displays. If the same keyword appears multiple times + * only in "canned displays", then keep the first (leftmost) occurence + * only (with the reasoning that columns requested first are the most + * important as their positions catch the eye more). + */ +static void +remove_redundant_columns(struct keyword_info *const keywords_info) +{ + struct varent *prev_vent, *vent, *next_vent; + + prev_vent = NULL; + STAILQ_FOREACH_SAFE(vent, &varlist, next_ve, next_vent) { + const VAR *const v = vent->var; + struct keyword_info *const kwi = + &keywords_info[keyword_index(v)]; + + /* + * If the current column is not marked as to absolutely keep, + * and we have either already output one with the same keyword + * or know we will output one later, remove it. + */ + if ((vent->flags & VE_KEEP) == 0 && + (kwi->flags & (KWI_HAS_MUST_KEEP_COLUMN | KWI_SEEN)) != 0) { + if (prev_vent == NULL) + STAILQ_REMOVE_HEAD(&varlist, next_ve); + else + STAILQ_REMOVE_AFTER(&varlist, prev_vent, + next_ve); + } else + prev_vent = vent; + + + kwi->flags |= KWI_SEEN; } }