[comp.sources.unix] v20i049: Operator interface with limited privileges

rsalz@uunet.uu.net (Rich Salz) (10/24/89)

Submitted-by: Carel Braam <mcvax!eutrc3!rccarel>
Posting-number: Volume 20, Issue 49
Archive-name: opcom

Opcom is a program, that enables users, belonging to a special group to
execute a limited set of commands with another userid or groupid.
This makes it possible to perform daily system administration tasks without
having the full privileges of root.

Carel Braam

uucp:	rccarel@eutrc3.UUCP	| Computing Centre
bitnet: rccarel@heithe5.BITNET	| Eindhoven University of Technology
phone: 040-472158		| Den Dolech 2, P.O. Box 513
home:  040-810381		| 5600 MB Eindhoven, The Netherlands

---

#! /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:  README bsd.make config detab.c opcom.c srctoman sysdep
#   sysv.make
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f README -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"README\"
else
echo shar: Extracting \"README\" \(1537 characters\)
sed "s/^X//" >README <<'END_OF_README'
XOpcom is a program, that enables users, belonging to a special group to
Xexecute a limited set of commands with another userid or groupid.
XThis makes it possible to perform daily system administration tasks without
Xhaving the full privileges of root.
X
X
XCarel Braam
X
Xuucp:	rccarel@eutrc3.UUCP	| Computing Centre
Xbitnet: rccarel@heithe5.BITNET	| Eindhoven University of Technology
Xphone: 040-472158		| Den Dolech 2, P.O. Box 513
Xhome:  040-810381		| 5600 MB Eindhoven, The Netherlands
X
X
X			    INSTALLATION NOTES
X			    ==================
X
XBefore you start off adapt the file config to your local needs.
XAll system dependent variables are set between the comment
X
X#	*** System dependent variables ***
X
Xand  the comment
X
X#	*** End system dependent variables ***
X
XCurrently the following variables can be set:
X
XOPCOMDIR	The directory for the files COMMANDS and PROFILE.
XCOMMANDS	The file containing the commands, that are allowed.
XPROFILE		The file containing the profile to be executed.
X
XAs well the manual page opcom.8 as sysdep.h are dependent on config.
XTo make opcom use the makefile bsd.make, if you have a bsd system, or
Xsysv.make, if you have system V.
XYou can create the manual page by entering make man.
XThen as super user type make install to install opcom and make installman
Xto install the manual page.
XSee the makefile for the definition of the local bin directory and the local
Xmanual directory.
X
X#++
X# CREATION DATE
X#	Thu Jan  5 11:25:04 MET 1989
X# LAST MODIFICATION
X#	Tue Jan 17 13:15:36 MET 1989
X# VERSION/RELEASE
X#	1.3
X#--
END_OF_README
if test 1537 -ne `wc -c <README`; then
    echo shar: \"README\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f bsd.make -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"bsd.make\"
else
echo shar: Extracting \"bsd.make\" \(1915 characters\)
sed "s/^X//" >bsd.make <<'END_OF_bsd.make'
X#++
X# NAME
X#	bsd.make 
X# SUMMARY
X#	makefile for opcom.
X# AUTHOR(S)
X#
X#      C.G.S.M. Braam
X#      Eindhoven University of Technology
X#      Computing Centre
X#      Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
X# CREATION DATE
X#	Thu Jan  5 10:41:57 MET 1989
X# LAST MODIFICATION
X#	Tue Jan 17 13:15:23 MET 1989
X# VERSION/RELEASE
X#	1.3
X#--
X
X# System dependent variables
X
XCC = cc
XCDEFS = -DBSD
XCFLAGS = -O
XLFLAGS = -h
XBINDIR = /usr/local/bin
XMANDIR = /usr/local/man
X
X# End system dependent variables
X
XCSOURCES = opcom.c
XHFILES = sysdep
XSHELLS = config
XMAKEFILES = bsd.make sysv.make
XOPSOURCES = $(CSOURCES) $(HFILES) $(SHELLS) $(MAKEFILES) README
XUSOURCES = detab.c srctoman
XSOURCES = $(OPSOURCES) $(USOURCES)
X
Xall:	opcom
X
Xsysdep.h: sysdep config
X	sh config sysdep >sysdep.h
X
Xman:	opcom.8
X
Xopcom.8: opcom.c config detab
X	sh srctoman -L opcom.c | sh config >opcom.8
X
X# Lint.
X
Xlint:	sysdep.h
X	lint $(LFLAGS) $(CDEFS) opcom.c
X
X
Xdetab: detab.c
X	$(CC) $(CFLAGS) $(CDEFS) detab.c -o detab
X
Xopcom: opcom.c sysdep.h
X	$(CC) $(CFLAGS) $(CDEFS) opcom.c -o opcom
X
X# Installation and update.
X
Xinstall: opcom
X	cp opcom $(BINDIR)
X	cd $(BINDIR); chown root opcom; chmod u+s opcom
X
Xinstallman: man
X	 cp opcom.8 $(MANDIR)/man8
X	 rm -f $(MANDIR)/cat8/opcom.8
X
Xarchive: $(OPSOURCES)
X	archive $(OPSOURCES)
X
Xrelease: $(OPSOURCES)
X	modsource +1 $(CSOURCES)
X	modsource +2 $(CSOURCES)
X	modsource +1 -c $(HFILES)
X	modsource +2 -c $(HFILES)
X	modsource +1 -mk $(MAKEFILES) README
X	modsource +2 -mk $(MAKEFILES) README
X	modsource +1 - $(SHELLS)
X	modsource +2 - $(SHELLS)
X	archive $(OPSOURCES)
X
Xversion: $(OPSOURCES)
X	modsource -1 $(CSOURCES)
X	modsource -1 -c $(HFILES)
X	modsource -1 -mk $(MAKEFILES) README
X	modsource -1 - $(SHELLS)
X	archive $(OPSOURCES)
X
Xshar:	$(SOURCES)
X	rm -f Part?? Part??.Z
X	makekit $(SUBDIRS) $(SOURCES) $(DIRENT)
X
Xclean:
X	-rm -f *.BAK *.o opcom detab opcom.8 tmp.* sysdep.h \
X	nohup.out core Part?? Part??.Z
END_OF_bsd.make
if test 1915 -ne `wc -c <bsd.make`; then
    echo shar: \"bsd.make\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f config -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"config\"
else
echo shar: Extracting \"config\" \(789 characters\)
sed "s/^X//" >config <<'END_OF_config'
X: use /bin/sh
X
X# opcom configuration script.
X#
X# In the first part of this script the directories and the administration
X# for opcom are defined.
X# Refer to all of these only by the environment variables defined here.
X
X#++
X# AUTHOR(S)
X#
X#      C.G.S.M. Braam
X#      Eindhoven University of Technology
X#      Computing Centre
X#      Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
X# CREATION DATE
X#	Thu Jan  5 11:09:45 MET 1989
X# LAST MODIFICATION
X#	Tue Jan 17 13:15:48 MET 1989
X# VERSION/RELEASE
X#	1.3
X#--
X
X#	*** System dependent variables ***
X
XOPCOMDIR="/usr/local/lib/opcom"	
XCOMMANDS="$OPCOMDIR/commands"		# command file
XPROFILE="$OPCOMDIR/profile"		# profile
X
X#	*** End system dependent variables ***
X
Xsed 's%XCOMMANDS%'"$COMMANDS"'%g
X     s%XPROFILE%'"$PROFILE"'%g' $*
END_OF_config
if test 789 -ne `wc -c <config`; then
    echo shar: \"config\" unpacked with wrong size!
fi
chmod +x config
# end of overwriting check
fi
if test -f detab.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"detab.c\"
else
echo shar: Extracting \"detab.c\" \(1053 characters\)
sed "s/^X//" >detab.c <<'END_OF_detab.c'
X/*++
X/* NAME
X/*	detab 1
X/* SUMMARY
X/*	expand tabs to blanks
X/* PROJECT
X/*	documentation
X/* SYNOPSIS
X/*	detab
X/* DESCRIPTION
X/*	Detab is a filter that expands tab stops in its standard input
X/*	to blanks. A tab stop distance of eight blanks is assumed.
X/* BUGS
X/*	This program does not handle backspaces.
X/* AUTHOR(S)
X/*	Wietse Venema
X/*	Eindhoven University of Technology
X/*	Department of Mathematics and Computer Science
X/*	Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
X/* CREATION DATE
X/*	Sep 14 1985
X/* LAST MODIFICATION
X/*	Mon May  4 20:32:48 GMT+1:00 1987
X/* VERSION/RELEASE
X/*	1.3
X/*--*/
X
X#include <stdio.h>
X
X#define BLANK	' '
X
Xmain()
X{
X    register int c;		/* character buffer */
X    register int ccount = 0;	/* nr of characters printed on current line */
X
X    while ((c = getchar()) != EOF) {
X	switch (c) {
X
X	    case '\r':
X	    case '\n':  putchar(c);
X			ccount = 0;
X			break;
X
X	    case '\t':  do { putchar(BLANK); } while (++ccount & 7);
X			break;
X
X	    default:    putchar(c);
X			ccount++;
X			break;
X	}
X    }
X    exit(0);
X}
END_OF_detab.c
if test 1053 -ne `wc -c <detab.c`; then
    echo shar: \"detab.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f opcom.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"opcom.c\"
else
echo shar: Extracting \"opcom.c\" \(9468 characters\)
sed "s/^X//" >opcom.c <<'END_OF_opcom.c'
X/*++
X/* NAME
X/*      opcom 8
X/* SUMMARY
X/*      execute an operator command.
X/* SYNOPSIS
X/*	.fi
X/*	.B opcom 
X/*	 command [ arguments ]
X/* DESCRIPTION
X/*	.I Opcom
X/*	enables users belonging to a special group (as defined in /etc/group)
X/*	to execute a limited set of commands with another userid (e.g. root)
X/*	or groupid. The file
X/*	.I XCOMMANDS
X/*	describes which commands are allowed for which groups and which
X/*	userid (groupid) must be used.
X/*	.br
X/*	.I Command
X/*	is a valid operator command, if it matches an entry in
X/*	.I XCOMMANDS.
X/*	Those entries have the following form:
X/*	
X/*	path-name : operator-group : [ new-userid ] [ : [ new-groupid ]]
X/*	
X/*	.I Command
X/*	matches an entry, if it is the basename of 
X/*	.I path-name
X/*	and if the user executing
X/*	.I opcom
X/*	is a member of the group
X/*	.I operator-group
X/*	(as defined in /etc/group).
X/*	If
X/*	.I command
X/*	matches more than one entry, the first matching entry is selected.
X/*	This allows multiple levels of privilege.
X/*	.br
X/*	If no match is found,
X/*	.I opcom
X/*	aborts with the message "access denied.".
X/*
X/*	.I Command
X/*	is executed with the given
X/*	.I arguments.
X/*	The environment is the current environment with the following
X/*	exceptions:
X/*	.br
X/*	If
X/*	.I user-id (group-id)
X/*	is not empty, the effective and real userid (groupid) are set to
X/*	.I user-id (group-id).
X/*	.br
X/*	If
X/*	.I user-id (group-id)
X/*	is empty, the effective and real userid (groupid)
X/*	are set to the real userid (groupid).
X/*
X/*	The following environment variables are set:
X/*	.IP \- 2.4
X/*	COMMAND to
X/*	.I command.
X/*	.IP \-
X/*	GROUP to
X/*	.I operator-group.
X/*	.IP \-
X/*	IFS to blank, tab and new line.
X/*	.IP \-
X/*	ORGUSER to the login name of the real userid of the invoker of
X/*	.I opcom.
X/*	.IP \-
X/*	PATH to /
X/*	.IP \-
X/*	USER and LOGNAME to the login name of the real userid executing
X/*	.I command.
X/*	.RE
X/*
X/*	If the script 
X/*	.I XPROFILE
X/*	exists, this script will be executed
X/*	within the shell, that executes
X/*	.I command.
X/*	So changes to the environment (e.g. PATH) can be put there.
X/* FILES
X/*	XCOMMANDS
X/*	XPROFILE
X/*	/etc/group
X/* CAVEAT
X/*	Beware of Trojan horses: don't allow commands with shell escapes.
X/* BUGS
X/*	The syntax of entries in
X/*	.I XCOMMANDS
X/*	is not checked rigorously.
X/* DIAGNOSTICS
X/*	In case of error
X/*	.I opcom
X/*	prints an error message on standard error and terminates
X/*	with nonzero status.
X/* AUTHOR(S)
X/*
X/*      C.G.S.M. Braam
X/*      Eindhoven University of Technology
X/*      Computing Centre
X/*      Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
X/* BUG REPORTS TO
X/*	rccarel@eutrc3.UUCP
X/* CREATION DATE
X/*	Wed Jan  4 14:12:59 MET 1989
X/* LAST MODIFICATION
X/*	Tue Jan 17 13:15:08 MET 1989
X/* VERSION/RELEASE
X/*	1.3
X/*--*/
X
X#include <stdio.h>
X#include "sysdep.h"
X#include <grp.h>
X#include <pwd.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <varargs.h>
X#include <ctype.h>
X
Xextern void exit ();
Xextern void perror ();
X
Xextern int errno;
Xextern char *sys_errlist [];
Xextern int sys_nerr;
X
Xextern int geteuid ();
Xextern int getgid ();
Xextern int setuid ();
Xextern struct group *getgrnam ();
Xextern struct passwd *getpwnam ();
Xextern struct passwd *getpwuid ();
Xextern char *malloc ();
Xextern int putenv ();
Xextern int system ();
X
X#define WARN	0
X#define ABORT	1
X
Xstruct command {
X    char *name;
X    char path [1024];
X    char group [32];
X    char new_group [32];
X    char new_user [32];
X};
X
Xvoid PERROR ();
Xint check_group ();
Xvoid error ();
Xvoid exec_cmnd ();
Xstruct command *get_command ();
Xchar *nextitem ();
Xvoid usage_error ();
Xvoid set_env ();
Xvoid set_ids ();
X
Xchar orguser [32];
Xchar *progname;				/* used for diagnostics */
X
Xmain (argc, argv)
Xint argc;
Xchar **argv;
X{
X    int fd;
X    struct command *command;
X
X    /* close all file descriptors > 2 */
X    for (fd = 3; fd <= _NFILE; fd++)
X	(void) close (fd);
X    if (progname = strrchr (*argv, '/'))
X	progname++;
X    else
X	progname = *argv;
X    argv++; argc--;
X
X    if (argc == 0)
X	usage_error ();
X
X    command = get_command (*argv);
X    set_ids (command->new_user, command->new_group);
X    exec_cmnd (command, argc-1, argv+1);
X    return (0);	/* makes lint happy	*/
X}
X
Xstruct command *get_command (name)
Xchar *name;
X{
X    static char cmndbuf [BUFSIZ];
X    static struct command command;
X    int found = 0;
X    char ch,
X	 *cmnd,
X	 *pc,
X	 *ptail,
X	 *pt;
X    FILE *file;
X
X    if ((file = fopen (COMMANDS, "r")) == NULL)
X	error (ABORT, "cannot open %s for reading.", COMMANDS);
X    command.name = name;
X    while (fgets (cmndbuf, BUFSIZ, file) != NULL) {
X	for (cmnd = cmndbuf; isspace (*cmnd); cmnd++);
X	pt = command.path;
X	ptail = cmnd;
X	while (! (isspace (ch = *ptail) || (ch == ':') || (ch == 0))) {
X	    *pt++ = ch;
X	    ptail++;
X	}
X	*pt = 0;
X	while (isspace (*ptail))
X	    ptail++;
X	if (*ptail != ':')	/* invalid entry	*/
X	    continue;
X	pt = command.path;
X	if ((pc = strrchr (pt, '/')) == NULL)
X	    pc = pt;
X	else
X	    pc++;
X	if (strcmp (name, pc) == 0) {
X	    ptail = nextitem (ptail+1, command.group);
X	    if ((found = check_group (command.group)))
X		break;
X	}
X    }
X    if (! found)
X	error (ABORT, "access denied.");
X    (void) fclose (file);
X    ptail = nextitem (ptail, command.new_user);
X    ptail = nextitem (ptail, command.new_group);
X    return (&command);
X}
X
Xchar *nextitem (pstart, target)
Xchar *pstart,
X     *target;
X{
X    char ch,
X	 *ps = pstart,
X	 *pt = target;
X
X	 while (isspace (*ps)) ps++;
X	 if (*ps == 0) {
X	    *pt = 0;
X	    return (ps);
X	}
X	for (ch = *ps; (ch != ':') & (ch != 0); ch = *++ps)
X	    *pt++ = ch;
X	*pt-- = 0;
X	for (ch = *pt; isspace (ch); ch = *pt--)
X	    *pt = 0;
X	if (*ps == 0)
X	    return (ps);
X	else
X	    return (ps+1);
X}
X
Xint check_group (groupname)
Xchar *groupname;
X{
X    int found = 0;
X    char **gr_list;
X    struct group *group;
X    struct passwd *passwd;
X
X    if ((passwd = getpwuid (getuid ())) == NULL)
X	error (ABORT, "cannot find passwd entry for userid %d.", getuid ());
X    (void) strcpy (orguser, passwd->pw_name);
X    if ((group = getgrnam (groupname)) != NULL)
X	for (gr_list = group->gr_mem; *gr_list != NULL; gr_list++) {
X	    if ((found = (strcmp (orguser, *gr_list) == 0)))
X		break;
X    }
X    return (found);
X}
X
Xvoid set_ids (new_user, new_group)
Xchar *new_user,
X     *new_group;
X{
X    struct group *group;
X    struct passwd *passwd;
X
X    if (*new_group != 0) {
X	/* not empty, must be set before uid is set 	*/
X	if ((group = getgrnam (new_group)) == NULL)
X	    error (ABORT, "cannot find group entry for groupid %s.", new_group);
X	if (setgid (group->gr_gid) < 0)
X	    PERROR (ABORT);
X    }
X    if (*new_user != 0) {	/* not empty 	*/
X	if ((passwd = getpwnam (new_user)) == NULL)
X	    error (ABORT, "cannot find passwd entry for userid %s.", new_user);
X	if (setuid (passwd->pw_uid) < 0)
X	    PERROR (ABORT);
X    } else if (setuid (getuid ()) < 0)
X	PERROR (ABORT);
X}
X
Xvoid exec_cmnd (command, argc, argv)
Xstruct command *command;
Xint argc;
Xchar **argv;
X{
X    unsigned cmnd_size = 0;
X    int i,
X	rslt;
X    struct stat prstat;
X    char 
X	 *cmnd,
X	 *pa,
X	 *pc;
X
X    set_env (command->name, command->group, command->new_user);
X    cmnd_size = strlen (command->path)+32;
X    for (i = 0; i < argc; i++)
X	cmnd_size += strlen (argv [i])+1;
X    pc = cmnd = malloc (cmnd_size+strlen (PROFILE));
X    if ((stat (PROFILE, &prstat) == 0) && (prstat.st_mode & 0400)) {
X	/* We must execute the profile */
X	(void) sprintf (pc, ". %s;", PROFILE);
X	pc += strlen (pc);
X    }
X    pa = command->path;
X    while (*pa != 0)
X	*pc++ = *pa++;
X    *pc++ = ' ';
X    while (argc-- > 0) {
X	pa = *argv++;
X	while (*pa != 0)
X	    *pc++ = *pa++;
X	*pc++ = ' ';
X    }
X    *pc = 0;	/* close string	*/
X
X    if ((rslt = system (cmnd)) < 0)
X	PERROR (ABORT);
X    exit (rslt);
X}
X
Xvoid set_env (name, group, new_user)
Xchar *name,
X     *group,
X     *new_user;
X{
X#define PUTENV(e)	{ if (putenv (e) < 0) PERROR (ABORT); }
X    static char envbuf [512];
X    char *penv = envbuf;
X
X    PUTENV ("IFS= \t\n");
X    PUTENV ("PATH=/");
X    (void) sprintf (penv, "ORGUSER=%s", orguser);
X    PUTENV (penv);
X    penv += strlen (penv)+1;
X    (void) sprintf (penv, "COMMAND=%s", name);
X    PUTENV (penv);
X    penv += strlen (penv)+1;
X    (void) sprintf (penv, "GROUP=%s", group);
X    PUTENV (penv);
X    penv += strlen (penv)+1;
X    if (*new_user == 0) {
X	(void) sprintf (penv, "USER=%s", orguser);
X	PUTENV (penv);
X	penv += strlen (penv)+1;
X	(void) sprintf (penv, "LOGNAME=%s", orguser);
X	PUTENV (penv);
X    } else {
X	(void) sprintf (penv, "USER=%s", new_user);
X	PUTENV (penv);
X	penv += strlen (penv)+1;
X	(void) sprintf (penv, "LOGNAME=%s", new_user);
X	PUTENV (penv);
X    }
X}
X
Xvoid PERROR (why)
Xint why;
X{
X    perror (progname);
X    if (why == ABORT)
X	exit (1);
X}
X
Xvoid usage_error ()
X{
X    error (ABORT, "usage: %s command [ arguments ]", progname);
X}
X
X/* error - barf and quit */
X
X/* VARARGS1 */
X
Xvoid error (why, fmt, va_alist)
Xint why;
Xregister char *fmt;
Xva_dcl
X{
X    va_list s;
X
X    va_start(s);
X
X    (void) fprintf (stderr, "%s: ", progname);
X    for (/* void */; *fmt; fmt++) {
X	if (*fmt != '%') {
X	    (void) putc(*fmt,stderr);
X	} else if (*++fmt == 's') {
X	    (void) fputs(va_arg(s,char *),stderr);
X	} else if (*fmt == 'c') {
X	    (void) putc(va_arg(s,int),stderr);
X	} else if (*fmt == 'u') {
X	    (void) fprintf(stderr,"%u",va_arg(s,unsigned));
X	} else if (*fmt == 'd') {
X	    (void) fprintf(stderr,"%d",va_arg(s,int));
X	}
X    }
X    va_end(s);
X    (void) fprintf (stderr, "\n");
X    if (why != WARN)
X	exit (why);
X    (void) fflush (stderr);
X}
END_OF_opcom.c
if test 9468 -ne `wc -c <opcom.c`; then
    echo shar: \"opcom.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f srctoman -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"srctoman\"
else
echo shar: Extracting \"srctoman\" \(4670 characters\)
sed "s/^X//" >srctoman <<'END_OF_srctoman'
X: srctoman - see comment below
X
X: process arguments
X
XLOCAL=
XHOST=
Xwhile :
Xdo
X    case $1 in
X    -L) HOST=`uuname -l | tr "[a-z]" "[A-Z]"`
X	LOCAL=LOCAL;;
X [0-9]) SECT=$1;;
X     -) LANG=$1; B='[#:]';;
X  -awk) LANG=$1; B='#';;
X    -c) LANG=$1; B='\/\*';;
X    -f) LANG=$1; B='[Cc]';;
X   -mk) LANG=$1; B='#';;
X -n|-t) LANG=$1; B='\\"';;
X    -p) LANG=$1; B='{';;
X    -r) LANG=$1; B='#';;
X    -C) LANG=$1; B=$2; shift;;
X    -*) ERROR="unknown option: $1"; break;;
X    "") ERROR="missing file argument"; break;;
X     *) break;;
X    esac
X    shift
Xdone
X
X: check error status
X
Xcase $ERROR in
X"") ;;
X *) echo "$0: $ERROR" 1>&2
X    echo "usage: $0 [-|-awk|-c|-f|-mk|-n|-p|-t|-r] [section] file(s)" 1>&2; exit 1;;
Xesac
X
X: set up for file suffix processing
X
Xcase $LANG in
X"") sh='[:#]';	r='#';	rh=$r;	awk='#'; mk='#';
X    c='\/\*';	h=$c;	y=$c;	l=$c;
X    f='[Cc]';	fh=$f;	p='{';	ph=$p;
X    ms='\\"';	nr=$ms;	mn=$ms;	man=$ms;
Xesac
X
X: extract comments
X
Xfor i in $*
Xdo
X    case $LANG in
X    "") eval B\="\$`expr $i : '^.*\.\([^.]*\)$'`"
X	test "$B" || { echo "$0: unknown suffix: $i; assuming c" 1>&2; B=$c; }
X    esac
X    sed '
X    /^'"$B"'++/,/^'"$B"'--/!d
X    /^'"$B"'++/d
X    /^'"$B"'--/d
X    s/[ 	]*$//
X    /^'"$B"' \([A-Z]\)/{
X	s//\1/
X	/^NAME/{
X	    N
X	    s/^.*\n'"$B"'[ 	]*//
X	    h
X	    y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
X	    s/,.*\([ 	][0-9][ 	]*\)$/ \1/
X	    s/^.*$/.TH & '"$SECT $LOCAL $HOST"'\
X.ad\
X.fi\
X.SH NAME/
X	    p
X	    g
X	    s/[ 	][0-9][ 	]*$//
X	    a\
X\\-
X	    p
X	    d
X	}
X	/^SUMMARY/d
X	/^DESCRIPTION/s//.SH &\
X.ad\
X.fi/
X	/^BUGS/s//.SH &\
X.ad\
X.fi/
X	/^DIAGNOSTICS/s//.SH &\
X.ad\
X.fi/
X	/^[A-Z][A-Z][A-Z][^a-z]*$/s//.SH &\
X.na\
X.nf/
X	p
X	d
X    }
X    s/^'"$B"'[ 	]*//
X    s/^[ 	]*$//
X' $i
Xdone | ./detab
X
Xexit
X
X:++
X: NAME
X:	srctoman 1
X: SUMMARY
X:	extract manual page from source file comment
X: PACKAGE
X:	source file maintentance tools
X: SYNOPSIS
X:	srctoman [-|-awk|-c|-f|-mk|-m|-n|-p|-t|-r] [section] file(s)
X: DESCRIPTION
X:	Sourcetoman converts comments in various programming languages to
X:	UNIX-style manual pages.
X:	The command processes comments in the style of newsource(1);
X:	its standard output is suitable for formatting with nroff(1) or 
X:	troff(1) using the "-man" macro package.  
X:	Typically, srctoman is integrated with make(1) scripts.
X:	If the option -L is given the text <hostname> (LOCAL) will be in
X:	the heading.
X:
X:	Source files are processed in the indicated order; if no
X:	files argument the command produces no output.
X:
X:	The source file language can be specified through a command-line
X:	option, or can be implied by the filename suffix.
X:	The expected start-of-comment symbol is shown in the last column.
X:
X: .nf
X	option	language	comment
X
X	-	shell		[:#]
X	-awk	awk		#
X	-c	c		/*
X	-f	fortran		[Cc]
X	-mk	make		#
X	-n	nroff		\\"
X	-p	pascal		{
X	-t	troff		\\"
X	-r	ratfor		#
X	-C	any language	next argument
X: .fi
X:
X: .nf
X	suffix	language	comment
X
X	.awk	awk		#
X	.c	c		/*
X	.f	fortran		[Cc]
X	.fh	fortran		[Cc]
X	.h	c		/*
X	.l	lex		/*
X	.man	nroff,troff	\\"
X	.mk	make		#
X	.me	nroff,troff	\\"
X	.ms	nroff,troff	\\"
X	.nr	nroff,troff	\\"
X	.p	pascal		{
X	.ph	pascal		{
X	.r	ratfor		#
X	.rh	ratfor		#
X	.sh	shell		[:#]
X	.y	yacc		/*
X: .fi
X:
X:	The required format of comments is discussed below, where SOC
X:	stands for the start-of-comment symbol of the language being used.
X:
X:	1) Start of manual: SOC, followed by `++'.
X:
X:	2) Section heading: SOC, blank, section name in upper case.
X:
X:	3) New paragraph: empty line or line with SOC only.
X:
X:	4) All other text: SOC and subsequent blanks or tabs are removed.
X:	Lines that do not start with SOC are left unchanged (useful for 
X:	inclusion of program text).
X:
X:	5) End of manual: SOC, followed by `--'.
X:	An end-of-comment may follow if the source file language requires this.
X:
X:	The following manual sections receive a special treatment:
X:	NAME and SUMMARY should appear at the beginning and in
X:	this order; DESCRIPTION, DIAGNOSTICS and BUGS will be
X:	right-margin adjusted.
X:	Other sections may be added freely without confusing srctoman.
X: COMMANDS
X:	sh(1), sed(1), detab(1)
X: SEE ALSO
X:	newsource(1), modsource(1), xman(1)
X:	The earlier commands new(1), mod(1), mkman(1) and dssman(1)
X:	by Ruud Zwart and Ben Noordzij (Erasmus University, Rotterdam) 
X: DIAGNOSTICS
X:	The program complaints if an unknown language is specified
X:	of if the language cannot be deduced from the file suffix.
X: AUTHOR(S)
X:	W.Z. Venema
X:	Eindhoven University of Technology
X:	Department of Mathematics and Computer Science
X:	Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
X: CREATION DATE
X:	Fri Jan 17 22:59:27 MET 1986
X: LAST MODIFICATION
X:	Tue Mar 22 07:24:47 PST 1988
X: VERSION/RELEASE
X:	1.20
X:--
X
X
END_OF_srctoman
if test 4670 -ne `wc -c <srctoman`; then
    echo shar: \"srctoman\" unpacked with wrong size!
fi
chmod +x srctoman
# end of overwriting check
fi
if test -f sysdep -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"sysdep\"
else
echo shar: Extracting \"sysdep\" \(272 characters\)
sed "s/^X//" >sysdep <<'END_OF_sysdep'
X#ifndef _NFILE
X#include <sys/param.h>
X#define _NFILE	NOFILE
X#endif
X
X#ifdef SYSV
X#define SIII
X#include <string.h>
X#else
X#include <strings.h>
X#define strchr	index
X#define strrchr	rindex
Xextern char *ctime ();
X#endif
X
X#define COMMANDS	"XCOMMANDS"
X#define PROFILE		"XPROFILE"
END_OF_sysdep
if test 272 -ne `wc -c <sysdep`; then
    echo shar: \"sysdep\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f sysv.make -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"sysv.make\"
else
echo shar: Extracting \"sysv.make\" \(1921 characters\)
sed "s/^X//" >sysv.make <<'END_OF_sysv.make'
X#++
X# NAME
X#	sysv.make 
X# SUMMARY
X#	makefile for opcom.
X# AUTHOR(S)
X#
X#      C.G.S.M. Braam
X#      Eindhoven University of Technology
X#      Computing Centre
X#      Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
X# CREATION DATE
X#	Thu Jan  5 10:41:57 MET 1989
X# LAST MODIFICATION
X#	Tue Jan 17 13:15:28 MET 1989
X# VERSION/RELEASE
X#	1.3
X#--
X
X# System dependent variables
X
XCC = cc
XCDEFS = -DSYSV
XCFLAGS = -O
XLFLAGS = -h -Ml
XBINDIR = /usr/local/bin
XMANDIR = /usr/local/man
X
X# End system dependent variables
X
XCSOURCES = opcom.c
XHFILES = sysdep
XSHELLS = config
XMAKEFILES = bsd.make sysv.make
XOPSOURCES = $(CSOURCES) $(HFILES) $(SHELLS) $(MAKEFILES) README
XUSOURCES = detab.c srctoman
XSOURCES = $(OPSOURCES) $(USOURCES)
X
Xall:	opcom
X
Xsysdep.h: sysdep config
X	sh config sysdep >sysdep.h
X
Xman:	opcom.8
X
Xopcom.8: opcom.c config detab
X	sh srctoman -L opcom.c | sh config >opcom.8
X
X# Lint.
X
Xlint:	sysdep.h
X	lint $(LFLAGS) $(CDEFS) opcom.c
X
X
Xdetab: detab.c
X	$(CC) $(CFLAGS) $(CDEFS) detab.c -o detab
X
Xopcom: opcom.c sysdep.h
X	$(CC) $(CFLAGS) $(CDEFS) opcom.c -o opcom
X
X# Installation and update.
X
Xinstall: opcom
X	cp opcom $(BINDIR)
X	cd $(BINDIR); chown root opcom; chmod u+s opcom
X
Xinstallman: man
X	 cp opcom.8 $(MANDIR)/man8
X	 rm -f $(MANDIR)/cat8/opcom.8
X
Xarchive: $(OPSOURCES)
X	archive $(OPSOURCES)
X
Xrelease: $(OPSOURCES)
X	modsource +1 $(CSOURCES)
X	modsource +2 $(CSOURCES)
X	modsource +1 -c $(HFILES)
X	modsource +2 -c $(HFILES)
X	modsource +1 -mk $(MAKEFILES) README
X	modsource +2 -mk $(MAKEFILES) README
X	modsource +1 - $(SHELLS)
X	modsource +2 - $(SHELLS)
X	archive $(OPSOURCES)
X
Xversion: $(OPSOURCES)
X	modsource -1 $(CSOURCES)
X	modsource -1 -c $(HFILES)
X	modsource -1 -mk $(MAKEFILES) README
X	modsource -1 - $(SHELLS)
X	archive $(OPSOURCES)
X
Xshar:	$(SOURCES)
X	rm -f Part?? Part??.Z
X	makekit $(SUBDIRS) $(SOURCES) $(DIRENT)
X
Xclean:
X	-rm -f *.BAK *.o opcom detab opcom.8 tmp.* sysdep.h \
X	nohup.out core Part?? Part??.Z
END_OF_sysv.make
if test 1921 -ne `wc -c <sysv.make`; then
    echo shar: \"sysv.make\" unpacked with wrong size!
fi
# end of overwriting check
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 unpacked all 1 archives.
    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

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.