/***************************************************************************** /* FAO.c Formatted write using $FAO()-like directives, just a functional subset, it also differs in some significant ways, including extended functionality. The functions can write into either specified storage or into the request's output buffer (probably more efficient that $FAO()ing into storage then copying this to the network buffer). All parameters are passed via a $FAOL()-like pointer-to-longword-vector. Strings are always assumed to be null-terminated. A resultant string is always null-terminated (even after an error), hence the 'BufferSize' can always only store 'BufferSize'-1 characters. 'LengthPtr' provides storage for the number of characters minus the null-termination. It is no exageration to state that this functionality has made the sorts of things undertaken with it ten times easier and more efficient!! $FAO DIRECTIVES SUPPORTED ------------------------- !AC counted ASCII string (first byte must be count, i.e. 0 to 255 chars) !AZ null-terminated string !SL decimal, signed longword !SQ signed quadword (indirect character MUST be used (i.e. "!@SQ")) !UL decimal, unsigned longword !XL hexadecimal, longword !ZL zero-filled decimal, unsigned longword !%D date/time !%I numeric to ASCII identifier !%S if the last converted numeric value was not 1 add "S" !%s if the last converted numeric value was not 1 add "s" !%T time !* multiple '*'+1 characters !+ step-over the next parameter from the vector (anything BUT a "!&@") !- back-up to the previous parameter in the vector (anything BUT a "!&@") !< begin specified width output field (cannot nest, must have width!) !> end specified width output field !! exclamation mark WASD EXTENSIONS --------------- !&. replace non-printable characters with periods (e.g. "!&.AZ") !&\ replace non-printable characters with slash-escapes (e.g. "!&\\AZ") !&, comma-ize the following number (e.g. "!&,SL") !&; HTML-escape the following string (e.g. "!&;AZ") !&% URL-encode the following string (e.g. "!&%AZ") !&[ format string as a file specification (requires preceding ODS flag) !&^ force the following string to upper case !&! force the following string to lower case !&_ underline the following string if it contains a space (..) !&" quote/escape the following string if it contains a space ("..\"..") !&+ if !AZ string is not null or empty then add a leading space !&/ if !AZ string is not null or empty the add a trailing newline !&8 UTF-8 encode string (8 bit characters only) !&A address as a parameter, as "&00000000" (for module WATCHing) !&B boolean (for WATCHing) !&C a single character !&D date/time string truncated after the minute !&E until end-of-line (i.e. up to the next or ) !&F local function address, as "00000000()" (for module WATCHing) !&H bytes in hexadecimal (field width is number) !&I dotted-decimal from 32 bit network-order IP address !&L same as !UL but implicitly comma-ized (as by '!&,') !&M full status message string (returned from sys$getmsg()) !&m text-only status message string (returned from sys$getmsg()) !&O add " SELECTED" if true, "" if false (for radio buttons) !&S VMS status value, as "%X00000000" (plus message string if non-success) !&U URL encode !&W weekday/date/time (field-width applies to date/time component only) !&X hexadecimal value, as "0x00000000" !&Z null-terminated string, as "{length}string" (for module WATCHing) !&? simple conditional, "!&?string if *vector TRUE\rstring if FALSE\r" !&@ nested format string address from vector (FILO stacks the current one) !%% same as !&@ (for backward compatibility of strings in WASD_CONFIG_MSG) The WASD extensions perform two roles. Some modify the way a following directive is formatted, for example HTML-escaping it. Others provide additonal format directives to standard $FAO. Field width should always be placed BEFORE the WASD extensions. Indirection should be indicated immediately before the respective formatting directive. Each directive can have a positive, decimal integer between the '!' and the directive providing a field width, e.g. "!10AZ", or have the field width taken from the vector using "!#AZ". These behave in the same manner as the sys$fao() field width parameter. Any directive may use the indirect character (unlike sys$fao(), where only numeric directives may). This indicates the vector value is the 32 bit address of the value, rather than the value itself, e.g. "!@AZ", "!10@UL", etc. VERSION HISTORY --------------- 29-OCT-2017 MGD bugfix; '%D' do not cancel field-width then "(none)" 02-JAN-2016 MGD bugfix; |if (fw <= 0) fw = 32;| to |if (fw < 0) fw = 32;| 23-APR-2015 MGD bugfix; FaoSAK() sdptr = StrDscBuffer(StrDscPtr); 11-JUL-2013 MGD FaoUrlEncodeTable tilde from "%7e" to "~" (cadaver issue) 11-SEP-2010 MGD '&\' slash-escapes common non-printable codes 27-JUN-2010 MGD '&S' now provides message string if non-success 30-MAY-2010 MGD '&8' UTF-8 encode string (only 8 bit characters) 09-JUN-2007 MGD use STR_DSC make '!&E' the same functionality as previously '!&L' and make '!&L' an implicitly comma-ized '!UL' 16-JUL-2005 MGD '&.' replace non-printable characters with periods, replace ErrorNoticed() with FaoErrorNoticed() in FaoToStdout() and FaoToOpcom() for the obvious reason 01-MAR-2003 MGD '&+' if not null or empty !AZ adds a leading space, '&/' if not null or empty !AZ adds a trailing newline, change behaviour of NULL !AZ to showing an empty string 18-OCT-2002 MGD '&"' quotes if there is a space in the string 19-AUG-2002 MGD '&[' for ODS file naming conversions, '&^' now force-to-upper-case, '&!' force-to-lower, '&_' underlines if there is a space in the string 08-AUG-2002 MGD '&U' request URI (mask password) now '&P', '&U' now URL-encodes a URL (allowing for query string, etc.) 16-JUN-2002 MGD make field-width work for '&,' 31-MAR-2002 MGD '&U' request URI (mask password) 17-MAR-2002 MGD 'SQ' kludge for VAX quadword values 10-JAN-2002 MGD add !&R and !&O 04-AUG-2001 MGD differentiate WASD extensions with '!&', support module WATCHing 07-JUL-2001 MGD add !IP to FaolSAK() 17-MAY-2001 MGD dignified with a module of it's own 02-FEB-2001 MGD add !%I to FaolSAK() 27-AUG-2000 MGD add !SL to FaolSAK() 04-MAR-2000 MGD add FaolSAK() and it's siblings FaoToBuffer(), FaolToBuffer(), FaoToNet() */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #define __VMS_VER 70000000 #undef __CRTL_VER #define __CRTL_VER 70000000 #endif /* standard C header files */ #include #include #include #include #include /* VMS related header files */ #include #include #include #include #include #include #include #include #include #include #include #include /* application related header files */ #include "wasd.h" #define WASD_MODULE "FAO" /******************/ /* global storage */ /******************/ #if WATCH_MOD BOOL DebugFaoSak; # define DEBUG_FAOSAK DebugFaoSak #else # define DEBUG_FAOSAK 0 #endif int OpcomMessages, OpcomTarget; char *FaoUrlEncodeTable [] = { "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", "%08", "%09", "%0a", "%0b", "%0c", "%0d", "%0e", "%0f", "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17", "%18", "%19", "%1a", "%1b", "%1c", "%1d", "%1e", "%1f", "%20", "%21", "%22", "%23", "$", "%25", "%26", "%27", "%28", "%29", "*", "%2b", "%2c", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "%3a", ";", "%3c", "%3d", "%3e", "%3f", "%40", "A", "B", "C", "D", "E", "F", "G", /* 0x47 */ "H", "I", "J", "K", "L", "M", "N", "O", /* 0x4f */ "P", "Q", "R", "S", "T", "U", "V", "W", /* 0x57 */ "X", "Y", "Z", "%5b", "%5c", "%5d", "%5e", "_", /* 0x5f */ "%60", "a", "b", "c", "d", "e", "f", "g", /* 0x67 */ "h", "i", "j", "k", "l", "m", "n", "o", /* 0x6f */ "p", "q", "r", "s", "t", "u", "v", "w", /* 0x77 */ "x", "y", "z", "%7b", "%7c", "%7d", "~", "%7f", "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87", "%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f", "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97", "%98", "%99", "%9a", "%9b", "%9c", "%9d", "%9e", "%9f", "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7", "%a8", "%a9", "%aa", "%ab", "%ac", "%ad", "%ae", "%af", "%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7", "%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf", "%c0", "%c1", "%c2", "%c3", "%c4", "%c5", "%c6", "%c7", "%c8", "%c9", "%ca", "%cb", "%cc", "%cd", "%ce", "%cf", "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7", "%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df", "%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7", "%e8", "%e9", "%ea", "%eb", "%ec", "%ed", "%ee", "%ef", "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7", "%ff", "%f9", "%fa", "%fb", "%fc", "%fd", "%fe", "%ff" }; /* keep this for a couple of versions until the above proves itself */ char *FaoUrlEncodeTable2 [] = { "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", "%08", "%09", "%0a", "%0b", "%0c", "%0d", "%0e", "%0f", "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17", "%18", "%19", "%1a", "%1b", "%1c", "%1d", "%1e", "%1f", "%20", "%21", "%22", "%23", "$", "%25", "%26", "%27", "%28", "%29", "*", "%2b", "%2c", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "%3a", ";", "%3c", "%3d", "%3e", "%3f", "%40", "A", "B", "C", "D", "E", "F", "G", /* 0x47 */ "H", "I", "J", "K", "L", "M", "N", "O", /* 0x4f */ "P", "Q", "R", "S", "T", "U", "V", "W", /* 0x57 */ "X", "Y", "Z", "%5b", "%5c", "%5d", "%5e", "_", /* 0x5f */ "%60", "a", "b", "c", "d", "e", "f", "g", /* 0x67 */ "h", "i", "j", "k", "l", "m", "n", "o", /* 0x6f */ "p", "q", "r", "s", "t", "u", "v", "w", /* 0x77 */ "x", "y", "z", "%7b", "%7c", "%7d", "%7e", "%7f", "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87", "%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f", "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97", "%98", "%99", "%9a", "%9b", "%9c", "%9d", "%9e", "%9f", "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7", "%a8", "%a9", "%aa", "%ab", "%ac", "%ad", "%ae", "%af", "%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7", "%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf", "%c0", "%c1", "%c2", "%c3", "%c4", "%c5", "%c6", "%c7", "%c8", "%c9", "%ca", "%cb", "%cc", "%cd", "%ce", "%cf", "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7", "%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df", "%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7", "%e8", "%e9", "%ea", "%eb", "%ec", "%ed", "%ee", "%ef", "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7", "%ff", "%f9", "%fa", "%fb", "%fc", "%fd", "%fe", "%ff" }; /********************/ /* external storage */ /********************/ #ifdef DBUG extern BOOL Debug; #else #define Debug 0 #endif extern BOOL OdsExtended; extern int OutputBufferSize; extern int ToLowerCase[], ToUpperCase[]; extern char ErrorSanityCheck[], SoftwareID[]; extern char *DayName[]; extern HTTPD_PROCESS HttpdProcess; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* FAOL Swiss Army Knife. This is the primary FAO function described in the module prologue. It will be more than obvious it has been written to optimize efficiency (in the author's limited appreciation anyway) rather than readability. */ int FaolSAK ( REQUEST_STRUCT *rqptr, void *BufferPtr, int BufferSize, ushort *LengthPtr, char *FormatString, ulong *VectorPtr ) { #define FORMAT_STACK_MAX 8 static char HexDigits [] = "0123456789abcdef"; static char HexDigitsUpper [] = "0123456789ABCDEF"; static char EncBuffer [] = "%xx"; static char OneChar [] = "\0"; static $DESCRIPTOR (DayDateTimeFaoDsc, "!AZ, !#%D\0"); static $DESCRIPTOR (HexValueFaoDsc, "0x!8XL\0"); static $DESCRIPTOR (LengthStringFaoDsc, "{!UL}!-!#AZ\0"); static $DESCRIPTOR (Md5FaoDsc, "!8XL!8XL!8XL!8XL\0"); static $DESCRIPTOR (Md5WidthFaoDsc, "!#\0"); static $DESCRIPTOR (PDFaoDsc, "!%D\0"); static $DESCRIPTOR (PIFaoDsc, "!%I\0"); static $DESCRIPTOR (PTFaoDsc, "!%T\0"); static $DESCRIPTOR (SLFaoDsc, "!SL\0"); static $DESCRIPTOR (SLWidthFaoDsc, "!#SL\0"); static $DESCRIPTOR (SQFaoDsc, "!@SQ\0"); static $DESCRIPTOR (SQWidthFaoDsc, "!#@SQ\0"); static $DESCRIPTOR (ULFaoDsc, "!UL\0"); static $DESCRIPTOR (ULWidthFaoDsc, "!#UL\0"); static $DESCRIPTOR (VmsStatusFaoDsc, "%X!8XL\0"); static $DESCRIPTOR (VmsStatusMsgFaoDsc, "%X!8XL (!AZ)\0"); static $DESCRIPTOR (WatchingAddressFaoDsc, "&!8XL/!AZ()\0"); static $DESCRIPTOR (WatchingFunctionFaoDsc, "!8XL()\0"); static $DESCRIPTOR (XLFaoDsc, "!XL\0"); static $DESCRIPTOR (XLWidthFaoDsc, "!#XL\0"); static $DESCRIPTOR (ZLFaoDsc, "!ZL\0"); static $DESCRIPTOR (ZLWidthFaoDsc, "!#ZL\0"); BOOL CommaNumber, FileSpec, ForceLowerCase, ForceUpperCase, HtmlEscape, IndirectValue, PeriodNonPrintable, QuoteIfSpace, SlashNonPrintable, SpaceThenString, TrailingNewlineString, UnderlineIfSpace, UrlEncode, Utf8Encode; int ch, fw, ofw, idx, status, CommaCount, DescrLength, FileOds, LastNumericValue, SysGetMsgFlags; ushort Length; ulong DayOfWeek, VectorValue; ulong Time64 [2]; ulong *ulptr, *Time64Ptr; char *bptr, *bzptr, *cptr, *fptr, *sptr, *zptr, *HexDigitsPtr, *LastFptr; char *FormatStack [FORMAT_STACK_MAX]; char FileString [ODS_MAX_FILE_NAME_LENGTH], String [2048]; $DESCRIPTOR (StringDsc, String); struct dsc$descriptor *VmsDscPtr; STR_DSC *sdptr, *StrDscPtr; STR_DSC_AUTO (BufferDsc); #ifdef __VAX double dValue; #endif #if WATCH_MOD { static BOOL DebugFaoSakCheck; if (!DebugFaoSakCheck) { DebugFaoSakCheck = true; DebugFaoSak = getenv ("WASD_DEBUG_FAOSAK"); } } #endif /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_FAO)) WatchThis (WATCHITM(rqptr), WATCH_MOD_FAO, "FaolSAK() !&X !UL !&X !AZ", BufferPtr, BufferSize, LengthPtr, FormatString); status = SS$_NORMAL; LastNumericValue = 1; /* start with output-field-width set to infinite */ ofw = -1; if (LengthPtr) *LengthPtr = 0; if (BufferPtr) { if (BufferSize == -1) { /* writing to specified STR_DSC storage */ sdptr = StrDscPtr = (STR_DSC*)BufferPtr; sdptr = StrDscBuffer (sdptr); bptr = STR_DSC_PTR(sdptr) + STR_DSC_LEN(sdptr); bzptr = STR_DSC_PTR(sdptr) + STR_DSC_SIZE(sdptr); } else { /* writing to specified char[] storage */ sdptr = StrDscPtr = StrDscExternal (NULL, &BufferDsc, BufferPtr, BufferSize); bptr = STR_DSC_PTR(sdptr) = BufferPtr; bzptr = STR_DSC_PTR(sdptr) + BufferSize-1; } } else { /* writing to network buffer */ sdptr = StrDscIfNotBegin (rqptr, &rqptr->NetWriteBufferDsc, OutputBufferSize); sdptr = StrDscPtr = StrDscBuffer (sdptr); bptr = STR_DSC_PTR(sdptr) + STR_DSC_LEN(sdptr); bzptr = STR_DSC_PTR(sdptr) + STR_DSC_SIZE(sdptr); } /* put initial format string into index zero of the format string stack */ idx = 0; FormatStack[idx] = FormatString; /*************************/ /* nested format strings */ /*************************/ for (;;) { /*****************/ /* format string */ /*****************/ fptr = FormatStack[idx]; while (*fptr) { while (*fptr && *fptr != '!' && ofw) { /* a literal character */ if (bptr < bzptr) { *bptr++ = *fptr++; ofw--; continue; } if (STR_DSC_IS_EXTERNAL(sdptr)) goto FaolSAKovf; STR_DSC_LEN(sdptr) = bptr - STR_DSC_PTR(sdptr); sdptr = StrDscBuffer (StrDscPtr); bptr = STR_DSC_PTR(sdptr) + STR_DSC_LEN(sdptr); bzptr = STR_DSC_PTR(sdptr) + STR_DSC_SIZE(sdptr); } /* if end of literal characters (i.e. not a '!') then break */ if (!*fptr) break; /* if no character follows the '!' then just forget it */ LastFptr = fptr; if (!*++fptr) break; /*********************/ /* format directives */ /*********************/ if (DEBUG_FAOSAK) fprintf (stdout, "fptr |%10.10s|\n", fptr); fw = -1; IndirectValue = false; /*********************/ /* field width, etc. */ /*********************/ if (isdigit(*fptr)) { /* field-width */ fw = atoi(fptr); while (isdigit(*fptr)) fptr++; /* for writes into network buffers, let's keep this < 1024 */ if (!BufferPtr) fw &= 0xfff; } else if (*fptr == '#') { /* field-width from vector */ fptr++; fw = (int)*VectorPtr++; /* for writes into network buffers, let's keep this < 1024 */ if (!BufferPtr) fw &= 0xfff; } /** if (DEBUG_FAOSAK) fprintf (stdout, "fw: %d\n", fw); **/ if (*fptr == '@') { fptr++; IndirectValue = true; } FileOds = 0; DescrLength = -1; CommaNumber = FileSpec = ForceLowerCase = ForceUpperCase = HtmlEscape = PeriodNonPrintable = QuoteIfSpace = SpaceThenString = SlashNonPrintable = TrailingNewlineString = UnderlineIfSpace = UrlEncode = Utf8Encode = false; while (*fptr == '&') { /************************/ /* formatting modifiers */ /************************/ if (SAME2(fptr,'&.')) { fptr += 2; PeriodNonPrintable = true; continue; } if (SAME2(fptr,'&\\')) { fptr += 2; SlashNonPrintable = true; continue; } if (SAME2(fptr,'&,')) { fptr += 2; CommaNumber = true; continue; } if (SAME2(fptr,'&;')) { fptr += 2; HtmlEscape = true; continue; } if (SAME2(fptr,'&%')) { fptr += 2; UrlEncode = true; continue; } if (SAME2(fptr,'&[')) { fptr += 2; FileSpec = true; /* get the numeric value of the file naming schema */ FileOds = *VectorPtr++; continue; } if (SAME2(fptr,'&!')) { fptr += 2; ForceLowerCase = true; continue; } if (SAME2(fptr,'&^')) { fptr += 2; ForceUpperCase = true; continue; } if (SAME2(fptr,'&_')) { fptr += 2; UnderlineIfSpace = true; continue; } if (SAME2(fptr,'&"')) { fptr += 2; QuoteIfSpace = true; continue; } if (SAME2(fptr,'&+')) { fptr += 2; SpaceThenString = true; continue; } if (SAME2(fptr,'&/')) { fptr += 2; TrailingNewlineString = true; continue; } if (SAME2(fptr,'&8')) { fptr += 2; Utf8Encode = true; continue; } break; } if (*fptr == '@') { fptr++; IndirectValue = true; } /* default is no string to copy if it drops through */ sptr = NULL; /* directives in guessed order of most common usage */ if (SAME2(fptr,'AZ')) { /**************************/ /* null-terminated string */ /**************************/ fptr += 2; if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } sptr = *(char*)*VectorPtr++; } else sptr = (char*)*VectorPtr++; if (WATCHMOD (rqptr, WATCH_MOD_FAO)) if (!sptr) WatchThis (WATCHITM(rqptr), WATCH_MOD_FAO, "(null)"); if (!sptr) sptr = ""; /* null or empty string suppresses these */ if (!sptr[0]) SpaceThenString = TrailingNewlineString = false; if (DEBUG_FAOSAK) fprintf (stdout, "!AZ |%s|\n", sptr); /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'UL') || SAME2(fptr,'&L')) { /*********************/ /* unsigned longword */ /*********************/ if (SAME2(fptr,'&L')) CommaNumber = true; fptr += 2; if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } LastNumericValue = *(ULONGPTR)VectorPtr++; } else LastNumericValue = *VectorPtr++; if (DEBUG_FAOSAK) fprintf (stdout, "!UL %d\n", LastNumericValue); if (fw > 0 && !CommaNumber) status = sys$fao (&ULWidthFaoDsc, 0, &StringDsc, fw, LastNumericValue); else status = sys$fao (&ULFaoDsc, 0, &StringDsc, LastNumericValue); if (VMSnok (status)) break; if (!CommaNumber) fw = -1; sptr = String; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'XL')) { /************************/ /* hexadecimal longword */ /************************/ fptr += 2; if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } LastNumericValue = *(ULONGPTR)VectorPtr++; } else LastNumericValue = *VectorPtr++; if (DEBUG_FAOSAK) fprintf (stdout, "!UL %d\n", LastNumericValue); if (fw > 0 && !CommaNumber) status = sys$fao (&XLWidthFaoDsc, 0, &StringDsc, fw, LastNumericValue); else status = sys$fao (&XLFaoDsc, 0, &StringDsc, LastNumericValue); if (VMSnok (status)) break; if (!CommaNumber) fw = -1; sptr = String; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'ZL')) { /************************/ /* zero-filled longword */ /************************/ fptr += 2; if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } LastNumericValue = *(ULONGPTR)VectorPtr++; } else LastNumericValue = *VectorPtr++; if (DEBUG_FAOSAK) fprintf (stdout, "!UL %d\n", LastNumericValue); if (fw > 0 && !CommaNumber) status = sys$fao (&ZLWidthFaoDsc, 0, &StringDsc, fw, LastNumericValue); else status = sys$fao (&ZLFaoDsc, 0, &StringDsc, LastNumericValue); if (VMSnok (status)) break; if (!CommaNumber) fw = -1; sptr = String; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'&@') || SAME2(fptr,'%%')) /* for backward compatibility */ { /************************/ /* nested format string */ /************************/ fptr += 2; if (idx >= FORMAT_STACK_MAX) { status = SS$_OVRMAXARG; break; } if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } cptr = *(char*)*VectorPtr++; } else cptr = (char*)*VectorPtr++; if (!cptr) { status = SS$_ACCVIO; break; } /* if empty then just continue with the current format string */ if (!cptr[0]) continue; /* save the current format string pointer */ FormatStack[idx++] = fptr; /* set the new format string pointer */ fptr = FormatStack[idx] = cptr; if (DEBUG_FAOSAK) fprintf (stdout, "!&@ idx: %d fptr |%s|\n", idx, fptr); continue; } else if (SAME2(fptr,'&h') || SAME2(fptr,'&H')) { /***************/ /* hexadecimal */ /***************/ fptr += 2; if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } cptr = *(char*)*VectorPtr++; } else cptr = (char*)*VectorPtr++; if (!cptr) sptr = "(null)"; else { if (SAME2(fptr,'&h')) HexDigitsPtr = HexDigits; else HexDigitsPtr = HexDigitsUpper; zptr = (sptr = String) + sizeof(String)-1; if (fw < 0) fw = 32; while (fw-- > 0) { if (sptr >= zptr) break; *sptr++ = HexDigitsPtr[(*(uchar*)cptr >> 4) & 0x0f]; if (sptr >= zptr) break; *sptr++ = HexDigitsPtr[*(uchar*)cptr & 0x0f]; cptr++; } *sptr = '\0'; sptr = String; } fw = -1; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'&I')) { /**************************************/ /* dotted-decimal/IPv6-hex IP address */ /**************************************/ fptr += 2; if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } LastNumericValue = *(ULONGPTR)VectorPtr++; } else LastNumericValue = *VectorPtr++; /* 'LastNumericValue' can be a pointer or an integer */ sptr = TcpIpAddressToString (LastNumericValue, fw); fw = -1; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'&E')) { /*****************/ /* the next line */ /*****************/ fptr += 2; if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } sptr = *(char*)*VectorPtr++; } else sptr = (char*)*VectorPtr++; if (sptr) { for (zptr = sptr; *zptr && *zptr != '\r' && *zptr != '\n'; zptr++); /* use field-width to control how much of the string is output */ fw = zptr - sptr; if (DEBUG_FAOSAK) fprintf (stdout, "!&L |%*.*s|\n", fw, fw, sptr); status = sys$fao (&LengthStringFaoDsc, 0, &StringDsc, fw, sptr); sptr = String; } else sptr = "{0}(null)"; fw = -1; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'&M') || SAME2(fptr,'&m')) { /******************/ /* status message */ /******************/ if (SAME2(fptr,'&M')) SysGetMsgFlags = 15; else SysGetMsgFlags = 1; fptr += 2; if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } VectorValue = *(ULONGPTR)VectorPtr++; } else VectorValue = *VectorPtr++; if (DEBUG_FAOSAK) fprintf (stdout, "!%%M %d\n", VectorValue); status = sys$getmsg (VectorValue, &Length, &StringDsc, SysGetMsgFlags, 0); if (VMSnok (status)) sptr = "*ERROR* sys$getmsg()"; else { (sptr = String)[Length] = '\0'; if (SysGetMsgFlags == 15) sptr++; } if (DEBUG_FAOSAK) fprintf (stdout, "sys$getmsg() |%s|\n", String); /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'SQ')) { /*******************/ /* signed quadword */ /*******************/ /* Note that the "!SQ" directive is being used, for VMS 6.2 the "!UQ" && "!UJ" loop infinitely for signed values. Also note the indirect character MUST be used (i.e. "!@SQ"). Also that VAX doesn't support it - so a kludge is employed. */ fptr += 2; if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } LastNumericValue = 0; } else { /* must be indirect */ #if WATCH_MOD fprintf (stdout, "%%HTTPD-W-FAO, SS$_BADPARAM %s:%d\n \\%s\\\n", FI_LI, fptr-2); #endif /* WATCH_MOD */ status = SS$_BADPARAM; break; } if (DEBUG_FAOSAK) fprintf (stdout, "!SQ %d\n", LastNumericValue); #ifdef __VAX /* kludge, apparently ok all the way up to 53 bits */ dValue = (double)((ULONGPTR)(*VectorPtr))[0]; dValue += (double)((ULONGPTR)(*VectorPtr))[1] * 4294967296.0; VectorPtr++; status = SS$_NORMAL; if (fw > 0 && !CommaNumber) sprintf (String, "%*.0f", fw, dValue); else sprintf (String, "%.0f", dValue); #else /* Alpha or ia64 */ if (fw > 0 && !CommaNumber) status = sys$fao (&SQWidthFaoDsc, 0, &StringDsc, fw, *VectorPtr++); else status = sys$fao (&SQFaoDsc, 0, &StringDsc, *VectorPtr++); #endif /* ifdef __VAX */ if (VMSnok (status)) break; if (!CommaNumber) fw = -1; sptr = String; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'SL')) { /*******************/ /* signed longword */ /*******************/ fptr += 2; if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } LastNumericValue = *(ULONGPTR)VectorPtr++; } else LastNumericValue = *VectorPtr++; if (DEBUG_FAOSAK) fprintf (stdout, "!SL %d\n", LastNumericValue); if (fw > 0 && !CommaNumber) status = sys$fao (&SLWidthFaoDsc, 0, &StringDsc, fw, LastNumericValue); else status = sys$fao (&SLFaoDsc, 0, &StringDsc, LastNumericValue); if (VMSnok (status)) break; if (!CommaNumber) fw = -1; sptr = String; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'%D') || SAME2(fptr,'&D')) { /*************/ /* date/time */ /*************/ cptr = fptr; fptr += 2; if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } Time64Ptr = *(ULONGPTR)VectorPtr++; } else Time64Ptr = *VectorPtr++; if (DEBUG_FAOSAK) fprintf (stdout, "!%%D %d\n", Time64Ptr); if (!Time64Ptr) sys$gettim (Time64Ptr = &Time64); if (QUAD_ZERO(Time64Ptr)) sptr = "(none)"; else { status = sys$fao (&PDFaoDsc, 0, &StringDsc, Time64Ptr); if (VMSnok (status)) break; for (sptr = String; *sptr && *sptr != '-'; sptr++); /* absolute times have leading space changed to leading zero */ if (*sptr && String[0] == ' ') (sptr = String)[0] = '0'; else if (*sptr) sptr = String; else /* delta times have leading spaces absorbed */ for (sptr = String; *sptr && *sptr == ' '; sptr++); if (SAME2(cptr,'&D')) { /* massage the time a little further */ for (cptr = sptr; *cptr && *cptr != ' '; cptr++); if (*cptr) if (sptr[2] == '-') *cptr++ = ':'; else *cptr++ = '-'; while (*cptr && *cptr != ':') cptr++; if (*cptr) cptr++; while (*cptr && *cptr != ':') cptr++; *cptr = '\0'; } } /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'%I')) { /***********************/ /* numeric to ASCII ID */ /***********************/ fptr += 2; if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } VectorValue = *(ULONGPTR)VectorPtr++; } else VectorValue = *VectorPtr++; if (DEBUG_FAOSAK) fprintf (stdout, "!%%D %d\n", VectorValue); status = sys$fao (&PIFaoDsc, 0, &StringDsc, VectorValue); if (VMSnok (status)) break; sptr = String; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'%T')) { /********/ /* time */ /********/ fptr += 2; if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } Time64Ptr = *(ULONGPTR)VectorPtr++; } else Time64Ptr = *VectorPtr++; if (DEBUG_FAOSAK) fprintf (stdout, "!%%T %d\n", Time64Ptr); if (!Time64Ptr) sys$gettim (Time64Ptr = &Time64); if (QUAD_ZERO(Time64Ptr)) { sptr = "(none)"; if (fw > 0) fw = -1; } else { status = sys$fao (&PTFaoDsc, 0, &StringDsc, Time64Ptr); if (VMSnok (status)) break; sptr = String; } /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'%s') || SAME2(fptr,'%S')) { /******************/ /* add 'S' or 's' */ /******************/ if (DEBUG_FAOSAK) fprintf (stdout, "!%%%c\n", *(fptr+1)); if (LastNumericValue == 1) { fptr += 2; continue; } fptr++; fw = -1; if (*fptr++ == 's') sptr = "s"; else sptr = "S"; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'AC')) { /************************/ /* counted ASCII string */ /************************/ fptr += 2; if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } cptr = *(char*)*VectorPtr++; } else cptr = (char*)*VectorPtr++; if (!cptr) { status = SS$_ACCVIO; break; } if (DEBUG_FAOSAK) fprintf (stdout, "!AC %d |%s|\n", (uint)*cptr, cptr+1); /* get the count byte */ ch = (uint)*cptr++; while (ch) { if (bptr < bzptr) { *bptr++ = *cptr++; ch--; continue; } if (STR_DSC_IS_EXTERNAL(sdptr)) goto FaolSAKovf; STR_DSC_LEN(sdptr) = bptr - STR_DSC_PTR(sdptr); sdptr = StrDscBuffer (StrDscPtr); bptr = STR_DSC_PTR(sdptr) + STR_DSC_LEN(sdptr); bzptr = STR_DSC_PTR(sdptr) + STR_DSC_SIZE(sdptr); } /* only if required drop through to space-fill */ if (fw <= 0) continue; /* no string to copy */ sptr = NULL; } else if (SAME2(fptr,'&C')) { /* a single character */ fptr += 2; String[0] = *VectorPtr++; String[1] = '\0'; sptr = String; fw = -1; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'&P')) { /*****************/ /* mask password */ /*****************/ fptr += 2; cptr = (char*)*VectorPtr++; if (!cptr) cptr = "(null)"; zptr = (sptr = String) + sizeof(String)-1; while (*cptr && sptr < zptr) { if (cptr[0] == ':' && cptr[1] == '/' && cptr[2] == '/') break; *sptr++ = *cptr++; } if (*cptr) { if (sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr++; while (*cptr && *cptr != '/' && *cptr != '@' && sptr < zptr) *sptr++ = *cptr++; if (*cptr == '@') { if (sptr < zptr) *sptr++ = *cptr++; while (sptr > String && *sptr != ':') sptr--; if (*sptr == ':') { sptr++; while (*sptr && *sptr != '@') *sptr++ = '*'; if (*sptr) sptr++; } } while (*cptr && sptr < zptr) *sptr++ = *cptr++; } *sptr = '\0'; sptr = String; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'&U')) { /******************/ /* encode URL/URI */ /******************/ fptr += 2; cptr = (char*)*VectorPtr++; if (cptr) StringUrlEncodeURL (cptr, sptr = String, sizeof(String)); else sptr = "(null)"; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'&W')) { /*********************/ /* weekday/date/time */ /*********************/ fptr += 2; if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } Time64Ptr = *(ULONGPTR)VectorPtr++; } else Time64Ptr = *VectorPtr++; if (DEBUG_FAOSAK) fprintf (stdout, "!%%W %d\n", Time64Ptr); if (!Time64Ptr) sys$gettim (Time64Ptr = &Time64); if (QUAD_ZERO(Time64Ptr)) { sptr = "(none)"; if (fw > 0) fw = -1; } else if (VMSnok (lib$day_of_week (Time64Ptr, &DayOfWeek))) sptr = "*ERROR*1"; else if (VMSnok (sys$fao (&DayDateTimeFaoDsc, 0, &StringDsc, DayName[DayOfWeek], fw > 0 ? fw : 23, Time64Ptr))) sptr = "*ERROR*2"; else sptr = String; /* reset the field-width (only applies to date/time) */ if (fw > 0) fw = -1; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'&R')) { /************************/ /* radio button boolean */ /************************/ fptr += 2; if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } VectorValue = *(ULONGPTR)VectorPtr++; } else VectorValue = *VectorPtr++; if (DEBUG_FAOSAK) fprintf (stdout, "!&? %d\n", VectorValue); if (VectorValue) sptr = " CHECKED"; else sptr = ""; fw = -1; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'&O')) { /*************************/ /* select option boolean */ /*************************/ fptr += 2; if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } VectorValue = *(ULONGPTR)VectorPtr++; } else VectorValue = *VectorPtr++; if (DEBUG_FAOSAK) fprintf (stdout, "!&? %d\n", VectorValue); if (VectorValue) sptr = " SELECTED"; else sptr = ""; fw = -1; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'&?')) { /**********************/ /* simple conditional */ /**********************/ /* e.g. "!&?string if *vector TRUE\rstring if FALSE\r" */ fptr += 2; if (IndirectValue) { if (!*VectorPtr) { status = SS$_ACCVIO; break; } VectorValue = *(ULONGPTR)VectorPtr++; } else VectorValue = *VectorPtr++; if (DEBUG_FAOSAK) fprintf (stdout, "!&? %d\n", VectorValue); if (!VectorValue) { /* if false skip over the true string */ while (*fptr && *fptr != '\r') fptr++; if (*fptr == '\r') fptr++; if (!*fptr) break; } while (*fptr && *fptr != '\r' && fw && ofw) { if (bptr < bzptr) { *bptr++ = *fptr++; fw--; ofw--; continue; } if (STR_DSC_IS_EXTERNAL(sdptr)) goto FaolSAKovf; STR_DSC_LEN(sdptr) = bptr - STR_DSC_PTR(sdptr); sdptr = StrDscBuffer (StrDscPtr); bptr = STR_DSC_PTR(sdptr) + STR_DSC_LEN(sdptr); bzptr = STR_DSC_PTR(sdptr) + STR_DSC_SIZE(sdptr); } if (*fptr != '\r') { /* field-width has expired before the return */ while (*fptr && *fptr != '\r') fptr++; } if (*fptr == '\r') fptr++; if (VectorValue) { /* was true, skip over the false string */ while (*fptr && *fptr != '\r') fptr++; if (*fptr == '\r') fptr++; } /* only if required drop through to space-fill */ if (fw <= 0) continue; } else if (*fptr == '+') { /**************************/ /* discard next parameter */ /**************************/ fptr++; VectorValue = *VectorPtr++; if (DEBUG_FAOSAK) fprintf (stdout, "!+ %d\n", VectorValue); } else if (*fptr == '-') { /****************************/ /* reuse previous parameter */ /****************************/ fptr++; VectorValue = *--VectorPtr; if (DEBUG_FAOSAK) fprintf (stdout, "!- %d\n", VectorValue); } else if (*fptr == '!') { /****************************/ /* an escaped '!' character */ /****************************/ if (DEBUG_FAOSAK) fprintf (stdout, "!!\n"); fptr++; sptr = "!"; /* drop through to buffer the string pointed to by 'sptr' */ } else if (*fptr == '*') { /*****************************/ /* multiple '*'+1 characters */ /*****************************/ if (DEBUG_FAOSAK) fprintf (stdout, "!*\n"); if (!*++fptr) fw = 0; while (fw > 0 && ofw) { if (bptr < bzptr) { *bptr++ = *fptr; fw--; ofw--; continue; } if (STR_DSC_IS_EXTERNAL(sdptr)) goto FaolSAKovf; STR_DSC_LEN(sdptr) = bptr - STR_DSC_PTR(sdptr); sdptr = StrDscBuffer (StrDscPtr); bptr = STR_DSC_PTR(sdptr) + STR_DSC_LEN(sdptr); bzptr = STR_DSC_PTR(sdptr) + STR_DSC_SIZE(sdptr); } if (*fptr) fptr++; continue; } else if (*fptr == '<') { /****************************/ /* begin output-field-width */ /****************************/ if (DEBUG_FAOSAK) fprintf (stdout, "!<\n"); fptr++; if (ofw >= 0) { /* can't nest these little blighters! */ #if WATCH_MOD fprintf (stdout, "%%HTTPD-W-FAO, SS$_BADPARAM %s:%d\n \\%s\\\n", FI_LI, fptr-1); #endif /* WATCH_MOD */ status = SS$_BADPARAM; break; } ofw = fw; continue; } else if (*fptr == '>') { /**************************/ /* end output-field-width */ /**************************/ if (DEBUG_FAOSAK) fprintf (stdout, "!>\n"); fptr++; if (ofw < 0) { /* didn't notice a leading "!<" */ #if WATCH_MOD fprintf (stdout, "%%HTTPD-W-FAO, SS$_BADPARAM %s:%d\n \\%s\\\n", FI_LI, fptr-1); #endif /* WATCH_MOD */ status = SS$_BADPARAM; break; } /* get (any) current outstanding that needs space-filling */ fw = ofw; /* reset output-field-width to infinite */ ofw = -1; /* only if required drop through to space-fill */ if (fw <= 0) continue; } /***********************/ /* mainly for WATCHing */ /***********************/ else if (SAME2(fptr,'&A')) { /* an "address" as &x00000000 */ fptr += 2; LastNumericValue = *VectorPtr++; if (!LastNumericValue) cptr = "null"; else if (!(cptr = WatchFunction (LastNumericValue))) cptr = "?"; status = sys$fao (&WatchingAddressFaoDsc, 0, &StringDsc, LastNumericValue, cptr); if (VMSnok (status)) break; fw = -1; sptr = String; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'&B')) { /* a boolean */ fptr += 2; LastNumericValue = *VectorPtr++; if (LastNumericValue) sptr = "TRUE"; else sptr = "FALSE"; fw = -1; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'&F')) { /* a "function" address as &00000000 */ fptr += 2; LastNumericValue = *VectorPtr++; status = sys$fao (&WatchingFunctionFaoDsc, 0, &StringDsc, LastNumericValue); if (VMSnok (status)) break; fw = -1; sptr = String; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'&S')) { /* VMS status value as %X00000000 */ fptr += 2; LastNumericValue = *VectorPtr++; if (LastNumericValue && VMSnok (LastNumericValue)) { /* for non-success status provide corresponding message */ char Msg [256]; $DESCRIPTOR (MsgDsc, Msg); SysGetMsgFlags = 15; status = sys$getmsg (LastNumericValue, &Length, &MsgDsc, SysGetMsgFlags, 0); if (VMSnok (status)) strcpy (Msg, "*ERROR* sys$getmsg()"); else Msg[Length] = '\0'; status = sys$fao (&VmsStatusMsgFaoDsc, 0, &StringDsc, LastNumericValue, Msg); } else status = sys$fao (&VmsStatusFaoDsc, 0, &StringDsc, LastNumericValue); if (VMSnok (status)) break; fw = -1; sptr = String; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'&X')) { /* hexadecimal as 0x00000000 */ fptr += 2; LastNumericValue = *VectorPtr++; status = sys$fao (&HexValueFaoDsc, 0, &StringDsc, LastNumericValue); if (VMSnok (status)) break; fw = -1; sptr = String; /* drop through to buffer the string pointed to by 'sptr' */ } else if (SAME2(fptr,'&Z')) { /* a null-terminated string as {length}string */ fptr += 2; sptr = (char*)*VectorPtr++; if (sptr) { status = sys$fao (&LengthStringFaoDsc, 0, &StringDsc, strlen(sptr), sptr); sptr = String; } else sptr = "{0}(null)"; if (DEBUG_FAOSAK) fprintf (stdout, "!AZ |%s|\n", sptr); /* drop through to buffer the string pointed to by 'sptr' */ } else { #if WATCH_MOD fprintf (stdout, "%%HTTPD-W-FAO, SS$_BADPARAM %s:%d\n \\%s\\\n", FI_LI, fptr); #endif /* WATCH_MOD */ status = SS$_BADPARAM; break; } /*********************************/ /* copy a string into the buffer */ /*********************************/ if (DEBUG_FAOSAK) fprintf (stdout, "sptr |%s|\n", sptr ? sptr : "(null)"); if (sptr) { if (CommaNumber) { /****************/ /* comma number */ /****************/ Length = strlen(sptr); while (fw && ofw && Length + ((Length-1) / 3) < fw) { if (bptr < bzptr) { *bptr++ = ' '; fw--; ofw--; continue; } if (STR_DSC_IS_EXTERNAL(sdptr)) goto FaolSAKovf; STR_DSC_LEN(sdptr) = bptr - STR_DSC_PTR(sdptr); sdptr = StrDscBuffer (StrDscPtr); bptr = STR_DSC_PTR(sdptr) + STR_DSC_LEN(sdptr); bzptr = STR_DSC_PTR(sdptr) + STR_DSC_SIZE(sdptr); } if (((Length-1) / 3) >= 1) { if (!(CommaCount = Length % 3)) CommaCount = 3; while (*sptr && fw && ofw) { if (bptr < bzptr) { if (!CommaCount--) { *bptr++ = ','; fw--; ofw--; CommaCount = 3; } else { *bptr++ = *sptr++; fw--; ofw--; } continue; } if (STR_DSC_IS_EXTERNAL(sdptr)) goto FaolSAKovf; STR_DSC_LEN(sdptr) = bptr - STR_DSC_PTR(sdptr); sdptr = StrDscBuffer (StrDscPtr); bptr = STR_DSC_PTR(sdptr) + STR_DSC_LEN(sdptr); bzptr = STR_DSC_PTR(sdptr) + STR_DSC_SIZE(sdptr); } /* only if required drop through to space-fill */ if (fw <= 0) continue; sptr = NULL; } /* drop thru to copy 3 or less digits into the buffer */ FileSpec = HtmlEscape = QuoteIfSpace = SpaceThenString = TrailingNewlineString = UnderlineIfSpace = UrlEncode = Utf8Encode = false; } } if (sptr) { if (FileSpec) { /**********************/ /* file specification */ /**********************/ switch (FileOds) { case 0 : case MAPURL_PATH_ODS_2 : if (!ForceUpperCase) ForceLowerCase = true; /* just leave 'sptr' the way it is! */ break; #ifdef ODS_EXTENDED case MAPURL_PATH_ODS_5 : MapOds5VmsToUrl (FileString, sptr, sizeof(FileString), true); sptr = FileString; break; #endif /* ODS_EXTENDED */ case MAPURL_PATH_ODS_ADS : case MAPURL_PATH_ODS_SMB : MapOdsAdsVmsToUrl (FileString, sptr, sizeof(FileString), true); sptr = FileString; break; case MAPURL_PATH_ODS_PWK : MapOdsPwkVmsToUrl (FileString, sptr, sizeof(FileString), true); sptr = FileString; break; case MAPURL_PATH_ODS_SRI : MapOdsSriVmsToUrl (FileString, sptr, sizeof(FileString), true); sptr = FileString; break; default : return (SS$_BADPARAM); } } if (QuoteIfSpace || SpaceThenString || UnderlineIfSpace) { for (cptr = sptr; *cptr && *cptr != ' '; cptr++); if (*cptr || SpaceThenString) { if (QuoteIfSpace) cptr = "\""; else if (SpaceThenString) cptr = " "; else cptr = ""; while (*cptr) { if (bptr < bzptr) { *bptr++ = *cptr++; continue; } if (STR_DSC_IS_EXTERNAL(sdptr)) goto FaolSAKovf; STR_DSC_LEN(sdptr) = bptr - STR_DSC_PTR(sdptr); sdptr = StrDscBuffer (StrDscPtr); bptr = STR_DSC_PTR(sdptr) + STR_DSC_LEN(sdptr); bzptr = STR_DSC_PTR(sdptr) + STR_DSC_SIZE(sdptr); } } else QuoteIfSpace = UnderlineIfSpace = false; } if (HtmlEscape) { /**********************/ /* HTML-escape string */ /**********************/ while (*sptr && fw && ofw) { fw--; ofw--; if (Utf8Encode && (*sptr & 0x80)) { cptr = String; cptr[0] = ((*sptr & 0xc0) >> 6) | 0xc0; cptr[1] = (*sptr++ & 0x3f) | 0x80; cptr[2] = '\0'; } else { if (ForceLowerCase) ch = TOLO(*sptr++); else if (ForceUpperCase) ch = TOUP(*sptr++); else ch = *sptr++; switch (ch) { case '<' : cptr = "<"; break; case '>' : cptr = ">"; break; case '&' : cptr = "&"; break; case '\"' : if (QuoteIfSpace) cptr = "\\""; else cptr = """; break; default : /* insert this character as-is */ *(cptr = OneChar) = ch; } } /* add the character(s) */ while (*cptr) { if (bptr < bzptr) { *bptr++ = *cptr++; continue; } if (STR_DSC_IS_EXTERNAL(sdptr)) goto FaolSAKovf; STR_DSC_LEN(sdptr) = bptr - STR_DSC_PTR(sdptr); sdptr = StrDscBuffer (StrDscPtr); bptr = STR_DSC_PTR(sdptr) + STR_DSC_LEN(sdptr); bzptr = STR_DSC_PTR(sdptr) + STR_DSC_SIZE(sdptr); } } } else if (UrlEncode) { /*********************/ /* URL-encode string */ /*********************/ while (*sptr && fw && ofw) { fw--; ofw--; cptr = NULL; if (Utf8Encode && (*sptr & 0x80)) { cptr = String; ch = ((*sptr & 0xc0) >> 6) | 0xc0; cptr[0]= FaoUrlEncodeTable[(uchar)ch][0]; cptr[1]= FaoUrlEncodeTable[(uchar)ch][1]; cptr[2]= FaoUrlEncodeTable[(uchar)ch][2]; ch = (*sptr++ & 0x3f) | 0x80; cptr[3]= FaoUrlEncodeTable[(uchar)ch][0]; cptr[4]= FaoUrlEncodeTable[(uchar)ch][1]; cptr[5]= FaoUrlEncodeTable[(uchar)ch][2]; cptr[6] = '\0'; } else { if (ForceLowerCase) ch = TOLO(*sptr++); else if (ForceUpperCase) ch = TOUP(*sptr++); else ch = *sptr++; if (QuoteIfSpace && ch == '\"') cptr = "%5c%22"; else cptr = FaoUrlEncodeTable[(uchar)ch]; } while (*cptr) { if (bptr < bzptr) { *bptr++ = *cptr++; continue; } if (STR_DSC_IS_EXTERNAL(sdptr)) goto FaolSAKovf; STR_DSC_LEN(sdptr) = bptr - STR_DSC_PTR(sdptr); sdptr = StrDscBuffer (StrDscPtr); bptr = STR_DSC_PTR(sdptr) + STR_DSC_LEN(sdptr); bzptr = STR_DSC_PTR(sdptr) + STR_DSC_SIZE(sdptr); } } } else { /******************/ /* literal string */ /******************/ ch = '\0'; while (*sptr && fw && ofw) { if (bptr < bzptr) { if (ch) { *bptr++ = ch; ch = '\0'; sptr++; } else if (QuoteIfSpace && *sptr == '\"') { *bptr++ = '\\'; ch = *sptr; } else if (PeriodNonPrintable && !isprint(*sptr)) { *bptr++ = '.'; sptr++; } else if (SlashNonPrintable && !isprint(*sptr)) { switch (*sptr) { case '\a' : ch = 'a'; break; case '\b' : ch = 'b'; break; case '\f' : ch = 'f'; break; case '\n' : ch = 'n'; break; case '\r' : ch = 'r'; break; case '\t' : ch = 't'; break; case '\v' : ch = 'v'; break; default : ch = '.'; } *bptr++ = '\\'; } else if (Utf8Encode && (*sptr & 0x80)) { *bptr++ = ((*sptr & 0xc0) >> 6) | 0xc0; if (bptr < bzptr) *bptr++ = (*sptr++ & 0x3f) | 0x80; else ch = (*sptr & 0x3f) | 0x80; } else if (ForceLowerCase) *bptr++ = TOLO(*sptr++); else if (ForceUpperCase) *bptr++ = TOUP(*sptr++); else *bptr++ = *sptr++; fw--; ofw--; continue; } if (STR_DSC_IS_EXTERNAL(sdptr)) goto FaolSAKovf; STR_DSC_LEN(sdptr) = bptr - STR_DSC_PTR(sdptr); sdptr = StrDscBuffer (StrDscPtr); bptr = STR_DSC_PTR(sdptr) + STR_DSC_LEN(sdptr); bzptr = STR_DSC_PTR(sdptr) + STR_DSC_SIZE(sdptr); } } if (QuoteIfSpace || TrailingNewlineString || UnderlineIfSpace) { if (QuoteIfSpace) cptr = "\""; else if (TrailingNewlineString) cptr = "\n"; else cptr = ""; while (*cptr) { if (bptr < bzptr) { *bptr++ = *cptr++; continue; } if (STR_DSC_IS_EXTERNAL(sdptr)) goto FaolSAKovf; STR_DSC_LEN(sdptr) = bptr - STR_DSC_PTR(sdptr); sdptr = StrDscBuffer (StrDscPtr); bptr = STR_DSC_PTR(sdptr) + STR_DSC_LEN(sdptr); bzptr = STR_DSC_PTR(sdptr) + STR_DSC_SIZE(sdptr); } } } /***************************************************/ /* if positive field-width, right-fill with spaces */ /***************************************************/ if (fw <= 0) continue; while (fw > 0 && ofw) { if (bptr < bzptr) { *bptr++ = ' '; fw--; ofw--; continue; } if (STR_DSC_IS_EXTERNAL(sdptr)) goto FaolSAKovf; STR_DSC_LEN(sdptr) = bptr - STR_DSC_PTR(sdptr); sdptr = StrDscBuffer (StrDscPtr); bptr = STR_DSC_PTR(sdptr) + STR_DSC_LEN(sdptr); bzptr = STR_DSC_PTR(sdptr) + STR_DSC_SIZE(sdptr); } } /*********************/ /* end format string */ /*********************/ if (VMSnok (status)) break; /* if there are still pointers on the format stack */ if (DEBUG_FAOSAK) fprintf (stdout, "idx: %d\n", idx); if (!idx) break; idx--; } /*****************************/ /* end nested format strings */ /*****************************/ if (VMSnok (status)) { if (WATCHMOD (rqptr, WATCH_MOD_FAO)) WatchThis (WATCHITM(rqptr), WATCH_MOD_FAO, "ERROR !AZ", LastFptr); /* if an error was detected append from the offending directive */ fptr = LastFptr; while (*fptr) { if (bptr < bzptr) { *bptr++ = *fptr++; fw--; ofw--; continue; } if (STR_DSC_IS_EXTERNAL(sdptr)) goto FaolSAKovf; STR_DSC_LEN(sdptr) = bptr - STR_DSC_PTR(sdptr); sdptr = StrDscBuffer (StrDscPtr); bptr = STR_DSC_PTR(sdptr) + STR_DSC_LEN(sdptr); bzptr = STR_DSC_PTR(sdptr) + STR_DSC_SIZE(sdptr); } } *bptr = '\0'; STR_DSC_LEN(sdptr) = bptr - STR_DSC_PTR(sdptr); if (LengthPtr) { if (STR_DSC_LEN(sdptr) > 65535) { /* indicate that we can't represent what we've written to buffer */ *LengthPtr = 65535; status = SS$_BUFFEROVF; } else *LengthPtr = (ushort)STR_DSC_LEN(sdptr); } if (Watch.Category) { /* if we're WATCHing anything at all */ if (VMSnok (status)) { /* and an error is being reported */ int cnt; WatchThis (WATCHITM(rqptr), WATCH_MOD_FAO, "!&S", status); WatchDataFormatted ("!&Z\n", FormatString); for (cnt = 0; cnt <= idx; cnt++) WatchDataFormatted ("!UL !&Z\n", cnt, FormatStack[cnt]); } } return (status); FaolSAKovf: { /* writing to storage and it's overflowed */ *bptr = '\0'; STR_DSC_LEN(sdptr) = bptr - STR_DSC_PTR(sdptr); if (LengthPtr) *LengthPtr = (ushort)STR_DSC_LEN(sdptr); return (SS$_BUFFEROVF); } } /*****************************************************************************/ /* Sanity bugchecks if |VectorPtr| has overflowed the |FaoVector| space. Should be called immedaitely before the FaoToBuffer() call for writes that have the potential to become buggy because of code modifications (and yes, I got caught, and yes it cost me a few hours). */ FaoCheck ( int SizeOfFaoVector, ulong *FaoVectorPtr, ulong *VectorPtr, char *SourceModuleName, int SourceLineNumber ) { char String [64]; /*********/ /* begin */ /*********/ if ((uchar*)VectorPtr - (uchar*)FaoVectorPtr <= SizeOfFaoVector) return; sprintf (String, "storage: %d vector: %d (longwords)", SizeOfFaoVector/ sizeof(ulong), ((uchar*)VectorPtr - (uchar*)FaoVectorPtr) / sizeof(ulong)); ErrorExitVmsStatus (SS$_BUGCHECK, String, SourceModuleName, SourceLineNumber); } /*****************************************************************************/ /* This is a variable-argument wrapper for FaolSAK(), see that for greater detail. This function just gets all the arguments from the call stack and puts them into a longword vector that FaolSAK() can use. 'LengthPtr' provides storage for returning the length of the generated string. The resultant string is always null-terminated (unless an error), hence the 'BufferSize' can always only store 'BufferSize'-1 characters. 'LengthPtr' provides storage for the number of characters minus the null-termination. If 'BufferSize' is passed containing -1 then the 'BufferPtr' specifies a STR_DSC structure. */ int FaoToBuffer ( char *BufferPtr, int BufferSize, ushort *LengthPtr, char *FormatString, ... ) { int status, argcnt; ulong *vecptr; ulong FaoVector [128]; va_list argptr; /*********/ /* begin */ /*********/ va_count (argcnt); if WATCH_MODULE(WATCH_MOD_FAO) WatchThis (WATCHALL, WATCH_MOD_FAO, "FaoToBuffer() !UL !8XL !SL", argcnt, BufferPtr, BufferSize); if (argcnt > 128+4) return (SS$_OVRMAXARG); vecptr = FaoVector; va_start (argptr, FormatString); for (argcnt -= 4; argcnt; argcnt--) *vecptr++ = va_arg (argptr, ulong); va_end (argptr); status = FaolSAK (NULL, BufferPtr, BufferSize, LengthPtr, FormatString, &FaoVector); if WATCH_MODULE(WATCH_MOD_FAO) WatchThis (WATCHALL, WATCH_MOD_FAO, "!&S", status); return (status); } /*****************************************************************************/ /* This is a variable-argument wrapper for FaolSAK(), see that for greater detail. This function just gets all the arguments from the call stack and puts them into a longword vector that FaolSAK() can use. Output is to the client network write descriptor. */ int FaoToNet ( REQUEST_STRUCT *rqptr, char *FormatString, ... ) { int status, argcnt; ulong *vecptr; ulong FaoVector [128]; va_list argptr; /*********/ /* begin */ /*********/ va_count (argcnt); if (WATCHMOD (rqptr, WATCH_MOD_FAO)) WatchThis (WATCHITM(rqptr), WATCH_MOD_FAO, "FaoToNet() !UL", argcnt); if (argcnt > 128+2) return (SS$_OVRMAXARG); vecptr = FaoVector; va_start (argptr, FormatString); for (argcnt -= 2; argcnt; argcnt--) *vecptr++ = va_arg (argptr, ulong); va_end (argptr); status = FaolSAK (rqptr, NULL, 0, NULL, FormatString, &FaoVector); if (WATCHMOD (rqptr, WATCH_MOD_FAO)) WatchThis (WATCHITM(rqptr), WATCH_MOD_FAO, "!&S", status); return (status); } /****************************************************************************/ /* A fixed-size, internal buffer of 986 bytes maximum is used and the result output as an OPCOM message. */ int FaoToOpcom ( char *FormatString, ... ) { static $DESCRIPTOR (OpcomDsc, ""); int status, argcnt; ushort Length; ulong *vecptr; ulong FaoVector [128+2]; va_list argptr; struct { ulong TargetType; ulong RequestId; char MsgText [986+1]; } OpcomMsg; /*********/ /* begin */ /*********/ va_count (argcnt); if WATCH_MODULE(WATCH_MOD_FAO) WatchThis (WATCHALL, WATCH_MOD_FAO, "FaoToOpcom() !UL !AZ", argcnt, FormatString); if (argcnt > 128+1) return (SS$_OVRMAXARG); vecptr = FaoVector; *vecptr++ = HttpdProcess.PrcNam; *vecptr++ = FormatString; va_start (argptr, FormatString); for (argcnt -= 1; argcnt; argcnt--) *vecptr++ = va_arg (argptr, ulong); va_end (argptr); status = FaolSAK (NULL, OpcomMsg.MsgText, sizeof(OpcomMsg.MsgText), &Length, "Process !AZ reports\r\n!&@", &FaoVector); if (VMSnok (status)) FaoErrorNoticed (status, NULL, FI_LI); if (DEBUG_FAOSAK) fprintf (stdout, "%d |%s|\n", Length, OpcomMsg.MsgText); OpcomMsg.TargetType = OPC$_RQ_RQST + ((OpcomTarget & 0xffffff) << 8); OpcomMsg.RequestId = 0; OpcomDsc.dsc$a_pointer = &OpcomMsg; OpcomDsc.dsc$w_length = Length + 8; status = sys$sndopr (&OpcomDsc, 0); if (VMSnok (status)) FaoErrorNoticed (status, NULL, FI_LI); return (status); } /****************************************************************************/ /* A fixed-size, (fairly large) internal buffer is used and the result output to the stream. */ int FaoToStdout ( char *FormatString, ... ) { int status, argcnt; ushort Length; ulong *vecptr; ulong FaoVector [128]; char Buffer [32767]; va_list argptr; /*********/ /* begin */ /*********/ va_count (argcnt); if WATCH_MODULE(WATCH_MOD_FAO) WatchThis (WATCHALL, WATCH_MOD_FAO, "FaoToStdout() !UL !AZ", argcnt, FormatString); if (argcnt > 128+1) return (SS$_OVRMAXARG); vecptr = FaoVector; va_start (argptr, FormatString); for (argcnt -= 1; argcnt; argcnt--) *vecptr++ = va_arg (argptr, ulong); va_end (argptr); status = FaolSAK (NULL, Buffer, sizeof(Buffer), &Length, FormatString, &FaoVector); if (VMSnok (status)) FaoErrorNoticed (status, NULL, FI_LI); fputs (Buffer, stdout); return (status); } /*****************************************************************************/ /* ErrorNoticed() uses FaoToStdout() and FaoToOpcom(). Let's not potentially compound our problems by trying to use ErrorNoticed() from within those functions! */ FaoErrorNoticed ( int StatusValue, char *Explanation, char *SourceModuleName, int SourceLineNumber ) { static char CurrentTime64 [24], GetMsgBuffer [256]; static $DESCRIPTOR (CurrentTime64Dsc, CurrentTime64); static $DESCRIPTOR (GetMsgDsc, GetMsgBuffer); static $DESCRIPTOR (TimeFaoDsc, "!20%D\0"); ushort ShortLength; /*********/ /* begin */ /*********/ if WATCH_MODULE(WATCH_MOD_FAO) WatchThis (WATCHALL, WATCH_MOD_FAO, "FaoErrorNoticed()"); sys$fao (&TimeFaoDsc, NULL, &CurrentTime64Dsc, 0); sys$getmsg (StatusValue, &ShortLength, &GetMsgDsc, 0, 0); GetMsgBuffer[ShortLength] = '\0'; fprintf (stdout, "%%HTTPD-W-NOTICED, %s, %s:%d, %s, %%X%08.08X\n-%s\n", CurrentTime64, SourceModuleName, SourceLineNumber, Explanation, StatusValue, GetMsgBuffer+1); } /****************************************************************************/