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