xref: /csrg-svn/usr.bin/sccs/sccs.c (revision 1433)
1148Seric # include <stdio.h>
2148Seric # include <sys/types.h>
3148Seric # include <sys/stat.h>
4261Seric # include <sys/dir.h>
5*1433Seric # include <errno.h>
6*1433Seric # include <signal.h>
7148Seric # include <sysexits.h>
8202Seric # include <whoami.h>
9148Seric 
10828Seric /*
11828Seric **  SCCS.C -- human-oriented front end to the SCCS system.
12828Seric **
13828Seric **	Without trying to add any functionality to speak of, this
14828Seric **	program tries to make SCCS a little more accessible to human
15828Seric **	types.  The main thing it does is automatically put the
16828Seric **	string "SCCS/s." on the front of names.  Also, it has a
17828Seric **	couple of things that are designed to shorten frequent
18828Seric **	combinations, e.g., "delget" which expands to a "delta"
19828Seric **	and a "get".
20828Seric **
21828Seric **	This program can also function as a setuid front end.
22828Seric **	To do this, you should copy the source, renaming it to
23828Seric **	whatever you want, e.g., "syssccs".  Change any defaults
24828Seric **	in the program (e.g., syssccs might default -d to
25828Seric **	"/usr/src/sys").  Then recompile and put the result
26828Seric **	as setuid to whomever you want.  In this mode, sccs
27828Seric **	knows to not run setuid for certain programs in order
28828Seric **	to preserve security, and so forth.
29828Seric **
30828Seric **	Usage:
31828Seric **		sccs [flags] command [args]
32828Seric **
33828Seric **	Flags:
34828Seric **		-d<dir>		<dir> represents a directory to search
35828Seric **				out of.  It should be a full pathname
36828Seric **				for general usage.  E.g., if <dir> is
37828Seric **				"/usr/src/sys", then a reference to the
38828Seric **				file "dev/bio.c" becomes a reference to
39828Seric **				"/usr/src/sys/dev/bio.c".
40828Seric **		-p<path>	prepends <path> to the final component
41828Seric **				of the pathname.  By default, this is
42828Seric **				"SCCS".  For example, in the -d example
43828Seric **				above, the path then gets modified to
44828Seric **				"/usr/src/sys/dev/SCCS/s.bio.c".  In
45828Seric **				more common usage (without the -d flag),
46828Seric **				"prog.c" would get modified to
47828Seric **				"SCCS/s.prog.c".  In both cases, the
48828Seric **				"s." gets automatically prepended.
49828Seric **		-r		run as the real user.
50828Seric **
51828Seric **	Commands:
52828Seric **		admin,
53828Seric **		get,
54828Seric **		delta,
55828Seric **		rmdel,
56828Seric **		chghist,
57828Seric **		etc.		Straight out of SCCS; only difference
58828Seric **				is that pathnames get modified as
59828Seric **				described above.
60828Seric **		edit		Macro for "get -e".
61828Seric **		unedit		Removes a file being edited, knowing
62828Seric **				about p-files, etc.
63828Seric **		delget		Macro for "delta" followed by "get".
64828Seric **		deledit		Macro for "delta" followed by "get -e".
65828Seric **		info		Tell what files being edited.
66828Seric **		clean		Remove all files that can be
67828Seric **				regenerated from SCCS files.
681205Seric **		check		Like info, but return exit status, for
69828Seric **				use in makefiles.
70828Seric **		fix		Remove a top delta & reedit, but save
71828Seric **				the previous changes in that delta.
72828Seric **
73828Seric **	Compilation Flags:
74828Seric **		UIDUSER -- determine who the user is by looking at the
75828Seric **			uid rather than the login name -- for machines
76828Seric **			where SCCS gets the user in this way.
771270Seric **		SCCSDIR -- if defined, forces the -d flag to take on
781205Seric **			this value.  This is so that the setuid
791205Seric **			aspects of this program cannot be abused.
801270Seric **			This flag also disables the -p flag.
811270Seric **		SCCSPATH -- the default for the -p flag.
82828Seric **
83828Seric **	Compilation Instructions:
84828Seric **		cc -O -n -s sccs.c
85828Seric **
86828Seric **	Author:
87828Seric **		Eric Allman, UCB/INGRES
881270Seric **		Copyright 1980 Regents of the University of California
89828Seric */
90155Seric 
91*1433Seric static char SccsId[] = "@(#)sccs.c	1.36 10/15/80";
921432Seric 
931270Seric /*******************  Configuration Information  ********************/
941270Seric 
95828Seric # ifdef CSVAX
96828Seric # define UIDUSER
971270Seric # define PROGPATH(name)	"/usr/local/name"
981270Seric # endif CSVAX
99828Seric 
1001432Seric # define SCCSPATH	"SCCS"	/* pathname in which to find s-files */
1011270Seric /* put #define SCCSDIR here */
102828Seric 
1031270Seric char	MyName[] = "sccs";	/* name used in messages */
1041270Seric 
1051432Seric # ifndef PROGPATH
1061432Seric # define PROGPATH(name)	"/usr/sccs/name"	/* place to find binaries */
1071432Seric # endif PROGPATH
1081432Seric 
1091270Seric /****************  End of Configuration Information  ****************/
1101432Seric 
111157Seric # define bitset(bit, word)	((bit) & (word))
112157Seric 
113157Seric typedef char	bool;
114200Seric # define TRUE	1
115200Seric # define FALSE	0
116157Seric 
117828Seric # ifdef UIDUSER
118828Seric # include <pwd.h>
119828Seric # endif UIDUSER
120828Seric 
121148Seric struct sccsprog
122148Seric {
123148Seric 	char	*sccsname;	/* name of SCCS routine */
124200Seric 	short	sccsoper;	/* opcode, see below */
125200Seric 	short	sccsflags;	/* flags, see below */
1261316Seric 	char	*sccsklets;	/* valid key-letters on macros */
127148Seric 	char	*sccspath;	/* pathname of binary implementing */
128148Seric };
129148Seric 
130200Seric /* values for sccsoper */
131200Seric # define PROG		0	/* call a program */
132201Seric # define CMACRO		1	/* command substitution macro */
133226Seric # define FIX		2	/* fix a delta */
134261Seric # define CLEAN		3	/* clean out recreatable files */
135396Seric # define UNEDIT		4	/* unedit a file */
1361431Seric # define SHELL		5	/* call a shell file (like PROG) */
137*1433Seric # define DIFFS		6	/* diff between sccs & file out */
138200Seric 
139157Seric /* bits for sccsflags */
140200Seric # define NO_SDOT	0001	/* no s. on front of args */
141200Seric # define REALUSER	0002	/* protected (e.g., admin) */
142148Seric 
143819Seric /* modes for the "clean", "info", "check" ops */
144819Seric # define CLEANC		0	/* clean command */
145819Seric # define INFOC		1	/* info command */
146819Seric # define CHECKC		2	/* check command */
147819Seric 
1481432Seric /*
1491432Seric **  Description of commands known to this program.
1501432Seric **	First argument puts the command into a class.  Second arg is
1511432Seric **	info regarding treatment of this command.  Third arg is a
1521432Seric **	list of flags this command accepts from macros, etc.  Fourth
1531432Seric **	arg is the pathname of the implementing program, or the
1541432Seric **	macro definition, or the arg to a sub-algorithm.
1551432Seric */
156202Seric 
157148Seric struct sccsprog SccsProg[] =
158148Seric {
1591316Seric 	"admin",	PROG,	REALUSER,	"",		PROGPATH(admin),
1601316Seric 	"chghist",	PROG,	0,		"",		PROGPATH(rmdel),
1611316Seric 	"comb",		PROG,	0,		"",		PROGPATH(comb),
1621316Seric 	"delta",	PROG,	0,		"mysrp",	PROGPATH(delta),
1631317Seric 	"get",		PROG,	0,		"ixbeskcl",	PROGPATH(get),
1641316Seric 	"help",		PROG,	NO_SDOT,	"",		PROGPATH(help),
1651316Seric 	"prt",		PROG,	0,		"",		PROGPATH(prt),
1661316Seric 	"rmdel",	PROG,	REALUSER,	"",		PROGPATH(rmdel),
1671316Seric 	"what",		PROG,	NO_SDOT,	"",		PROGPATH(what),
1681431Seric 	"sccsdiff",	SHELL,	REALUSER,	"",		PROGPATH(sccsdiff),
1691317Seric 	"edit",		CMACRO,	NO_SDOT,	"ixbscl",	"get -e",
1701316Seric 	"delget",	CMACRO,	NO_SDOT,	"",		"delta/get -t",
1711316Seric 	"deledit",	CMACRO,	NO_SDOT,	"",		"delta/get -e -t",
1721316Seric 	"fix",		FIX,	NO_SDOT,	"",		NULL,
1731316Seric 	"clean",	CLEAN,	REALUSER,	"",		(char *) CLEANC,
1741316Seric 	"info",		CLEAN,	REALUSER,	"",		(char *) INFOC,
1751316Seric 	"check",	CLEAN,	REALUSER,	"",		(char *) CHECKC,
1761316Seric 	"unedit",	UNEDIT,	NO_SDOT,	"",		NULL,
177*1433Seric 	"diffs",	DIFFS,	NO_SDOT|REALUSER, "",		NULL,
1781316Seric 	NULL,		-1,	0,		"",		NULL
179148Seric };
180148Seric 
1811432Seric /* one line from a p-file */
182396Seric struct pfile
183396Seric {
184396Seric 	char	*p_osid;	/* old SID */
185396Seric 	char	*p_nsid;	/* new SID */
186396Seric 	char	*p_user;	/* user who did edit */
187396Seric 	char	*p_date;	/* date of get */
188396Seric 	char	*p_time;	/* time of get */
189396Seric };
190396Seric 
1911270Seric char	*SccsPath = SCCSPATH;	/* pathname of SCCS files */
1921270Seric # ifdef SCCSDIR
1931270Seric char	*SccsDir = SCCSDIR;	/* directory to begin search from */
1941205Seric # else
1951270Seric char	*SccsDir = "";
1961205Seric # endif
197*1433Seric int	OutFile = -1;		/* override output file for commands */
198157Seric bool	RealUser;		/* if set, running as real user */
199393Seric # ifdef DEBUG
200393Seric bool	Debug;			/* turn on tracing */
201393Seric # endif
2021432Seric 
203148Seric main(argc, argv)
204148Seric 	int argc;
205148Seric 	char **argv;
206148Seric {
207148Seric 	register char *p;
208262Seric 	extern struct sccsprog *lookup();
2091282Seric 	register int i;
210148Seric 
211148Seric 	/*
212148Seric 	**  Detect and decode flags intended for this program.
213148Seric 	*/
214148Seric 
215200Seric 	if (argc < 2)
216148Seric 	{
2171205Seric 		fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName);
218200Seric 		exit(EX_USAGE);
219200Seric 	}
220200Seric 	argv[argc] = NULL;
221200Seric 
222262Seric 	if (lookup(argv[0]) == NULL)
223200Seric 	{
224262Seric 		while ((p = *++argv) != NULL)
225148Seric 		{
226262Seric 			if (*p != '-')
227262Seric 				break;
228262Seric 			switch (*++p)
229262Seric 			{
230262Seric 			  case 'r':		/* run as real user */
231262Seric 				setuid(getuid());
232262Seric 				RealUser++;
233262Seric 				break;
234148Seric 
2351270Seric # ifndef SCCSDIR
236262Seric 			  case 'p':		/* path of sccs files */
237262Seric 				SccsPath = ++p;
238262Seric 				break;
239148Seric 
240588Seric 			  case 'd':		/* directory to search from */
241588Seric 				SccsDir = ++p;
242588Seric 				break;
2431205Seric # endif
244588Seric 
245393Seric # ifdef DEBUG
246393Seric 			  case 'T':		/* trace */
247393Seric 				Debug++;
248393Seric 				break;
249393Seric # endif
250393Seric 
251262Seric 			  default:
2521205Seric 				usrerr("unknown option -%s", p);
253262Seric 				break;
254262Seric 			}
255148Seric 		}
256262Seric 		if (SccsPath[0] == '\0')
257262Seric 			SccsPath = ".";
258148Seric 	}
259148Seric 
2601316Seric 	i = command(argv, FALSE, FALSE, "");
2611282Seric 	exit(i);
262200Seric }
2631432Seric 
2641432Seric /*
2651282Seric **  COMMAND -- look up and perform a command
2661282Seric **
2671282Seric **	This routine is the guts of this program.  Given an
2681282Seric **	argument vector, it looks up the "command" (argv[0])
2691282Seric **	in the configuration table and does the necessary stuff.
2701282Seric **
2711282Seric **	Parameters:
2721282Seric **		argv -- an argument vector to process.
2731282Seric **		forkflag -- if set, fork before executing the command.
2741316Seric **		editflag -- if set, only include flags listed in the
2751316Seric **			sccsklets field of the command descriptor.
2761316Seric **		arg0 -- a space-seperated list of arguments to insert
2771316Seric **			before argv.
2781282Seric **
2791282Seric **	Returns:
2801282Seric **		zero -- command executed ok.
2811282Seric **		else -- error status.
2821282Seric **
2831282Seric **	Side Effects:
2841282Seric **		none.
2851282Seric */
286157Seric 
2871316Seric command(argv, forkflag, editflag, arg0)
288200Seric 	char **argv;
289201Seric 	bool forkflag;
2901316Seric 	bool editflag;
2911316Seric 	char *arg0;
292200Seric {
293200Seric 	register struct sccsprog *cmd;
294200Seric 	register char *p;
295201Seric 	char buf[40];
296262Seric 	extern struct sccsprog *lookup();
2971316Seric 	char *nav[1000];
2981316Seric 	char **np;
2991431Seric 	register char **ap;
300585Seric 	register int i;
3011431Seric 	register char *q;
302585Seric 	extern bool unedit();
3031282Seric 	int rval = 0;
3041316Seric 	extern char *index();
3051316Seric 	extern char *makefile();
306200Seric 
307393Seric # ifdef DEBUG
308393Seric 	if (Debug)
309393Seric 	{
3101316Seric 		printf("command:\n\t\"%s\"\n", arg0);
3111316Seric 		for (np = argv; *np != NULL; np++)
3121316Seric 			printf("\t\"%s\"\n", *np);
313393Seric 	}
314393Seric # endif
315393Seric 
316157Seric 	/*
3171316Seric 	**  Copy arguments.
3181316Seric 	**	Phase one -- from arg0 & if necessary argv[0].
3191316Seric 	*/
3201316Seric 
3211431Seric 	np = ap = &nav[1];
3221316Seric 	for (p = arg0, q = buf; *p != '\0' && *p != '/'; )
3231316Seric 	{
3241316Seric 		*np++ = q;
3251316Seric 		while (*p == ' ')
3261316Seric 			p++;
3271316Seric 		while (*p != ' ' && *p != '\0' && *p != '/')
3281316Seric 			*q++ = *p++;
3291316Seric 		*q++ = '\0';
3301316Seric 	}
3311316Seric 	*np = NULL;
3321431Seric 	if (*ap == NULL)
3331316Seric 		*np++ = *argv++;
3341316Seric 
3351316Seric 	/*
336148Seric 	**  Look up command.
3371431Seric 	**	At this point, *ap is the command name.
338148Seric 	*/
339148Seric 
3401431Seric 	cmd = lookup(*ap);
341262Seric 	if (cmd == NULL)
342148Seric 	{
3431431Seric 		usrerr("Unknown command \"%s\"", *ap);
3441282Seric 		return (EX_USAGE);
345148Seric 	}
346148Seric 
347148Seric 	/*
3481316Seric 	**  Copy remaining arguments doing editing as appropriate.
3491316Seric 	*/
3501316Seric 
3511316Seric 	for (; *argv != NULL; argv++)
3521316Seric 	{
3531316Seric 		p = *argv;
3541316Seric 		if (*p == '-')
3551316Seric 		{
3561316Seric 			if (p[1] == '\0' || !editflag || cmd->sccsklets == NULL ||
3571316Seric 			    index(cmd->sccsklets, p[1]) != NULL)
3581316Seric 				*np++ = p;
3591316Seric 		}
3601316Seric 		else
3611316Seric 		{
3621316Seric 			if (!bitset(NO_SDOT, cmd->sccsflags))
3631316Seric 				p = makefile(p);
3641316Seric 			if (p != NULL)
3651316Seric 				*np++ = p;
3661316Seric 		}
3671316Seric 	}
3681316Seric 	*np = NULL;
3691316Seric 
3701316Seric 	/*
371200Seric 	**  Interpret operation associated with this command.
372157Seric 	*/
373157Seric 
374200Seric 	switch (cmd->sccsoper)
375200Seric 	{
3761431Seric 	  case SHELL:		/* call a shell file */
3771431Seric 		*ap = cmd->sccspath;
3781431Seric 		*--ap = "sh";
3791431Seric 		rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag);
3801431Seric 		break;
3811431Seric 
382200Seric 	  case PROG:		/* call an sccs prog */
3831431Seric 		rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag);
384201Seric 		break;
385201Seric 
386201Seric 	  case CMACRO:		/* command macro */
387201Seric 		for (p = cmd->sccspath; *p != '\0'; p++)
388201Seric 		{
3891316Seric 			q = p;
3901316Seric 			while (*p != '\0' && *p != '/')
3911316Seric 				p++;
3921431Seric 			rval = command(&ap[1], *p != '\0', TRUE, q);
3931282Seric 			if (rval != 0)
3941282Seric 				break;
395201Seric 		}
3961282Seric 		break;
397157Seric 
398226Seric 	  case FIX:		/* fix a delta */
3991431Seric 		if (strncmp(ap[1], "-r", 2) != 0)
400226Seric 		{
4011205Seric 			usrerr("-r flag needed for fix command");
4021282Seric 			rval = EX_USAGE;
403226Seric 			break;
404226Seric 		}
4051431Seric 		rval = command(&ap[1], TRUE, TRUE, "get -k");
4061282Seric 		if (rval == 0)
4071431Seric 			rval = command(&ap[1], TRUE, TRUE, "rmdel");
4081282Seric 		if (rval == 0)
4091431Seric 			rval = command(&ap[2], FALSE, TRUE, "get -e -g");
4101282Seric 		break;
411226Seric 
412261Seric 	  case CLEAN:
4131282Seric 		rval = clean((int) cmd->sccspath);
414261Seric 		break;
415261Seric 
416396Seric 	  case UNEDIT:
4171431Seric 		for (argv = np = &ap[1]; *argv != NULL; argv++)
418585Seric 		{
4191316Seric 			if (unedit(*argv))
4201316Seric 				*np++ = *argv;
421585Seric 		}
4221316Seric 		*np = NULL;
423585Seric 		if (i > 0)
4241431Seric 			rval = command(&ap[1], FALSE, FALSE, "get");
425396Seric 		break;
426396Seric 
427*1433Seric 	  case DIFFS:		/* diff between s-file & edit file */
428*1433Seric 		/* find the end of the flag arguments */
429*1433Seric 		for (np = &ap[1]; *np != NULL && **np == '-'; np++)
430*1433Seric 			continue;
431*1433Seric 		argv = np;
432*1433Seric 
433*1433Seric 		/* for each file, do the diff */
434*1433Seric 		while (*np != NULL)
435*1433Seric 		{
436*1433Seric 			*argv = *np++;
437*1433Seric 			p = *np;
438*1433Seric 			*np = NULL;
439*1433Seric 			i = dodiff(ap, *argv);
440*1433Seric 			if (rval == 0)
441*1433Seric 				rval = i;
442*1433Seric 			*np = p;
443*1433Seric 		}
444*1433Seric 		break;
445*1433Seric 
446200Seric 	  default:
4471205Seric 		syserr("oper %d", cmd->sccsoper);
448200Seric 		exit(EX_SOFTWARE);
449200Seric 	}
4501282Seric # ifdef DEBUG
4511282Seric 	if (Debug)
4521282Seric 		printf("command: rval=%d\n", rval);
4531282Seric # endif
4541282Seric 	return (rval);
455200Seric }
4561432Seric 
4571432Seric /*
458262Seric **  LOOKUP -- look up an SCCS command name.
459262Seric **
460262Seric **	Parameters:
461262Seric **		name -- the name of the command to look up.
462262Seric **
463262Seric **	Returns:
464262Seric **		ptr to command descriptor for this command.
465262Seric **		NULL if no such entry.
466262Seric **
467262Seric **	Side Effects:
468262Seric **		none.
469262Seric */
470200Seric 
471262Seric struct sccsprog *
472262Seric lookup(name)
473262Seric 	char *name;
474262Seric {
475262Seric 	register struct sccsprog *cmd;
476226Seric 
477262Seric 	for (cmd = SccsProg; cmd->sccsname != NULL; cmd++)
478262Seric 	{
479262Seric 		if (strcmp(cmd->sccsname, name) == 0)
480262Seric 			return (cmd);
481262Seric 	}
482262Seric 	return (NULL);
483262Seric }
4841432Seric 
4851432Seric /*
4861282Seric **  CALLPROG -- call a program
4871282Seric **
4881316Seric **	Used to call the SCCS programs.
4891282Seric **
4901282Seric **	Parameters:
4911282Seric **		progpath -- pathname of the program to call.
4921282Seric **		flags -- status flags from the command descriptors.
4931282Seric **		argv -- an argument vector to pass to the program.
4941282Seric **		forkflag -- if true, fork before calling, else just
4951282Seric **			exec.
4961282Seric **
4971282Seric **	Returns:
4981282Seric **		The exit status of the program.
4991282Seric **		Nothing if forkflag == FALSE.
5001282Seric **
5011282Seric **	Side Effects:
5021282Seric **		Can exit if forkflag == FALSE.
5031282Seric */
504226Seric 
505200Seric callprog(progpath, flags, argv, forkflag)
506200Seric 	char *progpath;
507200Seric 	short flags;
508200Seric 	char **argv;
509200Seric 	bool forkflag;
510200Seric {
511200Seric 	register int i;
512201Seric 	auto int st;
513200Seric 
5141316Seric # ifdef DEBUG
5151316Seric 	if (Debug)
5161316Seric 	{
5171316Seric 		printf("callprog:\n");
5181316Seric 		for (i = 0; argv[i] != NULL; i++)
5191316Seric 			printf("\t\"%s\"\n", argv[i]);
5201316Seric 	}
5211316Seric # endif
5221316Seric 
523200Seric 	if (*argv == NULL)
524200Seric 		return (-1);
525200Seric 
526157Seric 	/*
527226Seric 	**  Fork if appropriate.
528148Seric 	*/
529148Seric 
530200Seric 	if (forkflag)
531200Seric 	{
532393Seric # ifdef DEBUG
533393Seric 		if (Debug)
534393Seric 			printf("Forking\n");
535393Seric # endif
536200Seric 		i = fork();
537200Seric 		if (i < 0)
538200Seric 		{
5391205Seric 			syserr("cannot fork");
540200Seric 			exit(EX_OSERR);
541200Seric 		}
542200Seric 		else if (i > 0)
543201Seric 		{
544201Seric 			wait(&st);
5451282Seric 			if ((st & 0377) == 0)
5461282Seric 				st = (st >> 8) & 0377;
547*1433Seric 			if (OutFile >= 0)
548*1433Seric 			{
549*1433Seric 				close(OutFile);
550*1433Seric 				OutFile = -1;
551*1433Seric 			}
552201Seric 			return (st);
553201Seric 		}
554200Seric 	}
555*1433Seric 	else if (OutFile >= 0)
556*1433Seric 	{
557*1433Seric 		syserr("callprog: setting stdout w/o forking");
558*1433Seric 		exit(EX_SOFTWARE);
559*1433Seric 	}
560200Seric 
561*1433Seric 	/* set protection as appropriate */
562200Seric 	if (bitset(REALUSER, flags))
563200Seric 		setuid(getuid());
564*1433Seric 
565*1433Seric 	/* change standard input & output if needed */
566*1433Seric 	if (OutFile >= 0)
567*1433Seric 	{
568*1433Seric 		close(1);
569*1433Seric 		dup(OutFile);
570*1433Seric 		close(OutFile);
571*1433Seric 	}
572226Seric 
573*1433Seric 	/* call real SCCS program */
574226Seric 	execv(progpath, argv);
5751205Seric 	syserr("cannot execute %s", progpath);
576148Seric 	exit(EX_UNAVAILABLE);
577148Seric }
5781432Seric 
5791432Seric /*
580586Seric **  MAKEFILE -- make filename of SCCS file
581586Seric **
582586Seric **	If the name passed is already the name of an SCCS file,
583586Seric **	just return it.  Otherwise, munge the name into the name
584586Seric **	of the actual SCCS file.
585586Seric **
586586Seric **	There are cases when it is not clear what you want to
587586Seric **	do.  For example, if SccsPath is an absolute pathname
588586Seric **	and the name given is also an absolute pathname, we go
589586Seric **	for SccsPath (& only use the last component of the name
590586Seric **	passed) -- this is important for security reasons (if
591586Seric **	sccs is being used as a setuid front end), but not
592586Seric **	particularly intuitive.
593586Seric **
594586Seric **	Parameters:
595586Seric **		name -- the file name to be munged.
596586Seric **
597586Seric **	Returns:
598586Seric **		The pathname of the sccs file.
599586Seric **		NULL on error.
600586Seric **
601586Seric **	Side Effects:
602586Seric **		none.
603586Seric */
604148Seric 
605148Seric char *
606148Seric makefile(name)
607148Seric 	char *name;
608148Seric {
609148Seric 	register char *p;
610148Seric 	register char c;
611148Seric 	char buf[512];
612588Seric 	struct stat stbuf;
613148Seric 	extern char *malloc();
614586Seric 	extern char *rindex();
615588Seric 	extern bool safepath();
616587Seric 	extern bool isdir();
617587Seric 	register char *q;
618148Seric 
619586Seric 	p = rindex(name, '/');
620586Seric 	if (p == NULL)
621586Seric 		p = name;
622586Seric 	else
623586Seric 		p++;
624586Seric 
625148Seric 	/*
626588Seric 	**  Check to see that the path is "safe", i.e., that we
627588Seric 	**  are not letting some nasty person use the setuid part
628588Seric 	**  of this program to look at or munge some presumably
629588Seric 	**  hidden files.
630148Seric 	*/
631148Seric 
632588Seric 	if (SccsDir[0] == '/' && !safepath(name))
633588Seric 		return (NULL);
634586Seric 
635586Seric 	/*
636588Seric 	**  Create the base pathname.
637586Seric 	*/
638586Seric 
639588Seric 	if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0)
640148Seric 	{
641588Seric 		strcpy(buf, SccsDir);
642586Seric 		strcat(buf, "/");
643586Seric 	}
644586Seric 	else
645586Seric 		strcpy(buf, "");
646587Seric 	strncat(buf, name, p - name);
647587Seric 	q = &buf[strlen(buf)];
648587Seric 	strcpy(q, p);
649587Seric 	if (strncmp(p, "s.", 2) != 0 && !isdir(buf))
650586Seric 	{
651588Seric 		strcpy(q, SccsPath);
652588Seric 		strcat(buf, "/s.");
653586Seric 		strcat(buf, p);
654586Seric 	}
655148Seric 
656588Seric 	if (strcmp(buf, name) == 0)
657588Seric 		p = name;
658588Seric 	else
659148Seric 	{
660588Seric 		p = malloc(strlen(buf) + 1);
661588Seric 		if (p == NULL)
662588Seric 		{
663588Seric 			perror("Sccs: no mem");
664588Seric 			exit(EX_OSERR);
665588Seric 		}
666588Seric 		strcpy(p, buf);
667148Seric 	}
668148Seric 	return (p);
669148Seric }
6701432Seric 
6711432Seric /*
672587Seric **  ISDIR -- return true if the argument is a directory.
673587Seric **
674587Seric **	Parameters:
675587Seric **		name -- the pathname of the file to check.
676587Seric **
677587Seric **	Returns:
678587Seric **		TRUE if 'name' is a directory, FALSE otherwise.
679587Seric **
680587Seric **	Side Effects:
681587Seric **		none.
682587Seric */
683587Seric 
684587Seric bool
685587Seric isdir(name)
686587Seric 	char *name;
687587Seric {
688587Seric 	struct stat stbuf;
689587Seric 
690587Seric 	return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR);
691587Seric }
6921432Seric 
6931432Seric /*
694586Seric **  SAFEPATH -- determine whether a pathname is "safe"
695586Seric **
696586Seric **	"Safe" pathnames only allow you to get deeper into the
697586Seric **	directory structure, i.e., full pathnames and ".." are
698586Seric **	not allowed.
699586Seric **
700586Seric **	Parameters:
701586Seric **		p -- the name to check.
702586Seric **
703586Seric **	Returns:
704586Seric **		TRUE -- if the path is safe.
705586Seric **		FALSE -- if the path is not safe.
706586Seric **
707586Seric **	Side Effects:
708586Seric **		Prints a message if the path is not safe.
709586Seric */
710586Seric 
711586Seric bool
712586Seric safepath(p)
713586Seric 	register char *p;
714586Seric {
715586Seric 	extern char *index();
716586Seric 
717586Seric 	if (*p != '/')
718586Seric 	{
719586Seric 		while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0)
720586Seric 		{
721586Seric 			p = index(p, '/');
722586Seric 			if (p == NULL)
723586Seric 				return (TRUE);
724586Seric 			p++;
725586Seric 		}
726586Seric 	}
727586Seric 
728586Seric 	printf("You may not use full pathnames or \"..\"\n");
729586Seric 	return (FALSE);
730586Seric }
7311432Seric 
7321432Seric /*
733261Seric **  CLEAN -- clean out recreatable files
734261Seric **
735261Seric **	Any file for which an "s." file exists but no "p." file
736261Seric **	exists in the current directory is purged.
737261Seric **
738261Seric **	Parameters:
739819Seric **		tells whether this came from a "clean", "info", or
740819Seric **		"check" command.
741261Seric **
742261Seric **	Returns:
743261Seric **		none.
744261Seric **
745261Seric **	Side Effects:
746819Seric **		Removes files in the current directory.
747819Seric **		Prints information regarding files being edited.
748819Seric **		Exits if a "check" command.
749261Seric */
750261Seric 
751819Seric clean(mode)
752819Seric 	int mode;
753261Seric {
754261Seric 	struct direct dir;
755261Seric 	struct stat stbuf;
756261Seric 	char buf[100];
757394Seric 	char pline[120];
758346Seric 	register FILE *dirfd;
759346Seric 	register char *basefile;
760351Seric 	bool gotedit;
761394Seric 	FILE *pfp;
762261Seric 
7631207Seric 	strcpy(buf, SccsDir);
7641207Seric 	if (buf[0] != '\0')
7651207Seric 		strcat(buf, "/");
7661207Seric 	strcat(buf, SccsPath);
7671207Seric 	dirfd = fopen(buf, "r");
768261Seric 	if (dirfd == NULL)
769261Seric 	{
7701207Seric 		usrerr("cannot open %s", buf);
7711282Seric 		return (EX_NOINPUT);
772261Seric 	}
773261Seric 
774261Seric 	/*
775261Seric 	**  Scan the SCCS directory looking for s. files.
776261Seric 	*/
777261Seric 
778351Seric 	gotedit = FALSE;
779261Seric 	while (fread(&dir, sizeof dir, 1, dirfd) != NULL)
780261Seric 	{
781568Seric 		if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0)
782261Seric 			continue;
783261Seric 
784261Seric 		/* got an s. file -- see if the p. file exists */
7851207Seric 		strcpy(buf, SccsDir);
7861207Seric 		if (buf[0] != '\0')
7871207Seric 			strcat(buf, "/");
7881207Seric 		strcat(buf, SccsPath);
789261Seric 		strcat(buf, "/p.");
790346Seric 		basefile = &buf[strlen(buf)];
791568Seric 		strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2);
792346Seric 		basefile[sizeof dir.d_name - 2] = '\0';
793394Seric 		pfp = fopen(buf, "r");
794394Seric 		if (pfp != NULL)
795346Seric 		{
796394Seric 			while (fgets(pline, sizeof pline, pfp) != NULL)
797416Seric 				printf("%12s: being edited: %s", basefile, pline);
798394Seric 			fclose(pfp);
799351Seric 			gotedit = TRUE;
800261Seric 			continue;
801346Seric 		}
802261Seric 
803261Seric 		/* the s. file exists and no p. file exists -- unlink the g-file */
804819Seric 		if (mode == CLEANC)
805346Seric 		{
806568Seric 			strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2);
807346Seric 			buf[sizeof dir.d_name - 2] = '\0';
808346Seric 			unlink(buf);
809346Seric 		}
810261Seric 	}
811261Seric 
812261Seric 	fclose(dirfd);
813819Seric 	if (!gotedit && mode == INFOC)
814416Seric 		printf("Nothing being edited\n");
815819Seric 	if (mode == CHECKC)
816819Seric 		exit(gotedit);
8171282Seric 	return (EX_OK);
818261Seric }
8191432Seric 
8201432Seric /*
821396Seric **  UNEDIT -- unedit a file
822396Seric **
823396Seric **	Checks to see that the current user is actually editting
824396Seric **	the file and arranges that s/he is not editting it.
825396Seric **
826396Seric **	Parameters:
827416Seric **		fn -- the name of the file to be unedited.
828396Seric **
829396Seric **	Returns:
830585Seric **		TRUE -- if the file was successfully unedited.
831585Seric **		FALSE -- if the file was not unedited for some
832585Seric **			reason.
833396Seric **
834396Seric **	Side Effects:
835396Seric **		fn is removed
836396Seric **		entries are removed from pfile.
837396Seric */
838396Seric 
839585Seric bool
840396Seric unedit(fn)
841396Seric 	char *fn;
842396Seric {
843396Seric 	register FILE *pfp;
844396Seric 	char *pfn;
845396Seric 	static char tfn[] = "/tmp/sccsXXXXX";
846396Seric 	FILE *tfp;
847396Seric 	register char *p;
848396Seric 	register char *q;
849396Seric 	bool delete = FALSE;
850396Seric 	bool others = FALSE;
851396Seric 	char *myname;
852396Seric 	extern char *getlogin();
853396Seric 	struct pfile *pent;
854396Seric 	extern struct pfile *getpfile();
855396Seric 	char buf[120];
8561316Seric 	extern char *makefile();
857828Seric # ifdef UIDUSER
858828Seric 	struct passwd *pw;
859828Seric 	extern struct passwd *getpwuid();
860828Seric # endif UIDUSER
861396Seric 
862396Seric 	/* make "s." filename & find the trailing component */
863396Seric 	pfn = makefile(fn);
864586Seric 	if (pfn == NULL)
865586Seric 		return (FALSE);
866586Seric 	q = rindex(pfn, '/');
867586Seric 	if (q == NULL)
868586Seric 		q = &pfn[-1];
869586Seric 	if (q[1] != 's' || q[2] != '.')
870396Seric 	{
8711205Seric 		usrerr("bad file name \"%s\"", fn);
872585Seric 		return (FALSE);
873396Seric 	}
874396Seric 
875396Seric 	/* turn "s." into "p." */
876396Seric 	*++q = 'p';
877396Seric 
878396Seric 	pfp = fopen(pfn, "r");
879396Seric 	if (pfp == NULL)
880396Seric 	{
881416Seric 		printf("%12s: not being edited\n", fn);
882585Seric 		return (FALSE);
883396Seric 	}
884396Seric 
885396Seric 	/*
886396Seric 	**  Copy p-file to temp file, doing deletions as needed.
887396Seric 	*/
888396Seric 
889396Seric 	mktemp(tfn);
890396Seric 	tfp = fopen(tfn, "w");
891396Seric 	if (tfp == NULL)
892396Seric 	{
8931205Seric 		usrerr("cannot create \"%s\"", tfn);
894396Seric 		exit(EX_OSERR);
895396Seric 	}
896396Seric 
897828Seric # ifdef UIDUSER
898828Seric 	pw = getpwuid(getuid());
899828Seric 	if (pw == NULL)
900828Seric 	{
9011205Seric 		syserr("who are you? (uid=%d)", getuid());
902828Seric 		exit(EX_OSERR);
903828Seric 	}
904828Seric 	myname = pw->pw_name;
905828Seric # else
906396Seric 	myname = getlogin();
907828Seric # endif UIDUSER
908396Seric 	while ((pent = getpfile(pfp)) != NULL)
909396Seric 	{
910396Seric 		if (strcmp(pent->p_user, myname) == 0)
911396Seric 		{
912396Seric 			/* a match */
913396Seric 			delete++;
914396Seric 		}
915396Seric 		else
916396Seric 		{
917396Seric 			fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid,
918396Seric 			    pent->p_nsid, pent->p_user, pent->p_date,
919396Seric 			    pent->p_time);
920396Seric 			others++;
921396Seric 		}
922396Seric 	}
923396Seric 
924396Seric 	/* do final cleanup */
925396Seric 	if (others)
926396Seric 	{
927396Seric 		if (freopen(tfn, "r", tfp) == NULL)
928396Seric 		{
9291205Seric 			syserr("cannot reopen \"%s\"", tfn);
930396Seric 			exit(EX_OSERR);
931396Seric 		}
932396Seric 		if (freopen(pfn, "w", pfp) == NULL)
933396Seric 		{
9341205Seric 			usrerr("cannot create \"%s\"", pfn);
935585Seric 			return (FALSE);
936396Seric 		}
937396Seric 		while (fgets(buf, sizeof buf, tfp) != NULL)
938396Seric 			fputs(buf, pfp);
939396Seric 	}
940396Seric 	else
941396Seric 	{
942396Seric 		unlink(pfn);
943396Seric 	}
944396Seric 	fclose(tfp);
945396Seric 	fclose(pfp);
946396Seric 	unlink(tfn);
947396Seric 
948396Seric 	if (delete)
949396Seric 	{
950396Seric 		unlink(fn);
951396Seric 		printf("%12s: removed\n", fn);
952585Seric 		return (TRUE);
953396Seric 	}
954396Seric 	else
955396Seric 	{
956416Seric 		printf("%12s: not being edited by you\n", fn);
957585Seric 		return (FALSE);
958396Seric 	}
959396Seric }
9601432Seric 
9611432Seric /*
962*1433Seric **  DODIFF -- diff an s-file against a g-file
963*1433Seric **
964*1433Seric **	Parameters:
965*1433Seric **		getv -- argv for the 'get' command.
966*1433Seric **		gfile -- name of the g-file to diff against.
967*1433Seric **
968*1433Seric **	Returns:
969*1433Seric **		Result of get.
970*1433Seric **
971*1433Seric **	Side Effects:
972*1433Seric **		none.
973*1433Seric */
974*1433Seric 
975*1433Seric dodiff(getv, gfile)
976*1433Seric 	char **getv;
977*1433Seric 	char *gfile;
978*1433Seric {
979*1433Seric 	int pipev[2];
980*1433Seric 	int rval;
981*1433Seric 	register int i;
982*1433Seric 	register int pid;
983*1433Seric 	auto int st;
984*1433Seric 	extern int errno;
985*1433Seric 	int (*osig)();
986*1433Seric 
987*1433Seric 	if (pipe(pipev) < 0)
988*1433Seric 	{
989*1433Seric 		syserr("dodiff: pipe failed");
990*1433Seric 		exit(EX_OSERR);
991*1433Seric 	}
992*1433Seric 	if ((pid = fork()) < 0)
993*1433Seric 	{
994*1433Seric 		syserr("dodiff: fork failed");
995*1433Seric 		exit(EX_OSERR);
996*1433Seric 	}
997*1433Seric 	else if (pid > 0)
998*1433Seric 	{
999*1433Seric 		/* in parent; run get */
1000*1433Seric 		OutFile = pipev[1];
1001*1433Seric 		close(pipev[0]);
1002*1433Seric 		rval = command(&getv[1], TRUE, FALSE, "get -s -k -p");
1003*1433Seric 		osig = signal(SIGINT, SIG_IGN);
1004*1433Seric 		while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR)
1005*1433Seric 			errno = 0;
1006*1433Seric 		signal(SIGINT, osig);
1007*1433Seric 		/* ignore result of diff */
1008*1433Seric 	}
1009*1433Seric 	else
1010*1433Seric 	{
1011*1433Seric 		/* in child, run diff */
1012*1433Seric 		if (close(pipev[1]) < 0 || close(0) < 0 ||
1013*1433Seric 		    dup(pipev[0]) != 0 || close(pipev[0]) < 0)
1014*1433Seric 		{
1015*1433Seric 			syserr("dodiff: magic failed");
1016*1433Seric 			exit(EX_OSERR);
1017*1433Seric 		}
1018*1433Seric 		execl(PROGPATH(bdiff), "bdiff", "-", gfile, NULL);
1019*1433Seric # ifndef V6
1020*1433Seric 		execlp("bdiff", "bdiff", "-", gfile, NULL);
1021*1433Seric 		execlp("diff", "diff", "-", gfile, NULL);
1022*1433Seric # endif NOT V6
1023*1433Seric 		syserr("bdiff: cannot execute");
1024*1433Seric 		exit(EX_OSERR);
1025*1433Seric 	}
1026*1433Seric 	return (rval);
1027*1433Seric }
1028*1433Seric 
1029*1433Seric /*
1030396Seric **  GETPFILE -- get an entry from the p-file
1031396Seric **
1032396Seric **	Parameters:
1033396Seric **		pfp -- p-file file pointer
1034396Seric **
1035396Seric **	Returns:
1036396Seric **		pointer to p-file struct for next entry
1037396Seric **		NULL on EOF or error
1038396Seric **
1039396Seric **	Side Effects:
1040396Seric **		Each call wipes out results of previous call.
1041396Seric */
1042396Seric 
1043396Seric struct pfile *
1044396Seric getpfile(pfp)
1045396Seric 	FILE *pfp;
1046396Seric {
1047396Seric 	static struct pfile ent;
1048396Seric 	static char buf[120];
1049396Seric 	register char *p;
1050396Seric 	extern char *nextfield();
1051396Seric 
1052396Seric 	if (fgets(buf, sizeof buf, pfp) == NULL)
1053396Seric 		return (NULL);
1054396Seric 
1055396Seric 	ent.p_osid = p = buf;
1056396Seric 	ent.p_nsid = p = nextfield(p);
1057396Seric 	ent.p_user = p = nextfield(p);
1058396Seric 	ent.p_date = p = nextfield(p);
1059396Seric 	ent.p_time = p = nextfield(p);
1060396Seric 	if (p == NULL || nextfield(p) != NULL)
1061396Seric 		return (NULL);
1062396Seric 
1063396Seric 	return (&ent);
1064396Seric }
1065396Seric 
1066396Seric 
1067396Seric char *
1068396Seric nextfield(p)
1069396Seric 	register char *p;
1070396Seric {
1071396Seric 	if (p == NULL || *p == '\0')
1072396Seric 		return (NULL);
1073396Seric 	while (*p != ' ' && *p != '\n' && *p != '\0')
1074396Seric 		p++;
1075396Seric 	if (*p == '\n' || *p == '\0')
1076396Seric 	{
1077396Seric 		*p = '\0';
1078396Seric 		return (NULL);
1079396Seric 	}
1080396Seric 	*p++ = '\0';
1081396Seric 	return (p);
1082396Seric }
10831432Seric 
10841432Seric /*
10851205Seric **  USRERR -- issue user-level error
10861205Seric **
10871205Seric **	Parameters:
10881205Seric **		f -- format string.
10891205Seric **		p1-p3 -- parameters to a printf.
10901205Seric **
10911205Seric **	Returns:
10921205Seric **		-1
10931205Seric **
10941205Seric **	Side Effects:
10951205Seric **		none.
10961205Seric */
10971205Seric 
10981205Seric usrerr(f, p1, p2, p3)
10991205Seric 	char *f;
11001205Seric {
11011205Seric 	fprintf(stderr, "\n%s: ", MyName);
11021205Seric 	fprintf(stderr, f, p1, p2, p3);
11031205Seric 	fprintf(stderr, "\n");
11041205Seric 
11051205Seric 	return (-1);
11061205Seric }
11071432Seric 
11081432Seric /*
11091205Seric **  SYSERR -- print system-generated error.
11101205Seric **
11111205Seric **	Parameters:
11121205Seric **		f -- format string to a printf.
11131205Seric **		p1, p2, p3 -- parameters to f.
11141205Seric **
11151205Seric **	Returns:
11161205Seric **		never.
11171205Seric **
11181205Seric **	Side Effects:
11191205Seric **		none.
11201205Seric */
11211205Seric 
11221205Seric syserr(f, p1, p2, p3)
11231205Seric 	char *f;
11241205Seric {
11251205Seric 	extern int errno;
11261205Seric 
11271205Seric 	fprintf(stderr, "\n%s SYSERR: ", MyName);
11281205Seric 	fprintf(stderr, f, p1, p2, p3);
11291205Seric 	fprintf(stderr, "\n");
11301205Seric 	if (errno == 0)
11311205Seric 		exit(EX_SOFTWARE);
11321205Seric 	else
11331205Seric 	{
11341205Seric 		perror(0);
11351205Seric 		exit(EX_OSERR);
11361205Seric 	}
11371205Seric }
1138