1adf8f7d4Seric # include <stdio.h> 2adf8f7d4Seric # include <sys/types.h> 3adf8f7d4Seric # include <sys/stat.h> 4b2538d76Seric # include <sys/dir.h> 5*78d41c6aSeric # include <errno.h> 6*78d41c6aSeric # 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. 8210cd16efSeric ** 8310cd16efSeric ** Compilation Instructions: 8410cd16efSeric ** cc -O -n -s sccs.c 8510cd16efSeric ** 8610cd16efSeric ** Author: 8710cd16efSeric ** Eric Allman, UCB/INGRES 887abec564Seric ** Copyright 1980 Regents of the University of California 8910cd16efSeric */ 9010cd16efSeric 91*78d41c6aSeric static char SccsId[] = "@(#)sccs.c 1.36 10/15/80"; 920faf63f9Seric 937abec564Seric /******************* Configuration Information ********************/ 947abec564Seric 9510cd16efSeric # ifdef CSVAX 9610cd16efSeric # define UIDUSER 977abec564Seric # define PROGPATH(name) "/usr/local/name" 987abec564Seric # endif CSVAX 9910cd16efSeric 1000faf63f9Seric # define SCCSPATH "SCCS" /* pathname in which to find s-files */ 1017abec564Seric /* put #define SCCSDIR here */ 1027abec564Seric 1037abec564Seric char MyName[] = "sccs"; /* name used in messages */ 1047abec564Seric 1050faf63f9Seric # ifndef PROGPATH 1060faf63f9Seric # define PROGPATH(name) "/usr/sccs/name" /* place to find binaries */ 1070faf63f9Seric # endif PROGPATH 1080faf63f9Seric 1097abec564Seric /**************** End of Configuration Information ****************/ 1100faf63f9Seric 111c4432be4Seric # define bitset(bit, word) ((bit) & (word)) 112c4432be4Seric 113c4432be4Seric typedef char bool; 1147de81dc7Seric # define TRUE 1 1157de81dc7Seric # define FALSE 0 116d02a4f42Seric 11710cd16efSeric # ifdef UIDUSER 11810cd16efSeric # include <pwd.h> 11910cd16efSeric # endif UIDUSER 12010cd16efSeric 121adf8f7d4Seric struct sccsprog 122adf8f7d4Seric { 123adf8f7d4Seric char *sccsname; /* name of SCCS routine */ 1247de81dc7Seric short sccsoper; /* opcode, see below */ 1257de81dc7Seric short sccsflags; /* flags, see below */ 126108d6082Seric char *sccsklets; /* valid key-letters on macros */ 127adf8f7d4Seric char *sccspath; /* pathname of binary implementing */ 128adf8f7d4Seric }; 129adf8f7d4Seric 1307de81dc7Seric /* values for sccsoper */ 1317de81dc7Seric # define PROG 0 /* call a program */ 1321777fbcbSeric # define CMACRO 1 /* command substitution macro */ 133172147faSeric # define FIX 2 /* fix a delta */ 134b2538d76Seric # define CLEAN 3 /* clean out recreatable files */ 135e39a5722Seric # define UNEDIT 4 /* unedit a file */ 136419708b0Seric # define SHELL 5 /* call a shell file (like PROG) */ 137*78d41c6aSeric # define DIFFS 6 /* diff between sccs & file out */ 1387de81dc7Seric 139c4432be4Seric /* bits for sccsflags */ 1407de81dc7Seric # define NO_SDOT 0001 /* no s. on front of args */ 1417de81dc7Seric # define REALUSER 0002 /* protected (e.g., admin) */ 142adf8f7d4Seric 14348ff0e7eSeric /* modes for the "clean", "info", "check" ops */ 14448ff0e7eSeric # define CLEANC 0 /* clean command */ 14548ff0e7eSeric # define INFOC 1 /* info command */ 14648ff0e7eSeric # define CHECKC 2 /* check command */ 14748ff0e7eSeric 1480faf63f9Seric /* 1490faf63f9Seric ** Description of commands known to this program. 1500faf63f9Seric ** First argument puts the command into a class. Second arg is 1510faf63f9Seric ** info regarding treatment of this command. Third arg is a 1520faf63f9Seric ** list of flags this command accepts from macros, etc. Fourth 1530faf63f9Seric ** arg is the pathname of the implementing program, or the 1540faf63f9Seric ** macro definition, or the arg to a sub-algorithm. 1550faf63f9Seric */ 156b5d4f080Seric 157adf8f7d4Seric struct sccsprog SccsProg[] = 158adf8f7d4Seric { 159108d6082Seric "admin", PROG, REALUSER, "", PROGPATH(admin), 160108d6082Seric "chghist", PROG, 0, "", PROGPATH(rmdel), 161108d6082Seric "comb", PROG, 0, "", PROGPATH(comb), 162108d6082Seric "delta", PROG, 0, "mysrp", PROGPATH(delta), 163c952b8bfSeric "get", PROG, 0, "ixbeskcl", PROGPATH(get), 164108d6082Seric "help", PROG, NO_SDOT, "", PROGPATH(help), 165108d6082Seric "prt", PROG, 0, "", PROGPATH(prt), 166108d6082Seric "rmdel", PROG, REALUSER, "", PROGPATH(rmdel), 167108d6082Seric "what", PROG, NO_SDOT, "", PROGPATH(what), 168419708b0Seric "sccsdiff", SHELL, REALUSER, "", PROGPATH(sccsdiff), 169c952b8bfSeric "edit", CMACRO, NO_SDOT, "ixbscl", "get -e", 170108d6082Seric "delget", CMACRO, NO_SDOT, "", "delta/get -t", 171108d6082Seric "deledit", CMACRO, NO_SDOT, "", "delta/get -e -t", 172108d6082Seric "fix", FIX, NO_SDOT, "", NULL, 173108d6082Seric "clean", CLEAN, REALUSER, "", (char *) CLEANC, 174108d6082Seric "info", CLEAN, REALUSER, "", (char *) INFOC, 175108d6082Seric "check", CLEAN, REALUSER, "", (char *) CHECKC, 176108d6082Seric "unedit", UNEDIT, NO_SDOT, "", NULL, 177*78d41c6aSeric "diffs", DIFFS, NO_SDOT|REALUSER, "", NULL, 178108d6082Seric NULL, -1, 0, "", NULL 179adf8f7d4Seric }; 180adf8f7d4Seric 1810faf63f9Seric /* one line from a p-file */ 182e39a5722Seric struct pfile 183e39a5722Seric { 184e39a5722Seric char *p_osid; /* old SID */ 185e39a5722Seric char *p_nsid; /* new SID */ 186e39a5722Seric char *p_user; /* user who did edit */ 187e39a5722Seric char *p_date; /* date of get */ 188e39a5722Seric char *p_time; /* time of get */ 189e39a5722Seric }; 190e39a5722Seric 1917abec564Seric char *SccsPath = SCCSPATH; /* pathname of SCCS files */ 1927abec564Seric # ifdef SCCSDIR 1937abec564Seric char *SccsDir = SCCSDIR; /* directory to begin search from */ 194095915d3Seric # else 1957abec564Seric char *SccsDir = ""; 196095915d3Seric # endif 197*78d41c6aSeric int OutFile = -1; /* override output file for commands */ 198c4432be4Seric bool RealUser; /* if set, running as real user */ 199f0cc3246Seric # ifdef DEBUG 200f0cc3246Seric bool Debug; /* turn on tracing */ 201f0cc3246Seric # endif 2020faf63f9Seric 203adf8f7d4Seric main(argc, argv) 204adf8f7d4Seric int argc; 205adf8f7d4Seric char **argv; 206adf8f7d4Seric { 207adf8f7d4Seric register char *p; 208b1ed8a43Seric extern struct sccsprog *lookup(); 20941290c52Seric register int i; 210adf8f7d4Seric 211adf8f7d4Seric /* 212adf8f7d4Seric ** Detect and decode flags intended for this program. 213adf8f7d4Seric */ 214adf8f7d4Seric 2157de81dc7Seric if (argc < 2) 216adf8f7d4Seric { 217095915d3Seric fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); 2187de81dc7Seric exit(EX_USAGE); 2197de81dc7Seric } 2207de81dc7Seric argv[argc] = NULL; 2217de81dc7Seric 222b1ed8a43Seric if (lookup(argv[0]) == NULL) 223b1ed8a43Seric { 2247de81dc7Seric while ((p = *++argv) != NULL) 2257de81dc7Seric { 226adf8f7d4Seric if (*p != '-') 227adf8f7d4Seric break; 228adf8f7d4Seric switch (*++p) 229adf8f7d4Seric { 230adf8f7d4Seric case 'r': /* run as real user */ 231adf8f7d4Seric setuid(getuid()); 232c4432be4Seric RealUser++; 233adf8f7d4Seric break; 234adf8f7d4Seric 2357abec564Seric # ifndef SCCSDIR 236adf8f7d4Seric case 'p': /* path of sccs files */ 237adf8f7d4Seric SccsPath = ++p; 238adf8f7d4Seric break; 239adf8f7d4Seric 2400a8b9ab0Seric case 'd': /* directory to search from */ 2410a8b9ab0Seric SccsDir = ++p; 2420a8b9ab0Seric break; 243095915d3Seric # endif 2440a8b9ab0Seric 245f0cc3246Seric # ifdef DEBUG 246f0cc3246Seric case 'T': /* trace */ 247f0cc3246Seric Debug++; 248f0cc3246Seric break; 249f0cc3246Seric # endif 250f0cc3246Seric 251adf8f7d4Seric default: 252095915d3Seric usrerr("unknown option -%s", p); 253adf8f7d4Seric break; 254adf8f7d4Seric } 255adf8f7d4Seric } 2565cabffd9Seric if (SccsPath[0] == '\0') 2575cabffd9Seric SccsPath = "."; 258b1ed8a43Seric } 259adf8f7d4Seric 260108d6082Seric i = command(argv, FALSE, FALSE, ""); 26141290c52Seric exit(i); 2627de81dc7Seric } 2630faf63f9Seric 2640faf63f9Seric /* 26541290c52Seric ** COMMAND -- look up and perform a command 26641290c52Seric ** 26741290c52Seric ** This routine is the guts of this program. Given an 26841290c52Seric ** argument vector, it looks up the "command" (argv[0]) 26941290c52Seric ** in the configuration table and does the necessary stuff. 27041290c52Seric ** 27141290c52Seric ** Parameters: 27241290c52Seric ** argv -- an argument vector to process. 27341290c52Seric ** forkflag -- if set, fork before executing the command. 274108d6082Seric ** editflag -- if set, only include flags listed in the 275108d6082Seric ** sccsklets field of the command descriptor. 276108d6082Seric ** arg0 -- a space-seperated list of arguments to insert 277108d6082Seric ** before argv. 27841290c52Seric ** 27941290c52Seric ** Returns: 28041290c52Seric ** zero -- command executed ok. 28141290c52Seric ** else -- error status. 28241290c52Seric ** 28341290c52Seric ** Side Effects: 28441290c52Seric ** none. 28541290c52Seric */ 2867de81dc7Seric 287108d6082Seric command(argv, forkflag, editflag, arg0) 2887de81dc7Seric char **argv; 2891777fbcbSeric bool forkflag; 290108d6082Seric bool editflag; 291108d6082Seric char *arg0; 2927de81dc7Seric { 2937de81dc7Seric register struct sccsprog *cmd; 2947de81dc7Seric register char *p; 2951777fbcbSeric char buf[40]; 296b1ed8a43Seric extern struct sccsprog *lookup(); 297108d6082Seric char *nav[1000]; 298108d6082Seric char **np; 299419708b0Seric register char **ap; 300d51cd7e5Seric register int i; 301419708b0Seric register char *q; 302d51cd7e5Seric extern bool unedit(); 30341290c52Seric int rval = 0; 304108d6082Seric extern char *index(); 305108d6082Seric extern char *makefile(); 306f0cc3246Seric 307f0cc3246Seric # ifdef DEBUG 308f0cc3246Seric if (Debug) 309f0cc3246Seric { 310108d6082Seric printf("command:\n\t\"%s\"\n", arg0); 311108d6082Seric for (np = argv; *np != NULL; np++) 312108d6082Seric printf("\t\"%s\"\n", *np); 313f0cc3246Seric } 314f0cc3246Seric # endif 315c4432be4Seric 316c4432be4Seric /* 317108d6082Seric ** Copy arguments. 318108d6082Seric ** Phase one -- from arg0 & if necessary argv[0]. 319adf8f7d4Seric */ 320adf8f7d4Seric 321419708b0Seric np = ap = &nav[1]; 322108d6082Seric for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) 323108d6082Seric { 324108d6082Seric *np++ = q; 325108d6082Seric while (*p == ' ') 326108d6082Seric p++; 327108d6082Seric while (*p != ' ' && *p != '\0' && *p != '/') 328108d6082Seric *q++ = *p++; 329108d6082Seric *q++ = '\0'; 330108d6082Seric } 331108d6082Seric *np = NULL; 332419708b0Seric if (*ap == NULL) 333108d6082Seric *np++ = *argv++; 334108d6082Seric 335108d6082Seric /* 336108d6082Seric ** Look up command. 337419708b0Seric ** At this point, *ap is the command name. 338108d6082Seric */ 339108d6082Seric 340419708b0Seric cmd = lookup(*ap); 341b1ed8a43Seric if (cmd == NULL) 342adf8f7d4Seric { 343419708b0Seric usrerr("Unknown command \"%s\"", *ap); 34441290c52Seric return (EX_USAGE); 345adf8f7d4Seric } 346adf8f7d4Seric 347adf8f7d4Seric /* 348108d6082Seric ** Copy remaining arguments doing editing as appropriate. 349108d6082Seric */ 350108d6082Seric 351108d6082Seric for (; *argv != NULL; argv++) 352108d6082Seric { 353108d6082Seric p = *argv; 354108d6082Seric if (*p == '-') 355108d6082Seric { 356108d6082Seric if (p[1] == '\0' || !editflag || cmd->sccsklets == NULL || 357108d6082Seric index(cmd->sccsklets, p[1]) != NULL) 358108d6082Seric *np++ = p; 359108d6082Seric } 360108d6082Seric else 361108d6082Seric { 362108d6082Seric if (!bitset(NO_SDOT, cmd->sccsflags)) 363108d6082Seric p = makefile(p); 364108d6082Seric if (p != NULL) 365108d6082Seric *np++ = p; 366108d6082Seric } 367108d6082Seric } 368108d6082Seric *np = NULL; 369108d6082Seric 370108d6082Seric /* 3717de81dc7Seric ** Interpret operation associated with this command. 372c4432be4Seric */ 373c4432be4Seric 3747de81dc7Seric switch (cmd->sccsoper) 3757de81dc7Seric { 376419708b0Seric case SHELL: /* call a shell file */ 377419708b0Seric *ap = cmd->sccspath; 378419708b0Seric *--ap = "sh"; 379419708b0Seric rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag); 380419708b0Seric break; 381419708b0Seric 3827de81dc7Seric case PROG: /* call an sccs prog */ 383419708b0Seric rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); 3841777fbcbSeric break; 3851777fbcbSeric 3861777fbcbSeric case CMACRO: /* command macro */ 3871777fbcbSeric for (p = cmd->sccspath; *p != '\0'; p++) 3881777fbcbSeric { 389108d6082Seric q = p; 390108d6082Seric while (*p != '\0' && *p != '/') 391108d6082Seric p++; 392419708b0Seric rval = command(&ap[1], *p != '\0', TRUE, q); 39341290c52Seric if (rval != 0) 39441290c52Seric break; 3951777fbcbSeric } 39641290c52Seric break; 3977de81dc7Seric 398172147faSeric case FIX: /* fix a delta */ 399419708b0Seric if (strncmp(ap[1], "-r", 2) != 0) 400172147faSeric { 401095915d3Seric usrerr("-r flag needed for fix command"); 40241290c52Seric rval = EX_USAGE; 403172147faSeric break; 404172147faSeric } 405419708b0Seric rval = command(&ap[1], TRUE, TRUE, "get -k"); 40641290c52Seric if (rval == 0) 407419708b0Seric rval = command(&ap[1], TRUE, TRUE, "rmdel"); 40841290c52Seric if (rval == 0) 409419708b0Seric rval = command(&ap[2], FALSE, TRUE, "get -e -g"); 41041290c52Seric break; 411172147faSeric 412b2538d76Seric case CLEAN: 41341290c52Seric rval = clean((int) cmd->sccspath); 414b2538d76Seric break; 415b2538d76Seric 416e39a5722Seric case UNEDIT: 417419708b0Seric for (argv = np = &ap[1]; *argv != NULL; argv++) 418d51cd7e5Seric { 419108d6082Seric if (unedit(*argv)) 420108d6082Seric *np++ = *argv; 421d51cd7e5Seric } 422108d6082Seric *np = NULL; 423d51cd7e5Seric if (i > 0) 424419708b0Seric rval = command(&ap[1], FALSE, FALSE, "get"); 425e39a5722Seric break; 426e39a5722Seric 427*78d41c6aSeric case DIFFS: /* diff between s-file & edit file */ 428*78d41c6aSeric /* find the end of the flag arguments */ 429*78d41c6aSeric for (np = &ap[1]; *np != NULL && **np == '-'; np++) 430*78d41c6aSeric continue; 431*78d41c6aSeric argv = np; 432*78d41c6aSeric 433*78d41c6aSeric /* for each file, do the diff */ 434*78d41c6aSeric while (*np != NULL) 435*78d41c6aSeric { 436*78d41c6aSeric *argv = *np++; 437*78d41c6aSeric p = *np; 438*78d41c6aSeric *np = NULL; 439*78d41c6aSeric i = dodiff(ap, *argv); 440*78d41c6aSeric if (rval == 0) 441*78d41c6aSeric rval = i; 442*78d41c6aSeric *np = p; 443*78d41c6aSeric } 444*78d41c6aSeric break; 445*78d41c6aSeric 4467de81dc7Seric default: 447095915d3Seric syserr("oper %d", cmd->sccsoper); 4487de81dc7Seric exit(EX_SOFTWARE); 4497de81dc7Seric } 45041290c52Seric # ifdef DEBUG 45141290c52Seric if (Debug) 45241290c52Seric printf("command: rval=%d\n", rval); 45341290c52Seric # endif 45441290c52Seric return (rval); 4557de81dc7Seric } 4560faf63f9Seric 4570faf63f9Seric /* 458b1ed8a43Seric ** LOOKUP -- look up an SCCS command name. 459b1ed8a43Seric ** 460b1ed8a43Seric ** Parameters: 461b1ed8a43Seric ** name -- the name of the command to look up. 462b1ed8a43Seric ** 463b1ed8a43Seric ** Returns: 464b1ed8a43Seric ** ptr to command descriptor for this command. 465b1ed8a43Seric ** NULL if no such entry. 466b1ed8a43Seric ** 467b1ed8a43Seric ** Side Effects: 468b1ed8a43Seric ** none. 469b1ed8a43Seric */ 470b1ed8a43Seric 471b1ed8a43Seric struct sccsprog * 472b1ed8a43Seric lookup(name) 473b1ed8a43Seric char *name; 474b1ed8a43Seric { 475b1ed8a43Seric register struct sccsprog *cmd; 476b1ed8a43Seric 477b1ed8a43Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 478b1ed8a43Seric { 479b1ed8a43Seric if (strcmp(cmd->sccsname, name) == 0) 480b1ed8a43Seric return (cmd); 481b1ed8a43Seric } 482b1ed8a43Seric return (NULL); 483b1ed8a43Seric } 4840faf63f9Seric 4850faf63f9Seric /* 48641290c52Seric ** CALLPROG -- call a program 48741290c52Seric ** 488108d6082Seric ** Used to call the SCCS programs. 48941290c52Seric ** 49041290c52Seric ** Parameters: 49141290c52Seric ** progpath -- pathname of the program to call. 49241290c52Seric ** flags -- status flags from the command descriptors. 49341290c52Seric ** argv -- an argument vector to pass to the program. 49441290c52Seric ** forkflag -- if true, fork before calling, else just 49541290c52Seric ** exec. 49641290c52Seric ** 49741290c52Seric ** Returns: 49841290c52Seric ** The exit status of the program. 49941290c52Seric ** Nothing if forkflag == FALSE. 50041290c52Seric ** 50141290c52Seric ** Side Effects: 50241290c52Seric ** Can exit if forkflag == FALSE. 50341290c52Seric */ 504172147faSeric 5057de81dc7Seric callprog(progpath, flags, argv, forkflag) 5067de81dc7Seric char *progpath; 5077de81dc7Seric short flags; 5087de81dc7Seric char **argv; 5097de81dc7Seric bool forkflag; 5107de81dc7Seric { 5117de81dc7Seric register int i; 5121777fbcbSeric auto int st; 513108d6082Seric 514108d6082Seric # ifdef DEBUG 515108d6082Seric if (Debug) 516108d6082Seric { 517108d6082Seric printf("callprog:\n"); 518108d6082Seric for (i = 0; argv[i] != NULL; i++) 519108d6082Seric printf("\t\"%s\"\n", argv[i]); 520108d6082Seric } 521108d6082Seric # endif 5227de81dc7Seric 5237de81dc7Seric if (*argv == NULL) 5247de81dc7Seric return (-1); 525c4432be4Seric 526c4432be4Seric /* 527172147faSeric ** Fork if appropriate. 528adf8f7d4Seric */ 529adf8f7d4Seric 5307de81dc7Seric if (forkflag) 5317de81dc7Seric { 532f0cc3246Seric # ifdef DEBUG 533f0cc3246Seric if (Debug) 534f0cc3246Seric printf("Forking\n"); 535f0cc3246Seric # endif 5367de81dc7Seric i = fork(); 5377de81dc7Seric if (i < 0) 5387de81dc7Seric { 539095915d3Seric syserr("cannot fork"); 5407de81dc7Seric exit(EX_OSERR); 5417de81dc7Seric } 5427de81dc7Seric else if (i > 0) 5431777fbcbSeric { 5441777fbcbSeric wait(&st); 54541290c52Seric if ((st & 0377) == 0) 54641290c52Seric st = (st >> 8) & 0377; 547*78d41c6aSeric if (OutFile >= 0) 548*78d41c6aSeric { 549*78d41c6aSeric close(OutFile); 550*78d41c6aSeric OutFile = -1; 551*78d41c6aSeric } 5521777fbcbSeric return (st); 5531777fbcbSeric } 5547de81dc7Seric } 555*78d41c6aSeric else if (OutFile >= 0) 556*78d41c6aSeric { 557*78d41c6aSeric syserr("callprog: setting stdout w/o forking"); 558*78d41c6aSeric exit(EX_SOFTWARE); 559*78d41c6aSeric } 5607de81dc7Seric 561*78d41c6aSeric /* set protection as appropriate */ 5627de81dc7Seric if (bitset(REALUSER, flags)) 5637de81dc7Seric setuid(getuid()); 5647de81dc7Seric 565*78d41c6aSeric /* change standard input & output if needed */ 566*78d41c6aSeric if (OutFile >= 0) 567*78d41c6aSeric { 568*78d41c6aSeric close(1); 569*78d41c6aSeric dup(OutFile); 570*78d41c6aSeric close(OutFile); 571*78d41c6aSeric } 5727de81dc7Seric 573*78d41c6aSeric /* call real SCCS program */ 574172147faSeric execv(progpath, argv); 575095915d3Seric syserr("cannot execute %s", progpath); 576adf8f7d4Seric exit(EX_UNAVAILABLE); 577adf8f7d4Seric } 5780faf63f9Seric 5790faf63f9Seric /* 580cdc1aa65Seric ** MAKEFILE -- make filename of SCCS file 581cdc1aa65Seric ** 582cdc1aa65Seric ** If the name passed is already the name of an SCCS file, 583cdc1aa65Seric ** just return it. Otherwise, munge the name into the name 584cdc1aa65Seric ** of the actual SCCS file. 585cdc1aa65Seric ** 586cdc1aa65Seric ** There are cases when it is not clear what you want to 587cdc1aa65Seric ** do. For example, if SccsPath is an absolute pathname 588cdc1aa65Seric ** and the name given is also an absolute pathname, we go 589cdc1aa65Seric ** for SccsPath (& only use the last component of the name 590cdc1aa65Seric ** passed) -- this is important for security reasons (if 591cdc1aa65Seric ** sccs is being used as a setuid front end), but not 592cdc1aa65Seric ** particularly intuitive. 593cdc1aa65Seric ** 594cdc1aa65Seric ** Parameters: 595cdc1aa65Seric ** name -- the file name to be munged. 596cdc1aa65Seric ** 597cdc1aa65Seric ** Returns: 598cdc1aa65Seric ** The pathname of the sccs file. 599cdc1aa65Seric ** NULL on error. 600cdc1aa65Seric ** 601cdc1aa65Seric ** Side Effects: 602cdc1aa65Seric ** none. 603cdc1aa65Seric */ 604adf8f7d4Seric 605adf8f7d4Seric char * 606adf8f7d4Seric makefile(name) 607adf8f7d4Seric char *name; 608adf8f7d4Seric { 609adf8f7d4Seric register char *p; 610adf8f7d4Seric register char c; 611adf8f7d4Seric char buf[512]; 6120a8b9ab0Seric struct stat stbuf; 613adf8f7d4Seric extern char *malloc(); 614cdc1aa65Seric extern char *rindex(); 6150a8b9ab0Seric extern bool safepath(); 6167d70f5d8Seric extern bool isdir(); 6177d70f5d8Seric register char *q; 618cdc1aa65Seric 619cdc1aa65Seric p = rindex(name, '/'); 620cdc1aa65Seric if (p == NULL) 621cdc1aa65Seric p = name; 622cdc1aa65Seric else 623cdc1aa65Seric p++; 624adf8f7d4Seric 625adf8f7d4Seric /* 6260a8b9ab0Seric ** Check to see that the path is "safe", i.e., that we 6270a8b9ab0Seric ** are not letting some nasty person use the setuid part 6280a8b9ab0Seric ** of this program to look at or munge some presumably 6290a8b9ab0Seric ** hidden files. 630adf8f7d4Seric */ 631adf8f7d4Seric 6320a8b9ab0Seric if (SccsDir[0] == '/' && !safepath(name)) 6330a8b9ab0Seric return (NULL); 634cdc1aa65Seric 635cdc1aa65Seric /* 6360a8b9ab0Seric ** Create the base pathname. 637cdc1aa65Seric */ 638cdc1aa65Seric 6390a8b9ab0Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) 640adf8f7d4Seric { 6410a8b9ab0Seric strcpy(buf, SccsDir); 642cdc1aa65Seric strcat(buf, "/"); 643cdc1aa65Seric } 644cdc1aa65Seric else 645cdc1aa65Seric strcpy(buf, ""); 646cdc1aa65Seric strncat(buf, name, p - name); 6477d70f5d8Seric q = &buf[strlen(buf)]; 6487d70f5d8Seric strcpy(q, p); 6497d70f5d8Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 6507d70f5d8Seric { 6510a8b9ab0Seric strcpy(q, SccsPath); 6520a8b9ab0Seric strcat(buf, "/s."); 653cdc1aa65Seric strcat(buf, p); 654cdc1aa65Seric } 655adf8f7d4Seric 6560a8b9ab0Seric if (strcmp(buf, name) == 0) 6570a8b9ab0Seric p = name; 6580a8b9ab0Seric else 6590a8b9ab0Seric { 660adf8f7d4Seric p = malloc(strlen(buf) + 1); 661adf8f7d4Seric if (p == NULL) 662adf8f7d4Seric { 663adf8f7d4Seric perror("Sccs: no mem"); 664adf8f7d4Seric exit(EX_OSERR); 665adf8f7d4Seric } 666adf8f7d4Seric strcpy(p, buf); 6670a8b9ab0Seric } 668adf8f7d4Seric return (p); 669adf8f7d4Seric } 6700faf63f9Seric 6710faf63f9Seric /* 6727d70f5d8Seric ** ISDIR -- return true if the argument is a directory. 6737d70f5d8Seric ** 6747d70f5d8Seric ** Parameters: 6757d70f5d8Seric ** name -- the pathname of the file to check. 6767d70f5d8Seric ** 6777d70f5d8Seric ** Returns: 6787d70f5d8Seric ** TRUE if 'name' is a directory, FALSE otherwise. 6797d70f5d8Seric ** 6807d70f5d8Seric ** Side Effects: 6817d70f5d8Seric ** none. 6827d70f5d8Seric */ 6837d70f5d8Seric 6847d70f5d8Seric bool 6857d70f5d8Seric isdir(name) 6867d70f5d8Seric char *name; 6877d70f5d8Seric { 6887d70f5d8Seric struct stat stbuf; 6897d70f5d8Seric 6907d70f5d8Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 6917d70f5d8Seric } 6920faf63f9Seric 6930faf63f9Seric /* 694cdc1aa65Seric ** SAFEPATH -- determine whether a pathname is "safe" 695cdc1aa65Seric ** 696cdc1aa65Seric ** "Safe" pathnames only allow you to get deeper into the 697cdc1aa65Seric ** directory structure, i.e., full pathnames and ".." are 698cdc1aa65Seric ** not allowed. 699cdc1aa65Seric ** 700cdc1aa65Seric ** Parameters: 701cdc1aa65Seric ** p -- the name to check. 702cdc1aa65Seric ** 703cdc1aa65Seric ** Returns: 704cdc1aa65Seric ** TRUE -- if the path is safe. 705cdc1aa65Seric ** FALSE -- if the path is not safe. 706cdc1aa65Seric ** 707cdc1aa65Seric ** Side Effects: 708cdc1aa65Seric ** Prints a message if the path is not safe. 709cdc1aa65Seric */ 710cdc1aa65Seric 711cdc1aa65Seric bool 712cdc1aa65Seric safepath(p) 713cdc1aa65Seric register char *p; 714cdc1aa65Seric { 715cdc1aa65Seric extern char *index(); 716cdc1aa65Seric 717cdc1aa65Seric if (*p != '/') 718cdc1aa65Seric { 719cdc1aa65Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 720cdc1aa65Seric { 721cdc1aa65Seric p = index(p, '/'); 722cdc1aa65Seric if (p == NULL) 723cdc1aa65Seric return (TRUE); 724cdc1aa65Seric p++; 725cdc1aa65Seric } 726cdc1aa65Seric } 727cdc1aa65Seric 728cdc1aa65Seric printf("You may not use full pathnames or \"..\"\n"); 729cdc1aa65Seric return (FALSE); 730cdc1aa65Seric } 7310faf63f9Seric 7320faf63f9Seric /* 733b2538d76Seric ** CLEAN -- clean out recreatable files 734b2538d76Seric ** 735b2538d76Seric ** Any file for which an "s." file exists but no "p." file 736b2538d76Seric ** exists in the current directory is purged. 737b2538d76Seric ** 738b2538d76Seric ** Parameters: 73948ff0e7eSeric ** tells whether this came from a "clean", "info", or 74048ff0e7eSeric ** "check" command. 741b2538d76Seric ** 742b2538d76Seric ** Returns: 743b2538d76Seric ** none. 744b2538d76Seric ** 745b2538d76Seric ** Side Effects: 74648ff0e7eSeric ** Removes files in the current directory. 74748ff0e7eSeric ** Prints information regarding files being edited. 74848ff0e7eSeric ** Exits if a "check" command. 749b2538d76Seric */ 750b2538d76Seric 75148ff0e7eSeric clean(mode) 75248ff0e7eSeric int mode; 753b2538d76Seric { 754b2538d76Seric struct direct dir; 755b2538d76Seric struct stat stbuf; 756b2538d76Seric char buf[100]; 757bf9c1e66Seric char pline[120]; 7583a208c77Seric register FILE *dirfd; 7593a208c77Seric register char *basefile; 7602caac8fdSeric bool gotedit; 761bf9c1e66Seric FILE *pfp; 762b2538d76Seric 763f77a08fbSeric strcpy(buf, SccsDir); 764f77a08fbSeric if (buf[0] != '\0') 765f77a08fbSeric strcat(buf, "/"); 766f77a08fbSeric strcat(buf, SccsPath); 767f77a08fbSeric dirfd = fopen(buf, "r"); 768b2538d76Seric if (dirfd == NULL) 769b2538d76Seric { 770f77a08fbSeric usrerr("cannot open %s", buf); 77141290c52Seric return (EX_NOINPUT); 772b2538d76Seric } 773b2538d76Seric 774b2538d76Seric /* 775b2538d76Seric ** Scan the SCCS directory looking for s. files. 776b2538d76Seric */ 777b2538d76Seric 7782caac8fdSeric gotedit = FALSE; 779b2538d76Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 780b2538d76Seric { 781c9c62fd0Seric if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) 782b2538d76Seric continue; 783b2538d76Seric 784b2538d76Seric /* got an s. file -- see if the p. file exists */ 785f77a08fbSeric strcpy(buf, SccsDir); 786f77a08fbSeric if (buf[0] != '\0') 787f77a08fbSeric strcat(buf, "/"); 788f77a08fbSeric strcat(buf, SccsPath); 789b2538d76Seric strcat(buf, "/p."); 7903a208c77Seric basefile = &buf[strlen(buf)]; 791c9c62fd0Seric strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 792bf9c1e66Seric basefile[sizeof dir.d_name - 2] = '\0'; 793bf9c1e66Seric pfp = fopen(buf, "r"); 794bf9c1e66Seric if (pfp != NULL) 7953a208c77Seric { 796bf9c1e66Seric while (fgets(pline, sizeof pline, pfp) != NULL) 7972444cd3eSeric printf("%12s: being edited: %s", basefile, pline); 798bf9c1e66Seric fclose(pfp); 7992caac8fdSeric gotedit = TRUE; 800b2538d76Seric continue; 8013a208c77Seric } 802b2538d76Seric 803b2538d76Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 80448ff0e7eSeric if (mode == CLEANC) 8053a208c77Seric { 806c9c62fd0Seric strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); 8073a208c77Seric buf[sizeof dir.d_name - 2] = '\0'; 808b2538d76Seric unlink(buf); 809b2538d76Seric } 8103a208c77Seric } 811b2538d76Seric 812b2538d76Seric fclose(dirfd); 81348ff0e7eSeric if (!gotedit && mode == INFOC) 8142444cd3eSeric printf("Nothing being edited\n"); 81548ff0e7eSeric if (mode == CHECKC) 81648ff0e7eSeric exit(gotedit); 81741290c52Seric return (EX_OK); 818b2538d76Seric } 8190faf63f9Seric 8200faf63f9Seric /* 821e39a5722Seric ** UNEDIT -- unedit a file 822e39a5722Seric ** 823e39a5722Seric ** Checks to see that the current user is actually editting 824e39a5722Seric ** the file and arranges that s/he is not editting it. 825e39a5722Seric ** 826e39a5722Seric ** Parameters: 8272444cd3eSeric ** fn -- the name of the file to be unedited. 828e39a5722Seric ** 829e39a5722Seric ** Returns: 830d51cd7e5Seric ** TRUE -- if the file was successfully unedited. 831d51cd7e5Seric ** FALSE -- if the file was not unedited for some 832d51cd7e5Seric ** reason. 833e39a5722Seric ** 834e39a5722Seric ** Side Effects: 835e39a5722Seric ** fn is removed 836e39a5722Seric ** entries are removed from pfile. 837e39a5722Seric */ 838e39a5722Seric 839d51cd7e5Seric bool 840e39a5722Seric unedit(fn) 841e39a5722Seric char *fn; 842e39a5722Seric { 843e39a5722Seric register FILE *pfp; 844e39a5722Seric char *pfn; 845e39a5722Seric static char tfn[] = "/tmp/sccsXXXXX"; 846e39a5722Seric FILE *tfp; 847e39a5722Seric register char *p; 848e39a5722Seric register char *q; 849e39a5722Seric bool delete = FALSE; 850e39a5722Seric bool others = FALSE; 851e39a5722Seric char *myname; 852e39a5722Seric extern char *getlogin(); 853e39a5722Seric struct pfile *pent; 854e39a5722Seric extern struct pfile *getpfile(); 855e39a5722Seric char buf[120]; 856108d6082Seric extern char *makefile(); 85710cd16efSeric # ifdef UIDUSER 85810cd16efSeric struct passwd *pw; 85910cd16efSeric extern struct passwd *getpwuid(); 86010cd16efSeric # endif UIDUSER 861e39a5722Seric 862e39a5722Seric /* make "s." filename & find the trailing component */ 863e39a5722Seric pfn = makefile(fn); 864cdc1aa65Seric if (pfn == NULL) 865cdc1aa65Seric return (FALSE); 866cdc1aa65Seric q = rindex(pfn, '/'); 867cdc1aa65Seric if (q == NULL) 868cdc1aa65Seric q = &pfn[-1]; 869cdc1aa65Seric if (q[1] != 's' || q[2] != '.') 870e39a5722Seric { 871095915d3Seric usrerr("bad file name \"%s\"", fn); 872d51cd7e5Seric return (FALSE); 873e39a5722Seric } 874e39a5722Seric 875e39a5722Seric /* turn "s." into "p." */ 876e39a5722Seric *++q = 'p'; 877e39a5722Seric 878e39a5722Seric pfp = fopen(pfn, "r"); 879e39a5722Seric if (pfp == NULL) 880e39a5722Seric { 8812444cd3eSeric printf("%12s: not being edited\n", fn); 882d51cd7e5Seric return (FALSE); 883e39a5722Seric } 884e39a5722Seric 885e39a5722Seric /* 886e39a5722Seric ** Copy p-file to temp file, doing deletions as needed. 887e39a5722Seric */ 888e39a5722Seric 889e39a5722Seric mktemp(tfn); 890e39a5722Seric tfp = fopen(tfn, "w"); 891e39a5722Seric if (tfp == NULL) 892e39a5722Seric { 893095915d3Seric usrerr("cannot create \"%s\"", tfn); 894e39a5722Seric exit(EX_OSERR); 895e39a5722Seric } 896e39a5722Seric 89710cd16efSeric # ifdef UIDUSER 89810cd16efSeric pw = getpwuid(getuid()); 89910cd16efSeric if (pw == NULL) 90010cd16efSeric { 901095915d3Seric syserr("who are you? (uid=%d)", getuid()); 90210cd16efSeric exit(EX_OSERR); 90310cd16efSeric } 90410cd16efSeric myname = pw->pw_name; 90510cd16efSeric # else 906e39a5722Seric myname = getlogin(); 90710cd16efSeric # endif UIDUSER 908e39a5722Seric while ((pent = getpfile(pfp)) != NULL) 909e39a5722Seric { 910e39a5722Seric if (strcmp(pent->p_user, myname) == 0) 911e39a5722Seric { 912e39a5722Seric /* a match */ 913e39a5722Seric delete++; 914e39a5722Seric } 915e39a5722Seric else 916e39a5722Seric { 917e39a5722Seric fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, 918e39a5722Seric pent->p_nsid, pent->p_user, pent->p_date, 919e39a5722Seric pent->p_time); 920e39a5722Seric others++; 921e39a5722Seric } 922e39a5722Seric } 923e39a5722Seric 924e39a5722Seric /* do final cleanup */ 925e39a5722Seric if (others) 926e39a5722Seric { 927e39a5722Seric if (freopen(tfn, "r", tfp) == NULL) 928e39a5722Seric { 929095915d3Seric syserr("cannot reopen \"%s\"", tfn); 930e39a5722Seric exit(EX_OSERR); 931e39a5722Seric } 932e39a5722Seric if (freopen(pfn, "w", pfp) == NULL) 933e39a5722Seric { 934095915d3Seric usrerr("cannot create \"%s\"", pfn); 935d51cd7e5Seric return (FALSE); 936e39a5722Seric } 937e39a5722Seric while (fgets(buf, sizeof buf, tfp) != NULL) 938e39a5722Seric fputs(buf, pfp); 939e39a5722Seric } 940e39a5722Seric else 941e39a5722Seric { 942e39a5722Seric unlink(pfn); 943e39a5722Seric } 944e39a5722Seric fclose(tfp); 945e39a5722Seric fclose(pfp); 946e39a5722Seric unlink(tfn); 947e39a5722Seric 948e39a5722Seric if (delete) 949e39a5722Seric { 950e39a5722Seric unlink(fn); 951e39a5722Seric printf("%12s: removed\n", fn); 952d51cd7e5Seric return (TRUE); 953e39a5722Seric } 954e39a5722Seric else 955e39a5722Seric { 9562444cd3eSeric printf("%12s: not being edited by you\n", fn); 957d51cd7e5Seric return (FALSE); 958e39a5722Seric } 959e39a5722Seric } 9600faf63f9Seric 9610faf63f9Seric /* 962*78d41c6aSeric ** DODIFF -- diff an s-file against a g-file 963*78d41c6aSeric ** 964*78d41c6aSeric ** Parameters: 965*78d41c6aSeric ** getv -- argv for the 'get' command. 966*78d41c6aSeric ** gfile -- name of the g-file to diff against. 967*78d41c6aSeric ** 968*78d41c6aSeric ** Returns: 969*78d41c6aSeric ** Result of get. 970*78d41c6aSeric ** 971*78d41c6aSeric ** Side Effects: 972*78d41c6aSeric ** none. 973*78d41c6aSeric */ 974*78d41c6aSeric 975*78d41c6aSeric dodiff(getv, gfile) 976*78d41c6aSeric char **getv; 977*78d41c6aSeric char *gfile; 978*78d41c6aSeric { 979*78d41c6aSeric int pipev[2]; 980*78d41c6aSeric int rval; 981*78d41c6aSeric register int i; 982*78d41c6aSeric register int pid; 983*78d41c6aSeric auto int st; 984*78d41c6aSeric extern int errno; 985*78d41c6aSeric int (*osig)(); 986*78d41c6aSeric 987*78d41c6aSeric if (pipe(pipev) < 0) 988*78d41c6aSeric { 989*78d41c6aSeric syserr("dodiff: pipe failed"); 990*78d41c6aSeric exit(EX_OSERR); 991*78d41c6aSeric } 992*78d41c6aSeric if ((pid = fork()) < 0) 993*78d41c6aSeric { 994*78d41c6aSeric syserr("dodiff: fork failed"); 995*78d41c6aSeric exit(EX_OSERR); 996*78d41c6aSeric } 997*78d41c6aSeric else if (pid > 0) 998*78d41c6aSeric { 999*78d41c6aSeric /* in parent; run get */ 1000*78d41c6aSeric OutFile = pipev[1]; 1001*78d41c6aSeric close(pipev[0]); 1002*78d41c6aSeric rval = command(&getv[1], TRUE, FALSE, "get -s -k -p"); 1003*78d41c6aSeric osig = signal(SIGINT, SIG_IGN); 1004*78d41c6aSeric while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR) 1005*78d41c6aSeric errno = 0; 1006*78d41c6aSeric signal(SIGINT, osig); 1007*78d41c6aSeric /* ignore result of diff */ 1008*78d41c6aSeric } 1009*78d41c6aSeric else 1010*78d41c6aSeric { 1011*78d41c6aSeric /* in child, run diff */ 1012*78d41c6aSeric if (close(pipev[1]) < 0 || close(0) < 0 || 1013*78d41c6aSeric dup(pipev[0]) != 0 || close(pipev[0]) < 0) 1014*78d41c6aSeric { 1015*78d41c6aSeric syserr("dodiff: magic failed"); 1016*78d41c6aSeric exit(EX_OSERR); 1017*78d41c6aSeric } 1018*78d41c6aSeric execl(PROGPATH(bdiff), "bdiff", "-", gfile, NULL); 1019*78d41c6aSeric # ifndef V6 1020*78d41c6aSeric execlp("bdiff", "bdiff", "-", gfile, NULL); 1021*78d41c6aSeric execlp("diff", "diff", "-", gfile, NULL); 1022*78d41c6aSeric # endif NOT V6 1023*78d41c6aSeric syserr("bdiff: cannot execute"); 1024*78d41c6aSeric exit(EX_OSERR); 1025*78d41c6aSeric } 1026*78d41c6aSeric return (rval); 1027*78d41c6aSeric } 1028*78d41c6aSeric 1029*78d41c6aSeric /* 1030e39a5722Seric ** GETPFILE -- get an entry from the p-file 1031e39a5722Seric ** 1032e39a5722Seric ** Parameters: 1033e39a5722Seric ** pfp -- p-file file pointer 1034e39a5722Seric ** 1035e39a5722Seric ** Returns: 1036e39a5722Seric ** pointer to p-file struct for next entry 1037e39a5722Seric ** NULL on EOF or error 1038e39a5722Seric ** 1039e39a5722Seric ** Side Effects: 1040e39a5722Seric ** Each call wipes out results of previous call. 1041e39a5722Seric */ 1042e39a5722Seric 1043e39a5722Seric struct pfile * 1044e39a5722Seric getpfile(pfp) 1045e39a5722Seric FILE *pfp; 1046e39a5722Seric { 1047e39a5722Seric static struct pfile ent; 1048e39a5722Seric static char buf[120]; 1049e39a5722Seric register char *p; 1050e39a5722Seric extern char *nextfield(); 1051e39a5722Seric 1052e39a5722Seric if (fgets(buf, sizeof buf, pfp) == NULL) 1053e39a5722Seric return (NULL); 1054e39a5722Seric 1055e39a5722Seric ent.p_osid = p = buf; 1056e39a5722Seric ent.p_nsid = p = nextfield(p); 1057e39a5722Seric ent.p_user = p = nextfield(p); 1058e39a5722Seric ent.p_date = p = nextfield(p); 1059e39a5722Seric ent.p_time = p = nextfield(p); 1060e39a5722Seric if (p == NULL || nextfield(p) != NULL) 1061e39a5722Seric return (NULL); 1062e39a5722Seric 1063e39a5722Seric return (&ent); 1064e39a5722Seric } 1065e39a5722Seric 1066e39a5722Seric 1067e39a5722Seric char * 1068e39a5722Seric nextfield(p) 1069e39a5722Seric register char *p; 1070e39a5722Seric { 1071e39a5722Seric if (p == NULL || *p == '\0') 1072e39a5722Seric return (NULL); 1073e39a5722Seric while (*p != ' ' && *p != '\n' && *p != '\0') 1074e39a5722Seric p++; 1075e39a5722Seric if (*p == '\n' || *p == '\0') 1076e39a5722Seric { 1077e39a5722Seric *p = '\0'; 1078e39a5722Seric return (NULL); 1079e39a5722Seric } 1080e39a5722Seric *p++ = '\0'; 1081e39a5722Seric return (p); 1082e39a5722Seric } 10830faf63f9Seric 10840faf63f9Seric /* 1085095915d3Seric ** USRERR -- issue user-level error 1086095915d3Seric ** 1087095915d3Seric ** Parameters: 1088095915d3Seric ** f -- format string. 1089095915d3Seric ** p1-p3 -- parameters to a printf. 1090095915d3Seric ** 1091095915d3Seric ** Returns: 1092095915d3Seric ** -1 1093095915d3Seric ** 1094095915d3Seric ** Side Effects: 1095095915d3Seric ** none. 1096095915d3Seric */ 1097095915d3Seric 1098095915d3Seric usrerr(f, p1, p2, p3) 1099095915d3Seric char *f; 1100095915d3Seric { 1101095915d3Seric fprintf(stderr, "\n%s: ", MyName); 1102095915d3Seric fprintf(stderr, f, p1, p2, p3); 1103095915d3Seric fprintf(stderr, "\n"); 1104095915d3Seric 1105095915d3Seric return (-1); 1106095915d3Seric } 11070faf63f9Seric 11080faf63f9Seric /* 1109095915d3Seric ** SYSERR -- print system-generated error. 1110095915d3Seric ** 1111095915d3Seric ** Parameters: 1112095915d3Seric ** f -- format string to a printf. 1113095915d3Seric ** p1, p2, p3 -- parameters to f. 1114095915d3Seric ** 1115095915d3Seric ** Returns: 1116095915d3Seric ** never. 1117095915d3Seric ** 1118095915d3Seric ** Side Effects: 1119095915d3Seric ** none. 1120095915d3Seric */ 1121095915d3Seric 1122095915d3Seric syserr(f, p1, p2, p3) 1123095915d3Seric char *f; 1124095915d3Seric { 1125095915d3Seric extern int errno; 1126095915d3Seric 1127095915d3Seric fprintf(stderr, "\n%s SYSERR: ", MyName); 1128095915d3Seric fprintf(stderr, f, p1, p2, p3); 1129095915d3Seric fprintf(stderr, "\n"); 1130095915d3Seric if (errno == 0) 1131095915d3Seric exit(EX_SOFTWARE); 1132095915d3Seric else 1133095915d3Seric { 1134095915d3Seric perror(0); 1135095915d3Seric exit(EX_OSERR); 1136095915d3Seric } 1137095915d3Seric } 1138