xref: /csrg-svn/usr.bin/sccs/sccs.c (revision 262)
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*262Seric static char SccsId[] = "@(#)sccs.c	1.12 06/09/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),
53201Seric 	"del",		CMACRO,	0,			"delta/get",
54226Seric 	"delt",		CMACRO,	0,			"delta/get",
55226Seric 	"fix",		FIX,	0,			NULL,
56261Seric 	"clean",	CLEAN,	REALUSER,		NULL,
57200Seric 	NULL,		-1,	0,			NULL
58148Seric };
59148Seric 
60157Seric char	*SccsPath = "SCCS";	/* pathname of SCCS files */
61157Seric bool	RealUser;		/* if set, running as real user */
62148Seric 
63148Seric main(argc, argv)
64148Seric 	int argc;
65148Seric 	char **argv;
66148Seric {
67148Seric 	register char *p;
68*262Seric 	extern struct sccsprog *lookup();
69148Seric 
70148Seric 	/*
71148Seric 	**  Detect and decode flags intended for this program.
72148Seric 	*/
73148Seric 
74200Seric 	if (argc < 2)
75148Seric 	{
76200Seric 		fprintf(stderr, "Usage: sccs [flags] command [flags]\n");
77200Seric 		exit(EX_USAGE);
78200Seric 	}
79200Seric 	argv[argc] = NULL;
80200Seric 
81*262Seric 	if (lookup(argv[0]) == NULL)
82200Seric 	{
83*262Seric 		while ((p = *++argv) != NULL)
84148Seric 		{
85*262Seric 			if (*p != '-')
86*262Seric 				break;
87*262Seric 			switch (*++p)
88*262Seric 			{
89*262Seric 			  case 'r':		/* run as real user */
90*262Seric 				setuid(getuid());
91*262Seric 				RealUser++;
92*262Seric 				break;
93148Seric 
94*262Seric 			  case 'p':		/* path of sccs files */
95*262Seric 				SccsPath = ++p;
96*262Seric 				break;
97148Seric 
98*262Seric 			  default:
99*262Seric 				fprintf(stderr, "Sccs: unknown option -%s\n", p);
100*262Seric 				break;
101*262Seric 			}
102148Seric 		}
103*262Seric 		if (SccsPath[0] == '\0')
104*262Seric 			SccsPath = ".";
105148Seric 	}
106148Seric 
107201Seric 	command(argv, FALSE);
108200Seric 	exit(EX_OK);
109200Seric }
110157Seric 
111201Seric command(argv, forkflag)
112200Seric 	char **argv;
113201Seric 	bool forkflag;
114200Seric {
115200Seric 	register struct sccsprog *cmd;
116200Seric 	register char *p;
117201Seric 	register char *q;
118201Seric 	char buf[40];
119*262Seric 	extern struct sccsprog *lookup();
120200Seric 
121157Seric 	/*
122148Seric 	**  Look up command.
123200Seric 	**	At this point, argv points to the command name.
124148Seric 	*/
125148Seric 
126200Seric 	p = *argv;
127*262Seric 	cmd = lookup(p);
128*262Seric 	if (cmd == NULL)
129148Seric 	{
130148Seric 		fprintf(stderr, "Sccs: Unknown command \"%s\"\n", p);
131148Seric 		exit(EX_USAGE);
132148Seric 	}
133148Seric 
134148Seric 	/*
135200Seric 	**  Interpret operation associated with this command.
136157Seric 	*/
137157Seric 
138200Seric 	switch (cmd->sccsoper)
139200Seric 	{
140200Seric 	  case PROG:		/* call an sccs prog */
141201Seric 		callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag);
142201Seric 		break;
143201Seric 
144201Seric 	  case CMACRO:		/* command macro */
145201Seric 		for (p = cmd->sccspath; *p != '\0'; p++)
146201Seric 		{
147201Seric 			for (q = buf; *p != '/' && *p != '\0'; p++, q++)
148201Seric 				*q = *p;
149201Seric 			*q = '\0';
150201Seric 			argv[0] = buf;
151201Seric 			command(argv, *p != '\0');
152201Seric 		}
153201Seric 		fprintf(stderr, "Sccs internal error: CMACRO\n");
154200Seric 		exit(EX_SOFTWARE);
155157Seric 
156226Seric 	  case FIX:		/* fix a delta */
157261Seric 		if (strcmpn(argv[1], "-r", 2) != 0)
158226Seric 		{
159226Seric 			fprintf(stderr, "Sccs: -r flag needed for fix command\n");
160226Seric 			break;
161226Seric 		}
162226Seric 		xcommand(&argv[1], TRUE, "get", "-k", NULL);
163226Seric 		xcommand(&argv[1], TRUE, "rmdel", NULL);
164226Seric 		xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL);
165226Seric 		fprintf(stderr, "Sccs internal error: FIX\n");
166226Seric 		exit(EX_SOFTWARE);
167226Seric 
168261Seric 	  case CLEAN:
169261Seric 		clean();
170261Seric 		break;
171261Seric 
172200Seric 	  default:
173200Seric 		fprintf(stderr, "Sccs internal error: oper %d\n", cmd->sccsoper);
174200Seric 		exit(EX_SOFTWARE);
175200Seric 	}
176200Seric }
177*262Seric /*
178*262Seric **  LOOKUP -- look up an SCCS command name.
179*262Seric **
180*262Seric **	Parameters:
181*262Seric **		name -- the name of the command to look up.
182*262Seric **
183*262Seric **	Returns:
184*262Seric **		ptr to command descriptor for this command.
185*262Seric **		NULL if no such entry.
186*262Seric **
187*262Seric **	Side Effects:
188*262Seric **		none.
189*262Seric */
190200Seric 
191*262Seric struct sccsprog *
192*262Seric lookup(name)
193*262Seric 	char *name;
194*262Seric {
195*262Seric 	register struct sccsprog *cmd;
196226Seric 
197*262Seric 	for (cmd = SccsProg; cmd->sccsname != NULL; cmd++)
198*262Seric 	{
199*262Seric 		if (strcmp(cmd->sccsname, name) == 0)
200*262Seric 			return (cmd);
201*262Seric 	}
202*262Seric 	return (NULL);
203*262Seric }
204*262Seric 
205*262Seric 
206226Seric xcommand(argv, forkflag, arg0)
207226Seric 	char **argv;
208226Seric 	bool forkflag;
209226Seric 	char *arg0;
210226Seric {
211226Seric 	register char **av;
212226Seric 	char *newargv[1000];
213226Seric 	register char **np;
214226Seric 
215226Seric 	np = newargv;
216226Seric 	for (av = &arg0; *av != NULL; av++)
217226Seric 		*np++ = *av;
218226Seric 	for (av = argv; *av != NULL; av++)
219226Seric 		*np++ = *av;
220226Seric 	*np = NULL;
221226Seric 	command(newargv, forkflag);
222226Seric }
223226Seric 
224200Seric callprog(progpath, flags, argv, forkflag)
225200Seric 	char *progpath;
226200Seric 	short flags;
227200Seric 	char **argv;
228200Seric 	bool forkflag;
229200Seric {
230200Seric 	register char *p;
231200Seric 	register char **av;
232200Seric 	extern char *makefile();
233200Seric 	register int i;
234201Seric 	auto int st;
235200Seric 
236200Seric 	if (*argv == NULL)
237200Seric 		return (-1);
238200Seric 
239157Seric 	/*
240226Seric 	**  Fork if appropriate.
241148Seric 	*/
242148Seric 
243200Seric 	if (forkflag)
244200Seric 	{
245200Seric 		i = fork();
246200Seric 		if (i < 0)
247200Seric 		{
248200Seric 			fprintf(stderr, "Sccs: cannot fork");
249200Seric 			exit(EX_OSERR);
250200Seric 		}
251200Seric 		else if (i > 0)
252201Seric 		{
253201Seric 			wait(&st);
254201Seric 			return (st);
255201Seric 		}
256200Seric 	}
257200Seric 
258200Seric 	/*
259226Seric 	**  Build new argument vector.
260226Seric 	*/
261226Seric 
262226Seric 	/* copy program filename arguments and flags */
263226Seric 	av = argv;
264226Seric 	while ((p = *++av) != NULL)
265226Seric 	{
266226Seric 		if (!bitset(NO_SDOT, flags) && *p != '-')
267226Seric 			*av = makefile(p);
268226Seric 	}
269226Seric 
270226Seric 	/*
271200Seric 	**  Set protection as appropriate.
272200Seric 	*/
273200Seric 
274200Seric 	if (bitset(REALUSER, flags))
275200Seric 		setuid(getuid());
276226Seric 
277200Seric 	/*
278226Seric 	**  Call real SCCS program.
279200Seric 	*/
280200Seric 
281226Seric 	execv(progpath, argv);
282148Seric 	fprintf(stderr, "Sccs: cannot execute ");
283200Seric 	perror(progpath);
284148Seric 	exit(EX_UNAVAILABLE);
285148Seric }
286148Seric 
287148Seric 
288148Seric char *
289148Seric makefile(name)
290148Seric 	char *name;
291148Seric {
292148Seric 	register char *p;
293148Seric 	register char c;
294148Seric 	char buf[512];
295148Seric 	struct stat stbuf;
296148Seric 	extern char *malloc();
297148Seric 
298148Seric 	/*
299148Seric 	**  See if this filename should be used as-is.
300148Seric 	**	There are three conditions where this can occur.
301148Seric 	**	1. The name already begins with "s.".
302148Seric 	**	2. The name has a "/" in it somewhere.
303148Seric 	**	3. The name references a directory.
304148Seric 	*/
305148Seric 
306261Seric 	if (strcmpn(name, "s.", 2) == 0)
307148Seric 		return (name);
308148Seric 	for (p = name; (c = *p) != '\0'; p++)
309148Seric 	{
310148Seric 		if (c == '/')
311148Seric 			return (name);
312148Seric 	}
313148Seric 	if (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR)
314148Seric 		return (name);
315148Seric 
316148Seric 	/*
317148Seric 	**  Prepend the path of the sccs file.
318148Seric 	*/
319148Seric 
320148Seric 	strcpy(buf, SccsPath);
321157Seric 	strcat(buf, "/s.");
322148Seric 	strcat(buf, name);
323148Seric 	p = malloc(strlen(buf) + 1);
324148Seric 	if (p == NULL)
325148Seric 	{
326148Seric 		perror("Sccs: no mem");
327148Seric 		exit(EX_OSERR);
328148Seric 	}
329148Seric 	strcpy(p, buf);
330148Seric 	return (p);
331148Seric }
332261Seric /*
333261Seric **  CLEAN -- clean out recreatable files
334261Seric **
335261Seric **	Any file for which an "s." file exists but no "p." file
336261Seric **	exists in the current directory is purged.
337261Seric **
338261Seric **	Parameters:
339261Seric **		none.
340261Seric **
341261Seric **	Returns:
342261Seric **		none.
343261Seric **
344261Seric **	Side Effects:
345261Seric **		removes files in the current directory.
346261Seric */
347261Seric 
348261Seric clean()
349261Seric {
350261Seric 	struct direct dir;
351261Seric 	struct stat stbuf;
352261Seric 	char buf[100];
353261Seric 	FILE *dirfd;
354261Seric 
355261Seric 	dirfd = fopen(SccsPath, "r");
356261Seric 	if (dirfd == NULL)
357261Seric 	{
358261Seric 		fprintf(stderr, "Sccs: cannot open %s\n", SccsPath);
359261Seric 		return;
360261Seric 	}
361261Seric 
362261Seric 	/*
363261Seric 	**  Scan the SCCS directory looking for s. files.
364261Seric 	*/
365261Seric 
366261Seric 	while (fread(&dir, sizeof dir, 1, dirfd) != NULL)
367261Seric 	{
368261Seric 		if (dir.d_ino == 0 || strcmpn(dir.d_name, "s.", 2) != 0)
369261Seric 			continue;
370261Seric 
371261Seric 		/* got an s. file -- see if the p. file exists */
372261Seric 		strcpy(buf, SccsPath);
373261Seric 		strcat(buf, "/p.");
374261Seric 		buf[strlen(buf) + sizeof dir.d_name - 2] = '\0';
375261Seric 		strcatn(buf, &dir.d_name[2], sizeof dir.d_name - 2);
376261Seric 		if (stat(buf, &stbuf) >= 0)
377261Seric 			continue;
378261Seric 
379261Seric 		/* the s. file exists and no p. file exists -- unlink the g-file */
380261Seric 		buf[sizeof dir.d_name - 2] = '\0';
381261Seric 		strcpyn(buf, &dir.d_name[2], sizeof dir.d_name - 2);
382261Seric 		unlink(buf);
383261Seric 	}
384261Seric 
385261Seric 	fclose(dirfd);
386261Seric }
387