/***************************************************************************** /* StrDsc.c One of the basic constraints with standard VMS string descriptors is the limited size - 65535 characters max (ignoring 64 bit descriptors which are not supported on all platforms WASD can be built upon). While 65k might seem an awful lot there are circumstances where using a standard descriptor might be a limitation. WASD descriptors use 32 bit pointers and counters. They also include pointers to allow the creation of a linked list of them. Where data does not need to be contiguous (as with output) this makes it possible to build large buffers of data without memory reallocation (and the expensive memory copying associated). Where data does need to be contiguous the WASD descriptor buffer can be grown through reallocating memory (and transparently moving content to the new location as necessary - a'la the C-RTL realloc()). The pointed-to strings are always null-terminated and so non-linked descriptors are amenable to processing with C-RTL functions, etc. A number of functions below allow these descriptors to be easily created, built (filled with data), and disposed of. In addition FaolSAK() understands the structure and can write formatted data into a descriptor, linking descriptors or growing the buffer as required over multiple formatted writes. Even more usefully, NetWriteStrDsc() will write one or a linked list of these descriptors to the client. The 'itself' member of the structure is used to both detect whether a structure has been initialised and also as a subsequent sanity check where functions expect an initialised structure. It does this by being set to the bit-wise negation of address of itself (to avoid an random stack content pointer match) and reset when disposed of. A sanity check of this and the flags field helps ensure integrity of descriptor usage during run-time. Allocated storage using VmGet() and VmGetHeap() returns zeroed storage so initialised descriptors are not a problem. On the stack with function automatic storage is a different situation and descriptors should be explicitly zeroed during declaration using the STR_DSC_AUTO macro. VERSION HISTORY --------------- 02-MAY-2022 MGD StrDscPrintf() 09-JUN-2007 MGD a long, long time coming but WASD string descriptors (and the associated functions) have finally arrived! */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #undef __VMS_VER #undef __CRTL_VER #define __CRTL_VER 70000000 #endif /* standard C header files */ #include #include #include #include #include /* VMS related header files */ #include #include /* application related header files */ #include "wasd.h" #define WASD_MODULE "STRDSC" /* obviously low-level debug can't use StrDsc..() functions - aka WATCH! */ #define DEBUG_STRDSC 0 #define ITSELF(ptr) ((STR_DSC*)~(unsigned long)ptr) /********************/ /* external storage */ /********************/ #ifdef DBUG extern BOOL Debug; #else #define Debug 0 #endif extern char ErrorSanityCheck[]; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* Initialise (or reinitialise) a descriptor ready for use. If 'sdptr' is NULL create a dynamically allocated descriptor otherwise use the supplied pointer. If 'size' is -1 allocate the default quantity of storage; if 0 allocate no storage; or allocate the indicated quantity. */ STR_DSC* StrDscBegin ( REQUEST_STRUCT *rqptr, STR_DSC *sdptr, int size ) { unsigned long flags = 0; /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscBegin()\n"); if (size < 0) size = STR_DSC_DEFAULT_SIZE; if (sdptr) { if (STR_DSC_SANITY(sdptr)) { flags = sdptr->flags; StrDscEnd (sdptr, FI_LI); } else memset (sdptr, 0, sizeof(STR_DSC)); } if (!sdptr || (flags & STR_DSC_FLAG_DYNAMIC)) { if (rqptr) { sdptr = VmGetHeap (rqptr, sizeof(STR_DSC)); if (sdptr->size = size) sdptr->pointer = VmGetHeap (rqptr, size); } else { sdptr = VmGet (sizeof(STR_DSC)); if (sdptr->size = size) sdptr->pointer = VmGet (size); #if WATCH_MOD if (size) StrDscWatchNotHeap (FI_LI); #endif } sdptr->flags = STR_DSC_FLAG_FLAG | STR_DSC_FLAG_DYNAMIC | STR_DSC_FLAG_STORAGE; } else { if (rqptr) { if (sdptr->size = size) sdptr->pointer = VmGetHeap (rqptr, size); } else { if (sdptr->size = size) sdptr->pointer = VmGet (size); #if WATCH_MOD if (size) StrDscWatchNotHeap (FI_LI); #endif } sdptr->flags = STR_DSC_FLAG_FLAG | STR_DSC_FLAG_STORAGE; } sdptr->itself = ITSELF(sdptr); sdptr->rqptr = rqptr; return (sdptr); } /*****************************************************************************/ /* Free this fixed or dynamically allocated WASD string descriptor. */ StrDscEnd ( STR_DSC *sdptr, char *SourceModuleName, int SourceLineNumber ) { STR_DSC *flink; /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscEnd()\n"); /* just consider it to have been unused */ if (!STR_DSC_SANITY(sdptr)) return; while (sdptr) { if (!STR_DSC_SANITY(sdptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, SourceModuleName, SourceLineNumber); flink = sdptr->flink; if (sdptr->rqptr) { if (sdptr->pointer && (sdptr->flags & STR_DSC_FLAG_STORAGE)) VmFreeFromHeap (sdptr->rqptr, sdptr->pointer, SourceModuleName, SourceLineNumber); if (sdptr->flags & STR_DSC_FLAG_DYNAMIC) VmFreeFromHeap (sdptr->rqptr, sdptr, SourceModuleName, SourceLineNumber); else memset (sdptr, 0, sizeof(STR_DSC)); } else { if (sdptr->pointer && (sdptr->flags & STR_DSC_FLAG_STORAGE)) VmFree (sdptr->pointer, SourceModuleName, SourceLineNumber); if (sdptr->flags & STR_DSC_FLAG_DYNAMIC) VmFree (sdptr, SourceModuleName, SourceLineNumber); else memset (sdptr, 0, sizeof(STR_DSC)); } sdptr = flink; } } /*****************************************************************************/ /* Ensure that a descriptor has been initialised. This is different to StrDscBegin() in that it doesn't re-initialise. */ STR_DSC* StrDscIfNotBegin ( REQUEST_STRUCT *rqptr, STR_DSC *sdptr, int BufferSize ) { /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscIfNotBegin()\n"); if (!sdptr || !STR_DSC_SANITY(sdptr)) sdptr = StrDscBegin (rqptr, sdptr, BufferSize); return (sdptr); } /*****************************************************************************/ /* Ensure that a collection descriptor has been initialised. */ STR_DSC* StrDscColIfNotBegin ( REQUEST_STRUCT *rqptr, STR_DSC *sdptr, int BufferSize ) { /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscColIfNotBegin()\n"); if (!sdptr || !STR_DSC_SANITY(sdptr)) { sdptr = StrDscBegin (rqptr, sdptr, BufferSize); sdptr->flags |= STR_DSC_FLAG_COLLECTION; } return (sdptr); } /*****************************************************************************/ /* Reallocate memory to the descriptor. In this case a non-zero 'grow' parameter is the *additional* memory to allocate, otherwise '->realloc' will be used. */ int StrDscRealloc ( REQUEST_STRUCT *rqptr, STR_DSC *sdptr, int grow, char *SourceModuleName, int SourceLineNumber ) { /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscRealloc()\n"); if (!grow) grow = sdptr->realloc; if (!grow) grow = STR_DSC_DEFAULT_SIZE; if (!STR_DSC_SANITY(sdptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); if (sdptr->rqptr) sdptr->pointer = VmReallocHeap (sdptr->rqptr, sdptr->pointer, sdptr->size += grow, SourceModuleName, SourceLineNumber); else { #if WATCH_MOD StrDscWatchNotHeap (FI_LI); #endif sdptr->pointer = VmRealloc (sdptr->pointer, sdptr->size += grow, SourceModuleName, SourceLineNumber); } return (sdptr->size); } /*****************************************************************************/ /* Set the descriptor so it points at some external (array of char) buffer. */ STR_DSC* StrDscExternal ( REQUEST_STRUCT *rqptr, STR_DSC *sdptr, char *BufferPtr, int BufferSize ) { /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscExternal()\n"); sdptr = StrDscBegin (rqptr, sdptr, 0); sdptr->flags &= ~STR_DSC_FLAG_STORAGE; sdptr->flags |= STR_DSC_FLAG_EXTERNAL; sdptr->pointer = BufferPtr; sdptr->size = BufferSize; return (sdptr); } /*****************************************************************************/ /* Set the descriptor so it points at some external, null-terminated text. Intended for constants or other text requiring a descriptor but be careful not to otherwise pull the text out from under it! NOTE: this seems to have an issue with the likes of StrDscThis(..,"\n",1) and I haven't been able to determined why. */ STR_DSC* StrDscThis ( REQUEST_STRUCT *rqptr, STR_DSC *sdptr, char *TextPtr, int TextLength ) { /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscThis()\n"); if (TextLength <= 0 && TextPtr) TextLength = strlen(TextPtr); sdptr = StrDscBegin (rqptr, sdptr, 0); sdptr->flags &= ~STR_DSC_FLAG_STORAGE; sdptr->flags |= STR_DSC_FLAG_EXTERNAL; sdptr->pointer = TextPtr; sdptr->size = (sdptr->length = TextLength) + 1; return (sdptr); } /*****************************************************************************/ /* This function is intended for quick, ad hoc, string descriptor variables, often on automatic storage, or where the text must persist. */ STR_DSC* StrDscText ( REQUEST_STRUCT *rqptr, STR_DSC *sdptr, char *TextPtr, int TextLength ) { /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscText()\n"); if (TextLength <= 0) TextLength = strlen(TextPtr); sdptr = StrDscBegin (rqptr, sdptr, TextLength+1); memcpy (sdptr->pointer, TextPtr, TextLength); sdptr->length = TextLength; return (sdptr); } /*****************************************************************************/ /* Given a pointer to a (potential) list of descriptors, find the first one with space remaining and return a pointer to that. The exact byte within that descriptor can then be calculated using its pointer and length. If there is no descriptor with space remaining then either reallocate extra space for the current descriptor, or create another one and add it to the list. Request and non-request related descriptors are distinguished by the presence or absence of a request pointer in the structure. */ STR_DSC* StrDscBuffer (STR_DSC *sd1ptr) { REQUEST_STRUCT *rqptr; STR_DSC *sd2ptr; /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscBuffer()\n"); if (!STR_DSC_SANITY(sd1ptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); if (sd1ptr->flags & STR_DSC_FLAG_EXTERNAL) return (sd1ptr); if (!(sd1ptr->flags & STR_DSC_FLAG_FROZEN)) if (sd1ptr->pointer && sd1ptr->length < sd1ptr->size) return (sd1ptr); rqptr = sd1ptr->rqptr; /* find the next descriptor with available storage space (if any) */ for (sd2ptr = sd1ptr; sd2ptr; sd2ptr = sd2ptr->flink) { if (!STR_DSC_SANITY(sd2ptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); if (sd2ptr->flags & STR_DSC_FLAG_FROZEN) continue; if (sd2ptr->pointer && sd2ptr->length < sd2ptr->size) return (sd2ptr); } if (!sd1ptr->size) sd1ptr->size = STR_DSC_DEFAULT_SIZE; if (rqptr) { /* request storage */ if (sd1ptr->pointer) { if (sd1ptr->realloc && !(sd1ptr->flags & STR_DSC_FLAG_FROZEN)) { StrDscRealloc (rqptr, sd1ptr, sd1ptr->realloc, FI_LI); return (sd1ptr); } else { /* subsequent allocation of buffer */ sd2ptr = VmGetHeap (rqptr, sizeof(STR_DSC)); sd2ptr->itself = ITSELF(sd2ptr); sd2ptr->pointer = VmGetHeap (rqptr, sd2ptr->size = sd1ptr->size); sd2ptr->flags = STR_DSC_FLAG_FLAG | STR_DSC_FLAG_DYNAMIC | STR_DSC_FLAG_STORAGE; sd2ptr->rqptr = rqptr; /* append it to the end of the list */ while (sd1ptr->flink) sd1ptr = sd1ptr->flink; sd1ptr->flink = sd2ptr; sd2ptr->blink = sd1ptr; return (sd2ptr); } } else { /* initial allocation of buffer */ sd1ptr->itself = ITSELF(sd1ptr); sd1ptr->pointer = VmGetHeap (rqptr, sd1ptr->size); sd1ptr->flags |= STR_DSC_FLAG_STORAGE; sd1ptr->length = 0; return (sd1ptr); } } else { /* non-request storage */ #if WATCH_MOD StrDscWatchNotHeap (FI_LI); #endif if (sd1ptr->pointer) { if (sd1ptr->realloc && !(sd1ptr->flags & STR_DSC_FLAG_FROZEN)) { StrDscRealloc (NULL, sd1ptr, sd1ptr->realloc, FI_LI); return (sd1ptr); } else { /* subsequent allocation of buffer */ sd2ptr = VmGet (sizeof(STR_DSC)); sd2ptr->itself = ITSELF(sd2ptr); sd2ptr->pointer = VmGet (sd2ptr->size = sd1ptr->size); sd2ptr->flags = STR_DSC_FLAG_FLAG | STR_DSC_FLAG_DYNAMIC | STR_DSC_FLAG_STORAGE; sd1ptr->flink = sd2ptr; sd2ptr->blink = sd1ptr; return (sd2ptr); } } else { /* initial allocation of buffer */ sd1ptr->itself = ITSELF(sd1ptr); sd1ptr->pointer = VmGet (sd1ptr->size); sd1ptr->flags |= STR_DSC_FLAG_STORAGE; sd1ptr->length = 0; return (sd1ptr); } } } /*****************************************************************************/ /* Return the text length of a (potentially) list of descriptors. */ int StrDscLength (STR_DSC *sdptr) { int len; /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscLength()\n"); len = 0; while (sdptr) { if (!STR_DSC_SANITY(sdptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); len += sdptr->length; sdptr = sdptr->flink; } return (len); } /*****************************************************************************/ /* Return a pointer to the final entry in a list of descriptors. */ STR_DSC* StrDscTail (STR_DSC *sdptr) { /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscTail()\n"); if (!sdptr) return (NULL); if (!STR_DSC_SANITY(sdptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); while (sdptr->flink) { sdptr = sdptr->flink; if (!STR_DSC_SANITY(sdptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } return (sdptr); } /*****************************************************************************/ /* Freeze the contents of the final entry in a list of descriptors. */ STR_DSC* StrDscTailFrozen (STR_DSC *sdptr) { /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscTailFrozen()\n"); if (!sdptr) return (NULL); if (!STR_DSC_SANITY(sdptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); while (sdptr->flink) { sdptr = sdptr->flink; if (!STR_DSC_SANITY(sdptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } sdptr->flags |= STR_DSC_FLAG_FROZEN; return (sdptr); } /*****************************************************************************/ /* Effectively move the sd2ptr (head-of-list) descriptor to sd1ptr overwriting and losing it's contents including orphaning any memory it points to. This is intended for a fixed descriptor rather than a pointer to descriptor which can of course be merely assigned. */ StrDscMove ( STR_DSC *sd1ptr, STR_DSC *sd2ptr ) { unsigned long flags; /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscMove()\n"); /* sd1ptr does not need to be sanity checked */ if (!STR_DSC_SANITY(sd2ptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); /* there is no back-link for head-of-list descriptors */ if (sd2ptr->flink) sd2ptr->flink->blink = sd1ptr; flags = sd1ptr->flags; memcpy (sd1ptr, sd2ptr, sizeof(STR_DSC)); sd1ptr->itself = ITSELF(sd1ptr); memset (sd2ptr, 0, sizeof(STR_DSC)); /* ensure the dynamic flag is propgated */ if (flags & STR_DSC_FLAG_DYNAMIC) sd1ptr->flags |= STR_DSC_FLAG_DYNAMIC; else sd1ptr->flags &= ~STR_DSC_FLAG_DYNAMIC; } /*****************************************************************************/ /* Effectively swap these two (head-of-list) descriptors (which may be fixed rather than allocated descriptors and therefore not amenable to mere pointer swapping). */ StrDscSwap ( STR_DSC *sd1ptr, STR_DSC *sd2ptr ) { STR_DSC TmpDsc; /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscSwap()\n"); if (!STR_DSC_SANITY(sd1ptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); if (!STR_DSC_SANITY(sd2ptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); /* must be head of list descriptors */ if (sd1ptr->blink || sd2ptr->blink) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); if (sd1ptr->flink) sd1ptr->flink->blink = sd2ptr; if (sd2ptr->flink) sd2ptr->flink->blink = sd1ptr; memcpy (&TmpDsc, sd1ptr, sizeof(STR_DSC)); memcpy (sd1ptr, sd2ptr, sizeof(STR_DSC)); sd1ptr->itself = ITSELF(sd1ptr); memcpy (sd2ptr, &TmpDsc, sizeof(STR_DSC)); sd2ptr->itself = ITSELF(sd2ptr); } /*****************************************************************************/ /* Swap the storage of these two descriptors (CAREFUL!). */ StrDscStorageSwap ( STR_DSC *sd1ptr, STR_DSC *sd2ptr ) { STR_DSC TmpDsc; /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscStroageSwap()\n"); if (!STR_DSC_SANITY(sd1ptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); if (!STR_DSC_SANITY(sd2ptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); TmpDsc.pointer = sd1ptr->pointer; sd1ptr->pointer = sd2ptr->pointer; sd2ptr->pointer = TmpDsc.pointer; TmpDsc.length = sd1ptr->length; sd1ptr->length = sd2ptr->length; sd2ptr->length = TmpDsc.length; TmpDsc.size = sd1ptr->size; sd1ptr->size = sd2ptr->size; sd2ptr->size = TmpDsc.size; } /*****************************************************************************/ /* Duplicate the descriptor (potentially a list of) provided by 'sd2ptr' in the descriptor 'sd1ptr'. If 'sd1ptr' is NULL a new dynamic descriptor is allocated. A pointer to 'sd1ptr' (existing or new) is returned. */ STR_DSC* StrDscDup ( STR_DSC *sd1ptr, STR_DSC *sd2ptr ) { REQUEST_STRUCT *rqptr; STR_DSC *sdptr; /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscDup()\n"); /* sd1ptr does not need to be sanity checked */ if (!STR_DSC_SANITY(sd2ptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); rqptr = sd2ptr->rqptr; sd1ptr = StrDscBegin (rqptr, sd1ptr, sd2ptr->size); /* initial descriptor */ if (sd1ptr->length = sd2ptr->length) { /* including the terminating null */ memcpy (sd1ptr->pointer, sd2ptr->pointer, sd2ptr->length+1); } /* subsequent descriptors */ sdptr = sd1ptr; for (sd2ptr = sd2ptr->flink; sd2ptr; sd2ptr = sd2ptr->flink) { if (rqptr) sdptr->flink = VmGetHeap (rqptr, sizeof(STR_DSC)); else { #if WATCH_MOD StrDscWatchNotHeap (FI_LI); #endif sdptr->flink = VmGet (sizeof(STR_DSC)); } /* careful! */ sdptr->flink->blink = sdptr; sdptr = sdptr->flink; sdptr->itself = ITSELF(sdptr); sdptr->rqptr = rqptr; sdptr->size = sd2ptr->size; sdptr->flags = STR_DSC_FLAG_FLAG | STR_DSC_FLAG_DYNAMIC | STR_DSC_FLAG_STORAGE; if (rqptr) sdptr->pointer = VmGetHeap (rqptr, sdptr->size); else sdptr->pointer = VmGet (sdptr->size); /* including the terminating null */ if (sd1ptr->length = sd2ptr->length) { /* including the terminating null */ memcpy (sd1ptr->pointer, sd2ptr->pointer, sd2ptr->length+1); } } return (sd1ptr); } /*****************************************************************************/ /* Comparison of the descriptor content. */ BOOL StrDscSame ( STR_DSC *sd1ptr, STR_DSC *sd2ptr, BOOL CaseLess ) { int len1, len2; char *cptr, *sptr; STR_DSC *sdptr; /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscSame()\n"); if (!sd1ptr->pointer || !sd2ptr->pointer) return (false); if (!STR_DSC_SANITY(sd1ptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); if (!STR_DSC_SANITY(sd2ptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); len1 = len2 = 0; for (sdptr = sd1ptr; sdptr; sdptr = sdptr->flink) len1 += sdptr->length; for (sdptr = sd2ptr; sdptr; sdptr = sdptr->flink) len2 += sdptr->length; if (len1 != len2) return (false); if (!len1 && !len2) return (true); cptr = sd1ptr->pointer; len1 = sd1ptr->length; sptr = sd2ptr->pointer; len2 = sd2ptr->length; for (;;) { if (!len1) { if (!(sd1ptr = sd1ptr->flink)) break; cptr = sd1ptr->pointer; len1 = sd1ptr->length; } if (!len2) { if (!(sd2ptr = sd2ptr->flink)) break; sptr = sd2ptr->pointer; len2 = sd2ptr->length; } if (CaseLess && to_lower(*cptr) != to_lower(*sptr)) break; if (!CaseLess && *cptr != *sptr) break; cptr++; sptr++; len1--; len2--; } if (len1 || len2) return (false); return (true); } /*****************************************************************************/ /* Reset a (potentially linked list of) descriptor buffer(s) to no content. */ StrDscNoContent (STR_DSC *sdptr) { /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscNoContent()\n"); while (sdptr) { if (!STR_DSC_SANITY(sdptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); sdptr->length = 0; sdptr = sdptr->flink; } } /*****************************************************************************/ /* Ensure the descriptor has a trailing newline. Returns a pointer to the current descriptor. */ STR_DSC* StrDscNewLine (STR_DSC *sdptr) { /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscNewLine()\n"); if (!STR_DSC_SANITY(sdptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); /* find the latest descriptor */ if (!sdptr->pointer || sdptr->flags & STR_DSC_FLAG_FROZEN) sdptr = StrDscBuffer (sdptr); else { while (sdptr->length >= sdptr->size) { if (sdptr->itself != ITSELF(sdptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); if (!sdptr->flink) break; sdptr = sdptr->flink; } } if (sdptr->length && sdptr->pointer[sdptr->length-1] == '\n') return (sdptr); if (sdptr->length >= sdptr->size) sdptr = StrDscBuffer (sdptr); sdptr->pointer[sdptr->length++] = '\n'; sdptr->pointer[sdptr->length] = '\0'; return (sdptr); } /*****************************************************************************/ /* Copy descriptor 2 or null-terminated string to descriptor 1 dynamically expanding buffer space as required. Returns a pointer to the current descriptor. */ STR_DSC* StrDscBuild ( STR_DSC *sd1ptr, STR_DSC *sd2ptr, char *AscizPtr ) { char *cptr, *czptr, *sptr, *zptr; STR_DSC *sdptr; /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscBuild()\n"); if (!STR_DSC_SANITY(sd1ptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); if (sd2ptr && !STR_DSC_SANITY(sd2ptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); /* find the latest descriptor */ if (!sd1ptr->pointer || sd1ptr->flags & STR_DSC_FLAG_FROZEN) sd1ptr = StrDscBuffer (sd1ptr); else { while (sd1ptr->length >= sd1ptr->size) { if (sd1ptr->itself != ITSELF(sd1ptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); if (!sd1ptr->flink) break; sd1ptr = sd1ptr->flink; } } if (AscizPtr) cptr = AscizPtr; else czptr = (cptr = sd2ptr->pointer) + sd2ptr->length; for (;;) { if ((AscizPtr && !*cptr) || (!AscizPtr && cptr >= czptr)) break; zptr = (sptr = sd1ptr->pointer) + sd1ptr->size; sptr += sd1ptr->length; if (AscizPtr) while (*cptr && sptr < zptr) *sptr++ = *cptr++; else while (cptr < czptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; sd1ptr->length = sptr - sd1ptr->pointer; if ((AscizPtr && !*cptr) || (!AscizPtr && cptr >= czptr)) break; sd1ptr = StrDscBuffer (sd1ptr); } return (sd1ptr); } /*****************************************************************************/ /* Copy descriptor 2 or null-terminated string to descriptor 1 dynamically expanding buffer space as required, HTML-escaping the '<', '>' and '&' characters. Returns a pointer to the current descriptor. */ STR_DSC* StrDscBuildHtmlEscape ( STR_DSC *sd1ptr, STR_DSC *sd2ptr, char *AscizPtr ) { static char ebuf [] = "\0"; char *cptr, *czptr, *eptr, *sptr, *zptr; STR_DSC *sdptr; /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscBuildHtmlEscape()\n"); if (!sd1ptr->pointer) sd1ptr = StrDscBuffer (sd1ptr); if (!STR_DSC_SANITY(sd1ptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); if (sd2ptr && !STR_DSC_SANITY(sd2ptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); /* find the latest descriptor */ if (sd1ptr->flags & STR_DSC_FLAG_FROZEN) sd1ptr = StrDscBuffer (sd1ptr); else { while (sd1ptr->length >= sd1ptr->size) { if (sd1ptr->itself != ITSELF(sd1ptr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); if (!sd1ptr->flink) break; sd1ptr = sd1ptr->flink; } } if (AscizPtr) cptr = AscizPtr; else czptr = (cptr = sd2ptr->pointer) + sd2ptr->length; zptr = (sptr = sd1ptr->pointer) + sd1ptr->size; sptr += sd1ptr->length; for (;;) { /* buffer non-forbidden chars and handle end-of-source */ if (AscizPtr) { while (*cptr && *cptr != '<' && *cptr != '>' && *cptr != '&' && sptr < zptr) *sptr++ = *cptr++; if (!*cptr) break; } else { while (cptr < czptr && *cptr != '<' && *cptr != '>' && *cptr != '&' && sptr < zptr) *sptr++ = *cptr++; if (cptr >= czptr) break; } /* forbidden character and buffer space exhausted drop through */ switch (*cptr) { case '<' : eptr = "<"; break; case '>' : eptr = ">"; break; case '&' : eptr = "&"; break; /* non-forbidden character (buffer space exhausted) */ default : *(eptr = ebuf) = *cptr; } cptr++; while (*eptr) { while (*eptr && sptr < zptr) *sptr++ = *eptr++; if (sptr >= zptr) { /* buffer space exhaustion */ *sptr = '\0'; sd1ptr->length = sptr - sd1ptr->pointer; sd1ptr = StrDscBuffer (sd1ptr); zptr = (sptr = sd1ptr->pointer) + sd1ptr->size; sptr += sd1ptr->length; } } } *sptr = '\0'; sd1ptr->length = sptr - sd1ptr->pointer; return (sd1ptr); } /*****************************************************************************/ /* This function acts as a snprintf() for WASD string descriptors. When used multiple times (without reset) it builds a composite string. */ STR_DSC* StrDscPrintf ( REQUEST_STRUCT *rqptr, STR_DSC *sdptr, int ChunkSize, char *FormatPtr, ... ) { int count, size; char *cptr; va_list aptr, aptr2; /*********/ /* begin */ /*********/ if (DEBUG_STRDSC) fprintf (stdout, "StrDscPrintf()\n"); if (ChunkSize <= 0) ChunkSize = STR_DSC_DEFAULT_SIZE; if (!sdptr) sdptr = StrDscBegin (rqptr, sdptr, ChunkSize); for (;;) { cptr = sdptr->pointer + sdptr->length; size = sdptr->size - sdptr->length; va_start (aptr, FormatPtr); count = vsnprintf (cptr, size, (const char*)FormatPtr, aptr); va_end(aptr); if (count < 0) { StrDscEnd (sdptr, FI_LI); return (NULL); } if (count > size) StrDscRealloc (rqptr, sdptr, count + ChunkSize, FI_LI); else { sdptr->length += count; sdptr->pointer[sdptr->length] = '\0'; break; } } return (sdptr); } /*****************************************************************************/ /* Has happened often enough during development to warrant a sanity check. Avoid using standard WATCH which uses FaoToBuffer() which in turn uses FaolSAK() which in turn uses StrDsc..()! */ #if WATCH_MOD StrDscWatchNotHeap ( char *SourceModuleName, int SourceLineNumber ) { int len; char NotHeap [128]; /*********/ /* begin */ /*********/ len = sprintf (NotHeap, "|%11s %8s %04u %4s %10s %s|\n", "", "STRDSC ", SourceLineNumber, "", "", "+++++ W A R N I N G +++++ not request heap"); if (Watch.LogFile) fputs (NotHeap, stdout); if (Watch.SecondsToGo) NetWrite (Watch.RequestPtr, 0, NotHeap, len); } #endif /*****************************************************************************/ /* Always seems a character flaw that perfect code cannot be written first-up! */ #if WATCH_MOD StrDscDebug ( STR_DSC *sdptr, char *WhereAt ) { /*********/ /* begin */ /*********/ if (sdptr) fprintf (stdout, "++++++++++ %s\n\ sdptr: %08.08X\n\ %08.08X%08.08X%08.08X%08.08X%08.08X%08.08X%08.08X\n\ ->itself: %08.08X (%08.08X) %s\n\ ->flags: %08.08X\n\ ->size: %d\n\ ->length: %d\n\ ->pointer: %08.08X\n\ ->realloc: %d\n\ ->rqptr: %08.08X\n\ ->flink: %08.08X\n\ ->blink: %08.08X\n\ ++++++++++\n", WhereAt ? WhereAt : "", sdptr, ((ULONGPTR)sdptr)[0], ((ULONGPTR)sdptr)[1], ((ULONGPTR)sdptr)[2], ((ULONGPTR)sdptr)[3], ((ULONGPTR)sdptr)[4], ((ULONGPTR)sdptr)[5], ((ULONGPTR)sdptr)[6], sdptr->itself, ~(unsigned long)sdptr, STR_DSC_SANITY(sdptr) ? "SANE" : "*", sdptr->flags, sdptr->size, sdptr->length, sdptr->pointer, sdptr->realloc, sdptr->rqptr, sdptr->flink, sdptr->blink); else fprintf (stdout, "++++++++++ %s\n\ sdptr: 00000000\n\ ++++++++++\n", WhereAt ? WhereAt : ""); } #endif /****************************************************************************/