xref: /original-bsd/usr.bin/sccs/sccs.c (revision 78d41c6a)
1adf8f7d4Seric # include <stdio.h>
2adf8f7d4Seric # include <sys/types.h>
3adf8f7d4Seric # include <sys/stat.h>
4b2538d76Seric # include <sys/dir.h>
5*78d41c6aSeric # include <errno.h>
6*78d41c6aSeric # include <signal.h>
7adf8f7d4Seric # include <sysexits.h>
8b5d4f080Seric # include <whoami.h>
9adf8f7d4Seric 
1010cd16efSeric /*
1110cd16efSeric **  SCCS.C -- human-oriented front end to the SCCS system.
1210cd16efSeric **
1310cd16efSeric **	Without trying to add any functionality to speak of, this
1410cd16efSeric **	program tries to make SCCS a little more accessible to human
1510cd16efSeric **	types.  The main thing it does is automatically put the
1610cd16efSeric **	string "SCCS/s." on the front of names.  Also, it has a
1710cd16efSeric **	couple of things that are designed to shorten frequent
1810cd16efSeric **	combinations, e.g., "delget" which expands to a "delta"
1910cd16efSeric **	and a "get".
2010cd16efSeric **
2110cd16efSeric **	This program can also function as a setuid front end.
2210cd16efSeric **	To do this, you should copy the source, renaming it to
2310cd16efSeric **	whatever you want, e.g., "syssccs".  Change any defaults
2410cd16efSeric **	in the program (e.g., syssccs might default -d to
2510cd16efSeric **	"/usr/src/sys").  Then recompile and put the result
2610cd16efSeric **	as setuid to whomever you want.  In this mode, sccs
2710cd16efSeric **	knows to not run setuid for certain programs in order
2810cd16efSeric **	to preserve security, and so forth.
2910cd16efSeric **
3010cd16efSeric **	Usage:
3110cd16efSeric **		sccs [flags] command [args]
3210cd16efSeric **
3310cd16efSeric **	Flags:
3410cd16efSeric **		-d<dir>		<dir> represents a directory to search
3510cd16efSeric **				out of.  It should be a full pathname
3610cd16efSeric **				for general usage.  E.g., if <dir> is
3710cd16efSeric **				"/usr/src/sys", then a reference to the
3810cd16efSeric **				file "dev/bio.c" becomes a reference to
3910cd16efSeric **				"/usr/src/sys/dev/bio.c".
4010cd16efSeric **		-p<path>	prepends <path> to the final component
4110cd16efSeric **				of the pathname.  By default, this is
4210cd16efSeric **				"SCCS".  For example, in the -d example
4310cd16efSeric **				above, the path then gets modified to
4410cd16efSeric **				"/usr/src/sys/dev/SCCS/s.bio.c".  In
4510cd16efSeric **				more common usage (without the -d flag),
4610cd16efSeric **				"prog.c" would get modified to
4710cd16efSeric **				"SCCS/s.prog.c".  In both cases, the
4810cd16efSeric **				"s." gets automatically prepended.
4910cd16efSeric **		-r		run as the real user.
5010cd16efSeric **
5110cd16efSeric **	Commands:
5210cd16efSeric **		admin,
5310cd16efSeric **		get,
5410cd16efSeric **		delta,
5510cd16efSeric **		rmdel,
5610cd16efSeric **		chghist,
5710cd16efSeric **		etc.		Straight out of SCCS; only difference
5810cd16efSeric **				is that pathnames get modified as
5910cd16efSeric **				described above.
6010cd16efSeric **		edit		Macro for "get -e".
6110cd16efSeric **		unedit		Removes a file being edited, knowing
6210cd16efSeric **				about p-files, etc.
6310cd16efSeric **		delget		Macro for "delta" followed by "get".
6410cd16efSeric **		deledit		Macro for "delta" followed by "get -e".
6510cd16efSeric **		info		Tell what files being edited.
6610cd16efSeric **		clean		Remove all files that can be
6710cd16efSeric **				regenerated from SCCS files.
68095915d3Seric **		check		Like info, but return exit status, for
6910cd16efSeric **				use in makefiles.
7010cd16efSeric **		fix		Remove a top delta & reedit, but save
7110cd16efSeric **				the previous changes in that delta.
7210cd16efSeric **
7310cd16efSeric **	Compilation Flags:
7410cd16efSeric **		UIDUSER -- determine who the user is by looking at the
7510cd16efSeric **			uid rather than the login name -- for machines
7610cd16efSeric **			where SCCS gets the user in this way.
777abec564Seric **		SCCSDIR -- if defined, forces the -d flag to take on
78095915d3Seric **			this value.  This is so that the setuid
79095915d3Seric **			aspects of this program cannot be abused.
807abec564Seric **			This flag also disables the -p flag.
817abec564Seric **		SCCSPATH -- the default for the -p flag.
8210cd16efSeric **
8310cd16efSeric **	Compilation Instructions:
8410cd16efSeric **		cc -O -n -s sccs.c
8510cd16efSeric **
8610cd16efSeric **	Author:
8710cd16efSeric **		Eric Allman, UCB/INGRES
887abec564Seric **		Copyright 1980 Regents of the University of California
8910cd16efSeric */
9010cd16efSeric 
91*78d41c6aSeric static char SccsId[] = "@(#)sccs.c	1.36 10/15/80";
920faf63f9Seric 
937abec564Seric /*******************  Configuration Information  ********************/
947abec564Seric 
9510cd16efSeric # ifdef CSVAX
9610cd16efSeric # define UIDUSER
977abec564Seric # define PROGPATH(name)	"/usr/local/name"
987abec564Seric # endif CSVAX
9910cd16efSeric 
1000faf63f9Seric # define SCCSPATH	"SCCS"	/* pathname in which to find s-files */
1017abec564Seric /* put #define SCCSDIR here */
1027abec564Seric 
1037abec564Seric char	MyName[] = "sccs";	/* name used in messages */
1047abec564Seric 
1050faf63f9Seric # ifndef PROGPATH
1060faf63f9Seric # define PROGPATH(name)	"/usr/sccs/name"	/* place to find binaries */
1070faf63f9Seric # endif PROGPATH
1080faf63f9Seric 
1097abec564Seric /****************  End of Configuration Information  ****************/
1100faf63f9Seric 
111c4432be4Seric # define bitset(bit, word)	((bit) & (word))
112c4432be4Seric 
113c4432be4Seric typedef char	bool;
1147de81dc7Seric # define TRUE	1
1157de81dc7Seric # define FALSE	0
116d02a4f42Seric 
11710cd16efSeric # ifdef UIDUSER
11810cd16efSeric # include <pwd.h>
11910cd16efSeric # endif UIDUSER
12010cd16efSeric 
121adf8f7d4Seric struct sccsprog
122adf8f7d4Seric {
123adf8f7d4Seric 	char	*sccsname;	/* name of SCCS routine */
1247de81dc7Seric 	short	sccsoper;	/* opcode, see below */
1257de81dc7Seric 	short	sccsflags;	/* flags, see below */
126108d6082Seric 	char	*sccsklets;	/* valid key-letters on macros */
127adf8f7d4Seric 	char	*sccspath;	/* pathname of binary implementing */
128adf8f7d4Seric };
129adf8f7d4Seric 
1307de81dc7Seric /* values for sccsoper */
1317de81dc7Seric # define PROG		0	/* call a program */
1321777fbcbSeric # define CMACRO		1	/* command substitution macro */
133172147faSeric # define FIX		2	/* fix a delta */
134b2538d76Seric # define CLEAN		3	/* clean out recreatable files */
135e39a5722Seric # define UNEDIT		4	/* unedit a file */
136419708b0Seric # define SHELL		5	/* call a shell file (like PROG) */
137*78d41c6aSeric # define DIFFS		6	/* diff between sccs & file out */
1387de81dc7Seric 
139c4432be4Seric /* bits for sccsflags */
1407de81dc7Seric # define NO_SDOT	0001	/* no s. on front of args */
1417de81dc7Seric # define REALUSER	0002	/* protected (e.g., admin) */
142adf8f7d4Seric 
14348ff0e7eSeric /* modes for the "clean", "info", "check" ops */
14448ff0e7eSeric # define CLEANC		0	/* clean command */
14548ff0e7eSeric # define INFOC		1	/* info command */
14648ff0e7eSeric # define CHECKC		2	/* check command */
14748ff0e7eSeric 
1480faf63f9Seric /*
1490faf63f9Seric **  Description of commands known to this program.
1500faf63f9Seric **	First argument puts the command into a class.  Second arg is
1510faf63f9Seric **	info regarding treatment of this command.  Third arg is a
1520faf63f9Seric **	list of flags this command accepts from macros, etc.  Fourth
1530faf63f9Seric **	arg is the pathname of the implementing program, or the
1540faf63f9Seric **	macro definition, or the arg to a sub-algorithm.
1550faf63f9Seric */
156b5d4f080Seric 
157adf8f7d4Seric struct sccsprog SccsProg[] =
158adf8f7d4Seric {
159108d6082Seric 	"admin",	PROG,	REALUSER,	"",		PROGPATH(admin),
160108d6082Seric 	"chghist",	PROG,	0,		"",		PROGPATH(rmdel),
161108d6082Seric 	"comb",		PROG,	0,		"",		PROGPATH(comb),
162108d6082Seric 	"delta",	PROG,	0,		"mysrp",	PROGPATH(delta),
163c952b8bfSeric 	"get",		PROG,	0,		"ixbeskcl",	PROGPATH(get),
164108d6082Seric 	"help",		PROG,	NO_SDOT,	"",		PROGPATH(help),
165108d6082Seric 	"prt",		PROG,	0,		"",		PROGPATH(prt),
166108d6082Seric 	"rmdel",	PROG,	REALUSER,	"",		PROGPATH(rmdel),
167108d6082Seric 	"what",		PROG,	NO_SDOT,	"",		PROGPATH(what),
168419708b0Seric 	"sccsdiff",	SHELL,	REALUSER,	"",		PROGPATH(sccsdiff),
169c952b8bfSeric 	"edit",		CMACRO,	NO_SDOT,	"ixbscl",	"get -e",
170108d6082Seric 	"delget",	CMACRO,	NO_SDOT,	"",		"delta/get -t",
171108d6082Seric 	"deledit",	CMACRO,	NO_SDOT,	"",		"delta/get -e -t",
172108d6082Seric 	"fix",		FIX,	NO_SDOT,	"",		NULL,
173108d6082Seric 	"clean",	CLEAN,	REALUSER,	"",		(char *) CLEANC,
174108d6082Seric 	"info",		CLEAN,	REALUSER,	"",		(char *) INFOC,
175108d6082Seric 	"check",	CLEAN,	REALUSER,	"",		(char *) CHECKC,
176108d6082Seric 	"unedit",	UNEDIT,	NO_SDOT,	"",		NULL,
177*78d41c6aSeric 	"diffs",	DIFFS,	NO_SDOT|REALUSER, "",		NULL,
178108d6082Seric 	NULL,		-1,	0,		"",		NULL
179adf8f7d4Seric };
180adf8f7d4Seric 
1810faf63f9Seric /* one line from a p-file */
182e39a5722Seric struct pfile
183e39a5722Seric {
184e39a5722Seric 	char	*p_osid;	/* old SID */
185e39a5722Seric 	char	*p_nsid;	/* new SID */
186e39a5722Seric 	char	*p_user;	/* user who did edit */
187e39a5722Seric 	char	*p_date;	/* date of get */
188e39a5722Seric 	char	*p_time;	/* time of get */
189e39a5722Seric };
190e39a5722Seric 
1917abec564Seric char	*SccsPath = SCCSPATH;	/* pathname of SCCS files */
1927abec564Seric # ifdef SCCSDIR
1937abec564Seric char	*SccsDir = SCCSDIR;	/* directory to begin search from */
194095915d3Seric # else
1957abec564Seric char	*SccsDir = "";
196095915d3Seric # endif
197*78d41c6aSeric int	OutFile = -1;		/* override output file for commands */
198c4432be4Seric bool	RealUser;		/* if set, running as real user */
199f0cc3246Seric # ifdef DEBUG
200f0cc3246Seric bool	Debug;			/* turn on tracing */
201f0cc3246Seric # endif
2020faf63f9Seric 
203adf8f7d4Seric main(argc, argv)
204adf8f7d4Seric 	int argc;
205adf8f7d4Seric 	char **argv;
206adf8f7d4Seric {
207adf8f7d4Seric 	register char *p;
208b1ed8a43Seric 	extern struct sccsprog *lookup();
20941290c52Seric 	register int i;
210adf8f7d4Seric 
211adf8f7d4Seric 	/*
212adf8f7d4Seric 	**  Detect and decode flags intended for this program.
213adf8f7d4Seric 	*/
214adf8f7d4Seric 
2157de81dc7Seric 	if (argc < 2)
216adf8f7d4Seric 	{
217095915d3Seric 		fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName);
2187de81dc7Seric 		exit(EX_USAGE);
2197de81dc7Seric 	}
2207de81dc7Seric 	argv[argc] = NULL;
2217de81dc7Seric 
222b1ed8a43Seric 	if (lookup(argv[0]) == NULL)
223b1ed8a43Seric 	{
2247de81dc7Seric 		while ((p = *++argv) != NULL)
2257de81dc7Seric 		{
226adf8f7d4Seric 			if (*p != '-')
227adf8f7d4Seric 				break;
228adf8f7d4Seric 			switch (*++p)
229adf8f7d4Seric 			{
230adf8f7d4Seric 			  case 'r':		/* run as real user */
231adf8f7d4Seric 				setuid(getuid());
232c4432be4Seric 				RealUser++;
233adf8f7d4Seric 				break;
234adf8f7d4Seric 
2357abec564Seric # ifndef SCCSDIR
236adf8f7d4Seric 			  case 'p':		/* path of sccs files */
237adf8f7d4Seric 				SccsPath = ++p;
238adf8f7d4Seric 				break;
239adf8f7d4Seric 
2400a8b9ab0Seric 			  case 'd':		/* directory to search from */
2410a8b9ab0Seric 				SccsDir = ++p;
2420a8b9ab0Seric 				break;
243095915d3Seric # endif
2440a8b9ab0Seric 
245f0cc3246Seric # ifdef DEBUG
246f0cc3246Seric 			  case 'T':		/* trace */
247f0cc3246Seric 				Debug++;
248f0cc3246Seric 				break;
249f0cc3246Seric # endif
250f0cc3246Seric 
251adf8f7d4Seric 			  default:
252095915d3Seric 				usrerr("unknown option -%s", p);
253adf8f7d4Seric 				break;
254adf8f7d4Seric 			}
255adf8f7d4Seric 		}
2565cabffd9Seric 		if (SccsPath[0] == '\0')
2575cabffd9Seric 			SccsPath = ".";
258b1ed8a43Seric 	}
259adf8f7d4Seric 
260108d6082Seric 	i = command(argv, FALSE, FALSE, "");
26141290c52Seric 	exit(i);
2627de81dc7Seric }
2630faf63f9Seric 
2640faf63f9Seric /*
26541290c52Seric **  COMMAND -- look up and perform a command
26641290c52Seric **
26741290c52Seric **	This routine is the guts of this program.  Given an
26841290c52Seric **	argument vector, it looks up the "command" (argv[0])
26941290c52Seric **	in the configuration table and does the necessary stuff.
27041290c52Seric **
27141290c52Seric **	Parameters:
27241290c52Seric **		argv -- an argument vector to process.
27341290c52Seric **		forkflag -- if set, fork before executing the command.
274108d6082Seric **		editflag -- if set, only include flags listed in the
275108d6082Seric **			sccsklets field of the command descriptor.
276108d6082Seric **		arg0 -- a space-seperated list of arguments to insert
277108d6082Seric **			before argv.
27841290c52Seric **
27941290c52Seric **	Returns:
28041290c52Seric **		zero -- command executed ok.
28141290c52Seric **		else -- error status.
28241290c52Seric **
28341290c52Seric **	Side Effects:
28441290c52Seric **		none.
28541290c52Seric */
2867de81dc7Seric 
287108d6082Seric command(argv, forkflag, editflag, arg0)
2887de81dc7Seric 	char **argv;
2891777fbcbSeric 	bool forkflag;
290108d6082Seric 	bool editflag;
291108d6082Seric 	char *arg0;
2927de81dc7Seric {
2937de81dc7Seric 	register struct sccsprog *cmd;
2947de81dc7Seric 	register char *p;
2951777fbcbSeric 	char buf[40];
296b1ed8a43Seric 	extern struct sccsprog *lookup();
297108d6082Seric 	char *nav[1000];
298108d6082Seric 	char **np;
299419708b0Seric 	register char **ap;
300d51cd7e5Seric 	register int i;
301419708b0Seric 	register char *q;
302d51cd7e5Seric 	extern bool unedit();
30341290c52Seric 	int rval = 0;
304108d6082Seric 	extern char *index();
305108d6082Seric 	extern char *makefile();
306f0cc3246Seric 
307f0cc3246Seric # ifdef DEBUG
308f0cc3246Seric 	if (Debug)
309f0cc3246Seric 	{
310108d6082Seric 		printf("command:\n\t\"%s\"\n", arg0);
311108d6082Seric 		for (np = argv; *np != NULL; np++)
312108d6082Seric 			printf("\t\"%s\"\n", *np);
313f0cc3246Seric 	}
314f0cc3246Seric # endif
315c4432be4Seric 
316c4432be4Seric 	/*
317108d6082Seric 	**  Copy arguments.
318108d6082Seric 	**	Phase one -- from arg0 & if necessary argv[0].
319adf8f7d4Seric 	*/
320adf8f7d4Seric 
321419708b0Seric 	np = ap = &nav[1];
322108d6082Seric 	for (p = arg0, q = buf; *p != '\0' && *p != '/'; )
323108d6082Seric 	{
324108d6082Seric 		*np++ = q;
325108d6082Seric 		while (*p == ' ')
326108d6082Seric 			p++;
327108d6082Seric 		while (*p != ' ' && *p != '\0' && *p != '/')
328108d6082Seric 			*q++ = *p++;
329108d6082Seric 		*q++ = '\0';
330108d6082Seric 	}
331108d6082Seric 	*np = NULL;
332419708b0Seric 	if (*ap == NULL)
333108d6082Seric 		*np++ = *argv++;
334108d6082Seric 
335108d6082Seric 	/*
336108d6082Seric 	**  Look up command.
337419708b0Seric 	**	At this point, *ap is the command name.
338108d6082Seric 	*/
339108d6082Seric 
340419708b0Seric 	cmd = lookup(*ap);
341b1ed8a43Seric 	if (cmd == NULL)
342adf8f7d4Seric 	{
343419708b0Seric 		usrerr("Unknown command \"%s\"", *ap);
34441290c52Seric 		return (EX_USAGE);
345adf8f7d4Seric 	}
346adf8f7d4Seric 
347adf8f7d4Seric 	/*
348108d6082Seric 	**  Copy remaining arguments doing editing as appropriate.
349108d6082Seric 	*/
350108d6082Seric 
351108d6082Seric 	for (; *argv != NULL; argv++)
352108d6082Seric 	{
353108d6082Seric 		p = *argv;
354108d6082Seric 		if (*p == '-')
355108d6082Seric 		{
356108d6082Seric 			if (p[1] == '\0' || !editflag || cmd->sccsklets == NULL ||
357108d6082Seric 			    index(cmd->sccsklets, p[1]) != NULL)
358108d6082Seric 				*np++ = p;
359108d6082Seric 		}
360108d6082Seric 		else
361108d6082Seric 		{
362108d6082Seric 			if (!bitset(NO_SDOT, cmd->sccsflags))
363108d6082Seric 				p = makefile(p);
364108d6082Seric 			if (p != NULL)
365108d6082Seric 				*np++ = p;
366108d6082Seric 		}
367108d6082Seric 	}
368108d6082Seric 	*np = NULL;
369108d6082Seric 
370108d6082Seric 	/*
3717de81dc7Seric 	**  Interpret operation associated with this command.
372c4432be4Seric 	*/
373c4432be4Seric 
3747de81dc7Seric 	switch (cmd->sccsoper)
3757de81dc7Seric 	{
376419708b0Seric 	  case SHELL:		/* call a shell file */
377419708b0Seric 		*ap = cmd->sccspath;
378419708b0Seric 		*--ap = "sh";
379419708b0Seric 		rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag);
380419708b0Seric 		break;
381419708b0Seric 
3827de81dc7Seric 	  case PROG:		/* call an sccs prog */
383419708b0Seric 		rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag);
3841777fbcbSeric 		break;
3851777fbcbSeric 
3861777fbcbSeric 	  case CMACRO:		/* command macro */
3871777fbcbSeric 		for (p = cmd->sccspath; *p != '\0'; p++)
3881777fbcbSeric 		{
389108d6082Seric 			q = p;
390108d6082Seric 			while (*p != '\0' && *p != '/')
391108d6082Seric 				p++;
392419708b0Seric 			rval = command(&ap[1], *p != '\0', TRUE, q);
39341290c52Seric 			if (rval != 0)
39441290c52Seric 				break;
3951777fbcbSeric 		}
39641290c52Seric 		break;
3977de81dc7Seric 
398172147faSeric 	  case FIX:		/* fix a delta */
399419708b0Seric 		if (strncmp(ap[1], "-r", 2) != 0)
400172147faSeric 		{
401095915d3Seric 			usrerr("-r flag needed for fix command");
40241290c52Seric 			rval = EX_USAGE;
403172147faSeric 			break;
404172147faSeric 		}
405419708b0Seric 		rval = command(&ap[1], TRUE, TRUE, "get -k");
40641290c52Seric 		if (rval == 0)
407419708b0Seric 			rval = command(&ap[1], TRUE, TRUE, "rmdel");
40841290c52Seric 		if (rval == 0)
409419708b0Seric 			rval = command(&ap[2], FALSE, TRUE, "get -e -g");
41041290c52Seric 		break;
411172147faSeric 
412b2538d76Seric 	  case CLEAN:
41341290c52Seric 		rval = clean((int) cmd->sccspath);
414b2538d76Seric 		break;
415b2538d76Seric 
416e39a5722Seric 	  case UNEDIT:
417419708b0Seric 		for (argv = np = &ap[1]; *argv != NULL; argv++)
418d51cd7e5Seric 		{
419108d6082Seric 			if (unedit(*argv))
420108d6082Seric 				*np++ = *argv;
421d51cd7e5Seric 		}
422108d6082Seric 		*np = NULL;
423d51cd7e5Seric 		if (i > 0)
424419708b0Seric 			rval = command(&ap[1], FALSE, FALSE, "get");
425e39a5722Seric 		break;
426e39a5722Seric 
427*78d41c6aSeric 	  case DIFFS:		/* diff between s-file & edit file */
428*78d41c6aSeric 		/* find the end of the flag arguments */
429*78d41c6aSeric 		for (np = &ap[1]; *np != NULL && **np == '-'; np++)
430*78d41c6aSeric 			continue;
431*78d41c6aSeric 		argv = np;
432*78d41c6aSeric 
433*78d41c6aSeric 		/* for each file, do the diff */
434*78d41c6aSeric 		while (*np != NULL)
435*78d41c6aSeric 		{
436*78d41c6aSeric 			*argv = *np++;
437*78d41c6aSeric 			p = *np;
438*78d41c6aSeric 			*np = NULL;
439*78d41c6aSeric 			i = dodiff(ap, *argv);
440*78d41c6aSeric 			if (rval == 0)
441*78d41c6aSeric 				rval = i;
442*78d41c6aSeric 			*np = p;
443*78d41c6aSeric 		}
444*78d41c6aSeric 		break;
445*78d41c6aSeric 
4467de81dc7Seric 	  default:
447095915d3Seric 		syserr("oper %d", cmd->sccsoper);
4487de81dc7Seric 		exit(EX_SOFTWARE);
4497de81dc7Seric 	}
45041290c52Seric # ifdef DEBUG
45141290c52Seric 	if (Debug)
45241290c52Seric 		printf("command: rval=%d\n", rval);
45341290c52Seric # endif
45441290c52Seric 	return (rval);
4557de81dc7Seric }
4560faf63f9Seric 
4570faf63f9Seric /*
458b1ed8a43Seric **  LOOKUP -- look up an SCCS command name.
459b1ed8a43Seric **
460b1ed8a43Seric **	Parameters:
461b1ed8a43Seric **		name -- the name of the command to look up.
462b1ed8a43Seric **
463b1ed8a43Seric **	Returns:
464b1ed8a43Seric **		ptr to command descriptor for this command.
465b1ed8a43Seric **		NULL if no such entry.
466b1ed8a43Seric **
467b1ed8a43Seric **	Side Effects:
468b1ed8a43Seric **		none.
469b1ed8a43Seric */
470b1ed8a43Seric 
471b1ed8a43Seric struct sccsprog *
472b1ed8a43Seric lookup(name)
473b1ed8a43Seric 	char *name;
474b1ed8a43Seric {
475b1ed8a43Seric 	register struct sccsprog *cmd;
476b1ed8a43Seric 
477b1ed8a43Seric 	for (cmd = SccsProg; cmd->sccsname != NULL; cmd++)
478b1ed8a43Seric 	{
479b1ed8a43Seric 		if (strcmp(cmd->sccsname, name) == 0)
480b1ed8a43Seric 			return (cmd);
481b1ed8a43Seric 	}
482b1ed8a43Seric 	return (NULL);
483b1ed8a43Seric }
4840faf63f9Seric 
4850faf63f9Seric /*
48641290c52Seric **  CALLPROG -- call a program
48741290c52Seric **
488108d6082Seric **	Used to call the SCCS programs.
48941290c52Seric **
49041290c52Seric **	Parameters:
49141290c52Seric **		progpath -- pathname of the program to call.
49241290c52Seric **		flags -- status flags from the command descriptors.
49341290c52Seric **		argv -- an argument vector to pass to the program.
49441290c52Seric **		forkflag -- if true, fork before calling, else just
49541290c52Seric **			exec.
49641290c52Seric **
49741290c52Seric **	Returns:
49841290c52Seric **		The exit status of the program.
49941290c52Seric **		Nothing if forkflag == FALSE.
50041290c52Seric **
50141290c52Seric **	Side Effects:
50241290c52Seric **		Can exit if forkflag == FALSE.
50341290c52Seric */
504172147faSeric 
5057de81dc7Seric callprog(progpath, flags, argv, forkflag)
5067de81dc7Seric 	char *progpath;
5077de81dc7Seric 	short flags;
5087de81dc7Seric 	char **argv;
5097de81dc7Seric 	bool forkflag;
5107de81dc7Seric {
5117de81dc7Seric 	register int i;
5121777fbcbSeric 	auto int st;
513108d6082Seric 
514108d6082Seric # ifdef DEBUG
515108d6082Seric 	if (Debug)
516108d6082Seric 	{
517108d6082Seric 		printf("callprog:\n");
518108d6082Seric 		for (i = 0; argv[i] != NULL; i++)
519108d6082Seric 			printf("\t\"%s\"\n", argv[i]);
520108d6082Seric 	}
521108d6082Seric # endif
5227de81dc7Seric 
5237de81dc7Seric 	if (*argv == NULL)
5247de81dc7Seric 		return (-1);
525c4432be4Seric 
526c4432be4Seric 	/*
527172147faSeric 	**  Fork if appropriate.
528adf8f7d4Seric 	*/
529adf8f7d4Seric 
5307de81dc7Seric 	if (forkflag)
5317de81dc7Seric 	{
532f0cc3246Seric # ifdef DEBUG
533f0cc3246Seric 		if (Debug)
534f0cc3246Seric 			printf("Forking\n");
535f0cc3246Seric # endif
5367de81dc7Seric 		i = fork();
5377de81dc7Seric 		if (i < 0)
5387de81dc7Seric 		{
539095915d3Seric 			syserr("cannot fork");
5407de81dc7Seric 			exit(EX_OSERR);
5417de81dc7Seric 		}
5427de81dc7Seric 		else if (i > 0)
5431777fbcbSeric 		{
5441777fbcbSeric 			wait(&st);
54541290c52Seric 			if ((st & 0377) == 0)
54641290c52Seric 				st = (st >> 8) & 0377;
547*78d41c6aSeric 			if (OutFile >= 0)
548*78d41c6aSeric 			{
549*78d41c6aSeric 				close(OutFile);
550*78d41c6aSeric 				OutFile = -1;
551*78d41c6aSeric 			}
5521777fbcbSeric 			return (st);
5531777fbcbSeric 		}
5547de81dc7Seric 	}
555*78d41c6aSeric 	else if (OutFile >= 0)
556*78d41c6aSeric 	{
557*78d41c6aSeric 		syserr("callprog: setting stdout w/o forking");
558*78d41c6aSeric 		exit(EX_SOFTWARE);
559*78d41c6aSeric 	}
5607de81dc7Seric 
561*78d41c6aSeric 	/* set protection as appropriate */
5627de81dc7Seric 	if (bitset(REALUSER, flags))
5637de81dc7Seric 		setuid(getuid());
5647de81dc7Seric 
565*78d41c6aSeric 	/* change standard input & output if needed */
566*78d41c6aSeric 	if (OutFile >= 0)
567*78d41c6aSeric 	{
568*78d41c6aSeric 		close(1);
569*78d41c6aSeric 		dup(OutFile);
570*78d41c6aSeric 		close(OutFile);
571*78d41c6aSeric 	}
5727de81dc7Seric 
573*78d41c6aSeric 	/* call real SCCS program */
574172147faSeric 	execv(progpath, argv);
575095915d3Seric 	syserr("cannot execute %s", progpath);
576adf8f7d4Seric 	exit(EX_UNAVAILABLE);
577adf8f7d4Seric }
5780faf63f9Seric 
5790faf63f9Seric /*
580cdc1aa65Seric **  MAKEFILE -- make filename of SCCS file
581cdc1aa65Seric **
582cdc1aa65Seric **	If the name passed is already the name of an SCCS file,
583cdc1aa65Seric **	just return it.  Otherwise, munge the name into the name
584cdc1aa65Seric **	of the actual SCCS file.
585cdc1aa65Seric **
586cdc1aa65Seric **	There are cases when it is not clear what you want to
587cdc1aa65Seric **	do.  For example, if SccsPath is an absolute pathname
588cdc1aa65Seric **	and the name given is also an absolute pathname, we go
589cdc1aa65Seric **	for SccsPath (& only use the last component of the name
590cdc1aa65Seric **	passed) -- this is important for security reasons (if
591cdc1aa65Seric **	sccs is being used as a setuid front end), but not
592cdc1aa65Seric **	particularly intuitive.
593cdc1aa65Seric **
594cdc1aa65Seric **	Parameters:
595cdc1aa65Seric **		name -- the file name to be munged.
596cdc1aa65Seric **
597cdc1aa65Seric **	Returns:
598cdc1aa65Seric **		The pathname of the sccs file.
599cdc1aa65Seric **		NULL on error.
600cdc1aa65Seric **
601cdc1aa65Seric **	Side Effects:
602cdc1aa65Seric **		none.
603cdc1aa65Seric */
604adf8f7d4Seric 
605adf8f7d4Seric char *
606adf8f7d4Seric makefile(name)
607adf8f7d4Seric 	char *name;
608adf8f7d4Seric {
609adf8f7d4Seric 	register char *p;
610adf8f7d4Seric 	register char c;
611adf8f7d4Seric 	char buf[512];
6120a8b9ab0Seric 	struct stat stbuf;
613adf8f7d4Seric 	extern char *malloc();
614cdc1aa65Seric 	extern char *rindex();
6150a8b9ab0Seric 	extern bool safepath();
6167d70f5d8Seric 	extern bool isdir();
6177d70f5d8Seric 	register char *q;
618cdc1aa65Seric 
619cdc1aa65Seric 	p = rindex(name, '/');
620cdc1aa65Seric 	if (p == NULL)
621cdc1aa65Seric 		p = name;
622cdc1aa65Seric 	else
623cdc1aa65Seric 		p++;
624adf8f7d4Seric 
625adf8f7d4Seric 	/*
6260a8b9ab0Seric 	**  Check to see that the path is "safe", i.e., that we
6270a8b9ab0Seric 	**  are not letting some nasty person use the setuid part
6280a8b9ab0Seric 	**  of this program to look at or munge some presumably
6290a8b9ab0Seric 	**  hidden files.
630adf8f7d4Seric 	*/
631adf8f7d4Seric 
6320a8b9ab0Seric 	if (SccsDir[0] == '/' && !safepath(name))
6330a8b9ab0Seric 		return (NULL);
634cdc1aa65Seric 
635cdc1aa65Seric 	/*
6360a8b9ab0Seric 	**  Create the base pathname.
637cdc1aa65Seric 	*/
638cdc1aa65Seric 
6390a8b9ab0Seric 	if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0)
640adf8f7d4Seric 	{
6410a8b9ab0Seric 		strcpy(buf, SccsDir);
642cdc1aa65Seric 		strcat(buf, "/");
643cdc1aa65Seric 	}
644cdc1aa65Seric 	else
645cdc1aa65Seric 		strcpy(buf, "");
646cdc1aa65Seric 	strncat(buf, name, p - name);
6477d70f5d8Seric 	q = &buf[strlen(buf)];
6487d70f5d8Seric 	strcpy(q, p);
6497d70f5d8Seric 	if (strncmp(p, "s.", 2) != 0 && !isdir(buf))
6507d70f5d8Seric 	{
6510a8b9ab0Seric 		strcpy(q, SccsPath);
6520a8b9ab0Seric 		strcat(buf, "/s.");
653cdc1aa65Seric 		strcat(buf, p);
654cdc1aa65Seric 	}
655adf8f7d4Seric 
6560a8b9ab0Seric 	if (strcmp(buf, name) == 0)
6570a8b9ab0Seric 		p = name;
6580a8b9ab0Seric 	else
6590a8b9ab0Seric 	{
660adf8f7d4Seric 		p = malloc(strlen(buf) + 1);
661adf8f7d4Seric 		if (p == NULL)
662adf8f7d4Seric 		{
663adf8f7d4Seric 			perror("Sccs: no mem");
664adf8f7d4Seric 			exit(EX_OSERR);
665adf8f7d4Seric 		}
666adf8f7d4Seric 		strcpy(p, buf);
6670a8b9ab0Seric 	}
668adf8f7d4Seric 	return (p);
669adf8f7d4Seric }
6700faf63f9Seric 
6710faf63f9Seric /*
6727d70f5d8Seric **  ISDIR -- return true if the argument is a directory.
6737d70f5d8Seric **
6747d70f5d8Seric **	Parameters:
6757d70f5d8Seric **		name -- the pathname of the file to check.
6767d70f5d8Seric **
6777d70f5d8Seric **	Returns:
6787d70f5d8Seric **		TRUE if 'name' is a directory, FALSE otherwise.
6797d70f5d8Seric **
6807d70f5d8Seric **	Side Effects:
6817d70f5d8Seric **		none.
6827d70f5d8Seric */
6837d70f5d8Seric 
6847d70f5d8Seric bool
6857d70f5d8Seric isdir(name)
6867d70f5d8Seric 	char *name;
6877d70f5d8Seric {
6887d70f5d8Seric 	struct stat stbuf;
6897d70f5d8Seric 
6907d70f5d8Seric 	return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR);
6917d70f5d8Seric }
6920faf63f9Seric 
6930faf63f9Seric /*
694cdc1aa65Seric **  SAFEPATH -- determine whether a pathname is "safe"
695cdc1aa65Seric **
696cdc1aa65Seric **	"Safe" pathnames only allow you to get deeper into the
697cdc1aa65Seric **	directory structure, i.e., full pathnames and ".." are
698cdc1aa65Seric **	not allowed.
699cdc1aa65Seric **
700cdc1aa65Seric **	Parameters:
701cdc1aa65Seric **		p -- the name to check.
702cdc1aa65Seric **
703cdc1aa65Seric **	Returns:
704cdc1aa65Seric **		TRUE -- if the path is safe.
705cdc1aa65Seric **		FALSE -- if the path is not safe.
706cdc1aa65Seric **
707cdc1aa65Seric **	Side Effects:
708cdc1aa65Seric **		Prints a message if the path is not safe.
709cdc1aa65Seric */
710cdc1aa65Seric 
711cdc1aa65Seric bool
712cdc1aa65Seric safepath(p)
713cdc1aa65Seric 	register char *p;
714cdc1aa65Seric {
715cdc1aa65Seric 	extern char *index();
716cdc1aa65Seric 
717cdc1aa65Seric 	if (*p != '/')
718cdc1aa65Seric 	{
719cdc1aa65Seric 		while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0)
720cdc1aa65Seric 		{
721cdc1aa65Seric 			p = index(p, '/');
722cdc1aa65Seric 			if (p == NULL)
723cdc1aa65Seric 				return (TRUE);
724cdc1aa65Seric 			p++;
725cdc1aa65Seric 		}
726cdc1aa65Seric 	}
727cdc1aa65Seric 
728cdc1aa65Seric 	printf("You may not use full pathnames or \"..\"\n");
729cdc1aa65Seric 	return (FALSE);
730cdc1aa65Seric }
7310faf63f9Seric 
7320faf63f9Seric /*
733b2538d76Seric **  CLEAN -- clean out recreatable files
734b2538d76Seric **
735b2538d76Seric **	Any file for which an "s." file exists but no "p." file
736b2538d76Seric **	exists in the current directory is purged.
737b2538d76Seric **
738b2538d76Seric **	Parameters:
73948ff0e7eSeric **		tells whether this came from a "clean", "info", or
74048ff0e7eSeric **		"check" command.
741b2538d76Seric **
742b2538d76Seric **	Returns:
743b2538d76Seric **		none.
744b2538d76Seric **
745b2538d76Seric **	Side Effects:
74648ff0e7eSeric **		Removes files in the current directory.
74748ff0e7eSeric **		Prints information regarding files being edited.
74848ff0e7eSeric **		Exits if a "check" command.
749b2538d76Seric */
750b2538d76Seric 
75148ff0e7eSeric clean(mode)
75248ff0e7eSeric 	int mode;
753b2538d76Seric {
754b2538d76Seric 	struct direct dir;
755b2538d76Seric 	struct stat stbuf;
756b2538d76Seric 	char buf[100];
757bf9c1e66Seric 	char pline[120];
7583a208c77Seric 	register FILE *dirfd;
7593a208c77Seric 	register char *basefile;
7602caac8fdSeric 	bool gotedit;
761bf9c1e66Seric 	FILE *pfp;
762b2538d76Seric 
763f77a08fbSeric 	strcpy(buf, SccsDir);
764f77a08fbSeric 	if (buf[0] != '\0')
765f77a08fbSeric 		strcat(buf, "/");
766f77a08fbSeric 	strcat(buf, SccsPath);
767f77a08fbSeric 	dirfd = fopen(buf, "r");
768b2538d76Seric 	if (dirfd == NULL)
769b2538d76Seric 	{
770f77a08fbSeric 		usrerr("cannot open %s", buf);
77141290c52Seric 		return (EX_NOINPUT);
772b2538d76Seric 	}
773b2538d76Seric 
774b2538d76Seric 	/*
775b2538d76Seric 	**  Scan the SCCS directory looking for s. files.
776b2538d76Seric 	*/
777b2538d76Seric 
7782caac8fdSeric 	gotedit = FALSE;
779b2538d76Seric 	while (fread(&dir, sizeof dir, 1, dirfd) != NULL)
780b2538d76Seric 	{
781c9c62fd0Seric 		if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0)
782b2538d76Seric 			continue;
783b2538d76Seric 
784b2538d76Seric 		/* got an s. file -- see if the p. file exists */
785f77a08fbSeric 		strcpy(buf, SccsDir);
786f77a08fbSeric 		if (buf[0] != '\0')
787f77a08fbSeric 			strcat(buf, "/");
788f77a08fbSeric 		strcat(buf, SccsPath);
789b2538d76Seric 		strcat(buf, "/p.");
7903a208c77Seric 		basefile = &buf[strlen(buf)];
791c9c62fd0Seric 		strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2);
792bf9c1e66Seric 		basefile[sizeof dir.d_name - 2] = '\0';
793bf9c1e66Seric 		pfp = fopen(buf, "r");
794bf9c1e66Seric 		if (pfp != NULL)
7953a208c77Seric 		{
796bf9c1e66Seric 			while (fgets(pline, sizeof pline, pfp) != NULL)
7972444cd3eSeric 				printf("%12s: being edited: %s", basefile, pline);
798bf9c1e66Seric 			fclose(pfp);
7992caac8fdSeric 			gotedit = TRUE;
800b2538d76Seric 			continue;
8013a208c77Seric 		}
802b2538d76Seric 
803b2538d76Seric 		/* the s. file exists and no p. file exists -- unlink the g-file */
80448ff0e7eSeric 		if (mode == CLEANC)
8053a208c77Seric 		{
806c9c62fd0Seric 			strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2);
8073a208c77Seric 			buf[sizeof dir.d_name - 2] = '\0';
808b2538d76Seric 			unlink(buf);
809b2538d76Seric 		}
8103a208c77Seric 	}
811b2538d76Seric 
812b2538d76Seric 	fclose(dirfd);
81348ff0e7eSeric 	if (!gotedit && mode == INFOC)
8142444cd3eSeric 		printf("Nothing being edited\n");
81548ff0e7eSeric 	if (mode == CHECKC)
81648ff0e7eSeric 		exit(gotedit);
81741290c52Seric 	return (EX_OK);
818b2538d76Seric }
8190faf63f9Seric 
8200faf63f9Seric /*
821e39a5722Seric **  UNEDIT -- unedit a file
822e39a5722Seric **
823e39a5722Seric **	Checks to see that the current user is actually editting
824e39a5722Seric **	the file and arranges that s/he is not editting it.
825e39a5722Seric **
826e39a5722Seric **	Parameters:
8272444cd3eSeric **		fn -- the name of the file to be unedited.
828e39a5722Seric **
829e39a5722Seric **	Returns:
830d51cd7e5Seric **		TRUE -- if the file was successfully unedited.
831d51cd7e5Seric **		FALSE -- if the file was not unedited for some
832d51cd7e5Seric **			reason.
833e39a5722Seric **
834e39a5722Seric **	Side Effects:
835e39a5722Seric **		fn is removed
836e39a5722Seric **		entries are removed from pfile.
837e39a5722Seric */
838e39a5722Seric 
839d51cd7e5Seric bool
840e39a5722Seric unedit(fn)
841e39a5722Seric 	char *fn;
842e39a5722Seric {
843e39a5722Seric 	register FILE *pfp;
844e39a5722Seric 	char *pfn;
845e39a5722Seric 	static char tfn[] = "/tmp/sccsXXXXX";
846e39a5722Seric 	FILE *tfp;
847e39a5722Seric 	register char *p;
848e39a5722Seric 	register char *q;
849e39a5722Seric 	bool delete = FALSE;
850e39a5722Seric 	bool others = FALSE;
851e39a5722Seric 	char *myname;
852e39a5722Seric 	extern char *getlogin();
853e39a5722Seric 	struct pfile *pent;
854e39a5722Seric 	extern struct pfile *getpfile();
855e39a5722Seric 	char buf[120];
856108d6082Seric 	extern char *makefile();
85710cd16efSeric # ifdef UIDUSER
85810cd16efSeric 	struct passwd *pw;
85910cd16efSeric 	extern struct passwd *getpwuid();
86010cd16efSeric # endif UIDUSER
861e39a5722Seric 
862e39a5722Seric 	/* make "s." filename & find the trailing component */
863e39a5722Seric 	pfn = makefile(fn);
864cdc1aa65Seric 	if (pfn == NULL)
865cdc1aa65Seric 		return (FALSE);
866cdc1aa65Seric 	q = rindex(pfn, '/');
867cdc1aa65Seric 	if (q == NULL)
868cdc1aa65Seric 		q = &pfn[-1];
869cdc1aa65Seric 	if (q[1] != 's' || q[2] != '.')
870e39a5722Seric 	{
871095915d3Seric 		usrerr("bad file name \"%s\"", fn);
872d51cd7e5Seric 		return (FALSE);
873e39a5722Seric 	}
874e39a5722Seric 
875e39a5722Seric 	/* turn "s." into "p." */
876e39a5722Seric 	*++q = 'p';
877e39a5722Seric 
878e39a5722Seric 	pfp = fopen(pfn, "r");
879e39a5722Seric 	if (pfp == NULL)
880e39a5722Seric 	{
8812444cd3eSeric 		printf("%12s: not being edited\n", fn);
882d51cd7e5Seric 		return (FALSE);
883e39a5722Seric 	}
884e39a5722Seric 
885e39a5722Seric 	/*
886e39a5722Seric 	**  Copy p-file to temp file, doing deletions as needed.
887e39a5722Seric 	*/
888e39a5722Seric 
889e39a5722Seric 	mktemp(tfn);
890e39a5722Seric 	tfp = fopen(tfn, "w");
891e39a5722Seric 	if (tfp == NULL)
892e39a5722Seric 	{
893095915d3Seric 		usrerr("cannot create \"%s\"", tfn);
894e39a5722Seric 		exit(EX_OSERR);
895e39a5722Seric 	}
896e39a5722Seric 
89710cd16efSeric # ifdef UIDUSER
89810cd16efSeric 	pw = getpwuid(getuid());
89910cd16efSeric 	if (pw == NULL)
90010cd16efSeric 	{
901095915d3Seric 		syserr("who are you? (uid=%d)", getuid());
90210cd16efSeric 		exit(EX_OSERR);
90310cd16efSeric 	}
90410cd16efSeric 	myname = pw->pw_name;
90510cd16efSeric # else
906e39a5722Seric 	myname = getlogin();
90710cd16efSeric # endif UIDUSER
908e39a5722Seric 	while ((pent = getpfile(pfp)) != NULL)
909e39a5722Seric 	{
910e39a5722Seric 		if (strcmp(pent->p_user, myname) == 0)
911e39a5722Seric 		{
912e39a5722Seric 			/* a match */
913e39a5722Seric 			delete++;
914e39a5722Seric 		}
915e39a5722Seric 		else
916e39a5722Seric 		{
917e39a5722Seric 			fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid,
918e39a5722Seric 			    pent->p_nsid, pent->p_user, pent->p_date,
919e39a5722Seric 			    pent->p_time);
920e39a5722Seric 			others++;
921e39a5722Seric 		}
922e39a5722Seric 	}
923e39a5722Seric 
924e39a5722Seric 	/* do final cleanup */
925e39a5722Seric 	if (others)
926e39a5722Seric 	{
927e39a5722Seric 		if (freopen(tfn, "r", tfp) == NULL)
928e39a5722Seric 		{
929095915d3Seric 			syserr("cannot reopen \"%s\"", tfn);
930e39a5722Seric 			exit(EX_OSERR);
931e39a5722Seric 		}
932e39a5722Seric 		if (freopen(pfn, "w", pfp) == NULL)
933e39a5722Seric 		{
934095915d3Seric 			usrerr("cannot create \"%s\"", pfn);
935d51cd7e5Seric 			return (FALSE);
936e39a5722Seric 		}
937e39a5722Seric 		while (fgets(buf, sizeof buf, tfp) != NULL)
938e39a5722Seric 			fputs(buf, pfp);
939e39a5722Seric 	}
940e39a5722Seric 	else
941e39a5722Seric 	{
942e39a5722Seric 		unlink(pfn);
943e39a5722Seric 	}
944e39a5722Seric 	fclose(tfp);
945e39a5722Seric 	fclose(pfp);
946e39a5722Seric 	unlink(tfn);
947e39a5722Seric 
948e39a5722Seric 	if (delete)
949e39a5722Seric 	{
950e39a5722Seric 		unlink(fn);
951e39a5722Seric 		printf("%12s: removed\n", fn);
952d51cd7e5Seric 		return (TRUE);
953e39a5722Seric 	}
954e39a5722Seric 	else
955e39a5722Seric 	{
9562444cd3eSeric 		printf("%12s: not being edited by you\n", fn);
957d51cd7e5Seric 		return (FALSE);
958e39a5722Seric 	}
959e39a5722Seric }
9600faf63f9Seric 
9610faf63f9Seric /*
962*78d41c6aSeric **  DODIFF -- diff an s-file against a g-file
963*78d41c6aSeric **
964*78d41c6aSeric **	Parameters:
965*78d41c6aSeric **		getv -- argv for the 'get' command.
966*78d41c6aSeric **		gfile -- name of the g-file to diff against.
967*78d41c6aSeric **
968*78d41c6aSeric **	Returns:
969*78d41c6aSeric **		Result of get.
970*78d41c6aSeric **
971*78d41c6aSeric **	Side Effects:
972*78d41c6aSeric **		none.
973*78d41c6aSeric */
974*78d41c6aSeric 
975*78d41c6aSeric dodiff(getv, gfile)
976*78d41c6aSeric 	char **getv;
977*78d41c6aSeric 	char *gfile;
978*78d41c6aSeric {
979*78d41c6aSeric 	int pipev[2];
980*78d41c6aSeric 	int rval;
981*78d41c6aSeric 	register int i;
982*78d41c6aSeric 	register int pid;
983*78d41c6aSeric 	auto int st;
984*78d41c6aSeric 	extern int errno;
985*78d41c6aSeric 	int (*osig)();
986*78d41c6aSeric 
987*78d41c6aSeric 	if (pipe(pipev) < 0)
988*78d41c6aSeric 	{
989*78d41c6aSeric 		syserr("dodiff: pipe failed");
990*78d41c6aSeric 		exit(EX_OSERR);
991*78d41c6aSeric 	}
992*78d41c6aSeric 	if ((pid = fork()) < 0)
993*78d41c6aSeric 	{
994*78d41c6aSeric 		syserr("dodiff: fork failed");
995*78d41c6aSeric 		exit(EX_OSERR);
996*78d41c6aSeric 	}
997*78d41c6aSeric 	else if (pid > 0)
998*78d41c6aSeric 	{
999*78d41c6aSeric 		/* in parent; run get */
1000*78d41c6aSeric 		OutFile = pipev[1];
1001*78d41c6aSeric 		close(pipev[0]);
1002*78d41c6aSeric 		rval = command(&getv[1], TRUE, FALSE, "get -s -k -p");
1003*78d41c6aSeric 		osig = signal(SIGINT, SIG_IGN);
1004*78d41c6aSeric 		while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR)
1005*78d41c6aSeric 			errno = 0;
1006*78d41c6aSeric 		signal(SIGINT, osig);
1007*78d41c6aSeric 		/* ignore result of diff */
1008*78d41c6aSeric 	}
1009*78d41c6aSeric 	else
1010*78d41c6aSeric 	{
1011*78d41c6aSeric 		/* in child, run diff */
1012*78d41c6aSeric 		if (close(pipev[1]) < 0 || close(0) < 0 ||
1013*78d41c6aSeric 		    dup(pipev[0]) != 0 || close(pipev[0]) < 0)
1014*78d41c6aSeric 		{
1015*78d41c6aSeric 			syserr("dodiff: magic failed");
1016*78d41c6aSeric 			exit(EX_OSERR);
1017*78d41c6aSeric 		}
1018*78d41c6aSeric 		execl(PROGPATH(bdiff), "bdiff", "-", gfile, NULL);
1019*78d41c6aSeric # ifndef V6
1020*78d41c6aSeric 		execlp("bdiff", "bdiff", "-", gfile, NULL);
1021*78d41c6aSeric 		execlp("diff", "diff", "-", gfile, NULL);
1022*78d41c6aSeric # endif NOT V6
1023*78d41c6aSeric 		syserr("bdiff: cannot execute");
1024*78d41c6aSeric 		exit(EX_OSERR);
1025*78d41c6aSeric 	}
1026*78d41c6aSeric 	return (rval);
1027*78d41c6aSeric }
1028*78d41c6aSeric 
1029*78d41c6aSeric /*
1030e39a5722Seric **  GETPFILE -- get an entry from the p-file
1031e39a5722Seric **
1032e39a5722Seric **	Parameters:
1033e39a5722Seric **		pfp -- p-file file pointer
1034e39a5722Seric **
1035e39a5722Seric **	Returns:
1036e39a5722Seric **		pointer to p-file struct for next entry
1037e39a5722Seric **		NULL on EOF or error
1038e39a5722Seric **
1039e39a5722Seric **	Side Effects:
1040e39a5722Seric **		Each call wipes out results of previous call.
1041e39a5722Seric */
1042e39a5722Seric 
1043e39a5722Seric struct pfile *
1044e39a5722Seric getpfile(pfp)
1045e39a5722Seric 	FILE *pfp;
1046e39a5722Seric {
1047e39a5722Seric 	static struct pfile ent;
1048e39a5722Seric 	static char buf[120];
1049e39a5722Seric 	register char *p;
1050e39a5722Seric 	extern char *nextfield();
1051e39a5722Seric 
1052e39a5722Seric 	if (fgets(buf, sizeof buf, pfp) == NULL)
1053e39a5722Seric 		return (NULL);
1054e39a5722Seric 
1055e39a5722Seric 	ent.p_osid = p = buf;
1056e39a5722Seric 	ent.p_nsid = p = nextfield(p);
1057e39a5722Seric 	ent.p_user = p = nextfield(p);
1058e39a5722Seric 	ent.p_date = p = nextfield(p);
1059e39a5722Seric 	ent.p_time = p = nextfield(p);
1060e39a5722Seric 	if (p == NULL || nextfield(p) != NULL)
1061e39a5722Seric 		return (NULL);
1062e39a5722Seric 
1063e39a5722Seric 	return (&ent);
1064e39a5722Seric }
1065e39a5722Seric 
1066e39a5722Seric 
1067e39a5722Seric char *
1068e39a5722Seric nextfield(p)
1069e39a5722Seric 	register char *p;
1070e39a5722Seric {
1071e39a5722Seric 	if (p == NULL || *p == '\0')
1072e39a5722Seric 		return (NULL);
1073e39a5722Seric 	while (*p != ' ' && *p != '\n' && *p != '\0')
1074e39a5722Seric 		p++;
1075e39a5722Seric 	if (*p == '\n' || *p == '\0')
1076e39a5722Seric 	{
1077e39a5722Seric 		*p = '\0';
1078e39a5722Seric 		return (NULL);
1079e39a5722Seric 	}
1080e39a5722Seric 	*p++ = '\0';
1081e39a5722Seric 	return (p);
1082e39a5722Seric }
10830faf63f9Seric 
10840faf63f9Seric /*
1085095915d3Seric **  USRERR -- issue user-level error
1086095915d3Seric **
1087095915d3Seric **	Parameters:
1088095915d3Seric **		f -- format string.
1089095915d3Seric **		p1-p3 -- parameters to a printf.
1090095915d3Seric **
1091095915d3Seric **	Returns:
1092095915d3Seric **		-1
1093095915d3Seric **
1094095915d3Seric **	Side Effects:
1095095915d3Seric **		none.
1096095915d3Seric */
1097095915d3Seric 
1098095915d3Seric usrerr(f, p1, p2, p3)
1099095915d3Seric 	char *f;
1100095915d3Seric {
1101095915d3Seric 	fprintf(stderr, "\n%s: ", MyName);
1102095915d3Seric 	fprintf(stderr, f, p1, p2, p3);
1103095915d3Seric 	fprintf(stderr, "\n");
1104095915d3Seric 
1105095915d3Seric 	return (-1);
1106095915d3Seric }
11070faf63f9Seric 
11080faf63f9Seric /*
1109095915d3Seric **  SYSERR -- print system-generated error.
1110095915d3Seric **
1111095915d3Seric **	Parameters:
1112095915d3Seric **		f -- format string to a printf.
1113095915d3Seric **		p1, p2, p3 -- parameters to f.
1114095915d3Seric **
1115095915d3Seric **	Returns:
1116095915d3Seric **		never.
1117095915d3Seric **
1118095915d3Seric **	Side Effects:
1119095915d3Seric **		none.
1120095915d3Seric */
1121095915d3Seric 
1122095915d3Seric syserr(f, p1, p2, p3)
1123095915d3Seric 	char *f;
1124095915d3Seric {
1125095915d3Seric 	extern int errno;
1126095915d3Seric 
1127095915d3Seric 	fprintf(stderr, "\n%s SYSERR: ", MyName);
1128095915d3Seric 	fprintf(stderr, f, p1, p2, p3);
1129095915d3Seric 	fprintf(stderr, "\n");
1130095915d3Seric 	if (errno == 0)
1131095915d3Seric 		exit(EX_SOFTWARE);
1132095915d3Seric 	else
1133095915d3Seric 	{
1134095915d3Seric 		perror(0);
1135095915d3Seric 		exit(EX_OSERR);
1136095915d3Seric 	}
1137095915d3Seric }
1138