~ubuntu-branches/ubuntu/saucy/procps/saucy

« back to all changes in this revision

Viewing changes to .pc/top_c_resize.patch/top.c

  • Committer: Package Import Robot
  • Author(s): Oliver Grawert
  • Date: 2012-06-20 13:12:40 UTC
  • mfrom: (2.1.21 sid)
  • Revision ID: package-import@ubuntu.com-20120620131240-923p0d8q88bmk3ac
Tags: 1:3.3.3-2ubuntu1
* Merge from Debian unstable.  Remaining changes:
  - debian/sysctl.d (Ubuntu-specific):
    + 10-console-messages.conf: stop low-level kernel messages on console.
    + 10-kernel-hardening.conf: add the kptr_restrict setting
    + 10-keyboard.conf.powerpc: mouse button emulation on PowerPC.
    + 10-network-security.conf: enable rp_filter and SYN-flood protection.
    + 10-ptrace.conf: describe new PTRACE setting.
    + 10-zeropage.conf: safe mmap_min_addr value for graceful fall-back.
    + README: describe how this directory is supposed to work.
  - debian/upstart (Ubuntu-specific): upstart configuration to replace old
    style sysv init script

Show diffs side-by-side

added added

removed removed

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