Updated with Tizen:Base source codes
[external/procps.git] / top.c
1 /* top.c - Source file:         show Linux processes */
2 /*
3  * Copyright (c) 2002, by:      James C. Warner
4  *    All rights reserved.      8921 Hilloway Road
5  *                              Eden Prairie, Minnesota 55347 USA
6  *                             <warnerjc@worldnet.att.net>
7  *
8  * This file may be used subject to the terms and conditions of the
9  * GNU Library General Public License Version 2, or any later version
10  * at your option, as published by the Free Software Foundation.
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Library General Public License for more details.
15  * 
16  * For their contributions to this program, the author wishes to thank:
17  *    Albert D. Cahalan, <albert@users.sf.net>
18  *    Craig Small, <csmall@small.dropbear.id.au>
19  *
20  * Changes by Albert Cahalan, 2002-2004.
21  */
22 #include <sys/ioctl.h>
23 #include <sys/resource.h>
24 #include <sys/time.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <ctype.h>
28 #include <curses.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <signal.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 // Foul POS defines all sorts of stuff...
38 #include <term.h>
39 #undef tab
40
41 #include <termios.h>
42 #include <time.h>
43 #include <unistd.h>
44 #include <values.h>
45
46 #include "proc/devname.h"
47 #include "proc/wchan.h"
48 #include "proc/procps.h"
49 #include "proc/readproc.h"
50 #include "proc/escape.h"
51 #include "proc/sig.h"
52 #include "proc/sysinfo.h"
53 #include "proc/version.h"
54 #include "proc/whattime.h"
55
56 #include "top.h"
57
58 /*######  Miscellaneous global stuff  ####################################*/
59
60         /* The original and new terminal attributes */
61 static struct termios Savedtty,
62                       Rawtty;
63 static int Ttychanged = 0;
64
65         /* Program name used in error messages and local 'rc' file name */
66 static char *Myname;
67
68         /* Name of user config file (dynamically constructed) and our
69            'Current' rcfile contents, initialized with defaults but may be
70            overridden with the local rcfile (old or new-style) values */
71 static char  Rc_name [OURPATHSZ];
72 static RCF_t Rc = DEF_RCFILE;
73
74         /* The run-time acquired page size */
75 static unsigned Page_size;
76 static unsigned page_to_kb_shift;
77
78         /* SMP Irix/Solaris mode */
79 static int  Cpu_tot;
80 static double pcpu_max_value;  // usually 99.9, for %CPU display
81         /* assume no IO-wait stats, overridden if linux 2.5.41 */
82 static const char *States_fmts = STATES_line2x4;
83
84         /* Specific process id monitoring support */
85 static pid_t Monpids [MONPIDMAX] = { 0 };
86 static int   Monpidsidx = 0;
87
88         /* A postponed error message */
89 static char Msg_delayed [SMLBUFSIZ];
90 static int  Msg_awaiting = 0;
91
92 // This is the select() timeout. Clear it in sig handlers to avoid a race.
93 // (signal happens just as we are about to select() and thus does not
94 // break us out of the select(), causing us to delay until timeout)
95 static volatile struct timeval tv;
96 #define ZAP_TIMEOUT do{tv.tv_usec=0; tv.tv_sec=0;}while(0);
97
98         /* Configurable Display support ##################################*/
99
100         /* Current screen dimensions.
101            note: the number of processes displayed is tracked on a per window
102                  basis (see the WIN_t).  Max_lines is the total number of
103                  screen rows after deducting summary information overhead. */
104         /* Current terminal screen size. */
105 static int Screen_cols, Screen_rows, Max_lines;
106
107 // set to 1 if writing to the last column would be troublesome
108 // (we don't distinguish the lowermost row from the other rows)
109 static int avoid_last_column;
110
111         /* This is really the number of lines needed to display the summary
112            information (0 - nn), but is used as the relative row where we
113            stick the cursor between frames. */
114 static int Msg_row;
115
116         /* Global/Non-windows mode stuff that is NOT persistent */
117 static int No_ksyms = -1,       // set to '0' if ksym avail, '1' otherwise
118            PSDBopen = 0,        // set to '1' if psdb opened (now postponed)
119            Batch = 0,           // batch mode, collect no input, dumb output
120            Loops = -1,          // number of iterations, -1 loops forever
121            Secure_mode = 0;     // set if some functionality restricted
122
123         /* Some cap's stuff to reduce runtime calls --
124            to accomodate 'Batch' mode, they begin life as empty strings */
125 static char  Cap_clr_eol    [CAPBUFSIZ],
126              Cap_clr_eos    [CAPBUFSIZ],
127              Cap_clr_scr    [CAPBUFSIZ],
128              Cap_rmam       [CAPBUFSIZ],
129              Cap_smam       [CAPBUFSIZ],
130              Cap_curs_norm  [CAPBUFSIZ],
131              Cap_curs_huge  [CAPBUFSIZ],
132              Cap_home       [CAPBUFSIZ],
133              Cap_norm       [CAPBUFSIZ],
134              Cap_reverse    [CAPBUFSIZ],
135              Caps_off       [CAPBUFSIZ];
136 static int   Cap_can_goto = 0;
137
138         /* Some optimization stuff, to reduce output demands...
139            The Pseudo_ guys are managed by wins_resize and frame_make.  They
140            are exploited in a macro and represent 90% of our optimization.
141            The Stdout_buf is transparent to our code and regardless of whose
142            buffer is used, stdout is flushed at frame end or if interactive. */
143 static char *Pseudo_scrn;
144 static int   Pseudo_row, Pseudo_cols, Pseudo_size;
145 #ifndef STDOUT_IOLBF
146         // less than stdout's normal buffer but with luck mostly '\n' anyway
147 static char  Stdout_buf[2048];
148 #endif
149
150
151         /* ////////////////////////////////////////////////////////////// */
152         /* Special Section: multiple windows/field groups  ---------------*/
153
154         /* The pointers to our four WIN_t's, and which of those is considered
155            the 'current' window (ie. which window is associated with any summ
156            info displayed and to which window commands are directed) */
157 static WIN_t Winstk [GROUPSMAX],
158              *Curwin;
159
160         /* Frame oriented stuff that can't remain local to any 1 function
161            and/or that would be too cumbersome managed as parms,
162            and/or that are simply more efficiently handled as globals
163            (first 2 persist beyond a single frame, changed infrequently) */
164 static int       Frames_libflags;       // PROC_FILLxxx flags (0 = need new)
165 //atic int       Frames_maxcmdln;       // the largest from the 4 windows
166 static unsigned  Frame_maxtask;         // last known number of active tasks
167                                         // ie. current 'size' of proc table
168 static unsigned  Frame_running,         // state categories for this frame
169                  Frame_sleepin,
170                  Frame_stopped,
171                  Frame_zombied;
172 static float     Frame_tscale;          // so we can '*' vs. '/' WHEN 'pcpu'
173 static int       Frame_srtflg,          // the subject window's sort direction
174                  Frame_ctimes,          // the subject window's ctimes flag
175                  Frame_cmdlin;          // the subject window's cmdlin flag
176         /* ////////////////////////////////////////////////////////////// */
177
178 \f
179 /*######  Sort callbacks  ################################################*/
180
181         /*
182          * These happen to be coded in the same order as the enum 'pflag'
183          * values.  Note that 2 of these routines serve double duty --
184          * 2 columns each.
185          */
186
187 SCB_NUMx(P_PID, XXXID)
188 SCB_NUMx(P_PPD, ppid)
189 SCB_STRx(P_URR, ruser)
190 SCB_NUMx(P_UID, euid)
191 SCB_STRx(P_URE, euser)
192 SCB_STRx(P_GRP, egroup)
193 SCB_NUMx(P_TTY, tty)
194 SCB_NUMx(P_PRI, priority)
195 SCB_NUMx(P_NCE, nice)
196 SCB_NUMx(P_CPN, processor)
197 SCB_NUM1(P_CPU, pcpu)
198                                         // also serves P_TM2 !
199 static int sort_P_TME (const proc_t **P, const proc_t **Q)
200 {
201    if (Frame_ctimes) {
202       if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime)
203         < ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
204            return SORT_lt;
205       if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime)
206         > ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
207            return SORT_gt;
208    } else {
209       if ( ((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime))
210          return SORT_lt;
211       if ( ((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime))
212          return SORT_gt;
213    }
214    return SORT_eq;
215 }
216
217 SCB_NUM1(P_VRT, size)
218 SCB_NUM2(P_SWP, size, resident)
219 SCB_NUM1(P_RES, resident)               // also serves P_MEM !
220 SCB_NUM1(P_COD, trs)
221 SCB_NUM1(P_DAT, drs)
222 SCB_NUM1(P_SHR, share)
223 SCB_NUM1(P_FLT, maj_flt)
224 SCB_NUM1(P_DRT, dt)
225 SCB_NUMx(P_STA, state)
226
227 static int sort_P_CMD (const proc_t **P, const proc_t **Q)
228 {
229    /* if a process doesn't have a cmdline, we'll consider it a kernel thread
230       -- since displayed tasks are given special treatment, we must too */
231    if (Frame_cmdlin && ((*P)->cmdline || (*Q)->cmdline)) {
232       if (!(*Q)->cmdline) return Frame_srtflg * -1;
233       if (!(*P)->cmdline) return Frame_srtflg;
234       return Frame_srtflg *
235          strncmp((*Q)->cmdline[0], (*P)->cmdline[0], (unsigned)Curwin->maxcmdln);
236    }
237    // this part also handles the compare if both are kernel threads
238    return Frame_srtflg * strcmp((*Q)->cmd, (*P)->cmd);
239 }
240
241 SCB_NUM1(P_WCH, wchan)
242 SCB_NUM1(P_FLG, flags)
243
244         /* ///////////////////////////////// special sort for prochlp() ! */
245 static int sort_HST_t (const HST_t *P, const HST_t *Q)
246 {
247    return P->pid - Q->pid;
248 }
249
250
251 /*######  Tiny useful routine(s)  ########################################*/
252
253         /*
254          * This routine isolates ALL user INPUT and ensures that we
255          * wont be mixing I/O from stdio and low-level read() requests */
256 static int chin (int ech, char *buf, unsigned cnt)
257 {
258    int rc;
259
260    fflush(stdout);
261    if (!ech)
262       rc = read(STDIN_FILENO, buf, cnt);
263    else {
264       tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
265       rc = read(STDIN_FILENO, buf, cnt);
266       tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty);
267    }
268    // may be the beginning of a lengthy escape sequence
269    tcflush(STDIN_FILENO, TCIFLUSH);
270    return rc;                   // note: we do NOT produce a vaid 'string'
271 }
272
273
274 // This routine simply formats whatever the caller wants and
275 // returns a pointer to the resulting 'const char' string...
276 static const char *fmtmk (const char *fmts, ...) __attribute__((format(printf,1,2)));
277 static const char *fmtmk (const char *fmts, ...)
278 {
279    static char buf[BIGBUFSIZ];          // with help stuff, our buffer
280    va_list va;                          // requirements exceed 1k
281
282    va_start(va, fmts);
283    vsnprintf(buf, sizeof(buf), fmts, va);
284    va_end(va);
285    return (const char *)buf;
286 }
287
288
289 // This guy is just our way of avoiding the overhead of the standard
290 // strcat function (should the caller choose to participate)
291 static inline char *scat (char *restrict dst, const char *restrict src)
292 {
293    while (*dst) dst++;
294    while ((*(dst++) = *(src++)));
295    return --dst;
296 }
297
298
299 // Trim the rc file lines and any 'open_psdb_message' result which arrives
300 // with an inappropriate newline (thanks to 'sysmap_mmap')
301 static char *strim_0 (char *str)
302 {
303    static const char ws[] = "\b\e\f\n\r\t\v\x9b";  // 0x9b is an escape
304    char *p;
305
306    if ((p = strpbrk(str, ws))) *p = 0;
307    return str;
308 }
309
310
311 // This guy just facilitates Batch and protects against dumb ttys
312 // -- we'd 'inline' him but he's only called twice per frame,
313 // yet used in many other locations.
314 static const char *tg2 (int x, int y)
315 {
316    return Cap_can_goto ? tgoto(cursor_address, x, y) : "";
317 }
318
319
320 /*######  Exit/Interrput routines  #######################################*/
321
322 // The usual program end -- called only by functions in this section.
323 static void bye_bye (FILE *fp, int eno, const char *str) NORETURN;
324 static void bye_bye (FILE *fp, int eno, const char *str)
325 {
326    if (!Batch)
327       tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
328    putp(tg2(0, Screen_rows));
329    putp(Cap_curs_norm);
330    putp(Cap_smam);
331    putp("\n");
332    fflush(stdout);
333
334 //#define ATEOJ_REPORT
335 #ifdef ATEOJ_REPORT
336
337    fprintf(fp,
338       "\n\tTerminal: %s"
339       "\n\t   device = %s, ncurses = v%s"
340       "\n\t   max_colors = %d, max_pairs = %d"
341       "\n\t   Cap_can_goto = %s"
342       "\n\t   Screen_cols = %d, Screen_rows = %d"
343       "\n\t   Max_lines = %d, most recent Pseudo_size = %d"
344       "\n"
345 #ifdef PRETENDNOCAP
346       , "dumb"
347 #else
348       , termname()
349 #endif
350       , ttyname(STDOUT_FILENO), NCURSES_VERSION
351       , max_colors, max_pairs
352       , Cap_can_goto ? "yes" : "No!"
353       , Screen_cols, Screen_rows
354       , Max_lines, Pseudo_size
355       );
356
357    fprintf(fp,
358 #ifndef STDOUT_IOLBF
359       "\n\t   Stdout_buf = %d, BUFSIZ = %u"
360 #endif
361       "\n\tWindows and Curwin->"
362       "\n\t   sizeof(WIN_t) = %u, GROUPSMAX = %d"
363       "\n\t   rc.winname = %s, grpname = %s"
364       "\n\t   rc.winflags = %08x, maxpflgs = %d"
365       "\n\t   rc.fieldscur = %s"
366       "\n\t   winlines  = %d, rc.maxtasks = %d, maxcmdln = %d"
367       "\n\t   rc.sortindx  = %d"
368       "\n"
369 #ifndef STDOUT_IOLBF
370       , sizeof(Stdout_buf), (unsigned)BUFSIZ
371 #endif
372       , sizeof(WIN_t), GROUPSMAX
373       , Curwin->rc.winname, Curwin->grpname
374       , Curwin->rc.winflags, Curwin->maxpflgs
375       , Curwin->rc.fieldscur
376       , Curwin->winlines, Curwin->rc.maxtasks, Curwin->maxcmdln
377       , Curwin->rc.sortindx
378       );
379
380    fprintf(fp,
381       "\n\tProgram"
382       "\n\t   Linux version = %u.%u.%u, %s"
383       "\n\t   Hertz = %u (%u bytes, %u-bit time)"
384       "\n\t   Page_size = %d, Cpu_tot = %d, sizeof(proc_t) = %u"
385       "\n\t   sizeof(CPU_t) = %u, sizeof(HST_t) = %u (%u HST_t's/Page)"
386       "\n"
387       , LINUX_VERSION_MAJOR(linux_version_code)
388       , LINUX_VERSION_MINOR(linux_version_code)
389       , LINUX_VERSION_PATCH(linux_version_code)
390       , procps_version
391       , (unsigned)Hertz, sizeof(Hertz), sizeof(Hertz) * 8
392       , Page_size, Cpu_tot, sizeof(proc_t)
393       , sizeof(CPU_t), sizeof(HST_t), Page_size / sizeof(HST_t)
394       );
395
396
397 #endif
398
399    if (str) fputs(str, fp);
400    exit(eno);
401 }
402
403
404         /*
405          * Normal end of execution.
406          * catches:
407          *    SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT and SIGTERM */
408 // FIXME: can't do this shit in a signal handler
409 static void end_pgm (int sig) NORETURN;
410 static void end_pgm (int sig)
411 {
412    if(sig)
413       sig |= 0x80; // for a proper process exit code
414    bye_bye(stdout, sig, NULL);
415 }
416
417
418         /*
419          * Standard error handler to normalize the look of all err o/p */
420 static void std_err (const char *str) NORETURN;
421 static void std_err (const char *str)
422 {
423    static char buf[SMLBUFSIZ];
424
425    fflush(stdout);
426    /* we'll use our own buffer so callers can still use fmtmk() and, yes the
427       leading tab is not the standard convention, but the standard is wrong
428       -- OUR msg won't get lost in screen clutter, like so many others! */
429    snprintf(buf, sizeof(buf), "\t%s: %s\n", Myname, str);
430    if (!Ttychanged) {
431       fprintf(stderr, "%s\n", buf);
432       exit(1);
433    }
434       /* not to worry, he'll change our exit code to 1 due to 'buf' */
435    bye_bye(stderr, 1, buf);
436 }
437
438
439         /*
440          * Standard out handler */
441 static void std_out (const char *str) NORETURN;
442 static void std_out (const char *str)
443 {
444    static char buf[SMLBUFSIZ];
445
446    fflush(stdout);
447    /* we'll use our own buffer so callers can still use fmtmk() and, yes the
448       leading tab is not the standard convention, but the standard is wrong
449       -- OUR msg won't get lost in screen clutter, like so many others! */
450    snprintf(buf, sizeof(buf), "\t%s: %s\n", Myname, str);
451    if (!Ttychanged) {
452       fprintf(stdout, "%s\n", buf);
453       exit(0);
454    }
455    bye_bye(stdout, 0, buf);
456 }
457
458
459         /*
460          * Suspend ourself.
461          * catches:
462          *    SIGTSTP, SIGTTIN and SIGTTOU */
463 // FIXME: can't do this shit in a signal handler!
464 static void suspend (int dont_care_sig)
465 {
466   (void)dont_care_sig;
467       /* reset terminal */
468    tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
469    putp(tg2(0, Screen_rows));
470    putp(Cap_curs_norm);
471    putp(Cap_smam);
472    putp("\n");
473    fflush(stdout);
474    raise(SIGSTOP);
475       /* later, after SIGCONT... */
476    ZAP_TIMEOUT
477    if (!Batch)
478       tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty);
479    putp(Cap_clr_scr);
480    putp(Cap_rmam);
481 }
482
483
484 /*######  Misc Color/Display support  ####################################*/
485
486    /* macro to test if a basic (non-color) capability is valid
487          thanks: Floyd Davidson <floyd@ptialaska.net> */
488 #define tIF(s)  s ? s : ""
489 #define CAPCOPY(dst,src) src && strcpy(dst,src)
490
491         /*
492          * Make the appropriate caps/color strings and set some
493          * lengths which are used to distinguish twix the displayed
494          * columns and an actual printed row!
495          * note: we avoid the use of background color so as to maximize
496          *       compatibility with the user's xterm settings */
497 static void capsmk (WIN_t *q)
498 {
499    static int capsdone = 0;
500
501    // we must NOT disturb our 'empty' terminfo strings!
502    if (Batch) return;
503
504    // these are the unchangeable puppies, so we only do 'em once
505    if (!capsdone) {
506       CAPCOPY(Cap_clr_eol, clr_eol);
507       CAPCOPY(Cap_clr_eos, clr_eos);
508       CAPCOPY(Cap_clr_scr, clear_screen);
509
510       if (!eat_newline_glitch) {  // we like the eat_newline_glitch
511          CAPCOPY(Cap_rmam, exit_am_mode);
512          CAPCOPY(Cap_smam, enter_am_mode);
513          if (!*Cap_rmam || !*Cap_smam) {  // need both
514             *Cap_rmam = '\0';
515             *Cap_smam = '\0';
516             if (auto_right_margin) {
517                avoid_last_column = 1;
518             }
519          }
520       }
521
522       CAPCOPY(Cap_curs_huge, cursor_visible);
523       CAPCOPY(Cap_curs_norm, cursor_normal);
524       CAPCOPY(Cap_home, cursor_home);
525       CAPCOPY(Cap_norm, exit_attribute_mode);
526       CAPCOPY(Cap_reverse, enter_reverse_mode);
527
528       snprintf(Caps_off, sizeof(Caps_off), "%s%s", Cap_norm, tIF(orig_pair));
529       if (tgoto(cursor_address, 1, 1)) Cap_can_goto = 1;
530       capsdone = 1;
531    }
532    /* the key to NO run-time costs for configurable colors -- we spend a
533       little time with the user now setting up our terminfo strings, and
534       the job's done until he/she/it has a change-of-heart */
535    strcpy(q->cap_bold, CHKw(q, View_NOBOLD) ? Cap_norm : tIF(enter_bold_mode));
536    if (CHKw(q, Show_COLORS) && max_colors > 0) {
537       strcpy(q->capclr_sum, tparm(set_a_foreground, q->rc.summclr));
538       snprintf(q->capclr_msg, sizeof(q->capclr_msg), "%s%s"
539          , tparm(set_a_foreground, q->rc.msgsclr), Cap_reverse);
540       snprintf(q->capclr_pmt, sizeof(q->capclr_pmt), "%s%s"
541          , tparm(set_a_foreground, q->rc.msgsclr), q->cap_bold);
542       snprintf(q->capclr_hdr, sizeof(q->capclr_hdr), "%s%s"
543          , tparm(set_a_foreground, q->rc.headclr), Cap_reverse);
544       snprintf(q->capclr_rownorm, sizeof(q->capclr_rownorm), "%s%s"
545          , Caps_off, tparm(set_a_foreground, q->rc.taskclr));
546    } else {
547       q->capclr_sum[0] = '\0';
548       strcpy(q->capclr_msg, Cap_reverse);
549       strcpy(q->capclr_pmt, q->cap_bold);
550       strcpy(q->capclr_hdr, Cap_reverse);
551       strcpy(q->capclr_rownorm, Cap_norm);
552    }
553    // composite(s), so we do 'em outside and after the if
554    snprintf(q->capclr_rowhigh, sizeof(q->capclr_rowhigh), "%s%s"
555       , q->capclr_rownorm, CHKw(q, Show_HIBOLD) ? q->cap_bold : Cap_reverse);
556    q->len_rownorm = strlen(q->capclr_rownorm);
557    q->len_rowhigh = strlen(q->capclr_rowhigh);
558
559 #undef tIF
560 }
561
562
563 // Show an error, but not right now.
564 // Due to the postponed opening of ksym, using open_psdb_message,
565 // if P_WCH had been selected and the program is restarted, the
566 // message would otherwise be displayed prematurely.
567 static void msg_save (const char *fmts, ...) __attribute__((format(printf,1,2)));
568 static void msg_save (const char *fmts, ...)
569 {
570    char tmp[SMLBUFSIZ];
571    va_list va;
572
573    va_start(va, fmts);
574    vsnprintf(tmp, sizeof(tmp), fmts, va);
575    va_end(va);
576       /* we'll add some extra attention grabbers to whatever this is */
577    snprintf(Msg_delayed, sizeof(Msg_delayed), "\a***  %s  ***", strim_0(tmp));
578    Msg_awaiting = 1;
579 }
580
581
582         /*
583          * Show an error message (caller may include a '\a' for sound) */
584 static void show_msg (const char *str)
585 {
586    PUTT("%s%s %s %s%s",
587       tg2(0, Msg_row),
588       Curwin->capclr_msg,
589       str,
590       Caps_off,
591       Cap_clr_eol
592    );
593    fflush(stdout);
594    sleep(MSG_SLEEP);
595    Msg_awaiting = 0;
596 }
597
598
599         /*
600          * Show an input prompt + larger cursor */
601 static void show_pmt (const char *str)
602 {
603    PUTT("%s%s%s: %s%s",
604       tg2(0, Msg_row),
605       Curwin->capclr_pmt,
606       str,
607       Cap_curs_huge,
608       Caps_off
609    );
610    fflush(stdout);
611 }
612
613
614         /*
615          * Show lines with specially formatted elements, but only output
616          * what will fit within the current screen width.
617          *    Our special formatting consists of:
618          *       "some text <_delimiter_> some more text <_delimiter_>...\n"
619          *    Where <_delimiter_> is a single byte in the range of:
620          *       \01 through \10  (in decimalizee, 1 - 8)
621          *    and is used to select an 'attribute' from a capabilities table
622          *    which is then applied to the *preceding* substring.
623          * Once recognized, the delimiter is replaced with a null character
624          * and viola, we've got a substring ready to output!  Strings or
625          * substrings without delimiters will receive the Cap_norm attribute.
626          *
627          * Caution:
628          *    This routine treats all non-delimiter bytes as displayable
629          *    data subject to our screen width marching orders.  If callers
630          *    embed non-display data like tabs or terminfo strings in our
631          *    glob, a line will truncate incorrectly at best.  Worse case
632          *    would be truncation of an embedded tty escape sequence.
633          *
634          *    Tabs must always be avoided or our efforts are wasted and
635          *    lines will wrap.  To lessen but not eliminate the risk of
636          *    terminfo string truncation, such non-display stuff should
637          *    be placed at the beginning of a "short" line.
638          *    (and as for tabs, gimme 1 more color then no worries, mate) */
639 static void show_special (int interact, const char *glob)
640 { /* note: the following is for documentation only,
641            the real captab is now found in a group's WIN_t !
642      +------------------------------------------------------+
643      | char *captab[] = {                 :   Cap's/Delim's |
644      |   Cap_norm, Cap_norm, Cap_bold,    =   \00, \01, \02 |
645      |   Sum_color,                       =   \03           |
646      |   Msg_color, Pmt_color,            =   \04, \05      |
647      |   Hdr_color,                       =   \06           |
648      |   Row_color_high,                  =   \07           |
649      |   Row_color_norm  };               =   \10 [octal!]  |
650      +------------------------------------------------------+ */
651    char lin[BIGBUFSIZ], row[ROWBUFSIZ], tmp[ROWBUFSIZ];
652    char *rp, *cap, *lin_end, *sub_beg, *sub_end;
653    int room;
654
655       /* handle multiple lines passed in a bunch */
656    while ((lin_end = strchr(glob, '\n'))) {
657
658          /* create a local copy we can extend and otherwise abuse */
659       size_t amt = lin_end - glob;
660       if(amt > sizeof lin - 1)
661          amt = sizeof lin - 1;  // shit happens
662       memcpy(lin, glob, amt);
663          /* zero terminate this part and prepare to parse substrings */
664       lin[amt] = '\0';
665       room = Screen_cols;
666       sub_beg = sub_end = lin;
667       *(rp = row) = '\0';
668
669       while (*sub_beg) {
670          switch (*sub_end) {
671             case 0:                     /* no end delim, captab makes normal */
672                *(sub_end + 1) = '\0';   /* extend str end, then fall through */
673             case 1 ... 8:
674                cap = Curwin->captab[(int)*sub_end];
675                *sub_end = '\0';
676                snprintf(tmp, sizeof(tmp), "%s%.*s%s", cap, room, sub_beg, Caps_off);
677                amt = strlen(tmp);
678                if(rp - row + amt + 1 > sizeof row)
679                   goto overflow;  // shit happens
680                rp = scat(rp, tmp);
681                room -= (sub_end - sub_beg);
682                sub_beg = ++sub_end;
683                break;
684             default:                    /* nothin' special, just text */
685                ++sub_end;
686          }
687          if (unlikely(0 >= room)) break; /* skip substrings that won't fit */
688       }
689 overflow:
690       if (interact) PUTT("%s%s\n", row, Cap_clr_eol);
691       else PUFF("%s%s\n", row, Cap_clr_eol);
692       glob = ++lin_end;                 /* point to next line (maybe) */
693    } /* end: while 'lines' */
694
695    /* If there's anything left in the glob (by virtue of no trailing '\n'),
696       it probably means caller wants to retain cursor position on this final
697       line.  That, in turn, means we're interactive and so we'll just do our
698       'fit-to-screen' thingy... */
699    if (*glob) PUTT("%.*s", Screen_cols, glob);
700 }
701
702
703 /*######  Small Utility routines  ########################################*/
704
705 // Get a string from the user
706 static char *ask4str (const char *prompt)
707 {
708    static char buf[GETBUFSIZ];
709
710    show_pmt(prompt);
711    memset(buf, '\0', sizeof(buf));
712    chin(1, buf, sizeof(buf) - 1);
713    putp(Cap_curs_norm);
714    return strim_0(buf);
715 }
716
717
718 // Get a float from the user
719 static float get_float (const char *prompt)
720 {
721    char *line;
722    float f;
723
724    if (!(*(line = ask4str(prompt)))) return -1;
725    // note: we're not allowing negative floats
726    if (strcspn(line, ",.1234567890")) {
727       show_msg("\aNot valid");
728       return -1;
729    }
730    sscanf(line, "%f", &f);
731    return f;
732 }
733
734
735 // Get an integer from the user
736 static int get_int (const char *prompt)
737 {
738    char *line;
739    int n;
740
741    if (!(*(line = ask4str(prompt)))) return -1;
742    // note: we've got to allow negative ints (renice)
743    if (strcspn(line, "-1234567890")) {
744       show_msg("\aNot valid");
745       return -1;
746    }
747    sscanf(line, "%d", &n);
748    return n;
749 }
750
751
752         /*
753          * Do some scaling stuff.
754          * We'll interpret 'num' as one of the following types and
755          * try to format it to fit 'width'.
756          *    SK_no (0) it's a byte count
757          *    SK_Kb (1) it's kilobytes
758          *    SK_Mb (2) it's megabytes
759          *    SK_Gb (3) it's gigabytes
760          *    SK_Tb (4) it's terabytes  */
761 static const char *scale_num (unsigned long num, const int width, const unsigned type)
762 {
763       /* kilobytes, megabytes, gigabytes, terabytes, duh! */
764    static double scale[] = { 1024.0, 1024.0*1024, 1024.0*1024*1024, 1024.0*1024*1024*1024, 0 };
765       /* kilo, mega, giga, tera, none */
766 #ifdef CASEUP_SCALE
767    static char nextup[] =  { 'K', 'M', 'G', 'T', 0 };
768 #else
769    static char nextup[] =  { 'k', 'm', 'g', 't', 0 };
770 #endif
771    static char buf[TNYBUFSIZ];
772    double *dp;
773    char *up;
774
775       /* try an unscaled version first... */
776    if (width >= snprintf(buf, sizeof(buf), "%lu", num)) return buf;
777
778       /* now try successively higher types until it fits */
779    for (up = nextup + type, dp = scale; *dp; ++dp, ++up) {
780          /* the most accurate version */
781       if (width >= snprintf(buf, sizeof(buf), "%.1f%c", num / *dp, *up))
782          return buf;
783          /* the integer version */
784       if (width >= snprintf(buf, sizeof(buf), "%ld%c", (unsigned long)(num / *dp), *up))
785          return buf;
786    }
787       /* well shoot, this outta' fit... */
788    return "?";
789 }
790
791
792         /*
793          * Do some scaling stuff.
794          * format 'tics' to fit 'width'. */
795 static const char *scale_tics (TIC_t tics, const int width)
796 {
797 #ifdef CASEUP_SCALE
798 #define HH "%uH"
799 #define DD "%uD"
800 #define WW "%uW"
801 #else
802 #define HH "%uh"
803 #define DD "%ud"
804 #define WW "%uw"
805 #endif
806    static char buf[TNYBUFSIZ];
807    unsigned long nt;    // narrow time, for speed on 32-bit
808    unsigned cc;         // centiseconds
809    unsigned nn;         // multi-purpose whatever
810
811    nt  = (tics * 100ull) / Hertz;
812    cc  = nt % 100;                              // centiseconds past second
813    nt /= 100;                                   // total seconds
814    nn  = nt % 60;                               // seconds past the minute
815    nt /= 60;                                    // total minutes
816    if (width >= snprintf(buf, sizeof(buf), "%lu:%02u.%02u", nt, nn, cc))
817       return buf;
818    if (width >= snprintf(buf, sizeof buf, "%lu:%02u", nt, nn))
819       return buf;
820    nn  = nt % 60;                               // minutes past the hour
821    nt /= 60;                                    // total hours
822    if (width >= snprintf(buf, sizeof buf, "%lu,%02u", nt, nn))
823       return buf;
824    nn = nt;                                     // now also hours
825    if (width >= snprintf(buf, sizeof buf, HH, nn))
826       return buf;
827    nn /= 24;                                    // now days
828    if (width >= snprintf(buf, sizeof buf, DD, nn))
829       return buf;
830    nn /= 7;                                     // now weeks
831    if (width >= snprintf(buf, sizeof buf, WW, nn))
832       return buf;
833       // well shoot, this outta' fit...
834    return "?";
835
836 #undef HH
837 #undef DD
838 #undef WW
839 }
840
841 #include <pwd.h>
842
843 static int selection_type;
844 static uid_t selection_uid;
845
846 // FIXME: this is "temporary" code we hope
847 static int good_uid(const proc_t *restrict const pp){
848    switch(selection_type){
849    case 'p':
850       return 1;
851    case 0:
852       return 1;
853    case 'U':
854       if (pp->ruid == selection_uid) return 1;
855       if (pp->suid == selection_uid) return 1;
856       if (pp->fuid == selection_uid) return 1;
857       // FALLTHROUGH
858    case 'u':
859       if (pp->euid == selection_uid) return 1;
860       // FALLTHROUGH
861    default:
862       ;  // don't know what it is; find bugs fast
863    }
864    return 0;
865 }
866
867 // swiped from ps, and ought to be in libproc
868 static const char *parse_uid(const char *restrict const str, uid_t *restrict const ret){
869    struct passwd *passwd_data;
870    char *endp;
871    unsigned long num;
872    static const char uidrange[] = "User ID out of range.";
873    static const char uidexist[] = "User name does not exist.";
874    num = strtoul(str, &endp, 0);
875    if(*endp != '\0'){  /* hmmm, try as login name */
876       passwd_data = getpwnam(str);
877       if(!passwd_data)    return uidexist;
878       num = passwd_data->pw_uid;
879    }
880    if(num > 0xfffffffeUL) return uidrange;
881    *ret = num;
882    return 0;
883 }
884
885
886 /*######  Library Alternatives  ##########################################*/
887
888         /*
889          * Handle our own memory stuff without the risk of leaving the
890          * user's terminal in an ugly state should things go sour. */
891
892 static void *alloc_c (unsigned numb) MALLOC;
893 static void *alloc_c (unsigned numb)
894 {
895    void * p;
896
897    if (!numb) ++numb;
898    if (!(p = calloc(1, numb)))
899       std_err("failed memory allocate");
900    return p;
901 }
902
903 static void *alloc_r (void *q, unsigned numb) MALLOC;
904 static void *alloc_r (void *q, unsigned numb)
905 {
906    void *p;
907
908    if (!numb) ++numb;
909    if (!(p = realloc(q, numb)))
910       std_err("failed memory allocate");
911    return p;
912 }
913
914
915         /*
916          * This guy's modeled on libproc's 'five_cpu_numbers' function except
917          * we preserve all cpu data in our CPU_t array which is organized
918          * as follows:
919          *    cpus[0] thru cpus[n] == tics for each separate cpu
920          *    cpus[Cpu_tot]        == tics from the 1st /proc/stat line */
921 static CPU_t *cpus_refresh (CPU_t *cpus)
922 {
923    static FILE *fp = NULL;
924    int i;
925    int num;
926    // enough for a /proc/stat CPU line (not the intr line)
927    char buf[SMLBUFSIZ];
928
929    /* by opening this file once, we'll avoid the hit on minor page faults
930       (sorry Linux, but you'll have to close it for us) */
931    if (!fp) {
932       if (!(fp = fopen("/proc/stat", "r")))
933          std_err(fmtmk("Failed /proc/stat open: %s", strerror(errno)));
934       /* note: we allocate one more CPU_t than Cpu_tot so that the last slot
935                can hold tics representing the /proc/stat cpu summary (the first
936                line read) -- that slot supports our View_CPUSUM toggle */
937       cpus = alloc_c((1 + Cpu_tot) * sizeof(CPU_t));
938    }
939    rewind(fp);
940    fflush(fp);
941
942    // first value the last slot with the cpu summary line
943    if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read");
944    cpus[Cpu_tot].x = 0;  // FIXME: can't tell by kernel version number
945    cpus[Cpu_tot].y = 0;  // FIXME: can't tell by kernel version number
946    cpus[Cpu_tot].z = 0;  // FIXME: can't tell by kernel version number
947    num = sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu",
948       &cpus[Cpu_tot].u,
949       &cpus[Cpu_tot].n,
950       &cpus[Cpu_tot].s,
951       &cpus[Cpu_tot].i,
952       &cpus[Cpu_tot].w,
953       &cpus[Cpu_tot].x,
954       &cpus[Cpu_tot].y,
955       &cpus[Cpu_tot].z
956    );
957    if (num < 4)
958          std_err("failed /proc/stat read");
959
960    // and just in case we're 2.2.xx compiled without SMP support...
961    if (Cpu_tot == 1) {
962       cpus[1].id = 0;
963       memcpy(cpus, &cpus[1], sizeof(CPU_t));
964    }
965
966    // now value each separate cpu's tics
967    for (i = 0; 1 < Cpu_tot && i < Cpu_tot; i++) {
968       if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read");
969       cpus[i].x = 0;  // FIXME: can't tell by kernel version number
970       cpus[i].y = 0;  // FIXME: can't tell by kernel version number
971       cpus[i].z = 0;  // FIXME: can't tell by kernel version number
972       num = sscanf(buf, "cpu%u %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu",
973          &cpus[i].id,
974          &cpus[i].u, &cpus[i].n, &cpus[i].s, &cpus[i].i, &cpus[i].w, &cpus[i].x, &cpus[i].y, &cpus[i].z
975       );
976       if (num < 4)
977             std_err("failed /proc/stat read");
978    }
979    return cpus;
980 }
981
982
983         /*
984          * Refresh procs *Helper* function to eliminate yet one more need
985          * to loop through our darn proc_t table.  He's responsible for:
986          *    1) calculating the elapsed time since the previous frame
987          *    2) counting the number of tasks in each state (run, sleep, etc)
988          *    3) maintaining the HST_t's and priming the proc_t pcpu field
989          *    4) establishing the total number tasks for this frame */
990 static void prochlp (proc_t *this)
991 {
992    static HST_t    *hist_sav = NULL;
993    static HST_t    *hist_new = NULL;
994    static unsigned  hist_siz = 0;       // number of structs
995    static unsigned  maxt_sav;           // prior frame's max tasks
996    TIC_t tics;
997
998    if (unlikely(!this)) {
999       static struct timeval oldtimev;
1000       struct timeval timev;
1001       struct timezone timez;
1002       HST_t *hist_tmp;
1003       float et;
1004
1005       gettimeofday(&timev, &timez);
1006       et = (timev.tv_sec - oldtimev.tv_sec)
1007          + (float)(timev.tv_usec - oldtimev.tv_usec) / 1000000.0;
1008       oldtimev.tv_sec = timev.tv_sec;
1009       oldtimev.tv_usec = timev.tv_usec;
1010
1011       // if in Solaris mode, adjust our scaling for all cpus
1012       Frame_tscale = 100.0f / ((float)Hertz * (float)et * (Rc.mode_irixps ? 1 : Cpu_tot));
1013       maxt_sav = Frame_maxtask;
1014       Frame_maxtask = Frame_running = Frame_sleepin = Frame_stopped = Frame_zombied = 0;
1015
1016       // reuse memory each time around
1017       hist_tmp = hist_sav;
1018       hist_sav = hist_new;
1019       hist_new = hist_tmp;
1020       // prep for our binary search by sorting the last frame's HST_t's
1021       qsort(hist_sav, maxt_sav, sizeof(HST_t), (QFP_t)sort_HST_t);
1022       return;
1023    }
1024
1025    switch (this->state) {
1026       case 'R':
1027          Frame_running++;
1028          break;
1029       case 'S':
1030       case 'D':
1031          Frame_sleepin++;
1032          break;
1033       case 'T':
1034          Frame_stopped++;
1035          break;
1036       case 'Z':
1037          Frame_zombied++;
1038          break;
1039    }
1040
1041    if (unlikely(Frame_maxtask+1 >= hist_siz)) {
1042       hist_siz = hist_siz * 5 / 4 + 100;  // grow by at least 25%
1043       hist_sav = alloc_r(hist_sav, sizeof(HST_t) * hist_siz);
1044       hist_new = alloc_r(hist_new, sizeof(HST_t) * hist_siz);
1045    }
1046    /* calculate time in this process; the sum of user time (utime) and
1047       system time (stime) -- but PLEASE dont waste time and effort on
1048       calcs and saves that go unused, like the old top! */
1049    hist_new[Frame_maxtask].pid  = this->tid;
1050    hist_new[Frame_maxtask].tics = tics = (this->utime + this->stime);
1051
1052 #if 0
1053 {  int i;
1054    int lo = 0;
1055    int hi = maxt_sav - 1;
1056
1057    // find matching entry from previous frame and make ticks elapsed
1058    while (lo <= hi) {
1059       i = (lo + hi) / 2;
1060       if (this->tid < hist_sav[i].pid)
1061          hi = i - 1;
1062       else if (likely(this->tid > hist_sav[i].pid))
1063          lo = i + 1;
1064       else {
1065          tics -= hist_sav[i].tics;
1066          break;
1067       }
1068    }
1069 }
1070 #else
1071 {
1072    HST_t tmp;
1073    const HST_t *ptr;
1074    tmp.pid = this->tid;
1075    ptr = bsearch(&tmp, hist_sav, maxt_sav, sizeof tmp, sort_HST_t);
1076    if(ptr) tics -= ptr->tics;
1077 }
1078 #endif
1079
1080    // we're just saving elapsed tics, to be converted into %cpu if
1081    // this task wins it's displayable screen row lottery... */
1082    this->pcpu = tics;
1083 // if (Frames_maxcmdln) { }
1084    // shout this to the world with the final call (or us the next time in)
1085    Frame_maxtask++;
1086 }
1087
1088
1089         /*
1090          * This guy's modeled on libproc's 'readproctab' function except
1091          * we reuse and extend any prior proc_t's.  He's been customized
1092          * for our specific needs and to avoid the use of <stdarg.h> */
1093 static proc_t **procs_refresh (proc_t **table, int flags)
1094 {
1095 #define PTRsz  sizeof(proc_t *)
1096 #define ENTsz  sizeof(proc_t)
1097    static unsigned savmax = 0;          // first time, Bypass: (i)
1098    proc_t *ptsk = (proc_t *)-1;         // first time, Force: (ii)
1099    unsigned curmax = 0;                 // every time  (jeeze)
1100    PROCTAB* PT;
1101    static int show_threads_was_enabled = 0; // optimization
1102
1103    prochlp(NULL);                       // prep for a new frame
1104    if (Monpidsidx)
1105       PT = openproc(flags, Monpids);
1106    else
1107       PT = openproc(flags);
1108
1109    // i) Allocated Chunks:  *Existing* table;  refresh + reuse
1110    if (!(CHKw(Curwin, Show_THREADS))) {
1111       while (curmax < savmax) {
1112          if (table[curmax]->cmdline) {
1113             unsigned idx;
1114             // Skip if Show_THREADS was never enabled
1115             if (show_threads_was_enabled) {
1116                for (idx = curmax + 1; idx < savmax; idx++) {
1117                   if (table[idx]->cmdline == table[curmax]->cmdline)
1118                      table[idx]->cmdline = NULL;
1119                }
1120             }
1121             free(*table[curmax]->cmdline);
1122             table[curmax]->cmdline = NULL;
1123          }
1124          if (unlikely(!(ptsk = readproc(PT, table[curmax])))) break;
1125          prochlp(ptsk);                    // tally & complete this proc_t
1126          ++curmax;
1127       }
1128    }
1129    else {                          // show each thread in a process separately
1130       while (curmax < savmax) {
1131          proc_t *ttsk;
1132          if (unlikely(!(ptsk = readproc(PT, NULL)))) break;
1133          show_threads_was_enabled = 1;
1134          while (curmax < savmax) {
1135             unsigned idx;
1136             if (table[curmax]->cmdline) {
1137                // threads share the same cmdline storage.  'table' is
1138                // qsort()ed, so must look through the rest of the table.
1139                for (idx = curmax + 1; idx < savmax; idx++) {
1140                   if (table[idx]->cmdline == table[curmax]->cmdline)
1141                      table[idx]->cmdline = NULL;
1142                }
1143                free(*table[curmax]->cmdline);  // only free once
1144                table[curmax]->cmdline = NULL;
1145             }
1146             if (!(ttsk = readtask(PT, ptsk, table[curmax]))) break;
1147             prochlp(ttsk);
1148             ++curmax;
1149          }
1150          free(ptsk);  // readproc() proc_t not used
1151       }
1152    }
1153
1154    // ii) Unallocated Chunks:  *New* or *Existing* table;  extend + fill
1155    if (!(CHKw(Curwin, Show_THREADS))) {
1156       while (ptsk) {
1157          // realloc as we go, keeping 'table' ahead of 'currmax++'
1158          table = alloc_r(table, (curmax + 1) * PTRsz);
1159          // here, readproc will allocate the underlying proc_t stg
1160          if (likely(ptsk = readproc(PT, NULL))) {
1161             prochlp(ptsk);                 // tally & complete this proc_t
1162             table[curmax++] = ptsk;
1163          }
1164       }
1165    }
1166    else {                          // show each thread in a process separately
1167       while (ptsk) {
1168          proc_t *ttsk;
1169          if (likely(ptsk = readproc(PT, NULL))) {
1170             show_threads_was_enabled = 1;
1171             while (1) {
1172                table = alloc_r(table, (curmax + 1) * PTRsz);
1173                if (!(ttsk = readtask(PT, ptsk, NULL))) break;
1174                prochlp(ttsk);
1175                table[curmax++] = ttsk;
1176             }
1177             free(ptsk);   // readproc() proc_t not used
1178          }
1179       }
1180    }
1181    closeproc(PT);
1182
1183    // iii) Chunkless:  make 'eot' entry, after ensuring proc_t exists
1184    if (curmax >= savmax) {
1185       table = alloc_r(table, (curmax + 1) * PTRsz);
1186       // here, we must allocate the underlying proc_t stg ourselves
1187       table[curmax] = alloc_c(ENTsz);
1188       savmax = curmax + 1;
1189    }
1190    // this frame's end, but not necessarily end of allocated space
1191    table[curmax]->tid = -1;
1192    return table;
1193
1194 #undef PTRsz
1195 #undef ENTsz
1196 }
1197
1198 /*######  Field Table/RCfile compatability support  ######################*/
1199
1200 // from either 'stat' or 'status' (preferred), via bits not otherwise used
1201 #define L_EITHER   PROC_SPARE_1
1202 // These are the Fieldstab.lflg values used here and in reframewins.
1203 // (own identifiers as documentation and protection against changes)
1204 #define L_stat     PROC_FILLSTAT
1205 #define L_statm    PROC_FILLMEM
1206 #define L_status   PROC_FILLSTATUS
1207 #define L_CMDLINE  L_EITHER | PROC_FILLARG
1208 #define L_EUSER    PROC_FILLUSR
1209 #define L_RUSER    L_status | PROC_FILLUSR
1210 #define L_GROUP    L_status | PROC_FILLGRP
1211 #define L_NONE     0
1212 // for reframewins and summary_show 1st pass
1213 #define L_DEFAULT  PROC_FILLSTAT
1214
1215 // a temporary macro, soon to be undef'd...
1216 #define SF(f) (QFP_t)sort_P_ ## f
1217
1218         /* These are our gosh darn 'Fields' !
1219            They MUST be kept in sync with pflags !!
1220            note: for integer data, the length modifiers found in .fmts may
1221                  NOT reflect the true field type found in proc_t -- this plus
1222                  a cast when/if displayed provides minimal width protection. */
1223 static FLD_t Fieldstab[] = {
1224 /* .lflg anomolies:
1225       P_UID, L_NONE  - natural outgrowth of 'stat()' in readproc        (euid)
1226       P_CPU, L_stat  - never filled by libproc, but requires times      (pcpu)
1227       P_CMD, L_stat  - may yet require L_CMDLINE in reframewins  (cmd/cmdline)
1228       L_EITHER       - must L_status, else 64-bit math, __udivdi3 on 32-bit !
1229       keys   head           fmts     width   scale  sort   desc                     lflg
1230      ------  -----------    -------  ------  -----  -----  ----------------------   -------- */
1231    { "AaAa", "   PID",      " %5u",     -1,    -1, SF(PID), "Process Id",           L_NONE   },
1232    { "BbBb", "  PPID",      " %5u",     -1,    -1, SF(PPD), "Parent Process Pid",   L_EITHER },
1233    { "CcQq", " RUSER   ",   " %-8.8s",  -1,    -1, SF(URR), "Real user name",       L_RUSER  },
1234    { "DdCc", "  UID",       " %4u",     -1,    -1, SF(UID), "User Id",              L_NONE   },
1235    { "EeDd", " USER    ",   " %-8.8s",  -1,    -1, SF(URE), "User Name",            L_EUSER  },
1236    { "FfNn", " GROUP   ",   " %-8.8s",  -1,    -1, SF(GRP), "Group Name",           L_GROUP  },
1237    { "GgGg", " TTY     ",   " %-8.8s",   8,    -1, SF(TTY), "Controlling Tty",      L_stat   },
1238    { "HhHh", "  PR",        " %3d",     -1,    -1, SF(PRI), "Priority",             L_stat   },
1239    { "IiIi", "  NI",        " %3d",     -1,    -1, SF(NCE), "Nice value",           L_stat   },
1240    { "JjYy", " #C",         " %2u",     -1,    -1, SF(CPN), "Last used cpu (SMP)",  L_stat   },
1241    { "KkEe", " %CPU",       " %#4.1f",  -1,    -1, SF(CPU), "CPU usage",            L_stat   },
1242    { "LlWw", "   TIME",     " %6.6s",    6,    -1, SF(TME), "CPU Time",             L_stat   },
1243    { "MmRr", "    TIME+ ",  " %9.9s",    9,    -1, SF(TME), "CPU Time, hundredths", L_stat   },
1244    { "NnFf", " %MEM",       " %#4.1f",  -1,    -1, SF(RES), "Memory usage (RES)",   L_statm  },
1245    { "OoMm", "  VIRT",      " %5.5s",    5, SK_Kb, SF(VRT), "Virtual Image (kb)",   L_statm  },
1246    { "PpOo", " SWAP",       " %4.4s",    4, SK_Kb, SF(SWP), "Swapped size (kb)",    L_statm  },
1247    { "QqTt", "  RES",       " %4.4s",    4, SK_Kb, SF(RES), "Resident size (kb)",   L_statm  },
1248    { "RrKk", " CODE",       " %4.4s",    4, SK_Kb, SF(COD), "Code size (kb)",       L_statm  },
1249    { "SsLl", " DATA",       " %4.4s",    4, SK_Kb, SF(DAT), "Data+Stack size (kb)", L_statm  },
1250    { "TtPp", "  SHR",       " %4.4s",    4, SK_Kb, SF(SHR), "Shared Mem size (kb)", L_statm  },
1251    { "UuJj", " nFLT",       " %4.4s",    4, SK_no, SF(FLT), "Page Fault count",     L_stat   },
1252    { "VvSs", " nDRT",       " %4.4s",    4, SK_no, SF(DRT), "Dirty Pages count",    L_statm  },
1253    { "WwVv", " S",          " %c",      -1,    -1, SF(STA), "Process Status",       L_EITHER },
1254    // next entry's special: '.head' will be formatted using table entry's own
1255    //                       '.fmts' plus runtime supplied conversion args!
1256    { "XxXx", " COMMAND",    " %-*.*s",  -1,    -1, SF(CMD), "Command name/line",    L_EITHER },
1257    { "YyUu", " WCHAN    ",  " %-9.9s",  -1,    -1, SF(WCH), "Sleeping in Function", L_stat   },
1258    // next entry's special: the 0's will be replaced with '.'!
1259    { "ZzZz", " Flags   ",   " %08lx",   -1,    -1, SF(FLG), "Task Flags <sched.h>", L_stat   },
1260 #if 0
1261    { "..Qq", "   A",        " %4.4s",    4, SK_no, SF(PID), "Accessed Page count",  L_stat   },
1262    { "..Nn", "  TRS",       " %4.4s",    4, SK_Kb, SF(PID), "Code in memory (kb)",  L_stat   },
1263    { "..Rr", "  WP",        " %4.4s",    4, SK_no, SF(PID), "Unwritable Pages",     L_stat   },
1264    { "Jj[{", " #C",         " %2u",     -1,    -1, SF(CPN), "Last used cpu (SMP)",  L_stat   },
1265    { "..\\|"," Bad",        " %2u",     -1,    -1, SF(CPN), "-- must ignore | --",  0        },
1266    { "..]}", " Bad",        " %2u",     -1,    -1, SF(CPN), "-- not used --",       0        },
1267    { "..^~", " Bad",        " %2u",     -1,    -1, SF(CPN), "-- not used --",       0        },
1268 #endif
1269 };
1270 #undef SF
1271
1272
1273         /* All right, those-that-follow -- Listen Up!
1274          * For the above table keys and the following present/future rc file
1275          * compatibility support, you have Mr. Albert D. Cahalan to thank.
1276          * He must have been in a 'Christmas spirit'.  Were it left to me,
1277          * this top would never have gotten that close to the former top's
1278          * crufty rcfile.  Not only is it illogical, it's odoriferous !
1279          */
1280
1281         // used as 'to' and/or 'from' args in the ft_xxx utilities...
1282 #define FT_NEW_fmt 0
1283 #define FT_OLD_fmt 2
1284
1285
1286 #if 0
1287         // convert, or 0 for failure
1288 static int ft_cvt_char (const int fr, const int to, int c) {
1289    int j = -1;
1290
1291    while (++j < MAXTBL(Fieldstab)) {
1292       if (c == Fieldstab[j].keys[fr])   return Fieldstab[j].keys[to];
1293       if (c == Fieldstab[j].keys[fr+1]) return Fieldstab[j].keys[to+1];
1294    }
1295    return 0;
1296 }
1297 #endif
1298
1299
1300         // convert
1301 static inline int ft_get_char (const int fr, int i) {
1302    int c;
1303    if (i < 0) return 0;
1304    if (i >= MAXTBL(Fieldstab)) return 0;
1305    c = Fieldstab[i].keys[fr];
1306    if (c == '.') c = 0;         // '.' marks a bad entry
1307    return c;
1308 }
1309
1310
1311 #if 0
1312         // convert, or -1 for failure
1313 static int ft_get_idx (const int fr, int c) {
1314    int j = -1;
1315
1316    while (++j < MAXTBL(Fieldstab)) {
1317       if (c == Fieldstab[j].keys[fr])   return j;
1318       if (c == Fieldstab[j].keys[fr+1]) return j;
1319    }
1320    return -1;
1321 }
1322 #endif
1323
1324
1325         // convert, or NULL for failure
1326 static const FLD_t *ft_get_ptr (const int fr, int c) {
1327    int j = -1;
1328
1329    while (++j < MAXTBL(Fieldstab)) {
1330       if (c == Fieldstab[j].keys[fr])   return Fieldstab+j;
1331       if (c == Fieldstab[j].keys[fr+1]) return Fieldstab+j;
1332    }
1333    return NULL;
1334 }
1335
1336
1337 #if 0
1338         // convert, or NULL for failure
1339 static const FLD_t *ft_idx_to_ptr (const int i) {
1340    if (i < 0) return NULL;
1341    if (i >= MAXTBL(Fieldstab)) return NULL;
1342    return Fieldstab + i;
1343 }
1344
1345
1346         // convert, or -1 for failure
1347 static int ft_ptr_to_idx (const FLD_t *p) {
1348    int i;
1349    if (p < Fieldstab) return -1;
1350    i = p - Fieldstab;
1351    if (i >= MAXTBL(Fieldstab)) return -1;
1352    return i;
1353 }
1354 #endif
1355
1356
1357 #if 0
1358 static void rc_bugless (const RCF_t *const rc) {
1359    const RCW_t *w;
1360    int i = 0;
1361
1362    fprintf(stderr,"\n%d %d %f %d\n",
1363       rc->mode_altscr, rc->mode_irixps, rc->delay_time, rc->win_index
1364    );
1365    while(i < 4) {
1366       w = &rc->win[i++];
1367       fprintf(stderr, "<%s> <%s> %d %08x %d %d %d %d %d\n",
1368          w->winname, w->fieldscur, w->sortindx, w->winflags, w->maxtasks,
1369          w->summclr, w->msgsclr, w->headclr, w->taskclr
1370       );
1371    }
1372 }
1373 #endif
1374
1375
1376 // '$HOME/Rc_name' contains multiple lines - 2 global + 3 per window.
1377 //   line 1: an eyecatcher, with a shameless advertisement
1378 //   line 2: an id, Mode_altcsr, Mode_irixps, Delay_time and Curwin.
1379 // For each of the 4 windows:
1380 //   line a: contains winname, fieldscur
1381 //   line b: contains winflags, sortindx, maxtasks
1382 //   line c: contains summclr, msgsclr, headclr, taskclr
1383 //   line d: if present, would crash procps-3.1.1
1384 static int rc_read_new (const char *const buf, RCF_t *rc) {
1385    int i;
1386    int cnt;
1387    const char *cp;
1388
1389    cp = strstr(buf, "\n\n" RCF_EYECATCHER);
1390    if (!cp) return -1;
1391    cp = strchr(cp + 2, '\n');
1392    if (!cp++) return -2;
1393
1394    cnt = sscanf(cp, "Id:a, Mode_altscr=%d, Mode_irixps=%d, Delay_time=%f, Curwin=%d\n",
1395       &rc->mode_altscr, &rc->mode_irixps, &rc->delay_time, &rc->win_index
1396    );
1397    if (cnt != 4) return -3;
1398    cp = strchr(cp, '\n');
1399    if (!cp++) return -4;
1400
1401    for (i = 0; i < GROUPSMAX; i++) {
1402       RCW_t *ptr = &rc->win[i];
1403       cnt = sscanf(cp, "%3s\tfieldscur=%31s\n", ptr->winname, ptr->fieldscur);
1404       if (cnt != 2) return 5+100*i;  // OK to have less than 4 windows
1405       if (WINNAMSIZ <= strlen(ptr->winname)) return -6;
1406       if (strlen(DEF_FIELDS) != strlen(ptr->fieldscur)) return -7;
1407       cp = strchr(cp, '\n');
1408       if (!cp++) return -(8+100*i);
1409
1410       cnt = sscanf(cp, "\twinflags=%d, sortindx=%u, maxtasks=%d \n",
1411          &ptr->winflags, &ptr->sortindx, &ptr->maxtasks
1412       );
1413       if (cnt != 3) return -(9+100*i);
1414       cp = strchr(cp, '\n');
1415       if (!cp++) return -(10+100*i);
1416
1417       cnt = sscanf(cp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d \n",
1418          &ptr->summclr, &ptr->msgsclr, &ptr->headclr, &ptr->taskclr
1419       );
1420       if (cnt != 4) return -(11+100*i);
1421       cp = strchr(cp, '\n');
1422       if (!cp++) return -(12+100*i);
1423       while (*cp == '\t') {  // skip unknown per-window settings
1424         cp = strchr(cp, '\n');
1425         if (!cp++) return -(13+100*i);
1426       }
1427    }
1428    return 13;
1429 }
1430
1431
1432
1433 static int rc_read_old (const char *const buf, RCF_t *rc) {
1434    const char std[] = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZzJj......";
1435    const char old[] = "AaBb..CcDd..GgHhIiYyEeWw..FfMmOoTtKkLlPpJjSsVvXxUuZz[{QqNnRr";
1436    unsigned u;
1437    const char *cp;
1438    unsigned c_show = 0;
1439    int badchar = 0;     // allow a limited number of duplicates and junk
1440
1441    char scoreboard[256];
1442    memset(scoreboard, '\0', sizeof scoreboard);
1443
1444    cp = buf+2;  // skip the "\n\n" we stuck at the beginning
1445    u = 0;
1446    for (;;) {
1447       const char *tmp;
1448       int c = *cp++;
1449       if (u+1 >= sizeof rc->win[0].fieldscur) return -1;
1450       if (c == '\0') return -2;
1451       if (c == '\n') break;
1452       if (c & ~0x7f) return -3;
1453       if (~c & 0x20) c_show |= 1 << (c & 0x1f); // 0x20 means lowercase means hidden
1454       if (scoreboard[c|0xe0u]) badchar++;       // duplicates not allowed
1455       scoreboard[c|0xe0u]++;
1456       tmp = strchr(old,c);
1457       if (!tmp) continue;
1458       c = *((tmp-old)+std);
1459       if (c == '.') continue;
1460       if (scoreboard[c&0x1fu]) badchar++;       // duplicates not allowed
1461       scoreboard[c&0x1fu]++;
1462       rc->win[0].fieldscur[u++] = c;
1463    }
1464    rc->win[0].fieldscur[u++] = '\0';
1465    if (u < 21) return -6;  // catch junk, not good files (had 23 chars in one)
1466    if (u > 33) return -7;  // catch junk, not good files (had 29 chars in one)
1467 // fprintf(stderr, "badchar: %d\n", badchar); sleep(2);
1468    if (badchar > 8) return -8;          // too much junk
1469    if (!c_show) return -9;              // nothing was shown
1470
1471    // rest of file is optional, but better look right if it exists
1472    if (!*cp) return 12;
1473    if (*cp < '2' || *cp > '9') return -13; // stupid, and why isn't '1' valid?
1474    rc->delay_time = *cp - '0';
1475
1476    memset(scoreboard, '\0', sizeof(scoreboard));
1477    for (;;) {
1478       int c = *++cp & 0xffu;    // protect scoreboard[] from negative char
1479       if (!c) return -14;       // not OK to hit EOL w/o '\n'
1480       if (c == '\n') break;
1481       switch (c) {
1482          case ' ':
1483          case '.':
1484          case '0' ... '9':
1485             return -15;                 // not supposed to have digits here
1486
1487 //       case 's':                      // mostly for global rcfile
1488 //          rc->mode_secure = 1;
1489 //          break;
1490          case 'S':
1491             rc->win[0].winflags |= Show_CTIMES;
1492             break;
1493          case 'c':
1494             rc->win[0].winflags |= Show_CMDLIN;
1495             break;
1496          case 'i':
1497             rc->win[0].winflags &= ~Show_IDLEPS;
1498             break;
1499          case 'H':
1500             rc->win[0].winflags |= Show_THREADS;
1501             break;
1502          case 'm':
1503             rc->win[0].winflags &= ~View_MEMORY;
1504             break;
1505          case 'l':
1506             rc->win[0].winflags &= ~View_LOADAV;
1507             break;
1508          case 't':
1509             rc->win[0].winflags &= ~View_STATES;
1510             break;
1511          case 'I':
1512             rc->mode_irixps = 0;
1513             break;
1514
1515          case 'M':
1516             c = 0; // for scoreboard
1517             rc->win[0].sortindx = P_MEM;
1518             break;
1519          case 'P':
1520             c = 0; // for scoreboard
1521             rc->win[0].sortindx = P_CPU;
1522             break;
1523          case 'A':                      // supposed to be start_time
1524             c = 0; // for scoreboard
1525             rc->win[0].sortindx = P_PID;
1526             break;
1527          case 'T':
1528             c = 0; // for scoreboard
1529             rc->win[0].sortindx = P_TM2;
1530             break;
1531          case 'N':
1532             c = 0; // for scoreboard
1533             rc->win[0].sortindx = P_PID;
1534             break;
1535
1536          default:
1537             // just ignore it, except for the scoreboard of course
1538             break;
1539       }
1540       if (scoreboard[c]) return -16;    // duplicates not allowed
1541       scoreboard[c] = 1;
1542    }
1543    return 17;
1544 }
1545
1546
1547 static void rc_write_new (FILE *fp) {
1548    int i;
1549
1550    fprintf(fp, RCF_EYECATCHER "\"%s with windows\"\t\t# shameless braggin'\n",
1551       Myname
1552    );
1553    fprintf(fp, RCF_DEPRECATED
1554       "Mode_altscr=%d, Mode_irixps=%d, Delay_time=%.3f, Curwin=%u\n",
1555       Rc.mode_altscr, Rc.mode_irixps, Rc.delay_time, (unsigned)(Curwin - Winstk)
1556    );
1557    for (i = 0; i < GROUPSMAX; i++) {
1558       char buf[40];
1559       char *cp = Winstk[i].rc.fieldscur;
1560       int j = 0;
1561
1562       while (j < 36) {
1563          int c = *cp++ & 0xff;
1564          switch (c) {
1565             case '.':
1566             case 1 ... ' ':
1567             case 0x7f ... 0xff:
1568                continue;                // throw away junk (some of it)
1569             default:
1570                buf[j++] = c;            // gets the '\0' too
1571          }
1572          if (!c) break;
1573       }
1574       fprintf(fp, "%s\tfieldscur=%s\n",
1575          Winstk[i].rc.winname, buf
1576       );
1577       fprintf(fp, "\twinflags=%d, sortindx=%d, maxtasks=%d\n",
1578          Winstk[i].rc.winflags, Winstk[i].rc.sortindx, Winstk[i].rc.maxtasks
1579       );
1580       fprintf(fp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d\n",
1581          Winstk[i].rc.summclr, Winstk[i].rc.msgsclr,
1582          Winstk[i].rc.headclr, Winstk[i].rc.taskclr
1583       );
1584    }
1585 }
1586
1587
1588 static const char *rc_write_whatever (void) {
1589    FILE *fp = fopen(Rc_name, "w");
1590
1591    if (!fp) return strerror(errno);
1592    rc_write_new(fp);
1593    fclose(fp);
1594    return NULL;
1595 }
1596
1597
1598 /*######  Startup routines  ##############################################*/
1599
1600 // No mater what *they* say, we handle the really really BIG and
1601 // IMPORTANT stuff upon which all those lessor functions depend!
1602 static void before (char *me)
1603 {
1604    int i;
1605
1606       /* setup our program name -- big! */
1607    Myname = strrchr(me, '/');
1608    if (Myname) ++Myname; else Myname = me;
1609
1610       /* establish cpu particulars -- even bigger! */
1611    Cpu_tot = smp_num_cpus;
1612    if (linux_version_code > LINUX_VERSION(2, 5, 41))
1613       States_fmts = STATES_line2x5;
1614    if (linux_version_code >= LINUX_VERSION(2, 6, 0))  // grrr... only some 2.6.0-testX :-(
1615       States_fmts = STATES_line2x6;
1616    if (linux_version_code >= LINUX_VERSION(2, 6, 11))
1617       States_fmts = STATES_line2x7;
1618
1619       /* get virtual page size -- nearing huge! */
1620    Page_size = getpagesize();
1621    i = Page_size;
1622    while(i>1024){
1623      i >>= 1;
1624      page_to_kb_shift++;
1625    }
1626
1627    pcpu_max_value = 99.9;
1628
1629    Fieldstab[P_CPN].head = " P";
1630    Fieldstab[P_CPN].fmts = " %1u";
1631    if(smp_num_cpus>9){
1632       Fieldstab[P_CPN].head = "  P";
1633       Fieldstab[P_CPN].fmts = " %2u";
1634    }
1635    if(smp_num_cpus>99){
1636       Fieldstab[P_CPN].head = "   P";
1637       Fieldstab[P_CPN].fmts = " %3u";
1638    }
1639    if(smp_num_cpus>999){
1640       Fieldstab[P_CPN].head = "    P";
1641       Fieldstab[P_CPN].fmts = " %4u";
1642    }
1643
1644    {
1645       static char pid_fmt[6];
1646       unsigned pid_digits = get_pid_digits();
1647       if(pid_digits<4) pid_digits=4;
1648       snprintf(pid_fmt, sizeof pid_fmt, " %%%uu", pid_digits);
1649       Fieldstab[P_PID].fmts = pid_fmt;
1650       Fieldstab[P_PID].head = "        PID" + 10 - pid_digits;
1651       Fieldstab[P_PPD].fmts = pid_fmt;
1652       Fieldstab[P_PPD].head = "       PPID" + 10 - pid_digits;
1653    }
1654 }
1655
1656
1657 // Config file read *helper* function.
1658 // Anything missing won't show as a choice in the field editor,
1659 // so make sure there is exactly one of each letter.
1660 //
1661 // Due to Rik blindly accepting damem's broken patches, procps-2.0.1x
1662 // has 3 ("three"!!!) instances of "#C", "LC", or "CPU". Fix that too.
1663 static void confighlp (char *fields) {
1664    unsigned upper[PFLAGSSIZ];
1665    unsigned lower[PFLAGSSIZ];
1666    char c;
1667    char *cp;
1668
1669    memset(upper, '\0', sizeof upper);
1670    memset(lower, '\0', sizeof lower);
1671
1672    cp = fields;
1673    for (;;) {
1674       c = *cp++;
1675       if (!c) break;
1676       if(isupper(c)) upper[c&0x1f]++;
1677       else           lower[c&0x1f]++;
1678    }
1679
1680    c = 'a';
1681    while (c <= 'z') {
1682       if (upper[c&0x1f] && lower[c&0x1f]) {
1683          lower[c&0x1f] = 0;             // got both, so wipe out unseen column
1684          for (;;) {
1685             cp = strchr(fields, c);
1686             if (cp) memmove(cp, cp+1, strlen(cp));
1687             else break;
1688          }
1689       }
1690       while (lower[c&0x1f] > 1) {               // got too many a..z
1691          lower[c&0x1f]--;
1692          cp = strchr(fields, c);
1693          memmove(cp, cp+1, strlen(cp));
1694       }
1695       while (upper[c&0x1f] > 1) {               // got too many A..Z
1696          upper[c&0x1f]--;
1697          cp = strchr(fields, toupper(c));
1698          memmove(cp, cp+1, strlen(cp));
1699       }
1700       if (!upper[c&0x1f] && !lower[c&0x1f]) {   // both missing
1701          lower[c&0x1f]++;
1702          memmove(fields+1, fields, strlen(fields)+1);
1703          fields[0] = c;
1704       }
1705       c++;
1706    }
1707 }
1708
1709
1710 // First attempt to read the /etc/rcfile which contains two lines
1711 // consisting of the secure mode switch and an update interval.
1712 // It's presence limits what ordinary users are allowed to do.
1713 // (it's actually an old-style config file)
1714 //
1715 // Then build the local rcfile name and try to read a crufty old-top
1716 // rcfile (whew, odoriferous), which may contain an embedded new-style
1717 // rcfile.   Whether embedded or standalone, new-style rcfile values
1718 // will always override that crufty stuff!
1719 // note: If running in secure mode via the /etc/rcfile,
1720 //       Delay_time will be ignored except for root.
1721 static void configs_read (void)
1722 {
1723    const RCF_t def_rcf = DEF_RCFILE;
1724    char fbuf[MEDBUFSIZ];
1725    int i, fd;
1726    RCF_t rcf;
1727    float delay = Rc.delay_time;
1728
1729    // read part of an old-style config in /etc/toprc
1730    fd = open(SYS_RCFILESPEC, O_RDONLY);
1731    if (fd > 0) {
1732       ssize_t num;
1733       num = read(fd, fbuf, sizeof(fbuf) - 1);
1734       if (num > 0) {
1735          const char *sec = strchr(fbuf, 's');
1736          const char *eol = strchr(fbuf, '\n');
1737          if (eol) {
1738             const char *two = eol + 1;  // line two
1739             if (sec < eol) Secure_mode = !!sec;
1740             eol = strchr(two, '\n');
1741             if (eol && eol > two && isdigit(*two)) Rc.delay_time = atof(two);
1742          }
1743       }
1744       close(fd);
1745    }
1746
1747    if (getenv("TOPRC")) {    // should switch on Myname before documenting this?
1748       // not the most optimal here...
1749       snprintf(Rc_name, sizeof(Rc_name), "%s", getenv("TOPRC"));
1750    } else {
1751       snprintf(Rc_name, sizeof(Rc_name), ".%src", Myname);  // eeew...
1752       if (getenv("HOME"))
1753          snprintf(Rc_name, sizeof(Rc_name), "%s/.%src", getenv("HOME"), Myname);
1754    }
1755
1756    rcf = def_rcf;
1757    fd = open(Rc_name, O_RDONLY);
1758    if (fd > 0) {
1759       ssize_t num;
1760       num = read(fd, fbuf+2, sizeof(fbuf) -3);
1761       if (num > 0) {
1762          fbuf[0] = '\n';
1763          fbuf[1] = '\n';
1764          fbuf[num+2] = '\0';
1765 //fprintf(stderr,"rc_read_old returns %d\n",rc_read_old(fbuf, &rcf));
1766 //sleep(2);
1767          if (rc_read_new(fbuf, &rcf) < 0) {
1768             rcf = def_rcf;                       // on failure, maybe mangled
1769             if (rc_read_old(fbuf, &rcf) < 0) rcf = def_rcf;
1770          }
1771          delay = rcf.delay_time;
1772       }
1773       close(fd);
1774    }
1775
1776    // update Rc defaults, establish a Curwin and fix up the window stack
1777    Rc.mode_altscr = rcf.mode_altscr;
1778    Rc.mode_irixps = rcf.mode_irixps;
1779    if (rcf.win_index >= GROUPSMAX) rcf.win_index = 0;
1780    Curwin = &Winstk[rcf.win_index];
1781    for (i = 0; i < GROUPSMAX; i++) {
1782       memcpy(&Winstk[i].rc, &rcf.win[i], sizeof rcf.win[i]);
1783       confighlp(Winstk[i].rc.fieldscur);
1784    }
1785
1786    if(Rc.mode_irixps && smp_num_cpus>1){
1787       // good for 100 CPUs per process
1788       pcpu_max_value = 9999.0;
1789       Fieldstab[P_CPU].fmts = " %4.0f";
1790    }
1791
1792    // lastly, establish the true runtime secure mode and delay time
1793    if (!getuid()) Secure_mode = 0;
1794    if (!Secure_mode) Rc.delay_time = delay;
1795 }
1796
1797
1798 // Parse command line arguments.
1799 // Note: it's assumed that the rc file(s) have already been read
1800 //       and our job is to see if any of those options are to be
1801 //       overridden -- we'll force some on and negate others in our
1802 //       best effort to honor the loser's (oops, user's) wishes...
1803 static void parse_args (char **args)
1804 {
1805    /* differences between us and the former top:
1806       -C (separate CPU states for SMP) is left to an rcfile
1807       -p (pid monitoring) allows a comma delimited list
1808       -q (zero delay) eliminated as redundant, incomplete and inappropriate
1809             use: "nice -n-10 top -d0" to achieve what was only claimed
1810       -c,i,S act as toggles (not 'on' switches) for enhanced user flexibility
1811       .  no deprecated/illegal use of 'breakargv:' with goto
1812       .  bunched args are actually handled properly and none are ignored
1813       .  we tolerate NO whitespace and NO switches -- maybe too tolerant? */
1814    static const char usage[] =
1815       " -hv | -bcisSH -d delay -n iterations [-u user | -U user] -p pid [,pid ...]";
1816    float tmp_delay = MAXFLOAT;
1817    char *p;
1818
1819    while (*args) {
1820       const char *cp = *(args++);
1821
1822       while (*cp) {
1823          switch (*cp) {
1824             case '\0':
1825             case '-':
1826                break;
1827             case 'b':
1828                Batch = 1;
1829                break;
1830             case 'c':
1831                TOGw(Curwin, Show_CMDLIN);
1832                break;
1833             case 'd':
1834                if (cp[1]) ++cp;
1835                else if (*args) cp = *args++;
1836                else std_err("-d requires argument");
1837                   /* a negative delay will be dealt with shortly... */
1838                if (sscanf(cp, "%f", &tmp_delay) != 1)
1839                   std_err(fmtmk("bad delay '%s'", cp));
1840                break;
1841             case 'H':
1842                TOGw(Curwin, Show_THREADS);
1843                break;
1844             case 'h':
1845             case 'v': case 'V':
1846                std_out(fmtmk("%s\nusage:\t%s%s", procps_version, Myname, usage));
1847             case 'i':
1848                TOGw(Curwin, Show_IDLEPS);
1849                Curwin->rc.maxtasks = 0;
1850                break;
1851             case 'n':
1852                if (cp[1]) cp++;
1853                else if (*args) cp = *args++;
1854                else std_err("-n requires argument");
1855                if (sscanf(cp, "%d", &Loops) != 1 || Loops < 1)
1856                   std_err(fmtmk("bad iterations arg '%s'", cp));
1857                break;
1858             case 'p':
1859                do {
1860                   if (selection_type && selection_type != 'p') std_err("conflicting process selection");
1861                   selection_type = 'p';
1862                   if (cp[1]) cp++;
1863                   else if (*args) cp = *args++;
1864                   else std_err("-p argument missing");
1865                   if (Monpidsidx >= MONPIDMAX)
1866                      std_err(fmtmk("pid limit (%d) exceeded", MONPIDMAX));
1867                   if (sscanf(cp, "%d", &Monpids[Monpidsidx]) != 1 || Monpids[Monpidsidx] < 0)
1868                      std_err(fmtmk("bad pid '%s'", cp));
1869                   if (!Monpids[Monpidsidx])
1870                      Monpids[Monpidsidx] = getpid();
1871                   Monpidsidx++;
1872                   if (!(p = strchr(cp, ',')))
1873                      break;
1874                   cp = p;
1875                } while (*cp);
1876                break;
1877             case 's':
1878                Secure_mode = 1;
1879                break;
1880             case 'S':
1881                TOGw(Curwin, Show_CTIMES);
1882                break;
1883             case 'u':
1884                do {
1885                   const char *errmsg;
1886                   if (selection_type /* && selection_type != 'u' */) std_err("conflicting process selection");
1887                   if (cp[1]) cp++;
1888                   else if (*args) cp = *args++;
1889                   else std_err("-u missing name");
1890                   errmsg = parse_uid(cp, &selection_uid);
1891                   if (errmsg) std_err(errmsg);
1892                   selection_type = 'u';
1893                   cp += snprintf(Curwin->colusrnam, USRNAMSIZ-1, "%s", cp); // FIXME: junk
1894                } while(0);
1895                break;
1896             case 'U':
1897                do {
1898                   const char *errmsg;
1899                   if (selection_type /* && selection_type != 'U' */) std_err("conflicting process selection");
1900                   if (cp[1]) cp++;
1901                   else if (*args) cp = *args++;
1902                   else std_err("-u missing name");
1903                   errmsg = parse_uid(cp, &selection_uid);
1904                   if (errmsg) std_err(errmsg);
1905                   selection_type = 'U';
1906                   cp += snprintf(Curwin->colusrnam, USRNAMSIZ-1, "%s", cp); // FIXME: junk
1907                } while(0);
1908                break;
1909             default :
1910                std_err(fmtmk("unknown argument '%c'\nusage:\t%s%s"
1911                   , *cp, Myname, usage));
1912
1913          } /* end: switch (*cp) */
1914
1915             /* advance cp and jump over any numerical args used above */
1916          if (*cp) cp += strspn(&cp[1], "- ,.1234567890") + 1;
1917       } /* end: while (*cp) */
1918    } /* end: while (*args) */
1919
1920       /* fixup delay time, maybe... */
1921    if (MAXFLOAT != tmp_delay) {
1922       if (Secure_mode || tmp_delay < 0)
1923          msg_save("Delay time Not changed");
1924       else
1925          Rc.delay_time = tmp_delay;
1926    }
1927 }
1928
1929
1930         /*
1931          * Set up the terminal attributes */
1932 static void whack_terminal (void)
1933 {
1934    struct termios newtty;
1935
1936    if (Batch) {
1937       setupterm("dumb", STDOUT_FILENO, NULL);
1938       return;
1939    }
1940    setupterm(NULL, STDOUT_FILENO, NULL);
1941    if (tcgetattr(STDIN_FILENO, &Savedtty) == -1)
1942       std_err("failed tty get");
1943    newtty = Savedtty;
1944    newtty.c_lflag &= ~(ICANON | ECHO);
1945    newtty.c_oflag &= ~(TAB3);
1946    newtty.c_cc[VMIN] = 1;
1947    newtty.c_cc[VTIME] = 0;
1948
1949    Ttychanged = 1;
1950    if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtty) == -1) {
1951       putp(Cap_clr_scr);
1952       std_err(fmtmk("failed tty set: %s", strerror(errno)));
1953    }
1954    tcgetattr(STDIN_FILENO, &Rawtty);
1955 #ifndef STDOUT_IOLBF
1956    // thanks anyway stdio, but we'll manage buffering at the frame level...
1957    setbuffer(stdout, Stdout_buf, sizeof(Stdout_buf));
1958 #endif
1959    putp(Cap_clr_scr);
1960    fflush(stdout);
1961 }
1962
1963
1964 /*######  Field Selection/Ordering routines  #############################*/
1965
1966
1967 // Display each field represented in the Fields Table along with its
1968 // description and mark (with a leading asterisk) fields associated
1969 // with upper case letter(s) in the passed 'fields string'.
1970 //
1971 // After all fields have been displayed, some extra explanatory
1972 // text may also be output
1973 static void display_fields (const char *fields, const char *xtra)
1974 {
1975 #define yRSVD 3
1976    const char *p;
1977    int i, cmax = Screen_cols / 2, rmax = Screen_rows - yRSVD;
1978
1979    /* we're relying on callers to first clear the screen and thus avoid screen
1980       flicker if they're too lazy to handle their own asterisk (*) logic */
1981    putp(Curwin->cap_bold);
1982    for (i = 0; fields[i]; ++i) {
1983       const FLD_t *f = ft_get_ptr(FT_NEW_fmt, fields[i]);
1984       int b = isupper(fields[i]);
1985
1986       if (!f) continue;                 // hey, should be std_err!
1987       for (p = f->head; ' ' == *p; ++p) // advance past any leading spaces
1988          ;
1989       PUTT("%s%s%c %c: %-10s = %s",
1990          tg2((i / rmax) * cmax, (i % rmax) + yRSVD),
1991          b ? Curwin->cap_bold : Cap_norm,
1992          b ? '*' : ' ',
1993          fields[i],
1994          p,
1995          f->desc
1996       );
1997    }
1998    if (xtra) {
1999       putp(Curwin->capclr_rownorm);
2000       while ((p = strchr(xtra, '\n'))) {
2001          ++i;
2002          PUTT("%s%.*s",
2003             tg2((i / rmax) * cmax, (i % rmax) + yRSVD),
2004             (int)(p - xtra),
2005             xtra
2006          );
2007          xtra = ++p;
2008       }
2009    }
2010    putp(Caps_off);
2011
2012 #undef yRSVD
2013 }
2014
2015
2016 // Change order of displayed fields.
2017 static void fields_reorder (void)
2018 {
2019    static const char prompt[] =
2020       "Upper case letter moves field left, lower case right";
2021    char c, *p;
2022    int i;
2023
2024    putp(Cap_clr_scr);
2025    putp(Cap_curs_huge);
2026    for (;;) {
2027       display_fields(Curwin->rc.fieldscur, FIELDS_xtra);
2028       show_special(1, fmtmk(FIELDS_current
2029          , Cap_home, Curwin->rc.fieldscur, Curwin->grpname, prompt));
2030       chin(0, &c, 1);
2031       if (!ft_get_ptr(FT_NEW_fmt, c)) break;
2032       i = toupper(c) - 'A';
2033       if (((p = strchr(Curwin->rc.fieldscur, i + 'A')))
2034       || ((p = strchr(Curwin->rc.fieldscur, i + 'a')))) {
2035          if (isupper(c)) p--;
2036          if (('\0' != p[1]) && (p >= Curwin->rc.fieldscur)) {
2037             c    = p[0];
2038             p[0] = p[1];
2039             p[1] = c;
2040          }
2041       }
2042    }
2043    putp(Cap_curs_norm);
2044 }
2045
2046 // Select sort field.
2047 static void fields_sort (void)
2048 {
2049    static const char prompt[] =
2050       "Select sort field via field letter, type any other key to return";
2051    char phoney[PFLAGSSIZ];
2052    char c, *p;
2053    int i, x;
2054
2055    strcpy(phoney, NUL_FIELDS);
2056    x = i = Curwin->rc.sortindx;
2057    putp(Cap_clr_scr);
2058    putp(Cap_curs_huge);
2059    for (;;) {
2060       p  = phoney + i;
2061       *p = toupper(*p);
2062       display_fields(phoney, SORT_xtra);
2063       show_special(1, fmtmk(SORT_fields, Cap_home, *p, Curwin->grpname, prompt));
2064       chin(0, &c, 1);
2065       if (!ft_get_ptr(FT_NEW_fmt, c)) break;
2066       i = toupper(c) - 'A';
2067       *p = tolower(*p);
2068       x = i;
2069    }
2070    if ((p = strchr(Curwin->rc.fieldscur, x + 'a')))
2071       *p = x + 'A';
2072    Curwin->rc.sortindx = x;
2073    putp(Cap_curs_norm);
2074 }
2075
2076
2077 // Toggle displayed fields.
2078 static void fields_toggle (void)
2079 {
2080    static const char prompt[] =
2081       "Toggle fields via field letter, type any other key to return";
2082    char c, *p;
2083    int i;
2084
2085    putp(Cap_clr_scr);
2086    putp(Cap_curs_huge);
2087    for (;;) {
2088       display_fields(Curwin->rc.fieldscur, FIELDS_xtra);
2089       show_special(1, fmtmk(FIELDS_current, Cap_home, Curwin->rc.fieldscur, Curwin->grpname, prompt));
2090       chin(0, &c, 1);
2091       if (!ft_get_ptr(FT_NEW_fmt, c)) break;
2092       i = toupper(c) - 'A';
2093       if ((p = strchr(Curwin->rc.fieldscur, i + 'A')))
2094          *p = i + 'a';
2095       else if ((p = strchr(Curwin->rc.fieldscur, i + 'a')))
2096          *p = i + 'A';
2097    }
2098    putp(Cap_curs_norm);
2099 }
2100
2101 /*######  Windows/Field Groups support  #################################*/
2102
2103 // For each of the four windows:
2104 //    1) Set the number of fields/columns to display
2105 //    2) Create the field columns heading
2106 //    3) Set maximum cmdline length, if command lines are in use
2107 // In the process, the required PROC_FILLxxx flags will be rebuilt!
2108 static void reframewins (void)
2109 {
2110    WIN_t *w;
2111    char *s;
2112    const char *h;
2113    int i, needpsdb = 0;
2114
2115 // Frames_libflags = 0;  // should be called only when it's zero
2116 // Frames_maxcmdln = 0;  // to become largest from up to 4 windows, if visible
2117    w = Curwin;
2118    do {
2119       if (!Rc.mode_altscr || CHKw(w, VISIBLE_tsk)) {
2120          // build window's procflags array and establish a tentative maxpflgs
2121          for (i = 0, w->maxpflgs = 0; w->rc.fieldscur[i]; i++) {
2122             if (isupper(w->rc.fieldscur[i]))
2123                w->procflags[w->maxpflgs++] = w->rc.fieldscur[i] - 'A';
2124          }
2125
2126          /* build a preliminary columns header not to exceed screen width
2127             while accounting for a possible leading window number */
2128          *(s = w->columnhdr) = '\0';
2129          if (Rc.mode_altscr) s = scat(s, " ");
2130          for (i = 0; i < w->maxpflgs; i++) {
2131             h = Fieldstab[w->procflags[i]].head;
2132             // oops, won't fit -- we're outta here...
2133             if (Screen_cols+1 < (int)((s - w->columnhdr) + strlen(h))) break;
2134             s = scat(s, h);
2135          }
2136
2137          // establish the final maxpflgs and prepare to grow the command column
2138          // heading via maxcmdln - it may be a fib if P_CMD wasn't encountered,
2139          // but that's ok because it won't be displayed anyway
2140          w->maxpflgs = i;
2141          w->maxcmdln = Screen_cols - (strlen(w->columnhdr) - strlen(Fieldstab[P_CMD].head));
2142
2143          // finally, we can build the true run-time columns header, format the
2144          // command column heading, if P_CMD is really being displayed, and
2145          // rebuild the all-important PROC_FILLxxx flags that will be used
2146          // until/if we're we're called again
2147          *(s = w->columnhdr) = '\0';
2148 //         if (Rc.mode_altscr) s = scat(s, fmtmk("%d", w->winnum));
2149          for (i = 0; i < w->maxpflgs; i++) {
2150             int advance = (i==0) && !Rc.mode_altscr;
2151             h = Fieldstab[w->procflags[i]].head;
2152             if (P_WCH == w->procflags[i]) needpsdb = 1;
2153             if (P_CMD == w->procflags[i]) {
2154                s = scat(s, fmtmk(Fieldstab[P_CMD].fmts+advance, w->maxcmdln, w->maxcmdln, "COMMAND"/*h*/  ));
2155                if (CHKw(w, Show_CMDLIN)) {
2156                   Frames_libflags |= L_CMDLINE;
2157 //                if (w->maxcmdln > Frames_maxcmdln) Frames_maxcmdln = w->maxcmdln;
2158                }
2159             } else
2160                s = scat(s, h+advance);
2161             Frames_libflags |= Fieldstab[w->procflags[i]].lflg;
2162          }
2163          if (Rc.mode_altscr) w->columnhdr[0] = w->winnum + '0';
2164       }
2165       if (Rc.mode_altscr) w = w->next;
2166    } while (w != Curwin);
2167
2168    // do we need the kernel symbol table (and is it already open?)
2169    if (needpsdb) {
2170       if (No_ksyms == -1) {
2171          No_ksyms = 0;
2172          if (open_psdb_message(NULL, msg_save))
2173             No_ksyms = 1;
2174          else
2175             PSDBopen = 1;
2176       }
2177    }
2178
2179    if (selection_type=='U') Frames_libflags |= L_status;
2180
2181    if (Frames_libflags & L_EITHER) {
2182       Frames_libflags &= ~L_EITHER;
2183       if (!(Frames_libflags & L_stat)) Frames_libflags |= L_status;
2184    }
2185    if (!Frames_libflags) Frames_libflags = L_DEFAULT;
2186    if (selection_type=='p') Frames_libflags |= PROC_PID;
2187 }
2188
2189
2190 // Value a window's name and make the associated group name.
2191 static void win_names (WIN_t *q, const char *name)
2192 {
2193    sprintf(q->rc.winname, "%.*s", WINNAMSIZ -1, name);
2194    sprintf(q->grpname, "%d:%.*s", q->winnum, WINNAMSIZ -1, name);
2195 }
2196
2197
2198 // Display a window/field group (ie. make it "current").
2199 static void win_select (char ch)
2200 {
2201    static const char prompt[] = "Choose field group (1 - 4)";
2202
2203    /* if there's no ch, it means we're supporting the external interface,
2204       so we must try to get our own darn ch by begging the user... */
2205    if (!ch) {
2206       show_pmt(prompt);
2207       chin(0, (char *)&ch, 1);
2208    }
2209    switch (ch) {
2210       case 'a':                         /* we don't carry 'a' / 'w' in our */
2211          Curwin = Curwin->next;         /* pmt - they're here for a good   */
2212          break;                         /* friend of ours -- wins_colors.  */
2213       case 'w':                         /* (however those letters work via */
2214          Curwin = Curwin->prev;         /* the pmt too but gee, end-loser  */
2215          break;                         /* should just press the darn key) */
2216       case '1': case '2':
2217       case '3': case '4':
2218          Curwin = &Winstk[ch - '1'];
2219          break;
2220    }
2221 }
2222
2223
2224 // Just warn the user when a command can't be honored.
2225 static int win_warn (void)
2226 {
2227    show_msg(fmtmk("\aCommand disabled, activate %s with '-' or '_'", Curwin->grpname));
2228    // we gotta' return false 'cause we're somewhat well known within
2229    // macro society, by way of that sassy little tertiary operator...
2230    return 0;
2231 }
2232
2233
2234 // Change colors *Helper* function to save/restore settings;
2235 // ensure colors will show; and rebuild the terminfo strings.
2236 static void winsclrhlp (WIN_t *q, int save)
2237 {
2238    static int flgssav, summsav, msgssav, headsav, tasksav;
2239
2240    if (save) {
2241       flgssav = q->rc.winflags; summsav = q->rc.summclr;
2242       msgssav = q->rc.msgsclr;  headsav = q->rc.headclr; tasksav = q->rc.taskclr;
2243       SETw(q, Show_COLORS);
2244    } else {
2245       q->rc.winflags = flgssav; q->rc.summclr = summsav;
2246       q->rc.msgsclr = msgssav;  q->rc.headclr = headsav; q->rc.taskclr = tasksav;
2247    }
2248    capsmk(q);
2249 }
2250
2251
2252 // Change colors used in display
2253 static void wins_colors (void)
2254 {
2255 #define kbdABORT  'q'
2256 #define kbdAPPLY  '\n'
2257    int clr = Curwin->rc.taskclr, *pclr = &Curwin->rc.taskclr;
2258    char ch, tgt = 'T';
2259
2260    if (0 >= max_colors) {
2261       show_msg("\aNo colors to map!");
2262       return;
2263    }
2264    winsclrhlp(Curwin, 1);
2265    putp(Cap_clr_scr);
2266    putp(Cap_curs_huge);
2267
2268    do {
2269       putp(Cap_home);
2270          /* this string is well above ISO C89's minimum requirements! */
2271       show_special(
2272          1,
2273          fmtmk(
2274             COLOR_help,
2275             procps_version,
2276             Curwin->grpname,
2277             CHKw(Curwin, View_NOBOLD) ? "On" : "Off",
2278             CHKw(Curwin, Show_COLORS) ? "On" : "Off",
2279             CHKw(Curwin, Show_HIBOLD) ? "On" : "Off",
2280             tgt,
2281             clr,
2282             Curwin->grpname
2283          )
2284       );
2285       chin(0, &ch, 1);
2286       switch (ch) {
2287          case 'S':
2288             pclr = &Curwin->rc.summclr;
2289             clr = *pclr;
2290             tgt = ch;
2291             break;
2292          case 'M':
2293             pclr = &Curwin->rc.msgsclr;
2294             clr = *pclr;
2295             tgt = ch;
2296             break;
2297          case 'H':
2298             pclr = &Curwin->rc.headclr;
2299             clr = *pclr;
2300             tgt = ch;
2301             break;
2302          case 'T':
2303             pclr = &Curwin->rc.taskclr;
2304             clr = *pclr;
2305             tgt = ch;
2306             break;
2307          case '0' ... '7':
2308             clr = ch - '0';
2309             *pclr = clr;
2310             break;
2311          case 'B':
2312             TOGw(Curwin, View_NOBOLD);
2313             break;
2314          case 'b':
2315             TOGw(Curwin, Show_HIBOLD);
2316             break;
2317          case 'z':
2318             TOGw(Curwin, Show_COLORS);
2319             break;
2320          case 'a':
2321          case 'w':
2322             win_select(ch);
2323             winsclrhlp(Curwin, 1);
2324             clr = Curwin->rc.taskclr, pclr = &Curwin->rc.taskclr;
2325             tgt = 'T';
2326             break;
2327       }
2328       capsmk(Curwin);
2329    } while (kbdAPPLY != ch && kbdABORT != ch);
2330
2331    if (kbdABORT == ch)
2332       winsclrhlp(Curwin, 0);
2333    putp(Cap_curs_norm);
2334
2335 #undef kbdABORT
2336 #undef kbdAPPLY
2337 }
2338
2339
2340 // Manipulate flag(s) for all our windows.
2341 static void wins_reflag (int what, int flg)
2342 {
2343    WIN_t *w;
2344
2345    w = Curwin;
2346    do {
2347       switch (what) {
2348          case Flags_TOG:
2349             TOGw(w, flg);
2350             break;
2351          case Flags_SET:                /* Ummmm, i can't find anybody */
2352             SETw(w, flg);               /* who uses Flags_set ...      */
2353             break;
2354          case Flags_OFF:
2355             OFFw(w, flg);
2356             break;
2357       }
2358       // a flag with special significance -- user wants to rebalance
2359       // display so we gotta' 'off' one number then force on two flags...
2360       if (EQUWINS_cwo == flg) {
2361          w->rc.maxtasks = 0;
2362          SETw(w, Show_IDLEPS | VISIBLE_tsk);
2363       }
2364       w = w->next;
2365    } while (w != Curwin);
2366 }
2367
2368
2369 // using a flag to avoid other code seeing inconsistant state
2370 static volatile int need_resize;
2371 static void wins_resize_sighandler (int dont_care_sig)
2372 {
2373    (void)dont_care_sig;
2374    need_resize = 1;
2375    ZAP_TIMEOUT
2376 }
2377
2378
2379 // Set the screen dimensions and arrange for the real workhorse.
2380 // (also) catches:
2381 //    SIGWINCH and SIGCONT
2382 static void wins_resize (void)
2383 {
2384    struct winsize wz;
2385    char *env_columns;  // Unix98 environment variable COLUMNS
2386    char *env_lines;    // Unix98 environment variable LINES
2387
2388    Screen_cols = columns;   // <term.h>
2389    Screen_rows = lines;     // <term.h>
2390
2391    if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &wz) != -1 && wz.ws_col>0 && wz.ws_row>0) {
2392       Screen_cols = wz.ws_col;
2393       Screen_rows = wz.ws_row;
2394    }
2395
2396    if (Batch) Screen_rows = MAXINT;
2397
2398    env_columns = getenv("COLUMNS");
2399    if(env_columns && *env_columns){
2400       long t;
2401       char *endptr;
2402       t = strtol(env_columns, &endptr, 0);
2403       if(!*endptr && (t>0) && (t<=0x7fffffffL)) Screen_cols = (int)t;
2404    }
2405    env_lines   = getenv("LINES");
2406    if(env_lines && *env_lines){
2407       long t;
2408       char *endptr;
2409       t = strtol(env_lines, &endptr, 0);
2410       if(!*endptr && (t>0) && (t<=0x7fffffffL)) Screen_rows = (int)t;
2411    }
2412
2413    // be crudely tolerant of crude tty emulators
2414    if (avoid_last_column) Screen_cols--;
2415
2416    // we might disappoint some folks (but they'll deserve it)
2417    if (SCREENMAX < Screen_cols) Screen_cols = SCREENMAX;
2418
2419    // keep our support for output optimization in sync with current reality
2420    // note: when we're in Batch mode, we don't really need a Pseudo_scrn and
2421    //       when not Batch, our buffer will contain 1 extra 'line' since
2422    //       Msg_row is never represented -- but it's nice to have some space
2423    //       between us and the great-beyond...
2424    Pseudo_cols = Screen_cols + CLRBUFSIZ + 1;
2425    if (Batch) Pseudo_size = ROWBUFSIZ + 1;
2426    else       Pseudo_size = Pseudo_cols * Screen_rows;
2427    Pseudo_scrn = alloc_r(Pseudo_scrn, Pseudo_size);
2428
2429    // force rebuild of column headers AND libproc/readproc requirements
2430    Frames_libflags = 0;
2431 }
2432
2433
2434 // Set up the raw/incomplete field group windows --
2435 // they'll be finished off after startup completes.
2436 // [ and very likely that will override most/all of our efforts ]
2437 // [               --- life-is-NOT-fair ---                     ]
2438 static void windows_stage1 (void)
2439 {
2440    WIN_t *w;
2441    int i;
2442
2443    for (i = 0; i < GROUPSMAX; i++) {
2444       w = &Winstk[i];
2445       w->winnum = i + 1;
2446       w->rc = Rc.win[i];
2447       w->captab[0] = Cap_norm;
2448       w->captab[1] = Cap_norm;
2449       w->captab[2] = w->cap_bold;
2450       w->captab[3] = w->capclr_sum;
2451       w->captab[4] = w->capclr_msg;
2452       w->captab[5] = w->capclr_pmt;
2453       w->captab[6] = w->capclr_hdr;
2454       w->captab[7] = w->capclr_rowhigh;
2455       w->captab[8] = w->capclr_rownorm;
2456       w->next = w + 1;
2457       w->prev = w - 1;
2458       ++w;
2459    }
2460       /* fixup the circular chains... */
2461    Winstk[3].next = &Winstk[0];
2462    Winstk[0].prev = &Winstk[3];
2463    Curwin = Winstk;
2464 }
2465
2466
2467 // This guy just completes the field group windows after the
2468 // rcfiles have been read and command line arguments parsed
2469 static void windows_stage2 (void)
2470 {
2471    int i;
2472
2473    for (i = 0; i < GROUPSMAX; i++) {
2474       win_names(&Winstk[i], Winstk[i].rc.winname);
2475       capsmk(&Winstk[i]);
2476    }
2477    // rely on this next guy to force a call (eventually) to reframewins
2478    wins_resize();
2479 }
2480
2481
2482 /*######  Main Screen routines  ##########################################*/
2483
2484 // Process keyboard input during the main loop
2485 static void do_key (unsigned c)
2486 {
2487    // standardized 'secure mode' errors
2488    static const char err_secure[] = "\aUnavailable in secure mode";
2489    static const char err_num_cpus[] = "\aSorry, terminal is not big enough";
2490 #ifdef WARN_NOT_SMP
2491    // standardized 'smp' errors
2492    static const char err_smp[] = "\aSorry, only 1 cpu detected";
2493 #endif
2494
2495    switch (c) {
2496       case '1':
2497          if (Cpu_tot+7 > Screen_rows && CHKw(Curwin, View_CPUSUM)) {
2498             show_msg(err_num_cpus);
2499             break;
2500          }
2501 #ifdef WARN_NOT_SMP
2502          if (Cpu_tot > 1) TOGw(Curwin, View_CPUSUM);
2503          else show_msg(err_smp);
2504 #else
2505          TOGw(Curwin, View_CPUSUM);
2506 #endif
2507          break;
2508
2509       case 'a':
2510          if (Rc.mode_altscr) Curwin = Curwin->next;
2511          break;
2512
2513       case 'A':
2514          Rc.mode_altscr = !Rc.mode_altscr;
2515          wins_resize();
2516          break;
2517
2518       case 'b':
2519          if (VIZCHKc) {
2520             if (!CHKw(Curwin, Show_HICOLS | Show_HIROWS))
2521                show_msg("\aNothing to highlight!");
2522             else {
2523                TOGw(Curwin, Show_HIBOLD);
2524                capsmk(Curwin);
2525             }
2526          }
2527          break;
2528
2529       case 'B':
2530          TOGw(Curwin, View_NOBOLD);
2531          capsmk(Curwin);
2532          break;
2533
2534       case 'c':
2535          VIZTOGc(Show_CMDLIN);
2536          break;
2537
2538       case 'd':
2539       case 's':
2540          if (Secure_mode)
2541             show_msg(err_secure);
2542          else {
2543             float tmp =
2544                get_float(fmtmk("Change delay from %.1f to", Rc.delay_time));
2545             if (tmp > -1) Rc.delay_time = tmp;
2546          }
2547          break;
2548
2549       case 'f':
2550          if (VIZCHKc) fields_toggle();
2551          break;
2552
2553       case 'F':
2554       case 'O':
2555          if (VIZCHKc) fields_sort();
2556          break;
2557
2558       case 'g':
2559          if (Rc.mode_altscr) {
2560             char tmp[GETBUFSIZ];
2561             strcpy(tmp, ask4str(fmtmk("Rename window '%s' to (1-3 chars)", Curwin->rc.winname)));
2562             if (tmp[0]) win_names(Curwin, tmp);
2563          }
2564          break;
2565
2566       case 'G':
2567          win_select(0);
2568          break;
2569
2570       case 'H':
2571          if (VIZCHKc) {
2572             TOGw(Curwin, Show_THREADS);
2573             show_msg(fmtmk("Show threads %s"
2574                , CHKw(Curwin, Show_THREADS) ? "On" : "Off"));
2575          }
2576          break;
2577
2578       case 'h':
2579       case '?':
2580       {  char ch;
2581          putp(Cap_clr_scr);
2582          putp(Cap_curs_huge);
2583             /* this string is well above ISO C89's minimum requirements! */
2584          show_special(
2585             1,
2586             fmtmk(
2587                KEYS_help,
2588                procps_version,
2589                Curwin->grpname,
2590                CHKw(Curwin, Show_CTIMES) ? "On" : "Off",
2591                Rc.delay_time,
2592                Secure_mode ? "On" : "Off",
2593                Secure_mode ? "" : KEYS_help_unsecured
2594             )
2595          );
2596          chin(0, &ch, 1);
2597          if ('?' == ch || 'h' == ch) {
2598             do {
2599                putp(Cap_clr_scr);
2600                show_special(1, fmtmk(WINDOWS_help
2601                   , Curwin->grpname
2602                   , Winstk[0].rc.winname
2603                   , Winstk[1].rc.winname
2604                   , Winstk[2].rc.winname
2605                   , Winstk[3].rc.winname));
2606                chin(0, &ch, 1);
2607                win_select(ch);
2608             } while ('\n' != ch);
2609          }
2610          putp(Cap_curs_norm);
2611       }
2612          break;
2613
2614       case 'i':
2615          VIZTOGc(Show_IDLEPS);
2616          break;
2617
2618       case 'I':
2619 #ifdef WARN_NOT_SMP
2620          if (Cpu_tot > 1) {
2621             Rc.mode_irixps = !Rc.mode_irixps;
2622             show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off"));
2623          } else
2624             show_msg(err_smp);
2625 #else
2626          Rc.mode_irixps = !Rc.mode_irixps;
2627          show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off"));
2628 #endif
2629          if(Rc.mode_irixps && smp_num_cpus>1){
2630             // good for 100 CPUs per process
2631             pcpu_max_value = 9999.0;
2632             Fieldstab[P_CPU].fmts = " %4.0f";
2633          } else {
2634             pcpu_max_value = 99.9;
2635             Fieldstab[P_CPU].fmts = " %#4.1f";
2636          }
2637          break;
2638
2639       case 'k':
2640          if (Secure_mode) {
2641             show_msg(err_secure);
2642          } else {
2643             int sig, pid = get_int("PID to kill");
2644             if (pid > 0) {
2645                sig = signal_name_to_number(
2646                   ask4str(fmtmk("Kill PID %d with signal [%i]", pid, DEF_SIGNAL)));
2647                if (sig == -1) sig = DEF_SIGNAL;
2648                if (sig && kill(pid, sig))
2649                   show_msg(fmtmk("\aKill of PID '%d' with '%d' failed: %s", pid, sig, strerror(errno)));
2650             }
2651          }
2652          break;
2653
2654       case 'l':
2655          TOGw(Curwin, View_LOADAV);
2656          break;
2657
2658       case 'm':
2659          TOGw(Curwin, View_MEMORY);
2660          break;
2661
2662       case 'n':
2663       case '#':
2664          if (VIZCHKc) {
2665             int num =
2666                get_int(fmtmk("Maximum tasks = %d, change to (0 is unlimited)", Curwin->rc.maxtasks));
2667             if (num > -1) Curwin->rc.maxtasks = num;
2668          }
2669          break;
2670
2671       case 'o':
2672          if (VIZCHKc) fields_reorder();
2673          break;
2674
2675       case 'q':
2676          end_pgm(0);
2677
2678       case 'r':
2679          if (Secure_mode)
2680             show_msg(err_secure);
2681          else {
2682             int val, pid = get_int("PID to renice");
2683             if (pid > 0) {
2684                val = get_int(fmtmk("Renice PID %d to value", pid));
2685                if (setpriority(PRIO_PROCESS, (unsigned)pid, val))
2686                   show_msg(fmtmk("\aRenice of PID %d to %d failed: %s", pid, val, strerror(errno)));
2687             }
2688          }
2689          break;
2690
2691       case 'R':
2692          VIZTOGc(Qsrt_NORMAL);
2693          break;
2694
2695       case 'S':
2696          if (VIZCHKc) {
2697             TOGw(Curwin, Show_CTIMES);
2698             show_msg(fmtmk("Cumulative time %s", CHKw(Curwin, Show_CTIMES) ? "On" : "Off"));
2699          }
2700          break;
2701
2702       case 't':
2703          TOGw(Curwin, View_STATES);
2704          break;
2705
2706 //    case 'u':
2707 //       if (VIZCHKc)
2708 //          strcpy(Curwin->colusrnam, ask4str("Which user (blank for all)"));
2709 //       break;
2710
2711       case 'u':
2712 //       if (!VIZCHKc) break;
2713          do {
2714             const char *errmsg;
2715             const char *answer;
2716             answer = ask4str("Which user (blank for all)");
2717             // FIXME: do this better:
2718             if (!answer || *answer=='\0' || *answer=='\n' || *answer=='\r' || *answer=='\t' || *answer==' ') {
2719                selection_type = 0;
2720                selection_uid = -1;
2721                break;
2722             }
2723             errmsg = parse_uid(answer, &selection_uid);
2724             if (errmsg) {
2725                show_msg(errmsg);
2726                // Change settings here? I guess not.
2727                break;
2728             }
2729             selection_type = 'u';
2730          } while(0);
2731          break;
2732
2733       case 'U':
2734 //       if (!VIZCHKc) break;
2735          do {
2736             const char *errmsg;
2737             const char *answer;
2738             answer = ask4str("Which user (blank for all)");
2739             // FIXME: do this better:
2740             if (!answer || *answer=='\0' || *answer=='\n' || *answer=='\r' || *answer=='\t' || *answer==' ') {
2741                selection_type = 0;
2742                selection_uid = -1;
2743                break;
2744             }
2745             errmsg = parse_uid(answer, &selection_uid);
2746             if (errmsg) {
2747                show_msg(errmsg);
2748                // Change settings here? I guess not.
2749                break;
2750             }
2751             selection_type = 'U';
2752          } while(0);
2753          break;
2754
2755       case 'w':
2756          if (Rc.mode_altscr) Curwin = Curwin->prev;
2757          break;
2758
2759       case 'W':
2760       {  const char *err = rc_write_whatever();
2761          if (err)
2762             show_msg(fmtmk("\aFailed '%s' open: %s", Rc_name, err));
2763          else
2764             show_msg(fmtmk("Wrote configuration to '%s'", Rc_name));
2765       }
2766          break;
2767
2768       case 'x':
2769          if (VIZCHKc) {
2770             TOGw(Curwin, Show_HICOLS);
2771             capsmk(Curwin);
2772          }
2773          break;
2774
2775       case 'y':
2776          if (VIZCHKc) {
2777             TOGw(Curwin, Show_HIROWS);
2778             capsmk(Curwin);
2779          }
2780          break;
2781
2782       case 'z':
2783          if (VIZCHKc) {
2784             TOGw(Curwin, Show_COLORS);
2785             capsmk(Curwin);
2786          }
2787          break;
2788
2789       case 'Z':
2790          wins_colors();
2791          break;
2792
2793       case '-':
2794          if (Rc.mode_altscr) TOGw(Curwin, VISIBLE_tsk);
2795          break;
2796
2797       case '_':
2798          if (Rc.mode_altscr) wins_reflag(Flags_TOG, VISIBLE_tsk);
2799          break;
2800
2801       case '=':
2802          Curwin->rc.maxtasks = 0;
2803          SETw(Curwin, Show_IDLEPS | VISIBLE_tsk);
2804          Monpidsidx = 0;
2805          selection_type = '\0';
2806          break;
2807
2808       case '+':
2809          if (Rc.mode_altscr) SETw(Curwin, EQUWINS_cwo);
2810          break;
2811
2812       case '<':
2813          if (VIZCHKc) {
2814             FLG_t *p = Curwin->procflags + Curwin->maxpflgs - 1;
2815             while (*p != Curwin->rc.sortindx) --p;
2816             if (--p >= Curwin->procflags)
2817                Curwin->rc.sortindx = *p;
2818          }
2819          break;
2820
2821       case '>':
2822          if (VIZCHKc) {
2823             FLG_t *p = Curwin->procflags;
2824             while (*p != Curwin->rc.sortindx) ++p;
2825             if (++p < Curwin->procflags + Curwin->maxpflgs)
2826                Curwin->rc.sortindx = *p;
2827          }
2828          break;
2829
2830       case 'M':         // these keys represent old-top compatability
2831       case 'N':         // -- grouped here so that if users could ever
2832       case 'P':         // be weaned, we would just whack this part...
2833       case 'T':
2834       {  static struct {
2835             const char     *xmsg;
2836             const unsigned  xkey;
2837             const FLG_t     sort;
2838          } xtab[] = {
2839             { "Memory", 'M', P_MEM, }, { "Numerical", 'N', P_PID, },
2840             { "CPU",    'P', P_CPU, }, { "Time",      'T', P_TM2  }, };
2841          int i;
2842          for (i = 0; i < MAXTBL(xtab); ++i)
2843             if (c == xtab[i].xkey) {
2844                Curwin->rc.sortindx = xtab[i].sort;
2845 //               show_msg(fmtmk("%s sort compatibility key honored", xtab[i].xmsg));
2846                break;
2847             }
2848       }
2849          break;
2850
2851       case '\n':        // just ignore these, they'll have the effect
2852       case ' ':         // of refreshing display after waking us up !
2853          break;
2854
2855       default:
2856          show_msg("\aUnknown command - try 'h' for help");
2857    }
2858    // The following assignment will force a rebuild of all column headers and
2859    // the PROC_FILLxxx flags.  It's NOT simply lazy programming.  Here are
2860    // some keys that COULD require new column headers and/or libproc flags:
2861    //    'A' - likely
2862    //    'c' - likely when !Mode_altscr, maybe when Mode_altscr
2863    //    'F' - maybe, if new field forced on
2864    //    'f' - likely
2865    //    'G' - likely
2866    //    'O' - maybe, if new field forced on
2867    //    'o' - maybe, if new field brought into view
2868    //    'Z' - likely, if 'Curwin' changed when !Mode_altscr
2869    //    '-' - likely (restricted to Mode_altscr)
2870    //    '_' - likely (restricted to Mode_altscr)
2871    //    '=' - maybe, but only when Mode_altscr
2872    //    '+' - likely (restricted to Mode_altscr)
2873    // ( At this point we have a human being involved and so have all the time )
2874    // ( in the world.  We can afford a few extra cpu cycles every now & then! )
2875    Frames_libflags = 0;
2876 }
2877
2878
2879 // State display *Helper* function to calc and display the state
2880 // percentages for a single cpu.  In this way, we can support
2881 // the following environments without the usual code bloat.
2882 //    1) single cpu machines
2883 //    2) modest smp boxes with room for each cpu's percentages
2884 //    3) massive smp guys leaving little or no room for process
2885 //       display and thus requiring the cpu summary toggle
2886 static void summaryhlp (CPU_t *cpu, const char *pfx)
2887 {
2888    // we'll trim to zero if we get negative time ticks,
2889    // which has happened with some SMP kernels (pre-2.4?)
2890 #define TRIMz(x)  ((tz = (SIC_t)(x)) < 0 ? 0 : tz)
2891    SIC_t u_frme, s_frme, n_frme, i_frme, w_frme, x_frme, y_frme, z_frme, tot_frme, tz;
2892    float scale;
2893
2894    u_frme = cpu->u - cpu->u_sav;
2895    s_frme = cpu->s - cpu->s_sav;
2896    n_frme = cpu->n - cpu->n_sav;
2897    i_frme = TRIMz(cpu->i - cpu->i_sav);
2898    w_frme = cpu->w - cpu->w_sav;
2899    x_frme = cpu->x - cpu->x_sav;
2900    y_frme = cpu->y - cpu->y_sav;
2901    z_frme = cpu->z - cpu->z_sav;
2902    tot_frme = u_frme + s_frme + n_frme + i_frme + w_frme + x_frme + y_frme + z_frme;
2903    if (tot_frme < 1) tot_frme = 1;
2904    scale = 100.0 / (float)tot_frme;
2905
2906    // display some kinda' cpu state percentages
2907    // (who or what is explained by the passed prefix)
2908    show_special(
2909       0,
2910       fmtmk(
2911          States_fmts,
2912          pfx,
2913          (float)u_frme * scale,
2914          (float)s_frme * scale,
2915          (float)n_frme * scale,
2916          (float)i_frme * scale,
2917          (float)w_frme * scale,
2918          (float)x_frme * scale,
2919          (float)y_frme * scale,
2920          (float)z_frme * scale
2921       )
2922    );
2923    Msg_row += 1;
2924
2925    // remember for next time around
2926    cpu->u_sav = cpu->u;
2927    cpu->s_sav = cpu->s;
2928    cpu->n_sav = cpu->n;
2929    cpu->i_sav = cpu->i;
2930    cpu->w_sav = cpu->w;
2931    cpu->x_sav = cpu->x;
2932    cpu->y_sav = cpu->y;
2933    cpu->z_sav = cpu->z;
2934
2935 #undef TRIMz
2936 }
2937
2938
2939 // Begin a new frame by:
2940 //    1) Refreshing the all important proc table
2941 //    2) Displaying uptime and load average (maybe)
2942 //    3) Displaying task/cpu states (maybe)
2943 //    4) Displaying memory & swap usage (maybe)
2944 // and then, returning a pointer to the pointers to the proc_t's!
2945 static proc_t **summary_show (void)
2946 {
2947    static proc_t **p_table = NULL;
2948    static CPU_t  *smpcpu = NULL;
2949
2950    // whoa first time, gotta' prime the pump...
2951    if (!p_table) {
2952       p_table = procs_refresh(NULL, Frames_libflags);
2953       putp(Cap_clr_scr);
2954       putp(Cap_rmam);
2955 #ifndef PROF
2956       // sleep for half a second
2957       tv.tv_sec = 0;
2958       tv.tv_usec = 500000;
2959       select(0, NULL, NULL, NULL, &tv);  // ought to loop until done
2960 #endif
2961    } else {
2962       putp(Batch ? "\n\n" : Cap_home);
2963    }
2964    p_table = procs_refresh(p_table, Frames_libflags);
2965
2966    // Display Uptime and Loadavg
2967    if (CHKw(Curwin, View_LOADAV)) {
2968       if (!Rc.mode_altscr) {
2969          show_special(0, fmtmk(LOADAV_line, Myname, sprint_uptime()));
2970       } else {
2971          show_special(
2972             0,
2973             fmtmk(
2974                CHKw(Curwin, VISIBLE_tsk) ? LOADAV_line_alt : LOADAV_line,
2975                Curwin->grpname,
2976                sprint_uptime()
2977             )
2978          );
2979       }
2980       Msg_row += 1;
2981    }
2982
2983    // Display Task and Cpu(s) States
2984    if (CHKw(Curwin, View_STATES)) {
2985       show_special(
2986          0,
2987          fmtmk(
2988             STATES_line1,
2989             Frame_maxtask, Frame_running, Frame_sleepin, Frame_stopped, Frame_zombied
2990          )
2991       );
2992       Msg_row += 1;
2993
2994       smpcpu = cpus_refresh(smpcpu);
2995
2996       if (CHKw(Curwin, View_CPUSUM)) {
2997          // display just the 1st /proc/stat line
2998          summaryhlp(&smpcpu[Cpu_tot], "Cpu(s):");
2999       } else {
3000          int i;
3001          char tmp[SMLBUFSIZ];
3002          // display each cpu's states separately
3003          for (i = 0; i < Cpu_tot; i++) {
3004             snprintf(tmp, sizeof(tmp), "Cpu%-3d:", smpcpu[i].id);
3005             summaryhlp(&smpcpu[i], tmp);
3006          }
3007       }
3008    }
3009
3010    // Display Memory and Swap stats
3011    meminfo();
3012    if (CHKw(Curwin, View_MEMORY)) {
3013       show_special(0, fmtmk(MEMORY_line1
3014          , kb_main_total, kb_main_used, kb_main_free, kb_main_buffers));
3015       show_special(0, fmtmk(MEMORY_line2
3016          , kb_swap_total, kb_swap_used, kb_swap_free, kb_main_cached));
3017       Msg_row += 2;
3018    }
3019
3020    SETw(Curwin, NEWFRAM_cwo);
3021    return p_table;
3022 }
3023
3024
3025 #define PAGES_TO_KB(n)  (unsigned long)( (n) << page_to_kb_shift )
3026
3027 // the following macro is our means to 'inline' emitting a column -- next to
3028 // procs_refresh, that's the most frequent and costly part of top's job !
3029 #define MKCOL(va...) do {                                                    \
3030    if(likely(!(   CHKw(q, Show_HICOLS)  &&  q->rc.sortindx==i   ))) {        \
3031       snprintf(cbuf, sizeof(cbuf), f, ## va);                                \
3032    } else {                                                                  \
3033       snprintf(_z, sizeof(_z), f, ## va);                                    \
3034       snprintf(cbuf, sizeof(cbuf), "%s%s%s",                                 \
3035          q->capclr_rowhigh,                                                  \
3036          _z,                                                                 \
3037          !(CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rownorm : "" \
3038       );                                                                     \
3039       pad += q->len_rowhigh;                                                 \
3040       if (!(CHKw(q, Show_HIROWS) && 'R' == p->state)) pad += q->len_rownorm; \
3041    }                                                                         \
3042 } while (0)
3043
3044 // Display information for a single task row.
3045 static void task_show (const WIN_t *q, const proc_t *p)
3046 {
3047    char rbuf[ROWBUFSIZ];
3048    char *rp = rbuf;
3049    int j, x, pad;
3050
3051    *rp = '\0';
3052
3053    pad = Rc.mode_altscr;
3054 //   if (pad) rp = scat(rp, " ");
3055
3056    for (x = 0; x < q->maxpflgs; x++) {
3057       char cbuf[ROWBUFSIZ], _z[ROWBUFSIZ];
3058       FLG_t       i = q->procflags[x];          // support for our field/column
3059       const char *f = Fieldstab[i].fmts;        // macro AND sometimes the fmt
3060       unsigned    s = Fieldstab[i].scale;       // string must be altered !
3061       unsigned    w = Fieldstab[i].width;
3062
3063       int advance = (x==0) && !Rc.mode_altscr;
3064
3065       switch (i) {
3066          case P_CMD:
3067          {  char tmp[ROWBUFSIZ];
3068             unsigned flags;
3069             int maxcmd = q->maxcmdln;
3070             if (CHKw(q, Show_CMDLIN)) flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS;
3071             else                      flags = ESC_DEFUNCT;
3072             escape_command(tmp, p, sizeof tmp, &maxcmd, flags);
3073             MKCOL(q->maxcmdln, q->maxcmdln, tmp);
3074          }
3075             break;
3076          case P_COD:
3077             MKCOL(scale_num(PAGES_TO_KB(p->trs), w, s));
3078             break;
3079          case P_CPN:
3080             MKCOL((unsigned)p->processor);
3081             break;
3082          case P_CPU:
3083          {  float u = (float)p->pcpu * Frame_tscale;
3084             if (u > pcpu_max_value) u = pcpu_max_value;
3085             MKCOL(u);
3086          }
3087             break;
3088          case P_DAT:
3089             MKCOL(scale_num(PAGES_TO_KB(p->drs), w, s));
3090             break;
3091          case P_DRT:
3092             MKCOL(scale_num((unsigned)p->dt, w, s));
3093             break;
3094          case P_FLG:
3095          {  char tmp[TNYBUFSIZ];
3096             snprintf(tmp, sizeof(tmp), f, (long)p->flags);
3097             for (j = 0; tmp[j]; j++) if ('0' == tmp[j]) tmp[j] = '.';
3098             f = tmp;
3099             MKCOL("");
3100          }
3101             break;
3102          case P_FLT:
3103             MKCOL(scale_num(p->maj_flt, w, s));
3104             break;
3105          case P_GRP:
3106             MKCOL(p->egroup);
3107             break;
3108          case P_MEM:
3109             MKCOL((float)PAGES_TO_KB(p->resident) * 100 / kb_main_total);
3110             break;
3111          case P_NCE:
3112             MKCOL((int)p->nice);
3113             break;
3114          case P_PID:
3115             MKCOL((unsigned)p->XXXID);
3116             break;
3117          case P_PPD:
3118             MKCOL((unsigned)p->ppid);
3119             break;
3120          case P_PRI:
3121             if (unlikely(-99 > p->priority) || unlikely(999 < p->priority)) {
3122                f = "  RT";
3123                MKCOL("");
3124             } else
3125                MKCOL((int)p->priority);
3126             break;
3127          case P_RES:
3128             MKCOL(scale_num(PAGES_TO_KB(p->resident), w, s));
3129             break;
3130          case P_SHR:
3131             MKCOL(scale_num(PAGES_TO_KB(p->share), w, s));
3132             break;
3133          case P_STA:
3134             MKCOL(p->state);
3135             break;
3136          case P_SWP:
3137             MKCOL(scale_num(PAGES_TO_KB(p->size - p->resident), w, s));
3138             break;
3139          case P_TME:
3140          case P_TM2:
3141          {  TIC_t t = p->utime + p->stime;
3142             if (CHKw(q, Show_CTIMES))
3143                t += (p->cutime + p->cstime);
3144             MKCOL(scale_tics(t, w));
3145          }
3146             break;
3147          case P_TTY:
3148          {  char tmp[TNYBUFSIZ];
3149             dev_to_tty(tmp, (int)w, p->tty, p->XXXID, ABBREV_DEV);
3150             MKCOL(tmp);
3151          }
3152             break;
3153          case P_UID:
3154             MKCOL((unsigned)p->euid);
3155             break;
3156          case P_URE:
3157             MKCOL(p->euser);
3158             break;
3159          case P_URR:
3160             MKCOL(p->ruser);
3161             break;
3162          case P_VRT:
3163             MKCOL(scale_num(PAGES_TO_KB(p->size), w, s));
3164             break;
3165          case P_WCH:
3166             if (No_ksyms) {
3167                f = " %08lx ";
3168                MKCOL((long)p->wchan);
3169             } else {
3170                MKCOL(lookup_wchan(p->wchan, p->XXXID));
3171             }
3172             break;
3173
3174         } /* end: switch 'procflag' */
3175
3176         rp = scat(rp, cbuf+advance);
3177    } /* end: for 'maxpflgs' */
3178
3179    PUFF(
3180       "\n%s%.*s%s%s",
3181       (CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rowhigh : q->capclr_rownorm,
3182       Screen_cols + pad,
3183       rbuf,
3184       Caps_off,
3185       "" /*Cap_clr_eol*/
3186    );
3187
3188 #undef MKCOL
3189 }
3190
3191
3192 // Squeeze as many tasks as we can into a single window,
3193 // after sorting the passed proc table.
3194 static void window_show (proc_t **ppt, WIN_t *q, int *lscr)
3195 {
3196 #ifdef SORT_SUPRESS
3197    // the 1 flag that DOES and 2 flags that MAY impact our proc table qsort
3198 #define srtMASK  ~( Qsrt_NORMAL | Show_CMDLIN | Show_CTIMES )
3199    static FLG_t sav_indx = 0;
3200    static int   sav_flgs = -1;
3201 #endif
3202    int i, lwin;
3203
3204    // Display Column Headings -- and distract 'em while we sort (maybe)
3205    PUFF("\n%s%s%s%s", q->capclr_hdr, q->columnhdr, Caps_off, Cap_clr_eol);
3206
3207 #ifdef SORT_SUPRESS
3208    if (CHKw(Curwin, NEWFRAM_cwo)
3209    || sav_indx != q->rc.sortindx
3210    || sav_flgs != (q->rc.winflags & srtMASK)) {
3211       sav_indx = q->rc.sortindx;
3212       sav_flgs = (q->rc.winflags & srtMASK);
3213 #endif
3214       if (CHKw(q, Qsrt_NORMAL)) Frame_srtflg = 1; // this one's always needed!
3215       else                      Frame_srtflg = -1;
3216       Frame_ctimes = CHKw(q, Show_CTIMES);        // this and next, only maybe
3217       Frame_cmdlin = CHKw(q, Show_CMDLIN);
3218       qsort(ppt, Frame_maxtask, sizeof(proc_t *), Fieldstab[q->rc.sortindx].sort);
3219 #ifdef SORT_SUPRESS
3220    }
3221 #endif
3222    // account for column headings
3223    (*lscr)++;
3224    lwin = 1;
3225    i = 0;
3226
3227    while ( ppt[i]->tid != -1 && *lscr < Max_lines  &&  (!q->winlines || (lwin <= q->winlines)) ) {
3228       if ((CHKw(q, Show_IDLEPS) || ('S' != ppt[i]->state && 'Z' != ppt[i]->state && 'T' != ppt[i]->state))
3229       && good_uid(ppt[i]) ) {
3230          // Display a process Row
3231          task_show(q, ppt[i]);
3232          (*lscr)++;
3233          ++lwin;
3234       }
3235       ++i;
3236    }
3237    // for this frame that window's toast, cleanup for next time
3238    q->winlines = 0;
3239    OFFw(Curwin, FLGSOFF_cwo);
3240
3241 #ifdef SORT_SUPRESS
3242 #undef srtMASK
3243 #endif
3244 }
3245
3246
3247 /*######  Entry point plus two  ##########################################*/
3248
3249 // This guy's just a *Helper* function who apportions the
3250 // remaining amount of screen real estate under multiple windows
3251 static void framehlp (int wix, int max)
3252 {
3253    int i, rsvd, size, wins;
3254
3255    // calc remaining number of visible windows + total 'user' lines
3256    for (i = wix, rsvd = 0, wins = 0; i < GROUPSMAX; i++) {
3257       if (CHKw(&Winstk[i], VISIBLE_tsk)) {
3258          rsvd += Winstk[i].rc.maxtasks;
3259          ++wins;
3260          if (max <= rsvd) break;
3261       }
3262    }
3263    if (!wins) wins = 1;
3264    // set aside 'rsvd' & deduct 1 line/window for the columns heading
3265    size = (max - wins) - rsvd;
3266    if (0 <= size) size = max;
3267    size = (max - wins) / wins;
3268
3269    // for remaining windows, set WIN_t winlines to either the user's
3270    // maxtask (1st choice) or our 'foxized' size calculation
3271    // (foxized  adj. -  'fair and balanced')
3272    for (i = wix ; i < GROUPSMAX; i++) {
3273       if (CHKw(&Winstk[i], VISIBLE_tsk)) {
3274          Winstk[i].winlines =
3275             Winstk[i].rc.maxtasks ? Winstk[i].rc.maxtasks : size;
3276       }
3277    }
3278 }
3279
3280
3281 // Initiate the Frame Display Update cycle at someone's whim!
3282 // This routine doesn't do much, mostly he just calls others.
3283 //
3284 // (Whoa, wait a minute, we DO caretake those row guys, plus)
3285 // (we CALCULATE that IMPORTANT Max_lines thingy so that the)
3286 // (*subordinate* functions invoked know WHEN the user's had)
3287 // (ENOUGH already.  And at Frame End, it SHOULD be apparent)
3288 // (WE am d'MAN -- clearing UNUSED screen LINES and ensuring)
3289 // (the CURSOR is STUCK in just the RIGHT place, know what I)
3290 // (mean?  Huh, "doesn't DO MUCH"!  Never, EVER think or say)
3291 // (THAT about THIS function again, Ok?  Good that's better.)
3292 static void frame_make (void)
3293 {
3294    proc_t **ppt;
3295    int i, scrlins;
3296
3297    // note: all libproc flags are managed by
3298    //       reframewins(), who also builds each window's column headers
3299    if (!Frames_libflags) {
3300       reframewins();
3301       memset(Pseudo_scrn, '\0', Pseudo_size);
3302    }
3303    Pseudo_row = Msg_row = scrlins = 0;
3304    ppt = summary_show();
3305    Max_lines = (Screen_rows - Msg_row) - 1;
3306
3307    if (CHKw(Curwin, EQUWINS_cwo))
3308       wins_reflag(Flags_OFF, EQUWINS_cwo);
3309
3310    // sure hope each window's columns header begins with a newline...
3311    putp(tg2(0, Msg_row));
3312
3313    if (!Rc.mode_altscr) {
3314       // only 1 window to show so, piece o' cake
3315       Curwin->winlines = Curwin->rc.maxtasks;
3316       window_show(ppt, Curwin, &scrlins);
3317    } else {
3318       // maybe NO window is visible but assume, pieces o' cakes
3319       for (i = 0 ; i < GROUPSMAX; i++) {
3320          if (CHKw(&Winstk[i], VISIBLE_tsk)) {
3321             framehlp(i, Max_lines - scrlins);
3322             window_show(ppt, &Winstk[i], &scrlins);
3323          }
3324          if (Max_lines <= scrlins) break;
3325       }
3326    }
3327    // clear to end-of-screen (critical if last window is 'idleps off'),
3328    // then put the cursor in-its-place, and rid us of any prior frame's msg
3329    // (main loop must iterate such that we're always called before sleep)
3330    PUTT(
3331       "%s%s%s%s",
3332       scrlins < Max_lines ? "\n"        : "",
3333       scrlins < Max_lines ? Cap_clr_eos : "",
3334       tg2(0, Msg_row),
3335       Cap_clr_eol
3336    );
3337    fflush(stdout);
3338 }
3339
3340
3341 int main (int dont_care_argc, char *argv[])
3342 {
3343    (void)dont_care_argc;
3344    before(*argv);
3345                                         //                 +-------------+
3346    windows_stage1();                    //                 top (sic) slice
3347    configs_read();                      //                 > spread etc, <
3348    parse_args(&argv[1]);                //                 > lean stuff, <
3349    whack_terminal();                    //                 > onions etc. <
3350    windows_stage2();                    //                 as bottom slice
3351                                         //                 +-------------+
3352    signal(SIGALRM,  end_pgm);
3353    signal(SIGHUP,   end_pgm);
3354    signal(SIGINT,   end_pgm);
3355    signal(SIGPIPE,  end_pgm);
3356    signal(SIGQUIT,  end_pgm);
3357    signal(SIGTERM,  end_pgm);
3358    signal(SIGTSTP,  suspend);
3359    signal(SIGTTIN,  suspend);
3360    signal(SIGTTOU,  suspend);
3361    signal(SIGCONT,  wins_resize_sighandler);
3362    signal(SIGWINCH, wins_resize_sighandler);
3363
3364    for (;;) {
3365       if (need_resize){
3366          need_resize = 0;
3367          wins_resize();
3368       }
3369       frame_make();
3370
3371       if (Msg_awaiting) show_msg(Msg_delayed);
3372       if (Loops > 0) --Loops;
3373       if (!Loops) end_pgm(0);
3374
3375       tv.tv_sec = Rc.delay_time;
3376       tv.tv_usec = (Rc.delay_time - (int)Rc.delay_time) * 1000000;
3377
3378       if (Batch) {
3379          select(0, NULL, NULL, NULL, &tv);  // ought to loop until done
3380       } else {
3381          long file_flags;
3382          int rc;
3383          char c;
3384          fd_set fs;
3385          FD_ZERO(&fs);
3386          FD_SET(STDIN_FILENO, &fs);
3387          file_flags = fcntl(STDIN_FILENO, F_GETFL);
3388          if(file_flags==-1) file_flags=0;
3389          fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags);
3390
3391          // check 1st, in case tv zeroed (by sig handler) before it got set
3392          rc = chin(0, &c, 1);
3393          if (rc <= 0) {
3394             // EOF is pretty much a "can't happen" except for a kernel bug.
3395             // We should quickly die via SIGHUP, and thus not spin here.
3396             // if (rc == 0) end_pgm(0); /* EOF from terminal */
3397             fcntl(STDIN_FILENO, F_SETFL, file_flags);
3398             select(1, &fs, NULL, NULL, &tv);
3399             fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags);
3400          }
3401          if (chin(0, &c, 1) > 0) {
3402             fcntl(STDIN_FILENO, F_SETFL, file_flags);
3403             do_key((unsigned)c);
3404          } else {
3405             fcntl(STDIN_FILENO, F_SETFL, file_flags);
3406          }
3407       }
3408    }
3409
3410    return 0;
3411 }