xref: /original-bsd/usr.bin/sccs/sccs.c (revision cc4e4f0d)
1adf8f7d4Seric # include <stdio.h>
2adf8f7d4Seric # include <sys/types.h>
3adf8f7d4Seric # include <sys/stat.h>
4b2538d76Seric # include <sys/dir.h>
578d41c6aSeric # include <errno.h>
678d41c6aSeric # 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.
82c4ad8825Seric **		MYNAME -- the title this program should print when it
83c4ad8825Seric **			gives error messages.
8410cd16efSeric **
8510cd16efSeric **	Compilation Instructions:
8610cd16efSeric **		cc -O -n -s sccs.c
87c4ad8825Seric **		The flags listed above can be -D defined to simplify
88c4ad8825Seric **			recompilation for variant versions.
8910cd16efSeric **
9010cd16efSeric **	Author:
9110cd16efSeric **		Eric Allman, UCB/INGRES
927abec564Seric **		Copyright 1980 Regents of the University of California
9310cd16efSeric */
9410cd16efSeric 
95*cc4e4f0dSeric static char SccsId[] = "@(#)sccs.c	1.40 10/17/80";
960faf63f9Seric 
977abec564Seric /*******************  Configuration Information  ********************/
987abec564Seric 
99c4ad8825Seric /* special defines for local berkeley systems */
100c4ad8825Seric # include <whoami.h>
101c4ad8825Seric 
10210cd16efSeric # ifdef CSVAX
10310cd16efSeric # define UIDUSER
1047abec564Seric # define PROGPATH(name)	"/usr/local/name"
1057abec564Seric # endif CSVAX
10610cd16efSeric 
107c4ad8825Seric /* end of berkeley systems defines */
1087abec564Seric 
109c4ad8825Seric # ifndef SCCSPATH
110c4ad8825Seric # define SCCSPATH	"SCCS"	/* pathname in which to find s-files */
111c4ad8825Seric # endif NOT SCCSPATH
112c4ad8825Seric 
113c4ad8825Seric # ifndef MYNAME
114c4ad8825Seric # define MYNAME		"sccs"	/* name used for printing errors */
115c4ad8825Seric # endif NOT MYNAME
1167abec564Seric 
1170faf63f9Seric # ifndef PROGPATH
1180faf63f9Seric # define PROGPATH(name)	"/usr/sccs/name"	/* place to find binaries */
1190faf63f9Seric # endif PROGPATH
1200faf63f9Seric 
1217abec564Seric /****************  End of Configuration Information  ****************/
1220faf63f9Seric 
123c4432be4Seric typedef char	bool;
1247de81dc7Seric # define TRUE	1
1257de81dc7Seric # define FALSE	0
126d02a4f42Seric 
127ff038098Seric # define bitset(bit, word)	((bool) ((bit) & (word)))
128ff038098Seric 
12910cd16efSeric # ifdef UIDUSER
13010cd16efSeric # include <pwd.h>
13110cd16efSeric # endif UIDUSER
13210cd16efSeric 
133adf8f7d4Seric struct sccsprog
134adf8f7d4Seric {
135adf8f7d4Seric 	char	*sccsname;	/* name of SCCS routine */
1367de81dc7Seric 	short	sccsoper;	/* opcode, see below */
1377de81dc7Seric 	short	sccsflags;	/* flags, see below */
138108d6082Seric 	char	*sccsklets;	/* valid key-letters on macros */
139adf8f7d4Seric 	char	*sccspath;	/* pathname of binary implementing */
140adf8f7d4Seric };
141adf8f7d4Seric 
1427de81dc7Seric /* values for sccsoper */
1437de81dc7Seric # define PROG		0	/* call a program */
1441777fbcbSeric # define CMACRO		1	/* command substitution macro */
145172147faSeric # define FIX		2	/* fix a delta */
146b2538d76Seric # define CLEAN		3	/* clean out recreatable files */
147e39a5722Seric # define UNEDIT		4	/* unedit a file */
148419708b0Seric # define SHELL		5	/* call a shell file (like PROG) */
14978d41c6aSeric # define DIFFS		6	/* diff between sccs & file out */
1507de81dc7Seric 
151c4432be4Seric /* bits for sccsflags */
1527de81dc7Seric # define NO_SDOT	0001	/* no s. on front of args */
1537de81dc7Seric # define REALUSER	0002	/* protected (e.g., admin) */
154adf8f7d4Seric 
15548ff0e7eSeric /* modes for the "clean", "info", "check" ops */
15648ff0e7eSeric # define CLEANC		0	/* clean command */
15748ff0e7eSeric # define INFOC		1	/* info command */
15848ff0e7eSeric # define CHECKC		2	/* check command */
15948ff0e7eSeric 
1600faf63f9Seric /*
1610faf63f9Seric **  Description of commands known to this program.
1620faf63f9Seric **	First argument puts the command into a class.  Second arg is
1630faf63f9Seric **	info regarding treatment of this command.  Third arg is a
1640faf63f9Seric **	list of flags this command accepts from macros, etc.  Fourth
1650faf63f9Seric **	arg is the pathname of the implementing program, or the
1660faf63f9Seric **	macro definition, or the arg to a sub-algorithm.
1670faf63f9Seric */
168b5d4f080Seric 
169adf8f7d4Seric struct sccsprog SccsProg[] =
170adf8f7d4Seric {
171108d6082Seric 	"admin",	PROG,	REALUSER,	"",		PROGPATH(admin),
172108d6082Seric 	"chghist",	PROG,	0,		"",		PROGPATH(rmdel),
173108d6082Seric 	"comb",		PROG,	0,		"",		PROGPATH(comb),
174108d6082Seric 	"delta",	PROG,	0,		"mysrp",	PROGPATH(delta),
175c952b8bfSeric 	"get",		PROG,	0,		"ixbeskcl",	PROGPATH(get),
176108d6082Seric 	"help",		PROG,	NO_SDOT,	"",		PROGPATH(help),
177108d6082Seric 	"prt",		PROG,	0,		"",		PROGPATH(prt),
178108d6082Seric 	"rmdel",	PROG,	REALUSER,	"",		PROGPATH(rmdel),
179108d6082Seric 	"what",		PROG,	NO_SDOT,	"",		PROGPATH(what),
180419708b0Seric 	"sccsdiff",	SHELL,	REALUSER,	"",		PROGPATH(sccsdiff),
181c952b8bfSeric 	"edit",		CMACRO,	NO_SDOT,	"ixbscl",	"get -e",
182108d6082Seric 	"delget",	CMACRO,	NO_SDOT,	"",		"delta/get -t",
183108d6082Seric 	"deledit",	CMACRO,	NO_SDOT,	"",		"delta/get -e -t",
184108d6082Seric 	"fix",		FIX,	NO_SDOT,	"",		NULL,
185108d6082Seric 	"clean",	CLEAN,	REALUSER,	"",		(char *) CLEANC,
186108d6082Seric 	"info",		CLEAN,	REALUSER,	"",		(char *) INFOC,
187108d6082Seric 	"check",	CLEAN,	REALUSER,	"",		(char *) CHECKC,
188108d6082Seric 	"unedit",	UNEDIT,	NO_SDOT,	"",		NULL,
18978d41c6aSeric 	"diffs",	DIFFS,	NO_SDOT|REALUSER, "",		NULL,
190108d6082Seric 	NULL,		-1,	0,		"",		NULL
191adf8f7d4Seric };
192adf8f7d4Seric 
1930faf63f9Seric /* one line from a p-file */
194e39a5722Seric struct pfile
195e39a5722Seric {
196e39a5722Seric 	char	*p_osid;	/* old SID */
197e39a5722Seric 	char	*p_nsid;	/* new SID */
198e39a5722Seric 	char	*p_user;	/* user who did edit */
199e39a5722Seric 	char	*p_date;	/* date of get */
200e39a5722Seric 	char	*p_time;	/* time of get */
201e39a5722Seric };
202e39a5722Seric 
2037abec564Seric char	*SccsPath = SCCSPATH;	/* pathname of SCCS files */
2047abec564Seric # ifdef SCCSDIR
2057abec564Seric char	*SccsDir = SCCSDIR;	/* directory to begin search from */
206095915d3Seric # else
2077abec564Seric char	*SccsDir = "";
208095915d3Seric # endif
209c4ad8825Seric char	MyName[] = MYNAME;	/* name used in messages */
21078d41c6aSeric int	OutFile = -1;		/* override output file for commands */
211c4432be4Seric bool	RealUser;		/* if set, running as real user */
212f0cc3246Seric # ifdef DEBUG
213f0cc3246Seric bool	Debug;			/* turn on tracing */
214f0cc3246Seric # endif
2150faf63f9Seric 
216adf8f7d4Seric main(argc, argv)
217adf8f7d4Seric 	int argc;
218adf8f7d4Seric 	char **argv;
219adf8f7d4Seric {
220adf8f7d4Seric 	register char *p;
221b1ed8a43Seric 	extern struct sccsprog *lookup();
22241290c52Seric 	register int i;
223adf8f7d4Seric 
224adf8f7d4Seric 	/*
225adf8f7d4Seric 	**  Detect and decode flags intended for this program.
226adf8f7d4Seric 	*/
227adf8f7d4Seric 
2287de81dc7Seric 	if (argc < 2)
229adf8f7d4Seric 	{
230095915d3Seric 		fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName);
2317de81dc7Seric 		exit(EX_USAGE);
2327de81dc7Seric 	}
2337de81dc7Seric 	argv[argc] = NULL;
2347de81dc7Seric 
235b1ed8a43Seric 	if (lookup(argv[0]) == NULL)
236b1ed8a43Seric 	{
2377de81dc7Seric 		while ((p = *++argv) != NULL)
2387de81dc7Seric 		{
239adf8f7d4Seric 			if (*p != '-')
240adf8f7d4Seric 				break;
241adf8f7d4Seric 			switch (*++p)
242adf8f7d4Seric 			{
243adf8f7d4Seric 			  case 'r':		/* run as real user */
244adf8f7d4Seric 				setuid(getuid());
245c4432be4Seric 				RealUser++;
246adf8f7d4Seric 				break;
247adf8f7d4Seric 
2487abec564Seric # ifndef SCCSDIR
249adf8f7d4Seric 			  case 'p':		/* path of sccs files */
250adf8f7d4Seric 				SccsPath = ++p;
251adf8f7d4Seric 				break;
252adf8f7d4Seric 
2530a8b9ab0Seric 			  case 'd':		/* directory to search from */
2540a8b9ab0Seric 				SccsDir = ++p;
2550a8b9ab0Seric 				break;
256095915d3Seric # endif
2570a8b9ab0Seric 
258f0cc3246Seric # ifdef DEBUG
259f0cc3246Seric 			  case 'T':		/* trace */
260f0cc3246Seric 				Debug++;
261f0cc3246Seric 				break;
262f0cc3246Seric # endif
263f0cc3246Seric 
264adf8f7d4Seric 			  default:
265095915d3Seric 				usrerr("unknown option -%s", p);
266adf8f7d4Seric 				break;
267adf8f7d4Seric 			}
268adf8f7d4Seric 		}
2695cabffd9Seric 		if (SccsPath[0] == '\0')
2705cabffd9Seric 			SccsPath = ".";
271b1ed8a43Seric 	}
272adf8f7d4Seric 
273108d6082Seric 	i = command(argv, FALSE, FALSE, "");
27441290c52Seric 	exit(i);
2757de81dc7Seric }
2760faf63f9Seric 
2770faf63f9Seric /*
27841290c52Seric **  COMMAND -- look up and perform a command
27941290c52Seric **
28041290c52Seric **	This routine is the guts of this program.  Given an
28141290c52Seric **	argument vector, it looks up the "command" (argv[0])
28241290c52Seric **	in the configuration table and does the necessary stuff.
28341290c52Seric **
28441290c52Seric **	Parameters:
28541290c52Seric **		argv -- an argument vector to process.
28641290c52Seric **		forkflag -- if set, fork before executing the command.
287108d6082Seric **		editflag -- if set, only include flags listed in the
288108d6082Seric **			sccsklets field of the command descriptor.
289108d6082Seric **		arg0 -- a space-seperated list of arguments to insert
290108d6082Seric **			before argv.
29141290c52Seric **
29241290c52Seric **	Returns:
29341290c52Seric **		zero -- command executed ok.
29441290c52Seric **		else -- error status.
29541290c52Seric **
29641290c52Seric **	Side Effects:
29741290c52Seric **		none.
29841290c52Seric */
2997de81dc7Seric 
300108d6082Seric command(argv, forkflag, editflag, arg0)
3017de81dc7Seric 	char **argv;
3021777fbcbSeric 	bool forkflag;
303108d6082Seric 	bool editflag;
304108d6082Seric 	char *arg0;
3057de81dc7Seric {
3067de81dc7Seric 	register struct sccsprog *cmd;
3077de81dc7Seric 	register char *p;
3081777fbcbSeric 	char buf[40];
309b1ed8a43Seric 	extern struct sccsprog *lookup();
310108d6082Seric 	char *nav[1000];
311108d6082Seric 	char **np;
312419708b0Seric 	register char **ap;
313d51cd7e5Seric 	register int i;
314419708b0Seric 	register char *q;
315d51cd7e5Seric 	extern bool unedit();
31641290c52Seric 	int rval = 0;
317108d6082Seric 	extern char *index();
318108d6082Seric 	extern char *makefile();
319287bbaa2Seric 	extern char *tail();
320f0cc3246Seric 
321f0cc3246Seric # ifdef DEBUG
322f0cc3246Seric 	if (Debug)
323f0cc3246Seric 	{
324108d6082Seric 		printf("command:\n\t\"%s\"\n", arg0);
325108d6082Seric 		for (np = argv; *np != NULL; np++)
326108d6082Seric 			printf("\t\"%s\"\n", *np);
327f0cc3246Seric 	}
328f0cc3246Seric # endif
329c4432be4Seric 
330c4432be4Seric 	/*
331108d6082Seric 	**  Copy arguments.
332ff038098Seric 	**	Copy from arg0 & if necessary at most one arg
333ff038098Seric 	**	from argv[0].
334adf8f7d4Seric 	*/
335adf8f7d4Seric 
336419708b0Seric 	np = ap = &nav[1];
337108d6082Seric 	for (p = arg0, q = buf; *p != '\0' && *p != '/'; )
338108d6082Seric 	{
339108d6082Seric 		*np++ = q;
340108d6082Seric 		while (*p == ' ')
341108d6082Seric 			p++;
342108d6082Seric 		while (*p != ' ' && *p != '\0' && *p != '/')
343108d6082Seric 			*q++ = *p++;
344108d6082Seric 		*q++ = '\0';
345108d6082Seric 	}
346108d6082Seric 	*np = NULL;
347419708b0Seric 	if (*ap == NULL)
348108d6082Seric 		*np++ = *argv++;
349108d6082Seric 
350108d6082Seric 	/*
351108d6082Seric 	**  Look up command.
352419708b0Seric 	**	At this point, *ap is the command name.
353108d6082Seric 	*/
354108d6082Seric 
355419708b0Seric 	cmd = lookup(*ap);
356b1ed8a43Seric 	if (cmd == NULL)
357adf8f7d4Seric 	{
358419708b0Seric 		usrerr("Unknown command \"%s\"", *ap);
35941290c52Seric 		return (EX_USAGE);
360adf8f7d4Seric 	}
361adf8f7d4Seric 
362adf8f7d4Seric 	/*
363108d6082Seric 	**  Copy remaining arguments doing editing as appropriate.
364108d6082Seric 	*/
365108d6082Seric 
366108d6082Seric 	for (; *argv != NULL; argv++)
367108d6082Seric 	{
368108d6082Seric 		p = *argv;
369108d6082Seric 		if (*p == '-')
370108d6082Seric 		{
371108d6082Seric 			if (p[1] == '\0' || !editflag || cmd->sccsklets == NULL ||
372108d6082Seric 			    index(cmd->sccsklets, p[1]) != NULL)
373108d6082Seric 				*np++ = p;
374108d6082Seric 		}
375108d6082Seric 		else
376108d6082Seric 		{
377108d6082Seric 			if (!bitset(NO_SDOT, cmd->sccsflags))
378108d6082Seric 				p = makefile(p);
379108d6082Seric 			if (p != NULL)
380108d6082Seric 				*np++ = p;
381108d6082Seric 		}
382108d6082Seric 	}
383108d6082Seric 	*np = NULL;
384108d6082Seric 
385108d6082Seric 	/*
3867de81dc7Seric 	**  Interpret operation associated with this command.
387c4432be4Seric 	*/
388c4432be4Seric 
3897de81dc7Seric 	switch (cmd->sccsoper)
3907de81dc7Seric 	{
391419708b0Seric 	  case SHELL:		/* call a shell file */
392419708b0Seric 		*ap = cmd->sccspath;
393419708b0Seric 		*--ap = "sh";
394419708b0Seric 		rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag);
395419708b0Seric 		break;
396419708b0Seric 
3977de81dc7Seric 	  case PROG:		/* call an sccs prog */
398419708b0Seric 		rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag);
3991777fbcbSeric 		break;
4001777fbcbSeric 
4011777fbcbSeric 	  case CMACRO:		/* command macro */
402ff038098Seric 		/* step through & execute each part of the macro */
4031777fbcbSeric 		for (p = cmd->sccspath; *p != '\0'; p++)
4041777fbcbSeric 		{
405108d6082Seric 			q = p;
406108d6082Seric 			while (*p != '\0' && *p != '/')
407108d6082Seric 				p++;
408419708b0Seric 			rval = command(&ap[1], *p != '\0', TRUE, q);
40941290c52Seric 			if (rval != 0)
41041290c52Seric 				break;
4111777fbcbSeric 		}
41241290c52Seric 		break;
4137de81dc7Seric 
414172147faSeric 	  case FIX:		/* fix a delta */
415419708b0Seric 		if (strncmp(ap[1], "-r", 2) != 0)
416172147faSeric 		{
417095915d3Seric 			usrerr("-r flag needed for fix command");
41841290c52Seric 			rval = EX_USAGE;
419172147faSeric 			break;
420172147faSeric 		}
421ff038098Seric 
422ff038098Seric 		/* get the version with all changes */
423419708b0Seric 		rval = command(&ap[1], TRUE, TRUE, "get -k");
424ff038098Seric 
425ff038098Seric 		/* now remove that version from the s-file */
42641290c52Seric 		if (rval == 0)
427419708b0Seric 			rval = command(&ap[1], TRUE, TRUE, "rmdel");
428ff038098Seric 
429ff038098Seric 		/* and edit the old version (but don't clobber new vers) */
43041290c52Seric 		if (rval == 0)
431419708b0Seric 			rval = command(&ap[2], FALSE, TRUE, "get -e -g");
43241290c52Seric 		break;
433172147faSeric 
434b2538d76Seric 	  case CLEAN:
43541290c52Seric 		rval = clean((int) cmd->sccspath);
436b2538d76Seric 		break;
437b2538d76Seric 
438e39a5722Seric 	  case UNEDIT:
439419708b0Seric 		for (argv = np = &ap[1]; *argv != NULL; argv++)
440d51cd7e5Seric 		{
441108d6082Seric 			if (unedit(*argv))
442108d6082Seric 				*np++ = *argv;
443d51cd7e5Seric 		}
444108d6082Seric 		*np = NULL;
445ff038098Seric 
446ff038098Seric 		/* get all the files that we unedited successfully */
447d51cd7e5Seric 		if (i > 0)
448419708b0Seric 			rval = command(&ap[1], FALSE, FALSE, "get");
449e39a5722Seric 		break;
450e39a5722Seric 
45178d41c6aSeric 	  case DIFFS:		/* diff between s-file & edit file */
45278d41c6aSeric 		/* find the end of the flag arguments */
45378d41c6aSeric 		for (np = &ap[1]; *np != NULL && **np == '-'; np++)
45478d41c6aSeric 			continue;
45578d41c6aSeric 		argv = np;
45678d41c6aSeric 
45778d41c6aSeric 		/* for each file, do the diff */
45878d41c6aSeric 		while (*np != NULL)
45978d41c6aSeric 		{
460ff038098Seric 			/* messy, but we need a null terminated argv */
46178d41c6aSeric 			*argv = *np++;
46278d41c6aSeric 			p = *np;
46378d41c6aSeric 			*np = NULL;
464287bbaa2Seric 			i = dodiff(ap, tail(*argv));
46578d41c6aSeric 			if (rval == 0)
46678d41c6aSeric 				rval = i;
46778d41c6aSeric 			*np = p;
46878d41c6aSeric 		}
46978d41c6aSeric 		break;
47078d41c6aSeric 
4717de81dc7Seric 	  default:
472095915d3Seric 		syserr("oper %d", cmd->sccsoper);
4737de81dc7Seric 		exit(EX_SOFTWARE);
4747de81dc7Seric 	}
47541290c52Seric # ifdef DEBUG
47641290c52Seric 	if (Debug)
47741290c52Seric 		printf("command: rval=%d\n", rval);
47841290c52Seric # endif
47941290c52Seric 	return (rval);
4807de81dc7Seric }
4810faf63f9Seric 
4820faf63f9Seric /*
483b1ed8a43Seric **  LOOKUP -- look up an SCCS command name.
484b1ed8a43Seric **
485b1ed8a43Seric **	Parameters:
486b1ed8a43Seric **		name -- the name of the command to look up.
487b1ed8a43Seric **
488b1ed8a43Seric **	Returns:
489b1ed8a43Seric **		ptr to command descriptor for this command.
490b1ed8a43Seric **		NULL if no such entry.
491b1ed8a43Seric **
492b1ed8a43Seric **	Side Effects:
493b1ed8a43Seric **		none.
494b1ed8a43Seric */
495b1ed8a43Seric 
496b1ed8a43Seric struct sccsprog *
497b1ed8a43Seric lookup(name)
498b1ed8a43Seric 	char *name;
499b1ed8a43Seric {
500b1ed8a43Seric 	register struct sccsprog *cmd;
501b1ed8a43Seric 
502b1ed8a43Seric 	for (cmd = SccsProg; cmd->sccsname != NULL; cmd++)
503b1ed8a43Seric 	{
504b1ed8a43Seric 		if (strcmp(cmd->sccsname, name) == 0)
505b1ed8a43Seric 			return (cmd);
506b1ed8a43Seric 	}
507b1ed8a43Seric 	return (NULL);
508b1ed8a43Seric }
5090faf63f9Seric 
5100faf63f9Seric /*
51141290c52Seric **  CALLPROG -- call a program
51241290c52Seric **
513108d6082Seric **	Used to call the SCCS programs.
51441290c52Seric **
51541290c52Seric **	Parameters:
51641290c52Seric **		progpath -- pathname of the program to call.
51741290c52Seric **		flags -- status flags from the command descriptors.
51841290c52Seric **		argv -- an argument vector to pass to the program.
51941290c52Seric **		forkflag -- if true, fork before calling, else just
52041290c52Seric **			exec.
52141290c52Seric **
52241290c52Seric **	Returns:
52341290c52Seric **		The exit status of the program.
52441290c52Seric **		Nothing if forkflag == FALSE.
52541290c52Seric **
52641290c52Seric **	Side Effects:
52741290c52Seric **		Can exit if forkflag == FALSE.
52841290c52Seric */
529172147faSeric 
5307de81dc7Seric callprog(progpath, flags, argv, forkflag)
5317de81dc7Seric 	char *progpath;
5327de81dc7Seric 	short flags;
5337de81dc7Seric 	char **argv;
5347de81dc7Seric 	bool forkflag;
5357de81dc7Seric {
5367de81dc7Seric 	register int i;
5371777fbcbSeric 	auto int st;
538108d6082Seric 
539108d6082Seric # ifdef DEBUG
540108d6082Seric 	if (Debug)
541108d6082Seric 	{
542108d6082Seric 		printf("callprog:\n");
543108d6082Seric 		for (i = 0; argv[i] != NULL; i++)
544108d6082Seric 			printf("\t\"%s\"\n", argv[i]);
545108d6082Seric 	}
546108d6082Seric # endif
5477de81dc7Seric 
5487de81dc7Seric 	if (*argv == NULL)
5497de81dc7Seric 		return (-1);
550c4432be4Seric 
551c4432be4Seric 	/*
552172147faSeric 	**  Fork if appropriate.
553adf8f7d4Seric 	*/
554adf8f7d4Seric 
5557de81dc7Seric 	if (forkflag)
5567de81dc7Seric 	{
557f0cc3246Seric # ifdef DEBUG
558f0cc3246Seric 		if (Debug)
559f0cc3246Seric 			printf("Forking\n");
560f0cc3246Seric # endif
5617de81dc7Seric 		i = fork();
5627de81dc7Seric 		if (i < 0)
5637de81dc7Seric 		{
564095915d3Seric 			syserr("cannot fork");
5657de81dc7Seric 			exit(EX_OSERR);
5667de81dc7Seric 		}
5677de81dc7Seric 		else if (i > 0)
5681777fbcbSeric 		{
5691777fbcbSeric 			wait(&st);
57041290c52Seric 			if ((st & 0377) == 0)
57141290c52Seric 				st = (st >> 8) & 0377;
57278d41c6aSeric 			if (OutFile >= 0)
57378d41c6aSeric 			{
57478d41c6aSeric 				close(OutFile);
57578d41c6aSeric 				OutFile = -1;
57678d41c6aSeric 			}
5771777fbcbSeric 			return (st);
5781777fbcbSeric 		}
5797de81dc7Seric 	}
58078d41c6aSeric 	else if (OutFile >= 0)
58178d41c6aSeric 	{
58278d41c6aSeric 		syserr("callprog: setting stdout w/o forking");
58378d41c6aSeric 		exit(EX_SOFTWARE);
58478d41c6aSeric 	}
5857de81dc7Seric 
58678d41c6aSeric 	/* set protection as appropriate */
5877de81dc7Seric 	if (bitset(REALUSER, flags))
5887de81dc7Seric 		setuid(getuid());
5897de81dc7Seric 
59078d41c6aSeric 	/* change standard input & output if needed */
59178d41c6aSeric 	if (OutFile >= 0)
59278d41c6aSeric 	{
59378d41c6aSeric 		close(1);
59478d41c6aSeric 		dup(OutFile);
59578d41c6aSeric 		close(OutFile);
59678d41c6aSeric 	}
5977de81dc7Seric 
59878d41c6aSeric 	/* call real SCCS program */
599172147faSeric 	execv(progpath, argv);
600095915d3Seric 	syserr("cannot execute %s", progpath);
601adf8f7d4Seric 	exit(EX_UNAVAILABLE);
602adf8f7d4Seric }
6030faf63f9Seric 
6040faf63f9Seric /*
605cdc1aa65Seric **  MAKEFILE -- make filename of SCCS file
606cdc1aa65Seric **
607cdc1aa65Seric **	If the name passed is already the name of an SCCS file,
608cdc1aa65Seric **	just return it.  Otherwise, munge the name into the name
609cdc1aa65Seric **	of the actual SCCS file.
610cdc1aa65Seric **
611cdc1aa65Seric **	There are cases when it is not clear what you want to
612cdc1aa65Seric **	do.  For example, if SccsPath is an absolute pathname
613cdc1aa65Seric **	and the name given is also an absolute pathname, we go
614cdc1aa65Seric **	for SccsPath (& only use the last component of the name
615cdc1aa65Seric **	passed) -- this is important for security reasons (if
616cdc1aa65Seric **	sccs is being used as a setuid front end), but not
617cdc1aa65Seric **	particularly intuitive.
618cdc1aa65Seric **
619cdc1aa65Seric **	Parameters:
620cdc1aa65Seric **		name -- the file name to be munged.
621cdc1aa65Seric **
622cdc1aa65Seric **	Returns:
623cdc1aa65Seric **		The pathname of the sccs file.
624cdc1aa65Seric **		NULL on error.
625cdc1aa65Seric **
626cdc1aa65Seric **	Side Effects:
627cdc1aa65Seric **		none.
628cdc1aa65Seric */
629adf8f7d4Seric 
630adf8f7d4Seric char *
631adf8f7d4Seric makefile(name)
632adf8f7d4Seric 	char *name;
633adf8f7d4Seric {
634adf8f7d4Seric 	register char *p;
635adf8f7d4Seric 	register char c;
636adf8f7d4Seric 	char buf[512];
6370a8b9ab0Seric 	struct stat stbuf;
638adf8f7d4Seric 	extern char *malloc();
639cdc1aa65Seric 	extern char *rindex();
6400a8b9ab0Seric 	extern bool safepath();
6417d70f5d8Seric 	extern bool isdir();
6427d70f5d8Seric 	register char *q;
643cdc1aa65Seric 
644cdc1aa65Seric 	p = rindex(name, '/');
645cdc1aa65Seric 	if (p == NULL)
646cdc1aa65Seric 		p = name;
647cdc1aa65Seric 	else
648cdc1aa65Seric 		p++;
649adf8f7d4Seric 
650adf8f7d4Seric 	/*
6510a8b9ab0Seric 	**  Check to see that the path is "safe", i.e., that we
6520a8b9ab0Seric 	**  are not letting some nasty person use the setuid part
6530a8b9ab0Seric 	**  of this program to look at or munge some presumably
6540a8b9ab0Seric 	**  hidden files.
655adf8f7d4Seric 	*/
656adf8f7d4Seric 
6570a8b9ab0Seric 	if (SccsDir[0] == '/' && !safepath(name))
6580a8b9ab0Seric 		return (NULL);
659cdc1aa65Seric 
660cdc1aa65Seric 	/*
6610a8b9ab0Seric 	**  Create the base pathname.
662cdc1aa65Seric 	*/
663cdc1aa65Seric 
664ff038098Seric 	/* first the directory part */
6650a8b9ab0Seric 	if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0)
666adf8f7d4Seric 	{
6670a8b9ab0Seric 		strcpy(buf, SccsDir);
668cdc1aa65Seric 		strcat(buf, "/");
669cdc1aa65Seric 	}
670cdc1aa65Seric 	else
671cdc1aa65Seric 		strcpy(buf, "");
672ff038098Seric 
673ff038098Seric 	/* then the head of the pathname */
674cdc1aa65Seric 	strncat(buf, name, p - name);
6757d70f5d8Seric 	q = &buf[strlen(buf)];
676ff038098Seric 
677ff038098Seric 	/* now copy the final part of the name, in case useful */
6787d70f5d8Seric 	strcpy(q, p);
679ff038098Seric 
680ff038098Seric 	/* so is it useful? */
6817d70f5d8Seric 	if (strncmp(p, "s.", 2) != 0 && !isdir(buf))
6827d70f5d8Seric 	{
683ff038098Seric 		/* sorry, no; copy the SCCS pathname & the "s." */
6840a8b9ab0Seric 		strcpy(q, SccsPath);
6850a8b9ab0Seric 		strcat(buf, "/s.");
686ff038098Seric 
687ff038098Seric 		/* and now the end of the name */
688cdc1aa65Seric 		strcat(buf, p);
689cdc1aa65Seric 	}
690adf8f7d4Seric 
691ff038098Seric 	/* if i haven't changed it, why did I do all this? */
6920a8b9ab0Seric 	if (strcmp(buf, name) == 0)
6930a8b9ab0Seric 		p = name;
6940a8b9ab0Seric 	else
6950a8b9ab0Seric 	{
696ff038098Seric 		/* but if I have, squirrel it away */
697adf8f7d4Seric 		p = malloc(strlen(buf) + 1);
698adf8f7d4Seric 		if (p == NULL)
699adf8f7d4Seric 		{
700adf8f7d4Seric 			perror("Sccs: no mem");
701adf8f7d4Seric 			exit(EX_OSERR);
702adf8f7d4Seric 		}
703adf8f7d4Seric 		strcpy(p, buf);
7040a8b9ab0Seric 	}
705ff038098Seric 
706adf8f7d4Seric 	return (p);
707adf8f7d4Seric }
7080faf63f9Seric 
7090faf63f9Seric /*
7107d70f5d8Seric **  ISDIR -- return true if the argument is a directory.
7117d70f5d8Seric **
7127d70f5d8Seric **	Parameters:
7137d70f5d8Seric **		name -- the pathname of the file to check.
7147d70f5d8Seric **
7157d70f5d8Seric **	Returns:
7167d70f5d8Seric **		TRUE if 'name' is a directory, FALSE otherwise.
7177d70f5d8Seric **
7187d70f5d8Seric **	Side Effects:
7197d70f5d8Seric **		none.
7207d70f5d8Seric */
7217d70f5d8Seric 
7227d70f5d8Seric bool
7237d70f5d8Seric isdir(name)
7247d70f5d8Seric 	char *name;
7257d70f5d8Seric {
7267d70f5d8Seric 	struct stat stbuf;
7277d70f5d8Seric 
7287d70f5d8Seric 	return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR);
7297d70f5d8Seric }
7300faf63f9Seric 
7310faf63f9Seric /*
732cdc1aa65Seric **  SAFEPATH -- determine whether a pathname is "safe"
733cdc1aa65Seric **
734cdc1aa65Seric **	"Safe" pathnames only allow you to get deeper into the
735cdc1aa65Seric **	directory structure, i.e., full pathnames and ".." are
736cdc1aa65Seric **	not allowed.
737cdc1aa65Seric **
738cdc1aa65Seric **	Parameters:
739cdc1aa65Seric **		p -- the name to check.
740cdc1aa65Seric **
741cdc1aa65Seric **	Returns:
742cdc1aa65Seric **		TRUE -- if the path is safe.
743cdc1aa65Seric **		FALSE -- if the path is not safe.
744cdc1aa65Seric **
745cdc1aa65Seric **	Side Effects:
746cdc1aa65Seric **		Prints a message if the path is not safe.
747cdc1aa65Seric */
748cdc1aa65Seric 
749cdc1aa65Seric bool
750cdc1aa65Seric safepath(p)
751cdc1aa65Seric 	register char *p;
752cdc1aa65Seric {
753cdc1aa65Seric 	extern char *index();
754cdc1aa65Seric 
755cdc1aa65Seric 	if (*p != '/')
756cdc1aa65Seric 	{
757cdc1aa65Seric 		while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0)
758cdc1aa65Seric 		{
759cdc1aa65Seric 			p = index(p, '/');
760cdc1aa65Seric 			if (p == NULL)
761cdc1aa65Seric 				return (TRUE);
762cdc1aa65Seric 			p++;
763cdc1aa65Seric 		}
764cdc1aa65Seric 	}
765cdc1aa65Seric 
766cdc1aa65Seric 	printf("You may not use full pathnames or \"..\"\n");
767cdc1aa65Seric 	return (FALSE);
768cdc1aa65Seric }
7690faf63f9Seric 
7700faf63f9Seric /*
771b2538d76Seric **  CLEAN -- clean out recreatable files
772b2538d76Seric **
773b2538d76Seric **	Any file for which an "s." file exists but no "p." file
774b2538d76Seric **	exists in the current directory is purged.
775b2538d76Seric **
776b2538d76Seric **	Parameters:
77748ff0e7eSeric **		tells whether this came from a "clean", "info", or
77848ff0e7eSeric **		"check" command.
779b2538d76Seric **
780b2538d76Seric **	Returns:
781b2538d76Seric **		none.
782b2538d76Seric **
783b2538d76Seric **	Side Effects:
78448ff0e7eSeric **		Removes files in the current directory.
78548ff0e7eSeric **		Prints information regarding files being edited.
78648ff0e7eSeric **		Exits if a "check" command.
787b2538d76Seric */
788b2538d76Seric 
78948ff0e7eSeric clean(mode)
79048ff0e7eSeric 	int mode;
791b2538d76Seric {
792b2538d76Seric 	struct direct dir;
793b2538d76Seric 	struct stat stbuf;
794b2538d76Seric 	char buf[100];
795bf9c1e66Seric 	char pline[120];
7963a208c77Seric 	register FILE *dirfd;
7973a208c77Seric 	register char *basefile;
7982caac8fdSeric 	bool gotedit;
799bf9c1e66Seric 	FILE *pfp;
800b2538d76Seric 
801ff038098Seric 	/*
802ff038098Seric 	**  Find and open the SCCS directory.
803ff038098Seric 	*/
804ff038098Seric 
805f77a08fbSeric 	strcpy(buf, SccsDir);
806f77a08fbSeric 	if (buf[0] != '\0')
807f77a08fbSeric 		strcat(buf, "/");
808f77a08fbSeric 	strcat(buf, SccsPath);
809ff038098Seric 
810f77a08fbSeric 	dirfd = fopen(buf, "r");
811b2538d76Seric 	if (dirfd == NULL)
812b2538d76Seric 	{
813f77a08fbSeric 		usrerr("cannot open %s", buf);
81441290c52Seric 		return (EX_NOINPUT);
815b2538d76Seric 	}
816b2538d76Seric 
817b2538d76Seric 	/*
818b2538d76Seric 	**  Scan the SCCS directory looking for s. files.
819ff038098Seric 	**	gotedit tells whether we have tried to clean any
820ff038098Seric 	**		files that are being edited.
821b2538d76Seric 	*/
822b2538d76Seric 
8232caac8fdSeric 	gotedit = FALSE;
824b2538d76Seric 	while (fread(&dir, sizeof dir, 1, dirfd) != NULL)
825b2538d76Seric 	{
826c9c62fd0Seric 		if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0)
827b2538d76Seric 			continue;
828b2538d76Seric 
829b2538d76Seric 		/* got an s. file -- see if the p. file exists */
830f77a08fbSeric 		strcpy(buf, SccsDir);
831f77a08fbSeric 		if (buf[0] != '\0')
832f77a08fbSeric 			strcat(buf, "/");
833f77a08fbSeric 		strcat(buf, SccsPath);
834b2538d76Seric 		strcat(buf, "/p.");
8353a208c77Seric 		basefile = &buf[strlen(buf)];
836c9c62fd0Seric 		strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2);
837bf9c1e66Seric 		basefile[sizeof dir.d_name - 2] = '\0';
838bf9c1e66Seric 		pfp = fopen(buf, "r");
839bf9c1e66Seric 		if (pfp != NULL)
8403a208c77Seric 		{
841ff038098Seric 			/* the file exists -- report it's contents */
842bf9c1e66Seric 			while (fgets(pline, sizeof pline, pfp) != NULL)
8432444cd3eSeric 				printf("%12s: being edited: %s", basefile, pline);
844bf9c1e66Seric 			fclose(pfp);
8452caac8fdSeric 			gotedit = TRUE;
846b2538d76Seric 			continue;
8473a208c77Seric 		}
848b2538d76Seric 
849b2538d76Seric 		/* the s. file exists and no p. file exists -- unlink the g-file */
85048ff0e7eSeric 		if (mode == CLEANC)
8513a208c77Seric 		{
852c9c62fd0Seric 			strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2);
8533a208c77Seric 			buf[sizeof dir.d_name - 2] = '\0';
854b2538d76Seric 			unlink(buf);
855b2538d76Seric 		}
8563a208c77Seric 	}
857b2538d76Seric 
858ff038098Seric 	/* cleanup & report results */
859b2538d76Seric 	fclose(dirfd);
86048ff0e7eSeric 	if (!gotedit && mode == INFOC)
8612444cd3eSeric 		printf("Nothing being edited\n");
86248ff0e7eSeric 	if (mode == CHECKC)
86348ff0e7eSeric 		exit(gotedit);
86441290c52Seric 	return (EX_OK);
865b2538d76Seric }
8660faf63f9Seric 
8670faf63f9Seric /*
868e39a5722Seric **  UNEDIT -- unedit a file
869e39a5722Seric **
870e39a5722Seric **	Checks to see that the current user is actually editting
871e39a5722Seric **	the file and arranges that s/he is not editting it.
872e39a5722Seric **
873e39a5722Seric **	Parameters:
8742444cd3eSeric **		fn -- the name of the file to be unedited.
875e39a5722Seric **
876e39a5722Seric **	Returns:
877d51cd7e5Seric **		TRUE -- if the file was successfully unedited.
878d51cd7e5Seric **		FALSE -- if the file was not unedited for some
879d51cd7e5Seric **			reason.
880e39a5722Seric **
881e39a5722Seric **	Side Effects:
882e39a5722Seric **		fn is removed
883e39a5722Seric **		entries are removed from pfile.
884e39a5722Seric */
885e39a5722Seric 
886d51cd7e5Seric bool
887e39a5722Seric unedit(fn)
888e39a5722Seric 	char *fn;
889e39a5722Seric {
890e39a5722Seric 	register FILE *pfp;
891e39a5722Seric 	char *pfn;
892e39a5722Seric 	static char tfn[] = "/tmp/sccsXXXXX";
893e39a5722Seric 	FILE *tfp;
894e39a5722Seric 	register char *p;
895e39a5722Seric 	register char *q;
896e39a5722Seric 	bool delete = FALSE;
897e39a5722Seric 	bool others = FALSE;
898e39a5722Seric 	char *myname;
899e39a5722Seric 	extern char *getlogin();
900e39a5722Seric 	struct pfile *pent;
901e39a5722Seric 	extern struct pfile *getpfile();
902e39a5722Seric 	char buf[120];
903108d6082Seric 	extern char *makefile();
90410cd16efSeric # ifdef UIDUSER
90510cd16efSeric 	struct passwd *pw;
90610cd16efSeric 	extern struct passwd *getpwuid();
90710cd16efSeric # endif UIDUSER
908e39a5722Seric 
909e39a5722Seric 	/* make "s." filename & find the trailing component */
910e39a5722Seric 	pfn = makefile(fn);
911cdc1aa65Seric 	if (pfn == NULL)
912cdc1aa65Seric 		return (FALSE);
913cdc1aa65Seric 	q = rindex(pfn, '/');
914cdc1aa65Seric 	if (q == NULL)
915cdc1aa65Seric 		q = &pfn[-1];
916cdc1aa65Seric 	if (q[1] != 's' || q[2] != '.')
917e39a5722Seric 	{
918095915d3Seric 		usrerr("bad file name \"%s\"", fn);
919d51cd7e5Seric 		return (FALSE);
920e39a5722Seric 	}
921e39a5722Seric 
922ff038098Seric 	/* turn "s." into "p." & try to open it */
923e39a5722Seric 	*++q = 'p';
924e39a5722Seric 
925e39a5722Seric 	pfp = fopen(pfn, "r");
926e39a5722Seric 	if (pfp == NULL)
927e39a5722Seric 	{
9282444cd3eSeric 		printf("%12s: not being edited\n", fn);
929d51cd7e5Seric 		return (FALSE);
930e39a5722Seric 	}
931e39a5722Seric 
932ff038098Seric 	/* create temp file for editing p-file */
933e39a5722Seric 	mktemp(tfn);
934e39a5722Seric 	tfp = fopen(tfn, "w");
935e39a5722Seric 	if (tfp == NULL)
936e39a5722Seric 	{
937095915d3Seric 		usrerr("cannot create \"%s\"", tfn);
938e39a5722Seric 		exit(EX_OSERR);
939e39a5722Seric 	}
940e39a5722Seric 
941ff038098Seric 	/* figure out who I am */
94210cd16efSeric # ifdef UIDUSER
94310cd16efSeric 	pw = getpwuid(getuid());
94410cd16efSeric 	if (pw == NULL)
94510cd16efSeric 	{
946095915d3Seric 		syserr("who are you? (uid=%d)", getuid());
94710cd16efSeric 		exit(EX_OSERR);
94810cd16efSeric 	}
94910cd16efSeric 	myname = pw->pw_name;
95010cd16efSeric # else
951e39a5722Seric 	myname = getlogin();
95210cd16efSeric # endif UIDUSER
953ff038098Seric 
954ff038098Seric 	/*
955ff038098Seric 	**  Copy p-file to temp file, doing deletions as needed.
956ff038098Seric 	*/
957ff038098Seric 
958e39a5722Seric 	while ((pent = getpfile(pfp)) != NULL)
959e39a5722Seric 	{
960e39a5722Seric 		if (strcmp(pent->p_user, myname) == 0)
961e39a5722Seric 		{
962e39a5722Seric 			/* a match */
963e39a5722Seric 			delete++;
964e39a5722Seric 		}
965e39a5722Seric 		else
966e39a5722Seric 		{
967ff038098Seric 			/* output it again */
968e39a5722Seric 			fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid,
969e39a5722Seric 			    pent->p_nsid, pent->p_user, pent->p_date,
970e39a5722Seric 			    pent->p_time);
971e39a5722Seric 			others++;
972e39a5722Seric 		}
973e39a5722Seric 	}
974e39a5722Seric 
975e39a5722Seric 	/* do final cleanup */
976e39a5722Seric 	if (others)
977e39a5722Seric 	{
978ff038098Seric 		/* copy it back (perhaps it should be linked?) */
979e39a5722Seric 		if (freopen(tfn, "r", tfp) == NULL)
980e39a5722Seric 		{
981095915d3Seric 			syserr("cannot reopen \"%s\"", tfn);
982e39a5722Seric 			exit(EX_OSERR);
983e39a5722Seric 		}
984e39a5722Seric 		if (freopen(pfn, "w", pfp) == NULL)
985e39a5722Seric 		{
986095915d3Seric 			usrerr("cannot create \"%s\"", pfn);
987d51cd7e5Seric 			return (FALSE);
988e39a5722Seric 		}
989e39a5722Seric 		while (fgets(buf, sizeof buf, tfp) != NULL)
990e39a5722Seric 			fputs(buf, pfp);
991e39a5722Seric 	}
992e39a5722Seric 	else
993e39a5722Seric 	{
994ff038098Seric 		/* it's empty -- remove it */
995e39a5722Seric 		unlink(pfn);
996e39a5722Seric 	}
997e39a5722Seric 	fclose(tfp);
998e39a5722Seric 	fclose(pfp);
999e39a5722Seric 	unlink(tfn);
1000e39a5722Seric 
1001ff038098Seric 	/* actually remove the g-file */
1002e39a5722Seric 	if (delete)
1003e39a5722Seric 	{
1004287bbaa2Seric 		unlink(tail(fn));
1005287bbaa2Seric 		printf("%12s: removed\n", tail(fn));
1006d51cd7e5Seric 		return (TRUE);
1007e39a5722Seric 	}
1008e39a5722Seric 	else
1009e39a5722Seric 	{
10102444cd3eSeric 		printf("%12s: not being edited by you\n", fn);
1011d51cd7e5Seric 		return (FALSE);
1012e39a5722Seric 	}
1013e39a5722Seric }
10140faf63f9Seric 
10150faf63f9Seric /*
101678d41c6aSeric **  DODIFF -- diff an s-file against a g-file
101778d41c6aSeric **
101878d41c6aSeric **	Parameters:
101978d41c6aSeric **		getv -- argv for the 'get' command.
102078d41c6aSeric **		gfile -- name of the g-file to diff against.
102178d41c6aSeric **
102278d41c6aSeric **	Returns:
102378d41c6aSeric **		Result of get.
102478d41c6aSeric **
102578d41c6aSeric **	Side Effects:
102678d41c6aSeric **		none.
102778d41c6aSeric */
102878d41c6aSeric 
102978d41c6aSeric dodiff(getv, gfile)
103078d41c6aSeric 	char **getv;
103178d41c6aSeric 	char *gfile;
103278d41c6aSeric {
103378d41c6aSeric 	int pipev[2];
103478d41c6aSeric 	int rval;
103578d41c6aSeric 	register int i;
103678d41c6aSeric 	register int pid;
103778d41c6aSeric 	auto int st;
103878d41c6aSeric 	extern int errno;
103978d41c6aSeric 	int (*osig)();
104078d41c6aSeric 
1041*cc4e4f0dSeric 	printf("\n------- %s -------\n", gfile);
1042*cc4e4f0dSeric 
1043ff038098Seric 	/* create context for diff to run in */
104478d41c6aSeric 	if (pipe(pipev) < 0)
104578d41c6aSeric 	{
104678d41c6aSeric 		syserr("dodiff: pipe failed");
104778d41c6aSeric 		exit(EX_OSERR);
104878d41c6aSeric 	}
104978d41c6aSeric 	if ((pid = fork()) < 0)
105078d41c6aSeric 	{
105178d41c6aSeric 		syserr("dodiff: fork failed");
105278d41c6aSeric 		exit(EX_OSERR);
105378d41c6aSeric 	}
105478d41c6aSeric 	else if (pid > 0)
105578d41c6aSeric 	{
105678d41c6aSeric 		/* in parent; run get */
105778d41c6aSeric 		OutFile = pipev[1];
105878d41c6aSeric 		close(pipev[0]);
105978d41c6aSeric 		rval = command(&getv[1], TRUE, FALSE, "get -s -k -p");
106078d41c6aSeric 		osig = signal(SIGINT, SIG_IGN);
106178d41c6aSeric 		while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR)
106278d41c6aSeric 			errno = 0;
106378d41c6aSeric 		signal(SIGINT, osig);
106478d41c6aSeric 		/* ignore result of diff */
106578d41c6aSeric 	}
106678d41c6aSeric 	else
106778d41c6aSeric 	{
106878d41c6aSeric 		/* in child, run diff */
106978d41c6aSeric 		if (close(pipev[1]) < 0 || close(0) < 0 ||
107078d41c6aSeric 		    dup(pipev[0]) != 0 || close(pipev[0]) < 0)
107178d41c6aSeric 		{
107278d41c6aSeric 			syserr("dodiff: magic failed");
107378d41c6aSeric 			exit(EX_OSERR);
107478d41c6aSeric 		}
107578d41c6aSeric 		execl(PROGPATH(bdiff), "bdiff", "-", gfile, NULL);
107678d41c6aSeric # ifndef V6
107778d41c6aSeric 		execlp("bdiff", "bdiff", "-", gfile, NULL);
107878d41c6aSeric 		execlp("diff", "diff", "-", gfile, NULL);
107978d41c6aSeric # endif NOT V6
108078d41c6aSeric 		syserr("bdiff: cannot execute");
108178d41c6aSeric 		exit(EX_OSERR);
108278d41c6aSeric 	}
108378d41c6aSeric 	return (rval);
108478d41c6aSeric }
108578d41c6aSeric 
108678d41c6aSeric /*
1087287bbaa2Seric **  TAIL -- return tail of filename.
1088287bbaa2Seric **
1089287bbaa2Seric **	Parameters:
1090287bbaa2Seric **		fn -- the filename.
1091287bbaa2Seric **
1092287bbaa2Seric **	Returns:
1093287bbaa2Seric **		a pointer to the tail of the filename; e.g., given
1094287bbaa2Seric **		"cmd/ls.c", "ls.c" is returned.
1095287bbaa2Seric **
1096287bbaa2Seric **	Side Effects:
1097287bbaa2Seric **		none.
1098287bbaa2Seric */
1099287bbaa2Seric 
1100287bbaa2Seric char *
1101287bbaa2Seric tail(fn)
1102287bbaa2Seric 	register char *fn;
1103287bbaa2Seric {
1104287bbaa2Seric 	register char *p;
1105287bbaa2Seric 
1106287bbaa2Seric 	for (p = fn; *p != 0; p++)
1107287bbaa2Seric 		if (*p == '/' && p[1] != '\0' && p[1] != '/')
1108287bbaa2Seric 			fn = &p[1];
1109287bbaa2Seric 	return (fn);
1110287bbaa2Seric }
1111287bbaa2Seric 
1112287bbaa2Seric /*
1113e39a5722Seric **  GETPFILE -- get an entry from the p-file
1114e39a5722Seric **
1115e39a5722Seric **	Parameters:
1116e39a5722Seric **		pfp -- p-file file pointer
1117e39a5722Seric **
1118e39a5722Seric **	Returns:
1119e39a5722Seric **		pointer to p-file struct for next entry
1120e39a5722Seric **		NULL on EOF or error
1121e39a5722Seric **
1122e39a5722Seric **	Side Effects:
1123e39a5722Seric **		Each call wipes out results of previous call.
1124e39a5722Seric */
1125e39a5722Seric 
1126e39a5722Seric struct pfile *
1127e39a5722Seric getpfile(pfp)
1128e39a5722Seric 	FILE *pfp;
1129e39a5722Seric {
1130e39a5722Seric 	static struct pfile ent;
1131e39a5722Seric 	static char buf[120];
1132e39a5722Seric 	register char *p;
1133e39a5722Seric 	extern char *nextfield();
1134e39a5722Seric 
1135e39a5722Seric 	if (fgets(buf, sizeof buf, pfp) == NULL)
1136e39a5722Seric 		return (NULL);
1137e39a5722Seric 
1138e39a5722Seric 	ent.p_osid = p = buf;
1139e39a5722Seric 	ent.p_nsid = p = nextfield(p);
1140e39a5722Seric 	ent.p_user = p = nextfield(p);
1141e39a5722Seric 	ent.p_date = p = nextfield(p);
1142e39a5722Seric 	ent.p_time = p = nextfield(p);
1143e39a5722Seric 	if (p == NULL || nextfield(p) != NULL)
1144e39a5722Seric 		return (NULL);
1145e39a5722Seric 
1146e39a5722Seric 	return (&ent);
1147e39a5722Seric }
1148e39a5722Seric 
1149e39a5722Seric 
1150e39a5722Seric char *
1151e39a5722Seric nextfield(p)
1152e39a5722Seric 	register char *p;
1153e39a5722Seric {
1154e39a5722Seric 	if (p == NULL || *p == '\0')
1155e39a5722Seric 		return (NULL);
1156e39a5722Seric 	while (*p != ' ' && *p != '\n' && *p != '\0')
1157e39a5722Seric 		p++;
1158e39a5722Seric 	if (*p == '\n' || *p == '\0')
1159e39a5722Seric 	{
1160e39a5722Seric 		*p = '\0';
1161e39a5722Seric 		return (NULL);
1162e39a5722Seric 	}
1163e39a5722Seric 	*p++ = '\0';
1164e39a5722Seric 	return (p);
1165e39a5722Seric }
11660faf63f9Seric 
11670faf63f9Seric /*
1168095915d3Seric **  USRERR -- issue user-level error
1169095915d3Seric **
1170095915d3Seric **	Parameters:
1171095915d3Seric **		f -- format string.
1172095915d3Seric **		p1-p3 -- parameters to a printf.
1173095915d3Seric **
1174095915d3Seric **	Returns:
1175095915d3Seric **		-1
1176095915d3Seric **
1177095915d3Seric **	Side Effects:
1178095915d3Seric **		none.
1179095915d3Seric */
1180095915d3Seric 
1181095915d3Seric usrerr(f, p1, p2, p3)
1182095915d3Seric 	char *f;
1183095915d3Seric {
1184095915d3Seric 	fprintf(stderr, "\n%s: ", MyName);
1185095915d3Seric 	fprintf(stderr, f, p1, p2, p3);
1186095915d3Seric 	fprintf(stderr, "\n");
1187095915d3Seric 
1188095915d3Seric 	return (-1);
1189095915d3Seric }
11900faf63f9Seric 
11910faf63f9Seric /*
1192095915d3Seric **  SYSERR -- print system-generated error.
1193095915d3Seric **
1194095915d3Seric **	Parameters:
1195095915d3Seric **		f -- format string to a printf.
1196095915d3Seric **		p1, p2, p3 -- parameters to f.
1197095915d3Seric **
1198095915d3Seric **	Returns:
1199095915d3Seric **		never.
1200095915d3Seric **
1201095915d3Seric **	Side Effects:
1202095915d3Seric **		none.
1203095915d3Seric */
1204095915d3Seric 
1205095915d3Seric syserr(f, p1, p2, p3)
1206095915d3Seric 	char *f;
1207095915d3Seric {
1208095915d3Seric 	extern int errno;
1209095915d3Seric 
1210095915d3Seric 	fprintf(stderr, "\n%s SYSERR: ", MyName);
1211095915d3Seric 	fprintf(stderr, f, p1, p2, p3);
1212095915d3Seric 	fprintf(stderr, "\n");
1213095915d3Seric 	if (errno == 0)
1214095915d3Seric 		exit(EX_SOFTWARE);
1215095915d3Seric 	else
1216095915d3Seric 	{
1217095915d3Seric 		perror(0);
1218095915d3Seric 		exit(EX_OSERR);
1219095915d3Seric 	}
1220095915d3Seric }
1221