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