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

Last change on this file since 8 was 8, checked in by umoeller, 25 years ago

Initial checkin of helpers code which used to be in WarpIN.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 29.6 KB
Line 
1
2/*
3 *@@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:
15 *
16 * 1) No external utility is necessary to change message
17 * files. Simply edit the text, and on the next call,
18 * the file is recompiled at run-time.
19 *
20 * 2) To identify messages, any string can be used, not
21 * only numerical IDs.
22 *
23 * This file is entirely new with V0.9.0.
24 *
25 * Usage: All OS/2 programs.
26 *
27 * Function prefixes:
28 * -- tmf* text message file functions
29 *
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 *
42 * Note: Version numbering in this file relates to XWorkplace version
43 * numbering.
44 *
45 *@@header "helpers\tmsgfile.h"
46 *@@added V0.9.0 [umoeller]
47 */
48
49/*
50 * Copyright (C) 1999 Christian Langanke.
51 * Copyright (C) 1999-2000 Ulrich M”ller.
52 * This file is part of the XWorkplace source package.
53 * XWorkplace is free software; you can redistribute it and/or modify
54 * it under the terms of the GNU General Public License as published
55 * by the Free Software Foundation, in version 2 as it comes in the
56 * "COPYING" file of the XWorkplace main distribution.
57 * This program is distributed in the hope that it will be useful,
58 * but WITHOUT ANY WARRANTY; without even the implied warranty of
59 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
60 * GNU General Public License for more details.
61 */
62
63#define OS2EMX_PLAIN_CHAR
64 // this is needed for "os2emx.h"; if this is defined,
65 // emx will define PSZ as _signed_ char, otherwise
66 // as unsigned char
67
68#define INCL_DOS
69#define INCL_ERRORS
70#include <os2.h>
71
72#include <stdio.h>
73#include <stdlib.h>
74#include <string.h>
75#include <stdarg.h>
76
77#include "setup.h" // code generation and debugging options
78
79#include "helpers\eah.h"
80#include "helpers\tmsgfile.h"
81
82/*
83 *@@category: Helpers\Control program helpers\Text message files (TMF)
84 */
85
86/* ******************************************************************
87 * *
88 * Declarations *
89 * *
90 ********************************************************************/
91
92// extended attribute used for timestamp; written to .TMF file
93#define EA_TIMESTAMP "TMF.FILEINFO"
94// extended attribute used for compiled text file; written to .TMF file
95#define EA_MSGTABLE "TMF.MSGTABLE"
96
97#define NEWLINE "\n"
98
99// start-of-msgid marker
100#define MSG_NAME_START "\r\n<--"
101// end-of-msgid marker
102#define MSG_NAME_END "-->:" // fixed: the space had to be removed, because
103 // if a newline was the first character after ":",
104 // we got complete garbage (crashes) (99-10-23) [umoeller]
105// comment marker
106#define MSG_COMMENT_LINE "\r\n;"
107
108// internal prototypes
109APIRET CompileMsgTable(PSZ pszMessageFile, PBYTE * ppbTableData);
110APIRET GetTimeStamp(PFILESTATUS3 pfs3, PSZ pszBuffer, ULONG ulBufferlen);
111
112/* ******************************************************************
113 * *
114 * Text Message File Code *
115 * *
116 ********************************************************************/
117
118/*
119 *@@ tmfGetMessage:
120 * just like DosGetMessage, except that this does not
121 * take a simple message number for the message in the
122 * given message file, but a PSZ message identifier
123 * (pszMessageName).
124 *
125 * This function automatically compiles the given .TMF
126 * file into the file's extended attributes, if we find
127 * that this is necessary, by calling CompileMsgTable.
128 *
129 * Note that for compatibily, this function performs the
130 * same brain-dead string processing as DosGetMessage, that
131 * is, the return string is _not_ null-terminated, and
132 * trailing newline characters are _not_ cut off.
133 *
134 * If you don't like this, call tmfGetMessageExt, which
135 * calls this function in turn.
136 *
137 * <B>Returns:</B>
138 * -- ERROR_INVALID_PARAMETER
139 * -- ERROR_BUFFER_OVERFLOW
140 * -- ERROR_MR_MID_NOT_FOUND
141 * -- ERROR_MR_INV_MSGF_FORMAT
142 * -- ERROR_BUFFER_OVERFLOW
143 *
144 * plus the error codes of DosOpen, DosRead, DosClose.
145 */
146
147APIRET tmfGetMessage(PCHAR* pTable, // in: pointer table for string insertion
148 ULONG cTable, // in: number of pointers in *pTable
149 PBYTE pbBuffer, // out: returned string
150 ULONG cbBuffer, // out: sizeof(*pbBuffer)
151 PSZ pszMessageName, // in: message identifier
152 PSZ pszFile, // in: message file (.TMF extension proposed)
153 PULONG pcbMsg) // out: bytes written to *pbBuffer
154{
155 // fixed UM 99-10-22: now initializing all vars to 0,
156 // because ulBytesRead might be returned from this func
157 APIRET rc = NO_ERROR;
158 CHAR szMessageFile[_MAX_PATH];
159 PBYTE pbTableData = NULL;
160 PSZ pszEntry = 0;
161 PSZ pszEndOfEntry = 0;
162 ULONG ulMessagePos = 0;
163 ULONG ulMessageLen = 0;
164
165 HFILE hfile = NULLHANDLE;
166 ULONG ulFilePtr = 0;
167 ULONG ulAction = 0;
168 ULONG ulBytesToRead = 0;
169 ULONG ulBytesRead = 0;
170
171 do
172 {
173 // check parms
174 if ((!pbBuffer) ||
175 (!cbBuffer) ||
176 (!pszMessageName) ||
177 (!*pszMessageName) ||
178 (!*pszFile))
179 {
180 rc = ERROR_INVALID_PARAMETER;
181 break;
182 }
183
184 if (cbBuffer < 2)
185 {
186 rc = ERROR_BUFFER_OVERFLOW;
187 break;
188 }
189
190 // search file
191 if ((strchr(pszFile, ':')) ||
192 (strchr(pszFile, '\\')) ||
193 (strchr(pszFile, '/')))
194 // drive and/or path given: no search in path
195 strcpy(szMessageFile, pszFile);
196 else
197 {
198 // only filename, search in current dir and DPATH
199 rc = DosSearchPath(SEARCH_IGNORENETERRS
200 | SEARCH_ENVIRONMENT
201 | SEARCH_CUR_DIRECTORY,
202 "DPATH",
203 pszFile,
204 szMessageFile,
205 sizeof(szMessageFile));
206 if (rc != NO_ERROR)
207 break;
208 }
209
210 // _Pmpf(("tmfGetMessage: Found %s", szMessageFile));
211
212 // compile table if neccessary
213 rc = CompileMsgTable(szMessageFile, &pbTableData);
214 if (rc != NO_ERROR)
215 break;
216
217 // _Pmpf(("tmfGetMessage: Now searching compiled file"));
218
219 // search the name
220 pszEntry = strstr(pbTableData, pszMessageName);
221 if (!pszEntry)
222 {
223 rc = ERROR_MR_MID_NOT_FOUND;
224 break;
225 }
226 else
227 pszEntry += strlen(pszMessageName) + 1;
228
229 // isolate entry
230 pszEndOfEntry = strchr(pszEntry, '\n');
231 if (pszEndOfEntry)
232 *pszEndOfEntry = 0;
233
234 // get numbers
235 ulMessagePos = atol(pszEntry);
236 if (ulMessagePos == 0)
237 if (!pszEntry)
238 {
239 rc = ERROR_MR_INV_MSGF_FORMAT;
240 break;
241 }
242
243 pszEntry = strchr(pszEntry, ' ');
244 if (!pszEntry)
245 {
246 rc = ERROR_MR_INV_MSGF_FORMAT;
247 break;
248 }
249 ulMessageLen = atol(pszEntry);
250
251 // determine maximum read len
252 ulBytesToRead = _min(ulMessageLen, cbBuffer);
253
254 // open file and read message
255 rc = DosOpen(pszFile,
256 &hfile,
257 &ulAction,
258 0, 0,
259 OPEN_ACTION_FAIL_IF_NEW
260 | OPEN_ACTION_OPEN_IF_EXISTS,
261 OPEN_FLAGS_FAIL_ON_ERROR
262 | OPEN_SHARE_DENYWRITE
263 | OPEN_ACCESS_READONLY,
264 NULL);
265 if (rc != NO_ERROR)
266 break;
267
268 rc = DosSetFilePtr(hfile, ulMessagePos, FILE_BEGIN, &ulFilePtr);
269 if ((rc != NO_ERROR) || (ulFilePtr != ulMessagePos))
270 break;
271
272 rc = DosRead(hfile,
273 pbBuffer,
274 ulBytesToRead,
275 &ulBytesRead);
276 if (rc != NO_ERROR)
277 break;
278
279 // report "buffer too small" here
280 if (ulBytesToRead < ulMessageLen)
281 rc = ERROR_BUFFER_OVERFLOW;
282
283 }
284 while (FALSE);
285
286 // replace string placeholders ("%1" etc.) (*UM)
287 if (rc == NO_ERROR) // added (99-10-24) [umoeller]
288 if (cTable)
289 {
290 // create a temporary buffer for replacements
291 PSZ pszTemp = (PSZ)malloc(cbBuffer); // ### memory leak here!
292
293 if (!pszTemp)
294 rc = ERROR_NOT_ENOUGH_MEMORY;
295 else
296 {
297 // two pointers for copying
298 PSZ pSource = pbBuffer,
299 pTarget = pszTemp;
300
301 CHAR szMsgNum[5] = "0";
302 ULONG ulTableIndex = 0;
303
304 ULONG ulCount = 0, ulTargetWritten = 0;
305
306 // 1) copy the stuff we've read to the
307 // new temporary buffer
308 for (ulCount = 0;
309 ulCount < ulBytesRead;
310 ulCount++)
311 *pTarget++ = *pSource++;
312
313 // 2) now go thru the source string
314 // (which we've read above) and
315 // look for "%" characters; we do
316 // this while copying the stuff
317 // back to pbBuffer
318
319 pSource = pszTemp;
320 pTarget = pbBuffer;
321
322 for (ulCount = 0;
323 ulCount < ulBytesRead;
324 ulCount++)
325 {
326 if (*pSource == '%')
327 {
328 // copy the index (should be > 0)
329 szMsgNum[0] = *(pSource + 1);
330 // szMsgNum[1] is still 0
331
332 // Pmpf((" Found %s", szMsgNum));
333
334 ulTableIndex = atoi(szMsgNum);
335 // _Pmpf((" --> %d", ulTableIndex));
336 if ((ulTableIndex != 0)
337 && (ulTableIndex <= cTable)
338 )
339 {
340 // valid index:
341 // insert stuff from table
342 PSZ *ppTableThis = (PSZ*)(pTable + (ulTableIndex - 1));
343
344 // (pTable) if 1, (pTable+1) if 2, ...
345 PSZ pTableSource = *ppTableThis;
346
347 // copy string from table
348 // and increase the target pointer
349 while ((*pTarget++ = *pTableSource++))
350 ulTargetWritten++;
351
352 // increase source pointer to point
353 // behind the "%x"
354 pSource += 2;
355 // decrease target again, because
356 // we've copied a null-byte
357 pTarget--;
358
359 ulCount++;
360 // next for
361 continue;
362 }
363 // else invalid index: just copy
364 }
365
366 // regular character: copy
367 *pTarget++ = *pSource++;
368 ulTargetWritten++;
369 } // end for (ulCount = 0) ...
370
371 ulBytesRead = ulTargetWritten;
372 }
373 } // end if (cTable)
374
375 // report bytes written (*UM)
376 if (pcbMsg)
377 *pcbMsg = ulBytesRead;
378
379 // cleanup
380 if (hfile)
381 DosClose(hfile);
382 if (pbTableData)
383 free(pbTableData);
384
385 return (rc);
386}
387
388/*
389 *@@ tmfGetMessageExt:
390 * just like tmfGetMessage, but this func is smart
391 * enough to return a zero-terminated string and
392 * strip trailing newlines.
393 *
394 * See tmfGetMessage for details.
395 */
396
397APIRET tmfGetMessageExt(PCHAR* pTable, // in: pointer table for string insertion
398 ULONG cTable, // in: number of pointers in *pTable
399 PBYTE pbBuffer, // out: returned string
400 ULONG cbBuffer, // out: sizeof(*pbBuffer)
401 PSZ pszMessageName, // in: message identifier
402 PSZ pszFile, // in: message file (.TMF extension proposed)
403 PULONG pcbMsg) // out: bytes written to *pbBuffer
404{
405 APIRET arc = NO_ERROR;
406 ULONG cbReturned = 0;
407 arc = tmfGetMessage(pTable, cTable, pbBuffer, cbBuffer, pszMessageName, pszFile,
408 &cbReturned);
409
410 if (arc == NO_ERROR)
411 {
412 PSZ p = pbBuffer;
413
414 // terminate string
415 if (cbReturned < cbBuffer)
416 *(pbBuffer+cbReturned) = 0;
417 else
418 *(pbBuffer+cbBuffer-1) = 0;
419
420 // remove leading spaces (99-10-24) [umoeller]
421 while (*p == ' ')
422 p++;
423 if (p != pbBuffer)
424 {
425 // we have leading spaces:
426 strcpy(pbBuffer, p);
427 // decrease byte count
428 cbReturned -= (p - pbBuffer);
429 }
430
431 // remove trailing newlines
432 while (TRUE)
433 {
434 p = pbBuffer + strlen(pbBuffer) - 1;
435 if ( (*p == '\n')
436 || (*p == '\r')
437 )
438 {
439 *p = '\0';
440 } else
441 break; // while (TRUE)
442 }
443 }
444
445 if (pcbMsg)
446 *pcbMsg = cbReturned;
447
448 return (arc);
449}
450
451/*
452 *@@ CompileMsgTable:
453 * this gets called from tmfGetMessage.
454 * Here we check for whether the given
455 * message file has been compiled to the
456 * EAs yet or if the compilation is
457 * outdated.
458 *
459 * If we need recompilation, we do this here.
460 * Memory for the compiled message table is
461 * allocated here using malloc(), and the
462 * buffer is stored in ppbTableData. You
463 * must free() this buffer after using this
464 * function.
465 *
466 * <B>Returns:</B>
467 * -- ERROR_INVALID_PARAMETER
468 * -- ERROR_BUFFER_OVERFLOW
469 * -- ERROR_NOT_ENOUGH_MEMORY
470 * -- ERROR_READ_FAULT
471 * -- ERROR_INVALID_DATA
472 *
473 *@@changed V0.9.1 (99-12-12) [umoeller]: fixed heap overwrites which caused irregular crashes
474 *@@changed V0.9.1 (99-12-12) [umoeller]: fixed realloc failure
475 *@@changed V0.9.1 (99-12-12) [umoeller]: file last-write date is now reset when EAs are written
476 *@@changed V0.9.1 (2000-02-01) [umoeller]: now skipping leading spaces in msg
477 */
478
479APIRET CompileMsgTable(PSZ pszMessageFile, // in: file to compile
480 PBYTE *ppbTableDataReturn) // out: compiled message table
481{
482 APIRET rc = NO_ERROR;
483 // CHAR szMessageFile[_MAX_PATH];
484
485 FILESTATUS3 fs3MessageFile;
486
487 // fixed UM 99-10-22: initializing all vars to 0
488 ULONG ulStampLength = 0;
489 PBYTE pbFileData = NULL;
490 ULONG ulFileDataLength = 0;
491
492 CHAR szFileStampOld[18] = ""; // yyyymmddhhmmssms.
493
494 CHAR szFileStampCurrent[18] = "";
495
496 PBYTE pbTableData = NULL;
497 ULONG cbTableDataAllocated = 0;
498 ULONG cbTableDataUsed = 0;
499
500 HFILE hfileMessageFile = NULLHANDLE;
501 ULONG ulAction = 0;
502 ULONG ulBytesRead = 0;
503
504 COUNTRYCODE cc = {0, 0};
505
506 ULONG cbOpenTag = strlen(MSG_NAME_START);
507 ULONG cbClosingTag = strlen(MSG_NAME_END);
508
509 do
510 {
511 PSZ pCurrentNameStart = 0;
512 PSZ pCurrentNameEnd = 0;
513 ULONG ulCurrentMessagePos = 0;
514 ULONG ulCurrentMessageLen = 0;
515 // BOOL fError = FALSE;
516
517 // check parms
518 if ((!pszMessageFile) ||
519 (!ppbTableDataReturn))
520 {
521 rc = ERROR_INVALID_PARAMETER;
522 break;
523 }
524
525 // get length and timestamp of file
526 rc = DosQueryPathInfo(pszMessageFile,
527 FIL_STANDARD,
528 &fs3MessageFile,
529 sizeof(fs3MessageFile));
530 if (rc != NO_ERROR)
531 break;
532 ulFileDataLength = fs3MessageFile.cbFile;
533
534 // determine current timestamp
535 GetTimeStamp(&fs3MessageFile, szFileStampCurrent, sizeof(szFileStampCurrent));
536
537 _Pmpf((" Timestamp for %s: '%s'", pszMessageFile, szFileStampCurrent));
538
539 // determine saved timestamp
540 ulStampLength = sizeof(szFileStampOld);
541 rc = eahReadStringEA(pszMessageFile,
542 EA_TIMESTAMP,
543 szFileStampOld,
544 &ulStampLength);
545
546 _Pmpf((" Saved timestamp for %s: '%s'", pszMessageFile, szFileStampOld));
547
548 // compare timestamps
549 if ((rc == NO_ERROR)
550 && (ulStampLength == (strlen(szFileStampCurrent) + 1))
551 && (!strcmp(szFileStampCurrent, szFileStampOld))
552 )
553 {
554
555 // read table out of EAs
556 do
557 {
558 // get ea length of table
559 rc = eahReadStringEA(pszMessageFile,
560 EA_MSGTABLE,
561 NULL, &cbTableDataAllocated);
562 if (rc != ERROR_BUFFER_OVERFLOW)
563 break;
564
565 // get memory
566 if ((pbTableData = (PBYTE)malloc(cbTableDataAllocated)) == NULL)
567 {
568 rc = ERROR_NOT_ENOUGH_MEMORY;
569 break;
570 }
571
572 // read table
573 rc = eahReadStringEA(pszMessageFile,
574 EA_MSGTABLE,
575 pbTableData, &cbTableDataAllocated);
576
577 }
578 while (FALSE);
579
580 // if no error occurred, we are finished
581 if (rc == NO_ERROR)
582 {
583 _Pmpf((" --> using precompiled table"));
584 break;
585 }
586 } // end if
587
588 _Pmpf((" --> recompiling table"));
589
590 // recompilation needed:
591 // get memory for file data
592 if ((pbFileData = (PBYTE)malloc(ulFileDataLength + 1)) == NULL)
593 {
594 rc = ERROR_NOT_ENOUGH_MEMORY;
595 break;
596 }
597 *(pbFileData + ulFileDataLength) = 0;
598
599 // get memory for table data
600 cbTableDataAllocated = ulFileDataLength / 2;
601 if ((pbTableData = (PBYTE)malloc(cbTableDataAllocated)) == NULL)
602 {
603 rc = ERROR_NOT_ENOUGH_MEMORY;
604 break;
605 }
606
607 *pbTableData = 0;
608 // fixed V0.9.1 (99-12-12);
609 // this was
610 // *(pbTableData + cbTableDataAllocated) = 0;
611 // since we're using "strcat" below to write into
612 // the table data, this wasn't such a good idea
613
614 // open file and read it
615 rc = DosOpen(pszMessageFile,
616 &hfileMessageFile,
617 &ulAction,
618 0, 0,
619 OPEN_ACTION_FAIL_IF_NEW
620 | OPEN_ACTION_OPEN_IF_EXISTS,
621 OPEN_FLAGS_FAIL_ON_ERROR
622 | OPEN_SHARE_DENYWRITE
623 | OPEN_ACCESS_READWRITE, // needed for EA attachement
624 NULL);
625 if (rc != NO_ERROR)
626 break;
627
628 rc = DosRead(hfileMessageFile,
629 pbFileData,
630 ulFileDataLength,
631 &ulBytesRead);
632 if (rc != NO_ERROR)
633 break;
634 if (ulBytesRead != ulFileDataLength)
635 {
636 rc = ERROR_READ_FAULT;
637 break;
638 }
639
640 // search first message name
641 // this will automatically skip comments
642 // at the beginning
643 pCurrentNameStart = strstr(pbFileData, MSG_NAME_START);
644
645 if (!pCurrentNameStart)
646 {
647 rc = ERROR_INVALID_DATA;
648 break;
649 }
650 else
651 pCurrentNameStart += cbOpenTag;
652 // this points to message ID string after "<--" now
653
654 // is first name complete ?
655 pCurrentNameEnd = strstr(pCurrentNameStart, MSG_NAME_END);
656 if (!pCurrentNameEnd)
657 {
658 rc = ERROR_INVALID_DATA;
659 break;
660 }
661
662 // scan through all names
663 while ( (pCurrentNameStart)
664 && (*pCurrentNameStart)
665 )
666 {
667 PSZ pNextNameStart = 0,
668 pNextCommentStart = 0,
669 pEndOfMsg = 0;
670 CHAR szEntry[500] = "";
671
672 // search end of name, if not exist, skip end of file
673 pCurrentNameEnd = strstr(pCurrentNameStart, MSG_NAME_END);
674 // this points to the closing "-->" now
675 if (!pCurrentNameEnd)
676 break;
677
678 // search next name (ID), if none, use end of string
679 pNextNameStart = strstr(pCurrentNameEnd, MSG_NAME_START);
680 // points to next "<--" now
681 if (!pNextNameStart)
682 // not found:
683 // use terminating null byte
684 pNextNameStart = pCurrentNameStart + strlen(pCurrentNameStart);
685 else
686 // found:
687 // point to next message ID string
688 pNextNameStart += cbOpenTag;
689
690 pEndOfMsg = pNextNameStart - cbOpenTag;
691
692 // search next comment (99-11-28) [umoeller]
693 pNextCommentStart = strstr(pCurrentNameEnd, MSG_COMMENT_LINE);
694 if (pNextCommentStart)
695 // another comment found:
696 if (pNextCommentStart < pNextNameStart)
697 // if it's even before the next key, use that
698 // for the end of the current string; otherwise
699 // the comment would appear in the message too
700 pEndOfMsg = pNextCommentStart;
701
702 // now we have:
703 // -- pCurrentNameStart: points to message ID after "<--"
704 // -- pCurrentNameEnd: points to "-->"
705 // -- pNextNameStart: points to _next_ message ID after "<--"
706 // -- pNextCommentStart: points to _next_ comment
707 // -- pEndOfMsg: either pNextNameStart-cbOpenTag
708 // or pNextCommentStart,
709 // whichever comes first
710
711 // calculate table entry data
712 *pCurrentNameEnd = 0;
713 ulCurrentMessagePos = (pCurrentNameEnd - pbFileData) + cbClosingTag;
714 // this points to the first character after the <-- --> tag now;
715 // if this points to a space character, take next char
716 // (V0.9.1 (2000-02-13) [umoeller])
717 while ( (*(pbFileData + ulCurrentMessagePos))
718 && (*(pbFileData + ulCurrentMessagePos) == ' ')
719 )
720 ulCurrentMessagePos++;
721
722 ulCurrentMessageLen = (pEndOfMsg - pbFileData) - ulCurrentMessagePos;
723 // pszNextNameStart - pbFileData - ulCurrentMessagePos - 1;
724
725 // determine entry
726 sprintf(szEntry, "%s %u %u" NEWLINE,
727 pCurrentNameStart,
728 ulCurrentMessagePos,
729 ulCurrentMessageLen);
730
731 _Pmpf(("Found %s at %d, length %d",
732 pCurrentNameStart,
733 ulCurrentMessagePos,
734 ulCurrentMessageLen));
735
736 // need more space ?
737 if ((cbTableDataUsed + strlen(szEntry) + 1) > cbTableDataAllocated)
738 {
739 PBYTE pbTmp;
740 _Pmpf((" Re-allocating!!"));
741
742 cbTableDataAllocated += ulFileDataLength / 2;
743 pbTmp = (PBYTE)realloc(pbTableData, cbTableDataAllocated);
744 if (!pbTmp)
745 {
746 rc = ERROR_NOT_ENOUGH_MEMORY;
747 break;
748 }
749 else
750 pbTableData = pbTmp;
751 }
752
753 // add entry
754 strcat(pbTableData, szEntry);
755 cbTableDataUsed += strlen(szEntry);
756 // added V0.9.1 (99-12-12);
757 // this was 0 all the time and never raised
758
759 // adress next entry
760 pCurrentNameStart = pNextNameStart;
761
762 } // while (pszCurrentNameStart)
763
764 // write new timestamp and table
765 // ### handle 64 kb limit here !!!
766 if (rc == NO_ERROR)
767 {
768 FILESTATUS3 fs3Tmp;
769
770 rc = eahWriteStringEA(hfileMessageFile, EA_TIMESTAMP, szFileStampCurrent);
771 rc = eahWriteStringEA(hfileMessageFile, EA_MSGTABLE, pbTableData);
772
773 if (rc == NO_ERROR)
774 {
775 // fixed V0.9.1 (99-12-12): we need to reset the file date to
776 // the old date,
777 // because writing EAs updates the "last write date" also;
778 // without this, we'd recompile every single time
779 rc = DosQueryFileInfo(hfileMessageFile,
780 FIL_STANDARD,
781 &fs3Tmp,
782 sizeof(fs3Tmp));
783 if (rc == NO_ERROR)
784 {
785 // restore original "last write" date
786 _Pmpf(("Resetting file date"));
787 fs3Tmp.fdateLastWrite = fs3MessageFile.fdateLastWrite;
788 fs3Tmp.ftimeLastWrite = fs3MessageFile.ftimeLastWrite;
789 DosSetFileInfo(hfileMessageFile,
790 FIL_STANDARD,
791 &fs3Tmp,
792 sizeof(fs3Tmp));
793 }
794 }
795 }
796 } while (FALSE);
797
798
799 if (rc == NO_ERROR)
800 {
801 // hand over result
802 *ppbTableDataReturn = pbTableData;
803
804 // make text uppercase
805 rc = DosMapCase(cbTableDataAllocated, &cc, pbTableData);
806 }
807 else
808 {
809 // error occured:
810 if (pbTableData)
811 free(pbTableData);
812 }
813
814 // cleanup
815 if (pbFileData)
816 free(pbFileData);
817 if (hfileMessageFile)
818 DosClose(hfileMessageFile);
819
820 return rc;
821}
822
823/*
824 * GetTimeStamp:
825 * this returns the "last write date" timestamp
826 * of the file specified in pfs3.
827 *
828 * <B>Usage:</B> called from CompileMsgTable
829 * to check whether the compilation is
830 * outdated.
831 *
832 * Returns:
833 * -- NO_ERROR
834 * -- ERROR_INVALID_PARAMETER
835 * -- ERROR_BUFFER_OVERFLOW: pszBuffer too small for timestamp
836 */
837
838APIRET GetTimeStamp(PFILESTATUS3 pfs3, // in: file to check
839 PSZ pszBuffer, // out: timestamp
840 ULONG ulBufferlen) // in: sizeof(*pszBuffer)
841{
842
843 APIRET rc = NO_ERROR;
844 CHAR szTimeStamp[15];
845 static PSZ pszFormatTimestamp = "%4u%02u%02u%02u%02u%02u%";
846
847 do
848 {
849 // check parms
850 if ((!pfs3) ||
851 (!pszBuffer))
852 {
853 rc = ERROR_INVALID_PARAMETER;
854 break;
855 }
856
857 // create stamp
858 sprintf(szTimeStamp,
859 pszFormatTimestamp,
860 pfs3->fdateLastWrite.year + 1980,
861 pfs3->fdateLastWrite.month,
862 pfs3->fdateLastWrite.day,
863 pfs3->ftimeLastWrite.hours,
864 pfs3->ftimeLastWrite.minutes,
865 pfs3->ftimeLastWrite.twosecs * 2);
866
867 // check bufferlen
868 if (strlen(szTimeStamp) + 1 > ulBufferlen)
869 {
870 rc = ERROR_BUFFER_OVERFLOW;
871 break;
872 }
873
874 // hand over result
875 strcpy(pszBuffer, szTimeStamp);
876 }
877 while (FALSE);
878
879 return rc;
880}
881
Note: See TracBrowser for help on using the repository browser.