xref: /csrg-svn/usr.bin/sccs/sccs.c (revision 393)
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 
8*393Seric static char SccsId[] = "@(#)sccs.c	1.15 07/24/80";
9155Seric 
10157Seric # define bitset(bit, word)	((bit) & (word))
11157Seric 
12157Seric typedef char	bool;
13200Seric # define TRUE	1
14200Seric # define FALSE	0
15157Seric 
16148Seric struct sccsprog
17148Seric {
18148Seric 	char	*sccsname;	/* name of SCCS routine */
19200Seric 	short	sccsoper;	/* opcode, see below */
20200Seric 	short	sccsflags;	/* flags, see below */
21148Seric 	char	*sccspath;	/* pathname of binary implementing */
22148Seric };
23148Seric 
24200Seric /* values for sccsoper */
25200Seric # define PROG		0	/* call a program */
26201Seric # define CMACRO		1	/* command substitution macro */
27226Seric # define FIX		2	/* fix a delta */
28261Seric # define CLEAN		3	/* clean out recreatable files */
29200Seric 
30157Seric /* bits for sccsflags */
31200Seric # define NO_SDOT	0001	/* no s. on front of args */
32200Seric # define REALUSER	0002	/* protected (e.g., admin) */
33148Seric 
34202Seric # ifdef CSVAX
35202Seric # define PROGPATH(name)	"/usr/local/name"
36202Seric # endif CSVAX
37202Seric 
38202Seric # ifndef PROGPATH
39202Seric # define PROGPATH(name)	"/usr/sccs/name"
40202Seric # endif PROGPATH
41202Seric 
42148Seric struct sccsprog SccsProg[] =
43148Seric {
44202Seric 	"admin",	PROG,	REALUSER,		PROGPATH(admin),
45202Seric 	"chghist",	PROG,	0,			PROGPATH(rmdel),
46202Seric 	"comb",		PROG,	0,			PROGPATH(comb),
47202Seric 	"delta",	PROG,	0,			PROGPATH(delta),
48202Seric 	"get",		PROG,	0,			PROGPATH(get),
49202Seric 	"help",		PROG,	NO_SDOT,		PROGPATH(help),
50202Seric 	"prt",		PROG,	0,			PROGPATH(prt),
51202Seric 	"rmdel",	PROG,	REALUSER,		PROGPATH(rmdel),
52202Seric 	"what",		PROG,	NO_SDOT,		PROGPATH(what),
53*393Seric 	"edit",		CMACRO,	0,			"get -e",
54*393Seric 	"delget",	CMACRO,	0,			"delta/get",
55*393Seric 	"deled",	CMACRO,	0,			"delta/get -e",
56201Seric 	"del",		CMACRO,	0,			"delta/get",
57226Seric 	"delt",		CMACRO,	0,			"delta/get",
58226Seric 	"fix",		FIX,	0,			NULL,
59346Seric 	"clean",	CLEAN,	REALUSER,		(char *) TRUE,
60346Seric 	"info",		CLEAN,	REALUSER,		(char *) FALSE,
61200Seric 	NULL,		-1,	0,			NULL
62148Seric };
63148Seric 
64157Seric char	*SccsPath = "SCCS";	/* pathname of SCCS files */
65157Seric bool	RealUser;		/* if set, running as real user */
66*393Seric # ifdef DEBUG
67*393Seric bool	Debug;			/* turn on tracing */
68*393Seric # endif
69148Seric 
70148Seric main(argc, argv)
71148Seric 	int argc;
72148Seric 	char **argv;
73148Seric {
74148Seric 	register char *p;
75262Seric 	extern struct sccsprog *lookup();
76148Seric 
77148Seric 	/*
78148Seric 	**  Detect and decode flags intended for this program.
79148Seric 	*/
80148Seric 
81200Seric 	if (argc < 2)
82148Seric 	{
83200Seric 		fprintf(stderr, "Usage: sccs [flags] command [flags]\n");
84200Seric 		exit(EX_USAGE);
85200Seric 	}
86200Seric 	argv[argc] = NULL;
87200Seric 
88262Seric 	if (lookup(argv[0]) == NULL)
89200Seric 	{
90262Seric 		while ((p = *++argv) != NULL)
91148Seric 		{
92262Seric 			if (*p != '-')
93262Seric 				break;
94262Seric 			switch (*++p)
95262Seric 			{
96262Seric 			  case 'r':		/* run as real user */
97262Seric 				setuid(getuid());
98262Seric 				RealUser++;
99262Seric 				break;
100148Seric 
101262Seric 			  case 'p':		/* path of sccs files */
102262Seric 				SccsPath = ++p;
103262Seric 				break;
104148Seric 
105*393Seric # ifdef DEBUG
106*393Seric 			  case 'T':		/* trace */
107*393Seric 				Debug++;
108*393Seric 				break;
109*393Seric # endif
110*393Seric 
111262Seric 			  default:
112262Seric 				fprintf(stderr, "Sccs: unknown option -%s\n", p);
113262Seric 				break;
114262Seric 			}
115148Seric 		}
116262Seric 		if (SccsPath[0] == '\0')
117262Seric 			SccsPath = ".";
118148Seric 	}
119148Seric 
120201Seric 	command(argv, FALSE);
121200Seric 	exit(EX_OK);
122200Seric }
123157Seric 
124201Seric command(argv, forkflag)
125200Seric 	char **argv;
126201Seric 	bool forkflag;
127200Seric {
128200Seric 	register struct sccsprog *cmd;
129200Seric 	register char *p;
130201Seric 	register char *q;
131201Seric 	char buf[40];
132262Seric 	extern struct sccsprog *lookup();
133*393Seric 	char *nav[7];
134*393Seric 	char **avp;
135200Seric 
136*393Seric # ifdef DEBUG
137*393Seric 	if (Debug)
138*393Seric 	{
139*393Seric 		printf("command:\n");
140*393Seric 		for (avp = argv; *avp != NULL; avp++)
141*393Seric 			printf("    \"%s\"\n", *avp);
142*393Seric 	}
143*393Seric # endif
144*393Seric 
145157Seric 	/*
146148Seric 	**  Look up command.
147200Seric 	**	At this point, argv points to the command name.
148148Seric 	*/
149148Seric 
150*393Seric 	cmd = lookup(*argv);
151262Seric 	if (cmd == NULL)
152148Seric 	{
153*393Seric 		fprintf(stderr, "Sccs: Unknown command \"%s\"\n", *argv);
154148Seric 		exit(EX_USAGE);
155148Seric 	}
156148Seric 
157148Seric 	/*
158200Seric 	**  Interpret operation associated with this command.
159157Seric 	*/
160157Seric 
161200Seric 	switch (cmd->sccsoper)
162200Seric 	{
163200Seric 	  case PROG:		/* call an sccs prog */
164201Seric 		callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag);
165201Seric 		break;
166201Seric 
167201Seric 	  case CMACRO:		/* command macro */
168201Seric 		for (p = cmd->sccspath; *p != '\0'; p++)
169201Seric 		{
170*393Seric 			avp = nav;
171*393Seric 			*avp++ = buf;
172201Seric 			for (q = buf; *p != '/' && *p != '\0'; p++, q++)
173*393Seric 			{
174*393Seric 				if (*p == ' ')
175*393Seric 				{
176*393Seric 					*q = '\0';
177*393Seric 					*avp++ = &q[1];
178*393Seric 				}
179*393Seric 				else
180*393Seric 					*q = *p;
181*393Seric 			}
182201Seric 			*q = '\0';
183*393Seric 			*avp = NULL;
184*393Seric 			xcommand(&argv[1], *p != '\0', nav[0], nav[1], nav[2],
185*393Seric 				 nav[3], nav[4], nav[5], nav[6]);
186201Seric 		}
187201Seric 		fprintf(stderr, "Sccs internal error: CMACRO\n");
188200Seric 		exit(EX_SOFTWARE);
189157Seric 
190226Seric 	  case FIX:		/* fix a delta */
191261Seric 		if (strcmpn(argv[1], "-r", 2) != 0)
192226Seric 		{
193226Seric 			fprintf(stderr, "Sccs: -r flag needed for fix command\n");
194226Seric 			break;
195226Seric 		}
196226Seric 		xcommand(&argv[1], TRUE, "get", "-k", NULL);
197226Seric 		xcommand(&argv[1], TRUE, "rmdel", NULL);
198226Seric 		xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL);
199226Seric 		fprintf(stderr, "Sccs internal error: FIX\n");
200226Seric 		exit(EX_SOFTWARE);
201226Seric 
202261Seric 	  case CLEAN:
203346Seric 		clean((bool) cmd->sccspath);
204261Seric 		break;
205261Seric 
206200Seric 	  default:
207200Seric 		fprintf(stderr, "Sccs internal error: oper %d\n", cmd->sccsoper);
208200Seric 		exit(EX_SOFTWARE);
209200Seric 	}
210200Seric }
211262Seric /*
212262Seric **  LOOKUP -- look up an SCCS command name.
213262Seric **
214262Seric **	Parameters:
215262Seric **		name -- the name of the command to look up.
216262Seric **
217262Seric **	Returns:
218262Seric **		ptr to command descriptor for this command.
219262Seric **		NULL if no such entry.
220262Seric **
221262Seric **	Side Effects:
222262Seric **		none.
223262Seric */
224200Seric 
225262Seric struct sccsprog *
226262Seric lookup(name)
227262Seric 	char *name;
228262Seric {
229262Seric 	register struct sccsprog *cmd;
230226Seric 
231262Seric 	for (cmd = SccsProg; cmd->sccsname != NULL; cmd++)
232262Seric 	{
233262Seric 		if (strcmp(cmd->sccsname, name) == 0)
234262Seric 			return (cmd);
235262Seric 	}
236262Seric 	return (NULL);
237262Seric }
238262Seric 
239262Seric 
240226Seric xcommand(argv, forkflag, arg0)
241226Seric 	char **argv;
242226Seric 	bool forkflag;
243226Seric 	char *arg0;
244226Seric {
245226Seric 	register char **av;
246226Seric 	char *newargv[1000];
247226Seric 	register char **np;
248226Seric 
249226Seric 	np = newargv;
250226Seric 	for (av = &arg0; *av != NULL; av++)
251226Seric 		*np++ = *av;
252226Seric 	for (av = argv; *av != NULL; av++)
253226Seric 		*np++ = *av;
254226Seric 	*np = NULL;
255226Seric 	command(newargv, forkflag);
256226Seric }
257226Seric 
258200Seric callprog(progpath, flags, argv, forkflag)
259200Seric 	char *progpath;
260200Seric 	short flags;
261200Seric 	char **argv;
262200Seric 	bool forkflag;
263200Seric {
264200Seric 	register char *p;
265200Seric 	register char **av;
266200Seric 	extern char *makefile();
267200Seric 	register int i;
268201Seric 	auto int st;
269200Seric 
270200Seric 	if (*argv == NULL)
271200Seric 		return (-1);
272200Seric 
273157Seric 	/*
274226Seric 	**  Fork if appropriate.
275148Seric 	*/
276148Seric 
277200Seric 	if (forkflag)
278200Seric 	{
279*393Seric # ifdef DEBUG
280*393Seric 		if (Debug)
281*393Seric 			printf("Forking\n");
282*393Seric # endif
283200Seric 		i = fork();
284200Seric 		if (i < 0)
285200Seric 		{
286200Seric 			fprintf(stderr, "Sccs: cannot fork");
287200Seric 			exit(EX_OSERR);
288200Seric 		}
289200Seric 		else if (i > 0)
290201Seric 		{
291201Seric 			wait(&st);
292201Seric 			return (st);
293201Seric 		}
294200Seric 	}
295200Seric 
296200Seric 	/*
297226Seric 	**  Build new argument vector.
298226Seric 	*/
299226Seric 
300226Seric 	/* copy program filename arguments and flags */
301226Seric 	av = argv;
302226Seric 	while ((p = *++av) != NULL)
303226Seric 	{
304226Seric 		if (!bitset(NO_SDOT, flags) && *p != '-')
305226Seric 			*av = makefile(p);
306226Seric 	}
307226Seric 
308226Seric 	/*
309200Seric 	**  Set protection as appropriate.
310200Seric 	*/
311200Seric 
312200Seric 	if (bitset(REALUSER, flags))
313200Seric 		setuid(getuid());
314226Seric 
315200Seric 	/*
316226Seric 	**  Call real SCCS program.
317200Seric 	*/
318200Seric 
319226Seric 	execv(progpath, argv);
320148Seric 	fprintf(stderr, "Sccs: cannot execute ");
321200Seric 	perror(progpath);
322148Seric 	exit(EX_UNAVAILABLE);
323148Seric }
324148Seric 
325148Seric 
326148Seric char *
327148Seric makefile(name)
328148Seric 	char *name;
329148Seric {
330148Seric 	register char *p;
331148Seric 	register char c;
332148Seric 	char buf[512];
333148Seric 	struct stat stbuf;
334148Seric 	extern char *malloc();
335148Seric 
336148Seric 	/*
337148Seric 	**  See if this filename should be used as-is.
338148Seric 	**	There are three conditions where this can occur.
339148Seric 	**	1. The name already begins with "s.".
340148Seric 	**	2. The name has a "/" in it somewhere.
341148Seric 	**	3. The name references a directory.
342148Seric 	*/
343148Seric 
344261Seric 	if (strcmpn(name, "s.", 2) == 0)
345148Seric 		return (name);
346148Seric 	for (p = name; (c = *p) != '\0'; p++)
347148Seric 	{
348148Seric 		if (c == '/')
349148Seric 			return (name);
350148Seric 	}
351148Seric 	if (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR)
352148Seric 		return (name);
353148Seric 
354148Seric 	/*
355148Seric 	**  Prepend the path of the sccs file.
356148Seric 	*/
357148Seric 
358148Seric 	strcpy(buf, SccsPath);
359157Seric 	strcat(buf, "/s.");
360148Seric 	strcat(buf, name);
361148Seric 	p = malloc(strlen(buf) + 1);
362148Seric 	if (p == NULL)
363148Seric 	{
364148Seric 		perror("Sccs: no mem");
365148Seric 		exit(EX_OSERR);
366148Seric 	}
367148Seric 	strcpy(p, buf);
368148Seric 	return (p);
369148Seric }
370261Seric /*
371261Seric **  CLEAN -- clean out recreatable files
372261Seric **
373261Seric **	Any file for which an "s." file exists but no "p." file
374261Seric **	exists in the current directory is purged.
375261Seric **
376261Seric **	Parameters:
377346Seric **		really -- if TRUE, remove everything.
378346Seric **			else, just report status.
379261Seric **
380261Seric **	Returns:
381261Seric **		none.
382261Seric **
383261Seric **	Side Effects:
384261Seric **		removes files in the current directory.
385261Seric */
386261Seric 
387346Seric clean(really)
388346Seric 	bool really;
389261Seric {
390261Seric 	struct direct dir;
391261Seric 	struct stat stbuf;
392261Seric 	char buf[100];
393346Seric 	register FILE *dirfd;
394346Seric 	register char *basefile;
395351Seric 	bool gotedit;
396261Seric 
397261Seric 	dirfd = fopen(SccsPath, "r");
398261Seric 	if (dirfd == NULL)
399261Seric 	{
400261Seric 		fprintf(stderr, "Sccs: cannot open %s\n", SccsPath);
401261Seric 		return;
402261Seric 	}
403261Seric 
404261Seric 	/*
405261Seric 	**  Scan the SCCS directory looking for s. files.
406261Seric 	*/
407261Seric 
408351Seric 	gotedit = FALSE;
409261Seric 	while (fread(&dir, sizeof dir, 1, dirfd) != NULL)
410261Seric 	{
411261Seric 		if (dir.d_ino == 0 || strcmpn(dir.d_name, "s.", 2) != 0)
412261Seric 			continue;
413261Seric 
414261Seric 		/* got an s. file -- see if the p. file exists */
415261Seric 		strcpy(buf, SccsPath);
416261Seric 		strcat(buf, "/p.");
417346Seric 		basefile = &buf[strlen(buf)];
418346Seric 		basefile[sizeof dir.d_name - 2] = '\0';
419346Seric 		strcpyn(basefile, &dir.d_name[2], sizeof dir.d_name - 2);
420261Seric 		if (stat(buf, &stbuf) >= 0)
421346Seric 		{
422346Seric 			printf("%s: being editted\n", basefile);
423351Seric 			gotedit = TRUE;
424261Seric 			continue;
425346Seric 		}
426261Seric 
427261Seric 		/* the s. file exists and no p. file exists -- unlink the g-file */
428346Seric 		if (really)
429346Seric 		{
430346Seric 			strcpyn(buf, &dir.d_name[2], sizeof dir.d_name - 2);
431346Seric 			buf[sizeof dir.d_name - 2] = '\0';
432346Seric 			unlink(buf);
433346Seric 		}
434261Seric 	}
435261Seric 
436261Seric 	fclose(dirfd);
437351Seric 	if (!gotedit && !really)
438351Seric 		printf("Nothing being editted\n");
439261Seric }
440