src/hg/encode/encodePatchTdb/encodePatchTdb.c 1.7

1.7 2010/01/05 20:15:44 kent
Fixing stanza merge problem when there is no line before stanza in patch file. Fixing replace eating a stanza. Making replace just comment out old stanza by default, but there's a noComment option. Putting in test suite.
Index: src/hg/encode/encodePatchTdb/encodePatchTdb.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/hg/encode/encodePatchTdb/encodePatchTdb.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -b -B -U 4 -r1.6 -r1.7
--- src/hg/encode/encodePatchTdb/encodePatchTdb.c	5 Jan 2010 05:39:14 -0000	1.6
+++ src/hg/encode/encodePatchTdb/encodePatchTdb.c	5 Jan 2010 20:15:44 -0000	1.7
@@ -13,8 +13,9 @@
 static char const rcsid[] = "$Id$";
 
 char *clMode = "add";
 char *clTest = NULL;
+boolean clNoComment = FALSE;
 
 void usage()
 /* Explain usage and exit. */
 {
@@ -26,18 +27,21 @@
   "   encodePatchTdb 849/out/trackDb.ra ~/kent/src/makeDb/trackDb/human/hg18/trackDb.wgEncode.ra\n"
   "options:\n"
   "   -mode=mode (default %s).  Operate in one of the following modes\n"
   "         replace - replace existing records rather than doing field by field update.\n"
+  "                   Leaves existing record commented out.\n"
   "         add - add new records at end of parent's subtrack list. Complain if record isn't new\n"
   "               warn if it's a new track rather than just new subtracks\n"
+  "   -noComment - If set will not leave old record commented out\n"
   "   -test=patchFile - rather than doing patches in place, write patched output to this file\n"
   , clMode
   );
 }
 
 static struct optionSpec options[] = {
    {"mode", OPTION_STRING},
    {"test", OPTION_STRING},
+   {"noComment", OPTION_BOOLEAN},
    {"root", OPTION_STRING},
    {NULL, 0},
 };
 
@@ -74,8 +78,9 @@
     int startLineIx, endLineIx; /* Start and end in file for error reporting. */
     struct raFile *file;	/* Pointer to file we are in. */
     char *endComments;		/* Some comments that may follow record. */
     struct raRecord *subtracks;	/* Subtracks of this track. */
+    boolean isRemoved;		/* If set, suppresses output. */
     };
 
 struct raFile
 /* A file full of ra's. */
@@ -426,32 +431,13 @@
     else
         break;
     }
 struct raRecord *recordBefore = (viewChild != NULL ? viewChild : view);
+sub->parent = view;
 sub->next = recordBefore->next;
 recordBefore->next = sub;
 }
 
-void substituteIntoView(struct raRecord *sub, struct raRecord *oldSub, struct raRecord *view)
-/* Substitute sub for oldSub as a child of view.  Assumes oldSub is in same file and after view. */
-{
-uglyf("substituteIntoView sub=%s oldSub=%s %p view=%s\n", sub->key, oldSub->key, oldSub, view->key);
-struct raRecord *recordBefore = NULL;
-struct raRecord *r, *prev = NULL;
-for (r = view; r != NULL; r = r->next)
-    {
-    if (r->next == oldSub)
-	{
-        recordBefore = prev;
-	break;
-	}
-    prev = r;
-    }
-assert(recordBefore != NULL);
-sub->next = oldSub->next;
-recordBefore->next = sub;
-}
-
 char *firstTagInText(char *text)
 /* Return the location of tag in text - skipping blank and comment lines and white-space */
 {
 char *s = text;
@@ -490,10 +476,40 @@
     dyStringAppendC(dy, '\n');
 t->text = dyStringCannibalize(&dy);
 }
 
-void indentTdbText(struct raRecord *r, int indentCount)
-/* Add spaces to start of all text in r. */
+boolean hasBlankLine(char *text)
+/* Return TRUE if there is an empty line in text. */
+{
+char *s, *e;
+for (s = text; !isEmpty(s); s = e)
+    {
+    e = strchr(s, '\n');
+    if (e == s)
+        return TRUE;
+    else
+        e += 1;
+    }
+return FALSE;
+}
+
+void makeSureBlankLineBefore(struct raRecord *r)
+/* Make sure there is a blank line before record. */
+{
+struct raTag *first = r->tagList;
+char *firstText = first->text;
+if (!hasBlankLine(firstText))
+    {
+    int len = strlen(firstText);
+    char *newText = needMem(len+2);
+    newText[0] = '\n';
+    strcpy(newText+1, firstText);
+    first->text = newText;
+    }
+}
+
+void addToStartOfTextLines(struct raRecord *r, char c, int charCount)
+/* Add char to start of all text in r. */
 {
 struct raTag *t;
 struct dyString *dy = dyStringNew(0);
 for (t = r->tagList; t != NULL; t = t->next)
@@ -511,10 +527,10 @@
 	    }
 	else
 	    {
 	    // Indent some extra. 
-	    for (i=0; i<indentCount; ++i)
-		dyStringAppendC(dy, ' ');
+	    for (i=0; i<charCount; ++i)
+		dyStringAppendC(dy, c);
 	    if (e == NULL)
 		{
 		dyStringAppend(dy, s);
 		}
@@ -529,8 +545,33 @@
     }
 dyStringFree(&dy);
 }
 
+void indentTdbText(struct raRecord *r, int indentCount)
+/* Add spaces to start of all text in r. */
+{
+addToStartOfTextLines(r, ' ', indentCount);
+}
+
+void commentOutStanza(struct raRecord *r)
+/* Add # to start of all test in r. */
+{
+addToStartOfTextLines(r, '#', 1);
+}
+
+void substituteIntoView(struct raRecord *sub, struct raRecord *oldSub, struct raRecord *view)
+/* Substitute sub for oldSub as a child of view.  Assumes oldSub is in same file and after view. 
+ * Leaves in oldSub, but "commented out" */
+{
+sub->parent = view;
+sub->next = oldSub->next;
+oldSub->next = sub;
+if (clNoComment)
+    oldSub->isRemoved = TRUE;
+else
+    commentOutStanza(oldSub);
+}
+
 void patchInSubtrack(struct raRecord *parent, struct raRecord *sub)
 /* Patch sub into the correct view of parent */
 {
 if (hasViewSubtracks(parent))
@@ -545,8 +586,9 @@
     char *release = nonNullRelease(parentRelease, subRelease);
     struct raRecord *view = findRecordCompatibleWithRelease(parent->file, release, viewTrackName);
     validateParentViewSub(parent, view, sub);
     substituteParentText(parent, view, sub);
+    makeSureBlankLineBefore(sub);
     indentTdbText(sub, 4);
     struct raRecord *oldSub = findRecordCompatibleWithRelease(parent->file, release, sub->key);
     if (glReplace)
         {
@@ -607,9 +649,9 @@
 
 /* Load file to patch. */
 struct raFile *tdbFile = raFileRead(tdbFileName);
 int oldTdbCount = slCount(tdbFile->recordList);
-if (oldTdbCount < 100)
+if (oldTdbCount < 50)
     warn("%s only has %d records, I hope you meant to hit a new file\n", tdbFileName, 
     	oldTdbCount);
 linkUpParents(tdbFile);
 
@@ -625,14 +667,19 @@
 FILE *f = mustOpen(outName, "w");
 struct raRecord *r;
 for (r = tdbFile->recordList; r != NULL; r = r->next)
     {
+    if (!r->isRemoved)
+	{
     struct raTag *tag;
     for (tag = r->tagList; tag != NULL; tag = tag->next)
+	    {
         fputs(tag->text, f);
+	    }
     if (r->endComments != NULL)
 	fputs(r->endComments, f);
     }
+    }
 fputs(tdbFile->endSpace, f);
 carefulClose(&f);
 }
 
@@ -644,8 +691,9 @@
 if (argc != 3)
     usage();
 clMode = optionVal("mode", clMode);
 clTest = optionVal("test", clTest);
+clNoComment = optionExists("noComment");
 if (sameString(clMode, "add"))
     glReplace = FALSE;
 else if (sameString(clMode, "replace"))
     glReplace = TRUE;