xref: /csrg-svn/usr.bin/sccs/sccs.c (revision 588)
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*588Seric static char SccsId[] = "@(#)sccs.c	1.24 08/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 */
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 */
76*588Seric char	*SccsDir = "";		/* directory to begin search from */
77157Seric bool	RealUser;		/* if set, running as real user */
78393Seric # ifdef DEBUG
79393Seric bool	Debug;			/* turn on tracing */
80393Seric # endif
81148Seric 
82148Seric main(argc, argv)
83148Seric 	int argc;
84148Seric 	char **argv;
85148Seric {
86148Seric 	register char *p;
87262Seric 	extern struct sccsprog *lookup();
88148Seric 
89148Seric 	/*
90148Seric 	**  Detect and decode flags intended for this program.
91148Seric 	*/
92148Seric 
93200Seric 	if (argc < 2)
94148Seric 	{
95200Seric 		fprintf(stderr, "Usage: sccs [flags] command [flags]\n");
96200Seric 		exit(EX_USAGE);
97200Seric 	}
98200Seric 	argv[argc] = NULL;
99200Seric 
100262Seric 	if (lookup(argv[0]) == NULL)
101200Seric 	{
102262Seric 		while ((p = *++argv) != NULL)
103148Seric 		{
104262Seric 			if (*p != '-')
105262Seric 				break;
106262Seric 			switch (*++p)
107262Seric 			{
108262Seric 			  case 'r':		/* run as real user */
109262Seric 				setuid(getuid());
110262Seric 				RealUser++;
111262Seric 				break;
112148Seric 
113262Seric 			  case 'p':		/* path of sccs files */
114262Seric 				SccsPath = ++p;
115262Seric 				break;
116148Seric 
117*588Seric 			  case 'd':		/* directory to search from */
118*588Seric 				SccsDir = ++p;
119*588Seric 				break;
120*588Seric 
121393Seric # ifdef DEBUG
122393Seric 			  case 'T':		/* trace */
123393Seric 				Debug++;
124393Seric 				break;
125393Seric # endif
126393Seric 
127262Seric 			  default:
128262Seric 				fprintf(stderr, "Sccs: unknown option -%s\n", p);
129262Seric 				break;
130262Seric 			}
131148Seric 		}
132262Seric 		if (SccsPath[0] == '\0')
133262Seric 			SccsPath = ".";
134148Seric 	}
135148Seric 
136201Seric 	command(argv, FALSE);
137200Seric 	exit(EX_OK);
138200Seric }
139157Seric 
140201Seric command(argv, forkflag)
141200Seric 	char **argv;
142201Seric 	bool forkflag;
143200Seric {
144200Seric 	register struct sccsprog *cmd;
145200Seric 	register char *p;
146201Seric 	register char *q;
147201Seric 	char buf[40];
148262Seric 	extern struct sccsprog *lookup();
149585Seric 	char *nav[200];
150393Seric 	char **avp;
151585Seric 	register int i;
152585Seric 	extern bool unedit();
153200Seric 
154393Seric # ifdef DEBUG
155393Seric 	if (Debug)
156393Seric 	{
157393Seric 		printf("command:\n");
158393Seric 		for (avp = argv; *avp != NULL; avp++)
159393Seric 			printf("    \"%s\"\n", *avp);
160393Seric 	}
161393Seric # endif
162393Seric 
163157Seric 	/*
164148Seric 	**  Look up command.
165200Seric 	**	At this point, argv points to the command name.
166148Seric 	*/
167148Seric 
168396Seric 	cmd = lookup(argv[0]);
169262Seric 	if (cmd == NULL)
170148Seric 	{
171396Seric 		fprintf(stderr, "Sccs: Unknown command \"%s\"\n", argv[0]);
172148Seric 		exit(EX_USAGE);
173148Seric 	}
174148Seric 
175148Seric 	/*
176200Seric 	**  Interpret operation associated with this command.
177157Seric 	*/
178157Seric 
179200Seric 	switch (cmd->sccsoper)
180200Seric 	{
181200Seric 	  case PROG:		/* call an sccs prog */
182201Seric 		callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag);
183201Seric 		break;
184201Seric 
185201Seric 	  case CMACRO:		/* command macro */
186201Seric 		for (p = cmd->sccspath; *p != '\0'; p++)
187201Seric 		{
188393Seric 			avp = nav;
189393Seric 			*avp++ = buf;
190201Seric 			for (q = buf; *p != '/' && *p != '\0'; p++, q++)
191393Seric 			{
192393Seric 				if (*p == ' ')
193393Seric 				{
194393Seric 					*q = '\0';
195393Seric 					*avp++ = &q[1];
196393Seric 				}
197393Seric 				else
198393Seric 					*q = *p;
199393Seric 			}
200201Seric 			*q = '\0';
201393Seric 			*avp = NULL;
202393Seric 			xcommand(&argv[1], *p != '\0', nav[0], nav[1], nav[2],
203393Seric 				 nav[3], nav[4], nav[5], nav[6]);
204201Seric 		}
205201Seric 		fprintf(stderr, "Sccs internal error: CMACRO\n");
206200Seric 		exit(EX_SOFTWARE);
207157Seric 
208226Seric 	  case FIX:		/* fix a delta */
209568Seric 		if (strncmp(argv[1], "-r", 2) != 0)
210226Seric 		{
211226Seric 			fprintf(stderr, "Sccs: -r flag needed for fix command\n");
212226Seric 			break;
213226Seric 		}
214226Seric 		xcommand(&argv[1], TRUE, "get", "-k", NULL);
215226Seric 		xcommand(&argv[1], TRUE, "rmdel", NULL);
216226Seric 		xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL);
217226Seric 		fprintf(stderr, "Sccs internal error: FIX\n");
218226Seric 		exit(EX_SOFTWARE);
219226Seric 
220261Seric 	  case CLEAN:
221346Seric 		clean((bool) cmd->sccspath);
222261Seric 		break;
223261Seric 
224396Seric 	  case UNEDIT:
225585Seric 		i = 0;
226396Seric 		for (avp = &argv[1]; *avp != NULL; avp++)
227585Seric 		{
228585Seric 			if (unedit(*avp))
229585Seric 				nav[i++] = *avp;
230585Seric 		}
231585Seric 		nav[i] = NULL;
232585Seric 		if (i > 0)
233585Seric 			xcommand(nav, FALSE, "get", NULL);
234396Seric 		break;
235396Seric 
236200Seric 	  default:
237200Seric 		fprintf(stderr, "Sccs internal error: oper %d\n", cmd->sccsoper);
238200Seric 		exit(EX_SOFTWARE);
239200Seric 	}
240200Seric }
241262Seric /*
242262Seric **  LOOKUP -- look up an SCCS command name.
243262Seric **
244262Seric **	Parameters:
245262Seric **		name -- the name of the command to look up.
246262Seric **
247262Seric **	Returns:
248262Seric **		ptr to command descriptor for this command.
249262Seric **		NULL if no such entry.
250262Seric **
251262Seric **	Side Effects:
252262Seric **		none.
253262Seric */
254200Seric 
255262Seric struct sccsprog *
256262Seric lookup(name)
257262Seric 	char *name;
258262Seric {
259262Seric 	register struct sccsprog *cmd;
260226Seric 
261262Seric 	for (cmd = SccsProg; cmd->sccsname != NULL; cmd++)
262262Seric 	{
263262Seric 		if (strcmp(cmd->sccsname, name) == 0)
264262Seric 			return (cmd);
265262Seric 	}
266262Seric 	return (NULL);
267262Seric }
268262Seric 
269262Seric 
270226Seric xcommand(argv, forkflag, arg0)
271226Seric 	char **argv;
272226Seric 	bool forkflag;
273226Seric 	char *arg0;
274226Seric {
275226Seric 	register char **av;
276226Seric 	char *newargv[1000];
277226Seric 	register char **np;
278226Seric 
279226Seric 	np = newargv;
280226Seric 	for (av = &arg0; *av != NULL; av++)
281226Seric 		*np++ = *av;
282226Seric 	for (av = argv; *av != NULL; av++)
283226Seric 		*np++ = *av;
284226Seric 	*np = NULL;
285226Seric 	command(newargv, forkflag);
286226Seric }
287226Seric 
288200Seric callprog(progpath, flags, argv, forkflag)
289200Seric 	char *progpath;
290200Seric 	short flags;
291200Seric 	char **argv;
292200Seric 	bool forkflag;
293200Seric {
294200Seric 	register char *p;
295200Seric 	register char **av;
296200Seric 	extern char *makefile();
297200Seric 	register int i;
298201Seric 	auto int st;
299586Seric 	register char **nav;
300200Seric 
301200Seric 	if (*argv == NULL)
302200Seric 		return (-1);
303200Seric 
304157Seric 	/*
305226Seric 	**  Fork if appropriate.
306148Seric 	*/
307148Seric 
308200Seric 	if (forkflag)
309200Seric 	{
310393Seric # ifdef DEBUG
311393Seric 		if (Debug)
312393Seric 			printf("Forking\n");
313393Seric # endif
314200Seric 		i = fork();
315200Seric 		if (i < 0)
316200Seric 		{
317200Seric 			fprintf(stderr, "Sccs: cannot fork");
318200Seric 			exit(EX_OSERR);
319200Seric 		}
320200Seric 		else if (i > 0)
321201Seric 		{
322201Seric 			wait(&st);
323201Seric 			return (st);
324201Seric 		}
325200Seric 	}
326200Seric 
327200Seric 	/*
328226Seric 	**  Build new argument vector.
329226Seric 	*/
330226Seric 
331226Seric 	/* copy program filename arguments and flags */
332586Seric 	nav = &argv[1];
333226Seric 	av = argv;
334226Seric 	while ((p = *++av) != NULL)
335226Seric 	{
336226Seric 		if (!bitset(NO_SDOT, flags) && *p != '-')
337586Seric 			*nav = makefile(p);
338586Seric 		else
339586Seric 			*nav = p;
340586Seric 		if (*nav != NULL)
341586Seric 			nav++;
342226Seric 	}
343586Seric 	*nav = NULL;
344226Seric 
345226Seric 	/*
346200Seric 	**  Set protection as appropriate.
347200Seric 	*/
348200Seric 
349200Seric 	if (bitset(REALUSER, flags))
350200Seric 		setuid(getuid());
351226Seric 
352200Seric 	/*
353226Seric 	**  Call real SCCS program.
354200Seric 	*/
355200Seric 
356226Seric 	execv(progpath, argv);
357148Seric 	fprintf(stderr, "Sccs: cannot execute ");
358200Seric 	perror(progpath);
359148Seric 	exit(EX_UNAVAILABLE);
360148Seric }
361586Seric /*
362586Seric **  MAKEFILE -- make filename of SCCS file
363586Seric **
364586Seric **	If the name passed is already the name of an SCCS file,
365586Seric **	just return it.  Otherwise, munge the name into the name
366586Seric **	of the actual SCCS file.
367586Seric **
368586Seric **	There are cases when it is not clear what you want to
369586Seric **	do.  For example, if SccsPath is an absolute pathname
370586Seric **	and the name given is also an absolute pathname, we go
371586Seric **	for SccsPath (& only use the last component of the name
372586Seric **	passed) -- this is important for security reasons (if
373586Seric **	sccs is being used as a setuid front end), but not
374586Seric **	particularly intuitive.
375586Seric **
376586Seric **	Parameters:
377586Seric **		name -- the file name to be munged.
378586Seric **
379586Seric **	Returns:
380586Seric **		The pathname of the sccs file.
381586Seric **		NULL on error.
382586Seric **
383586Seric **	Side Effects:
384586Seric **		none.
385586Seric */
386148Seric 
387148Seric char *
388148Seric makefile(name)
389148Seric 	char *name;
390148Seric {
391148Seric 	register char *p;
392148Seric 	register char c;
393148Seric 	char buf[512];
394*588Seric 	struct stat stbuf;
395148Seric 	extern char *malloc();
396586Seric 	extern char *rindex();
397*588Seric 	extern bool safepath();
398587Seric 	extern bool isdir();
399587Seric 	register char *q;
400148Seric 
401586Seric 	p = rindex(name, '/');
402586Seric 	if (p == NULL)
403586Seric 		p = name;
404586Seric 	else
405586Seric 		p++;
406586Seric 
407148Seric 	/*
408*588Seric 	**  Check to see that the path is "safe", i.e., that we
409*588Seric 	**  are not letting some nasty person use the setuid part
410*588Seric 	**  of this program to look at or munge some presumably
411*588Seric 	**  hidden files.
412148Seric 	*/
413148Seric 
414*588Seric 	if (SccsDir[0] == '/' && !safepath(name))
415*588Seric 		return (NULL);
416586Seric 
417586Seric 	/*
418*588Seric 	**  Create the base pathname.
419586Seric 	*/
420586Seric 
421*588Seric 	if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0)
422148Seric 	{
423*588Seric 		strcpy(buf, SccsDir);
424586Seric 		strcat(buf, "/");
425586Seric 	}
426586Seric 	else
427586Seric 		strcpy(buf, "");
428587Seric 	strncat(buf, name, p - name);
429587Seric 	q = &buf[strlen(buf)];
430587Seric 	strcpy(q, p);
431587Seric 	if (strncmp(p, "s.", 2) != 0 && !isdir(buf))
432586Seric 	{
433*588Seric 		strcpy(q, SccsPath);
434*588Seric 		strcat(buf, "/s.");
435586Seric 		strcat(buf, p);
436586Seric 	}
437148Seric 
438*588Seric 	if (strcmp(buf, name) == 0)
439*588Seric 		p = name;
440*588Seric 	else
441148Seric 	{
442*588Seric 		p = malloc(strlen(buf) + 1);
443*588Seric 		if (p == NULL)
444*588Seric 		{
445*588Seric 			perror("Sccs: no mem");
446*588Seric 			exit(EX_OSERR);
447*588Seric 		}
448*588Seric 		strcpy(p, buf);
449148Seric 	}
450148Seric 	return (p);
451148Seric }
452261Seric /*
453587Seric **  ISDIR -- return true if the argument is a directory.
454587Seric **
455587Seric **	Parameters:
456587Seric **		name -- the pathname of the file to check.
457587Seric **
458587Seric **	Returns:
459587Seric **		TRUE if 'name' is a directory, FALSE otherwise.
460587Seric **
461587Seric **	Side Effects:
462587Seric **		none.
463587Seric */
464587Seric 
465587Seric bool
466587Seric isdir(name)
467587Seric 	char *name;
468587Seric {
469587Seric 	struct stat stbuf;
470587Seric 
471587Seric 	return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR);
472587Seric }
473587Seric /*
474586Seric **  SAFEPATH -- determine whether a pathname is "safe"
475586Seric **
476586Seric **	"Safe" pathnames only allow you to get deeper into the
477586Seric **	directory structure, i.e., full pathnames and ".." are
478586Seric **	not allowed.
479586Seric **
480586Seric **	Parameters:
481586Seric **		p -- the name to check.
482586Seric **
483586Seric **	Returns:
484586Seric **		TRUE -- if the path is safe.
485586Seric **		FALSE -- if the path is not safe.
486586Seric **
487586Seric **	Side Effects:
488586Seric **		Prints a message if the path is not safe.
489586Seric */
490586Seric 
491586Seric bool
492586Seric safepath(p)
493586Seric 	register char *p;
494586Seric {
495586Seric 	extern char *index();
496586Seric 
497586Seric 	if (*p != '/')
498586Seric 	{
499586Seric 		while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0)
500586Seric 		{
501586Seric 			p = index(p, '/');
502586Seric 			if (p == NULL)
503586Seric 				return (TRUE);
504586Seric 			p++;
505586Seric 		}
506586Seric 	}
507586Seric 
508586Seric 	printf("You may not use full pathnames or \"..\"\n");
509586Seric 	return (FALSE);
510586Seric }
511586Seric /*
512261Seric **  CLEAN -- clean out recreatable files
513261Seric **
514261Seric **	Any file for which an "s." file exists but no "p." file
515261Seric **	exists in the current directory is purged.
516261Seric **
517261Seric **	Parameters:
518346Seric **		really -- if TRUE, remove everything.
519346Seric **			else, just report status.
520261Seric **
521261Seric **	Returns:
522261Seric **		none.
523261Seric **
524261Seric **	Side Effects:
525261Seric **		removes files in the current directory.
526261Seric */
527261Seric 
528346Seric clean(really)
529346Seric 	bool really;
530261Seric {
531261Seric 	struct direct dir;
532261Seric 	struct stat stbuf;
533261Seric 	char buf[100];
534394Seric 	char pline[120];
535346Seric 	register FILE *dirfd;
536346Seric 	register char *basefile;
537351Seric 	bool gotedit;
538394Seric 	FILE *pfp;
539261Seric 
540261Seric 	dirfd = fopen(SccsPath, "r");
541261Seric 	if (dirfd == NULL)
542261Seric 	{
543261Seric 		fprintf(stderr, "Sccs: cannot open %s\n", SccsPath);
544261Seric 		return;
545261Seric 	}
546261Seric 
547261Seric 	/*
548261Seric 	**  Scan the SCCS directory looking for s. files.
549261Seric 	*/
550261Seric 
551351Seric 	gotedit = FALSE;
552261Seric 	while (fread(&dir, sizeof dir, 1, dirfd) != NULL)
553261Seric 	{
554568Seric 		if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0)
555261Seric 			continue;
556261Seric 
557261Seric 		/* got an s. file -- see if the p. file exists */
558261Seric 		strcpy(buf, SccsPath);
559261Seric 		strcat(buf, "/p.");
560346Seric 		basefile = &buf[strlen(buf)];
561568Seric 		strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2);
562346Seric 		basefile[sizeof dir.d_name - 2] = '\0';
563394Seric 		pfp = fopen(buf, "r");
564394Seric 		if (pfp != NULL)
565346Seric 		{
566394Seric 			while (fgets(pline, sizeof pline, pfp) != NULL)
567416Seric 				printf("%12s: being edited: %s", basefile, pline);
568394Seric 			fclose(pfp);
569351Seric 			gotedit = TRUE;
570261Seric 			continue;
571346Seric 		}
572261Seric 
573261Seric 		/* the s. file exists and no p. file exists -- unlink the g-file */
574346Seric 		if (really)
575346Seric 		{
576568Seric 			strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2);
577346Seric 			buf[sizeof dir.d_name - 2] = '\0';
578346Seric 			unlink(buf);
579346Seric 		}
580261Seric 	}
581261Seric 
582261Seric 	fclose(dirfd);
583351Seric 	if (!gotedit && !really)
584416Seric 		printf("Nothing being edited\n");
585261Seric }
586396Seric /*
587396Seric **  UNEDIT -- unedit a file
588396Seric **
589396Seric **	Checks to see that the current user is actually editting
590396Seric **	the file and arranges that s/he is not editting it.
591396Seric **
592396Seric **	Parameters:
593416Seric **		fn -- the name of the file to be unedited.
594396Seric **
595396Seric **	Returns:
596585Seric **		TRUE -- if the file was successfully unedited.
597585Seric **		FALSE -- if the file was not unedited for some
598585Seric **			reason.
599396Seric **
600396Seric **	Side Effects:
601396Seric **		fn is removed
602396Seric **		entries are removed from pfile.
603396Seric */
604396Seric 
605585Seric bool
606396Seric unedit(fn)
607396Seric 	char *fn;
608396Seric {
609396Seric 	register FILE *pfp;
610396Seric 	char *pfn;
611396Seric 	static char tfn[] = "/tmp/sccsXXXXX";
612396Seric 	FILE *tfp;
613396Seric 	register char *p;
614396Seric 	register char *q;
615396Seric 	bool delete = FALSE;
616396Seric 	bool others = FALSE;
617396Seric 	char *myname;
618396Seric 	extern char *getlogin();
619396Seric 	struct pfile *pent;
620396Seric 	extern struct pfile *getpfile();
621396Seric 	char buf[120];
622396Seric 
623396Seric 	/* make "s." filename & find the trailing component */
624396Seric 	pfn = makefile(fn);
625586Seric 	if (pfn == NULL)
626586Seric 		return (FALSE);
627586Seric 	q = rindex(pfn, '/');
628586Seric 	if (q == NULL)
629586Seric 		q = &pfn[-1];
630586Seric 	if (q[1] != 's' || q[2] != '.')
631396Seric 	{
632396Seric 		fprintf(stderr, "Sccs: bad file name \"%s\"\n", fn);
633585Seric 		return (FALSE);
634396Seric 	}
635396Seric 
636396Seric 	/* turn "s." into "p." */
637396Seric 	*++q = 'p';
638396Seric 
639396Seric 	pfp = fopen(pfn, "r");
640396Seric 	if (pfp == NULL)
641396Seric 	{
642416Seric 		printf("%12s: not being edited\n", fn);
643585Seric 		return (FALSE);
644396Seric 	}
645396Seric 
646396Seric 	/*
647396Seric 	**  Copy p-file to temp file, doing deletions as needed.
648396Seric 	*/
649396Seric 
650396Seric 	mktemp(tfn);
651396Seric 	tfp = fopen(tfn, "w");
652396Seric 	if (tfp == NULL)
653396Seric 	{
654396Seric 		fprintf(stderr, "Sccs: cannot create \"%s\"\n", tfn);
655396Seric 		exit(EX_OSERR);
656396Seric 	}
657396Seric 
658396Seric 	myname = getlogin();
659396Seric 	while ((pent = getpfile(pfp)) != NULL)
660396Seric 	{
661396Seric 		if (strcmp(pent->p_user, myname) == 0)
662396Seric 		{
663396Seric 			/* a match */
664396Seric 			delete++;
665396Seric 		}
666396Seric 		else
667396Seric 		{
668396Seric 			fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid,
669396Seric 			    pent->p_nsid, pent->p_user, pent->p_date,
670396Seric 			    pent->p_time);
671396Seric 			others++;
672396Seric 		}
673396Seric 	}
674396Seric 
675396Seric 	/* do final cleanup */
676396Seric 	if (others)
677396Seric 	{
678396Seric 		if (freopen(tfn, "r", tfp) == NULL)
679396Seric 		{
680396Seric 			fprintf(stderr, "Sccs: cannot reopen \"%s\"\n", tfn);
681396Seric 			exit(EX_OSERR);
682396Seric 		}
683396Seric 		if (freopen(pfn, "w", pfp) == NULL)
684396Seric 		{
685396Seric 			fprintf(stderr, "Sccs: cannot create \"%s\"\n", pfn);
686585Seric 			return (FALSE);
687396Seric 		}
688396Seric 		while (fgets(buf, sizeof buf, tfp) != NULL)
689396Seric 			fputs(buf, pfp);
690396Seric 	}
691396Seric 	else
692396Seric 	{
693396Seric 		unlink(pfn);
694396Seric 	}
695396Seric 	fclose(tfp);
696396Seric 	fclose(pfp);
697396Seric 	unlink(tfn);
698396Seric 
699396Seric 	if (delete)
700396Seric 	{
701396Seric 		unlink(fn);
702396Seric 		printf("%12s: removed\n", fn);
703585Seric 		return (TRUE);
704396Seric 	}
705396Seric 	else
706396Seric 	{
707416Seric 		printf("%12s: not being edited by you\n", fn);
708585Seric 		return (FALSE);
709396Seric 	}
710396Seric }
711396Seric /*
712396Seric **  GETPFILE -- get an entry from the p-file
713396Seric **
714396Seric **	Parameters:
715396Seric **		pfp -- p-file file pointer
716396Seric **
717396Seric **	Returns:
718396Seric **		pointer to p-file struct for next entry
719396Seric **		NULL on EOF or error
720396Seric **
721396Seric **	Side Effects:
722396Seric **		Each call wipes out results of previous call.
723396Seric */
724396Seric 
725396Seric struct pfile *
726396Seric getpfile(pfp)
727396Seric 	FILE *pfp;
728396Seric {
729396Seric 	static struct pfile ent;
730396Seric 	static char buf[120];
731396Seric 	register char *p;
732396Seric 	extern char *nextfield();
733396Seric 
734396Seric 	if (fgets(buf, sizeof buf, pfp) == NULL)
735396Seric 		return (NULL);
736396Seric 
737396Seric 	ent.p_osid = p = buf;
738396Seric 	ent.p_nsid = p = nextfield(p);
739396Seric 	ent.p_user = p = nextfield(p);
740396Seric 	ent.p_date = p = nextfield(p);
741396Seric 	ent.p_time = p = nextfield(p);
742396Seric 	if (p == NULL || nextfield(p) != NULL)
743396Seric 		return (NULL);
744396Seric 
745396Seric 	return (&ent);
746396Seric }
747396Seric 
748396Seric 
749396Seric char *
750396Seric nextfield(p)
751396Seric 	register char *p;
752396Seric {
753396Seric 	if (p == NULL || *p == '\0')
754396Seric 		return (NULL);
755396Seric 	while (*p != ' ' && *p != '\n' && *p != '\0')
756396Seric 		p++;
757396Seric 	if (*p == '\n' || *p == '\0')
758396Seric 	{
759396Seric 		*p = '\0';
760396Seric 		return (NULL);
761396Seric 	}
762396Seric 	*p++ = '\0';
763396Seric 	return (p);
764396Seric }
765