/*
* 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 */