home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-12-26 | 73.3 KB | 2,982 lines |
- Newsgroups: comp.sources.unix
- From: koblas@mips.com (David Koblas)
- Subject: v26i080: op - a tool to allow customizable super user access, Part01/01
- Sender: unix-sources-moderator@pa.dec.com
- Approved: vixie@pa.dec.com
-
- Submitted-By: koblas@mips.com (David Koblas)
- Posting-Number: Volume 26, Issue 80
- Archive-Name: op/part01
-
- 'op' is a tool designed to allow customizable super user access,
- you can do everthing from emulating giving a super user shell
- for nothing to only allowing one or two users access via login
- names, or special passwords that are neither root, nor their own.
-
- Plus, as an added bonus, for those commands that you would like
- users to be able to use, but need to place restrictions on the
- arguments, you can configure that as well. (ie. if you want
- your users to be able to mount NFS file systems).
-
- Op was orginally developed at Convex by Tom Christiansen, and
- subsequently publised in a USENEX LISA procedings. This version
- was developed entierly from the description that was published.
-
- This release is the first official release of 'op', for those of you
- who have the alpha release, only two tiny fixes have been made.
- It has been sudgested that I get the syslog logging to conform to
- the Convex version, but this will follow in a latter patch.
-
- If you have any problems or find any bugs, please report them to:
-
- op-bugs@mips.com
-
- Enjoy,
-
- David Koblas (koblas@mips.com)
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 1 (of 1)."
- # Contents: MANIFEST Makefile README atov.c defs.h lex.l main.c op.1
- # op.access regerror.c regexp.c regexp.h regmagic.h regsub.c
- # vfprintf.c
- # Wrapped by vixie@cognition.pa.dec.com on Sun Dec 27 15:09:10 1992
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'MANIFEST' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'MANIFEST'\"
- else
- echo shar: Extracting \"'MANIFEST'\" \(579 characters\)
- sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
- X File Name Archive # Description
- X-----------------------------------------------------------
- X MANIFEST 1 This shipping list
- X Makefile 1
- X README 1
- X atov.c 1
- X defs.h 1
- X lex.l 1
- X main.c 1
- X op.1 1
- X op.access 1
- X regerror.c 1
- X regexp.c 1
- X regexp.h 1
- X regmagic.h 1
- X regsub.c 1
- X vfprintf.c 1
- END_OF_FILE
- if test 579 -ne `wc -c <'MANIFEST'`; then
- echo shar: \"'MANIFEST'\" unpacked with wrong size!
- fi
- # end of 'MANIFEST'
- fi
- if test -f 'Makefile' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'Makefile'\"
- else
- echo shar: Extracting \"'Makefile'\" \(346 characters\)
- sed "s/^X//" >'Makefile' <<'END_OF_FILE'
- CFLAGS=-g -I.
- X
- VPF = vfprintf.o
- REG = regexp.o regsub.o regerror.o
- OBJ = lex.o main.o atov.o $(REG) $(VPF)
- X
- all: op
- X
- op: $(OBJ) $(REG)
- X $(CC) $(CFLAGS) -o $@ $(OBJ) -ll
- X
- clean:
- X rm -f $(OBJ) op core
- X
- kit:
- X @makekit README \
- X atov.c defs.h lex.l main.c op.1 \
- X op.access regerror.c regexp.c regexp.h regmagic.h \
- X regsub.c vfprintf.c Makefile
- END_OF_FILE
- if test 346 -ne `wc -c <'Makefile'`; then
- echo shar: \"'Makefile'\" unpacked with wrong size!
- fi
- # end of 'Makefile'
- fi
- if test -f 'README' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'README'\"
- else
- echo shar: Extracting \"'README'\" \(1079 characters\)
- sed "s/^X//" >'README' <<'END_OF_FILE'
- X'op' is a tool designed to allow customizable super user access,
- you can do everthing from emulating giving a super user shell
- for nothing to only allowing one or two users access via login
- names, or special passwords that are neither root, nor their own.
- X
- Plus, as an added bonus, for those commands that you would like
- users to be able to use, but need to place restrictions on the
- arguments, you can configure that as well. (ie. if you want
- your users to be able to mount NFS file systems).
- X
- Op was orginally developed at Convex by Tom Christiansen, and
- subsequently publised in a USENEX LISA procedings. This version
- was developed entierly from the description that was published.
- X
- This release is the first official release of 'op', for those of you
- who have the alpha release, only two tiny fixes have been made.
- It has been sudgested that I get the syslog logging to conform to
- the Convex version, but this will follow in a latter patch.
- X
- If you have any problems or find any bugs, please report them to:
- X
- X op-bugs@mips.com
- X
- XEnjoy,
- X
- David Koblas (koblas@mips.com)
- END_OF_FILE
- if test 1079 -ne `wc -c <'README'`; then
- echo shar: \"'README'\" unpacked with wrong size!
- fi
- # end of 'README'
- fi
- if test -f 'atov.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'atov.c'\"
- else
- echo shar: Extracting \"'atov.c'\" \(1561 characters\)
- sed "s/^X//" >'atov.c' <<'END_OF_FILE'
- X/* +-------------------------------------------------------------------+ */
- X/* | Copyright 1988,1991, David Koblas. | */
- X/* | Permission to use, copy, modify, and distribute this software | */
- X/* | and its documentation for any purpose and without fee is hereby | */
- X/* | granted, provided that the above copyright notice appear in all | */
- X/* | copies and that both that copyright notice and this permission | */
- X/* | notice appear in supporting documentation. This software is | */
- X/* | provided "as is" without express or implied warranty. | */
- X/* +-------------------------------------------------------------------+ */
- X
- X#include <ctype.h>
- X
- X#ifdef TEST
- main(argc,argv)
- int argc;
- char **argv;
- X{
- X int i;
- X for (i=1;i<argc;i++)
- X printf("%10s == %d\n",argv[i],atov(argv[i],0));
- X}
- X#endif
- X
- atov(str,type)
- char *str;
- int type;
- X{
- X int sign = 1;
- X int i;
- X char c;
- X int val=0,n;
- X
- X i=0;
- X while ((str[i]==' ') || (str[i]=='\t')) i++;
- X if (str[i]=='-') {
- X sign = -1;
- X i++;
- X } else if (str[i]=='+') {
- X sign = 1;
- X i++;
- X }
- X if (type==0) {
- X if (str[i]=='0') {
- X i++;
- X if (str[i]=='%') {
- X i++;
- X type=2;
- X } else if (str[i]=='x') {
- X i++;
- X type=16;
- X } else {
- X type=8;
- X }
- X } else {
- X type=10;
- X }
- X }
- X for (;i<strlen(str);i++) {
- X c=str[i];
- X if (isdigit(c)) {
- X n = c - '0';
- X } else if (isupper(c)) {
- X n = c - 'A' + 10;
- X } else if (islower(c)) {
- X n = c - 'a' + 10;
- X } else {
- X goto out;
- X }
- X if (n>=type)
- X goto out;
- X val = (val*type)+n;
- X }
- out:
- X return(val * sign);
- X}
- END_OF_FILE
- if test 1561 -ne `wc -c <'atov.c'`; then
- echo shar: \"'atov.c'\" unpacked with wrong size!
- fi
- # end of 'atov.c'
- fi
- if test -f 'defs.h' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'defs.h'\"
- else
- echo shar: Extracting \"'defs.h'\" \(870 characters\)
- sed "s/^X//" >'defs.h' <<'END_OF_FILE'
- X/* +-------------------------------------------------------------------+ */
- X/* | Copyright 1991, David Koblas. | */
- X/* | Permission to use, copy, modify, and distribute this software | */
- X/* | and its documentation for any purpose and without fee is hereby | */
- X/* | granted, provided that the above copyright notice appear in all | */
- X/* | copies and that both that copyright notice and this permission | */
- X/* | notice appear in supporting documentation. This software is | */
- X/* | provided "as is" without express or implied warranty. | */
- X/* +-------------------------------------------------------------------+ */
- X
- typedef struct cmd_s {
- X char *name;
- X int nargs, nopts;
- X int margs, mopts;
- X char **args, **opts;
- X struct cmd_s *next;
- X} cmd_t;
- X
- extern cmd_t *First, *Build();
- X
- X#define MAXSTRLEN 256
- END_OF_FILE
- if test 870 -ne `wc -c <'defs.h'`; then
- echo shar: \"'defs.h'\" unpacked with wrong size!
- fi
- # end of 'defs.h'
- fi
- if test -f 'lex.l' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'lex.l'\"
- else
- echo shar: Extracting \"'lex.l'\" \(5725 characters\)
- sed "s/^X//" >'lex.l' <<'END_OF_FILE'
- X%{
- X/* +-------------------------------------------------------------------+ */
- X/* | Copyright 1991, David Koblas. | */
- X/* | Permission to use, copy, modify, and distribute this software | */
- X/* | and its documentation for any purpose and without fee is hereby | */
- X/* | granted, provided that the above copyright notice appear in all | */
- X/* | copies and that both that copyright notice and this permission | */
- X/* | notice appear in supporting documentation. This software is | */
- X/* | provided "as is" without express or implied warranty. | */
- X/* +-------------------------------------------------------------------+ */
- X
- X#include <stdio.h>
- X#include <varargs.h>
- X#include <ctype.h>
- X#include "defs.h"
- X
- static cmd_t *newcmd();
- char *savestr();
- X
- extern char *index();
- X
- int yyline = 1;
- X
- X%}
- X
- WS [ \t]*
- NWS [^ \n\t;]+
- X
- X%s ARGS
- X
- X%%
- X int state = 0;
- X cmd_t *cmd;
- X
- X#[^\n]* ;
- X\n { yyline++; BEGIN 0; }
- X^[^ \n\t]+ { cmd = newcmd(yytext);
- X state = (strcmp(yytext,"DEFAULT")==0) ? 1 : 0;
- X BEGIN ARGS; }
- X^{WS} BEGIN ARGS;
- X<ARGS>";" state++;
- X<ARGS>{NWS} addarg(state, cmd, yytext);
- X<ARGS>{WS} ;
- X%%
- X#include <sys/types.h>
- X#include <sys/stat.h>
- X#include <syslog.h>
- X
- msg(va_alist)
- X va_dcl
- X{
- X#if 0
- X va_list ap;
- X char *s;
- X
- X va_start(ap);
- X s = va_arg(ap, char *);
- X fprintf(stderr,"line %d: ",yyline);
- X vfprintf(stderr, s, ap);
- X fputc('\n', stderr);
- X va_end(ap);
- X#endif
- X}
- X
- static addarg(state, cmd, str)
- int state;
- cmd_t *cmd;
- char *str;
- X{
- X if (state == 0) {
- X msg("cmd='%s' add arg '%s'",cmd->name,str);
- X if (cmd->margs == cmd->nargs) {
- X cmd->margs += cmd->margs;
- X cmd->args = (char **)realloc(cmd->args,
- X sizeof(char *) * cmd->margs);
- X if (cmd->args == NULL)
- X fatal("Unable to groupw args");
- X }
- X cmd->args[cmd->nargs++] = savestr(str);
- X } else if (state == 1) {
- X msg("cmd='%s' add opt '%s'",cmd->name,str);
- X if (cmd->mopts == cmd->nopts) {
- X cmd->mopts += cmd->mopts;
- X cmd->opts = (char **)realloc(cmd->opts,
- X sizeof(char *) * cmd->mopts);
- X if (cmd->opts == NULL)
- X fatal("Unable to groupw opts");
- X }
- X cmd->opts[cmd->nopts++] = savestr(str);
- X } else {
- X fatal("bad state (%d) received\n",state);
- X }
- X}
- X
- char *savestr(str)
- char *str;
- X{
- X char *s = (char *)malloc(strlen(str)+1);
- X
- X if (s == NULL)
- X fatal("No string space");
- X
- X strcpy(s, str);
- X return s;
- X}
- X
- static cmd_t *newcmd(name)
- char *name;
- X{
- X cmd_t *cmd = (cmd_t *)malloc(sizeof(cmd_t));
- X
- X cmd->next = First;
- X First = cmd;
- X
- X if (cmd == NULL)
- X fatal("Unable to alloc space for new command");
- X
- X cmd->name = savestr(name);
- X cmd->nargs = 0; cmd->margs = 16;
- X cmd->nopts = 0; cmd->mopts = 16;
- X cmd->args = (char **)malloc(sizeof(char *)*cmd->margs);
- X cmd->opts = (char **)malloc(sizeof(char *)*cmd->mopts);
- X
- X return cmd;
- X}
- X
- ReadFile(file)
- char *file;
- X{
- X struct stat statbuf;
- X FILE *fd;
- X
- X if ((stat(file, &statbuf) < 0) ||
- X (statbuf.st_uid != 0) || /* Owned by root */
- X ((statbuf.st_mode & 0077) != 0)) {/* no perm */
- X syslog(LOG_ERR, "Permission problems on %s", file);
- X fatal("Permission problems on %s", file);
- X }
- X if ((fd = fopen(file,"r")) == NULL) {
- X syslog(LOG_ERR, "Couldn't open on %s", file);
- X fatal("Couldn't open %s", file);
- X }
- X
- X yyin = fd;
- X yylex();
- X
- X return 0;
- X}
- X
- CountArgs(cmd)
- cmd_t *cmd;
- X{
- X int i, val;
- X int wild = 0, max = 0;
- X char *cp, *np, str[MAXSTRLEN];
- X
- X for (i = 0; i < cmd->nargs; i++) {
- X np = cmd->args[i];
- X
- X while ((cp = index(np, '$')) != NULL) {
- X if ((cp != cmd->args[i]) && (*(cp-1) == '\\'))
- X np = cp + 1;
- X else
- X break;
- X }
- X
- X if (cp == NULL)
- X continue;
- X if (*(cp+1) == '*') {
- X wild = 1;
- X continue;
- X }
- X
- X cp++;
- X np = cp;
- X
- X while (isdigit(*cp))
- X cp++;
- X if ((cp - np) == 0)
- X continue;
- X strncpy(str, np, cp - np);
- X str[cp - np] = '\0';
- X val = atoi(str);
- X if (val > max)
- X max = val;
- X }
- X
- X if (wild)
- X return -max;
- X return max;
- X}
- X
- static int cmpopts(a, b)
- char *a, *b;
- X{
- X char *cp_a, *cp_b;
- X int val_a, val_b;
- X char str_a[MAXSTRLEN], str_b[MAXSTRLEN];
- X
- X if (*a != '$' && *b != '$')
- X return 0;
- X if (*a == '$' && *b != '$')
- X return -1;
- X if (*a != '$' && *b == '$')
- X return 1;
- X
- X cp_a = ++a;
- X cp_b = ++b;
- X while ((*cp_a != '\0') && (*cp_a != '='))
- X if (! isdigit(*cp_a))
- X break;
- X while ((*cp_b != '\0') && (*cp_b != '='))
- X if (! isdigit(*cp_b))
- X break;
- X
- X if (*cp_a != '=' && *cp_b != '=')
- X return 0;
- X if (*cp_a == '=' && *cp_b != '=')
- X return -1;
- X if (*cp_a != '=' && *cp_b == '=')
- X return 1;
- X
- X strncpy(str_a, a, cp_a - a);
- X str_a[cp_a - a] = '\0';
- X val_a = atoi(str_a);
- X strncpy(str_b, b, cp_b - a);
- X str_a[cp_b - b] = '\0';
- X val_b = atoi(str_b);
- X
- X if (val_a < val_b)
- X return -1;
- X if (val_a > val_b)
- X return 1;
- X return 0;
- X}
- X
- sortopts(cmd)
- cmd_t *cmd;
- X{
- X qsort(cmd->opts, cmd->nopts, sizeof(char *), cmpopts);
- X}
- X
- cmd_t *Build(def, cmd)
- cmd_t *def, *cmd;
- X{
- X cmd_t *new = newcmd("");
- X char defname[MAXSTRLEN], optname[MAXSTRLEN], *cp;
- X int i, j;
- X extern char *index();
- X
- X if (cmd == NULL)
- X return def;
- X if (def == NULL)
- X return cmd;
- X
- X for (i = 0; i < cmd->nargs; i++)
- X addarg(0, new, cmd->args[i]);
- X
- X for (i = 0; i < def->nopts; i++) {
- X if ((cp = index(def->opts[i], '=')) == NULL)
- X strcpy(defname, def->opts[i]);
- X else {
- X int l = cp - def->opts[i];
- X strncpy(defname, def->opts[i], l);
- X defname[l] = '\0';
- X }
- X for (j = 0; j < cmd->nopts; j++) {
- X if ((cp = index(cmd->opts[j], '=')) == NULL)
- X strcpy(optname, cmd->opts[j]);
- X else {
- X int l = cp - cmd->opts[j];
- X strncpy(optname, cmd->opts[j], l);
- X optname[l] = '\0';
- X }
- X if (strcmp(defname, optname) == 0)
- X def->opts[i][0] = '\0';
- X }
- X if (def->opts[i][0] != '\0')
- X addarg(1, new, def->opts[i]);
- X }
- X for (j = 0; j < cmd->nopts; j++)
- X addarg(1, new, cmd->opts[j]);
- X
- X /* sortopts(new); */
- X
- X return new;
- X}
- END_OF_FILE
- if test 5725 -ne `wc -c <'lex.l'`; then
- echo shar: \"'lex.l'\" unpacked with wrong size!
- fi
- # end of 'lex.l'
- fi
- if test -f 'main.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'main.c'\"
- else
- echo shar: Extracting \"'main.c'\" \(11440 characters\)
- sed "s/^X//" >'main.c' <<'END_OF_FILE'
- X/* +-------------------------------------------------------------------+ */
- X/* | Copyright 1991, David Koblas. | */
- X/* | Permission to use, copy, modify, and distribute this software | */
- X/* | and its documentation for any purpose and without fee is hereby | */
- X/* | granted, provided that the above copyright notice appear in all | */
- X/* | copies and that both that copyright notice and this permission | */
- X/* | notice appear in supporting documentation. This software is | */
- X/* | provided "as is" without express or implied warranty. | */
- X/* +-------------------------------------------------------------------+ */
- X
- X#include <stdio.h>
- X#include <varargs.h>
- X#include <syslog.h>
- X#include <pwd.h>
- X#include <grp.h>
- X#include <ctype.h>
- X#include "defs.h"
- X#include "regexp.h"
- X
- X#ifndef LOG_AUTH
- X/*
- X** Pmax's don't have LOG_AUTH
- X*/
- X#define LOG_AUTH LOG_WARNING
- X#endif
- X
- X/*
- X** File containing 'op' definitions.
- X*/
- X#define ACCESS_FILE "/etc/op.access"
- X
- X#define MAXARG 1024
- X#define MAXENV MAXARG
- X
- extern char *index();
- extern char *savestr();
- extern char *getpass(), *crypt();
- X
- char *Progname;
- cmd_t *Find();
- cmd_t *First = NULL;
- X
- Usage()
- X{
- X fatal("Usage: %s mnemonic [args]\n %s -h [-u username] mnemonic",
- X Progname, Progname);
- X}
- X
- main(argc, argv)
- int argc;
- char **argv;
- X{
- X int num, argStart = 1;
- X char user[MAXSTRLEN];
- X cmd_t *cmd, *def, *new;
- X struct passwd *pw;
- X int hflag = 0;
- X char *uptr = NULL;
- X
- X Progname = argv[0];
- X
- X while (1) {
- X if (argStart >= argc)
- X break;
- X
- X if (strcmp("-h", argv[argStart]) == 0) {
- X hflag++;
- X argStart++;
- X } else if (strcmp("-u", argv[argStart]) == 0) {
- X if (strlen(argv[argStart]) == 2) {
- X if (argStart+1 >= argc)
- X Usage();
- X argStart++;
- X uptr = argv[argStart];
- X }
- X argStart++;
- X } else if (strcmp("-uh", argv[argStart]) == 0) {
- X hflag++;
- X if (strlen(argv[argStart]) == 3) {
- X if (argStart+1 >= argc)
- X Usage();
- X argStart++;
- X uptr = argv[argStart];
- X }
- X argStart++;
- X } else if (strcmp("-hu", argv[argStart]) == 0) {
- X hflag++;
- X if (strlen(argv[argStart]) == 3) {
- X if (argStart+1 >= argc)
- X Usage();
- X argStart++;
- X uptr = argv[argStart];
- X }
- X argStart++;
- X } else {
- X break;
- X }
- X }
- X
- X if (openlog("op", 0, LOG_AUTH) < 0)
- X fatal("openlog failed");
- X
- X ReadFile(ACCESS_FILE);
- X
- X if (hflag) {
- X if (uptr != NULL) {
- X if (getuid() != 0)
- X fatal("Permission denied for -u option");
- X }
- X }
- X if (uptr != NULL)
- X Usage();
- X
- X if (argStart >= argc)
- X Usage();
- X
- X def = Find("DEFAULT");
- X cmd = Find(argv[argStart]);
- X
- X if (cmd == NULL)
- X fatal("No such command %s", argv[1]);
- X
- X argc -= argStart;
- X argv += argStart;
- X
- X new = Build(def, cmd);
- X num = CountArgs(new);
- X
- X if ((num < 0) && ((argc-1) < -num))
- X fatal("Improper number of arguments");
- X if ((num > 0) && ((argc-1) != num))
- X fatal("Improper number of arguments");
- X if (num <0)
- X num = -num;
- X
- X if ((pw = getpwuid(getuid())) == NULL)
- X exit(1);
- X strcpy(user, pw->pw_name);
- X if (Verify(new, num, argc, argv) < 0) {
- X syslog(LOG_NOTICE, "user %s FAILED to execute '%s'",
- X user, argv[0]);
- X fatal("Permission denied");
- X } else {
- X syslog(LOG_NOTICE, "user %s SUCCEDED to execute '%s'",
- X user, argv[0]);
- X }
- X
- X return Go(new, num, argc, argv);
- X}
- X
- fatal(va_alist)
- X va_dcl
- X{
- X va_list ap;
- X char *s;
- X
- X va_start(ap);
- X s = va_arg(ap, char *);
- X vfprintf(stderr, s, ap);
- X fputc('\n', stderr);
- X va_end(ap);
- X
- X exit(1);
- X}
- X
- cmd_t *Find(name)
- char *name;
- X{
- X cmd_t *cmd;
- X
- X for (cmd = First; cmd != NULL; cmd = cmd ->next) {
- X if (strcmp(cmd->name, name) == 0)
- X break;
- X }
- X
- X return cmd;
- X}
- X
- char *FindOpt(cmd, str)
- cmd_t *cmd;
- char *str;
- X{
- X static char nul[2] = "";
- X int i;
- X char *cp;
- X
- X for (i = 0; i < cmd->nopts; i++) {
- X if ((cp = index(cmd->opts[i], '=')) == NULL) {
- X if (strcmp(cmd->opts[i], str) == 0)
- X return nul;
- X } else {
- X int l = cp - cmd->opts[i];
- X if (strncmp(cmd->opts[i], str, l) == 0)
- X return cp+1;
- X }
- X }
- X
- X return NULL;
- X}
- X
- char *GetField(cp, str)
- char *cp, *str;
- X{
- X if (*cp == '\0')
- X return NULL;
- X
- X while ((*cp != '\0') && (*cp != ',')) {
- X if (*cp == '\\')
- X if (*(cp+1) == ',') {
- X *str++ = ',';
- X cp++;
- X } else
- X *str++ = '\\';
- X else
- X *str++ = *cp;
- X cp++;
- X }
- X
- X *str = '\0';
- X
- X return (*cp == '\0') ? cp : (cp+1);
- X}
- X
- Verify(cmd, num, argc, argv)
- cmd_t *cmd;
- int argc;
- int num;
- char **argv;
- X{
- X int gr_fail = 1, uid_fail = 1;
- X int i, j, val;
- X char *np, *cp, str[MAXSTRLEN], buf[MAXSTRLEN];
- X regexp *reg = NULL;
- X struct passwd *pw, spw;
- X struct group *gr;
- X
- X if ((pw = getpwuid(getuid())) == NULL)
- X return -1;
- X
- X if ((cp=FindOpt(cmd, "password")) != NULL) {
- X if ((np = getpass("Password:")) == NULL)
- X return -1;
- X if (((cp = GetField(cp, str)) != NULL) &&
- X ((pw = getpwnam(str)) == NULL))
- X return -1;
- X if (strcmp(crypt(np, pw->pw_passwd), pw->pw_passwd) != 0)
- X return -1;
- X }
- X
- X if ((pw = getpwuid(getuid())) == NULL)
- X return -1;
- X
- X if ((cp = FindOpt(cmd, "groups")) != NULL) {
- X for (cp=GetField(cp, str); cp!=NULL; cp=GetField(cp, str)) {
- X if (re_comp(str) != 0)
- X return -1;
- X if ((gr = getgrgid(pw->pw_gid)) != NULL) {
- X if (re_exec(gr->gr_name) == 1) {
- X gr_fail = 0;
- X break;
- X }
- X }
- X setgrent();
- X while ((gr = getgrent()) != NULL) {
- X i = 0;
- X while (gr->gr_mem[i] != NULL) {
- X if (strcmp(gr->gr_mem[i],
- X pw->pw_name)==0)
- X break;
- X i++;
- X }
- X if ((gr->gr_mem[i] != NULL) &&
- X (re_exec(gr->gr_name) == 1)) {
- X gr_fail = 0;
- X break;
- X }
- X }
- X }
- X }
- X if (gr_fail && ((cp = FindOpt(cmd, "users")) != NULL)) {
- X for (cp=GetField(cp, str); cp!=NULL; cp=GetField(cp, str)) {
- X if (re_comp(str) != 0)
- X return -1;
- X if (re_exec(pw->pw_name) == 1) {
- X uid_fail = 0;
- X break;
- X }
- X }
- X }
- X
- X if (gr_fail && uid_fail)
- X return -1;
- X
- X for (i = 0; i < cmd->nopts; i++) {
- X if ((cmd->opts[i][0] != '$') ||
- X ((cp = index(cmd->opts[i], '=')) == NULL))
- X continue;
- X if (cmd->opts[i][1] != '*') {
- X for (np = cmd->opts[i] + 1; np != cp; np++)
- X if (!isdigit(*np))
- X break;
- X if (np != cp)
- X continue;
- X } else {
- X if (cmd->opts[i][2] != '=')
- X continue;
- X np = cmd->opts[i] + 3;
- X for (j = num+1; j < argc; j++) {
- X cp = np;
- X for (cp=GetField(cp, str); cp!=NULL;
- X cp=GetField(cp, str)) {
- X if (re_comp(str) != 0)
- X return -1;
- X if (re_exec(argv[j]) == 1)
- X break;
- X }
- X if (cp == NULL)
- X return -1;
- X }
- X }
- X
- X strncpy(str, cmd->opts[i] + 1, cp - cmd->opts[i] - 1);
- X str[cp - cmd->opts[i] - 1] = '\0';
- X val = atoi(str);
- X
- X if (val >= argc)
- X continue;
- X cp++;
- X np = cp;
- X if (reg != NULL) {
- X for (cp=GetField(cp, str); cp!=NULL;
- X cp=GetField(cp, str)) {
- X regsub(reg, str, buf);
- X if (strcmp(buf, argv[val]) == 0)
- X break;
- X }
- X
- X if (cp != NULL)
- X continue;
- X
- X free(reg);
- X reg = NULL;
- X }
- X
- X if ((reg == NULL) || (cp == NULL)) {
- X cp = np;
- X for (cp=GetField(cp, str); cp!=NULL;
- X cp=GetField(cp, str)) {
- X if ((reg = regcomp(str)) == NULL)
- X return -1;
- X if (regexec(reg, argv[val]) == 1)
- X break;
- X
- X free(reg);
- X reg = NULL;
- X }
- X }
- X
- X if (cp == NULL)
- X return -1;
- X }
- X}
- X
- Go(cmd, num, argc, argv)
- cmd_t *cmd;
- int argc;
- int num;
- char **argv;
- X{
- X extern char **environ;
- X int i, j, flag, val, len = 0;
- X char *cp, *np;
- X struct passwd *pw;
- X struct group *gr;
- X int ngroups=0, gidset[256];
- X int curenv = 0, curarg = 0;
- X char *new_envp[MAXENV];
- X char *new_argv[MAXARG];
- X char str[MAXSTRLEN], buf[4*MAXSTRLEN];
- X
- X if ((cp = FindOpt(cmd, "gid")) == NULL) {
- X ; /* don't have a default */
- X } else {
- X for (; cp!=NULL; cp=GetField(cp, str)) {
- X if ((gr = getgrnam(cp)) != NULL)
- X gidset[ngroups++] = gr->gr_gid;
- X }
- X if (ngroups == 0)
- X fatal("invalid groups");
- X if (setgroups(ngroups, gidset) < 0)
- X fatal("setgroups failed");
- X if (setgid(gidset[0]))
- X fatal("setgid failed");
- X }
- X
- X if ((cp = FindOpt(cmd, "uid")) == NULL) {
- X if (setuid(geteuid()) < 0)
- X fatal("Unable to set uid to default", cp);
- X } else {
- X if ((pw = getpwnam(cp)) == NULL) {
- X if (setuid(atoi(cp)) < 0)
- X fatal("Unable to set uid to %s", cp);
- X }
- X if (setuid(pw->pw_uid) < 0)
- X fatal("Unable to set uid to %s", cp);
- X }
- X
- X if ((cp = FindOpt(cmd, "umask")) == NULL) {
- X if (umask(0022) < 0)
- X fatal("Unable to set umask to default");
- X } else {
- X if (umask(atov(cp, 8)) < 0)
- X fatal("Unable to set umask to %s", cp);
- X }
- X
- X if ((cp = FindOpt(cmd, "chroot")) == NULL) {
- X ; /* don't have a default */
- X } else {
- X if (chroot(cp) < 0)
- X fatal("Unable to chroot to %s", cp);
- X }
- X
- X if ((cp = FindOpt(cmd, "dir")) == NULL) {
- X ; /* don't have a default */
- X } else {
- X if (chdir(cp) < 0)
- X fatal("Unable to chdir to %s", cp);
- X }
- X
- X if (FindOpt(cmd, "environment") == NULL) {
- X for (i = 0; i < cmd->nopts; i++) {
- X if (cmd->opts[i][0] != '$')
- X continue;
- X cp = cmd->opts[i] + 1;
- X flag = 0;
- X while ((*cp != '\0') && (*cp != '=')) {
- X if (! isdigit(*cp))
- X flag = 1;
- X cp++;
- X }
- X if (! flag)
- X continue;
- X if (index(cmd->opts[i], '=') != NULL) {
- X new_envp[curenv++] = cmd->opts[i] + 1;
- X continue;
- X }
- X for (j = 0; environ[j] != NULL ; j++) {
- X if ((cp = index(environ[j], '=')) == NULL)
- X continue;
- X if (strncmp(cmd->opts[i] + 1, environ[j],
- X cp - environ[j]) == 0) {
- X new_envp[curenv++] = environ[j];
- X break;
- X }
- X }
- X }
- X } else {
- X for (i = 0; environ[i] != NULL; i++)
- X new_envp[curenv++] = environ[i];
- X }
- X new_envp[curenv] = NULL;
- X
- X if (strcmp("MAGIC_SHELL", cmd->args[0]) == 0) {
- X for (i = 0; environ[i] != NULL; i++)
- X if (strncmp("SHELL=", environ[i], 6) == 0)
- X break;
- X
- X if (environ[i] != NULL)
- X new_argv[curarg++] = environ[i] + 6;
- X else {
- X fprintf(stderr,"No shell\n");
- X exit(1);
- X }
- X
- X if (argc != 1) {
- X new_argv[curarg++] = "-c";
- X
- X for (i = 1; i < argc; i++)
- X len += strlen(argv[i]) + 1;
- X
- X if ((cp = (char *)malloc(len + 10)) == NULL) {
- X fprintf(stderr, "Unable to create buffer");
- X exit(1);
- X }
- X
- X len = 0;
- X *cp = '\0';
- X
- X for (i = 1; i < argc; i++) {
- X strcat(cp, argv[i]);
- X strcat(cp, " ");
- X }
- X new_argv[curarg++] = cp;
- X }
- X } else {
- X for (i = 0; i < cmd->nargs; i++) {
- X np = cmd->args[i];
- X
- X while ((cp = index(np, '$')) != NULL) {
- X if ((cp != cmd->args[i]) && (*(cp-1) == '\\'))
- X np = cp + 1;
- X else
- X break;
- X }
- X
- X if (cp == NULL) {
- X new_argv[curarg++] = cmd->args[i];
- X continue;
- X }
- X if (*(cp+1) == '*') {
- X for (j = num + 1; j < argc; j++) {
- X new_argv[curarg++] = argv[j];
- X }
- X continue;
- X }
- X
- X cp++;
- X np = cp;
- X while (isdigit(*cp))
- X cp++;
- X if ((cp - np) == 0) {
- X new_argv[curarg++] = cmd->args[i];
- X continue;
- X }
- X strncpy(str, np, cp - np);
- X str[cp - np] = '\0';
- X val = atoi(str);
- X buf[0] = '\0';
- X strncpy(buf, cmd->args[i], np - cmd->args[i] - 1);
- X strcat(buf, argv[val]);
- X strcat(buf, cp);
- X new_argv[curarg++] = savestr(buf);
- X }
- X }
- X new_argv[curarg] = NULL;
- X
- X if (execve(new_argv[0], new_argv, new_envp) < 0)
- X perror("execve");
- X}
- X
- output(cmd)
- cmd_t *cmd;
- X{
- X int i;
- X
- X printf("cmd '%s'\n",cmd->name);
- X printf("\n args\t");
- X for (i = 0; i < cmd->nargs; i++)
- X printf("'%s' ",cmd->args[i]);
- X printf("\n opts\t");
- X for (i = 0; i < cmd->nopts; i++)
- X printf("'%s' ",cmd->opts[i]);
- X printf("\n");
- X}
- END_OF_FILE
- if test 11440 -ne `wc -c <'main.c'`; then
- echo shar: \"'main.c'\" unpacked with wrong size!
- fi
- # end of 'main.c'
- fi
- if test -f 'op.1' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'op.1'\"
- else
- echo shar: Extracting \"'op.1'\" \(6076 characters\)
- sed "s/^X//" >'op.1' <<'END_OF_FILE'
- X.TH OP 1 "December 14, 1989"
- X.UC 4
- X.SH NAME
- op \- operator access
- X.SH SYNOPSIS
- X.B op
- mnemonic [arg]
- X.SH DESCRIPTION
- The
- X.I op
- tool provides a flexible means for system administrators to grant
- trusted users access to certain
- X.B root
- operations without having to give them full superuser privileges.
- Different sets of users may access different operations, and the
- security-related aspects of environment of each
- operation can be carefully controlled.
- X.PP
- The fields of the entries in
- X.I op.access
- are separated by white space. Each entry may span several lines and
- continues until the next alphanumeric string is found at the beginning of
- a lines (which is taken to be the next
- X.I mnemonic,
- and thus the beginning of a new entry). Comments may be embedded
- beginning with a # character. Each entry in op
- X.I op.access
- has the following form:
- X.RS
- X.DT
- X\fImnemonic command \fR[\fI arg ... \fR]\fI ; \fR[\fI option ... \fR]
- X.RE
- where the fields are interpreted in the following manner:
- X.TP
- X.I mnemonic
- a unique, alphanumeric identifier for each operator function.
- X.TP
- X.I command
- the full pathname of the executable to be run by
- X.I op
- when the associated
- X.I mnemonic
- is chosen.
- X.TP
- X.I arg(s)
- any arguments, either literal or variable, needed by
- X.I command.
- Literal arguments are simply specified directly, like specific command
- options (\fB0Gun\fR) or files (\fB/dev/rmt20\fR). Variable arguments
- are specified here as \fB$1, $2 ... $\fR\fIn\fR; these are described
- more fully in the options section below. \fB$*\fR indicates any number
- trailing arguments.
- X.TP
- X.I option(s)
- a set of optional parameters to specify settings or restoring for the
- particular
- X.I mnemonic,
- define variable arguments specified for the
- X.I command,
- space and are of the form
- X.I keyword=value.
- The absence of a specific list of values separated by commas, where
- appropriate.
- There should be no white space in each element of the
- X.I value
- string unless quoted. The
- X.I keyword
- is any of the following types:
- X.TP
- X.B uid
- Set the user id to the value specified. The value can be numeric user
- ID or a login name. The default is
- X.B root.
- X.TP
- X.B gid
- Set the group id's to the values specified. Each value can be a numeric
- group ID or a group name.
- X.TP
- X.B dir
- Change the current working directory to the path specified.
- X.TP
- X.B chroot
- Change the root directory to the path specified using
- X.I chroot.
- X.TP
- X.B umask
- Set the file creation umask to the octal value specified. The default
- is to set it to
- X.B 022.
- X.TP
- X.B groups
- Allow any user who belongs to a group listed here to execute this
- X.I op
- function. The default is not to allow any specific group.
- X.TP
- X.B users
- Allow any user listed here to execute this
- X.I op
- function. The default is not allow any specific users. You may user
- the regular expression .* to indicate that all users may use this
- mnemonic.
- X.TP
- X.B password
- Queries the user for a password, if there is no = part the
- users own password is asked.
- X.TP
- X.BI $VAR
- where
- X.I VAR
- is the name of an environment variable. The specified environment
- case, simply using
- X.I $VAR with no = part (as in
- X.B $USER)
- means that this environment variable is inherited unchanged from
- the caller's shell.
- X.TP
- X.B environment
- Disables the destruction of the users environment.
- X.TP
- X.B $n
- defines the \fIn\fRth variable argument specified in the command
- X.I arg
- list. The value for this type may be a comma-separated list of regular
- expressions using \fIegrep\fR(1). option defines the range of values
- allowed for the variable arguments A variable argument specified as a
- command
- X.I arg
- but not described in the
- X.I options
- section may take on any value. If an argument does not match any
- of its permitted values, then a diagnostic is printed and the
- command is not executed. When using '(' syntax to pass values
- to other options, only the next options can use values from
- the previous search.
- X.TP
- X.B $*
- is used in the
- X.I options
- section to place restriction on the trailing arguments
- specified as $* in the
- X.I args
- section. If any of these (possibly many) arguments do not match, then
- a diagnostic is printed, and the command is not executed.
- X.PP
- There can also be a special entry in the file beginning at the first
- non-comment line
- that can define default values to override the builtin defaults listed
- here, yet still be overridden by any entry that wants to redefine any of
- the keyword fields described above. It should have the following format:
- X.RS
- X.DT
- X\fBDEFAULT\fR \fIkeyword_option\fR
- X.RE
- where \fIkeyword_option\fR is a \fIkeyword=value\fR string mentioned above
- under \fIoptions\fR.
- X.PP
- It should be noted that if any regular
- X.I mnemonic
- entry defines its own
- X.I option,
- the value given for that entry must explicitly include the item from the
- DEFAULT line if the default values is to be included. That is, the
- X.I options
- definitions completely override any
- defaults; they do not add to them In this way, if a value specified on
- the DEFAULT line for
- X.B users
- or
- X.B groups
- X(for example) needs to be "erased" without redefining new values
- X(that is, we want no users or groups to be allowed to run the mnemonic),
- then the default value must be overridden with nothing (as in
- X\fBusers=\fR). For the
- X.B users
- or
- X.B groups
- fields, such a null setting has the effect of setting the list of
- allowable users or groups to be empty. For the other keywords (\fR
- uid, gid, dir, chroot, \fRand\fB umask\fR), a null setting leaves that
- attribute as it is upon invocation of the
- X.I op
- program, overriding any defaults.
- X.PP
- Another note is that if the
- X.I command
- for a
- X.I mnemonic
- is
- X.B MAGIC_SHELL
- then a shell (using the users $SHELL environment variable) is created,
- if there are arguments in addition to the
- X.I mnemonic
- on the command line then the shell is invoked "-c args".
- X.SH FILES
- X.DT
- X/etc/op.access access control description
- X.SH "SEE ALSO"
- su(1), chroot(2), egrep(1)
- X.SH CREDIT
- X.B "Op: A flexible Tool for Restricted Superuser Access",
- by
- X.I "Tom Christiansen",
- CONVEX Computer Corporation,
- X.B "Proceedings of the Large Installation Systems Administration III Workshop"
- END_OF_FILE
- if test 6076 -ne `wc -c <'op.1'`; then
- echo shar: \"'op.1'\" unpacked with wrong size!
- fi
- # end of 'op.1'
- fi
- if test -f 'op.access' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'op.access'\"
- else
- echo shar: Extracting \"'op.access'\" \(1952 characters\)
- sed "s/^X//" >'op.access' <<'END_OF_FILE'
- X# first, define the site defaults we want to use here
- X# we would like the people in 'operator' group to be able to execute
- X# almost everything, so it iseasier to put it here than on every line...
- X# set up default envariables
- X#
- DEFAULT groups=operator $USER $TERM $PATH=/usr/ucb:/usr/bin:/bin
- X#
- X# find out who's filled up the disk; anyone may do this
- X#
- full /usr/etc/quot $1; users=.*
- X#
- X# filesystem backups
- X#
- daily /etc/dump 5Gun $1; $1=/,/usr[0-9]*,/project
- weekly /etc/dump 0Gun $1; $1=/,/usr[0-9]*,/project
- X#
- X# tape handling commands
- X# must include 'operator' if we want them to be allowed as well
- X#
- tape /etc/tpc $1 $2; groups=tapeopers,operator users=boss
- X $1=enable,disable,stop,restart $2=all,unit[01]
- X#
- mounted /etc/tpc mounted unit$1 $2; $1=[0-3]
- X#
- X# taking the system down
- X# $1 shows a good user of regular expression;
- X# $2 can be anything, but is required; no instant shutdowns
- X#
- shutdown /etc/shutdown -h $1 $2; $1=+[1-9][0-9]*,[0-9]*:[0-9]*
- reboot /etc/shutdown -r $1 $2; $1=+[1-9][0-9]*,[0-9]*:[0-9]*
- X#
- X# start up disco daemon
- disco /etc/opbin/start_disco ; uid=disco gid=proj dir=/scratch
- X umask=027 groups=geo,disco users=snoopy,linus
- X $USER=disco $SHELL=/bin/shell
- X#
- X# let certain people mount and unmount the removable drive
- X#
- rdsmount /etc/mount $1 $2; groups=operator,swdev,disco
- X users=bob,steve $1=/dev/dd0[a-z] $2=/.*
- rdsumount /etc/umount $1 ; groups=operator,swdev,disco
- X users=bob,steve $1=/dev/dd0[a-z]
- X#
- X# allow operators to give files away; notice that they
- X# must give at least two s, but may give more
- X#
- chown /bsd43/bin//chown $1 $2 $*; $1=[a-z0-9][a-z0-9]*
- X#
- X# permit development personnel to run install
- X#
- inst /usr/bin/install -o root -g system $1 $2; groups=devel
- X $2=/bin,/usr/bin,/usr/ucb,/usr/new,/usr/local
- X#
- nfsmount /etc/mount -o timeo=100,hard,inter $1 $2;
- X groups=devel,operator
- X $1=([a-zA-Z0-9_]*):(.*) $2=/remote/\1\2
- X
- foo /bin/cat $1; users=koblas
- X
- shell MAGIC_SHELL ; password environment
- END_OF_FILE
- if test 1952 -ne `wc -c <'op.access'`; then
- echo shar: \"'op.access'\" unpacked with wrong size!
- fi
- # end of 'op.access'
- fi
- if test -f 'regerror.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'regerror.c'\"
- else
- echo shar: Extracting \"'regerror.c'\" \(224 characters\)
- sed "s/^X//" >'regerror.c' <<'END_OF_FILE'
- X#include <stdio.h>
- X
- void
- regerror(s)
- char *s;
- X{
- X#ifdef ERRAVAIL
- X error("regexp: %s", s);
- X#else
- X/*
- X fprintf(stderr, "regexp(3): %s\n", s);
- X exit(1);
- X*/
- X return; /* let std. egrep handle errors */
- X#endif
- X /* NOTREACHED */
- X}
- END_OF_FILE
- if test 224 -ne `wc -c <'regerror.c'`; then
- echo shar: \"'regerror.c'\" unpacked with wrong size!
- fi
- # end of 'regerror.c'
- fi
- if test -f 'regexp.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'regexp.c'\"
- else
- echo shar: Extracting \"'regexp.c'\" \(31149 characters\)
- sed "s/^X//" >'regexp.c' <<'END_OF_FILE'
- X/*
- X * regcomp and regexec -- regsub and regerror are elsewhere
- X *
- X * Copyright (c) 1986 by University of Toronto.
- X * Written by Henry Spencer. Not derived from licensed software.
- X *
- X * Permission is granted to anyone to use this software for any
- X * purpose on any computer system, and to redistribute it freely,
- X * subject to the following restrictions:
- X *
- X * 1. The author is not responsible for the consequences of use of
- X * this software, no matter how awful, even if they arise
- X * from defects in it.
- X *
- X * 2. The origin of this software must not be misrepresented, either
- X * by explicit claim or by omission.
- X *
- X * 3. Altered versions must be plainly marked as such, and must not
- X * be misrepresented as being the original software.
- X *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore,
- X *** hoptoad!gnu, on 27 Dec 1986, to add \n as an alternative to |
- X *** to assist in implementing egrep.
- X *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore,
- X *** hoptoad!gnu, on 27 Dec 1986, to add \< and \> for word-matching
- X *** as in BSD grep and ex.
- X *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore,
- X *** hoptoad!gnu, on 28 Dec 1986, to optimize characters quoted with \.
- X *** THIS IS AN ALTERED VERSION. It was altered by James A. Woods,
- X *** ames!jaw, on 19 June 1987, to quash a regcomp() redundancy.
- X *
- X * Beware that some of this code is subtly aware of the way operator
- X * precedence is structured in regular expressions. Serious changes in
- X * regular-expression syntax might require a total rethink.
- X */
- X#include <stdio.h>
- X#include <ctype.h>
- X#include <regexp.h>
- X#include "regmagic.h"
- X
- X/*
- X * The "internal use only" fields in regexp.h are present to pass info from
- X * compile to execute that permits the execute phase to run lots faster on
- X * simple cases. They are:
- X *
- X * regstart char that must begin a match; '\0' if none obvious
- X * reganch is the match anchored (at beginning-of-line only)?
- X * regmust string (pointer into program) that match must include, or NULL
- X * regmlen length of regmust string
- X *
- X * Regstart and reganch permit very fast decisions on suitable starting points
- X * for a match, cutting down the work a lot. Regmust permits fast rejection
- X * of lines that cannot possibly match. The regmust tests are costly enough
- X * that regcomp() supplies a regmust only if the r.e. contains something
- X * potentially expensive (at present, the only such thing detected is * or +
- X * at the start of the r.e., which can involve a lot of backup). Regmlen is
- X * supplied because the test in regexec() needs it and regcomp() is computing
- X * it anyway.
- X */
- X
- X/*
- X * Structure for regexp "program". This is essentially a linear encoding
- X * of a nondeterministic finite-state machine (aka syntax charts or
- X * "railroad normal form" in parsing technology). Each node is an opcode
- X * plus a "next" pointer, possibly plus an operand. "Next" pointers of
- X * all nodes except BRANCH implement concatenation; a "next" pointer with
- X * a BRANCH on both ends of it is connecting two alternatives. (Here we
- X * have one of the subtle syntax dependencies: an individual BRANCH (as
- X * opposed to a collection of them) is never concatenated with anything
- X * because of operator precedence.) The operand of some types of node is
- X * a literal string; for others, it is a node leading into a sub-FSM. In
- X * particular, the operand of a BRANCH node is the first node of the branch.
- X * (NB this is *not* a tree structure: the tail of the branch connects
- X * to the thing following the set of BRANCHes.) The opcodes are:
- X */
- X
- X/* definition number opnd? meaning */
- X#define END 0 /* no End of program. */
- X#define BOL 1 /* no Match "" at beginning of line. */
- X#define EOL 2 /* no Match "" at end of line. */
- X#define ANY 3 /* no Match any one character. */
- X#define ANYOF 4 /* str Match any character in this string. */
- X#define ANYBUT 5 /* str Match any character not in this string. */
- X#define BRANCH 6 /* node Match this alternative, or the next... */
- X#define BACK 7 /* no Match "", "next" ptr points backward. */
- X#define EXACTLY 8 /* str Match this string. */
- X#define NOTHING 9 /* no Match empty string. */
- X#define STAR 10 /* node Match this (simple) thing 0 or more times. */
- X#define PLUS 11 /* node Match this (simple) thing 1 or more times. */
- X#define WORDA 12 /* no Match "" at wordchar, where prev is nonword */
- X#define WORDZ 13 /* no Match "" at nonwordchar, where prev is word */
- X#define OPEN 20 /* no Mark this point in input as start of #n. */
- X /* OPEN+1 is number 1, etc. */
- X#define CLOSE 30 /* no Analogous to OPEN. */
- X
- X/*
- X * Opcode notes:
- X *
- X * BRANCH The set of branches constituting a single choice are hooked
- X * together with their "next" pointers, since precedence prevents
- X * anything being concatenated to any individual branch. The
- X * "next" pointer of the last BRANCH in a choice points to the
- X * thing following the whole choice. This is also where the
- X * final "next" pointer of each individual branch points; each
- X * branch starts with the operand node of a BRANCH node.
- X *
- X * BACK Normal "next" pointers all implicitly point forward; BACK
- X * exists to make loop structures possible.
- X *
- X * STAR,PLUS '?', and complex '*' and '+', are implemented as circular
- X * BRANCH structures using BACK. Simple cases (one character
- X * per match) are implemented with STAR and PLUS for speed
- X * and to minimize recursive plunges.
- X *
- X * OPEN,CLOSE ...are numbered at compile time.
- X */
- X
- X/*
- X * A node is one char of opcode followed by two chars of "next" pointer.
- X * "Next" pointers are stored as two 8-bit pieces, high order first. The
- X * value is a positive offset from the opcode of the node containing it.
- X * An operand, if any, simply follows the node. (Note that much of the
- X * code generation knows about this implicit relationship.)
- X *
- X * Using two bytes for the "next" pointer is vast overkill for most things,
- X * but allows patterns to get big without disasters.
- X */
- X#define OP(p) (*(p))
- X#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
- X#define OPERAND(p) ((p) + 3)
- X
- X/*
- X * See regmagic.h for one further detail of program structure.
- X */
- X
- X
- X/*
- X * Utility definitions.
- X */
- X#ifndef CHARBITS
- X#define UCHARAT(p) ((int)*(unsigned char *)(p))
- X#else
- X#define UCHARAT(p) ((int)*(p)&CHARBITS)
- X#endif
- X
- X#define FAIL(m) { regerror(m); return(NULL); }
- X#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?')
- X
- X/*
- X * Flags to be passed up and down.
- X */
- X#define HASWIDTH 01 /* Known never to match null string. */
- X#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */
- X#define SPSTART 04 /* Starts with * or +. */
- X#define WORST 0 /* Worst case. */
- X
- X/*
- X * Global work variables for regcomp().
- X */
- static char *regparse; /* Input-scan pointer. */
- static int regnpar; /* () count. */
- static char regdummy;
- static char *regcode; /* Code-emit pointer; ®dummy = don't. */
- static long regsize; /* Code size. */
- X
- X/*
- X * Forward declarations for regcomp()'s friends.
- X */
- X#ifndef STATIC
- X#define STATIC static
- X#endif
- STATIC char *reg();
- STATIC char *regbranch();
- STATIC char *regpiece();
- STATIC char *regatom();
- STATIC char *regnode();
- STATIC char *regnext();
- STATIC void regc();
- STATIC void reginsert();
- STATIC void regtail();
- STATIC void regoptail();
- X#ifdef STRCSPN
- STATIC int strcspn();
- X#endif
- X
- X/*
- X - regcomp - compile a regular expression into internal code
- X *
- X * We can't allocate space until we know how big the compiled form will be,
- X * but we can't compile it (and thus know how big it is) until we've got a
- X * place to put the code. So we cheat: we compile it twice, once with code
- X * generation turned off and size counting turned on, and once "for real".
- X * This also means that we don't allocate space until we are sure that the
- X * thing really will compile successfully, and we never have to move the
- X * code and thus invalidate pointers into it. (Note that it has to be in
- X * one piece because free() must be able to free it all.)
- X *
- X * Beware that the optimization-preparation code in here knows about some
- X * of the structure of the compiled regexp.
- X */
- regexp *
- regcomp(exp)
- char *exp;
- X{
- X register regexp *r;
- X register char *scan;
- X register char *longest;
- X register int len;
- X int flags;
- X extern char *malloc();
- X
- X if (exp == NULL)
- X FAIL("NULL argument");
- X
- X /* First pass: determine size, legality. */
- X if (exp[0] == '.' && exp[1] == '*') exp += 2; /* aid grep */
- X regparse = exp;
- X regnpar = 1;
- X regsize = 0L;
- X regcode = ®dummy;
- X regc(MAGIC);
- X if (reg(0, &flags) == NULL)
- X return(NULL);
- X
- X /* Small enough for pointer-storage convention? */
- X if (regsize >= 32767L) /* Probably could be 65535L. */
- X FAIL("regexp too big");
- X
- X /* Allocate space. */
- X r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize);
- X if (r == NULL)
- X FAIL("out of space");
- X
- X /* Second pass: emit code. */
- X regparse = exp;
- X regnpar = 1;
- X regcode = r->program;
- X regc(MAGIC);
- X if (reg(0, &flags) == NULL)
- X return(NULL);
- X
- X /* Dig out information for optimizations. */
- X r->regstart = '\0'; /* Worst-case defaults. */
- X r->reganch = 0;
- X r->regmust = NULL;
- X r->regmlen = 0;
- X scan = r->program+1; /* First BRANCH. */
- X if (OP(regnext(scan)) == END) { /* Only one top-level choice. */
- X scan = OPERAND(scan);
- X
- X /* Starting-point info. */
- X if (OP(scan) == EXACTLY)
- X r->regstart = *OPERAND(scan);
- X else if (OP(scan) == BOL)
- X r->reganch++;
- X
- X /*
- X * If there's something expensive in the r.e., find the
- X * longest literal string that must appear and make it the
- X * regmust. Resolve ties in favor of later strings, since
- X * the regstart check works with the beginning of the r.e.
- X * and avoiding duplication strengthens checking. Not a
- X * strong reason, but sufficient in the absence of others.
- X */
- X if (flags&SPSTART) {
- X longest = NULL;
- X len = 0;
- X for (; scan != NULL; scan = regnext(scan))
- X if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
- X longest = OPERAND(scan);
- X len = strlen(OPERAND(scan));
- X }
- X r->regmust = longest;
- X r->regmlen = len;
- X }
- X }
- X
- X return(r);
- X}
- X
- X/*
- X - reg - regular expression, i.e. main body or parenthesized thing
- X *
- X * Caller must absorb opening parenthesis.
- X *
- X * Combining parenthesis handling with the base level of regular expression
- X * is a trifle forced, but the need to tie the tails of the branches to what
- X * follows makes it hard to avoid.
- X */
- static char *
- reg(paren, flagp)
- int paren; /* Parenthesized? */
- int *flagp;
- X{
- X register char *ret;
- X register char *br;
- X register char *ender;
- X register int parno;
- X int flags;
- X
- X *flagp = HASWIDTH; /* Tentatively. */
- X
- X /* Make an OPEN node, if parenthesized. */
- X if (paren) {
- X if (regnpar >= NSUBEXP)
- X FAIL("too many ()");
- X parno = regnpar;
- X regnpar++;
- X ret = regnode(OPEN+parno);
- X } else
- X ret = NULL;
- X
- X /* Pick up the branches, linking them together. */
- X br = regbranch(&flags);
- X if (br == NULL)
- X return(NULL);
- X if (ret != NULL)
- X regtail(ret, br); /* OPEN -> first. */
- X else
- X ret = br;
- X if (!(flags&HASWIDTH))
- X *flagp &= ~HASWIDTH;
- X *flagp |= flags&SPSTART;
- X while (*regparse == '|' || *regparse == '\n') {
- X regparse++;
- X br = regbranch(&flags);
- X if (br == NULL)
- X return(NULL);
- X regtail(ret, br); /* BRANCH -> BRANCH. */
- X if (!(flags&HASWIDTH))
- X *flagp &= ~HASWIDTH;
- X *flagp |= flags&SPSTART;
- X }
- X
- X /* Make a closing node, and hook it on the end. */
- X ender = regnode((paren) ? CLOSE+parno : END);
- X regtail(ret, ender);
- X
- X /* Hook the tails of the branches to the closing node. */
- X for (br = ret; br != NULL; br = regnext(br))
- X regoptail(br, ender);
- X
- X /* Check for proper termination. */
- X if (paren && *regparse++ != ')') {
- X FAIL("unmatched ()");
- X } else if (!paren && *regparse != '\0') {
- X if (*regparse == ')') {
- X FAIL("unmatched ()");
- X } else
- X FAIL("junk on end"); /* "Can't happen". */
- X /* NOTREACHED */
- X }
- X
- X return(ret);
- X}
- X
- X/*
- X - regbranch - one alternative of an | operator
- X *
- X * Implements the concatenation operator.
- X */
- static char *
- regbranch(flagp)
- int *flagp;
- X{
- X register char *ret;
- X register char *chain;
- X register char *latest;
- X int flags;
- X
- X *flagp = WORST; /* Tentatively. */
- X
- X ret = regnode(BRANCH);
- X chain = NULL;
- X while (*regparse != '\0' && *regparse != ')' &&
- X *regparse != '\n' && *regparse != '|') {
- X latest = regpiece(&flags);
- X if (latest == NULL)
- X return(NULL);
- X *flagp |= flags&HASWIDTH;
- X if (chain == NULL) /* First piece. */
- X *flagp |= flags&SPSTART;
- X else
- X regtail(chain, latest);
- X chain = latest;
- X }
- X if (chain == NULL) /* Loop ran zero times. */
- X (void) regnode(NOTHING);
- X
- X return(ret);
- X}
- X
- X/*
- X - regpiece - something followed by possible [*+?]
- X *
- X * Note that the branching code sequences used for ? and the general cases
- X * of * and + are somewhat optimized: they use the same NOTHING node as
- X * both the endmarker for their branch list and the body of the last branch.
- X * It might seem that this node could be dispensed with entirely, but the
- X * endmarker role is not redundant.
- X */
- static char *
- regpiece(flagp)
- int *flagp;
- X{
- X register char *ret;
- X register char op;
- X register char *next;
- X int flags;
- X
- X ret = regatom(&flags);
- X if (ret == NULL)
- X return(NULL);
- X
- X op = *regparse;
- X if (!ISMULT(op)) {
- X *flagp = flags;
- X return(ret);
- X }
- X
- X if (!(flags&HASWIDTH) && op != '?')
- X FAIL("*+ operand could be empty");
- X *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
- X
- X if (op == '*' && (flags&SIMPLE))
- X reginsert(STAR, ret);
- X else if (op == '*') {
- X /* Emit x* as (x&|), where & means "self". */
- X reginsert(BRANCH, ret); /* Either x */
- X regoptail(ret, regnode(BACK)); /* and loop */
- X regoptail(ret, ret); /* back */
- X regtail(ret, regnode(BRANCH)); /* or */
- X regtail(ret, regnode(NOTHING)); /* null. */
- X } else if (op == '+' && (flags&SIMPLE))
- X reginsert(PLUS, ret);
- X else if (op == '+') {
- X /* Emit x+ as x(&|), where & means "self". */
- X next = regnode(BRANCH); /* Either */
- X regtail(ret, next);
- X regtail(regnode(BACK), ret); /* loop back */
- X regtail(next, regnode(BRANCH)); /* or */
- X regtail(ret, regnode(NOTHING)); /* null. */
- X } else if (op == '?') {
- X /* Emit x? as (x|) */
- X reginsert(BRANCH, ret); /* Either x */
- X regtail(ret, regnode(BRANCH)); /* or */
- X next = regnode(NOTHING); /* null. */
- X regtail(ret, next);
- X regoptail(ret, next);
- X }
- X regparse++;
- X if (ISMULT(*regparse))
- X FAIL("nested *?+");
- X
- X return(ret);
- X}
- X
- X/*
- X - regatom - the lowest level
- X *
- X * Optimization: gobbles an entire sequence of ordinary characters so that
- X * it can turn them into a single node, which is smaller to store and
- X * faster to run. Backslashed characters are exceptions, each becoming a
- X * separate node; the code is simpler that way and it's not worth fixing.
- X */
- static char *
- regatom(flagp)
- int *flagp;
- X{
- X register char *ret;
- X int flags;
- X
- X *flagp = WORST; /* Tentatively. */
- X
- X switch (*regparse++) {
- X /* FIXME: these chars only have meaning at beg/end of pat? */
- X case '^':
- X ret = regnode(BOL);
- X break;
- X case '$':
- X ret = regnode(EOL);
- X break;
- X case '.':
- X ret = regnode(ANY);
- X *flagp |= HASWIDTH|SIMPLE;
- X break;
- X case '[': {
- X register int class;
- X register int classend;
- X
- X if (*regparse == '^') { /* Complement of range. */
- X ret = regnode(ANYBUT);
- X regparse++;
- X } else
- X ret = regnode(ANYOF);
- X if (*regparse == ']' || *regparse == '-')
- X regc(*regparse++);
- X while (*regparse != '\0' && *regparse != ']') {
- X if (*regparse == '-') {
- X regparse++;
- X if (*regparse == ']' || *regparse == '\0')
- X regc('-');
- X else {
- X class = UCHARAT(regparse-2)+1;
- X classend = UCHARAT(regparse);
- X if (class > classend+1)
- X FAIL("invalid [] range");
- X for (; class <= classend; class++)
- X regc(class);
- X regparse++;
- X }
- X } else
- X regc(*regparse++);
- X }
- X regc('\0');
- X if (*regparse != ']')
- X FAIL("unmatched []");
- X regparse++;
- X *flagp |= HASWIDTH|SIMPLE;
- X }
- X break;
- X case '(':
- X ret = reg(1, &flags);
- X if (ret == NULL)
- X return(NULL);
- X *flagp |= flags&(HASWIDTH|SPSTART);
- X break;
- X case '\0':
- X case '|':
- X case '\n':
- X case ')':
- X FAIL("internal urp"); /* Supposed to be caught earlier. */
- X break;
- X case '?':
- X case '+':
- X case '*':
- X FAIL("?+* follows nothing");
- X break;
- X case '\\':
- X switch (*regparse++) {
- X case '\0':
- X FAIL("trailing \\");
- X break;
- X case '<':
- X ret = regnode(WORDA);
- X break;
- X case '>':
- X ret = regnode(WORDZ);
- X break;
- X /* FIXME: Someday handle \1, \2, ... */
- X default:
- X /* Handle general quoted chars in exact-match routine */
- X goto de_fault;
- X }
- X break;
- X de_fault:
- X default:
- X /*
- X * Encode a string of characters to be matched exactly.
- X *
- X * This is a bit tricky due to quoted chars and due to
- X * '*', '+', and '?' taking the SINGLE char previous
- X * as their operand.
- X *
- X * On entry, the char at regparse[-1] is going to go
- X * into the string, no matter what it is. (It could be
- X * following a \ if we are entered from the '\' case.)
- X *
- X * Basic idea is to pick up a good char in ch and
- X * examine the next char. If it's *+? then we twiddle.
- X * If it's \ then we frozzle. If it's other magic char
- X * we push ch and terminate the string. If none of the
- X * above, we push ch on the string and go around again.
- X *
- X * regprev is used to remember where "the current char"
- X * starts in the string, if due to a *+? we need to back
- X * up and put the current char in a separate, 1-char, string.
- X * When regprev is NULL, ch is the only char in the
- X * string; this is used in *+? handling, and in setting
- X * flags |= SIMPLE at the end.
- X */
- X {
- X char *regprev;
- X register char ch;
- X
- X regparse--; /* Look at cur char */
- X ret = regnode(EXACTLY);
- X for ( regprev = 0 ; ; ) {
- X ch = *regparse++; /* Get current char */
- X switch (*regparse) { /* look at next one */
- X
- X default:
- X regc(ch); /* Add cur to string */
- X break;
- X
- X case '.': case '[': case '(':
- X case ')': case '|': case '\n':
- X case '$': case '^':
- X case '\0':
- X /* FIXME, $ and ^ should not always be magic */
- X magic:
- X regc(ch); /* dump cur char */
- X goto done; /* and we are done */
- X
- X case '?': case '+': case '*':
- X if (!regprev) /* If just ch in str, */
- X goto magic; /* use it */
- X /* End mult-char string one early */
- X regparse = regprev; /* Back up parse */
- X goto done;
- X
- X case '\\':
- X regc(ch); /* Cur char OK */
- X switch (regparse[1]){ /* Look after \ */
- X case '\0':
- X case '<':
- X case '>':
- X /* FIXME: Someday handle \1, \2, ... */
- X goto done; /* Not quoted */
- X default:
- X /* Backup point is \, scan * point is after it. */
- X regprev = regparse;
- X regparse++;
- X continue; /* NOT break; */
- X }
- X }
- X regprev = regparse; /* Set backup point */
- X }
- X done:
- X regc('\0');
- X *flagp |= HASWIDTH;
- X if (!regprev) /* One char? */
- X *flagp |= SIMPLE;
- X }
- X break;
- X }
- X
- X return(ret);
- X}
- X
- X/*
- X - regnode - emit a node
- X */
- static char * /* Location. */
- regnode(op)
- char op;
- X{
- X register char *ret;
- X register char *ptr;
- X
- X ret = regcode;
- X if (ret == ®dummy) {
- X regsize += 3;
- X return(ret);
- X }
- X
- X ptr = ret;
- X *ptr++ = op;
- X *ptr++ = '\0'; /* Null "next" pointer. */
- X *ptr++ = '\0';
- X regcode = ptr;
- X
- X return(ret);
- X}
- X
- X/*
- X - regc - emit (if appropriate) a byte of code
- X */
- static void
- regc(b)
- char b;
- X{
- X if (regcode != ®dummy)
- X *regcode++ = b;
- X else
- X regsize++;
- X}
- X
- X/*
- X - reginsert - insert an operator in front of already-emitted operand
- X *
- X * Means relocating the operand.
- X */
- static void
- reginsert(op, opnd)
- char op;
- char *opnd;
- X{
- X register char *src;
- X register char *dst;
- X register char *place;
- X
- X if (regcode == ®dummy) {
- X regsize += 3;
- X return;
- X }
- X
- X src = regcode;
- X regcode += 3;
- X dst = regcode;
- X while (src > opnd)
- X *--dst = *--src;
- X
- X place = opnd; /* Op node, where operand used to be. */
- X *place++ = op;
- X *place++ = '\0';
- X *place++ = '\0';
- X}
- X
- X/*
- X - regtail - set the next-pointer at the end of a node chain
- X */
- static void
- regtail(p, val)
- char *p;
- char *val;
- X{
- X register char *scan;
- X register char *temp;
- X register int offset;
- X
- X if (p == ®dummy)
- X return;
- X
- X /* Find last node. */
- X scan = p;
- X for (;;) {
- X temp = regnext(scan);
- X if (temp == NULL)
- X break;
- X scan = temp;
- X }
- X
- X if (OP(scan) == BACK)
- X offset = scan - val;
- X else
- X offset = val - scan;
- X *(scan+1) = (offset>>8)&0377;
- X *(scan+2) = offset&0377;
- X}
- X
- X/*
- X - regoptail - regtail on operand of first argument; nop if operandless
- X */
- static void
- regoptail(p, val)
- char *p;
- char *val;
- X{
- X /* "Operandless" and "op != BRANCH" are synonymous in practice. */
- X if (p == NULL || p == ®dummy || OP(p) != BRANCH)
- X return;
- X regtail(OPERAND(p), val);
- X}
- X
- X/*
- X * regexec and friends
- X */
- X
- X/*
- X * Global work variables for regexec().
- X */
- static char *reginput; /* String-input pointer. */
- static char *regbol; /* Beginning of input, for ^ check. */
- static char **regstartp; /* Pointer to startp array. */
- static char **regendp; /* Ditto for endp. */
- X
- X/*
- X * Forwards.
- X */
- STATIC int regtry();
- STATIC int regmatch();
- STATIC int regrepeat();
- X
- X#ifdef DEBUG
- int regnarrate = 0;
- void regdump();
- STATIC char *regprop();
- X#endif
- X
- X/*
- X - regexec - match a regexp against a string
- X */
- int
- regexec(prog, string)
- register regexp *prog;
- register char *string;
- X{
- X register char *s;
- X extern char *strchr();
- X
- X /* Be paranoid... */
- X if (prog == NULL || string == NULL) {
- X regerror("NULL parameter");
- X return(0);
- X }
- X
- X /* Check validity of program. */
- X if (UCHARAT(prog->program) != MAGIC) {
- X regerror("corrupted program");
- X return(0);
- X }
- X
- X /* If there is a "must appear" string, look for it. */
- X if (prog->regmust != NULL) {
- X s = string;
- X while ((s = strchr(s, prog->regmust[0])) != NULL) {
- X if (strncmp(s, prog->regmust, prog->regmlen) == 0)
- X break; /* Found it. */
- X s++;
- X }
- X if (s == NULL) /* Not present. */
- X return(0);
- X }
- X
- X /* Mark beginning of line for ^ . */
- X regbol = string;
- X
- X /* Simplest case: anchored match need be tried only once. */
- X if (prog->reganch)
- X return(regtry(prog, string));
- X
- X /* Messy cases: unanchored match. */
- X s = string;
- X if (prog->regstart != '\0')
- X /* We know what char it must start with. */
- X while ((s = strchr(s, prog->regstart)) != NULL) {
- X if (regtry(prog, s))
- X return(1);
- X s++;
- X }
- X else
- X /* We don't -- general case. */
- X do {
- X if (regtry(prog, s))
- X return(1);
- X } while (*s++ != '\0');
- X
- X /* Failure. */
- X return(0);
- X}
- X
- X/*
- X - regtry - try match at specific point
- X */
- static int /* 0 failure, 1 success */
- regtry(prog, string)
- regexp *prog;
- char *string;
- X{
- X register int i;
- X register char **sp;
- X register char **ep;
- X
- X reginput = string;
- X regstartp = prog->startp;
- X regendp = prog->endp;
- X
- X sp = prog->startp;
- X ep = prog->endp;
- X for (i = NSUBEXP; i > 0; i--) {
- X *sp++ = NULL;
- X *ep++ = NULL;
- X }
- X if (regmatch(prog->program + 1)) {
- X prog->startp[0] = string;
- X prog->endp[0] = reginput;
- X return(1);
- X } else
- X return(0);
- X}
- X
- X/*
- X - regmatch - main matching routine
- X *
- X * Conceptually the strategy is simple: check to see whether the current
- X * node matches, call self recursively to see whether the rest matches,
- X * and then act accordingly. In practice we make some effort to avoid
- X * recursion, in particular by going through "ordinary" nodes (that don't
- X * need to know whether the rest of the match failed) by a loop instead of
- X * by recursion.
- X */
- static int /* 0 failure, 1 success */
- regmatch(prog)
- char *prog;
- X{
- X register char *scan; /* Current node. */
- X char *next; /* Next node. */
- X extern char *strchr();
- X
- X scan = prog;
- X#ifdef DEBUG
- X if (scan != NULL && regnarrate)
- X fprintf(stderr, "%s(\n", regprop(scan));
- X#endif
- X while (scan != NULL) {
- X#ifdef DEBUG
- X if (regnarrate)
- X fprintf(stderr, "%s...\n", regprop(scan));
- X#endif
- X next = regnext(scan);
- X
- X switch (OP(scan)) {
- X case BOL:
- X if (reginput != regbol)
- X return(0);
- X break;
- X case EOL:
- X if (*reginput != '\0')
- X return(0);
- X break;
- X case WORDA:
- X /* Must be looking at a letter, digit, or _ */
- X if ((!isalnum(*reginput)) && *reginput != '_')
- X return(0);
- X /* Prev must be BOL or nonword */
- X if (reginput > regbol &&
- X (isalnum(reginput[-1]) || reginput[-1] == '_'))
- X return(0);
- X break;
- X case WORDZ:
- X /* Must be looking at non letter, digit, or _ */
- X if (isalnum(*reginput) || *reginput == '_')
- X return(0);
- X /* We don't care what the previous char was */
- X break;
- X case ANY:
- X if (*reginput == '\0')
- X return(0);
- X reginput++;
- X break;
- X case EXACTLY: {
- X register int len;
- X register char *opnd;
- X
- X opnd = OPERAND(scan);
- X /* Inline the first character, for speed. */
- X if (*opnd != *reginput)
- X return(0);
- X len = strlen(opnd);
- X if (len > 1 && strncmp(opnd, reginput, len) != 0)
- X return(0);
- X reginput += len;
- X }
- X break;
- X case ANYOF:
- X if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL)
- X return(0);
- X reginput++;
- X break;
- X case ANYBUT:
- X if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL)
- X return(0);
- X reginput++;
- X break;
- X case NOTHING:
- X break;
- X case BACK:
- X break;
- X case OPEN+1:
- X case OPEN+2:
- X case OPEN+3:
- X case OPEN+4:
- X case OPEN+5:
- X case OPEN+6:
- X case OPEN+7:
- X case OPEN+8:
- X case OPEN+9: {
- X register int no;
- X register char *save;
- X
- X no = OP(scan) - OPEN;
- X save = reginput;
- X
- X if (regmatch(next)) {
- X /*
- X * Don't set startp if some later
- X * invocation of the same parentheses
- X * already has.
- X */
- X if (regstartp[no] == NULL)
- X regstartp[no] = save;
- X return(1);
- X } else
- X return(0);
- X }
- X break;
- X case CLOSE+1:
- X case CLOSE+2:
- X case CLOSE+3:
- X case CLOSE+4:
- X case CLOSE+5:
- X case CLOSE+6:
- X case CLOSE+7:
- X case CLOSE+8:
- X case CLOSE+9: {
- X register int no;
- X register char *save;
- X
- X no = OP(scan) - CLOSE;
- X save = reginput;
- X
- X if (regmatch(next)) {
- X /*
- X * Don't set endp if some later
- X * invocation of the same parentheses
- X * already has.
- X */
- X if (regendp[no] == NULL)
- X regendp[no] = save;
- X return(1);
- X } else
- X return(0);
- X }
- X break;
- X case BRANCH: {
- X register char *save;
- X
- X if (OP(next) != BRANCH) /* No choice. */
- X next = OPERAND(scan); /* Avoid recursion. */
- X else {
- X do {
- X save = reginput;
- X if (regmatch(OPERAND(scan)))
- X return(1);
- X reginput = save;
- X scan = regnext(scan);
- X } while (scan != NULL && OP(scan) == BRANCH);
- X return(0);
- X /* NOTREACHED */
- X }
- X }
- X break;
- X case STAR:
- X case PLUS: {
- X register char nextch;
- X register int no;
- X register char *save;
- X register int min;
- X
- X /*
- X * Lookahead to avoid useless match attempts
- X * when we know what character comes next.
- X */
- X nextch = '\0';
- X if (OP(next) == EXACTLY)
- X nextch = *OPERAND(next);
- X min = (OP(scan) == STAR) ? 0 : 1;
- X save = reginput;
- X no = regrepeat(OPERAND(scan));
- X while (no >= min) {
- X /* If it could work, try it. */
- X if (nextch == '\0' || *reginput == nextch)
- X if (regmatch(next))
- X return(1);
- X /* Couldn't or didn't -- back up. */
- X no--;
- X reginput = save + no;
- X }
- X return(0);
- X }
- X break;
- X case END:
- X return(1); /* Success! */
- X break;
- X default:
- X regerror("memory corruption");
- X return(0);
- X break;
- X }
- X
- X scan = next;
- X }
- X
- X /*
- X * We get here only if there's trouble -- normally "case END" is
- X * the terminating point.
- X */
- X regerror("corrupted pointers");
- X return(0);
- X}
- X
- X/*
- X - regrepeat - repeatedly match something simple, report how many
- X */
- static int
- regrepeat(p)
- char *p;
- X{
- X register int count = 0;
- X register char *scan;
- X register char *opnd;
- X
- X scan = reginput;
- X opnd = OPERAND(p);
- X switch (OP(p)) {
- X case ANY:
- X count = strlen(scan);
- X scan += count;
- X break;
- X case EXACTLY:
- X while (*opnd == *scan) {
- X count++;
- X scan++;
- X }
- X break;
- X case ANYOF:
- X while (*scan != '\0' && strchr(opnd, *scan) != NULL) {
- X count++;
- X scan++;
- X }
- X break;
- X case ANYBUT:
- X while (*scan != '\0' && strchr(opnd, *scan) == NULL) {
- X count++;
- X scan++;
- X }
- X break;
- X default: /* Oh dear. Called inappropriately. */
- X regerror("internal foulup");
- X count = 0; /* Best compromise. */
- X break;
- X }
- X reginput = scan;
- X
- X return(count);
- X}
- X
- X/*
- X - regnext - dig the "next" pointer out of a node
- X */
- static char *
- regnext(p)
- register char *p;
- X{
- X register int offset;
- X
- X if (p == ®dummy)
- X return(NULL);
- X
- X offset = NEXT(p);
- X if (offset == 0)
- X return(NULL);
- X
- X if (OP(p) == BACK)
- X return(p-offset);
- X else
- X return(p+offset);
- X}
- X
- X#ifdef DEBUG
- X
- STATIC char *regprop();
- X
- X/*
- X - regdump - dump a regexp onto stdout in vaguely comprehensible form
- X */
- void
- regdump(r)
- regexp *r;
- X{
- X register char *s;
- X register char op = EXACTLY; /* Arbitrary non-END op. */
- X register char *next;
- X extern char *strchr();
- X
- X
- X s = r->program + 1;
- X while (op != END) { /* While that wasn't END last time... */
- X op = OP(s);
- X printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */
- X next = regnext(s);
- X if (next == NULL) /* Next ptr. */
- X printf("(0)");
- X else
- X printf("(%d)", (s-r->program)+(next-s));
- X s += 3;
- X if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
- X /* Literal string, where present. */
- X while (*s != '\0') {
- X putchar(*s);
- X s++;
- X }
- X s++;
- X }
- X putchar('\n');
- X }
- X
- X /* Header fields of interest. */
- X if (r->regstart != '\0')
- X printf("start `%c' ", r->regstart);
- X if (r->reganch)
- X printf("anchored ");
- X if (r->regmust != NULL)
- X printf("must have \"%s\"", r->regmust);
- X printf("\n");
- X}
- X
- X/*
- X - regprop - printable representation of opcode
- X */
- static char *
- regprop(op)
- char *op;
- X{
- X register char *p;
- X static char buf[50];
- X
- X (void) strcpy(buf, ":");
- X
- X switch (OP(op)) {
- X case BOL:
- X p = "BOL";
- X break;
- X case EOL:
- X p = "EOL";
- X break;
- X case ANY:
- X p = "ANY";
- X break;
- X case ANYOF:
- X p = "ANYOF";
- X break;
- X case ANYBUT:
- X p = "ANYBUT";
- X break;
- X case BRANCH:
- X p = "BRANCH";
- X break;
- X case EXACTLY:
- X p = "EXACTLY";
- X break;
- X case NOTHING:
- X p = "NOTHING";
- X break;
- X case BACK:
- X p = "BACK";
- X break;
- X case END:
- X p = "END";
- X break;
- X case OPEN+1:
- X case OPEN+2:
- X case OPEN+3:
- X case OPEN+4:
- X case OPEN+5:
- X case OPEN+6:
- X case OPEN+7:
- X case OPEN+8:
- X case OPEN+9:
- X sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN);
- X p = NULL;
- X break;
- X case CLOSE+1:
- X case CLOSE+2:
- X case CLOSE+3:
- X case CLOSE+4:
- X case CLOSE+5:
- X case CLOSE+6:
- X case CLOSE+7:
- X case CLOSE+8:
- X case CLOSE+9:
- X sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE);
- X p = NULL;
- X break;
- X case STAR:
- X p = "STAR";
- X break;
- X case PLUS:
- X p = "PLUS";
- X break;
- X case WORDA:
- X p = "WORDA";
- X break;
- X case WORDZ:
- X p = "WORDZ";
- X break;
- X default:
- X regerror("corrupted opcode");
- X break;
- X }
- X if (p != NULL)
- X (void) strcat(buf, p);
- X return(buf);
- X}
- X#endif
- X
- X/*
- X * The following is provided for those people who do not have strcspn() in
- X * their C libraries. They should get off their butts and do something
- X * about it; at least one public-domain implementation of those (highly
- X * useful) string routines has been published on Usenet.
- X */
- X#ifdef STRCSPN
- X/*
- X * strcspn - find length of initial segment of s1 consisting entirely
- X * of characters not from s2
- X */
- X
- static int
- strcspn(s1, s2)
- char *s1;
- char *s2;
- X{
- X register char *scan1;
- X register char *scan2;
- X register int count;
- X
- X count = 0;
- X for (scan1 = s1; *scan1 != '\0'; scan1++) {
- X for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */
- X if (*scan1 == *scan2++)
- X return(count);
- X count++;
- X }
- X return(count);
- X}
- X#endif
- END_OF_FILE
- if test 31149 -ne `wc -c <'regexp.c'`; then
- echo shar: \"'regexp.c'\" unpacked with wrong size!
- fi
- # end of 'regexp.c'
- fi
- if test -f 'regexp.h' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'regexp.h'\"
- else
- echo shar: Extracting \"'regexp.h'\" \(574 characters\)
- sed "s/^X//" >'regexp.h' <<'END_OF_FILE'
- X/*
- X * Definitions etc. for regexp(3) routines.
- X *
- X * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof],
- X * not the System V one.
- X */
- X#define NSUBEXP 10
- typedef struct regexp {
- X char *startp[NSUBEXP];
- X char *endp[NSUBEXP];
- X char regstart; /* Internal use only. */
- X char reganch; /* Internal use only. */
- X char *regmust; /* Internal use only. */
- X int regmlen; /* Internal use only. */
- X char program[1]; /* Unwarranted chumminess with compiler. */
- X} regexp;
- X
- extern regexp *regcomp();
- extern int regexec();
- extern void regsub();
- extern void regerror();
- END_OF_FILE
- if test 574 -ne `wc -c <'regexp.h'`; then
- echo shar: \"'regexp.h'\" unpacked with wrong size!
- fi
- # end of 'regexp.h'
- fi
- if test -f 'regmagic.h' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'regmagic.h'\"
- else
- echo shar: Extracting \"'regmagic.h'\" \(153 characters\)
- sed "s/^X//" >'regmagic.h' <<'END_OF_FILE'
- X/*
- X * The first byte of the regexp internal "program" is actually this magic
- X * number; the start node begins in the second byte.
- X */
- X#define MAGIC 0234
- END_OF_FILE
- if test 153 -ne `wc -c <'regmagic.h'`; then
- echo shar: \"'regmagic.h'\" unpacked with wrong size!
- fi
- # end of 'regmagic.h'
- fi
- if test -f 'regsub.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'regsub.c'\"
- else
- echo shar: Extracting \"'regsub.c'\" \(1962 characters\)
- sed "s/^X//" >'regsub.c' <<'END_OF_FILE'
- X/*
- X * regsub
- X *
- X * Copyright (c) 1986 by University of Toronto.
- X * Written by Henry Spencer. Not derived from licensed software.
- X *
- X * Permission is granted to anyone to use this software for any
- X * purpose on any computer system, and to redistribute it freely,
- X * subject to the following restrictions:
- X *
- X * 1. The author is not responsible for the consequences of use of
- X * this software, no matter how awful, even if they arise
- X * from defects in it.
- X *
- X * 2. The origin of this software must not be misrepresented, either
- X * by explicit claim or by omission.
- X *
- X * 3. Altered versions must be plainly marked as such, and must not
- X * be misrepresented as being the original software.
- X */
- X#include <stdio.h>
- X#include <regexp.h>
- X#include "regmagic.h"
- X
- X#ifndef CHARBITS
- X#define UCHARAT(p) ((int)*(unsigned char *)(p))
- X#else
- X#define UCHARAT(p) ((int)*(p)&CHARBITS)
- X#endif
- X
- X/*
- X - regsub - perform substitutions after a regexp match
- X */
- void
- regsub(prog, source, dest)
- regexp *prog;
- char *source;
- char *dest;
- X{
- X register char *src;
- X register char *dst;
- X register char c;
- X register int no;
- X register int len;
- X extern char *strncpy();
- X
- X if (prog == NULL || source == NULL || dest == NULL) {
- X regerror("NULL parm to regsub");
- X return;
- X }
- X if (UCHARAT(prog->program) != MAGIC) {
- X regerror("damaged regexp fed to regsub");
- X return;
- X }
- X
- X src = source;
- X dst = dest;
- X while ((c = *src++) != '\0') {
- X if (c == '&')
- X no = 0;
- X else if (c == '\\' && '0' <= *src && *src <= '9')
- X no = *src++ - '0';
- X else
- X no = -1;
- X if (no < 0) { /* Ordinary character. */
- X if (c == '\\' && (*src == '\\' || *src == '&'))
- X c = *src++;
- X *dst++ = c;
- X } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) {
- X len = prog->endp[no] - prog->startp[no];
- X (void) strncpy(dst, prog->startp[no], len);
- X dst += len;
- X if (len != 0 && *(dst-1) == '\0') { /* strncpy hit NUL. */
- X regerror("damaged match string");
- X return;
- X }
- X }
- X }
- X *dst++ = '\0';
- X}
- END_OF_FILE
- if test 1962 -ne `wc -c <'regsub.c'`; then
- echo shar: \"'regsub.c'\" unpacked with wrong size!
- fi
- # end of 'regsub.c'
- fi
- if test -f 'vfprintf.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'vfprintf.c'\"
- else
- echo shar: Extracting \"'vfprintf.c'\" \(1408 characters\)
- sed "s/^X//" >'vfprintf.c' <<'END_OF_FILE'
- X/*
- X * Copyright (c) 1988 Regents of the University of California.
- X * All rights reserved.
- X *
- X * Redistribution and use in source and binary forms are permitted
- X * provided that the above copyright notice and this paragraph are
- X * duplicated in all such forms and that any documentation,
- X * advertising materials, and other materials related to such
- X * distribution and use acknowledge that the software was developed
- X * by the University of California, Berkeley. The name of the
- X * University may not be used to endorse or promote products derived
- X * from this software without specific prior written permission.
- X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- X */
- X
- X#if defined(LIBC_SCCS) && !defined(lint)
- static char sccsid[] = "@(#)vfprintf.c 5.2 (Berkeley) 6/27/88";
- X#endif /* LIBC_SCCS and not lint */
- X
- X#include <stdio.h>
- X#include <varargs.h>
- X
- int
- vfprintf(iop, fmt, ap)
- X FILE *iop;
- X char *fmt;
- X va_list ap;
- X{
- X int len;
- X char localbuf[BUFSIZ];
- X
- X if (iop->_flag & _IONBF) {
- X iop->_flag &= ~_IONBF;
- X iop->_ptr = iop->_base = localbuf;
- X len = _doprnt(fmt, ap, iop);
- X (void) fflush(iop);
- X iop->_flag |= _IONBF;
- X iop->_base = NULL;
- X iop->_bufsiz = 0;
- X iop->_cnt = 0;
- X } else
- X len = _doprnt(fmt, ap, iop);
- X
- X return (ferror(iop) ? EOF : len);
- X}
- END_OF_FILE
- if test 1408 -ne `wc -c <'vfprintf.c'`; then
- echo shar: \"'vfprintf.c'\" unpacked with wrong size!
- fi
- # end of 'vfprintf.c'
- fi
- echo shar: End of archive 1 \(of 1\).
- cp /dev/null ark1isdone
- MISSING=""
- for I in 1 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have the archive.
- rm -f ark[1-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
-