Ignore:
Timestamp:
Oct 13, 2001, 7:57:58 PM (24 years ago)
Author:
umoeller
Message:

Lots of updates from the last week for conditional compiles and other stuff.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/helpers/tmsgfile.c

    r102 r108  
    22/*
    33 *@@sourcefile tmsgfile.c:
    4  *      contains a "text message file" helper function,
    5  *      which works similar to DosGetMessage. See
    6  *      tmfGetMessage for details.
    7  *
    8  *      tmfGetMessage operates on plain-text ".TMF" files.
    9  *      The file is "compiled" into the file's extended
    10  *      attributes for faster access after the first access.
    11  *      When the file's contents change, the EAs are recompiled
    12  *      automatically.
    13  *
    14  *      This has the following advantages over DosGetMessage:
     4 *      replacement code for DosGetMessage for decent NLS support.
     5 *
     6 *      This code has the following advantages over DosGetMessage:
    157 *
    168 *      1)  No external utility is necessary to change message
     
    2113 *          only numerical IDs.
    2214 *
    23  *      This file is entirely new with V0.9.0.
     15 *      The .TMF file must have the following format:
     16 *
     17 *      1)  Any message must start on the beginning of a line
     18 *          and look like this:
     19 *
     20 +              <--MSGID-->: message text
     21 *
     22 *          "MSGID" can be any string and will serve as the
     23 *          message identifier for tmfGetMessage.
     24 *          Leading spaces after "-->:" will be ignored.
     25 *
     26 *      2)  The message text may span across several lines.
     27 *          If so, the line breaks are returned as plain
     28 *          newline (\n) characters by tmfGetMessage.
     29 *
     30 *      3)  Comments in the file are supported if they start
     31 *          with a semicolon (";") at the beginning of a line.
     32 *          Any following text is ignored until the next
     33 *          message ID.
     34 *
     35 *      This file was originally added V0.9.0. The original
     36 *      code was contributed by Christian Langanke, but this
     37 *      has been completely rewritten with V0.9.16 to use my
     38 *      fast string functions now. Also, tmfGetMessage requires
     39 *      tmfOpenMessageFile to be called beforehand.
    2440 *
    2541 *      Usage: All OS/2 programs.
     
    2844 *      --  tmf*   text message file functions
    2945 *
    30  *      This code uses only C standard library functions
    31  *      which are also part of the subsystem libraries.
    32  *      It does _not_ use the fopen etc. functions, but
    33  *      DosOpen etc. instead.
    34  *      You can therefore use this code with your software
    35  *      which uses the subsystem libraries only.
    36  *
    37  *      This code was kindly provided by Christian Langanke.
    38  *      Modifications April 1999 by Ulrich M”ller. Any
    39  *      substantial changes (other than comments and
    40  *      formatting) are marked with (*UM).
    41  *
    4246 *      Note: Version numbering in this file relates to XWorkplace version
    4347 *            numbering.
     
    4852
    4953/*
    50  *      Copyright (C) 1999 Christian Langanke.
    51  *      Copyright (C) 1999-2000 Ulrich M”ller.
     54 *      Copyright (C) 2001 Ulrich M”ller.
    5255 *      This file is part of the "XWorkplace helpers" source package.
    5356 *      This is free software; you can redistribute it and/or modify
     
    6871#define INCL_DOSFILEMGR
    6972#define INCL_DOSMISC
    70 #define INCL_DOSNLS
    7173#define INCL_DOSERRORS
    7274#include <os2.h>
     
    7981#include "setup.h"                      // code generation and debugging options
    8082
     83#include "helpers\dosh.h"
    8184#include "helpers\eah.h"
     85#include "helpers\standards.h"
     86#include "helpers\stringh.h"
     87#include "helpers\tree.h"
     88#include "helpers\xstring.h"
     89
    8290#include "helpers\tmsgfile.h"
    8391
     
    93101 ********************************************************************/
    94102
    95 // extended attribute used for timestamp; written to .TMF file
    96 #define EA_TIMESTAMP "TMF.FILEINFO"
    97 // extended attribute used for compiled text file; written to .TMF file
    98 #define EA_MSGTABLE  "TMF.MSGTABLE"
    99 
    100 #define NEWLINE "\n"
    101 
    102 // start-of-msgid marker
    103 #define MSG_NAME_START   "\r\n<--"
    104 // end-of-msgid marker
    105 #define MSG_NAME_END     "-->:"       // fixed: the space had to be removed, because
    106                                       // if a newline was the first character after ":",
    107                                       // we got complete garbage (crashes) (99-10-23) [umoeller]
    108 // comment marker
    109 #define MSG_COMMENT_LINE "\r\n;"
    110 
    111 // internal prototypes
    112 APIRET          CompileMsgTable(PSZ pszMessageFile, PBYTE * ppbTableData);
    113 APIRET          GetTimeStamp(PFILESTATUS3 pfs3, PSZ pszBuffer, ULONG ulBufferlen);
     103/*
     104 *@@ MSGENTRY:
     105 *
     106 *@@added V0.9.16 (2001-10-08) [umoeller]
     107 */
     108
     109typedef struct _MSGENTRY
     110{
     111    TREE    Tree;               // ulKey points to strID.psz
     112    XSTRING strID;              // message ID
     113    ULONG   ulOfsText;          // offset of start of text (in C-format buffer)
     114    ULONG   cbText;             // length of text in msg file
     115} MSGENTRY, *PMSGENTRY;
     116
     117// globals
     118static PCSZ     G_pcszStartMarker = "\n<--",
     119                G_pcszEndMarker = "-->:";
    114120
    115121/* ******************************************************************
    116122 *
    117  *   Text Message File Code
     123 *   Functions
    118124 *
    119125 ********************************************************************/
    120126
    121127/*
    122  *@@ tmfGetMessage:
    123  *      just like DosGetMessage, except that this does not
    124  *      take a simple message number for the message in the
    125  *      given message file, but a PSZ message identifier
    126  *      (pszMessageName).
    127  *
    128  *      This function automatically compiles the given .TMF
    129  *      file into the file's extended attributes, if we find
    130  *      that this is necessary, by calling CompileMsgTable.
    131  *
    132  *      Note that for compatibily, this function performs the
    133  *      same brain-dead string processing as DosGetMessage, that
    134  *      is, the return string is _not_ null-terminated, and
    135  *      trailing newline characters are _not_ cut off.
    136  *
    137  *      If you don't like this, call tmfGetMessageExt, which
    138  *      calls this function in turn.
    139  *
    140  *      <B>Returns:</B>
    141  *      --  ERROR_INVALID_PARAMETER
    142  *      --  ERROR_BUFFER_OVERFLOW
    143  *      --  ERROR_MR_MID_NOT_FOUND
    144  *      --  ERROR_MR_INV_MSGF_FORMAT
    145  *      --  ERROR_BUFFER_OVERFLOW
    146  *
    147  *      plus the error codes of DosOpen, DosRead, DosClose.
    148  */
    149 
    150 APIRET tmfGetMessage(PCHAR* pTable,    // in: pointer table for string insertion
    151                      ULONG cTable,     // in: number of pointers in *pTable
    152                      PBYTE pbBuffer,   // out: returned string
    153                      ULONG cbBuffer,   // out: sizeof(*pbBuffer)
    154                      PCSZ pszMessageName,  // in: message identifier
    155                      PCSZ pszFile,      // in: message file (.TMF extension proposed)
    156                      PULONG pcbMsg)    // out: bytes written to *pbBuffer
     128 *@@ tmfOpenMessageFile:
     129 *      opens a .TMF message file for future use
     130 *      with tmfGetMessage.
     131 *
     132 *      Use tmfCloseMessageFile to close the file
     133 *      again and free all resources. This thing
     134 *      can allocate quite a bit of memory.
     135 *
     136 *      Returns:
     137 *
     138 *      --  NO_ERROR: *ppMsgFile has received the
     139 *          new TMFMSGFILE structure.
     140 *
     141 *      --  ERROR_NOT_ENOUGH_MEMORY
     142 *
     143 *      plus any of the errors of doshLoadTextFile,
     144 *      such as ERROR_FILE_NOT_FOUND.
     145 *
     146 *@@added V0.9.16 (2001-10-08) [umoeller]
     147 */
     148
     149APIRET tmfOpenMessageFile(const char *pcszMessageFile, // in: fully q'fied .TMF file name
     150                          PTMFMSGFILE *ppMsgFile)     // out: TMFMSGFILE struct
    157151{
    158     // fixed UM 99-10-22: now initializing all vars to 0,
    159     // because ulBytesRead might be returned from this func
    160     APIRET          rc = NO_ERROR;
    161     CHAR            szMessageFile[_MAX_PATH];
    162     PBYTE           pbTableData = NULL;
    163     PSZ             pszEntry = 0;
    164     PSZ             pszEndOfEntry = 0;
    165     ULONG           ulMessagePos = 0;
    166     ULONG           ulMessageLen = 0;
    167 
    168     HFILE           hfile = NULLHANDLE;
    169     ULONG           ulFilePtr = 0;
    170     ULONG           ulAction = 0;
    171     ULONG           ulBytesToRead = 0;
    172     ULONG           ulBytesRead = 0;
    173 
    174     do
     152    APIRET arc;
     153    PSZ pszContent = NULL;
     154
     155    if (!(arc = doshLoadTextFile(pcszMessageFile,
     156                                 &pszContent)))
    175157    {
    176         // check parms
    177         if ((!pbBuffer) ||
    178             (!cbBuffer) ||
    179             (!pszMessageName) ||
    180             (!*pszMessageName) ||
    181             (!*pszFile))
     158        // file loaded:
     159        // create a TMFMSGFILE entry
     160        PTMFMSGFILE pFile;
     161        if (!(pFile = NEW(TMFMSGFILE)))
    182162        {
    183             rc = ERROR_INVALID_PARAMETER;
    184             break;
     163            arc = ERROR_NOT_ENOUGH_MEMORY;
     164            free(pszContent);
    185165        }
    186 
    187         if (cbBuffer < 2)
    188         {
    189             rc = ERROR_BUFFER_OVERFLOW;
    190             break;
    191         }
    192 
    193         // search file
    194         if ((strchr(pszFile, ':')) ||
    195             (strchr(pszFile, '\\')) ||
    196             (strchr(pszFile, '/')))
    197             // drive and/or path given: no search in path
    198             strcpy(szMessageFile, pszFile);
    199166        else
    200167        {
    201             // only filename, search in current dir and DPATH
    202             rc = DosSearchPath(SEARCH_IGNORENETERRS
    203                                | SEARCH_ENVIRONMENT
    204                                | SEARCH_CUR_DIRECTORY,
    205                                "DPATH",
    206                                (PSZ)pszFile,
    207                                szMessageFile,
    208                                sizeof(szMessageFile));
    209             if (rc != NO_ERROR)
    210                 break;
    211         }
    212 
    213         // _Pmpf(("tmfGetMessage: Found %s", szMessageFile));
    214 
    215         // compile table if neccessary
    216         rc = CompileMsgTable(szMessageFile, &pbTableData);
    217         if (rc != NO_ERROR)
    218             break;
    219 
    220         // _Pmpf(("tmfGetMessage: Now searching compiled file"));
    221 
    222         // search the name
    223         pszEntry = strstr(pbTableData, pszMessageName);
    224         if (!pszEntry)
    225         {
    226             rc = ERROR_MR_MID_NOT_FOUND;
    227             break;
    228         }
    229         else
    230             pszEntry += strlen(pszMessageName) + 1;
    231 
    232         // isolate entry
    233         pszEndOfEntry = strchr(pszEntry, '\n');
    234         if (pszEndOfEntry)
    235             *pszEndOfEntry = 0;
    236 
    237         // get numbers
    238         ulMessagePos = atol(pszEntry);
    239         if (ulMessagePos == 0)
    240             if (!pszEntry)
     168            // TMFMSGFILE created:
     169
     170            PCSZ    pStartOfFile,
     171                    pStartOfMarker;
     172
     173            ULONG   ulStartMarkerLength = strlen(G_pcszStartMarker),
     174                    ulEndMarkerLength = strlen(G_pcszEndMarker);
     175
     176            // initialize TMFMSGFILE struct
     177            ZERO(pFile);
     178            pFile->pszFilename = strdup(pcszMessageFile);
     179            treeInit(&pFile->IDsTreeRoot);
     180
     181            xstrInitSet(&pFile->strContent, pszContent);
     182
     183            // convert to plain C format
     184            xstrConvertLineFormat(&pFile->strContent,
     185                                  CRLF2LF);
     186
     187            // kick out all the comments
     188            while (pStartOfMarker = strstr(pFile->strContent.psz, "\n;"))
    241189            {
    242                 rc = ERROR_MR_INV_MSGF_FORMAT;
    243                 break;
     190                // copy the next line over this
     191                PCSZ pEOL = strhFindEOL(pStartOfMarker + 2, NULL);
     192                /* printf("pStartOfMarker = %lX, pEOL = %lX\n",
     193                        pStartOfMarker,
     194                        pEOL); */
     195                xstrrpl(&pFile->strContent,
     196                        // ofs of first char to replace: "\n;"
     197                        pStartOfMarker - pFile->strContent.psz,
     198                        // no. of chars to replace:
     199                        pEOL - pStartOfMarker,
     200                        // string to replace chars with:
     201                        NULL,
     202                        // length of replacement string:
     203                        0);
    244204            }
    245205
    246         pszEntry = strchr(pszEntry, ' ');
    247         if (!pszEntry)
    248         {
    249             rc = ERROR_MR_INV_MSGF_FORMAT;
    250             break;
    251         }
    252         ulMessageLen = atol(pszEntry);
    253 
    254         // determine maximum read len
    255         ulBytesToRead = _min(ulMessageLen, cbBuffer);
    256 
    257         // open file and read message
    258         rc = DosOpen((PSZ)pszFile,
    259                      &hfile,
    260                      &ulAction,
    261                      0, 0,
    262                      OPEN_ACTION_FAIL_IF_NEW
    263                      | OPEN_ACTION_OPEN_IF_EXISTS,
    264                      OPEN_FLAGS_FAIL_ON_ERROR
    265                      | OPEN_SHARE_DENYWRITE
    266                      | OPEN_ACCESS_READONLY,
    267                      NULL);
    268         if (rc != NO_ERROR)
    269             break;
    270 
    271         rc = DosSetFilePtr(hfile, ulMessagePos, FILE_BEGIN, &ulFilePtr);
    272         if ((rc != NO_ERROR) || (ulFilePtr != ulMessagePos))
    273             break;
    274 
    275         rc = DosRead(hfile,
    276                      pbBuffer,
    277                      ulBytesToRead,
    278                      &ulBytesRead);
    279         if (rc != NO_ERROR)
    280             break;
    281 
    282         // report "buffer too small" here
    283         if (ulBytesToRead < ulMessageLen)
    284             rc = ERROR_BUFFER_OVERFLOW;
    285 
    286     }
    287     while (FALSE);
    288 
    289     // replace string placeholders ("%1" etc.) (*UM)
    290     if (rc == NO_ERROR)     // added (99-10-24) [umoeller]
    291         if (cTable)
    292         {
    293             // create a temporary buffer for replacements
    294             PSZ             pszTemp = (PSZ)malloc(cbBuffer);
    295 
    296             if (!pszTemp)
    297                 rc = ERROR_NOT_ENOUGH_MEMORY;
     206            // free excessive memory
     207            xstrShrink(&pFile->strContent);
     208
     209            pStartOfFile = pFile->strContent.psz;
     210
     211            // go build a tree of all message IDs...
     212
     213            // find first start message marker
     214            pStartOfMarker = strstr(pStartOfFile,
     215                                    G_pcszStartMarker);     // start-of-line marker
     216            while (    (pStartOfMarker)
     217                    && (!arc)
     218                  )
     219            {
     220                // start marker found:
     221                PCSZ pStartOfMsgID = pStartOfMarker + ulStartMarkerLength;
     222                // search next start marker
     223                PCSZ pStartOfNextMarker = strstr(pStartOfMsgID + 1,
     224                                                 G_pcszStartMarker);
     225                // and the end-marker
     226                PCSZ pEndOfMarker = strstr(pStartOfMsgID + 1,
     227                                           G_pcszEndMarker);
     228
     229                PMSGENTRY pNew;
     230
     231                // sanity checks...
     232
     233                if (    (pStartOfNextMarker)
     234                     && (pStartOfNextMarker < pEndOfMarker)
     235                   )
     236                {
     237                    // next start marker before end marker:
     238                    // that doesn't look correct, skip this entry
     239                    pStartOfMarker = pStartOfNextMarker;
     240                    continue;
     241                }
     242
     243                if (!pEndOfMarker)
     244                    // no end marker found:
     245                    // that's invalid too, and there can't be any
     246                    // message left in the file then...
     247                    break;
     248
     249                // alright, this ID looks correct now
     250                if (!(pNew = NEW(MSGENTRY)))
     251                    arc = ERROR_NOT_ENOUGH_MEMORY;
     252                else
     253                {
     254                    // length of the ID
     255                    ULONG ulIDLength = pEndOfMarker - pStartOfMsgID;
     256                    PCSZ pStartOfText = pEndOfMarker + ulEndMarkerLength;
     257
     258                    ZERO(pNew);
     259
     260                    // copy the string ID (between start and end markers)
     261                    xstrInit(&pNew->strID, 0);
     262                    xstrcpy(&pNew->strID,
     263                            pStartOfMsgID,
     264                            ulIDLength);
     265                    // make ulKey point to the string ID for tree sorting
     266                    pNew->Tree.ulKey = (ULONG)pNew->strID.psz;
     267
     268                    // skip leading spaces
     269                    while (*pStartOfText == ' ')
     270                        pStartOfText++;
     271
     272                    // store start of text
     273                    pNew->ulOfsText = pStartOfText - pStartOfFile;
     274
     275                    // check if there's a comment before the
     276                    // next item
     277                    /* if (pNextComment = strstr(pStartOfText, "\n;"))
     278                    {
     279                        if (    (!pStartOfNextMarker)
     280                             || (pNextComment < pStartOfNextMarker)
     281                           )
     282                            pEndOfText = pNextComment;
     283                    } */
     284
     285                    if (pStartOfNextMarker)
     286                        // other markers left:
     287                        pNew->cbText =    // offset of next marker
     288                                         (pStartOfNextMarker - pStartOfFile)
     289                                       - pNew->ulOfsText;
     290                    else
     291                        // this was the last message:
     292                        pNew->cbText = strlen(pStartOfText);
     293
     294                    // store this thing
     295                    if (!treeInsert(&pFile->IDsTreeRoot,
     296                                    (TREE*)pNew,
     297                                    treeCompareStrings))
     298                        // successfully inserted:
     299                        (pFile->cIDs)++;
     300                }
     301
     302                // go on with next start marker (can be NULL)
     303                pStartOfMarker = pStartOfNextMarker;
     304            } // end while (    (pStartOfMarker) ...
     305
     306            // done with IDs, or error occured:
     307            if (!arc)
     308                // output
     309                *ppMsgFile = pFile;
    298310            else
    299             {
    300                 // two pointers for copying
    301                 PSZ             pSource = pbBuffer,
    302                                 pTarget = pszTemp;
    303 
    304                 CHAR            szMsgNum[5] = "0";
    305                 ULONG           ulTableIndex = 0;
    306 
    307                 ULONG           ulCount = 0, ulTargetWritten = 0;
    308 
    309                 // 1) copy the stuff we've read to the
    310                 // new temporary buffer
    311                 for (ulCount = 0;
    312                      ulCount < ulBytesRead;
    313                      ulCount++)
    314                     *pTarget++ = *pSource++;
    315 
    316                 // 2) now go thru the source string
    317                 // (which we've read above) and
    318                 // look for "%" characters; we do
    319                 // this while copying the stuff
    320                 // back to pbBuffer
    321 
    322                 pSource = pszTemp;
    323                 pTarget = pbBuffer;
    324 
    325                 for (ulCount = 0;
    326                      ulCount < ulBytesRead;
    327                      ulCount++)
    328                 {
    329                     if (*pSource == '%')
    330                     {
    331                         // copy the index (should be > 0)
    332                         szMsgNum[0] = *(pSource + 1);
    333                         // szMsgNum[1] is still 0
    334 
    335                         // Pmpf(("      Found %s", szMsgNum));
    336 
    337                         ulTableIndex = atoi(szMsgNum);
    338                         // _Pmpf(("        --> %d", ulTableIndex));
    339                         if ((ulTableIndex != 0)
    340                             && (ulTableIndex <= cTable)
    341                             )
    342                         {
    343                             // valid index:
    344                             // insert stuff from table
    345                             PSZ            *ppTableThis = (PSZ*)(pTable + (ulTableIndex - 1));
    346 
    347                             // (pTable) if 1, (pTable+1) if 2, ...
    348                             PSZ             pTableSource = *ppTableThis;
    349 
    350                             // copy string from table
    351                             // and increase the target pointer
    352                             while ((*pTarget++ = *pTableSource++))
    353                                 ulTargetWritten++;
    354 
    355                             // increase source pointer to point
    356                             // behind the "%x"
    357                             pSource += 2;
    358                             // decrease target again, because
    359                             // we've copied a null-byte
    360                             pTarget--;
    361 
    362                             ulCount++;
    363                             // next for
    364                             continue;
    365                         }
    366                         // else invalid index: just copy
    367                     }
    368 
    369                     // regular character: copy
    370                     *pTarget++ = *pSource++;
    371                     ulTargetWritten++;
    372                 }                   // end for (ulCount = 0) ...
    373 
    374                 ulBytesRead = ulTargetWritten;
    375 
    376                 free(pszTemp);      // V0.9.9 (2001-03-13) [umoeller]
    377             } // if pszTemp
    378         }                           // end if (cTable)
    379 
    380     // report bytes written (*UM)
    381     if (pcbMsg)
    382         *pcbMsg = ulBytesRead;
    383 
    384     // cleanup
    385     if (hfile)
    386         DosClose(hfile);
    387     if (pbTableData)
    388         free(pbTableData);
    389 
    390     return (rc);
    391 }
    392 
    393 /*
    394  *@@ tmfGetMessageExt:
    395  *      just like tmfGetMessage, but this func is smart
    396  *      enough to return a zero-terminated string and
    397  *      strip trailing newlines.
    398  *
    399  *      See tmfGetMessage for details.
    400  */
    401 
    402 APIRET tmfGetMessageExt(PCHAR* pTable,    // in: pointer table for string insertion
    403                         ULONG cTable,     // in: number of pointers in *pTable
    404                         PBYTE pbBuffer,   // out: returned string
    405                         ULONG cbBuffer,   // out: sizeof(*pbBuffer)
    406                         PCSZ pszMessageName,  // in: message identifier
    407                         PCSZ pszFile,      // in: message file (.TMF extension proposed)
    408                         PULONG pcbMsg)    // out: bytes written to *pbBuffer
    409 {
    410     APIRET  arc = NO_ERROR;
    411     ULONG   cbReturned = 0;
    412     arc = tmfGetMessage(pTable, cTable, pbBuffer, cbBuffer, pszMessageName, pszFile,
    413                         &cbReturned);
    414 
    415     if (arc == NO_ERROR)
    416     {
    417         PSZ p = pbBuffer;
    418 
    419         // terminate string
    420         if (cbReturned < cbBuffer)
    421             *(pbBuffer+cbReturned) = 0;
    422         else
    423             *(pbBuffer+cbBuffer-1) = 0;
    424 
    425         // remove leading spaces (99-10-24) [umoeller]
    426         while (*p == ' ')
    427             p++;
    428         if (p != pbBuffer)
    429         {
    430             // we have leading spaces:
    431             strcpy(pbBuffer, p);
    432             // decrease byte count
    433             cbReturned -= (p - pbBuffer);
    434         }
    435 
    436         // remove trailing newlines
    437         while (TRUE)
    438         {
    439             p = pbBuffer + strlen(pbBuffer) - 1;
    440             if (    (*p == '\n')
    441                  || (*p == '\r')
    442                )
    443             {
    444                 *p = '\0';
    445             } else
    446                 break; // while (TRUE)
    447         }
    448     }
    449 
    450     if (pcbMsg)
    451         *pcbMsg = cbReturned;
     311                // error:
     312                tmfCloseMessageFile(&pFile);
     313
     314        } // end else if (!(pFile = NEW(TMFMSGFILE)))
     315    } // end if (!(arc = doshLoadTextFile(pcszMessageFile,
    452316
    453317    return (arc);
     
    455319
    456320/*
    457  *@@ CompileMsgTable:
    458  *      this gets called from tmfGetMessage.
    459  *      Here we check for whether the given
    460  *      message file has been compiled to the
    461  *      EAs yet or if the compilation is
    462  *      outdated.
    463  *
    464  *      If we need recompilation, we do this here.
    465  *      Memory for the compiled message table is
    466  *      allocated here using malloc(), and the
    467  *      buffer is stored in ppbTableData. You
    468  *      must free() this buffer after using this
    469  *      function.
    470  *
    471  *      <B>Returns:</B>
     321 *@@ tmfCloseMessageFile:
     322 *      closes a message file opened by
     323 *      tmfOpenMessageFile, frees all resources,
     324 *      and sets *ppMsgFile to NULL for safety.
     325 *
     326 *@@added V0.9.16 (2001-10-08) [umoeller]
     327 */
     328
     329APIRET tmfCloseMessageFile(PTMFMSGFILE *ppMsgFile)
     330{
     331    if (ppMsgFile && *ppMsgFile)
     332    {
     333        PTMFMSGFILE pFile = *ppMsgFile;
     334        ULONG   cItems;
     335        TREE**  papNodes;
     336
     337        if (pFile->pszFilename)
     338            free(pFile->pszFilename);
     339        xstrClear(&pFile->strContent);
     340
     341        if (cItems = pFile->cIDs)
     342        {
     343            if (papNodes = treeBuildArray(pFile->IDsTreeRoot,
     344                                          &cItems))
     345            {
     346                ULONG ul;
     347                for (ul = 0; ul < cItems; ul++)
     348                {
     349                    PMSGENTRY pNodeThis = (PMSGENTRY)(papNodes[ul]);
     350
     351                    xstrClear(&pNodeThis->strID);
     352
     353                    free(pNodeThis);
     354                }
     355
     356                free(papNodes);
     357            }
     358        }
     359
     360        free(pFile);
     361        *ppMsgFile = NULL;
     362
     363        return (NO_ERROR);
     364    }
     365
     366    return (ERROR_INVALID_PARAMETER);
     367}
     368
     369/*
     370 *@@ tmfGetMessage:
     371 *      replacement for DosGetMessage.
     372 *
     373 *      After you have opened a .TMF file with tmfOpenMessageFile,
     374 *      you can pass it to this function to retrieve a message
     375 *      with the given string (!) ID. See tmsgfile.c for details.
     376 *
     377 *      Note that this will invoke xstrcpy on the given XSTRING
     378 *      buffer. In other words, the string must be initialized
     379 *      (see xstrInit), but will be replaced.
     380 *
     381 *      This does perform the same simple string replacements
     382 *      as DosGetMessage, that is, the string "%1" will be
     383 *      replaced with pTable[0], "%2" will be replaced with
     384 *      pTable[1], and so on.
     385 *
     386 *      Returns:
     387 *
     388 *      --  NO_ERROR;
     389 *
    472390 *      --  ERROR_INVALID_PARAMETER
    473  *      --  ERROR_BUFFER_OVERFLOW
    474  *      --  ERROR_NOT_ENOUGH_MEMORY
    475  *      --  ERROR_READ_FAULT
    476  *      --  ERROR_INVALID_DATA
    477  *
    478  *@@changed V0.9.1 (99-12-12) [umoeller]: fixed heap overwrites which caused irregular crashes
    479  *@@changed V0.9.1 (99-12-12) [umoeller]: fixed realloc failure
    480  *@@changed V0.9.1 (99-12-12) [umoeller]: file last-write date is now reset when EAs are written
    481  *@@changed V0.9.1 (2000-02-01) [umoeller]: now skipping leading spaces in msg
    482  *@@todo handle 64KB EA limit
    483  */
    484 
    485 APIRET CompileMsgTable(PSZ pszMessageFile,     // in: file to compile
    486                        PBYTE *ppbTableDataReturn)  // out: compiled message table
     391 *
     392 *      --  ERROR_MR_MID_NOT_FOUND
     393 *
     394 *@@added V0.9.16 (2001-10-08) [umoeller]
     395 */
     396
     397APIRET tmfGetMessage(PTMFMSGFILE pMsgFile,      // in: msg file opened by tmfOpenMessageFile
     398                     PCSZ pcszMessageName,      // in: msg name to look for (case-sensitive!)
     399                     PXSTRING pstr,             // out: message string, if found (XSTRING must be initialized)
     400                     PSZ *pTable,               // in: replacement table or NULL
     401                     ULONG cTableEntries)       // in: count of items in pTable or null
    487402{
    488     APIRET          rc = NO_ERROR;
    489     // CHAR            szMessageFile[_MAX_PATH];
    490 
    491     FILESTATUS3     fs3MessageFile;
    492 
    493     // fixed UM 99-10-22: initializing all vars to 0
    494     ULONG           ulStampLength = 0;
    495     PBYTE           pbFileData = NULL;
    496     ULONG           ulFileDataLength = 0;
    497 
    498     CHAR            szFileStampOld[18] = "";     // yyyymmddhhmmssms.
    499 
    500     CHAR            szFileStampCurrent[18] = "";
    501 
    502     PBYTE           pbTableData = NULL;
    503     ULONG           cbTableDataAllocated = 0;
    504     ULONG           cbTableDataUsed = 0;
    505 
    506     HFILE           hfileMessageFile = NULLHANDLE;
    507     ULONG           ulAction = 0;
    508     ULONG           ulBytesRead = 0;
    509 
    510     COUNTRYCODE     cc = {0, 0};
    511 
    512     ULONG           cbOpenTag = strlen(MSG_NAME_START);
    513     ULONG           cbClosingTag = strlen(MSG_NAME_END);
    514 
    515     do
     403    APIRET arc = NO_ERROR;
     404
     405    if (!pMsgFile)
     406        arc = ERROR_INVALID_PARAMETER;
     407    else
    516408    {
    517         PSZ             pCurrentNameStart = 0;
    518         PSZ             pCurrentNameEnd = 0;
    519         ULONG           ulCurrentMessagePos = 0;
    520         ULONG           ulCurrentMessageLen = 0;
    521         // BOOL            fError = FALSE;
    522 
    523         // check parms
    524         if ((!pszMessageFile) ||
    525             (!ppbTableDataReturn))
     409        // go find the message in the tree
     410        PMSGENTRY pEntry;
     411        if (pEntry = (PMSGENTRY)treeFind(pMsgFile->IDsTreeRoot,
     412                                         (ULONG)pcszMessageName,
     413                                         treeCompareStrings))
    526414        {
    527             rc = ERROR_INVALID_PARAMETER;
    528             break;
    529         }
    530 
    531         // get length and timestamp of file
    532         rc = DosQueryPathInfo(pszMessageFile,
    533                               FIL_STANDARD,
    534                               &fs3MessageFile,
    535                               sizeof(fs3MessageFile));
    536         if (rc != NO_ERROR)
    537             break;
    538         ulFileDataLength = fs3MessageFile.cbFile;
    539 
    540         // determine current timestamp
    541         GetTimeStamp(&fs3MessageFile, szFileStampCurrent, sizeof(szFileStampCurrent));
    542 
    543         // _Pmpf(("    Timestamp for %s: '%s'", pszMessageFile, szFileStampCurrent));
    544 
    545         // determine saved timestamp
    546         ulStampLength = sizeof(szFileStampOld);
    547         rc = eahReadStringEA(pszMessageFile,
    548                           EA_TIMESTAMP,
    549                           szFileStampOld,
    550                           &ulStampLength);
    551 
    552         // _Pmpf(("    Saved timestamp for %s: '%s'", pszMessageFile, szFileStampOld));
    553 
    554         // compare timestamps
    555         if ((rc == NO_ERROR)
    556             && (ulStampLength == (strlen(szFileStampCurrent) + 1))
    557             && (!strcmp(szFileStampCurrent, szFileStampOld))
    558             )
    559         {
    560 
    561             // read table out of EAs
    562             do
     415            // copy the raw string to the output buffer
     416            xstrcpy(pstr,
     417                    pMsgFile->strContent.psz + pEntry->ulOfsText,
     418                    pEntry->cbText);
     419
     420            // now replace strings from the table
     421            if (cTableEntries && pTable)
    563422            {
    564                 // get ea length of table
    565                 rc = eahReadStringEA(pszMessageFile,
    566                                   EA_MSGTABLE,
    567                                   NULL, &cbTableDataAllocated);
    568                 if (rc != ERROR_BUFFER_OVERFLOW)
    569                     break;
    570 
    571                 // get memory
    572                 if ((pbTableData = (PBYTE)malloc(cbTableDataAllocated)) == NULL)
     423                CHAR szFind[34] = "%0";
     424                ULONG ul;
     425                for (ul = 0;
     426                     ul < cTableEntries;
     427                     ul++)
    573428                {
    574                     rc = ERROR_NOT_ENOUGH_MEMORY;
    575                     break;
    576                 }
    577 
    578                 // read table
    579                 rc = eahReadStringEA(pszMessageFile,
    580                                   EA_MSGTABLE,
    581                                   pbTableData, &cbTableDataAllocated);
    582 
    583             }
    584             while (FALSE);
    585 
    586             // if no error occurred, we are finished
    587             if (rc == NO_ERROR)
    588             {
    589                 // _Pmpf(("      --> using precompiled table"));
    590                 break;
    591             }
    592         } // end if
    593 
    594         // _Pmpf(("      --> recompiling table"));
    595 
    596         // recompilation needed:
    597         // get memory for file data
    598         if ((pbFileData = (PBYTE)malloc(ulFileDataLength + 1)) == NULL)
    599         {
    600             rc = ERROR_NOT_ENOUGH_MEMORY;
    601             break;
    602         }
    603         *(pbFileData + ulFileDataLength) = 0;
    604 
    605         // get memory for table data
    606         cbTableDataAllocated = ulFileDataLength / 2;
    607         if ((pbTableData = (PBYTE)malloc(cbTableDataAllocated)) == NULL)
    608         {
    609             rc = ERROR_NOT_ENOUGH_MEMORY;
    610             break;
    611         }
    612 
    613         *pbTableData = 0;
    614                 // fixed V0.9.1 (99-12-12);
    615                 // this was
    616                 //      *(pbTableData + cbTableDataAllocated) = 0;
    617                 // since we're using "strcat" below to write into
    618                 // the table data, this wasn't such a good idea
    619 
    620         // open file and read it
    621         rc = DosOpen(pszMessageFile,
    622                      &hfileMessageFile,
    623                      &ulAction,
    624                      0, 0,
    625                      OPEN_ACTION_FAIL_IF_NEW
    626                      | OPEN_ACTION_OPEN_IF_EXISTS,
    627                      OPEN_FLAGS_FAIL_ON_ERROR
    628                      | OPEN_SHARE_DENYWRITE
    629                      | OPEN_ACCESS_READWRITE,   // needed for EA attachement
    630                       NULL);
    631         if (rc != NO_ERROR)
    632             break;
    633 
    634         rc = DosRead(hfileMessageFile,
    635                      pbFileData,
    636                      ulFileDataLength,
    637                      &ulBytesRead);
    638         if (rc != NO_ERROR)
    639             break;
    640         if (ulBytesRead != ulFileDataLength)
    641         {
    642             rc = ERROR_READ_FAULT;
    643             break;
    644         }
    645 
    646         // search first message name
    647         // this will automatically skip comments
    648         // at the beginning
    649         pCurrentNameStart = strstr(pbFileData, MSG_NAME_START);
    650 
    651         if (!pCurrentNameStart)
    652         {
    653             rc = ERROR_INVALID_DATA;
    654             break;
    655         }
    656         else
    657             pCurrentNameStart += cbOpenTag;
    658                 // this points to message ID string after "<--" now
    659 
    660         // is first name complete ?
    661         pCurrentNameEnd = strstr(pCurrentNameStart, MSG_NAME_END);
    662         if (!pCurrentNameEnd)
    663         {
    664             rc = ERROR_INVALID_DATA;
    665             break;
    666         }
    667 
    668         // scan through all names
    669         while (     (pCurrentNameStart)
    670                  && (*pCurrentNameStart)
    671               )
    672         {
    673             PSZ     pNextNameStart = 0,
    674                     pNextCommentStart = 0,
    675                     pEndOfMsg = 0;
    676             CHAR    szEntry[500] = "";
    677 
    678             // search end of name, if not exist, skip end of file
    679             pCurrentNameEnd = strstr(pCurrentNameStart, MSG_NAME_END);
    680                 // this points to the closing "-->" now
    681             if (!pCurrentNameEnd)
    682                 break;
    683 
    684             // search next name (ID), if none, use end of string
    685             pNextNameStart = strstr(pCurrentNameEnd, MSG_NAME_START);
    686                     // points to next "<--" now
    687             if (!pNextNameStart)
    688                 // not found:
    689                 // use terminating null byte
    690                 pNextNameStart = pCurrentNameStart + strlen(pCurrentNameStart);
    691             else
    692                 // found:
    693                 // point to next message ID string
    694                 pNextNameStart += cbOpenTag;
    695 
    696             pEndOfMsg = pNextNameStart - cbOpenTag;
    697 
    698             // search next comment (99-11-28) [umoeller]
    699             pNextCommentStart = strstr(pCurrentNameEnd, MSG_COMMENT_LINE);
    700             if (pNextCommentStart)
    701                 // another comment found:
    702                 if (pNextCommentStart < pNextNameStart)
    703                     // if it's even before the next key, use that
    704                     // for the end of the current string; otherwise
    705                     // the comment would appear in the message too
    706                     pEndOfMsg = pNextCommentStart;
    707 
    708             // now we have:
    709             // -- pCurrentNameStart: points to message ID after "<--"
    710             // -- pCurrentNameEnd: points to "-->"
    711             // -- pNextNameStart: points to _next_ message ID after "<--"
    712             // -- pNextCommentStart: points to _next_ comment
    713             // -- pEndOfMsg: either pNextNameStart-cbOpenTag
    714             //               or pNextCommentStart,
    715             //               whichever comes first
    716 
    717             // calculate table entry data
    718             *pCurrentNameEnd = 0;
    719             ulCurrentMessagePos = (pCurrentNameEnd - pbFileData) + cbClosingTag;
    720             // this points to the first character after the <-- --> tag now;
    721             // if this points to a space character, take next char
    722             // (V0.9.1 (2000-02-13) [umoeller])
    723             while (     (*(pbFileData + ulCurrentMessagePos))
    724                      && (*(pbFileData + ulCurrentMessagePos) == ' ')
    725                   )
    726                 ulCurrentMessagePos++;
    727 
    728             ulCurrentMessageLen = (pEndOfMsg - pbFileData) - ulCurrentMessagePos;
    729                     // pszNextNameStart - pbFileData - ulCurrentMessagePos - 1;
    730 
    731             // determine entry
    732             sprintf(szEntry, "%s %lu %lu" NEWLINE,
    733                     pCurrentNameStart,
    734                     ulCurrentMessagePos,
    735                     ulCurrentMessageLen);
    736 
    737             /* _Pmpf(("Found %s at %d, length %d",
    738                     pCurrentNameStart,
    739                     ulCurrentMessagePos,
    740                     ulCurrentMessageLen)); */
    741 
    742             // need more space ?
    743             if ((cbTableDataUsed + strlen(szEntry) + 1) > cbTableDataAllocated)
    744             {
    745                 PBYTE           pbTmp;
    746                 // _Pmpf(("  Re-allocating!!"));
    747 
    748                 cbTableDataAllocated += ulFileDataLength / 2;
    749                 pbTmp = (PBYTE)realloc(pbTableData, cbTableDataAllocated);
    750                 if (!pbTmp)
    751                 {
    752                     rc = ERROR_NOT_ENOUGH_MEMORY;
    753                     break;
    754                 }
    755                 else
    756                     pbTableData = pbTmp;
    757             }
    758 
    759             // add entry
    760             strcat(pbTableData, szEntry);
    761             cbTableDataUsed += strlen(szEntry);
    762                     // added V0.9.1 (99-12-12);
    763                     // this was 0 all the time and never raised
    764 
    765             // adress next entry
    766             pCurrentNameStart = pNextNameStart;
    767 
    768         }  // while (pszCurrentNameStart)
    769 
    770         // write new timestamp and table
    771         // @@todo handle 64 kb limit here !!!
    772         if (rc == NO_ERROR)
    773         {
    774             FILESTATUS3 fs3Tmp;
    775 
    776             rc = eahWriteStringEA(hfileMessageFile, EA_TIMESTAMP, szFileStampCurrent);
    777             rc = eahWriteStringEA(hfileMessageFile, EA_MSGTABLE, pbTableData);
    778 
    779             if (rc == NO_ERROR)
    780             {
    781                 // fixed V0.9.1 (99-12-12): we need to reset the file date to
    782                 // the old date,
    783                 // because writing EAs updates the "last write date" also;
    784                 // without this, we'd recompile every single time
    785                 rc = DosQueryFileInfo(hfileMessageFile,
    786                                       FIL_STANDARD,
    787                                       &fs3Tmp,
    788                                       sizeof(fs3Tmp));
    789                 if (rc == NO_ERROR)
    790                 {
    791                     // restore original "last write" date
    792                     // _Pmpf(("Resetting file date"));
    793                     fs3Tmp.fdateLastWrite = fs3MessageFile.fdateLastWrite;
    794                     fs3Tmp.ftimeLastWrite = fs3MessageFile.ftimeLastWrite;
    795                     DosSetFileInfo(hfileMessageFile,
    796                                    FIL_STANDARD,
    797                                    &fs3Tmp,
    798                                    sizeof(fs3Tmp));
     429                    ULONG ulOfs = 0;
     430                    PSZ pszReplThis = pTable[ul];
     431
     432                    _ultoa(ul + 1, szFind + 1, 10);
     433                    while (xstrFindReplaceC(pstr,
     434                                            &ulOfs,
     435                                            szFind,
     436                                            pszReplThis))
     437                        ;
    799438                }
    800439            }
    801440        }
    802     } while (FALSE);
    803 
    804 
    805     if (rc == NO_ERROR)
    806     {
    807         // hand over result
    808         *ppbTableDataReturn = pbTableData;
    809 
    810         // make text uppercase
    811         rc = DosMapCase(cbTableDataAllocated, &cc, pbTableData);
     441        else
     442            arc = ERROR_MR_MID_NOT_FOUND;
    812443    }
     444
     445    return (arc);
     446}
     447
     448/* test case */
     449
     450#ifdef __TMFDEBUG__
     451
     452int main(int argc, char *argv[])
     453{
     454    APIRET arc;
     455    PTMFMSGFILE pMsgFile;
     456
     457    if (argc < 3)
     458        printf("tmsgfile <file> <msgid>\n");
    813459    else
    814460    {
    815         // error occured:
    816         if (pbTableData)
    817             free(pbTableData);
     461        if (!(arc = tmfOpenMessageFile(argv[1], &pMsgFile)))
     462        {
     463            XSTRING str;
     464            xstrInit(&str, 0);
     465            if (!(arc = tmfGetMessage(pMsgFile,
     466                                      argv[2],
     467                                      &str,
     468                                      NULL,
     469                                      0)))
     470            {
     471                printf("String:\n%s", str.psz);
     472            }
     473            else
     474                printf("tmfGetMessage returned %d\n", arc);
     475
     476            xstrClear(&str);
     477
     478            tmfCloseMessageFile(&pMsgFile);
     479        }
     480        else
     481            printf("tmfOpenMessageFile returned %d\n", arc);
    818482    }
    819 
    820     // cleanup
    821     if (pbFileData)
    822         free(pbFileData);
    823     if (hfileMessageFile)
    824         DosClose(hfileMessageFile);
    825 
    826     return rc;
    827483}
    828484
    829 /*
    830  * GetTimeStamp:
    831  *      this returns the "last write date" timestamp
    832  *      of the file specified in pfs3.
    833  *
    834  *      <B>Usage:</B> called from CompileMsgTable
    835  *      to check whether the compilation is
    836  *      outdated.
    837  *
    838  *      Returns:
    839  *      --  NO_ERROR
    840  *      --  ERROR_INVALID_PARAMETER
    841  *      --  ERROR_BUFFER_OVERFLOW: pszBuffer too small for timestamp
    842  */
    843 
    844 APIRET GetTimeStamp(PFILESTATUS3 pfs3,     // in: file to check
    845                     PSZ pszBuffer,    // out: timestamp
    846                     ULONG ulBufferlen)    // in: sizeof(*pszBuffer)
    847 {
    848 
    849     APIRET          rc = NO_ERROR;
    850     CHAR            szTimeStamp[15];
    851     static PSZ      pszFormatTimestamp = "%4u%02u%02u%02u%02u%02u%";
    852 
    853     do
    854     {
    855         // check parms
    856         if ((!pfs3) ||
    857             (!pszBuffer))
    858         {
    859             rc = ERROR_INVALID_PARAMETER;
    860             break;
    861         }
    862 
    863         // create stamp
    864         sprintf(szTimeStamp,
    865                 pszFormatTimestamp,
    866                 pfs3->fdateLastWrite.year + 1980,
    867                 pfs3->fdateLastWrite.month,
    868                 pfs3->fdateLastWrite.day,
    869                 pfs3->ftimeLastWrite.hours,
    870                 pfs3->ftimeLastWrite.minutes,
    871                 pfs3->ftimeLastWrite.twosecs * 2);
    872 
    873         // check bufferlen
    874         if (strlen(szTimeStamp) + 1 > ulBufferlen)
    875         {
    876             rc = ERROR_BUFFER_OVERFLOW;
    877             break;
    878         }
    879 
    880         // hand over result
    881         strcpy(pszBuffer, szTimeStamp);
    882     }
    883     while (FALSE);
    884 
    885     return rc;
    886 }
    887 
     485#endif
Note: See TracChangeset for help on using the changeset viewer.