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. 821437Seric ** MYNAME -- the title this program should print when it 831437Seric ** gives error messages. 84828Seric ** 85828Seric ** Compilation Instructions: 86828Seric ** cc -O -n -s sccs.c 871437Seric ** The flags listed above can be -D defined to simplify 881437Seric ** recompilation for variant versions. 89828Seric ** 90828Seric ** Author: 91828Seric ** Eric Allman, UCB/INGRES 921270Seric ** Copyright 1980 Regents of the University of California 93828Seric */ 94155Seric 95*1618Seric static char SccsId[] = "@(#)sccs.c 1.43 10/25/80"; 961432Seric 971270Seric /******************* Configuration Information ********************/ 981270Seric 991437Seric /* special defines for local berkeley systems */ 1001437Seric # include <whoami.h> 1011437Seric 102828Seric # ifdef CSVAX 103828Seric # define UIDUSER 1041270Seric # define PROGPATH(name) "/usr/local/name" 1051270Seric # endif CSVAX 106828Seric 107*1618Seric # ifdef INGVAX 108*1618Seric # define PROGPATH(name) "/usr/local/name" 109*1618Seric # endif INGVAX 110*1618Seric 1111437Seric /* end of berkeley systems defines */ 1121437Seric 1131437Seric # ifndef SCCSPATH 1141432Seric # define SCCSPATH "SCCS" /* pathname in which to find s-files */ 1151437Seric # endif NOT SCCSPATH 116828Seric 1171437Seric # ifndef MYNAME 1181437Seric # define MYNAME "sccs" /* name used for printing errors */ 1191437Seric # endif NOT MYNAME 1201270Seric 1211432Seric # ifndef PROGPATH 1221432Seric # define PROGPATH(name) "/usr/sccs/name" /* place to find binaries */ 1231432Seric # endif PROGPATH 1241432Seric 1251270Seric /**************** End of Configuration Information ****************/ 1261432Seric 127157Seric typedef char bool; 128200Seric # define TRUE 1 129200Seric # define FALSE 0 130157Seric 1311438Seric # define bitset(bit, word) ((bool) ((bit) & (word))) 1321438Seric 133828Seric # ifdef UIDUSER 134828Seric # include <pwd.h> 135828Seric # endif UIDUSER 136828Seric 137148Seric struct sccsprog 138148Seric { 139148Seric char *sccsname; /* name of SCCS routine */ 140200Seric short sccsoper; /* opcode, see below */ 141200Seric short sccsflags; /* flags, see below */ 1421316Seric char *sccsklets; /* valid key-letters on macros */ 143148Seric char *sccspath; /* pathname of binary implementing */ 144148Seric }; 145148Seric 146200Seric /* values for sccsoper */ 147200Seric # define PROG 0 /* call a program */ 148201Seric # define CMACRO 1 /* command substitution macro */ 149226Seric # define FIX 2 /* fix a delta */ 150261Seric # define CLEAN 3 /* clean out recreatable files */ 151396Seric # define UNEDIT 4 /* unedit a file */ 1521431Seric # define SHELL 5 /* call a shell file (like PROG) */ 1531433Seric # define DIFFS 6 /* diff between sccs & file out */ 154200Seric 155157Seric /* bits for sccsflags */ 156200Seric # define NO_SDOT 0001 /* no s. on front of args */ 157200Seric # define REALUSER 0002 /* protected (e.g., admin) */ 158148Seric 159819Seric /* modes for the "clean", "info", "check" ops */ 160819Seric # define CLEANC 0 /* clean command */ 161819Seric # define INFOC 1 /* info command */ 162819Seric # define CHECKC 2 /* check command */ 163819Seric 1641432Seric /* 1651432Seric ** Description of commands known to this program. 1661432Seric ** First argument puts the command into a class. Second arg is 1671432Seric ** info regarding treatment of this command. Third arg is a 1681432Seric ** list of flags this command accepts from macros, etc. Fourth 1691432Seric ** arg is the pathname of the implementing program, or the 1701432Seric ** macro definition, or the arg to a sub-algorithm. 1711432Seric */ 172202Seric 173148Seric struct sccsprog SccsProg[] = 174148Seric { 1751316Seric "admin", PROG, REALUSER, "", PROGPATH(admin), 1761316Seric "chghist", PROG, 0, "", PROGPATH(rmdel), 1771316Seric "comb", PROG, 0, "", PROGPATH(comb), 1781316Seric "delta", PROG, 0, "mysrp", PROGPATH(delta), 1791317Seric "get", PROG, 0, "ixbeskcl", PROGPATH(get), 1801316Seric "help", PROG, NO_SDOT, "", PROGPATH(help), 1811316Seric "prt", PROG, 0, "", PROGPATH(prt), 1821505Seric "rmdel", PROG, REALUSER, "r", PROGPATH(rmdel), 1831316Seric "what", PROG, NO_SDOT, "", PROGPATH(what), 1841431Seric "sccsdiff", SHELL, REALUSER, "", PROGPATH(sccsdiff), 1851317Seric "edit", CMACRO, NO_SDOT, "ixbscl", "get -e", 1861316Seric "delget", CMACRO, NO_SDOT, "", "delta/get -t", 1871316Seric "deledit", CMACRO, NO_SDOT, "", "delta/get -e -t", 1881316Seric "fix", FIX, NO_SDOT, "", NULL, 1891316Seric "clean", CLEAN, REALUSER, "", (char *) CLEANC, 1901316Seric "info", CLEAN, REALUSER, "", (char *) INFOC, 1911316Seric "check", CLEAN, REALUSER, "", (char *) CHECKC, 1921316Seric "unedit", UNEDIT, NO_SDOT, "", NULL, 1931433Seric "diffs", DIFFS, NO_SDOT|REALUSER, "", NULL, 1941316Seric NULL, -1, 0, "", NULL 195148Seric }; 196148Seric 1971432Seric /* one line from a p-file */ 198396Seric struct pfile 199396Seric { 200396Seric char *p_osid; /* old SID */ 201396Seric char *p_nsid; /* new SID */ 202396Seric char *p_user; /* user who did edit */ 203396Seric char *p_date; /* date of get */ 204396Seric char *p_time; /* time of get */ 205396Seric }; 206396Seric 2071270Seric char *SccsPath = SCCSPATH; /* pathname of SCCS files */ 2081270Seric # ifdef SCCSDIR 2091270Seric char *SccsDir = SCCSDIR; /* directory to begin search from */ 2101205Seric # else 2111270Seric char *SccsDir = ""; 2121205Seric # endif 2131437Seric char MyName[] = MYNAME; /* name used in messages */ 2141433Seric int OutFile = -1; /* override output file for commands */ 215157Seric bool RealUser; /* if set, running as real user */ 216393Seric # ifdef DEBUG 217393Seric bool Debug; /* turn on tracing */ 218393Seric # endif 2191432Seric 220148Seric main(argc, argv) 221148Seric int argc; 222148Seric char **argv; 223148Seric { 224148Seric register char *p; 225262Seric extern struct sccsprog *lookup(); 2261282Seric register int i; 227148Seric 228148Seric /* 229148Seric ** Detect and decode flags intended for this program. 230148Seric */ 231148Seric 232200Seric if (argc < 2) 233148Seric { 2341205Seric fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); 235200Seric exit(EX_USAGE); 236200Seric } 237200Seric argv[argc] = NULL; 238200Seric 239262Seric if (lookup(argv[0]) == NULL) 240200Seric { 241262Seric while ((p = *++argv) != NULL) 242148Seric { 243262Seric if (*p != '-') 244262Seric break; 245262Seric switch (*++p) 246262Seric { 247262Seric case 'r': /* run as real user */ 248262Seric setuid(getuid()); 249262Seric RealUser++; 250262Seric break; 251148Seric 2521270Seric # ifndef SCCSDIR 253262Seric case 'p': /* path of sccs files */ 254262Seric SccsPath = ++p; 255262Seric break; 256148Seric 257588Seric case 'd': /* directory to search from */ 258588Seric SccsDir = ++p; 259588Seric break; 2601205Seric # endif 261588Seric 262393Seric # ifdef DEBUG 263393Seric case 'T': /* trace */ 264393Seric Debug++; 265393Seric break; 266393Seric # endif 267393Seric 268262Seric default: 2691205Seric usrerr("unknown option -%s", p); 270262Seric break; 271262Seric } 272148Seric } 273262Seric if (SccsPath[0] == '\0') 274262Seric SccsPath = "."; 275148Seric } 276148Seric 2771316Seric i = command(argv, FALSE, FALSE, ""); 2781282Seric exit(i); 279200Seric } 2801432Seric 2811432Seric /* 2821282Seric ** COMMAND -- look up and perform a command 2831282Seric ** 2841282Seric ** This routine is the guts of this program. Given an 2851282Seric ** argument vector, it looks up the "command" (argv[0]) 2861282Seric ** in the configuration table and does the necessary stuff. 2871282Seric ** 2881282Seric ** Parameters: 2891282Seric ** argv -- an argument vector to process. 2901282Seric ** forkflag -- if set, fork before executing the command. 2911316Seric ** editflag -- if set, only include flags listed in the 2921316Seric ** sccsklets field of the command descriptor. 2931316Seric ** arg0 -- a space-seperated list of arguments to insert 2941316Seric ** before argv. 2951282Seric ** 2961282Seric ** Returns: 2971282Seric ** zero -- command executed ok. 2981282Seric ** else -- error status. 2991282Seric ** 3001282Seric ** Side Effects: 3011282Seric ** none. 3021282Seric */ 303157Seric 3041316Seric command(argv, forkflag, editflag, arg0) 305200Seric char **argv; 306201Seric bool forkflag; 3071316Seric bool editflag; 3081316Seric char *arg0; 309200Seric { 310200Seric register struct sccsprog *cmd; 311200Seric register char *p; 312201Seric char buf[40]; 313262Seric extern struct sccsprog *lookup(); 3141316Seric char *nav[1000]; 3151316Seric char **np; 3161431Seric register char **ap; 317585Seric register int i; 3181431Seric register char *q; 319585Seric extern bool unedit(); 3201282Seric int rval = 0; 3211316Seric extern char *index(); 3221316Seric extern char *makefile(); 3231435Seric extern char *tail(); 324200Seric 325393Seric # ifdef DEBUG 326393Seric if (Debug) 327393Seric { 3281316Seric printf("command:\n\t\"%s\"\n", arg0); 3291316Seric for (np = argv; *np != NULL; np++) 3301316Seric printf("\t\"%s\"\n", *np); 331393Seric } 332393Seric # endif 333393Seric 334157Seric /* 3351316Seric ** Copy arguments. 3361438Seric ** Copy from arg0 & if necessary at most one arg 3371438Seric ** from argv[0]. 3381316Seric */ 3391316Seric 3401431Seric np = ap = &nav[1]; 3411316Seric for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) 3421316Seric { 3431316Seric *np++ = q; 3441316Seric while (*p == ' ') 3451316Seric p++; 3461316Seric while (*p != ' ' && *p != '\0' && *p != '/') 3471316Seric *q++ = *p++; 3481316Seric *q++ = '\0'; 3491316Seric } 3501316Seric *np = NULL; 3511431Seric if (*ap == NULL) 3521316Seric *np++ = *argv++; 3531316Seric 3541316Seric /* 355148Seric ** Look up command. 3561431Seric ** At this point, *ap is the command name. 357148Seric */ 358148Seric 3591431Seric cmd = lookup(*ap); 360262Seric if (cmd == NULL) 361148Seric { 3621431Seric usrerr("Unknown command \"%s\"", *ap); 3631282Seric return (EX_USAGE); 364148Seric } 365148Seric 366148Seric /* 3671316Seric ** Copy remaining arguments doing editing as appropriate. 3681316Seric */ 3691316Seric 3701316Seric for (; *argv != NULL; argv++) 3711316Seric { 3721316Seric p = *argv; 3731316Seric if (*p == '-') 3741316Seric { 3751316Seric if (p[1] == '\0' || !editflag || cmd->sccsklets == NULL || 3761316Seric index(cmd->sccsklets, p[1]) != NULL) 3771316Seric *np++ = p; 3781316Seric } 3791316Seric else 3801316Seric { 3811316Seric if (!bitset(NO_SDOT, cmd->sccsflags)) 3821316Seric p = makefile(p); 3831316Seric if (p != NULL) 3841316Seric *np++ = p; 3851316Seric } 3861316Seric } 3871316Seric *np = NULL; 3881316Seric 3891316Seric /* 390200Seric ** Interpret operation associated with this command. 391157Seric */ 392157Seric 393200Seric switch (cmd->sccsoper) 394200Seric { 3951431Seric case SHELL: /* call a shell file */ 3961431Seric *ap = cmd->sccspath; 3971431Seric *--ap = "sh"; 3981431Seric rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag); 3991431Seric break; 4001431Seric 401200Seric case PROG: /* call an sccs prog */ 4021431Seric rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); 403201Seric break; 404201Seric 405201Seric case CMACRO: /* command macro */ 4061438Seric /* step through & execute each part of the macro */ 407201Seric for (p = cmd->sccspath; *p != '\0'; p++) 408201Seric { 4091316Seric q = p; 4101316Seric while (*p != '\0' && *p != '/') 4111316Seric p++; 4121431Seric rval = command(&ap[1], *p != '\0', TRUE, q); 4131282Seric if (rval != 0) 4141282Seric break; 415201Seric } 4161282Seric break; 417157Seric 418226Seric case FIX: /* fix a delta */ 4191431Seric if (strncmp(ap[1], "-r", 2) != 0) 420226Seric { 4211205Seric usrerr("-r flag needed for fix command"); 4221282Seric rval = EX_USAGE; 423226Seric break; 424226Seric } 4251438Seric 4261438Seric /* get the version with all changes */ 4271431Seric rval = command(&ap[1], TRUE, TRUE, "get -k"); 4281438Seric 4291438Seric /* now remove that version from the s-file */ 4301282Seric if (rval == 0) 4311431Seric rval = command(&ap[1], TRUE, TRUE, "rmdel"); 4321438Seric 4331438Seric /* and edit the old version (but don't clobber new vers) */ 4341282Seric if (rval == 0) 4351431Seric rval = command(&ap[2], FALSE, TRUE, "get -e -g"); 4361282Seric break; 437226Seric 438261Seric case CLEAN: 4391282Seric rval = clean((int) cmd->sccspath); 440261Seric break; 441261Seric 442396Seric case UNEDIT: 4431431Seric for (argv = np = &ap[1]; *argv != NULL; argv++) 444585Seric { 4451316Seric if (unedit(*argv)) 4461316Seric *np++ = *argv; 447585Seric } 4481316Seric *np = NULL; 4491438Seric 4501438Seric /* get all the files that we unedited successfully */ 451585Seric if (i > 0) 4521431Seric rval = command(&ap[1], FALSE, FALSE, "get"); 453396Seric break; 454396Seric 4551433Seric case DIFFS: /* diff between s-file & edit file */ 4561433Seric /* find the end of the flag arguments */ 4571433Seric for (np = &ap[1]; *np != NULL && **np == '-'; np++) 4581433Seric continue; 4591433Seric argv = np; 4601433Seric 4611433Seric /* for each file, do the diff */ 4621502Seric p = argv[1]; 4631433Seric while (*np != NULL) 4641433Seric { 4651438Seric /* messy, but we need a null terminated argv */ 4661433Seric *argv = *np++; 4671502Seric argv[1] = NULL; 4681435Seric i = dodiff(ap, tail(*argv)); 4691433Seric if (rval == 0) 4701433Seric rval = i; 4711502Seric argv[1] = p; 4721433Seric } 4731433Seric break; 4741433Seric 475200Seric default: 4761205Seric syserr("oper %d", cmd->sccsoper); 477200Seric exit(EX_SOFTWARE); 478200Seric } 4791282Seric # ifdef DEBUG 4801282Seric if (Debug) 4811282Seric printf("command: rval=%d\n", rval); 4821282Seric # endif 4831282Seric return (rval); 484200Seric } 4851432Seric 4861432Seric /* 487262Seric ** LOOKUP -- look up an SCCS command name. 488262Seric ** 489262Seric ** Parameters: 490262Seric ** name -- the name of the command to look up. 491262Seric ** 492262Seric ** Returns: 493262Seric ** ptr to command descriptor for this command. 494262Seric ** NULL if no such entry. 495262Seric ** 496262Seric ** Side Effects: 497262Seric ** none. 498262Seric */ 499200Seric 500262Seric struct sccsprog * 501262Seric lookup(name) 502262Seric char *name; 503262Seric { 504262Seric register struct sccsprog *cmd; 505226Seric 506262Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 507262Seric { 508262Seric if (strcmp(cmd->sccsname, name) == 0) 509262Seric return (cmd); 510262Seric } 511262Seric return (NULL); 512262Seric } 5131432Seric 5141432Seric /* 5151282Seric ** CALLPROG -- call a program 5161282Seric ** 5171316Seric ** Used to call the SCCS programs. 5181282Seric ** 5191282Seric ** Parameters: 5201282Seric ** progpath -- pathname of the program to call. 5211282Seric ** flags -- status flags from the command descriptors. 5221282Seric ** argv -- an argument vector to pass to the program. 5231282Seric ** forkflag -- if true, fork before calling, else just 5241282Seric ** exec. 5251282Seric ** 5261282Seric ** Returns: 5271282Seric ** The exit status of the program. 5281282Seric ** Nothing if forkflag == FALSE. 5291282Seric ** 5301282Seric ** Side Effects: 5311282Seric ** Can exit if forkflag == FALSE. 5321282Seric */ 533226Seric 534200Seric callprog(progpath, flags, argv, forkflag) 535200Seric char *progpath; 536200Seric short flags; 537200Seric char **argv; 538200Seric bool forkflag; 539200Seric { 540200Seric register int i; 541201Seric auto int st; 542200Seric 5431316Seric # ifdef DEBUG 5441316Seric if (Debug) 5451316Seric { 5461316Seric printf("callprog:\n"); 5471316Seric for (i = 0; argv[i] != NULL; i++) 5481316Seric printf("\t\"%s\"\n", argv[i]); 5491316Seric } 5501316Seric # endif 5511316Seric 552200Seric if (*argv == NULL) 553200Seric return (-1); 554200Seric 555157Seric /* 556226Seric ** Fork if appropriate. 557148Seric */ 558148Seric 559200Seric if (forkflag) 560200Seric { 561393Seric # ifdef DEBUG 562393Seric if (Debug) 563393Seric printf("Forking\n"); 564393Seric # endif 565200Seric i = fork(); 566200Seric if (i < 0) 567200Seric { 5681205Seric syserr("cannot fork"); 569200Seric exit(EX_OSERR); 570200Seric } 571200Seric else if (i > 0) 572201Seric { 573201Seric wait(&st); 5741282Seric if ((st & 0377) == 0) 5751282Seric st = (st >> 8) & 0377; 5761433Seric if (OutFile >= 0) 5771433Seric { 5781433Seric close(OutFile); 5791433Seric OutFile = -1; 5801433Seric } 581201Seric return (st); 582201Seric } 583200Seric } 5841433Seric else if (OutFile >= 0) 5851433Seric { 5861433Seric syserr("callprog: setting stdout w/o forking"); 5871433Seric exit(EX_SOFTWARE); 5881433Seric } 589200Seric 5901433Seric /* set protection as appropriate */ 591200Seric if (bitset(REALUSER, flags)) 592200Seric setuid(getuid()); 5931433Seric 5941433Seric /* change standard input & output if needed */ 5951433Seric if (OutFile >= 0) 5961433Seric { 5971433Seric close(1); 5981433Seric dup(OutFile); 5991433Seric close(OutFile); 6001433Seric } 601226Seric 6021433Seric /* call real SCCS program */ 603226Seric execv(progpath, argv); 6041205Seric syserr("cannot execute %s", progpath); 605148Seric exit(EX_UNAVAILABLE); 606148Seric } 6071432Seric 6081432Seric /* 609586Seric ** MAKEFILE -- make filename of SCCS file 610586Seric ** 611586Seric ** If the name passed is already the name of an SCCS file, 612586Seric ** just return it. Otherwise, munge the name into the name 613586Seric ** of the actual SCCS file. 614586Seric ** 615586Seric ** There are cases when it is not clear what you want to 616586Seric ** do. For example, if SccsPath is an absolute pathname 617586Seric ** and the name given is also an absolute pathname, we go 618586Seric ** for SccsPath (& only use the last component of the name 619586Seric ** passed) -- this is important for security reasons (if 620586Seric ** sccs is being used as a setuid front end), but not 621586Seric ** particularly intuitive. 622586Seric ** 623586Seric ** Parameters: 624586Seric ** name -- the file name to be munged. 625586Seric ** 626586Seric ** Returns: 627586Seric ** The pathname of the sccs file. 628586Seric ** NULL on error. 629586Seric ** 630586Seric ** Side Effects: 631586Seric ** none. 632586Seric */ 633148Seric 634148Seric char * 635148Seric makefile(name) 636148Seric char *name; 637148Seric { 638148Seric register char *p; 639148Seric register char c; 640148Seric char buf[512]; 641588Seric struct stat stbuf; 642148Seric extern char *malloc(); 643586Seric extern char *rindex(); 644588Seric extern bool safepath(); 645587Seric extern bool isdir(); 646587Seric register char *q; 647148Seric 648586Seric p = rindex(name, '/'); 649586Seric if (p == NULL) 650586Seric p = name; 651586Seric else 652586Seric p++; 653586Seric 654148Seric /* 655588Seric ** Check to see that the path is "safe", i.e., that we 656588Seric ** are not letting some nasty person use the setuid part 657588Seric ** of this program to look at or munge some presumably 658588Seric ** hidden files. 659148Seric */ 660148Seric 661588Seric if (SccsDir[0] == '/' && !safepath(name)) 662588Seric return (NULL); 663586Seric 664586Seric /* 665588Seric ** Create the base pathname. 666586Seric */ 667586Seric 6681438Seric /* first the directory part */ 669588Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) 670148Seric { 671588Seric strcpy(buf, SccsDir); 672586Seric strcat(buf, "/"); 673586Seric } 674586Seric else 675586Seric strcpy(buf, ""); 6761438Seric 6771438Seric /* then the head of the pathname */ 678587Seric strncat(buf, name, p - name); 679587Seric q = &buf[strlen(buf)]; 6801438Seric 6811438Seric /* now copy the final part of the name, in case useful */ 682587Seric strcpy(q, p); 6831438Seric 6841438Seric /* so is it useful? */ 685587Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 686586Seric { 6871438Seric /* sorry, no; copy the SCCS pathname & the "s." */ 688588Seric strcpy(q, SccsPath); 689588Seric strcat(buf, "/s."); 6901438Seric 6911438Seric /* and now the end of the name */ 692586Seric strcat(buf, p); 693586Seric } 694148Seric 6951438Seric /* if i haven't changed it, why did I do all this? */ 696588Seric if (strcmp(buf, name) == 0) 697588Seric p = name; 698588Seric else 699148Seric { 7001438Seric /* but if I have, squirrel it away */ 701588Seric p = malloc(strlen(buf) + 1); 702588Seric if (p == NULL) 703588Seric { 704588Seric perror("Sccs: no mem"); 705588Seric exit(EX_OSERR); 706588Seric } 707588Seric strcpy(p, buf); 708148Seric } 7091438Seric 710148Seric return (p); 711148Seric } 7121432Seric 7131432Seric /* 714587Seric ** ISDIR -- return true if the argument is a directory. 715587Seric ** 716587Seric ** Parameters: 717587Seric ** name -- the pathname of the file to check. 718587Seric ** 719587Seric ** Returns: 720587Seric ** TRUE if 'name' is a directory, FALSE otherwise. 721587Seric ** 722587Seric ** Side Effects: 723587Seric ** none. 724587Seric */ 725587Seric 726587Seric bool 727587Seric isdir(name) 728587Seric char *name; 729587Seric { 730587Seric struct stat stbuf; 731587Seric 732587Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 733587Seric } 7341432Seric 7351432Seric /* 736586Seric ** SAFEPATH -- determine whether a pathname is "safe" 737586Seric ** 738586Seric ** "Safe" pathnames only allow you to get deeper into the 739586Seric ** directory structure, i.e., full pathnames and ".." are 740586Seric ** not allowed. 741586Seric ** 742586Seric ** Parameters: 743586Seric ** p -- the name to check. 744586Seric ** 745586Seric ** Returns: 746586Seric ** TRUE -- if the path is safe. 747586Seric ** FALSE -- if the path is not safe. 748586Seric ** 749586Seric ** Side Effects: 750586Seric ** Prints a message if the path is not safe. 751586Seric */ 752586Seric 753586Seric bool 754586Seric safepath(p) 755586Seric register char *p; 756586Seric { 757586Seric extern char *index(); 758586Seric 759586Seric if (*p != '/') 760586Seric { 761586Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 762586Seric { 763586Seric p = index(p, '/'); 764586Seric if (p == NULL) 765586Seric return (TRUE); 766586Seric p++; 767586Seric } 768586Seric } 769586Seric 770586Seric printf("You may not use full pathnames or \"..\"\n"); 771586Seric return (FALSE); 772586Seric } 7731432Seric 7741432Seric /* 775261Seric ** CLEAN -- clean out recreatable files 776261Seric ** 777261Seric ** Any file for which an "s." file exists but no "p." file 778261Seric ** exists in the current directory is purged. 779261Seric ** 780261Seric ** Parameters: 781819Seric ** tells whether this came from a "clean", "info", or 782819Seric ** "check" command. 783261Seric ** 784261Seric ** Returns: 785261Seric ** none. 786261Seric ** 787261Seric ** Side Effects: 788819Seric ** Removes files in the current directory. 789819Seric ** Prints information regarding files being edited. 790819Seric ** Exits if a "check" command. 791261Seric */ 792261Seric 793819Seric clean(mode) 794819Seric int mode; 795261Seric { 796261Seric struct direct dir; 797261Seric struct stat stbuf; 798261Seric char buf[100]; 799394Seric char pline[120]; 800346Seric register FILE *dirfd; 801346Seric register char *basefile; 802351Seric bool gotedit; 803394Seric FILE *pfp; 804261Seric 8051438Seric /* 8061438Seric ** Find and open the SCCS directory. 8071438Seric */ 8081438Seric 8091207Seric strcpy(buf, SccsDir); 8101207Seric if (buf[0] != '\0') 8111207Seric strcat(buf, "/"); 8121207Seric strcat(buf, SccsPath); 8131438Seric 8141207Seric dirfd = fopen(buf, "r"); 815261Seric if (dirfd == NULL) 816261Seric { 8171207Seric usrerr("cannot open %s", buf); 8181282Seric return (EX_NOINPUT); 819261Seric } 820261Seric 821261Seric /* 822261Seric ** Scan the SCCS directory looking for s. files. 8231438Seric ** gotedit tells whether we have tried to clean any 8241438Seric ** files that are being edited. 825261Seric */ 826261Seric 827351Seric gotedit = FALSE; 828261Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 829261Seric { 830568Seric if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) 831261Seric continue; 832261Seric 833261Seric /* got an s. file -- see if the p. file exists */ 8341207Seric strcpy(buf, SccsDir); 8351207Seric if (buf[0] != '\0') 8361207Seric strcat(buf, "/"); 8371207Seric strcat(buf, SccsPath); 838261Seric strcat(buf, "/p."); 839346Seric basefile = &buf[strlen(buf)]; 840568Seric strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 841346Seric basefile[sizeof dir.d_name - 2] = '\0'; 842394Seric pfp = fopen(buf, "r"); 843394Seric if (pfp != NULL) 844346Seric { 8451438Seric /* the file exists -- report it's contents */ 846394Seric while (fgets(pline, sizeof pline, pfp) != NULL) 847416Seric printf("%12s: being edited: %s", basefile, pline); 848394Seric fclose(pfp); 849351Seric gotedit = TRUE; 850261Seric continue; 851346Seric } 852261Seric 853261Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 854819Seric if (mode == CLEANC) 855346Seric { 856568Seric strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); 857346Seric buf[sizeof dir.d_name - 2] = '\0'; 858346Seric unlink(buf); 859346Seric } 860261Seric } 861261Seric 8621438Seric /* cleanup & report results */ 863261Seric fclose(dirfd); 864819Seric if (!gotedit && mode == INFOC) 865416Seric printf("Nothing being edited\n"); 866819Seric if (mode == CHECKC) 867819Seric exit(gotedit); 8681282Seric return (EX_OK); 869261Seric } 8701432Seric 8711432Seric /* 872396Seric ** UNEDIT -- unedit a file 873396Seric ** 874396Seric ** Checks to see that the current user is actually editting 875396Seric ** the file and arranges that s/he is not editting it. 876396Seric ** 877396Seric ** Parameters: 878416Seric ** fn -- the name of the file to be unedited. 879396Seric ** 880396Seric ** Returns: 881585Seric ** TRUE -- if the file was successfully unedited. 882585Seric ** FALSE -- if the file was not unedited for some 883585Seric ** reason. 884396Seric ** 885396Seric ** Side Effects: 886396Seric ** fn is removed 887396Seric ** entries are removed from pfile. 888396Seric */ 889396Seric 890585Seric bool 891396Seric unedit(fn) 892396Seric char *fn; 893396Seric { 894396Seric register FILE *pfp; 895396Seric char *pfn; 896396Seric static char tfn[] = "/tmp/sccsXXXXX"; 897396Seric FILE *tfp; 898396Seric register char *p; 899396Seric register char *q; 900396Seric bool delete = FALSE; 901396Seric bool others = FALSE; 902396Seric char *myname; 903396Seric extern char *getlogin(); 904396Seric struct pfile *pent; 905396Seric extern struct pfile *getpfile(); 906396Seric char buf[120]; 9071316Seric extern char *makefile(); 908828Seric # ifdef UIDUSER 909828Seric struct passwd *pw; 910828Seric extern struct passwd *getpwuid(); 911828Seric # endif UIDUSER 912396Seric 913396Seric /* make "s." filename & find the trailing component */ 914396Seric pfn = makefile(fn); 915586Seric if (pfn == NULL) 916586Seric return (FALSE); 917586Seric q = rindex(pfn, '/'); 918586Seric if (q == NULL) 919586Seric q = &pfn[-1]; 920586Seric if (q[1] != 's' || q[2] != '.') 921396Seric { 9221205Seric usrerr("bad file name \"%s\"", fn); 923585Seric return (FALSE); 924396Seric } 925396Seric 9261438Seric /* turn "s." into "p." & try to open it */ 927396Seric *++q = 'p'; 928396Seric 929396Seric pfp = fopen(pfn, "r"); 930396Seric if (pfp == NULL) 931396Seric { 932416Seric printf("%12s: not being edited\n", fn); 933585Seric return (FALSE); 934396Seric } 935396Seric 9361438Seric /* create temp file for editing p-file */ 937396Seric mktemp(tfn); 938396Seric tfp = fopen(tfn, "w"); 939396Seric if (tfp == NULL) 940396Seric { 9411205Seric usrerr("cannot create \"%s\"", tfn); 942396Seric exit(EX_OSERR); 943396Seric } 944396Seric 9451438Seric /* figure out who I am */ 946828Seric # ifdef UIDUSER 947828Seric pw = getpwuid(getuid()); 948828Seric if (pw == NULL) 949828Seric { 9501205Seric syserr("who are you? (uid=%d)", getuid()); 951828Seric exit(EX_OSERR); 952828Seric } 953828Seric myname = pw->pw_name; 954828Seric # else 955396Seric myname = getlogin(); 956828Seric # endif UIDUSER 9571438Seric 9581438Seric /* 9591438Seric ** Copy p-file to temp file, doing deletions as needed. 9601438Seric */ 9611438Seric 962396Seric while ((pent = getpfile(pfp)) != NULL) 963396Seric { 964396Seric if (strcmp(pent->p_user, myname) == 0) 965396Seric { 966396Seric /* a match */ 967396Seric delete++; 968396Seric } 969396Seric else 970396Seric { 9711438Seric /* output it again */ 972396Seric fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, 973396Seric pent->p_nsid, pent->p_user, pent->p_date, 974396Seric pent->p_time); 975396Seric others++; 976396Seric } 977396Seric } 978396Seric 979396Seric /* do final cleanup */ 980396Seric if (others) 981396Seric { 9821438Seric /* copy it back (perhaps it should be linked?) */ 983396Seric if (freopen(tfn, "r", tfp) == NULL) 984396Seric { 9851205Seric syserr("cannot reopen \"%s\"", tfn); 986396Seric exit(EX_OSERR); 987396Seric } 988396Seric if (freopen(pfn, "w", pfp) == NULL) 989396Seric { 9901205Seric usrerr("cannot create \"%s\"", pfn); 991585Seric return (FALSE); 992396Seric } 993396Seric while (fgets(buf, sizeof buf, tfp) != NULL) 994396Seric fputs(buf, pfp); 995396Seric } 996396Seric else 997396Seric { 9981438Seric /* it's empty -- remove it */ 999396Seric unlink(pfn); 1000396Seric } 1001396Seric fclose(tfp); 1002396Seric fclose(pfp); 1003396Seric unlink(tfn); 1004396Seric 10051438Seric /* actually remove the g-file */ 1006396Seric if (delete) 1007396Seric { 10081435Seric unlink(tail(fn)); 10091435Seric printf("%12s: removed\n", tail(fn)); 1010585Seric return (TRUE); 1011396Seric } 1012396Seric else 1013396Seric { 1014416Seric printf("%12s: not being edited by you\n", fn); 1015585Seric return (FALSE); 1016396Seric } 1017396Seric } 10181432Seric 10191432Seric /* 10201433Seric ** DODIFF -- diff an s-file against a g-file 10211433Seric ** 10221433Seric ** Parameters: 10231433Seric ** getv -- argv for the 'get' command. 10241433Seric ** gfile -- name of the g-file to diff against. 10251433Seric ** 10261433Seric ** Returns: 10271433Seric ** Result of get. 10281433Seric ** 10291433Seric ** Side Effects: 10301433Seric ** none. 10311433Seric */ 10321433Seric 10331433Seric dodiff(getv, gfile) 10341433Seric char **getv; 10351433Seric char *gfile; 10361433Seric { 10371433Seric int pipev[2]; 10381433Seric int rval; 10391433Seric register int i; 10401433Seric register int pid; 10411433Seric auto int st; 10421433Seric extern int errno; 10431433Seric int (*osig)(); 10441433Seric 10451501Seric printf("\n------- %s -------\n", gfile); 10461501Seric 10471438Seric /* create context for diff to run in */ 10481433Seric if (pipe(pipev) < 0) 10491433Seric { 10501433Seric syserr("dodiff: pipe failed"); 10511433Seric exit(EX_OSERR); 10521433Seric } 10531433Seric if ((pid = fork()) < 0) 10541433Seric { 10551433Seric syserr("dodiff: fork failed"); 10561433Seric exit(EX_OSERR); 10571433Seric } 10581433Seric else if (pid > 0) 10591433Seric { 10601433Seric /* in parent; run get */ 10611433Seric OutFile = pipev[1]; 10621433Seric close(pipev[0]); 10631433Seric rval = command(&getv[1], TRUE, FALSE, "get -s -k -p"); 10641433Seric osig = signal(SIGINT, SIG_IGN); 10651433Seric while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR) 10661433Seric errno = 0; 10671433Seric signal(SIGINT, osig); 10681433Seric /* ignore result of diff */ 10691433Seric } 10701433Seric else 10711433Seric { 10721433Seric /* in child, run diff */ 10731433Seric if (close(pipev[1]) < 0 || close(0) < 0 || 10741433Seric dup(pipev[0]) != 0 || close(pipev[0]) < 0) 10751433Seric { 10761433Seric syserr("dodiff: magic failed"); 10771433Seric exit(EX_OSERR); 10781433Seric } 10791433Seric execl(PROGPATH(bdiff), "bdiff", "-", gfile, NULL); 10801433Seric # ifndef V6 10811433Seric execlp("bdiff", "bdiff", "-", gfile, NULL); 10821433Seric execlp("diff", "diff", "-", gfile, NULL); 10831433Seric # endif NOT V6 10841433Seric syserr("bdiff: cannot execute"); 10851433Seric exit(EX_OSERR); 10861433Seric } 10871433Seric return (rval); 10881433Seric } 10891433Seric 10901433Seric /* 10911435Seric ** TAIL -- return tail of filename. 10921435Seric ** 10931435Seric ** Parameters: 10941435Seric ** fn -- the filename. 10951435Seric ** 10961435Seric ** Returns: 10971435Seric ** a pointer to the tail of the filename; e.g., given 10981435Seric ** "cmd/ls.c", "ls.c" is returned. 10991435Seric ** 11001435Seric ** Side Effects: 11011435Seric ** none. 11021435Seric */ 11031435Seric 11041435Seric char * 11051435Seric tail(fn) 11061435Seric register char *fn; 11071435Seric { 11081435Seric register char *p; 11091435Seric 11101435Seric for (p = fn; *p != 0; p++) 11111435Seric if (*p == '/' && p[1] != '\0' && p[1] != '/') 11121435Seric fn = &p[1]; 11131435Seric return (fn); 11141435Seric } 11151435Seric 11161435Seric /* 1117396Seric ** GETPFILE -- get an entry from the p-file 1118396Seric ** 1119396Seric ** Parameters: 1120396Seric ** pfp -- p-file file pointer 1121396Seric ** 1122396Seric ** Returns: 1123396Seric ** pointer to p-file struct for next entry 1124396Seric ** NULL on EOF or error 1125396Seric ** 1126396Seric ** Side Effects: 1127396Seric ** Each call wipes out results of previous call. 1128396Seric */ 1129396Seric 1130396Seric struct pfile * 1131396Seric getpfile(pfp) 1132396Seric FILE *pfp; 1133396Seric { 1134396Seric static struct pfile ent; 1135396Seric static char buf[120]; 1136396Seric register char *p; 1137396Seric extern char *nextfield(); 1138396Seric 1139396Seric if (fgets(buf, sizeof buf, pfp) == NULL) 1140396Seric return (NULL); 1141396Seric 1142396Seric ent.p_osid = p = buf; 1143396Seric ent.p_nsid = p = nextfield(p); 1144396Seric ent.p_user = p = nextfield(p); 1145396Seric ent.p_date = p = nextfield(p); 1146396Seric ent.p_time = p = nextfield(p); 1147396Seric if (p == NULL || nextfield(p) != NULL) 1148396Seric return (NULL); 1149396Seric 1150396Seric return (&ent); 1151396Seric } 1152396Seric 1153396Seric 1154396Seric char * 1155396Seric nextfield(p) 1156396Seric register char *p; 1157396Seric { 1158396Seric if (p == NULL || *p == '\0') 1159396Seric return (NULL); 1160396Seric while (*p != ' ' && *p != '\n' && *p != '\0') 1161396Seric p++; 1162396Seric if (*p == '\n' || *p == '\0') 1163396Seric { 1164396Seric *p = '\0'; 1165396Seric return (NULL); 1166396Seric } 1167396Seric *p++ = '\0'; 1168396Seric return (p); 1169396Seric } 11701432Seric 11711432Seric /* 11721205Seric ** USRERR -- issue user-level error 11731205Seric ** 11741205Seric ** Parameters: 11751205Seric ** f -- format string. 11761205Seric ** p1-p3 -- parameters to a printf. 11771205Seric ** 11781205Seric ** Returns: 11791205Seric ** -1 11801205Seric ** 11811205Seric ** Side Effects: 11821205Seric ** none. 11831205Seric */ 11841205Seric 11851205Seric usrerr(f, p1, p2, p3) 11861205Seric char *f; 11871205Seric { 11881205Seric fprintf(stderr, "\n%s: ", MyName); 11891205Seric fprintf(stderr, f, p1, p2, p3); 11901205Seric fprintf(stderr, "\n"); 11911205Seric 11921205Seric return (-1); 11931205Seric } 11941432Seric 11951432Seric /* 11961205Seric ** SYSERR -- print system-generated error. 11971205Seric ** 11981205Seric ** Parameters: 11991205Seric ** f -- format string to a printf. 12001205Seric ** p1, p2, p3 -- parameters to f. 12011205Seric ** 12021205Seric ** Returns: 12031205Seric ** never. 12041205Seric ** 12051205Seric ** Side Effects: 12061205Seric ** none. 12071205Seric */ 12081205Seric 12091205Seric syserr(f, p1, p2, p3) 12101205Seric char *f; 12111205Seric { 12121205Seric extern int errno; 12131205Seric 12141205Seric fprintf(stderr, "\n%s SYSERR: ", MyName); 12151205Seric fprintf(stderr, f, p1, p2, p3); 12161205Seric fprintf(stderr, "\n"); 12171205Seric if (errno == 0) 12181205Seric exit(EX_SOFTWARE); 12191205Seric else 12201205Seric { 12211205Seric perror(0); 12221205Seric exit(EX_OSERR); 12231205Seric } 12241205Seric } 1225