xref: /csrg-svn/usr.bin/sccs/sccs.c (revision 351)
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*351Seric static char SccsId[] = "@(#)sccs.c	1.14 07/07/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,
56346Seric 	"clean",	CLEAN,	REALUSER,		(char *) TRUE,
57346Seric 	"info",		CLEAN,	REALUSER,		(char *) FALSE,
58200Seric 	NULL,		-1,	0,			NULL
59148Seric };
60148Seric 
61157Seric char	*SccsPath = "SCCS";	/* pathname of SCCS files */
62157Seric bool	RealUser;		/* if set, running as real user */
63148Seric 
64148Seric main(argc, argv)
65148Seric 	int argc;
66148Seric 	char **argv;
67148Seric {
68148Seric 	register char *p;
69262Seric 	extern struct sccsprog *lookup();
70148Seric 
71148Seric 	/*
72148Seric 	**  Detect and decode flags intended for this program.
73148Seric 	*/
74148Seric 
75200Seric 	if (argc < 2)
76148Seric 	{
77200Seric 		fprintf(stderr, "Usage: sccs [flags] command [flags]\n");
78200Seric 		exit(EX_USAGE);
79200Seric 	}
80200Seric 	argv[argc] = NULL;
81200Seric 
82262Seric 	if (lookup(argv[0]) == NULL)
83200Seric 	{
84262Seric 		while ((p = *++argv) != NULL)
85148Seric 		{
86262Seric 			if (*p != '-')
87262Seric 				break;
88262Seric 			switch (*++p)
89262Seric 			{
90262Seric 			  case 'r':		/* run as real user */
91262Seric 				setuid(getuid());
92262Seric 				RealUser++;
93262Seric 				break;
94148Seric 
95262Seric 			  case 'p':		/* path of sccs files */
96262Seric 				SccsPath = ++p;
97262Seric 				break;
98148Seric 
99262Seric 			  default:
100262Seric 				fprintf(stderr, "Sccs: unknown option -%s\n", p);
101262Seric 				break;
102262Seric 			}
103148Seric 		}
104262Seric 		if (SccsPath[0] == '\0')
105262Seric 			SccsPath = ".";
106148Seric 	}
107148Seric 
108201Seric 	command(argv, FALSE);
109200Seric 	exit(EX_OK);
110200Seric }
111157Seric 
112201Seric command(argv, forkflag)
113200Seric 	char **argv;
114201Seric 	bool forkflag;
115200Seric {
116200Seric 	register struct sccsprog *cmd;
117200Seric 	register char *p;
118201Seric 	register char *q;
119201Seric 	char buf[40];
120262Seric 	extern struct sccsprog *lookup();
121200Seric 
122157Seric 	/*
123148Seric 	**  Look up command.
124200Seric 	**	At this point, argv points to the command name.
125148Seric 	*/
126148Seric 
127200Seric 	p = *argv;
128262Seric 	cmd = lookup(p);
129262Seric 	if (cmd == NULL)
130148Seric 	{
131148Seric 		fprintf(stderr, "Sccs: Unknown command \"%s\"\n", p);
132148Seric 		exit(EX_USAGE);
133148Seric 	}
134148Seric 
135148Seric 	/*
136200Seric 	**  Interpret operation associated with this command.
137157Seric 	*/
138157Seric 
139200Seric 	switch (cmd->sccsoper)
140200Seric 	{
141200Seric 	  case PROG:		/* call an sccs prog */
142201Seric 		callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag);
143201Seric 		break;
144201Seric 
145201Seric 	  case CMACRO:		/* command macro */
146201Seric 		for (p = cmd->sccspath; *p != '\0'; p++)
147201Seric 		{
148201Seric 			for (q = buf; *p != '/' && *p != '\0'; p++, q++)
149201Seric 				*q = *p;
150201Seric 			*q = '\0';
151201Seric 			argv[0] = buf;
152201Seric 			command(argv, *p != '\0');
153201Seric 		}
154201Seric 		fprintf(stderr, "Sccs internal error: CMACRO\n");
155200Seric 		exit(EX_SOFTWARE);
156157Seric 
157226Seric 	  case FIX:		/* fix a delta */
158261Seric 		if (strcmpn(argv[1], "-r", 2) != 0)
159226Seric 		{
160226Seric 			fprintf(stderr, "Sccs: -r flag needed for fix command\n");
161226Seric 			break;
162226Seric 		}
163226Seric 		xcommand(&argv[1], TRUE, "get", "-k", NULL);
164226Seric 		xcommand(&argv[1], TRUE, "rmdel", NULL);
165226Seric 		xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL);
166226Seric 		fprintf(stderr, "Sccs internal error: FIX\n");
167226Seric 		exit(EX_SOFTWARE);
168226Seric 
169261Seric 	  case CLEAN:
170346Seric 		clean((bool) cmd->sccspath);
171261Seric 		break;
172261Seric 
173200Seric 	  default:
174200Seric 		fprintf(stderr, "Sccs internal error: oper %d\n", cmd->sccsoper);
175200Seric 		exit(EX_SOFTWARE);
176200Seric 	}
177200Seric }
178262Seric /*
179262Seric **  LOOKUP -- look up an SCCS command name.
180262Seric **
181262Seric **	Parameters:
182262Seric **		name -- the name of the command to look up.
183262Seric **
184262Seric **	Returns:
185262Seric **		ptr to command descriptor for this command.
186262Seric **		NULL if no such entry.
187262Seric **
188262Seric **	Side Effects:
189262Seric **		none.
190262Seric */
191200Seric 
192262Seric struct sccsprog *
193262Seric lookup(name)
194262Seric 	char *name;
195262Seric {
196262Seric 	register struct sccsprog *cmd;
197226Seric 
198262Seric 	for (cmd = SccsProg; cmd->sccsname != NULL; cmd++)
199262Seric 	{
200262Seric 		if (strcmp(cmd->sccsname, name) == 0)
201262Seric 			return (cmd);
202262Seric 	}
203262Seric 	return (NULL);
204262Seric }
205262Seric 
206262Seric 
207226Seric xcommand(argv, forkflag, arg0)
208226Seric 	char **argv;
209226Seric 	bool forkflag;
210226Seric 	char *arg0;
211226Seric {
212226Seric 	register char **av;
213226Seric 	char *newargv[1000];
214226Seric 	register char **np;
215226Seric 
216226Seric 	np = newargv;
217226Seric 	for (av = &arg0; *av != NULL; av++)
218226Seric 		*np++ = *av;
219226Seric 	for (av = argv; *av != NULL; av++)
220226Seric 		*np++ = *av;
221226Seric 	*np = NULL;
222226Seric 	command(newargv, forkflag);
223226Seric }
224226Seric 
225200Seric callprog(progpath, flags, argv, forkflag)
226200Seric 	char *progpath;
227200Seric 	short flags;
228200Seric 	char **argv;
229200Seric 	bool forkflag;
230200Seric {
231200Seric 	register char *p;
232200Seric 	register char **av;
233200Seric 	extern char *makefile();
234200Seric 	register int i;
235201Seric 	auto int st;
236200Seric 
237200Seric 	if (*argv == NULL)
238200Seric 		return (-1);
239200Seric 
240157Seric 	/*
241226Seric 	**  Fork if appropriate.
242148Seric 	*/
243148Seric 
244200Seric 	if (forkflag)
245200Seric 	{
246200Seric 		i = fork();
247200Seric 		if (i < 0)
248200Seric 		{
249200Seric 			fprintf(stderr, "Sccs: cannot fork");
250200Seric 			exit(EX_OSERR);
251200Seric 		}
252200Seric 		else if (i > 0)
253201Seric 		{
254201Seric 			wait(&st);
255201Seric 			return (st);
256201Seric 		}
257200Seric 	}
258200Seric 
259200Seric 	/*
260226Seric 	**  Build new argument vector.
261226Seric 	*/
262226Seric 
263226Seric 	/* copy program filename arguments and flags */
264226Seric 	av = argv;
265226Seric 	while ((p = *++av) != NULL)
266226Seric 	{
267226Seric 		if (!bitset(NO_SDOT, flags) && *p != '-')
268226Seric 			*av = makefile(p);
269226Seric 	}
270226Seric 
271226Seric 	/*
272200Seric 	**  Set protection as appropriate.
273200Seric 	*/
274200Seric 
275200Seric 	if (bitset(REALUSER, flags))
276200Seric 		setuid(getuid());
277226Seric 
278200Seric 	/*
279226Seric 	**  Call real SCCS program.
280200Seric 	*/
281200Seric 
282226Seric 	execv(progpath, argv);
283148Seric 	fprintf(stderr, "Sccs: cannot execute ");
284200Seric 	perror(progpath);
285148Seric 	exit(EX_UNAVAILABLE);
286148Seric }
287148Seric 
288148Seric 
289148Seric char *
290148Seric makefile(name)
291148Seric 	char *name;
292148Seric {
293148Seric 	register char *p;
294148Seric 	register char c;
295148Seric 	char buf[512];
296148Seric 	struct stat stbuf;
297148Seric 	extern char *malloc();
298148Seric 
299148Seric 	/*
300148Seric 	**  See if this filename should be used as-is.
301148Seric 	**	There are three conditions where this can occur.
302148Seric 	**	1. The name already begins with "s.".
303148Seric 	**	2. The name has a "/" in it somewhere.
304148Seric 	**	3. The name references a directory.
305148Seric 	*/
306148Seric 
307261Seric 	if (strcmpn(name, "s.", 2) == 0)
308148Seric 		return (name);
309148Seric 	for (p = name; (c = *p) != '\0'; p++)
310148Seric 	{
311148Seric 		if (c == '/')
312148Seric 			return (name);
313148Seric 	}
314148Seric 	if (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR)
315148Seric 		return (name);
316148Seric 
317148Seric 	/*
318148Seric 	**  Prepend the path of the sccs file.
319148Seric 	*/
320148Seric 
321148Seric 	strcpy(buf, SccsPath);
322157Seric 	strcat(buf, "/s.");
323148Seric 	strcat(buf, name);
324148Seric 	p = malloc(strlen(buf) + 1);
325148Seric 	if (p == NULL)
326148Seric 	{
327148Seric 		perror("Sccs: no mem");
328148Seric 		exit(EX_OSERR);
329148Seric 	}
330148Seric 	strcpy(p, buf);
331148Seric 	return (p);
332148Seric }
333261Seric /*
334261Seric **  CLEAN -- clean out recreatable files
335261Seric **
336261Seric **	Any file for which an "s." file exists but no "p." file
337261Seric **	exists in the current directory is purged.
338261Seric **
339261Seric **	Parameters:
340346Seric **		really -- if TRUE, remove everything.
341346Seric **			else, just report status.
342261Seric **
343261Seric **	Returns:
344261Seric **		none.
345261Seric **
346261Seric **	Side Effects:
347261Seric **		removes files in the current directory.
348261Seric */
349261Seric 
350346Seric clean(really)
351346Seric 	bool really;
352261Seric {
353261Seric 	struct direct dir;
354261Seric 	struct stat stbuf;
355261Seric 	char buf[100];
356346Seric 	register FILE *dirfd;
357346Seric 	register char *basefile;
358*351Seric 	bool gotedit;
359261Seric 
360261Seric 	dirfd = fopen(SccsPath, "r");
361261Seric 	if (dirfd == NULL)
362261Seric 	{
363261Seric 		fprintf(stderr, "Sccs: cannot open %s\n", SccsPath);
364261Seric 		return;
365261Seric 	}
366261Seric 
367261Seric 	/*
368261Seric 	**  Scan the SCCS directory looking for s. files.
369261Seric 	*/
370261Seric 
371*351Seric 	gotedit = FALSE;
372261Seric 	while (fread(&dir, sizeof dir, 1, dirfd) != NULL)
373261Seric 	{
374261Seric 		if (dir.d_ino == 0 || strcmpn(dir.d_name, "s.", 2) != 0)
375261Seric 			continue;
376261Seric 
377261Seric 		/* got an s. file -- see if the p. file exists */
378261Seric 		strcpy(buf, SccsPath);
379261Seric 		strcat(buf, "/p.");
380346Seric 		basefile = &buf[strlen(buf)];
381346Seric 		basefile[sizeof dir.d_name - 2] = '\0';
382346Seric 		strcpyn(basefile, &dir.d_name[2], sizeof dir.d_name - 2);
383261Seric 		if (stat(buf, &stbuf) >= 0)
384346Seric 		{
385346Seric 			printf("%s: being editted\n", basefile);
386*351Seric 			gotedit = TRUE;
387261Seric 			continue;
388346Seric 		}
389261Seric 
390261Seric 		/* the s. file exists and no p. file exists -- unlink the g-file */
391346Seric 		if (really)
392346Seric 		{
393346Seric 			strcpyn(buf, &dir.d_name[2], sizeof dir.d_name - 2);
394346Seric 			buf[sizeof dir.d_name - 2] = '\0';
395346Seric 			unlink(buf);
396346Seric 		}
397261Seric 	}
398261Seric 
399261Seric 	fclose(dirfd);
400*351Seric 	if (!gotedit && !really)
401*351Seric 		printf("Nothing being editted\n");
402261Seric }
403