# 2.11BSD#431 - /usr/src/ucb/sccs.c

##### Functions

    callprog          X
    clean             X
    command           X
    dodiff            X
    getpfent          X
    gstrbotch         X
    gstrcat           X
    gstrcpy           X
    gstrncat          X
    isbranch          X
    isdir             X
    lookup            X
    main              X
    makefile          X
    nextfield         X
    putpfent          X
    safepath          X
    syserr            X
    tail              X
    unedit            X
    username          X
    usrerr            X

##### Variables

    Debug             X
    MyName            X
    OutFile           X
    RealUser          X
    SccsDir           X
    SccsPath          X
    SccsProg          X
    copyright         X

##### Macros

    CHECKC            X
    CLEAN             X
    CLEANC            X
    CMACRO            X
    DIFFS             X
    DODIFF            X
    ENTER             X
    FALSE             X
    FBUFSIZ           X
    FIX               X
    INFOC             X
    MYNAME            X
    NO_SDOT           X
    PFILELG           X
    PROG              X
    PROGPATH          X
    REALUSER          X
    SCCSPATH          X
    SHELL             X
    TELLC             X
    TRUE              X
    UNEDIT            X
    bitset            X

##### struct's

    pfile             X
    sccsprog          X

##### typedef's

    bool              X

       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

- in line 463-467(2)

clean defined in line 908; used 1 times

- in line 504

command defined in line 362; used 9 times

- in line 335, 477, 492-500(3), 517, 577, 1299, 1315

dodiff defined in line 1268; used 1 times

- in line 533

getpfent defined in line 1360; used 4 times

- in line 921, 1013, 1125, 1168

gstrbotch defined in line 1553; used 3 times

- in line 1528, 1539, 1549

gstrcat defined in line 1523; used 12 times

- in line 231, 265-269(2), 573, 582, 786, 803-806(2), 970-976(4)

gstrcpy defined in line 1544; used 11 times

- in line 233, 264-268(2), 785-789(2), 796, 802, 968, 998-1000(2), 1036

gstrncat defined in line 1533; used 2 times

- in line 232, 792

isbranch defined in line 1072; used 1 times

- in line 1015

isdir defined in line 840; used 2 times

- in line 759, 799

lookup defined in line 615; used 4 times

- in line 242, 293, 370, 425

main defined in line 237; never used
makefile defined in line 750; used 4 times

- in line 379, 447, 1127-1130(2)

nextfield defined in line 1383; used 6 times

- in line 1367, 1373-1377(5)

putpfent defined in line 1413; used 2 times

- in line 1027, 1178

safepath defined in line 867; used 2 times

- in line 758, 775

syserr defined in line 1465; used 10 times

- in line 558, 591, 683, 701, 719, 1215, 1286-1291(2), 1312, 1504

tail defined in line 1334; used 6 times

- in line 381, 533, 575, 582, 1109, 1190

unedit defined in line 1111; used 2 times

- in line 376, 510

username defined in line 1494; used 4 times

- in line 924, 951, 1123, 1162

usrerr defined in line 1440; used 11 times

- in line 261, 272, 327, 428, 486, 958, 982, 1138, 1157, 1220, 1556

#### Defined variables

Debug defined in line 225; used 5 times

- in line 322, 384, 595, 659, 677

MyName defined in line 221; used 3 times

- in line 288, 1443, 1470

OutFile defined in line 222; used 8 times

- in line 691-699(4), 710-714(3), 1297

RealUser defined in line 223; used 1 times

- in line 303

SccsDir defined in line 219; used 9 times

- in line 255, 276, 314-316(3), 775, 783-785(2), 968

SccsPath defined in line 215; used 7 times

- in line 308-310(3), 331-332(2), 802, 976

SccsProg defined in line 171; used 1 times

- in line 621

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

- in line 921-922(4), 1124-1125(4), 1360-1364(4), 1414(2)

sccsprog defined in line 133; used 12 times

- in line 171(2), 242(2), 367-370(4), 615-619(4)

#### Defined typedef's

bool defined in line 127; used 16 times

- in line 223-225(2), 364, 653, 840, 867, 1111-131(2), 376, 758-759(2), 917-920(3), 1120-1121(2)

#### Defined macros

CHECKC defined in line 159; used 2 times

- in line 191, 1053

CLEAN defined in line 145; used 4 times

- in line 189-192(4)

CLEANC defined in line 157; used 3 times

- in line 189, 1017, 1033

CMACRO defined in line 143; used 6 times

- in line 185-187(3), 196-200(3)

DIFFS defined in line 148; used 1 times

- in line 194

DODIFF defined in line 149; used 1 times

- in line 195

ENTER defined in line 150; used 1 times

- in line 199

FALSE defined in line 129; used 17 times

- in line 335, 500, 517, 885, 920, 992, 1009, 1086, 1120-1121(2), 1132, 1139, 1149, 1206, 1221, 1250, 1315

FBUFSIZ defined in line 234; used 5 times

- in line 248, 369, 755, 913, 1035

FIX defined in line 144; used 1 times

- in line 188

INFOC defined in line 158; used 2 times

- in line 190, 1043

MYNAME defined in line 118; used 2 times

- in line 117, 221

NO_SDOT defined in line 153; used 17 times

- in line 178-200(16), 446

PFILELG defined in line 235; used 2 times

- in line 1126, 1365

PROG defined in line 142; used 11 times

- in line 173-183(11)

PROGPATH defined in line 122; used 14 times

- in line 121, 173-184(12), 195

REALUSER defined in line 154; used 10 times

- in line 173, 181-195(8), 706

SCCSPATH defined in line 114; used 2 times

- in line 113, 215

SHELL defined in line 147; used 1 times

- in line 184

TELLC defined in line 160; used 2 times

- in line 192, 1021

TRUE defined in line 128; used 10 times

- in line 492-496(2), 577, 879, 942, 1019-1020(2), 1084, 1245, 1299

UNEDIT defined in line 146; used 1 times

- in line 193

bitset defined in line 131; used 2 times

- in line 446, 706

Last modified: 1997-10-03 Generated: 2016-12-26 | Generated by src2html V0.67 page hit count: 3478 |