1243 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1243 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * parser.c - ps command options parser
 | 
						|
 * Copyright 1998-2003 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
 | 
						|
 */
 | 
						|
 | 
						|
/* Ought to have debug print stuff like this:
 | 
						|
 * #define Print(fmt, args...) printf("Debug: " fmt, ## args)
 | 
						|
 */
 | 
						|
 | 
						|
#include <grp.h>
 | 
						|
#include <pwd.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <unistd.h>
 | 
						|
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <sys/types.h>
 | 
						|
 | 
						|
#include "../include/c.h"
 | 
						|
#include "../include/xalloc.h"
 | 
						|
 | 
						|
#include "common.h"
 | 
						|
 | 
						|
#define ARG_GNU  0
 | 
						|
#define ARG_END  1
 | 
						|
#define ARG_PGRP 2
 | 
						|
#define ARG_SYSV 3
 | 
						|
#define ARG_PID  4
 | 
						|
#define ARG_BSD  5
 | 
						|
#define ARG_FAIL 6
 | 
						|
#define ARG_SESS 7
 | 
						|
 | 
						|
static int w_count = 0;
 | 
						|
 | 
						|
static int ps_argc;    /* global argc */
 | 
						|
static char **ps_argv; /* global argv */
 | 
						|
static int thisarg;    /* index into ps_argv */
 | 
						|
static char *flagptr;  /* current location in ps_argv[thisarg] */
 | 
						|
static int force_bsd = 0;  /* set when normal parsing fails */
 | 
						|
 | 
						|
#define exclusive(x) if((ps_argc != 2) || strcmp(ps_argv[1],x)) \
 | 
						|
  return _("the option is exclusive: " x)
 | 
						|
 | 
						|
/********** utility functions **********/
 | 
						|
static void display_ps_version(void)
 | 
						|
{
 | 
						|
    fprintf(stdout, PROCPS_NG_VERSION);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * Both "-Oppid" and "-O ppid" should be legal, though Unix98
 | 
						|
 * does not require it. BSD and Digital Unix allow both.
 | 
						|
 * Return the argument or NULL;
 | 
						|
 */
 | 
						|
static const char *get_opt_arg(void){
 | 
						|
  if(*(flagptr+1)){     /* argument is part of ps_argv[thisarg] */
 | 
						|
    return flagptr+1;
 | 
						|
  }
 | 
						|
  if(thisarg+2 > ps_argc) return NULL;   /* there is nothing left */
 | 
						|
  /* argument follows ps_argv[thisarg] */
 | 
						|
  if(*(ps_argv[thisarg+1]) == '\0') return NULL;
 | 
						|
  return ps_argv[++thisarg];
 | 
						|
}
 | 
						|
 | 
						|
/********** parse lists (of UID, tty, GID, PID...) **********/
 | 
						|
 | 
						|
static const char *parse_pid(char *str, sel_union *ret){
 | 
						|
  char *endp;
 | 
						|
  unsigned long num;
 | 
						|
  num = strtoul(str, &endp, 0);
 | 
						|
  if(*endp != '\0')      return _("process ID list syntax error");
 | 
						|
  if(num<1)              return _("process ID out of range");
 | 
						|
  if(num > 0x7fffffffUL) return _("process ID out of range");
 | 
						|
  ret->pid = num;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static const char *parse_uid(char *str, sel_union *ret){
 | 
						|
  struct passwd *passwd_data;
 | 
						|
  char *endp;
 | 
						|
  unsigned long num;
 | 
						|
  num = strtoul(str, &endp, 0);
 | 
						|
  if(*endp != '\0'){  /* hmmm, try as login name */
 | 
						|
    passwd_data = getpwnam(str);
 | 
						|
    if(!passwd_data){
 | 
						|
      if(!negate_selection) return _("user name does not exist");
 | 
						|
      num = -1;
 | 
						|
    }
 | 
						|
    else
 | 
						|
      num = passwd_data->pw_uid;
 | 
						|
  }
 | 
						|
  if(!negate_selection && (num > 0xfffffffeUL)) return _("user ID out of range");
 | 
						|
  ret->uid = num;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static const char *parse_gid(char *str, sel_union *ret){
 | 
						|
  struct group *group_data;
 | 
						|
  char *endp;
 | 
						|
  unsigned long num;
 | 
						|
  num = strtoul(str, &endp, 0);
 | 
						|
  if(*endp != '\0'){  /* hmmm, try as login name */
 | 
						|
    group_data = getgrnam(str);
 | 
						|
    if(!group_data){
 | 
						|
      if(!negate_selection) return _("group name does not exist");
 | 
						|
      num = -1;
 | 
						|
    }
 | 
						|
    else
 | 
						|
      num = group_data->gr_gid;
 | 
						|
  }
 | 
						|
  if(!negate_selection && (num > 0xfffffffeUL)) return _("group ID out of range");
 | 
						|
  ret->gid = num;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static const char *parse_cmd(char *str, sel_union *ret){
 | 
						|
  strncpy(ret->cmd, str, sizeof ret->cmd);  // strncpy pads to end
 | 
						|
  ret->cmd[sizeof(ret->cmd)-1] = '\0';      // but let's be safe
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static const char *parse_tty(char *str, sel_union *ret){
 | 
						|
  struct stat sbuf;
 | 
						|
  char path[4096];
 | 
						|
  if(str[0]=='/'){
 | 
						|
    if(stat(str, &sbuf) >= 0) goto found_it;
 | 
						|
    return _("TTY could not be found");
 | 
						|
  }
 | 
						|
#define lookup(p) \
 | 
						|
  snprintf(path,4096,p,str); \
 | 
						|
  if(stat(path, &sbuf) >= 0) goto found_it
 | 
						|
 | 
						|
  lookup("/dev/pts/%s");  /* New Unix98 ptys go first */
 | 
						|
  lookup("/dev/%s");
 | 
						|
  lookup("/dev/tty%s");
 | 
						|
  lookup("/dev/pty%s");
 | 
						|
  lookup("/dev/%snsole"); /* "co" means "console", maybe do all VCs too? */
 | 
						|
  if(!strcmp(str,"-")){   /* "-" means no tty (from AIX) */
 | 
						|
    ret->tty = 0;  /* processes w/o tty */
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  if(!strcmp(str,"?")){   /* "?" means no tty, which bash eats (Reno BSD?) */
 | 
						|
    ret->tty = 0;  /* processes w/o tty */
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  if(!*(str+1) && (stat(str,&sbuf)>=0)){  /* Kludge! Assume bash ate '?'. */
 | 
						|
    ret->tty = 0;  /* processes w/o tty */
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
#undef lookup
 | 
						|
  return _("TTY could not be found");
 | 
						|
found_it:
 | 
						|
  if(!S_ISCHR(sbuf.st_mode)) return _("list member was not a TTY");
 | 
						|
  ret->tty = sbuf.st_rdev;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Used to parse lists in a generic way. (function pointers)
 | 
						|
 */
 | 
						|
static const char *parse_list(const char *arg, const char *(*parse_fn)(char *, sel_union *) ){
 | 
						|
  selection_node *node;
 | 
						|
  char *buf;                      /* temp copy of arg to hack on */
 | 
						|
  char *sep_loc;                  /* separator location: " \t," */
 | 
						|
  char *walk;
 | 
						|
  int items;
 | 
						|
  int need_item;
 | 
						|
  const char *err;       /* error code that could or did happen */
 | 
						|
  /*** prepare to operate ***/
 | 
						|
  node = xmalloc(sizeof(selection_node));
 | 
						|
  node->u = xmalloc(strlen(arg)*sizeof(sel_union)); /* waste is insignificant */
 | 
						|
  node->n = 0;
 | 
						|
  buf = strdup(arg);
 | 
						|
  /*** sanity check and count items ***/
 | 
						|
  need_item = 1; /* true */
 | 
						|
  items = 0;
 | 
						|
  walk = buf;
 | 
						|
  err = _("improper list");
 | 
						|
  do{
 | 
						|
    switch(*walk){
 | 
						|
    case ' ': case ',': case '\t': case '\0':
 | 
						|
      if(need_item) goto parse_error;
 | 
						|
      need_item=1;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      if(need_item) items++;
 | 
						|
      need_item=0;
 | 
						|
    }
 | 
						|
  } while (*++walk);
 | 
						|
  if(need_item) goto parse_error;
 | 
						|
  node->n = items;
 | 
						|
  /*** actually parse the list ***/
 | 
						|
  walk = buf;
 | 
						|
  while(items--){
 | 
						|
    sep_loc = strpbrk(walk," ,\t");
 | 
						|
    if(sep_loc) *sep_loc = '\0';
 | 
						|
    if(( err=(parse_fn)(walk, node->u+items) )) goto parse_error;
 | 
						|
    walk = sep_loc + 1; /* point to next item, if any */
 | 
						|
  }
 | 
						|
  free(buf);
 | 
						|
  node->next = selection_list;
 | 
						|
  selection_list = node;
 | 
						|
  return NULL;
 | 
						|
parse_error:
 | 
						|
  free(buf);
 | 
						|
  free(node->u);
 | 
						|
  free(node);
 | 
						|
  return err;
 | 
						|
}
 | 
						|
 | 
						|
/***************** parse SysV options, including Unix98  *****************/
 | 
						|
static const char *parse_sysv_option(void){
 | 
						|
  const char *arg;
 | 
						|
  const char *err;
 | 
						|
 | 
						|
  flagptr = ps_argv[thisarg];
 | 
						|
  while(*++flagptr){
 | 
						|
    switch(*flagptr){
 | 
						|
    case 'A':
 | 
						|
      trace("-A selects all processes\n");
 | 
						|
      all_processes = 1;
 | 
						|
      break;
 | 
						|
    case 'C': /* end */
 | 
						|
      trace("-C select by process name\n");  /* Why only HP/UX and us? */
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return _("list of command names must follow -C");
 | 
						|
      err=parse_list(arg, parse_cmd);
 | 
						|
      if(err) return err;
 | 
						|
      selection_list->typecode = SEL_COMM;
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
    case 'F':  /* DYNIX/ptx -f plus sz,rss,psr=ENG between c and stime */
 | 
						|
      trace("-F does fuller listing\n");
 | 
						|
      format_modifiers |= FM_F;
 | 
						|
      format_flags |= FF_Uf;
 | 
						|
      unix_f_option = 1; /* does this matter? */
 | 
						|
      break;
 | 
						|
    case 'G': /* end */
 | 
						|
      trace("-G select by RGID (supports names)\n");
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return _("list of real groups must follow -G");
 | 
						|
      err=parse_list(arg, parse_gid);
 | 
						|
      if(err) return err;
 | 
						|
      selection_list->typecode = SEL_RGID;
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
    case 'H':     /* another nice HP/UX feature */
 | 
						|
      trace("-H process hierarchy (like ASCII art forest option)\n");
 | 
						|
      forest_type = 'u';
 | 
						|
      break;
 | 
						|
#if 0
 | 
						|
    case 'J':  // specify list of job IDs in hex (IRIX) -- like HP "-R" maybe?
 | 
						|
      trace("-J select by job ID\n");  // want a JID ("jid") for "-j" too
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return _("list of jobs must follow -J");
 | 
						|
      err=parse_list(arg, parse_jid);
 | 
						|
      if(err) return err;
 | 
						|
      selection_list->typecode = SEL_JID;
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
#endif
 | 
						|
    case 'L':  /*  */
 | 
						|
      /* In spite of the insane 2-level thread system, Sun appears to
 | 
						|
       * have made this option Linux-compatible. If a process has N
 | 
						|
       * threads, ps will produce N lines of output. (not N+1 lines)
 | 
						|
       * Zombies are the only exception, with NLWP==0 and 1 output line.
 | 
						|
       * SCO UnixWare uses -L too.
 | 
						|
       */
 | 
						|
      trace("-L print LWP (thread) info\n");
 | 
						|
      thread_flags |= TF_U_L;
 | 
						|
//      format_modifiers |= FM_L;
 | 
						|
      break;
 | 
						|
    case 'M':  // typically the SELinux context
 | 
						|
      trace("-M print security label for Mandatory Access Control\n");
 | 
						|
      format_modifiers |= FM_M;
 | 
						|
      break;
 | 
						|
    case 'N':
 | 
						|
      trace("-N negates\n");
 | 
						|
      negate_selection = 1;
 | 
						|
      break;
 | 
						|
    case 'O': /* end */
 | 
						|
      trace("-O is preloaded -o\n");
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return _("format or sort specification must follow -O");
 | 
						|
      defer_sf_option(arg, SF_U_O);
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
    case 'P':     /* SunOS 5 "psr" or unknown HP/UX feature */
 | 
						|
      trace("-P adds columns of PRM info (HP-UX), PSR (SunOS), or capabilities (IRIX)\n");
 | 
						|
      format_modifiers |= FM_P;
 | 
						|
      break;
 | 
						|
#if 0
 | 
						|
    case 'R':    // unknown HP/UX feature, like IRIX "-J" maybe?
 | 
						|
      trace("-R select by PRM group\n");
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return _("list of PRM groups must follow -R");
 | 
						|
      err=parse_list(arg, parse_prm);
 | 
						|
      if(err) return err;
 | 
						|
      selection_list->typecode = SEL_PRM;
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
#endif
 | 
						|
    case 'T':
 | 
						|
      /* IRIX 6.5 docs suggest POSIX threads get shown individually.
 | 
						|
       * This would make -T be like -L, -m, and m. (but an extra column)
 | 
						|
       * Testing (w/ normal processes) shows 1 line/process, not 2.
 | 
						|
       * Also, testing shows PID==SPID for all normal processes.
 | 
						|
       */
 | 
						|
      trace("-T adds strange SPID column (old sproc() threads?)\n");
 | 
						|
      thread_flags |= TF_U_T;
 | 
						|
//      format_modifiers |= FM_T;
 | 
						|
      break;
 | 
						|
    case 'U': /* end */
 | 
						|
      trace("-U select by RUID (supports names)\n");
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return _("list of real users must follow -U");
 | 
						|
      err=parse_list(arg, parse_uid);
 | 
						|
      if(err) return err;
 | 
						|
      selection_list->typecode = SEL_RUID;
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
    case 'V': /* single */
 | 
						|
      trace("-V prints version\n");
 | 
						|
      exclusive("-V");
 | 
						|
      display_ps_version();
 | 
						|
      exit(0);
 | 
						|
    // This must be verified against SVR4-MP. (UnixWare or Powermax)
 | 
						|
    // Leave it undocumented until that problem is solved.
 | 
						|
    case 'Z':     /* full Mandatory Access Control level info */
 | 
						|
      trace("-Z shows full MAC info\n");
 | 
						|
      format_modifiers |= FM_M;
 | 
						|
      break;
 | 
						|
    case 'a':
 | 
						|
      trace("-a select all with a tty, but omit session leaders\n");
 | 
						|
      simple_select |= SS_U_a;
 | 
						|
      break;
 | 
						|
    case 'c':
 | 
						|
      /* HP-UX and SunOS 5 scheduling info modifier */
 | 
						|
      trace("-c changes scheduling info\n");
 | 
						|
      format_modifiers |= FM_c;
 | 
						|
      break;
 | 
						|
    case 'd':
 | 
						|
      trace("-d select all, but omit session leaders\n");
 | 
						|
      simple_select |= SS_U_d;
 | 
						|
      break;
 | 
						|
    case 'e':
 | 
						|
      trace("-e selects all processes\n");
 | 
						|
      all_processes = 1;
 | 
						|
      break;
 | 
						|
    case 'f':
 | 
						|
      trace("-f does full listing\n");
 | 
						|
      format_flags |= FF_Uf;
 | 
						|
      unix_f_option = 1; /* does this matter? */
 | 
						|
      break;
 | 
						|
    case 'g': /* end */
 | 
						|
      trace("-g selects by session leader OR by group name\n");
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return _("list of session leaders OR effective group names must follow -g");
 | 
						|
      err=parse_list(arg, parse_pid);
 | 
						|
      if(!err){
 | 
						|
        selection_list->typecode = SEL_SESS;
 | 
						|
        return NULL; /* can't have any more options */
 | 
						|
      }
 | 
						|
      err=parse_list(arg, parse_gid);
 | 
						|
      if(!err){
 | 
						|
        selection_list->typecode = SEL_EGID;
 | 
						|
        return NULL; /* can't have any more options */
 | 
						|
      }
 | 
						|
      return _("list of session leaders OR effective group IDs was invalid");
 | 
						|
    case 'j':
 | 
						|
      trace("-j jobs format\n");
 | 
						|
      /* old Debian used RD_j and Digital uses JFMT */
 | 
						|
      if(sysv_j_format) format_flags |= FF_Uj;
 | 
						|
      else format_modifiers |= FM_j;
 | 
						|
      break;
 | 
						|
    case 'l':
 | 
						|
      trace("-l long format\n");
 | 
						|
      format_flags |= FF_Ul;
 | 
						|
      break;
 | 
						|
    case 'm':
 | 
						|
      trace("-m shows threads\n");
 | 
						|
      /* note that AIX shows 2 lines for a normal process */
 | 
						|
      thread_flags |= TF_U_m;
 | 
						|
      break;
 | 
						|
    case 'o': /* end */
 | 
						|
      /* Unix98 has gross behavior regarding this. From the following: */
 | 
						|
      /*            ps -o pid,nice=NICE,tty=TERMINAL,comm              */
 | 
						|
      /* The result must be 2 columns: "PID NICE,tty=TERMINAL,comm"    */
 | 
						|
      /* Yes, the second column has the name "NICE,tty=TERMINAL,comm"  */
 | 
						|
      /* This parser looks for any excuse to ignore that braindamage.  */
 | 
						|
      trace("-o user-defined format\n");
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return _("format specification must follow -o");
 | 
						|
      defer_sf_option(arg, SF_U_o);
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
    case 'p': /* end */
 | 
						|
      trace("-p select by PID\n");
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return _("list of process IDs must follow -p");
 | 
						|
      err=parse_list(arg, parse_pid);
 | 
						|
      if(err) return err;
 | 
						|
      selection_list->typecode = SEL_PID;
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
    case 'q': /* end */
 | 
						|
      trace("-q quick select by PID.\n");
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return "List of process IDs must follow -q.";
 | 
						|
      err=parse_list(arg, parse_pid);
 | 
						|
      if(err) return err;
 | 
						|
      selection_list->typecode = SEL_PID_QUICK;
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
#if 0
 | 
						|
    case 'r':
 | 
						|
      trace("-r some Digital Unix thing about warnings...\n");
 | 
						|
      trace("   or SCO's option to chroot() for new /proc and /dev\n");
 | 
						|
      return _("the -r option is reserved");
 | 
						|
      break;
 | 
						|
#endif
 | 
						|
    case 's': /* end */
 | 
						|
      trace("-s select processes belonging to the sessions given\n");
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return _("list of session IDs must follow -s");
 | 
						|
      err=parse_list(arg, parse_pid);
 | 
						|
      if(err) return err;
 | 
						|
      selection_list->typecode = SEL_SESS;
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
    case 't': /* end */
 | 
						|
      trace("-t select by tty\n");
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return _("list of terminals (pty, tty...) must follow -t");
 | 
						|
      err=parse_list(arg, parse_tty);
 | 
						|
      if(err) return err;
 | 
						|
      selection_list->typecode = SEL_TTY;
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
    case 'u': /* end */
 | 
						|
      trace("-u select by user effective ID (supports names)\n");
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return _("list of users must follow -u");
 | 
						|
      err=parse_list(arg, parse_uid);
 | 
						|
      if(err) return err;
 | 
						|
      selection_list->typecode = SEL_EUID;
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
    case 'w':
 | 
						|
      trace("-w wide output\n");
 | 
						|
      w_count++;
 | 
						|
      break;
 | 
						|
    case 'x':  /* behind personality until "ps -ax" habit is uncommon */
 | 
						|
      if(personality & PER_SVR4_x){
 | 
						|
        // Same as -y, but for System V Release 4 MP
 | 
						|
        trace("-x works like Sun Solaris & SCO Unixware -y option\n");
 | 
						|
        format_modifiers |= FM_y;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      if(personality & PER_HPUX_x){
 | 
						|
        trace("-x extends the command line\n");
 | 
						|
        w_count += 2;
 | 
						|
        unix_f_option = 1;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      return _("must set personality to get -x option");
 | 
						|
    case 'y':  /* Sun's -l hack (also: Irix "lnode" resource control info) */
 | 
						|
      trace("-y print lnone info in UID/USER column or do Sun -l hack\n");
 | 
						|
      format_modifiers |= FM_y;
 | 
						|
      break;
 | 
						|
#if 0
 | 
						|
    // This must be verified against SVR4-MP (UnixWare or Powermax)
 | 
						|
    case 'z':     /* alias of Mandatory Access Control level info */
 | 
						|
      trace("-z shows aliased MAC info\n");
 | 
						|
      format_modifiers |= FM_M;
 | 
						|
      break;
 | 
						|
    // Solaris 10 does this
 | 
						|
    case 'z':     /* select by zone */
 | 
						|
      trace("-z secects by zone\n");
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return _("list of zones (contexts, labels, whatever?) must follow -z");
 | 
						|
      err=parse_list(arg, parse_zone);
 | 
						|
      if(err) return err;
 | 
						|
      selection_list->typecode = SEL_ZONE;
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
#endif
 | 
						|
    case '-':
 | 
						|
      return _("embedded '-' among SysV options makes no sense");
 | 
						|
      break;
 | 
						|
    case '\0':
 | 
						|
      catastrophic_failure(__FILE__, __LINE__, _("please report this bug"));
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      return _("unsupported SysV option");
 | 
						|
    } /* switch */
 | 
						|
  } /* while */
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/************************* parse BSD options **********************/
 | 
						|
static const char *parse_bsd_option(void){
 | 
						|
  const char *arg;
 | 
						|
  const char *err;
 | 
						|
 | 
						|
  flagptr = ps_argv[thisarg];  /* assume we _have_ a '-' */
 | 
						|
  if(flagptr[0]=='-'){
 | 
						|
    if(!force_bsd) return _("cannot happen - problem #1");
 | 
						|
  }else{
 | 
						|
    flagptr--; /* off beginning, will increment before use */
 | 
						|
    if(personality & PER_FORCE_BSD){
 | 
						|
      if(!force_bsd) return _("cannot happen - problem #2");
 | 
						|
    }else{
 | 
						|
      if(force_bsd) return _("second chance parse failed, not BSD or SysV");
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  while(*++flagptr){
 | 
						|
    switch(*flagptr){
 | 
						|
    case '0' ... '9': /* end */
 | 
						|
      trace("0..9  pld BSD-style select by process ID\n");
 | 
						|
      arg=flagptr;
 | 
						|
      err=parse_list(arg, parse_pid);
 | 
						|
      if(err) return err;
 | 
						|
      selection_list->typecode = SEL_PID;
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
#if 0
 | 
						|
    case 'A':
 | 
						|
      /* maybe this just does a larger malloc() ? */
 | 
						|
      trace("A increases the argument space (Digital Unix)\n");
 | 
						|
      return _("option A is reserved");
 | 
						|
      break;
 | 
						|
    case 'C':
 | 
						|
      /* should divide result by 1-(e**(foo*log(bar))) */
 | 
						|
      trace("C use raw CPU time for %%CPU instead of decaying ave\n");
 | 
						|
      return _("option C is reserved");
 | 
						|
      break;
 | 
						|
#endif
 | 
						|
    case 'H':    // The FreeBSD way (NetBSD:s OpenBSD:k FreeBSD:H  -- NIH???)
 | 
						|
      trace("H print LWP (thread) info\n");   // was: Use /vmcore as c-dumpfile\n");
 | 
						|
      thread_flags |= TF_B_H;
 | 
						|
      //format_modifiers |= FM_L;    // FIXME: determine if we need something like this
 | 
						|
      break;
 | 
						|
    case 'L': /* single */
 | 
						|
      trace("L list all format specifiers\n");
 | 
						|
      exclusive("L");
 | 
						|
      print_format_specifiers();
 | 
						|
      exit(0);
 | 
						|
    case 'M':   // undocumented for now: these are proliferating!
 | 
						|
      trace("M MacOS X thread display, like AIX/Tru64\n");
 | 
						|
      thread_flags |= TF_B_m;
 | 
						|
      break;
 | 
						|
    case 'O': /* end */
 | 
						|
      trace("O like o + defaults, add new columns after PID, also sort\n");
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return _("format or sort specification must follow O");
 | 
						|
      defer_sf_option(arg, SF_B_O);
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
      break;
 | 
						|
    case 'S':
 | 
						|
      trace("S include dead kids in sum\n");
 | 
						|
      include_dead_children = 1;
 | 
						|
      break;
 | 
						|
    case 'T':
 | 
						|
      trace("T select all processes on this terminal\n");
 | 
						|
      /* put our tty on a tiny list */
 | 
						|
      {
 | 
						|
        selection_node *node;
 | 
						|
        node = xmalloc(sizeof(selection_node));
 | 
						|
        node->u = xmalloc(sizeof(sel_union));
 | 
						|
        node->u[0].tty = cached_tty;
 | 
						|
        node->typecode = SEL_TTY;
 | 
						|
        node->n = 1;
 | 
						|
        node->next = selection_list;
 | 
						|
        selection_list = node;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case 'U': /* end */
 | 
						|
      trace("U select processes for specified users\n");
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return _("list of users must follow U");
 | 
						|
      err=parse_list(arg, parse_uid);
 | 
						|
      if(err) return err;
 | 
						|
      selection_list->typecode = SEL_EUID;
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
    case 'V': /* single */
 | 
						|
      trace("V show version info\n");
 | 
						|
      exclusive("V");
 | 
						|
      display_ps_version();
 | 
						|
      exit(0);
 | 
						|
    case 'W':
 | 
						|
      trace("W N/A get swap info from ... not /dev/drum.\n");
 | 
						|
      return _("obsolete W option not supported (you have a /dev/drum?)");
 | 
						|
      break;
 | 
						|
    case 'X':
 | 
						|
      trace("X old Linux i386 register format\n");
 | 
						|
      format_flags |= FF_LX;
 | 
						|
      break;
 | 
						|
    case 'Z':  /* FreeBSD does MAC like SGI's Irix does it */
 | 
						|
      trace("Z print security label for Mandatory Access Control.\n");
 | 
						|
      format_modifiers |= FM_M;
 | 
						|
      break;
 | 
						|
    case 'a':
 | 
						|
      trace("a select all w/tty, including other users\n");
 | 
						|
      simple_select |= SS_B_a;
 | 
						|
      break;
 | 
						|
    case 'c':
 | 
						|
      trace("c true command name\n");
 | 
						|
      bsd_c_option = 1;
 | 
						|
      break;
 | 
						|
//  case 'd':
 | 
						|
//    trace("d FreeBSD-style tree\n");
 | 
						|
//    forest_type = 'f';
 | 
						|
//    break;
 | 
						|
    case 'e':
 | 
						|
      trace("e environment\n");
 | 
						|
      bsd_e_option = 1;
 | 
						|
      break;
 | 
						|
    case 'f':
 | 
						|
      trace("f ASCII art forest\n");
 | 
						|
      forest_type = 'b';
 | 
						|
      break;
 | 
						|
    case 'g':
 | 
						|
      trace("g _all_, even group leaders\n");
 | 
						|
      simple_select |= SS_B_g;
 | 
						|
      break;
 | 
						|
    case 'h':
 | 
						|
      trace("h repeat header\n");
 | 
						|
      if(header_type) return _("only one heading option may be specified");
 | 
						|
      if(personality & PER_BSD_h) header_type = HEAD_MULTI;
 | 
						|
      else                        header_type = HEAD_NONE;
 | 
						|
      break;
 | 
						|
    case 'j':
 | 
						|
      trace("j job control format\n");
 | 
						|
      format_flags |= FF_Bj;
 | 
						|
      break;
 | 
						|
    case 'k':
 | 
						|
      // OpenBSD: don't hide "kernel threads" -- like the swapper?
 | 
						|
      // trace("k Print LWP (thread) info.\n");   // was: Use /vmcore as c-dumpfile\n");
 | 
						|
 | 
						|
      // NetBSD, and soon (?) FreeBSD: sort-by-keyword
 | 
						|
      trace("k specify sorting keywords\n");
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return _("long sort specification must follow 'k'");
 | 
						|
      defer_sf_option(arg, SF_G_sort);
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
    case 'l':
 | 
						|
      trace("l display long format\n");
 | 
						|
      format_flags |= FF_Bl;
 | 
						|
      break;
 | 
						|
    case 'm':
 | 
						|
      trace("m all threads, sort on mem use, show mem info\n");
 | 
						|
      if(personality & PER_OLD_m){
 | 
						|
        format_flags |= FF_Lm;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      if(personality & PER_BSD_m){
 | 
						|
        defer_sf_option("pmem", SF_B_m);
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      thread_flags |= TF_B_m;
 | 
						|
      break;
 | 
						|
    case 'n':
 | 
						|
      trace("n numeric output for WCHAN, and USER replaced by UID\n");
 | 
						|
      wchan_is_number = 1;
 | 
						|
      user_is_number = 1;
 | 
						|
      /* TODO add tty_is_number too? */
 | 
						|
      break;
 | 
						|
    case 'o': /* end */
 | 
						|
      trace("o specify user-defined format\n");
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return _("format specification must follow o");
 | 
						|
      defer_sf_option(arg, SF_B_o);
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
    case 'p': /* end */
 | 
						|
      trace("p select by process ID\n");
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return _("list of process IDs must follow p");
 | 
						|
      err=parse_list(arg, parse_pid);
 | 
						|
      if(err) return err;
 | 
						|
      selection_list->typecode = SEL_PID;
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
    case 'q': /* end */
 | 
						|
      trace("q Quick select by process ID\n");
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg) return "List of process IDs must follow q.";
 | 
						|
      err=parse_list(arg, parse_pid);
 | 
						|
      if(err) return err;
 | 
						|
      selection_list->typecode = SEL_PID_QUICK;
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
    case 'r':
 | 
						|
      trace("r select running processes\n");
 | 
						|
      running_only = 1;
 | 
						|
      break;
 | 
						|
    case 's':
 | 
						|
      trace("s display signal format\n");
 | 
						|
      format_flags |= FF_Bs;
 | 
						|
      break;
 | 
						|
    case 't': /* end */
 | 
						|
      trace("t select by tty\n");
 | 
						|
      /* List of terminals (tty, pty...) _should_ follow t. */
 | 
						|
      arg=get_opt_arg();
 | 
						|
      if(!arg){
 | 
						|
        /* Wow, obsolete BSD syntax. Put our tty on a tiny list. */
 | 
						|
        selection_node *node;
 | 
						|
        node = xmalloc(sizeof(selection_node));
 | 
						|
        node->u = xmalloc(sizeof(sel_union));
 | 
						|
        node->u[0].tty = cached_tty;
 | 
						|
        node->typecode = SEL_TTY;
 | 
						|
        node->n = 1;
 | 
						|
        node->next = selection_list;
 | 
						|
        selection_list = node;
 | 
						|
        return NULL;
 | 
						|
      }
 | 
						|
      err=parse_list(arg, parse_tty);
 | 
						|
      if(err) return err;
 | 
						|
      selection_list->typecode = SEL_TTY;
 | 
						|
      return NULL; /* can't have any more options */
 | 
						|
    case 'u':
 | 
						|
      trace("u display user-oriented\n");
 | 
						|
      format_flags |= FF_Bu;
 | 
						|
      break;
 | 
						|
    case 'v':
 | 
						|
      trace("v display virtual memory\n");
 | 
						|
      format_flags |= FF_Bv;
 | 
						|
      break;
 | 
						|
    case 'w':
 | 
						|
      trace("w wide output\n");
 | 
						|
      w_count++;
 | 
						|
      break;
 | 
						|
    case 'x':
 | 
						|
      trace("x select processes without controlling ttys\n");
 | 
						|
      simple_select |= SS_B_x;
 | 
						|
      break;
 | 
						|
    case '-':
 | 
						|
      return _("embedded '-' among BSD options makes no sense");
 | 
						|
      break;
 | 
						|
    case '\0':
 | 
						|
      catastrophic_failure(__FILE__, __LINE__, _("please report this bug"));
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      return _("unsupported option (BSD syntax)");
 | 
						|
    } /* switch */
 | 
						|
  } /* while */
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/*************** gnu long options **********************/
 | 
						|
 | 
						|
/*
 | 
						|
 * Return the argument or NULL
 | 
						|
 */
 | 
						|
static const char *grab_gnu_arg(void){
 | 
						|
  switch(*flagptr){     /* argument is part of ps_argv[thisarg] */
 | 
						|
  default:
 | 
						|
    return NULL;                     /* something bad */
 | 
						|
  case '=': case ':':
 | 
						|
    if(*++flagptr) return flagptr;   /* found it */
 | 
						|
    return NULL;                     /* empty '=' or ':' */
 | 
						|
  case '\0': /* try next argv[] */
 | 
						|
    ;
 | 
						|
  }
 | 
						|
  if(thisarg+2 > ps_argc) return NULL;   /* there is nothing left */
 | 
						|
  /* argument follows ps_argv[thisarg] */
 | 
						|
  if(*(ps_argv[thisarg+1]) == '\0') return NULL;
 | 
						|
  return ps_argv[++thisarg];
 | 
						|
}
 | 
						|
 | 
						|
typedef struct gnu_table_struct {
 | 
						|
  const char *name; /* long option name */
 | 
						|
  const void *jump; /* See gcc extension info.   :-)   */
 | 
						|
} gnu_table_struct;
 | 
						|
 | 
						|
static int compare_gnu_table_structs(const void *a, const void *b){
 | 
						|
  return strcmp(((const gnu_table_struct*)a)->name,((const gnu_table_struct*)b)->name);
 | 
						|
}
 | 
						|
 | 
						|
/* Option arguments are after ':', after '=', or in argv[n+1] */
 | 
						|
static const char *parse_gnu_option(void){
 | 
						|
  const char *arg;
 | 
						|
  const char *err;
 | 
						|
  char *s;
 | 
						|
  size_t sl;
 | 
						|
  char buf[16];
 | 
						|
  gnu_table_struct findme = { buf, NULL};
 | 
						|
  gnu_table_struct *found;
 | 
						|
  static const gnu_table_struct gnu_table[] = {
 | 
						|
  {"Group",         &&case_Group},       /* rgid */
 | 
						|
  {"User",          &&case_User},        /* ruid */
 | 
						|
  {"cols",          &&case_cols},
 | 
						|
  {"columns",       &&case_columns},
 | 
						|
  {"context",       &&case_context},
 | 
						|
  {"cumulative",    &&case_cumulative},
 | 
						|
  {"deselect",      &&case_deselect},    /* -N */
 | 
						|
  {"forest",        &&case_forest},      /* f -H */
 | 
						|
  {"format",        &&case_format},
 | 
						|
  {"group",         &&case_group},       /* egid */
 | 
						|
  {"header",        &&case_header},
 | 
						|
  {"headers",       &&case_headers},
 | 
						|
  {"heading",       &&case_heading},
 | 
						|
  {"headings",      &&case_headings},
 | 
						|
//{"help",          &&case_help},        /* now TRANSLATABLE ! */
 | 
						|
  {"info",          &&case_info},
 | 
						|
  {"lines",         &&case_lines},
 | 
						|
  {"no-header",     &&case_no_header},
 | 
						|
  {"no-headers",    &&case_no_headers},
 | 
						|
  {"no-heading",    &&case_no_heading},
 | 
						|
  {"no-headings",   &&case_no_headings},
 | 
						|
  {"noheader",      &&case_noheader},
 | 
						|
  {"noheaders",     &&case_noheaders},
 | 
						|
  {"noheading",     &&case_noheading},
 | 
						|
  {"noheadings",    &&case_noheadings},
 | 
						|
  {"pid",           &&case_pid},
 | 
						|
  {"ppid",          &&case_ppid},
 | 
						|
  {"quick-pid",     &&case_pid_quick},
 | 
						|
  {"rows",          &&case_rows},
 | 
						|
  {"sid",           &&case_sid},
 | 
						|
  {"sort",          &&case_sort},
 | 
						|
  {"tty",           &&case_tty},
 | 
						|
  {"user",          &&case_user},        /* euid */
 | 
						|
  {"version",       &&case_version},
 | 
						|
  {"width",         &&case_width},
 | 
						|
  };
 | 
						|
  const int gnu_table_count = sizeof(gnu_table)/sizeof(gnu_table_struct);
 | 
						|
 | 
						|
  s = ps_argv[thisarg]+2;
 | 
						|
  sl = strcspn(s,":=");
 | 
						|
  if(sl > 15) return _("unknown gnu long option");
 | 
						|
  strncpy(buf, s, sl);
 | 
						|
  buf[sl] = '\0';
 | 
						|
  flagptr = s+sl;
 | 
						|
 | 
						|
  found = bsearch(&findme, gnu_table, gnu_table_count,
 | 
						|
      sizeof(gnu_table_struct), compare_gnu_table_structs
 | 
						|
  );
 | 
						|
 | 
						|
  if(!found) {
 | 
						|
    if (!strcmp(buf, the_word_help))
 | 
						|
      goto case_help;
 | 
						|
    return _("unknown gnu long option");
 | 
						|
  }
 | 
						|
 | 
						|
  goto *(found->jump);    /* See gcc extension info.  :-)   */
 | 
						|
 | 
						|
  case_Group:
 | 
						|
    trace("--Group\n");
 | 
						|
    arg = grab_gnu_arg();
 | 
						|
    if(!arg) return _("list of real groups must follow --Group");
 | 
						|
    err=parse_list(arg, parse_gid);
 | 
						|
    if(err) return err;
 | 
						|
    selection_list->typecode = SEL_RGID;
 | 
						|
    return NULL;
 | 
						|
  case_User:
 | 
						|
    trace("--User\n");
 | 
						|
    arg = grab_gnu_arg();
 | 
						|
    if(!arg) return _("list of real users must follow --User");
 | 
						|
    err=parse_list(arg, parse_uid);
 | 
						|
    if(err) return err;
 | 
						|
    selection_list->typecode = SEL_RUID;
 | 
						|
    return NULL;
 | 
						|
  case_cols:
 | 
						|
  case_width:
 | 
						|
  case_columns:
 | 
						|
    trace("--cols\n");
 | 
						|
    arg = grab_gnu_arg();
 | 
						|
    if(arg && *arg){
 | 
						|
      long t;
 | 
						|
      char *endptr;
 | 
						|
      t = strtol(arg, &endptr, 0);
 | 
						|
      if(!*endptr && (t>0) && (t<2000000000)){
 | 
						|
        screen_cols = (int)t;
 | 
						|
        return NULL;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return _("number of columns must follow --cols, --width, or --columns");
 | 
						|
  case_cumulative:
 | 
						|
    trace("--cumulative\n");
 | 
						|
    if(s[sl]) return _("option --cumulative does not take an argument");
 | 
						|
    include_dead_children = 1;
 | 
						|
    return NULL;
 | 
						|
  case_deselect:
 | 
						|
    trace("--deselect\n");
 | 
						|
    if(s[sl]) return _("option --deselect does not take an argument");
 | 
						|
    negate_selection = 1;
 | 
						|
    return NULL;
 | 
						|
  case_no_header:
 | 
						|
  case_no_headers:
 | 
						|
  case_no_heading:
 | 
						|
  case_no_headings:
 | 
						|
  case_noheader:
 | 
						|
  case_noheaders:
 | 
						|
  case_noheading:
 | 
						|
  case_noheadings:
 | 
						|
    trace("--noheaders\n");
 | 
						|
    if(s[sl]) return _("option --no-heading does not take an argument");
 | 
						|
    if(header_type) return _("only one heading option may be specified");
 | 
						|
    header_type = HEAD_NONE;
 | 
						|
    return NULL;
 | 
						|
  case_header:
 | 
						|
  case_headers:
 | 
						|
  case_heading:
 | 
						|
  case_headings:
 | 
						|
    trace("--headers\n");
 | 
						|
    if(s[sl]) return _("option --heading does not take an argument");
 | 
						|
    if(header_type) return _("only one heading option may be specified");
 | 
						|
    header_type = HEAD_MULTI;
 | 
						|
    return NULL;
 | 
						|
  case_forest:
 | 
						|
    trace("--forest\n");
 | 
						|
    if(s[sl]) return _("option --forest does not take an argument");
 | 
						|
    forest_type = 'g';
 | 
						|
    return NULL;
 | 
						|
  case_format:
 | 
						|
    trace("--format\n");
 | 
						|
    arg=grab_gnu_arg();
 | 
						|
    if(!arg) return _("format specification must follow --format");
 | 
						|
    defer_sf_option(arg, SF_G_format);
 | 
						|
    return NULL;
 | 
						|
  case_group:
 | 
						|
    trace("--group\n");
 | 
						|
    arg = grab_gnu_arg();
 | 
						|
    if(!arg) return _("list of effective groups must follow --group");
 | 
						|
    err=parse_list(arg, parse_gid);
 | 
						|
    if(err) return err;
 | 
						|
    selection_list->typecode = SEL_EGID;
 | 
						|
    return NULL;
 | 
						|
  case_help:
 | 
						|
    trace("--help\n");
 | 
						|
    arg = grab_gnu_arg();
 | 
						|
    do_help(arg, EXIT_SUCCESS);
 | 
						|
  case_info:
 | 
						|
    trace("--info\n");
 | 
						|
    exclusive("--info");
 | 
						|
    self_info();
 | 
						|
    exit(0);
 | 
						|
    return NULL;
 | 
						|
  case_pid:
 | 
						|
    trace("--pid\n");
 | 
						|
    arg = grab_gnu_arg();
 | 
						|
    if(!arg) return _("list of process IDs must follow --pid");
 | 
						|
    err=parse_list(arg, parse_pid);
 | 
						|
    if(err) return err;
 | 
						|
    selection_list->typecode = SEL_PID;
 | 
						|
    return NULL;
 | 
						|
  case_pid_quick:
 | 
						|
    trace("--quick-pid\n");
 | 
						|
    arg = grab_gnu_arg();
 | 
						|
    if(!arg) return "List of process IDs must follow --quick-pid.";
 | 
						|
    err=parse_list(arg, parse_pid);
 | 
						|
    if(err) return err;
 | 
						|
    selection_list->typecode = SEL_PID_QUICK;
 | 
						|
    return NULL;
 | 
						|
  case_ppid:
 | 
						|
    trace("--ppid\n");
 | 
						|
    arg = grab_gnu_arg();
 | 
						|
    if(!arg) return _("list of process IDs must follow --ppid");
 | 
						|
    err=parse_list(arg, parse_pid);
 | 
						|
    if(err) return err;
 | 
						|
    selection_list->typecode = SEL_PPID;
 | 
						|
    return NULL;
 | 
						|
  case_rows:
 | 
						|
  case_lines:
 | 
						|
    trace("--rows\n");
 | 
						|
    arg = grab_gnu_arg();
 | 
						|
    if(arg && *arg){
 | 
						|
      long t;
 | 
						|
      char *endptr;
 | 
						|
      t = strtol(arg, &endptr, 0);
 | 
						|
      if(!*endptr && (t>0) && (t<2000000000)){
 | 
						|
        screen_rows = (int)t;
 | 
						|
        return NULL;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return _("number of rows must follow --rows or --lines");
 | 
						|
  case_sid:
 | 
						|
    trace("--sid\n");
 | 
						|
    arg = grab_gnu_arg();
 | 
						|
    if(!arg) return _("some sid thing(s) must follow --sid");
 | 
						|
    err=parse_list(arg, parse_pid);
 | 
						|
    if(err) return err;
 | 
						|
    selection_list->typecode = SEL_SESS;
 | 
						|
    return NULL;
 | 
						|
  case_sort:
 | 
						|
    trace("--sort\n");
 | 
						|
    arg=grab_gnu_arg();
 | 
						|
    if(!arg) return _("long sort specification must follow --sort");
 | 
						|
    defer_sf_option(arg, SF_G_sort);
 | 
						|
    return NULL;
 | 
						|
  case_tty:
 | 
						|
    trace("--tty\n");
 | 
						|
    arg = grab_gnu_arg();
 | 
						|
    if(!arg) return _("list of ttys must follow --tty");
 | 
						|
    err=parse_list(arg, parse_tty);
 | 
						|
    if(err) return err;
 | 
						|
    selection_list->typecode = SEL_TTY;
 | 
						|
    return NULL;
 | 
						|
  case_user:
 | 
						|
    trace("--user\n");
 | 
						|
    arg = grab_gnu_arg();
 | 
						|
    if(!arg) return _("list of effective users must follow --user");
 | 
						|
    err=parse_list(arg, parse_uid);
 | 
						|
    if(err) return err;
 | 
						|
    selection_list->typecode = SEL_EUID;
 | 
						|
    return NULL;
 | 
						|
  case_version:
 | 
						|
    trace("--version\n");
 | 
						|
    exclusive("--version");
 | 
						|
    display_ps_version();
 | 
						|
    exit(0);
 | 
						|
    return NULL;
 | 
						|
  case_context:
 | 
						|
    trace("--context\n");
 | 
						|
    format_flags |= FF_Fc;
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/*************** process trailing PIDs  **********************/
 | 
						|
static const char *parse_trailing_pids(void){
 | 
						|
  selection_node *pidnode;  /* pid */
 | 
						|
  selection_node *grpnode;  /* process group */
 | 
						|
  selection_node *sidnode;  /* session */
 | 
						|
  char **argp;     /* pointer to pointer to text of PID */
 | 
						|
  const char *err;       /* error code that could or did happen */
 | 
						|
  int i;
 | 
						|
 | 
						|
  i = ps_argc - thisarg;  /* how many trailing PIDs, SIDs, PGRPs?? */
 | 
						|
  argp = ps_argv + thisarg;
 | 
						|
  thisarg = ps_argc - 1;   /* we must be at the end now */
 | 
						|
 | 
						|
  pidnode = xmalloc(sizeof(selection_node));
 | 
						|
  pidnode->u = xmalloc(i*sizeof(sel_union)); /* waste is insignificant */
 | 
						|
  pidnode->n = 0;
 | 
						|
 | 
						|
  grpnode = xmalloc(sizeof(selection_node));
 | 
						|
  grpnode->u = xmalloc(i*sizeof(sel_union)); /* waste is insignificant */
 | 
						|
  grpnode->n = 0;
 | 
						|
 | 
						|
  sidnode = xmalloc(sizeof(selection_node));
 | 
						|
  sidnode->u = xmalloc(i*sizeof(sel_union)); /* waste is insignificant */
 | 
						|
  sidnode->n = 0;
 | 
						|
 | 
						|
  while(i--){
 | 
						|
    char *data;
 | 
						|
    data = *(argp++);
 | 
						|
    switch(*data){
 | 
						|
    default:   err = parse_pid(  data, pidnode->u + pidnode->n++); break;
 | 
						|
    case '-':  err = parse_pid(++data, grpnode->u + grpnode->n++); break;
 | 
						|
    case '+':  err = parse_pid(++data, sidnode->u + sidnode->n++); break;
 | 
						|
    }
 | 
						|
    if(err) return err;     /* the node gets freed with the list */
 | 
						|
  }
 | 
						|
 | 
						|
  if(pidnode->n){
 | 
						|
    pidnode->next = selection_list;
 | 
						|
    selection_list = pidnode;
 | 
						|
    selection_list->typecode = SEL_PID;
 | 
						|
  }  /* else free both parts */
 | 
						|
 | 
						|
  if(grpnode->n){
 | 
						|
    grpnode->next = selection_list;
 | 
						|
    selection_list = grpnode;
 | 
						|
    selection_list->typecode = SEL_PGRP;
 | 
						|
  }  /* else free both parts */
 | 
						|
 | 
						|
  if(sidnode->n){
 | 
						|
    sidnode->next = selection_list;
 | 
						|
    selection_list = sidnode;
 | 
						|
    selection_list->typecode = SEL_SESS;
 | 
						|
  }  /* else free both parts */
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/************** misc stuff ***********/
 | 
						|
 | 
						|
static void reset_parser(void){
 | 
						|
  w_count = 0;
 | 
						|
}
 | 
						|
 | 
						|
static int arg_type(const char *str){
 | 
						|
  int tmp = str[0];
 | 
						|
  if((tmp>='a') && (tmp<='z'))   return ARG_BSD;
 | 
						|
  if((tmp>='A') && (tmp<='Z'))   return ARG_BSD;
 | 
						|
  if((tmp>='0') && (tmp<='9'))   return ARG_PID;
 | 
						|
  if(tmp=='+')                   return ARG_SESS;
 | 
						|
  if(tmp!='-')                   return ARG_FAIL;
 | 
						|
  tmp = str[1];
 | 
						|
  if((tmp>='a') && (tmp<='z'))   return ARG_SYSV;
 | 
						|
  if((tmp>='A') && (tmp<='Z'))   return ARG_SYSV;
 | 
						|
  if((tmp>='0') && (tmp<='9'))   return ARG_PGRP;
 | 
						|
  if(tmp!='-')                   return ARG_FAIL;
 | 
						|
  tmp = str[2];
 | 
						|
  if((tmp>='a') && (tmp<='z'))   return ARG_GNU;
 | 
						|
  if((tmp>='A') && (tmp<='Z'))   return ARG_GNU;
 | 
						|
  if(tmp=='\0')                  return ARG_END;
 | 
						|
  return ARG_FAIL;
 | 
						|
}
 | 
						|
 | 
						|
/* First assume sysv, because that is the POSIX and Unix98 standard. */
 | 
						|
static const char *parse_all_options(void){
 | 
						|
  const char *err = NULL;
 | 
						|
  int at;
 | 
						|
  while(++thisarg < ps_argc){
 | 
						|
  trace("parse_all_options calling arg_type for \"%s\"\n", ps_argv[thisarg]);
 | 
						|
    at = arg_type(ps_argv[thisarg]);
 | 
						|
    trace("ps_argv[thisarg] is %s\n", ps_argv[thisarg]);
 | 
						|
    switch(at){
 | 
						|
    case ARG_GNU:
 | 
						|
      err = parse_gnu_option();
 | 
						|
      break;
 | 
						|
    case ARG_SYSV:
 | 
						|
      if(!force_bsd){   /* else go past case ARG_BSD */
 | 
						|
        err = parse_sysv_option();
 | 
						|
        break;
 | 
						|
    case ARG_BSD:
 | 
						|
        if(force_bsd && !(personality & PER_FORCE_BSD)) return _("way bad");
 | 
						|
      }
 | 
						|
      prefer_bsd_defaults = 1;
 | 
						|
      err = parse_bsd_option();
 | 
						|
      break;
 | 
						|
    case ARG_PGRP:
 | 
						|
    case ARG_SESS:
 | 
						|
    case ARG_PID:
 | 
						|
      prefer_bsd_defaults = 1;
 | 
						|
      err = parse_trailing_pids();
 | 
						|
      break;
 | 
						|
    case ARG_END:
 | 
						|
    case ARG_FAIL:
 | 
						|
      trace("              FAIL/END on [%s]\n",ps_argv[thisarg]);
 | 
						|
      return _("garbage option");
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      printf("                  ?    %s\n",ps_argv[thisarg]);
 | 
						|
      return _("something broke");
 | 
						|
    } /* switch */
 | 
						|
    if(err) return err;
 | 
						|
  } /* while */
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static void choose_dimensions(void){
 | 
						|
  if(w_count && (screen_cols<132)) screen_cols=132;
 | 
						|
  if(w_count>1) screen_cols=OUTBUF_SIZE;
 | 
						|
  /* perhaps --html and --null should set unlimited width */
 | 
						|
}
 | 
						|
 | 
						|
static const char *thread_option_check(void){
 | 
						|
  if(!thread_flags){
 | 
						|
    thread_flags = TF_show_proc;
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  if(forest_type){
 | 
						|
    return _("thread display conflicts with forest display");
 | 
						|
  }
 | 
						|
  //thread_flags |= TF_no_forest;
 | 
						|
 | 
						|
  if((thread_flags&TF_B_H) && (thread_flags&(TF_B_m|TF_U_m)))
 | 
						|
    return _("thread flags conflict; can't use H with m or -m");
 | 
						|
  if((thread_flags&TF_B_m) && (thread_flags&TF_U_m))
 | 
						|
    return _("thread flags conflict; can't use both m and -m");
 | 
						|
  if((thread_flags&TF_U_L) && (thread_flags&TF_U_T))
 | 
						|
    return _("thread flags conflict; can't use both -L and -T");
 | 
						|
 | 
						|
  if(thread_flags&TF_B_H) thread_flags |= (TF_show_proc|TF_loose_tasks);
 | 
						|
  if(thread_flags&(TF_B_m|TF_U_m)) thread_flags |= (TF_show_proc|TF_show_task|TF_show_both);
 | 
						|
 | 
						|
  if(thread_flags&(TF_U_T|TF_U_L)){
 | 
						|
    if(thread_flags&(TF_B_m|TF_U_m|TF_B_H)){
 | 
						|
      // Got a thread style, so format modification is a requirement?
 | 
						|
      // Maybe -T/-L has H thread style though. (sorting interaction?)
 | 
						|
      //return _("Huh? Tell procps@freelists.org what you expected.");
 | 
						|
      thread_flags |= TF_must_use;
 | 
						|
    }else{
 | 
						|
      // using -L/-T thread style, so format from elsewhere is OK
 | 
						|
      thread_flags |= TF_show_task;  // or like the H option?
 | 
						|
      //thread_flags |= TF_no_sort;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
int arg_parse(int argc, char *argv[]){
 | 
						|
  const char *err = NULL;
 | 
						|
  const char *err2 = NULL;
 | 
						|
  ps_argc = argc;
 | 
						|
  ps_argv = argv;
 | 
						|
  thisarg = 0;
 | 
						|
 | 
						|
  if(personality & PER_FORCE_BSD) goto try_bsd;
 | 
						|
 | 
						|
  err = parse_all_options();
 | 
						|
  if(err) goto try_bsd;
 | 
						|
  err = thread_option_check();
 | 
						|
  if(err) goto try_bsd;
 | 
						|
  err = process_sf_options();
 | 
						|
  if(err) goto try_bsd;
 | 
						|
  err = select_bits_setup();
 | 
						|
  if(err) goto try_bsd;
 | 
						|
 | 
						|
  choose_dimensions();
 | 
						|
  return 0;
 | 
						|
 | 
						|
try_bsd:
 | 
						|
  trace("--------- now try BSD ------\n");
 | 
						|
 | 
						|
  reset_global();
 | 
						|
  reset_parser();
 | 
						|
  reset_sortformat();
 | 
						|
  format_flags = 0;
 | 
						|
  ps_argc = argc;
 | 
						|
  ps_argv = argv;
 | 
						|
  thisarg = 0;
 | 
						|
  /* no need to reset flagptr */
 | 
						|
  force_bsd=1;
 | 
						|
  prefer_bsd_defaults=1;
 | 
						|
  if(!( (PER_OLD_m|PER_BSD_m) & personality )) /* if default m setting... */
 | 
						|
    personality |= PER_OLD_m; /* Prefer old Linux over true BSD. */
 | 
						|
  /* Do not set PER_FORCE_BSD! It is tested below. */
 | 
						|
 | 
						|
  err2 = parse_all_options();
 | 
						|
  if(err2) goto total_failure;
 | 
						|
  err2 = thread_option_check();
 | 
						|
  if(err2) goto total_failure;
 | 
						|
  err2 = process_sf_options();
 | 
						|
  if(err2) goto total_failure;
 | 
						|
  err2 = select_bits_setup();
 | 
						|
  if(err2) goto total_failure;
 | 
						|
 | 
						|
  choose_dimensions();
 | 
						|
  return 0;
 | 
						|
 | 
						|
total_failure:
 | 
						|
  reset_parser();
 | 
						|
  if(personality & PER_FORCE_BSD) fprintf(stderr, _("error: %s\n"), err2);
 | 
						|
  else fprintf(stderr, _("error: %s\n"), err);
 | 
						|
  do_help(NULL, EXIT_FAILURE);
 | 
						|
}
 |