~ubuntu-branches/ubuntu/trusty/procps/trusty-proposed

« back to all changes in this revision

Viewing changes to .pc/p_pid-enum.diff/top/top.c

  • Committer: Package Import Robot
  • Author(s): Matthias Klose
  • Date: 2013-12-05 13:27:37 UTC
  • Revision ID: package-import@ubuntu.com-20131205132737-0ftdo3qzvtwxrqa5
Tags: 1:3.3.8-2ubuntu2
* Build using dh-autoreconf, update libtool.m4.
* Fix build with glibc-2.18/linux-3.13.

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-2013, by: James C. Warner
 
4
 *    All rights reserved.      8921 Hilloway Road
 
5
 *                              Eden Prairie, Minnesota 55347 USA
 
6
 *
 
7
 * This file may be used subject to the terms and conditions of the
 
8
 * GNU Library General Public License Version 2, or any later version
 
9
 * at your option, as published by the Free Software Foundation.
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU Library General Public License for more details.
 
14
 */
 
15
/* For contributions to this program, the author wishes to thank:
 
16
 *    Craig Small, <csmall@small.dropbear.id.au>
 
17
 *    Albert D. Cahalan, <albert@users.sf.net>
 
18
 *    Sami Kerola, <kerolasa@iki.fi>
 
19
 */
 
20
 
 
21
#include <sys/ioctl.h>
 
22
#include <sys/resource.h>
 
23
#include <sys/select.h>
 
24
#include <sys/time.h>
 
25
#include <sys/types.h>
 
26
 
 
27
#include <ctype.h>
 
28
#include <curses.h>
 
29
#ifndef NUMA_DISABLE
 
30
#include <dlfcn.h>
 
31
#endif
 
32
#include <errno.h>
 
33
#include <fcntl.h>
 
34
#include <pwd.h>
 
35
#include <signal.h>
 
36
#include <stdarg.h>
 
37
#include <stdio.h>
 
38
#include <stdlib.h>
 
39
#include <string.h>
 
40
#include <term.h>       // foul sob, defines all sorts of stuff...
 
41
#undef    tab
 
42
#undef    TTY
 
43
#include <termios.h>
 
44
#include <time.h>
 
45
#include <unistd.h>
 
46
#include <values.h>
 
47
 
 
48
#include "../include/fileutils.h"
 
49
#include "../include/nls.h"
 
50
 
 
51
#include "../proc/devname.h"
 
52
#include "../proc/procps.h"
 
53
#include "../proc/readproc.h"
 
54
#include "../proc/sig.h"
 
55
#include "../proc/sysinfo.h"
 
56
#include "../proc/version.h"
 
57
#include "../proc/wchan.h"
 
58
#include "../proc/whattime.h"
 
59
 
 
60
#include "top.h"
 
61
#include "top_nls.h"
 
62
 
 
63
 
 
64
/*######  Miscellaneous global stuff  ####################################*/
 
65
 
 
66
        /* The original and new terminal definitions
 
67
           (only set when not in 'Batch' mode) */
 
68
static struct termios Tty_original,    // our inherited terminal definition
 
69
#ifdef TERMIOS_ONLY
 
70
                      Tty_tweaked,     // for interactive 'line' input
 
71
#endif
 
72
                      Tty_raw;         // for unsolicited input
 
73
static int Ttychanged = 0;
 
74
 
 
75
        /* Last established cursor state/shape */
 
76
static const char *Cursor_state = "";
 
77
 
 
78
        /* Program name used in error messages and local 'rc' file name */
 
79
static char *Myname;
 
80
 
 
81
        /* Our constant sigset, so we need initialize it but once */
 
82
static sigset_t Sigwinch_set;
 
83
 
 
84
        /* The 'local' config file support */
 
85
static char  Rc_name [OURPATHSZ];
 
86
static RCF_t Rc = DEF_RCFILE;
 
87
static int   Rc_questions;
 
88
 
 
89
        /* The run-time acquired page stuff */
 
90
static unsigned Page_size;
 
91
static unsigned Pg2K_shft = 0;
 
92
 
 
93
        /* SMP, Irix/Solaris mode, Linux 2.5.xx support */
 
94
static int         Cpu_faux_tot;
 
95
static float       Cpu_pmax;
 
96
static const char *Cpu_States_fmts;
 
97
 
 
98
        /* Specific process id monitoring support */
 
99
static pid_t Monpids [MONPIDMAX] = { 0 };
 
100
static int   Monpidsidx = 0;
 
101
 
 
102
        /* Current screen dimensions.
 
103
           note: the number of processes displayed is tracked on a per window
 
104
                 basis (see the WIN_t).  Max_lines is the total number of
 
105
                 screen rows after deducting summary information overhead. */
 
106
        /* Current terminal screen size. */
 
107
static int Screen_cols, Screen_rows, Max_lines;
 
108
 
 
109
        /* This is really the number of lines needed to display the summary
 
110
           information (0 - nn), but is used as the relative row where we
 
111
           stick the cursor between frames. */
 
112
static int Msg_row;
 
113
 
 
114
        /* The nearly complete scroll coordinates message for the current
 
115
           window, built at the time column headers are constructed */
 
116
static char Scroll_fmts [SMLBUFSIZ];
 
117
 
 
118
        /* Global/Non-windows mode stuff that is NOT persistent */
 
119
static int No_ksyms = -1,       // set to '0' if ksym avail, '1' otherwise
 
120
           PSDBopen = 0,        // set to '1' if psdb opened (now postponed)
 
121
           Batch = 0,           // batch mode, collect no input, dumb output
 
122
           Loops = -1,          // number of iterations, -1 loops forever
 
123
           Secure_mode = 0,     // set if some functionality restricted
 
124
           Thread_mode = 0,     // set w/ 'H' - show threads via readeither()
 
125
           Width_mode = 0;      // set w/ 'w' - potential output override
 
126
 
 
127
        /* Unchangeable cap's stuff built just once (if at all) and
 
128
           thus NOT saved in a WIN_t's RCW_t.  To accommodate 'Batch'
 
129
           mode, they begin life as empty strings so the overlying
 
130
           logic need not change ! */
 
131
static char  Cap_clr_eol    [CAPBUFSIZ] = "",    // global and/or static vars
 
132
             Cap_nl_clreos  [CAPBUFSIZ] = "",    // are initialized to zeros!
 
133
             Cap_clr_scr    [CAPBUFSIZ] = "",    // the assignments used here
 
134
             Cap_curs_norm  [CAPBUFSIZ] = "",    // cost nothing but DO serve
 
135
             Cap_curs_huge  [CAPBUFSIZ] = "",    // to remind people of those
 
136
             Cap_curs_hide  [CAPBUFSIZ] = "",    // batch requirements!
 
137
             Cap_clr_eos    [CAPBUFSIZ] = "",
 
138
             Cap_home       [CAPBUFSIZ] = "",
 
139
             Cap_norm       [CAPBUFSIZ] = "",
 
140
             Cap_reverse    [CAPBUFSIZ] = "",
 
141
             Caps_off       [CAPBUFSIZ] = "",
 
142
             Caps_endline   [CAPBUFSIZ] = "";
 
143
#ifndef RMAN_IGNORED
 
144
static char  Cap_rmam       [CAPBUFSIZ] = "",
 
145
             Cap_smam       [CAPBUFSIZ] = "";
 
146
        /* set to 1 if writing to the last column would be troublesome
 
147
           (we don't distinguish the lowermost row from the other rows) */
 
148
static int   Cap_avoid_eol = 0;
 
149
#endif
 
150
static int   Cap_can_goto = 0;
 
151
 
 
152
        /* Some optimization stuff, to reduce output demands...
 
153
           The Pseudo_ guys are managed by adj_geometry and frame_make.  They
 
154
           are exploited in a macro and represent 90% of our optimization.
 
155
           The Stdout_buf is transparent to our code and regardless of whose
 
156
           buffer is used, stdout is flushed at frame end or if interactive. */
 
157
static char  *Pseudo_screen;
 
158
static int    Pseudo_row = PROC_XTRA;
 
159
static size_t Pseudo_size;
 
160
#ifndef OFF_STDIOLBF
 
161
        // less than stdout's normal buffer but with luck mostly '\n' anyway
 
162
static char  Stdout_buf[2048];
 
163
#endif
 
164
 
 
165
        /* Our four WIN_t's, and which of those is considered the 'current'
 
166
           window (ie. which window is associated with any summ info displayed
 
167
           and to which window commands are directed) */
 
168
static WIN_t  Winstk [GROUPSMAX];
 
169
static WIN_t *Curwin;
 
170
 
 
171
        /* Frame oriented stuff that can't remain local to any 1 function
 
172
           and/or that would be too cumbersome managed as parms,
 
173
           and/or that are simply more efficiently handled as globals
 
174
           [ 'Frames_...' (plural) stuff persists beyond 1 frame ]
 
175
           [ or are used in response to async signals received ! ] */
 
176
static volatile int Frames_resize;     // time to rebuild all column headers
 
177
static          int Frames_libflags;   // PROC_FILLxxx flags
 
178
static int          Frame_maxtask;     // last known number of active tasks
 
179
                                       // ie. current 'size' of proc table
 
180
static float        Frame_etscale;     // so we can '*' vs. '/' WHEN 'pcpu'
 
181
static unsigned     Frame_running,     // state categories for this frame
 
182
                    Frame_sleepin,
 
183
                    Frame_stopped,
 
184
                    Frame_zombied;
 
185
static int          Frame_srtflg,      // the subject window's sort direction
 
186
                    Frame_ctimes,      // the subject window's ctimes flag
 
187
                    Frame_cmdlin;      // the subject window's cmdlin flag
 
188
 
 
189
        /* Support for 'history' processing so we can calculate %cpu */
 
190
static int    HHist_siz;               // max number of HST_t structs
 
191
static HST_t *PHist_sav,               // alternating 'old/new' HST_t anchors
 
192
             *PHist_new;
 
193
#ifndef OFF_HST_HASH
 
194
#define       HHASH_SIZ  1024
 
195
static int    HHash_one [HHASH_SIZ],   // actual hash tables ( hereafter known
 
196
              HHash_two [HHASH_SIZ],   // as PHash_sav/PHash_new )
 
197
              HHash_nul [HHASH_SIZ];   // 'empty' hash table image
 
198
static int   *PHash_sav = HHash_one,   // alternating 'old/new' hash tables
 
199
             *PHash_new = HHash_two;
 
200
#endif
 
201
 
 
202
        /* Support for automatically sized fixed-width column expansions.
 
203
         * (hopefully, the macros help clarify/document our new 'feature') */
 
204
static int Autox_array [P_MAXPFLGS],
 
205
           Autox_found;
 
206
#define AUTOX_NO      P_MAXPFLGS
 
207
#define AUTOX_COL(f)  if (P_MAXPFLGS > f) Autox_array[f] = Autox_found = 1
 
208
#define AUTOX_MODE   (0 > Rc.fixed_widest)
 
209
 
 
210
        /* Support for scale_mem and scale_num (to avoid duplication. */
 
211
#ifdef CASEUP_SUFIX                                                // nls_maybe
 
212
   static char Scaled_sfxtab[] =  { 'K', 'M', 'G', 'T', 'P', 'E', 0 };
 
213
#else                                                              // nls_maybe
 
214
   static char Scaled_sfxtab[] =  { 'k', 'm', 'g', 't', 'p', 'e', 0 };
 
215
#endif
 
216
 
 
217
        /* Support for NUMA Node display, node expansion/targeting and
 
218
           run-time dynamic linking with libnuma.so treated as a plugin */
 
219
static int Numa_node_tot;
 
220
static int Numa_node_sel = -1;
 
221
#ifndef NUMA_DISABLE
 
222
static void *Libnuma_handle;
 
223
#if defined(PRETEND_NUMA) || defined(PRETEND8CPUS)
 
224
static int Numa_max_node(void) { return 3; }
 
225
static int Numa_node_of_cpu(int num) { return (num % 4); }
 
226
#else
 
227
static int (*Numa_max_node)(void);
 
228
static int (*Numa_node_of_cpu)(int num);
 
229
#endif
 
230
#endif
 
231
 
 
232
/*######  Sort callbacks  ################################################*/
 
233
 
 
234
        /*
 
235
         * These happen to be coded in the enum identifier alphabetic order,
 
236
         * not the order of the enum 'pflgs' value.  Also note that a callback
 
237
         * routine may serve more than one column.
 
238
         */
 
239
 
 
240
SCB_STRS(CGR, cgroup[0])
 
241
SCB_STRV(CMD, Frame_cmdlin, cmdline, cmd)
 
242
SCB_NUM1(COD, trs)
 
243
SCB_NUMx(CPN, processor)
 
244
SCB_NUM1(CPU, pcpu)
 
245
SCB_NUM1(DAT, drs)
 
246
SCB_NUM1(DRT, dt)
 
247
SCB_STRS(ENV, environ[0])
 
248
SCB_NUM1(FLG, flags)
 
249
SCB_NUM1(FL1, maj_flt)
 
250
SCB_NUM1(FL2, min_flt)
 
251
SCB_NUM1(FV1, maj_delta)
 
252
SCB_NUM1(FV2, min_delta)
 
253
SCB_NUMx(GID, egid)
 
254
SCB_STRS(GRP, egroup)
 
255
SCB_NUMx(NCE, nice)
 
256
#ifdef OOMEM_ENABLE
 
257
SCB_NUM1(OOA, oom_adj)
 
258
SCB_NUM1(OOM, oom_score)
 
259
#endif
 
260
SCB_NUMx(PGD, pgrp)
 
261
SCB_NUMx(PID, tid)
 
262
SCB_NUMx(PPD, ppid)
 
263
SCB_NUMx(PRI, priority)
 
264
SCB_NUM1(RES, resident)                // also serves MEM !
 
265
SCB_STRX(SGD, supgid)
 
266
SCB_STRS(SGN, supgrp)
 
267
SCB_NUM1(SHR, share)
 
268
SCB_NUM1(SID, session)
 
269
SCB_NUMx(STA, state)
 
270
SCB_NUM1(SWP, vm_swap)
 
271
SCB_NUMx(TGD, tgid)
 
272
SCB_NUMx(THD, nlwp)
 
273
                                       // also serves TM2 !
 
274
static int SCB_NAME(TME) (const proc_t **P, const proc_t **Q) {
 
275
   if (Frame_ctimes) {
 
276
      if (((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime)
 
277
        < ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime))
 
278
           return SORT_lt;
 
279
      if (((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime)
 
280
        > ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime))
 
281
           return SORT_gt;
 
282
   } else {
 
283
      if (((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime))
 
284
         return SORT_lt;
 
285
      if (((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime))
 
286
         return SORT_gt;
 
287
   }
 
288
   return SORT_eq;
 
289
}
 
290
SCB_NUM1(TPG, tpgid)
 
291
SCB_NUMx(TTY, tty)
 
292
SCB_NUMx(UED, euid)
 
293
SCB_STRS(UEN, euser)
 
294
SCB_NUMx(URD, ruid)
 
295
SCB_STRS(URN, ruser)
 
296
SCB_NUMx(USD, suid)
 
297
SCB_NUM2(USE, resident, vm_swap)
 
298
SCB_STRS(USN, suser)
 
299
SCB_NUM1(VRT, size)
 
300
SCB_NUM1(WCH, wchan)
 
301
 
 
302
#ifdef OFF_HST_HASH
 
303
        /* special sort for procs_hlp() ! ------------------------ */
 
304
static int sort_HST_t (const HST_t *P, const HST_t *Q) {
 
305
   return P->pid - Q->pid;
 
306
}
 
307
#endif
 
308
 
 
309
/*######  Tiny useful routine(s)  ########################################*/
 
310
 
 
311
        /*
 
312
         * This routine simply formats whatever the caller wants and
 
313
         * returns a pointer to the resulting 'const char' string... */
 
314
static const char *fmtmk (const char *fmts, ...) __attribute__((format(printf,1,2)));
 
315
static const char *fmtmk (const char *fmts, ...) {
 
316
   static char buf[BIGBUFSIZ];          // with help stuff, our buffer
 
317
   va_list va;                          // requirements now exceed 1k
 
318
 
 
319
   va_start(va, fmts);
 
320
   vsnprintf(buf, sizeof(buf), fmts, va);
 
321
   va_end(va);
 
322
   return (const char *)buf;
 
323
} // end: fmtmk
 
324
 
 
325
 
 
326
        /*
 
327
         * This guy is just our way of avoiding the overhead of the standard
 
328
         * strcat function (should the caller choose to participate) */
 
329
static inline char *scat (char *dst, const char *src) {
 
330
   while (*dst) dst++;
 
331
   while ((*(dst++) = *(src++)));
 
332
   return --dst;
 
333
} // end: scat
 
334
 
 
335
 
 
336
        /*
 
337
         * This guy just facilitates Batch and protects against dumb ttys
 
338
         * -- we'd 'inline' him but he's only called twice per frame,
 
339
         * yet used in many other locations. */
 
340
static const char *tg2 (int x, int y) {
 
341
   // it's entirely possible we're trying for an invalid row...
 
342
   return Cap_can_goto ? tgoto(cursor_address, x, y) : "";
 
343
} // end: tg2
 
344
 
 
345
/*######  Exit/Interrput routines  #######################################*/
 
346
 
 
347
        /*
 
348
         * Reset the tty, if necessary */
 
349
static void at_eoj (void) {
 
350
   if (Ttychanged) {
 
351
      tcsetattr(STDIN_FILENO, TCSAFLUSH, &Tty_original);
 
352
      if (keypad_local) putp(keypad_local);
 
353
      putp("\n");
 
354
      if (exit_ca_mode) {
 
355
         // this next will also replace top's most recent screen with the
 
356
         // original display contents that were visible at our invocation
 
357
         putp(exit_ca_mode);
 
358
      }
 
359
      putp(Cap_curs_norm);
 
360
      putp(Cap_clr_eol);
 
361
#ifndef RMAN_IGNORED
 
362
      putp(Cap_smam);
 
363
#endif
 
364
   }
 
365
   fflush(stdout);
 
366
} // end: at_eoj
 
367
 
 
368
 
 
369
        /*
 
370
         * The real program end */
 
371
static void bye_bye (const char *str) NORETURN;
 
372
static void bye_bye (const char *str) {
 
373
   at_eoj();                 // restore tty in preparation for exit
 
374
#ifdef ATEOJ_RPTSTD
 
375
{  proc_t *p;
 
376
   if (!str && Ttychanged) { fprintf(stderr,
 
377
      "\n%s's Summary report:"
 
378
      "\n\tProgram"
 
379
      "\n\t   Linux version = %u.%u.%u, %s"
 
380
      "\n\t   Hertz = %u (%u bytes, %u-bit time)"
 
381
      "\n\t   Page_size = %d, Cpu_faux_tot = %d, smp_num_cpus = %d"
 
382
      "\n\t   sizeof(CPU_t) = %u, sizeof(HST_t) = %u (%u HST_t's/Page), HHist_siz = %u"
 
383
      "\n\t   sizeof(proc_t) = %u, sizeof(proc_t.cmd) = %u, sizeof(proc_t*) = %u"
 
384
      "\n\t   Frames_libflags = %08lX"
 
385
      "\n\t   SCREENMAX = %u, ROWMINSIZ = %u, ROWMAXSIZ = %u"
 
386
      "\n\t   PACKAGE = '%s', LOCALEDIR = '%s'"
 
387
      "\n\tTerminal: %s"
 
388
      "\n\t   device = %s, ncurses = v%s"
 
389
      "\n\t   max_colors = %d, max_pairs = %d"
 
390
      "\n\t   Cap_can_goto = %s"
 
391
      "\n\t   Screen_cols = %d, Screen_rows = %d"
 
392
      "\n\t   Max_lines = %d, most recent Pseudo_size = %u"
 
393
#ifndef OFF_STDIOLBF
 
394
      "\n\t   Stdout_buf = %u, BUFSIZ = %u"
 
395
#endif
 
396
      "\n\tWindows and Curwin->"
 
397
      "\n\t   sizeof(WIN_t) = %u, GROUPSMAX = %d"
 
398
      "\n\t   winname = %s, grpname = %s"
 
399
#ifdef CASEUP_HEXES
 
400
      "\n\t   winflags = %08X, maxpflgs = %d"
 
401
#else
 
402
      "\n\t   winflags = %08x, maxpflgs = %d"
 
403
#endif
 
404
      "\n\t   sortindx = %d, fieldscur = %s"
 
405
      "\n\t   maxtasks = %d, varcolsz = %d, winlines = %d"
 
406
      "\n\t   strlen(columnhdr) = %d"
 
407
      "\n"
 
408
      , __func__
 
409
      , LINUX_VERSION_MAJOR(linux_version_code)
 
410
      , LINUX_VERSION_MINOR(linux_version_code)
 
411
      , LINUX_VERSION_PATCH(linux_version_code)
 
412
      , procps_version
 
413
      , (unsigned)Hertz, (unsigned)sizeof(Hertz), (unsigned)sizeof(Hertz) * 8
 
414
      , Page_size, Cpu_faux_tot, (int)smp_num_cpus, (unsigned)sizeof(CPU_t)
 
415
      , (unsigned)sizeof(HST_t), Page_size / (unsigned)sizeof(HST_t), HHist_siz
 
416
      , (unsigned)sizeof(proc_t), (unsigned)sizeof(p->cmd), (unsigned)sizeof(proc_t*)
 
417
      , (long)Frames_libflags
 
418
      , (unsigned)SCREENMAX, (unsigned)ROWMINSIZ, (unsigned)ROWMAXSIZ
 
419
      , PACKAGE, LOCALEDIR
 
420
#ifdef PRETENDNOCAP
 
421
      , "dumb"
 
422
#else
 
423
      , termname()
 
424
#endif
 
425
      , ttyname(STDOUT_FILENO), NCURSES_VERSION
 
426
      , max_colors, max_pairs
 
427
      , Cap_can_goto ? "yes" : "No!"
 
428
      , Screen_cols, Screen_rows
 
429
      , Max_lines, (unsigned)Pseudo_size
 
430
#ifndef OFF_STDIOLBF
 
431
      , (unsigned)sizeof(Stdout_buf), (unsigned)BUFSIZ
 
432
#endif
 
433
      , (unsigned)sizeof(WIN_t), GROUPSMAX
 
434
      , Curwin->rc.winname, Curwin->grpname
 
435
      , Curwin->rc.winflags, Curwin->maxpflgs
 
436
      , Curwin->rc.sortindx, Curwin->rc.fieldscur
 
437
      , Curwin->rc.maxtasks, Curwin->varcolsz, Curwin->winlines
 
438
      , (int)strlen(Curwin->columnhdr)
 
439
      );
 
440
   }
 
441
}
 
442
#endif // end: ATEOJ_RPTSTD
 
443
 
 
444
#ifndef OFF_HST_HASH
 
445
#ifdef ATEOJ_RPTHSH
 
446
   if (!str && Ttychanged) {
 
447
      int i, j, pop, total_occupied, maxdepth, maxdepth_sav, numdepth
 
448
         , cross_foot, sz = HHASH_SIZ * (unsigned)sizeof(int);
 
449
      int depths[HHASH_SIZ];
 
450
 
 
451
      for (i = 0, total_occupied = 0, maxdepth = 0; i < HHASH_SIZ; i++) {
 
452
         int V = PHash_new[i];
 
453
         j = 0;
 
454
         if (-1 < V) {
 
455
            ++total_occupied;
 
456
            while (-1 < V) {
 
457
               V = PHist_new[V].lnk;
 
458
               if (-1 < V) j++;
 
459
            }
 
460
         }
 
461
         depths[i] = j;
 
462
         if (maxdepth < j) maxdepth = j;
 
463
      }
 
464
      maxdepth_sav = maxdepth;
 
465
 
 
466
      fprintf(stderr,
 
467
         "\n%s's Supplementary HASH report:"
 
468
         "\n\tTwo Tables providing for %d entries each + 1 extra for 'empty' image"
 
469
         "\n\t%dk (%d bytes) per table, %d total bytes (including 'empty' image)"
 
470
         "\n\tResults from latest hash (PHash_new + PHist_new)..."
 
471
         "\n"
 
472
         "\n\tTotal hashed = %d"
 
473
         "\n\tLevel-0 hash entries = %d (%d%% occupied)"
 
474
         "\n\tMax Depth = %d"
 
475
         "\n\n"
 
476
         , __func__
 
477
         , HHASH_SIZ, sz / 1024, sz, sz * 3
 
478
         , Frame_maxtask
 
479
         , total_occupied, (total_occupied * 100) / HHASH_SIZ
 
480
         , maxdepth + 1);
 
481
 
 
482
      if (total_occupied) {
 
483
         for (pop = total_occupied, cross_foot = 0; maxdepth; maxdepth--) {
 
484
            for (i = 0, numdepth = 0; i < HHASH_SIZ; i++)
 
485
               if (depths[i] == maxdepth) ++numdepth;
 
486
            fprintf(stderr,
 
487
               "\t %5d (%3d%%) hash table entries at depth %d\n"
 
488
               , numdepth, (numdepth * 100) / total_occupied, maxdepth + 1);
 
489
            pop -= numdepth;
 
490
            cross_foot += numdepth;
 
491
            if (0 == pop && cross_foot == total_occupied) break;
 
492
         }
 
493
         if (pop) {
 
494
            fprintf(stderr, "\t %5d (%3d%%) unchained hash table entries\n"
 
495
               , pop, (pop * 100) / total_occupied);
 
496
            cross_foot += pop;
 
497
         }
 
498
         fprintf(stderr,
 
499
            "\t -----\n"
 
500
            "\t %5d total entries occupied\n", cross_foot);
 
501
 
 
502
         if (maxdepth_sav) {
 
503
            fprintf(stderr, "\nPIDs at max depth: ");
 
504
            for (i = 0; i < HHASH_SIZ; i++)
 
505
               if (depths[i] == maxdepth_sav) {
 
506
                  j = PHash_new[i];
 
507
                  fprintf(stderr, "\n\tpos %4d:  %05d", i, PHist_new[j].pid);
 
508
                  while (-1 < j) {
 
509
                     j = PHist_new[j].lnk;
 
510
                     if (-1 < j) fprintf(stderr, ", %05d", PHist_new[j].pid);
 
511
                  }
 
512
               }
 
513
            fprintf(stderr, "\n");
 
514
         }
 
515
      }
 
516
   }
 
517
#endif // end: ATEOJ_RPTHSH
 
518
#endif // end: OFF_HST_HASH
 
519
 
 
520
#ifndef NUMA_DISABLE
 
521
  if (Libnuma_handle) dlclose(Libnuma_handle);
 
522
#endif
 
523
   if (str) {
 
524
      fputs(str, stderr);
 
525
      exit(EXIT_FAILURE);
 
526
   }
 
527
   exit(EXIT_SUCCESS);
 
528
} // end: bye_bye
 
529
 
 
530
 
 
531
        /*
 
532
         * Standard error handler to normalize the look of all err output */
 
533
static void error_exit (const char *str) NORETURN;
 
534
static void error_exit (const char *str) {
 
535
   static char buf[MEDBUFSIZ];
 
536
 
 
537
   /* we'll use our own buffer so callers can still use fmtmk() and, after
 
538
      twelve long years, 2013 was the year we finally eliminated the leading
 
539
      tab character -- now our message can get lost in screen clutter too! */
 
540
   snprintf(buf, sizeof(buf), "%s: %s\n", Myname, str);
 
541
   bye_bye(buf);
 
542
} // end: error_exit
 
543
 
 
544
 
 
545
        /*
 
546
         * Handle library errors ourselves rather than accept a default
 
547
         * fprintf to stderr (since we've mucked with the termios struct) */
 
548
static void library_err (const char *fmts, ...) NORETURN;
 
549
static void library_err (const char *fmts, ...) {
 
550
   static char tmp[MEDBUFSIZ];
 
551
   va_list va;
 
552
 
 
553
   va_start(va, fmts);
 
554
   vsnprintf(tmp, sizeof(tmp), fmts, va);
 
555
   va_end(va);
 
556
   error_exit(tmp);
 
557
} // end: library_err
 
558
 
 
559
 
 
560
        /*
 
561
         * Catches all remaining signals not otherwise handled */
 
562
static void sig_abexit (int sig) {
 
563
   sigset_t ss;
 
564
 
 
565
// POSIX.1-2004 async-signal-safe: sigfillset, sigprocmask, signal, raise
 
566
   sigfillset(&ss);
 
567
   sigprocmask(SIG_BLOCK, &ss, NULL);
 
568
   at_eoj();                 // restore tty in preparation for exit
 
569
   fprintf(stderr, N_fmt(EXIT_signals_fmt)
 
570
      , sig, signal_number_to_name(sig), Myname);
 
571
   signal(sig, SIG_DFL);     // allow core dumps, if applicable
 
572
   raise(sig);               // ( plus set proper return code )
 
573
} // end: sig_abexit
 
574
 
 
575
 
 
576
        /*
 
577
         * Catches:
 
578
         *    SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM,
 
579
         *    SIGUSR1 and SIGUSR2 */
 
580
static void sig_endpgm (int dont_care_sig) NORETURN;
 
581
static void sig_endpgm (int dont_care_sig) {
 
582
   sigset_t ss;
 
583
 
 
584
// POSIX.1-2004 async-signal-safe: sigfillset, sigprocmask
 
585
   sigfillset(&ss);
 
586
   sigprocmask(SIG_BLOCK, &ss, NULL);
 
587
   bye_bye(NULL);
 
588
   (void)dont_care_sig;
 
589
} // end: sig_endpgm
 
590
 
 
591
 
 
592
        /*
 
593
         * Catches:
 
594
         *    SIGTSTP, SIGTTIN and SIGTTOU
 
595
         * note:
 
596
         *    we don't fiddle with with those enter/exit_ca_mode strings
 
597
         *    because we want to retain most of the last screen contents
 
598
         *    as a visual reminder this program is suspended, not ended! */
 
599
static void sig_paused (int dont_care_sig) {
 
600
// POSIX.1-2004 async-signal-safe: tcsetattr, tcdrain, raise
 
601
   if (-1 == tcsetattr(STDIN_FILENO, TCSAFLUSH, &Tty_original))
 
602
      error_exit(fmtmk(N_fmt(FAIL_tty_set_fmt), strerror(errno)));
 
603
   if (keypad_local) putp(keypad_local);
 
604
   putp(tg2(0, Screen_rows));
 
605
   putp(Cap_curs_norm);
 
606
#ifndef RMAN_IGNORED
 
607
   putp(Cap_smam);
 
608
#endif
 
609
   // tcdrain(STDOUT_FILENO) was not reliable prior to ncurses-5.9.20121017,
 
610
   // so we'll risk POSIX's wrath with good ol' fflush, lest 'Stopped' gets
 
611
   // co-mingled with our most recent output...
 
612
   fflush(stdout);
 
613
   raise(SIGSTOP);
 
614
   // later, after SIGCONT...
 
615
   if (-1 == tcsetattr(STDIN_FILENO, TCSAFLUSH, &Tty_raw))
 
616
      error_exit(fmtmk(N_fmt(FAIL_tty_set_fmt), strerror(errno)));
 
617
#ifndef RMAN_IGNORED
 
618
   putp(Cap_rmam);
 
619
#endif
 
620
   if (keypad_xmit) putp(keypad_xmit);
 
621
   putp(Cursor_state);
 
622
   Frames_resize = RESIZ_sig;
 
623
   (void)dont_care_sig;
 
624
} // end: sig_paused
 
625
 
 
626
 
 
627
        /*
 
628
         * Catches:
 
629
         *    SIGCONT and SIGWINCH */
 
630
static void sig_resize (int dont_care_sig) {
 
631
// POSIX.1-2004 async-signal-safe: tcdrain
 
632
   tcdrain(STDOUT_FILENO);
 
633
   Frames_resize = RESIZ_sig;
 
634
   (void)dont_care_sig;
 
635
} // end: sig_resize
 
636
 
 
637
/*######  Misc Color/Display support  ####################################*/
 
638
 
 
639
        /*
 
640
         * Make the appropriate caps/color strings for a window/field group.
 
641
         * note: we avoid the use of background color so as to maximize
 
642
         *       compatibility with the user's xterm settings */
 
643
static void capsmk (WIN_t *q) {
 
644
   /* macro to test if a basic (non-color) capability is valid
 
645
         thanks: Floyd Davidson <floyd@ptialaska.net> */
 
646
 #define tIF(s)  s ? s : ""
 
647
   static int capsdone = 0;
 
648
 
 
649
   // we must NOT disturb our 'empty' terminfo strings!
 
650
   if (Batch) return;
 
651
 
 
652
   // these are the unchangeable puppies, so we only do 'em once
 
653
   if (!capsdone) {
 
654
      STRLCPY(Cap_clr_eol, tIF(clr_eol))
 
655
      STRLCPY(Cap_clr_eos, tIF(clr_eos))
 
656
      STRLCPY(Cap_clr_scr, tIF(clear_screen))
 
657
      // due to the leading newline, the following must be used with care
 
658
      snprintf(Cap_nl_clreos, sizeof(Cap_nl_clreos), "\n%s", tIF(clr_eos));
 
659
      STRLCPY(Cap_curs_huge, tIF(cursor_visible))
 
660
      STRLCPY(Cap_curs_norm, tIF(cursor_normal))
 
661
      STRLCPY(Cap_curs_hide, tIF(cursor_invisible))
 
662
      STRLCPY(Cap_home, tIF(cursor_home))
 
663
      STRLCPY(Cap_norm, tIF(exit_attribute_mode))
 
664
      STRLCPY(Cap_reverse, tIF(enter_reverse_mode))
 
665
#ifndef RMAN_IGNORED
 
666
      if (!eat_newline_glitch) {
 
667
         STRLCPY(Cap_rmam, tIF(exit_am_mode))
 
668
         STRLCPY(Cap_smam, tIF(enter_am_mode))
 
669
         if (!*Cap_rmam || !*Cap_smam) {
 
670
            *Cap_rmam = '\0';
 
671
            *Cap_smam = '\0';
 
672
            if (auto_right_margin)
 
673
               Cap_avoid_eol = 1;
 
674
         }
 
675
         putp(Cap_rmam);
 
676
      }
 
677
#endif
 
678
      snprintf(Caps_off, sizeof(Caps_off), "%s%s", Cap_norm, tIF(orig_pair));
 
679
      snprintf(Caps_endline, sizeof(Caps_endline), "%s%s", Caps_off, Cap_clr_eol);
 
680
      if (tgoto(cursor_address, 1, 1)) Cap_can_goto = 1;
 
681
      capsdone = 1;
 
682
   }
 
683
 
 
684
   /* the key to NO run-time costs for configurable colors -- we spend a
 
685
      little time with the user now setting up our terminfo strings, and
 
686
      the job's done until he/she/it has a change-of-heart */
 
687
   STRLCPY(q->cap_bold, CHKw(q, View_NOBOLD) ? Cap_norm : tIF(enter_bold_mode))
 
688
   if (CHKw(q, Show_COLORS) && max_colors > 0) {
 
689
      STRLCPY(q->capclr_sum, tparm(set_a_foreground, q->rc.summclr))
 
690
      snprintf(q->capclr_msg, sizeof(q->capclr_msg), "%s%s"
 
691
         , tparm(set_a_foreground, q->rc.msgsclr), Cap_reverse);
 
692
      snprintf(q->capclr_pmt, sizeof(q->capclr_pmt), "%s%s"
 
693
         , tparm(set_a_foreground, q->rc.msgsclr), q->cap_bold);
 
694
      snprintf(q->capclr_hdr, sizeof(q->capclr_hdr), "%s%s"
 
695
         , tparm(set_a_foreground, q->rc.headclr), Cap_reverse);
 
696
      snprintf(q->capclr_rownorm, sizeof(q->capclr_rownorm), "%s%s"
 
697
         , Caps_off, tparm(set_a_foreground, q->rc.taskclr));
 
698
   } else {
 
699
      q->capclr_sum[0] = '\0';
 
700
#ifdef USE_X_COLHDR
 
701
      snprintf(q->capclr_msg, sizeof(q->capclr_pmt), "%s%s"
 
702
         , Cap_reverse, q->cap_bold);
 
703
#else
 
704
      STRLCPY(q->capclr_msg, Cap_reverse)
 
705
#endif
 
706
      STRLCPY(q->capclr_pmt, q->cap_bold)
 
707
      STRLCPY(q->capclr_hdr, Cap_reverse)
 
708
      STRLCPY(q->capclr_rownorm, Cap_norm)
 
709
   }
 
710
 
 
711
   // composite(s), so we do 'em outside and after the if
 
712
   snprintf(q->capclr_rowhigh, sizeof(q->capclr_rowhigh), "%s%s"
 
713
      , q->capclr_rownorm, CHKw(q, Show_HIBOLD) ? q->cap_bold : Cap_reverse);
 
714
 #undef tIF
 
715
} // end: capsmk
 
716
 
 
717
 
 
718
        /*
 
719
         * Show an error message (caller may include '\a' for sound) */
 
720
static void show_msg (const char *str) {
 
721
   PUTT("%s%s %.*s %s%s%s"
 
722
      , tg2(0, Msg_row)
 
723
      , Curwin->capclr_msg
 
724
      , Screen_cols - 2
 
725
      , str
 
726
      , Cap_curs_hide
 
727
      , Caps_off
 
728
      , Cap_clr_eol);
 
729
   fflush(stdout);
 
730
   usleep(MSG_USLEEP);
 
731
} // end: show_msg
 
732
 
 
733
 
 
734
        /*
 
735
         * Show an input prompt + larger cursor (if possible) */
 
736
static int show_pmt (const char *str) {
 
737
   int rc;
 
738
 
 
739
   PUTT("%s%s%.*s %s%s%s"
 
740
      , tg2(0, Msg_row)
 
741
      , Curwin->capclr_pmt
 
742
      , Screen_cols - 2
 
743
      , str
 
744
      , Cap_curs_huge
 
745
      , Caps_off
 
746
      , Cap_clr_eol);
 
747
   fflush(stdout);
 
748
   // +1 for the space we added or -1 for the cursor...
 
749
   return ((rc = (int)strlen(str)+1) < Screen_cols) ? rc : Screen_cols-1;
 
750
} // end: show_pmt
 
751
 
 
752
 
 
753
        /*
 
754
         * Show a special coordinate message, in support of scrolling */
 
755
static inline void show_scroll (void) {
 
756
   PUTT(Scroll_fmts, tg2(0, Msg_row), Frame_maxtask);
 
757
} // end: show_scroll
 
758
 
 
759
 
 
760
        /*
 
761
         * Show lines with specially formatted elements, but only output
 
762
         * what will fit within the current screen width.
 
763
         *    Our special formatting consists of:
 
764
         *       "some text <_delimiter_> some more text <_delimiter_>...\n"
 
765
         *    Where <_delimiter_> is a two byte combination consisting of a
 
766
         *    tilde followed by an ascii digit in the the range of 1 - 8.
 
767
         *       examples: ~1,  ~5,  ~8, etc.
 
768
         *    The tilde is effectively stripped and the next digit
 
769
         *    converted to an index which is then used to select an
 
770
         *    'attribute' from a capabilities table.  That attribute
 
771
         *    is then applied to the *preceding* substring.
 
772
         * Once recognized, the delimiter is replaced with a null character
 
773
         * and viola, we've got a substring ready to output!  Strings or
 
774
         * substrings without delimiters will receive the Cap_norm attribute.
 
775
         *
 
776
         * Caution:
 
777
         *    This routine treats all non-delimiter bytes as displayable
 
778
         *    data subject to our screen width marching orders.  If callers
 
779
         *    embed non-display data like tabs or terminfo strings in our
 
780
         *    glob, a line will truncate incorrectly at best.  Worse case
 
781
         *    would be truncation of an embedded tty escape sequence.
 
782
         *
 
783
         *    Tabs must always be avoided or our efforts are wasted and
 
784
         *    lines will wrap.  To lessen but not eliminate the risk of
 
785
         *    terminfo string truncation, such non-display stuff should
 
786
         *    be placed at the beginning of a "short" line. */
 
787
static void show_special (int interact, const char *glob) {
 
788
  /* note: the following is for documentation only,
 
789
           the real captab is now found in a group's WIN_t !
 
790
     +------------------------------------------------------+
 
791
     | char *captab[] = {                 :   Cap's/Delim's |
 
792
     |   Cap_norm, Cap_norm,              =   \000, \001,   |
 
793
     |   cap_bold, capclr_sum,            =   \002, \003,   |
 
794
     |   capclr_msg, capclr_pmt,          =   \004, \005,   |
 
795
     |   capclr_hdr,                      =   \006,         |
 
796
     |   capclr_rowhigh,                  =   \007,         |
 
797
     |   capclr_rownorm  };               =   \010 [octal!] |
 
798
     +------------------------------------------------------+ */
 
799
  /* ( Pssst, after adding the termcap transitions, row may )
 
800
     ( exceed 300+ bytes, even in an 80x24 terminal window! )
 
801
     ( And if we're no longer guaranteed lines created only )
 
802
     ( by top, we'll need larger buffs plus some protection )
 
803
     ( against overrunning them with this 'lin_end - glob'. ) */
 
804
   char tmp[LRGBUFSIZ], lin[LRGBUFSIZ], row[ROWMAXSIZ];
 
805
   char *rp, *lin_end, *sub_beg, *sub_end;
 
806
   int room;
 
807
 
 
808
   // handle multiple lines passed in a bunch
 
809
   while ((lin_end = strchr(glob, '\n'))) {
 
810
     #define myMIN(a,b) (((a) < (b)) ? (a) : (b))
 
811
      size_t lessor = myMIN((size_t)(lin_end - glob), sizeof(lin) -1);
 
812
 
 
813
      // create a local copy we can extend and otherwise abuse
 
814
      memcpy(lin, glob, lessor);
 
815
      // zero terminate this part and prepare to parse substrings
 
816
      lin[lessor] = '\0';
 
817
      room = Screen_cols;
 
818
      sub_beg = sub_end = lin;
 
819
      *(rp = row) = '\0';
 
820
 
 
821
      while (*sub_beg) {
 
822
         int ch = *sub_end;
 
823
         if ('~' == ch) ch = *(sub_end + 1) - '0';
 
824
         switch (ch) {
 
825
            case 0:                    // no end delim, captab makes normal
 
826
               *(sub_end + 1) = '\0';  // extend str end, then fall through
 
827
               *(sub_end + 2) = '\0';  // ( +1 optimization for usual path )
 
828
            case 1: case 2: case 3: case 4:
 
829
            case 5: case 6: case 7: case 8:
 
830
               *sub_end = '\0';
 
831
               snprintf(tmp, sizeof(tmp), "%s%.*s%s"
 
832
                  , Curwin->captab[ch], room, sub_beg, Caps_off);
 
833
               rp = scat(rp, tmp);
 
834
               room -= (sub_end - sub_beg);
 
835
               sub_beg = (sub_end += 2);
 
836
               break;
 
837
            default:                   // nothin' special, just text
 
838
               ++sub_end;
 
839
         }
 
840
         if (0 >= room) break;         // skip substrings that won't fit
 
841
      }
 
842
 
 
843
      if (interact) PUTT("%s%s\n", row, Cap_clr_eol);
 
844
      else PUFF("%s%s\n", row, Caps_endline);
 
845
      glob = ++lin_end;                // point to next line (maybe)
 
846
 
 
847
     #undef myMIN
 
848
   } // end: while 'lines'
 
849
 
 
850
   /* If there's anything left in the glob (by virtue of no trailing '\n'),
 
851
      it probably means caller wants to retain cursor position on this final
 
852
      line.  That, in turn, means we're interactive and so we'll just do our
 
853
      'fit-to-screen' thingy while also leaving room for the cursor... */
 
854
   if (*glob) PUTT("%.*s", Screen_cols -1, glob);
 
855
} // end: show_special
 
856
 
 
857
 
 
858
        /*
 
859
         * Create a nearly complete scroll coordinates message, but still
 
860
         * a format string since we'll be missing a tgoto and total tasks. */
 
861
static void updt_scroll_msg (void) {
 
862
   char tmp1[SMLBUFSIZ], tmp2[SMLBUFSIZ];
 
863
   int totpflgs = Curwin->totpflgs;
 
864
   int begpflgs = Curwin->begpflg + 1;
 
865
 
 
866
#ifndef USE_X_COLHDR
 
867
   if (CHKw(Curwin, Show_HICOLS)) {
 
868
      totpflgs -= 2;
 
869
      if (ENUpos(Curwin, Curwin->rc.sortindx) < Curwin->begpflg) begpflgs -= 2;
 
870
   }
 
871
#endif
 
872
   if (1 > totpflgs) totpflgs = 1;
 
873
   if (1 > begpflgs) begpflgs = 1;
 
874
   snprintf(tmp1, sizeof(tmp1)
 
875
      , N_fmt(SCROLL_coord_fmt), Curwin->begtask + 1, begpflgs, totpflgs);
 
876
   strcpy(tmp2, tmp1);
 
877
#ifndef SCROLLVAR_NO
 
878
   if (Curwin->varcolbeg)
 
879
      snprintf(tmp2, sizeof(tmp2), "%s + %d", tmp1, Curwin->varcolbeg);
 
880
#endif
 
881
   snprintf(Scroll_fmts, sizeof(Scroll_fmts)
 
882
      , "%%s%s  %.*s%s", Caps_off, Screen_cols - 3, tmp2, Cap_clr_eol);
 
883
} // end: updt_scroll_msg
 
884
 
 
885
/*######  Low Level Memory/Keyboard/File I/O support  ####################*/
 
886
 
 
887
        /*
 
888
         * Handle our own memory stuff without the risk of leaving the
 
889
         * user's terminal in an ugly state should things go sour. */
 
890
 
 
891
static void *alloc_c (size_t num) MALLOC;
 
892
static void *alloc_c (size_t num) {
 
893
   void *pv;
 
894
 
 
895
   if (!num) ++num;
 
896
   if (!(pv = calloc(1, num)))
 
897
      error_exit(N_txt(FAIL_alloc_c_txt));
 
898
   return pv;
 
899
} // end: alloc_c
 
900
 
 
901
 
 
902
static void *alloc_r (void *ptr, size_t num) MALLOC;
 
903
static void *alloc_r (void *ptr, size_t num) {
 
904
   void *pv;
 
905
 
 
906
   if (!num) ++num;
 
907
   if (!(pv = realloc(ptr, num)))
 
908
      error_exit(N_txt(FAIL_alloc_r_txt));
 
909
   return pv;
 
910
} // end: alloc_r
 
911
 
 
912
 
 
913
static char *alloc_s (const char *str) MALLOC;
 
914
static char *alloc_s (const char *str) {
 
915
   return strcpy(alloc_c(strlen(str) +1), str);
 
916
} // end: alloc_s
 
917
 
 
918
 
 
919
        /*
 
920
         * This function is used in connection with raw single byte
 
921
         * unsolicited keyboard input that's susceptible to SIGWINCH
 
922
         * interrupts (or any other signal).  He also supports timout
 
923
         * in the absence of user keystrokes or some signal interrupt. */
 
924
static inline int ioa (struct timespec *ts) {
 
925
   fd_set fs;
 
926
   int rc;
 
927
 
 
928
   FD_ZERO(&fs);
 
929
   FD_SET(STDIN_FILENO, &fs);
 
930
 
 
931
#ifndef SIGNALS_LESS // conditional comments are silly, but help in documenting
 
932
   // hold here until we've got keyboard input, any signal (including SIGWINCH)
 
933
#else
 
934
   // hold here until we've got keyboard input, any signal except SIGWINCH
 
935
#endif
 
936
   // or (optionally) we timeout with nanosecond granularity
 
937
   rc = pselect(STDIN_FILENO + 1, &fs, NULL, NULL, ts, &Sigwinch_set);
 
938
 
 
939
   if (rc < 0) rc = 0;
 
940
   return rc;
 
941
} // end: ioa
 
942
 
 
943
 
 
944
        /*
 
945
         * This routine isolates ALL user INPUT and ensures that we
 
946
         * wont be mixing I/O from stdio and low-level read() requests */
 
947
static int ioch (int ech, char *buf, unsigned cnt) {
 
948
   int rc = -1;
 
949
 
 
950
#ifdef TERMIOS_ONLY
 
951
   if (ech) {
 
952
      tcsetattr(STDIN_FILENO, TCSAFLUSH, &Tty_tweaked);
 
953
      rc = read(STDIN_FILENO, buf, cnt);
 
954
      tcsetattr(STDIN_FILENO, TCSAFLUSH, &Tty_raw);
 
955
   } else {
 
956
      if (ioa(NULL))
 
957
         rc = read(STDIN_FILENO, buf, cnt);
 
958
   }
 
959
#else
 
960
   (void)ech;
 
961
   if (ioa(NULL))
 
962
      rc = read(STDIN_FILENO, buf, cnt);
 
963
#endif
 
964
 
 
965
   // zero means EOF, might happen if we erroneously get detached from terminal
 
966
   if (0 == rc) bye_bye(NULL);
 
967
 
 
968
   // it may have been the beginning of a lengthy escape sequence
 
969
   tcflush(STDIN_FILENO, TCIFLUSH);
 
970
 
 
971
   // note: we do NOT produce a vaid 'string'
 
972
   return rc;
 
973
} // end: ioch
 
974
 
 
975
 
 
976
        /*
 
977
         * Support for single or multiple keystroke input AND
 
978
         * escaped cursor motion keys.
 
979
         * note: we support more keys than we currently need, in case
 
980
         *       we attract new consumers in the future */
 
981
static int iokey (int action) {
 
982
   static char buf12[CAPBUFSIZ], buf13[CAPBUFSIZ]
 
983
      , buf14[CAPBUFSIZ], buf15[CAPBUFSIZ];
 
984
   static struct {
 
985
      const char *str;
 
986
      int key;
 
987
   } tinfo_tab[] = {
 
988
      { "\033\n",kbd_ENTER }, { NULL, kbd_UP       }, { NULL, kbd_DOWN     },
 
989
      { NULL, kbd_LEFT     }, { NULL, kbd_RIGHT    }, { NULL, kbd_PGUP     },
 
990
      { NULL, kbd_PGDN     }, { NULL, kbd_HOME     }, { NULL, kbd_END      },
 
991
      { NULL, kbd_BKSP     }, { NULL, kbd_INS      }, { NULL, kbd_DEL      },
 
992
         // next 4 destined to be meta + arrow keys...
 
993
      { buf12, kbd_PGUP    }, { buf13, kbd_PGDN    },
 
994
      { buf14, kbd_HOME    }, { buf15, kbd_END     },
 
995
         // remainder are alternatives for above, just in case...
 
996
         // ( the k,j,l,h entries are the vim cursor motion keys )
 
997
      { "\033\\",   kbd_UP    }, { "\033/",    kbd_DOWN  }, /* meta+      \,/ */
 
998
      { "\033<",    kbd_LEFT  }, { "\033>",    kbd_RIGHT }, /* meta+      <,> */
 
999
      { "\033k",    kbd_UP    }, { "\033j",    kbd_DOWN  }, /* meta+      k,j */
 
1000
      { "\033h",    kbd_LEFT  }, { "\033l",    kbd_RIGHT }, /* meta+      h,l */
 
1001
      { "\033\013", kbd_PGUP  }, { "\033\012", kbd_PGDN  }, /* ctrl+meta+ k,j */
 
1002
      { "\033\010", kbd_HOME  }, { "\033\014", kbd_END   }  /* ctrl+meta+ h,l */
 
1003
   };
 
1004
#ifdef TERMIOS_ONLY
 
1005
   char buf[SMLBUFSIZ], *pb;
 
1006
#else
 
1007
   static char buf[SMLBUFSIZ];
 
1008
   static int pos, len;
 
1009
   char *pb;
 
1010
#endif
 
1011
   int i;
 
1012
 
 
1013
   if (action == 0) {
 
1014
    #define tOk(s)  s ? s : ""
 
1015
      tinfo_tab[1].str  = tOk(key_up);
 
1016
      tinfo_tab[2].str  = tOk(key_down);
 
1017
      tinfo_tab[3].str  = tOk(key_left);
 
1018
      tinfo_tab[4].str  = tOk(key_right);
 
1019
      tinfo_tab[5].str  = tOk(key_ppage);
 
1020
      tinfo_tab[6].str  = tOk(key_npage);
 
1021
      tinfo_tab[7].str  = tOk(key_home);
 
1022
      tinfo_tab[8].str  = tOk(key_end);
 
1023
      tinfo_tab[9].str  = tOk(key_backspace);
 
1024
      tinfo_tab[10].str = tOk(key_ic);
 
1025
      tinfo_tab[11].str = tOk(key_dc);
 
1026
      STRLCPY(buf12, fmtmk("\033%s", tOk(key_up)));
 
1027
      STRLCPY(buf13, fmtmk("\033%s", tOk(key_down)));
 
1028
      STRLCPY(buf14, fmtmk("\033%s", tOk(key_left)));
 
1029
      STRLCPY(buf15, fmtmk("\033%s", tOk(key_right)));
 
1030
      // next is critical so returned results match bound terminfo keys
 
1031
      putp(tOk(keypad_xmit));
 
1032
      // ( converse keypad_local issued at pause/pgm end, just in case )
 
1033
      return 0;
 
1034
    #undef tOk
 
1035
   }
 
1036
 
 
1037
   if (action == 1) {
 
1038
      memset(buf, '\0', sizeof(buf));
 
1039
      if (1 > ioch(0, buf, sizeof(buf)-1)) return 0;
 
1040
   }
 
1041
 
 
1042
#ifndef TERMIOS_ONLY
 
1043
   if (action == 2) {
 
1044
      if (pos < len)
 
1045
         return buf[pos++];            // exhaust prior keystrokes
 
1046
      pos = len = 0;
 
1047
      memset(buf, '\0', sizeof(buf));
 
1048
      if (1 > ioch(0, buf, sizeof(buf)-1)) return 0;
 
1049
      if (isprint(buf[0])) {           // no need for translation
 
1050
         len = strlen(buf);
 
1051
         pos = 1;
 
1052
         return buf[0];
 
1053
      }
 
1054
   }
 
1055
#endif
 
1056
 
 
1057
   /* some emulators implement 'key repeat' too well and we get duplicate
 
1058
      key sequences -- so we'll focus on the last escaped sequence, while
 
1059
      also allowing use of the meta key... */
 
1060
   if (!(pb = strrchr(buf, '\033'))) pb = buf;
 
1061
   else if (pb > buf && '\033' == *(pb - 1)) --pb;
 
1062
 
 
1063
   for (i = 0; i < MAXTBL(tinfo_tab); i++)
 
1064
      if (!strcmp(tinfo_tab[i].str, pb))
 
1065
         return tinfo_tab[i].key;
 
1066
 
 
1067
   // no match, so we'll return single non-escaped keystrokes only
 
1068
   if (buf[0] == '\033' && buf[1]) return 0;
 
1069
   return buf[0];
 
1070
} // end: iokey
 
1071
 
 
1072
 
 
1073
#ifdef TERMIOS_ONLY
 
1074
        /*
 
1075
         * Get line oriented interactive input from the user,
 
1076
         * using native tty support */
 
1077
static char *ioline (const char *prompt) {
 
1078
   static const char ws[] = "\b\f\n\r\t\v\x1b\x9b";  // 0x1b + 0x9b are escape
 
1079
   static char buf[MEDBUFSIZ];
 
1080
   char *p;
 
1081
 
 
1082
   show_pmt(prompt);
 
1083
   memset(buf, '\0', sizeof(buf));
 
1084
   ioch(1, buf, sizeof(buf)-1);
 
1085
 
 
1086
   if ((p = strpbrk(buf, ws))) *p = '\0';
 
1087
   // note: we DO produce a vaid 'string'
 
1088
   return buf;
 
1089
} // end: ioline
 
1090
 
 
1091
#else
 
1092
        /*
 
1093
         * Get line oriented interactive input from the user,
 
1094
         * going way beyond native tty support by providing:
 
1095
         * . true line editing, not just destructive backspace
 
1096
         * . an input limit sensitive to current screen dimensions
 
1097
         * . ability to recall prior strings for re-input/re-editing */
 
1098
static char *ioline (const char *prompt) {
 
1099
 #define savMAX  50
 
1100
    // thank goodness memmove allows the two strings to overlap
 
1101
 #define sqzSTR  { memmove(&buf[pos], &buf[pos+1], bufMAX-pos); \
 
1102
       buf[sizeof(buf)-1] = '\0'; }
 
1103
 #define expSTR  if (len+1 < bufMAX && len+beg+1 < Screen_cols) { \
 
1104
       memmove(&buf[pos+1], &buf[pos], bufMAX-pos); buf[pos] = ' '; }
 
1105
 #define logCOL  (pos+1)
 
1106
 #define phyCOL  (beg+pos+1)
 
1107
 #define bufMAX  ((int)sizeof(buf)-2)  // -1 for '\0' string delimeter
 
1108
   static char buf[MEDBUFSIZ+1];       // +1 for '\0' string delimeter
 
1109
   static int ovt;
 
1110
   int beg, pos, len, key, i;
 
1111
   struct lin_s {
 
1112
      struct lin_s *bkw;               // ptr to older saved strs
 
1113
      struct lin_s *fwd;               // ptr to newer saved strs
 
1114
      char *str;                       // the saved string
 
1115
   };
 
1116
   static struct lin_s *anchor, *plin;
 
1117
 
 
1118
   if (!anchor) {
 
1119
      anchor = alloc_c(sizeof(struct lin_s));
 
1120
      anchor->str = alloc_s("");       // top-of-stack == empty str
 
1121
   }
 
1122
   plin = anchor;
 
1123
   pos = 0;
 
1124
   beg = show_pmt(prompt);
 
1125
   memset(buf, '\0', sizeof(buf));
 
1126
   putp(ovt ? Cap_curs_huge : Cap_curs_norm);
 
1127
 
 
1128
   do {
 
1129
      fflush(stdout);
 
1130
      len = strlen(buf);
 
1131
      key = iokey(2);
 
1132
      switch (key) {
 
1133
         case 0:
 
1134
         case kbd_ESC:
 
1135
            buf[0] = '\0';             // fall through !
 
1136
         case kbd_ENTER:
 
1137
            continue;
 
1138
         case kbd_INS:
 
1139
            ovt = !ovt;
 
1140
            putp(ovt ? Cap_curs_huge : Cap_curs_norm);
 
1141
            break;
 
1142
         case kbd_DEL:
 
1143
            sqzSTR
 
1144
            break;
 
1145
         case kbd_BKSP :
 
1146
            if (0 < pos) { --pos; sqzSTR }
 
1147
            break;
 
1148
         case kbd_LEFT:
 
1149
            if (0 < pos) --pos;
 
1150
            break;
 
1151
         case kbd_RIGHT:
 
1152
            if (pos < len) ++pos;
 
1153
            break;
 
1154
         case kbd_HOME:
 
1155
            pos = 0;
 
1156
            break;
 
1157
         case kbd_END:
 
1158
            pos = len;
 
1159
            break;
 
1160
         case kbd_UP:
 
1161
            if (plin->bkw) {
 
1162
               plin = plin->bkw;
 
1163
               memset(buf, '\0', sizeof(buf));
 
1164
               pos = snprintf(buf, sizeof(buf), "%s", plin->str);
 
1165
            }
 
1166
            break;
 
1167
         case kbd_DOWN:
 
1168
            memset(buf, '\0', sizeof(buf));
 
1169
            if (plin->fwd) plin = plin->fwd;
 
1170
            pos = snprintf(buf, sizeof(buf), "%s", plin->str);
 
1171
            break;
 
1172
         default:                      // what we REALLY wanted (maybe)
 
1173
            if (isprint(key) && logCOL < bufMAX && phyCOL < Screen_cols) {
 
1174
               if (!ovt) expSTR
 
1175
               buf[pos++] = key;
 
1176
            }
 
1177
            break;
 
1178
      }
 
1179
      putp(fmtmk("%s%s%s", tg2(beg, Msg_row), Cap_clr_eol, buf));
 
1180
      putp(tg2(beg+pos, Msg_row));
 
1181
   } while (key && key != kbd_ENTER && key != kbd_ESC);
 
1182
 
 
1183
   // weed out duplicates, including empty strings (top-of-stack)...
 
1184
   for (i = 0, plin = anchor; ; i++) {
 
1185
#ifdef RECALL_FIXED
 
1186
      if (!STRCMP(plin->str, buf))     // if matched, retain original order
 
1187
         return buf;
 
1188
#else
 
1189
      if (!STRCMP(plin->str, buf)) {   // if matched, rearrange stack order
 
1190
         if (i > 1) {                  // but not null str or if already #2
 
1191
            if (plin->bkw)             // splice around this matched string
 
1192
               plin->bkw->fwd = plin->fwd; // if older exists link to newer
 
1193
            plin->fwd->bkw = plin->bkw;    // newer linked to older or NULL
 
1194
            anchor->bkw->fwd = plin;   // stick matched on top of former #2
 
1195
            plin->bkw = anchor->bkw;   // keep empty string at top-of-stack
 
1196
            plin->fwd = anchor;        // then prepare to be the 2nd banana
 
1197
            anchor->bkw = plin;        // by sliding us in below the anchor
 
1198
         }
 
1199
         return buf;
 
1200
      }
 
1201
#endif
 
1202
      if (!plin->bkw) break;           // let i equal total stacked strings
 
1203
      plin = plin->bkw;                // ( with plin representing bottom )
 
1204
   }
 
1205
   if (i < savMAX)
 
1206
      plin = alloc_c(sizeof(struct lin_s));
 
1207
   else {                              // when a new string causes overflow
 
1208
      plin->fwd->bkw = NULL;           // make next-to-last string new last
 
1209
      free(plin->str);                 // and toss copy but keep the struct
 
1210
   }
 
1211
   plin->str = alloc_s(buf);           // copy user's new unique input line
 
1212
   plin->bkw = anchor->bkw;            // keep empty string as top-of-stack
 
1213
   if (plin->bkw)                      // did we have some already stacked?
 
1214
      plin->bkw->fwd = plin;           // yep, so point prior to new string
 
1215
   plin->fwd = anchor;                 // and prepare to be a second banana
 
1216
   anchor->bkw = plin;                 // by sliding it in as new number 2!
 
1217
 
 
1218
   return buf;                         // protect our copy, return original
 
1219
 #undef savMAX
 
1220
 #undef sqzSTR
 
1221
 #undef expSTR
 
1222
 #undef logCOL
 
1223
 #undef phyCOL
 
1224
 #undef bufMAX
 
1225
} // end: ioline
 
1226
#endif
 
1227
 
 
1228
 
 
1229
        /*
 
1230
         * This routine provides the i/o in support of files whose size
 
1231
         * cannot be determined in advance.  Given a stream pointer, he'll
 
1232
         * try to slurp in the whole thing and return a dynamically acquired
 
1233
         * buffer supporting that single string glob.
 
1234
         *
 
1235
         * He always creates a buffer at least READMINSZ big, possibly
 
1236
         * all zeros (an empty string), even if the file wasn't read. */
 
1237
static int readfile (FILE *fp, char **baddr, size_t *bsize, size_t *bread) {
 
1238
   char chunk[4096*16];
 
1239
   size_t num;
 
1240
 
 
1241
   *bread = 0;
 
1242
   *bsize = READMINSZ;
 
1243
   *baddr = alloc_c(READMINSZ);
 
1244
   if (fp) {
 
1245
      while (0 < (num = fread(chunk, 1, sizeof(chunk), fp))) {
 
1246
         *baddr = alloc_r(*baddr, num + *bsize);
 
1247
         memcpy(*baddr + *bread, chunk, num);
 
1248
         *bread += num;
 
1249
         *bsize += num;
 
1250
      };
 
1251
      *(*baddr + *bread) = '\0';
 
1252
      return ferror(fp);
 
1253
   }
 
1254
   return ENOENT;
 
1255
} // end: readfile
 
1256
 
 
1257
/*######  Small Utility routines  ########################################*/
 
1258
 
 
1259
        /*
 
1260
         * Get a float from the user */
 
1261
static float get_float (const char *prompt) {
 
1262
   char *line;
 
1263
   float f;
 
1264
 
 
1265
   line = ioline(prompt);
 
1266
   if (!line[0] || Frames_resize) return -1.0;
 
1267
   // note: we're not allowing negative floats
 
1268
   if (strcspn(line, "+,.0123456789")) {
 
1269
      show_msg(N_txt(BAD_numfloat_txt));
 
1270
      return -1.0;
 
1271
   }
 
1272
   sscanf(line, "%f", &f);
 
1273
   return f;
 
1274
} // end: get_float
 
1275
 
 
1276
 
 
1277
#define GET_INT_BAD  INT_MIN
 
1278
#define GET_INTNONE (INT_MIN + 1)
 
1279
 
 
1280
        /*
 
1281
         * Get an integer from the user, returning INT_MIN for error */
 
1282
static int get_int (const char *prompt) {
 
1283
   char *line;
 
1284
   int n;
 
1285
 
 
1286
   line = ioline(prompt);
 
1287
   if (Frames_resize) return GET_INT_BAD;
 
1288
   if (!line[0]) return GET_INTNONE;
 
1289
   // note: we've got to allow negative ints (renice)
 
1290
   if (strcspn(line, "-+0123456789")) {
 
1291
      show_msg(N_txt(BAD_integers_txt));
 
1292
      return GET_INT_BAD;
 
1293
   }
 
1294
   sscanf(line, "%d", &n);
 
1295
   return n;
 
1296
} // end: get_int
 
1297
 
 
1298
 
 
1299
        /*
 
1300
         * Make a hex value, and maybe suppress zeroes. */
 
1301
static inline const char *hex_make (KLONG num, int noz) {
 
1302
   static char buf[SMLBUFSIZ];
 
1303
   int i;
 
1304
 
 
1305
#ifdef CASEUP_HEXES
 
1306
   snprintf(buf, sizeof(buf), "%08" KLF "X", num);
 
1307
#else
 
1308
   snprintf(buf, sizeof(buf), "%08" KLF "x", num);
 
1309
#endif
 
1310
   if (noz)
 
1311
      for (i = 0; buf[i]; i++)
 
1312
         if ('0' == buf[i])
 
1313
            buf[i] = '.';
 
1314
   return buf;
 
1315
} // end: hex_make
 
1316
 
 
1317
 
 
1318
        /*
 
1319
         * This sructure is hung from a WIN_t when other filtering is active */
 
1320
struct osel_s {
 
1321
   struct osel_s *nxt;                         // the next criteria or NULL.
 
1322
   int (*rel)(const char *, const char *);     // relational strings compare
 
1323
   char *(*sel)(const char *, const char *);   // for selection str compares
 
1324
   char *raw;                                  // raw user input (dup check)
 
1325
   char *val;                                  // value included or excluded
 
1326
   int   ops;                                  // filter delimiter/operation
 
1327
   int   inc;                                  // include == 1, exclude == 0
 
1328
   int   enu;                                  // field (procflag) to filter
 
1329
};
 
1330
 
 
1331
 
 
1332
        /*
 
1333
         * A function to turn off entire other filtering in the given window */
 
1334
static void osel_clear (WIN_t *q) {
 
1335
   struct osel_s *osel = q->osel_1st;
 
1336
 
 
1337
   while (osel) {
 
1338
      struct osel_s *nxt = osel->nxt;
 
1339
      free(osel->val);
 
1340
      free(osel->raw);
 
1341
      free(osel);
 
1342
      osel = nxt;
 
1343
   }
 
1344
   q->osel_tot = 0;
 
1345
   q->osel_1st = NULL;
 
1346
   free (q->osel_prt);
 
1347
   q->osel_prt = NULL;
 
1348
#ifndef USE_X_COLHDR
 
1349
   OFFw(Curwin, NOHISEL_xxx);
 
1350
#endif
 
1351
} // end: osel_clear
 
1352
 
 
1353
 
 
1354
        /*
 
1355
         * Determine if there is a matching value or releationship among the
 
1356
         * other criteria in this passed window -- it's called from only one
 
1357
         * place, and likely inlined even without the directive */
 
1358
static inline int osel_matched (const WIN_t *q, FLG_t enu, const char *str) {
 
1359
   struct osel_s *osel = q->osel_1st;
 
1360
 
 
1361
   while (osel) {
 
1362
      if (osel->enu == enu) {
 
1363
         int r;
 
1364
         switch (osel->ops) {
 
1365
            case '<':                          // '<' needs the r < 0 unless
 
1366
               r = osel->rel(str, osel->val);  // '!' which needs an inverse
 
1367
               if ((r >= 0 && osel->inc) || (r < 0 && !osel->inc)) return 0;
 
1368
               break;
 
1369
            case '>':                          // '>' needs the r > 0 unless
 
1370
               r = osel->rel(str, osel->val);  // '!' which needs an inverse
 
1371
               if ((r <= 0 && osel->inc) || (r > 0 && !osel->inc)) return 0;
 
1372
               break;
 
1373
            default:
 
1374
            {  char *p = osel->sel(str, osel->val);
 
1375
               if ((!p && osel->inc) || (p && !osel->inc)) return 0;
 
1376
            }
 
1377
               break;
 
1378
         }
 
1379
      }
 
1380
      osel = osel->nxt;
 
1381
   }
 
1382
   return 1;
 
1383
} // end: osel_matched
 
1384
 
 
1385
 
 
1386
        /*
 
1387
         * Validate the passed string as a user name or number,
 
1388
         * and/or update the window's 'u/U' selection stuff. */
 
1389
static const char *user_certify (WIN_t *q, const char *str, char typ) {
 
1390
   struct passwd *pwd;
 
1391
   char *endp;
 
1392
   uid_t num;
 
1393
 
 
1394
   q->usrseltyp = 0;
 
1395
   q->usrselflg = 1;
 
1396
   Monpidsidx = 0;
 
1397
   if (*str) {
 
1398
      if ('!' == *str) { ++str; q->usrselflg = 0; }
 
1399
      num = (uid_t)strtoul(str, &endp, 0);
 
1400
      if ('\0' == *endp) {
 
1401
         pwd = getpwuid(num);
 
1402
         if (!pwd) {
 
1403
         /* allow foreign users, from e.g within chroot
 
1404
          ( thanks Dr. Werner Fink <werner@suse.de> ) */
 
1405
            q->usrseluid = num;
 
1406
            q->usrseltyp = typ;
 
1407
            return NULL;
 
1408
         }
 
1409
      } else
 
1410
         pwd = getpwnam(str);
 
1411
      if (!pwd) return N_txt(BAD_username_txt);
 
1412
      q->usrseluid = pwd->pw_uid;
 
1413
      q->usrseltyp = typ;
 
1414
   }
 
1415
   return NULL;
 
1416
} // end: user_certify
 
1417
 
 
1418
 
 
1419
        /*
 
1420
         * Determine if this proc_t matches the 'u/U' selection criteria
 
1421
         * for a given window -- it's called from only one place, and
 
1422
         * likely inlined even without the directive */
 
1423
static inline int user_matched (const WIN_t *q, const proc_t *p) {
 
1424
   switch(q->usrseltyp) {
 
1425
      case 0:                                    // uid selection inactive
 
1426
         return 1;
 
1427
      case 'U':                                  // match any uid
 
1428
         if (p->ruid == q->usrseluid) return q->usrselflg;
 
1429
         if (p->suid == q->usrseluid) return q->usrselflg;
 
1430
         if (p->fuid == q->usrseluid) return q->usrselflg;
 
1431
      // fall through...
 
1432
      case 'u':                                  // match effective uid
 
1433
         if (p->euid == q->usrseluid) return q->usrselflg;
 
1434
      // fall through...
 
1435
      default:                                   // no match...
 
1436
         ;
 
1437
   }
 
1438
   return !q->usrselflg;
 
1439
} // end: user_matched
 
1440
 
 
1441
/*######  Basic Formatting support  ######################################*/
 
1442
 
 
1443
        /*
 
1444
         * Just do some justify stuff, then add post column padding. */
 
1445
static inline const char *justify_pad (const char *str, int width, int justr) {
 
1446
   static char l_fmt[]  = "%-*.*s%s", r_fmt[] = "%*.*s%s";
 
1447
   static char buf[SCREENMAX];
 
1448
 
 
1449
   snprintf(buf, sizeof(buf), justr ? r_fmt : l_fmt, width, width, str, COLPADSTR);
 
1450
   return buf;
 
1451
} // end: justify_pad
 
1452
 
 
1453
 
 
1454
        /*
 
1455
         * Make and then justify a single character. */
 
1456
static inline const char *make_chr (const char ch, int width, int justr) {
 
1457
   static char buf[SMLBUFSIZ];
 
1458
 
 
1459
   snprintf(buf, sizeof(buf), "%c", ch);
 
1460
   return justify_pad(buf, width, justr);
 
1461
} // end: make_chr
 
1462
 
 
1463
 
 
1464
        /*
 
1465
         * Make and then justify an integer NOT subject to scaling,
 
1466
         * and include a visual clue should tuncation be necessary. */
 
1467
static inline const char *make_num (long num, int width, int justr, int col) {
 
1468
   static char buf[SMLBUFSIZ];
 
1469
 
 
1470
   if (width < snprintf(buf, sizeof(buf), "%ld", num)) {
 
1471
      buf[width-1] = COLPLUSCH;
 
1472
      AUTOX_COL(col);
 
1473
   }
 
1474
   return justify_pad(buf, width, justr);
 
1475
} // end: make_num
 
1476
 
 
1477
 
 
1478
        /*
 
1479
         * Make and then justify a character string,
 
1480
         * and include a visual clue should tuncation be necessary. */
 
1481
static inline const char *make_str (const char *str, int width, int justr, int col) {
 
1482
   static char buf[SCREENMAX];
 
1483
 
 
1484
   if (width < snprintf(buf, sizeof(buf), "%s", str)) {
 
1485
      buf[width-1] = COLPLUSCH;
 
1486
      AUTOX_COL(col);
 
1487
   }
 
1488
   return justify_pad(buf, width, justr);
 
1489
} // end: make_str
 
1490
 
 
1491
 
 
1492
        /*
 
1493
         * Do some scaling then justify stuff.
 
1494
         * We'll interpret 'num' as a kibibytes quantity and try to
 
1495
         * format it to reach 'target' while also fitting 'width'. */
 
1496
static const char *scale_mem (int target, unsigned long num, int width, int justr) {
 
1497
#ifndef NOBOOST_MEMS
 
1498
   //                               SK_Kb   SK_Mb      SK_Gb      SK_Tb      SK_Pb      SK_Eb
 
1499
   static const char *fmttab[] =  { "%.0f", "%#.1f%c", "%#.3f%c", "%#.3f%c", "%#.3f%c", NULL };
 
1500
#else
 
1501
   static const char *fmttab[] =  { "%.0f", "%.0f%c",  "%.0f%c",  "%.0f%c",  "%.0f%c",  NULL };
 
1502
#endif
 
1503
   static char buf[SMLBUFSIZ];
 
1504
   float scaled_num;
 
1505
   char *psfx;
 
1506
   int i;
 
1507
 
 
1508
   buf[0] = '\0';
 
1509
   if (Rc.zero_suppress && 0 >= num)
 
1510
      goto end_justifies;
 
1511
 
 
1512
   scaled_num = num;
 
1513
   for (i = SK_Kb, psfx = Scaled_sfxtab; i < SK_Eb; psfx++, i++) {
 
1514
      if (i >= target
 
1515
      && (width >= snprintf(buf, sizeof(buf), fmttab[i], scaled_num, *psfx)))
 
1516
         goto end_justifies;
 
1517
      scaled_num /= 1024.0;
 
1518
   }
 
1519
 
 
1520
   // well shoot, this outta' fit...
 
1521
   snprintf(buf, sizeof(buf), "?");
 
1522
end_justifies:
 
1523
   return justify_pad(buf, width, justr);
 
1524
} // end: scale_mem
 
1525
 
 
1526
 
 
1527
        /*
 
1528
         * Do some scaling then justify stuff. */
 
1529
static const char *scale_num (unsigned long num, int width, int justr) {
 
1530
   static char buf[SMLBUFSIZ];
 
1531
   float scaled_num;
 
1532
   char *psfx;
 
1533
 
 
1534
   buf[0] = '\0';
 
1535
   if (Rc.zero_suppress && 0 >= num)
 
1536
      goto end_justifies;
 
1537
   if (width >= snprintf(buf, sizeof(buf), "%lu", num))
 
1538
      goto end_justifies;
 
1539
 
 
1540
   scaled_num = num;
 
1541
   for (psfx = Scaled_sfxtab; 0 < *psfx; psfx++) {
 
1542
      scaled_num /= 1024.0;
 
1543
      if (width >= snprintf(buf, sizeof(buf), "%.1f%c", scaled_num, *psfx))
 
1544
         goto end_justifies;
 
1545
      if (width >= snprintf(buf, sizeof(buf), "%.0f%c", scaled_num, *psfx))
 
1546
         goto end_justifies;
 
1547
   }
 
1548
 
 
1549
   // well shoot, this outta' fit...
 
1550
   snprintf(buf, sizeof(buf), "?");
 
1551
end_justifies:
 
1552
   return justify_pad(buf, width, justr);
 
1553
} // end: scale_num
 
1554
 
 
1555
 
 
1556
        /*
 
1557
         * Make and then justify a percentage, with decreasing precision. */
 
1558
static const char *scale_pcnt (float num, int width, int justr) {
 
1559
   static char buf[SMLBUFSIZ];
 
1560
 
 
1561
   buf[0] = '\0';
 
1562
   if (Rc.zero_suppress && 0 >= num)
 
1563
      goto end_justifies;
 
1564
#ifdef BOOST_PERCNT
 
1565
   if (width >= snprintf(buf, sizeof(buf), "%#.3f", num))
 
1566
      goto end_justifies;
 
1567
   if (width >= snprintf(buf, sizeof(buf), "%#.2f", num))
 
1568
      goto end_justifies;
 
1569
#endif
 
1570
   if (width >= snprintf(buf, sizeof(buf), "%#.1f", num))
 
1571
      goto end_justifies;
 
1572
   if (width >= snprintf(buf, sizeof(buf), "%*.0f", width, num))
 
1573
      goto end_justifies;
 
1574
 
 
1575
   // well shoot, this outta' fit...
 
1576
   snprintf(buf, sizeof(buf), "?");
 
1577
end_justifies:
 
1578
   return justify_pad(buf, width, justr);
 
1579
} // end: scale_pcnt
 
1580
 
 
1581
 
 
1582
        /*
 
1583
         * Do some scaling stuff.
 
1584
         * Format 'tics' to fit 'width', then justify it. */
 
1585
static const char *scale_tics (TIC_t tics, int width, int justr) {
 
1586
#ifdef CASEUP_SUFIX
 
1587
 #define HH "%uH"                                                  // nls_maybe
 
1588
 #define DD "%uD"
 
1589
 #define WW "%uW"
 
1590
#else
 
1591
 #define HH "%uh"                                                  // nls_maybe
 
1592
 #define DD "%ud"
 
1593
 #define WW "%uw"
 
1594
#endif
 
1595
   static char buf[SMLBUFSIZ];
 
1596
   unsigned long nt;    // narrow time, for speed on 32-bit
 
1597
   unsigned cc;         // centiseconds
 
1598
   unsigned nn;         // multi-purpose whatever
 
1599
 
 
1600
   buf[0] = '\0';
 
1601
   nt  = (tics * 100ull) / Hertz;               // up to 68 weeks of cpu time
 
1602
   if (Rc.zero_suppress && 0 >= nt)
 
1603
      goto end_justifies;
 
1604
   cc  = nt % 100;                              // centiseconds past second
 
1605
   nt /= 100;                                   // total seconds
 
1606
   nn  = nt % 60;                               // seconds past the minute
 
1607
   nt /= 60;                                    // total minutes
 
1608
   if (width >= snprintf(buf, sizeof(buf), "%lu:%02u.%02u", nt, nn, cc))
 
1609
      goto end_justifies;
 
1610
   if (width >= snprintf(buf, sizeof(buf), "%lu:%02u", nt, nn))
 
1611
      goto end_justifies;
 
1612
   nn  = nt % 60;                               // minutes past the hour
 
1613
   nt /= 60;                                    // total hours
 
1614
   if (width >= snprintf(buf, sizeof(buf), "%lu,%02u", nt, nn))
 
1615
      goto end_justifies;
 
1616
   nn = nt;                                     // now also hours
 
1617
   if (width >= snprintf(buf, sizeof(buf), HH, nn))
 
1618
      goto end_justifies;
 
1619
   nn /= 24;                                    // now days
 
1620
   if (width >= snprintf(buf, sizeof(buf), DD, nn))
 
1621
      goto end_justifies;
 
1622
   nn /= 7;                                     // now weeks
 
1623
   if (width >= snprintf(buf, sizeof(buf), WW, nn))
 
1624
      goto end_justifies;
 
1625
 
 
1626
   // well shoot, this outta' fit...
 
1627
   snprintf(buf, sizeof(buf), "?");
 
1628
end_justifies:
 
1629
   return justify_pad(buf, width, justr);
 
1630
 #undef HH
 
1631
 #undef DD
 
1632
 #undef WW
 
1633
} // end: scale_tics
 
1634
 
 
1635
/*######  Fields Management support  #####################################*/
 
1636
 
 
1637
   /* These are the Fieldstab.lflg values used here and in calibrate_fields.
 
1638
      (own identifiers as documentation and protection against changes) */
 
1639
#define L_stat     PROC_FILLSTAT
 
1640
#define L_statm    PROC_FILLMEM
 
1641
#define L_status   PROC_FILLSTATUS
 
1642
#define L_CGROUP   PROC_EDITCGRPCVT | PROC_FILLCGROUP
 
1643
#define L_CMDLINE  PROC_EDITCMDLCVT | PROC_FILLARG
 
1644
#define L_ENVIRON  PROC_EDITENVRCVT | PROC_FILLENV
 
1645
#define L_EUSER    PROC_FILLUSR
 
1646
#define L_OUSER    PROC_FILLSTATUS | PROC_FILLUSR
 
1647
#define L_EGROUP   PROC_FILLSTATUS | PROC_FILLGRP
 
1648
#define L_SUPGRP   PROC_FILLSTATUS | PROC_FILLSUPGRP
 
1649
#define L_USED     PROC_FILLSTATUS | PROC_FILLMEM
 
1650
   // make 'none' non-zero (used to be important to Frames_libflags)
 
1651
#define L_NONE     PROC_SPARE_1
 
1652
   // from either 'stat' or 'status' (preferred), via bits not otherwise used
 
1653
#define L_EITHER   PROC_SPARE_2
 
1654
   // for calibrate_fields and summary_show 1st pass
 
1655
#define L_DEFAULT  PROC_FILLSTAT
 
1656
 
 
1657
        /* These are our gosh darn 'Fields' !
 
1658
           They MUST be kept in sync with pflags !! */
 
1659
static FLD_t Fieldstab[] = {
 
1660
   // a temporary macro, soon to be undef'd...
 
1661
 #define SF(f) (QFP_t)SCB_NAME(f)
 
1662
   // these identifiers reflect the default column alignment but they really
 
1663
   // contain the WIN_t flag used to check/change justification at run-time!
 
1664
 #define A_right Show_JRNUMS       /* toggled with upper case 'J' */
 
1665
 #define A_left  Show_JRSTRS       /* toggled with lower case 'j' */
 
1666
 
 
1667
/* .width anomalies:
 
1668
        a -1 width represents variable width columns
 
1669
        a  0 width represents columns set once at startup (see zap_fieldstab)
 
1670
   .lflg anomalies:
 
1671
        P_UED, L_NONE  - natural outgrowth of 'stat()' in readproc        (euid)
 
1672
        P_CPU, L_stat  - never filled by libproc, but requires times      (pcpu)
 
1673
        P_CMD, L_stat  - may yet require L_CMDLINE in calibrate_fields    (cmd/cmdline)
 
1674
        L_EITHER       - must L_status, else L_stat == 64-bit math (__udivdi3) on 32-bit !
 
1675
 
 
1676
     .width  .scale  .align    .sort     .lflg
 
1677
     ------  ------  --------  --------  --------  */
 
1678
   {     0,     -1,  A_right,  SF(PID),  L_NONE    },
 
1679
   {     0,     -1,  A_right,  SF(PPD),  L_EITHER  },
 
1680
   {     5,     -1,  A_right,  SF(UED),  L_NONE    },
 
1681
   {     8,     -1,  A_left,   SF(UEN),  L_EUSER   },
 
1682
   {     5,     -1,  A_right,  SF(URD),  L_status  },
 
1683
   {     8,     -1,  A_left,   SF(URN),  L_OUSER   },
 
1684
   {     5,     -1,  A_right,  SF(USD),  L_status  },
 
1685
   {     8,     -1,  A_left,   SF(USN),  L_OUSER   },
 
1686
   {     5,     -1,  A_right,  SF(GID),  L_NONE    },
 
1687
   {     8,     -1,  A_left,   SF(GRP),  L_EGROUP  },
 
1688
   {     0,     -1,  A_right,  SF(PGD),  L_stat    },
 
1689
   {     8,     -1,  A_left,   SF(TTY),  L_stat    },
 
1690
   {     0,     -1,  A_right,  SF(TPG),  L_stat    },
 
1691
   {     0,     -1,  A_right,  SF(SID),  L_stat    },
 
1692
   {     3,     -1,  A_right,  SF(PRI),  L_stat    },
 
1693
   {     3,     -1,  A_right,  SF(NCE),  L_stat    },
 
1694
   {     3,     -1,  A_right,  SF(THD),  L_EITHER  },
 
1695
   {     0,     -1,  A_right,  SF(CPN),  L_stat    },
 
1696
   {     0,     -1,  A_right,  SF(CPU),  L_stat    },
 
1697
   {     6,     -1,  A_right,  SF(TME),  L_stat    },
 
1698
   {     9,     -1,  A_right,  SF(TME),  L_stat    }, // P_TM2 slot
 
1699
#ifdef BOOST_PERCNT
 
1700
   {     5,     -1,  A_right,  SF(RES),  L_statm   }, // P_MEM slot
 
1701
#else
 
1702
   {     4,     -1,  A_right,  SF(RES),  L_statm   }, // P_MEM slot
 
1703
#endif
 
1704
#ifndef NOBOOST_MEMS
 
1705
   {     7,  SK_Kb,  A_right,  SF(VRT),  L_statm   },
 
1706
   {     6,  SK_Kb,  A_right,  SF(SWP),  L_status  },
 
1707
   {     6,  SK_Kb,  A_right,  SF(RES),  L_statm   },
 
1708
   {     6,  SK_Kb,  A_right,  SF(COD),  L_statm   },
 
1709
   {     7,  SK_Kb,  A_right,  SF(DAT),  L_statm   },
 
1710
   {     6,  SK_Kb,  A_right,  SF(SHR),  L_statm   },
 
1711
#else
 
1712
   {     5,  SK_Kb,  A_right,  SF(VRT),  L_statm   },
 
1713
   {     4,  SK_Kb,  A_right,  SF(SWP),  L_status  },
 
1714
   {     4,  SK_Kb,  A_right,  SF(RES),  L_statm   },
 
1715
   {     4,  SK_Kb,  A_right,  SF(COD),  L_statm   },
 
1716
   {     5,  SK_Kb,  A_right,  SF(DAT),  L_statm   },
 
1717
   {     4,  SK_Kb,  A_right,  SF(SHR),  L_statm   },
 
1718
#endif
 
1719
   {     4,     -1,  A_right,  SF(FL1),  L_stat    },
 
1720
   {     4,     -1,  A_right,  SF(FL2),  L_stat    },
 
1721
   {     4,     -1,  A_right,  SF(DRT),  L_statm   },
 
1722
   {     1,     -1,  A_right,  SF(STA),  L_EITHER  },
 
1723
   {    -1,     -1,  A_left,   SF(CMD),  L_EITHER  },
 
1724
   {    10,     -1,  A_left,   SF(WCH),  L_stat    },
 
1725
   {     8,     -1,  A_left,   SF(FLG),  L_stat    },
 
1726
   {    -1,     -1,  A_left,   SF(CGR),  L_CGROUP  },
 
1727
   {    -1,     -1,  A_left,   SF(SGD),  L_status  },
 
1728
   {    -1,     -1,  A_left,   SF(SGN),  L_SUPGRP  },
 
1729
   {     0,     -1,  A_right,  SF(TGD),  L_status  },
 
1730
#ifdef OOMEM_ENABLE
 
1731
#define L_oom      PROC_FILLOOM
 
1732
   {     3,     -1,  A_right,  SF(OOA),  L_oom     },
 
1733
   {     8,     -1,  A_right,  SF(OOM),  L_oom     },
 
1734
#undef L_oom
 
1735
#endif
 
1736
   {    -1,     -1,  A_left,   SF(ENV),  L_ENVIRON },
 
1737
   {     3,     -1,  A_right,  SF(FV1),  L_stat    },
 
1738
   {     3,     -1,  A_right,  SF(FV2),  L_stat    },
 
1739
#ifndef NOBOOST_MEMS
 
1740
   {     6,  SK_Kb,  A_right,  SF(USE),  L_USED    }
 
1741
#else
 
1742
   {     4,  SK_Kb,  A_right,  SF(USE),  L_USED    }
 
1743
#endif
 
1744
 #undef SF
 
1745
 #undef A_left
 
1746
 #undef A_right
 
1747
};
 
1748
 
 
1749
 
 
1750
        /*
 
1751
         * A calibrate_fields() *Helper* function to refresh the
 
1752
         * cached screen geometry and related variables */
 
1753
static void adj_geometry (void) {
 
1754
   static size_t pseudo_max = 0;
 
1755
   static int w_set = 0, w_cols = 0, w_rows = 0;
 
1756
   struct winsize wz;
 
1757
 
 
1758
   Screen_cols = columns;    // <term.h>
 
1759
   Screen_rows = lines;      // <term.h>
 
1760
 
 
1761
   if (-1 != ioctl(STDOUT_FILENO, TIOCGWINSZ, &wz)
 
1762
   && 0 < wz.ws_col && 0 < wz.ws_row) {
 
1763
      Screen_cols = wz.ws_col;
 
1764
      Screen_rows = wz.ws_row;
 
1765
   }
 
1766
 
 
1767
#ifndef RMAN_IGNORED
 
1768
   // be crudely tolerant of crude tty emulators
 
1769
   if (Cap_avoid_eol) Screen_cols--;
 
1770
#endif
 
1771
 
 
1772
   // we might disappoint some folks (but they'll deserve it)
 
1773
   if (SCREENMAX < Screen_cols) Screen_cols = SCREENMAX;
 
1774
 
 
1775
   if (!w_set) {
 
1776
      if (Width_mode > 0)              // -w with arg, we'll try to honor
 
1777
         w_cols = Width_mode;
 
1778
      else
 
1779
      if (Width_mode < 0) {            // -w without arg, try environment
 
1780
         char *env_columns = getenv("COLUMNS"),
 
1781
              *env_lines = getenv("LINES"),
 
1782
              *ep;
 
1783
         if (env_columns && *env_columns) {
 
1784
            long t, tc = 0;
 
1785
            t = strtol(env_columns, &ep, 0);
 
1786
            if (!*ep && (t > 0) && (t <= 0x7fffffffL)) tc = t;
 
1787
            if (0 < tc) w_cols = (int)tc;
 
1788
         }
 
1789
         if (env_lines && *env_lines) {
 
1790
            long t, tr = 0;
 
1791
            t = strtol(env_lines, &ep, 0);
 
1792
            if (!*ep && (t > 0) && (t <= 0x7fffffffL)) tr = t;
 
1793
            if (0 < tr) w_rows = (int)tr;
 
1794
         }
 
1795
         if (!w_cols) w_cols = SCREENMAX;
 
1796
         if (w_cols && w_cols < W_MIN_COL) w_cols = W_MIN_COL;
 
1797
         if (w_rows && w_rows < W_MIN_ROW) w_rows = W_MIN_ROW;
 
1798
      }
 
1799
      w_set = 1;
 
1800
   }
 
1801
 
 
1802
   /* keep our support for output optimization in sync with current reality
 
1803
      note: when we're in Batch mode, we don't really need a Pseudo_screen
 
1804
            and when not Batch, our buffer will contain 1 extra 'line' since
 
1805
            Msg_row is never represented -- but it's nice to have some space
 
1806
            between us and the great-beyond... */
 
1807
   if (Batch) {
 
1808
      if (w_cols) Screen_cols = w_cols;
 
1809
      Screen_rows = w_rows ? w_rows : MAXINT;
 
1810
      Pseudo_size = (sizeof(*Pseudo_screen) * ROWMAXSIZ);
 
1811
   } else {
 
1812
      if (w_cols && w_cols < Screen_cols) Screen_cols = w_cols;
 
1813
      if (w_rows && w_rows < Screen_rows) Screen_rows = w_rows;
 
1814
      Pseudo_size = (sizeof(*Pseudo_screen) * ROWMAXSIZ) * Screen_rows;
 
1815
   }
 
1816
   // we'll only grow our Pseudo_screen, never shrink it
 
1817
   if (pseudo_max < Pseudo_size) {
 
1818
      pseudo_max = Pseudo_size;
 
1819
      Pseudo_screen = alloc_r(Pseudo_screen, pseudo_max);
 
1820
   }
 
1821
   // ensure each row is repainted (just in case)
 
1822
   PSU_CLREOS(0);
 
1823
 
 
1824
   fflush(stdout);
 
1825
   Frames_resize = RESIZ_clr;
 
1826
} // end: adj_geometry
 
1827
 
 
1828
 
 
1829
        /*
 
1830
         * A calibrate_fields() *Helper* function to build the
 
1831
         * actual column headers and required library flags */
 
1832
static void build_headers (void) {
 
1833
   FLG_t f;
 
1834
   char *s;
 
1835
   WIN_t *w = Curwin;
 
1836
#ifdef EQUCOLHDRYES
 
1837
   int x, hdrmax = 0;
 
1838
#endif
 
1839
   int i, needpsdb = 0;
 
1840
 
 
1841
   Frames_libflags = 0;
 
1842
 
 
1843
   do {
 
1844
      if (VIZISw(w)) {
 
1845
         memset((s = w->columnhdr), 0, sizeof(w->columnhdr));
 
1846
         if (Rc.mode_altscr) s = scat(s, fmtmk("%d", w->winnum));
 
1847
         for (i = 0; i < w->maxpflgs; i++) {
 
1848
            f = w->procflgs[i];
 
1849
#ifdef USE_X_COLHDR
 
1850
            if (CHKw(w, Show_HICOLS) && f == w->rc.sortindx) {
 
1851
               s = scat(s, fmtmk("%s%s", Caps_off, w->capclr_msg));
 
1852
               w->hdrcaplen += strlen(Caps_off) + strlen(w->capclr_msg);
 
1853
            }
 
1854
#else
 
1855
            if (P_MAXPFLGS <= f) continue;
 
1856
#endif
 
1857
            if (P_WCH == f) needpsdb = 1;
 
1858
            if (P_CMD == f && CHKw(w, Show_CMDLIN)) Frames_libflags |= L_CMDLINE;
 
1859
            Frames_libflags |= Fieldstab[w->procflgs[i]].lflg;
 
1860
            s = scat(s, justify_pad(N_col(f)
 
1861
               , VARcol(f) ? w->varcolsz : Fieldstab[f].width
 
1862
               , CHKw(w, Fieldstab[f].align)));
 
1863
#ifdef USE_X_COLHDR
 
1864
            if (CHKw(w, Show_HICOLS) && f == w->rc.sortindx) {
 
1865
               s = scat(s, fmtmk("%s%s", Caps_off, w->capclr_hdr));
 
1866
               w->hdrcaplen += strlen(Caps_off) + strlen(w->capclr_hdr);
 
1867
            }
 
1868
#endif
 
1869
         }
 
1870
#ifdef EQUCOLHDRYES
 
1871
         // prepare to even out column header lengths...
 
1872
         if (hdrmax + w->hdrcaplen < (x = strlen(w->columnhdr))) hdrmax = x - w->hdrcaplen;
 
1873
#endif
 
1874
         // with forest view mode, we'll need tgid, ppid & start_time...
 
1875
         if (CHKw(w, Show_FOREST)) Frames_libflags |= (L_status | L_stat);
 
1876
         // for 'busy' only processes, we'll need pcpu (utime & stime)...
 
1877
         if (!CHKw(w, Show_IDLEPS)) Frames_libflags |= L_stat;
 
1878
         // we must also accommodate an out of view sort field...
 
1879
         f = w->rc.sortindx;
 
1880
         Frames_libflags |= Fieldstab[f].lflg;
 
1881
         if (P_CMD == f && CHKw(w, Show_CMDLIN)) Frames_libflags |= L_CMDLINE;
 
1882
      } // end: VIZISw(w)
 
1883
 
 
1884
      if (Rc.mode_altscr) w = w->next;
 
1885
   } while (w != Curwin);
 
1886
 
 
1887
#ifdef EQUCOLHDRYES
 
1888
   /* now we can finally even out column header lengths
 
1889
      (we're assuming entire columnhdr was memset to '\0') */
 
1890
   if (Rc.mode_altscr && SCREENMAX > Screen_cols)
 
1891
      for (i = 0; i < GROUPSMAX; i++) {
 
1892
         w = &Winstk[i];
 
1893
         if (CHKw(w, Show_TASKON))
 
1894
            if (hdrmax + w->hdrcaplen > (x = strlen(w->columnhdr)))
 
1895
               memset(&w->columnhdr[x], ' ', hdrmax + w->hdrcaplen - x);
 
1896
      }
 
1897
#endif
 
1898
 
 
1899
   // do we need the kernel symbol table (and is it already open?)
 
1900
   if (needpsdb) {
 
1901
      if (-1 == No_ksyms) {
 
1902
         No_ksyms = 0;
 
1903
         if (open_psdb_message(NULL, library_err))
 
1904
            No_ksyms = 1;
 
1905
         else
 
1906
            PSDBopen = 1;
 
1907
      }
 
1908
   }
 
1909
   // finalize/touchup the libproc PROC_FILLxxx flags for current config...
 
1910
   if ((Frames_libflags & L_EITHER) && !(Frames_libflags & L_stat))
 
1911
      Frames_libflags |= L_status;
 
1912
   if (!Frames_libflags) Frames_libflags = L_DEFAULT;
 
1913
   if (Monpidsidx) Frames_libflags |= PROC_PID;
 
1914
} // end: build_headers
 
1915
 
 
1916
 
 
1917
        /*
 
1918
         * This guy coordinates the activities surrounding the maintenance
 
1919
         * of each visible window's columns headers and the library flags
 
1920
         * required for the openproc interface. */
 
1921
static void calibrate_fields (void) {
 
1922
   FLG_t f;
 
1923
   char *s;
 
1924
   const char *h;
 
1925
   WIN_t *w = Curwin;
 
1926
   int i, varcolcnt, len;
 
1927
 
 
1928
   adj_geometry();
 
1929
 
 
1930
   do {
 
1931
      if (VIZISw(w)) {
 
1932
         w->hdrcaplen = 0;   // really only used with USE_X_COLHDR
 
1933
         // build window's pflgsall array, establish upper bounds for maxpflgs
 
1934
         for (i = 0, w->totpflgs = 0; i < P_MAXPFLGS; i++) {
 
1935
            if (FLDviz(w, i)) {
 
1936
               f = FLDget(w, i);
 
1937
#ifdef USE_X_COLHDR
 
1938
               w->pflgsall[w->totpflgs++] = f;
 
1939
#else
 
1940
               if (CHKw(w, Show_HICOLS) && f == w->rc.sortindx) {
 
1941
                  w->pflgsall[w->totpflgs++] = X_XON;
 
1942
                  w->pflgsall[w->totpflgs++] = f;
 
1943
                  w->pflgsall[w->totpflgs++] = X_XOF;
 
1944
               } else
 
1945
                  w->pflgsall[w->totpflgs++] = f;
 
1946
#endif
 
1947
            }
 
1948
         }
 
1949
 
 
1950
         /* build a preliminary columns header not to exceed screen width
 
1951
            while accounting for a possible leading window number */
 
1952
         w->varcolsz = varcolcnt = 0;
 
1953
         *(s = w->columnhdr) = '\0';
 
1954
         if (Rc.mode_altscr) s = scat(s, " ");
 
1955
         for (i = 0; i + w->begpflg < w->totpflgs; i++) {
 
1956
            f = w->pflgsall[i + w->begpflg];
 
1957
            w->procflgs[i] = f;
 
1958
#ifndef USE_X_COLHDR
 
1959
            if (P_MAXPFLGS <= f) continue;
 
1960
#endif
 
1961
            h = N_col(f);
 
1962
            len = (VARcol(f) ? (int)strlen(h) : Fieldstab[f].width) + COLPADSIZ;
 
1963
            // oops, won't fit -- we're outta here...
 
1964
            if (Screen_cols < ((int)(s - w->columnhdr) + len)) break;
 
1965
            if (VARcol(f)) { ++varcolcnt; w->varcolsz += strlen(h); }
 
1966
            s = scat(s, fmtmk("%*.*s", len, len, h));
 
1967
         }
 
1968
#ifndef USE_X_COLHDR
 
1969
         if (X_XON == w->procflgs[i - 1]) --i;
 
1970
#endif
 
1971
 
 
1972
         /* establish the final maxpflgs and prepare to grow the variable column
 
1973
            heading(s) via varcolsz - it may be a fib if their pflags weren't
 
1974
            encountered, but that's ok because they won't be displayed anyway */
 
1975
         w->maxpflgs = i;
 
1976
         w->varcolsz += Screen_cols - strlen(w->columnhdr);
 
1977
         if (varcolcnt) w->varcolsz /= varcolcnt;
 
1978
 
 
1979
         /* establish the field where all remaining fields would still
 
1980
            fit within screen width, including a leading window number */
 
1981
         *(s = w->columnhdr) = '\0';
 
1982
         if (Rc.mode_altscr) s = scat(s, " ");
 
1983
         for (i = w->totpflgs - 1; -1 < i; i--) {
 
1984
            f = w->pflgsall[i];
 
1985
#ifndef USE_X_COLHDR
 
1986
            if (P_MAXPFLGS <= f) { w->endpflg = i; continue; }
 
1987
#endif
 
1988
            h = N_col(f);
 
1989
            len = (VARcol(f) ? (int)strlen(h) : Fieldstab[f].width) + COLPADSIZ;
 
1990
            if (Screen_cols < ((int)(s - w->columnhdr) + len)) break;
 
1991
            s = scat(s, fmtmk("%*.*s", len, len, h));
 
1992
            w->endpflg = i;
 
1993
         }
 
1994
#ifndef USE_X_COLHDR
 
1995
         if (X_XOF == w->pflgsall[w->endpflg]) ++w->endpflg;
 
1996
#endif
 
1997
      } // end: if (VIZISw(w))
 
1998
 
 
1999
      if (Rc.mode_altscr) w = w->next;
 
2000
   } while (w != Curwin);
 
2001
 
 
2002
   build_headers();
 
2003
   if (CHKw(Curwin, View_SCROLL))
 
2004
      updt_scroll_msg();
 
2005
} // end: calibrate_fields
 
2006
 
 
2007
 
 
2008
        /*
 
2009
         * Display each field represented in the current window's fieldscur
 
2010
         * array along with its description.  Mark with bold and a leading
 
2011
         * asterisk those fields associated with the "on" or "active" state.
 
2012
         *
 
2013
         * Special highlighting will be accorded the "focus" field with such
 
2014
         * highlighting potentially extended to include the description.
 
2015
         *
 
2016
         * Below is the current Fieldstab space requirement and how
 
2017
         * we apportion it.  The xSUFX is considered sacrificial,
 
2018
         * something we can reduce or do without.
 
2019
         *            0        1         2         3
 
2020
         *            12345678901234567890123456789012
 
2021
         *            * HEADING = Longest Description!
 
2022
         *      xPRFX ----------______________________ xSUFX
 
2023
         *    ( xPRFX has pos 2 & 10 for 'extending' when at minimums )
 
2024
         *
 
2025
         * The first 4 screen rows are reserved for explanatory text, and
 
2026
         * the maximum number of columns is Screen_cols / xPRFX + 1 space
 
2027
         * between columns.  Thus, for example, with 42 fields a tty will
 
2028
         * still remain useable under these extremes:
 
2029
         *       rows       columns     what's
 
2030
         *       tty  top   tty  top    displayed
 
2031
         *       ---  ---   ---  ---    ------------------
 
2032
         *        46   42    10    1    xPRFX only
 
2033
         *        46   42    32    1    full xPRFX + xSUFX
 
2034
         *         6    2   231   21    xPRFX only
 
2035
         *        10    6   231    7    full xPRFX + xSUFX
 
2036
         */
 
2037
static void display_fields (int focus, int extend) {
 
2038
 #define mkERR { putp("\n"); putp(N_txt(XTRA_winsize_txt)); return; }
 
2039
 #define mxCOL ( (Screen_cols / 11) > 0 ? (Screen_cols / 11) : 1 )
 
2040
 #define yRSVD  4
 
2041
 #define xSUFX  22
 
2042
 #define xPRFX (10 + xadd)
 
2043
 #define xTOTL (xPRFX + xSUFX)
 
2044
   WIN_t *w = Curwin;                  // avoid gcc bloat with a local copy
 
2045
   int i;                              // utility int (a row, tot cols, ix)
 
2046
   int smax;                           // printable width of xSUFX
 
2047
   int xadd = 0;                       // spacing between data columns
 
2048
   int cmax = Screen_cols;             // total data column width
 
2049
   int rmax = Screen_rows - yRSVD;     // total useable rows
 
2050
   static int col_sav, row_sav;
 
2051
 
 
2052
   i = (P_MAXPFLGS % mxCOL) ? 1 : 0;
 
2053
   if (rmax < i + (P_MAXPFLGS / mxCOL)) mkERR;
 
2054
   i = P_MAXPFLGS / rmax;
 
2055
   if (P_MAXPFLGS % rmax) ++i;
 
2056
   if (i > 1) { cmax /= i; xadd = 1; }
 
2057
   if (cmax > xTOTL) cmax = xTOTL;
 
2058
   smax = cmax - xPRFX;
 
2059
   if (smax < 0) mkERR;
 
2060
 
 
2061
   /* we'll go the extra distance to avoid any potential screen flicker
 
2062
      which occurs under some terminal emulators (but it was our fault) */
 
2063
   if (col_sav != Screen_cols || row_sav != Screen_rows) {
 
2064
      col_sav = Screen_cols;
 
2065
      row_sav = Screen_rows;
 
2066
      putp(Cap_clr_eos);
 
2067
   }
 
2068
   fflush(stdout);
 
2069
 
 
2070
   for (i = 0; i < P_MAXPFLGS; ++i) {
 
2071
      int b = FLDviz(w, i), x = (i / rmax) * cmax, y = (i % rmax) + yRSVD;
 
2072
      const char *e = (i == focus && extend) ? w->capclr_hdr : "";
 
2073
      FLG_t f = FLDget(w, i);
 
2074
      char sbuf[xSUFX+1];
 
2075
 
 
2076
      // prep sacrificial suffix
 
2077
      snprintf(sbuf, sizeof(sbuf), "= %s", N_fld(f));
 
2078
 
 
2079
      PUTT("%s%c%s%s %s%-7.7s%s%s%s %-*.*s%s"
 
2080
         , tg2(x, y)
 
2081
         , b ? '*' : ' '
 
2082
         , b ? w->cap_bold : Cap_norm
 
2083
         , e
 
2084
         , i == focus ? w->capclr_hdr : ""
 
2085
         , N_col(f)
 
2086
         , Cap_norm
 
2087
         , b ? w->cap_bold : ""
 
2088
         , e
 
2089
         , smax, smax
 
2090
         , sbuf
 
2091
         , Cap_norm);
 
2092
   }
 
2093
 
 
2094
   putp(Caps_off);
 
2095
 #undef mkERR
 
2096
 #undef mxCOL
 
2097
 #undef yRSVD
 
2098
 #undef xSUFX
 
2099
 #undef xPRFX
 
2100
 #undef xTOTL
 
2101
} // end: display_fields
 
2102
 
 
2103
 
 
2104
        /*
 
2105
         * Manage all fields aspects (order/toggle/sort), for all windows. */
 
2106
static void fields_utility (void) {
 
2107
#ifndef SCROLLVAR_NO
 
2108
 #define unSCRL  { w->begpflg = w->varcolbeg = 0; OFFw(w, Show_HICOLS); }
 
2109
#else
 
2110
 #define unSCRL  { w->begpflg = 0; OFFw(w, Show_HICOLS); }
 
2111
#endif
 
2112
 #define swapEM  { char c; unSCRL; c = w->rc.fieldscur[i]; \
 
2113
       w->rc.fieldscur[i] = *p; *p = c; p = &w->rc.fieldscur[i]; }
 
2114
 #define spewFI  { char *t; f = w->rc.sortindx; t = strchr(w->rc.fieldscur, f + FLD_OFFSET); \
 
2115
       if (!t) t = strchr(w->rc.fieldscur, (f + FLD_OFFSET) | 0x80); \
 
2116
       i = (t) ? (int)(t - w->rc.fieldscur) : 0; }
 
2117
   WIN_t *w = Curwin;             // avoid gcc bloat with a local copy
 
2118
   const char *h = NULL;
 
2119
   char *p = NULL;
 
2120
   int i, key;
 
2121
   FLG_t f;
 
2122
 
 
2123
   spewFI
 
2124
signify_that:
 
2125
   putp(Cap_clr_scr);
 
2126
   adj_geometry();
 
2127
 
 
2128
   do {
 
2129
      if (!h) h = N_col(f);
 
2130
      putp(Cap_home);
 
2131
      show_special(1, fmtmk(N_unq(FIELD_header_fmt)
 
2132
         , w->grpname, CHKw(w, Show_FOREST) ? N_txt(FOREST_views_txt) : h));
 
2133
      display_fields(i, (p != NULL));
 
2134
      fflush(stdout);
 
2135
 
 
2136
      if (Frames_resize) goto signify_that;
 
2137
      key = iokey(1);
 
2138
      if (key < 1) goto signify_that;
 
2139
 
 
2140
      switch (key) {
 
2141
         case kbd_UP:
 
2142
            if (i > 0) { --i; if (p) swapEM }
 
2143
            break;
 
2144
         case kbd_DOWN:
 
2145
            if (i + 1 < P_MAXPFLGS) { ++i; if (p) swapEM }
 
2146
            break;
 
2147
         case kbd_LEFT:
 
2148
         case kbd_ENTER:
 
2149
            p = NULL;
 
2150
            break;
 
2151
         case kbd_RIGHT:
 
2152
            p = &w->rc.fieldscur[i];
 
2153
            break;
 
2154
         case kbd_HOME:
 
2155
         case kbd_PGUP:
 
2156
            if (!p) i = 0;
 
2157
            break;
 
2158
         case kbd_END:
 
2159
         case kbd_PGDN:
 
2160
            if (!p) i = P_MAXPFLGS - 1;
 
2161
            break;
 
2162
         case kbd_SPACE:
 
2163
         case 'd':
 
2164
            if (!p) { FLDtog(w, i); unSCRL }
 
2165
            break;
 
2166
         case 's':
 
2167
#ifdef TREE_NORESET
 
2168
            if (!p && !CHKw(w, Show_FOREST)) { w->rc.sortindx = f = FLDget(w, i); h = NULL; unSCRL }
 
2169
#else
 
2170
            if (!p) { w->rc.sortindx = f = FLDget(w, i); h = NULL; unSCRL; OFFw(w, Show_FOREST); }
 
2171
#endif
 
2172
            break;
 
2173
         case 'a':
 
2174
         case 'w':
 
2175
            Curwin = w = ('a' == key) ? w->next : w->prev;
 
2176
            spewFI
 
2177
            h = p = NULL;
 
2178
            break;
 
2179
         default:                 // keep gcc happy
 
2180
            break;
 
2181
      }
 
2182
   } while (key != 'q' && key != kbd_ESC);
 
2183
 #undef unSCRL
 
2184
 #undef swapEM
 
2185
 #undef spewFI
 
2186
} // end: fields_utility
 
2187
 
 
2188
 
 
2189
        /*
 
2190
         * This routine takes care of auto sizing field widths
 
2191
         * if/when the user sets Rc.fixed_widest to -1.  Along the
 
2192
         * way he reinitializes some things for the next frame. */
 
2193
static inline void widths_resize (void) {
 
2194
   int i;
 
2195
 
 
2196
   // next var may also be set by the guys that actually truncate stuff
 
2197
   Autox_found = 0;
 
2198
   for (i = 0; i < P_MAXPFLGS; i++) {
 
2199
      if (Autox_array[i]) {
 
2200
         Fieldstab[i].width++;
 
2201
         Autox_array[i] = 0;
 
2202
         Autox_found = 1;
 
2203
      }
 
2204
   }
 
2205
   if (Autox_found) calibrate_fields();
 
2206
} // end: widths_resize
 
2207
 
 
2208
 
 
2209
        /*
 
2210
         * This routine exists just to consolidate most of the messin'
 
2211
         * around with the Fieldstab array and some related stuff. */
 
2212
static void zap_fieldstab (void) {
 
2213
   static int once;
 
2214
   unsigned digits;
 
2215
   char buf[8];
 
2216
 
 
2217
   if (!once) {
 
2218
      Fieldstab[P_PID].width = Fieldstab[P_PPD].width
 
2219
         = Fieldstab[P_PGD].width = Fieldstab[P_SID].width
 
2220
         = Fieldstab[P_TGD].width = Fieldstab[P_TPG].width = 5;
 
2221
      if (5 < (digits = get_pid_digits())) {
 
2222
         if (10 < digits) error_exit(N_txt(FAIL_widepid_txt));
 
2223
         Fieldstab[P_PID].width = Fieldstab[P_PPD].width
 
2224
            = Fieldstab[P_PGD].width = Fieldstab[P_SID].width
 
2225
            = Fieldstab[P_TGD].width = Fieldstab[P_TPG].width = digits;
 
2226
      }
 
2227
      once = 1;
 
2228
   }
 
2229
 
 
2230
   /*** hotplug_acclimated ***/
 
2231
 
 
2232
   Fieldstab[P_CPN].width = 1;
 
2233
   if (1 < (digits = (unsigned)snprintf(buf, sizeof(buf), "%u", (unsigned)smp_num_cpus))) {
 
2234
      if (5 < digits) error_exit(N_txt(FAIL_widecpu_txt));
 
2235
      Fieldstab[P_CPN].width = digits;
 
2236
   }
 
2237
 
 
2238
#ifdef BOOST_PERCNT
 
2239
   Cpu_pmax = 99.9;
 
2240
   Fieldstab[P_CPU].width = 5;
 
2241
   if (Rc.mode_irixps && smp_num_cpus > 1 && !Thread_mode) {
 
2242
      Cpu_pmax = 100.0 * smp_num_cpus;
 
2243
      if (smp_num_cpus > 10) {
 
2244
         if (Cpu_pmax > 99999.0) Cpu_pmax = 99999.0;
 
2245
      } else {
 
2246
         if (Cpu_pmax > 999.9) Cpu_pmax = 999.9;
 
2247
      }
 
2248
   }
 
2249
#else
 
2250
   Cpu_pmax = 99.9;
 
2251
   Fieldstab[P_CPU].width = 4;
 
2252
   if (Rc.mode_irixps && smp_num_cpus > 1 && !Thread_mode) {
 
2253
      Cpu_pmax = 100.0 * smp_num_cpus;
 
2254
      if (smp_num_cpus > 10) {
 
2255
         if (Cpu_pmax > 99999.0) Cpu_pmax = 99999.0;
 
2256
      } else {
 
2257
         if (Cpu_pmax > 999.9) Cpu_pmax = 999.9;
 
2258
      }
 
2259
      Fieldstab[P_CPU].width = 5;
 
2260
   }
 
2261
#endif
 
2262
 
 
2263
   /* and accommodate optional wider non-scalable columns (maybe) */
 
2264
   if (!AUTOX_MODE) {
 
2265
      Fieldstab[P_UED].width = Fieldstab[P_URD].width
 
2266
         = Fieldstab[P_USD].width = Fieldstab[P_GID].width
 
2267
         = Rc.fixed_widest ? 5 + Rc.fixed_widest : 5;
 
2268
      Fieldstab[P_UEN].width = Fieldstab[P_URN].width
 
2269
         = Fieldstab[P_USN].width = Fieldstab[P_GRP].width
 
2270
         = Rc.fixed_widest ? 8 + Rc.fixed_widest : 8;
 
2271
      Fieldstab[P_TTY].width
 
2272
         = Rc.fixed_widest ? 8 + Rc.fixed_widest : 8;
 
2273
      Fieldstab[P_WCH].width
 
2274
         = Rc.fixed_widest ? 10 + Rc.fixed_widest : 10;
 
2275
   }
 
2276
 
 
2277
   /* plus user selectable scaling */
 
2278
   Fieldstab[P_VRT].scale = Fieldstab[P_SWP].scale
 
2279
      = Fieldstab[P_RES].scale = Fieldstab[P_COD].scale
 
2280
      = Fieldstab[P_DAT].scale = Fieldstab[P_SHR].scale
 
2281
      = Fieldstab[P_USE].scale = Rc.task_mscale;
 
2282
 
 
2283
   // lastly, ensure we've got proper column headers...
 
2284
   calibrate_fields();
 
2285
} // end: zap_fieldstab
 
2286
 
 
2287
/*######  Library Interface  #############################################*/
 
2288
 
 
2289
        /*
 
2290
         * This guy's modeled on libproc's 'eight_cpu_numbers' function except
 
2291
         * we preserve all cpu data in our CPU_t array which is organized
 
2292
         * as follows:
 
2293
         *    cpus[0] thru cpus[n] == tics for each separate cpu
 
2294
         *    cpus[sumSLOT]        == tics from the 1st /proc/stat line
 
2295
         *  [ and beyond sumSLOT   == tics for each cpu NUMA node ] */
 
2296
static CPU_t *cpus_refresh (CPU_t *cpus) {
 
2297
 #define sumSLOT ( smp_num_cpus )
 
2298
 #define totSLOT ( 1 + smp_num_cpus + Numa_node_tot)
 
2299
   static FILE *fp = NULL;
 
2300
   static int siz, sav_slot = -1;
 
2301
   static char *buf;
 
2302
   int i, num, tot_read;
 
2303
#ifndef NUMA_DISABLE
 
2304
   int node;
 
2305
#endif
 
2306
   char *bp;
 
2307
 
 
2308
   /*** hotplug_acclimated ***/
 
2309
   if (sav_slot != sumSLOT) {
 
2310
      sav_slot = sumSLOT;
 
2311
      zap_fieldstab();
 
2312
      if (fp) { fclose(fp); fp = NULL; }
 
2313
      if (cpus) { free(cpus); cpus = NULL; }
 
2314
   }
 
2315
 
 
2316
   /* by opening this file once, we'll avoid the hit on minor page faults
 
2317
      (sorry Linux, but you'll have to close it for us) */
 
2318
   if (!fp) {
 
2319
      if (!(fp = fopen("/proc/stat", "r")))
 
2320
         error_exit(fmtmk(N_fmt(FAIL_statopn_fmt), strerror(errno)));
 
2321
      /* note: we allocate one more CPU_t via totSLOT than 'cpus' so that a
 
2322
               slot can hold tics representing the /proc/stat cpu summary */
 
2323
      cpus = alloc_c(totSLOT * sizeof(CPU_t));
 
2324
   }
 
2325
   rewind(fp);
 
2326
   fflush(fp);
 
2327
 
 
2328
 #define buffGRW 1024
 
2329
   /* we slurp in the entire directory thus avoiding repeated calls to fgets,
 
2330
      especially in a massively parallel environment.  additionally, each cpu
 
2331
      line is then frozen in time rather than changing until we get around to
 
2332
      accessing it.  this helps to minimize (not eliminate) most distortions. */
 
2333
   tot_read = 0;
 
2334
   if (buf) buf[0] = '\0';
 
2335
   else buf = alloc_c((siz = buffGRW));
 
2336
   while (0 < (num = fread(buf + tot_read, 1, (siz - tot_read), fp))) {
 
2337
      tot_read += num;
 
2338
      if (tot_read < siz) break;
 
2339
      buf = alloc_r(buf, (siz += buffGRW));
 
2340
   };
 
2341
   buf[tot_read] = '\0';
 
2342
   bp = buf;
 
2343
 #undef buffGRW
 
2344
 
 
2345
   // remember from last time around
 
2346
   memcpy(&cpus[sumSLOT].sav, &cpus[sumSLOT].cur, sizeof(CT_t));
 
2347
   // then value the last slot with the cpu summary line
 
2348
   if (4 > sscanf(bp, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu"
 
2349
      , &cpus[sumSLOT].cur.u, &cpus[sumSLOT].cur.n, &cpus[sumSLOT].cur.s
 
2350
      , &cpus[sumSLOT].cur.i, &cpus[sumSLOT].cur.w, &cpus[sumSLOT].cur.x
 
2351
      , &cpus[sumSLOT].cur.y, &cpus[sumSLOT].cur.z))
 
2352
         error_exit(N_txt(FAIL_statget_txt));
 
2353
#ifndef CPU_ZEROTICS
 
2354
   cpus[sumSLOT].cur.tot = cpus[sumSLOT].cur.u + cpus[sumSLOT].cur.s
 
2355
      + cpus[sumSLOT].cur.n + cpus[sumSLOT].cur.i + cpus[sumSLOT].cur.w
 
2356
      + cpus[sumSLOT].cur.x + cpus[sumSLOT].cur.y + cpus[sumSLOT].cur.z;
 
2357
   /* if a cpu has registered substantially fewer tics than those expected,
 
2358
      we'll force it to be treated as 'idle' so as not to present misleading
 
2359
      percentages. */
 
2360
   cpus[sumSLOT].edge =
 
2361
      ((cpus[sumSLOT].cur.tot - cpus[sumSLOT].sav.tot) / smp_num_cpus) / (100 / TICS_EDGE);
 
2362
#endif
 
2363
 
 
2364
#ifndef NUMA_DISABLE
 
2365
   for (i = 0; i < Numa_node_tot; i++) {
 
2366
      node = sumSLOT + 1 + i;
 
2367
      // remember from last time around
 
2368
      memcpy(&cpus[node].sav, &cpus[node].cur, sizeof(CT_t));
 
2369
      // initialize current node statistics
 
2370
      memset(&cpus[node].cur, 0, sizeof(CT_t));
 
2371
#ifndef CPU_ZEROTICS
 
2372
      cpus[node].edge = cpus[sumSLOT].edge;
 
2373
      // this is for symmetry only, it's not currently required
 
2374
      cpus[node].cur.tot = cpus[sumSLOT].cur.tot;
 
2375
#endif
 
2376
   }
 
2377
#endif
 
2378
 
 
2379
   // now value each separate cpu's tics...
 
2380
   for (i = 0; i < sumSLOT; i++) {
 
2381
#ifdef PRETEND8CPUS
 
2382
      bp = buf;
 
2383
#endif
 
2384
      bp = 1 + strchr(bp, '\n');
 
2385
      // remember from last time around
 
2386
      memcpy(&cpus[i].sav, &cpus[i].cur, sizeof(CT_t));
 
2387
      if (4 > sscanf(bp, "cpu%d %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", &cpus[i].id
 
2388
         , &cpus[i].cur.u, &cpus[i].cur.n, &cpus[i].cur.s
 
2389
         , &cpus[i].cur.i, &cpus[i].cur.w, &cpus[i].cur.x
 
2390
         , &cpus[i].cur.y, &cpus[i].cur.z)) {
 
2391
            memmove(&cpus[i], &cpus[sumSLOT], sizeof(CPU_t));
 
2392
            break;        // tolerate cpus taken offline
 
2393
      }
 
2394
 
 
2395
#ifndef CPU_ZEROTICS
 
2396
      cpus[i].edge = cpus[sumSLOT].edge;
 
2397
      // this is for symmetry only, it's not currently required
 
2398
      cpus[i].cur.tot = cpus[sumSLOT].cur.tot;
 
2399
#endif
 
2400
#ifdef PRETEND8CPUS
 
2401
      cpus[i].id = i;
 
2402
#endif
 
2403
#ifndef NUMA_DISABLE
 
2404
      if (Numa_node_tot
 
2405
      && -1 < (node = Numa_node_of_cpu(cpus[i].id))) {
 
2406
         cpus[i].node = node;
 
2407
         node += (sumSLOT + 1);
 
2408
         cpus[node].cur.u += cpus[i].cur.u;
 
2409
         cpus[node].cur.n += cpus[i].cur.n;
 
2410
         cpus[node].cur.s += cpus[i].cur.s;
 
2411
         cpus[node].cur.i += cpus[i].cur.i;
 
2412
         cpus[node].cur.w += cpus[i].cur.w;
 
2413
         cpus[node].cur.x += cpus[i].cur.x;
 
2414
         cpus[node].cur.y += cpus[i].cur.y;
 
2415
         cpus[node].cur.z += cpus[i].cur.z;
 
2416
      }
 
2417
#endif
 
2418
   }
 
2419
 
 
2420
   Cpu_faux_tot = i;      // tolerate cpus taken offline
 
2421
 
 
2422
   return cpus;
 
2423
 #undef sumSLOT
 
2424
 #undef totSLOT
 
2425
} // end: cpus_refresh
 
2426
 
 
2427
 
 
2428
#ifdef OFF_HST_HASH
 
2429
        /*
 
2430
         * Binary Search for HST_t's put/get support */
 
2431
 
 
2432
static inline HST_t *hstbsrch (HST_t *hst, int max, int pid) {
 
2433
   int mid, min = 0;
 
2434
 
 
2435
   while (min <= max) {
 
2436
      mid = (min + max) / 2;
 
2437
      if (pid < hst[mid].pid) max = mid - 1;
 
2438
      else if (pid > hst[mid].pid) min = mid + 1;
 
2439
      else return &hst[mid];
 
2440
   }
 
2441
   return NULL;
 
2442
} // end: hstbsrch
 
2443
 
 
2444
#else
 
2445
        /*
 
2446
         * Hashing functions for HST_t's put/get support
 
2447
         * (not your normal 'chaining', those damn HST_t's might move!) */
 
2448
 
 
2449
#define _HASH_(K) (K & (HHASH_SIZ - 1))
 
2450
 
 
2451
static inline HST_t *hstget (int pid) {
 
2452
   int V = PHash_sav[_HASH_(pid)];
 
2453
 
 
2454
   while (-1 < V) {
 
2455
      if (PHist_sav[V].pid == pid) return &PHist_sav[V];
 
2456
      V = PHist_sav[V].lnk; }
 
2457
   return NULL;
 
2458
} // end: hstget
 
2459
 
 
2460
 
 
2461
static inline void hstput (unsigned idx) {
 
2462
   int V = _HASH_(PHist_new[idx].pid);
 
2463
 
 
2464
   PHist_new[idx].lnk = PHash_new[V];
 
2465
   PHash_new[V] = idx;
 
2466
} // end: hstput
 
2467
 
 
2468
#undef _HASH_
 
2469
#endif
 
2470
 
 
2471
        /*
 
2472
         * Refresh procs *Helper* function to eliminate yet one more need
 
2473
         * to loop through our darn proc_t table.  He's responsible for:
 
2474
         *    1) calculating the elapsed time since the previous frame
 
2475
         *    2) counting the number of tasks in each state (run, sleep, etc)
 
2476
         *    3) maintaining the HST_t's and priming the proc_t pcpu field
 
2477
         *    4) establishing the total number tasks for this frame */
 
2478
static void procs_hlp (proc_t *this) {
 
2479
#ifdef OFF_HST_HASH
 
2480
   static unsigned maxt_sav = 0;        // prior frame's max tasks
 
2481
#endif
 
2482
   TIC_t tics;
 
2483
   HST_t *h;
 
2484
 
 
2485
   if (!this) {
 
2486
      static struct timeval oldtimev;
 
2487
      struct timeval timev;
 
2488
      struct timezone timez;
 
2489
      float et;
 
2490
      void *v;
 
2491
 
 
2492
      gettimeofday(&timev, &timez);
 
2493
      et = (timev.tv_sec - oldtimev.tv_sec)
 
2494
         + (float)(timev.tv_usec - oldtimev.tv_usec) / 1000000.0;
 
2495
      oldtimev.tv_sec = timev.tv_sec;
 
2496
      oldtimev.tv_usec = timev.tv_usec;
 
2497
 
 
2498
      // if in Solaris mode, adjust our scaling for all cpus
 
2499
      Frame_etscale = 100.0f / ((float)Hertz * (float)et * (Rc.mode_irixps ? 1 : smp_num_cpus));
 
2500
#ifdef OFF_HST_HASH
 
2501
      maxt_sav = Frame_maxtask;
 
2502
#endif
 
2503
      Frame_maxtask = Frame_running = Frame_sleepin = Frame_stopped = Frame_zombied = 0;
 
2504
 
 
2505
      // prep for saving this frame's HST_t's (and reuse mem each time around)
 
2506
      v = PHist_sav;
 
2507
      PHist_sav = PHist_new;
 
2508
      PHist_new = v;
 
2509
#ifdef OFF_HST_HASH
 
2510
      // prep for binary search by sorting the last frame's HST_t's
 
2511
      qsort(PHist_sav, maxt_sav, sizeof(HST_t), (QFP_t)sort_HST_t);
 
2512
#else
 
2513
      v = PHash_sav;
 
2514
      PHash_sav = PHash_new;
 
2515
      PHash_new = v;
 
2516
      memcpy(PHash_new, HHash_nul, sizeof(HHash_nul));
 
2517
#endif
 
2518
      return;
 
2519
   }
 
2520
 
 
2521
   switch (this->state) {
 
2522
      case 'R':
 
2523
         Frame_running++;
 
2524
         break;
 
2525
      case 'S':
 
2526
      case 'D':
 
2527
         Frame_sleepin++;
 
2528
         break;
 
2529
      case 'T':
 
2530
         Frame_stopped++;
 
2531
         break;
 
2532
      case 'Z':
 
2533
         Frame_zombied++;
 
2534
         break;
 
2535
      default:                    // keep gcc happy
 
2536
         break;
 
2537
   }
 
2538
 
 
2539
   if (Frame_maxtask+1 >= HHist_siz) {
 
2540
      HHist_siz = HHist_siz * 5 / 4 + 100;
 
2541
      PHist_sav = alloc_r(PHist_sav, sizeof(HST_t) * HHist_siz);
 
2542
      PHist_new = alloc_r(PHist_new, sizeof(HST_t) * HHist_siz);
 
2543
   }
 
2544
 
 
2545
   /* calculate time in this process; the sum of user time (utime) and
 
2546
      system time (stime) -- but PLEASE dont waste time and effort on
 
2547
      calcs and saves that go unused, like the old top! */
 
2548
   PHist_new[Frame_maxtask].pid  = this->tid;
 
2549
   PHist_new[Frame_maxtask].tics = tics = (this->utime + this->stime);
 
2550
   // finally, save major/minor fault counts in case the deltas are displayable
 
2551
   PHist_new[Frame_maxtask].maj = this->maj_flt;
 
2552
   PHist_new[Frame_maxtask].min = this->min_flt;
 
2553
 
 
2554
#ifdef OFF_HST_HASH
 
2555
   // find matching entry from previous frame and make stuff elapsed
 
2556
   if ((h = hstbsrch(PHist_sav, maxt_sav - 1, this->tid))) {
 
2557
      tics -= h->tics;
 
2558
      this->maj_delta = this->maj_flt - h->maj;
 
2559
      this->min_delta = this->min_flt - h->min;
 
2560
   }
 
2561
#else
 
2562
   // hash & save for the next frame
 
2563
   hstput(Frame_maxtask);
 
2564
   // find matching entry from previous frame and make stuff elapsed
 
2565
   if ((h = hstget(this->tid))) {
 
2566
      tics -= h->tics;
 
2567
      this->maj_delta = this->maj_flt - h->maj;
 
2568
      this->min_delta = this->min_flt - h->min;
 
2569
   }
 
2570
#endif
 
2571
 
 
2572
   /* we're just saving elapsed tics, to be converted into %cpu if
 
2573
      this task wins it's displayable screen row lottery... */
 
2574
   this->pcpu = tics;
 
2575
 
 
2576
   // shout this to the world with the final call (or us the next time in)
 
2577
   Frame_maxtask++;
 
2578
} // end: procs_hlp
 
2579
 
 
2580
 
 
2581
        /*
 
2582
         * This guy's modeled on libproc's 'readproctab' function except
 
2583
         * we reuse and extend any prior proc_t's.  He's been customized
 
2584
         * for our specific needs and to avoid the use of <stdarg.h> */
 
2585
static void procs_refresh (void) {
 
2586
 #define n_used  Frame_maxtask                   // maintained by procs_hlp()
 
2587
   static proc_t **private_ppt;                  // our base proc_t ptr table
 
2588
   static int n_alloc = 0;                       // size of our private_ppt
 
2589
   static int n_saved = 0;                       // last window ppt size
 
2590
   proc_t *ptask;
 
2591
   PROCTAB* PT;
 
2592
   int i;
 
2593
   proc_t*(*read_something)(PROCTAB*, proc_t*);
 
2594
 
 
2595
   procs_hlp(NULL);                              // prep for a new frame
 
2596
   if (NULL == (PT = openproc(Frames_libflags, Monpids)))
 
2597
      error_exit(fmtmk(N_fmt(FAIL_openlib_fmt), strerror(errno)));
 
2598
   read_something = Thread_mode ? readeither : readproc;
 
2599
 
 
2600
   for (;;) {
 
2601
      if (n_used == n_alloc) {
 
2602
         n_alloc = 10 + ((n_alloc * 5) / 4);     // grow by over 25%
 
2603
         private_ppt = alloc_r(private_ppt, sizeof(proc_t*) * n_alloc);
 
2604
         // ensure NULL pointers for the additional memory just acquired
 
2605
         memset(private_ppt + n_used, 0, sizeof(proc_t*) * (n_alloc - n_used));
 
2606
      }
 
2607
      // on the way to n_alloc, the library will allocate the underlying
 
2608
      // proc_t storage whenever our private_ppt[] pointer is NULL...
 
2609
      if (!(ptask = read_something(PT, private_ppt[n_used]))) break;
 
2610
      procs_hlp((private_ppt[n_used] = ptask));  // tally this proc_t
 
2611
   }
 
2612
 
 
2613
   closeproc(PT);
 
2614
 
 
2615
   // lastly, refresh each window's proc pointers table...
 
2616
   if (n_saved == n_alloc)
 
2617
      for (i = 0; i < GROUPSMAX; i++)
 
2618
         memcpy(Winstk[i].ppt, private_ppt, sizeof(proc_t*) * n_used);
 
2619
   else {
 
2620
      n_saved = n_alloc;
 
2621
      for (i = 0; i < GROUPSMAX; i++) {
 
2622
         Winstk[i].ppt = alloc_r(Winstk[i].ppt, sizeof(proc_t*) * n_alloc);
 
2623
         memcpy(Winstk[i].ppt, private_ppt, sizeof(proc_t*) * n_used);
 
2624
      }
 
2625
   }
 
2626
 #undef n_used
 
2627
} // end: procs_refresh
 
2628
 
 
2629
 
 
2630
        /*
 
2631
         * This serves as our interface to the memory & cpu count (sysinfo)
 
2632
         * portion of libproc.  In support of those hotpluggable resources,
 
2633
         * the sampling frequencies are reduced so as to minimize overhead.
 
2634
         * We'll strive to verify the number of cpus every 5 minutes and the
 
2635
         * memory availability/usage every 3 seconds. */
 
2636
static void sysinfo_refresh (int forced) {
 
2637
   static time_t mem_secs, cpu_secs;
 
2638
   time_t cur_secs;
 
2639
 
 
2640
   if (forced)
 
2641
      mem_secs = cpu_secs = 0;
 
2642
   time(&cur_secs);
 
2643
 
 
2644
   /*** hotplug_acclimated ***/
 
2645
   if (3 <= cur_secs - mem_secs) {
 
2646
      meminfo();
 
2647
      mem_secs = cur_secs;
 
2648
   }
 
2649
#ifndef PRETEND8CPUS
 
2650
   /*** hotplug_acclimated ***/
 
2651
   if (300 <= cur_secs - cpu_secs) {
 
2652
      cpuinfo();
 
2653
      Cpu_faux_tot = smp_num_cpus;
 
2654
      cpu_secs = cur_secs;
 
2655
#ifndef NUMA_DISABLE
 
2656
      if (Libnuma_handle)
 
2657
         Numa_node_tot = Numa_max_node() + 1;
 
2658
#endif
 
2659
   }
 
2660
#endif
 
2661
} // end: sysinfo_refresh
 
2662
 
 
2663
/*######  Inspect Other Output  ##########################################*/
 
2664
 
 
2665
        /*
 
2666
         * HOWTO Extend the top 'inspect' functionality:
 
2667
         *
 
2668
         * To exploit the 'Y' interactive command, one must add entries to
 
2669
         * the top personal configuration file.  Such entries simply reflect
 
2670
         * a file to be read or command/pipeline to be executed whose results
 
2671
         * will then be displayed in a separate scrollable window.
 
2672
         *
 
2673
         * Entries beginning with a '#' character are ignored, regardless of
 
2674
         * content.  Otherwise they consist of the following 3 elements, each
 
2675
         * of which must be separated by a tab character (thus 2 '\t' total):
 
2676
         *     type:  literal 'file' or 'pipe'
 
2677
         *     name:  selection shown on the Inspect screen
 
2678
         *     fmts:  string representing a path or command
 
2679
         *
 
2680
         * The two types of Inspect entries are not interchangeable.
 
2681
         * Those designated 'file' will be accessed using fopen/fread and must
 
2682
         * reference a single file in the 'fmts' element.  Entries specifying
 
2683
         * 'pipe' will employ popen/fread, their 'fmts' element could contain
 
2684
         * many pipelined commands and, none can be interactive.
 
2685
         *
 
2686
         * Here are some examples of both types of inspection entries.
 
2687
         * The first entry will be ignored due to the initial '#' character.
 
2688
         * For clarity, the pseudo tab depictions (^I) are surrounded by an
 
2689
         * extra space but the actual tabs would not be.
 
2690
         *
 
2691
         *     # pipe ^I Sockets ^I lsof -n -P -i 2>&1
 
2692
         *     pipe ^I Open Files ^I lsof -P -p %d 2>&1
 
2693
         *     file ^I NUMA Info ^I /proc/%d/numa_maps
 
2694
         *     pipe ^I Log ^I tail -n100 /var/log/syslog | sort -Mr
 
2695
         *
 
2696
         * Caution:  If the output contains unprintable characters they will
 
2697
         * be displayed in either the ^I notation or hexidecimal <FF> form.
 
2698
         * This applies to tab characters as well.  So if one wants a more
 
2699
         * accurate display, any tabs should be expanded within the 'fmts'.
 
2700
         *
 
2701
         * The following example takes what could have been a 'file' entry
 
2702
         * but employs a 'pipe' instead so as to expand the tabs.
 
2703
         *
 
2704
         *     # next would have contained '\t' ...
 
2705
         *     # file ^I <your_name> ^I /proc/%d/status
 
2706
         *     # but this will eliminate embedded '\t' ...
 
2707
         *     pipe ^I <your_name> ^I cat /proc/%d/status | expand -
 
2708
         */
 
2709
 
 
2710
        /*
 
2711
         * Our driving table support, the basis for generalized inspection,
 
2712
         * built at startup (if at all) from rcfile or demo entries. */
 
2713
struct I_ent {
 
2714
   void (*func)(char *, int);     // a pointer to file/pipe/demo function
 
2715
   char *type;                    // the type of entry ('file' or 'pipe')
 
2716
   char *name;                    // the selection label for display
 
2717
   char *fmts;                    // format string to build path or command
 
2718
   int   farg;                    // 1 = '%d' in fmts, 0 = not (future use)
 
2719
   const char *caps;              // not really caps, show_special() delim's
 
2720
   char *fstr;                    // entry's current/active search string
 
2721
   int   flen;                    // above's strlen, without call overhead
 
2722
};
 
2723
struct I_struc {
 
2724
   int demo;                      // do NOT save table entries in rcfile
 
2725
   int total;                     // total I_ent table entries
 
2726
   char *raw;                     // all entries for 'W', incl '#' & blank
 
2727
   struct I_ent *tab;
 
2728
};
 
2729
static struct I_struc Inspect;
 
2730
 
 
2731
static char   **Insp_p;           // pointers to each line start
 
2732
static int      Insp_nl;          // total lines, total Insp_p entries
 
2733
static char    *Insp_buf;         // the results from insp_do_file/pipe
 
2734
static size_t   Insp_bufsz;       // allocated size of Insp_buf
 
2735
static size_t   Insp_bufrd;       // bytes actually in Insp_buf
 
2736
static struct I_ent *Insp_sel;    // currently selected Inspect entry
 
2737
 
 
2738
        // Our 'make status line' macro
 
2739
#define INSP_MKSL(big,txt) { int _sz = big ? Screen_cols : 80; \
 
2740
   putp(tg2(0, (Msg_row = 3))); \
 
2741
   PUTT("%s%.*s", Curwin->capclr_hdr, Screen_cols -1 \
 
2742
      , fmtmk("%-*.*s%s", _sz, _sz, txt, Cap_clr_eol)); \
 
2743
   putp(Caps_off); fflush(stdout); }
 
2744
 
 
2745
        // Our 'row length' macro, equivalent to a strlen() call
 
2746
#define INSP_RLEN(idx) (int)(Insp_p[idx +1] - Insp_p[idx] -1)
 
2747
 
 
2748
        // Our 'busy' (wait please) macro
 
2749
#define INSP_BUSY  { INSP_MKSL(0, N_txt(YINSP_workin_txt)); }
 
2750
 
 
2751
 
 
2752
        /*
 
2753
         * Establish the number of lines present in the Insp_buf glob plus
 
2754
         * build the all important row start array.  It is that array that
 
2755
         * others will rely on since we dare not try to use strlen() on what
 
2756
         * is potentially raw binary data.  Who knows what some user might
 
2757
         * name as a file or include in a pipeline (scary, ain't it?). */
 
2758
static void insp_cnt_nl (void) {
 
2759
   char *beg = Insp_buf;
 
2760
   char *cur = Insp_buf;
 
2761
   char *end = Insp_buf + Insp_bufrd + 1;
 
2762
 
 
2763
#ifdef INSP_SAVEBUF
 
2764
{
 
2765
   static int n = 1;
 
2766
   char fn[SMLBUFSIZ];
 
2767
   FILE *fd;
 
2768
   snprintf(fn, sizeof(fn), "%s.Insp_buf.%02d.txt", Myname, n++);
 
2769
   fd = fopen(fn, "w");
 
2770
   if (fd) {
 
2771
      fwrite(Insp_buf, 1, Insp_bufrd, fd);
 
2772
      fclose(fd);
 
2773
   }
 
2774
}
 
2775
#endif
 
2776
   Insp_p = alloc_c(sizeof(char*) * 2);
 
2777
 
 
2778
   for (Insp_nl = 0; beg < end; beg++) {
 
2779
      if (*beg == '\n') {
 
2780
         Insp_p[Insp_nl++] = cur;
 
2781
         // keep our array ahead of next potential need (plus the 2 above)
 
2782
         Insp_p = alloc_r(Insp_p, (sizeof(char*) * (Insp_nl +3)));
 
2783
         cur = beg +1;
 
2784
      }
 
2785
   }
 
2786
   Insp_p[0] = Insp_buf;
 
2787
   Insp_p[Insp_nl++] = cur;
 
2788
   Insp_p[Insp_nl] = end;
 
2789
   if ((end - cur) == 1)          // if there's a eof null delimiter,
 
2790
      --Insp_nl;                  // don't count it as a new line
 
2791
} // end: insp_cnt_nl
 
2792
 
 
2793
 
 
2794
#ifndef INSP_OFFDEMO
 
2795
        /*
 
2796
         * The pseudo output DEMO utility. */
 
2797
static void insp_do_demo (char *fmts, int pid) {
 
2798
   (void)fmts; (void)pid;
 
2799
   /* next will put us on a par with the real file/pipe read buffers
 
2800
    ( and also avoid a harmless, but evil sounding, valgrind warning ) */
 
2801
   Insp_bufsz = READMINSZ + strlen(N_txt(YINSP_dstory_txt));
 
2802
   Insp_buf   = alloc_c(Insp_bufsz);
 
2803
   Insp_bufrd = snprintf(Insp_buf, Insp_bufsz, "%s", N_txt(YINSP_dstory_txt));
 
2804
   insp_cnt_nl();
 
2805
} // end: insp_do_demo
 
2806
#endif
 
2807
 
 
2808
 
 
2809
        /*
 
2810
         * The generalized FILE utility. */
 
2811
static void insp_do_file (char *fmts, int pid) {
 
2812
   char buf[LRGBUFSIZ];
 
2813
   FILE *fp;
 
2814
   int rc;
 
2815
 
 
2816
   snprintf(buf, sizeof(buf), fmts, pid);
 
2817
   fp = fopen(buf, "r");
 
2818
   rc = readfile(fp, &Insp_buf, &Insp_bufsz, &Insp_bufrd);
 
2819
   if (fp) fclose(fp);
 
2820
   if (rc) Insp_bufrd = snprintf(Insp_buf, Insp_bufsz, "%s"
 
2821
      , fmtmk(N_fmt(YINSP_failed_fmt), strerror(errno)));
 
2822
   insp_cnt_nl();
 
2823
} // end: insp_do_file
 
2824
 
 
2825
 
 
2826
        /*
 
2827
         * The generalized PIPE utility. */
 
2828
static void insp_do_pipe (char *fmts, int pid) {
 
2829
   char buf[LRGBUFSIZ];
 
2830
   FILE *fp;
 
2831
   int rc;
 
2832
 
 
2833
   snprintf(buf, sizeof(buf), fmts, pid);
 
2834
   fp = popen(buf, "r");
 
2835
   rc = readfile(fp, &Insp_buf, &Insp_bufsz, &Insp_bufrd);
 
2836
   if (fp) pclose(fp);
 
2837
   if (rc) Insp_bufrd = snprintf(Insp_buf, Insp_bufsz, "%s"
 
2838
      , fmtmk(N_fmt(YINSP_failed_fmt), strerror(errno)));
 
2839
   insp_cnt_nl();
 
2840
} // end: insp_do_pipe
 
2841
 
 
2842
 
 
2843
        /*
 
2844
         * This guy is a *Helper* function serving the following two masters:
 
2845
         *   insp_find_str() - find the next Insp_sel->fstr match
 
2846
         *   insp_make_row() - highlight any Insp_sel->fstr matches in-view
 
2847
         * If Insp_sel->fstr is found in the designated row, he returns the
 
2848
         * offset from the start of the row, otherwise he returns a huge
 
2849
         * integer so traditional fencepost usage can be employed. */
 
2850
static inline int insp_find_ofs (int col, int row) {
 
2851
 #define begFS (int)(fnd - Insp_p[row])
 
2852
   char *p, *fnd = NULL;
 
2853
 
 
2854
   if (Insp_sel->fstr[0]) {
 
2855
      // skip this row, if there's no chance of a match
 
2856
      if (memchr(Insp_p[row], Insp_sel->fstr[0], INSP_RLEN(row))) {
 
2857
         for ( ; col < INSP_RLEN(row); col++) {
 
2858
            if (!*(p = Insp_p[row] + col))       // skip any empty strings
 
2859
               continue;
 
2860
            fnd = STRSTR(p, Insp_sel->fstr);     // with binary data, each
 
2861
            if (fnd)                             // row may have '\0'.  so
 
2862
               break;                            // our scans must be done
 
2863
            col += strlen(p);                    // as individual strings.
 
2864
         }
 
2865
         if (fnd && fnd < Insp_p[row + 1])       // and, we must watch out
 
2866
            return begFS;                        // for potential overrun!
 
2867
      }
 
2868
   }
 
2869
   return INT_MAX;
 
2870
 #undef begFS
 
2871
} // end: insp_find_ofs
 
2872
 
 
2873
 
 
2874
        /*
 
2875
         * This guy supports the inspect 'L' and '&' search provisions
 
2876
         * and returns the row and *optimal* column for viewing any match
 
2877
         * ( we'll always opt for left column justification since any )
 
2878
         * ( preceding ctrl chars appropriate an unpredictable amount ) */
 
2879
static void insp_find_str (int ch, int *col, int *row) {
 
2880
 #define reDUX (found) ? N_txt(WORD_another_txt) : ""
 
2881
   static int found;
 
2882
 
 
2883
   if ((ch == '&' || ch == 'n') && !Insp_sel->fstr[0]) {
 
2884
      show_msg(N_txt(FIND_no_next_txt));
 
2885
      return;
 
2886
   }
 
2887
   if (ch == 'L' || ch == '/') {
 
2888
      snprintf(Insp_sel->fstr, FNDBUFSIZ, "%s", ioline(N_txt(GET_find_str_txt)));
 
2889
      Insp_sel->flen = strlen(Insp_sel->fstr);
 
2890
      found = 0;
 
2891
   }
 
2892
   if (Insp_sel->fstr[0]) {
 
2893
      int xx, yy;
 
2894
 
 
2895
      INSP_BUSY;
 
2896
      for (xx = *col, yy = *row; yy < Insp_nl; ) {
 
2897
         xx = insp_find_ofs(xx, yy);
 
2898
         if (xx < INSP_RLEN(yy)) {
 
2899
            found = 1;
 
2900
            if (xx == *col &&  yy == *row) {     // matched where we were!
 
2901
               ++xx;                             // ( was the user maybe )
 
2902
               continue;                         // ( trying to fool us? )
 
2903
            }
 
2904
            *col = xx;
 
2905
            *row = yy;
 
2906
            return;
 
2907
         }
 
2908
         xx = 0;
 
2909
         ++yy;
 
2910
      }
 
2911
      show_msg(fmtmk(N_fmt(FIND_no_find_fmt), reDUX, Insp_sel->fstr));
 
2912
   }
 
2913
 #undef reDUX
 
2914
} // end: insp_find_str
 
2915
 
 
2916
 
 
2917
        /*
 
2918
         * This guy is a *Helper* function responsible for positioning a
 
2919
         * single row in the current 'X axis', then displaying the results.
 
2920
         * Along the way, he makes sure control characters and/or unprintable
 
2921
         * characters display in a less-like fashion:
 
2922
         *    '^A'    for control chars
 
2923
         *    '<BC>'  for other unprintable stuff
 
2924
         * Those will be highlighted with the current windows's capclr_msg,
 
2925
         * while visible search matches display with capclr_hdr for emphasis.
 
2926
         * ( we hide ugly plumbing in macros to concentrate on the algorithm ) */
 
2927
static inline void insp_make_row (int col, int row) {
 
2928
 #define maxSZ ( Screen_cols - (to + 1) )
 
2929
 #define capNO { if (hicap) { putp(Caps_off); hicap = 0; } }
 
2930
 #define mkFND { PUTT("%s%.*s%s", Curwin->capclr_hdr, maxSZ, Insp_sel->fstr, Caps_off); \
 
2931
    fr += Insp_sel->flen -1; to += Insp_sel->flen; hicap = 0; }
 
2932
#ifndef INSP_JUSTNOT
 
2933
 #define mkCTL { int x = maxSZ; const char *p = fmtmk("^%c", uch + '@'); \
 
2934
    PUTT("%s%.*s", (!hicap) ? Curwin->capclr_msg : "", x, p); to += 2; hicap = 1; }
 
2935
 #define mkUNP { int x = maxSZ; const char *p = fmtmk("<%02X>", uch); \
 
2936
    PUTT("%s%.*s", (!hicap) ? Curwin->capclr_msg : "", x, p); to += 4; hicap = 1; }
 
2937
#else
 
2938
 #define mkCTL { if ((to += 2) <= Screen_cols) \
 
2939
    PUTT("%s^%c", (!hicap) ? Curwin->capclr_msg : "", uch + '@'); hicap = 1; }
 
2940
 #define mkUNP { if ((to += 4) <= Screen_cols) \
 
2941
    PUTT("%s<%02X>", (!hicap) ? Curwin->capclr_msg : "", uch); hicap = 1; }
 
2942
#endif
 
2943
 #define mkSTD { capNO; if (++to <= Screen_cols) { static char _str[2]; \
 
2944
    _str[0] = uch; putp(_str); } }
 
2945
   char tline[SCREENMAX];
 
2946
   int fr, to, ofs;
 
2947
   int hicap = 0;
 
2948
 
 
2949
   capNO;
 
2950
   if (col < INSP_RLEN(row))
 
2951
      memcpy(tline, Insp_p[row] + col, sizeof(tline));
 
2952
   else tline[0] = '\n';
 
2953
 
 
2954
   for (fr = 0, to = 0, ofs = 0; to < Screen_cols -1; fr++) {
 
2955
      if (!ofs)
 
2956
         ofs = insp_find_ofs(col + fr, row);
 
2957
      if (col + fr < ofs) {
 
2958
         unsigned char uch = tline[fr];
 
2959
         if (uch == '\n')   break;     // a no show  (he,he)
 
2960
         if (uch > 126)     mkUNP      // show as: '<AB>'
 
2961
         else if (uch < 32) mkCTL      // show as:  '^C'
 
2962
         else               mkSTD      // a show off (he,he)
 
2963
      } else {              mkFND      // a big show (he,he)
 
2964
         ofs = 0;
 
2965
      }
 
2966
      if (col + fr >= INSP_RLEN(row)) break;
 
2967
   }
 
2968
   capNO;
 
2969
   putp(Cap_clr_eol);
 
2970
 
 
2971
 #undef maxSZ
 
2972
 #undef capNO
 
2973
 #undef mkFND
 
2974
 #undef mkCTL
 
2975
 #undef mkUNP
 
2976
 #undef mkSTD
 
2977
} // end: insp_make_row
 
2978
 
 
2979
 
 
2980
        /*
 
2981
         * This guy is an insp_view_choice() *Helper* function who displays
 
2982
         * a page worth of of the user's damages.  He also creates a status
 
2983
         * line based on maximum digits for the current selection's lines and
 
2984
         * hozizontal position (so it serves to inform, not distract, by
 
2985
         * otherwise being jumpy). */
 
2986
static inline void insp_show_pgs (int col, int row, int max) {
 
2987
   char buf[SMLBUFSIZ];
 
2988
   int r = snprintf(buf, sizeof(buf), "%d", Insp_nl);
 
2989
   int c = snprintf(buf, sizeof(buf), "%d", col +Screen_cols);
 
2990
   int l = row +1, ls = Insp_nl;;
 
2991
 
 
2992
   if (!Insp_bufrd)
 
2993
      l = ls = 0;
 
2994
   snprintf(buf, sizeof(buf), N_fmt(YINSP_status_fmt)
 
2995
      , Insp_sel->name
 
2996
      , r, l, r, ls
 
2997
      , c, col + 1, c, col + Screen_cols
 
2998
      , (unsigned long)Insp_bufrd);
 
2999
   INSP_MKSL(0, buf);
 
3000
 
 
3001
   for ( ; max && row < Insp_nl; row++) {
 
3002
      putp("\n");
 
3003
      insp_make_row(col, row);
 
3004
      --max;
 
3005
   }
 
3006
 
 
3007
   if (max)
 
3008
      putp(Cap_nl_clreos);
 
3009
} // end: insp_show_pgs
 
3010
 
 
3011
 
 
3012
        /*
 
3013
         * This guy is responsible for displaying the Insp_buf contents and
 
3014
         * managing all scrolling/locate requests until the user gives up. */
 
3015
static int insp_view_choice (proc_t *obj) {
 
3016
#ifdef INSP_SLIDE_1
 
3017
 #define hzAMT  1
 
3018
#else
 
3019
 #define hzAMT  8
 
3020
#endif
 
3021
 #define maxLN (Screen_rows - (Msg_row +1))
 
3022
 #define makHD(b1,b2,b3) { \
 
3023
    snprintf(b1, sizeof(b1), "%s", make_num(obj->tid,   5, 1, AUTOX_NO)); \
 
3024
    snprintf(b2, sizeof(b2), "%s", make_str(obj->cmd,   8, 1, AUTOX_NO)); \
 
3025
    snprintf(b3, sizeof(b3), "%s", make_str(obj->euser, 8, 1, AUTOX_NO)); }
 
3026
 #define makFS(dst) { if (Insp_sel->flen < 22) \
 
3027
       snprintf(dst, sizeof(dst), "%s", Insp_sel->fstr); \
 
3028
    else snprintf(dst, sizeof(dst), "%.19s...", Insp_sel->fstr); }
 
3029
   char buf[SMLBUFSIZ];
 
3030
   int key, curlin = 0, curcol = 0;
 
3031
 
 
3032
signify_that:
 
3033
   putp(Cap_clr_scr);
 
3034
   adj_geometry();
 
3035
 
 
3036
   for (;;) {
 
3037
      char pid[6], cmd[9], usr[9];
 
3038
 
 
3039
      if (curcol < 0) curcol = 0;
 
3040
      if (curlin >= Insp_nl) curlin = Insp_nl -1;
 
3041
      if (curlin < 0) curlin = 0;
 
3042
 
 
3043
      makFS(buf)
 
3044
      makHD(pid,cmd,usr)
 
3045
      putp(Cap_home);
 
3046
      show_special(1, fmtmk(N_unq(INSP_hdrview_fmt)
 
3047
         , pid, cmd, usr, (Insp_sel->fstr[0]) ? buf : " N/A "));   // nls_maybe
 
3048
      insp_show_pgs(curcol, curlin, maxLN);
 
3049
      fflush(stdout);
 
3050
      /* fflush(stdin) didn't do the trick, so we'll just dip a little deeper
 
3051
         lest repeated <Enter> keys produce immediate re-selection in caller */
 
3052
      tcflush(STDIN_FILENO, TCIFLUSH);
 
3053
 
 
3054
      if (Frames_resize) goto signify_that;
 
3055
      key = iokey(1);
 
3056
      if (key < 1) goto signify_that;
 
3057
 
 
3058
      switch (key) {
 
3059
         case kbd_ENTER:          // must force new iokey()
 
3060
            key = INT_MAX;        // fall through !
 
3061
         case kbd_ESC:
 
3062
         case 'q':
 
3063
            putp(Cap_clr_scr);
 
3064
            return key;
 
3065
         case kbd_LEFT:
 
3066
            curcol -= hzAMT;
 
3067
            break;
 
3068
         case kbd_RIGHT:
 
3069
            curcol += hzAMT;
 
3070
            break;
 
3071
         case kbd_UP:
 
3072
            --curlin;
 
3073
            break;
 
3074
         case kbd_DOWN:
 
3075
            ++curlin;
 
3076
            break;
 
3077
         case kbd_PGUP:
 
3078
         case 'b':
 
3079
            curlin -= maxLN -1;   // keep 1 line for reference
 
3080
            break;
 
3081
         case kbd_PGDN:
 
3082
         case kbd_SPACE:
 
3083
            curlin += maxLN -1;   // ditto
 
3084
            break;
 
3085
         case kbd_HOME:
 
3086
         case 'g':
 
3087
            curcol = curlin = 0;
 
3088
            break;
 
3089
         case kbd_END:
 
3090
         case 'G':
 
3091
            curcol = 0;
 
3092
            curlin = Insp_nl - maxLN;
 
3093
            break;
 
3094
         case 'L':
 
3095
         case '&':
 
3096
         case '/':
 
3097
         case 'n':
 
3098
            insp_find_str(key, &curcol, &curlin);
 
3099
            putp((Cursor_state = Cap_curs_hide));
 
3100
            break;
 
3101
         case '=':
 
3102
            snprintf(buf, sizeof(buf), "%s: %s", Insp_sel->type, Insp_sel->fmts);
 
3103
            INSP_MKSL(1, buf);    // show an extended SL
 
3104
            if (iokey(1) < 1)
 
3105
               goto signify_that;
 
3106
            break;
 
3107
         default:                 // keep gcc happy
 
3108
            break;
 
3109
      }
 
3110
   }
 
3111
 #undef hzAMT
 
3112
 #undef maxLN
 
3113
 #undef makHD
 
3114
 #undef makFS
 
3115
} // end: insp_view_choice
 
3116
 
 
3117
 
 
3118
        /*
 
3119
         * This is the main Inspect routine, responsible for:
 
3120
         *   1) validating the passed pid (required, but not always used)
 
3121
         *   2) presenting/establishing the target selection
 
3122
         *   3) arranging to fill Insp_buf (via the Inspect.tab[?].func)
 
3123
         *   4) invoking insp_view_choice for viewing/scrolling/searching
 
3124
         *   5) cleaning up the dynamically acquired memory afterwards */
 
3125
static void inspection_utility (int pid) {
 
3126
 #define mkSEL(dst) { for (i = 0; i < Inspect.total; i++) Inspect.tab[i].caps = "~1"; \
 
3127
      Inspect.tab[sel].caps = "~4"; dst[0] = '\0'; \
 
3128
      for (i = 0; i < Inspect.total; i++) { char _s[SMLBUFSIZ]; \
 
3129
         snprintf(_s, sizeof(_s), " %s %s", Inspect.tab[i].name, Inspect.tab[i].caps); \
 
3130
         strcat(dst, _s); } }
 
3131
   char sels[MEDBUFSIZ];
 
3132
   static int sel;
 
3133
   int i, key;
 
3134
   proc_t *p;
 
3135
 
 
3136
   for (i = 0, p = NULL; i < Frame_maxtask; i++)
 
3137
      if (pid == Curwin->ppt[i]->tid) {
 
3138
         p = Curwin->ppt[i];
 
3139
         break;
 
3140
      }
 
3141
   if (!p) {
 
3142
      show_msg(fmtmk(N_fmt(YINSP_pidbad_fmt), pid));
 
3143
      return;
 
3144
   }
 
3145
   // must re-hide cursor since the prompt for a pid made it huge
 
3146
   putp((Cursor_state = Cap_curs_hide));
 
3147
signify_that:
 
3148
   putp(Cap_clr_scr);
 
3149
   adj_geometry();
 
3150
 
 
3151
   key = INT_MAX;
 
3152
   do {
 
3153
      mkSEL(sels);
 
3154
      putp(Cap_home);
 
3155
      show_special(1, fmtmk(N_unq(INSP_hdrsels_fmt)
 
3156
         , pid, p->cmd, p->euser, sels));
 
3157
      INSP_MKSL(0, " ");
 
3158
 
 
3159
      if (Frames_resize) goto signify_that;
 
3160
      if (key == INT_MAX) key = iokey(1);
 
3161
      if (key < 1) goto signify_that;
 
3162
 
 
3163
      switch (key) {
 
3164
         case 'q':
 
3165
         case kbd_ESC:
 
3166
            break;
 
3167
         case kbd_END:
 
3168
            sel = 0;              // fall through !
 
3169
         case kbd_LEFT:
 
3170
            if (--sel < 0) sel = Inspect.total -1;
 
3171
            key = INT_MAX;
 
3172
            break;
 
3173
         case kbd_HOME:
 
3174
            sel = Inspect.total;  // fall through !
 
3175
         case kbd_RIGHT:
 
3176
            if (++sel >= Inspect.total) sel = 0;
 
3177
            key = INT_MAX;
 
3178
            break;
 
3179
         case kbd_ENTER:
 
3180
            INSP_BUSY;
 
3181
            Insp_sel = &Inspect.tab[sel];
 
3182
            Inspect.tab[sel].func(Inspect.tab[sel].fmts, pid);
 
3183
            key = insp_view_choice(p);
 
3184
            free(Insp_buf);
 
3185
            free(Insp_p);
 
3186
            break;
 
3187
         default:
 
3188
            goto signify_that;
 
3189
      }
 
3190
   } while (key != 'q' && key != kbd_ESC);
 
3191
 
 
3192
 #undef mkSEL
 
3193
} // end: inspection_utility
 
3194
#undef INSP_MKSL
 
3195
#undef INSP_RLEN
 
3196
#undef INSP_BUSY
 
3197
 
 
3198
/*######  Startup routines  ##############################################*/
 
3199
 
 
3200
        /*
 
3201
         * No matter what *they* say, we handle the really really BIG and
 
3202
         * IMPORTANT stuff upon which all those lessor functions depend! */
 
3203
static void before (char *me) {
 
3204
   struct sigaction sa;
 
3205
   proc_t p;
 
3206
   int i;
 
3207
 
 
3208
   atexit(close_stdout);
 
3209
 
 
3210
   // is /proc mounted?
 
3211
   look_up_our_self(&p);
 
3212
 
 
3213
   // setup our program name
 
3214
   Myname = strrchr(me, '/');
 
3215
   if (Myname) ++Myname; else Myname = me;
 
3216
 
 
3217
   // accommodate nls/gettext potential translations
 
3218
   initialize_nls();
 
3219
 
 
3220
   // establish cpu particulars
 
3221
#ifdef PRETEND8CPUS
 
3222
   smp_num_cpus = 8;
 
3223
#endif
 
3224
   Cpu_faux_tot = smp_num_cpus;
 
3225
   Cpu_States_fmts = N_unq(STATE_lin2x4_fmt);
 
3226
   if (linux_version_code > LINUX_VERSION(2, 5, 41))
 
3227
      Cpu_States_fmts = N_unq(STATE_lin2x5_fmt);
 
3228
   if (linux_version_code >= LINUX_VERSION(2, 6, 0))
 
3229
      Cpu_States_fmts = N_unq(STATE_lin2x6_fmt);
 
3230
   if (linux_version_code >= LINUX_VERSION(2, 6, 11))
 
3231
      Cpu_States_fmts = N_unq(STATE_lin2x7_fmt);
 
3232
 
 
3233
   // get virtual page stuff
 
3234
   Page_size = getpagesize();
 
3235
   i = Page_size;
 
3236
   while(i > 1024) { i >>= 1; Pg2K_shft++; }
 
3237
 
 
3238
#ifndef OFF_HST_HASH
 
3239
   // prep for HST_t's put/get hashing optimizations
 
3240
   for (i = 0; i < HHASH_SIZ; i++) HHash_nul[i] = -1;
 
3241
   memcpy(HHash_one, HHash_nul, sizeof(HHash_nul));
 
3242
   memcpy(HHash_two, HHash_nul, sizeof(HHash_nul));
 
3243
#endif
 
3244
 
 
3245
#ifndef NUMA_DISABLE
 
3246
#if defined(PRETEND_NUMA) || defined(PRETEND8CPUS)
 
3247
   Numa_node_tot = Numa_max_node() + 1;
 
3248
#else
 
3249
   Libnuma_handle = dlopen("libnuma.so", RTLD_LAZY);
 
3250
   if (Libnuma_handle) {
 
3251
      Numa_max_node = dlsym(Libnuma_handle, "numa_max_node");
 
3252
      Numa_node_of_cpu = dlsym(Libnuma_handle, "numa_node_of_cpu");
 
3253
      if (Numa_max_node && Numa_node_of_cpu)
 
3254
         Numa_node_tot = Numa_max_node() + 1;
 
3255
      else {
 
3256
         dlclose(Libnuma_handle);
 
3257
         Libnuma_handle = NULL;
 
3258
      }
 
3259
   }
 
3260
#endif
 
3261
#endif
 
3262
 
 
3263
#ifndef SIGRTMAX       // not available on hurd, maybe others too
 
3264
#define SIGRTMAX 32
 
3265
#endif
 
3266
   // lastly, establish a robust signals environment
 
3267
   sigemptyset(&sa.sa_mask);
 
3268
   // with user position perserved through SIGWINCH, we must avoid SA_RESTART
 
3269
   sa.sa_flags = 0;
 
3270
   for (i = SIGRTMAX; i; i--) {
 
3271
      switch (i) {
 
3272
         case SIGALRM: case SIGHUP:  case SIGINT:
 
3273
         case SIGPIPE: case SIGQUIT: case SIGTERM:
 
3274
         case SIGUSR1: case SIGUSR2:
 
3275
            sa.sa_handler = sig_endpgm;
 
3276
            break;
 
3277
         case SIGTSTP: case SIGTTIN: case SIGTTOU:
 
3278
            sa.sa_handler = sig_paused;
 
3279
            break;
 
3280
         case SIGCONT: case SIGWINCH:
 
3281
            sa.sa_handler = sig_resize;
 
3282
            break;
 
3283
         default:
 
3284
            sa.sa_handler = sig_abexit;
 
3285
            break;
 
3286
         case SIGCHLD: // we can't catch this
 
3287
            continue;  // when opening a pipe
 
3288
      }
 
3289
      sigaction(i, &sa, NULL);
 
3290
   }
 
3291
} // end: before
 
3292
 
 
3293
 
 
3294
        /*
 
3295
         * A configs_read *Helper* function responsible for converting
 
3296
         * a single window's old rc stuff into a new style rcfile entry */
 
3297
static int config_cvt (WIN_t *q) {
 
3298
   static struct {
 
3299
      int old, new;
 
3300
   } flags_tab[] = {
 
3301
    #define old_View_NOBOLD  0x000001
 
3302
    #define old_VISIBLE_tsk  0x000008
 
3303
    #define old_Qsrt_NORMAL  0x000010
 
3304
    #define old_Show_HICOLS  0x000200
 
3305
    #define old_Show_THREAD  0x010000
 
3306
      { old_View_NOBOLD, View_NOBOLD },
 
3307
      { old_VISIBLE_tsk, Show_TASKON },
 
3308
      { old_Qsrt_NORMAL, Qsrt_NORMAL },
 
3309
      { old_Show_HICOLS, Show_HICOLS },
 
3310
      { old_Show_THREAD, 0           }
 
3311
    #undef old_View_NOBOLD
 
3312
    #undef old_VISIBLE_tsk
 
3313
    #undef old_Qsrt_NORMAL
 
3314
    #undef old_Show_HICOLS
 
3315
    #undef old_Show_THREAD
 
3316
   };
 
3317
   static const char fields_src[] = CVT_FIELDS;
 
3318
#ifdef OOMEM_ENABLE
 
3319
   char fields_dst[PFLAGSSIZ], *p1, *p2;
 
3320
#else
 
3321
   char fields_dst[PFLAGSSIZ];
 
3322
#endif
 
3323
   int i, j, x;
 
3324
 
 
3325
   // first we'll touch up this window's winflags...
 
3326
   x = q->rc.winflags;
 
3327
   q->rc.winflags = 0;
 
3328
   for (i = 0; i < MAXTBL(flags_tab); i++) {
 
3329
      if (x & flags_tab[i].old) {
 
3330
         x &= ~flags_tab[i].old;
 
3331
         q->rc.winflags |= flags_tab[i].new;
 
3332
      }
 
3333
   }
 
3334
   q->rc.winflags |= x;
 
3335
   SETw(q, Show_JRNUMS);
 
3336
 
 
3337
   // now let's convert old top's more limited fields...
 
3338
   j = strlen(q->rc.fieldscur);
 
3339
   if (j > CVT_FLDMAX)
 
3340
      return 1;
 
3341
   strcpy(fields_dst, fields_src);
 
3342
#ifdef OOMEM_ENABLE
 
3343
   /* all other fields represent the 'on' state with a capitalized version
 
3344
      of a particular qwerty key.  for the 2 additional suse out-of-memory
 
3345
      fields it makes perfect sense to do the exact opposite, doesn't it?
 
3346
      in any case, we must turn them 'off' temporarily... */
 
3347
   if ((p1 = strchr(q->rc.fieldscur, '[')))  *p1 = '{';
 
3348
   if ((p2 = strchr(q->rc.fieldscur, '\\'))) *p2 = '|';
 
3349
#endif
 
3350
   for (i = 0; i < j; i++) {
 
3351
      int c = q->rc.fieldscur[i];
 
3352
      x = tolower(c) - 'a';
 
3353
      if (x < 0 || x >= CVT_FLDMAX)
 
3354
         return 1;
 
3355
      fields_dst[i] = fields_src[x];
 
3356
      if (isupper(c))
 
3357
         FLDon(fields_dst[i]);
 
3358
   }
 
3359
#ifdef OOMEM_ENABLE
 
3360
   // if we turned any suse only fields off, turn 'em back on OUR way...
 
3361
   if (p1) FLDon(fields_dst[p1 - q->rc.fieldscur]);
 
3362
   if (p2) FLDon(fields_dst[p2 - q->rc.fieldscur]);
 
3363
#endif
 
3364
   strcpy(q->rc.fieldscur, fields_dst);
 
3365
 
 
3366
   // lastly, we must adjust the old sort field enum...
 
3367
   x = q->rc.sortindx;
 
3368
   q->rc.sortindx = fields_src[x] - FLD_OFFSET;
 
3369
 
 
3370
   Rc_questions = 1;
 
3371
   return 0;
 
3372
} // end: config_cvt
 
3373
 
 
3374
 
 
3375
        /*
 
3376
         * Build the local RC file name then try to read both of 'em.
 
3377
         * 'SYS_RCFILESPEC' contains two lines consisting of the secure
 
3378
         *   mode switch and an update interval.  It's presence limits what
 
3379
         *   ordinary users are allowed to do.
 
3380
         * 'Rc_name' contains multiple lines - 3 global + 3 per window.
 
3381
         *   line 1  : an eyecatcher and creating program/alias name
 
3382
         *   line 2  : an id, Mode_altcsr, Mode_irixps, Delay_time, Curwin.
 
3383
         *   For each of the 4 windows:
 
3384
         *     line a: contains w->winname, fieldscur
 
3385
         *     line b: contains w->winflags, sortindx, maxtasks
 
3386
         *     line c: contains w->summclr, msgsclr, headclr, taskclr
 
3387
         *   line 15 : Fixed_widest, Summ_mscale, Task_mscale, Zero_suppress */
 
3388
static void configs_read (void) {
 
3389
   float tmp_delay = DEF_DELAY;
 
3390
   char fbuf[LRGBUFSIZ];
 
3391
   const char *p;
 
3392
   FILE *fp;
 
3393
   int i;
 
3394
 
 
3395
   p = getenv("HOME");
 
3396
   snprintf(Rc_name, sizeof(Rc_name), "%s/.%src", (p && *p) ? p : ".", Myname);
 
3397
 
 
3398
   fp = fopen(SYS_RCFILESPEC, "r");
 
3399
   if (fp) {
 
3400
      fbuf[0] = '\0';
 
3401
      fgets(fbuf, sizeof(fbuf), fp);             // sys rc file, line 1
 
3402
      if (strchr(fbuf, 's')) Secure_mode = 1;
 
3403
      fbuf[0] = '\0';
 
3404
      fgets(fbuf, sizeof(fbuf), fp);             // sys rc file, line 2
 
3405
      sscanf(fbuf, "%f", &Rc.delay_time);
 
3406
      fclose(fp);
 
3407
   }
 
3408
 
 
3409
   fp = fopen(Rc_name, "r");
 
3410
   if (fp) {
 
3411
      int tmp_whole, tmp_fract;
 
3412
      fbuf[0] = '\0';
 
3413
      fgets(fbuf, sizeof(fbuf), fp);             // ignore eyecatcher
 
3414
      if (6 != fscanf(fp
 
3415
         , "Id:%c, Mode_altscr=%d, Mode_irixps=%d, Delay_time=%d.%d, Curwin=%d\n"
 
3416
         , &Rc.id, &Rc.mode_altscr, &Rc.mode_irixps, &tmp_whole, &tmp_fract, &i)) {
 
3417
            p = fmtmk(N_fmt(RC_bad_files_fmt), Rc_name);
 
3418
            Rc_questions = -1;
 
3419
            goto try_inspect_entries;            // maybe a faulty 'inspect' echo
 
3420
      }
 
3421
      // you saw that, right?  (fscanf stickin' it to 'i')
 
3422
      Curwin = &Winstk[i];
 
3423
      // this may be ugly, but it keeps us locale independent...
 
3424
      tmp_delay = (float)tmp_whole + (float)tmp_fract / 1000;
 
3425
 
 
3426
      for (i = 0 ; i < GROUPSMAX; i++) {
 
3427
         int x;
 
3428
         WIN_t *w = &Winstk[i];
 
3429
         p = fmtmk(N_fmt(RC_bad_entry_fmt), i+1, Rc_name);
 
3430
 
 
3431
         // note: "fieldscur=%__s" on next line should equal PFLAGSSIZ !
 
3432
         if (2 != fscanf(fp, "%3s\tfieldscur=%64s\n"
 
3433
            , w->rc.winname, w->rc.fieldscur))
 
3434
               goto default_or_error;
 
3435
#if PFLAGSSIZ > 64
 
3436
 // too bad fscanf is not as flexible with his format string as snprintf
 
3437
 # error Hey, fix the above fscanf 'PFLAGSSIZ' dependency !
 
3438
#endif
 
3439
         if (3 != fscanf(fp, "\twinflags=%d, sortindx=%d, maxtasks=%d\n"
 
3440
            , &w->rc.winflags, &w->rc.sortindx, &w->rc.maxtasks))
 
3441
               goto default_or_error;
 
3442
         if (4 != fscanf(fp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d\n"
 
3443
            , &w->rc.summclr, &w->rc.msgsclr
 
3444
            , &w->rc.headclr, &w->rc.taskclr))
 
3445
               goto default_or_error;
 
3446
 
 
3447
         switch (Rc.id) {
 
3448
            case 'f':                  // 3.3.0 thru 3.3.3 (procps-ng)
 
3449
               SETw(w, Show_JRNUMS);   //    fall through !
 
3450
            case 'g':                  // current RCF_VERSION_ID
 
3451
            default:                   // and future versions?
 
3452
               if (strlen(w->rc.fieldscur) != sizeof(DEF_FIELDS) - 1)
 
3453
                  goto default_or_error;
 
3454
               for (x = 0; x < P_MAXPFLGS; ++x)
 
3455
                  if (P_MAXPFLGS <= FLDget(w, x))
 
3456
                     goto default_or_error;
 
3457
               break;
 
3458
            case 'a':                  // 3.2.8 (former procps)
 
3459
               if (config_cvt(w))
 
3460
                  goto default_or_error;
 
3461
               break;
 
3462
         }
 
3463
#ifndef USE_X_COLHDR
 
3464
         OFFw(w, NOHIFND_xxx | NOHISEL_xxx);
 
3465
#endif
 
3466
      } // end: for (GROUPSMAX)
 
3467
 
 
3468
      // any new addition(s) last, for older rcfiles compatibility...
 
3469
      fscanf(fp, "Fixed_widest=%d, Summ_mscale=%d, Task_mscale=%d, Zero_suppress=%d\n"
 
3470
         , &Rc.fixed_widest, &Rc.summ_mscale, &Rc.task_mscale, &Rc.zero_suppress);
 
3471
 
 
3472
try_inspect_entries:
 
3473
      // we'll start off Inspect stuff with 1 'potential' blank line
 
3474
      // ( only realized if we end up with Inspect.total > 0 )
 
3475
      for (i = 0, Inspect.raw = alloc_s("\n");;) {
 
3476
       #define iT(element) Inspect.tab[i].element
 
3477
         size_t lraw = strlen(Inspect.raw) +1;
 
3478
         char *s;
 
3479
 
 
3480
         if (!fgets(fbuf, sizeof(fbuf), fp)) break;
 
3481
         lraw += strlen(fbuf) +1;
 
3482
         Inspect.raw = alloc_r(Inspect.raw, lraw);
 
3483
         strcat(Inspect.raw, fbuf);
 
3484
 
 
3485
         if (fbuf[0] == '#' || fbuf[0] == '\n') continue;
 
3486
         Inspect.tab = alloc_r(Inspect.tab, sizeof(struct I_ent) * (i + 1));
 
3487
         p = fmtmk(N_fmt(YINSP_rcfile_fmt), i +1);
 
3488
 
 
3489
         if (!(s = strtok(fbuf, "\t\n"))) { Rc_questions = 1; continue; }
 
3490
         iT(type) = alloc_s(s);
 
3491
         if (!(s = strtok(NULL, "\t\n"))) { Rc_questions = 1; continue; }
 
3492
         iT(name) = alloc_s(s);
 
3493
         if (!(s = strtok(NULL, "\t\n"))) { Rc_questions = 1; continue; }
 
3494
         iT(fmts) = alloc_s(s);
 
3495
 
 
3496
         switch (toupper(fbuf[0])) {
 
3497
            case 'F':
 
3498
               iT(func) = insp_do_file;
 
3499
               break;
 
3500
            case 'P':
 
3501
               iT(func) = insp_do_pipe;
 
3502
               break;
 
3503
            default:
 
3504
               Rc_questions = 1;
 
3505
               continue;
 
3506
         }
 
3507
 
 
3508
         iT(farg) = (strstr(iT(fmts), "%d")) ? 1 : 0;
 
3509
         iT(fstr) = alloc_c(FNDBUFSIZ);
 
3510
         iT(flen) = 0;
 
3511
 
 
3512
         if (Rc_questions < 0) Rc_questions = 1;
 
3513
         ++i;
 
3514
       #undef iT
 
3515
      } // end: for ('inspect' entries)
 
3516
 
 
3517
      Inspect.total = i;
 
3518
#ifndef INSP_OFFDEMO
 
3519
      if (!Inspect.total) {
 
3520
       #define mkS(n) N_txt(YINSP_demo ## n ## _txt)
 
3521
         const char *sels[] = { mkS(01), mkS(02), mkS(03) };
 
3522
         Inspect.total = Inspect.demo = MAXTBL(sels);
 
3523
         Inspect.tab = alloc_c(sizeof(struct I_ent) * Inspect.total);
 
3524
         for (i = 0; i < Inspect.total; i++) {
 
3525
            Inspect.tab[i].type = alloc_s(N_txt(YINSP_deqtyp_txt));
 
3526
            Inspect.tab[i].name = alloc_s(sels[i]);
 
3527
            Inspect.tab[i].func = insp_do_demo;
 
3528
            Inspect.tab[i].fmts = alloc_s(N_txt(YINSP_deqfmt_txt));
 
3529
            Inspect.tab[i].fstr = alloc_c(FNDBUFSIZ);
 
3530
         }
 
3531
       #undef mkS
 
3532
      }
 
3533
#endif
 
3534
      if (Rc_questions < 0) {
 
3535
         p = fmtmk(N_fmt(RC_bad_files_fmt), Rc_name);
 
3536
         goto default_or_error;
 
3537
      }
 
3538
      fclose(fp);
 
3539
   } // end: if (fp)
 
3540
 
 
3541
   // lastly, establish the true runtime secure mode and delay time
 
3542
   if (!getuid()) Secure_mode = 0;
 
3543
   if (!Secure_mode) Rc.delay_time = tmp_delay;
 
3544
   return;
 
3545
 
 
3546
default_or_error:
 
3547
#ifdef RCFILE_NOERR
 
3548
{  RCF_t rcdef = DEF_RCFILE;
 
3549
   fclose(fp);
 
3550
   Rc = rcdef;
 
3551
   for (i = 0 ; i < GROUPSMAX; i++)
 
3552
      Winstk[i].rc  = Rc.win[i];
 
3553
   Rc_questions = 1;
 
3554
}
 
3555
#else
 
3556
   error_exit(p);
 
3557
#endif
 
3558
} // end: configs_read
 
3559
 
 
3560
 
 
3561
        /*
 
3562
         * Parse command line arguments.
 
3563
         * Note: it's assumed that the rc file(s) have already been read
 
3564
         *       and our job is to see if any of those options are to be
 
3565
         *       overridden -- we'll force some on and negate others in our
 
3566
         *       best effort to honor the loser's (oops, user's) wishes... */
 
3567
static void parse_args (char **args) {
 
3568
   /* differences between us and the former top:
 
3569
      -C (separate CPU states for SMP) is left to an rcfile
 
3570
      -u (user monitoring) added to compliment interactive 'u'
 
3571
      -p (pid monitoring) allows a comma delimited list
 
3572
      -q (zero delay) eliminated as redundant, incomplete and inappropriate
 
3573
            use: "nice -n-10 top -d0" to achieve what was only claimed
 
3574
      .  most switches act as toggles (not 'on' sw) for more user flexibility
 
3575
      .  no deprecated/illegal use of 'breakargv:' with goto
 
3576
      .  bunched args are actually handled properly and none are ignored
 
3577
      .  we tolerate NO whitespace and NO switches -- maybe too tolerant? */
 
3578
   static const char numbs_str[] = "+,-.0123456789";
 
3579
   float tmp_delay = MAXFLOAT;
 
3580
   char *p;
 
3581
   int i;
 
3582
 
 
3583
   while (*args) {
 
3584
      const char *cp = *(args++);
 
3585
 
 
3586
      while (*cp) {
 
3587
         char ch;
 
3588
         switch ((ch = *cp)) {
 
3589
            case '\0':
 
3590
               break;
 
3591
            case '-':
 
3592
               if (cp[1]) ++cp;
 
3593
               else if (*args) cp = *args++;
 
3594
               if (strspn(cp, numbs_str))
 
3595
                  error_exit(fmtmk(N_fmt(WRONG_switch_fmt)
 
3596
                     , cp, Myname, N_txt(USAGE_abbrev_txt)));
 
3597
               continue;
 
3598
            case 'b':
 
3599
               Batch = 1;
 
3600
               break;
 
3601
            case 'c':
 
3602
               TOGw(Curwin, Show_CMDLIN);
 
3603
               break;
 
3604
            case 'd':
 
3605
               if (cp[1]) ++cp;
 
3606
               else if (*args) cp = *args++;
 
3607
               else error_exit(fmtmk(N_fmt(MISSING_args_fmt), ch));
 
3608
                  /* a negative delay will be dealt with shortly... */
 
3609
               if (1 != sscanf(cp, "%f", &tmp_delay))
 
3610
                  error_exit(fmtmk(N_fmt(BAD_delayint_fmt), cp));
 
3611
               break;
 
3612
            case 'H':
 
3613
               Thread_mode = 1;
 
3614
               break;
 
3615
            case 'h':
 
3616
            case 'v':
 
3617
               puts(fmtmk(N_fmt(HELP_cmdline_fmt)
 
3618
                  , procps_version, Myname, N_txt(USAGE_abbrev_txt)));
 
3619
               bye_bye(NULL);
 
3620
            case 'i':
 
3621
               TOGw(Curwin, Show_IDLEPS);
 
3622
               Curwin->rc.maxtasks = 0;
 
3623
               break;
 
3624
            case 'n':
 
3625
               if (cp[1]) cp++;
 
3626
               else if (*args) cp = *args++;
 
3627
               else error_exit(fmtmk(N_fmt(MISSING_args_fmt), ch));
 
3628
               if (1 != sscanf(cp, "%d", &Loops) || 1 > Loops)
 
3629
                  error_exit(fmtmk(N_fmt(BAD_niterate_fmt), cp));
 
3630
               break;
 
3631
            case 'o':
 
3632
               if (cp[1]) cp++;
 
3633
               else if (*args) cp = *args++;
 
3634
               else error_exit(fmtmk(N_fmt(MISSING_args_fmt), ch));
 
3635
               if (*cp == '+') { SETw(Curwin, Qsrt_NORMAL); ++cp; }
 
3636
               else if (*cp == '-') { OFFw(Curwin, Qsrt_NORMAL); ++cp; }
 
3637
               for (i = 0; i < P_MAXPFLGS; i++)
 
3638
                  if (!STRCMP(cp, N_col(i))) break;
 
3639
               if (i == P_MAXPFLGS)
 
3640
                  error_exit(fmtmk(N_fmt(XTRA_badflds_fmt), cp));
 
3641
               OFFw(Curwin, Show_FOREST);
 
3642
               Curwin->rc.sortindx = i;
 
3643
               cp += strlen(cp);
 
3644
               break;
 
3645
            case 'O':
 
3646
               for (i = 0; i < P_MAXPFLGS; i++)
 
3647
                  puts(N_col(i));
 
3648
               bye_bye(NULL);
 
3649
            case 'p':
 
3650
               if (Curwin->usrseltyp) error_exit(N_txt(SELECT_clash_txt));
 
3651
               do { int pid;
 
3652
                  if (cp[1]) cp++;
 
3653
                  else if (*args) cp = *args++;
 
3654
                  else error_exit(fmtmk(N_fmt(MISSING_args_fmt), ch));
 
3655
                  if (Monpidsidx >= MONPIDMAX)
 
3656
                     error_exit(fmtmk(N_fmt(LIMIT_exceed_fmt), MONPIDMAX));
 
3657
                  if (1 != sscanf(cp, "%d", &pid) || 0 > pid)
 
3658
                     error_exit(fmtmk(N_fmt(BAD_mon_pids_fmt), cp));
 
3659
                  if (!pid) pid = getpid();
 
3660
                  for (i = 0; i < Monpidsidx; i++)
 
3661
                     if (Monpids[i] == pid) goto next_pid;
 
3662
                  Monpids[Monpidsidx++] = pid;
 
3663
               next_pid:
 
3664
                  if (!(p = strchr(cp, ','))) break;
 
3665
                  cp = p;
 
3666
               } while (*cp);
 
3667
               break;
 
3668
            case 's':
 
3669
               Secure_mode = 1;
 
3670
               break;
 
3671
            case 'S':
 
3672
               TOGw(Curwin, Show_CTIMES);
 
3673
               break;
 
3674
            case 'u':
 
3675
            case 'U':
 
3676
            {  const char *errmsg;
 
3677
               if (Monpidsidx || Curwin->usrseltyp) error_exit(N_txt(SELECT_clash_txt));
 
3678
               if (cp[1]) cp++;
 
3679
               else if (*args) cp = *args++;
 
3680
               else error_exit(fmtmk(N_fmt(MISSING_args_fmt), ch));
 
3681
               if ((errmsg = user_certify(Curwin, cp, ch))) error_exit(errmsg);
 
3682
               cp += strlen(cp);
 
3683
               break;
 
3684
            }
 
3685
            case 'w':
 
3686
            {  const char *pn = NULL;
 
3687
               int ai = 0, ci = 0;
 
3688
               Width_mode = -1;
 
3689
               if (cp[1]) pn = &cp[1];
 
3690
               else if (*args) { pn = *args; ai = 1; }
 
3691
               if (pn && !(ci = strspn(pn, "0123456789"))) { ai = 0; pn = NULL; }
 
3692
               if (pn && (1 != sscanf(pn, "%d", &Width_mode)
 
3693
               || Width_mode < W_MIN_COL))
 
3694
                  error_exit(fmtmk(N_fmt(BAD_widtharg_fmt), pn, W_MIN_COL-1));
 
3695
               cp++;
 
3696
               args += ai;
 
3697
               if (pn) cp = pn + ci;
 
3698
               continue;
 
3699
            }
 
3700
            default :
 
3701
               error_exit(fmtmk(N_fmt(UNKNOWN_opts_fmt)
 
3702
                  , *cp, Myname, N_txt(USAGE_abbrev_txt)));
 
3703
 
 
3704
         } // end: switch (*cp)
 
3705
 
 
3706
         // advance cp and jump over any numerical args used above
 
3707
         if (*cp) cp += strspn(&cp[1], numbs_str) + 1;
 
3708
 
 
3709
      } // end: while (*cp)
 
3710
   } // end: while (*args)