Subversion Repositories pentevo

Rev

Blame | Last modification | View Log | Download | RSS feed | ?url?

  1. /* nlmessages.c */
  2. /*****************************************************************************/
  3. /* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
  4. /*                                                                           */
  5. /* AS-Portierung                                                             */
  6. /*                                                                           */
  7. /* Einlesen und Verwalten von Meldungs-Strings                               */
  8. /*                                                                           */
  9. /* Historie: 13. 8.1997 Grundsteinlegung                                     */
  10. /*           17. 8.1997 Verallgemeinerung auf mehrere Kataloge               */
  11. /*                                                                           */
  12. /*****************************************************************************/
  13.  
  14. #include "stdinc.h"
  15. #include <string.h>
  16. #include "strutil.h"
  17.  
  18. #include "be_le.h"
  19. #include "bpemu.h"
  20. #include "nls.h"
  21. #include "chardefs.h"
  22.  
  23. #include "nlmessages.h"
  24.  
  25. /*****************************************************************************/
  26.  
  27. static const char *IdentString = "AS Message Catalog - not readable\n\032\004";
  28.  
  29. static const char *EOpenMsg = "cannot open msg file %s";
  30. static const char *ERdMsg = "cannot read from msg file";
  31. static const char *EIndMsg = "string table index error";
  32.  
  33. static TMsgCat DefaultCatalog =
  34. {
  35.   NULL, NULL, 0
  36. };
  37.  
  38. typedef struct
  39. {
  40.   Byte *p_data;
  41.   size_t data_length, rd_ptr;
  42.   Boolean data_allocated;
  43. } as_raw_msg_struct_t;
  44.  
  45. /*!------------------------------------------------------------------------
  46.  * \fn     free_raw(as_raw_msg_struct_t *p_raw)
  47.  * \brief  free raw message struct
  48.  * \param  p_raw struct to free
  49.  * ------------------------------------------------------------------------ */
  50.  
  51. static void free_raw(as_raw_msg_struct_t *p_raw)
  52. {
  53.   if (p_raw)
  54.   {
  55.     if (p_raw->p_data && p_raw->data_allocated)
  56.       free(p_raw->p_data);
  57.     p_raw->p_data = NULL;
  58.     p_raw->data_allocated = False;
  59.     p_raw->data_length = p_raw->rd_ptr = 0;
  60.     free(p_raw);
  61.   }
  62. }
  63.  
  64. /*!------------------------------------------------------------------------
  65.  * \fn     seek_raw(as_raw_msg_struct_t *p_raw, size_t offset, int whence)
  66.  * \brief  seek in raw message struct
  67.  * \param  p_raw struct to seek in
  68.  * \param  offset offset from ref position
  69.  * \param  whence ref position (like for fseek())
  70.  * \return 0 for OK, -1 for error
  71.  * ------------------------------------------------------------------------ */
  72.  
  73. static int seek_raw(as_raw_msg_struct_t *p_raw, size_t offset, int whence)
  74. {
  75.   size_t new_pos;
  76.  
  77.   switch (whence)
  78.   {
  79.     case SEEK_SET: new_pos = offset; break;
  80.     case SEEK_CUR: new_pos = p_raw->rd_ptr + offset; break;
  81.     case SEEK_END: new_pos = p_raw->data_length + offset; break;
  82.     default:
  83.       return -1;
  84.   }
  85.  
  86.   if (new_pos <= p_raw->data_length)
  87.   {
  88.     p_raw->rd_ptr = new_pos;
  89.     return 0;
  90.   }
  91.   else
  92.     return -1;
  93. }
  94.  
  95. /*!------------------------------------------------------------------------
  96.  * \fn     read_raw(void *p_buf, size_t memb_size, size_t n_memb, as_raw_msg_struct_t *p_raw)
  97.  * \brief  read data from raw message block
  98.  * \param  p_buf where to read
  99.  * \param  memb_size size of elements to read
  100.  * \param  n_memb # of elements to read
  101.  * \param  p_raw struct to read from
  102.  * \return actual # of elements read
  103.  * ------------------------------------------------------------------------ */
  104.  
  105. static size_t read_raw(void *p_buf, size_t memb_size, size_t n_memb, as_raw_msg_struct_t *p_raw)
  106. {
  107.   size_t avl = ((p_raw->data_length > p_raw->rd_ptr)
  108.               ? (p_raw->data_length - p_raw->rd_ptr) : 0) / memb_size,
  109.              to_copy;
  110.  
  111.   if (avl < n_memb)
  112.     n_memb = avl;
  113.   to_copy = n_memb * memb_size;
  114.   memcpy(p_buf, &p_raw->p_data[p_raw->rd_ptr], to_copy);
  115.   p_raw->rd_ptr += to_copy;
  116.   return n_memb;
  117. }
  118.  
  119. /*!------------------------------------------------------------------------
  120.  * \fn     read_raw_4(as_raw_msg_struct_t *p_raw, LongInt *p_dest)
  121.  * \brief  read 32 bit integer from raw message block
  122.  * \param  p_raw struct to read from
  123.  * \param  p_dest where to read
  124.  * \return True if success
  125.  * ------------------------------------------------------------------------ */
  126.  
  127. static Boolean read_raw_4(as_raw_msg_struct_t *p_raw, LongInt *p_dest)
  128. {
  129.   if (read_raw(p_dest, 4, 1, p_raw) != 1)
  130.     return False;
  131.   if (HostBigEndian)
  132.     DSwap(p_dest, 4);
  133.   return True;
  134. }
  135.  
  136. /*!------------------------------------------------------------------------
  137.  * \fn     create_raw_file(const char *p_file_name)
  138.  * \brief  create raw message struct from file
  139.  * \param  p_file_name file path & name
  140.  * \return * to struct or NULL if failed
  141.  * ------------------------------------------------------------------------ */
  142.  
  143. static as_raw_msg_struct_t *create_raw_file(const char *p_file_name)
  144. {
  145.   FILE *p_file = NULL;
  146.   as_raw_msg_struct_t *p_result = NULL;
  147.   Boolean success = False;
  148.  
  149.   p_file = fopen(p_file_name, OPENRDMODE);
  150.   if (!p_file)
  151.     goto func_exit;
  152.   p_result = (as_raw_msg_struct_t*)calloc(1, sizeof(*p_result));
  153.   if (!p_result)
  154.     goto func_exit;
  155.   fseek(p_file, 0, SEEK_END);
  156.   p_result->data_length = ftell(p_file);
  157.   if (p_result->data_length >= 65535u)
  158.     p_result->data_length = 65535u;
  159.   fseek(p_file, 0, SEEK_SET);
  160.  
  161.   p_result->p_data = (Byte*)malloc(p_result->data_length);
  162.   if (p_result->data_length && !p_result->p_data)
  163.     goto func_exit;
  164.   p_result->data_allocated = True;
  165.   if (fread(p_result->p_data, 1, p_result->data_length, p_file) != p_result->data_length)
  166.     goto func_exit;
  167.  
  168.   success = True;
  169.  
  170. func_exit:
  171.   if (p_file)
  172.     fclose(p_file);
  173.   if (p_result && !success)
  174.   {
  175.     free_raw(p_result);
  176.     p_result = NULL;
  177.   }
  178.   return p_result;
  179. }
  180.  
  181. /*****************************************************************************/
  182.  
  183. static void error(const char *Msg)
  184. {
  185.   fprintf(stderr, "message catalog handling: %s - program terminated\n", Msg);
  186.   exit(255);
  187. }
  188.  
  189. char *catgetmessage(PMsgCat Catalog, int Num)
  190. {
  191.   if ((Num >= 0) && (Num < Catalog->MsgCount))
  192.     return Catalog->MsgBlock + Catalog->StrPosis[Num];
  193.   else
  194.   {
  195.     static char *umess = NULL;
  196.  
  197.     if (!umess)
  198.       umess = (char*)malloc(sizeof(char) * STRINGSIZE);
  199.     as_snprintf(umess, STRINGSIZE, "catgetmessage: message number %d does not exist", Num);
  200.     return umess;
  201.   }
  202. }
  203.  
  204. char *getmessage(int Num)
  205. {
  206.   return catgetmessage(&DefaultCatalog, Num);
  207. }
  208.  
  209. /*!------------------------------------------------------------------------
  210.  * \fn     check_header_raw(as_raw_msg_struct_t *p_raw, LongInt msg_id1, LongInt msg_id2)
  211.  * \brief  check raw msg file for correct header
  212.  * \param  p_raw file to check
  213.  * \param  msg_id1, msg_id2 expected identifiers
  214.  * \return True if file OK
  215.  * ------------------------------------------------------------------------ */
  216.  
  217. static Boolean check_header_raw(as_raw_msg_struct_t *p_raw, LongInt msg_id1, LongInt msg_id2)
  218. {
  219.   String line;
  220.   size_t ident_len = strlen(IdentString);
  221.   LongInt r_id1, r_id2;
  222.  
  223.   return !seek_raw(p_raw, 0, SEEK_SET)
  224.       && (ident_len == read_raw(line, 1, ident_len, p_raw))
  225.       && !memcmp(line, IdentString, ident_len)
  226.       && read_raw_4(p_raw, &r_id1)
  227.       && (r_id1 == msg_id1)
  228.       && read_raw_4(p_raw, &r_id2)
  229.       && (r_id2 == msg_id2);
  230. }
  231.  
  232. /*!------------------------------------------------------------------------
  233.  * \fn     open_raw_file_and_check(const char *name, LongInt MsgId1, LongInt MsgId2)
  234.  * \brief  open raw msg file from file and check header
  235.  * \param  name file's name & path
  236.  * \param  MsgId1, MsgId2, expected identifiers
  237.  * \return * to raw file or NULL if failed
  238.  * ------------------------------------------------------------------------ */
  239.  
  240. as_raw_msg_struct_t *open_raw_file_and_check(const char *name, LongInt MsgId1, LongInt MsgId2)
  241. {
  242.   as_raw_msg_struct_t *p_ret = NULL;
  243.  
  244.   p_ret = create_raw_file(name);
  245.   if (!p_ret)
  246.     return NULL;
  247.   if (!check_header_raw(p_ret, MsgId1, MsgId2))
  248.   {
  249.     fprintf(stderr, "message catalog handling: warning: %s has invalid format or is out of date\n", name);
  250.     free_raw(p_ret); p_ret = NULL;
  251.   }
  252.   return p_ret;
  253. }
  254.  
  255. /*!------------------------------------------------------------------------
  256.  * \fn     parse_raw_msg_file(PMsgCat p_cat, as_raw_msg_struct_t *p_msg_raw)
  257.  * \brief  parse raw message catalog into runtime structure
  258.  * \param  p_cat where to store result
  259.  * \param  p_raw raw catalog to parse
  260.  * ------------------------------------------------------------------------ */
  261.  
  262. static void parse_raw_file(PMsgCat p_cat, as_raw_msg_struct_t *p_msg_raw)
  263. {
  264.   LongInt DefPos = -1, MomPos, DefLength = 0, MomLength, z, StrStart, CtryCnt, Ctrys[100];
  265.   unsigned StrCap;
  266.   Boolean fi, Gotcha;
  267.   const tNLSCharacterTab *CharacterTab;
  268.   char *pStr, str[2048], *ptr;
  269.   tNLSCharacter Ch;
  270.   const char *lcstring;
  271.   Word CountryCode;
  272.  
  273.   /* get reference for finding out which language set to use */
  274.  
  275.   CountryCode = NLS_GetCountryCode();
  276.   lcstring = getenv("LC_MESSAGES");
  277.   if (!lcstring)
  278.     lcstring = getenv("LC_ALL");
  279.   if (!lcstring)
  280.     lcstring = getenv("LANG");
  281.   if (!lcstring)
  282.     lcstring = "";
  283.  
  284.   Gotcha = False;
  285.   do
  286.   {
  287.     ptr = str;
  288.     do
  289.     {
  290.       if (read_raw(ptr, 1, 1, p_msg_raw) != 1)
  291.         error(ERdMsg);
  292.       fi = (*ptr == '\0');
  293.       if (!fi) ptr++;
  294.     }
  295.     while (!fi);
  296.     if (*str != '\0')
  297.     {
  298.       if (!read_raw_4(p_msg_raw, &MomLength))
  299.         error(ERdMsg);
  300.       if (!read_raw_4(p_msg_raw, &CtryCnt))
  301.         error(ERdMsg);
  302.       for (z = 0; z < CtryCnt; z++)
  303.         if (!read_raw_4(p_msg_raw, Ctrys + z))
  304.           error(ERdMsg);
  305.       if (!read_raw_4(p_msg_raw, &MomPos))
  306.         error(ERdMsg);
  307.       if (DefPos == -1)
  308.       {
  309.         DefPos = MomPos;
  310.         DefLength = MomLength;
  311.       }
  312.       for (z = 0; z < CtryCnt; z++)
  313.         if (Ctrys[z] == CountryCode)
  314.           Gotcha = True;
  315.       if (!Gotcha)
  316.         Gotcha = !as_strncasecmp(lcstring, str, strlen(str));
  317.     }
  318.   }
  319.   while ((*str != '\0') && (!Gotcha));
  320.   if (*str == '\0')
  321.   {
  322.     MomPos = DefPos;
  323.     MomLength = DefLength;
  324.   }
  325.  
  326.   /* read pointer table */
  327.  
  328.   seek_raw(p_msg_raw, MomPos, SEEK_SET);
  329.   if (!read_raw_4(p_msg_raw, &StrStart))
  330.     error(ERdMsg);
  331.   p_cat->MsgCount = (StrStart - MomPos) >> 2;
  332.   p_cat->StrPosis = (LongInt *) malloc(sizeof(LongInt)*p_cat->MsgCount);
  333.   p_cat->StrPosis[0] = 0;
  334.   if ((int)read_raw(p_cat->StrPosis + 1, 4, p_cat->MsgCount - 1, p_msg_raw) + 1 != p_cat->MsgCount)
  335.     error(ERdMsg);
  336.   if (HostBigEndian)
  337.     DSwap(p_cat->StrPosis + 1, (p_cat->MsgCount - 1) << 2);
  338.   for (z = 1; z < p_cat->MsgCount; z++)
  339.   {
  340.     p_cat->StrPosis[z] -= StrStart;
  341.     if ((p_cat->StrPosis[z] < 0) || (p_cat->StrPosis[z] >= MomLength))
  342.       error(EIndMsg);
  343.   }
  344.  
  345.   /* read string table */
  346.  
  347.   seek_raw(p_msg_raw, StrStart, SEEK_SET);
  348.   p_cat->MsgBlock = (char *) malloc(MomLength);
  349.   if ((int)read_raw(p_cat->MsgBlock, 1, MomLength, p_msg_raw) != MomLength)
  350.     error(ERdMsg);
  351.  
  352.   /* character replacement according to runtime codepage */
  353.  
  354.   CharacterTab = GetCharacterTab(NLS_GetCodepage());
  355.   for (z = 1; z < p_cat->MsgCount; z++)
  356.   {
  357.     pStr = p_cat->MsgBlock + p_cat->StrPosis[z];
  358.     StrCap = strlen(pStr);
  359.     for (Ch = (tNLSCharacter)0; Ch < eCH_cnt; Ch++)
  360.       strreplace(pStr, NLS_HtmlCharacterTab[Ch], (*CharacterTab)[Ch], 2, StrCap);
  361.   }
  362. }
  363.  
  364. #define MSGPATHNAME "AS_MSGPATH"
  365.  
  366. /*!------------------------------------------------------------------------
  367.  * \fn     msg_catalog_open_file(PMsgCat p_catalog, const char *p_file_name, const char *p_exe_path, LongInt msg_id1, LongInt msg_id2)
  368.  * \brief  open message catalog from file
  369.  * \param  p_catalog catalog to fill
  370.  * \param  p_file_name catalog's file name
  371.  * \param  p_exe_path executable's path
  372.  * \param  file_msg_id1, file_msg_id1 file magic values to expect
  373.  * ------------------------------------------------------------------------ */
  374.  
  375. void msg_catalog_open_file(PMsgCat p_catalog, const char *p_file_name, const char *p_exe_path, LongInt msg_id1, LongInt msg_id2)
  376. {
  377.   as_raw_msg_struct_t *p_msg_raw;
  378.   String str;
  379.   char *ptr;
  380.   const char *pSep;
  381.  
  382.   /* find first valid message file: current directory has prio 1: */
  383.  
  384.   p_msg_raw = open_raw_file_and_check(p_file_name, msg_id1, msg_id2);
  385.  
  386.   /* if executable path (argv[0]) is given and contains more than just the
  387.      plain name, try its path with next-highest prio: */
  388.  
  389.   if (!p_msg_raw && p_exe_path && *p_exe_path)
  390.   {
  391. #ifdef __CYGWIN32__
  392.     for (ptr = (char*)p_exe_path; *ptr != '\0'; ptr++)
  393.       if (*ptr == '/') *ptr = '\\';
  394. #endif
  395.     pSep = strrchr(p_exe_path, PATHSEP);
  396.     if (pSep)
  397.     {
  398.       int path_len = pSep - p_exe_path;
  399.  
  400.       as_snprintf(str, sizeof(str), "%*.*s%s%s", path_len, path_len, p_exe_path, SPATHSEP, p_file_name);
  401.       p_msg_raw = open_raw_file_and_check(str, msg_id1, msg_id2);
  402.     }
  403.   }
  404.  
  405.   if (!p_msg_raw)
  406.   {
  407.     /* AS_MSGPATH has priority over.. */
  408.  
  409.     ptr = getenv(MSGPATHNAME);
  410.     if (ptr)
  411.     {
  412.       as_snprintf(str, sizeof(str), "%s%c%s", ptr, PATHSEP, p_file_name);
  413.       p_msg_raw = open_raw_file_and_check(str, msg_id1, msg_id2);
  414.     }
  415.  
  416.     /* ...PATH: */
  417.  
  418.     else
  419.     {
  420.       ptr = getenv("PATH");
  421.       if (ptr)
  422.       {
  423.         String Dest;
  424.         int Result;
  425.  
  426. #ifdef __CYGWIN32__
  427.         DeCygWinDirList(pCopy);
  428. #endif
  429.         Result = FSearch(Dest, sizeof(Dest), p_file_name, NULL, ptr);
  430.         p_msg_raw = Result ? NULL : open_raw_file_and_check(Dest, msg_id1, msg_id2);
  431.  
  432.         /* If we were found via PATH (no slashes in argv[0]/p_exe_path), look up this
  433.            path and replace bin/ with lib/ for 'companion path': */
  434.  
  435.         if (!p_msg_raw && !strrchr(p_exe_path, PATHSEP) && !FSearch(Dest, sizeof(Dest), p_exe_path, NULL, ptr))
  436.         {
  437.           char *pSep2;
  438.  
  439.           strreplace(Dest, SPATHSEP "bin", SPATHSEP "lib", 0, sizeof(Dest));
  440.           pSep2 = strrchr(Dest, PATHSEP);
  441.           if (pSep2)
  442.             *pSep2 = '\0';
  443.           strmaxcat(Dest, SPATHSEP, sizeof(Dest));
  444.           strmaxcat(Dest, p_file_name, sizeof(Dest));
  445.           p_msg_raw = open_raw_file_and_check(Dest, msg_id1, msg_id2);
  446.         }
  447.       }
  448.     }
  449.   }
  450.  
  451. #ifdef LIBDIR
  452.   if (!p_msg_raw)
  453.   {
  454.     as_snprintf(str, sizeof(str), "%s%c%s", LIBDIR, PATHSEP, p_file_name);
  455.     p_msg_raw = open_raw_file_and_check(str, msg_id1, msg_id2);
  456.   }
  457. #endif
  458.  
  459.   if (!p_msg_raw)
  460.   {
  461.     as_snprintf(str, sizeof(str), EOpenMsg, p_file_name);
  462.     error(str);
  463.   }
  464.  
  465.   parse_raw_file(p_catalog, p_msg_raw);
  466.   free_raw(p_msg_raw);
  467. }
  468.  
  469. /*!------------------------------------------------------------------------
  470.  * \fn     msg_catalog_open_buffer(PMsgCat p_catalog, const unsigned char *p_buffer, size_t buffer_size, LongInt msg_id1, LongInt msg_id2)
  471.  * \brief  open message catalog from file
  472.  * \param  p_catalog catalog to fill
  473.  * \param  p_buffer buffer containing catalog
  474.  * \param  buffer_size buffer's size
  475.  * \param  msg_id1, msg_id1 file magic values to expect
  476.  * ------------------------------------------------------------------------ */
  477.  
  478. void msg_catalog_open_buffer(PMsgCat p_catalog, const unsigned char *p_buffer, size_t buffer_size, LongInt msg_id1, LongInt msg_id2)
  479. {
  480.   as_raw_msg_struct_t *p_raw = (as_raw_msg_struct_t*)calloc(1, sizeof(*p_raw));
  481.  
  482.   p_raw->p_data = (Byte*)p_buffer;
  483.   p_raw->data_allocated = False;
  484.   p_raw->data_length = buffer_size;
  485.   p_raw->rd_ptr = 0;
  486.  
  487.   if (!check_header_raw(p_raw, msg_id1, msg_id2))
  488.   {
  489.     fprintf(stderr, "message catalog handling: error: catalog'y buffer has invalid format or is out of date\n");
  490.     exit(255);
  491.   }
  492.  
  493.   parse_raw_file(p_catalog, p_raw);
  494.   free_raw(p_raw);
  495. }
  496.  
  497. /*!------------------------------------------------------------------------
  498.  * \fn     nlmessages_init_file(const char *p_file_name, char *p_exe_path, LongInt msg_id1, LongInt msg_id2)
  499.  * \brief  module initialization, load default catalog from file
  500.  * \param  p_file_name catalog's file name
  501.  * \param  p_exe_path executable's path
  502.  * \param  msg_id1, msg_id2 catalog file magic to expect
  503.  * ------------------------------------------------------------------------ */
  504.  
  505. void nlmessages_init_file(const char *p_file_name, char *p_exe_path, LongInt msg_id1, LongInt msg_id2)
  506. {
  507.   msg_catalog_open_file(&DefaultCatalog, p_file_name, p_exe_path, msg_id1, msg_id2);
  508. }
  509.  
  510. /*!------------------------------------------------------------------------
  511.  * \fn     nlmessages_init_buffer(const unsigned char *p_buffer, size_t buffer_size, LongInt msg_id1, LongInt msg_id2)
  512.  * \brief  module initialization, load default catalog from buffer
  513.  * \param  p_buffer buffer containing catalog
  514.  * \param  buffer_size buffer's size
  515.  * \param  msg_id1, msg_id2 catalog file magic to expect
  516.  * ------------------------------------------------------------------------ */
  517.  
  518. void nlmessages_init_buffer(const unsigned char *p_buffer, size_t buffer_size, LongInt msg_id1, LongInt msg_id2)
  519. {
  520.   msg_catalog_open_buffer(&DefaultCatalog, p_buffer, buffer_size, msg_id1, msg_id2);
  521. }
  522.