2002-02-02 04:17:29 +05:30
|
|
|
/*
|
2004-04-26 06:17:31 +05:30
|
|
|
* Copyright 1998-2004 by Albert Cahalan; all rights resered.
|
2002-02-02 04:17:29 +05:30
|
|
|
* This file may be used subject to the terms and conditions of the
|
|
|
|
* GNU Library General Public License Version 2, or any later version
|
|
|
|
* at your option, as published by the Free Software Foundation.
|
|
|
|
* This program 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 Library General Public License for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
/* username lookups */
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <grp.h>
|
|
|
|
|
|
|
|
#include "../proc/readproc.h"
|
2003-07-03 10:50:19 +05:30
|
|
|
#include "../proc/sysinfo.h"
|
2002-02-02 04:17:29 +05:30
|
|
|
#include "common.h"
|
|
|
|
|
|
|
|
static sf_node *sf_list = NULL; /* deferred sorting and formatting */
|
|
|
|
static int broken; /* use gross Unix98 parsing? */
|
|
|
|
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 = malloc(sizeof(format_node));
|
2003-09-28 22:53:29 +05:30
|
|
|
if(fs->flags & CF_PIDMAX){
|
2003-07-03 10:50:19 +05:30
|
|
|
w1 = (int)get_pid_digits();
|
|
|
|
w2 = strlen(fs->head);
|
|
|
|
if(w2>w1) w1=w2; // FIXME w/ separate header/body column sizing
|
|
|
|
}else{
|
|
|
|
w1 = fs->width;
|
|
|
|
}
|
2002-02-02 04:17:29 +05:30
|
|
|
if(override){
|
|
|
|
w2 = strlen(override);
|
|
|
|
thisnode->width = (w1>w2)?w1:w2;
|
|
|
|
thisnode->name = malloc(strlen(override)+1);
|
|
|
|
strcpy(thisnode->name, override);
|
|
|
|
}else{
|
|
|
|
thisnode->width = w1;
|
|
|
|
thisnode->name = malloc(strlen(fs->head)+1);
|
|
|
|
strcpy(thisnode->name, fs->head);
|
|
|
|
}
|
|
|
|
thisnode->pr = fs->pr;
|
2002-10-02 17:40:39 +05:30
|
|
|
thisnode->need = fs->need;
|
2002-02-02 04:17:29 +05:30
|
|
|
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;
|
2002-10-12 09:55:57 +05:30
|
|
|
const char *trailer;
|
2002-02-02 04:17:29 +05:30
|
|
|
|
|
|
|
trailer = (otype=='b') ? "END_BSD" : "END_SYS5" ;
|
|
|
|
|
|
|
|
fnode = do_one_spec("pid",NULL);
|
|
|
|
if(!fnode)fprintf(stderr,"Seriously crashing. Goodbye cruel world.\n");
|
|
|
|
endp = sfn->f_cooked; while(endp->next) endp = endp->next; /* find end */
|
|
|
|
endp->next = fnode;
|
|
|
|
|
|
|
|
fnode = do_one_spec(trailer,NULL);
|
|
|
|
if(!fnode)fprintf(stderr,"Seriously crashing. Goodbye cruel world.\n");
|
|
|
|
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:
|
2002-10-02 06:03:56 +05:30
|
|
|
;
|
2002-02-02 04:17:29 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
/*** sanity check passed ***/
|
|
|
|
buf = malloc(strlen(sfn->sf)+1);
|
|
|
|
strcpy(buf, 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 = malloc(sizeof(format_node));
|
|
|
|
fnode->width = len;
|
|
|
|
fnode->name = malloc(len+1);
|
|
|
|
strcpy(fnode->name, buf);
|
|
|
|
fnode->pr = NULL; /* checked for */
|
2002-10-02 17:40:39 +05:30
|
|
|
fnode->need = 0;
|
2002-02-02 04:17:29 +05:30
|
|
|
fnode->vendor = AIX;
|
2003-09-29 09:39:52 +05:30
|
|
|
fnode->flags = CF_PRINT_EVERY_TIME;
|
2002-02-02 04:17:29 +05:30
|
|
|
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.
|
|
|
|
* The "broken" flag enables a really bad Unix98 misfeature.
|
|
|
|
* 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 = malloc(strlen(sfn->sf)+1);
|
|
|
|
strcpy(buf, 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;
|
|
|
|
case '=':
|
|
|
|
if(broken) goto out;
|
|
|
|
/* fall through */
|
|
|
|
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;
|
2003-09-17 22:44:32 +05:30
|
|
|
char *colon_loc;
|
2002-02-02 04:17:29 +05:30
|
|
|
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++;
|
|
|
|
}
|
2003-09-17 22:44:32 +05:30
|
|
|
colon_loc = strpbrk(walk,":");
|
|
|
|
if(colon_loc){ /* if width override */
|
|
|
|
*colon_loc = '\0';
|
|
|
|
colon_loc++;
|
2003-10-07 08:42:50 +05:30
|
|
|
if(strspn(colon_loc,"0123456789") != strlen(colon_loc) || *colon_loc=='0' || !*colon_loc){
|
2003-09-17 22:44:32 +05:30
|
|
|
free(buf);
|
|
|
|
goto badwidth;
|
|
|
|
}
|
|
|
|
}
|
2002-02-02 04:17:29 +05:30
|
|
|
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;
|
|
|
|
}
|
2003-09-17 22:44:32 +05:30
|
|
|
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
|
|
|
|
}
|
2002-02-02 04:17:29 +05:30
|
|
|
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.";
|
2003-09-17 22:44:32 +05:30
|
|
|
if(0) badwidth: err="Column widths must be unsigned decimal numbers.";
|
|
|
|
if(0) notmacro: err="Can't set width for a macro (multi-column) format specifier.";
|
2002-02-02 04:17:29 +05:30
|
|
|
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;
|
|
|
|
int reverse = 0;
|
|
|
|
if(*spec == '-'){
|
|
|
|
reverse = 1;
|
|
|
|
spec++;
|
|
|
|
}
|
|
|
|
fs = search_format_array(spec);
|
|
|
|
if(fs){
|
|
|
|
sort_node *thisnode;
|
2003-01-23 11:18:27 +05:30
|
|
|
thisnode = malloc(sizeof(sort_node));
|
2002-02-02 04:17:29 +05:30
|
|
|
thisnode->sr = fs->sr;
|
2002-10-14 10:42:14 +05:30
|
|
|
thisnode->need = fs->need;
|
2002-02-02 04:17:29 +05:30
|
|
|
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 = malloc(strlen(sfn->sf)+1);
|
|
|
|
strcpy(buf, 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){
|
2002-10-10 03:02:33 +05:30
|
|
|
const char all[] = "CGJKMNPRSTUcfgjkmnoprstuvy+-";
|
2002-02-02 04:17:29 +05:30
|
|
|
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){
|
|
|
|
int direction = 0;
|
|
|
|
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 = 0;
|
|
|
|
break;
|
|
|
|
case '-':
|
|
|
|
direction = 1;
|
|
|
|
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.
|
|
|
|
* The "broken" flag enables a really bad Unix98 misfeature.
|
|
|
|
* 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 ***/
|
|
|
|
return "Bug: parse_O_option got weirdness!";
|
|
|
|
}
|
|
|
|
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 = malloc(sizeof(sf_node));
|
|
|
|
sfn->sf = malloc(strlen(arg)+1);
|
|
|
|
strcpy(sfn->sf, 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 = malloc(sizeof(format_node));
|
|
|
|
fn->width = 1;
|
|
|
|
fn->name = malloc(2);
|
|
|
|
strcpy(fn->name, ":");
|
|
|
|
fn->pr = NULL; /* checked for */
|
2002-10-02 17:40:39 +05:30
|
|
|
fn->need = 0;
|
2002-02-02 04:17:29 +05:30
|
|
|
fn->vendor = AIX; /* yes, for SGI weirdness */
|
2003-09-29 09:39:52 +05:30
|
|
|
fn->flags = CF_PRINT_EVERY_TIME;
|
2002-02-02 04:17:29 +05:30
|
|
|
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"); PUSH("opri");
|
|
|
|
}
|
2004-09-23 19:11:33 +05:30
|
|
|
|
|
|
|
// 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?
|
2003-10-07 08:42:50 +05:30
|
|
|
if((thread_flags & TF_U_L) && (format_flags & FF_Uf)) PUSH("nlwp");
|
2002-02-02 04:17:29 +05:30
|
|
|
if( (format_flags & (FF_Uf|FF_Ul)) && !(format_modifiers & FM_c) ) PUSH("c");
|
2004-09-23 19:11:33 +05:30
|
|
|
|
2002-02-02 04:17:29 +05:30
|
|
|
if(format_modifiers & FM_P) PUSH("psr");
|
2003-10-07 08:42:50 +05:30
|
|
|
if(thread_flags & TF_U_L) PUSH("lwp");
|
2002-02-02 04:17:29 +05:30
|
|
|
if(format_modifiers & FM_j){
|
|
|
|
PUSH("sid");
|
|
|
|
PUSH("pgid");
|
|
|
|
}
|
|
|
|
if(format_flags & (FF_Uf|FF_Ul)) PUSH("ppid");
|
2003-10-07 08:42:50 +05:30
|
|
|
if(thread_flags & TF_U_T) PUSH("spid");
|
2002-02-02 04:17:29 +05:30
|
|
|
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(int localbroken){
|
|
|
|
sf_node *sf_walk;
|
2003-09-17 22:44:32 +05:30
|
|
|
|
2002-02-02 04:17:29 +05:30
|
|
|
if(personality & PER_BROKEN_o) localbroken = 1;
|
|
|
|
if(personality & PER_GOOD_o) localbroken = 0;
|
|
|
|
broken = localbroken;
|
|
|
|
if(sf_list){
|
2003-09-17 22:44:32 +05:30
|
|
|
const char *err;
|
2002-02-02 04:17:29 +05:30
|
|
|
err = parse_O_option(sf_list);
|
|
|
|
if(err) return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(format_list) printf("Bug: must reset the list first!\n");
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
while(srt_walk){ /* put any nodes onto sort_list in opposite way */
|
|
|
|
sort_node *travler;
|
|
|
|
travler = srt_walk;
|
|
|
|
srt_walk = srt_walk->next;
|
|
|
|
travler->next = sort_list;
|
|
|
|
sort_list = travler;
|
|
|
|
}
|
|
|
|
sf_walk = sf_walk->next;
|
|
|
|
}
|
|
|
|
|
2003-10-07 08:42:50 +05:30
|
|
|
// 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-feedback@lists.sf.net 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;
|
2003-10-20 05:07:47 +05:30
|
|
|
if(thread_flags&TF_must_use) return "Tell procps-feedback@sf.net what you want. (-L/-T, -m/m/H, and $PS_FORMAT)";
|
2003-10-07 08:42:50 +05:30
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-02-02 04:17:29 +05:30
|
|
|
if(format_list){
|
|
|
|
if(format_flags) return "Conflicting format options.";
|
2003-10-07 08:42:50 +05:30
|
|
|
if(format_modifiers) return "Can't 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{
|
2002-02-02 04:17:29 +05:30
|
|
|
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;
|
2002-09-30 12:41:30 +05:30
|
|
|
|
2003-12-24 09:48:24 +05:30
|
|
|
/* This is the sole FLASK security option. */
|
2002-09-27 19:18:00 +05:30
|
|
|
case FF_Fc: spec="FLASK_context"; break;
|
2002-02-02 04:17:29 +05:30
|
|
|
|
|
|
|
} /* end switch(format_flags) */
|
|
|
|
|
2003-10-07 08:42:50 +05:30
|
|
|
// not just for case 0, since sysv_l_format and such may be NULL
|
|
|
|
if(!spec) return generate_sysv_list();
|
2002-02-02 04:17:29 +05:30
|
|
|
|
2003-10-07 08:42:50 +05:30
|
|
|
do{
|
2003-09-17 22:44:32 +05:30
|
|
|
format_node *fmt_walk;
|
|
|
|
fmt_walk = do_one_spec(spec, NULL); /* use override "" for no headers */
|
2002-02-02 04:17:29 +05:30
|
|
|
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;
|
|
|
|
}
|
2003-10-07 08:42:50 +05:30
|
|
|
}while(0);
|
|
|
|
}while(0);
|
|
|
|
|
|
|
|
do{
|
2002-02-02 04:17:29 +05:30
|
|
|
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))
|
|
|
|
return "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))
|
|
|
|
return "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!";
|
|
|
|
}
|
2003-10-07 08:42:50 +05:30
|
|
|
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);
|
|
|
|
}
|
2004-03-27 09:25:52 +05:30
|
|
|
if(format_modifiers & FM_M){ // Mandatory Access Control, IRIX style
|
|
|
|
fn = do_one_spec("label", NULL);
|
|
|
|
fn->next=format_list;
|
|
|
|
format_list=fn;
|
|
|
|
}
|
2002-02-02 04:17:29 +05:30
|
|
|
/* 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");
|
|
|
|
}
|
2003-10-07 08:42:50 +05:30
|
|
|
}while(0);
|
2002-02-02 04:17:29 +05:30
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|