xref: /original-bsd/usr.bin/sccs/sccs.c (revision 7d70f5d8)
1adf8f7d4Seric # include <stdio.h>
2adf8f7d4Seric # include <sys/types.h>
3adf8f7d4Seric # include <sys/stat.h>
4b2538d76Seric # include <sys/dir.h>
5adf8f7d4Seric # include <sysexits.h>
6b5d4f080Seric # include <whoami.h>
7adf8f7d4Seric 
8*7d70f5d8Seric static char SccsId[] = "@(#)sccs.c	1.23.1.1 08/09/80";
9c4432be4Seric 
10c4432be4Seric # define bitset(bit, word)	((bit) & (word))
11c4432be4Seric 
12c4432be4Seric typedef char	bool;
137de81dc7Seric # define TRUE	1
147de81dc7Seric # define FALSE	0
15d02a4f42Seric 
16adf8f7d4Seric struct sccsprog
17adf8f7d4Seric {
18adf8f7d4Seric 	char	*sccsname;	/* name of SCCS routine */
197de81dc7Seric 	short	sccsoper;	/* opcode, see below */
207de81dc7Seric 	short	sccsflags;	/* flags, see below */
21adf8f7d4Seric 	char	*sccspath;	/* pathname of binary implementing */
22adf8f7d4Seric };
23adf8f7d4Seric 
247de81dc7Seric /* values for sccsoper */
257de81dc7Seric # define PROG		0	/* call a program */
261777fbcbSeric # define CMACRO		1	/* command substitution macro */
27172147faSeric # define FIX		2	/* fix a delta */
28b2538d76Seric # define CLEAN		3	/* clean out recreatable files */
29e39a5722Seric # define UNEDIT		4	/* unedit a file */
307de81dc7Seric 
31c4432be4Seric /* bits for sccsflags */
327de81dc7Seric # define NO_SDOT	0001	/* no s. on front of args */
337de81dc7Seric # define REALUSER	0002	/* protected (e.g., admin) */
34adf8f7d4Seric 
35b5d4f080Seric # ifdef CSVAX
36b5d4f080Seric # define PROGPATH(name)	"/usr/local/name"
37b5d4f080Seric # endif CSVAX
38b5d4f080Seric 
39b5d4f080Seric # ifndef PROGPATH
40b5d4f080Seric # define PROGPATH(name)	"/usr/sccs/name"
41b5d4f080Seric # endif PROGPATH
42b5d4f080Seric 
43adf8f7d4Seric struct sccsprog SccsProg[] =
44adf8f7d4Seric {
45b5d4f080Seric 	"admin",	PROG,	REALUSER,		PROGPATH(admin),
46b5d4f080Seric 	"chghist",	PROG,	0,			PROGPATH(rmdel),
47b5d4f080Seric 	"comb",		PROG,	0,			PROGPATH(comb),
48b5d4f080Seric 	"delta",	PROG,	0,			PROGPATH(delta),
49b5d4f080Seric 	"get",		PROG,	0,			PROGPATH(get),
50b5d4f080Seric 	"help",		PROG,	NO_SDOT,		PROGPATH(help),
51b5d4f080Seric 	"prt",		PROG,	0,			PROGPATH(prt),
52b5d4f080Seric 	"rmdel",	PROG,	REALUSER,		PROGPATH(rmdel),
53b5d4f080Seric 	"what",		PROG,	NO_SDOT,		PROGPATH(what),
54f0cc3246Seric 	"edit",		CMACRO,	0,			"get -e",
55f0cc3246Seric 	"delget",	CMACRO,	0,			"delta/get",
56d7e8b658Seric 	"deledit",	CMACRO,	0,			"delta/get -e",
571777fbcbSeric 	"del",		CMACRO,	0,			"delta/get",
58172147faSeric 	"delt",		CMACRO,	0,			"delta/get",
59172147faSeric 	"fix",		FIX,	0,			NULL,
603a208c77Seric 	"clean",	CLEAN,	REALUSER,		(char *) TRUE,
613a208c77Seric 	"info",		CLEAN,	REALUSER,		(char *) FALSE,
62e39a5722Seric 	"unedit",	UNEDIT,	0,			NULL,
637de81dc7Seric 	NULL,		-1,	0,			NULL
64adf8f7d4Seric };
65adf8f7d4Seric 
66e39a5722Seric struct pfile
67e39a5722Seric {
68e39a5722Seric 	char	*p_osid;	/* old SID */
69e39a5722Seric 	char	*p_nsid;	/* new SID */
70e39a5722Seric 	char	*p_user;	/* user who did edit */
71e39a5722Seric 	char	*p_date;	/* date of get */
72e39a5722Seric 	char	*p_time;	/* time of get */
73e39a5722Seric };
74e39a5722Seric 
75c4432be4Seric char	*SccsPath = "SCCS";	/* pathname of SCCS files */
76c4432be4Seric bool	RealUser;		/* if set, running as real user */
77f0cc3246Seric # ifdef DEBUG
78f0cc3246Seric bool	Debug;			/* turn on tracing */
79f0cc3246Seric # endif
80adf8f7d4Seric 
81adf8f7d4Seric main(argc, argv)
82adf8f7d4Seric 	int argc;
83adf8f7d4Seric 	char **argv;
84adf8f7d4Seric {
85adf8f7d4Seric 	register char *p;
86b1ed8a43Seric 	extern struct sccsprog *lookup();
87adf8f7d4Seric 
88adf8f7d4Seric 	/*
89adf8f7d4Seric 	**  Detect and decode flags intended for this program.
90adf8f7d4Seric 	*/
91adf8f7d4Seric 
927de81dc7Seric 	if (argc < 2)
93adf8f7d4Seric 	{
947de81dc7Seric 		fprintf(stderr, "Usage: sccs [flags] command [flags]\n");
957de81dc7Seric 		exit(EX_USAGE);
967de81dc7Seric 	}
977de81dc7Seric 	argv[argc] = NULL;
987de81dc7Seric 
99b1ed8a43Seric 	if (lookup(argv[0]) == NULL)
100b1ed8a43Seric 	{
1017de81dc7Seric 		while ((p = *++argv) != NULL)
1027de81dc7Seric 		{
103adf8f7d4Seric 			if (*p != '-')
104adf8f7d4Seric 				break;
105adf8f7d4Seric 			switch (*++p)
106adf8f7d4Seric 			{
107adf8f7d4Seric 			  case 'r':		/* run as real user */
108adf8f7d4Seric 				setuid(getuid());
109c4432be4Seric 				RealUser++;
110adf8f7d4Seric 				break;
111adf8f7d4Seric 
112adf8f7d4Seric 			  case 'p':		/* path of sccs files */
113adf8f7d4Seric 				SccsPath = ++p;
114adf8f7d4Seric 				break;
115adf8f7d4Seric 
116f0cc3246Seric # ifdef DEBUG
117f0cc3246Seric 			  case 'T':		/* trace */
118f0cc3246Seric 				Debug++;
119f0cc3246Seric 				break;
120f0cc3246Seric # endif
121f0cc3246Seric 
122adf8f7d4Seric 			  default:
123adf8f7d4Seric 				fprintf(stderr, "Sccs: unknown option -%s\n", p);
124adf8f7d4Seric 				break;
125adf8f7d4Seric 			}
126adf8f7d4Seric 		}
1275cabffd9Seric 		if (SccsPath[0] == '\0')
1285cabffd9Seric 			SccsPath = ".";
129b1ed8a43Seric 	}
130adf8f7d4Seric 
1311777fbcbSeric 	command(argv, FALSE);
1327de81dc7Seric 	exit(EX_OK);
1337de81dc7Seric }
1347de81dc7Seric 
1351777fbcbSeric command(argv, forkflag)
1367de81dc7Seric 	char **argv;
1371777fbcbSeric 	bool forkflag;
1387de81dc7Seric {
1397de81dc7Seric 	register struct sccsprog *cmd;
1407de81dc7Seric 	register char *p;
1411777fbcbSeric 	register char *q;
1421777fbcbSeric 	char buf[40];
143b1ed8a43Seric 	extern struct sccsprog *lookup();
144d51cd7e5Seric 	char *nav[200];
145f0cc3246Seric 	char **avp;
146d51cd7e5Seric 	register int i;
147d51cd7e5Seric 	extern bool unedit();
148f0cc3246Seric 
149f0cc3246Seric # ifdef DEBUG
150f0cc3246Seric 	if (Debug)
151f0cc3246Seric 	{
152f0cc3246Seric 		printf("command:\n");
153f0cc3246Seric 		for (avp = argv; *avp != NULL; avp++)
154f0cc3246Seric 			printf("    \"%s\"\n", *avp);
155f0cc3246Seric 	}
156f0cc3246Seric # endif
157c4432be4Seric 
158c4432be4Seric 	/*
159adf8f7d4Seric 	**  Look up command.
1607de81dc7Seric 	**	At this point, argv points to the command name.
161adf8f7d4Seric 	*/
162adf8f7d4Seric 
163e39a5722Seric 	cmd = lookup(argv[0]);
164b1ed8a43Seric 	if (cmd == NULL)
165adf8f7d4Seric 	{
166e39a5722Seric 		fprintf(stderr, "Sccs: Unknown command \"%s\"\n", argv[0]);
167adf8f7d4Seric 		exit(EX_USAGE);
168adf8f7d4Seric 	}
169adf8f7d4Seric 
170adf8f7d4Seric 	/*
1717de81dc7Seric 	**  Interpret operation associated with this command.
172c4432be4Seric 	*/
173c4432be4Seric 
1747de81dc7Seric 	switch (cmd->sccsoper)
1757de81dc7Seric 	{
1767de81dc7Seric 	  case PROG:		/* call an sccs prog */
1771777fbcbSeric 		callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag);
1781777fbcbSeric 		break;
1791777fbcbSeric 
1801777fbcbSeric 	  case CMACRO:		/* command macro */
1811777fbcbSeric 		for (p = cmd->sccspath; *p != '\0'; p++)
1821777fbcbSeric 		{
183f0cc3246Seric 			avp = nav;
184f0cc3246Seric 			*avp++ = buf;
1851777fbcbSeric 			for (q = buf; *p != '/' && *p != '\0'; p++, q++)
186f0cc3246Seric 			{
187f0cc3246Seric 				if (*p == ' ')
188f0cc3246Seric 				{
1891777fbcbSeric 					*q = '\0';
190f0cc3246Seric 					*avp++ = &q[1];
191f0cc3246Seric 				}
192f0cc3246Seric 				else
193f0cc3246Seric 					*q = *p;
194f0cc3246Seric 			}
195f0cc3246Seric 			*q = '\0';
196f0cc3246Seric 			*avp = NULL;
197f0cc3246Seric 			xcommand(&argv[1], *p != '\0', nav[0], nav[1], nav[2],
198f0cc3246Seric 				 nav[3], nav[4], nav[5], nav[6]);
1991777fbcbSeric 		}
2001777fbcbSeric 		fprintf(stderr, "Sccs internal error: CMACRO\n");
2017de81dc7Seric 		exit(EX_SOFTWARE);
2027de81dc7Seric 
203172147faSeric 	  case FIX:		/* fix a delta */
204c9c62fd0Seric 		if (strncmp(argv[1], "-r", 2) != 0)
205172147faSeric 		{
206172147faSeric 			fprintf(stderr, "Sccs: -r flag needed for fix command\n");
207172147faSeric 			break;
208172147faSeric 		}
209172147faSeric 		xcommand(&argv[1], TRUE, "get", "-k", NULL);
210172147faSeric 		xcommand(&argv[1], TRUE, "rmdel", NULL);
211172147faSeric 		xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL);
212172147faSeric 		fprintf(stderr, "Sccs internal error: FIX\n");
213172147faSeric 		exit(EX_SOFTWARE);
214172147faSeric 
215b2538d76Seric 	  case CLEAN:
2163a208c77Seric 		clean((bool) cmd->sccspath);
217b2538d76Seric 		break;
218b2538d76Seric 
219e39a5722Seric 	  case UNEDIT:
220d51cd7e5Seric 		i = 0;
221e39a5722Seric 		for (avp = &argv[1]; *avp != NULL; avp++)
222d51cd7e5Seric 		{
223d51cd7e5Seric 			if (unedit(*avp))
224d51cd7e5Seric 				nav[i++] = *avp;
225d51cd7e5Seric 		}
226d51cd7e5Seric 		nav[i] = NULL;
227d51cd7e5Seric 		if (i > 0)
228d51cd7e5Seric 			xcommand(nav, FALSE, "get", NULL);
229e39a5722Seric 		break;
230e39a5722Seric 
2317de81dc7Seric 	  default:
2327de81dc7Seric 		fprintf(stderr, "Sccs internal error: oper %d\n", cmd->sccsoper);
2337de81dc7Seric 		exit(EX_SOFTWARE);
2347de81dc7Seric 	}
2357de81dc7Seric }
236b1ed8a43Seric /*
237b1ed8a43Seric **  LOOKUP -- look up an SCCS command name.
238b1ed8a43Seric **
239b1ed8a43Seric **	Parameters:
240b1ed8a43Seric **		name -- the name of the command to look up.
241b1ed8a43Seric **
242b1ed8a43Seric **	Returns:
243b1ed8a43Seric **		ptr to command descriptor for this command.
244b1ed8a43Seric **		NULL if no such entry.
245b1ed8a43Seric **
246b1ed8a43Seric **	Side Effects:
247b1ed8a43Seric **		none.
248b1ed8a43Seric */
249b1ed8a43Seric 
250b1ed8a43Seric struct sccsprog *
251b1ed8a43Seric lookup(name)
252b1ed8a43Seric 	char *name;
253b1ed8a43Seric {
254b1ed8a43Seric 	register struct sccsprog *cmd;
255b1ed8a43Seric 
256b1ed8a43Seric 	for (cmd = SccsProg; cmd->sccsname != NULL; cmd++)
257b1ed8a43Seric 	{
258b1ed8a43Seric 		if (strcmp(cmd->sccsname, name) == 0)
259b1ed8a43Seric 			return (cmd);
260b1ed8a43Seric 	}
261b1ed8a43Seric 	return (NULL);
262b1ed8a43Seric }
2637de81dc7Seric 
264172147faSeric 
265172147faSeric xcommand(argv, forkflag, arg0)
266172147faSeric 	char **argv;
267172147faSeric 	bool forkflag;
268172147faSeric 	char *arg0;
269172147faSeric {
270172147faSeric 	register char **av;
271172147faSeric 	char *newargv[1000];
272172147faSeric 	register char **np;
273172147faSeric 
274172147faSeric 	np = newargv;
275172147faSeric 	for (av = &arg0; *av != NULL; av++)
276172147faSeric 		*np++ = *av;
277172147faSeric 	for (av = argv; *av != NULL; av++)
278172147faSeric 		*np++ = *av;
279172147faSeric 	*np = NULL;
280172147faSeric 	command(newargv, forkflag);
281172147faSeric }
282172147faSeric 
2837de81dc7Seric callprog(progpath, flags, argv, forkflag)
2847de81dc7Seric 	char *progpath;
2857de81dc7Seric 	short flags;
2867de81dc7Seric 	char **argv;
2877de81dc7Seric 	bool forkflag;
2887de81dc7Seric {
2897de81dc7Seric 	register char *p;
2907de81dc7Seric 	register char **av;
2917de81dc7Seric 	extern char *makefile();
2927de81dc7Seric 	register int i;
2931777fbcbSeric 	auto int st;
294cdc1aa65Seric 	register char **nav;
2957de81dc7Seric 
2967de81dc7Seric 	if (*argv == NULL)
2977de81dc7Seric 		return (-1);
298c4432be4Seric 
299c4432be4Seric 	/*
300172147faSeric 	**  Fork if appropriate.
301adf8f7d4Seric 	*/
302adf8f7d4Seric 
3037de81dc7Seric 	if (forkflag)
3047de81dc7Seric 	{
305f0cc3246Seric # ifdef DEBUG
306f0cc3246Seric 		if (Debug)
307f0cc3246Seric 			printf("Forking\n");
308f0cc3246Seric # endif
3097de81dc7Seric 		i = fork();
3107de81dc7Seric 		if (i < 0)
3117de81dc7Seric 		{
3127de81dc7Seric 			fprintf(stderr, "Sccs: cannot fork");
3137de81dc7Seric 			exit(EX_OSERR);
3147de81dc7Seric 		}
3157de81dc7Seric 		else if (i > 0)
3161777fbcbSeric 		{
3171777fbcbSeric 			wait(&st);
3181777fbcbSeric 			return (st);
3191777fbcbSeric 		}
3207de81dc7Seric 	}
3217de81dc7Seric 
3227de81dc7Seric 	/*
323172147faSeric 	**  Build new argument vector.
324172147faSeric 	*/
325172147faSeric 
326172147faSeric 	/* copy program filename arguments and flags */
327cdc1aa65Seric 	nav = &argv[1];
328172147faSeric 	av = argv;
329172147faSeric 	while ((p = *++av) != NULL)
330172147faSeric 	{
331172147faSeric 		if (!bitset(NO_SDOT, flags) && *p != '-')
332cdc1aa65Seric 			*nav = makefile(p);
333cdc1aa65Seric 		else
334cdc1aa65Seric 			*nav = p;
335cdc1aa65Seric 		if (*nav != NULL)
336cdc1aa65Seric 			nav++;
337172147faSeric 	}
338cdc1aa65Seric 	*nav = NULL;
339172147faSeric 
340172147faSeric 	/*
3417de81dc7Seric 	**  Set protection as appropriate.
3427de81dc7Seric 	*/
3437de81dc7Seric 
3447de81dc7Seric 	if (bitset(REALUSER, flags))
3457de81dc7Seric 		setuid(getuid());
3467de81dc7Seric 
3477de81dc7Seric 	/*
348172147faSeric 	**  Call real SCCS program.
3497de81dc7Seric 	*/
3507de81dc7Seric 
351172147faSeric 	execv(progpath, argv);
352adf8f7d4Seric 	fprintf(stderr, "Sccs: cannot execute ");
3537de81dc7Seric 	perror(progpath);
354adf8f7d4Seric 	exit(EX_UNAVAILABLE);
355adf8f7d4Seric }
356cdc1aa65Seric /*
357cdc1aa65Seric **  MAKEFILE -- make filename of SCCS file
358cdc1aa65Seric **
359cdc1aa65Seric **	If the name passed is already the name of an SCCS file,
360cdc1aa65Seric **	just return it.  Otherwise, munge the name into the name
361cdc1aa65Seric **	of the actual SCCS file.
362cdc1aa65Seric **
363cdc1aa65Seric **	There are cases when it is not clear what you want to
364cdc1aa65Seric **	do.  For example, if SccsPath is an absolute pathname
365cdc1aa65Seric **	and the name given is also an absolute pathname, we go
366cdc1aa65Seric **	for SccsPath (& only use the last component of the name
367cdc1aa65Seric **	passed) -- this is important for security reasons (if
368cdc1aa65Seric **	sccs is being used as a setuid front end), but not
369cdc1aa65Seric **	particularly intuitive.
370cdc1aa65Seric **
371cdc1aa65Seric **	Parameters:
372cdc1aa65Seric **		name -- the file name to be munged.
373cdc1aa65Seric **
374cdc1aa65Seric **	Returns:
375cdc1aa65Seric **		The pathname of the sccs file.
376cdc1aa65Seric **		NULL on error.
377cdc1aa65Seric **
378cdc1aa65Seric **	Side Effects:
379cdc1aa65Seric **		none.
380cdc1aa65Seric */
381adf8f7d4Seric 
382adf8f7d4Seric char *
383adf8f7d4Seric makefile(name)
384adf8f7d4Seric 	char *name;
385adf8f7d4Seric {
386adf8f7d4Seric 	register char *p;
387adf8f7d4Seric 	register char c;
388adf8f7d4Seric 	char buf[512];
389adf8f7d4Seric 	extern char *malloc();
390cdc1aa65Seric 	extern char *rindex();
391*7d70f5d8Seric 	extern bool isdir();
392*7d70f5d8Seric 	register char *q;
393cdc1aa65Seric 
394cdc1aa65Seric 	p = rindex(name, '/');
395cdc1aa65Seric 	if (p == NULL)
396cdc1aa65Seric 		p = name;
397cdc1aa65Seric 	else
398cdc1aa65Seric 		p++;
399adf8f7d4Seric 
400adf8f7d4Seric 	/*
401*7d70f5d8Seric 	**  See if the name can be used as-is.
402adf8f7d4Seric 	*/
403adf8f7d4Seric 
404*7d70f5d8Seric 	if (SccsPath[0] != '/' || name[0] == '/' || strncmp(name, "./", 2) == 0)
405*7d70f5d8Seric 	{
406*7d70f5d8Seric 		if (strncmp(p, "s.", 2) == 0)
407*7d70f5d8Seric 			return (name);
408*7d70f5d8Seric 		if (isdir(name))
409*7d70f5d8Seric 			return (name);
410*7d70f5d8Seric 	}
411cdc1aa65Seric 
412cdc1aa65Seric 	/*
413*7d70f5d8Seric 	**  Create the actual pathname.
414cdc1aa65Seric 	*/
415cdc1aa65Seric 
416*7d70f5d8Seric 	if (name[0] != '/')
417adf8f7d4Seric 	{
418cdc1aa65Seric 		strcpy(buf, SccsPath);
419cdc1aa65Seric 		strcat(buf, "/");
420cdc1aa65Seric 	}
421cdc1aa65Seric 	else
422cdc1aa65Seric 		strcpy(buf, "");
423cdc1aa65Seric 	strncat(buf, name, p - name);
424*7d70f5d8Seric 	q = &buf[strlen(buf)];
425*7d70f5d8Seric 	strcpy(q, p);
426*7d70f5d8Seric 	if (strncmp(p, "s.", 2) != 0 && !isdir(buf))
427*7d70f5d8Seric 	{
428*7d70f5d8Seric 		strcpy(q, "");
429*7d70f5d8Seric 		if (SccsPath[0] != '/' && name[0] == '/')
430cdc1aa65Seric 		{
431cdc1aa65Seric 			strcat(buf, SccsPath);
432cdc1aa65Seric 			strcat(buf, "/");
433cdc1aa65Seric 		}
434cdc1aa65Seric 		strcat(buf, "s.");
435cdc1aa65Seric 		strcat(buf, p);
436cdc1aa65Seric 	}
437adf8f7d4Seric 
438adf8f7d4Seric 	p = malloc(strlen(buf) + 1);
439adf8f7d4Seric 	if (p == NULL)
440adf8f7d4Seric 	{
441adf8f7d4Seric 		perror("Sccs: no mem");
442adf8f7d4Seric 		exit(EX_OSERR);
443adf8f7d4Seric 	}
444adf8f7d4Seric 	strcpy(p, buf);
445adf8f7d4Seric 	return (p);
446adf8f7d4Seric }
447b2538d76Seric /*
448*7d70f5d8Seric **  ISDIR -- return true if the argument is a directory.
449*7d70f5d8Seric **
450*7d70f5d8Seric **	Parameters:
451*7d70f5d8Seric **		name -- the pathname of the file to check.
452*7d70f5d8Seric **
453*7d70f5d8Seric **	Returns:
454*7d70f5d8Seric **		TRUE if 'name' is a directory, FALSE otherwise.
455*7d70f5d8Seric **
456*7d70f5d8Seric **	Side Effects:
457*7d70f5d8Seric **		none.
458*7d70f5d8Seric */
459*7d70f5d8Seric 
460*7d70f5d8Seric bool
461*7d70f5d8Seric isdir(name)
462*7d70f5d8Seric 	char *name;
463*7d70f5d8Seric {
464*7d70f5d8Seric 	struct stat stbuf;
465*7d70f5d8Seric 
466*7d70f5d8Seric 	return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR);
467*7d70f5d8Seric }
468*7d70f5d8Seric /*
469cdc1aa65Seric **  SAFEPATH -- determine whether a pathname is "safe"
470cdc1aa65Seric **
471cdc1aa65Seric **	"Safe" pathnames only allow you to get deeper into the
472cdc1aa65Seric **	directory structure, i.e., full pathnames and ".." are
473cdc1aa65Seric **	not allowed.
474cdc1aa65Seric **
475cdc1aa65Seric **	Parameters:
476cdc1aa65Seric **		p -- the name to check.
477cdc1aa65Seric **
478cdc1aa65Seric **	Returns:
479cdc1aa65Seric **		TRUE -- if the path is safe.
480cdc1aa65Seric **		FALSE -- if the path is not safe.
481cdc1aa65Seric **
482cdc1aa65Seric **	Side Effects:
483cdc1aa65Seric **		Prints a message if the path is not safe.
484cdc1aa65Seric */
485cdc1aa65Seric 
486cdc1aa65Seric bool
487cdc1aa65Seric safepath(p)
488cdc1aa65Seric 	register char *p;
489cdc1aa65Seric {
490cdc1aa65Seric 	extern char *index();
491cdc1aa65Seric 
492cdc1aa65Seric 	if (*p != '/')
493cdc1aa65Seric 	{
494cdc1aa65Seric 		while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0)
495cdc1aa65Seric 		{
496cdc1aa65Seric 			p = index(p, '/');
497cdc1aa65Seric 			if (p == NULL)
498cdc1aa65Seric 				return (TRUE);
499cdc1aa65Seric 			p++;
500cdc1aa65Seric 		}
501cdc1aa65Seric 	}
502cdc1aa65Seric 
503cdc1aa65Seric 	printf("You may not use full pathnames or \"..\"\n");
504cdc1aa65Seric 	return (FALSE);
505cdc1aa65Seric }
506cdc1aa65Seric /*
507b2538d76Seric **  CLEAN -- clean out recreatable files
508b2538d76Seric **
509b2538d76Seric **	Any file for which an "s." file exists but no "p." file
510b2538d76Seric **	exists in the current directory is purged.
511b2538d76Seric **
512b2538d76Seric **	Parameters:
5133a208c77Seric **		really -- if TRUE, remove everything.
5143a208c77Seric **			else, just report status.
515b2538d76Seric **
516b2538d76Seric **	Returns:
517b2538d76Seric **		none.
518b2538d76Seric **
519b2538d76Seric **	Side Effects:
520b2538d76Seric **		removes files in the current directory.
521b2538d76Seric */
522b2538d76Seric 
5233a208c77Seric clean(really)
5243a208c77Seric 	bool really;
525b2538d76Seric {
526b2538d76Seric 	struct direct dir;
527b2538d76Seric 	struct stat stbuf;
528b2538d76Seric 	char buf[100];
529bf9c1e66Seric 	char pline[120];
5303a208c77Seric 	register FILE *dirfd;
5313a208c77Seric 	register char *basefile;
5322caac8fdSeric 	bool gotedit;
533bf9c1e66Seric 	FILE *pfp;
534b2538d76Seric 
535b2538d76Seric 	dirfd = fopen(SccsPath, "r");
536b2538d76Seric 	if (dirfd == NULL)
537b2538d76Seric 	{
538b2538d76Seric 		fprintf(stderr, "Sccs: cannot open %s\n", SccsPath);
539b2538d76Seric 		return;
540b2538d76Seric 	}
541b2538d76Seric 
542b2538d76Seric 	/*
543b2538d76Seric 	**  Scan the SCCS directory looking for s. files.
544b2538d76Seric 	*/
545b2538d76Seric 
5462caac8fdSeric 	gotedit = FALSE;
547b2538d76Seric 	while (fread(&dir, sizeof dir, 1, dirfd) != NULL)
548b2538d76Seric 	{
549c9c62fd0Seric 		if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0)
550b2538d76Seric 			continue;
551b2538d76Seric 
552b2538d76Seric 		/* got an s. file -- see if the p. file exists */
553b2538d76Seric 		strcpy(buf, SccsPath);
554b2538d76Seric 		strcat(buf, "/p.");
5553a208c77Seric 		basefile = &buf[strlen(buf)];
556c9c62fd0Seric 		strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2);
557bf9c1e66Seric 		basefile[sizeof dir.d_name - 2] = '\0';
558bf9c1e66Seric 		pfp = fopen(buf, "r");
559bf9c1e66Seric 		if (pfp != NULL)
5603a208c77Seric 		{
561bf9c1e66Seric 			while (fgets(pline, sizeof pline, pfp) != NULL)
5622444cd3eSeric 				printf("%12s: being edited: %s", basefile, pline);
563bf9c1e66Seric 			fclose(pfp);
5642caac8fdSeric 			gotedit = TRUE;
565b2538d76Seric 			continue;
5663a208c77Seric 		}
567b2538d76Seric 
568b2538d76Seric 		/* the s. file exists and no p. file exists -- unlink the g-file */
5693a208c77Seric 		if (really)
5703a208c77Seric 		{
571c9c62fd0Seric 			strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2);
5723a208c77Seric 			buf[sizeof dir.d_name - 2] = '\0';
573b2538d76Seric 			unlink(buf);
574b2538d76Seric 		}
5753a208c77Seric 	}
576b2538d76Seric 
577b2538d76Seric 	fclose(dirfd);
5782caac8fdSeric 	if (!gotedit && !really)
5792444cd3eSeric 		printf("Nothing being edited\n");
580b2538d76Seric }
581e39a5722Seric /*
582e39a5722Seric **  UNEDIT -- unedit a file
583e39a5722Seric **
584e39a5722Seric **	Checks to see that the current user is actually editting
585e39a5722Seric **	the file and arranges that s/he is not editting it.
586e39a5722Seric **
587e39a5722Seric **	Parameters:
5882444cd3eSeric **		fn -- the name of the file to be unedited.
589e39a5722Seric **
590e39a5722Seric **	Returns:
591d51cd7e5Seric **		TRUE -- if the file was successfully unedited.
592d51cd7e5Seric **		FALSE -- if the file was not unedited for some
593d51cd7e5Seric **			reason.
594e39a5722Seric **
595e39a5722Seric **	Side Effects:
596e39a5722Seric **		fn is removed
597e39a5722Seric **		entries are removed from pfile.
598e39a5722Seric */
599e39a5722Seric 
600d51cd7e5Seric bool
601e39a5722Seric unedit(fn)
602e39a5722Seric 	char *fn;
603e39a5722Seric {
604e39a5722Seric 	register FILE *pfp;
605e39a5722Seric 	char *pfn;
606e39a5722Seric 	static char tfn[] = "/tmp/sccsXXXXX";
607e39a5722Seric 	FILE *tfp;
608e39a5722Seric 	register char *p;
609e39a5722Seric 	register char *q;
610e39a5722Seric 	bool delete = FALSE;
611e39a5722Seric 	bool others = FALSE;
612e39a5722Seric 	char *myname;
613e39a5722Seric 	extern char *getlogin();
614e39a5722Seric 	struct pfile *pent;
615e39a5722Seric 	extern struct pfile *getpfile();
616e39a5722Seric 	char buf[120];
617e39a5722Seric 
618e39a5722Seric 	/* make "s." filename & find the trailing component */
619e39a5722Seric 	pfn = makefile(fn);
620cdc1aa65Seric 	if (pfn == NULL)
621cdc1aa65Seric 		return (FALSE);
622cdc1aa65Seric 	q = rindex(pfn, '/');
623cdc1aa65Seric 	if (q == NULL)
624cdc1aa65Seric 		q = &pfn[-1];
625cdc1aa65Seric 	if (q[1] != 's' || q[2] != '.')
626e39a5722Seric 	{
627e39a5722Seric 		fprintf(stderr, "Sccs: bad file name \"%s\"\n", fn);
628d51cd7e5Seric 		return (FALSE);
629e39a5722Seric 	}
630e39a5722Seric 
631e39a5722Seric 	/* turn "s." into "p." */
632e39a5722Seric 	*++q = 'p';
633e39a5722Seric 
634e39a5722Seric 	pfp = fopen(pfn, "r");
635e39a5722Seric 	if (pfp == NULL)
636e39a5722Seric 	{
6372444cd3eSeric 		printf("%12s: not being edited\n", fn);
638d51cd7e5Seric 		return (FALSE);
639e39a5722Seric 	}
640e39a5722Seric 
641e39a5722Seric 	/*
642e39a5722Seric 	**  Copy p-file to temp file, doing deletions as needed.
643e39a5722Seric 	*/
644e39a5722Seric 
645e39a5722Seric 	mktemp(tfn);
646e39a5722Seric 	tfp = fopen(tfn, "w");
647e39a5722Seric 	if (tfp == NULL)
648e39a5722Seric 	{
649e39a5722Seric 		fprintf(stderr, "Sccs: cannot create \"%s\"\n", tfn);
650e39a5722Seric 		exit(EX_OSERR);
651e39a5722Seric 	}
652e39a5722Seric 
653e39a5722Seric 	myname = getlogin();
654e39a5722Seric 	while ((pent = getpfile(pfp)) != NULL)
655e39a5722Seric 	{
656e39a5722Seric 		if (strcmp(pent->p_user, myname) == 0)
657e39a5722Seric 		{
658e39a5722Seric 			/* a match */
659e39a5722Seric 			delete++;
660e39a5722Seric 		}
661e39a5722Seric 		else
662e39a5722Seric 		{
663e39a5722Seric 			fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid,
664e39a5722Seric 			    pent->p_nsid, pent->p_user, pent->p_date,
665e39a5722Seric 			    pent->p_time);
666e39a5722Seric 			others++;
667e39a5722Seric 		}
668e39a5722Seric 	}
669e39a5722Seric 
670e39a5722Seric 	/* do final cleanup */
671e39a5722Seric 	if (others)
672e39a5722Seric 	{
673e39a5722Seric 		if (freopen(tfn, "r", tfp) == NULL)
674e39a5722Seric 		{
675e39a5722Seric 			fprintf(stderr, "Sccs: cannot reopen \"%s\"\n", tfn);
676e39a5722Seric 			exit(EX_OSERR);
677e39a5722Seric 		}
678e39a5722Seric 		if (freopen(pfn, "w", pfp) == NULL)
679e39a5722Seric 		{
680e39a5722Seric 			fprintf(stderr, "Sccs: cannot create \"%s\"\n", pfn);
681d51cd7e5Seric 			return (FALSE);
682e39a5722Seric 		}
683e39a5722Seric 		while (fgets(buf, sizeof buf, tfp) != NULL)
684e39a5722Seric 			fputs(buf, pfp);
685e39a5722Seric 	}
686e39a5722Seric 	else
687e39a5722Seric 	{
688e39a5722Seric 		unlink(pfn);
689e39a5722Seric 	}
690e39a5722Seric 	fclose(tfp);
691e39a5722Seric 	fclose(pfp);
692e39a5722Seric 	unlink(tfn);
693e39a5722Seric 
694e39a5722Seric 	if (delete)
695e39a5722Seric 	{
696e39a5722Seric 		unlink(fn);
697e39a5722Seric 		printf("%12s: removed\n", fn);
698d51cd7e5Seric 		return (TRUE);
699e39a5722Seric 	}
700e39a5722Seric 	else
701e39a5722Seric 	{
7022444cd3eSeric 		printf("%12s: not being edited by you\n", fn);
703d51cd7e5Seric 		return (FALSE);
704e39a5722Seric 	}
705e39a5722Seric }
706e39a5722Seric /*
707e39a5722Seric **  GETPFILE -- get an entry from the p-file
708e39a5722Seric **
709e39a5722Seric **	Parameters:
710e39a5722Seric **		pfp -- p-file file pointer
711e39a5722Seric **
712e39a5722Seric **	Returns:
713e39a5722Seric **		pointer to p-file struct for next entry
714e39a5722Seric **		NULL on EOF or error
715e39a5722Seric **
716e39a5722Seric **	Side Effects:
717e39a5722Seric **		Each call wipes out results of previous call.
718e39a5722Seric */
719e39a5722Seric 
720e39a5722Seric struct pfile *
721e39a5722Seric getpfile(pfp)
722e39a5722Seric 	FILE *pfp;
723e39a5722Seric {
724e39a5722Seric 	static struct pfile ent;
725e39a5722Seric 	static char buf[120];
726e39a5722Seric 	register char *p;
727e39a5722Seric 	extern char *nextfield();
728e39a5722Seric 
729e39a5722Seric 	if (fgets(buf, sizeof buf, pfp) == NULL)
730e39a5722Seric 		return (NULL);
731e39a5722Seric 
732e39a5722Seric 	ent.p_osid = p = buf;
733e39a5722Seric 	ent.p_nsid = p = nextfield(p);
734e39a5722Seric 	ent.p_user = p = nextfield(p);
735e39a5722Seric 	ent.p_date = p = nextfield(p);
736e39a5722Seric 	ent.p_time = p = nextfield(p);
737e39a5722Seric 	if (p == NULL || nextfield(p) != NULL)
738e39a5722Seric 		return (NULL);
739e39a5722Seric 
740e39a5722Seric 	return (&ent);
741e39a5722Seric }
742e39a5722Seric 
743e39a5722Seric 
744e39a5722Seric char *
745e39a5722Seric nextfield(p)
746e39a5722Seric 	register char *p;
747e39a5722Seric {
748e39a5722Seric 	if (p == NULL || *p == '\0')
749e39a5722Seric 		return (NULL);
750e39a5722Seric 	while (*p != ' ' && *p != '\n' && *p != '\0')
751e39a5722Seric 		p++;
752e39a5722Seric 	if (*p == '\n' || *p == '\0')
753e39a5722Seric 	{
754e39a5722Seric 		*p = '\0';
755e39a5722Seric 		return (NULL);
756e39a5722Seric 	}
757e39a5722Seric 	*p++ = '\0';
758e39a5722Seric 	return (p);
759e39a5722Seric }
760