xref: /original-bsd/usr.bin/sccs/sccs.c (revision f0cc3246)
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*f0cc3246Seric static char SccsId[] = "@(#)sccs.c	1.15 07/24/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 */
297de81dc7Seric 
30c4432be4Seric /* bits for sccsflags */
317de81dc7Seric # define NO_SDOT	0001	/* no s. on front of args */
327de81dc7Seric # define REALUSER	0002	/* protected (e.g., admin) */
33adf8f7d4Seric 
34b5d4f080Seric # ifdef CSVAX
35b5d4f080Seric # define PROGPATH(name)	"/usr/local/name"
36b5d4f080Seric # endif CSVAX
37b5d4f080Seric 
38b5d4f080Seric # ifndef PROGPATH
39b5d4f080Seric # define PROGPATH(name)	"/usr/sccs/name"
40b5d4f080Seric # endif PROGPATH
41b5d4f080Seric 
42adf8f7d4Seric struct sccsprog SccsProg[] =
43adf8f7d4Seric {
44b5d4f080Seric 	"admin",	PROG,	REALUSER,		PROGPATH(admin),
45b5d4f080Seric 	"chghist",	PROG,	0,			PROGPATH(rmdel),
46b5d4f080Seric 	"comb",		PROG,	0,			PROGPATH(comb),
47b5d4f080Seric 	"delta",	PROG,	0,			PROGPATH(delta),
48b5d4f080Seric 	"get",		PROG,	0,			PROGPATH(get),
49b5d4f080Seric 	"help",		PROG,	NO_SDOT,		PROGPATH(help),
50b5d4f080Seric 	"prt",		PROG,	0,			PROGPATH(prt),
51b5d4f080Seric 	"rmdel",	PROG,	REALUSER,		PROGPATH(rmdel),
52b5d4f080Seric 	"what",		PROG,	NO_SDOT,		PROGPATH(what),
53*f0cc3246Seric 	"edit",		CMACRO,	0,			"get -e",
54*f0cc3246Seric 	"delget",	CMACRO,	0,			"delta/get",
55*f0cc3246Seric 	"deled",	CMACRO,	0,			"delta/get -e",
561777fbcbSeric 	"del",		CMACRO,	0,			"delta/get",
57172147faSeric 	"delt",		CMACRO,	0,			"delta/get",
58172147faSeric 	"fix",		FIX,	0,			NULL,
593a208c77Seric 	"clean",	CLEAN,	REALUSER,		(char *) TRUE,
603a208c77Seric 	"info",		CLEAN,	REALUSER,		(char *) FALSE,
617de81dc7Seric 	NULL,		-1,	0,			NULL
62adf8f7d4Seric };
63adf8f7d4Seric 
64c4432be4Seric char	*SccsPath = "SCCS";	/* pathname of SCCS files */
65c4432be4Seric bool	RealUser;		/* if set, running as real user */
66*f0cc3246Seric # ifdef DEBUG
67*f0cc3246Seric bool	Debug;			/* turn on tracing */
68*f0cc3246Seric # endif
69adf8f7d4Seric 
70adf8f7d4Seric main(argc, argv)
71adf8f7d4Seric 	int argc;
72adf8f7d4Seric 	char **argv;
73adf8f7d4Seric {
74adf8f7d4Seric 	register char *p;
75b1ed8a43Seric 	extern struct sccsprog *lookup();
76adf8f7d4Seric 
77adf8f7d4Seric 	/*
78adf8f7d4Seric 	**  Detect and decode flags intended for this program.
79adf8f7d4Seric 	*/
80adf8f7d4Seric 
817de81dc7Seric 	if (argc < 2)
82adf8f7d4Seric 	{
837de81dc7Seric 		fprintf(stderr, "Usage: sccs [flags] command [flags]\n");
847de81dc7Seric 		exit(EX_USAGE);
857de81dc7Seric 	}
867de81dc7Seric 	argv[argc] = NULL;
877de81dc7Seric 
88b1ed8a43Seric 	if (lookup(argv[0]) == NULL)
89b1ed8a43Seric 	{
907de81dc7Seric 		while ((p = *++argv) != NULL)
917de81dc7Seric 		{
92adf8f7d4Seric 			if (*p != '-')
93adf8f7d4Seric 				break;
94adf8f7d4Seric 			switch (*++p)
95adf8f7d4Seric 			{
96adf8f7d4Seric 			  case 'r':		/* run as real user */
97adf8f7d4Seric 				setuid(getuid());
98c4432be4Seric 				RealUser++;
99adf8f7d4Seric 				break;
100adf8f7d4Seric 
101adf8f7d4Seric 			  case 'p':		/* path of sccs files */
102adf8f7d4Seric 				SccsPath = ++p;
103adf8f7d4Seric 				break;
104adf8f7d4Seric 
105*f0cc3246Seric # ifdef DEBUG
106*f0cc3246Seric 			  case 'T':		/* trace */
107*f0cc3246Seric 				Debug++;
108*f0cc3246Seric 				break;
109*f0cc3246Seric # endif
110*f0cc3246Seric 
111adf8f7d4Seric 			  default:
112adf8f7d4Seric 				fprintf(stderr, "Sccs: unknown option -%s\n", p);
113adf8f7d4Seric 				break;
114adf8f7d4Seric 			}
115adf8f7d4Seric 		}
1165cabffd9Seric 		if (SccsPath[0] == '\0')
1175cabffd9Seric 			SccsPath = ".";
118b1ed8a43Seric 	}
119adf8f7d4Seric 
1201777fbcbSeric 	command(argv, FALSE);
1217de81dc7Seric 	exit(EX_OK);
1227de81dc7Seric }
1237de81dc7Seric 
1241777fbcbSeric command(argv, forkflag)
1257de81dc7Seric 	char **argv;
1261777fbcbSeric 	bool forkflag;
1277de81dc7Seric {
1287de81dc7Seric 	register struct sccsprog *cmd;
1297de81dc7Seric 	register char *p;
1301777fbcbSeric 	register char *q;
1311777fbcbSeric 	char buf[40];
132b1ed8a43Seric 	extern struct sccsprog *lookup();
133*f0cc3246Seric 	char *nav[7];
134*f0cc3246Seric 	char **avp;
135*f0cc3246Seric 
136*f0cc3246Seric # ifdef DEBUG
137*f0cc3246Seric 	if (Debug)
138*f0cc3246Seric 	{
139*f0cc3246Seric 		printf("command:\n");
140*f0cc3246Seric 		for (avp = argv; *avp != NULL; avp++)
141*f0cc3246Seric 			printf("    \"%s\"\n", *avp);
142*f0cc3246Seric 	}
143*f0cc3246Seric # endif
144c4432be4Seric 
145c4432be4Seric 	/*
146adf8f7d4Seric 	**  Look up command.
1477de81dc7Seric 	**	At this point, argv points to the command name.
148adf8f7d4Seric 	*/
149adf8f7d4Seric 
150*f0cc3246Seric 	cmd = lookup(*argv);
151b1ed8a43Seric 	if (cmd == NULL)
152adf8f7d4Seric 	{
153*f0cc3246Seric 		fprintf(stderr, "Sccs: Unknown command \"%s\"\n", *argv);
154adf8f7d4Seric 		exit(EX_USAGE);
155adf8f7d4Seric 	}
156adf8f7d4Seric 
157adf8f7d4Seric 	/*
1587de81dc7Seric 	**  Interpret operation associated with this command.
159c4432be4Seric 	*/
160c4432be4Seric 
1617de81dc7Seric 	switch (cmd->sccsoper)
1627de81dc7Seric 	{
1637de81dc7Seric 	  case PROG:		/* call an sccs prog */
1641777fbcbSeric 		callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag);
1651777fbcbSeric 		break;
1661777fbcbSeric 
1671777fbcbSeric 	  case CMACRO:		/* command macro */
1681777fbcbSeric 		for (p = cmd->sccspath; *p != '\0'; p++)
1691777fbcbSeric 		{
170*f0cc3246Seric 			avp = nav;
171*f0cc3246Seric 			*avp++ = buf;
1721777fbcbSeric 			for (q = buf; *p != '/' && *p != '\0'; p++, q++)
173*f0cc3246Seric 			{
174*f0cc3246Seric 				if (*p == ' ')
175*f0cc3246Seric 				{
1761777fbcbSeric 					*q = '\0';
177*f0cc3246Seric 					*avp++ = &q[1];
178*f0cc3246Seric 				}
179*f0cc3246Seric 				else
180*f0cc3246Seric 					*q = *p;
181*f0cc3246Seric 			}
182*f0cc3246Seric 			*q = '\0';
183*f0cc3246Seric 			*avp = NULL;
184*f0cc3246Seric 			xcommand(&argv[1], *p != '\0', nav[0], nav[1], nav[2],
185*f0cc3246Seric 				 nav[3], nav[4], nav[5], nav[6]);
1861777fbcbSeric 		}
1871777fbcbSeric 		fprintf(stderr, "Sccs internal error: CMACRO\n");
1887de81dc7Seric 		exit(EX_SOFTWARE);
1897de81dc7Seric 
190172147faSeric 	  case FIX:		/* fix a delta */
191b2538d76Seric 		if (strcmpn(argv[1], "-r", 2) != 0)
192172147faSeric 		{
193172147faSeric 			fprintf(stderr, "Sccs: -r flag needed for fix command\n");
194172147faSeric 			break;
195172147faSeric 		}
196172147faSeric 		xcommand(&argv[1], TRUE, "get", "-k", NULL);
197172147faSeric 		xcommand(&argv[1], TRUE, "rmdel", NULL);
198172147faSeric 		xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL);
199172147faSeric 		fprintf(stderr, "Sccs internal error: FIX\n");
200172147faSeric 		exit(EX_SOFTWARE);
201172147faSeric 
202b2538d76Seric 	  case CLEAN:
2033a208c77Seric 		clean((bool) cmd->sccspath);
204b2538d76Seric 		break;
205b2538d76Seric 
2067de81dc7Seric 	  default:
2077de81dc7Seric 		fprintf(stderr, "Sccs internal error: oper %d\n", cmd->sccsoper);
2087de81dc7Seric 		exit(EX_SOFTWARE);
2097de81dc7Seric 	}
2107de81dc7Seric }
211b1ed8a43Seric /*
212b1ed8a43Seric **  LOOKUP -- look up an SCCS command name.
213b1ed8a43Seric **
214b1ed8a43Seric **	Parameters:
215b1ed8a43Seric **		name -- the name of the command to look up.
216b1ed8a43Seric **
217b1ed8a43Seric **	Returns:
218b1ed8a43Seric **		ptr to command descriptor for this command.
219b1ed8a43Seric **		NULL if no such entry.
220b1ed8a43Seric **
221b1ed8a43Seric **	Side Effects:
222b1ed8a43Seric **		none.
223b1ed8a43Seric */
224b1ed8a43Seric 
225b1ed8a43Seric struct sccsprog *
226b1ed8a43Seric lookup(name)
227b1ed8a43Seric 	char *name;
228b1ed8a43Seric {
229b1ed8a43Seric 	register struct sccsprog *cmd;
230b1ed8a43Seric 
231b1ed8a43Seric 	for (cmd = SccsProg; cmd->sccsname != NULL; cmd++)
232b1ed8a43Seric 	{
233b1ed8a43Seric 		if (strcmp(cmd->sccsname, name) == 0)
234b1ed8a43Seric 			return (cmd);
235b1ed8a43Seric 	}
236b1ed8a43Seric 	return (NULL);
237b1ed8a43Seric }
2387de81dc7Seric 
239172147faSeric 
240172147faSeric xcommand(argv, forkflag, arg0)
241172147faSeric 	char **argv;
242172147faSeric 	bool forkflag;
243172147faSeric 	char *arg0;
244172147faSeric {
245172147faSeric 	register char **av;
246172147faSeric 	char *newargv[1000];
247172147faSeric 	register char **np;
248172147faSeric 
249172147faSeric 	np = newargv;
250172147faSeric 	for (av = &arg0; *av != NULL; av++)
251172147faSeric 		*np++ = *av;
252172147faSeric 	for (av = argv; *av != NULL; av++)
253172147faSeric 		*np++ = *av;
254172147faSeric 	*np = NULL;
255172147faSeric 	command(newargv, forkflag);
256172147faSeric }
257172147faSeric 
2587de81dc7Seric callprog(progpath, flags, argv, forkflag)
2597de81dc7Seric 	char *progpath;
2607de81dc7Seric 	short flags;
2617de81dc7Seric 	char **argv;
2627de81dc7Seric 	bool forkflag;
2637de81dc7Seric {
2647de81dc7Seric 	register char *p;
2657de81dc7Seric 	register char **av;
2667de81dc7Seric 	extern char *makefile();
2677de81dc7Seric 	register int i;
2681777fbcbSeric 	auto int st;
2697de81dc7Seric 
2707de81dc7Seric 	if (*argv == NULL)
2717de81dc7Seric 		return (-1);
272c4432be4Seric 
273c4432be4Seric 	/*
274172147faSeric 	**  Fork if appropriate.
275adf8f7d4Seric 	*/
276adf8f7d4Seric 
2777de81dc7Seric 	if (forkflag)
2787de81dc7Seric 	{
279*f0cc3246Seric # ifdef DEBUG
280*f0cc3246Seric 		if (Debug)
281*f0cc3246Seric 			printf("Forking\n");
282*f0cc3246Seric # endif
2837de81dc7Seric 		i = fork();
2847de81dc7Seric 		if (i < 0)
2857de81dc7Seric 		{
2867de81dc7Seric 			fprintf(stderr, "Sccs: cannot fork");
2877de81dc7Seric 			exit(EX_OSERR);
2887de81dc7Seric 		}
2897de81dc7Seric 		else if (i > 0)
2901777fbcbSeric 		{
2911777fbcbSeric 			wait(&st);
2921777fbcbSeric 			return (st);
2931777fbcbSeric 		}
2947de81dc7Seric 	}
2957de81dc7Seric 
2967de81dc7Seric 	/*
297172147faSeric 	**  Build new argument vector.
298172147faSeric 	*/
299172147faSeric 
300172147faSeric 	/* copy program filename arguments and flags */
301172147faSeric 	av = argv;
302172147faSeric 	while ((p = *++av) != NULL)
303172147faSeric 	{
304172147faSeric 		if (!bitset(NO_SDOT, flags) && *p != '-')
305172147faSeric 			*av = makefile(p);
306172147faSeric 	}
307172147faSeric 
308172147faSeric 	/*
3097de81dc7Seric 	**  Set protection as appropriate.
3107de81dc7Seric 	*/
3117de81dc7Seric 
3127de81dc7Seric 	if (bitset(REALUSER, flags))
3137de81dc7Seric 		setuid(getuid());
3147de81dc7Seric 
3157de81dc7Seric 	/*
316172147faSeric 	**  Call real SCCS program.
3177de81dc7Seric 	*/
3187de81dc7Seric 
319172147faSeric 	execv(progpath, argv);
320adf8f7d4Seric 	fprintf(stderr, "Sccs: cannot execute ");
3217de81dc7Seric 	perror(progpath);
322adf8f7d4Seric 	exit(EX_UNAVAILABLE);
323adf8f7d4Seric }
324adf8f7d4Seric 
325adf8f7d4Seric 
326adf8f7d4Seric char *
327adf8f7d4Seric makefile(name)
328adf8f7d4Seric 	char *name;
329adf8f7d4Seric {
330adf8f7d4Seric 	register char *p;
331adf8f7d4Seric 	register char c;
332adf8f7d4Seric 	char buf[512];
333adf8f7d4Seric 	struct stat stbuf;
334adf8f7d4Seric 	extern char *malloc();
335adf8f7d4Seric 
336adf8f7d4Seric 	/*
337adf8f7d4Seric 	**  See if this filename should be used as-is.
338adf8f7d4Seric 	**	There are three conditions where this can occur.
339adf8f7d4Seric 	**	1. The name already begins with "s.".
340adf8f7d4Seric 	**	2. The name has a "/" in it somewhere.
341adf8f7d4Seric 	**	3. The name references a directory.
342adf8f7d4Seric 	*/
343adf8f7d4Seric 
344b2538d76Seric 	if (strcmpn(name, "s.", 2) == 0)
345adf8f7d4Seric 		return (name);
346adf8f7d4Seric 	for (p = name; (c = *p) != '\0'; p++)
347adf8f7d4Seric 	{
348adf8f7d4Seric 		if (c == '/')
349adf8f7d4Seric 			return (name);
350adf8f7d4Seric 	}
351adf8f7d4Seric 	if (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR)
352adf8f7d4Seric 		return (name);
353adf8f7d4Seric 
354adf8f7d4Seric 	/*
355adf8f7d4Seric 	**  Prepend the path of the sccs file.
356adf8f7d4Seric 	*/
357adf8f7d4Seric 
358adf8f7d4Seric 	strcpy(buf, SccsPath);
359c4432be4Seric 	strcat(buf, "/s.");
360adf8f7d4Seric 	strcat(buf, name);
361adf8f7d4Seric 	p = malloc(strlen(buf) + 1);
362adf8f7d4Seric 	if (p == NULL)
363adf8f7d4Seric 	{
364adf8f7d4Seric 		perror("Sccs: no mem");
365adf8f7d4Seric 		exit(EX_OSERR);
366adf8f7d4Seric 	}
367adf8f7d4Seric 	strcpy(p, buf);
368adf8f7d4Seric 	return (p);
369adf8f7d4Seric }
370b2538d76Seric /*
371b2538d76Seric **  CLEAN -- clean out recreatable files
372b2538d76Seric **
373b2538d76Seric **	Any file for which an "s." file exists but no "p." file
374b2538d76Seric **	exists in the current directory is purged.
375b2538d76Seric **
376b2538d76Seric **	Parameters:
3773a208c77Seric **		really -- if TRUE, remove everything.
3783a208c77Seric **			else, just report status.
379b2538d76Seric **
380b2538d76Seric **	Returns:
381b2538d76Seric **		none.
382b2538d76Seric **
383b2538d76Seric **	Side Effects:
384b2538d76Seric **		removes files in the current directory.
385b2538d76Seric */
386b2538d76Seric 
3873a208c77Seric clean(really)
3883a208c77Seric 	bool really;
389b2538d76Seric {
390b2538d76Seric 	struct direct dir;
391b2538d76Seric 	struct stat stbuf;
392b2538d76Seric 	char buf[100];
3933a208c77Seric 	register FILE *dirfd;
3943a208c77Seric 	register char *basefile;
3952caac8fdSeric 	bool gotedit;
396b2538d76Seric 
397b2538d76Seric 	dirfd = fopen(SccsPath, "r");
398b2538d76Seric 	if (dirfd == NULL)
399b2538d76Seric 	{
400b2538d76Seric 		fprintf(stderr, "Sccs: cannot open %s\n", SccsPath);
401b2538d76Seric 		return;
402b2538d76Seric 	}
403b2538d76Seric 
404b2538d76Seric 	/*
405b2538d76Seric 	**  Scan the SCCS directory looking for s. files.
406b2538d76Seric 	*/
407b2538d76Seric 
4082caac8fdSeric 	gotedit = FALSE;
409b2538d76Seric 	while (fread(&dir, sizeof dir, 1, dirfd) != NULL)
410b2538d76Seric 	{
411b2538d76Seric 		if (dir.d_ino == 0 || strcmpn(dir.d_name, "s.", 2) != 0)
412b2538d76Seric 			continue;
413b2538d76Seric 
414b2538d76Seric 		/* got an s. file -- see if the p. file exists */
415b2538d76Seric 		strcpy(buf, SccsPath);
416b2538d76Seric 		strcat(buf, "/p.");
4173a208c77Seric 		basefile = &buf[strlen(buf)];
4183a208c77Seric 		basefile[sizeof dir.d_name - 2] = '\0';
4193a208c77Seric 		strcpyn(basefile, &dir.d_name[2], sizeof dir.d_name - 2);
420b2538d76Seric 		if (stat(buf, &stbuf) >= 0)
4213a208c77Seric 		{
4223a208c77Seric 			printf("%s: being editted\n", basefile);
4232caac8fdSeric 			gotedit = TRUE;
424b2538d76Seric 			continue;
4253a208c77Seric 		}
426b2538d76Seric 
427b2538d76Seric 		/* the s. file exists and no p. file exists -- unlink the g-file */
4283a208c77Seric 		if (really)
4293a208c77Seric 		{
430b2538d76Seric 			strcpyn(buf, &dir.d_name[2], sizeof dir.d_name - 2);
4313a208c77Seric 			buf[sizeof dir.d_name - 2] = '\0';
432b2538d76Seric 			unlink(buf);
433b2538d76Seric 		}
4343a208c77Seric 	}
435b2538d76Seric 
436b2538d76Seric 	fclose(dirfd);
4372caac8fdSeric 	if (!gotedit && !really)
4382caac8fdSeric 		printf("Nothing being editted\n");
439b2538d76Seric }
440