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> 9148Seric 10828Seric /* 11828Seric ** SCCS.C -- human-oriented front end to the SCCS system. 12828Seric ** 13828Seric ** Without trying to add any functionality to speak of, this 14828Seric ** program tries to make SCCS a little more accessible to human 15828Seric ** types. The main thing it does is automatically put the 16828Seric ** string "SCCS/s." on the front of names. Also, it has a 17828Seric ** couple of things that are designed to shorten frequent 18828Seric ** combinations, e.g., "delget" which expands to a "delta" 19828Seric ** and a "get". 20828Seric ** 21828Seric ** This program can also function as a setuid front end. 22828Seric ** To do this, you should copy the source, renaming it to 23828Seric ** whatever you want, e.g., "syssccs". Change any defaults 24828Seric ** in the program (e.g., syssccs might default -d to 25828Seric ** "/usr/src/sys"). Then recompile and put the result 26828Seric ** as setuid to whomever you want. In this mode, sccs 27828Seric ** knows to not run setuid for certain programs in order 28828Seric ** to preserve security, and so forth. 29828Seric ** 30828Seric ** Usage: 31828Seric ** sccs [flags] command [args] 32828Seric ** 33828Seric ** Flags: 34828Seric ** -d<dir> <dir> represents a directory to search 35828Seric ** out of. It should be a full pathname 36828Seric ** for general usage. E.g., if <dir> is 37828Seric ** "/usr/src/sys", then a reference to the 38828Seric ** file "dev/bio.c" becomes a reference to 39828Seric ** "/usr/src/sys/dev/bio.c". 40828Seric ** -p<path> prepends <path> to the final component 41828Seric ** of the pathname. By default, this is 42828Seric ** "SCCS". For example, in the -d example 43828Seric ** above, the path then gets modified to 44828Seric ** "/usr/src/sys/dev/SCCS/s.bio.c". In 45828Seric ** more common usage (without the -d flag), 46828Seric ** "prog.c" would get modified to 47828Seric ** "SCCS/s.prog.c". In both cases, the 48828Seric ** "s." gets automatically prepended. 49828Seric ** -r run as the real user. 50828Seric ** 51828Seric ** Commands: 52828Seric ** admin, 53828Seric ** get, 54828Seric ** delta, 55828Seric ** rmdel, 56828Seric ** chghist, 57828Seric ** etc. Straight out of SCCS; only difference 58828Seric ** is that pathnames get modified as 59828Seric ** described above. 60828Seric ** edit Macro for "get -e". 61828Seric ** unedit Removes a file being edited, knowing 62828Seric ** about p-files, etc. 63828Seric ** delget Macro for "delta" followed by "get". 64828Seric ** deledit Macro for "delta" followed by "get -e". 65828Seric ** info Tell what files being edited. 66828Seric ** clean Remove all files that can be 67828Seric ** regenerated from SCCS files. 681205Seric ** check Like info, but return exit status, for 69828Seric ** use in makefiles. 70828Seric ** fix Remove a top delta & reedit, but save 71828Seric ** the previous changes in that delta. 72828Seric ** 73828Seric ** Compilation Flags: 74828Seric ** UIDUSER -- determine who the user is by looking at the 75828Seric ** uid rather than the login name -- for machines 76828Seric ** where SCCS gets the user in this way. 771270Seric ** SCCSDIR -- if defined, forces the -d flag to take on 781205Seric ** this value. This is so that the setuid 791205Seric ** aspects of this program cannot be abused. 801270Seric ** This flag also disables the -p flag. 811270Seric ** SCCSPATH -- the default for the -p flag. 821437Seric ** MYNAME -- the title this program should print when it 831437Seric ** gives error messages. 84828Seric ** 85828Seric ** Compilation Instructions: 86828Seric ** cc -O -n -s sccs.c 871437Seric ** The flags listed above can be -D defined to simplify 881437Seric ** recompilation for variant versions. 89828Seric ** 90828Seric ** Author: 91828Seric ** Eric Allman, UCB/INGRES 921270Seric ** Copyright 1980 Regents of the University of California 93828Seric */ 94155Seric 95*1871Seric static char SccsId[] = "@(#)sccs.c 1.52 12/05/80"; 961432Seric 971270Seric /******************* Configuration Information ********************/ 981270Seric 991437Seric /* special defines for local berkeley systems */ 1001437Seric # include <whoami.h> 1011437Seric 102828Seric # ifdef CSVAX 103828Seric # define UIDUSER 1041270Seric # define PROGPATH(name) "/usr/local/name" 1051270Seric # endif CSVAX 106828Seric 1071618Seric # ifdef INGVAX 1081618Seric # define PROGPATH(name) "/usr/local/name" 1091618Seric # endif INGVAX 1101618Seric 1111867Seric # ifdef CORY 1121867Seric # define PROGPATH(name) "/usr/eecs/bin/name" 1131867Seric # endif CORY 1141867Seric 1151437Seric /* end of berkeley systems defines */ 1161437Seric 1171437Seric # ifndef SCCSPATH 1181432Seric # define SCCSPATH "SCCS" /* pathname in which to find s-files */ 1191437Seric # endif NOT SCCSPATH 120828Seric 1211437Seric # ifndef MYNAME 1221437Seric # define MYNAME "sccs" /* name used for printing errors */ 1231437Seric # endif NOT MYNAME 1241270Seric 1251432Seric # ifndef PROGPATH 1261432Seric # define PROGPATH(name) "/usr/sccs/name" /* place to find binaries */ 1271432Seric # endif PROGPATH 1281432Seric 1291270Seric /**************** End of Configuration Information ****************/ 1301432Seric 131157Seric typedef char bool; 132200Seric # define TRUE 1 133200Seric # define FALSE 0 134157Seric 1351438Seric # define bitset(bit, word) ((bool) ((bit) & (word))) 1361438Seric 137828Seric # ifdef UIDUSER 138828Seric # include <pwd.h> 139828Seric # endif UIDUSER 140828Seric 141148Seric struct sccsprog 142148Seric { 143148Seric char *sccsname; /* name of SCCS routine */ 144200Seric short sccsoper; /* opcode, see below */ 145200Seric short sccsflags; /* flags, see below */ 146148Seric char *sccspath; /* pathname of binary implementing */ 147148Seric }; 148148Seric 149200Seric /* values for sccsoper */ 150200Seric # define PROG 0 /* call a program */ 151201Seric # define CMACRO 1 /* command substitution macro */ 152226Seric # define FIX 2 /* fix a delta */ 153261Seric # define CLEAN 3 /* clean out recreatable files */ 154396Seric # define UNEDIT 4 /* unedit a file */ 1551431Seric # define SHELL 5 /* call a shell file (like PROG) */ 1561433Seric # define DIFFS 6 /* diff between sccs & file out */ 157*1871Seric # define DODIFF 7 /* internal call to diff program */ 158200Seric 159157Seric /* bits for sccsflags */ 160200Seric # define NO_SDOT 0001 /* no s. on front of args */ 161200Seric # define REALUSER 0002 /* protected (e.g., admin) */ 162148Seric 163819Seric /* modes for the "clean", "info", "check" ops */ 164819Seric # define CLEANC 0 /* clean command */ 165819Seric # define INFOC 1 /* info command */ 166819Seric # define CHECKC 2 /* check command */ 1671730Seric # define TELLC 3 /* give list of files being edited */ 168819Seric 1691432Seric /* 1701432Seric ** Description of commands known to this program. 1711432Seric ** First argument puts the command into a class. Second arg is 1721432Seric ** info regarding treatment of this command. Third arg is a 1731432Seric ** list of flags this command accepts from macros, etc. Fourth 1741432Seric ** arg is the pathname of the implementing program, or the 1751432Seric ** macro definition, or the arg to a sub-algorithm. 1761432Seric */ 177202Seric 178148Seric struct sccsprog SccsProg[] = 179148Seric { 1801864Seric "admin", PROG, REALUSER, PROGPATH(admin), 1811864Seric "chghist", PROG, 0, PROGPATH(rmdel), 1821864Seric "comb", PROG, 0, PROGPATH(comb), 1831864Seric "delta", PROG, 0, PROGPATH(delta), 1841864Seric "get", PROG, 0, PROGPATH(get), 1851864Seric "help", PROG, NO_SDOT, PROGPATH(help), 1861864Seric "prt", PROG, 0, PROGPATH(prt), 1871864Seric "rmdel", PROG, REALUSER, PROGPATH(rmdel), 1881864Seric "what", PROG, NO_SDOT, PROGPATH(what), 1891864Seric "sccsdiff", SHELL, REALUSER, PROGPATH(sccsdiff), 1901864Seric "edit", CMACRO, NO_SDOT, "get -e", 1911864Seric "delget", CMACRO, NO_SDOT, "delta:mysrp/get:ixbeskcl -t", 1921864Seric "deledit", CMACRO, NO_SDOT, "delta:mysrp/get:ixbskcl -e -t", 1931864Seric "fix", FIX, NO_SDOT, NULL, 1941864Seric "clean", CLEAN, REALUSER|NO_SDOT, (char *) CLEANC, 1951864Seric "info", CLEAN, REALUSER|NO_SDOT, (char *) INFOC, 1961864Seric "check", CLEAN, REALUSER|NO_SDOT, (char *) CHECKC, 1971864Seric "tell", CLEAN, REALUSER|NO_SDOT, (char *) TELLC, 1981864Seric "unedit", UNEDIT, NO_SDOT, NULL, 1991864Seric "diffs", DIFFS, NO_SDOT|REALUSER, NULL, 200*1871Seric "-diff", DODIFF, NO_SDOT|REALUSER, PROGPATH(bdiff), 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 2261432Seric 227148Seric main(argc, argv) 228148Seric int argc; 229148Seric char **argv; 230148Seric { 231148Seric register char *p; 232262Seric extern struct sccsprog *lookup(); 2331282Seric register int i; 234148Seric 235148Seric /* 236148Seric ** Detect and decode flags intended for this program. 237148Seric */ 238148Seric 239200Seric if (argc < 2) 240148Seric { 2411205Seric fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); 242200Seric exit(EX_USAGE); 243200Seric } 244200Seric argv[argc] = NULL; 245200Seric 246262Seric if (lookup(argv[0]) == NULL) 247200Seric { 248262Seric while ((p = *++argv) != NULL) 249148Seric { 250262Seric if (*p != '-') 251262Seric break; 252262Seric switch (*++p) 253262Seric { 254262Seric case 'r': /* run as real user */ 255262Seric setuid(getuid()); 256262Seric RealUser++; 257262Seric break; 258148Seric 2591270Seric # ifndef SCCSDIR 260262Seric case 'p': /* path of sccs files */ 261262Seric SccsPath = ++p; 262262Seric break; 263148Seric 264588Seric case 'd': /* directory to search from */ 265588Seric SccsDir = ++p; 266588Seric break; 2671205Seric # endif 268588Seric 269393Seric # ifdef DEBUG 270393Seric case 'T': /* trace */ 271393Seric Debug++; 272393Seric break; 273393Seric # endif 274393Seric 275262Seric default: 2761205Seric usrerr("unknown option -%s", p); 277262Seric break; 278262Seric } 279148Seric } 280262Seric if (SccsPath[0] == '\0') 281262Seric SccsPath = "."; 282148Seric } 283148Seric 2841737Seric i = command(argv, FALSE, ""); 2851282Seric exit(i); 286200Seric } 2871432Seric 2881432Seric /* 2891282Seric ** COMMAND -- look up and perform a command 2901282Seric ** 2911282Seric ** This routine is the guts of this program. Given an 2921282Seric ** argument vector, it looks up the "command" (argv[0]) 2931282Seric ** in the configuration table and does the necessary stuff. 2941282Seric ** 2951282Seric ** Parameters: 2961282Seric ** argv -- an argument vector to process. 2971282Seric ** forkflag -- if set, fork before executing the command. 2981316Seric ** editflag -- if set, only include flags listed in the 2991316Seric ** sccsklets field of the command descriptor. 3001316Seric ** arg0 -- a space-seperated list of arguments to insert 3011316Seric ** before argv. 3021282Seric ** 3031282Seric ** Returns: 3041282Seric ** zero -- command executed ok. 3051282Seric ** else -- error status. 3061282Seric ** 3071282Seric ** Side Effects: 3081282Seric ** none. 3091282Seric */ 310157Seric 3111737Seric command(argv, forkflag, arg0) 312200Seric char **argv; 313201Seric bool forkflag; 3141316Seric char *arg0; 315200Seric { 316200Seric register struct sccsprog *cmd; 317200Seric register char *p; 318201Seric char buf[40]; 319262Seric extern struct sccsprog *lookup(); 3201316Seric char *nav[1000]; 3211316Seric char **np; 3221431Seric register char **ap; 323585Seric register int i; 3241431Seric register char *q; 325585Seric extern bool unedit(); 3261282Seric int rval = 0; 3271316Seric extern char *index(); 3281316Seric extern char *makefile(); 3291737Seric char *editchs; 3301435Seric extern char *tail(); 331200Seric 332393Seric # ifdef DEBUG 333393Seric if (Debug) 334393Seric { 3351316Seric printf("command:\n\t\"%s\"\n", arg0); 3361316Seric for (np = argv; *np != NULL; np++) 3371316Seric printf("\t\"%s\"\n", *np); 338393Seric } 339393Seric # endif 340393Seric 341157Seric /* 3421316Seric ** Copy arguments. 3431438Seric ** Copy from arg0 & if necessary at most one arg 3441438Seric ** from argv[0]. 3451316Seric */ 3461316Seric 3471431Seric np = ap = &nav[1]; 3481737Seric editchs = NULL; 3491821Seric for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) 3501316Seric { 3511316Seric *np++ = q; 3521316Seric while (*p == ' ') 3531316Seric p++; 3541737Seric while (*p != ' ' && *p != '\0' && *p != '/' && *p != ':') 3551316Seric *q++ = *p++; 3561316Seric *q++ = '\0'; 3571737Seric if (*p == ':') 3581737Seric { 3591737Seric editchs = q; 3601821Seric while (*++p != '\0' && *p != '/' && *p != ' ') 3611737Seric *q++ = *p; 3621737Seric *q++ = '\0'; 3631737Seric } 3641316Seric } 3651316Seric *np = NULL; 3661431Seric if (*ap == NULL) 3671316Seric *np++ = *argv++; 3681316Seric 3691316Seric /* 370148Seric ** Look up command. 3711431Seric ** At this point, *ap is the command name. 372148Seric */ 373148Seric 3741431Seric cmd = lookup(*ap); 375262Seric if (cmd == NULL) 376148Seric { 3771431Seric usrerr("Unknown command \"%s\"", *ap); 3781282Seric return (EX_USAGE); 379148Seric } 380148Seric 381148Seric /* 3821316Seric ** Copy remaining arguments doing editing as appropriate. 3831316Seric */ 3841316Seric 3851316Seric for (; *argv != NULL; argv++) 3861316Seric { 3871316Seric p = *argv; 3881316Seric if (*p == '-') 3891316Seric { 3901737Seric if (p[1] == '\0' || editchs == NULL || index(editchs, p[1]) != NULL) 3911316Seric *np++ = p; 3921316Seric } 3931316Seric else 3941316Seric { 3951316Seric if (!bitset(NO_SDOT, cmd->sccsflags)) 3961316Seric p = makefile(p); 3971316Seric if (p != NULL) 3981316Seric *np++ = p; 3991316Seric } 4001316Seric } 4011316Seric *np = NULL; 4021316Seric 4031316Seric /* 404200Seric ** Interpret operation associated with this command. 405157Seric */ 406157Seric 407200Seric switch (cmd->sccsoper) 408200Seric { 4091431Seric case SHELL: /* call a shell file */ 4101431Seric *ap = cmd->sccspath; 4111431Seric *--ap = "sh"; 4121431Seric rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag); 4131431Seric break; 4141431Seric 415200Seric case PROG: /* call an sccs prog */ 4161431Seric rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); 417201Seric break; 418201Seric 419201Seric case CMACRO: /* command macro */ 4201438Seric /* step through & execute each part of the macro */ 421201Seric for (p = cmd->sccspath; *p != '\0'; p++) 422201Seric { 4231316Seric q = p; 4241316Seric while (*p != '\0' && *p != '/') 4251316Seric p++; 4261737Seric rval = command(&ap[1], *p != '\0', q); 4271282Seric if (rval != 0) 4281282Seric break; 429201Seric } 4301282Seric break; 431157Seric 432226Seric case FIX: /* fix a delta */ 4331431Seric if (strncmp(ap[1], "-r", 2) != 0) 434226Seric { 4351205Seric usrerr("-r flag needed for fix command"); 4361282Seric rval = EX_USAGE; 437226Seric break; 438226Seric } 4391438Seric 4401438Seric /* get the version with all changes */ 4411737Seric rval = command(&ap[1], TRUE, "get -k"); 4421438Seric 4431438Seric /* now remove that version from the s-file */ 4441282Seric if (rval == 0) 4451737Seric rval = command(&ap[1], TRUE, "rmdel:r"); 4461438Seric 4471438Seric /* and edit the old version (but don't clobber new vers) */ 4481282Seric if (rval == 0) 4491737Seric rval = command(&ap[2], FALSE, "get -e -g"); 4501282Seric break; 451226Seric 452261Seric case CLEAN: 4531822Seric rval = clean((int) cmd->sccspath, ap); 454261Seric break; 455261Seric 456396Seric case UNEDIT: 4571431Seric for (argv = np = &ap[1]; *argv != NULL; argv++) 458585Seric { 4591316Seric if (unedit(*argv)) 4601316Seric *np++ = *argv; 461585Seric } 4621316Seric *np = NULL; 4631438Seric 4641438Seric /* get all the files that we unedited successfully */ 4651738Seric if (np > &ap[1]) 4661737Seric rval = command(&ap[1], FALSE, "get"); 467396Seric break; 468396Seric 4691433Seric case DIFFS: /* diff between s-file & edit file */ 4701433Seric /* find the end of the flag arguments */ 4711433Seric for (np = &ap[1]; *np != NULL && **np == '-'; np++) 4721433Seric continue; 4731433Seric argv = np; 4741433Seric 4751433Seric /* for each file, do the diff */ 4761502Seric p = argv[1]; 4771433Seric while (*np != NULL) 4781433Seric { 4791438Seric /* messy, but we need a null terminated argv */ 4801433Seric *argv = *np++; 4811502Seric argv[1] = NULL; 4821435Seric i = dodiff(ap, tail(*argv)); 4831433Seric if (rval == 0) 4841433Seric rval = i; 4851502Seric argv[1] = p; 4861433Seric } 4871433Seric break; 4881433Seric 489*1871Seric case DODIFF: /* internal diff call */ 490*1871Seric setuid(getuid()); 491*1871Seric for (np = ap; *np != NULL; np++) 492*1871Seric { 493*1871Seric if ((*np)[0] == '-' && (*np)[1] == 'C') 494*1871Seric (*np)[1] = 'c'; 495*1871Seric } 496*1871Seric 497*1871Seric /* insert "-" argument */ 498*1871Seric np[1] = NULL; 499*1871Seric np[0] = np[-1]; 500*1871Seric np[-1] = "-"; 501*1871Seric 502*1871Seric /* execute the diff program of choice */ 503*1871Seric # ifndef V6 504*1871Seric execvp("diff", ap); 505*1871Seric # endif 506*1871Seric execv(cmd->sccspath, argv); 507*1871Seric syserr("cannot exec %s", cmd->sccspath); 508*1871Seric exit(EX_OSERR); 509*1871Seric 510200Seric default: 5111205Seric syserr("oper %d", cmd->sccsoper); 512200Seric exit(EX_SOFTWARE); 513200Seric } 5141282Seric # ifdef DEBUG 5151282Seric if (Debug) 5161282Seric printf("command: rval=%d\n", rval); 5171282Seric # endif 5181282Seric return (rval); 519200Seric } 5201432Seric 5211432Seric /* 522262Seric ** LOOKUP -- look up an SCCS command name. 523262Seric ** 524262Seric ** Parameters: 525262Seric ** name -- the name of the command to look up. 526262Seric ** 527262Seric ** Returns: 528262Seric ** ptr to command descriptor for this command. 529262Seric ** NULL if no such entry. 530262Seric ** 531262Seric ** Side Effects: 532262Seric ** none. 533262Seric */ 534200Seric 535262Seric struct sccsprog * 536262Seric lookup(name) 537262Seric char *name; 538262Seric { 539262Seric register struct sccsprog *cmd; 540226Seric 541262Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 542262Seric { 543262Seric if (strcmp(cmd->sccsname, name) == 0) 544262Seric return (cmd); 545262Seric } 546262Seric return (NULL); 547262Seric } 5481432Seric 5491432Seric /* 5501282Seric ** CALLPROG -- call a program 5511282Seric ** 5521316Seric ** Used to call the SCCS programs. 5531282Seric ** 5541282Seric ** Parameters: 5551282Seric ** progpath -- pathname of the program to call. 5561282Seric ** flags -- status flags from the command descriptors. 5571282Seric ** argv -- an argument vector to pass to the program. 5581282Seric ** forkflag -- if true, fork before calling, else just 5591282Seric ** exec. 5601282Seric ** 5611282Seric ** Returns: 5621282Seric ** The exit status of the program. 5631282Seric ** Nothing if forkflag == FALSE. 5641282Seric ** 5651282Seric ** Side Effects: 5661282Seric ** Can exit if forkflag == FALSE. 5671282Seric */ 568226Seric 569200Seric callprog(progpath, flags, argv, forkflag) 570200Seric char *progpath; 571200Seric short flags; 572200Seric char **argv; 573200Seric bool forkflag; 574200Seric { 575200Seric register int i; 576201Seric auto int st; 577200Seric 5781316Seric # ifdef DEBUG 5791316Seric if (Debug) 5801316Seric { 5811316Seric printf("callprog:\n"); 5821316Seric for (i = 0; argv[i] != NULL; i++) 5831316Seric printf("\t\"%s\"\n", argv[i]); 5841316Seric } 5851316Seric # endif 5861316Seric 587200Seric if (*argv == NULL) 588200Seric return (-1); 589200Seric 590157Seric /* 591226Seric ** Fork if appropriate. 592148Seric */ 593148Seric 594200Seric if (forkflag) 595200Seric { 596393Seric # ifdef DEBUG 597393Seric if (Debug) 598393Seric printf("Forking\n"); 599393Seric # endif 600200Seric i = fork(); 601200Seric if (i < 0) 602200Seric { 6031205Seric syserr("cannot fork"); 604200Seric exit(EX_OSERR); 605200Seric } 606200Seric else if (i > 0) 607201Seric { 608201Seric wait(&st); 6091282Seric if ((st & 0377) == 0) 6101282Seric st = (st >> 8) & 0377; 6111433Seric if (OutFile >= 0) 6121433Seric { 6131433Seric close(OutFile); 6141433Seric OutFile = -1; 6151433Seric } 616201Seric return (st); 617201Seric } 618200Seric } 6191433Seric else if (OutFile >= 0) 6201433Seric { 6211433Seric syserr("callprog: setting stdout w/o forking"); 6221433Seric exit(EX_SOFTWARE); 6231433Seric } 624200Seric 6251433Seric /* set protection as appropriate */ 626200Seric if (bitset(REALUSER, flags)) 627200Seric setuid(getuid()); 6281433Seric 6291433Seric /* change standard input & output if needed */ 6301433Seric if (OutFile >= 0) 6311433Seric { 6321433Seric close(1); 6331433Seric dup(OutFile); 6341433Seric close(OutFile); 6351433Seric } 636226Seric 6371433Seric /* call real SCCS program */ 638226Seric execv(progpath, argv); 6391205Seric syserr("cannot execute %s", progpath); 640148Seric exit(EX_UNAVAILABLE); 6411738Seric /*NOTREACHED*/ 642148Seric } 6431432Seric 6441432Seric /* 645586Seric ** MAKEFILE -- make filename of SCCS file 646586Seric ** 647586Seric ** If the name passed is already the name of an SCCS file, 648586Seric ** just return it. Otherwise, munge the name into the name 649586Seric ** of the actual SCCS file. 650586Seric ** 651586Seric ** There are cases when it is not clear what you want to 652586Seric ** do. For example, if SccsPath is an absolute pathname 653586Seric ** and the name given is also an absolute pathname, we go 654586Seric ** for SccsPath (& only use the last component of the name 655586Seric ** passed) -- this is important for security reasons (if 656586Seric ** sccs is being used as a setuid front end), but not 657586Seric ** particularly intuitive. 658586Seric ** 659586Seric ** Parameters: 660586Seric ** name -- the file name to be munged. 661586Seric ** 662586Seric ** Returns: 663586Seric ** The pathname of the sccs file. 664586Seric ** NULL on error. 665586Seric ** 666586Seric ** Side Effects: 667586Seric ** none. 668586Seric */ 669148Seric 670148Seric char * 671148Seric makefile(name) 672148Seric char *name; 673148Seric { 674148Seric register char *p; 675148Seric char buf[512]; 676148Seric extern char *malloc(); 677586Seric extern char *rindex(); 678588Seric extern bool safepath(); 679587Seric extern bool isdir(); 680587Seric register char *q; 681148Seric 682586Seric p = rindex(name, '/'); 683586Seric if (p == NULL) 684586Seric p = name; 685586Seric else 686586Seric p++; 687586Seric 688148Seric /* 689588Seric ** Check to see that the path is "safe", i.e., that we 690588Seric ** are not letting some nasty person use the setuid part 691588Seric ** of this program to look at or munge some presumably 692588Seric ** hidden files. 693148Seric */ 694148Seric 695588Seric if (SccsDir[0] == '/' && !safepath(name)) 696588Seric return (NULL); 697586Seric 698586Seric /* 699588Seric ** Create the base pathname. 700586Seric */ 701586Seric 7021438Seric /* first the directory part */ 703588Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) 704148Seric { 705588Seric strcpy(buf, SccsDir); 706586Seric strcat(buf, "/"); 707586Seric } 708586Seric else 709586Seric strcpy(buf, ""); 7101438Seric 7111438Seric /* then the head of the pathname */ 712587Seric strncat(buf, name, p - name); 713587Seric q = &buf[strlen(buf)]; 7141438Seric 7151438Seric /* now copy the final part of the name, in case useful */ 716587Seric strcpy(q, p); 7171438Seric 7181438Seric /* so is it useful? */ 719587Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 720586Seric { 7211438Seric /* sorry, no; copy the SCCS pathname & the "s." */ 722588Seric strcpy(q, SccsPath); 723588Seric strcat(buf, "/s."); 7241438Seric 7251438Seric /* and now the end of the name */ 726586Seric strcat(buf, p); 727586Seric } 728148Seric 7291438Seric /* if i haven't changed it, why did I do all this? */ 730588Seric if (strcmp(buf, name) == 0) 731588Seric p = name; 732588Seric else 733148Seric { 7341438Seric /* but if I have, squirrel it away */ 735588Seric p = malloc(strlen(buf) + 1); 736588Seric if (p == NULL) 737588Seric { 738588Seric perror("Sccs: no mem"); 739588Seric exit(EX_OSERR); 740588Seric } 741588Seric strcpy(p, buf); 742148Seric } 7431438Seric 744148Seric return (p); 745148Seric } 7461432Seric 7471432Seric /* 748587Seric ** ISDIR -- return true if the argument is a directory. 749587Seric ** 750587Seric ** Parameters: 751587Seric ** name -- the pathname of the file to check. 752587Seric ** 753587Seric ** Returns: 754587Seric ** TRUE if 'name' is a directory, FALSE otherwise. 755587Seric ** 756587Seric ** Side Effects: 757587Seric ** none. 758587Seric */ 759587Seric 760587Seric bool 761587Seric isdir(name) 762587Seric char *name; 763587Seric { 764587Seric struct stat stbuf; 765587Seric 766587Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 767587Seric } 7681432Seric 7691432Seric /* 770586Seric ** SAFEPATH -- determine whether a pathname is "safe" 771586Seric ** 772586Seric ** "Safe" pathnames only allow you to get deeper into the 773586Seric ** directory structure, i.e., full pathnames and ".." are 774586Seric ** not allowed. 775586Seric ** 776586Seric ** Parameters: 777586Seric ** p -- the name to check. 778586Seric ** 779586Seric ** Returns: 780586Seric ** TRUE -- if the path is safe. 781586Seric ** FALSE -- if the path is not safe. 782586Seric ** 783586Seric ** Side Effects: 784586Seric ** Prints a message if the path is not safe. 785586Seric */ 786586Seric 787586Seric bool 788586Seric safepath(p) 789586Seric register char *p; 790586Seric { 791586Seric extern char *index(); 792586Seric 793586Seric if (*p != '/') 794586Seric { 795586Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 796586Seric { 797586Seric p = index(p, '/'); 798586Seric if (p == NULL) 799586Seric return (TRUE); 800586Seric p++; 801586Seric } 802586Seric } 803586Seric 804586Seric printf("You may not use full pathnames or \"..\"\n"); 805586Seric return (FALSE); 806586Seric } 8071432Seric 8081432Seric /* 809261Seric ** CLEAN -- clean out recreatable files 810261Seric ** 811261Seric ** Any file for which an "s." file exists but no "p." file 812261Seric ** exists in the current directory is purged. 813261Seric ** 814261Seric ** Parameters: 8151822Seric ** mode -- tells whether this came from a "clean", "info", or 8161822Seric ** "check" command. 8171822Seric ** argv -- the rest of the argument vector. 818261Seric ** 819261Seric ** Returns: 820261Seric ** none. 821261Seric ** 822261Seric ** Side Effects: 823819Seric ** Removes files in the current directory. 824819Seric ** Prints information regarding files being edited. 825819Seric ** Exits if a "check" command. 826261Seric */ 827261Seric 8281822Seric clean(mode, argv) 829819Seric int mode; 8301822Seric char **argv; 831261Seric { 832261Seric struct direct dir; 833261Seric char buf[100]; 834346Seric register FILE *dirfd; 835346Seric register char *basefile; 836351Seric bool gotedit; 8371822Seric bool gotpfent; 838394Seric FILE *pfp; 8391822Seric bool nobranch = FALSE; 8401822Seric extern struct pfile *getpfent(); 8411822Seric register struct pfile *pf; 8421822Seric register char **ap; 8431864Seric extern char *username(); 8441864Seric char *usernm = NULL; 845261Seric 8461438Seric /* 8471822Seric ** Process the argv 8481822Seric */ 8491822Seric 8501822Seric for (ap = argv; *ap != NULL; ap++) 8511822Seric { 8521864Seric if (**ap == '-') 8531864Seric { 8541864Seric /* we have a flag */ 8551864Seric switch ((*ap)[1]) 8561864Seric { 8571864Seric case 'b': 8581864Seric nobranch = TRUE; 8591864Seric break; 8601864Seric 8611864Seric case 'u': 8621864Seric if ((*ap)[2] != '\0') 8631864Seric usernm = &(*ap)[2]; 8641864Seric else if (ap[1] != NULL && ap[1][0] != '-') 8651864Seric usernm = *++ap; 8661864Seric else 8671864Seric usernm = username(); 8681864Seric break; 8691864Seric } 8701864Seric } 8711822Seric } 8721822Seric 8731822Seric /* 8741438Seric ** Find and open the SCCS directory. 8751438Seric */ 8761438Seric 8771207Seric strcpy(buf, SccsDir); 8781207Seric if (buf[0] != '\0') 8791207Seric strcat(buf, "/"); 8801207Seric strcat(buf, SccsPath); 8811438Seric 8821207Seric dirfd = fopen(buf, "r"); 883261Seric if (dirfd == NULL) 884261Seric { 8851207Seric usrerr("cannot open %s", buf); 8861282Seric return (EX_NOINPUT); 887261Seric } 888261Seric 889261Seric /* 890261Seric ** Scan the SCCS directory looking for s. files. 8911438Seric ** gotedit tells whether we have tried to clean any 8921438Seric ** files that are being edited. 893261Seric */ 894261Seric 895351Seric gotedit = FALSE; 8961738Seric while (fread((char *)&dir, sizeof dir, 1, dirfd) != NULL) 897261Seric { 898568Seric if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) 899261Seric continue; 900261Seric 901261Seric /* got an s. file -- see if the p. file exists */ 9021207Seric strcpy(buf, SccsDir); 9031207Seric if (buf[0] != '\0') 9041207Seric strcat(buf, "/"); 9051207Seric strcat(buf, SccsPath); 906261Seric strcat(buf, "/p."); 907346Seric basefile = &buf[strlen(buf)]; 908568Seric strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 909346Seric basefile[sizeof dir.d_name - 2] = '\0'; 9101822Seric 9111822Seric /* 9121822Seric ** open and scan the p-file. 9131822Seric ** 'gotpfent' tells if we have found a valid p-file 9141822Seric ** entry. 9151822Seric */ 9161822Seric 917394Seric pfp = fopen(buf, "r"); 9181822Seric gotpfent = FALSE; 919394Seric if (pfp != NULL) 920346Seric { 9211438Seric /* the file exists -- report it's contents */ 9221822Seric while ((pf = getpfent(pfp)) != NULL) 9231730Seric { 9241822Seric if (nobranch && isbranch(pf->p_nsid)) 9251822Seric continue; 9261864Seric if (usernm != NULL && strcmp(usernm, pf->p_user) != 0 && mode != CLEANC) 9271864Seric continue; 9281822Seric gotedit = TRUE; 9291822Seric gotpfent = TRUE; 9301822Seric if (mode == TELLC) 9311822Seric { 9321822Seric printf("%s\n", basefile); 9331822Seric break; 9341822Seric } 9351822Seric printf("%12s: being edited: %s %s %s %s %s\n", 9361822Seric basefile, pf->p_osid, pf->p_nsid, 9371822Seric pf->p_user, pf->p_date, pf->p_time); 9381730Seric } 939394Seric fclose(pfp); 9401822Seric } 941261Seric 942261Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 9431870Seric if (mode == CLEANC && !gotpfent) 944346Seric { 945568Seric strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); 946346Seric buf[sizeof dir.d_name - 2] = '\0'; 947346Seric unlink(buf); 948346Seric } 949261Seric } 950261Seric 9511438Seric /* cleanup & report results */ 952261Seric fclose(dirfd); 953819Seric if (!gotedit && mode == INFOC) 9541864Seric { 9551864Seric printf("Nothing being edited"); 9561864Seric if (nobranch) 9571864Seric printf(" (on trunk)"); 9581864Seric if (usernm == NULL) 9591864Seric printf("\n"); 9601864Seric else 9611864Seric printf(" by %s\n", usernm); 9621864Seric } 963819Seric if (mode == CHECKC) 964819Seric exit(gotedit); 9651282Seric return (EX_OK); 966261Seric } 9671432Seric 9681432Seric /* 9691822Seric ** ISBRANCH -- is the SID a branch? 9701822Seric ** 9711822Seric ** Parameters: 9721822Seric ** sid -- the sid to check. 9731822Seric ** 9741822Seric ** Returns: 9751822Seric ** TRUE if the sid represents a branch. 9761822Seric ** FALSE otherwise. 9771822Seric ** 9781822Seric ** Side Effects: 9791822Seric ** none. 9801822Seric */ 9811822Seric 9821822Seric isbranch(sid) 9831822Seric char *sid; 9841822Seric { 9851822Seric register char *p; 9861822Seric int dots; 9871822Seric 9881822Seric dots = 0; 9891822Seric for (p = sid; *p != '\0'; p++) 9901822Seric { 9911822Seric if (*p == '.') 9921822Seric dots++; 9931822Seric if (dots > 1) 9941822Seric return (TRUE); 9951822Seric } 9961822Seric return (FALSE); 9971822Seric } 9981822Seric 9991822Seric /* 1000396Seric ** UNEDIT -- unedit a file 1001396Seric ** 1002396Seric ** Checks to see that the current user is actually editting 1003396Seric ** the file and arranges that s/he is not editting it. 1004396Seric ** 1005396Seric ** Parameters: 1006416Seric ** fn -- the name of the file to be unedited. 1007396Seric ** 1008396Seric ** Returns: 1009585Seric ** TRUE -- if the file was successfully unedited. 1010585Seric ** FALSE -- if the file was not unedited for some 1011585Seric ** reason. 1012396Seric ** 1013396Seric ** Side Effects: 1014396Seric ** fn is removed 1015396Seric ** entries are removed from pfile. 1016396Seric */ 1017396Seric 1018585Seric bool 1019396Seric unedit(fn) 1020396Seric char *fn; 1021396Seric { 1022396Seric register FILE *pfp; 1023396Seric char *pfn; 1024396Seric static char tfn[] = "/tmp/sccsXXXXX"; 1025396Seric FILE *tfp; 1026396Seric register char *q; 1027396Seric bool delete = FALSE; 1028396Seric bool others = FALSE; 1029396Seric char *myname; 10301864Seric extern char *username(); 1031396Seric struct pfile *pent; 10321822Seric extern struct pfile *getpfent(); 1033396Seric char buf[120]; 10341316Seric extern char *makefile(); 1035396Seric 1036396Seric /* make "s." filename & find the trailing component */ 1037396Seric pfn = makefile(fn); 1038586Seric if (pfn == NULL) 1039586Seric return (FALSE); 1040586Seric q = rindex(pfn, '/'); 1041586Seric if (q == NULL) 1042586Seric q = &pfn[-1]; 1043586Seric if (q[1] != 's' || q[2] != '.') 1044396Seric { 10451205Seric usrerr("bad file name \"%s\"", fn); 1046585Seric return (FALSE); 1047396Seric } 1048396Seric 10491438Seric /* turn "s." into "p." & try to open it */ 1050396Seric *++q = 'p'; 1051396Seric 1052396Seric pfp = fopen(pfn, "r"); 1053396Seric if (pfp == NULL) 1054396Seric { 1055416Seric printf("%12s: not being edited\n", fn); 1056585Seric return (FALSE); 1057396Seric } 1058396Seric 10591438Seric /* create temp file for editing p-file */ 1060396Seric mktemp(tfn); 1061396Seric tfp = fopen(tfn, "w"); 1062396Seric if (tfp == NULL) 1063396Seric { 10641205Seric usrerr("cannot create \"%s\"", tfn); 1065396Seric exit(EX_OSERR); 1066396Seric } 1067396Seric 10681438Seric /* figure out who I am */ 10691864Seric myname = username(); 10701438Seric 10711438Seric /* 10721438Seric ** Copy p-file to temp file, doing deletions as needed. 10731438Seric */ 10741438Seric 10751822Seric while ((pent = getpfent(pfp)) != NULL) 1076396Seric { 1077396Seric if (strcmp(pent->p_user, myname) == 0) 1078396Seric { 1079396Seric /* a match */ 1080396Seric delete++; 1081396Seric } 1082396Seric else 1083396Seric { 10841438Seric /* output it again */ 1085396Seric fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, 1086396Seric pent->p_nsid, pent->p_user, pent->p_date, 1087396Seric pent->p_time); 1088396Seric others++; 1089396Seric } 1090396Seric } 1091396Seric 1092396Seric /* do final cleanup */ 1093396Seric if (others) 1094396Seric { 10951438Seric /* copy it back (perhaps it should be linked?) */ 1096396Seric if (freopen(tfn, "r", tfp) == NULL) 1097396Seric { 10981205Seric syserr("cannot reopen \"%s\"", tfn); 1099396Seric exit(EX_OSERR); 1100396Seric } 1101396Seric if (freopen(pfn, "w", pfp) == NULL) 1102396Seric { 11031205Seric usrerr("cannot create \"%s\"", pfn); 1104585Seric return (FALSE); 1105396Seric } 1106396Seric while (fgets(buf, sizeof buf, tfp) != NULL) 1107396Seric fputs(buf, pfp); 1108396Seric } 1109396Seric else 1110396Seric { 11111438Seric /* it's empty -- remove it */ 1112396Seric unlink(pfn); 1113396Seric } 1114396Seric fclose(tfp); 1115396Seric fclose(pfp); 1116396Seric unlink(tfn); 1117396Seric 11181438Seric /* actually remove the g-file */ 1119396Seric if (delete) 1120396Seric { 11211435Seric unlink(tail(fn)); 11221435Seric printf("%12s: removed\n", tail(fn)); 1123585Seric return (TRUE); 1124396Seric } 1125396Seric else 1126396Seric { 1127416Seric printf("%12s: not being edited by you\n", fn); 1128585Seric return (FALSE); 1129396Seric } 1130396Seric } 11311432Seric 11321432Seric /* 11331433Seric ** DODIFF -- diff an s-file against a g-file 11341433Seric ** 11351433Seric ** Parameters: 11361433Seric ** getv -- argv for the 'get' command. 11371433Seric ** gfile -- name of the g-file to diff against. 11381433Seric ** 11391433Seric ** Returns: 11401433Seric ** Result of get. 11411433Seric ** 11421433Seric ** Side Effects: 11431433Seric ** none. 11441433Seric */ 11451433Seric 11461433Seric dodiff(getv, gfile) 11471433Seric char **getv; 11481433Seric char *gfile; 11491433Seric { 11501433Seric int pipev[2]; 11511433Seric int rval; 11521433Seric register int i; 11531433Seric register int pid; 11541433Seric auto int st; 11551433Seric extern int errno; 11561433Seric int (*osig)(); 11571433Seric 11581501Seric printf("\n------- %s -------\n", gfile); 1159*1871Seric fflush(stdout); 11601501Seric 11611438Seric /* create context for diff to run in */ 11621433Seric if (pipe(pipev) < 0) 11631433Seric { 11641433Seric syserr("dodiff: pipe failed"); 11651433Seric exit(EX_OSERR); 11661433Seric } 11671433Seric if ((pid = fork()) < 0) 11681433Seric { 11691433Seric syserr("dodiff: fork failed"); 11701433Seric exit(EX_OSERR); 11711433Seric } 11721433Seric else if (pid > 0) 11731433Seric { 11741433Seric /* in parent; run get */ 11751433Seric OutFile = pipev[1]; 11761433Seric close(pipev[0]); 1177*1871Seric rval = command(&getv[1], TRUE, "get:rcixt -s -k -p"); 11781433Seric osig = signal(SIGINT, SIG_IGN); 11791433Seric while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR) 11801433Seric errno = 0; 11811433Seric signal(SIGINT, osig); 11821433Seric /* ignore result of diff */ 11831433Seric } 11841433Seric else 11851433Seric { 11861433Seric /* in child, run diff */ 11871433Seric if (close(pipev[1]) < 0 || close(0) < 0 || 11881433Seric dup(pipev[0]) != 0 || close(pipev[0]) < 0) 11891433Seric { 11901433Seric syserr("dodiff: magic failed"); 11911433Seric exit(EX_OSERR); 11921433Seric } 1193*1871Seric command(&getv[1], FALSE, "-diff:elsfhbC"); 11941433Seric } 11951433Seric return (rval); 11961433Seric } 11971433Seric 11981433Seric /* 11991435Seric ** TAIL -- return tail of filename. 12001435Seric ** 12011435Seric ** Parameters: 12021435Seric ** fn -- the filename. 12031435Seric ** 12041435Seric ** Returns: 12051435Seric ** a pointer to the tail of the filename; e.g., given 12061435Seric ** "cmd/ls.c", "ls.c" is returned. 12071435Seric ** 12081435Seric ** Side Effects: 12091435Seric ** none. 12101435Seric */ 12111435Seric 12121435Seric char * 12131435Seric tail(fn) 12141435Seric register char *fn; 12151435Seric { 12161435Seric register char *p; 12171435Seric 12181435Seric for (p = fn; *p != 0; p++) 12191435Seric if (*p == '/' && p[1] != '\0' && p[1] != '/') 12201435Seric fn = &p[1]; 12211435Seric return (fn); 12221435Seric } 12231435Seric 12241435Seric /* 12251822Seric ** GETPFENT -- get an entry from the p-file 1226396Seric ** 1227396Seric ** Parameters: 1228396Seric ** pfp -- p-file file pointer 1229396Seric ** 1230396Seric ** Returns: 1231396Seric ** pointer to p-file struct for next entry 1232396Seric ** NULL on EOF or error 1233396Seric ** 1234396Seric ** Side Effects: 1235396Seric ** Each call wipes out results of previous call. 1236396Seric */ 1237396Seric 1238396Seric struct pfile * 12391822Seric getpfent(pfp) 1240396Seric FILE *pfp; 1241396Seric { 1242396Seric static struct pfile ent; 1243396Seric static char buf[120]; 1244396Seric register char *p; 1245396Seric extern char *nextfield(); 1246396Seric 1247396Seric if (fgets(buf, sizeof buf, pfp) == NULL) 1248396Seric return (NULL); 1249396Seric 1250396Seric ent.p_osid = p = buf; 1251396Seric ent.p_nsid = p = nextfield(p); 1252396Seric ent.p_user = p = nextfield(p); 1253396Seric ent.p_date = p = nextfield(p); 1254396Seric ent.p_time = p = nextfield(p); 1255396Seric if (p == NULL || nextfield(p) != NULL) 1256396Seric return (NULL); 1257396Seric 1258396Seric return (&ent); 1259396Seric } 1260396Seric 1261396Seric 1262396Seric char * 1263396Seric nextfield(p) 1264396Seric register char *p; 1265396Seric { 1266396Seric if (p == NULL || *p == '\0') 1267396Seric return (NULL); 1268396Seric while (*p != ' ' && *p != '\n' && *p != '\0') 1269396Seric p++; 1270396Seric if (*p == '\n' || *p == '\0') 1271396Seric { 1272396Seric *p = '\0'; 1273396Seric return (NULL); 1274396Seric } 1275396Seric *p++ = '\0'; 1276396Seric return (p); 1277396Seric } 12781432Seric 12791432Seric /* 12801205Seric ** USRERR -- issue user-level error 12811205Seric ** 12821205Seric ** Parameters: 12831205Seric ** f -- format string. 12841205Seric ** p1-p3 -- parameters to a printf. 12851205Seric ** 12861205Seric ** Returns: 12871205Seric ** -1 12881205Seric ** 12891205Seric ** Side Effects: 12901205Seric ** none. 12911205Seric */ 12921205Seric 12931738Seric /*VARARGS1*/ 12941205Seric usrerr(f, p1, p2, p3) 12951205Seric char *f; 12961205Seric { 12971205Seric fprintf(stderr, "\n%s: ", MyName); 12981205Seric fprintf(stderr, f, p1, p2, p3); 12991205Seric fprintf(stderr, "\n"); 13001205Seric 13011205Seric return (-1); 13021205Seric } 13031432Seric 13041432Seric /* 13051205Seric ** SYSERR -- print system-generated error. 13061205Seric ** 13071205Seric ** Parameters: 13081205Seric ** f -- format string to a printf. 13091205Seric ** p1, p2, p3 -- parameters to f. 13101205Seric ** 13111205Seric ** Returns: 13121205Seric ** never. 13131205Seric ** 13141205Seric ** Side Effects: 13151205Seric ** none. 13161205Seric */ 13171205Seric 13181738Seric /*VARARGS1*/ 13191205Seric syserr(f, p1, p2, p3) 13201205Seric char *f; 13211205Seric { 13221205Seric extern int errno; 13231205Seric 13241205Seric fprintf(stderr, "\n%s SYSERR: ", MyName); 13251205Seric fprintf(stderr, f, p1, p2, p3); 13261205Seric fprintf(stderr, "\n"); 13271205Seric if (errno == 0) 13281205Seric exit(EX_SOFTWARE); 13291205Seric else 13301205Seric { 13311738Seric perror(NULL); 13321205Seric exit(EX_OSERR); 13331205Seric } 13341205Seric } 13351864Seric /* 13361864Seric ** USERNAME -- return name of the current user 13371864Seric ** 13381864Seric ** Parameters: 13391864Seric ** none 13401864Seric ** 13411864Seric ** Returns: 13421864Seric ** name of current user 13431864Seric ** 13441864Seric ** Side Effects: 13451864Seric ** none 13461864Seric */ 13471864Seric 13481864Seric char * 13491864Seric username() 13501864Seric { 13511864Seric # ifdef UIDUSER 13521864Seric extern struct passwd *getpwuid(); 13531864Seric register struct passwd *pw; 13541864Seric 13551864Seric pw = getpwuid(getuid()); 13561864Seric if (pw == NULL) 13571864Seric { 13581864Seric syserr("who are you? (uid=%d)", getuid()); 13591864Seric exit(EX_OSERR); 13601864Seric } 13611864Seric return (pw->pw_name); 13621864Seric # else 13631864Seric return (getlogin()); 13641864Seric # endif UIDUSER 13651864Seric } 1366