|
|
1.1 ! root 1: #ifndef lint ! 2: static char sccsid[] = "@(#)sccs.c 1.71 (Berkeley) 7/2/83"; ! 3: #endif ! 4: ! 5: # include <stdio.h> ! 6: # include <sys/param.h> ! 7: # include <sys/stat.h> ! 8: # include <sys/dir.h> ! 9: # include <errno.h> ! 10: # include <signal.h> ! 11: # include <sysexits.h> ! 12: # include <pwd.h> ! 13: ! 14: /* ! 15: ** SCCS.C -- human-oriented front end to the SCCS system. ! 16: ** ! 17: ** Without trying to add any functionality to speak of, this ! 18: ** program tries to make SCCS a little more accessible to human ! 19: ** types. The main thing it does is automatically put the ! 20: ** string "SCCS/s." on the front of names. Also, it has a ! 21: ** couple of things that are designed to shorten frequent ! 22: ** combinations, e.g., "delget" which expands to a "delta" ! 23: ** and a "get". ! 24: ** ! 25: ** This program can also function as a setuid front end. ! 26: ** To do this, you should copy the source, renaming it to ! 27: ** whatever you want, e.g., "syssccs". Change any defaults ! 28: ** in the program (e.g., syssccs might default -d to ! 29: ** "/usr/src/sys"). Then recompile and put the result ! 30: ** as setuid to whomever you want. In this mode, sccs ! 31: ** knows to not run setuid for certain programs in order ! 32: ** to preserve security, and so forth. ! 33: ** ! 34: ** Usage: ! 35: ** sccs [flags] command [args] ! 36: ** ! 37: ** Flags: ! 38: ** -d<dir> <dir> represents a directory to search ! 39: ** out of. It should be a full pathname ! 40: ** for general usage. E.g., if <dir> is ! 41: ** "/usr/src/sys", then a reference to the ! 42: ** file "dev/bio.c" becomes a reference to ! 43: ** "/usr/src/sys/dev/bio.c". ! 44: ** -p<path> prepends <path> to the final component ! 45: ** of the pathname. By default, this is ! 46: ** "SCCS". For example, in the -d example ! 47: ** above, the path then gets modified to ! 48: ** "/usr/src/sys/dev/SCCS/s.bio.c". In ! 49: ** more common usage (without the -d flag), ! 50: ** "prog.c" would get modified to ! 51: ** "SCCS/s.prog.c". In both cases, the ! 52: ** "s." gets automatically prepended. ! 53: ** -r run as the real user. ! 54: ** ! 55: ** Commands: ! 56: ** admin, ! 57: ** get, ! 58: ** delta, ! 59: ** rmdel, ! 60: ** chghist, ! 61: ** etc. Straight out of SCCS; only difference ! 62: ** is that pathnames get modified as ! 63: ** described above. ! 64: ** edit Macro for "get -e". ! 65: ** unedit Removes a file being edited, knowing ! 66: ** about p-files, etc. ! 67: ** delget Macro for "delta" followed by "get". ! 68: ** deledit Macro for "delta" followed by "get -e". ! 69: ** info Tell what files being edited. ! 70: ** clean Remove all files that can be ! 71: ** regenerated from SCCS files. ! 72: ** check Like info, but return exit status, for ! 73: ** use in makefiles. ! 74: ** fix Remove a top delta & reedit, but save ! 75: ** the previous changes in that delta. ! 76: ** ! 77: ** Compilation Flags: ! 78: ** UIDUSER -- determine who the user is by looking at the ! 79: ** uid rather than the login name -- for machines ! 80: ** where SCCS gets the user in this way. ! 81: ** SCCSDIR -- if defined, forces the -d flag to take on ! 82: ** this value. This is so that the setuid ! 83: ** aspects of this program cannot be abused. ! 84: ** This flag also disables the -p flag. ! 85: ** SCCSPATH -- the default for the -p flag. ! 86: ** MYNAME -- the title this program should print when it ! 87: ** gives error messages. ! 88: ** ! 89: ** Compilation Instructions: ! 90: ** cc -O -n -s sccs.c ! 91: ** The flags listed above can be -D defined to simplify ! 92: ** recompilation for variant versions. ! 93: ** ! 94: ** Author: ! 95: ** Eric Allman, UCB/INGRES ! 96: ** Copyright 1980 Regents of the University of California ! 97: */ ! 98: ! 99: ! 100: /******************* Configuration Information ********************/ ! 101: ! 102: # ifndef SCCSPATH ! 103: # define SCCSPATH "SCCS" /* pathname in which to find s-files */ ! 104: # endif NOT SCCSPATH ! 105: ! 106: # ifndef MYNAME ! 107: # define MYNAME "sccs" /* name used for printing errors */ ! 108: # endif NOT MYNAME ! 109: ! 110: # ifndef PROGPATH ! 111: # define PROGPATH(name) "/usr/local/name" /* place to find binaries */ ! 112: # endif PROGPATH ! 113: ! 114: /**************** End of Configuration Information ****************/ ! 115: ! 116: typedef char bool; ! 117: # define TRUE 1 ! 118: # define FALSE 0 ! 119: ! 120: # define bitset(bit, word) ((bool) ((bit) & (word))) ! 121: ! 122: struct sccsprog ! 123: { ! 124: char *sccsname; /* name of SCCS routine */ ! 125: short sccsoper; /* opcode, see below */ ! 126: short sccsflags; /* flags, see below */ ! 127: char *sccspath; /* pathname of binary implementing */ ! 128: }; ! 129: ! 130: /* values for sccsoper */ ! 131: # define PROG 0 /* call a program */ ! 132: # define CMACRO 1 /* command substitution macro */ ! 133: # define FIX 2 /* fix a delta */ ! 134: # define CLEAN 3 /* clean out recreatable files */ ! 135: # define UNEDIT 4 /* unedit a file */ ! 136: # define SHELL 5 /* call a shell file (like PROG) */ ! 137: # define DIFFS 6 /* diff between sccs & file out */ ! 138: # define DODIFF 7 /* internal call to diff program */ ! 139: # define ENTER 8 /* enter new files */ ! 140: ! 141: /* bits for sccsflags */ ! 142: # define NO_SDOT 0001 /* no s. on front of args */ ! 143: # define REALUSER 0002 /* protected (e.g., admin) */ ! 144: ! 145: /* modes for the "clean", "info", "check" ops */ ! 146: # define CLEANC 0 /* clean command */ ! 147: # define INFOC 1 /* info command */ ! 148: # define CHECKC 2 /* check command */ ! 149: # define TELLC 3 /* give list of files being edited */ ! 150: ! 151: /* ! 152: ** Description of commands known to this program. ! 153: ** First argument puts the command into a class. Second arg is ! 154: ** info regarding treatment of this command. Third arg is a ! 155: ** list of flags this command accepts from macros, etc. Fourth ! 156: ** arg is the pathname of the implementing program, or the ! 157: ** macro definition, or the arg to a sub-algorithm. ! 158: */ ! 159: ! 160: struct sccsprog SccsProg[] = ! 161: { ! 162: "admin", PROG, REALUSER, PROGPATH(admin), ! 163: "chghist", PROG, 0, PROGPATH(rmdel), ! 164: "comb", PROG, 0, PROGPATH(comb), ! 165: "delta", PROG, 0, PROGPATH(delta), ! 166: "get", PROG, 0, PROGPATH(get), ! 167: "help", PROG, NO_SDOT, PROGPATH(help), ! 168: "prs", PROG, 0, PROGPATH(prs), ! 169: "prt", PROG, 0, PROGPATH(prt), ! 170: "rmdel", PROG, REALUSER, PROGPATH(rmdel), ! 171: "val", PROG, 0, PROGPATH(val), ! 172: "what", PROG, NO_SDOT, PROGPATH(what), ! 173: "sccsdiff", SHELL, REALUSER, PROGPATH(sccsdiff), ! 174: "edit", CMACRO, NO_SDOT, "get -e", ! 175: "delget", CMACRO, NO_SDOT, "delta:mysrp/get:ixbeskcl -t", ! 176: "deledit", CMACRO, NO_SDOT, "delta:mysrp -n/get:ixbskcl -e -t -g", ! 177: "fix", FIX, NO_SDOT, NULL, ! 178: "clean", CLEAN, REALUSER|NO_SDOT, (char *) CLEANC, ! 179: "info", CLEAN, REALUSER|NO_SDOT, (char *) INFOC, ! 180: "check", CLEAN, REALUSER|NO_SDOT, (char *) CHECKC, ! 181: "tell", CLEAN, REALUSER|NO_SDOT, (char *) TELLC, ! 182: "unedit", UNEDIT, NO_SDOT, NULL, ! 183: "diffs", DIFFS, NO_SDOT|REALUSER, NULL, ! 184: "-diff", DODIFF, NO_SDOT|REALUSER, PROGPATH(bdiff), ! 185: "print", CMACRO, 0, "prt -e/get -p -m -s", ! 186: "branch", CMACRO, NO_SDOT, ! 187: "get:ixrc -e -b/delta: -s -n -ybranch-place-holder/get:pl -e -t -g", ! 188: "enter", ENTER, NO_SDOT, NULL, ! 189: "create", CMACRO, NO_SDOT, "enter/get:ixbeskcl -t", ! 190: NULL, -1, 0, NULL ! 191: }; ! 192: ! 193: /* one line from a p-file */ ! 194: struct pfile ! 195: { ! 196: char *p_osid; /* old SID */ ! 197: char *p_nsid; /* new SID */ ! 198: char *p_user; /* user who did edit */ ! 199: char *p_date; /* date of get */ ! 200: char *p_time; /* time of get */ ! 201: char *p_aux; /* extra info at end */ ! 202: }; ! 203: ! 204: char *SccsPath = SCCSPATH; /* pathname of SCCS files */ ! 205: # ifdef SCCSDIR ! 206: char *SccsDir = SCCSDIR; /* directory to begin search from */ ! 207: # else ! 208: char *SccsDir = ""; ! 209: # endif ! 210: char MyName[] = MYNAME; /* name used in messages */ ! 211: int OutFile = -1; /* override output file for commands */ ! 212: bool RealUser; /* if set, running as real user */ ! 213: # ifdef DEBUG ! 214: bool Debug; /* turn on tracing */ ! 215: # endif ! 216: # ifndef V6 ! 217: extern char *getenv(); ! 218: # endif V6 ! 219: ! 220: char *gstrcat(), *strcat(); ! 221: char *gstrncat(), *strncat(); ! 222: char *gstrcpy(), *strcpy(); ! 223: #define FBUFSIZ BUFSIZ ! 224: #define PFILELG 120 ! 225: ! 226: main(argc, argv) ! 227: int argc; ! 228: char **argv; ! 229: { ! 230: register char *p; ! 231: extern struct sccsprog *lookup(); ! 232: register int i; ! 233: # ifndef V6 ! 234: # ifndef SCCSDIR ! 235: register struct passwd *pw; ! 236: extern struct passwd *getpwnam(); ! 237: char buf[FBUFSIZ]; ! 238: ! 239: /* pull "SccsDir" out of the environment (possibly) */ ! 240: p = getenv("PROJECTDIR"); ! 241: if (p != NULL && p[0] != '\0') ! 242: { ! 243: if (p[0] == '/') ! 244: SccsDir = p; ! 245: else ! 246: { ! 247: pw = getpwnam(p); ! 248: if (pw == NULL) ! 249: { ! 250: usrerr("user %s does not exist", p); ! 251: exit(EX_USAGE); ! 252: } ! 253: gstrcpy(buf, pw->pw_dir, sizeof(buf)); ! 254: gstrcat(buf, "/src", sizeof(buf)); ! 255: if (access(buf, 0) < 0) ! 256: { ! 257: gstrcpy(buf, pw->pw_dir, sizeof(buf)); ! 258: gstrcat(buf, "/source", sizeof(buf)); ! 259: if (access(buf, 0) < 0) ! 260: { ! 261: usrerr("project %s has no source!", p); ! 262: exit(EX_USAGE); ! 263: } ! 264: } ! 265: SccsDir = buf; ! 266: } ! 267: } ! 268: # endif SCCSDIR ! 269: # endif V6 ! 270: ! 271: /* ! 272: ** Detect and decode flags intended for this program. ! 273: */ ! 274: ! 275: if (argc < 2) ! 276: { ! 277: fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); ! 278: exit(EX_USAGE); ! 279: } ! 280: argv[argc] = NULL; ! 281: ! 282: if (lookup(argv[0]) == NULL) ! 283: { ! 284: while ((p = *++argv) != NULL) ! 285: { ! 286: if (*p != '-') ! 287: break; ! 288: switch (*++p) ! 289: { ! 290: case 'r': /* run as real user */ ! 291: setuid(getuid()); ! 292: RealUser++; ! 293: break; ! 294: ! 295: # ifndef SCCSDIR ! 296: case 'p': /* path of sccs files */ ! 297: SccsPath = ++p; ! 298: if (SccsPath[0] == '\0' && argv[1] != NULL) ! 299: SccsPath = *++argv; ! 300: break; ! 301: ! 302: case 'd': /* directory to search from */ ! 303: SccsDir = ++p; ! 304: if (SccsDir[0] == '\0' && argv[1] != NULL) ! 305: SccsDir = *++argv; ! 306: break; ! 307: # endif ! 308: ! 309: # ifdef DEBUG ! 310: case 'T': /* trace */ ! 311: Debug++; ! 312: break; ! 313: # endif ! 314: ! 315: default: ! 316: usrerr("unknown option -%s", p); ! 317: break; ! 318: } ! 319: } ! 320: if (SccsPath[0] == '\0') ! 321: SccsPath = "."; ! 322: } ! 323: ! 324: i = command(argv, FALSE, ""); ! 325: exit(i); ! 326: } ! 327: ! 328: /* ! 329: ** COMMAND -- look up and perform a command ! 330: ** ! 331: ** This routine is the guts of this program. Given an ! 332: ** argument vector, it looks up the "command" (argv[0]) ! 333: ** in the configuration table and does the necessary stuff. ! 334: ** ! 335: ** Parameters: ! 336: ** argv -- an argument vector to process. ! 337: ** forkflag -- if set, fork before executing the command. ! 338: ** editflag -- if set, only include flags listed in the ! 339: ** sccsklets field of the command descriptor. ! 340: ** arg0 -- a space-seperated list of arguments to insert ! 341: ** before argv. ! 342: ** ! 343: ** Returns: ! 344: ** zero -- command executed ok. ! 345: ** else -- error status. ! 346: ** ! 347: ** Side Effects: ! 348: ** none. ! 349: */ ! 350: ! 351: command(argv, forkflag, arg0) ! 352: char **argv; ! 353: bool forkflag; ! 354: char *arg0; ! 355: { ! 356: register struct sccsprog *cmd; ! 357: register char *p; ! 358: char buf[FBUFSIZ]; ! 359: extern struct sccsprog *lookup(); ! 360: char *nav[1000]; ! 361: char **np; ! 362: register char **ap; ! 363: register int i; ! 364: register char *q; ! 365: extern bool unedit(); ! 366: int rval = 0; ! 367: extern char *index(); ! 368: extern char *makefile(); ! 369: char *editchs; ! 370: extern char *tail(); ! 371: ! 372: # ifdef DEBUG ! 373: if (Debug) ! 374: { ! 375: printf("command:\n\t\"%s\"\n", arg0); ! 376: for (np = argv; *np != NULL; np++) ! 377: printf("\t\"%s\"\n", *np); ! 378: } ! 379: # endif ! 380: ! 381: /* ! 382: ** Copy arguments. ! 383: ** Copy from arg0 & if necessary at most one arg ! 384: ** from argv[0]. ! 385: */ ! 386: ! 387: np = ap = &nav[1]; ! 388: editchs = NULL; ! 389: for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) ! 390: { ! 391: *np++ = q; ! 392: while (*p == ' ') ! 393: p++; ! 394: while (*p != ' ' && *p != '\0' && *p != '/' && *p != ':') ! 395: *q++ = *p++; ! 396: *q++ = '\0'; ! 397: if (*p == ':') ! 398: { ! 399: editchs = q; ! 400: while (*++p != '\0' && *p != '/' && *p != ' ') ! 401: *q++ = *p; ! 402: *q++ = '\0'; ! 403: } ! 404: } ! 405: *np = NULL; ! 406: if (*ap == NULL) ! 407: *np++ = *argv++; ! 408: ! 409: /* ! 410: ** Look up command. ! 411: ** At this point, *ap is the command name. ! 412: */ ! 413: ! 414: cmd = lookup(*ap); ! 415: if (cmd == NULL) ! 416: { ! 417: usrerr("Unknown command \"%s\"", *ap); ! 418: return (EX_USAGE); ! 419: } ! 420: ! 421: /* ! 422: ** Copy remaining arguments doing editing as appropriate. ! 423: */ ! 424: ! 425: for (; *argv != NULL; argv++) ! 426: { ! 427: p = *argv; ! 428: if (*p == '-') ! 429: { ! 430: if (p[1] == '\0' || editchs == NULL || index(editchs, p[1]) != NULL) ! 431: *np++ = p; ! 432: } ! 433: else ! 434: { ! 435: if (!bitset(NO_SDOT, cmd->sccsflags)) ! 436: p = makefile(p); ! 437: if (p != NULL) ! 438: *np++ = p; ! 439: } ! 440: } ! 441: *np = NULL; ! 442: ! 443: /* ! 444: ** Interpret operation associated with this command. ! 445: */ ! 446: ! 447: switch (cmd->sccsoper) ! 448: { ! 449: case SHELL: /* call a shell file */ ! 450: *ap = cmd->sccspath; ! 451: *--ap = "sh"; ! 452: rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag); ! 453: break; ! 454: ! 455: case PROG: /* call an sccs prog */ ! 456: rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); ! 457: break; ! 458: ! 459: case CMACRO: /* command macro */ ! 460: /* step through & execute each part of the macro */ ! 461: for (p = cmd->sccspath; *p != '\0'; p++) ! 462: { ! 463: q = p; ! 464: while (*p != '\0' && *p != '/') ! 465: p++; ! 466: rval = command(&ap[1], *p != '\0', q); ! 467: if (rval != 0) ! 468: break; ! 469: } ! 470: break; ! 471: ! 472: case FIX: /* fix a delta */ ! 473: if (strncmp(ap[1], "-r", 2) != 0) ! 474: { ! 475: usrerr("-r flag needed for fix command"); ! 476: rval = EX_USAGE; ! 477: break; ! 478: } ! 479: ! 480: /* get the version with all changes */ ! 481: rval = command(&ap[1], TRUE, "get -k"); ! 482: ! 483: /* now remove that version from the s-file */ ! 484: if (rval == 0) ! 485: rval = command(&ap[1], TRUE, "rmdel:r"); ! 486: ! 487: /* and edit the old version (but don't clobber new vers) */ ! 488: if (rval == 0) ! 489: rval = command(&ap[2], FALSE, "get -e -g"); ! 490: break; ! 491: ! 492: case CLEAN: ! 493: rval = clean((int) cmd->sccspath, ap); ! 494: break; ! 495: ! 496: case UNEDIT: ! 497: for (argv = np = &ap[1]; *argv != NULL; argv++) ! 498: { ! 499: if (unedit(*argv)) ! 500: *np++ = *argv; ! 501: } ! 502: *np = NULL; ! 503: ! 504: /* get all the files that we unedited successfully */ ! 505: if (np > &ap[1]) ! 506: rval = command(&ap[1], FALSE, "get"); ! 507: break; ! 508: ! 509: case DIFFS: /* diff between s-file & edit file */ ! 510: /* find the end of the flag arguments */ ! 511: for (np = &ap[1]; *np != NULL && **np == '-'; np++) ! 512: continue; ! 513: argv = np; ! 514: ! 515: /* for each file, do the diff */ ! 516: p = argv[1]; ! 517: while (*np != NULL) ! 518: { ! 519: /* messy, but we need a null terminated argv */ ! 520: *argv = *np++; ! 521: argv[1] = NULL; ! 522: i = dodiff(ap, tail(*argv)); ! 523: if (rval == 0) ! 524: rval = i; ! 525: argv[1] = p; ! 526: } ! 527: break; ! 528: ! 529: case DODIFF: /* internal diff call */ ! 530: setuid(getuid()); ! 531: for (np = ap; *np != NULL; np++) ! 532: { ! 533: if ((*np)[0] == '-' && (*np)[1] == 'C') ! 534: (*np)[1] = 'c'; ! 535: } ! 536: ! 537: /* insert "-" argument */ ! 538: np[1] = NULL; ! 539: np[0] = np[-1]; ! 540: np[-1] = "-"; ! 541: ! 542: /* execute the diff program of choice */ ! 543: # ifndef V6 ! 544: execvp("diff", ap); ! 545: # endif ! 546: execv(cmd->sccspath, argv); ! 547: syserr("cannot exec %s", cmd->sccspath); ! 548: exit(EX_OSERR); ! 549: ! 550: case ENTER: /* enter new sccs files */ ! 551: /* skip over flag arguments */ ! 552: for (np = &ap[1]; *np != NULL && **np == '-'; np++) ! 553: continue; ! 554: argv = np; ! 555: ! 556: /* do an admin for each file */ ! 557: p = argv[1]; ! 558: while (*np != NULL) ! 559: { ! 560: printf("\n%s:\n", *np); ! 561: strcpy(buf, "-i"); ! 562: gstrcat(buf, *np, sizeof(buf)); ! 563: ap[0] = buf; ! 564: argv[0] = tail(*np); ! 565: argv[1] = NULL; ! 566: rval = command(ap, TRUE, "admin"); ! 567: argv[1] = p; ! 568: if (rval == 0) ! 569: { ! 570: strcpy(buf, ","); ! 571: gstrcat(buf, tail(*np), sizeof(buf)); ! 572: if (link(*np, buf) >= 0) ! 573: unlink(*np); ! 574: } ! 575: np++; ! 576: } ! 577: break; ! 578: ! 579: default: ! 580: syserr("oper %d", cmd->sccsoper); ! 581: exit(EX_SOFTWARE); ! 582: } ! 583: # ifdef DEBUG ! 584: if (Debug) ! 585: printf("command: rval=%d\n", rval); ! 586: # endif ! 587: return (rval); ! 588: } ! 589: ! 590: /* ! 591: ** LOOKUP -- look up an SCCS command name. ! 592: ** ! 593: ** Parameters: ! 594: ** name -- the name of the command to look up. ! 595: ** ! 596: ** Returns: ! 597: ** ptr to command descriptor for this command. ! 598: ** NULL if no such entry. ! 599: ** ! 600: ** Side Effects: ! 601: ** none. ! 602: */ ! 603: ! 604: struct sccsprog * ! 605: lookup(name) ! 606: char *name; ! 607: { ! 608: register struct sccsprog *cmd; ! 609: ! 610: for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) ! 611: { ! 612: if (strcmp(cmd->sccsname, name) == 0) ! 613: return (cmd); ! 614: } ! 615: return (NULL); ! 616: } ! 617: ! 618: /* ! 619: ** CALLPROG -- call a program ! 620: ** ! 621: ** Used to call the SCCS programs. ! 622: ** ! 623: ** Parameters: ! 624: ** progpath -- pathname of the program to call. ! 625: ** flags -- status flags from the command descriptors. ! 626: ** argv -- an argument vector to pass to the program. ! 627: ** forkflag -- if true, fork before calling, else just ! 628: ** exec. ! 629: ** ! 630: ** Returns: ! 631: ** The exit status of the program. ! 632: ** Nothing if forkflag == FALSE. ! 633: ** ! 634: ** Side Effects: ! 635: ** Can exit if forkflag == FALSE. ! 636: */ ! 637: ! 638: callprog(progpath, flags, argv, forkflag) ! 639: char *progpath; ! 640: short flags; ! 641: char **argv; ! 642: bool forkflag; ! 643: { ! 644: register int i; ! 645: auto int st; ! 646: ! 647: # ifdef DEBUG ! 648: if (Debug) ! 649: { ! 650: printf("callprog:\n"); ! 651: for (i = 0; argv[i] != NULL; i++) ! 652: printf("\t\"%s\"\n", argv[i]); ! 653: } ! 654: # endif ! 655: ! 656: if (*argv == NULL) ! 657: return (-1); ! 658: ! 659: /* ! 660: ** Fork if appropriate. ! 661: */ ! 662: ! 663: if (forkflag) ! 664: { ! 665: # ifdef DEBUG ! 666: if (Debug) ! 667: printf("Forking\n"); ! 668: # endif ! 669: i = fork(); ! 670: if (i < 0) ! 671: { ! 672: syserr("cannot fork"); ! 673: exit(EX_OSERR); ! 674: } ! 675: else if (i > 0) ! 676: { ! 677: wait(&st); ! 678: if ((st & 0377) == 0) ! 679: st = (st >> 8) & 0377; ! 680: if (OutFile >= 0) ! 681: { ! 682: close(OutFile); ! 683: OutFile = -1; ! 684: } ! 685: return (st); ! 686: } ! 687: } ! 688: else if (OutFile >= 0) ! 689: { ! 690: syserr("callprog: setting stdout w/o forking"); ! 691: exit(EX_SOFTWARE); ! 692: } ! 693: ! 694: /* set protection as appropriate */ ! 695: if (bitset(REALUSER, flags)) ! 696: setuid(getuid()); ! 697: ! 698: /* change standard input & output if needed */ ! 699: if (OutFile >= 0) ! 700: { ! 701: close(1); ! 702: dup(OutFile); ! 703: close(OutFile); ! 704: } ! 705: ! 706: /* call real SCCS program */ ! 707: execv(progpath, argv); ! 708: syserr("cannot execute %s", progpath); ! 709: exit(EX_UNAVAILABLE); ! 710: /*NOTREACHED*/ ! 711: } ! 712: ! 713: /* ! 714: ** MAKEFILE -- make filename of SCCS file ! 715: ** ! 716: ** If the name passed is already the name of an SCCS file, ! 717: ** just return it. Otherwise, munge the name into the name ! 718: ** of the actual SCCS file. ! 719: ** ! 720: ** There are cases when it is not clear what you want to ! 721: ** do. For example, if SccsPath is an absolute pathname ! 722: ** and the name given is also an absolute pathname, we go ! 723: ** for SccsPath (& only use the last component of the name ! 724: ** passed) -- this is important for security reasons (if ! 725: ** sccs is being used as a setuid front end), but not ! 726: ** particularly intuitive. ! 727: ** ! 728: ** Parameters: ! 729: ** name -- the file name to be munged. ! 730: ** ! 731: ** Returns: ! 732: ** The pathname of the sccs file. ! 733: ** NULL on error. ! 734: ** ! 735: ** Side Effects: ! 736: ** none. ! 737: */ ! 738: ! 739: char * ! 740: makefile(name) ! 741: char *name; ! 742: { ! 743: register char *p; ! 744: char buf[3*FBUFSIZ]; ! 745: extern char *malloc(); ! 746: extern char *rindex(); ! 747: extern bool safepath(); ! 748: extern bool isdir(); ! 749: register char *q; ! 750: ! 751: p = rindex(name, '/'); ! 752: if (p == NULL) ! 753: p = name; ! 754: else ! 755: p++; ! 756: ! 757: /* ! 758: ** Check to see that the path is "safe", i.e., that we ! 759: ** are not letting some nasty person use the setuid part ! 760: ** of this program to look at or munge some presumably ! 761: ** hidden files. ! 762: */ ! 763: ! 764: if (SccsDir[0] == '/' && !safepath(name)) ! 765: return (NULL); ! 766: ! 767: /* ! 768: ** Create the base pathname. ! 769: */ ! 770: ! 771: /* first the directory part */ ! 772: if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) ! 773: { ! 774: gstrcpy(buf, SccsDir, sizeof(buf)); ! 775: gstrcat(buf, "/", sizeof(buf)); ! 776: } ! 777: else ! 778: gstrcpy(buf, "", sizeof(buf)); ! 779: ! 780: /* then the head of the pathname */ ! 781: gstrncat(buf, name, p - name, sizeof(buf)); ! 782: q = &buf[strlen(buf)]; ! 783: ! 784: /* now copy the final part of the name, in case useful */ ! 785: gstrcpy(q, p, sizeof(buf)); ! 786: ! 787: /* so is it useful? */ ! 788: if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) ! 789: { ! 790: /* sorry, no; copy the SCCS pathname & the "s." */ ! 791: gstrcpy(q, SccsPath, sizeof(buf)); ! 792: gstrcat(buf, "/s.", sizeof(buf)); ! 793: ! 794: /* and now the end of the name */ ! 795: gstrcat(buf, p, sizeof(buf)); ! 796: } ! 797: ! 798: /* if i haven't changed it, why did I do all this? */ ! 799: if (strcmp(buf, name) == 0) ! 800: p = name; ! 801: else ! 802: { ! 803: /* but if I have, squirrel it away */ ! 804: p = malloc(strlen(buf) + 1); ! 805: if (p == NULL) ! 806: { ! 807: perror("Sccs: no mem"); ! 808: exit(EX_OSERR); ! 809: } ! 810: strcpy(p, buf); ! 811: } ! 812: ! 813: return (p); ! 814: } ! 815: ! 816: /* ! 817: ** ISDIR -- return true if the argument is a directory. ! 818: ** ! 819: ** Parameters: ! 820: ** name -- the pathname of the file to check. ! 821: ** ! 822: ** Returns: ! 823: ** TRUE if 'name' is a directory, FALSE otherwise. ! 824: ** ! 825: ** Side Effects: ! 826: ** none. ! 827: */ ! 828: ! 829: bool ! 830: isdir(name) ! 831: char *name; ! 832: { ! 833: struct stat stbuf; ! 834: ! 835: return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); ! 836: } ! 837: ! 838: /* ! 839: ** SAFEPATH -- determine whether a pathname is "safe" ! 840: ** ! 841: ** "Safe" pathnames only allow you to get deeper into the ! 842: ** directory structure, i.e., full pathnames and ".." are ! 843: ** not allowed. ! 844: ** ! 845: ** Parameters: ! 846: ** p -- the name to check. ! 847: ** ! 848: ** Returns: ! 849: ** TRUE -- if the path is safe. ! 850: ** FALSE -- if the path is not safe. ! 851: ** ! 852: ** Side Effects: ! 853: ** Prints a message if the path is not safe. ! 854: */ ! 855: ! 856: bool ! 857: safepath(p) ! 858: register char *p; ! 859: { ! 860: extern char *index(); ! 861: ! 862: if (*p != '/') ! 863: { ! 864: while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) ! 865: { ! 866: p = index(p, '/'); ! 867: if (p == NULL) ! 868: return (TRUE); ! 869: p++; ! 870: } ! 871: } ! 872: ! 873: printf("You may not use full pathnames or \"..\"\n"); ! 874: return (FALSE); ! 875: } ! 876: ! 877: /* ! 878: ** CLEAN -- clean out recreatable files ! 879: ** ! 880: ** Any file for which an "s." file exists but no "p." file ! 881: ** exists in the current directory is purged. ! 882: ** ! 883: ** Parameters: ! 884: ** mode -- tells whether this came from a "clean", "info", or ! 885: ** "check" command. ! 886: ** argv -- the rest of the argument vector. ! 887: ** ! 888: ** Returns: ! 889: ** none. ! 890: ** ! 891: ** Side Effects: ! 892: ** Removes files in the current directory. ! 893: ** Prints information regarding files being edited. ! 894: ** Exits if a "check" command. ! 895: */ ! 896: ! 897: clean(mode, argv) ! 898: int mode; ! 899: char **argv; ! 900: { ! 901: struct direct *dir; ! 902: char buf[FBUFSIZ]; ! 903: char *bufend; ! 904: register DIR *dirfd; ! 905: register char *basefile; ! 906: bool gotedit; ! 907: bool gotpfent; ! 908: FILE *pfp; ! 909: bool nobranch = FALSE; ! 910: extern struct pfile *getpfent(); ! 911: register struct pfile *pf; ! 912: register char **ap; ! 913: extern char *username(); ! 914: char *usernm = NULL; ! 915: char *subdir = NULL; ! 916: char *cmdname; ! 917: ! 918: /* ! 919: ** Process the argv ! 920: */ ! 921: ! 922: cmdname = *argv; ! 923: for (ap = argv; *++ap != NULL; ) ! 924: { ! 925: if (**ap == '-') ! 926: { ! 927: /* we have a flag */ ! 928: switch ((*ap)[1]) ! 929: { ! 930: case 'b': ! 931: nobranch = TRUE; ! 932: break; ! 933: ! 934: case 'u': ! 935: if ((*ap)[2] != '\0') ! 936: usernm = &(*ap)[2]; ! 937: else if (ap[1] != NULL && ap[1][0] != '-') ! 938: usernm = *++ap; ! 939: else ! 940: usernm = username(); ! 941: break; ! 942: } ! 943: } ! 944: else ! 945: { ! 946: if (subdir != NULL) ! 947: usrerr("too many args"); ! 948: else ! 949: subdir = *ap; ! 950: } ! 951: } ! 952: ! 953: /* ! 954: ** Find and open the SCCS directory. ! 955: */ ! 956: ! 957: gstrcpy(buf, SccsDir, sizeof(buf)); ! 958: if (buf[0] != '\0') ! 959: gstrcat(buf, "/", sizeof(buf)); ! 960: if (subdir != NULL) ! 961: { ! 962: gstrcat(buf, subdir, sizeof(buf)); ! 963: gstrcat(buf, "/", sizeof(buf)); ! 964: } ! 965: gstrcat(buf, SccsPath, sizeof(buf)); ! 966: bufend = &buf[strlen(buf)]; ! 967: ! 968: dirfd = opendir(buf); ! 969: if (dirfd == NULL) ! 970: { ! 971: usrerr("cannot open %s", buf); ! 972: return (EX_NOINPUT); ! 973: } ! 974: ! 975: /* ! 976: ** Scan the SCCS directory looking for s. files. ! 977: ** gotedit tells whether we have tried to clean any ! 978: ** files that are being edited. ! 979: */ ! 980: ! 981: gotedit = FALSE; ! 982: while (dir = readdir(dirfd)) { ! 983: if (strncmp(dir->d_name, "s.", 2) != 0) ! 984: continue; ! 985: ! 986: /* got an s. file -- see if the p. file exists */ ! 987: gstrcpy(bufend, "/p.", sizeof(buf)); ! 988: basefile = bufend + 3; ! 989: gstrcpy(basefile, &dir->d_name[2], sizeof(buf)); ! 990: ! 991: /* ! 992: ** open and scan the p-file. ! 993: ** 'gotpfent' tells if we have found a valid p-file ! 994: ** entry. ! 995: */ ! 996: ! 997: pfp = fopen(buf, "r"); ! 998: gotpfent = FALSE; ! 999: if (pfp != NULL) ! 1000: { ! 1001: /* the file exists -- report it's contents */ ! 1002: while ((pf = getpfent(pfp)) != NULL) ! 1003: { ! 1004: if (nobranch && isbranch(pf->p_nsid)) ! 1005: continue; ! 1006: if (usernm != NULL && strcmp(usernm, pf->p_user) != 0 && mode != CLEANC) ! 1007: continue; ! 1008: gotedit = TRUE; ! 1009: gotpfent = TRUE; ! 1010: if (mode == TELLC) ! 1011: { ! 1012: printf("%s\n", basefile); ! 1013: break; ! 1014: } ! 1015: printf("%12s: being edited: ", basefile); ! 1016: putpfent(pf, stdout); ! 1017: } ! 1018: fclose(pfp); ! 1019: } ! 1020: ! 1021: /* the s. file exists and no p. file exists -- unlink the g-file */ ! 1022: if (mode == CLEANC && !gotpfent) ! 1023: { ! 1024: char unlinkbuf[FBUFSIZ]; ! 1025: gstrcpy(unlinkbuf, &dir->d_name[2], sizeof(unlinkbuf)); ! 1026: unlink(unlinkbuf); ! 1027: } ! 1028: } ! 1029: ! 1030: /* cleanup & report results */ ! 1031: closedir(dirfd); ! 1032: if (!gotedit && mode == INFOC) ! 1033: { ! 1034: printf("Nothing being edited"); ! 1035: if (nobranch) ! 1036: printf(" (on trunk)"); ! 1037: if (usernm == NULL) ! 1038: printf("\n"); ! 1039: else ! 1040: printf(" by %s\n", usernm); ! 1041: } ! 1042: if (mode == CHECKC) ! 1043: exit(gotedit); ! 1044: return (EX_OK); ! 1045: } ! 1046: ! 1047: /* ! 1048: ** ISBRANCH -- is the SID a branch? ! 1049: ** ! 1050: ** Parameters: ! 1051: ** sid -- the sid to check. ! 1052: ** ! 1053: ** Returns: ! 1054: ** TRUE if the sid represents a branch. ! 1055: ** FALSE otherwise. ! 1056: ** ! 1057: ** Side Effects: ! 1058: ** none. ! 1059: */ ! 1060: ! 1061: isbranch(sid) ! 1062: char *sid; ! 1063: { ! 1064: register char *p; ! 1065: int dots; ! 1066: ! 1067: dots = 0; ! 1068: for (p = sid; *p != '\0'; p++) ! 1069: { ! 1070: if (*p == '.') ! 1071: dots++; ! 1072: if (dots > 1) ! 1073: return (TRUE); ! 1074: } ! 1075: return (FALSE); ! 1076: } ! 1077: ! 1078: /* ! 1079: ** UNEDIT -- unedit a file ! 1080: ** ! 1081: ** Checks to see that the current user is actually editting ! 1082: ** the file and arranges that s/he is not editting it. ! 1083: ** ! 1084: ** Parameters: ! 1085: ** fn -- the name of the file to be unedited. ! 1086: ** ! 1087: ** Returns: ! 1088: ** TRUE -- if the file was successfully unedited. ! 1089: ** FALSE -- if the file was not unedited for some ! 1090: ** reason. ! 1091: ** ! 1092: ** Side Effects: ! 1093: ** fn is removed ! 1094: ** entries are removed from pfile. ! 1095: */ ! 1096: ! 1097: bool ! 1098: unedit(fn) ! 1099: char *fn; ! 1100: { ! 1101: register FILE *pfp; ! 1102: char *cp, *pfn; ! 1103: static char tfn[] = "/tmp/sccsXXXXX"; ! 1104: FILE *tfp; ! 1105: register char *q; ! 1106: bool delete = FALSE; ! 1107: bool others = FALSE; ! 1108: char *myname; ! 1109: extern char *username(); ! 1110: struct pfile *pent; ! 1111: extern struct pfile *getpfent(); ! 1112: char buf[PFILELG]; ! 1113: extern char *makefile(); ! 1114: ! 1115: /* make "s." filename & find the trailing component */ ! 1116: pfn = makefile(fn); ! 1117: if (pfn == NULL) ! 1118: return (FALSE); ! 1119: q = rindex(pfn, '/'); ! 1120: if (q == NULL) ! 1121: q = &pfn[-1]; ! 1122: if (q[1] != 's' || q[2] != '.') ! 1123: { ! 1124: usrerr("bad file name \"%s\"", fn); ! 1125: return (FALSE); ! 1126: } ! 1127: ! 1128: /* turn "s." into "p." & try to open it */ ! 1129: *++q = 'p'; ! 1130: ! 1131: pfp = fopen(pfn, "r"); ! 1132: if (pfp == NULL) ! 1133: { ! 1134: printf("%12s: not being edited\n", fn); ! 1135: return (FALSE); ! 1136: } ! 1137: ! 1138: /* create temp file for editing p-file */ ! 1139: mktemp(tfn); ! 1140: tfp = fopen(tfn, "w"); ! 1141: if (tfp == NULL) ! 1142: { ! 1143: usrerr("cannot create \"%s\"", tfn); ! 1144: exit(EX_OSERR); ! 1145: } ! 1146: ! 1147: /* figure out who I am */ ! 1148: myname = username(); ! 1149: ! 1150: /* ! 1151: ** Copy p-file to temp file, doing deletions as needed. ! 1152: */ ! 1153: ! 1154: while ((pent = getpfent(pfp)) != NULL) ! 1155: { ! 1156: if (strcmp(pent->p_user, myname) == 0) ! 1157: { ! 1158: /* a match */ ! 1159: delete++; ! 1160: } ! 1161: else ! 1162: { ! 1163: /* output it again */ ! 1164: putpfent(pent, tfp); ! 1165: others++; ! 1166: } ! 1167: } ! 1168: ! 1169: /* ! 1170: * Before changing anything, make sure we can remove ! 1171: * the file in question (assuming it exists). ! 1172: */ ! 1173: if (delete) { ! 1174: extern int errno; ! 1175: ! 1176: cp = tail(fn); ! 1177: errno = 0; ! 1178: if (access(cp, 0) < 0 && errno != ENOENT) ! 1179: goto bad; ! 1180: if (errno == 0) ! 1181: /* ! 1182: * This is wrong, but the rest of the program ! 1183: * has built in assumptions about "." as well, ! 1184: * so why make unedit a special case? ! 1185: */ ! 1186: if (access(".", 2) < 0) { ! 1187: bad: ! 1188: printf("%12s: can't remove\n", cp); ! 1189: fclose(tfp); ! 1190: fclose(pfp); ! 1191: unlink(tfn); ! 1192: return (FALSE); ! 1193: } ! 1194: } ! 1195: /* do final cleanup */ ! 1196: if (others) ! 1197: { ! 1198: /* copy it back (perhaps it should be linked?) */ ! 1199: if (freopen(tfn, "r", tfp) == NULL) ! 1200: { ! 1201: syserr("cannot reopen \"%s\"", tfn); ! 1202: exit(EX_OSERR); ! 1203: } ! 1204: if (freopen(pfn, "w", pfp) == NULL) ! 1205: { ! 1206: usrerr("cannot create \"%s\"", pfn); ! 1207: return (FALSE); ! 1208: } ! 1209: while (fgets(buf, sizeof buf, tfp) != NULL) ! 1210: fputs(buf, pfp); ! 1211: } ! 1212: else ! 1213: { ! 1214: /* it's empty -- remove it */ ! 1215: unlink(pfn); ! 1216: } ! 1217: fclose(tfp); ! 1218: fclose(pfp); ! 1219: unlink(tfn); ! 1220: ! 1221: /* actually remove the g-file */ ! 1222: if (delete) ! 1223: { ! 1224: /* ! 1225: * Since we've checked above, we can ! 1226: * use the return from unlink to ! 1227: * determine if the file existed or not. ! 1228: */ ! 1229: if (unlink(cp) >= 0) ! 1230: printf("%12s: removed\n", cp); ! 1231: return (TRUE); ! 1232: } ! 1233: else ! 1234: { ! 1235: printf("%12s: not being edited by you\n", fn); ! 1236: return (FALSE); ! 1237: } ! 1238: } ! 1239: ! 1240: /* ! 1241: ** DODIFF -- diff an s-file against a g-file ! 1242: ** ! 1243: ** Parameters: ! 1244: ** getv -- argv for the 'get' command. ! 1245: ** gfile -- name of the g-file to diff against. ! 1246: ** ! 1247: ** Returns: ! 1248: ** Result of get. ! 1249: ** ! 1250: ** Side Effects: ! 1251: ** none. ! 1252: */ ! 1253: ! 1254: dodiff(getv, gfile) ! 1255: char **getv; ! 1256: char *gfile; ! 1257: { ! 1258: int pipev[2]; ! 1259: int rval; ! 1260: register int i; ! 1261: register int pid; ! 1262: auto int st; ! 1263: extern int errno; ! 1264: int (*osig)(); ! 1265: ! 1266: printf("\n------- %s -------\n", gfile); ! 1267: fflush(stdout); ! 1268: ! 1269: /* create context for diff to run in */ ! 1270: if (pipe(pipev) < 0) ! 1271: { ! 1272: syserr("dodiff: pipe failed"); ! 1273: exit(EX_OSERR); ! 1274: } ! 1275: if ((pid = fork()) < 0) ! 1276: { ! 1277: syserr("dodiff: fork failed"); ! 1278: exit(EX_OSERR); ! 1279: } ! 1280: else if (pid > 0) ! 1281: { ! 1282: /* in parent; run get */ ! 1283: OutFile = pipev[1]; ! 1284: close(pipev[0]); ! 1285: rval = command(&getv[1], TRUE, "get:rcixt -s -k -p"); ! 1286: osig = signal(SIGINT, SIG_IGN); ! 1287: while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR) ! 1288: errno = 0; ! 1289: signal(SIGINT, osig); ! 1290: /* ignore result of diff */ ! 1291: } ! 1292: else ! 1293: { ! 1294: /* in child, run diff */ ! 1295: if (close(pipev[1]) < 0 || close(0) < 0 || ! 1296: dup(pipev[0]) != 0 || close(pipev[0]) < 0) ! 1297: { ! 1298: syserr("dodiff: magic failed"); ! 1299: exit(EX_OSERR); ! 1300: } ! 1301: command(&getv[1], FALSE, "-diff:elsfhbC"); ! 1302: } ! 1303: return (rval); ! 1304: } ! 1305: ! 1306: /* ! 1307: ** TAIL -- return tail of filename. ! 1308: ** ! 1309: ** Parameters: ! 1310: ** fn -- the filename. ! 1311: ** ! 1312: ** Returns: ! 1313: ** a pointer to the tail of the filename; e.g., given ! 1314: ** "cmd/ls.c", "ls.c" is returned. ! 1315: ** ! 1316: ** Side Effects: ! 1317: ** none. ! 1318: */ ! 1319: ! 1320: char * ! 1321: tail(fn) ! 1322: register char *fn; ! 1323: { ! 1324: register char *p; ! 1325: ! 1326: for (p = fn; *p != 0; p++) ! 1327: if (*p == '/' && p[1] != '\0' && p[1] != '/') ! 1328: fn = &p[1]; ! 1329: return (fn); ! 1330: } ! 1331: ! 1332: /* ! 1333: ** GETPFENT -- get an entry from the p-file ! 1334: ** ! 1335: ** Parameters: ! 1336: ** pfp -- p-file file pointer ! 1337: ** ! 1338: ** Returns: ! 1339: ** pointer to p-file struct for next entry ! 1340: ** NULL on EOF or error ! 1341: ** ! 1342: ** Side Effects: ! 1343: ** Each call wipes out results of previous call. ! 1344: */ ! 1345: ! 1346: struct pfile * ! 1347: getpfent(pfp) ! 1348: FILE *pfp; ! 1349: { ! 1350: static struct pfile ent; ! 1351: static char buf[PFILELG]; ! 1352: register char *p; ! 1353: extern char *nextfield(); ! 1354: ! 1355: if (fgets(buf, sizeof buf, pfp) == NULL) ! 1356: return (NULL); ! 1357: ! 1358: ent.p_osid = p = buf; ! 1359: ent.p_nsid = p = nextfield(p); ! 1360: ent.p_user = p = nextfield(p); ! 1361: ent.p_date = p = nextfield(p); ! 1362: ent.p_time = p = nextfield(p); ! 1363: ent.p_aux = p = nextfield(p); ! 1364: ! 1365: return (&ent); ! 1366: } ! 1367: ! 1368: ! 1369: char * ! 1370: nextfield(p) ! 1371: register char *p; ! 1372: { ! 1373: if (p == NULL || *p == '\0') ! 1374: return (NULL); ! 1375: while (*p != ' ' && *p != '\n' && *p != '\0') ! 1376: p++; ! 1377: if (*p == '\n' || *p == '\0') ! 1378: { ! 1379: *p = '\0'; ! 1380: return (NULL); ! 1381: } ! 1382: *p++ = '\0'; ! 1383: return (p); ! 1384: } ! 1385: /* ! 1386: ** PUTPFENT -- output a p-file entry to a file ! 1387: ** ! 1388: ** Parameters: ! 1389: ** pf -- the p-file entry ! 1390: ** f -- the file to put it on. ! 1391: ** ! 1392: ** Returns: ! 1393: ** none. ! 1394: ** ! 1395: ** Side Effects: ! 1396: ** pf is written onto file f. ! 1397: */ ! 1398: ! 1399: putpfent(pf, f) ! 1400: register struct pfile *pf; ! 1401: register FILE *f; ! 1402: { ! 1403: fprintf(f, "%s %s %s %s %s", pf->p_osid, pf->p_nsid, ! 1404: pf->p_user, pf->p_date, pf->p_time); ! 1405: if (pf->p_aux != NULL) ! 1406: fprintf(f, " %s", pf->p_aux); ! 1407: else ! 1408: fprintf(f, "\n"); ! 1409: } ! 1410: ! 1411: /* ! 1412: ** USRERR -- issue user-level error ! 1413: ** ! 1414: ** Parameters: ! 1415: ** f -- format string. ! 1416: ** p1-p3 -- parameters to a printf. ! 1417: ** ! 1418: ** Returns: ! 1419: ** -1 ! 1420: ** ! 1421: ** Side Effects: ! 1422: ** none. ! 1423: */ ! 1424: ! 1425: /*VARARGS1*/ ! 1426: usrerr(f, p1, p2, p3) ! 1427: char *f; ! 1428: { ! 1429: fprintf(stderr, "\n%s: ", MyName); ! 1430: fprintf(stderr, f, p1, p2, p3); ! 1431: fprintf(stderr, "\n"); ! 1432: ! 1433: return (-1); ! 1434: } ! 1435: ! 1436: /* ! 1437: ** SYSERR -- print system-generated error. ! 1438: ** ! 1439: ** Parameters: ! 1440: ** f -- format string to a printf. ! 1441: ** p1, p2, p3 -- parameters to f. ! 1442: ** ! 1443: ** Returns: ! 1444: ** never. ! 1445: ** ! 1446: ** Side Effects: ! 1447: ** none. ! 1448: */ ! 1449: ! 1450: /*VARARGS1*/ ! 1451: syserr(f, p1, p2, p3) ! 1452: char *f; ! 1453: { ! 1454: extern int errno; ! 1455: ! 1456: fprintf(stderr, "\n%s SYSERR: ", MyName); ! 1457: fprintf(stderr, f, p1, p2, p3); ! 1458: fprintf(stderr, "\n"); ! 1459: if (errno == 0) ! 1460: exit(EX_SOFTWARE); ! 1461: else ! 1462: { ! 1463: perror(NULL); ! 1464: exit(EX_OSERR); ! 1465: } ! 1466: } ! 1467: /* ! 1468: ** USERNAME -- return name of the current user ! 1469: ** ! 1470: ** Parameters: ! 1471: ** none ! 1472: ** ! 1473: ** Returns: ! 1474: ** name of current user ! 1475: ** ! 1476: ** Side Effects: ! 1477: ** none ! 1478: */ ! 1479: ! 1480: char * ! 1481: username() ! 1482: { ! 1483: # ifdef UIDUSER ! 1484: extern struct passwd *getpwuid(); ! 1485: register struct passwd *pw; ! 1486: ! 1487: pw = getpwuid(getuid()); ! 1488: if (pw == NULL) ! 1489: { ! 1490: syserr("who are you? (uid=%d)", getuid()); ! 1491: exit(EX_OSERR); ! 1492: } ! 1493: return (pw->pw_name); ! 1494: # else ! 1495: extern char *getlogin(); ! 1496: extern char *getenv(); ! 1497: register char *p; ! 1498: ! 1499: p = getenv("USER"); ! 1500: if (p == NULL || p[0] == '\0') ! 1501: p = getlogin(); ! 1502: return (p); ! 1503: # endif UIDUSER ! 1504: } ! 1505: ! 1506: /* ! 1507: ** Guarded string manipulation routines; the last argument ! 1508: ** is the length of the buffer into which the strcpy or strcat ! 1509: ** is to be done. ! 1510: */ ! 1511: char *gstrcat(to, from, length) ! 1512: char *to, *from; ! 1513: int length; ! 1514: { ! 1515: if (strlen(from) + strlen(to) >= length) { ! 1516: gstrbotch(to, from); ! 1517: } ! 1518: return(strcat(to, from)); ! 1519: } ! 1520: ! 1521: char *gstrncat(to, from, n, length) ! 1522: char *to, *from; ! 1523: int n; ! 1524: int length; ! 1525: { ! 1526: if (n + strlen(to) >= length) { ! 1527: gstrbotch(to, from); ! 1528: } ! 1529: return(strncat(to, from, n)); ! 1530: } ! 1531: ! 1532: char *gstrcpy(to, from, length) ! 1533: char *to, *from; ! 1534: int length; ! 1535: { ! 1536: if (strlen(from) >= length) { ! 1537: gstrbotch(from, (char *)0); ! 1538: } ! 1539: return(strcpy(to, from)); ! 1540: } ! 1541: gstrbotch(str1, str2) ! 1542: char *str1, *str2; ! 1543: { ! 1544: usrerr("Filename(s) too long: %s %s", str1, str2); ! 1545: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.