/* ** Copyright (c) 1986, 1994, 1996, 2000, 2002 ** Jeff Forys (jeffware@marjum.com). All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that: (1) Redistributions of ** source code must retain the above copyright notice, this list of ** conditions and the following disclaimer, (2) Redistributions in ** binary form must reproduce the above copyright notice, this list ** of conditions and the following disclaimer in the documentation ** and/or other materials provided with the distribution, (3) All ** advertising materials mentioning features or use of this software ** must display the following acknowledgment: ``This product includes ** software developed by Jeff Forys (jeffware@marjum.com).'', (4) ** The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED ** WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint static char rcsid[] = "$Id: argparse.c,v 1.17 2005/04/06 23:43:25 forys Exp $"; #endif #define NO_AEXTERN #include "conf.h" #undef NO_AEXTERN #include #include #include /* * Define/initialize linked lists of user supplied things. */ tty_T *TtyList = NULL; /* list of ttys */ uid_T *UidList = NULL; /* list of user id's */ pid_T *PidList = NULL; /* list of process id's */ cmd_T *CmdList = NULL; /* list of commands */ int TtyIndx = 0, UidIndx = 0, PidIndx = 0, CmdIndx = 0; int Aflag = 0, Fflag = 0, Iflag = 0, Nflag = 0, Vflag = 0, Wflag = 0; /* * Because some vendors now have long symbolic signal names, "kill -l" may * display fewer than 16 signals per line. Since 16 is the default, we set * it here; the machine-dependent initialization routine -- MdepInit() -- * may change it so that "skill -l" properly mimics "kill -l". */ int SigsPerLine = 16; /* * ArgParse(argc, argv) * * Destructively parse argv[] into appropriate global variables. */ void ArgParse(argc, argv) int argc; char *argv[]; { extern int atoi(); extern void *calloc(); struct passwd *pp; struct stat st; int argcnt = argc; /* saved so ALLOC() knows max mem to allocate */ int haveno = 0; /* set when we get a signal or priority */ int help, argloop; #define ALLOC(cnt,type,loc,nstr) \ if ((loc = (type) calloc((unsigned)cnt,sizeof(type))) == NULL) \ Exceed(nstr) #ifndef NO_GETENV /* if "SKILL_VERBOSE" env var is set, enable verbose mode */ { extern char *getenv(); if (getenv("SKILL_VERBOSE") != NULL) Vflag++; } #endif /* * This is the main argument parsing loop. At first glance * this may look confusing. We destructively move across * arguments by incrementing that which "*argv" points to. * * Basically, for each argument, we first check if we have a * flag (flags are preceded by a `-'). If so, then we enter * the flag loop, processing each character until we reach * end of string, or we hit a flag that requires special * processing (i.e. any of "ltucp", "SIGNO", or "PRIORITY"). * * Second, we check for the special case flag, "+PRIORITY". * Finally, if we do not have a flag, we simply go on to the * argument classification phase. This is where we decide * if a string represents a tty, a user name, a process id, * or a command. * * If at any time something incomprehensible is unearthed, * we call Usage(), who in turn calls exit(). Using "-l" also * forces an immediate exit() (through ListSigs() or Usage()). */ while (--argc > 0) { /* for each argument */ help = 0; if (**(++argv) == '-') { /* found a flag */ do { argloop = 0; switch (*++(*argv)) { case 'a': Aflag++; break; case 'l': if (Skill) ListSigs(); else Usage(E_USAGE); /*NOTREACHED*/ break; case 'f': /* fast mode (if possible) */ Fflag++; argloop++; /* look for more flags */ break; case 'i': /* interactive mode */ Iflag++; argloop++; /* look for more flags */ break; case 'v': /* verbose mode */ Vflag++; argloop++; /* look for more flags */ break; case 'w': /* display warning messages */ Wflag++; argloop++; /* look for more flags */ break; case 'n': /* display pid's (do not act on them) */ Nflag++; argloop++; /* look for more flags */ break; case 't': case 'u': case 'c': case 'p': help = **argv; break; default: /* * This code is kind of gnarly. The user * may specify "-SIGNO" or "-PRIORITY" * (depending on what we are doing). This * is further complicated by the fact that * SIGNO may be the signal *name*. The * special case of "+PRIORITY" is handled * outside this switch() statement. */ if (haveno) Usage(E_USAGE); if (isdigit(**argv)) { if (Skill) { SigPri = atoi(*argv); if (SigPri < 0 || SigPri > NSig) Usage(E_SIGNO); } else { SigPri = -atoi(*argv); if (SigPri < PrioMin) SigPri = PrioMin; } haveno++; } else if (!Skill) { Usage(E_USAGE); } else for (SigPri=0; SigPri <= NSig; SigPri++) if (STREQU(SigMap[SigPri], *argv)) { haveno++; break; } if (!haveno) { fprintf(stderr, "%s: Unknown signal (%s); %s -l lists signals.\n", ProgName, *argv, ProgName); exit(EX_UERR); } break; } } while (argloop && *(*argv+1) != '\0'); /* * There can be no more flags in this string. If the * help variable was not set, we can short-circuit the * remainder of the argument processing and move on to * the next argument. */ if (!help) /* go to next arg */ continue; } else if (!Skill && **argv == '+') { /* "+priority" */ ++(*argv); if (isdigit(**argv)) { SigPri = atoi(*argv); if (SigPri > PrioMax) SigPri = PrioMax; haveno++; continue; /* go to next arg */ } Usage(E_USAGE); /* dopey priority: usage error */ } /* * At this point, all we have left are ttys, user names, * processes id's, and commands. If we have `help', our * job is easy; otherwise we have to hunt for things. */ if (help && !(*++(*argv) || (--argc && *++argv))) Usage(E_USAGE); /* no arg following flag */ if (!help || help == 't') { /* tty? */ st.st_rdev = (dev_t)-2; /* default: no ctrling term */ if ((**argv == '?' && *(*argv + 1) == '\0') || (stat(*argv,&st)>=0&&(st.st_mode&S_IFMT)==S_IFCHR)){ if (TtyIndx == 0) ALLOC(argcnt, tty_T *, TtyList, "tty"); *(TtyList + TtyIndx++) = st.st_rdev; help = -1; } else if (help) Not(*argv, "tty"); } if (!help || help == 'u') { /* user name? */ if ((pp=getpwnam(*argv)) != NULL) { if (UidIndx == 0) ALLOC(argcnt, uid_T *, UidList, "user"); *(UidList + UidIndx++) = pp->pw_uid; help = -1; } else if (help) Not(*argv, "user name"); } if (!help || help == 'p') { /* process id? */ if (isdigit(**argv)) { if (PidIndx == 0) ALLOC(argcnt, pid_T *, PidList, "pid"); *(PidList + PidIndx++) = atoi(*argv); help = -1; } else if (help) Not(*argv, "process id"); } if (help > -1) { /* default: it's a command */ if (CmdIndx == 0) ALLOC(argcnt, cmd_T *, CmdList, "command"); if (**argv == '/') { char *cp = ++(*argv); while (*cp != '\0') cp++; if (cp == *argv || *(cp - 1) != '/') Not(*argv, "valid regular expression"); *(cp - 1) = '\0'; (CmdList + CmdIndx)->flags = CMD_FLAG_REGEX; RegexComp(*argv, (CmdList + CmdIndx)); } else { (CmdList + CmdIndx)->flags = CMD_FLAG_EXACT; (CmdList + CmdIndx)->cmd.cmdstr = *argv; } CmdIndx++; } } if (TtyIndx == 0 && UidIndx == 0 && PidIndx == 0 && CmdIndx == 0) Usage((Fflag == 0 && Iflag == 0 && Nflag == 0 && Vflag == 1 && Wflag == 0)? E_VERS: E_USAGE); /* * If this is not the superuser and no specific users were * mentioned, we assume they only want to deal with their * own processes. This cuts down on error messages since * the general user is not usually allowed to modify the * state of other users' processes. */ if (Aflag == 0 && (UidIndx == 0 && MyUid != ROOTUID)) { ALLOC(1, uid_T *, UidList, "user"); *(UidList + UidIndx++) = MyUid; } #undef ALLOC } /* * ListSigs() * * Display a list of available signals and exit. */ void ListSigs() { register int signo; register int didprint = 0; for (signo = 1; signo <= NSig; signo++) if (!isdigit(*SigMap[signo])) { printf("%s%c", SigMap[signo], /* signals/line */ (++didprint % SigsPerLine) == 0? '\n': ' '); } if ((didprint % SigsPerLine) != 0) (void) putc('\n', stdout); exit(EX_OKAY); } /* * Not(thought, this) * * The user incorrectly forced a type (e.g. "-u tty00"). * Display an error message and exit. */ void Not(thought, this) char *thought, *this; { fprintf(stderr, "%s: %s: not a %s\n", ProgName, thought, this); exit(EX_UERR); } /* * Exceed(what) * * Ran out of memory allocating space for `what'. */ void Exceed(what) char *what; { fprintf(stderr, "%s: out of memory (can't alloc %s)\n", ProgName, what); exit(EX_SERR); }