1 /* top.c - Source file: show Linux processes */
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>
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.
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>
20 * Changes by Albert Cahalan, 2002-2004.
22 #include <sys/ioctl.h>
23 #include <sys/resource.h>
25 #include <sys/types.h>
37 // Foul POS defines all sorts of stuff...
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"
52 #include "proc/sysinfo.h"
53 #include "proc/version.h"
54 #include "proc/whattime.h"
58 /*###### Miscellaneous global stuff ####################################*/
60 /* The original and new terminal attributes */
61 static struct termios Savedtty,
63 static int Ttychanged = 0;
65 /* Program name used in error messages and local 'rc' file name */
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;
74 /* The run-time acquired page size */
75 static unsigned Page_size;
76 static unsigned page_to_kb_shift;
78 /* SMP Irix/Solaris mode */
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;
84 /* Specific process id monitoring support */
85 static pid_t Monpids [MONPIDMAX] = { 0 };
86 static int Monpidsidx = 0;
88 /* A postponed error message */
89 static char Msg_delayed [SMLBUFSIZ];
90 static int Msg_awaiting = 0;
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);
98 /* Configurable Display support ##################################*/
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;
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;
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. */
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
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;
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;
146 // less than stdout's normal buffer but with luck mostly '\n' anyway
147 static char Stdout_buf[2048];
151 /* ////////////////////////////////////////////////////////////// */
152 /* Special Section: multiple windows/field groups ---------------*/
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],
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
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 /* ////////////////////////////////////////////////////////////// */
179 /*###### Sort callbacks ################################################*/
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 --
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)
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)
202 if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime)
203 < ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
205 if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime)
206 > ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
209 if ( ((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime))
211 if ( ((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime))
217 SCB_NUM1(P_VRT, size)
218 SCB_NUM2(P_SWP, size, resident)
219 SCB_NUM1(P_RES, resident) // also serves P_MEM !
222 SCB_NUM1(P_SHR, share)
223 SCB_NUM1(P_FLT, maj_flt)
225 SCB_NUMx(P_STA, state)
227 static int sort_P_CMD (const proc_t **P, const proc_t **Q)
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);
237 // this part also handles the compare if both are kernel threads
238 return Frame_srtflg * strcmp((*Q)->cmd, (*P)->cmd);
241 SCB_NUM1(P_WCH, wchan)
242 SCB_NUM1(P_FLG, flags)
244 /* ///////////////////////////////// special sort for prochlp() ! */
245 static int sort_HST_t (const HST_t *P, const HST_t *Q)
247 return P->pid - Q->pid;
251 /*###### Tiny useful routine(s) ########################################*/
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)
262 rc = read(STDIN_FILENO, buf, cnt);
264 tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
265 rc = read(STDIN_FILENO, buf, cnt);
266 tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty);
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'
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, ...)
279 static char buf[BIGBUFSIZ]; // with help stuff, our buffer
280 va_list va; // requirements exceed 1k
283 vsnprintf(buf, sizeof(buf), fmts, va);
285 return (const char *)buf;
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)
294 while ((*(dst++) = *(src++)));
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)
303 static const char ws[] = "\b\e\f\n\r\t\v\x9b"; // 0x9b is an escape
306 if ((p = strpbrk(str, ws))) *p = 0;
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)
316 return Cap_can_goto ? tgoto(cursor_address, x, y) : "";
320 /*###### Exit/Interrput routines #######################################*/
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)
327 tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
328 putp(tg2(0, Screen_rows));
334 //#define ATEOJ_REPORT
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"
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
359 "\n\t Stdout_buf = %d, BUFSIZ = %u"
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"
370 , sizeof(Stdout_buf), (unsigned)BUFSIZ
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
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)"
387 , LINUX_VERSION_MAJOR(linux_version_code)
388 , LINUX_VERSION_MINOR(linux_version_code)
389 , LINUX_VERSION_PATCH(linux_version_code)
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)
399 if (str) fputs(str, fp);
405 * Normal end of execution.
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)
413 sig |= 0x80; // for a proper process exit code
414 bye_bye(stdout, sig, NULL);
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)
423 static char buf[SMLBUFSIZ];
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);
431 fprintf(stderr, "%s\n", buf);
434 /* not to worry, he'll change our exit code to 1 due to 'buf' */
435 bye_bye(stderr, 1, buf);
440 * Standard out handler */
441 static void std_out (const char *str) NORETURN;
442 static void std_out (const char *str)
444 static char buf[SMLBUFSIZ];
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);
452 fprintf(stdout, "%s\n", buf);
455 bye_bye(stdout, 0, buf);
462 * SIGTSTP, SIGTTIN and SIGTTOU */
463 // FIXME: can't do this shit in a signal handler!
464 static void suspend (int dont_care_sig)
468 tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
469 putp(tg2(0, Screen_rows));
475 /* later, after SIGCONT... */
478 tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty);
484 /*###### Misc Color/Display support ####################################*/
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)
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)
499 static int capsdone = 0;
501 // we must NOT disturb our 'empty' terminfo strings!
504 // these are the unchangeable puppies, so we only do 'em once
506 CAPCOPY(Cap_clr_eol, clr_eol);
507 CAPCOPY(Cap_clr_eos, clr_eos);
508 CAPCOPY(Cap_clr_scr, clear_screen);
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
516 if (auto_right_margin) {
517 avoid_last_column = 1;
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);
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;
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));
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);
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);
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, ...)
574 vsnprintf(tmp, sizeof(tmp), fmts, 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));
583 * Show an error message (caller may include a '\a' for sound) */
584 static void show_msg (const char *str)
600 * Show an input prompt + larger cursor */
601 static void show_pmt (const char *str)
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.
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.
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 |
646 | Msg_color, Pmt_color, = \04, \05 |
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;
655 /* handle multiple lines passed in a bunch */
656 while ((lin_end = strchr(glob, '\n'))) {
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 */
666 sub_beg = sub_end = lin;
671 case 0: /* no end delim, captab makes normal */
672 *(sub_end + 1) = '\0'; /* extend str end, then fall through */
674 cap = Curwin->captab[(int)*sub_end];
676 snprintf(tmp, sizeof(tmp), "%s%.*s%s", cap, room, sub_beg, Caps_off);
678 if(rp - row + amt + 1 > sizeof row)
679 goto overflow; // shit happens
681 room -= (sub_end - sub_beg);
684 default: /* nothin' special, just text */
687 if (unlikely(0 >= room)) break; /* skip substrings that won't fit */
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' */
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);
703 /*###### Small Utility routines ########################################*/
705 // Get a string from the user
706 static char *ask4str (const char *prompt)
708 static char buf[GETBUFSIZ];
711 memset(buf, '\0', sizeof(buf));
712 chin(1, buf, sizeof(buf) - 1);
718 // Get a float from the user
719 static float get_float (const char *prompt)
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");
730 sscanf(line, "%f", &f);
735 // Get an integer from the user
736 static int get_int (const char *prompt)
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");
747 sscanf(line, "%d", &n);
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)
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 */
767 static char nextup[] = { 'K', 'M', 'G', 'T', 0 };
769 static char nextup[] = { 'k', 'm', 'g', 't', 0 };
771 static char buf[TNYBUFSIZ];
775 /* try an unscaled version first... */
776 if (width >= snprintf(buf, sizeof(buf), "%lu", num)) return buf;
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))
783 /* the integer version */
784 if (width >= snprintf(buf, sizeof(buf), "%ld%c", (unsigned long)(num / *dp), *up))
787 /* well shoot, this outta' fit... */
793 * Do some scaling stuff.
794 * format 'tics' to fit 'width'. */
795 static const char *scale_tics (TIC_t tics, const int width)
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
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))
818 if (width >= snprintf(buf, sizeof buf, "%lu:%02u", nt, nn))
820 nn = nt % 60; // minutes past the hour
821 nt /= 60; // total hours
822 if (width >= snprintf(buf, sizeof buf, "%lu,%02u", nt, nn))
824 nn = nt; // now also hours
825 if (width >= snprintf(buf, sizeof buf, HH, nn))
827 nn /= 24; // now days
828 if (width >= snprintf(buf, sizeof buf, DD, nn))
830 nn /= 7; // now weeks
831 if (width >= snprintf(buf, sizeof buf, WW, nn))
833 // well shoot, this outta' fit...
843 static int selection_type;
844 static uid_t selection_uid;
846 // FIXME: this is "temporary" code we hope
847 static int good_uid(const proc_t *restrict const pp){
848 switch(selection_type){
854 if (pp->ruid == selection_uid) return 1;
855 if (pp->suid == selection_uid) return 1;
856 if (pp->fuid == selection_uid) return 1;
859 if (pp->euid == selection_uid) return 1;
862 ; // don't know what it is; find bugs fast
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;
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;
880 if(num > 0xfffffffeUL) return uidrange;
886 /*###### Library Alternatives ##########################################*/
889 * Handle our own memory stuff without the risk of leaving the
890 * user's terminal in an ugly state should things go sour. */
892 static void *alloc_c (unsigned numb) MALLOC;
893 static void *alloc_c (unsigned numb)
898 if (!(p = calloc(1, numb)))
899 std_err("failed memory allocate");
903 static void *alloc_r (void *q, unsigned numb) MALLOC;
904 static void *alloc_r (void *q, unsigned numb)
909 if (!(p = realloc(q, numb)))
910 std_err("failed memory allocate");
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
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)
923 static FILE *fp = NULL;
926 // enough for a /proc/stat CPU line (not the intr line)
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) */
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));
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",
958 std_err("failed /proc/stat read");
960 // and just in case we're 2.2.xx compiled without SMP support...
963 memcpy(cpus, &cpus[1], sizeof(CPU_t));
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",
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
977 std_err("failed /proc/stat read");
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)
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
998 if (unlikely(!this)) {
999 static struct timeval oldtimev;
1000 struct timeval timev;
1001 struct timezone timez;
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;
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;
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);
1025 switch (this->state) {
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);
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);
1055 int hi = maxt_sav - 1;
1057 // find matching entry from previous frame and make ticks elapsed
1060 if (this->tid < hist_sav[i].pid)
1062 else if (likely(this->tid > hist_sav[i].pid))
1065 tics -= hist_sav[i].tics;
1074 tmp.pid = this->tid;
1075 ptr = bsearch(&tmp, hist_sav, maxt_sav, sizeof tmp, sort_HST_t);
1076 if(ptr) tics -= ptr->tics;
1080 // we're just saving elapsed tics, to be converted into %cpu if
1081 // this task wins it's displayable screen row lottery... */
1083 // if (Frames_maxcmdln) { }
1084 // shout this to the world with the final call (or us the next time in)
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)
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)
1101 static int show_threads_was_enabled = 0; // optimization
1103 prochlp(NULL); // prep for a new frame
1105 PT = openproc(flags, Monpids);
1107 PT = openproc(flags);
1109 // i) Allocated Chunks: *Existing* table; refresh + reuse
1110 if (!(CHKw(Curwin, Show_THREADS))) {
1111 while (curmax < savmax) {
1112 if (table[curmax]->cmdline) {
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;
1121 free(*table[curmax]->cmdline);
1122 table[curmax]->cmdline = NULL;
1124 if (unlikely(!(ptsk = readproc(PT, table[curmax])))) break;
1125 prochlp(ptsk); // tally & complete this proc_t
1129 else { // show each thread in a process separately
1130 while (curmax < savmax) {
1132 if (unlikely(!(ptsk = readproc(PT, NULL)))) break;
1133 show_threads_was_enabled = 1;
1134 while (curmax < savmax) {
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;
1143 free(*table[curmax]->cmdline); // only free once
1144 table[curmax]->cmdline = NULL;
1146 if (!(ttsk = readtask(PT, ptsk, table[curmax]))) break;
1150 free(ptsk); // readproc() proc_t not used
1154 // ii) Unallocated Chunks: *New* or *Existing* table; extend + fill
1155 if (!(CHKw(Curwin, Show_THREADS))) {
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;
1166 else { // show each thread in a process separately
1169 if (likely(ptsk = readproc(PT, NULL))) {
1170 show_threads_was_enabled = 1;
1172 table = alloc_r(table, (curmax + 1) * PTRsz);
1173 if (!(ttsk = readtask(PT, ptsk, NULL))) break;
1175 table[curmax++] = ttsk;
1177 free(ptsk); // readproc() proc_t not used
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;
1190 // this frame's end, but not necessarily end of allocated space
1191 table[curmax]->tid = -1;
1198 /*###### Field Table/RCfile compatability support ######################*/
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
1212 // for reframewins and summary_show 1st pass
1213 #define L_DEFAULT PROC_FILLSTAT
1215 // a temporary macro, soon to be undef'd...
1216 #define SF(f) (QFP_t)sort_P_ ## f
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[] = {
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 },
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 },
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 !
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
1287 // convert, or 0 for failure
1288 static int ft_cvt_char (const int fr, const int to, int c) {
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];
1301 static inline int ft_get_char (const int fr, int i) {
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
1312 // convert, or -1 for failure
1313 static int ft_get_idx (const int fr, int c) {
1316 while (++j < MAXTBL(Fieldstab)) {
1317 if (c == Fieldstab[j].keys[fr]) return j;
1318 if (c == Fieldstab[j].keys[fr+1]) return j;
1325 // convert, or NULL for failure
1326 static const FLD_t *ft_get_ptr (const int fr, int c) {
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;
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;
1346 // convert, or -1 for failure
1347 static int ft_ptr_to_idx (const FLD_t *p) {
1349 if (p < Fieldstab) return -1;
1351 if (i >= MAXTBL(Fieldstab)) return -1;
1358 static void rc_bugless (const RCF_t *const rc) {
1362 fprintf(stderr,"\n%d %d %f %d\n",
1363 rc->mode_altscr, rc->mode_irixps, rc->delay_time, rc->win_index
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
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) {
1389 cp = strstr(buf, "\n\n" RCF_EYECATCHER);
1391 cp = strchr(cp + 2, '\n');
1392 if (!cp++) return -2;
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
1397 if (cnt != 4) return -3;
1398 cp = strchr(cp, '\n');
1399 if (!cp++) return -4;
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);
1410 cnt = sscanf(cp, "\twinflags=%d, sortindx=%u, maxtasks=%d \n",
1411 &ptr->winflags, &ptr->sortindx, &ptr->maxtasks
1413 if (cnt != 3) return -(9+100*i);
1414 cp = strchr(cp, '\n');
1415 if (!cp++) return -(10+100*i);
1417 cnt = sscanf(cp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d \n",
1418 &ptr->summclr, &ptr->msgsclr, &ptr->headclr, &ptr->taskclr
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);
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";
1438 unsigned c_show = 0;
1439 int badchar = 0; // allow a limited number of duplicates and junk
1441 char scoreboard[256];
1442 memset(scoreboard, '\0', sizeof scoreboard);
1444 cp = buf+2; // skip the "\n\n" we stuck at the beginning
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);
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;
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
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';
1476 memset(scoreboard, '\0', sizeof(scoreboard));
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;
1485 return -15; // not supposed to have digits here
1487 // case 's': // mostly for global rcfile
1488 // rc->mode_secure = 1;
1491 rc->win[0].winflags |= Show_CTIMES;
1494 rc->win[0].winflags |= Show_CMDLIN;
1497 rc->win[0].winflags &= ~Show_IDLEPS;
1500 rc->win[0].winflags |= Show_THREADS;
1503 rc->win[0].winflags &= ~View_MEMORY;
1506 rc->win[0].winflags &= ~View_LOADAV;
1509 rc->win[0].winflags &= ~View_STATES;
1512 rc->mode_irixps = 0;
1516 c = 0; // for scoreboard
1517 rc->win[0].sortindx = P_MEM;
1520 c = 0; // for scoreboard
1521 rc->win[0].sortindx = P_CPU;
1523 case 'A': // supposed to be start_time
1524 c = 0; // for scoreboard
1525 rc->win[0].sortindx = P_PID;
1528 c = 0; // for scoreboard
1529 rc->win[0].sortindx = P_TM2;
1532 c = 0; // for scoreboard
1533 rc->win[0].sortindx = P_PID;
1537 // just ignore it, except for the scoreboard of course
1540 if (scoreboard[c]) return -16; // duplicates not allowed
1547 static void rc_write_new (FILE *fp) {
1550 fprintf(fp, RCF_EYECATCHER "\"%s with windows\"\t\t# shameless braggin'\n",
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)
1557 for (i = 0; i < GROUPSMAX; i++) {
1559 char *cp = Winstk[i].rc.fieldscur;
1563 int c = *cp++ & 0xff;
1568 continue; // throw away junk (some of it)
1570 buf[j++] = c; // gets the '\0' too
1574 fprintf(fp, "%s\tfieldscur=%s\n",
1575 Winstk[i].rc.winname, buf
1577 fprintf(fp, "\twinflags=%d, sortindx=%d, maxtasks=%d\n",
1578 Winstk[i].rc.winflags, Winstk[i].rc.sortindx, Winstk[i].rc.maxtasks
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
1588 static const char *rc_write_whatever (void) {
1589 FILE *fp = fopen(Rc_name, "w");
1591 if (!fp) return strerror(errno);
1598 /*###### Startup routines ##############################################*/
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)
1606 /* setup our program name -- big! */
1607 Myname = strrchr(me, '/');
1608 if (Myname) ++Myname; else Myname = me;
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;
1619 /* get virtual page size -- nearing huge! */
1620 Page_size = getpagesize();
1627 pcpu_max_value = 99.9;
1629 Fieldstab[P_CPN].head = " P";
1630 Fieldstab[P_CPN].fmts = " %1u";
1632 Fieldstab[P_CPN].head = " P";
1633 Fieldstab[P_CPN].fmts = " %2u";
1635 if(smp_num_cpus>99){
1636 Fieldstab[P_CPN].head = " P";
1637 Fieldstab[P_CPN].fmts = " %3u";
1639 if(smp_num_cpus>999){
1640 Fieldstab[P_CPN].head = " P";
1641 Fieldstab[P_CPN].fmts = " %4u";
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;
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.
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];
1669 memset(upper, '\0', sizeof upper);
1670 memset(lower, '\0', sizeof lower);
1676 if(isupper(c)) upper[c&0x1f]++;
1677 else lower[c&0x1f]++;
1682 if (upper[c&0x1f] && lower[c&0x1f]) {
1683 lower[c&0x1f] = 0; // got both, so wipe out unseen column
1685 cp = strchr(fields, c);
1686 if (cp) memmove(cp, cp+1, strlen(cp));
1690 while (lower[c&0x1f] > 1) { // got too many a..z
1692 cp = strchr(fields, c);
1693 memmove(cp, cp+1, strlen(cp));
1695 while (upper[c&0x1f] > 1) { // got too many A..Z
1697 cp = strchr(fields, toupper(c));
1698 memmove(cp, cp+1, strlen(cp));
1700 if (!upper[c&0x1f] && !lower[c&0x1f]) { // both missing
1702 memmove(fields+1, fields, strlen(fields)+1);
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)
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)
1723 const RCF_t def_rcf = DEF_RCFILE;
1724 char fbuf[MEDBUFSIZ];
1727 float delay = Rc.delay_time;
1729 // read part of an old-style config in /etc/toprc
1730 fd = open(SYS_RCFILESPEC, O_RDONLY);
1733 num = read(fd, fbuf, sizeof(fbuf) - 1);
1735 const char *sec = strchr(fbuf, 's');
1736 const char *eol = strchr(fbuf, '\n');
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);
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"));
1751 snprintf(Rc_name, sizeof(Rc_name), ".%src", Myname); // eeew...
1753 snprintf(Rc_name, sizeof(Rc_name), "%s/.%src", getenv("HOME"), Myname);
1757 fd = open(Rc_name, O_RDONLY);
1760 num = read(fd, fbuf+2, sizeof(fbuf) -3);
1765 //fprintf(stderr,"rc_read_old returns %d\n",rc_read_old(fbuf, &rcf));
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;
1771 delay = rcf.delay_time;
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);
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";
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;
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)
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;
1820 const char *cp = *(args++);
1831 TOGw(Curwin, Show_CMDLIN);
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));
1842 TOGw(Curwin, Show_THREADS);
1846 std_out(fmtmk("%s\nusage:\t%s%s", procps_version, Myname, usage));
1848 TOGw(Curwin, Show_IDLEPS);
1849 Curwin->rc.maxtasks = 0;
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));
1860 if (selection_type && selection_type != 'p') std_err("conflicting process selection");
1861 selection_type = 'p';
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();
1872 if (!(p = strchr(cp, ',')))
1881 TOGw(Curwin, Show_CTIMES);
1886 if (selection_type /* && selection_type != 'u' */) std_err("conflicting process selection");
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
1899 if (selection_type /* && selection_type != 'U' */) std_err("conflicting process selection");
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
1910 std_err(fmtmk("unknown argument '%c'\nusage:\t%s%s"
1911 , *cp, Myname, usage));
1913 } /* end: switch (*cp) */
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) */
1920 /* fixup delay time, maybe... */
1921 if (MAXFLOAT != tmp_delay) {
1922 if (Secure_mode || tmp_delay < 0)
1923 msg_save("Delay time Not changed");
1925 Rc.delay_time = tmp_delay;
1931 * Set up the terminal attributes */
1932 static void whack_terminal (void)
1934 struct termios newtty;
1937 setupterm("dumb", STDOUT_FILENO, NULL);
1940 setupterm(NULL, STDOUT_FILENO, NULL);
1941 if (tcgetattr(STDIN_FILENO, &Savedtty) == -1)
1942 std_err("failed tty get");
1944 newtty.c_lflag &= ~(ICANON | ECHO);
1945 newtty.c_oflag &= ~(TAB3);
1946 newtty.c_cc[VMIN] = 1;
1947 newtty.c_cc[VTIME] = 0;
1950 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtty) == -1) {
1952 std_err(fmtmk("failed tty set: %s", strerror(errno)));
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));
1964 /*###### Field Selection/Ordering routines #############################*/
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'.
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)
1977 int i, cmax = Screen_cols / 2, rmax = Screen_rows - yRSVD;
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]);
1986 if (!f) continue; // hey, should be std_err!
1987 for (p = f->head; ' ' == *p; ++p) // advance past any leading spaces
1989 PUTT("%s%s%c %c: %-10s = %s",
1990 tg2((i / rmax) * cmax, (i % rmax) + yRSVD),
1991 b ? Curwin->cap_bold : Cap_norm,
1999 putp(Curwin->capclr_rownorm);
2000 while ((p = strchr(xtra, '\n'))) {
2003 tg2((i / rmax) * cmax, (i % rmax) + yRSVD),
2016 // Change order of displayed fields.
2017 static void fields_reorder (void)
2019 static const char prompt[] =
2020 "Upper case letter moves field left, lower case right";
2025 putp(Cap_curs_huge);
2027 display_fields(Curwin->rc.fieldscur, FIELDS_xtra);
2028 show_special(1, fmtmk(FIELDS_current
2029 , Cap_home, Curwin->rc.fieldscur, Curwin->grpname, prompt));
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)) {
2043 putp(Cap_curs_norm);
2046 // Select sort field.
2047 static void fields_sort (void)
2049 static const char prompt[] =
2050 "Select sort field via field letter, type any other key to return";
2051 char phoney[PFLAGSSIZ];
2055 strcpy(phoney, NUL_FIELDS);
2056 x = i = Curwin->rc.sortindx;
2058 putp(Cap_curs_huge);
2062 display_fields(phoney, SORT_xtra);
2063 show_special(1, fmtmk(SORT_fields, Cap_home, *p, Curwin->grpname, prompt));
2065 if (!ft_get_ptr(FT_NEW_fmt, c)) break;
2066 i = toupper(c) - 'A';
2070 if ((p = strchr(Curwin->rc.fieldscur, x + 'a')))
2072 Curwin->rc.sortindx = x;
2073 putp(Cap_curs_norm);
2077 // Toggle displayed fields.
2078 static void fields_toggle (void)
2080 static const char prompt[] =
2081 "Toggle fields via field letter, type any other key to return";
2086 putp(Cap_curs_huge);
2088 display_fields(Curwin->rc.fieldscur, FIELDS_xtra);
2089 show_special(1, fmtmk(FIELDS_current, Cap_home, Curwin->rc.fieldscur, Curwin->grpname, prompt));
2091 if (!ft_get_ptr(FT_NEW_fmt, c)) break;
2092 i = toupper(c) - 'A';
2093 if ((p = strchr(Curwin->rc.fieldscur, i + 'A')))
2095 else if ((p = strchr(Curwin->rc.fieldscur, i + 'a')))
2098 putp(Cap_curs_norm);
2101 /*###### Windows/Field Groups support #################################*/
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)
2113 int i, needpsdb = 0;
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
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';
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;
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
2141 w->maxcmdln = Screen_cols - (strlen(w->columnhdr) - strlen(Fieldstab[P_CMD].head));
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;
2160 s = scat(s, h+advance);
2161 Frames_libflags |= Fieldstab[w->procflags[i]].lflg;
2163 if (Rc.mode_altscr) w->columnhdr[0] = w->winnum + '0';
2165 if (Rc.mode_altscr) w = w->next;
2166 } while (w != Curwin);
2168 // do we need the kernel symbol table (and is it already open?)
2170 if (No_ksyms == -1) {
2172 if (open_psdb_message(NULL, msg_save))
2179 if (selection_type=='U') Frames_libflags |= L_status;
2181 if (Frames_libflags & L_EITHER) {
2182 Frames_libflags &= ~L_EITHER;
2183 if (!(Frames_libflags & L_stat)) Frames_libflags |= L_status;
2185 if (!Frames_libflags) Frames_libflags = L_DEFAULT;
2186 if (selection_type=='p') Frames_libflags |= PROC_PID;
2190 // Value a window's name and make the associated group name.
2191 static void win_names (WIN_t *q, const char *name)
2193 sprintf(q->rc.winname, "%.*s", WINNAMSIZ -1, name);
2194 sprintf(q->grpname, "%d:%.*s", q->winnum, WINNAMSIZ -1, name);
2198 // Display a window/field group (ie. make it "current").
2199 static void win_select (char ch)
2201 static const char prompt[] = "Choose field group (1 - 4)";
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... */
2207 chin(0, (char *)&ch, 1);
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) */
2218 Curwin = &Winstk[ch - '1'];
2224 // Just warn the user when a command can't be honored.
2225 static int win_warn (void)
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...
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)
2238 static int flgssav, summsav, msgssav, headsav, tasksav;
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);
2245 q->rc.winflags = flgssav; q->rc.summclr = summsav;
2246 q->rc.msgsclr = msgssav; q->rc.headclr = headsav; q->rc.taskclr = tasksav;
2252 // Change colors used in display
2253 static void wins_colors (void)
2255 #define kbdABORT 'q'
2256 #define kbdAPPLY '\n'
2257 int clr = Curwin->rc.taskclr, *pclr = &Curwin->rc.taskclr;
2260 if (0 >= max_colors) {
2261 show_msg("\aNo colors to map!");
2264 winsclrhlp(Curwin, 1);
2266 putp(Cap_curs_huge);
2270 /* this string is well above ISO C89's minimum requirements! */
2277 CHKw(Curwin, View_NOBOLD) ? "On" : "Off",
2278 CHKw(Curwin, Show_COLORS) ? "On" : "Off",
2279 CHKw(Curwin, Show_HIBOLD) ? "On" : "Off",
2288 pclr = &Curwin->rc.summclr;
2293 pclr = &Curwin->rc.msgsclr;
2298 pclr = &Curwin->rc.headclr;
2303 pclr = &Curwin->rc.taskclr;
2312 TOGw(Curwin, View_NOBOLD);
2315 TOGw(Curwin, Show_HIBOLD);
2318 TOGw(Curwin, Show_COLORS);
2323 winsclrhlp(Curwin, 1);
2324 clr = Curwin->rc.taskclr, pclr = &Curwin->rc.taskclr;
2329 } while (kbdAPPLY != ch && kbdABORT != ch);
2332 winsclrhlp(Curwin, 0);
2333 putp(Cap_curs_norm);
2340 // Manipulate flag(s) for all our windows.
2341 static void wins_reflag (int what, int flg)
2351 case Flags_SET: /* Ummmm, i can't find anybody */
2352 SETw(w, flg); /* who uses Flags_set ... */
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) {
2362 SETw(w, Show_IDLEPS | VISIBLE_tsk);
2365 } while (w != Curwin);
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)
2373 (void)dont_care_sig;
2379 // Set the screen dimensions and arrange for the real workhorse.
2381 // SIGWINCH and SIGCONT
2382 static void wins_resize (void)
2385 char *env_columns; // Unix98 environment variable COLUMNS
2386 char *env_lines; // Unix98 environment variable LINES
2388 Screen_cols = columns; // <term.h>
2389 Screen_rows = lines; // <term.h>
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;
2396 if (Batch) Screen_rows = MAXINT;
2398 env_columns = getenv("COLUMNS");
2399 if(env_columns && *env_columns){
2402 t = strtol(env_columns, &endptr, 0);
2403 if(!*endptr && (t>0) && (t<=0x7fffffffL)) Screen_cols = (int)t;
2405 env_lines = getenv("LINES");
2406 if(env_lines && *env_lines){
2409 t = strtol(env_lines, &endptr, 0);
2410 if(!*endptr && (t>0) && (t<=0x7fffffffL)) Screen_rows = (int)t;
2413 // be crudely tolerant of crude tty emulators
2414 if (avoid_last_column) Screen_cols--;
2416 // we might disappoint some folks (but they'll deserve it)
2417 if (SCREENMAX < Screen_cols) Screen_cols = SCREENMAX;
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);
2429 // force rebuild of column headers AND libproc/readproc requirements
2430 Frames_libflags = 0;
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)
2443 for (i = 0; i < GROUPSMAX; 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;
2460 /* fixup the circular chains... */
2461 Winstk[3].next = &Winstk[0];
2462 Winstk[0].prev = &Winstk[3];
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)
2473 for (i = 0; i < GROUPSMAX; i++) {
2474 win_names(&Winstk[i], Winstk[i].rc.winname);
2477 // rely on this next guy to force a call (eventually) to reframewins
2482 /*###### Main Screen routines ##########################################*/
2484 // Process keyboard input during the main loop
2485 static void do_key (unsigned c)
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";
2491 // standardized 'smp' errors
2492 static const char err_smp[] = "\aSorry, only 1 cpu detected";
2497 if (Cpu_tot+7 > Screen_rows && CHKw(Curwin, View_CPUSUM)) {
2498 show_msg(err_num_cpus);
2502 if (Cpu_tot > 1) TOGw(Curwin, View_CPUSUM);
2503 else show_msg(err_smp);
2505 TOGw(Curwin, View_CPUSUM);
2510 if (Rc.mode_altscr) Curwin = Curwin->next;
2514 Rc.mode_altscr = !Rc.mode_altscr;
2520 if (!CHKw(Curwin, Show_HICOLS | Show_HIROWS))
2521 show_msg("\aNothing to highlight!");
2523 TOGw(Curwin, Show_HIBOLD);
2530 TOGw(Curwin, View_NOBOLD);
2535 VIZTOGc(Show_CMDLIN);
2541 show_msg(err_secure);
2544 get_float(fmtmk("Change delay from %.1f to", Rc.delay_time));
2545 if (tmp > -1) Rc.delay_time = tmp;
2550 if (VIZCHKc) fields_toggle();
2555 if (VIZCHKc) fields_sort();
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);
2572 TOGw(Curwin, Show_THREADS);
2573 show_msg(fmtmk("Show threads %s"
2574 , CHKw(Curwin, Show_THREADS) ? "On" : "Off"));
2582 putp(Cap_curs_huge);
2583 /* this string is well above ISO C89's minimum requirements! */
2590 CHKw(Curwin, Show_CTIMES) ? "On" : "Off",
2592 Secure_mode ? "On" : "Off",
2593 Secure_mode ? "" : KEYS_help_unsecured
2597 if ('?' == ch || 'h' == ch) {
2600 show_special(1, fmtmk(WINDOWS_help
2602 , Winstk[0].rc.winname
2603 , Winstk[1].rc.winname
2604 , Winstk[2].rc.winname
2605 , Winstk[3].rc.winname));
2608 } while ('\n' != ch);
2610 putp(Cap_curs_norm);
2615 VIZTOGc(Show_IDLEPS);
2621 Rc.mode_irixps = !Rc.mode_irixps;
2622 show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off"));
2626 Rc.mode_irixps = !Rc.mode_irixps;
2627 show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off"));
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";
2634 pcpu_max_value = 99.9;
2635 Fieldstab[P_CPU].fmts = " %#4.1f";
2641 show_msg(err_secure);
2643 int sig, pid = get_int("PID to kill");
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)));
2655 TOGw(Curwin, View_LOADAV);
2659 TOGw(Curwin, View_MEMORY);
2666 get_int(fmtmk("Maximum tasks = %d, change to (0 is unlimited)", Curwin->rc.maxtasks));
2667 if (num > -1) Curwin->rc.maxtasks = num;
2672 if (VIZCHKc) fields_reorder();
2680 show_msg(err_secure);
2682 int val, pid = get_int("PID to renice");
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)));
2692 VIZTOGc(Qsrt_NORMAL);
2697 TOGw(Curwin, Show_CTIMES);
2698 show_msg(fmtmk("Cumulative time %s", CHKw(Curwin, Show_CTIMES) ? "On" : "Off"));
2703 TOGw(Curwin, View_STATES);
2708 // strcpy(Curwin->colusrnam, ask4str("Which user (blank for all)"));
2712 // if (!VIZCHKc) break;
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==' ') {
2723 errmsg = parse_uid(answer, &selection_uid);
2726 // Change settings here? I guess not.
2729 selection_type = 'u';
2734 // if (!VIZCHKc) break;
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==' ') {
2745 errmsg = parse_uid(answer, &selection_uid);
2748 // Change settings here? I guess not.
2751 selection_type = 'U';
2756 if (Rc.mode_altscr) Curwin = Curwin->prev;
2760 { const char *err = rc_write_whatever();
2762 show_msg(fmtmk("\aFailed '%s' open: %s", Rc_name, err));
2764 show_msg(fmtmk("Wrote configuration to '%s'", Rc_name));
2770 TOGw(Curwin, Show_HICOLS);
2777 TOGw(Curwin, Show_HIROWS);
2784 TOGw(Curwin, Show_COLORS);
2794 if (Rc.mode_altscr) TOGw(Curwin, VISIBLE_tsk);
2798 if (Rc.mode_altscr) wins_reflag(Flags_TOG, VISIBLE_tsk);
2802 Curwin->rc.maxtasks = 0;
2803 SETw(Curwin, Show_IDLEPS | VISIBLE_tsk);
2805 selection_type = '\0';
2809 if (Rc.mode_altscr) SETw(Curwin, EQUWINS_cwo);
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;
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;
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...
2836 const unsigned xkey;
2839 { "Memory", 'M', P_MEM, }, { "Numerical", 'N', P_PID, },
2840 { "CPU", 'P', P_CPU, }, { "Time", 'T', P_TM2 }, };
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));
2851 case '\n': // just ignore these, they'll have the effect
2852 case ' ': // of refreshing display after waking us up !
2856 show_msg("\aUnknown command - try 'h' for help");
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:
2862 // 'c' - likely when !Mode_altscr, maybe when Mode_altscr
2863 // 'F' - maybe, if new field forced on
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;
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)
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;
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;
2906 // display some kinda' cpu state percentages
2907 // (who or what is explained by the passed prefix)
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
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;
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)
2947 static proc_t **p_table = NULL;
2948 static CPU_t *smpcpu = NULL;
2950 // whoa first time, gotta' prime the pump...
2952 p_table = procs_refresh(NULL, Frames_libflags);
2956 // sleep for half a second
2958 tv.tv_usec = 500000;
2959 select(0, NULL, NULL, NULL, &tv); // ought to loop until done
2962 putp(Batch ? "\n\n" : Cap_home);
2964 p_table = procs_refresh(p_table, Frames_libflags);
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()));
2974 CHKw(Curwin, VISIBLE_tsk) ? LOADAV_line_alt : LOADAV_line,
2983 // Display Task and Cpu(s) States
2984 if (CHKw(Curwin, View_STATES)) {
2989 Frame_maxtask, Frame_running, Frame_sleepin, Frame_stopped, Frame_zombied
2994 smpcpu = cpus_refresh(smpcpu);
2996 if (CHKw(Curwin, View_CPUSUM)) {
2997 // display just the 1st /proc/stat line
2998 summaryhlp(&smpcpu[Cpu_tot], "Cpu(s):");
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);
3010 // Display Memory and Swap stats
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));
3020 SETw(Curwin, NEWFRAM_cwo);
3025 #define PAGES_TO_KB(n) (unsigned long)( (n) << page_to_kb_shift )
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); \
3033 snprintf(_z, sizeof(_z), f, ## va); \
3034 snprintf(cbuf, sizeof(cbuf), "%s%s%s", \
3035 q->capclr_rowhigh, \
3037 !(CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rownorm : "" \
3039 pad += q->len_rowhigh; \
3040 if (!(CHKw(q, Show_HIROWS) && 'R' == p->state)) pad += q->len_rownorm; \
3044 // Display information for a single task row.
3045 static void task_show (const WIN_t *q, const proc_t *p)
3047 char rbuf[ROWBUFSIZ];
3053 pad = Rc.mode_altscr;
3054 // if (pad) rp = scat(rp, " ");
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;
3063 int advance = (x==0) && !Rc.mode_altscr;
3067 { char tmp[ROWBUFSIZ];
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);
3077 MKCOL(scale_num(PAGES_TO_KB(p->trs), w, s));
3080 MKCOL((unsigned)p->processor);
3083 { float u = (float)p->pcpu * Frame_tscale;
3084 if (u > pcpu_max_value) u = pcpu_max_value;
3089 MKCOL(scale_num(PAGES_TO_KB(p->drs), w, s));
3092 MKCOL(scale_num((unsigned)p->dt, w, s));
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] = '.';
3103 MKCOL(scale_num(p->maj_flt, w, s));
3109 MKCOL((float)PAGES_TO_KB(p->resident) * 100 / kb_main_total);
3112 MKCOL((int)p->nice);
3115 MKCOL((unsigned)p->XXXID);
3118 MKCOL((unsigned)p->ppid);
3121 if (unlikely(-99 > p->priority) || unlikely(999 < p->priority)) {
3125 MKCOL((int)p->priority);
3128 MKCOL(scale_num(PAGES_TO_KB(p->resident), w, s));
3131 MKCOL(scale_num(PAGES_TO_KB(p->share), w, s));
3137 MKCOL(scale_num(PAGES_TO_KB(p->size - p->resident), w, s));
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));
3148 { char tmp[TNYBUFSIZ];
3149 dev_to_tty(tmp, (int)w, p->tty, p->XXXID, ABBREV_DEV);
3154 MKCOL((unsigned)p->euid);
3163 MKCOL(scale_num(PAGES_TO_KB(p->size), w, s));
3168 MKCOL((long)p->wchan);
3170 MKCOL(lookup_wchan(p->wchan, p->XXXID));
3174 } /* end: switch 'procflag' */
3176 rp = scat(rp, cbuf+advance);
3177 } /* end: for 'maxpflgs' */
3181 (CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rowhigh : q->capclr_rownorm,
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)
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;
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);
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);
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);
3222 // account for column headings
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]);
3237 // for this frame that window's toast, cleanup for next time
3239 OFFw(Curwin, FLGSOFF_cwo);
3247 /*###### Entry point plus two ##########################################*/
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)
3253 int i, rsvd, size, wins;
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;
3260 if (max <= rsvd) break;
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;
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;
3281 // Initiate the Frame Display Update cycle at someone's whim!
3282 // This routine doesn't do much, mostly he just calls others.
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)
3297 // note: all libproc flags are managed by
3298 // reframewins(), who also builds each window's column headers
3299 if (!Frames_libflags) {
3301 memset(Pseudo_scrn, '\0', Pseudo_size);
3303 Pseudo_row = Msg_row = scrlins = 0;
3304 ppt = summary_show();
3305 Max_lines = (Screen_rows - Msg_row) - 1;
3307 if (CHKw(Curwin, EQUWINS_cwo))
3308 wins_reflag(Flags_OFF, EQUWINS_cwo);
3310 // sure hope each window's columns header begins with a newline...
3311 putp(tg2(0, Msg_row));
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);
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);
3324 if (Max_lines <= scrlins) break;
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)
3332 scrlins < Max_lines ? "\n" : "",
3333 scrlins < Max_lines ? Cap_clr_eos : "",
3341 int main (int dont_care_argc, char *argv[])
3343 (void)dont_care_argc;
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
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);
3371 if (Msg_awaiting) show_msg(Msg_delayed);
3372 if (Loops > 0) --Loops;
3373 if (!Loops) end_pgm(0);
3375 tv.tv_sec = Rc.delay_time;
3376 tv.tv_usec = (Rc.delay_time - (int)Rc.delay_time) * 1000000;
3379 select(0, NULL, NULL, NULL, &tv); // ought to loop until done
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);
3391 // check 1st, in case tv zeroed (by sig handler) before it got set
3392 rc = chin(0, &c, 1);
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);
3401 if (chin(0, &c, 1) > 0) {
3402 fcntl(STDIN_FILENO, F_SETFL, file_flags);
3403 do_key((unsigned)c);
3405 fcntl(STDIN_FILENO, F_SETFL, file_flags);