|
|
1.1 root 1: #ifndef lint
2: static char sccsid[] = "@(#)sccs.c 1.71 (Berkeley) 7/2/83";
3: #endif
4:
5: # include <stdio.h>
6: # include <sys/param.h>
7: # include <sys/stat.h>
8: # include <sys/dir.h>
9: # include <errno.h>
10: # include <signal.h>
11: # include <sysexits.h>
12: # include <pwd.h>
13:
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.
72: ** check Like info, but return exit status, for
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.
81: ** SCCSDIR -- if defined, forces the -d flag to take on
82: ** this value. This is so that the setuid
83: ** aspects of this program cannot be abused.
84: ** This flag also disables the -p flag.
85: ** SCCSPATH -- the default for the -p flag.
86: ** MYNAME -- the title this program should print when it
87: ** gives error messages.
88: **
89: ** Compilation Instructions:
90: ** cc -O -n -s sccs.c
91: ** The flags listed above can be -D defined to simplify
92: ** recompilation for variant versions.
93: **
94: ** Author:
95: ** Eric Allman, UCB/INGRES
96: ** Copyright 1980 Regents of the University of California
97: */
98:
99:
100: /******************* Configuration Information ********************/
101:
102: # ifndef SCCSPATH
103: # define SCCSPATH "SCCS" /* pathname in which to find s-files */
104: # endif NOT SCCSPATH
105:
106: # ifndef MYNAME
107: # define MYNAME "sccs" /* name used for printing errors */
108: # endif NOT MYNAME
109:
110: # ifndef PROGPATH
111: # define PROGPATH(name) "/usr/local/name" /* place to find binaries */
112: # endif PROGPATH
113:
114: /**************** End of Configuration Information ****************/
115:
116: typedef char bool;
117: # define TRUE 1
118: # define FALSE 0
119:
120: # define bitset(bit, word) ((bool) ((bit) & (word)))
121:
122: struct sccsprog
123: {
124: char *sccsname; /* name of SCCS routine */
125: short sccsoper; /* opcode, see below */
126: short sccsflags; /* flags, see below */
127: char *sccspath; /* pathname of binary implementing */
128: };
129:
130: /* values for sccsoper */
131: # define PROG 0 /* call a program */
132: # define CMACRO 1 /* command substitution macro */
133: # define FIX 2 /* fix a delta */
134: # define CLEAN 3 /* clean out recreatable files */
135: # define UNEDIT 4 /* unedit a file */
136: # define SHELL 5 /* call a shell file (like PROG) */
137: # define DIFFS 6 /* diff between sccs & file out */
138: # define DODIFF 7 /* internal call to diff program */
139: # define ENTER 8 /* enter new files */
140:
141: /* bits for sccsflags */
142: # define NO_SDOT 0001 /* no s. on front of args */
143: # define REALUSER 0002 /* protected (e.g., admin) */
144:
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 */
149: # define TELLC 3 /* give list of files being edited */
150:
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: */
159:
160: struct sccsprog SccsProg[] =
161: {
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),
168: "prs", PROG, 0, PROGPATH(prs),
169: "prt", PROG, 0, PROGPATH(prt),
170: "rmdel", PROG, REALUSER, PROGPATH(rmdel),
171: "val", PROG, 0, PROGPATH(val),
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",
176: "deledit", CMACRO, NO_SDOT, "delta:mysrp -n/get:ixbskcl -e -t -g",
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,
184: "-diff", DODIFF, NO_SDOT|REALUSER, PROGPATH(bdiff),
185: "print", CMACRO, 0, "prt -e/get -p -m -s",
186: "branch", CMACRO, NO_SDOT,
187: "get:ixrc -e -b/delta: -s -n -ybranch-place-holder/get:pl -e -t -g",
188: "enter", ENTER, NO_SDOT, NULL,
189: "create", CMACRO, NO_SDOT, "enter/get:ixbeskcl -t",
190: NULL, -1, 0, NULL
191: };
192:
193: /* one line from a p-file */
194: struct 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 */
201: char *p_aux; /* extra info at end */
202: };
203:
204: char *SccsPath = SCCSPATH; /* pathname of SCCS files */
205: # ifdef SCCSDIR
206: char *SccsDir = SCCSDIR; /* directory to begin search from */
207: # else
208: char *SccsDir = "";
209: # endif
210: char MyName[] = MYNAME; /* name used in messages */
211: int OutFile = -1; /* override output file for commands */
212: bool RealUser; /* if set, running as real user */
213: # ifdef DEBUG
214: bool Debug; /* turn on tracing */
215: # endif
216: # ifndef V6
217: extern char *getenv();
218: # endif V6
219:
220: char *gstrcat(), *strcat();
221: char *gstrncat(), *strncat();
222: char *gstrcpy(), *strcpy();
223: #define FBUFSIZ BUFSIZ
224: #define PFILELG 120
225:
226: main(argc, argv)
227: int argc;
228: char **argv;
229: {
230: register char *p;
231: extern struct sccsprog *lookup();
232: register int i;
233: # ifndef V6
234: # ifndef SCCSDIR
235: register struct passwd *pw;
236: extern struct passwd *getpwnam();
237: char buf[FBUFSIZ];
238:
239: /* pull "SccsDir" out of the environment (possibly) */
240: p = getenv("PROJECTDIR");
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: }
253: gstrcpy(buf, pw->pw_dir, sizeof(buf));
254: gstrcat(buf, "/src", sizeof(buf));
255: if (access(buf, 0) < 0)
256: {
257: gstrcpy(buf, pw->pw_dir, sizeof(buf));
258: gstrcat(buf, "/source", sizeof(buf));
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: }
268: # endif SCCSDIR
269: # endif V6
270:
271: /*
272: ** Detect and decode flags intended for this program.
273: */
274:
275: if (argc < 2)
276: {
277: fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName);
278: exit(EX_USAGE);
279: }
280: argv[argc] = NULL;
281:
282: if (lookup(argv[0]) == NULL)
283: {
284: while ((p = *++argv) != NULL)
285: {
286: if (*p != '-')
287: break;
288: switch (*++p)
289: {
290: case 'r': /* run as real user */
291: setuid(getuid());
292: RealUser++;
293: break;
294:
295: # ifndef SCCSDIR
296: case 'p': /* path of sccs files */
297: SccsPath = ++p;
298: if (SccsPath[0] == '\0' && argv[1] != NULL)
299: SccsPath = *++argv;
300: break;
301:
302: case 'd': /* directory to search from */
303: SccsDir = ++p;
304: if (SccsDir[0] == '\0' && argv[1] != NULL)
305: SccsDir = *++argv;
306: break;
307: # endif
308:
309: # ifdef DEBUG
310: case 'T': /* trace */
311: Debug++;
312: break;
313: # endif
314:
315: default:
316: usrerr("unknown option -%s", p);
317: break;
318: }
319: }
320: if (SccsPath[0] == '\0')
321: SccsPath = ".";
322: }
323:
324: i = command(argv, FALSE, "");
325: exit(i);
326: }
327:
328: /*
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.
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.
342: **
343: ** Returns:
344: ** zero -- command executed ok.
345: ** else -- error status.
346: **
347: ** Side Effects:
348: ** none.
349: */
350:
351: command(argv, forkflag, arg0)
352: char **argv;
353: bool forkflag;
354: char *arg0;
355: {
356: register struct sccsprog *cmd;
357: register char *p;
358: char buf[FBUFSIZ];
359: extern struct sccsprog *lookup();
360: char *nav[1000];
361: char **np;
362: register char **ap;
363: register int i;
364: register char *q;
365: extern bool unedit();
366: int rval = 0;
367: extern char *index();
368: extern char *makefile();
369: char *editchs;
370: extern char *tail();
371:
372: # ifdef DEBUG
373: if (Debug)
374: {
375: printf("command:\n\t\"%s\"\n", arg0);
376: for (np = argv; *np != NULL; np++)
377: printf("\t\"%s\"\n", *np);
378: }
379: # endif
380:
381: /*
382: ** Copy arguments.
383: ** Copy from arg0 & if necessary at most one arg
384: ** from argv[0].
385: */
386:
387: np = ap = &nav[1];
388: editchs = NULL;
389: for (p = arg0, q = buf; *p != '\0' && *p != '/'; )
390: {
391: *np++ = q;
392: while (*p == ' ')
393: p++;
394: while (*p != ' ' && *p != '\0' && *p != '/' && *p != ':')
395: *q++ = *p++;
396: *q++ = '\0';
397: if (*p == ':')
398: {
399: editchs = q;
400: while (*++p != '\0' && *p != '/' && *p != ' ')
401: *q++ = *p;
402: *q++ = '\0';
403: }
404: }
405: *np = NULL;
406: if (*ap == NULL)
407: *np++ = *argv++;
408:
409: /*
410: ** Look up command.
411: ** At this point, *ap is the command name.
412: */
413:
414: cmd = lookup(*ap);
415: if (cmd == NULL)
416: {
417: usrerr("Unknown command \"%s\"", *ap);
418: return (EX_USAGE);
419: }
420:
421: /*
422: ** Copy remaining arguments doing editing as appropriate.
423: */
424:
425: for (; *argv != NULL; argv++)
426: {
427: p = *argv;
428: if (*p == '-')
429: {
430: if (p[1] == '\0' || editchs == NULL || index(editchs, p[1]) != NULL)
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:
443: /*
444: ** Interpret operation associated with this command.
445: */
446:
447: switch (cmd->sccsoper)
448: {
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:
455: case PROG: /* call an sccs prog */
456: rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag);
457: break;
458:
459: case CMACRO: /* command macro */
460: /* step through & execute each part of the macro */
461: for (p = cmd->sccspath; *p != '\0'; p++)
462: {
463: q = p;
464: while (*p != '\0' && *p != '/')
465: p++;
466: rval = command(&ap[1], *p != '\0', q);
467: if (rval != 0)
468: break;
469: }
470: break;
471:
472: case FIX: /* fix a delta */
473: if (strncmp(ap[1], "-r", 2) != 0)
474: {
475: usrerr("-r flag needed for fix command");
476: rval = EX_USAGE;
477: break;
478: }
479:
480: /* get the version with all changes */
481: rval = command(&ap[1], TRUE, "get -k");
482:
483: /* now remove that version from the s-file */
484: if (rval == 0)
485: rval = command(&ap[1], TRUE, "rmdel:r");
486:
487: /* and edit the old version (but don't clobber new vers) */
488: if (rval == 0)
489: rval = command(&ap[2], FALSE, "get -e -g");
490: break;
491:
492: case CLEAN:
493: rval = clean((int) cmd->sccspath, ap);
494: break;
495:
496: case UNEDIT:
497: for (argv = np = &ap[1]; *argv != NULL; argv++)
498: {
499: if (unedit(*argv))
500: *np++ = *argv;
501: }
502: *np = NULL;
503:
504: /* get all the files that we unedited successfully */
505: if (np > &ap[1])
506: rval = command(&ap[1], FALSE, "get");
507: break;
508:
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 */
516: p = argv[1];
517: while (*np != NULL)
518: {
519: /* messy, but we need a null terminated argv */
520: *argv = *np++;
521: argv[1] = NULL;
522: i = dodiff(ap, tail(*argv));
523: if (rval == 0)
524: rval = i;
525: argv[1] = p;
526: }
527: break;
528:
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:
550: case ENTER: /* enter new sccs files */
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: {
560: printf("\n%s:\n", *np);
561: strcpy(buf, "-i");
562: gstrcat(buf, *np, sizeof(buf));
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: {
570: strcpy(buf, ",");
571: gstrcat(buf, tail(*np), sizeof(buf));
572: if (link(*np, buf) >= 0)
573: unlink(*np);
574: }
575: np++;
576: }
577: break;
578:
579: default:
580: syserr("oper %d", cmd->sccsoper);
581: exit(EX_SOFTWARE);
582: }
583: # ifdef DEBUG
584: if (Debug)
585: printf("command: rval=%d\n", rval);
586: # endif
587: return (rval);
588: }
589:
590: /*
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:
604: struct sccsprog *
605: lookup(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: }
617:
618: /*
619: ** CALLPROG -- call a program
620: **
621: ** Used to call the SCCS programs.
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: */
637:
638: callprog(progpath, flags, argv, forkflag)
639: char *progpath;
640: short flags;
641: char **argv;
642: bool forkflag;
643: {
644: register int i;
645: auto int st;
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
655:
656: if (*argv == NULL)
657: return (-1);
658:
659: /*
660: ** Fork if appropriate.
661: */
662:
663: if (forkflag)
664: {
665: # ifdef DEBUG
666: if (Debug)
667: printf("Forking\n");
668: # endif
669: i = fork();
670: if (i < 0)
671: {
672: syserr("cannot fork");
673: exit(EX_OSERR);
674: }
675: else if (i > 0)
676: {
677: wait(&st);
678: if ((st & 0377) == 0)
679: st = (st >> 8) & 0377;
680: if (OutFile >= 0)
681: {
682: close(OutFile);
683: OutFile = -1;
684: }
685: return (st);
686: }
687: }
688: else if (OutFile >= 0)
689: {
690: syserr("callprog: setting stdout w/o forking");
691: exit(EX_SOFTWARE);
692: }
693:
694: /* set protection as appropriate */
695: if (bitset(REALUSER, flags))
696: setuid(getuid());
697:
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 */
707: execv(progpath, argv);
708: syserr("cannot execute %s", progpath);
709: exit(EX_UNAVAILABLE);
710: /*NOTREACHED*/
711: }
712:
713: /*
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: */
738:
739: char *
740: makefile(name)
741: char *name;
742: {
743: register char *p;
744: char buf[3*FBUFSIZ];
745: extern char *malloc();
746: extern char *rindex();
747: extern bool safepath();
748: extern bool isdir();
749: register char *q;
750:
751: p = rindex(name, '/');
752: if (p == NULL)
753: p = name;
754: else
755: p++;
756:
757: /*
758: ** Check to see that the path is "safe", i.e., that we
759: ** are not letting some nasty person use the setuid part
760: ** of this program to look at or munge some presumably
761: ** hidden files.
762: */
763:
764: if (SccsDir[0] == '/' && !safepath(name))
765: return (NULL);
766:
767: /*
768: ** Create the base pathname.
769: */
770:
771: /* first the directory part */
772: if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0)
773: {
774: gstrcpy(buf, SccsDir, sizeof(buf));
775: gstrcat(buf, "/", sizeof(buf));
776: }
777: else
778: gstrcpy(buf, "", sizeof(buf));
779:
780: /* then the head of the pathname */
781: gstrncat(buf, name, p - name, sizeof(buf));
782: q = &buf[strlen(buf)];
783:
784: /* now copy the final part of the name, in case useful */
785: gstrcpy(q, p, sizeof(buf));
786:
787: /* so is it useful? */
788: if (strncmp(p, "s.", 2) != 0 && !isdir(buf))
789: {
790: /* sorry, no; copy the SCCS pathname & the "s." */
791: gstrcpy(q, SccsPath, sizeof(buf));
792: gstrcat(buf, "/s.", sizeof(buf));
793:
794: /* and now the end of the name */
795: gstrcat(buf, p, sizeof(buf));
796: }
797:
798: /* if i haven't changed it, why did I do all this? */
799: if (strcmp(buf, name) == 0)
800: p = name;
801: else
802: {
803: /* but if I have, squirrel it away */
804: p = malloc(strlen(buf) + 1);
805: if (p == NULL)
806: {
807: perror("Sccs: no mem");
808: exit(EX_OSERR);
809: }
810: strcpy(p, buf);
811: }
812:
813: return (p);
814: }
815:
816: /*
817: ** ISDIR -- return true if the argument is a directory.
818: **
819: ** Parameters:
820: ** name -- the pathname of the file to check.
821: **
822: ** Returns:
823: ** TRUE if 'name' is a directory, FALSE otherwise.
824: **
825: ** Side Effects:
826: ** none.
827: */
828:
829: bool
830: isdir(name)
831: char *name;
832: {
833: struct stat stbuf;
834:
835: return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR);
836: }
837:
838: /*
839: ** SAFEPATH -- determine whether a pathname is "safe"
840: **
841: ** "Safe" pathnames only allow you to get deeper into the
842: ** directory structure, i.e., full pathnames and ".." are
843: ** not allowed.
844: **
845: ** Parameters:
846: ** p -- the name to check.
847: **
848: ** Returns:
849: ** TRUE -- if the path is safe.
850: ** FALSE -- if the path is not safe.
851: **
852: ** Side Effects:
853: ** Prints a message if the path is not safe.
854: */
855:
856: bool
857: safepath(p)
858: register char *p;
859: {
860: extern char *index();
861:
862: if (*p != '/')
863: {
864: while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0)
865: {
866: p = index(p, '/');
867: if (p == NULL)
868: return (TRUE);
869: p++;
870: }
871: }
872:
873: printf("You may not use full pathnames or \"..\"\n");
874: return (FALSE);
875: }
876:
877: /*
878: ** CLEAN -- clean out recreatable files
879: **
880: ** Any file for which an "s." file exists but no "p." file
881: ** exists in the current directory is purged.
882: **
883: ** Parameters:
884: ** mode -- tells whether this came from a "clean", "info", or
885: ** "check" command.
886: ** argv -- the rest of the argument vector.
887: **
888: ** Returns:
889: ** none.
890: **
891: ** Side Effects:
892: ** Removes files in the current directory.
893: ** Prints information regarding files being edited.
894: ** Exits if a "check" command.
895: */
896:
897: clean(mode, argv)
898: int mode;
899: char **argv;
900: {
901: struct direct *dir;
902: char buf[FBUFSIZ];
903: char *bufend;
904: register DIR *dirfd;
905: register char *basefile;
906: bool gotedit;
907: bool gotpfent;
908: FILE *pfp;
909: bool nobranch = FALSE;
910: extern struct pfile *getpfent();
911: register struct pfile *pf;
912: register char **ap;
913: extern char *username();
914: char *usernm = NULL;
915: char *subdir = NULL;
916: char *cmdname;
917:
918: /*
919: ** Process the argv
920: */
921:
922: cmdname = *argv;
923: for (ap = argv; *++ap != NULL; )
924: {
925: if (**ap == '-')
926: {
927: /* we have a flag */
928: switch ((*ap)[1])
929: {
930: case 'b':
931: nobranch = TRUE;
932: break;
933:
934: case 'u':
935: if ((*ap)[2] != '\0')
936: usernm = &(*ap)[2];
937: else if (ap[1] != NULL && ap[1][0] != '-')
938: usernm = *++ap;
939: else
940: usernm = username();
941: break;
942: }
943: }
944: else
945: {
946: if (subdir != NULL)
947: usrerr("too many args");
948: else
949: subdir = *ap;
950: }
951: }
952:
953: /*
954: ** Find and open the SCCS directory.
955: */
956:
957: gstrcpy(buf, SccsDir, sizeof(buf));
958: if (buf[0] != '\0')
959: gstrcat(buf, "/", sizeof(buf));
960: if (subdir != NULL)
961: {
962: gstrcat(buf, subdir, sizeof(buf));
963: gstrcat(buf, "/", sizeof(buf));
964: }
965: gstrcat(buf, SccsPath, sizeof(buf));
966: bufend = &buf[strlen(buf)];
967:
968: dirfd = opendir(buf);
969: if (dirfd == NULL)
970: {
971: usrerr("cannot open %s", buf);
972: return (EX_NOINPUT);
973: }
974:
975: /*
976: ** Scan the SCCS directory looking for s. files.
977: ** gotedit tells whether we have tried to clean any
978: ** files that are being edited.
979: */
980:
981: gotedit = FALSE;
982: while (dir = readdir(dirfd)) {
983: if (strncmp(dir->d_name, "s.", 2) != 0)
984: continue;
985:
986: /* got an s. file -- see if the p. file exists */
987: gstrcpy(bufend, "/p.", sizeof(buf));
988: basefile = bufend + 3;
989: gstrcpy(basefile, &dir->d_name[2], sizeof(buf));
990:
991: /*
992: ** open and scan the p-file.
993: ** 'gotpfent' tells if we have found a valid p-file
994: ** entry.
995: */
996:
997: pfp = fopen(buf, "r");
998: gotpfent = FALSE;
999: if (pfp != NULL)
1000: {
1001: /* the file exists -- report it's contents */
1002: while ((pf = getpfent(pfp)) != NULL)
1003: {
1004: if (nobranch && isbranch(pf->p_nsid))
1005: continue;
1006: if (usernm != NULL && strcmp(usernm, pf->p_user) != 0 && mode != CLEANC)
1007: continue;
1008: gotedit = TRUE;
1009: gotpfent = TRUE;
1010: if (mode == TELLC)
1011: {
1012: printf("%s\n", basefile);
1013: break;
1014: }
1015: printf("%12s: being edited: ", basefile);
1016: putpfent(pf, stdout);
1017: }
1018: fclose(pfp);
1019: }
1020:
1021: /* the s. file exists and no p. file exists -- unlink the g-file */
1022: if (mode == CLEANC && !gotpfent)
1023: {
1024: char unlinkbuf[FBUFSIZ];
1025: gstrcpy(unlinkbuf, &dir->d_name[2], sizeof(unlinkbuf));
1026: unlink(unlinkbuf);
1027: }
1028: }
1029:
1030: /* cleanup & report results */
1031: closedir(dirfd);
1032: if (!gotedit && mode == INFOC)
1033: {
1034: printf("Nothing being edited");
1035: if (nobranch)
1036: printf(" (on trunk)");
1037: if (usernm == NULL)
1038: printf("\n");
1039: else
1040: printf(" by %s\n", usernm);
1041: }
1042: if (mode == CHECKC)
1043: exit(gotedit);
1044: return (EX_OK);
1045: }
1046:
1047: /*
1048: ** ISBRANCH -- is the SID a branch?
1049: **
1050: ** Parameters:
1051: ** sid -- the sid to check.
1052: **
1053: ** Returns:
1054: ** TRUE if the sid represents a branch.
1055: ** FALSE otherwise.
1056: **
1057: ** Side Effects:
1058: ** none.
1059: */
1060:
1061: isbranch(sid)
1062: char *sid;
1063: {
1064: register char *p;
1065: int dots;
1066:
1067: dots = 0;
1068: for (p = sid; *p != '\0'; p++)
1069: {
1070: if (*p == '.')
1071: dots++;
1072: if (dots > 1)
1073: return (TRUE);
1074: }
1075: return (FALSE);
1076: }
1077:
1078: /*
1079: ** UNEDIT -- unedit a file
1080: **
1081: ** Checks to see that the current user is actually editting
1082: ** the file and arranges that s/he is not editting it.
1083: **
1084: ** Parameters:
1085: ** fn -- the name of the file to be unedited.
1086: **
1087: ** Returns:
1088: ** TRUE -- if the file was successfully unedited.
1089: ** FALSE -- if the file was not unedited for some
1090: ** reason.
1091: **
1092: ** Side Effects:
1093: ** fn is removed
1094: ** entries are removed from pfile.
1095: */
1096:
1097: bool
1098: unedit(fn)
1099: char *fn;
1100: {
1101: register FILE *pfp;
1102: char *cp, *pfn;
1103: static char tfn[] = "/tmp/sccsXXXXX";
1104: FILE *tfp;
1105: register char *q;
1106: bool delete = FALSE;
1107: bool others = FALSE;
1108: char *myname;
1109: extern char *username();
1110: struct pfile *pent;
1111: extern struct pfile *getpfent();
1112: char buf[PFILELG];
1113: extern char *makefile();
1114:
1115: /* make "s." filename & find the trailing component */
1116: pfn = makefile(fn);
1117: if (pfn == NULL)
1118: return (FALSE);
1119: q = rindex(pfn, '/');
1120: if (q == NULL)
1121: q = &pfn[-1];
1122: if (q[1] != 's' || q[2] != '.')
1123: {
1124: usrerr("bad file name \"%s\"", fn);
1125: return (FALSE);
1126: }
1127:
1128: /* turn "s." into "p." & try to open it */
1129: *++q = 'p';
1130:
1131: pfp = fopen(pfn, "r");
1132: if (pfp == NULL)
1133: {
1134: printf("%12s: not being edited\n", fn);
1135: return (FALSE);
1136: }
1137:
1138: /* create temp file for editing p-file */
1139: mktemp(tfn);
1140: tfp = fopen(tfn, "w");
1141: if (tfp == NULL)
1142: {
1143: usrerr("cannot create \"%s\"", tfn);
1144: exit(EX_OSERR);
1145: }
1146:
1147: /* figure out who I am */
1148: myname = username();
1149:
1150: /*
1151: ** Copy p-file to temp file, doing deletions as needed.
1152: */
1153:
1154: while ((pent = getpfent(pfp)) != NULL)
1155: {
1156: if (strcmp(pent->p_user, myname) == 0)
1157: {
1158: /* a match */
1159: delete++;
1160: }
1161: else
1162: {
1163: /* output it again */
1164: putpfent(pent, tfp);
1165: others++;
1166: }
1167: }
1168:
1169: /*
1170: * Before changing anything, make sure we can remove
1171: * the file in question (assuming it exists).
1172: */
1173: if (delete) {
1174: extern int errno;
1175:
1176: cp = tail(fn);
1177: errno = 0;
1178: if (access(cp, 0) < 0 && errno != ENOENT)
1179: goto bad;
1180: if (errno == 0)
1181: /*
1182: * This is wrong, but the rest of the program
1183: * has built in assumptions about "." as well,
1184: * so why make unedit a special case?
1185: */
1186: if (access(".", 2) < 0) {
1187: bad:
1188: printf("%12s: can't remove\n", cp);
1189: fclose(tfp);
1190: fclose(pfp);
1191: unlink(tfn);
1192: return (FALSE);
1193: }
1194: }
1195: /* do final cleanup */
1196: if (others)
1197: {
1198: /* copy it back (perhaps it should be linked?) */
1199: if (freopen(tfn, "r", tfp) == NULL)
1200: {
1201: syserr("cannot reopen \"%s\"", tfn);
1202: exit(EX_OSERR);
1203: }
1204: if (freopen(pfn, "w", pfp) == NULL)
1205: {
1206: usrerr("cannot create \"%s\"", pfn);
1207: return (FALSE);
1208: }
1209: while (fgets(buf, sizeof buf, tfp) != NULL)
1210: fputs(buf, pfp);
1211: }
1212: else
1213: {
1214: /* it's empty -- remove it */
1215: unlink(pfn);
1216: }
1217: fclose(tfp);
1218: fclose(pfp);
1219: unlink(tfn);
1220:
1221: /* actually remove the g-file */
1222: if (delete)
1223: {
1224: /*
1225: * Since we've checked above, we can
1226: * use the return from unlink to
1227: * determine if the file existed or not.
1228: */
1229: if (unlink(cp) >= 0)
1230: printf("%12s: removed\n", cp);
1231: return (TRUE);
1232: }
1233: else
1234: {
1235: printf("%12s: not being edited by you\n", fn);
1236: return (FALSE);
1237: }
1238: }
1239:
1240: /*
1241: ** DODIFF -- diff an s-file against a g-file
1242: **
1243: ** Parameters:
1244: ** getv -- argv for the 'get' command.
1245: ** gfile -- name of the g-file to diff against.
1246: **
1247: ** Returns:
1248: ** Result of get.
1249: **
1250: ** Side Effects:
1251: ** none.
1252: */
1253:
1254: dodiff(getv, gfile)
1255: char **getv;
1256: char *gfile;
1257: {
1258: int pipev[2];
1259: int rval;
1260: register int i;
1261: register int pid;
1262: auto int st;
1263: extern int errno;
1264: int (*osig)();
1265:
1266: printf("\n------- %s -------\n", gfile);
1267: fflush(stdout);
1268:
1269: /* create context for diff to run in */
1270: if (pipe(pipev) < 0)
1271: {
1272: syserr("dodiff: pipe failed");
1273: exit(EX_OSERR);
1274: }
1275: if ((pid = fork()) < 0)
1276: {
1277: syserr("dodiff: fork failed");
1278: exit(EX_OSERR);
1279: }
1280: else if (pid > 0)
1281: {
1282: /* in parent; run get */
1283: OutFile = pipev[1];
1284: close(pipev[0]);
1285: rval = command(&getv[1], TRUE, "get:rcixt -s -k -p");
1286: osig = signal(SIGINT, SIG_IGN);
1287: while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR)
1288: errno = 0;
1289: signal(SIGINT, osig);
1290: /* ignore result of diff */
1291: }
1292: else
1293: {
1294: /* in child, run diff */
1295: if (close(pipev[1]) < 0 || close(0) < 0 ||
1296: dup(pipev[0]) != 0 || close(pipev[0]) < 0)
1297: {
1298: syserr("dodiff: magic failed");
1299: exit(EX_OSERR);
1300: }
1301: command(&getv[1], FALSE, "-diff:elsfhbC");
1302: }
1303: return (rval);
1304: }
1305:
1306: /*
1307: ** TAIL -- return tail of filename.
1308: **
1309: ** Parameters:
1310: ** fn -- the filename.
1311: **
1312: ** Returns:
1313: ** a pointer to the tail of the filename; e.g., given
1314: ** "cmd/ls.c", "ls.c" is returned.
1315: **
1316: ** Side Effects:
1317: ** none.
1318: */
1319:
1320: char *
1321: tail(fn)
1322: register char *fn;
1323: {
1324: register char *p;
1325:
1326: for (p = fn; *p != 0; p++)
1327: if (*p == '/' && p[1] != '\0' && p[1] != '/')
1328: fn = &p[1];
1329: return (fn);
1330: }
1331:
1332: /*
1333: ** GETPFENT -- get an entry from the p-file
1334: **
1335: ** Parameters:
1336: ** pfp -- p-file file pointer
1337: **
1338: ** Returns:
1339: ** pointer to p-file struct for next entry
1340: ** NULL on EOF or error
1341: **
1342: ** Side Effects:
1343: ** Each call wipes out results of previous call.
1344: */
1345:
1346: struct pfile *
1347: getpfent(pfp)
1348: FILE *pfp;
1349: {
1350: static struct pfile ent;
1351: static char buf[PFILELG];
1352: register char *p;
1353: extern char *nextfield();
1354:
1355: if (fgets(buf, sizeof buf, pfp) == NULL)
1356: return (NULL);
1357:
1358: ent.p_osid = p = buf;
1359: ent.p_nsid = p = nextfield(p);
1360: ent.p_user = p = nextfield(p);
1361: ent.p_date = p = nextfield(p);
1362: ent.p_time = p = nextfield(p);
1363: ent.p_aux = p = nextfield(p);
1364:
1365: return (&ent);
1366: }
1367:
1368:
1369: char *
1370: nextfield(p)
1371: register char *p;
1372: {
1373: if (p == NULL || *p == '\0')
1374: return (NULL);
1375: while (*p != ' ' && *p != '\n' && *p != '\0')
1376: p++;
1377: if (*p == '\n' || *p == '\0')
1378: {
1379: *p = '\0';
1380: return (NULL);
1381: }
1382: *p++ = '\0';
1383: return (p);
1384: }
1385: /*
1386: ** PUTPFENT -- output a p-file entry to a file
1387: **
1388: ** Parameters:
1389: ** pf -- the p-file entry
1390: ** f -- the file to put it on.
1391: **
1392: ** Returns:
1393: ** none.
1394: **
1395: ** Side Effects:
1396: ** pf is written onto file f.
1397: */
1398:
1399: putpfent(pf, f)
1400: register struct pfile *pf;
1401: register FILE *f;
1402: {
1403: fprintf(f, "%s %s %s %s %s", pf->p_osid, pf->p_nsid,
1404: pf->p_user, pf->p_date, pf->p_time);
1405: if (pf->p_aux != NULL)
1406: fprintf(f, " %s", pf->p_aux);
1407: else
1408: fprintf(f, "\n");
1409: }
1410:
1411: /*
1412: ** USRERR -- issue user-level error
1413: **
1414: ** Parameters:
1415: ** f -- format string.
1416: ** p1-p3 -- parameters to a printf.
1417: **
1418: ** Returns:
1419: ** -1
1420: **
1421: ** Side Effects:
1422: ** none.
1423: */
1424:
1425: /*VARARGS1*/
1426: usrerr(f, p1, p2, p3)
1427: char *f;
1428: {
1429: fprintf(stderr, "\n%s: ", MyName);
1430: fprintf(stderr, f, p1, p2, p3);
1431: fprintf(stderr, "\n");
1432:
1433: return (-1);
1434: }
1435:
1436: /*
1437: ** SYSERR -- print system-generated error.
1438: **
1439: ** Parameters:
1440: ** f -- format string to a printf.
1441: ** p1, p2, p3 -- parameters to f.
1442: **
1443: ** Returns:
1444: ** never.
1445: **
1446: ** Side Effects:
1447: ** none.
1448: */
1449:
1450: /*VARARGS1*/
1451: syserr(f, p1, p2, p3)
1452: char *f;
1453: {
1454: extern int errno;
1455:
1456: fprintf(stderr, "\n%s SYSERR: ", MyName);
1457: fprintf(stderr, f, p1, p2, p3);
1458: fprintf(stderr, "\n");
1459: if (errno == 0)
1460: exit(EX_SOFTWARE);
1461: else
1462: {
1463: perror(NULL);
1464: exit(EX_OSERR);
1465: }
1466: }
1467: /*
1468: ** USERNAME -- return name of the current user
1469: **
1470: ** Parameters:
1471: ** none
1472: **
1473: ** Returns:
1474: ** name of current user
1475: **
1476: ** Side Effects:
1477: ** none
1478: */
1479:
1480: char *
1481: username()
1482: {
1483: # ifdef UIDUSER
1484: extern struct passwd *getpwuid();
1485: register struct passwd *pw;
1486:
1487: pw = getpwuid(getuid());
1488: if (pw == NULL)
1489: {
1490: syserr("who are you? (uid=%d)", getuid());
1491: exit(EX_OSERR);
1492: }
1493: return (pw->pw_name);
1494: # else
1495: extern char *getlogin();
1496: extern char *getenv();
1497: register char *p;
1498:
1499: p = getenv("USER");
1500: if (p == NULL || p[0] == '\0')
1501: p = getlogin();
1502: return (p);
1503: # endif UIDUSER
1504: }
1505:
1506: /*
1507: ** Guarded string manipulation routines; the last argument
1508: ** is the length of the buffer into which the strcpy or strcat
1509: ** is to be done.
1510: */
1511: char *gstrcat(to, from, length)
1512: char *to, *from;
1513: int length;
1514: {
1515: if (strlen(from) + strlen(to) >= length) {
1516: gstrbotch(to, from);
1517: }
1518: return(strcat(to, from));
1519: }
1520:
1521: char *gstrncat(to, from, n, length)
1522: char *to, *from;
1523: int n;
1524: int length;
1525: {
1526: if (n + strlen(to) >= length) {
1527: gstrbotch(to, from);
1528: }
1529: return(strncat(to, from, n));
1530: }
1531:
1532: char *gstrcpy(to, from, length)
1533: char *to, *from;
1534: int length;
1535: {
1536: if (strlen(from) >= length) {
1537: gstrbotch(from, (char *)0);
1538: }
1539: return(strcpy(to, from));
1540: }
1541: gstrbotch(str1, str2)
1542: char *str1, *str2;
1543: {
1544: usrerr("Filename(s) too long: %s %s", str1, str2);
1545: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.