c431bff1752a358d5fbfd30dacfb42c567a00a6b
tdreszer
  Mon Aug 13 13:13:39 2012 -0700
rewrote/renamed some dsPrint functions based upon Angie's advice in code review.  Also added support for some print buffer surgery.  However, the regex version is ifdef'd out for now, as I am not sure it is a good idea to check in a requirement for including <regex.h> into src/lib.
diff --git src/lib/dsPrint.c src/lib/dsPrint.c
index fd25fb3..335d371 100644
--- src/lib/dsPrint.c
+++ src/lib/dsPrint.c
@@ -1,330 +1,366 @@
 // dsPrint - Dynamic string Stack based Printing.
 //
 // This module relies upon a dyString based stack of buffers which accumulate output
 // for later printing.  As a stack, only the top buffer can be filled and flushed out
-// (dsPrintFlush).  When flushing, the top buffer's content is appended to the next
-// lower buffer, unless explicitly requested otherwise (dsPrintDirectly).
-// If the stack is empty, dsPrintf will be equivalent to printf.
+// (dspFlush).  When flushing, the top buffer's content is appended to the next
+// lower buffer, unless explicitly requested otherwise (dspDirectly).
+// If the stack is empty, dspPrintf will be equivalent to printf.
+//
+// NOTE: module prefix is 'dsp' not 'dsPrint' so that "print" is always used as a verb.
 //
 // Output goes to STDOUT unless a single alternative out file is registered.
 
 #include "common.h"
 #include "dsPrint.h"
 
 // The stack of dyString buffers is only accessible locally
-static struct dyString *dsStack = NULL;
+static struct dyString *dspStack = NULL;
 // It is desirable to return a unique token but NOT give access to the dyString it is based on
-#define dsPointerToToken(pointer) (int)((long)(pointer) & 0xffffffff)
-#define dsAssertToken(token) assert(dsPointerToToken(dsStack) == token)
+#define dspPointerToToken(pointer) (int)((long)(pointer) & 0xffffffff)
+#define dspAssertToken(token) assert(dspPointerToToken(dspStack) == token)
 
 // This module defaults to STDOUT but an alternative can be registered
-static FILE *dsOut = NULL;
-#define dsInitializeOut() {if (dsOut == NULL) dsOut = stdout;}
-#define dsAssertUnregisteredOut() assert(dsOut == stdout || dsOut == NULL)
-#define dsAssertRegisteredOut(out) assert(dsOut == out)
+static FILE *dspOut = NULL;
+#define dspInitializeOut() {if (dspOut == NULL) dspOut = stdout;}
+#define dspAssertUnregisteredOut() assert(dspOut == stdout || dspOut == NULL)
+#define dspAssertRegisteredOut(out) assert(dspOut == out)
 
 
-int dsPrintOpen(int initialBufSize)
+int dspOpen(int initialBufSize)
 // Allocate dynamic string and puts at top of stack
 // returns token to be used for later stack accounting asserts
 {
 if (initialBufSize <= 0)
     initialBufSize = 2048; // presume large
 struct dyString *ds = dyStringNew(initialBufSize);
-slAddHead(&dsStack,ds);
-return dsPointerToToken(ds);
+slAddHead(&dspStack,ds);
+return dspPointerToToken(ds);
 }
 
 
-int dsPrintf(char *format, ...)
+int dspPrintf(char *format, ...)
 // Prints into end of the top ds buffer, and return resulting string length
 // If there is no current buffer, this acts like a simple printf and returns -1
 {
 int len = -1; // caller could assert returned length > 0 to ensure dsPrint is open!
 va_list args;
 va_start(args, format);
-if (dsStack != NULL)
+if (dspStack != NULL)
     {
-    dyStringVaPrintf(dsStack, format, args);
-    len = dyStringLen(dsStack);
+    dyStringVaPrintf(dspStack, format, args);
+    len = dyStringLen(dspStack);
     }
 else
     {
-    dsInitializeOut()
-    vfprintf(dsOut,format, args);
+    dspInitializeOut()
+    vfprintf(dspOut,format, args);
     }
 va_end(args);
 return len;
 }
 
 
-int dsPrintDirectly(int token,FILE *file)
+int dspPrintDirectly(int token,FILE *file)
 // Prints the contents of the top buffer directly to a file.
 // Will leave the filled buffer at top of stack
 // Returns the length printed.
 {
-dsAssertToken(token);
+dspAssertToken(token);
 
-int len = dyStringLen(dsStack);
+int len = dyStringLen(dspStack);
 if (len != 0)
-    fprintf(file,"%s",dyStringContents(dsStack));
+    fprintf(file,"%s",dyStringContents(dspStack));
     fflush(file);
 return len;
 }
 
 
-int dsPrintFlush(int token)
+int dspFlush(int token)
 // Flushes the top buffer to the next lower one and empties top buffer.
 // If there is no lower buffer then the content is printed to STDOUT (or registered out).
 // Returns the length flushed.
 {
-dsAssertToken(token);
+dspAssertToken(token);
 
-int len = dyStringLen(dsStack);
+int len = dyStringLen(dspStack);
 if (len != 0)
     {
-    if (dsStack->next == NULL)
+    if (dspStack->next == NULL)
         {
-        dsInitializeOut();
-        fprintf(dsOut,"%s",dyStringContents(dsStack));
-        fflush(dsOut);
+        dspInitializeOut();
+        fprintf(dspOut,"%s",dyStringContents(dspStack));
+        fflush(dspOut);
         }
     else
-        dyStringAppend(dsStack->next,dyStringContents(dsStack));
-    dyStringClear(dsStack);
+        dyStringAppend(dspStack->next,dyStringContents(dspStack));
+    dyStringClear(dspStack);
     }
 return len;
 }
 
 
-int dsPrintClose(int token)
+int dspClose(int token)
 // Abandons the top buffer and its content, freeing memory.
 // Returns the length abandoned.
 {
-dsAssertToken(token);
-int len = dyStringLen(dsStack);
-struct dyString *ds = slPopHead(&dsStack);
+dspAssertToken(token);
+int len = dyStringLen(dspStack);
+struct dyString *ds = slPopHead(&dspStack);
 dyStringFree(&ds);
 return len;
 }
 
-int dsPrintFlushAndClose(int token)
+int dspFlushAndClose(int token)
 // Flushes the top buffer to the next lower one and frees the top buffer.
 // If there is no lower buffer then the content is printed to STDOUT (or registered out).
 // Returns the length flushed.
 {
-int len = dsPrintFlush(token);
-dsPrintClose(token);
+int len = dspFlush(token);
+dspClose(token);
+return len;
+}
+
+
+int dspFlushAndCloseAll()
+// CAUTION: Bad practice to not Open/Close levels in turn!
+// flushes, frees and closes all stacked buffers
+// returns length flushed
+{
+int len = 0;
+
+// more efficient method than repeated dspFlushAndClose calls
+if (dspStack != NULL)
+    {
+    dspInitializeOut();
+    slReverse(&dspStack);  // Oldest prints first.
+    while (dspStack != NULL)
+        {
+        struct dyString *ds = slPopHead(&dspStack);
+        char *content = dyStringCannibalize(&ds);
+        len += strlen(content);
+        fprintf(dspOut,"%s",content);
+        freeMem(content);
+        }
+    fflush(dspOut);
+    }
 return len;
 }
 
 
-char * dsPrintContent(int token)
+char * dspContent(int token)
 // returns the content of the current buffer as a string, but does not flush or free it
 // NOTE: returned pointer is not cloned memory and will be changed by successive dsPrint calls
 {
-dsAssertToken(token);
-return dyStringContents(dsStack);
+dspAssertToken(token);
+return dyStringContents(dspStack);
 }
 
 
-int dsPrintEmpty(int token)
+int dspEmpty(int token)
 // Empties the top buffer in stack (abandoning content), but leave buffer in place
 // Returns the length abandoned.
 {
-dsAssertToken(token);
-int len = dyStringLen(dsStack);
-dyStringClear(dsStack);
+dspAssertToken(token);
+int len = dyStringLen(dspStack);
+dyStringClear(dspStack);
 return len;
 }
 
 
-static int dsPrintStackIxFromToken(int token)
+static struct dyString *dspPointerFromToken(int token)
 // Return the stack member corresponding to the token.
 // This ix can be used to walk up the stack from a given point
 {
-struct dyString *ds = dsStack; 
+struct dyString *ds = dspStack;
 int ix = 0;
 
 for (; ds != NULL; ds = ds->next, ++ix)
     {
-    if (dsPointerToToken(ds) == token)
-        return ix;
+    if (dspPointerToToken(ds) == token)
+        return ds;
     }
-return -1;
+return NULL;
 }
 
 
-static struct dyString *dsPrintStackMemberFromIx(int ix)
-// Return the stack member corresponding to the ix.
-// By walking stack ix's, you can walk up or down the stack
+int dspSize(int token)
+// Returns the current length of the buffer starting at the level corresponding to token.
+// Unlike other cases, the token does not have to correspond to the top of the stack!
 {
-if (ix < 0)
-    return NULL;
-return slElementFromIx(dsStack,ix);
+struct dyString *dsTarget = dspPointerFromToken(token);
+assert(dsTarget != NULL);
+
+int len = dyStringLen(dsTarget);
+struct dyString *ds = dspStack;
+for (; ds != dsTarget && ds != NULL; ds = ds->next) // assertable != NULL, but still
+    len += dyStringLen(ds);
+return len;
 }
 
 
-int dsPrintSize(int token)
-// Returns the curent length of the buffer starting at the level corresponding to token.
-// Unlike other cases, the token does not have to correspond to the top of the stack!
+int dspSizeAll()
+// returns combined current size of all buffers
 {
-//dsAssertToken(token);
-//return dyStringLen(dsStack);
-int ix = dsPrintStackIxFromToken(token);
-assert(ix >= 0);
-
 int len = 0;
-struct dyString *ds = NULL;
-for (;ix>0;ix--)
+if (dspStack != NULL)
     {
-    ds = dsPrintStackMemberFromIx(ix);
-    assert(ds != NULL);
+    struct dyString *ds = dspStack;
+    for (;ds != NULL; ds = ds->next)
     len += dyStringLen(ds);
     }
-len += dyStringLen(dsStack);
 return len;
 }
 
 
-int dsPrintStackDepth()
+int dspStackDepth()
 // Returns the current dsPrint buffer stack depth
 {
-return slCount(dsStack);
+return slCount(dspStack);
 }
 
 
-void dsPrintRegisterOut(FILE *out)
+void dspRegisterOutStream(FILE *out)
 // Registers an alternative to STDOUT.  After registering, all dsPrint out goes to this file.
 // NOTE: There is no stack. Register/Unregister serially.
 {
-dsAssertUnregisteredOut();
-dsOut = out;
+dspAssertUnregisteredOut();
+dspOut = out;
 }
 
 
-void dsPrintUnregisterOut(FILE *out)
+void dspUnregisterOutStream(FILE *out)
 // Unregisters the alternative to STDOUT.  After unregistering all dsPrint out goes to STDOUT.
 // NOTE: There is no stack. Register/Unregister serially.
 {
-dsAssertRegisteredOut(out);
-dsOut = stdout;
+dspAssertRegisteredOut(out);
+dspOut = stdout;
 }
 
 
-int dsPrintSizeAll()
-// returns combined current size of all buffers
-{
-int len = 0;
-if (dsStack != NULL)
+char *dspCannibalizeAndClose(int token)
+// Closes the top stack buffer returning content.  Returned string should be freed.
     {
-    struct dyString *ds = dsStack;
-    for (;ds != NULL; ds = ds->next)
-        len += dyStringLen(ds);
-    }
-return len;
+dspAssertToken(token);
+struct dyString *ds = slPopHead(&dspStack);
+return dyStringCannibalize(&ds);
 }
 
 
-int dsPrintFlushAndCloseAll()
-// flushes, frees and closes all stacked buffers
-// returns length flushed
-{
-int len = 0;
-if (dsStack != NULL)
+char *dspCannibalizeAndCloseAll()
+// CAUTION: Bad practice to not Open/Close levels in turn!
+// Closes all stack buffers and returns a single string that is the full content.  
+// Returned string should be freed.
     {
-    while (dsStack != NULL)
+// Using repeated dspFlushAndClose calls will build the contents into a single string.
+while (dspStack != NULL && dspStack->next != NULL) // Leaves only one on the stack
         {
-        int token = dsPointerToToken(dsStack);
-        len = dsPrintFlushAndClose(token); // Only the last length is needed!
+    int token = dspPointerToToken(dspStack);
+    dspFlushAndClose(token); // Flushes each to the next lower buffer
         }
-    }
-return len;
+if (dspStack == NULL)
+    return NULL;
+else
+    return dyStringCannibalize(&dspStack);
 }
 
 
-char *dsPrintCannibalizeAndClose(int token)
-// Closes the top stack buffer returning content.  Returned string should be freed.
+void *dspStackPop(int token)
+// CAUTION: Not for the faint of heart: print buffer surgery is no substitute for proper coding.
+// Returns a pointer which can be dspStackPush'd back into place.
+// Pop/Push is useful for inserting print immediately before a dspOpen'd print buffer
 {
-dsAssertToken(token);
-struct dyString *ds = slPopHead(&dsStack);
-return dyStringCannibalize(&ds);
+// Note: while this returns a struct dyString *, the caller is kept in the dark.
+//       This is because only this module should manipulate these buffers directly.
+dspAssertToken(token);
+return slPopHead(&dspStack);
 }
 
 
-char *dsPrintCannibalizeAndCloseAll()
-// Closes all stack buffers and returns a single string that is the full content.  
-// Returned string should be freed.
-{
-while (dsStack != NULL && dsStack->next != NULL)
+void dspStackPush(int token,void *dspPointer)
+// CAUTION: Not for the faint of heart: print buffer surgery is no substitute for proper coding.
+// Push a previously dspStackPop'd print buffer back onto the stack
+// Pop/Push is useful for inserting print immediately before a dspOpen'd print buffer
     {
-    int token = dsPointerToToken(dsStack);
-    dsPrintFlushAndClose(token); // Flushes each to the next lower buffer
+assert(dspPointerToToken(dspPointer) == token);
+slAddHead(&dspStack,dspPointer);
     }
-if (dsStack == NULL)
-    return NULL;
-else
-    return dyStringCannibalize(&dsStack);
+
+
+#ifdef NOT_NOW
+// Requires regex function added to dystring.c which has been successfully tested.
+boolean dspRegexReplace(int token, char *regExpression, char *replaceWith)
+// CAUTION: Not for the faint of heart: print buffer surgery is no substitute for proper coding.
+// Looks for and replaces a single instance of a wildcard match in the current dsp print buffer
+// regex follows normal EXTENDED (egrep) rules except:
+// ^ - at beginning of the regExpression only and matches beginning of buffer (not of line)
+// $ - at end of regExpression only and matches end of buffer (not end of line)
+// \n - newlines in the body of the regExpression will match newlines in the current print buffer
+// Returns TRUE on success
+// CAUTION: Wildcards match largest sub-string [w.*d] matches all of "wild wild world"
+{
+dspAssertToken(token);
+return dyStringRegexReplace(dspStack,regExpression,replaceWith);
 }
 
-/*
+
 void dsPrintTest()
 // Tests of dsPrint functions
 {
 printf("Plain printf\n");
-dsPrintf("1 dsPrintf unopened\n");
-
-int token1 = dsPrintOpen(0);
-dsPrintf("2 dsOpen:1\n");
-dsPrintFlush(token1);
-dsPrintf("3^10   1:%d after flush\n",dsPrintStackDepth());
-int token2 = dsPrintOpen(256);
-dsPrintf("4 dsOpen:2 len1:%d  len2:%d  lenAll:%d\n",
-         dsPrintSize(token1),dsPrintSize(token2),dsPrintSizeAll());
-dsPrintRegisterOut(stderr);
-dsPrintFlush(token2);
-dsPrintUnregisterOut(stderr);
-dsPrintf("5      2:%d After flush  len1:%d  len2:%d  lenAll:%d\n",
-         dsPrintStackDepth(),dsPrintSize(token1),dsPrintSize(token2),dsPrintSizeAll());
-dsPrintFlushAndClose(token2);
-dsPrintf("6      1:%d After flushAndClose:2  len1:%d\n",dsPrintStackDepth(),dsPrintSize(token1));
-token2 = dsPrintOpen(256);
-dsPrintf("7 dsOpen:2 reopen:2  WILL ABANDON CONTENT\n");
-char *content = cloneString(dsPrintContent(token2));
-dsPrintAbandonContent(token2);
-dsPrintf("8x7    2:%d len1:%d len2:%d lenAll:%d isEmpty2:%s\n",
-         dsPrintStackDepth(),dsPrintSize(token1),dsPrintSize(token2),dsPrintSizeAll(),
-         (dsPrintIsEmpty(token2) ? "EMPTY":"NOT_EMPTY"));
+dspPrintf("1 dspPrintf unopened\n");
+
+int token1 = dspOpen(0);
+dspPrintf("2 dsOpen:1\n");
+dspFlush(token1);
+dspPrintf("3^10   1:%d after flush\n",dspStackDepth());
+int token2 = dspOpen(256);
+dspPrintf("4 dsOpen:2 len1:%d  len2:%d  lenAll:%d\n",
+         dspSize(token1),dspSize(token2),dspSizeAll());
+dspRegisterOutStream(stderr);
+dspFlush(token2);
+dspUnregisterOutStream(stderr);
+dspPrintf("5      2:%d After flush  len1:%d  len2:%d  lenAll:%d\n",
+         dspStackDepth(),dspSize(token1),dspSize(token2),dspSizeAll());
+dspFlushAndClose(token2);
+dspPrintf("6      1:%d After flushAndClose:2  len1:%d\n",dspStackDepth(),dspSize(token1));
+token2 = dspOpen(256);
+dspPrintf("7 dsOpen:2 reopen:2  WILL ABANDON CONTENT\n");
+char *content = cloneString(dspContent(token2));
+dspAbandonContent(token2);
+dspPrintf("8x7    2:%d len1:%d len2:%d lenAll:%d isEmpty2:%s\n",
+         dspStackDepth(),dspSize(token1),dspSize(token2),dspSizeAll(),
+         (dspIsEmpty(token2) ? "EMPTY":"NOT_EMPTY"));
 strSwapChar(content,'\n','~');  // Replace newline before printing.
-dsPrintf("9      2:%d No flush yet   len1:%d  len2:%d  lenAll:%d  prev7:[%s]\n",
-         dsPrintStackDepth(),dsPrintSize(token1),dsPrintSize(token2),dsPrintSizeAll(),content);
+dspPrintf("9      2:%d No flush yet   len1:%d  len2:%d  lenAll:%d  prev7:[%s]\n",
+         dspStackDepth(),dspSize(token1),dspSize(token2),dspSizeAll(),content);
 freez(&content);
-int token3 = dsPrintOpen(256);
-dsPuts("10  Open:3 Line doubled and out of turn due to dsPrintDirectly()");
-dsPrintDirectly(token3,stdout);
-dsPrintf("11     3:%d Just prior to closing all.  len1:%d  len2:%d  len3:%d  lenAll:%d",
-         dsPrintStackDepth(),
-         dsPrintSize(token1),dsPrintSize(token2),dsPrintSize(token3),dsPrintSizeAll());
-int token4 = dsPrintOpen(256);
-dsPrintf("12  Open:4 Opened  stack:%d  WILL ABANDON\n",dsPrintStackDepth());
-dsPrintAbandon(token4);
-dsPutc('\n');
-dsPuts("13  Puts:  Added last '\\n' with dsPutc(), this with dsPuts().");
-dsPrintf("14     3:%d Last line!  Expect 7 & 12 missing, 10 dupped.  lenAll:%d  Bye.\n",
-         dsPrintStackDepth(),dsPrintSizeAll());
-dsPrintFlushAndCloseAll();
-assert(dsStack == NULL);
+int token3 = dspOpen(256);
+dspPuts("10  Open:3 Line doubled and out of turn due to dspPrintDirectly()");
+dspPrintDirectly(token3,stdout);
+dspPrintf("11     3:%d Just prior to closing all.  len1:%d  len2:%d  len3:%d  lenAll:%d",
+         dspStackDepth(),
+         dspSize(token1),dspSize(token2),dspSize(token3),dspSizeAll());
+int token4 = dspOpen(256);
+dspPrintf("12  Open:4 Opened  stack:%d  WILL ABANDON\n",dspStackDepth());
+dspAbandon(token4);
+dspPutc('\n');
+dspPuts("13  Puts:  Added last '\\n' with dsPutc(), this with dsPuts().");
+dspPrintf("14     3:%d Last line!  Expect 7 & 12 missing, 10 dupped.  lenAll:%d  Bye.\n",
+         dspStackDepth(),dspSizeAll());
+dspFlushAndCloseAll();
+assert(dspStack == NULL);
 int ix = 0;
 for (;ix<20;ix++) // tested to 1000
     {
-    dsPrintOpen(32); // Don't even care about tokens!
+    dspOpen(32); // Don't even care about tokens!
     if (ix == 0)
-        dsPrintf("CannibalizeAndCloseAll():");
-    dsPrintf(" %d",dsPrintStackDepth());
+        dspPrintf("CannibalizeAndCloseAll():");
+    dspPrintf(" %d",dspStackDepth());
     }
-content = dsPrintCannibalizeAndCloseAll();
+content = dspCannibalizeAndCloseAll();
 printf("\n[%s]\n",content);
 freez(&content);
 }
-*/
+#endif///def NOT_NOW
+