mirror of
https://github.com/simon987/antiword.git
synced 2025-04-10 13:06:41 +00:00
895 lines
21 KiB
C
895 lines
21 KiB
C
/*
|
|
* misc.c
|
|
* Copyright (C) 1998-2005 A.J. van Os; Released under GNU GPL
|
|
*
|
|
* Description:
|
|
* Miscellaneous functions
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
#if defined(__riscos)
|
|
#include "DeskLib:SWI.h"
|
|
#else
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#endif /* __riscos */
|
|
#if !defined(S_ISREG)
|
|
#define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
|
|
#endif /* !S_ISREG */
|
|
#include "antiword.h"
|
|
#if defined(__vms)
|
|
#include <unixlib.h>
|
|
#endif
|
|
|
|
#if !defined(__riscos)
|
|
/*
|
|
* szGetHomeDirectory - get the name of the home directory
|
|
*/
|
|
const char *
|
|
szGetHomeDirectory(void)
|
|
{
|
|
const char *szHome;
|
|
|
|
#if defined(__vms)
|
|
szHome = decc$translate_vms(getenv("HOME"));
|
|
#elif defined(__Plan9__)
|
|
szHome = getenv("home");
|
|
#else
|
|
szHome = getenv("HOME");
|
|
#endif /* __vms */
|
|
|
|
if (szHome == NULL || szHome[0] == '\0') {
|
|
#if defined(N_PLAT_NLM)
|
|
szHome = "SYS:";
|
|
#elif defined(__dos)
|
|
szHome = "C:";
|
|
#else
|
|
werr(0, "I can't find the name of your HOME directory");
|
|
szHome = "";
|
|
#endif /* __dos */
|
|
}
|
|
return szHome;
|
|
} /* end of szGetHomeDirectory */
|
|
|
|
/*
|
|
* szGetAntiwordDirectory - get the name of the Antiword directory
|
|
*/
|
|
const char *
|
|
szGetAntiwordDirectory(void)
|
|
{
|
|
#if defined(__vms)
|
|
return decc$translate_vms(getenv("ANTIWORDHOME"));
|
|
#else
|
|
return getenv("ANTIWORDHOME");
|
|
#endif /* __vms */
|
|
} /* end of szGetAntiwordDirectory */
|
|
#endif /* !__riscos */
|
|
|
|
/*
|
|
* Get the size of the specified file.
|
|
* Returns -1 if the file does not exist or is not a proper file.
|
|
*/
|
|
long
|
|
lGetFilesize(const char *szFilename)
|
|
{
|
|
#if defined(__riscos)
|
|
os_error *e;
|
|
int iType, iSize;
|
|
|
|
e = SWI(2, 5, SWI_OS_File | XOS_Bit,
|
|
17, szFilename,
|
|
&iType, NULL, NULL, NULL, &iSize);
|
|
if (e != NULL) {
|
|
werr(0, "Get Filesize error %d: %s",
|
|
e->errnum, e->errmess);
|
|
return -1;
|
|
}
|
|
if (iType != 1) {
|
|
/* It's not a proper file or the file does not exist */
|
|
return -1;
|
|
}
|
|
return (long)iSize;
|
|
#else
|
|
struct stat tBuffer;
|
|
|
|
errno = 0;
|
|
if (stat(szFilename, &tBuffer) != 0) {
|
|
werr(0, "Get Filesize error %d", errno);
|
|
return -1;
|
|
}
|
|
if (!S_ISREG(tBuffer.st_mode)) {
|
|
/* It's not a regular file */
|
|
return -1;
|
|
}
|
|
return (long)tBuffer.st_size;
|
|
#endif /* __riscos */
|
|
} /* end of lGetFilesize */
|
|
|
|
#if defined(DEBUG)
|
|
void
|
|
vPrintBlock(const char *szFile, int iLine,
|
|
const UCHAR *aucBlock, size_t tLength)
|
|
{
|
|
int i, j;
|
|
|
|
fail(szFile == NULL || iLine < 0 || aucBlock == NULL);
|
|
|
|
fprintf(stderr, "%s[%3d]:\n", szFile, iLine);
|
|
for (i = 0; i < 32; i++) {
|
|
if (16 * i >= (int)tLength) {
|
|
return;
|
|
}
|
|
fprintf(stderr, "%03x: ", (unsigned int)(16 * i));
|
|
for (j = 0; j < 16; j++) {
|
|
if (16 * i + j < (int)tLength) {
|
|
fprintf(stderr, "%02x ",
|
|
(unsigned int)aucBlock[16 * i + j]);
|
|
}
|
|
}
|
|
fprintf(stderr, "\n");
|
|
}
|
|
} /* end of vPrintBlock */
|
|
|
|
void
|
|
vPrintUnicode(const char *szFile, int iLine, const UCHAR *aucUni, size_t tLen)
|
|
{
|
|
char *szASCII;
|
|
|
|
fail(tLen % 2 != 0);
|
|
|
|
tLen /= 2; /* Length in bytes to length in characters */
|
|
szASCII = xmalloc(tLen + 1);
|
|
(void)unincpy(szASCII, aucUni, tLen);
|
|
szASCII[tLen] = '\0';
|
|
(void)fprintf(stderr, "%s[%3d]: %.*s\n",
|
|
szFile, iLine, (int)tLen, szASCII);
|
|
szASCII = xfree(szASCII);
|
|
} /* end of vPrintUnicode */
|
|
|
|
BOOL
|
|
bCheckDoubleLinkedList(output_type *pAnchor)
|
|
{
|
|
output_type *pCurr, *pLast;
|
|
int iInList;
|
|
|
|
pLast = pAnchor;
|
|
iInList = 0;
|
|
for (pCurr = pAnchor; pCurr != NULL; pCurr = pCurr->pNext) {
|
|
pLast = pCurr;
|
|
iInList++;
|
|
}
|
|
NO_DBG_DEC(iInList);
|
|
for (pCurr = pLast; pCurr != NULL; pCurr = pCurr->pPrev) {
|
|
pLast = pCurr;
|
|
iInList--;
|
|
}
|
|
DBG_DEC_C(iInList != 0, iInList);
|
|
return pAnchor == pLast && iInList == 0;
|
|
} /* end of bCheckDoubleLinkedList */
|
|
#endif /* DEBUG */
|
|
|
|
/*
|
|
* bReadBytes
|
|
* This function reads the specified number of bytes from the specified file,
|
|
* starting from the specified offset.
|
|
* Returns TRUE when successfull, otherwise FALSE
|
|
*/
|
|
BOOL
|
|
bReadBytes(UCHAR *aucBytes, size_t tMemb, ULONG ulOffset, FILE *pFile)
|
|
{
|
|
fail(aucBytes == NULL || pFile == NULL || ulOffset > (ULONG)LONG_MAX);
|
|
|
|
if (ulOffset > (ULONG)LONG_MAX) {
|
|
return FALSE;
|
|
}
|
|
if (fseek(pFile, (long)ulOffset, SEEK_SET) != 0) {
|
|
return FALSE;
|
|
}
|
|
if (fread(aucBytes, sizeof(UCHAR), tMemb, pFile) != tMemb) {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
} /* end of bReadBytes */
|
|
|
|
/*
|
|
* bReadBuffer
|
|
* This function fills the specified buffer with the specified number of bytes,
|
|
* starting at the specified offset within the Big/Small Block Depot.
|
|
*
|
|
* Returns TRUE when successful, otherwise FALSE
|
|
*/
|
|
BOOL
|
|
bReadBuffer(FILE *pFile, ULONG ulStartBlock,
|
|
const ULONG *aulBlockDepot, size_t tBlockDepotLen, size_t tBlockSize,
|
|
UCHAR *aucBuffer, ULONG ulOffset, size_t tToRead)
|
|
{
|
|
ULONG ulBegin, ulIndex;
|
|
size_t tLen;
|
|
|
|
fail(pFile == NULL);
|
|
fail(ulStartBlock > MAX_BLOCKNUMBER && ulStartBlock != END_OF_CHAIN);
|
|
fail(aulBlockDepot == NULL);
|
|
fail(tBlockSize != BIG_BLOCK_SIZE && tBlockSize != SMALL_BLOCK_SIZE);
|
|
fail(aucBuffer == NULL);
|
|
fail(tToRead == 0);
|
|
|
|
for (ulIndex = ulStartBlock;
|
|
ulIndex != END_OF_CHAIN && tToRead != 0;
|
|
ulIndex = aulBlockDepot[ulIndex]) {
|
|
if (ulIndex >= (ULONG)tBlockDepotLen) {
|
|
DBG_DEC(ulIndex);
|
|
DBG_DEC(tBlockDepotLen);
|
|
if (tBlockSize >= BIG_BLOCK_SIZE) {
|
|
werr(1, "The Big Block Depot is damaged");
|
|
} else {
|
|
werr(1, "The Small Block Depot is damaged");
|
|
}
|
|
}
|
|
if (ulOffset >= (ULONG)tBlockSize) {
|
|
ulOffset -= tBlockSize;
|
|
continue;
|
|
}
|
|
ulBegin = ulDepotOffset(ulIndex, tBlockSize) + ulOffset;
|
|
tLen = min(tBlockSize - (size_t)ulOffset, tToRead);
|
|
ulOffset = 0;
|
|
if (!bReadBytes(aucBuffer, tLen, ulBegin, pFile)) {
|
|
werr(0, "Read big block 0x%lx not possible", ulBegin);
|
|
return FALSE;
|
|
}
|
|
aucBuffer += tLen;
|
|
tToRead -= tLen;
|
|
}
|
|
DBG_DEC_C(tToRead != 0, tToRead);
|
|
return tToRead == 0;
|
|
} /* end of bReadBuffer */
|
|
|
|
/*
|
|
* Convert a Word colornumber into a true color for use in a drawfile
|
|
*
|
|
* Returns the true color
|
|
*/
|
|
ULONG
|
|
ulColor2Color(UCHAR ucFontColor)
|
|
{
|
|
static const ULONG aulColorTable[] = {
|
|
/* 0 */ 0x00000000UL, /* Automatic */
|
|
/* 1 */ 0x00000000UL, /* Black */
|
|
/* 2 */ 0xff000000UL, /* Blue */
|
|
/* 3 */ 0xffff0000UL, /* Turquoise */
|
|
/* 4 */ 0x00ff0000UL, /* Bright Green */
|
|
/* 5 */ 0xff00ff00UL, /* Pink */
|
|
/* 6 */ 0x0000ff00UL, /* Red */
|
|
/* 7 */ 0x00ffff00UL, /* Yellow */
|
|
/* 8 */ 0xffffff00UL, /* White */
|
|
/* 9 */ 0x80000000UL, /* Dark Blue */
|
|
/* 10 */ 0x80800000UL, /* Teal */
|
|
/* 11 */ 0x00800000UL, /* Green */
|
|
/* 12 */ 0x80008000UL, /* Violet */
|
|
/* 13 */ 0x00008000UL, /* Dark Red */
|
|
/* 14 */ 0x00808000UL, /* Dark Yellow */
|
|
/* 15 */ 0x80808000UL, /* Gray 50% */
|
|
/* 16 */ 0xc0c0c000UL, /* Gray 25% */
|
|
};
|
|
if ((size_t)ucFontColor >= elementsof(aulColorTable)) {
|
|
return aulColorTable[0];
|
|
}
|
|
return aulColorTable[(int)ucFontColor];
|
|
} /* end of ulColor2Color */
|
|
|
|
/*
|
|
* iFindSplit - find a place to split the string
|
|
*
|
|
* returns the index of the split character or -1 if no split found.
|
|
*/
|
|
static int
|
|
iFindSplit(const char *szString, size_t tStringLen)
|
|
{
|
|
size_t tSplit;
|
|
|
|
if (tStringLen == 0) {
|
|
return -1;
|
|
}
|
|
tSplit = tStringLen - 1;
|
|
while (tSplit >= 1) {
|
|
if (szString[tSplit] == ' ' ||
|
|
(szString[tSplit] == '-' && szString[tSplit - 1] != ' ')) {
|
|
return (int)tSplit;
|
|
}
|
|
tSplit--;
|
|
}
|
|
return -1;
|
|
} /* end of iFindSplit */
|
|
|
|
/*
|
|
* pSplitList - split the specified list in a printable part and a leftover part
|
|
*
|
|
* returns the pointer to the leftover part
|
|
*/
|
|
output_type *
|
|
pSplitList(output_type *pAnchor)
|
|
{
|
|
output_type *pCurr, *pLeftOver;
|
|
int iIndex;
|
|
|
|
fail(pAnchor == NULL);
|
|
|
|
for (pCurr = pAnchor; pCurr->pNext != NULL; pCurr = pCurr->pNext)
|
|
; /* EMPTY */
|
|
iIndex = -1;
|
|
for (; pCurr != NULL; pCurr = pCurr->pPrev) {
|
|
iIndex = iFindSplit(pCurr->szStorage, pCurr->tNextFree);
|
|
if (iIndex >= 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pCurr == NULL || iIndex < 0) {
|
|
/* No split, no leftover */
|
|
return NULL;
|
|
}
|
|
/* Split over the iIndex-th character */
|
|
NO_DBG_MSG("pLeftOver");
|
|
pLeftOver = xmalloc(sizeof(*pLeftOver));
|
|
fail(pCurr->tNextFree < (size_t)iIndex);
|
|
pLeftOver->tStorageSize = pCurr->tNextFree - (size_t)iIndex;
|
|
pLeftOver->szStorage = xmalloc(pLeftOver->tStorageSize);
|
|
pLeftOver->tNextFree = pCurr->tNextFree - (size_t)iIndex - 1;
|
|
(void)strncpy(pLeftOver->szStorage,
|
|
pCurr->szStorage + iIndex + 1, pLeftOver->tNextFree);
|
|
pLeftOver->szStorage[pLeftOver->tNextFree] = '\0';
|
|
NO_DBG_MSG(pLeftOver->szStorage);
|
|
pLeftOver->ucFontColor = pCurr->ucFontColor;
|
|
pLeftOver->usFontStyle = pCurr->usFontStyle;
|
|
pLeftOver->tFontRef = pCurr->tFontRef;
|
|
pLeftOver->usFontSize = pCurr->usFontSize;
|
|
pLeftOver->lStringWidth = lComputeStringWidth(
|
|
pLeftOver->szStorage,
|
|
pLeftOver->tNextFree,
|
|
pLeftOver->tFontRef,
|
|
pLeftOver->usFontSize);
|
|
pLeftOver->pPrev = NULL;
|
|
pLeftOver->pNext = pCurr->pNext;
|
|
if (pLeftOver->pNext != NULL) {
|
|
pLeftOver->pNext->pPrev = pLeftOver;
|
|
}
|
|
fail(!bCheckDoubleLinkedList(pLeftOver));
|
|
|
|
NO_DBG_MSG("pAnchor");
|
|
NO_DBG_HEX(pCurr->szStorage[iIndex]);
|
|
while (iIndex >= 0 && isspace((int)(UCHAR)pCurr->szStorage[iIndex])) {
|
|
iIndex--;
|
|
}
|
|
pCurr->tNextFree = (size_t)iIndex + 1;
|
|
pCurr->szStorage[pCurr->tNextFree] = '\0';
|
|
NO_DBG_MSG(pCurr->szStorage);
|
|
pCurr->lStringWidth = lComputeStringWidth(
|
|
pCurr->szStorage,
|
|
pCurr->tNextFree,
|
|
pCurr->tFontRef,
|
|
pCurr->usFontSize);
|
|
pCurr->pNext = NULL;
|
|
fail(!bCheckDoubleLinkedList(pAnchor));
|
|
|
|
return pLeftOver;
|
|
} /* end of pSplitList */
|
|
|
|
/*
|
|
* tNumber2Roman - convert a number to Roman Numerals
|
|
*
|
|
* returns the number of characters written
|
|
*/
|
|
size_t
|
|
tNumber2Roman(UINT uiNumber, BOOL bUpperCase, char *szOutput)
|
|
{
|
|
char *outp, *p, *q;
|
|
UINT uiNextVal, uiValue;
|
|
|
|
fail(szOutput == NULL);
|
|
|
|
uiNumber %= 4000; /* Very high numbers can't be represented */
|
|
if (uiNumber == 0) {
|
|
szOutput[0] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
outp = szOutput;
|
|
p = bUpperCase ? "M\2D\5C\2L\5X\2V\5I" : "m\2d\5c\2l\5x\2v\5i";
|
|
uiValue = 1000;
|
|
for (;;) {
|
|
while (uiNumber >= uiValue) {
|
|
*outp++ = *p;
|
|
uiNumber -= uiValue;
|
|
}
|
|
if (uiNumber == 0) {
|
|
*outp = '\0';
|
|
fail(outp < szOutput);
|
|
return (size_t)(outp - szOutput);
|
|
}
|
|
q = p + 1;
|
|
uiNextVal = uiValue / (UINT)(UCHAR)*q;
|
|
if ((int)*q == 2) { /* magic */
|
|
uiNextVal /= (UINT)(UCHAR)*(q += 2);
|
|
}
|
|
if (uiNumber + uiNextVal >= uiValue) {
|
|
*outp++ = *++q;
|
|
uiNumber += uiNextVal;
|
|
} else {
|
|
p++;
|
|
uiValue /= (UINT)(UCHAR)(*p++);
|
|
}
|
|
}
|
|
} /* end of tNumber2Roman */
|
|
|
|
/*
|
|
* iNumber2Alpha - convert a number to alphabetic "numbers"
|
|
*
|
|
* returns the number of characters written
|
|
*/
|
|
size_t
|
|
tNumber2Alpha(UINT uiNumber, BOOL bUpperCase, char *szOutput)
|
|
{
|
|
char *outp;
|
|
UINT uiTmp;
|
|
|
|
fail(szOutput == NULL);
|
|
|
|
if (uiNumber == 0) {
|
|
szOutput[0] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
outp = szOutput;
|
|
uiTmp = (UINT)(bUpperCase ? 'A': 'a');
|
|
if (uiNumber <= 26) {
|
|
uiNumber -= 1;
|
|
*outp++ = (char)(uiTmp + uiNumber);
|
|
} else if (uiNumber <= 26U + 26U*26U) {
|
|
uiNumber -= 26 + 1;
|
|
*outp++ = (char)(uiTmp + uiNumber / 26);
|
|
*outp++ = (char)(uiTmp + uiNumber % 26);
|
|
} else if (uiNumber <= 26U + 26U*26U + 26U*26U*26U) {
|
|
uiNumber -= 26 + 26*26 + 1;
|
|
*outp++ = (char)(uiTmp + uiNumber / (26*26));
|
|
*outp++ = (char)(uiTmp + uiNumber / 26 % 26);
|
|
*outp++ = (char)(uiTmp + uiNumber % 26);
|
|
}
|
|
*outp = '\0';
|
|
fail(outp < szOutput);
|
|
return (size_t)(outp - szOutput);
|
|
} /* end of tNumber2Alpha */
|
|
|
|
/*
|
|
* unincpy - copy a counted Unicode string to an single-byte string
|
|
*/
|
|
char *
|
|
unincpy(char *s1, const UCHAR *s2, size_t n)
|
|
{
|
|
char *pcDest;
|
|
ULONG ulChar;
|
|
size_t tLen;
|
|
USHORT usUni;
|
|
|
|
for (pcDest = s1, tLen = 0; tLen < n; pcDest++, tLen++) {
|
|
usUni = usGetWord(tLen * 2, s2);
|
|
if (usUni == 0) {
|
|
break;
|
|
}
|
|
ulChar = ulTranslateCharacters(usUni, 0, 8,
|
|
conversion_unknown, encoding_neutral, FALSE);
|
|
if (ulChar == IGNORE_CHARACTER) {
|
|
ulChar = (ULONG)'?';
|
|
}
|
|
*pcDest = (char)ulChar;
|
|
}
|
|
for (; tLen < n; tLen++) {
|
|
*pcDest++ = '\0';
|
|
}
|
|
return s1;
|
|
} /* end of unincpy */
|
|
|
|
/*
|
|
* unilen - calculate the length of a Unicode string
|
|
*
|
|
* returns the length in bytes
|
|
*/
|
|
size_t
|
|
unilen(const UCHAR *s)
|
|
{
|
|
size_t tLen;
|
|
USHORT usUni;
|
|
|
|
tLen = 0;
|
|
for (;;) {
|
|
usUni = usGetWord(tLen, s);
|
|
if (usUni == 0) {
|
|
return tLen;
|
|
}
|
|
tLen += 2;
|
|
}
|
|
} /* end of unilen */
|
|
|
|
/*
|
|
* szBaseName - get the basename of the specified filename
|
|
*/
|
|
const char *
|
|
szBasename(const char *szFilename)
|
|
{
|
|
const char *szTmp;
|
|
|
|
fail(szFilename == NULL);
|
|
|
|
if (szFilename == NULL || szFilename[0] == '\0') {
|
|
return "null";
|
|
}
|
|
|
|
szTmp = strrchr(szFilename, FILE_SEPARATOR[0]);
|
|
if (szTmp == NULL) {
|
|
return szFilename;
|
|
}
|
|
return ++szTmp;
|
|
} /* end of szBasename */
|
|
|
|
/*
|
|
* lComputeLeading - compute the leading
|
|
*
|
|
* NOTE: the fontsize is specified in half points
|
|
*
|
|
* Returns the leading in drawunits
|
|
*/
|
|
long
|
|
lComputeLeading(USHORT usFontSize)
|
|
{
|
|
long lLeading;
|
|
|
|
lLeading = (long)usFontSize * 500L;
|
|
if (usFontSize < 18) { /* Small text: 112% */
|
|
lLeading *= 112;
|
|
} else if (usFontSize < 28) { /* Normal text: 124% */
|
|
lLeading *= 124;
|
|
} else if (usFontSize < 48) { /* Small headlines: 104% */
|
|
lLeading *= 104;
|
|
} else { /* Large headlines: 100% */
|
|
lLeading *= 100;
|
|
}
|
|
lLeading = lMilliPoints2DrawUnits(lLeading);
|
|
lLeading += 50;
|
|
lLeading /= 100;
|
|
return lLeading;
|
|
} /* end of lComputeLeading */
|
|
|
|
/*
|
|
* Convert a UCS character to an UTF-8 string
|
|
*
|
|
* Returns the string length of the result
|
|
*/
|
|
size_t
|
|
tUcs2Utf8(ULONG ulChar, char *szResult, size_t tMaxResultLen)
|
|
{
|
|
if (szResult == NULL || tMaxResultLen == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (ulChar < 0x80 && tMaxResultLen >= 2) {
|
|
szResult[0] = (char)ulChar;
|
|
szResult[1] = '\0';
|
|
return 1;
|
|
}
|
|
if (ulChar < 0x800 && tMaxResultLen >= 3) {
|
|
szResult[0] = (char)(0xc0 | ulChar >> 6);
|
|
szResult[1] = (char)(0x80 | (ulChar & 0x3f));
|
|
szResult[2] = '\0';
|
|
return 2;
|
|
}
|
|
if (ulChar < 0x10000 && tMaxResultLen >= 4) {
|
|
szResult[0] = (char)(0xe0 | ulChar >> 12);
|
|
szResult[1] = (char)(0x80 | (ulChar >> 6 & 0x3f));
|
|
szResult[2] = (char)(0x80 | (ulChar & 0x3f));
|
|
szResult[3] = '\0';
|
|
return 3;
|
|
}
|
|
if (ulChar < 0x200000 && tMaxResultLen >= 5) {
|
|
szResult[0] = (char)(0xf0 | ulChar >> 18);
|
|
szResult[1] = (char)(0x80 | (ulChar >> 12 & 0x3f));
|
|
szResult[2] = (char)(0x80 | (ulChar >> 6 & 0x3f));
|
|
szResult[3] = (char)(0x80 | (ulChar & 0x3f));
|
|
szResult[4] = '\0';
|
|
return 4;
|
|
}
|
|
szResult[0] = '\0';
|
|
return 0;
|
|
} /* end of tUcs2Utf8 */
|
|
|
|
/*
|
|
* vGetBulletValue - get the bullet value for the conversing type and encoding
|
|
*/
|
|
void
|
|
vGetBulletValue(conversion_type eConversionType, encoding_type eEncoding,
|
|
char *szResult, size_t tMaxResultLen)
|
|
{
|
|
fail(szResult == NULL);
|
|
fail(tMaxResultLen < 2);
|
|
|
|
if (eEncoding == encoding_utf_8) {
|
|
(void)tUcs2Utf8(UNICODE_BULLET, szResult, tMaxResultLen);
|
|
} else {
|
|
szResult[0] = (char)ucGetBulletCharacter(eConversionType,
|
|
eEncoding);
|
|
szResult[1] = '\0';
|
|
}
|
|
} /* end of vGetBulletValue */
|
|
|
|
/*
|
|
* bAllZero - are all bytes zero?
|
|
*/
|
|
BOOL
|
|
bAllZero(const UCHAR *aucBytes, size_t tLength)
|
|
{
|
|
size_t tIndex;
|
|
|
|
if (aucBytes == NULL || tLength == 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
for (tIndex = 0; tIndex < tLength; tIndex++) {
|
|
if (aucBytes[tIndex] != 0) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
} /* end of bAllZero */
|
|
|
|
#if !defined(__riscos)
|
|
/*
|
|
* GetCodesetFromLocale - get the codeset from the current locale
|
|
*
|
|
* Original version: Copyright (C) 1999 Bruno Haible
|
|
* Syntax:
|
|
* language[_territory][.codeset][@modifier][+special][,[sponsor][_revision]]
|
|
*
|
|
* Returns TRUE when sucessful, otherwise FALSE
|
|
*/
|
|
static BOOL
|
|
bGetCodesetFromLocale(char *szCodeset, size_t tMaxCodesetLength, BOOL *pbEuro)
|
|
{
|
|
#if !defined(__dos)
|
|
const char *szLocale;
|
|
const char *pcTmp;
|
|
size_t tIndex;
|
|
char szModifier[6];
|
|
#endif /* __dos */
|
|
|
|
if (pbEuro != NULL) {
|
|
*pbEuro = FALSE; /* Until proven otherwise */
|
|
}
|
|
if (szCodeset == NULL || tMaxCodesetLength == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
#if defined(__dos)
|
|
if (tMaxCodesetLength < 2 + sizeof(int) * 3 + 1) {
|
|
DBG_DEC(tMaxCodesetLength);
|
|
DBG_DEC(2 + sizeof(int) * 3 + 1);
|
|
return FALSE;
|
|
}
|
|
/* Get the active codepage from DOS */
|
|
sprintf(szCodeset, "cp%d", iGetCodepage());
|
|
DBG_MSG(szCodeset);
|
|
#else
|
|
/* Get the locale from the environment */
|
|
szLocale = getenv("LC_ALL");
|
|
if (szLocale == NULL || szLocale[0] == '\0') {
|
|
szLocale = getenv("LC_CTYPE");
|
|
if (szLocale == NULL || szLocale[0] == '\0') {
|
|
szLocale = getenv("LANG");
|
|
}
|
|
}
|
|
if (szLocale == NULL || szLocale[0] == '\0') {
|
|
/* No locale, so no codeset name and no modifier */
|
|
return FALSE;
|
|
}
|
|
DBG_MSG(szLocale);
|
|
pcTmp = strchr(szLocale, '.');
|
|
if (pcTmp == NULL) {
|
|
/* No codeset name */
|
|
szCodeset[0] = '\0';
|
|
} else {
|
|
/* Copy the codeset name */
|
|
pcTmp++;
|
|
for (tIndex = 0; tIndex < tMaxCodesetLength; tIndex++) {
|
|
if (*pcTmp == '@' || *pcTmp == '+' ||
|
|
*pcTmp == ',' || *pcTmp == '_' ||
|
|
*pcTmp == '\0') {
|
|
szCodeset[tIndex] = '\0';
|
|
break;
|
|
}
|
|
szCodeset[tIndex] = *pcTmp;
|
|
pcTmp++;
|
|
}
|
|
szCodeset[tMaxCodesetLength - 1] = '\0';
|
|
}
|
|
if (pbEuro == NULL) {
|
|
/* No need to get the modifier */
|
|
return TRUE;
|
|
}
|
|
pcTmp = strchr(szLocale, '@');
|
|
if (pcTmp != NULL) {
|
|
/* Copy the modifier */
|
|
pcTmp++;
|
|
for (tIndex = 0; tIndex < sizeof(szModifier); tIndex++) {
|
|
if (*pcTmp == '+' || *pcTmp == ',' ||
|
|
*pcTmp == '_' || *pcTmp == '\0') {
|
|
szModifier[tIndex] = '\0';
|
|
break;
|
|
}
|
|
szModifier[tIndex] = *pcTmp;
|
|
pcTmp++;
|
|
}
|
|
szModifier[sizeof(szModifier) - 1] = '\0';
|
|
*pbEuro = STRCEQ(szModifier, "Euro");
|
|
}
|
|
#endif /* __dos */
|
|
return TRUE;
|
|
} /* end of bGetCodesetFromLocale */
|
|
|
|
/*
|
|
* GetNormalizedCodeset - get the normalized codeset from the current locale
|
|
*
|
|
* Returns TRUE when sucessful, otherwise FALSE
|
|
*/
|
|
BOOL
|
|
bGetNormalizedCodeset(char *szCodeset, size_t tMaxCodesetLength, BOOL *pbEuro)
|
|
{
|
|
BOOL bOnlyDigits;
|
|
const char *pcSrc;
|
|
char *pcDest;
|
|
char *szTmp, *szCodesetNorm;
|
|
|
|
if (pbEuro != NULL) {
|
|
*pbEuro = FALSE; /* Until proven otherwise */
|
|
}
|
|
if (szCodeset == NULL || tMaxCodesetLength < 4) {
|
|
return FALSE;
|
|
}
|
|
|
|
/* Get the codeset name */
|
|
szTmp = xmalloc(tMaxCodesetLength - 3);
|
|
if (!bGetCodesetFromLocale(szTmp, tMaxCodesetLength - 3, pbEuro)) {
|
|
szTmp = xfree(szTmp);
|
|
return FALSE;
|
|
}
|
|
/* Normalize the codeset name */
|
|
szCodesetNorm = xmalloc(tMaxCodesetLength - 3);
|
|
bOnlyDigits = TRUE;
|
|
pcDest = szCodesetNorm;
|
|
for (pcSrc = szTmp; *pcSrc != '\0'; pcSrc++) {
|
|
if (isalnum(*pcSrc)) {
|
|
*pcDest = tolower(*pcSrc);
|
|
if (!isdigit(*pcDest)) {
|
|
bOnlyDigits = FALSE;
|
|
}
|
|
pcDest++;
|
|
}
|
|
}
|
|
*pcDest = '\0';
|
|
DBG_MSG(szCodesetNorm);
|
|
/* Add "iso" when szCodesetNorm contains all digits */
|
|
if (bOnlyDigits && szCodesetNorm[0] != '\0') {
|
|
fail(strlen(szCodesetNorm) + 3 >= tMaxCodesetLength);
|
|
sprintf(szCodeset, "iso%s", szCodesetNorm);
|
|
} else {
|
|
fail(strlen(szCodesetNorm) >= tMaxCodesetLength);
|
|
strncpy(szCodeset, szCodesetNorm, pcDest - szCodesetNorm + 1);
|
|
szCodeset[tMaxCodesetLength - 1] = '\0';
|
|
}
|
|
DBG_MSG(szCodeset);
|
|
/* Clean up and leave */
|
|
szCodesetNorm = xfree(szCodesetNorm);
|
|
szTmp = xfree(szTmp);
|
|
return TRUE;
|
|
} /* end of bGetNormalizedCodeset */
|
|
|
|
/*
|
|
* szGetDefaultMappingFile - get the default mapping file
|
|
*
|
|
* Returns the basename of the default mapping file
|
|
*/
|
|
const char *
|
|
szGetDefaultMappingFile(void)
|
|
{
|
|
static const struct {
|
|
const char *szCodeset;
|
|
const char *szMappingFile;
|
|
} atMappingFile[] = {
|
|
{ "iso88591", MAPPING_FILE_8859_1 },
|
|
{ "iso88592", MAPPING_FILE_8859_2 },
|
|
{ "iso88593", "8859-3.txt" },
|
|
{ "iso88594", "8859-4.txt" },
|
|
{ "iso88595", "8859-5.txt" },
|
|
{ "iso88596", MAPPING_FILE_8859_5 },
|
|
{ "iso88597", "8859-7.txt" },
|
|
{ "iso88598", "8859-8.txt" },
|
|
{ "iso88599", "8859-9.txt" },
|
|
{ "iso885910", "8859-10.txt" },
|
|
{ "iso885913", "8859-13.txt" },
|
|
{ "iso885914", "8859-14.txt" },
|
|
{ "iso885915", MAPPING_FILE_8859_15 },
|
|
{ "iso885916", "8859-16.txt" },
|
|
{ "koi8r", MAPPING_FILE_KOI8_R },
|
|
{ "koi8u", MAPPING_FILE_KOI8_U },
|
|
{ "utf8", MAPPING_FILE_UTF_8 },
|
|
{ "cp437", MAPPING_FILE_CP437 },
|
|
{ "cp850", "cp850.txt" },
|
|
{ "cp852", MAPPING_FILE_CP852 },
|
|
{ "cp862", "cp862.txt" },
|
|
{ "cp864", "cp864.txt" },
|
|
{ "cp866", MAPPING_FILE_CP866 },
|
|
{ "cp1250", MAPPING_FILE_CP1250 },
|
|
{ "cp1251", MAPPING_FILE_CP1251 },
|
|
{ "cp1252", "cp1252.txt" },
|
|
};
|
|
size_t tIndex;
|
|
BOOL bEuro;
|
|
char szCodeset[20];
|
|
|
|
szCodeset[0] = '\0';
|
|
bEuro = FALSE;
|
|
/* Get the normalized codeset name */
|
|
if (!bGetNormalizedCodeset(szCodeset, sizeof(szCodeset), &bEuro)) {
|
|
return MAPPING_FILE_8859_1;
|
|
}
|
|
if (szCodeset[0] == '\0') {
|
|
if (bEuro) {
|
|
/* Default mapping file (with Euro sign) */
|
|
return MAPPING_FILE_8859_15;
|
|
} else {
|
|
/* Default mapping file (without Euro sign) */
|
|
return MAPPING_FILE_8859_1;
|
|
}
|
|
}
|
|
/* Find the name in the table */
|
|
for (tIndex = 0; tIndex < elementsof(atMappingFile); tIndex++) {
|
|
if (STREQ(atMappingFile[tIndex].szCodeset, szCodeset)) {
|
|
return atMappingFile[tIndex].szMappingFile;
|
|
}
|
|
}
|
|
/* Default default mapping file */
|
|
#if defined(__dos)
|
|
return MAPPING_FILE_CP437;
|
|
#else
|
|
return MAPPING_FILE_8859_1;
|
|
#endif /* __dos */
|
|
} /* end of szGetDefaultMappingFile */
|
|
#endif /* !__riscos */
|
|
|
|
/*
|
|
* tConvertDTTM - convert Windows Date and Time format
|
|
*
|
|
* returns Unix time_t or -1
|
|
*/
|
|
time_t
|
|
tConvertDTTM(ULONG ulDTTM)
|
|
{
|
|
struct tm tTime;
|
|
time_t tResult;
|
|
|
|
if (ulDTTM == 0) {
|
|
return (time_t)-1;
|
|
}
|
|
memset(&tTime, 0, sizeof(tTime));
|
|
tTime.tm_min = (int)(ulDTTM & 0x0000003f);
|
|
tTime.tm_hour = (int)((ulDTTM & 0x000007c0) >> 6);
|
|
tTime.tm_mday = (int)((ulDTTM & 0x0000f800) >> 11);
|
|
tTime.tm_mon = (int)((ulDTTM & 0x000f0000) >> 16);
|
|
tTime.tm_year = (int)((ulDTTM & 0x1ff00000) >> 20);
|
|
tTime.tm_isdst = -1;
|
|
tTime.tm_mon--; /* From 01-12 to 00-11 */
|
|
tResult = mktime(&tTime);
|
|
NO_DBG_MSG(ctime(&tResult));
|
|
return tResult;
|
|
} /* end of tConvertDTTM */
|