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

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

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

  • 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);
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 (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;
310 else
311 // error:
312 tmfCloseMessageFile(&pFile);
313
314 } // end else if (!(pFile = NEW(TMFMSGFILE)))
315 } // end if (!(arc = doshLoadTextFile(pcszMessageFile,
316
317 return (arc);
318}
319
320/*
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 *
390 * -- ERROR_INVALID_PARAMETER
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
402{
403 APIRET arc = NO_ERROR;
404
405 if (!pMsgFile)
406 arc = ERROR_INVALID_PARAMETER;
407 else
408 {
409 // go find the message in the tree
410 PMSGENTRY pEntry;
411 if (pEntry = (PMSGENTRY)treeFind(pMsgFile->IDsTreeRoot,
412 (ULONG)pcszMessageName,
413 treeCompareStrings))
414 {
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)
422 {
423 CHAR szFind[34] = "%0";
424 ULONG ul;
425 for (ul = 0;
426 ul < cTableEntries;
427 ul++)
428 {
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 ;
438 }
439 }
440 }
441 else
442 arc = ERROR_MR_MID_NOT_FOUND;
443 }
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");
459 else
460 {
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);
482 }
483}
484
485#endif
Note: See TracBrowser for help on using the repository browser.