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