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> 9393e27f2Seric # include <pwd.h> 10adf8f7d4Seric 1110cd16efSeric /* 1210cd16efSeric ** SCCS.C -- human-oriented front end to the SCCS system. 1310cd16efSeric ** 1410cd16efSeric ** Without trying to add any functionality to speak of, this 1510cd16efSeric ** program tries to make SCCS a little more accessible to human 1610cd16efSeric ** types. The main thing it does is automatically put the 1710cd16efSeric ** string "SCCS/s." on the front of names. Also, it has a 1810cd16efSeric ** couple of things that are designed to shorten frequent 1910cd16efSeric ** combinations, e.g., "delget" which expands to a "delta" 2010cd16efSeric ** and a "get". 2110cd16efSeric ** 2210cd16efSeric ** This program can also function as a setuid front end. 2310cd16efSeric ** To do this, you should copy the source, renaming it to 2410cd16efSeric ** whatever you want, e.g., "syssccs". Change any defaults 2510cd16efSeric ** in the program (e.g., syssccs might default -d to 2610cd16efSeric ** "/usr/src/sys"). Then recompile and put the result 2710cd16efSeric ** as setuid to whomever you want. In this mode, sccs 2810cd16efSeric ** knows to not run setuid for certain programs in order 2910cd16efSeric ** to preserve security, and so forth. 3010cd16efSeric ** 3110cd16efSeric ** Usage: 3210cd16efSeric ** sccs [flags] command [args] 3310cd16efSeric ** 3410cd16efSeric ** Flags: 3510cd16efSeric ** -d<dir> <dir> represents a directory to search 3610cd16efSeric ** out of. It should be a full pathname 3710cd16efSeric ** for general usage. E.g., if <dir> is 3810cd16efSeric ** "/usr/src/sys", then a reference to the 3910cd16efSeric ** file "dev/bio.c" becomes a reference to 4010cd16efSeric ** "/usr/src/sys/dev/bio.c". 4110cd16efSeric ** -p<path> prepends <path> to the final component 4210cd16efSeric ** of the pathname. By default, this is 4310cd16efSeric ** "SCCS". For example, in the -d example 4410cd16efSeric ** above, the path then gets modified to 4510cd16efSeric ** "/usr/src/sys/dev/SCCS/s.bio.c". In 4610cd16efSeric ** more common usage (without the -d flag), 4710cd16efSeric ** "prog.c" would get modified to 4810cd16efSeric ** "SCCS/s.prog.c". In both cases, the 4910cd16efSeric ** "s." gets automatically prepended. 5010cd16efSeric ** -r run as the real user. 5110cd16efSeric ** 5210cd16efSeric ** Commands: 5310cd16efSeric ** admin, 5410cd16efSeric ** get, 5510cd16efSeric ** delta, 5610cd16efSeric ** rmdel, 5710cd16efSeric ** chghist, 5810cd16efSeric ** etc. Straight out of SCCS; only difference 5910cd16efSeric ** is that pathnames get modified as 6010cd16efSeric ** described above. 6110cd16efSeric ** edit Macro for "get -e". 6210cd16efSeric ** unedit Removes a file being edited, knowing 6310cd16efSeric ** about p-files, etc. 6410cd16efSeric ** delget Macro for "delta" followed by "get". 6510cd16efSeric ** deledit Macro for "delta" followed by "get -e". 6610cd16efSeric ** info Tell what files being edited. 6710cd16efSeric ** clean Remove all files that can be 6810cd16efSeric ** regenerated from SCCS files. 69095915d3Seric ** check Like info, but return exit status, for 7010cd16efSeric ** use in makefiles. 7110cd16efSeric ** fix Remove a top delta & reedit, but save 7210cd16efSeric ** the previous changes in that delta. 7310cd16efSeric ** 7410cd16efSeric ** Compilation Flags: 7510cd16efSeric ** UIDUSER -- determine who the user is by looking at the 7610cd16efSeric ** uid rather than the login name -- for machines 7710cd16efSeric ** where SCCS gets the user in this way. 787abec564Seric ** SCCSDIR -- if defined, forces the -d flag to take on 79095915d3Seric ** this value. This is so that the setuid 80095915d3Seric ** aspects of this program cannot be abused. 817abec564Seric ** This flag also disables the -p flag. 827abec564Seric ** SCCSPATH -- the default for the -p flag. 83c4ad8825Seric ** MYNAME -- the title this program should print when it 84c4ad8825Seric ** gives error messages. 8510cd16efSeric ** 8610cd16efSeric ** Compilation Instructions: 8710cd16efSeric ** cc -O -n -s sccs.c 88c4ad8825Seric ** The flags listed above can be -D defined to simplify 89c4ad8825Seric ** recompilation for variant versions. 9010cd16efSeric ** 9110cd16efSeric ** Author: 9210cd16efSeric ** Eric Allman, UCB/INGRES 937abec564Seric ** Copyright 1980 Regents of the University of California 9410cd16efSeric */ 9510cd16efSeric 96*a4f81e73Seric static char SccsId[] = "@(#)sccs.c 1.60 01/25/81"; 970faf63f9Seric 987abec564Seric /******************* Configuration Information ********************/ 997abec564Seric 100c4ad8825Seric /* special defines for local berkeley systems */ 101c4ad8825Seric # include <whoami.h> 102c4ad8825Seric 10310cd16efSeric # ifdef CSVAX 10410cd16efSeric # define UIDUSER 1057abec564Seric # define PROGPATH(name) "/usr/local/name" 1067abec564Seric # endif CSVAX 10710cd16efSeric 1086de0267aSeric # ifdef INGVAX 1096de0267aSeric # define PROGPATH(name) "/usr/local/name" 1106de0267aSeric # endif INGVAX 1116de0267aSeric 11231bc6d6cSeric # ifdef CORY 11331bc6d6cSeric # define PROGPATH(name) "/usr/eecs/bin/name" 11431bc6d6cSeric # endif CORY 11531bc6d6cSeric 116c4ad8825Seric /* end of berkeley systems defines */ 1177abec564Seric 118c4ad8825Seric # ifndef SCCSPATH 119c4ad8825Seric # define SCCSPATH "SCCS" /* pathname in which to find s-files */ 120c4ad8825Seric # endif NOT SCCSPATH 121c4ad8825Seric 122c4ad8825Seric # ifndef MYNAME 123c4ad8825Seric # define MYNAME "sccs" /* name used for printing errors */ 124c4ad8825Seric # endif NOT MYNAME 1257abec564Seric 1260faf63f9Seric # ifndef PROGPATH 1270faf63f9Seric # define PROGPATH(name) "/usr/sccs/name" /* place to find binaries */ 1280faf63f9Seric # endif PROGPATH 1290faf63f9Seric 1307abec564Seric /**************** End of Configuration Information ****************/ 1310faf63f9Seric 132c4432be4Seric typedef char bool; 1337de81dc7Seric # define TRUE 1 1347de81dc7Seric # define FALSE 0 135d02a4f42Seric 136ff038098Seric # define bitset(bit, word) ((bool) ((bit) & (word))) 137ff038098Seric 138adf8f7d4Seric struct sccsprog 139adf8f7d4Seric { 140adf8f7d4Seric char *sccsname; /* name of SCCS routine */ 1417de81dc7Seric short sccsoper; /* opcode, see below */ 1427de81dc7Seric short sccsflags; /* flags, see below */ 143adf8f7d4Seric char *sccspath; /* pathname of binary implementing */ 144adf8f7d4Seric }; 145adf8f7d4Seric 1467de81dc7Seric /* values for sccsoper */ 1477de81dc7Seric # define PROG 0 /* call a program */ 1481777fbcbSeric # define CMACRO 1 /* command substitution macro */ 149172147faSeric # define FIX 2 /* fix a delta */ 150b2538d76Seric # define CLEAN 3 /* clean out recreatable files */ 151e39a5722Seric # define UNEDIT 4 /* unedit a file */ 152419708b0Seric # define SHELL 5 /* call a shell file (like PROG) */ 15378d41c6aSeric # define DIFFS 6 /* diff between sccs & file out */ 1542569d37aSeric # define DODIFF 7 /* internal call to diff program */ 1557de81dc7Seric 156c4432be4Seric /* bits for sccsflags */ 1577de81dc7Seric # define NO_SDOT 0001 /* no s. on front of args */ 1587de81dc7Seric # define REALUSER 0002 /* protected (e.g., admin) */ 159adf8f7d4Seric 16048ff0e7eSeric /* modes for the "clean", "info", "check" ops */ 16148ff0e7eSeric # define CLEANC 0 /* clean command */ 16248ff0e7eSeric # define INFOC 1 /* info command */ 16348ff0e7eSeric # define CHECKC 2 /* check command */ 1642734e237Seric # define TELLC 3 /* give list of files being edited */ 16548ff0e7eSeric 1660faf63f9Seric /* 1670faf63f9Seric ** Description of commands known to this program. 1680faf63f9Seric ** First argument puts the command into a class. Second arg is 1690faf63f9Seric ** info regarding treatment of this command. Third arg is a 1700faf63f9Seric ** list of flags this command accepts from macros, etc. Fourth 1710faf63f9Seric ** arg is the pathname of the implementing program, or the 1720faf63f9Seric ** macro definition, or the arg to a sub-algorithm. 1730faf63f9Seric */ 174b5d4f080Seric 175adf8f7d4Seric struct sccsprog SccsProg[] = 176adf8f7d4Seric { 1773642a8d2Seric "admin", PROG, REALUSER, PROGPATH(admin), 1783642a8d2Seric "chghist", PROG, 0, PROGPATH(rmdel), 1793642a8d2Seric "comb", PROG, 0, PROGPATH(comb), 1803642a8d2Seric "delta", PROG, 0, PROGPATH(delta), 1813642a8d2Seric "get", PROG, 0, PROGPATH(get), 1823642a8d2Seric "help", PROG, NO_SDOT, PROGPATH(help), 1833a3bf7bfSeric "prs", PROG, 0, PROGPATH(prs), 1843642a8d2Seric "prt", PROG, 0, PROGPATH(prt), 1853642a8d2Seric "rmdel", PROG, REALUSER, PROGPATH(rmdel), 1863a3bf7bfSeric "val", PROG, 0, PROGPATH(val), 1873642a8d2Seric "what", PROG, NO_SDOT, PROGPATH(what), 1883642a8d2Seric "sccsdiff", SHELL, REALUSER, PROGPATH(sccsdiff), 1893642a8d2Seric "edit", CMACRO, NO_SDOT, "get -e", 19059e81a43Seric "delget", CMACRO, NO_SDOT, "delta:mysrp/get:ixbeskcl -t", 1911cf60de3Seric "deledit", CMACRO, NO_SDOT, "delta:mysrp -n/get:ixbskcl -e -t -g", 1923642a8d2Seric "fix", FIX, NO_SDOT, NULL, 1931e0924d0Seric "clean", CLEAN, REALUSER|NO_SDOT, (char *) CLEANC, 1941e0924d0Seric "info", CLEAN, REALUSER|NO_SDOT, (char *) INFOC, 1951e0924d0Seric "check", CLEAN, REALUSER|NO_SDOT, (char *) CHECKC, 1961e0924d0Seric "tell", CLEAN, REALUSER|NO_SDOT, (char *) TELLC, 1973642a8d2Seric "unedit", UNEDIT, NO_SDOT, NULL, 1983642a8d2Seric "diffs", DIFFS, NO_SDOT|REALUSER, NULL, 1992569d37aSeric "-diff", DODIFF, NO_SDOT|REALUSER, PROGPATH(bdiff), 200b387a58fSeric "print", CMACRO, 0, "prt -e/get -p -m -s", 201*a4f81e73Seric "branch", CMACRO, NO_SDOT, 202*a4f81e73Seric "get:ixrc -e -b/delta: -s -n -ybranch-place-holder/get:pl -e -t -g", 2033642a8d2Seric NULL, -1, 0, NULL 204adf8f7d4Seric }; 205adf8f7d4Seric 2060faf63f9Seric /* one line from a p-file */ 207e39a5722Seric struct pfile 208e39a5722Seric { 209e39a5722Seric char *p_osid; /* old SID */ 210e39a5722Seric char *p_nsid; /* new SID */ 211e39a5722Seric char *p_user; /* user who did edit */ 212e39a5722Seric char *p_date; /* date of get */ 213e39a5722Seric char *p_time; /* time of get */ 21477a8a0dfSeric char *p_aux; /* extra info at end */ 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 22992fabd68Seric # ifndef V6 23092fabd68Seric extern char *getenv(); 23192fabd68Seric # 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; 24092fabd68Seric # ifndef V6 24192fabd68Seric # ifndef SCCSDIR 242393e27f2Seric register struct passwd *pw; 243393e27f2Seric extern struct passwd *getpwnam(); 244393e27f2Seric char buf[100]; 245393e27f2Seric 24692fabd68Seric /* pull "SccsDir" out of the environment (possibly) */ 24792fabd68Seric p = getenv("PROJECT"); 248393e27f2Seric if (p != NULL && p[0] != '\0') 249393e27f2Seric { 25092fabd68Seric if (p[0] == '/') 25192fabd68Seric SccsDir = p; 25292fabd68Seric else 253393e27f2Seric { 254393e27f2Seric pw = getpwnam(p); 255393e27f2Seric if (pw == NULL) 256393e27f2Seric { 257393e27f2Seric usrerr("user %s does not exist", p); 258393e27f2Seric exit(EX_USAGE); 259393e27f2Seric } 260393e27f2Seric strcpy(buf, pw->pw_dir); 261393e27f2Seric strcat(buf, "/src"); 262393e27f2Seric if (access(buf, 0) < 0) 263393e27f2Seric { 264393e27f2Seric strcpy(buf, pw->pw_dir); 265393e27f2Seric strcat(buf, "/source"); 266393e27f2Seric if (access(buf, 0) < 0) 267393e27f2Seric { 268393e27f2Seric usrerr("project %s has no source!", p); 269393e27f2Seric exit(EX_USAGE); 270393e27f2Seric } 271393e27f2Seric } 272393e27f2Seric SccsDir = buf; 273393e27f2Seric } 274393e27f2Seric } 27592fabd68Seric # endif SCCSDIR 27692fabd68Seric # endif V6 27792fabd68Seric 278adf8f7d4Seric /* 279adf8f7d4Seric ** Detect and decode flags intended for this program. 280adf8f7d4Seric */ 281adf8f7d4Seric 2827de81dc7Seric if (argc < 2) 283adf8f7d4Seric { 284095915d3Seric fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); 2857de81dc7Seric exit(EX_USAGE); 2867de81dc7Seric } 2877de81dc7Seric argv[argc] = NULL; 2887de81dc7Seric 289b1ed8a43Seric if (lookup(argv[0]) == NULL) 290b1ed8a43Seric { 2917de81dc7Seric while ((p = *++argv) != NULL) 2927de81dc7Seric { 293adf8f7d4Seric if (*p != '-') 294adf8f7d4Seric break; 295adf8f7d4Seric switch (*++p) 296adf8f7d4Seric { 297adf8f7d4Seric case 'r': /* run as real user */ 298adf8f7d4Seric setuid(getuid()); 299c4432be4Seric RealUser++; 300adf8f7d4Seric break; 301adf8f7d4Seric 3027abec564Seric # ifndef SCCSDIR 303adf8f7d4Seric case 'p': /* path of sccs files */ 304adf8f7d4Seric SccsPath = ++p; 305adf8f7d4Seric break; 306adf8f7d4Seric 3070a8b9ab0Seric case 'd': /* directory to search from */ 3080a8b9ab0Seric SccsDir = ++p; 3090a8b9ab0Seric break; 310095915d3Seric # endif 3110a8b9ab0Seric 312f0cc3246Seric # ifdef DEBUG 313f0cc3246Seric case 'T': /* trace */ 314f0cc3246Seric Debug++; 315f0cc3246Seric break; 316f0cc3246Seric # endif 317f0cc3246Seric 318adf8f7d4Seric default: 319095915d3Seric usrerr("unknown option -%s", p); 320adf8f7d4Seric break; 321adf8f7d4Seric } 322adf8f7d4Seric } 3235cabffd9Seric if (SccsPath[0] == '\0') 3245cabffd9Seric SccsPath = "."; 325b1ed8a43Seric } 326adf8f7d4Seric 3273642a8d2Seric i = command(argv, FALSE, ""); 32841290c52Seric exit(i); 3297de81dc7Seric } 3300faf63f9Seric 3310faf63f9Seric /* 33241290c52Seric ** COMMAND -- look up and perform a command 33341290c52Seric ** 33441290c52Seric ** This routine is the guts of this program. Given an 33541290c52Seric ** argument vector, it looks up the "command" (argv[0]) 33641290c52Seric ** in the configuration table and does the necessary stuff. 33741290c52Seric ** 33841290c52Seric ** Parameters: 33941290c52Seric ** argv -- an argument vector to process. 34041290c52Seric ** forkflag -- if set, fork before executing the command. 341108d6082Seric ** editflag -- if set, only include flags listed in the 342108d6082Seric ** sccsklets field of the command descriptor. 343108d6082Seric ** arg0 -- a space-seperated list of arguments to insert 344108d6082Seric ** before argv. 34541290c52Seric ** 34641290c52Seric ** Returns: 34741290c52Seric ** zero -- command executed ok. 34841290c52Seric ** else -- error status. 34941290c52Seric ** 35041290c52Seric ** Side Effects: 35141290c52Seric ** none. 35241290c52Seric */ 3537de81dc7Seric 3543642a8d2Seric command(argv, forkflag, arg0) 3557de81dc7Seric char **argv; 3561777fbcbSeric bool forkflag; 357108d6082Seric char *arg0; 3587de81dc7Seric { 3597de81dc7Seric register struct sccsprog *cmd; 3607de81dc7Seric register char *p; 3611777fbcbSeric char buf[40]; 362b1ed8a43Seric extern struct sccsprog *lookup(); 363108d6082Seric char *nav[1000]; 364108d6082Seric char **np; 365419708b0Seric register char **ap; 366d51cd7e5Seric register int i; 367419708b0Seric register char *q; 368d51cd7e5Seric extern bool unedit(); 36941290c52Seric int rval = 0; 370108d6082Seric extern char *index(); 371108d6082Seric extern char *makefile(); 3723642a8d2Seric char *editchs; 373287bbaa2Seric extern char *tail(); 374f0cc3246Seric 375f0cc3246Seric # ifdef DEBUG 376f0cc3246Seric if (Debug) 377f0cc3246Seric { 378108d6082Seric printf("command:\n\t\"%s\"\n", arg0); 379108d6082Seric for (np = argv; *np != NULL; np++) 380108d6082Seric printf("\t\"%s\"\n", *np); 381f0cc3246Seric } 382f0cc3246Seric # endif 383c4432be4Seric 384c4432be4Seric /* 385108d6082Seric ** Copy arguments. 386ff038098Seric ** Copy from arg0 & if necessary at most one arg 387ff038098Seric ** from argv[0]. 388adf8f7d4Seric */ 389adf8f7d4Seric 390419708b0Seric np = ap = &nav[1]; 3913642a8d2Seric editchs = NULL; 39259e81a43Seric for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) 393108d6082Seric { 394108d6082Seric *np++ = q; 395108d6082Seric while (*p == ' ') 396108d6082Seric p++; 3973642a8d2Seric while (*p != ' ' && *p != '\0' && *p != '/' && *p != ':') 398108d6082Seric *q++ = *p++; 399108d6082Seric *q++ = '\0'; 4003642a8d2Seric if (*p == ':') 4013642a8d2Seric { 4023642a8d2Seric editchs = q; 40359e81a43Seric while (*++p != '\0' && *p != '/' && *p != ' ') 4043642a8d2Seric *q++ = *p; 4053642a8d2Seric *q++ = '\0'; 4063642a8d2Seric } 407108d6082Seric } 408108d6082Seric *np = NULL; 409419708b0Seric if (*ap == NULL) 410108d6082Seric *np++ = *argv++; 411108d6082Seric 412108d6082Seric /* 413108d6082Seric ** Look up command. 414419708b0Seric ** At this point, *ap is the command name. 415108d6082Seric */ 416108d6082Seric 417419708b0Seric cmd = lookup(*ap); 418b1ed8a43Seric if (cmd == NULL) 419adf8f7d4Seric { 420419708b0Seric usrerr("Unknown command \"%s\"", *ap); 42141290c52Seric return (EX_USAGE); 422adf8f7d4Seric } 423adf8f7d4Seric 424adf8f7d4Seric /* 425108d6082Seric ** Copy remaining arguments doing editing as appropriate. 426108d6082Seric */ 427108d6082Seric 428108d6082Seric for (; *argv != NULL; argv++) 429108d6082Seric { 430108d6082Seric p = *argv; 431108d6082Seric if (*p == '-') 432108d6082Seric { 4333642a8d2Seric if (p[1] == '\0' || editchs == NULL || index(editchs, p[1]) != NULL) 434108d6082Seric *np++ = p; 435108d6082Seric } 436108d6082Seric else 437108d6082Seric { 438108d6082Seric if (!bitset(NO_SDOT, cmd->sccsflags)) 439108d6082Seric p = makefile(p); 440108d6082Seric if (p != NULL) 441108d6082Seric *np++ = p; 442108d6082Seric } 443108d6082Seric } 444108d6082Seric *np = NULL; 445108d6082Seric 446108d6082Seric /* 4477de81dc7Seric ** Interpret operation associated with this command. 448c4432be4Seric */ 449c4432be4Seric 4507de81dc7Seric switch (cmd->sccsoper) 4517de81dc7Seric { 452419708b0Seric case SHELL: /* call a shell file */ 453419708b0Seric *ap = cmd->sccspath; 454419708b0Seric *--ap = "sh"; 455419708b0Seric rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag); 456419708b0Seric break; 457419708b0Seric 4587de81dc7Seric case PROG: /* call an sccs prog */ 459419708b0Seric rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); 4601777fbcbSeric break; 4611777fbcbSeric 4621777fbcbSeric case CMACRO: /* command macro */ 463ff038098Seric /* step through & execute each part of the macro */ 4641777fbcbSeric for (p = cmd->sccspath; *p != '\0'; p++) 4651777fbcbSeric { 466108d6082Seric q = p; 467108d6082Seric while (*p != '\0' && *p != '/') 468108d6082Seric p++; 4693642a8d2Seric rval = command(&ap[1], *p != '\0', q); 47041290c52Seric if (rval != 0) 47141290c52Seric break; 4721777fbcbSeric } 47341290c52Seric break; 4747de81dc7Seric 475172147faSeric case FIX: /* fix a delta */ 476419708b0Seric if (strncmp(ap[1], "-r", 2) != 0) 477172147faSeric { 478095915d3Seric usrerr("-r flag needed for fix command"); 47941290c52Seric rval = EX_USAGE; 480172147faSeric break; 481172147faSeric } 482ff038098Seric 483ff038098Seric /* get the version with all changes */ 4843642a8d2Seric rval = command(&ap[1], TRUE, "get -k"); 485ff038098Seric 486ff038098Seric /* now remove that version from the s-file */ 48741290c52Seric if (rval == 0) 4883642a8d2Seric rval = command(&ap[1], TRUE, "rmdel:r"); 489ff038098Seric 490ff038098Seric /* and edit the old version (but don't clobber new vers) */ 49141290c52Seric if (rval == 0) 4923642a8d2Seric rval = command(&ap[2], FALSE, "get -e -g"); 49341290c52Seric break; 494172147faSeric 495b2538d76Seric case CLEAN: 496ec4190ddSeric rval = clean((int) cmd->sccspath, ap); 497b2538d76Seric break; 498b2538d76Seric 499e39a5722Seric case UNEDIT: 500419708b0Seric for (argv = np = &ap[1]; *argv != NULL; argv++) 501d51cd7e5Seric { 502108d6082Seric if (unedit(*argv)) 503108d6082Seric *np++ = *argv; 504d51cd7e5Seric } 505108d6082Seric *np = NULL; 506ff038098Seric 507ff038098Seric /* get all the files that we unedited successfully */ 508fe7b004aSeric if (np > &ap[1]) 5093642a8d2Seric rval = command(&ap[1], FALSE, "get"); 510e39a5722Seric break; 511e39a5722Seric 51278d41c6aSeric case DIFFS: /* diff between s-file & edit file */ 51378d41c6aSeric /* find the end of the flag arguments */ 51478d41c6aSeric for (np = &ap[1]; *np != NULL && **np == '-'; np++) 51578d41c6aSeric continue; 51678d41c6aSeric argv = np; 51778d41c6aSeric 51878d41c6aSeric /* for each file, do the diff */ 51996370756Seric p = argv[1]; 52078d41c6aSeric while (*np != NULL) 52178d41c6aSeric { 522ff038098Seric /* messy, but we need a null terminated argv */ 52378d41c6aSeric *argv = *np++; 52496370756Seric argv[1] = NULL; 525287bbaa2Seric i = dodiff(ap, tail(*argv)); 52678d41c6aSeric if (rval == 0) 52778d41c6aSeric rval = i; 52896370756Seric argv[1] = p; 52978d41c6aSeric } 53078d41c6aSeric break; 53178d41c6aSeric 5322569d37aSeric case DODIFF: /* internal diff call */ 5332569d37aSeric setuid(getuid()); 5342569d37aSeric for (np = ap; *np != NULL; np++) 5352569d37aSeric { 5362569d37aSeric if ((*np)[0] == '-' && (*np)[1] == 'C') 5372569d37aSeric (*np)[1] = 'c'; 5382569d37aSeric } 5392569d37aSeric 5402569d37aSeric /* insert "-" argument */ 5412569d37aSeric np[1] = NULL; 5422569d37aSeric np[0] = np[-1]; 5432569d37aSeric np[-1] = "-"; 5442569d37aSeric 5452569d37aSeric /* execute the diff program of choice */ 5462569d37aSeric # ifndef V6 5472569d37aSeric execvp("diff", ap); 5482569d37aSeric # endif 5492569d37aSeric execv(cmd->sccspath, argv); 5502569d37aSeric syserr("cannot exec %s", cmd->sccspath); 5512569d37aSeric exit(EX_OSERR); 5522569d37aSeric 5537de81dc7Seric default: 554095915d3Seric syserr("oper %d", cmd->sccsoper); 5557de81dc7Seric exit(EX_SOFTWARE); 5567de81dc7Seric } 55741290c52Seric # ifdef DEBUG 55841290c52Seric if (Debug) 55941290c52Seric printf("command: rval=%d\n", rval); 56041290c52Seric # endif 56141290c52Seric return (rval); 5627de81dc7Seric } 5630faf63f9Seric 5640faf63f9Seric /* 565b1ed8a43Seric ** LOOKUP -- look up an SCCS command name. 566b1ed8a43Seric ** 567b1ed8a43Seric ** Parameters: 568b1ed8a43Seric ** name -- the name of the command to look up. 569b1ed8a43Seric ** 570b1ed8a43Seric ** Returns: 571b1ed8a43Seric ** ptr to command descriptor for this command. 572b1ed8a43Seric ** NULL if no such entry. 573b1ed8a43Seric ** 574b1ed8a43Seric ** Side Effects: 575b1ed8a43Seric ** none. 576b1ed8a43Seric */ 577b1ed8a43Seric 578b1ed8a43Seric struct sccsprog * 579b1ed8a43Seric lookup(name) 580b1ed8a43Seric char *name; 581b1ed8a43Seric { 582b1ed8a43Seric register struct sccsprog *cmd; 583b1ed8a43Seric 584b1ed8a43Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 585b1ed8a43Seric { 586b1ed8a43Seric if (strcmp(cmd->sccsname, name) == 0) 587b1ed8a43Seric return (cmd); 588b1ed8a43Seric } 589b1ed8a43Seric return (NULL); 590b1ed8a43Seric } 5910faf63f9Seric 5920faf63f9Seric /* 59341290c52Seric ** CALLPROG -- call a program 59441290c52Seric ** 595108d6082Seric ** Used to call the SCCS programs. 59641290c52Seric ** 59741290c52Seric ** Parameters: 59841290c52Seric ** progpath -- pathname of the program to call. 59941290c52Seric ** flags -- status flags from the command descriptors. 60041290c52Seric ** argv -- an argument vector to pass to the program. 60141290c52Seric ** forkflag -- if true, fork before calling, else just 60241290c52Seric ** exec. 60341290c52Seric ** 60441290c52Seric ** Returns: 60541290c52Seric ** The exit status of the program. 60641290c52Seric ** Nothing if forkflag == FALSE. 60741290c52Seric ** 60841290c52Seric ** Side Effects: 60941290c52Seric ** Can exit if forkflag == FALSE. 61041290c52Seric */ 611172147faSeric 6127de81dc7Seric callprog(progpath, flags, argv, forkflag) 6137de81dc7Seric char *progpath; 6147de81dc7Seric short flags; 6157de81dc7Seric char **argv; 6167de81dc7Seric bool forkflag; 6177de81dc7Seric { 6187de81dc7Seric register int i; 6191777fbcbSeric auto int st; 620108d6082Seric 621108d6082Seric # ifdef DEBUG 622108d6082Seric if (Debug) 623108d6082Seric { 624108d6082Seric printf("callprog:\n"); 625108d6082Seric for (i = 0; argv[i] != NULL; i++) 626108d6082Seric printf("\t\"%s\"\n", argv[i]); 627108d6082Seric } 628108d6082Seric # endif 6297de81dc7Seric 6307de81dc7Seric if (*argv == NULL) 6317de81dc7Seric return (-1); 632c4432be4Seric 633c4432be4Seric /* 634172147faSeric ** Fork if appropriate. 635adf8f7d4Seric */ 636adf8f7d4Seric 6377de81dc7Seric if (forkflag) 6387de81dc7Seric { 639f0cc3246Seric # ifdef DEBUG 640f0cc3246Seric if (Debug) 641f0cc3246Seric printf("Forking\n"); 642f0cc3246Seric # endif 6437de81dc7Seric i = fork(); 6447de81dc7Seric if (i < 0) 6457de81dc7Seric { 646095915d3Seric syserr("cannot fork"); 6477de81dc7Seric exit(EX_OSERR); 6487de81dc7Seric } 6497de81dc7Seric else if (i > 0) 6501777fbcbSeric { 6511777fbcbSeric wait(&st); 65241290c52Seric if ((st & 0377) == 0) 65341290c52Seric st = (st >> 8) & 0377; 65478d41c6aSeric if (OutFile >= 0) 65578d41c6aSeric { 65678d41c6aSeric close(OutFile); 65778d41c6aSeric OutFile = -1; 65878d41c6aSeric } 6591777fbcbSeric return (st); 6601777fbcbSeric } 6617de81dc7Seric } 66278d41c6aSeric else if (OutFile >= 0) 66378d41c6aSeric { 66478d41c6aSeric syserr("callprog: setting stdout w/o forking"); 66578d41c6aSeric exit(EX_SOFTWARE); 66678d41c6aSeric } 6677de81dc7Seric 66878d41c6aSeric /* set protection as appropriate */ 6697de81dc7Seric if (bitset(REALUSER, flags)) 6707de81dc7Seric setuid(getuid()); 6717de81dc7Seric 67278d41c6aSeric /* change standard input & output if needed */ 67378d41c6aSeric if (OutFile >= 0) 67478d41c6aSeric { 67578d41c6aSeric close(1); 67678d41c6aSeric dup(OutFile); 67778d41c6aSeric close(OutFile); 67878d41c6aSeric } 6797de81dc7Seric 68078d41c6aSeric /* call real SCCS program */ 681172147faSeric execv(progpath, argv); 682095915d3Seric syserr("cannot execute %s", progpath); 683adf8f7d4Seric exit(EX_UNAVAILABLE); 684fe7b004aSeric /*NOTREACHED*/ 685adf8f7d4Seric } 6860faf63f9Seric 6870faf63f9Seric /* 688cdc1aa65Seric ** MAKEFILE -- make filename of SCCS file 689cdc1aa65Seric ** 690cdc1aa65Seric ** If the name passed is already the name of an SCCS file, 691cdc1aa65Seric ** just return it. Otherwise, munge the name into the name 692cdc1aa65Seric ** of the actual SCCS file. 693cdc1aa65Seric ** 694cdc1aa65Seric ** There are cases when it is not clear what you want to 695cdc1aa65Seric ** do. For example, if SccsPath is an absolute pathname 696cdc1aa65Seric ** and the name given is also an absolute pathname, we go 697cdc1aa65Seric ** for SccsPath (& only use the last component of the name 698cdc1aa65Seric ** passed) -- this is important for security reasons (if 699cdc1aa65Seric ** sccs is being used as a setuid front end), but not 700cdc1aa65Seric ** particularly intuitive. 701cdc1aa65Seric ** 702cdc1aa65Seric ** Parameters: 703cdc1aa65Seric ** name -- the file name to be munged. 704cdc1aa65Seric ** 705cdc1aa65Seric ** Returns: 706cdc1aa65Seric ** The pathname of the sccs file. 707cdc1aa65Seric ** NULL on error. 708cdc1aa65Seric ** 709cdc1aa65Seric ** Side Effects: 710cdc1aa65Seric ** none. 711cdc1aa65Seric */ 712adf8f7d4Seric 713adf8f7d4Seric char * 714adf8f7d4Seric makefile(name) 715adf8f7d4Seric char *name; 716adf8f7d4Seric { 717adf8f7d4Seric register char *p; 718adf8f7d4Seric char buf[512]; 719adf8f7d4Seric extern char *malloc(); 720cdc1aa65Seric extern char *rindex(); 7210a8b9ab0Seric extern bool safepath(); 7227d70f5d8Seric extern bool isdir(); 7237d70f5d8Seric register char *q; 724cdc1aa65Seric 725cdc1aa65Seric p = rindex(name, '/'); 726cdc1aa65Seric if (p == NULL) 727cdc1aa65Seric p = name; 728cdc1aa65Seric else 729cdc1aa65Seric p++; 730adf8f7d4Seric 731adf8f7d4Seric /* 7320a8b9ab0Seric ** Check to see that the path is "safe", i.e., that we 7330a8b9ab0Seric ** are not letting some nasty person use the setuid part 7340a8b9ab0Seric ** of this program to look at or munge some presumably 7350a8b9ab0Seric ** hidden files. 736adf8f7d4Seric */ 737adf8f7d4Seric 7380a8b9ab0Seric if (SccsDir[0] == '/' && !safepath(name)) 7390a8b9ab0Seric return (NULL); 740cdc1aa65Seric 741cdc1aa65Seric /* 7420a8b9ab0Seric ** Create the base pathname. 743cdc1aa65Seric */ 744cdc1aa65Seric 745ff038098Seric /* first the directory part */ 7460a8b9ab0Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) 747adf8f7d4Seric { 7480a8b9ab0Seric strcpy(buf, SccsDir); 749cdc1aa65Seric strcat(buf, "/"); 750cdc1aa65Seric } 751cdc1aa65Seric else 752cdc1aa65Seric strcpy(buf, ""); 753ff038098Seric 754ff038098Seric /* then the head of the pathname */ 755cdc1aa65Seric strncat(buf, name, p - name); 7567d70f5d8Seric q = &buf[strlen(buf)]; 757ff038098Seric 758ff038098Seric /* now copy the final part of the name, in case useful */ 7597d70f5d8Seric strcpy(q, p); 760ff038098Seric 761ff038098Seric /* so is it useful? */ 7627d70f5d8Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 7637d70f5d8Seric { 764ff038098Seric /* sorry, no; copy the SCCS pathname & the "s." */ 7650a8b9ab0Seric strcpy(q, SccsPath); 7660a8b9ab0Seric strcat(buf, "/s."); 767ff038098Seric 768ff038098Seric /* and now the end of the name */ 769cdc1aa65Seric strcat(buf, p); 770cdc1aa65Seric } 771adf8f7d4Seric 772ff038098Seric /* if i haven't changed it, why did I do all this? */ 7730a8b9ab0Seric if (strcmp(buf, name) == 0) 7740a8b9ab0Seric p = name; 7750a8b9ab0Seric else 7760a8b9ab0Seric { 777ff038098Seric /* but if I have, squirrel it away */ 778adf8f7d4Seric p = malloc(strlen(buf) + 1); 779adf8f7d4Seric if (p == NULL) 780adf8f7d4Seric { 781adf8f7d4Seric perror("Sccs: no mem"); 782adf8f7d4Seric exit(EX_OSERR); 783adf8f7d4Seric } 784adf8f7d4Seric strcpy(p, buf); 7850a8b9ab0Seric } 786ff038098Seric 787adf8f7d4Seric return (p); 788adf8f7d4Seric } 7890faf63f9Seric 7900faf63f9Seric /* 7917d70f5d8Seric ** ISDIR -- return true if the argument is a directory. 7927d70f5d8Seric ** 7937d70f5d8Seric ** Parameters: 7947d70f5d8Seric ** name -- the pathname of the file to check. 7957d70f5d8Seric ** 7967d70f5d8Seric ** Returns: 7977d70f5d8Seric ** TRUE if 'name' is a directory, FALSE otherwise. 7987d70f5d8Seric ** 7997d70f5d8Seric ** Side Effects: 8007d70f5d8Seric ** none. 8017d70f5d8Seric */ 8027d70f5d8Seric 8037d70f5d8Seric bool 8047d70f5d8Seric isdir(name) 8057d70f5d8Seric char *name; 8067d70f5d8Seric { 8077d70f5d8Seric struct stat stbuf; 8087d70f5d8Seric 8097d70f5d8Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 8107d70f5d8Seric } 8110faf63f9Seric 8120faf63f9Seric /* 813cdc1aa65Seric ** SAFEPATH -- determine whether a pathname is "safe" 814cdc1aa65Seric ** 815cdc1aa65Seric ** "Safe" pathnames only allow you to get deeper into the 816cdc1aa65Seric ** directory structure, i.e., full pathnames and ".." are 817cdc1aa65Seric ** not allowed. 818cdc1aa65Seric ** 819cdc1aa65Seric ** Parameters: 820cdc1aa65Seric ** p -- the name to check. 821cdc1aa65Seric ** 822cdc1aa65Seric ** Returns: 823cdc1aa65Seric ** TRUE -- if the path is safe. 824cdc1aa65Seric ** FALSE -- if the path is not safe. 825cdc1aa65Seric ** 826cdc1aa65Seric ** Side Effects: 827cdc1aa65Seric ** Prints a message if the path is not safe. 828cdc1aa65Seric */ 829cdc1aa65Seric 830cdc1aa65Seric bool 831cdc1aa65Seric safepath(p) 832cdc1aa65Seric register char *p; 833cdc1aa65Seric { 834cdc1aa65Seric extern char *index(); 835cdc1aa65Seric 836cdc1aa65Seric if (*p != '/') 837cdc1aa65Seric { 838cdc1aa65Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 839cdc1aa65Seric { 840cdc1aa65Seric p = index(p, '/'); 841cdc1aa65Seric if (p == NULL) 842cdc1aa65Seric return (TRUE); 843cdc1aa65Seric p++; 844cdc1aa65Seric } 845cdc1aa65Seric } 846cdc1aa65Seric 847cdc1aa65Seric printf("You may not use full pathnames or \"..\"\n"); 848cdc1aa65Seric return (FALSE); 849cdc1aa65Seric } 8500faf63f9Seric 8510faf63f9Seric /* 852b2538d76Seric ** CLEAN -- clean out recreatable files 853b2538d76Seric ** 854b2538d76Seric ** Any file for which an "s." file exists but no "p." file 855b2538d76Seric ** exists in the current directory is purged. 856b2538d76Seric ** 857b2538d76Seric ** Parameters: 858ec4190ddSeric ** mode -- tells whether this came from a "clean", "info", or 85948ff0e7eSeric ** "check" command. 860ec4190ddSeric ** argv -- the rest of the argument vector. 861b2538d76Seric ** 862b2538d76Seric ** Returns: 863b2538d76Seric ** none. 864b2538d76Seric ** 865b2538d76Seric ** Side Effects: 86648ff0e7eSeric ** Removes files in the current directory. 86748ff0e7eSeric ** Prints information regarding files being edited. 86848ff0e7eSeric ** Exits if a "check" command. 869b2538d76Seric */ 870b2538d76Seric 871ec4190ddSeric clean(mode, argv) 87248ff0e7eSeric int mode; 873ec4190ddSeric char **argv; 874b2538d76Seric { 875b2538d76Seric struct direct dir; 876b2538d76Seric char buf[100]; 877393e27f2Seric char *bufend; 8783a208c77Seric register FILE *dirfd; 8793a208c77Seric register char *basefile; 8802caac8fdSeric bool gotedit; 881ec4190ddSeric bool gotpfent; 882bf9c1e66Seric FILE *pfp; 883ec4190ddSeric bool nobranch = FALSE; 884ec4190ddSeric extern struct pfile *getpfent(); 885ec4190ddSeric register struct pfile *pf; 886ec4190ddSeric register char **ap; 8871e0924d0Seric extern char *username(); 8881e0924d0Seric char *usernm = NULL; 889393e27f2Seric char *subdir = NULL; 890393e27f2Seric char *cmdname; 891ec4190ddSeric 892ec4190ddSeric /* 893ec4190ddSeric ** Process the argv 894ec4190ddSeric */ 895ec4190ddSeric 896393e27f2Seric cmdname = *argv; 897393e27f2Seric for (ap = argv; *++ap != NULL; ) 898ec4190ddSeric { 8991e0924d0Seric if (**ap == '-') 9001e0924d0Seric { 9011e0924d0Seric /* we have a flag */ 9021e0924d0Seric switch ((*ap)[1]) 9031e0924d0Seric { 9041e0924d0Seric case 'b': 905ec4190ddSeric nobranch = TRUE; 9061e0924d0Seric break; 9071e0924d0Seric 9081e0924d0Seric case 'u': 9091e0924d0Seric if ((*ap)[2] != '\0') 9101e0924d0Seric usernm = &(*ap)[2]; 9111e0924d0Seric else if (ap[1] != NULL && ap[1][0] != '-') 9121e0924d0Seric usernm = *++ap; 9131e0924d0Seric else 9141e0924d0Seric usernm = username(); 9151e0924d0Seric break; 9161e0924d0Seric } 9171e0924d0Seric } 918393e27f2Seric else 919393e27f2Seric { 920393e27f2Seric if (subdir != NULL) 921393e27f2Seric usrerr("too many args"); 922393e27f2Seric else 923393e27f2Seric subdir = *ap; 924393e27f2Seric } 925ec4190ddSeric } 926b2538d76Seric 927ff038098Seric /* 928ff038098Seric ** Find and open the SCCS directory. 929ff038098Seric */ 930ff038098Seric 931f77a08fbSeric strcpy(buf, SccsDir); 932f77a08fbSeric if (buf[0] != '\0') 933f77a08fbSeric strcat(buf, "/"); 934393e27f2Seric if (subdir != NULL) 935393e27f2Seric { 936393e27f2Seric strcat(buf, subdir); 937393e27f2Seric strcat(buf, "/"); 938393e27f2Seric } 939f77a08fbSeric strcat(buf, SccsPath); 940393e27f2Seric bufend = &buf[strlen(buf)]; 941ff038098Seric 942f77a08fbSeric dirfd = fopen(buf, "r"); 943b2538d76Seric if (dirfd == NULL) 944b2538d76Seric { 945f77a08fbSeric usrerr("cannot open %s", buf); 94641290c52Seric return (EX_NOINPUT); 947b2538d76Seric } 948b2538d76Seric 949b2538d76Seric /* 950b2538d76Seric ** Scan the SCCS directory looking for s. files. 951ff038098Seric ** gotedit tells whether we have tried to clean any 952ff038098Seric ** files that are being edited. 953b2538d76Seric */ 954b2538d76Seric 9552caac8fdSeric gotedit = FALSE; 956fe7b004aSeric while (fread((char *)&dir, sizeof dir, 1, dirfd) != NULL) 957b2538d76Seric { 958c9c62fd0Seric if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) 959b2538d76Seric continue; 960b2538d76Seric 961b2538d76Seric /* got an s. file -- see if the p. file exists */ 962393e27f2Seric strcpy(bufend, "/p."); 963393e27f2Seric basefile = bufend + 3; 964c9c62fd0Seric strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 965bf9c1e66Seric basefile[sizeof dir.d_name - 2] = '\0'; 966ec4190ddSeric 967ec4190ddSeric /* 968ec4190ddSeric ** open and scan the p-file. 969ec4190ddSeric ** 'gotpfent' tells if we have found a valid p-file 970ec4190ddSeric ** entry. 971ec4190ddSeric */ 972ec4190ddSeric 973bf9c1e66Seric pfp = fopen(buf, "r"); 974ec4190ddSeric gotpfent = FALSE; 975bf9c1e66Seric if (pfp != NULL) 9763a208c77Seric { 977ff038098Seric /* the file exists -- report it's contents */ 978ec4190ddSeric while ((pf = getpfent(pfp)) != NULL) 9792734e237Seric { 980ec4190ddSeric if (nobranch && isbranch(pf->p_nsid)) 981ec4190ddSeric continue; 9821e0924d0Seric if (usernm != NULL && strcmp(usernm, pf->p_user) != 0 && mode != CLEANC) 9831e0924d0Seric continue; 984ec4190ddSeric gotedit = TRUE; 985ec4190ddSeric gotpfent = TRUE; 986ec4190ddSeric if (mode == TELLC) 987ec4190ddSeric { 988ec4190ddSeric printf("%s\n", basefile); 989ec4190ddSeric break; 990ec4190ddSeric } 99177a8a0dfSeric printf("%12s: being edited: ", basefile); 99277a8a0dfSeric putpfent(pf, stdout); 9932734e237Seric } 994bf9c1e66Seric fclose(pfp); 9953a208c77Seric } 996b2538d76Seric 997b2538d76Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 9989d7ed7d6Seric if (mode == CLEANC && !gotpfent) 9993a208c77Seric { 1000c9c62fd0Seric strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); 10013a208c77Seric buf[sizeof dir.d_name - 2] = '\0'; 1002b2538d76Seric unlink(buf); 1003b2538d76Seric } 10043a208c77Seric } 1005b2538d76Seric 1006ff038098Seric /* cleanup & report results */ 1007b2538d76Seric fclose(dirfd); 100848ff0e7eSeric if (!gotedit && mode == INFOC) 10091e0924d0Seric { 10101e0924d0Seric printf("Nothing being edited"); 10111e0924d0Seric if (nobranch) 10121e0924d0Seric printf(" (on trunk)"); 10131e0924d0Seric if (usernm == NULL) 10141e0924d0Seric printf("\n"); 10151e0924d0Seric else 10161e0924d0Seric printf(" by %s\n", usernm); 10171e0924d0Seric } 101848ff0e7eSeric if (mode == CHECKC) 101948ff0e7eSeric exit(gotedit); 102041290c52Seric return (EX_OK); 1021b2538d76Seric } 10220faf63f9Seric 10230faf63f9Seric /* 1024ec4190ddSeric ** ISBRANCH -- is the SID a branch? 1025ec4190ddSeric ** 1026ec4190ddSeric ** Parameters: 1027ec4190ddSeric ** sid -- the sid to check. 1028ec4190ddSeric ** 1029ec4190ddSeric ** Returns: 1030ec4190ddSeric ** TRUE if the sid represents a branch. 1031ec4190ddSeric ** FALSE otherwise. 1032ec4190ddSeric ** 1033ec4190ddSeric ** Side Effects: 1034ec4190ddSeric ** none. 1035ec4190ddSeric */ 1036ec4190ddSeric 1037ec4190ddSeric isbranch(sid) 1038ec4190ddSeric char *sid; 1039ec4190ddSeric { 1040ec4190ddSeric register char *p; 1041ec4190ddSeric int dots; 1042ec4190ddSeric 1043ec4190ddSeric dots = 0; 1044ec4190ddSeric for (p = sid; *p != '\0'; p++) 1045ec4190ddSeric { 1046ec4190ddSeric if (*p == '.') 1047ec4190ddSeric dots++; 1048ec4190ddSeric if (dots > 1) 1049ec4190ddSeric return (TRUE); 1050ec4190ddSeric } 1051ec4190ddSeric return (FALSE); 1052ec4190ddSeric } 1053ec4190ddSeric 1054ec4190ddSeric /* 1055e39a5722Seric ** UNEDIT -- unedit a file 1056e39a5722Seric ** 1057e39a5722Seric ** Checks to see that the current user is actually editting 1058e39a5722Seric ** the file and arranges that s/he is not editting it. 1059e39a5722Seric ** 1060e39a5722Seric ** Parameters: 10612444cd3eSeric ** fn -- the name of the file to be unedited. 1062e39a5722Seric ** 1063e39a5722Seric ** Returns: 1064d51cd7e5Seric ** TRUE -- if the file was successfully unedited. 1065d51cd7e5Seric ** FALSE -- if the file was not unedited for some 1066d51cd7e5Seric ** reason. 1067e39a5722Seric ** 1068e39a5722Seric ** Side Effects: 1069e39a5722Seric ** fn is removed 1070e39a5722Seric ** entries are removed from pfile. 1071e39a5722Seric */ 1072e39a5722Seric 1073d51cd7e5Seric bool 1074e39a5722Seric unedit(fn) 1075e39a5722Seric char *fn; 1076e39a5722Seric { 1077e39a5722Seric register FILE *pfp; 1078e39a5722Seric char *pfn; 1079e39a5722Seric static char tfn[] = "/tmp/sccsXXXXX"; 1080e39a5722Seric FILE *tfp; 1081e39a5722Seric register char *q; 1082e39a5722Seric bool delete = FALSE; 1083e39a5722Seric bool others = FALSE; 1084e39a5722Seric char *myname; 10851e0924d0Seric extern char *username(); 1086e39a5722Seric struct pfile *pent; 1087ec4190ddSeric extern struct pfile *getpfent(); 1088e39a5722Seric char buf[120]; 1089108d6082Seric extern char *makefile(); 1090e39a5722Seric 1091e39a5722Seric /* make "s." filename & find the trailing component */ 1092e39a5722Seric pfn = makefile(fn); 1093cdc1aa65Seric if (pfn == NULL) 1094cdc1aa65Seric return (FALSE); 1095cdc1aa65Seric q = rindex(pfn, '/'); 1096cdc1aa65Seric if (q == NULL) 1097cdc1aa65Seric q = &pfn[-1]; 1098cdc1aa65Seric if (q[1] != 's' || q[2] != '.') 1099e39a5722Seric { 1100095915d3Seric usrerr("bad file name \"%s\"", fn); 1101d51cd7e5Seric return (FALSE); 1102e39a5722Seric } 1103e39a5722Seric 1104ff038098Seric /* turn "s." into "p." & try to open it */ 1105e39a5722Seric *++q = 'p'; 1106e39a5722Seric 1107e39a5722Seric pfp = fopen(pfn, "r"); 1108e39a5722Seric if (pfp == NULL) 1109e39a5722Seric { 11102444cd3eSeric printf("%12s: not being edited\n", fn); 1111d51cd7e5Seric return (FALSE); 1112e39a5722Seric } 1113e39a5722Seric 1114ff038098Seric /* create temp file for editing p-file */ 1115e39a5722Seric mktemp(tfn); 1116e39a5722Seric tfp = fopen(tfn, "w"); 1117e39a5722Seric if (tfp == NULL) 1118e39a5722Seric { 1119095915d3Seric usrerr("cannot create \"%s\"", tfn); 1120e39a5722Seric exit(EX_OSERR); 1121e39a5722Seric } 1122e39a5722Seric 1123ff038098Seric /* figure out who I am */ 11241e0924d0Seric myname = username(); 1125ff038098Seric 1126ff038098Seric /* 1127ff038098Seric ** Copy p-file to temp file, doing deletions as needed. 1128ff038098Seric */ 1129ff038098Seric 1130ec4190ddSeric while ((pent = getpfent(pfp)) != NULL) 1131e39a5722Seric { 1132e39a5722Seric if (strcmp(pent->p_user, myname) == 0) 1133e39a5722Seric { 1134e39a5722Seric /* a match */ 1135e39a5722Seric delete++; 1136e39a5722Seric } 1137e39a5722Seric else 1138e39a5722Seric { 1139ff038098Seric /* output it again */ 114077a8a0dfSeric putpfent(pent, tfp); 1141e39a5722Seric others++; 1142e39a5722Seric } 1143e39a5722Seric } 1144e39a5722Seric 1145e39a5722Seric /* do final cleanup */ 1146e39a5722Seric if (others) 1147e39a5722Seric { 1148ff038098Seric /* copy it back (perhaps it should be linked?) */ 1149e39a5722Seric if (freopen(tfn, "r", tfp) == NULL) 1150e39a5722Seric { 1151095915d3Seric syserr("cannot reopen \"%s\"", tfn); 1152e39a5722Seric exit(EX_OSERR); 1153e39a5722Seric } 1154e39a5722Seric if (freopen(pfn, "w", pfp) == NULL) 1155e39a5722Seric { 1156095915d3Seric usrerr("cannot create \"%s\"", pfn); 1157d51cd7e5Seric return (FALSE); 1158e39a5722Seric } 1159e39a5722Seric while (fgets(buf, sizeof buf, tfp) != NULL) 1160e39a5722Seric fputs(buf, pfp); 1161e39a5722Seric } 1162e39a5722Seric else 1163e39a5722Seric { 1164ff038098Seric /* it's empty -- remove it */ 1165e39a5722Seric unlink(pfn); 1166e39a5722Seric } 1167e39a5722Seric fclose(tfp); 1168e39a5722Seric fclose(pfp); 1169e39a5722Seric unlink(tfn); 1170e39a5722Seric 1171ff038098Seric /* actually remove the g-file */ 1172e39a5722Seric if (delete) 1173e39a5722Seric { 1174287bbaa2Seric unlink(tail(fn)); 1175287bbaa2Seric printf("%12s: removed\n", tail(fn)); 1176d51cd7e5Seric return (TRUE); 1177e39a5722Seric } 1178e39a5722Seric else 1179e39a5722Seric { 11802444cd3eSeric printf("%12s: not being edited by you\n", fn); 1181d51cd7e5Seric return (FALSE); 1182e39a5722Seric } 1183e39a5722Seric } 11840faf63f9Seric 11850faf63f9Seric /* 118678d41c6aSeric ** DODIFF -- diff an s-file against a g-file 118778d41c6aSeric ** 118878d41c6aSeric ** Parameters: 118978d41c6aSeric ** getv -- argv for the 'get' command. 119078d41c6aSeric ** gfile -- name of the g-file to diff against. 119178d41c6aSeric ** 119278d41c6aSeric ** Returns: 119378d41c6aSeric ** Result of get. 119478d41c6aSeric ** 119578d41c6aSeric ** Side Effects: 119678d41c6aSeric ** none. 119778d41c6aSeric */ 119878d41c6aSeric 119978d41c6aSeric dodiff(getv, gfile) 120078d41c6aSeric char **getv; 120178d41c6aSeric char *gfile; 120278d41c6aSeric { 120378d41c6aSeric int pipev[2]; 120478d41c6aSeric int rval; 120578d41c6aSeric register int i; 120678d41c6aSeric register int pid; 120778d41c6aSeric auto int st; 120878d41c6aSeric extern int errno; 120978d41c6aSeric int (*osig)(); 121078d41c6aSeric 1211cc4e4f0dSeric printf("\n------- %s -------\n", gfile); 12122569d37aSeric fflush(stdout); 1213cc4e4f0dSeric 1214ff038098Seric /* create context for diff to run in */ 121578d41c6aSeric if (pipe(pipev) < 0) 121678d41c6aSeric { 121778d41c6aSeric syserr("dodiff: pipe failed"); 121878d41c6aSeric exit(EX_OSERR); 121978d41c6aSeric } 122078d41c6aSeric if ((pid = fork()) < 0) 122178d41c6aSeric { 122278d41c6aSeric syserr("dodiff: fork failed"); 122378d41c6aSeric exit(EX_OSERR); 122478d41c6aSeric } 122578d41c6aSeric else if (pid > 0) 122678d41c6aSeric { 122778d41c6aSeric /* in parent; run get */ 122878d41c6aSeric OutFile = pipev[1]; 122978d41c6aSeric close(pipev[0]); 12302569d37aSeric rval = command(&getv[1], TRUE, "get:rcixt -s -k -p"); 123178d41c6aSeric osig = signal(SIGINT, SIG_IGN); 123278d41c6aSeric while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR) 123378d41c6aSeric errno = 0; 123478d41c6aSeric signal(SIGINT, osig); 123578d41c6aSeric /* ignore result of diff */ 123678d41c6aSeric } 123778d41c6aSeric else 123878d41c6aSeric { 123978d41c6aSeric /* in child, run diff */ 124078d41c6aSeric if (close(pipev[1]) < 0 || close(0) < 0 || 124178d41c6aSeric dup(pipev[0]) != 0 || close(pipev[0]) < 0) 124278d41c6aSeric { 124378d41c6aSeric syserr("dodiff: magic failed"); 124478d41c6aSeric exit(EX_OSERR); 124578d41c6aSeric } 12462569d37aSeric command(&getv[1], FALSE, "-diff:elsfhbC"); 124778d41c6aSeric } 124878d41c6aSeric return (rval); 124978d41c6aSeric } 125078d41c6aSeric 125178d41c6aSeric /* 1252287bbaa2Seric ** TAIL -- return tail of filename. 1253287bbaa2Seric ** 1254287bbaa2Seric ** Parameters: 1255287bbaa2Seric ** fn -- the filename. 1256287bbaa2Seric ** 1257287bbaa2Seric ** Returns: 1258287bbaa2Seric ** a pointer to the tail of the filename; e.g., given 1259287bbaa2Seric ** "cmd/ls.c", "ls.c" is returned. 1260287bbaa2Seric ** 1261287bbaa2Seric ** Side Effects: 1262287bbaa2Seric ** none. 1263287bbaa2Seric */ 1264287bbaa2Seric 1265287bbaa2Seric char * 1266287bbaa2Seric tail(fn) 1267287bbaa2Seric register char *fn; 1268287bbaa2Seric { 1269287bbaa2Seric register char *p; 1270287bbaa2Seric 1271287bbaa2Seric for (p = fn; *p != 0; p++) 1272287bbaa2Seric if (*p == '/' && p[1] != '\0' && p[1] != '/') 1273287bbaa2Seric fn = &p[1]; 1274287bbaa2Seric return (fn); 1275287bbaa2Seric } 1276287bbaa2Seric 1277287bbaa2Seric /* 1278ec4190ddSeric ** GETPFENT -- get an entry from the p-file 1279e39a5722Seric ** 1280e39a5722Seric ** Parameters: 1281e39a5722Seric ** pfp -- p-file file pointer 1282e39a5722Seric ** 1283e39a5722Seric ** Returns: 1284e39a5722Seric ** pointer to p-file struct for next entry 1285e39a5722Seric ** NULL on EOF or error 1286e39a5722Seric ** 1287e39a5722Seric ** Side Effects: 1288e39a5722Seric ** Each call wipes out results of previous call. 1289e39a5722Seric */ 1290e39a5722Seric 1291e39a5722Seric struct pfile * 1292ec4190ddSeric getpfent(pfp) 1293e39a5722Seric FILE *pfp; 1294e39a5722Seric { 1295e39a5722Seric static struct pfile ent; 1296e39a5722Seric static char buf[120]; 1297e39a5722Seric register char *p; 1298e39a5722Seric extern char *nextfield(); 1299e39a5722Seric 1300e39a5722Seric if (fgets(buf, sizeof buf, pfp) == NULL) 1301e39a5722Seric return (NULL); 1302e39a5722Seric 1303e39a5722Seric ent.p_osid = p = buf; 1304e39a5722Seric ent.p_nsid = p = nextfield(p); 1305e39a5722Seric ent.p_user = p = nextfield(p); 1306e39a5722Seric ent.p_date = p = nextfield(p); 1307e39a5722Seric ent.p_time = p = nextfield(p); 130877a8a0dfSeric ent.p_aux = p = nextfield(p); 1309e39a5722Seric 1310e39a5722Seric return (&ent); 1311e39a5722Seric } 1312e39a5722Seric 1313e39a5722Seric 1314e39a5722Seric char * 1315e39a5722Seric nextfield(p) 1316e39a5722Seric register char *p; 1317e39a5722Seric { 1318e39a5722Seric if (p == NULL || *p == '\0') 1319e39a5722Seric return (NULL); 1320e39a5722Seric while (*p != ' ' && *p != '\n' && *p != '\0') 1321e39a5722Seric p++; 1322e39a5722Seric if (*p == '\n' || *p == '\0') 1323e39a5722Seric { 1324e39a5722Seric *p = '\0'; 1325e39a5722Seric return (NULL); 1326e39a5722Seric } 1327e39a5722Seric *p++ = '\0'; 1328e39a5722Seric return (p); 1329e39a5722Seric } 133077a8a0dfSeric /* 133177a8a0dfSeric ** PUTPFENT -- output a p-file entry to a file 133277a8a0dfSeric ** 133377a8a0dfSeric ** Parameters: 133477a8a0dfSeric ** pf -- the p-file entry 133577a8a0dfSeric ** f -- the file to put it on. 133677a8a0dfSeric ** 133777a8a0dfSeric ** Returns: 133877a8a0dfSeric ** none. 133977a8a0dfSeric ** 134077a8a0dfSeric ** Side Effects: 134177a8a0dfSeric ** pf is written onto file f. 134277a8a0dfSeric */ 134377a8a0dfSeric 134477a8a0dfSeric putpfent(pf, f) 134577a8a0dfSeric register struct pfile *pf; 134677a8a0dfSeric register FILE *f; 134777a8a0dfSeric { 134877a8a0dfSeric fprintf(f, "%s %s %s %s %s", pf->p_osid, pf->p_nsid, 134977a8a0dfSeric pf->p_user, pf->p_date, pf->p_time); 135077a8a0dfSeric if (pf->p_aux != NULL) 135177a8a0dfSeric fprintf(f, " %s", pf->p_aux); 135277a8a0dfSeric else 135377a8a0dfSeric fprintf(f, "\n"); 135477a8a0dfSeric } 13550faf63f9Seric 13560faf63f9Seric /* 1357095915d3Seric ** USRERR -- issue user-level error 1358095915d3Seric ** 1359095915d3Seric ** Parameters: 1360095915d3Seric ** f -- format string. 1361095915d3Seric ** p1-p3 -- parameters to a printf. 1362095915d3Seric ** 1363095915d3Seric ** Returns: 1364095915d3Seric ** -1 1365095915d3Seric ** 1366095915d3Seric ** Side Effects: 1367095915d3Seric ** none. 1368095915d3Seric */ 1369095915d3Seric 1370fe7b004aSeric /*VARARGS1*/ 1371095915d3Seric usrerr(f, p1, p2, p3) 1372095915d3Seric char *f; 1373095915d3Seric { 1374095915d3Seric fprintf(stderr, "\n%s: ", MyName); 1375095915d3Seric fprintf(stderr, f, p1, p2, p3); 1376095915d3Seric fprintf(stderr, "\n"); 1377095915d3Seric 1378095915d3Seric return (-1); 1379095915d3Seric } 13800faf63f9Seric 13810faf63f9Seric /* 1382095915d3Seric ** SYSERR -- print system-generated error. 1383095915d3Seric ** 1384095915d3Seric ** Parameters: 1385095915d3Seric ** f -- format string to a printf. 1386095915d3Seric ** p1, p2, p3 -- parameters to f. 1387095915d3Seric ** 1388095915d3Seric ** Returns: 1389095915d3Seric ** never. 1390095915d3Seric ** 1391095915d3Seric ** Side Effects: 1392095915d3Seric ** none. 1393095915d3Seric */ 1394095915d3Seric 1395fe7b004aSeric /*VARARGS1*/ 1396095915d3Seric syserr(f, p1, p2, p3) 1397095915d3Seric char *f; 1398095915d3Seric { 1399095915d3Seric extern int errno; 1400095915d3Seric 1401095915d3Seric fprintf(stderr, "\n%s SYSERR: ", MyName); 1402095915d3Seric fprintf(stderr, f, p1, p2, p3); 1403095915d3Seric fprintf(stderr, "\n"); 1404095915d3Seric if (errno == 0) 1405095915d3Seric exit(EX_SOFTWARE); 1406095915d3Seric else 1407095915d3Seric { 1408fe7b004aSeric perror(NULL); 1409095915d3Seric exit(EX_OSERR); 1410095915d3Seric } 1411095915d3Seric } 14121e0924d0Seric /* 14131e0924d0Seric ** USERNAME -- return name of the current user 14141e0924d0Seric ** 14151e0924d0Seric ** Parameters: 14161e0924d0Seric ** none 14171e0924d0Seric ** 14181e0924d0Seric ** Returns: 14191e0924d0Seric ** name of current user 14201e0924d0Seric ** 14211e0924d0Seric ** Side Effects: 14221e0924d0Seric ** none 14231e0924d0Seric */ 14241e0924d0Seric 14251e0924d0Seric char * 14261e0924d0Seric username() 14271e0924d0Seric { 14281e0924d0Seric # ifdef UIDUSER 14291e0924d0Seric extern struct passwd *getpwuid(); 14301e0924d0Seric register struct passwd *pw; 14311e0924d0Seric 14321e0924d0Seric pw = getpwuid(getuid()); 14331e0924d0Seric if (pw == NULL) 14341e0924d0Seric { 14351e0924d0Seric syserr("who are you? (uid=%d)", getuid()); 14361e0924d0Seric exit(EX_OSERR); 14371e0924d0Seric } 14381e0924d0Seric return (pw->pw_name); 14391e0924d0Seric # else 1440ed320446Seric extern char *getlogin(); 1441ed320446Seric 14421e0924d0Seric return (getlogin()); 14431e0924d0Seric # endif UIDUSER 14441e0924d0Seric } 1445