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