summaryrefslogtreecommitdiffstats
path: root/src/ps/output.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ps/output.c')
-rw-r--r--src/ps/output.c2370
1 files changed, 2370 insertions, 0 deletions
diff --git a/src/ps/output.c b/src/ps/output.c
new file mode 100644
index 0000000..a4b3833
--- /dev/null
+++ b/src/ps/output.c
@@ -0,0 +1,2370 @@
+/*
+ * output.c - ps output definitions
+ *
+ * Copyright © 2015-2023 Jim Warner <james.warner@comcast.net
+ * Copyright © 2004-2023 Craig Small <csmall@dropbear.xyz>
+ * Copyright © 2011 Lukas Nykryn <lnykryn@redhat.com>
+ * Copyright © 1999-2004 Albert Cahalan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * This file is really gross, and I know it. I looked into several
+ * alternate ways to deal with the mess, and they were all ugly.
+ *
+ * FreeBSD has a fancy hack using offsets into a struct -- that
+ * saves code but it is _really_ gross. See the PO macro below.
+ *
+ * We could have a second column width for wide output format.
+ * For example, Digital prints the real-time signals.
+ */
+
+/*
+ * Data table idea:
+ *
+ * table 1 maps aix to specifier
+ * table 2 maps shortsort to specifier
+ * table 3 maps macro to specifiers
+ * table 4 maps specifier to title,datatype,offset,vendor,helptext
+ * table 5 maps datatype to justification,width,widewidth,sorting,printing
+ *
+ * Here, "datatype" could be user,uid,u16,pages,deltaT,signals,tty,longtty...
+ * It must be enough to determine printing and sorting.
+ *
+ * After the tables, increase width as needed to fit the header.
+ *
+ * Table 5 could go in a file with the output functions.
+ */
+
+#include <ctype.h>
+#if ENABLE_LIBSELINUX
+#include <dlfcn.h>
+#endif
+#include <ctype.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <langinfo.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+
+#include "c.h"
+
+#include "common.h"
+
+/* TODO:
+ * Stop assuming system time is local time.
+ */
+
+#define COLWID 240 /* satisfy snprintf, which is faster than sprintf */
+#define SIGNAL_NAME_WIDTH 27
+
+static unsigned max_rightward = OUTBUF_SIZE-1; /* space for RIGHT stuff */
+static unsigned max_leftward = OUTBUF_SIZE-1; /* space for LEFT stuff */
+
+
+static int wide_signals; /* true if we have room */
+
+static time_t seconds_since_1970;
+
+
+extern long Hertz;
+
+
+static unsigned int boot_time(void)
+{
+ static unsigned int boot_time = 0;
+ struct stat_info *stat_info = NULL;
+ if (boot_time == 0) {
+ if (procps_stat_new(&stat_info) < 0)
+ xerrx(EXIT_FAILURE, _("Unable to get system boot time"));
+ boot_time = STAT_GET(stat_info, STAT_SYS_TIME_OF_BOOT, ul_int);
+ procps_stat_unref(&stat_info);
+ }
+ return boot_time;
+}
+
+static unsigned long memory_total()
+{
+ static unsigned long memory_total = 0;
+ struct meminfo_info *mem_info = NULL;
+
+ if (memory_total == 0) {
+ if (procps_meminfo_new(&mem_info) < 0)
+ xerrx(EXIT_FAILURE,
+ _("Unable to get total memory"));
+ memory_total = MEMINFO_GET(mem_info, MEMINFO_MEM_TOTAL, ul_int);
+ procps_meminfo_unref(&mem_info);
+ }
+ return memory_total;
+}
+
+#define SECURE_ESCAPE_ARGS(dst, bytes, cells) do { \
+ if ((bytes) <= 0) return 0; \
+ *(dst) = '\0'; \
+ if ((bytes) >= INT_MAX) return 0; \
+ if ((cells) >= INT_MAX) return 0; \
+ if ((cells) <= 0) return 0; \
+} while (0)
+
+// copy a string that doesn't need to be 'escaped'
+static int escaped_copy(char *restrict dst, const char *restrict src, int bufsize, int *maxroom){
+ int n;
+
+ SECURE_ESCAPE_ARGS(dst, bufsize, *maxroom);
+ if (bufsize > *maxroom+1)
+ bufsize = *maxroom+1;
+ n = snprintf(dst, bufsize, "%s", src);
+ if (n < 0) {
+ *dst = '\0';
+ return 0;
+ }
+ if (n >= bufsize)
+ n = bufsize-1;
+ *maxroom -= n;
+ return n;
+}
+
+// duplicated from proc/escape.c so both can be made private
+static int escape_str_utf8 (char *dst, const char *src, int bufsize, int *maxcells) {
+ int my_cells = 0;
+ int my_bytes = 0;
+ mbstate_t s;
+
+ SECURE_ESCAPE_ARGS(dst, bufsize, *maxcells);
+
+ memset(&s, 0, sizeof (s));
+
+ for(;;) {
+ wchar_t wc;
+ int len = 0;
+
+ if(my_cells >= *maxcells || my_bytes+1 >= bufsize)
+ break;
+
+ if (!(len = mbrtowc (&wc, src, MB_CUR_MAX, &s)))
+ /* 'str' contains \0 */
+ break;
+
+ if (len < 0) {
+ /* invalid multibyte sequence -- zeroize state */
+ memset (&s, 0, sizeof (s));
+ *(dst++) = '?';
+ src++;
+ my_cells++;
+ my_bytes++;
+
+ } else if (len==1) {
+ /* non-multibyte */
+ *(dst++) = isprint(*src) ? *src : '?';
+ src++;
+ my_cells++;
+ my_bytes++;
+
+ } else if (!iswprint(wc)) {
+ /* multibyte - no printable */
+ *(dst++) = '?';
+ src+=len;
+ my_cells++;
+ my_bytes++;
+
+ } else {
+ /* multibyte - maybe, kinda "printable" */
+ int wlen = wcwidth(wc);
+ // Got space?
+ if (wlen > *maxcells-my_cells || len >= bufsize-(my_bytes+1)) break;
+ // safe multibyte
+ memcpy(dst, src, len);
+ dst += len;
+ src += len;
+ my_bytes += len;
+ if (wlen > 0) my_cells += wlen;
+ }
+ //fprintf(stdout, "cells: %d\n", my_cells);
+ }
+ *dst = '\0';
+
+ // fprintf(stderr, "maxcells: %d, my_cells; %d\n", *maxcells, my_cells);
+
+ *maxcells -= my_cells;
+ return my_bytes; // bytes of text, excluding the NUL
+}
+
+// duplicated from proc/escape.c so both can be made private
+static int escape_str (char *dst, const char *src, int bufsize, int *maxcells) {
+ unsigned char c;
+ int my_cells = 0;
+ int my_bytes = 0;
+ const char codes[] =
+ "Z..............................."
+ "||||||||||||||||||||||||||||||||"
+ "||||||||||||||||||||||||||||||||"
+ "|||||||||||||||||||||||||||||||."
+ "????????????????????????????????"
+ "????????????????????????????????"
+ "????????????????????????????????"
+ "????????????????????????????????";
+ static int utf_init=0;
+
+ if(utf_init==0){
+ /* first call -- check if UTF stuff is usable */
+ char *enc = nl_langinfo(CODESET);
+ utf_init = enc && strcasecmp(enc, "UTF-8")==0 ? 1 : -1;
+ }
+ if (utf_init==1 && MB_CUR_MAX>1) {
+ /* UTF8 locales */
+ return escape_str_utf8(dst, src, bufsize, maxcells);
+ }
+
+ SECURE_ESCAPE_ARGS(dst, bufsize, *maxcells);
+
+ if(bufsize > *maxcells+1) bufsize=*maxcells+1; // FIXME: assumes 8-bit locale
+
+ for(;;){
+ if(my_cells >= *maxcells || my_bytes+1 >= bufsize)
+ break;
+ c = (unsigned char) *(src++);
+ if(!c) break;
+ if(codes[c]!='|') c=codes[c];
+ my_cells++;
+ my_bytes++;
+ *(dst++) = c;
+ }
+ *dst = '\0';
+
+ *maxcells -= my_cells;
+ return my_bytes; // bytes of text, excluding the NUL
+}
+
+/***************************************************************************/
+/************ Lots of format functions, starting with the NOP **************/
+
+// so popular it can't be "static"
+int pr_nop(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(noop)
+ (void)pp;
+ return snprintf(outbuf, COLWID, "%c", '-');
+}
+
+/********* Unix 98 ************/
+/***
+
+Only comm and args are allowed to contain blank characters; all others are
+not. Any implementation-dependent variables will be specified in the system
+documentation along with the default header and indicating if the field
+may contain blank characters.
+
+Some headers do not have a standardized specifier!
+
+%CPU pcpu The % of cpu time used recently, with unspecified "recently".
+ADDR The address of the process.
+C Processor utilisation for scheduling.
+CMD The command name, or everything with -f.
+COMMAND args Command + args. May chop as desired. May use either version.
+COMMAND comm argv[0]
+ELAPSED etime Elapsed time since the process was started. [[dd-]hh:]mm:ss
+F Flags (octal and additive)
+GROUP group Effective group ID, prefer text over decimal.
+NI nice Decimal system scheduling priority, see nice(1).
+PGID pgid The decimal value of the process group ID.
+PID pid Decimal PID.
+PPID ppid Decimal PID.
+PRI Priority. Higher numbers mean lower priority.
+RGROUP rgroup Real group ID, prefer text over decimal.
+RUSER ruser Real user ID, prefer text over decimal.
+S The state of the process.
+STIME Starting time of the process.
+SZ The size in blocks of the core image of the process.
+TIME time Cumulative CPU time. [dd-]hh:mm:ss
+TT tty Name of tty in format used by who(1).
+TTY The controlling terminal for the process.
+UID UID, or name when -f
+USER user Effective user ID, prefer text over decimal.
+VSZ vsz Virtual memory size in decimal kB.
+WCHAN Where waiting/sleeping or blank if running.
+
+The nice value is used to compute the priority.
+
+For some undefined ones, Digital does:
+
+F flag Process flags -- but in hex!
+PRI pri Process priority
+S state Symbolic process status
+TTY tt,tty,tname,longtname -- all do "ttyp1", "console", "??"
+UID uid Process user ID (effective UID)
+WCHAN wchan Address of event on which a
+
+For some undefined ones, Sun does:
+
+ADDR addr memory address of the process
+C c Processor utilization for scheduling (obsolete).
+CMD
+F f
+S s state: OSRZT
+STIME start time, printed w/o blanks. If 24h old, months & days
+SZ size (in pages) of the swappable process's image in main memory
+TTY
+UID uid
+WCHAN wchan
+
+For some undefined ones, SCO does:
+ADDR addr Virtual address of the process' entry in the process table.
+SZ swappable size in kB of the virtual data and stack
+STIME stime hms or md time format
+***/
+
+/* Source & destination are known. Return bytes or screen characters? */
+//
+// OldLinux FreeBSD HPUX
+// ' ' ' ' ' ' ' '
+// 'L' ' \_ ' '`-' ' '
+// '+' ' \_ ' '|-' ' '
+// '|' ' | ' '| ' ' '
+//
+static int forest_helper(char *restrict const outbuf){
+ char *p = forest_prefix;
+ char *q = outbuf;
+ int rightward = max_rightward < OUTBUF_SIZE ? max_rightward : OUTBUF_SIZE-1;
+ *q = '\0';
+ if(!*p) return 0;
+ /* Arrrgh! somebody defined unix as 1 */
+ if(forest_type == 'u') goto unixy;
+ while(*p){
+ if (rightward < 4) break;
+ switch(*p){
+ case ' ': strcpy(q, " "); break;
+ case 'L': strcpy(q, " \\_ "); break;
+ case '+': strcpy(q, " \\_ "); break;
+ case '|': strcpy(q, " | "); break;
+ case '\0': return q-outbuf; /* redundant & not used */
+ }
+ q += 4;
+ rightward -= 4;
+ p++;
+ }
+ return q-outbuf; /* gcc likes this here */
+unixy:
+ while(*p){
+ if (rightward < 2) break;
+ switch(*p){
+ case ' ': strcpy(q, " "); break;
+ case 'L': strcpy(q, " "); break;
+ case '+': strcpy(q, " "); break;
+ case '|': strcpy(q, " "); break;
+ case '\0': return q-outbuf; /* redundant & not used */
+ }
+ q += 2;
+ rightward -= 2;
+ p++;
+ }
+ return q-outbuf; /* gcc likes this here */
+}
+
+
+/* XPG4-UNIX, according to Digital:
+The "args" and "command" specifiers show what was passed to the command.
+Modifications to the arguments are not shown.
+*/
+
+/*
+ * pp->cmd short accounting name (comm & ucomm)
+ * pp->cmdline long name with args (args & command)
+ * pp->environ environment
+ */
+
+// FIXME: some of these may hit the guard page in forest mode
+
+#define OUTBUF_SIZE_AT(endp) \
+ (((endp) >= outbuf && (endp) < outbuf + OUTBUF_SIZE) ? (outbuf + OUTBUF_SIZE) - (endp) : 0)
+
+/*
+ * "args", "cmd", "command" are all the same: long unless c
+ * "comm", "ucmd", "ucomm" are all the same: short unless -f
+ * ( determinations are made in display.c, we mostly deal with results ) */
+static int pr_args(char *restrict const outbuf, const proc_t *restrict const pp){
+ char *endp;
+ int rightward, fh;
+setREL3(CMDLINE,CMD,ENVIRON)
+ endp = outbuf;
+ rightward = max_rightward;
+ fh = forest_helper(outbuf);
+ endp += fh;
+ rightward -= fh;
+ if (!bsd_c_option)
+ endp += escape_str(endp, rSv(CMDLINE, str, pp), OUTBUF_SIZE_AT(endp), &rightward);
+ else
+ endp += escape_str(endp, rSv(CMD, str, pp), OUTBUF_SIZE_AT(endp), &rightward);
+ if(bsd_e_option && rightward>1) {
+ char *e = rSv(ENVIRON, str, pp);
+ if(*e != '-' || *(e+1) != '\0') {
+ *endp++ = ' ';
+ rightward--;
+ escape_str(endp, e, OUTBUF_SIZE_AT(endp), &rightward);
+ }
+ }
+ return max_rightward-rightward;
+}
+
+/*
+ * "args", "cmd", "command" are all the same: long unless c
+ * "comm", "ucmd", "ucomm" are all the same: short unless -f
+ * ( determinations are made in display.c, we mostly deal with results ) */
+static int pr_comm(char *restrict const outbuf, const proc_t *restrict const pp){
+ char *endp;
+ int rightward, fh;
+setREL3(CMD,CMDLINE,ENVIRON)
+ endp = outbuf;
+ rightward = max_rightward;
+ fh = forest_helper(outbuf);
+ endp += fh;
+ rightward -= fh;
+ if(unix_f_option)
+ endp += escape_str(endp, rSv(CMDLINE, str, pp), OUTBUF_SIZE_AT(endp), &rightward);
+ else
+ endp += escape_str(endp, rSv(CMD, str, pp), OUTBUF_SIZE_AT(endp), &rightward);
+ if(bsd_e_option && rightward>1) {
+ char *e = rSv(ENVIRON, str, pp);
+ if(*e != '-' || *(e+1) != '\0') {
+ *endp++ = ' ';
+ rightward--;
+ escape_str(endp, e, OUTBUF_SIZE_AT(endp), &rightward);
+ }
+ }
+ return max_rightward-rightward;
+}
+
+static int pr_cgname(char *restrict const outbuf,const proc_t *restrict const pp) {
+ int rightward;
+setREL1(CGNAME)
+ rightward = max_rightward;
+ escape_str(outbuf, rSv(CGNAME, str, pp), OUTBUF_SIZE, &rightward);
+ return max_rightward-rightward;
+}
+
+static int pr_cgroup(char *restrict const outbuf,const proc_t *restrict const pp) {
+ int rightward;
+setREL1(CGROUP)
+ rightward = max_rightward;
+ escape_str(outbuf, rSv(CGROUP, str, pp), OUTBUF_SIZE, &rightward);
+ return max_rightward-rightward;
+}
+
+/* Non-standard, from SunOS 5 */
+static int pr_fname(char *restrict const outbuf, const proc_t *restrict const pp){
+ char *endp;
+ int rightward, fh;
+setREL1(CMD)
+ endp = outbuf;
+ rightward = max_rightward;
+ fh = forest_helper(outbuf);
+ endp += fh;
+ rightward -= fh;
+ if (rightward>8) /* 8=default, but forest maybe feeds more */
+ rightward = 8;
+ endp += escape_str(endp, rSv(CMD, str, pp), OUTBUF_SIZE_AT(endp), &rightward);
+ //return endp - outbuf;
+ return max_rightward-rightward;
+}
+
+#undef OUTBUF_SIZE_AT
+
+/* elapsed wall clock time, [[dd-]hh:]mm:ss format (not same as "time") */
+static int pr_etime(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long t;
+ unsigned dd,hh,mm,ss;
+ char *cp;
+setREL1(TIME_ELAPSED)
+ cp = outbuf;
+ t = rSv(TIME_ELAPSED, real, pp);
+ ss = t%60;
+ t /= 60;
+ mm = t%60;
+ t /= 60;
+ hh = t%24;
+ t /= 24;
+ dd = t;
+ cp +=( dd ? snprintf(cp, COLWID, "%u-", dd) : 0 );
+ cp +=( (dd || hh) ? snprintf(cp, COLWID, "%02u:", hh) : 0 );
+ cp += snprintf(cp, COLWID, "%02u:%02u", mm, ss) ;
+ return (int)(cp-outbuf);
+}
+
+/* elapsed wall clock time in seconds */
+static int pr_etimes(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned t;
+setREL1(TIME_ELAPSED)
+ t = rSv(TIME_ELAPSED, real, pp);
+ return snprintf(outbuf, COLWID, "%u", t);
+}
+
+/* "Processor utilisation for scheduling." --- we use %cpu w/o fraction */
+static int pr_c(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long long total_time; /* jiffies used by this process */
+ unsigned pcpu; /* scaled %cpu, 99 means 99% */
+ unsigned long long jiffies; /* jiffies of process life */
+setREL4(TICS_ALL,TICS_ALL_C,TIME_ELAPSED,UTILIZATION)
+ pcpu = 0;
+ if(include_dead_children) total_time = rSv(TICS_ALL_C, ull_int, pp);
+ else total_time = rSv(TICS_ALL, ull_int, pp);
+ jiffies = rSv(TIME_ELAPSED, real, pp) * Hertz;
+ if(jiffies) pcpu = (total_time * 100ULL) / jiffies;
+ if (pcpu > 99U) pcpu = 99U;
+ return snprintf(outbuf, COLWID, "%2u", pcpu);
+}
+
+/* normal %CPU in ##.# format. */
+static int pr_pcpu(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long long total_time; /* jiffies used by this process */
+ unsigned pcpu; /* scaled %cpu, 999 means 99.9% */
+ unsigned long long jiffies; /* jiffies of process life */
+setREL4(TICS_ALL,TICS_ALL_C,TIME_ELAPSED,UTILIZATION)
+ pcpu = 0;
+ if(include_dead_children) total_time = rSv(TICS_ALL_C, ull_int, pp);
+ else total_time = rSv(TICS_ALL, ull_int, pp);
+ jiffies = rSv(TIME_ELAPSED, real, pp) * Hertz;
+ if(jiffies) pcpu = (total_time * 1000ULL) / jiffies;
+ if (pcpu > 999U)
+ return snprintf(outbuf, COLWID, "%u", pcpu/10U);
+ return snprintf(outbuf, COLWID, "%u.%u", pcpu/10U, pcpu%10U);
+}
+
+/* this is a "per-mill" format, like %cpu with no decimal point */
+static int pr_cp(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long long total_time; /* jiffies used by this process */
+ unsigned pcpu; /* scaled %cpu, 999 means 99.9% */
+ unsigned long long jiffies; /* jiffies of process life */
+setREL4(TICS_ALL,TICS_ALL_C,TIME_ELAPSED,UTILIZATION)
+ pcpu = 0;
+ if(include_dead_children) total_time = rSv(TICS_ALL_C, ull_int, pp);
+ else total_time = rSv(TICS_ALL, ull_int, pp);
+ jiffies = rSv(TIME_ELAPSED, real, pp) * Hertz;
+ if(jiffies) pcpu = (total_time * 1000ULL) / jiffies;
+ if (pcpu > 999U) pcpu = 999U;
+ return snprintf(outbuf, COLWID, "%3u", pcpu);
+}
+
+static int pr_pgid(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(ID_PGRP)
+ return snprintf(outbuf, COLWID, "%u", rSv(ID_PGRP, s_int, pp));
+}
+static int pr_ppid(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(ID_PPID)
+ return snprintf(outbuf, COLWID, "%u", rSv(ID_PPID, s_int, pp));
+}
+
+/* cumulative CPU time, [dd-]hh:mm:ss format (not same as "etime") */
+static int pr_time(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long t;
+ unsigned dd,hh,mm,ss;
+ int c;
+setREL1(TIME_ALL)
+ t = rSv(TIME_ALL, real, pp);
+ ss = t%60;
+ t /= 60;
+ mm = t%60;
+ t /= 60;
+ hh = t%24;
+ t /= 24;
+ dd = t;
+ c =( dd ? snprintf(outbuf, COLWID, "%u-", dd) : 0 );
+ c +=( snprintf(outbuf+c, COLWID, "%02u:%02u:%02u", hh, mm, ss) );
+ return c;
+}
+
+/* cumulative CPU time in seconds (not same as "etimes") */
+static int pr_times(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long t;
+setREL1(TIME_ALL)
+ t = rSv(TIME_ALL, real, pp);
+ return snprintf(outbuf, COLWID, "%lu", t);
+}
+
+/* HP-UX puts this (I forget, vsz or vsize?) in kB and uses "sz" for pages.
+ * Unix98 requires "vsz" to be kB.
+ * Tru64 does both vsize and vsz like "1.23M"
+ *
+ * Our pp->vm_size is kB and our pp->vsize is pages.
+ *
+ * TODO: add flag for "1.23M" behavior, on this and other columns.
+ */
+static int pr_vsz(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(VM_SIZE)
+ return snprintf(outbuf, COLWID, "%lu", rSv(VM_SIZE, ul_int, pp));
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+// "PRI" is created by "opri", or by "pri" when -c is used.
+//
+// Unix98 only specifies that a high "PRI" is low priority.
+// Sun and SCO add the -c behavior. Sun defines "pri" and "opri".
+// Linux may use "priority" for historical purposes.
+//
+// According to the kernel's fs/proc/array.c and kernel/sched.c source,
+// the kernel reports it in /proc via this:
+// p->prio - MAX_RT_PRIO
+// such that "RT tasks are offset by -200. Normal tasks are centered
+// around 0, value goes from -16 to +15" but who knows if that is
+// before or after the conversion...
+//
+// <linux/sched.h> says:
+// MAX_RT_PRIO is currently 100. (so we see 0 in /proc)
+// RT tasks have a p->prio of 0 to 99. (so we see -100 to -1)
+// non-RT tasks are from 100 to 139. (so we see 0 to 39)
+// Lower values have higher priority, as in the UNIX standard.
+//
+// In any case, pp->priority+100 should get us back to what the kernel
+// has for p->prio.
+//
+// Test results with the "yes" program on a 2.6.x kernel:
+//
+// # ps -C19,_20 -o pri,opri,intpri,priority,ni,pcpu,pid,comm
+// PRI PRI PRI PRI NI %CPU PID COMMAND
+// 0 99 99 39 19 10.6 8686 19
+// 34 65 65 5 -20 94.7 8687 _20
+//
+// Grrr. So the UNIX standard "PRI" must NOT be from "pri".
+// Either of the others will do. We use "opri" for this.
+// (and use "pri" when the "-c" option is used)
+// Probably we should have Linux-specific "pri_for_l" and "pri_for_lc"
+//
+// sched_get_priority_min.2 says the Linux static priority is
+// 1..99 for RT and 0 for other... maybe 100 is kernel-only?
+//
+// A nice range would be -99..0 for RT and 1..40 for normal,
+// which is pp->priority+1. (3-digit max, positive is normal,
+// negative or 0 is RT, and meets the standard for PRI)
+//
+
+// legal as UNIX "PRI"
+// "priority" (was -20..20, now -100..39)
+static int pr_priority(char *restrict const outbuf, const proc_t *restrict const pp){ /* -20..20 */
+setREL1(PRIORITY)
+ return snprintf(outbuf, COLWID, "%d", rSv(PRIORITY, s_int, pp));
+}
+
+// legal as UNIX "PRI"
+// "intpri" and "opri" (was 39..79, now -40..99)
+static int pr_opri(char *restrict const outbuf, const proc_t *restrict const pp){ /* 39..79 */
+setREL1(PRIORITY)
+ return snprintf(outbuf, COLWID, "%d", 60 + rSv(PRIORITY, s_int, pp));
+}
+
+// legal as UNIX "PRI"
+// "pri_foo" -- match up w/ nice values of sleeping processes (-120..19)
+static int pr_pri_foo(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(PRIORITY)
+ return snprintf(outbuf, COLWID, "%d", rSv(PRIORITY, s_int, pp) - 20);
+}
+
+// legal as UNIX "PRI"
+// "pri_bar" -- makes RT pri show as negative (-99..40)
+static int pr_pri_bar(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(PRIORITY)
+ return snprintf(outbuf, COLWID, "%d", rSv(PRIORITY, s_int, pp) + 1);
+}
+
+// legal as UNIX "PRI"
+// "pri_baz" -- the kernel's ->prio value, as of Linux 2.6.8 (1..140)
+static int pr_pri_baz(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(PRIORITY)
+ return snprintf(outbuf, COLWID, "%d", rSv(PRIORITY, s_int, pp) + 100);
+}
+
+// not legal as UNIX "PRI"
+// "pri" (was 20..60, now 0..139)
+static int pr_pri(char *restrict const outbuf, const proc_t *restrict const pp){ /* 20..60 */
+setREL1(PRIORITY)
+ return snprintf(outbuf, COLWID, "%d", 39 - rSv(PRIORITY, s_int, pp));
+}
+
+// not legal as UNIX "PRI"
+// "pri_api" -- match up w/ RT API (-40..99)
+static int pr_pri_api(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(PRIORITY)
+ return snprintf(outbuf, COLWID, "%d", -1 - rSv(PRIORITY, s_int, pp));
+}
+
+// Linux applies nice value in the scheduling policies (classes)
+// SCHED_OTHER(0) and SCHED_BATCH(3). Ref: sched_setscheduler(2).
+// Also print nice value for old kernels which didn't use scheduling
+// policies (-1).
+static int pr_nice(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL2(NICE,SCHED_CLASS)
+ if(rSv(SCHED_CLASS, s_int, pp)!=0 && rSv(SCHED_CLASS, s_int, pp)!=3 && rSv(SCHED_CLASS, s_int, pp)!=-1) return snprintf(outbuf, COLWID, "-");
+ return snprintf(outbuf, COLWID, "%d", rSv(NICE, s_int, pp));
+}
+
+static int pr_oom_adj(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(OOM_ADJ)
+ return snprintf(outbuf, COLWID, "%d", rSv(OOM_ADJ, s_int, pp));
+}
+
+static int pr_oom(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(OOM_SCORE)
+ return snprintf(outbuf, COLWID, "%d", rSv(OOM_SCORE, s_int, pp));
+}
+// HP-UX "cls": RT RR RR2 ???? HPUX FIFO KERN
+// Solaris "class": SYS TS FX IA RT FSS (FIFO is RR w/ Inf quant)
+// FIFO+RR share RT; FIFO has Inf quant
+// IA=interactive; FX=fixed; TS=timeshare; SYS=system
+// FSS=fairshare; INTS=interrupts
+// Tru64 "policy": FF RR TS
+// IRIX "class": RT TS B BC WL GN
+// RT=real-time; TS=time-share; B=batch; BC=batch-critical
+// WL=weightless; GN=gang-scheduled
+// see miser(1) for this; PRI has some letter codes too
+static int pr_class(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(SCHED_CLASS)
+ switch(rSv(SCHED_CLASS, s_int, pp)){
+ case -1: return snprintf(outbuf, COLWID, "-"); // not reported
+ case 0: return snprintf(outbuf, COLWID, "TS"); // SCHED_OTHER SCHED_NORMAL
+ case 1: return snprintf(outbuf, COLWID, "FF"); // SCHED_FIFO
+ case 2: return snprintf(outbuf, COLWID, "RR"); // SCHED_RR
+ case 3: return snprintf(outbuf, COLWID, "B"); // SCHED_BATCH
+ case 4: return snprintf(outbuf, COLWID, "ISO"); // reserved for SCHED_ISO (Con Kolivas)
+ case 5: return snprintf(outbuf, COLWID, "IDL"); // SCHED_IDLE
+ case 6: return snprintf(outbuf, COLWID, "DLN"); // SCHED_DEADLINE
+ case 7: return snprintf(outbuf, COLWID, "#7"); //
+ case 8: return snprintf(outbuf, COLWID, "#8"); //
+ case 9: return snprintf(outbuf, COLWID, "#9"); //
+ default: return snprintf(outbuf, COLWID, "?"); // unknown value
+ }
+}
+
+// Based on "type", FreeBSD would do:
+// REALTIME "real:%u", prio
+// NORMAL "normal"
+// IDLE "idle:%u", prio
+// default "%u:%u", type, prio
+// We just print the priority, and have other keywords for type.
+static int pr_rtprio(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL2(SCHED_CLASS,PRIORITY_RT)
+ if(rSv(SCHED_CLASS, s_int, pp)==0 || rSv(SCHED_CLASS, s_int, pp)==-1) return snprintf(outbuf, COLWID, "-");
+ return snprintf(outbuf, COLWID, "%d", rSv(PRIORITY_RT, s_int, pp));
+}
+
+static int pr_sched(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(SCHED_CLASS)
+ if(rSv(SCHED_CLASS, s_int, pp)==-1) return snprintf(outbuf, COLWID, "-");
+ return snprintf(outbuf, COLWID, "%d", rSv(SCHED_CLASS, s_int, pp));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+static int pr_wchan(char *restrict const outbuf, const proc_t *restrict const pp){
+/*
+ * Unix98 says "blank if running" and also "no blanks"! :-(
+ * Unix98 also says to use '-' if something is meaningless.
+ * Digital uses both '*' and '-', with undocumented differences.
+ * (the '*' for -1 (rare) and the '-' for 0)
+ * Sun claims to use a blank AND use '-', in the same man page.
+ * Perhaps "blank" should mean '-'.
+ *
+ * AIX uses '-' for running processes, the location when there is
+ * only one thread waiting in the kernel, and '*' when there is
+ * more than one thread waiting in the kernel.
+ *
+ * The output should be truncated to maximal columns width -- overflow
+ * is not supported for the "wchan".
+ */
+ const char *w;
+ size_t len;
+setREL1(WCHAN_NAME)
+ w = rSv(WCHAN_NAME, str, pp);
+ len = strlen(w);
+ if(len>max_rightward) len=max_rightward;
+ memcpy(outbuf, w, len);
+ outbuf[len] = '\0';
+ return len;
+}
+
+/* Terrible trunctuation, like BSD crap uses: I999 J999 K999 */
+/* FIXME: disambiguate /dev/tty69 and /dev/pts/69. */
+static int pr_tty4(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(TTY_NUMBER)
+ return snprintf(outbuf, COLWID, "%s", rSv(TTY_NUMBER, str, pp));
+}
+
+/* Unix98: format is unspecified, but must match that used by who(1). */
+static int pr_tty8(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(TTY_NAME)
+ return snprintf(outbuf, COLWID, "%s", rSv(TTY_NAME, str, pp));
+}
+
+#if 0
+/* This BSD state display may contain spaces, which is illegal. */
+static int pr_oldstate(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%s", status(pp));
+}
+#endif
+
+// This state display is Unix98 compliant and has lots of info like BSD.
+static int pr_stat(char *restrict const outbuf, const proc_t *restrict const pp){
+ int end;
+ if (!outbuf) {
+ chkREL(STATE)
+ chkREL(NICE)
+ chkREL(VM_RSS_LOCKED)
+ chkREL(ID_SESSION)
+ chkREL(ID_TGID)
+ chkREL(NLWP)
+ chkREL(ID_PGRP)
+ chkREL(ID_TPGID)
+ return 0;
+ }
+ end = 0;
+ outbuf[end++] = rSv(STATE, s_ch, pp);
+// if(rSv(RSS, ul_int, pp)==0 && rSv(STATE, s_ch, pp)!='Z') outbuf[end++] = 'W'; // useless "swapped out"
+ if(rSv(NICE, s_int, pp) < 0) outbuf[end++] = '<';
+ if(rSv(NICE, s_int, pp) > 0) outbuf[end++] = 'N';
+// In this order, NetBSD would add:
+// traced 'X'
+// systrace 'x'
+// exiting 'E' (not printed for zombies)
+// vforked 'V'
+// system 'K' (and do not print 'L' too)
+ if(rSv(VM_RSS_LOCKED, ul_int, pp)) outbuf[end++] = 'L';
+ if(rSv(ID_SESSION, s_int, pp) == rSv(ID_TGID, s_int, pp)) outbuf[end++] = 's'; // session leader
+ if(rSv(NLWP, s_int, pp) > 1) outbuf[end++] = 'l'; // multi-threaded
+ if(rSv(ID_PGRP, s_int, pp) == rSv(ID_TPGID, s_int, pp)) outbuf[end++] = '+'; // in foreground process group
+ outbuf[end] = '\0';
+ return end;
+}
+
+/* This minimal state display is Unix98 compliant, like SCO and SunOS 5 */
+static int pr_s(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(STATE)
+ outbuf[0] = rSv(STATE, s_ch, pp);
+ outbuf[1] = '\0';
+ return 1;
+}
+
+static int pr_flag(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(FLAGS)
+ /* Unix98 requires octal flags */
+ /* this user-hostile and volatile junk gets 1 character */
+ return snprintf(outbuf, COLWID, "%o", (unsigned)(rSv(FLAGS, ul_int, pp)>>6U)&0x7U);
+}
+
+// plus these: euid,ruid,egroup,rgroup (elsewhere in this file)
+
+/*********** non-standard ***********/
+
+/*** BSD
+sess session pointer
+(SCO has:Process session leader ID as a decimal value. (SESSION))
+jobc job control count
+cpu short-term cpu usage factor (for scheduling)
+sl sleep time (in seconds; 127 = infinity)
+re core residency time (in seconds; 127 = infinity)
+pagein pageins (same as majflt)
+lim soft memory limit
+tsiz text size (in Kbytes)
+***/
+
+static int pr_stackp(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(ADDR_STACK_START)
+ return snprintf(outbuf, COLWID, "%0*lx", (int)(2*sizeof(long)), rSv(ADDR_STACK_START, ul_int, pp));
+}
+
+static int pr_esp(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(ADDR_CURR_ESP)
+ return snprintf(outbuf, COLWID, "%0*lx", (int)(2*sizeof(long)), rSv(ADDR_CURR_ESP, ul_int, pp));
+}
+
+static int pr_eip(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(ADDR_CURR_EIP)
+ return snprintf(outbuf, COLWID, "%0*lx", (int)(2*sizeof(long)), rSv(ADDR_CURR_EIP, ul_int, pp));
+}
+
+static int pr_bsdtime(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long long t;
+ unsigned u;
+setREL2(TICS_ALL,TICS_ALL_C)
+ if(include_dead_children) t = rSv(TICS_ALL_C, ull_int, pp);
+ else t = rSv(TICS_ALL, ull_int, pp);
+ u = t / Hertz;
+ return snprintf(outbuf, COLWID, "%3u:%02u", u/60U, u%60U);
+}
+
+static int pr_bsdstart(char *restrict const outbuf, const proc_t *restrict const pp){
+ time_t start;
+ time_t seconds_ago;
+setREL1(TICS_BEGAN)
+ start = boot_time() + rSv(TICS_BEGAN, ull_int, pp) / Hertz;
+ seconds_ago = seconds_since_1970 - start;
+ if(seconds_ago < 0) seconds_ago=0;
+ if(seconds_ago > 3600*24) snprintf(outbuf, COLWID, "%s", ctime(&start)+4);
+ else snprintf(outbuf, COLWID, "%s", ctime(&start)+10);
+ outbuf[6] = '\0';
+ return 6;
+}
+
+/* HP-UX puts this in pages and uses "vsz" for kB */
+static int pr_sz(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(VM_SIZE)
+ return snprintf(outbuf, COLWID, "%lu", rSv(VM_SIZE, ul_int, pp)/(page_size/1024));
+}
+
+/*
+ * FIXME: trs,drs,tsiz,dsiz,m_trs,m_drs,vm_exe,vm_data,trss
+ * I suspect some/all of those are broken. They seem to have been
+ * inherited by Linux and AIX from early BSD systems. FreeBSD only
+ * retains tsiz. The prefixed versions come from Debian.
+ * Sun and Digital have none of this crap. The code here comes
+ * from an old Linux ps, and might not be correct for ELF executables.
+ *
+ * AIX TRS size of resident-set (real memory) of text
+ * AIX TSIZ size of text (shared-program) image
+ * FreeBSD tsiz text size (in Kbytes)
+ * 4.3BSD NET/2 trss text resident set size (in Kbytes)
+ * 4.3BSD NET/2 tsiz text size (in Kbytes)
+ */
+
+/* kB data size. See drs, tsiz & trs. */
+static int pr_dsiz(char *restrict const outbuf, const proc_t *restrict const pp){
+ long dsiz;
+setREL3(VSIZE_BYTES,ADDR_CODE_END,ADDR_CODE_START)
+ dsiz = 0;
+ if(rSv(VSIZE_BYTES, ul_int, pp)) dsiz += (rSv(VSIZE_BYTES, ul_int, pp) - rSv(ADDR_CODE_END, ul_int, pp) + rSv(ADDR_CODE_START, ul_int, pp)) >> 10;
+ return snprintf(outbuf, COLWID, "%ld", dsiz);
+}
+
+/* kB text (code) size. See trs, dsiz & drs. */
+static int pr_tsiz(char *restrict const outbuf, const proc_t *restrict const pp){
+ long tsiz;
+setREL3(VSIZE_BYTES,ADDR_CODE_END,ADDR_CODE_START)
+ tsiz = 0;
+ if(rSv(VSIZE_BYTES, ul_int, pp)) tsiz += (rSv(ADDR_CODE_END, ul_int, pp) - rSv(ADDR_CODE_START, ul_int, pp)) >> 10;
+ return snprintf(outbuf, COLWID, "%ld", tsiz);
+}
+
+/* kB _resident_ data size. See dsiz, tsiz & trs. */
+static int pr_drs(char *restrict const outbuf, const proc_t *restrict const pp){
+ long drs;
+setREL3(VSIZE_BYTES,ADDR_CODE_END,ADDR_CODE_START)
+ drs = 0;
+ if(rSv(VSIZE_BYTES, ul_int, pp)) drs += (rSv(VSIZE_BYTES, ul_int, pp) - rSv(ADDR_CODE_END, ul_int, pp) + rSv(ADDR_CODE_START, ul_int, pp)) >> 10;
+ return snprintf(outbuf, COLWID, "%ld", drs);
+}
+
+/* kB text _resident_ (code) size. See tsiz, dsiz & drs. */
+static int pr_trs(char *restrict const outbuf, const proc_t *restrict const pp){
+ long trs;
+setREL3(VSIZE_BYTES,ADDR_CODE_END,ADDR_CODE_START)
+ trs = 0;
+ if(rSv(VSIZE_BYTES, ul_int, pp)) trs += (rSv(ADDR_CODE_END, ul_int, pp) - rSv(ADDR_CODE_START, ul_int, pp)) >> 10;
+ return snprintf(outbuf, COLWID, "%ld", trs);
+}
+
+static int pr_swapable(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL3(VM_DATA,VM_STACK,VSIZE_BYTES) // that last enum will approximate sort needs
+ return snprintf(outbuf, COLWID, "%lu", rSv(VM_DATA, ul_int, pp) + rSv(VM_STACK, ul_int, pp));
+}
+
+/* nasty old Debian thing */
+static int pr_size(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(VSIZE_BYTES)
+ return snprintf(outbuf, COLWID, "%lu", rSv(VSIZE_BYTES, ul_int, pp));
+}
+
+static int pr_minflt(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL2(FLT_MIN,FLT_MIN_C)
+ unsigned long flt = rSv(FLT_MIN, ul_int, pp);
+ if(include_dead_children) flt = rSv(FLT_MIN_C, ul_int, pp);
+ return snprintf(outbuf, COLWID, "%lu", flt);
+}
+
+static int pr_majflt(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL2(FLT_MAJ,FLT_MAJ_C)
+ unsigned long flt = rSv(FLT_MAJ, ul_int, pp);
+ if(include_dead_children) flt = rSv(FLT_MAJ_C, ul_int, pp);
+ return snprintf(outbuf, COLWID, "%lu", flt);
+}
+
+static int pr_lim(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(RSS_RLIM)
+ if(rSv(RSS_RLIM, ul_int, pp) == RLIM_INFINITY){
+ outbuf[0] = 'x';
+ outbuf[1] = 'x';
+ outbuf[2] = '\0';
+ return 2;
+ }
+ return snprintf(outbuf, COLWID, "%5lu", rSv(RSS_RLIM, ul_int, pp) >> 10L);
+}
+
+/* should print leading tilde ('~') if process is bound to the CPU */
+static int pr_psr(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(PROCESSOR)
+ return snprintf(outbuf, COLWID, "%d", rSv(PROCESSOR, s_int, pp));
+}
+
+static int pr_pss(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(SMAP_PSS)
+ return snprintf(outbuf, COLWID, "%lu", rSv(SMAP_PSS, ul_int, pp));
+}
+
+static int pr_numa(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(PROCESSOR_NODE)
+ return snprintf(outbuf, COLWID, "%d", rSv(PROCESSOR_NODE, s_int, pp));
+}
+
+static int pr_rss(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(VM_RSS)
+ return snprintf(outbuf, COLWID, "%lu", rSv(VM_RSS, ul_int, pp));
+}
+
+/* pp->vm_rss * 1000 would overflow on 32-bit systems with 64 GB memory */
+static int pr_pmem(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long pmem;
+setREL1(VM_RSS)
+ pmem = 0;
+ pmem = rSv(VM_RSS, ul_int, pp) * 1000ULL / memory_total();
+ if (pmem > 999) pmem = 999;
+ return snprintf(outbuf, COLWID, "%2u.%u", (unsigned)(pmem/10), (unsigned)(pmem%10));
+}
+
+// Format cannot be %c as the length changes depending on locale
+#define DEFAULT_LSTART_FORMAT "%a %b %e %H:%M:%S %Y"
+static int pr_lstart(char *restrict const outbuf, const proc_t *restrict const pp){
+ time_t t;
+ struct tm start_time;
+ size_t len;
+setREL1(TICS_BEGAN)
+ t = boot_time() + rSv(TICS_BEGAN, ull_int, pp) / Hertz;
+ if (localtime_r(&t, &start_time) == NULL)
+ return 0;
+ len = strftime(outbuf, COLWID,
+ (lstart_format?lstart_format:DEFAULT_LSTART_FORMAT), &start_time);
+ if (len <= 0 || len >= COLWID)
+ outbuf[len = 0] = '\0';
+ return len;
+}
+
+/* Unix98 specifies a STIME header for a column that shows the start
+ * time of the process, but does not specify a format or format specifier.
+ * From the general Unix98 rules, we know there must not be any spaces.
+ * Most systems violate that rule, though the Solaris documentation
+ * claims to print the column without spaces. (NOT!)
+ *
+ * So this isn't broken, but could be renamed to u98_std_stime,
+ * as long as it still shows as STIME when using the -f option.
+ */
+static int pr_stime(char *restrict const outbuf, const proc_t *restrict const pp){
+ struct tm proc_time;
+ struct tm our_time;
+ time_t t;
+ const char *fmt;
+ int tm_year;
+ int tm_yday;
+ size_t len;
+setREL1(TICS_BEGAN)
+ if (localtime_r(&seconds_since_1970, &our_time) == NULL)
+ return 0;
+ tm_year = our_time.tm_year;
+ tm_yday = our_time.tm_yday;
+ t = boot_time() + rSv(TICS_BEGAN, ull_int, pp) / Hertz;
+ if (localtime_r(&t, &proc_time) == NULL)
+ return 0;
+ fmt = "%H:%M"; /* 03:02 23:59 */
+ if(tm_yday != proc_time.tm_yday) fmt = "%b%d"; /* Jun06 Aug27 */
+ if(tm_year != proc_time.tm_year) fmt = "%Y"; /* 1991 2001 */
+ len = strftime(outbuf, COLWID, fmt, &proc_time);
+ if(len <= 0 || len >= COLWID) outbuf[len = 0] = '\0';
+ return len;
+}
+
+static int pr_start(char *restrict const outbuf, const proc_t *restrict const pp){
+ time_t t;
+ char *str;
+setREL1(TICS_BEGAN)
+ t = boot_time() + rSv(TICS_BEGAN, ull_int, pp) / Hertz;
+ str = ctime(&t);
+ if(str[8]==' ') str[8]='0';
+ if(str[11]==' ') str[11]='0';
+ if((unsigned long)t+60*60*24 > (unsigned long)seconds_since_1970)
+ return snprintf(outbuf, COLWID, "%8.8s", str+11);
+ return snprintf(outbuf, COLWID, " %6.6s", str+4);
+}
+
+static int help_pr_sig(char *restrict const outbuf, const char *restrict const sig){
+ int ret;
+ const size_t len = strlen(sig);
+
+ if (signal_names) {
+ int rightward;
+ rightward = max_rightward;
+ if ( (ret = print_signame(outbuf, sig, rightward)) > 0)
+ return ret;
+ }
+
+ if(wide_signals){
+ if(len>8) return snprintf(outbuf, COLWID, "%s", sig);
+ return snprintf(outbuf, COLWID, "00000000%s", sig);
+ }
+ if(len-strspn(sig,"0") > 8)
+ return snprintf(outbuf, COLWID, "<%s", sig+len-8);
+ if(len < 8)
+ return snprintf(outbuf, COLWID, "%s%s", "00000000"+len, sig);
+ return snprintf(outbuf, COLWID, "%s", sig+len-8);
+}
+
+// This one is always thread-specific pending. (from Dragonfly BSD)
+static int pr_tsig(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(SIGPENDING)
+ return help_pr_sig(outbuf, rSv(SIGPENDING, str, pp));
+}
+// This one is (wrongly?) thread-specific when printing thread lines,
+// but process-pending otherwise.
+static int pr_sig(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(SIGNALS)
+ return help_pr_sig(outbuf, rSv(SIGNALS, str, pp));
+}
+static int pr_sigmask(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(SIGBLOCKED)
+ return help_pr_sig(outbuf, rSv(SIGBLOCKED, str, pp));
+}
+static int pr_sigignore(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(SIGIGNORE)
+ return help_pr_sig(outbuf, rSv(SIGIGNORE, str, pp));
+}
+static int pr_sigcatch(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(SIGCATCH)
+ return help_pr_sig(outbuf, rSv(SIGCATCH, str, pp));
+}
+
+static int pr_uss(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(SMAP_PRV_TOTAL)
+ return snprintf(outbuf, COLWID, "%lu", rSv(SMAP_PRV_TOTAL, ul_int, pp));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+/*
+ * internal terms: ruid euid suid fuid
+ * kernel vars: uid euid suid fsuid
+ * command args: ruid uid svuid n/a
+ */
+
+static int pr_egid(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(ID_EGID)
+ return snprintf(outbuf, COLWID, "%d", rSv(ID_EGID, u_int, pp));
+}
+static int pr_rgid(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(ID_RGID)
+ return snprintf(outbuf, COLWID, "%d", rSv(ID_RGID, u_int, pp));
+}
+static int pr_sgid(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(ID_SGID)
+ return snprintf(outbuf, COLWID, "%d", rSv(ID_SGID, u_int, pp));
+}
+static int pr_fgid(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(ID_FGID)
+ return snprintf(outbuf, COLWID, "%d", rSv(ID_FGID, u_int, pp));
+}
+
+static int pr_euid(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(ID_EUID)
+ return snprintf(outbuf, COLWID, "%d", rSv(ID_EUID, u_int, pp));
+}
+static int pr_ruid(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(ID_RUID)
+ return snprintf(outbuf, COLWID, "%d", rSv(ID_RUID, u_int, pp));
+}
+static int pr_suid(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(ID_SUID)
+ return snprintf(outbuf, COLWID, "%d", rSv(ID_SUID, u_int, pp));
+}
+static int pr_fuid(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(ID_FUID)
+ return snprintf(outbuf, COLWID, "%d", rSv(ID_FUID, u_int, pp));
+}
+static int pr_luid(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(ID_LOGIN)
+ if(rSv(ID_LOGIN, s_int, pp)==-1) return snprintf(outbuf, COLWID, "-");
+ return snprintf(outbuf, COLWID, "%d", rSv(ID_LOGIN, s_int, pp));
+}
+
+// The Open Group Base Specifications Issue 6 (IEEE Std 1003.1, 2004 Edition)
+// requires that user and group names print as decimal numbers if there is
+// not enough room in the column. However, we will now truncate such names
+// and provide a visual hint of such truncation. Hopefully, this will reduce
+// the volume of bug reports regarding that former 'feature'.
+//
+// The UNIX and POSIX way to change column width is to rename it:
+// ps -o pid,user=CumbersomeUserNames -o comm
+// The easy way is to directly specify the desired width:
+// ps -o pid,user:19,comm
+//
+static int do_pr_name(char *restrict const outbuf, const char *restrict const name, unsigned u){
+ if(!user_is_number){
+ int rightward = OUTBUF_SIZE; /* max cells */
+ int len; /* real cells */
+
+ escape_str(outbuf, name, OUTBUF_SIZE, &rightward);
+ len = OUTBUF_SIZE-rightward;
+
+ if(len <= (int)max_rightward)
+ return len; /* returns number of cells */
+
+ // only use '+' when not on a multi-byte char, else show uid
+ if (max_rightward >= 1 && (unsigned)outbuf[max_rightward-1] < 127) {
+ len = max_rightward-1;
+ outbuf[len++] = '+';
+ outbuf[len] = 0;
+ return len;
+ }
+ }
+ return snprintf(outbuf, COLWID, "%u", u);
+}
+
+static int pr_ruser(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL2(ID_RUSER,ID_RUID)
+ return do_pr_name(outbuf, rSv(ID_RUSER, str, pp), rSv(ID_RUID, u_int, pp));
+}
+static int pr_euser(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL2(ID_EUSER,ID_EUID)
+ return do_pr_name(outbuf, rSv(ID_EUSER, str, pp), rSv(ID_EUID, u_int, pp));
+}
+static int pr_fuser(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL2(ID_FUSER,ID_FUID)
+ return do_pr_name(outbuf, rSv(ID_FUSER, str, pp), rSv(ID_FUID, u_int, pp));
+}
+static int pr_suser(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL2(ID_SUSER,ID_SUID)
+ return do_pr_name(outbuf, rSv(ID_SUSER, str, pp), rSv(ID_SUID, u_int, pp));
+}
+static int pr_egroup(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL2(ID_EGROUP,ID_EGID)
+ return do_pr_name(outbuf, rSv(ID_EGROUP, str, pp), rSv(ID_EGID, u_int, pp));
+}
+static int pr_rgroup(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL2(ID_RGROUP,ID_RGID)
+ return do_pr_name(outbuf, rSv(ID_RGROUP, str, pp), rSv(ID_RGID, u_int, pp));
+}
+static int pr_fgroup(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL2(ID_FGROUP,ID_FGID)
+ return do_pr_name(outbuf, rSv(ID_FGROUP, str, pp), rSv(ID_FGID, u_int, pp));
+}
+static int pr_sgroup(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL2(ID_SGROUP,ID_SGID)
+ return do_pr_name(outbuf, rSv(ID_SGROUP, str, pp), rSv(ID_SGID, u_int, pp));
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+
+// IO stats
+static int pr_rbytes(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(IO_READ_BYTES)
+ return snprintf(outbuf, COLWID, "%lu", rSv(IO_READ_BYTES, ul_int, pp));
+}
+static int pr_rchars(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(IO_READ_CHARS)
+ return snprintf(outbuf, COLWID, "%lu", rSv(IO_READ_CHARS, ul_int, pp));
+}
+static int pr_rops(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(IO_READ_OPS)
+ return snprintf(outbuf, COLWID, "%lu", rSv(IO_READ_OPS, ul_int, pp));
+}
+static int pr_wbytes(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(IO_WRITE_BYTES)
+ return snprintf(outbuf, COLWID, "%lu", rSv(IO_WRITE_BYTES, ul_int, pp));
+}
+static int pr_wcbytes(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(IO_WRITE_CBYTES)
+ return snprintf(outbuf, COLWID, "%lu", rSv(IO_WRITE_CBYTES, ul_int, pp));
+}
+static int pr_wchars(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(IO_WRITE_CHARS)
+ return snprintf(outbuf, COLWID, "%lu", rSv(IO_WRITE_CHARS, ul_int, pp));
+}
+static int pr_wops(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(IO_WRITE_OPS)
+ return snprintf(outbuf, COLWID, "%lu", rSv(IO_WRITE_OPS, ul_int, pp));
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+
+// PID pid, TGID tgid
+static int pr_procs(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(ID_TGID)
+ return snprintf(outbuf, COLWID, "%d", rSv(ID_TGID, s_int, pp));
+}
+// LWP lwp, SPID spid, TID tid
+static int pr_tasks(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(ID_PID)
+ return snprintf(outbuf, COLWID, "%d", rSv(ID_PID, s_int, pp));
+}
+// thcount THCNT
+static int pr_nlwp(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(NLWP)
+ return snprintf(outbuf, COLWID, "%d", rSv(NLWP, s_int, pp));
+}
+
+static int pr_sess(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(ID_SESSION)
+ return snprintf(outbuf, COLWID, "%d", rSv(ID_SESSION, s_int, pp));
+}
+
+static int pr_supgid(char *restrict const outbuf, const proc_t *restrict const pp){
+ int rightward;
+setREL1(SUPGIDS)
+ rightward = max_rightward;
+ escaped_copy(outbuf, rSv(SUPGIDS, str, pp), OUTBUF_SIZE, &rightward);
+ return max_rightward-rightward;
+}
+
+static int pr_supgrp(char *restrict const outbuf, const proc_t *restrict const pp){
+ int rightward;
+setREL1(SUPGROUPS)
+ rightward = max_rightward;
+ escape_str(outbuf, rSv(SUPGROUPS, str, pp), OUTBUF_SIZE, &rightward);
+ return max_rightward-rightward;
+}
+
+static int pr_tpgid(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(ID_TPGID)
+ return snprintf(outbuf, COLWID, "%d", rSv(ID_TPGID, s_int, pp));
+}
+
+/* SGI uses "cpu" to print the processor ID with header "P" */
+static int pr_sgi_p(char *restrict const outbuf, const proc_t *restrict const pp){ /* FIXME */
+setREL2(STATE,PROCESSOR)
+ if(rSv(STATE, s_ch, pp) == 'R') return snprintf(outbuf, COLWID, "%u", rSv(PROCESSOR, u_int, pp));
+ return snprintf(outbuf, COLWID, "*");
+}
+
+/* full path to executable */
+static int pr_exe(char *restrict const outbuf, const proc_t *restrict const pp){
+ int rightward;
+setREL1(EXE)
+ rightward = max_rightward;
+ escape_str(outbuf, rSv(EXE, str, pp), OUTBUF_SIZE, &rightward);
+ return max_rightward-rightward;
+}
+
+/* %cpu utilization over task lifetime, as ##.### format */
+static int pr_utilization(char *restrict const outbuf, const proc_t *restrict const pp){
+double cu;
+setREL1(UTILIZATION)
+ cu = rSv(UTILIZATION, real, pp);
+ /* this check is really just for us (the ps program) since we will be very
+ short lived and the library might reflect 100% or even more utilization */
+ if (cu > 99.0) cu = 99.999;
+ return snprintf(outbuf, COLWID, "%#.3f", cu);
+}
+
+/* %cpu utilization (plus dead children) over task lifetime, as ##.### format */
+static int pr_utilization_c(char *restrict const outbuf, const proc_t *restrict const pp){
+double cu;
+setREL1(UTILIZATION_C)
+ cu = rSv(UTILIZATION_C, real, pp);
+ /* this check is really just for us (the ps program) since we will be very
+ short lived and the library might reflect 100% or even more utilization */
+ if (cu > 99.0) cu = 99.999;
+ return snprintf(outbuf, COLWID, "%#.3f", cu);
+}
+
+/************************* Systemd stuff ********************************/
+static int pr_sd_unit(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(SD_UNIT)
+ return snprintf(outbuf, COLWID, "%s", rSv(SD_UNIT, str, pp));
+}
+
+static int pr_sd_session(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(SD_SESS)
+ return snprintf(outbuf, COLWID, "%s", rSv(SD_SESS, str, pp));
+}
+
+static int pr_sd_ouid(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(SD_OUID)
+ return snprintf(outbuf, COLWID, "%s", rSv(SD_OUID, str, pp));
+}
+
+static int pr_sd_machine(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(SD_MACH)
+ return snprintf(outbuf, COLWID, "%s", rSv(SD_MACH, str, pp));
+}
+
+static int pr_sd_uunit(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(SD_UUNIT)
+ return snprintf(outbuf, COLWID, "%s", rSv(SD_UUNIT, str, pp));
+}
+
+static int pr_sd_seat(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(SD_SEAT)
+ return snprintf(outbuf, COLWID, "%s", rSv(SD_SEAT, str, pp));
+}
+
+static int pr_sd_slice(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(SD_SLICE)
+ return snprintf(outbuf, COLWID, "%s", rSv(SD_SLICE, str, pp));
+}
+/************************ Linux namespaces ******************************/
+
+#define _pr_ns(NAME, ID)\
+static int pr_##NAME(char *restrict const outbuf, const proc_t *restrict const pp) {\
+ setREL1(NS_ ## ID) \
+ if (rSv(NS_ ## ID, ul_int, pp)) \
+ return snprintf(outbuf, COLWID, "%lu", rSv(NS_ ## ID, ul_int, pp)); \
+ else \
+ return snprintf(outbuf, COLWID, "-"); \
+}
+_pr_ns(cgroupns, CGROUP);
+_pr_ns(ipcns, IPC);
+_pr_ns(mntns, MNT);
+_pr_ns(netns, NET);
+_pr_ns(pidns, PID);
+_pr_ns(timens, TIME);
+_pr_ns(userns, USER);
+_pr_ns(utsns, UTS);
+#undef _pr_ns
+
+/************************ Linux containers ******************************/
+static int pr_lxcname(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(LXCNAME)
+ return snprintf(outbuf, COLWID, "%s", rSv(LXCNAME, str, pp));
+}
+
+/****************** FLASK & seLinux security stuff **********************/
+// move the bulk of this to libproc sometime
+// This needs more study, considering:
+// 1. the static linking option (maybe disable this in that case)
+// 2. the -z and -Z option issue
+// 3. width of output
+static int pr_context(char *restrict const outbuf, const proc_t *restrict const pp){
+ static void (*ps_freecon)(char*);
+ static int (*ps_getpidcon)(pid_t pid, char **context);
+#if ENABLE_LIBSELINUX
+ static int (*ps_is_selinux_enabled)(void);
+ static int tried_load;
+#endif
+ static int selinux_enabled;
+ size_t len;
+ char *context;
+setREL1(ID_TGID)
+
+#if ENABLE_LIBSELINUX
+ if(!ps_getpidcon && !tried_load){
+ void *handle = dlopen("libselinux.so.1", RTLD_NOW);
+ if(handle){
+ ps_freecon = dlsym(handle, "freecon");
+ if(dlerror())
+ ps_freecon = 0;
+ dlerror();
+ ps_getpidcon = dlsym(handle, "getpidcon");
+ if(dlerror())
+ ps_getpidcon = 0;
+ ps_is_selinux_enabled = dlsym(handle, "is_selinux_enabled");
+ if(dlerror())
+ ps_is_selinux_enabled = 0;
+ else
+ selinux_enabled = ps_is_selinux_enabled();
+ }
+ tried_load++;
+ }
+#endif
+ if(ps_getpidcon && selinux_enabled && !ps_getpidcon(rSv(ID_TGID, s_int, pp), &context)){
+ size_t max_len = OUTBUF_SIZE-1;
+ len = strlen(context);
+ if(len > max_len) len = max_len;
+ memcpy(outbuf, context, len);
+ if (len >= 1 && outbuf[len-1] == '\n') --len;
+ outbuf[len] = '\0';
+ ps_freecon(context);
+ }else{
+ char filename[48];
+ ssize_t num_read;
+ int fd;
+
+ snprintf(filename, sizeof filename, "/proc/%d/attr/current", rSv(ID_TGID, s_int, pp));
+
+ if ((fd = open(filename, O_RDONLY, 0)) != -1) {
+ num_read = read(fd, outbuf, OUTBUF_SIZE-1);
+ close(fd);
+ if (num_read > 0) {
+ outbuf[num_read] = '\0';
+ len = 0;
+ while(isprint(outbuf[len]))
+ len++;
+ outbuf[len] = '\0';
+ if(len)
+ return len;
+ }
+ }
+ outbuf[0] = '-';
+ outbuf[1] = '\0';
+ len = 1;
+ }
+ return len;
+}
+
+/************************ Linux autogroups ******************************/
+static int pr_agid(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(AUTOGRP_ID)
+ return snprintf(outbuf, COLWID, "%d", rSv(AUTOGRP_ID, s_int, pp));
+}
+static int pr_agnice(char *restrict const outbuf, const proc_t *restrict const pp){
+setREL1(AUTOGRP_NICE)
+ return snprintf(outbuf, COLWID, "%d", rSv(AUTOGRP_NICE, s_int, pp));
+}
+
+////////////////////////////// Test code /////////////////////////////////
+
+// like "args"
+static int pr_t_unlimited(char *restrict const outbuf, const proc_t *restrict const pp){
+ static const char *const vals[] = {"[123456789-12345] <defunct>","ps","123456789-123456"};
+ if (!outbuf) return 0;
+ (void)pp;
+ snprintf(outbuf, max_rightward+1, "%s", vals[lines_to_next_header%3u]);
+ return strlen(outbuf);
+}
+static int pr_t_unlimited2(char *restrict const outbuf, const proc_t *restrict const pp){
+ static const char *const vals[] = {"unlimited", "[123456789-12345] <defunct>","ps","123456789-123456"};
+ if (!outbuf) return 0;
+ (void)pp;
+ snprintf(outbuf, max_rightward+1, "%s", vals[lines_to_next_header%4u]);
+ return strlen(outbuf);
+}
+
+// like "etime"
+static int pr_t_right(char *restrict const outbuf, const proc_t *restrict const pp){
+ static const char *const vals[] = {"999-23:59:59","99-23:59:59","9-23:59:59","59:59"};
+ if (!outbuf) return 0;
+ (void)pp;
+ return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%4u]);
+}
+static int pr_t_right2(char *restrict const outbuf, const proc_t *restrict const pp){
+ static const char *const vals[] = {"999-23:59:59","99-23:59:59","9-23:59:59"};
+ if (!outbuf) return 0;
+ (void)pp;
+ return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%3u]);
+}
+
+// like "tty"
+static int pr_t_left(char *restrict const outbuf, const proc_t *restrict const pp){
+ static const char *const vals[] = {"tty7","pts/9999","iseries/vtty42","ttySMX0","3270/tty4"};
+ if (!outbuf) return 0;
+ (void)pp;
+ return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%5u]);
+}
+static int pr_t_left2(char *restrict const outbuf, const proc_t *restrict const pp){
+ static const char *const vals[] = {"tty7","pts/9999","ttySMX0","3270/tty4"};
+ if (!outbuf) return 0;
+ (void)pp;
+ return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%4u]);
+}
+
+/***************************************************************************/
+/*************************** other stuff ***********************************/
+
+/*
+ * Old header specifications.
+ *
+ * short Up " PID TTY STAT TIME COMMAND"
+ * long l Pp " FLAGS UID PID PPID PRI NI SIZE RSS WCHAN STA TTY TIME COMMAND
+ * user u up "USER PID %CPU %MEM SIZE RSS TTY STAT START TIME COMMAND
+ * jobs j gPp " PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
+ * sig s p " UID PID SIGNAL BLOCKED IGNORED CATCHED STAT TTY TIME COMMAND
+ * vm v r " PID TTY STAT TIME PAGEIN TSIZ DSIZ RSS LIM %MEM COMMAND
+ * m m r " PID TTY MAJFLT MINFLT TRS DRS SIZE SWAP RSS SHRD LIB DT COMMAND
+ * regs X p "NR PID STACK ESP EIP TMOUT ALARM STAT TTY TIME COMMAND
+ */
+
+/*
+ * Unix98 requires that the heading for tty is TT, though XPG4, Digital,
+ * and BSD use TTY. The Unix98 headers are:
+ * args,comm,etime,group,nice,pcpu,pgid
+ * pid,ppid,rgroup,ruser,time,tty,user,vsz
+ *
+ * BSD c: "command" becomes accounting name ("comm" or "ucomm")
+ * BSD n: "user" becomes "uid" and "wchan" becomes "nwchan" (number)
+ */
+
+/* Justification control for flags field. */
+#define USER CF_USER // left if text, right if numeric
+#define LEFT CF_LEFT
+#define RIGHT CF_RIGHT
+#define UNLIMITED CF_UNLIMITED
+#define WCHAN CF_WCHAN // left if text, right if numeric
+#define SIGNAL CF_SIGNAL // right in 9, or 16 if room
+#define PIDMAX CF_PIDMAX
+#define TO CF_PRINT_THREAD_ONLY
+#define PO CF_PRINT_PROCESS_ONLY
+#define ET CF_PRINT_EVERY_TIME
+#define AN CF_PRINT_AS_NEEDED // no idea
+
+
+/* TODO
+ * pull out annoying BSD aliases into another table (to macro table?)
+ * add sorting functions here (to unify names)
+ */
+
+/* temporary hack -- mark new stuff grabbed from Debian ps */
+#define LNx LNX
+
+/* Note: upon conversion to the <pids> API the numerous former sort provisions
+ for otherwise non-printable fields (pr_nop) have been retained. And,
+ since the new library can sort on any item, many previously printable
+ but unsortable fields have now been made sortable. */
+/* there are about 211 listed */
+/* Many of these are placeholders for unsupported options. */
+static const format_struct format_array[] = { /*
+ .spec .head .pr .sr .width .vendor .flags */
+{"%cpu", "%CPU", pr_pcpu, PIDS_UTILIZATION, 4, BSD, ET|RIGHT}, /*pcpu*/
+{"%mem", "%MEM", pr_pmem, PIDS_VM_RSS, 4, BSD, PO|RIGHT}, /*pmem*/
+{"_left", "LLLLLLLL", pr_t_left, PIDS_noop, 8, TST, ET|LEFT},
+{"_left2", "L2L2L2L2", pr_t_left2, PIDS_noop, 8, TST, ET|LEFT},
+{"_right", "RRRRRRRRRRR", pr_t_right, PIDS_noop, 11, TST, ET|RIGHT},
+{"_right2", "R2R2R2R2R2R", pr_t_right2, PIDS_noop, 11, TST, ET|RIGHT},
+{"_unlimited","U", pr_t_unlimited, PIDS_noop, 16, TST, ET|UNLIMITED},
+{"_unlimited2","U2", pr_t_unlimited2, PIDS_noop, 16, TST, ET|UNLIMITED},
+{"acflag", "ACFLG", pr_nop, PIDS_noop, 5, XXX, AN|RIGHT}, /*acflg*/
+{"acflg", "ACFLG", pr_nop, PIDS_noop, 5, BSD, AN|RIGHT}, /*acflag*/
+{"addr", "ADDR", pr_nop, PIDS_noop, 4, XXX, AN|RIGHT},
+{"addr_1", "ADDR", pr_nop, PIDS_noop, 1, LNX, AN|LEFT},
+{"ag_id", "AGID", pr_agid, PIDS_AUTOGRP_ID, 5, LNX, AN|RIGHT},
+{"ag_nice", "AGNI", pr_agnice, PIDS_AUTOGRP_NICE, 4, LNX, AN|RIGHT},
+{"alarm", "ALARM", pr_nop, PIDS_noop, 5, LNX, AN|RIGHT},
+{"argc", "ARGC", pr_nop, PIDS_noop, 4, LNX, PO|RIGHT},
+{"args", "COMMAND", pr_args, PIDS_CMDLINE, 27, U98, PO|UNLIMITED}, /*command*/
+{"atime", "TIME", pr_time, PIDS_TIME_ALL, 8, SOE, ET|RIGHT}, /*cputime*/ /* was 6 wide */
+{"blocked", "BLOCKED", pr_sigmask, PIDS_SIGBLOCKED, 9, BSD, TO|SIGNAL},/*sigmask*/
+{"bnd", "BND", pr_nop, PIDS_noop, 1, AIX, TO|RIGHT},
+{"bsdstart", "START", pr_bsdstart, PIDS_TICS_BEGAN, 6, LNX, ET|RIGHT},
+{"bsdtime", "TIME", pr_bsdtime, PIDS_TICS_ALL, 6, LNX, ET|RIGHT},
+{"c", "C", pr_c, PIDS_UTILIZATION, 2, SUN, ET|RIGHT},
+{"caught", "CAUGHT", pr_sigcatch, PIDS_SIGCATCH, 9, BSD, TO|SIGNAL}, /*sigcatch*/
+{"cgname", "CGNAME", pr_cgname, PIDS_CGNAME, 27, LNX, PO|UNLIMITED},
+{"cgroup", "CGROUP", pr_cgroup, PIDS_CGROUP, 27, LNX, PO|UNLIMITED},
+{"cgroupns", "CGROUPNS",pr_cgroupns, PIDS_NS_CGROUP, 10, LNX, ET|RIGHT},
+{"class", "CLS", pr_class, PIDS_SCHED_CLASS, 3, XXX, TO|LEFT},
+{"cls", "CLS", pr_class, PIDS_SCHED_CLASS, 3, HPU, TO|RIGHT}, /*says HPUX or RT*/
+{"cmaj_flt", "-", pr_nop, PIDS_noop, 1, LNX, AN|RIGHT},
+{"cmd", "CMD", pr_args, PIDS_CMDLINE, 27, DEC, PO|UNLIMITED}, /*ucomm*/
+{"cmin_flt", "-", pr_nop, PIDS_noop, 1, LNX, AN|RIGHT},
+{"cnswap", "-", pr_nop, PIDS_noop, 1, LNX, AN|RIGHT},
+{"comm", "COMMAND", pr_comm, PIDS_CMD, 15, U98, PO|UNLIMITED}, /*ucomm*/
+{"command", "COMMAND", pr_args, PIDS_CMDLINE, 27, XXX, PO|UNLIMITED}, /*args*/
+{"context", "CONTEXT", pr_context, PIDS_ID_TGID, 31, LNX, ET|LEFT},
+{"cp", "CP", pr_cp, PIDS_UTILIZATION, 3, DEC, ET|RIGHT}, /*cpu*/
+{"cpu", "CPU", pr_nop, PIDS_noop, 3, BSD, AN|RIGHT}, /* FIXME ... HP-UX wants this as the CPU number for SMP? */
+{"cpuid", "CPUID", pr_psr, PIDS_PROCESSOR, 5, BSD, TO|RIGHT}, // OpenBSD: 8 wide!
+{"cputime", "TIME", pr_time, PIDS_TIME_ALL, 8, DEC, ET|RIGHT}, /*time*/
+{"cputimes", "TIME", pr_times, PIDS_TIME_ALL, 8, LNX, ET|RIGHT}, /*time*/
+{"ctid", "CTID", pr_nop, PIDS_noop, 5, SUN, ET|RIGHT}, // resource contracts?
+{"cuc", "%CUC", pr_utilization_c, PIDS_UTILIZATION_C, 7, XXX, AN|RIGHT},
+{"cursig", "CURSIG", pr_nop, PIDS_noop, 6, DEC, AN|RIGHT},
+{"cutime", "-", pr_nop, PIDS_TICS_USER_C, 1, LNX, AN|RIGHT},
+{"cuu", "%CUU", pr_utilization, PIDS_UTILIZATION, 6, XXX, AN|RIGHT},
+{"cwd", "CWD", pr_nop, PIDS_noop, 3, LNX, AN|LEFT},
+{"drs", "DRS", pr_drs, PIDS_VSIZE_BYTES, 5, LNX, PO|RIGHT},
+{"dsiz", "DSIZ", pr_dsiz, PIDS_VSIZE_BYTES, 4, LNX, PO|RIGHT},
+{"egid", "EGID", pr_egid, PIDS_ID_EGID, 5, LNX, ET|RIGHT},
+{"egroup", "EGROUP", pr_egroup, PIDS_ID_EGROUP, 8, LNX, ET|USER},
+{"eip", "EIP", pr_eip, PIDS_ADDR_CURR_EIP, (int)(2*sizeof(long)), LNX, TO|RIGHT},
+{"emul", "EMUL", pr_nop, PIDS_noop, 13, BSD, PO|LEFT}, /* "FreeBSD ELF32" and such */
+{"end_code", "E_CODE", pr_nop, PIDS_ADDR_CODE_END, (int)(2*sizeof(long)), LNx, PO|RIGHT}, // sortable, but unprintable ??
+{"environ","ENVIRONMENT",pr_nop, PIDS_noop, 11, LNx, PO|UNLIMITED},
+{"esp", "ESP", pr_esp, PIDS_ADDR_CURR_ESP, (int)(2*sizeof(long)), LNX, TO|RIGHT},
+{"etime", "ELAPSED", pr_etime, PIDS_TIME_ELAPSED, 11, U98, ET|RIGHT}, /* was 7 wide */
+{"etimes", "ELAPSED", pr_etimes, PIDS_TIME_ELAPSED, 7, BSD, ET|RIGHT}, /* FreeBSD */
+{"euid", "EUID", pr_euid, PIDS_ID_EUID, 5, LNX, ET|RIGHT},
+{"euser", "EUSER", pr_euser, PIDS_ID_EUSER, 8, LNX, ET|USER},
+{"exe", "EXE", pr_exe, PIDS_EXE, 27, LNX, PO|UNLIMITED},
+{"f", "F", pr_flag, PIDS_FLAGS, 1, XXX, ET|RIGHT}, /*flags*/
+{"fgid", "FGID", pr_fgid, PIDS_FLAGS, 5, LNX, ET|RIGHT},
+{"fgroup", "FGROUP", pr_fgroup, PIDS_ID_FGROUP, 8, LNX, ET|USER},
+{"flag", "F", pr_flag, PIDS_FLAGS, 1, DEC, ET|RIGHT},
+{"flags", "F", pr_flag, PIDS_FLAGS, 1, BSD, ET|RIGHT}, /*f*/ /* was FLAGS, 8 wide */
+{"fname", "COMMAND", pr_fname, PIDS_CMD, 8, SUN, PO|LEFT},
+{"fsgid", "FSGID", pr_fgid, PIDS_ID_FGID, 5, LNX, ET|RIGHT},
+{"fsgroup", "FSGROUP", pr_fgroup, PIDS_ID_FGROUP, 8, LNX, ET|USER},
+{"fsuid", "FSUID", pr_fuid, PIDS_ID_FUID, 5, LNX, ET|RIGHT},
+{"fsuser", "FSUSER", pr_fuser, PIDS_ID_FUSER, 8, LNX, ET|USER},
+{"fuid", "FUID", pr_fuid, PIDS_ID_FUID, 5, LNX, ET|RIGHT},
+{"fuser", "FUSER", pr_fuser, PIDS_ID_FUSER, 8, LNX, ET|USER},
+{"gid", "GID", pr_egid, PIDS_ID_EGID, 5, SUN, ET|RIGHT},
+{"group", "GROUP", pr_egroup, PIDS_ID_EGROUP, 8, U98, ET|USER},
+{"ignored", "IGNORED", pr_sigignore, PIDS_SIGIGNORE, 9, BSD, TO|SIGNAL},/*sigignore*/
+{"inblk", "INBLK", pr_nop, PIDS_noop, 5, BSD, AN|RIGHT}, /*inblock*/
+{"inblock", "INBLK", pr_nop, PIDS_noop, 5, DEC, AN|RIGHT}, /*inblk*/
+{"intpri", "PRI", pr_opri, PIDS_PRIORITY, 3, HPU, TO|RIGHT},
+{"ipcns", "IPCNS", pr_ipcns, PIDS_NS_IPC, 10, LNX, ET|RIGHT},
+{"jid", "JID", pr_nop, PIDS_noop, 1, SGI, PO|RIGHT},
+{"jobc", "JOBC", pr_nop, PIDS_noop, 4, XXX, AN|RIGHT},
+{"ktrace", "KTRACE", pr_nop, PIDS_noop, 8, BSD, AN|RIGHT},
+{"ktracep", "KTRACEP", pr_nop, PIDS_noop, 8, BSD, AN|RIGHT},
+{"label", "LABEL", pr_context, PIDS_ID_TGID, 31, SGI, ET|LEFT},
+{"lastcpu", "C", pr_psr, PIDS_PROCESSOR, 3, BSD, TO|RIGHT}, // DragonFly
+{"lim", "LIM", pr_lim, PIDS_RSS_RLIM, 5, BSD, AN|RIGHT},
+{"login", "LOGNAME", pr_nop, PIDS_noop, 8, BSD, AN|LEFT}, /*logname*/ /* double check */
+{"logname", "LOGNAME", pr_nop, PIDS_noop, 8, XXX, AN|LEFT}, /*login*/
+{"longtname", "TTY", pr_tty8, PIDS_TTY_NAME, 8, DEC, PO|LEFT},
+{"lsession", "SESSION", pr_sd_session, PIDS_SD_SESS, 11, LNX, ET|LEFT},
+{"lstart", "STARTED", pr_lstart, PIDS_TICS_BEGAN, 24, XXX, ET|RIGHT},
+{"luid", "LUID", pr_luid, PIDS_ID_LOGIN, 5, LNX, ET|RIGHT}, /* login ID */
+{"luser", "LUSER", pr_nop, PIDS_noop, 8, LNX, ET|USER}, /* login USER */
+{"lwp", "LWP", pr_tasks, PIDS_ID_PID, 5, SUN, TO|PIDMAX|RIGHT},
+{"lxc", "LXC", pr_lxcname, PIDS_LXCNAME, 8, LNX, ET|LEFT},
+{"m_drs", "DRS", pr_drs, PIDS_VSIZE_BYTES, 5, LNx, PO|RIGHT},
+{"m_dt", "DT", pr_nop, PIDS_noop, 4, LNx, PO|RIGHT},
+{"m_lrs", "LRS", pr_nop, PIDS_noop, 5, LNx, PO|RIGHT},
+{"m_resident", "RES", pr_nop, PIDS_MEM_RES_PGS, 5, LNx, PO|RIGHT},
+{"m_share", "SHRD", pr_nop, PIDS_MEM_SHR_PGS, 5, LNx, PO|RIGHT},
+{"m_size", "SIZE", pr_size, PIDS_VSIZE_BYTES, 5, LNX, PO|RIGHT},
+{"m_swap", "SWAP", pr_nop, PIDS_noop, 5, LNx, PO|RIGHT},
+{"m_trs", "TRS", pr_trs, PIDS_VSIZE_BYTES, 5, LNx, PO|RIGHT},
+{"machine", "MACHINE", pr_sd_machine, PIDS_SD_MACH, 31, LNX, ET|LEFT},
+{"maj_flt", "MAJFL", pr_majflt, PIDS_FLT_MAJ, 6, LNX, AN|RIGHT},
+{"majflt", "MAJFLT", pr_majflt, PIDS_FLT_MAJ, 6, XXX, AN|RIGHT},
+{"min_flt", "MINFL", pr_minflt, PIDS_FLT_MIN, 6, LNX, AN|RIGHT},
+{"minflt", "MINFLT", pr_minflt, PIDS_FLT_MIN, 6, XXX, AN|RIGHT},
+{"mntns", "MNTNS", pr_mntns, PIDS_NS_MNT, 10, LNX, ET|RIGHT},
+{"msgrcv", "MSGRCV", pr_nop, PIDS_noop, 6, XXX, AN|RIGHT},
+{"msgsnd", "MSGSND", pr_nop, PIDS_noop, 6, XXX, AN|RIGHT},
+{"mwchan", "MWCHAN", pr_nop, PIDS_noop, 6, BSD, TO|WCHAN}, /* mutex (FreeBSD) */
+{"netns", "NETNS", pr_netns, PIDS_NS_NET, 10, LNX, ET|RIGHT},
+{"ni", "NI", pr_nice, PIDS_NICE, 3, BSD, TO|RIGHT}, /*nice*/
+{"nice", "NI", pr_nice, PIDS_NICE, 3, U98, TO|RIGHT}, /*ni*/
+{"nivcsw", "IVCSW", pr_nop, PIDS_noop, 5, XXX, AN|RIGHT},
+{"nlwp", "NLWP", pr_nlwp, PIDS_NLWP, 4, SUN, PO|RIGHT},
+{"nsignals", "NSIGS", pr_nop, PIDS_noop, 5, DEC, AN|RIGHT}, /*nsigs*/
+{"nsigs", "NSIGS", pr_nop, PIDS_noop, 5, BSD, AN|RIGHT}, /*nsignals*/
+{"nswap", "NSWAP", pr_nop, PIDS_noop, 5, XXX, AN|RIGHT},
+{"numa", "NUMA", pr_numa, PIDS_PROCESSOR_NODE, 4, XXX, AN|RIGHT},
+{"nvcsw", "VCSW", pr_nop, PIDS_noop, 5, XXX, AN|RIGHT},
+{"nwchan", "WCHAN", pr_nop, PIDS_noop, 6, XXX, TO|RIGHT},
+{"oom", "OOM", pr_oom, PIDS_OOM_SCORE, 4, XXX, TO|RIGHT},
+{"oomadj", "OOMADJ", pr_oom_adj, PIDS_OOM_ADJ, 5, XXX, TO|RIGHT},
+{"opri", "PRI", pr_opri, PIDS_PRIORITY, 3, SUN, TO|RIGHT},
+{"osz", "SZ", pr_nop, PIDS_noop, 2, SUN, PO|RIGHT},
+{"oublk", "OUBLK", pr_nop, PIDS_noop, 5, BSD, AN|RIGHT}, /*oublock*/
+{"oublock", "OUBLK", pr_nop, PIDS_noop, 5, DEC, AN|RIGHT}, /*oublk*/
+{"ouid", "OWNER", pr_sd_ouid, PIDS_SD_OUID, 5, LNX, ET|LEFT},
+{"p_ru", "P_RU", pr_nop, PIDS_noop, 6, BSD, AN|RIGHT},
+{"paddr", "PADDR", pr_nop, PIDS_noop, 6, BSD, AN|RIGHT},
+{"pagein", "PAGEIN", pr_majflt, PIDS_FLT_MAJ, 6, XXX, AN|RIGHT},
+{"pcpu", "%CPU", pr_pcpu, PIDS_UTILIZATION, 4, U98, ET|RIGHT}, /*%cpu*/
+{"pending", "PENDING", pr_sig, PIDS_SIGNALS, 9, BSD, ET|SIGNAL}, /*sig*/
+{"pgid", "PGID", pr_pgid, PIDS_ID_PGRP, 5, U98, PO|PIDMAX|RIGHT},
+{"pgrp", "PGRP", pr_pgid, PIDS_ID_PGRP, 5, LNX, PO|PIDMAX|RIGHT},
+{"pid", "PID", pr_procs, PIDS_ID_TGID, 5, U98, PO|PIDMAX|RIGHT},
+{"pidns", "PIDNS", pr_pidns, PIDS_NS_PID, 10, LNX, ET|RIGHT},
+{"pmem", "%MEM", pr_pmem, PIDS_VM_RSS, 4, XXX, PO|RIGHT}, /* %mem */
+{"poip", "-", pr_nop, PIDS_noop, 1, BSD, AN|RIGHT},
+{"policy", "POL", pr_class, PIDS_SCHED_CLASS, 3, DEC, TO|LEFT},
+{"ppid", "PPID", pr_ppid, PIDS_ID_PPID, 5, U98, PO|PIDMAX|RIGHT},
+{"pri", "PRI", pr_pri, PIDS_PRIORITY, 3, XXX, TO|RIGHT},
+{"pri_api", "API", pr_pri_api, PIDS_PRIORITY, 3, LNX, TO|RIGHT},
+{"pri_bar", "BAR", pr_pri_bar, PIDS_PRIORITY, 3, LNX, TO|RIGHT},
+{"pri_baz", "BAZ", pr_pri_baz, PIDS_PRIORITY, 3, LNX, TO|RIGHT},
+{"pri_foo", "FOO", pr_pri_foo, PIDS_PRIORITY, 3, LNX, TO|RIGHT},
+{"priority", "PRI", pr_priority, PIDS_PRIORITY, 3, LNX, TO|RIGHT},
+{"prmgrp", "PRMGRP", pr_nop, PIDS_noop, 12, HPU, PO|RIGHT},
+{"prmid", "PRMID", pr_nop, PIDS_noop, 12, HPU, PO|RIGHT},
+{"project", "PROJECT", pr_nop, PIDS_noop, 12, SUN, PO|LEFT}, // see prm* andctid
+{"projid", "PROJID", pr_nop, PIDS_noop, 5, SUN, PO|RIGHT},
+{"pset", "PSET", pr_nop, PIDS_noop, 4, DEC, TO|RIGHT},
+{"psr", "PSR", pr_psr, PIDS_PROCESSOR, 3, DEC, TO|RIGHT},
+{"pss", "PSS", pr_pss, PIDS_SMAP_PSS, 5, XXX, PO|RIGHT},
+{"psxpri", "PPR", pr_nop, PIDS_noop, 3, DEC, TO|RIGHT},
+{"rbytes", "RBYTES", pr_rbytes, PIDS_IO_READ_BYTES, 5, LNX, TO|RIGHT},
+{"rchars", "RCHARS", pr_rchars, PIDS_IO_READ_CHARS, 5, LNX, TO|RIGHT},
+{"re", "RE", pr_nop, PIDS_noop, 3, BSD, AN|RIGHT},
+{"resident", "RES", pr_nop, PIDS_MEM_RES_PGS, 5, LNX, PO|RIGHT},
+{"rgid", "RGID", pr_rgid, PIDS_ID_RGID, 5, XXX, ET|RIGHT},
+{"rgroup", "RGROUP", pr_rgroup, PIDS_ID_RGROUP, 8, U98, ET|USER}, /* was 8 wide */
+{"rlink", "RLINK", pr_nop, PIDS_noop, 8, BSD, AN|RIGHT},
+{"rops", "ROPS", pr_rops, PIDS_IO_READ_OPS, 5, LNX, TO|RIGHT},
+{"rss", "RSS", pr_rss, PIDS_VM_RSS, 5, XXX, PO|RIGHT}, /* was 5 wide */
+{"rssize", "RSS", pr_rss, PIDS_VM_RSS, 5, DEC, PO|RIGHT}, /*rsz*/
+{"rsz", "RSZ", pr_rss, PIDS_VM_RSS, 5, BSD, PO|RIGHT}, /*rssize*/
+{"rtprio", "RTPRIO", pr_rtprio, PIDS_PRIORITY_RT, 6, BSD, TO|RIGHT},
+{"ruid", "RUID", pr_ruid, PIDS_ID_RUID, 5, XXX, ET|RIGHT},
+{"ruser", "RUSER", pr_ruser, PIDS_ID_RUSER, 8, U98, ET|USER},
+{"s", "S", pr_s, PIDS_STATE, 1, SUN, TO|LEFT}, /*stat,state*/
+{"sched", "SCH", pr_sched, PIDS_SCHED_CLASS, 3, AIX, TO|RIGHT},
+{"scnt", "SCNT", pr_nop, PIDS_noop, 4, DEC, AN|RIGHT}, /* man page misspelling of scount? */
+{"scount", "SC", pr_nop, PIDS_noop, 4, AIX, AN|RIGHT}, /* scnt==scount, DEC claims both */
+{"seat", "SEAT", pr_sd_seat, PIDS_SD_SEAT, 11, LNX, ET|LEFT},
+{"sess", "SESS", pr_sess, PIDS_ID_SESSION, 5, XXX, PO|PIDMAX|RIGHT},
+{"session", "SESS", pr_sess, PIDS_ID_SESSION, 5, LNX, PO|PIDMAX|RIGHT},
+{"sgi_p", "P", pr_sgi_p, PIDS_STATE, 1, LNX, TO|RIGHT}, /* "cpu" number */
+{"sgi_rss", "RSS", pr_rss, PIDS_VM_RSS, 4, LNX, PO|LEFT}, /* SZ:RSS */
+{"sgid", "SGID", pr_sgid, PIDS_ID_SGID, 5, LNX, ET|RIGHT},
+{"sgroup", "SGROUP", pr_sgroup, PIDS_ID_SGROUP, 8, LNX, ET|USER},
+{"share", "-", pr_nop, PIDS_noop, 1, LNX, PO|RIGHT},
+{"sid", "SID", pr_sess, PIDS_ID_SESSION, 5, XXX, PO|PIDMAX|RIGHT}, /* Sun & HP */
+{"sig", "PENDING", pr_sig, PIDS_SIGNALS, 9, XXX, ET|SIGNAL}, /*pending -- Dragonfly uses this for whole-proc and "tsig" for thread */
+{"sig_block", "BLOCKED", pr_sigmask, PIDS_SIGBLOCKED, 9, LNX, TO|SIGNAL},
+{"sig_catch", "CATCHED", pr_sigcatch, PIDS_SIGCATCH, 9, LNX, TO|SIGNAL},
+{"sig_ignore", "IGNORED",pr_sigignore, PIDS_SIGIGNORE, 9, LNX, TO|SIGNAL},
+{"sig_pend", "SIGNAL", pr_sig, PIDS_SIGNALS, 9, LNX, ET|SIGNAL},
+{"sigcatch", "CAUGHT", pr_sigcatch, PIDS_SIGCATCH, 9, XXX, TO|SIGNAL}, /*caught*/
+{"sigignore", "IGNORED", pr_sigignore, PIDS_SIGIGNORE, 9, XXX, TO|SIGNAL}, /*ignored*/
+{"sigmask", "BLOCKED", pr_sigmask, PIDS_SIGBLOCKED, 9, XXX, TO|SIGNAL}, /*blocked*/
+{"size", "SIZE", pr_swapable, PIDS_VSIZE_BYTES, 5, SCO, PO|RIGHT},
+{"sl", "SL", pr_nop, PIDS_noop, 3, XXX, AN|RIGHT},
+{"slice", "SLICE", pr_sd_slice, PIDS_SD_SLICE, 31, LNX, ET|LEFT},
+{"spid", "SPID", pr_tasks, PIDS_ID_PID, 5, SGI, TO|PIDMAX|RIGHT},
+{"stackp", "STACKP", pr_stackp, PIDS_ADDR_STACK_START, (int)(2*sizeof(long)), LNX, PO|RIGHT}, /*start_stack*/
+{"start", "STARTED", pr_start, PIDS_TICS_BEGAN, 8, XXX, ET|RIGHT},
+{"start_code", "S_CODE", pr_nop, PIDS_ADDR_CODE_START, (int)(2*sizeof(long)), LNx, PO|RIGHT}, // sortable, but unprintable ??
+{"start_stack", "STACKP",pr_stackp, PIDS_ADDR_STACK_START, (int)(2*sizeof(long)), LNX, PO|RIGHT}, /*stackp*/
+{"start_time", "START", pr_stime, PIDS_TICS_BEGAN, 5, LNx, ET|RIGHT},
+{"stat", "STAT", pr_stat, PIDS_STATE, 4, BSD, TO|LEFT}, /*state,s*/
+{"state", "S", pr_s, PIDS_STATE, 1, XXX, TO|LEFT}, /*stat,s*/ /* was STAT */
+{"status", "STATUS", pr_nop, PIDS_noop, 6, DEC, AN|RIGHT},
+{"stime", "STIME", pr_stime, PIDS_TICS_BEGAN, 5, XXX, ET|RIGHT}, /* was 6 wide */
+{"suid", "SUID", pr_suid, PIDS_ID_SUID, 5, LNx, ET|RIGHT},
+{"supgid", "SUPGID", pr_supgid, PIDS_SUPGIDS, 20, LNX, PO|UNLIMITED},
+{"supgrp", "SUPGRP", pr_supgrp, PIDS_SUPGROUPS, 40, LNX, PO|UNLIMITED},
+{"suser", "SUSER", pr_suser, PIDS_ID_SUSER, 8, LNx, ET|USER},
+{"svgid", "SVGID", pr_sgid, PIDS_ID_SGID, 5, XXX, ET|RIGHT},
+{"svgroup", "SVGROUP", pr_sgroup, PIDS_ID_SGROUP, 8, LNX, ET|USER},
+{"svuid", "SVUID", pr_suid, PIDS_ID_SUID, 5, XXX, ET|RIGHT},
+{"svuser", "SVUSER", pr_suser, PIDS_ID_SUSER, 8, LNX, ET|USER},
+{"systime", "SYSTEM", pr_nop, PIDS_noop, 6, DEC, ET|RIGHT},
+{"sz", "SZ", pr_sz, PIDS_VM_SIZE, 5, HPU, PO|RIGHT},
+{"taskid", "TASKID", pr_nop, PIDS_noop, 5, SUN, TO|PIDMAX|RIGHT}, // is this a thread ID?
+{"tdev", "TDEV", pr_nop, PIDS_noop, 4, XXX, AN|RIGHT},
+{"tgid", "TGID", pr_procs, PIDS_ID_TGID, 5, LNX, PO|PIDMAX|RIGHT},
+{"thcount", "THCNT", pr_nlwp, PIDS_NLWP, 5, AIX, PO|RIGHT},
+{"tid", "TID", pr_tasks, PIDS_ID_PID, 5, AIX, TO|PIDMAX|RIGHT},
+{"time", "TIME", pr_time, PIDS_TIME_ALL, 8, U98, ET|RIGHT}, /*cputime*/ /* was 6 wide */
+{"timens", "TIMENS", pr_timens, PIDS_NS_TIME, 10, LNX, ET|RIGHT},
+{"timeout", "TMOUT", pr_nop, PIDS_noop, 5, LNX, AN|RIGHT}, // 2.0.xx era
+{"times", "TIME", pr_times, PIDS_TIME_ALL, 8, LNX, ET|RIGHT},
+{"tmout", "TMOUT", pr_nop, PIDS_noop, 5, LNX, AN|RIGHT}, // 2.0.xx era
+{"tname", "TTY", pr_tty8, PIDS_TTY_NAME, 8, DEC, PO|LEFT},
+{"tpgid", "TPGID", pr_tpgid, PIDS_ID_TPGID, 5, XXX, PO|PIDMAX|RIGHT},
+{"trs", "TRS", pr_trs, PIDS_VSIZE_BYTES, 4, AIX, PO|RIGHT},
+{"trss", "TRSS", pr_trs, PIDS_VSIZE_BYTES, 4, BSD, PO|RIGHT}, /* 4.3BSD NET/2 */
+{"tsess", "TSESS", pr_nop, PIDS_noop, 5, BSD, PO|PIDMAX|RIGHT},
+{"tsession", "TSESS", pr_nop, PIDS_noop, 5, DEC, PO|PIDMAX|RIGHT},
+{"tsid", "TSID", pr_nop, PIDS_noop, 5, BSD, PO|PIDMAX|RIGHT},
+{"tsig", "PENDING", pr_tsig, PIDS_SIGPENDING, 9, BSD, ET|SIGNAL}, /* Dragonfly used this for thread-specific, and "sig" for whole-proc */
+{"tsiz", "TSIZ", pr_tsiz, PIDS_VSIZE_BYTES, 4, BSD, PO|RIGHT},
+{"tt", "TT", pr_tty8, PIDS_TTY_NAME, 8, BSD, PO|LEFT},
+{"tty", "TT", pr_tty8, PIDS_TTY_NAME, 8, U98, PO|LEFT}, /* Unix98 requires "TT" but has "TTY" too. :-( */ /* was 3 wide */
+{"tty4", "TTY", pr_tty4, PIDS_TTY_NAME, 4, LNX, PO|LEFT},
+{"tty8", "TTY", pr_tty8, PIDS_TTY_NAME, 8, LNX, PO|LEFT},
+{"u_procp", "UPROCP", pr_nop, PIDS_noop, 6, DEC, AN|RIGHT},
+{"ucmd", "CMD", pr_comm, PIDS_CMD, 15, DEC, PO|UNLIMITED}, /*ucomm*/
+{"ucomm", "COMMAND", pr_comm, PIDS_CMD, 15, XXX, PO|UNLIMITED}, /*comm*/
+{"uid", "UID", pr_euid, PIDS_ID_EUID, 5, XXX, ET|RIGHT},
+{"uid_hack", "UID", pr_euser, PIDS_ID_EUSER, 8, XXX, ET|USER},
+{"umask", "UMASK", pr_nop, PIDS_noop, 5, DEC, AN|RIGHT},
+{"uname", "USER", pr_euser, PIDS_ID_EUSER, 8, DEC, ET|USER}, /* man page misspelling of user? */
+{"unit", "UNIT", pr_sd_unit, PIDS_SD_UNIT, 31, LNX, ET|LEFT},
+{"upr", "UPR", pr_nop, PIDS_noop, 3, BSD, TO|RIGHT}, /*usrpri*/
+{"uprocp", "UPROCP", pr_nop, PIDS_noop, 8, BSD, AN|RIGHT},
+{"user", "USER", pr_euser, PIDS_ID_EUSER, 8, U98, ET|USER}, /* BSD n forces this to UID */
+{"userns", "USERNS", pr_userns, PIDS_NS_USER, 10, LNX, ET|RIGHT},
+{"usertime", "USER", pr_nop, PIDS_noop, 4, DEC, ET|RIGHT},
+{"usrpri", "UPR", pr_nop, PIDS_noop, 3, DEC, TO|RIGHT}, /*upr*/
+{"uss", "USS", pr_uss, PIDS_SMAP_PRV_TOTAL, 5, XXX, PO|RIGHT},
+{"util", "C", pr_c, PIDS_UTILIZATION, 2, SGI, ET|RIGHT}, // not sure about "C"
+{"utime", "UTIME", pr_nop, PIDS_TICS_USER, 6, LNx, ET|RIGHT},
+{"utsns", "UTSNS", pr_utsns, PIDS_NS_UTS, 10, LNX, ET|RIGHT},
+{"uunit", "UUNIT", pr_sd_uunit, PIDS_SD_UUNIT, 31, LNX, ET|LEFT},
+{"vm_data", "DATA", pr_nop, PIDS_VM_DATA, 5, LNx, PO|RIGHT},
+{"vm_exe", "EXE", pr_nop, PIDS_VM_EXE, 5, LNx, PO|RIGHT},
+{"vm_lib", "LIB", pr_nop, PIDS_VM_LIB, 5, LNx, PO|RIGHT},
+{"vm_lock", "LCK", pr_nop, PIDS_VM_RSS_LOCKED, 3, LNx, PO|RIGHT},
+{"vm_stack", "STACK", pr_nop, PIDS_VM_STACK, 5, LNx, PO|RIGHT},
+{"vsize", "VSZ", pr_vsz, PIDS_VSIZE_BYTES, 6, DEC, PO|RIGHT}, /*vsz*/
+{"vsz", "VSZ", pr_vsz, PIDS_VM_SIZE, 6, U98, PO|RIGHT}, /*vsize*/
+{"wbytes", "WBYTES", pr_wbytes, PIDS_IO_WRITE_BYTES, 5, LNX, TO|RIGHT},
+{"wcbytes", "WCBYTES", pr_wcbytes, PIDS_IO_WRITE_CBYTES, 5, LNX, TO|RIGHT},
+{"wchan", "WCHAN", pr_wchan, PIDS_WCHAN_NAME, 6, XXX, TO|WCHAN}, /* BSD n forces this to nwchan */ /* was 10 wide */
+{"wchars", "WCHARS", pr_wchars, PIDS_IO_WRITE_CHARS, 5, LNX, TO|RIGHT},
+{"wname", "WCHAN", pr_wchan, PIDS_WCHAN_NAME, 6, SGI, TO|WCHAN}, /* opposite of nwchan */
+{"wops", "WOPS", pr_wops, PIDS_IO_WRITE_OPS, 5, LNX, TO|RIGHT},
+{"xstat", "XSTAT", pr_nop, PIDS_noop, 5, BSD, AN|RIGHT},
+{"zone", "ZONE", pr_context, PIDS_ID_TGID, 31, SUN, ET|LEFT}, // Solaris zone == Linux context?
+{"zoneid", "ZONEID", pr_nop, PIDS_noop, 31, SUN, ET|RIGHT}, // Linux only offers context names
+{"~", "-", pr_nop, PIDS_noop, 1, LNX, AN|RIGHT} /* NULL would ruin alphabetical order */
+};
+
+#undef USER
+#undef LEFT
+#undef RIGHT
+#undef UNLIMITED
+#undef WCHAN
+#undef SIGNAL
+#undef PIDMAX
+#undef PO
+#undef TO
+#undef AN
+#undef ET
+
+static const int format_array_count = sizeof(format_array)/sizeof(format_struct);
+
+
+/****************************** Macro formats *******************************/
+/* First X field may be NR, which is p->start_code>>26 printed with %2ld */
+/* That seems useless though, and Debian already killed it. */
+/* The ones marked "Digital" have the name defined, not just the data. */
+static const macro_struct macro_array[] = {
+{"DFMT", "pid,tname,state,cputime,cmd"}, /* Digital's default */
+{"DefBSD", "pid,tname,stat,bsdtime,args"}, /* Our BSD default */
+{"DefSysV", "pid,tname,time,cmd"}, /* Our SysV default */
+{"END_BSD", "state,tname,cputime,comm"}, /* trailer for O */
+{"END_SYS5", "state,tname,time,command"}, /* trailer for -O */
+{"F5FMT", "uname,pid,ppid,c,start,tname,time,cmd"}, /* Digital -f */
+
+{"FB_", "pid,tt,stat,time,command"}, /* FreeBSD default */
+{"FB_j", "user,pid,ppid,pgid,sess,jobc,stat,tt,time,command"}, /* FreeBSD j */
+{"FB_l", "uid,pid,ppid,cpu,pri,nice,vsz,rss,wchan,stat,tt,time,command"}, /* FreeBSD l */
+{"FB_u", "user,pid,pcpu,pmem,vsz,rss,tt,stat,start,time,command"}, /* FreeBSD u */
+{"FB_v", "pid,stat,time,sl,re,pagein,vsz,rss,lim,tsiz,pcpu,pmem,command"}, /* FreeBSD v */
+
+{"FD_", "pid,tty,time,comm"}, /* Fictional Debian SysV default */
+{"FD_f", "user,pid,ppid,start_time,tty,time,comm"}, /* Fictional Debian -f */
+{"FD_fj", "user,pid,ppid,start_time,tty,time,pgid,sid,comm"}, /* Fictional Debian -jf */
+{"FD_j", "pid,tty,time,pgid,sid,comm"}, /* Fictional Debian -j */
+{"FD_l", "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,comm"}, /* Fictional Debian -l */
+{"FD_lj", "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,pgid,sid,comm"}, /* Fictional Debian -jl */
+
+{"FL5FMT", "f,state,uid,pid,ppid,pcpu,pri,nice,rss,wchan,start,time,command"}, /* Digital -fl */
+
+{"FLASK_context", "pid,context,command"}, /* Flask Linux context, --context */
+
+{"HP_", "pid,tty,time,comm"}, /* HP default */
+{"HP_f", "user,pid,ppid,cpu,stime,tty,time,args"}, /* HP -f */
+{"HP_fl", "flags,state,user,pid,ppid,cpu,intpri,nice,addr,sz,wchan,stime,tty,time,args"}, /* HP -fl */
+{"HP_l", "flags,state,uid,pid,ppid,cpu,intpri,nice,addr,sz,wchan,tty,time,comm"}, /* HP -l */
+
+{"J390", "pid,sid,pgrp,tname,atime,args"}, /* OS/390 -j */
+{"JFMT", "user,pid,ppid,pgid,sess,jobc,state,tname,cputime,command"}, /* Digital j and -j */
+{"L5FMT", "f,state,uid,pid,ppid,c,pri,nice,addr,sz,wchan,tt,time,ucmd"}, /* Digital -l */
+{"LFMT", "uid,pid,ppid,cp,pri,nice,vsz,rss,wchan,state,tname,cputime,command"}, /* Digital l */
+
+{"OL_X", "pid,start_stack,esp,eip,timeout,alarm,stat,tname,bsdtime,args"}, /* Old i386 Linux X */
+{"OL_j", "ppid,pid,pgid,sid,tname,tpgid,stat,uid,bsdtime,args"}, /* Old Linux j */
+{"OL_l", "flags,uid,pid,ppid,priority,nice,vsz,rss,wchan,stat,tname,bsdtime,args"}, /* Old Linux l */
+{"OL_m", "pid,tname,majflt,minflt,m_trs,m_drs,m_size,m_swap,rss,m_share,vm_lib,m_dt,args"}, /* Old Linux m */
+{"OL_s", "uid,pid,pending,sig_block,sig_ignore,caught,stat,tname,bsdtime,args"}, /* Old Linux s */
+{"OL_u", "user,pid,pcpu,pmem,vsz,rss,tname,stat,start_time,bsdtime,args"}, /* Old Linux u */
+{"OL_v", "pid,tname,stat,bsdtime,maj_flt,m_trs,m_drs,rss,pmem,args"}, /* Old Linux v */
+
+{"RD_", "pid,tname,state,bsdtime,comm"}, /* Real Debian default */
+{"RD_f", "uid,pid,ppid,start_time,tname,bsdtime,args"}, /* Real Debian -f */
+{"RD_fj", "uid,pid,ppid,start_time,tname,bsdtime,pgid,sid,args"}, /* Real Debian -jf */
+{"RD_j", "pid,tname,state,bsdtime,pgid,sid,comm"}, /* Real Debian -j */
+{"RD_l", "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,comm"}, /* Real Debian -l */
+{"RD_lj", "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,pgid,sid,comm"}, /* Real Debian -jl */
+
+{"RUSAGE", "minflt,majflt,nswap,inblock,oublock,msgsnd,msgrcv,nsigs,nvcsw,nivcsw"}, /* Digital -o "RUSAGE" */
+{"SCHED", "user,pcpu,pri,usrpri,nice,psxpri,psr,policy,pset"}, /* Digital -o "SCHED" */
+{"SFMT", "uid,pid,cursig,sig,sigmask,sigignore,sigcatch,stat,tname,command"}, /* Digital s */
+
+{"Std_f", "uid_hack,pid,ppid,c,stime,tname,time,cmd"}, /* new -f */
+{"Std_fl", "f,s,uid_hack,pid,ppid,c,opri,ni,addr,sz,wchan,stime,tname,time,cmd"}, /* -fl */
+{"Std_l", "f,s,uid,pid,ppid,c,opri,ni,addr,sz,wchan,tname,time,ucmd"}, /* new -l */
+
+{"THREAD", "user,pcpu,pri,scnt,wchan,usertime,systime"}, /* Digital -o "THREAD" */
+{"UFMT", "uname,pid,pcpu,pmem,vsz,rss,tt,state,start,time,command"}, /* Digital u */
+{"VFMT", "pid,tt,state,time,sl,pagein,vsz,rss,pcpu,pmem,command"}, /* Digital v */
+{"~", "~"} /* NULL would ruin alphabetical order */
+};
+
+static const int macro_array_count = sizeof(macro_array)/sizeof(macro_struct);
+
+
+/*************************** AIX formats ********************/
+/* Convert AIX format codes to normal format specifiers. */
+static const aix_struct aix_array[] = {
+{'C', "pcpu", "%CPU"},
+{'G', "group", "GROUP"},
+{'P', "ppid", "PPID"},
+{'U', "user", "USER"},
+{'a', "args", "COMMAND"},
+{'c', "comm", "COMMAND"},
+{'g', "rgroup", "RGROUP"},
+{'n', "nice", "NI"},
+{'p', "pid", "PID"},
+{'r', "pgid", "PGID"},
+{'t', "etime", "ELAPSED"},
+{'u', "ruser", "RUSER"},
+{'x', "time", "TIME"},
+{'y', "tty", "TTY"},
+{'z', "vsz", "VSZ"},
+{'~', "~", "~"} /* NULL would ruin alphabetical order */
+};
+
+
+/********************* sorting ***************************/
+/* Convert short sorting codes to normal format specifiers. */
+static const shortsort_struct shortsort_array[] = {
+{'C', "pcpu" },
+{'G', "tpgid" },
+{'J', "cstime" },
+/* {'K', "stime" }, */ /* conflict, system vs. start time */
+{'M', "maj_flt" },
+{'N', "cmaj_flt" },
+{'P', "ppid" },
+{'R', "resident" },
+{'S', "share" },
+{'T', "start_time" },
+{'U', "uid" }, /* euid */
+{'c', "cmd" },
+{'f', "flags" },
+{'g', "pgrp" },
+{'j', "cutime" },
+{'k', "utime" },
+{'m', "min_flt" },
+{'n', "cmin_flt" },
+{'o', "session" },
+{'p', "pid" },
+{'r', "rss" },
+{'s', "size" },
+{'t', "tty" },
+{'u', "user" },
+{'v', "vsize" },
+{'y', "priority" }, /* nice */
+{'~', "~" } /* NULL would ruin alphabetical order */
+};
+
+
+/*********** print format_array **********/
+/* called by the parser in another file */
+void print_format_specifiers(void){
+ const format_struct *walk = format_array;
+ while(*(walk->spec) != '~'){
+ if(walk->pr != pr_nop) printf("%-12.12s %-8.8s\n", walk->spec, walk->head);
+ walk++;
+ }
+}
+
+/************ comparison functions for bsearch *************/
+
+static int compare_format_structs(const void *a, const void *b){
+ return strcmp(((const format_struct*)a)->spec,((const format_struct*)b)->spec);
+}
+
+static int compare_macro_structs(const void *a, const void *b){
+ return strcmp(((const macro_struct*)a)->spec,((const macro_struct*)b)->spec);
+}
+
+/******** look up structs as needed by the sort & format parsers ******/
+
+const shortsort_struct *search_shortsort_array(const int findme){
+ const shortsort_struct *walk = shortsort_array;
+ while(walk->desc != '~'){
+ if(walk->desc == findme) return walk;
+ walk++;
+ }
+ return NULL;
+}
+
+const aix_struct *search_aix_array(const int findme){
+ const aix_struct *walk = aix_array;
+ while(walk->desc != '~'){
+ if(walk->desc == findme) return walk;
+ walk++;
+ }
+ return NULL;
+}
+
+const format_struct *search_format_array(const char *findme){
+ format_struct key;
+ key.spec = findme;
+ return bsearch(&key, format_array, format_array_count,
+ sizeof(format_struct), compare_format_structs
+ );
+}
+
+const macro_struct *search_macro_array(const char *findme){
+ macro_struct key;
+ key.spec = findme;
+ return bsearch(&key, macro_array, macro_array_count,
+ sizeof(macro_struct), compare_macro_structs
+ );
+}
+
+static unsigned int active_cols; /* some multiple of screen_cols */
+
+/***** Last chance, avoid needless trunctuation. */
+static void check_header_width(void){
+ format_node *walk = format_list;
+ unsigned int total = 0;
+ int was_normal = 0;
+ unsigned int i = 0;
+ unsigned int sigs = 0;
+ while(walk){
+ switch((walk->flags) & CF_JUST_MASK){
+ default:
+ total += walk->width;
+ total += was_normal;
+ was_normal = 1;
+ break;
+ case CF_SIGNAL:
+ sigs++;
+ if (signal_names) {
+ if (walk->width < SIGNAL_NAME_WIDTH)
+ walk->width = SIGNAL_NAME_WIDTH;
+ walk->flags = CF_UNLIMITED;
+ if (walk->next)
+ total += walk->width;
+ else
+ total += 3;
+ } else {
+ total += walk->width;
+ }
+ total += was_normal;
+ was_normal = 1;
+ break;
+ case CF_UNLIMITED: /* could chop this a bit */
+ if(walk->next) total += walk->width;
+ else total += 3; /* not strlen(walk->name) */
+ total += was_normal;
+ was_normal = 1;
+ break;
+ case 0: /* AIX */
+ total += walk->width;
+ was_normal = 0;
+ break;
+ }
+ walk = walk->next;
+ }
+ for(;;){
+ i++;
+ active_cols = screen_cols * i;
+ if(active_cols>=total) break;
+ if(screen_cols*i >= OUTBUF_SIZE/2) break; /* can't go over */
+ }
+ wide_signals = (total+sigs*7 <= active_cols);
+}
+
+
+/********** show one process (NULL proc prints header) **********/
+
+//#define SPACE_AMOUNT page_size
+#define SPACE_AMOUNT 144
+
+static char *saved_outbuf;
+
+void show_one_proc(const proc_t *restrict const p, const format_node *restrict fmt){
+ /* unknown: maybe set correct & actual to 1, remove +/- 1 below */
+ int correct = 0; /* screen position we should be at */
+ int actual = 0; /* screen position we are at */
+ int amount = 0; /* amount of text that this data is */
+ int leftpad = 0; /* amount of space this column _could_ need */
+ int space = 0; /* amount of space we actually need to print */
+ int dospace = 0; /* previous column determined that we need a space */
+ int legit = 0; /* legitimately stolen extra space */
+ int sz = 0; /* real size of data in outbuffer */
+ int tmpspace = 0;
+ char *restrict const outbuf = saved_outbuf;
+ static int did_stuff = 0; /* have we ever printed anything? */
+
+ if(-1==(long)p){ /* true only once, at the end */
+ if(did_stuff) return;
+ /* have _never_ printed anything, but might need a header */
+ if(!--lines_to_next_header){
+ lines_to_next_header = header_gap;
+ show_one_proc(NULL,fmt);
+ }
+ /* fprintf(stderr, "No processes available.\n"); */ /* legal? */
+ exit(1);
+ }
+ if(p){ /* not header, maybe we should call ourselves for it */
+ if(!--lines_to_next_header){
+ lines_to_next_header = header_gap;
+ show_one_proc(NULL,fmt);
+ }
+ }
+ did_stuff = 1;
+ if(active_cols>(int)OUTBUF_SIZE) fprintf(stderr,_("fix bigness error\n"));
+
+ /* print row start sequence */
+ for(;;){
+ legit = 0;
+ /* set width suggestion which might be ignored */
+// if(likely(fmt->next)) max_rightward = fmt->width;
+// else max_rightward = active_cols-((correct>actual) ? correct : actual);
+
+ if(fmt->next){
+ max_rightward = fmt->width;
+ tmpspace = 0;
+ }else{
+ tmpspace = correct-actual;
+ if (tmpspace<1){
+ tmpspace = dospace;
+ max_rightward = active_cols-actual-tmpspace;
+ }else{
+ max_rightward = active_cols - ( (correct>actual) ? correct : actual );
+ }
+ }
+ if(max_rightward <= 0) max_rightward = 0;
+ else if(max_rightward >= OUTBUF_SIZE) max_rightward = OUTBUF_SIZE-1;
+
+ max_leftward = fmt->width + actual - correct; /* TODO check this */
+ if(max_leftward <= 0) max_leftward = 0;
+ else if(max_leftward >= OUTBUF_SIZE) max_leftward = OUTBUF_SIZE-1;
+
+// fprintf(stderr, "cols: %d, max_rightward: %d, max_leftward: %d, actual: %d, correct: %d\n",
+// active_cols, max_rightward, max_leftward, actual, correct);
+
+ /* prepare data and calculate leftpad */
+ if(p && fmt->pr) amount = (*fmt->pr)(outbuf,p);
+ else amount = snprintf(outbuf, OUTBUF_SIZE, "%s", fmt->name); /* AIX or headers */
+
+ if(amount < 0) outbuf[amount = 0] = '\0';
+ else if(amount >= OUTBUF_SIZE) outbuf[amount = OUTBUF_SIZE-1] = '\0';
+
+ switch((fmt->flags) & CF_JUST_MASK){
+ case 0: /* for AIX, assigned outside this file */
+ leftpad = 0;
+ break;
+ case CF_LEFT: /* bad */
+ leftpad = 0;
+ break;
+ case CF_RIGHT: /* OK */
+ leftpad = fmt->width - amount;
+ if(leftpad < 0) leftpad = 0;
+ break;
+ case CF_SIGNAL:
+ /* if the screen is wide enough, use full 16-character output */
+ if(wide_signals){
+ leftpad = 16 - amount;
+ legit = 7;
+ }else{
+ leftpad = 9 - amount;
+ }
+ if(leftpad < 0) leftpad = 0;
+ break;
+ case CF_USER: /* bad */
+ leftpad = fmt->width - amount;
+ if(leftpad < 0) leftpad = 0;
+ if(!user_is_number) leftpad = 0;
+ break;
+ case CF_WCHAN: /* bad */
+ if(wchan_is_number){
+ leftpad = fmt->width - amount;
+ if(leftpad < 0) leftpad = 0;
+ break;
+ }else{
+ if ((active_cols-actual-tmpspace)<1)
+ outbuf[1] = '\0'; /* oops, we (mostly) lose this column... */
+ leftpad = 0;
+ break;
+ }
+ case CF_UNLIMITED:
+ {
+ if(active_cols-actual-tmpspace < 1)
+ outbuf[1] = '\0'; /* oops, we (mostly) lose this column... */
+ leftpad = 0;
+ break;
+ }
+ default:
+ fprintf(stderr, _("bad alignment code\n"));
+ break;
+ }
+ /* At this point:
+ *
+ * correct from previous column
+ * actual from previous column
+ * amount not needed (garbage due to chopping)
+ * leftpad left padding for this column alone (not make-up or gap)
+ * space not needed (will recalculate now)
+ * dospace if we require space between this and the prior column
+ * legit space we were allowed to steal, and thus did steal
+ */
+ space = correct - actual + leftpad;
+ if(space<1) space=dospace;
+ if(space>SPACE_AMOUNT) space=SPACE_AMOUNT; // only so much available
+
+ /* real size -- don't forget in 'amount' is number of cells */
+ outbuf[OUTBUF_SIZE-1] = '\0';
+ sz = strlen(outbuf);
+
+ /* print data, set x position stuff */
+ if(!fmt->next){
+ /* Last column. Write padding + data + newline all together. */
+ outbuf[sz] = '\n';
+ fwrite(outbuf-space, space+sz+1, 1, stdout);
+ break;
+ }
+ /* Not the last column. Write padding + data together. */
+ fwrite(outbuf-space, space+sz, 1, stdout);
+ actual += space+amount;
+ correct += fmt->width;
+ correct += legit; /* adjust for SIGNAL expansion */
+ if(fmt->pr && fmt->next->pr){ /* neither is AIX filler */
+ correct++;
+ dospace = 1;
+ }else{
+ dospace = 0;
+ }
+ fmt = fmt->next;
+ /* At this point:
+ *
+ * correct screen position we should be at
+ * actual screen position we are at
+ * amount not needed
+ * leftpad not needed
+ * space not needed
+ * dospace if have determined that we need a space next time
+ * legit not needed
+ */
+ }
+}
+
+
+void init_output(void)
+{
+ int outbuf_pages;
+ char *outbuf;
+
+ // add page_size-1 to round up
+ outbuf_pages = (OUTBUF_SIZE+SPACE_AMOUNT+page_size-1)/page_size;
+ outbuf = mmap(
+ 0,
+ page_size * (outbuf_pages+1), // 1 more, for guard page at high addresses
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1,
+ 0);
+
+ if(outbuf == MAP_FAILED)
+ catastrophic_failure(__FILE__, __LINE__, _("please report this bug"));
+
+ memset(outbuf, ' ', SPACE_AMOUNT);
+ if(SPACE_AMOUNT==page_size)
+ mprotect(outbuf, page_size, PROT_READ);
+ mprotect(outbuf + page_size*outbuf_pages, page_size, PROT_NONE); // guard page
+ saved_outbuf = outbuf + SPACE_AMOUNT;
+ // available space: page_size*outbuf_pages-SPACE_AMOUNT
+ seconds_since_1970 = time(NULL);
+
+ check_header_width();
+}