Updated with Tizen:Base source codes
[external/procps.git] / ps / output.c
1 /*
2  * Copyright 1999-2004 by Albert Cahalan; all rights reserved.
3  *
4  * This file may be used subject to the terms and conditions of the
5  * GNU Library General Public License Version 2, or any later version
6  * at your option, as published by the Free Software Foundation.
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU Library General Public License for more details.
11  */
12
13 /*
14  * This file is really gross, and I know it. I looked into several
15  * alternate ways to deal with the mess, and they were all ugly.
16  *
17  * FreeBSD has a fancy hack using offsets into a struct -- that
18  * saves code but it is _really_ gross. See the PO macro below.
19  *
20  * We could have a second column width for wide output format.
21  * For example, Digital prints the real-time signals.
22  */
23
24
25 /*
26  * Data table idea:
27  *
28  * table 1 maps aix to specifier
29  * table 2 maps shortsort to specifier
30  * table 3 maps macro to specifiers
31  * table 4 maps specifier to title,datatype,offset,vendor,helptext
32  * table 5 maps datatype to justification,width,widewidth,sorting,printing
33  *
34  * Here, "datatype" could be user,uid,u16,pages,deltaT,signals,tty,longtty...
35  * It must be enough to determine printing and sorting.
36  *
37  * After the tables, increase width as needed to fit the header.
38  *
39  * Table 5 could go in a file with the output functions.
40  */
41  
42 #include <ctype.h>
43 #include <fcntl.h>
44 #include <grp.h>
45 #include <limits.h>
46 #include <pwd.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <sys/ioctl.h>
51 #include <sys/resource.h>
52 #include <sys/types.h>
53 #include <sys/mman.h>
54 #include <time.h>
55 #include <unistd.h>
56 #include <dlfcn.h>
57
58 #include "../proc/readproc.h"
59 #include "../proc/sysinfo.h"
60 #include "../proc/wchan.h"
61 #include "../proc/procps.h"
62 #include "../proc/devname.h"
63 #include "../proc/escape.h"
64 #include "common.h"
65
66 /* TODO:
67  * Stop assuming system time is local time.
68  */
69
70 #define COLWID 240 /* satisfy snprintf, which is faster than sprintf */
71
72 static unsigned max_rightward = 0x12345678; /* space for RIGHT stuff */
73 static unsigned max_leftward = 0x12345678; /* space for LEFT stuff */
74
75
76
77 static int wide_signals;  /* true if we have room */
78
79 static unsigned long seconds_since_1970;
80 static unsigned long time_of_boot;
81 static unsigned long page_shift;
82
83
84 /*************************************************************************/
85 /************ Lots of sort functions, starting with the NOP **************/
86
87 static int sr_nop(const proc_t* a, const proc_t* b){
88   (void)a;(void)b; /* shut up gcc */
89   return 0;
90 }
91
92 #define CMP_STR(NAME) \
93 static int sr_ ## NAME(const proc_t* P, const proc_t* Q) { \
94     return strcmp(P->NAME, Q->NAME); \
95 }
96
97 #define CMP_INT(NAME) \
98 static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \
99     if (P->NAME < Q->NAME) return -1; \
100     if (P->NAME > Q->NAME) return  1; \
101     return 0; \
102 }
103
104 /* fast version, for values which either:
105  * a. differ by no more than 0x7fffffff
106  * b. only need to be grouped same w/ same
107  */
108 #define CMP_SMALL(NAME) \
109 static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \
110     return (int)(P->NAME) - (int)(Q->NAME); \
111 }
112
113 CMP_INT(rtprio)
114 CMP_SMALL(sched)
115 CMP_INT(cutime)
116 CMP_INT(cstime)
117 CMP_SMALL(priority)                                             /* nice */
118 CMP_SMALL(nlwp)
119 CMP_SMALL(nice)                                                 /* priority */
120 CMP_INT(rss)      /* resident set size from stat file */ /* vm_rss, resident */
121 CMP_INT(alarm)
122 CMP_INT(size)      /* total pages */                     /* vm_size, vsize */
123 CMP_INT(resident)  /* resident pages */                     /* vm_rss, rss */
124 CMP_INT(share)     /* shared pages */
125 CMP_INT(trs)       /* executable pages */
126 CMP_INT(lrs)       /* obsolete "library" pages above 0x60000000 */
127 CMP_INT(drs)       /* other pages (assumed data?) */
128 CMP_INT(dt)        /* dirty pages */
129
130 CMP_INT(vm_size)    /* kB VM */                             /* size, vsize */
131 CMP_INT(vm_lock)    /* kB locked */
132 CMP_INT(vm_rss)     /* kB rss */                          /* rss, resident */
133 CMP_INT(vm_data)    /* kB "data" == data-stack */
134 CMP_INT(vm_stack)   /* kB stack */
135 CMP_INT(vm_exe)     /* kB "exec" == exec-lib */
136 CMP_INT(vm_lib)     /* kB "libraries" */
137 CMP_INT(vsize)      /* pages VM */                        /* size, vm_size */
138 CMP_INT(rss_rlim)
139 CMP_SMALL(flags)
140 CMP_INT(min_flt)
141 CMP_INT(maj_flt)
142 CMP_INT(cmin_flt)
143 CMP_INT(cmaj_flt)
144 CMP_INT(utime)
145 CMP_INT(stime)    /* Old: sort by systime. New: show start time. Uh oh. */
146 CMP_INT(start_code)
147 CMP_INT(end_code)
148 CMP_INT(start_stack)
149 CMP_INT(kstk_esp)
150 CMP_INT(kstk_eip)
151 CMP_INT(start_time)
152 CMP_INT(wchan)
153
154 /* CMP_STR(*environ) */
155 /* CMP_STR(*cmdline) */
156
157 CMP_STR(ruser)
158 CMP_STR(euser)
159 CMP_STR(suser)
160 CMP_STR(fuser)
161 CMP_STR(rgroup)
162 CMP_STR(egroup)
163 CMP_STR(sgroup)
164 CMP_STR(fgroup)
165 CMP_STR(cmd)
166 /* CMP_STR(ttyc) */    /* FIXME -- use strncmp with 8 max */
167
168 CMP_INT(ruid)
169 CMP_INT(rgid)
170 CMP_INT(euid)
171 CMP_INT(egid)
172 CMP_INT(suid)
173 CMP_INT(sgid)
174 CMP_INT(fuid)
175 CMP_INT(fgid)
176 CMP_SMALL(tid)
177 CMP_SMALL(tgid)
178 CMP_SMALL(ppid)
179 CMP_SMALL(pgrp)
180 CMP_SMALL(session)
181 CMP_INT(tty)
182 CMP_SMALL(tpgid)
183
184 CMP_SMALL(pcpu)
185
186 CMP_SMALL(state)
187
188 /* approximation to: kB of address space that could end up in swap */
189 static int sr_swapable(const proc_t* P, const proc_t* Q) {
190   unsigned long p_swapable = P->vm_data + P->vm_stack;
191   unsigned long q_swapable = Q->vm_data + Q->vm_stack;
192   if (p_swapable < q_swapable) return -1;
193   if (p_swapable > q_swapable) return  1;
194   return 0;
195 }
196
197
198 /***************************************************************************/
199 /************ Lots of format functions, starting with the NOP **************/
200
201 // so popular it can't be "static"
202 int pr_nop(char *restrict const outbuf, const proc_t *restrict const pp){
203   (void)pp;
204   return snprintf(outbuf, COLWID, "%c", '-');
205 }
206
207
208 /********* Unix 98 ************/
209
210 /***
211
212 Only comm and args are allowed to contain blank characters; all others are
213 not. Any implementation-dependent variables will be specified in the system
214 documentation along with the default header and indicating if the field
215 may contain blank characters.
216
217 Some headers do not have a standardized specifier!
218
219 %CPU    pcpu    The % of cpu time used recently, with unspecified "recently".
220 ADDR            The address of the process.
221 C               Processor utilisation for scheduling.
222 CMD             The command name, or everything with -f.
223 COMMAND args    Command + args. May chop as desired. May use either version.
224 COMMAND comm    argv[0]
225 ELAPSED etime   Elapsed time since the process was started. [[dd-]hh:]mm:ss
226 F               Flags (octal and additive)
227 GROUP   group   Effective group ID, prefer text over decimal.
228 NI      nice    Decimal system scheduling priority, see nice(1).
229 PGID    pgid    The decimal value of the process group ID.
230 PID     pid     Decimal PID.
231 PPID    ppid    Decimal PID.
232 PRI             Priority. Higher numbers mean lower priority.
233 RGROUP  rgroup  Real group ID, prefer text over decimal.
234 RUSER   ruser   Real user ID, prefer text over decimal.
235 S               The state of the process.
236 STIME           Starting time of the process.
237 SZ              The size in blocks of the core image of the process.
238 TIME    time    Cumulative CPU time. [dd-]hh:mm:ss
239 TT      tty     Name of tty in format used by who(1).
240 TTY             The controlling terminal for the process.
241 UID             UID, or name when -f
242 USER    user    Effective user ID, prefer text over decimal.
243 VSZ     vsz     Virtual memory size in decimal kB.
244 WCHAN           Where waiting/sleeping or blank if running.
245
246 The nice value is used to compute the priority.
247
248 For some undefined ones, Digital does:
249
250 F       flag    Process flags -- but in hex!
251 PRI     pri     Process priority
252 S       state   Symbolic process status
253 TTY     tt,tty,tname,longtname  -- all do "ttyp1", "console", "??"
254 UID     uid     Process user ID (effective UID)
255 WCHAN   wchan   Address of event on which a
256
257 For some undefined ones, Sun does:
258
259 ADDR    addr    memory address of the process
260 C       c       Processor utilization  for  scheduling  (obsolete).
261 CMD
262 F       f
263 S       s       state: OSRZT
264 STIME           start time, printed w/o blanks. If 24h old, months & days
265 SZ              size (in pages) of the swappable process's image in main memory
266 TTY
267 UID     uid
268 WCHAN   wchan
269
270 For some undefined ones, SCO does:
271 ADDR    addr    Virtual address of the process' entry in the process table.
272 SZ              swappable size in kB of the virtual data and stack
273 STIME   stime   hms or md time format
274 ***/
275
276 /* Source & destination are known. Return bytes or screen characters? */
277 static int forest_helper(char *restrict const outbuf){
278   char *p = forest_prefix;
279   char *q = outbuf;
280   int rightward=max_rightward;
281   if(!*p) return 0;
282   /* Arrrgh! somebody defined unix as 1 */
283   if(forest_type == 'u') goto unixy;
284   while(*p){
285     switch(*p){
286     case ' ': strcpy(q, "    ");  break;
287     case 'L': strcpy(q, " \\_ "); break;
288     case '+': strcpy(q, " \\_ "); break;
289     case '|': strcpy(q, " |  ");  break;
290     case '\0': return q-outbuf;    /* redundant & not used */
291     }
292     if (rightward-4 < 0) {
293       *(q+rightward)='\0';
294       return max_rightward;
295     }
296     q += 4;
297     rightward -= 4;
298     p++;
299   }
300   return q-outbuf;   /* gcc likes this here */
301 unixy:
302   while(*p){
303     switch(*p){
304     case ' ': strcpy(q, "  "); break;
305     case 'L': strcpy(q, "  "); break;
306     case '+': strcpy(q, "  "); break;
307     case '|': strcpy(q, "  "); break;
308     case '\0': return q-outbuf;    /* redundant & not used */
309     }
310     if (rightward-2 < 0) {
311       *(q+rightward)='\0';
312       return max_rightward;
313     }
314     q += 2;
315     rightward -= 2;
316     p++;
317   }
318   return q-outbuf;   /* gcc likes this here */
319 }
320
321
322 /* XPG4-UNIX, according to Digital:
323 The "args" and "command" specifiers show what was passed to the command.
324 Modifications to the arguments are not shown.
325 */
326
327 /*
328  * pp->cmd       short accounting name (comm & ucomm)
329  * pp->cmdline   long name with args (args & command)
330  * pp->environ   environment
331  */
332
333 // FIXME: some of these may hit the guard page in forest mode
334
335 /* "command" is the same thing: long unless c */
336 static int pr_args(char *restrict const outbuf, const proc_t *restrict const pp){
337   char *endp = outbuf;
338   unsigned flags;
339   int rightward=max_rightward;
340
341   if(forest_prefix){
342     int fh = forest_helper(outbuf);
343     endp += fh;
344     rightward -= fh;
345   }
346   if(bsd_c_option) flags = ESC_DEFUNCT;
347   else             flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS;
348   endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, flags);
349
350   if(bsd_e_option && rightward>1){
351     const char **env = (const char**)pp->environ;
352     if(env && *env){
353       *endp++ = ' ';
354       rightward--;
355       endp += escape_strlist(endp, env, OUTBUF_SIZE, &rightward);
356     }
357   }
358   //return endp - outbuf;
359   return max_rightward-rightward;
360 }
361
362 /* "ucomm" is the same thing: short unless -f */
363 static int pr_comm(char *restrict const outbuf, const proc_t *restrict const pp){
364   char *endp = outbuf;
365   unsigned flags;
366   int rightward=max_rightward;
367   
368   if(forest_prefix){
369     int fh = forest_helper(outbuf);
370     endp += fh;
371     rightward -= fh;
372   }
373   if(unix_f_option) flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS;
374   else              flags = ESC_DEFUNCT;
375   endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, flags);
376
377   if(bsd_e_option && rightward>1){
378     const char **env = (const char**)pp->environ;
379     if(env && *env){
380       *endp++ = ' ';
381       rightward--;
382       endp += escape_strlist(endp, env, OUTBUF_SIZE, &rightward);
383     }
384   }
385   //return endp - outbuf;
386   return max_rightward-rightward;
387 }
388 /* Non-standard, from SunOS 5 */
389 static int pr_fname(char *restrict const outbuf, const proc_t *restrict const pp){
390   char *endp = outbuf;
391   int rightward = max_rightward;
392   
393   if(forest_prefix){
394     int fh = forest_helper(outbuf);
395     endp += fh;
396     rightward -= fh;
397   }
398   if (rightward>8)  /* 8=default, but forest maybe feeds more */
399     rightward = 8;
400   
401   endp += escape_str(endp, pp->cmd, OUTBUF_SIZE, &rightward);
402   //return endp - outbuf;
403   return max_rightward-rightward;
404 }
405
406 /* elapsed wall clock time, [[dd-]hh:]mm:ss format (not same as "time") */
407 static int pr_etime(char *restrict const outbuf, const proc_t *restrict const pp){
408   unsigned long t;
409   unsigned dd,hh,mm,ss;
410   char *cp = outbuf;
411   t = seconds_since_boot - (unsigned long)(pp->start_time / Hertz);
412   ss = t%60;
413   t /= 60;
414   mm = t%60;
415   t /= 60;
416   hh = t%24;
417   t /= 24;
418   dd = t;
419   cp +=(     dd      ?  snprintf(cp, COLWID, "%u-", dd)           :  0 );
420   cp +=( (dd || hh)  ?  snprintf(cp, COLWID, "%02u:", hh)         :  0 );
421   cp +=                 snprintf(cp, COLWID, "%02u:%02u", mm, ss)       ;
422   return (int)(cp-outbuf);
423 }
424
425 /* "Processor utilisation for scheduling."  --- we use %cpu w/o fraction */
426 static int pr_c(char *restrict const outbuf, const proc_t *restrict const pp){
427   unsigned long long total_time;   /* jiffies used by this process */
428   unsigned pcpu = 0;               /* scaled %cpu, 99 means 99% */
429   unsigned long long seconds;      /* seconds of process life */
430   total_time = pp->utime + pp->stime;
431   if(include_dead_children) total_time += (pp->cutime + pp->cstime);
432   seconds = seconds_since_boot - pp->start_time / Hertz;
433   if(seconds) pcpu = (total_time * 100ULL / Hertz) / seconds;
434   if (pcpu > 99U) pcpu = 99U;
435   return snprintf(outbuf, COLWID, "%2u", pcpu);
436 }
437 /* normal %CPU in ##.# format. */
438 static int pr_pcpu(char *restrict const outbuf, const proc_t *restrict const pp){
439   unsigned long long total_time;   /* jiffies used by this process */
440   unsigned pcpu = 0;               /* scaled %cpu, 999 means 99.9% */
441   unsigned long long seconds;      /* seconds of process life */
442   total_time = pp->utime + pp->stime;
443   if(include_dead_children) total_time += (pp->cutime + pp->cstime);
444   seconds = seconds_since_boot - pp->start_time / Hertz;
445   if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
446   if (pcpu > 999U)
447     return snprintf(outbuf, COLWID, "%u", pcpu/10U);
448   return snprintf(outbuf, COLWID, "%u.%u", pcpu/10U, pcpu%10U);
449 }
450 /* this is a "per-mill" format, like %cpu with no decimal point */
451 static int pr_cp(char *restrict const outbuf, const proc_t *restrict const pp){
452   unsigned long long total_time;   /* jiffies used by this process */
453   unsigned pcpu = 0;               /* scaled %cpu, 999 means 99.9% */
454   unsigned long long seconds;      /* seconds of process life */
455   total_time = pp->utime + pp->stime;
456   if(include_dead_children) total_time += (pp->cutime + pp->cstime);
457   seconds = seconds_since_boot - pp->start_time / Hertz ;
458   if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
459   if (pcpu > 999U) pcpu = 999U;
460   return snprintf(outbuf, COLWID, "%3u", pcpu);
461 }
462
463 static int pr_pgid(char *restrict const outbuf, const proc_t *restrict const pp){
464   return snprintf(outbuf, COLWID, "%u", pp->pgrp);
465 }
466 static int pr_pid(char *restrict const outbuf, const proc_t *restrict const pp){
467   return snprintf(outbuf, COLWID, "%u", pp->tgid);
468 }
469 static int pr_ppid(char *restrict const outbuf, const proc_t *restrict const pp){
470   return snprintf(outbuf, COLWID, "%u", pp->ppid);
471 }
472
473
474 /* cumulative CPU time, [dd-]hh:mm:ss format (not same as "etime") */
475 static int pr_time(char *restrict const outbuf, const proc_t *restrict const pp){
476   unsigned long t;
477   unsigned dd,hh,mm,ss;
478   int c;
479   t = (pp->utime + pp->stime) / Hertz;
480   ss = t%60;
481   t /= 60;
482   mm = t%60;
483   t /= 60;
484   hh = t%24;
485   t /= 24;
486   dd = t;
487   c  =( dd ? snprintf(outbuf, COLWID, "%u-", dd) : 0              );
488   c +=( snprintf(outbuf+c, COLWID, "%02u:%02u:%02u", hh, mm, ss)    );
489   return c;
490 }
491
492 /* HP-UX puts this (I forget, vsz or vsize?) in kB and uses "sz" for pages.
493  * Unix98 requires "vsz" to be kB.
494  * Tru64 does both vsize and vsz like "1.23M"
495  *
496  * Our pp->vm_size is kB and our pp->vsize is pages.
497  *
498  * TODO: add flag for "1.23M" behavior, on this and other columns.
499  */
500 static int pr_vsz(char *restrict const outbuf, const proc_t *restrict const pp){
501   return snprintf(outbuf, COLWID, "%lu", pp->vm_size);
502 }
503
504 //////////////////////////////////////////////////////////////////////////////////////
505
506 // "PRI" is created by "opri", or by "pri" when -c is used.
507 //
508 // Unix98 only specifies that a high "PRI" is low priority.
509 // Sun and SCO add the -c behavior. Sun defines "pri" and "opri".
510 // Linux may use "priority" for historical purposes.
511 //
512 // According to the kernel's fs/proc/array.c and kernel/sched.c source,
513 // the kernel reports it in /proc via this:
514 //        p->prio - MAX_RT_PRIO
515 // such that "RT tasks are offset by -200. Normal tasks are centered
516 // around 0, value goes from -16 to +15" but who knows if that is
517 // before or after the conversion...
518 //
519 // <linux/sched.h> says:
520 // MAX_RT_PRIO is currently 100.       (so we see 0 in /proc)
521 // RT tasks have a p->prio of 0 to 99. (so we see -100 to -1)
522 // non-RT tasks are from 100 to 139.   (so we see 0 to 39)
523 // Lower values have higher priority, as in the UNIX standard.
524 //
525 // In any case, pp->priority+100 should get us back to what the kernel
526 // has for p->prio.
527 //
528 // Test results with the "yes" program on a 2.6.x kernel:
529 //
530 // # ps -C19,_20 -o pri,opri,intpri,priority,ni,pcpu,pid,comm
531 // PRI PRI PRI PRI  NI %CPU  PID COMMAND
532 //   0  99  99  39  19 10.6 8686 19
533 //  34  65  65   5 -20 94.7 8687 _20
534 //
535 // Grrr. So the UNIX standard "PRI" must NOT be from "pri".
536 // Either of the others will do. We use "opri" for this.
537 // (and use "pri" when the "-c" option is used)
538 // Probably we should have Linux-specific "pri_for_l" and "pri_for_lc"
539 //
540 // sched_get_priority_min.2 says the Linux static priority is
541 // 1..99 for RT and 0 for other... maybe 100 is kernel-only?
542 //
543 // A nice range would be -99..0 for RT and 1..40 for normal,
544 // which is pp->priority+1. (3-digit max, positive is normal,
545 // negative or 0 is RT, and meets the standard for PRI)
546 //
547
548 // legal as UNIX "PRI"
549 // "priority"         (was -20..20, now -100..39)
550 static int pr_priority(char *restrict const outbuf, const proc_t *restrict const pp){    /* -20..20 */
551     return snprintf(outbuf, COLWID, "%ld", pp->priority);
552 }
553
554 // legal as UNIX "PRI"
555 // "intpri" and "opri" (was 39..79, now  -40..99)
556 static int pr_opri(char *restrict const outbuf, const proc_t *restrict const pp){        /* 39..79 */
557     return snprintf(outbuf, COLWID, "%ld", 60 + pp->priority);
558 }
559
560 // legal as UNIX "PRI"
561 // "pri_foo"   --  match up w/ nice values of sleeping processes (-120..19)
562 static int pr_pri_foo(char *restrict const outbuf, const proc_t *restrict const pp){
563     return snprintf(outbuf, COLWID, "%ld", pp->priority - 20);
564 }
565
566 // legal as UNIX "PRI"
567 // "pri_bar"   --  makes RT pri show as negative       (-99..40)
568 static int pr_pri_bar(char *restrict const outbuf, const proc_t *restrict const pp){
569     return snprintf(outbuf, COLWID, "%ld", pp->priority + 1);
570 }
571
572 // legal as UNIX "PRI"
573 // "pri_baz"   --  the kernel's ->prio value, as of Linux 2.6.8     (1..140)
574 static int pr_pri_baz(char *restrict const outbuf, const proc_t *restrict const pp){
575     return snprintf(outbuf, COLWID, "%ld", pp->priority + 100);
576 }
577
578
579 // not legal as UNIX "PRI"
580 // "pri"               (was 20..60, now    0..139)
581 static int pr_pri(char *restrict const outbuf, const proc_t *restrict const pp){         /* 20..60 */
582     return snprintf(outbuf, COLWID, "%ld", 39 - pp->priority);
583 }
584
585 // not legal as UNIX "PRI"
586 // "pri_api"   --  match up w/ RT API    (-40..99)
587 static int pr_pri_api(char *restrict const outbuf, const proc_t *restrict const pp){
588     return snprintf(outbuf, COLWID, "%ld", -1 - pp->priority);
589 }
590
591 static int pr_nice(char *restrict const outbuf, const proc_t *restrict const pp){
592   if(pp->sched!=0 && pp->sched!=-1) return snprintf(outbuf, COLWID, "-");
593   return snprintf(outbuf, COLWID, "%ld", pp->nice);
594 }
595
596 // HP-UX   "cls": RT RR RR2 ???? HPUX FIFO KERN
597 // Solaris "class": SYS TS FX IA RT FSS (FIFO is RR w/ Inf quant)
598 //                  FIFO+RR share RT; FIFO has Inf quant
599 //                  IA=interactive; FX=fixed; TS=timeshare; SYS=system
600 //                  FSS=fairshare; INTS=interrupts
601 // Tru64   "policy": FF RR TS
602 // IRIX    "class": RT TS B BC WL GN
603 //                  RT=real-time; TS=time-share; B=batch; BC=batch-critical
604 //                  WL=weightless; GN=gang-scheduled
605 //                  see miser(1) for this; PRI has some letter codes too
606 static int pr_class(char *restrict const outbuf, const proc_t *restrict const pp){
607   switch(pp->sched){
608   case -1: return snprintf(outbuf, COLWID, "-");   // not reported
609   case  0: return snprintf(outbuf, COLWID, "TS");  // SCHED_OTHER SCHED_NORMAL
610   case  1: return snprintf(outbuf, COLWID, "FF");  // SCHED_FIFO
611   case  2: return snprintf(outbuf, COLWID, "RR");  // SCHED_RR
612   case  3: return snprintf(outbuf, COLWID, "B");   // SCHED_BATCH
613   case  4: return snprintf(outbuf, COLWID, "ISO"); // reserved for SCHED_ISO (Con Kolivas)
614   case  5: return snprintf(outbuf, COLWID, "IDL"); // SCHED_IDLE
615   case  6: return snprintf(outbuf, COLWID, "#6");  //
616   case  7: return snprintf(outbuf, COLWID, "#7");  //
617   case  8: return snprintf(outbuf, COLWID, "#8");  //
618   case  9: return snprintf(outbuf, COLWID, "#9");  //
619   default: return snprintf(outbuf, COLWID, "?");   // unknown value
620   }
621 }
622 // Based on "type", FreeBSD would do:
623 //    REALTIME  "real:%u", prio
624 //    NORMAL    "normal"
625 //    IDLE      "idle:%u", prio
626 //    default   "%u:%u", type, prio
627 // We just print the priority, and have other keywords for type.
628 static int pr_rtprio(char *restrict const outbuf, const proc_t *restrict const pp){
629   if(pp->sched==0 || pp->sched==-1) return snprintf(outbuf, COLWID, "-");
630   return snprintf(outbuf, COLWID, "%ld", pp->rtprio);
631 }
632 static int pr_sched(char *restrict const outbuf, const proc_t *restrict const pp){
633   if(pp->sched==-1) return snprintf(outbuf, COLWID, "-");
634   return snprintf(outbuf, COLWID, "%ld", pp->sched);
635 }
636
637 ////////////////////////////////////////////////////////////////////////////////
638
639 static int pr_wchan(char *restrict const outbuf, const proc_t *restrict const pp){
640 /*
641  * Unix98 says "blank if running" and also "no blanks"! :-(
642  * Unix98 also says to use '-' if something is meaningless.
643  * Digital uses both '*' and '-', with undocumented differences.
644  * (the '*' for -1 (rare) and the '-' for 0)
645  * Sun claims to use a blank AND use '-', in the same man page.
646  * Perhaps "blank" should mean '-'.
647  *
648  * AIX uses '-' for running processes, the location when there is
649  * only one thread waiting in the kernel, and '*' when there is
650  * more than one thread waiting in the kernel.
651  *
652  * The output should be truncated to maximal columns width -- overflow
653  * is not supported for the "wchan".
654  */
655   const char *w;
656   size_t len;
657   if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1;
658   if(wchan_is_number) return snprintf(outbuf, COLWID, "%x", (unsigned)(pp->wchan) & 0xffffffu);
659   w = lookup_wchan(pp->wchan, pp->XXXID);
660   len = strlen(w);
661   if(len>max_rightward) len=max_rightward;
662   memcpy(outbuf, w, len);
663   outbuf[len] = '\0';
664   return len;
665 }
666
667 static int pr_wname(char *restrict const outbuf, const proc_t *restrict const pp){
668 /* SGI's IRIX always uses a number for "wchan", so "wname" is provided too.
669  *
670  * We use '-' for running processes, the location when there is
671  * only one thread waiting in the kernel, and '*' when there is
672  * more than one thread waiting in the kernel.
673  *
674  * The output should be truncated to maximal columns width -- overflow
675  * is not supported for the "wchan".
676  */
677   const char *w;
678   size_t len;
679   if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1;
680   w = lookup_wchan(pp->wchan, pp->XXXID);
681   len = strlen(w);
682   if(len>max_rightward) len=max_rightward;
683   memcpy(outbuf, w, len);
684   outbuf[len] = '\0';
685   return len;
686 }
687
688 static int pr_nwchan(char *restrict const outbuf, const proc_t *restrict const pp){
689   if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1;
690   return snprintf(outbuf, COLWID, "%x", (unsigned)(pp->wchan) & 0xffffffu);
691 }
692
693 /* Terrible trunctuation, like BSD crap uses: I999 J999 K999 */
694 /* FIXME: disambiguate /dev/tty69 and /dev/pts/69. */
695 static int pr_tty4(char *restrict const outbuf, const proc_t *restrict const pp){
696 /* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */
697   return dev_to_tty(outbuf, 4, pp->tty, pp->XXXID, ABBREV_DEV|ABBREV_TTY|ABBREV_PTS);
698 }
699
700 /* Unix98: format is unspecified, but must match that used by who(1). */
701 static int pr_tty8(char *restrict const outbuf, const proc_t *restrict const pp){
702 /* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */
703   return dev_to_tty(outbuf, COLWID, pp->tty, pp->XXXID, ABBREV_DEV);
704 }
705
706 #if 0
707 /* This BSD state display may contain spaces, which is illegal. */
708 static int pr_oldstate(char *restrict const outbuf, const proc_t *restrict const pp){
709     return snprintf(outbuf, COLWID, "%s", status(pp));
710 }
711 #endif
712
713 // This state display is Unix98 compliant and has lots of info like BSD.
714 static int pr_stat(char *restrict const outbuf, const proc_t *restrict const pp){
715     int end = 0;
716     outbuf[end++] = pp->state;
717 //  if(pp->rss==0 && pp->state!='Z')  outbuf[end++] = 'W'; // useless "swapped out"
718     if(pp->nice < 0)                  outbuf[end++] = '<';
719     if(pp->nice > 0)                  outbuf[end++] = 'N';
720 // In this order, NetBSD would add:
721 //     traced   'X'
722 //     systrace 'x'
723 //     exiting  'E' (not printed for zombies)
724 //     vforked  'V'
725 //     system   'K' (and do not print 'L' too)
726     if(pp->vm_lock)                   outbuf[end++] = 'L';
727     if(pp->session == pp->tgid)       outbuf[end++] = 's'; // session leader
728     if(pp->nlwp > 1)                  outbuf[end++] = 'l'; // multi-threaded
729     if(pp->pgrp == pp->tpgid)         outbuf[end++] = '+'; // in foreground process group
730     outbuf[end] = '\0';
731     return end;
732 }
733
734 /* This minimal state display is Unix98 compliant, like SCO and SunOS 5 */
735 static int pr_s(char *restrict const outbuf, const proc_t *restrict const pp){
736     outbuf[0] = pp->state;
737     outbuf[1] = '\0';
738     return 1;
739 }
740
741 static int pr_flag(char *restrict const outbuf, const proc_t *restrict const pp){
742     /* Unix98 requires octal flags */
743     /* this user-hostile and volatile junk gets 1 character */
744     return snprintf(outbuf, COLWID, "%o", (unsigned)(pp->flags>>6U)&0x7U);
745 }
746
747 // plus these: euid,ruid,egroup,rgroup (elsewhere in this file)
748
749 /*********** non-standard ***********/
750
751 /*** BSD
752 sess    session pointer
753 (SCO has:Process session leader ID as a decimal value. (SESSION))
754 jobc    job control count
755 cpu     short-term cpu usage factor (for scheduling)
756 sl      sleep time (in seconds; 127 = infinity)
757 re      core residency time (in seconds; 127 = infinity)
758 pagein  pageins (same as majflt)
759 lim     soft memory limit
760 tsiz    text size (in Kbytes)
761 ***/
762
763 static int pr_stackp(char *restrict const outbuf, const proc_t *restrict const pp){
764     return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->start_stack));
765 }
766
767 static int pr_esp(char *restrict const outbuf, const proc_t *restrict const pp){
768     return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_esp));
769 }
770
771 static int pr_eip(char *restrict const outbuf, const proc_t *restrict const pp){
772     return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_eip));
773 }
774
775 /* This function helps print old-style time formats */
776 static int old_time_helper(char *dst, unsigned long long t, unsigned long long rel) {
777   if(!t)            return snprintf(dst, COLWID, "    -");
778   if(t == ~0ULL)    return snprintf(dst, COLWID, "   xx");
779   if((long long)(t-=rel) < 0)  t=0ULL;
780   if(t>9999ULL)     return snprintf(dst, COLWID, "%5Lu", t/100ULL);
781   else              return snprintf(dst, COLWID, "%2u.%02u", (unsigned)t/100U, (unsigned)t%100U);
782 }
783
784 static int pr_bsdtime(char *restrict const outbuf, const proc_t *restrict const pp){
785     unsigned long long t;
786     unsigned u;
787     t = pp->utime + pp->stime;
788     if(include_dead_children) t += (pp->cutime + pp->cstime);
789     u = t / Hertz;
790     return snprintf(outbuf, COLWID, "%3u:%02u", u/60U, u%60U);
791 }
792
793 static int pr_bsdstart(char *restrict const outbuf, const proc_t *restrict const pp){
794   time_t start;
795   time_t seconds_ago;
796   start = time_of_boot + pp->start_time / Hertz;
797   seconds_ago = seconds_since_1970 - start;
798   if(seconds_ago < 0) seconds_ago=0;
799   if(seconds_ago > 3600*24)  strcpy(outbuf, ctime(&start)+4);
800   else                       strcpy(outbuf, ctime(&start)+10);
801   outbuf[6] = '\0';
802   return 6;
803 }
804
805 static int pr_alarm(char *restrict const outbuf, const proc_t *restrict const pp){
806     return old_time_helper(outbuf, pp->alarm, 0ULL);
807 }
808
809 /* HP-UX puts this in pages and uses "vsz" for kB */
810 static int pr_sz(char *restrict const outbuf, const proc_t *restrict const pp){
811   return snprintf(outbuf, COLWID, "%lu", (pp->vm_size)/(page_size/1024));
812 }
813
814
815 /*
816  * FIXME: trs,drs,tsiz,dsiz,m_trs,m_drs,vm_exe,vm_data,trss
817  * I suspect some/all of those are broken. They seem to have been
818  * inherited by Linux and AIX from early BSD systems. FreeBSD only
819  * retains tsiz. The prefixed versions come from Debian.
820  * Sun and Digital have none of this crap. The code here comes
821  * from an old Linux ps, and might not be correct for ELF executables.
822  *
823  * AIX            TRS    size of resident-set (real memory) of text
824  * AIX            TSIZ   size of text (shared-program) image
825  * FreeBSD        tsiz   text size (in Kbytes)
826  * 4.3BSD NET/2   trss   text resident set size (in Kbytes)
827  * 4.3BSD NET/2   tsiz   text size (in Kbytes)
828  */
829
830 /* kB data size. See drs, tsiz & trs. */
831 static int pr_dsiz(char *restrict const outbuf, const proc_t *restrict const pp){
832     long dsiz = 0;
833     if(pp->vsize) dsiz += (pp->vsize - pp->end_code + pp->start_code) >> 10;
834     return snprintf(outbuf, COLWID, "%ld", dsiz);
835 }
836
837 /* kB text (code) size. See trs, dsiz & drs. */
838 static int pr_tsiz(char *restrict const outbuf, const proc_t *restrict const pp){
839     long tsiz = 0;
840     if(pp->vsize) tsiz += (pp->end_code - pp->start_code) >> 10;
841     return snprintf(outbuf, COLWID, "%ld", tsiz);
842 }
843
844 /* kB _resident_ data size. See dsiz, tsiz & trs. */
845 static int pr_drs(char *restrict const outbuf, const proc_t *restrict const pp){
846     long drs = 0;
847     if(pp->vsize) drs += (pp->vsize - pp->end_code + pp->start_code) >> 10;
848     return snprintf(outbuf, COLWID, "%ld", drs);
849 }
850
851 /* kB text _resident_ (code) size. See tsiz, dsiz & drs. */
852 static int pr_trs(char *restrict const outbuf, const proc_t *restrict const pp){
853     long trs = 0;
854     if(pp->vsize) trs += (pp->end_code - pp->start_code) >> 10;
855     return snprintf(outbuf, COLWID, "%ld", trs);
856 }
857
858 /* approximation to: kB of address space that could end up in swap */
859 static int pr_swapable(char *restrict const outbuf, const proc_t *restrict const pp){
860   return snprintf(outbuf, COLWID, "%ld", pp->vm_data + pp->vm_stack);
861 }
862
863 /* nasty old Debian thing */
864 static int pr_size(char *restrict const outbuf, const proc_t *restrict const pp){
865   return snprintf(outbuf, COLWID, "%ld", pp->size);
866 }
867
868
869 static int pr_minflt(char *restrict const outbuf, const proc_t *restrict const pp){
870     long flt = pp->min_flt;
871     if(include_dead_children) flt += pp->cmin_flt;
872     return snprintf(outbuf, COLWID, "%ld", flt);
873 }
874
875 static int pr_majflt(char *restrict const outbuf, const proc_t *restrict const pp){
876     long flt = pp->maj_flt;
877     if(include_dead_children) flt += pp->cmaj_flt;
878     return snprintf(outbuf, COLWID, "%ld", flt);
879 }
880
881 static int pr_lim(char *restrict const outbuf, const proc_t *restrict const pp){
882     if(pp->rss_rlim == RLIM_INFINITY){
883       outbuf[0] = 'x';
884       outbuf[1] = 'x';
885       outbuf[2] = '\0';
886       return 2;
887     }
888     return snprintf(outbuf, COLWID, "%5ld", pp->rss_rlim >> 10);
889 }
890
891 /* should print leading tilde ('~') if process is bound to the CPU */
892 static int pr_psr(char *restrict const outbuf, const proc_t *restrict const pp){
893   return snprintf(outbuf, COLWID, "%d", pp->processor);
894 }
895
896 static int pr_rss(char *restrict const outbuf, const proc_t *restrict const pp){
897   return snprintf(outbuf, COLWID, "%lu", pp->vm_rss);
898 }
899
900 /* pp->vm_rss * 1000 would overflow on 32-bit systems with 64 GB memory */
901 static int pr_pmem(char *restrict const outbuf, const proc_t *restrict const pp){
902   unsigned long pmem = 0;
903   pmem = pp->vm_rss * 1000ULL / kb_main_total;
904   if (pmem > 999) pmem = 999;
905   return snprintf(outbuf, COLWID, "%2u.%u", (unsigned)(pmem/10), (unsigned)(pmem%10));
906 }
907
908 static int pr_lstart(char *restrict const outbuf, const proc_t *restrict const pp){
909   time_t t;
910   t = time_of_boot + pp->start_time / Hertz;
911   return snprintf(outbuf, COLWID, "%24.24s", ctime(&t));
912 }
913
914 /* Unix98 specifies a STIME header for a column that shows the start
915  * time of the process, but does not specify a format or format specifier.
916  * From the general Unix98 rules, we know there must not be any spaces.
917  * Most systems violate that rule, though the Solaris documentation
918  * claims to print the column without spaces. (NOT!)
919  *
920  * So this isn't broken, but could be renamed to u98_std_stime,
921  * as long as it still shows as STIME when using the -f option.
922  */
923 static int pr_stime(char *restrict const outbuf, const proc_t *restrict const pp){
924   struct tm *proc_time;
925   struct tm *our_time;
926   time_t t;
927   const char *fmt;
928   int tm_year;
929   int tm_yday;
930   our_time = localtime(&seconds_since_1970);   /* not reentrant */
931   tm_year = our_time->tm_year;
932   tm_yday = our_time->tm_yday;
933   t = time_of_boot + pp->start_time / Hertz;
934   proc_time = localtime(&t); /* not reentrant, this corrupts our_time */
935   fmt = "%H:%M";                                   /* 03:02 23:59 */
936   if(tm_yday != proc_time->tm_yday) fmt = "%b%d";  /* Jun06 Aug27 */
937   if(tm_year != proc_time->tm_year) fmt = "%Y";    /* 1991 2001 */
938   return strftime(outbuf, 42, fmt, proc_time);
939 }
940
941 static int pr_start(char *restrict const outbuf, const proc_t *restrict const pp){
942   time_t t;
943   char *str;
944   t = time_of_boot + pp->start_time / Hertz;
945   str = ctime(&t);
946   if(str[8]==' ')  str[8]='0';
947   if(str[11]==' ') str[11]='0';
948   if((unsigned long)t+60*60*24 > seconds_since_1970)
949     return snprintf(outbuf, COLWID, "%8.8s", str+11);
950   return snprintf(outbuf, COLWID, "  %6.6s", str+4);
951 }
952
953
954 #ifdef SIGNAL_STRING
955 static int help_pr_sig(char *restrict const outbuf, const char *restrict const sig){
956   long len = 0;
957   len = strlen(sig);
958   if(wide_signals){
959     if(len>8) return snprintf(outbuf, COLWID, "%s", sig);
960     return snprintf(outbuf, COLWID, "00000000%s", sig);
961   }
962   if(len-strspn(sig,"0") > 8)
963     return snprintf(outbuf, COLWID, "<%s", sig+len-8);
964   return snprintf(outbuf, COLWID,  "%s", sig+len-8);
965 }
966 #else
967 static int help_pr_sig(unsigned long long sig){
968   if(wide_signals) return snprintf(outbuf, COLWID, "%016Lx", sig);
969   if(sig>>32)      return snprintf(outbuf, COLWID, "<%08Lx", sig&0xffffffffLL);
970   return                  snprintf(outbuf, COLWID,  "%08Lx", sig&0xffffffffLL);
971 }
972 #endif
973
974 // This one is always thread-specific pending. (from Dragonfly BSD)
975 static int pr_tsig(char *restrict const outbuf, const proc_t *restrict const pp){
976   return help_pr_sig(outbuf, pp->_sigpnd);
977 }
978 // This one is (wrongly?) thread-specific when printing thread lines,
979 // but process-pending otherwise.
980 static int pr_sig(char *restrict const outbuf, const proc_t *restrict const pp){
981   return help_pr_sig(outbuf, pp->signal);
982 }
983 static int pr_sigmask(char *restrict const outbuf, const proc_t *restrict const pp){
984   return help_pr_sig(outbuf, pp->blocked);
985 }
986 static int pr_sigignore(char *restrict const outbuf, const proc_t *restrict const pp){
987   return help_pr_sig(outbuf, pp->sigignore);
988 }
989 static int pr_sigcatch(char *restrict const outbuf, const proc_t *restrict const pp){
990   return help_pr_sig(outbuf, pp->sigcatch);
991 }
992
993
994 ////////////////////////////////////////////////////////////////////////////////
995
996 /*
997  * internal terms:  ruid  euid  suid  fuid
998  * kernel vars:      uid  euid  suid fsuid
999  * command args:    ruid   uid svuid   n/a
1000  */
1001
1002 static int pr_egid(char *restrict const outbuf, const proc_t *restrict const pp){
1003   return snprintf(outbuf, COLWID, "%d", pp->egid);
1004 }
1005 static int pr_rgid(char *restrict const outbuf, const proc_t *restrict const pp){
1006   return snprintf(outbuf, COLWID, "%d", pp->rgid);
1007 }
1008 static int pr_sgid(char *restrict const outbuf, const proc_t *restrict const pp){
1009   return snprintf(outbuf, COLWID, "%d", pp->sgid);
1010 }
1011 static int pr_fgid(char *restrict const outbuf, const proc_t *restrict const pp){
1012   return snprintf(outbuf, COLWID, "%d", pp->fgid);
1013 }
1014
1015 static int pr_euid(char *restrict const outbuf, const proc_t *restrict const pp){
1016   return snprintf(outbuf, COLWID, "%d", pp->euid);
1017 }
1018 static int pr_ruid(char *restrict const outbuf, const proc_t *restrict const pp){
1019   return snprintf(outbuf, COLWID, "%d", pp->ruid);
1020 }
1021 static int pr_suid(char *restrict const outbuf, const proc_t *restrict const pp){
1022   return snprintf(outbuf, COLWID, "%d", pp->suid);
1023 }
1024 static int pr_fuid(char *restrict const outbuf, const proc_t *restrict const pp){
1025   return snprintf(outbuf, COLWID, "%d", pp->fuid);
1026 }
1027
1028 // The Open Group Base Specifications Issue 6 (IEEE Std 1003.1, 2004 Edition)
1029 // requires that user and group names print as decimal numbers if there is
1030 // not enough room in the column, so tough luck if you don't like it.
1031 //
1032 // The UNIX and POSIX way to change column width is to rename it:
1033 //      ps -o pid,user=CumbersomeUserNames -o comm
1034 // The easy way is to directly specify the desired width:
1035 //      ps -o pid,user:19,comm
1036 //
1037 static int do_pr_name(char *restrict const outbuf, const char *restrict const name, unsigned u){
1038   if(!user_is_number){
1039     int rightward = OUTBUF_SIZE;        /* max cells */
1040     int len;                            /* real cells */
1041     
1042     escape_str(outbuf, name, OUTBUF_SIZE, &rightward);
1043     len = OUTBUF_SIZE-rightward;
1044     
1045     if(len <= (int)max_rightward)
1046       return len;  /* returns number of cells */
1047   }
1048   return snprintf(outbuf, COLWID, "%u", u);
1049 }
1050
1051 static int pr_ruser(char *restrict const outbuf, const proc_t *restrict const pp){
1052   return do_pr_name(outbuf, pp->ruser, pp->ruid);
1053 }
1054 static int pr_euser(char *restrict const outbuf, const proc_t *restrict const pp){
1055   return do_pr_name(outbuf, pp->euser, pp->euid);
1056 }
1057 static int pr_fuser(char *restrict const outbuf, const proc_t *restrict const pp){
1058   return do_pr_name(outbuf, pp->fuser, pp->fuid);
1059 }
1060 static int pr_suser(char *restrict const outbuf, const proc_t *restrict const pp){
1061   return do_pr_name(outbuf, pp->suser, pp->suid);
1062 }
1063
1064 static int pr_egroup(char *restrict const outbuf, const proc_t *restrict const pp){
1065   return do_pr_name(outbuf, pp->egroup, pp->egid);
1066 }
1067 static int pr_rgroup(char *restrict const outbuf, const proc_t *restrict const pp){
1068   return do_pr_name(outbuf, pp->rgroup, pp->rgid);
1069 }
1070 static int pr_fgroup(char *restrict const outbuf, const proc_t *restrict const pp){
1071   return do_pr_name(outbuf, pp->fgroup, pp->fgid);
1072 }
1073 static int pr_sgroup(char *restrict const outbuf, const proc_t *restrict const pp){
1074   return do_pr_name(outbuf, pp->sgroup, pp->sgid);
1075 }
1076
1077 //////////////////////////////////////////////////////////////////////////////////
1078
1079 // TID tid LWP lwp SPID spid
1080 static int pr_thread(char *restrict const outbuf, const proc_t *restrict const pp){
1081   return snprintf(outbuf, COLWID, "%u", pp->tid);
1082 }
1083 // thcount THCNT
1084 static int pr_nlwp(char *restrict const outbuf, const proc_t *restrict const pp){
1085     return snprintf(outbuf, COLWID, "%d", pp->nlwp);
1086 }
1087
1088 static int pr_sess(char *restrict const outbuf, const proc_t *restrict const pp){
1089   return snprintf(outbuf, COLWID, "%u", pp->session);
1090 }
1091 static int pr_tpgid(char *restrict const outbuf, const proc_t *restrict const pp){
1092   return snprintf(outbuf, COLWID, "%d", pp->tpgid);
1093 }
1094
1095
1096 /* SGI uses "cpu" to print the processor ID with header "P" */
1097 static int pr_sgi_p(char *restrict const outbuf, const proc_t *restrict const pp){          /* FIXME */
1098   if(pp->state == 'R') return snprintf(outbuf, COLWID, "%d", pp->processor);
1099   return snprintf(outbuf, COLWID, "*");
1100 }
1101
1102
1103 /****************** FLASK & seLinux security stuff **********************/
1104 // move the bulk of this to libproc sometime
1105
1106 static int pr_context(char *restrict const outbuf, const proc_t *restrict const pp){
1107   char filename[48];
1108   size_t len;
1109   ssize_t num_read;
1110   int fd;
1111
1112 // wchan file is suitable for testing
1113 //snprintf(filename, sizeof filename, "/proc/%d/wchan", pp->tgid);
1114 snprintf(filename, sizeof filename, "/proc/%d/attr/current", pp->tgid);
1115
1116   fd = open(filename, O_RDONLY, 0);
1117   if(likely(fd==-1)) goto fail;
1118   num_read = read(fd, outbuf, 666);
1119   close(fd);
1120   if(unlikely(num_read<=0)) goto fail;
1121   outbuf[num_read] = '\0';
1122
1123   len = 0;
1124   while(outbuf[len]>' ' && outbuf[len]<='~') len++;
1125   outbuf[len] = '\0';
1126   if(len) return len;
1127
1128 fail:
1129   outbuf[0] = '-';
1130   outbuf[1] = '\0';
1131   return 1;
1132 }
1133
1134 #if 0
1135 // This needs more study, considering:
1136 // 1. the static linking option (maybe disable this in that case)
1137 // 2. the -z and -Z option issue
1138 // 3. width of output
1139 static int pr_context(char *restrict const outbuf, const proc_t *restrict const pp){
1140   static int (*ps_getpidcon)(pid_t pid, char **context) = 0;
1141   static int tried_load = 0;
1142   size_t len;
1143   char *context;
1144
1145   if(!ps_getpidcon && !tried_load){
1146     void *handle = dlopen("libselinux.so.1", RTLD_NOW);
1147     if(handle){
1148       dlerror();
1149       ps_getpidcon = dlsym(handle, "getpidcon");
1150       if(dlerror())
1151         ps_getpidcon = 0;
1152     }
1153     tried_load++;
1154   }
1155   if(ps_getpidcon && !ps_getpidcon(pp->tgid, &context)){
1156     size_t max_len = OUTBUF_SIZE-1;
1157     len = strlen(context);
1158     if(len > max_len) len = max_len;
1159     memcpy(outbuf, context, len);
1160     outbuf[len] = '\0';
1161     free(context);
1162   }else{
1163     outbuf[0] = '-';
1164     outbuf[1] = '\0';
1165     len = 1;
1166   }
1167   return len;
1168 }
1169 #endif
1170
1171
1172 ////////////////////////////// Test code /////////////////////////////////
1173
1174 // like "args"
1175 static int pr_t_unlimited(char *restrict const outbuf, const proc_t *restrict const pp){
1176   static const char *const vals[] = {"[123456789-12345] <defunct>","ps","123456789-123456"};
1177   (void)pp;
1178   snprintf(outbuf, max_rightward+1, "%s", vals[lines_to_next_header%3u]);
1179   return strlen(outbuf);
1180 }
1181 static int pr_t_unlimited2(char *restrict const outbuf, const proc_t *restrict const pp){
1182   static const char *const vals[] = {"unlimited", "[123456789-12345] <defunct>","ps","123456789-123456"};
1183   (void)pp;
1184   snprintf(outbuf, max_rightward+1, "%s", vals[lines_to_next_header%4u]);
1185   return strlen(outbuf);
1186 }
1187
1188 // like "etime"
1189 static int pr_t_right(char *restrict const outbuf, const proc_t *restrict const pp){
1190   static const char *const vals[] = {"999-23:59:59","99-23:59:59","9-23:59:59","59:59"};
1191   (void)pp;
1192   return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%4u]);
1193 }
1194 static int pr_t_right2(char *restrict const outbuf, const proc_t *restrict const pp){
1195   static const char *const vals[] = {"999-23:59:59","99-23:59:59","9-23:59:59"};
1196   (void)pp;
1197   return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%3u]);
1198 }
1199
1200 // like "tty"
1201 static int pr_t_left(char *restrict const outbuf, const proc_t *restrict const pp){
1202   static const char *const vals[] = {"tty7","pts/9999","iseries/vtty42","ttySMX0","3270/tty4"};
1203   (void)pp;
1204   return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%5u]);
1205 }
1206 static int pr_t_left2(char *restrict const outbuf, const proc_t *restrict const pp){
1207   static const char *const vals[] = {"tty7","pts/9999","ttySMX0","3270/tty4"};
1208   (void)pp;
1209   return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%4u]);
1210 }
1211
1212 /***************************************************************************/
1213 /*************************** other stuff ***********************************/
1214
1215 /*
1216  * Old header specifications.
1217  *
1218  * short   Up  "  PID TTY STAT  TIME COMMAND"
1219  * long  l Pp  " FLAGS   UID   PID  PPID PRI  NI   SIZE   RSS WCHAN       STA TTY TIME COMMAND
1220  * user  u up  "USER       PID %CPU %MEM  SIZE   RSS TTY STAT START   TIME COMMAND
1221  * jobs  j gPp " PPID   PID  PGID   SID TTY TPGID  STAT   UID   TIME COMMAND
1222  * sig   s p   "  UID   PID SIGNAL   BLOCKED  IGNORED  CATCHED  STAT TTY   TIME COMMAND
1223  * vm    v r   "  PID TTY STAT  TIME  PAGEIN TSIZ DSIZ  RSS   LIM %MEM COMMAND
1224  * m     m r   "  PID TTY MAJFLT MINFLT   TRS   DRS  SIZE  SWAP   RSS  SHRD   LIB  DT COMMAND
1225  * regs  X p   "NR   PID    STACK      ESP      EIP TMOUT ALARM STAT TTY   TIME COMMAND
1226  */
1227
1228 /*
1229  * Unix98 requires that the heading for tty is TT, though XPG4, Digital,
1230  * and BSD use TTY. The Unix98 headers are:
1231  *              args,comm,etime,group,nice,pcpu,pgid
1232  *              pid,ppid,rgroup,ruser,time,tty,user,vsz
1233  *
1234  * BSD c:   "command" becomes accounting name ("comm" or "ucomm")
1235  * BSD n:   "user" becomes "uid" and "wchan" becomes "nwchan" (number)
1236  */
1237
1238 /* Justification control for flags field. */
1239 #define USER      CF_USER   // left if text, right if numeric
1240 #define LEFT      CF_LEFT
1241 #define RIGHT     CF_RIGHT
1242 #define UNLIMITED CF_UNLIMITED
1243 #define WCHAN     CF_WCHAN  // left if text, right if numeric
1244 #define SIGNAL    CF_SIGNAL // right in 9, or 16 if room
1245 #define PIDMAX    CF_PIDMAX
1246 #define TO        CF_PRINT_THREAD_ONLY
1247 #define PO        CF_PRINT_PROCESS_ONLY
1248 #define ET        CF_PRINT_EVERY_TIME
1249 #define AN        CF_PRINT_AS_NEEDED // no idea
1250
1251 /* short names to save space */
1252 #define MEM PROC_FILLMEM     /* read statm  */
1253 #define ARG PROC_FILLARG     /* read cmdline (cleared if c option) */
1254 #define COM PROC_FILLCOM     /* read cmdline (cleared if not -f option) */
1255 #define ENV PROC_FILLENV     /* read environ */
1256 #define USR PROC_FILLUSR     /* uid_t -> user names */
1257 #define GRP PROC_FILLGRP     /* gid_t -> group names */
1258 #define WCH PROC_FILLWCHAN   /* do WCHAN lookup */
1259
1260
1261 /* TODO
1262  *      pull out annoying BSD aliases into another table (to macro table?)
1263  *      add sorting functions here (to unify names)
1264  */
1265
1266 /* temporary hack -- mark new stuff grabbed from Debian ps */
1267 #define LNx LNX
1268
1269 /* there are about 211 listed */
1270
1271 /* Many of these are placeholders for unsupported options. */
1272 static const format_struct format_array[] = {
1273 /* code       header     print()      sort()    width need vendor flags  */
1274 {"%cpu",      "%CPU",    pr_pcpu,     sr_pcpu,    4,   0,    BSD, ET|RIGHT}, /*pcpu*/
1275 {"%mem",      "%MEM",    pr_pmem,     sr_nop,     4,   0,    BSD, PO|RIGHT}, /*pmem*/
1276 {"_left",     "LLLLLLLL", pr_t_left,  sr_nop,     8,   0,    TST, ET|LEFT},
1277 {"_left2",    "L2L2L2L2", pr_t_left2, sr_nop,     8,   0,    TST, ET|LEFT},
1278 {"_right",    "RRRRRRRRRRR", pr_t_right, sr_nop, 11,   0,    TST, ET|RIGHT},
1279 {"_right2",   "R2R2R2R2R2R", pr_t_right2, sr_nop, 11,  0,    TST, ET|RIGHT},
1280 {"_unlimited","U",   pr_t_unlimited,  sr_nop,    16,   0,    TST, ET|UNLIMITED},
1281 {"_unlimited2","U2", pr_t_unlimited2, sr_nop,    16,   0,    TST, ET|UNLIMITED},
1282 {"acflag",    "ACFLG",   pr_nop,      sr_nop,     5,   0,    XXX, AN|RIGHT}, /*acflg*/
1283 {"acflg",     "ACFLG",   pr_nop,      sr_nop,     5,   0,    BSD, AN|RIGHT}, /*acflag*/
1284 {"addr",      "ADDR",    pr_nop,      sr_nop,     4,   0,    XXX, AN|RIGHT},
1285 {"addr_1",    "ADDR",    pr_nop,      sr_nop,     1,   0,    LNX, AN|LEFT},
1286 {"alarm",     "ALARM",   pr_alarm,    sr_alarm,   5,   0,    LNX, AN|RIGHT},
1287 {"argc",      "ARGC",    pr_nop,      sr_nop,     4,   0,    LNX, PO|RIGHT},
1288 {"args",      "COMMAND", pr_args,     sr_cmd,    27, ARG,    U98, PO|UNLIMITED}, /*command*/
1289 {"atime",     "TIME",    pr_time,     sr_nop,     8,   0,    SOE, ET|RIGHT}, /*cputime*/ /* was 6 wide */
1290 {"blocked",   "BLOCKED", pr_sigmask,  sr_nop,     9,   0,    BSD, TO|SIGNAL}, /*sigmask*/
1291 {"bnd",       "BND",     pr_nop,      sr_nop,     1,   0,    AIX, TO|RIGHT},
1292 {"bsdstart",  "START",   pr_bsdstart, sr_nop,     6,   0,    LNX, ET|RIGHT},
1293 {"bsdtime",   "TIME",    pr_bsdtime,  sr_nop,     6,   0,    LNX, ET|RIGHT},
1294 {"c",         "C",       pr_c,        sr_pcpu,    2,   0,    SUN, ET|RIGHT},
1295 {"caught",    "CAUGHT",  pr_sigcatch, sr_nop,     9,   0,    BSD, TO|SIGNAL}, /*sigcatch*/
1296 {"class",     "CLS",     pr_class,    sr_sched,   3,   0,    XXX, TO|LEFT},
1297 {"cls",       "CLS",     pr_class,    sr_sched,   3,   0,    HPU, TO|RIGHT}, /*says HPUX or RT*/
1298 {"cmaj_flt",  "-",       pr_nop,      sr_cmaj_flt, 1,  0,    LNX, AN|RIGHT},
1299 {"cmd",       "CMD",     pr_args,     sr_cmd,    27, ARG,    DEC, PO|UNLIMITED}, /*ucomm*/
1300 {"cmin_flt",  "-",       pr_nop,      sr_cmin_flt, 1,  0,    LNX, AN|RIGHT},
1301 {"cnswap",    "-",       pr_nop,      sr_nop,     1,   0,    LNX, AN|RIGHT},
1302 {"comm",      "COMMAND", pr_comm,     sr_cmd,    15, COM,    U98, PO|UNLIMITED}, /*ucomm*/
1303 {"command",   "COMMAND", pr_args,     sr_cmd,    27, ARG,    XXX, PO|UNLIMITED}, /*args*/
1304 {"context",   "CONTEXT", pr_context,  sr_nop,    31,   0,    LNX, ET|LEFT},
1305 {"cp",        "CP",      pr_cp,       sr_pcpu,    3,   0,    DEC, ET|RIGHT}, /*cpu*/
1306 {"cpu",       "CPU",     pr_nop,      sr_nop,     3,   0,    BSD, AN|RIGHT}, /* FIXME ... HP-UX wants this as the CPU number for SMP? */
1307 {"cpuid",     "CPUID",   pr_psr,      sr_nop,     5,   0,    BSD, TO|RIGHT}, // OpenBSD: 8 wide!
1308 {"cputime",   "TIME",    pr_time,     sr_nop,     8,   0,    DEC, ET|RIGHT}, /*time*/
1309 {"cstime",    "-",       pr_nop,      sr_cstime,  1,   0,    LNX, AN|RIGHT},
1310 {"ctid",      "CTID",    pr_nop,      sr_nop,     5,   0,    SUN, ET|RIGHT}, // resource contracts?
1311 {"cursig",    "CURSIG",  pr_nop,      sr_nop,     6,   0,    DEC, AN|RIGHT},
1312 {"cutime",    "-",       pr_nop,      sr_cutime,  1,   0,    LNX, AN|RIGHT},
1313 {"cwd",       "CWD",     pr_nop,      sr_nop,     3,   0,    LNX, AN|LEFT},
1314 {"drs",       "DRS",     pr_drs,      sr_drs,     5, MEM,    LNX, PO|RIGHT},
1315 {"dsiz",      "DSIZ",    pr_dsiz,     sr_nop,     4,   0,    LNX, PO|RIGHT},
1316 {"egid",      "EGID",    pr_egid,     sr_egid,    5,   0,    LNX, ET|RIGHT},
1317 {"egroup",    "EGROUP",  pr_egroup,   sr_egroup,  8, GRP,    LNX, ET|USER},
1318 {"eip",       "EIP",     pr_eip,      sr_kstk_eip, 8,  0,    LNX, TO|RIGHT},
1319 {"emul",      "EMUL",    pr_nop,      sr_nop,    13,   0,    BSD, PO|LEFT}, /* "FreeBSD ELF32" and such */
1320 {"end_code",  "E_CODE",  pr_nop,      sr_end_code, 8,  0,    LNx, PO|RIGHT},
1321 {"environ","ENVIRONMENT",pr_nop,      sr_nop,    11, ENV,    LNx, PO|UNLIMITED},
1322 {"esp",       "ESP",     pr_esp,      sr_kstk_esp, 8,  0,    LNX, TO|RIGHT},
1323 {"etime",     "ELAPSED", pr_etime,    sr_nop,    11,   0,    U98, ET|RIGHT}, /* was 7 wide */
1324 {"euid",      "EUID",    pr_euid,     sr_euid,    5,   0,    LNX, ET|RIGHT},
1325 {"euser",     "EUSER",   pr_euser,    sr_euser,   8, USR,    LNX, ET|USER},
1326 {"f",         "F",       pr_flag,     sr_flags,   1,   0,    XXX, ET|RIGHT}, /*flags*/
1327 {"fgid",      "FGID",    pr_fgid,     sr_fgid,    5,   0,    LNX, ET|RIGHT},
1328 {"fgroup",    "FGROUP",  pr_fgroup,   sr_fgroup,  8, GRP,    LNX, ET|USER},
1329 {"flag",      "F",       pr_flag,     sr_flags,   1,   0,    DEC, ET|RIGHT},
1330 {"flags",     "F",       pr_flag,     sr_flags,   1,   0,    BSD, ET|RIGHT}, /*f*/ /* was FLAGS, 8 wide */
1331 {"fname",     "COMMAND", pr_fname,    sr_nop,     8,   0,    SUN, PO|LEFT},
1332 {"fsgid",     "FSGID",   pr_fgid,     sr_fgid,    5,   0,    LNX, ET|RIGHT},
1333 {"fsgroup",   "FSGROUP", pr_fgroup,   sr_fgroup,  8, GRP,    LNX, ET|USER},
1334 {"fsuid",     "FSUID",   pr_fuid,     sr_fuid,    5,   0,    LNX, ET|RIGHT},
1335 {"fsuser",    "FSUSER",  pr_fuser,    sr_fuser,   8, USR,    LNX, ET|USER},
1336 {"fuid",      "FUID",    pr_fuid,     sr_fuid,    5,   0,    LNX, ET|RIGHT},
1337 {"fuser",     "FUSER",   pr_fuser,    sr_fuser,   8, USR,    LNX, ET|USER},
1338 {"gid",       "GID",     pr_egid,     sr_egid,    5,   0,    SUN, ET|RIGHT},
1339 {"group",     "GROUP",   pr_egroup,   sr_egroup,  8, GRP,    U98, ET|USER},
1340 {"ignored",   "IGNORED", pr_sigignore,sr_nop,     9,   0,    BSD, TO|SIGNAL}, /*sigignore*/
1341 {"inblk",     "INBLK",   pr_nop,      sr_nop,     5,   0,    BSD, AN|RIGHT}, /*inblock*/
1342 {"inblock",   "INBLK",   pr_nop,      sr_nop,     5,   0,    DEC, AN|RIGHT}, /*inblk*/
1343 {"intpri",    "PRI",     pr_opri,     sr_priority, 3,  0,    HPU, TO|RIGHT},
1344 {"jid",       "JID",     pr_nop,      sr_nop,     1,   0,    SGI, PO|RIGHT},
1345 {"jobc",      "JOBC",    pr_nop,      sr_nop,     4,   0,    XXX, AN|RIGHT},
1346 {"ktrace",    "KTRACE",  pr_nop,      sr_nop,     8,   0,    BSD, AN|RIGHT},
1347 {"ktracep",   "KTRACEP", pr_nop,      sr_nop,     8,   0,    BSD, AN|RIGHT},
1348 {"label",     "LABEL",   pr_context,  sr_nop,    31,  0,     SGI, ET|LEFT},
1349 {"lastcpu",   "C",       pr_psr,      sr_nop,     3,   0,    BSD, TO|RIGHT}, // DragonFly
1350 {"lim",       "LIM",     pr_lim,      sr_rss_rlim, 5,  0,    BSD, AN|RIGHT},
1351 {"login",     "LOGNAME", pr_nop,      sr_nop,     8,   0,    BSD, AN|LEFT}, /*logname*/   /* double check */
1352 {"logname",   "LOGNAME", pr_nop,      sr_nop,     8,   0,    XXX, AN|LEFT}, /*login*/
1353 {"longtname", "TTY",     pr_tty8,     sr_tty,     8,   0,    DEC, PO|LEFT},
1354 {"lstart",    "STARTED", pr_lstart,   sr_nop,    24,   0,    XXX, ET|RIGHT},
1355 {"luid",      "LUID",    pr_nop,      sr_nop,     5,   0,    LNX, ET|RIGHT}, /* login ID */
1356 {"luser",     "LUSER",   pr_nop,      sr_nop,     8, USR,    LNX, ET|USER}, /* login USER */
1357 {"lwp",       "LWP",     pr_thread,   sr_tid,     5,   0,    SUN, TO|PIDMAX|RIGHT},
1358 {"m_drs",     "DRS",     pr_drs,      sr_drs,     5, MEM,    LNx, PO|RIGHT},
1359 {"m_dt",      "DT",      pr_nop,      sr_dt,      4, MEM,    LNx, PO|RIGHT},
1360 {"m_lrs",     "LRS",     pr_nop,      sr_lrs,     5, MEM,    LNx, PO|RIGHT},
1361 {"m_resident", "RES",    pr_nop,      sr_resident, 5,MEM,    LNx, PO|RIGHT},
1362 {"m_share",   "SHRD",    pr_nop,      sr_share,   5, MEM,    LNx, PO|RIGHT},
1363 {"m_size",    "SIZE",    pr_size,     sr_size,    5, MEM,    LNX, PO|RIGHT},
1364 {"m_swap",    "SWAP",    pr_nop,      sr_nop,     5,   0,    LNx, PO|RIGHT},
1365 {"m_trs",     "TRS",     pr_trs,      sr_trs,     5, MEM,    LNx, PO|RIGHT},
1366 {"maj_flt",   "MAJFL",   pr_majflt,   sr_maj_flt, 6,   0,    LNX, AN|RIGHT},
1367 {"majflt",    "MAJFLT",  pr_majflt,   sr_maj_flt, 6,   0,    XXX, AN|RIGHT},
1368 {"min_flt",   "MINFL",   pr_minflt,   sr_min_flt, 6,   0,    LNX, AN|RIGHT},
1369 {"minflt",    "MINFLT",  pr_minflt,   sr_min_flt, 6,   0,    XXX, AN|RIGHT},
1370 {"msgrcv",    "MSGRCV",  pr_nop,      sr_nop,     6,   0,    XXX, AN|RIGHT},
1371 {"msgsnd",    "MSGSND",  pr_nop,      sr_nop,     6,   0,    XXX, AN|RIGHT},
1372 {"mwchan",    "MWCHAN",  pr_nop,      sr_nop,     6, WCH,    BSD, TO|WCHAN}, /* mutex (FreeBSD) */
1373 {"ni",        "NI",      pr_nice,     sr_nice,    3,   0,    BSD, TO|RIGHT}, /*nice*/
1374 {"nice",      "NI",      pr_nice,     sr_nice,    3,   0,    U98, TO|RIGHT}, /*ni*/
1375 {"nivcsw",    "IVCSW",   pr_nop,      sr_nop,     5,   0,    XXX, AN|RIGHT},
1376 {"nlwp",      "NLWP",    pr_nlwp,     sr_nlwp,    4,   0,    SUN, PO|RIGHT},
1377 {"nsignals",  "NSIGS",   pr_nop,      sr_nop,     5,   0,    DEC, AN|RIGHT}, /*nsigs*/
1378 {"nsigs",     "NSIGS",   pr_nop,      sr_nop,     5,   0,    BSD, AN|RIGHT}, /*nsignals*/
1379 {"nswap",     "NSWAP",   pr_nop,      sr_nop,     5,   0,    XXX, AN|RIGHT},
1380 {"nvcsw",     "VCSW",    pr_nop,      sr_nop,     5,   0,    XXX, AN|RIGHT},
1381 {"nwchan",    "WCHAN",   pr_nwchan,   sr_nop,     6,   0,    XXX, TO|RIGHT},
1382 {"opri",      "PRI",     pr_opri,     sr_priority, 3,  0,    SUN, TO|RIGHT},
1383 {"osz",       "SZ",      pr_nop,      sr_nop,     2,   0,    SUN, PO|RIGHT},
1384 {"oublk",     "OUBLK",   pr_nop,      sr_nop,     5,   0,    BSD, AN|RIGHT}, /*oublock*/
1385 {"oublock",   "OUBLK",   pr_nop,      sr_nop,     5,   0,    DEC, AN|RIGHT}, /*oublk*/
1386 {"p_ru",      "P_RU",    pr_nop,      sr_nop,     6,   0,    BSD, AN|RIGHT},
1387 {"paddr",     "PADDR",   pr_nop,      sr_nop,     6,   0,    BSD, AN|RIGHT},
1388 {"pagein",    "PAGEIN",  pr_majflt,   sr_maj_flt, 6,   0,    XXX, AN|RIGHT},
1389 {"pcpu",      "%CPU",    pr_pcpu,     sr_pcpu,    4,   0,    U98, ET|RIGHT}, /*%cpu*/
1390 {"pending",   "PENDING", pr_sig,      sr_nop,     9,   0,    BSD, ET|SIGNAL}, /*sig*/
1391 {"pgid",      "PGID",    pr_pgid,     sr_pgrp,    5,   0,    U98, PO|PIDMAX|RIGHT},
1392 {"pgrp",      "PGRP",    pr_pgid,     sr_pgrp,    5,   0,    LNX, PO|PIDMAX|RIGHT},
1393 {"pid",       "PID",     pr_pid,      sr_tgid,    5,   0,    U98, PO|PIDMAX|RIGHT},
1394 {"pmem",      "%MEM",    pr_pmem,     sr_nop,     4,   0,    XXX, PO|RIGHT}, /*%mem*/
1395 {"poip",      "-",       pr_nop,      sr_nop,     1,   0,    BSD, AN|RIGHT},
1396 {"policy",    "POL",     pr_class,    sr_sched,   3,   0,    DEC, TO|LEFT},
1397 {"ppid",      "PPID",    pr_ppid,     sr_ppid,    5,   0,    U98, PO|PIDMAX|RIGHT},
1398 {"pri",       "PRI",     pr_pri,      sr_nop,     3,   0,    XXX, TO|RIGHT},
1399 {"pri_api",   "API",     pr_pri_api,  sr_nop,     3,   0,    LNX, TO|RIGHT},
1400 {"pri_bar",   "BAR",     pr_pri_bar,  sr_nop,     3,   0,    LNX, TO|RIGHT},
1401 {"pri_baz",   "BAZ",     pr_pri_baz,  sr_nop,     3,   0,    LNX, TO|RIGHT},
1402 {"pri_foo",   "FOO",     pr_pri_foo,  sr_nop,     3,   0,    LNX, TO|RIGHT},
1403 {"priority",  "PRI",     pr_priority, sr_priority, 3,  0,    LNX, TO|RIGHT},
1404 {"prmgrp",    "PRMGRP",  pr_nop,      sr_nop,    12,   0,    HPU, PO|RIGHT},
1405 {"prmid",     "PRMID",   pr_nop,      sr_nop,    12,   0,    HPU, PO|RIGHT},
1406 {"project",   "PROJECT", pr_nop,      sr_nop,    12,   0,    SUN, PO|LEFT}, // see prm* andctid
1407 {"projid",    "PROJID",  pr_nop,      sr_nop,     5,   0,    SUN, PO|RIGHT},
1408 {"pset",      "PSET",    pr_nop,      sr_nop,     4,   0,    DEC, TO|RIGHT},
1409 {"psr",       "PSR",     pr_psr,      sr_nop,     3,   0,    DEC, TO|RIGHT},
1410 {"psxpri",    "PPR",     pr_nop,      sr_nop,     3,   0,    DEC, TO|RIGHT},
1411 {"re",        "RE",      pr_nop,      sr_nop,     3,   0,    BSD, AN|RIGHT},
1412 {"resident",  "RES",     pr_nop,      sr_resident, 5,MEM,    LNX, PO|RIGHT},
1413 {"rgid",      "RGID",    pr_rgid,     sr_rgid,    5,   0,    XXX, ET|RIGHT},
1414 {"rgroup",    "RGROUP",  pr_rgroup,   sr_rgroup,  8, GRP,    U98, ET|USER}, /* was 8 wide */
1415 {"rlink",     "RLINK",   pr_nop,      sr_nop,     8,   0,    BSD, AN|RIGHT},
1416 {"rss",       "RSS",     pr_rss,      sr_rss,     5,   0,    XXX, PO|RIGHT}, /* was 5 wide */
1417 {"rssize",    "RSS",     pr_rss,      sr_vm_rss,  5,   0,    DEC, PO|RIGHT}, /*rsz*/
1418 {"rsz",       "RSZ",     pr_rss,      sr_vm_rss,  5,   0,    BSD, PO|RIGHT}, /*rssize*/
1419 {"rtprio",    "RTPRIO",  pr_rtprio,   sr_rtprio,  6,   0,    BSD, TO|RIGHT},
1420 {"ruid",      "RUID",    pr_ruid,     sr_ruid,    5,   0,    XXX, ET|RIGHT},
1421 {"ruser",     "RUSER",   pr_ruser,    sr_ruser,   8, USR,    U98, ET|USER},
1422 {"s",         "S",       pr_s,        sr_state,   1,   0,    SUN, TO|LEFT}, /*stat,state*/
1423 {"sched",     "SCH",     pr_sched,    sr_sched,   3,   0,    AIX, TO|RIGHT},
1424 {"scnt",      "SCNT",    pr_nop,      sr_nop,     4,   0,    DEC, AN|RIGHT},  /* man page misspelling of scount? */
1425 {"scount",    "SC",      pr_nop,      sr_nop,     4,   0,    AIX, AN|RIGHT},  /* scnt==scount, DEC claims both */
1426 {"sess",      "SESS",    pr_sess,     sr_session, 5,   0,    XXX, PO|PIDMAX|RIGHT},
1427 {"session",   "SESS",    pr_sess,     sr_session, 5,   0,    LNX, PO|PIDMAX|RIGHT},
1428 {"sgi_p",     "P",       pr_sgi_p,    sr_nop,     1,   0,    LNX, TO|RIGHT}, /* "cpu" number */
1429 {"sgi_rss",   "RSS",     pr_rss,      sr_nop,     4,   0,    LNX, PO|LEFT}, /* SZ:RSS */
1430 {"sgid",      "SGID",    pr_sgid,     sr_sgid,    5,   0,    LNX, ET|RIGHT},
1431 {"sgroup",    "SGROUP",  pr_sgroup,   sr_sgroup,  8, GRP,    LNX, ET|USER},
1432 {"share",     "-",       pr_nop,      sr_share,   1, MEM,    LNX, PO|RIGHT},
1433 {"sid",       "SID",     pr_sess,     sr_session, 5,   0,    XXX, PO|PIDMAX|RIGHT}, /* Sun & HP */
1434 {"sig",       "PENDING", pr_sig,      sr_nop,     9,   0,    XXX, ET|SIGNAL}, /*pending -- Dragonfly uses this for whole-proc and "tsig" for thread */
1435 {"sig_block", "BLOCKED",  pr_sigmask, sr_nop,     9,   0,    LNX, TO|SIGNAL},
1436 {"sig_catch", "CATCHED", pr_sigcatch, sr_nop,     9,   0,    LNX, TO|SIGNAL},
1437 {"sig_ignore", "IGNORED",pr_sigignore, sr_nop,    9,   0,    LNX, TO|SIGNAL},
1438 {"sig_pend",  "SIGNAL",   pr_sig,     sr_nop,     9,   0,    LNX, ET|SIGNAL},
1439 {"sigcatch",  "CAUGHT",  pr_sigcatch, sr_nop,     9,   0,    XXX, TO|SIGNAL}, /*caught*/
1440 {"sigignore", "IGNORED", pr_sigignore,sr_nop,     9,   0,    XXX, TO|SIGNAL}, /*ignored*/
1441 {"sigmask",   "BLOCKED", pr_sigmask,  sr_nop,     9,   0,    XXX, TO|SIGNAL}, /*blocked*/
1442 {"size",      "SZ",      pr_swapable, sr_swapable, 5,  0,    SCO, PO|RIGHT},
1443 {"sl",        "SL",      pr_nop,      sr_nop,     3,   0,    XXX, AN|RIGHT},
1444 {"spid",      "SPID",    pr_thread,   sr_tid,     5,   0,    SGI, TO|PIDMAX|RIGHT},
1445 {"stackp",    "STACKP",  pr_stackp,   sr_start_stack, 8, 0,  LNX, PO|RIGHT}, /*start_stack*/
1446 {"start",     "STARTED", pr_start,    sr_nop,     8,   0,    XXX, ET|RIGHT},
1447 {"start_code", "S_CODE",  pr_nop,     sr_start_code, 8, 0,   LNx, PO|RIGHT},
1448 {"start_stack", "STACKP", pr_stackp,  sr_start_stack, 8, 0,  LNX, PO|RIGHT}, /*stackp*/
1449 {"start_time", "START",  pr_stime,    sr_start_time, 5, 0,   LNx, ET|RIGHT},
1450 {"stat",      "STAT",    pr_stat,     sr_state,   4,   0,    BSD, TO|LEFT}, /*state,s*/
1451 {"state",     "S",       pr_s,        sr_state,   1,   0,    XXX, TO|LEFT}, /*stat,s*/ /* was STAT */
1452 {"status",    "STATUS",  pr_nop,      sr_nop,     6,   0,    DEC, AN|RIGHT},
1453 {"stime",     "STIME",   pr_stime,    sr_stime,   5,   0,    XXX, ET|RIGHT}, /* was 6 wide */
1454 {"suid",      "SUID",    pr_suid,     sr_suid,    5,   0,    LNx, ET|RIGHT},
1455 {"suser",     "SUSER",   pr_suser,    sr_suser,   8, USR,    LNx, ET|USER},
1456 {"svgid",     "SVGID",   pr_sgid,     sr_sgid,    5,   0,    XXX, ET|RIGHT},
1457 {"svgroup",   "SVGROUP", pr_sgroup,   sr_sgroup,  8, GRP,    LNX, ET|USER},
1458 {"svuid",     "SVUID",   pr_suid,     sr_suid,    5,   0,    XXX, ET|RIGHT},
1459 {"svuser",    "SVUSER",  pr_suser,    sr_suser,   8, USR,    LNX, ET|USER},
1460 {"systime",   "SYSTEM",  pr_nop,      sr_nop,     6,   0,    DEC, ET|RIGHT},
1461 {"sz",        "SZ",      pr_sz,       sr_nop,     5,   0,    HPU, PO|RIGHT},
1462 {"taskid",    "TASKID",  pr_nop,      sr_nop,     5,   0,    SUN, TO|PIDMAX|RIGHT}, // is this a thread ID?
1463 {"tdev",      "TDEV",    pr_nop,      sr_nop,     4,   0,    XXX, AN|RIGHT},
1464 {"thcount",   "THCNT",   pr_nlwp,     sr_nlwp,    5,   0,    AIX, PO|RIGHT},
1465 {"tid",       "TID",     pr_thread,   sr_tid,     5,   0,    AIX, TO|PIDMAX|RIGHT},
1466 {"time",      "TIME",    pr_time,     sr_nop,     8,   0,    U98, ET|RIGHT}, /*cputime*/ /* was 6 wide */
1467 {"timeout",   "TMOUT",   pr_nop,      sr_nop,     5,   0,    LNX, AN|RIGHT}, // 2.0.xx era
1468 {"tmout",     "TMOUT",   pr_nop,      sr_nop,     5,   0,    LNX, AN|RIGHT}, // 2.0.xx era
1469 {"tname",     "TTY",     pr_tty8,     sr_tty,     8,   0,    DEC, PO|LEFT},
1470 {"tpgid",     "TPGID",   pr_tpgid,    sr_tpgid,   5,   0,    XXX, PO|PIDMAX|RIGHT},
1471 {"trs",       "TRS",     pr_trs,      sr_trs,     4, MEM,    AIX, PO|RIGHT},
1472 {"trss",      "TRSS",    pr_trs,      sr_trs,     4, MEM,    BSD, PO|RIGHT}, /* 4.3BSD NET/2 */
1473 {"tsess",     "TSESS",   pr_nop,      sr_nop,     5,   0,    BSD, PO|PIDMAX|RIGHT},
1474 {"tsession",  "TSESS",   pr_nop,      sr_nop,     5,   0,    DEC, PO|PIDMAX|RIGHT},
1475 {"tsid",      "TSID",    pr_nop,      sr_nop,     5,   0,    BSD, PO|PIDMAX|RIGHT},
1476 {"tsig",      "PENDING", pr_tsig,     sr_nop,     9,   0,    BSD, ET|SIGNAL}, /* Dragonfly used this for thread-specific, and "sig" for whole-proc */
1477 {"tsiz",      "TSIZ",    pr_tsiz,     sr_nop,     4,   0,    BSD, PO|RIGHT},
1478 {"tt",        "TT",      pr_tty8,     sr_tty,     8,   0,    BSD, PO|LEFT},
1479 {"tty",       "TT",      pr_tty8,     sr_tty,     8,   0,    U98, PO|LEFT}, /* Unix98 requires "TT" but has "TTY" too. :-( */  /* was 3 wide */
1480 {"tty4",      "TTY",     pr_tty4,     sr_tty,     4,   0,    LNX, PO|LEFT},
1481 {"tty8",      "TTY",     pr_tty8,     sr_tty,     8,   0,    LNX, PO|LEFT},
1482 {"u_procp",   "UPROCP",  pr_nop,      sr_nop,     6,   0,    DEC, AN|RIGHT},
1483 {"ucmd",      "CMD",     pr_comm,     sr_cmd,    15, COM,    DEC, PO|UNLIMITED}, /*ucomm*/
1484 {"ucomm",     "COMMAND", pr_comm,     sr_cmd,    15, COM,    XXX, PO|UNLIMITED}, /*comm*/
1485 {"uid",       "UID",     pr_euid,     sr_euid,    5,   0,    XXX, ET|RIGHT},
1486 {"uid_hack",  "UID",     pr_euser,    sr_euser,   8, USR,    XXX, ET|USER},
1487 {"umask",     "UMASK",   pr_nop,      sr_nop,     5,   0,    DEC, AN|RIGHT},
1488 {"uname",     "USER",    pr_euser,    sr_euser,   8, USR,    DEC, ET|USER}, /* man page misspelling of user? */
1489 {"upr",       "UPR",     pr_nop,      sr_nop,     3,   0,    BSD, TO|RIGHT}, /*usrpri*/
1490 {"uprocp",    "UPROCP",  pr_nop,      sr_nop,     8,   0,    BSD, AN|RIGHT},
1491 {"user",      "USER",    pr_euser,    sr_euser,   8, USR,    U98, ET|USER}, /* BSD n forces this to UID */
1492 {"usertime",  "USER",    pr_nop,      sr_nop,     4,   0,    DEC, ET|RIGHT},
1493 {"usrpri",    "UPR",     pr_nop,      sr_nop,     3,   0,    DEC, TO|RIGHT}, /*upr*/
1494 {"util",      "C",       pr_c,        sr_pcpu,    2,   0,    SGI, ET|RIGHT}, // not sure about "C"
1495 {"utime",     "UTIME",   pr_nop,      sr_utime,   6,   0,    LNx, ET|RIGHT},
1496 {"vm_data",   "DATA",    pr_nop,      sr_vm_data, 5,   0,    LNx, PO|RIGHT},
1497 {"vm_exe",    "EXE",     pr_nop,      sr_vm_exe,  5,   0,    LNx, PO|RIGHT},
1498 {"vm_lib",    "LIB",     pr_nop,      sr_vm_lib,  5,   0,    LNx, PO|RIGHT},
1499 {"vm_lock",   "LCK",     pr_nop,      sr_vm_lock, 3,   0,    LNx, PO|RIGHT},
1500 {"vm_stack",  "STACK",   pr_nop,      sr_vm_stack, 5,  0,    LNx, PO|RIGHT},
1501 {"vsize",     "VSZ",     pr_vsz,      sr_vsize,   6,   0,    DEC, PO|RIGHT}, /*vsz*/
1502 {"vsz",       "VSZ",     pr_vsz,      sr_vm_size, 6,   0,    U98, PO|RIGHT}, /*vsize*/
1503 {"wchan",     "WCHAN",   pr_wchan,    sr_wchan,   6, WCH,    XXX, TO|WCHAN}, /* BSD n forces this to nwchan */ /* was 10 wide */
1504 {"wname",     "WCHAN",   pr_wname,    sr_nop,     6, WCH,    SGI, TO|WCHAN}, /* opposite of nwchan */
1505 {"xstat",     "XSTAT",   pr_nop,      sr_nop,     5,   0,    BSD, AN|RIGHT},
1506 {"zone",      "ZONE",    pr_context,  sr_nop,    31,   0,    SUN, ET|LEFT}, // Solaris zone == Linux context?
1507 {"zoneid",    "ZONEID",  pr_nop,      sr_nop,    31,   0,    SUN, ET|RIGHT},// Linux only offers context names
1508 {"~",         "-",       pr_nop,      sr_nop,     1,   0,    LNX, AN|RIGHT}  /* NULL would ruin alphabetical order */
1509 };
1510
1511 #undef USER
1512 #undef LEFT
1513 #undef RIGHT
1514 #undef UNLIMITED
1515 #undef WCHAN
1516 #undef SIGNAL
1517 #undef PIDMAX
1518 #undef PO
1519 #undef TO
1520 #undef AN
1521 #undef ET
1522
1523 static const int format_array_count = sizeof(format_array)/sizeof(format_struct);
1524
1525
1526 /****************************** Macro formats *******************************/
1527 /* First X field may be NR, which is p->start_code>>26 printed with %2ld */
1528 /* That seems useless though, and Debian already killed it. */
1529 /* The ones marked "Digital" have the name defined, not just the data. */
1530 static const macro_struct macro_array[] = {
1531 {"DFMT",     "pid,tname,state,cputime,cmd"},         /* Digital's default */
1532 {"DefBSD",   "pid,tname,stat,bsdtime,args"},               /* Our BSD default */
1533 {"DefSysV",  "pid,tname,time,cmd"},                     /* Our SysV default */
1534 {"END_BSD",  "state,tname,cputime,comm"},                 /* trailer for O */
1535 {"END_SYS5", "state,tname,time,command"},                 /* trailer for -O */
1536 {"F5FMT",    "uname,pid,ppid,c,start,tname,time,cmd"},       /* Digital -f */
1537
1538 {"FB_",      "pid,tt,stat,time,command"},                          /* FreeBSD default */
1539 {"FB_j",     "user,pid,ppid,pgid,sess,jobc,stat,tt,time,command"},     /* FreeBSD j */
1540 {"FB_l",     "uid,pid,ppid,cpu,pri,nice,vsz,rss,wchan,stat,tt,time,command"},   /* FreeBSD l */
1541 {"FB_u",     "user,pid,pcpu,pmem,vsz,rss,tt,stat,start,time,command"},     /* FreeBSD u */
1542 {"FB_v",     "pid,stat,time,sl,re,pagein,vsz,rss,lim,tsiz,pcpu,pmem,command"},   /* FreeBSD v */
1543
1544 {"FD_",      "pid,tty,time,comm"},                                 /* Fictional Debian SysV default */
1545 {"FD_f",     "user,pid,ppid,start_time,tty,time,comm"},                /* Fictional Debian -f */
1546 {"FD_fj",    "user,pid,ppid,start_time,tty,time,pgid,sid,comm"},        /* Fictional Debian -jf */
1547 {"FD_j",     "pid,tty,time,pgid,sid,comm"},                                  /* Fictional Debian -j */
1548 {"FD_l",     "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,comm"},    /* Fictional Debian -l */
1549 {"FD_lj",    "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,pgid,sid,comm"}, /* Fictional Debian -jl */
1550
1551 {"FL5FMT",   "f,state,uid,pid,ppid,pcpu,pri,nice,rss,wchan,start,time,command"},  /* Digital -fl */
1552
1553 {"FLASK_context",   "pid,context,command"},  /* Flask Linux context, --context */
1554
1555 {"HP_",      "pid,tty,time,comm"},  /* HP default */
1556 {"HP_f",     "user,pid,ppid,cpu,stime,tty,time,args"},  /* HP -f */
1557 {"HP_fl",    "flags,state,user,pid,ppid,cpu,intpri,nice,addr,sz,wchan,stime,tty,time,args"},  /* HP -fl */
1558 {"HP_l",     "flags,state,uid,pid,ppid,cpu,intpri,nice,addr,sz,wchan,tty,time,comm"},  /* HP -l */
1559
1560 {"J390",     "pid,sid,pgrp,tname,atime,args"},   /* OS/390 -j */
1561 {"JFMT",     "user,pid,ppid,pgid,sess,jobc,state,tname,cputime,command"},   /* Digital j and -j */
1562 {"L5FMT",    "f,state,uid,pid,ppid,c,pri,nice,addr,sz,wchan,tt,time,ucmd"},   /* Digital -l */
1563 {"LFMT",     "uid,pid,ppid,cp,pri,nice,vsz,rss,wchan,state,tname,cputime,command"},   /* Digital l */
1564
1565 {"OL_X",     "pid,start_stack,esp,eip,timeout,alarm,stat,tname,bsdtime,args"},      /* Old i386 Linux X */
1566 {"OL_j",     "ppid,pid,pgid,sid,tname,tpgid,stat,uid,bsdtime,args"},                   /* Old Linux j */
1567 {"OL_l",     "flags,uid,pid,ppid,priority,nice,vsz,rss,wchan,stat,tname,bsdtime,args"},     /* Old Linux l */
1568 {"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 */
1569 {"OL_s",     "uid,pid,pending,sig_block,sig_ignore,caught,stat,tname,bsdtime,args"},  /* Old Linux s */
1570 {"OL_u",     "user,pid,pcpu,pmem,vsz,rss,tname,stat,start_time,bsdtime,args"},       /* Old Linux u */
1571 {"OL_v",     "pid,tname,stat,bsdtime,maj_flt,m_trs,m_drs,rss,pmem,args"},            /* Old Linux v */
1572
1573 {"RD_",      "pid,tname,state,bsdtime,comm"},                                       /* Real Debian default */
1574 {"RD_f",     "uid,pid,ppid,start_time,tname,bsdtime,args"},                         /* Real Debian -f */
1575 {"RD_fj",    "uid,pid,ppid,start_time,tname,bsdtime,pgid,sid,args"},                /* Real Debian -jf */
1576 {"RD_j",     "pid,tname,state,bsdtime,pgid,sid,comm"},                               /* Real Debian -j */
1577 {"RD_l",     "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,comm"},           /* Real Debian -l */
1578 {"RD_lj",    "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,pgid,sid,comm"},  /* Real Debian -jl */
1579
1580 {"RUSAGE",   "minflt,majflt,nswap,inblock,oublock,msgsnd,msgrcv,nsigs,nvcsw,nivcsw"}, /* Digital -o "RUSAGE" */
1581 {"SCHED",    "user,pcpu,pri,usrpri,nice,psxpri,psr,policy,pset"},                /* Digital -o "SCHED" */
1582 {"SFMT",     "uid,pid,cursig,sig,sigmask,sigignore,sigcatch,stat,tname,command"},  /* Digital s */
1583
1584 {"Std_f",    "uid_hack,pid,ppid,c,stime,tname,time,cmd"},                     /* new -f */
1585 {"Std_fl",   "f,s,uid_hack,pid,ppid,c,opri,ni,addr,sz,wchan,stime,tname,time,cmd"}, /* -fl */
1586 {"Std_l",    "f,s,uid,pid,ppid,c,opri,ni,addr,sz,wchan,tname,time,ucmd"},  /* new -l */
1587
1588 {"THREAD",   "user,pcpu,pri,scnt,wchan,usertime,systime"},                /* Digital -o "THREAD" */
1589 {"UFMT",     "uname,pid,pcpu,pmem,vsz,rss,tt,state,start,time,command"},   /* Digital u */
1590 {"VFMT",     "pid,tt,state,time,sl,pagein,vsz,rss,pcpu,pmem,command"},   /* Digital v */
1591 {"~", "~"} /* NULL would ruin alphabetical order */
1592 };
1593
1594 static const int macro_array_count = sizeof(macro_array)/sizeof(macro_struct);
1595
1596
1597 /*************************** AIX formats ********************/
1598 /* Convert AIX format codes to normal format specifiers. */
1599 static const aix_struct aix_array[] = {
1600 {'C', "pcpu",   "%CPU"},
1601 {'G', "group",  "GROUP"},
1602 {'P', "ppid",   "PPID"},
1603 {'U', "user",   "USER"},
1604 {'a', "args",   "COMMAND"},
1605 {'c', "comm",   "COMMAND"},
1606 {'g', "rgroup", "RGROUP"},
1607 {'n', "nice",   "NI"},
1608 {'p', "pid",    "PID"},
1609 {'r', "pgid",   "PGID"},
1610 {'t', "etime",  "ELAPSED"},
1611 {'u', "ruser",  "RUSER"},
1612 {'x', "time",   "TIME"},
1613 {'y', "tty",    "TTY"},
1614 {'z', "vsz",    "VSZ"},
1615 {'~', "~",      "~"} /* NULL would ruin alphabetical order */
1616 };
1617 static const int aix_array_count = sizeof(aix_array)/sizeof(aix_struct);
1618
1619
1620 /********************* sorting ***************************/
1621 /* Convert short sorting codes to normal format specifiers. */
1622 static const shortsort_struct shortsort_array[] = {
1623 {'C', "pcpu"       },
1624 {'G', "tpgid"      },
1625 {'J', "cstime"     },
1626 /* {'K', "stime"      }, */  /* conflict, system vs. start time */
1627 {'M', "maj_flt"    },
1628 {'N', "cmaj_flt"   },
1629 {'P', "ppid"       },
1630 {'R', "resident"   },
1631 {'S', "share"      },
1632 {'T', "start_time" },
1633 {'U', "uid"        }, /* euid */
1634 {'c', "cmd"        },
1635 {'f', "flags"      },
1636 {'g', "pgrp"       },
1637 {'j', "cutime"     },
1638 {'k', "utime"      },
1639 {'m', "min_flt"    },
1640 {'n', "cmin_flt"   },
1641 {'o', "session"    },
1642 {'p', "pid"        },
1643 {'r', "rss"        },
1644 {'s', "size"       },
1645 {'t', "tty"        },
1646 {'u', "user"       },
1647 {'v', "vsize"      },
1648 {'y', "priority"   }, /* nice */
1649 {'~', "~"          } /* NULL would ruin alphabetical order */
1650 };
1651 static const int shortsort_array_count = sizeof(shortsort_array)/sizeof(shortsort_struct);
1652
1653
1654 /*********** print format_array **********/
1655 /* called by the parser in another file */
1656 void print_format_specifiers(void){
1657   const format_struct *walk = format_array;
1658   while(*(walk->spec) != '~'){
1659     if(walk->pr != pr_nop) printf("%-12.12s %-8.8s\n", walk->spec, walk->head);
1660     walk++;
1661   }
1662 }
1663
1664 /************ comparison functions for bsearch *************/
1665
1666 static int compare_format_structs(const void *a, const void *b){
1667   return strcmp(((const format_struct*)a)->spec,((const format_struct*)b)->spec);
1668 }
1669
1670 static int compare_macro_structs(const void *a, const void *b){
1671   return strcmp(((const macro_struct*)a)->spec,((const macro_struct*)b)->spec);
1672 }
1673
1674 /******** look up structs as needed by the sort & format parsers ******/
1675
1676 const shortsort_struct *search_shortsort_array(const int findme){
1677   const shortsort_struct *walk = shortsort_array;
1678   while(walk->desc != '~'){
1679     if(walk->desc == findme) return walk;
1680     walk++;
1681   }
1682   return NULL;
1683 }
1684
1685 const aix_struct *search_aix_array(const int findme){
1686   const aix_struct *walk = aix_array;
1687   while(walk->desc != '~'){
1688     if(walk->desc == findme) return walk;
1689     walk++;
1690   }
1691   return NULL;
1692 }
1693
1694 const format_struct *search_format_array(const char *findme){
1695   format_struct key;
1696   key.spec = findme;
1697   return bsearch(&key, format_array, format_array_count,
1698     sizeof(format_struct), compare_format_structs
1699   );
1700 }
1701
1702 const macro_struct *search_macro_array(const char *findme){
1703   macro_struct key;
1704   key.spec = findme;
1705   return bsearch(&key, macro_array, macro_array_count,
1706     sizeof(macro_struct), compare_macro_structs
1707   );
1708 }
1709
1710 static unsigned int active_cols;  /* some multiple of screen_cols */
1711
1712 /***** Last chance, avoid needless trunctuation. */
1713 static void check_header_width(void){
1714   format_node *walk = format_list;
1715   unsigned int total = 0;
1716   int was_normal = 0;
1717   unsigned int i = 0;
1718   unsigned int sigs = 0;
1719   while(walk){
1720     switch((walk->flags) & CF_JUST_MASK){
1721     default:
1722       total += walk->width;
1723       total += was_normal;
1724       was_normal = 1;
1725       break;
1726     case CF_SIGNAL:
1727       sigs++;
1728       total += walk->width;
1729       total += was_normal;
1730       was_normal = 1;
1731       break;
1732     case CF_UNLIMITED:  /* could chop this a bit */
1733       if(walk->next) total += walk->width;
1734       else total += 3; /* not strlen(walk->name) */
1735       total += was_normal;
1736       was_normal = 1;
1737       break;
1738     case 0:  /* AIX */
1739       total += walk->width;
1740       was_normal = 0;
1741       break;
1742     }
1743     walk = walk->next;
1744   }
1745   for(;;){
1746     i++;
1747     active_cols = screen_cols * i;
1748     if(active_cols>=total) break;
1749     if(screen_cols*i >= OUTBUF_SIZE/2) break; /* can't go over */
1750   }
1751   wide_signals = (total+sigs*7 <= active_cols);
1752 }
1753
1754
1755 /********** show one process (NULL proc prints header) **********/
1756
1757 //#define SPACE_AMOUNT page_size
1758 #define SPACE_AMOUNT 144
1759
1760 static char *saved_outbuf;
1761
1762 void show_one_proc(const proc_t *restrict const p, const format_node *restrict fmt){
1763   /* unknown: maybe set correct & actual to 1, remove +/- 1 below */
1764   int correct  = 0;  /* screen position we should be at */
1765   int actual   = 0;  /* screen position we are at */
1766   int amount   = 0;  /* amount of text that this data is */
1767   int leftpad  = 0;  /* amount of space this column _could_ need */
1768   int space    = 0;  /* amount of space we actually need to print */
1769   int dospace  = 0;  /* previous column determined that we need a space */
1770   int legit    = 0;  /* legitimately stolen extra space */
1771   int sz       = 0;  /* real size of data in outbuffer */
1772   int tmpspace = 0;
1773   char *restrict const outbuf = saved_outbuf;
1774   static int did_stuff = 0;  /* have we ever printed anything? */
1775
1776   if(unlikely(-1==(long)p)){    /* true only once, at the end */
1777     if(did_stuff) return;
1778     /* have _never_ printed anything, but might need a header */
1779     if(!--lines_to_next_header){
1780       lines_to_next_header = header_gap;
1781       show_one_proc(NULL,fmt);
1782     }
1783     /* fprintf(stderr, "No processes available.\n"); */  /* legal? */
1784     exit(1);
1785   }
1786   if(likely(p)){  /* not header, maybe we should call ourselves for it */
1787     if(unlikely(!--lines_to_next_header)){
1788       lines_to_next_header = header_gap;
1789       show_one_proc(NULL,fmt);
1790     }
1791   }
1792   did_stuff = 1;
1793   if(unlikely(active_cols>(int)OUTBUF_SIZE)) fprintf(stderr,"Fix bigness error.\n");
1794
1795   /* print row start sequence */
1796   for(;;){
1797     legit = 0;
1798     /* set width suggestion which might be ignored */
1799 //    if(likely(fmt->next)) max_rightward = fmt->width;
1800 //    else max_rightward = active_cols-((correct>actual) ? correct : actual);
1801
1802     if(likely(fmt->next)){
1803       max_rightward = fmt->width;
1804       tmpspace = 0;
1805     }else{
1806       tmpspace = correct-actual;
1807       if (tmpspace<1){
1808         tmpspace = dospace;
1809         max_rightward = active_cols-actual-tmpspace;
1810       }else{
1811         max_rightward = active_cols - ( (correct>actual) ? correct : actual );
1812       }
1813     }
1814     max_leftward  = fmt->width + actual - correct; /* TODO check this */
1815     
1816 //    fprintf(stderr, "cols: %d, max_rightward: %d, max_leftward: %d, actual: %d, correct: %d\n",
1817 //                  active_cols, max_rightward, max_leftward, actual, correct);
1818     
1819     /* prepare data and calculate leftpad */
1820     if(likely(p) && likely(fmt->pr)) amount = (*fmt->pr)(outbuf,p);
1821     else amount = strlen(strcpy(outbuf, fmt->name)); /* AIX or headers */
1822     
1823     switch((fmt->flags) & CF_JUST_MASK){
1824     case 0:  /* for AIX, assigned outside this file */
1825       leftpad = 0;
1826       break;
1827     case CF_LEFT:          /* bad */
1828       leftpad = 0;
1829       break;
1830     case CF_RIGHT:     /* OK */
1831       leftpad = fmt->width - amount;
1832       if(leftpad < 0) leftpad = 0;
1833       break;
1834     case CF_SIGNAL:
1835       /* if the screen is wide enough, use full 16-character output */
1836       if(wide_signals){
1837         leftpad = 16 - amount;
1838         legit = 7;
1839       }else{
1840         leftpad =  9 - amount;
1841       }
1842       if(leftpad < 0) leftpad = 0;
1843       break;
1844     case CF_USER:       /* bad */
1845       leftpad = fmt->width - amount;
1846       if(leftpad < 0) leftpad = 0;
1847       if(!user_is_number) leftpad = 0;
1848       break;
1849     case CF_WCHAN:       /* bad */
1850       if(wchan_is_number){
1851         leftpad = fmt->width - amount;
1852         if(leftpad < 0) leftpad = 0;
1853         break;
1854       }else{
1855         if ((active_cols-actual-tmpspace)<1)
1856           outbuf[1] = '\0';  /* oops, we (mostly) lose this column... */
1857         leftpad = 0;
1858         break;
1859       }
1860     case CF_UNLIMITED:
1861     {
1862       if(active_cols-actual-tmpspace < 1)
1863         outbuf[1] = '\0';    /* oops, we (mostly) lose this column... */
1864       leftpad = 0;
1865       break;
1866     }
1867     default:
1868       fprintf(stderr, "bad alignment code\n");
1869       break;
1870     }
1871     /* At this point:
1872      *
1873      * correct   from previous column
1874      * actual    from previous column
1875      * amount    not needed (garbage due to chopping)
1876      * leftpad   left padding for this column alone (not make-up or gap)
1877      * space     not needed (will recalculate now)
1878      * dospace   if we require space between this and the prior column
1879      * legit     space we were allowed to steal, and thus did steal
1880      */
1881     space = correct - actual + leftpad;
1882     if(space<1) space=dospace;
1883     if(unlikely(space>SPACE_AMOUNT)) space=SPACE_AMOUNT;  // only so much available
1884
1885     /* real size -- don't forget in 'amount' is number of cells */
1886     sz = strlen(outbuf);
1887     
1888     /* print data, set x position stuff */
1889     if(unlikely(!fmt->next)){
1890       /* Last column. Write padding + data + newline all together. */
1891       outbuf[sz] = '\n';
1892       fwrite(outbuf-space, space+sz+1, 1, stdout);
1893       break;
1894     }
1895     /* Not the last column. Write padding + data together. */
1896     fwrite(outbuf-space, space+sz, 1, stdout);
1897     actual  += space+amount;
1898     correct += fmt->width;
1899     correct += legit;        /* adjust for SIGNAL expansion */
1900     if(fmt->pr && fmt->next->pr){ /* neither is AIX filler */
1901       correct++;
1902       dospace = 1;
1903     }else{
1904       dospace = 0;
1905     }
1906     fmt = fmt->next;
1907     /* At this point:
1908      *
1909      * correct   screen position we should be at
1910      * actual    screen position we are at
1911      * amount    not needed
1912      * leftpad   not needed
1913      * space     not needed
1914      * dospace   if have determined that we need a space next time
1915      * legit     not needed
1916      */
1917   }
1918 }
1919
1920
1921 #ifdef TESTING
1922 static void sanity_check(void){
1923   format_struct *fs = format_array;
1924   while((fs->spec)[0] != '~'){
1925     if(strlen(fs->head) > fs->width) printf("%d %s\n",strlen(fs->head),fs->spec);
1926     fs++;
1927   }
1928 }
1929 #endif
1930
1931
1932 void init_output(void){
1933   int outbuf_pages;
1934   char *outbuf;
1935
1936   switch(page_size){
1937   case 65536: page_shift = 16; break;
1938   case 32768: page_shift = 15; break;
1939   case 16384: page_shift = 14; break;
1940   case  8192: page_shift = 13; break;
1941   default: fprintf(stderr, "Unknown page size! (assume 4096)\n");
1942   case  4096: page_shift = 12; break;
1943   case  2048: page_shift = 11; break;
1944   case  1024: page_shift = 10; break;
1945   }
1946
1947   // add page_size-1 to round up
1948   outbuf_pages = (OUTBUF_SIZE+SPACE_AMOUNT+page_size-1)/page_size;
1949   outbuf = mmap(
1950     0,
1951     page_size * (outbuf_pages+1), // 1 more, for guard page at high addresses
1952     PROT_READ | PROT_WRITE,
1953     MAP_PRIVATE | MAP_ANONYMOUS,
1954     -1,
1955     0
1956   );
1957   memset(outbuf, ' ', SPACE_AMOUNT);
1958   if(SPACE_AMOUNT==page_size) mprotect(outbuf, page_size, PROT_READ);
1959   mprotect(outbuf + page_size*outbuf_pages, page_size, PROT_NONE); // gaurd page
1960   saved_outbuf = outbuf + SPACE_AMOUNT;
1961   // available space:  page_size*outbuf_pages-SPACE_AMOUNT
1962
1963   seconds_since_1970 = time(NULL);
1964   time_of_boot = seconds_since_1970 - seconds_since_boot;
1965
1966   meminfo();
1967
1968   check_header_width();
1969 }