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