1adf8f7d4Seric # include <stdio.h> 2adf8f7d4Seric # include <sys/types.h> 3adf8f7d4Seric # include <sys/stat.h> 4b2538d76Seric # include <sys/dir.h> 5adf8f7d4Seric # include <sysexits.h> 6b5d4f080Seric # include <whoami.h> 7adf8f7d4Seric 810cd16efSeric /* 910cd16efSeric ** SCCS.C -- human-oriented front end to the SCCS system. 1010cd16efSeric ** 1110cd16efSeric ** Without trying to add any functionality to speak of, this 1210cd16efSeric ** program tries to make SCCS a little more accessible to human 1310cd16efSeric ** types. The main thing it does is automatically put the 1410cd16efSeric ** string "SCCS/s." on the front of names. Also, it has a 1510cd16efSeric ** couple of things that are designed to shorten frequent 1610cd16efSeric ** combinations, e.g., "delget" which expands to a "delta" 1710cd16efSeric ** and a "get". 1810cd16efSeric ** 1910cd16efSeric ** This program can also function as a setuid front end. 2010cd16efSeric ** To do this, you should copy the source, renaming it to 2110cd16efSeric ** whatever you want, e.g., "syssccs". Change any defaults 2210cd16efSeric ** in the program (e.g., syssccs might default -d to 2310cd16efSeric ** "/usr/src/sys"). Then recompile and put the result 2410cd16efSeric ** as setuid to whomever you want. In this mode, sccs 2510cd16efSeric ** knows to not run setuid for certain programs in order 2610cd16efSeric ** to preserve security, and so forth. 2710cd16efSeric ** 2810cd16efSeric ** Usage: 2910cd16efSeric ** sccs [flags] command [args] 3010cd16efSeric ** 3110cd16efSeric ** Flags: 3210cd16efSeric ** -d<dir> <dir> represents a directory to search 3310cd16efSeric ** out of. It should be a full pathname 3410cd16efSeric ** for general usage. E.g., if <dir> is 3510cd16efSeric ** "/usr/src/sys", then a reference to the 3610cd16efSeric ** file "dev/bio.c" becomes a reference to 3710cd16efSeric ** "/usr/src/sys/dev/bio.c". 3810cd16efSeric ** -p<path> prepends <path> to the final component 3910cd16efSeric ** of the pathname. By default, this is 4010cd16efSeric ** "SCCS". For example, in the -d example 4110cd16efSeric ** above, the path then gets modified to 4210cd16efSeric ** "/usr/src/sys/dev/SCCS/s.bio.c". In 4310cd16efSeric ** more common usage (without the -d flag), 4410cd16efSeric ** "prog.c" would get modified to 4510cd16efSeric ** "SCCS/s.prog.c". In both cases, the 4610cd16efSeric ** "s." gets automatically prepended. 4710cd16efSeric ** -r run as the real user. 4810cd16efSeric ** 4910cd16efSeric ** Commands: 5010cd16efSeric ** admin, 5110cd16efSeric ** get, 5210cd16efSeric ** delta, 5310cd16efSeric ** rmdel, 5410cd16efSeric ** chghist, 5510cd16efSeric ** etc. Straight out of SCCS; only difference 5610cd16efSeric ** is that pathnames get modified as 5710cd16efSeric ** described above. 5810cd16efSeric ** edit Macro for "get -e". 5910cd16efSeric ** unedit Removes a file being edited, knowing 6010cd16efSeric ** about p-files, etc. 6110cd16efSeric ** delget Macro for "delta" followed by "get". 6210cd16efSeric ** deledit Macro for "delta" followed by "get -e". 6310cd16efSeric ** info Tell what files being edited. 6410cd16efSeric ** clean Remove all files that can be 6510cd16efSeric ** regenerated from SCCS files. 66095915d3Seric ** check Like info, but return exit status, for 6710cd16efSeric ** use in makefiles. 6810cd16efSeric ** fix Remove a top delta & reedit, but save 6910cd16efSeric ** the previous changes in that delta. 7010cd16efSeric ** 7110cd16efSeric ** Compilation Flags: 7210cd16efSeric ** UIDUSER -- determine who the user is by looking at the 7310cd16efSeric ** uid rather than the login name -- for machines 7410cd16efSeric ** where SCCS gets the user in this way. 757abec564Seric ** SCCSDIR -- if defined, forces the -d flag to take on 76095915d3Seric ** this value. This is so that the setuid 77095915d3Seric ** aspects of this program cannot be abused. 787abec564Seric ** This flag also disables the -p flag. 797abec564Seric ** SCCSPATH -- the default for the -p flag. 8010cd16efSeric ** 8110cd16efSeric ** Compilation Instructions: 8210cd16efSeric ** cc -O -n -s sccs.c 8310cd16efSeric ** 8410cd16efSeric ** Author: 8510cd16efSeric ** Eric Allman, UCB/INGRES 867abec564Seric ** Copyright 1980 Regents of the University of California 8710cd16efSeric */ 8810cd16efSeric 89*0faf63f9Seric static char SccsId[] = "@(#)sccs.c 1.35 10/15/80"; 90*0faf63f9Seric 917abec564Seric /******************* Configuration Information ********************/ 927abec564Seric 9310cd16efSeric # ifdef CSVAX 9410cd16efSeric # define UIDUSER 957abec564Seric # define PROGPATH(name) "/usr/local/name" 967abec564Seric # endif CSVAX 9710cd16efSeric 98*0faf63f9Seric # define SCCSPATH "SCCS" /* pathname in which to find s-files */ 997abec564Seric /* put #define SCCSDIR here */ 1007abec564Seric 1017abec564Seric char MyName[] = "sccs"; /* name used in messages */ 1027abec564Seric 103*0faf63f9Seric # ifndef PROGPATH 104*0faf63f9Seric # define PROGPATH(name) "/usr/sccs/name" /* place to find binaries */ 105*0faf63f9Seric # endif PROGPATH 106*0faf63f9Seric 1077abec564Seric /**************** End of Configuration Information ****************/ 108*0faf63f9Seric 109c4432be4Seric # define bitset(bit, word) ((bit) & (word)) 110c4432be4Seric 111c4432be4Seric typedef char bool; 1127de81dc7Seric # define TRUE 1 1137de81dc7Seric # define FALSE 0 114d02a4f42Seric 11510cd16efSeric # ifdef UIDUSER 11610cd16efSeric # include <pwd.h> 11710cd16efSeric # endif UIDUSER 11810cd16efSeric 119adf8f7d4Seric struct sccsprog 120adf8f7d4Seric { 121adf8f7d4Seric char *sccsname; /* name of SCCS routine */ 1227de81dc7Seric short sccsoper; /* opcode, see below */ 1237de81dc7Seric short sccsflags; /* flags, see below */ 124108d6082Seric char *sccsklets; /* valid key-letters on macros */ 125adf8f7d4Seric char *sccspath; /* pathname of binary implementing */ 126adf8f7d4Seric }; 127adf8f7d4Seric 1287de81dc7Seric /* values for sccsoper */ 1297de81dc7Seric # define PROG 0 /* call a program */ 1301777fbcbSeric # define CMACRO 1 /* command substitution macro */ 131172147faSeric # define FIX 2 /* fix a delta */ 132b2538d76Seric # define CLEAN 3 /* clean out recreatable files */ 133e39a5722Seric # define UNEDIT 4 /* unedit a file */ 134419708b0Seric # define SHELL 5 /* call a shell file (like PROG) */ 1357de81dc7Seric 136c4432be4Seric /* bits for sccsflags */ 1377de81dc7Seric # define NO_SDOT 0001 /* no s. on front of args */ 1387de81dc7Seric # define REALUSER 0002 /* protected (e.g., admin) */ 139adf8f7d4Seric 14048ff0e7eSeric /* modes for the "clean", "info", "check" ops */ 14148ff0e7eSeric # define CLEANC 0 /* clean command */ 14248ff0e7eSeric # define INFOC 1 /* info command */ 14348ff0e7eSeric # define CHECKC 2 /* check command */ 14448ff0e7eSeric 145*0faf63f9Seric /* 146*0faf63f9Seric ** Description of commands known to this program. 147*0faf63f9Seric ** First argument puts the command into a class. Second arg is 148*0faf63f9Seric ** info regarding treatment of this command. Third arg is a 149*0faf63f9Seric ** list of flags this command accepts from macros, etc. Fourth 150*0faf63f9Seric ** arg is the pathname of the implementing program, or the 151*0faf63f9Seric ** macro definition, or the arg to a sub-algorithm. 152*0faf63f9Seric */ 153b5d4f080Seric 154adf8f7d4Seric struct sccsprog SccsProg[] = 155adf8f7d4Seric { 156108d6082Seric "admin", PROG, REALUSER, "", PROGPATH(admin), 157108d6082Seric "chghist", PROG, 0, "", PROGPATH(rmdel), 158108d6082Seric "comb", PROG, 0, "", PROGPATH(comb), 159108d6082Seric "delta", PROG, 0, "mysrp", PROGPATH(delta), 160c952b8bfSeric "get", PROG, 0, "ixbeskcl", PROGPATH(get), 161108d6082Seric "help", PROG, NO_SDOT, "", PROGPATH(help), 162108d6082Seric "prt", PROG, 0, "", PROGPATH(prt), 163108d6082Seric "rmdel", PROG, REALUSER, "", PROGPATH(rmdel), 164108d6082Seric "what", PROG, NO_SDOT, "", PROGPATH(what), 165419708b0Seric "sccsdiff", SHELL, REALUSER, "", PROGPATH(sccsdiff), 166c952b8bfSeric "edit", CMACRO, NO_SDOT, "ixbscl", "get -e", 167108d6082Seric "delget", CMACRO, NO_SDOT, "", "delta/get -t", 168108d6082Seric "deledit", CMACRO, NO_SDOT, "", "delta/get -e -t", 169108d6082Seric "fix", FIX, NO_SDOT, "", NULL, 170108d6082Seric "clean", CLEAN, REALUSER, "", (char *) CLEANC, 171108d6082Seric "info", CLEAN, REALUSER, "", (char *) INFOC, 172108d6082Seric "check", CLEAN, REALUSER, "", (char *) CHECKC, 173108d6082Seric "unedit", UNEDIT, NO_SDOT, "", NULL, 174108d6082Seric NULL, -1, 0, "", NULL 175adf8f7d4Seric }; 176adf8f7d4Seric 177*0faf63f9Seric /* one line from a p-file */ 178e39a5722Seric struct pfile 179e39a5722Seric { 180e39a5722Seric char *p_osid; /* old SID */ 181e39a5722Seric char *p_nsid; /* new SID */ 182e39a5722Seric char *p_user; /* user who did edit */ 183e39a5722Seric char *p_date; /* date of get */ 184e39a5722Seric char *p_time; /* time of get */ 185e39a5722Seric }; 186e39a5722Seric 1877abec564Seric char *SccsPath = SCCSPATH; /* pathname of SCCS files */ 1887abec564Seric # ifdef SCCSDIR 1897abec564Seric char *SccsDir = SCCSDIR; /* directory to begin search from */ 190095915d3Seric # else 1917abec564Seric char *SccsDir = ""; 192095915d3Seric # endif 193c4432be4Seric bool RealUser; /* if set, running as real user */ 194f0cc3246Seric # ifdef DEBUG 195f0cc3246Seric bool Debug; /* turn on tracing */ 196f0cc3246Seric # endif 197*0faf63f9Seric 198adf8f7d4Seric main(argc, argv) 199adf8f7d4Seric int argc; 200adf8f7d4Seric char **argv; 201adf8f7d4Seric { 202adf8f7d4Seric register char *p; 203b1ed8a43Seric extern struct sccsprog *lookup(); 20441290c52Seric register int i; 205adf8f7d4Seric 206adf8f7d4Seric /* 207adf8f7d4Seric ** Detect and decode flags intended for this program. 208adf8f7d4Seric */ 209adf8f7d4Seric 2107de81dc7Seric if (argc < 2) 211adf8f7d4Seric { 212095915d3Seric fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); 2137de81dc7Seric exit(EX_USAGE); 2147de81dc7Seric } 2157de81dc7Seric argv[argc] = NULL; 2167de81dc7Seric 217b1ed8a43Seric if (lookup(argv[0]) == NULL) 218b1ed8a43Seric { 2197de81dc7Seric while ((p = *++argv) != NULL) 2207de81dc7Seric { 221adf8f7d4Seric if (*p != '-') 222adf8f7d4Seric break; 223adf8f7d4Seric switch (*++p) 224adf8f7d4Seric { 225adf8f7d4Seric case 'r': /* run as real user */ 226adf8f7d4Seric setuid(getuid()); 227c4432be4Seric RealUser++; 228adf8f7d4Seric break; 229adf8f7d4Seric 2307abec564Seric # ifndef SCCSDIR 231adf8f7d4Seric case 'p': /* path of sccs files */ 232adf8f7d4Seric SccsPath = ++p; 233adf8f7d4Seric break; 234adf8f7d4Seric 2350a8b9ab0Seric case 'd': /* directory to search from */ 2360a8b9ab0Seric SccsDir = ++p; 2370a8b9ab0Seric break; 238095915d3Seric # endif 2390a8b9ab0Seric 240f0cc3246Seric # ifdef DEBUG 241f0cc3246Seric case 'T': /* trace */ 242f0cc3246Seric Debug++; 243f0cc3246Seric break; 244f0cc3246Seric # endif 245f0cc3246Seric 246adf8f7d4Seric default: 247095915d3Seric usrerr("unknown option -%s", p); 248adf8f7d4Seric break; 249adf8f7d4Seric } 250adf8f7d4Seric } 2515cabffd9Seric if (SccsPath[0] == '\0') 2525cabffd9Seric SccsPath = "."; 253b1ed8a43Seric } 254adf8f7d4Seric 255108d6082Seric i = command(argv, FALSE, FALSE, ""); 25641290c52Seric exit(i); 2577de81dc7Seric } 258*0faf63f9Seric 259*0faf63f9Seric /* 26041290c52Seric ** COMMAND -- look up and perform a command 26141290c52Seric ** 26241290c52Seric ** This routine is the guts of this program. Given an 26341290c52Seric ** argument vector, it looks up the "command" (argv[0]) 26441290c52Seric ** in the configuration table and does the necessary stuff. 26541290c52Seric ** 26641290c52Seric ** Parameters: 26741290c52Seric ** argv -- an argument vector to process. 26841290c52Seric ** forkflag -- if set, fork before executing the command. 269108d6082Seric ** editflag -- if set, only include flags listed in the 270108d6082Seric ** sccsklets field of the command descriptor. 271108d6082Seric ** arg0 -- a space-seperated list of arguments to insert 272108d6082Seric ** before argv. 27341290c52Seric ** 27441290c52Seric ** Returns: 27541290c52Seric ** zero -- command executed ok. 27641290c52Seric ** else -- error status. 27741290c52Seric ** 27841290c52Seric ** Side Effects: 27941290c52Seric ** none. 28041290c52Seric */ 2817de81dc7Seric 282108d6082Seric command(argv, forkflag, editflag, arg0) 2837de81dc7Seric char **argv; 2841777fbcbSeric bool forkflag; 285108d6082Seric bool editflag; 286108d6082Seric char *arg0; 2877de81dc7Seric { 2887de81dc7Seric register struct sccsprog *cmd; 2897de81dc7Seric register char *p; 2901777fbcbSeric char buf[40]; 291b1ed8a43Seric extern struct sccsprog *lookup(); 292108d6082Seric char *nav[1000]; 293108d6082Seric char **np; 294419708b0Seric register char **ap; 295d51cd7e5Seric register int i; 296419708b0Seric register char *q; 297d51cd7e5Seric extern bool unedit(); 29841290c52Seric int rval = 0; 299108d6082Seric extern char *index(); 300108d6082Seric extern char *makefile(); 301f0cc3246Seric 302f0cc3246Seric # ifdef DEBUG 303f0cc3246Seric if (Debug) 304f0cc3246Seric { 305108d6082Seric printf("command:\n\t\"%s\"\n", arg0); 306108d6082Seric for (np = argv; *np != NULL; np++) 307108d6082Seric printf("\t\"%s\"\n", *np); 308f0cc3246Seric } 309f0cc3246Seric # endif 310c4432be4Seric 311c4432be4Seric /* 312108d6082Seric ** Copy arguments. 313108d6082Seric ** Phase one -- from arg0 & if necessary argv[0]. 314adf8f7d4Seric */ 315adf8f7d4Seric 316419708b0Seric np = ap = &nav[1]; 317108d6082Seric for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) 318108d6082Seric { 319108d6082Seric *np++ = q; 320108d6082Seric while (*p == ' ') 321108d6082Seric p++; 322108d6082Seric while (*p != ' ' && *p != '\0' && *p != '/') 323108d6082Seric *q++ = *p++; 324108d6082Seric *q++ = '\0'; 325108d6082Seric } 326108d6082Seric *np = NULL; 327419708b0Seric if (*ap == NULL) 328108d6082Seric *np++ = *argv++; 329108d6082Seric 330108d6082Seric /* 331108d6082Seric ** Look up command. 332419708b0Seric ** At this point, *ap is the command name. 333108d6082Seric */ 334108d6082Seric 335419708b0Seric cmd = lookup(*ap); 336b1ed8a43Seric if (cmd == NULL) 337adf8f7d4Seric { 338419708b0Seric usrerr("Unknown command \"%s\"", *ap); 33941290c52Seric return (EX_USAGE); 340adf8f7d4Seric } 341adf8f7d4Seric 342adf8f7d4Seric /* 343108d6082Seric ** Copy remaining arguments doing editing as appropriate. 344108d6082Seric */ 345108d6082Seric 346108d6082Seric for (; *argv != NULL; argv++) 347108d6082Seric { 348108d6082Seric p = *argv; 349108d6082Seric if (*p == '-') 350108d6082Seric { 351108d6082Seric if (p[1] == '\0' || !editflag || cmd->sccsklets == NULL || 352108d6082Seric index(cmd->sccsklets, p[1]) != NULL) 353108d6082Seric *np++ = p; 354108d6082Seric } 355108d6082Seric else 356108d6082Seric { 357108d6082Seric if (!bitset(NO_SDOT, cmd->sccsflags)) 358108d6082Seric p = makefile(p); 359108d6082Seric if (p != NULL) 360108d6082Seric *np++ = p; 361108d6082Seric } 362108d6082Seric } 363108d6082Seric *np = NULL; 364108d6082Seric 365108d6082Seric /* 3667de81dc7Seric ** Interpret operation associated with this command. 367c4432be4Seric */ 368c4432be4Seric 3697de81dc7Seric switch (cmd->sccsoper) 3707de81dc7Seric { 371419708b0Seric case SHELL: /* call a shell file */ 372419708b0Seric *ap = cmd->sccspath; 373419708b0Seric *--ap = "sh"; 374419708b0Seric rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag); 375419708b0Seric break; 376419708b0Seric 3777de81dc7Seric case PROG: /* call an sccs prog */ 378419708b0Seric rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); 3791777fbcbSeric break; 3801777fbcbSeric 3811777fbcbSeric case CMACRO: /* command macro */ 3821777fbcbSeric for (p = cmd->sccspath; *p != '\0'; p++) 3831777fbcbSeric { 384108d6082Seric q = p; 385108d6082Seric while (*p != '\0' && *p != '/') 386108d6082Seric p++; 387419708b0Seric rval = command(&ap[1], *p != '\0', TRUE, q); 38841290c52Seric if (rval != 0) 38941290c52Seric break; 3901777fbcbSeric } 39141290c52Seric break; 3927de81dc7Seric 393172147faSeric case FIX: /* fix a delta */ 394419708b0Seric if (strncmp(ap[1], "-r", 2) != 0) 395172147faSeric { 396095915d3Seric usrerr("-r flag needed for fix command"); 39741290c52Seric rval = EX_USAGE; 398172147faSeric break; 399172147faSeric } 400419708b0Seric rval = command(&ap[1], TRUE, TRUE, "get -k"); 40141290c52Seric if (rval == 0) 402419708b0Seric rval = command(&ap[1], TRUE, TRUE, "rmdel"); 40341290c52Seric if (rval == 0) 404419708b0Seric rval = command(&ap[2], FALSE, TRUE, "get -e -g"); 40541290c52Seric break; 406172147faSeric 407b2538d76Seric case CLEAN: 40841290c52Seric rval = clean((int) cmd->sccspath); 409b2538d76Seric break; 410b2538d76Seric 411e39a5722Seric case UNEDIT: 412419708b0Seric for (argv = np = &ap[1]; *argv != NULL; argv++) 413d51cd7e5Seric { 414108d6082Seric if (unedit(*argv)) 415108d6082Seric *np++ = *argv; 416d51cd7e5Seric } 417108d6082Seric *np = NULL; 418d51cd7e5Seric if (i > 0) 419419708b0Seric rval = command(&ap[1], FALSE, FALSE, "get"); 420e39a5722Seric break; 421e39a5722Seric 4227de81dc7Seric default: 423095915d3Seric syserr("oper %d", cmd->sccsoper); 4247de81dc7Seric exit(EX_SOFTWARE); 4257de81dc7Seric } 42641290c52Seric # ifdef DEBUG 42741290c52Seric if (Debug) 42841290c52Seric printf("command: rval=%d\n", rval); 42941290c52Seric # endif 43041290c52Seric return (rval); 4317de81dc7Seric } 432*0faf63f9Seric 433*0faf63f9Seric /* 434b1ed8a43Seric ** LOOKUP -- look up an SCCS command name. 435b1ed8a43Seric ** 436b1ed8a43Seric ** Parameters: 437b1ed8a43Seric ** name -- the name of the command to look up. 438b1ed8a43Seric ** 439b1ed8a43Seric ** Returns: 440b1ed8a43Seric ** ptr to command descriptor for this command. 441b1ed8a43Seric ** NULL if no such entry. 442b1ed8a43Seric ** 443b1ed8a43Seric ** Side Effects: 444b1ed8a43Seric ** none. 445b1ed8a43Seric */ 446b1ed8a43Seric 447b1ed8a43Seric struct sccsprog * 448b1ed8a43Seric lookup(name) 449b1ed8a43Seric char *name; 450b1ed8a43Seric { 451b1ed8a43Seric register struct sccsprog *cmd; 452b1ed8a43Seric 453b1ed8a43Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 454b1ed8a43Seric { 455b1ed8a43Seric if (strcmp(cmd->sccsname, name) == 0) 456b1ed8a43Seric return (cmd); 457b1ed8a43Seric } 458b1ed8a43Seric return (NULL); 459b1ed8a43Seric } 460*0faf63f9Seric 461*0faf63f9Seric /* 46241290c52Seric ** CALLPROG -- call a program 46341290c52Seric ** 464108d6082Seric ** Used to call the SCCS programs. 46541290c52Seric ** 46641290c52Seric ** Parameters: 46741290c52Seric ** progpath -- pathname of the program to call. 46841290c52Seric ** flags -- status flags from the command descriptors. 46941290c52Seric ** argv -- an argument vector to pass to the program. 47041290c52Seric ** forkflag -- if true, fork before calling, else just 47141290c52Seric ** exec. 47241290c52Seric ** 47341290c52Seric ** Returns: 47441290c52Seric ** The exit status of the program. 47541290c52Seric ** Nothing if forkflag == FALSE. 47641290c52Seric ** 47741290c52Seric ** Side Effects: 47841290c52Seric ** Can exit if forkflag == FALSE. 47941290c52Seric */ 480172147faSeric 4817de81dc7Seric callprog(progpath, flags, argv, forkflag) 4827de81dc7Seric char *progpath; 4837de81dc7Seric short flags; 4847de81dc7Seric char **argv; 4857de81dc7Seric bool forkflag; 4867de81dc7Seric { 4877de81dc7Seric register int i; 4881777fbcbSeric auto int st; 489108d6082Seric 490108d6082Seric # ifdef DEBUG 491108d6082Seric if (Debug) 492108d6082Seric { 493108d6082Seric printf("callprog:\n"); 494108d6082Seric for (i = 0; argv[i] != NULL; i++) 495108d6082Seric printf("\t\"%s\"\n", argv[i]); 496108d6082Seric } 497108d6082Seric # endif 4987de81dc7Seric 4997de81dc7Seric if (*argv == NULL) 5007de81dc7Seric return (-1); 501c4432be4Seric 502c4432be4Seric /* 503172147faSeric ** Fork if appropriate. 504adf8f7d4Seric */ 505adf8f7d4Seric 5067de81dc7Seric if (forkflag) 5077de81dc7Seric { 508f0cc3246Seric # ifdef DEBUG 509f0cc3246Seric if (Debug) 510f0cc3246Seric printf("Forking\n"); 511f0cc3246Seric # endif 5127de81dc7Seric i = fork(); 5137de81dc7Seric if (i < 0) 5147de81dc7Seric { 515095915d3Seric syserr("cannot fork"); 5167de81dc7Seric exit(EX_OSERR); 5177de81dc7Seric } 5187de81dc7Seric else if (i > 0) 5191777fbcbSeric { 5201777fbcbSeric wait(&st); 52141290c52Seric if ((st & 0377) == 0) 52241290c52Seric st = (st >> 8) & 0377; 5231777fbcbSeric return (st); 5241777fbcbSeric } 5257de81dc7Seric } 5267de81dc7Seric 5277de81dc7Seric /* 5287de81dc7Seric ** Set protection as appropriate. 5297de81dc7Seric */ 5307de81dc7Seric 5317de81dc7Seric if (bitset(REALUSER, flags)) 5327de81dc7Seric setuid(getuid()); 5337de81dc7Seric 5347de81dc7Seric /* 535172147faSeric ** Call real SCCS program. 5367de81dc7Seric */ 5377de81dc7Seric 538172147faSeric execv(progpath, argv); 539095915d3Seric syserr("cannot execute %s", progpath); 540adf8f7d4Seric exit(EX_UNAVAILABLE); 541adf8f7d4Seric } 542*0faf63f9Seric 543*0faf63f9Seric /* 544cdc1aa65Seric ** MAKEFILE -- make filename of SCCS file 545cdc1aa65Seric ** 546cdc1aa65Seric ** If the name passed is already the name of an SCCS file, 547cdc1aa65Seric ** just return it. Otherwise, munge the name into the name 548cdc1aa65Seric ** of the actual SCCS file. 549cdc1aa65Seric ** 550cdc1aa65Seric ** There are cases when it is not clear what you want to 551cdc1aa65Seric ** do. For example, if SccsPath is an absolute pathname 552cdc1aa65Seric ** and the name given is also an absolute pathname, we go 553cdc1aa65Seric ** for SccsPath (& only use the last component of the name 554cdc1aa65Seric ** passed) -- this is important for security reasons (if 555cdc1aa65Seric ** sccs is being used as a setuid front end), but not 556cdc1aa65Seric ** particularly intuitive. 557cdc1aa65Seric ** 558cdc1aa65Seric ** Parameters: 559cdc1aa65Seric ** name -- the file name to be munged. 560cdc1aa65Seric ** 561cdc1aa65Seric ** Returns: 562cdc1aa65Seric ** The pathname of the sccs file. 563cdc1aa65Seric ** NULL on error. 564cdc1aa65Seric ** 565cdc1aa65Seric ** Side Effects: 566cdc1aa65Seric ** none. 567cdc1aa65Seric */ 568adf8f7d4Seric 569adf8f7d4Seric char * 570adf8f7d4Seric makefile(name) 571adf8f7d4Seric char *name; 572adf8f7d4Seric { 573adf8f7d4Seric register char *p; 574adf8f7d4Seric register char c; 575adf8f7d4Seric char buf[512]; 5760a8b9ab0Seric struct stat stbuf; 577adf8f7d4Seric extern char *malloc(); 578cdc1aa65Seric extern char *rindex(); 5790a8b9ab0Seric extern bool safepath(); 5807d70f5d8Seric extern bool isdir(); 5817d70f5d8Seric register char *q; 582cdc1aa65Seric 583cdc1aa65Seric p = rindex(name, '/'); 584cdc1aa65Seric if (p == NULL) 585cdc1aa65Seric p = name; 586cdc1aa65Seric else 587cdc1aa65Seric p++; 588adf8f7d4Seric 589adf8f7d4Seric /* 5900a8b9ab0Seric ** Check to see that the path is "safe", i.e., that we 5910a8b9ab0Seric ** are not letting some nasty person use the setuid part 5920a8b9ab0Seric ** of this program to look at or munge some presumably 5930a8b9ab0Seric ** hidden files. 594adf8f7d4Seric */ 595adf8f7d4Seric 5960a8b9ab0Seric if (SccsDir[0] == '/' && !safepath(name)) 5970a8b9ab0Seric return (NULL); 598cdc1aa65Seric 599cdc1aa65Seric /* 6000a8b9ab0Seric ** Create the base pathname. 601cdc1aa65Seric */ 602cdc1aa65Seric 6030a8b9ab0Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) 604adf8f7d4Seric { 6050a8b9ab0Seric strcpy(buf, SccsDir); 606cdc1aa65Seric strcat(buf, "/"); 607cdc1aa65Seric } 608cdc1aa65Seric else 609cdc1aa65Seric strcpy(buf, ""); 610cdc1aa65Seric strncat(buf, name, p - name); 6117d70f5d8Seric q = &buf[strlen(buf)]; 6127d70f5d8Seric strcpy(q, p); 6137d70f5d8Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 6147d70f5d8Seric { 6150a8b9ab0Seric strcpy(q, SccsPath); 6160a8b9ab0Seric strcat(buf, "/s."); 617cdc1aa65Seric strcat(buf, p); 618cdc1aa65Seric } 619adf8f7d4Seric 6200a8b9ab0Seric if (strcmp(buf, name) == 0) 6210a8b9ab0Seric p = name; 6220a8b9ab0Seric else 6230a8b9ab0Seric { 624adf8f7d4Seric p = malloc(strlen(buf) + 1); 625adf8f7d4Seric if (p == NULL) 626adf8f7d4Seric { 627adf8f7d4Seric perror("Sccs: no mem"); 628adf8f7d4Seric exit(EX_OSERR); 629adf8f7d4Seric } 630adf8f7d4Seric strcpy(p, buf); 6310a8b9ab0Seric } 632adf8f7d4Seric return (p); 633adf8f7d4Seric } 634*0faf63f9Seric 635*0faf63f9Seric /* 6367d70f5d8Seric ** ISDIR -- return true if the argument is a directory. 6377d70f5d8Seric ** 6387d70f5d8Seric ** Parameters: 6397d70f5d8Seric ** name -- the pathname of the file to check. 6407d70f5d8Seric ** 6417d70f5d8Seric ** Returns: 6427d70f5d8Seric ** TRUE if 'name' is a directory, FALSE otherwise. 6437d70f5d8Seric ** 6447d70f5d8Seric ** Side Effects: 6457d70f5d8Seric ** none. 6467d70f5d8Seric */ 6477d70f5d8Seric 6487d70f5d8Seric bool 6497d70f5d8Seric isdir(name) 6507d70f5d8Seric char *name; 6517d70f5d8Seric { 6527d70f5d8Seric struct stat stbuf; 6537d70f5d8Seric 6547d70f5d8Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 6557d70f5d8Seric } 656*0faf63f9Seric 657*0faf63f9Seric /* 658cdc1aa65Seric ** SAFEPATH -- determine whether a pathname is "safe" 659cdc1aa65Seric ** 660cdc1aa65Seric ** "Safe" pathnames only allow you to get deeper into the 661cdc1aa65Seric ** directory structure, i.e., full pathnames and ".." are 662cdc1aa65Seric ** not allowed. 663cdc1aa65Seric ** 664cdc1aa65Seric ** Parameters: 665cdc1aa65Seric ** p -- the name to check. 666cdc1aa65Seric ** 667cdc1aa65Seric ** Returns: 668cdc1aa65Seric ** TRUE -- if the path is safe. 669cdc1aa65Seric ** FALSE -- if the path is not safe. 670cdc1aa65Seric ** 671cdc1aa65Seric ** Side Effects: 672cdc1aa65Seric ** Prints a message if the path is not safe. 673cdc1aa65Seric */ 674cdc1aa65Seric 675cdc1aa65Seric bool 676cdc1aa65Seric safepath(p) 677cdc1aa65Seric register char *p; 678cdc1aa65Seric { 679cdc1aa65Seric extern char *index(); 680cdc1aa65Seric 681cdc1aa65Seric if (*p != '/') 682cdc1aa65Seric { 683cdc1aa65Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 684cdc1aa65Seric { 685cdc1aa65Seric p = index(p, '/'); 686cdc1aa65Seric if (p == NULL) 687cdc1aa65Seric return (TRUE); 688cdc1aa65Seric p++; 689cdc1aa65Seric } 690cdc1aa65Seric } 691cdc1aa65Seric 692cdc1aa65Seric printf("You may not use full pathnames or \"..\"\n"); 693cdc1aa65Seric return (FALSE); 694cdc1aa65Seric } 695*0faf63f9Seric 696*0faf63f9Seric /* 697b2538d76Seric ** CLEAN -- clean out recreatable files 698b2538d76Seric ** 699b2538d76Seric ** Any file for which an "s." file exists but no "p." file 700b2538d76Seric ** exists in the current directory is purged. 701b2538d76Seric ** 702b2538d76Seric ** Parameters: 70348ff0e7eSeric ** tells whether this came from a "clean", "info", or 70448ff0e7eSeric ** "check" command. 705b2538d76Seric ** 706b2538d76Seric ** Returns: 707b2538d76Seric ** none. 708b2538d76Seric ** 709b2538d76Seric ** Side Effects: 71048ff0e7eSeric ** Removes files in the current directory. 71148ff0e7eSeric ** Prints information regarding files being edited. 71248ff0e7eSeric ** Exits if a "check" command. 713b2538d76Seric */ 714b2538d76Seric 71548ff0e7eSeric clean(mode) 71648ff0e7eSeric int mode; 717b2538d76Seric { 718b2538d76Seric struct direct dir; 719b2538d76Seric struct stat stbuf; 720b2538d76Seric char buf[100]; 721bf9c1e66Seric char pline[120]; 7223a208c77Seric register FILE *dirfd; 7233a208c77Seric register char *basefile; 7242caac8fdSeric bool gotedit; 725bf9c1e66Seric FILE *pfp; 726b2538d76Seric 727f77a08fbSeric strcpy(buf, SccsDir); 728f77a08fbSeric if (buf[0] != '\0') 729f77a08fbSeric strcat(buf, "/"); 730f77a08fbSeric strcat(buf, SccsPath); 731f77a08fbSeric dirfd = fopen(buf, "r"); 732b2538d76Seric if (dirfd == NULL) 733b2538d76Seric { 734f77a08fbSeric usrerr("cannot open %s", buf); 73541290c52Seric return (EX_NOINPUT); 736b2538d76Seric } 737b2538d76Seric 738b2538d76Seric /* 739b2538d76Seric ** Scan the SCCS directory looking for s. files. 740b2538d76Seric */ 741b2538d76Seric 7422caac8fdSeric gotedit = FALSE; 743b2538d76Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 744b2538d76Seric { 745c9c62fd0Seric if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) 746b2538d76Seric continue; 747b2538d76Seric 748b2538d76Seric /* got an s. file -- see if the p. file exists */ 749f77a08fbSeric strcpy(buf, SccsDir); 750f77a08fbSeric if (buf[0] != '\0') 751f77a08fbSeric strcat(buf, "/"); 752f77a08fbSeric strcat(buf, SccsPath); 753b2538d76Seric strcat(buf, "/p."); 7543a208c77Seric basefile = &buf[strlen(buf)]; 755c9c62fd0Seric strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 756bf9c1e66Seric basefile[sizeof dir.d_name - 2] = '\0'; 757bf9c1e66Seric pfp = fopen(buf, "r"); 758bf9c1e66Seric if (pfp != NULL) 7593a208c77Seric { 760bf9c1e66Seric while (fgets(pline, sizeof pline, pfp) != NULL) 7612444cd3eSeric printf("%12s: being edited: %s", basefile, pline); 762bf9c1e66Seric fclose(pfp); 7632caac8fdSeric gotedit = TRUE; 764b2538d76Seric continue; 7653a208c77Seric } 766b2538d76Seric 767b2538d76Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 76848ff0e7eSeric if (mode == CLEANC) 7693a208c77Seric { 770c9c62fd0Seric strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); 7713a208c77Seric buf[sizeof dir.d_name - 2] = '\0'; 772b2538d76Seric unlink(buf); 773b2538d76Seric } 7743a208c77Seric } 775b2538d76Seric 776b2538d76Seric fclose(dirfd); 77748ff0e7eSeric if (!gotedit && mode == INFOC) 7782444cd3eSeric printf("Nothing being edited\n"); 77948ff0e7eSeric if (mode == CHECKC) 78048ff0e7eSeric exit(gotedit); 78141290c52Seric return (EX_OK); 782b2538d76Seric } 783*0faf63f9Seric 784*0faf63f9Seric /* 785e39a5722Seric ** UNEDIT -- unedit a file 786e39a5722Seric ** 787e39a5722Seric ** Checks to see that the current user is actually editting 788e39a5722Seric ** the file and arranges that s/he is not editting it. 789e39a5722Seric ** 790e39a5722Seric ** Parameters: 7912444cd3eSeric ** fn -- the name of the file to be unedited. 792e39a5722Seric ** 793e39a5722Seric ** Returns: 794d51cd7e5Seric ** TRUE -- if the file was successfully unedited. 795d51cd7e5Seric ** FALSE -- if the file was not unedited for some 796d51cd7e5Seric ** reason. 797e39a5722Seric ** 798e39a5722Seric ** Side Effects: 799e39a5722Seric ** fn is removed 800e39a5722Seric ** entries are removed from pfile. 801e39a5722Seric */ 802e39a5722Seric 803d51cd7e5Seric bool 804e39a5722Seric unedit(fn) 805e39a5722Seric char *fn; 806e39a5722Seric { 807e39a5722Seric register FILE *pfp; 808e39a5722Seric char *pfn; 809e39a5722Seric static char tfn[] = "/tmp/sccsXXXXX"; 810e39a5722Seric FILE *tfp; 811e39a5722Seric register char *p; 812e39a5722Seric register char *q; 813e39a5722Seric bool delete = FALSE; 814e39a5722Seric bool others = FALSE; 815e39a5722Seric char *myname; 816e39a5722Seric extern char *getlogin(); 817e39a5722Seric struct pfile *pent; 818e39a5722Seric extern struct pfile *getpfile(); 819e39a5722Seric char buf[120]; 820108d6082Seric extern char *makefile(); 82110cd16efSeric # ifdef UIDUSER 82210cd16efSeric struct passwd *pw; 82310cd16efSeric extern struct passwd *getpwuid(); 82410cd16efSeric # endif UIDUSER 825e39a5722Seric 826e39a5722Seric /* make "s." filename & find the trailing component */ 827e39a5722Seric pfn = makefile(fn); 828cdc1aa65Seric if (pfn == NULL) 829cdc1aa65Seric return (FALSE); 830cdc1aa65Seric q = rindex(pfn, '/'); 831cdc1aa65Seric if (q == NULL) 832cdc1aa65Seric q = &pfn[-1]; 833cdc1aa65Seric if (q[1] != 's' || q[2] != '.') 834e39a5722Seric { 835095915d3Seric usrerr("bad file name \"%s\"", fn); 836d51cd7e5Seric return (FALSE); 837e39a5722Seric } 838e39a5722Seric 839e39a5722Seric /* turn "s." into "p." */ 840e39a5722Seric *++q = 'p'; 841e39a5722Seric 842e39a5722Seric pfp = fopen(pfn, "r"); 843e39a5722Seric if (pfp == NULL) 844e39a5722Seric { 8452444cd3eSeric printf("%12s: not being edited\n", fn); 846d51cd7e5Seric return (FALSE); 847e39a5722Seric } 848e39a5722Seric 849e39a5722Seric /* 850e39a5722Seric ** Copy p-file to temp file, doing deletions as needed. 851e39a5722Seric */ 852e39a5722Seric 853e39a5722Seric mktemp(tfn); 854e39a5722Seric tfp = fopen(tfn, "w"); 855e39a5722Seric if (tfp == NULL) 856e39a5722Seric { 857095915d3Seric usrerr("cannot create \"%s\"", tfn); 858e39a5722Seric exit(EX_OSERR); 859e39a5722Seric } 860e39a5722Seric 86110cd16efSeric # ifdef UIDUSER 86210cd16efSeric pw = getpwuid(getuid()); 86310cd16efSeric if (pw == NULL) 86410cd16efSeric { 865095915d3Seric syserr("who are you? (uid=%d)", getuid()); 86610cd16efSeric exit(EX_OSERR); 86710cd16efSeric } 86810cd16efSeric myname = pw->pw_name; 86910cd16efSeric # else 870e39a5722Seric myname = getlogin(); 87110cd16efSeric # endif UIDUSER 872e39a5722Seric while ((pent = getpfile(pfp)) != NULL) 873e39a5722Seric { 874e39a5722Seric if (strcmp(pent->p_user, myname) == 0) 875e39a5722Seric { 876e39a5722Seric /* a match */ 877e39a5722Seric delete++; 878e39a5722Seric } 879e39a5722Seric else 880e39a5722Seric { 881e39a5722Seric fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, 882e39a5722Seric pent->p_nsid, pent->p_user, pent->p_date, 883e39a5722Seric pent->p_time); 884e39a5722Seric others++; 885e39a5722Seric } 886e39a5722Seric } 887e39a5722Seric 888e39a5722Seric /* do final cleanup */ 889e39a5722Seric if (others) 890e39a5722Seric { 891e39a5722Seric if (freopen(tfn, "r", tfp) == NULL) 892e39a5722Seric { 893095915d3Seric syserr("cannot reopen \"%s\"", tfn); 894e39a5722Seric exit(EX_OSERR); 895e39a5722Seric } 896e39a5722Seric if (freopen(pfn, "w", pfp) == NULL) 897e39a5722Seric { 898095915d3Seric usrerr("cannot create \"%s\"", pfn); 899d51cd7e5Seric return (FALSE); 900e39a5722Seric } 901e39a5722Seric while (fgets(buf, sizeof buf, tfp) != NULL) 902e39a5722Seric fputs(buf, pfp); 903e39a5722Seric } 904e39a5722Seric else 905e39a5722Seric { 906e39a5722Seric unlink(pfn); 907e39a5722Seric } 908e39a5722Seric fclose(tfp); 909e39a5722Seric fclose(pfp); 910e39a5722Seric unlink(tfn); 911e39a5722Seric 912e39a5722Seric if (delete) 913e39a5722Seric { 914e39a5722Seric unlink(fn); 915e39a5722Seric printf("%12s: removed\n", fn); 916d51cd7e5Seric return (TRUE); 917e39a5722Seric } 918e39a5722Seric else 919e39a5722Seric { 9202444cd3eSeric printf("%12s: not being edited by you\n", fn); 921d51cd7e5Seric return (FALSE); 922e39a5722Seric } 923e39a5722Seric } 924*0faf63f9Seric 925*0faf63f9Seric /* 926e39a5722Seric ** GETPFILE -- get an entry from the p-file 927e39a5722Seric ** 928e39a5722Seric ** Parameters: 929e39a5722Seric ** pfp -- p-file file pointer 930e39a5722Seric ** 931e39a5722Seric ** Returns: 932e39a5722Seric ** pointer to p-file struct for next entry 933e39a5722Seric ** NULL on EOF or error 934e39a5722Seric ** 935e39a5722Seric ** Side Effects: 936e39a5722Seric ** Each call wipes out results of previous call. 937e39a5722Seric */ 938e39a5722Seric 939e39a5722Seric struct pfile * 940e39a5722Seric getpfile(pfp) 941e39a5722Seric FILE *pfp; 942e39a5722Seric { 943e39a5722Seric static struct pfile ent; 944e39a5722Seric static char buf[120]; 945e39a5722Seric register char *p; 946e39a5722Seric extern char *nextfield(); 947e39a5722Seric 948e39a5722Seric if (fgets(buf, sizeof buf, pfp) == NULL) 949e39a5722Seric return (NULL); 950e39a5722Seric 951e39a5722Seric ent.p_osid = p = buf; 952e39a5722Seric ent.p_nsid = p = nextfield(p); 953e39a5722Seric ent.p_user = p = nextfield(p); 954e39a5722Seric ent.p_date = p = nextfield(p); 955e39a5722Seric ent.p_time = p = nextfield(p); 956e39a5722Seric if (p == NULL || nextfield(p) != NULL) 957e39a5722Seric return (NULL); 958e39a5722Seric 959e39a5722Seric return (&ent); 960e39a5722Seric } 961e39a5722Seric 962e39a5722Seric 963e39a5722Seric char * 964e39a5722Seric nextfield(p) 965e39a5722Seric register char *p; 966e39a5722Seric { 967e39a5722Seric if (p == NULL || *p == '\0') 968e39a5722Seric return (NULL); 969e39a5722Seric while (*p != ' ' && *p != '\n' && *p != '\0') 970e39a5722Seric p++; 971e39a5722Seric if (*p == '\n' || *p == '\0') 972e39a5722Seric { 973e39a5722Seric *p = '\0'; 974e39a5722Seric return (NULL); 975e39a5722Seric } 976e39a5722Seric *p++ = '\0'; 977e39a5722Seric return (p); 978e39a5722Seric } 979*0faf63f9Seric 980*0faf63f9Seric /* 981095915d3Seric ** USRERR -- issue user-level error 982095915d3Seric ** 983095915d3Seric ** Parameters: 984095915d3Seric ** f -- format string. 985095915d3Seric ** p1-p3 -- parameters to a printf. 986095915d3Seric ** 987095915d3Seric ** Returns: 988095915d3Seric ** -1 989095915d3Seric ** 990095915d3Seric ** Side Effects: 991095915d3Seric ** none. 992095915d3Seric */ 993095915d3Seric 994095915d3Seric usrerr(f, p1, p2, p3) 995095915d3Seric char *f; 996095915d3Seric { 997095915d3Seric fprintf(stderr, "\n%s: ", MyName); 998095915d3Seric fprintf(stderr, f, p1, p2, p3); 999095915d3Seric fprintf(stderr, "\n"); 1000095915d3Seric 1001095915d3Seric return (-1); 1002095915d3Seric } 1003*0faf63f9Seric 1004*0faf63f9Seric /* 1005095915d3Seric ** SYSERR -- print system-generated error. 1006095915d3Seric ** 1007095915d3Seric ** Parameters: 1008095915d3Seric ** f -- format string to a printf. 1009095915d3Seric ** p1, p2, p3 -- parameters to f. 1010095915d3Seric ** 1011095915d3Seric ** Returns: 1012095915d3Seric ** never. 1013095915d3Seric ** 1014095915d3Seric ** Side Effects: 1015095915d3Seric ** none. 1016095915d3Seric */ 1017095915d3Seric 1018095915d3Seric syserr(f, p1, p2, p3) 1019095915d3Seric char *f; 1020095915d3Seric { 1021095915d3Seric extern int errno; 1022095915d3Seric 1023095915d3Seric fprintf(stderr, "\n%s SYSERR: ", MyName); 1024095915d3Seric fprintf(stderr, f, p1, p2, p3); 1025095915d3Seric fprintf(stderr, "\n"); 1026095915d3Seric if (errno == 0) 1027095915d3Seric exit(EX_SOFTWARE); 1028095915d3Seric else 1029095915d3Seric { 1030095915d3Seric perror(0); 1031095915d3Seric exit(EX_OSERR); 1032095915d3Seric } 1033095915d3Seric } 1034