1148Seric # include <stdio.h> 2148Seric # include <sys/types.h> 3148Seric # include <sys/stat.h> 4261Seric # include <sys/dir.h> 51433Seric # include <errno.h> 61433Seric # include <signal.h> 7148Seric # include <sysexits.h> 8202Seric # include <whoami.h> 9148Seric 10828Seric /* 11828Seric ** SCCS.C -- human-oriented front end to the SCCS system. 12828Seric ** 13828Seric ** Without trying to add any functionality to speak of, this 14828Seric ** program tries to make SCCS a little more accessible to human 15828Seric ** types. The main thing it does is automatically put the 16828Seric ** string "SCCS/s." on the front of names. Also, it has a 17828Seric ** couple of things that are designed to shorten frequent 18828Seric ** combinations, e.g., "delget" which expands to a "delta" 19828Seric ** and a "get". 20828Seric ** 21828Seric ** This program can also function as a setuid front end. 22828Seric ** To do this, you should copy the source, renaming it to 23828Seric ** whatever you want, e.g., "syssccs". Change any defaults 24828Seric ** in the program (e.g., syssccs might default -d to 25828Seric ** "/usr/src/sys"). Then recompile and put the result 26828Seric ** as setuid to whomever you want. In this mode, sccs 27828Seric ** knows to not run setuid for certain programs in order 28828Seric ** to preserve security, and so forth. 29828Seric ** 30828Seric ** Usage: 31828Seric ** sccs [flags] command [args] 32828Seric ** 33828Seric ** Flags: 34828Seric ** -d<dir> <dir> represents a directory to search 35828Seric ** out of. It should be a full pathname 36828Seric ** for general usage. E.g., if <dir> is 37828Seric ** "/usr/src/sys", then a reference to the 38828Seric ** file "dev/bio.c" becomes a reference to 39828Seric ** "/usr/src/sys/dev/bio.c". 40828Seric ** -p<path> prepends <path> to the final component 41828Seric ** of the pathname. By default, this is 42828Seric ** "SCCS". For example, in the -d example 43828Seric ** above, the path then gets modified to 44828Seric ** "/usr/src/sys/dev/SCCS/s.bio.c". In 45828Seric ** more common usage (without the -d flag), 46828Seric ** "prog.c" would get modified to 47828Seric ** "SCCS/s.prog.c". In both cases, the 48828Seric ** "s." gets automatically prepended. 49828Seric ** -r run as the real user. 50828Seric ** 51828Seric ** Commands: 52828Seric ** admin, 53828Seric ** get, 54828Seric ** delta, 55828Seric ** rmdel, 56828Seric ** chghist, 57828Seric ** etc. Straight out of SCCS; only difference 58828Seric ** is that pathnames get modified as 59828Seric ** described above. 60828Seric ** edit Macro for "get -e". 61828Seric ** unedit Removes a file being edited, knowing 62828Seric ** about p-files, etc. 63828Seric ** delget Macro for "delta" followed by "get". 64828Seric ** deledit Macro for "delta" followed by "get -e". 65828Seric ** info Tell what files being edited. 66828Seric ** clean Remove all files that can be 67828Seric ** regenerated from SCCS files. 681205Seric ** check Like info, but return exit status, for 69828Seric ** use in makefiles. 70828Seric ** fix Remove a top delta & reedit, but save 71828Seric ** the previous changes in that delta. 72828Seric ** 73828Seric ** Compilation Flags: 74828Seric ** UIDUSER -- determine who the user is by looking at the 75828Seric ** uid rather than the login name -- for machines 76828Seric ** where SCCS gets the user in this way. 771270Seric ** SCCSDIR -- if defined, forces the -d flag to take on 781205Seric ** this value. This is so that the setuid 791205Seric ** aspects of this program cannot be abused. 801270Seric ** This flag also disables the -p flag. 811270Seric ** SCCSPATH -- the default for the -p flag. 82*1437Seric ** MYNAME -- the title this program should print when it 83*1437Seric ** gives error messages. 84828Seric ** 85828Seric ** Compilation Instructions: 86828Seric ** cc -O -n -s sccs.c 87*1437Seric ** The flags listed above can be -D defined to simplify 88*1437Seric ** recompilation for variant versions. 89828Seric ** 90828Seric ** Author: 91828Seric ** Eric Allman, UCB/INGRES 921270Seric ** Copyright 1980 Regents of the University of California 93828Seric */ 94155Seric 95*1437Seric static char SccsId[] = "@(#)sccs.c 1.38 10/15/80"; 961432Seric 971270Seric /******************* Configuration Information ********************/ 981270Seric 99*1437Seric /* special defines for local berkeley systems */ 100*1437Seric # include <whoami.h> 101*1437Seric 102828Seric # ifdef CSVAX 103828Seric # define UIDUSER 1041270Seric # define PROGPATH(name) "/usr/local/name" 1051270Seric # endif CSVAX 106828Seric 107*1437Seric /* end of berkeley systems defines */ 108*1437Seric 109*1437Seric # ifndef SCCSPATH 1101432Seric # define SCCSPATH "SCCS" /* pathname in which to find s-files */ 111*1437Seric # endif NOT SCCSPATH 112828Seric 113*1437Seric # ifndef MYNAME 114*1437Seric # define MYNAME "sccs" /* name used for printing errors */ 115*1437Seric # endif NOT MYNAME 1161270Seric 1171432Seric # ifndef PROGPATH 1181432Seric # define PROGPATH(name) "/usr/sccs/name" /* place to find binaries */ 1191432Seric # endif PROGPATH 1201432Seric 1211270Seric /**************** End of Configuration Information ****************/ 1221432Seric 123157Seric # define bitset(bit, word) ((bit) & (word)) 124157Seric 125157Seric typedef char bool; 126200Seric # define TRUE 1 127200Seric # define FALSE 0 128157Seric 129828Seric # ifdef UIDUSER 130828Seric # include <pwd.h> 131828Seric # endif UIDUSER 132828Seric 133148Seric struct sccsprog 134148Seric { 135148Seric char *sccsname; /* name of SCCS routine */ 136200Seric short sccsoper; /* opcode, see below */ 137200Seric short sccsflags; /* flags, see below */ 1381316Seric char *sccsklets; /* valid key-letters on macros */ 139148Seric char *sccspath; /* pathname of binary implementing */ 140148Seric }; 141148Seric 142200Seric /* values for sccsoper */ 143200Seric # define PROG 0 /* call a program */ 144201Seric # define CMACRO 1 /* command substitution macro */ 145226Seric # define FIX 2 /* fix a delta */ 146261Seric # define CLEAN 3 /* clean out recreatable files */ 147396Seric # define UNEDIT 4 /* unedit a file */ 1481431Seric # define SHELL 5 /* call a shell file (like PROG) */ 1491433Seric # define DIFFS 6 /* diff between sccs & file out */ 150200Seric 151157Seric /* bits for sccsflags */ 152200Seric # define NO_SDOT 0001 /* no s. on front of args */ 153200Seric # define REALUSER 0002 /* protected (e.g., admin) */ 154148Seric 155819Seric /* modes for the "clean", "info", "check" ops */ 156819Seric # define CLEANC 0 /* clean command */ 157819Seric # define INFOC 1 /* info command */ 158819Seric # define CHECKC 2 /* check command */ 159819Seric 1601432Seric /* 1611432Seric ** Description of commands known to this program. 1621432Seric ** First argument puts the command into a class. Second arg is 1631432Seric ** info regarding treatment of this command. Third arg is a 1641432Seric ** list of flags this command accepts from macros, etc. Fourth 1651432Seric ** arg is the pathname of the implementing program, or the 1661432Seric ** macro definition, or the arg to a sub-algorithm. 1671432Seric */ 168202Seric 169148Seric struct sccsprog SccsProg[] = 170148Seric { 1711316Seric "admin", PROG, REALUSER, "", PROGPATH(admin), 1721316Seric "chghist", PROG, 0, "", PROGPATH(rmdel), 1731316Seric "comb", PROG, 0, "", PROGPATH(comb), 1741316Seric "delta", PROG, 0, "mysrp", PROGPATH(delta), 1751317Seric "get", PROG, 0, "ixbeskcl", PROGPATH(get), 1761316Seric "help", PROG, NO_SDOT, "", PROGPATH(help), 1771316Seric "prt", PROG, 0, "", PROGPATH(prt), 1781316Seric "rmdel", PROG, REALUSER, "", PROGPATH(rmdel), 1791316Seric "what", PROG, NO_SDOT, "", PROGPATH(what), 1801431Seric "sccsdiff", SHELL, REALUSER, "", PROGPATH(sccsdiff), 1811317Seric "edit", CMACRO, NO_SDOT, "ixbscl", "get -e", 1821316Seric "delget", CMACRO, NO_SDOT, "", "delta/get -t", 1831316Seric "deledit", CMACRO, NO_SDOT, "", "delta/get -e -t", 1841316Seric "fix", FIX, NO_SDOT, "", NULL, 1851316Seric "clean", CLEAN, REALUSER, "", (char *) CLEANC, 1861316Seric "info", CLEAN, REALUSER, "", (char *) INFOC, 1871316Seric "check", CLEAN, REALUSER, "", (char *) CHECKC, 1881316Seric "unedit", UNEDIT, NO_SDOT, "", NULL, 1891433Seric "diffs", DIFFS, NO_SDOT|REALUSER, "", NULL, 1901316Seric NULL, -1, 0, "", NULL 191148Seric }; 192148Seric 1931432Seric /* one line from a p-file */ 194396Seric struct pfile 195396Seric { 196396Seric char *p_osid; /* old SID */ 197396Seric char *p_nsid; /* new SID */ 198396Seric char *p_user; /* user who did edit */ 199396Seric char *p_date; /* date of get */ 200396Seric char *p_time; /* time of get */ 201396Seric }; 202396Seric 2031270Seric char *SccsPath = SCCSPATH; /* pathname of SCCS files */ 2041270Seric # ifdef SCCSDIR 2051270Seric char *SccsDir = SCCSDIR; /* directory to begin search from */ 2061205Seric # else 2071270Seric char *SccsDir = ""; 2081205Seric # endif 209*1437Seric char MyName[] = MYNAME; /* name used in messages */ 2101433Seric int OutFile = -1; /* override output file for commands */ 211157Seric bool RealUser; /* if set, running as real user */ 212393Seric # ifdef DEBUG 213393Seric bool Debug; /* turn on tracing */ 214393Seric # endif 2151432Seric 216148Seric main(argc, argv) 217148Seric int argc; 218148Seric char **argv; 219148Seric { 220148Seric register char *p; 221262Seric extern struct sccsprog *lookup(); 2221282Seric register int i; 223148Seric 224148Seric /* 225148Seric ** Detect and decode flags intended for this program. 226148Seric */ 227148Seric 228200Seric if (argc < 2) 229148Seric { 2301205Seric fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); 231200Seric exit(EX_USAGE); 232200Seric } 233200Seric argv[argc] = NULL; 234200Seric 235262Seric if (lookup(argv[0]) == NULL) 236200Seric { 237262Seric while ((p = *++argv) != NULL) 238148Seric { 239262Seric if (*p != '-') 240262Seric break; 241262Seric switch (*++p) 242262Seric { 243262Seric case 'r': /* run as real user */ 244262Seric setuid(getuid()); 245262Seric RealUser++; 246262Seric break; 247148Seric 2481270Seric # ifndef SCCSDIR 249262Seric case 'p': /* path of sccs files */ 250262Seric SccsPath = ++p; 251262Seric break; 252148Seric 253588Seric case 'd': /* directory to search from */ 254588Seric SccsDir = ++p; 255588Seric break; 2561205Seric # endif 257588Seric 258393Seric # ifdef DEBUG 259393Seric case 'T': /* trace */ 260393Seric Debug++; 261393Seric break; 262393Seric # endif 263393Seric 264262Seric default: 2651205Seric usrerr("unknown option -%s", p); 266262Seric break; 267262Seric } 268148Seric } 269262Seric if (SccsPath[0] == '\0') 270262Seric SccsPath = "."; 271148Seric } 272148Seric 2731316Seric i = command(argv, FALSE, FALSE, ""); 2741282Seric exit(i); 275200Seric } 2761432Seric 2771432Seric /* 2781282Seric ** COMMAND -- look up and perform a command 2791282Seric ** 2801282Seric ** This routine is the guts of this program. Given an 2811282Seric ** argument vector, it looks up the "command" (argv[0]) 2821282Seric ** in the configuration table and does the necessary stuff. 2831282Seric ** 2841282Seric ** Parameters: 2851282Seric ** argv -- an argument vector to process. 2861282Seric ** forkflag -- if set, fork before executing the command. 2871316Seric ** editflag -- if set, only include flags listed in the 2881316Seric ** sccsklets field of the command descriptor. 2891316Seric ** arg0 -- a space-seperated list of arguments to insert 2901316Seric ** before argv. 2911282Seric ** 2921282Seric ** Returns: 2931282Seric ** zero -- command executed ok. 2941282Seric ** else -- error status. 2951282Seric ** 2961282Seric ** Side Effects: 2971282Seric ** none. 2981282Seric */ 299157Seric 3001316Seric command(argv, forkflag, editflag, arg0) 301200Seric char **argv; 302201Seric bool forkflag; 3031316Seric bool editflag; 3041316Seric char *arg0; 305200Seric { 306200Seric register struct sccsprog *cmd; 307200Seric register char *p; 308201Seric char buf[40]; 309262Seric extern struct sccsprog *lookup(); 3101316Seric char *nav[1000]; 3111316Seric char **np; 3121431Seric register char **ap; 313585Seric register int i; 3141431Seric register char *q; 315585Seric extern bool unedit(); 3161282Seric int rval = 0; 3171316Seric extern char *index(); 3181316Seric extern char *makefile(); 3191435Seric extern char *tail(); 320200Seric 321393Seric # ifdef DEBUG 322393Seric if (Debug) 323393Seric { 3241316Seric printf("command:\n\t\"%s\"\n", arg0); 3251316Seric for (np = argv; *np != NULL; np++) 3261316Seric printf("\t\"%s\"\n", *np); 327393Seric } 328393Seric # endif 329393Seric 330157Seric /* 3311316Seric ** Copy arguments. 3321316Seric ** Phase one -- from arg0 & if necessary argv[0]. 3331316Seric */ 3341316Seric 3351431Seric np = ap = &nav[1]; 3361316Seric for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) 3371316Seric { 3381316Seric *np++ = q; 3391316Seric while (*p == ' ') 3401316Seric p++; 3411316Seric while (*p != ' ' && *p != '\0' && *p != '/') 3421316Seric *q++ = *p++; 3431316Seric *q++ = '\0'; 3441316Seric } 3451316Seric *np = NULL; 3461431Seric if (*ap == NULL) 3471316Seric *np++ = *argv++; 3481316Seric 3491316Seric /* 350148Seric ** Look up command. 3511431Seric ** At this point, *ap is the command name. 352148Seric */ 353148Seric 3541431Seric cmd = lookup(*ap); 355262Seric if (cmd == NULL) 356148Seric { 3571431Seric usrerr("Unknown command \"%s\"", *ap); 3581282Seric return (EX_USAGE); 359148Seric } 360148Seric 361148Seric /* 3621316Seric ** Copy remaining arguments doing editing as appropriate. 3631316Seric */ 3641316Seric 3651316Seric for (; *argv != NULL; argv++) 3661316Seric { 3671316Seric p = *argv; 3681316Seric if (*p == '-') 3691316Seric { 3701316Seric if (p[1] == '\0' || !editflag || cmd->sccsklets == NULL || 3711316Seric index(cmd->sccsklets, p[1]) != NULL) 3721316Seric *np++ = p; 3731316Seric } 3741316Seric else 3751316Seric { 3761316Seric if (!bitset(NO_SDOT, cmd->sccsflags)) 3771316Seric p = makefile(p); 3781316Seric if (p != NULL) 3791316Seric *np++ = p; 3801316Seric } 3811316Seric } 3821316Seric *np = NULL; 3831316Seric 3841316Seric /* 385200Seric ** Interpret operation associated with this command. 386157Seric */ 387157Seric 388200Seric switch (cmd->sccsoper) 389200Seric { 3901431Seric case SHELL: /* call a shell file */ 3911431Seric *ap = cmd->sccspath; 3921431Seric *--ap = "sh"; 3931431Seric rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag); 3941431Seric break; 3951431Seric 396200Seric case PROG: /* call an sccs prog */ 3971431Seric rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); 398201Seric break; 399201Seric 400201Seric case CMACRO: /* command macro */ 401201Seric for (p = cmd->sccspath; *p != '\0'; p++) 402201Seric { 4031316Seric q = p; 4041316Seric while (*p != '\0' && *p != '/') 4051316Seric p++; 4061431Seric rval = command(&ap[1], *p != '\0', TRUE, q); 4071282Seric if (rval != 0) 4081282Seric break; 409201Seric } 4101282Seric break; 411157Seric 412226Seric case FIX: /* fix a delta */ 4131431Seric if (strncmp(ap[1], "-r", 2) != 0) 414226Seric { 4151205Seric usrerr("-r flag needed for fix command"); 4161282Seric rval = EX_USAGE; 417226Seric break; 418226Seric } 4191431Seric rval = command(&ap[1], TRUE, TRUE, "get -k"); 4201282Seric if (rval == 0) 4211431Seric rval = command(&ap[1], TRUE, TRUE, "rmdel"); 4221282Seric if (rval == 0) 4231431Seric rval = command(&ap[2], FALSE, TRUE, "get -e -g"); 4241282Seric break; 425226Seric 426261Seric case CLEAN: 4271282Seric rval = clean((int) cmd->sccspath); 428261Seric break; 429261Seric 430396Seric case UNEDIT: 4311431Seric for (argv = np = &ap[1]; *argv != NULL; argv++) 432585Seric { 4331316Seric if (unedit(*argv)) 4341316Seric *np++ = *argv; 435585Seric } 4361316Seric *np = NULL; 437585Seric if (i > 0) 4381431Seric rval = command(&ap[1], FALSE, FALSE, "get"); 439396Seric break; 440396Seric 4411433Seric case DIFFS: /* diff between s-file & edit file */ 4421433Seric /* find the end of the flag arguments */ 4431433Seric for (np = &ap[1]; *np != NULL && **np == '-'; np++) 4441433Seric continue; 4451433Seric argv = np; 4461433Seric 4471433Seric /* for each file, do the diff */ 4481433Seric while (*np != NULL) 4491433Seric { 4501433Seric *argv = *np++; 4511433Seric p = *np; 4521433Seric *np = NULL; 4531435Seric i = dodiff(ap, tail(*argv)); 4541433Seric if (rval == 0) 4551433Seric rval = i; 4561433Seric *np = p; 4571433Seric } 4581433Seric break; 4591433Seric 460200Seric default: 4611205Seric syserr("oper %d", cmd->sccsoper); 462200Seric exit(EX_SOFTWARE); 463200Seric } 4641282Seric # ifdef DEBUG 4651282Seric if (Debug) 4661282Seric printf("command: rval=%d\n", rval); 4671282Seric # endif 4681282Seric return (rval); 469200Seric } 4701432Seric 4711432Seric /* 472262Seric ** LOOKUP -- look up an SCCS command name. 473262Seric ** 474262Seric ** Parameters: 475262Seric ** name -- the name of the command to look up. 476262Seric ** 477262Seric ** Returns: 478262Seric ** ptr to command descriptor for this command. 479262Seric ** NULL if no such entry. 480262Seric ** 481262Seric ** Side Effects: 482262Seric ** none. 483262Seric */ 484200Seric 485262Seric struct sccsprog * 486262Seric lookup(name) 487262Seric char *name; 488262Seric { 489262Seric register struct sccsprog *cmd; 490226Seric 491262Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 492262Seric { 493262Seric if (strcmp(cmd->sccsname, name) == 0) 494262Seric return (cmd); 495262Seric } 496262Seric return (NULL); 497262Seric } 4981432Seric 4991432Seric /* 5001282Seric ** CALLPROG -- call a program 5011282Seric ** 5021316Seric ** Used to call the SCCS programs. 5031282Seric ** 5041282Seric ** Parameters: 5051282Seric ** progpath -- pathname of the program to call. 5061282Seric ** flags -- status flags from the command descriptors. 5071282Seric ** argv -- an argument vector to pass to the program. 5081282Seric ** forkflag -- if true, fork before calling, else just 5091282Seric ** exec. 5101282Seric ** 5111282Seric ** Returns: 5121282Seric ** The exit status of the program. 5131282Seric ** Nothing if forkflag == FALSE. 5141282Seric ** 5151282Seric ** Side Effects: 5161282Seric ** Can exit if forkflag == FALSE. 5171282Seric */ 518226Seric 519200Seric callprog(progpath, flags, argv, forkflag) 520200Seric char *progpath; 521200Seric short flags; 522200Seric char **argv; 523200Seric bool forkflag; 524200Seric { 525200Seric register int i; 526201Seric auto int st; 527200Seric 5281316Seric # ifdef DEBUG 5291316Seric if (Debug) 5301316Seric { 5311316Seric printf("callprog:\n"); 5321316Seric for (i = 0; argv[i] != NULL; i++) 5331316Seric printf("\t\"%s\"\n", argv[i]); 5341316Seric } 5351316Seric # endif 5361316Seric 537200Seric if (*argv == NULL) 538200Seric return (-1); 539200Seric 540157Seric /* 541226Seric ** Fork if appropriate. 542148Seric */ 543148Seric 544200Seric if (forkflag) 545200Seric { 546393Seric # ifdef DEBUG 547393Seric if (Debug) 548393Seric printf("Forking\n"); 549393Seric # endif 550200Seric i = fork(); 551200Seric if (i < 0) 552200Seric { 5531205Seric syserr("cannot fork"); 554200Seric exit(EX_OSERR); 555200Seric } 556200Seric else if (i > 0) 557201Seric { 558201Seric wait(&st); 5591282Seric if ((st & 0377) == 0) 5601282Seric st = (st >> 8) & 0377; 5611433Seric if (OutFile >= 0) 5621433Seric { 5631433Seric close(OutFile); 5641433Seric OutFile = -1; 5651433Seric } 566201Seric return (st); 567201Seric } 568200Seric } 5691433Seric else if (OutFile >= 0) 5701433Seric { 5711433Seric syserr("callprog: setting stdout w/o forking"); 5721433Seric exit(EX_SOFTWARE); 5731433Seric } 574200Seric 5751433Seric /* set protection as appropriate */ 576200Seric if (bitset(REALUSER, flags)) 577200Seric setuid(getuid()); 5781433Seric 5791433Seric /* change standard input & output if needed */ 5801433Seric if (OutFile >= 0) 5811433Seric { 5821433Seric close(1); 5831433Seric dup(OutFile); 5841433Seric close(OutFile); 5851433Seric } 586226Seric 5871433Seric /* call real SCCS program */ 588226Seric execv(progpath, argv); 5891205Seric syserr("cannot execute %s", progpath); 590148Seric exit(EX_UNAVAILABLE); 591148Seric } 5921432Seric 5931432Seric /* 594586Seric ** MAKEFILE -- make filename of SCCS file 595586Seric ** 596586Seric ** If the name passed is already the name of an SCCS file, 597586Seric ** just return it. Otherwise, munge the name into the name 598586Seric ** of the actual SCCS file. 599586Seric ** 600586Seric ** There are cases when it is not clear what you want to 601586Seric ** do. For example, if SccsPath is an absolute pathname 602586Seric ** and the name given is also an absolute pathname, we go 603586Seric ** for SccsPath (& only use the last component of the name 604586Seric ** passed) -- this is important for security reasons (if 605586Seric ** sccs is being used as a setuid front end), but not 606586Seric ** particularly intuitive. 607586Seric ** 608586Seric ** Parameters: 609586Seric ** name -- the file name to be munged. 610586Seric ** 611586Seric ** Returns: 612586Seric ** The pathname of the sccs file. 613586Seric ** NULL on error. 614586Seric ** 615586Seric ** Side Effects: 616586Seric ** none. 617586Seric */ 618148Seric 619148Seric char * 620148Seric makefile(name) 621148Seric char *name; 622148Seric { 623148Seric register char *p; 624148Seric register char c; 625148Seric char buf[512]; 626588Seric struct stat stbuf; 627148Seric extern char *malloc(); 628586Seric extern char *rindex(); 629588Seric extern bool safepath(); 630587Seric extern bool isdir(); 631587Seric register char *q; 632148Seric 633586Seric p = rindex(name, '/'); 634586Seric if (p == NULL) 635586Seric p = name; 636586Seric else 637586Seric p++; 638586Seric 639148Seric /* 640588Seric ** Check to see that the path is "safe", i.e., that we 641588Seric ** are not letting some nasty person use the setuid part 642588Seric ** of this program to look at or munge some presumably 643588Seric ** hidden files. 644148Seric */ 645148Seric 646588Seric if (SccsDir[0] == '/' && !safepath(name)) 647588Seric return (NULL); 648586Seric 649586Seric /* 650588Seric ** Create the base pathname. 651586Seric */ 652586Seric 653588Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) 654148Seric { 655588Seric strcpy(buf, SccsDir); 656586Seric strcat(buf, "/"); 657586Seric } 658586Seric else 659586Seric strcpy(buf, ""); 660587Seric strncat(buf, name, p - name); 661587Seric q = &buf[strlen(buf)]; 662587Seric strcpy(q, p); 663587Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 664586Seric { 665588Seric strcpy(q, SccsPath); 666588Seric strcat(buf, "/s."); 667586Seric strcat(buf, p); 668586Seric } 669148Seric 670588Seric if (strcmp(buf, name) == 0) 671588Seric p = name; 672588Seric else 673148Seric { 674588Seric p = malloc(strlen(buf) + 1); 675588Seric if (p == NULL) 676588Seric { 677588Seric perror("Sccs: no mem"); 678588Seric exit(EX_OSERR); 679588Seric } 680588Seric strcpy(p, buf); 681148Seric } 682148Seric return (p); 683148Seric } 6841432Seric 6851432Seric /* 686587Seric ** ISDIR -- return true if the argument is a directory. 687587Seric ** 688587Seric ** Parameters: 689587Seric ** name -- the pathname of the file to check. 690587Seric ** 691587Seric ** Returns: 692587Seric ** TRUE if 'name' is a directory, FALSE otherwise. 693587Seric ** 694587Seric ** Side Effects: 695587Seric ** none. 696587Seric */ 697587Seric 698587Seric bool 699587Seric isdir(name) 700587Seric char *name; 701587Seric { 702587Seric struct stat stbuf; 703587Seric 704587Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 705587Seric } 7061432Seric 7071432Seric /* 708586Seric ** SAFEPATH -- determine whether a pathname is "safe" 709586Seric ** 710586Seric ** "Safe" pathnames only allow you to get deeper into the 711586Seric ** directory structure, i.e., full pathnames and ".." are 712586Seric ** not allowed. 713586Seric ** 714586Seric ** Parameters: 715586Seric ** p -- the name to check. 716586Seric ** 717586Seric ** Returns: 718586Seric ** TRUE -- if the path is safe. 719586Seric ** FALSE -- if the path is not safe. 720586Seric ** 721586Seric ** Side Effects: 722586Seric ** Prints a message if the path is not safe. 723586Seric */ 724586Seric 725586Seric bool 726586Seric safepath(p) 727586Seric register char *p; 728586Seric { 729586Seric extern char *index(); 730586Seric 731586Seric if (*p != '/') 732586Seric { 733586Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 734586Seric { 735586Seric p = index(p, '/'); 736586Seric if (p == NULL) 737586Seric return (TRUE); 738586Seric p++; 739586Seric } 740586Seric } 741586Seric 742586Seric printf("You may not use full pathnames or \"..\"\n"); 743586Seric return (FALSE); 744586Seric } 7451432Seric 7461432Seric /* 747261Seric ** CLEAN -- clean out recreatable files 748261Seric ** 749261Seric ** Any file for which an "s." file exists but no "p." file 750261Seric ** exists in the current directory is purged. 751261Seric ** 752261Seric ** Parameters: 753819Seric ** tells whether this came from a "clean", "info", or 754819Seric ** "check" command. 755261Seric ** 756261Seric ** Returns: 757261Seric ** none. 758261Seric ** 759261Seric ** Side Effects: 760819Seric ** Removes files in the current directory. 761819Seric ** Prints information regarding files being edited. 762819Seric ** Exits if a "check" command. 763261Seric */ 764261Seric 765819Seric clean(mode) 766819Seric int mode; 767261Seric { 768261Seric struct direct dir; 769261Seric struct stat stbuf; 770261Seric char buf[100]; 771394Seric char pline[120]; 772346Seric register FILE *dirfd; 773346Seric register char *basefile; 774351Seric bool gotedit; 775394Seric FILE *pfp; 776261Seric 7771207Seric strcpy(buf, SccsDir); 7781207Seric if (buf[0] != '\0') 7791207Seric strcat(buf, "/"); 7801207Seric strcat(buf, SccsPath); 7811207Seric dirfd = fopen(buf, "r"); 782261Seric if (dirfd == NULL) 783261Seric { 7841207Seric usrerr("cannot open %s", buf); 7851282Seric return (EX_NOINPUT); 786261Seric } 787261Seric 788261Seric /* 789261Seric ** Scan the SCCS directory looking for s. files. 790261Seric */ 791261Seric 792351Seric gotedit = FALSE; 793261Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 794261Seric { 795568Seric if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) 796261Seric continue; 797261Seric 798261Seric /* got an s. file -- see if the p. file exists */ 7991207Seric strcpy(buf, SccsDir); 8001207Seric if (buf[0] != '\0') 8011207Seric strcat(buf, "/"); 8021207Seric strcat(buf, SccsPath); 803261Seric strcat(buf, "/p."); 804346Seric basefile = &buf[strlen(buf)]; 805568Seric strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 806346Seric basefile[sizeof dir.d_name - 2] = '\0'; 807394Seric pfp = fopen(buf, "r"); 808394Seric if (pfp != NULL) 809346Seric { 810394Seric while (fgets(pline, sizeof pline, pfp) != NULL) 811416Seric printf("%12s: being edited: %s", basefile, pline); 812394Seric fclose(pfp); 813351Seric gotedit = TRUE; 814261Seric continue; 815346Seric } 816261Seric 817261Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 818819Seric if (mode == CLEANC) 819346Seric { 820568Seric strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); 821346Seric buf[sizeof dir.d_name - 2] = '\0'; 822346Seric unlink(buf); 823346Seric } 824261Seric } 825261Seric 826261Seric fclose(dirfd); 827819Seric if (!gotedit && mode == INFOC) 828416Seric printf("Nothing being edited\n"); 829819Seric if (mode == CHECKC) 830819Seric exit(gotedit); 8311282Seric return (EX_OK); 832261Seric } 8331432Seric 8341432Seric /* 835396Seric ** UNEDIT -- unedit a file 836396Seric ** 837396Seric ** Checks to see that the current user is actually editting 838396Seric ** the file and arranges that s/he is not editting it. 839396Seric ** 840396Seric ** Parameters: 841416Seric ** fn -- the name of the file to be unedited. 842396Seric ** 843396Seric ** Returns: 844585Seric ** TRUE -- if the file was successfully unedited. 845585Seric ** FALSE -- if the file was not unedited for some 846585Seric ** reason. 847396Seric ** 848396Seric ** Side Effects: 849396Seric ** fn is removed 850396Seric ** entries are removed from pfile. 851396Seric */ 852396Seric 853585Seric bool 854396Seric unedit(fn) 855396Seric char *fn; 856396Seric { 857396Seric register FILE *pfp; 858396Seric char *pfn; 859396Seric static char tfn[] = "/tmp/sccsXXXXX"; 860396Seric FILE *tfp; 861396Seric register char *p; 862396Seric register char *q; 863396Seric bool delete = FALSE; 864396Seric bool others = FALSE; 865396Seric char *myname; 866396Seric extern char *getlogin(); 867396Seric struct pfile *pent; 868396Seric extern struct pfile *getpfile(); 869396Seric char buf[120]; 8701316Seric extern char *makefile(); 871828Seric # ifdef UIDUSER 872828Seric struct passwd *pw; 873828Seric extern struct passwd *getpwuid(); 874828Seric # endif UIDUSER 875396Seric 876396Seric /* make "s." filename & find the trailing component */ 877396Seric pfn = makefile(fn); 878586Seric if (pfn == NULL) 879586Seric return (FALSE); 880586Seric q = rindex(pfn, '/'); 881586Seric if (q == NULL) 882586Seric q = &pfn[-1]; 883586Seric if (q[1] != 's' || q[2] != '.') 884396Seric { 8851205Seric usrerr("bad file name \"%s\"", fn); 886585Seric return (FALSE); 887396Seric } 888396Seric 889396Seric /* turn "s." into "p." */ 890396Seric *++q = 'p'; 891396Seric 892396Seric pfp = fopen(pfn, "r"); 893396Seric if (pfp == NULL) 894396Seric { 895416Seric printf("%12s: not being edited\n", fn); 896585Seric return (FALSE); 897396Seric } 898396Seric 899396Seric /* 900396Seric ** Copy p-file to temp file, doing deletions as needed. 901396Seric */ 902396Seric 903396Seric mktemp(tfn); 904396Seric tfp = fopen(tfn, "w"); 905396Seric if (tfp == NULL) 906396Seric { 9071205Seric usrerr("cannot create \"%s\"", tfn); 908396Seric exit(EX_OSERR); 909396Seric } 910396Seric 911828Seric # ifdef UIDUSER 912828Seric pw = getpwuid(getuid()); 913828Seric if (pw == NULL) 914828Seric { 9151205Seric syserr("who are you? (uid=%d)", getuid()); 916828Seric exit(EX_OSERR); 917828Seric } 918828Seric myname = pw->pw_name; 919828Seric # else 920396Seric myname = getlogin(); 921828Seric # endif UIDUSER 922396Seric while ((pent = getpfile(pfp)) != NULL) 923396Seric { 924396Seric if (strcmp(pent->p_user, myname) == 0) 925396Seric { 926396Seric /* a match */ 927396Seric delete++; 928396Seric } 929396Seric else 930396Seric { 931396Seric fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, 932396Seric pent->p_nsid, pent->p_user, pent->p_date, 933396Seric pent->p_time); 934396Seric others++; 935396Seric } 936396Seric } 937396Seric 938396Seric /* do final cleanup */ 939396Seric if (others) 940396Seric { 941396Seric if (freopen(tfn, "r", tfp) == NULL) 942396Seric { 9431205Seric syserr("cannot reopen \"%s\"", tfn); 944396Seric exit(EX_OSERR); 945396Seric } 946396Seric if (freopen(pfn, "w", pfp) == NULL) 947396Seric { 9481205Seric usrerr("cannot create \"%s\"", pfn); 949585Seric return (FALSE); 950396Seric } 951396Seric while (fgets(buf, sizeof buf, tfp) != NULL) 952396Seric fputs(buf, pfp); 953396Seric } 954396Seric else 955396Seric { 956396Seric unlink(pfn); 957396Seric } 958396Seric fclose(tfp); 959396Seric fclose(pfp); 960396Seric unlink(tfn); 961396Seric 962396Seric if (delete) 963396Seric { 9641435Seric unlink(tail(fn)); 9651435Seric printf("%12s: removed\n", tail(fn)); 966585Seric return (TRUE); 967396Seric } 968396Seric else 969396Seric { 970416Seric printf("%12s: not being edited by you\n", fn); 971585Seric return (FALSE); 972396Seric } 973396Seric } 9741432Seric 9751432Seric /* 9761433Seric ** DODIFF -- diff an s-file against a g-file 9771433Seric ** 9781433Seric ** Parameters: 9791433Seric ** getv -- argv for the 'get' command. 9801433Seric ** gfile -- name of the g-file to diff against. 9811433Seric ** 9821433Seric ** Returns: 9831433Seric ** Result of get. 9841433Seric ** 9851433Seric ** Side Effects: 9861433Seric ** none. 9871433Seric */ 9881433Seric 9891433Seric dodiff(getv, gfile) 9901433Seric char **getv; 9911433Seric char *gfile; 9921433Seric { 9931433Seric int pipev[2]; 9941433Seric int rval; 9951433Seric register int i; 9961433Seric register int pid; 9971433Seric auto int st; 9981433Seric extern int errno; 9991433Seric int (*osig)(); 10001433Seric 10011433Seric if (pipe(pipev) < 0) 10021433Seric { 10031433Seric syserr("dodiff: pipe failed"); 10041433Seric exit(EX_OSERR); 10051433Seric } 10061433Seric if ((pid = fork()) < 0) 10071433Seric { 10081433Seric syserr("dodiff: fork failed"); 10091433Seric exit(EX_OSERR); 10101433Seric } 10111433Seric else if (pid > 0) 10121433Seric { 10131433Seric /* in parent; run get */ 10141433Seric OutFile = pipev[1]; 10151433Seric close(pipev[0]); 10161433Seric rval = command(&getv[1], TRUE, FALSE, "get -s -k -p"); 10171433Seric osig = signal(SIGINT, SIG_IGN); 10181433Seric while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR) 10191433Seric errno = 0; 10201433Seric signal(SIGINT, osig); 10211433Seric /* ignore result of diff */ 10221433Seric } 10231433Seric else 10241433Seric { 10251433Seric /* in child, run diff */ 10261433Seric if (close(pipev[1]) < 0 || close(0) < 0 || 10271433Seric dup(pipev[0]) != 0 || close(pipev[0]) < 0) 10281433Seric { 10291433Seric syserr("dodiff: magic failed"); 10301433Seric exit(EX_OSERR); 10311433Seric } 10321433Seric execl(PROGPATH(bdiff), "bdiff", "-", gfile, NULL); 10331433Seric # ifndef V6 10341433Seric execlp("bdiff", "bdiff", "-", gfile, NULL); 10351433Seric execlp("diff", "diff", "-", gfile, NULL); 10361433Seric # endif NOT V6 10371433Seric syserr("bdiff: cannot execute"); 10381433Seric exit(EX_OSERR); 10391433Seric } 10401433Seric return (rval); 10411433Seric } 10421433Seric 10431433Seric /* 10441435Seric ** TAIL -- return tail of filename. 10451435Seric ** 10461435Seric ** Parameters: 10471435Seric ** fn -- the filename. 10481435Seric ** 10491435Seric ** Returns: 10501435Seric ** a pointer to the tail of the filename; e.g., given 10511435Seric ** "cmd/ls.c", "ls.c" is returned. 10521435Seric ** 10531435Seric ** Side Effects: 10541435Seric ** none. 10551435Seric */ 10561435Seric 10571435Seric char * 10581435Seric tail(fn) 10591435Seric register char *fn; 10601435Seric { 10611435Seric register char *p; 10621435Seric 10631435Seric for (p = fn; *p != 0; p++) 10641435Seric if (*p == '/' && p[1] != '\0' && p[1] != '/') 10651435Seric fn = &p[1]; 10661435Seric return (fn); 10671435Seric } 10681435Seric 10691435Seric /* 1070396Seric ** GETPFILE -- get an entry from the p-file 1071396Seric ** 1072396Seric ** Parameters: 1073396Seric ** pfp -- p-file file pointer 1074396Seric ** 1075396Seric ** Returns: 1076396Seric ** pointer to p-file struct for next entry 1077396Seric ** NULL on EOF or error 1078396Seric ** 1079396Seric ** Side Effects: 1080396Seric ** Each call wipes out results of previous call. 1081396Seric */ 1082396Seric 1083396Seric struct pfile * 1084396Seric getpfile(pfp) 1085396Seric FILE *pfp; 1086396Seric { 1087396Seric static struct pfile ent; 1088396Seric static char buf[120]; 1089396Seric register char *p; 1090396Seric extern char *nextfield(); 1091396Seric 1092396Seric if (fgets(buf, sizeof buf, pfp) == NULL) 1093396Seric return (NULL); 1094396Seric 1095396Seric ent.p_osid = p = buf; 1096396Seric ent.p_nsid = p = nextfield(p); 1097396Seric ent.p_user = p = nextfield(p); 1098396Seric ent.p_date = p = nextfield(p); 1099396Seric ent.p_time = p = nextfield(p); 1100396Seric if (p == NULL || nextfield(p) != NULL) 1101396Seric return (NULL); 1102396Seric 1103396Seric return (&ent); 1104396Seric } 1105396Seric 1106396Seric 1107396Seric char * 1108396Seric nextfield(p) 1109396Seric register char *p; 1110396Seric { 1111396Seric if (p == NULL || *p == '\0') 1112396Seric return (NULL); 1113396Seric while (*p != ' ' && *p != '\n' && *p != '\0') 1114396Seric p++; 1115396Seric if (*p == '\n' || *p == '\0') 1116396Seric { 1117396Seric *p = '\0'; 1118396Seric return (NULL); 1119396Seric } 1120396Seric *p++ = '\0'; 1121396Seric return (p); 1122396Seric } 11231432Seric 11241432Seric /* 11251205Seric ** USRERR -- issue user-level error 11261205Seric ** 11271205Seric ** Parameters: 11281205Seric ** f -- format string. 11291205Seric ** p1-p3 -- parameters to a printf. 11301205Seric ** 11311205Seric ** Returns: 11321205Seric ** -1 11331205Seric ** 11341205Seric ** Side Effects: 11351205Seric ** none. 11361205Seric */ 11371205Seric 11381205Seric usrerr(f, p1, p2, p3) 11391205Seric char *f; 11401205Seric { 11411205Seric fprintf(stderr, "\n%s: ", MyName); 11421205Seric fprintf(stderr, f, p1, p2, p3); 11431205Seric fprintf(stderr, "\n"); 11441205Seric 11451205Seric return (-1); 11461205Seric } 11471432Seric 11481432Seric /* 11491205Seric ** SYSERR -- print system-generated error. 11501205Seric ** 11511205Seric ** Parameters: 11521205Seric ** f -- format string to a printf. 11531205Seric ** p1, p2, p3 -- parameters to f. 11541205Seric ** 11551205Seric ** Returns: 11561205Seric ** never. 11571205Seric ** 11581205Seric ** Side Effects: 11591205Seric ** none. 11601205Seric */ 11611205Seric 11621205Seric syserr(f, p1, p2, p3) 11631205Seric char *f; 11641205Seric { 11651205Seric extern int errno; 11661205Seric 11671205Seric fprintf(stderr, "\n%s SYSERR: ", MyName); 11681205Seric fprintf(stderr, f, p1, p2, p3); 11691205Seric fprintf(stderr, "\n"); 11701205Seric if (errno == 0) 11711205Seric exit(EX_SOFTWARE); 11721205Seric else 11731205Seric { 11741205Seric perror(0); 11751205Seric exit(EX_OSERR); 11761205Seric } 11771205Seric } 1178