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