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

Defined functions

callprog defined in line 650; used 2 times
clean defined in line 909; used 1 times
command defined in line 363; used 9 times
dodiff defined in line 1266; used 1 times
getpfent defined in line 1358; 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 1073; used 1 times
isdir defined in line 841; used 2 times
lookup defined in line 616; used 4 times
main defined in line 238; never used
makefile defined in line 751; used 4 times
nextfield defined in line 1381; used 6 times
putpfent defined in line 1411; used 2 times
safepath defined in line 868; used 2 times
syserr defined in line 1463; used 10 times
tail defined in line 1332; used 5 times
unedit defined in line 1109; used 2 times
username defined in line 1492; used 4 times
usrerr defined in line 1438; used 11 times

Defined variables

Debug defined in line 226; used 5 times
MyName defined in line 222; used 3 times
OutFile defined in line 223; used 8 times
RealUser defined in line 224; used 1 times
SccsDir defined in line 220; used 9 times
SccsPath defined in line 216; used 7 times
SccsProg defined in line 172; used 1 times
copyright defined in line 8; never used
sccsid defined in line 14; never used

Defined struct's

pfile defined in line 206; used 14 times
sccsprog defined in line 134; used 12 times

Defined typedef's

bool defined in line 128; used 16 times

Defined macros

CHECKC defined in line 160; used 2 times
CLEAN defined in line 146; used 4 times
CLEANC defined in line 158; used 3 times
CMACRO defined in line 144; used 6 times
DIFFS defined in line 149; used 1 times
DODIFF defined in line 150; used 1 times
ENTER defined in line 151; used 1 times
FALSE defined in line 130; used 17 times
FBUFSIZ defined in line 235; used 5 times
FIX defined in line 145; used 1 times
INFOC defined in line 159; used 2 times
MYNAME defined in line 119; used 2 times
NO_SDOT defined in line 154; used 17 times
PFILELG defined in line 236; used 2 times
PROG defined in line 143; used 11 times
PROGPATH defined in line 123; used 14 times
REALUSER defined in line 155; used 10 times
SCCSPATH defined in line 115; used 2 times
SHELL defined in line 148; used 1 times
TELLC defined in line 161; used 2 times
TRUE defined in line 129; used 10 times
UNEDIT defined in line 147; used 1 times
bitset defined in line 132; used 2 times
Last modified: 1985-05-31
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 4234
Valid CSS Valid XHTML 1.0 Strict