http://www.berklix.com/~jhs/src/bsd/fixes/FreeBSD/src/gen/usr.sbin/ctm/ctm/patch-for-ctm-v09.REL=12.0-RELEASE.diff

13.0-RELEASE-p8 has no /usr/src/usr.sbin/ctm/ it moved to ports/misc/ctm/
I have Not moved this patch to my ~/public_html/src/bsd/fixes/FreeBSD/ports/gen/misc/ctm

See ports/ Equivalent
../../../../../ports/gen/misc/ctm/files/patch-for-ctm-v09
http://www.berklix.com/~jhs/src/bsd/fixes/FreeBSD/ports/gen/misc/ctm/files/patch-for-ctm-v09

From Stephen Montgomery-Smith's ftp://ctm.berklix.org/pub/FreeBSD/development/CTM/svn-cur/patch-for-ctm-v09
Just pathnames changed.

diff -ur /usr/src/usr.sbin/ctm/ctm/ctm.1 ctm/ctm.1
--- old/src/usr.sbin/ctm/ctm/ctm.1	2013-01-02 14:12:15.000000000 -0600
+++ new/src/usr.sbin/ctm/ctm/ctm.1	2014-11-15 14:47:20.000000000 -0600
@@ -52,8 +52,11 @@
 You can pass a CTM delta on stdin, or you can give the
 filename as an argument.
 If you do the latter, you make life a lot
-easier for your self, since the program can accept gzip'ed files and
+easier for your self, since the program can accept gzip'ed,
+bzip2'ed, or xz'ed files and
 since it will not have to make a temporary copy of your file.
+(If you pass it an xz'ed file, and xz is not part of your base system,
+you will have to install xz from the ports.)
 You can
 specify multiple deltas at one time, they will be processed one at a
 time.
@@ -272,6 +275,15 @@
 Changing
 or removing this file will greatly confuse
 .Nm .
+.sp
+.Pa .svn_revision
+contains the revision number emitted by SVN 
+.\" eg from approx
+.\"	svn up  src | tail -1 | \
+.\"     sed -E 's/[^[:digit:]]//g' > src/.svn_revision
+(to reference when discussing sources with users of 
+.Nm svn
+direct).
 .Pp
 Using the
 .Fl e
diff -ur /usr/src/usr.sbin/ctm/ctm/ctm.c ctm/ctm.c
--- old/src/usr.sbin/ctm/ctm/ctm.c	2013-01-02 14:12:15.000000000 -0600
+++ new/src/usr.sbin/ctm/ctm/ctm.c	2014-11-15 14:42:56.000000000 -0600
@@ -211,6 +211,22 @@
 	strcat(p,filename);
 	f = popen(p,"r");
 	if(!f) { warn("%s", p); return Exit_Garbage; }
+    } else if(p && !strcmp(p,".bz2")) {
+	p = alloca(20 + strlen(filename));
+	strcpy(p,"bzcat < ");
+	strcat(p,filename);
+	f = popen(p,"r");
+	if(!f) { warn("%s", p); return Exit_Garbage; }
+    } else if(p && !strcmp(p,".xz")) {
+	if (system("which -s xz") != 0) {
+	    fprintf(stderr, "xz is not found in $PATH.  You can install it from ports, or adjust $PATH.\n");
+	    return Exit_Garbage;
+	}
+	p = alloca(20 + strlen(filename));
+	strcpy(p,"xz -dc < ");
+	strcat(p,filename);
+	f = popen(p,"r");
+	if(!f) { warn("%s", p); return Exit_Garbage; }
     } else {
 	p = 0;
 	f = fopen(filename,"r");
diff -ur /usr/src/usr.sbin/ctm/ctm/ctm.h ctm/ctm.h
--- old/src/usr.sbin/ctm/ctm/ctm.h	2013-01-02 14:12:15.000000000 -0600
+++ new/src/usr.sbin/ctm/ctm/ctm.h	2014-11-15 14:42:56.000000000 -0600
@@ -24,6 +24,7 @@
 #include <sys/stat.h>
 #include <sys/file.h>
 #include <sys/time.h>
+#include <stdint.h>
 
 #define VERSION "2.0"
 
@@ -40,6 +41,8 @@
 #define CTM_F_MD5		0x05
 #define CTM_F_Count		0x06
 #define CTM_F_Bytes		0x07
+#define CTM_F_Release		0x08
+#define CTM_F_Forward		0x09
 
 /* The qualifiers... */
 #define CTM_Q_MASK		0xff00
@@ -47,10 +50,13 @@
 #define CTM_Q_Name_Dir		0x0200
 #define CTM_Q_Name_New		0x0400
 #define CTM_Q_Name_Subst	0x0800
+#define CTM_Q_Name_Svnbase	0x1000
 #define CTM_Q_MD5_After		0x0100
 #define CTM_Q_MD5_Before	0x0200
 #define CTM_Q_MD5_Chunk		0x0400
 #define CTM_Q_MD5_Force		0x0800
+#define CTM_Q_Forward_Tar	0x0100
+#define CTM_Q_Forward_SVN	0x0200
 
 struct CTM_Syntax {
     char	*Key;		/* CTM key for operation */
@@ -145,14 +151,16 @@
 u_char * Ffield(FILE *fd, MD5_CTX *ctx,u_char term);
 u_char * Fname(FILE *fd, MD5_CTX *ctx,u_char term,int qual, int verbose);
 
-int Fbytecnt(FILE *fd, MD5_CTX *ctx, u_char term);
+intmax_t Fbytecnt(FILE *fd, MD5_CTX *ctx, u_char term);
 
 u_char * Fdata(FILE *fd, int u_chars, MD5_CTX *ctx);
+int Fforward(FILE *fd, intmax_t u_chars, MD5_CTX *ctx, FILE *fd_to);
 
 #define GETFIELD(p,q) if(!((p)=Ffield(fd,&ctx,(q)))) return BADREAD
 #define GETFIELDCOPY(p,q) if(!((p)=Ffield(fd,&ctx,(q)))) return BADREAD; else p=String(p)
 #define GETBYTECNT(p,q) if(0 >((p)= Fbytecnt(fd,&ctx,(q)))) return BADREAD
 #define GETDATA(p,q) if(!((p) = Fdata(fd,(q),&ctx))) return BADREAD
+#define GETFORWARD(p,q) if(!Fforward(fd,(p),&ctx,q)) return BADREAD
 #define GETNAMECOPY(p,q,r,v) if(!((p)=Fname(fd,&ctx,(q),(r),(v)))) return BADREAD; else p=String(p)
 
 int Pass1(FILE *fd, unsigned applied);
diff -ur /usr/src/usr.sbin/ctm/ctm/ctm_input.c ctm/ctm_input.c
--- old/src/usr.sbin/ctm/ctm/ctm_input.c	2013-01-02 14:12:15.000000000 -0600
+++ new/src/usr.sbin/ctm/ctm/ctm_input.c	2014-11-15 14:42:56.000000000 -0600
@@ -61,11 +61,11 @@
     return buf;
 }
 
-int
+intmax_t
 Fbytecnt(FILE *fd, MD5_CTX *ctx, u_char term)
 {
     u_char *p,*q;
-    int u_chars=0;
+    intmax_t u_chars=0;
 
     p = Ffield(fd,ctx,term);
     if(!p) return -1;
@@ -100,6 +100,42 @@
     return p;
 }
 
+int Fforward(FILE *fd, intmax_t u_chars, MD5_CTX *ctx, FILE *fd_to)
+{
+    u_char buf[BUFSIZ];
+    intmax_t amount_read = 0;
+    int amount_to_read;
+
+    while (amount_read < u_chars) {
+	if (u_chars - amount_read >= BUFSIZ)
+	    amount_to_read = BUFSIZ;
+	else
+	    amount_to_read = u_chars - amount_read;
+	if(amount_to_read != fread(buf, 1, amount_to_read, fd)) {
+	    Fatal("Truncated patch.");
+	    return 0;
+	}
+	MD5Update(ctx,buf,amount_to_read);
+	if (fd_to != NULL) {
+	    if (amount_to_read != fwrite(buf, 1, amount_to_read, fd_to)) {
+		Fatal("Write error.");
+		return 0;
+	    }
+	}
+	amount_read += amount_to_read;
+    }
+
+    if(getc(fd) != '\n') {
+	if(Verbose > 3)
+	    printf("FileData wasn't followed by a newline.\n");
+        Fatal("Corrupt patch.");
+	return 0;
+    }
+    MD5Update(ctx,"\n",1);
+    return 1;
+}
+
+
 /*---------------------------------------------------------------------------*/
 /* get the filename in the next field, prepend BaseDir and give back the result
    strings. The sustitute filename is return (the one with the suffix SUBSUFF) 
diff -ur /usr/src/usr.sbin/ctm/ctm/ctm_pass1.c ctm/ctm_pass1.c
--- old/src/usr.sbin/ctm/ctm/ctm_pass1.c	2013-01-02 14:12:15.000000000 -0600
+++ new/src/usr.sbin/ctm/ctm/ctm_pass1.c	2014-11-15 14:42:56.000000000 -0600
@@ -22,7 +22,8 @@
 {
     u_char *p,*q;
     MD5_CTX ctx;
-    int i,j,sep,cnt;
+    int i,j,sep;
+    intmax_t cnt, rel;
     u_char *md5=0,*name=0,*trash=0;
     struct CTM_Syntax *sp;
     int slashwarn=0, match=0, total_matches=0;
@@ -96,7 +97,7 @@
 	if(Verbose > 5)
 	    fprintf(stderr,"%s ",sp->Key);
 	for(i=0;(j = sp->List[i]);i++) {
-	    if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
+	    if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Forward)
 		sep = ' ';
 	    else
 		sep = '\n';
@@ -209,6 +210,17 @@
 		    if(md5 && strcmp(md5,p)) {
 			Fatal("Internal MD5 failed.");
 			return Exit_Garbage;
+		case CTM_F_Release:
+		    GETBYTECNT(rel,sep);
+		    break;
+		case CTM_F_Forward:
+		    if(cnt < 0) WRONG
+		    if ((j & CTM_Q_MASK) == CTM_Q_Forward_SVN && system("which -s svnadmin") != 0) {
+			fprintf(stderr, "svn is not found in $PATH.  You can install it from ports/devel/subversion, or adjust $PATH.\n");
+			return Exit_Garbage;
+		    }
+		    GETFORWARD(cnt,NULL);
+		    break;
 		default:
 			fprintf(stderr,"List = 0x%x\n",j);
 			Fatal("List had garbage.");
diff -ur /usr/src/usr.sbin/ctm/ctm/ctm_pass2.c ctm/ctm_pass2.c
--- old/src/usr.sbin/ctm/ctm/ctm_pass2.c	2013-01-02 14:12:15.000000000 -0600
+++ new/src/usr.sbin/ctm/ctm/ctm_pass2.c	2014-11-15 14:42:56.000000000 -0600
@@ -22,7 +22,11 @@
 {
     u_char *p,*q,*md5=0;
     MD5_CTX ctx;
-    int i,j,sep,cnt,fdesc;
+    int i,j,sep,fdesc;
+    intmax_t cnt, rel;
+    int rel2;
+    FILE *current;
+    char *current_file_name = NULL;
     u_char *trash=0,*name=0;
     struct CTM_Syntax *sp;
     struct stat st;
@@ -30,7 +34,7 @@
     int match = 0;
     char md5_1[33];
     struct CTM_Filter *filter;
-    FILE *ed = NULL;
+    FILE *ed = NULL, *fd_to = NULL;
     static char *template = NULL;
 
     if(Verbose>3)
@@ -72,7 +76,7 @@
 	WRONG
     found:
 	for(i=0;(j = sp->List[i]);i++) {
-	    if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
+	    if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Forward)
 		sep = ' ';
 	    else
 		sep = '\n';
@@ -128,6 +132,22 @@
 				sp->Key,name);
 			    ret |= Exit_NotOK;
 			}
+			if (j & CTM_Q_Name_Svnbase) {
+			    current_file_name = alloca(strlen(name)+128);
+			    strcpy(current_file_name,name);
+			    strcat(current_file_name,"/db/current");
+	 		    current = fopen(current_file_name,"r");
+			    if (current==NULL) {
+				fprintf(stderr,"Cannot open %s\n",current_file_name);
+				WRONG
+			    }
+			    if (fscanf(current,"%d",&rel2) != 1) {
+				fprintf(stderr,"Cannot find release number in %s\n",current_file_name);
+				fclose(current);
+				WRONG
+			    }
+			    fclose(current);
+			}
 			break;
 		    }
 		    if (j & CTM_Q_Name_File) {
@@ -284,6 +304,42 @@
 		    }
 
 		    break;
+		case CTM_F_Release:
+		    GETBYTECNT(rel,sep);
+		    if(Verbose > 3)
+			printf("Expecting release number %jd\n",rel);
+		    if(Verbose > 3)
+			printf("Actual release number %d\n",rel2);
+		    if (rel != rel2) {
+			fprintf(stderr,"Release number mismatch: found %d, need %jd\n",rel2,rel);
+			WRONG
+		    }
+		    break;
+		case CTM_F_Forward:
+		    if ((j & CTM_Q_MASK) == CTM_Q_Forward_SVN) {
+			if(Verbose>3)
+			    printf("This is a svn dump file and there is no certainty that it will apply cleanly.\n");
+			GETFORWARD(cnt,NULL);
+		    }
+		    else if ((j & CTM_Q_MASK) == CTM_Q_Forward_Tar) {
+			if(Verbose>3) {
+			    printf("This is a tar file and there is no certainty that it will apply cleanly even if it passes the following test.\n");
+			    fd_to = popen("tar tvf -","w");
+			} else
+			    fd_to = popen("tar tf - >/dev/null 2>&1","w");
+			if (fd_to == NULL) {
+			    fprintf(stderr,"Cannot forward\n");
+			    WRONG
+			}
+			GETFORWARD(cnt,fd_to);
+			if (pclose(fd_to)) {
+			    fprintf(stderr,"Tar failed to close properly\n");
+			    WRONG
+			} else
+			    if (Verbose > 3)
+				printf("Tar file test was good\n");
+		    }
+		    break;
 		default: WRONG
 	    }
         }
diff -ur /usr/src/usr.sbin/ctm/ctm/ctm_pass3.c ctm/ctm_pass3.c
--- old/src/usr.sbin/ctm/ctm/ctm_pass3.c	2013-01-02 14:12:15.000000000 -0600
+++ new/src/usr.sbin/ctm/ctm/ctm_pass3.c	2014-11-15 14:42:56.000000000 -0600
@@ -33,10 +33,12 @@
 {
     u_char *p,*q,buf[BUFSIZ];
     MD5_CTX ctx;
-    int i,j,sep,cnt;
+    int i,j,sep;
+    intmax_t cnt,rel;
+    char *svn_command = NULL;
     u_char *md5=0,*md5before=0,*trash=0,*name=0,*uid=0,*gid=0,*mode=0;
     struct CTM_Syntax *sp;
-    FILE *ed=0;
+    FILE *ed=0, *fd_to;
     struct stat st;
     char md5_1[33];
     int match=0;
@@ -129,7 +131,7 @@
 	WRONG
     found:
 	for(i=0;(j = sp->List[i]);i++) {
-	    if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
+	    if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Forward)
 		sep = ' ';
 	    else
 		sep = '\n';
@@ -147,53 +149,98 @@
 		    break;
 		case CTM_F_Count: GETBYTECNT(cnt,sep); break;
 		case CTM_F_Bytes: GETDATA(trash,cnt); break;
+		case CTM_F_Release: GETBYTECNT(rel,sep); break;
+		case CTM_F_Forward:
+		    if ((j & CTM_Q_MASK) == CTM_Q_Forward_Tar) {
+			if (Verbose > 0)
+			    fd_to = popen("tar xvf -","w");
+			else
+			    fd_to = popen("tar xvf - >/dev/null 2>&1","w");
+		    } else if ((j & CTM_Q_MASK) == CTM_Q_Forward_SVN) {
+			svn_command = alloca(strlen(name)+128);
+			if (Verbose > 0)
+			    snprintf(svn_command,strlen(name)+127,"svnadmin load %s\n", name);
+			else
+			    snprintf(svn_command,strlen(name)+127,"svnadmin load %s > /dev/null 2>&1\n", name);
+			fd_to = popen(svn_command,"w");
+		    } else WRONG
+		    if (fd_to == NULL) {
+			fprintf(stderr,"Cannot forward\n");
+			WRONG
+		    }
+	    	    if (Verbose > 0) {
+			if (!strcmp(sp->Key,"TR"))
+			    fprintf(stderr,"> %s\n",sp->Key);
+			else
+	 		    fprintf(stderr,"> %s %s\n",sp->Key,name);
+		    }
+		    GETFORWARD(cnt,fd_to);
+		    if (pclose(fd_to)) {
+			if ((j & CTM_Q_MASK) == CTM_Q_Forward_Tar)
+			    fprintf(stderr,"Tar failed to close properly\n");
+			else if ((j & CTM_Q_MASK) == CTM_Q_Forward_SVN)
+			    fprintf(stderr,"Svnadmin failed to close properly\n");
+			WRONG
+		    }
+		    if ((j & CTM_Q_MASK) == CTM_Q_Forward_SVN) {
+			snprintf(svn_command,strlen(name)+127,"svnadmin pack %s\n", name);
+			if (system(svn_command)) {
+			    fprintf(stderr,"\"%s\" didn't work.", svn_command);
+			    WRONG
+			}
+		    }
+		    break;
 		default: WRONG
 		}
 	    }
-	/* XXX This should go away.  Disallow trailing '/' */
-	j = strlen(name)-1;
-	if(name[j] == '/') name[j] = '\0';
-
-	/*
-	 * If a filter list is specified, run thru the filter list and
-	 * match `name' against filters.  If the name matches, set the
-	 * required action to that specified in the filter.
-	 * The default action if no filterlist is given is to match
-	 * everything.  
-	 */
-
-	match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE);
-	for (filter = FilterList; filter; filter = filter->Next) {
-	    if (0 == regexec(&filter->CompiledRegex, name,
-		0, 0, 0)) {
-		match = filter->Action;
+
+	if (name) {
+	    /* XXX This should go away.  Disallow trailing '/' */
+	    j = strlen(name)-1;
+	    if(name[j] == '/') name[j] = '\0';
+
+	    /*
+	     * If a filter list is specified, run thru the filter list and
+	     * match `name' against filters.  If the name matches, set the
+	     * required action to that specified in the filter.
+	     * The default action if no filterlist is given is to match
+	     * everything.  
+	     */
+
+	    match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE);
+	    for (filter = FilterList; filter; filter = filter->Next) {
+		if (0 == regexec(&filter->CompiledRegex, name,
+		    0, 0, 0)) {
+		    match = filter->Action;
+		}
 	    }
-	}
 
-	if (CTM_FILTER_DISABLE == match) /* skip file if disabled */
-		continue;
+	    if (CTM_FILTER_DISABLE == match) /* skip file if disabled */
+		    continue;
 
-	if (Verbose > 0)
+	    if (Verbose > 0 && strcmp(sp->Key,"SV") && strcmp(sp->Key,"TR"))
 		fprintf(stderr,"> %s %s\n",sp->Key,name);
-	if(!strcmp(sp->Key,"FM") || !strcmp(sp->Key, "FS")) {
-	    i = open(name,O_WRONLY|O_CREAT|O_TRUNC,0666);
-	    if(i < 0) {
-		warn("%s", name);
-		WRONG
-	    }
-	    if(cnt != write(i,trash,cnt)) {
-		warn("%s", name);
-		WRONG
-	    }
-	    close(i);
-	    if(strcmp(md5,MD5File(name,md5_1))) {
-		fprintf(stderr,"  %s %s MD5 didn't come out right\n",
-		   sp->Key,name);
-		WRONG
+	    if(!strcmp(sp->Key,"FM") || !strcmp(sp->Key, "FS")) {
+		i = open(name,O_WRONLY|O_CREAT|O_TRUNC,0666);
+		if(i < 0) {
+		    warn("%s", name);
+		    WRONG
+		}
+		if(cnt != write(i,trash,cnt)) {
+		    warn("%s", name);
+		    WRONG
+		}
+		close(i);
+		if(strcmp(md5,MD5File(name,md5_1))) {
+		    fprintf(stderr,"  %s %s MD5 didn't come out right\n",
+		       sp->Key,name);
+		    WRONG
+		}
+		if (settime(name,times)) WRONG
+		continue;
 	    }
-	    if (settime(name,times)) WRONG
-	    continue;
 	}
+
 	if(!strcmp(sp->Key,"FE")) {
 	    ed = popen("ed","w");
 	    if(!ed) {
@@ -276,6 +323,8 @@
 	    }
 	    continue;
 	}
+	if(!strcmp(sp->Key,"TR") || !strcmp(sp->Key,"SV"))
+	    continue;
 	WRONG
     }
 
diff -ur /usr/src/usr.sbin/ctm/ctm/ctm_syntax.c ctm/ctm_syntax.c
--- old/src/usr.sbin/ctm/ctm/ctm_syntax.c	2013-01-02 14:12:15.000000000 -0600
+++ new/src/usr.sbin/ctm/ctm/ctm_syntax.c	2014-11-15 14:42:56.000000000 -0600
@@ -20,16 +20,21 @@
 #define MD5	CTM_F_MD5
 #define Count	CTM_F_Count
 #define Bytes	CTM_F_Bytes
+#define Release	CTM_F_Release
+#define Forward	CTM_F_Forward
 
 /* The qualifiers... */
 #define File	CTM_Q_Name_File
 #define Dir	CTM_Q_Name_Dir
+#define Svnbase	CTM_Q_Name_Svnbase
 #define New	CTM_Q_Name_New
 #define Subst	CTM_Q_Name_Subst
 #define After	CTM_Q_MD5_After
 #define Before	CTM_Q_MD5_Before
 #define Chunk	CTM_Q_MD5_Chunk
 #define Force	CTM_Q_MD5_Force
+#define Tar	CTM_Q_Forward_Tar
+#define SVN	CTM_Q_Forward_SVN
 
 static int ctmFM[] = /* File Make */
     { Name|File|New|Subst, Uid, Gid, Mode,
@@ -55,6 +60,12 @@
 static int ctmDR[] = /* Directory Remove */
     { Name|Dir, 0 };
 
+static int ctmTR[] = /* Forward to tar */
+    { Count, Forward|Tar, 0 };
+
+static int ctmSV[] = /* Forward to svnadmin load */
+    { Name|Dir|Svnbase, Release, Count, Forward|SVN, 0 };
+
 struct CTM_Syntax Syntax[] = {
     { "FM",  	ctmFM },
     { "FS",  	ctmFS },
@@ -64,4 +75,6 @@
     { "AS", 	ctmAS },
     { "DM",  	ctmDM },
     { "DR",  	ctmDR },
+    { "TR",  	ctmTR },
+    { "SV",  	ctmSV },
     { 0,    	0} };
