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. 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*1435Seric static char SccsId[] = "@(#)sccs.c 1.37 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) */ 1371433Seric # 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, 1771433Seric "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 1971433Seric 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(); 306*1435Seric extern char *tail(); 307200Seric 308393Seric # ifdef DEBUG 309393Seric if (Debug) 310393Seric { 3111316Seric printf("command:\n\t\"%s\"\n", arg0); 3121316Seric for (np = argv; *np != NULL; np++) 3131316Seric printf("\t\"%s\"\n", *np); 314393Seric } 315393Seric # endif 316393Seric 317157Seric /* 3181316Seric ** Copy arguments. 3191316Seric ** Phase one -- from arg0 & if necessary argv[0]. 3201316Seric */ 3211316Seric 3221431Seric np = ap = &nav[1]; 3231316Seric for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) 3241316Seric { 3251316Seric *np++ = q; 3261316Seric while (*p == ' ') 3271316Seric p++; 3281316Seric while (*p != ' ' && *p != '\0' && *p != '/') 3291316Seric *q++ = *p++; 3301316Seric *q++ = '\0'; 3311316Seric } 3321316Seric *np = NULL; 3331431Seric if (*ap == NULL) 3341316Seric *np++ = *argv++; 3351316Seric 3361316Seric /* 337148Seric ** Look up command. 3381431Seric ** At this point, *ap is the command name. 339148Seric */ 340148Seric 3411431Seric cmd = lookup(*ap); 342262Seric if (cmd == NULL) 343148Seric { 3441431Seric usrerr("Unknown command \"%s\"", *ap); 3451282Seric return (EX_USAGE); 346148Seric } 347148Seric 348148Seric /* 3491316Seric ** Copy remaining arguments doing editing as appropriate. 3501316Seric */ 3511316Seric 3521316Seric for (; *argv != NULL; argv++) 3531316Seric { 3541316Seric p = *argv; 3551316Seric if (*p == '-') 3561316Seric { 3571316Seric if (p[1] == '\0' || !editflag || cmd->sccsklets == NULL || 3581316Seric index(cmd->sccsklets, p[1]) != NULL) 3591316Seric *np++ = p; 3601316Seric } 3611316Seric else 3621316Seric { 3631316Seric if (!bitset(NO_SDOT, cmd->sccsflags)) 3641316Seric p = makefile(p); 3651316Seric if (p != NULL) 3661316Seric *np++ = p; 3671316Seric } 3681316Seric } 3691316Seric *np = NULL; 3701316Seric 3711316Seric /* 372200Seric ** Interpret operation associated with this command. 373157Seric */ 374157Seric 375200Seric switch (cmd->sccsoper) 376200Seric { 3771431Seric case SHELL: /* call a shell file */ 3781431Seric *ap = cmd->sccspath; 3791431Seric *--ap = "sh"; 3801431Seric rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag); 3811431Seric break; 3821431Seric 383200Seric case PROG: /* call an sccs prog */ 3841431Seric rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); 385201Seric break; 386201Seric 387201Seric case CMACRO: /* command macro */ 388201Seric for (p = cmd->sccspath; *p != '\0'; p++) 389201Seric { 3901316Seric q = p; 3911316Seric while (*p != '\0' && *p != '/') 3921316Seric p++; 3931431Seric rval = command(&ap[1], *p != '\0', TRUE, q); 3941282Seric if (rval != 0) 3951282Seric break; 396201Seric } 3971282Seric break; 398157Seric 399226Seric case FIX: /* fix a delta */ 4001431Seric if (strncmp(ap[1], "-r", 2) != 0) 401226Seric { 4021205Seric usrerr("-r flag needed for fix command"); 4031282Seric rval = EX_USAGE; 404226Seric break; 405226Seric } 4061431Seric rval = command(&ap[1], TRUE, TRUE, "get -k"); 4071282Seric if (rval == 0) 4081431Seric rval = command(&ap[1], TRUE, TRUE, "rmdel"); 4091282Seric if (rval == 0) 4101431Seric rval = command(&ap[2], FALSE, TRUE, "get -e -g"); 4111282Seric break; 412226Seric 413261Seric case CLEAN: 4141282Seric rval = clean((int) cmd->sccspath); 415261Seric break; 416261Seric 417396Seric case UNEDIT: 4181431Seric for (argv = np = &ap[1]; *argv != NULL; argv++) 419585Seric { 4201316Seric if (unedit(*argv)) 4211316Seric *np++ = *argv; 422585Seric } 4231316Seric *np = NULL; 424585Seric if (i > 0) 4251431Seric rval = command(&ap[1], FALSE, FALSE, "get"); 426396Seric break; 427396Seric 4281433Seric case DIFFS: /* diff between s-file & edit file */ 4291433Seric /* find the end of the flag arguments */ 4301433Seric for (np = &ap[1]; *np != NULL && **np == '-'; np++) 4311433Seric continue; 4321433Seric argv = np; 4331433Seric 4341433Seric /* for each file, do the diff */ 4351433Seric while (*np != NULL) 4361433Seric { 4371433Seric *argv = *np++; 4381433Seric p = *np; 4391433Seric *np = NULL; 440*1435Seric i = dodiff(ap, tail(*argv)); 4411433Seric if (rval == 0) 4421433Seric rval = i; 4431433Seric *np = p; 4441433Seric } 4451433Seric break; 4461433Seric 447200Seric default: 4481205Seric syserr("oper %d", cmd->sccsoper); 449200Seric exit(EX_SOFTWARE); 450200Seric } 4511282Seric # ifdef DEBUG 4521282Seric if (Debug) 4531282Seric printf("command: rval=%d\n", rval); 4541282Seric # endif 4551282Seric return (rval); 456200Seric } 4571432Seric 4581432Seric /* 459262Seric ** LOOKUP -- look up an SCCS command name. 460262Seric ** 461262Seric ** Parameters: 462262Seric ** name -- the name of the command to look up. 463262Seric ** 464262Seric ** Returns: 465262Seric ** ptr to command descriptor for this command. 466262Seric ** NULL if no such entry. 467262Seric ** 468262Seric ** Side Effects: 469262Seric ** none. 470262Seric */ 471200Seric 472262Seric struct sccsprog * 473262Seric lookup(name) 474262Seric char *name; 475262Seric { 476262Seric register struct sccsprog *cmd; 477226Seric 478262Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 479262Seric { 480262Seric if (strcmp(cmd->sccsname, name) == 0) 481262Seric return (cmd); 482262Seric } 483262Seric return (NULL); 484262Seric } 4851432Seric 4861432Seric /* 4871282Seric ** CALLPROG -- call a program 4881282Seric ** 4891316Seric ** Used to call the SCCS programs. 4901282Seric ** 4911282Seric ** Parameters: 4921282Seric ** progpath -- pathname of the program to call. 4931282Seric ** flags -- status flags from the command descriptors. 4941282Seric ** argv -- an argument vector to pass to the program. 4951282Seric ** forkflag -- if true, fork before calling, else just 4961282Seric ** exec. 4971282Seric ** 4981282Seric ** Returns: 4991282Seric ** The exit status of the program. 5001282Seric ** Nothing if forkflag == FALSE. 5011282Seric ** 5021282Seric ** Side Effects: 5031282Seric ** Can exit if forkflag == FALSE. 5041282Seric */ 505226Seric 506200Seric callprog(progpath, flags, argv, forkflag) 507200Seric char *progpath; 508200Seric short flags; 509200Seric char **argv; 510200Seric bool forkflag; 511200Seric { 512200Seric register int i; 513201Seric auto int st; 514200Seric 5151316Seric # ifdef DEBUG 5161316Seric if (Debug) 5171316Seric { 5181316Seric printf("callprog:\n"); 5191316Seric for (i = 0; argv[i] != NULL; i++) 5201316Seric printf("\t\"%s\"\n", argv[i]); 5211316Seric } 5221316Seric # endif 5231316Seric 524200Seric if (*argv == NULL) 525200Seric return (-1); 526200Seric 527157Seric /* 528226Seric ** Fork if appropriate. 529148Seric */ 530148Seric 531200Seric if (forkflag) 532200Seric { 533393Seric # ifdef DEBUG 534393Seric if (Debug) 535393Seric printf("Forking\n"); 536393Seric # endif 537200Seric i = fork(); 538200Seric if (i < 0) 539200Seric { 5401205Seric syserr("cannot fork"); 541200Seric exit(EX_OSERR); 542200Seric } 543200Seric else if (i > 0) 544201Seric { 545201Seric wait(&st); 5461282Seric if ((st & 0377) == 0) 5471282Seric st = (st >> 8) & 0377; 5481433Seric if (OutFile >= 0) 5491433Seric { 5501433Seric close(OutFile); 5511433Seric OutFile = -1; 5521433Seric } 553201Seric return (st); 554201Seric } 555200Seric } 5561433Seric else if (OutFile >= 0) 5571433Seric { 5581433Seric syserr("callprog: setting stdout w/o forking"); 5591433Seric exit(EX_SOFTWARE); 5601433Seric } 561200Seric 5621433Seric /* set protection as appropriate */ 563200Seric if (bitset(REALUSER, flags)) 564200Seric setuid(getuid()); 5651433Seric 5661433Seric /* change standard input & output if needed */ 5671433Seric if (OutFile >= 0) 5681433Seric { 5691433Seric close(1); 5701433Seric dup(OutFile); 5711433Seric close(OutFile); 5721433Seric } 573226Seric 5741433Seric /* call real SCCS program */ 575226Seric execv(progpath, argv); 5761205Seric syserr("cannot execute %s", progpath); 577148Seric exit(EX_UNAVAILABLE); 578148Seric } 5791432Seric 5801432Seric /* 581586Seric ** MAKEFILE -- make filename of SCCS file 582586Seric ** 583586Seric ** If the name passed is already the name of an SCCS file, 584586Seric ** just return it. Otherwise, munge the name into the name 585586Seric ** of the actual SCCS file. 586586Seric ** 587586Seric ** There are cases when it is not clear what you want to 588586Seric ** do. For example, if SccsPath is an absolute pathname 589586Seric ** and the name given is also an absolute pathname, we go 590586Seric ** for SccsPath (& only use the last component of the name 591586Seric ** passed) -- this is important for security reasons (if 592586Seric ** sccs is being used as a setuid front end), but not 593586Seric ** particularly intuitive. 594586Seric ** 595586Seric ** Parameters: 596586Seric ** name -- the file name to be munged. 597586Seric ** 598586Seric ** Returns: 599586Seric ** The pathname of the sccs file. 600586Seric ** NULL on error. 601586Seric ** 602586Seric ** Side Effects: 603586Seric ** none. 604586Seric */ 605148Seric 606148Seric char * 607148Seric makefile(name) 608148Seric char *name; 609148Seric { 610148Seric register char *p; 611148Seric register char c; 612148Seric char buf[512]; 613588Seric struct stat stbuf; 614148Seric extern char *malloc(); 615586Seric extern char *rindex(); 616588Seric extern bool safepath(); 617587Seric extern bool isdir(); 618587Seric register char *q; 619148Seric 620586Seric p = rindex(name, '/'); 621586Seric if (p == NULL) 622586Seric p = name; 623586Seric else 624586Seric p++; 625586Seric 626148Seric /* 627588Seric ** Check to see that the path is "safe", i.e., that we 628588Seric ** are not letting some nasty person use the setuid part 629588Seric ** of this program to look at or munge some presumably 630588Seric ** hidden files. 631148Seric */ 632148Seric 633588Seric if (SccsDir[0] == '/' && !safepath(name)) 634588Seric return (NULL); 635586Seric 636586Seric /* 637588Seric ** Create the base pathname. 638586Seric */ 639586Seric 640588Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) 641148Seric { 642588Seric strcpy(buf, SccsDir); 643586Seric strcat(buf, "/"); 644586Seric } 645586Seric else 646586Seric strcpy(buf, ""); 647587Seric strncat(buf, name, p - name); 648587Seric q = &buf[strlen(buf)]; 649587Seric strcpy(q, p); 650587Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 651586Seric { 652588Seric strcpy(q, SccsPath); 653588Seric strcat(buf, "/s."); 654586Seric strcat(buf, p); 655586Seric } 656148Seric 657588Seric if (strcmp(buf, name) == 0) 658588Seric p = name; 659588Seric else 660148Seric { 661588Seric p = malloc(strlen(buf) + 1); 662588Seric if (p == NULL) 663588Seric { 664588Seric perror("Sccs: no mem"); 665588Seric exit(EX_OSERR); 666588Seric } 667588Seric strcpy(p, buf); 668148Seric } 669148Seric return (p); 670148Seric } 6711432Seric 6721432Seric /* 673587Seric ** ISDIR -- return true if the argument is a directory. 674587Seric ** 675587Seric ** Parameters: 676587Seric ** name -- the pathname of the file to check. 677587Seric ** 678587Seric ** Returns: 679587Seric ** TRUE if 'name' is a directory, FALSE otherwise. 680587Seric ** 681587Seric ** Side Effects: 682587Seric ** none. 683587Seric */ 684587Seric 685587Seric bool 686587Seric isdir(name) 687587Seric char *name; 688587Seric { 689587Seric struct stat stbuf; 690587Seric 691587Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 692587Seric } 6931432Seric 6941432Seric /* 695586Seric ** SAFEPATH -- determine whether a pathname is "safe" 696586Seric ** 697586Seric ** "Safe" pathnames only allow you to get deeper into the 698586Seric ** directory structure, i.e., full pathnames and ".." are 699586Seric ** not allowed. 700586Seric ** 701586Seric ** Parameters: 702586Seric ** p -- the name to check. 703586Seric ** 704586Seric ** Returns: 705586Seric ** TRUE -- if the path is safe. 706586Seric ** FALSE -- if the path is not safe. 707586Seric ** 708586Seric ** Side Effects: 709586Seric ** Prints a message if the path is not safe. 710586Seric */ 711586Seric 712586Seric bool 713586Seric safepath(p) 714586Seric register char *p; 715586Seric { 716586Seric extern char *index(); 717586Seric 718586Seric if (*p != '/') 719586Seric { 720586Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 721586Seric { 722586Seric p = index(p, '/'); 723586Seric if (p == NULL) 724586Seric return (TRUE); 725586Seric p++; 726586Seric } 727586Seric } 728586Seric 729586Seric printf("You may not use full pathnames or \"..\"\n"); 730586Seric return (FALSE); 731586Seric } 7321432Seric 7331432Seric /* 734261Seric ** CLEAN -- clean out recreatable files 735261Seric ** 736261Seric ** Any file for which an "s." file exists but no "p." file 737261Seric ** exists in the current directory is purged. 738261Seric ** 739261Seric ** Parameters: 740819Seric ** tells whether this came from a "clean", "info", or 741819Seric ** "check" command. 742261Seric ** 743261Seric ** Returns: 744261Seric ** none. 745261Seric ** 746261Seric ** Side Effects: 747819Seric ** Removes files in the current directory. 748819Seric ** Prints information regarding files being edited. 749819Seric ** Exits if a "check" command. 750261Seric */ 751261Seric 752819Seric clean(mode) 753819Seric int mode; 754261Seric { 755261Seric struct direct dir; 756261Seric struct stat stbuf; 757261Seric char buf[100]; 758394Seric char pline[120]; 759346Seric register FILE *dirfd; 760346Seric register char *basefile; 761351Seric bool gotedit; 762394Seric FILE *pfp; 763261Seric 7641207Seric strcpy(buf, SccsDir); 7651207Seric if (buf[0] != '\0') 7661207Seric strcat(buf, "/"); 7671207Seric strcat(buf, SccsPath); 7681207Seric dirfd = fopen(buf, "r"); 769261Seric if (dirfd == NULL) 770261Seric { 7711207Seric usrerr("cannot open %s", buf); 7721282Seric return (EX_NOINPUT); 773261Seric } 774261Seric 775261Seric /* 776261Seric ** Scan the SCCS directory looking for s. files. 777261Seric */ 778261Seric 779351Seric gotedit = FALSE; 780261Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 781261Seric { 782568Seric if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) 783261Seric continue; 784261Seric 785261Seric /* got an s. file -- see if the p. file exists */ 7861207Seric strcpy(buf, SccsDir); 7871207Seric if (buf[0] != '\0') 7881207Seric strcat(buf, "/"); 7891207Seric strcat(buf, SccsPath); 790261Seric strcat(buf, "/p."); 791346Seric basefile = &buf[strlen(buf)]; 792568Seric strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 793346Seric basefile[sizeof dir.d_name - 2] = '\0'; 794394Seric pfp = fopen(buf, "r"); 795394Seric if (pfp != NULL) 796346Seric { 797394Seric while (fgets(pline, sizeof pline, pfp) != NULL) 798416Seric printf("%12s: being edited: %s", basefile, pline); 799394Seric fclose(pfp); 800351Seric gotedit = TRUE; 801261Seric continue; 802346Seric } 803261Seric 804261Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 805819Seric if (mode == CLEANC) 806346Seric { 807568Seric strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); 808346Seric buf[sizeof dir.d_name - 2] = '\0'; 809346Seric unlink(buf); 810346Seric } 811261Seric } 812261Seric 813261Seric fclose(dirfd); 814819Seric if (!gotedit && mode == INFOC) 815416Seric printf("Nothing being edited\n"); 816819Seric if (mode == CHECKC) 817819Seric exit(gotedit); 8181282Seric return (EX_OK); 819261Seric } 8201432Seric 8211432Seric /* 822396Seric ** UNEDIT -- unedit a file 823396Seric ** 824396Seric ** Checks to see that the current user is actually editting 825396Seric ** the file and arranges that s/he is not editting it. 826396Seric ** 827396Seric ** Parameters: 828416Seric ** fn -- the name of the file to be unedited. 829396Seric ** 830396Seric ** Returns: 831585Seric ** TRUE -- if the file was successfully unedited. 832585Seric ** FALSE -- if the file was not unedited for some 833585Seric ** reason. 834396Seric ** 835396Seric ** Side Effects: 836396Seric ** fn is removed 837396Seric ** entries are removed from pfile. 838396Seric */ 839396Seric 840585Seric bool 841396Seric unedit(fn) 842396Seric char *fn; 843396Seric { 844396Seric register FILE *pfp; 845396Seric char *pfn; 846396Seric static char tfn[] = "/tmp/sccsXXXXX"; 847396Seric FILE *tfp; 848396Seric register char *p; 849396Seric register char *q; 850396Seric bool delete = FALSE; 851396Seric bool others = FALSE; 852396Seric char *myname; 853396Seric extern char *getlogin(); 854396Seric struct pfile *pent; 855396Seric extern struct pfile *getpfile(); 856396Seric char buf[120]; 8571316Seric extern char *makefile(); 858828Seric # ifdef UIDUSER 859828Seric struct passwd *pw; 860828Seric extern struct passwd *getpwuid(); 861828Seric # endif UIDUSER 862396Seric 863396Seric /* make "s." filename & find the trailing component */ 864396Seric pfn = makefile(fn); 865586Seric if (pfn == NULL) 866586Seric return (FALSE); 867586Seric q = rindex(pfn, '/'); 868586Seric if (q == NULL) 869586Seric q = &pfn[-1]; 870586Seric if (q[1] != 's' || q[2] != '.') 871396Seric { 8721205Seric usrerr("bad file name \"%s\"", fn); 873585Seric return (FALSE); 874396Seric } 875396Seric 876396Seric /* turn "s." into "p." */ 877396Seric *++q = 'p'; 878396Seric 879396Seric pfp = fopen(pfn, "r"); 880396Seric if (pfp == NULL) 881396Seric { 882416Seric printf("%12s: not being edited\n", fn); 883585Seric return (FALSE); 884396Seric } 885396Seric 886396Seric /* 887396Seric ** Copy p-file to temp file, doing deletions as needed. 888396Seric */ 889396Seric 890396Seric mktemp(tfn); 891396Seric tfp = fopen(tfn, "w"); 892396Seric if (tfp == NULL) 893396Seric { 8941205Seric usrerr("cannot create \"%s\"", tfn); 895396Seric exit(EX_OSERR); 896396Seric } 897396Seric 898828Seric # ifdef UIDUSER 899828Seric pw = getpwuid(getuid()); 900828Seric if (pw == NULL) 901828Seric { 9021205Seric syserr("who are you? (uid=%d)", getuid()); 903828Seric exit(EX_OSERR); 904828Seric } 905828Seric myname = pw->pw_name; 906828Seric # else 907396Seric myname = getlogin(); 908828Seric # endif UIDUSER 909396Seric while ((pent = getpfile(pfp)) != NULL) 910396Seric { 911396Seric if (strcmp(pent->p_user, myname) == 0) 912396Seric { 913396Seric /* a match */ 914396Seric delete++; 915396Seric } 916396Seric else 917396Seric { 918396Seric fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, 919396Seric pent->p_nsid, pent->p_user, pent->p_date, 920396Seric pent->p_time); 921396Seric others++; 922396Seric } 923396Seric } 924396Seric 925396Seric /* do final cleanup */ 926396Seric if (others) 927396Seric { 928396Seric if (freopen(tfn, "r", tfp) == NULL) 929396Seric { 9301205Seric syserr("cannot reopen \"%s\"", tfn); 931396Seric exit(EX_OSERR); 932396Seric } 933396Seric if (freopen(pfn, "w", pfp) == NULL) 934396Seric { 9351205Seric usrerr("cannot create \"%s\"", pfn); 936585Seric return (FALSE); 937396Seric } 938396Seric while (fgets(buf, sizeof buf, tfp) != NULL) 939396Seric fputs(buf, pfp); 940396Seric } 941396Seric else 942396Seric { 943396Seric unlink(pfn); 944396Seric } 945396Seric fclose(tfp); 946396Seric fclose(pfp); 947396Seric unlink(tfn); 948396Seric 949396Seric if (delete) 950396Seric { 951*1435Seric unlink(tail(fn)); 952*1435Seric printf("%12s: removed\n", tail(fn)); 953585Seric return (TRUE); 954396Seric } 955396Seric else 956396Seric { 957416Seric printf("%12s: not being edited by you\n", fn); 958585Seric return (FALSE); 959396Seric } 960396Seric } 9611432Seric 9621432Seric /* 9631433Seric ** DODIFF -- diff an s-file against a g-file 9641433Seric ** 9651433Seric ** Parameters: 9661433Seric ** getv -- argv for the 'get' command. 9671433Seric ** gfile -- name of the g-file to diff against. 9681433Seric ** 9691433Seric ** Returns: 9701433Seric ** Result of get. 9711433Seric ** 9721433Seric ** Side Effects: 9731433Seric ** none. 9741433Seric */ 9751433Seric 9761433Seric dodiff(getv, gfile) 9771433Seric char **getv; 9781433Seric char *gfile; 9791433Seric { 9801433Seric int pipev[2]; 9811433Seric int rval; 9821433Seric register int i; 9831433Seric register int pid; 9841433Seric auto int st; 9851433Seric extern int errno; 9861433Seric int (*osig)(); 9871433Seric 9881433Seric if (pipe(pipev) < 0) 9891433Seric { 9901433Seric syserr("dodiff: pipe failed"); 9911433Seric exit(EX_OSERR); 9921433Seric } 9931433Seric if ((pid = fork()) < 0) 9941433Seric { 9951433Seric syserr("dodiff: fork failed"); 9961433Seric exit(EX_OSERR); 9971433Seric } 9981433Seric else if (pid > 0) 9991433Seric { 10001433Seric /* in parent; run get */ 10011433Seric OutFile = pipev[1]; 10021433Seric close(pipev[0]); 10031433Seric rval = command(&getv[1], TRUE, FALSE, "get -s -k -p"); 10041433Seric osig = signal(SIGINT, SIG_IGN); 10051433Seric while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR) 10061433Seric errno = 0; 10071433Seric signal(SIGINT, osig); 10081433Seric /* ignore result of diff */ 10091433Seric } 10101433Seric else 10111433Seric { 10121433Seric /* in child, run diff */ 10131433Seric if (close(pipev[1]) < 0 || close(0) < 0 || 10141433Seric dup(pipev[0]) != 0 || close(pipev[0]) < 0) 10151433Seric { 10161433Seric syserr("dodiff: magic failed"); 10171433Seric exit(EX_OSERR); 10181433Seric } 10191433Seric execl(PROGPATH(bdiff), "bdiff", "-", gfile, NULL); 10201433Seric # ifndef V6 10211433Seric execlp("bdiff", "bdiff", "-", gfile, NULL); 10221433Seric execlp("diff", "diff", "-", gfile, NULL); 10231433Seric # endif NOT V6 10241433Seric syserr("bdiff: cannot execute"); 10251433Seric exit(EX_OSERR); 10261433Seric } 10271433Seric return (rval); 10281433Seric } 10291433Seric 10301433Seric /* 1031*1435Seric ** TAIL -- return tail of filename. 1032*1435Seric ** 1033*1435Seric ** Parameters: 1034*1435Seric ** fn -- the filename. 1035*1435Seric ** 1036*1435Seric ** Returns: 1037*1435Seric ** a pointer to the tail of the filename; e.g., given 1038*1435Seric ** "cmd/ls.c", "ls.c" is returned. 1039*1435Seric ** 1040*1435Seric ** Side Effects: 1041*1435Seric ** none. 1042*1435Seric */ 1043*1435Seric 1044*1435Seric char * 1045*1435Seric tail(fn) 1046*1435Seric register char *fn; 1047*1435Seric { 1048*1435Seric register char *p; 1049*1435Seric 1050*1435Seric for (p = fn; *p != 0; p++) 1051*1435Seric if (*p == '/' && p[1] != '\0' && p[1] != '/') 1052*1435Seric fn = &p[1]; 1053*1435Seric return (fn); 1054*1435Seric } 1055*1435Seric 1056*1435Seric /* 1057396Seric ** GETPFILE -- get an entry from the p-file 1058396Seric ** 1059396Seric ** Parameters: 1060396Seric ** pfp -- p-file file pointer 1061396Seric ** 1062396Seric ** Returns: 1063396Seric ** pointer to p-file struct for next entry 1064396Seric ** NULL on EOF or error 1065396Seric ** 1066396Seric ** Side Effects: 1067396Seric ** Each call wipes out results of previous call. 1068396Seric */ 1069396Seric 1070396Seric struct pfile * 1071396Seric getpfile(pfp) 1072396Seric FILE *pfp; 1073396Seric { 1074396Seric static struct pfile ent; 1075396Seric static char buf[120]; 1076396Seric register char *p; 1077396Seric extern char *nextfield(); 1078396Seric 1079396Seric if (fgets(buf, sizeof buf, pfp) == NULL) 1080396Seric return (NULL); 1081396Seric 1082396Seric ent.p_osid = p = buf; 1083396Seric ent.p_nsid = p = nextfield(p); 1084396Seric ent.p_user = p = nextfield(p); 1085396Seric ent.p_date = p = nextfield(p); 1086396Seric ent.p_time = p = nextfield(p); 1087396Seric if (p == NULL || nextfield(p) != NULL) 1088396Seric return (NULL); 1089396Seric 1090396Seric return (&ent); 1091396Seric } 1092396Seric 1093396Seric 1094396Seric char * 1095396Seric nextfield(p) 1096396Seric register char *p; 1097396Seric { 1098396Seric if (p == NULL || *p == '\0') 1099396Seric return (NULL); 1100396Seric while (*p != ' ' && *p != '\n' && *p != '\0') 1101396Seric p++; 1102396Seric if (*p == '\n' || *p == '\0') 1103396Seric { 1104396Seric *p = '\0'; 1105396Seric return (NULL); 1106396Seric } 1107396Seric *p++ = '\0'; 1108396Seric return (p); 1109396Seric } 11101432Seric 11111432Seric /* 11121205Seric ** USRERR -- issue user-level error 11131205Seric ** 11141205Seric ** Parameters: 11151205Seric ** f -- format string. 11161205Seric ** p1-p3 -- parameters to a printf. 11171205Seric ** 11181205Seric ** Returns: 11191205Seric ** -1 11201205Seric ** 11211205Seric ** Side Effects: 11221205Seric ** none. 11231205Seric */ 11241205Seric 11251205Seric usrerr(f, p1, p2, p3) 11261205Seric char *f; 11271205Seric { 11281205Seric fprintf(stderr, "\n%s: ", MyName); 11291205Seric fprintf(stderr, f, p1, p2, p3); 11301205Seric fprintf(stderr, "\n"); 11311205Seric 11321205Seric return (-1); 11331205Seric } 11341432Seric 11351432Seric /* 11361205Seric ** SYSERR -- print system-generated error. 11371205Seric ** 11381205Seric ** Parameters: 11391205Seric ** f -- format string to a printf. 11401205Seric ** p1, p2, p3 -- parameters to f. 11411205Seric ** 11421205Seric ** Returns: 11431205Seric ** never. 11441205Seric ** 11451205Seric ** Side Effects: 11461205Seric ** none. 11471205Seric */ 11481205Seric 11491205Seric syserr(f, p1, p2, p3) 11501205Seric char *f; 11511205Seric { 11521205Seric extern int errno; 11531205Seric 11541205Seric fprintf(stderr, "\n%s SYSERR: ", MyName); 11551205Seric fprintf(stderr, f, p1, p2, p3); 11561205Seric fprintf(stderr, "\n"); 11571205Seric if (errno == 0) 11581205Seric exit(EX_SOFTWARE); 11591205Seric else 11601205Seric { 11611205Seric perror(0); 11621205Seric exit(EX_OSERR); 11631205Seric } 11641205Seric } 1165