+ gstrcpy(buf, SccsDir, sizeof(buf));
+ gstrcat(buf, "/", sizeof(buf));
+ }
+ else
+ gstrcpy(buf, "", sizeof(buf));
+
+ /* then the head of the pathname */
+ strncat(buf, name, p - name);
+ q = &buf[strlen(buf)];
+ strcpy(q, p);
+ if (strncmp(p, "s.", 2) != 0 && !isdir(buf))
+ {
+ /* sorry, no; copy the SCCS pathname & the "s." */
+ gstrcpy(q, SccsPath, sizeof(buf));
+ gstrcat(buf, "/s.", sizeof(buf));
+
+ /* and now the end of the name */
+ gstrcat(buf, p, sizeof(buf));
+ }
+
+ /* if i haven't changed it, why did I do all this? */
+ if (strcmp(buf, name) == 0)
+ p = name;
+
+ return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR);
+}
+\f/*
+** ISDIR -- return true if the argument is a directory.
+**
+** Parameters:
+** name -- the pathname of the file to check.
+**
+** Returns:
+** TRUE if 'name' is a directory, FALSE otherwise.
+**
+** Side Effects:
+** none.
+*/
+
+bool
+isdir(name)
+ char *name;
+{
+ struct stat stbuf;
+
+ return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR);
+}
+\f
+/*
+** SAFEPATH -- determine whether a pathname is "safe"
+**
+** "Safe" pathnames only allow you to get deeper into the
+** directory structure, i.e., full pathnames and ".." are
+** not allowed.
+**
+** Parameters:
+** p -- the name to check.
+**
+** Returns:
+** TRUE -- if the path is safe.
+** FALSE -- if the path is not safe.
+**
+** Side Effects:
+** Prints a message if the path is not safe.
+*/
+
+bool
+safepath(p)
+ register char *p;
+{
+ extern char *index();
+
+ if (*p != '/')
+ {
+ while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0)
+ {
+ p = index(p, '/');
+ if (p == NULL)
+ return (TRUE);
+ p++;
+ }
+ }
+
+ printf("You may not use full pathnames or \"..\"\n");
+ return (FALSE);
+}
+\f
+/*
+** CLEAN -- clean out recreatable files
+**
+** Any file for which an "s." file exists but no "p." file
+** exists in the current directory is purged.
+**
+** Parameters:
+** mode -- tells whether this came from a "clean", "info", or
+** "check" command.
+** argv -- the rest of the argument vector.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Removes files in the current directory.
+** Prints information regarding files being edited.
+** Exits if a "check" command.
+*/
+
+clean(mode, argv)
+ int mode;
+ char **argv;
+{
+ struct direct *dir;
+ char buf[FBUFSIZ];
+ char *bufend;
+ register DIR *dirfd;
+ register char *basefile;
+ bool gotedit;
+ bool gotpfent;
+ FILE *pfp;
+ bool nobranch = FALSE;
+ extern struct pfile *getpfent();
+ register struct pfile *pf;
+ register char **ap;
+ extern char *username();
+ char *usernm = NULL;
+ char *subdir = NULL;
+ char *cmdname;
+
+ /*
+ ** Process the argv
+ */
+
+ cmdname = *argv;
+ for (ap = argv; *++ap != NULL; )
+ {
+ if (**ap == '-')
+ {
+ /* we have a flag */
+ switch ((*ap)[1])
+ {
+ case 'b':
+ nobranch = TRUE;
+ break;
+
+ case 'u':
+ if ((*ap)[2] != '\0')
+ usernm = &(*ap)[2];
+ else if (ap[1] != NULL && ap[1][0] != '-')
+ usernm = *++ap;
+ else
+ usernm = username();
+ break;
+ }
+ }
+ else
+ {
+ if (subdir != NULL)
+ usrerr("too many args");
+ else
+ subdir = *ap;
+ }
+ }
+
+ /*
+ ** Find and open the SCCS directory.
+ */
+
+ gstrcpy(buf, SccsDir, sizeof(buf));
+ if (buf[0] != '\0')
+ gstrcat(buf, "/", sizeof(buf));
+ if (subdir != NULL)
+ {
+ gstrcat(buf, subdir, sizeof(buf));
+ gstrcat(buf, "/", sizeof(buf));
+ }
+ gstrcat(buf, SccsPath, sizeof(buf));
+ bufend = &buf[strlen(buf)];
+
+ dirfd = opendir(buf);
+ if (dirfd == NULL)
+ {
+ usrerr("cannot open %s", buf);
+ return (EX_NOINPUT);
+ }
+
+ /*
+ ** Scan the SCCS directory looking for s. files.
+ ** gotedit tells whether we have tried to clean any
+ ** files that are being edited.
+ */
+
+ gotedit = FALSE;
+ while (dir = readdir(dirfd)) {
+ if (strncmp(dir->d_name, "s.", 2) != 0)
+ continue;
+
+ /* got an s. file -- see if the p. file exists */
+ gstrcpy(bufend, "/p.", sizeof(buf));
+ basefile = bufend + 3;
+ gstrcpy(basefile, &dir->d_name[2], sizeof(buf));
+
+ /*
+ ** open and scan the p-file.
+ ** 'gotpfent' tells if we have found a valid p-file
+ ** entry.
+ */
+
+ pfp = fopen(buf, "r");
+ gotpfent = FALSE;
+ if (pfp != NULL)
+ {
+ /* the file exists -- report it's contents */
+ while ((pf = getpfent(pfp)) != NULL)
+ {
+ if (nobranch && isbranch(pf->p_nsid))
+ continue;
+ if (usernm != NULL && strcmp(usernm, pf->p_user) != 0 && mode != CLEANC)
+ continue;
+ gotedit = TRUE;
+ gotpfent = TRUE;
+ if (mode == TELLC)
+ {
+ printf("%s\n", basefile);
+ break;
+ }
+ printf("%12s: being edited: ", basefile);
+ putpfent(pf, stdout);
+ }
+ fclose(pfp);
+ }
+
+ /* the s. file exists and no p. file exists -- unlink the g-file */
+ if (mode == CLEANC && !gotpfent)
+ {
+ char unlinkbuf[FBUFSIZ];
+ gstrcpy(unlinkbuf, &dir->d_name[2], sizeof(unlinkbuf));
+ unlink(unlinkbuf);
+ }
+ }
+
+ /* cleanup & report results */
+ closedir(dirfd);
+ if (!gotedit && mode == INFOC)
+ {
+ printf("Nothing being edited");
+ if (nobranch)
+ printf(" (on trunk)");
+ if (usernm == NULL)
+ printf("\n");
+ else
+ printf(" by %s\n", usernm);
+ }
+ if (mode == CHECKC)
+ exit(gotedit);
+ return (EX_OK);
+}
+\f
+/*
+** ISBRANCH -- is the SID a branch?
+**
+** Parameters:
+** sid -- the sid to check.
+**
+** Returns:
+** TRUE if the sid represents a branch.
+** FALSE otherwise.
+**
+** Side Effects:
+** none.
+*/
+
+isbranch(sid)
+ char *sid;
+{
+ register char *p;
+ int dots;
+
+ dots = 0;
+ for (p = sid; *p != '\0'; p++)
+ {
+ if (*p == '.')
+ dots++;
+ if (dots > 1)
+ return (TRUE);
+ }
+ return (FALSE);
+}
+\f
+/*
+** UNEDIT -- unedit a file
+**
+** Checks to see that the current user is actually editting
+** the file and arranges that s/he is not editting it.
+**
+** Parameters:
+** fn -- the name of the file to be unedited.
+**
+** Returns:
+** TRUE -- if the file was successfully unedited.
+** FALSE -- if the file was not unedited for some
+** reason.
+**
+** Side Effects:
+** fn is removed
+** entries are removed from pfile.
+*/
+
+bool
+unedit(fn)
+ char *fn;
+{
+ register FILE *pfp;
+ char *cp, *pfn;
+ static char tfn[] = "/tmp/sccsXXXXX";
+ FILE *tfp;
+ register char *q;
+ bool delete = FALSE;
+ bool others = FALSE;
+ char *myname;
+ extern char *username();
+ struct pfile *pent;
+ extern struct pfile *getpfent();
+ char buf[PFILELG];
+ extern char *makefile();
+
+ /* make "s." filename & find the trailing component */
+ pfn = makefile(fn);
+ if (pfn == NULL)
+ return (FALSE);
+ q = rindex(pfn, '/');
+ if (q == NULL)
+ q = &pfn[-1];
+ if (q[1] != 's' || q[2] != '.')
+ {
+ usrerr("bad file name \"%s\"", fn);
+ return (FALSE);
+ }
+
+ /* turn "s." into "p." & try to open it */
+ *++q = 'p';
+
+ pfp = fopen(pfn, "r");
+ if (pfp == NULL)
+ {
+ printf("%12s: not being edited\n", fn);
+ return (FALSE);
+ }
+
+ /* create temp file for editing p-file */
+ mktemp(tfn);
+ tfp = fopen(tfn, "w");
+ if (tfp == NULL)
+ {
+ usrerr("cannot create \"%s\"", tfn);