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