xref: /csrg-svn/usr.bin/sccs/sccs.c (revision 1437)
1148Seric # include <stdio.h>
2148Seric # include <sys/types.h>
3148Seric # include <sys/stat.h>
4261Seric # include <sys/dir.h>
51433Seric # include <errno.h>
61433Seric # 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.
82*1437Seric **		MYNAME -- the title this program should print when it
83*1437Seric **			gives error messages.
84828Seric **
85828Seric **	Compilation Instructions:
86828Seric **		cc -O -n -s sccs.c
87*1437Seric **		The flags listed above can be -D defined to simplify
88*1437Seric **			recompilation for variant versions.
89828Seric **
90828Seric **	Author:
91828Seric **		Eric Allman, UCB/INGRES
921270Seric **		Copyright 1980 Regents of the University of California
93828Seric */
94155Seric 
95*1437Seric static char SccsId[] = "@(#)sccs.c	1.38 10/15/80";
961432Seric 
971270Seric /*******************  Configuration Information  ********************/
981270Seric 
99*1437Seric /* special defines for local berkeley systems */
100*1437Seric # include <whoami.h>
101*1437Seric 
102828Seric # ifdef CSVAX
103828Seric # define UIDUSER
1041270Seric # define PROGPATH(name)	"/usr/local/name"
1051270Seric # endif CSVAX
106828Seric 
107*1437Seric /* end of berkeley systems defines */
108*1437Seric 
109*1437Seric # ifndef SCCSPATH
1101432Seric # define SCCSPATH	"SCCS"	/* pathname in which to find s-files */
111*1437Seric # endif NOT SCCSPATH
112828Seric 
113*1437Seric # ifndef MYNAME
114*1437Seric # define MYNAME		"sccs"	/* name used for printing errors */
115*1437Seric # endif NOT MYNAME
1161270Seric 
1171432Seric # ifndef PROGPATH
1181432Seric # define PROGPATH(name)	"/usr/sccs/name"	/* place to find binaries */
1191432Seric # endif PROGPATH
1201432Seric 
1211270Seric /****************  End of Configuration Information  ****************/
1221432Seric 
123157Seric # define bitset(bit, word)	((bit) & (word))
124157Seric 
125157Seric typedef char	bool;
126200Seric # define TRUE	1
127200Seric # define FALSE	0
128157Seric 
129828Seric # ifdef UIDUSER
130828Seric # include <pwd.h>
131828Seric # endif UIDUSER
132828Seric 
133148Seric struct sccsprog
134148Seric {
135148Seric 	char	*sccsname;	/* name of SCCS routine */
136200Seric 	short	sccsoper;	/* opcode, see below */
137200Seric 	short	sccsflags;	/* flags, see below */
1381316Seric 	char	*sccsklets;	/* valid key-letters on macros */
139148Seric 	char	*sccspath;	/* pathname of binary implementing */
140148Seric };
141148Seric 
142200Seric /* values for sccsoper */
143200Seric # define PROG		0	/* call a program */
144201Seric # define CMACRO		1	/* command substitution macro */
145226Seric # define FIX		2	/* fix a delta */
146261Seric # define CLEAN		3	/* clean out recreatable files */
147396Seric # define UNEDIT		4	/* unedit a file */
1481431Seric # define SHELL		5	/* call a shell file (like PROG) */
1491433Seric # define DIFFS		6	/* diff between sccs & file out */
150200Seric 
151157Seric /* bits for sccsflags */
152200Seric # define NO_SDOT	0001	/* no s. on front of args */
153200Seric # define REALUSER	0002	/* protected (e.g., admin) */
154148Seric 
155819Seric /* modes for the "clean", "info", "check" ops */
156819Seric # define CLEANC		0	/* clean command */
157819Seric # define INFOC		1	/* info command */
158819Seric # define CHECKC		2	/* check command */
159819Seric 
1601432Seric /*
1611432Seric **  Description of commands known to this program.
1621432Seric **	First argument puts the command into a class.  Second arg is
1631432Seric **	info regarding treatment of this command.  Third arg is a
1641432Seric **	list of flags this command accepts from macros, etc.  Fourth
1651432Seric **	arg is the pathname of the implementing program, or the
1661432Seric **	macro definition, or the arg to a sub-algorithm.
1671432Seric */
168202Seric 
169148Seric struct sccsprog SccsProg[] =
170148Seric {
1711316Seric 	"admin",	PROG,	REALUSER,	"",		PROGPATH(admin),
1721316Seric 	"chghist",	PROG,	0,		"",		PROGPATH(rmdel),
1731316Seric 	"comb",		PROG,	0,		"",		PROGPATH(comb),
1741316Seric 	"delta",	PROG,	0,		"mysrp",	PROGPATH(delta),
1751317Seric 	"get",		PROG,	0,		"ixbeskcl",	PROGPATH(get),
1761316Seric 	"help",		PROG,	NO_SDOT,	"",		PROGPATH(help),
1771316Seric 	"prt",		PROG,	0,		"",		PROGPATH(prt),
1781316Seric 	"rmdel",	PROG,	REALUSER,	"",		PROGPATH(rmdel),
1791316Seric 	"what",		PROG,	NO_SDOT,	"",		PROGPATH(what),
1801431Seric 	"sccsdiff",	SHELL,	REALUSER,	"",		PROGPATH(sccsdiff),
1811317Seric 	"edit",		CMACRO,	NO_SDOT,	"ixbscl",	"get -e",
1821316Seric 	"delget",	CMACRO,	NO_SDOT,	"",		"delta/get -t",
1831316Seric 	"deledit",	CMACRO,	NO_SDOT,	"",		"delta/get -e -t",
1841316Seric 	"fix",		FIX,	NO_SDOT,	"",		NULL,
1851316Seric 	"clean",	CLEAN,	REALUSER,	"",		(char *) CLEANC,
1861316Seric 	"info",		CLEAN,	REALUSER,	"",		(char *) INFOC,
1871316Seric 	"check",	CLEAN,	REALUSER,	"",		(char *) CHECKC,
1881316Seric 	"unedit",	UNEDIT,	NO_SDOT,	"",		NULL,
1891433Seric 	"diffs",	DIFFS,	NO_SDOT|REALUSER, "",		NULL,
1901316Seric 	NULL,		-1,	0,		"",		NULL
191148Seric };
192148Seric 
1931432Seric /* one line from a p-file */
194396Seric struct pfile
195396Seric {
196396Seric 	char	*p_osid;	/* old SID */
197396Seric 	char	*p_nsid;	/* new SID */
198396Seric 	char	*p_user;	/* user who did edit */
199396Seric 	char	*p_date;	/* date of get */
200396Seric 	char	*p_time;	/* time of get */
201396Seric };
202396Seric 
2031270Seric char	*SccsPath = SCCSPATH;	/* pathname of SCCS files */
2041270Seric # ifdef SCCSDIR
2051270Seric char	*SccsDir = SCCSDIR;	/* directory to begin search from */
2061205Seric # else
2071270Seric char	*SccsDir = "";
2081205Seric # endif
209*1437Seric char	MyName[] = MYNAME;	/* name used in messages */
2101433Seric int	OutFile = -1;		/* override output file for commands */
211157Seric bool	RealUser;		/* if set, running as real user */
212393Seric # ifdef DEBUG
213393Seric bool	Debug;			/* turn on tracing */
214393Seric # endif
2151432Seric 
216148Seric main(argc, argv)
217148Seric 	int argc;
218148Seric 	char **argv;
219148Seric {
220148Seric 	register char *p;
221262Seric 	extern struct sccsprog *lookup();
2221282Seric 	register int i;
223148Seric 
224148Seric 	/*
225148Seric 	**  Detect and decode flags intended for this program.
226148Seric 	*/
227148Seric 
228200Seric 	if (argc < 2)
229148Seric 	{
2301205Seric 		fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName);
231200Seric 		exit(EX_USAGE);
232200Seric 	}
233200Seric 	argv[argc] = NULL;
234200Seric 
235262Seric 	if (lookup(argv[0]) == NULL)
236200Seric 	{
237262Seric 		while ((p = *++argv) != NULL)
238148Seric 		{
239262Seric 			if (*p != '-')
240262Seric 				break;
241262Seric 			switch (*++p)
242262Seric 			{
243262Seric 			  case 'r':		/* run as real user */
244262Seric 				setuid(getuid());
245262Seric 				RealUser++;
246262Seric 				break;
247148Seric 
2481270Seric # ifndef SCCSDIR
249262Seric 			  case 'p':		/* path of sccs files */
250262Seric 				SccsPath = ++p;
251262Seric 				break;
252148Seric 
253588Seric 			  case 'd':		/* directory to search from */
254588Seric 				SccsDir = ++p;
255588Seric 				break;
2561205Seric # endif
257588Seric 
258393Seric # ifdef DEBUG
259393Seric 			  case 'T':		/* trace */
260393Seric 				Debug++;
261393Seric 				break;
262393Seric # endif
263393Seric 
264262Seric 			  default:
2651205Seric 				usrerr("unknown option -%s", p);
266262Seric 				break;
267262Seric 			}
268148Seric 		}
269262Seric 		if (SccsPath[0] == '\0')
270262Seric 			SccsPath = ".";
271148Seric 	}
272148Seric 
2731316Seric 	i = command(argv, FALSE, FALSE, "");
2741282Seric 	exit(i);
275200Seric }
2761432Seric 
2771432Seric /*
2781282Seric **  COMMAND -- look up and perform a command
2791282Seric **
2801282Seric **	This routine is the guts of this program.  Given an
2811282Seric **	argument vector, it looks up the "command" (argv[0])
2821282Seric **	in the configuration table and does the necessary stuff.
2831282Seric **
2841282Seric **	Parameters:
2851282Seric **		argv -- an argument vector to process.
2861282Seric **		forkflag -- if set, fork before executing the command.
2871316Seric **		editflag -- if set, only include flags listed in the
2881316Seric **			sccsklets field of the command descriptor.
2891316Seric **		arg0 -- a space-seperated list of arguments to insert
2901316Seric **			before argv.
2911282Seric **
2921282Seric **	Returns:
2931282Seric **		zero -- command executed ok.
2941282Seric **		else -- error status.
2951282Seric **
2961282Seric **	Side Effects:
2971282Seric **		none.
2981282Seric */
299157Seric 
3001316Seric command(argv, forkflag, editflag, arg0)
301200Seric 	char **argv;
302201Seric 	bool forkflag;
3031316Seric 	bool editflag;
3041316Seric 	char *arg0;
305200Seric {
306200Seric 	register struct sccsprog *cmd;
307200Seric 	register char *p;
308201Seric 	char buf[40];
309262Seric 	extern struct sccsprog *lookup();
3101316Seric 	char *nav[1000];
3111316Seric 	char **np;
3121431Seric 	register char **ap;
313585Seric 	register int i;
3141431Seric 	register char *q;
315585Seric 	extern bool unedit();
3161282Seric 	int rval = 0;
3171316Seric 	extern char *index();
3181316Seric 	extern char *makefile();
3191435Seric 	extern char *tail();
320200Seric 
321393Seric # ifdef DEBUG
322393Seric 	if (Debug)
323393Seric 	{
3241316Seric 		printf("command:\n\t\"%s\"\n", arg0);
3251316Seric 		for (np = argv; *np != NULL; np++)
3261316Seric 			printf("\t\"%s\"\n", *np);
327393Seric 	}
328393Seric # endif
329393Seric 
330157Seric 	/*
3311316Seric 	**  Copy arguments.
3321316Seric 	**	Phase one -- from arg0 & if necessary argv[0].
3331316Seric 	*/
3341316Seric 
3351431Seric 	np = ap = &nav[1];
3361316Seric 	for (p = arg0, q = buf; *p != '\0' && *p != '/'; )
3371316Seric 	{
3381316Seric 		*np++ = q;
3391316Seric 		while (*p == ' ')
3401316Seric 			p++;
3411316Seric 		while (*p != ' ' && *p != '\0' && *p != '/')
3421316Seric 			*q++ = *p++;
3431316Seric 		*q++ = '\0';
3441316Seric 	}
3451316Seric 	*np = NULL;
3461431Seric 	if (*ap == NULL)
3471316Seric 		*np++ = *argv++;
3481316Seric 
3491316Seric 	/*
350148Seric 	**  Look up command.
3511431Seric 	**	At this point, *ap is the command name.
352148Seric 	*/
353148Seric 
3541431Seric 	cmd = lookup(*ap);
355262Seric 	if (cmd == NULL)
356148Seric 	{
3571431Seric 		usrerr("Unknown command \"%s\"", *ap);
3581282Seric 		return (EX_USAGE);
359148Seric 	}
360148Seric 
361148Seric 	/*
3621316Seric 	**  Copy remaining arguments doing editing as appropriate.
3631316Seric 	*/
3641316Seric 
3651316Seric 	for (; *argv != NULL; argv++)
3661316Seric 	{
3671316Seric 		p = *argv;
3681316Seric 		if (*p == '-')
3691316Seric 		{
3701316Seric 			if (p[1] == '\0' || !editflag || cmd->sccsklets == NULL ||
3711316Seric 			    index(cmd->sccsklets, p[1]) != NULL)
3721316Seric 				*np++ = p;
3731316Seric 		}
3741316Seric 		else
3751316Seric 		{
3761316Seric 			if (!bitset(NO_SDOT, cmd->sccsflags))
3771316Seric 				p = makefile(p);
3781316Seric 			if (p != NULL)
3791316Seric 				*np++ = p;
3801316Seric 		}
3811316Seric 	}
3821316Seric 	*np = NULL;
3831316Seric 
3841316Seric 	/*
385200Seric 	**  Interpret operation associated with this command.
386157Seric 	*/
387157Seric 
388200Seric 	switch (cmd->sccsoper)
389200Seric 	{
3901431Seric 	  case SHELL:		/* call a shell file */
3911431Seric 		*ap = cmd->sccspath;
3921431Seric 		*--ap = "sh";
3931431Seric 		rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag);
3941431Seric 		break;
3951431Seric 
396200Seric 	  case PROG:		/* call an sccs prog */
3971431Seric 		rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag);
398201Seric 		break;
399201Seric 
400201Seric 	  case CMACRO:		/* command macro */
401201Seric 		for (p = cmd->sccspath; *p != '\0'; p++)
402201Seric 		{
4031316Seric 			q = p;
4041316Seric 			while (*p != '\0' && *p != '/')
4051316Seric 				p++;
4061431Seric 			rval = command(&ap[1], *p != '\0', TRUE, q);
4071282Seric 			if (rval != 0)
4081282Seric 				break;
409201Seric 		}
4101282Seric 		break;
411157Seric 
412226Seric 	  case FIX:		/* fix a delta */
4131431Seric 		if (strncmp(ap[1], "-r", 2) != 0)
414226Seric 		{
4151205Seric 			usrerr("-r flag needed for fix command");
4161282Seric 			rval = EX_USAGE;
417226Seric 			break;
418226Seric 		}
4191431Seric 		rval = command(&ap[1], TRUE, TRUE, "get -k");
4201282Seric 		if (rval == 0)
4211431Seric 			rval = command(&ap[1], TRUE, TRUE, "rmdel");
4221282Seric 		if (rval == 0)
4231431Seric 			rval = command(&ap[2], FALSE, TRUE, "get -e -g");
4241282Seric 		break;
425226Seric 
426261Seric 	  case CLEAN:
4271282Seric 		rval = clean((int) cmd->sccspath);
428261Seric 		break;
429261Seric 
430396Seric 	  case UNEDIT:
4311431Seric 		for (argv = np = &ap[1]; *argv != NULL; argv++)
432585Seric 		{
4331316Seric 			if (unedit(*argv))
4341316Seric 				*np++ = *argv;
435585Seric 		}
4361316Seric 		*np = NULL;
437585Seric 		if (i > 0)
4381431Seric 			rval = command(&ap[1], FALSE, FALSE, "get");
439396Seric 		break;
440396Seric 
4411433Seric 	  case DIFFS:		/* diff between s-file & edit file */
4421433Seric 		/* find the end of the flag arguments */
4431433Seric 		for (np = &ap[1]; *np != NULL && **np == '-'; np++)
4441433Seric 			continue;
4451433Seric 		argv = np;
4461433Seric 
4471433Seric 		/* for each file, do the diff */
4481433Seric 		while (*np != NULL)
4491433Seric 		{
4501433Seric 			*argv = *np++;
4511433Seric 			p = *np;
4521433Seric 			*np = NULL;
4531435Seric 			i = dodiff(ap, tail(*argv));
4541433Seric 			if (rval == 0)
4551433Seric 				rval = i;
4561433Seric 			*np = p;
4571433Seric 		}
4581433Seric 		break;
4591433Seric 
460200Seric 	  default:
4611205Seric 		syserr("oper %d", cmd->sccsoper);
462200Seric 		exit(EX_SOFTWARE);
463200Seric 	}
4641282Seric # ifdef DEBUG
4651282Seric 	if (Debug)
4661282Seric 		printf("command: rval=%d\n", rval);
4671282Seric # endif
4681282Seric 	return (rval);
469200Seric }
4701432Seric 
4711432Seric /*
472262Seric **  LOOKUP -- look up an SCCS command name.
473262Seric **
474262Seric **	Parameters:
475262Seric **		name -- the name of the command to look up.
476262Seric **
477262Seric **	Returns:
478262Seric **		ptr to command descriptor for this command.
479262Seric **		NULL if no such entry.
480262Seric **
481262Seric **	Side Effects:
482262Seric **		none.
483262Seric */
484200Seric 
485262Seric struct sccsprog *
486262Seric lookup(name)
487262Seric 	char *name;
488262Seric {
489262Seric 	register struct sccsprog *cmd;
490226Seric 
491262Seric 	for (cmd = SccsProg; cmd->sccsname != NULL; cmd++)
492262Seric 	{
493262Seric 		if (strcmp(cmd->sccsname, name) == 0)
494262Seric 			return (cmd);
495262Seric 	}
496262Seric 	return (NULL);
497262Seric }
4981432Seric 
4991432Seric /*
5001282Seric **  CALLPROG -- call a program
5011282Seric **
5021316Seric **	Used to call the SCCS programs.
5031282Seric **
5041282Seric **	Parameters:
5051282Seric **		progpath -- pathname of the program to call.
5061282Seric **		flags -- status flags from the command descriptors.
5071282Seric **		argv -- an argument vector to pass to the program.
5081282Seric **		forkflag -- if true, fork before calling, else just
5091282Seric **			exec.
5101282Seric **
5111282Seric **	Returns:
5121282Seric **		The exit status of the program.
5131282Seric **		Nothing if forkflag == FALSE.
5141282Seric **
5151282Seric **	Side Effects:
5161282Seric **		Can exit if forkflag == FALSE.
5171282Seric */
518226Seric 
519200Seric callprog(progpath, flags, argv, forkflag)
520200Seric 	char *progpath;
521200Seric 	short flags;
522200Seric 	char **argv;
523200Seric 	bool forkflag;
524200Seric {
525200Seric 	register int i;
526201Seric 	auto int st;
527200Seric 
5281316Seric # ifdef DEBUG
5291316Seric 	if (Debug)
5301316Seric 	{
5311316Seric 		printf("callprog:\n");
5321316Seric 		for (i = 0; argv[i] != NULL; i++)
5331316Seric 			printf("\t\"%s\"\n", argv[i]);
5341316Seric 	}
5351316Seric # endif
5361316Seric 
537200Seric 	if (*argv == NULL)
538200Seric 		return (-1);
539200Seric 
540157Seric 	/*
541226Seric 	**  Fork if appropriate.
542148Seric 	*/
543148Seric 
544200Seric 	if (forkflag)
545200Seric 	{
546393Seric # ifdef DEBUG
547393Seric 		if (Debug)
548393Seric 			printf("Forking\n");
549393Seric # endif
550200Seric 		i = fork();
551200Seric 		if (i < 0)
552200Seric 		{
5531205Seric 			syserr("cannot fork");
554200Seric 			exit(EX_OSERR);
555200Seric 		}
556200Seric 		else if (i > 0)
557201Seric 		{
558201Seric 			wait(&st);
5591282Seric 			if ((st & 0377) == 0)
5601282Seric 				st = (st >> 8) & 0377;
5611433Seric 			if (OutFile >= 0)
5621433Seric 			{
5631433Seric 				close(OutFile);
5641433Seric 				OutFile = -1;
5651433Seric 			}
566201Seric 			return (st);
567201Seric 		}
568200Seric 	}
5691433Seric 	else if (OutFile >= 0)
5701433Seric 	{
5711433Seric 		syserr("callprog: setting stdout w/o forking");
5721433Seric 		exit(EX_SOFTWARE);
5731433Seric 	}
574200Seric 
5751433Seric 	/* set protection as appropriate */
576200Seric 	if (bitset(REALUSER, flags))
577200Seric 		setuid(getuid());
5781433Seric 
5791433Seric 	/* change standard input & output if needed */
5801433Seric 	if (OutFile >= 0)
5811433Seric 	{
5821433Seric 		close(1);
5831433Seric 		dup(OutFile);
5841433Seric 		close(OutFile);
5851433Seric 	}
586226Seric 
5871433Seric 	/* call real SCCS program */
588226Seric 	execv(progpath, argv);
5891205Seric 	syserr("cannot execute %s", progpath);
590148Seric 	exit(EX_UNAVAILABLE);
591148Seric }
5921432Seric 
5931432Seric /*
594586Seric **  MAKEFILE -- make filename of SCCS file
595586Seric **
596586Seric **	If the name passed is already the name of an SCCS file,
597586Seric **	just return it.  Otherwise, munge the name into the name
598586Seric **	of the actual SCCS file.
599586Seric **
600586Seric **	There are cases when it is not clear what you want to
601586Seric **	do.  For example, if SccsPath is an absolute pathname
602586Seric **	and the name given is also an absolute pathname, we go
603586Seric **	for SccsPath (& only use the last component of the name
604586Seric **	passed) -- this is important for security reasons (if
605586Seric **	sccs is being used as a setuid front end), but not
606586Seric **	particularly intuitive.
607586Seric **
608586Seric **	Parameters:
609586Seric **		name -- the file name to be munged.
610586Seric **
611586Seric **	Returns:
612586Seric **		The pathname of the sccs file.
613586Seric **		NULL on error.
614586Seric **
615586Seric **	Side Effects:
616586Seric **		none.
617586Seric */
618148Seric 
619148Seric char *
620148Seric makefile(name)
621148Seric 	char *name;
622148Seric {
623148Seric 	register char *p;
624148Seric 	register char c;
625148Seric 	char buf[512];
626588Seric 	struct stat stbuf;
627148Seric 	extern char *malloc();
628586Seric 	extern char *rindex();
629588Seric 	extern bool safepath();
630587Seric 	extern bool isdir();
631587Seric 	register char *q;
632148Seric 
633586Seric 	p = rindex(name, '/');
634586Seric 	if (p == NULL)
635586Seric 		p = name;
636586Seric 	else
637586Seric 		p++;
638586Seric 
639148Seric 	/*
640588Seric 	**  Check to see that the path is "safe", i.e., that we
641588Seric 	**  are not letting some nasty person use the setuid part
642588Seric 	**  of this program to look at or munge some presumably
643588Seric 	**  hidden files.
644148Seric 	*/
645148Seric 
646588Seric 	if (SccsDir[0] == '/' && !safepath(name))
647588Seric 		return (NULL);
648586Seric 
649586Seric 	/*
650588Seric 	**  Create the base pathname.
651586Seric 	*/
652586Seric 
653588Seric 	if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0)
654148Seric 	{
655588Seric 		strcpy(buf, SccsDir);
656586Seric 		strcat(buf, "/");
657586Seric 	}
658586Seric 	else
659586Seric 		strcpy(buf, "");
660587Seric 	strncat(buf, name, p - name);
661587Seric 	q = &buf[strlen(buf)];
662587Seric 	strcpy(q, p);
663587Seric 	if (strncmp(p, "s.", 2) != 0 && !isdir(buf))
664586Seric 	{
665588Seric 		strcpy(q, SccsPath);
666588Seric 		strcat(buf, "/s.");
667586Seric 		strcat(buf, p);
668586Seric 	}
669148Seric 
670588Seric 	if (strcmp(buf, name) == 0)
671588Seric 		p = name;
672588Seric 	else
673148Seric 	{
674588Seric 		p = malloc(strlen(buf) + 1);
675588Seric 		if (p == NULL)
676588Seric 		{
677588Seric 			perror("Sccs: no mem");
678588Seric 			exit(EX_OSERR);
679588Seric 		}
680588Seric 		strcpy(p, buf);
681148Seric 	}
682148Seric 	return (p);
683148Seric }
6841432Seric 
6851432Seric /*
686587Seric **  ISDIR -- return true if the argument is a directory.
687587Seric **
688587Seric **	Parameters:
689587Seric **		name -- the pathname of the file to check.
690587Seric **
691587Seric **	Returns:
692587Seric **		TRUE if 'name' is a directory, FALSE otherwise.
693587Seric **
694587Seric **	Side Effects:
695587Seric **		none.
696587Seric */
697587Seric 
698587Seric bool
699587Seric isdir(name)
700587Seric 	char *name;
701587Seric {
702587Seric 	struct stat stbuf;
703587Seric 
704587Seric 	return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR);
705587Seric }
7061432Seric 
7071432Seric /*
708586Seric **  SAFEPATH -- determine whether a pathname is "safe"
709586Seric **
710586Seric **	"Safe" pathnames only allow you to get deeper into the
711586Seric **	directory structure, i.e., full pathnames and ".." are
712586Seric **	not allowed.
713586Seric **
714586Seric **	Parameters:
715586Seric **		p -- the name to check.
716586Seric **
717586Seric **	Returns:
718586Seric **		TRUE -- if the path is safe.
719586Seric **		FALSE -- if the path is not safe.
720586Seric **
721586Seric **	Side Effects:
722586Seric **		Prints a message if the path is not safe.
723586Seric */
724586Seric 
725586Seric bool
726586Seric safepath(p)
727586Seric 	register char *p;
728586Seric {
729586Seric 	extern char *index();
730586Seric 
731586Seric 	if (*p != '/')
732586Seric 	{
733586Seric 		while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0)
734586Seric 		{
735586Seric 			p = index(p, '/');
736586Seric 			if (p == NULL)
737586Seric 				return (TRUE);
738586Seric 			p++;
739586Seric 		}
740586Seric 	}
741586Seric 
742586Seric 	printf("You may not use full pathnames or \"..\"\n");
743586Seric 	return (FALSE);
744586Seric }
7451432Seric 
7461432Seric /*
747261Seric **  CLEAN -- clean out recreatable files
748261Seric **
749261Seric **	Any file for which an "s." file exists but no "p." file
750261Seric **	exists in the current directory is purged.
751261Seric **
752261Seric **	Parameters:
753819Seric **		tells whether this came from a "clean", "info", or
754819Seric **		"check" command.
755261Seric **
756261Seric **	Returns:
757261Seric **		none.
758261Seric **
759261Seric **	Side Effects:
760819Seric **		Removes files in the current directory.
761819Seric **		Prints information regarding files being edited.
762819Seric **		Exits if a "check" command.
763261Seric */
764261Seric 
765819Seric clean(mode)
766819Seric 	int mode;
767261Seric {
768261Seric 	struct direct dir;
769261Seric 	struct stat stbuf;
770261Seric 	char buf[100];
771394Seric 	char pline[120];
772346Seric 	register FILE *dirfd;
773346Seric 	register char *basefile;
774351Seric 	bool gotedit;
775394Seric 	FILE *pfp;
776261Seric 
7771207Seric 	strcpy(buf, SccsDir);
7781207Seric 	if (buf[0] != '\0')
7791207Seric 		strcat(buf, "/");
7801207Seric 	strcat(buf, SccsPath);
7811207Seric 	dirfd = fopen(buf, "r");
782261Seric 	if (dirfd == NULL)
783261Seric 	{
7841207Seric 		usrerr("cannot open %s", buf);
7851282Seric 		return (EX_NOINPUT);
786261Seric 	}
787261Seric 
788261Seric 	/*
789261Seric 	**  Scan the SCCS directory looking for s. files.
790261Seric 	*/
791261Seric 
792351Seric 	gotedit = FALSE;
793261Seric 	while (fread(&dir, sizeof dir, 1, dirfd) != NULL)
794261Seric 	{
795568Seric 		if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0)
796261Seric 			continue;
797261Seric 
798261Seric 		/* got an s. file -- see if the p. file exists */
7991207Seric 		strcpy(buf, SccsDir);
8001207Seric 		if (buf[0] != '\0')
8011207Seric 			strcat(buf, "/");
8021207Seric 		strcat(buf, SccsPath);
803261Seric 		strcat(buf, "/p.");
804346Seric 		basefile = &buf[strlen(buf)];
805568Seric 		strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2);
806346Seric 		basefile[sizeof dir.d_name - 2] = '\0';
807394Seric 		pfp = fopen(buf, "r");
808394Seric 		if (pfp != NULL)
809346Seric 		{
810394Seric 			while (fgets(pline, sizeof pline, pfp) != NULL)
811416Seric 				printf("%12s: being edited: %s", basefile, pline);
812394Seric 			fclose(pfp);
813351Seric 			gotedit = TRUE;
814261Seric 			continue;
815346Seric 		}
816261Seric 
817261Seric 		/* the s. file exists and no p. file exists -- unlink the g-file */
818819Seric 		if (mode == CLEANC)
819346Seric 		{
820568Seric 			strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2);
821346Seric 			buf[sizeof dir.d_name - 2] = '\0';
822346Seric 			unlink(buf);
823346Seric 		}
824261Seric 	}
825261Seric 
826261Seric 	fclose(dirfd);
827819Seric 	if (!gotedit && mode == INFOC)
828416Seric 		printf("Nothing being edited\n");
829819Seric 	if (mode == CHECKC)
830819Seric 		exit(gotedit);
8311282Seric 	return (EX_OK);
832261Seric }
8331432Seric 
8341432Seric /*
835396Seric **  UNEDIT -- unedit a file
836396Seric **
837396Seric **	Checks to see that the current user is actually editting
838396Seric **	the file and arranges that s/he is not editting it.
839396Seric **
840396Seric **	Parameters:
841416Seric **		fn -- the name of the file to be unedited.
842396Seric **
843396Seric **	Returns:
844585Seric **		TRUE -- if the file was successfully unedited.
845585Seric **		FALSE -- if the file was not unedited for some
846585Seric **			reason.
847396Seric **
848396Seric **	Side Effects:
849396Seric **		fn is removed
850396Seric **		entries are removed from pfile.
851396Seric */
852396Seric 
853585Seric bool
854396Seric unedit(fn)
855396Seric 	char *fn;
856396Seric {
857396Seric 	register FILE *pfp;
858396Seric 	char *pfn;
859396Seric 	static char tfn[] = "/tmp/sccsXXXXX";
860396Seric 	FILE *tfp;
861396Seric 	register char *p;
862396Seric 	register char *q;
863396Seric 	bool delete = FALSE;
864396Seric 	bool others = FALSE;
865396Seric 	char *myname;
866396Seric 	extern char *getlogin();
867396Seric 	struct pfile *pent;
868396Seric 	extern struct pfile *getpfile();
869396Seric 	char buf[120];
8701316Seric 	extern char *makefile();
871828Seric # ifdef UIDUSER
872828Seric 	struct passwd *pw;
873828Seric 	extern struct passwd *getpwuid();
874828Seric # endif UIDUSER
875396Seric 
876396Seric 	/* make "s." filename & find the trailing component */
877396Seric 	pfn = makefile(fn);
878586Seric 	if (pfn == NULL)
879586Seric 		return (FALSE);
880586Seric 	q = rindex(pfn, '/');
881586Seric 	if (q == NULL)
882586Seric 		q = &pfn[-1];
883586Seric 	if (q[1] != 's' || q[2] != '.')
884396Seric 	{
8851205Seric 		usrerr("bad file name \"%s\"", fn);
886585Seric 		return (FALSE);
887396Seric 	}
888396Seric 
889396Seric 	/* turn "s." into "p." */
890396Seric 	*++q = 'p';
891396Seric 
892396Seric 	pfp = fopen(pfn, "r");
893396Seric 	if (pfp == NULL)
894396Seric 	{
895416Seric 		printf("%12s: not being edited\n", fn);
896585Seric 		return (FALSE);
897396Seric 	}
898396Seric 
899396Seric 	/*
900396Seric 	**  Copy p-file to temp file, doing deletions as needed.
901396Seric 	*/
902396Seric 
903396Seric 	mktemp(tfn);
904396Seric 	tfp = fopen(tfn, "w");
905396Seric 	if (tfp == NULL)
906396Seric 	{
9071205Seric 		usrerr("cannot create \"%s\"", tfn);
908396Seric 		exit(EX_OSERR);
909396Seric 	}
910396Seric 
911828Seric # ifdef UIDUSER
912828Seric 	pw = getpwuid(getuid());
913828Seric 	if (pw == NULL)
914828Seric 	{
9151205Seric 		syserr("who are you? (uid=%d)", getuid());
916828Seric 		exit(EX_OSERR);
917828Seric 	}
918828Seric 	myname = pw->pw_name;
919828Seric # else
920396Seric 	myname = getlogin();
921828Seric # endif UIDUSER
922396Seric 	while ((pent = getpfile(pfp)) != NULL)
923396Seric 	{
924396Seric 		if (strcmp(pent->p_user, myname) == 0)
925396Seric 		{
926396Seric 			/* a match */
927396Seric 			delete++;
928396Seric 		}
929396Seric 		else
930396Seric 		{
931396Seric 			fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid,
932396Seric 			    pent->p_nsid, pent->p_user, pent->p_date,
933396Seric 			    pent->p_time);
934396Seric 			others++;
935396Seric 		}
936396Seric 	}
937396Seric 
938396Seric 	/* do final cleanup */
939396Seric 	if (others)
940396Seric 	{
941396Seric 		if (freopen(tfn, "r", tfp) == NULL)
942396Seric 		{
9431205Seric 			syserr("cannot reopen \"%s\"", tfn);
944396Seric 			exit(EX_OSERR);
945396Seric 		}
946396Seric 		if (freopen(pfn, "w", pfp) == NULL)
947396Seric 		{
9481205Seric 			usrerr("cannot create \"%s\"", pfn);
949585Seric 			return (FALSE);
950396Seric 		}
951396Seric 		while (fgets(buf, sizeof buf, tfp) != NULL)
952396Seric 			fputs(buf, pfp);
953396Seric 	}
954396Seric 	else
955396Seric 	{
956396Seric 		unlink(pfn);
957396Seric 	}
958396Seric 	fclose(tfp);
959396Seric 	fclose(pfp);
960396Seric 	unlink(tfn);
961396Seric 
962396Seric 	if (delete)
963396Seric 	{
9641435Seric 		unlink(tail(fn));
9651435Seric 		printf("%12s: removed\n", tail(fn));
966585Seric 		return (TRUE);
967396Seric 	}
968396Seric 	else
969396Seric 	{
970416Seric 		printf("%12s: not being edited by you\n", fn);
971585Seric 		return (FALSE);
972396Seric 	}
973396Seric }
9741432Seric 
9751432Seric /*
9761433Seric **  DODIFF -- diff an s-file against a g-file
9771433Seric **
9781433Seric **	Parameters:
9791433Seric **		getv -- argv for the 'get' command.
9801433Seric **		gfile -- name of the g-file to diff against.
9811433Seric **
9821433Seric **	Returns:
9831433Seric **		Result of get.
9841433Seric **
9851433Seric **	Side Effects:
9861433Seric **		none.
9871433Seric */
9881433Seric 
9891433Seric dodiff(getv, gfile)
9901433Seric 	char **getv;
9911433Seric 	char *gfile;
9921433Seric {
9931433Seric 	int pipev[2];
9941433Seric 	int rval;
9951433Seric 	register int i;
9961433Seric 	register int pid;
9971433Seric 	auto int st;
9981433Seric 	extern int errno;
9991433Seric 	int (*osig)();
10001433Seric 
10011433Seric 	if (pipe(pipev) < 0)
10021433Seric 	{
10031433Seric 		syserr("dodiff: pipe failed");
10041433Seric 		exit(EX_OSERR);
10051433Seric 	}
10061433Seric 	if ((pid = fork()) < 0)
10071433Seric 	{
10081433Seric 		syserr("dodiff: fork failed");
10091433Seric 		exit(EX_OSERR);
10101433Seric 	}
10111433Seric 	else if (pid > 0)
10121433Seric 	{
10131433Seric 		/* in parent; run get */
10141433Seric 		OutFile = pipev[1];
10151433Seric 		close(pipev[0]);
10161433Seric 		rval = command(&getv[1], TRUE, FALSE, "get -s -k -p");
10171433Seric 		osig = signal(SIGINT, SIG_IGN);
10181433Seric 		while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR)
10191433Seric 			errno = 0;
10201433Seric 		signal(SIGINT, osig);
10211433Seric 		/* ignore result of diff */
10221433Seric 	}
10231433Seric 	else
10241433Seric 	{
10251433Seric 		/* in child, run diff */
10261433Seric 		if (close(pipev[1]) < 0 || close(0) < 0 ||
10271433Seric 		    dup(pipev[0]) != 0 || close(pipev[0]) < 0)
10281433Seric 		{
10291433Seric 			syserr("dodiff: magic failed");
10301433Seric 			exit(EX_OSERR);
10311433Seric 		}
10321433Seric 		execl(PROGPATH(bdiff), "bdiff", "-", gfile, NULL);
10331433Seric # ifndef V6
10341433Seric 		execlp("bdiff", "bdiff", "-", gfile, NULL);
10351433Seric 		execlp("diff", "diff", "-", gfile, NULL);
10361433Seric # endif NOT V6
10371433Seric 		syserr("bdiff: cannot execute");
10381433Seric 		exit(EX_OSERR);
10391433Seric 	}
10401433Seric 	return (rval);
10411433Seric }
10421433Seric 
10431433Seric /*
10441435Seric **  TAIL -- return tail of filename.
10451435Seric **
10461435Seric **	Parameters:
10471435Seric **		fn -- the filename.
10481435Seric **
10491435Seric **	Returns:
10501435Seric **		a pointer to the tail of the filename; e.g., given
10511435Seric **		"cmd/ls.c", "ls.c" is returned.
10521435Seric **
10531435Seric **	Side Effects:
10541435Seric **		none.
10551435Seric */
10561435Seric 
10571435Seric char *
10581435Seric tail(fn)
10591435Seric 	register char *fn;
10601435Seric {
10611435Seric 	register char *p;
10621435Seric 
10631435Seric 	for (p = fn; *p != 0; p++)
10641435Seric 		if (*p == '/' && p[1] != '\0' && p[1] != '/')
10651435Seric 			fn = &p[1];
10661435Seric 	return (fn);
10671435Seric }
10681435Seric 
10691435Seric /*
1070396Seric **  GETPFILE -- get an entry from the p-file
1071396Seric **
1072396Seric **	Parameters:
1073396Seric **		pfp -- p-file file pointer
1074396Seric **
1075396Seric **	Returns:
1076396Seric **		pointer to p-file struct for next entry
1077396Seric **		NULL on EOF or error
1078396Seric **
1079396Seric **	Side Effects:
1080396Seric **		Each call wipes out results of previous call.
1081396Seric */
1082396Seric 
1083396Seric struct pfile *
1084396Seric getpfile(pfp)
1085396Seric 	FILE *pfp;
1086396Seric {
1087396Seric 	static struct pfile ent;
1088396Seric 	static char buf[120];
1089396Seric 	register char *p;
1090396Seric 	extern char *nextfield();
1091396Seric 
1092396Seric 	if (fgets(buf, sizeof buf, pfp) == NULL)
1093396Seric 		return (NULL);
1094396Seric 
1095396Seric 	ent.p_osid = p = buf;
1096396Seric 	ent.p_nsid = p = nextfield(p);
1097396Seric 	ent.p_user = p = nextfield(p);
1098396Seric 	ent.p_date = p = nextfield(p);
1099396Seric 	ent.p_time = p = nextfield(p);
1100396Seric 	if (p == NULL || nextfield(p) != NULL)
1101396Seric 		return (NULL);
1102396Seric 
1103396Seric 	return (&ent);
1104396Seric }
1105396Seric 
1106396Seric 
1107396Seric char *
1108396Seric nextfield(p)
1109396Seric 	register char *p;
1110396Seric {
1111396Seric 	if (p == NULL || *p == '\0')
1112396Seric 		return (NULL);
1113396Seric 	while (*p != ' ' && *p != '\n' && *p != '\0')
1114396Seric 		p++;
1115396Seric 	if (*p == '\n' || *p == '\0')
1116396Seric 	{
1117396Seric 		*p = '\0';
1118396Seric 		return (NULL);
1119396Seric 	}
1120396Seric 	*p++ = '\0';
1121396Seric 	return (p);
1122396Seric }
11231432Seric 
11241432Seric /*
11251205Seric **  USRERR -- issue user-level error
11261205Seric **
11271205Seric **	Parameters:
11281205Seric **		f -- format string.
11291205Seric **		p1-p3 -- parameters to a printf.
11301205Seric **
11311205Seric **	Returns:
11321205Seric **		-1
11331205Seric **
11341205Seric **	Side Effects:
11351205Seric **		none.
11361205Seric */
11371205Seric 
11381205Seric usrerr(f, p1, p2, p3)
11391205Seric 	char *f;
11401205Seric {
11411205Seric 	fprintf(stderr, "\n%s: ", MyName);
11421205Seric 	fprintf(stderr, f, p1, p2, p3);
11431205Seric 	fprintf(stderr, "\n");
11441205Seric 
11451205Seric 	return (-1);
11461205Seric }
11471432Seric 
11481432Seric /*
11491205Seric **  SYSERR -- print system-generated error.
11501205Seric **
11511205Seric **	Parameters:
11521205Seric **		f -- format string to a printf.
11531205Seric **		p1, p2, p3 -- parameters to f.
11541205Seric **
11551205Seric **	Returns:
11561205Seric **		never.
11571205Seric **
11581205Seric **	Side Effects:
11591205Seric **		none.
11601205Seric */
11611205Seric 
11621205Seric syserr(f, p1, p2, p3)
11631205Seric 	char *f;
11641205Seric {
11651205Seric 	extern int errno;
11661205Seric 
11671205Seric 	fprintf(stderr, "\n%s SYSERR: ", MyName);
11681205Seric 	fprintf(stderr, f, p1, p2, p3);
11691205Seric 	fprintf(stderr, "\n");
11701205Seric 	if (errno == 0)
11711205Seric 		exit(EX_SOFTWARE);
11721205Seric 	else
11731205Seric 	{
11741205Seric 		perror(0);
11751205Seric 		exit(EX_OSERR);
11761205Seric 	}
11771205Seric }
1178