2 * Copyright 1998-2004 by Albert Cahalan; all rights resered.
3 * This file may be used subject to the terms and conditions of the
4 * GNU Library General Public License Version 2, or any later version
5 * at your option, as published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU Library General Public License for more details.
16 /* username lookups */
17 #include <sys/types.h>
21 #include "../proc/readproc.h"
22 #include "../proc/sysinfo.h"
25 static sf_node *sf_list = NULL; /* deferred sorting and formatting */
26 static int broken; /* use gross Unix98 parsing? */
27 static int have_gnu_sort = 0; /* if true, "O" must be format */
28 static int already_parsed_sort = 0; /* redundantly set in & out of fn */
29 static int already_parsed_format = 0;
32 /**************** Parse single format specifier *******************/
33 static format_node *do_one_spec(const char *spec, const char *override){
34 const format_struct *fs;
35 const macro_struct *ms;
37 fs = search_format_array(spec);
40 format_node *thisnode;
41 thisnode = malloc(sizeof(format_node));
42 if(fs->flags & CF_PIDMAX){
43 w1 = (int)get_pid_digits();
44 w2 = strlen(fs->head);
45 if(w2>w1) w1=w2; // FIXME w/ separate header/body column sizing
50 w2 = strlen(override);
51 thisnode->width = (w1>w2)?w1:w2;
52 thisnode->name = malloc(strlen(override)+1);
53 strcpy(thisnode->name, override);
56 thisnode->name = malloc(strlen(fs->head)+1);
57 strcpy(thisnode->name, fs->head);
59 thisnode->pr = fs->pr;
60 thisnode->need = fs->need;
61 thisnode->vendor = fs->vendor;
62 thisnode->flags = fs->flags;
63 thisnode->next = NULL;
67 /* That failed, so try it as a macro. */
68 ms = search_macro_array(spec);
70 format_node *list = NULL;
74 char buf[16]; /* trust strings will be short (from above, not user) */
77 dist = strcspn(walk, ", ");
78 strncpy(buf,walk,dist);
80 newnode = do_one_spec(buf,override); /* call self, assume success */
88 return NULL; /* bad, spec not found */
92 /************ must wrap user format in default *************/
93 static void O_wrap(sf_node *sfn, int otype){
98 trailer = (otype=='b') ? "END_BSD" : "END_SYS5" ;
100 fnode = do_one_spec("pid",NULL);
101 if(!fnode)fprintf(stderr,"Seriously crashing. Goodbye cruel world.\n");
102 endp = sfn->f_cooked; while(endp->next) endp = endp->next; /* find end */
105 fnode = do_one_spec(trailer,NULL);
106 if(!fnode)fprintf(stderr,"Seriously crashing. Goodbye cruel world.\n");
107 endp = fnode; while(endp->next) endp = endp->next; /* find end */
108 endp->next = sfn->f_cooked;
109 sfn->f_cooked = fnode;
112 /******************************************************************
113 * Used to parse option AIX field descriptors.
114 * Put each completed format_node onto the list starting at ->f_cooked
116 static const char *aix_format_parse(sf_node *sfn){
117 char *buf; /* temp copy of arg to hack on */
121 /*** sanity check and count items ***/
124 /* state machine */ {
128 if(c=='%') goto get_desc;
129 if(!c) goto looks_ok;
134 if(c=='%') goto get_desc;
135 if(c) goto get_more_text;
141 return "Improper AIX field descriptor.";
146 /*** sanity check passed ***/
147 buf = malloc(strlen(sfn->sf)+1);
148 strcpy(buf, sfn->sf);
152 format_node *fnode; /* newly allocated */
153 format_node *endp; /* for list manipulation */
156 const aix_struct *aix;
158 if(*walk == '%') goto double_percent;
159 aix = search_aix_array(*walk);
163 return "Unknown AIX field descriptor.";
165 fnode = do_one_spec(aix->spec, aix->head);
168 return "AIX field descriptor processing bug.";
172 len = strcspn(walk, "%");
173 memcpy(buf,walk,len);
181 fnode = malloc(sizeof(format_node));
183 fnode->name = malloc(len+1);
184 strcpy(fnode->name, buf);
185 fnode->pr = NULL; /* checked for */
188 fnode->flags = CF_PRINT_EVERY_TIME;
192 endp = fnode; while(endp->next) endp = endp->next; /* find end */
193 endp->next = sfn->f_cooked;
194 sfn->f_cooked = fnode;
197 already_parsed_format = 1;
201 /***************************************************************
202 * Used to parse option O lists. Option O is shared between
203 * sorting and formatting. Users may expect one or the other.
204 * The "broken" flag enables a really bad Unix98 misfeature.
205 * Put each completed format_node onto the list starting at ->f_cooked
207 static const char *format_parse(sf_node *sfn){
208 char *buf; /* temp copy of arg to hack on */
209 char *sep_loc; /* separator location: " \t,\n" */
211 const char *err; /* error code that could or did happen */
215 static char errbuf[80]; /* for variable-text error message */
217 /*** prepare to operate ***/
218 buf = malloc(strlen(sfn->sf)+1);
219 strcpy(buf, sfn->sf);
221 /*** sanity check and count items ***/
222 need_item = 1; /* true */
227 case ' ': case ',': case '\t': case '\n': case '\0':
228 /* Linux extension: allow \t and \n as delimiters */
239 if(need_item) items++;
249 if(need_item){ /* can't have trailing deliminator */
254 if(need_item){ /* allow 1 trailing deliminator */
255 *--walk='\0'; /* remove the trailing deliminator */
258 /*** actually parse the list ***/
264 sep_loc = strpbrk(walk," ,\t\n");
265 /* if items left, then sep_loc is not in header override */
266 if(items && sep_loc) *sep_loc = '\0';
267 equal_loc = strpbrk(walk,"=");
268 if(equal_loc){ /* if header override */
272 colon_loc = strpbrk(walk,":");
273 if(colon_loc){ /* if width override */
276 if(strspn(colon_loc,"0123456789") != strlen(colon_loc) || *colon_loc=='0' || !*colon_loc){
281 fnode = do_one_spec(walk,equal_loc);
283 if(!*errbuf){ /* if didn't already create an error string */
287 "Unknown user-defined format specifier \"%s\".",
299 // FIXME: enforce signal width to 8, 9, or 16 (grep: SIGNAL wide_signals)
300 fnode->width = atoi(colon_loc); // already verified to be a number
302 endp = fnode; while(endp->next) endp = endp->next; /* find end */
303 endp->next = sfn->f_cooked;
304 sfn->f_cooked = fnode;
305 walk = sep_loc + 1; /* point to next item, if any */
308 already_parsed_format = 1;
311 /* errors may cause a retry looking for AIX format codes */
312 if(0) unknown: err=errbuf;
313 if(0) empty: err="Empty format list.";
314 if(0) improper: err="Improper format list.";
315 if(0) badwidth: err="Column widths must be unsigned decimal numbers.";
316 if(0) notmacro: err="Can't set width for a macro (multi-column) format specifier.";
317 if(strchr(sfn->sf,'%')) err = aix_format_parse(sfn);
321 /**************** Parse single sort specifier *******************/
322 static sort_node *do_one_sort_spec(const char *spec){
323 const format_struct *fs;
328 } else if(*spec == '+'){
331 fs = search_format_array(spec);
334 thisnode = malloc(sizeof(sort_node));
335 thisnode->sr = fs->sr;
336 thisnode->need = fs->need;
337 thisnode->reverse = reverse;
338 thisnode->next = NULL;
341 return NULL; /* bad, spec not found */
345 /**************************************************************
346 * Used to parse long sorting options.
347 * Put each completed sort_node onto the list starting at ->s_cooked
349 static const char *long_sort_parse(sf_node *sfn){
350 char *buf; /* temp copy of arg to hack on */
351 char *sep_loc; /* separator location: " \t,\n" */
357 /*** prepare to operate ***/
358 buf = malloc(strlen(sfn->sf)+1);
359 strcpy(buf, sfn->sf);
361 /*** sanity check and count items ***/
362 need_item = 1; /* true */
367 case ' ': case ',': case '\t': case '\n': case '\0':
370 return "Improper sort list";
375 if(need_item) items++;
381 return "Empty sort list.";
384 if(need_item){ /* can't have trailing deliminator */
386 return "Improper sort list.";
389 if(need_item){ /* allow 1 trailing deliminator */
390 *--walk='\0'; /* remove the trailing deliminator */
393 /*** actually parse the list ***/
397 sep_loc = strpbrk(walk," ,\t\n");
398 if(sep_loc) *sep_loc = '\0';
399 snode = do_one_sort_spec(walk);
402 return "Unknown sort specifier.";
404 endp = snode; while(endp->next) endp = endp->next; /* find end */
405 endp->next = sfn->s_cooked;
406 sfn->s_cooked = snode;
407 walk = sep_loc + 1; /* point to next item, if any */
410 already_parsed_sort = 1;
419 /************ pre-parse short sorting option *************/
420 /* Errors _must_ be detected so that the "O" option can try to
421 * reparse as formatting codes.
423 static const char *verify_short_sort(const char *arg){
424 const char all[] = "CGJKMNPRSTUcfgjkmnoprstuvy+-";
429 if(strspn(arg,all) != strlen(arg)) return "Bad sorting code.";
430 for(i=256; i--;) checkoff[i] = 0;
436 return NULL; /* looks good */
440 if(!tmp || tmp=='+' || tmp=='-') return "Bad sorting code.";
443 if(forest_type) return "PPID sort and forest output conflict.";
446 if(checkoff[tmp]) return "Bad sorting code."; /* repeated */
447 /* ought to check against already accepted sort options */
457 /************ parse short sorting option *************/
458 static const char *short_sort_parse(sf_node *sfn){
464 const struct shortsort_struct *ss;
470 already_parsed_sort = 1;
479 ss = search_shortsort_array(tmp);
480 if(!ss) return "Unknown sort specifier.";
481 snode = do_one_sort_spec(ss->spec);
482 if(!snode) return "Unknown sort specifier.";
483 snode->reverse = direction;
484 endp = snode; while(endp->next) endp = endp->next; /* find end */
485 endp->next = sfn->s_cooked;
486 sfn->s_cooked = snode;
494 /******************* high-level below here *********************/
498 * Used to parse option O lists. Option O is shared between
499 * sorting and formatting. Users may expect one or the other.
500 * The "broken" flag enables a really bad Unix98 misfeature.
501 * Recursion is to preserve original order.
503 static const char *parse_O_option(sf_node *sfn){
504 const char *err; /* error code that could or did happen */
507 err = parse_O_option(sfn->next);
511 switch(sfn->sf_code){
512 case SF_B_o: case SF_G_format: case SF_U_o: /*** format ***/
513 err = format_parse(sfn);
514 if(!err) already_parsed_format = 1;
516 case SF_U_O: /*** format ***/
517 /* Can have -l -f f u... set already_parsed_format like DEC does */
518 if(already_parsed_format) return "option -O can not follow other format options.";
519 err = format_parse(sfn);
521 already_parsed_format = 1;
522 O_wrap(sfn,'u'); /* must wrap user format in default */
524 case SF_B_O: /*** both ***/
525 if(have_gnu_sort || already_parsed_sort) err = "Multiple sort options.";
526 else err = verify_short_sort(sfn->sf);
527 if(!err){ /* success as sorting code */
528 short_sort_parse(sfn);
529 already_parsed_sort = 1;
532 if(already_parsed_format){
533 err = "option O is neither first format nor sort order.";
536 if(!format_parse(sfn)){ /* if success as format code */
537 already_parsed_format = 1;
538 O_wrap(sfn,'b'); /* must wrap user format in default */
542 case SF_G_sort: case SF_B_m: /*** sort ***/
543 if(already_parsed_sort) err = "Multiple sort options.";
544 else err = long_sort_parse(sfn);
545 already_parsed_sort = 1;
547 default: /*** junk ***/
548 return "Bug: parse_O_option got weirdness!";
550 return err; /* could be NULL */
554 /************ Main parser calls this to save lists for later **********/
555 /* store data for later and return 1 if arg looks non-standard */
556 int defer_sf_option(const char *arg, int source){
560 const format_struct *fs;
563 sfn = malloc(sizeof(sf_node));
564 sfn->sf = malloc(strlen(arg)+1);
565 strcpy(sfn->sf, arg);
566 sfn->sf_code = source;
567 sfn->s_cooked = NULL;
568 sfn->f_cooked = NULL;
572 if(source == SF_G_sort) have_gnu_sort = 1;
574 /* Now try to find an excuse to ignore broken Unix98 parsing. */
575 if(source != SF_U_o) return 1; /* Wonderful! Already non-Unix98. */
578 case ' ': case ',': case '\0': /* no \t\n\r support in Unix98 */
579 if(need_item) return 1; /* something wrong */
583 if(need_item) return 1; /* something wrong */
584 return 0; /* broken Unix98 parsing is required */
586 if(!need_item) break;
588 dist = strcspn(arg,", =");
589 if(dist>15) return 1; /* something wrong, sort maybe? */
590 strncpy(buf,arg,dist); /* no '\0' on end */
591 buf[dist] = '\0'; /* fix that problem */
592 fs = search_format_array(buf);
593 if(!fs) return 1; /* invalid spec, macro or sort maybe? */
594 if(fs->vendor) return 1; /* Wonderful! Legal non-Unix98 spec. */
598 return 0; /* boring, Unix98 is no change */
601 /***** Since ps is not long-lived, the memory leak can be ignored. ******/
602 void reset_sortformat(void){
603 sf_list = NULL; /* deferred sorting and formatting */
604 format_list = NULL; /* digested formatting options */
605 sort_list = NULL; /* digested sorting options (redundant?) */
607 already_parsed_sort = 0;
608 already_parsed_format = 0;
612 /***** Search format_list for findme, then insert putme after findme. ****/
613 static int fmt_add_after(const char *findme, format_node *putme){
615 if(!strcmp(format_list->name, findme)){
616 putme->next = format_list->next;
617 format_list->next = putme;
618 return 1; /* success */
622 if(!strcmp(walk->next->name, findme)){
623 putme->next = walk->next->next;
624 walk->next->next = putme;
625 return 1; /* success */
632 /******* Search format_list for findme, then delete it. ********/
633 static int fmt_delete(const char *findme){
636 if(!strcmp(format_list->name, findme)){
638 format_list = format_list->next;
640 return 1; /* success */
644 if(!strcmp(walk->next->name, findme)){
646 walk->next = walk->next->next;
648 return 1; /* success */
656 /************ Build a SysV format backwards. ***********/
657 #define PUSH(foo) (fn=do_one_spec(foo, NULL), fn->next=format_list, format_list=fn)
658 static const char *generate_sysv_list(void){
660 if((format_modifiers & FM_y) && !(format_flags & FF_Ul))
661 return "Modifier -y without format -l makes no sense.";
662 if(prefer_bsd_defaults){
663 if(format_flags) PUSH("cmd");
666 if(!(format_flags & FF_Ul)) PUSH("stat");
668 if(format_flags & FF_Uf) PUSH("cmd");
672 PUSH("tname"); /* Unix98 says "TTY" here, yet "tty" produces "TT". */
673 if(format_flags & FF_Uf) PUSH("stime");
674 /* avoid duplicate columns from -FP and -Fly */
675 if(format_modifiers & FM_F){
676 /* if -FP take the Sun-style column instead (sorry about "sgi_p") */
677 if(!(format_modifiers & FM_P)) PUSH("psr"); /* should be ENG */
678 /* if -Fly take the ADDR-replacement RSS instead */
679 if(!( (format_flags & FF_Ul) && (format_modifiers & FM_y) )) PUSH("rss");
681 if(format_flags & FF_Ul){
684 /* since FM_y adds RSS anyway, don't do this hack when that is true */
685 if( (format_flags & FF_Ul) && !(format_modifiers & FM_y) ){
686 if(personality & PER_IRIX_l){ /* add "rss" then ':' here */
688 fn = malloc(sizeof(format_node));
690 fn->name = malloc(2);
691 strcpy(fn->name, ":");
692 fn->pr = NULL; /* checked for */
694 fn->vendor = AIX; /* yes, for SGI weirdness */
695 fn->flags = CF_PRINT_EVERY_TIME;
696 fn->next = format_list;
700 if((format_modifiers & FM_F) || (format_flags & FF_Ul)){
703 if(format_flags & FF_Ul){
704 if(format_modifiers & FM_y) PUSH("rss");
705 else if(personality & (PER_ZAP_ADDR|PER_IRIX_l)) PUSH("sgi_p");
708 if(format_modifiers & FM_c){
709 PUSH("pri"); PUSH("class");
710 }else if(format_flags & FF_Ul){
712 if(personality & PER_IRIX_l) PUSH("priority");
713 else /* is this good? */ PUSH("opri");
716 // FIXME TODO XXX -- this is a serious problem
717 // These somehow got flipped around.
718 // The bug is in procps-3.1.1, procps-990211, prior too?
719 if((thread_flags & TF_U_L) && (format_flags & FF_Uf)) PUSH("nlwp");
720 if( (format_flags & (FF_Uf|FF_Ul)) && !(format_modifiers & FM_c) ) PUSH("c");
722 if(format_modifiers & FM_P) PUSH("psr");
723 if(thread_flags & TF_U_L) PUSH("lwp");
724 if(format_modifiers & FM_j){
728 if(format_flags & (FF_Uf|FF_Ul)) PUSH("ppid");
729 if(thread_flags & TF_U_T) PUSH("spid");
731 if(format_flags & FF_Uf){
732 if(personality & PER_SANE_USER) PUSH("user");
733 else PUSH("uid_hack");
734 }else if(format_flags & FF_Ul){
737 if(format_flags & FF_Ul){
739 if(!(format_modifiers & FM_y)) PUSH("f");
741 if(format_modifiers & FM_M){
742 PUSH("label"); /* Mandatory Access Control */
748 /**************************************************************************
749 * Used to parse option O lists. Option O is shared between
750 * sorting and formatting. Users may expect one or the other.
751 * The "broken" flag enables a really bad Unix98 misfeature.
753 const char *process_sf_options(int localbroken){
756 if(personality & PER_BROKEN_o) localbroken = 1;
757 if(personality & PER_GOOD_o) localbroken = 0;
758 broken = localbroken;
761 err = parse_O_option(sf_list);
765 if(format_list) printf("Bug: must reset the list first!\n");
767 /* merge formatting info of sf_list into format_list here */
770 format_node *fmt_walk;
771 fmt_walk = sf_walk->f_cooked;
772 sf_walk->f_cooked = NULL;
773 while(fmt_walk){ /* put any nodes onto format_list in opposite way */
774 format_node *travler;
776 fmt_walk = fmt_walk->next;
777 travler->next = format_list;
778 format_list = travler;
780 sf_walk = sf_walk->next;
783 /* merge sorting info of sf_list into sort_list here */
787 srt_walk = sf_walk->s_cooked;
788 sf_walk->s_cooked = NULL;
789 while(srt_walk){ /* put any nodes onto sort_list in opposite way */
792 srt_walk = srt_walk->next;
793 travler->next = sort_list;
796 sf_walk = sf_walk->next;
799 // Get somebody to explain how -L/-T is supposed to interact
800 // with sorting. Do the threads remain grouped, with sorting
801 // by process, or do the threads get sorted by themselves?
802 if(sort_list && (thread_flags&TF_no_sort)){
803 return "Tell procps-feedback@lists.sf.net what you expected.";
806 // If nothing else, try to use $PS_FORMAT before the default.
807 if(!format_flags && !format_modifiers && !format_list){
809 tmp = getenv("PS_FORMAT"); /* user override kills default */
813 if(thread_flags&TF_must_use) return "Tell procps-feedback@sf.net what you want. (-L/-T, -m/m/H, and $PS_FORMAT)";
816 err = format_parse(&sfn);
818 format_node *fmt_walk;
819 fmt_walk = sfn.f_cooked;
820 while(fmt_walk){ /* put any nodes onto format_list in opposite way */
821 format_node *travler;
823 fmt_walk = fmt_walk->next;
824 travler->next = format_list;
825 format_list = travler;
829 // FIXME: prove that this won't be hit on valid bogus-BSD options
830 fprintf(stderr, "Warning: $PS_FORMAT ignored. (%s)\n", err);
835 if(format_flags) return "Conflicting format options.";
836 if(format_modifiers) return "Can't use output modifiers with user-defined output";
837 if(thread_flags&TF_must_use) return "-L/-T with H/m/-m and -o/-O/o/O is nonsense";
843 switch(format_flags){
845 default: return "Conflicting format options.";
847 /* These can be NULL, which enables SysV list generation code. */
848 case 0: spec=NULL; break;
849 case FF_Uf | FF_Ul: spec=sysv_fl_format; break;
850 case FF_Uf: spec=sysv_f_format; break;
851 case FF_Ul: spec=sysv_l_format; break;
853 /* These are NOT REACHED for normal -j processing. */
854 case FF_Uj: spec=sysv_j_format; break; /* Debian & Digital */
855 case FF_Uj | FF_Ul: spec="RD_lj"; break; /* Debian */
856 case FF_Uj | FF_Uf: spec="RD_fj"; break; /* Debian */
858 /* These are true BSD options. */
859 case FF_Bj: spec=bsd_j_format; break;
860 case FF_Bl: spec=bsd_l_format; break;
861 case FF_Bs: spec=bsd_s_format; break;
862 case FF_Bu: spec=bsd_u_format; break;
863 case FF_Bv: spec=bsd_v_format; break;
865 /* These are old Linux options. Option m is overloaded. */
866 case FF_LX: spec="OL_X"; break;
867 case FF_Lm: spec="OL_m"; break;
869 /* This is the sole FLASK security option. */
870 case FF_Fc: spec="FLASK_context"; break;
872 } /* end switch(format_flags) */
874 // not just for case 0, since sysv_l_format and such may be NULL
875 if(!spec) return generate_sysv_list();
878 format_node *fmt_walk;
879 fmt_walk = do_one_spec(spec, NULL); /* use override "" for no headers */
880 while(fmt_walk){ /* put any nodes onto format_list in opposite way */
881 format_node *travler;
883 fmt_walk = fmt_walk->next;
884 travler->next = format_list;
885 format_list = travler;
892 if(format_modifiers & FM_j){
893 fn = do_one_spec("pgid", NULL);
894 if(!fmt_add_after("PPID", fn)) if(!fmt_add_after("PID", fn))
895 return "Internal error, no PID or PPID for -j option.";
896 fn = do_one_spec("sid", NULL);
897 if(!fmt_add_after("PGID", fn)) return "Lost my PGID!";
899 if(format_modifiers & FM_y){
900 /* TODO: check for failure to do something, and complain if so */
902 fn = do_one_spec("rss", NULL);
903 if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR");
905 if(format_modifiers & FM_c){
906 fmt_delete("%CPU"); fmt_delete("CPU"); fmt_delete("CP"); fmt_delete("C");
908 fn = do_one_spec("class", NULL);
909 if(!fmt_add_after("PRI", fn))
910 return "Internal error, no PRI for -c option.";
911 fmt_delete("PRI"); /* we want a different one */
912 fn = do_one_spec("pri", NULL);
913 if(!fmt_add_after("CLS", fn)) return "Lost my CLS!";
915 if(thread_flags & TF_U_T){
916 fn = do_one_spec("spid", NULL);
917 if(!fmt_add_after("PID", fn) && (thread_flags&TF_must_use))
918 return "-T with H/-m/m but no PID for SPID to follow";
920 if(thread_flags & TF_U_L){
921 fn = do_one_spec("lwp", NULL);
922 if(fmt_add_after("SID", fn)) goto did_lwp;
923 if(fmt_add_after("SESS", fn)) goto did_lwp;
924 if(fmt_add_after("PGID", fn)) goto did_lwp;
925 if(fmt_add_after("PGRP", fn)) goto did_lwp;
926 if(fmt_add_after("PPID", fn)) goto did_lwp;
927 if(fmt_add_after("PID", fn)) goto did_lwp;
928 if(thread_flags&TF_must_use)
929 return "-L with H/-m/m but no PID/PGID/SID/SESS for NLWP to follow";
931 fn = do_one_spec("nlwp", NULL);
932 fmt_add_after("%CPU", fn);
934 if(format_modifiers & FM_M){ // Mandatory Access Control, IRIX style
935 fn = do_one_spec("label", NULL);
936 fn->next=format_list;
939 /* Do personality-specific translations not covered by format_flags.
940 * Generally, these only get hit when personality overrides unix output.
941 * That (mostly?) means the Digital and Debian personalities.
943 if((personality & PER_ZAP_ADDR) && (format_flags & FF_Ul)){
944 fn = do_one_spec("sgi_p", NULL);
945 if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR");
947 if((personality & PER_SANE_USER) && (format_flags & FF_Uf)){
948 fn = do_one_spec("user", NULL);
949 if(fmt_add_after("UID", fn)) fmt_delete("UID");