+/*
+ * Copyright 1999-2004 by Albert Cahalan; all rights reserved.
+ *
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program 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 Library General Public License for more details.
+ */
+
+/*
+ * 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>
+#include <fcntl.h>
+#include <grp.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <time.h>
+#include <unistd.h>
+#include <dlfcn.h>
+
+#include "../proc/readproc.h"
+#include "../proc/sysinfo.h"
+#include "../proc/wchan.h"
+#include "../proc/procps.h"
+#include "../proc/devname.h"
+#include "../proc/escape.h"
+#include "common.h"
+
+/* TODO:
+ * Stop assuming system time is local time.
+ */
+
+#define COLWID 240 /* satisfy snprintf, which is faster than sprintf */
+
+static unsigned max_rightward = 0x12345678; /* space for RIGHT stuff */
+static unsigned max_leftward = 0x12345678; /* space for LEFT stuff */
+
+
+
+static int wide_signals; /* true if we have room */
+
+static unsigned long seconds_since_1970;
+static unsigned long time_of_boot;
+static unsigned long page_shift;
+
+
+/*************************************************************************/
+/************ Lots of sort functions, starting with the NOP **************/
+
+static int sr_nop(const proc_t* a, const proc_t* b){
+ (void)a;(void)b; /* shut up gcc */
+ return 0;
+}
+
+#define CMP_STR(NAME) \
+static int sr_ ## NAME(const proc_t* P, const proc_t* Q) { \
+ return strcmp(P->NAME, Q->NAME); \
+}
+
+#define CMP_INT(NAME) \
+static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \
+ if (P->NAME < Q->NAME) return -1; \
+ if (P->NAME > Q->NAME) return 1; \
+ return 0; \
+}
+
+/* fast version, for values which either:
+ * a. differ by no more than 0x7fffffff
+ * b. only need to be grouped same w/ same
+ */
+#define CMP_SMALL(NAME) \
+static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \
+ return (int)(P->NAME) - (int)(Q->NAME); \
+}
+
+CMP_INT(rtprio)
+CMP_SMALL(sched)
+CMP_INT(cutime)
+CMP_INT(cstime)
+CMP_SMALL(priority) /* nice */
+CMP_SMALL(nlwp)
+CMP_SMALL(nice) /* priority */
+CMP_INT(rss) /* resident set size from stat file */ /* vm_rss, resident */
+CMP_INT(alarm)
+CMP_INT(size) /* total pages */ /* vm_size, vsize */
+CMP_INT(resident) /* resident pages */ /* vm_rss, rss */
+CMP_INT(share) /* shared pages */
+CMP_INT(trs) /* executable pages */
+CMP_INT(lrs) /* obsolete "library" pages above 0x60000000 */
+CMP_INT(drs) /* other pages (assumed data?) */
+CMP_INT(dt) /* dirty pages */
+
+CMP_INT(vm_size) /* kB VM */ /* size, vsize */
+CMP_INT(vm_lock) /* kB locked */
+CMP_INT(vm_rss) /* kB rss */ /* rss, resident */
+CMP_INT(vm_data) /* kB "data" == data-stack */
+CMP_INT(vm_stack) /* kB stack */
+CMP_INT(vm_exe) /* kB "exec" == exec-lib */
+CMP_INT(vm_lib) /* kB "libraries" */
+CMP_INT(vsize) /* pages VM */ /* size, vm_size */
+CMP_INT(rss_rlim)
+CMP_SMALL(flags)
+CMP_INT(min_flt)
+CMP_INT(maj_flt)
+CMP_INT(cmin_flt)
+CMP_INT(cmaj_flt)
+CMP_INT(utime)
+CMP_INT(stime) /* Old: sort by systime. New: show start time. Uh oh. */
+CMP_INT(start_code)
+CMP_INT(end_code)
+CMP_INT(start_stack)
+CMP_INT(kstk_esp)
+CMP_INT(kstk_eip)
+CMP_INT(start_time)
+CMP_INT(wchan)
+
+/* CMP_STR(*environ) */
+/* CMP_STR(*cmdline) */
+
+CMP_STR(ruser)
+CMP_STR(euser)
+CMP_STR(suser)
+CMP_STR(fuser)
+CMP_STR(rgroup)
+CMP_STR(egroup)
+CMP_STR(sgroup)
+CMP_STR(fgroup)
+CMP_STR(cmd)
+/* CMP_STR(ttyc) */ /* FIXME -- use strncmp with 8 max */
+
+CMP_INT(ruid)
+CMP_INT(rgid)
+CMP_INT(euid)
+CMP_INT(egid)
+CMP_INT(suid)
+CMP_INT(sgid)
+CMP_INT(fuid)
+CMP_INT(fgid)
+CMP_SMALL(tid)
+CMP_SMALL(tgid)
+CMP_SMALL(ppid)
+CMP_SMALL(pgrp)
+CMP_SMALL(session)
+CMP_INT(tty)
+CMP_SMALL(tpgid)
+
+CMP_SMALL(pcpu)
+
+CMP_SMALL(state)
+
+/* approximation to: kB of address space that could end up in swap */
+static int sr_swapable(const proc_t* P, const proc_t* Q) {
+ unsigned long p_swapable = P->vm_data + P->vm_stack;
+ unsigned long q_swapable = Q->vm_data + Q->vm_stack;
+ if (p_swapable < q_swapable) return -1;
+ if (p_swapable > q_swapable) return 1;
+ return 0;
+}
+
+
+/***************************************************************************/
+/************ 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){
+ (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? */
+static int forest_helper(char *restrict const outbuf){
+ char *p = forest_prefix;
+ char *q = outbuf;
+ int rightward=max_rightward;
+ if(!*p) return 0;
+ /* Arrrgh! somebody defined unix as 1 */
+ if(forest_type == 'u') goto unixy;
+ while(*p){
+ 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 */
+ }
+ if (rightward-4 < 0) {
+ *(q+rightward)='\0';
+ return max_rightward;
+ }
+ q += 4;
+ rightward -= 4;
+ p++;
+ }
+ return q-outbuf; /* gcc likes this here */
+unixy:
+ while(*p){
+ 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 */
+ }
+ if (rightward-2 < 0) {
+ *(q+rightward)='\0';
+ return max_rightward;
+ }
+ 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
+
+/* "command" is the same thing: long unless c */
+static int pr_args(char *restrict const outbuf, const proc_t *restrict const pp){
+ char *endp = outbuf;
+ unsigned flags;
+ int rightward=max_rightward;
+
+ if(forest_prefix){
+ int fh = forest_helper(outbuf);
+ endp += fh;
+ rightward -= fh;
+ }
+ if(bsd_c_option) flags = ESC_DEFUNCT;
+ else flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS;
+ endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, flags);
+
+ if(bsd_e_option && rightward>1){
+ const char **env = (const char**)pp->environ;
+ if(env && *env){
+ *endp++ = ' ';
+ rightward--;
+ endp += escape_strlist(endp, env, OUTBUF_SIZE, &rightward);
+ }
+ }
+ //return endp - outbuf;
+ return max_rightward-rightward;
+}
+
+/* "ucomm" is the same thing: short unless -f */
+static int pr_comm(char *restrict const outbuf, const proc_t *restrict const pp){
+ char *endp = outbuf;
+ unsigned flags;
+ int rightward=max_rightward;
+
+ if(forest_prefix){
+ int fh = forest_helper(outbuf);
+ endp += fh;
+ rightward -= fh;
+ }
+ if(unix_f_option) flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS;
+ else flags = ESC_DEFUNCT;
+ endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, flags);
+
+ if(bsd_e_option && rightward>1){
+ const char **env = (const char**)pp->environ;
+ if(env && *env){
+ *endp++ = ' ';
+ rightward--;
+ endp += escape_strlist(endp, env, OUTBUF_SIZE, &rightward);
+ }
+ }
+ //return endp - outbuf;
+ 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 = outbuf;
+ int rightward = max_rightward;
+
+ if(forest_prefix){
+ int fh = forest_helper(outbuf);
+ endp += fh;
+ rightward -= fh;
+ }
+ if (rightward>8) /* 8=default, but forest maybe feeds more */
+ rightward = 8;
+
+ endp += escape_str(endp, pp->cmd, OUTBUF_SIZE, &rightward);
+ //return endp - outbuf;
+ return max_rightward-rightward;
+}
+
+/* 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 = outbuf;
+ t = seconds_since_boot - (unsigned long)(pp->start_time / Hertz);
+ 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);
+}
+
+/* "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 = 0; /* scaled %cpu, 99 means 99% */
+ unsigned long long seconds; /* seconds of process life */
+ total_time = pp->utime + pp->stime;
+ if(include_dead_children) total_time += (pp->cutime + pp->cstime);
+ seconds = seconds_since_boot - pp->start_time / Hertz;
+ if(seconds) pcpu = (total_time * 100ULL / Hertz) / seconds;
+ 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 = 0; /* scaled %cpu, 999 means 99.9% */
+ unsigned long long seconds; /* seconds of process life */
+ total_time = pp->utime + pp->stime;
+ if(include_dead_children) total_time += (pp->cutime + pp->cstime);
+ seconds = seconds_since_boot - pp->start_time / Hertz;
+ if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
+ 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 = 0; /* scaled %cpu, 999 means 99.9% */
+ unsigned long long seconds; /* seconds of process life */
+ total_time = pp->utime + pp->stime;
+ if(include_dead_children) total_time += (pp->cutime + pp->cstime);
+ seconds = seconds_since_boot - pp->start_time / Hertz ;
+ if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
+ 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){
+ return snprintf(outbuf, COLWID, "%u", pp->pgrp);
+}
+static int pr_pid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%u", pp->tgid);
+}
+static int pr_ppid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%u", pp->ppid);
+}
+
+
+/* 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;
+ t = (pp->utime + pp->stime) / Hertz;
+ 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;
+}
+
+/* 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){
+ return snprintf(outbuf, COLWID, "%lu", pp->vm_size);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+// "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 */
+ return snprintf(outbuf, COLWID, "%ld", pp->priority);
+}
+
+// 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 */
+ return snprintf(outbuf, COLWID, "%ld", 60 + pp->priority);
+}
+
+// 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){
+ return snprintf(outbuf, COLWID, "%ld", pp->priority - 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){
+ return snprintf(outbuf, COLWID, "%ld", pp->priority + 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){
+ return snprintf(outbuf, COLWID, "%ld", pp->priority + 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 */
+ return snprintf(outbuf, COLWID, "%ld", 39 - pp->priority);
+}
+
+// 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){
+ return snprintf(outbuf, COLWID, "%ld", -1 - pp->priority);
+}
+
+static int pr_nice(char *restrict const outbuf, const proc_t *restrict const pp){
+ if(pp->sched!=0 && pp->sched!=-1) return snprintf(outbuf, COLWID, "-");
+ return snprintf(outbuf, COLWID, "%ld", pp->nice);
+}
+
+// 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){
+ switch(pp->sched){
+ 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, "#6"); //
+ 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){
+ if(pp->sched==0 || pp->sched==-1) return snprintf(outbuf, COLWID, "-");
+ return snprintf(outbuf, COLWID, "%ld", pp->rtprio);
+}
+static int pr_sched(char *restrict const outbuf, const proc_t *restrict const pp){
+ if(pp->sched==-1) return snprintf(outbuf, COLWID, "-");
+ return snprintf(outbuf, COLWID, "%ld", pp->sched);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+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;
+ if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1;
+ if(wchan_is_number) return snprintf(outbuf, COLWID, "%x", (unsigned)(pp->wchan) & 0xffffffu);
+ w = lookup_wchan(pp->wchan, pp->XXXID);
+ len = strlen(w);
+ if(len>max_rightward) len=max_rightward;
+ memcpy(outbuf, w, len);
+ outbuf[len] = '\0';
+ return len;
+}
+
+static int pr_wname(char *restrict const outbuf, const proc_t *restrict const pp){
+/* SGI's IRIX always uses a number for "wchan", so "wname" is provided too.
+ *
+ * We use '-' 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;
+ if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1;
+ w = lookup_wchan(pp->wchan, pp->XXXID);
+ len = strlen(w);
+ if(len>max_rightward) len=max_rightward;
+ memcpy(outbuf, w, len);
+ outbuf[len] = '\0';
+ return len;
+}
+
+static int pr_nwchan(char *restrict const outbuf, const proc_t *restrict const pp){
+ if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1;
+ return snprintf(outbuf, COLWID, "%x", (unsigned)(pp->wchan) & 0xffffffu);
+}
+
+/* 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){
+/* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */
+ return dev_to_tty(outbuf, 4, pp->tty, pp->XXXID, ABBREV_DEV|ABBREV_TTY|ABBREV_PTS);
+}
+
+/* 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){
+/* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */
+ return dev_to_tty(outbuf, COLWID, pp->tty, pp->XXXID, ABBREV_DEV);
+}
+
+#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 = 0;
+ outbuf[end++] = pp->state;
+// if(pp->rss==0 && pp->state!='Z') outbuf[end++] = 'W'; // useless "swapped out"
+ if(pp->nice < 0) outbuf[end++] = '<';
+ if(pp->nice > 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(pp->vm_lock) outbuf[end++] = 'L';
+ if(pp->session == pp->tgid) outbuf[end++] = 's'; // session leader
+ if(pp->nlwp > 1) outbuf[end++] = 'l'; // multi-threaded
+ if(pp->pgrp == pp->tpgid) 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){
+ outbuf[0] = pp->state;
+ outbuf[1] = '\0';
+ return 1;
+}
+
+static int pr_flag(char *restrict const outbuf, const proc_t *restrict const pp){
+ /* Unix98 requires octal flags */
+ /* this user-hostile and volatile junk gets 1 character */
+ return snprintf(outbuf, COLWID, "%o", (unsigned)(pp->flags>>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){
+ return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->start_stack));
+}
+
+static int pr_esp(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_esp));
+}
+
+static int pr_eip(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_eip));
+}
+
+/* This function helps print old-style time formats */
+static int old_time_helper(char *dst, unsigned long long t, unsigned long long rel) {
+ if(!t) return snprintf(dst, COLWID, " -");
+ if(t == ~0ULL) return snprintf(dst, COLWID, " xx");
+ if((long long)(t-=rel) < 0) t=0ULL;
+ if(t>9999ULL) return snprintf(dst, COLWID, "%5Lu", t/100ULL);
+ else return snprintf(dst, COLWID, "%2u.%02u", (unsigned)t/100U, (unsigned)t%100U);
+}
+
+static int pr_bsdtime(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long long t;
+ unsigned u;
+ t = pp->utime + pp->stime;
+ if(include_dead_children) t += (pp->cutime + pp->cstime);
+ 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;
+ start = time_of_boot + pp->start_time / Hertz;
+ seconds_ago = seconds_since_1970 - start;
+ if(seconds_ago < 0) seconds_ago=0;
+ if(seconds_ago > 3600*24) strcpy(outbuf, ctime(&start)+4);
+ else strcpy(outbuf, ctime(&start)+10);
+ outbuf[6] = '\0';
+ return 6;
+}
+
+static int pr_alarm(char *restrict const outbuf, const proc_t *restrict const pp){
+ return old_time_helper(outbuf, pp->alarm, 0ULL);
+}
+
+/* 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){
+ return snprintf(outbuf, COLWID, "%lu", (pp->vm_size)/(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 = 0;
+ if(pp->vsize) dsiz += (pp->vsize - pp->end_code + pp->start_code) >> 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 = 0;
+ if(pp->vsize) tsiz += (pp->end_code - pp->start_code) >> 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 = 0;
+ if(pp->vsize) drs += (pp->vsize - pp->end_code + pp->start_code) >> 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 = 0;
+ if(pp->vsize) trs += (pp->end_code - pp->start_code) >> 10;
+ return snprintf(outbuf, COLWID, "%ld", trs);
+}
+
+/* approximation to: kB of address space that could end up in swap */
+static int pr_swapable(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%ld", pp->vm_data + pp->vm_stack);
+}
+
+/* nasty old Debian thing */
+static int pr_size(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%ld", pp->size);
+}
+
+
+static int pr_minflt(char *restrict const outbuf, const proc_t *restrict const pp){
+ long flt = pp->min_flt;
+ if(include_dead_children) flt += pp->cmin_flt;
+ return snprintf(outbuf, COLWID, "%ld", flt);
+}
+
+static int pr_majflt(char *restrict const outbuf, const proc_t *restrict const pp){
+ long flt = pp->maj_flt;
+ if(include_dead_children) flt += pp->cmaj_flt;
+ return snprintf(outbuf, COLWID, "%ld", flt);
+}
+
+static int pr_lim(char *restrict const outbuf, const proc_t *restrict const pp){
+ if(pp->rss_rlim == RLIM_INFINITY){
+ outbuf[0] = 'x';
+ outbuf[1] = 'x';
+ outbuf[2] = '\0';
+ return 2;
+ }
+ return snprintf(outbuf, COLWID, "%5ld", pp->rss_rlim >> 10);
+}
+
+/* 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){
+ return snprintf(outbuf, COLWID, "%d", pp->processor);
+}
+
+static int pr_rss(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%lu", pp->vm_rss);
+}
+
+/* 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 = 0;
+ pmem = pp->vm_rss * 1000ULL / kb_main_total;
+ if (pmem > 999) pmem = 999;
+ return snprintf(outbuf, COLWID, "%2u.%u", (unsigned)(pmem/10), (unsigned)(pmem%10));
+}
+
+static int pr_lstart(char *restrict const outbuf, const proc_t *restrict const pp){
+ time_t t;
+ t = time_of_boot + pp->start_time / Hertz;
+ return snprintf(outbuf, COLWID, "%24.24s", ctime(&t));
+}
+
+/* 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;
+ our_time = localtime(&seconds_since_1970); /* not reentrant */
+ tm_year = our_time->tm_year;
+ tm_yday = our_time->tm_yday;
+ t = time_of_boot + pp->start_time / Hertz;
+ proc_time = localtime(&t); /* not reentrant, this corrupts our_time */
+ 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 */
+ return strftime(outbuf, 42, fmt, proc_time);
+}
+
+static int pr_start(char *restrict const outbuf, const proc_t *restrict const pp){
+ time_t t;
+ char *str;
+ t = time_of_boot + pp->start_time / Hertz;
+ str = ctime(&t);
+ if(str[8]==' ') str[8]='0';
+ if(str[11]==' ') str[11]='0';
+ if((unsigned long)t+60*60*24 > seconds_since_1970)
+ return snprintf(outbuf, COLWID, "%8.8s", str+11);
+ return snprintf(outbuf, COLWID, " %6.6s", str+4);
+}
+
+
+#ifdef SIGNAL_STRING
+static int help_pr_sig(char *restrict const outbuf, const char *restrict const sig){
+ long len = 0;
+ len = strlen(sig);
+ 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);
+ return snprintf(outbuf, COLWID, "%s", sig+len-8);
+}
+#else
+static int help_pr_sig(unsigned long long sig){
+ if(wide_signals) return snprintf(outbuf, COLWID, "%016Lx", sig);
+ if(sig>>32) return snprintf(outbuf, COLWID, "<%08Lx", sig&0xffffffffLL);
+ return snprintf(outbuf, COLWID, "%08Lx", sig&0xffffffffLL);
+}
+#endif
+
+// This one is always thread-specific pending. (from Dragonfly BSD)
+static int pr_tsig(char *restrict const outbuf, const proc_t *restrict const pp){
+ return help_pr_sig(outbuf, pp->_sigpnd);
+}
+// 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){
+ return help_pr_sig(outbuf, pp->signal);
+}
+static int pr_sigmask(char *restrict const outbuf, const proc_t *restrict const pp){
+ return help_pr_sig(outbuf, pp->blocked);
+}
+static int pr_sigignore(char *restrict const outbuf, const proc_t *restrict const pp){
+ return help_pr_sig(outbuf, pp->sigignore);
+}
+static int pr_sigcatch(char *restrict const outbuf, const proc_t *restrict const pp){
+ return help_pr_sig(outbuf, pp->sigcatch);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+/*
+ * 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){
+ return snprintf(outbuf, COLWID, "%d", pp->egid);
+}
+static int pr_rgid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->rgid);
+}
+static int pr_sgid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->sgid);
+}
+static int pr_fgid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->fgid);
+}
+
+static int pr_euid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->euid);
+}
+static int pr_ruid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->ruid);
+}
+static int pr_suid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->suid);
+}
+static int pr_fuid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->fuid);
+}
+
+// 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, so tough luck if you don't like it.
+//
+// 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 */
+ }
+ return snprintf(outbuf, COLWID, "%u", u);
+}
+
+static int pr_ruser(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->ruser, pp->ruid);
+}
+static int pr_euser(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->euser, pp->euid);
+}
+static int pr_fuser(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->fuser, pp->fuid);
+}
+static int pr_suser(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->suser, pp->suid);
+}
+
+static int pr_egroup(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->egroup, pp->egid);
+}
+static int pr_rgroup(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->rgroup, pp->rgid);
+}
+static int pr_fgroup(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->fgroup, pp->fgid);
+}
+static int pr_sgroup(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->sgroup, pp->sgid);
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+
+// TID tid LWP lwp SPID spid
+static int pr_thread(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%u", pp->tid);
+}
+// thcount THCNT
+static int pr_nlwp(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->nlwp);
+}
+
+static int pr_sess(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%u", pp->session);
+}
+static int pr_tpgid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->tpgid);
+}
+
+
+/* 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 */
+ if(pp->state == 'R') return snprintf(outbuf, COLWID, "%d", pp->processor);
+ return snprintf(outbuf, COLWID, "*");
+}
+
+
+/****************** FLASK & seLinux security stuff **********************/
+// move the bulk of this to libproc sometime
+
+static int pr_context(char *restrict const outbuf, const proc_t *restrict const pp){
+ char filename[48];
+ size_t len;
+ ssize_t num_read;
+ int fd;
+
+// wchan file is suitable for testing
+//snprintf(filename, sizeof filename, "/proc/%d/wchan", pp->tgid);
+snprintf(filename, sizeof filename, "/proc/%d/attr/current", pp->tgid);
+
+ fd = open(filename, O_RDONLY, 0);
+ if(likely(fd==-1)) goto fail;
+ num_read = read(fd, outbuf, 666);
+ close(fd);
+ if(unlikely(num_read<=0)) goto fail;
+ outbuf[num_read] = '\0';
+
+ len = 0;
+ while(outbuf[len]>' ' && outbuf[len]<='~') len++;
+ outbuf[len] = '\0';
+ if(len) return len;
+
+fail:
+ outbuf[0] = '-';
+ outbuf[1] = '\0';
+ return 1;
+}
+
+#if 0
+// 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 int (*ps_getpidcon)(pid_t pid, char **context) = 0;
+ static int tried_load = 0;
+ size_t len;
+ char *context;
+
+ if(!ps_getpidcon && !tried_load){
+ void *handle = dlopen("libselinux.so.1", RTLD_NOW);
+ if(handle){
+ dlerror();
+ ps_getpidcon = dlsym(handle, "getpidcon");
+ if(dlerror())
+ ps_getpidcon = 0;
+ }
+ tried_load++;
+ }
+ if(ps_getpidcon && !ps_getpidcon(pp->tgid, &context)){
+ size_t max_len = OUTBUF_SIZE-1;
+ len = strlen(context);
+ if(len > max_len) len = max_len;
+ memcpy(outbuf, context, len);
+ outbuf[len] = '\0';
+ free(context);
+ }else{
+ outbuf[0] = '-';
+ outbuf[1] = '\0';
+ len = 1;
+ }
+ return len;
+}
+#endif
+
+
+////////////////////////////// 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"};
+ (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"};
+ (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"};
+ (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"};
+ (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"};
+ (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"};
+ (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
+
+/* short names to save space */
+#define MEM PROC_FILLMEM /* read statm */
+#define ARG PROC_FILLARG /* read cmdline (cleared if c option) */
+#define COM PROC_FILLCOM /* read cmdline (cleared if not -f option) */
+#define ENV PROC_FILLENV /* read environ */
+#define USR PROC_FILLUSR /* uid_t -> user names */
+#define GRP PROC_FILLGRP /* gid_t -> group names */
+#define WCH PROC_FILLWCHAN /* do WCHAN lookup */
+
+
+/* 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
+
+/* there are about 211 listed */
+
+/* Many of these are placeholders for unsupported options. */
+static const format_struct format_array[] = {
+/* code header print() sort() width need vendor flags */
+{"%cpu", "%CPU", pr_pcpu, sr_pcpu, 4, 0, BSD, ET|RIGHT}, /*pcpu*/
+{"%mem", "%MEM", pr_pmem, sr_nop, 4, 0, BSD, PO|RIGHT}, /*pmem*/
+{"_left", "LLLLLLLL", pr_t_left, sr_nop, 8, 0, TST, ET|LEFT},
+{"_left2", "L2L2L2L2", pr_t_left2, sr_nop, 8, 0, TST, ET|LEFT},
+{"_right", "RRRRRRRRRRR", pr_t_right, sr_nop, 11, 0, TST, ET|RIGHT},
+{"_right2", "R2R2R2R2R2R", pr_t_right2, sr_nop, 11, 0, TST, ET|RIGHT},
+{"_unlimited","U", pr_t_unlimited, sr_nop, 16, 0, TST, ET|UNLIMITED},
+{"_unlimited2","U2", pr_t_unlimited2, sr_nop, 16, 0, TST, ET|UNLIMITED},
+{"acflag", "ACFLG", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT}, /*acflg*/
+{"acflg", "ACFLG", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*acflag*/
+{"addr", "ADDR", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT},
+{"addr_1", "ADDR", pr_nop, sr_nop, 1, 0, LNX, AN|LEFT},
+{"alarm", "ALARM", pr_alarm, sr_alarm, 5, 0, LNX, AN|RIGHT},
+{"argc", "ARGC", pr_nop, sr_nop, 4, 0, LNX, PO|RIGHT},
+{"args", "COMMAND", pr_args, sr_cmd, 27, ARG, U98, PO|UNLIMITED}, /*command*/
+{"atime", "TIME", pr_time, sr_nop, 8, 0, SOE, ET|RIGHT}, /*cputime*/ /* was 6 wide */
+{"blocked", "BLOCKED", pr_sigmask, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigmask*/
+{"bnd", "BND", pr_nop, sr_nop, 1, 0, AIX, TO|RIGHT},
+{"bsdstart", "START", pr_bsdstart, sr_nop, 6, 0, LNX, ET|RIGHT},
+{"bsdtime", "TIME", pr_bsdtime, sr_nop, 6, 0, LNX, ET|RIGHT},
+{"c", "C", pr_c, sr_pcpu, 2, 0, SUN, ET|RIGHT},
+{"caught", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigcatch*/
+{"class", "CLS", pr_class, sr_sched, 3, 0, XXX, TO|LEFT},
+{"cls", "CLS", pr_class, sr_sched, 3, 0, HPU, TO|RIGHT}, /*says HPUX or RT*/
+{"cmaj_flt", "-", pr_nop, sr_cmaj_flt, 1, 0, LNX, AN|RIGHT},
+{"cmd", "CMD", pr_args, sr_cmd, 27, ARG, DEC, PO|UNLIMITED}, /*ucomm*/
+{"cmin_flt", "-", pr_nop, sr_cmin_flt, 1, 0, LNX, AN|RIGHT},
+{"cnswap", "-", pr_nop, sr_nop, 1, 0, LNX, AN|RIGHT},
+{"comm", "COMMAND", pr_comm, sr_cmd, 15, COM, U98, PO|UNLIMITED}, /*ucomm*/
+{"command", "COMMAND", pr_args, sr_cmd, 27, ARG, XXX, PO|UNLIMITED}, /*args*/
+{"context", "CONTEXT", pr_context, sr_nop, 31, 0, LNX, ET|LEFT},
+{"cp", "CP", pr_cp, sr_pcpu, 3, 0, DEC, ET|RIGHT}, /*cpu*/
+{"cpu", "CPU", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT}, /* FIXME ... HP-UX wants this as the CPU number for SMP? */
+{"cpuid", "CPUID", pr_psr, sr_nop, 5, 0, BSD, TO|RIGHT}, // OpenBSD: 8 wide!
+{"cputime", "TIME", pr_time, sr_nop, 8, 0, DEC, ET|RIGHT}, /*time*/
+{"cstime", "-", pr_nop, sr_cstime, 1, 0, LNX, AN|RIGHT},
+{"ctid", "CTID", pr_nop, sr_nop, 5, 0, SUN, ET|RIGHT}, // resource contracts?
+{"cursig", "CURSIG", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT},
+{"cutime", "-", pr_nop, sr_cutime, 1, 0, LNX, AN|RIGHT},
+{"cwd", "CWD", pr_nop, sr_nop, 3, 0, LNX, AN|LEFT},
+{"drs", "DRS", pr_drs, sr_drs, 5, MEM, LNX, PO|RIGHT},
+{"dsiz", "DSIZ", pr_dsiz, sr_nop, 4, 0, LNX, PO|RIGHT},
+{"egid", "EGID", pr_egid, sr_egid, 5, 0, LNX, ET|RIGHT},
+{"egroup", "EGROUP", pr_egroup, sr_egroup, 8, GRP, LNX, ET|USER},
+{"eip", "EIP", pr_eip, sr_kstk_eip, 8, 0, LNX, TO|RIGHT},
+{"emul", "EMUL", pr_nop, sr_nop, 13, 0, BSD, PO|LEFT}, /* "FreeBSD ELF32" and such */
+{"end_code", "E_CODE", pr_nop, sr_end_code, 8, 0, LNx, PO|RIGHT},
+{"environ","ENVIRONMENT",pr_nop, sr_nop, 11, ENV, LNx, PO|UNLIMITED},
+{"esp", "ESP", pr_esp, sr_kstk_esp, 8, 0, LNX, TO|RIGHT},
+{"etime", "ELAPSED", pr_etime, sr_nop, 11, 0, U98, ET|RIGHT}, /* was 7 wide */
+{"euid", "EUID", pr_euid, sr_euid, 5, 0, LNX, ET|RIGHT},
+{"euser", "EUSER", pr_euser, sr_euser, 8, USR, LNX, ET|USER},
+{"f", "F", pr_flag, sr_flags, 1, 0, XXX, ET|RIGHT}, /*flags*/
+{"fgid", "FGID", pr_fgid, sr_fgid, 5, 0, LNX, ET|RIGHT},
+{"fgroup", "FGROUP", pr_fgroup, sr_fgroup, 8, GRP, LNX, ET|USER},
+{"flag", "F", pr_flag, sr_flags, 1, 0, DEC, ET|RIGHT},
+{"flags", "F", pr_flag, sr_flags, 1, 0, BSD, ET|RIGHT}, /*f*/ /* was FLAGS, 8 wide */
+{"fname", "COMMAND", pr_fname, sr_nop, 8, 0, SUN, PO|LEFT},
+{"fsgid", "FSGID", pr_fgid, sr_fgid, 5, 0, LNX, ET|RIGHT},
+{"fsgroup", "FSGROUP", pr_fgroup, sr_fgroup, 8, GRP, LNX, ET|USER},
+{"fsuid", "FSUID", pr_fuid, sr_fuid, 5, 0, LNX, ET|RIGHT},
+{"fsuser", "FSUSER", pr_fuser, sr_fuser, 8, USR, LNX, ET|USER},
+{"fuid", "FUID", pr_fuid, sr_fuid, 5, 0, LNX, ET|RIGHT},
+{"fuser", "FUSER", pr_fuser, sr_fuser, 8, USR, LNX, ET|USER},
+{"gid", "GID", pr_egid, sr_egid, 5, 0, SUN, ET|RIGHT},
+{"group", "GROUP", pr_egroup, sr_egroup, 8, GRP, U98, ET|USER},
+{"ignored", "IGNORED", pr_sigignore,sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigignore*/
+{"inblk", "INBLK", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*inblock*/
+{"inblock", "INBLK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*inblk*/
+{"intpri", "PRI", pr_opri, sr_priority, 3, 0, HPU, TO|RIGHT},
+{"jid", "JID", pr_nop, sr_nop, 1, 0, SGI, PO|RIGHT},
+{"jobc", "JOBC", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT},
+{"ktrace", "KTRACE", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
+{"ktracep", "KTRACEP", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
+{"label", "LABEL", pr_context, sr_nop, 31, 0, SGI, ET|LEFT},
+{"lastcpu", "C", pr_psr, sr_nop, 3, 0, BSD, TO|RIGHT}, // DragonFly
+{"lim", "LIM", pr_lim, sr_rss_rlim, 5, 0, BSD, AN|RIGHT},
+{"login", "LOGNAME", pr_nop, sr_nop, 8, 0, BSD, AN|LEFT}, /*logname*/ /* double check */
+{"logname", "LOGNAME", pr_nop, sr_nop, 8, 0, XXX, AN|LEFT}, /*login*/
+{"longtname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, PO|LEFT},
+{"lstart", "STARTED", pr_lstart, sr_nop, 24, 0, XXX, ET|RIGHT},
+{"luid", "LUID", pr_nop, sr_nop, 5, 0, LNX, ET|RIGHT}, /* login ID */
+{"luser", "LUSER", pr_nop, sr_nop, 8, USR, LNX, ET|USER}, /* login USER */
+{"lwp", "LWP", pr_thread, sr_tid, 5, 0, SUN, TO|PIDMAX|RIGHT},
+{"m_drs", "DRS", pr_drs, sr_drs, 5, MEM, LNx, PO|RIGHT},
+{"m_dt", "DT", pr_nop, sr_dt, 4, MEM, LNx, PO|RIGHT},
+{"m_lrs", "LRS", pr_nop, sr_lrs, 5, MEM, LNx, PO|RIGHT},
+{"m_resident", "RES", pr_nop, sr_resident, 5,MEM, LNx, PO|RIGHT},
+{"m_share", "SHRD", pr_nop, sr_share, 5, MEM, LNx, PO|RIGHT},
+{"m_size", "SIZE", pr_size, sr_size, 5, MEM, LNX, PO|RIGHT},
+{"m_swap", "SWAP", pr_nop, sr_nop, 5, 0, LNx, PO|RIGHT},
+{"m_trs", "TRS", pr_trs, sr_trs, 5, MEM, LNx, PO|RIGHT},
+{"maj_flt", "MAJFL", pr_majflt, sr_maj_flt, 6, 0, LNX, AN|RIGHT},
+{"majflt", "MAJFLT", pr_majflt, sr_maj_flt, 6, 0, XXX, AN|RIGHT},
+{"min_flt", "MINFL", pr_minflt, sr_min_flt, 6, 0, LNX, AN|RIGHT},
+{"minflt", "MINFLT", pr_minflt, sr_min_flt, 6, 0, XXX, AN|RIGHT},
+{"msgrcv", "MSGRCV", pr_nop, sr_nop, 6, 0, XXX, AN|RIGHT},
+{"msgsnd", "MSGSND", pr_nop, sr_nop, 6, 0, XXX, AN|RIGHT},
+{"mwchan", "MWCHAN", pr_nop, sr_nop, 6, WCH, BSD, TO|WCHAN}, /* mutex (FreeBSD) */
+{"ni", "NI", pr_nice, sr_nice, 3, 0, BSD, TO|RIGHT}, /*nice*/
+{"nice", "NI", pr_nice, sr_nice, 3, 0, U98, TO|RIGHT}, /*ni*/
+{"nivcsw", "IVCSW", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT},
+{"nlwp", "NLWP", pr_nlwp, sr_nlwp, 4, 0, SUN, PO|RIGHT},
+{"nsignals", "NSIGS", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*nsigs*/
+{"nsigs", "NSIGS", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*nsignals*/
+{"nswap", "NSWAP", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT},
+{"nvcsw", "VCSW", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT},
+{"nwchan", "WCHAN", pr_nwchan, sr_nop, 6, 0, XXX, TO|RIGHT},
+{"opri", "PRI", pr_opri, sr_priority, 3, 0, SUN, TO|RIGHT},
+{"osz", "SZ", pr_nop, sr_nop, 2, 0, SUN, PO|RIGHT},
+{"oublk", "OUBLK", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*oublock*/
+{"oublock", "OUBLK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*oublk*/
+{"p_ru", "P_RU", pr_nop, sr_nop, 6, 0, BSD, AN|RIGHT},
+{"paddr", "PADDR", pr_nop, sr_nop, 6, 0, BSD, AN|RIGHT},
+{"pagein", "PAGEIN", pr_majflt, sr_maj_flt, 6, 0, XXX, AN|RIGHT},
+{"pcpu", "%CPU", pr_pcpu, sr_pcpu, 4, 0, U98, ET|RIGHT}, /*%cpu*/
+{"pending", "PENDING", pr_sig, sr_nop, 9, 0, BSD, ET|SIGNAL}, /*sig*/
+{"pgid", "PGID", pr_pgid, sr_pgrp, 5, 0, U98, PO|PIDMAX|RIGHT},
+{"pgrp", "PGRP", pr_pgid, sr_pgrp, 5, 0, LNX, PO|PIDMAX|RIGHT},
+{"pid", "PID", pr_pid, sr_tgid, 5, 0, U98, PO|PIDMAX|RIGHT},
+{"pmem", "%MEM", pr_pmem, sr_nop, 4, 0, XXX, PO|RIGHT}, /*%mem*/
+{"poip", "-", pr_nop, sr_nop, 1, 0, BSD, AN|RIGHT},
+{"policy", "POL", pr_class, sr_sched, 3, 0, DEC, TO|LEFT},
+{"ppid", "PPID", pr_ppid, sr_ppid, 5, 0, U98, PO|PIDMAX|RIGHT},
+{"pri", "PRI", pr_pri, sr_nop, 3, 0, XXX, TO|RIGHT},
+{"pri_api", "API", pr_pri_api, sr_nop, 3, 0, LNX, TO|RIGHT},
+{"pri_bar", "BAR", pr_pri_bar, sr_nop, 3, 0, LNX, TO|RIGHT},
+{"pri_baz", "BAZ", pr_pri_baz, sr_nop, 3, 0, LNX, TO|RIGHT},
+{"pri_foo", "FOO", pr_pri_foo, sr_nop, 3, 0, LNX, TO|RIGHT},
+{"priority", "PRI", pr_priority, sr_priority, 3, 0, LNX, TO|RIGHT},
+{"prmgrp", "PRMGRP", pr_nop, sr_nop, 12, 0, HPU, PO|RIGHT},
+{"prmid", "PRMID", pr_nop, sr_nop, 12, 0, HPU, PO|RIGHT},
+{"project", "PROJECT", pr_nop, sr_nop, 12, 0, SUN, PO|LEFT}, // see prm* andctid
+{"projid", "PROJID", pr_nop, sr_nop, 5, 0, SUN, PO|RIGHT},
+{"pset", "PSET", pr_nop, sr_nop, 4, 0, DEC, TO|RIGHT},
+{"psr", "PSR", pr_psr, sr_nop, 3, 0, DEC, TO|RIGHT},
+{"psxpri", "PPR", pr_nop, sr_nop, 3, 0, DEC, TO|RIGHT},
+{"re", "RE", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT},
+{"resident", "RES", pr_nop, sr_resident, 5,MEM, LNX, PO|RIGHT},
+{"rgid", "RGID", pr_rgid, sr_rgid, 5, 0, XXX, ET|RIGHT},
+{"rgroup", "RGROUP", pr_rgroup, sr_rgroup, 8, GRP, U98, ET|USER}, /* was 8 wide */
+{"rlink", "RLINK", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
+{"rss", "RSS", pr_rss, sr_rss, 5, 0, XXX, PO|RIGHT}, /* was 5 wide */
+{"rssize", "RSS", pr_rss, sr_vm_rss, 5, 0, DEC, PO|RIGHT}, /*rsz*/
+{"rsz", "RSZ", pr_rss, sr_vm_rss, 5, 0, BSD, PO|RIGHT}, /*rssize*/
+{"rtprio", "RTPRIO", pr_rtprio, sr_rtprio, 6, 0, BSD, TO|RIGHT},
+{"ruid", "RUID", pr_ruid, sr_ruid, 5, 0, XXX, ET|RIGHT},
+{"ruser", "RUSER", pr_ruser, sr_ruser, 8, USR, U98, ET|USER},
+{"s", "S", pr_s, sr_state, 1, 0, SUN, TO|LEFT}, /*stat,state*/
+{"sched", "SCH", pr_sched, sr_sched, 3, 0, AIX, TO|RIGHT},
+{"scnt", "SCNT", pr_nop, sr_nop, 4, 0, DEC, AN|RIGHT}, /* man page misspelling of scount? */
+{"scount", "SC", pr_nop, sr_nop, 4, 0, AIX, AN|RIGHT}, /* scnt==scount, DEC claims both */
+{"sess", "SESS", pr_sess, sr_session, 5, 0, XXX, PO|PIDMAX|RIGHT},
+{"session", "SESS", pr_sess, sr_session, 5, 0, LNX, PO|PIDMAX|RIGHT},
+{"sgi_p", "P", pr_sgi_p, sr_nop, 1, 0, LNX, TO|RIGHT}, /* "cpu" number */
+{"sgi_rss", "RSS", pr_rss, sr_nop, 4, 0, LNX, PO|LEFT}, /* SZ:RSS */
+{"sgid", "SGID", pr_sgid, sr_sgid, 5, 0, LNX, ET|RIGHT},
+{"sgroup", "SGROUP", pr_sgroup, sr_sgroup, 8, GRP, LNX, ET|USER},
+{"share", "-", pr_nop, sr_share, 1, MEM, LNX, PO|RIGHT},
+{"sid", "SID", pr_sess, sr_session, 5, 0, XXX, PO|PIDMAX|RIGHT}, /* Sun & HP */
+{"sig", "PENDING", pr_sig, sr_nop, 9, 0, XXX, ET|SIGNAL}, /*pending -- Dragonfly uses this for whole-proc and "tsig" for thread */
+{"sig_block", "BLOCKED", pr_sigmask, sr_nop, 9, 0, LNX, TO|SIGNAL},
+{"sig_catch", "CATCHED", pr_sigcatch, sr_nop, 9, 0, LNX, TO|SIGNAL},
+{"sig_ignore", "IGNORED",pr_sigignore, sr_nop, 9, 0, LNX, TO|SIGNAL},
+{"sig_pend", "SIGNAL", pr_sig, sr_nop, 9, 0, LNX, ET|SIGNAL},
+{"sigcatch", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, XXX, TO|SIGNAL}, /*caught*/
+{"sigignore", "IGNORED", pr_sigignore,sr_nop, 9, 0, XXX, TO|SIGNAL}, /*ignored*/
+{"sigmask", "BLOCKED", pr_sigmask, sr_nop, 9, 0, XXX, TO|SIGNAL}, /*blocked*/
+{"size", "SZ", pr_swapable, sr_swapable, 5, 0, SCO, PO|RIGHT},
+{"sl", "SL", pr_nop, sr_nop, 3, 0, XXX, AN|RIGHT},
+{"spid", "SPID", pr_thread, sr_tid, 5, 0, SGI, TO|PIDMAX|RIGHT},
+{"stackp", "STACKP", pr_stackp, sr_start_stack, 8, 0, LNX, PO|RIGHT}, /*start_stack*/
+{"start", "STARTED", pr_start, sr_nop, 8, 0, XXX, ET|RIGHT},
+{"start_code", "S_CODE", pr_nop, sr_start_code, 8, 0, LNx, PO|RIGHT},
+{"start_stack", "STACKP", pr_stackp, sr_start_stack, 8, 0, LNX, PO|RIGHT}, /*stackp*/
+{"start_time", "START", pr_stime, sr_start_time, 5, 0, LNx, ET|RIGHT},
+{"stat", "STAT", pr_stat, sr_state, 4, 0, BSD, TO|LEFT}, /*state,s*/
+{"state", "S", pr_s, sr_state, 1, 0, XXX, TO|LEFT}, /*stat,s*/ /* was STAT */
+{"status", "STATUS", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT},
+{"stime", "STIME", pr_stime, sr_stime, 5, 0, XXX, ET|RIGHT}, /* was 6 wide */
+{"suid", "SUID", pr_suid, sr_suid, 5, 0, LNx, ET|RIGHT},
+{"suser", "SUSER", pr_suser, sr_suser, 8, USR, LNx, ET|USER},
+{"svgid", "SVGID", pr_sgid, sr_sgid, 5, 0, XXX, ET|RIGHT},
+{"svgroup", "SVGROUP", pr_sgroup, sr_sgroup, 8, GRP, LNX, ET|USER},
+{"svuid", "SVUID", pr_suid, sr_suid, 5, 0, XXX, ET|RIGHT},
+{"svuser", "SVUSER", pr_suser, sr_suser, 8, USR, LNX, ET|USER},
+{"systime", "SYSTEM", pr_nop, sr_nop, 6, 0, DEC, ET|RIGHT},
+{"sz", "SZ", pr_sz, sr_nop, 5, 0, HPU, PO|RIGHT},
+{"taskid", "TASKID", pr_nop, sr_nop, 5, 0, SUN, TO|PIDMAX|RIGHT}, // is this a thread ID?
+{"tdev", "TDEV", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT},
+{"thcount", "THCNT", pr_nlwp, sr_nlwp, 5, 0, AIX, PO|RIGHT},
+{"tid", "TID", pr_thread, sr_tid, 5, 0, AIX, TO|PIDMAX|RIGHT},
+{"time", "TIME", pr_time, sr_nop, 8, 0, U98, ET|RIGHT}, /*cputime*/ /* was 6 wide */
+{"timeout", "TMOUT", pr_nop, sr_nop, 5, 0, LNX, AN|RIGHT}, // 2.0.xx era
+{"tmout", "TMOUT", pr_nop, sr_nop, 5, 0, LNX, AN|RIGHT}, // 2.0.xx era
+{"tname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, PO|LEFT},
+{"tpgid", "TPGID", pr_tpgid, sr_tpgid, 5, 0, XXX, PO|PIDMAX|RIGHT},
+{"trs", "TRS", pr_trs, sr_trs, 4, MEM, AIX, PO|RIGHT},
+{"trss", "TRSS", pr_trs, sr_trs, 4, MEM, BSD, PO|RIGHT}, /* 4.3BSD NET/2 */
+{"tsess", "TSESS", pr_nop, sr_nop, 5, 0, BSD, PO|PIDMAX|RIGHT},
+{"tsession", "TSESS", pr_nop, sr_nop, 5, 0, DEC, PO|PIDMAX|RIGHT},
+{"tsid", "TSID", pr_nop, sr_nop, 5, 0, BSD, PO|PIDMAX|RIGHT},
+{"tsig", "PENDING", pr_tsig, sr_nop, 9, 0, BSD, ET|SIGNAL}, /* Dragonfly used this for thread-specific, and "sig" for whole-proc */
+{"tsiz", "TSIZ", pr_tsiz, sr_nop, 4, 0, BSD, PO|RIGHT},
+{"tt", "TT", pr_tty8, sr_tty, 8, 0, BSD, PO|LEFT},
+{"tty", "TT", pr_tty8, sr_tty, 8, 0, U98, PO|LEFT}, /* Unix98 requires "TT" but has "TTY" too. :-( */ /* was 3 wide */
+{"tty4", "TTY", pr_tty4, sr_tty, 4, 0, LNX, PO|LEFT},
+{"tty8", "TTY", pr_tty8, sr_tty, 8, 0, LNX, PO|LEFT},
+{"u_procp", "UPROCP", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT},
+{"ucmd", "CMD", pr_comm, sr_cmd, 15, COM, DEC, PO|UNLIMITED}, /*ucomm*/
+{"ucomm", "COMMAND", pr_comm, sr_cmd, 15, COM, XXX, PO|UNLIMITED}, /*comm*/
+{"uid", "UID", pr_euid, sr_euid, 5, 0, XXX, ET|RIGHT},
+{"uid_hack", "UID", pr_euser, sr_euser, 8, USR, XXX, ET|USER},
+{"umask", "UMASK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT},
+{"uname", "USER", pr_euser, sr_euser, 8, USR, DEC, ET|USER}, /* man page misspelling of user? */
+{"upr", "UPR", pr_nop, sr_nop, 3, 0, BSD, TO|RIGHT}, /*usrpri*/
+{"uprocp", "UPROCP", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
+{"user", "USER", pr_euser, sr_euser, 8, USR, U98, ET|USER}, /* BSD n forces this to UID */
+{"usertime", "USER", pr_nop, sr_nop, 4, 0, DEC, ET|RIGHT},
+{"usrpri", "UPR", pr_nop, sr_nop, 3, 0, DEC, TO|RIGHT}, /*upr*/
+{"util", "C", pr_c, sr_pcpu, 2, 0, SGI, ET|RIGHT}, // not sure about "C"
+{"utime", "UTIME", pr_nop, sr_utime, 6, 0, LNx, ET|RIGHT},
+{"vm_data", "DATA", pr_nop, sr_vm_data, 5, 0, LNx, PO|RIGHT},
+{"vm_exe", "EXE", pr_nop, sr_vm_exe, 5, 0, LNx, PO|RIGHT},
+{"vm_lib", "LIB", pr_nop, sr_vm_lib, 5, 0, LNx, PO|RIGHT},
+{"vm_lock", "LCK", pr_nop, sr_vm_lock, 3, 0, LNx, PO|RIGHT},
+{"vm_stack", "STACK", pr_nop, sr_vm_stack, 5, 0, LNx, PO|RIGHT},
+{"vsize", "VSZ", pr_vsz, sr_vsize, 6, 0, DEC, PO|RIGHT}, /*vsz*/
+{"vsz", "VSZ", pr_vsz, sr_vm_size, 6, 0, U98, PO|RIGHT}, /*vsize*/
+{"wchan", "WCHAN", pr_wchan, sr_wchan, 6, WCH, XXX, TO|WCHAN}, /* BSD n forces this to nwchan */ /* was 10 wide */
+{"wname", "WCHAN", pr_wname, sr_nop, 6, WCH, SGI, TO|WCHAN}, /* opposite of nwchan */
+{"xstat", "XSTAT", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT},
+{"zone", "ZONE", pr_context, sr_nop, 31, 0, SUN, ET|LEFT}, // Solaris zone == Linux context?
+{"zoneid", "ZONEID", pr_nop, sr_nop, 31, 0, SUN, ET|RIGHT},// Linux only offers context names
+{"~", "-", pr_nop, sr_nop, 1, 0, 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 */
+};
+static const int aix_array_count = sizeof(aix_array)/sizeof(aix_struct);
+
+
+/********************* 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 */
+};
+static const int shortsort_array_count = sizeof(shortsort_array)/sizeof(shortsort_struct);
+
+
+/*********** 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++;
+ 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(unlikely(-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(likely(p)){ /* not header, maybe we should call ourselves for it */
+ if(unlikely(!--lines_to_next_header)){
+ lines_to_next_header = header_gap;
+ show_one_proc(NULL,fmt);
+ }
+ }
+ did_stuff = 1;
+ if(unlikely(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(likely(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 );
+ }
+ }
+ max_leftward = fmt->width + actual - correct; /* TODO check this */
+
+// 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(likely(p) && likely(fmt->pr)) amount = (*fmt->pr)(outbuf,p);
+ else amount = strlen(strcpy(outbuf, fmt->name)); /* AIX or headers */
+
+ 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(unlikely(space>SPACE_AMOUNT)) space=SPACE_AMOUNT; // only so much available
+
+ /* real size -- don't forget in 'amount' is number of cells */
+ sz = strlen(outbuf);
+
+ /* print data, set x position stuff */
+ if(unlikely(!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
+ */
+ }
+}
+
+
+#ifdef TESTING
+static void sanity_check(void){
+ format_struct *fs = format_array;
+ while((fs->spec)[0] != '~'){
+ if(strlen(fs->head) > fs->width) printf("%d %s\n",strlen(fs->head),fs->spec);
+ fs++;
+ }
+}
+#endif
+
+
+void init_output(void){
+ int outbuf_pages;
+ char *outbuf;
+
+ switch(page_size){
+ case 65536: page_shift = 16; break;
+ case 32768: page_shift = 15; break;
+ case 16384: page_shift = 14; break;
+ case 8192: page_shift = 13; break;
+ default: fprintf(stderr, "Unknown page size! (assume 4096)\n");
+ case 4096: page_shift = 12; break;
+ case 2048: page_shift = 11; break;
+ case 1024: page_shift = 10; break;
+ }
+
+ // 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
+ );
+ 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); // gaurd page
+ saved_outbuf = outbuf + SPACE_AMOUNT;
+ // available space: page_size*outbuf_pages-SPACE_AMOUNT
+
+ seconds_since_1970 = time(NULL);
+ time_of_boot = seconds_since_1970 - seconds_since_boot;
+
+ meminfo();
+
+ check_header_width();
+}