home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.bin / sccs / sccs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-18  |  33.8 KB  |  1,624 lines

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