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