xref: /csrg-svn/usr.bin/sccs/sccs.c (revision 587)
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*587Seric static char SccsId[] = "@(#)sccs.c	1.23.1.1 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 */
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();
144585Seric 	char *nav[200];
145393Seric 	char **avp;
146585Seric 	register int i;
147585Seric 	extern bool unedit();
148200Seric 
149393Seric # ifdef DEBUG
150393Seric 	if (Debug)
151393Seric 	{
152393Seric 		printf("command:\n");
153393Seric 		for (avp = argv; *avp != NULL; avp++)
154393Seric 			printf("    \"%s\"\n", *avp);
155393Seric 	}
156393Seric # endif
157393Seric 
158157Seric 	/*
159148Seric 	**  Look up command.
160200Seric 	**	At this point, argv points to the command name.
161148Seric 	*/
162148Seric 
163396Seric 	cmd = lookup(argv[0]);
164262Seric 	if (cmd == NULL)
165148Seric 	{
166396Seric 		fprintf(stderr, "Sccs: Unknown command \"%s\"\n", argv[0]);
167148Seric 		exit(EX_USAGE);
168148Seric 	}
169148Seric 
170148Seric 	/*
171200Seric 	**  Interpret operation associated with this command.
172157Seric 	*/
173157Seric 
174200Seric 	switch (cmd->sccsoper)
175200Seric 	{
176200Seric 	  case PROG:		/* call an sccs prog */
177201Seric 		callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag);
178201Seric 		break;
179201Seric 
180201Seric 	  case CMACRO:		/* command macro */
181201Seric 		for (p = cmd->sccspath; *p != '\0'; p++)
182201Seric 		{
183393Seric 			avp = nav;
184393Seric 			*avp++ = buf;
185201Seric 			for (q = buf; *p != '/' && *p != '\0'; p++, q++)
186393Seric 			{
187393Seric 				if (*p == ' ')
188393Seric 				{
189393Seric 					*q = '\0';
190393Seric 					*avp++ = &q[1];
191393Seric 				}
192393Seric 				else
193393Seric 					*q = *p;
194393Seric 			}
195201Seric 			*q = '\0';
196393Seric 			*avp = NULL;
197393Seric 			xcommand(&argv[1], *p != '\0', nav[0], nav[1], nav[2],
198393Seric 				 nav[3], nav[4], nav[5], nav[6]);
199201Seric 		}
200201Seric 		fprintf(stderr, "Sccs internal error: CMACRO\n");
201200Seric 		exit(EX_SOFTWARE);
202157Seric 
203226Seric 	  case FIX:		/* fix a delta */
204568Seric 		if (strncmp(argv[1], "-r", 2) != 0)
205226Seric 		{
206226Seric 			fprintf(stderr, "Sccs: -r flag needed for fix command\n");
207226Seric 			break;
208226Seric 		}
209226Seric 		xcommand(&argv[1], TRUE, "get", "-k", NULL);
210226Seric 		xcommand(&argv[1], TRUE, "rmdel", NULL);
211226Seric 		xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL);
212226Seric 		fprintf(stderr, "Sccs internal error: FIX\n");
213226Seric 		exit(EX_SOFTWARE);
214226Seric 
215261Seric 	  case CLEAN:
216346Seric 		clean((bool) cmd->sccspath);
217261Seric 		break;
218261Seric 
219396Seric 	  case UNEDIT:
220585Seric 		i = 0;
221396Seric 		for (avp = &argv[1]; *avp != NULL; avp++)
222585Seric 		{
223585Seric 			if (unedit(*avp))
224585Seric 				nav[i++] = *avp;
225585Seric 		}
226585Seric 		nav[i] = NULL;
227585Seric 		if (i > 0)
228585Seric 			xcommand(nav, FALSE, "get", NULL);
229396Seric 		break;
230396Seric 
231200Seric 	  default:
232200Seric 		fprintf(stderr, "Sccs internal error: oper %d\n", cmd->sccsoper);
233200Seric 		exit(EX_SOFTWARE);
234200Seric 	}
235200Seric }
236262Seric /*
237262Seric **  LOOKUP -- look up an SCCS command name.
238262Seric **
239262Seric **	Parameters:
240262Seric **		name -- the name of the command to look up.
241262Seric **
242262Seric **	Returns:
243262Seric **		ptr to command descriptor for this command.
244262Seric **		NULL if no such entry.
245262Seric **
246262Seric **	Side Effects:
247262Seric **		none.
248262Seric */
249200Seric 
250262Seric struct sccsprog *
251262Seric lookup(name)
252262Seric 	char *name;
253262Seric {
254262Seric 	register struct sccsprog *cmd;
255226Seric 
256262Seric 	for (cmd = SccsProg; cmd->sccsname != NULL; cmd++)
257262Seric 	{
258262Seric 		if (strcmp(cmd->sccsname, name) == 0)
259262Seric 			return (cmd);
260262Seric 	}
261262Seric 	return (NULL);
262262Seric }
263262Seric 
264262Seric 
265226Seric xcommand(argv, forkflag, arg0)
266226Seric 	char **argv;
267226Seric 	bool forkflag;
268226Seric 	char *arg0;
269226Seric {
270226Seric 	register char **av;
271226Seric 	char *newargv[1000];
272226Seric 	register char **np;
273226Seric 
274226Seric 	np = newargv;
275226Seric 	for (av = &arg0; *av != NULL; av++)
276226Seric 		*np++ = *av;
277226Seric 	for (av = argv; *av != NULL; av++)
278226Seric 		*np++ = *av;
279226Seric 	*np = NULL;
280226Seric 	command(newargv, forkflag);
281226Seric }
282226Seric 
283200Seric callprog(progpath, flags, argv, forkflag)
284200Seric 	char *progpath;
285200Seric 	short flags;
286200Seric 	char **argv;
287200Seric 	bool forkflag;
288200Seric {
289200Seric 	register char *p;
290200Seric 	register char **av;
291200Seric 	extern char *makefile();
292200Seric 	register int i;
293201Seric 	auto int st;
294586Seric 	register char **nav;
295200Seric 
296200Seric 	if (*argv == NULL)
297200Seric 		return (-1);
298200Seric 
299157Seric 	/*
300226Seric 	**  Fork if appropriate.
301148Seric 	*/
302148Seric 
303200Seric 	if (forkflag)
304200Seric 	{
305393Seric # ifdef DEBUG
306393Seric 		if (Debug)
307393Seric 			printf("Forking\n");
308393Seric # endif
309200Seric 		i = fork();
310200Seric 		if (i < 0)
311200Seric 		{
312200Seric 			fprintf(stderr, "Sccs: cannot fork");
313200Seric 			exit(EX_OSERR);
314200Seric 		}
315200Seric 		else if (i > 0)
316201Seric 		{
317201Seric 			wait(&st);
318201Seric 			return (st);
319201Seric 		}
320200Seric 	}
321200Seric 
322200Seric 	/*
323226Seric 	**  Build new argument vector.
324226Seric 	*/
325226Seric 
326226Seric 	/* copy program filename arguments and flags */
327586Seric 	nav = &argv[1];
328226Seric 	av = argv;
329226Seric 	while ((p = *++av) != NULL)
330226Seric 	{
331226Seric 		if (!bitset(NO_SDOT, flags) && *p != '-')
332586Seric 			*nav = makefile(p);
333586Seric 		else
334586Seric 			*nav = p;
335586Seric 		if (*nav != NULL)
336586Seric 			nav++;
337226Seric 	}
338586Seric 	*nav = NULL;
339226Seric 
340226Seric 	/*
341200Seric 	**  Set protection as appropriate.
342200Seric 	*/
343200Seric 
344200Seric 	if (bitset(REALUSER, flags))
345200Seric 		setuid(getuid());
346226Seric 
347200Seric 	/*
348226Seric 	**  Call real SCCS program.
349200Seric 	*/
350200Seric 
351226Seric 	execv(progpath, argv);
352148Seric 	fprintf(stderr, "Sccs: cannot execute ");
353200Seric 	perror(progpath);
354148Seric 	exit(EX_UNAVAILABLE);
355148Seric }
356586Seric /*
357586Seric **  MAKEFILE -- make filename of SCCS file
358586Seric **
359586Seric **	If the name passed is already the name of an SCCS file,
360586Seric **	just return it.  Otherwise, munge the name into the name
361586Seric **	of the actual SCCS file.
362586Seric **
363586Seric **	There are cases when it is not clear what you want to
364586Seric **	do.  For example, if SccsPath is an absolute pathname
365586Seric **	and the name given is also an absolute pathname, we go
366586Seric **	for SccsPath (& only use the last component of the name
367586Seric **	passed) -- this is important for security reasons (if
368586Seric **	sccs is being used as a setuid front end), but not
369586Seric **	particularly intuitive.
370586Seric **
371586Seric **	Parameters:
372586Seric **		name -- the file name to be munged.
373586Seric **
374586Seric **	Returns:
375586Seric **		The pathname of the sccs file.
376586Seric **		NULL on error.
377586Seric **
378586Seric **	Side Effects:
379586Seric **		none.
380586Seric */
381148Seric 
382148Seric char *
383148Seric makefile(name)
384148Seric 	char *name;
385148Seric {
386148Seric 	register char *p;
387148Seric 	register char c;
388148Seric 	char buf[512];
389148Seric 	extern char *malloc();
390586Seric 	extern char *rindex();
391*587Seric 	extern bool isdir();
392*587Seric 	register char *q;
393148Seric 
394586Seric 	p = rindex(name, '/');
395586Seric 	if (p == NULL)
396586Seric 		p = name;
397586Seric 	else
398586Seric 		p++;
399586Seric 
400148Seric 	/*
401*587Seric 	**  See if the name can be used as-is.
402148Seric 	*/
403148Seric 
404*587Seric 	if (SccsPath[0] != '/' || name[0] == '/' || strncmp(name, "./", 2) == 0)
405*587Seric 	{
406*587Seric 		if (strncmp(p, "s.", 2) == 0)
407*587Seric 			return (name);
408*587Seric 		if (isdir(name))
409*587Seric 			return (name);
410*587Seric 	}
411586Seric 
412586Seric 	/*
413*587Seric 	**  Create the actual pathname.
414586Seric 	*/
415586Seric 
416*587Seric 	if (name[0] != '/')
417148Seric 	{
418586Seric 		strcpy(buf, SccsPath);
419586Seric 		strcat(buf, "/");
420586Seric 	}
421586Seric 	else
422586Seric 		strcpy(buf, "");
423*587Seric 	strncat(buf, name, p - name);
424*587Seric 	q = &buf[strlen(buf)];
425*587Seric 	strcpy(q, p);
426*587Seric 	if (strncmp(p, "s.", 2) != 0 && !isdir(buf))
427586Seric 	{
428*587Seric 		strcpy(q, "");
429*587Seric 		if (SccsPath[0] != '/' && name[0] == '/')
430586Seric 		{
431586Seric 			strcat(buf, SccsPath);
432586Seric 			strcat(buf, "/");
433586Seric 		}
434586Seric 		strcat(buf, "s.");
435586Seric 		strcat(buf, p);
436586Seric 	}
437148Seric 
438148Seric 	p = malloc(strlen(buf) + 1);
439148Seric 	if (p == NULL)
440148Seric 	{
441148Seric 		perror("Sccs: no mem");
442148Seric 		exit(EX_OSERR);
443148Seric 	}
444148Seric 	strcpy(p, buf);
445148Seric 	return (p);
446148Seric }
447261Seric /*
448*587Seric **  ISDIR -- return true if the argument is a directory.
449*587Seric **
450*587Seric **	Parameters:
451*587Seric **		name -- the pathname of the file to check.
452*587Seric **
453*587Seric **	Returns:
454*587Seric **		TRUE if 'name' is a directory, FALSE otherwise.
455*587Seric **
456*587Seric **	Side Effects:
457*587Seric **		none.
458*587Seric */
459*587Seric 
460*587Seric bool
461*587Seric isdir(name)
462*587Seric 	char *name;
463*587Seric {
464*587Seric 	struct stat stbuf;
465*587Seric 
466*587Seric 	return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR);
467*587Seric }
468*587Seric /*
469586Seric **  SAFEPATH -- determine whether a pathname is "safe"
470586Seric **
471586Seric **	"Safe" pathnames only allow you to get deeper into the
472586Seric **	directory structure, i.e., full pathnames and ".." are
473586Seric **	not allowed.
474586Seric **
475586Seric **	Parameters:
476586Seric **		p -- the name to check.
477586Seric **
478586Seric **	Returns:
479586Seric **		TRUE -- if the path is safe.
480586Seric **		FALSE -- if the path is not safe.
481586Seric **
482586Seric **	Side Effects:
483586Seric **		Prints a message if the path is not safe.
484586Seric */
485586Seric 
486586Seric bool
487586Seric safepath(p)
488586Seric 	register char *p;
489586Seric {
490586Seric 	extern char *index();
491586Seric 
492586Seric 	if (*p != '/')
493586Seric 	{
494586Seric 		while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0)
495586Seric 		{
496586Seric 			p = index(p, '/');
497586Seric 			if (p == NULL)
498586Seric 				return (TRUE);
499586Seric 			p++;
500586Seric 		}
501586Seric 	}
502586Seric 
503586Seric 	printf("You may not use full pathnames or \"..\"\n");
504586Seric 	return (FALSE);
505586Seric }
506586Seric /*
507261Seric **  CLEAN -- clean out recreatable files
508261Seric **
509261Seric **	Any file for which an "s." file exists but no "p." file
510261Seric **	exists in the current directory is purged.
511261Seric **
512261Seric **	Parameters:
513346Seric **		really -- if TRUE, remove everything.
514346Seric **			else, just report status.
515261Seric **
516261Seric **	Returns:
517261Seric **		none.
518261Seric **
519261Seric **	Side Effects:
520261Seric **		removes files in the current directory.
521261Seric */
522261Seric 
523346Seric clean(really)
524346Seric 	bool really;
525261Seric {
526261Seric 	struct direct dir;
527261Seric 	struct stat stbuf;
528261Seric 	char buf[100];
529394Seric 	char pline[120];
530346Seric 	register FILE *dirfd;
531346Seric 	register char *basefile;
532351Seric 	bool gotedit;
533394Seric 	FILE *pfp;
534261Seric 
535261Seric 	dirfd = fopen(SccsPath, "r");
536261Seric 	if (dirfd == NULL)
537261Seric 	{
538261Seric 		fprintf(stderr, "Sccs: cannot open %s\n", SccsPath);
539261Seric 		return;
540261Seric 	}
541261Seric 
542261Seric 	/*
543261Seric 	**  Scan the SCCS directory looking for s. files.
544261Seric 	*/
545261Seric 
546351Seric 	gotedit = FALSE;
547261Seric 	while (fread(&dir, sizeof dir, 1, dirfd) != NULL)
548261Seric 	{
549568Seric 		if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0)
550261Seric 			continue;
551261Seric 
552261Seric 		/* got an s. file -- see if the p. file exists */
553261Seric 		strcpy(buf, SccsPath);
554261Seric 		strcat(buf, "/p.");
555346Seric 		basefile = &buf[strlen(buf)];
556568Seric 		strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2);
557346Seric 		basefile[sizeof dir.d_name - 2] = '\0';
558394Seric 		pfp = fopen(buf, "r");
559394Seric 		if (pfp != NULL)
560346Seric 		{
561394Seric 			while (fgets(pline, sizeof pline, pfp) != NULL)
562416Seric 				printf("%12s: being edited: %s", basefile, pline);
563394Seric 			fclose(pfp);
564351Seric 			gotedit = TRUE;
565261Seric 			continue;
566346Seric 		}
567261Seric 
568261Seric 		/* the s. file exists and no p. file exists -- unlink the g-file */
569346Seric 		if (really)
570346Seric 		{
571568Seric 			strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2);
572346Seric 			buf[sizeof dir.d_name - 2] = '\0';
573346Seric 			unlink(buf);
574346Seric 		}
575261Seric 	}
576261Seric 
577261Seric 	fclose(dirfd);
578351Seric 	if (!gotedit && !really)
579416Seric 		printf("Nothing being edited\n");
580261Seric }
581396Seric /*
582396Seric **  UNEDIT -- unedit a file
583396Seric **
584396Seric **	Checks to see that the current user is actually editting
585396Seric **	the file and arranges that s/he is not editting it.
586396Seric **
587396Seric **	Parameters:
588416Seric **		fn -- the name of the file to be unedited.
589396Seric **
590396Seric **	Returns:
591585Seric **		TRUE -- if the file was successfully unedited.
592585Seric **		FALSE -- if the file was not unedited for some
593585Seric **			reason.
594396Seric **
595396Seric **	Side Effects:
596396Seric **		fn is removed
597396Seric **		entries are removed from pfile.
598396Seric */
599396Seric 
600585Seric bool
601396Seric unedit(fn)
602396Seric 	char *fn;
603396Seric {
604396Seric 	register FILE *pfp;
605396Seric 	char *pfn;
606396Seric 	static char tfn[] = "/tmp/sccsXXXXX";
607396Seric 	FILE *tfp;
608396Seric 	register char *p;
609396Seric 	register char *q;
610396Seric 	bool delete = FALSE;
611396Seric 	bool others = FALSE;
612396Seric 	char *myname;
613396Seric 	extern char *getlogin();
614396Seric 	struct pfile *pent;
615396Seric 	extern struct pfile *getpfile();
616396Seric 	char buf[120];
617396Seric 
618396Seric 	/* make "s." filename & find the trailing component */
619396Seric 	pfn = makefile(fn);
620586Seric 	if (pfn == NULL)
621586Seric 		return (FALSE);
622586Seric 	q = rindex(pfn, '/');
623586Seric 	if (q == NULL)
624586Seric 		q = &pfn[-1];
625586Seric 	if (q[1] != 's' || q[2] != '.')
626396Seric 	{
627396Seric 		fprintf(stderr, "Sccs: bad file name \"%s\"\n", fn);
628585Seric 		return (FALSE);
629396Seric 	}
630396Seric 
631396Seric 	/* turn "s." into "p." */
632396Seric 	*++q = 'p';
633396Seric 
634396Seric 	pfp = fopen(pfn, "r");
635396Seric 	if (pfp == NULL)
636396Seric 	{
637416Seric 		printf("%12s: not being edited\n", fn);
638585Seric 		return (FALSE);
639396Seric 	}
640396Seric 
641396Seric 	/*
642396Seric 	**  Copy p-file to temp file, doing deletions as needed.
643396Seric 	*/
644396Seric 
645396Seric 	mktemp(tfn);
646396Seric 	tfp = fopen(tfn, "w");
647396Seric 	if (tfp == NULL)
648396Seric 	{
649396Seric 		fprintf(stderr, "Sccs: cannot create \"%s\"\n", tfn);
650396Seric 		exit(EX_OSERR);
651396Seric 	}
652396Seric 
653396Seric 	myname = getlogin();
654396Seric 	while ((pent = getpfile(pfp)) != NULL)
655396Seric 	{
656396Seric 		if (strcmp(pent->p_user, myname) == 0)
657396Seric 		{
658396Seric 			/* a match */
659396Seric 			delete++;
660396Seric 		}
661396Seric 		else
662396Seric 		{
663396Seric 			fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid,
664396Seric 			    pent->p_nsid, pent->p_user, pent->p_date,
665396Seric 			    pent->p_time);
666396Seric 			others++;
667396Seric 		}
668396Seric 	}
669396Seric 
670396Seric 	/* do final cleanup */
671396Seric 	if (others)
672396Seric 	{
673396Seric 		if (freopen(tfn, "r", tfp) == NULL)
674396Seric 		{
675396Seric 			fprintf(stderr, "Sccs: cannot reopen \"%s\"\n", tfn);
676396Seric 			exit(EX_OSERR);
677396Seric 		}
678396Seric 		if (freopen(pfn, "w", pfp) == NULL)
679396Seric 		{
680396Seric 			fprintf(stderr, "Sccs: cannot create \"%s\"\n", pfn);
681585Seric 			return (FALSE);
682396Seric 		}
683396Seric 		while (fgets(buf, sizeof buf, tfp) != NULL)
684396Seric 			fputs(buf, pfp);
685396Seric 	}
686396Seric 	else
687396Seric 	{
688396Seric 		unlink(pfn);
689396Seric 	}
690396Seric 	fclose(tfp);
691396Seric 	fclose(pfp);
692396Seric 	unlink(tfn);
693396Seric 
694396Seric 	if (delete)
695396Seric 	{
696396Seric 		unlink(fn);
697396Seric 		printf("%12s: removed\n", fn);
698585Seric 		return (TRUE);
699396Seric 	}
700396Seric 	else
701396Seric 	{
702416Seric 		printf("%12s: not being edited by you\n", fn);
703585Seric 		return (FALSE);
704396Seric 	}
705396Seric }
706396Seric /*
707396Seric **  GETPFILE -- get an entry from the p-file
708396Seric **
709396Seric **	Parameters:
710396Seric **		pfp -- p-file file pointer
711396Seric **
712396Seric **	Returns:
713396Seric **		pointer to p-file struct for next entry
714396Seric **		NULL on EOF or error
715396Seric **
716396Seric **	Side Effects:
717396Seric **		Each call wipes out results of previous call.
718396Seric */
719396Seric 
720396Seric struct pfile *
721396Seric getpfile(pfp)
722396Seric 	FILE *pfp;
723396Seric {
724396Seric 	static struct pfile ent;
725396Seric 	static char buf[120];
726396Seric 	register char *p;
727396Seric 	extern char *nextfield();
728396Seric 
729396Seric 	if (fgets(buf, sizeof buf, pfp) == NULL)
730396Seric 		return (NULL);
731396Seric 
732396Seric 	ent.p_osid = p = buf;
733396Seric 	ent.p_nsid = p = nextfield(p);
734396Seric 	ent.p_user = p = nextfield(p);
735396Seric 	ent.p_date = p = nextfield(p);
736396Seric 	ent.p_time = p = nextfield(p);
737396Seric 	if (p == NULL || nextfield(p) != NULL)
738396Seric 		return (NULL);
739396Seric 
740396Seric 	return (&ent);
741396Seric }
742396Seric 
743396Seric 
744396Seric char *
745396Seric nextfield(p)
746396Seric 	register char *p;
747396Seric {
748396Seric 	if (p == NULL || *p == '\0')
749396Seric 		return (NULL);
750396Seric 	while (*p != ' ' && *p != '\n' && *p != '\0')
751396Seric 		p++;
752396Seric 	if (*p == '\n' || *p == '\0')
753396Seric 	{
754396Seric 		*p = '\0';
755396Seric 		return (NULL);
756396Seric 	}
757396Seric 	*p++ = '\0';
758396Seric 	return (p);
759396Seric }
760