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 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 +