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

Defined functions

callprog defined in line 649; used 2 times
clean defined in line 908; used 1 times
command defined in line 362; used 9 times
dodiff defined in line 1268; used 1 times
getpfent defined in line 1360; used 4 times
gstrbotch defined in line 1553; used 3 times
gstrcat defined in line 1523; used 12 times
gstrcpy defined in line 1544; used 11 times
gstrncat defined in line 1533; used 2 times
isbranch defined in line 1072; used 1 times
isdir defined in line 840; used 2 times
lookup defined in line 615; used 4 times
main defined in line 237; never used
makefile defined in line 750; used 4 times
nextfield defined in line 1383; used 6 times
putpfent defined in line 1413; used 2 times
safepath defined in line 867; used 2 times
syserr defined in line 1465; used 10 times
tail defined in line 1334; used 6 times
unedit defined in line 1111; used 2 times
username defined in line 1494; used 4 times
usrerr defined in line 1440; used 11 times

Defined variables

Debug defined in line 225; used 5 times
MyName defined in line 221; used 3 times
OutFile defined in line 222; used 8 times
RealUser defined in line 223; used 1 times
SccsDir defined in line 219; used 9 times
SccsPath defined in line 215; used 7 times
SccsProg defined in line 171; used 1 times
copyright defined in line 8; never used
sccsid defined in line 12; never used

Defined struct's

pfile defined in line 205; used 14 times
sccsprog defined in line 133; used 12 times

Defined typedef's

bool defined in line 127; used 16 times

Defined macros

CHECKC defined in line 159; used 2 times
CLEAN defined in line 145; used 4 times
CLEANC defined in line 157; used 3 times
CMACRO defined in line 143; used 6 times
DIFFS defined in line 148; used 1 times
DODIFF defined in line 149; used 1 times
ENTER defined in line 150; used 1 times
FALSE defined in line 129; used 17 times
FBUFSIZ defined in line 234; used 5 times
FIX defined in line 144; used 1 times
INFOC defined in line 158; used 2 times
MYNAME defined in line 118; used 2 times
NO_SDOT defined in line 153; used 17 times
PFILELG defined in line 235; used 2 times
PROG defined in line 142; used 11 times
PROGPATH defined in line 122; used 14 times
REALUSER defined in line 154; used 10 times
SCCSPATH defined in line 114; used 2 times
SHELL defined in line 147; used 1 times
TELLC defined in line 160; used 2 times
TRUE defined in line 128; used 10 times
UNEDIT defined in line 146; used 1 times
bitset defined in line 131; used 2 times
Last modified: 1997-10-03
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 7458
Valid CSS Valid XHTML 1.0 Strict