housel@en.ecn.purdue.edu (Peter S. Housel) (01/17/89)
For those who missed it last time around, or who rejected it because it required too many fixes to the stdio libraries, here is SVC again. It has gotten me through 1.1->1.2->1.3*->1.3c conversions during the past year without any problems whatsoever. -Peter S. Housel- housel@en.ecn.purdue.edu ...!pur-ee!housel echo 'x - Makefile' sed 's/^X//' <<'**-Makefile-EOF-**' >Makefile X# X# makefile for SVC X# X XCFLAGS = -F -T/usr/tmp X Xall: ci co X Xci: ci.c X cc $(CFLAGS) -o ci ci.c X chmem =8000 ci X Xco: co.c X cc $(CFLAGS) -o co co.c X chmem =8000 co X Xsvclog: svclog.sh X cp svclog.sh svclog X chmod 755 svclog **-Makefile-EOF-** echo 'x - README' sed 's/^X//' <<'**-README-EOF-**' >README XThis file explains the purposes of the files in the SVC package. X X SVC is the Shell Version Control system, and is inspired by Walter XTichy's Revision Control System, or RCS. The intention of SVC is to implement Xthe subset of RCS that is commonly used for everyday maintainance. (That is, Xmost of the part I know how to use.) X X SVC requires Minix 1.3d or Minix 1.2 with the 1.3 patches for Xgetc()/fgetc(), fseek(), scanf(), and ctime(). I suspect some of these Xbugs have not been fixed in the ST versions of these files, but I may Xbe wrong. X X To use SVC on a non-Minix system, you will need to find a copy Xof "fix", or of Larry Wall's "patch" program. To use patch, you need to Xcompile ci.c with "-DPATCH". X XMAN PAGES X XCommand: ci - check in an SVC revision XSyntax: ci [-l] [-u] file XFlags: -l After checking in, check back out again and lock X -u After checking in, do not delete the file XExamples: ci -u Makefile check Makefile back in X ci -l newfile create SVC file for newfile, relock X X Ci checks the specified file into its SVC archive. If an SVC archive file Xdid not exist, one is created with the name "file,S". (If a directory named X"SVC" is present in the same directory as "file", the archive will be placed Xthere instead.) X A log message will be prompted for, and read from standard input; this Xmay be several lines, terminated with "." on a line by itself or EOT. After Xthe file has been checked in, the default action is to remove the source Xfile. If "-u" is specified, the file will not be unlinked, but all write Xpermissions will be removed. If "-l" is specified, a lock is placed on Xthe SVC archive, and the file will remain writable by the owner. SVC archive Xfiles are always read-only. X Version numbers start at "1" and are incremented by one for each revision Xchecked in. X X XCommand: co - check out an SVC revision XSyntax: co [-l] [-r rev] file XFlags: -l Place a lock on the SVC archive after checkout X -r rev Check out revision "rev" instead most recent revision XExamples: co -l Makefile check out and lock Makefile X co -r 4 foo check out version 4 of foo X X Co checks the specified file out of its SVC archive. If the archive X".../file,S" does not exist, it is searched for in ".../SVC/file,S". X The specified revision will be checked out and placed in "file"; by Xdefault it will be non-writable. If "-l" is specified, a lock will be Xplaced on the archive and the file will be writable. X X XCommand: svclog - print log of SVC revisions XSyntax: svclog file XFlags: none XExamples: svclog foo print out revision history for foo X svclog Makefile,S print out revision history for Makefile X X Svclog prints out the revision history of the specified file. This Xinformation includes the revision numbers, dates, and log entries for each Xof the revisions, and indicates whether or not a lock on this file exists. XFor simplicity, the output format is rather raw. X X XINSTALLATION X X Installation of SVC should be rather straightforward. The "ci" and X"co" binaries should be compiled using the supplied makefile, and the "ci", X"co", and "svclog" files should be put in your favorite default path Xdirectory (such as /usr/bin). It will also be helpful to increase the memory Xallocation for /usr/bin/diff and /usr/bin/fix (using chmem) to as high as Xpossible. X XBACKGROUND X X SVC stands for Shell Version Control, and is so named for two Xreasons. The first is that it was originally implemented using Bourne Xshell scripts. (These are included as "ci.sh" and "co.sh"; they serve Xno useful purpose in the current version but are presented for your Xamusement and/or enlightenment). This implementation gave pretty good Xperformance on an 8-MIP machine running BSD 4.3-Tahoe, but on my AT Xcompatible it was just too slow. Also, the scripts make heavy use of X"sed," which didn't quite work on Minix at the time. For this reason X"ci" and "co" were rewritten in C. X X The other reason for the name is that the SVC archives are in the Xform of shell scripts, in the manner of shar-files. The original reason for Xthis was so that the archives could literally extract themselves - by Xexecuting them with "sh". This format is reasonably easy to figure out by Xreading it, and not too difficult for the program to understand. In a pinch, Xyou can do without "co". X X However, there is a problem with this approach. Shell input using X"<<" (a here document in Minix sh source file terminology) is processed Xby the shell, including the expansion of environment variables and such. XThis means that files which contain shell-variable syntax (i.e. makefiles Xand other shell scripts) may be garbled on extraction. This was another Xreason why the programs were translated into C. (Here documents are also Xvery slow...) X X ci.c and co.c were designed to be somewhat bullet-proof, and have Xworked very well in the time I have been using them. X XSUGGESTED USE X X The major use for SVC on my system is keeping track of system Xsources. There are "SVC" subdirectories all throughout /usr/src, and Xwhenever a fix is posted to the net I can check out a file, apply the Xpatches, and check it back in. The advantage of this is that if I want Xto refer to an earlier version of the file, or want to apply patches to a Xfile I have changed since the last official version, an older revision can Xbe checked out (going back to Minix 1.1 in most cases). X X I suggest experimenting with the system to get a feel for how it Xworks if you are not already familiar with RCS. Do not use it on any Xfiles for which you do not have a backup copy until you are confident Xthat you are doing things correctly. X X-Peter S. Housel- housel@ecn.purdue.edu ...!pur-ee!housel **-README-EOF-** echo 'x - ci.c' sed 's/^X//' <<'**-ci.c-EOF-**' >ci.c X/* file: ci.c X** author: Peter S. Housel 12/17/87 X*/ X X#include <stdio.h> X#include <string.h> X#include <sys/stat.h> X#include <pwd.h> X#include <signal.h> X X#define SUFFIX ",S" /* svc indicator */ X#define SVCDIR "SVC" /* svc postfix indicator */ X X#define LINELEN 256 /* maximum line length */ X X#ifndef PATCH X#define FIX "fix $1 Fix.$1 > New.$1; mv New.$1 $1\n" X#else X#define FIX "patch -n -s $1 < Fix.$1; rm -f $1.orig\n" X#endif !PATCH X X#ifdef MAXPATHLEN X#define PATHLEN MAXPATHLEN X#else X#define PATHLEN 128 /* buffer length for filenames */ X#endif X Xint unlocked = 0; /* leave unlocked after checkin */ Xint relock = 0; /* lock next revision after checkin */ Xchar file[PATHLEN]; /* file to be checked in */ Xchar svc[PATHLEN]; /* filename for svc file */ Xchar newsvc[PATHLEN]; /* new copy of SVC file */ Xchar line[LINELEN]; /* temporary line buffer */ Xchar *p; /* scratch character pointer */ X XFILE *svcfp; /* svc file */ XFILE *origfp, *newfp; /* "orig" and "new" temp files */ XFILE *srcfp; /* source file */ Xint rev; /* new revision number */ Xint status; /* wait() buffer */ Xstruct stat stb1, stb2; /* stat buffers for size compare */ Xchar original[] = "/tmp/cioXXXXXX"; /* previous revision */ Xchar diffout[] = "/tmp/cidXXXXXX"; /* diffs */ X Xextern FILE *fopen(); Xextern char *mktemp(), *fgets(), *rindex(), *index(); Xextern char *ctime(); Xextern struct passwd *getpwuid(); Xextern long ftell(); X Xchar *whoami(); Xint onintr(); X Xmain(argc, argv) Xint argc; char **argv; X{ X#ifdef perprintf X char errbuf[BUFSIZ]; X setbuf(stderr, errbuf); X perprintf(stderr); X#endif X X while(++argv, --argc) X { X if('-' == (*argv)[0]) X { X if('u' == (*argv)[1]) X ++unlocked; X else if('l' == (*argv)[1]) X ++relock; X else X {fprintf(stderr, "ci: illegal option -%c\n", (*argv)[1]); X exit(1); X } X } X else X break; X } X X if(1 != argc) X { X fprintf(stderr, "ci: bad number of files arguments\n"); X exit(1); X } X X fname(*argv, file); X svcname(file, svc); X X fprintf(stderr, "%s -> %s\n", file, svc); X X signal(SIGHUP, onintr); X signal(SIGINT, onintr); X signal(SIGTERM, onintr); X X#ifndef BSD X if(NULL == (p = rindex(file, '/'))) X p = file; X else X ++p; X X if(strlen(p) > 13) X { X fprintf(stderr, "ci: filename %s is too long\n"); X exit(1); X } X#endif !BSD X X strcpy(newsvc, svc); X *(rindex(newsvc, ',')) = ';'; /* temporary file will be "file;S" */ X X if(NULL == (newfp = fopen(newsvc, "w"))) X { X perror("ci: can't create SVC temporary"); X exit(1); X } X X (void) mktemp(original); X (void) mktemp(diffout); X X if(NULL != (svcfp = fopen(svc, "r"))) /* does svc-file exist? */ X { X fgets(line, LINELEN, svcfp); X if(1 != sscanf(line, "# %d", &rev)) X { X fprintf(stderr, "ci: %s: illegal SVC file header\n", svc); X exit(1); X } X ++rev; X X if(!lockcheck(svcfp, rev)) X { X fprintf(stderr, "Revision %d not locked\n", rev); X clean(); X exit(1); X } X X if(NULL == (origfp = fopen(original, "w"))) X { X fprintf(stderr, "ci: can't create %s", original); X perror(""); X } X fgets(line, LINELEN, svcfp); /* skip "cat <<***MAIN-eof***" line */ X X while(NULL != fgets(line, LINELEN, svcfp) X && strcmp(line, "***MAIN-eof***\n")) X { X fputs(line, origfp); X if(ferror(origfp)) X { X perror("ci: origfile"); X exit(1); X } X } X fclose(origfp); X X rundiff(); X X if(0 != stat(original, &stb1) || 0 != stat(diffout, &stb2)) X { X perror("ci: can't stat original or diffout"); X clean(); X exit(1); X } X } X else X { /* no - create one */ X rev = 1; X } X X fprintf(newfp, "# %d\n", rev); X fprintf(newfp, "cat <<***MAIN-eof*** >$1\n"); X if(NULL == (srcfp = fopen(file, "r"))) X { X perror("ci: can't read source file"); X clean(); X exit(1); X } X while(NULL != fgets(line, LINELEN, srcfp)) X fputs(line, newfp); X fclose(srcfp); X fputs("***MAIN-eof***\n", newfp); X X if(rev > 1) X { X fprintf(newfp, "if test $2 -ge %d ; then rm -f Fix.$1 ; exit 0 ; fi ; cat <<***%d-eof*** >Fix.$1\n", rev, rev); X p = (stb1.st_size <= stb2.st_size) ? original : diffout; X if(NULL == (origfp = fopen(p, "r"))) X { X perror("can't open diff output file"); X clean(); X exit(1); X } X while(NULL != fgets(line, LINELEN, origfp)) X fputs(line, newfp); X fclose(origfp); X fprintf(newfp, "***%d-eof***\n", rev); X fputs((original == p) ? "mv Fix.$1 $1\n" : FIX, newfp); X logmsg(newfp); X while(NULL != fgets(line, LINELEN, svcfp) && strncmp(line, "#***SVCLOCK***", 14)) X fputs(line, newfp); X } X else X { X logmsg(newfp); X fputs("rm -f Fix.$1\n", newfp); X } X X if(relock) X {fprintf(stderr, "(relocking into revision %d)\n", rev+1); X fprintf(newfp, "#***SVCLOCK*** %s %d\n", whoami(), rev+1); X } X X signal(SIGHUP, SIG_IGN); /* disable during critical section */ X signal(SIGINT, SIG_IGN); X X if(ferror(newfp) || fclose(newfp) || ((rev > 1) && unlink(svc)) X || link(newsvc, svc)) X { X fprintf(stderr, "SVC file write/link error - Checkin aborted\n"); X clean(); X exit(1); X } X else X fprintf(stderr, "Checkin complete.\n"); X X if(stat(svc, &stb1) < 0 || chmod(svc, stb1.st_mode & 0555) < 0) X perror("ci: can't chmod SVC file"); X X if(unlocked) X {if(stat(file, &stb1) < 0 || chmod(file, stb1.st_mode & 0555) < 0) X perror("ci: can't chmod source file"); X } X else if(relock) X {if(stat(file, &stb1) < 0 || chmod(file, stb1.st_mode | 0200) < 0) X perror("ci: can't chmod source file"); X } X else X unlink(file); X X clean(); X exit(0); X} X Xrundiff() X{ /* do "diff file original > diffout" */ X int fd; /* redirected output file */ X X switch(fork()) X { X case -1:perror("ci: fork"); /* error */ X clean(); X exit(1); X X case 0: /* child */ X if((fd = creat(diffout, 0600)) < 0 || -1 == dup2(fd, 1)) X { X perror("ci: diffout"); X clean(); X exit(1); X } X close(fd); X execl("/usr/bin/diff", "diff", file, original, 0); X perror("ci: exec diff failed"); X exit(1); X X default:break; /* parent */ X } X wait(&status); X if(0 != status && 1<<8 != status) X { X fprintf(stderr, "ci: bad return status (0x%x) from diff\n", status); X clean(); X exit(1); X } X} X Xlogmsg(fp) XFILE *fp; X{ X long now; X X time(&now); X fprintf(stderr, "Enter log message for revision %d (end with ^D or '.'):\n", rev); X fprintf(fp, "#***SVC*** revision %d %s %s", rev, file, ctime(&now)); X while(NULL != gets(line) && strcmp(line, ".")) X fprintf(fp, "#***SVC*** %s\n", line); X} X Xfname(src, dst) Xchar *src, *dst; X{ X char *p; X strcpy(dst, src); X p = &dst[strlen(src) - strlen(SUFFIX)]; X if(!strcmp(p, SUFFIX)) X *p = '\0'; X} X Xsvcname(src, dst) Xchar *src, *dst; X{ X extern char *rindex(); X char *p; X X strcpy(dst, src); X strcat(dst, SUFFIX); X X if(0 != access(dst, 4)) X { X char dirname[PATHLEN]; X if(NULL != (p = rindex(src, '/'))) X strncpy(dirname, src, p - src + 1); X else X dirname[0] = '\0'; X strcat(dirname, SVCDIR); X X if(0 == access(dirname, 1)) X { X strcpy(dst, dirname); X if(NULL == p) X {strcat(dst, "/"); X strcat(dst, src); X } X else X strcat(dst, p); X strcat(dst, SUFFIX); X } X } X} X Xlockcheck(fp, rev) XFILE *fp; Xint rev; X{ X char lock[40], check[40]; X long pos; X int ret; X X sprintf(lock, "#***SVCLOCK*** %s %d\n", whoami(), rev); X X pos = ftell(fp); X fseek(fp, -((long)strlen(lock)), 2); X fgets(check, 40, fp); X ret = (0 == strcmp(lock, check)); X fseek(fp, pos, 0); X X return ret; X} X Xonintr() X{ X fprintf(stderr, "Interrupt - Aborting checkin, cleaning up\n"); X clean(); X exit(1); X} X Xclean() X{ X if(strlen(original)) /* if only more programs made this check! */ X unlink(original); X if(strlen(diffout)) X unlink(diffout); X if(strlen(newsvc)) X unlink(newsvc); X} X Xchar *whoami() X{ X struct passwd *pw; X X if(NULL != (pw = getpwuid(getuid()))) X return pw->pw_name; X else X return "nobody"; X} **-ci.c-EOF-** echo 'x - ci.sh' sed 's/^X//' <<'**-ci.sh-EOF-**' >ci.sh X#!/bin/sh X# SVC - the Shell Version Control system X# author: Peter S. Housel 10/24/87 X# Xremove="rm -f" Xlock="false" X# Xwhile :; do X case $1 in X-u ) remove="chmod a-w" X shift;; X-l ) remove="chmod u+w" X lock=":" X shift;; X* ) break X esac Xdone X# Xfile=`basename $1 ,S` Xsvc=$file,S Xif test \( ! -r $svc \) -a -d "SVC" ; then svc=SVC/$svc ; fi Xecho '***MAIN-eof***' >/tmp/cir$$ Xif test -r $svc; then X rev=`sed -n '1s/#.* //p' $svc`; rev=`expr $rev + 1` X if test "#***SVCLOCK*** $USER $rev" != "`tail -1 $svc`" ; then X echo "Revision $rev not locked by $USER" X exit 1 X fi X sed -e '3,/^\*\*\*MAIN-eof\*\*\*/!d' \ X -e '/^\*\*\*MAIN-eof\*\*\*/d' $svc >/tmp/cio$$ X sed -e '/^\*\*\*MAIN-eof\*\*\*/,$!d' \ X -e '/^#\*\*\*SVCLOCK\*\*\*/d' \ X -e '/^\*\*\*MAIN-eof\*\*\*/d' $svc >/tmp/cid$$ X echo 'if test $2 -ge $rev ; then rm -f /tmp/$$ ; exit 0 ; fi ; cat <<***$rev-eof*** >/tmp/$$' >>/tmp/cir$$ X diff $file /tmp/cio$$ >>/tmp/cir$$ X echo "***$rev-eof***" >>/tmp/cir$$ X echo 'fix $1 /tmp/$$ > New.$1; mv New.$1 $1' >>/tmp/cir$$ Xelse X rev=1 X echo 'rm -f /tmp/$$' > /tmp/cid$$ Xfi Xecho "Enter log message for revision $rev (end with ^D or '.'):" Xecho "#***SVC*** revision $rev $file "`date` $USER >>/tmp/cir$$ Xwhile read logline; do X if test x"$logline" = x. ; then break; fi X echo "#***SVC*** $logline" >>/tmp/cir$$ Xdone Xecho "# " $rev >$svc+ Xecho 'cat <<***MAIN-eof*** >$1' >> $svc+ Xif cat $file /tmp/cir$$ /tmp/cid$$ >> $svc+ ; then X rm -f /tmp/ci?$$ X echo "Checkin complete." X mv $svc+ $svc X if $lock; then X rev=`expr $rev + 1` X echo "#***SVCLOCK*** $USER $rev" >>$svc X echo "(Locking into revision $rev.)" X fi X chmod a-w $svc X $remove $file Xelse X echo "Checkout ABORTED!!!" X exit 1 Xfi **-ci.sh-EOF-** echo 'x - co.c' sed 's/^X//' <<'**-co.c-EOF-**' >co.c X/* file: co.c X** author: Peter S. Housel 12/24/87 X*/ X X#include <stdio.h> X#include <string.h> X#include <sys/stat.h> X#include <pwd.h> X X#define SUFFIX ",S" /* svc indicator */ X#define SVCDIR "SVC" /* svc postfix indicator */ X X#define LINELEN 256 /* maximum line length */ X X#ifdef MAXPATHLEN X#define PATHLEN MAXPATHLEN X#else X#define PATHLEN 128 /* buffer length for filenames */ X#endif X Xchar file[PATHLEN]; /* file to be checked in */ Xchar svc[PATHLEN]; /* filename for svc file */ Xchar newsvc[PATHLEN]; /* new copy of SVC file */ Xchar line[LINELEN]; /* temporary line buffer */ Xchar *p; /* scratch character pointer */ X XFILE *svcfp; /* svc file */ Xint rev; /* old revision number */ Xint lastrev, lockrev; /* latest file revision, lock into */ Xint status; /* wait() buffer */ Xint lock; /* lock the SVC file */ Xstruct stat stb; /* stat() buffer */ Xchar *base; /* basename of file */ X Xchar difftemp[PATHLEN]; /* extract() fix/patch input */ X Xextern FILE *fopen(); Xextern char *mktemp(), *fgets(), *rindex(), *index(); Xextern struct passwd *getpwuid(); X Xchar *whoami(), *basename(); X Xmain(argc, argv) Xint argc; char **argv; X{ X#ifdef perprintf X char errbuf[BUFSIZ]; X setbuf(stderr, errbuf); X perprintf(stderr); X#endif X X while(++argv, --argc) X { X if('-' == (*argv)[0]) X { X if('r' == (*argv)[1]) X {--argc; X rev = atoi(*++argv); X if(rev < 1) X {fprintf(stderr, "Illegal revision number\n"); X exit(1); X } X } X else if('l' == (*argv)[1]) X ++lock; X else X {fprintf(stderr, "co: illegal option -%c\n", (*argv)[1]); X exit(1); X } X } X else X break; X } X X if(1 != argc) X { X fprintf(stderr, "co: bad number of files arguments\n"); X exit(1); X } X X fname(*argv, file); X svcname(file, svc); X X fprintf(stderr, "%s -> %s\n", svc, base = basename(file)); X X if(NULL == (svcfp = fopen(svc, "r"))) X { X perror("co: can't read SVC file"); X exit(1); X } X X if(1 != fscanf(svcfp, "# %d", &lastrev) || lastrev < 1) X { X fprintf(stderr, "co: illegal SVC file format\n"); X exit(1); X } X X fclose(svcfp); X X if(stat(base, &stb) >= 0 && (stb.st_mode & 0222)) X { X fprintf(stderr, "Writable %s exists - overwrite (n/y)? ", base); X if(!getyn()) X { X fprintf(stderr, "Checkout aborted\n"); X exit(1); X } X } X X if(strlen(base)) X unlink(base); X X if(0 == rev) X rev = lastrev; X X fprintf(stderr, "Checking out revision %d", rev); X X extract(svc, base, rev); X X if(lock) X { X lockrev = lastrev + 1; X fprintf(stderr, "; Locking into revision %d\n", lockrev); X if(stat(svc, &stb) < 0 || chmod(svc, stb.st_mode | 0200) < 0) X perror("co: can't chmod SVC file"); X X if(stat(base, &stb) < 0 || chmod(base, stb.st_mode | 0200) < 0) X perror("co: can't chmod source file"); X X if(NULL == (svcfp = fopen(svc, "a")) X || (fprintf(svcfp, "#***SVCLOCK*** %s %d\n", whoami(), lockrev), ferror(svcfp))) X { X fprintf(stderr, "co: can't lock %s\n", svc); X exit(1); X } X if(stat(svc, &stb) < 0 || chmod(svc, stb.st_mode & 0555)) X perror("co: can't chmod SVC file"); X } X else X { X putchar('\n'); X if(stat(base, &stb) < 0 || chmod(base, stb.st_mode & 0555)) X perror("co: can't chmod source file"); X } X X exit(0); X} X X Xfname(src, dst) Xchar *src, *dst; X{ X char *p; X strcpy(dst, src); X p = &dst[strlen(src) - strlen(SUFFIX)]; X if(!strcmp(p, SUFFIX)) X *p = '\0'; X} X Xsvcname(src, dst) Xchar *src, *dst; X{ X extern char *rindex(); X char *p; X X strcpy(dst, src); X strcat(dst, SUFFIX); X X if(0 != access(dst, 4)) X { X char dirname[PATHLEN]; X if(NULL != (p = rindex(src, '/'))) X strncpy(dirname, src, p - src + 1); X else X dirname[0] = '\0'; X strcat(dirname, SVCDIR); X X if(0 == access(dirname, 1)) X { X strcpy(dst, dirname); X if(NULL == p) X {strcat(dst, "/"); X strcat(dst, src); X } X else X strcat(dst, p); X strcat(dst, SUFFIX); X } X } X} X Xextract(script, out, rev) Xchar *script, *out; int rev; X{ X FILE *outfp; X int testrev; X char buf[80]; X X sprintf(difftemp, "Fix.%s", out); X X svcfp = fopen(script, "r"); X fgets(line, LINELEN, svcfp); /* skip '# rev' line */ X fgets(line, LINELEN, svcfp); /* skip 'cat <***MAIN-eof***' line */ X X if(NULL == (outfp = fopen(out, "w"))) X { X perror("co: can't create output file"); X return; X } X X while(NULL != fgets(line, LINELEN, svcfp) && strcmp(line, "***MAIN-eof***\n")) X fputs(line, outfp); X X fclose(outfp); X X while(NULL != fgets(line, LINELEN, svcfp)) X { X if(!strncmp(line, "if ", 3)) X { X sscanf(line, "if test $2 -ge %d", &testrev); X if(rev >= testrev) X { X unlink(difftemp); X return; X } X if(NULL == (outfp = fopen(difftemp, "w"))) X { X perror("co: can't create output file"); X return; X } X sprintf(buf, "***%d-eof***\n", testrev); X while(NULL != fgets(line, LINELEN, svcfp) && strcmp(line, buf)) X fputs(line, outfp); X fclose(outfp); X } X else if(!strncmp(line, "mv ", 3)) X { X sprintf(buf, "mv Fix.%s %s", out, out); X system(buf); X } X else if(!strncmp(line, "fix ", 4)) X { X sprintf(buf, "fix %s Fix.%s > New.%s; mv New.%s %s", out, out, out, out, out); X system(buf); X } X else if(!strncmp(line, "patch ", 6)) X { X sprintf(buf, "patch -n -s %s < Fix.%s; rm -f %s.orig", out, out, out); X system(buf); X } X else X { /* ignore */ X } X } X X unlink(difftemp); X return; X} X Xchar *basename(name) Xchar *name; X{ X char *p; X X if(NULL == (p = rindex(name, '/'))) X return name; X else X return p + 1; X} X Xchar *whoami() X{ X struct passwd *pw; X X if(NULL != (pw = getpwuid(getuid()))) X return pw->pw_name; X else X return "nobody"; X} X Xint getyn() X{ X char ans[10]; X X return (NULL != fgets(ans, 10, stdin)) && ('y' == ans[0] || 'Y' == ans[0]); X} **-co.c-EOF-** echo 'x - co.sh' sed 's/^X//' <<'**-co.sh-EOF-**' >co.sh X#!/bin/sh X# SVC - the Shell Version Control system X# author: Peter S. Housel 10/24/87 X# Xlock="false" X# Xwhile :; do X case $1 in X-l ) lock=":" X shift;; X-r ) rev=$2; X shift; X shift;; X* ) break X esac Xdone X# Xfile=`basename $1 ,S` Xsvc=$file,S Xif test \( ! -r $svc \) -a -d "SVC" ; then svc=SVC/$svc ; fi Xif test ! -r $svc ; then X echo "can't find $file,S or RCS/$file,S" X exit 1 Xfi Xlastrev=`sed -n '1s/#.* //p' $svc` Xif test -w $file; then X echo -n "Writable $file exists; Continue (n/y)? " ; read yn X if test x$yn != xy ; then echo "Checkout aborted" ; exit 1 ; fi Xelif test -f $file; then X chmod u+w $file Xfi Xecho -n "Checking out revision " ${rev=$lastrev} Xsh $svc $file $rev Xif $lock; then X lockrev=`expr $lastrev + 1` X echo "; Locking into revision $lockrev" X chmod u+w $file X chmod u+w $svc; echo "#***SVCLOCK*** $USER $lockrev" >>$svc; chmod u-w $svc Xelse X echo X chmod a-w $file Xfi Xexit 0 X **-co.sh-EOF-** echo 'x - svclog.sh' sed 's/^X//' <<'**-svclog.sh-EOF-**' >svclog.sh X#!/bin/sh X# Xsvc=`basename $1 ,S`,S Xif test \( ! -r $svc \) -a -d "SVC" ; then svc=SVC/$svc ; fi Xgrep '^#\*\*\*SVC' $svc X **-svclog.sh-EOF-**