ccb6ae8de1
The includes used to define a lot of things a library include should not. It was also a bit messy what was exposed in the library and what was not. get_pid_digits -> procps_pid_length and exported correctly MALLOC attribute move into relevant .c files NORETURN attribute moved to relevant .c, not used in library PURE attribute removed, it wasn't used KLONG/KLF/STRTOUKL were fixed for long, so now just use long HIDDEN attribute removed. It was for 3 functions. The PROCPS_EXPORT seems to do the same (opposite) thing. likely/unlikely removed from most places, its highly debateable this does anything useful as CPUs have gotten smarter about branches. Re-arranged the includes, ALL external programs should just #include <proc/procps.h> then proc/procps.h includes headers for files that have exported functions. procps.h and the headers it includes should not use items that are not exportable (e.g. hidden functions or macros) they go in procps-private.h
944 lines
28 KiB
C
944 lines
28 KiB
C
/*
|
|
* sortformat - ps output sorting
|
|
* Copyright 1998-2004 by Albert Cahalan
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <grp.h>
|
|
#include <pwd.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include "../proc/sysinfo.h"
|
|
#include "../include/xalloc.h"
|
|
|
|
#include "common.h"
|
|
|
|
static sf_node *sf_list = NULL; /* deferred sorting and formatting */
|
|
static int have_gnu_sort = 0; /* if true, "O" must be format */
|
|
static int already_parsed_sort = 0; /* redundantly set in & out of fn */
|
|
static int already_parsed_format = 0;
|
|
|
|
|
|
/**************** Parse single format specifier *******************/
|
|
static format_node *do_one_spec(const char *spec, const char *override){
|
|
const format_struct *fs;
|
|
const macro_struct *ms;
|
|
|
|
fs = search_format_array(spec);
|
|
if(fs){
|
|
int w1, w2;
|
|
format_node *thisnode;
|
|
thisnode = xmalloc(sizeof(format_node));
|
|
if(fs->flags & CF_PIDMAX){
|
|
w1 = (int)procps_pid_length();
|
|
w2 = strlen(fs->head);
|
|
if(w2>w1) w1=w2; // FIXME w/ separate header/body column sizing
|
|
}else{
|
|
w1 = fs->width;
|
|
}
|
|
if(override){
|
|
w2 = strlen(override);
|
|
thisnode->width = (w1>w2)?w1:w2;
|
|
thisnode->name = strdup(override);
|
|
}else{
|
|
thisnode->width = w1;
|
|
thisnode->name = strdup(fs->head);
|
|
}
|
|
thisnode->pr = fs->pr;
|
|
thisnode->vendor = fs->vendor;
|
|
thisnode->flags = fs->flags;
|
|
thisnode->next = NULL;
|
|
return thisnode;
|
|
}
|
|
|
|
/* That failed, so try it as a macro. */
|
|
ms = search_macro_array(spec);
|
|
if(ms){
|
|
format_node *list = NULL;
|
|
format_node *newnode;
|
|
const char *walk;
|
|
int dist;
|
|
char buf[16]; /* trust strings will be short (from above, not user) */
|
|
walk = ms->head;
|
|
while(*walk){
|
|
dist = strcspn(walk, ", ");
|
|
strncpy(buf,walk,dist);
|
|
buf[dist] = '\0';
|
|
newnode = do_one_spec(buf,override); /* call self, assume success */
|
|
newnode->next = list;
|
|
list = newnode;
|
|
walk += dist;
|
|
if(*walk) walk++;
|
|
}
|
|
return list;
|
|
}
|
|
return NULL; /* bad, spec not found */
|
|
}
|
|
|
|
|
|
/************ must wrap user format in default *************/
|
|
static void O_wrap(sf_node *sfn, int otype){
|
|
format_node *fnode;
|
|
format_node *endp;
|
|
const char *trailer;
|
|
|
|
trailer = (otype=='b') ? "END_BSD" : "END_SYS5" ;
|
|
|
|
fnode = do_one_spec("pid",NULL);
|
|
if(!fnode)catastrophic_failure(__FILE__, __LINE__, _("seriously crashing: goodbye cruel world"));
|
|
endp = sfn->f_cooked; while(endp->next) endp = endp->next; /* find end */
|
|
endp->next = fnode;
|
|
|
|
fnode = do_one_spec(trailer,NULL);
|
|
if(!fnode)catastrophic_failure(__FILE__, __LINE__, _("seriously crashing: goodbye cruel world"));
|
|
endp = fnode; while(endp->next) endp = endp->next; /* find end */
|
|
endp->next = sfn->f_cooked;
|
|
sfn->f_cooked = fnode;
|
|
}
|
|
|
|
/******************************************************************
|
|
* Used to parse option AIX field descriptors.
|
|
* Put each completed format_node onto the list starting at ->f_cooked
|
|
*/
|
|
static const char *aix_format_parse(sf_node *sfn){
|
|
char *buf; /* temp copy of arg to hack on */
|
|
char *walk;
|
|
int items;
|
|
|
|
/*** sanity check and count items ***/
|
|
items = 0;
|
|
walk = sfn->sf;
|
|
/* state machine */ {
|
|
int c;
|
|
initial:
|
|
c = *walk++;
|
|
if(c=='%') goto get_desc;
|
|
if(!c) goto looks_ok;
|
|
/* get_text: */
|
|
items++;
|
|
get_more_text:
|
|
c = *walk++;
|
|
if(c=='%') goto get_desc;
|
|
if(c) goto get_more_text;
|
|
goto looks_ok;
|
|
get_desc:
|
|
items++;
|
|
c = *walk++;
|
|
if(c) goto initial;
|
|
return _("improper AIX field descriptor");
|
|
looks_ok:
|
|
;
|
|
}
|
|
|
|
/*** sanity check passed ***/
|
|
buf = strdup(sfn->sf);
|
|
walk = sfn->sf;
|
|
|
|
while(items--){
|
|
format_node *fnode; /* newly allocated */
|
|
format_node *endp; /* for list manipulation */
|
|
|
|
if(*walk == '%'){
|
|
const aix_struct *aix;
|
|
walk++;
|
|
if(*walk == '%') goto double_percent;
|
|
aix = search_aix_array(*walk);
|
|
walk++;
|
|
if(!aix){
|
|
free(buf);
|
|
return _("unknown AIX field descriptor");
|
|
}
|
|
fnode = do_one_spec(aix->spec, aix->head);
|
|
if(!fnode){
|
|
free(buf);
|
|
return _("AIX field descriptor processing bug");
|
|
}
|
|
} else {
|
|
int len;
|
|
len = strcspn(walk, "%");
|
|
memcpy(buf,walk,len);
|
|
if(0){
|
|
double_percent:
|
|
len = 1;
|
|
buf[0] = '%';
|
|
}
|
|
buf[len] = '\0';
|
|
walk += len;
|
|
fnode = xmalloc(sizeof(format_node));
|
|
fnode->width = len;
|
|
fnode->name = strdup(buf);
|
|
fnode->pr = NULL; /* checked for */
|
|
fnode->vendor = AIX;
|
|
fnode->flags = CF_PRINT_EVERY_TIME;
|
|
fnode->next = NULL;
|
|
}
|
|
|
|
endp = fnode; while(endp->next) endp = endp->next; /* find end */
|
|
endp->next = sfn->f_cooked;
|
|
sfn->f_cooked = fnode;
|
|
}
|
|
free(buf);
|
|
already_parsed_format = 1;
|
|
return NULL;
|
|
}
|
|
|
|
/***************************************************************
|
|
* Used to parse option O lists. Option O is shared between
|
|
* sorting and formatting. Users may expect one or the other.
|
|
* Put each completed format_node onto the list starting at ->f_cooked
|
|
*/
|
|
static const char *format_parse(sf_node *sfn){
|
|
char *buf; /* temp copy of arg to hack on */
|
|
char *sep_loc; /* separator location: " \t,\n" */
|
|
char *walk;
|
|
const char *err; /* error code that could or did happen */
|
|
format_node *fnode;
|
|
int items;
|
|
int need_item;
|
|
static char errbuf[80]; /* for variable-text error message */
|
|
|
|
/*** prepare to operate ***/
|
|
buf = strdup(sfn->sf);
|
|
|
|
/*** sanity check and count items ***/
|
|
need_item = 1; /* true */
|
|
items = 0;
|
|
walk = buf;
|
|
do{
|
|
switch(*walk){
|
|
case ' ': case ',': case '\t': case '\n': case '\0':
|
|
/* Linux extension: allow \t and \n as delimiters */
|
|
if(need_item){
|
|
free(buf);
|
|
goto improper;
|
|
}
|
|
need_item=1;
|
|
break;
|
|
default:
|
|
if(need_item) items++;
|
|
need_item=0;
|
|
}
|
|
} while (*++walk);
|
|
out:
|
|
if(!items){
|
|
free(buf);
|
|
goto empty;
|
|
}
|
|
#ifdef STRICT_LIST
|
|
if(need_item){ /* can't have trailing deliminator */
|
|
free(buf);
|
|
goto improper;
|
|
}
|
|
#else
|
|
if(need_item){ /* allow 1 trailing deliminator */
|
|
*--walk='\0'; /* remove the trailing deliminator */
|
|
}
|
|
#endif
|
|
/*** actually parse the list ***/
|
|
walk = buf;
|
|
while(items--){
|
|
format_node *endp;
|
|
char *equal_loc;
|
|
char *colon_loc;
|
|
sep_loc = strpbrk(walk," ,\t\n");
|
|
/* if items left, then sep_loc is not in header override */
|
|
if(items && sep_loc) *sep_loc = '\0';
|
|
equal_loc = strpbrk(walk,"=");
|
|
if(equal_loc){ /* if header override */
|
|
*equal_loc = '\0';
|
|
equal_loc++;
|
|
}
|
|
colon_loc = strpbrk(walk,":");
|
|
if(colon_loc){ /* if width override */
|
|
*colon_loc = '\0';
|
|
colon_loc++;
|
|
if(strspn(colon_loc,"0123456789") != strlen(colon_loc) || *colon_loc=='0' || !*colon_loc){
|
|
free(buf);
|
|
goto badwidth;
|
|
}
|
|
}
|
|
fnode = do_one_spec(walk,equal_loc);
|
|
if(!fnode){
|
|
if(!*errbuf){ /* if didn't already create an error string */
|
|
snprintf(
|
|
errbuf,
|
|
sizeof(errbuf),
|
|
_("unknown user-defined format specifier \"%s\""),
|
|
walk
|
|
);
|
|
}
|
|
free(buf);
|
|
goto unknown;
|
|
}
|
|
if(colon_loc){
|
|
if(fnode->next){
|
|
free(buf);
|
|
goto notmacro;
|
|
}
|
|
// FIXME: enforce signal width to 8, 9, or 16 (grep: SIGNAL wide_signals)
|
|
fnode->width = atoi(colon_loc); // already verified to be a number
|
|
}
|
|
endp = fnode; while(endp->next) endp = endp->next; /* find end */
|
|
endp->next = sfn->f_cooked;
|
|
sfn->f_cooked = fnode;
|
|
walk = sep_loc + 1; /* point to next item, if any */
|
|
}
|
|
free(buf);
|
|
already_parsed_format = 1;
|
|
return NULL;
|
|
|
|
/* errors may cause a retry looking for AIX format codes */
|
|
if(0) unknown: err=errbuf;
|
|
if(0) empty: err=_("empty format list");
|
|
if(0) improper: err=_("improper format list");
|
|
if(0) badwidth: err=_("column widths must be unsigned decimal numbers");
|
|
if(0) notmacro: err=_("can not set width for a macro (multi-column) format specifier");
|
|
if(strchr(sfn->sf,'%')) err = aix_format_parse(sfn);
|
|
return err;
|
|
}
|
|
|
|
/**************** Parse single sort specifier *******************/
|
|
static sort_node *do_one_sort_spec(const char *spec){
|
|
const format_struct *fs;
|
|
enum pids_sort_order reverse = PROCPS_SORT_ASCEND;
|
|
if(*spec == '-'){
|
|
reverse = PROCPS_SORT_DESCEND;
|
|
spec++;
|
|
} else if(*spec == '+'){
|
|
spec++;
|
|
}
|
|
fs = search_format_array(spec);
|
|
if(fs){
|
|
sort_node *thisnode;
|
|
thisnode = xmalloc(sizeof(sort_node));
|
|
thisnode->sr = fs->sr;
|
|
// next is a special pointer, called to help with rel enums
|
|
thisnode->xe = (int(*)(char*,proc_t*))fs->pr;
|
|
thisnode->reverse = reverse;
|
|
thisnode->next = NULL;
|
|
return thisnode;
|
|
}
|
|
return NULL; /* bad, spec not found */
|
|
}
|
|
|
|
|
|
/**************************************************************
|
|
* Used to parse long sorting options.
|
|
* Put each completed sort_node onto the list starting at ->s_cooked
|
|
*/
|
|
static const char *long_sort_parse(sf_node *sfn){
|
|
char *buf; /* temp copy of arg to hack on */
|
|
char *sep_loc; /* separator location: " \t,\n" */
|
|
char *walk;
|
|
sort_node *snode;
|
|
int items;
|
|
int need_item;
|
|
|
|
/*** prepare to operate ***/
|
|
buf = strdup(sfn->sf);
|
|
|
|
/*** sanity check and count items ***/
|
|
need_item = 1; /* true */
|
|
items = 0;
|
|
walk = buf;
|
|
do{
|
|
switch(*walk){
|
|
case ' ': case ',': case '\t': case '\n': case '\0':
|
|
if(need_item){
|
|
free(buf);
|
|
return _("improper sort list");
|
|
}
|
|
need_item=1;
|
|
break;
|
|
default:
|
|
if(need_item) items++;
|
|
need_item=0;
|
|
}
|
|
} while (*++walk);
|
|
if(!items){
|
|
free(buf);
|
|
return _("empty sort list");
|
|
}
|
|
#ifdef STRICT_LIST
|
|
if(need_item){ /* can't have trailing deliminator */
|
|
free(buf);
|
|
return _("improper sort list");
|
|
}
|
|
#else
|
|
if(need_item){ /* allow 1 trailing deliminator */
|
|
*--walk='\0'; /* remove the trailing deliminator */
|
|
}
|
|
#endif
|
|
/*** actually parse the list ***/
|
|
walk = buf;
|
|
while(items--){
|
|
sort_node *endp;
|
|
sep_loc = strpbrk(walk," ,\t\n");
|
|
if(sep_loc) *sep_loc = '\0';
|
|
snode = do_one_sort_spec(walk);
|
|
if(!snode){
|
|
free(buf);
|
|
return _("unknown sort specifier");
|
|
}
|
|
endp = snode; while(endp->next) endp = endp->next; /* find end */
|
|
endp->next = sfn->s_cooked;
|
|
sfn->s_cooked = snode;
|
|
walk = sep_loc + 1; /* point to next item, if any */
|
|
}
|
|
free(buf);
|
|
already_parsed_sort = 1;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/************ pre-parse short sorting option *************/
|
|
/* Errors _must_ be detected so that the "O" option can try to
|
|
* reparse as formatting codes.
|
|
*/
|
|
static const char *verify_short_sort(const char *arg){
|
|
const char all[] = "CGJKMNPRSTUcfgjkmnoprstuvy+-";
|
|
char checkoff[256];
|
|
int i;
|
|
const char *walk;
|
|
int tmp;
|
|
if(strspn(arg,all) != strlen(arg)) return _("bad sorting code");
|
|
for(i=256; i--;) checkoff[i] = 0;
|
|
walk = arg;
|
|
for(;;){
|
|
tmp = *walk;
|
|
switch(tmp){
|
|
case '\0':
|
|
return NULL; /* looks good */
|
|
case '+':
|
|
case '-':
|
|
tmp = *(walk+1);
|
|
if(!tmp || tmp=='+' || tmp=='-') return _("bad sorting code");
|
|
break;
|
|
case 'P':
|
|
if(forest_type) return _("PPID sort and forest output conflict");
|
|
/* fall through */
|
|
default:
|
|
if(checkoff[tmp]) return _("bad sorting code"); /* repeated */
|
|
/* ought to check against already accepted sort options */
|
|
checkoff[tmp] = 1;
|
|
break;
|
|
}
|
|
walk++;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/************ parse short sorting option *************/
|
|
static const char *short_sort_parse(sf_node *sfn){
|
|
enum pids_sort_order direction = PROCPS_SORT_ASCEND;
|
|
const char *walk;
|
|
int tmp;
|
|
sort_node *snode;
|
|
sort_node *endp;
|
|
const struct shortsort_struct *ss;
|
|
walk = sfn->sf;
|
|
for(;;){
|
|
tmp = *walk;
|
|
switch(tmp){
|
|
case '\0':
|
|
already_parsed_sort = 1;
|
|
return NULL;
|
|
case '+':
|
|
direction = PROCPS_SORT_ASCEND;
|
|
break;
|
|
case '-':
|
|
direction = PROCPS_SORT_DESCEND;
|
|
break;
|
|
default:
|
|
ss = search_shortsort_array(tmp);
|
|
if(!ss) return _("unknown sort specifier");
|
|
snode = do_one_sort_spec(ss->spec);
|
|
if(!snode) return _("unknown sort specifier");
|
|
snode->reverse = direction;
|
|
endp = snode; while(endp->next) endp = endp->next; /* find end */
|
|
endp->next = sfn->s_cooked;
|
|
sfn->s_cooked = snode;
|
|
direction = 0;
|
|
break;
|
|
}
|
|
walk++;
|
|
}
|
|
}
|
|
|
|
/******************* high-level below here *********************/
|
|
|
|
|
|
/*
|
|
* Used to parse option O lists. Option O is shared between
|
|
* sorting and formatting. Users may expect one or the other.
|
|
* Recursion is to preserve original order.
|
|
*/
|
|
static const char *parse_O_option(sf_node *sfn){
|
|
const char *err; /* error code that could or did happen */
|
|
|
|
if(sfn->next){
|
|
err = parse_O_option(sfn->next);
|
|
if(err) return err;
|
|
}
|
|
|
|
switch(sfn->sf_code){
|
|
case SF_B_o: case SF_G_format: case SF_U_o: /*** format ***/
|
|
err = format_parse(sfn);
|
|
if(!err) already_parsed_format = 1;
|
|
break;
|
|
case SF_U_O: /*** format ***/
|
|
/* Can have -l -f f u... set already_parsed_format like DEC does */
|
|
if(already_parsed_format) return _("option -O can not follow other format options");
|
|
err = format_parse(sfn);
|
|
if(err) return err;
|
|
already_parsed_format = 1;
|
|
O_wrap(sfn,'u'); /* must wrap user format in default */
|
|
break;
|
|
case SF_B_O: /*** both ***/
|
|
if(have_gnu_sort || already_parsed_sort) err = _("multiple sort options");
|
|
else err = verify_short_sort(sfn->sf);
|
|
if(!err){ /* success as sorting code */
|
|
short_sort_parse(sfn);
|
|
already_parsed_sort = 1;
|
|
return NULL;
|
|
}
|
|
if(already_parsed_format){
|
|
err = _("option O is neither first format nor sort order");
|
|
break;
|
|
}
|
|
if(!format_parse(sfn)){ /* if success as format code */
|
|
already_parsed_format = 1;
|
|
O_wrap(sfn,'b'); /* must wrap user format in default */
|
|
return NULL;
|
|
}
|
|
break;
|
|
case SF_G_sort: case SF_B_m: /*** sort ***/
|
|
if(already_parsed_sort) err = _("multiple sort options");
|
|
else err = long_sort_parse(sfn);
|
|
already_parsed_sort = 1;
|
|
break;
|
|
default: /*** junk ***/
|
|
catastrophic_failure(__FILE__, __LINE__, _("please report this bug"));
|
|
}
|
|
return err; /* could be NULL */
|
|
}
|
|
|
|
|
|
/************ Main parser calls this to save lists for later **********/
|
|
/* store data for later and return 1 if arg looks non-standard */
|
|
int defer_sf_option(const char *arg, int source){
|
|
sf_node *sfn;
|
|
char buf[16];
|
|
int dist;
|
|
const format_struct *fs;
|
|
int need_item = 1;
|
|
|
|
sfn = xmalloc(sizeof(sf_node));
|
|
sfn->sf = strdup(arg);
|
|
sfn->sf_code = source;
|
|
sfn->s_cooked = NULL;
|
|
sfn->f_cooked = NULL;
|
|
sfn->next = sf_list;
|
|
sf_list = sfn;
|
|
|
|
if(source == SF_G_sort) have_gnu_sort = 1;
|
|
|
|
/* Now try to find an excuse to ignore broken Unix98 parsing. */
|
|
if(source != SF_U_o) return 1; /* Wonderful! Already non-Unix98. */
|
|
do{
|
|
switch(*arg){
|
|
case ' ': case ',': case '\0': /* no \t\n\r support in Unix98 */
|
|
if(need_item) return 1; /* something wrong */
|
|
need_item=1;
|
|
break;
|
|
case '=':
|
|
if(need_item) return 1; /* something wrong */
|
|
return 0; /* broken Unix98 parsing is required */
|
|
default:
|
|
if(!need_item) break;
|
|
need_item=0;
|
|
dist = strcspn(arg,", =");
|
|
if(dist>15) return 1; /* something wrong, sort maybe? */
|
|
strncpy(buf,arg,dist); /* no '\0' on end */
|
|
buf[dist] = '\0'; /* fix that problem */
|
|
fs = search_format_array(buf);
|
|
if(!fs) return 1; /* invalid spec, macro or sort maybe? */
|
|
if(fs->vendor) return 1; /* Wonderful! Legal non-Unix98 spec. */
|
|
}
|
|
} while (*++arg);
|
|
|
|
return 0; /* boring, Unix98 is no change */
|
|
}
|
|
|
|
/***** Since ps is not long-lived, the memory leak can be ignored. ******/
|
|
void reset_sortformat(void){
|
|
sf_list = NULL; /* deferred sorting and formatting */
|
|
format_list = NULL; /* digested formatting options */
|
|
sort_list = NULL; /* digested sorting options (redundant?) */
|
|
have_gnu_sort = 0;
|
|
already_parsed_sort = 0;
|
|
already_parsed_format = 0;
|
|
}
|
|
|
|
|
|
/***** Search format_list for findme, then insert putme after findme. ****/
|
|
static int fmt_add_after(const char *findme, format_node *putme){
|
|
format_node *walk;
|
|
if(!strcmp(format_list->name, findme)){
|
|
putme->next = format_list->next;
|
|
format_list->next = putme;
|
|
return 1; /* success */
|
|
}
|
|
walk = format_list;
|
|
while(walk->next){
|
|
if(!strcmp(walk->next->name, findme)){
|
|
putme->next = walk->next->next;
|
|
walk->next->next = putme;
|
|
return 1; /* success */
|
|
}
|
|
walk = walk->next;
|
|
}
|
|
return 0; /* fail */
|
|
}
|
|
|
|
/******* Search format_list for findme, then delete it. ********/
|
|
static int fmt_delete(const char *findme){
|
|
format_node *walk;
|
|
format_node *old;
|
|
if(!strcmp(format_list->name, findme)){
|
|
old = format_list;
|
|
format_list = format_list->next;
|
|
free(old);
|
|
return 1; /* success */
|
|
}
|
|
walk = format_list;
|
|
while(walk->next){
|
|
if(!strcmp(walk->next->name, findme)){
|
|
old = walk->next;
|
|
walk->next = walk->next->next;
|
|
free(old);
|
|
return 1; /* success */
|
|
}
|
|
walk = walk->next;
|
|
}
|
|
return 0; /* fail */
|
|
}
|
|
|
|
|
|
/************ Build a SysV format backwards. ***********/
|
|
#define PUSH(foo) (fn=do_one_spec(foo, NULL), fn->next=format_list, format_list=fn)
|
|
static const char *generate_sysv_list(void){
|
|
format_node *fn;
|
|
if((format_modifiers & FM_y) && !(format_flags & FF_Ul))
|
|
return _("modifier -y without format -l makes no sense");
|
|
if(prefer_bsd_defaults){
|
|
if(format_flags) PUSH("cmd");
|
|
else PUSH("args");
|
|
PUSH("bsdtime");
|
|
if(!(format_flags & FF_Ul)) PUSH("stat");
|
|
}else{
|
|
if(format_flags & FF_Uf) PUSH("cmd");
|
|
else PUSH("ucmd");
|
|
PUSH("time");
|
|
}
|
|
PUSH("tname"); /* Unix98 says "TTY" here, yet "tty" produces "TT". */
|
|
if(format_flags & FF_Uf) PUSH("stime");
|
|
/* avoid duplicate columns from -FP and -Fly */
|
|
if(format_modifiers & FM_F){
|
|
/* if -FP take the Sun-style column instead (sorry about "sgi_p") */
|
|
if(!(format_modifiers & FM_P)) PUSH("psr"); /* should be ENG */
|
|
/* if -Fly take the ADDR-replacement RSS instead */
|
|
if(!( (format_flags & FF_Ul) && (format_modifiers & FM_y) )) PUSH("rss");
|
|
}
|
|
if(format_flags & FF_Ul){
|
|
PUSH("wchan");
|
|
}
|
|
/* since FM_y adds RSS anyway, don't do this hack when that is true */
|
|
if( (format_flags & FF_Ul) && !(format_modifiers & FM_y) ){
|
|
if(personality & PER_IRIX_l){ /* add "rss" then ':' here */
|
|
PUSH("sgi_rss");
|
|
fn = xmalloc(sizeof(format_node));
|
|
fn->width = 1;
|
|
fn->name = strdup(":");
|
|
fn->pr = NULL; /* checked for */
|
|
fn->vendor = AIX; /* yes, for SGI weirdness */
|
|
fn->flags = CF_PRINT_EVERY_TIME;
|
|
fn->next = format_list;
|
|
format_list=fn;
|
|
}
|
|
}
|
|
if((format_modifiers & FM_F) || (format_flags & FF_Ul)){
|
|
PUSH("sz");
|
|
}
|
|
if(format_flags & FF_Ul){
|
|
if(format_modifiers & FM_y) PUSH("rss");
|
|
else if(personality & (PER_ZAP_ADDR|PER_IRIX_l)) PUSH("sgi_p");
|
|
else PUSH("addr_1");
|
|
}
|
|
if(format_modifiers & FM_c){
|
|
PUSH("pri"); PUSH("class");
|
|
}else if(format_flags & FF_Ul){
|
|
PUSH("ni");
|
|
if(personality & PER_IRIX_l) PUSH("priority");
|
|
else /* is this good? */ PUSH("opri");
|
|
}
|
|
|
|
// FIXME TODO XXX -- this is a serious problem
|
|
// These somehow got flipped around.
|
|
// The bug is in procps-3.1.1, procps-990211, prior too?
|
|
if((thread_flags & TF_U_L) && (format_flags & FF_Uf)) PUSH("nlwp");
|
|
if( (format_flags & (FF_Uf|FF_Ul)) && !(format_modifiers & FM_c) ) PUSH("c");
|
|
|
|
if(format_modifiers & FM_P) PUSH("psr");
|
|
if(thread_flags & TF_U_L) PUSH("lwp");
|
|
if(format_modifiers & FM_j){
|
|
PUSH("sid");
|
|
PUSH("pgid");
|
|
}
|
|
if(format_flags & (FF_Uf|FF_Ul)) PUSH("ppid");
|
|
if(thread_flags & TF_U_T) PUSH("spid");
|
|
PUSH("pid");
|
|
if(format_flags & FF_Uf){
|
|
if(personality & PER_SANE_USER) PUSH("user");
|
|
else PUSH("uid_hack");
|
|
}else if(format_flags & FF_Ul){
|
|
PUSH("uid");
|
|
}
|
|
if(format_flags & FF_Ul){
|
|
PUSH("s");
|
|
if(!(format_modifiers & FM_y)) PUSH("f");
|
|
}
|
|
if(format_modifiers & FM_M){
|
|
PUSH("label"); /* Mandatory Access Control */
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* Used to parse option O lists. Option O is shared between
|
|
* sorting and formatting. Users may expect one or the other.
|
|
* The "broken" flag enables a really bad Unix98 misfeature.
|
|
*/
|
|
const char *process_sf_options(void){
|
|
sf_node *sf_walk;
|
|
|
|
if(sf_list){
|
|
const char *err;
|
|
err = parse_O_option(sf_list);
|
|
if(err) return err;
|
|
}
|
|
|
|
if(format_list) catastrophic_failure(__FILE__, __LINE__, _("bug: must reset the list first"));
|
|
|
|
/* merge formatting info of sf_list into format_list here */
|
|
sf_walk = sf_list;
|
|
while(sf_walk){
|
|
format_node *fmt_walk;
|
|
fmt_walk = sf_walk->f_cooked;
|
|
sf_walk->f_cooked = NULL;
|
|
while(fmt_walk){ /* put any nodes onto format_list in opposite way */
|
|
format_node *travler;
|
|
travler = fmt_walk;
|
|
fmt_walk = fmt_walk->next;
|
|
travler->next = format_list;
|
|
format_list = travler;
|
|
}
|
|
sf_walk = sf_walk->next;
|
|
}
|
|
|
|
/* merge sorting info of sf_list into sort_list here */
|
|
sf_walk = sf_list;
|
|
while(sf_walk){
|
|
sort_node *srt_walk;
|
|
srt_walk = sf_walk->s_cooked;
|
|
sf_walk->s_cooked = NULL;
|
|
if (srt_walk) {
|
|
sort_node *travler = srt_walk;
|
|
while (travler->next) travler = travler->next;
|
|
travler->next = sort_list;
|
|
sort_list = srt_walk;
|
|
}
|
|
sf_walk = sf_walk->next;
|
|
}
|
|
|
|
// Get somebody to explain how -L/-T is supposed to interact
|
|
// with sorting. Do the threads remain grouped, with sorting
|
|
// by process, or do the threads get sorted by themselves?
|
|
if(sort_list && (thread_flags&TF_no_sort)){
|
|
return _("tell <procps@freelists.org> what you expected");
|
|
}
|
|
|
|
// If nothing else, try to use $PS_FORMAT before the default.
|
|
if(!format_flags && !format_modifiers && !format_list){
|
|
char *tmp;
|
|
tmp = getenv("PS_FORMAT"); /* user override kills default */
|
|
if(tmp && *tmp){
|
|
const char *err;
|
|
sf_node sfn;
|
|
if(thread_flags&TF_must_use) return _("tell <procps@freelists.org> what you want (-L/-T, -m/m/H, and $PS_FORMAT)");
|
|
sfn.sf = tmp;
|
|
sfn.f_cooked = NULL;
|
|
err = format_parse(&sfn);
|
|
if(!err){
|
|
format_node *fmt_walk;
|
|
fmt_walk = sfn.f_cooked;
|
|
while(fmt_walk){ /* put any nodes onto format_list in opposite way */
|
|
format_node *travler;
|
|
travler = fmt_walk;
|
|
fmt_walk = fmt_walk->next;
|
|
travler->next = format_list;
|
|
format_list = travler;
|
|
}
|
|
return NULL;
|
|
}
|
|
// FIXME: prove that this won't be hit on valid bogus-BSD options
|
|
fprintf(stderr, _("warning: $PS_FORMAT ignored. (%s)\n"), err);
|
|
}
|
|
}
|
|
|
|
if(format_list){
|
|
if(format_flags) return _("conflicting format options");
|
|
if(format_modifiers) return _("can not use output modifiers with user-defined output");
|
|
if(thread_flags&TF_must_use) return _("-L/-T with H/m/-m and -o/-O/o/O is nonsense");
|
|
return NULL;
|
|
}
|
|
|
|
do{
|
|
const char *spec;
|
|
switch(format_flags){
|
|
|
|
default: return _("conflicting format options");
|
|
|
|
/* These can be NULL, which enables SysV list generation code. */
|
|
case 0: spec=NULL; break;
|
|
case FF_Uf | FF_Ul: spec=sysv_fl_format; break;
|
|
case FF_Uf: spec=sysv_f_format; break;
|
|
case FF_Ul: spec=sysv_l_format; break;
|
|
|
|
/* These are NOT REACHED for normal -j processing. */
|
|
case FF_Uj: spec=sysv_j_format; break; /* Debian & Digital */
|
|
case FF_Uj | FF_Ul: spec="RD_lj"; break; /* Debian */
|
|
case FF_Uj | FF_Uf: spec="RD_fj"; break; /* Debian */
|
|
|
|
/* These are true BSD options. */
|
|
case FF_Bj: spec=bsd_j_format; break;
|
|
case FF_Bl: spec=bsd_l_format; break;
|
|
case FF_Bs: spec=bsd_s_format; break;
|
|
case FF_Bu: spec=bsd_u_format; break;
|
|
case FF_Bv: spec=bsd_v_format; break;
|
|
|
|
/* These are old Linux options. Option m is overloaded. */
|
|
case FF_LX: spec="OL_X"; break;
|
|
case FF_Lm: spec="OL_m"; break;
|
|
|
|
/* This is the sole FLASK security option. */
|
|
case FF_Fc: spec="FLASK_context"; break;
|
|
|
|
} /* end switch(format_flags) */
|
|
|
|
// not just for case 0, since sysv_l_format and such may be NULL
|
|
if(!spec) return generate_sysv_list();
|
|
|
|
do{
|
|
format_node *fmt_walk;
|
|
fmt_walk = do_one_spec(spec, NULL); /* use override "" for no headers */
|
|
while(fmt_walk){ /* put any nodes onto format_list in opposite way */
|
|
format_node *travler;
|
|
travler = fmt_walk;
|
|
fmt_walk = fmt_walk->next;
|
|
travler->next = format_list;
|
|
format_list = travler;
|
|
}
|
|
}while(0);
|
|
}while(0);
|
|
|
|
do{
|
|
format_node *fn;
|
|
if(format_modifiers & FM_j){
|
|
fn = do_one_spec("pgid", NULL);
|
|
if(!fmt_add_after("PPID", fn)) if(!fmt_add_after("PID", fn))
|
|
catastrophic_failure(__FILE__, __LINE__, _("internal error: no PID or PPID for -j option"));
|
|
fn = do_one_spec("sid", NULL);
|
|
if(!fmt_add_after("PGID", fn)) return _("lost my PGID");
|
|
}
|
|
if(format_modifiers & FM_y){
|
|
/* TODO: check for failure to do something, and complain if so */
|
|
fmt_delete("F");
|
|
fn = do_one_spec("rss", NULL);
|
|
if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR");
|
|
}
|
|
if(format_modifiers & FM_c){
|
|
fmt_delete("%CPU"); fmt_delete("CPU"); fmt_delete("CP"); fmt_delete("C");
|
|
fmt_delete("NI");
|
|
fn = do_one_spec("class", NULL);
|
|
if(!fmt_add_after("PRI", fn))
|
|
catastrophic_failure(__FILE__, __LINE__, _("internal error: no PRI for -c option"));
|
|
fmt_delete("PRI"); /* we want a different one */
|
|
fn = do_one_spec("pri", NULL);
|
|
if(!fmt_add_after("CLS", fn)) return _("lost my CLS");
|
|
}
|
|
if(thread_flags & TF_U_T){
|
|
fn = do_one_spec("spid", NULL);
|
|
if(!fmt_add_after("PID", fn) && (thread_flags&TF_must_use))
|
|
return _("-T with H/-m/m but no PID for SPID to follow");
|
|
}
|
|
if(thread_flags & TF_U_L){
|
|
fn = do_one_spec("lwp", NULL);
|
|
if(fmt_add_after("SID", fn)) goto did_lwp;
|
|
if(fmt_add_after("SESS", fn)) goto did_lwp;
|
|
if(fmt_add_after("PGID", fn)) goto did_lwp;
|
|
if(fmt_add_after("PGRP", fn)) goto did_lwp;
|
|
if(fmt_add_after("PPID", fn)) goto did_lwp;
|
|
if(fmt_add_after("PID", fn)) goto did_lwp;
|
|
if(thread_flags&TF_must_use)
|
|
return _("-L with H/-m/m but no PID/PGID/SID/SESS for NLWP to follow");
|
|
did_lwp:
|
|
fn = do_one_spec("nlwp", NULL);
|
|
fmt_add_after("%CPU", fn);
|
|
}
|
|
if(format_modifiers & FM_M){ // Mandatory Access Control, IRIX style
|
|
fn = do_one_spec("label", NULL);
|
|
fn->next=format_list;
|
|
format_list=fn;
|
|
}
|
|
/* Do personality-specific translations not covered by format_flags.
|
|
* Generally, these only get hit when personality overrides unix output.
|
|
* That (mostly?) means the Digital and Debian personalities.
|
|
*/
|
|
if((personality & PER_ZAP_ADDR) && (format_flags & FF_Ul)){
|
|
fn = do_one_spec("sgi_p", NULL);
|
|
if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR");
|
|
}
|
|
if((personality & PER_SANE_USER) && (format_flags & FF_Uf)){
|
|
fn = do_one_spec("user", NULL);
|
|
if(fmt_add_after("UID", fn)) fmt_delete("UID");
|
|
}
|
|
}while(0);
|
|
|
|
return NULL;
|
|
}
|
|
|