121574Sdist /* 221574Sdist * Copyright (c) 1980 Regents of the University of California. 334204Sbostic * All rights reserved. 434204Sbostic * 5*42765Sbostic * %sccs.include.redist.c% 621574Sdist */ 721574Sdist 813622Ssam #ifndef lint 921574Sdist char copyright[] = 1021574Sdist "@(#) Copyright (c) 1980 Regents of the University of California.\n\ 1121574Sdist All rights reserved.\n"; 1234204Sbostic #endif /* not lint */ 1313622Ssam 1421574Sdist #ifndef lint 15*42765Sbostic static char sccsid[] = "@(#)sccs.c 5.9 (Berkeley) 06/01/90"; 1634204Sbostic #endif /* not lint */ 1721574Sdist 18148Seric # include <stdio.h> 196784Smckusick # include <sys/param.h> 20148Seric # include <sys/stat.h> 2113622Ssam # include <sys/dir.h> 221433Seric # include <errno.h> 231433Seric # include <signal.h> 24148Seric # include <sysexits.h> 252140Seric # include <pwd.h> 2637921Sbostic # include "pathnames.h" 27148Seric 28828Seric /* 29828Seric ** SCCS.C -- human-oriented front end to the SCCS system. 30828Seric ** 31828Seric ** Without trying to add any functionality to speak of, this 32828Seric ** program tries to make SCCS a little more accessible to human 33828Seric ** types. The main thing it does is automatically put the 34828Seric ** string "SCCS/s." on the front of names. Also, it has a 35828Seric ** couple of things that are designed to shorten frequent 36828Seric ** combinations, e.g., "delget" which expands to a "delta" 37828Seric ** and a "get". 38828Seric ** 39828Seric ** This program can also function as a setuid front end. 40828Seric ** To do this, you should copy the source, renaming it to 41828Seric ** whatever you want, e.g., "syssccs". Change any defaults 42828Seric ** in the program (e.g., syssccs might default -d to 43828Seric ** "/usr/src/sys"). Then recompile and put the result 44828Seric ** as setuid to whomever you want. In this mode, sccs 45828Seric ** knows to not run setuid for certain programs in order 46828Seric ** to preserve security, and so forth. 47828Seric ** 48828Seric ** Usage: 49828Seric ** sccs [flags] command [args] 50828Seric ** 51828Seric ** Flags: 52828Seric ** -d<dir> <dir> represents a directory to search 53828Seric ** out of. It should be a full pathname 54828Seric ** for general usage. E.g., if <dir> is 55828Seric ** "/usr/src/sys", then a reference to the 56828Seric ** file "dev/bio.c" becomes a reference to 57828Seric ** "/usr/src/sys/dev/bio.c". 58828Seric ** -p<path> prepends <path> to the final component 59828Seric ** of the pathname. By default, this is 60828Seric ** "SCCS". For example, in the -d example 61828Seric ** above, the path then gets modified to 62828Seric ** "/usr/src/sys/dev/SCCS/s.bio.c". In 63828Seric ** more common usage (without the -d flag), 64828Seric ** "prog.c" would get modified to 65828Seric ** "SCCS/s.prog.c". In both cases, the 66828Seric ** "s." gets automatically prepended. 67828Seric ** -r run as the real user. 68828Seric ** 69828Seric ** Commands: 70828Seric ** admin, 71828Seric ** get, 72828Seric ** delta, 73828Seric ** rmdel, 7430959Sbostic ** cdc, 75828Seric ** etc. Straight out of SCCS; only difference 76828Seric ** is that pathnames get modified as 77828Seric ** described above. 7830959Sbostic ** enter Front end doing "sccs admin -i<name> <name>" 7930959Sbostic ** create Macro for "enter" followed by "get". 80828Seric ** edit Macro for "get -e". 81828Seric ** unedit Removes a file being edited, knowing 82828Seric ** about p-files, etc. 83828Seric ** delget Macro for "delta" followed by "get". 84828Seric ** deledit Macro for "delta" followed by "get -e". 8530959Sbostic ** branch Macro for "get -b -e", followed by "delta 8630959Sbostic ** -s -n", followd by "get -e -t -g". 8730959Sbostic ** diffs "diff" the specified version of files 8830959Sbostic ** and the checked-out version. 8930959Sbostic ** print Macro for "prs -e" followed by "get -p -m". 9030959Sbostic ** tell List what files are being edited. 9130959Sbostic ** info Print information about files being edited. 92828Seric ** clean Remove all files that can be 93828Seric ** regenerated from SCCS files. 941205Seric ** check Like info, but return exit status, for 95828Seric ** use in makefiles. 96828Seric ** fix Remove a top delta & reedit, but save 97828Seric ** the previous changes in that delta. 98828Seric ** 99828Seric ** Compilation Flags: 100828Seric ** UIDUSER -- determine who the user is by looking at the 101828Seric ** uid rather than the login name -- for machines 102828Seric ** where SCCS gets the user in this way. 1031270Seric ** SCCSDIR -- if defined, forces the -d flag to take on 1041205Seric ** this value. This is so that the setuid 1051205Seric ** aspects of this program cannot be abused. 1061270Seric ** This flag also disables the -p flag. 1071270Seric ** SCCSPATH -- the default for the -p flag. 1081437Seric ** MYNAME -- the title this program should print when it 1091437Seric ** gives error messages. 110828Seric ** 111828Seric ** Compilation Instructions: 112828Seric ** cc -O -n -s sccs.c 1131437Seric ** The flags listed above can be -D defined to simplify 1141437Seric ** recompilation for variant versions. 115828Seric ** 116828Seric ** Author: 117828Seric ** Eric Allman, UCB/INGRES 1181270Seric ** Copyright 1980 Regents of the University of California 119828Seric */ 120155Seric 1211432Seric 1221270Seric /******************* Configuration Information ********************/ 1231270Seric 1241437Seric # ifndef SCCSPATH 1251432Seric # define SCCSPATH "SCCS" /* pathname in which to find s-files */ 1261437Seric # endif NOT SCCSPATH 127828Seric 1281437Seric # ifndef MYNAME 1291437Seric # define MYNAME "sccs" /* name used for printing errors */ 1301437Seric # endif NOT MYNAME 1311270Seric 1321270Seric /**************** End of Configuration Information ****************/ 1331432Seric 134157Seric typedef char bool; 135200Seric # define TRUE 1 136200Seric # define FALSE 0 137157Seric 1381438Seric # define bitset(bit, word) ((bool) ((bit) & (word))) 1391438Seric 140148Seric struct sccsprog 141148Seric { 142148Seric char *sccsname; /* name of SCCS routine */ 143200Seric short sccsoper; /* opcode, see below */ 144200Seric short sccsflags; /* flags, see below */ 145148Seric char *sccspath; /* pathname of binary implementing */ 146148Seric }; 147148Seric 148200Seric /* values for sccsoper */ 149200Seric # define PROG 0 /* call a program */ 150201Seric # define CMACRO 1 /* command substitution macro */ 151226Seric # define FIX 2 /* fix a delta */ 152261Seric # define CLEAN 3 /* clean out recreatable files */ 153396Seric # define UNEDIT 4 /* unedit a file */ 1541431Seric # define SHELL 5 /* call a shell file (like PROG) */ 1551433Seric # define DIFFS 6 /* diff between sccs & file out */ 1561871Seric # define DODIFF 7 /* internal call to diff program */ 15710104Srrh # define ENTER 8 /* enter new files */ 158200Seric 159157Seric /* bits for sccsflags */ 160200Seric # define NO_SDOT 0001 /* no s. on front of args */ 161200Seric # define REALUSER 0002 /* protected (e.g., admin) */ 162148Seric 163819Seric /* modes for the "clean", "info", "check" ops */ 164819Seric # define CLEANC 0 /* clean command */ 165819Seric # define INFOC 1 /* info command */ 166819Seric # define CHECKC 2 /* check command */ 1671730Seric # define TELLC 3 /* give list of files being edited */ 168819Seric 1691432Seric /* 1701432Seric ** Description of commands known to this program. 1711432Seric ** First argument puts the command into a class. Second arg is 1721432Seric ** info regarding treatment of this command. Third arg is a 1731432Seric ** list of flags this command accepts from macros, etc. Fourth 1741432Seric ** arg is the pathname of the implementing program, or the 1751432Seric ** macro definition, or the arg to a sub-algorithm. 1761432Seric */ 177202Seric 178148Seric struct sccsprog SccsProg[] = 179148Seric { 1801864Seric "admin", PROG, REALUSER, PROGPATH(admin), 18130959Sbostic "cdc", PROG, 0, PROGPATH(rmdel), 1821864Seric "comb", PROG, 0, PROGPATH(comb), 1831864Seric "delta", PROG, 0, PROGPATH(delta), 1841864Seric "get", PROG, 0, PROGPATH(get), 1851864Seric "help", PROG, NO_SDOT, PROGPATH(help), 1862136Seric "prs", PROG, 0, PROGPATH(prs), 1871864Seric "prt", PROG, 0, PROGPATH(prt), 1881864Seric "rmdel", PROG, REALUSER, PROGPATH(rmdel), 1892136Seric "val", PROG, 0, PROGPATH(val), 1901864Seric "what", PROG, NO_SDOT, PROGPATH(what), 1911864Seric "sccsdiff", SHELL, REALUSER, PROGPATH(sccsdiff), 1921864Seric "edit", CMACRO, NO_SDOT, "get -e", 1931864Seric "delget", CMACRO, NO_SDOT, "delta:mysrp/get:ixbeskcl -t", 1942138Seric "deledit", CMACRO, NO_SDOT, "delta:mysrp -n/get:ixbskcl -e -t -g", 1951864Seric "fix", FIX, NO_SDOT, NULL, 1961864Seric "clean", CLEAN, REALUSER|NO_SDOT, (char *) CLEANC, 1971864Seric "info", CLEAN, REALUSER|NO_SDOT, (char *) INFOC, 1981864Seric "check", CLEAN, REALUSER|NO_SDOT, (char *) CHECKC, 1991864Seric "tell", CLEAN, REALUSER|NO_SDOT, (char *) TELLC, 2001864Seric "unedit", UNEDIT, NO_SDOT, NULL, 2011864Seric "diffs", DIFFS, NO_SDOT|REALUSER, NULL, 2021871Seric "-diff", DODIFF, NO_SDOT|REALUSER, PROGPATH(bdiff), 20330959Sbostic "print", CMACRO, 0, "prs -e/get -p -m -s", 2042226Seric "branch", CMACRO, NO_SDOT, 2052226Seric "get:ixrc -e -b/delta: -s -n -ybranch-place-holder/get:pl -e -t -g", 20610104Srrh "enter", ENTER, NO_SDOT, NULL, 20710104Srrh "create", CMACRO, NO_SDOT, "enter/get:ixbeskcl -t", 2081864Seric NULL, -1, 0, NULL 209148Seric }; 210148Seric 2111432Seric /* one line from a p-file */ 212396Seric struct pfile 213396Seric { 214396Seric char *p_osid; /* old SID */ 215396Seric char *p_nsid; /* new SID */ 216396Seric char *p_user; /* user who did edit */ 217396Seric char *p_date; /* date of get */ 218396Seric char *p_time; /* time of get */ 2192161Seric char *p_aux; /* extra info at end */ 220396Seric }; 221396Seric 2221270Seric char *SccsPath = SCCSPATH; /* pathname of SCCS files */ 2231270Seric # ifdef SCCSDIR 2241270Seric char *SccsDir = SCCSDIR; /* directory to begin search from */ 2251205Seric # else 2261270Seric char *SccsDir = ""; 2271205Seric # endif 2281437Seric char MyName[] = MYNAME; /* name used in messages */ 2291433Seric int OutFile = -1; /* override output file for commands */ 230157Seric bool RealUser; /* if set, running as real user */ 231393Seric # ifdef DEBUG 232393Seric bool Debug; /* turn on tracing */ 233393Seric # endif 2342139Seric # ifndef V6 2352139Seric extern char *getenv(); 2362139Seric # endif V6 23710110Srrh 23830959Sbostic extern char *sys_siglist[]; 23930959Sbostic 24010110Srrh char *gstrcat(), *strcat(); 24110110Srrh char *gstrncat(), *strncat(); 24210110Srrh char *gstrcpy(), *strcpy(); 24310110Srrh #define FBUFSIZ BUFSIZ 24410110Srrh #define PFILELG 120 2451432Seric 246148Seric main(argc, argv) 247148Seric int argc; 248148Seric char **argv; 249148Seric { 250148Seric register char *p; 251262Seric extern struct sccsprog *lookup(); 2521282Seric register int i; 2532139Seric # ifndef V6 2542139Seric # ifndef SCCSDIR 2552140Seric register struct passwd *pw; 2562140Seric extern struct passwd *getpwnam(); 25710110Srrh char buf[FBUFSIZ]; 2582140Seric 2592139Seric /* pull "SccsDir" out of the environment (possibly) */ 26010260Seric p = getenv("PROJECTDIR"); 2612140Seric if (p != NULL && p[0] != '\0') 2622140Seric { 2632140Seric if (p[0] == '/') 2642140Seric SccsDir = p; 2652140Seric else 2662140Seric { 2672140Seric pw = getpwnam(p); 2682140Seric if (pw == NULL) 2692140Seric { 2702140Seric usrerr("user %s does not exist", p); 2712140Seric exit(EX_USAGE); 2722140Seric } 27310110Srrh gstrcpy(buf, pw->pw_dir, sizeof(buf)); 27410110Srrh gstrcat(buf, "/src", sizeof(buf)); 2752140Seric if (access(buf, 0) < 0) 2762140Seric { 27710110Srrh gstrcpy(buf, pw->pw_dir, sizeof(buf)); 27810110Srrh gstrcat(buf, "/source", sizeof(buf)); 2792140Seric if (access(buf, 0) < 0) 2802140Seric { 2812140Seric usrerr("project %s has no source!", p); 2822140Seric exit(EX_USAGE); 2832140Seric } 2842140Seric } 2852140Seric SccsDir = buf; 2862140Seric } 2872140Seric } 2882139Seric # endif SCCSDIR 2892139Seric # endif V6 2902139Seric 291148Seric /* 292148Seric ** Detect and decode flags intended for this program. 293148Seric */ 294148Seric 295200Seric if (argc < 2) 296148Seric { 2971205Seric fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); 298200Seric exit(EX_USAGE); 299200Seric } 300200Seric argv[argc] = NULL; 301200Seric 302262Seric if (lookup(argv[0]) == NULL) 303200Seric { 304262Seric while ((p = *++argv) != NULL) 305148Seric { 306262Seric if (*p != '-') 307262Seric break; 308262Seric switch (*++p) 309262Seric { 310262Seric case 'r': /* run as real user */ 311262Seric setuid(getuid()); 312262Seric RealUser++; 313262Seric break; 314148Seric 3151270Seric # ifndef SCCSDIR 316262Seric case 'p': /* path of sccs files */ 317262Seric SccsPath = ++p; 3182348Seric if (SccsPath[0] == '\0' && argv[1] != NULL) 3192348Seric SccsPath = *++argv; 320262Seric break; 321148Seric 322588Seric case 'd': /* directory to search from */ 323588Seric SccsDir = ++p; 3242348Seric if (SccsDir[0] == '\0' && argv[1] != NULL) 3252348Seric SccsDir = *++argv; 326588Seric break; 3271205Seric # endif 328588Seric 329393Seric # ifdef DEBUG 330393Seric case 'T': /* trace */ 331393Seric Debug++; 332393Seric break; 333393Seric # endif 334393Seric 335262Seric default: 3361205Seric usrerr("unknown option -%s", p); 337262Seric break; 338262Seric } 339148Seric } 340262Seric if (SccsPath[0] == '\0') 341262Seric SccsPath = "."; 342148Seric } 343148Seric 3441737Seric i = command(argv, FALSE, ""); 3451282Seric exit(i); 346200Seric } 3471432Seric 3481432Seric /* 3491282Seric ** COMMAND -- look up and perform a command 3501282Seric ** 3511282Seric ** This routine is the guts of this program. Given an 3521282Seric ** argument vector, it looks up the "command" (argv[0]) 3531282Seric ** in the configuration table and does the necessary stuff. 3541282Seric ** 3551282Seric ** Parameters: 3561282Seric ** argv -- an argument vector to process. 3571282Seric ** forkflag -- if set, fork before executing the command. 3581316Seric ** editflag -- if set, only include flags listed in the 3591316Seric ** sccsklets field of the command descriptor. 3601316Seric ** arg0 -- a space-seperated list of arguments to insert 3611316Seric ** before argv. 3621282Seric ** 3631282Seric ** Returns: 3641282Seric ** zero -- command executed ok. 3651282Seric ** else -- error status. 3661282Seric ** 3671282Seric ** Side Effects: 3681282Seric ** none. 3691282Seric */ 370157Seric 3711737Seric command(argv, forkflag, arg0) 372200Seric char **argv; 373201Seric bool forkflag; 3741316Seric char *arg0; 375200Seric { 376200Seric register struct sccsprog *cmd; 377200Seric register char *p; 37810110Srrh char buf[FBUFSIZ]; 379262Seric extern struct sccsprog *lookup(); 3801316Seric char *nav[1000]; 3811316Seric char **np; 3821431Seric register char **ap; 383585Seric register int i; 3841431Seric register char *q; 385585Seric extern bool unedit(); 3861282Seric int rval = 0; 3871316Seric extern char *index(); 3881316Seric extern char *makefile(); 3891737Seric char *editchs; 3901435Seric extern char *tail(); 391200Seric 392393Seric # ifdef DEBUG 393393Seric if (Debug) 394393Seric { 3951316Seric printf("command:\n\t\"%s\"\n", arg0); 3961316Seric for (np = argv; *np != NULL; np++) 3971316Seric printf("\t\"%s\"\n", *np); 398393Seric } 399393Seric # endif 400393Seric 401157Seric /* 4021316Seric ** Copy arguments. 4031438Seric ** Copy from arg0 & if necessary at most one arg 4041438Seric ** from argv[0]. 4051316Seric */ 4061316Seric 4071431Seric np = ap = &nav[1]; 4081737Seric editchs = NULL; 4091821Seric for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) 4101316Seric { 4111316Seric *np++ = q; 4121316Seric while (*p == ' ') 4131316Seric p++; 4141737Seric while (*p != ' ' && *p != '\0' && *p != '/' && *p != ':') 4151316Seric *q++ = *p++; 4161316Seric *q++ = '\0'; 4171737Seric if (*p == ':') 4181737Seric { 4191737Seric editchs = q; 4201821Seric while (*++p != '\0' && *p != '/' && *p != ' ') 4211737Seric *q++ = *p; 4221737Seric *q++ = '\0'; 4231737Seric } 4241316Seric } 4251316Seric *np = NULL; 4261431Seric if (*ap == NULL) 4271316Seric *np++ = *argv++; 4281316Seric 4291316Seric /* 430148Seric ** Look up command. 4311431Seric ** At this point, *ap is the command name. 432148Seric */ 433148Seric 4341431Seric cmd = lookup(*ap); 435262Seric if (cmd == NULL) 436148Seric { 4371431Seric usrerr("Unknown command \"%s\"", *ap); 4381282Seric return (EX_USAGE); 439148Seric } 440148Seric 441148Seric /* 4421316Seric ** Copy remaining arguments doing editing as appropriate. 4431316Seric */ 4441316Seric 4451316Seric for (; *argv != NULL; argv++) 4461316Seric { 4471316Seric p = *argv; 4481316Seric if (*p == '-') 4491316Seric { 4501737Seric if (p[1] == '\0' || editchs == NULL || index(editchs, p[1]) != NULL) 4511316Seric *np++ = p; 4521316Seric } 4531316Seric else 4541316Seric { 4551316Seric if (!bitset(NO_SDOT, cmd->sccsflags)) 4561316Seric p = makefile(p); 4571316Seric if (p != NULL) 4581316Seric *np++ = p; 4591316Seric } 4601316Seric } 4611316Seric *np = NULL; 4621316Seric 4631316Seric /* 464200Seric ** Interpret operation associated with this command. 465157Seric */ 466157Seric 467200Seric switch (cmd->sccsoper) 468200Seric { 4691431Seric case SHELL: /* call a shell file */ 4701431Seric *ap = cmd->sccspath; 4711431Seric *--ap = "sh"; 47237921Sbostic rval = callprog(_PATH_BSHELL, cmd->sccsflags, ap, forkflag); 4731431Seric break; 4741431Seric 475200Seric case PROG: /* call an sccs prog */ 4761431Seric rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); 477201Seric break; 478201Seric 479201Seric case CMACRO: /* command macro */ 4801438Seric /* step through & execute each part of the macro */ 481201Seric for (p = cmd->sccspath; *p != '\0'; p++) 482201Seric { 4831316Seric q = p; 4841316Seric while (*p != '\0' && *p != '/') 4851316Seric p++; 4861737Seric rval = command(&ap[1], *p != '\0', q); 4871282Seric if (rval != 0) 4881282Seric break; 489201Seric } 4901282Seric break; 491157Seric 492226Seric case FIX: /* fix a delta */ 49330959Sbostic if (ap[1]==0 || strncmp(ap[1], "-r", 2)!=0) 494226Seric { 4951205Seric usrerr("-r flag needed for fix command"); 4961282Seric rval = EX_USAGE; 497226Seric break; 498226Seric } 4991438Seric 5001438Seric /* get the version with all changes */ 5011737Seric rval = command(&ap[1], TRUE, "get -k"); 5021438Seric 5031438Seric /* now remove that version from the s-file */ 5041282Seric if (rval == 0) 5051737Seric rval = command(&ap[1], TRUE, "rmdel:r"); 5061438Seric 5071438Seric /* and edit the old version (but don't clobber new vers) */ 5081282Seric if (rval == 0) 5091737Seric rval = command(&ap[2], FALSE, "get -e -g"); 5101282Seric break; 511226Seric 512261Seric case CLEAN: 5131822Seric rval = clean((int) cmd->sccspath, ap); 514261Seric break; 515261Seric 516396Seric case UNEDIT: 5171431Seric for (argv = np = &ap[1]; *argv != NULL; argv++) 518585Seric { 5191316Seric if (unedit(*argv)) 5201316Seric *np++ = *argv; 521585Seric } 5221316Seric *np = NULL; 5231438Seric 5241438Seric /* get all the files that we unedited successfully */ 5251738Seric if (np > &ap[1]) 5261737Seric rval = command(&ap[1], FALSE, "get"); 527396Seric break; 528396Seric 5291433Seric case DIFFS: /* diff between s-file & edit file */ 5301433Seric /* find the end of the flag arguments */ 5311433Seric for (np = &ap[1]; *np != NULL && **np == '-'; np++) 5321433Seric continue; 5331433Seric argv = np; 5341433Seric 5351433Seric /* for each file, do the diff */ 5361502Seric p = argv[1]; 5371433Seric while (*np != NULL) 5381433Seric { 5391438Seric /* messy, but we need a null terminated argv */ 5401433Seric *argv = *np++; 5411502Seric argv[1] = NULL; 5421435Seric i = dodiff(ap, tail(*argv)); 5431433Seric if (rval == 0) 5441433Seric rval = i; 5451502Seric argv[1] = p; 5461433Seric } 5471433Seric break; 5481433Seric 5491871Seric case DODIFF: /* internal diff call */ 5501871Seric setuid(getuid()); 5511871Seric for (np = ap; *np != NULL; np++) 5521871Seric { 5531871Seric if ((*np)[0] == '-' && (*np)[1] == 'C') 5541871Seric (*np)[1] = 'c'; 5551871Seric } 5561871Seric 5571871Seric /* insert "-" argument */ 5581871Seric np[1] = NULL; 5591871Seric np[0] = np[-1]; 5601871Seric np[-1] = "-"; 5611871Seric 5621871Seric /* execute the diff program of choice */ 5631871Seric # ifndef V6 5641871Seric execvp("diff", ap); 5651871Seric # endif 5661871Seric execv(cmd->sccspath, argv); 5671871Seric syserr("cannot exec %s", cmd->sccspath); 5681871Seric exit(EX_OSERR); 5691871Seric 57010104Srrh case ENTER: /* enter new sccs files */ 5716785Smckusick /* skip over flag arguments */ 5726785Smckusick for (np = &ap[1]; *np != NULL && **np == '-'; np++) 5736785Smckusick continue; 5746785Smckusick argv = np; 5756785Smckusick 5766785Smckusick /* do an admin for each file */ 5776785Smckusick p = argv[1]; 5786785Smckusick while (*np != NULL) 5796785Smckusick { 5806785Smckusick printf("\n%s:\n", *np); 58110110Srrh strcpy(buf, "-i"); 58210110Srrh gstrcat(buf, *np, sizeof(buf)); 5836785Smckusick ap[0] = buf; 5846785Smckusick argv[0] = tail(*np); 5856785Smckusick argv[1] = NULL; 5866785Smckusick rval = command(ap, TRUE, "admin"); 5876785Smckusick argv[1] = p; 5886785Smckusick if (rval == 0) 5896785Smckusick { 59010110Srrh strcpy(buf, ","); 59110110Srrh gstrcat(buf, tail(*np), sizeof(buf)); 5926785Smckusick if (link(*np, buf) >= 0) 5936785Smckusick unlink(*np); 5946785Smckusick } 5956785Smckusick np++; 5966785Smckusick } 5976785Smckusick break; 5986785Smckusick 599200Seric default: 6001205Seric syserr("oper %d", cmd->sccsoper); 601200Seric exit(EX_SOFTWARE); 602200Seric } 6031282Seric # ifdef DEBUG 6041282Seric if (Debug) 6051282Seric printf("command: rval=%d\n", rval); 6061282Seric # endif 6071282Seric return (rval); 608200Seric } 6091432Seric 6101432Seric /* 611262Seric ** LOOKUP -- look up an SCCS command name. 612262Seric ** 613262Seric ** Parameters: 614262Seric ** name -- the name of the command to look up. 615262Seric ** 616262Seric ** Returns: 617262Seric ** ptr to command descriptor for this command. 618262Seric ** NULL if no such entry. 619262Seric ** 620262Seric ** Side Effects: 621262Seric ** none. 622262Seric */ 623200Seric 624262Seric struct sccsprog * 625262Seric lookup(name) 626262Seric char *name; 627262Seric { 628262Seric register struct sccsprog *cmd; 629226Seric 630262Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 631262Seric { 632262Seric if (strcmp(cmd->sccsname, name) == 0) 633262Seric return (cmd); 634262Seric } 635262Seric return (NULL); 636262Seric } 6371432Seric 6381432Seric /* 6391282Seric ** CALLPROG -- call a program 6401282Seric ** 6411316Seric ** Used to call the SCCS programs. 6421282Seric ** 6431282Seric ** Parameters: 6441282Seric ** progpath -- pathname of the program to call. 6451282Seric ** flags -- status flags from the command descriptors. 6461282Seric ** argv -- an argument vector to pass to the program. 6471282Seric ** forkflag -- if true, fork before calling, else just 6481282Seric ** exec. 6491282Seric ** 6501282Seric ** Returns: 6511282Seric ** The exit status of the program. 6521282Seric ** Nothing if forkflag == FALSE. 6531282Seric ** 6541282Seric ** Side Effects: 6551282Seric ** Can exit if forkflag == FALSE. 6561282Seric */ 657226Seric 658200Seric callprog(progpath, flags, argv, forkflag) 659200Seric char *progpath; 660200Seric short flags; 661200Seric char **argv; 662200Seric bool forkflag; 663200Seric { 664200Seric register int i; 66530959Sbostic register int wpid; 666201Seric auto int st; 66730959Sbostic register int sigcode; 66830959Sbostic register int coredumped; 66930959Sbostic register char *sigmsg; 67030959Sbostic auto char sigmsgbuf[10+1]; /* "Signal 127" + terminating '\0' */ 671200Seric 6721316Seric # ifdef DEBUG 6731316Seric if (Debug) 6741316Seric { 6751316Seric printf("callprog:\n"); 6761316Seric for (i = 0; argv[i] != NULL; i++) 6771316Seric printf("\t\"%s\"\n", argv[i]); 6781316Seric } 6791316Seric # endif 6801316Seric 681200Seric if (*argv == NULL) 682200Seric return (-1); 683200Seric 684157Seric /* 685226Seric ** Fork if appropriate. 686148Seric */ 687148Seric 688200Seric if (forkflag) 689200Seric { 690393Seric # ifdef DEBUG 691393Seric if (Debug) 692393Seric printf("Forking\n"); 693393Seric # endif 694200Seric i = fork(); 695200Seric if (i < 0) 696200Seric { 6971205Seric syserr("cannot fork"); 698200Seric exit(EX_OSERR); 699200Seric } 700200Seric else if (i > 0) 701201Seric { 70230959Sbostic while ((wpid = wait(&st)) != -1 && wpid != i) 70330959Sbostic ; 70430959Sbostic if ((sigcode = st & 0377) == 0) 7051282Seric st = (st >> 8) & 0377; 70630959Sbostic else 70730959Sbostic { 70830959Sbostic coredumped = sigcode & 0200; 70930959Sbostic sigcode &= 0177; 71030959Sbostic if (sigcode != SIGINT && sigcode != SIGPIPE) 71130959Sbostic { 71230959Sbostic if (sigcode < NSIG) 71330959Sbostic sigmsg = sys_siglist[sigcode]; 71430959Sbostic else 71530959Sbostic { 71630959Sbostic sprintf(sigmsgbuf, "Signal %d", 71730959Sbostic sigcode); 71830959Sbostic sigmsg = sigmsgbuf; 71930959Sbostic } 72030959Sbostic fprintf(stderr, "sccs: %s: %s%s", argv[0], 72130959Sbostic sigmsg, 72230959Sbostic coredumped ? " - core dumped": ""); 72330959Sbostic } 72430959Sbostic st = EX_SOFTWARE; 72530959Sbostic } 7261433Seric if (OutFile >= 0) 7271433Seric { 7281433Seric close(OutFile); 7291433Seric OutFile = -1; 7301433Seric } 731201Seric return (st); 732201Seric } 733200Seric } 7341433Seric else if (OutFile >= 0) 7351433Seric { 7361433Seric syserr("callprog: setting stdout w/o forking"); 7371433Seric exit(EX_SOFTWARE); 7381433Seric } 739200Seric 7401433Seric /* set protection as appropriate */ 741200Seric if (bitset(REALUSER, flags)) 742200Seric setuid(getuid()); 7431433Seric 7441433Seric /* change standard input & output if needed */ 7451433Seric if (OutFile >= 0) 7461433Seric { 7471433Seric close(1); 7481433Seric dup(OutFile); 7491433Seric close(OutFile); 7501433Seric } 751226Seric 7521433Seric /* call real SCCS program */ 753226Seric execv(progpath, argv); 7541205Seric syserr("cannot execute %s", progpath); 755148Seric exit(EX_UNAVAILABLE); 7561738Seric /*NOTREACHED*/ 757148Seric } 7581432Seric 7591432Seric /* 760586Seric ** MAKEFILE -- make filename of SCCS file 761586Seric ** 762586Seric ** If the name passed is already the name of an SCCS file, 763586Seric ** just return it. Otherwise, munge the name into the name 764586Seric ** of the actual SCCS file. 765586Seric ** 766586Seric ** There are cases when it is not clear what you want to 767586Seric ** do. For example, if SccsPath is an absolute pathname 768586Seric ** and the name given is also an absolute pathname, we go 769586Seric ** for SccsPath (& only use the last component of the name 770586Seric ** passed) -- this is important for security reasons (if 771586Seric ** sccs is being used as a setuid front end), but not 772586Seric ** particularly intuitive. 773586Seric ** 774586Seric ** Parameters: 775586Seric ** name -- the file name to be munged. 776586Seric ** 777586Seric ** Returns: 778586Seric ** The pathname of the sccs file. 779586Seric ** NULL on error. 780586Seric ** 781586Seric ** Side Effects: 782586Seric ** none. 783586Seric */ 784148Seric 785148Seric char * 786148Seric makefile(name) 787148Seric char *name; 788148Seric { 789148Seric register char *p; 79010110Srrh char buf[3*FBUFSIZ]; 791148Seric extern char *malloc(); 792586Seric extern char *rindex(); 793588Seric extern bool safepath(); 794587Seric extern bool isdir(); 795587Seric register char *q; 796148Seric 797586Seric p = rindex(name, '/'); 798586Seric if (p == NULL) 799586Seric p = name; 800586Seric else 801586Seric p++; 802586Seric 803148Seric /* 804588Seric ** Check to see that the path is "safe", i.e., that we 805588Seric ** are not letting some nasty person use the setuid part 806588Seric ** of this program to look at or munge some presumably 807588Seric ** hidden files. 808148Seric */ 809148Seric 810588Seric if (SccsDir[0] == '/' && !safepath(name)) 811588Seric return (NULL); 812586Seric 813586Seric /* 814588Seric ** Create the base pathname. 815586Seric */ 816586Seric 8171438Seric /* first the directory part */ 818588Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) 819148Seric { 82010110Srrh gstrcpy(buf, SccsDir, sizeof(buf)); 82110110Srrh gstrcat(buf, "/", sizeof(buf)); 822586Seric } 823586Seric else 82410110Srrh gstrcpy(buf, "", sizeof(buf)); 8251438Seric 8261438Seric /* then the head of the pathname */ 82710110Srrh gstrncat(buf, name, p - name, sizeof(buf)); 828587Seric q = &buf[strlen(buf)]; 8291438Seric 8301438Seric /* now copy the final part of the name, in case useful */ 83110110Srrh gstrcpy(q, p, sizeof(buf)); 8321438Seric 8331438Seric /* so is it useful? */ 834587Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 835586Seric { 8361438Seric /* sorry, no; copy the SCCS pathname & the "s." */ 83710110Srrh gstrcpy(q, SccsPath, sizeof(buf)); 83810110Srrh gstrcat(buf, "/s.", sizeof(buf)); 8391438Seric 8401438Seric /* and now the end of the name */ 84110110Srrh gstrcat(buf, p, sizeof(buf)); 842586Seric } 843148Seric 8441438Seric /* if i haven't changed it, why did I do all this? */ 845588Seric if (strcmp(buf, name) == 0) 846588Seric p = name; 847588Seric else 848148Seric { 8491438Seric /* but if I have, squirrel it away */ 850588Seric p = malloc(strlen(buf) + 1); 851588Seric if (p == NULL) 852588Seric { 853588Seric perror("Sccs: no mem"); 854588Seric exit(EX_OSERR); 855588Seric } 856588Seric strcpy(p, buf); 857148Seric } 8581438Seric 859148Seric return (p); 860148Seric } 8611432Seric 8621432Seric /* 863587Seric ** ISDIR -- return true if the argument is a directory. 864587Seric ** 865587Seric ** Parameters: 866587Seric ** name -- the pathname of the file to check. 867587Seric ** 868587Seric ** Returns: 869587Seric ** TRUE if 'name' is a directory, FALSE otherwise. 870587Seric ** 871587Seric ** Side Effects: 872587Seric ** none. 873587Seric */ 874587Seric 875587Seric bool 876587Seric isdir(name) 877587Seric char *name; 878587Seric { 879587Seric struct stat stbuf; 880587Seric 881587Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 882587Seric } 8831432Seric 8841432Seric /* 885586Seric ** SAFEPATH -- determine whether a pathname is "safe" 886586Seric ** 887586Seric ** "Safe" pathnames only allow you to get deeper into the 888586Seric ** directory structure, i.e., full pathnames and ".." are 889586Seric ** not allowed. 890586Seric ** 891586Seric ** Parameters: 892586Seric ** p -- the name to check. 893586Seric ** 894586Seric ** Returns: 895586Seric ** TRUE -- if the path is safe. 896586Seric ** FALSE -- if the path is not safe. 897586Seric ** 898586Seric ** Side Effects: 899586Seric ** Prints a message if the path is not safe. 900586Seric */ 901586Seric 902586Seric bool 903586Seric safepath(p) 904586Seric register char *p; 905586Seric { 906586Seric extern char *index(); 907586Seric 908586Seric if (*p != '/') 909586Seric { 910586Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 911586Seric { 912586Seric p = index(p, '/'); 913586Seric if (p == NULL) 914586Seric return (TRUE); 915586Seric p++; 916586Seric } 917586Seric } 918586Seric 919586Seric printf("You may not use full pathnames or \"..\"\n"); 920586Seric return (FALSE); 921586Seric } 9221432Seric 9231432Seric /* 924261Seric ** CLEAN -- clean out recreatable files 925261Seric ** 926261Seric ** Any file for which an "s." file exists but no "p." file 927261Seric ** exists in the current directory is purged. 928261Seric ** 929261Seric ** Parameters: 9301822Seric ** mode -- tells whether this came from a "clean", "info", or 9311822Seric ** "check" command. 9321822Seric ** argv -- the rest of the argument vector. 933261Seric ** 934261Seric ** Returns: 935261Seric ** none. 936261Seric ** 937261Seric ** Side Effects: 938819Seric ** Removes files in the current directory. 939819Seric ** Prints information regarding files being edited. 940819Seric ** Exits if a "check" command. 941261Seric */ 942261Seric 9431822Seric clean(mode, argv) 944819Seric int mode; 9451822Seric char **argv; 946261Seric { 9476784Smckusick struct direct *dir; 94810110Srrh char buf[FBUFSIZ]; 9492140Seric char *bufend; 95030466Smckusick register DIR *dirp; 951346Seric register char *basefile; 952351Seric bool gotedit; 9531822Seric bool gotpfent; 954394Seric FILE *pfp; 9551822Seric bool nobranch = FALSE; 9561822Seric extern struct pfile *getpfent(); 9571822Seric register struct pfile *pf; 9581822Seric register char **ap; 9591864Seric extern char *username(); 9601864Seric char *usernm = NULL; 9612140Seric char *subdir = NULL; 9622140Seric char *cmdname; 963261Seric 9641438Seric /* 9651822Seric ** Process the argv 9661822Seric */ 9671822Seric 9682140Seric cmdname = *argv; 9692140Seric for (ap = argv; *++ap != NULL; ) 9701822Seric { 9711864Seric if (**ap == '-') 9721864Seric { 9731864Seric /* we have a flag */ 9741864Seric switch ((*ap)[1]) 9751864Seric { 9761864Seric case 'b': 9771864Seric nobranch = TRUE; 9781864Seric break; 9791864Seric 9801864Seric case 'u': 9811864Seric if ((*ap)[2] != '\0') 9821864Seric usernm = &(*ap)[2]; 9831864Seric else if (ap[1] != NULL && ap[1][0] != '-') 9841864Seric usernm = *++ap; 9851864Seric else 9861864Seric usernm = username(); 9871864Seric break; 9881864Seric } 9891864Seric } 9902140Seric else 9912140Seric { 9922140Seric if (subdir != NULL) 9932140Seric usrerr("too many args"); 9942140Seric else 9952140Seric subdir = *ap; 9962140Seric } 9971822Seric } 9981822Seric 9991822Seric /* 10001438Seric ** Find and open the SCCS directory. 10011438Seric */ 10021438Seric 100310110Srrh gstrcpy(buf, SccsDir, sizeof(buf)); 10041207Seric if (buf[0] != '\0') 100510110Srrh gstrcat(buf, "/", sizeof(buf)); 10062140Seric if (subdir != NULL) 10072140Seric { 100810110Srrh gstrcat(buf, subdir, sizeof(buf)); 100910110Srrh gstrcat(buf, "/", sizeof(buf)); 10102140Seric } 101110110Srrh gstrcat(buf, SccsPath, sizeof(buf)); 10122140Seric bufend = &buf[strlen(buf)]; 10131438Seric 101430466Smckusick dirp = opendir(buf); 101530466Smckusick if (dirp == NULL) 1016261Seric { 10171207Seric usrerr("cannot open %s", buf); 10181282Seric return (EX_NOINPUT); 1019261Seric } 1020261Seric 1021261Seric /* 1022261Seric ** Scan the SCCS directory looking for s. files. 10231438Seric ** gotedit tells whether we have tried to clean any 10241438Seric ** files that are being edited. 1025261Seric */ 1026261Seric 1027351Seric gotedit = FALSE; 102830466Smckusick while (dir = readdir(dirp)) { 10296784Smckusick if (strncmp(dir->d_name, "s.", 2) != 0) 1030261Seric continue; 1031261Seric 1032261Seric /* got an s. file -- see if the p. file exists */ 103310110Srrh gstrcpy(bufend, "/p.", sizeof(buf)); 10342140Seric basefile = bufend + 3; 103510110Srrh gstrcpy(basefile, &dir->d_name[2], sizeof(buf)); 10361822Seric 10371822Seric /* 10381822Seric ** open and scan the p-file. 10391822Seric ** 'gotpfent' tells if we have found a valid p-file 10401822Seric ** entry. 10411822Seric */ 10421822Seric 1043394Seric pfp = fopen(buf, "r"); 10441822Seric gotpfent = FALSE; 1045394Seric if (pfp != NULL) 1046346Seric { 10471438Seric /* the file exists -- report it's contents */ 10481822Seric while ((pf = getpfent(pfp)) != NULL) 10491730Seric { 10501822Seric if (nobranch && isbranch(pf->p_nsid)) 10511822Seric continue; 10521864Seric if (usernm != NULL && strcmp(usernm, pf->p_user) != 0 && mode != CLEANC) 10531864Seric continue; 10541822Seric gotedit = TRUE; 10551822Seric gotpfent = TRUE; 10561822Seric if (mode == TELLC) 10571822Seric { 10581822Seric printf("%s\n", basefile); 10591822Seric break; 10601822Seric } 10612161Seric printf("%12s: being edited: ", basefile); 10622161Seric putpfent(pf, stdout); 10631730Seric } 1064394Seric fclose(pfp); 10651822Seric } 1066261Seric 1067261Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 10681870Seric if (mode == CLEANC && !gotpfent) 1069346Seric { 107010110Srrh char unlinkbuf[FBUFSIZ]; 107110110Srrh gstrcpy(unlinkbuf, &dir->d_name[2], sizeof(unlinkbuf)); 107210103Srrh unlink(unlinkbuf); 1073346Seric } 1074261Seric } 1075261Seric 10761438Seric /* cleanup & report results */ 107730466Smckusick closedir(dirp); 1078819Seric if (!gotedit && mode == INFOC) 10791864Seric { 10801864Seric printf("Nothing being edited"); 10811864Seric if (nobranch) 10821864Seric printf(" (on trunk)"); 10831864Seric if (usernm == NULL) 10841864Seric printf("\n"); 10851864Seric else 10861864Seric printf(" by %s\n", usernm); 10871864Seric } 1088819Seric if (mode == CHECKC) 1089819Seric exit(gotedit); 10901282Seric return (EX_OK); 1091261Seric } 10921432Seric 10931432Seric /* 10941822Seric ** ISBRANCH -- is the SID a branch? 10951822Seric ** 10961822Seric ** Parameters: 10971822Seric ** sid -- the sid to check. 10981822Seric ** 10991822Seric ** Returns: 11001822Seric ** TRUE if the sid represents a branch. 11011822Seric ** FALSE otherwise. 11021822Seric ** 11031822Seric ** Side Effects: 11041822Seric ** none. 11051822Seric */ 11061822Seric 11071822Seric isbranch(sid) 11081822Seric char *sid; 11091822Seric { 11101822Seric register char *p; 11111822Seric int dots; 11121822Seric 11131822Seric dots = 0; 11141822Seric for (p = sid; *p != '\0'; p++) 11151822Seric { 11161822Seric if (*p == '.') 11171822Seric dots++; 11181822Seric if (dots > 1) 11191822Seric return (TRUE); 11201822Seric } 11211822Seric return (FALSE); 11221822Seric } 11231822Seric 11241822Seric /* 1125396Seric ** UNEDIT -- unedit a file 1126396Seric ** 1127396Seric ** Checks to see that the current user is actually editting 1128396Seric ** the file and arranges that s/he is not editting it. 1129396Seric ** 1130396Seric ** Parameters: 1131416Seric ** fn -- the name of the file to be unedited. 1132396Seric ** 1133396Seric ** Returns: 1134585Seric ** TRUE -- if the file was successfully unedited. 1135585Seric ** FALSE -- if the file was not unedited for some 1136585Seric ** reason. 1137396Seric ** 1138396Seric ** Side Effects: 1139396Seric ** fn is removed 1140396Seric ** entries are removed from pfile. 1141396Seric */ 1142396Seric 1143585Seric bool 1144396Seric unedit(fn) 1145396Seric char *fn; 1146396Seric { 1147396Seric register FILE *pfp; 114812153Ssam char *cp, *pfn; 114937921Sbostic static char tfn[] = _PATH_TMP; 1150396Seric FILE *tfp; 1151396Seric register char *q; 1152396Seric bool delete = FALSE; 1153396Seric bool others = FALSE; 1154396Seric char *myname; 11551864Seric extern char *username(); 1156396Seric struct pfile *pent; 11571822Seric extern struct pfile *getpfent(); 115810110Srrh char buf[PFILELG]; 115933251Sbostic extern char *makefile(), *rindex(), *tail(); 1160396Seric 1161396Seric /* make "s." filename & find the trailing component */ 1162396Seric pfn = makefile(fn); 1163586Seric if (pfn == NULL) 1164586Seric return (FALSE); 1165586Seric q = rindex(pfn, '/'); 1166586Seric if (q == NULL) 1167586Seric q = &pfn[-1]; 1168586Seric if (q[1] != 's' || q[2] != '.') 1169396Seric { 11701205Seric usrerr("bad file name \"%s\"", fn); 1171585Seric return (FALSE); 1172396Seric } 1173396Seric 11741438Seric /* turn "s." into "p." & try to open it */ 1175396Seric *++q = 'p'; 1176396Seric 1177396Seric pfp = fopen(pfn, "r"); 1178396Seric if (pfp == NULL) 1179396Seric { 1180416Seric printf("%12s: not being edited\n", fn); 1181585Seric return (FALSE); 1182396Seric } 1183396Seric 11841438Seric /* create temp file for editing p-file */ 1185396Seric mktemp(tfn); 1186396Seric tfp = fopen(tfn, "w"); 1187396Seric if (tfp == NULL) 1188396Seric { 11891205Seric usrerr("cannot create \"%s\"", tfn); 1190396Seric exit(EX_OSERR); 1191396Seric } 1192396Seric 11931438Seric /* figure out who I am */ 11941864Seric myname = username(); 11951438Seric 11961438Seric /* 11971438Seric ** Copy p-file to temp file, doing deletions as needed. 11981438Seric */ 11991438Seric 12001822Seric while ((pent = getpfent(pfp)) != NULL) 1201396Seric { 1202396Seric if (strcmp(pent->p_user, myname) == 0) 1203396Seric { 1204396Seric /* a match */ 1205396Seric delete++; 1206396Seric } 1207396Seric else 1208396Seric { 12091438Seric /* output it again */ 12102161Seric putpfent(pent, tfp); 1211396Seric others++; 1212396Seric } 1213396Seric } 1214396Seric 121512153Ssam /* 121612153Ssam * Before changing anything, make sure we can remove 121712153Ssam * the file in question (assuming it exists). 121812153Ssam */ 121912153Ssam if (delete) { 122012153Ssam extern int errno; 122112153Ssam 122212153Ssam cp = tail(fn); 122312153Ssam errno = 0; 122412153Ssam if (access(cp, 0) < 0 && errno != ENOENT) 122512153Ssam goto bad; 122612153Ssam if (errno == 0) 122712153Ssam /* 122812153Ssam * This is wrong, but the rest of the program 122912153Ssam * has built in assumptions about "." as well, 123012153Ssam * so why make unedit a special case? 123112153Ssam */ 123212153Ssam if (access(".", 2) < 0) { 123312153Ssam bad: 123412153Ssam printf("%12s: can't remove\n", cp); 123512153Ssam fclose(tfp); 123612153Ssam fclose(pfp); 123712153Ssam unlink(tfn); 123812153Ssam return (FALSE); 123912153Ssam } 124012153Ssam } 1241396Seric /* do final cleanup */ 1242396Seric if (others) 1243396Seric { 12441438Seric /* copy it back (perhaps it should be linked?) */ 1245396Seric if (freopen(tfn, "r", tfp) == NULL) 1246396Seric { 12471205Seric syserr("cannot reopen \"%s\"", tfn); 1248396Seric exit(EX_OSERR); 1249396Seric } 1250396Seric if (freopen(pfn, "w", pfp) == NULL) 1251396Seric { 12521205Seric usrerr("cannot create \"%s\"", pfn); 1253585Seric return (FALSE); 1254396Seric } 1255396Seric while (fgets(buf, sizeof buf, tfp) != NULL) 1256396Seric fputs(buf, pfp); 1257396Seric } 1258396Seric else 1259396Seric { 12601438Seric /* it's empty -- remove it */ 1261396Seric unlink(pfn); 1262396Seric } 1263396Seric fclose(tfp); 1264396Seric fclose(pfp); 1265396Seric unlink(tfn); 1266396Seric 12671438Seric /* actually remove the g-file */ 1268396Seric if (delete) 1269396Seric { 127012153Ssam /* 127112153Ssam * Since we've checked above, we can 127212153Ssam * use the return from unlink to 127312153Ssam * determine if the file existed or not. 127412153Ssam */ 127512153Ssam if (unlink(cp) >= 0) 127612153Ssam printf("%12s: removed\n", cp); 1277585Seric return (TRUE); 1278396Seric } 1279396Seric else 1280396Seric { 1281416Seric printf("%12s: not being edited by you\n", fn); 1282585Seric return (FALSE); 1283396Seric } 1284396Seric } 12851432Seric 12861432Seric /* 12871433Seric ** DODIFF -- diff an s-file against a g-file 12881433Seric ** 12891433Seric ** Parameters: 12901433Seric ** getv -- argv for the 'get' command. 12911433Seric ** gfile -- name of the g-file to diff against. 12921433Seric ** 12931433Seric ** Returns: 12941433Seric ** Result of get. 12951433Seric ** 12961433Seric ** Side Effects: 12971433Seric ** none. 12981433Seric */ 12991433Seric 13001433Seric dodiff(getv, gfile) 13011433Seric char **getv; 13021433Seric char *gfile; 13031433Seric { 13041433Seric int pipev[2]; 13051433Seric int rval; 13061433Seric register int i; 13071433Seric register int pid; 13081433Seric auto int st; 13091433Seric extern int errno; 131039832Sbostic sig_t osig; 13111433Seric 13121905Seric printf("\n------- %s -------\n", gfile); 13131871Seric fflush(stdout); 13141501Seric 13151438Seric /* create context for diff to run in */ 13161433Seric if (pipe(pipev) < 0) 13171433Seric { 13181433Seric syserr("dodiff: pipe failed"); 13191433Seric exit(EX_OSERR); 13201433Seric } 13211433Seric if ((pid = fork()) < 0) 13221433Seric { 13231433Seric syserr("dodiff: fork failed"); 13241433Seric exit(EX_OSERR); 13251433Seric } 13261433Seric else if (pid > 0) 13271433Seric { 13281433Seric /* in parent; run get */ 13291433Seric OutFile = pipev[1]; 13301433Seric close(pipev[0]); 13311871Seric rval = command(&getv[1], TRUE, "get:rcixt -s -k -p"); 13321433Seric osig = signal(SIGINT, SIG_IGN); 13331433Seric while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR) 13341433Seric errno = 0; 13351433Seric signal(SIGINT, osig); 13361433Seric /* ignore result of diff */ 13371433Seric } 13381433Seric else 13391433Seric { 13401433Seric /* in child, run diff */ 13411433Seric if (close(pipev[1]) < 0 || close(0) < 0 || 13421433Seric dup(pipev[0]) != 0 || close(pipev[0]) < 0) 13431433Seric { 13441433Seric syserr("dodiff: magic failed"); 13451433Seric exit(EX_OSERR); 13461433Seric } 13471871Seric command(&getv[1], FALSE, "-diff:elsfhbC"); 13481433Seric } 13491433Seric return (rval); 13501433Seric } 13511433Seric 13521433Seric /* 13531435Seric ** TAIL -- return tail of filename. 13541435Seric ** 13551435Seric ** Parameters: 13561435Seric ** fn -- the filename. 13571435Seric ** 13581435Seric ** Returns: 13591435Seric ** a pointer to the tail of the filename; e.g., given 13601435Seric ** "cmd/ls.c", "ls.c" is returned. 13611435Seric ** 13621435Seric ** Side Effects: 13631435Seric ** none. 13641435Seric */ 13651435Seric 13661435Seric char * 13671435Seric tail(fn) 13681435Seric register char *fn; 13691435Seric { 13701435Seric register char *p; 13711435Seric 13721435Seric for (p = fn; *p != 0; p++) 13731435Seric if (*p == '/' && p[1] != '\0' && p[1] != '/') 13741435Seric fn = &p[1]; 13751435Seric return (fn); 13761435Seric } 13771435Seric 13781435Seric /* 13791822Seric ** GETPFENT -- get an entry from the p-file 1380396Seric ** 1381396Seric ** Parameters: 1382396Seric ** pfp -- p-file file pointer 1383396Seric ** 1384396Seric ** Returns: 1385396Seric ** pointer to p-file struct for next entry 1386396Seric ** NULL on EOF or error 1387396Seric ** 1388396Seric ** Side Effects: 1389396Seric ** Each call wipes out results of previous call. 1390396Seric */ 1391396Seric 1392396Seric struct pfile * 13931822Seric getpfent(pfp) 1394396Seric FILE *pfp; 1395396Seric { 1396396Seric static struct pfile ent; 139710110Srrh static char buf[PFILELG]; 1398396Seric register char *p; 1399396Seric extern char *nextfield(); 1400396Seric 1401396Seric if (fgets(buf, sizeof buf, pfp) == NULL) 1402396Seric return (NULL); 1403396Seric 1404396Seric ent.p_osid = p = buf; 1405396Seric ent.p_nsid = p = nextfield(p); 1406396Seric ent.p_user = p = nextfield(p); 1407396Seric ent.p_date = p = nextfield(p); 1408396Seric ent.p_time = p = nextfield(p); 14092161Seric ent.p_aux = p = nextfield(p); 1410396Seric 1411396Seric return (&ent); 1412396Seric } 1413396Seric 1414396Seric 1415396Seric char * 1416396Seric nextfield(p) 1417396Seric register char *p; 1418396Seric { 1419396Seric if (p == NULL || *p == '\0') 1420396Seric return (NULL); 1421396Seric while (*p != ' ' && *p != '\n' && *p != '\0') 1422396Seric p++; 1423396Seric if (*p == '\n' || *p == '\0') 1424396Seric { 1425396Seric *p = '\0'; 1426396Seric return (NULL); 1427396Seric } 1428396Seric *p++ = '\0'; 1429396Seric return (p); 1430396Seric } 14312161Seric /* 14322161Seric ** PUTPFENT -- output a p-file entry to a file 14332161Seric ** 14342161Seric ** Parameters: 14352161Seric ** pf -- the p-file entry 14362161Seric ** f -- the file to put it on. 14372161Seric ** 14382161Seric ** Returns: 14392161Seric ** none. 14402161Seric ** 14412161Seric ** Side Effects: 14422161Seric ** pf is written onto file f. 14432161Seric */ 14442161Seric 14452161Seric putpfent(pf, f) 14462161Seric register struct pfile *pf; 14472161Seric register FILE *f; 14482161Seric { 14492161Seric fprintf(f, "%s %s %s %s %s", pf->p_osid, pf->p_nsid, 14502161Seric pf->p_user, pf->p_date, pf->p_time); 14512161Seric if (pf->p_aux != NULL) 14522161Seric fprintf(f, " %s", pf->p_aux); 14532161Seric else 14542161Seric fprintf(f, "\n"); 14552161Seric } 14561432Seric 14571432Seric /* 14581205Seric ** USRERR -- issue user-level error 14591205Seric ** 14601205Seric ** Parameters: 14611205Seric ** f -- format string. 14621205Seric ** p1-p3 -- parameters to a printf. 14631205Seric ** 14641205Seric ** Returns: 14651205Seric ** -1 14661205Seric ** 14671205Seric ** Side Effects: 14681205Seric ** none. 14691205Seric */ 14701205Seric 14711738Seric /*VARARGS1*/ 14721205Seric usrerr(f, p1, p2, p3) 14731205Seric char *f; 14741205Seric { 14751205Seric fprintf(stderr, "\n%s: ", MyName); 14761205Seric fprintf(stderr, f, p1, p2, p3); 14771205Seric fprintf(stderr, "\n"); 14781205Seric 14791205Seric return (-1); 14801205Seric } 14811432Seric 14821432Seric /* 14831205Seric ** SYSERR -- print system-generated error. 14841205Seric ** 14851205Seric ** Parameters: 14861205Seric ** f -- format string to a printf. 14871205Seric ** p1, p2, p3 -- parameters to f. 14881205Seric ** 14891205Seric ** Returns: 14901205Seric ** never. 14911205Seric ** 14921205Seric ** Side Effects: 14931205Seric ** none. 14941205Seric */ 14951205Seric 14961738Seric /*VARARGS1*/ 14971205Seric syserr(f, p1, p2, p3) 14981205Seric char *f; 14991205Seric { 15001205Seric extern int errno; 15011205Seric 15021205Seric fprintf(stderr, "\n%s SYSERR: ", MyName); 15031205Seric fprintf(stderr, f, p1, p2, p3); 15041205Seric fprintf(stderr, "\n"); 15051205Seric if (errno == 0) 15061205Seric exit(EX_SOFTWARE); 15071205Seric else 15081205Seric { 15091738Seric perror(NULL); 15101205Seric exit(EX_OSERR); 15111205Seric } 15121205Seric } 15131864Seric /* 15141864Seric ** USERNAME -- return name of the current user 15151864Seric ** 15161864Seric ** Parameters: 15171864Seric ** none 15181864Seric ** 15191864Seric ** Returns: 15201864Seric ** name of current user 15211864Seric ** 15221864Seric ** Side Effects: 15231864Seric ** none 15241864Seric */ 15251864Seric 15261864Seric char * 15271864Seric username() 15281864Seric { 15291864Seric # ifdef UIDUSER 15301864Seric extern struct passwd *getpwuid(); 15311864Seric register struct passwd *pw; 15321864Seric 15331864Seric pw = getpwuid(getuid()); 15341864Seric if (pw == NULL) 15351864Seric { 15361864Seric syserr("who are you? (uid=%d)", getuid()); 15371864Seric exit(EX_OSERR); 15381864Seric } 15391864Seric return (pw->pw_name); 15401864Seric # else 15411905Seric extern char *getlogin(); 15426785Smckusick register char *p; 15431905Seric 15446785Smckusick p = getenv("USER"); 15456785Smckusick if (p == NULL || p[0] == '\0') 15466785Smckusick p = getlogin(); 15476785Smckusick return (p); 15481864Seric # endif UIDUSER 15491864Seric } 155010110Srrh 155110110Srrh /* 155210110Srrh ** Guarded string manipulation routines; the last argument 155310110Srrh ** is the length of the buffer into which the strcpy or strcat 155410110Srrh ** is to be done. 155510110Srrh */ 155610110Srrh char *gstrcat(to, from, length) 155710110Srrh char *to, *from; 155810110Srrh int length; 155910110Srrh { 156010110Srrh if (strlen(from) + strlen(to) >= length) { 156110110Srrh gstrbotch(to, from); 156210110Srrh } 156310110Srrh return(strcat(to, from)); 156410110Srrh } 156510110Srrh 156610110Srrh char *gstrncat(to, from, n, length) 156710110Srrh char *to, *from; 156810110Srrh int n; 156910110Srrh int length; 157010110Srrh { 157110110Srrh if (n + strlen(to) >= length) { 157210110Srrh gstrbotch(to, from); 157310110Srrh } 157410110Srrh return(strncat(to, from, n)); 157510110Srrh } 157610110Srrh 157710110Srrh char *gstrcpy(to, from, length) 157810110Srrh char *to, *from; 157910110Srrh int length; 158010110Srrh { 158110110Srrh if (strlen(from) >= length) { 158210110Srrh gstrbotch(from, (char *)0); 158310110Srrh } 158410110Srrh return(strcpy(to, from)); 158510110Srrh } 158610110Srrh gstrbotch(str1, str2) 158710110Srrh char *str1, *str2; 158810110Srrh { 158910110Srrh usrerr("Filename(s) too long: %s %s", str1, str2); 159010110Srrh } 1591