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

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

Executable updates, mostly.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 15.9 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 NULL)))
158 {
159 // file loaded:
160 // create a TMFMSGFILE entry
161 PTMFMSGFILE pFile;
162 if (!(pFile = NEW(TMFMSGFILE)))
163 {
164 arc = ERROR_NOT_ENOUGH_MEMORY;
165 free(pszContent);
166 }
167 else
168 {
169 // TMFMSGFILE created:
170
171 PCSZ pStartOfFile,
172 pStartOfMarker;
173
174 ULONG ulStartMarkerLength = strlen(G_pcszStartMarker),
175 ulEndMarkerLength = strlen(G_pcszEndMarker);
176
177 // initialize TMFMSGFILE struct
178 ZERO(pFile);
179 pFile->pszFilename = strdup(pcszMessageFile);
180 treeInit(&pFile->IDsTreeRoot, NULL);
181
182 xstrInitSet(&pFile->strContent, pszContent);
183
184 // convert to plain C format
185 xstrConvertLineFormat(&pFile->strContent,
186 CRLF2LF);
187
188 // kick out all the comments
189 while (pStartOfMarker = strstr(pFile->strContent.psz, "\n;"))
190 {
191 // copy the next line over this
192 PCSZ pEOL = strhFindEOL(pStartOfMarker + 2, NULL);
193 /* printf("pStartOfMarker = %lX, pEOL = %lX\n",
194 pStartOfMarker,
195 pEOL); */
196 xstrrpl(&pFile->strContent,
197 // ofs of first char to replace: "\n;"
198 pStartOfMarker - pFile->strContent.psz,
199 // no. of chars to replace:
200 pEOL - pStartOfMarker,
201 // string to replace chars with:
202 NULL,
203 // length of replacement string:
204 0);
205 }
206
207 // free excessive memory
208 xstrShrink(&pFile->strContent);
209
210 pStartOfFile = pFile->strContent.psz;
211
212 // go build a tree of all message IDs...
213
214 // find first start message marker
215 pStartOfMarker = strstr(pStartOfFile,
216 G_pcszStartMarker); // start-of-line marker
217 while ( (pStartOfMarker)
218 && (!arc)
219 )
220 {
221 // start marker found:
222 PCSZ pStartOfMsgID = pStartOfMarker + ulStartMarkerLength;
223 // search next start marker
224 PCSZ pStartOfNextMarker = strstr(pStartOfMsgID + 1,
225 G_pcszStartMarker);
226 // and the end-marker
227 PCSZ pEndOfMarker = strstr(pStartOfMsgID + 1,
228 G_pcszEndMarker);
229
230 PMSGENTRY pNew;
231
232 // sanity checks...
233
234 if ( (pStartOfNextMarker)
235 && (pStartOfNextMarker < pEndOfMarker)
236 )
237 {
238 // next start marker before end marker:
239 // that doesn't look correct, skip this entry
240 pStartOfMarker = pStartOfNextMarker;
241 continue;
242 }
243
244 if (!pEndOfMarker)
245 // no end marker found:
246 // that's invalid too, and there can't be any
247 // message left in the file then...
248 break;
249
250 // alright, this ID looks correct now
251 if (!(pNew = NEW(MSGENTRY)))
252 arc = ERROR_NOT_ENOUGH_MEMORY;
253 else
254 {
255 // length of the ID
256 ULONG ulIDLength = pEndOfMarker - pStartOfMsgID;
257 PCSZ pStartOfText = pEndOfMarker + ulEndMarkerLength;
258
259 ZERO(pNew);
260
261 // copy the string ID (between start and end markers)
262 xstrInit(&pNew->strID, 0);
263 xstrcpy(&pNew->strID,
264 pStartOfMsgID,
265 ulIDLength);
266 // make ulKey point to the string ID for tree sorting
267 pNew->Tree.ulKey = (ULONG)pNew->strID.psz;
268
269 // skip leading spaces
270 while (*pStartOfText == ' ')
271 pStartOfText++;
272
273 // store start of text
274 pNew->ulOfsText = pStartOfText - pStartOfFile;
275
276 // check if there's a comment before the
277 // next item
278 /* if (pNextComment = strstr(pStartOfText, "\n;"))
279 {
280 if ( (!pStartOfNextMarker)
281 || (pNextComment < pStartOfNextMarker)
282 )
283 pEndOfText = pNextComment;
284 } */
285
286 if (pStartOfNextMarker)
287 // other markers left:
288 pNew->cbText = // offset of next marker
289 (pStartOfNextMarker - pStartOfFile)
290 - pNew->ulOfsText;
291 else
292 // this was the last message:
293 pNew->cbText = strlen(pStartOfText);
294
295 // remove trailing newlines
296 while ( (pNew->cbText)
297 && (pStartOfText[pNew->cbText-1] == '\n')
298 )
299 (pNew->cbText)--;
300
301 // store this thing
302 if (!treeInsert(&pFile->IDsTreeRoot,
303 NULL,
304 (TREE*)pNew,
305 treeCompareStrings))
306 // successfully inserted:
307 (pFile->cIDs)++;
308 }
309
310 // go on with next start marker (can be NULL)
311 pStartOfMarker = pStartOfNextMarker;
312 } // end while ( (pStartOfMarker) ...
313
314 // done with IDs, or error occured:
315 if (!arc)
316 // output
317 *ppMsgFile = pFile;
318 else
319 // error:
320 tmfCloseMessageFile(&pFile);
321
322 } // end else if (!(pFile = NEW(TMFMSGFILE)))
323 } // end if (!(arc = doshLoadTextFile(pcszMessageFile,
324
325 return (arc);
326}
327
328/*
329 *@@ tmfCloseMessageFile:
330 * closes a message file opened by
331 * tmfOpenMessageFile, frees all resources,
332 * and sets *ppMsgFile to NULL for safety.
333 *
334 *@@added V0.9.16 (2001-10-08) [umoeller]
335 */
336
337APIRET tmfCloseMessageFile(PTMFMSGFILE *ppMsgFile)
338{
339 if (ppMsgFile && *ppMsgFile)
340 {
341 PTMFMSGFILE pFile = *ppMsgFile;
342 LONG cItems;
343 TREE** papNodes;
344
345 if (pFile->pszFilename)
346 free(pFile->pszFilename);
347 xstrClear(&pFile->strContent);
348
349 if (cItems = pFile->cIDs)
350 {
351 if (papNodes = treeBuildArray(pFile->IDsTreeRoot,
352 &cItems))
353 {
354 ULONG ul;
355 for (ul = 0; ul < cItems; ul++)
356 {
357 PMSGENTRY pNodeThis = (PMSGENTRY)(papNodes[ul]);
358
359 xstrClear(&pNodeThis->strID);
360
361 free(pNodeThis);
362 }
363
364 free(papNodes);
365 }
366 }
367
368 free(pFile);
369 *ppMsgFile = NULL;
370
371 return (NO_ERROR);
372 }
373
374 return (ERROR_INVALID_PARAMETER);
375}
376
377/*
378 *@@ tmfGetMessage:
379 * replacement for DosGetMessage.
380 *
381 * After you have opened a .TMF file with tmfOpenMessageFile,
382 * you can pass it to this function to retrieve a message
383 * with the given string (!) ID. See tmsgfile.c for details.
384 *
385 * Note that this will invoke xstrcpy on the given XSTRING
386 * buffer. In other words, the string must be initialized
387 * (see xstrInit), but will be replaced.
388 *
389 * This does perform the same simple string replacements
390 * as DosGetMessage, that is, the string "%1" will be
391 * replaced with pTable[0], "%2" will be replaced with
392 * pTable[1], and so on.
393 *
394 * Returns:
395 *
396 * -- NO_ERROR;
397 *
398 * -- ERROR_INVALID_PARAMETER
399 *
400 * -- ERROR_MR_MID_NOT_FOUND
401 *
402 *@@added V0.9.16 (2001-10-08) [umoeller]
403 */
404
405APIRET tmfGetMessage(PTMFMSGFILE pMsgFile, // in: msg file opened by tmfOpenMessageFile
406 PCSZ pcszMessageName, // in: msg name to look for (case-sensitive!)
407 PXSTRING pstr, // out: message string, if found (XSTRING must be initialized)
408 PSZ *pTable, // in: replacement table or NULL
409 ULONG cTableEntries) // in: count of items in pTable or null
410{
411 APIRET arc = NO_ERROR;
412
413 if (!pMsgFile)
414 arc = ERROR_INVALID_PARAMETER;
415 else
416 {
417 // go find the message in the tree
418 PMSGENTRY pEntry;
419 if (pEntry = (PMSGENTRY)treeFind(pMsgFile->IDsTreeRoot,
420 (ULONG)pcszMessageName,
421 treeCompareStrings))
422 {
423 // copy the raw string to the output buffer
424 xstrcpy(pstr,
425 pMsgFile->strContent.psz + pEntry->ulOfsText,
426 pEntry->cbText);
427
428 // now replace strings from the table
429 if (cTableEntries && pTable)
430 {
431 CHAR szFind[10] = "%0";
432 ULONG ul;
433 for (ul = 0;
434 ul < cTableEntries;
435 ul++)
436 {
437 ULONG ulOfs = 0;
438
439 _ultoa(ul + 1, szFind + 1, 10);
440 while (xstrFindReplaceC(pstr,
441 &ulOfs,
442 szFind,
443 pTable[ul]))
444 ;
445 }
446 }
447 }
448 else
449 arc = ERROR_MR_MID_NOT_FOUND;
450 }
451
452 return (arc);
453}
454
455/* test case */
456
457#ifdef __TMFDEBUG__
458
459int main(int argc, char *argv[])
460{
461 APIRET arc;
462 PTMFMSGFILE pMsgFile;
463
464 if (argc < 3)
465 printf("tmsgfile <file> <msgid>\n");
466 else
467 {
468 if (!(arc = tmfOpenMessageFile(argv[1], &pMsgFile)))
469 {
470 XSTRING str;
471 xstrInit(&str, 0);
472 if (!(arc = tmfGetMessage(pMsgFile,
473 argv[2],
474 &str,
475 NULL,
476 0)))
477 {
478 printf("String:\n%s", str.psz);
479 }
480 else
481 printf("tmfGetMessage returned %d\n", arc);
482
483 xstrClear(&str);
484
485 tmfCloseMessageFile(&pMsgFile);
486 }
487 else
488 printf("tmfOpenMessageFile returned %d\n", arc);
489 }
490}
491
492#endif
Note: See TracBrowser for help on using the repository browser.