#include #include #include #include #include #include #ifdef RLDOS #include "dosdefs.h" #else #include "windefs.h" #endif #include "restok.h" #include "resread.h" #include "toklist.h" #include "commbase.h" #define MAXLINE 1024 #define MAXTERM 512 extern UCHAR szDHW[]; extern PROJDATA gProj; extern MSTRDATA gMstr; #ifdef WIN32 extern HINSTANCE hInst; // Instance of the main window #else extern HWND hInst; // Instance of the main window #endif static fUnicodeGlossary = FALSE; static long GetGlossaryIndex( FILE *, TCHAR, long []); static void ParseGlossEntry( TCHAR *, TCHAR *, TCHAR[], TCHAR *, TCHAR[]); static void ParseTextHotKeyToBuf( TCHAR *, TCHAR, TCHAR *); static void ParseBufToTextHotKey( TCHAR *, TCHAR[], TCHAR *); static WORD NormalizeIndex( TCHAR); static int MyPutGlossStr( TCHAR *, FILE *); static TCHAR *MyGetGlossStr( TCHAR *, int, FILE *); static void BuildGlossEntry( TCHAR *, TCHAR *, TCHAR, TCHAR *, TCHAR); static BOOL NotAMember( TRANSLIST *, TCHAR *); FILE * OpenGlossary( CHAR *szGlossFile, CHAR chAccessType) { CHAR * szRW[4] = {"rb", "rt", "wb", "wt"}; int nRW = 0; // assume access type is 'r' (read) FILE *fpRC = NULL; if ( chAccessType == 'w' ) // is access type 'w' (write)? { nRW = fUnicodeGlossary ? 2 : 3; // yes (Unicode file or not?) } fpRC = fopen( szGlossFile, szRW[ nRW]); if ( fpRC && chAccessType == 'r' ) { USHORT usMark = GetWord( fpRC, NULL); if ( usMark == 0xfeff ) { fUnicodeGlossary = TRUE; // it's a Unicode text file } else if ( usMark == 0xfffe ) { QuitA( IDS_WRONGENDIAN, szGlossFile, NULL); } else { fclose( fpRC); fpRC = fopen( szGlossFile, szRW[ ++nRW]); // it's an ANSI text file } } return( fpRC); } /** * * * Function: * * * Arguments: * * Returns: * * Errors Codes: * * History: * * **/ int MakeGlossIndex( LONG * lFilePointer) { TCHAR szGlossEntry[MAXLINE] = TEXT(""); WORD iCurrent = 0; LONG lFPointer = -1; FILE *pFile = NULL; pFile = OpenGlossary( gProj.szGlo, 'r'); if ( pFile == NULL ) { return( 1); } // Glossaries some times have this bogus header at the begining. // which we want to skip if it exists if ( ! MyGetGlossStr( szGlossEntry, MAXLINE, pFile) ) { // Error during first read from the glossary. fclose( pFile); return( 1); } lFPointer = ftell( pFile); // check for glossary header if ( lstrlen( szGlossEntry) >= 7 ) { // lstrcpy( (TCHAR *)szDHW, szGlossEntry); // szDHW[ MEMSIZE( 7)] = szDHW[ MEMSIZE( 7) + 1] = '\0'; // CharLower( (TCHAR *)szDHW); // // if ( lstrcmp( (TCHAR *)szDHW, TEXT("english")) == 0 ) if ( CompareStringW( MAKELCID( gMstr.wLanguageID, SORT_DEFAULT), SORT_STRINGSORT | NORM_IGNORECASE, szGlossEntry, 7, TEXT("ENGLISH"), 7) == 2 ) { lFPointer = ftell (pFile); if ( ! MyGetGlossStr( szGlossEntry, MAXLINE, pFile) ) { fclose( pFile); return (1); } } } // now assume we are at the correct location in glossary // file to begin generating the index, we want to save // this location lFilePointer[0] = lFPointer; // glossary file is sorted so, any non letter items // in the glossary would be first. Index into this location // using the 1st position // 1st lets make sure we have non letters items in // the glossary // now skip ( if any ) the non letter entries in the glossary while( (WORD) szGlossEntry[0] < (WORD) TEXT('A' ) ) { if ( ! MyGetGlossStr( szGlossEntry, MAXLINE, pFile) ) { fclose( pFile); return( 1); } } // now position at alpha characters iCurrent = NormalizeIndex( szGlossEntry[0] ); // now we read through the remaining glossary entries // and save the offsets for each index as we go do { if ( NormalizeIndex( szGlossEntry[0] ) > iCurrent ) { // we passed the region for our current index // so save the location, and move to the next index. // note we may be skiping indexs, lFilePointer[ iCurrent] = lFPointer; iCurrent = NormalizeIndex( szGlossEntry[0] ); } lFPointer = ftell( pFile ); // otherwise the current index is valied for this // section of the glossary indexes, so just continue } while ( MyGetGlossStr( szGlossEntry, MAXLINE, pFile) ); fclose( pFile); return( 0); } /** * * * Function: TransString * Builds a circular linked list containing all translations of a string. * The first entry in the list is the untranslated string. * * Arguments: * fpGlossFile, handle to open glossary file * szKeyText, string with the text to build translation table * szCurrentText, text currently in the box. * ppTransList, pointer to a pointer to a node in a circular linked list * lFilePointer, pointer to index table for glossary file * * Returns: * number of nodes in list * * Errors Codes: * * History: * Recoded by SteveBl, 3/92 * **/ /* Translate the string, if possible. */ int TransString( TCHAR *szKeyText, TCHAR *szCurrentText, TRANSLIST **ppTransList, LONG *lFilePointer) { int n = 0; long lFileIndex; TRANSLIST **ppCurrentPointer; static TCHAR szGlossEntry[MAXLINE]; static TCHAR szEngText[260]; static TCHAR szIntlText[260]; TCHAR *szCurText = NULL; TCHAR cEngHotKey = TEXT('\0'); TCHAR cIntlHotKey = TEXT('\0'); TCHAR cCurHotKey = TEXT('\0'); FILE *fpGlossFile = NULL; // *Is* there a glossary file? if ( (fpGlossFile = OpenGlossary( gProj.szGlo, 'r')) == NULL ) { return( 0); } // FIRST let's erase the list if ( *ppTransList ) { (*ppTransList)->pPrev->pNext = NULL; // so we can find the end of the list } while ( *ppTransList ) { TRANSLIST *pTemp; pTemp = *ppTransList; *ppTransList = pTemp->pNext; RLFREE( pTemp->sz); RLFREE( pTemp); } ppCurrentPointer = ppTransList; // DONE removing the list // Now make the first node (which is the untranslated string) { TCHAR * psz; psz = (TCHAR *)FALLOC( MEMSIZE( lstrlen( szCurrentText) + 1)); lstrcpy( psz,szCurrentText); *ppTransList = ( TRANSLIST *)FALLOC( sizeof( TRANSLIST)); (*ppTransList)->pPrev = (*ppTransList)->pNext = *ppTransList; (*ppTransList)->sz = psz; ppCurrentPointer = ppTransList; n++; } szCurText = (TCHAR *)FALLOC( MEMSIZE( lstrlen( szKeyText) + 1) ); ParseBufToTextHotKey( szCurText, &cCurHotKey, szKeyText); lFileIndex = GetGlossaryIndex( fpGlossFile, szCurText[0], lFilePointer); fseek (fpGlossFile, lFileIndex, SEEK_SET); while ( TRUE) { if ( ! MyGetGlossStr( szGlossEntry, MAXLINE, fpGlossFile) ) { // Reached end of glossary file RLFREE( szCurText); fclose( fpGlossFile); return n; } ParseGlossEntry( szGlossEntry, szEngText, &cEngHotKey, szIntlText, &cIntlHotKey); // make comparision, using text, and hot keys // if ( ( ! lstrcmp( szCurText, szEngText )) && cCurHotKey == cEngHotKey ) if ( CompareStringW( MAKELCID( gMstr.wLanguageID, SORT_DEFAULT), SORT_STRINGSORT, szCurText, -1, szEngText, -1) == 2 && cCurHotKey == cEngHotKey ) { TCHAR * psz; static TCHAR szTemp[ MAXINPUTBUFFER]; // we have a match, put translated text into token if ( cIntlHotKey ) { ParseTextHotKeyToBuf( szIntlText, cIntlHotKey, szTemp); } else { lstrcpy( szTemp, szIntlText); } if ( NotAMember( *ppTransList, szTemp) ) { // add matched glossary text to circular list of matches psz = (TCHAR *) FALLOC( MEMSIZE( lstrlen( szTemp) + 1)); lstrcpy( psz,szTemp); (*ppCurrentPointer)->pNext = (TRANSLIST *) FALLOC( sizeof( TRANSLIST)); ((*ppCurrentPointer)->pNext)->pPrev = *ppCurrentPointer; ppCurrentPointer = (TRANSLIST **)&((*ppCurrentPointer)->pNext); (*ppCurrentPointer)->pPrev->pNext = *ppCurrentPointer; (*ppCurrentPointer)->pNext = *ppTransList; (*ppTransList)->pPrev = *ppCurrentPointer; (*ppCurrentPointer)->sz = psz; ++n; } } else { // can we terminate search? // if( lstrcmpi( szEngText, szCurText ) > 0 ) if ( CompareStringW( MAKELCID( gMstr.wLanguageID, SORT_DEFAULT), SORT_STRINGSORT, szEngText, -1, szCurText, -1) == 3 ) { // went past index section RLFREE( szCurText); fclose( fpGlossFile); return( n); } } } RLFREE( szCurText); fclose( fpGlossFile); return( n); } // TransString /** * * * Function: NormalizeIndex * * * Arguments: * * Returns: * * Errors Codes: * * History: * * **/ static WORD NormalizeIndex( TCHAR chIndex ) { TCHAR chTmp = chIndex; CharLowerBuff( &chTmp, 1); return( (chTmp != TEXT('"') && chTmp >= TEXT('a') && chTmp <= TEXT('z')) ? chTmp - TEXT('a') + 1 : 0); } /* * Function:NotAMember * * Arguments: * pList, pointer to a TRANSLIST node * sz, string to find * * Returns: * TRUE if not found in the list else FALSE * * History: * 3/92, implemented SteveBl **/ static BOOL NotAMember( TRANSLIST *pList, TCHAR *sz) { TRANSLIST *pCurrent = pList; if ( ! pList ) { return( TRUE); // empty list } do { // if ( lstrcmp( sz, pCurrent->sz) == 0 ) if ( CompareStringW( MAKELCID( gMstr.wLanguageID, SORT_DEFAULT), SORT_STRINGSORT, sz, -1, pCurrent->sz, -1) == 2 ) { return( FALSE); // found in list } pCurrent = pCurrent->pNext; }while ( pList != pCurrent ); return( TRUE); // not found } /** * * * Function: * * * Arguments: * * Returns: * * Errors Codes: * * History: * * **/ static void ParseGlossEntry( TCHAR szGlossEntry[], TCHAR szEngText[], TCHAR cEngHotKey[1], TCHAR szIntlText[], TCHAR cIntlHotKey[1]) { WORD wIndex, wIndex2; // format is: // // Any field could be null and if there aren't the right amount of // tabs we'll just assume that the remaining fields are empty. wIndex=wIndex2=0; // first get the english text while ( szGlossEntry[wIndex2] != TEXT('\t') && szGlossEntry[wIndex2] != TEXT('\0') ) { szEngText[ wIndex++] = szGlossEntry[ wIndex2++]; } szEngText[wIndex]=TEXT('\0'); if ( szGlossEntry[ wIndex2] == TEXT('\t') ) { ++wIndex2; // skip the tab } // now get the eng hot key if ( szGlossEntry[wIndex2] != TEXT('\t') && szGlossEntry[wIndex2] != TEXT('\0') ) { *cEngHotKey = szGlossEntry[wIndex2++]; } else { *cEngHotKey = TEXT('\0'); } while ( szGlossEntry[ wIndex2] != TEXT('\t') && szGlossEntry[ wIndex2] != TEXT('\0') ) { ++wIndex2; // make sure the hot key field doesn't hold more than one char } if ( szGlossEntry[ wIndex2] == TEXT('\t') ) { ++wIndex2; // skip the tab } wIndex = 0; // now get the intl text while ( szGlossEntry[ wIndex2] != TEXT('\t') && szGlossEntry[ wIndex2] != TEXT('\0') ) { szIntlText[wIndex++]=szGlossEntry[wIndex2++]; } szIntlText[wIndex]='\0'; if ( szGlossEntry[ wIndex2] == TEXT('\t') ) { ++wIndex2; // skip the tab } // now get the intl hot key if ( szGlossEntry[ wIndex2] != TEXT('\t') && szGlossEntry[ wIndex2] != TEXT('\0') ) { *cIntlHotKey = szGlossEntry[ wIndex2++]; } else { *cIntlHotKey = TEXT('\0'); } } /** * * * Function: * * * Arguments: * * Returns: * * Errors Codes: * * History: * * **/ static void ParseBufToTextHotKey( TCHAR *szText, TCHAR cHotKey[1], TCHAR *szBuf) { WORD wIndexBuf = 0; WORD wIndexText = 0; *cHotKey = TEXT('\0'); while( szBuf[ wIndexBuf] ) { if ( szBuf[ wIndexBuf ] == TEXT('&') ) { *cHotKey = szBuf[ ++wIndexBuf]; } else { szText[ wIndexText++] = szBuf[ wIndexBuf++]; } } szText[ wIndexText] = TEXT('\0'); } /** * * * Function: * * * Arguments: * * Returns: * * Errors Codes: * * History: * * **/ static void ParseTextHotKeyToBuf( TCHAR *szText, TCHAR cHotKey, TCHAR *szBuf ) { WORD wIndexBuf = 0; WORD wIndexText = 0; // TCHAR cTmp; while ( szText[ wIndexText] ) { // cTmp = szText[ wIndexText]; // // CharUpperBuff( &cTmp, 1); // // if ( cTmp == cHotKey ) if ( szText[ wIndexText] == cHotKey ) { szBuf[ wIndexBuf++] = TEXT('&'); szBuf[ wIndexBuf++] = szText[ wIndexText++]; break; } else { szBuf[ wIndexBuf++] = szText[ wIndexText++]; } } // copy remaining string while( szText[ wIndexText] ) { szBuf[ wIndexBuf++] = szText[ wIndexText++]; } szBuf[ wIndexBuf] = TEXT('\0'); } static long GetGlossaryIndex( FILE *fpGlossFile, TCHAR c, long *lGlossaryIndex ) { int i = 0; TCHAR cTmp = c; CharLowerBuff( &cTmp, 1); if ( cTmp >= TEXT('a') && cTmp <= TEXT('z') ) { i = NormalizeIndex( c ); return( lGlossaryIndex[ i > 0 ? i - 1 : 0]); } else { return( 0); } } /******************************************************************************* * PROCEDURE: BuildGlossEntry * Builds a glossary entry line. * * Parameters: * sz, line buffer * sz1, untranslated text * c1, untranslated hot key (or 0 if no hot key) * sz2, translated text * c2, translated hot key (or 0 if no hot key) * * Returns: * nothing. sz contains the line. (assumes there is room in the buffer) * * History: * 3/93 - initial implementation - SteveBl *******************************************************************************/ static void BuildGlossEntry( TCHAR *sz, TCHAR *sz1, TCHAR c1, TCHAR *sz2, TCHAR c2) { *sz = TEXT('\0'); wsprintf( sz, TEXT("%s\t%c\t%s\t%c"), sz1, c1, sz2, c2); } /****************************************************************************** * PROCEDURE: AddTranslation * Adds a translation to a glossary file. * * PARAMETERS: * szGlossFile, path to the glossary * szKey, untranslated text * szTranslation, translated text * lFilePointer, pointer to index hash table for glossary * * RETURNS: * nothing. Key is added to glossary if no errors are encountered else * file is left unchanged. * * COMMENTS: * rebuilds the global pointer list lFilePointer * * HISTORY: * * 3/92 - initial implementation - SteveBl ******************************************************************************/ void AddTranslation( TCHAR *szKey, TCHAR *szTranslation, LONG *lFilePointer) { // DBCS begin TCHAR szCurText [520]; TCHAR szTransText [520]; // DBCS end TCHAR cTransHot = TEXT('\0'); TCHAR cCurHotKey = TEXT('\0'); CHAR szTempFileName [255]; FILE *fTemp = NULL; FILE *fpGlossFile = NULL; TCHAR szTempText [MAXLINE]; // DBCS begin TCHAR szNewText [MAXLINE * 2]; // DBCS end TCHAR *r = NULL; TCHAR chTmp = TEXT('\0'); MyGetTempFileName( 0, "", 0, szTempFileName); if ( (fTemp = OpenGlossary( szTempFileName, 'w')) != NULL ) { if ( fUnicodeGlossary ) { fprintf( fTemp, "%hu", 0xfeff); // Mark new one as Unicode } ParseBufToTextHotKey( szCurText, &cCurHotKey, szKey); ParseBufToTextHotKey( szTransText, &cTransHot, szTranslation); BuildGlossEntry( szNewText, szCurText, cCurHotKey, szTransText, cTransHot); // If the glossary file exists, get its first // line. If it doesn't exist, we'll create it // (via CopyFile) at the end of this function. if ( (fpGlossFile = OpenGlossary( gProj.szGlo, 'r')) != NULL ) { if ( (r = MyGetGlossStr( szTempText, TCHARSIN( sizeof( szTempText)), fpGlossFile)) ) { // lstrcpy( (TCHAR *)szDHW, szTempText); // szDHW[ MEMSIZE( 7)] = szDHW[ MEMSIZE( 7) + 1] = '\0'; // CharLower( (TCHAR *)szDHW); // // if ( lstrcmpi( (TCHAR *)szDHW, TEXT("ENGLISH")) == 0 ) if ( CompareStringW( MAKELCID( gMstr.wLanguageID, SORT_DEFAULT), SORT_STRINGSORT | NORM_IGNORECASE, szTempText, 7, TEXT("ENGLISH"), 7) == 2 ) { // skip first line MyPutGlossStr( szTempText, fTemp); r = MyGetGlossStr( szTempText, TCHARSIN( sizeof( szTempText)), fpGlossFile); } } } else { r = NULL; } // if ( r ) // { // chTmp = szTempText[0]; // CharLowerBuff( &chTmp, 1); // } // else // { // chTmp = szTempText[0] = TEXT('\0'); // } // // Does the new text begin with a letter? // // if ( chTmp >= TEXT('a') ) // { // // begins with a letter, we need to find where to put it // // while ( r && chTmp < TEXT('a') ) while ( r && CompareStringW( MAKELCID( gMstr.wLanguageID, SORT_DEFAULT), SORT_STRINGSORT, szTempText, -1, szNewText, -1) == 1 ) { // skip the non letter section MyPutGlossStr( szTempText, fTemp); r = MyGetGlossStr( szTempText, TCHARSIN( sizeof( szTempText)), fpGlossFile); // if ( (r = MyGetGlossStr( szTempText, // TCHARSIN( sizeof( szTempText)), // fpGlossFile)) ) // { // chTmp = szTempText[0]; // CharLowerBuff( &chTmp, 1); // } } // while ( r && _tcsicmp( szTempText, szNewText) < 0 ) // { // // skip anything smaller than me // // MyPutGlossStr( szTempText, fTemp); // r = MyGetGlossStr( szTempText, TCHARSIN( sizeof( szTempText)), fpGlossFile); // } // } // else // { // // doesn't begin with a letter, we need to insert it before // // the letter sections begin but it still must be sorted // // while ( r // && chTmp < TEXT('a') // && _tcsicmp( szTempText, szNewText) < 0 ) // { // MyPutGlossStr( szTempText, fTemp); // // if ( (r = MyGetGlossStr( szTempText, // TCHARSIN( sizeof( szTempText)), // fpGlossFile)) ) // { // chTmp = szTempText[0]; // CharLowerBuff( &chTmp, 1); // } // } // } MyPutGlossStr( szNewText, fTemp); while ( r ) { MyPutGlossStr( szTempText,fTemp); r = MyGetGlossStr( szTempText, TCHARSIN( sizeof( szTempText)), fpGlossFile); } fclose( fTemp); if ( fpGlossFile ) { fclose( fpGlossFile); } // This call will create the glossary file // if it didn't already exist. if ( ! CopyFileA( szTempFileName, gProj.szGlo, FALSE) ) { QuitA( IDS_COPYFILE_FAILED, szTempFileName, gProj.szGlo); } remove( szTempFileName); MakeGlossIndex( lFilePointer); } else { QuitA( IDS_NO_TMP_GLOSS, szTempFileName, NULL); } } /** * * * Function: MyGetGlossStr * Replaces C runtime fgets function. * * History: * 5/92, Implemented. TerryRu. * * **/ static TCHAR *MyGetGlossStr( TCHAR * ptszStr, int nCount, FILE * fIn) { int i = 0; #ifdef RLRES32 // It this a Unicode glossary file? TCHAR tCh = TEXT('\0'); if ( fUnicodeGlossary ) { do // Yes { tCh = ptszStr[ i++] = (TCHAR)GetWord( fIn, NULL); } while ( i < nCount && tCh != TEXT('\n') ); if ( tCh == TEXT('\0') || feof( fIn) ) { return( NULL); } ptszStr[i] = TEXT('\0'); StripNewLineW( ptszStr); } else // No, it's an ANSI glossary file { if ( fgets( szDHW, DHWSIZE, fIn) != NULL ) { StripNewLineA( szDHW); _MBSTOWCS( ptszStr, szDHW, nCount, (UINT)-1); } else { return( NULL); } } return( ptszStr); #else //RLRES32 if ( fgets( ptszStr, nCount, fIn) ) { StripNewLineA( ptszStr); } else { return( NULL); } #endif //RLRES32 } /** * * * Function: MyPutGlossStr * Replaces C runtime fputs function. * History: * 6/92, Implemented. TerryRu. * * **/ static int MyPutGlossStr( TCHAR * ptszStr, FILE * fOut) { #ifdef RLRES32 int i = 0; // It this a Unicode glossary file? if ( fUnicodeGlossary ) { do // Yes { PutWord( fOut, ptszStr[i], NULL); } while ( ptszStr[ i++] ); PutWord( fOut, TEXT('\r'), NULL); PutWord( fOut, TEXT('\n'), NULL); i += 2; } else // No, it's an ANSI glossary file { _WCSTOMBS( szDHW, ptszStr, DHWSIZE, lstrlen( ptszStr) + 1); i = fputs( szDHW, fOut); fputs( "\n", fOut); } #else //RLRES32 i = fputs( ptszStr, fOut); fputs( "\n", fOut); #endif //RLRES32 return(i); }