summaryrefslogtreecommitdiffstats
path: root/src/ps/sortformat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ps/sortformat.c')
-rw-r--r--src/ps/sortformat.c949
1 files changed, 949 insertions, 0 deletions
diff --git a/src/ps/sortformat.c b/src/ps/sortformat.c
new file mode 100644
index 0000000..a76ddee
--- /dev/null
+++ b/src/ps/sortformat.c
@@ -0,0 +1,949 @@
+/*
+ * sortformat.c - ps output sorting
+ *
+ * Copyright © 2011-2023 Jim Warner <james.warner@comcast.net
+ * Copyright © 2004-2023 Craig Small <csmall@dropbear.xyz
+ * Copyright © 2011-2012 Sami Kerola <kerolasa@iki.fi>
+ * Copyright © 1998-2004 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 <limits.h>
+
+#include <sys/types.h>
+
+#include "misc.h"
+#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 = *walk++;
+ initial:
+ if(c=='%') goto get_desc;
+ if(!c) goto looks_ok;
+ /* get_text: */
+ items++;
+ get_more:
+ c = *walk++;
+ if(c=='%') goto get_desc;
+ if(c==' ') goto get_more;
+ if(c) goto aix_oops;
+ goto looks_ok;
+ get_desc:
+ items++;
+ c = *walk++;
+ if(c&&c!=' ') goto initial;
+ return _("missing AIX field descriptor");
+ aix_oops:
+ 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 == '%')
+ return _("missing AIX field descriptor");
+ 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 {
+ size_t len;
+ len = strcspn(walk, "%");
+ memcpy(buf,walk,len);
+ buf[len] = '\0';
+ walk += len;
+ fnode = xmalloc(sizeof(format_node));
+ fnode->width = len < INT_MAX ? len : INT_MAX;
+ 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);
+
+ 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;
+ if(!walk) catastrophic_failure(__FILE__, __LINE__, _("please report this bug"));
+ 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 || atoi(colon_loc) <= 0){
+ 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
+ if(fnode->width <= 0) catastrophic_failure(__FILE__, __LINE__, _("please report this bug"));
+ }
+ endp = fnode; while(endp->next) endp = endp->next; /* find end */
+ endp->next = sfn->f_cooked;
+ sfn->f_cooked = fnode;
+ walk = sep_loc ? sep_loc + 1 : NULL; /* 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 = PIDS_SORT_ASCEND;
+ if(*spec == '-'){
+ reverse = PIDS_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;
+ if(tmp < 0 || (size_t)tmp >= sizeof(checkoff)) return _("bad sorting code");
+ 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 = PIDS_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 = PIDS_SORT_ASCEND;
+ break;
+ case '-':
+ direction = PIDS_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;
+}
+