X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/1dbb3fe754d1161b51153772b00b52bfb79a3701..cab3a5758f3532c9d40749aee2d317c7d7da94c6:/usr/src/usr.bin/sccs/sccs.c diff --git a/usr/src/usr.bin/sccs/sccs.c b/usr/src/usr.bin/sccs/sccs.c index a72026cbd6..8f3a613abc 100644 --- a/usr/src/usr.bin/sccs/sccs.c +++ b/usr/src/usr.bin/sccs/sccs.c @@ -1,9 +1,27 @@ +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1980 Regents of the University of California.\n\ + All rights reserved.\n"; +#endif not lint + +#ifndef lint +static char sccsid[] = "@(#)sccs.c 5.1 (Berkeley) %G%"; +#endif not lint + # include -# include +# include # include # include +# include +# include # include -# include +# include /* ** SCCS.C -- human-oriented front end to the SCCS system. @@ -77,47 +95,47 @@ ** aspects of this program cannot be abused. ** This flag also disables the -p flag. ** SCCSPATH -- the default for the -p flag. +** MYNAME -- the title this program should print when it +** gives error messages. ** ** Compilation Instructions: ** cc -O -n -s sccs.c +** The flags listed above can be -D defined to simplify +** recompilation for variant versions. ** ** Author: ** Eric Allman, UCB/INGRES ** Copyright 1980 Regents of the University of California */ + /******************* Configuration Information ********************/ -# ifdef CSVAX -# define UIDUSER -# define PROGPATH(name) "/usr/local/name" -# endif CSVAX +# ifndef SCCSPATH +# define SCCSPATH "SCCS" /* pathname in which to find s-files */ +# endif NOT SCCSPATH -# define SCCSPATH "SCCS" -/* put #define SCCSDIR here */ +# ifndef MYNAME +# define MYNAME "sccs" /* name used for printing errors */ +# endif NOT MYNAME -char MyName[] = "sccs"; /* name used in messages */ +# ifndef PROGPATH +# define PROGPATH(name) "/usr/local/name" /* place to find binaries */ +# endif PROGPATH /**************** End of Configuration Information ****************/ - -static char SccsId[] = "@(#)sccs.c 1.32 %G%"; - -# define bitset(bit, word) ((bit) & (word)) - + typedef char bool; # define TRUE 1 # define FALSE 0 -# ifdef UIDUSER -# include -# endif UIDUSER +# define bitset(bit, word) ((bool) ((bit) & (word))) struct sccsprog { char *sccsname; /* name of SCCS routine */ short sccsoper; /* opcode, see below */ short sccsflags; /* flags, see below */ - char *sccsklets; /* valid key-letters on macros */ char *sccspath; /* pathname of binary implementing */ }; @@ -127,6 +145,10 @@ struct sccsprog # define FIX 2 /* fix a delta */ # define CLEAN 3 /* clean out recreatable files */ # define UNEDIT 4 /* unedit a file */ +# define SHELL 5 /* call a shell file (like PROG) */ +# define DIFFS 6 /* diff between sccs & file out */ +# define DODIFF 7 /* internal call to diff program */ +# define ENTER 8 /* enter new files */ /* bits for sccsflags */ # define NO_SDOT 0001 /* no s. on front of args */ @@ -136,33 +158,51 @@ struct sccsprog # define CLEANC 0 /* clean command */ # define INFOC 1 /* info command */ # define CHECKC 2 /* check command */ +# define TELLC 3 /* give list of files being edited */ -# ifndef PROGPATH -# define PROGPATH(name) "/usr/sccs/name" -# endif PROGPATH +/* +** Description of commands known to this program. +** First argument puts the command into a class. Second arg is +** info regarding treatment of this command. Third arg is a +** list of flags this command accepts from macros, etc. Fourth +** arg is the pathname of the implementing program, or the +** macro definition, or the arg to a sub-algorithm. +*/ struct sccsprog SccsProg[] = { - "admin", PROG, REALUSER, "", PROGPATH(admin), - "chghist", PROG, 0, "", PROGPATH(rmdel), - "comb", PROG, 0, "", PROGPATH(comb), - "delta", PROG, 0, "mysrp", PROGPATH(delta), - "get", PROG, 0, "ixbeskc", PROGPATH(get), - "help", PROG, NO_SDOT, "", PROGPATH(help), - "prt", PROG, 0, "", PROGPATH(prt), - "rmdel", PROG, REALUSER, "", PROGPATH(rmdel), - "what", PROG, NO_SDOT, "", PROGPATH(what), - "edit", CMACRO, NO_SDOT, "ixbsc", "get -e", - "delget", CMACRO, NO_SDOT, "", "delta/get -t", - "deledit", CMACRO, NO_SDOT, "", "delta/get -e -t", - "fix", FIX, NO_SDOT, "", NULL, - "clean", CLEAN, REALUSER, "", (char *) CLEANC, - "info", CLEAN, REALUSER, "", (char *) INFOC, - "check", CLEAN, REALUSER, "", (char *) CHECKC, - "unedit", UNEDIT, NO_SDOT, "", NULL, - NULL, -1, 0, "", NULL + "admin", PROG, REALUSER, PROGPATH(admin), + "chghist", PROG, 0, PROGPATH(rmdel), + "comb", PROG, 0, PROGPATH(comb), + "delta", PROG, 0, PROGPATH(delta), + "get", PROG, 0, PROGPATH(get), + "help", PROG, NO_SDOT, PROGPATH(help), + "prs", PROG, 0, PROGPATH(prs), + "prt", PROG, 0, PROGPATH(prt), + "rmdel", PROG, REALUSER, PROGPATH(rmdel), + "val", PROG, 0, PROGPATH(val), + "what", PROG, NO_SDOT, PROGPATH(what), + "sccsdiff", SHELL, REALUSER, PROGPATH(sccsdiff), + "edit", CMACRO, NO_SDOT, "get -e", + "delget", CMACRO, NO_SDOT, "delta:mysrp/get:ixbeskcl -t", + "deledit", CMACRO, NO_SDOT, "delta:mysrp -n/get:ixbskcl -e -t -g", + "fix", FIX, NO_SDOT, NULL, + "clean", CLEAN, REALUSER|NO_SDOT, (char *) CLEANC, + "info", CLEAN, REALUSER|NO_SDOT, (char *) INFOC, + "check", CLEAN, REALUSER|NO_SDOT, (char *) CHECKC, + "tell", CLEAN, REALUSER|NO_SDOT, (char *) TELLC, + "unedit", UNEDIT, NO_SDOT, NULL, + "diffs", DIFFS, NO_SDOT|REALUSER, NULL, + "-diff", DODIFF, NO_SDOT|REALUSER, PROGPATH(bdiff), + "print", CMACRO, 0, "prt -e/get -p -m -s", + "branch", CMACRO, NO_SDOT, + "get:ixrc -e -b/delta: -s -n -ybranch-place-holder/get:pl -e -t -g", + "enter", ENTER, NO_SDOT, NULL, + "create", CMACRO, NO_SDOT, "enter/get:ixbeskcl -t", + NULL, -1, 0, NULL }; +/* one line from a p-file */ struct pfile { char *p_osid; /* old SID */ @@ -170,6 +210,7 @@ struct pfile char *p_user; /* user who did edit */ char *p_date; /* date of get */ char *p_time; /* time of get */ + char *p_aux; /* extra info at end */ }; char *SccsPath = SCCSPATH; /* pathname of SCCS files */ @@ -178,11 +219,22 @@ char *SccsDir = SCCSDIR; /* directory to begin search from */ # else char *SccsDir = ""; # endif +char MyName[] = MYNAME; /* name used in messages */ +int OutFile = -1; /* override output file for commands */ bool RealUser; /* if set, running as real user */ # ifdef DEBUG bool Debug; /* turn on tracing */ # endif - +# ifndef V6 +extern char *getenv(); +# endif V6 + +char *gstrcat(), *strcat(); +char *gstrncat(), *strncat(); +char *gstrcpy(), *strcpy(); +#define FBUFSIZ BUFSIZ +#define PFILELG 120 + main(argc, argv) int argc; char **argv; @@ -190,6 +242,43 @@ main(argc, argv) register char *p; extern struct sccsprog *lookup(); register int i; +# ifndef V6 +# ifndef SCCSDIR + register struct passwd *pw; + extern struct passwd *getpwnam(); + char buf[FBUFSIZ]; + + /* pull "SccsDir" out of the environment (possibly) */ + p = getenv("PROJECTDIR"); + if (p != NULL && p[0] != '\0') + { + if (p[0] == '/') + SccsDir = p; + else + { + pw = getpwnam(p); + if (pw == NULL) + { + usrerr("user %s does not exist", p); + exit(EX_USAGE); + } + gstrcpy(buf, pw->pw_dir, sizeof(buf)); + gstrcat(buf, "/src", sizeof(buf)); + if (access(buf, 0) < 0) + { + gstrcpy(buf, pw->pw_dir, sizeof(buf)); + gstrcat(buf, "/source", sizeof(buf)); + if (access(buf, 0) < 0) + { + usrerr("project %s has no source!", p); + exit(EX_USAGE); + } + } + SccsDir = buf; + } + } +# endif SCCSDIR +# endif V6 /* ** Detect and decode flags intended for this program. @@ -218,10 +307,14 @@ main(argc, argv) # ifndef SCCSDIR case 'p': /* path of sccs files */ SccsPath = ++p; + if (SccsPath[0] == '\0' && argv[1] != NULL) + SccsPath = *++argv; break; case 'd': /* directory to search from */ SccsDir = ++p; + if (SccsDir[0] == '\0' && argv[1] != NULL) + SccsDir = *++argv; break; # endif @@ -240,10 +333,11 @@ main(argc, argv) SccsPath = "."; } - i = command(argv, FALSE, FALSE, ""); + i = command(argv, FALSE, ""); exit(i); } - /* + +/* ** COMMAND -- look up and perform a command ** ** This routine is the guts of this program. Given an @@ -266,25 +360,26 @@ main(argc, argv) ** none. */ -command(argv, forkflag, editflag, arg0) +command(argv, forkflag, arg0) char **argv; bool forkflag; - bool editflag; char *arg0; { register struct sccsprog *cmd; register char *p; - register char *q; - char buf[40]; + char buf[FBUFSIZ]; extern struct sccsprog *lookup(); char *nav[1000]; char **np; - char **ap; + register char **ap; register int i; + register char *q; extern bool unedit(); int rval = 0; extern char *index(); extern char *makefile(); + char *editchs; + extern char *tail(); # ifdef DEBUG if (Debug) @@ -297,32 +392,41 @@ command(argv, forkflag, editflag, arg0) /* ** Copy arguments. - ** Phase one -- from arg0 & if necessary argv[0]. + ** Copy from arg0 & if necessary at most one arg + ** from argv[0]. */ - np = nav; + np = ap = &nav[1]; + editchs = NULL; for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) { *np++ = q; while (*p == ' ') p++; - while (*p != ' ' && *p != '\0' && *p != '/') + while (*p != ' ' && *p != '\0' && *p != '/' && *p != ':') *q++ = *p++; *q++ = '\0'; + if (*p == ':') + { + editchs = q; + while (*++p != '\0' && *p != '/' && *p != ' ') + *q++ = *p; + *q++ = '\0'; + } } *np = NULL; - if (nav[0] == NULL) + if (*ap == NULL) *np++ = *argv++; /* ** Look up command. - ** At this point, nav[0] is the command name. + ** At this point, *ap is the command name. */ - cmd = lookup(nav[0]); + cmd = lookup(*ap); if (cmd == NULL) { - usrerr("Unknown command \"%s\"", argv[0]); + usrerr("Unknown command \"%s\"", *ap); return (EX_USAGE); } @@ -335,8 +439,7 @@ command(argv, forkflag, editflag, arg0) p = *argv; if (*p == '-') { - if (p[1] == '\0' || !editflag || cmd->sccsklets == NULL || - index(cmd->sccsklets, p[1]) != NULL) + if (p[1] == '\0' || editchs == NULL || index(editchs, p[1]) != NULL) *np++ = p; } else @@ -355,49 +458,134 @@ command(argv, forkflag, editflag, arg0) switch (cmd->sccsoper) { + case SHELL: /* call a shell file */ + *ap = cmd->sccspath; + *--ap = "sh"; + rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag); + break; + case PROG: /* call an sccs prog */ - rval = callprog(cmd->sccspath, cmd->sccsflags, nav, forkflag); + rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); break; case CMACRO: /* command macro */ + /* step through & execute each part of the macro */ for (p = cmd->sccspath; *p != '\0'; p++) { q = p; while (*p != '\0' && *p != '/') p++; - rval = command(&nav[1], *p != '\0', TRUE, q); + rval = command(&ap[1], *p != '\0', q); if (rval != 0) break; } break; case FIX: /* fix a delta */ - if (strncmp(nav[1], "-r", 2) != 0) + if (strncmp(ap[1], "-r", 2) != 0) { usrerr("-r flag needed for fix command"); rval = EX_USAGE; break; } - rval = command(&nav[1], TRUE, TRUE, "get -k"); + + /* get the version with all changes */ + rval = command(&ap[1], TRUE, "get -k"); + + /* now remove that version from the s-file */ if (rval == 0) - rval = command(&nav[1], TRUE, TRUE, "rmdel"); + rval = command(&ap[1], TRUE, "rmdel:r"); + + /* and edit the old version (but don't clobber new vers) */ if (rval == 0) - rval = command(&nav[2], FALSE, TRUE, "get -e -g"); + rval = command(&ap[2], FALSE, "get -e -g"); break; case CLEAN: - rval = clean((int) cmd->sccspath); + rval = clean((int) cmd->sccspath, ap); break; case UNEDIT: - for (argv = np = &nav[1]; *argv != NULL; argv++) + for (argv = np = &ap[1]; *argv != NULL; argv++) { if (unedit(*argv)) *np++ = *argv; } *np = NULL; - if (i > 0) - rval = command(&nav[1], FALSE, FALSE, "get"); + + /* get all the files that we unedited successfully */ + if (np > &ap[1]) + rval = command(&ap[1], FALSE, "get"); + break; + + case DIFFS: /* diff between s-file & edit file */ + /* find the end of the flag arguments */ + for (np = &ap[1]; *np != NULL && **np == '-'; np++) + continue; + argv = np; + + /* for each file, do the diff */ + p = argv[1]; + while (*np != NULL) + { + /* messy, but we need a null terminated argv */ + *argv = *np++; + argv[1] = NULL; + i = dodiff(ap, tail(*argv)); + if (rval == 0) + rval = i; + argv[1] = p; + } + break; + + case DODIFF: /* internal diff call */ + setuid(getuid()); + for (np = ap; *np != NULL; np++) + { + if ((*np)[0] == '-' && (*np)[1] == 'C') + (*np)[1] = 'c'; + } + + /* insert "-" argument */ + np[1] = NULL; + np[0] = np[-1]; + np[-1] = "-"; + + /* execute the diff program of choice */ +# ifndef V6 + execvp("diff", ap); +# endif + execv(cmd->sccspath, argv); + syserr("cannot exec %s", cmd->sccspath); + exit(EX_OSERR); + + case ENTER: /* enter new sccs files */ + /* skip over flag arguments */ + for (np = &ap[1]; *np != NULL && **np == '-'; np++) + continue; + argv = np; + + /* do an admin for each file */ + p = argv[1]; + while (*np != NULL) + { + printf("\n%s:\n", *np); + strcpy(buf, "-i"); + gstrcat(buf, *np, sizeof(buf)); + ap[0] = buf; + argv[0] = tail(*np); + argv[1] = NULL; + rval = command(ap, TRUE, "admin"); + argv[1] = p; + if (rval == 0) + { + strcpy(buf, ","); + gstrcat(buf, tail(*np), sizeof(buf)); + if (link(*np, buf) >= 0) + unlink(*np); + } + np++; + } break; default: @@ -410,7 +598,8 @@ command(argv, forkflag, editflag, arg0) # endif return (rval); } - /* + +/* ** LOOKUP -- look up an SCCS command name. ** ** Parameters: @@ -437,7 +626,8 @@ lookup(name) } return (NULL); } - /* + +/* ** CALLPROG -- call a program ** ** Used to call the SCCS programs. @@ -499,26 +689,40 @@ callprog(progpath, flags, argv, forkflag) wait(&st); if ((st & 0377) == 0) st = (st >> 8) & 0377; + if (OutFile >= 0) + { + close(OutFile); + OutFile = -1; + } return (st); } } + else if (OutFile >= 0) + { + syserr("callprog: setting stdout w/o forking"); + exit(EX_SOFTWARE); + } - /* - ** Set protection as appropriate. - */ - + /* set protection as appropriate */ if (bitset(REALUSER, flags)) setuid(getuid()); - - /* - ** Call real SCCS program. - */ + /* change standard input & output if needed */ + if (OutFile >= 0) + { + close(1); + dup(OutFile); + close(OutFile); + } + + /* call real SCCS program */ execv(progpath, argv); syserr("cannot execute %s", progpath); exit(EX_UNAVAILABLE); + /*NOTREACHED*/ } - /* + +/* ** MAKEFILE -- make filename of SCCS file ** ** If the name passed is already the name of an SCCS file, @@ -549,8 +753,7 @@ makefile(name) char *name; { register char *p; - register char c; - char buf[512]; + char buf[3*FBUFSIZ]; extern char *malloc(); extern char *rindex(); extern bool isdir(); @@ -578,23 +781,30 @@ makefile(name) ** Create the actual pathname. */ + /* first the directory part */ if (name[0] != '/') { - strcpy(buf, SccsDir); - strcat(buf, "/"); + gstrcpy(buf, SccsDir, sizeof(buf)); + gstrcat(buf, "/", sizeof(buf)); } else - strcpy(buf, ""); + 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)) { - strcpy(q, SccsPath); - strcat(buf, "/s."); - strcat(buf, p); + /* 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; @@ -621,7 +831,8 @@ isdir(name) return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); } - /* + +/* ** SAFEPATH -- determine whether a pathname is "safe" ** ** "Safe" pathnames only allow you to get deeper into the @@ -659,15 +870,17 @@ safepath(p) printf("You may not use full pathnames or \"..\"\n"); return (FALSE); } - /* + +/* ** 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: -** tells whether this came from a "clean", "info", or -** "check" command. +** mode -- tells whether this came from a "clean", "info", or +** "check" command. +** argv -- the rest of the argument vector. ** ** Returns: ** none. @@ -678,23 +891,78 @@ safepath(p) ** Exits if a "check" command. */ -clean(mode) +clean(mode, argv) int mode; + char **argv; { - struct direct dir; - struct stat stbuf; - char buf[100]; - char pline[120]; - register FILE *dirfd; + 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. + */ - strcpy(buf, SccsDir); + gstrcpy(buf, SccsDir, sizeof(buf)); if (buf[0] != '\0') - strcat(buf, "/"); - strcat(buf, SccsPath); - dirfd = fopen(buf, "r"); + 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); @@ -703,50 +971,108 @@ clean(mode) /* ** 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 (fread(&dir, sizeof dir, 1, dirfd) != NULL) - { - if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) + while (dir = readdir(dirfd)) { + if (strncmp(dir->d_name, "s.", 2) != 0) continue; /* got an s. file -- see if the p. file exists */ - strcpy(buf, SccsDir); - if (buf[0] != '\0') - strcat(buf, "/"); - strcat(buf, SccsPath); - strcat(buf, "/p."); - basefile = &buf[strlen(buf)]; - strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); - basefile[sizeof dir.d_name - 2] = '\0'; + 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) { - while (fgets(pline, sizeof pline, pfp) != NULL) - printf("%12s: being edited: %s", basefile, pline); + /* 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); - gotedit = TRUE; - continue; } /* the s. file exists and no p. file exists -- unlink the g-file */ - if (mode == CLEANC) + if (mode == CLEANC && !gotpfent) { - strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); - buf[sizeof dir.d_name - 2] = '\0'; - unlink(buf); + char unlinkbuf[FBUFSIZ]; + gstrcpy(unlinkbuf, &dir->d_name[2], sizeof(unlinkbuf)); + unlink(unlinkbuf); } } - fclose(dirfd); + /* cleanup & report results */ + closedir(dirfd); if (!gotedit && mode == INFOC) - printf("Nothing being edited\n"); + { + 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); } - /* + +/* +** 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); +} + +/* ** UNEDIT -- unedit a file ** ** Checks to see that the current user is actually editting @@ -770,23 +1096,18 @@ unedit(fn) char *fn; { register FILE *pfp; - char *pfn; + char *cp, *pfn; static char tfn[] = "/tmp/sccsXXXXX"; FILE *tfp; - register char *p; register char *q; bool delete = FALSE; bool others = FALSE; char *myname; - extern char *getlogin(); + extern char *username(); struct pfile *pent; - extern struct pfile *getpfile(); - char buf[120]; + extern struct pfile *getpfent(); + char buf[PFILELG]; extern char *makefile(); -# ifdef UIDUSER - struct passwd *pw; - extern struct passwd *getpwuid(); -# endif UIDUSER /* make "s." filename & find the trailing component */ pfn = makefile(fn); @@ -801,7 +1122,7 @@ unedit(fn) return (FALSE); } - /* turn "s." into "p." */ + /* turn "s." into "p." & try to open it */ *++q = 'p'; pfp = fopen(pfn, "r"); @@ -811,10 +1132,7 @@ unedit(fn) return (FALSE); } - /* - ** Copy p-file to temp file, doing deletions as needed. - */ - + /* create temp file for editing p-file */ mktemp(tfn); tfp = fopen(tfn, "w"); if (tfp == NULL) @@ -823,18 +1141,14 @@ unedit(fn) exit(EX_OSERR); } -# ifdef UIDUSER - pw = getpwuid(getuid()); - if (pw == NULL) - { - syserr("who are you? (uid=%d)", getuid()); - exit(EX_OSERR); - } - myname = pw->pw_name; -# else - myname = getlogin(); -# endif UIDUSER - while ((pent = getpfile(pfp)) != NULL) + /* figure out who I am */ + myname = username(); + + /* + ** Copy p-file to temp file, doing deletions as needed. + */ + + while ((pent = getpfent(pfp)) != NULL) { if (strcmp(pent->p_user, myname) == 0) { @@ -843,16 +1157,42 @@ unedit(fn) } else { - fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, - pent->p_nsid, pent->p_user, pent->p_date, - pent->p_time); + /* output it again */ + putpfent(pent, tfp); others++; } } + /* + * Before changing anything, make sure we can remove + * the file in question (assuming it exists). + */ + if (delete) { + extern int errno; + + cp = tail(fn); + errno = 0; + if (access(cp, 0) < 0 && errno != ENOENT) + goto bad; + if (errno == 0) + /* + * This is wrong, but the rest of the program + * has built in assumptions about "." as well, + * so why make unedit a special case? + */ + if (access(".", 2) < 0) { + bad: + printf("%12s: can't remove\n", cp); + fclose(tfp); + fclose(pfp); + unlink(tfn); + return (FALSE); + } + } /* do final cleanup */ if (others) { + /* copy it back (perhaps it should be linked?) */ if (freopen(tfn, "r", tfp) == NULL) { syserr("cannot reopen \"%s\"", tfn); @@ -868,16 +1208,23 @@ unedit(fn) } else { + /* it's empty -- remove it */ unlink(pfn); } fclose(tfp); fclose(pfp); unlink(tfn); + /* actually remove the g-file */ if (delete) { - unlink(fn); - printf("%12s: removed\n", fn); + /* + * Since we've checked above, we can + * use the return from unlink to + * determine if the file existed or not. + */ + if (unlink(cp) >= 0) + printf("%12s: removed\n", cp); return (TRUE); } else @@ -886,8 +1233,134 @@ unedit(fn) return (FALSE); } } - /* -** GETPFILE -- get an entry from the p-file + +/* +** DODIFF -- diff an s-file against a g-file +** +** Parameters: +** getv -- argv for the 'get' command. +** gfile -- name of the g-file to diff against. +** +** Returns: +** Result of get. +** +** Side Effects: +** none. +*/ + +dodiff(getv, gfile) + char **getv; + char *gfile; +{ + int pipev[2]; + int rval; + register int i; + register int pid; + auto int st; + extern int errno; + int (*osig)(); + register char *p; + register char **ap; + bool makescript = FALSE; + + for (ap = getv; *ap != NULL; ap++) + { + p = *ap; + if (p[0] == '-') + { + switch (p[1]) + { + case 'E': + p[1] = 'e'; + makescript = TRUE; + break; + } + } + } + + if (makescript) + { + printf("sccs edit %s\n", gfile); + printf("ed - %s << 'xxEOFxx'\n", gfile); + } + else + printf("\n------- %s -------\n", gfile); + fflush(stdout); + + /* create context for diff to run in */ + if (pipe(pipev) < 0) + { + syserr("dodiff: pipe failed"); + exit(EX_OSERR); + } + if ((pid = fork()) < 0) + { + syserr("dodiff: fork failed"); + exit(EX_OSERR); + } + else if (pid > 0) + { + /* in parent; run get */ + OutFile = pipev[1]; + close(pipev[0]); + rval = command(&getv[1], TRUE, "get:rcixt -s -k -p"); + osig = signal(SIGINT, SIG_IGN); + while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR) + errno = 0; + signal(SIGINT, osig); + /* ignore result of diff */ + } + else + { + /* in child, run diff */ + if (close(pipev[1]) < 0 || close(0) < 0 || + dup(pipev[0]) != 0 || close(pipev[0]) < 0) + { + syserr("dodiff: magic failed"); + exit(EX_OSERR); + } + command(&getv[1], FALSE, "-diff:elsfhbC"); + } + + if (makescript) + { + printf("w\n"); + printf("q\n"); + printf("'xxEOFxx'\n"); + printf("sccs delta %s\n", gfile); + } + + return (rval); +} + +/* +** TAIL -- return tail of filename. +** +** Parameters: +** fn -- the filename. +** +** Returns: +** a pointer to the tail of the filename; e.g., given +** "cmd/ls.c", "ls.c" is returned. +** +** Side Effects: +** none. +*/ + +char * +tail(fn) + register char *fn; +{ + register char *p; + + for (p = fn; *p != 0; p++) + if (*p == '/' && p[1] != '\0' && p[1] != '/') + fn = &p[1]; + return (fn); +} + +/* +** GETPFENT -- get an entry from the p-file ** ** Parameters: ** pfp -- p-file file pointer @@ -901,11 +1374,11 @@ unedit(fn) */ struct pfile * -getpfile(pfp) +getpfent(pfp) FILE *pfp; { static struct pfile ent; - static char buf[120]; + static char buf[PFILELG]; register char *p; extern char *nextfield(); @@ -917,8 +1390,7 @@ getpfile(pfp) ent.p_user = p = nextfield(p); ent.p_date = p = nextfield(p); ent.p_time = p = nextfield(p); - if (p == NULL || nextfield(p) != NULL) - return (NULL); + ent.p_aux = p = nextfield(p); return (&ent); } @@ -941,6 +1413,32 @@ nextfield(p) return (p); } /* +** PUTPFENT -- output a p-file entry to a file +** +** Parameters: +** pf -- the p-file entry +** f -- the file to put it on. +** +** Returns: +** none. +** +** Side Effects: +** pf is written onto file f. +*/ + +putpfent(pf, f) + register struct pfile *pf; + register FILE *f; +{ + fprintf(f, "%s %s %s %s %s", pf->p_osid, pf->p_nsid, + pf->p_user, pf->p_date, pf->p_time); + if (pf->p_aux != NULL) + fprintf(f, " %s", pf->p_aux); + else + fprintf(f, "\n"); +} + +/* ** USRERR -- issue user-level error ** ** Parameters: @@ -954,6 +1452,7 @@ nextfield(p) ** none. */ +/*VARARGS1*/ usrerr(f, p1, p2, p3) char *f; { @@ -963,7 +1462,8 @@ usrerr(f, p1, p2, p3) return (-1); } - /* + +/* ** SYSERR -- print system-generated error. ** ** Parameters: @@ -977,6 +1477,7 @@ usrerr(f, p1, p2, p3) ** none. */ +/*VARARGS1*/ syserr(f, p1, p2, p3) char *f; { @@ -989,7 +1490,86 @@ syserr(f, p1, p2, p3) exit(EX_SOFTWARE); else { - perror(0); + perror(NULL); + exit(EX_OSERR); + } +} + /* +** USERNAME -- return name of the current user +** +** Parameters: +** none +** +** Returns: +** name of current user +** +** Side Effects: +** none +*/ + +char * +username() +{ +# ifdef UIDUSER + extern struct passwd *getpwuid(); + register struct passwd *pw; + + pw = getpwuid(getuid()); + if (pw == NULL) + { + syserr("who are you? (uid=%d)", getuid()); exit(EX_OSERR); } + return (pw->pw_name); +# else + extern char *getlogin(); + extern char *getenv(); + register char *p; + + p = getenv("USER"); + if (p == NULL || p[0] == '\0') + p = getlogin(); + return (p); +# endif UIDUSER +} + +/* +** Guarded string manipulation routines; the last argument +** is the length of the buffer into which the strcpy or strcat +** is to be done. +*/ +char *gstrcat(to, from, length) + char *to, *from; + int length; +{ + if (strlen(from) + strlen(to) >= length) { + gstrbotch(to, from); + } + return(strcat(to, from)); +} + +char *gstrncat(to, from, n, length) + char *to, *from; + int n; + int length; +{ + if (n + strlen(to) >= length) { + gstrbotch(to, from); + } + return(strncat(to, from, n)); +} + +char *gstrcpy(to, from, length) + char *to, *from; + int length; +{ + if (strlen(from) >= length) { + gstrbotch(from, (char *)0); + } + return(strcpy(to, from)); +} +gstrbotch(str1, str2) + char *str1, *str2; +{ + usrerr("Filename(s) too long: %s %s", str1, str2); }