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

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

Minor fixes.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 15.8 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 now
39 * requires 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 // remove trailing newlines
295 while ( (pNew->cbText)
296 && (pStartOfText[pNew->cbText-1] == '\n')
297 )
298 (pNew->cbText)--;
299
300 // store this thing
301 if (!treeInsert(&pFile->IDsTreeRoot,
302 NULL,
303 (TREE*)pNew,
304 treeCompareStrings))
305 // successfully inserted:
306 (pFile->cIDs)++;
307 }
308
309 // go on with next start marker (can be NULL)
310 pStartOfMarker = pStartOfNextMarker;
311 } // end while ( (pStartOfMarker) ...
312
313 // done with IDs, or error occured:
314 if (!arc)
315 // output
316 *ppMsgFile = pFile;
317 else
318 // error:
319 tmfCloseMessageFile(&pFile);
320
321 } // end else if (!(pFile = NEW(TMFMSGFILE)))
322 } // end if (!(arc = doshLoadTextFile(pcszMessageFile,
323
324 return (arc);
325}
326
327/*
328 *@@ tmfCloseMessageFile:
329 * closes a message file opened by
330 * tmfOpenMessageFile, frees all resources,
331 * and sets *ppMsgFile to NULL for safety.
332 *
333 *@@added V0.9.16 (2001-10-08) [umoeller]
334 */
335
336APIRET tmfCloseMessageFile(PTMFMSGFILE *ppMsgFile)
337{
338 if (ppMsgFile && *ppMsgFile)
339 {
340 PTMFMSGFILE pFile = *ppMsgFile;
341 LONG cItems;
342 TREE** papNodes;
343
344 if (pFile->pszFilename)
345 free(pFile->pszFilename);
346 xstrClear(&pFile->strContent);
347
348 if (cItems = pFile->cIDs)
349 {
350 if (papNodes = treeBuildArray(pFile->IDsTreeRoot,
351 &cItems))
352 {
353 ULONG ul;
354 for (ul = 0; ul < cItems; ul++)
355 {
356 PMSGENTRY pNodeThis = (PMSGENTRY)(papNodes[ul]);
357
358 xstrClear(&pNodeThis->strID);
359
360 free(pNodeThis);
361 }
362
363 free(papNodes);
364 }
365 }
366
367 free(pFile);
368 *ppMsgFile = NULL;
369
370 return (NO_ERROR);
371 }
372
373 return (ERROR_INVALID_PARAMETER);
374}
375
376/*
377 *@@ tmfGetMessage:
378 * replacement for DosGetMessage.
379 *
380 * After you have opened a .TMF file with tmfOpenMessageFile,
381 * you can pass it to this function to retrieve a message
382 * with the given string (!) ID. See tmsgfile.c for details.
383 *
384 * Note that this will invoke xstrcpy on the given XSTRING
385 * buffer. In other words, the string must be initialized
386 * (see xstrInit), but will be replaced.
387 *
388 * This does perform the same simple string replacements
389 * as DosGetMessage, that is, the string "%1" will be
390 * replaced with pTable[0], "%2" will be replaced with
391 * pTable[1], and so on.
392 *
393 * Returns:
394 *
395 * -- NO_ERROR;
396 *
397 * -- ERROR_INVALID_PARAMETER
398 *
399 * -- ERROR_MR_MID_NOT_FOUND
400 *
401 *@@added V0.9.16 (2001-10-08) [umoeller]
402 */
403
404APIRET tmfGetMessage(PTMFMSGFILE pMsgFile, // in: msg file opened by tmfOpenMessageFile
405 PCSZ pcszMessageName, // in: msg name to look for (case-sensitive!)
406 PXSTRING pstr, // out: message string, if found (XSTRING must be initialized)
407 PSZ *pTable, // in: replacement table or NULL
408 ULONG cTableEntries) // in: count of items in pTable or null
409{
410 APIRET arc = NO_ERROR;
411
412 if (!pMsgFile)
413 arc = ERROR_INVALID_PARAMETER;
414 else
415 {
416 // go find the message in the tree
417 PMSGENTRY pEntry;
418 if (pEntry = (PMSGENTRY)treeFind(pMsgFile->IDsTreeRoot,
419 (ULONG)pcszMessageName,
420 treeCompareStrings))
421 {
422 // copy the raw string to the output buffer
423 xstrcpy(pstr,
424 pMsgFile->strContent.psz + pEntry->ulOfsText,
425 pEntry->cbText);
426
427 // now replace strings from the table
428 if (cTableEntries && pTable)
429 {
430 CHAR szFind[10] = "%0";
431 ULONG ul;
432 for (ul = 0;
433 ul < cTableEntries;
434 ul++)
435 {
436 ULONG ulOfs = 0;
437
438 _ultoa(ul + 1, szFind + 1, 10);
439 while (xstrFindReplaceC(pstr,
440 &ulOfs,
441 szFind,
442 pTable[ul]))
443 ;
444 }
445 }
446 }
447 else
448 arc = ERROR_MR_MID_NOT_FOUND;
449 }
450
451 return (arc);
452}
453
454/* test case */
455
456#ifdef __TMFDEBUG__
457
458int main(int argc, char *argv[])
459{
460 APIRET arc;
461 PTMFMSGFILE pMsgFile;
462
463 if (argc < 3)
464 printf("tmsgfile <file> <msgid>\n");
465 else
466 {
467 if (!(arc = tmfOpenMessageFile(argv[1], &pMsgFile)))
468 {
469 XSTRING str;
470 xstrInit(&str, 0);
471 if (!(arc = tmfGetMessage(pMsgFile,
472 argv[2],
473 &str,
474 NULL,
475 0)))
476 {
477 printf("String:\n%s", str.psz);
478 }
479 else
480 printf("tmfGetMessage returned %d\n", arc);
481
482 xstrClear(&str);
483
484 tmfCloseMessageFile(&pMsgFile);
485 }
486 else
487 printf("tmfOpenMessageFile returned %d\n", arc);
488 }
489}
490
491#endif
Note: See TracBrowser for help on using the repository browser.