1148Seric # include <stdio.h> 2148Seric # include <sys/types.h> 3148Seric # include <sys/stat.h> 4261Seric # include <sys/dir.h> 5*1433Seric # include <errno.h> 6*1433Seric # 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. 82828Seric ** 83828Seric ** Compilation Instructions: 84828Seric ** cc -O -n -s sccs.c 85828Seric ** 86828Seric ** Author: 87828Seric ** Eric Allman, UCB/INGRES 881270Seric ** Copyright 1980 Regents of the University of California 89828Seric */ 90155Seric 91*1433Seric static char SccsId[] = "@(#)sccs.c 1.36 10/15/80"; 921432Seric 931270Seric /******************* Configuration Information ********************/ 941270Seric 95828Seric # ifdef CSVAX 96828Seric # define UIDUSER 971270Seric # define PROGPATH(name) "/usr/local/name" 981270Seric # endif CSVAX 99828Seric 1001432Seric # define SCCSPATH "SCCS" /* pathname in which to find s-files */ 1011270Seric /* put #define SCCSDIR here */ 102828Seric 1031270Seric char MyName[] = "sccs"; /* name used in messages */ 1041270Seric 1051432Seric # ifndef PROGPATH 1061432Seric # define PROGPATH(name) "/usr/sccs/name" /* place to find binaries */ 1071432Seric # endif PROGPATH 1081432Seric 1091270Seric /**************** End of Configuration Information ****************/ 1101432Seric 111157Seric # define bitset(bit, word) ((bit) & (word)) 112157Seric 113157Seric typedef char bool; 114200Seric # define TRUE 1 115200Seric # define FALSE 0 116157Seric 117828Seric # ifdef UIDUSER 118828Seric # include <pwd.h> 119828Seric # endif UIDUSER 120828Seric 121148Seric struct sccsprog 122148Seric { 123148Seric char *sccsname; /* name of SCCS routine */ 124200Seric short sccsoper; /* opcode, see below */ 125200Seric short sccsflags; /* flags, see below */ 1261316Seric char *sccsklets; /* valid key-letters on macros */ 127148Seric char *sccspath; /* pathname of binary implementing */ 128148Seric }; 129148Seric 130200Seric /* values for sccsoper */ 131200Seric # define PROG 0 /* call a program */ 132201Seric # define CMACRO 1 /* command substitution macro */ 133226Seric # define FIX 2 /* fix a delta */ 134261Seric # define CLEAN 3 /* clean out recreatable files */ 135396Seric # define UNEDIT 4 /* unedit a file */ 1361431Seric # define SHELL 5 /* call a shell file (like PROG) */ 137*1433Seric # define DIFFS 6 /* diff between sccs & file out */ 138200Seric 139157Seric /* bits for sccsflags */ 140200Seric # define NO_SDOT 0001 /* no s. on front of args */ 141200Seric # define REALUSER 0002 /* protected (e.g., admin) */ 142148Seric 143819Seric /* modes for the "clean", "info", "check" ops */ 144819Seric # define CLEANC 0 /* clean command */ 145819Seric # define INFOC 1 /* info command */ 146819Seric # define CHECKC 2 /* check command */ 147819Seric 1481432Seric /* 1491432Seric ** Description of commands known to this program. 1501432Seric ** First argument puts the command into a class. Second arg is 1511432Seric ** info regarding treatment of this command. Third arg is a 1521432Seric ** list of flags this command accepts from macros, etc. Fourth 1531432Seric ** arg is the pathname of the implementing program, or the 1541432Seric ** macro definition, or the arg to a sub-algorithm. 1551432Seric */ 156202Seric 157148Seric struct sccsprog SccsProg[] = 158148Seric { 1591316Seric "admin", PROG, REALUSER, "", PROGPATH(admin), 1601316Seric "chghist", PROG, 0, "", PROGPATH(rmdel), 1611316Seric "comb", PROG, 0, "", PROGPATH(comb), 1621316Seric "delta", PROG, 0, "mysrp", PROGPATH(delta), 1631317Seric "get", PROG, 0, "ixbeskcl", PROGPATH(get), 1641316Seric "help", PROG, NO_SDOT, "", PROGPATH(help), 1651316Seric "prt", PROG, 0, "", PROGPATH(prt), 1661316Seric "rmdel", PROG, REALUSER, "", PROGPATH(rmdel), 1671316Seric "what", PROG, NO_SDOT, "", PROGPATH(what), 1681431Seric "sccsdiff", SHELL, REALUSER, "", PROGPATH(sccsdiff), 1691317Seric "edit", CMACRO, NO_SDOT, "ixbscl", "get -e", 1701316Seric "delget", CMACRO, NO_SDOT, "", "delta/get -t", 1711316Seric "deledit", CMACRO, NO_SDOT, "", "delta/get -e -t", 1721316Seric "fix", FIX, NO_SDOT, "", NULL, 1731316Seric "clean", CLEAN, REALUSER, "", (char *) CLEANC, 1741316Seric "info", CLEAN, REALUSER, "", (char *) INFOC, 1751316Seric "check", CLEAN, REALUSER, "", (char *) CHECKC, 1761316Seric "unedit", UNEDIT, NO_SDOT, "", NULL, 177*1433Seric "diffs", DIFFS, NO_SDOT|REALUSER, "", NULL, 1781316Seric NULL, -1, 0, "", NULL 179148Seric }; 180148Seric 1811432Seric /* one line from a p-file */ 182396Seric struct pfile 183396Seric { 184396Seric char *p_osid; /* old SID */ 185396Seric char *p_nsid; /* new SID */ 186396Seric char *p_user; /* user who did edit */ 187396Seric char *p_date; /* date of get */ 188396Seric char *p_time; /* time of get */ 189396Seric }; 190396Seric 1911270Seric char *SccsPath = SCCSPATH; /* pathname of SCCS files */ 1921270Seric # ifdef SCCSDIR 1931270Seric char *SccsDir = SCCSDIR; /* directory to begin search from */ 1941205Seric # else 1951270Seric char *SccsDir = ""; 1961205Seric # endif 197*1433Seric int OutFile = -1; /* override output file for commands */ 198157Seric bool RealUser; /* if set, running as real user */ 199393Seric # ifdef DEBUG 200393Seric bool Debug; /* turn on tracing */ 201393Seric # endif 2021432Seric 203148Seric main(argc, argv) 204148Seric int argc; 205148Seric char **argv; 206148Seric { 207148Seric register char *p; 208262Seric extern struct sccsprog *lookup(); 2091282Seric register int i; 210148Seric 211148Seric /* 212148Seric ** Detect and decode flags intended for this program. 213148Seric */ 214148Seric 215200Seric if (argc < 2) 216148Seric { 2171205Seric fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); 218200Seric exit(EX_USAGE); 219200Seric } 220200Seric argv[argc] = NULL; 221200Seric 222262Seric if (lookup(argv[0]) == NULL) 223200Seric { 224262Seric while ((p = *++argv) != NULL) 225148Seric { 226262Seric if (*p != '-') 227262Seric break; 228262Seric switch (*++p) 229262Seric { 230262Seric case 'r': /* run as real user */ 231262Seric setuid(getuid()); 232262Seric RealUser++; 233262Seric break; 234148Seric 2351270Seric # ifndef SCCSDIR 236262Seric case 'p': /* path of sccs files */ 237262Seric SccsPath = ++p; 238262Seric break; 239148Seric 240588Seric case 'd': /* directory to search from */ 241588Seric SccsDir = ++p; 242588Seric break; 2431205Seric # endif 244588Seric 245393Seric # ifdef DEBUG 246393Seric case 'T': /* trace */ 247393Seric Debug++; 248393Seric break; 249393Seric # endif 250393Seric 251262Seric default: 2521205Seric usrerr("unknown option -%s", p); 253262Seric break; 254262Seric } 255148Seric } 256262Seric if (SccsPath[0] == '\0') 257262Seric SccsPath = "."; 258148Seric } 259148Seric 2601316Seric i = command(argv, FALSE, FALSE, ""); 2611282Seric exit(i); 262200Seric } 2631432Seric 2641432Seric /* 2651282Seric ** COMMAND -- look up and perform a command 2661282Seric ** 2671282Seric ** This routine is the guts of this program. Given an 2681282Seric ** argument vector, it looks up the "command" (argv[0]) 2691282Seric ** in the configuration table and does the necessary stuff. 2701282Seric ** 2711282Seric ** Parameters: 2721282Seric ** argv -- an argument vector to process. 2731282Seric ** forkflag -- if set, fork before executing the command. 2741316Seric ** editflag -- if set, only include flags listed in the 2751316Seric ** sccsklets field of the command descriptor. 2761316Seric ** arg0 -- a space-seperated list of arguments to insert 2771316Seric ** before argv. 2781282Seric ** 2791282Seric ** Returns: 2801282Seric ** zero -- command executed ok. 2811282Seric ** else -- error status. 2821282Seric ** 2831282Seric ** Side Effects: 2841282Seric ** none. 2851282Seric */ 286157Seric 2871316Seric command(argv, forkflag, editflag, arg0) 288200Seric char **argv; 289201Seric bool forkflag; 2901316Seric bool editflag; 2911316Seric char *arg0; 292200Seric { 293200Seric register struct sccsprog *cmd; 294200Seric register char *p; 295201Seric char buf[40]; 296262Seric extern struct sccsprog *lookup(); 2971316Seric char *nav[1000]; 2981316Seric char **np; 2991431Seric register char **ap; 300585Seric register int i; 3011431Seric register char *q; 302585Seric extern bool unedit(); 3031282Seric int rval = 0; 3041316Seric extern char *index(); 3051316Seric extern char *makefile(); 306200Seric 307393Seric # ifdef DEBUG 308393Seric if (Debug) 309393Seric { 3101316Seric printf("command:\n\t\"%s\"\n", arg0); 3111316Seric for (np = argv; *np != NULL; np++) 3121316Seric printf("\t\"%s\"\n", *np); 313393Seric } 314393Seric # endif 315393Seric 316157Seric /* 3171316Seric ** Copy arguments. 3181316Seric ** Phase one -- from arg0 & if necessary argv[0]. 3191316Seric */ 3201316Seric 3211431Seric np = ap = &nav[1]; 3221316Seric for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) 3231316Seric { 3241316Seric *np++ = q; 3251316Seric while (*p == ' ') 3261316Seric p++; 3271316Seric while (*p != ' ' && *p != '\0' && *p != '/') 3281316Seric *q++ = *p++; 3291316Seric *q++ = '\0'; 3301316Seric } 3311316Seric *np = NULL; 3321431Seric if (*ap == NULL) 3331316Seric *np++ = *argv++; 3341316Seric 3351316Seric /* 336148Seric ** Look up command. 3371431Seric ** At this point, *ap is the command name. 338148Seric */ 339148Seric 3401431Seric cmd = lookup(*ap); 341262Seric if (cmd == NULL) 342148Seric { 3431431Seric usrerr("Unknown command \"%s\"", *ap); 3441282Seric return (EX_USAGE); 345148Seric } 346148Seric 347148Seric /* 3481316Seric ** Copy remaining arguments doing editing as appropriate. 3491316Seric */ 3501316Seric 3511316Seric for (; *argv != NULL; argv++) 3521316Seric { 3531316Seric p = *argv; 3541316Seric if (*p == '-') 3551316Seric { 3561316Seric if (p[1] == '\0' || !editflag || cmd->sccsklets == NULL || 3571316Seric index(cmd->sccsklets, p[1]) != NULL) 3581316Seric *np++ = p; 3591316Seric } 3601316Seric else 3611316Seric { 3621316Seric if (!bitset(NO_SDOT, cmd->sccsflags)) 3631316Seric p = makefile(p); 3641316Seric if (p != NULL) 3651316Seric *np++ = p; 3661316Seric } 3671316Seric } 3681316Seric *np = NULL; 3691316Seric 3701316Seric /* 371200Seric ** Interpret operation associated with this command. 372157Seric */ 373157Seric 374200Seric switch (cmd->sccsoper) 375200Seric { 3761431Seric case SHELL: /* call a shell file */ 3771431Seric *ap = cmd->sccspath; 3781431Seric *--ap = "sh"; 3791431Seric rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag); 3801431Seric break; 3811431Seric 382200Seric case PROG: /* call an sccs prog */ 3831431Seric rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); 384201Seric break; 385201Seric 386201Seric case CMACRO: /* command macro */ 387201Seric for (p = cmd->sccspath; *p != '\0'; p++) 388201Seric { 3891316Seric q = p; 3901316Seric while (*p != '\0' && *p != '/') 3911316Seric p++; 3921431Seric rval = command(&ap[1], *p != '\0', TRUE, q); 3931282Seric if (rval != 0) 3941282Seric break; 395201Seric } 3961282Seric break; 397157Seric 398226Seric case FIX: /* fix a delta */ 3991431Seric if (strncmp(ap[1], "-r", 2) != 0) 400226Seric { 4011205Seric usrerr("-r flag needed for fix command"); 4021282Seric rval = EX_USAGE; 403226Seric break; 404226Seric } 4051431Seric rval = command(&ap[1], TRUE, TRUE, "get -k"); 4061282Seric if (rval == 0) 4071431Seric rval = command(&ap[1], TRUE, TRUE, "rmdel"); 4081282Seric if (rval == 0) 4091431Seric rval = command(&ap[2], FALSE, TRUE, "get -e -g"); 4101282Seric break; 411226Seric 412261Seric case CLEAN: 4131282Seric rval = clean((int) cmd->sccspath); 414261Seric break; 415261Seric 416396Seric case UNEDIT: 4171431Seric for (argv = np = &ap[1]; *argv != NULL; argv++) 418585Seric { 4191316Seric if (unedit(*argv)) 4201316Seric *np++ = *argv; 421585Seric } 4221316Seric *np = NULL; 423585Seric if (i > 0) 4241431Seric rval = command(&ap[1], FALSE, FALSE, "get"); 425396Seric break; 426396Seric 427*1433Seric case DIFFS: /* diff between s-file & edit file */ 428*1433Seric /* find the end of the flag arguments */ 429*1433Seric for (np = &ap[1]; *np != NULL && **np == '-'; np++) 430*1433Seric continue; 431*1433Seric argv = np; 432*1433Seric 433*1433Seric /* for each file, do the diff */ 434*1433Seric while (*np != NULL) 435*1433Seric { 436*1433Seric *argv = *np++; 437*1433Seric p = *np; 438*1433Seric *np = NULL; 439*1433Seric i = dodiff(ap, *argv); 440*1433Seric if (rval == 0) 441*1433Seric rval = i; 442*1433Seric *np = p; 443*1433Seric } 444*1433Seric break; 445*1433Seric 446200Seric default: 4471205Seric syserr("oper %d", cmd->sccsoper); 448200Seric exit(EX_SOFTWARE); 449200Seric } 4501282Seric # ifdef DEBUG 4511282Seric if (Debug) 4521282Seric printf("command: rval=%d\n", rval); 4531282Seric # endif 4541282Seric return (rval); 455200Seric } 4561432Seric 4571432Seric /* 458262Seric ** LOOKUP -- look up an SCCS command name. 459262Seric ** 460262Seric ** Parameters: 461262Seric ** name -- the name of the command to look up. 462262Seric ** 463262Seric ** Returns: 464262Seric ** ptr to command descriptor for this command. 465262Seric ** NULL if no such entry. 466262Seric ** 467262Seric ** Side Effects: 468262Seric ** none. 469262Seric */ 470200Seric 471262Seric struct sccsprog * 472262Seric lookup(name) 473262Seric char *name; 474262Seric { 475262Seric register struct sccsprog *cmd; 476226Seric 477262Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 478262Seric { 479262Seric if (strcmp(cmd->sccsname, name) == 0) 480262Seric return (cmd); 481262Seric } 482262Seric return (NULL); 483262Seric } 4841432Seric 4851432Seric /* 4861282Seric ** CALLPROG -- call a program 4871282Seric ** 4881316Seric ** Used to call the SCCS programs. 4891282Seric ** 4901282Seric ** Parameters: 4911282Seric ** progpath -- pathname of the program to call. 4921282Seric ** flags -- status flags from the command descriptors. 4931282Seric ** argv -- an argument vector to pass to the program. 4941282Seric ** forkflag -- if true, fork before calling, else just 4951282Seric ** exec. 4961282Seric ** 4971282Seric ** Returns: 4981282Seric ** The exit status of the program. 4991282Seric ** Nothing if forkflag == FALSE. 5001282Seric ** 5011282Seric ** Side Effects: 5021282Seric ** Can exit if forkflag == FALSE. 5031282Seric */ 504226Seric 505200Seric callprog(progpath, flags, argv, forkflag) 506200Seric char *progpath; 507200Seric short flags; 508200Seric char **argv; 509200Seric bool forkflag; 510200Seric { 511200Seric register int i; 512201Seric auto int st; 513200Seric 5141316Seric # ifdef DEBUG 5151316Seric if (Debug) 5161316Seric { 5171316Seric printf("callprog:\n"); 5181316Seric for (i = 0; argv[i] != NULL; i++) 5191316Seric printf("\t\"%s\"\n", argv[i]); 5201316Seric } 5211316Seric # endif 5221316Seric 523200Seric if (*argv == NULL) 524200Seric return (-1); 525200Seric 526157Seric /* 527226Seric ** Fork if appropriate. 528148Seric */ 529148Seric 530200Seric if (forkflag) 531200Seric { 532393Seric # ifdef DEBUG 533393Seric if (Debug) 534393Seric printf("Forking\n"); 535393Seric # endif 536200Seric i = fork(); 537200Seric if (i < 0) 538200Seric { 5391205Seric syserr("cannot fork"); 540200Seric exit(EX_OSERR); 541200Seric } 542200Seric else if (i > 0) 543201Seric { 544201Seric wait(&st); 5451282Seric if ((st & 0377) == 0) 5461282Seric st = (st >> 8) & 0377; 547*1433Seric if (OutFile >= 0) 548*1433Seric { 549*1433Seric close(OutFile); 550*1433Seric OutFile = -1; 551*1433Seric } 552201Seric return (st); 553201Seric } 554200Seric } 555*1433Seric else if (OutFile >= 0) 556*1433Seric { 557*1433Seric syserr("callprog: setting stdout w/o forking"); 558*1433Seric exit(EX_SOFTWARE); 559*1433Seric } 560200Seric 561*1433Seric /* set protection as appropriate */ 562200Seric if (bitset(REALUSER, flags)) 563200Seric setuid(getuid()); 564*1433Seric 565*1433Seric /* change standard input & output if needed */ 566*1433Seric if (OutFile >= 0) 567*1433Seric { 568*1433Seric close(1); 569*1433Seric dup(OutFile); 570*1433Seric close(OutFile); 571*1433Seric } 572226Seric 573*1433Seric /* call real SCCS program */ 574226Seric execv(progpath, argv); 5751205Seric syserr("cannot execute %s", progpath); 576148Seric exit(EX_UNAVAILABLE); 577148Seric } 5781432Seric 5791432Seric /* 580586Seric ** MAKEFILE -- make filename of SCCS file 581586Seric ** 582586Seric ** If the name passed is already the name of an SCCS file, 583586Seric ** just return it. Otherwise, munge the name into the name 584586Seric ** of the actual SCCS file. 585586Seric ** 586586Seric ** There are cases when it is not clear what you want to 587586Seric ** do. For example, if SccsPath is an absolute pathname 588586Seric ** and the name given is also an absolute pathname, we go 589586Seric ** for SccsPath (& only use the last component of the name 590586Seric ** passed) -- this is important for security reasons (if 591586Seric ** sccs is being used as a setuid front end), but not 592586Seric ** particularly intuitive. 593586Seric ** 594586Seric ** Parameters: 595586Seric ** name -- the file name to be munged. 596586Seric ** 597586Seric ** Returns: 598586Seric ** The pathname of the sccs file. 599586Seric ** NULL on error. 600586Seric ** 601586Seric ** Side Effects: 602586Seric ** none. 603586Seric */ 604148Seric 605148Seric char * 606148Seric makefile(name) 607148Seric char *name; 608148Seric { 609148Seric register char *p; 610148Seric register char c; 611148Seric char buf[512]; 612588Seric struct stat stbuf; 613148Seric extern char *malloc(); 614586Seric extern char *rindex(); 615588Seric extern bool safepath(); 616587Seric extern bool isdir(); 617587Seric register char *q; 618148Seric 619586Seric p = rindex(name, '/'); 620586Seric if (p == NULL) 621586Seric p = name; 622586Seric else 623586Seric p++; 624586Seric 625148Seric /* 626588Seric ** Check to see that the path is "safe", i.e., that we 627588Seric ** are not letting some nasty person use the setuid part 628588Seric ** of this program to look at or munge some presumably 629588Seric ** hidden files. 630148Seric */ 631148Seric 632588Seric if (SccsDir[0] == '/' && !safepath(name)) 633588Seric return (NULL); 634586Seric 635586Seric /* 636588Seric ** Create the base pathname. 637586Seric */ 638586Seric 639588Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) 640148Seric { 641588Seric strcpy(buf, SccsDir); 642586Seric strcat(buf, "/"); 643586Seric } 644586Seric else 645586Seric strcpy(buf, ""); 646587Seric strncat(buf, name, p - name); 647587Seric q = &buf[strlen(buf)]; 648587Seric strcpy(q, p); 649587Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 650586Seric { 651588Seric strcpy(q, SccsPath); 652588Seric strcat(buf, "/s."); 653586Seric strcat(buf, p); 654586Seric } 655148Seric 656588Seric if (strcmp(buf, name) == 0) 657588Seric p = name; 658588Seric else 659148Seric { 660588Seric p = malloc(strlen(buf) + 1); 661588Seric if (p == NULL) 662588Seric { 663588Seric perror("Sccs: no mem"); 664588Seric exit(EX_OSERR); 665588Seric } 666588Seric strcpy(p, buf); 667148Seric } 668148Seric return (p); 669148Seric } 6701432Seric 6711432Seric /* 672587Seric ** ISDIR -- return true if the argument is a directory. 673587Seric ** 674587Seric ** Parameters: 675587Seric ** name -- the pathname of the file to check. 676587Seric ** 677587Seric ** Returns: 678587Seric ** TRUE if 'name' is a directory, FALSE otherwise. 679587Seric ** 680587Seric ** Side Effects: 681587Seric ** none. 682587Seric */ 683587Seric 684587Seric bool 685587Seric isdir(name) 686587Seric char *name; 687587Seric { 688587Seric struct stat stbuf; 689587Seric 690587Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 691587Seric } 6921432Seric 6931432Seric /* 694586Seric ** SAFEPATH -- determine whether a pathname is "safe" 695586Seric ** 696586Seric ** "Safe" pathnames only allow you to get deeper into the 697586Seric ** directory structure, i.e., full pathnames and ".." are 698586Seric ** not allowed. 699586Seric ** 700586Seric ** Parameters: 701586Seric ** p -- the name to check. 702586Seric ** 703586Seric ** Returns: 704586Seric ** TRUE -- if the path is safe. 705586Seric ** FALSE -- if the path is not safe. 706586Seric ** 707586Seric ** Side Effects: 708586Seric ** Prints a message if the path is not safe. 709586Seric */ 710586Seric 711586Seric bool 712586Seric safepath(p) 713586Seric register char *p; 714586Seric { 715586Seric extern char *index(); 716586Seric 717586Seric if (*p != '/') 718586Seric { 719586Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 720586Seric { 721586Seric p = index(p, '/'); 722586Seric if (p == NULL) 723586Seric return (TRUE); 724586Seric p++; 725586Seric } 726586Seric } 727586Seric 728586Seric printf("You may not use full pathnames or \"..\"\n"); 729586Seric return (FALSE); 730586Seric } 7311432Seric 7321432Seric /* 733261Seric ** CLEAN -- clean out recreatable files 734261Seric ** 735261Seric ** Any file for which an "s." file exists but no "p." file 736261Seric ** exists in the current directory is purged. 737261Seric ** 738261Seric ** Parameters: 739819Seric ** tells whether this came from a "clean", "info", or 740819Seric ** "check" command. 741261Seric ** 742261Seric ** Returns: 743261Seric ** none. 744261Seric ** 745261Seric ** Side Effects: 746819Seric ** Removes files in the current directory. 747819Seric ** Prints information regarding files being edited. 748819Seric ** Exits if a "check" command. 749261Seric */ 750261Seric 751819Seric clean(mode) 752819Seric int mode; 753261Seric { 754261Seric struct direct dir; 755261Seric struct stat stbuf; 756261Seric char buf[100]; 757394Seric char pline[120]; 758346Seric register FILE *dirfd; 759346Seric register char *basefile; 760351Seric bool gotedit; 761394Seric FILE *pfp; 762261Seric 7631207Seric strcpy(buf, SccsDir); 7641207Seric if (buf[0] != '\0') 7651207Seric strcat(buf, "/"); 7661207Seric strcat(buf, SccsPath); 7671207Seric dirfd = fopen(buf, "r"); 768261Seric if (dirfd == NULL) 769261Seric { 7701207Seric usrerr("cannot open %s", buf); 7711282Seric return (EX_NOINPUT); 772261Seric } 773261Seric 774261Seric /* 775261Seric ** Scan the SCCS directory looking for s. files. 776261Seric */ 777261Seric 778351Seric gotedit = FALSE; 779261Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 780261Seric { 781568Seric if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) 782261Seric continue; 783261Seric 784261Seric /* got an s. file -- see if the p. file exists */ 7851207Seric strcpy(buf, SccsDir); 7861207Seric if (buf[0] != '\0') 7871207Seric strcat(buf, "/"); 7881207Seric strcat(buf, SccsPath); 789261Seric strcat(buf, "/p."); 790346Seric basefile = &buf[strlen(buf)]; 791568Seric strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 792346Seric basefile[sizeof dir.d_name - 2] = '\0'; 793394Seric pfp = fopen(buf, "r"); 794394Seric if (pfp != NULL) 795346Seric { 796394Seric while (fgets(pline, sizeof pline, pfp) != NULL) 797416Seric printf("%12s: being edited: %s", basefile, pline); 798394Seric fclose(pfp); 799351Seric gotedit = TRUE; 800261Seric continue; 801346Seric } 802261Seric 803261Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 804819Seric if (mode == CLEANC) 805346Seric { 806568Seric strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); 807346Seric buf[sizeof dir.d_name - 2] = '\0'; 808346Seric unlink(buf); 809346Seric } 810261Seric } 811261Seric 812261Seric fclose(dirfd); 813819Seric if (!gotedit && mode == INFOC) 814416Seric printf("Nothing being edited\n"); 815819Seric if (mode == CHECKC) 816819Seric exit(gotedit); 8171282Seric return (EX_OK); 818261Seric } 8191432Seric 8201432Seric /* 821396Seric ** UNEDIT -- unedit a file 822396Seric ** 823396Seric ** Checks to see that the current user is actually editting 824396Seric ** the file and arranges that s/he is not editting it. 825396Seric ** 826396Seric ** Parameters: 827416Seric ** fn -- the name of the file to be unedited. 828396Seric ** 829396Seric ** Returns: 830585Seric ** TRUE -- if the file was successfully unedited. 831585Seric ** FALSE -- if the file was not unedited for some 832585Seric ** reason. 833396Seric ** 834396Seric ** Side Effects: 835396Seric ** fn is removed 836396Seric ** entries are removed from pfile. 837396Seric */ 838396Seric 839585Seric bool 840396Seric unedit(fn) 841396Seric char *fn; 842396Seric { 843396Seric register FILE *pfp; 844396Seric char *pfn; 845396Seric static char tfn[] = "/tmp/sccsXXXXX"; 846396Seric FILE *tfp; 847396Seric register char *p; 848396Seric register char *q; 849396Seric bool delete = FALSE; 850396Seric bool others = FALSE; 851396Seric char *myname; 852396Seric extern char *getlogin(); 853396Seric struct pfile *pent; 854396Seric extern struct pfile *getpfile(); 855396Seric char buf[120]; 8561316Seric extern char *makefile(); 857828Seric # ifdef UIDUSER 858828Seric struct passwd *pw; 859828Seric extern struct passwd *getpwuid(); 860828Seric # endif UIDUSER 861396Seric 862396Seric /* make "s." filename & find the trailing component */ 863396Seric pfn = makefile(fn); 864586Seric if (pfn == NULL) 865586Seric return (FALSE); 866586Seric q = rindex(pfn, '/'); 867586Seric if (q == NULL) 868586Seric q = &pfn[-1]; 869586Seric if (q[1] != 's' || q[2] != '.') 870396Seric { 8711205Seric usrerr("bad file name \"%s\"", fn); 872585Seric return (FALSE); 873396Seric } 874396Seric 875396Seric /* turn "s." into "p." */ 876396Seric *++q = 'p'; 877396Seric 878396Seric pfp = fopen(pfn, "r"); 879396Seric if (pfp == NULL) 880396Seric { 881416Seric printf("%12s: not being edited\n", fn); 882585Seric return (FALSE); 883396Seric } 884396Seric 885396Seric /* 886396Seric ** Copy p-file to temp file, doing deletions as needed. 887396Seric */ 888396Seric 889396Seric mktemp(tfn); 890396Seric tfp = fopen(tfn, "w"); 891396Seric if (tfp == NULL) 892396Seric { 8931205Seric usrerr("cannot create \"%s\"", tfn); 894396Seric exit(EX_OSERR); 895396Seric } 896396Seric 897828Seric # ifdef UIDUSER 898828Seric pw = getpwuid(getuid()); 899828Seric if (pw == NULL) 900828Seric { 9011205Seric syserr("who are you? (uid=%d)", getuid()); 902828Seric exit(EX_OSERR); 903828Seric } 904828Seric myname = pw->pw_name; 905828Seric # else 906396Seric myname = getlogin(); 907828Seric # endif UIDUSER 908396Seric while ((pent = getpfile(pfp)) != NULL) 909396Seric { 910396Seric if (strcmp(pent->p_user, myname) == 0) 911396Seric { 912396Seric /* a match */ 913396Seric delete++; 914396Seric } 915396Seric else 916396Seric { 917396Seric fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, 918396Seric pent->p_nsid, pent->p_user, pent->p_date, 919396Seric pent->p_time); 920396Seric others++; 921396Seric } 922396Seric } 923396Seric 924396Seric /* do final cleanup */ 925396Seric if (others) 926396Seric { 927396Seric if (freopen(tfn, "r", tfp) == NULL) 928396Seric { 9291205Seric syserr("cannot reopen \"%s\"", tfn); 930396Seric exit(EX_OSERR); 931396Seric } 932396Seric if (freopen(pfn, "w", pfp) == NULL) 933396Seric { 9341205Seric usrerr("cannot create \"%s\"", pfn); 935585Seric return (FALSE); 936396Seric } 937396Seric while (fgets(buf, sizeof buf, tfp) != NULL) 938396Seric fputs(buf, pfp); 939396Seric } 940396Seric else 941396Seric { 942396Seric unlink(pfn); 943396Seric } 944396Seric fclose(tfp); 945396Seric fclose(pfp); 946396Seric unlink(tfn); 947396Seric 948396Seric if (delete) 949396Seric { 950396Seric unlink(fn); 951396Seric printf("%12s: removed\n", fn); 952585Seric return (TRUE); 953396Seric } 954396Seric else 955396Seric { 956416Seric printf("%12s: not being edited by you\n", fn); 957585Seric return (FALSE); 958396Seric } 959396Seric } 9601432Seric 9611432Seric /* 962*1433Seric ** DODIFF -- diff an s-file against a g-file 963*1433Seric ** 964*1433Seric ** Parameters: 965*1433Seric ** getv -- argv for the 'get' command. 966*1433Seric ** gfile -- name of the g-file to diff against. 967*1433Seric ** 968*1433Seric ** Returns: 969*1433Seric ** Result of get. 970*1433Seric ** 971*1433Seric ** Side Effects: 972*1433Seric ** none. 973*1433Seric */ 974*1433Seric 975*1433Seric dodiff(getv, gfile) 976*1433Seric char **getv; 977*1433Seric char *gfile; 978*1433Seric { 979*1433Seric int pipev[2]; 980*1433Seric int rval; 981*1433Seric register int i; 982*1433Seric register int pid; 983*1433Seric auto int st; 984*1433Seric extern int errno; 985*1433Seric int (*osig)(); 986*1433Seric 987*1433Seric if (pipe(pipev) < 0) 988*1433Seric { 989*1433Seric syserr("dodiff: pipe failed"); 990*1433Seric exit(EX_OSERR); 991*1433Seric } 992*1433Seric if ((pid = fork()) < 0) 993*1433Seric { 994*1433Seric syserr("dodiff: fork failed"); 995*1433Seric exit(EX_OSERR); 996*1433Seric } 997*1433Seric else if (pid > 0) 998*1433Seric { 999*1433Seric /* in parent; run get */ 1000*1433Seric OutFile = pipev[1]; 1001*1433Seric close(pipev[0]); 1002*1433Seric rval = command(&getv[1], TRUE, FALSE, "get -s -k -p"); 1003*1433Seric osig = signal(SIGINT, SIG_IGN); 1004*1433Seric while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR) 1005*1433Seric errno = 0; 1006*1433Seric signal(SIGINT, osig); 1007*1433Seric /* ignore result of diff */ 1008*1433Seric } 1009*1433Seric else 1010*1433Seric { 1011*1433Seric /* in child, run diff */ 1012*1433Seric if (close(pipev[1]) < 0 || close(0) < 0 || 1013*1433Seric dup(pipev[0]) != 0 || close(pipev[0]) < 0) 1014*1433Seric { 1015*1433Seric syserr("dodiff: magic failed"); 1016*1433Seric exit(EX_OSERR); 1017*1433Seric } 1018*1433Seric execl(PROGPATH(bdiff), "bdiff", "-", gfile, NULL); 1019*1433Seric # ifndef V6 1020*1433Seric execlp("bdiff", "bdiff", "-", gfile, NULL); 1021*1433Seric execlp("diff", "diff", "-", gfile, NULL); 1022*1433Seric # endif NOT V6 1023*1433Seric syserr("bdiff: cannot execute"); 1024*1433Seric exit(EX_OSERR); 1025*1433Seric } 1026*1433Seric return (rval); 1027*1433Seric } 1028*1433Seric 1029*1433Seric /* 1030396Seric ** GETPFILE -- get an entry from the p-file 1031396Seric ** 1032396Seric ** Parameters: 1033396Seric ** pfp -- p-file file pointer 1034396Seric ** 1035396Seric ** Returns: 1036396Seric ** pointer to p-file struct for next entry 1037396Seric ** NULL on EOF or error 1038396Seric ** 1039396Seric ** Side Effects: 1040396Seric ** Each call wipes out results of previous call. 1041396Seric */ 1042396Seric 1043396Seric struct pfile * 1044396Seric getpfile(pfp) 1045396Seric FILE *pfp; 1046396Seric { 1047396Seric static struct pfile ent; 1048396Seric static char buf[120]; 1049396Seric register char *p; 1050396Seric extern char *nextfield(); 1051396Seric 1052396Seric if (fgets(buf, sizeof buf, pfp) == NULL) 1053396Seric return (NULL); 1054396Seric 1055396Seric ent.p_osid = p = buf; 1056396Seric ent.p_nsid = p = nextfield(p); 1057396Seric ent.p_user = p = nextfield(p); 1058396Seric ent.p_date = p = nextfield(p); 1059396Seric ent.p_time = p = nextfield(p); 1060396Seric if (p == NULL || nextfield(p) != NULL) 1061396Seric return (NULL); 1062396Seric 1063396Seric return (&ent); 1064396Seric } 1065396Seric 1066396Seric 1067396Seric char * 1068396Seric nextfield(p) 1069396Seric register char *p; 1070396Seric { 1071396Seric if (p == NULL || *p == '\0') 1072396Seric return (NULL); 1073396Seric while (*p != ' ' && *p != '\n' && *p != '\0') 1074396Seric p++; 1075396Seric if (*p == '\n' || *p == '\0') 1076396Seric { 1077396Seric *p = '\0'; 1078396Seric return (NULL); 1079396Seric } 1080396Seric *p++ = '\0'; 1081396Seric return (p); 1082396Seric } 10831432Seric 10841432Seric /* 10851205Seric ** USRERR -- issue user-level error 10861205Seric ** 10871205Seric ** Parameters: 10881205Seric ** f -- format string. 10891205Seric ** p1-p3 -- parameters to a printf. 10901205Seric ** 10911205Seric ** Returns: 10921205Seric ** -1 10931205Seric ** 10941205Seric ** Side Effects: 10951205Seric ** none. 10961205Seric */ 10971205Seric 10981205Seric usrerr(f, p1, p2, p3) 10991205Seric char *f; 11001205Seric { 11011205Seric fprintf(stderr, "\n%s: ", MyName); 11021205Seric fprintf(stderr, f, p1, p2, p3); 11031205Seric fprintf(stderr, "\n"); 11041205Seric 11051205Seric return (-1); 11061205Seric } 11071432Seric 11081432Seric /* 11091205Seric ** SYSERR -- print system-generated error. 11101205Seric ** 11111205Seric ** Parameters: 11121205Seric ** f -- format string to a printf. 11131205Seric ** p1, p2, p3 -- parameters to f. 11141205Seric ** 11151205Seric ** Returns: 11161205Seric ** never. 11171205Seric ** 11181205Seric ** Side Effects: 11191205Seric ** none. 11201205Seric */ 11211205Seric 11221205Seric syserr(f, p1, p2, p3) 11231205Seric char *f; 11241205Seric { 11251205Seric extern int errno; 11261205Seric 11271205Seric fprintf(stderr, "\n%s SYSERR: ", MyName); 11281205Seric fprintf(stderr, f, p1, p2, p3); 11291205Seric fprintf(stderr, "\n"); 11301205Seric if (errno == 0) 11311205Seric exit(EX_SOFTWARE); 11321205Seric else 11331205Seric { 11341205Seric perror(0); 11351205Seric exit(EX_OSERR); 11361205Seric } 11371205Seric } 1138