Logo Search packages:      
Sourcecode: lfc version File versions  Download package

gop_outp.c

// $Id: gop_outp.c,v 1.4 2007/03/20 07:53:24 grodid Exp $

/*
 * This file is really gross, and I know it. I looked into several
 * alternate ways to deal with the mess, and they were all ugly.
 *
 * FreeBSD has a fancy hack using offsets into a struct -- that
 * saves code but it is _really_ gross. See the PO macro below.
 *
 * We could have a second column width for wide output format.
 * For example, Digital prints the real-time signals.
 */


/*
 * Data table idea:
 *
 * table 1 maps aix to specifier
 * table 2 maps shortsort to specifier
 * table 3 maps macro to specifiers
 * table 4 maps specifier to title,datatype,offset,vendor,helptext
 * table 5 maps datatype to justification,width,widewidth,sorting,printing
 *
 * Here, "datatype" could be user,uid,u16,pages,deltaT,signals,tty,longtty...
 * It must be enough to determine printing and sorting.
 *
 * After the tables, increase width as needed to fit the header.
 *
 * Table 5 could go in a file with the output functions.
 */
 
#include <ctype.h>
#include <fcntl.h>
#include <grp.h>
#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>

//#include "gop_readf.h"
#include "gop_synfo.h"
#include "gop_chan.h"
#include "gop_clic.h"
#include "gop_devnm.h"
#include "gop_escp.h"
#include "gop_comm.h"

/* TODO:
 * Stop assuming system time is local time.
 */

#define COLWID 240 /* satisfy snprintf, which is faster than sprintf */

static unsigned max_rightward = 0x12345678; /* space for RIGHT stuff */
static unsigned max_leftward = 0x12345678; /* space for LEFT stuff */



static int wide_signals;  /* true if we have room */

static unsigned long seconds_since_1970;
static unsigned long time_of_boot;
static unsigned long page_shift;


/*************************************************************************/
/************ Lots of sort functions, starting with the NOP **************/

/* GG */
static int sr_nop(const dpmfile_t* a, const dpmfile_t* b){
  (void)a;(void)b; /* shut up gcc */
  return 0;
}

#define CMP_STR(NAME) \
static int sr_ ## NAME(const dpmfile_t* P, const dpmfile_t* Q) { \
    return strcmp(P->NAME, Q->NAME); \
}

#define CMP_INT(NAME) \
static int sr_ ## NAME (const dpmfile_t* P, const dpmfile_t* Q) { \
    if (P->NAME < Q->NAME) return -1; \
    if (P->NAME > Q->NAME) return  1; \
    return 0; \
}

/* fast version, for values which either:
 * a. differ by no more than 0x7fffffff
 * b. only need to be grouped same w/ same
 */
#define CMP_SMALL(NAME) \
static int sr_ ## NAME (const dpmfile_t* P, const dpmfile_t* Q) { \
    return (int)(P->NAME) - (int)(Q->NAME); \
}



/***************************************************************************/
/************ Lots of format functions, starting with the NOP **************/

// so popular it can't be "static"
int pr_nop(char *restrict const outbuf, const dpmfile_t *restrict const pp){
  (void)pp;
  return snprintf(outbuf, COLWID, "%c", '-');
}


/********* Unix 98 ************/

/***

Only comm and args are allowed to contain blank characters; all others are
not. Any implementation-dependent variables will be specified in the system
documentation along with the default header and indicating if the field
may contain blank characters.

Some headers do not have a standardized specifier!

%CPU  pcpu  The % of cpu time used recently, with unspecified "recently".
ADDR        The address of the process.
C           Processor utilisation for scheduling.
CMD         The command name, or everything with -f.
COMMAND     args  Command + args. May chop as desired. May use either version.
COMMAND     comm  argv[0]
ELAPSED     etime Elapsed time since the process was started. [[dd-]hh:]mm:ss
F           Flags (octal and additive)
GROUP group Effective group ID, prefer text over decimal.
NI    nice  Decimal system scheduling priority, see nice(1).
PGID  pgid  The decimal value of the process group ID.
PID   pid   Decimal PID.
PPID  ppid  Decimal PID.
PRI         Priority. Higher numbers mean lower priority.
RGROUP      rgroup      Real group ID, prefer text over decimal.
RUSER ruser Real user ID, prefer text over decimal.
S           The state of the process.
STIME       Starting time of the process.
SZ          The size in blocks of the core image of the process.
TIME  time  Cumulative CPU time. [dd-]hh:mm:ss
TT    tty   Name of tty in format used by who(1).
TTY         The controlling terminal for the process.
UID         UID, or name when -f
USER  user  Effective user ID, prefer text over decimal.
VSZ   vsz   Virtual memory size in decimal kB.
WCHAN       Where waiting/sleeping or blank if running.

The nice value is used to compute the priority.

For some undefined ones, Digital does:

F       flag    Process flags -- but in hex!
PRI     pri     Process priority
S       state   Symbolic process status
TTY     tt,tty,tname,longtname  -- all do "ttyp1", "console", "??"
UID     uid     Process user ID (effective UID)
WCHAN   wchan   Address of event on which a

For some undefined ones, Sun does:

ADDR  addr  memory address of the process
C     c     Processor utilization  for  scheduling  (obsolete).
CMD
F     f
S     s     state: OSRZT
STIME       start time, printed w/o blanks. If 24h old, months & days
SZ          size (in pages) of the swappable process's image in main memory
TTY
UID   uid
WCHAN wchan

For some undefined ones, SCO does:
ADDR  addr  Virtual address of the process' entry in the process table.
SZ          swappable size in kB of the virtual data and stack
STIME stime hms or md time format
***/



/* XPG4-UNIX, according to Digital:
The "args" and "command" specifiers show what was passed to the command.
Modifications to the arguments are not shown.
*/

/*
 * pp->cmd       short accounting name (comm & ucomm)
 * pp->cmdline   long name with args (args & command)
 * pp->environ   environment
 */


#if 0
/* elapsed wall clock time, [[dd-]hh:]mm:ss format (not same as "time") */
static int pr_etime(char *restrict const outbuf, const dpmfile_t *restrict const pp){
  unsigned long t;
  unsigned dd,hh,mm,ss;
  char *cp = outbuf;
  t = seconds_since_boot - (unsigned long)(pp->start_time / Hertz);
  ss = t%60;
  t /= 60;
  mm = t%60;
  t /= 60;
  hh = t%24;
  t /= 24;
  dd = t;
  cp +=(     dd      ?  snprintf(cp, COLWID, "%u-", dd)           :  0 );
  cp +=( (dd || hh)  ?  snprintf(cp, COLWID, "%02u:", hh)         :  0 );
  cp +=                 snprintf(cp, COLWID, "%02u:%02u", mm, ss)       ;
  return (int)(cp-outbuf);
}
#endif

#if 0
static int pr_nice(char *restrict const outbuf, const dpmfile_t *restrict const pp){
  if(pp->sched!=0 && pp->sched!=-1) return snprintf(outbuf, COLWID, "-");
  return snprintf(outbuf, COLWID, "%ld", pp->nice);
}
#endif

#if 0
/* "Processor utilisation for scheduling."  --- we use %cpu w/o fraction */
static int pr_c(char *restrict const outbuf, const dpmfile_t *restrict const pp){
  unsigned long long total_time;   /* jiffies used by this process */
  unsigned pcpu = 0;               /* scaled %cpu, 99 means 99% */
  unsigned long long seconds;      /* seconds of process life */
  total_time = pp->utime + pp->stime;
  if(include_dead_children) total_time += (pp->cutime + pp->cstime);
  seconds = seconds_since_boot - pp->start_time / Hertz;
  if(seconds) pcpu = (total_time * 100ULL / Hertz) / seconds;
  if (pcpu > 99U) pcpu = 99U;
  return snprintf(outbuf, COLWID, "%2u", pcpu);
}
/* normal %CPU in ##.# format. */
static int pr_pcpu(char *restrict const outbuf, const dpmfile_t *restrict const pp){
  unsigned long long total_time;   /* jiffies used by this process */
  unsigned pcpu = 0;               /* scaled %cpu, 999 means 99.9% */
  unsigned long long seconds;      /* seconds of process life */
  total_time = pp->utime + pp->stime;
  if(include_dead_children) total_time += (pp->cutime + pp->cstime);
  seconds = seconds_since_boot - pp->start_time / Hertz;
  if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
  if (pcpu > 999U) pcpu = 999U;
  return snprintf(outbuf, COLWID, "%2u.%u", pcpu/10U, pcpu%10U);
}
/* this is a "per-mill" format, like %cpu with no decimal point */
static int pr_cp(char *restrict const outbuf, const dpmfile_t *restrict const pp){
  unsigned long long total_time;   /* jiffies used by this process */
  unsigned pcpu = 0;               /* scaled %cpu, 999 means 99.9% */
  unsigned long long seconds;      /* seconds of process life */
  total_time = pp->utime + pp->stime;
  if(include_dead_children) total_time += (pp->cutime + pp->cstime);
  seconds = seconds_since_boot - pp->start_time / Hertz ;
  if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
  if (pcpu > 999U) pcpu = 999U;
  return snprintf(outbuf, COLWID, "%3u", pcpu);
}
#endif

#if 0
/* cumulative CPU time, [dd-]hh:mm:ss format (not same as "etime") */
static int pr_time(char *restrict const outbuf, const dpmfile_t *restrict const pp){
  unsigned long t;
  unsigned dd,hh,mm,ss;
  int c;
  t = (pp->utime + pp->stime) / Hertz;
  ss = t%60;
  t /= 60;
  mm = t%60;
  t /= 60;
  hh = t%24;
  t /= 24;
  dd = t;
  c  =( dd ? snprintf(outbuf, COLWID, "%u-", dd) : 0              );
  c +=( snprintf(outbuf+c, COLWID, "%02u:%02u:%02u", hh, mm, ss)    );
  return c;
}
#endif

/********* maybe standard (Unix98 only defines the header) **********/



// plus these: euid,ruid,egroup,rgroup (elsewhere in this file)

/*********** non-standard ***********/


#if 0
/* Unix98 specifies a STIME header for a column that shows the start
 * time of the process, but does not specify a format or format specifier.
 * From the general Unix98 rules, we know there must not be any spaces.
 * Most systems violate that rule, though the Solaris documentation
 * claims to print the column without spaces. (NOT!)
 *
 * So this isn't broken, but could be renamed to u98_std_stime,
 * as long as it still shows as STIME when using the -f option.
 */
static int pr_stime(char *restrict const outbuf, const dpmfile_t *restrict const pp){
  struct tm *proc_time;
  struct tm *our_time;
  time_t t;
  const char *fmt;
  int tm_year;
  int tm_yday;
  our_time = localtime(&seconds_since_1970);   /* not reentrant */
  tm_year = our_time->tm_year;
  tm_yday = our_time->tm_yday;
  t = time_of_boot + pp->start_time / Hertz;
  proc_time = localtime(&t); /* not reentrant, this corrupts our_time */
  fmt = "%H:%M";                                   /* 03:02 23:59 */
  if(tm_yday != proc_time->tm_yday) fmt = "%b%d";  /* Jun06 Aug27 */
  if(tm_year != proc_time->tm_year) fmt = "%Y";    /* 1991 2001 */
  return strftime(outbuf, 42, fmt, proc_time);
}
#endif



#define PR_C(NAME) \
static int pr_dpm ## NAME (char *restrict const outbuf, const dpmfile_t *restrict const pp){ \
  char *endp; \
  endp = outbuf; \
  if ( pp->/**/NAME )                                        \
    endp += escape_str(endp, pp->/**/NAME , OUTBUF_SIZE, OUTBUF_SIZE); \
  else \
    endp += escape_str(endp, "-" , OUTBUF_SIZE, OUTBUF_SIZE); \
  return endp - outbuf; \
}

#define OPR_C(NAME) \
static int pr_dpm ## NAME (char *restrict const outbuf, const dpmfile_t *restrict const pp){ \
  char *endp; \
  endp = outbuf; \
  if ( pp-> ## NAME ) \
    endp += escape_str(endp, pp-> ## NAME , OUTBUF_SIZE, OUTBUF_SIZE); \
  else \
    endp += escape_str(endp, "-" , OUTBUF_SIZE, OUTBUF_SIZE); \
  return endp - outbuf; \
}

//p  printf (" PR_C %s %x %x \n", "pr_dpm ## NAME", pp, pp-> ## NAME ); 

PR_C(cred)
PR_C(fileid)
PR_C(filecl)
PR_C(mode)
PR_C(laccs)
PR_C(modif)
PR_C(metam)

PR_C(credate)
PR_C(cretime)
PR_C(lastdate)
PR_C(lasttime)

PR_C(rtok)
PR_C(surl)
PR_C(turl)
PR_C(fsiz)
PR_C(pint)
PR_C(rtyp)

PR_C(lfn)
PR_C(owner)
PR_C(ownerg)
PR_C(pool)
PR_C(pfn)
PR_C(sfn)
PR_C(status)
PR_C(fstat)


/* dpmnbaccess */
static int pr_dpmnbaccess(char *restrict const outbuf, const dpmfile_t *restrict const pp){
    return snprintf(outbuf, COLWID, "%d", pp->nbaccess);
}

/* dpmcount */
static int pr_dpmcount(char *restrict const outbuf, const dpmfile_t *restrict const pp){
    return snprintf(outbuf, COLWID, "%d", pp->count);
}

/* dpmreqid */
static int pr_dpmreqid(char *restrict const outbuf, const dpmfile_t *restrict const pp){
    return snprintf(outbuf, COLWID, "%lu", pp->reqid);
}



/***************************************************************************/
/*************************** other stuff ***********************************/

/*
 * Old header specifications.
 *
 * short   Up  "  PID TTY STAT  TIME COMMAND"
 * long  l Pp  " FLAGS   UID   PID  PPID PRI  NI   SIZE   RSS WCHAN       STA TTY TIME COMMAND
 * user  u up  "USER       PID %CPU %MEM  SIZE   RSS TTY STAT START   TIME COMMAND
 * jobs  j gPp " PPID   PID  PGID   SID TTY TPGID  STAT   UID   TIME COMMAND
 * sig   s p   "  UID   PID SIGNAL   BLOCKED  IGNORED  CATCHED  STAT TTY   TIME COMMAND
 * vm    v r   "  PID TTY STAT  TIME  PAGEIN TSIZ DSIZ  RSS   LIM %MEM COMMAND
 * m     m r   "  PID TTY MAJFLT MINFLT   TRS   DRS  SIZE  SWAP   RSS  SHRD   LIB  DT COMMAND
 * regs  X p   "NR   PID    STACK      ESP      EIP TMOUT ALARM STAT TTY   TIME COMMAND
 */

/*
 * Unix98 requires that the heading for tty is TT, though XPG4, Digital,
 * and BSD use TTY. The Unix98 headers are:
 *              args,comm,etime,group,nice,pcpu,pgid
 *              pid,ppid,rgroup,ruser,time,tty,user,vsz
 *
 * BSD c:   "command" becomes accounting name ("comm" or "ucomm")
 * BSD n:   "user" becomes "uid" and "wchan" becomes "nwchan" (number)
 */

/* Justification control for flags field. */
#define USER      CF_USER   // left if text, right if numeric
#define LEFT      CF_LEFT
#define RIGHT     CF_RIGHT
#define UNLIMITED CF_UNLIMITED
#define WCHAN     CF_WCHAN  // left if text, right if numeric
#define SIGNAL    CF_SIGNAL // right in 9, or 16 if room
#define PIDMAX    CF_PIDMAX
#define TO        CF_PRINT_THREAD_ONLY
#define PO        CF_PRINT_PROCESS_ONLY
#define ET        CF_PRINT_EVERY_TIME
#define AN        CF_PRINT_AS_NEEDED // no idea

/* short names to save space */
#define MEM PROC_FILLMEM     /* read statm  */
#define ARG PROC_FILLARG     /* read cmdline (cleared if c option) */
#define COM PROC_FILLCOM     /* read cmdline (cleared if not -f option) */
#define ENV PROC_FILLENV     /* read environ */
#define USR PROC_FILLUSR     /* uid_t -> user names */
#define GRP PROC_FILLGRP     /* gid_t -> group names */
#define WCH PROC_FILLWCHAN   /* do WCHAN lookup */


/* TODO
 *      pull out annoying BSD aliases into another table (to macro table?)
 *      add sorting functions here (to unify names)
 */

/* temporary hack -- mark new stuff grabbed from Debian ps */
#define LNx LNX

/* Many of these are placeholders for unsupported options. */
static const format_struct format_array[] = {
/* code         header     print()      sort()    width need vendor flags  */
{"dpmcount",    "COUNT",   pr_dpmcount,  sr_nop,     5,  0,    XXX, AN|RIGHT},   /* GG, int */ // Beware of alphabetical order !!!!!!!!!!!!
{"dpmcred",     "CRED",    pr_dpmcred,   sr_nop,    16,  0,    XXX, AN|RIGHT},   /* GG, char */ // Beware of alphabetical order !!!!!!!!!!!!
{"dpmcredate",  "DCREDA",  pr_dpmcredate, sr_nop,    10,  0,    XXX, AN|RIGHT},   /* GG, char */ // Beware of alphabetical order !!!!!!!!!!!!
{"dpmcretime",  "DCRETI",  pr_dpmcretime, sr_nop,     8,  0,    XXX, AN|RIGHT},   /* GG, char */
{"dpmfilecl",   "FCL",     pr_dpmfilecl, sr_nop,      3,  0,    XXX, AN|RIGHT},   /* GG, char */
{"dpmfileid",   "FID",     pr_dpmfileid, sr_nop,     21,  0,    XXX, AN|RIGHT},   /* GG, char */
{"dpmfsiz",      "FSIZ",   pr_dpmfsiz,   sr_nop,     15,  0,    XXX, AN|RIGHT},   /* GG, char */
{"dpmfstat",     "FSTAT",  pr_dpmfstat,  sr_nop,     15,  0,    XXX, AN|LEFT},   /* GG, char */
{"dpmlaccs",    "LACCS",   pr_dpmlaccs,   sr_nop,    13,  0,    XXX, AN|RIGHT},   /* GG, char */
{"dpmlastdate", "DLASDA",  pr_dpmlastdate, sr_nop,   10,  0,    XXX, AN|RIGHT},   /* GG, char */
{"dpmlasttime", "DLASTI",  pr_dpmlasttime, sr_nop,    8,  0,    XXX, AN|RIGHT},   /* GG, char */
{"dpmlfn",      "DLFN",    pr_dpmlfn,   sr_nop,      64,  0,    XXX, AN|RIGHT},   /* GG, char */
{"dpmmetam",    "METAM",   pr_dpmmetam,   sr_nop,    13,  0,    XXX, AN|RIGHT},   /* GG, char */
{"dpmmode",     "MODE",    pr_dpmmode,   sr_nop,     10,  0,    XXX, AN|RIGHT},   /* GG, char */
{"dpmmodif",    "MODIF",   pr_dpmmodif,   sr_nop,    13,  0,    XXX, AN|RIGHT},   /* GG, char */
{"dpmnbaccess", "DNACC",   pr_dpmnbaccess,sr_nop,    4,  0,    XXX, AN|LEFT},   /* GG,  */
{"dpmowner",    "DOWN",    pr_dpmowner,   sr_nop,   10,  0,    XXX, AN|RIGHT},   /* GG, char */
{"dpmownerg",   "DGOWN",   pr_dpmownerg,  sr_nop,    8,  0,    XXX, AN|RIGHT},   /* GG, char */
{"dpmpfn",      "DPFN",    pr_dpmpfn,   sr_nop,     64,  0,    XXX, AN|RIGHT},   /* GG, char */
{"dpmpint",      "PINT",   pr_dpmpint,   sr_nop,     25,  0,    XXX, AN|RIGHT},   /* GG, char */
{"dpmpool",     "DPOOL",   pr_dpmpool,   sr_nop,    16,  0,    XXX, AN|RIGHT},   /* GG, char */
{"dpmreqid",    "DREQD",   pr_dpmreqid,  sr_nop,     8,  0,    XXX, AN|LEFT},   /* GG */
{"dpmrtok",     "RTOK",    pr_dpmrtok,  sr_nop,    37,  0,     XXX, AN|LEFT},   /* GG */
{"dpmrtyp",     "RTYP",    pr_dpmrtyp,  sr_nop,     2,  0,     XXX, AN|LEFT},   /* GG */
{"dpmsfn",      "DSFN",    pr_dpmsfn,   sr_nop,     96,  0,    XXX, AN|RIGHT},   /* GG, char */
{"dpmstatus",   "STAT",    pr_dpmstatus,sr_nop,      8,  0,    XXX, AN|RIGHT},   /* GG */
{"dpmsurl",     "SURL",    pr_dpmsurl,  sr_nop,     32,  0,    XXX, AN|RIGHT},   /* GG, char */
{"dpmturl",     "TURL",    pr_dpmturl,  sr_nop,     52,  0,    XXX, AN|RIGHT},   /* GG, char */
{"~",           "-",       pr_nop,      sr_nop,     1,   0,    LNX, AN|RIGHT}  /* NULL would ruin alphabetical order */
};

#undef USER
#undef LEFT
#undef RIGHT
#undef UNLIMITED
#undef WCHAN
#undef SIGNAL
#undef PIDMAX
#undef PO
#undef TO
#undef AN
#undef ET

static const int format_array_count = sizeof(format_array)/sizeof(format_struct);


/****************************** Macro formats *******************************/
/* First X field may be NR, which is p->start_code>>26 printed with %2ld */
/* That seems useless though, and Debian already killed it. */
/* The ones marked "Digital" have the name defined, not just the data. */
static const macro_struct macro_array[] = {
{"DEFDPM",   "dpmowner,dpmnbaccess,dpmreqid,dpmstatus,dpmpool,dpmsfn"},    /* GG */         /* DPM default */
{"DPMDat",   "dpmcredate,dpmcretime,dpmlastdate,dpmlasttime"},             /* GG */         /* DPM dates */
{"DPMDef",   "dpmowner,dpmnbaccess,dpmreqid,dpmstatus,dpmpool,dpmsfn"},    /* GG */         /* DPM default */
{"~", "~"} /* NULL would ruin alphabetical order */
};

static const int macro_array_count = sizeof(macro_array)/sizeof(macro_struct);


/*************************** AIX formats ********************/
/* Convert AIX format codes to normal format specifiers. */
static const aix_struct aix_array[] = {
#if 0
{'C', "pcpu",   "%CPU"},
#endif
{'~', "~",      "~"} /* NULL would ruin alphabetical order */
};
static const int aix_array_count = sizeof(aix_array)/sizeof(aix_struct);


/********************* sorting ***************************/
/* Convert short sorting codes to normal format specifiers. */
static const shortsort_struct shortsort_array[] = {
{'C', "pcpu"       },
{'G', "tpgid"      },
{'J', "cstime"     },
/* {'K', "stime"      }, */  /* conflict, system vs. start time */
{'M', "maj_flt"    },
{'N', "cmaj_flt"   },
{'P', "ppid"       },
{'R', "resident"   },
{'S', "share"      },
{'T', "start_time" },
{'U', "uid"        }, /* euid */
{'c', "cmd"        },
{'f', "flags"      },
{'g', "pgrp"       },
{'j', "cutime"     },
{'k', "utime"      },
{'m', "min_flt"    },
{'n', "cmin_flt"   },
{'o', "session"    },
{'p', "pid"        },
{'r', "rss"        },
{'s', "size"       },
{'t', "tty"        },
{'u', "user"       },
{'v', "vsize"      },
{'y', "priority"   }, /* nice */
{'~', "~"          } /* NULL would ruin alphabetical order */
};
static const int shortsort_array_count = sizeof(shortsort_array)/sizeof(shortsort_struct);


/************ comparison functions for bsearch *************/

static int compare_format_structs(const void *a, const void *b){
  return strcmp(((const format_struct*)a)->spec,((const format_struct*)b)->spec);
}

static int compare_macro_structs(const void *a, const void *b){
  return strcmp(((const macro_struct*)a)->spec,((const macro_struct*)b)->spec);
}

/******** look up structs as needed by the sort & format parsers ******/


const format_struct *search_format_array(const char *findme){
  format_struct key;
  key.spec = findme;
  return bsearch(&key, format_array, format_array_count,
    sizeof(format_struct), compare_format_structs
  );
}

const macro_struct *search_macro_array(const char *findme){
  macro_struct key;
  key.spec = findme;
  return bsearch(&key, macro_array, macro_array_count,
    sizeof(macro_struct), compare_macro_structs
  );
}

static unsigned int active_cols;  /* some multiple of screen_cols */

#if 1
/***** Last chance, avoid needless trunctuation. */
static void check_header_width(void){
  format_node *walk = format_list;
  unsigned int total = 0;
  int was_normal = 0;
  unsigned int i = 0;
  unsigned int sigs = 0;
  while(walk){
    switch((walk->flags) & CF_JUST_MASK){
    default:
      total += walk->width;
      total += was_normal;
      was_normal = 1;
      break;
    case CF_SIGNAL:
      sigs++;
      total += walk->width;
      total += was_normal;
      was_normal = 1;
      break;
    case CF_UNLIMITED:  /* could chop this a bit */
      if(walk->next) total += walk->width;
      else total += 3; /* not strlen(walk->name) */
      total += was_normal;
      was_normal = 1;
      break;
    case 0:  /* AIX */
      total += walk->width;
      was_normal = 0;
      break;
    }
    walk = walk->next;
  }
  for(;;){
    i++;
    active_cols = screen_cols * i;
    if(active_cols>=total) break;
    if(screen_cols*i >= OUTBUF_SIZE/2) break; /* can't go over */
  }
  wide_signals = (total+sigs*7 <= active_cols);
}
#endif


/********** show one process (NULL proc prints header) **********/

//#define SPACE_AMOUNT page_size
#define SPACE_AMOUNT 144

static char *saved_outbuf;


/* GG */ 
void show_one_procf(const dpmfile_t *restrict const p, const format_node *restrict fmt){
  /* unknown: maybe set correct & actual to 1, remove +/- 1 below */
  int correct  = 0;  /* screen position we should be at */
  int actual   = 0;  /* screen position we are at */
  int amount   = 0;  /* amount of text that this data is */
  int leftpad  = 0;  /* amount of space this column _could_ need */
  int space    = 0;  /* amount of space we actually need to print */
  int dospace  = 0;  /* previous column determined that we need a space */
  int legit    = 0;  /* legitimately stolen extra space */
  char *restrict const outbuf = saved_outbuf;
  static int did_stuff = 0;  /* have we ever printed anything? */

  if(unlikely(-1==(long)p)){    /* true only once, at the end */
    if(did_stuff) return;
    /* have _never_ printed anything, but might need a header */
    if(!--lines_to_next_header){
      lines_to_next_header = header_gap;
      show_one_procf(NULL,fmt);
    }
    /* fprintf(stderr, "No processes available.\n"); */  /* legal? */
    exit(1);
  }
  if(likely(p)){  /* not header, maybe we should call ourselves for it */
    if(unlikely(!--lines_to_next_header)){
      lines_to_next_header = header_gap;
      show_one_procf(NULL,fmt);
    }
  }
  did_stuff = 1;
  if(unlikely(active_cols>(int)OUTBUF_SIZE)) fprintf(stderr,"Fix bigness error.\n");

  /* print row start sequence */
  for(;;){
    legit = 0;
    /* set width suggestion which might be ignored */
    if(likely(fmt->next)) max_rightward = fmt->width;
    else max_rightward = active_cols-((correct>actual) ? correct : actual);
    max_leftward  = fmt->width + actual - correct; /* TODO check this */
    /* prepare data and calculate leftpad */
    if(likely(p) && likely(fmt->pr)) amount = (*fmt->pr)(outbuf,p);
    else amount = strlen(strcpy(outbuf, fmt->name)); /* AIX or headers */
    switch((fmt->flags) & CF_JUST_MASK){
    case 0:  /* for AIX, assigned outside this file */
      leftpad = 0;
      break;
    case CF_LEFT:          /* bad */
      leftpad = 0;
      break;
    case CF_RIGHT:     /* OK */
      leftpad = fmt->width - amount;
      if(leftpad < 0) leftpad = 0;
      break;
    case CF_SIGNAL:
      /* if the screen is wide enough, use full 16-character output */
      if(wide_signals){
        leftpad = 16 - amount;
        legit = 7;
      }else{
        leftpad =  9 - amount;
      }
      if(leftpad < 0) leftpad = 0;
      break;
    case CF_USER:       /* bad */
      leftpad = fmt->width - amount;
      if(leftpad < 0) leftpad = 0;
      if(!user_is_number) leftpad = 0;
      break;
    case CF_WCHAN:       /* bad */
      if(wchan_is_number){
        leftpad = fmt->width - amount;
        if(leftpad < 0) leftpad = 0;
        break;
      }else{
        if(fmt->next){
          outbuf[fmt->width] = '\0';  /* Must chop, more columns! */
        }else{
          int chopspot;  /* place to chop */
          int tmpspace;  /* need "space" before it is calculated below */
          tmpspace = correct - actual;
          if(tmpspace<1) tmpspace = dospace;
          chopspot = active_cols-actual-tmpspace;
          if(chopspot<1) chopspot=1;  /* oops, we (mostly) lose this column... */
          outbuf[chopspot] = '\0';    /* chop at screen/buffer limit */
        }
        leftpad = 0;
        break;
      }
    case CF_UNLIMITED:
      if(unlikely(fmt->next)){
        outbuf[fmt->width] = '\0';  /* Must chop, more columns! */
      }else{
        int chopspot;  /* place to chop */
        int tmpspace;  /* need "space" before it is calculated below */
        tmpspace = correct - actual;
        if(tmpspace<1) tmpspace = dospace;
        chopspot = active_cols-actual-tmpspace;
        if(chopspot<1) chopspot=1;  /* oops, we (mostly) lose this column... */
        outbuf[chopspot] = '\0';    /* chop at screen/buffer limit */
      }
      leftpad = 0;
      break;
    default:
      fprintf(stderr, "bad alignment code\n");
      break;
    }
    /* At this point:
     *
     * correct   from previous column
     * actual    from previous column
     * amount    not needed (garbage due to chopping)
     * leftpad   left padding for this column alone (not make-up or gap)
     * space     not needed (will recalculate now)
     * dospace   if we require space between this and the prior column
     * legit     space we were allowed to steal, and thus did steal
     */
    space = correct - actual + leftpad;
    if(space<1) space=dospace;
    if(unlikely(space>SPACE_AMOUNT)) space=SPACE_AMOUNT;  // only so much available

    /* print data, set x position stuff */
    amount = strlen(outbuf);  /* post-chop data width */
    if(unlikely(!fmt->next)){
      /* Last column. Write padding + data + newline all together. */
      outbuf[amount] = '\n';
      fwrite(outbuf-space, space+amount+1, 1, stdout);
      break;
    }
    /* Not the last column. Write padding + data together. */
    fwrite(outbuf-space, space+amount, 1, stdout);
    actual  += space+amount;
    correct += fmt->width;
    correct += legit;        /* adjust for SIGNAL expansion */
    if(fmt->pr && fmt->next->pr){ /* neither is AIX filler */
      correct++;
      dospace = 1;
    }else{
      dospace = 0;
    }
    fmt = fmt->next;
    /* At this point:
     *
     * correct   screen position we should be at
     * actual    screen position we are at
     * amount    not needed
     * leftpad   not needed
     * space     not needed
     * dospace   if have determined that we need a space next time
     * legit     not needed
     */
  }
}
/* end of GG */


void init_output(void){
  int outbuf_pages;
  char *outbuf;

  switch(page_size){
  case 65536: page_shift = 16; break;
  case 32768: page_shift = 15; break;
  case 16384: page_shift = 14; break;
  case  8192: page_shift = 13; break;
  default: fprintf(stderr, "Unknown page size! (assume 4096)\n");
  case  4096: page_shift = 12; break;
  case  2048: page_shift = 11; break;
  case  1024: page_shift = 10; break;
  }

  // add page_size-1 to round up
  outbuf_pages = (OUTBUF_SIZE+SPACE_AMOUNT+page_size-1)/page_size;
  outbuf = mmap(
    0,
    page_size * (outbuf_pages+1), // 1 more, for guard page at high addresses
    PROT_READ | PROT_WRITE,
    MAP_PRIVATE | MAP_ANONYMOUS,
    -1,
    0
  );
  memset(outbuf, ' ', SPACE_AMOUNT);
  if(SPACE_AMOUNT==page_size) mprotect(outbuf, page_size, PROT_READ);
  mprotect(outbuf + page_size*outbuf_pages, page_size, PROT_NONE); // gaurd page
  saved_outbuf = outbuf + SPACE_AMOUNT;
  // available space:  page_size*outbuf_pages-SPACE_AMOUNT

  seconds_since_1970 = time(NULL);
  time_of_boot = seconds_since_1970 - seconds_since_boot;

  meminfo();

  check_header_width();
}

Generated by  Doxygen 1.6.0   Back to index