1adf8f7d4Seric # include <stdio.h> 2adf8f7d4Seric # include <sys/types.h> 3adf8f7d4Seric # include <sys/stat.h> 4b2538d76Seric # include <sys/dir.h> 578d41c6aSeric # include <errno.h> 678d41c6aSeric # include <signal.h> 7adf8f7d4Seric # include <sysexits.h> 8b5d4f080Seric # include <whoami.h> 9adf8f7d4Seric 1010cd16efSeric /* 1110cd16efSeric ** SCCS.C -- human-oriented front end to the SCCS system. 1210cd16efSeric ** 1310cd16efSeric ** Without trying to add any functionality to speak of, this 1410cd16efSeric ** program tries to make SCCS a little more accessible to human 1510cd16efSeric ** types. The main thing it does is automatically put the 1610cd16efSeric ** string "SCCS/s." on the front of names. Also, it has a 1710cd16efSeric ** couple of things that are designed to shorten frequent 1810cd16efSeric ** combinations, e.g., "delget" which expands to a "delta" 1910cd16efSeric ** and a "get". 2010cd16efSeric ** 2110cd16efSeric ** This program can also function as a setuid front end. 2210cd16efSeric ** To do this, you should copy the source, renaming it to 2310cd16efSeric ** whatever you want, e.g., "syssccs". Change any defaults 2410cd16efSeric ** in the program (e.g., syssccs might default -d to 2510cd16efSeric ** "/usr/src/sys"). Then recompile and put the result 2610cd16efSeric ** as setuid to whomever you want. In this mode, sccs 2710cd16efSeric ** knows to not run setuid for certain programs in order 2810cd16efSeric ** to preserve security, and so forth. 2910cd16efSeric ** 3010cd16efSeric ** Usage: 3110cd16efSeric ** sccs [flags] command [args] 3210cd16efSeric ** 3310cd16efSeric ** Flags: 3410cd16efSeric ** -d<dir> <dir> represents a directory to search 3510cd16efSeric ** out of. It should be a full pathname 3610cd16efSeric ** for general usage. E.g., if <dir> is 3710cd16efSeric ** "/usr/src/sys", then a reference to the 3810cd16efSeric ** file "dev/bio.c" becomes a reference to 3910cd16efSeric ** "/usr/src/sys/dev/bio.c". 4010cd16efSeric ** -p<path> prepends <path> to the final component 4110cd16efSeric ** of the pathname. By default, this is 4210cd16efSeric ** "SCCS". For example, in the -d example 4310cd16efSeric ** above, the path then gets modified to 4410cd16efSeric ** "/usr/src/sys/dev/SCCS/s.bio.c". In 4510cd16efSeric ** more common usage (without the -d flag), 4610cd16efSeric ** "prog.c" would get modified to 4710cd16efSeric ** "SCCS/s.prog.c". In both cases, the 4810cd16efSeric ** "s." gets automatically prepended. 4910cd16efSeric ** -r run as the real user. 5010cd16efSeric ** 5110cd16efSeric ** Commands: 5210cd16efSeric ** admin, 5310cd16efSeric ** get, 5410cd16efSeric ** delta, 5510cd16efSeric ** rmdel, 5610cd16efSeric ** chghist, 5710cd16efSeric ** etc. Straight out of SCCS; only difference 5810cd16efSeric ** is that pathnames get modified as 5910cd16efSeric ** described above. 6010cd16efSeric ** edit Macro for "get -e". 6110cd16efSeric ** unedit Removes a file being edited, knowing 6210cd16efSeric ** about p-files, etc. 6310cd16efSeric ** delget Macro for "delta" followed by "get". 6410cd16efSeric ** deledit Macro for "delta" followed by "get -e". 6510cd16efSeric ** info Tell what files being edited. 6610cd16efSeric ** clean Remove all files that can be 6710cd16efSeric ** regenerated from SCCS files. 68095915d3Seric ** check Like info, but return exit status, for 6910cd16efSeric ** use in makefiles. 7010cd16efSeric ** fix Remove a top delta & reedit, but save 7110cd16efSeric ** the previous changes in that delta. 7210cd16efSeric ** 7310cd16efSeric ** Compilation Flags: 7410cd16efSeric ** UIDUSER -- determine who the user is by looking at the 7510cd16efSeric ** uid rather than the login name -- for machines 7610cd16efSeric ** where SCCS gets the user in this way. 777abec564Seric ** SCCSDIR -- if defined, forces the -d flag to take on 78095915d3Seric ** this value. This is so that the setuid 79095915d3Seric ** aspects of this program cannot be abused. 807abec564Seric ** This flag also disables the -p flag. 817abec564Seric ** SCCSPATH -- the default for the -p flag. 82c4ad8825Seric ** MYNAME -- the title this program should print when it 83c4ad8825Seric ** gives error messages. 8410cd16efSeric ** 8510cd16efSeric ** Compilation Instructions: 8610cd16efSeric ** cc -O -n -s sccs.c 87c4ad8825Seric ** The flags listed above can be -D defined to simplify 88c4ad8825Seric ** recompilation for variant versions. 8910cd16efSeric ** 9010cd16efSeric ** Author: 9110cd16efSeric ** Eric Allman, UCB/INGRES 927abec564Seric ** Copyright 1980 Regents of the University of California 9310cd16efSeric */ 9410cd16efSeric 95*cc4e4f0dSeric static char SccsId[] = "@(#)sccs.c 1.40 10/17/80"; 960faf63f9Seric 977abec564Seric /******************* Configuration Information ********************/ 987abec564Seric 99c4ad8825Seric /* special defines for local berkeley systems */ 100c4ad8825Seric # include <whoami.h> 101c4ad8825Seric 10210cd16efSeric # ifdef CSVAX 10310cd16efSeric # define UIDUSER 1047abec564Seric # define PROGPATH(name) "/usr/local/name" 1057abec564Seric # endif CSVAX 10610cd16efSeric 107c4ad8825Seric /* end of berkeley systems defines */ 1087abec564Seric 109c4ad8825Seric # ifndef SCCSPATH 110c4ad8825Seric # define SCCSPATH "SCCS" /* pathname in which to find s-files */ 111c4ad8825Seric # endif NOT SCCSPATH 112c4ad8825Seric 113c4ad8825Seric # ifndef MYNAME 114c4ad8825Seric # define MYNAME "sccs" /* name used for printing errors */ 115c4ad8825Seric # endif NOT MYNAME 1167abec564Seric 1170faf63f9Seric # ifndef PROGPATH 1180faf63f9Seric # define PROGPATH(name) "/usr/sccs/name" /* place to find binaries */ 1190faf63f9Seric # endif PROGPATH 1200faf63f9Seric 1217abec564Seric /**************** End of Configuration Information ****************/ 1220faf63f9Seric 123c4432be4Seric typedef char bool; 1247de81dc7Seric # define TRUE 1 1257de81dc7Seric # define FALSE 0 126d02a4f42Seric 127ff038098Seric # define bitset(bit, word) ((bool) ((bit) & (word))) 128ff038098Seric 12910cd16efSeric # ifdef UIDUSER 13010cd16efSeric # include <pwd.h> 13110cd16efSeric # endif UIDUSER 13210cd16efSeric 133adf8f7d4Seric struct sccsprog 134adf8f7d4Seric { 135adf8f7d4Seric char *sccsname; /* name of SCCS routine */ 1367de81dc7Seric short sccsoper; /* opcode, see below */ 1377de81dc7Seric short sccsflags; /* flags, see below */ 138108d6082Seric char *sccsklets; /* valid key-letters on macros */ 139adf8f7d4Seric char *sccspath; /* pathname of binary implementing */ 140adf8f7d4Seric }; 141adf8f7d4Seric 1427de81dc7Seric /* values for sccsoper */ 1437de81dc7Seric # define PROG 0 /* call a program */ 1441777fbcbSeric # define CMACRO 1 /* command substitution macro */ 145172147faSeric # define FIX 2 /* fix a delta */ 146b2538d76Seric # define CLEAN 3 /* clean out recreatable files */ 147e39a5722Seric # define UNEDIT 4 /* unedit a file */ 148419708b0Seric # define SHELL 5 /* call a shell file (like PROG) */ 14978d41c6aSeric # define DIFFS 6 /* diff between sccs & file out */ 1507de81dc7Seric 151c4432be4Seric /* bits for sccsflags */ 1527de81dc7Seric # define NO_SDOT 0001 /* no s. on front of args */ 1537de81dc7Seric # define REALUSER 0002 /* protected (e.g., admin) */ 154adf8f7d4Seric 15548ff0e7eSeric /* modes for the "clean", "info", "check" ops */ 15648ff0e7eSeric # define CLEANC 0 /* clean command */ 15748ff0e7eSeric # define INFOC 1 /* info command */ 15848ff0e7eSeric # define CHECKC 2 /* check command */ 15948ff0e7eSeric 1600faf63f9Seric /* 1610faf63f9Seric ** Description of commands known to this program. 1620faf63f9Seric ** First argument puts the command into a class. Second arg is 1630faf63f9Seric ** info regarding treatment of this command. Third arg is a 1640faf63f9Seric ** list of flags this command accepts from macros, etc. Fourth 1650faf63f9Seric ** arg is the pathname of the implementing program, or the 1660faf63f9Seric ** macro definition, or the arg to a sub-algorithm. 1670faf63f9Seric */ 168b5d4f080Seric 169adf8f7d4Seric struct sccsprog SccsProg[] = 170adf8f7d4Seric { 171108d6082Seric "admin", PROG, REALUSER, "", PROGPATH(admin), 172108d6082Seric "chghist", PROG, 0, "", PROGPATH(rmdel), 173108d6082Seric "comb", PROG, 0, "", PROGPATH(comb), 174108d6082Seric "delta", PROG, 0, "mysrp", PROGPATH(delta), 175c952b8bfSeric "get", PROG, 0, "ixbeskcl", PROGPATH(get), 176108d6082Seric "help", PROG, NO_SDOT, "", PROGPATH(help), 177108d6082Seric "prt", PROG, 0, "", PROGPATH(prt), 178108d6082Seric "rmdel", PROG, REALUSER, "", PROGPATH(rmdel), 179108d6082Seric "what", PROG, NO_SDOT, "", PROGPATH(what), 180419708b0Seric "sccsdiff", SHELL, REALUSER, "", PROGPATH(sccsdiff), 181c952b8bfSeric "edit", CMACRO, NO_SDOT, "ixbscl", "get -e", 182108d6082Seric "delget", CMACRO, NO_SDOT, "", "delta/get -t", 183108d6082Seric "deledit", CMACRO, NO_SDOT, "", "delta/get -e -t", 184108d6082Seric "fix", FIX, NO_SDOT, "", NULL, 185108d6082Seric "clean", CLEAN, REALUSER, "", (char *) CLEANC, 186108d6082Seric "info", CLEAN, REALUSER, "", (char *) INFOC, 187108d6082Seric "check", CLEAN, REALUSER, "", (char *) CHECKC, 188108d6082Seric "unedit", UNEDIT, NO_SDOT, "", NULL, 18978d41c6aSeric "diffs", DIFFS, NO_SDOT|REALUSER, "", NULL, 190108d6082Seric NULL, -1, 0, "", NULL 191adf8f7d4Seric }; 192adf8f7d4Seric 1930faf63f9Seric /* one line from a p-file */ 194e39a5722Seric struct pfile 195e39a5722Seric { 196e39a5722Seric char *p_osid; /* old SID */ 197e39a5722Seric char *p_nsid; /* new SID */ 198e39a5722Seric char *p_user; /* user who did edit */ 199e39a5722Seric char *p_date; /* date of get */ 200e39a5722Seric char *p_time; /* time of get */ 201e39a5722Seric }; 202e39a5722Seric 2037abec564Seric char *SccsPath = SCCSPATH; /* pathname of SCCS files */ 2047abec564Seric # ifdef SCCSDIR 2057abec564Seric char *SccsDir = SCCSDIR; /* directory to begin search from */ 206095915d3Seric # else 2077abec564Seric char *SccsDir = ""; 208095915d3Seric # endif 209c4ad8825Seric char MyName[] = MYNAME; /* name used in messages */ 21078d41c6aSeric int OutFile = -1; /* override output file for commands */ 211c4432be4Seric bool RealUser; /* if set, running as real user */ 212f0cc3246Seric # ifdef DEBUG 213f0cc3246Seric bool Debug; /* turn on tracing */ 214f0cc3246Seric # endif 2150faf63f9Seric 216adf8f7d4Seric main(argc, argv) 217adf8f7d4Seric int argc; 218adf8f7d4Seric char **argv; 219adf8f7d4Seric { 220adf8f7d4Seric register char *p; 221b1ed8a43Seric extern struct sccsprog *lookup(); 22241290c52Seric register int i; 223adf8f7d4Seric 224adf8f7d4Seric /* 225adf8f7d4Seric ** Detect and decode flags intended for this program. 226adf8f7d4Seric */ 227adf8f7d4Seric 2287de81dc7Seric if (argc < 2) 229adf8f7d4Seric { 230095915d3Seric fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); 2317de81dc7Seric exit(EX_USAGE); 2327de81dc7Seric } 2337de81dc7Seric argv[argc] = NULL; 2347de81dc7Seric 235b1ed8a43Seric if (lookup(argv[0]) == NULL) 236b1ed8a43Seric { 2377de81dc7Seric while ((p = *++argv) != NULL) 2387de81dc7Seric { 239adf8f7d4Seric if (*p != '-') 240adf8f7d4Seric break; 241adf8f7d4Seric switch (*++p) 242adf8f7d4Seric { 243adf8f7d4Seric case 'r': /* run as real user */ 244adf8f7d4Seric setuid(getuid()); 245c4432be4Seric RealUser++; 246adf8f7d4Seric break; 247adf8f7d4Seric 2487abec564Seric # ifndef SCCSDIR 249adf8f7d4Seric case 'p': /* path of sccs files */ 250adf8f7d4Seric SccsPath = ++p; 251adf8f7d4Seric break; 252adf8f7d4Seric 2530a8b9ab0Seric case 'd': /* directory to search from */ 2540a8b9ab0Seric SccsDir = ++p; 2550a8b9ab0Seric break; 256095915d3Seric # endif 2570a8b9ab0Seric 258f0cc3246Seric # ifdef DEBUG 259f0cc3246Seric case 'T': /* trace */ 260f0cc3246Seric Debug++; 261f0cc3246Seric break; 262f0cc3246Seric # endif 263f0cc3246Seric 264adf8f7d4Seric default: 265095915d3Seric usrerr("unknown option -%s", p); 266adf8f7d4Seric break; 267adf8f7d4Seric } 268adf8f7d4Seric } 2695cabffd9Seric if (SccsPath[0] == '\0') 2705cabffd9Seric SccsPath = "."; 271b1ed8a43Seric } 272adf8f7d4Seric 273108d6082Seric i = command(argv, FALSE, FALSE, ""); 27441290c52Seric exit(i); 2757de81dc7Seric } 2760faf63f9Seric 2770faf63f9Seric /* 27841290c52Seric ** COMMAND -- look up and perform a command 27941290c52Seric ** 28041290c52Seric ** This routine is the guts of this program. Given an 28141290c52Seric ** argument vector, it looks up the "command" (argv[0]) 28241290c52Seric ** in the configuration table and does the necessary stuff. 28341290c52Seric ** 28441290c52Seric ** Parameters: 28541290c52Seric ** argv -- an argument vector to process. 28641290c52Seric ** forkflag -- if set, fork before executing the command. 287108d6082Seric ** editflag -- if set, only include flags listed in the 288108d6082Seric ** sccsklets field of the command descriptor. 289108d6082Seric ** arg0 -- a space-seperated list of arguments to insert 290108d6082Seric ** before argv. 29141290c52Seric ** 29241290c52Seric ** Returns: 29341290c52Seric ** zero -- command executed ok. 29441290c52Seric ** else -- error status. 29541290c52Seric ** 29641290c52Seric ** Side Effects: 29741290c52Seric ** none. 29841290c52Seric */ 2997de81dc7Seric 300108d6082Seric command(argv, forkflag, editflag, arg0) 3017de81dc7Seric char **argv; 3021777fbcbSeric bool forkflag; 303108d6082Seric bool editflag; 304108d6082Seric char *arg0; 3057de81dc7Seric { 3067de81dc7Seric register struct sccsprog *cmd; 3077de81dc7Seric register char *p; 3081777fbcbSeric char buf[40]; 309b1ed8a43Seric extern struct sccsprog *lookup(); 310108d6082Seric char *nav[1000]; 311108d6082Seric char **np; 312419708b0Seric register char **ap; 313d51cd7e5Seric register int i; 314419708b0Seric register char *q; 315d51cd7e5Seric extern bool unedit(); 31641290c52Seric int rval = 0; 317108d6082Seric extern char *index(); 318108d6082Seric extern char *makefile(); 319287bbaa2Seric extern char *tail(); 320f0cc3246Seric 321f0cc3246Seric # ifdef DEBUG 322f0cc3246Seric if (Debug) 323f0cc3246Seric { 324108d6082Seric printf("command:\n\t\"%s\"\n", arg0); 325108d6082Seric for (np = argv; *np != NULL; np++) 326108d6082Seric printf("\t\"%s\"\n", *np); 327f0cc3246Seric } 328f0cc3246Seric # endif 329c4432be4Seric 330c4432be4Seric /* 331108d6082Seric ** Copy arguments. 332ff038098Seric ** Copy from arg0 & if necessary at most one arg 333ff038098Seric ** from argv[0]. 334adf8f7d4Seric */ 335adf8f7d4Seric 336419708b0Seric np = ap = &nav[1]; 337108d6082Seric for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) 338108d6082Seric { 339108d6082Seric *np++ = q; 340108d6082Seric while (*p == ' ') 341108d6082Seric p++; 342108d6082Seric while (*p != ' ' && *p != '\0' && *p != '/') 343108d6082Seric *q++ = *p++; 344108d6082Seric *q++ = '\0'; 345108d6082Seric } 346108d6082Seric *np = NULL; 347419708b0Seric if (*ap == NULL) 348108d6082Seric *np++ = *argv++; 349108d6082Seric 350108d6082Seric /* 351108d6082Seric ** Look up command. 352419708b0Seric ** At this point, *ap is the command name. 353108d6082Seric */ 354108d6082Seric 355419708b0Seric cmd = lookup(*ap); 356b1ed8a43Seric if (cmd == NULL) 357adf8f7d4Seric { 358419708b0Seric usrerr("Unknown command \"%s\"", *ap); 35941290c52Seric return (EX_USAGE); 360adf8f7d4Seric } 361adf8f7d4Seric 362adf8f7d4Seric /* 363108d6082Seric ** Copy remaining arguments doing editing as appropriate. 364108d6082Seric */ 365108d6082Seric 366108d6082Seric for (; *argv != NULL; argv++) 367108d6082Seric { 368108d6082Seric p = *argv; 369108d6082Seric if (*p == '-') 370108d6082Seric { 371108d6082Seric if (p[1] == '\0' || !editflag || cmd->sccsklets == NULL || 372108d6082Seric index(cmd->sccsklets, p[1]) != NULL) 373108d6082Seric *np++ = p; 374108d6082Seric } 375108d6082Seric else 376108d6082Seric { 377108d6082Seric if (!bitset(NO_SDOT, cmd->sccsflags)) 378108d6082Seric p = makefile(p); 379108d6082Seric if (p != NULL) 380108d6082Seric *np++ = p; 381108d6082Seric } 382108d6082Seric } 383108d6082Seric *np = NULL; 384108d6082Seric 385108d6082Seric /* 3867de81dc7Seric ** Interpret operation associated with this command. 387c4432be4Seric */ 388c4432be4Seric 3897de81dc7Seric switch (cmd->sccsoper) 3907de81dc7Seric { 391419708b0Seric case SHELL: /* call a shell file */ 392419708b0Seric *ap = cmd->sccspath; 393419708b0Seric *--ap = "sh"; 394419708b0Seric rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag); 395419708b0Seric break; 396419708b0Seric 3977de81dc7Seric case PROG: /* call an sccs prog */ 398419708b0Seric rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); 3991777fbcbSeric break; 4001777fbcbSeric 4011777fbcbSeric case CMACRO: /* command macro */ 402ff038098Seric /* step through & execute each part of the macro */ 4031777fbcbSeric for (p = cmd->sccspath; *p != '\0'; p++) 4041777fbcbSeric { 405108d6082Seric q = p; 406108d6082Seric while (*p != '\0' && *p != '/') 407108d6082Seric p++; 408419708b0Seric rval = command(&ap[1], *p != '\0', TRUE, q); 40941290c52Seric if (rval != 0) 41041290c52Seric break; 4111777fbcbSeric } 41241290c52Seric break; 4137de81dc7Seric 414172147faSeric case FIX: /* fix a delta */ 415419708b0Seric if (strncmp(ap[1], "-r", 2) != 0) 416172147faSeric { 417095915d3Seric usrerr("-r flag needed for fix command"); 41841290c52Seric rval = EX_USAGE; 419172147faSeric break; 420172147faSeric } 421ff038098Seric 422ff038098Seric /* get the version with all changes */ 423419708b0Seric rval = command(&ap[1], TRUE, TRUE, "get -k"); 424ff038098Seric 425ff038098Seric /* now remove that version from the s-file */ 42641290c52Seric if (rval == 0) 427419708b0Seric rval = command(&ap[1], TRUE, TRUE, "rmdel"); 428ff038098Seric 429ff038098Seric /* and edit the old version (but don't clobber new vers) */ 43041290c52Seric if (rval == 0) 431419708b0Seric rval = command(&ap[2], FALSE, TRUE, "get -e -g"); 43241290c52Seric break; 433172147faSeric 434b2538d76Seric case CLEAN: 43541290c52Seric rval = clean((int) cmd->sccspath); 436b2538d76Seric break; 437b2538d76Seric 438e39a5722Seric case UNEDIT: 439419708b0Seric for (argv = np = &ap[1]; *argv != NULL; argv++) 440d51cd7e5Seric { 441108d6082Seric if (unedit(*argv)) 442108d6082Seric *np++ = *argv; 443d51cd7e5Seric } 444108d6082Seric *np = NULL; 445ff038098Seric 446ff038098Seric /* get all the files that we unedited successfully */ 447d51cd7e5Seric if (i > 0) 448419708b0Seric rval = command(&ap[1], FALSE, FALSE, "get"); 449e39a5722Seric break; 450e39a5722Seric 45178d41c6aSeric case DIFFS: /* diff between s-file & edit file */ 45278d41c6aSeric /* find the end of the flag arguments */ 45378d41c6aSeric for (np = &ap[1]; *np != NULL && **np == '-'; np++) 45478d41c6aSeric continue; 45578d41c6aSeric argv = np; 45678d41c6aSeric 45778d41c6aSeric /* for each file, do the diff */ 45878d41c6aSeric while (*np != NULL) 45978d41c6aSeric { 460ff038098Seric /* messy, but we need a null terminated argv */ 46178d41c6aSeric *argv = *np++; 46278d41c6aSeric p = *np; 46378d41c6aSeric *np = NULL; 464287bbaa2Seric i = dodiff(ap, tail(*argv)); 46578d41c6aSeric if (rval == 0) 46678d41c6aSeric rval = i; 46778d41c6aSeric *np = p; 46878d41c6aSeric } 46978d41c6aSeric break; 47078d41c6aSeric 4717de81dc7Seric default: 472095915d3Seric syserr("oper %d", cmd->sccsoper); 4737de81dc7Seric exit(EX_SOFTWARE); 4747de81dc7Seric } 47541290c52Seric # ifdef DEBUG 47641290c52Seric if (Debug) 47741290c52Seric printf("command: rval=%d\n", rval); 47841290c52Seric # endif 47941290c52Seric return (rval); 4807de81dc7Seric } 4810faf63f9Seric 4820faf63f9Seric /* 483b1ed8a43Seric ** LOOKUP -- look up an SCCS command name. 484b1ed8a43Seric ** 485b1ed8a43Seric ** Parameters: 486b1ed8a43Seric ** name -- the name of the command to look up. 487b1ed8a43Seric ** 488b1ed8a43Seric ** Returns: 489b1ed8a43Seric ** ptr to command descriptor for this command. 490b1ed8a43Seric ** NULL if no such entry. 491b1ed8a43Seric ** 492b1ed8a43Seric ** Side Effects: 493b1ed8a43Seric ** none. 494b1ed8a43Seric */ 495b1ed8a43Seric 496b1ed8a43Seric struct sccsprog * 497b1ed8a43Seric lookup(name) 498b1ed8a43Seric char *name; 499b1ed8a43Seric { 500b1ed8a43Seric register struct sccsprog *cmd; 501b1ed8a43Seric 502b1ed8a43Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 503b1ed8a43Seric { 504b1ed8a43Seric if (strcmp(cmd->sccsname, name) == 0) 505b1ed8a43Seric return (cmd); 506b1ed8a43Seric } 507b1ed8a43Seric return (NULL); 508b1ed8a43Seric } 5090faf63f9Seric 5100faf63f9Seric /* 51141290c52Seric ** CALLPROG -- call a program 51241290c52Seric ** 513108d6082Seric ** Used to call the SCCS programs. 51441290c52Seric ** 51541290c52Seric ** Parameters: 51641290c52Seric ** progpath -- pathname of the program to call. 51741290c52Seric ** flags -- status flags from the command descriptors. 51841290c52Seric ** argv -- an argument vector to pass to the program. 51941290c52Seric ** forkflag -- if true, fork before calling, else just 52041290c52Seric ** exec. 52141290c52Seric ** 52241290c52Seric ** Returns: 52341290c52Seric ** The exit status of the program. 52441290c52Seric ** Nothing if forkflag == FALSE. 52541290c52Seric ** 52641290c52Seric ** Side Effects: 52741290c52Seric ** Can exit if forkflag == FALSE. 52841290c52Seric */ 529172147faSeric 5307de81dc7Seric callprog(progpath, flags, argv, forkflag) 5317de81dc7Seric char *progpath; 5327de81dc7Seric short flags; 5337de81dc7Seric char **argv; 5347de81dc7Seric bool forkflag; 5357de81dc7Seric { 5367de81dc7Seric register int i; 5371777fbcbSeric auto int st; 538108d6082Seric 539108d6082Seric # ifdef DEBUG 540108d6082Seric if (Debug) 541108d6082Seric { 542108d6082Seric printf("callprog:\n"); 543108d6082Seric for (i = 0; argv[i] != NULL; i++) 544108d6082Seric printf("\t\"%s\"\n", argv[i]); 545108d6082Seric } 546108d6082Seric # endif 5477de81dc7Seric 5487de81dc7Seric if (*argv == NULL) 5497de81dc7Seric return (-1); 550c4432be4Seric 551c4432be4Seric /* 552172147faSeric ** Fork if appropriate. 553adf8f7d4Seric */ 554adf8f7d4Seric 5557de81dc7Seric if (forkflag) 5567de81dc7Seric { 557f0cc3246Seric # ifdef DEBUG 558f0cc3246Seric if (Debug) 559f0cc3246Seric printf("Forking\n"); 560f0cc3246Seric # endif 5617de81dc7Seric i = fork(); 5627de81dc7Seric if (i < 0) 5637de81dc7Seric { 564095915d3Seric syserr("cannot fork"); 5657de81dc7Seric exit(EX_OSERR); 5667de81dc7Seric } 5677de81dc7Seric else if (i > 0) 5681777fbcbSeric { 5691777fbcbSeric wait(&st); 57041290c52Seric if ((st & 0377) == 0) 57141290c52Seric st = (st >> 8) & 0377; 57278d41c6aSeric if (OutFile >= 0) 57378d41c6aSeric { 57478d41c6aSeric close(OutFile); 57578d41c6aSeric OutFile = -1; 57678d41c6aSeric } 5771777fbcbSeric return (st); 5781777fbcbSeric } 5797de81dc7Seric } 58078d41c6aSeric else if (OutFile >= 0) 58178d41c6aSeric { 58278d41c6aSeric syserr("callprog: setting stdout w/o forking"); 58378d41c6aSeric exit(EX_SOFTWARE); 58478d41c6aSeric } 5857de81dc7Seric 58678d41c6aSeric /* set protection as appropriate */ 5877de81dc7Seric if (bitset(REALUSER, flags)) 5887de81dc7Seric setuid(getuid()); 5897de81dc7Seric 59078d41c6aSeric /* change standard input & output if needed */ 59178d41c6aSeric if (OutFile >= 0) 59278d41c6aSeric { 59378d41c6aSeric close(1); 59478d41c6aSeric dup(OutFile); 59578d41c6aSeric close(OutFile); 59678d41c6aSeric } 5977de81dc7Seric 59878d41c6aSeric /* call real SCCS program */ 599172147faSeric execv(progpath, argv); 600095915d3Seric syserr("cannot execute %s", progpath); 601adf8f7d4Seric exit(EX_UNAVAILABLE); 602adf8f7d4Seric } 6030faf63f9Seric 6040faf63f9Seric /* 605cdc1aa65Seric ** MAKEFILE -- make filename of SCCS file 606cdc1aa65Seric ** 607cdc1aa65Seric ** If the name passed is already the name of an SCCS file, 608cdc1aa65Seric ** just return it. Otherwise, munge the name into the name 609cdc1aa65Seric ** of the actual SCCS file. 610cdc1aa65Seric ** 611cdc1aa65Seric ** There are cases when it is not clear what you want to 612cdc1aa65Seric ** do. For example, if SccsPath is an absolute pathname 613cdc1aa65Seric ** and the name given is also an absolute pathname, we go 614cdc1aa65Seric ** for SccsPath (& only use the last component of the name 615cdc1aa65Seric ** passed) -- this is important for security reasons (if 616cdc1aa65Seric ** sccs is being used as a setuid front end), but not 617cdc1aa65Seric ** particularly intuitive. 618cdc1aa65Seric ** 619cdc1aa65Seric ** Parameters: 620cdc1aa65Seric ** name -- the file name to be munged. 621cdc1aa65Seric ** 622cdc1aa65Seric ** Returns: 623cdc1aa65Seric ** The pathname of the sccs file. 624cdc1aa65Seric ** NULL on error. 625cdc1aa65Seric ** 626cdc1aa65Seric ** Side Effects: 627cdc1aa65Seric ** none. 628cdc1aa65Seric */ 629adf8f7d4Seric 630adf8f7d4Seric char * 631adf8f7d4Seric makefile(name) 632adf8f7d4Seric char *name; 633adf8f7d4Seric { 634adf8f7d4Seric register char *p; 635adf8f7d4Seric register char c; 636adf8f7d4Seric char buf[512]; 6370a8b9ab0Seric struct stat stbuf; 638adf8f7d4Seric extern char *malloc(); 639cdc1aa65Seric extern char *rindex(); 6400a8b9ab0Seric extern bool safepath(); 6417d70f5d8Seric extern bool isdir(); 6427d70f5d8Seric register char *q; 643cdc1aa65Seric 644cdc1aa65Seric p = rindex(name, '/'); 645cdc1aa65Seric if (p == NULL) 646cdc1aa65Seric p = name; 647cdc1aa65Seric else 648cdc1aa65Seric p++; 649adf8f7d4Seric 650adf8f7d4Seric /* 6510a8b9ab0Seric ** Check to see that the path is "safe", i.e., that we 6520a8b9ab0Seric ** are not letting some nasty person use the setuid part 6530a8b9ab0Seric ** of this program to look at or munge some presumably 6540a8b9ab0Seric ** hidden files. 655adf8f7d4Seric */ 656adf8f7d4Seric 6570a8b9ab0Seric if (SccsDir[0] == '/' && !safepath(name)) 6580a8b9ab0Seric return (NULL); 659cdc1aa65Seric 660cdc1aa65Seric /* 6610a8b9ab0Seric ** Create the base pathname. 662cdc1aa65Seric */ 663cdc1aa65Seric 664ff038098Seric /* first the directory part */ 6650a8b9ab0Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) 666adf8f7d4Seric { 6670a8b9ab0Seric strcpy(buf, SccsDir); 668cdc1aa65Seric strcat(buf, "/"); 669cdc1aa65Seric } 670cdc1aa65Seric else 671cdc1aa65Seric strcpy(buf, ""); 672ff038098Seric 673ff038098Seric /* then the head of the pathname */ 674cdc1aa65Seric strncat(buf, name, p - name); 6757d70f5d8Seric q = &buf[strlen(buf)]; 676ff038098Seric 677ff038098Seric /* now copy the final part of the name, in case useful */ 6787d70f5d8Seric strcpy(q, p); 679ff038098Seric 680ff038098Seric /* so is it useful? */ 6817d70f5d8Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 6827d70f5d8Seric { 683ff038098Seric /* sorry, no; copy the SCCS pathname & the "s." */ 6840a8b9ab0Seric strcpy(q, SccsPath); 6850a8b9ab0Seric strcat(buf, "/s."); 686ff038098Seric 687ff038098Seric /* and now the end of the name */ 688cdc1aa65Seric strcat(buf, p); 689cdc1aa65Seric } 690adf8f7d4Seric 691ff038098Seric /* if i haven't changed it, why did I do all this? */ 6920a8b9ab0Seric if (strcmp(buf, name) == 0) 6930a8b9ab0Seric p = name; 6940a8b9ab0Seric else 6950a8b9ab0Seric { 696ff038098Seric /* but if I have, squirrel it away */ 697adf8f7d4Seric p = malloc(strlen(buf) + 1); 698adf8f7d4Seric if (p == NULL) 699adf8f7d4Seric { 700adf8f7d4Seric perror("Sccs: no mem"); 701adf8f7d4Seric exit(EX_OSERR); 702adf8f7d4Seric } 703adf8f7d4Seric strcpy(p, buf); 7040a8b9ab0Seric } 705ff038098Seric 706adf8f7d4Seric return (p); 707adf8f7d4Seric } 7080faf63f9Seric 7090faf63f9Seric /* 7107d70f5d8Seric ** ISDIR -- return true if the argument is a directory. 7117d70f5d8Seric ** 7127d70f5d8Seric ** Parameters: 7137d70f5d8Seric ** name -- the pathname of the file to check. 7147d70f5d8Seric ** 7157d70f5d8Seric ** Returns: 7167d70f5d8Seric ** TRUE if 'name' is a directory, FALSE otherwise. 7177d70f5d8Seric ** 7187d70f5d8Seric ** Side Effects: 7197d70f5d8Seric ** none. 7207d70f5d8Seric */ 7217d70f5d8Seric 7227d70f5d8Seric bool 7237d70f5d8Seric isdir(name) 7247d70f5d8Seric char *name; 7257d70f5d8Seric { 7267d70f5d8Seric struct stat stbuf; 7277d70f5d8Seric 7287d70f5d8Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 7297d70f5d8Seric } 7300faf63f9Seric 7310faf63f9Seric /* 732cdc1aa65Seric ** SAFEPATH -- determine whether a pathname is "safe" 733cdc1aa65Seric ** 734cdc1aa65Seric ** "Safe" pathnames only allow you to get deeper into the 735cdc1aa65Seric ** directory structure, i.e., full pathnames and ".." are 736cdc1aa65Seric ** not allowed. 737cdc1aa65Seric ** 738cdc1aa65Seric ** Parameters: 739cdc1aa65Seric ** p -- the name to check. 740cdc1aa65Seric ** 741cdc1aa65Seric ** Returns: 742cdc1aa65Seric ** TRUE -- if the path is safe. 743cdc1aa65Seric ** FALSE -- if the path is not safe. 744cdc1aa65Seric ** 745cdc1aa65Seric ** Side Effects: 746cdc1aa65Seric ** Prints a message if the path is not safe. 747cdc1aa65Seric */ 748cdc1aa65Seric 749cdc1aa65Seric bool 750cdc1aa65Seric safepath(p) 751cdc1aa65Seric register char *p; 752cdc1aa65Seric { 753cdc1aa65Seric extern char *index(); 754cdc1aa65Seric 755cdc1aa65Seric if (*p != '/') 756cdc1aa65Seric { 757cdc1aa65Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 758cdc1aa65Seric { 759cdc1aa65Seric p = index(p, '/'); 760cdc1aa65Seric if (p == NULL) 761cdc1aa65Seric return (TRUE); 762cdc1aa65Seric p++; 763cdc1aa65Seric } 764cdc1aa65Seric } 765cdc1aa65Seric 766cdc1aa65Seric printf("You may not use full pathnames or \"..\"\n"); 767cdc1aa65Seric return (FALSE); 768cdc1aa65Seric } 7690faf63f9Seric 7700faf63f9Seric /* 771b2538d76Seric ** CLEAN -- clean out recreatable files 772b2538d76Seric ** 773b2538d76Seric ** Any file for which an "s." file exists but no "p." file 774b2538d76Seric ** exists in the current directory is purged. 775b2538d76Seric ** 776b2538d76Seric ** Parameters: 77748ff0e7eSeric ** tells whether this came from a "clean", "info", or 77848ff0e7eSeric ** "check" command. 779b2538d76Seric ** 780b2538d76Seric ** Returns: 781b2538d76Seric ** none. 782b2538d76Seric ** 783b2538d76Seric ** Side Effects: 78448ff0e7eSeric ** Removes files in the current directory. 78548ff0e7eSeric ** Prints information regarding files being edited. 78648ff0e7eSeric ** Exits if a "check" command. 787b2538d76Seric */ 788b2538d76Seric 78948ff0e7eSeric clean(mode) 79048ff0e7eSeric int mode; 791b2538d76Seric { 792b2538d76Seric struct direct dir; 793b2538d76Seric struct stat stbuf; 794b2538d76Seric char buf[100]; 795bf9c1e66Seric char pline[120]; 7963a208c77Seric register FILE *dirfd; 7973a208c77Seric register char *basefile; 7982caac8fdSeric bool gotedit; 799bf9c1e66Seric FILE *pfp; 800b2538d76Seric 801ff038098Seric /* 802ff038098Seric ** Find and open the SCCS directory. 803ff038098Seric */ 804ff038098Seric 805f77a08fbSeric strcpy(buf, SccsDir); 806f77a08fbSeric if (buf[0] != '\0') 807f77a08fbSeric strcat(buf, "/"); 808f77a08fbSeric strcat(buf, SccsPath); 809ff038098Seric 810f77a08fbSeric dirfd = fopen(buf, "r"); 811b2538d76Seric if (dirfd == NULL) 812b2538d76Seric { 813f77a08fbSeric usrerr("cannot open %s", buf); 81441290c52Seric return (EX_NOINPUT); 815b2538d76Seric } 816b2538d76Seric 817b2538d76Seric /* 818b2538d76Seric ** Scan the SCCS directory looking for s. files. 819ff038098Seric ** gotedit tells whether we have tried to clean any 820ff038098Seric ** files that are being edited. 821b2538d76Seric */ 822b2538d76Seric 8232caac8fdSeric gotedit = FALSE; 824b2538d76Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 825b2538d76Seric { 826c9c62fd0Seric if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) 827b2538d76Seric continue; 828b2538d76Seric 829b2538d76Seric /* got an s. file -- see if the p. file exists */ 830f77a08fbSeric strcpy(buf, SccsDir); 831f77a08fbSeric if (buf[0] != '\0') 832f77a08fbSeric strcat(buf, "/"); 833f77a08fbSeric strcat(buf, SccsPath); 834b2538d76Seric strcat(buf, "/p."); 8353a208c77Seric basefile = &buf[strlen(buf)]; 836c9c62fd0Seric strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 837bf9c1e66Seric basefile[sizeof dir.d_name - 2] = '\0'; 838bf9c1e66Seric pfp = fopen(buf, "r"); 839bf9c1e66Seric if (pfp != NULL) 8403a208c77Seric { 841ff038098Seric /* the file exists -- report it's contents */ 842bf9c1e66Seric while (fgets(pline, sizeof pline, pfp) != NULL) 8432444cd3eSeric printf("%12s: being edited: %s", basefile, pline); 844bf9c1e66Seric fclose(pfp); 8452caac8fdSeric gotedit = TRUE; 846b2538d76Seric continue; 8473a208c77Seric } 848b2538d76Seric 849b2538d76Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 85048ff0e7eSeric if (mode == CLEANC) 8513a208c77Seric { 852c9c62fd0Seric strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); 8533a208c77Seric buf[sizeof dir.d_name - 2] = '\0'; 854b2538d76Seric unlink(buf); 855b2538d76Seric } 8563a208c77Seric } 857b2538d76Seric 858ff038098Seric /* cleanup & report results */ 859b2538d76Seric fclose(dirfd); 86048ff0e7eSeric if (!gotedit && mode == INFOC) 8612444cd3eSeric printf("Nothing being edited\n"); 86248ff0e7eSeric if (mode == CHECKC) 86348ff0e7eSeric exit(gotedit); 86441290c52Seric return (EX_OK); 865b2538d76Seric } 8660faf63f9Seric 8670faf63f9Seric /* 868e39a5722Seric ** UNEDIT -- unedit a file 869e39a5722Seric ** 870e39a5722Seric ** Checks to see that the current user is actually editting 871e39a5722Seric ** the file and arranges that s/he is not editting it. 872e39a5722Seric ** 873e39a5722Seric ** Parameters: 8742444cd3eSeric ** fn -- the name of the file to be unedited. 875e39a5722Seric ** 876e39a5722Seric ** Returns: 877d51cd7e5Seric ** TRUE -- if the file was successfully unedited. 878d51cd7e5Seric ** FALSE -- if the file was not unedited for some 879d51cd7e5Seric ** reason. 880e39a5722Seric ** 881e39a5722Seric ** Side Effects: 882e39a5722Seric ** fn is removed 883e39a5722Seric ** entries are removed from pfile. 884e39a5722Seric */ 885e39a5722Seric 886d51cd7e5Seric bool 887e39a5722Seric unedit(fn) 888e39a5722Seric char *fn; 889e39a5722Seric { 890e39a5722Seric register FILE *pfp; 891e39a5722Seric char *pfn; 892e39a5722Seric static char tfn[] = "/tmp/sccsXXXXX"; 893e39a5722Seric FILE *tfp; 894e39a5722Seric register char *p; 895e39a5722Seric register char *q; 896e39a5722Seric bool delete = FALSE; 897e39a5722Seric bool others = FALSE; 898e39a5722Seric char *myname; 899e39a5722Seric extern char *getlogin(); 900e39a5722Seric struct pfile *pent; 901e39a5722Seric extern struct pfile *getpfile(); 902e39a5722Seric char buf[120]; 903108d6082Seric extern char *makefile(); 90410cd16efSeric # ifdef UIDUSER 90510cd16efSeric struct passwd *pw; 90610cd16efSeric extern struct passwd *getpwuid(); 90710cd16efSeric # endif UIDUSER 908e39a5722Seric 909e39a5722Seric /* make "s." filename & find the trailing component */ 910e39a5722Seric pfn = makefile(fn); 911cdc1aa65Seric if (pfn == NULL) 912cdc1aa65Seric return (FALSE); 913cdc1aa65Seric q = rindex(pfn, '/'); 914cdc1aa65Seric if (q == NULL) 915cdc1aa65Seric q = &pfn[-1]; 916cdc1aa65Seric if (q[1] != 's' || q[2] != '.') 917e39a5722Seric { 918095915d3Seric usrerr("bad file name \"%s\"", fn); 919d51cd7e5Seric return (FALSE); 920e39a5722Seric } 921e39a5722Seric 922ff038098Seric /* turn "s." into "p." & try to open it */ 923e39a5722Seric *++q = 'p'; 924e39a5722Seric 925e39a5722Seric pfp = fopen(pfn, "r"); 926e39a5722Seric if (pfp == NULL) 927e39a5722Seric { 9282444cd3eSeric printf("%12s: not being edited\n", fn); 929d51cd7e5Seric return (FALSE); 930e39a5722Seric } 931e39a5722Seric 932ff038098Seric /* create temp file for editing p-file */ 933e39a5722Seric mktemp(tfn); 934e39a5722Seric tfp = fopen(tfn, "w"); 935e39a5722Seric if (tfp == NULL) 936e39a5722Seric { 937095915d3Seric usrerr("cannot create \"%s\"", tfn); 938e39a5722Seric exit(EX_OSERR); 939e39a5722Seric } 940e39a5722Seric 941ff038098Seric /* figure out who I am */ 94210cd16efSeric # ifdef UIDUSER 94310cd16efSeric pw = getpwuid(getuid()); 94410cd16efSeric if (pw == NULL) 94510cd16efSeric { 946095915d3Seric syserr("who are you? (uid=%d)", getuid()); 94710cd16efSeric exit(EX_OSERR); 94810cd16efSeric } 94910cd16efSeric myname = pw->pw_name; 95010cd16efSeric # else 951e39a5722Seric myname = getlogin(); 95210cd16efSeric # endif UIDUSER 953ff038098Seric 954ff038098Seric /* 955ff038098Seric ** Copy p-file to temp file, doing deletions as needed. 956ff038098Seric */ 957ff038098Seric 958e39a5722Seric while ((pent = getpfile(pfp)) != NULL) 959e39a5722Seric { 960e39a5722Seric if (strcmp(pent->p_user, myname) == 0) 961e39a5722Seric { 962e39a5722Seric /* a match */ 963e39a5722Seric delete++; 964e39a5722Seric } 965e39a5722Seric else 966e39a5722Seric { 967ff038098Seric /* output it again */ 968e39a5722Seric fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, 969e39a5722Seric pent->p_nsid, pent->p_user, pent->p_date, 970e39a5722Seric pent->p_time); 971e39a5722Seric others++; 972e39a5722Seric } 973e39a5722Seric } 974e39a5722Seric 975e39a5722Seric /* do final cleanup */ 976e39a5722Seric if (others) 977e39a5722Seric { 978ff038098Seric /* copy it back (perhaps it should be linked?) */ 979e39a5722Seric if (freopen(tfn, "r", tfp) == NULL) 980e39a5722Seric { 981095915d3Seric syserr("cannot reopen \"%s\"", tfn); 982e39a5722Seric exit(EX_OSERR); 983e39a5722Seric } 984e39a5722Seric if (freopen(pfn, "w", pfp) == NULL) 985e39a5722Seric { 986095915d3Seric usrerr("cannot create \"%s\"", pfn); 987d51cd7e5Seric return (FALSE); 988e39a5722Seric } 989e39a5722Seric while (fgets(buf, sizeof buf, tfp) != NULL) 990e39a5722Seric fputs(buf, pfp); 991e39a5722Seric } 992e39a5722Seric else 993e39a5722Seric { 994ff038098Seric /* it's empty -- remove it */ 995e39a5722Seric unlink(pfn); 996e39a5722Seric } 997e39a5722Seric fclose(tfp); 998e39a5722Seric fclose(pfp); 999e39a5722Seric unlink(tfn); 1000e39a5722Seric 1001ff038098Seric /* actually remove the g-file */ 1002e39a5722Seric if (delete) 1003e39a5722Seric { 1004287bbaa2Seric unlink(tail(fn)); 1005287bbaa2Seric printf("%12s: removed\n", tail(fn)); 1006d51cd7e5Seric return (TRUE); 1007e39a5722Seric } 1008e39a5722Seric else 1009e39a5722Seric { 10102444cd3eSeric printf("%12s: not being edited by you\n", fn); 1011d51cd7e5Seric return (FALSE); 1012e39a5722Seric } 1013e39a5722Seric } 10140faf63f9Seric 10150faf63f9Seric /* 101678d41c6aSeric ** DODIFF -- diff an s-file against a g-file 101778d41c6aSeric ** 101878d41c6aSeric ** Parameters: 101978d41c6aSeric ** getv -- argv for the 'get' command. 102078d41c6aSeric ** gfile -- name of the g-file to diff against. 102178d41c6aSeric ** 102278d41c6aSeric ** Returns: 102378d41c6aSeric ** Result of get. 102478d41c6aSeric ** 102578d41c6aSeric ** Side Effects: 102678d41c6aSeric ** none. 102778d41c6aSeric */ 102878d41c6aSeric 102978d41c6aSeric dodiff(getv, gfile) 103078d41c6aSeric char **getv; 103178d41c6aSeric char *gfile; 103278d41c6aSeric { 103378d41c6aSeric int pipev[2]; 103478d41c6aSeric int rval; 103578d41c6aSeric register int i; 103678d41c6aSeric register int pid; 103778d41c6aSeric auto int st; 103878d41c6aSeric extern int errno; 103978d41c6aSeric int (*osig)(); 104078d41c6aSeric 1041*cc4e4f0dSeric printf("\n------- %s -------\n", gfile); 1042*cc4e4f0dSeric 1043ff038098Seric /* create context for diff to run in */ 104478d41c6aSeric if (pipe(pipev) < 0) 104578d41c6aSeric { 104678d41c6aSeric syserr("dodiff: pipe failed"); 104778d41c6aSeric exit(EX_OSERR); 104878d41c6aSeric } 104978d41c6aSeric if ((pid = fork()) < 0) 105078d41c6aSeric { 105178d41c6aSeric syserr("dodiff: fork failed"); 105278d41c6aSeric exit(EX_OSERR); 105378d41c6aSeric } 105478d41c6aSeric else if (pid > 0) 105578d41c6aSeric { 105678d41c6aSeric /* in parent; run get */ 105778d41c6aSeric OutFile = pipev[1]; 105878d41c6aSeric close(pipev[0]); 105978d41c6aSeric rval = command(&getv[1], TRUE, FALSE, "get -s -k -p"); 106078d41c6aSeric osig = signal(SIGINT, SIG_IGN); 106178d41c6aSeric while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR) 106278d41c6aSeric errno = 0; 106378d41c6aSeric signal(SIGINT, osig); 106478d41c6aSeric /* ignore result of diff */ 106578d41c6aSeric } 106678d41c6aSeric else 106778d41c6aSeric { 106878d41c6aSeric /* in child, run diff */ 106978d41c6aSeric if (close(pipev[1]) < 0 || close(0) < 0 || 107078d41c6aSeric dup(pipev[0]) != 0 || close(pipev[0]) < 0) 107178d41c6aSeric { 107278d41c6aSeric syserr("dodiff: magic failed"); 107378d41c6aSeric exit(EX_OSERR); 107478d41c6aSeric } 107578d41c6aSeric execl(PROGPATH(bdiff), "bdiff", "-", gfile, NULL); 107678d41c6aSeric # ifndef V6 107778d41c6aSeric execlp("bdiff", "bdiff", "-", gfile, NULL); 107878d41c6aSeric execlp("diff", "diff", "-", gfile, NULL); 107978d41c6aSeric # endif NOT V6 108078d41c6aSeric syserr("bdiff: cannot execute"); 108178d41c6aSeric exit(EX_OSERR); 108278d41c6aSeric } 108378d41c6aSeric return (rval); 108478d41c6aSeric } 108578d41c6aSeric 108678d41c6aSeric /* 1087287bbaa2Seric ** TAIL -- return tail of filename. 1088287bbaa2Seric ** 1089287bbaa2Seric ** Parameters: 1090287bbaa2Seric ** fn -- the filename. 1091287bbaa2Seric ** 1092287bbaa2Seric ** Returns: 1093287bbaa2Seric ** a pointer to the tail of the filename; e.g., given 1094287bbaa2Seric ** "cmd/ls.c", "ls.c" is returned. 1095287bbaa2Seric ** 1096287bbaa2Seric ** Side Effects: 1097287bbaa2Seric ** none. 1098287bbaa2Seric */ 1099287bbaa2Seric 1100287bbaa2Seric char * 1101287bbaa2Seric tail(fn) 1102287bbaa2Seric register char *fn; 1103287bbaa2Seric { 1104287bbaa2Seric register char *p; 1105287bbaa2Seric 1106287bbaa2Seric for (p = fn; *p != 0; p++) 1107287bbaa2Seric if (*p == '/' && p[1] != '\0' && p[1] != '/') 1108287bbaa2Seric fn = &p[1]; 1109287bbaa2Seric return (fn); 1110287bbaa2Seric } 1111287bbaa2Seric 1112287bbaa2Seric /* 1113e39a5722Seric ** GETPFILE -- get an entry from the p-file 1114e39a5722Seric ** 1115e39a5722Seric ** Parameters: 1116e39a5722Seric ** pfp -- p-file file pointer 1117e39a5722Seric ** 1118e39a5722Seric ** Returns: 1119e39a5722Seric ** pointer to p-file struct for next entry 1120e39a5722Seric ** NULL on EOF or error 1121e39a5722Seric ** 1122e39a5722Seric ** Side Effects: 1123e39a5722Seric ** Each call wipes out results of previous call. 1124e39a5722Seric */ 1125e39a5722Seric 1126e39a5722Seric struct pfile * 1127e39a5722Seric getpfile(pfp) 1128e39a5722Seric FILE *pfp; 1129e39a5722Seric { 1130e39a5722Seric static struct pfile ent; 1131e39a5722Seric static char buf[120]; 1132e39a5722Seric register char *p; 1133e39a5722Seric extern char *nextfield(); 1134e39a5722Seric 1135e39a5722Seric if (fgets(buf, sizeof buf, pfp) == NULL) 1136e39a5722Seric return (NULL); 1137e39a5722Seric 1138e39a5722Seric ent.p_osid = p = buf; 1139e39a5722Seric ent.p_nsid = p = nextfield(p); 1140e39a5722Seric ent.p_user = p = nextfield(p); 1141e39a5722Seric ent.p_date = p = nextfield(p); 1142e39a5722Seric ent.p_time = p = nextfield(p); 1143e39a5722Seric if (p == NULL || nextfield(p) != NULL) 1144e39a5722Seric return (NULL); 1145e39a5722Seric 1146e39a5722Seric return (&ent); 1147e39a5722Seric } 1148e39a5722Seric 1149e39a5722Seric 1150e39a5722Seric char * 1151e39a5722Seric nextfield(p) 1152e39a5722Seric register char *p; 1153e39a5722Seric { 1154e39a5722Seric if (p == NULL || *p == '\0') 1155e39a5722Seric return (NULL); 1156e39a5722Seric while (*p != ' ' && *p != '\n' && *p != '\0') 1157e39a5722Seric p++; 1158e39a5722Seric if (*p == '\n' || *p == '\0') 1159e39a5722Seric { 1160e39a5722Seric *p = '\0'; 1161e39a5722Seric return (NULL); 1162e39a5722Seric } 1163e39a5722Seric *p++ = '\0'; 1164e39a5722Seric return (p); 1165e39a5722Seric } 11660faf63f9Seric 11670faf63f9Seric /* 1168095915d3Seric ** USRERR -- issue user-level error 1169095915d3Seric ** 1170095915d3Seric ** Parameters: 1171095915d3Seric ** f -- format string. 1172095915d3Seric ** p1-p3 -- parameters to a printf. 1173095915d3Seric ** 1174095915d3Seric ** Returns: 1175095915d3Seric ** -1 1176095915d3Seric ** 1177095915d3Seric ** Side Effects: 1178095915d3Seric ** none. 1179095915d3Seric */ 1180095915d3Seric 1181095915d3Seric usrerr(f, p1, p2, p3) 1182095915d3Seric char *f; 1183095915d3Seric { 1184095915d3Seric fprintf(stderr, "\n%s: ", MyName); 1185095915d3Seric fprintf(stderr, f, p1, p2, p3); 1186095915d3Seric fprintf(stderr, "\n"); 1187095915d3Seric 1188095915d3Seric return (-1); 1189095915d3Seric } 11900faf63f9Seric 11910faf63f9Seric /* 1192095915d3Seric ** SYSERR -- print system-generated error. 1193095915d3Seric ** 1194095915d3Seric ** Parameters: 1195095915d3Seric ** f -- format string to a printf. 1196095915d3Seric ** p1, p2, p3 -- parameters to f. 1197095915d3Seric ** 1198095915d3Seric ** Returns: 1199095915d3Seric ** never. 1200095915d3Seric ** 1201095915d3Seric ** Side Effects: 1202095915d3Seric ** none. 1203095915d3Seric */ 1204095915d3Seric 1205095915d3Seric syserr(f, p1, p2, p3) 1206095915d3Seric char *f; 1207095915d3Seric { 1208095915d3Seric extern int errno; 1209095915d3Seric 1210095915d3Seric fprintf(stderr, "\n%s SYSERR: ", MyName); 1211095915d3Seric fprintf(stderr, f, p1, p2, p3); 1212095915d3Seric fprintf(stderr, "\n"); 1213095915d3Seric if (errno == 0) 1214095915d3Seric exit(EX_SOFTWARE); 1215095915d3Seric else 1216095915d3Seric { 1217095915d3Seric perror(0); 1218095915d3Seric exit(EX_OSERR); 1219095915d3Seric } 1220095915d3Seric } 1221