3438a8ca132910e102f2269b4239df6bfeffc685
tdreszer
  Mon Aug 20 12:10:32 2012 -0700
Renamed dsprint to dyOut.  Hopefully this proves to be a more popular name.
diff --git src/lib/dyOut.c src/lib/dyOut.c
new file mode 100644
index 0000000..335d371
--- /dev/null
+++ src/lib/dyOut.c
@@ -0,0 +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
+// (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 *dspStack = NULL;
+// It is desirable to return a unique token but NOT give access to the dyString it is based on
+#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 *dspOut = NULL;
+#define dspInitializeOut() {if (dspOut == NULL) dspOut = stdout;}
+#define dspAssertUnregisteredOut() assert(dspOut == stdout || dspOut == NULL)
+#define dspAssertRegisteredOut(out) assert(dspOut == out)
+
+
+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(&dspStack,ds);
+return dspPointerToToken(ds);
+}
+
+
+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 (dspStack != NULL)
+    {
+    dyStringVaPrintf(dspStack, format, args);
+    len = dyStringLen(dspStack);
+    }
+else
+    {
+    dspInitializeOut()
+    vfprintf(dspOut,format, args);
+    }
+va_end(args);
+return len;
+}
+
+
+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.
+{
+dspAssertToken(token);
+
+int len = dyStringLen(dspStack);
+if (len != 0)
+    fprintf(file,"%s",dyStringContents(dspStack));
+    fflush(file);
+return len;
+}
+
+
+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.
+{
+dspAssertToken(token);
+
+int len = dyStringLen(dspStack);
+if (len != 0)
+    {
+    if (dspStack->next == NULL)
+        {
+        dspInitializeOut();
+        fprintf(dspOut,"%s",dyStringContents(dspStack));
+        fflush(dspOut);
+        }
+    else
+        dyStringAppend(dspStack->next,dyStringContents(dspStack));
+    dyStringClear(dspStack);
+    }
+return len;
+}
+
+
+int dspClose(int token)
+// Abandons the top buffer and its content, freeing memory.
+// Returns the length abandoned.
+{
+dspAssertToken(token);
+int len = dyStringLen(dspStack);
+struct dyString *ds = slPopHead(&dspStack);
+dyStringFree(&ds);
+return len;
+}
+
+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 = 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 * 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
+{
+dspAssertToken(token);
+return dyStringContents(dspStack);
+}
+
+
+int dspEmpty(int token)
+// Empties the top buffer in stack (abandoning content), but leave buffer in place
+// Returns the length abandoned.
+{
+dspAssertToken(token);
+int len = dyStringLen(dspStack);
+dyStringClear(dspStack);
+return len;
+}
+
+
+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 = dspStack;
+int ix = 0;
+
+for (; ds != NULL; ds = ds->next, ++ix)
+    {
+    if (dspPointerToToken(ds) == token)
+        return ds;
+    }
+return NULL;
+}
+
+
+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!
+{
+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 dspSizeAll()
+// returns combined current size of all buffers
+{
+int len = 0;
+if (dspStack != NULL)
+    {
+    struct dyString *ds = dspStack;
+    for (;ds != NULL; ds = ds->next)
+        len += dyStringLen(ds);
+    }
+return len;
+}
+
+
+int dspStackDepth()
+// Returns the current dsPrint buffer stack depth
+{
+return slCount(dspStack);
+}
+
+
+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.
+{
+dspAssertUnregisteredOut();
+dspOut = 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.
+{
+dspAssertRegisteredOut(out);
+dspOut = stdout;
+}
+
+
+char *dspCannibalizeAndClose(int token)
+// Closes the top stack buffer returning content.  Returned string should be freed.
+{
+dspAssertToken(token);
+struct dyString *ds = slPopHead(&dspStack);
+return dyStringCannibalize(&ds);
+}
+
+
+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.
+{
+// 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 = dspPointerToToken(dspStack);
+    dspFlushAndClose(token); // Flushes each to the next lower buffer
+    }
+if (dspStack == NULL)
+    return NULL;
+else
+    return dyStringCannibalize(&dspStack);
+}
+
+
+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
+{
+// 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);
+}
+
+
+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
+{
+assert(dspPointerToToken(dspPointer) == token);
+slAddHead(&dspStack,dspPointer);
+}
+
+
+#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");
+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.
+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 = 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
+    {
+    dspOpen(32); // Don't even care about tokens!
+    if (ix == 0)
+        dspPrintf("CannibalizeAndCloseAll():");
+    dspPrintf(" %d",dspStackDepth());
+    }
+content = dspCannibalizeAndCloseAll();
+printf("\n[%s]\n",content);
+freez(&content);
+}
+#endif///def NOT_NOW
+
+