/*****************************************************************************/ /* msg.c Provide a site-configurable message database. This is particularly directed towards non-English language sites. No attempt has been made to completely internationalize the server, only messages the average client is likely to be exposed to are currently provided. The only way of reloading the database is to restart the server. This is not of great concern for once customized the messages should remain completely stable between releases. When multiple languages are configured the message language processing may be observed using the WATCH response-header item. MULTIPLE LANGUAGE SPECIFICATIONS -------------------------------- Multiple language messages can be specified in two ways: 1) within the one file 2) in multiple files specified by a multivalued logical name Within The One File ~~~~~~~~~~~~~~~~~~~ Language availability is specified through the use of [Language] directives. These must be numbered from 1 to the count of those supplied. The highest numbered language must have the complete set of messages for this is the fallback when obtaining any message (this would normally be 'en'). The [Language] may be specified as a comma-separated list of equivalent or similar specifications, which during request processing will be matched against a client specified list of accepted-languages one at a time in specified order. A wildcard may be specified which matches all fitting the template. In this manner a single language can be used also to match minor variants or language specification synonyms. [Version] 9.0 [Language] 1 es,es-ES [Language] 2 de,de-* [Language] 3 en In the above (rather contrived) example a client request with Accept-Language: es-ES,de;q=0.6,en;q=0.3 would have language 1 selected, a client with Accept-Language: de-ch,es;q=0.6,en;q=0.3 language 2 selected, with Accept-Language: pt-br,de;q=0.6,en;q=0.3 also language 2 selected, with Accept-Language: pt language 3 (the default) selected, etc. Note that the messages for each language must use the *first* language specification provided in the [Language] list. In the example above all messages for language 1 would be introduced using 'es', for language 2 with 'de' and for language 3 with 'en'. Multiple Files - Multivalued Logical Name ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ With this approach a logical name containing multiple file names is defined (more commonly described as a logical search list). The final file specified must contain the full message set. Files specified prior to this, can contain as many or as few of the full set as is desired. A [Language] number does not need to be specified as they are processed in the order the logical name specifies them in. Other language file directives are required. The following is an example of a logical name providing the same three languages in the examples above. $ DEFINE /SYSTEM WASD_CONFIG_MSG WASD_ROOT:[LOCAL]HTTPD$MSG_ES.CONF, - WASD_ROOT:[LOCAL]HTTPD$MSG_DE.CONF, - WASD_ROOT:[LOCAL]HTTPD$MSG.CONF The file contents would be as follows (very contrived examples :-) # HTTPD$MSG_ES.CONF [Version] 9.0 [Language] 0 es,es-ES [auth] es 01 Habla Espanol es 02 Habla Inglesi [dir] es 03 Habla Espanol es 04 Habla Inglesi # HTTPD$MSG_DE.CONF [Version] 9.0 [Language] 0 de,de-* [auth] de 01 Sprechen Sie Deutsches de 02 Sprechen Sie Englisch [dir] de 03 Sprechen Sie Deutsches de 04 Sprechen Sie Englisch # HTTPD$MSG.CONF [Version] 9.0 [Language] 0 en [auth] . . .(full set of messages) The major ADVANTAGE of maintaining multiple files in this way is there is no need to MERGE files when a new revision is required. Just update the version number and add any new required messages to the existing secondary file. messages to the existing ADDITIONAL [LANGUAGE] PARAMETERS -------------------------------- Two optional parameters can follow the language number and language specification synonym. The first is a character set to be associated with the message set. This is specified using the keyword 'charset=' followed by the character set specification. For example [Language] 2 charset=ISO-8859-5 The second optional parameter is a comma-separated list of host domain specifications that the message set can be associated with. If there is no "Accept-Language:" list associated with the request then this information will be used to select the message set. For example [Language] 2 charset=ISO-8859-5 hosts=cz,cz-* CURRENT MESSAGE GROUPINGS ------------------------- [auth] authentication/authorization [dir] directory listing [general] messages belonging to no particular module or category [htadmin] authentication database administration [http] http status messages [mapping] mapping rules [proxy] proxy module [put] PUT/POST module [request] request acceptance and initial parsing [script] DCL/scripting module [ssi] Server Side Includes [status] error and other status reporting [upd] update module VERSION HISTORY --------------- 06-JUL-2016 MGD MsgConfigLoadCallback() make [ismap] optional 10-APR-2016 MGD [ismap] is now obsolete but retained to not break msg files 10-JUN-2010 MGD bugfix; allow METACON_TOKEN_INCLUDE for [IncludeFile] 04-MAR-2005 MGD allow WASD_CONFIG_MSG to be a logical search list to support multiple, independent language files (also see METACON.C) 05-SEP-2003 MGD optional 'charset=' [language] parameter 15-AUG-2003 MGD where CDATA constraints make using entity impossible use a field name of hidden$lf and ^ substituted for it 04-APR-2003 MGD wildcard and comma-separated list of languages can be specified (e.g. "[Language] es-ES,es,es-*") 12-OCT-2002 MGD refine metacon reporting 31-MAR-2002 MGD MsgForNoRequest() no longer required 22-JAN-2002 MGD bugfix; MsgFor() Accept-Lang: comparison (jpp@esme.fr) 29-SEP-2001 MGD instance support 15-SEP-2001 MGD meta-config 04-AUG-2001 MGD support module WATCHing 28-FEB-2001 MGD OdsLoadTextFile(), OdsParseTextFile(), [IncludeFile] 18-JUN-2000 MGD add MsgRevise() 08-APR-2000 MGD HTTP status messages, v7.0 04-MAR-2000 MGD use FaolToBuffer(), et.al. 02-JAN-2000 MGD config file opened via ODS module 04-DEC-1999 MGD additional messages for v6.1 05-MAY-1999 MGD proxy messages, v6.0 06-JUL-1998 MGD bugfix; MsgUnload(), VmFree() of 'HostListPtr' 14-FEB-1998 MGD message file format changed from v4.4 to v5.0 25-OCT-1997 MGD changes around MsgFor() when no request structure available 09-AUG-1997 MGD initial development (HTTPd v4.4) */ /*****************************************************************************/ #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 /* VMS related header files */ #include #include #include /* application related header files */ #include "wasd.h" #define WASD_MODULE "MSG" /******************/ /* global storage */ /******************/ char MsgNotSet [] = "INTERNAL ERROR, please notify the administrator."; char ErrorMsgDupLangNumber [] = "Duplicate [language] number", ErrorMsgDupMsgNumber [] = "Duplicate message number", ErrorMsgLangDisabled [] = "[language] disabled", ErrorMsgLangName [] = "[language] name not specified", ErrorMsgLangNotFound [] = "[language] not found", ErrorMsgLangNotSpecified [] = "[language] not specified", ErrorMsgLangNumber [] = "[language] number out-of-range", ErrorMsgLangTooMany [] = "Too many [language]s", ErrorMsgMessageNumber [] = "Message number out-of-range", ErrorMsgNoGroup [] = "[group-name] has not been specified", ErrorMsgNoLangNumber [] = "No [language] number", ErrorMsgNoLangName [] = "No [language] name", ErrorMsgNoMessageNumber [] = "No message number", ErrorMsgObsoleteGroup [] = "Obsolete group", ErrorMsgTooFew [] = "Too few messages", ErrorMsgTooMany [] = "Too many messages", ErrorMsgUnknownGroup [] = "Unknown [group-name]", ErrorMsgVersion [] = "Message file [version] mismatch", ErrorMsgVersionNotChecked [] = "No [version] for checking"; MSG_META MsgMeta; MSG_META *MsgMetaPtr; /********************/ /* external storage */ /********************/ #ifdef DBUG extern BOOL Debug; #else #define Debug 0 #endif extern BOOL HttpdServerStartup; extern int ToLowerCase[], ToUpperCase[]; extern char ErrorSanityCheck[], SoftwareID[]; extern CONFIG_STRUCT Config; extern META_CONFIG *MetaGlobalMsgPtr; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* */ int MsgConfigLoad (META_CONFIG **MetaConPtrPtr) { int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MSG)) WatchThis (WATCHALL, WATCH_MOD_MSG, "MsgConfigLoad()"); status = MetaConLoad (MetaConPtrPtr, v10orPrev10(CONFIG_MSG_FILE_NAME,-1), &MsgConfigLoadCallback, false, false); if (*MetaConPtrPtr == MetaGlobalMsgPtr) { /* server startup */ MetaConStartupReport (MetaGlobalMsgPtr, "MSG"); if (VMSnok (status)) exit (status); } return (status); } /*****************************************************************************/ /* Called by MetaConUnload() to free resources allocated during msg configuration. */ MsgConfigUnload (META_CONFIG *mcptr) { int Count, LanguageCount; MSG_META *mmptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MSG)) WatchThis (WATCHALL, WATCH_MOD_MSG, "MsgConfigUnload()"); mmptr = mcptr->MsgMetaPtr; for (LanguageCount = 1; LanguageCount <= mmptr->LanguageCount; LanguageCount++) { if (mmptr->Msgs[LanguageCount]) { for (Count = 0; Count <= MSG_RANGE; Count++) { /* the '\b' backspace is a sentinal, will never occur in a message */ if (mmptr->Msgs[LanguageCount]->TextPtr[Count] && !SAME2(mmptr->Msgs[LanguageCount]->TextPtr[Count],'\b[')) { /* remember that MsgCreate() returns allocation plus one! */ VmFree (mmptr->Msgs[LanguageCount]->TextPtr[Count]-1, FI_LI); } } if (mmptr->Msgs[LanguageCount]->CharsetPtr) VmFree (mmptr->Msgs[LanguageCount]->CharsetPtr, FI_LI); if (mmptr->Msgs[LanguageCount]->HostListPtr) VmFree (mmptr->Msgs[LanguageCount]->HostListPtr, FI_LI); VmFree (mmptr->Msgs[LanguageCount], FI_LI); } } /* clean it out completely, just in case it's to be reused! */ memset (mmptr, 0, sizeof(MSG_META)); } /*****************************************************************************/ /* */ BOOL MsgConfigLoadCallback (META_CONFIG *mcptr) { static BOOL IgnoreIsMap; static char ProblemOverflow [] = "Storage overflow", ProblemUsage [] = "Cannot use during message configuration"; int status, ConfigFileIndex, Count, MessageNumber; char *cptr, *sptr, *zptr; char LanguageName [128], StringBuffer [512]; MSG_META *mmptr; METACON_LINE *mclptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MSG)) { WatchThis (WATCHALL, WATCH_MOD_MSG, "MsgConfigLoadCallback() !&F !&X", &MsgConfigLoadCallback, mcptr); if (WATCH_MODULE(WATCH_MOD__DETAIL)) { mclptr = mcptr->ParsePtr; WatchDataFormatted ("!&X !UL !UL !UL !UL !&X !&Z !&Z\n", mclptr, mclptr->Size, mclptr->Token, mclptr->Number, mclptr->Length, mclptr->LineDataPtr, mclptr->TextPtr, mclptr->InlineTextPtr); } } if (mcptr->LnmIndex >= mcptr->LnmCount) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); /* get a pointer to the current "line" */ mclptr = mcptr->ParsePtr; /* if this is during server startup then set the global msg pointer */ if (HttpdServerStartup) mmptr = mcptr->MsgMetaPtr = MsgMetaPtr = &MsgMeta; else /* if a report then conjure one from the singularity */ if (!mcptr->MsgMetaPtr) mmptr = mcptr->MsgMetaPtr = VmGet (sizeof(MSG_META)); else /* not the first time through */ mmptr = mcptr->MsgMetaPtr; if (mclptr->Token == METACON_TOKEN_PRE) { /******************/ /* pre-initialize */ /******************/ mmptr->CheckedLanguages = mmptr->VersionChecked = false; mmptr->LanguageCount = 0; mmptr->MessageBase = -1; mmptr->MessageCount = 0; return (true); } if (mclptr->Token == METACON_TOKEN_POST) { /****************/ /* post-process */ /****************/ if (mcptr->LnmIndex+1 == mcptr->LnmCount) { if (mmptr->MessageCount < MSG_TOTAL) { if (HttpdServerStartup) ErrorExitVmsStatus (0, ErrorMsgTooFew, FI_LI); else MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgTooFew); } else if (mmptr->MessageCount > MSG_TOTAL) { /* technically, this shouldn't be possible! */ if (HttpdServerStartup) ErrorExitVmsStatus (0, ErrorMsgTooMany, FI_LI); else MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgTooMany); } mmptr->LanguageDefault = mmptr->LanguageCount; mmptr->SearchListCount = mcptr->LnmCount; } strzcpy (mmptr->Msgs[mmptr->LanguageNumber]->FileName, mcptr->LoadReport.FileName, sizeof(mmptr->Msgs[mmptr->LanguageNumber]->FileName)); return (true); } if (mclptr->Token == METACON_TOKEN_INCLUDE) return (true); if (mclptr->Token == METACON_TOKEN_VERSION) return (true); if (mclptr->Token != METACON_TOKEN_TEXT) { /* only interested in directive text */ MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemUsage); return (false); } /***********/ /* process */ /***********/ /* buffer the text associated with the current "line" */ zptr = (sptr = StringBuffer) + sizeof(StringBuffer); cptr = mclptr->TextPtr; while (*cptr && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemOverflow); return (false); } if (sptr > StringBuffer) { sptr--; while (sptr > StringBuffer && ISLWS(*sptr)) sptr--; sptr++; } *sptr = '\0'; cptr = StringBuffer; if (*cptr == '[') { if (strsame (cptr, "[version]", 9)) { /*************/ /* [version] */ /*************/ cptr += 9; while (*cptr && ISLWS(*cptr)) cptr++; if (!strsame (cptr, MSG_VERSION, -1)) { if (HttpdServerStartup) ErrorExitVmsStatus (0, ErrorMsgVersion, FI_LI); else { MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgVersion); return (false); } } mmptr->VersionChecked = true; return (true); } if (strsame (cptr, "[language]", 10)) { /**************/ /* [language] */ /**************/ /* allow for empty [Language] generated by configuration revise */ for (sptr = cptr+10; *sptr && !isalnum(*sptr); sptr++); if (!*sptr) return (true); /* step over the [language] and any trailing white-space */ cptr += 10; while (*cptr && ISLWS(*cptr)) cptr++; /* allow a dangling "charset="/"hosts=" for on-line configuration */ if (strsame (cptr, "charset=", 8) || strsame (cptr, "hosts=", 6)) return (true); if (mcptr->LnmCount > 1) mmptr->LanguageCount = mcptr->LnmIndex+1; else mmptr->LanguageCount++; if (WATCH_MODULE(WATCH_MOD_MSG)) WatchThis (WATCHALL, WATCH_MOD_MSG, "LANG COUNT !UL", mmptr->LanguageCount); /* check if the number of languages specified exceeds limits */ if (mmptr->LanguageCount > MAX_LANGUAGES) { if (HttpdServerStartup) ErrorExitVmsStatus (0, ErrorMsgLangTooMany, FI_LI); else { MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgLangTooMany); return (false); } } if (mcptr->LnmCount > 1) { /* with a language list it's from the index of the search list */ mmptr->LanguageNumber = mcptr->LnmIndex+1; } else { if (!isdigit(*cptr)) { /* no language number (order) has been specified */ if (HttpdServerStartup) ErrorExitVmsStatus (0, ErrorMsgNoLangNumber, FI_LI); else { MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgNoLangNumber); return (false); } } /* language number (order) */ mmptr->LanguageNumber = atol(cptr); } /* check if the number of languages specified exceeds limits */ if (mmptr->LanguageNumber < 0 || mmptr->LanguageNumber > MAX_LANGUAGES) { if (HttpdServerStartup) ErrorExitVmsStatus (0, ErrorMsgLangNumber, FI_LI); else { MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgLangNumber); return (false); } } for (Count = 1; Count < mmptr->LanguageCount; Count++) { if (!mmptr->Msgs[Count]) continue; if (mmptr->Msgs[Count]->LanguageNumber == mmptr->LanguageNumber) { if (HttpdServerStartup) ErrorExitVmsStatus (0, ErrorMsgDupLangNumber, FI_LI); else { MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgDupLangNumber); return (false); } } } /* allocate memory for this new language */ mmptr->Msgs[mmptr->LanguageNumber] = (MSG_STRUCT*)VmGet(sizeof(MSG_STRUCT)); /* set all the messages in the language to NULL */ for (Count = 0; Count <= MSG_RANGE; Count++) mmptr->Msgs[mmptr->LanguageNumber]->TextPtr[Count] = NULL; /* set the language number */ mmptr->Msgs[mmptr->LanguageNumber]->LanguageNumber = mmptr->LanguageNumber; /* get language name (start by skipping over the languge number) */ while (*cptr && isdigit(*cptr)) cptr++; while (*cptr && ISLWS(*cptr)) cptr++; if (!*cptr) { if (HttpdServerStartup) ErrorExitVmsStatus (0, ErrorMsgNoLangName, FI_LI); else { MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgNoLangName); return (false); } } zptr = (sptr = mmptr->Msgs[mmptr->LanguageNumber]->LanguageList) + sizeof(mmptr->Msgs[mmptr->LanguageNumber]->LanguageList); while (*cptr && !ISLWS(*cptr) && sptr < zptr) *sptr++ = *cptr++; if (sptr > zptr) sptr--; *sptr = '\0'; while (*cptr && ISLWS(*cptr)) cptr++; if (strsame (cptr, "charset=", 8)) { /* character set for the messages */ cptr += 8; sptr = cptr; while (*cptr && !ISLWS(*cptr)) cptr++; /* empty charsets are permitted to allow on-line revision */ if (cptr - sptr) { zptr = mmptr->Msgs[mmptr->LanguageNumber]->CharsetPtr = VmGet (cptr-sptr+1); memcpy (zptr, sptr, cptr-sptr); zptr[cptr-sptr] = '\0'; } } while (*cptr && ISLWS(*cptr)) cptr++; if (*cptr) { /* trailing host list found */ if (strsame (cptr, "hosts=", 6)) cptr += 6; sptr = cptr; while (*cptr) cptr++; /* trim trailing white-space */ if (cptr > sptr) cptr--; while (cptr > sptr && ISLWS(*cptr)) cptr--; if (cptr > sptr) cptr++; zptr = mmptr->Msgs[mmptr->LanguageNumber]->HostListPtr = VmGet (cptr-sptr+1); memcpy (zptr, sptr, cptr-sptr); zptr[cptr-sptr] = '\0'; } if (mmptr->Msgs[mmptr->LanguageNumber]->LanguageNumber == 0) MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgLangDisabled); /* get (any) primary language from (any) list */ zptr = (sptr = mmptr->Msgs[mmptr->LanguageNumber]->LanguageName) + sizeof(mmptr->Msgs[mmptr->LanguageNumber]->LanguageName)-1; for (cptr = mmptr->Msgs[mmptr->LanguageNumber]->LanguageList; *cptr && *cptr != ',' && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; if (WATCH_MODULE(WATCH_MOD_MSG)) WatchThis (WATCHALL, WATCH_MOD_MSG, "!UL !&Z !&Z !&Z !&Z", mmptr->Msgs[mmptr->LanguageNumber]->LanguageNumber, mmptr->Msgs[mmptr->LanguageNumber]->LanguageName, mmptr->Msgs[mmptr->LanguageNumber]->LanguageList, mmptr->Msgs[mmptr->LanguageNumber]->CharsetPtr, mmptr->Msgs[mmptr->LanguageNumber]->HostListPtr); return (true); } /****************************/ /* check language numbering */ /****************************/ if (!mmptr->CheckedLanguages) { mmptr->CheckedLanguages = true; for (Count = 1; Count <= mmptr->LanguageCount; Count++) { if (mmptr->Msgs[Count]->LanguageNumber < 0 || mmptr->Msgs[Count]->LanguageNumber > mmptr->LanguageCount) { if (HttpdServerStartup) ErrorExitVmsStatus (0, ErrorMsgLangNumber, FI_LI); else { MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgLangNumber); return (false); } } } } /****************/ /* [group-name] */ /****************/ IgnoreIsMap = false; /* the '\b' backspace is a sentinal, will never occur in a message */ if (strsame (cptr, "[auth]", 6)) { mmptr->MessageBase = MSG_AUTH__BASE; mmptr->MessageMax = MSG_AUTH__MAX; mmptr->Msgs[1]->TextPtr[mmptr->MessageBase] = "\b[auth]"; } else if (strsame (cptr, "[dir]", 6)) { mmptr->MessageBase = MSG_DIR__BASE; mmptr->MessageMax = MSG_DIR__MAX; mmptr->Msgs[1]->TextPtr[mmptr->MessageBase] = "\b[dir]"; } else if (strsame (cptr, "[general]", 9)) { mmptr->MessageBase = MSG_GENERAL__BASE; mmptr->MessageMax = MSG_GENERAL__MAX; mmptr->Msgs[1]->TextPtr[mmptr->MessageBase] = "\b[general]"; } else if (strsame (cptr, "[htadmin]", 9)) { mmptr->MessageBase = MSG_HTADMIN__BASE; mmptr->MessageMax = MSG_HTADMIN__MAX; mmptr->Msgs[1]->TextPtr[mmptr->MessageBase] = "\b[htadmin]"; } else if (strsame (cptr, "[http]", 6)) { mmptr->MessageBase = MSG_HTTP__BASE; mmptr->MessageMax = MSG_HTTP__MAX; mmptr->Msgs[1]->TextPtr[mmptr->MessageBase] = "\b[http]"; } else if (strsame (cptr, "[ismap]", 7)) { IgnoreIsMap = true; MetaConReport (mcptr, METACON_REPORT_INFORM, ErrorMsgObsoleteGroup); } else if (strsame (cptr, "[mapping]", 9)) { mmptr->MessageBase = MSG_MAPPING__BASE; mmptr->MessageMax = MSG_MAPPING__MAX; mmptr->Msgs[1]->TextPtr[mmptr->MessageBase] = "\b[mapping]"; } else if (strsame (cptr, "[proxy]", 7)) { mmptr->MessageBase = MSG_PROXY__BASE; mmptr->MessageMax = MSG_PROXY__MAX; mmptr->Msgs[1]->TextPtr[mmptr->MessageBase] = "\b[proxy]"; } else if (strsame (cptr, "[put]", 5)) { mmptr->MessageBase = MSG_PUT__BASE; mmptr->MessageMax = MSG_PUT__MAX; mmptr->Msgs[1]->TextPtr[mmptr->MessageBase] = "\b[put]"; } else if (strsame (cptr, "[request]", 9)) { mmptr->MessageBase = MSG_REQUEST__BASE; mmptr->MessageMax = MSG_REQUEST__MAX; mmptr->Msgs[1]->TextPtr[mmptr->MessageBase] = "\b[request]"; } else if (strsame (cptr, "[script]", 8)) { mmptr->MessageBase = MSG_SCRIPT__BASE; mmptr->MessageMax = MSG_SCRIPT__MAX; mmptr->Msgs[1]->TextPtr[mmptr->MessageBase] = "\b[script]"; } else if (strsame (cptr, "[ssi]", 5)) { mmptr->MessageBase = MSG_SSI__BASE; mmptr->MessageMax = MSG_SSI__MAX; mmptr->Msgs[1]->TextPtr[mmptr->MessageBase] = "\b[ssi]"; } else if (strsame (cptr, "[status]", 8)) { mmptr->MessageBase = MSG_STATUS__BASE; mmptr->MessageMax = MSG_STATUS__MAX; mmptr->Msgs[1]->TextPtr[mmptr->MessageBase] = "\b[status]"; } else if (strsame (cptr, "[upd]", 5)) { mmptr->MessageBase = MSG_UPD__BASE; mmptr->MessageMax = MSG_UPD__MAX; mmptr->Msgs[1]->TextPtr[mmptr->MessageBase] = "\b[upd]"; } else { mmptr->MessageBase = -1; if (HttpdServerStartup) ErrorExitVmsStatus (0, ErrorMsgUnknownGroup, FI_LI); else MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgUnknownGroup); } return (true); } /***********/ /* message */ /***********/ if (IgnoreIsMap) return (true); if (!mmptr->VersionChecked) { if (HttpdServerStartup) ErrorExitVmsStatus (0, ErrorMsgVersionNotChecked, FI_LI); else { MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgVersionNotChecked); return (false); } } if (!mmptr->LanguageCount) { if (HttpdServerStartup) ErrorExitVmsStatus (0, ErrorMsgLangNotSpecified, FI_LI); else { MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgLangNotSpecified); return (false); } } if (mmptr->MessageBase < 0) { /* before any [goup-name] has been specified */ MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgNoGroup); return (false); } if (!isalpha(*cptr)) { /* language name is not being specified at start of line */ MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgNoLangName); return (false); } zptr = (sptr = LanguageName) + sizeof(LanguageName)-1; while (*cptr && !ISLWS(*cptr) && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; /* find the index for the specified (perhaps leading) language */ for (Count = 1; Count <= mmptr->LanguageCount; Count++) { if (WATCH_MODULE(WATCH_MOD_MSG)) WatchThis (WATCHALL, WATCH_MOD_MSG, "!UL !&Z !&Z", Count, LanguageName, mmptr->Msgs[mmptr->LanguageCount]->LanguageName); if (strsame (LanguageName, mmptr->Msgs[Count]->LanguageName, -1)) { mmptr->LanguageNumber = Count; break; } } if (Count > mmptr->LanguageCount) { MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgLangNotFound); return (false); } while (*cptr && !ISLWS(*cptr)) cptr++; while (*cptr && ISLWS(*cptr)) cptr++; if (!isdigit(*cptr)) { /* no message number has been specified */ MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgNoMessageNumber); return (false); } MessageNumber = atol(cptr); if (WATCH_MODULE(WATCH_MOD_MSG)) WatchThis (WATCHALL, WATCH_MOD_MSG, "!UL", MessageNumber); /* zero disables a message or whole language */ if (!MessageNumber || !mmptr->Msgs[mmptr->LanguageNumber]->LanguageNumber) return (true); if (MessageNumber < 0 || MessageNumber > mmptr->MessageMax) { MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgMessageNumber); return (false); } MessageNumber += mmptr->MessageBase; if (WATCH_MODULE(WATCH_MOD_MSG)) WatchThis (WATCHALL, WATCH_MOD_MSG, "!UL", MessageNumber); while (*cptr && !ISLWS(*cptr)) cptr++; while (*cptr && ISLWS(*cptr)) cptr++; /* empty message (from configuration load) */ if (*cptr == '\0') return (true); /* deliberately empty message */ if (SAME2(cptr,'#\0')) cptr++; if (!mmptr->Msgs[mmptr->LanguageNumber]->TextPtr[MessageNumber]) { mmptr->Msgs[mmptr->LanguageNumber]->TextPtr[MessageNumber] = MsgCreate (cptr); if (mmptr->LanguageNumber == mmptr->LanguageCount) mmptr->MessageCount++; } else MetaConReport (mcptr, METACON_REPORT_ERROR, ErrorMsgDupMsgNumber); return (true); } /*****************************************************************************/ /* Allocate dynamic memory for the text of the message, copy it into it and return a pointer to it. The MapUrl.c module (for horrible, historical reasons) requires a leading null character before the message. Fudge this by creating a one character longer string with that leading null and returning a pointer to the first character. The MapUrl.c module will the just use the returned pointer minus one! (Neat huh? Well it works anyway!) */ char* MsgCreate (char *Text) { char *MsgPtr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MSG)) WatchThis (WATCHALL, WATCH_MOD_MSG, "MsgCreate() !&Z", Text); MsgPtr = VmGet (strlen(Text)+2); *MsgPtr = '\0'; strcpy (MsgPtr+1, Text); return (MsgPtr+1); } /*****************************************************************************/ /* Return a pointer to a character string for the message number supplied in 'Message'. If multiple languages are in use then use any supplied client list of accepted languages ("Accept-Language:" request header field) to see if the message can be supplied in a prefered language. If none supplied or if none match then check if the language has geographical information against it (a list of host/domain specifications that can be used to determine if a specific language would be more appropriate. If none of the "hit" then return the configuration-prefered language. */ char* MsgFor ( REQUEST_STRUCT *rqptr, int Message ) { BOOL LangMatch; int Count, Language; char *cptr, *lptr, *sptr, *tptr; /*********/ /* begin */ /*********/ if (WATCHMOD(rqptr, WATCH_MOD_MSG)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MSG, "MsgFor() !UL", Message); if (!rqptr) { /* no request structure, use the default language */ Language = MsgMeta.LanguageDefault; } else if (rqptr->MsgLanguage) { /* request message language has previously been set */ Language = rqptr->MsgLanguage; } else if (MsgMeta.LanguageCount == 1) { /* only one language in use, use it! */ Language = rqptr->MsgLanguage = MsgMeta.LanguageDefault; } else { if (rqptr->rqHeader.AcceptLangPtr) { /*******************************************/ /* look for the client's prefered language */ /*******************************************/ lptr = rqptr->rqHeader.AcceptLangPtr; while (*lptr) { for (Count = 1; Count <= MsgMeta.LanguageCount; Count++) { sptr = MsgMeta.Msgs[Count]->LanguageList; /* can be in the comma-separated form "es-ES,es,es-*" */ while (*sptr) { if (WATCH_MODULE(WATCH_MOD_MSG)) WatchThis (WATCHALL, WATCH_MOD_MSG, "!UL !&Z !&Z", Count, sptr, lptr); /* compare to this language specified by the client */ tptr = sptr; cptr = lptr; while (*cptr && TOUP(*cptr) == TOUP(*sptr) && *cptr != ';' && *cptr != ',' && !ISLWS(*cptr)) { cptr++; sptr++; } LangMatch = true; if (*sptr && *sptr != ',' && !ISLWS(*sptr) && *sptr != '*') LangMatch = false; else if (*sptr != '*' && *cptr && *cptr != ';' && *cptr != ',' && !ISLWS(*cptr)) LangMatch = false; if (!LangMatch) { while (*sptr && *sptr != ',') sptr++; while (*sptr && (*sptr == ',' || ISLWS(*sptr))) sptr++; continue; } /*********/ /* match */ /*********/ if (WATCHING (rqptr, WATCH_RESPONSE_HEADER)) { while (*sptr && *sptr != ',') sptr++; WatchThis (WATCHITM(rqptr), WATCH_RESPONSE_HEADER, "LANG !UL:!AZ (\"!#AZ\" in \"!AZ\")", MsgMeta.Msgs[Count]->LanguageNumber, MsgMeta.Msgs[Count]->LanguageList, sptr-tptr, tptr, rqptr->rqHeader.AcceptLangPtr); } Language = rqptr->MsgLanguage = MsgMeta.Msgs[Count]->LanguageNumber; break; } if (rqptr->MsgLanguage) break; } /* if we've found one */ if (rqptr->MsgLanguage) break; /* step to the next language (if any) in the client list */ while (*lptr && *lptr != ',' && !ISLWS(*lptr)) lptr++; while (*lptr && (*lptr == ',' || ISLWS(*lptr))) lptr++; } } if (!rqptr->MsgLanguage) { /************************/ /* look for a host list */ /************************/ for (Count = 1; Count <= MsgMeta.LanguageCount; Count++) { if (WATCH_MODULE(WATCH_MOD_MSG)) WatchThis (WATCHALL, WATCH_MOD_MSG, "!UL !&Z", Count, MsgMeta.Msgs[Count]->LanguageList); if (MsgMeta.Msgs[Count]->HostListPtr) { if (MsgInHostList (rqptr, MsgMeta.Msgs[Count]->HostListPtr)) { if (WATCHING (rqptr, WATCH_RESPONSE_HEADER)) WatchThis (WATCHITM(rqptr), WATCH_RESPONSE_HEADER, "LANG !UL:!AZ (!AZ)", MsgMeta.Msgs[Count]->LanguageNumber, MsgMeta.Msgs[Count]->LanguageList, MsgMeta.Msgs[Count]->HostListPtr); Language = rqptr->MsgLanguage = MsgMeta.Msgs[Count]->LanguageNumber; break; } } } } /* if none matching then fall back to the default language */ if (!rqptr->MsgLanguage) { if (WATCHING (rqptr, WATCH_RESPONSE_HEADER)) WatchThis (WATCHITM(rqptr), WATCH_RESPONSE_HEADER, "LANG !UL:!AZ (default)", MsgMeta.Msgs[MsgMeta.LanguageDefault]->LanguageNumber, MsgMeta.Msgs[MsgMeta.LanguageDefault]->LanguageList); Language = rqptr->MsgLanguage = MsgMeta.LanguageDefault; } } if (Message <= 0 || Message > MSG_RANGE || Language < 1 || Language > MsgMeta.LanguageCount) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); /*******************/ /* get the message */ /*******************/ /* if a message has been assigned then return it */ if (cptr = MsgMeta.Msgs[Language]->TextPtr[Message]) { if (rqptr && MsgMeta.Msgs[Language]->CharsetPtr) rqptr->rqResponse.MsgCharsetPtr = MsgMeta.Msgs[Language]->CharsetPtr; if (MsgMeta.LanguageCount == 1) return (cptr); if (WATCHING (rqptr, WATCH_RESPONSE_HEADER)) { WatchThis (WATCHITM(rqptr), WATCH_RESPONSE_HEADER, "LANG !UL:!AZ", Language, MsgMeta.Msgs[Language]->LanguageList); WatchDataFormatted ("!AZ\n", cptr); } return (cptr); } /*********************************/ /* fallback to the base language */ /*********************************/ if (cptr = MsgMeta.Msgs[MsgMeta.LanguageCount]->TextPtr[Message]) { if (rqptr && MsgMeta.Msgs[MsgMeta.LanguageCount]->CharsetPtr) rqptr->rqResponse.MsgCharsetPtr = MsgMeta.Msgs[MsgMeta.LanguageCount]->CharsetPtr; if (MsgMeta.LanguageCount == 1) return (cptr); if (WATCHING (rqptr, WATCH_RESPONSE_HEADER)) { WatchThis (WATCHITM(rqptr), WATCH_RESPONSE_HEADER, "LANG !UL:!AZ", MsgMeta.LanguageCount, MsgMeta.Msgs[MsgMeta.LanguageCount]->LanguageList); WatchDataFormatted ("!AZ\n", cptr); } return (cptr); } /**************************************/ /* no message was set for this event! */ /**************************************/ if (WATCH_MODULE(WATCH_MOD_MSG)) WatchThis (WATCHALL, WATCH_MOD_MSG, "!UL !&Z", Language, MsgNotSet); return (MsgNotSet); } /*****************************************************************************/ /* If the client's IP host name or address matches the wildcard string in 'HostList' then return true, else return false. */ BOOL MsgInHostList ( REQUEST_STRUCT *rqptr, char *HostList ) { char ch; char *cptr, *hptr, *sptr; /*********/ /* begin */ /*********/ if (WATCHMOD(rqptr, WATCH_MOD_MSG)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MSG, "MsgInHostList() !&Z !&Z !&Z", rqptr->ClientPtr->Lookup.HostName, &rqptr->ClientPtr->IpAddressString, HostList); hptr = HostList; while (*hptr) { while (*hptr && (*hptr == ',' || ISLWS(*hptr))) hptr++; sptr = hptr; while (*hptr && *hptr != ',' && !ISLWS(*hptr)) hptr++; ch = *hptr; *hptr = '\0'; /* match against host address or name */ if (isdigit(*sptr)) cptr = &rqptr->ClientPtr->IpAddressString; else cptr = rqptr->ClientPtr->Lookup.HostName; if (WATCH_MODULE(WATCH_MOD_MSG)) WatchThis (WATCHALL, WATCH_MOD_MSG, "!&Z !&Z", cptr, sptr); if (StringMatch (rqptr, cptr, sptr)) { *hptr = ch; return (true); } *hptr = ch; if (*hptr) hptr++; } return (false); } /*****************************************************************************/ /* A server administration report on the server's configuration. This function just wraps the reporting function, loading a temporary database if necessary for reporting from the configuration file. */ MsgConfigReport ( REQUEST_STRUCT *rqptr, REQUEST_AST NextTaskFunction, BOOL UseServerDatabase ) { int status; META_CONFIG *mcptr; /*********/ /* begin */ /*********/ if (WATCHMOD(rqptr, WATCH_MOD_MSG)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MSG, "MsgConfigReport() !&F !&A !UL", &MsgConfigReport, NextTaskFunction, UseServerDatabase); if (UseServerDatabase) MsgConfigReportNow (rqptr, MetaGlobalMsgPtr); else { status = MsgConfigLoad (&mcptr); if (VMSnok (status)) { /* severe error reported */ rqptr->rqResponse.HttpStatus = 403; ErrorGeneral (rqptr, mcptr->LoadReport.TextPtr, FI_LI); } else MsgConfigReportNow (rqptr, mcptr); MetaConUnload (&mcptr, NULL); } SysDclAst (NextTaskFunction, rqptr); } /*****************************************************************************/ /* A server administration report on the message database. */ MsgConfigReportNow ( REQUEST_STRUCT *rqptr, META_CONFIG *mcptr ) { static char LanguageTable [] = "

\n\ \n\ \n\
Languages
\n\ \n\ \ \ \ \ \ !&@\ \n"; static char EndLanguageTable [] = "
LangCharsetHost List
\n\
\n"; static char OneLanguageFao [] = "\ !UL\ !AZ\ !AZ\ !AZ\ !&@\ \n"; static char GroupFao [] = "

\n\ \n\ \n\
!AZ
\n\ \n"; static char MessageFao [] = "!&@\n"; char EndOfGroup [] = "
!&;AZ!&@
\n\
"; char EndOfPage [] = "\n\ \n\ \n"; BOOL MultipleLines; int status, GroupCount, Count, Language, RowCount; unsigned long FaoVector [32]; unsigned long *vecptr; char *cptr; MSG_META *mmptr; /*********/ /* begin */ /*********/ if (WATCHMOD(rqptr, WATCH_MOD_MSG)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MSG, "MsgConfigReportNow()"); /* get a pointer to the meta-config data */ mmptr = mcptr->MsgMetaPtr; AdminPageTitle (rqptr, "Server Messages"); AdminMetaConReport (rqptr, mcptr, MetaGlobalMsgPtr); AdminMetaConSource (rqptr, mcptr, MetaGlobalMsgPtr); /********************/ /* language summary */ /********************/ vecptr = FaoVector; if (mmptr->SearchListCount > 1) *vecptr++ = "File"; else *vecptr++ = ""; status = FaolToNet (rqptr, LanguageTable, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); for (Count = 1; Count <= mmptr->LanguageCount; Count++) { vecptr = FaoVector; *vecptr++ = Count; *vecptr++ = mmptr->Msgs[Count]->LanguageList; if (mmptr->Msgs[Count]->CharsetPtr) *vecptr++ = mmptr->Msgs[Count]->CharsetPtr; else *vecptr++ = "(default)"; if (mmptr->Msgs[Count]->HostListPtr) *vecptr++ = mmptr->Msgs[Count]->HostListPtr; else *vecptr++ = "(none)"; if (mmptr->SearchListCount > 1) { *vecptr++ = "!AZ"; *vecptr++ = mmptr->Msgs[Count]->FileName; } else *vecptr++ = ""; status = FaolToNet (rqptr, OneLanguageFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } status = FaolToNet (rqptr, EndLanguageTable, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); /*****************************/ /* loop through all messages */ /*****************************/ Count = GroupCount = 0; for (Count = 0; Count < MSG_RANGE; Count++) { if (mmptr->Msgs[1]->TextPtr[Count]) { /* the '\b' backspace is a sentinal, will never occur in a message */ if (SAME2(mmptr->Msgs[1]->TextPtr[Count],'\b[')) { if (GroupCount++) { status = FaolToNet (rqptr, EndOfGroup, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } vecptr = FaoVector; *vecptr++ = mmptr->Msgs[1]->TextPtr[Count]+1; status = FaolToNet (rqptr, GroupFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); Count++; mmptr->MessageCount = 0; } } RowCount = 0; for (Language = 1; Language <= mmptr->LanguageCount; Language++) if (mmptr->Msgs[Language]->TextPtr[Count]) RowCount++; for (Language = 1; Language <= mmptr->LanguageCount; Language++) { if (!mmptr->Msgs[Language]->TextPtr[Count]) continue; /* '*cptr' will not be a pointer to a null character if linefeeds */ for (cptr = mmptr->Msgs[Language]->TextPtr[Count]; *cptr && *cptr != '\n'; cptr++); if (*cptr) MultipleLines = true; else MultipleLines = false; vecptr = FaoVector; if (RowCount > 1) { *vecptr++ = "!2ZL"; *vecptr++ = RowCount; *vecptr++ = ++mmptr->MessageCount; RowCount = 0; } else if (RowCount == 1) { *vecptr++ = "!2ZL"; *vecptr++ = ++mmptr->MessageCount; } else *vecptr++ = ""; *vecptr++ = mmptr->Msgs[Language]->LanguageName; if (MultipleLines) *vecptr++ = "

!&;AZ
"; else *vecptr++ = "!&;AZ"; *vecptr++ = mmptr->Msgs[Language]->TextPtr[Count]; status = FaolToNet (rqptr, MessageFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } } /**************/ /* end report */ /**************/ status = FaolToNet (rqptr, EndOfPage, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN; ResponseHeader200 (rqptr, "text/html", &rqptr->NetWriteBufferDsc); } /*****************************************************************************/ /* A server administration menu for message configuration. This function just wraps the revision function, loading a temporary database if necessary for reporting from the message configuration file. */ MsgConfigRevise ( REQUEST_STRUCT *rqptr, REQUEST_AST NextTaskFunction, BOOL UseServerDatabase ) { int status; META_CONFIG *mcptr; /*********/ /* begin */ /*********/ if (WATCHMOD(rqptr, WATCH_MOD_MSG)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MSG, "MsgConfigRevise() !&F !&A !UL", &MsgConfigRevise, NextTaskFunction, UseServerDatabase); if (UseServerDatabase) MsgConfigReviseNow (rqptr, MetaGlobalMsgPtr); else { status = MsgConfigLoad (&mcptr); if (VMSnok (status)) { /* severe error reported */ rqptr->rqResponse.HttpStatus = 403; ErrorGeneral (rqptr, mcptr->LoadReport.TextPtr, FI_LI); } else MsgConfigReviseNow (rqptr, mcptr); MetaConUnload (&mcptr, NULL); } SysDclAst (NextTaskFunction, rqptr); } /*****************************************************************************/ /* A server administration revision for the message database. */ MsgConfigReviseNow ( REQUEST_STRUCT *rqptr, META_CONFIG *mcptr ) { static char LanguageTable [] = "

\n\ \n\ \n
Languages
\n\ \n\ \ \ \ \n\ \n"; static char EndLanguageTable [] = "\n\
OrderLanguageCharsetHost List
\n\ IMPORTANT: \n\ The primary language (that with all message texts complete \ - usually "en") must be the highest numbered (order) \ language.\n\ Failure to ensure this will render the server unable to start!!\n\
\n
\n"; static char OneLanguageFao [] = "\n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n"; static char GroupFao [] = "

\n\ \n\ \n\ \n
!AZ
\n\ \n"; static char MessageFao [] = "\n"; char EndOfGroup [] = "
!2ZL!&;AZ!&@
\n
"; int status, GroupCount, Count, Language, LineCount; unsigned long FaoVector [32]; unsigned long *vecptr; char *cptr, *sptr, *zptr, *MsgGroupPtr, *MsgTextPtr; char MultiLineBuffer [2048]; MSG_META *mmptr; /*********/ /* begin */ /*********/ if (WATCHMOD(rqptr, WATCH_MOD_MSG)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MSG, "MsgConfigReviseNow()"); /* get a pointer to the meta-config data */ mmptr = mcptr->MsgMetaPtr; AdminPageTitle (rqptr, "Server Messages"); AdminMetaConReport (rqptr, mcptr, MetaGlobalMsgPtr); AdminMetaConSource (rqptr, mcptr, MetaGlobalMsgPtr); AdminMetaConBeginUpdateForm (rqptr); /********************/ /* language summary */ /********************/ vecptr = FaoVector; *vecptr++ = MSG_VERSION; status = FaolToNet (rqptr, LanguageTable, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); for (Count = 1; Count <= mmptr->LanguageCount; Count++) { vecptr = FaoVector; *vecptr++ = "!UL"; *vecptr++ = Count; *vecptr++ = mmptr->Msgs[Count]->LanguageList; if (mmptr->Msgs[Count]->CharsetPtr) *vecptr++ = mmptr->Msgs[Count]->CharsetPtr; else *vecptr++ = ""; if (mmptr->Msgs[Count]->HostListPtr) *vecptr++ = mmptr->Msgs[Count]->HostListPtr; else *vecptr++ = ""; status = FaolToNet (rqptr, OneLanguageFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } vecptr = FaoVector; *vecptr++ = ""; *vecptr++ = ""; *vecptr++ = ""; *vecptr++ = ""; status = FaolToNet (rqptr, OneLanguageFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); status = FaolToNet (rqptr, EndLanguageTable, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); /*****************************/ /* loop through all messages */ /*****************************/ Count = GroupCount = 0; for (Count = 0; Count < MSG_RANGE; Count++) { if (mmptr->Msgs[1]->TextPtr[Count]) { /* the '\b' backspace is a sentinal, will never occur in a message */ if (SAME2(mmptr->Msgs[1]->TextPtr[Count],'\b[')) { if (GroupCount++) { status = FaolToNet (rqptr, EndOfGroup, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } vecptr = FaoVector; *vecptr++ = MsgGroupPtr = mmptr->Msgs[1]->TextPtr[Count]+1; *vecptr++ = mmptr->Msgs[1]->TextPtr[Count]+1; status = FaolToNet (rqptr, GroupFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); Count++; mmptr->MessageCount = 0; } } mmptr->MessageCount++; for (Language = 1; Language <= mmptr->LanguageCount; Language++) { if (!mmptr->Msgs[Language]->TextPtr[Count]) { MsgTextPtr = ""; /* fall back to the size of the message in the base language */ cptr = mmptr->Msgs[mmptr->LanguageCount]->TextPtr[Count]; } else MsgTextPtr = cptr = mmptr->Msgs[Language]->TextPtr[Count]; /* count number of lines in message */ LineCount = 1; for ( /*above*/ ; *cptr; cptr++) if (*cptr == '\n') LineCount++; vecptr = FaoVector; *vecptr++ = mmptr->MessageCount; *vecptr++ = mmptr->Msgs[Language]->LanguageName; if (LineCount > 1) { /* add a '\' (line continuation) character at each end-of-line */ cptr = MsgTextPtr; zptr = (sptr = MultiLineBuffer) + sizeof(MultiLineBuffer); while (*cptr && sptr < zptr) { if (*cptr == '\n' && sptr < zptr) *sptr++ = '\\'; if (sptr < zptr) *sptr++ = *cptr++; } if (sptr >= zptr) { ErrorGeneralOverflow (rqptr, FI_LI); break; } if (sptr > MultiLineBuffer) sptr--; while (sptr > MultiLineBuffer && (ISLWS(*sptr) || *sptr == '\n' || *sptr == '\\')) sptr--; if (sptr > MultiLineBuffer) sptr++; *sptr = '\0'; *vecptr++ = "\n\ "; *vecptr++ = mmptr->Msgs[Language]->LanguageList; *vecptr++ = mmptr->MessageCount; *vecptr++ = MsgGroupPtr; *vecptr++ = mmptr->Msgs[Language]->LanguageList; *vecptr++ = mmptr->MessageCount; *vecptr++ = LineCount; *vecptr++ = MultiLineBuffer; } else { *vecptr++ = "\n\ "; *vecptr++ = mmptr->Msgs[Language]->LanguageList; *vecptr++ = mmptr->MessageCount; *vecptr++ = MsgGroupPtr; *vecptr++ = mmptr->Msgs[Language]->LanguageList; *vecptr++ = mmptr->MessageCount; *vecptr++ = MsgTextPtr; } status = FaolToNet (rqptr, MessageFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } } /**************/ /* end revise */ /**************/ status = FaolToNet (rqptr, "\n\n\n", NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); AdminMetaConEndUpdateForm (rqptr); status = FaolToNet (rqptr, "\n\n", NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN; ResponseHeader200 (rqptr, "text/html", &rqptr->NetWriteBufferDsc); } /*****************************************************************************/