%% options copyright owner = Dirk Krause copyright year = 2015-xxxx license = bsd %% header /** @file String table data type. Normally there is no need for you to deal with string tables manually. String tables are handled by the dk4_app_t related functions. CRT on Windows: Optional. */ #ifndef DK4CONF_H_INCLUDED #include "dk4conf.h" #endif #ifndef DK4TYPES_H_INCLUDED #include "dk4types.h" #endif #ifndef DK4STRM_H_INCLUDED #include "dk4strm.h" #endif #ifndef DK4ERROR_H_INCLUDED #include "dk4error.h" #endif /** String table. */ typedef struct { dkChar *shrtfn; /**< Short file name. */ dkChar **strings; /**< Strings read from file. */ size_t nstrings; /**< Number of strings. */ } dk4_string_table_t; #ifdef __cplusplus extern "C" { #endif /** Create string table from file. @param fn Short file name to store in structure. @param sz Number of lines required. @param erp Error report, may be NULL. @return Pointer to new string table structure on success, NULL on error. */ dk4_string_table_t * dk4stt_open(const dkChar *fn, size_t sz, dk4_er_t *erp); /** Apply a text stream to a string table. @param tptr String table to configure. @param strm Text stream, opened for reading. @param pre Processing encoding. @param erp1 Error report for decoding/encoding, may be NULL. @param erp2 Error report for processing, may be NULL. @return 1 on success, 0 on error. */ int dk4stt_apply_stream( dk4_string_table_t *tptr, dk4_stream_t *strm, int pre, dk4_er_t *erp1, dk4_er_t *erp2 ); /** Destroy a string table, free allocated memory. @param ptr String table to destroy. */ void dk4stt_close(dk4_string_table_t *ptr); /** Compare two string tables by short name. @param l Left pointer (always string table). @param r Right pointer (string table or short name). @param cr Comparison criteria (0=string table/string table, 1=string table/short name). @return Comparison result. */ int dk4stt_compare(const void *l, const void *r, int cr); #ifdef __cplusplus } #endif %% module #include "dk4error.h" #include "dk4mem.h" #include "dk4strd.h" #include "dk4strm.h" #include "dk4stt.h" #include "dk4tspdk.h" #include "dk4enc.h" $!trace-include void dk4stt_close(dk4_string_table_t *ptr) { dkChar **stptr; size_t nstrings; $? "+ dk4stt_close" if (NULL != ptr) { if (NULL != ptr->strings) { stptr = ptr->strings; nstrings = ptr->nstrings; while (0 < nstrings--) { dk4mem_release(*stptr); stptr++; } dk4mem_release(ptr->strings); } dk4mem_release(ptr->shrtfn); ptr->nstrings = 0; dk4mem_free(ptr); } $? "- dk4stt_close" } dk4_string_table_t * dk4stt_open(const dkChar *fn, size_t sz, dk4_er_t *erp) { dk4_string_table_t *back = NULL; dkChar **stptr = NULL; int ok = 0; $? "+ dk4stt_open \"%!ds\" %u", TR_DKSTR(fn), (unsigned)sz if ((NULL != fn) && (0 < sz)) { back = dk4mem_new(dk4_string_table_t,1,erp); if (NULL != back) { back->strings = NULL; back->nstrings = 0; back->shrtfn = dk4str_dup(fn, erp); if (NULL != back->shrtfn) { back->nstrings = sz; back->strings = dk4mem_new(DK4_PDKCHAR,sz,erp); if (NULL != back->strings) { stptr = back->strings; while (0 < sz--) { *(stptr++) = NULL; } ok = 1; } } if (1 > ok) { dk4stt_close(back); back = NULL; } } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } $? "- dk4stt_open PTR=%d", TR_IPTR(back) return back; } int dk4stt_compare(const void *l, const void *r, int cr) { int back = 0; const dk4_string_table_t *pl; const dk4_string_table_t *pr; if (NULL != l) { if (NULL != r) { pl = (const dk4_string_table_t *)l; switch (cr) { case 1: { if (NULL != pl->shrtfn) { back = dk4str_cmp(pl->shrtfn, (const dkChar *)r); } else { back = -1; } } break; default: { pr = (const dk4_string_table_t *)r; if (NULL != pl->shrtfn) { if (NULL != pr->shrtfn) { back = dk4str_cmp(pl->shrtfn, pr->shrtfn); } else { back = 1; } } else { if (NULL != pr->shrtfn) { back = -1; } } } break; } } else { back = 1; } } else { if (NULL != r) { back = -1; } } return back; } /** Structure to read a string table from stream. */ typedef struct { dk4_string_table_t *dptr; /**< String table to configure. */ size_t curpos; /**< Current line number. */ } dk4_string_table_reader_t; /** Handler function for text lines. @param obj Object to modify while processing the character. @param line Text line to process. @param lineno Current line number. @param erp Error report, may be NULL. @return DK4_TSP_RES_OK if the character was processed successfully, DK4_TSP_RES_ERROR if there was an error but we can continue, DK4_TSP_RES_FATAL if there was a fatal error so we should abort processing. */ static int dk4stt_line_handler( void *obj, dkChar *line, dk4_um_t lineno, dk4_er_t *erp ) { dk4_string_table_reader_t *reader; /* Reader structure */ #if TRACE_DEBUG dkChar *olcp; /* Original pointer */ #endif dkChar *linecp; /* Copy of line */ int back = DK4_TSP_RES_ERROR; $? "+ dk4stt_line_handler \"%!ds\"", line reader = (dk4_string_table_reader_t *)obj; if (dkT('#') != line[0]) { $? ". data line %lu", (unsigned long)(reader->curpos) if (reader->curpos < reader->dptr->nstrings) { $? ". need processing" linecp = dk4str_dup(line, NULL); if (NULL != linecp) { $? ". copy allocated" #if TRACE_DEBUG olcp = linecp; #endif /* Save copy of line to string table */ (reader->dptr->strings)[reader->curpos] = linecp; reader->curpos += 1; back = DK4_TSP_RES_OK; /* Handle backslash escape sequences, remove trailing newline */ while (dkT('\0') != *linecp) { if (dkT('\\') == *linecp) { if (dkT('r') == linecp[1]) { *linecp = dkT('\r'); dk4str_cpy_to_left(&(linecp[1]), &(linecp[2])); } else { if (dkT('n') == linecp[1]) { *linecp = dkT('\n'); dk4str_cpy_to_left(&(linecp[1]), &(linecp[2])); } else { if (dkT('t') == linecp[1]) { *linecp = dkT('\t'); dk4str_cpy_to_left(&(linecp[1]), &(linecp[2])); } else { dk4str_cpy_to_left(linecp, &(linecp[1])); if (dkT('\0') == *linecp) { linecp--; } } } } } else { if (dkT('\n') == *linecp) { *linecp = dkT('\0'); linecp--; } else { if ((dkT('\r') == *linecp) && (dkT('\n') == linecp[1])) { *linecp = dkT('\0'); linecp--; } } } linecp++; } $? ". processing result: \"%!ds\"", olcp } else { $? "! memory" /* Not enough memory. */ back = DK4_TSP_RES_FATAL; } } else { $? ". additional line" /* More lines than expected, ignore additional lines. */ back = DK4_TSP_RES_OK; } } else { $? ". comment line" /* Comment line, no processing required. */ back = DK4_TSP_RES_OK; } $? "- dk4stt_line_handler %d", back return back; } int dk4stt_apply_stream( dk4_string_table_t *tptr, dk4_stream_t *strm, int pre, dk4_er_t *erp1, dk4_er_t *erp2 ) { dkChar line[1024]; /* Input line buffer */ char buf[4096]; /* Buffer for stream input */ dk4_string_table_reader_t reader; /* Reader structure */ dk4_tspdk_t tspdk; /* Text stream processor */ dk4_er_t e1; /* Error report f decoding */ dk4_er_t e2; /* Error report f processing */ size_t rdb; /* Bytes read */ int cc; /* Flag: Can continue */ int back = 0; $? "+ dk4stt_apply_stream" if ((NULL != tptr) && (NULL != strm)) { $? ". args ok" dk4error_init(&e1); dk4error_init(&e2); reader.dptr = tptr; reader.curpos = 0; back = dk4tspdk_setup_line( &tspdk, (void *)(&reader), dk4stt_line_handler, line, DK4_SIZEOF(line,dkChar), pre, DK4_FILE_ENCODING_UTF8, &e2 ); if (0 < back) { $? ". init ok" #if VERSION_BEFORE_20150821 cc = 1; #endif do { cc = 0; rdb = sizeof(buf); if (0 < dk4stream_read(buf, &rdb, strm, NULL)) { if (0 < rdb) { $? ". bytes found" switch (dk4tspdk_add_bytes(&tspdk, (unsigned char *)buf, rdb)) { case DK4_TSP_RES_OK: { $? ". success" cc = 1; } break; case DK4_TSP_RES_ERROR: { $? "! error" cc = 1; } break; default: { $? "! fatal error" back = 0; } break; } } else { $? ". no byte found" } } else { $? ". end of input" } } while (cc); if (0 < back) { $? ". flush final bytes" if (DK4_TSP_RES_FATAL == dk4tspdk_finish(&tspdk)) { back = 0; $? "! flush failed" } if (0 < back) { $? ". flush succeeded" if (reader.curpos < (reader.dptr)->nstrings) { back = 0; $? "! too few lines" $? ". curpos = %lu", (unsigned long)(reader.curpos) $? ". nstrings = %lu", (unsigned long)((reader.dptr)->nstrings) } } } } else { $? "! init" } } else { $? "! args" dk4error_set_simple_error_code(erp2, DK4_E_INVALID_ARGUMENTS); } $? "- dk4stt_apply_stream %d", back return back; }