/* Made up from data.c and other supplementary files of dictd-1.0.11 for the * GoldenDict program. */ /* data.c -- * Created: Tue Jul 16 12:45:41 1996 by faith@dict.org * Revised: Sat Mar 30 10:46:06 2002 by faith@dict.org * Copyright 1996, 1997, 1998, 2000, 2002 Rickard E. Faith (faith@dict.org) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 1, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Suite 500, Boston, MA 02110, USA. */ #include #include #include "dictzip.hh" #include #include #include #include #include #include "ufile.hh" #define BUFFERSIZE 10240 #define OUT_BUFFER_SIZE 0xffffL #define IN_BUFFER_SIZE ( (unsigned long)( (double)( OUT_BUFFER_SIZE - 12 ) * 0.89 ) ) /* For gzip-compatible header, as defined in RFC 1952 */ /* Magic for GZIP (rfc1952) */ #define GZ_MAGIC1 0x1f /* First magic byte */ #define GZ_MAGIC2 0x8b /* Second magic byte */ /* FLaGs (bitmapped), from rfc1952 */ #define GZ_FTEXT 0x01 /* Set for ASCII text */ #define GZ_FHCRC 0x02 /* Header CRC16 */ #define GZ_FEXTRA 0x04 /* Optional field (random access index) */ #define GZ_FNAME 0x08 /* Original name */ #define GZ_COMMENT 0x10 /* Zero-terminated, human-readable comment */ #define GZ_MAX 2 /* Maximum compression */ #define GZ_FAST 4 /* Fasted compression */ /* These are from rfc1952 */ #define GZ_OS_FAT 0 /* FAT filesystem (MS-DOS, OS/2, NT/Win32) */ #define GZ_OS_AMIGA 1 /* Amiga */ #define GZ_OS_VMS 2 /* VMS (or OpenVMS) */ #define GZ_OS_UNIX 3 /* Unix */ #define GZ_OS_VMCMS 4 /* VM/CMS */ #define GZ_OS_ATARI 5 /* Atari TOS */ #define GZ_OS_HPFS 6 /* HPFS filesystem (OS/2, NT) */ #define GZ_OS_MAC 7 /* Macintosh */ #define GZ_OS_Z 8 /* Z-System */ #define GZ_OS_CPM 9 /* CP/M */ #define GZ_OS_TOPS20 10 /* TOPS-20 */ #define GZ_OS_NTFS 11 /* NTFS filesystem (NT) */ #define GZ_OS_QDOS 12 /* QDOS */ #define GZ_OS_ACORN 13 /* Acorn RISCOS */ #define GZ_OS_UNKNOWN 255 /* unknown */ #define GZ_RND_S1 'R' /* First magic for random access format */ #define GZ_RND_S2 'A' /* Second magic for random access format */ #define GZ_ID1 0 /* GZ_MAGIC1 */ #define GZ_ID2 1 /* GZ_MAGIC2 */ #define GZ_CM 2 /* Compression Method (Z_DEFALTED) */ #define GZ_FLG 3 /* FLaGs (see above) */ #define GZ_MTIME 4 /* Modification TIME */ #define GZ_XFL 8 /* eXtra FLags (GZ_MAX or GZ_FAST) */ #define GZ_OS 9 /* Operating System */ #define GZ_XLEN 10 /* eXtra LENgth (16bit) */ #define GZ_FEXTRA_START 12 /* Start of extra fields */ #define GZ_SI1 12 /* Subfield ID1 */ #define GZ_SI2 13 /* Subfield ID2 */ #define GZ_SUBLEN 14 /* Subfield length (16bit) */ #define GZ_VERSION 16 /* Version for subfield format */ #define GZ_CHUNKLEN 18 /* Chunk length (16bit) */ #define GZ_CHUNKCNT 20 /* Number of chunks (16bit) */ #define GZ_RNDDATA 22 /* Random access data (16bit) */ #define DBG_VERBOSE ( 0 << 30 | 1 << 0 ) /* Verbose */ #define DBG_ZIP ( 0 << 30 | 1 << 1 ) /* Zip */ #define DBG_UNZIP ( 0 << 30 | 1 << 2 ) /* Unzip */ #define DBG_SEARCH ( 0 << 30 | 1 << 3 ) /* Search */ #define DBG_SCAN ( 0 << 30 | 1 << 4 ) /* Config file scan */ #define DBG_PARSE ( 0 << 30 | 1 << 5 ) /* Config file parse */ #define DBG_INIT ( 0 << 30 | 1 << 6 ) /* Database initialization */ #define DBG_PORT ( 0 << 30 | 1 << 7 ) /* Log port number for connections */ #define DBG_LEV ( 0 << 30 | 1 << 8 ) /* Levenshtein matching */ #define DBG_AUTH ( 0 << 30 | 1 << 9 ) /* Debug authentication */ #define DBG_NODETACH ( 0 << 30 | 1 << 10 ) /* Don't detach as a background proc. */ #define DBG_NOFORK ( 0 << 30 | 1 << 11 ) /* Don't fork (single threaded) */ #define DBG_ALT ( 0 << 30 | 1 << 12 ) /* altcompare() */ #define LOG_SERVER ( 0 << 30 | 1 << 0 ) /* Log server diagnostics */ #define LOG_CONNECT ( 0 << 30 | 1 << 1 ) /* Log connection information */ #define LOG_STATS ( 0 << 30 | 1 << 2 ) /* Log termination information */ #define LOG_COMMAND ( 0 << 30 | 1 << 3 ) /* Log commands */ #define LOG_FOUND ( 0 << 30 | 1 << 4 ) /* Log words found */ #define LOG_NOTFOUND ( 0 << 30 | 1 << 5 ) /* Log words not found */ #define LOG_CLIENT ( 0 << 30 | 1 << 6 ) /* Log client */ #define LOG_HOST ( 0 << 30 | 1 << 7 ) /* Log remote host name */ #define LOG_TIMESTAMP ( 0 << 30 | 1 << 8 ) /* Log with timestamps */ #define LOG_MIN ( 0 << 30 | 1 << 9 ) /* Log a few minimal things */ #define LOG_AUTH ( 0 << 30 | 1 << 10 ) /* Log authentication denials */ #define DICT_LOG_TERM 0 #define DICT_LOG_DEFINE 1 #define DICT_LOG_MATCH 2 #define DICT_LOG_NOMATCH 3 #define DICT_LOG_CLIENT 4 #define DICT_LOG_TRACE 5 #define DICT_LOG_COMMAND 6 #define DICT_LOG_AUTH 7 #define DICT_LOG_CONNECT 8 #define DICT_UNKNOWN 0 #define DICT_TEXT 1 #define DICT_GZIP 2 #define DICT_DZIP 3 #include #include #include #include #define USE_CACHE 1 #define dict_data_filter( ... ) #define PRINTF( ... ) #define xmalloc malloc #define xfree free static const char * _err_programName = "GoldenDict"; #define log_error( ... ) #define log_error_va( ... ) static void err_fatal( const char * routine, const char * format, ... ) { va_list ap; fflush( stdout ); if ( _err_programName ) { if ( routine ) fprintf( stderr, "%s (%s): ", _err_programName, routine ); else fprintf( stderr, "%s: ", _err_programName ); } else { if ( routine ) fprintf( stderr, "%s: ", routine ); } va_start( ap, format ); vfprintf( stderr, format, ap ); log_error_va( routine, format, ap ); va_end( ap ); fflush( stderr ); fflush( stdout ); // exit ( 1 ); } /* \doc |err_fatal_errno| flushes "stdout", prints a fatal error report on "stderr", prints the system error corresponding to |errno|, flushes "stderr" and "stdout", and calls |exit|. |routine| is the name of the routine in which the error took place. */ static void err_fatal_errno( const char * routine, const char * format, ... ) { va_list ap; int errorno = errno; fflush( stdout ); if ( _err_programName ) { if ( routine ) fprintf( stderr, "%s (%s): ", _err_programName, routine ); else fprintf( stderr, "%s: ", _err_programName ); } else { if ( routine ) fprintf( stderr, "%s: ", routine ); } va_start( ap, format ); vfprintf( stderr, format, ap ); log_error_va( routine, format, ap ); va_end( ap ); #if HAVE_STRERROR fprintf( stderr, "%s: %s\n", routine, strerror( errorno ) ); log_error( routine, "%s: %s\n", routine, strerror( errorno ) ); #else errno = errorno; perror( routine ); log_error( routine, "%s: errno = %d\n", routine, errorno ); #endif fflush( stderr ); fflush( stdout ); // exit( 1 ); } /* \doc |err_internal| flushes "stdout", prints the fatal error message, flushes "stderr" and "stdout", and calls |abort| so that a core dump is generated. */ static void err_internal( const char * routine, const char * format, ... ) { va_list ap; fflush( stdout ); if ( _err_programName ) { if ( routine ) fprintf( stderr, "%s (%s): Internal error\n ", _err_programName, routine ); else fprintf( stderr, "%s: Internal error\n ", _err_programName ); } else { if ( routine ) fprintf( stderr, "%s: Internal error\n ", routine ); else fprintf( stderr, "Internal error\n " ); } va_start( ap, format ); vfprintf( stderr, format, ap ); log_error( routine, format, ap ); va_end( ap ); if ( _err_programName ) fprintf( stderr, "Aborting %s...\n", _err_programName ); else fprintf( stderr, "Aborting...\n" ); fflush( stderr ); fflush( stdout ); // abort(); } #ifndef __func__ #ifdef __FUNCTION__ #define __func__ __FUNCTION__ #else #define __func__ __FILE__ #endif #endif static enum DZ_ERRORS dict_read_header( const char * filename, dictData * header, int computeCRC ) { FILE * str; int id1, id2, si1, si2; char buffer[ BUFFERSIZE ]; int extraLength, subLength; int i; char * pt; int c; struct stat sb; unsigned long crc = crc32( 0L, Z_NULL, 0 ); int count; unsigned long offset; if ( !( str = gd_fopen( filename, "rb" ) ) ) { err_fatal_errno( __func__, "Cannot open data file \"%s\" for read\n", filename ); return DZ_ERR_OPENFILE; } header->filename = NULL; //str_find( filename ); header->headerLength = GZ_XLEN - 1; header->type = DICT_UNKNOWN; id1 = getc( str ); id2 = getc( str ); if ( id1 != GZ_MAGIC1 || id2 != GZ_MAGIC2 ) { header->type = DICT_TEXT; fstat( fileno( str ), &sb ); header->compressedLength = header->length = sb.st_size; header->origFilename = NULL; //str_find( filename ); header->mtime = sb.st_mtime; if ( computeCRC ) { rewind( str ); while ( !feof( str ) ) { if ( ( count = fread( buffer, 1, BUFFERSIZE, str ) ) ) { crc = crc32( crc, (Bytef *)buffer, count ); } } } header->crc = crc; fclose( str ); return DZ_NOERROR; } header->type = DICT_GZIP; header->method = getc( str ); header->flags = getc( str ); header->mtime = getc( str ) << 0; header->mtime |= getc( str ) << 8; header->mtime |= getc( str ) << 16; header->mtime |= getc( str ) << 24; header->extraFlags = getc( str ); header->os = getc( str ); if ( header->flags & GZ_FEXTRA ) { extraLength = getc( str ) << 0; extraLength |= getc( str ) << 8; header->headerLength += extraLength + 2; si1 = getc( str ); si2 = getc( str ); if ( si1 == GZ_RND_S1 && si2 == GZ_RND_S2 ) { subLength = getc( str ) << 0; subLength |= getc( str ) << 8; header->version = getc( str ) << 0; header->version |= getc( str ) << 8; if ( header->version != 1 ) { err_internal( __func__, "dzip header version %d not supported\n", header->version ); fclose( str ); return DZ_ERR_UNSUPPORTED_FORMAT; } header->chunkLength = getc( str ) << 0; header->chunkLength |= getc( str ) << 8; header->chunkCount = getc( str ) << 0; header->chunkCount |= getc( str ) << 8; if ( header->chunkCount <= 0 ) { fclose( str ); return DZ_ERR_INVALID_FORMAT; } header->chunks = xmalloc( sizeof( header->chunks[ 0 ] ) * header->chunkCount ); if ( header->chunks == 0 ) { fclose( str ); return DZ_ERR_NOMEMORY; } for ( i = 0; i < header->chunkCount; i++ ) { header->chunks[ i ] = getc( str ) << 0; header->chunks[ i ] |= getc( str ) << 8; } header->type = DICT_DZIP; } else { fseek( str, header->headerLength, SEEK_SET ); } } if ( header->flags & GZ_FNAME ) { /* FIXME! Add checking against header len */ pt = buffer; while ( ( c = getc( str ) ) && c != EOF ) { *pt++ = c; if ( pt == buffer + sizeof( buffer ) ) { err_fatal( __func__, "too long FNAME field in dzip file \"%s\"\n", filename ); fclose( str ); if ( header->chunks ) { free( header->chunks ); header->chunks = NULL; } return DZ_ERR_INVALID_FORMAT; } } *pt = '\0'; header->origFilename = NULL; //str_find( buffer ); header->headerLength += strlen( buffer ) + 1; } else { header->origFilename = NULL; } if ( header->flags & GZ_COMMENT ) { /* FIXME! Add checking for header len */ pt = buffer; while ( ( c = getc( str ) ) && c != EOF ) { *pt++ = c; if ( pt == buffer + sizeof( buffer ) ) { err_fatal( __func__, "too long COMMENT field in dzip file \"%s\"\n", filename ); fclose( str ); if ( header->chunks ) { free( header->chunks ); header->chunks = NULL; } return DZ_ERR_INVALID_FORMAT; } } *pt = '\0'; header->comment = NULL; //str_find( buffer ); header->headerLength += strlen( buffer ) + 1; } else { header->comment = NULL; } if ( header->flags & GZ_FHCRC ) { getc( str ); getc( str ); header->headerLength += 2; } if ( ftell( str ) != header->headerLength + 1 ) { err_internal( __func__, "File position (%lu) != header length + 1 (%d)\n", ftell( str ), header->headerLength + 1 ); fclose( str ); if ( header->chunks ) { free( header->chunks ); header->chunks = NULL; } return DZ_ERR_INVALID_FORMAT; } fseek( str, -8, SEEK_END ); header->crc = getc( str ) << 0; header->crc |= getc( str ) << 8; header->crc |= getc( str ) << 16; header->crc |= getc( str ) << 24; header->length = getc( str ) << 0; header->length |= getc( str ) << 8; header->length |= getc( str ) << 16; header->length |= getc( str ) << 24; header->compressedLength = ftell( str ); /* Compute offsets */ header->offsets = xmalloc( sizeof( header->offsets[ 0 ] ) * header->chunkCount ); if ( header->offsets == 0 ) { if ( header->chunks ) { free( header->chunks ); header->chunks = NULL; } fclose( str ); return DZ_ERR_NOMEMORY; } for ( offset = header->headerLength + 1, i = 0; i < header->chunkCount; i++ ) { header->offsets[ i ] = offset; offset += header->chunks[ i ]; } fclose( str ); return DZ_NOERROR; } dictData * dict_data_open( const char * filename, enum DZ_ERRORS * error, int computeCRC ) { dictData * h = NULL; // struct stat sb; int j; if ( !filename ) { *error = DZ_ERR_OPENFILE; return NULL; } h = xmalloc( sizeof( struct dictData ) ); if ( h == 0 ) { *error = DZ_ERR_NOMEMORY; return 0; } memset( h, 0, sizeof( struct dictData ) ); #ifdef __WIN32 h->fd = INVALID_HANDLE_VALUE; #endif h->initialized = 0; for ( ;; ) { #ifdef __WIN32 wchar_t wname[ 16384 ]; #endif *error = dict_read_header( filename, h, computeCRC ); if ( *error != DZ_NOERROR ) { break; /* err_fatal( __func__, "\"%s\" not in text or dzip format\n", filename );*/ } #ifdef __WIN32 if ( MultiByteToWideChar( CP_UTF8, 0, filename, -1, wname, 16384 ) == 0 ) { *error = DZ_ERR_OPENFILE; break; } h->fd = CreateFileW( wname, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0 ); if ( h->fd == INVALID_HANDLE_VALUE ) { *error = DZ_ERR_OPENFILE; break; } h->size = GetFileSize( h->fd, 0 ); #else h->fd = gd_fopen( filename, "rb" ); if ( !h->fd ) { *error = DZ_ERR_OPENFILE; break; /*err_fatal_errno( __func__, "Cannot open data file \"%s\"\n", filename );*/ } fseek( h->fd, 0, SEEK_END ); h->size = ftell( h->fd ); #endif for ( j = 0; j < DICT_CACHE_SIZE; j++ ) { h->cache[ j ].chunk = -1; h->cache[ j ].stamp = -1; h->cache[ j ].inBuffer = NULL; h->cache[ j ].count = 0; } *error = DZ_NOERROR; return h; } dict_data_close( h ); return ( 0 ); } void dict_data_close( dictData * header ) { int i; if ( !header ) return; #ifdef __WIN32 if ( header->fd != INVALID_HANDLE_VALUE ) CloseHandle( header->fd ); #else if ( header->fd ) fclose( header->fd ); #endif if ( header->chunks ) xfree( header->chunks ); if ( header->offsets ) xfree( header->offsets ); if ( header->initialized ) { if ( inflateEnd( &header->zStream ) ) err_internal( __func__, "Cannot shut down inflation engine: %s\n", header->zStream.msg ); } for ( i = 0; i < DICT_CACHE_SIZE; ++i ) { if ( header->cache[ i ].inBuffer ) xfree( header->cache[ i ].inBuffer ); } xfree( header ); } char * dict_data_read_( dictData * h, unsigned long start, unsigned long size, const char * preFilter, const char * postFilter ) { char * buffer; char * pt; unsigned long end; int count; char * inBuffer; char outBuffer[ OUT_BUFFER_SIZE ]; int firstChunk, lastChunk; int firstOffset, lastOffset; int i, j; int found, target, lastStamp; (void)preFilter; (void)postFilter; end = start + size; buffer = xmalloc( size + 1 ); if ( !buffer ) { strcpy( h->errorString, dz_error_str( DZ_ERR_NOMEMORY ) ); return 0; } if ( !size ) { *buffer = 0; return buffer; } PRINTF( DBG_UNZIP, ( "dict_data_read( %p, %lu, %lu, %s, %s )\n", h, start, size, preFilter, postFilter ) ); assert( h != NULL ); switch ( h->type ) { case DICT_GZIP: /* err_fatal( __func__, "Cannot seek on pure gzip format files.\n" "Use plain text (for performance)" " or dzip format (for space savings).\n" ); break; */ strcpy( h->errorString, "Cannot seek on pure gzip format files" ); xfree( buffer ); return 0; case DICT_TEXT: { #ifdef __WIN32 long hiPtr = 0; DWORD pos = SetFilePointer( h->fd, start, &hiPtr, FILE_BEGIN ); DWORD readed = 0; if ( pos != INVALID_SET_FILE_POINTER || GetLastError() != NO_ERROR ) ReadFile( h->fd, buffer, size, &readed, 0 ); if ( size != readed ) #else if ( fseek( h->fd, start, SEEK_SET ) != 0 || fread( buffer, size, 1, h->fd ) != 1 ) #endif { strcpy( h->errorString, dz_error_str( DZ_ERR_READFILE ) ); xfree( buffer ); return 0; } buffer[ size ] = '\0'; } break; case DICT_DZIP: if ( !h->initialized ) { h->zStream.zalloc = NULL; h->zStream.zfree = NULL; h->zStream.opaque = NULL; h->zStream.next_in = 0; h->zStream.avail_in = 0; h->zStream.next_out = NULL; h->zStream.avail_out = 0; if ( inflateInit2( &h->zStream, -15 ) != Z_OK ) /* err_internal( __func__, "Cannot initialize inflation engine: %s\n", h->zStream.msg ); */ { sprintf( h->errorString, "Cannot initialize inflation engine: %s", h->zStream.msg ); xfree( buffer ); return 0; } ++h->initialized; } firstChunk = start / h->chunkLength; firstOffset = start - firstChunk * h->chunkLength; lastChunk = end / h->chunkLength; lastOffset = end - lastChunk * h->chunkLength; PRINTF( DBG_UNZIP, ( " start = %lu, end = %lu\n" "firstChunk = %d, firstOffset = %d," " lastChunk = %d, lastOffset = %d\n", start, end, firstChunk, firstOffset, lastChunk, lastOffset ) ); for ( pt = buffer, i = firstChunk; i <= lastChunk; i++ ) { /* Access cache */ found = 0; target = 0; lastStamp = INT_MAX; for ( j = 0; j < DICT_CACHE_SIZE; j++ ) { #if USE_CACHE if ( h->cache[ j ].chunk == i ) { found = 1; target = j; break; } #endif if ( h->cache[ j ].stamp < lastStamp ) { lastStamp = h->cache[ j ].stamp; target = j; } } h->cache[ target ].stamp = ++h->stamp; if ( h->stamp < 0 ) { h->stamp = 0; for ( j = 0; j < DICT_CACHE_SIZE; j++ ) h->cache[ j ].stamp = -1; } if ( found ) { count = h->cache[ target ].count; inBuffer = h->cache[ target ].inBuffer; } else { #ifdef __WIN32 DWORD pos; DWORD readed; #endif h->cache[ target ].chunk = -1; if ( !h->cache[ target ].inBuffer ) h->cache[ target ].inBuffer = xmalloc( h->chunkLength ); inBuffer = h->cache[ target ].inBuffer; if ( !inBuffer ) { strcpy( h->errorString, dz_error_str( DZ_ERR_NOMEMORY ) ); xfree( buffer ); return 0; } if ( h->chunks[ i ] >= OUT_BUFFER_SIZE ) { /* err_internal( __func__, "h->chunks[%d] = %d >= %ld (OUT_BUFFER_SIZE)\n", i, h->chunks[i], OUT_BUFFER_SIZE ); */ sprintf( h->errorString, "h->chunks[%d] = %d >= %ld (OUT_BUFFER_SIZE)\n", i, h->chunks[ i ], OUT_BUFFER_SIZE ); xfree( buffer ); return 0; } #ifdef __WIN32 pos = SetFilePointer( h->fd, h->offsets[ i ], 0, FILE_BEGIN ); readed = 0; if ( pos != INVALID_SET_FILE_POINTER || GetLastError() != NO_ERROR ) ReadFile( h->fd, outBuffer, h->chunks[ i ], &readed, 0 ); if ( h->chunks[ i ] != (int)readed ) #else if ( fseek( h->fd, h->offsets[ i ], SEEK_SET ) != 0 || fread( outBuffer, h->chunks[ i ], 1, h->fd ) != 1 ) #endif { strcpy( h->errorString, dz_error_str( DZ_ERR_READFILE ) ); xfree( buffer ); return 0; } dict_data_filter( outBuffer, &count, OUT_BUFFER_SIZE, preFilter ); h->zStream.next_in = (Bytef *)outBuffer; h->zStream.avail_in = h->chunks[ i ]; h->zStream.next_out = (Bytef *)inBuffer; h->zStream.avail_out = h->chunkLength; if ( inflate( &h->zStream, Z_PARTIAL_FLUSH ) != Z_OK ) { // err_fatal( __func__, "inflate: %s\n", h->zStream.msg ); sprintf( h->errorString, "inflate: %s\n", h->zStream.msg ); xfree( buffer ); return 0; } if ( h->zStream.avail_in ) /* err_internal( __func__, "inflate did not flush (%d pending, %d avail)\n", h->zStream.avail_in, h->zStream.avail_out ); */ { sprintf( h->errorString, "inflate did not flush (%d pending, %d avail)\n", h->zStream.avail_in, h->zStream.avail_out ); xfree( buffer ); return 0; } count = h->chunkLength - h->zStream.avail_out; dict_data_filter( inBuffer, &count, h->chunkLength, postFilter ); h->cache[ target ].count = count; h->cache[ target ].chunk = i; } if ( i == firstChunk ) { if ( i == lastChunk ) { memcpy( pt, inBuffer + firstOffset, lastOffset - firstOffset ); pt += lastOffset - firstOffset; } else { if ( count != h->chunkLength ) /* err_internal( __func__, "Length = %d instead of %d\n", count, h->chunkLength ); */ { sprintf( h->errorString, "Length = %d instead of %d\n", count, h->chunkLength ); xfree( buffer ); return 0; } memcpy( pt, inBuffer + firstOffset, h->chunkLength - firstOffset ); pt += h->chunkLength - firstOffset; } } else if ( i == lastChunk ) { memcpy( pt, inBuffer, lastOffset ); pt += lastOffset; } else { assert( count == h->chunkLength ); memcpy( pt, inBuffer, h->chunkLength ); pt += h->chunkLength; } } *pt = '\0'; break; case DICT_UNKNOWN: // err_fatal( __func__, "Cannot read unknown file type\n" ); strcpy( h->errorString, "Cannot read unknown file type" ); xfree( buffer ); return 0; } h->errorString[ 0 ] = 0; return buffer; } char * dict_error_str( dictData * data ) { return data->errorString; } const char * dz_error_str( enum DZ_ERRORS error ) { switch ( error ) { case DZ_NOERROR: return "No error"; case DZ_ERR_OPENFILE: return "Open file error"; case DZ_ERR_READFILE: return "Read file error"; case DZ_ERR_INVALID_FORMAT: return "Invalid file format"; case DZ_ERR_UNSUPPORTED_FORMAT: return "Unsupported file format"; case DZ_ERR_NOMEMORY: return "Memory allocation error"; case DZ_ERR_INTERNAL: return "Internal error"; } return "Unknown error"; }