121574Sdist /*
2*62219Sbostic * Copyright (c) 1980, 1993
3*62219Sbostic * The Regents of the University of California. All rights reserved.
434204Sbostic *
542765Sbostic * %sccs.include.redist.c%
621574Sdist */
721574Sdist
813622Ssam #ifndef lint
9*62219Sbostic static char copyright[] =
10*62219Sbostic "@(#) Copyright (c) 1980, 1993\n\
11*62219Sbostic The Regents of the University of California. All rights reserved.\n";
1234204Sbostic #endif /* not lint */
1313622Ssam
1421574Sdist #ifndef lint
15*62219Sbostic static char sccsid[] = "@(#)sccs.c 8.1 (Berkeley) 06/06/93";
1634204Sbostic #endif /* not lint */
1721574Sdist
1846974Sbostic #include <sys/cdefs.h>
1946974Sbostic #include <sys/param.h>
2046974Sbostic #include <sys/stat.h>
2146974Sbostic #include <sys/dir.h>
2246974Sbostic #include <signal.h>
2346974Sbostic #include <sysexits.h>
2446974Sbostic #include <errno.h>
2546974Sbostic #include <pwd.h>
2646974Sbostic #include <stdio.h>
2746974Sbostic #include "pathnames.h"
28148Seric
29828Seric /*
30828Seric ** SCCS.C -- human-oriented front end to the SCCS system.
31828Seric **
32828Seric ** Without trying to add any functionality to speak of, this
33828Seric ** program tries to make SCCS a little more accessible to human
34828Seric ** types. The main thing it does is automatically put the
35828Seric ** string "SCCS/s." on the front of names. Also, it has a
36828Seric ** couple of things that are designed to shorten frequent
37828Seric ** combinations, e.g., "delget" which expands to a "delta"
38828Seric ** and a "get".
39828Seric **
40828Seric ** This program can also function as a setuid front end.
41828Seric ** To do this, you should copy the source, renaming it to
42828Seric ** whatever you want, e.g., "syssccs". Change any defaults
43828Seric ** in the program (e.g., syssccs might default -d to
44828Seric ** "/usr/src/sys"). Then recompile and put the result
45828Seric ** as setuid to whomever you want. In this mode, sccs
46828Seric ** knows to not run setuid for certain programs in order
47828Seric ** to preserve security, and so forth.
48828Seric **
49828Seric ** Usage:
50828Seric ** sccs [flags] command [args]
51828Seric **
52828Seric ** Flags:
53828Seric ** -d<dir> <dir> represents a directory to search
54828Seric ** out of. It should be a full pathname
55828Seric ** for general usage. E.g., if <dir> is
56828Seric ** "/usr/src/sys", then a reference to the
57828Seric ** file "dev/bio.c" becomes a reference to
58828Seric ** "/usr/src/sys/dev/bio.c".
59828Seric ** -p<path> prepends <path> to the final component
60828Seric ** of the pathname. By default, this is
61828Seric ** "SCCS". For example, in the -d example
62828Seric ** above, the path then gets modified to
63828Seric ** "/usr/src/sys/dev/SCCS/s.bio.c". In
64828Seric ** more common usage (without the -d flag),
65828Seric ** "prog.c" would get modified to
66828Seric ** "SCCS/s.prog.c". In both cases, the
67828Seric ** "s." gets automatically prepended.
68828Seric ** -r run as the real user.
69828Seric **
70828Seric ** Commands:
71828Seric ** admin,
72828Seric ** get,
73828Seric ** delta,
74828Seric ** rmdel,
7530959Sbostic ** cdc,
76828Seric ** etc. Straight out of SCCS; only difference
77828Seric ** is that pathnames get modified as
78828Seric ** described above.
7930959Sbostic ** enter Front end doing "sccs admin -i<name> <name>"
8030959Sbostic ** create Macro for "enter" followed by "get".
81828Seric ** edit Macro for "get -e".
82828Seric ** unedit Removes a file being edited, knowing
83828Seric ** about p-files, etc.
84828Seric ** delget Macro for "delta" followed by "get".
85828Seric ** deledit Macro for "delta" followed by "get -e".
8630959Sbostic ** branch Macro for "get -b -e", followed by "delta
8730959Sbostic ** -s -n", followd by "get -e -t -g".
8830959Sbostic ** diffs "diff" the specified version of files
8930959Sbostic ** and the checked-out version.
9030959Sbostic ** print Macro for "prs -e" followed by "get -p -m".
9130959Sbostic ** tell List what files are being edited.
9230959Sbostic ** info Print information about files being edited.
93828Seric ** clean Remove all files that can be
94828Seric ** regenerated from SCCS files.
951205Seric ** check Like info, but return exit status, for
96828Seric ** use in makefiles.
97828Seric ** fix Remove a top delta & reedit, but save
98828Seric ** the previous changes in that delta.
99828Seric **
100828Seric ** Compilation Flags:
101828Seric ** UIDUSER -- determine who the user is by looking at the
102828Seric ** uid rather than the login name -- for machines
103828Seric ** where SCCS gets the user in this way.
1041270Seric ** SCCSDIR -- if defined, forces the -d flag to take on
1051205Seric ** this value. This is so that the setuid
1061205Seric ** aspects of this program cannot be abused.
1071270Seric ** This flag also disables the -p flag.
1081270Seric ** SCCSPATH -- the default for the -p flag.
1091437Seric ** MYNAME -- the title this program should print when it
1101437Seric ** gives error messages.
111828Seric **
112828Seric ** Compilation Instructions:
113828Seric ** cc -O -n -s sccs.c
1141437Seric ** The flags listed above can be -D defined to simplify
1151437Seric ** recompilation for variant versions.
116828Seric **
117828Seric ** Author:
118828Seric ** Eric Allman, UCB/INGRES
1191270Seric ** Copyright 1980 Regents of the University of California
120828Seric */
121155Seric
1221432Seric
1231270Seric /******************* Configuration Information ********************/
1241270Seric
1251437Seric # ifndef SCCSPATH
1261432Seric # define SCCSPATH "SCCS" /* pathname in which to find s-files */
1271437Seric # endif NOT SCCSPATH
128828Seric
1291437Seric # ifndef MYNAME
1301437Seric # define MYNAME "sccs" /* name used for printing errors */
1311437Seric # endif NOT MYNAME
1321270Seric
1331270Seric /**************** End of Configuration Information ****************/
1341432Seric
135157Seric typedef char bool;
136200Seric # define TRUE 1
137200Seric # define FALSE 0
138157Seric
1391438Seric # define bitset(bit, word) ((bool) ((bit) & (word)))
1401438Seric
141148Seric struct sccsprog
142148Seric {
143148Seric char *sccsname; /* name of SCCS routine */
144200Seric short sccsoper; /* opcode, see below */
145200Seric short sccsflags; /* flags, see below */
146148Seric char *sccspath; /* pathname of binary implementing */
147148Seric };
148148Seric
149200Seric /* values for sccsoper */
150200Seric # define PROG 0 /* call a program */
151201Seric # define CMACRO 1 /* command substitution macro */
152226Seric # define FIX 2 /* fix a delta */
153261Seric # define CLEAN 3 /* clean out recreatable files */
154396Seric # define UNEDIT 4 /* unedit a file */
1551431Seric # define SHELL 5 /* call a shell file (like PROG) */
1561433Seric # define DIFFS 6 /* diff between sccs & file out */
1571871Seric # define DODIFF 7 /* internal call to diff program */
15810104Srrh # define ENTER 8 /* enter new files */
159200Seric
160157Seric /* bits for sccsflags */
161200Seric # define NO_SDOT 0001 /* no s. on front of args */
162200Seric # define REALUSER 0002 /* protected (e.g., admin) */
163148Seric
164819Seric /* modes for the "clean", "info", "check" ops */
165819Seric # define CLEANC 0 /* clean command */
166819Seric # define INFOC 1 /* info command */
167819Seric # define CHECKC 2 /* check command */
1681730Seric # define TELLC 3 /* give list of files being edited */
169819Seric
1701432Seric /*
1711432Seric ** Description of commands known to this program.
1721432Seric ** First argument puts the command into a class. Second arg is
1731432Seric ** info regarding treatment of this command. Third arg is a
1741432Seric ** list of flags this command accepts from macros, etc. Fourth
1751432Seric ** arg is the pathname of the implementing program, or the
1761432Seric ** macro definition, or the arg to a sub-algorithm.
1771432Seric */
178202Seric
17946902Sbostic struct sccsprog SccsProg[] = {
18046974Sbostic "admin", PROG, REALUSER, _PATH_SCCSADMIN,
18146974Sbostic "cdc", PROG, 0, _PATH_SCCSRMDEL,
18246974Sbostic "comb", PROG, 0, _PATH_SCCSCOMB,
18346974Sbostic "delta", PROG, 0, _PATH_SCCSDELTA,
18446974Sbostic "get", PROG, 0, _PATH_SCCSGET,
18546974Sbostic "help", PROG, NO_SDOT, _PATH_SCCSHELP,
18646974Sbostic "prs", PROG, 0, _PATH_SCCSPRS,
18746974Sbostic "prt", PROG, 0, _PATH_SCCSPRT,
18846974Sbostic "rmdel", PROG, REALUSER, _PATH_SCCSRMDEL,
18946974Sbostic "val", PROG, 0, _PATH_SCCSVAL,
19046974Sbostic "what", PROG, NO_SDOT, _PATH_SCCSWHAT,
19146974Sbostic "sccsdiff", SHELL, REALUSER, _PATH_SCCSDIFF,
19246902Sbostic "edit", CMACRO, NO_SDOT, "get -e",
19346902Sbostic "delget", CMACRO, NO_SDOT, "delta:mysrp/get:ixbeskcl -t",
19446902Sbostic "deledit", CMACRO, NO_SDOT,
19546902Sbostic "delta:mysrp -n/get:ixbskcl -e -t -g",
19646902Sbostic "fix", FIX, NO_SDOT, NULL,
19746902Sbostic "clean", CLEAN, REALUSER|NO_SDOT,
19846902Sbostic (char *) CLEANC,
19946902Sbostic "info", CLEAN, REALUSER|NO_SDOT,
20046902Sbostic (char *) INFOC,
20146902Sbostic "check", CLEAN, REALUSER|NO_SDOT,
20246902Sbostic (char *) CHECKC,
20346902Sbostic "tell", CLEAN, REALUSER|NO_SDOT,
20446902Sbostic (char *) TELLC,
20546902Sbostic "unedit", UNEDIT, NO_SDOT, NULL,
20646902Sbostic "diffs", DIFFS, NO_SDOT|REALUSER,
20746902Sbostic NULL,
20846902Sbostic "-diff", DODIFF, NO_SDOT|REALUSER,
20946974Sbostic _PATH_SCCSBDIFF,
21046902Sbostic "print", CMACRO, 0, "prs -e/get -p -m -s",
2112226Seric "branch", CMACRO, NO_SDOT,
21246902Sbostic "get:ixrc -e -b/delta: -s -n -ybranch-place-holder/get:pl -e -t -g",
21346902Sbostic "enter", ENTER, NO_SDOT, NULL,
21446902Sbostic "create", CMACRO, NO_SDOT, "enter/get:ixbeskcl -t",
21546902Sbostic NULL, -1, 0, NULL
216148Seric };
217148Seric
2181432Seric /* one line from a p-file */
219396Seric struct pfile
220396Seric {
221396Seric char *p_osid; /* old SID */
222396Seric char *p_nsid; /* new SID */
223396Seric char *p_user; /* user who did edit */
224396Seric char *p_date; /* date of get */
225396Seric char *p_time; /* time of get */
2262161Seric char *p_aux; /* extra info at end */
227396Seric };
228396Seric
2291270Seric char *SccsPath = SCCSPATH; /* pathname of SCCS files */
2301270Seric # ifdef SCCSDIR
2311270Seric char *SccsDir = SCCSDIR; /* directory to begin search from */
2321205Seric # else
2331270Seric char *SccsDir = "";
2341205Seric # endif
2351437Seric char MyName[] = MYNAME; /* name used in messages */
2361433Seric int OutFile = -1; /* override output file for commands */
237157Seric bool RealUser; /* if set, running as real user */
238393Seric # ifdef DEBUG
239393Seric bool Debug; /* turn on tracing */
240393Seric # endif
2412139Seric # ifndef V6
2422139Seric extern char *getenv();
2432139Seric # endif V6
24410110Srrh
24510110Srrh char *gstrcat(), *strcat();
24610110Srrh char *gstrncat(), *strncat();
24710110Srrh char *gstrcpy(), *strcpy();
24810110Srrh #define FBUFSIZ BUFSIZ
24910110Srrh #define PFILELG 120
2501432Seric
main(argc,argv)251148Seric main(argc, argv)
252148Seric int argc;
253148Seric char **argv;
254148Seric {
255148Seric register char *p;
256262Seric extern struct sccsprog *lookup();
2571282Seric register int i;
2582139Seric # ifndef V6
2592139Seric # ifndef SCCSDIR
2602140Seric register struct passwd *pw;
2612140Seric extern struct passwd *getpwnam();
26210110Srrh char buf[FBUFSIZ];
2632140Seric
2642139Seric /* pull "SccsDir" out of the environment (possibly) */
26510260Seric p = getenv("PROJECTDIR");
2662140Seric if (p != NULL && p[0] != '\0')
2672140Seric {
2682140Seric if (p[0] == '/')
2692140Seric SccsDir = p;
2702140Seric else
2712140Seric {
2722140Seric pw = getpwnam(p);
2732140Seric if (pw == NULL)
2742140Seric {
2752140Seric usrerr("user %s does not exist", p);
2762140Seric exit(EX_USAGE);
2772140Seric }
27810110Srrh gstrcpy(buf, pw->pw_dir, sizeof(buf));
27910110Srrh gstrcat(buf, "/src", sizeof(buf));
2802140Seric if (access(buf, 0) < 0)
2812140Seric {
28210110Srrh gstrcpy(buf, pw->pw_dir, sizeof(buf));
28310110Srrh gstrcat(buf, "/source", sizeof(buf));
2842140Seric if (access(buf, 0) < 0)
2852140Seric {
2862140Seric usrerr("project %s has no source!", p);
2872140Seric exit(EX_USAGE);
2882140Seric }
2892140Seric }
2902140Seric SccsDir = buf;
2912140Seric }
2922140Seric }
2932139Seric # endif SCCSDIR
2942139Seric # endif V6
2952139Seric
296148Seric /*
297148Seric ** Detect and decode flags intended for this program.
298148Seric */
299148Seric
300200Seric if (argc < 2)
301148Seric {
3021205Seric fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName);
303200Seric exit(EX_USAGE);
304200Seric }
305200Seric argv[argc] = NULL;
306200Seric
307262Seric if (lookup(argv[0]) == NULL)
308200Seric {
309262Seric while ((p = *++argv) != NULL)
310148Seric {
311262Seric if (*p != '-')
312262Seric break;
313262Seric switch (*++p)
314262Seric {
315262Seric case 'r': /* run as real user */
316262Seric setuid(getuid());
317262Seric RealUser++;
318262Seric break;
319148Seric
3201270Seric # ifndef SCCSDIR
321262Seric case 'p': /* path of sccs files */
322262Seric SccsPath = ++p;
3232348Seric if (SccsPath[0] == '\0' && argv[1] != NULL)
3242348Seric SccsPath = *++argv;
325262Seric break;
326148Seric
327588Seric case 'd': /* directory to search from */
328588Seric SccsDir = ++p;
3292348Seric if (SccsDir[0] == '\0' && argv[1] != NULL)
3302348Seric SccsDir = *++argv;
331588Seric break;
3321205Seric # endif
333588Seric
334393Seric # ifdef DEBUG
335393Seric case 'T': /* trace */
336393Seric Debug++;
337393Seric break;
338393Seric # endif
339393Seric
340262Seric default:
3411205Seric usrerr("unknown option -%s", p);
342262Seric break;
343262Seric }
344148Seric }
345262Seric if (SccsPath[0] == '\0')
346262Seric SccsPath = ".";
347148Seric }
348148Seric
3491737Seric i = command(argv, FALSE, "");
3501282Seric exit(i);
351200Seric }
3521432Seric
3531432Seric /*
3541282Seric ** COMMAND -- look up and perform a command
3551282Seric **
3561282Seric ** This routine is the guts of this program. Given an
3571282Seric ** argument vector, it looks up the "command" (argv[0])
3581282Seric ** in the configuration table and does the necessary stuff.
3591282Seric **
3601282Seric ** Parameters:
3611282Seric ** argv -- an argument vector to process.
3621282Seric ** forkflag -- if set, fork before executing the command.
3631316Seric ** editflag -- if set, only include flags listed in the
3641316Seric ** sccsklets field of the command descriptor.
3651316Seric ** arg0 -- a space-seperated list of arguments to insert
3661316Seric ** before argv.
3671282Seric **
3681282Seric ** Returns:
3691282Seric ** zero -- command executed ok.
3701282Seric ** else -- error status.
3711282Seric **
3721282Seric ** Side Effects:
3731282Seric ** none.
3741282Seric */
375157Seric
command(argv,forkflag,arg0)3761737Seric command(argv, forkflag, arg0)
377200Seric char **argv;
378201Seric bool forkflag;
3791316Seric char *arg0;
380200Seric {
381200Seric register struct sccsprog *cmd;
382200Seric register char *p;
38310110Srrh char buf[FBUFSIZ];
384262Seric extern struct sccsprog *lookup();
3851316Seric char *nav[1000];
3861316Seric char **np;
3871431Seric register char **ap;
388585Seric register int i;
3891431Seric register char *q;
390585Seric extern bool unedit();
3911282Seric int rval = 0;
3921316Seric extern char *index();
3931316Seric extern char *makefile();
3941737Seric char *editchs;
3951435Seric extern char *tail();
396200Seric
397393Seric # ifdef DEBUG
398393Seric if (Debug)
399393Seric {
4001316Seric printf("command:\n\t\"%s\"\n", arg0);
4011316Seric for (np = argv; *np != NULL; np++)
4021316Seric printf("\t\"%s\"\n", *np);
403393Seric }
404393Seric # endif
405393Seric
406157Seric /*
4071316Seric ** Copy arguments.
4081438Seric ** Copy from arg0 & if necessary at most one arg
4091438Seric ** from argv[0].
4101316Seric */
4111316Seric
4121431Seric np = ap = &nav[1];
4131737Seric editchs = NULL;
4141821Seric for (p = arg0, q = buf; *p != '\0' && *p != '/'; )
4151316Seric {
4161316Seric *np++ = q;
4171316Seric while (*p == ' ')
4181316Seric p++;
4191737Seric while (*p != ' ' && *p != '\0' && *p != '/' && *p != ':')
4201316Seric *q++ = *p++;
4211316Seric *q++ = '\0';
4221737Seric if (*p == ':')
4231737Seric {
4241737Seric editchs = q;
4251821Seric while (*++p != '\0' && *p != '/' && *p != ' ')
4261737Seric *q++ = *p;
4271737Seric *q++ = '\0';
4281737Seric }
4291316Seric }
4301316Seric *np = NULL;
4311431Seric if (*ap == NULL)
4321316Seric *np++ = *argv++;
4331316Seric
4341316Seric /*
435148Seric ** Look up command.
4361431Seric ** At this point, *ap is the command name.
437148Seric */
438148Seric
4391431Seric cmd = lookup(*ap);
440262Seric if (cmd == NULL)
441148Seric {
4421431Seric usrerr("Unknown command \"%s\"", *ap);
4431282Seric return (EX_USAGE);
444148Seric }
445148Seric
446148Seric /*
4471316Seric ** Copy remaining arguments doing editing as appropriate.
4481316Seric */
4491316Seric
4501316Seric for (; *argv != NULL; argv++)
4511316Seric {
4521316Seric p = *argv;
4531316Seric if (*p == '-')
4541316Seric {
4551737Seric if (p[1] == '\0' || editchs == NULL || index(editchs, p[1]) != NULL)
4561316Seric *np++ = p;
4571316Seric }
4581316Seric else
4591316Seric {
4601316Seric if (!bitset(NO_SDOT, cmd->sccsflags))
4611316Seric p = makefile(p);
4621316Seric if (p != NULL)
4631316Seric *np++ = p;
4641316Seric }
4651316Seric }
4661316Seric *np = NULL;
4671316Seric
4681316Seric /*
469200Seric ** Interpret operation associated with this command.
470157Seric */
471157Seric
472200Seric switch (cmd->sccsoper)
473200Seric {
4741431Seric case SHELL: /* call a shell file */
4751431Seric *ap = cmd->sccspath;
4761431Seric *--ap = "sh";
47737921Sbostic rval = callprog(_PATH_BSHELL, cmd->sccsflags, ap, forkflag);
4781431Seric break;
4791431Seric
480200Seric case PROG: /* call an sccs prog */
4811431Seric rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag);
482201Seric break;
483201Seric
484201Seric case CMACRO: /* command macro */
4851438Seric /* step through & execute each part of the macro */
486201Seric for (p = cmd->sccspath; *p != '\0'; p++)
487201Seric {
4881316Seric q = p;
4891316Seric while (*p != '\0' && *p != '/')
4901316Seric p++;
4911737Seric rval = command(&ap[1], *p != '\0', q);
4921282Seric if (rval != 0)
4931282Seric break;
494201Seric }
4951282Seric break;
496157Seric
497226Seric case FIX: /* fix a delta */
49830959Sbostic if (ap[1]==0 || strncmp(ap[1], "-r", 2)!=0)
499226Seric {
5001205Seric usrerr("-r flag needed for fix command");
5011282Seric rval = EX_USAGE;
502226Seric break;
503226Seric }
5041438Seric
5051438Seric /* get the version with all changes */
5061737Seric rval = command(&ap[1], TRUE, "get -k");
5071438Seric
5081438Seric /* now remove that version from the s-file */
5091282Seric if (rval == 0)
5101737Seric rval = command(&ap[1], TRUE, "rmdel:r");
5111438Seric
5121438Seric /* and edit the old version (but don't clobber new vers) */
5131282Seric if (rval == 0)
5141737Seric rval = command(&ap[2], FALSE, "get -e -g");
5151282Seric break;
516226Seric
517261Seric case CLEAN:
5181822Seric rval = clean((int) cmd->sccspath, ap);
519261Seric break;
520261Seric
521396Seric case UNEDIT:
5221431Seric for (argv = np = &ap[1]; *argv != NULL; argv++)
523585Seric {
5241316Seric if (unedit(*argv))
5251316Seric *np++ = *argv;
526585Seric }
5271316Seric *np = NULL;
5281438Seric
5291438Seric /* get all the files that we unedited successfully */
5301738Seric if (np > &ap[1])
5311737Seric rval = command(&ap[1], FALSE, "get");
532396Seric break;
533396Seric
5341433Seric case DIFFS: /* diff between s-file & edit file */
5351433Seric /* find the end of the flag arguments */
5361433Seric for (np = &ap[1]; *np != NULL && **np == '-'; np++)
5371433Seric continue;
5381433Seric argv = np;
5391433Seric
5401433Seric /* for each file, do the diff */
5411502Seric p = argv[1];
5421433Seric while (*np != NULL)
5431433Seric {
5441438Seric /* messy, but we need a null terminated argv */
5451433Seric *argv = *np++;
5461502Seric argv[1] = NULL;
5471435Seric i = dodiff(ap, tail(*argv));
5481433Seric if (rval == 0)
5491433Seric rval = i;
5501502Seric argv[1] = p;
5511433Seric }
5521433Seric break;
5531433Seric
5541871Seric case DODIFF: /* internal diff call */
5551871Seric setuid(getuid());
5561871Seric for (np = ap; *np != NULL; np++)
5571871Seric {
5581871Seric if ((*np)[0] == '-' && (*np)[1] == 'C')
5591871Seric (*np)[1] = 'c';
5601871Seric }
5611871Seric
5621871Seric /* insert "-" argument */
5631871Seric np[1] = NULL;
5641871Seric np[0] = np[-1];
5651871Seric np[-1] = "-";
5661871Seric
5671871Seric /* execute the diff program of choice */
5681871Seric # ifndef V6
5691871Seric execvp("diff", ap);
5701871Seric # endif
5711871Seric execv(cmd->sccspath, argv);
5721871Seric syserr("cannot exec %s", cmd->sccspath);
5731871Seric exit(EX_OSERR);
5741871Seric
57510104Srrh case ENTER: /* enter new sccs files */
5766785Smckusick /* skip over flag arguments */
5776785Smckusick for (np = &ap[1]; *np != NULL && **np == '-'; np++)
5786785Smckusick continue;
5796785Smckusick argv = np;
5806785Smckusick
5816785Smckusick /* do an admin for each file */
5826785Smckusick p = argv[1];
5836785Smckusick while (*np != NULL)
5846785Smckusick {
5856785Smckusick printf("\n%s:\n", *np);
58610110Srrh strcpy(buf, "-i");
58710110Srrh gstrcat(buf, *np, sizeof(buf));
5886785Smckusick ap[0] = buf;
5896785Smckusick argv[0] = tail(*np);
5906785Smckusick argv[1] = NULL;
5916785Smckusick rval = command(ap, TRUE, "admin");
5926785Smckusick argv[1] = p;
5936785Smckusick if (rval == 0)
5946785Smckusick {
59510110Srrh strcpy(buf, ",");
59610110Srrh gstrcat(buf, tail(*np), sizeof(buf));
5976785Smckusick if (link(*np, buf) >= 0)
5986785Smckusick unlink(*np);
5996785Smckusick }
6006785Smckusick np++;
6016785Smckusick }
6026785Smckusick break;
6036785Smckusick
604200Seric default:
6051205Seric syserr("oper %d", cmd->sccsoper);
606200Seric exit(EX_SOFTWARE);
607200Seric }
6081282Seric # ifdef DEBUG
6091282Seric if (Debug)
6101282Seric printf("command: rval=%d\n", rval);
6111282Seric # endif
6121282Seric return (rval);
613200Seric }
6141432Seric
6151432Seric /*
616262Seric ** LOOKUP -- look up an SCCS command name.
617262Seric **
618262Seric ** Parameters:
619262Seric ** name -- the name of the command to look up.
620262Seric **
621262Seric ** Returns:
622262Seric ** ptr to command descriptor for this command.
623262Seric ** NULL if no such entry.
624262Seric **
625262Seric ** Side Effects:
626262Seric ** none.
627262Seric */
628200Seric
629262Seric struct sccsprog *
lookup(name)630262Seric lookup(name)
631262Seric char *name;
632262Seric {
633262Seric register struct sccsprog *cmd;
634226Seric
635262Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++)
636262Seric {
637262Seric if (strcmp(cmd->sccsname, name) == 0)
638262Seric return (cmd);
639262Seric }
640262Seric return (NULL);
641262Seric }
6421432Seric
6431432Seric /*
6441282Seric ** CALLPROG -- call a program
6451282Seric **
6461316Seric ** Used to call the SCCS programs.
6471282Seric **
6481282Seric ** Parameters:
6491282Seric ** progpath -- pathname of the program to call.
6501282Seric ** flags -- status flags from the command descriptors.
6511282Seric ** argv -- an argument vector to pass to the program.
6521282Seric ** forkflag -- if true, fork before calling, else just
6531282Seric ** exec.
6541282Seric **
6551282Seric ** Returns:
6561282Seric ** The exit status of the program.
6571282Seric ** Nothing if forkflag == FALSE.
6581282Seric **
6591282Seric ** Side Effects:
6601282Seric ** Can exit if forkflag == FALSE.
6611282Seric */
662226Seric
callprog(progpath,flags,argv,forkflag)663200Seric callprog(progpath, flags, argv, forkflag)
664200Seric char *progpath;
665200Seric short flags;
666200Seric char **argv;
667200Seric bool forkflag;
668200Seric {
669200Seric register int i;
67030959Sbostic register int wpid;
671201Seric auto int st;
67230959Sbostic register int sigcode;
67330959Sbostic register int coredumped;
67460177Sbostic register const char *sigmsg;
67560177Sbostic char sigmsgbuf[10+1]; /* "Signal 127" + terminating '\0' */
676200Seric
6771316Seric # ifdef DEBUG
6781316Seric if (Debug)
6791316Seric {
6801316Seric printf("callprog:\n");
6811316Seric for (i = 0; argv[i] != NULL; i++)
6821316Seric printf("\t\"%s\"\n", argv[i]);
6831316Seric }
6841316Seric # endif
6851316Seric
686200Seric if (*argv == NULL)
687200Seric return (-1);
688200Seric
689157Seric /*
690226Seric ** Fork if appropriate.
691148Seric */
692148Seric
693200Seric if (forkflag)
694200Seric {
695393Seric # ifdef DEBUG
696393Seric if (Debug)
697393Seric printf("Forking\n");
698393Seric # endif
699200Seric i = fork();
700200Seric if (i < 0)
701200Seric {
7021205Seric syserr("cannot fork");
703200Seric exit(EX_OSERR);
704200Seric }
705200Seric else if (i > 0)
706201Seric {
70730959Sbostic while ((wpid = wait(&st)) != -1 && wpid != i)
70830959Sbostic ;
70930959Sbostic if ((sigcode = st & 0377) == 0)
7101282Seric st = (st >> 8) & 0377;
71130959Sbostic else
71230959Sbostic {
71330959Sbostic coredumped = sigcode & 0200;
71430959Sbostic sigcode &= 0177;
71530959Sbostic if (sigcode != SIGINT && sigcode != SIGPIPE)
71630959Sbostic {
71730959Sbostic if (sigcode < NSIG)
71830959Sbostic sigmsg = sys_siglist[sigcode];
71930959Sbostic else
72030959Sbostic {
72130959Sbostic sprintf(sigmsgbuf, "Signal %d",
72230959Sbostic sigcode);
72330959Sbostic sigmsg = sigmsgbuf;
72430959Sbostic }
72530959Sbostic fprintf(stderr, "sccs: %s: %s%s", argv[0],
72630959Sbostic sigmsg,
72730959Sbostic coredumped ? " - core dumped": "");
72830959Sbostic }
72930959Sbostic st = EX_SOFTWARE;
73030959Sbostic }
7311433Seric if (OutFile >= 0)
7321433Seric {
7331433Seric close(OutFile);
7341433Seric OutFile = -1;
7351433Seric }
736201Seric return (st);
737201Seric }
738200Seric }
7391433Seric else if (OutFile >= 0)
7401433Seric {
7411433Seric syserr("callprog: setting stdout w/o forking");
7421433Seric exit(EX_SOFTWARE);
7431433Seric }
744200Seric
7451433Seric /* set protection as appropriate */
746200Seric if (bitset(REALUSER, flags))
747200Seric setuid(getuid());
7481433Seric
7491433Seric /* change standard input & output if needed */
7501433Seric if (OutFile >= 0)
7511433Seric {
7521433Seric close(1);
7531433Seric dup(OutFile);
7541433Seric close(OutFile);
7551433Seric }
756226Seric
7571433Seric /* call real SCCS program */
758226Seric execv(progpath, argv);
7591205Seric syserr("cannot execute %s", progpath);
760148Seric exit(EX_UNAVAILABLE);
7611738Seric /*NOTREACHED*/
762148Seric }
7631432Seric
7641432Seric /*
765586Seric ** MAKEFILE -- make filename of SCCS file
766586Seric **
767586Seric ** If the name passed is already the name of an SCCS file,
768586Seric ** just return it. Otherwise, munge the name into the name
769586Seric ** of the actual SCCS file.
770586Seric **
771586Seric ** There are cases when it is not clear what you want to
772586Seric ** do. For example, if SccsPath is an absolute pathname
773586Seric ** and the name given is also an absolute pathname, we go
774586Seric ** for SccsPath (& only use the last component of the name
775586Seric ** passed) -- this is important for security reasons (if
776586Seric ** sccs is being used as a setuid front end), but not
777586Seric ** particularly intuitive.
778586Seric **
779586Seric ** Parameters:
780586Seric ** name -- the file name to be munged.
781586Seric **
782586Seric ** Returns:
783586Seric ** The pathname of the sccs file.
784586Seric ** NULL on error.
785586Seric **
786586Seric ** Side Effects:
787586Seric ** none.
788586Seric */
789148Seric
790148Seric char *
makefile(name)791148Seric makefile(name)
792148Seric char *name;
793148Seric {
794148Seric register char *p;
79510110Srrh char buf[3*FBUFSIZ];
796148Seric extern char *malloc();
797586Seric extern char *rindex();
798588Seric extern bool safepath();
799587Seric extern bool isdir();
800587Seric register char *q;
801148Seric
802586Seric p = rindex(name, '/');
803586Seric if (p == NULL)
804586Seric p = name;
805586Seric else
806586Seric p++;
807586Seric
808148Seric /*
809588Seric ** Check to see that the path is "safe", i.e., that we
810588Seric ** are not letting some nasty person use the setuid part
811588Seric ** of this program to look at or munge some presumably
812588Seric ** hidden files.
813148Seric */
814148Seric
815588Seric if (SccsDir[0] == '/' && !safepath(name))
816588Seric return (NULL);
817586Seric
818586Seric /*
819588Seric ** Create the base pathname.
820586Seric */
821586Seric
8221438Seric /* first the directory part */
823588Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0)
824148Seric {
82510110Srrh gstrcpy(buf, SccsDir, sizeof(buf));
82610110Srrh gstrcat(buf, "/", sizeof(buf));
827586Seric }
828586Seric else
82910110Srrh gstrcpy(buf, "", sizeof(buf));
8301438Seric
8311438Seric /* then the head of the pathname */
83210110Srrh gstrncat(buf, name, p - name, sizeof(buf));
833587Seric q = &buf[strlen(buf)];
8341438Seric
8351438Seric /* now copy the final part of the name, in case useful */
83610110Srrh gstrcpy(q, p, sizeof(buf));
8371438Seric
8381438Seric /* so is it useful? */
839587Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf))
840586Seric {
8411438Seric /* sorry, no; copy the SCCS pathname & the "s." */
84210110Srrh gstrcpy(q, SccsPath, sizeof(buf));
84310110Srrh gstrcat(buf, "/s.", sizeof(buf));
8441438Seric
8451438Seric /* and now the end of the name */
84610110Srrh gstrcat(buf, p, sizeof(buf));
847586Seric }
848148Seric
8491438Seric /* if i haven't changed it, why did I do all this? */
850588Seric if (strcmp(buf, name) == 0)
851588Seric p = name;
852588Seric else
853148Seric {
8541438Seric /* but if I have, squirrel it away */
855588Seric p = malloc(strlen(buf) + 1);
856588Seric if (p == NULL)
857588Seric {
858588Seric perror("Sccs: no mem");
859588Seric exit(EX_OSERR);
860588Seric }
861588Seric strcpy(p, buf);
862148Seric }
8631438Seric
864148Seric return (p);
865148Seric }
8661432Seric
8671432Seric /*
868587Seric ** ISDIR -- return true if the argument is a directory.
869587Seric **
870587Seric ** Parameters:
871587Seric ** name -- the pathname of the file to check.
872587Seric **
873587Seric ** Returns:
874587Seric ** TRUE if 'name' is a directory, FALSE otherwise.
875587Seric **
876587Seric ** Side Effects:
877587Seric ** none.
878587Seric */
879587Seric
880587Seric bool
isdir(name)881587Seric isdir(name)
882587Seric char *name;
883587Seric {
884587Seric struct stat stbuf;
885587Seric
886587Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR);
887587Seric }
8881432Seric
8891432Seric /*
890586Seric ** SAFEPATH -- determine whether a pathname is "safe"
891586Seric **
892586Seric ** "Safe" pathnames only allow you to get deeper into the
893586Seric ** directory structure, i.e., full pathnames and ".." are
894586Seric ** not allowed.
895586Seric **
896586Seric ** Parameters:
897586Seric ** p -- the name to check.
898586Seric **
899586Seric ** Returns:
900586Seric ** TRUE -- if the path is safe.
901586Seric ** FALSE -- if the path is not safe.
902586Seric **
903586Seric ** Side Effects:
904586Seric ** Prints a message if the path is not safe.
905586Seric */
906586Seric
907586Seric bool
safepath(p)908586Seric safepath(p)
909586Seric register char *p;
910586Seric {
911586Seric extern char *index();
912586Seric
913586Seric if (*p != '/')
914586Seric {
915586Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0)
916586Seric {
917586Seric p = index(p, '/');
918586Seric if (p == NULL)
919586Seric return (TRUE);
920586Seric p++;
921586Seric }
922586Seric }
923586Seric
924586Seric printf("You may not use full pathnames or \"..\"\n");
925586Seric return (FALSE);
926586Seric }
9271432Seric
9281432Seric /*
929261Seric ** CLEAN -- clean out recreatable files
930261Seric **
931261Seric ** Any file for which an "s." file exists but no "p." file
932261Seric ** exists in the current directory is purged.
933261Seric **
934261Seric ** Parameters:
9351822Seric ** mode -- tells whether this came from a "clean", "info", or
9361822Seric ** "check" command.
9371822Seric ** argv -- the rest of the argument vector.
938261Seric **
939261Seric ** Returns:
940261Seric ** none.
941261Seric **
942261Seric ** Side Effects:
943819Seric ** Removes files in the current directory.
944819Seric ** Prints information regarding files being edited.
945819Seric ** Exits if a "check" command.
946261Seric */
947261Seric
clean(mode,argv)9481822Seric clean(mode, argv)
949819Seric int mode;
9501822Seric char **argv;
951261Seric {
9526784Smckusick struct direct *dir;
95310110Srrh char buf[FBUFSIZ];
9542140Seric char *bufend;
95530466Smckusick register DIR *dirp;
956346Seric register char *basefile;
957351Seric bool gotedit;
9581822Seric bool gotpfent;
959394Seric FILE *pfp;
9601822Seric bool nobranch = FALSE;
9611822Seric extern struct pfile *getpfent();
9621822Seric register struct pfile *pf;
9631822Seric register char **ap;
9641864Seric extern char *username();
9651864Seric char *usernm = NULL;
9662140Seric char *subdir = NULL;
9672140Seric char *cmdname;
968261Seric
9691438Seric /*
9701822Seric ** Process the argv
9711822Seric */
9721822Seric
9732140Seric cmdname = *argv;
9742140Seric for (ap = argv; *++ap != NULL; )
9751822Seric {
9761864Seric if (**ap == '-')
9771864Seric {
9781864Seric /* we have a flag */
9791864Seric switch ((*ap)[1])
9801864Seric {
9811864Seric case 'b':
9821864Seric nobranch = TRUE;
9831864Seric break;
9841864Seric
9851864Seric case 'u':
9861864Seric if ((*ap)[2] != '\0')
9871864Seric usernm = &(*ap)[2];
9881864Seric else if (ap[1] != NULL && ap[1][0] != '-')
9891864Seric usernm = *++ap;
9901864Seric else
9911864Seric usernm = username();
9921864Seric break;
9931864Seric }
9941864Seric }
9952140Seric else
9962140Seric {
9972140Seric if (subdir != NULL)
9982140Seric usrerr("too many args");
9992140Seric else
10002140Seric subdir = *ap;
10012140Seric }
10021822Seric }
10031822Seric
10041822Seric /*
10051438Seric ** Find and open the SCCS directory.
10061438Seric */
10071438Seric
100810110Srrh gstrcpy(buf, SccsDir, sizeof(buf));
10091207Seric if (buf[0] != '\0')
101010110Srrh gstrcat(buf, "/", sizeof(buf));
10112140Seric if (subdir != NULL)
10122140Seric {
101310110Srrh gstrcat(buf, subdir, sizeof(buf));
101410110Srrh gstrcat(buf, "/", sizeof(buf));
10152140Seric }
101610110Srrh gstrcat(buf, SccsPath, sizeof(buf));
10172140Seric bufend = &buf[strlen(buf)];
10181438Seric
101930466Smckusick dirp = opendir(buf);
102030466Smckusick if (dirp == NULL)
1021261Seric {
10221207Seric usrerr("cannot open %s", buf);
10231282Seric return (EX_NOINPUT);
1024261Seric }
1025261Seric
1026261Seric /*
1027261Seric ** Scan the SCCS directory looking for s. files.
10281438Seric ** gotedit tells whether we have tried to clean any
10291438Seric ** files that are being edited.
1030261Seric */
1031261Seric
1032351Seric gotedit = FALSE;
103330466Smckusick while (dir = readdir(dirp)) {
10346784Smckusick if (strncmp(dir->d_name, "s.", 2) != 0)
1035261Seric continue;
1036261Seric
1037261Seric /* got an s. file -- see if the p. file exists */
103810110Srrh gstrcpy(bufend, "/p.", sizeof(buf));
10392140Seric basefile = bufend + 3;
104010110Srrh gstrcpy(basefile, &dir->d_name[2], sizeof(buf));
10411822Seric
10421822Seric /*
10431822Seric ** open and scan the p-file.
10441822Seric ** 'gotpfent' tells if we have found a valid p-file
10451822Seric ** entry.
10461822Seric */
10471822Seric
1048394Seric pfp = fopen(buf, "r");
10491822Seric gotpfent = FALSE;
1050394Seric if (pfp != NULL)
1051346Seric {
10521438Seric /* the file exists -- report it's contents */
10531822Seric while ((pf = getpfent(pfp)) != NULL)
10541730Seric {
10551822Seric if (nobranch && isbranch(pf->p_nsid))
10561822Seric continue;
10571864Seric if (usernm != NULL && strcmp(usernm, pf->p_user) != 0 && mode != CLEANC)
10581864Seric continue;
10591822Seric gotedit = TRUE;
10601822Seric gotpfent = TRUE;
10611822Seric if (mode == TELLC)
10621822Seric {
10631822Seric printf("%s\n", basefile);
10641822Seric break;
10651822Seric }
10662161Seric printf("%12s: being edited: ", basefile);
10672161Seric putpfent(pf, stdout);
10681730Seric }
1069394Seric fclose(pfp);
10701822Seric }
1071261Seric
1072261Seric /* the s. file exists and no p. file exists -- unlink the g-file */
10731870Seric if (mode == CLEANC && !gotpfent)
1074346Seric {
107510110Srrh char unlinkbuf[FBUFSIZ];
107610110Srrh gstrcpy(unlinkbuf, &dir->d_name[2], sizeof(unlinkbuf));
107710103Srrh unlink(unlinkbuf);
1078346Seric }
1079261Seric }
1080261Seric
10811438Seric /* cleanup & report results */
108230466Smckusick closedir(dirp);
1083819Seric if (!gotedit && mode == INFOC)
10841864Seric {
10851864Seric printf("Nothing being edited");
10861864Seric if (nobranch)
10871864Seric printf(" (on trunk)");
10881864Seric if (usernm == NULL)
10891864Seric printf("\n");
10901864Seric else
10911864Seric printf(" by %s\n", usernm);
10921864Seric }
1093819Seric if (mode == CHECKC)
1094819Seric exit(gotedit);
10951282Seric return (EX_OK);
1096261Seric }
10971432Seric
10981432Seric /*
10991822Seric ** ISBRANCH -- is the SID a branch?
11001822Seric **
11011822Seric ** Parameters:
11021822Seric ** sid -- the sid to check.
11031822Seric **
11041822Seric ** Returns:
11051822Seric ** TRUE if the sid represents a branch.
11061822Seric ** FALSE otherwise.
11071822Seric **
11081822Seric ** Side Effects:
11091822Seric ** none.
11101822Seric */
11111822Seric
isbranch(sid)11121822Seric isbranch(sid)
11131822Seric char *sid;
11141822Seric {
11151822Seric register char *p;
11161822Seric int dots;
11171822Seric
11181822Seric dots = 0;
11191822Seric for (p = sid; *p != '\0'; p++)
11201822Seric {
11211822Seric if (*p == '.')
11221822Seric dots++;
11231822Seric if (dots > 1)
11241822Seric return (TRUE);
11251822Seric }
11261822Seric return (FALSE);
11271822Seric }
11281822Seric
11291822Seric /*
1130396Seric ** UNEDIT -- unedit a file
1131396Seric **
1132396Seric ** Checks to see that the current user is actually editting
1133396Seric ** the file and arranges that s/he is not editting it.
1134396Seric **
1135396Seric ** Parameters:
1136416Seric ** fn -- the name of the file to be unedited.
1137396Seric **
1138396Seric ** Returns:
1139585Seric ** TRUE -- if the file was successfully unedited.
1140585Seric ** FALSE -- if the file was not unedited for some
1141585Seric ** reason.
1142396Seric **
1143396Seric ** Side Effects:
1144396Seric ** fn is removed
1145396Seric ** entries are removed from pfile.
1146396Seric */
1147396Seric
1148585Seric bool
unedit(fn)1149396Seric unedit(fn)
1150396Seric char *fn;
1151396Seric {
1152396Seric register FILE *pfp;
115312153Ssam char *cp, *pfn;
115437921Sbostic static char tfn[] = _PATH_TMP;
1155396Seric FILE *tfp;
1156396Seric register char *q;
1157396Seric bool delete = FALSE;
1158396Seric bool others = FALSE;
1159396Seric char *myname;
11601864Seric extern char *username();
1161396Seric struct pfile *pent;
11621822Seric extern struct pfile *getpfent();
116310110Srrh char buf[PFILELG];
116433251Sbostic extern char *makefile(), *rindex(), *tail();
1165396Seric
1166396Seric /* make "s." filename & find the trailing component */
1167396Seric pfn = makefile(fn);
1168586Seric if (pfn == NULL)
1169586Seric return (FALSE);
1170586Seric q = rindex(pfn, '/');
1171586Seric if (q == NULL)
1172586Seric q = &pfn[-1];
1173586Seric if (q[1] != 's' || q[2] != '.')
1174396Seric {
11751205Seric usrerr("bad file name \"%s\"", fn);
1176585Seric return (FALSE);
1177396Seric }
1178396Seric
11791438Seric /* turn "s." into "p." & try to open it */
1180396Seric *++q = 'p';
1181396Seric
1182396Seric pfp = fopen(pfn, "r");
1183396Seric if (pfp == NULL)
1184396Seric {
1185416Seric printf("%12s: not being edited\n", fn);
1186585Seric return (FALSE);
1187396Seric }
1188396Seric
11891438Seric /* create temp file for editing p-file */
1190396Seric mktemp(tfn);
1191396Seric tfp = fopen(tfn, "w");
1192396Seric if (tfp == NULL)
1193396Seric {
11941205Seric usrerr("cannot create \"%s\"", tfn);
1195396Seric exit(EX_OSERR);
1196396Seric }
1197396Seric
11981438Seric /* figure out who I am */
11991864Seric myname = username();
12001438Seric
12011438Seric /*
12021438Seric ** Copy p-file to temp file, doing deletions as needed.
12031438Seric */
12041438Seric
12051822Seric while ((pent = getpfent(pfp)) != NULL)
1206396Seric {
1207396Seric if (strcmp(pent->p_user, myname) == 0)
1208396Seric {
1209396Seric /* a match */
1210396Seric delete++;
1211396Seric }
1212396Seric else
1213396Seric {
12141438Seric /* output it again */
12152161Seric putpfent(pent, tfp);
1216396Seric others++;
1217396Seric }
1218396Seric }
1219396Seric
122012153Ssam /*
122112153Ssam * Before changing anything, make sure we can remove
122212153Ssam * the file in question (assuming it exists).
122312153Ssam */
122412153Ssam if (delete) {
122512153Ssam extern int errno;
122612153Ssam
122712153Ssam cp = tail(fn);
122812153Ssam errno = 0;
122912153Ssam if (access(cp, 0) < 0 && errno != ENOENT)
123012153Ssam goto bad;
123112153Ssam if (errno == 0)
123212153Ssam /*
123312153Ssam * This is wrong, but the rest of the program
123412153Ssam * has built in assumptions about "." as well,
123512153Ssam * so why make unedit a special case?
123612153Ssam */
123712153Ssam if (access(".", 2) < 0) {
123812153Ssam bad:
123912153Ssam printf("%12s: can't remove\n", cp);
124012153Ssam fclose(tfp);
124112153Ssam fclose(pfp);
124212153Ssam unlink(tfn);
124312153Ssam return (FALSE);
124412153Ssam }
124512153Ssam }
1246396Seric /* do final cleanup */
1247396Seric if (others)
1248396Seric {
12491438Seric /* copy it back (perhaps it should be linked?) */
1250396Seric if (freopen(tfn, "r", tfp) == NULL)
1251396Seric {
12521205Seric syserr("cannot reopen \"%s\"", tfn);
1253396Seric exit(EX_OSERR);
1254396Seric }
1255396Seric if (freopen(pfn, "w", pfp) == NULL)
1256396Seric {
12571205Seric usrerr("cannot create \"%s\"", pfn);
1258585Seric return (FALSE);
1259396Seric }
1260396Seric while (fgets(buf, sizeof buf, tfp) != NULL)
1261396Seric fputs(buf, pfp);
1262396Seric }
1263396Seric else
1264396Seric {
12651438Seric /* it's empty -- remove it */
1266396Seric unlink(pfn);
1267396Seric }
1268396Seric fclose(tfp);
1269396Seric fclose(pfp);
1270396Seric unlink(tfn);
1271396Seric
12721438Seric /* actually remove the g-file */
1273396Seric if (delete)
1274396Seric {
127512153Ssam /*
127612153Ssam * Since we've checked above, we can
127712153Ssam * use the return from unlink to
127812153Ssam * determine if the file existed or not.
127912153Ssam */
128012153Ssam if (unlink(cp) >= 0)
128112153Ssam printf("%12s: removed\n", cp);
1282585Seric return (TRUE);
1283396Seric }
1284396Seric else
1285396Seric {
1286416Seric printf("%12s: not being edited by you\n", fn);
1287585Seric return (FALSE);
1288396Seric }
1289396Seric }
12901432Seric
12911432Seric /*
12921433Seric ** DODIFF -- diff an s-file against a g-file
12931433Seric **
12941433Seric ** Parameters:
12951433Seric ** getv -- argv for the 'get' command.
12961433Seric ** gfile -- name of the g-file to diff against.
12971433Seric **
12981433Seric ** Returns:
12991433Seric ** Result of get.
13001433Seric **
13011433Seric ** Side Effects:
13021433Seric ** none.
13031433Seric */
13041433Seric
dodiff(getv,gfile)13051433Seric dodiff(getv, gfile)
13061433Seric char **getv;
13071433Seric char *gfile;
13081433Seric {
13091433Seric int pipev[2];
13101433Seric int rval;
13111433Seric register int i;
13121433Seric register int pid;
13131433Seric auto int st;
13141433Seric extern int errno;
131539832Sbostic sig_t osig;
13161433Seric
13171905Seric printf("\n------- %s -------\n", gfile);
13181871Seric fflush(stdout);
13191501Seric
13201438Seric /* create context for diff to run in */
13211433Seric if (pipe(pipev) < 0)
13221433Seric {
13231433Seric syserr("dodiff: pipe failed");
13241433Seric exit(EX_OSERR);
13251433Seric }
13261433Seric if ((pid = fork()) < 0)
13271433Seric {
13281433Seric syserr("dodiff: fork failed");
13291433Seric exit(EX_OSERR);
13301433Seric }
13311433Seric else if (pid > 0)
13321433Seric {
13331433Seric /* in parent; run get */
13341433Seric OutFile = pipev[1];
13351433Seric close(pipev[0]);
13361871Seric rval = command(&getv[1], TRUE, "get:rcixt -s -k -p");
13371433Seric osig = signal(SIGINT, SIG_IGN);
13381433Seric while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR)
13391433Seric errno = 0;
13401433Seric signal(SIGINT, osig);
13411433Seric /* ignore result of diff */
13421433Seric }
13431433Seric else
13441433Seric {
13451433Seric /* in child, run diff */
13461433Seric if (close(pipev[1]) < 0 || close(0) < 0 ||
13471433Seric dup(pipev[0]) != 0 || close(pipev[0]) < 0)
13481433Seric {
13491433Seric syserr("dodiff: magic failed");
13501433Seric exit(EX_OSERR);
13511433Seric }
13521871Seric command(&getv[1], FALSE, "-diff:elsfhbC");
13531433Seric }
13541433Seric return (rval);
13551433Seric }
13561433Seric
13571433Seric /*
13581435Seric ** TAIL -- return tail of filename.
13591435Seric **
13601435Seric ** Parameters:
13611435Seric ** fn -- the filename.
13621435Seric **
13631435Seric ** Returns:
13641435Seric ** a pointer to the tail of the filename; e.g., given
13651435Seric ** "cmd/ls.c", "ls.c" is returned.
13661435Seric **
13671435Seric ** Side Effects:
13681435Seric ** none.
13691435Seric */
13701435Seric
13711435Seric char *
tail(fn)13721435Seric tail(fn)
13731435Seric register char *fn;
13741435Seric {
13751435Seric register char *p;
13761435Seric
13771435Seric for (p = fn; *p != 0; p++)
13781435Seric if (*p == '/' && p[1] != '\0' && p[1] != '/')
13791435Seric fn = &p[1];
13801435Seric return (fn);
13811435Seric }
13821435Seric
13831435Seric /*
13841822Seric ** GETPFENT -- get an entry from the p-file
1385396Seric **
1386396Seric ** Parameters:
1387396Seric ** pfp -- p-file file pointer
1388396Seric **
1389396Seric ** Returns:
1390396Seric ** pointer to p-file struct for next entry
1391396Seric ** NULL on EOF or error
1392396Seric **
1393396Seric ** Side Effects:
1394396Seric ** Each call wipes out results of previous call.
1395396Seric */
1396396Seric
1397396Seric struct pfile *
getpfent(pfp)13981822Seric getpfent(pfp)
1399396Seric FILE *pfp;
1400396Seric {
1401396Seric static struct pfile ent;
140210110Srrh static char buf[PFILELG];
1403396Seric register char *p;
1404396Seric extern char *nextfield();
1405396Seric
1406396Seric if (fgets(buf, sizeof buf, pfp) == NULL)
1407396Seric return (NULL);
1408396Seric
1409396Seric ent.p_osid = p = buf;
1410396Seric ent.p_nsid = p = nextfield(p);
1411396Seric ent.p_user = p = nextfield(p);
1412396Seric ent.p_date = p = nextfield(p);
1413396Seric ent.p_time = p = nextfield(p);
14142161Seric ent.p_aux = p = nextfield(p);
1415396Seric
1416396Seric return (&ent);
1417396Seric }
1418396Seric
1419396Seric
1420396Seric char *
nextfield(p)1421396Seric nextfield(p)
1422396Seric register char *p;
1423396Seric {
1424396Seric if (p == NULL || *p == '\0')
1425396Seric return (NULL);
1426396Seric while (*p != ' ' && *p != '\n' && *p != '\0')
1427396Seric p++;
1428396Seric if (*p == '\n' || *p == '\0')
1429396Seric {
1430396Seric *p = '\0';
1431396Seric return (NULL);
1432396Seric }
1433396Seric *p++ = '\0';
1434396Seric return (p);
1435396Seric }
14362161Seric /*
14372161Seric ** PUTPFENT -- output a p-file entry to a file
14382161Seric **
14392161Seric ** Parameters:
14402161Seric ** pf -- the p-file entry
14412161Seric ** f -- the file to put it on.
14422161Seric **
14432161Seric ** Returns:
14442161Seric ** none.
14452161Seric **
14462161Seric ** Side Effects:
14472161Seric ** pf is written onto file f.
14482161Seric */
14492161Seric
putpfent(pf,f)14502161Seric putpfent(pf, f)
14512161Seric register struct pfile *pf;
14522161Seric register FILE *f;
14532161Seric {
14542161Seric fprintf(f, "%s %s %s %s %s", pf->p_osid, pf->p_nsid,
14552161Seric pf->p_user, pf->p_date, pf->p_time);
14562161Seric if (pf->p_aux != NULL)
14572161Seric fprintf(f, " %s", pf->p_aux);
14582161Seric else
14592161Seric fprintf(f, "\n");
14602161Seric }
14611432Seric
14621432Seric /*
14631205Seric ** USRERR -- issue user-level error
14641205Seric **
14651205Seric ** Parameters:
14661205Seric ** f -- format string.
14671205Seric ** p1-p3 -- parameters to a printf.
14681205Seric **
14691205Seric ** Returns:
14701205Seric ** -1
14711205Seric **
14721205Seric ** Side Effects:
14731205Seric ** none.
14741205Seric */
14751205Seric
14761738Seric /*VARARGS1*/
usrerr(f,p1,p2,p3)14771205Seric usrerr(f, p1, p2, p3)
14781205Seric char *f;
14791205Seric {
14801205Seric fprintf(stderr, "\n%s: ", MyName);
14811205Seric fprintf(stderr, f, p1, p2, p3);
14821205Seric fprintf(stderr, "\n");
14831205Seric
14841205Seric return (-1);
14851205Seric }
14861432Seric
14871432Seric /*
14881205Seric ** SYSERR -- print system-generated error.
14891205Seric **
14901205Seric ** Parameters:
14911205Seric ** f -- format string to a printf.
14921205Seric ** p1, p2, p3 -- parameters to f.
14931205Seric **
14941205Seric ** Returns:
14951205Seric ** never.
14961205Seric **
14971205Seric ** Side Effects:
14981205Seric ** none.
14991205Seric */
15001205Seric
15011738Seric /*VARARGS1*/
syserr(f,p1,p2,p3)15021205Seric syserr(f, p1, p2, p3)
15031205Seric char *f;
15041205Seric {
15051205Seric extern int errno;
15061205Seric
15071205Seric fprintf(stderr, "\n%s SYSERR: ", MyName);
15081205Seric fprintf(stderr, f, p1, p2, p3);
15091205Seric fprintf(stderr, "\n");
15101205Seric if (errno == 0)
15111205Seric exit(EX_SOFTWARE);
15121205Seric else
15131205Seric {
15141738Seric perror(NULL);
15151205Seric exit(EX_OSERR);
15161205Seric }
15171205Seric }
15181864Seric /*
15191864Seric ** USERNAME -- return name of the current user
15201864Seric **
15211864Seric ** Parameters:
15221864Seric ** none
15231864Seric **
15241864Seric ** Returns:
15251864Seric ** name of current user
15261864Seric **
15271864Seric ** Side Effects:
15281864Seric ** none
15291864Seric */
15301864Seric
15311864Seric char *
username()15321864Seric username()
15331864Seric {
15341864Seric # ifdef UIDUSER
15351864Seric extern struct passwd *getpwuid();
15361864Seric register struct passwd *pw;
15371864Seric
15381864Seric pw = getpwuid(getuid());
15391864Seric if (pw == NULL)
15401864Seric {
15411864Seric syserr("who are you? (uid=%d)", getuid());
15421864Seric exit(EX_OSERR);
15431864Seric }
15441864Seric return (pw->pw_name);
15451864Seric # else
15461905Seric extern char *getlogin();
15476785Smckusick register char *p;
15481905Seric
15496785Smckusick p = getenv("USER");
15506785Smckusick if (p == NULL || p[0] == '\0')
15516785Smckusick p = getlogin();
15526785Smckusick return (p);
15531864Seric # endif UIDUSER
15541864Seric }
155510110Srrh
155610110Srrh /*
155710110Srrh ** Guarded string manipulation routines; the last argument
155810110Srrh ** is the length of the buffer into which the strcpy or strcat
155910110Srrh ** is to be done.
156010110Srrh */
gstrcat(to,from,length)156110110Srrh char *gstrcat(to, from, length)
156210110Srrh char *to, *from;
156310110Srrh int length;
156410110Srrh {
156510110Srrh if (strlen(from) + strlen(to) >= length) {
156610110Srrh gstrbotch(to, from);
156710110Srrh }
156810110Srrh return(strcat(to, from));
156910110Srrh }
157010110Srrh
gstrncat(to,from,n,length)157110110Srrh char *gstrncat(to, from, n, length)
157210110Srrh char *to, *from;
157310110Srrh int n;
157410110Srrh int length;
157510110Srrh {
157610110Srrh if (n + strlen(to) >= length) {
157710110Srrh gstrbotch(to, from);
157810110Srrh }
157910110Srrh return(strncat(to, from, n));
158010110Srrh }
158110110Srrh
gstrcpy(to,from,length)158210110Srrh char *gstrcpy(to, from, length)
158310110Srrh char *to, *from;
158410110Srrh int length;
158510110Srrh {
158610110Srrh if (strlen(from) >= length) {
158710110Srrh gstrbotch(from, (char *)0);
158810110Srrh }
158910110Srrh return(strcpy(to, from));
159010110Srrh }
gstrbotch(str1,str2)159110110Srrh gstrbotch(str1, str2)
159210110Srrh char *str1, *str2;
159310110Srrh {
159410110Srrh usrerr("Filename(s) too long: %s %s", str1, str2);
159510110Srrh }
1596