xref: /csrg-svn/usr.bin/sccs/sccs.c (revision 1431)
1148Seric # include <stdio.h>
2148Seric # include <sys/types.h>
3148Seric # include <sys/stat.h>
4261Seric # include <sys/dir.h>
5148Seric # include <sysexits.h>
6202Seric # include <whoami.h>
7148Seric 
8828Seric /*
9828Seric **  SCCS.C -- human-oriented front end to the SCCS system.
10828Seric **
11828Seric **	Without trying to add any functionality to speak of, this
12828Seric **	program tries to make SCCS a little more accessible to human
13828Seric **	types.  The main thing it does is automatically put the
14828Seric **	string "SCCS/s." on the front of names.  Also, it has a
15828Seric **	couple of things that are designed to shorten frequent
16828Seric **	combinations, e.g., "delget" which expands to a "delta"
17828Seric **	and a "get".
18828Seric **
19828Seric **	This program can also function as a setuid front end.
20828Seric **	To do this, you should copy the source, renaming it to
21828Seric **	whatever you want, e.g., "syssccs".  Change any defaults
22828Seric **	in the program (e.g., syssccs might default -d to
23828Seric **	"/usr/src/sys").  Then recompile and put the result
24828Seric **	as setuid to whomever you want.  In this mode, sccs
25828Seric **	knows to not run setuid for certain programs in order
26828Seric **	to preserve security, and so forth.
27828Seric **
28828Seric **	Usage:
29828Seric **		sccs [flags] command [args]
30828Seric **
31828Seric **	Flags:
32828Seric **		-d<dir>		<dir> represents a directory to search
33828Seric **				out of.  It should be a full pathname
34828Seric **				for general usage.  E.g., if <dir> is
35828Seric **				"/usr/src/sys", then a reference to the
36828Seric **				file "dev/bio.c" becomes a reference to
37828Seric **				"/usr/src/sys/dev/bio.c".
38828Seric **		-p<path>	prepends <path> to the final component
39828Seric **				of the pathname.  By default, this is
40828Seric **				"SCCS".  For example, in the -d example
41828Seric **				above, the path then gets modified to
42828Seric **				"/usr/src/sys/dev/SCCS/s.bio.c".  In
43828Seric **				more common usage (without the -d flag),
44828Seric **				"prog.c" would get modified to
45828Seric **				"SCCS/s.prog.c".  In both cases, the
46828Seric **				"s." gets automatically prepended.
47828Seric **		-r		run as the real user.
48828Seric **
49828Seric **	Commands:
50828Seric **		admin,
51828Seric **		get,
52828Seric **		delta,
53828Seric **		rmdel,
54828Seric **		chghist,
55828Seric **		etc.		Straight out of SCCS; only difference
56828Seric **				is that pathnames get modified as
57828Seric **				described above.
58828Seric **		edit		Macro for "get -e".
59828Seric **		unedit		Removes a file being edited, knowing
60828Seric **				about p-files, etc.
61828Seric **		delget		Macro for "delta" followed by "get".
62828Seric **		deledit		Macro for "delta" followed by "get -e".
63828Seric **		info		Tell what files being edited.
64828Seric **		clean		Remove all files that can be
65828Seric **				regenerated from SCCS files.
661205Seric **		check		Like info, but return exit status, for
67828Seric **				use in makefiles.
68828Seric **		fix		Remove a top delta & reedit, but save
69828Seric **				the previous changes in that delta.
70828Seric **
71828Seric **	Compilation Flags:
72828Seric **		UIDUSER -- determine who the user is by looking at the
73828Seric **			uid rather than the login name -- for machines
74828Seric **			where SCCS gets the user in this way.
751270Seric **		SCCSDIR -- if defined, forces the -d flag to take on
761205Seric **			this value.  This is so that the setuid
771205Seric **			aspects of this program cannot be abused.
781270Seric **			This flag also disables the -p flag.
791270Seric **		SCCSPATH -- the default for the -p flag.
80828Seric **
81828Seric **	Compilation Instructions:
82828Seric **		cc -O -n -s sccs.c
83828Seric **
84828Seric **	Author:
85828Seric **		Eric Allman, UCB/INGRES
861270Seric **		Copyright 1980 Regents of the University of California
87828Seric */
88155Seric 
891270Seric /*******************  Configuration Information  ********************/
901270Seric 
91828Seric # ifdef CSVAX
92828Seric # define UIDUSER
931270Seric # define PROGPATH(name)	"/usr/local/name"
941270Seric # endif CSVAX
95828Seric 
961270Seric # define SCCSPATH	"SCCS"
971270Seric /* put #define SCCSDIR here */
98828Seric 
991270Seric char	MyName[] = "sccs";	/* name used in messages */
1001270Seric 
1011270Seric /****************  End of Configuration Information  ****************/
1021270Seric 
103*1431Seric static char SccsId[] = "@(#)sccs.c	1.34 10/15/80";
1041270Seric 
105157Seric # define bitset(bit, word)	((bit) & (word))
106157Seric 
107157Seric typedef char	bool;
108200Seric # define TRUE	1
109200Seric # define FALSE	0
110157Seric 
111828Seric # ifdef UIDUSER
112828Seric # include <pwd.h>
113828Seric # endif UIDUSER
114828Seric 
115148Seric struct sccsprog
116148Seric {
117148Seric 	char	*sccsname;	/* name of SCCS routine */
118200Seric 	short	sccsoper;	/* opcode, see below */
119200Seric 	short	sccsflags;	/* flags, see below */
1201316Seric 	char	*sccsklets;	/* valid key-letters on macros */
121148Seric 	char	*sccspath;	/* pathname of binary implementing */
122148Seric };
123148Seric 
124200Seric /* values for sccsoper */
125200Seric # define PROG		0	/* call a program */
126201Seric # define CMACRO		1	/* command substitution macro */
127226Seric # define FIX		2	/* fix a delta */
128261Seric # define CLEAN		3	/* clean out recreatable files */
129396Seric # define UNEDIT		4	/* unedit a file */
130*1431Seric # define SHELL		5	/* call a shell file (like PROG) */
131200Seric 
132157Seric /* bits for sccsflags */
133200Seric # define NO_SDOT	0001	/* no s. on front of args */
134200Seric # define REALUSER	0002	/* protected (e.g., admin) */
135148Seric 
136819Seric /* modes for the "clean", "info", "check" ops */
137819Seric # define CLEANC		0	/* clean command */
138819Seric # define INFOC		1	/* info command */
139819Seric # define CHECKC		2	/* check command */
140819Seric 
141202Seric # ifndef PROGPATH
142202Seric # define PROGPATH(name)	"/usr/sccs/name"
143202Seric # endif PROGPATH
144202Seric 
145148Seric struct sccsprog SccsProg[] =
146148Seric {
1471316Seric 	"admin",	PROG,	REALUSER,	"",		PROGPATH(admin),
1481316Seric 	"chghist",	PROG,	0,		"",		PROGPATH(rmdel),
1491316Seric 	"comb",		PROG,	0,		"",		PROGPATH(comb),
1501316Seric 	"delta",	PROG,	0,		"mysrp",	PROGPATH(delta),
1511317Seric 	"get",		PROG,	0,		"ixbeskcl",	PROGPATH(get),
1521316Seric 	"help",		PROG,	NO_SDOT,	"",		PROGPATH(help),
1531316Seric 	"prt",		PROG,	0,		"",		PROGPATH(prt),
1541316Seric 	"rmdel",	PROG,	REALUSER,	"",		PROGPATH(rmdel),
1551316Seric 	"what",		PROG,	NO_SDOT,	"",		PROGPATH(what),
156*1431Seric 	"sccsdiff",	SHELL,	REALUSER,	"",		PROGPATH(sccsdiff),
1571317Seric 	"edit",		CMACRO,	NO_SDOT,	"ixbscl",	"get -e",
1581316Seric 	"delget",	CMACRO,	NO_SDOT,	"",		"delta/get -t",
1591316Seric 	"deledit",	CMACRO,	NO_SDOT,	"",		"delta/get -e -t",
1601316Seric 	"fix",		FIX,	NO_SDOT,	"",		NULL,
1611316Seric 	"clean",	CLEAN,	REALUSER,	"",		(char *) CLEANC,
1621316Seric 	"info",		CLEAN,	REALUSER,	"",		(char *) INFOC,
1631316Seric 	"check",	CLEAN,	REALUSER,	"",		(char *) CHECKC,
1641316Seric 	"unedit",	UNEDIT,	NO_SDOT,	"",		NULL,
1651316Seric 	NULL,		-1,	0,		"",		NULL
166148Seric };
167148Seric 
168396Seric struct pfile
169396Seric {
170396Seric 	char	*p_osid;	/* old SID */
171396Seric 	char	*p_nsid;	/* new SID */
172396Seric 	char	*p_user;	/* user who did edit */
173396Seric 	char	*p_date;	/* date of get */
174396Seric 	char	*p_time;	/* time of get */
175396Seric };
176396Seric 
1771270Seric char	*SccsPath = SCCSPATH;	/* pathname of SCCS files */
1781270Seric # ifdef SCCSDIR
1791270Seric char	*SccsDir = SCCSDIR;	/* directory to begin search from */
1801205Seric # else
1811270Seric char	*SccsDir = "";
1821205Seric # endif
183157Seric bool	RealUser;		/* if set, running as real user */
184393Seric # ifdef DEBUG
185393Seric bool	Debug;			/* turn on tracing */
186393Seric # endif
187148Seric 
188148Seric main(argc, argv)
189148Seric 	int argc;
190148Seric 	char **argv;
191148Seric {
192148Seric 	register char *p;
193262Seric 	extern struct sccsprog *lookup();
1941282Seric 	register int i;
195148Seric 
196148Seric 	/*
197148Seric 	**  Detect and decode flags intended for this program.
198148Seric 	*/
199148Seric 
200200Seric 	if (argc < 2)
201148Seric 	{
2021205Seric 		fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName);
203200Seric 		exit(EX_USAGE);
204200Seric 	}
205200Seric 	argv[argc] = NULL;
206200Seric 
207262Seric 	if (lookup(argv[0]) == NULL)
208200Seric 	{
209262Seric 		while ((p = *++argv) != NULL)
210148Seric 		{
211262Seric 			if (*p != '-')
212262Seric 				break;
213262Seric 			switch (*++p)
214262Seric 			{
215262Seric 			  case 'r':		/* run as real user */
216262Seric 				setuid(getuid());
217262Seric 				RealUser++;
218262Seric 				break;
219148Seric 
2201270Seric # ifndef SCCSDIR
221262Seric 			  case 'p':		/* path of sccs files */
222262Seric 				SccsPath = ++p;
223262Seric 				break;
224148Seric 
225588Seric 			  case 'd':		/* directory to search from */
226588Seric 				SccsDir = ++p;
227588Seric 				break;
2281205Seric # endif
229588Seric 
230393Seric # ifdef DEBUG
231393Seric 			  case 'T':		/* trace */
232393Seric 				Debug++;
233393Seric 				break;
234393Seric # endif
235393Seric 
236262Seric 			  default:
2371205Seric 				usrerr("unknown option -%s", p);
238262Seric 				break;
239262Seric 			}
240148Seric 		}
241262Seric 		if (SccsPath[0] == '\0')
242262Seric 			SccsPath = ".";
243148Seric 	}
244148Seric 
2451316Seric 	i = command(argv, FALSE, FALSE, "");
2461282Seric 	exit(i);
247200Seric }
2481282Seric /*
2491282Seric **  COMMAND -- look up and perform a command
2501282Seric **
2511282Seric **	This routine is the guts of this program.  Given an
2521282Seric **	argument vector, it looks up the "command" (argv[0])
2531282Seric **	in the configuration table and does the necessary stuff.
2541282Seric **
2551282Seric **	Parameters:
2561282Seric **		argv -- an argument vector to process.
2571282Seric **		forkflag -- if set, fork before executing the command.
2581316Seric **		editflag -- if set, only include flags listed in the
2591316Seric **			sccsklets field of the command descriptor.
2601316Seric **		arg0 -- a space-seperated list of arguments to insert
2611316Seric **			before argv.
2621282Seric **
2631282Seric **	Returns:
2641282Seric **		zero -- command executed ok.
2651282Seric **		else -- error status.
2661282Seric **
2671282Seric **	Side Effects:
2681282Seric **		none.
2691282Seric */
270157Seric 
2711316Seric command(argv, forkflag, editflag, arg0)
272200Seric 	char **argv;
273201Seric 	bool forkflag;
2741316Seric 	bool editflag;
2751316Seric 	char *arg0;
276200Seric {
277200Seric 	register struct sccsprog *cmd;
278200Seric 	register char *p;
279201Seric 	char buf[40];
280262Seric 	extern struct sccsprog *lookup();
2811316Seric 	char *nav[1000];
2821316Seric 	char **np;
283*1431Seric 	register char **ap;
284585Seric 	register int i;
285*1431Seric 	register char *q;
286585Seric 	extern bool unedit();
2871282Seric 	int rval = 0;
2881316Seric 	extern char *index();
2891316Seric 	extern char *makefile();
290200Seric 
291393Seric # ifdef DEBUG
292393Seric 	if (Debug)
293393Seric 	{
2941316Seric 		printf("command:\n\t\"%s\"\n", arg0);
2951316Seric 		for (np = argv; *np != NULL; np++)
2961316Seric 			printf("\t\"%s\"\n", *np);
297393Seric 	}
298393Seric # endif
299393Seric 
300157Seric 	/*
3011316Seric 	**  Copy arguments.
3021316Seric 	**	Phase one -- from arg0 & if necessary argv[0].
3031316Seric 	*/
3041316Seric 
305*1431Seric 	np = ap = &nav[1];
3061316Seric 	for (p = arg0, q = buf; *p != '\0' && *p != '/'; )
3071316Seric 	{
3081316Seric 		*np++ = q;
3091316Seric 		while (*p == ' ')
3101316Seric 			p++;
3111316Seric 		while (*p != ' ' && *p != '\0' && *p != '/')
3121316Seric 			*q++ = *p++;
3131316Seric 		*q++ = '\0';
3141316Seric 	}
3151316Seric 	*np = NULL;
316*1431Seric 	if (*ap == NULL)
3171316Seric 		*np++ = *argv++;
3181316Seric 
3191316Seric 	/*
320148Seric 	**  Look up command.
321*1431Seric 	**	At this point, *ap is the command name.
322148Seric 	*/
323148Seric 
324*1431Seric 	cmd = lookup(*ap);
325262Seric 	if (cmd == NULL)
326148Seric 	{
327*1431Seric 		usrerr("Unknown command \"%s\"", *ap);
3281282Seric 		return (EX_USAGE);
329148Seric 	}
330148Seric 
331148Seric 	/*
3321316Seric 	**  Copy remaining arguments doing editing as appropriate.
3331316Seric 	*/
3341316Seric 
3351316Seric 	for (; *argv != NULL; argv++)
3361316Seric 	{
3371316Seric 		p = *argv;
3381316Seric 		if (*p == '-')
3391316Seric 		{
3401316Seric 			if (p[1] == '\0' || !editflag || cmd->sccsklets == NULL ||
3411316Seric 			    index(cmd->sccsklets, p[1]) != NULL)
3421316Seric 				*np++ = p;
3431316Seric 		}
3441316Seric 		else
3451316Seric 		{
3461316Seric 			if (!bitset(NO_SDOT, cmd->sccsflags))
3471316Seric 				p = makefile(p);
3481316Seric 			if (p != NULL)
3491316Seric 				*np++ = p;
3501316Seric 		}
3511316Seric 	}
3521316Seric 	*np = NULL;
3531316Seric 
3541316Seric 	/*
355200Seric 	**  Interpret operation associated with this command.
356157Seric 	*/
357157Seric 
358200Seric 	switch (cmd->sccsoper)
359200Seric 	{
360*1431Seric 	  case SHELL:		/* call a shell file */
361*1431Seric 		*ap = cmd->sccspath;
362*1431Seric 		*--ap = "sh";
363*1431Seric 		rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag);
364*1431Seric 		break;
365*1431Seric 
366200Seric 	  case PROG:		/* call an sccs prog */
367*1431Seric 		rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag);
368201Seric 		break;
369201Seric 
370201Seric 	  case CMACRO:		/* command macro */
371201Seric 		for (p = cmd->sccspath; *p != '\0'; p++)
372201Seric 		{
3731316Seric 			q = p;
3741316Seric 			while (*p != '\0' && *p != '/')
3751316Seric 				p++;
376*1431Seric 			rval = command(&ap[1], *p != '\0', TRUE, q);
3771282Seric 			if (rval != 0)
3781282Seric 				break;
379201Seric 		}
3801282Seric 		break;
381157Seric 
382226Seric 	  case FIX:		/* fix a delta */
383*1431Seric 		if (strncmp(ap[1], "-r", 2) != 0)
384226Seric 		{
3851205Seric 			usrerr("-r flag needed for fix command");
3861282Seric 			rval = EX_USAGE;
387226Seric 			break;
388226Seric 		}
389*1431Seric 		rval = command(&ap[1], TRUE, TRUE, "get -k");
3901282Seric 		if (rval == 0)
391*1431Seric 			rval = command(&ap[1], TRUE, TRUE, "rmdel");
3921282Seric 		if (rval == 0)
393*1431Seric 			rval = command(&ap[2], FALSE, TRUE, "get -e -g");
3941282Seric 		break;
395226Seric 
396261Seric 	  case CLEAN:
3971282Seric 		rval = clean((int) cmd->sccspath);
398261Seric 		break;
399261Seric 
400396Seric 	  case UNEDIT:
401*1431Seric 		for (argv = np = &ap[1]; *argv != NULL; argv++)
402585Seric 		{
4031316Seric 			if (unedit(*argv))
4041316Seric 				*np++ = *argv;
405585Seric 		}
4061316Seric 		*np = NULL;
407585Seric 		if (i > 0)
408*1431Seric 			rval = command(&ap[1], FALSE, FALSE, "get");
409396Seric 		break;
410396Seric 
411200Seric 	  default:
4121205Seric 		syserr("oper %d", cmd->sccsoper);
413200Seric 		exit(EX_SOFTWARE);
414200Seric 	}
4151282Seric # ifdef DEBUG
4161282Seric 	if (Debug)
4171282Seric 		printf("command: rval=%d\n", rval);
4181282Seric # endif
4191282Seric 	return (rval);
420200Seric }
421262Seric /*
422262Seric **  LOOKUP -- look up an SCCS command name.
423262Seric **
424262Seric **	Parameters:
425262Seric **		name -- the name of the command to look up.
426262Seric **
427262Seric **	Returns:
428262Seric **		ptr to command descriptor for this command.
429262Seric **		NULL if no such entry.
430262Seric **
431262Seric **	Side Effects:
432262Seric **		none.
433262Seric */
434200Seric 
435262Seric struct sccsprog *
436262Seric lookup(name)
437262Seric 	char *name;
438262Seric {
439262Seric 	register struct sccsprog *cmd;
440226Seric 
441262Seric 	for (cmd = SccsProg; cmd->sccsname != NULL; cmd++)
442262Seric 	{
443262Seric 		if (strcmp(cmd->sccsname, name) == 0)
444262Seric 			return (cmd);
445262Seric 	}
446262Seric 	return (NULL);
447262Seric }
4481282Seric /*
4491282Seric **  CALLPROG -- call a program
4501282Seric **
4511316Seric **	Used to call the SCCS programs.
4521282Seric **
4531282Seric **	Parameters:
4541282Seric **		progpath -- pathname of the program to call.
4551282Seric **		flags -- status flags from the command descriptors.
4561282Seric **		argv -- an argument vector to pass to the program.
4571282Seric **		forkflag -- if true, fork before calling, else just
4581282Seric **			exec.
4591282Seric **
4601282Seric **	Returns:
4611282Seric **		The exit status of the program.
4621282Seric **		Nothing if forkflag == FALSE.
4631282Seric **
4641282Seric **	Side Effects:
4651282Seric **		Can exit if forkflag == FALSE.
4661282Seric */
467226Seric 
468200Seric callprog(progpath, flags, argv, forkflag)
469200Seric 	char *progpath;
470200Seric 	short flags;
471200Seric 	char **argv;
472200Seric 	bool forkflag;
473200Seric {
474200Seric 	register int i;
475201Seric 	auto int st;
476200Seric 
4771316Seric # ifdef DEBUG
4781316Seric 	if (Debug)
4791316Seric 	{
4801316Seric 		printf("callprog:\n");
4811316Seric 		for (i = 0; argv[i] != NULL; i++)
4821316Seric 			printf("\t\"%s\"\n", argv[i]);
4831316Seric 	}
4841316Seric # endif
4851316Seric 
486200Seric 	if (*argv == NULL)
487200Seric 		return (-1);
488200Seric 
489157Seric 	/*
490226Seric 	**  Fork if appropriate.
491148Seric 	*/
492148Seric 
493200Seric 	if (forkflag)
494200Seric 	{
495393Seric # ifdef DEBUG
496393Seric 		if (Debug)
497393Seric 			printf("Forking\n");
498393Seric # endif
499200Seric 		i = fork();
500200Seric 		if (i < 0)
501200Seric 		{
5021205Seric 			syserr("cannot fork");
503200Seric 			exit(EX_OSERR);
504200Seric 		}
505200Seric 		else if (i > 0)
506201Seric 		{
507201Seric 			wait(&st);
5081282Seric 			if ((st & 0377) == 0)
5091282Seric 				st = (st >> 8) & 0377;
510201Seric 			return (st);
511201Seric 		}
512200Seric 	}
513200Seric 
514200Seric 	/*
515200Seric 	**  Set protection as appropriate.
516200Seric 	*/
517200Seric 
518200Seric 	if (bitset(REALUSER, flags))
519200Seric 		setuid(getuid());
520226Seric 
521200Seric 	/*
522226Seric 	**  Call real SCCS program.
523200Seric 	*/
524200Seric 
525226Seric 	execv(progpath, argv);
5261205Seric 	syserr("cannot execute %s", progpath);
527148Seric 	exit(EX_UNAVAILABLE);
528148Seric }
529586Seric /*
530586Seric **  MAKEFILE -- make filename of SCCS file
531586Seric **
532586Seric **	If the name passed is already the name of an SCCS file,
533586Seric **	just return it.  Otherwise, munge the name into the name
534586Seric **	of the actual SCCS file.
535586Seric **
536586Seric **	There are cases when it is not clear what you want to
537586Seric **	do.  For example, if SccsPath is an absolute pathname
538586Seric **	and the name given is also an absolute pathname, we go
539586Seric **	for SccsPath (& only use the last component of the name
540586Seric **	passed) -- this is important for security reasons (if
541586Seric **	sccs is being used as a setuid front end), but not
542586Seric **	particularly intuitive.
543586Seric **
544586Seric **	Parameters:
545586Seric **		name -- the file name to be munged.
546586Seric **
547586Seric **	Returns:
548586Seric **		The pathname of the sccs file.
549586Seric **		NULL on error.
550586Seric **
551586Seric **	Side Effects:
552586Seric **		none.
553586Seric */
554148Seric 
555148Seric char *
556148Seric makefile(name)
557148Seric 	char *name;
558148Seric {
559148Seric 	register char *p;
560148Seric 	register char c;
561148Seric 	char buf[512];
562588Seric 	struct stat stbuf;
563148Seric 	extern char *malloc();
564586Seric 	extern char *rindex();
565588Seric 	extern bool safepath();
566587Seric 	extern bool isdir();
567587Seric 	register char *q;
568148Seric 
569586Seric 	p = rindex(name, '/');
570586Seric 	if (p == NULL)
571586Seric 		p = name;
572586Seric 	else
573586Seric 		p++;
574586Seric 
575148Seric 	/*
576588Seric 	**  Check to see that the path is "safe", i.e., that we
577588Seric 	**  are not letting some nasty person use the setuid part
578588Seric 	**  of this program to look at or munge some presumably
579588Seric 	**  hidden files.
580148Seric 	*/
581148Seric 
582588Seric 	if (SccsDir[0] == '/' && !safepath(name))
583588Seric 		return (NULL);
584586Seric 
585586Seric 	/*
586588Seric 	**  Create the base pathname.
587586Seric 	*/
588586Seric 
589588Seric 	if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0)
590148Seric 	{
591588Seric 		strcpy(buf, SccsDir);
592586Seric 		strcat(buf, "/");
593586Seric 	}
594586Seric 	else
595586Seric 		strcpy(buf, "");
596587Seric 	strncat(buf, name, p - name);
597587Seric 	q = &buf[strlen(buf)];
598587Seric 	strcpy(q, p);
599587Seric 	if (strncmp(p, "s.", 2) != 0 && !isdir(buf))
600586Seric 	{
601588Seric 		strcpy(q, SccsPath);
602588Seric 		strcat(buf, "/s.");
603586Seric 		strcat(buf, p);
604586Seric 	}
605148Seric 
606588Seric 	if (strcmp(buf, name) == 0)
607588Seric 		p = name;
608588Seric 	else
609148Seric 	{
610588Seric 		p = malloc(strlen(buf) + 1);
611588Seric 		if (p == NULL)
612588Seric 		{
613588Seric 			perror("Sccs: no mem");
614588Seric 			exit(EX_OSERR);
615588Seric 		}
616588Seric 		strcpy(p, buf);
617148Seric 	}
618148Seric 	return (p);
619148Seric }
620261Seric /*
621587Seric **  ISDIR -- return true if the argument is a directory.
622587Seric **
623587Seric **	Parameters:
624587Seric **		name -- the pathname of the file to check.
625587Seric **
626587Seric **	Returns:
627587Seric **		TRUE if 'name' is a directory, FALSE otherwise.
628587Seric **
629587Seric **	Side Effects:
630587Seric **		none.
631587Seric */
632587Seric 
633587Seric bool
634587Seric isdir(name)
635587Seric 	char *name;
636587Seric {
637587Seric 	struct stat stbuf;
638587Seric 
639587Seric 	return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR);
640587Seric }
641587Seric /*
642586Seric **  SAFEPATH -- determine whether a pathname is "safe"
643586Seric **
644586Seric **	"Safe" pathnames only allow you to get deeper into the
645586Seric **	directory structure, i.e., full pathnames and ".." are
646586Seric **	not allowed.
647586Seric **
648586Seric **	Parameters:
649586Seric **		p -- the name to check.
650586Seric **
651586Seric **	Returns:
652586Seric **		TRUE -- if the path is safe.
653586Seric **		FALSE -- if the path is not safe.
654586Seric **
655586Seric **	Side Effects:
656586Seric **		Prints a message if the path is not safe.
657586Seric */
658586Seric 
659586Seric bool
660586Seric safepath(p)
661586Seric 	register char *p;
662586Seric {
663586Seric 	extern char *index();
664586Seric 
665586Seric 	if (*p != '/')
666586Seric 	{
667586Seric 		while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0)
668586Seric 		{
669586Seric 			p = index(p, '/');
670586Seric 			if (p == NULL)
671586Seric 				return (TRUE);
672586Seric 			p++;
673586Seric 		}
674586Seric 	}
675586Seric 
676586Seric 	printf("You may not use full pathnames or \"..\"\n");
677586Seric 	return (FALSE);
678586Seric }
679586Seric /*
680261Seric **  CLEAN -- clean out recreatable files
681261Seric **
682261Seric **	Any file for which an "s." file exists but no "p." file
683261Seric **	exists in the current directory is purged.
684261Seric **
685261Seric **	Parameters:
686819Seric **		tells whether this came from a "clean", "info", or
687819Seric **		"check" command.
688261Seric **
689261Seric **	Returns:
690261Seric **		none.
691261Seric **
692261Seric **	Side Effects:
693819Seric **		Removes files in the current directory.
694819Seric **		Prints information regarding files being edited.
695819Seric **		Exits if a "check" command.
696261Seric */
697261Seric 
698819Seric clean(mode)
699819Seric 	int mode;
700261Seric {
701261Seric 	struct direct dir;
702261Seric 	struct stat stbuf;
703261Seric 	char buf[100];
704394Seric 	char pline[120];
705346Seric 	register FILE *dirfd;
706346Seric 	register char *basefile;
707351Seric 	bool gotedit;
708394Seric 	FILE *pfp;
709261Seric 
7101207Seric 	strcpy(buf, SccsDir);
7111207Seric 	if (buf[0] != '\0')
7121207Seric 		strcat(buf, "/");
7131207Seric 	strcat(buf, SccsPath);
7141207Seric 	dirfd = fopen(buf, "r");
715261Seric 	if (dirfd == NULL)
716261Seric 	{
7171207Seric 		usrerr("cannot open %s", buf);
7181282Seric 		return (EX_NOINPUT);
719261Seric 	}
720261Seric 
721261Seric 	/*
722261Seric 	**  Scan the SCCS directory looking for s. files.
723261Seric 	*/
724261Seric 
725351Seric 	gotedit = FALSE;
726261Seric 	while (fread(&dir, sizeof dir, 1, dirfd) != NULL)
727261Seric 	{
728568Seric 		if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0)
729261Seric 			continue;
730261Seric 
731261Seric 		/* got an s. file -- see if the p. file exists */
7321207Seric 		strcpy(buf, SccsDir);
7331207Seric 		if (buf[0] != '\0')
7341207Seric 			strcat(buf, "/");
7351207Seric 		strcat(buf, SccsPath);
736261Seric 		strcat(buf, "/p.");
737346Seric 		basefile = &buf[strlen(buf)];
738568Seric 		strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2);
739346Seric 		basefile[sizeof dir.d_name - 2] = '\0';
740394Seric 		pfp = fopen(buf, "r");
741394Seric 		if (pfp != NULL)
742346Seric 		{
743394Seric 			while (fgets(pline, sizeof pline, pfp) != NULL)
744416Seric 				printf("%12s: being edited: %s", basefile, pline);
745394Seric 			fclose(pfp);
746351Seric 			gotedit = TRUE;
747261Seric 			continue;
748346Seric 		}
749261Seric 
750261Seric 		/* the s. file exists and no p. file exists -- unlink the g-file */
751819Seric 		if (mode == CLEANC)
752346Seric 		{
753568Seric 			strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2);
754346Seric 			buf[sizeof dir.d_name - 2] = '\0';
755346Seric 			unlink(buf);
756346Seric 		}
757261Seric 	}
758261Seric 
759261Seric 	fclose(dirfd);
760819Seric 	if (!gotedit && mode == INFOC)
761416Seric 		printf("Nothing being edited\n");
762819Seric 	if (mode == CHECKC)
763819Seric 		exit(gotedit);
7641282Seric 	return (EX_OK);
765261Seric }
766396Seric /*
767396Seric **  UNEDIT -- unedit a file
768396Seric **
769396Seric **	Checks to see that the current user is actually editting
770396Seric **	the file and arranges that s/he is not editting it.
771396Seric **
772396Seric **	Parameters:
773416Seric **		fn -- the name of the file to be unedited.
774396Seric **
775396Seric **	Returns:
776585Seric **		TRUE -- if the file was successfully unedited.
777585Seric **		FALSE -- if the file was not unedited for some
778585Seric **			reason.
779396Seric **
780396Seric **	Side Effects:
781396Seric **		fn is removed
782396Seric **		entries are removed from pfile.
783396Seric */
784396Seric 
785585Seric bool
786396Seric unedit(fn)
787396Seric 	char *fn;
788396Seric {
789396Seric 	register FILE *pfp;
790396Seric 	char *pfn;
791396Seric 	static char tfn[] = "/tmp/sccsXXXXX";
792396Seric 	FILE *tfp;
793396Seric 	register char *p;
794396Seric 	register char *q;
795396Seric 	bool delete = FALSE;
796396Seric 	bool others = FALSE;
797396Seric 	char *myname;
798396Seric 	extern char *getlogin();
799396Seric 	struct pfile *pent;
800396Seric 	extern struct pfile *getpfile();
801396Seric 	char buf[120];
8021316Seric 	extern char *makefile();
803828Seric # ifdef UIDUSER
804828Seric 	struct passwd *pw;
805828Seric 	extern struct passwd *getpwuid();
806828Seric # endif UIDUSER
807396Seric 
808396Seric 	/* make "s." filename & find the trailing component */
809396Seric 	pfn = makefile(fn);
810586Seric 	if (pfn == NULL)
811586Seric 		return (FALSE);
812586Seric 	q = rindex(pfn, '/');
813586Seric 	if (q == NULL)
814586Seric 		q = &pfn[-1];
815586Seric 	if (q[1] != 's' || q[2] != '.')
816396Seric 	{
8171205Seric 		usrerr("bad file name \"%s\"", fn);
818585Seric 		return (FALSE);
819396Seric 	}
820396Seric 
821396Seric 	/* turn "s." into "p." */
822396Seric 	*++q = 'p';
823396Seric 
824396Seric 	pfp = fopen(pfn, "r");
825396Seric 	if (pfp == NULL)
826396Seric 	{
827416Seric 		printf("%12s: not being edited\n", fn);
828585Seric 		return (FALSE);
829396Seric 	}
830396Seric 
831396Seric 	/*
832396Seric 	**  Copy p-file to temp file, doing deletions as needed.
833396Seric 	*/
834396Seric 
835396Seric 	mktemp(tfn);
836396Seric 	tfp = fopen(tfn, "w");
837396Seric 	if (tfp == NULL)
838396Seric 	{
8391205Seric 		usrerr("cannot create \"%s\"", tfn);
840396Seric 		exit(EX_OSERR);
841396Seric 	}
842396Seric 
843828Seric # ifdef UIDUSER
844828Seric 	pw = getpwuid(getuid());
845828Seric 	if (pw == NULL)
846828Seric 	{
8471205Seric 		syserr("who are you? (uid=%d)", getuid());
848828Seric 		exit(EX_OSERR);
849828Seric 	}
850828Seric 	myname = pw->pw_name;
851828Seric # else
852396Seric 	myname = getlogin();
853828Seric # endif UIDUSER
854396Seric 	while ((pent = getpfile(pfp)) != NULL)
855396Seric 	{
856396Seric 		if (strcmp(pent->p_user, myname) == 0)
857396Seric 		{
858396Seric 			/* a match */
859396Seric 			delete++;
860396Seric 		}
861396Seric 		else
862396Seric 		{
863396Seric 			fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid,
864396Seric 			    pent->p_nsid, pent->p_user, pent->p_date,
865396Seric 			    pent->p_time);
866396Seric 			others++;
867396Seric 		}
868396Seric 	}
869396Seric 
870396Seric 	/* do final cleanup */
871396Seric 	if (others)
872396Seric 	{
873396Seric 		if (freopen(tfn, "r", tfp) == NULL)
874396Seric 		{
8751205Seric 			syserr("cannot reopen \"%s\"", tfn);
876396Seric 			exit(EX_OSERR);
877396Seric 		}
878396Seric 		if (freopen(pfn, "w", pfp) == NULL)
879396Seric 		{
8801205Seric 			usrerr("cannot create \"%s\"", pfn);
881585Seric 			return (FALSE);
882396Seric 		}
883396Seric 		while (fgets(buf, sizeof buf, tfp) != NULL)
884396Seric 			fputs(buf, pfp);
885396Seric 	}
886396Seric 	else
887396Seric 	{
888396Seric 		unlink(pfn);
889396Seric 	}
890396Seric 	fclose(tfp);
891396Seric 	fclose(pfp);
892396Seric 	unlink(tfn);
893396Seric 
894396Seric 	if (delete)
895396Seric 	{
896396Seric 		unlink(fn);
897396Seric 		printf("%12s: removed\n", fn);
898585Seric 		return (TRUE);
899396Seric 	}
900396Seric 	else
901396Seric 	{
902416Seric 		printf("%12s: not being edited by you\n", fn);
903585Seric 		return (FALSE);
904396Seric 	}
905396Seric }
906396Seric /*
907396Seric **  GETPFILE -- get an entry from the p-file
908396Seric **
909396Seric **	Parameters:
910396Seric **		pfp -- p-file file pointer
911396Seric **
912396Seric **	Returns:
913396Seric **		pointer to p-file struct for next entry
914396Seric **		NULL on EOF or error
915396Seric **
916396Seric **	Side Effects:
917396Seric **		Each call wipes out results of previous call.
918396Seric */
919396Seric 
920396Seric struct pfile *
921396Seric getpfile(pfp)
922396Seric 	FILE *pfp;
923396Seric {
924396Seric 	static struct pfile ent;
925396Seric 	static char buf[120];
926396Seric 	register char *p;
927396Seric 	extern char *nextfield();
928396Seric 
929396Seric 	if (fgets(buf, sizeof buf, pfp) == NULL)
930396Seric 		return (NULL);
931396Seric 
932396Seric 	ent.p_osid = p = buf;
933396Seric 	ent.p_nsid = p = nextfield(p);
934396Seric 	ent.p_user = p = nextfield(p);
935396Seric 	ent.p_date = p = nextfield(p);
936396Seric 	ent.p_time = p = nextfield(p);
937396Seric 	if (p == NULL || nextfield(p) != NULL)
938396Seric 		return (NULL);
939396Seric 
940396Seric 	return (&ent);
941396Seric }
942396Seric 
943396Seric 
944396Seric char *
945396Seric nextfield(p)
946396Seric 	register char *p;
947396Seric {
948396Seric 	if (p == NULL || *p == '\0')
949396Seric 		return (NULL);
950396Seric 	while (*p != ' ' && *p != '\n' && *p != '\0')
951396Seric 		p++;
952396Seric 	if (*p == '\n' || *p == '\0')
953396Seric 	{
954396Seric 		*p = '\0';
955396Seric 		return (NULL);
956396Seric 	}
957396Seric 	*p++ = '\0';
958396Seric 	return (p);
959396Seric }
9601205Seric /*
9611205Seric **  USRERR -- issue user-level error
9621205Seric **
9631205Seric **	Parameters:
9641205Seric **		f -- format string.
9651205Seric **		p1-p3 -- parameters to a printf.
9661205Seric **
9671205Seric **	Returns:
9681205Seric **		-1
9691205Seric **
9701205Seric **	Side Effects:
9711205Seric **		none.
9721205Seric */
9731205Seric 
9741205Seric usrerr(f, p1, p2, p3)
9751205Seric 	char *f;
9761205Seric {
9771205Seric 	fprintf(stderr, "\n%s: ", MyName);
9781205Seric 	fprintf(stderr, f, p1, p2, p3);
9791205Seric 	fprintf(stderr, "\n");
9801205Seric 
9811205Seric 	return (-1);
9821205Seric }
9831205Seric /*
9841205Seric **  SYSERR -- print system-generated error.
9851205Seric **
9861205Seric **	Parameters:
9871205Seric **		f -- format string to a printf.
9881205Seric **		p1, p2, p3 -- parameters to f.
9891205Seric **
9901205Seric **	Returns:
9911205Seric **		never.
9921205Seric **
9931205Seric **	Side Effects:
9941205Seric **		none.
9951205Seric */
9961205Seric 
9971205Seric syserr(f, p1, p2, p3)
9981205Seric 	char *f;
9991205Seric {
10001205Seric 	extern int errno;
10011205Seric 
10021205Seric 	fprintf(stderr, "\n%s SYSERR: ", MyName);
10031205Seric 	fprintf(stderr, f, p1, p2, p3);
10041205Seric 	fprintf(stderr, "\n");
10051205Seric 	if (errno == 0)
10061205Seric 		exit(EX_SOFTWARE);
10071205Seric 	else
10081205Seric 	{
10091205Seric 		perror(0);
10101205Seric 		exit(EX_OSERR);
10111205Seric 	}
10121205Seric }
1013