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*1e0924d0Seric static char SccsId[] = "@(#)sccs.c 1.49 12/02/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 1076de0267aSeric # ifdef INGVAX 1086de0267aSeric # define PROGPATH(name) "/usr/local/name" 1096de0267aSeric # endif INGVAX 1106de0267aSeric 111c4ad8825Seric /* end of berkeley systems defines */ 1127abec564Seric 113c4ad8825Seric # ifndef SCCSPATH 114c4ad8825Seric # define SCCSPATH "SCCS" /* pathname in which to find s-files */ 115c4ad8825Seric # endif NOT SCCSPATH 116c4ad8825Seric 117c4ad8825Seric # ifndef MYNAME 118c4ad8825Seric # define MYNAME "sccs" /* name used for printing errors */ 119c4ad8825Seric # endif NOT MYNAME 1207abec564Seric 1210faf63f9Seric # ifndef PROGPATH 1220faf63f9Seric # define PROGPATH(name) "/usr/sccs/name" /* place to find binaries */ 1230faf63f9Seric # endif PROGPATH 1240faf63f9Seric 1257abec564Seric /**************** End of Configuration Information ****************/ 1260faf63f9Seric 127c4432be4Seric typedef char bool; 1287de81dc7Seric # define TRUE 1 1297de81dc7Seric # define FALSE 0 130d02a4f42Seric 131ff038098Seric # define bitset(bit, word) ((bool) ((bit) & (word))) 132ff038098Seric 13310cd16efSeric # ifdef UIDUSER 13410cd16efSeric # include <pwd.h> 13510cd16efSeric # endif UIDUSER 13610cd16efSeric 137adf8f7d4Seric struct sccsprog 138adf8f7d4Seric { 139adf8f7d4Seric char *sccsname; /* name of SCCS routine */ 1407de81dc7Seric short sccsoper; /* opcode, see below */ 1417de81dc7Seric short sccsflags; /* flags, see below */ 142adf8f7d4Seric char *sccspath; /* pathname of binary implementing */ 143adf8f7d4Seric }; 144adf8f7d4Seric 1457de81dc7Seric /* values for sccsoper */ 1467de81dc7Seric # define PROG 0 /* call a program */ 1471777fbcbSeric # define CMACRO 1 /* command substitution macro */ 148172147faSeric # define FIX 2 /* fix a delta */ 149b2538d76Seric # define CLEAN 3 /* clean out recreatable files */ 150e39a5722Seric # define UNEDIT 4 /* unedit a file */ 151419708b0Seric # define SHELL 5 /* call a shell file (like PROG) */ 15278d41c6aSeric # define DIFFS 6 /* diff between sccs & file out */ 1537de81dc7Seric 154c4432be4Seric /* bits for sccsflags */ 1557de81dc7Seric # define NO_SDOT 0001 /* no s. on front of args */ 1567de81dc7Seric # define REALUSER 0002 /* protected (e.g., admin) */ 157adf8f7d4Seric 15848ff0e7eSeric /* modes for the "clean", "info", "check" ops */ 15948ff0e7eSeric # define CLEANC 0 /* clean command */ 16048ff0e7eSeric # define INFOC 1 /* info command */ 16148ff0e7eSeric # define CHECKC 2 /* check command */ 1622734e237Seric # define TELLC 3 /* give list of files being edited */ 16348ff0e7eSeric 1640faf63f9Seric /* 1650faf63f9Seric ** Description of commands known to this program. 1660faf63f9Seric ** First argument puts the command into a class. Second arg is 1670faf63f9Seric ** info regarding treatment of this command. Third arg is a 1680faf63f9Seric ** list of flags this command accepts from macros, etc. Fourth 1690faf63f9Seric ** arg is the pathname of the implementing program, or the 1700faf63f9Seric ** macro definition, or the arg to a sub-algorithm. 1710faf63f9Seric */ 172b5d4f080Seric 173adf8f7d4Seric struct sccsprog SccsProg[] = 174adf8f7d4Seric { 1753642a8d2Seric "admin", PROG, REALUSER, PROGPATH(admin), 1763642a8d2Seric "chghist", PROG, 0, PROGPATH(rmdel), 1773642a8d2Seric "comb", PROG, 0, PROGPATH(comb), 1783642a8d2Seric "delta", PROG, 0, PROGPATH(delta), 1793642a8d2Seric "get", PROG, 0, PROGPATH(get), 1803642a8d2Seric "help", PROG, NO_SDOT, PROGPATH(help), 1813642a8d2Seric "prt", PROG, 0, PROGPATH(prt), 1823642a8d2Seric "rmdel", PROG, REALUSER, PROGPATH(rmdel), 1833642a8d2Seric "what", PROG, NO_SDOT, PROGPATH(what), 1843642a8d2Seric "sccsdiff", SHELL, REALUSER, PROGPATH(sccsdiff), 1853642a8d2Seric "edit", CMACRO, NO_SDOT, "get -e", 18659e81a43Seric "delget", CMACRO, NO_SDOT, "delta:mysrp/get:ixbeskcl -t", 18759e81a43Seric "deledit", CMACRO, NO_SDOT, "delta:mysrp/get:ixbskcl -e -t", 1883642a8d2Seric "fix", FIX, NO_SDOT, NULL, 189*1e0924d0Seric "clean", CLEAN, REALUSER|NO_SDOT, (char *) CLEANC, 190*1e0924d0Seric "info", CLEAN, REALUSER|NO_SDOT, (char *) INFOC, 191*1e0924d0Seric "check", CLEAN, REALUSER|NO_SDOT, (char *) CHECKC, 192*1e0924d0Seric "tell", CLEAN, REALUSER|NO_SDOT, (char *) TELLC, 1933642a8d2Seric "unedit", UNEDIT, NO_SDOT, NULL, 1943642a8d2Seric "diffs", DIFFS, NO_SDOT|REALUSER, NULL, 1953642a8d2Seric NULL, -1, 0, NULL 196adf8f7d4Seric }; 197adf8f7d4Seric 1980faf63f9Seric /* one line from a p-file */ 199e39a5722Seric struct pfile 200e39a5722Seric { 201e39a5722Seric char *p_osid; /* old SID */ 202e39a5722Seric char *p_nsid; /* new SID */ 203e39a5722Seric char *p_user; /* user who did edit */ 204e39a5722Seric char *p_date; /* date of get */ 205e39a5722Seric char *p_time; /* time of get */ 206e39a5722Seric }; 207e39a5722Seric 2087abec564Seric char *SccsPath = SCCSPATH; /* pathname of SCCS files */ 2097abec564Seric # ifdef SCCSDIR 2107abec564Seric char *SccsDir = SCCSDIR; /* directory to begin search from */ 211095915d3Seric # else 2127abec564Seric char *SccsDir = ""; 213095915d3Seric # endif 214c4ad8825Seric char MyName[] = MYNAME; /* name used in messages */ 21578d41c6aSeric int OutFile = -1; /* override output file for commands */ 216c4432be4Seric bool RealUser; /* if set, running as real user */ 217f0cc3246Seric # ifdef DEBUG 218f0cc3246Seric bool Debug; /* turn on tracing */ 219f0cc3246Seric # endif 2200faf63f9Seric 221adf8f7d4Seric main(argc, argv) 222adf8f7d4Seric int argc; 223adf8f7d4Seric char **argv; 224adf8f7d4Seric { 225adf8f7d4Seric register char *p; 226b1ed8a43Seric extern struct sccsprog *lookup(); 22741290c52Seric register int i; 228adf8f7d4Seric 229adf8f7d4Seric /* 230adf8f7d4Seric ** Detect and decode flags intended for this program. 231adf8f7d4Seric */ 232adf8f7d4Seric 2337de81dc7Seric if (argc < 2) 234adf8f7d4Seric { 235095915d3Seric fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); 2367de81dc7Seric exit(EX_USAGE); 2377de81dc7Seric } 2387de81dc7Seric argv[argc] = NULL; 2397de81dc7Seric 240b1ed8a43Seric if (lookup(argv[0]) == NULL) 241b1ed8a43Seric { 2427de81dc7Seric while ((p = *++argv) != NULL) 2437de81dc7Seric { 244adf8f7d4Seric if (*p != '-') 245adf8f7d4Seric break; 246adf8f7d4Seric switch (*++p) 247adf8f7d4Seric { 248adf8f7d4Seric case 'r': /* run as real user */ 249adf8f7d4Seric setuid(getuid()); 250c4432be4Seric RealUser++; 251adf8f7d4Seric break; 252adf8f7d4Seric 2537abec564Seric # ifndef SCCSDIR 254adf8f7d4Seric case 'p': /* path of sccs files */ 255adf8f7d4Seric SccsPath = ++p; 256adf8f7d4Seric break; 257adf8f7d4Seric 2580a8b9ab0Seric case 'd': /* directory to search from */ 2590a8b9ab0Seric SccsDir = ++p; 2600a8b9ab0Seric break; 261095915d3Seric # endif 2620a8b9ab0Seric 263f0cc3246Seric # ifdef DEBUG 264f0cc3246Seric case 'T': /* trace */ 265f0cc3246Seric Debug++; 266f0cc3246Seric break; 267f0cc3246Seric # endif 268f0cc3246Seric 269adf8f7d4Seric default: 270095915d3Seric usrerr("unknown option -%s", p); 271adf8f7d4Seric break; 272adf8f7d4Seric } 273adf8f7d4Seric } 2745cabffd9Seric if (SccsPath[0] == '\0') 2755cabffd9Seric SccsPath = "."; 276b1ed8a43Seric } 277adf8f7d4Seric 2783642a8d2Seric i = command(argv, FALSE, ""); 27941290c52Seric exit(i); 2807de81dc7Seric } 2810faf63f9Seric 2820faf63f9Seric /* 28341290c52Seric ** COMMAND -- look up and perform a command 28441290c52Seric ** 28541290c52Seric ** This routine is the guts of this program. Given an 28641290c52Seric ** argument vector, it looks up the "command" (argv[0]) 28741290c52Seric ** in the configuration table and does the necessary stuff. 28841290c52Seric ** 28941290c52Seric ** Parameters: 29041290c52Seric ** argv -- an argument vector to process. 29141290c52Seric ** forkflag -- if set, fork before executing the command. 292108d6082Seric ** editflag -- if set, only include flags listed in the 293108d6082Seric ** sccsklets field of the command descriptor. 294108d6082Seric ** arg0 -- a space-seperated list of arguments to insert 295108d6082Seric ** before argv. 29641290c52Seric ** 29741290c52Seric ** Returns: 29841290c52Seric ** zero -- command executed ok. 29941290c52Seric ** else -- error status. 30041290c52Seric ** 30141290c52Seric ** Side Effects: 30241290c52Seric ** none. 30341290c52Seric */ 3047de81dc7Seric 3053642a8d2Seric command(argv, forkflag, arg0) 3067de81dc7Seric char **argv; 3071777fbcbSeric bool forkflag; 308108d6082Seric char *arg0; 3097de81dc7Seric { 3107de81dc7Seric register struct sccsprog *cmd; 3117de81dc7Seric register char *p; 3121777fbcbSeric char buf[40]; 313b1ed8a43Seric extern struct sccsprog *lookup(); 314108d6082Seric char *nav[1000]; 315108d6082Seric char **np; 316419708b0Seric register char **ap; 317d51cd7e5Seric register int i; 318419708b0Seric register char *q; 319d51cd7e5Seric extern bool unedit(); 32041290c52Seric int rval = 0; 321108d6082Seric extern char *index(); 322108d6082Seric extern char *makefile(); 3233642a8d2Seric char *editchs; 324287bbaa2Seric extern char *tail(); 325f0cc3246Seric 326f0cc3246Seric # ifdef DEBUG 327f0cc3246Seric if (Debug) 328f0cc3246Seric { 329108d6082Seric printf("command:\n\t\"%s\"\n", arg0); 330108d6082Seric for (np = argv; *np != NULL; np++) 331108d6082Seric printf("\t\"%s\"\n", *np); 332f0cc3246Seric } 333f0cc3246Seric # endif 334c4432be4Seric 335c4432be4Seric /* 336108d6082Seric ** Copy arguments. 337ff038098Seric ** Copy from arg0 & if necessary at most one arg 338ff038098Seric ** from argv[0]. 339adf8f7d4Seric */ 340adf8f7d4Seric 341419708b0Seric np = ap = &nav[1]; 3423642a8d2Seric editchs = NULL; 34359e81a43Seric for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) 344108d6082Seric { 345108d6082Seric *np++ = q; 346108d6082Seric while (*p == ' ') 347108d6082Seric p++; 3483642a8d2Seric while (*p != ' ' && *p != '\0' && *p != '/' && *p != ':') 349108d6082Seric *q++ = *p++; 350108d6082Seric *q++ = '\0'; 3513642a8d2Seric if (*p == ':') 3523642a8d2Seric { 3533642a8d2Seric editchs = q; 35459e81a43Seric while (*++p != '\0' && *p != '/' && *p != ' ') 3553642a8d2Seric *q++ = *p; 3563642a8d2Seric *q++ = '\0'; 3573642a8d2Seric } 358108d6082Seric } 359108d6082Seric *np = NULL; 360419708b0Seric if (*ap == NULL) 361108d6082Seric *np++ = *argv++; 362108d6082Seric 363108d6082Seric /* 364108d6082Seric ** Look up command. 365419708b0Seric ** At this point, *ap is the command name. 366108d6082Seric */ 367108d6082Seric 368419708b0Seric cmd = lookup(*ap); 369b1ed8a43Seric if (cmd == NULL) 370adf8f7d4Seric { 371419708b0Seric usrerr("Unknown command \"%s\"", *ap); 37241290c52Seric return (EX_USAGE); 373adf8f7d4Seric } 374adf8f7d4Seric 375adf8f7d4Seric /* 376108d6082Seric ** Copy remaining arguments doing editing as appropriate. 377108d6082Seric */ 378108d6082Seric 379108d6082Seric for (; *argv != NULL; argv++) 380108d6082Seric { 381108d6082Seric p = *argv; 382108d6082Seric if (*p == '-') 383108d6082Seric { 3843642a8d2Seric if (p[1] == '\0' || editchs == NULL || index(editchs, p[1]) != NULL) 385108d6082Seric *np++ = p; 386108d6082Seric } 387108d6082Seric else 388108d6082Seric { 389108d6082Seric if (!bitset(NO_SDOT, cmd->sccsflags)) 390108d6082Seric p = makefile(p); 391108d6082Seric if (p != NULL) 392108d6082Seric *np++ = p; 393108d6082Seric } 394108d6082Seric } 395108d6082Seric *np = NULL; 396108d6082Seric 397108d6082Seric /* 3987de81dc7Seric ** Interpret operation associated with this command. 399c4432be4Seric */ 400c4432be4Seric 4017de81dc7Seric switch (cmd->sccsoper) 4027de81dc7Seric { 403419708b0Seric case SHELL: /* call a shell file */ 404419708b0Seric *ap = cmd->sccspath; 405419708b0Seric *--ap = "sh"; 406419708b0Seric rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag); 407419708b0Seric break; 408419708b0Seric 4097de81dc7Seric case PROG: /* call an sccs prog */ 410419708b0Seric rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); 4111777fbcbSeric break; 4121777fbcbSeric 4131777fbcbSeric case CMACRO: /* command macro */ 414ff038098Seric /* step through & execute each part of the macro */ 4151777fbcbSeric for (p = cmd->sccspath; *p != '\0'; p++) 4161777fbcbSeric { 417108d6082Seric q = p; 418108d6082Seric while (*p != '\0' && *p != '/') 419108d6082Seric p++; 4203642a8d2Seric rval = command(&ap[1], *p != '\0', q); 42141290c52Seric if (rval != 0) 42241290c52Seric break; 4231777fbcbSeric } 42441290c52Seric break; 4257de81dc7Seric 426172147faSeric case FIX: /* fix a delta */ 427419708b0Seric if (strncmp(ap[1], "-r", 2) != 0) 428172147faSeric { 429095915d3Seric usrerr("-r flag needed for fix command"); 43041290c52Seric rval = EX_USAGE; 431172147faSeric break; 432172147faSeric } 433ff038098Seric 434ff038098Seric /* get the version with all changes */ 4353642a8d2Seric rval = command(&ap[1], TRUE, "get -k"); 436ff038098Seric 437ff038098Seric /* now remove that version from the s-file */ 43841290c52Seric if (rval == 0) 4393642a8d2Seric rval = command(&ap[1], TRUE, "rmdel:r"); 440ff038098Seric 441ff038098Seric /* and edit the old version (but don't clobber new vers) */ 44241290c52Seric if (rval == 0) 4433642a8d2Seric rval = command(&ap[2], FALSE, "get -e -g"); 44441290c52Seric break; 445172147faSeric 446b2538d76Seric case CLEAN: 447ec4190ddSeric rval = clean((int) cmd->sccspath, ap); 448b2538d76Seric break; 449b2538d76Seric 450e39a5722Seric case UNEDIT: 451419708b0Seric for (argv = np = &ap[1]; *argv != NULL; argv++) 452d51cd7e5Seric { 453108d6082Seric if (unedit(*argv)) 454108d6082Seric *np++ = *argv; 455d51cd7e5Seric } 456108d6082Seric *np = NULL; 457ff038098Seric 458ff038098Seric /* get all the files that we unedited successfully */ 459fe7b004aSeric if (np > &ap[1]) 4603642a8d2Seric rval = command(&ap[1], FALSE, "get"); 461e39a5722Seric break; 462e39a5722Seric 46378d41c6aSeric case DIFFS: /* diff between s-file & edit file */ 46478d41c6aSeric /* find the end of the flag arguments */ 46578d41c6aSeric for (np = &ap[1]; *np != NULL && **np == '-'; np++) 46678d41c6aSeric continue; 46778d41c6aSeric argv = np; 46878d41c6aSeric 46978d41c6aSeric /* for each file, do the diff */ 47096370756Seric p = argv[1]; 47178d41c6aSeric while (*np != NULL) 47278d41c6aSeric { 473ff038098Seric /* messy, but we need a null terminated argv */ 47478d41c6aSeric *argv = *np++; 47596370756Seric argv[1] = NULL; 476287bbaa2Seric i = dodiff(ap, tail(*argv)); 47778d41c6aSeric if (rval == 0) 47878d41c6aSeric rval = i; 47996370756Seric argv[1] = p; 48078d41c6aSeric } 48178d41c6aSeric break; 48278d41c6aSeric 4837de81dc7Seric default: 484095915d3Seric syserr("oper %d", cmd->sccsoper); 4857de81dc7Seric exit(EX_SOFTWARE); 4867de81dc7Seric } 48741290c52Seric # ifdef DEBUG 48841290c52Seric if (Debug) 48941290c52Seric printf("command: rval=%d\n", rval); 49041290c52Seric # endif 49141290c52Seric return (rval); 4927de81dc7Seric } 4930faf63f9Seric 4940faf63f9Seric /* 495b1ed8a43Seric ** LOOKUP -- look up an SCCS command name. 496b1ed8a43Seric ** 497b1ed8a43Seric ** Parameters: 498b1ed8a43Seric ** name -- the name of the command to look up. 499b1ed8a43Seric ** 500b1ed8a43Seric ** Returns: 501b1ed8a43Seric ** ptr to command descriptor for this command. 502b1ed8a43Seric ** NULL if no such entry. 503b1ed8a43Seric ** 504b1ed8a43Seric ** Side Effects: 505b1ed8a43Seric ** none. 506b1ed8a43Seric */ 507b1ed8a43Seric 508b1ed8a43Seric struct sccsprog * 509b1ed8a43Seric lookup(name) 510b1ed8a43Seric char *name; 511b1ed8a43Seric { 512b1ed8a43Seric register struct sccsprog *cmd; 513b1ed8a43Seric 514b1ed8a43Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 515b1ed8a43Seric { 516b1ed8a43Seric if (strcmp(cmd->sccsname, name) == 0) 517b1ed8a43Seric return (cmd); 518b1ed8a43Seric } 519b1ed8a43Seric return (NULL); 520b1ed8a43Seric } 5210faf63f9Seric 5220faf63f9Seric /* 52341290c52Seric ** CALLPROG -- call a program 52441290c52Seric ** 525108d6082Seric ** Used to call the SCCS programs. 52641290c52Seric ** 52741290c52Seric ** Parameters: 52841290c52Seric ** progpath -- pathname of the program to call. 52941290c52Seric ** flags -- status flags from the command descriptors. 53041290c52Seric ** argv -- an argument vector to pass to the program. 53141290c52Seric ** forkflag -- if true, fork before calling, else just 53241290c52Seric ** exec. 53341290c52Seric ** 53441290c52Seric ** Returns: 53541290c52Seric ** The exit status of the program. 53641290c52Seric ** Nothing if forkflag == FALSE. 53741290c52Seric ** 53841290c52Seric ** Side Effects: 53941290c52Seric ** Can exit if forkflag == FALSE. 54041290c52Seric */ 541172147faSeric 5427de81dc7Seric callprog(progpath, flags, argv, forkflag) 5437de81dc7Seric char *progpath; 5447de81dc7Seric short flags; 5457de81dc7Seric char **argv; 5467de81dc7Seric bool forkflag; 5477de81dc7Seric { 5487de81dc7Seric register int i; 5491777fbcbSeric auto int st; 550108d6082Seric 551108d6082Seric # ifdef DEBUG 552108d6082Seric if (Debug) 553108d6082Seric { 554108d6082Seric printf("callprog:\n"); 555108d6082Seric for (i = 0; argv[i] != NULL; i++) 556108d6082Seric printf("\t\"%s\"\n", argv[i]); 557108d6082Seric } 558108d6082Seric # endif 5597de81dc7Seric 5607de81dc7Seric if (*argv == NULL) 5617de81dc7Seric return (-1); 562c4432be4Seric 563c4432be4Seric /* 564172147faSeric ** Fork if appropriate. 565adf8f7d4Seric */ 566adf8f7d4Seric 5677de81dc7Seric if (forkflag) 5687de81dc7Seric { 569f0cc3246Seric # ifdef DEBUG 570f0cc3246Seric if (Debug) 571f0cc3246Seric printf("Forking\n"); 572f0cc3246Seric # endif 5737de81dc7Seric i = fork(); 5747de81dc7Seric if (i < 0) 5757de81dc7Seric { 576095915d3Seric syserr("cannot fork"); 5777de81dc7Seric exit(EX_OSERR); 5787de81dc7Seric } 5797de81dc7Seric else if (i > 0) 5801777fbcbSeric { 5811777fbcbSeric wait(&st); 58241290c52Seric if ((st & 0377) == 0) 58341290c52Seric st = (st >> 8) & 0377; 58478d41c6aSeric if (OutFile >= 0) 58578d41c6aSeric { 58678d41c6aSeric close(OutFile); 58778d41c6aSeric OutFile = -1; 58878d41c6aSeric } 5891777fbcbSeric return (st); 5901777fbcbSeric } 5917de81dc7Seric } 59278d41c6aSeric else if (OutFile >= 0) 59378d41c6aSeric { 59478d41c6aSeric syserr("callprog: setting stdout w/o forking"); 59578d41c6aSeric exit(EX_SOFTWARE); 59678d41c6aSeric } 5977de81dc7Seric 59878d41c6aSeric /* set protection as appropriate */ 5997de81dc7Seric if (bitset(REALUSER, flags)) 6007de81dc7Seric setuid(getuid()); 6017de81dc7Seric 60278d41c6aSeric /* change standard input & output if needed */ 60378d41c6aSeric if (OutFile >= 0) 60478d41c6aSeric { 60578d41c6aSeric close(1); 60678d41c6aSeric dup(OutFile); 60778d41c6aSeric close(OutFile); 60878d41c6aSeric } 6097de81dc7Seric 61078d41c6aSeric /* call real SCCS program */ 611172147faSeric execv(progpath, argv); 612095915d3Seric syserr("cannot execute %s", progpath); 613adf8f7d4Seric exit(EX_UNAVAILABLE); 614fe7b004aSeric /*NOTREACHED*/ 615adf8f7d4Seric } 6160faf63f9Seric 6170faf63f9Seric /* 618cdc1aa65Seric ** MAKEFILE -- make filename of SCCS file 619cdc1aa65Seric ** 620cdc1aa65Seric ** If the name passed is already the name of an SCCS file, 621cdc1aa65Seric ** just return it. Otherwise, munge the name into the name 622cdc1aa65Seric ** of the actual SCCS file. 623cdc1aa65Seric ** 624cdc1aa65Seric ** There are cases when it is not clear what you want to 625cdc1aa65Seric ** do. For example, if SccsPath is an absolute pathname 626cdc1aa65Seric ** and the name given is also an absolute pathname, we go 627cdc1aa65Seric ** for SccsPath (& only use the last component of the name 628cdc1aa65Seric ** passed) -- this is important for security reasons (if 629cdc1aa65Seric ** sccs is being used as a setuid front end), but not 630cdc1aa65Seric ** particularly intuitive. 631cdc1aa65Seric ** 632cdc1aa65Seric ** Parameters: 633cdc1aa65Seric ** name -- the file name to be munged. 634cdc1aa65Seric ** 635cdc1aa65Seric ** Returns: 636cdc1aa65Seric ** The pathname of the sccs file. 637cdc1aa65Seric ** NULL on error. 638cdc1aa65Seric ** 639cdc1aa65Seric ** Side Effects: 640cdc1aa65Seric ** none. 641cdc1aa65Seric */ 642adf8f7d4Seric 643adf8f7d4Seric char * 644adf8f7d4Seric makefile(name) 645adf8f7d4Seric char *name; 646adf8f7d4Seric { 647adf8f7d4Seric register char *p; 648adf8f7d4Seric char buf[512]; 649adf8f7d4Seric extern char *malloc(); 650cdc1aa65Seric extern char *rindex(); 6510a8b9ab0Seric extern bool safepath(); 6527d70f5d8Seric extern bool isdir(); 6537d70f5d8Seric register char *q; 654cdc1aa65Seric 655cdc1aa65Seric p = rindex(name, '/'); 656cdc1aa65Seric if (p == NULL) 657cdc1aa65Seric p = name; 658cdc1aa65Seric else 659cdc1aa65Seric p++; 660adf8f7d4Seric 661adf8f7d4Seric /* 6620a8b9ab0Seric ** Check to see that the path is "safe", i.e., that we 6630a8b9ab0Seric ** are not letting some nasty person use the setuid part 6640a8b9ab0Seric ** of this program to look at or munge some presumably 6650a8b9ab0Seric ** hidden files. 666adf8f7d4Seric */ 667adf8f7d4Seric 6680a8b9ab0Seric if (SccsDir[0] == '/' && !safepath(name)) 6690a8b9ab0Seric return (NULL); 670cdc1aa65Seric 671cdc1aa65Seric /* 6720a8b9ab0Seric ** Create the base pathname. 673cdc1aa65Seric */ 674cdc1aa65Seric 675ff038098Seric /* first the directory part */ 6760a8b9ab0Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) 677adf8f7d4Seric { 6780a8b9ab0Seric strcpy(buf, SccsDir); 679cdc1aa65Seric strcat(buf, "/"); 680cdc1aa65Seric } 681cdc1aa65Seric else 682cdc1aa65Seric strcpy(buf, ""); 683ff038098Seric 684ff038098Seric /* then the head of the pathname */ 685cdc1aa65Seric strncat(buf, name, p - name); 6867d70f5d8Seric q = &buf[strlen(buf)]; 687ff038098Seric 688ff038098Seric /* now copy the final part of the name, in case useful */ 6897d70f5d8Seric strcpy(q, p); 690ff038098Seric 691ff038098Seric /* so is it useful? */ 6927d70f5d8Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 6937d70f5d8Seric { 694ff038098Seric /* sorry, no; copy the SCCS pathname & the "s." */ 6950a8b9ab0Seric strcpy(q, SccsPath); 6960a8b9ab0Seric strcat(buf, "/s."); 697ff038098Seric 698ff038098Seric /* and now the end of the name */ 699cdc1aa65Seric strcat(buf, p); 700cdc1aa65Seric } 701adf8f7d4Seric 702ff038098Seric /* if i haven't changed it, why did I do all this? */ 7030a8b9ab0Seric if (strcmp(buf, name) == 0) 7040a8b9ab0Seric p = name; 7050a8b9ab0Seric else 7060a8b9ab0Seric { 707ff038098Seric /* but if I have, squirrel it away */ 708adf8f7d4Seric p = malloc(strlen(buf) + 1); 709adf8f7d4Seric if (p == NULL) 710adf8f7d4Seric { 711adf8f7d4Seric perror("Sccs: no mem"); 712adf8f7d4Seric exit(EX_OSERR); 713adf8f7d4Seric } 714adf8f7d4Seric strcpy(p, buf); 7150a8b9ab0Seric } 716ff038098Seric 717adf8f7d4Seric return (p); 718adf8f7d4Seric } 7190faf63f9Seric 7200faf63f9Seric /* 7217d70f5d8Seric ** ISDIR -- return true if the argument is a directory. 7227d70f5d8Seric ** 7237d70f5d8Seric ** Parameters: 7247d70f5d8Seric ** name -- the pathname of the file to check. 7257d70f5d8Seric ** 7267d70f5d8Seric ** Returns: 7277d70f5d8Seric ** TRUE if 'name' is a directory, FALSE otherwise. 7287d70f5d8Seric ** 7297d70f5d8Seric ** Side Effects: 7307d70f5d8Seric ** none. 7317d70f5d8Seric */ 7327d70f5d8Seric 7337d70f5d8Seric bool 7347d70f5d8Seric isdir(name) 7357d70f5d8Seric char *name; 7367d70f5d8Seric { 7377d70f5d8Seric struct stat stbuf; 7387d70f5d8Seric 7397d70f5d8Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 7407d70f5d8Seric } 7410faf63f9Seric 7420faf63f9Seric /* 743cdc1aa65Seric ** SAFEPATH -- determine whether a pathname is "safe" 744cdc1aa65Seric ** 745cdc1aa65Seric ** "Safe" pathnames only allow you to get deeper into the 746cdc1aa65Seric ** directory structure, i.e., full pathnames and ".." are 747cdc1aa65Seric ** not allowed. 748cdc1aa65Seric ** 749cdc1aa65Seric ** Parameters: 750cdc1aa65Seric ** p -- the name to check. 751cdc1aa65Seric ** 752cdc1aa65Seric ** Returns: 753cdc1aa65Seric ** TRUE -- if the path is safe. 754cdc1aa65Seric ** FALSE -- if the path is not safe. 755cdc1aa65Seric ** 756cdc1aa65Seric ** Side Effects: 757cdc1aa65Seric ** Prints a message if the path is not safe. 758cdc1aa65Seric */ 759cdc1aa65Seric 760cdc1aa65Seric bool 761cdc1aa65Seric safepath(p) 762cdc1aa65Seric register char *p; 763cdc1aa65Seric { 764cdc1aa65Seric extern char *index(); 765cdc1aa65Seric 766cdc1aa65Seric if (*p != '/') 767cdc1aa65Seric { 768cdc1aa65Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 769cdc1aa65Seric { 770cdc1aa65Seric p = index(p, '/'); 771cdc1aa65Seric if (p == NULL) 772cdc1aa65Seric return (TRUE); 773cdc1aa65Seric p++; 774cdc1aa65Seric } 775cdc1aa65Seric } 776cdc1aa65Seric 777cdc1aa65Seric printf("You may not use full pathnames or \"..\"\n"); 778cdc1aa65Seric return (FALSE); 779cdc1aa65Seric } 7800faf63f9Seric 7810faf63f9Seric /* 782b2538d76Seric ** CLEAN -- clean out recreatable files 783b2538d76Seric ** 784b2538d76Seric ** Any file for which an "s." file exists but no "p." file 785b2538d76Seric ** exists in the current directory is purged. 786b2538d76Seric ** 787b2538d76Seric ** Parameters: 788ec4190ddSeric ** mode -- tells whether this came from a "clean", "info", or 78948ff0e7eSeric ** "check" command. 790ec4190ddSeric ** argv -- the rest of the argument vector. 791b2538d76Seric ** 792b2538d76Seric ** Returns: 793b2538d76Seric ** none. 794b2538d76Seric ** 795b2538d76Seric ** Side Effects: 79648ff0e7eSeric ** Removes files in the current directory. 79748ff0e7eSeric ** Prints information regarding files being edited. 79848ff0e7eSeric ** Exits if a "check" command. 799b2538d76Seric */ 800b2538d76Seric 801ec4190ddSeric clean(mode, argv) 80248ff0e7eSeric int mode; 803ec4190ddSeric char **argv; 804b2538d76Seric { 805b2538d76Seric struct direct dir; 806b2538d76Seric char buf[100]; 8073a208c77Seric register FILE *dirfd; 8083a208c77Seric register char *basefile; 8092caac8fdSeric bool gotedit; 810ec4190ddSeric bool gotpfent; 811bf9c1e66Seric FILE *pfp; 812ec4190ddSeric bool nobranch = FALSE; 813ec4190ddSeric extern struct pfile *getpfent(); 814ec4190ddSeric register struct pfile *pf; 815ec4190ddSeric register char **ap; 816*1e0924d0Seric extern char *username(); 817*1e0924d0Seric char *usernm = NULL; 818ec4190ddSeric 819ec4190ddSeric /* 820ec4190ddSeric ** Process the argv 821ec4190ddSeric */ 822ec4190ddSeric 823ec4190ddSeric for (ap = argv; *ap != NULL; ap++) 824ec4190ddSeric { 825*1e0924d0Seric if (**ap == '-') 826*1e0924d0Seric { 827*1e0924d0Seric /* we have a flag */ 828*1e0924d0Seric switch ((*ap)[1]) 829*1e0924d0Seric { 830*1e0924d0Seric case 'b': 831ec4190ddSeric nobranch = TRUE; 832*1e0924d0Seric break; 833*1e0924d0Seric 834*1e0924d0Seric case 'u': 835*1e0924d0Seric if ((*ap)[2] != '\0') 836*1e0924d0Seric usernm = &(*ap)[2]; 837*1e0924d0Seric else if (ap[1] != NULL && ap[1][0] != '-') 838*1e0924d0Seric usernm = *++ap; 839*1e0924d0Seric else 840*1e0924d0Seric usernm = username(); 841*1e0924d0Seric break; 842*1e0924d0Seric } 843*1e0924d0Seric } 844ec4190ddSeric } 845b2538d76Seric 846ff038098Seric /* 847ff038098Seric ** Find and open the SCCS directory. 848ff038098Seric */ 849ff038098Seric 850f77a08fbSeric strcpy(buf, SccsDir); 851f77a08fbSeric if (buf[0] != '\0') 852f77a08fbSeric strcat(buf, "/"); 853f77a08fbSeric strcat(buf, SccsPath); 854ff038098Seric 855f77a08fbSeric dirfd = fopen(buf, "r"); 856b2538d76Seric if (dirfd == NULL) 857b2538d76Seric { 858f77a08fbSeric usrerr("cannot open %s", buf); 85941290c52Seric return (EX_NOINPUT); 860b2538d76Seric } 861b2538d76Seric 862b2538d76Seric /* 863b2538d76Seric ** Scan the SCCS directory looking for s. files. 864ff038098Seric ** gotedit tells whether we have tried to clean any 865ff038098Seric ** files that are being edited. 866b2538d76Seric */ 867b2538d76Seric 8682caac8fdSeric gotedit = FALSE; 869fe7b004aSeric while (fread((char *)&dir, sizeof dir, 1, dirfd) != NULL) 870b2538d76Seric { 871c9c62fd0Seric if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) 872b2538d76Seric continue; 873b2538d76Seric 874b2538d76Seric /* got an s. file -- see if the p. file exists */ 875f77a08fbSeric strcpy(buf, SccsDir); 876f77a08fbSeric if (buf[0] != '\0') 877f77a08fbSeric strcat(buf, "/"); 878f77a08fbSeric strcat(buf, SccsPath); 879b2538d76Seric strcat(buf, "/p."); 8803a208c77Seric basefile = &buf[strlen(buf)]; 881c9c62fd0Seric strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 882bf9c1e66Seric basefile[sizeof dir.d_name - 2] = '\0'; 883ec4190ddSeric 884ec4190ddSeric /* 885ec4190ddSeric ** open and scan the p-file. 886ec4190ddSeric ** 'gotpfent' tells if we have found a valid p-file 887ec4190ddSeric ** entry. 888ec4190ddSeric */ 889ec4190ddSeric 890bf9c1e66Seric pfp = fopen(buf, "r"); 891ec4190ddSeric gotpfent = FALSE; 892bf9c1e66Seric if (pfp != NULL) 8933a208c77Seric { 894ff038098Seric /* the file exists -- report it's contents */ 895ec4190ddSeric while ((pf = getpfent(pfp)) != NULL) 8962734e237Seric { 897ec4190ddSeric if (nobranch && isbranch(pf->p_nsid)) 898ec4190ddSeric continue; 899*1e0924d0Seric if (usernm != NULL && strcmp(usernm, pf->p_user) != 0 && mode != CLEANC) 900*1e0924d0Seric continue; 901ec4190ddSeric gotedit = TRUE; 902ec4190ddSeric gotpfent = TRUE; 903ec4190ddSeric if (mode == TELLC) 904ec4190ddSeric { 905ec4190ddSeric printf("%s\n", basefile); 906ec4190ddSeric break; 907ec4190ddSeric } 908ec4190ddSeric printf("%12s: being edited: %s %s %s %s %s\n", 909ec4190ddSeric basefile, pf->p_osid, pf->p_nsid, 910ec4190ddSeric pf->p_user, pf->p_date, pf->p_time); 9112734e237Seric } 912bf9c1e66Seric fclose(pfp); 9133a208c77Seric } 914ec4190ddSeric if (!gotpfent) 915ec4190ddSeric continue; 916b2538d76Seric 917b2538d76Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 91848ff0e7eSeric if (mode == CLEANC) 9193a208c77Seric { 920c9c62fd0Seric strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); 9213a208c77Seric buf[sizeof dir.d_name - 2] = '\0'; 922b2538d76Seric unlink(buf); 923b2538d76Seric } 9243a208c77Seric } 925b2538d76Seric 926ff038098Seric /* cleanup & report results */ 927b2538d76Seric fclose(dirfd); 92848ff0e7eSeric if (!gotedit && mode == INFOC) 929*1e0924d0Seric { 930*1e0924d0Seric printf("Nothing being edited"); 931*1e0924d0Seric if (nobranch) 932*1e0924d0Seric printf(" (on trunk)"); 933*1e0924d0Seric if (usernm == NULL) 934*1e0924d0Seric printf("\n"); 935*1e0924d0Seric else 936*1e0924d0Seric printf(" by %s\n", usernm); 937*1e0924d0Seric } 93848ff0e7eSeric if (mode == CHECKC) 93948ff0e7eSeric exit(gotedit); 94041290c52Seric return (EX_OK); 941b2538d76Seric } 9420faf63f9Seric 9430faf63f9Seric /* 944ec4190ddSeric ** ISBRANCH -- is the SID a branch? 945ec4190ddSeric ** 946ec4190ddSeric ** Parameters: 947ec4190ddSeric ** sid -- the sid to check. 948ec4190ddSeric ** 949ec4190ddSeric ** Returns: 950ec4190ddSeric ** TRUE if the sid represents a branch. 951ec4190ddSeric ** FALSE otherwise. 952ec4190ddSeric ** 953ec4190ddSeric ** Side Effects: 954ec4190ddSeric ** none. 955ec4190ddSeric */ 956ec4190ddSeric 957ec4190ddSeric isbranch(sid) 958ec4190ddSeric char *sid; 959ec4190ddSeric { 960ec4190ddSeric register char *p; 961ec4190ddSeric int dots; 962ec4190ddSeric 963ec4190ddSeric dots = 0; 964ec4190ddSeric for (p = sid; *p != '\0'; p++) 965ec4190ddSeric { 966ec4190ddSeric if (*p == '.') 967ec4190ddSeric dots++; 968ec4190ddSeric if (dots > 1) 969ec4190ddSeric return (TRUE); 970ec4190ddSeric } 971ec4190ddSeric return (FALSE); 972ec4190ddSeric } 973ec4190ddSeric 974ec4190ddSeric /* 975e39a5722Seric ** UNEDIT -- unedit a file 976e39a5722Seric ** 977e39a5722Seric ** Checks to see that the current user is actually editting 978e39a5722Seric ** the file and arranges that s/he is not editting it. 979e39a5722Seric ** 980e39a5722Seric ** Parameters: 9812444cd3eSeric ** fn -- the name of the file to be unedited. 982e39a5722Seric ** 983e39a5722Seric ** Returns: 984d51cd7e5Seric ** TRUE -- if the file was successfully unedited. 985d51cd7e5Seric ** FALSE -- if the file was not unedited for some 986d51cd7e5Seric ** reason. 987e39a5722Seric ** 988e39a5722Seric ** Side Effects: 989e39a5722Seric ** fn is removed 990e39a5722Seric ** entries are removed from pfile. 991e39a5722Seric */ 992e39a5722Seric 993d51cd7e5Seric bool 994e39a5722Seric unedit(fn) 995e39a5722Seric char *fn; 996e39a5722Seric { 997e39a5722Seric register FILE *pfp; 998e39a5722Seric char *pfn; 999e39a5722Seric static char tfn[] = "/tmp/sccsXXXXX"; 1000e39a5722Seric FILE *tfp; 1001e39a5722Seric register char *q; 1002e39a5722Seric bool delete = FALSE; 1003e39a5722Seric bool others = FALSE; 1004e39a5722Seric char *myname; 1005*1e0924d0Seric extern char *username(); 1006e39a5722Seric struct pfile *pent; 1007ec4190ddSeric extern struct pfile *getpfent(); 1008e39a5722Seric char buf[120]; 1009108d6082Seric extern char *makefile(); 1010e39a5722Seric 1011e39a5722Seric /* make "s." filename & find the trailing component */ 1012e39a5722Seric pfn = makefile(fn); 1013cdc1aa65Seric if (pfn == NULL) 1014cdc1aa65Seric return (FALSE); 1015cdc1aa65Seric q = rindex(pfn, '/'); 1016cdc1aa65Seric if (q == NULL) 1017cdc1aa65Seric q = &pfn[-1]; 1018cdc1aa65Seric if (q[1] != 's' || q[2] != '.') 1019e39a5722Seric { 1020095915d3Seric usrerr("bad file name \"%s\"", fn); 1021d51cd7e5Seric return (FALSE); 1022e39a5722Seric } 1023e39a5722Seric 1024ff038098Seric /* turn "s." into "p." & try to open it */ 1025e39a5722Seric *++q = 'p'; 1026e39a5722Seric 1027e39a5722Seric pfp = fopen(pfn, "r"); 1028e39a5722Seric if (pfp == NULL) 1029e39a5722Seric { 10302444cd3eSeric printf("%12s: not being edited\n", fn); 1031d51cd7e5Seric return (FALSE); 1032e39a5722Seric } 1033e39a5722Seric 1034ff038098Seric /* create temp file for editing p-file */ 1035e39a5722Seric mktemp(tfn); 1036e39a5722Seric tfp = fopen(tfn, "w"); 1037e39a5722Seric if (tfp == NULL) 1038e39a5722Seric { 1039095915d3Seric usrerr("cannot create \"%s\"", tfn); 1040e39a5722Seric exit(EX_OSERR); 1041e39a5722Seric } 1042e39a5722Seric 1043ff038098Seric /* figure out who I am */ 1044*1e0924d0Seric myname = username(); 1045ff038098Seric 1046ff038098Seric /* 1047ff038098Seric ** Copy p-file to temp file, doing deletions as needed. 1048ff038098Seric */ 1049ff038098Seric 1050ec4190ddSeric while ((pent = getpfent(pfp)) != NULL) 1051e39a5722Seric { 1052e39a5722Seric if (strcmp(pent->p_user, myname) == 0) 1053e39a5722Seric { 1054e39a5722Seric /* a match */ 1055e39a5722Seric delete++; 1056e39a5722Seric } 1057e39a5722Seric else 1058e39a5722Seric { 1059ff038098Seric /* output it again */ 1060e39a5722Seric fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, 1061e39a5722Seric pent->p_nsid, pent->p_user, pent->p_date, 1062e39a5722Seric pent->p_time); 1063e39a5722Seric others++; 1064e39a5722Seric } 1065e39a5722Seric } 1066e39a5722Seric 1067e39a5722Seric /* do final cleanup */ 1068e39a5722Seric if (others) 1069e39a5722Seric { 1070ff038098Seric /* copy it back (perhaps it should be linked?) */ 1071e39a5722Seric if (freopen(tfn, "r", tfp) == NULL) 1072e39a5722Seric { 1073095915d3Seric syserr("cannot reopen \"%s\"", tfn); 1074e39a5722Seric exit(EX_OSERR); 1075e39a5722Seric } 1076e39a5722Seric if (freopen(pfn, "w", pfp) == NULL) 1077e39a5722Seric { 1078095915d3Seric usrerr("cannot create \"%s\"", pfn); 1079d51cd7e5Seric return (FALSE); 1080e39a5722Seric } 1081e39a5722Seric while (fgets(buf, sizeof buf, tfp) != NULL) 1082e39a5722Seric fputs(buf, pfp); 1083e39a5722Seric } 1084e39a5722Seric else 1085e39a5722Seric { 1086ff038098Seric /* it's empty -- remove it */ 1087e39a5722Seric unlink(pfn); 1088e39a5722Seric } 1089e39a5722Seric fclose(tfp); 1090e39a5722Seric fclose(pfp); 1091e39a5722Seric unlink(tfn); 1092e39a5722Seric 1093ff038098Seric /* actually remove the g-file */ 1094e39a5722Seric if (delete) 1095e39a5722Seric { 1096287bbaa2Seric unlink(tail(fn)); 1097287bbaa2Seric printf("%12s: removed\n", tail(fn)); 1098d51cd7e5Seric return (TRUE); 1099e39a5722Seric } 1100e39a5722Seric else 1101e39a5722Seric { 11022444cd3eSeric printf("%12s: not being edited by you\n", fn); 1103d51cd7e5Seric return (FALSE); 1104e39a5722Seric } 1105e39a5722Seric } 11060faf63f9Seric 11070faf63f9Seric /* 110878d41c6aSeric ** DODIFF -- diff an s-file against a g-file 110978d41c6aSeric ** 111078d41c6aSeric ** Parameters: 111178d41c6aSeric ** getv -- argv for the 'get' command. 111278d41c6aSeric ** gfile -- name of the g-file to diff against. 111378d41c6aSeric ** 111478d41c6aSeric ** Returns: 111578d41c6aSeric ** Result of get. 111678d41c6aSeric ** 111778d41c6aSeric ** Side Effects: 111878d41c6aSeric ** none. 111978d41c6aSeric */ 112078d41c6aSeric 112178d41c6aSeric dodiff(getv, gfile) 112278d41c6aSeric char **getv; 112378d41c6aSeric char *gfile; 112478d41c6aSeric { 112578d41c6aSeric int pipev[2]; 112678d41c6aSeric int rval; 112778d41c6aSeric register int i; 112878d41c6aSeric register int pid; 112978d41c6aSeric auto int st; 113078d41c6aSeric extern int errno; 113178d41c6aSeric int (*osig)(); 113278d41c6aSeric 1133cc4e4f0dSeric printf("\n------- %s -------\n", gfile); 1134cc4e4f0dSeric 1135ff038098Seric /* create context for diff to run in */ 113678d41c6aSeric if (pipe(pipev) < 0) 113778d41c6aSeric { 113878d41c6aSeric syserr("dodiff: pipe failed"); 113978d41c6aSeric exit(EX_OSERR); 114078d41c6aSeric } 114178d41c6aSeric if ((pid = fork()) < 0) 114278d41c6aSeric { 114378d41c6aSeric syserr("dodiff: fork failed"); 114478d41c6aSeric exit(EX_OSERR); 114578d41c6aSeric } 114678d41c6aSeric else if (pid > 0) 114778d41c6aSeric { 114878d41c6aSeric /* in parent; run get */ 114978d41c6aSeric OutFile = pipev[1]; 115078d41c6aSeric close(pipev[0]); 11513642a8d2Seric rval = command(&getv[1], TRUE, "get -s -k -p"); 115278d41c6aSeric osig = signal(SIGINT, SIG_IGN); 115378d41c6aSeric while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR) 115478d41c6aSeric errno = 0; 115578d41c6aSeric signal(SIGINT, osig); 115678d41c6aSeric /* ignore result of diff */ 115778d41c6aSeric } 115878d41c6aSeric else 115978d41c6aSeric { 116078d41c6aSeric /* in child, run diff */ 116178d41c6aSeric if (close(pipev[1]) < 0 || close(0) < 0 || 116278d41c6aSeric dup(pipev[0]) != 0 || close(pipev[0]) < 0) 116378d41c6aSeric { 116478d41c6aSeric syserr("dodiff: magic failed"); 116578d41c6aSeric exit(EX_OSERR); 116678d41c6aSeric } 116778d41c6aSeric execl(PROGPATH(bdiff), "bdiff", "-", gfile, NULL); 116878d41c6aSeric # ifndef V6 116978d41c6aSeric execlp("bdiff", "bdiff", "-", gfile, NULL); 117078d41c6aSeric execlp("diff", "diff", "-", gfile, NULL); 117178d41c6aSeric # endif NOT V6 117278d41c6aSeric syserr("bdiff: cannot execute"); 117378d41c6aSeric exit(EX_OSERR); 117478d41c6aSeric } 117578d41c6aSeric return (rval); 117678d41c6aSeric } 117778d41c6aSeric 117878d41c6aSeric /* 1179287bbaa2Seric ** TAIL -- return tail of filename. 1180287bbaa2Seric ** 1181287bbaa2Seric ** Parameters: 1182287bbaa2Seric ** fn -- the filename. 1183287bbaa2Seric ** 1184287bbaa2Seric ** Returns: 1185287bbaa2Seric ** a pointer to the tail of the filename; e.g., given 1186287bbaa2Seric ** "cmd/ls.c", "ls.c" is returned. 1187287bbaa2Seric ** 1188287bbaa2Seric ** Side Effects: 1189287bbaa2Seric ** none. 1190287bbaa2Seric */ 1191287bbaa2Seric 1192287bbaa2Seric char * 1193287bbaa2Seric tail(fn) 1194287bbaa2Seric register char *fn; 1195287bbaa2Seric { 1196287bbaa2Seric register char *p; 1197287bbaa2Seric 1198287bbaa2Seric for (p = fn; *p != 0; p++) 1199287bbaa2Seric if (*p == '/' && p[1] != '\0' && p[1] != '/') 1200287bbaa2Seric fn = &p[1]; 1201287bbaa2Seric return (fn); 1202287bbaa2Seric } 1203287bbaa2Seric 1204287bbaa2Seric /* 1205ec4190ddSeric ** GETPFENT -- get an entry from the p-file 1206e39a5722Seric ** 1207e39a5722Seric ** Parameters: 1208e39a5722Seric ** pfp -- p-file file pointer 1209e39a5722Seric ** 1210e39a5722Seric ** Returns: 1211e39a5722Seric ** pointer to p-file struct for next entry 1212e39a5722Seric ** NULL on EOF or error 1213e39a5722Seric ** 1214e39a5722Seric ** Side Effects: 1215e39a5722Seric ** Each call wipes out results of previous call. 1216e39a5722Seric */ 1217e39a5722Seric 1218e39a5722Seric struct pfile * 1219ec4190ddSeric getpfent(pfp) 1220e39a5722Seric FILE *pfp; 1221e39a5722Seric { 1222e39a5722Seric static struct pfile ent; 1223e39a5722Seric static char buf[120]; 1224e39a5722Seric register char *p; 1225e39a5722Seric extern char *nextfield(); 1226e39a5722Seric 1227e39a5722Seric if (fgets(buf, sizeof buf, pfp) == NULL) 1228e39a5722Seric return (NULL); 1229e39a5722Seric 1230e39a5722Seric ent.p_osid = p = buf; 1231e39a5722Seric ent.p_nsid = p = nextfield(p); 1232e39a5722Seric ent.p_user = p = nextfield(p); 1233e39a5722Seric ent.p_date = p = nextfield(p); 1234e39a5722Seric ent.p_time = p = nextfield(p); 1235e39a5722Seric if (p == NULL || nextfield(p) != NULL) 1236e39a5722Seric return (NULL); 1237e39a5722Seric 1238e39a5722Seric return (&ent); 1239e39a5722Seric } 1240e39a5722Seric 1241e39a5722Seric 1242e39a5722Seric char * 1243e39a5722Seric nextfield(p) 1244e39a5722Seric register char *p; 1245e39a5722Seric { 1246e39a5722Seric if (p == NULL || *p == '\0') 1247e39a5722Seric return (NULL); 1248e39a5722Seric while (*p != ' ' && *p != '\n' && *p != '\0') 1249e39a5722Seric p++; 1250e39a5722Seric if (*p == '\n' || *p == '\0') 1251e39a5722Seric { 1252e39a5722Seric *p = '\0'; 1253e39a5722Seric return (NULL); 1254e39a5722Seric } 1255e39a5722Seric *p++ = '\0'; 1256e39a5722Seric return (p); 1257e39a5722Seric } 12580faf63f9Seric 12590faf63f9Seric /* 1260095915d3Seric ** USRERR -- issue user-level error 1261095915d3Seric ** 1262095915d3Seric ** Parameters: 1263095915d3Seric ** f -- format string. 1264095915d3Seric ** p1-p3 -- parameters to a printf. 1265095915d3Seric ** 1266095915d3Seric ** Returns: 1267095915d3Seric ** -1 1268095915d3Seric ** 1269095915d3Seric ** Side Effects: 1270095915d3Seric ** none. 1271095915d3Seric */ 1272095915d3Seric 1273fe7b004aSeric /*VARARGS1*/ 1274095915d3Seric usrerr(f, p1, p2, p3) 1275095915d3Seric char *f; 1276095915d3Seric { 1277095915d3Seric fprintf(stderr, "\n%s: ", MyName); 1278095915d3Seric fprintf(stderr, f, p1, p2, p3); 1279095915d3Seric fprintf(stderr, "\n"); 1280095915d3Seric 1281095915d3Seric return (-1); 1282095915d3Seric } 12830faf63f9Seric 12840faf63f9Seric /* 1285095915d3Seric ** SYSERR -- print system-generated error. 1286095915d3Seric ** 1287095915d3Seric ** Parameters: 1288095915d3Seric ** f -- format string to a printf. 1289095915d3Seric ** p1, p2, p3 -- parameters to f. 1290095915d3Seric ** 1291095915d3Seric ** Returns: 1292095915d3Seric ** never. 1293095915d3Seric ** 1294095915d3Seric ** Side Effects: 1295095915d3Seric ** none. 1296095915d3Seric */ 1297095915d3Seric 1298fe7b004aSeric /*VARARGS1*/ 1299095915d3Seric syserr(f, p1, p2, p3) 1300095915d3Seric char *f; 1301095915d3Seric { 1302095915d3Seric extern int errno; 1303095915d3Seric 1304095915d3Seric fprintf(stderr, "\n%s SYSERR: ", MyName); 1305095915d3Seric fprintf(stderr, f, p1, p2, p3); 1306095915d3Seric fprintf(stderr, "\n"); 1307095915d3Seric if (errno == 0) 1308095915d3Seric exit(EX_SOFTWARE); 1309095915d3Seric else 1310095915d3Seric { 1311fe7b004aSeric perror(NULL); 1312095915d3Seric exit(EX_OSERR); 1313095915d3Seric } 1314095915d3Seric } 1315*1e0924d0Seric /* 1316*1e0924d0Seric ** USERNAME -- return name of the current user 1317*1e0924d0Seric ** 1318*1e0924d0Seric ** Parameters: 1319*1e0924d0Seric ** none 1320*1e0924d0Seric ** 1321*1e0924d0Seric ** Returns: 1322*1e0924d0Seric ** name of current user 1323*1e0924d0Seric ** 1324*1e0924d0Seric ** Side Effects: 1325*1e0924d0Seric ** none 1326*1e0924d0Seric */ 1327*1e0924d0Seric 1328*1e0924d0Seric char * 1329*1e0924d0Seric username() 1330*1e0924d0Seric { 1331*1e0924d0Seric # ifdef UIDUSER 1332*1e0924d0Seric extern struct passwd *getpwuid(); 1333*1e0924d0Seric register struct passwd *pw; 1334*1e0924d0Seric 1335*1e0924d0Seric pw = getpwuid(getuid()); 1336*1e0924d0Seric if (pw == NULL) 1337*1e0924d0Seric { 1338*1e0924d0Seric syserr("who are you? (uid=%d)", getuid()); 1339*1e0924d0Seric exit(EX_OSERR); 1340*1e0924d0Seric } 1341*1e0924d0Seric return (pw->pw_name); 1342*1e0924d0Seric # else 1343*1e0924d0Seric return (getlogin()); 1344*1e0924d0Seric # endif UIDUSER 1345*1e0924d0Seric } 1346