xref: /csrg-svn/usr.bin/sccs/sccs.c (revision 416)
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*416Seric static char SccsId[] = "@(#)sccs.c	1.19 07/28/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 */
29396Seric # define UNEDIT		4	/* unedit a file */
30200Seric 
31157Seric /* bits for sccsflags */
32200Seric # define NO_SDOT	0001	/* no s. on front of args */
33200Seric # define REALUSER	0002	/* protected (e.g., admin) */
34148Seric 
35202Seric # ifdef CSVAX
36202Seric # define PROGPATH(name)	"/usr/local/name"
37202Seric # endif CSVAX
38202Seric 
39202Seric # ifndef PROGPATH
40202Seric # define PROGPATH(name)	"/usr/sccs/name"
41202Seric # endif PROGPATH
42202Seric 
43148Seric struct sccsprog SccsProg[] =
44148Seric {
45202Seric 	"admin",	PROG,	REALUSER,		PROGPATH(admin),
46202Seric 	"chghist",	PROG,	0,			PROGPATH(rmdel),
47202Seric 	"comb",		PROG,	0,			PROGPATH(comb),
48202Seric 	"delta",	PROG,	0,			PROGPATH(delta),
49202Seric 	"get",		PROG,	0,			PROGPATH(get),
50202Seric 	"help",		PROG,	NO_SDOT,		PROGPATH(help),
51202Seric 	"prt",		PROG,	0,			PROGPATH(prt),
52202Seric 	"rmdel",	PROG,	REALUSER,		PROGPATH(rmdel),
53202Seric 	"what",		PROG,	NO_SDOT,		PROGPATH(what),
54393Seric 	"edit",		CMACRO,	0,			"get -e",
55393Seric 	"delget",	CMACRO,	0,			"delta/get",
56398Seric 	"deledit",	CMACRO,	0,			"delta/get -e",
57201Seric 	"del",		CMACRO,	0,			"delta/get",
58226Seric 	"delt",		CMACRO,	0,			"delta/get",
59226Seric 	"fix",		FIX,	0,			NULL,
60346Seric 	"clean",	CLEAN,	REALUSER,		(char *) TRUE,
61346Seric 	"info",		CLEAN,	REALUSER,		(char *) FALSE,
62396Seric 	"unedit",	UNEDIT,	0,			NULL,
63200Seric 	NULL,		-1,	0,			NULL
64148Seric };
65148Seric 
66396Seric struct pfile
67396Seric {
68396Seric 	char	*p_osid;	/* old SID */
69396Seric 	char	*p_nsid;	/* new SID */
70396Seric 	char	*p_user;	/* user who did edit */
71396Seric 	char	*p_date;	/* date of get */
72396Seric 	char	*p_time;	/* time of get */
73396Seric };
74396Seric 
75157Seric char	*SccsPath = "SCCS";	/* pathname of SCCS files */
76157Seric bool	RealUser;		/* if set, running as real user */
77393Seric # ifdef DEBUG
78393Seric bool	Debug;			/* turn on tracing */
79393Seric # endif
80148Seric 
81148Seric main(argc, argv)
82148Seric 	int argc;
83148Seric 	char **argv;
84148Seric {
85148Seric 	register char *p;
86262Seric 	extern struct sccsprog *lookup();
87148Seric 
88148Seric 	/*
89148Seric 	**  Detect and decode flags intended for this program.
90148Seric 	*/
91148Seric 
92200Seric 	if (argc < 2)
93148Seric 	{
94200Seric 		fprintf(stderr, "Usage: sccs [flags] command [flags]\n");
95200Seric 		exit(EX_USAGE);
96200Seric 	}
97200Seric 	argv[argc] = NULL;
98200Seric 
99262Seric 	if (lookup(argv[0]) == NULL)
100200Seric 	{
101262Seric 		while ((p = *++argv) != NULL)
102148Seric 		{
103262Seric 			if (*p != '-')
104262Seric 				break;
105262Seric 			switch (*++p)
106262Seric 			{
107262Seric 			  case 'r':		/* run as real user */
108262Seric 				setuid(getuid());
109262Seric 				RealUser++;
110262Seric 				break;
111148Seric 
112262Seric 			  case 'p':		/* path of sccs files */
113262Seric 				SccsPath = ++p;
114262Seric 				break;
115148Seric 
116393Seric # ifdef DEBUG
117393Seric 			  case 'T':		/* trace */
118393Seric 				Debug++;
119393Seric 				break;
120393Seric # endif
121393Seric 
122262Seric 			  default:
123262Seric 				fprintf(stderr, "Sccs: unknown option -%s\n", p);
124262Seric 				break;
125262Seric 			}
126148Seric 		}
127262Seric 		if (SccsPath[0] == '\0')
128262Seric 			SccsPath = ".";
129148Seric 	}
130148Seric 
131201Seric 	command(argv, FALSE);
132200Seric 	exit(EX_OK);
133200Seric }
134157Seric 
135201Seric command(argv, forkflag)
136200Seric 	char **argv;
137201Seric 	bool forkflag;
138200Seric {
139200Seric 	register struct sccsprog *cmd;
140200Seric 	register char *p;
141201Seric 	register char *q;
142201Seric 	char buf[40];
143262Seric 	extern struct sccsprog *lookup();
144393Seric 	char *nav[7];
145393Seric 	char **avp;
146200Seric 
147393Seric # ifdef DEBUG
148393Seric 	if (Debug)
149393Seric 	{
150393Seric 		printf("command:\n");
151393Seric 		for (avp = argv; *avp != NULL; avp++)
152393Seric 			printf("    \"%s\"\n", *avp);
153393Seric 	}
154393Seric # endif
155393Seric 
156157Seric 	/*
157148Seric 	**  Look up command.
158200Seric 	**	At this point, argv points to the command name.
159148Seric 	*/
160148Seric 
161396Seric 	cmd = lookup(argv[0]);
162262Seric 	if (cmd == NULL)
163148Seric 	{
164396Seric 		fprintf(stderr, "Sccs: Unknown command \"%s\"\n", argv[0]);
165148Seric 		exit(EX_USAGE);
166148Seric 	}
167148Seric 
168148Seric 	/*
169200Seric 	**  Interpret operation associated with this command.
170157Seric 	*/
171157Seric 
172200Seric 	switch (cmd->sccsoper)
173200Seric 	{
174200Seric 	  case PROG:		/* call an sccs prog */
175201Seric 		callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag);
176201Seric 		break;
177201Seric 
178201Seric 	  case CMACRO:		/* command macro */
179201Seric 		for (p = cmd->sccspath; *p != '\0'; p++)
180201Seric 		{
181393Seric 			avp = nav;
182393Seric 			*avp++ = buf;
183201Seric 			for (q = buf; *p != '/' && *p != '\0'; p++, q++)
184393Seric 			{
185393Seric 				if (*p == ' ')
186393Seric 				{
187393Seric 					*q = '\0';
188393Seric 					*avp++ = &q[1];
189393Seric 				}
190393Seric 				else
191393Seric 					*q = *p;
192393Seric 			}
193201Seric 			*q = '\0';
194393Seric 			*avp = NULL;
195393Seric 			xcommand(&argv[1], *p != '\0', nav[0], nav[1], nav[2],
196393Seric 				 nav[3], nav[4], nav[5], nav[6]);
197201Seric 		}
198201Seric 		fprintf(stderr, "Sccs internal error: CMACRO\n");
199200Seric 		exit(EX_SOFTWARE);
200157Seric 
201226Seric 	  case FIX:		/* fix a delta */
202261Seric 		if (strcmpn(argv[1], "-r", 2) != 0)
203226Seric 		{
204226Seric 			fprintf(stderr, "Sccs: -r flag needed for fix command\n");
205226Seric 			break;
206226Seric 		}
207226Seric 		xcommand(&argv[1], TRUE, "get", "-k", NULL);
208226Seric 		xcommand(&argv[1], TRUE, "rmdel", NULL);
209226Seric 		xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL);
210226Seric 		fprintf(stderr, "Sccs internal error: FIX\n");
211226Seric 		exit(EX_SOFTWARE);
212226Seric 
213261Seric 	  case CLEAN:
214346Seric 		clean((bool) cmd->sccspath);
215261Seric 		break;
216261Seric 
217396Seric 	  case UNEDIT:
218396Seric 		for (avp = &argv[1]; *avp != NULL; avp++)
219396Seric 			unedit(*avp);
220396Seric 		break;
221396Seric 
222200Seric 	  default:
223200Seric 		fprintf(stderr, "Sccs internal error: oper %d\n", cmd->sccsoper);
224200Seric 		exit(EX_SOFTWARE);
225200Seric 	}
226200Seric }
227262Seric /*
228262Seric **  LOOKUP -- look up an SCCS command name.
229262Seric **
230262Seric **	Parameters:
231262Seric **		name -- the name of the command to look up.
232262Seric **
233262Seric **	Returns:
234262Seric **		ptr to command descriptor for this command.
235262Seric **		NULL if no such entry.
236262Seric **
237262Seric **	Side Effects:
238262Seric **		none.
239262Seric */
240200Seric 
241262Seric struct sccsprog *
242262Seric lookup(name)
243262Seric 	char *name;
244262Seric {
245262Seric 	register struct sccsprog *cmd;
246226Seric 
247262Seric 	for (cmd = SccsProg; cmd->sccsname != NULL; cmd++)
248262Seric 	{
249262Seric 		if (strcmp(cmd->sccsname, name) == 0)
250262Seric 			return (cmd);
251262Seric 	}
252262Seric 	return (NULL);
253262Seric }
254262Seric 
255262Seric 
256226Seric xcommand(argv, forkflag, arg0)
257226Seric 	char **argv;
258226Seric 	bool forkflag;
259226Seric 	char *arg0;
260226Seric {
261226Seric 	register char **av;
262226Seric 	char *newargv[1000];
263226Seric 	register char **np;
264226Seric 
265226Seric 	np = newargv;
266226Seric 	for (av = &arg0; *av != NULL; av++)
267226Seric 		*np++ = *av;
268226Seric 	for (av = argv; *av != NULL; av++)
269226Seric 		*np++ = *av;
270226Seric 	*np = NULL;
271226Seric 	command(newargv, forkflag);
272226Seric }
273226Seric 
274200Seric callprog(progpath, flags, argv, forkflag)
275200Seric 	char *progpath;
276200Seric 	short flags;
277200Seric 	char **argv;
278200Seric 	bool forkflag;
279200Seric {
280200Seric 	register char *p;
281200Seric 	register char **av;
282200Seric 	extern char *makefile();
283200Seric 	register int i;
284201Seric 	auto int st;
285200Seric 
286200Seric 	if (*argv == NULL)
287200Seric 		return (-1);
288200Seric 
289157Seric 	/*
290226Seric 	**  Fork if appropriate.
291148Seric 	*/
292148Seric 
293200Seric 	if (forkflag)
294200Seric 	{
295393Seric # ifdef DEBUG
296393Seric 		if (Debug)
297393Seric 			printf("Forking\n");
298393Seric # endif
299200Seric 		i = fork();
300200Seric 		if (i < 0)
301200Seric 		{
302200Seric 			fprintf(stderr, "Sccs: cannot fork");
303200Seric 			exit(EX_OSERR);
304200Seric 		}
305200Seric 		else if (i > 0)
306201Seric 		{
307201Seric 			wait(&st);
308201Seric 			return (st);
309201Seric 		}
310200Seric 	}
311200Seric 
312200Seric 	/*
313226Seric 	**  Build new argument vector.
314226Seric 	*/
315226Seric 
316226Seric 	/* copy program filename arguments and flags */
317226Seric 	av = argv;
318226Seric 	while ((p = *++av) != NULL)
319226Seric 	{
320226Seric 		if (!bitset(NO_SDOT, flags) && *p != '-')
321226Seric 			*av = makefile(p);
322226Seric 	}
323226Seric 
324226Seric 	/*
325200Seric 	**  Set protection as appropriate.
326200Seric 	*/
327200Seric 
328200Seric 	if (bitset(REALUSER, flags))
329200Seric 		setuid(getuid());
330226Seric 
331200Seric 	/*
332226Seric 	**  Call real SCCS program.
333200Seric 	*/
334200Seric 
335226Seric 	execv(progpath, argv);
336148Seric 	fprintf(stderr, "Sccs: cannot execute ");
337200Seric 	perror(progpath);
338148Seric 	exit(EX_UNAVAILABLE);
339148Seric }
340148Seric 
341148Seric 
342148Seric char *
343148Seric makefile(name)
344148Seric 	char *name;
345148Seric {
346148Seric 	register char *p;
347148Seric 	register char c;
348148Seric 	char buf[512];
349148Seric 	struct stat stbuf;
350148Seric 	extern char *malloc();
351148Seric 
352148Seric 	/*
353148Seric 	**  See if this filename should be used as-is.
354148Seric 	**	There are three conditions where this can occur.
355148Seric 	**	1. The name already begins with "s.".
356148Seric 	**	2. The name has a "/" in it somewhere.
357148Seric 	**	3. The name references a directory.
358148Seric 	*/
359148Seric 
360261Seric 	if (strcmpn(name, "s.", 2) == 0)
361148Seric 		return (name);
362148Seric 	for (p = name; (c = *p) != '\0'; p++)
363148Seric 	{
364148Seric 		if (c == '/')
365148Seric 			return (name);
366148Seric 	}
367148Seric 	if (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR)
368148Seric 		return (name);
369148Seric 
370148Seric 	/*
371148Seric 	**  Prepend the path of the sccs file.
372148Seric 	*/
373148Seric 
374148Seric 	strcpy(buf, SccsPath);
375157Seric 	strcat(buf, "/s.");
376148Seric 	strcat(buf, name);
377148Seric 	p = malloc(strlen(buf) + 1);
378148Seric 	if (p == NULL)
379148Seric 	{
380148Seric 		perror("Sccs: no mem");
381148Seric 		exit(EX_OSERR);
382148Seric 	}
383148Seric 	strcpy(p, buf);
384148Seric 	return (p);
385148Seric }
386261Seric /*
387261Seric **  CLEAN -- clean out recreatable files
388261Seric **
389261Seric **	Any file for which an "s." file exists but no "p." file
390261Seric **	exists in the current directory is purged.
391261Seric **
392261Seric **	Parameters:
393346Seric **		really -- if TRUE, remove everything.
394346Seric **			else, just report status.
395261Seric **
396261Seric **	Returns:
397261Seric **		none.
398261Seric **
399261Seric **	Side Effects:
400261Seric **		removes files in the current directory.
401261Seric */
402261Seric 
403346Seric clean(really)
404346Seric 	bool really;
405261Seric {
406261Seric 	struct direct dir;
407261Seric 	struct stat stbuf;
408261Seric 	char buf[100];
409394Seric 	char pline[120];
410346Seric 	register FILE *dirfd;
411346Seric 	register char *basefile;
412351Seric 	bool gotedit;
413394Seric 	FILE *pfp;
414261Seric 
415261Seric 	dirfd = fopen(SccsPath, "r");
416261Seric 	if (dirfd == NULL)
417261Seric 	{
418261Seric 		fprintf(stderr, "Sccs: cannot open %s\n", SccsPath);
419261Seric 		return;
420261Seric 	}
421261Seric 
422261Seric 	/*
423261Seric 	**  Scan the SCCS directory looking for s. files.
424261Seric 	*/
425261Seric 
426351Seric 	gotedit = FALSE;
427261Seric 	while (fread(&dir, sizeof dir, 1, dirfd) != NULL)
428261Seric 	{
429261Seric 		if (dir.d_ino == 0 || strcmpn(dir.d_name, "s.", 2) != 0)
430261Seric 			continue;
431261Seric 
432261Seric 		/* got an s. file -- see if the p. file exists */
433261Seric 		strcpy(buf, SccsPath);
434261Seric 		strcat(buf, "/p.");
435346Seric 		basefile = &buf[strlen(buf)];
436394Seric 		strcpyn(basefile, &dir.d_name[2], sizeof dir.d_name - 2);
437346Seric 		basefile[sizeof dir.d_name - 2] = '\0';
438394Seric 		pfp = fopen(buf, "r");
439394Seric 		if (pfp != NULL)
440346Seric 		{
441394Seric 			while (fgets(pline, sizeof pline, pfp) != NULL)
442*416Seric 				printf("%12s: being edited: %s", basefile, pline);
443394Seric 			fclose(pfp);
444351Seric 			gotedit = TRUE;
445261Seric 			continue;
446346Seric 		}
447261Seric 
448261Seric 		/* the s. file exists and no p. file exists -- unlink the g-file */
449346Seric 		if (really)
450346Seric 		{
451346Seric 			strcpyn(buf, &dir.d_name[2], sizeof dir.d_name - 2);
452346Seric 			buf[sizeof dir.d_name - 2] = '\0';
453346Seric 			unlink(buf);
454346Seric 		}
455261Seric 	}
456261Seric 
457261Seric 	fclose(dirfd);
458351Seric 	if (!gotedit && !really)
459*416Seric 		printf("Nothing being edited\n");
460261Seric }
461396Seric /*
462396Seric **  UNEDIT -- unedit a file
463396Seric **
464396Seric **	Checks to see that the current user is actually editting
465396Seric **	the file and arranges that s/he is not editting it.
466396Seric **
467396Seric **	Parameters:
468*416Seric **		fn -- the name of the file to be unedited.
469396Seric **
470396Seric **	Returns:
471396Seric **		none.
472396Seric **
473396Seric **	Side Effects:
474396Seric **		fn is removed
475396Seric **		entries are removed from pfile.
476396Seric */
477396Seric 
478396Seric unedit(fn)
479396Seric 	char *fn;
480396Seric {
481396Seric 	register FILE *pfp;
482396Seric 	char *pfn;
483396Seric 	static char tfn[] = "/tmp/sccsXXXXX";
484396Seric 	FILE *tfp;
485396Seric 	register char *p;
486396Seric 	register char *q;
487396Seric 	bool delete = FALSE;
488396Seric 	bool others = FALSE;
489396Seric 	char *myname;
490396Seric 	extern char *getlogin();
491396Seric 	struct pfile *pent;
492396Seric 	extern struct pfile *getpfile();
493396Seric 	char buf[120];
494396Seric 
495396Seric 	/* make "s." filename & find the trailing component */
496396Seric 	pfn = makefile(fn);
497396Seric 	q = &pfn[strlen(pfn) - 1];
498396Seric 	while (q > pfn && *q != '/')
499396Seric 		q--;
500396Seric 	if (q <= pfn && (q[0] != 's' || q[1] != '.'))
501396Seric 	{
502396Seric 		fprintf(stderr, "Sccs: bad file name \"%s\"\n", fn);
503396Seric 		return;
504396Seric 	}
505396Seric 
506396Seric 	/* turn "s." into "p." */
507396Seric 	*++q = 'p';
508396Seric 
509396Seric 	pfp = fopen(pfn, "r");
510396Seric 	if (pfp == NULL)
511396Seric 	{
512*416Seric 		printf("%12s: not being edited\n", fn);
513396Seric 		return;
514396Seric 	}
515396Seric 
516396Seric 	/*
517396Seric 	**  Copy p-file to temp file, doing deletions as needed.
518396Seric 	*/
519396Seric 
520396Seric 	mktemp(tfn);
521396Seric 	tfp = fopen(tfn, "w");
522396Seric 	if (tfp == NULL)
523396Seric 	{
524396Seric 		fprintf(stderr, "Sccs: cannot create \"%s\"\n", tfn);
525396Seric 		exit(EX_OSERR);
526396Seric 	}
527396Seric 
528396Seric 	myname = getlogin();
529396Seric 	while ((pent = getpfile(pfp)) != NULL)
530396Seric 	{
531396Seric 		if (strcmp(pent->p_user, myname) == 0)
532396Seric 		{
533396Seric 			/* a match */
534396Seric 			delete++;
535396Seric 		}
536396Seric 		else
537396Seric 		{
538396Seric 			fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid,
539396Seric 			    pent->p_nsid, pent->p_user, pent->p_date,
540396Seric 			    pent->p_time);
541396Seric 			others++;
542396Seric 		}
543396Seric 	}
544396Seric 
545396Seric 	/* do final cleanup */
546396Seric 	if (others)
547396Seric 	{
548396Seric 		if (freopen(tfn, "r", tfp) == NULL)
549396Seric 		{
550396Seric 			fprintf(stderr, "Sccs: cannot reopen \"%s\"\n", tfn);
551396Seric 			exit(EX_OSERR);
552396Seric 		}
553396Seric 		if (freopen(pfn, "w", pfp) == NULL)
554396Seric 		{
555396Seric 			fprintf(stderr, "Sccs: cannot create \"%s\"\n", pfn);
556396Seric 			return;
557396Seric 		}
558396Seric 		while (fgets(buf, sizeof buf, tfp) != NULL)
559396Seric 			fputs(buf, pfp);
560396Seric 	}
561396Seric 	else
562396Seric 	{
563396Seric 		unlink(pfn);
564396Seric 	}
565396Seric 	fclose(tfp);
566396Seric 	fclose(pfp);
567396Seric 	unlink(tfn);
568396Seric 
569396Seric 	if (delete)
570396Seric 	{
571396Seric 		unlink(fn);
572396Seric 		printf("%12s: removed\n", fn);
573396Seric 	}
574396Seric 	else
575396Seric 	{
576*416Seric 		printf("%12s: not being edited by you\n", fn);
577396Seric 	}
578396Seric }
579396Seric /*
580396Seric **  GETPFILE -- get an entry from the p-file
581396Seric **
582396Seric **	Parameters:
583396Seric **		pfp -- p-file file pointer
584396Seric **
585396Seric **	Returns:
586396Seric **		pointer to p-file struct for next entry
587396Seric **		NULL on EOF or error
588396Seric **
589396Seric **	Side Effects:
590396Seric **		Each call wipes out results of previous call.
591396Seric */
592396Seric 
593396Seric struct pfile *
594396Seric getpfile(pfp)
595396Seric 	FILE *pfp;
596396Seric {
597396Seric 	static struct pfile ent;
598396Seric 	static char buf[120];
599396Seric 	register char *p;
600396Seric 	extern char *nextfield();
601396Seric 
602396Seric 	if (fgets(buf, sizeof buf, pfp) == NULL)
603396Seric 		return (NULL);
604396Seric 
605396Seric 	ent.p_osid = p = buf;
606396Seric 	ent.p_nsid = p = nextfield(p);
607396Seric 	ent.p_user = p = nextfield(p);
608396Seric 	ent.p_date = p = nextfield(p);
609396Seric 	ent.p_time = p = nextfield(p);
610396Seric 	if (p == NULL || nextfield(p) != NULL)
611396Seric 		return (NULL);
612396Seric 
613396Seric 	return (&ent);
614396Seric }
615396Seric 
616396Seric 
617396Seric char *
618396Seric nextfield(p)
619396Seric 	register char *p;
620396Seric {
621396Seric 	if (p == NULL || *p == '\0')
622396Seric 		return (NULL);
623396Seric 	while (*p != ' ' && *p != '\n' && *p != '\0')
624396Seric 		p++;
625396Seric 	if (*p == '\n' || *p == '\0')
626396Seric 	{
627396Seric 		*p = '\0';
628396Seric 		return (NULL);
629396Seric 	}
630396Seric 	*p++ = '\0';
631396Seric 	return (p);
632396Seric }
633