xref: /original-bsd/usr.bin/sccs/sccs.c (revision 0faf63f9)
1adf8f7d4Seric # include <stdio.h>
2adf8f7d4Seric # include <sys/types.h>
3adf8f7d4Seric # include <sys/stat.h>
4b2538d76Seric # include <sys/dir.h>
5adf8f7d4Seric # include <sysexits.h>
6b5d4f080Seric # include <whoami.h>
7adf8f7d4Seric 
810cd16efSeric /*
910cd16efSeric **  SCCS.C -- human-oriented front end to the SCCS system.
1010cd16efSeric **
1110cd16efSeric **	Without trying to add any functionality to speak of, this
1210cd16efSeric **	program tries to make SCCS a little more accessible to human
1310cd16efSeric **	types.  The main thing it does is automatically put the
1410cd16efSeric **	string "SCCS/s." on the front of names.  Also, it has a
1510cd16efSeric **	couple of things that are designed to shorten frequent
1610cd16efSeric **	combinations, e.g., "delget" which expands to a "delta"
1710cd16efSeric **	and a "get".
1810cd16efSeric **
1910cd16efSeric **	This program can also function as a setuid front end.
2010cd16efSeric **	To do this, you should copy the source, renaming it to
2110cd16efSeric **	whatever you want, e.g., "syssccs".  Change any defaults
2210cd16efSeric **	in the program (e.g., syssccs might default -d to
2310cd16efSeric **	"/usr/src/sys").  Then recompile and put the result
2410cd16efSeric **	as setuid to whomever you want.  In this mode, sccs
2510cd16efSeric **	knows to not run setuid for certain programs in order
2610cd16efSeric **	to preserve security, and so forth.
2710cd16efSeric **
2810cd16efSeric **	Usage:
2910cd16efSeric **		sccs [flags] command [args]
3010cd16efSeric **
3110cd16efSeric **	Flags:
3210cd16efSeric **		-d<dir>		<dir> represents a directory to search
3310cd16efSeric **				out of.  It should be a full pathname
3410cd16efSeric **				for general usage.  E.g., if <dir> is
3510cd16efSeric **				"/usr/src/sys", then a reference to the
3610cd16efSeric **				file "dev/bio.c" becomes a reference to
3710cd16efSeric **				"/usr/src/sys/dev/bio.c".
3810cd16efSeric **		-p<path>	prepends <path> to the final component
3910cd16efSeric **				of the pathname.  By default, this is
4010cd16efSeric **				"SCCS".  For example, in the -d example
4110cd16efSeric **				above, the path then gets modified to
4210cd16efSeric **				"/usr/src/sys/dev/SCCS/s.bio.c".  In
4310cd16efSeric **				more common usage (without the -d flag),
4410cd16efSeric **				"prog.c" would get modified to
4510cd16efSeric **				"SCCS/s.prog.c".  In both cases, the
4610cd16efSeric **				"s." gets automatically prepended.
4710cd16efSeric **		-r		run as the real user.
4810cd16efSeric **
4910cd16efSeric **	Commands:
5010cd16efSeric **		admin,
5110cd16efSeric **		get,
5210cd16efSeric **		delta,
5310cd16efSeric **		rmdel,
5410cd16efSeric **		chghist,
5510cd16efSeric **		etc.		Straight out of SCCS; only difference
5610cd16efSeric **				is that pathnames get modified as
5710cd16efSeric **				described above.
5810cd16efSeric **		edit		Macro for "get -e".
5910cd16efSeric **		unedit		Removes a file being edited, knowing
6010cd16efSeric **				about p-files, etc.
6110cd16efSeric **		delget		Macro for "delta" followed by "get".
6210cd16efSeric **		deledit		Macro for "delta" followed by "get -e".
6310cd16efSeric **		info		Tell what files being edited.
6410cd16efSeric **		clean		Remove all files that can be
6510cd16efSeric **				regenerated from SCCS files.
66095915d3Seric **		check		Like info, but return exit status, for
6710cd16efSeric **				use in makefiles.
6810cd16efSeric **		fix		Remove a top delta & reedit, but save
6910cd16efSeric **				the previous changes in that delta.
7010cd16efSeric **
7110cd16efSeric **	Compilation Flags:
7210cd16efSeric **		UIDUSER -- determine who the user is by looking at the
7310cd16efSeric **			uid rather than the login name -- for machines
7410cd16efSeric **			where SCCS gets the user in this way.
757abec564Seric **		SCCSDIR -- if defined, forces the -d flag to take on
76095915d3Seric **			this value.  This is so that the setuid
77095915d3Seric **			aspects of this program cannot be abused.
787abec564Seric **			This flag also disables the -p flag.
797abec564Seric **		SCCSPATH -- the default for the -p flag.
8010cd16efSeric **
8110cd16efSeric **	Compilation Instructions:
8210cd16efSeric **		cc -O -n -s sccs.c
8310cd16efSeric **
8410cd16efSeric **	Author:
8510cd16efSeric **		Eric Allman, UCB/INGRES
867abec564Seric **		Copyright 1980 Regents of the University of California
8710cd16efSeric */
8810cd16efSeric 
89*0faf63f9Seric static char SccsId[] = "@(#)sccs.c	1.35 10/15/80";
90*0faf63f9Seric 
917abec564Seric /*******************  Configuration Information  ********************/
927abec564Seric 
9310cd16efSeric # ifdef CSVAX
9410cd16efSeric # define UIDUSER
957abec564Seric # define PROGPATH(name)	"/usr/local/name"
967abec564Seric # endif CSVAX
9710cd16efSeric 
98*0faf63f9Seric # define SCCSPATH	"SCCS"	/* pathname in which to find s-files */
997abec564Seric /* put #define SCCSDIR here */
1007abec564Seric 
1017abec564Seric char	MyName[] = "sccs";	/* name used in messages */
1027abec564Seric 
103*0faf63f9Seric # ifndef PROGPATH
104*0faf63f9Seric # define PROGPATH(name)	"/usr/sccs/name"	/* place to find binaries */
105*0faf63f9Seric # endif PROGPATH
106*0faf63f9Seric 
1077abec564Seric /****************  End of Configuration Information  ****************/
108*0faf63f9Seric 
109c4432be4Seric # define bitset(bit, word)	((bit) & (word))
110c4432be4Seric 
111c4432be4Seric typedef char	bool;
1127de81dc7Seric # define TRUE	1
1137de81dc7Seric # define FALSE	0
114d02a4f42Seric 
11510cd16efSeric # ifdef UIDUSER
11610cd16efSeric # include <pwd.h>
11710cd16efSeric # endif UIDUSER
11810cd16efSeric 
119adf8f7d4Seric struct sccsprog
120adf8f7d4Seric {
121adf8f7d4Seric 	char	*sccsname;	/* name of SCCS routine */
1227de81dc7Seric 	short	sccsoper;	/* opcode, see below */
1237de81dc7Seric 	short	sccsflags;	/* flags, see below */
124108d6082Seric 	char	*sccsklets;	/* valid key-letters on macros */
125adf8f7d4Seric 	char	*sccspath;	/* pathname of binary implementing */
126adf8f7d4Seric };
127adf8f7d4Seric 
1287de81dc7Seric /* values for sccsoper */
1297de81dc7Seric # define PROG		0	/* call a program */
1301777fbcbSeric # define CMACRO		1	/* command substitution macro */
131172147faSeric # define FIX		2	/* fix a delta */
132b2538d76Seric # define CLEAN		3	/* clean out recreatable files */
133e39a5722Seric # define UNEDIT		4	/* unedit a file */
134419708b0Seric # define SHELL		5	/* call a shell file (like PROG) */
1357de81dc7Seric 
136c4432be4Seric /* bits for sccsflags */
1377de81dc7Seric # define NO_SDOT	0001	/* no s. on front of args */
1387de81dc7Seric # define REALUSER	0002	/* protected (e.g., admin) */
139adf8f7d4Seric 
14048ff0e7eSeric /* modes for the "clean", "info", "check" ops */
14148ff0e7eSeric # define CLEANC		0	/* clean command */
14248ff0e7eSeric # define INFOC		1	/* info command */
14348ff0e7eSeric # define CHECKC		2	/* check command */
14448ff0e7eSeric 
145*0faf63f9Seric /*
146*0faf63f9Seric **  Description of commands known to this program.
147*0faf63f9Seric **	First argument puts the command into a class.  Second arg is
148*0faf63f9Seric **	info regarding treatment of this command.  Third arg is a
149*0faf63f9Seric **	list of flags this command accepts from macros, etc.  Fourth
150*0faf63f9Seric **	arg is the pathname of the implementing program, or the
151*0faf63f9Seric **	macro definition, or the arg to a sub-algorithm.
152*0faf63f9Seric */
153b5d4f080Seric 
154adf8f7d4Seric struct sccsprog SccsProg[] =
155adf8f7d4Seric {
156108d6082Seric 	"admin",	PROG,	REALUSER,	"",		PROGPATH(admin),
157108d6082Seric 	"chghist",	PROG,	0,		"",		PROGPATH(rmdel),
158108d6082Seric 	"comb",		PROG,	0,		"",		PROGPATH(comb),
159108d6082Seric 	"delta",	PROG,	0,		"mysrp",	PROGPATH(delta),
160c952b8bfSeric 	"get",		PROG,	0,		"ixbeskcl",	PROGPATH(get),
161108d6082Seric 	"help",		PROG,	NO_SDOT,	"",		PROGPATH(help),
162108d6082Seric 	"prt",		PROG,	0,		"",		PROGPATH(prt),
163108d6082Seric 	"rmdel",	PROG,	REALUSER,	"",		PROGPATH(rmdel),
164108d6082Seric 	"what",		PROG,	NO_SDOT,	"",		PROGPATH(what),
165419708b0Seric 	"sccsdiff",	SHELL,	REALUSER,	"",		PROGPATH(sccsdiff),
166c952b8bfSeric 	"edit",		CMACRO,	NO_SDOT,	"ixbscl",	"get -e",
167108d6082Seric 	"delget",	CMACRO,	NO_SDOT,	"",		"delta/get -t",
168108d6082Seric 	"deledit",	CMACRO,	NO_SDOT,	"",		"delta/get -e -t",
169108d6082Seric 	"fix",		FIX,	NO_SDOT,	"",		NULL,
170108d6082Seric 	"clean",	CLEAN,	REALUSER,	"",		(char *) CLEANC,
171108d6082Seric 	"info",		CLEAN,	REALUSER,	"",		(char *) INFOC,
172108d6082Seric 	"check",	CLEAN,	REALUSER,	"",		(char *) CHECKC,
173108d6082Seric 	"unedit",	UNEDIT,	NO_SDOT,	"",		NULL,
174108d6082Seric 	NULL,		-1,	0,		"",		NULL
175adf8f7d4Seric };
176adf8f7d4Seric 
177*0faf63f9Seric /* one line from a p-file */
178e39a5722Seric struct pfile
179e39a5722Seric {
180e39a5722Seric 	char	*p_osid;	/* old SID */
181e39a5722Seric 	char	*p_nsid;	/* new SID */
182e39a5722Seric 	char	*p_user;	/* user who did edit */
183e39a5722Seric 	char	*p_date;	/* date of get */
184e39a5722Seric 	char	*p_time;	/* time of get */
185e39a5722Seric };
186e39a5722Seric 
1877abec564Seric char	*SccsPath = SCCSPATH;	/* pathname of SCCS files */
1887abec564Seric # ifdef SCCSDIR
1897abec564Seric char	*SccsDir = SCCSDIR;	/* directory to begin search from */
190095915d3Seric # else
1917abec564Seric char	*SccsDir = "";
192095915d3Seric # endif
193c4432be4Seric bool	RealUser;		/* if set, running as real user */
194f0cc3246Seric # ifdef DEBUG
195f0cc3246Seric bool	Debug;			/* turn on tracing */
196f0cc3246Seric # endif
197*0faf63f9Seric 
198adf8f7d4Seric main(argc, argv)
199adf8f7d4Seric 	int argc;
200adf8f7d4Seric 	char **argv;
201adf8f7d4Seric {
202adf8f7d4Seric 	register char *p;
203b1ed8a43Seric 	extern struct sccsprog *lookup();
20441290c52Seric 	register int i;
205adf8f7d4Seric 
206adf8f7d4Seric 	/*
207adf8f7d4Seric 	**  Detect and decode flags intended for this program.
208adf8f7d4Seric 	*/
209adf8f7d4Seric 
2107de81dc7Seric 	if (argc < 2)
211adf8f7d4Seric 	{
212095915d3Seric 		fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName);
2137de81dc7Seric 		exit(EX_USAGE);
2147de81dc7Seric 	}
2157de81dc7Seric 	argv[argc] = NULL;
2167de81dc7Seric 
217b1ed8a43Seric 	if (lookup(argv[0]) == NULL)
218b1ed8a43Seric 	{
2197de81dc7Seric 		while ((p = *++argv) != NULL)
2207de81dc7Seric 		{
221adf8f7d4Seric 			if (*p != '-')
222adf8f7d4Seric 				break;
223adf8f7d4Seric 			switch (*++p)
224adf8f7d4Seric 			{
225adf8f7d4Seric 			  case 'r':		/* run as real user */
226adf8f7d4Seric 				setuid(getuid());
227c4432be4Seric 				RealUser++;
228adf8f7d4Seric 				break;
229adf8f7d4Seric 
2307abec564Seric # ifndef SCCSDIR
231adf8f7d4Seric 			  case 'p':		/* path of sccs files */
232adf8f7d4Seric 				SccsPath = ++p;
233adf8f7d4Seric 				break;
234adf8f7d4Seric 
2350a8b9ab0Seric 			  case 'd':		/* directory to search from */
2360a8b9ab0Seric 				SccsDir = ++p;
2370a8b9ab0Seric 				break;
238095915d3Seric # endif
2390a8b9ab0Seric 
240f0cc3246Seric # ifdef DEBUG
241f0cc3246Seric 			  case 'T':		/* trace */
242f0cc3246Seric 				Debug++;
243f0cc3246Seric 				break;
244f0cc3246Seric # endif
245f0cc3246Seric 
246adf8f7d4Seric 			  default:
247095915d3Seric 				usrerr("unknown option -%s", p);
248adf8f7d4Seric 				break;
249adf8f7d4Seric 			}
250adf8f7d4Seric 		}
2515cabffd9Seric 		if (SccsPath[0] == '\0')
2525cabffd9Seric 			SccsPath = ".";
253b1ed8a43Seric 	}
254adf8f7d4Seric 
255108d6082Seric 	i = command(argv, FALSE, FALSE, "");
25641290c52Seric 	exit(i);
2577de81dc7Seric }
258*0faf63f9Seric 
259*0faf63f9Seric /*
26041290c52Seric **  COMMAND -- look up and perform a command
26141290c52Seric **
26241290c52Seric **	This routine is the guts of this program.  Given an
26341290c52Seric **	argument vector, it looks up the "command" (argv[0])
26441290c52Seric **	in the configuration table and does the necessary stuff.
26541290c52Seric **
26641290c52Seric **	Parameters:
26741290c52Seric **		argv -- an argument vector to process.
26841290c52Seric **		forkflag -- if set, fork before executing the command.
269108d6082Seric **		editflag -- if set, only include flags listed in the
270108d6082Seric **			sccsklets field of the command descriptor.
271108d6082Seric **		arg0 -- a space-seperated list of arguments to insert
272108d6082Seric **			before argv.
27341290c52Seric **
27441290c52Seric **	Returns:
27541290c52Seric **		zero -- command executed ok.
27641290c52Seric **		else -- error status.
27741290c52Seric **
27841290c52Seric **	Side Effects:
27941290c52Seric **		none.
28041290c52Seric */
2817de81dc7Seric 
282108d6082Seric command(argv, forkflag, editflag, arg0)
2837de81dc7Seric 	char **argv;
2841777fbcbSeric 	bool forkflag;
285108d6082Seric 	bool editflag;
286108d6082Seric 	char *arg0;
2877de81dc7Seric {
2887de81dc7Seric 	register struct sccsprog *cmd;
2897de81dc7Seric 	register char *p;
2901777fbcbSeric 	char buf[40];
291b1ed8a43Seric 	extern struct sccsprog *lookup();
292108d6082Seric 	char *nav[1000];
293108d6082Seric 	char **np;
294419708b0Seric 	register char **ap;
295d51cd7e5Seric 	register int i;
296419708b0Seric 	register char *q;
297d51cd7e5Seric 	extern bool unedit();
29841290c52Seric 	int rval = 0;
299108d6082Seric 	extern char *index();
300108d6082Seric 	extern char *makefile();
301f0cc3246Seric 
302f0cc3246Seric # ifdef DEBUG
303f0cc3246Seric 	if (Debug)
304f0cc3246Seric 	{
305108d6082Seric 		printf("command:\n\t\"%s\"\n", arg0);
306108d6082Seric 		for (np = argv; *np != NULL; np++)
307108d6082Seric 			printf("\t\"%s\"\n", *np);
308f0cc3246Seric 	}
309f0cc3246Seric # endif
310c4432be4Seric 
311c4432be4Seric 	/*
312108d6082Seric 	**  Copy arguments.
313108d6082Seric 	**	Phase one -- from arg0 & if necessary argv[0].
314adf8f7d4Seric 	*/
315adf8f7d4Seric 
316419708b0Seric 	np = ap = &nav[1];
317108d6082Seric 	for (p = arg0, q = buf; *p != '\0' && *p != '/'; )
318108d6082Seric 	{
319108d6082Seric 		*np++ = q;
320108d6082Seric 		while (*p == ' ')
321108d6082Seric 			p++;
322108d6082Seric 		while (*p != ' ' && *p != '\0' && *p != '/')
323108d6082Seric 			*q++ = *p++;
324108d6082Seric 		*q++ = '\0';
325108d6082Seric 	}
326108d6082Seric 	*np = NULL;
327419708b0Seric 	if (*ap == NULL)
328108d6082Seric 		*np++ = *argv++;
329108d6082Seric 
330108d6082Seric 	/*
331108d6082Seric 	**  Look up command.
332419708b0Seric 	**	At this point, *ap is the command name.
333108d6082Seric 	*/
334108d6082Seric 
335419708b0Seric 	cmd = lookup(*ap);
336b1ed8a43Seric 	if (cmd == NULL)
337adf8f7d4Seric 	{
338419708b0Seric 		usrerr("Unknown command \"%s\"", *ap);
33941290c52Seric 		return (EX_USAGE);
340adf8f7d4Seric 	}
341adf8f7d4Seric 
342adf8f7d4Seric 	/*
343108d6082Seric 	**  Copy remaining arguments doing editing as appropriate.
344108d6082Seric 	*/
345108d6082Seric 
346108d6082Seric 	for (; *argv != NULL; argv++)
347108d6082Seric 	{
348108d6082Seric 		p = *argv;
349108d6082Seric 		if (*p == '-')
350108d6082Seric 		{
351108d6082Seric 			if (p[1] == '\0' || !editflag || cmd->sccsklets == NULL ||
352108d6082Seric 			    index(cmd->sccsklets, p[1]) != NULL)
353108d6082Seric 				*np++ = p;
354108d6082Seric 		}
355108d6082Seric 		else
356108d6082Seric 		{
357108d6082Seric 			if (!bitset(NO_SDOT, cmd->sccsflags))
358108d6082Seric 				p = makefile(p);
359108d6082Seric 			if (p != NULL)
360108d6082Seric 				*np++ = p;
361108d6082Seric 		}
362108d6082Seric 	}
363108d6082Seric 	*np = NULL;
364108d6082Seric 
365108d6082Seric 	/*
3667de81dc7Seric 	**  Interpret operation associated with this command.
367c4432be4Seric 	*/
368c4432be4Seric 
3697de81dc7Seric 	switch (cmd->sccsoper)
3707de81dc7Seric 	{
371419708b0Seric 	  case SHELL:		/* call a shell file */
372419708b0Seric 		*ap = cmd->sccspath;
373419708b0Seric 		*--ap = "sh";
374419708b0Seric 		rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag);
375419708b0Seric 		break;
376419708b0Seric 
3777de81dc7Seric 	  case PROG:		/* call an sccs prog */
378419708b0Seric 		rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag);
3791777fbcbSeric 		break;
3801777fbcbSeric 
3811777fbcbSeric 	  case CMACRO:		/* command macro */
3821777fbcbSeric 		for (p = cmd->sccspath; *p != '\0'; p++)
3831777fbcbSeric 		{
384108d6082Seric 			q = p;
385108d6082Seric 			while (*p != '\0' && *p != '/')
386108d6082Seric 				p++;
387419708b0Seric 			rval = command(&ap[1], *p != '\0', TRUE, q);
38841290c52Seric 			if (rval != 0)
38941290c52Seric 				break;
3901777fbcbSeric 		}
39141290c52Seric 		break;
3927de81dc7Seric 
393172147faSeric 	  case FIX:		/* fix a delta */
394419708b0Seric 		if (strncmp(ap[1], "-r", 2) != 0)
395172147faSeric 		{
396095915d3Seric 			usrerr("-r flag needed for fix command");
39741290c52Seric 			rval = EX_USAGE;
398172147faSeric 			break;
399172147faSeric 		}
400419708b0Seric 		rval = command(&ap[1], TRUE, TRUE, "get -k");
40141290c52Seric 		if (rval == 0)
402419708b0Seric 			rval = command(&ap[1], TRUE, TRUE, "rmdel");
40341290c52Seric 		if (rval == 0)
404419708b0Seric 			rval = command(&ap[2], FALSE, TRUE, "get -e -g");
40541290c52Seric 		break;
406172147faSeric 
407b2538d76Seric 	  case CLEAN:
40841290c52Seric 		rval = clean((int) cmd->sccspath);
409b2538d76Seric 		break;
410b2538d76Seric 
411e39a5722Seric 	  case UNEDIT:
412419708b0Seric 		for (argv = np = &ap[1]; *argv != NULL; argv++)
413d51cd7e5Seric 		{
414108d6082Seric 			if (unedit(*argv))
415108d6082Seric 				*np++ = *argv;
416d51cd7e5Seric 		}
417108d6082Seric 		*np = NULL;
418d51cd7e5Seric 		if (i > 0)
419419708b0Seric 			rval = command(&ap[1], FALSE, FALSE, "get");
420e39a5722Seric 		break;
421e39a5722Seric 
4227de81dc7Seric 	  default:
423095915d3Seric 		syserr("oper %d", cmd->sccsoper);
4247de81dc7Seric 		exit(EX_SOFTWARE);
4257de81dc7Seric 	}
42641290c52Seric # ifdef DEBUG
42741290c52Seric 	if (Debug)
42841290c52Seric 		printf("command: rval=%d\n", rval);
42941290c52Seric # endif
43041290c52Seric 	return (rval);
4317de81dc7Seric }
432*0faf63f9Seric 
433*0faf63f9Seric /*
434b1ed8a43Seric **  LOOKUP -- look up an SCCS command name.
435b1ed8a43Seric **
436b1ed8a43Seric **	Parameters:
437b1ed8a43Seric **		name -- the name of the command to look up.
438b1ed8a43Seric **
439b1ed8a43Seric **	Returns:
440b1ed8a43Seric **		ptr to command descriptor for this command.
441b1ed8a43Seric **		NULL if no such entry.
442b1ed8a43Seric **
443b1ed8a43Seric **	Side Effects:
444b1ed8a43Seric **		none.
445b1ed8a43Seric */
446b1ed8a43Seric 
447b1ed8a43Seric struct sccsprog *
448b1ed8a43Seric lookup(name)
449b1ed8a43Seric 	char *name;
450b1ed8a43Seric {
451b1ed8a43Seric 	register struct sccsprog *cmd;
452b1ed8a43Seric 
453b1ed8a43Seric 	for (cmd = SccsProg; cmd->sccsname != NULL; cmd++)
454b1ed8a43Seric 	{
455b1ed8a43Seric 		if (strcmp(cmd->sccsname, name) == 0)
456b1ed8a43Seric 			return (cmd);
457b1ed8a43Seric 	}
458b1ed8a43Seric 	return (NULL);
459b1ed8a43Seric }
460*0faf63f9Seric 
461*0faf63f9Seric /*
46241290c52Seric **  CALLPROG -- call a program
46341290c52Seric **
464108d6082Seric **	Used to call the SCCS programs.
46541290c52Seric **
46641290c52Seric **	Parameters:
46741290c52Seric **		progpath -- pathname of the program to call.
46841290c52Seric **		flags -- status flags from the command descriptors.
46941290c52Seric **		argv -- an argument vector to pass to the program.
47041290c52Seric **		forkflag -- if true, fork before calling, else just
47141290c52Seric **			exec.
47241290c52Seric **
47341290c52Seric **	Returns:
47441290c52Seric **		The exit status of the program.
47541290c52Seric **		Nothing if forkflag == FALSE.
47641290c52Seric **
47741290c52Seric **	Side Effects:
47841290c52Seric **		Can exit if forkflag == FALSE.
47941290c52Seric */
480172147faSeric 
4817de81dc7Seric callprog(progpath, flags, argv, forkflag)
4827de81dc7Seric 	char *progpath;
4837de81dc7Seric 	short flags;
4847de81dc7Seric 	char **argv;
4857de81dc7Seric 	bool forkflag;
4867de81dc7Seric {
4877de81dc7Seric 	register int i;
4881777fbcbSeric 	auto int st;
489108d6082Seric 
490108d6082Seric # ifdef DEBUG
491108d6082Seric 	if (Debug)
492108d6082Seric 	{
493108d6082Seric 		printf("callprog:\n");
494108d6082Seric 		for (i = 0; argv[i] != NULL; i++)
495108d6082Seric 			printf("\t\"%s\"\n", argv[i]);
496108d6082Seric 	}
497108d6082Seric # endif
4987de81dc7Seric 
4997de81dc7Seric 	if (*argv == NULL)
5007de81dc7Seric 		return (-1);
501c4432be4Seric 
502c4432be4Seric 	/*
503172147faSeric 	**  Fork if appropriate.
504adf8f7d4Seric 	*/
505adf8f7d4Seric 
5067de81dc7Seric 	if (forkflag)
5077de81dc7Seric 	{
508f0cc3246Seric # ifdef DEBUG
509f0cc3246Seric 		if (Debug)
510f0cc3246Seric 			printf("Forking\n");
511f0cc3246Seric # endif
5127de81dc7Seric 		i = fork();
5137de81dc7Seric 		if (i < 0)
5147de81dc7Seric 		{
515095915d3Seric 			syserr("cannot fork");
5167de81dc7Seric 			exit(EX_OSERR);
5177de81dc7Seric 		}
5187de81dc7Seric 		else if (i > 0)
5191777fbcbSeric 		{
5201777fbcbSeric 			wait(&st);
52141290c52Seric 			if ((st & 0377) == 0)
52241290c52Seric 				st = (st >> 8) & 0377;
5231777fbcbSeric 			return (st);
5241777fbcbSeric 		}
5257de81dc7Seric 	}
5267de81dc7Seric 
5277de81dc7Seric 	/*
5287de81dc7Seric 	**  Set protection as appropriate.
5297de81dc7Seric 	*/
5307de81dc7Seric 
5317de81dc7Seric 	if (bitset(REALUSER, flags))
5327de81dc7Seric 		setuid(getuid());
5337de81dc7Seric 
5347de81dc7Seric 	/*
535172147faSeric 	**  Call real SCCS program.
5367de81dc7Seric 	*/
5377de81dc7Seric 
538172147faSeric 	execv(progpath, argv);
539095915d3Seric 	syserr("cannot execute %s", progpath);
540adf8f7d4Seric 	exit(EX_UNAVAILABLE);
541adf8f7d4Seric }
542*0faf63f9Seric 
543*0faf63f9Seric /*
544cdc1aa65Seric **  MAKEFILE -- make filename of SCCS file
545cdc1aa65Seric **
546cdc1aa65Seric **	If the name passed is already the name of an SCCS file,
547cdc1aa65Seric **	just return it.  Otherwise, munge the name into the name
548cdc1aa65Seric **	of the actual SCCS file.
549cdc1aa65Seric **
550cdc1aa65Seric **	There are cases when it is not clear what you want to
551cdc1aa65Seric **	do.  For example, if SccsPath is an absolute pathname
552cdc1aa65Seric **	and the name given is also an absolute pathname, we go
553cdc1aa65Seric **	for SccsPath (& only use the last component of the name
554cdc1aa65Seric **	passed) -- this is important for security reasons (if
555cdc1aa65Seric **	sccs is being used as a setuid front end), but not
556cdc1aa65Seric **	particularly intuitive.
557cdc1aa65Seric **
558cdc1aa65Seric **	Parameters:
559cdc1aa65Seric **		name -- the file name to be munged.
560cdc1aa65Seric **
561cdc1aa65Seric **	Returns:
562cdc1aa65Seric **		The pathname of the sccs file.
563cdc1aa65Seric **		NULL on error.
564cdc1aa65Seric **
565cdc1aa65Seric **	Side Effects:
566cdc1aa65Seric **		none.
567cdc1aa65Seric */
568adf8f7d4Seric 
569adf8f7d4Seric char *
570adf8f7d4Seric makefile(name)
571adf8f7d4Seric 	char *name;
572adf8f7d4Seric {
573adf8f7d4Seric 	register char *p;
574adf8f7d4Seric 	register char c;
575adf8f7d4Seric 	char buf[512];
5760a8b9ab0Seric 	struct stat stbuf;
577adf8f7d4Seric 	extern char *malloc();
578cdc1aa65Seric 	extern char *rindex();
5790a8b9ab0Seric 	extern bool safepath();
5807d70f5d8Seric 	extern bool isdir();
5817d70f5d8Seric 	register char *q;
582cdc1aa65Seric 
583cdc1aa65Seric 	p = rindex(name, '/');
584cdc1aa65Seric 	if (p == NULL)
585cdc1aa65Seric 		p = name;
586cdc1aa65Seric 	else
587cdc1aa65Seric 		p++;
588adf8f7d4Seric 
589adf8f7d4Seric 	/*
5900a8b9ab0Seric 	**  Check to see that the path is "safe", i.e., that we
5910a8b9ab0Seric 	**  are not letting some nasty person use the setuid part
5920a8b9ab0Seric 	**  of this program to look at or munge some presumably
5930a8b9ab0Seric 	**  hidden files.
594adf8f7d4Seric 	*/
595adf8f7d4Seric 
5960a8b9ab0Seric 	if (SccsDir[0] == '/' && !safepath(name))
5970a8b9ab0Seric 		return (NULL);
598cdc1aa65Seric 
599cdc1aa65Seric 	/*
6000a8b9ab0Seric 	**  Create the base pathname.
601cdc1aa65Seric 	*/
602cdc1aa65Seric 
6030a8b9ab0Seric 	if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0)
604adf8f7d4Seric 	{
6050a8b9ab0Seric 		strcpy(buf, SccsDir);
606cdc1aa65Seric 		strcat(buf, "/");
607cdc1aa65Seric 	}
608cdc1aa65Seric 	else
609cdc1aa65Seric 		strcpy(buf, "");
610cdc1aa65Seric 	strncat(buf, name, p - name);
6117d70f5d8Seric 	q = &buf[strlen(buf)];
6127d70f5d8Seric 	strcpy(q, p);
6137d70f5d8Seric 	if (strncmp(p, "s.", 2) != 0 && !isdir(buf))
6147d70f5d8Seric 	{
6150a8b9ab0Seric 		strcpy(q, SccsPath);
6160a8b9ab0Seric 		strcat(buf, "/s.");
617cdc1aa65Seric 		strcat(buf, p);
618cdc1aa65Seric 	}
619adf8f7d4Seric 
6200a8b9ab0Seric 	if (strcmp(buf, name) == 0)
6210a8b9ab0Seric 		p = name;
6220a8b9ab0Seric 	else
6230a8b9ab0Seric 	{
624adf8f7d4Seric 		p = malloc(strlen(buf) + 1);
625adf8f7d4Seric 		if (p == NULL)
626adf8f7d4Seric 		{
627adf8f7d4Seric 			perror("Sccs: no mem");
628adf8f7d4Seric 			exit(EX_OSERR);
629adf8f7d4Seric 		}
630adf8f7d4Seric 		strcpy(p, buf);
6310a8b9ab0Seric 	}
632adf8f7d4Seric 	return (p);
633adf8f7d4Seric }
634*0faf63f9Seric 
635*0faf63f9Seric /*
6367d70f5d8Seric **  ISDIR -- return true if the argument is a directory.
6377d70f5d8Seric **
6387d70f5d8Seric **	Parameters:
6397d70f5d8Seric **		name -- the pathname of the file to check.
6407d70f5d8Seric **
6417d70f5d8Seric **	Returns:
6427d70f5d8Seric **		TRUE if 'name' is a directory, FALSE otherwise.
6437d70f5d8Seric **
6447d70f5d8Seric **	Side Effects:
6457d70f5d8Seric **		none.
6467d70f5d8Seric */
6477d70f5d8Seric 
6487d70f5d8Seric bool
6497d70f5d8Seric isdir(name)
6507d70f5d8Seric 	char *name;
6517d70f5d8Seric {
6527d70f5d8Seric 	struct stat stbuf;
6537d70f5d8Seric 
6547d70f5d8Seric 	return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR);
6557d70f5d8Seric }
656*0faf63f9Seric 
657*0faf63f9Seric /*
658cdc1aa65Seric **  SAFEPATH -- determine whether a pathname is "safe"
659cdc1aa65Seric **
660cdc1aa65Seric **	"Safe" pathnames only allow you to get deeper into the
661cdc1aa65Seric **	directory structure, i.e., full pathnames and ".." are
662cdc1aa65Seric **	not allowed.
663cdc1aa65Seric **
664cdc1aa65Seric **	Parameters:
665cdc1aa65Seric **		p -- the name to check.
666cdc1aa65Seric **
667cdc1aa65Seric **	Returns:
668cdc1aa65Seric **		TRUE -- if the path is safe.
669cdc1aa65Seric **		FALSE -- if the path is not safe.
670cdc1aa65Seric **
671cdc1aa65Seric **	Side Effects:
672cdc1aa65Seric **		Prints a message if the path is not safe.
673cdc1aa65Seric */
674cdc1aa65Seric 
675cdc1aa65Seric bool
676cdc1aa65Seric safepath(p)
677cdc1aa65Seric 	register char *p;
678cdc1aa65Seric {
679cdc1aa65Seric 	extern char *index();
680cdc1aa65Seric 
681cdc1aa65Seric 	if (*p != '/')
682cdc1aa65Seric 	{
683cdc1aa65Seric 		while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0)
684cdc1aa65Seric 		{
685cdc1aa65Seric 			p = index(p, '/');
686cdc1aa65Seric 			if (p == NULL)
687cdc1aa65Seric 				return (TRUE);
688cdc1aa65Seric 			p++;
689cdc1aa65Seric 		}
690cdc1aa65Seric 	}
691cdc1aa65Seric 
692cdc1aa65Seric 	printf("You may not use full pathnames or \"..\"\n");
693cdc1aa65Seric 	return (FALSE);
694cdc1aa65Seric }
695*0faf63f9Seric 
696*0faf63f9Seric /*
697b2538d76Seric **  CLEAN -- clean out recreatable files
698b2538d76Seric **
699b2538d76Seric **	Any file for which an "s." file exists but no "p." file
700b2538d76Seric **	exists in the current directory is purged.
701b2538d76Seric **
702b2538d76Seric **	Parameters:
70348ff0e7eSeric **		tells whether this came from a "clean", "info", or
70448ff0e7eSeric **		"check" command.
705b2538d76Seric **
706b2538d76Seric **	Returns:
707b2538d76Seric **		none.
708b2538d76Seric **
709b2538d76Seric **	Side Effects:
71048ff0e7eSeric **		Removes files in the current directory.
71148ff0e7eSeric **		Prints information regarding files being edited.
71248ff0e7eSeric **		Exits if a "check" command.
713b2538d76Seric */
714b2538d76Seric 
71548ff0e7eSeric clean(mode)
71648ff0e7eSeric 	int mode;
717b2538d76Seric {
718b2538d76Seric 	struct direct dir;
719b2538d76Seric 	struct stat stbuf;
720b2538d76Seric 	char buf[100];
721bf9c1e66Seric 	char pline[120];
7223a208c77Seric 	register FILE *dirfd;
7233a208c77Seric 	register char *basefile;
7242caac8fdSeric 	bool gotedit;
725bf9c1e66Seric 	FILE *pfp;
726b2538d76Seric 
727f77a08fbSeric 	strcpy(buf, SccsDir);
728f77a08fbSeric 	if (buf[0] != '\0')
729f77a08fbSeric 		strcat(buf, "/");
730f77a08fbSeric 	strcat(buf, SccsPath);
731f77a08fbSeric 	dirfd = fopen(buf, "r");
732b2538d76Seric 	if (dirfd == NULL)
733b2538d76Seric 	{
734f77a08fbSeric 		usrerr("cannot open %s", buf);
73541290c52Seric 		return (EX_NOINPUT);
736b2538d76Seric 	}
737b2538d76Seric 
738b2538d76Seric 	/*
739b2538d76Seric 	**  Scan the SCCS directory looking for s. files.
740b2538d76Seric 	*/
741b2538d76Seric 
7422caac8fdSeric 	gotedit = FALSE;
743b2538d76Seric 	while (fread(&dir, sizeof dir, 1, dirfd) != NULL)
744b2538d76Seric 	{
745c9c62fd0Seric 		if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0)
746b2538d76Seric 			continue;
747b2538d76Seric 
748b2538d76Seric 		/* got an s. file -- see if the p. file exists */
749f77a08fbSeric 		strcpy(buf, SccsDir);
750f77a08fbSeric 		if (buf[0] != '\0')
751f77a08fbSeric 			strcat(buf, "/");
752f77a08fbSeric 		strcat(buf, SccsPath);
753b2538d76Seric 		strcat(buf, "/p.");
7543a208c77Seric 		basefile = &buf[strlen(buf)];
755c9c62fd0Seric 		strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2);
756bf9c1e66Seric 		basefile[sizeof dir.d_name - 2] = '\0';
757bf9c1e66Seric 		pfp = fopen(buf, "r");
758bf9c1e66Seric 		if (pfp != NULL)
7593a208c77Seric 		{
760bf9c1e66Seric 			while (fgets(pline, sizeof pline, pfp) != NULL)
7612444cd3eSeric 				printf("%12s: being edited: %s", basefile, pline);
762bf9c1e66Seric 			fclose(pfp);
7632caac8fdSeric 			gotedit = TRUE;
764b2538d76Seric 			continue;
7653a208c77Seric 		}
766b2538d76Seric 
767b2538d76Seric 		/* the s. file exists and no p. file exists -- unlink the g-file */
76848ff0e7eSeric 		if (mode == CLEANC)
7693a208c77Seric 		{
770c9c62fd0Seric 			strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2);
7713a208c77Seric 			buf[sizeof dir.d_name - 2] = '\0';
772b2538d76Seric 			unlink(buf);
773b2538d76Seric 		}
7743a208c77Seric 	}
775b2538d76Seric 
776b2538d76Seric 	fclose(dirfd);
77748ff0e7eSeric 	if (!gotedit && mode == INFOC)
7782444cd3eSeric 		printf("Nothing being edited\n");
77948ff0e7eSeric 	if (mode == CHECKC)
78048ff0e7eSeric 		exit(gotedit);
78141290c52Seric 	return (EX_OK);
782b2538d76Seric }
783*0faf63f9Seric 
784*0faf63f9Seric /*
785e39a5722Seric **  UNEDIT -- unedit a file
786e39a5722Seric **
787e39a5722Seric **	Checks to see that the current user is actually editting
788e39a5722Seric **	the file and arranges that s/he is not editting it.
789e39a5722Seric **
790e39a5722Seric **	Parameters:
7912444cd3eSeric **		fn -- the name of the file to be unedited.
792e39a5722Seric **
793e39a5722Seric **	Returns:
794d51cd7e5Seric **		TRUE -- if the file was successfully unedited.
795d51cd7e5Seric **		FALSE -- if the file was not unedited for some
796d51cd7e5Seric **			reason.
797e39a5722Seric **
798e39a5722Seric **	Side Effects:
799e39a5722Seric **		fn is removed
800e39a5722Seric **		entries are removed from pfile.
801e39a5722Seric */
802e39a5722Seric 
803d51cd7e5Seric bool
804e39a5722Seric unedit(fn)
805e39a5722Seric 	char *fn;
806e39a5722Seric {
807e39a5722Seric 	register FILE *pfp;
808e39a5722Seric 	char *pfn;
809e39a5722Seric 	static char tfn[] = "/tmp/sccsXXXXX";
810e39a5722Seric 	FILE *tfp;
811e39a5722Seric 	register char *p;
812e39a5722Seric 	register char *q;
813e39a5722Seric 	bool delete = FALSE;
814e39a5722Seric 	bool others = FALSE;
815e39a5722Seric 	char *myname;
816e39a5722Seric 	extern char *getlogin();
817e39a5722Seric 	struct pfile *pent;
818e39a5722Seric 	extern struct pfile *getpfile();
819e39a5722Seric 	char buf[120];
820108d6082Seric 	extern char *makefile();
82110cd16efSeric # ifdef UIDUSER
82210cd16efSeric 	struct passwd *pw;
82310cd16efSeric 	extern struct passwd *getpwuid();
82410cd16efSeric # endif UIDUSER
825e39a5722Seric 
826e39a5722Seric 	/* make "s." filename & find the trailing component */
827e39a5722Seric 	pfn = makefile(fn);
828cdc1aa65Seric 	if (pfn == NULL)
829cdc1aa65Seric 		return (FALSE);
830cdc1aa65Seric 	q = rindex(pfn, '/');
831cdc1aa65Seric 	if (q == NULL)
832cdc1aa65Seric 		q = &pfn[-1];
833cdc1aa65Seric 	if (q[1] != 's' || q[2] != '.')
834e39a5722Seric 	{
835095915d3Seric 		usrerr("bad file name \"%s\"", fn);
836d51cd7e5Seric 		return (FALSE);
837e39a5722Seric 	}
838e39a5722Seric 
839e39a5722Seric 	/* turn "s." into "p." */
840e39a5722Seric 	*++q = 'p';
841e39a5722Seric 
842e39a5722Seric 	pfp = fopen(pfn, "r");
843e39a5722Seric 	if (pfp == NULL)
844e39a5722Seric 	{
8452444cd3eSeric 		printf("%12s: not being edited\n", fn);
846d51cd7e5Seric 		return (FALSE);
847e39a5722Seric 	}
848e39a5722Seric 
849e39a5722Seric 	/*
850e39a5722Seric 	**  Copy p-file to temp file, doing deletions as needed.
851e39a5722Seric 	*/
852e39a5722Seric 
853e39a5722Seric 	mktemp(tfn);
854e39a5722Seric 	tfp = fopen(tfn, "w");
855e39a5722Seric 	if (tfp == NULL)
856e39a5722Seric 	{
857095915d3Seric 		usrerr("cannot create \"%s\"", tfn);
858e39a5722Seric 		exit(EX_OSERR);
859e39a5722Seric 	}
860e39a5722Seric 
86110cd16efSeric # ifdef UIDUSER
86210cd16efSeric 	pw = getpwuid(getuid());
86310cd16efSeric 	if (pw == NULL)
86410cd16efSeric 	{
865095915d3Seric 		syserr("who are you? (uid=%d)", getuid());
86610cd16efSeric 		exit(EX_OSERR);
86710cd16efSeric 	}
86810cd16efSeric 	myname = pw->pw_name;
86910cd16efSeric # else
870e39a5722Seric 	myname = getlogin();
87110cd16efSeric # endif UIDUSER
872e39a5722Seric 	while ((pent = getpfile(pfp)) != NULL)
873e39a5722Seric 	{
874e39a5722Seric 		if (strcmp(pent->p_user, myname) == 0)
875e39a5722Seric 		{
876e39a5722Seric 			/* a match */
877e39a5722Seric 			delete++;
878e39a5722Seric 		}
879e39a5722Seric 		else
880e39a5722Seric 		{
881e39a5722Seric 			fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid,
882e39a5722Seric 			    pent->p_nsid, pent->p_user, pent->p_date,
883e39a5722Seric 			    pent->p_time);
884e39a5722Seric 			others++;
885e39a5722Seric 		}
886e39a5722Seric 	}
887e39a5722Seric 
888e39a5722Seric 	/* do final cleanup */
889e39a5722Seric 	if (others)
890e39a5722Seric 	{
891e39a5722Seric 		if (freopen(tfn, "r", tfp) == NULL)
892e39a5722Seric 		{
893095915d3Seric 			syserr("cannot reopen \"%s\"", tfn);
894e39a5722Seric 			exit(EX_OSERR);
895e39a5722Seric 		}
896e39a5722Seric 		if (freopen(pfn, "w", pfp) == NULL)
897e39a5722Seric 		{
898095915d3Seric 			usrerr("cannot create \"%s\"", pfn);
899d51cd7e5Seric 			return (FALSE);
900e39a5722Seric 		}
901e39a5722Seric 		while (fgets(buf, sizeof buf, tfp) != NULL)
902e39a5722Seric 			fputs(buf, pfp);
903e39a5722Seric 	}
904e39a5722Seric 	else
905e39a5722Seric 	{
906e39a5722Seric 		unlink(pfn);
907e39a5722Seric 	}
908e39a5722Seric 	fclose(tfp);
909e39a5722Seric 	fclose(pfp);
910e39a5722Seric 	unlink(tfn);
911e39a5722Seric 
912e39a5722Seric 	if (delete)
913e39a5722Seric 	{
914e39a5722Seric 		unlink(fn);
915e39a5722Seric 		printf("%12s: removed\n", fn);
916d51cd7e5Seric 		return (TRUE);
917e39a5722Seric 	}
918e39a5722Seric 	else
919e39a5722Seric 	{
9202444cd3eSeric 		printf("%12s: not being edited by you\n", fn);
921d51cd7e5Seric 		return (FALSE);
922e39a5722Seric 	}
923e39a5722Seric }
924*0faf63f9Seric 
925*0faf63f9Seric /*
926e39a5722Seric **  GETPFILE -- get an entry from the p-file
927e39a5722Seric **
928e39a5722Seric **	Parameters:
929e39a5722Seric **		pfp -- p-file file pointer
930e39a5722Seric **
931e39a5722Seric **	Returns:
932e39a5722Seric **		pointer to p-file struct for next entry
933e39a5722Seric **		NULL on EOF or error
934e39a5722Seric **
935e39a5722Seric **	Side Effects:
936e39a5722Seric **		Each call wipes out results of previous call.
937e39a5722Seric */
938e39a5722Seric 
939e39a5722Seric struct pfile *
940e39a5722Seric getpfile(pfp)
941e39a5722Seric 	FILE *pfp;
942e39a5722Seric {
943e39a5722Seric 	static struct pfile ent;
944e39a5722Seric 	static char buf[120];
945e39a5722Seric 	register char *p;
946e39a5722Seric 	extern char *nextfield();
947e39a5722Seric 
948e39a5722Seric 	if (fgets(buf, sizeof buf, pfp) == NULL)
949e39a5722Seric 		return (NULL);
950e39a5722Seric 
951e39a5722Seric 	ent.p_osid = p = buf;
952e39a5722Seric 	ent.p_nsid = p = nextfield(p);
953e39a5722Seric 	ent.p_user = p = nextfield(p);
954e39a5722Seric 	ent.p_date = p = nextfield(p);
955e39a5722Seric 	ent.p_time = p = nextfield(p);
956e39a5722Seric 	if (p == NULL || nextfield(p) != NULL)
957e39a5722Seric 		return (NULL);
958e39a5722Seric 
959e39a5722Seric 	return (&ent);
960e39a5722Seric }
961e39a5722Seric 
962e39a5722Seric 
963e39a5722Seric char *
964e39a5722Seric nextfield(p)
965e39a5722Seric 	register char *p;
966e39a5722Seric {
967e39a5722Seric 	if (p == NULL || *p == '\0')
968e39a5722Seric 		return (NULL);
969e39a5722Seric 	while (*p != ' ' && *p != '\n' && *p != '\0')
970e39a5722Seric 		p++;
971e39a5722Seric 	if (*p == '\n' || *p == '\0')
972e39a5722Seric 	{
973e39a5722Seric 		*p = '\0';
974e39a5722Seric 		return (NULL);
975e39a5722Seric 	}
976e39a5722Seric 	*p++ = '\0';
977e39a5722Seric 	return (p);
978e39a5722Seric }
979*0faf63f9Seric 
980*0faf63f9Seric /*
981095915d3Seric **  USRERR -- issue user-level error
982095915d3Seric **
983095915d3Seric **	Parameters:
984095915d3Seric **		f -- format string.
985095915d3Seric **		p1-p3 -- parameters to a printf.
986095915d3Seric **
987095915d3Seric **	Returns:
988095915d3Seric **		-1
989095915d3Seric **
990095915d3Seric **	Side Effects:
991095915d3Seric **		none.
992095915d3Seric */
993095915d3Seric 
994095915d3Seric usrerr(f, p1, p2, p3)
995095915d3Seric 	char *f;
996095915d3Seric {
997095915d3Seric 	fprintf(stderr, "\n%s: ", MyName);
998095915d3Seric 	fprintf(stderr, f, p1, p2, p3);
999095915d3Seric 	fprintf(stderr, "\n");
1000095915d3Seric 
1001095915d3Seric 	return (-1);
1002095915d3Seric }
1003*0faf63f9Seric 
1004*0faf63f9Seric /*
1005095915d3Seric **  SYSERR -- print system-generated error.
1006095915d3Seric **
1007095915d3Seric **	Parameters:
1008095915d3Seric **		f -- format string to a printf.
1009095915d3Seric **		p1, p2, p3 -- parameters to f.
1010095915d3Seric **
1011095915d3Seric **	Returns:
1012095915d3Seric **		never.
1013095915d3Seric **
1014095915d3Seric **	Side Effects:
1015095915d3Seric **		none.
1016095915d3Seric */
1017095915d3Seric 
1018095915d3Seric syserr(f, p1, p2, p3)
1019095915d3Seric 	char *f;
1020095915d3Seric {
1021095915d3Seric 	extern int errno;
1022095915d3Seric 
1023095915d3Seric 	fprintf(stderr, "\n%s SYSERR: ", MyName);
1024095915d3Seric 	fprintf(stderr, f, p1, p2, p3);
1025095915d3Seric 	fprintf(stderr, "\n");
1026095915d3Seric 	if (errno == 0)
1027095915d3Seric 		exit(EX_SOFTWARE);
1028095915d3Seric 	else
1029095915d3Seric 	{
1030095915d3Seric 		perror(0);
1031095915d3Seric 		exit(EX_OSERR);
1032095915d3Seric 	}
1033095915d3Seric }
1034