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