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