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