1adf8f7d4Seric # include <stdio.h> 2683375a0Smckusick # include <sys/param.h> 3adf8f7d4Seric # include <sys/stat.h> 4683375a0Smckusick # include <dir.h> 578d41c6aSeric # include <errno.h> 678d41c6aSeric # include <signal.h> 7adf8f7d4Seric # include <sysexits.h> 8393e27f2Seric # include <pwd.h> 9adf8f7d4Seric 1010cd16efSeric /* 1110cd16efSeric ** SCCS.C -- human-oriented front end to the SCCS system. 1210cd16efSeric ** 1310cd16efSeric ** Without trying to add any functionality to speak of, this 1410cd16efSeric ** program tries to make SCCS a little more accessible to human 1510cd16efSeric ** types. The main thing it does is automatically put the 1610cd16efSeric ** string "SCCS/s." on the front of names. Also, it has a 1710cd16efSeric ** couple of things that are designed to shorten frequent 1810cd16efSeric ** combinations, e.g., "delget" which expands to a "delta" 1910cd16efSeric ** and a "get". 2010cd16efSeric ** 2110cd16efSeric ** This program can also function as a setuid front end. 2210cd16efSeric ** To do this, you should copy the source, renaming it to 2310cd16efSeric ** whatever you want, e.g., "syssccs". Change any defaults 2410cd16efSeric ** in the program (e.g., syssccs might default -d to 2510cd16efSeric ** "/usr/src/sys"). Then recompile and put the result 2610cd16efSeric ** as setuid to whomever you want. In this mode, sccs 2710cd16efSeric ** knows to not run setuid for certain programs in order 2810cd16efSeric ** to preserve security, and so forth. 2910cd16efSeric ** 3010cd16efSeric ** Usage: 3110cd16efSeric ** sccs [flags] command [args] 3210cd16efSeric ** 3310cd16efSeric ** Flags: 3410cd16efSeric ** -d<dir> <dir> represents a directory to search 3510cd16efSeric ** out of. It should be a full pathname 3610cd16efSeric ** for general usage. E.g., if <dir> is 3710cd16efSeric ** "/usr/src/sys", then a reference to the 3810cd16efSeric ** file "dev/bio.c" becomes a reference to 3910cd16efSeric ** "/usr/src/sys/dev/bio.c". 4010cd16efSeric ** -p<path> prepends <path> to the final component 4110cd16efSeric ** of the pathname. By default, this is 4210cd16efSeric ** "SCCS". For example, in the -d example 4310cd16efSeric ** above, the path then gets modified to 4410cd16efSeric ** "/usr/src/sys/dev/SCCS/s.bio.c". In 4510cd16efSeric ** more common usage (without the -d flag), 4610cd16efSeric ** "prog.c" would get modified to 4710cd16efSeric ** "SCCS/s.prog.c". In both cases, the 4810cd16efSeric ** "s." gets automatically prepended. 4910cd16efSeric ** -r run as the real user. 5010cd16efSeric ** 5110cd16efSeric ** Commands: 5210cd16efSeric ** admin, 5310cd16efSeric ** get, 5410cd16efSeric ** delta, 5510cd16efSeric ** rmdel, 5610cd16efSeric ** chghist, 5710cd16efSeric ** etc. Straight out of SCCS; only difference 5810cd16efSeric ** is that pathnames get modified as 5910cd16efSeric ** described above. 6010cd16efSeric ** edit Macro for "get -e". 6110cd16efSeric ** unedit Removes a file being edited, knowing 6210cd16efSeric ** about p-files, etc. 6310cd16efSeric ** delget Macro for "delta" followed by "get". 6410cd16efSeric ** deledit Macro for "delta" followed by "get -e". 6510cd16efSeric ** info Tell what files being edited. 6610cd16efSeric ** clean Remove all files that can be 6710cd16efSeric ** regenerated from SCCS files. 68095915d3Seric ** check Like info, but return exit status, for 6910cd16efSeric ** use in makefiles. 7010cd16efSeric ** fix Remove a top delta & reedit, but save 7110cd16efSeric ** the previous changes in that delta. 7210cd16efSeric ** 7310cd16efSeric ** Compilation Flags: 7410cd16efSeric ** UIDUSER -- determine who the user is by looking at the 7510cd16efSeric ** uid rather than the login name -- for machines 7610cd16efSeric ** where SCCS gets the user in this way. 777abec564Seric ** SCCSDIR -- if defined, forces the -d flag to take on 78095915d3Seric ** this value. This is so that the setuid 79095915d3Seric ** aspects of this program cannot be abused. 807abec564Seric ** This flag also disables the -p flag. 817abec564Seric ** SCCSPATH -- the default for the -p flag. 82c4ad8825Seric ** MYNAME -- the title this program should print when it 83c4ad8825Seric ** gives error messages. 8410cd16efSeric ** 8510cd16efSeric ** Compilation Instructions: 8610cd16efSeric ** cc -O -n -s sccs.c 87c4ad8825Seric ** The flags listed above can be -D defined to simplify 88c4ad8825Seric ** recompilation for variant versions. 8910cd16efSeric ** 9010cd16efSeric ** Author: 9110cd16efSeric ** Eric Allman, UCB/INGRES 927abec564Seric ** Copyright 1980 Regents of the University of California 9310cd16efSeric */ 9410cd16efSeric 95*f76dd34fSrrh static char SccsId[] = "@(#)sccs.c 1.68 01/03/83"; 960faf63f9Seric 977abec564Seric /******************* Configuration Information ********************/ 987abec564Seric 99c4ad8825Seric # ifndef SCCSPATH 100c4ad8825Seric # define SCCSPATH "SCCS" /* pathname in which to find s-files */ 101c4ad8825Seric # endif NOT SCCSPATH 102c4ad8825Seric 103c4ad8825Seric # ifndef MYNAME 104c4ad8825Seric # define MYNAME "sccs" /* name used for printing errors */ 105c4ad8825Seric # endif NOT MYNAME 1067abec564Seric 1070faf63f9Seric # ifndef PROGPATH 108683375a0Smckusick # define PROGPATH(name) "/usr/local/name" /* place to find binaries */ 1090faf63f9Seric # endif PROGPATH 1100faf63f9Seric 1117abec564Seric /**************** End of Configuration Information ****************/ 1120faf63f9Seric 113c4432be4Seric typedef char bool; 1147de81dc7Seric # define TRUE 1 1157de81dc7Seric # define FALSE 0 116d02a4f42Seric 117ff038098Seric # define bitset(bit, word) ((bool) ((bit) & (word))) 118ff038098Seric 119adf8f7d4Seric struct sccsprog 120adf8f7d4Seric { 121adf8f7d4Seric char *sccsname; /* name of SCCS routine */ 1227de81dc7Seric short sccsoper; /* opcode, see below */ 1237de81dc7Seric short sccsflags; /* flags, see below */ 124adf8f7d4Seric char *sccspath; /* pathname of binary implementing */ 125adf8f7d4Seric }; 126adf8f7d4Seric 1277de81dc7Seric /* values for sccsoper */ 1287de81dc7Seric # define PROG 0 /* call a program */ 1291777fbcbSeric # define CMACRO 1 /* command substitution macro */ 130172147faSeric # define FIX 2 /* fix a delta */ 131b2538d76Seric # define CLEAN 3 /* clean out recreatable files */ 132e39a5722Seric # define UNEDIT 4 /* unedit a file */ 133419708b0Seric # define SHELL 5 /* call a shell file (like PROG) */ 13478d41c6aSeric # define DIFFS 6 /* diff between sccs & file out */ 1352569d37aSeric # define DODIFF 7 /* internal call to diff program */ 13644c626a5Srrh # define ENTER 8 /* enter new files */ 1377de81dc7Seric 138c4432be4Seric /* bits for sccsflags */ 1397de81dc7Seric # define NO_SDOT 0001 /* no s. on front of args */ 1407de81dc7Seric # define REALUSER 0002 /* protected (e.g., admin) */ 141adf8f7d4Seric 14248ff0e7eSeric /* modes for the "clean", "info", "check" ops */ 14348ff0e7eSeric # define CLEANC 0 /* clean command */ 14448ff0e7eSeric # define INFOC 1 /* info command */ 14548ff0e7eSeric # define CHECKC 2 /* check command */ 1462734e237Seric # define TELLC 3 /* give list of files being edited */ 14748ff0e7eSeric 1480faf63f9Seric /* 1490faf63f9Seric ** Description of commands known to this program. 1500faf63f9Seric ** First argument puts the command into a class. Second arg is 1510faf63f9Seric ** info regarding treatment of this command. Third arg is a 1520faf63f9Seric ** list of flags this command accepts from macros, etc. Fourth 1530faf63f9Seric ** arg is the pathname of the implementing program, or the 1540faf63f9Seric ** macro definition, or the arg to a sub-algorithm. 1550faf63f9Seric */ 156b5d4f080Seric 157adf8f7d4Seric struct sccsprog SccsProg[] = 158adf8f7d4Seric { 1593642a8d2Seric "admin", PROG, REALUSER, PROGPATH(admin), 1603642a8d2Seric "chghist", PROG, 0, PROGPATH(rmdel), 1613642a8d2Seric "comb", PROG, 0, PROGPATH(comb), 1623642a8d2Seric "delta", PROG, 0, PROGPATH(delta), 1633642a8d2Seric "get", PROG, 0, PROGPATH(get), 1643642a8d2Seric "help", PROG, NO_SDOT, PROGPATH(help), 1653a3bf7bfSeric "prs", PROG, 0, PROGPATH(prs), 1663642a8d2Seric "prt", PROG, 0, PROGPATH(prt), 1673642a8d2Seric "rmdel", PROG, REALUSER, PROGPATH(rmdel), 1683a3bf7bfSeric "val", PROG, 0, PROGPATH(val), 1693642a8d2Seric "what", PROG, NO_SDOT, PROGPATH(what), 1703642a8d2Seric "sccsdiff", SHELL, REALUSER, PROGPATH(sccsdiff), 1713642a8d2Seric "edit", CMACRO, NO_SDOT, "get -e", 17259e81a43Seric "delget", CMACRO, NO_SDOT, "delta:mysrp/get:ixbeskcl -t", 1731cf60de3Seric "deledit", CMACRO, NO_SDOT, "delta:mysrp -n/get:ixbskcl -e -t -g", 1743642a8d2Seric "fix", FIX, NO_SDOT, NULL, 1751e0924d0Seric "clean", CLEAN, REALUSER|NO_SDOT, (char *) CLEANC, 1761e0924d0Seric "info", CLEAN, REALUSER|NO_SDOT, (char *) INFOC, 1771e0924d0Seric "check", CLEAN, REALUSER|NO_SDOT, (char *) CHECKC, 1781e0924d0Seric "tell", CLEAN, REALUSER|NO_SDOT, (char *) TELLC, 1793642a8d2Seric "unedit", UNEDIT, NO_SDOT, NULL, 1803642a8d2Seric "diffs", DIFFS, NO_SDOT|REALUSER, NULL, 1812569d37aSeric "-diff", DODIFF, NO_SDOT|REALUSER, PROGPATH(bdiff), 182b387a58fSeric "print", CMACRO, 0, "prt -e/get -p -m -s", 183a4f81e73Seric "branch", CMACRO, NO_SDOT, 184a4f81e73Seric "get:ixrc -e -b/delta: -s -n -ybranch-place-holder/get:pl -e -t -g", 18544c626a5Srrh "enter", ENTER, NO_SDOT, NULL, 18644c626a5Srrh "create", CMACRO, NO_SDOT, "enter/get:ixbeskcl -t", 1873642a8d2Seric NULL, -1, 0, NULL 188adf8f7d4Seric }; 189adf8f7d4Seric 1900faf63f9Seric /* one line from a p-file */ 191e39a5722Seric struct pfile 192e39a5722Seric { 193e39a5722Seric char *p_osid; /* old SID */ 194e39a5722Seric char *p_nsid; /* new SID */ 195e39a5722Seric char *p_user; /* user who did edit */ 196e39a5722Seric char *p_date; /* date of get */ 197e39a5722Seric char *p_time; /* time of get */ 19877a8a0dfSeric char *p_aux; /* extra info at end */ 199e39a5722Seric }; 200e39a5722Seric 2017abec564Seric char *SccsPath = SCCSPATH; /* pathname of SCCS files */ 2027abec564Seric # ifdef SCCSDIR 2037abec564Seric char *SccsDir = SCCSDIR; /* directory to begin search from */ 204095915d3Seric # else 2057abec564Seric char *SccsDir = ""; 206095915d3Seric # endif 207c4ad8825Seric char MyName[] = MYNAME; /* name used in messages */ 20878d41c6aSeric int OutFile = -1; /* override output file for commands */ 209c4432be4Seric bool RealUser; /* if set, running as real user */ 210f0cc3246Seric # ifdef DEBUG 211f0cc3246Seric bool Debug; /* turn on tracing */ 212f0cc3246Seric # endif 21392fabd68Seric # ifndef V6 21492fabd68Seric extern char *getenv(); 21592fabd68Seric # endif V6 216*f76dd34fSrrh 217*f76dd34fSrrh char *gstrcat(), *strcat(); 218*f76dd34fSrrh char *gstrncat(), *strncat(); 219*f76dd34fSrrh char *gstrcpy(), *strcpy(); 220*f76dd34fSrrh #define FBUFSIZ BUFSIZ 221*f76dd34fSrrh #define PFILELG 120 2220faf63f9Seric 223adf8f7d4Seric main(argc, argv) 224adf8f7d4Seric int argc; 225adf8f7d4Seric char **argv; 226adf8f7d4Seric { 227adf8f7d4Seric register char *p; 228b1ed8a43Seric extern struct sccsprog *lookup(); 22941290c52Seric register int i; 23092fabd68Seric # ifndef V6 23192fabd68Seric # ifndef SCCSDIR 232393e27f2Seric register struct passwd *pw; 233393e27f2Seric extern struct passwd *getpwnam(); 234*f76dd34fSrrh char buf[FBUFSIZ]; 235393e27f2Seric 23692fabd68Seric /* pull "SccsDir" out of the environment (possibly) */ 23792fabd68Seric p = getenv("PROJECT"); 238393e27f2Seric if (p != NULL && p[0] != '\0') 239393e27f2Seric { 24092fabd68Seric if (p[0] == '/') 24192fabd68Seric SccsDir = p; 24292fabd68Seric else 243393e27f2Seric { 244393e27f2Seric pw = getpwnam(p); 245393e27f2Seric if (pw == NULL) 246393e27f2Seric { 247393e27f2Seric usrerr("user %s does not exist", p); 248393e27f2Seric exit(EX_USAGE); 249393e27f2Seric } 250*f76dd34fSrrh gstrcpy(buf, pw->pw_dir, sizeof(buf)); 251*f76dd34fSrrh gstrcat(buf, "/src", sizeof(buf)); 252393e27f2Seric if (access(buf, 0) < 0) 253393e27f2Seric { 254*f76dd34fSrrh gstrcpy(buf, pw->pw_dir, sizeof(buf)); 255*f76dd34fSrrh gstrcat(buf, "/source", sizeof(buf)); 256393e27f2Seric if (access(buf, 0) < 0) 257393e27f2Seric { 258393e27f2Seric usrerr("project %s has no source!", p); 259393e27f2Seric exit(EX_USAGE); 260393e27f2Seric } 261393e27f2Seric } 262393e27f2Seric SccsDir = buf; 263393e27f2Seric } 264393e27f2Seric } 26592fabd68Seric # endif SCCSDIR 26692fabd68Seric # endif V6 26792fabd68Seric 268adf8f7d4Seric /* 269adf8f7d4Seric ** Detect and decode flags intended for this program. 270adf8f7d4Seric */ 271adf8f7d4Seric 2727de81dc7Seric if (argc < 2) 273adf8f7d4Seric { 274095915d3Seric fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); 2757de81dc7Seric exit(EX_USAGE); 2767de81dc7Seric } 2777de81dc7Seric argv[argc] = NULL; 2787de81dc7Seric 279b1ed8a43Seric if (lookup(argv[0]) == NULL) 280b1ed8a43Seric { 2817de81dc7Seric while ((p = *++argv) != NULL) 2827de81dc7Seric { 283adf8f7d4Seric if (*p != '-') 284adf8f7d4Seric break; 285adf8f7d4Seric switch (*++p) 286adf8f7d4Seric { 287adf8f7d4Seric case 'r': /* run as real user */ 288adf8f7d4Seric setuid(getuid()); 289c4432be4Seric RealUser++; 290adf8f7d4Seric break; 291adf8f7d4Seric 2927abec564Seric # ifndef SCCSDIR 293adf8f7d4Seric case 'p': /* path of sccs files */ 294adf8f7d4Seric SccsPath = ++p; 2956c7454d2Seric if (SccsPath[0] == '\0' && argv[1] != NULL) 2966c7454d2Seric SccsPath = *++argv; 297adf8f7d4Seric break; 298adf8f7d4Seric 2990a8b9ab0Seric case 'd': /* directory to search from */ 3000a8b9ab0Seric SccsDir = ++p; 3016c7454d2Seric if (SccsDir[0] == '\0' && argv[1] != NULL) 3026c7454d2Seric SccsDir = *++argv; 3030a8b9ab0Seric break; 304095915d3Seric # endif 3050a8b9ab0Seric 306f0cc3246Seric # ifdef DEBUG 307f0cc3246Seric case 'T': /* trace */ 308f0cc3246Seric Debug++; 309f0cc3246Seric break; 310f0cc3246Seric # endif 311f0cc3246Seric 312adf8f7d4Seric default: 313095915d3Seric usrerr("unknown option -%s", p); 314adf8f7d4Seric break; 315adf8f7d4Seric } 316adf8f7d4Seric } 3175cabffd9Seric if (SccsPath[0] == '\0') 3185cabffd9Seric SccsPath = "."; 319b1ed8a43Seric } 320adf8f7d4Seric 3213642a8d2Seric i = command(argv, FALSE, ""); 32241290c52Seric exit(i); 3237de81dc7Seric } 3240faf63f9Seric 3250faf63f9Seric /* 32641290c52Seric ** COMMAND -- look up and perform a command 32741290c52Seric ** 32841290c52Seric ** This routine is the guts of this program. Given an 32941290c52Seric ** argument vector, it looks up the "command" (argv[0]) 33041290c52Seric ** in the configuration table and does the necessary stuff. 33141290c52Seric ** 33241290c52Seric ** Parameters: 33341290c52Seric ** argv -- an argument vector to process. 33441290c52Seric ** forkflag -- if set, fork before executing the command. 335108d6082Seric ** editflag -- if set, only include flags listed in the 336108d6082Seric ** sccsklets field of the command descriptor. 337108d6082Seric ** arg0 -- a space-seperated list of arguments to insert 338108d6082Seric ** before argv. 33941290c52Seric ** 34041290c52Seric ** Returns: 34141290c52Seric ** zero -- command executed ok. 34241290c52Seric ** else -- error status. 34341290c52Seric ** 34441290c52Seric ** Side Effects: 34541290c52Seric ** none. 34641290c52Seric */ 3477de81dc7Seric 3483642a8d2Seric command(argv, forkflag, arg0) 3497de81dc7Seric char **argv; 3501777fbcbSeric bool forkflag; 351108d6082Seric char *arg0; 3527de81dc7Seric { 3537de81dc7Seric register struct sccsprog *cmd; 3547de81dc7Seric register char *p; 355*f76dd34fSrrh char buf[FBUFSIZ]; 356b1ed8a43Seric extern struct sccsprog *lookup(); 357108d6082Seric char *nav[1000]; 358108d6082Seric char **np; 359419708b0Seric register char **ap; 360d51cd7e5Seric register int i; 361419708b0Seric register char *q; 362d51cd7e5Seric extern bool unedit(); 36341290c52Seric int rval = 0; 364108d6082Seric extern char *index(); 365108d6082Seric extern char *makefile(); 3663642a8d2Seric char *editchs; 367287bbaa2Seric extern char *tail(); 368f0cc3246Seric 369f0cc3246Seric # ifdef DEBUG 370f0cc3246Seric if (Debug) 371f0cc3246Seric { 372108d6082Seric printf("command:\n\t\"%s\"\n", arg0); 373108d6082Seric for (np = argv; *np != NULL; np++) 374108d6082Seric printf("\t\"%s\"\n", *np); 375f0cc3246Seric } 376f0cc3246Seric # endif 377c4432be4Seric 378c4432be4Seric /* 379108d6082Seric ** Copy arguments. 380ff038098Seric ** Copy from arg0 & if necessary at most one arg 381ff038098Seric ** from argv[0]. 382adf8f7d4Seric */ 383adf8f7d4Seric 384419708b0Seric np = ap = &nav[1]; 3853642a8d2Seric editchs = NULL; 38659e81a43Seric for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) 387108d6082Seric { 388108d6082Seric *np++ = q; 389108d6082Seric while (*p == ' ') 390108d6082Seric p++; 3913642a8d2Seric while (*p != ' ' && *p != '\0' && *p != '/' && *p != ':') 392108d6082Seric *q++ = *p++; 393108d6082Seric *q++ = '\0'; 3943642a8d2Seric if (*p == ':') 3953642a8d2Seric { 3963642a8d2Seric editchs = q; 39759e81a43Seric while (*++p != '\0' && *p != '/' && *p != ' ') 3983642a8d2Seric *q++ = *p; 3993642a8d2Seric *q++ = '\0'; 4003642a8d2Seric } 401108d6082Seric } 402108d6082Seric *np = NULL; 403419708b0Seric if (*ap == NULL) 404108d6082Seric *np++ = *argv++; 405108d6082Seric 406108d6082Seric /* 407108d6082Seric ** Look up command. 408419708b0Seric ** At this point, *ap is the command name. 409108d6082Seric */ 410108d6082Seric 411419708b0Seric cmd = lookup(*ap); 412b1ed8a43Seric if (cmd == NULL) 413adf8f7d4Seric { 414419708b0Seric usrerr("Unknown command \"%s\"", *ap); 41541290c52Seric return (EX_USAGE); 416adf8f7d4Seric } 417adf8f7d4Seric 418adf8f7d4Seric /* 419108d6082Seric ** Copy remaining arguments doing editing as appropriate. 420108d6082Seric */ 421108d6082Seric 422108d6082Seric for (; *argv != NULL; argv++) 423108d6082Seric { 424108d6082Seric p = *argv; 425108d6082Seric if (*p == '-') 426108d6082Seric { 4273642a8d2Seric if (p[1] == '\0' || editchs == NULL || index(editchs, p[1]) != NULL) 428108d6082Seric *np++ = p; 429108d6082Seric } 430108d6082Seric else 431108d6082Seric { 432108d6082Seric if (!bitset(NO_SDOT, cmd->sccsflags)) 433108d6082Seric p = makefile(p); 434108d6082Seric if (p != NULL) 435108d6082Seric *np++ = p; 436108d6082Seric } 437108d6082Seric } 438108d6082Seric *np = NULL; 439108d6082Seric 440108d6082Seric /* 4417de81dc7Seric ** Interpret operation associated with this command. 442c4432be4Seric */ 443c4432be4Seric 4447de81dc7Seric switch (cmd->sccsoper) 4457de81dc7Seric { 446419708b0Seric case SHELL: /* call a shell file */ 447419708b0Seric *ap = cmd->sccspath; 448419708b0Seric *--ap = "sh"; 449419708b0Seric rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag); 450419708b0Seric break; 451419708b0Seric 4527de81dc7Seric case PROG: /* call an sccs prog */ 453419708b0Seric rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); 4541777fbcbSeric break; 4551777fbcbSeric 4561777fbcbSeric case CMACRO: /* command macro */ 457ff038098Seric /* step through & execute each part of the macro */ 4581777fbcbSeric for (p = cmd->sccspath; *p != '\0'; p++) 4591777fbcbSeric { 460108d6082Seric q = p; 461108d6082Seric while (*p != '\0' && *p != '/') 462108d6082Seric p++; 4633642a8d2Seric rval = command(&ap[1], *p != '\0', q); 46441290c52Seric if (rval != 0) 46541290c52Seric break; 4661777fbcbSeric } 46741290c52Seric break; 4687de81dc7Seric 469172147faSeric case FIX: /* fix a delta */ 470419708b0Seric if (strncmp(ap[1], "-r", 2) != 0) 471172147faSeric { 472095915d3Seric usrerr("-r flag needed for fix command"); 47341290c52Seric rval = EX_USAGE; 474172147faSeric break; 475172147faSeric } 476ff038098Seric 477ff038098Seric /* get the version with all changes */ 4783642a8d2Seric rval = command(&ap[1], TRUE, "get -k"); 479ff038098Seric 480ff038098Seric /* now remove that version from the s-file */ 48141290c52Seric if (rval == 0) 4823642a8d2Seric rval = command(&ap[1], TRUE, "rmdel:r"); 483ff038098Seric 484ff038098Seric /* and edit the old version (but don't clobber new vers) */ 48541290c52Seric if (rval == 0) 4863642a8d2Seric rval = command(&ap[2], FALSE, "get -e -g"); 48741290c52Seric break; 488172147faSeric 489b2538d76Seric case CLEAN: 490ec4190ddSeric rval = clean((int) cmd->sccspath, ap); 491b2538d76Seric break; 492b2538d76Seric 493e39a5722Seric case UNEDIT: 494419708b0Seric for (argv = np = &ap[1]; *argv != NULL; argv++) 495d51cd7e5Seric { 496108d6082Seric if (unedit(*argv)) 497108d6082Seric *np++ = *argv; 498d51cd7e5Seric } 499108d6082Seric *np = NULL; 500ff038098Seric 501ff038098Seric /* get all the files that we unedited successfully */ 502fe7b004aSeric if (np > &ap[1]) 5033642a8d2Seric rval = command(&ap[1], FALSE, "get"); 504e39a5722Seric break; 505e39a5722Seric 50678d41c6aSeric case DIFFS: /* diff between s-file & edit file */ 50778d41c6aSeric /* find the end of the flag arguments */ 50878d41c6aSeric for (np = &ap[1]; *np != NULL && **np == '-'; np++) 50978d41c6aSeric continue; 51078d41c6aSeric argv = np; 51178d41c6aSeric 51278d41c6aSeric /* for each file, do the diff */ 51396370756Seric p = argv[1]; 51478d41c6aSeric while (*np != NULL) 51578d41c6aSeric { 516ff038098Seric /* messy, but we need a null terminated argv */ 51778d41c6aSeric *argv = *np++; 51896370756Seric argv[1] = NULL; 519287bbaa2Seric i = dodiff(ap, tail(*argv)); 52078d41c6aSeric if (rval == 0) 52178d41c6aSeric rval = i; 52296370756Seric argv[1] = p; 52378d41c6aSeric } 52478d41c6aSeric break; 52578d41c6aSeric 5262569d37aSeric case DODIFF: /* internal diff call */ 5272569d37aSeric setuid(getuid()); 5282569d37aSeric for (np = ap; *np != NULL; np++) 5292569d37aSeric { 5302569d37aSeric if ((*np)[0] == '-' && (*np)[1] == 'C') 5312569d37aSeric (*np)[1] = 'c'; 5322569d37aSeric } 5332569d37aSeric 5342569d37aSeric /* insert "-" argument */ 5352569d37aSeric np[1] = NULL; 5362569d37aSeric np[0] = np[-1]; 5372569d37aSeric np[-1] = "-"; 5382569d37aSeric 5392569d37aSeric /* execute the diff program of choice */ 5402569d37aSeric # ifndef V6 5412569d37aSeric execvp("diff", ap); 5422569d37aSeric # endif 5432569d37aSeric execv(cmd->sccspath, argv); 5442569d37aSeric syserr("cannot exec %s", cmd->sccspath); 5452569d37aSeric exit(EX_OSERR); 5462569d37aSeric 54744c626a5Srrh case ENTER: /* enter new sccs files */ 548bb5509cbSmckusick /* skip over flag arguments */ 549bb5509cbSmckusick for (np = &ap[1]; *np != NULL && **np == '-'; np++) 550bb5509cbSmckusick continue; 551bb5509cbSmckusick argv = np; 552bb5509cbSmckusick 553bb5509cbSmckusick /* do an admin for each file */ 554bb5509cbSmckusick p = argv[1]; 555bb5509cbSmckusick while (*np != NULL) 556bb5509cbSmckusick { 557bb5509cbSmckusick printf("\n%s:\n", *np); 558*f76dd34fSrrh strcpy(buf, "-i"); 559*f76dd34fSrrh gstrcat(buf, *np, sizeof(buf)); 560bb5509cbSmckusick ap[0] = buf; 561bb5509cbSmckusick argv[0] = tail(*np); 562bb5509cbSmckusick argv[1] = NULL; 563bb5509cbSmckusick rval = command(ap, TRUE, "admin"); 564bb5509cbSmckusick argv[1] = p; 565bb5509cbSmckusick if (rval == 0) 566bb5509cbSmckusick { 567*f76dd34fSrrh strcpy(buf, ","); 568*f76dd34fSrrh gstrcat(buf, tail(*np), sizeof(buf)); 569bb5509cbSmckusick if (link(*np, buf) >= 0) 570bb5509cbSmckusick unlink(*np); 571bb5509cbSmckusick } 572bb5509cbSmckusick np++; 573bb5509cbSmckusick } 574bb5509cbSmckusick break; 575bb5509cbSmckusick 5767de81dc7Seric default: 577095915d3Seric syserr("oper %d", cmd->sccsoper); 5787de81dc7Seric exit(EX_SOFTWARE); 5797de81dc7Seric } 58041290c52Seric # ifdef DEBUG 58141290c52Seric if (Debug) 58241290c52Seric printf("command: rval=%d\n", rval); 58341290c52Seric # endif 58441290c52Seric return (rval); 5857de81dc7Seric } 5860faf63f9Seric 5870faf63f9Seric /* 588b1ed8a43Seric ** LOOKUP -- look up an SCCS command name. 589b1ed8a43Seric ** 590b1ed8a43Seric ** Parameters: 591b1ed8a43Seric ** name -- the name of the command to look up. 592b1ed8a43Seric ** 593b1ed8a43Seric ** Returns: 594b1ed8a43Seric ** ptr to command descriptor for this command. 595b1ed8a43Seric ** NULL if no such entry. 596b1ed8a43Seric ** 597b1ed8a43Seric ** Side Effects: 598b1ed8a43Seric ** none. 599b1ed8a43Seric */ 600b1ed8a43Seric 601b1ed8a43Seric struct sccsprog * 602b1ed8a43Seric lookup(name) 603b1ed8a43Seric char *name; 604b1ed8a43Seric { 605b1ed8a43Seric register struct sccsprog *cmd; 606b1ed8a43Seric 607b1ed8a43Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 608b1ed8a43Seric { 609b1ed8a43Seric if (strcmp(cmd->sccsname, name) == 0) 610b1ed8a43Seric return (cmd); 611b1ed8a43Seric } 612b1ed8a43Seric return (NULL); 613b1ed8a43Seric } 6140faf63f9Seric 6150faf63f9Seric /* 61641290c52Seric ** CALLPROG -- call a program 61741290c52Seric ** 618108d6082Seric ** Used to call the SCCS programs. 61941290c52Seric ** 62041290c52Seric ** Parameters: 62141290c52Seric ** progpath -- pathname of the program to call. 62241290c52Seric ** flags -- status flags from the command descriptors. 62341290c52Seric ** argv -- an argument vector to pass to the program. 62441290c52Seric ** forkflag -- if true, fork before calling, else just 62541290c52Seric ** exec. 62641290c52Seric ** 62741290c52Seric ** Returns: 62841290c52Seric ** The exit status of the program. 62941290c52Seric ** Nothing if forkflag == FALSE. 63041290c52Seric ** 63141290c52Seric ** Side Effects: 63241290c52Seric ** Can exit if forkflag == FALSE. 63341290c52Seric */ 634172147faSeric 6357de81dc7Seric callprog(progpath, flags, argv, forkflag) 6367de81dc7Seric char *progpath; 6377de81dc7Seric short flags; 6387de81dc7Seric char **argv; 6397de81dc7Seric bool forkflag; 6407de81dc7Seric { 6417de81dc7Seric register int i; 6421777fbcbSeric auto int st; 643108d6082Seric 644108d6082Seric # ifdef DEBUG 645108d6082Seric if (Debug) 646108d6082Seric { 647108d6082Seric printf("callprog:\n"); 648108d6082Seric for (i = 0; argv[i] != NULL; i++) 649108d6082Seric printf("\t\"%s\"\n", argv[i]); 650108d6082Seric } 651108d6082Seric # endif 6527de81dc7Seric 6537de81dc7Seric if (*argv == NULL) 6547de81dc7Seric return (-1); 655c4432be4Seric 656c4432be4Seric /* 657172147faSeric ** Fork if appropriate. 658adf8f7d4Seric */ 659adf8f7d4Seric 6607de81dc7Seric if (forkflag) 6617de81dc7Seric { 662f0cc3246Seric # ifdef DEBUG 663f0cc3246Seric if (Debug) 664f0cc3246Seric printf("Forking\n"); 665f0cc3246Seric # endif 6667de81dc7Seric i = fork(); 6677de81dc7Seric if (i < 0) 6687de81dc7Seric { 669095915d3Seric syserr("cannot fork"); 6707de81dc7Seric exit(EX_OSERR); 6717de81dc7Seric } 6727de81dc7Seric else if (i > 0) 6731777fbcbSeric { 6741777fbcbSeric wait(&st); 67541290c52Seric if ((st & 0377) == 0) 67641290c52Seric st = (st >> 8) & 0377; 67778d41c6aSeric if (OutFile >= 0) 67878d41c6aSeric { 67978d41c6aSeric close(OutFile); 68078d41c6aSeric OutFile = -1; 68178d41c6aSeric } 6821777fbcbSeric return (st); 6831777fbcbSeric } 6847de81dc7Seric } 68578d41c6aSeric else if (OutFile >= 0) 68678d41c6aSeric { 68778d41c6aSeric syserr("callprog: setting stdout w/o forking"); 68878d41c6aSeric exit(EX_SOFTWARE); 68978d41c6aSeric } 6907de81dc7Seric 69178d41c6aSeric /* set protection as appropriate */ 6927de81dc7Seric if (bitset(REALUSER, flags)) 6937de81dc7Seric setuid(getuid()); 6947de81dc7Seric 69578d41c6aSeric /* change standard input & output if needed */ 69678d41c6aSeric if (OutFile >= 0) 69778d41c6aSeric { 69878d41c6aSeric close(1); 69978d41c6aSeric dup(OutFile); 70078d41c6aSeric close(OutFile); 70178d41c6aSeric } 7027de81dc7Seric 70378d41c6aSeric /* call real SCCS program */ 704172147faSeric execv(progpath, argv); 705095915d3Seric syserr("cannot execute %s", progpath); 706adf8f7d4Seric exit(EX_UNAVAILABLE); 707fe7b004aSeric /*NOTREACHED*/ 708adf8f7d4Seric } 7090faf63f9Seric 7100faf63f9Seric /* 711cdc1aa65Seric ** MAKEFILE -- make filename of SCCS file 712cdc1aa65Seric ** 713cdc1aa65Seric ** If the name passed is already the name of an SCCS file, 714cdc1aa65Seric ** just return it. Otherwise, munge the name into the name 715cdc1aa65Seric ** of the actual SCCS file. 716cdc1aa65Seric ** 717cdc1aa65Seric ** There are cases when it is not clear what you want to 718cdc1aa65Seric ** do. For example, if SccsPath is an absolute pathname 719cdc1aa65Seric ** and the name given is also an absolute pathname, we go 720cdc1aa65Seric ** for SccsPath (& only use the last component of the name 721cdc1aa65Seric ** passed) -- this is important for security reasons (if 722cdc1aa65Seric ** sccs is being used as a setuid front end), but not 723cdc1aa65Seric ** particularly intuitive. 724cdc1aa65Seric ** 725cdc1aa65Seric ** Parameters: 726cdc1aa65Seric ** name -- the file name to be munged. 727cdc1aa65Seric ** 728cdc1aa65Seric ** Returns: 729cdc1aa65Seric ** The pathname of the sccs file. 730cdc1aa65Seric ** NULL on error. 731cdc1aa65Seric ** 732cdc1aa65Seric ** Side Effects: 733cdc1aa65Seric ** none. 734cdc1aa65Seric */ 735adf8f7d4Seric 736adf8f7d4Seric char * 737adf8f7d4Seric makefile(name) 738adf8f7d4Seric char *name; 739adf8f7d4Seric { 740adf8f7d4Seric register char *p; 741*f76dd34fSrrh char buf[3*FBUFSIZ]; 742adf8f7d4Seric extern char *malloc(); 743cdc1aa65Seric extern char *rindex(); 7440a8b9ab0Seric extern bool safepath(); 7457d70f5d8Seric extern bool isdir(); 7467d70f5d8Seric register char *q; 747cdc1aa65Seric 748cdc1aa65Seric p = rindex(name, '/'); 749cdc1aa65Seric if (p == NULL) 750cdc1aa65Seric p = name; 751cdc1aa65Seric else 752cdc1aa65Seric p++; 753adf8f7d4Seric 754adf8f7d4Seric /* 7550a8b9ab0Seric ** Check to see that the path is "safe", i.e., that we 7560a8b9ab0Seric ** are not letting some nasty person use the setuid part 7570a8b9ab0Seric ** of this program to look at or munge some presumably 7580a8b9ab0Seric ** hidden files. 759adf8f7d4Seric */ 760adf8f7d4Seric 7610a8b9ab0Seric if (SccsDir[0] == '/' && !safepath(name)) 7620a8b9ab0Seric return (NULL); 763cdc1aa65Seric 764cdc1aa65Seric /* 7650a8b9ab0Seric ** Create the base pathname. 766cdc1aa65Seric */ 767cdc1aa65Seric 768ff038098Seric /* first the directory part */ 7690a8b9ab0Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) 770adf8f7d4Seric { 771*f76dd34fSrrh gstrcpy(buf, SccsDir, sizeof(buf)); 772*f76dd34fSrrh gstrcat(buf, "/", sizeof(buf)); 773cdc1aa65Seric } 774cdc1aa65Seric else 775*f76dd34fSrrh gstrcpy(buf, "", sizeof(buf)); 776ff038098Seric 777ff038098Seric /* then the head of the pathname */ 778*f76dd34fSrrh gstrncat(buf, name, p - name, sizeof(buf)); 7797d70f5d8Seric q = &buf[strlen(buf)]; 780ff038098Seric 781ff038098Seric /* now copy the final part of the name, in case useful */ 782*f76dd34fSrrh gstrcpy(q, p, sizeof(buf)); 783ff038098Seric 784ff038098Seric /* so is it useful? */ 7857d70f5d8Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 7867d70f5d8Seric { 787ff038098Seric /* sorry, no; copy the SCCS pathname & the "s." */ 788*f76dd34fSrrh gstrcpy(q, SccsPath, sizeof(buf)); 789*f76dd34fSrrh gstrcat(buf, "/s.", sizeof(buf)); 790ff038098Seric 791ff038098Seric /* and now the end of the name */ 792*f76dd34fSrrh gstrcat(buf, p, sizeof(buf)); 793cdc1aa65Seric } 794adf8f7d4Seric 795ff038098Seric /* if i haven't changed it, why did I do all this? */ 7960a8b9ab0Seric if (strcmp(buf, name) == 0) 7970a8b9ab0Seric p = name; 7980a8b9ab0Seric else 7990a8b9ab0Seric { 800ff038098Seric /* but if I have, squirrel it away */ 801adf8f7d4Seric p = malloc(strlen(buf) + 1); 802adf8f7d4Seric if (p == NULL) 803adf8f7d4Seric { 804adf8f7d4Seric perror("Sccs: no mem"); 805adf8f7d4Seric exit(EX_OSERR); 806adf8f7d4Seric } 807adf8f7d4Seric strcpy(p, buf); 8080a8b9ab0Seric } 809ff038098Seric 810adf8f7d4Seric return (p); 811adf8f7d4Seric } 8120faf63f9Seric 8130faf63f9Seric /* 8147d70f5d8Seric ** ISDIR -- return true if the argument is a directory. 8157d70f5d8Seric ** 8167d70f5d8Seric ** Parameters: 8177d70f5d8Seric ** name -- the pathname of the file to check. 8187d70f5d8Seric ** 8197d70f5d8Seric ** Returns: 8207d70f5d8Seric ** TRUE if 'name' is a directory, FALSE otherwise. 8217d70f5d8Seric ** 8227d70f5d8Seric ** Side Effects: 8237d70f5d8Seric ** none. 8247d70f5d8Seric */ 8257d70f5d8Seric 8267d70f5d8Seric bool 8277d70f5d8Seric isdir(name) 8287d70f5d8Seric char *name; 8297d70f5d8Seric { 8307d70f5d8Seric struct stat stbuf; 8317d70f5d8Seric 8327d70f5d8Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 8337d70f5d8Seric } 8340faf63f9Seric 8350faf63f9Seric /* 836cdc1aa65Seric ** SAFEPATH -- determine whether a pathname is "safe" 837cdc1aa65Seric ** 838cdc1aa65Seric ** "Safe" pathnames only allow you to get deeper into the 839cdc1aa65Seric ** directory structure, i.e., full pathnames and ".." are 840cdc1aa65Seric ** not allowed. 841cdc1aa65Seric ** 842cdc1aa65Seric ** Parameters: 843cdc1aa65Seric ** p -- the name to check. 844cdc1aa65Seric ** 845cdc1aa65Seric ** Returns: 846cdc1aa65Seric ** TRUE -- if the path is safe. 847cdc1aa65Seric ** FALSE -- if the path is not safe. 848cdc1aa65Seric ** 849cdc1aa65Seric ** Side Effects: 850cdc1aa65Seric ** Prints a message if the path is not safe. 851cdc1aa65Seric */ 852cdc1aa65Seric 853cdc1aa65Seric bool 854cdc1aa65Seric safepath(p) 855cdc1aa65Seric register char *p; 856cdc1aa65Seric { 857cdc1aa65Seric extern char *index(); 858cdc1aa65Seric 859cdc1aa65Seric if (*p != '/') 860cdc1aa65Seric { 861cdc1aa65Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 862cdc1aa65Seric { 863cdc1aa65Seric p = index(p, '/'); 864cdc1aa65Seric if (p == NULL) 865cdc1aa65Seric return (TRUE); 866cdc1aa65Seric p++; 867cdc1aa65Seric } 868cdc1aa65Seric } 869cdc1aa65Seric 870cdc1aa65Seric printf("You may not use full pathnames or \"..\"\n"); 871cdc1aa65Seric return (FALSE); 872cdc1aa65Seric } 8730faf63f9Seric 8740faf63f9Seric /* 875b2538d76Seric ** CLEAN -- clean out recreatable files 876b2538d76Seric ** 877b2538d76Seric ** Any file for which an "s." file exists but no "p." file 878b2538d76Seric ** exists in the current directory is purged. 879b2538d76Seric ** 880b2538d76Seric ** Parameters: 881ec4190ddSeric ** mode -- tells whether this came from a "clean", "info", or 88248ff0e7eSeric ** "check" command. 883ec4190ddSeric ** argv -- the rest of the argument vector. 884b2538d76Seric ** 885b2538d76Seric ** Returns: 886b2538d76Seric ** none. 887b2538d76Seric ** 888b2538d76Seric ** Side Effects: 88948ff0e7eSeric ** Removes files in the current directory. 89048ff0e7eSeric ** Prints information regarding files being edited. 89148ff0e7eSeric ** Exits if a "check" command. 892b2538d76Seric */ 893b2538d76Seric 894ec4190ddSeric clean(mode, argv) 89548ff0e7eSeric int mode; 896ec4190ddSeric char **argv; 897b2538d76Seric { 898683375a0Smckusick struct direct *dir; 899*f76dd34fSrrh char buf[FBUFSIZ]; 900393e27f2Seric char *bufend; 901683375a0Smckusick register DIR *dirfd; 9023a208c77Seric register char *basefile; 9032caac8fdSeric bool gotedit; 904ec4190ddSeric bool gotpfent; 905bf9c1e66Seric FILE *pfp; 906ec4190ddSeric bool nobranch = FALSE; 907ec4190ddSeric extern struct pfile *getpfent(); 908ec4190ddSeric register struct pfile *pf; 909ec4190ddSeric register char **ap; 9101e0924d0Seric extern char *username(); 9111e0924d0Seric char *usernm = NULL; 912393e27f2Seric char *subdir = NULL; 913393e27f2Seric char *cmdname; 914ec4190ddSeric 915ec4190ddSeric /* 916ec4190ddSeric ** Process the argv 917ec4190ddSeric */ 918ec4190ddSeric 919393e27f2Seric cmdname = *argv; 920393e27f2Seric for (ap = argv; *++ap != NULL; ) 921ec4190ddSeric { 9221e0924d0Seric if (**ap == '-') 9231e0924d0Seric { 9241e0924d0Seric /* we have a flag */ 9251e0924d0Seric switch ((*ap)[1]) 9261e0924d0Seric { 9271e0924d0Seric case 'b': 928ec4190ddSeric nobranch = TRUE; 9291e0924d0Seric break; 9301e0924d0Seric 9311e0924d0Seric case 'u': 9321e0924d0Seric if ((*ap)[2] != '\0') 9331e0924d0Seric usernm = &(*ap)[2]; 9341e0924d0Seric else if (ap[1] != NULL && ap[1][0] != '-') 9351e0924d0Seric usernm = *++ap; 9361e0924d0Seric else 9371e0924d0Seric usernm = username(); 9381e0924d0Seric break; 9391e0924d0Seric } 9401e0924d0Seric } 941393e27f2Seric else 942393e27f2Seric { 943393e27f2Seric if (subdir != NULL) 944393e27f2Seric usrerr("too many args"); 945393e27f2Seric else 946393e27f2Seric subdir = *ap; 947393e27f2Seric } 948ec4190ddSeric } 949b2538d76Seric 950ff038098Seric /* 951ff038098Seric ** Find and open the SCCS directory. 952ff038098Seric */ 953ff038098Seric 954*f76dd34fSrrh gstrcpy(buf, SccsDir, sizeof(buf)); 955f77a08fbSeric if (buf[0] != '\0') 956*f76dd34fSrrh gstrcat(buf, "/", sizeof(buf)); 957393e27f2Seric if (subdir != NULL) 958393e27f2Seric { 959*f76dd34fSrrh gstrcat(buf, subdir, sizeof(buf)); 960*f76dd34fSrrh gstrcat(buf, "/", sizeof(buf)); 961393e27f2Seric } 962*f76dd34fSrrh gstrcat(buf, SccsPath, sizeof(buf)); 963393e27f2Seric bufend = &buf[strlen(buf)]; 964ff038098Seric 965683375a0Smckusick dirfd = opendir(buf); 966b2538d76Seric if (dirfd == NULL) 967b2538d76Seric { 968f77a08fbSeric usrerr("cannot open %s", buf); 96941290c52Seric return (EX_NOINPUT); 970b2538d76Seric } 971b2538d76Seric 972b2538d76Seric /* 973b2538d76Seric ** Scan the SCCS directory looking for s. files. 974ff038098Seric ** gotedit tells whether we have tried to clean any 975ff038098Seric ** files that are being edited. 976b2538d76Seric */ 977b2538d76Seric 9782caac8fdSeric gotedit = FALSE; 979683375a0Smckusick while (dir = readdir(dirfd)) { 980683375a0Smckusick if (strncmp(dir->d_name, "s.", 2) != 0) 981b2538d76Seric continue; 982b2538d76Seric 983b2538d76Seric /* got an s. file -- see if the p. file exists */ 984*f76dd34fSrrh gstrcpy(bufend, "/p.", sizeof(buf)); 985393e27f2Seric basefile = bufend + 3; 986*f76dd34fSrrh gstrcpy(basefile, &dir->d_name[2], sizeof(buf)); 987ec4190ddSeric 988ec4190ddSeric /* 989ec4190ddSeric ** open and scan the p-file. 990ec4190ddSeric ** 'gotpfent' tells if we have found a valid p-file 991ec4190ddSeric ** entry. 992ec4190ddSeric */ 993ec4190ddSeric 994bf9c1e66Seric pfp = fopen(buf, "r"); 995ec4190ddSeric gotpfent = FALSE; 996bf9c1e66Seric if (pfp != NULL) 9973a208c77Seric { 998ff038098Seric /* the file exists -- report it's contents */ 999ec4190ddSeric while ((pf = getpfent(pfp)) != NULL) 10002734e237Seric { 1001ec4190ddSeric if (nobranch && isbranch(pf->p_nsid)) 1002ec4190ddSeric continue; 10031e0924d0Seric if (usernm != NULL && strcmp(usernm, pf->p_user) != 0 && mode != CLEANC) 10041e0924d0Seric continue; 1005ec4190ddSeric gotedit = TRUE; 1006ec4190ddSeric gotpfent = TRUE; 1007ec4190ddSeric if (mode == TELLC) 1008ec4190ddSeric { 1009ec4190ddSeric printf("%s\n", basefile); 1010ec4190ddSeric break; 1011ec4190ddSeric } 101277a8a0dfSeric printf("%12s: being edited: ", basefile); 101377a8a0dfSeric putpfent(pf, stdout); 10142734e237Seric } 1015bf9c1e66Seric fclose(pfp); 10163a208c77Seric } 1017b2538d76Seric 1018b2538d76Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 10199d7ed7d6Seric if (mode == CLEANC && !gotpfent) 10203a208c77Seric { 1021*f76dd34fSrrh char unlinkbuf[FBUFSIZ]; 1022*f76dd34fSrrh gstrcpy(unlinkbuf, &dir->d_name[2], sizeof(unlinkbuf)); 10230770051cSrrh unlink(unlinkbuf); 1024b2538d76Seric } 10253a208c77Seric } 1026b2538d76Seric 1027ff038098Seric /* cleanup & report results */ 1028683375a0Smckusick closedir(dirfd); 102948ff0e7eSeric if (!gotedit && mode == INFOC) 10301e0924d0Seric { 10311e0924d0Seric printf("Nothing being edited"); 10321e0924d0Seric if (nobranch) 10331e0924d0Seric printf(" (on trunk)"); 10341e0924d0Seric if (usernm == NULL) 10351e0924d0Seric printf("\n"); 10361e0924d0Seric else 10371e0924d0Seric printf(" by %s\n", usernm); 10381e0924d0Seric } 103948ff0e7eSeric if (mode == CHECKC) 104048ff0e7eSeric exit(gotedit); 104141290c52Seric return (EX_OK); 1042b2538d76Seric } 10430faf63f9Seric 10440faf63f9Seric /* 1045ec4190ddSeric ** ISBRANCH -- is the SID a branch? 1046ec4190ddSeric ** 1047ec4190ddSeric ** Parameters: 1048ec4190ddSeric ** sid -- the sid to check. 1049ec4190ddSeric ** 1050ec4190ddSeric ** Returns: 1051ec4190ddSeric ** TRUE if the sid represents a branch. 1052ec4190ddSeric ** FALSE otherwise. 1053ec4190ddSeric ** 1054ec4190ddSeric ** Side Effects: 1055ec4190ddSeric ** none. 1056ec4190ddSeric */ 1057ec4190ddSeric 1058ec4190ddSeric isbranch(sid) 1059ec4190ddSeric char *sid; 1060ec4190ddSeric { 1061ec4190ddSeric register char *p; 1062ec4190ddSeric int dots; 1063ec4190ddSeric 1064ec4190ddSeric dots = 0; 1065ec4190ddSeric for (p = sid; *p != '\0'; p++) 1066ec4190ddSeric { 1067ec4190ddSeric if (*p == '.') 1068ec4190ddSeric dots++; 1069ec4190ddSeric if (dots > 1) 1070ec4190ddSeric return (TRUE); 1071ec4190ddSeric } 1072ec4190ddSeric return (FALSE); 1073ec4190ddSeric } 1074ec4190ddSeric 1075ec4190ddSeric /* 1076e39a5722Seric ** UNEDIT -- unedit a file 1077e39a5722Seric ** 1078e39a5722Seric ** Checks to see that the current user is actually editting 1079e39a5722Seric ** the file and arranges that s/he is not editting it. 1080e39a5722Seric ** 1081e39a5722Seric ** Parameters: 10822444cd3eSeric ** fn -- the name of the file to be unedited. 1083e39a5722Seric ** 1084e39a5722Seric ** Returns: 1085d51cd7e5Seric ** TRUE -- if the file was successfully unedited. 1086d51cd7e5Seric ** FALSE -- if the file was not unedited for some 1087d51cd7e5Seric ** reason. 1088e39a5722Seric ** 1089e39a5722Seric ** Side Effects: 1090e39a5722Seric ** fn is removed 1091e39a5722Seric ** entries are removed from pfile. 1092e39a5722Seric */ 1093e39a5722Seric 1094d51cd7e5Seric bool 1095e39a5722Seric unedit(fn) 1096e39a5722Seric char *fn; 1097e39a5722Seric { 1098e39a5722Seric register FILE *pfp; 1099e39a5722Seric char *pfn; 1100e39a5722Seric static char tfn[] = "/tmp/sccsXXXXX"; 1101e39a5722Seric FILE *tfp; 1102e39a5722Seric register char *q; 1103e39a5722Seric bool delete = FALSE; 1104e39a5722Seric bool others = FALSE; 1105e39a5722Seric char *myname; 11061e0924d0Seric extern char *username(); 1107e39a5722Seric struct pfile *pent; 1108ec4190ddSeric extern struct pfile *getpfent(); 1109*f76dd34fSrrh char buf[PFILELG]; 1110108d6082Seric extern char *makefile(); 1111e39a5722Seric 1112e39a5722Seric /* make "s." filename & find the trailing component */ 1113e39a5722Seric pfn = makefile(fn); 1114cdc1aa65Seric if (pfn == NULL) 1115cdc1aa65Seric return (FALSE); 1116cdc1aa65Seric q = rindex(pfn, '/'); 1117cdc1aa65Seric if (q == NULL) 1118cdc1aa65Seric q = &pfn[-1]; 1119cdc1aa65Seric if (q[1] != 's' || q[2] != '.') 1120e39a5722Seric { 1121095915d3Seric usrerr("bad file name \"%s\"", fn); 1122d51cd7e5Seric return (FALSE); 1123e39a5722Seric } 1124e39a5722Seric 1125ff038098Seric /* turn "s." into "p." & try to open it */ 1126e39a5722Seric *++q = 'p'; 1127e39a5722Seric 1128e39a5722Seric pfp = fopen(pfn, "r"); 1129e39a5722Seric if (pfp == NULL) 1130e39a5722Seric { 11312444cd3eSeric printf("%12s: not being edited\n", fn); 1132d51cd7e5Seric return (FALSE); 1133e39a5722Seric } 1134e39a5722Seric 1135ff038098Seric /* create temp file for editing p-file */ 1136e39a5722Seric mktemp(tfn); 1137e39a5722Seric tfp = fopen(tfn, "w"); 1138e39a5722Seric if (tfp == NULL) 1139e39a5722Seric { 1140095915d3Seric usrerr("cannot create \"%s\"", tfn); 1141e39a5722Seric exit(EX_OSERR); 1142e39a5722Seric } 1143e39a5722Seric 1144ff038098Seric /* figure out who I am */ 11451e0924d0Seric myname = username(); 1146ff038098Seric 1147ff038098Seric /* 1148ff038098Seric ** Copy p-file to temp file, doing deletions as needed. 1149ff038098Seric */ 1150ff038098Seric 1151ec4190ddSeric while ((pent = getpfent(pfp)) != NULL) 1152e39a5722Seric { 1153e39a5722Seric if (strcmp(pent->p_user, myname) == 0) 1154e39a5722Seric { 1155e39a5722Seric /* a match */ 1156e39a5722Seric delete++; 1157e39a5722Seric } 1158e39a5722Seric else 1159e39a5722Seric { 1160ff038098Seric /* output it again */ 116177a8a0dfSeric putpfent(pent, tfp); 1162e39a5722Seric others++; 1163e39a5722Seric } 1164e39a5722Seric } 1165e39a5722Seric 1166e39a5722Seric /* do final cleanup */ 1167e39a5722Seric if (others) 1168e39a5722Seric { 1169ff038098Seric /* copy it back (perhaps it should be linked?) */ 1170e39a5722Seric if (freopen(tfn, "r", tfp) == NULL) 1171e39a5722Seric { 1172095915d3Seric syserr("cannot reopen \"%s\"", tfn); 1173e39a5722Seric exit(EX_OSERR); 1174e39a5722Seric } 1175e39a5722Seric if (freopen(pfn, "w", pfp) == NULL) 1176e39a5722Seric { 1177095915d3Seric usrerr("cannot create \"%s\"", pfn); 1178d51cd7e5Seric return (FALSE); 1179e39a5722Seric } 1180e39a5722Seric while (fgets(buf, sizeof buf, tfp) != NULL) 1181e39a5722Seric fputs(buf, pfp); 1182e39a5722Seric } 1183e39a5722Seric else 1184e39a5722Seric { 1185ff038098Seric /* it's empty -- remove it */ 1186e39a5722Seric unlink(pfn); 1187e39a5722Seric } 1188e39a5722Seric fclose(tfp); 1189e39a5722Seric fclose(pfp); 1190e39a5722Seric unlink(tfn); 1191e39a5722Seric 1192ff038098Seric /* actually remove the g-file */ 1193e39a5722Seric if (delete) 1194e39a5722Seric { 1195287bbaa2Seric unlink(tail(fn)); 1196287bbaa2Seric printf("%12s: removed\n", tail(fn)); 1197d51cd7e5Seric return (TRUE); 1198e39a5722Seric } 1199e39a5722Seric else 1200e39a5722Seric { 12012444cd3eSeric printf("%12s: not being edited by you\n", fn); 1202d51cd7e5Seric return (FALSE); 1203e39a5722Seric } 1204e39a5722Seric } 12050faf63f9Seric 12060faf63f9Seric /* 120778d41c6aSeric ** DODIFF -- diff an s-file against a g-file 120878d41c6aSeric ** 120978d41c6aSeric ** Parameters: 121078d41c6aSeric ** getv -- argv for the 'get' command. 121178d41c6aSeric ** gfile -- name of the g-file to diff against. 121278d41c6aSeric ** 121378d41c6aSeric ** Returns: 121478d41c6aSeric ** Result of get. 121578d41c6aSeric ** 121678d41c6aSeric ** Side Effects: 121778d41c6aSeric ** none. 121878d41c6aSeric */ 121978d41c6aSeric 122078d41c6aSeric dodiff(getv, gfile) 122178d41c6aSeric char **getv; 122278d41c6aSeric char *gfile; 122378d41c6aSeric { 122478d41c6aSeric int pipev[2]; 122578d41c6aSeric int rval; 122678d41c6aSeric register int i; 122778d41c6aSeric register int pid; 122878d41c6aSeric auto int st; 122978d41c6aSeric extern int errno; 123078d41c6aSeric int (*osig)(); 123178d41c6aSeric 1232cc4e4f0dSeric printf("\n------- %s -------\n", gfile); 12332569d37aSeric fflush(stdout); 1234cc4e4f0dSeric 1235ff038098Seric /* create context for diff to run in */ 123678d41c6aSeric if (pipe(pipev) < 0) 123778d41c6aSeric { 123878d41c6aSeric syserr("dodiff: pipe failed"); 123978d41c6aSeric exit(EX_OSERR); 124078d41c6aSeric } 124178d41c6aSeric if ((pid = fork()) < 0) 124278d41c6aSeric { 124378d41c6aSeric syserr("dodiff: fork failed"); 124478d41c6aSeric exit(EX_OSERR); 124578d41c6aSeric } 124678d41c6aSeric else if (pid > 0) 124778d41c6aSeric { 124878d41c6aSeric /* in parent; run get */ 124978d41c6aSeric OutFile = pipev[1]; 125078d41c6aSeric close(pipev[0]); 12512569d37aSeric rval = command(&getv[1], TRUE, "get:rcixt -s -k -p"); 125278d41c6aSeric osig = signal(SIGINT, SIG_IGN); 125378d41c6aSeric while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR) 125478d41c6aSeric errno = 0; 125578d41c6aSeric signal(SIGINT, osig); 125678d41c6aSeric /* ignore result of diff */ 125778d41c6aSeric } 125878d41c6aSeric else 125978d41c6aSeric { 126078d41c6aSeric /* in child, run diff */ 126178d41c6aSeric if (close(pipev[1]) < 0 || close(0) < 0 || 126278d41c6aSeric dup(pipev[0]) != 0 || close(pipev[0]) < 0) 126378d41c6aSeric { 126478d41c6aSeric syserr("dodiff: magic failed"); 126578d41c6aSeric exit(EX_OSERR); 126678d41c6aSeric } 12672569d37aSeric command(&getv[1], FALSE, "-diff:elsfhbC"); 126878d41c6aSeric } 126978d41c6aSeric return (rval); 127078d41c6aSeric } 127178d41c6aSeric 127278d41c6aSeric /* 1273287bbaa2Seric ** TAIL -- return tail of filename. 1274287bbaa2Seric ** 1275287bbaa2Seric ** Parameters: 1276287bbaa2Seric ** fn -- the filename. 1277287bbaa2Seric ** 1278287bbaa2Seric ** Returns: 1279287bbaa2Seric ** a pointer to the tail of the filename; e.g., given 1280287bbaa2Seric ** "cmd/ls.c", "ls.c" is returned. 1281287bbaa2Seric ** 1282287bbaa2Seric ** Side Effects: 1283287bbaa2Seric ** none. 1284287bbaa2Seric */ 1285287bbaa2Seric 1286287bbaa2Seric char * 1287287bbaa2Seric tail(fn) 1288287bbaa2Seric register char *fn; 1289287bbaa2Seric { 1290287bbaa2Seric register char *p; 1291287bbaa2Seric 1292287bbaa2Seric for (p = fn; *p != 0; p++) 1293287bbaa2Seric if (*p == '/' && p[1] != '\0' && p[1] != '/') 1294287bbaa2Seric fn = &p[1]; 1295287bbaa2Seric return (fn); 1296287bbaa2Seric } 1297287bbaa2Seric 1298287bbaa2Seric /* 1299ec4190ddSeric ** GETPFENT -- get an entry from the p-file 1300e39a5722Seric ** 1301e39a5722Seric ** Parameters: 1302e39a5722Seric ** pfp -- p-file file pointer 1303e39a5722Seric ** 1304e39a5722Seric ** Returns: 1305e39a5722Seric ** pointer to p-file struct for next entry 1306e39a5722Seric ** NULL on EOF or error 1307e39a5722Seric ** 1308e39a5722Seric ** Side Effects: 1309e39a5722Seric ** Each call wipes out results of previous call. 1310e39a5722Seric */ 1311e39a5722Seric 1312e39a5722Seric struct pfile * 1313ec4190ddSeric getpfent(pfp) 1314e39a5722Seric FILE *pfp; 1315e39a5722Seric { 1316e39a5722Seric static struct pfile ent; 1317*f76dd34fSrrh static char buf[PFILELG]; 1318e39a5722Seric register char *p; 1319e39a5722Seric extern char *nextfield(); 1320e39a5722Seric 1321e39a5722Seric if (fgets(buf, sizeof buf, pfp) == NULL) 1322e39a5722Seric return (NULL); 1323e39a5722Seric 1324e39a5722Seric ent.p_osid = p = buf; 1325e39a5722Seric ent.p_nsid = p = nextfield(p); 1326e39a5722Seric ent.p_user = p = nextfield(p); 1327e39a5722Seric ent.p_date = p = nextfield(p); 1328e39a5722Seric ent.p_time = p = nextfield(p); 132977a8a0dfSeric ent.p_aux = p = nextfield(p); 1330e39a5722Seric 1331e39a5722Seric return (&ent); 1332e39a5722Seric } 1333e39a5722Seric 1334e39a5722Seric 1335e39a5722Seric char * 1336e39a5722Seric nextfield(p) 1337e39a5722Seric register char *p; 1338e39a5722Seric { 1339e39a5722Seric if (p == NULL || *p == '\0') 1340e39a5722Seric return (NULL); 1341e39a5722Seric while (*p != ' ' && *p != '\n' && *p != '\0') 1342e39a5722Seric p++; 1343e39a5722Seric if (*p == '\n' || *p == '\0') 1344e39a5722Seric { 1345e39a5722Seric *p = '\0'; 1346e39a5722Seric return (NULL); 1347e39a5722Seric } 1348e39a5722Seric *p++ = '\0'; 1349e39a5722Seric return (p); 1350e39a5722Seric } 135177a8a0dfSeric /* 135277a8a0dfSeric ** PUTPFENT -- output a p-file entry to a file 135377a8a0dfSeric ** 135477a8a0dfSeric ** Parameters: 135577a8a0dfSeric ** pf -- the p-file entry 135677a8a0dfSeric ** f -- the file to put it on. 135777a8a0dfSeric ** 135877a8a0dfSeric ** Returns: 135977a8a0dfSeric ** none. 136077a8a0dfSeric ** 136177a8a0dfSeric ** Side Effects: 136277a8a0dfSeric ** pf is written onto file f. 136377a8a0dfSeric */ 136477a8a0dfSeric 136577a8a0dfSeric putpfent(pf, f) 136677a8a0dfSeric register struct pfile *pf; 136777a8a0dfSeric register FILE *f; 136877a8a0dfSeric { 136977a8a0dfSeric fprintf(f, "%s %s %s %s %s", pf->p_osid, pf->p_nsid, 137077a8a0dfSeric pf->p_user, pf->p_date, pf->p_time); 137177a8a0dfSeric if (pf->p_aux != NULL) 137277a8a0dfSeric fprintf(f, " %s", pf->p_aux); 137377a8a0dfSeric else 137477a8a0dfSeric fprintf(f, "\n"); 137577a8a0dfSeric } 13760faf63f9Seric 13770faf63f9Seric /* 1378095915d3Seric ** USRERR -- issue user-level error 1379095915d3Seric ** 1380095915d3Seric ** Parameters: 1381095915d3Seric ** f -- format string. 1382095915d3Seric ** p1-p3 -- parameters to a printf. 1383095915d3Seric ** 1384095915d3Seric ** Returns: 1385095915d3Seric ** -1 1386095915d3Seric ** 1387095915d3Seric ** Side Effects: 1388095915d3Seric ** none. 1389095915d3Seric */ 1390095915d3Seric 1391fe7b004aSeric /*VARARGS1*/ 1392095915d3Seric usrerr(f, p1, p2, p3) 1393095915d3Seric char *f; 1394095915d3Seric { 1395095915d3Seric fprintf(stderr, "\n%s: ", MyName); 1396095915d3Seric fprintf(stderr, f, p1, p2, p3); 1397095915d3Seric fprintf(stderr, "\n"); 1398095915d3Seric 1399095915d3Seric return (-1); 1400095915d3Seric } 14010faf63f9Seric 14020faf63f9Seric /* 1403095915d3Seric ** SYSERR -- print system-generated error. 1404095915d3Seric ** 1405095915d3Seric ** Parameters: 1406095915d3Seric ** f -- format string to a printf. 1407095915d3Seric ** p1, p2, p3 -- parameters to f. 1408095915d3Seric ** 1409095915d3Seric ** Returns: 1410095915d3Seric ** never. 1411095915d3Seric ** 1412095915d3Seric ** Side Effects: 1413095915d3Seric ** none. 1414095915d3Seric */ 1415095915d3Seric 1416fe7b004aSeric /*VARARGS1*/ 1417095915d3Seric syserr(f, p1, p2, p3) 1418095915d3Seric char *f; 1419095915d3Seric { 1420095915d3Seric extern int errno; 1421095915d3Seric 1422095915d3Seric fprintf(stderr, "\n%s SYSERR: ", MyName); 1423095915d3Seric fprintf(stderr, f, p1, p2, p3); 1424095915d3Seric fprintf(stderr, "\n"); 1425095915d3Seric if (errno == 0) 1426095915d3Seric exit(EX_SOFTWARE); 1427095915d3Seric else 1428095915d3Seric { 1429fe7b004aSeric perror(NULL); 1430095915d3Seric exit(EX_OSERR); 1431095915d3Seric } 1432095915d3Seric } 14331e0924d0Seric /* 14341e0924d0Seric ** USERNAME -- return name of the current user 14351e0924d0Seric ** 14361e0924d0Seric ** Parameters: 14371e0924d0Seric ** none 14381e0924d0Seric ** 14391e0924d0Seric ** Returns: 14401e0924d0Seric ** name of current user 14411e0924d0Seric ** 14421e0924d0Seric ** Side Effects: 14431e0924d0Seric ** none 14441e0924d0Seric */ 14451e0924d0Seric 14461e0924d0Seric char * 14471e0924d0Seric username() 14481e0924d0Seric { 14491e0924d0Seric # ifdef UIDUSER 14501e0924d0Seric extern struct passwd *getpwuid(); 14511e0924d0Seric register struct passwd *pw; 14521e0924d0Seric 14531e0924d0Seric pw = getpwuid(getuid()); 14541e0924d0Seric if (pw == NULL) 14551e0924d0Seric { 14561e0924d0Seric syserr("who are you? (uid=%d)", getuid()); 14571e0924d0Seric exit(EX_OSERR); 14581e0924d0Seric } 14591e0924d0Seric return (pw->pw_name); 14601e0924d0Seric # else 1461ed320446Seric extern char *getlogin(); 1462bb5509cbSmckusick extern char *getenv(); 1463bb5509cbSmckusick register char *p; 1464ed320446Seric 1465bb5509cbSmckusick p = getenv("USER"); 1466bb5509cbSmckusick if (p == NULL || p[0] == '\0') 1467bb5509cbSmckusick p = getlogin(); 1468bb5509cbSmckusick return (p); 14691e0924d0Seric # endif UIDUSER 14701e0924d0Seric } 1471*f76dd34fSrrh 1472*f76dd34fSrrh /* 1473*f76dd34fSrrh ** Guarded string manipulation routines; the last argument 1474*f76dd34fSrrh ** is the length of the buffer into which the strcpy or strcat 1475*f76dd34fSrrh ** is to be done. 1476*f76dd34fSrrh */ 1477*f76dd34fSrrh char *gstrcat(to, from, length) 1478*f76dd34fSrrh char *to, *from; 1479*f76dd34fSrrh int length; 1480*f76dd34fSrrh { 1481*f76dd34fSrrh if (strlen(from) + strlen(to) >= length) { 1482*f76dd34fSrrh gstrbotch(to, from); 1483*f76dd34fSrrh } 1484*f76dd34fSrrh return(strcat(to, from)); 1485*f76dd34fSrrh } 1486*f76dd34fSrrh 1487*f76dd34fSrrh char *gstrncat(to, from, n, length) 1488*f76dd34fSrrh char *to, *from; 1489*f76dd34fSrrh int n; 1490*f76dd34fSrrh int length; 1491*f76dd34fSrrh { 1492*f76dd34fSrrh if (n + strlen(to) >= length) { 1493*f76dd34fSrrh gstrbotch(to, from); 1494*f76dd34fSrrh } 1495*f76dd34fSrrh return(strncat(to, from, n)); 1496*f76dd34fSrrh } 1497*f76dd34fSrrh 1498*f76dd34fSrrh char *gstrcpy(to, from, length) 1499*f76dd34fSrrh char *to, *from; 1500*f76dd34fSrrh int length; 1501*f76dd34fSrrh { 1502*f76dd34fSrrh if (strlen(from) >= length) { 1503*f76dd34fSrrh gstrbotch(from, (char *)0); 1504*f76dd34fSrrh } 1505*f76dd34fSrrh return(strcpy(to, from)); 1506*f76dd34fSrrh } 1507*f76dd34fSrrh gstrbotch(str1, str2) 1508*f76dd34fSrrh char *str1, *str2; 1509*f76dd34fSrrh { 1510*f76dd34fSrrh usrerr("Filename(s) too long: %s %s", str1, str2); 1511*f76dd34fSrrh } 1512