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