1148Seric # include <stdio.h> 2148Seric # include <sys/types.h> 3148Seric # include <sys/stat.h> 4261Seric # include <sys/dir.h> 51433Seric # include <errno.h> 61433Seric # include <signal.h> 7148Seric # include <sysexits.h> 8202Seric # include <whoami.h> 9*2140Seric # include <pwd.h> 10148Seric 11828Seric /* 12828Seric ** SCCS.C -- human-oriented front end to the SCCS system. 13828Seric ** 14828Seric ** Without trying to add any functionality to speak of, this 15828Seric ** program tries to make SCCS a little more accessible to human 16828Seric ** types. The main thing it does is automatically put the 17828Seric ** string "SCCS/s." on the front of names. Also, it has a 18828Seric ** couple of things that are designed to shorten frequent 19828Seric ** combinations, e.g., "delget" which expands to a "delta" 20828Seric ** and a "get". 21828Seric ** 22828Seric ** This program can also function as a setuid front end. 23828Seric ** To do this, you should copy the source, renaming it to 24828Seric ** whatever you want, e.g., "syssccs". Change any defaults 25828Seric ** in the program (e.g., syssccs might default -d to 26828Seric ** "/usr/src/sys"). Then recompile and put the result 27828Seric ** as setuid to whomever you want. In this mode, sccs 28828Seric ** knows to not run setuid for certain programs in order 29828Seric ** to preserve security, and so forth. 30828Seric ** 31828Seric ** Usage: 32828Seric ** sccs [flags] command [args] 33828Seric ** 34828Seric ** Flags: 35828Seric ** -d<dir> <dir> represents a directory to search 36828Seric ** out of. It should be a full pathname 37828Seric ** for general usage. E.g., if <dir> is 38828Seric ** "/usr/src/sys", then a reference to the 39828Seric ** file "dev/bio.c" becomes a reference to 40828Seric ** "/usr/src/sys/dev/bio.c". 41828Seric ** -p<path> prepends <path> to the final component 42828Seric ** of the pathname. By default, this is 43828Seric ** "SCCS". For example, in the -d example 44828Seric ** above, the path then gets modified to 45828Seric ** "/usr/src/sys/dev/SCCS/s.bio.c". In 46828Seric ** more common usage (without the -d flag), 47828Seric ** "prog.c" would get modified to 48828Seric ** "SCCS/s.prog.c". In both cases, the 49828Seric ** "s." gets automatically prepended. 50828Seric ** -r run as the real user. 51828Seric ** 52828Seric ** Commands: 53828Seric ** admin, 54828Seric ** get, 55828Seric ** delta, 56828Seric ** rmdel, 57828Seric ** chghist, 58828Seric ** etc. Straight out of SCCS; only difference 59828Seric ** is that pathnames get modified as 60828Seric ** described above. 61828Seric ** edit Macro for "get -e". 62828Seric ** unedit Removes a file being edited, knowing 63828Seric ** about p-files, etc. 64828Seric ** delget Macro for "delta" followed by "get". 65828Seric ** deledit Macro for "delta" followed by "get -e". 66828Seric ** info Tell what files being edited. 67828Seric ** clean Remove all files that can be 68828Seric ** regenerated from SCCS files. 691205Seric ** check Like info, but return exit status, for 70828Seric ** use in makefiles. 71828Seric ** fix Remove a top delta & reedit, but save 72828Seric ** the previous changes in that delta. 73828Seric ** 74828Seric ** Compilation Flags: 75828Seric ** UIDUSER -- determine who the user is by looking at the 76828Seric ** uid rather than the login name -- for machines 77828Seric ** where SCCS gets the user in this way. 781270Seric ** SCCSDIR -- if defined, forces the -d flag to take on 791205Seric ** this value. This is so that the setuid 801205Seric ** aspects of this program cannot be abused. 811270Seric ** This flag also disables the -p flag. 821270Seric ** SCCSPATH -- the default for the -p flag. 831437Seric ** MYNAME -- the title this program should print when it 841437Seric ** gives error messages. 85828Seric ** 86828Seric ** Compilation Instructions: 87828Seric ** cc -O -n -s sccs.c 881437Seric ** The flags listed above can be -D defined to simplify 891437Seric ** recompilation for variant versions. 90828Seric ** 91828Seric ** Author: 92828Seric ** Eric Allman, UCB/INGRES 931270Seric ** Copyright 1980 Regents of the University of California 94828Seric */ 95155Seric 96*2140Seric static char SccsId[] = "@(#)sccs.c 1.58 01/14/81"; 971432Seric 981270Seric /******************* Configuration Information ********************/ 991270Seric 1001437Seric /* special defines for local berkeley systems */ 1011437Seric # include <whoami.h> 1021437Seric 103828Seric # ifdef CSVAX 104828Seric # define UIDUSER 1051270Seric # define PROGPATH(name) "/usr/local/name" 1061270Seric # endif CSVAX 107828Seric 1081618Seric # ifdef INGVAX 1091618Seric # define PROGPATH(name) "/usr/local/name" 1101618Seric # endif INGVAX 1111618Seric 1121867Seric # ifdef CORY 1131867Seric # define PROGPATH(name) "/usr/eecs/bin/name" 1141867Seric # endif CORY 1151867Seric 1161437Seric /* end of berkeley systems defines */ 1171437Seric 1181437Seric # ifndef SCCSPATH 1191432Seric # define SCCSPATH "SCCS" /* pathname in which to find s-files */ 1201437Seric # endif NOT SCCSPATH 121828Seric 1221437Seric # ifndef MYNAME 1231437Seric # define MYNAME "sccs" /* name used for printing errors */ 1241437Seric # endif NOT MYNAME 1251270Seric 1261432Seric # ifndef PROGPATH 1271432Seric # define PROGPATH(name) "/usr/sccs/name" /* place to find binaries */ 1281432Seric # endif PROGPATH 1291432Seric 1301270Seric /**************** End of Configuration Information ****************/ 1311432Seric 132157Seric typedef char bool; 133200Seric # define TRUE 1 134200Seric # define FALSE 0 135157Seric 1361438Seric # define bitset(bit, word) ((bool) ((bit) & (word))) 1371438Seric 138148Seric struct sccsprog 139148Seric { 140148Seric char *sccsname; /* name of SCCS routine */ 141200Seric short sccsoper; /* opcode, see below */ 142200Seric short sccsflags; /* flags, see below */ 143148Seric char *sccspath; /* pathname of binary implementing */ 144148Seric }; 145148Seric 146200Seric /* values for sccsoper */ 147200Seric # define PROG 0 /* call a program */ 148201Seric # define CMACRO 1 /* command substitution macro */ 149226Seric # define FIX 2 /* fix a delta */ 150261Seric # define CLEAN 3 /* clean out recreatable files */ 151396Seric # define UNEDIT 4 /* unedit a file */ 1521431Seric # define SHELL 5 /* call a shell file (like PROG) */ 1531433Seric # define DIFFS 6 /* diff between sccs & file out */ 1541871Seric # define DODIFF 7 /* internal call to diff program */ 155200Seric 156157Seric /* bits for sccsflags */ 157200Seric # define NO_SDOT 0001 /* no s. on front of args */ 158200Seric # define REALUSER 0002 /* protected (e.g., admin) */ 159148Seric 160819Seric /* modes for the "clean", "info", "check" ops */ 161819Seric # define CLEANC 0 /* clean command */ 162819Seric # define INFOC 1 /* info command */ 163819Seric # define CHECKC 2 /* check command */ 1641730Seric # define TELLC 3 /* give list of files being edited */ 165819Seric 1661432Seric /* 1671432Seric ** Description of commands known to this program. 1681432Seric ** First argument puts the command into a class. Second arg is 1691432Seric ** info regarding treatment of this command. Third arg is a 1701432Seric ** list of flags this command accepts from macros, etc. Fourth 1711432Seric ** arg is the pathname of the implementing program, or the 1721432Seric ** macro definition, or the arg to a sub-algorithm. 1731432Seric */ 174202Seric 175148Seric struct sccsprog SccsProg[] = 176148Seric { 1771864Seric "admin", PROG, REALUSER, PROGPATH(admin), 1781864Seric "chghist", PROG, 0, PROGPATH(rmdel), 1791864Seric "comb", PROG, 0, PROGPATH(comb), 1801864Seric "delta", PROG, 0, PROGPATH(delta), 1811864Seric "get", PROG, 0, PROGPATH(get), 1821864Seric "help", PROG, NO_SDOT, PROGPATH(help), 1832136Seric "prs", PROG, 0, PROGPATH(prs), 1841864Seric "prt", PROG, 0, PROGPATH(prt), 1851864Seric "rmdel", PROG, REALUSER, PROGPATH(rmdel), 1862136Seric "val", PROG, 0, PROGPATH(val), 1871864Seric "what", PROG, NO_SDOT, PROGPATH(what), 1881864Seric "sccsdiff", SHELL, REALUSER, PROGPATH(sccsdiff), 1891864Seric "edit", CMACRO, NO_SDOT, "get -e", 1901864Seric "delget", CMACRO, NO_SDOT, "delta:mysrp/get:ixbeskcl -t", 1912138Seric "deledit", CMACRO, NO_SDOT, "delta:mysrp -n/get:ixbskcl -e -t -g", 1921864Seric "fix", FIX, NO_SDOT, NULL, 1931864Seric "clean", CLEAN, REALUSER|NO_SDOT, (char *) CLEANC, 1941864Seric "info", CLEAN, REALUSER|NO_SDOT, (char *) INFOC, 1951864Seric "check", CLEAN, REALUSER|NO_SDOT, (char *) CHECKC, 1961864Seric "tell", CLEAN, REALUSER|NO_SDOT, (char *) TELLC, 1971864Seric "unedit", UNEDIT, NO_SDOT, NULL, 1981864Seric "diffs", DIFFS, NO_SDOT|REALUSER, NULL, 1991871Seric "-diff", DODIFF, NO_SDOT|REALUSER, PROGPATH(bdiff), 2002137Seric "print", CMACRO, 0, "prt -e/get -p -m -s", 2011864Seric NULL, -1, 0, NULL 202148Seric }; 203148Seric 2041432Seric /* one line from a p-file */ 205396Seric struct pfile 206396Seric { 207396Seric char *p_osid; /* old SID */ 208396Seric char *p_nsid; /* new SID */ 209396Seric char *p_user; /* user who did edit */ 210396Seric char *p_date; /* date of get */ 211396Seric char *p_time; /* time of get */ 212396Seric }; 213396Seric 2141270Seric char *SccsPath = SCCSPATH; /* pathname of SCCS files */ 2151270Seric # ifdef SCCSDIR 2161270Seric char *SccsDir = SCCSDIR; /* directory to begin search from */ 2171205Seric # else 2181270Seric char *SccsDir = ""; 2191205Seric # endif 2201437Seric char MyName[] = MYNAME; /* name used in messages */ 2211433Seric int OutFile = -1; /* override output file for commands */ 222157Seric bool RealUser; /* if set, running as real user */ 223393Seric # ifdef DEBUG 224393Seric bool Debug; /* turn on tracing */ 225393Seric # endif 2262139Seric # ifndef V6 2272139Seric extern char *getenv(); 2282139Seric # endif V6 2291432Seric 230148Seric main(argc, argv) 231148Seric int argc; 232148Seric char **argv; 233148Seric { 234148Seric register char *p; 235262Seric extern struct sccsprog *lookup(); 2361282Seric register int i; 2372139Seric # ifndef V6 2382139Seric # ifndef SCCSDIR 239*2140Seric register struct passwd *pw; 240*2140Seric extern struct passwd *getpwnam(); 241*2140Seric char buf[100]; 242*2140Seric 2432139Seric /* pull "SccsDir" out of the environment (possibly) */ 2442139Seric p = getenv("PROJECT"); 245*2140Seric if (p != NULL && p[0] != '\0') 246*2140Seric { 247*2140Seric if (p[0] == '/') 248*2140Seric SccsDir = p; 249*2140Seric else 250*2140Seric { 251*2140Seric pw = getpwnam(p); 252*2140Seric if (pw == NULL) 253*2140Seric { 254*2140Seric usrerr("user %s does not exist", p); 255*2140Seric exit(EX_USAGE); 256*2140Seric } 257*2140Seric strcpy(buf, pw->pw_dir); 258*2140Seric strcat(buf, "/src"); 259*2140Seric if (access(buf, 0) < 0) 260*2140Seric { 261*2140Seric strcpy(buf, pw->pw_dir); 262*2140Seric strcat(buf, "/source"); 263*2140Seric if (access(buf, 0) < 0) 264*2140Seric { 265*2140Seric usrerr("project %s has no source!", p); 266*2140Seric exit(EX_USAGE); 267*2140Seric } 268*2140Seric } 269*2140Seric SccsDir = buf; 270*2140Seric } 271*2140Seric } 2722139Seric # endif SCCSDIR 2732139Seric # endif V6 2742139Seric 275148Seric /* 276148Seric ** Detect and decode flags intended for this program. 277148Seric */ 278148Seric 279200Seric if (argc < 2) 280148Seric { 2811205Seric fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); 282200Seric exit(EX_USAGE); 283200Seric } 284200Seric argv[argc] = NULL; 285200Seric 286262Seric if (lookup(argv[0]) == NULL) 287200Seric { 288262Seric while ((p = *++argv) != NULL) 289148Seric { 290262Seric if (*p != '-') 291262Seric break; 292262Seric switch (*++p) 293262Seric { 294262Seric case 'r': /* run as real user */ 295262Seric setuid(getuid()); 296262Seric RealUser++; 297262Seric break; 298148Seric 2991270Seric # ifndef SCCSDIR 300262Seric case 'p': /* path of sccs files */ 301262Seric SccsPath = ++p; 302262Seric break; 303148Seric 304588Seric case 'd': /* directory to search from */ 305588Seric SccsDir = ++p; 306588Seric break; 3071205Seric # endif 308588Seric 309393Seric # ifdef DEBUG 310393Seric case 'T': /* trace */ 311393Seric Debug++; 312393Seric break; 313393Seric # endif 314393Seric 315262Seric default: 3161205Seric usrerr("unknown option -%s", p); 317262Seric break; 318262Seric } 319148Seric } 320262Seric if (SccsPath[0] == '\0') 321262Seric SccsPath = "."; 322148Seric } 323148Seric 3241737Seric i = command(argv, FALSE, ""); 3251282Seric exit(i); 326200Seric } 3271432Seric 3281432Seric /* 3291282Seric ** COMMAND -- look up and perform a command 3301282Seric ** 3311282Seric ** This routine is the guts of this program. Given an 3321282Seric ** argument vector, it looks up the "command" (argv[0]) 3331282Seric ** in the configuration table and does the necessary stuff. 3341282Seric ** 3351282Seric ** Parameters: 3361282Seric ** argv -- an argument vector to process. 3371282Seric ** forkflag -- if set, fork before executing the command. 3381316Seric ** editflag -- if set, only include flags listed in the 3391316Seric ** sccsklets field of the command descriptor. 3401316Seric ** arg0 -- a space-seperated list of arguments to insert 3411316Seric ** before argv. 3421282Seric ** 3431282Seric ** Returns: 3441282Seric ** zero -- command executed ok. 3451282Seric ** else -- error status. 3461282Seric ** 3471282Seric ** Side Effects: 3481282Seric ** none. 3491282Seric */ 350157Seric 3511737Seric command(argv, forkflag, arg0) 352200Seric char **argv; 353201Seric bool forkflag; 3541316Seric char *arg0; 355200Seric { 356200Seric register struct sccsprog *cmd; 357200Seric register char *p; 358201Seric char buf[40]; 359262Seric extern struct sccsprog *lookup(); 3601316Seric char *nav[1000]; 3611316Seric char **np; 3621431Seric register char **ap; 363585Seric register int i; 3641431Seric register char *q; 365585Seric extern bool unedit(); 3661282Seric int rval = 0; 3671316Seric extern char *index(); 3681316Seric extern char *makefile(); 3691737Seric char *editchs; 3701435Seric extern char *tail(); 371200Seric 372393Seric # ifdef DEBUG 373393Seric if (Debug) 374393Seric { 3751316Seric printf("command:\n\t\"%s\"\n", arg0); 3761316Seric for (np = argv; *np != NULL; np++) 3771316Seric printf("\t\"%s\"\n", *np); 378393Seric } 379393Seric # endif 380393Seric 381157Seric /* 3821316Seric ** Copy arguments. 3831438Seric ** Copy from arg0 & if necessary at most one arg 3841438Seric ** from argv[0]. 3851316Seric */ 3861316Seric 3871431Seric np = ap = &nav[1]; 3881737Seric editchs = NULL; 3891821Seric for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) 3901316Seric { 3911316Seric *np++ = q; 3921316Seric while (*p == ' ') 3931316Seric p++; 3941737Seric while (*p != ' ' && *p != '\0' && *p != '/' && *p != ':') 3951316Seric *q++ = *p++; 3961316Seric *q++ = '\0'; 3971737Seric if (*p == ':') 3981737Seric { 3991737Seric editchs = q; 4001821Seric while (*++p != '\0' && *p != '/' && *p != ' ') 4011737Seric *q++ = *p; 4021737Seric *q++ = '\0'; 4031737Seric } 4041316Seric } 4051316Seric *np = NULL; 4061431Seric if (*ap == NULL) 4071316Seric *np++ = *argv++; 4081316Seric 4091316Seric /* 410148Seric ** Look up command. 4111431Seric ** At this point, *ap is the command name. 412148Seric */ 413148Seric 4141431Seric cmd = lookup(*ap); 415262Seric if (cmd == NULL) 416148Seric { 4171431Seric usrerr("Unknown command \"%s\"", *ap); 4181282Seric return (EX_USAGE); 419148Seric } 420148Seric 421148Seric /* 4221316Seric ** Copy remaining arguments doing editing as appropriate. 4231316Seric */ 4241316Seric 4251316Seric for (; *argv != NULL; argv++) 4261316Seric { 4271316Seric p = *argv; 4281316Seric if (*p == '-') 4291316Seric { 4301737Seric if (p[1] == '\0' || editchs == NULL || index(editchs, p[1]) != NULL) 4311316Seric *np++ = p; 4321316Seric } 4331316Seric else 4341316Seric { 4351316Seric if (!bitset(NO_SDOT, cmd->sccsflags)) 4361316Seric p = makefile(p); 4371316Seric if (p != NULL) 4381316Seric *np++ = p; 4391316Seric } 4401316Seric } 4411316Seric *np = NULL; 4421316Seric 4431316Seric /* 444200Seric ** Interpret operation associated with this command. 445157Seric */ 446157Seric 447200Seric switch (cmd->sccsoper) 448200Seric { 4491431Seric case SHELL: /* call a shell file */ 4501431Seric *ap = cmd->sccspath; 4511431Seric *--ap = "sh"; 4521431Seric rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag); 4531431Seric break; 4541431Seric 455200Seric case PROG: /* call an sccs prog */ 4561431Seric rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); 457201Seric break; 458201Seric 459201Seric case CMACRO: /* command macro */ 4601438Seric /* step through & execute each part of the macro */ 461201Seric for (p = cmd->sccspath; *p != '\0'; p++) 462201Seric { 4631316Seric q = p; 4641316Seric while (*p != '\0' && *p != '/') 4651316Seric p++; 4661737Seric rval = command(&ap[1], *p != '\0', q); 4671282Seric if (rval != 0) 4681282Seric break; 469201Seric } 4701282Seric break; 471157Seric 472226Seric case FIX: /* fix a delta */ 4731431Seric if (strncmp(ap[1], "-r", 2) != 0) 474226Seric { 4751205Seric usrerr("-r flag needed for fix command"); 4761282Seric rval = EX_USAGE; 477226Seric break; 478226Seric } 4791438Seric 4801438Seric /* get the version with all changes */ 4811737Seric rval = command(&ap[1], TRUE, "get -k"); 4821438Seric 4831438Seric /* now remove that version from the s-file */ 4841282Seric if (rval == 0) 4851737Seric rval = command(&ap[1], TRUE, "rmdel:r"); 4861438Seric 4871438Seric /* and edit the old version (but don't clobber new vers) */ 4881282Seric if (rval == 0) 4891737Seric rval = command(&ap[2], FALSE, "get -e -g"); 4901282Seric break; 491226Seric 492261Seric case CLEAN: 4931822Seric rval = clean((int) cmd->sccspath, ap); 494261Seric break; 495261Seric 496396Seric case UNEDIT: 4971431Seric for (argv = np = &ap[1]; *argv != NULL; argv++) 498585Seric { 4991316Seric if (unedit(*argv)) 5001316Seric *np++ = *argv; 501585Seric } 5021316Seric *np = NULL; 5031438Seric 5041438Seric /* get all the files that we unedited successfully */ 5051738Seric if (np > &ap[1]) 5061737Seric rval = command(&ap[1], FALSE, "get"); 507396Seric break; 508396Seric 5091433Seric case DIFFS: /* diff between s-file & edit file */ 5101433Seric /* find the end of the flag arguments */ 5111433Seric for (np = &ap[1]; *np != NULL && **np == '-'; np++) 5121433Seric continue; 5131433Seric argv = np; 5141433Seric 5151433Seric /* for each file, do the diff */ 5161502Seric p = argv[1]; 5171433Seric while (*np != NULL) 5181433Seric { 5191438Seric /* messy, but we need a null terminated argv */ 5201433Seric *argv = *np++; 5211502Seric argv[1] = NULL; 5221435Seric i = dodiff(ap, tail(*argv)); 5231433Seric if (rval == 0) 5241433Seric rval = i; 5251502Seric argv[1] = p; 5261433Seric } 5271433Seric break; 5281433Seric 5291871Seric case DODIFF: /* internal diff call */ 5301871Seric setuid(getuid()); 5311871Seric for (np = ap; *np != NULL; np++) 5321871Seric { 5331871Seric if ((*np)[0] == '-' && (*np)[1] == 'C') 5341871Seric (*np)[1] = 'c'; 5351871Seric } 5361871Seric 5371871Seric /* insert "-" argument */ 5381871Seric np[1] = NULL; 5391871Seric np[0] = np[-1]; 5401871Seric np[-1] = "-"; 5411871Seric 5421871Seric /* execute the diff program of choice */ 5431871Seric # ifndef V6 5441871Seric execvp("diff", ap); 5451871Seric # endif 5461871Seric execv(cmd->sccspath, argv); 5471871Seric syserr("cannot exec %s", cmd->sccspath); 5481871Seric exit(EX_OSERR); 5491871Seric 550200Seric default: 5511205Seric syserr("oper %d", cmd->sccsoper); 552200Seric exit(EX_SOFTWARE); 553200Seric } 5541282Seric # ifdef DEBUG 5551282Seric if (Debug) 5561282Seric printf("command: rval=%d\n", rval); 5571282Seric # endif 5581282Seric return (rval); 559200Seric } 5601432Seric 5611432Seric /* 562262Seric ** LOOKUP -- look up an SCCS command name. 563262Seric ** 564262Seric ** Parameters: 565262Seric ** name -- the name of the command to look up. 566262Seric ** 567262Seric ** Returns: 568262Seric ** ptr to command descriptor for this command. 569262Seric ** NULL if no such entry. 570262Seric ** 571262Seric ** Side Effects: 572262Seric ** none. 573262Seric */ 574200Seric 575262Seric struct sccsprog * 576262Seric lookup(name) 577262Seric char *name; 578262Seric { 579262Seric register struct sccsprog *cmd; 580226Seric 581262Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 582262Seric { 583262Seric if (strcmp(cmd->sccsname, name) == 0) 584262Seric return (cmd); 585262Seric } 586262Seric return (NULL); 587262Seric } 5881432Seric 5891432Seric /* 5901282Seric ** CALLPROG -- call a program 5911282Seric ** 5921316Seric ** Used to call the SCCS programs. 5931282Seric ** 5941282Seric ** Parameters: 5951282Seric ** progpath -- pathname of the program to call. 5961282Seric ** flags -- status flags from the command descriptors. 5971282Seric ** argv -- an argument vector to pass to the program. 5981282Seric ** forkflag -- if true, fork before calling, else just 5991282Seric ** exec. 6001282Seric ** 6011282Seric ** Returns: 6021282Seric ** The exit status of the program. 6031282Seric ** Nothing if forkflag == FALSE. 6041282Seric ** 6051282Seric ** Side Effects: 6061282Seric ** Can exit if forkflag == FALSE. 6071282Seric */ 608226Seric 609200Seric callprog(progpath, flags, argv, forkflag) 610200Seric char *progpath; 611200Seric short flags; 612200Seric char **argv; 613200Seric bool forkflag; 614200Seric { 615200Seric register int i; 616201Seric auto int st; 617200Seric 6181316Seric # ifdef DEBUG 6191316Seric if (Debug) 6201316Seric { 6211316Seric printf("callprog:\n"); 6221316Seric for (i = 0; argv[i] != NULL; i++) 6231316Seric printf("\t\"%s\"\n", argv[i]); 6241316Seric } 6251316Seric # endif 6261316Seric 627200Seric if (*argv == NULL) 628200Seric return (-1); 629200Seric 630157Seric /* 631226Seric ** Fork if appropriate. 632148Seric */ 633148Seric 634200Seric if (forkflag) 635200Seric { 636393Seric # ifdef DEBUG 637393Seric if (Debug) 638393Seric printf("Forking\n"); 639393Seric # endif 640200Seric i = fork(); 641200Seric if (i < 0) 642200Seric { 6431205Seric syserr("cannot fork"); 644200Seric exit(EX_OSERR); 645200Seric } 646200Seric else if (i > 0) 647201Seric { 648201Seric wait(&st); 6491282Seric if ((st & 0377) == 0) 6501282Seric st = (st >> 8) & 0377; 6511433Seric if (OutFile >= 0) 6521433Seric { 6531433Seric close(OutFile); 6541433Seric OutFile = -1; 6551433Seric } 656201Seric return (st); 657201Seric } 658200Seric } 6591433Seric else if (OutFile >= 0) 6601433Seric { 6611433Seric syserr("callprog: setting stdout w/o forking"); 6621433Seric exit(EX_SOFTWARE); 6631433Seric } 664200Seric 6651433Seric /* set protection as appropriate */ 666200Seric if (bitset(REALUSER, flags)) 667200Seric setuid(getuid()); 6681433Seric 6691433Seric /* change standard input & output if needed */ 6701433Seric if (OutFile >= 0) 6711433Seric { 6721433Seric close(1); 6731433Seric dup(OutFile); 6741433Seric close(OutFile); 6751433Seric } 676226Seric 6771433Seric /* call real SCCS program */ 678226Seric execv(progpath, argv); 6791205Seric syserr("cannot execute %s", progpath); 680148Seric exit(EX_UNAVAILABLE); 6811738Seric /*NOTREACHED*/ 682148Seric } 6831432Seric 6841432Seric /* 685586Seric ** MAKEFILE -- make filename of SCCS file 686586Seric ** 687586Seric ** If the name passed is already the name of an SCCS file, 688586Seric ** just return it. Otherwise, munge the name into the name 689586Seric ** of the actual SCCS file. 690586Seric ** 691586Seric ** There are cases when it is not clear what you want to 692586Seric ** do. For example, if SccsPath is an absolute pathname 693586Seric ** and the name given is also an absolute pathname, we go 694586Seric ** for SccsPath (& only use the last component of the name 695586Seric ** passed) -- this is important for security reasons (if 696586Seric ** sccs is being used as a setuid front end), but not 697586Seric ** particularly intuitive. 698586Seric ** 699586Seric ** Parameters: 700586Seric ** name -- the file name to be munged. 701586Seric ** 702586Seric ** Returns: 703586Seric ** The pathname of the sccs file. 704586Seric ** NULL on error. 705586Seric ** 706586Seric ** Side Effects: 707586Seric ** none. 708586Seric */ 709148Seric 710148Seric char * 711148Seric makefile(name) 712148Seric char *name; 713148Seric { 714148Seric register char *p; 715148Seric char buf[512]; 716148Seric extern char *malloc(); 717586Seric extern char *rindex(); 718588Seric extern bool safepath(); 719587Seric extern bool isdir(); 720587Seric register char *q; 721148Seric 722586Seric p = rindex(name, '/'); 723586Seric if (p == NULL) 724586Seric p = name; 725586Seric else 726586Seric p++; 727586Seric 728148Seric /* 729588Seric ** Check to see that the path is "safe", i.e., that we 730588Seric ** are not letting some nasty person use the setuid part 731588Seric ** of this program to look at or munge some presumably 732588Seric ** hidden files. 733148Seric */ 734148Seric 735588Seric if (SccsDir[0] == '/' && !safepath(name)) 736588Seric return (NULL); 737586Seric 738586Seric /* 739588Seric ** Create the base pathname. 740586Seric */ 741586Seric 7421438Seric /* first the directory part */ 743588Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) 744148Seric { 745588Seric strcpy(buf, SccsDir); 746586Seric strcat(buf, "/"); 747586Seric } 748586Seric else 749586Seric strcpy(buf, ""); 7501438Seric 7511438Seric /* then the head of the pathname */ 752587Seric strncat(buf, name, p - name); 753587Seric q = &buf[strlen(buf)]; 7541438Seric 7551438Seric /* now copy the final part of the name, in case useful */ 756587Seric strcpy(q, p); 7571438Seric 7581438Seric /* so is it useful? */ 759587Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 760586Seric { 7611438Seric /* sorry, no; copy the SCCS pathname & the "s." */ 762588Seric strcpy(q, SccsPath); 763588Seric strcat(buf, "/s."); 7641438Seric 7651438Seric /* and now the end of the name */ 766586Seric strcat(buf, p); 767586Seric } 768148Seric 7691438Seric /* if i haven't changed it, why did I do all this? */ 770588Seric if (strcmp(buf, name) == 0) 771588Seric p = name; 772588Seric else 773148Seric { 7741438Seric /* but if I have, squirrel it away */ 775588Seric p = malloc(strlen(buf) + 1); 776588Seric if (p == NULL) 777588Seric { 778588Seric perror("Sccs: no mem"); 779588Seric exit(EX_OSERR); 780588Seric } 781588Seric strcpy(p, buf); 782148Seric } 7831438Seric 784148Seric return (p); 785148Seric } 7861432Seric 7871432Seric /* 788587Seric ** ISDIR -- return true if the argument is a directory. 789587Seric ** 790587Seric ** Parameters: 791587Seric ** name -- the pathname of the file to check. 792587Seric ** 793587Seric ** Returns: 794587Seric ** TRUE if 'name' is a directory, FALSE otherwise. 795587Seric ** 796587Seric ** Side Effects: 797587Seric ** none. 798587Seric */ 799587Seric 800587Seric bool 801587Seric isdir(name) 802587Seric char *name; 803587Seric { 804587Seric struct stat stbuf; 805587Seric 806587Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 807587Seric } 8081432Seric 8091432Seric /* 810586Seric ** SAFEPATH -- determine whether a pathname is "safe" 811586Seric ** 812586Seric ** "Safe" pathnames only allow you to get deeper into the 813586Seric ** directory structure, i.e., full pathnames and ".." are 814586Seric ** not allowed. 815586Seric ** 816586Seric ** Parameters: 817586Seric ** p -- the name to check. 818586Seric ** 819586Seric ** Returns: 820586Seric ** TRUE -- if the path is safe. 821586Seric ** FALSE -- if the path is not safe. 822586Seric ** 823586Seric ** Side Effects: 824586Seric ** Prints a message if the path is not safe. 825586Seric */ 826586Seric 827586Seric bool 828586Seric safepath(p) 829586Seric register char *p; 830586Seric { 831586Seric extern char *index(); 832586Seric 833586Seric if (*p != '/') 834586Seric { 835586Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 836586Seric { 837586Seric p = index(p, '/'); 838586Seric if (p == NULL) 839586Seric return (TRUE); 840586Seric p++; 841586Seric } 842586Seric } 843586Seric 844586Seric printf("You may not use full pathnames or \"..\"\n"); 845586Seric return (FALSE); 846586Seric } 8471432Seric 8481432Seric /* 849261Seric ** CLEAN -- clean out recreatable files 850261Seric ** 851261Seric ** Any file for which an "s." file exists but no "p." file 852261Seric ** exists in the current directory is purged. 853261Seric ** 854261Seric ** Parameters: 8551822Seric ** mode -- tells whether this came from a "clean", "info", or 8561822Seric ** "check" command. 8571822Seric ** argv -- the rest of the argument vector. 858261Seric ** 859261Seric ** Returns: 860261Seric ** none. 861261Seric ** 862261Seric ** Side Effects: 863819Seric ** Removes files in the current directory. 864819Seric ** Prints information regarding files being edited. 865819Seric ** Exits if a "check" command. 866261Seric */ 867261Seric 8681822Seric clean(mode, argv) 869819Seric int mode; 8701822Seric char **argv; 871261Seric { 872261Seric struct direct dir; 873261Seric char buf[100]; 874*2140Seric char *bufend; 875346Seric register FILE *dirfd; 876346Seric register char *basefile; 877351Seric bool gotedit; 8781822Seric bool gotpfent; 879394Seric FILE *pfp; 8801822Seric bool nobranch = FALSE; 8811822Seric extern struct pfile *getpfent(); 8821822Seric register struct pfile *pf; 8831822Seric register char **ap; 8841864Seric extern char *username(); 8851864Seric char *usernm = NULL; 886*2140Seric char *subdir = NULL; 887*2140Seric char *cmdname; 888261Seric 8891438Seric /* 8901822Seric ** Process the argv 8911822Seric */ 8921822Seric 893*2140Seric cmdname = *argv; 894*2140Seric for (ap = argv; *++ap != NULL; ) 8951822Seric { 8961864Seric if (**ap == '-') 8971864Seric { 8981864Seric /* we have a flag */ 8991864Seric switch ((*ap)[1]) 9001864Seric { 9011864Seric case 'b': 9021864Seric nobranch = TRUE; 9031864Seric break; 9041864Seric 9051864Seric case 'u': 9061864Seric if ((*ap)[2] != '\0') 9071864Seric usernm = &(*ap)[2]; 9081864Seric else if (ap[1] != NULL && ap[1][0] != '-') 9091864Seric usernm = *++ap; 9101864Seric else 9111864Seric usernm = username(); 9121864Seric break; 9131864Seric } 9141864Seric } 915*2140Seric else 916*2140Seric { 917*2140Seric if (subdir != NULL) 918*2140Seric usrerr("too many args"); 919*2140Seric else 920*2140Seric subdir = *ap; 921*2140Seric } 9221822Seric } 9231822Seric 9241822Seric /* 9251438Seric ** Find and open the SCCS directory. 9261438Seric */ 9271438Seric 9281207Seric strcpy(buf, SccsDir); 9291207Seric if (buf[0] != '\0') 9301207Seric strcat(buf, "/"); 931*2140Seric if (subdir != NULL) 932*2140Seric { 933*2140Seric strcat(buf, subdir); 934*2140Seric strcat(buf, "/"); 935*2140Seric } 9361207Seric strcat(buf, SccsPath); 937*2140Seric bufend = &buf[strlen(buf)]; 9381438Seric 9391207Seric dirfd = fopen(buf, "r"); 940261Seric if (dirfd == NULL) 941261Seric { 9421207Seric usrerr("cannot open %s", buf); 9431282Seric return (EX_NOINPUT); 944261Seric } 945261Seric 946261Seric /* 947261Seric ** Scan the SCCS directory looking for s. files. 9481438Seric ** gotedit tells whether we have tried to clean any 9491438Seric ** files that are being edited. 950261Seric */ 951261Seric 952351Seric gotedit = FALSE; 9531738Seric while (fread((char *)&dir, sizeof dir, 1, dirfd) != NULL) 954261Seric { 955568Seric if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) 956261Seric continue; 957261Seric 958261Seric /* got an s. file -- see if the p. file exists */ 959*2140Seric strcpy(bufend, "/p."); 960*2140Seric basefile = bufend + 3; 961568Seric strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 962346Seric basefile[sizeof dir.d_name - 2] = '\0'; 9631822Seric 9641822Seric /* 9651822Seric ** open and scan the p-file. 9661822Seric ** 'gotpfent' tells if we have found a valid p-file 9671822Seric ** entry. 9681822Seric */ 9691822Seric 970394Seric pfp = fopen(buf, "r"); 9711822Seric gotpfent = FALSE; 972394Seric if (pfp != NULL) 973346Seric { 9741438Seric /* the file exists -- report it's contents */ 9751822Seric while ((pf = getpfent(pfp)) != NULL) 9761730Seric { 9771822Seric if (nobranch && isbranch(pf->p_nsid)) 9781822Seric continue; 9791864Seric if (usernm != NULL && strcmp(usernm, pf->p_user) != 0 && mode != CLEANC) 9801864Seric continue; 9811822Seric gotedit = TRUE; 9821822Seric gotpfent = TRUE; 9831822Seric if (mode == TELLC) 9841822Seric { 9851822Seric printf("%s\n", basefile); 9861822Seric break; 9871822Seric } 9881822Seric printf("%12s: being edited: %s %s %s %s %s\n", 9891822Seric basefile, pf->p_osid, pf->p_nsid, 9901822Seric pf->p_user, pf->p_date, pf->p_time); 9911730Seric } 992394Seric fclose(pfp); 9931822Seric } 994261Seric 995261Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 9961870Seric if (mode == CLEANC && !gotpfent) 997346Seric { 998568Seric strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); 999346Seric buf[sizeof dir.d_name - 2] = '\0'; 1000346Seric unlink(buf); 1001346Seric } 1002261Seric } 1003261Seric 10041438Seric /* cleanup & report results */ 1005261Seric fclose(dirfd); 1006819Seric if (!gotedit && mode == INFOC) 10071864Seric { 10081864Seric printf("Nothing being edited"); 10091864Seric if (nobranch) 10101864Seric printf(" (on trunk)"); 10111864Seric if (usernm == NULL) 10121864Seric printf("\n"); 10131864Seric else 10141864Seric printf(" by %s\n", usernm); 10151864Seric } 1016819Seric if (mode == CHECKC) 1017819Seric exit(gotedit); 10181282Seric return (EX_OK); 1019261Seric } 10201432Seric 10211432Seric /* 10221822Seric ** ISBRANCH -- is the SID a branch? 10231822Seric ** 10241822Seric ** Parameters: 10251822Seric ** sid -- the sid to check. 10261822Seric ** 10271822Seric ** Returns: 10281822Seric ** TRUE if the sid represents a branch. 10291822Seric ** FALSE otherwise. 10301822Seric ** 10311822Seric ** Side Effects: 10321822Seric ** none. 10331822Seric */ 10341822Seric 10351822Seric isbranch(sid) 10361822Seric char *sid; 10371822Seric { 10381822Seric register char *p; 10391822Seric int dots; 10401822Seric 10411822Seric dots = 0; 10421822Seric for (p = sid; *p != '\0'; p++) 10431822Seric { 10441822Seric if (*p == '.') 10451822Seric dots++; 10461822Seric if (dots > 1) 10471822Seric return (TRUE); 10481822Seric } 10491822Seric return (FALSE); 10501822Seric } 10511822Seric 10521822Seric /* 1053396Seric ** UNEDIT -- unedit a file 1054396Seric ** 1055396Seric ** Checks to see that the current user is actually editting 1056396Seric ** the file and arranges that s/he is not editting it. 1057396Seric ** 1058396Seric ** Parameters: 1059416Seric ** fn -- the name of the file to be unedited. 1060396Seric ** 1061396Seric ** Returns: 1062585Seric ** TRUE -- if the file was successfully unedited. 1063585Seric ** FALSE -- if the file was not unedited for some 1064585Seric ** reason. 1065396Seric ** 1066396Seric ** Side Effects: 1067396Seric ** fn is removed 1068396Seric ** entries are removed from pfile. 1069396Seric */ 1070396Seric 1071585Seric bool 1072396Seric unedit(fn) 1073396Seric char *fn; 1074396Seric { 1075396Seric register FILE *pfp; 1076396Seric char *pfn; 1077396Seric static char tfn[] = "/tmp/sccsXXXXX"; 1078396Seric FILE *tfp; 1079396Seric register char *q; 1080396Seric bool delete = FALSE; 1081396Seric bool others = FALSE; 1082396Seric char *myname; 10831864Seric extern char *username(); 1084396Seric struct pfile *pent; 10851822Seric extern struct pfile *getpfent(); 1086396Seric char buf[120]; 10871316Seric extern char *makefile(); 1088396Seric 1089396Seric /* make "s." filename & find the trailing component */ 1090396Seric pfn = makefile(fn); 1091586Seric if (pfn == NULL) 1092586Seric return (FALSE); 1093586Seric q = rindex(pfn, '/'); 1094586Seric if (q == NULL) 1095586Seric q = &pfn[-1]; 1096586Seric if (q[1] != 's' || q[2] != '.') 1097396Seric { 10981205Seric usrerr("bad file name \"%s\"", fn); 1099585Seric return (FALSE); 1100396Seric } 1101396Seric 11021438Seric /* turn "s." into "p." & try to open it */ 1103396Seric *++q = 'p'; 1104396Seric 1105396Seric pfp = fopen(pfn, "r"); 1106396Seric if (pfp == NULL) 1107396Seric { 1108416Seric printf("%12s: not being edited\n", fn); 1109585Seric return (FALSE); 1110396Seric } 1111396Seric 11121438Seric /* create temp file for editing p-file */ 1113396Seric mktemp(tfn); 1114396Seric tfp = fopen(tfn, "w"); 1115396Seric if (tfp == NULL) 1116396Seric { 11171205Seric usrerr("cannot create \"%s\"", tfn); 1118396Seric exit(EX_OSERR); 1119396Seric } 1120396Seric 11211438Seric /* figure out who I am */ 11221864Seric myname = username(); 11231438Seric 11241438Seric /* 11251438Seric ** Copy p-file to temp file, doing deletions as needed. 11261438Seric */ 11271438Seric 11281822Seric while ((pent = getpfent(pfp)) != NULL) 1129396Seric { 1130396Seric if (strcmp(pent->p_user, myname) == 0) 1131396Seric { 1132396Seric /* a match */ 1133396Seric delete++; 1134396Seric } 1135396Seric else 1136396Seric { 11371438Seric /* output it again */ 1138396Seric fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, 1139396Seric pent->p_nsid, pent->p_user, pent->p_date, 1140396Seric pent->p_time); 1141396Seric others++; 1142396Seric } 1143396Seric } 1144396Seric 1145396Seric /* do final cleanup */ 1146396Seric if (others) 1147396Seric { 11481438Seric /* copy it back (perhaps it should be linked?) */ 1149396Seric if (freopen(tfn, "r", tfp) == NULL) 1150396Seric { 11511205Seric syserr("cannot reopen \"%s\"", tfn); 1152396Seric exit(EX_OSERR); 1153396Seric } 1154396Seric if (freopen(pfn, "w", pfp) == NULL) 1155396Seric { 11561205Seric usrerr("cannot create \"%s\"", pfn); 1157585Seric return (FALSE); 1158396Seric } 1159396Seric while (fgets(buf, sizeof buf, tfp) != NULL) 1160396Seric fputs(buf, pfp); 1161396Seric } 1162396Seric else 1163396Seric { 11641438Seric /* it's empty -- remove it */ 1165396Seric unlink(pfn); 1166396Seric } 1167396Seric fclose(tfp); 1168396Seric fclose(pfp); 1169396Seric unlink(tfn); 1170396Seric 11711438Seric /* actually remove the g-file */ 1172396Seric if (delete) 1173396Seric { 11741435Seric unlink(tail(fn)); 11751435Seric printf("%12s: removed\n", tail(fn)); 1176585Seric return (TRUE); 1177396Seric } 1178396Seric else 1179396Seric { 1180416Seric printf("%12s: not being edited by you\n", fn); 1181585Seric return (FALSE); 1182396Seric } 1183396Seric } 11841432Seric 11851432Seric /* 11861433Seric ** DODIFF -- diff an s-file against a g-file 11871433Seric ** 11881433Seric ** Parameters: 11891433Seric ** getv -- argv for the 'get' command. 11901433Seric ** gfile -- name of the g-file to diff against. 11911433Seric ** 11921433Seric ** Returns: 11931433Seric ** Result of get. 11941433Seric ** 11951433Seric ** Side Effects: 11961433Seric ** none. 11971433Seric */ 11981433Seric 11991433Seric dodiff(getv, gfile) 12001433Seric char **getv; 12011433Seric char *gfile; 12021433Seric { 12031433Seric int pipev[2]; 12041433Seric int rval; 12051433Seric register int i; 12061433Seric register int pid; 12071433Seric auto int st; 12081433Seric extern int errno; 12091433Seric int (*osig)(); 12101433Seric 12111905Seric printf("\n------- %s -------\n", gfile); 12121871Seric fflush(stdout); 12131501Seric 12141438Seric /* create context for diff to run in */ 12151433Seric if (pipe(pipev) < 0) 12161433Seric { 12171433Seric syserr("dodiff: pipe failed"); 12181433Seric exit(EX_OSERR); 12191433Seric } 12201433Seric if ((pid = fork()) < 0) 12211433Seric { 12221433Seric syserr("dodiff: fork failed"); 12231433Seric exit(EX_OSERR); 12241433Seric } 12251433Seric else if (pid > 0) 12261433Seric { 12271433Seric /* in parent; run get */ 12281433Seric OutFile = pipev[1]; 12291433Seric close(pipev[0]); 12301871Seric rval = command(&getv[1], TRUE, "get:rcixt -s -k -p"); 12311433Seric osig = signal(SIGINT, SIG_IGN); 12321433Seric while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR) 12331433Seric errno = 0; 12341433Seric signal(SIGINT, osig); 12351433Seric /* ignore result of diff */ 12361433Seric } 12371433Seric else 12381433Seric { 12391433Seric /* in child, run diff */ 12401433Seric if (close(pipev[1]) < 0 || close(0) < 0 || 12411433Seric dup(pipev[0]) != 0 || close(pipev[0]) < 0) 12421433Seric { 12431433Seric syserr("dodiff: magic failed"); 12441433Seric exit(EX_OSERR); 12451433Seric } 12461871Seric command(&getv[1], FALSE, "-diff:elsfhbC"); 12471433Seric } 12481433Seric return (rval); 12491433Seric } 12501433Seric 12511433Seric /* 12521435Seric ** TAIL -- return tail of filename. 12531435Seric ** 12541435Seric ** Parameters: 12551435Seric ** fn -- the filename. 12561435Seric ** 12571435Seric ** Returns: 12581435Seric ** a pointer to the tail of the filename; e.g., given 12591435Seric ** "cmd/ls.c", "ls.c" is returned. 12601435Seric ** 12611435Seric ** Side Effects: 12621435Seric ** none. 12631435Seric */ 12641435Seric 12651435Seric char * 12661435Seric tail(fn) 12671435Seric register char *fn; 12681435Seric { 12691435Seric register char *p; 12701435Seric 12711435Seric for (p = fn; *p != 0; p++) 12721435Seric if (*p == '/' && p[1] != '\0' && p[1] != '/') 12731435Seric fn = &p[1]; 12741435Seric return (fn); 12751435Seric } 12761435Seric 12771435Seric /* 12781822Seric ** GETPFENT -- get an entry from the p-file 1279396Seric ** 1280396Seric ** Parameters: 1281396Seric ** pfp -- p-file file pointer 1282396Seric ** 1283396Seric ** Returns: 1284396Seric ** pointer to p-file struct for next entry 1285396Seric ** NULL on EOF or error 1286396Seric ** 1287396Seric ** Side Effects: 1288396Seric ** Each call wipes out results of previous call. 1289396Seric */ 1290396Seric 1291396Seric struct pfile * 12921822Seric getpfent(pfp) 1293396Seric FILE *pfp; 1294396Seric { 1295396Seric static struct pfile ent; 1296396Seric static char buf[120]; 1297396Seric register char *p; 1298396Seric extern char *nextfield(); 1299396Seric 1300396Seric if (fgets(buf, sizeof buf, pfp) == NULL) 1301396Seric return (NULL); 1302396Seric 1303396Seric ent.p_osid = p = buf; 1304396Seric ent.p_nsid = p = nextfield(p); 1305396Seric ent.p_user = p = nextfield(p); 1306396Seric ent.p_date = p = nextfield(p); 1307396Seric ent.p_time = p = nextfield(p); 1308396Seric if (p == NULL || nextfield(p) != NULL) 1309396Seric return (NULL); 1310396Seric 1311396Seric return (&ent); 1312396Seric } 1313396Seric 1314396Seric 1315396Seric char * 1316396Seric nextfield(p) 1317396Seric register char *p; 1318396Seric { 1319396Seric if (p == NULL || *p == '\0') 1320396Seric return (NULL); 1321396Seric while (*p != ' ' && *p != '\n' && *p != '\0') 1322396Seric p++; 1323396Seric if (*p == '\n' || *p == '\0') 1324396Seric { 1325396Seric *p = '\0'; 1326396Seric return (NULL); 1327396Seric } 1328396Seric *p++ = '\0'; 1329396Seric return (p); 1330396Seric } 13311432Seric 13321432Seric /* 13331205Seric ** USRERR -- issue user-level error 13341205Seric ** 13351205Seric ** Parameters: 13361205Seric ** f -- format string. 13371205Seric ** p1-p3 -- parameters to a printf. 13381205Seric ** 13391205Seric ** Returns: 13401205Seric ** -1 13411205Seric ** 13421205Seric ** Side Effects: 13431205Seric ** none. 13441205Seric */ 13451205Seric 13461738Seric /*VARARGS1*/ 13471205Seric usrerr(f, p1, p2, p3) 13481205Seric char *f; 13491205Seric { 13501205Seric fprintf(stderr, "\n%s: ", MyName); 13511205Seric fprintf(stderr, f, p1, p2, p3); 13521205Seric fprintf(stderr, "\n"); 13531205Seric 13541205Seric return (-1); 13551205Seric } 13561432Seric 13571432Seric /* 13581205Seric ** SYSERR -- print system-generated error. 13591205Seric ** 13601205Seric ** Parameters: 13611205Seric ** f -- format string to a printf. 13621205Seric ** p1, p2, p3 -- parameters to f. 13631205Seric ** 13641205Seric ** Returns: 13651205Seric ** never. 13661205Seric ** 13671205Seric ** Side Effects: 13681205Seric ** none. 13691205Seric */ 13701205Seric 13711738Seric /*VARARGS1*/ 13721205Seric syserr(f, p1, p2, p3) 13731205Seric char *f; 13741205Seric { 13751205Seric extern int errno; 13761205Seric 13771205Seric fprintf(stderr, "\n%s SYSERR: ", MyName); 13781205Seric fprintf(stderr, f, p1, p2, p3); 13791205Seric fprintf(stderr, "\n"); 13801205Seric if (errno == 0) 13811205Seric exit(EX_SOFTWARE); 13821205Seric else 13831205Seric { 13841738Seric perror(NULL); 13851205Seric exit(EX_OSERR); 13861205Seric } 13871205Seric } 13881864Seric /* 13891864Seric ** USERNAME -- return name of the current user 13901864Seric ** 13911864Seric ** Parameters: 13921864Seric ** none 13931864Seric ** 13941864Seric ** Returns: 13951864Seric ** name of current user 13961864Seric ** 13971864Seric ** Side Effects: 13981864Seric ** none 13991864Seric */ 14001864Seric 14011864Seric char * 14021864Seric username() 14031864Seric { 14041864Seric # ifdef UIDUSER 14051864Seric extern struct passwd *getpwuid(); 14061864Seric register struct passwd *pw; 14071864Seric 14081864Seric pw = getpwuid(getuid()); 14091864Seric if (pw == NULL) 14101864Seric { 14111864Seric syserr("who are you? (uid=%d)", getuid()); 14121864Seric exit(EX_OSERR); 14131864Seric } 14141864Seric return (pw->pw_name); 14151864Seric # else 14161905Seric extern char *getlogin(); 14171905Seric 14181864Seric return (getlogin()); 14191864Seric # endif UIDUSER 14201864Seric } 1421