source: trunk/src/helpers/tmsgfile.c@ 113

Last change on this file since 113 was 113, checked in by umoeller, 24 years ago

Misc updates.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 15.6 KB
Line 
1
2/*
3 *@@sourcefile tmsgfile.c:
4 * replacement code for DosGetMessage for decent NLS support.
5 *
6 * This code has the following advantages over DosGetMessage:
7 *
8 * 1) No external utility is necessary to change message
9 * files. Simply edit the text, and on the next call,
10 * the file is recompiled at run-time.
11 *
12 * 2) To identify messages, any string can be used, not
13 * only numerical IDs.
14 *
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.
40 *
41 * Usage: All OS/2 programs.
42 *
43 * Function prefixes:
44 * -- tmf* text message file functions
45 *
46 * Note: Version numbering in this file relates to XWorkplace version
47 * numbering.
48 *
49 *@@header "helpers\tmsgfile.h"
50 *@@added V0.9.0 [umoeller]
51 */
52
53/*
54 * Copyright (C) 2001 Ulrich M”ller.
55 * This file is part of the "XWorkplace helpers" source package.
56 * This is free software; you can redistribute it and/or modify
57 * it under the terms of the GNU General Public License as published
58 * by the Free Software Foundation, in version 2 as it comes in the
59 * "COPYING" file of the XWorkplace main distribution.
60 * This program is distributed in the hope that it will be useful,
61 * but WITHOUT ANY WARRANTY; without even the implied warranty of
62 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
63 * GNU General Public License for more details.
64 */
65
66#define OS2EMX_PLAIN_CHAR
67 // this is needed for "os2emx.h"; if this is defined,
68 // emx will define PSZ as _signed_ char, otherwise
69 // as unsigned char
70
71#define INCL_DOSFILEMGR
72#define INCL_DOSMISC
73#define INCL_DOSERRORS
74#include <os2.h>
75
76#include <stdio.h>
77#include <stdlib.h>
78#include <string.h>
79#include <stdarg.h>
80
81#include "setup.h" // code generation and debugging options
82
83#include "helpers\dosh.h"
84#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
90#include "helpers\tmsgfile.h"
91
92/*
93 *@@category: Helpers\Control program helpers\Text message files (TMF)
94 * see tmsgfile.c.
95 */
96
97/* ******************************************************************
98 *
99 * Declarations
100 *
101 ********************************************************************/
102
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 = "-->:";
120
121/* ******************************************************************
122 *
123 * Functions
124 *
125 ********************************************************************/
126
127/*
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
151{
152 APIRET arc;
153 PSZ pszContent = NULL;
154
155 if (!(arc = doshLoadTextFile(pcszMessageFile,
156 &pszContent)))
157 {
158 // file loaded:
159 // create a TMFMSGFILE entry
160 PTMFMSGFILE pFile;
161 if (!(pFile = NEW(TMFMSGFILE)))
162 {
163 arc = ERROR_NOT_ENOUGH_MEMORY;
164 free(pszContent);
165 }
166 else
167 {
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, NULL);
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;"))
189 {
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);
204 }
205
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 NULL,
297 (TREE*)pNew,
298 treeCompareStrings))
299 // successfully inserted:
300 (pFile->cIDs)++;
301 }
302
303 // go on with next start marker (can be NULL)
304 pStartOfMarker = pStartOfNextMarker;
305 } // end while ( (pStartOfMarker) ...
306
307 // done with IDs, or error occured:
308 if (!arc)
309 // output
310 *ppMsgFile = pFile;
311 else
312 // error:
313 tmfCloseMessageFile(&pFile);
314
315 } // end else if (!(pFile = NEW(TMFMSGFILE)))
316 } // end if (!(arc = doshLoadTextFile(pcszMessageFile,
317
318 return (arc);
319}
320
321/*
322 *@@ tmfCloseMessageFile:
323 * closes a message file opened by
324 * tmfOpenMessageFile, frees all resources,
325 * and sets *ppMsgFile to NULL for safety.
326 *
327 *@@added V0.9.16 (2001-10-08) [umoeller]
328 */
329
330APIRET tmfCloseMessageFile(PTMFMSGFILE *ppMsgFile)
331{
332 if (ppMsgFile && *ppMsgFile)
333 {
334 PTMFMSGFILE pFile = *ppMsgFile;
335 LONG cItems;
336 TREE** papNodes;
337
338 if (pFile->pszFilename)
339 free(pFile->pszFilename);
340 xstrClear(&pFile->strContent);
341
342 if (cItems = pFile->cIDs)
343 {
344 if (papNodes = treeBuildArray(pFile->IDsTreeRoot,
345 &cItems))
346 {
347 ULONG ul;
348 for (ul = 0; ul < cItems; ul++)
349 {
350 PMSGENTRY pNodeThis = (PMSGENTRY)(papNodes[ul]);
351
352 xstrClear(&pNodeThis->strID);
353
354 free(pNodeThis);
355 }
356
357 free(papNodes);
358 }
359 }
360
361 free(pFile);
362 *ppMsgFile = NULL;
363
364 return (NO_ERROR);
365 }
366
367 return (ERROR_INVALID_PARAMETER);
368}
369
370/*
371 *@@ tmfGetMessage:
372 * replacement for DosGetMessage.
373 *
374 * After you have opened a .TMF file with tmfOpenMessageFile,
375 * you can pass it to this function to retrieve a message
376 * with the given string (!) ID. See tmsgfile.c for details.
377 *
378 * Note that this will invoke xstrcpy on the given XSTRING
379 * buffer. In other words, the string must be initialized
380 * (see xstrInit), but will be replaced.
381 *
382 * This does perform the same simple string replacements
383 * as DosGetMessage, that is, the string "%1" will be
384 * replaced with pTable[0], "%2" will be replaced with
385 * pTable[1], and so on.
386 *
387 * Returns:
388 *
389 * -- NO_ERROR;
390 *
391 * -- ERROR_INVALID_PARAMETER
392 *
393 * -- ERROR_MR_MID_NOT_FOUND
394 *
395 *@@added V0.9.16 (2001-10-08) [umoeller]
396 */
397
398APIRET tmfGetMessage(PTMFMSGFILE pMsgFile, // in: msg file opened by tmfOpenMessageFile
399 PCSZ pcszMessageName, // in: msg name to look for (case-sensitive!)
400 PXSTRING pstr, // out: message string, if found (XSTRING must be initialized)
401 PSZ *pTable, // in: replacement table or NULL
402 ULONG cTableEntries) // in: count of items in pTable or null
403{
404 APIRET arc = NO_ERROR;
405
406 if (!pMsgFile)
407 arc = ERROR_INVALID_PARAMETER;
408 else
409 {
410 // go find the message in the tree
411 PMSGENTRY pEntry;
412 if (pEntry = (PMSGENTRY)treeFind(pMsgFile->IDsTreeRoot,
413 (ULONG)pcszMessageName,
414 treeCompareStrings))
415 {
416 // copy the raw string to the output buffer
417 xstrcpy(pstr,
418 pMsgFile->strContent.psz + pEntry->ulOfsText,
419 pEntry->cbText);
420
421 // now replace strings from the table
422 if (cTableEntries && pTable)
423 {
424 CHAR szFind[34] = "%0";
425 ULONG ul;
426 for (ul = 0;
427 ul < cTableEntries;
428 ul++)
429 {
430 ULONG ulOfs = 0;
431 PSZ pszReplThis = pTable[ul];
432
433 _ultoa(ul + 1, szFind + 1, 10);
434 while (xstrFindReplaceC(pstr,
435 &ulOfs,
436 szFind,
437 pszReplThis))
438 ;
439 }
440 }
441 }
442 else
443 arc = ERROR_MR_MID_NOT_FOUND;
444 }
445
446 return (arc);
447}
448
449/* test case */
450
451#ifdef __TMFDEBUG__
452
453int main(int argc, char *argv[])
454{
455 APIRET arc;
456 PTMFMSGFILE pMsgFile;
457
458 if (argc < 3)
459 printf("tmsgfile <file> <msgid>\n");
460 else
461 {
462 if (!(arc = tmfOpenMessageFile(argv[1], &pMsgFile)))
463 {
464 XSTRING str;
465 xstrInit(&str, 0);
466 if (!(arc = tmfGetMessage(pMsgFile,
467 argv[2],
468 &str,
469 NULL,
470 0)))
471 {
472 printf("String:\n%s", str.psz);
473 }
474 else
475 printf("tmfGetMessage returned %d\n", arc);
476
477 xstrClear(&str);
478
479 tmfCloseMessageFile(&pMsgFile);
480 }
481 else
482 printf("tmfOpenMessageFile returned %d\n", arc);
483 }
484}
485
486#endif
Note: See TracBrowser for help on using the repository browser.