1adf8f7d4Seric # include <stdio.h> 2adf8f7d4Seric # include <sys/types.h> 3adf8f7d4Seric # include <sys/stat.h> 4b2538d76Seric # include <sys/dir.h> 578d41c6aSeric # include <errno.h> 678d41c6aSeric # include <signal.h> 7adf8f7d4Seric # include <sysexits.h> 8b5d4f080Seric # include <whoami.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*92fabd68Seric static char SccsId[] = "@(#)sccs.c 1.57 01/14/81"; 960faf63f9Seric 977abec564Seric /******************* Configuration Information ********************/ 987abec564Seric 99c4ad8825Seric /* special defines for local berkeley systems */ 100c4ad8825Seric # include <whoami.h> 101c4ad8825Seric 10210cd16efSeric # ifdef CSVAX 10310cd16efSeric # define UIDUSER 1047abec564Seric # define PROGPATH(name) "/usr/local/name" 1057abec564Seric # endif CSVAX 10610cd16efSeric 1076de0267aSeric # ifdef INGVAX 1086de0267aSeric # define PROGPATH(name) "/usr/local/name" 1096de0267aSeric # endif INGVAX 1106de0267aSeric 11131bc6d6cSeric # ifdef CORY 11231bc6d6cSeric # define PROGPATH(name) "/usr/eecs/bin/name" 11331bc6d6cSeric # endif CORY 11431bc6d6cSeric 115c4ad8825Seric /* end of berkeley systems defines */ 1167abec564Seric 117c4ad8825Seric # ifndef SCCSPATH 118c4ad8825Seric # define SCCSPATH "SCCS" /* pathname in which to find s-files */ 119c4ad8825Seric # endif NOT SCCSPATH 120c4ad8825Seric 121c4ad8825Seric # ifndef MYNAME 122c4ad8825Seric # define MYNAME "sccs" /* name used for printing errors */ 123c4ad8825Seric # endif NOT MYNAME 1247abec564Seric 1250faf63f9Seric # ifndef PROGPATH 1260faf63f9Seric # define PROGPATH(name) "/usr/sccs/name" /* place to find binaries */ 1270faf63f9Seric # endif PROGPATH 1280faf63f9Seric 1297abec564Seric /**************** End of Configuration Information ****************/ 1300faf63f9Seric 131c4432be4Seric typedef char bool; 1327de81dc7Seric # define TRUE 1 1337de81dc7Seric # define FALSE 0 134d02a4f42Seric 135ff038098Seric # define bitset(bit, word) ((bool) ((bit) & (word))) 136ff038098Seric 13710cd16efSeric # ifdef UIDUSER 13810cd16efSeric # include <pwd.h> 13910cd16efSeric # endif UIDUSER 14010cd16efSeric 141adf8f7d4Seric struct sccsprog 142adf8f7d4Seric { 143adf8f7d4Seric char *sccsname; /* name of SCCS routine */ 1447de81dc7Seric short sccsoper; /* opcode, see below */ 1457de81dc7Seric short sccsflags; /* flags, see below */ 146adf8f7d4Seric char *sccspath; /* pathname of binary implementing */ 147adf8f7d4Seric }; 148adf8f7d4Seric 1497de81dc7Seric /* values for sccsoper */ 1507de81dc7Seric # define PROG 0 /* call a program */ 1511777fbcbSeric # define CMACRO 1 /* command substitution macro */ 152172147faSeric # define FIX 2 /* fix a delta */ 153b2538d76Seric # define CLEAN 3 /* clean out recreatable files */ 154e39a5722Seric # define UNEDIT 4 /* unedit a file */ 155419708b0Seric # define SHELL 5 /* call a shell file (like PROG) */ 15678d41c6aSeric # define DIFFS 6 /* diff between sccs & file out */ 1572569d37aSeric # define DODIFF 7 /* internal call to diff program */ 1587de81dc7Seric 159c4432be4Seric /* bits for sccsflags */ 1607de81dc7Seric # define NO_SDOT 0001 /* no s. on front of args */ 1617de81dc7Seric # define REALUSER 0002 /* protected (e.g., admin) */ 162adf8f7d4Seric 16348ff0e7eSeric /* modes for the "clean", "info", "check" ops */ 16448ff0e7eSeric # define CLEANC 0 /* clean command */ 16548ff0e7eSeric # define INFOC 1 /* info command */ 16648ff0e7eSeric # define CHECKC 2 /* check command */ 1672734e237Seric # define TELLC 3 /* give list of files being edited */ 16848ff0e7eSeric 1690faf63f9Seric /* 1700faf63f9Seric ** Description of commands known to this program. 1710faf63f9Seric ** First argument puts the command into a class. Second arg is 1720faf63f9Seric ** info regarding treatment of this command. Third arg is a 1730faf63f9Seric ** list of flags this command accepts from macros, etc. Fourth 1740faf63f9Seric ** arg is the pathname of the implementing program, or the 1750faf63f9Seric ** macro definition, or the arg to a sub-algorithm. 1760faf63f9Seric */ 177b5d4f080Seric 178adf8f7d4Seric struct sccsprog SccsProg[] = 179adf8f7d4Seric { 1803642a8d2Seric "admin", PROG, REALUSER, PROGPATH(admin), 1813642a8d2Seric "chghist", PROG, 0, PROGPATH(rmdel), 1823642a8d2Seric "comb", PROG, 0, PROGPATH(comb), 1833642a8d2Seric "delta", PROG, 0, PROGPATH(delta), 1843642a8d2Seric "get", PROG, 0, PROGPATH(get), 1853642a8d2Seric "help", PROG, NO_SDOT, PROGPATH(help), 1863a3bf7bfSeric "prs", PROG, 0, PROGPATH(prs), 1873642a8d2Seric "prt", PROG, 0, PROGPATH(prt), 1883642a8d2Seric "rmdel", PROG, REALUSER, PROGPATH(rmdel), 1893a3bf7bfSeric "val", PROG, 0, PROGPATH(val), 1903642a8d2Seric "what", PROG, NO_SDOT, PROGPATH(what), 1913642a8d2Seric "sccsdiff", SHELL, REALUSER, PROGPATH(sccsdiff), 1923642a8d2Seric "edit", CMACRO, NO_SDOT, "get -e", 19359e81a43Seric "delget", CMACRO, NO_SDOT, "delta:mysrp/get:ixbeskcl -t", 1941cf60de3Seric "deledit", CMACRO, NO_SDOT, "delta:mysrp -n/get:ixbskcl -e -t -g", 1953642a8d2Seric "fix", FIX, NO_SDOT, NULL, 1961e0924d0Seric "clean", CLEAN, REALUSER|NO_SDOT, (char *) CLEANC, 1971e0924d0Seric "info", CLEAN, REALUSER|NO_SDOT, (char *) INFOC, 1981e0924d0Seric "check", CLEAN, REALUSER|NO_SDOT, (char *) CHECKC, 1991e0924d0Seric "tell", CLEAN, REALUSER|NO_SDOT, (char *) TELLC, 2003642a8d2Seric "unedit", UNEDIT, NO_SDOT, NULL, 2013642a8d2Seric "diffs", DIFFS, NO_SDOT|REALUSER, NULL, 2022569d37aSeric "-diff", DODIFF, NO_SDOT|REALUSER, PROGPATH(bdiff), 203b387a58fSeric "print", CMACRO, 0, "prt -e/get -p -m -s", 2043642a8d2Seric NULL, -1, 0, NULL 205adf8f7d4Seric }; 206adf8f7d4Seric 2070faf63f9Seric /* one line from a p-file */ 208e39a5722Seric struct pfile 209e39a5722Seric { 210e39a5722Seric char *p_osid; /* old SID */ 211e39a5722Seric char *p_nsid; /* new SID */ 212e39a5722Seric char *p_user; /* user who did edit */ 213e39a5722Seric char *p_date; /* date of get */ 214e39a5722Seric char *p_time; /* time of get */ 215e39a5722Seric }; 216e39a5722Seric 2177abec564Seric char *SccsPath = SCCSPATH; /* pathname of SCCS files */ 2187abec564Seric # ifdef SCCSDIR 2197abec564Seric char *SccsDir = SCCSDIR; /* directory to begin search from */ 220095915d3Seric # else 2217abec564Seric char *SccsDir = ""; 222095915d3Seric # endif 223c4ad8825Seric char MyName[] = MYNAME; /* name used in messages */ 22478d41c6aSeric int OutFile = -1; /* override output file for commands */ 225c4432be4Seric bool RealUser; /* if set, running as real user */ 226f0cc3246Seric # ifdef DEBUG 227f0cc3246Seric bool Debug; /* turn on tracing */ 228f0cc3246Seric # endif 229*92fabd68Seric # ifndef V6 230*92fabd68Seric extern char *getenv(); 231*92fabd68Seric # endif V6 2320faf63f9Seric 233adf8f7d4Seric main(argc, argv) 234adf8f7d4Seric int argc; 235adf8f7d4Seric char **argv; 236adf8f7d4Seric { 237adf8f7d4Seric register char *p; 238b1ed8a43Seric extern struct sccsprog *lookup(); 23941290c52Seric register int i; 240adf8f7d4Seric 241*92fabd68Seric # ifndef V6 242*92fabd68Seric # ifndef SCCSDIR 243*92fabd68Seric /* pull "SccsDir" out of the environment (possibly) */ 244*92fabd68Seric p = getenv("PROJECT"); 245*92fabd68Seric if (p[0] == '/') 246*92fabd68Seric SccsDir = p; 247*92fabd68Seric else 248*92fabd68Seric fprintf(stderr, "PROJECT must be a full pathname\n"); 249*92fabd68Seric # endif SCCSDIR 250*92fabd68Seric # endif V6 251*92fabd68Seric 252adf8f7d4Seric /* 253adf8f7d4Seric ** Detect and decode flags intended for this program. 254adf8f7d4Seric */ 255adf8f7d4Seric 2567de81dc7Seric if (argc < 2) 257adf8f7d4Seric { 258095915d3Seric fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); 2597de81dc7Seric exit(EX_USAGE); 2607de81dc7Seric } 2617de81dc7Seric argv[argc] = NULL; 2627de81dc7Seric 263b1ed8a43Seric if (lookup(argv[0]) == NULL) 264b1ed8a43Seric { 2657de81dc7Seric while ((p = *++argv) != NULL) 2667de81dc7Seric { 267adf8f7d4Seric if (*p != '-') 268adf8f7d4Seric break; 269adf8f7d4Seric switch (*++p) 270adf8f7d4Seric { 271adf8f7d4Seric case 'r': /* run as real user */ 272adf8f7d4Seric setuid(getuid()); 273c4432be4Seric RealUser++; 274adf8f7d4Seric break; 275adf8f7d4Seric 2767abec564Seric # ifndef SCCSDIR 277adf8f7d4Seric case 'p': /* path of sccs files */ 278adf8f7d4Seric SccsPath = ++p; 279adf8f7d4Seric break; 280adf8f7d4Seric 2810a8b9ab0Seric case 'd': /* directory to search from */ 2820a8b9ab0Seric SccsDir = ++p; 2830a8b9ab0Seric break; 284095915d3Seric # endif 2850a8b9ab0Seric 286f0cc3246Seric # ifdef DEBUG 287f0cc3246Seric case 'T': /* trace */ 288f0cc3246Seric Debug++; 289f0cc3246Seric break; 290f0cc3246Seric # endif 291f0cc3246Seric 292adf8f7d4Seric default: 293095915d3Seric usrerr("unknown option -%s", p); 294adf8f7d4Seric break; 295adf8f7d4Seric } 296adf8f7d4Seric } 2975cabffd9Seric if (SccsPath[0] == '\0') 2985cabffd9Seric SccsPath = "."; 299b1ed8a43Seric } 300adf8f7d4Seric 3013642a8d2Seric i = command(argv, FALSE, ""); 30241290c52Seric exit(i); 3037de81dc7Seric } 3040faf63f9Seric 3050faf63f9Seric /* 30641290c52Seric ** COMMAND -- look up and perform a command 30741290c52Seric ** 30841290c52Seric ** This routine is the guts of this program. Given an 30941290c52Seric ** argument vector, it looks up the "command" (argv[0]) 31041290c52Seric ** in the configuration table and does the necessary stuff. 31141290c52Seric ** 31241290c52Seric ** Parameters: 31341290c52Seric ** argv -- an argument vector to process. 31441290c52Seric ** forkflag -- if set, fork before executing the command. 315108d6082Seric ** editflag -- if set, only include flags listed in the 316108d6082Seric ** sccsklets field of the command descriptor. 317108d6082Seric ** arg0 -- a space-seperated list of arguments to insert 318108d6082Seric ** before argv. 31941290c52Seric ** 32041290c52Seric ** Returns: 32141290c52Seric ** zero -- command executed ok. 32241290c52Seric ** else -- error status. 32341290c52Seric ** 32441290c52Seric ** Side Effects: 32541290c52Seric ** none. 32641290c52Seric */ 3277de81dc7Seric 3283642a8d2Seric command(argv, forkflag, arg0) 3297de81dc7Seric char **argv; 3301777fbcbSeric bool forkflag; 331108d6082Seric char *arg0; 3327de81dc7Seric { 3337de81dc7Seric register struct sccsprog *cmd; 3347de81dc7Seric register char *p; 3351777fbcbSeric char buf[40]; 336b1ed8a43Seric extern struct sccsprog *lookup(); 337108d6082Seric char *nav[1000]; 338108d6082Seric char **np; 339419708b0Seric register char **ap; 340d51cd7e5Seric register int i; 341419708b0Seric register char *q; 342d51cd7e5Seric extern bool unedit(); 34341290c52Seric int rval = 0; 344108d6082Seric extern char *index(); 345108d6082Seric extern char *makefile(); 3463642a8d2Seric char *editchs; 347287bbaa2Seric extern char *tail(); 348f0cc3246Seric 349f0cc3246Seric # ifdef DEBUG 350f0cc3246Seric if (Debug) 351f0cc3246Seric { 352108d6082Seric printf("command:\n\t\"%s\"\n", arg0); 353108d6082Seric for (np = argv; *np != NULL; np++) 354108d6082Seric printf("\t\"%s\"\n", *np); 355f0cc3246Seric } 356f0cc3246Seric # endif 357c4432be4Seric 358c4432be4Seric /* 359108d6082Seric ** Copy arguments. 360ff038098Seric ** Copy from arg0 & if necessary at most one arg 361ff038098Seric ** from argv[0]. 362adf8f7d4Seric */ 363adf8f7d4Seric 364419708b0Seric np = ap = &nav[1]; 3653642a8d2Seric editchs = NULL; 36659e81a43Seric for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) 367108d6082Seric { 368108d6082Seric *np++ = q; 369108d6082Seric while (*p == ' ') 370108d6082Seric p++; 3713642a8d2Seric while (*p != ' ' && *p != '\0' && *p != '/' && *p != ':') 372108d6082Seric *q++ = *p++; 373108d6082Seric *q++ = '\0'; 3743642a8d2Seric if (*p == ':') 3753642a8d2Seric { 3763642a8d2Seric editchs = q; 37759e81a43Seric while (*++p != '\0' && *p != '/' && *p != ' ') 3783642a8d2Seric *q++ = *p; 3793642a8d2Seric *q++ = '\0'; 3803642a8d2Seric } 381108d6082Seric } 382108d6082Seric *np = NULL; 383419708b0Seric if (*ap == NULL) 384108d6082Seric *np++ = *argv++; 385108d6082Seric 386108d6082Seric /* 387108d6082Seric ** Look up command. 388419708b0Seric ** At this point, *ap is the command name. 389108d6082Seric */ 390108d6082Seric 391419708b0Seric cmd = lookup(*ap); 392b1ed8a43Seric if (cmd == NULL) 393adf8f7d4Seric { 394419708b0Seric usrerr("Unknown command \"%s\"", *ap); 39541290c52Seric return (EX_USAGE); 396adf8f7d4Seric } 397adf8f7d4Seric 398adf8f7d4Seric /* 399108d6082Seric ** Copy remaining arguments doing editing as appropriate. 400108d6082Seric */ 401108d6082Seric 402108d6082Seric for (; *argv != NULL; argv++) 403108d6082Seric { 404108d6082Seric p = *argv; 405108d6082Seric if (*p == '-') 406108d6082Seric { 4073642a8d2Seric if (p[1] == '\0' || editchs == NULL || index(editchs, p[1]) != NULL) 408108d6082Seric *np++ = p; 409108d6082Seric } 410108d6082Seric else 411108d6082Seric { 412108d6082Seric if (!bitset(NO_SDOT, cmd->sccsflags)) 413108d6082Seric p = makefile(p); 414108d6082Seric if (p != NULL) 415108d6082Seric *np++ = p; 416108d6082Seric } 417108d6082Seric } 418108d6082Seric *np = NULL; 419108d6082Seric 420108d6082Seric /* 4217de81dc7Seric ** Interpret operation associated with this command. 422c4432be4Seric */ 423c4432be4Seric 4247de81dc7Seric switch (cmd->sccsoper) 4257de81dc7Seric { 426419708b0Seric case SHELL: /* call a shell file */ 427419708b0Seric *ap = cmd->sccspath; 428419708b0Seric *--ap = "sh"; 429419708b0Seric rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag); 430419708b0Seric break; 431419708b0Seric 4327de81dc7Seric case PROG: /* call an sccs prog */ 433419708b0Seric rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); 4341777fbcbSeric break; 4351777fbcbSeric 4361777fbcbSeric case CMACRO: /* command macro */ 437ff038098Seric /* step through & execute each part of the macro */ 4381777fbcbSeric for (p = cmd->sccspath; *p != '\0'; p++) 4391777fbcbSeric { 440108d6082Seric q = p; 441108d6082Seric while (*p != '\0' && *p != '/') 442108d6082Seric p++; 4433642a8d2Seric rval = command(&ap[1], *p != '\0', q); 44441290c52Seric if (rval != 0) 44541290c52Seric break; 4461777fbcbSeric } 44741290c52Seric break; 4487de81dc7Seric 449172147faSeric case FIX: /* fix a delta */ 450419708b0Seric if (strncmp(ap[1], "-r", 2) != 0) 451172147faSeric { 452095915d3Seric usrerr("-r flag needed for fix command"); 45341290c52Seric rval = EX_USAGE; 454172147faSeric break; 455172147faSeric } 456ff038098Seric 457ff038098Seric /* get the version with all changes */ 4583642a8d2Seric rval = command(&ap[1], TRUE, "get -k"); 459ff038098Seric 460ff038098Seric /* now remove that version from the s-file */ 46141290c52Seric if (rval == 0) 4623642a8d2Seric rval = command(&ap[1], TRUE, "rmdel:r"); 463ff038098Seric 464ff038098Seric /* and edit the old version (but don't clobber new vers) */ 46541290c52Seric if (rval == 0) 4663642a8d2Seric rval = command(&ap[2], FALSE, "get -e -g"); 46741290c52Seric break; 468172147faSeric 469b2538d76Seric case CLEAN: 470ec4190ddSeric rval = clean((int) cmd->sccspath, ap); 471b2538d76Seric break; 472b2538d76Seric 473e39a5722Seric case UNEDIT: 474419708b0Seric for (argv = np = &ap[1]; *argv != NULL; argv++) 475d51cd7e5Seric { 476108d6082Seric if (unedit(*argv)) 477108d6082Seric *np++ = *argv; 478d51cd7e5Seric } 479108d6082Seric *np = NULL; 480ff038098Seric 481ff038098Seric /* get all the files that we unedited successfully */ 482fe7b004aSeric if (np > &ap[1]) 4833642a8d2Seric rval = command(&ap[1], FALSE, "get"); 484e39a5722Seric break; 485e39a5722Seric 48678d41c6aSeric case DIFFS: /* diff between s-file & edit file */ 48778d41c6aSeric /* find the end of the flag arguments */ 48878d41c6aSeric for (np = &ap[1]; *np != NULL && **np == '-'; np++) 48978d41c6aSeric continue; 49078d41c6aSeric argv = np; 49178d41c6aSeric 49278d41c6aSeric /* for each file, do the diff */ 49396370756Seric p = argv[1]; 49478d41c6aSeric while (*np != NULL) 49578d41c6aSeric { 496ff038098Seric /* messy, but we need a null terminated argv */ 49778d41c6aSeric *argv = *np++; 49896370756Seric argv[1] = NULL; 499287bbaa2Seric i = dodiff(ap, tail(*argv)); 50078d41c6aSeric if (rval == 0) 50178d41c6aSeric rval = i; 50296370756Seric argv[1] = p; 50378d41c6aSeric } 50478d41c6aSeric break; 50578d41c6aSeric 5062569d37aSeric case DODIFF: /* internal diff call */ 5072569d37aSeric setuid(getuid()); 5082569d37aSeric for (np = ap; *np != NULL; np++) 5092569d37aSeric { 5102569d37aSeric if ((*np)[0] == '-' && (*np)[1] == 'C') 5112569d37aSeric (*np)[1] = 'c'; 5122569d37aSeric } 5132569d37aSeric 5142569d37aSeric /* insert "-" argument */ 5152569d37aSeric np[1] = NULL; 5162569d37aSeric np[0] = np[-1]; 5172569d37aSeric np[-1] = "-"; 5182569d37aSeric 5192569d37aSeric /* execute the diff program of choice */ 5202569d37aSeric # ifndef V6 5212569d37aSeric execvp("diff", ap); 5222569d37aSeric # endif 5232569d37aSeric execv(cmd->sccspath, argv); 5242569d37aSeric syserr("cannot exec %s", cmd->sccspath); 5252569d37aSeric exit(EX_OSERR); 5262569d37aSeric 5277de81dc7Seric default: 528095915d3Seric syserr("oper %d", cmd->sccsoper); 5297de81dc7Seric exit(EX_SOFTWARE); 5307de81dc7Seric } 53141290c52Seric # ifdef DEBUG 53241290c52Seric if (Debug) 53341290c52Seric printf("command: rval=%d\n", rval); 53441290c52Seric # endif 53541290c52Seric return (rval); 5367de81dc7Seric } 5370faf63f9Seric 5380faf63f9Seric /* 539b1ed8a43Seric ** LOOKUP -- look up an SCCS command name. 540b1ed8a43Seric ** 541b1ed8a43Seric ** Parameters: 542b1ed8a43Seric ** name -- the name of the command to look up. 543b1ed8a43Seric ** 544b1ed8a43Seric ** Returns: 545b1ed8a43Seric ** ptr to command descriptor for this command. 546b1ed8a43Seric ** NULL if no such entry. 547b1ed8a43Seric ** 548b1ed8a43Seric ** Side Effects: 549b1ed8a43Seric ** none. 550b1ed8a43Seric */ 551b1ed8a43Seric 552b1ed8a43Seric struct sccsprog * 553b1ed8a43Seric lookup(name) 554b1ed8a43Seric char *name; 555b1ed8a43Seric { 556b1ed8a43Seric register struct sccsprog *cmd; 557b1ed8a43Seric 558b1ed8a43Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 559b1ed8a43Seric { 560b1ed8a43Seric if (strcmp(cmd->sccsname, name) == 0) 561b1ed8a43Seric return (cmd); 562b1ed8a43Seric } 563b1ed8a43Seric return (NULL); 564b1ed8a43Seric } 5650faf63f9Seric 5660faf63f9Seric /* 56741290c52Seric ** CALLPROG -- call a program 56841290c52Seric ** 569108d6082Seric ** Used to call the SCCS programs. 57041290c52Seric ** 57141290c52Seric ** Parameters: 57241290c52Seric ** progpath -- pathname of the program to call. 57341290c52Seric ** flags -- status flags from the command descriptors. 57441290c52Seric ** argv -- an argument vector to pass to the program. 57541290c52Seric ** forkflag -- if true, fork before calling, else just 57641290c52Seric ** exec. 57741290c52Seric ** 57841290c52Seric ** Returns: 57941290c52Seric ** The exit status of the program. 58041290c52Seric ** Nothing if forkflag == FALSE. 58141290c52Seric ** 58241290c52Seric ** Side Effects: 58341290c52Seric ** Can exit if forkflag == FALSE. 58441290c52Seric */ 585172147faSeric 5867de81dc7Seric callprog(progpath, flags, argv, forkflag) 5877de81dc7Seric char *progpath; 5887de81dc7Seric short flags; 5897de81dc7Seric char **argv; 5907de81dc7Seric bool forkflag; 5917de81dc7Seric { 5927de81dc7Seric register int i; 5931777fbcbSeric auto int st; 594108d6082Seric 595108d6082Seric # ifdef DEBUG 596108d6082Seric if (Debug) 597108d6082Seric { 598108d6082Seric printf("callprog:\n"); 599108d6082Seric for (i = 0; argv[i] != NULL; i++) 600108d6082Seric printf("\t\"%s\"\n", argv[i]); 601108d6082Seric } 602108d6082Seric # endif 6037de81dc7Seric 6047de81dc7Seric if (*argv == NULL) 6057de81dc7Seric return (-1); 606c4432be4Seric 607c4432be4Seric /* 608172147faSeric ** Fork if appropriate. 609adf8f7d4Seric */ 610adf8f7d4Seric 6117de81dc7Seric if (forkflag) 6127de81dc7Seric { 613f0cc3246Seric # ifdef DEBUG 614f0cc3246Seric if (Debug) 615f0cc3246Seric printf("Forking\n"); 616f0cc3246Seric # endif 6177de81dc7Seric i = fork(); 6187de81dc7Seric if (i < 0) 6197de81dc7Seric { 620095915d3Seric syserr("cannot fork"); 6217de81dc7Seric exit(EX_OSERR); 6227de81dc7Seric } 6237de81dc7Seric else if (i > 0) 6241777fbcbSeric { 6251777fbcbSeric wait(&st); 62641290c52Seric if ((st & 0377) == 0) 62741290c52Seric st = (st >> 8) & 0377; 62878d41c6aSeric if (OutFile >= 0) 62978d41c6aSeric { 63078d41c6aSeric close(OutFile); 63178d41c6aSeric OutFile = -1; 63278d41c6aSeric } 6331777fbcbSeric return (st); 6341777fbcbSeric } 6357de81dc7Seric } 63678d41c6aSeric else if (OutFile >= 0) 63778d41c6aSeric { 63878d41c6aSeric syserr("callprog: setting stdout w/o forking"); 63978d41c6aSeric exit(EX_SOFTWARE); 64078d41c6aSeric } 6417de81dc7Seric 64278d41c6aSeric /* set protection as appropriate */ 6437de81dc7Seric if (bitset(REALUSER, flags)) 6447de81dc7Seric setuid(getuid()); 6457de81dc7Seric 64678d41c6aSeric /* change standard input & output if needed */ 64778d41c6aSeric if (OutFile >= 0) 64878d41c6aSeric { 64978d41c6aSeric close(1); 65078d41c6aSeric dup(OutFile); 65178d41c6aSeric close(OutFile); 65278d41c6aSeric } 6537de81dc7Seric 65478d41c6aSeric /* call real SCCS program */ 655172147faSeric execv(progpath, argv); 656095915d3Seric syserr("cannot execute %s", progpath); 657adf8f7d4Seric exit(EX_UNAVAILABLE); 658fe7b004aSeric /*NOTREACHED*/ 659adf8f7d4Seric } 6600faf63f9Seric 6610faf63f9Seric /* 662cdc1aa65Seric ** MAKEFILE -- make filename of SCCS file 663cdc1aa65Seric ** 664cdc1aa65Seric ** If the name passed is already the name of an SCCS file, 665cdc1aa65Seric ** just return it. Otherwise, munge the name into the name 666cdc1aa65Seric ** of the actual SCCS file. 667cdc1aa65Seric ** 668cdc1aa65Seric ** There are cases when it is not clear what you want to 669cdc1aa65Seric ** do. For example, if SccsPath is an absolute pathname 670cdc1aa65Seric ** and the name given is also an absolute pathname, we go 671cdc1aa65Seric ** for SccsPath (& only use the last component of the name 672cdc1aa65Seric ** passed) -- this is important for security reasons (if 673cdc1aa65Seric ** sccs is being used as a setuid front end), but not 674cdc1aa65Seric ** particularly intuitive. 675cdc1aa65Seric ** 676cdc1aa65Seric ** Parameters: 677cdc1aa65Seric ** name -- the file name to be munged. 678cdc1aa65Seric ** 679cdc1aa65Seric ** Returns: 680cdc1aa65Seric ** The pathname of the sccs file. 681cdc1aa65Seric ** NULL on error. 682cdc1aa65Seric ** 683cdc1aa65Seric ** Side Effects: 684cdc1aa65Seric ** none. 685cdc1aa65Seric */ 686adf8f7d4Seric 687adf8f7d4Seric char * 688adf8f7d4Seric makefile(name) 689adf8f7d4Seric char *name; 690adf8f7d4Seric { 691adf8f7d4Seric register char *p; 692adf8f7d4Seric char buf[512]; 693adf8f7d4Seric extern char *malloc(); 694cdc1aa65Seric extern char *rindex(); 6950a8b9ab0Seric extern bool safepath(); 6967d70f5d8Seric extern bool isdir(); 6977d70f5d8Seric register char *q; 698cdc1aa65Seric 699cdc1aa65Seric p = rindex(name, '/'); 700cdc1aa65Seric if (p == NULL) 701cdc1aa65Seric p = name; 702cdc1aa65Seric else 703cdc1aa65Seric p++; 704adf8f7d4Seric 705adf8f7d4Seric /* 7060a8b9ab0Seric ** Check to see that the path is "safe", i.e., that we 7070a8b9ab0Seric ** are not letting some nasty person use the setuid part 7080a8b9ab0Seric ** of this program to look at or munge some presumably 7090a8b9ab0Seric ** hidden files. 710adf8f7d4Seric */ 711adf8f7d4Seric 7120a8b9ab0Seric if (SccsDir[0] == '/' && !safepath(name)) 7130a8b9ab0Seric return (NULL); 714cdc1aa65Seric 715cdc1aa65Seric /* 7160a8b9ab0Seric ** Create the base pathname. 717cdc1aa65Seric */ 718cdc1aa65Seric 719ff038098Seric /* first the directory part */ 7200a8b9ab0Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) 721adf8f7d4Seric { 7220a8b9ab0Seric strcpy(buf, SccsDir); 723cdc1aa65Seric strcat(buf, "/"); 724cdc1aa65Seric } 725cdc1aa65Seric else 726cdc1aa65Seric strcpy(buf, ""); 727ff038098Seric 728ff038098Seric /* then the head of the pathname */ 729cdc1aa65Seric strncat(buf, name, p - name); 7307d70f5d8Seric q = &buf[strlen(buf)]; 731ff038098Seric 732ff038098Seric /* now copy the final part of the name, in case useful */ 7337d70f5d8Seric strcpy(q, p); 734ff038098Seric 735ff038098Seric /* so is it useful? */ 7367d70f5d8Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 7377d70f5d8Seric { 738ff038098Seric /* sorry, no; copy the SCCS pathname & the "s." */ 7390a8b9ab0Seric strcpy(q, SccsPath); 7400a8b9ab0Seric strcat(buf, "/s."); 741ff038098Seric 742ff038098Seric /* and now the end of the name */ 743cdc1aa65Seric strcat(buf, p); 744cdc1aa65Seric } 745adf8f7d4Seric 746ff038098Seric /* if i haven't changed it, why did I do all this? */ 7470a8b9ab0Seric if (strcmp(buf, name) == 0) 7480a8b9ab0Seric p = name; 7490a8b9ab0Seric else 7500a8b9ab0Seric { 751ff038098Seric /* but if I have, squirrel it away */ 752adf8f7d4Seric p = malloc(strlen(buf) + 1); 753adf8f7d4Seric if (p == NULL) 754adf8f7d4Seric { 755adf8f7d4Seric perror("Sccs: no mem"); 756adf8f7d4Seric exit(EX_OSERR); 757adf8f7d4Seric } 758adf8f7d4Seric strcpy(p, buf); 7590a8b9ab0Seric } 760ff038098Seric 761adf8f7d4Seric return (p); 762adf8f7d4Seric } 7630faf63f9Seric 7640faf63f9Seric /* 7657d70f5d8Seric ** ISDIR -- return true if the argument is a directory. 7667d70f5d8Seric ** 7677d70f5d8Seric ** Parameters: 7687d70f5d8Seric ** name -- the pathname of the file to check. 7697d70f5d8Seric ** 7707d70f5d8Seric ** Returns: 7717d70f5d8Seric ** TRUE if 'name' is a directory, FALSE otherwise. 7727d70f5d8Seric ** 7737d70f5d8Seric ** Side Effects: 7747d70f5d8Seric ** none. 7757d70f5d8Seric */ 7767d70f5d8Seric 7777d70f5d8Seric bool 7787d70f5d8Seric isdir(name) 7797d70f5d8Seric char *name; 7807d70f5d8Seric { 7817d70f5d8Seric struct stat stbuf; 7827d70f5d8Seric 7837d70f5d8Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 7847d70f5d8Seric } 7850faf63f9Seric 7860faf63f9Seric /* 787cdc1aa65Seric ** SAFEPATH -- determine whether a pathname is "safe" 788cdc1aa65Seric ** 789cdc1aa65Seric ** "Safe" pathnames only allow you to get deeper into the 790cdc1aa65Seric ** directory structure, i.e., full pathnames and ".." are 791cdc1aa65Seric ** not allowed. 792cdc1aa65Seric ** 793cdc1aa65Seric ** Parameters: 794cdc1aa65Seric ** p -- the name to check. 795cdc1aa65Seric ** 796cdc1aa65Seric ** Returns: 797cdc1aa65Seric ** TRUE -- if the path is safe. 798cdc1aa65Seric ** FALSE -- if the path is not safe. 799cdc1aa65Seric ** 800cdc1aa65Seric ** Side Effects: 801cdc1aa65Seric ** Prints a message if the path is not safe. 802cdc1aa65Seric */ 803cdc1aa65Seric 804cdc1aa65Seric bool 805cdc1aa65Seric safepath(p) 806cdc1aa65Seric register char *p; 807cdc1aa65Seric { 808cdc1aa65Seric extern char *index(); 809cdc1aa65Seric 810cdc1aa65Seric if (*p != '/') 811cdc1aa65Seric { 812cdc1aa65Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 813cdc1aa65Seric { 814cdc1aa65Seric p = index(p, '/'); 815cdc1aa65Seric if (p == NULL) 816cdc1aa65Seric return (TRUE); 817cdc1aa65Seric p++; 818cdc1aa65Seric } 819cdc1aa65Seric } 820cdc1aa65Seric 821cdc1aa65Seric printf("You may not use full pathnames or \"..\"\n"); 822cdc1aa65Seric return (FALSE); 823cdc1aa65Seric } 8240faf63f9Seric 8250faf63f9Seric /* 826b2538d76Seric ** CLEAN -- clean out recreatable files 827b2538d76Seric ** 828b2538d76Seric ** Any file for which an "s." file exists but no "p." file 829b2538d76Seric ** exists in the current directory is purged. 830b2538d76Seric ** 831b2538d76Seric ** Parameters: 832ec4190ddSeric ** mode -- tells whether this came from a "clean", "info", or 83348ff0e7eSeric ** "check" command. 834ec4190ddSeric ** argv -- the rest of the argument vector. 835b2538d76Seric ** 836b2538d76Seric ** Returns: 837b2538d76Seric ** none. 838b2538d76Seric ** 839b2538d76Seric ** Side Effects: 84048ff0e7eSeric ** Removes files in the current directory. 84148ff0e7eSeric ** Prints information regarding files being edited. 84248ff0e7eSeric ** Exits if a "check" command. 843b2538d76Seric */ 844b2538d76Seric 845ec4190ddSeric clean(mode, argv) 84648ff0e7eSeric int mode; 847ec4190ddSeric char **argv; 848b2538d76Seric { 849b2538d76Seric struct direct dir; 850b2538d76Seric char buf[100]; 8513a208c77Seric register FILE *dirfd; 8523a208c77Seric register char *basefile; 8532caac8fdSeric bool gotedit; 854ec4190ddSeric bool gotpfent; 855bf9c1e66Seric FILE *pfp; 856ec4190ddSeric bool nobranch = FALSE; 857ec4190ddSeric extern struct pfile *getpfent(); 858ec4190ddSeric register struct pfile *pf; 859ec4190ddSeric register char **ap; 8601e0924d0Seric extern char *username(); 8611e0924d0Seric char *usernm = NULL; 862ec4190ddSeric 863ec4190ddSeric /* 864ec4190ddSeric ** Process the argv 865ec4190ddSeric */ 866ec4190ddSeric 867ec4190ddSeric for (ap = argv; *ap != NULL; ap++) 868ec4190ddSeric { 8691e0924d0Seric if (**ap == '-') 8701e0924d0Seric { 8711e0924d0Seric /* we have a flag */ 8721e0924d0Seric switch ((*ap)[1]) 8731e0924d0Seric { 8741e0924d0Seric case 'b': 875ec4190ddSeric nobranch = TRUE; 8761e0924d0Seric break; 8771e0924d0Seric 8781e0924d0Seric case 'u': 8791e0924d0Seric if ((*ap)[2] != '\0') 8801e0924d0Seric usernm = &(*ap)[2]; 8811e0924d0Seric else if (ap[1] != NULL && ap[1][0] != '-') 8821e0924d0Seric usernm = *++ap; 8831e0924d0Seric else 8841e0924d0Seric usernm = username(); 8851e0924d0Seric break; 8861e0924d0Seric } 8871e0924d0Seric } 888ec4190ddSeric } 889b2538d76Seric 890ff038098Seric /* 891ff038098Seric ** Find and open the SCCS directory. 892ff038098Seric */ 893ff038098Seric 894f77a08fbSeric strcpy(buf, SccsDir); 895f77a08fbSeric if (buf[0] != '\0') 896f77a08fbSeric strcat(buf, "/"); 897f77a08fbSeric strcat(buf, SccsPath); 898ff038098Seric 899f77a08fbSeric dirfd = fopen(buf, "r"); 900b2538d76Seric if (dirfd == NULL) 901b2538d76Seric { 902f77a08fbSeric usrerr("cannot open %s", buf); 90341290c52Seric return (EX_NOINPUT); 904b2538d76Seric } 905b2538d76Seric 906b2538d76Seric /* 907b2538d76Seric ** Scan the SCCS directory looking for s. files. 908ff038098Seric ** gotedit tells whether we have tried to clean any 909ff038098Seric ** files that are being edited. 910b2538d76Seric */ 911b2538d76Seric 9122caac8fdSeric gotedit = FALSE; 913fe7b004aSeric while (fread((char *)&dir, sizeof dir, 1, dirfd) != NULL) 914b2538d76Seric { 915c9c62fd0Seric if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) 916b2538d76Seric continue; 917b2538d76Seric 918b2538d76Seric /* got an s. file -- see if the p. file exists */ 919f77a08fbSeric strcpy(buf, SccsDir); 920f77a08fbSeric if (buf[0] != '\0') 921f77a08fbSeric strcat(buf, "/"); 922f77a08fbSeric strcat(buf, SccsPath); 923b2538d76Seric strcat(buf, "/p."); 9243a208c77Seric basefile = &buf[strlen(buf)]; 925c9c62fd0Seric strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 926bf9c1e66Seric basefile[sizeof dir.d_name - 2] = '\0'; 927ec4190ddSeric 928ec4190ddSeric /* 929ec4190ddSeric ** open and scan the p-file. 930ec4190ddSeric ** 'gotpfent' tells if we have found a valid p-file 931ec4190ddSeric ** entry. 932ec4190ddSeric */ 933ec4190ddSeric 934bf9c1e66Seric pfp = fopen(buf, "r"); 935ec4190ddSeric gotpfent = FALSE; 936bf9c1e66Seric if (pfp != NULL) 9373a208c77Seric { 938ff038098Seric /* the file exists -- report it's contents */ 939ec4190ddSeric while ((pf = getpfent(pfp)) != NULL) 9402734e237Seric { 941ec4190ddSeric if (nobranch && isbranch(pf->p_nsid)) 942ec4190ddSeric continue; 9431e0924d0Seric if (usernm != NULL && strcmp(usernm, pf->p_user) != 0 && mode != CLEANC) 9441e0924d0Seric continue; 945ec4190ddSeric gotedit = TRUE; 946ec4190ddSeric gotpfent = TRUE; 947ec4190ddSeric if (mode == TELLC) 948ec4190ddSeric { 949ec4190ddSeric printf("%s\n", basefile); 950ec4190ddSeric break; 951ec4190ddSeric } 952ec4190ddSeric printf("%12s: being edited: %s %s %s %s %s\n", 953ec4190ddSeric basefile, pf->p_osid, pf->p_nsid, 954ec4190ddSeric pf->p_user, pf->p_date, pf->p_time); 9552734e237Seric } 956bf9c1e66Seric fclose(pfp); 9573a208c77Seric } 958b2538d76Seric 959b2538d76Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 9609d7ed7d6Seric if (mode == CLEANC && !gotpfent) 9613a208c77Seric { 962c9c62fd0Seric strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); 9633a208c77Seric buf[sizeof dir.d_name - 2] = '\0'; 964b2538d76Seric unlink(buf); 965b2538d76Seric } 9663a208c77Seric } 967b2538d76Seric 968ff038098Seric /* cleanup & report results */ 969b2538d76Seric fclose(dirfd); 97048ff0e7eSeric if (!gotedit && mode == INFOC) 9711e0924d0Seric { 9721e0924d0Seric printf("Nothing being edited"); 9731e0924d0Seric if (nobranch) 9741e0924d0Seric printf(" (on trunk)"); 9751e0924d0Seric if (usernm == NULL) 9761e0924d0Seric printf("\n"); 9771e0924d0Seric else 9781e0924d0Seric printf(" by %s\n", usernm); 9791e0924d0Seric } 98048ff0e7eSeric if (mode == CHECKC) 98148ff0e7eSeric exit(gotedit); 98241290c52Seric return (EX_OK); 983b2538d76Seric } 9840faf63f9Seric 9850faf63f9Seric /* 986ec4190ddSeric ** ISBRANCH -- is the SID a branch? 987ec4190ddSeric ** 988ec4190ddSeric ** Parameters: 989ec4190ddSeric ** sid -- the sid to check. 990ec4190ddSeric ** 991ec4190ddSeric ** Returns: 992ec4190ddSeric ** TRUE if the sid represents a branch. 993ec4190ddSeric ** FALSE otherwise. 994ec4190ddSeric ** 995ec4190ddSeric ** Side Effects: 996ec4190ddSeric ** none. 997ec4190ddSeric */ 998ec4190ddSeric 999ec4190ddSeric isbranch(sid) 1000ec4190ddSeric char *sid; 1001ec4190ddSeric { 1002ec4190ddSeric register char *p; 1003ec4190ddSeric int dots; 1004ec4190ddSeric 1005ec4190ddSeric dots = 0; 1006ec4190ddSeric for (p = sid; *p != '\0'; p++) 1007ec4190ddSeric { 1008ec4190ddSeric if (*p == '.') 1009ec4190ddSeric dots++; 1010ec4190ddSeric if (dots > 1) 1011ec4190ddSeric return (TRUE); 1012ec4190ddSeric } 1013ec4190ddSeric return (FALSE); 1014ec4190ddSeric } 1015ec4190ddSeric 1016ec4190ddSeric /* 1017e39a5722Seric ** UNEDIT -- unedit a file 1018e39a5722Seric ** 1019e39a5722Seric ** Checks to see that the current user is actually editting 1020e39a5722Seric ** the file and arranges that s/he is not editting it. 1021e39a5722Seric ** 1022e39a5722Seric ** Parameters: 10232444cd3eSeric ** fn -- the name of the file to be unedited. 1024e39a5722Seric ** 1025e39a5722Seric ** Returns: 1026d51cd7e5Seric ** TRUE -- if the file was successfully unedited. 1027d51cd7e5Seric ** FALSE -- if the file was not unedited for some 1028d51cd7e5Seric ** reason. 1029e39a5722Seric ** 1030e39a5722Seric ** Side Effects: 1031e39a5722Seric ** fn is removed 1032e39a5722Seric ** entries are removed from pfile. 1033e39a5722Seric */ 1034e39a5722Seric 1035d51cd7e5Seric bool 1036e39a5722Seric unedit(fn) 1037e39a5722Seric char *fn; 1038e39a5722Seric { 1039e39a5722Seric register FILE *pfp; 1040e39a5722Seric char *pfn; 1041e39a5722Seric static char tfn[] = "/tmp/sccsXXXXX"; 1042e39a5722Seric FILE *tfp; 1043e39a5722Seric register char *q; 1044e39a5722Seric bool delete = FALSE; 1045e39a5722Seric bool others = FALSE; 1046e39a5722Seric char *myname; 10471e0924d0Seric extern char *username(); 1048e39a5722Seric struct pfile *pent; 1049ec4190ddSeric extern struct pfile *getpfent(); 1050e39a5722Seric char buf[120]; 1051108d6082Seric extern char *makefile(); 1052e39a5722Seric 1053e39a5722Seric /* make "s." filename & find the trailing component */ 1054e39a5722Seric pfn = makefile(fn); 1055cdc1aa65Seric if (pfn == NULL) 1056cdc1aa65Seric return (FALSE); 1057cdc1aa65Seric q = rindex(pfn, '/'); 1058cdc1aa65Seric if (q == NULL) 1059cdc1aa65Seric q = &pfn[-1]; 1060cdc1aa65Seric if (q[1] != 's' || q[2] != '.') 1061e39a5722Seric { 1062095915d3Seric usrerr("bad file name \"%s\"", fn); 1063d51cd7e5Seric return (FALSE); 1064e39a5722Seric } 1065e39a5722Seric 1066ff038098Seric /* turn "s." into "p." & try to open it */ 1067e39a5722Seric *++q = 'p'; 1068e39a5722Seric 1069e39a5722Seric pfp = fopen(pfn, "r"); 1070e39a5722Seric if (pfp == NULL) 1071e39a5722Seric { 10722444cd3eSeric printf("%12s: not being edited\n", fn); 1073d51cd7e5Seric return (FALSE); 1074e39a5722Seric } 1075e39a5722Seric 1076ff038098Seric /* create temp file for editing p-file */ 1077e39a5722Seric mktemp(tfn); 1078e39a5722Seric tfp = fopen(tfn, "w"); 1079e39a5722Seric if (tfp == NULL) 1080e39a5722Seric { 1081095915d3Seric usrerr("cannot create \"%s\"", tfn); 1082e39a5722Seric exit(EX_OSERR); 1083e39a5722Seric } 1084e39a5722Seric 1085ff038098Seric /* figure out who I am */ 10861e0924d0Seric myname = username(); 1087ff038098Seric 1088ff038098Seric /* 1089ff038098Seric ** Copy p-file to temp file, doing deletions as needed. 1090ff038098Seric */ 1091ff038098Seric 1092ec4190ddSeric while ((pent = getpfent(pfp)) != NULL) 1093e39a5722Seric { 1094e39a5722Seric if (strcmp(pent->p_user, myname) == 0) 1095e39a5722Seric { 1096e39a5722Seric /* a match */ 1097e39a5722Seric delete++; 1098e39a5722Seric } 1099e39a5722Seric else 1100e39a5722Seric { 1101ff038098Seric /* output it again */ 1102e39a5722Seric fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, 1103e39a5722Seric pent->p_nsid, pent->p_user, pent->p_date, 1104e39a5722Seric pent->p_time); 1105e39a5722Seric others++; 1106e39a5722Seric } 1107e39a5722Seric } 1108e39a5722Seric 1109e39a5722Seric /* do final cleanup */ 1110e39a5722Seric if (others) 1111e39a5722Seric { 1112ff038098Seric /* copy it back (perhaps it should be linked?) */ 1113e39a5722Seric if (freopen(tfn, "r", tfp) == NULL) 1114e39a5722Seric { 1115095915d3Seric syserr("cannot reopen \"%s\"", tfn); 1116e39a5722Seric exit(EX_OSERR); 1117e39a5722Seric } 1118e39a5722Seric if (freopen(pfn, "w", pfp) == NULL) 1119e39a5722Seric { 1120095915d3Seric usrerr("cannot create \"%s\"", pfn); 1121d51cd7e5Seric return (FALSE); 1122e39a5722Seric } 1123e39a5722Seric while (fgets(buf, sizeof buf, tfp) != NULL) 1124e39a5722Seric fputs(buf, pfp); 1125e39a5722Seric } 1126e39a5722Seric else 1127e39a5722Seric { 1128ff038098Seric /* it's empty -- remove it */ 1129e39a5722Seric unlink(pfn); 1130e39a5722Seric } 1131e39a5722Seric fclose(tfp); 1132e39a5722Seric fclose(pfp); 1133e39a5722Seric unlink(tfn); 1134e39a5722Seric 1135ff038098Seric /* actually remove the g-file */ 1136e39a5722Seric if (delete) 1137e39a5722Seric { 1138287bbaa2Seric unlink(tail(fn)); 1139287bbaa2Seric printf("%12s: removed\n", tail(fn)); 1140d51cd7e5Seric return (TRUE); 1141e39a5722Seric } 1142e39a5722Seric else 1143e39a5722Seric { 11442444cd3eSeric printf("%12s: not being edited by you\n", fn); 1145d51cd7e5Seric return (FALSE); 1146e39a5722Seric } 1147e39a5722Seric } 11480faf63f9Seric 11490faf63f9Seric /* 115078d41c6aSeric ** DODIFF -- diff an s-file against a g-file 115178d41c6aSeric ** 115278d41c6aSeric ** Parameters: 115378d41c6aSeric ** getv -- argv for the 'get' command. 115478d41c6aSeric ** gfile -- name of the g-file to diff against. 115578d41c6aSeric ** 115678d41c6aSeric ** Returns: 115778d41c6aSeric ** Result of get. 115878d41c6aSeric ** 115978d41c6aSeric ** Side Effects: 116078d41c6aSeric ** none. 116178d41c6aSeric */ 116278d41c6aSeric 116378d41c6aSeric dodiff(getv, gfile) 116478d41c6aSeric char **getv; 116578d41c6aSeric char *gfile; 116678d41c6aSeric { 116778d41c6aSeric int pipev[2]; 116878d41c6aSeric int rval; 116978d41c6aSeric register int i; 117078d41c6aSeric register int pid; 117178d41c6aSeric auto int st; 117278d41c6aSeric extern int errno; 117378d41c6aSeric int (*osig)(); 117478d41c6aSeric 1175cc4e4f0dSeric printf("\n------- %s -------\n", gfile); 11762569d37aSeric fflush(stdout); 1177cc4e4f0dSeric 1178ff038098Seric /* create context for diff to run in */ 117978d41c6aSeric if (pipe(pipev) < 0) 118078d41c6aSeric { 118178d41c6aSeric syserr("dodiff: pipe failed"); 118278d41c6aSeric exit(EX_OSERR); 118378d41c6aSeric } 118478d41c6aSeric if ((pid = fork()) < 0) 118578d41c6aSeric { 118678d41c6aSeric syserr("dodiff: fork failed"); 118778d41c6aSeric exit(EX_OSERR); 118878d41c6aSeric } 118978d41c6aSeric else if (pid > 0) 119078d41c6aSeric { 119178d41c6aSeric /* in parent; run get */ 119278d41c6aSeric OutFile = pipev[1]; 119378d41c6aSeric close(pipev[0]); 11942569d37aSeric rval = command(&getv[1], TRUE, "get:rcixt -s -k -p"); 119578d41c6aSeric osig = signal(SIGINT, SIG_IGN); 119678d41c6aSeric while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR) 119778d41c6aSeric errno = 0; 119878d41c6aSeric signal(SIGINT, osig); 119978d41c6aSeric /* ignore result of diff */ 120078d41c6aSeric } 120178d41c6aSeric else 120278d41c6aSeric { 120378d41c6aSeric /* in child, run diff */ 120478d41c6aSeric if (close(pipev[1]) < 0 || close(0) < 0 || 120578d41c6aSeric dup(pipev[0]) != 0 || close(pipev[0]) < 0) 120678d41c6aSeric { 120778d41c6aSeric syserr("dodiff: magic failed"); 120878d41c6aSeric exit(EX_OSERR); 120978d41c6aSeric } 12102569d37aSeric command(&getv[1], FALSE, "-diff:elsfhbC"); 121178d41c6aSeric } 121278d41c6aSeric return (rval); 121378d41c6aSeric } 121478d41c6aSeric 121578d41c6aSeric /* 1216287bbaa2Seric ** TAIL -- return tail of filename. 1217287bbaa2Seric ** 1218287bbaa2Seric ** Parameters: 1219287bbaa2Seric ** fn -- the filename. 1220287bbaa2Seric ** 1221287bbaa2Seric ** Returns: 1222287bbaa2Seric ** a pointer to the tail of the filename; e.g., given 1223287bbaa2Seric ** "cmd/ls.c", "ls.c" is returned. 1224287bbaa2Seric ** 1225287bbaa2Seric ** Side Effects: 1226287bbaa2Seric ** none. 1227287bbaa2Seric */ 1228287bbaa2Seric 1229287bbaa2Seric char * 1230287bbaa2Seric tail(fn) 1231287bbaa2Seric register char *fn; 1232287bbaa2Seric { 1233287bbaa2Seric register char *p; 1234287bbaa2Seric 1235287bbaa2Seric for (p = fn; *p != 0; p++) 1236287bbaa2Seric if (*p == '/' && p[1] != '\0' && p[1] != '/') 1237287bbaa2Seric fn = &p[1]; 1238287bbaa2Seric return (fn); 1239287bbaa2Seric } 1240287bbaa2Seric 1241287bbaa2Seric /* 1242ec4190ddSeric ** GETPFENT -- get an entry from the p-file 1243e39a5722Seric ** 1244e39a5722Seric ** Parameters: 1245e39a5722Seric ** pfp -- p-file file pointer 1246e39a5722Seric ** 1247e39a5722Seric ** Returns: 1248e39a5722Seric ** pointer to p-file struct for next entry 1249e39a5722Seric ** NULL on EOF or error 1250e39a5722Seric ** 1251e39a5722Seric ** Side Effects: 1252e39a5722Seric ** Each call wipes out results of previous call. 1253e39a5722Seric */ 1254e39a5722Seric 1255e39a5722Seric struct pfile * 1256ec4190ddSeric getpfent(pfp) 1257e39a5722Seric FILE *pfp; 1258e39a5722Seric { 1259e39a5722Seric static struct pfile ent; 1260e39a5722Seric static char buf[120]; 1261e39a5722Seric register char *p; 1262e39a5722Seric extern char *nextfield(); 1263e39a5722Seric 1264e39a5722Seric if (fgets(buf, sizeof buf, pfp) == NULL) 1265e39a5722Seric return (NULL); 1266e39a5722Seric 1267e39a5722Seric ent.p_osid = p = buf; 1268e39a5722Seric ent.p_nsid = p = nextfield(p); 1269e39a5722Seric ent.p_user = p = nextfield(p); 1270e39a5722Seric ent.p_date = p = nextfield(p); 1271e39a5722Seric ent.p_time = p = nextfield(p); 1272e39a5722Seric if (p == NULL || nextfield(p) != NULL) 1273e39a5722Seric return (NULL); 1274e39a5722Seric 1275e39a5722Seric return (&ent); 1276e39a5722Seric } 1277e39a5722Seric 1278e39a5722Seric 1279e39a5722Seric char * 1280e39a5722Seric nextfield(p) 1281e39a5722Seric register char *p; 1282e39a5722Seric { 1283e39a5722Seric if (p == NULL || *p == '\0') 1284e39a5722Seric return (NULL); 1285e39a5722Seric while (*p != ' ' && *p != '\n' && *p != '\0') 1286e39a5722Seric p++; 1287e39a5722Seric if (*p == '\n' || *p == '\0') 1288e39a5722Seric { 1289e39a5722Seric *p = '\0'; 1290e39a5722Seric return (NULL); 1291e39a5722Seric } 1292e39a5722Seric *p++ = '\0'; 1293e39a5722Seric return (p); 1294e39a5722Seric } 12950faf63f9Seric 12960faf63f9Seric /* 1297095915d3Seric ** USRERR -- issue user-level error 1298095915d3Seric ** 1299095915d3Seric ** Parameters: 1300095915d3Seric ** f -- format string. 1301095915d3Seric ** p1-p3 -- parameters to a printf. 1302095915d3Seric ** 1303095915d3Seric ** Returns: 1304095915d3Seric ** -1 1305095915d3Seric ** 1306095915d3Seric ** Side Effects: 1307095915d3Seric ** none. 1308095915d3Seric */ 1309095915d3Seric 1310fe7b004aSeric /*VARARGS1*/ 1311095915d3Seric usrerr(f, p1, p2, p3) 1312095915d3Seric char *f; 1313095915d3Seric { 1314095915d3Seric fprintf(stderr, "\n%s: ", MyName); 1315095915d3Seric fprintf(stderr, f, p1, p2, p3); 1316095915d3Seric fprintf(stderr, "\n"); 1317095915d3Seric 1318095915d3Seric return (-1); 1319095915d3Seric } 13200faf63f9Seric 13210faf63f9Seric /* 1322095915d3Seric ** SYSERR -- print system-generated error. 1323095915d3Seric ** 1324095915d3Seric ** Parameters: 1325095915d3Seric ** f -- format string to a printf. 1326095915d3Seric ** p1, p2, p3 -- parameters to f. 1327095915d3Seric ** 1328095915d3Seric ** Returns: 1329095915d3Seric ** never. 1330095915d3Seric ** 1331095915d3Seric ** Side Effects: 1332095915d3Seric ** none. 1333095915d3Seric */ 1334095915d3Seric 1335fe7b004aSeric /*VARARGS1*/ 1336095915d3Seric syserr(f, p1, p2, p3) 1337095915d3Seric char *f; 1338095915d3Seric { 1339095915d3Seric extern int errno; 1340095915d3Seric 1341095915d3Seric fprintf(stderr, "\n%s SYSERR: ", MyName); 1342095915d3Seric fprintf(stderr, f, p1, p2, p3); 1343095915d3Seric fprintf(stderr, "\n"); 1344095915d3Seric if (errno == 0) 1345095915d3Seric exit(EX_SOFTWARE); 1346095915d3Seric else 1347095915d3Seric { 1348fe7b004aSeric perror(NULL); 1349095915d3Seric exit(EX_OSERR); 1350095915d3Seric } 1351095915d3Seric } 13521e0924d0Seric /* 13531e0924d0Seric ** USERNAME -- return name of the current user 13541e0924d0Seric ** 13551e0924d0Seric ** Parameters: 13561e0924d0Seric ** none 13571e0924d0Seric ** 13581e0924d0Seric ** Returns: 13591e0924d0Seric ** name of current user 13601e0924d0Seric ** 13611e0924d0Seric ** Side Effects: 13621e0924d0Seric ** none 13631e0924d0Seric */ 13641e0924d0Seric 13651e0924d0Seric char * 13661e0924d0Seric username() 13671e0924d0Seric { 13681e0924d0Seric # ifdef UIDUSER 13691e0924d0Seric extern struct passwd *getpwuid(); 13701e0924d0Seric register struct passwd *pw; 13711e0924d0Seric 13721e0924d0Seric pw = getpwuid(getuid()); 13731e0924d0Seric if (pw == NULL) 13741e0924d0Seric { 13751e0924d0Seric syserr("who are you? (uid=%d)", getuid()); 13761e0924d0Seric exit(EX_OSERR); 13771e0924d0Seric } 13781e0924d0Seric return (pw->pw_name); 13791e0924d0Seric # else 1380ed320446Seric extern char *getlogin(); 1381ed320446Seric 13821e0924d0Seric return (getlogin()); 13831e0924d0Seric # endif UIDUSER 13841e0924d0Seric } 1385