3 * Copyright (C) 2002-2005 A.J. van Os; Released under GNU GPL
6 * Read the summary information of a Word document
13 #define P_HEADER_SZ 28
14 #define P_SECTIONLIST_SZ 20
16 #define P_SECTION_MAX_SZ (2 * P_SECTIONLIST_SZ + P_LENGTH_SZ)
17 #define P_SECTION_SZ(x) ((x) * P_SECTIONLIST_SZ + P_LENGTH_SZ)
22 #define PID_CREATE_DTM 12
23 #define PID_LASTSAVE_DTM 13
24 #define PID_APPNAME 18
26 #define PIDD_MANAGER 14
27 #define PIDD_COMPANY 15
30 #define VT_FILETIME 64
32 #define TIME_OFFSET_HI 0x019db1de
33 #define TIME_OFFSET_LO 0xd53e8000
35 static char *szTitle
= NULL
;
36 static char *szSubject
= NULL
;
37 static char *szAuthor
= NULL
;
38 static time_t tCreateDtm
= (time_t)-1;
39 static time_t tLastSaveDtm
= (time_t)-1;
40 static char *szAppName
= NULL
;
41 static char *szManager
= NULL
;
42 static char *szCompany
= NULL
;
43 static USHORT usLid
= (USHORT
)-1;
47 * vDestroySummaryInfo - destroy the summary information
50 vDestroySummaryInfo(void)
52 TRACE_MSG("vDestroySummaryInfo");
54 szTitle
= xfree(szTitle
);
55 szSubject
= xfree(szSubject
);
56 szAuthor
= xfree(szAuthor
);
57 tCreateDtm
= (time_t)-1;
58 tLastSaveDtm
= (time_t)-1;
59 szAppName
= xfree(szAppName
);
60 szManager
= xfree(szManager
);
61 szCompany
= xfree(szCompany
);
63 } /* end of vDestroySummaryInfo */
66 * tConvertDosDate - convert DOS date format
68 * returns Unix time_t or -1
71 tConvertDosDate(const char *szDosDate
)
77 memset(&tTime
, 0, sizeof(tTime
));
80 if (!isdigit(*pcTmp
)) {
83 tTime
.tm_mon
= (int)(*pcTmp
- '0');
85 if (isdigit(*pcTmp
)) {
87 tTime
.tm_mon
+= (int)(*pcTmp
- '0');
90 /* Get the first separater */
91 if (isalnum(*pcTmp
)) {
96 if (!isdigit(*pcTmp
)) {
99 tTime
.tm_mday
= (int)(*pcTmp
- '0');
101 if (isdigit(*pcTmp
)) {
103 tTime
.tm_mday
+= (int)(*pcTmp
- '0');
106 /* Get the second separater */
107 if (isalnum(*pcTmp
)) {
112 if (!isdigit(*pcTmp
)) {
115 tTime
.tm_year
= (int)(*pcTmp
- '0');
117 if (isdigit(*pcTmp
)) {
119 tTime
.tm_year
+= (int)(*pcTmp
- '0');
122 /* Check the values */
123 if (tTime
.tm_mon
== 0 || tTime
.tm_mday
== 0 || tTime
.tm_mday
> 31) {
126 /* Correct the values */
127 tTime
.tm_mon
--; /* From 01-12 to 00-11 */
128 if (tTime
.tm_year
< 80) {
129 tTime
.tm_year
+= 100; /* 00 means 2000 is 100 */
132 tResult
= mktime(&tTime
);
133 NO_DBG_MSG(ctime(&tResult
));
135 } /* end of tConvertDosDate */
138 * szLpstr - get a zero terminate string property
141 szLpstr(ULONG ulOffset
, const UCHAR
*aucBuffer
)
143 char *szStart
, *szResult
, *szTmp
;
146 tSize
= (size_t)ulGetLong(ulOffset
+ 4, aucBuffer
);
151 /* Remove white space from the start of the string */
152 szStart
= (char *)aucBuffer
+ ulOffset
+ 8;
154 fail(strlen(szStart
) >= tSize
);
155 while (isspace(*szStart
)) {
158 if (szStart
[0] == '\0') {
161 szResult
= xstrdup(szStart
);
162 /* Remove white space from the end of the string */
163 szTmp
= szResult
+ strlen(szResult
) - 1;
164 while (isspace(*szTmp
)) {
168 NO_DBG_MSG(szResult
);
170 } /* end of szLpstr */
173 * tFiletime - get a filetime property
176 tFiletime(ULONG ulOffset
, const UCHAR
*aucBuffer
)
178 double dHi
, dLo
, dTmp
;
182 ulLo
= ulGetLong(ulOffset
+ 4, aucBuffer
);
183 ulHi
= ulGetLong(ulOffset
+ 8, aucBuffer
);
187 /* Move the starting point from 01 Jan 1601 to 01 Jan 1970 */
188 dHi
= (double)ulHi
- (double)TIME_OFFSET_HI
;
189 dLo
= (double)ulLo
- (double)TIME_OFFSET_LO
;
193 /* Combine the values and divide by 10^7 to get seconds */
194 dTmp
= dLo
/ 10000000.0; /* 10^7 */
195 dTmp
+= dHi
* 429.4967926; /* 2^32 / 10^7 */
199 if (dTmp
- 0.5 < TIME_T_MIN
|| dTmp
+ 0.5 > TIME_T_MAX
) {
202 tResult
= dTmp
< 0.0 ? (time_t)(dTmp
- 0.5) : (time_t)(dTmp
+ 0.5);
203 NO_DBG_MSG(ctime(&tResult
));
205 } /* end of tFiletime */
208 * vAnalyseSummaryInfo - analyse the summary information
211 vAnalyseSummaryInfo(const UCHAR
*aucBuffer
)
214 size_t tIndex
, tCount
, tPropID
, tPropType
;
216 tCount
= (size_t)ulGetLong(4, aucBuffer
);
218 for (tIndex
= 0; tIndex
< tCount
; tIndex
++) {
219 tPropID
= (size_t)ulGetLong(8 + tIndex
* 8, aucBuffer
);
220 ulOffset
= ulGetLong(12 + tIndex
* 8, aucBuffer
);
222 NO_DBG_HEX(ulOffset
);
223 tPropType
= (size_t)ulGetLong(ulOffset
, aucBuffer
);
224 NO_DBG_DEC(tPropType
);
227 if (tPropType
== VT_LPSTR
&& szTitle
== NULL
) {
228 szTitle
= szLpstr(ulOffset
, aucBuffer
);
232 if (tPropType
== VT_LPSTR
&& szSubject
== NULL
) {
233 szSubject
= szLpstr(ulOffset
, aucBuffer
);
237 if (tPropType
== VT_LPSTR
&& szAuthor
== NULL
) {
238 szAuthor
= szLpstr(ulOffset
, aucBuffer
);
242 if (tPropType
== VT_FILETIME
&&
243 tCreateDtm
== (time_t)-1) {
244 tCreateDtm
= tFiletime(ulOffset
, aucBuffer
);
247 case PID_LASTSAVE_DTM
:
248 if (tPropType
== VT_FILETIME
&&
249 tLastSaveDtm
== (time_t)-1) {
250 tLastSaveDtm
= tFiletime(ulOffset
, aucBuffer
);
254 if (tPropType
== VT_LPSTR
&& szAppName
== NULL
) {
255 szAppName
= szLpstr(ulOffset
, aucBuffer
);
262 } /* end of vAnalyseSummaryInfo */
265 * vAnalyseDocumentSummaryInfo - analyse the document summary information
268 vAnalyseDocumentSummaryInfo(const UCHAR
*aucBuffer
)
271 size_t tIndex
, tCount
, tPropID
, tPropType
;
273 tCount
= (size_t)ulGetLong(4, aucBuffer
);
275 for (tIndex
= 0; tIndex
< tCount
; tIndex
++) {
276 tPropID
= (size_t)ulGetLong(8 + tIndex
* 8, aucBuffer
);
277 ulOffset
= ulGetLong(12 + tIndex
* 8, aucBuffer
);
279 NO_DBG_HEX(ulOffset
);
280 tPropType
= (size_t)ulGetLong(ulOffset
, aucBuffer
);
281 NO_DBG_DEC(tPropType
);
284 if (tPropType
== VT_LPSTR
&& szManager
== NULL
) {
285 szManager
= szLpstr(ulOffset
, aucBuffer
);
289 if (tPropType
== VT_LPSTR
&& szCompany
== NULL
) {
290 szCompany
= szLpstr(ulOffset
, aucBuffer
);
297 } /* end of vAnalyseDocumentSummaryInfo */
300 * pucAnalyseSummaryInfoHeader-
303 pucAnalyseSummaryInfoHeader(FILE *pFile
,
304 ULONG ulStartBlock
, ULONG ulSize
,
305 const ULONG
*aulBBD
, size_t tBBDLen
,
306 const ULONG
*aulSBD
, size_t tSBDLen
)
308 const ULONG
*aulBlockDepot
;
310 size_t tBlockDepotLen
, tBlockSize
, tSectionCount
, tLength
;
311 ULONG ulTmp
, ulOffset
;
312 USHORT usLittleEndian
, usEmpty
, usOS
, usVersion
;
313 UCHAR aucHdr
[P_HEADER_SZ
], aucSecLst
[P_SECTION_MAX_SZ
];
315 if (ulSize
< MIN_SIZE_FOR_BBD_USE
) {
316 /* Use the Small Block Depot */
317 aulBlockDepot
= aulSBD
;
318 tBlockDepotLen
= tSBDLen
;
319 tBlockSize
= SMALL_BLOCK_SIZE
;
321 /* Use the Big Block Depot */
322 aulBlockDepot
= aulBBD
;
323 tBlockDepotLen
= tBBDLen
;
324 tBlockSize
= BIG_BLOCK_SIZE
;
327 if (tBlockDepotLen
== 0) {
328 DBG_MSG("The Block Depot length is zero");
332 /* Read the Summery Information header */
333 if (!bReadBuffer(pFile
, ulStartBlock
,
334 aulBlockDepot
, tBlockDepotLen
, tBlockSize
,
335 aucHdr
, 0, P_HEADER_SZ
)) {
338 NO_DBG_PRINT_BLOCK(aucHdr
, P_HEADER_SZ
);
340 /* Analyse the Summery Information header */
341 usLittleEndian
= usGetWord(0, aucHdr
);
342 if (usLittleEndian
!= 0xfffe) {
343 DBG_HEX(usLittleEndian
);
344 DBG_MSG_C(usLittleEndian
== 0xfeff, "Big endian");
347 usEmpty
= usGetWord(2, aucHdr
);
348 if (usEmpty
!= 0x0000) {
352 ulTmp
= ulGetLong(4, aucHdr
);
354 usOS
= (USHORT
)(ulTmp
>> 16);
355 usVersion
= (USHORT
)(ulTmp
& 0xffff);
374 tSectionCount
= (size_t)ulGetLong(24, aucHdr
);
375 DBG_DEC_C(tSectionCount
!= 1 && tSectionCount
!= 2, tSectionCount
);
376 if (tSectionCount
!= 1 && tSectionCount
!= 2) {
380 /* Read the Summery Information Section Lists */
381 if (!bReadBuffer(pFile
, ulStartBlock
,
382 aulBlockDepot
, tBlockDepotLen
, tBlockSize
,
383 aucSecLst
, P_HEADER_SZ
, P_SECTION_SZ(tSectionCount
))) {
386 NO_DBG_PRINT_BLOCK(aucSecLst
, P_SECTION_SZ(tSectionCount
));
388 ulTmp
= ulGetLong(0, aucSecLst
);
390 ulTmp
= ulGetLong(4, aucSecLst
);
392 ulTmp
= ulGetLong(8, aucSecLst
);
394 ulTmp
= ulGetLong(12, aucSecLst
);
396 ulOffset
= ulGetLong(16, aucSecLst
);
397 DBG_DEC_C(ulOffset
!= P_HEADER_SZ
+ P_SECTIONLIST_SZ
&&
398 ulOffset
!= P_HEADER_SZ
+ 2 * P_SECTIONLIST_SZ
,
400 fail(ulOffset
!= P_HEADER_SZ
+ P_SECTIONLIST_SZ
&&
401 ulOffset
!= P_HEADER_SZ
+ 2 * P_SECTIONLIST_SZ
);
403 (size_t)ulGetLong(tSectionCount
* P_SECTIONLIST_SZ
, aucSecLst
);
405 fail(ulOffset
+ tLength
> ulSize
);
407 /* Read the Summery Information */
408 aucBuffer
= xmalloc(tLength
);
409 if (!bReadBuffer(pFile
, ulStartBlock
,
410 aulBlockDepot
, tBlockDepotLen
, tBlockSize
,
411 aucBuffer
, ulOffset
, tLength
)) {
412 aucBuffer
= xfree(aucBuffer
);
415 NO_DBG_PRINT_BLOCK(aucBuffer
, tLength
);
417 } /* end of pucAnalyseSummaryInfoHeader */
420 * vSet0SummaryInfo - set summary information from a Word for DOS file
423 vSet0SummaryInfo(FILE *pFile
, const UCHAR
*aucHeader
)
426 ULONG ulBeginSumdInfo
, ulBeginNextBlock
;
428 USHORT usCodepage
, usOffset
;
430 TRACE_MSG("vSet0SummaryInfo");
432 fail(pFile
== NULL
|| aucHeader
== NULL
);
434 /* First check the header */
435 usCodepage
= usGetWord(0x7e, aucHeader
);
437 switch (usCodepage
) {
438 case 850: usLid
= 0x0809; break; /* Latin1 -> British English */
439 case 862: usLid
= 0x040d; break; /* Hebrew */
440 case 866: usLid
= 0x0419; break; /* Russian */
443 default: usLid
= 0x0409; break; /* ASCII -> American English */
446 /* Second check the summary information block */
447 ulBeginSumdInfo
= 128 * (ULONG
)usGetWord(0x1c, aucHeader
);
448 DBG_HEX(ulBeginSumdInfo
);
449 ulBeginNextBlock
= 128 * (ULONG
)usGetWord(0x6a, aucHeader
);
450 DBG_HEX(ulBeginNextBlock
);
452 if (ulBeginSumdInfo
>= ulBeginNextBlock
|| ulBeginNextBlock
== 0) {
453 /* There is no summary information block */
456 tLen
= (size_t)(ulBeginNextBlock
- ulBeginSumdInfo
);
457 aucBuffer
= xmalloc(tLen
);
458 /* Read the summary information block */
459 if (!bReadBytes(aucBuffer
, tLen
, ulBeginSumdInfo
, pFile
)) {
462 usOffset
= usGetWord(0, aucBuffer
);
463 if (aucBuffer
[usOffset
] != 0) {
464 NO_DBG_MSG(aucBuffer
+ usOffset
);
465 szTitle
= xstrdup((char *)aucBuffer
+ usOffset
);
467 usOffset
= usGetWord(2, aucBuffer
);
468 if (aucBuffer
[usOffset
] != 0) {
469 NO_DBG_MSG(aucBuffer
+ usOffset
);
470 szAuthor
= xstrdup((char *)aucBuffer
+ usOffset
);
472 usOffset
= usGetWord(12, aucBuffer
);
473 if (aucBuffer
[usOffset
] != 0) {
474 NO_DBG_STRN(aucBuffer
+ usOffset
, 8);
475 tLastSaveDtm
= tConvertDosDate((char *)aucBuffer
+ usOffset
);
477 usOffset
= usGetWord(14, aucBuffer
);
478 if (aucBuffer
[usOffset
] != 0) {
479 NO_DBG_STRN(aucBuffer
+ usOffset
, 8);
480 tCreateDtm
= tConvertDosDate((char *)aucBuffer
+ usOffset
);
482 aucBuffer
= xfree(aucBuffer
);
483 } /* end of vSet0SummaryInfo */
486 * vSet2SummaryInfo - set summary information from a WinWord 1/2 file
489 vSet2SummaryInfo(FILE *pFile
, int iWordVersion
, const UCHAR
*aucHeader
)
492 ULONG ulBeginSumdInfo
, ulBeginDocpInfo
, ulTmp
;
493 size_t tSumdInfoLen
, tDocpInfoLen
, tLen
, tCounter
, tStart
;
495 TRACE_MSG("vSet2SummaryInfo");
497 fail(pFile
== NULL
|| aucHeader
== NULL
);
498 fail(iWordVersion
!= 1 && iWordVersion
!= 2);
500 /* First check the header */
501 usLid
= usGetWord(0x06, aucHeader
); /* Language IDentification */
503 if (usLid
< 999 && iWordVersion
== 1) {
505 case 1: usLid
= 0x0409; break; /* American English */
506 case 2: usLid
= 0x0c0c; break; /* Canadian French */
507 case 31: usLid
= 0x0413; break; /* Dutch */
508 case 33: usLid
= 0x040c; break; /* French */
509 case 34: usLid
= 0x040a; break; /* Spanish */
510 case 36: usLid
= 0x040e; break; /* Hungarian */
511 case 39: usLid
= 0x0410; break; /* Italian */
512 case 44: usLid
= 0x0809; break; /* British English */
513 case 45: usLid
= 0x0406; break; /* Danish */
514 case 46: usLid
= 0x041f; break; /* Swedish */
515 case 47: usLid
= 0x0414; break; /* Norwegian */
516 case 48: usLid
= 0x0415; break; /* Polish */
517 case 49: usLid
= 0x0407; break; /* German */
518 case 351: usLid
= 0x0816; break; /* Portuguese */
519 case 358: usLid
= 0x040b; break; /* Finnish */
523 usLid
= 0x0409; /* American English */
528 if (iWordVersion
!= 2) {
529 /* Unknown where to find the associated strings */
533 /* Second check the associated strings */
534 ulBeginSumdInfo
= ulGetLong(0x118, aucHeader
); /* fcSttbfAssoc */
535 DBG_HEX(ulBeginSumdInfo
);
536 tSumdInfoLen
= (size_t)usGetWord(0x11c, aucHeader
); /* cbSttbfAssoc */
537 DBG_DEC(tSumdInfoLen
);
539 if (tSumdInfoLen
== 0) {
540 /* There is no summary information */
544 aucBuffer
= xmalloc(tSumdInfoLen
);
545 if (!bReadBytes(aucBuffer
, tSumdInfoLen
, ulBeginSumdInfo
, pFile
)) {
546 aucBuffer
= xfree(aucBuffer
);
549 NO_DBG_PRINT_BLOCK(aucBuffer
, tSumdInfoLen
);
550 tLen
= (size_t)ucGetByte(0, aucBuffer
);
551 DBG_DEC_C(tSumdInfoLen
!= tLen
, tSumdInfoLen
);
552 DBG_DEC_C(tSumdInfoLen
!= tLen
, tLen
);
554 for (tCounter
= 0; tCounter
< 17; tCounter
++) {
555 if (tStart
>= tSumdInfoLen
) {
558 tLen
= (size_t)ucGetByte(tStart
, aucBuffer
);
560 NO_DBG_DEC(tCounter
);
561 NO_DBG_STRN(aucBuffer
+ tStart
+ 1, tLen
);
564 szTitle
= xmalloc(tLen
+ 1);
566 (char *)aucBuffer
+ tStart
+ 1, tLen
);
567 szTitle
[tLen
] = '\0';
570 szSubject
= xmalloc(tLen
+ 1);
572 (char *)aucBuffer
+ tStart
+ 1, tLen
);
573 szSubject
[tLen
] = '\0';
576 szAuthor
= xmalloc(tLen
+ 1);
578 (char *)aucBuffer
+ tStart
+ 1, tLen
);
579 szAuthor
[tLen
] = '\0';
587 aucBuffer
= xfree(aucBuffer
);
589 /* Third check the document properties */
590 ulBeginDocpInfo
= ulGetLong(0x112, aucHeader
); /* fcDop */
591 DBG_HEX(ulBeginDocpInfo
);
592 tDocpInfoLen
= (size_t)usGetWord(0x116, aucHeader
); /* cbDop */
593 DBG_DEC(tDocpInfoLen
);
594 if (tDocpInfoLen
< 12) {
598 aucBuffer
= xmalloc(tDocpInfoLen
);
599 if (!bReadBytes(aucBuffer
, tDocpInfoLen
, ulBeginDocpInfo
, pFile
)) {
600 aucBuffer
= xfree(aucBuffer
);
603 ulTmp
= ulGetLong(0x14, aucBuffer
); /* dttmCreated */
604 tCreateDtm
= tConvertDTTM(ulTmp
);
605 ulTmp
= ulGetLong(0x18, aucBuffer
); /* dttmRevised */
606 tLastSaveDtm
= tConvertDTTM(ulTmp
);
607 aucBuffer
= xfree(aucBuffer
);
608 } /* end of vSet2SummaryInfo */
611 * vSetSummaryInfoOLE - set summary information from a Word 6+ file
614 vSetSummaryInfoOLE(FILE *pFile
, const pps_info_type
*pPPS
,
615 const ULONG
*aulBBD
, size_t tBBDLen
,
616 const ULONG
*aulSBD
, size_t tSBDLen
)
620 fail(pFile
== NULL
|| pPPS
== NULL
);
621 fail(aulBBD
== NULL
|| aulSBD
== NULL
);
623 /* Summary Information */
624 pucBuffer
= pucAnalyseSummaryInfoHeader(pFile
,
625 pPPS
->tSummaryInfo
.ulSB
, pPPS
->tSummaryInfo
.ulSize
,
626 aulBBD
, tBBDLen
, aulSBD
, tSBDLen
);
627 if (pucBuffer
!= NULL
) {
628 vAnalyseSummaryInfo(pucBuffer
);
629 pucBuffer
= xfree(pucBuffer
);
632 /* Document Summary Information */
633 pucBuffer
= pucAnalyseSummaryInfoHeader(pFile
,
634 pPPS
->tDocSummaryInfo
.ulSB
, pPPS
->tDocSummaryInfo
.ulSize
,
635 aulBBD
, tBBDLen
, aulSBD
, tSBDLen
);
636 if (pucBuffer
!= NULL
) {
637 vAnalyseDocumentSummaryInfo(pucBuffer
);
638 pucBuffer
= xfree(pucBuffer
);
640 } /* end of vSetSummaryInfoOLE */
643 * vSet6SummaryInfo - set summary information from a Word 6/7 file
646 vSet6SummaryInfo(FILE *pFile
, const pps_info_type
*pPPS
,
647 const ULONG
*aulBBD
, size_t tBBDLen
,
648 const ULONG
*aulSBD
, size_t tSBDLen
,
649 const UCHAR
*aucHeader
)
651 TRACE_MSG("vSet6SummaryInfo");
653 /* Header Information */
654 usLid
= usGetWord(0x06, aucHeader
); /* Language IDentification */
657 /* Summery Information */
658 vSetSummaryInfoOLE(pFile
, pPPS
, aulBBD
, tBBDLen
, aulSBD
, tSBDLen
);
659 } /* end of vSet6SummaryInfo */
662 * vSet8SummaryInfo - set summary information a Word 8/9/10 file
665 vSet8SummaryInfo(FILE *pFile
, const pps_info_type
*pPPS
,
666 const ULONG
*aulBBD
, size_t tBBDLen
,
667 const ULONG
*aulSBD
, size_t tSBDLen
,
668 const UCHAR
*aucHeader
)
672 TRACE_MSG("vSet8SummaryInfo");
674 /* Header Information */
675 usTmp
= usGetWord(0x0a, aucHeader
);
676 if (usTmp
& BIT(14)) {
677 /* Language IDentification Far East */
678 usLid
= usGetWord(0x3c, aucHeader
);
680 /* Language IDentification */
681 usLid
= usGetWord(0x06, aucHeader
);
685 /* Summery Information */
686 vSetSummaryInfoOLE(pFile
, pPPS
, aulBBD
, tBBDLen
, aulSBD
, tSBDLen
);
687 } /* end of vSet8SummaryInfo */
690 * szGetTitle - get the title field
696 } /* end of szGetTitle */
699 * szGetSubject - get the subject field
705 } /* end of szGetSubject */
708 * szGetAuthor - get the author field
714 } /* end of szGetAuthor */
717 * szGetLastSaveDtm - get the last save date field
720 szGetLastSaveDtm(void)
722 static char szTime
[12];
725 if (tLastSaveDtm
== (time_t)-1) {
728 pTime
= localtime(&tLastSaveDtm
);
732 sprintf(szTime
, "%04d-%02d-%02d",
733 pTime
->tm_year
+ 1900, pTime
->tm_mon
+ 1, pTime
->tm_mday
);
735 } /* end of szGetLastSaveDtm */
738 * szGetModDate - get the last save date field
743 static char szTime
[20];
746 if (tLastSaveDtm
== (time_t)-1) {
749 pTime
= localtime(&tLastSaveDtm
);
753 sprintf(szTime
, "D:%04d%02d%02d%02d%02d",
754 pTime
->tm_year
+ 1900, pTime
->tm_mon
+ 1, pTime
->tm_mday
,
755 pTime
->tm_hour
, pTime
->tm_min
);
757 } /* end of szGetModDate */
760 * szGetCreationDate - get the last save date field
763 szGetCreationDate(void)
765 static char szTime
[20];
768 if (tCreateDtm
== (time_t)-1) {
771 pTime
= localtime(&tCreateDtm
);
775 sprintf(szTime
, "D:%04d%02d%02d%02d%02d",
776 pTime
->tm_year
+ 1900, pTime
->tm_mon
+ 1, pTime
->tm_mday
,
777 pTime
->tm_hour
, pTime
->tm_min
);
779 } /* end of szGetCreationDate */
782 * szGetCompany - get the company field
788 } /* end of szGetCompany */
791 * szGetLanguage - get de language field
796 if (usLid
== (USHORT
)-1) {
797 /* No Language IDentification */
801 /* This is a Locale, not a Language IDentification */
806 /* Exceptions to the general rule */
808 case 0x0404: return "zh_TW"; /* Traditional Chinese */
809 case 0x0804: return "zh_CN"; /* Simplified Chinese */
810 case 0x0c04: return "zh_HK"; /* Hong Kong Chinese */
811 case 0x1004: return "zh_SG"; /* Singapore Chinese */
812 case 0x0807: return "de_CH"; /* Swiss German */
813 case 0x0409: return "en_US"; /* American English */
814 case 0x0809: return "en_GB"; /* British English */
815 case 0x0c09: return "en_AU"; /* Australian English */
816 case 0x080a: return "es_MX"; /* Mexican Spanish */
817 case 0x080c: return "fr_BE"; /* Belgian French */
818 case 0x0c0c: return "fr_CA"; /* Canadian French */
819 case 0x100c: return "fr_CH"; /* Swiss French */
820 case 0x0810: return "it_CH"; /* Swiss Italian */
821 case 0x0813: return "nl_BE"; /* Belgian Dutch */
822 case 0x0416: return "pt_BR"; /* Brazilian Portuguese */
824 case 0x0c1a: return "sr"; /* Serbian */
825 case 0x081d: return "sv_FI"; /* Finland Swedish */
830 /* The general rule */
831 switch (usLid
& 0x00ff) {
832 case 0x01: return "ar"; /* Arabic */
833 case 0x02: return "bg"; /* Bulgarian */
834 case 0x03: return "ca"; /* Catalan */
835 case 0x04: return "zh"; /* Chinese */
836 case 0x05: return "cs"; /* Czech */
837 case 0x06: return "da"; /* Danish */
838 case 0x07: return "de"; /* German */
839 case 0x08: return "el"; /* Greek */
840 case 0x09: return "en"; /* English */
841 case 0x0a: return "es"; /* Spanish */
842 case 0x0b: return "fi"; /* Finnish */
843 case 0x0c: return "fr"; /* French */
844 case 0x0d: return "he"; /* Hebrew */
845 case 0x0e: return "hu"; /* Hungarian */
846 case 0x0f: return "is"; /* Icelandic */
847 case 0x10: return "it"; /* Italian */
848 case 0x11: return "ja"; /* Japanese */
849 case 0x12: return "ko"; /* Korean */
850 case 0x13: return "nl"; /* Dutch */
851 case 0x14: return "no"; /* Norwegian */
852 case 0x15: return "pl"; /* Polish */
853 case 0x16: return "pt"; /* Portuguese */
854 case 0x17: return "rm"; /* Rhaeto-Romance */
855 case 0x18: return "ro"; /* Romanian */
856 case 0x19: return "ru"; /* Russian */
857 case 0x1a: return "hr"; /* Croatian */
858 case 0x1b: return "sk"; /* Slovak */
859 case 0x1c: return "sq"; /* Albanian */
860 case 0x1d: return "sv"; /* Swedish */
861 case 0x1e: return "th"; /* Thai */
862 case 0x1f: return "tr"; /* Turkish */
863 case 0x20: return "ur"; /* Urdu */
864 case 0x21: return "id"; /* Indonesian */
865 case 0x22: return "uk"; /* Ukrainian */
866 case 0x23: return "be"; /* Belarusian */
867 case 0x24: return "sl"; /* Slovenian */
868 case 0x25: return "et"; /* Estonian */
869 case 0x26: return "lv"; /* Latvian */
870 case 0x27: return "lt"; /* Lithuanian */
871 case 0x29: return "fa"; /* Farsi */
872 case 0x2a: return "vi"; /* Viet Nam */
873 case 0x2b: return "hy"; /* Armenian */
874 case 0x2c: return "az"; /* Azeri */
875 case 0x2d: return "eu"; /* Basque */
876 case 0x2f: return "mk"; /* Macedonian */
877 case 0x36: return "af"; /* Afrikaans */
878 case 0x37: return "ka"; /* Georgian */
879 case 0x38: return "fo"; /* Faeroese */
880 case 0x39: return "hi"; /* Hindi */
881 case 0x3e: return "ms"; /* Malay */
882 case 0x3f: return "kk"; /* Kazakh */
888 } /* end of szGetLanguage */