%% options copyright owner = Dirk Krause copyright year = 2015-xxxx license = bsd %% header /** @file dk4uc2l.h Text to LaTeX conversion. LaTeX sequences to represent a 32 bit character are found in lookup tables in the ${datarootdir}/dktools/charmap directory. The functions in this module read those lookup tables needed, cache the contents and provide LaTeX sequences. After finishing output you can write recommendations (i.e. LaTeX packages to include, font encoding to use) as comments to the file. */ #ifndef DK4ERROR_H_INCLUDED #include "dk4app.h" #endif #ifndef DK4STO_H_INCLUDED #include "dk4sto.h" #endif /** Information about a package. */ typedef struct { char *pn; /**< Package name. */ unsigned char used; /**< Flag: Package used. */ } dk4_uc2l_pkg_t; /** Data for contents of one file. The structure contains information about the character ranges covered by the file. */ typedef struct { dkChar *fn; /**< Short file name. */ unsigned char loaded; /**< Flag: File loaded. */ } dk4_uc2l_file_t; /** Range of characters. */ typedef struct { dk4_uc2l_file_t *file; /**< File containing data. */ char **both; /**< LaTeX text both modes. */ char **text; /**< LaTeX text text mode. */ char **math; /**< LaTeX text math mode. */ dk4_uc2l_pkg_t ***pkgs; /**< Packages for characters. */ unsigned char *fenc; /**< Required font encodings. */ dk4_c32_t start; /**< First character of range. */ dk4_c32_t end; /**< Final character of range. */ unsigned char ia; /**< Flag: Initialisation att. */ } dk4_uc2l_range_t; /** Text to LaTeX conversion structure. */ typedef struct { char buf[16]; /**< Buffer for direct output. */ const dkChar * const *msgs; /**< Localized messages */ dkChar *dname; /**< Directory name. */ dk4_sto_t *s_ranges; /**< Ranges container. */ dk4_sto_it_t *i_ranges; /**< Ranges container iterator. */ dk4_sto_t *s_files; /**< Files container. */ dk4_sto_it_t *i_files; /**< Files container iterator. */ dk4_sto_t *s_pkgs; /**< Package information container. */ dk4_sto_it_t *i_pkgs; /**< Package information iterator. */ size_t szmsgs; /**< Number of messages in msgs. */ int had_error; /**< Flag: Errors occured. */ unsigned char utf8allowed; /**< Flag: UTF-8 output allowed. */ unsigned char fallowed; /**< Allowed font encodings. */ } dk4_uc2l_t; /** Font encodings */ enum { /** OT1 font encoding. */ DK4_UC2L_FONT_ENCODING_OT1 = 1 , /** T1 font encoding. */ DK4_UC2L_FONT_ENCODING_T1 = 2 , /** T4 font encoding. */ DK4_UC2L_FONT_ENCODING_T4 = 4 , /** T5 font encoding. */ DK4_UC2L_FONT_ENCODING_T5 = 8 , DK4_UC2L_FONT_ENCODING_ALL = ( 0 | DK4_UC2L_FONT_ENCODING_OT1 | DK4_UC2L_FONT_ENCODING_T1 | DK4_UC2L_FONT_ENCODING_T4 | DK4_UC2L_FONT_ENCODING_T5 ) }; /** Errors from this module */ enum { /** Memory allocation failed. */ DK4_UC2L_ERROR_MEMORY = 1 , /** Syntax error in configuration file. */ DK4_UC2L_ERROR_SYNTAX = 2 , /** Decoding error in configuration file. */ DK4_UC2L_ERROR_DECODING = 4 , /** Other processing errors while reading data. */ DK4_UC2L_ERROR_PROCESSING = 8 , /** Failed to open one of the data files. */ DK4_UC2L_ERROR_FOPEN = 16 , }; #ifdef __cplusplus extern "C" { #endif /** Open conversion structure, dynamically allocate memory. @param dn Directory name containing the data files. @param app Application structure for diagnostics, may be NULL. @return Pointer to conversion structure on success, NULL on error. Use dk4uc2l_close() to close the conversion structure when done with it. */ dk4_uc2l_t * dk4uc2l_open(const dkChar *dn, dk4_app_t *app); /** Open conversion structure, dynamically allocate memory. The directory containing the data files is obtained from the application (if not NULL), ${datarootdir}/dktools/charmap is used by default. @param app Application structure for diagnostics, may be NULL. @return Pointer to conversion structure on success, NULL on error. Use dk4uc2l_close() to close the conversion structure when done with it. */ dk4_uc2l_t * dk4uc2l_open_from_app(dk4_app_t *app); /** Close conversion structure, release memory. @param ulptr Conversion structure to close. */ void dk4uc2l_close(dk4_uc2l_t *ulptr); /** Enable or disable direct UTF-8 output. If you use usepackage[utf-8]{inputenc} you might want to enable UTF-8 output. @param ulptr Conversion structure to set up. @param enabled Flag: Enable UTF-8 output. */ void dk4uc2l_allow_utf8(dk4_uc2l_t *ulptr, int enabled); /** Reset information about used packages and allowed and denied font encodings. Typically you want to use this function only after finishing one output file and starting a new one. @param ulptr Conversion structure to reset. */ void dk4uc2l_clean_packages_and_fonts(dk4_uc2l_t *ulptr); /** Find LaTeX encoding for one 32 bit character. @param ulptr Conversion structure. @param c Character to search encoding for. @param mm Flag: Math mode (1) or text mode (0). @param app Application structure for diagnostics, may be NULL. @return Pointer to encoding string on success, NULL on error. */ const char * dk4uc2l_find(dk4_uc2l_t *ulptr, dk4_c32_t c, int mm, dk4_app_t *app); /** Retrieve or-combination of all allowed font encodings. You should use this function after processing all characters. */ unsigned char dk4uc2l_retrieve_allowed_fe(dk4_uc2l_t *ulptr); /** Check whether a character can be written to a LaTeX file directly. @param c32 Character to check. @return 1 for yes, 0 for no. */ int dk4uc2lat_direct(dk4_c32_t c32); /** Issue recommendations about font encodings and packages. Use this function after processing all characters. @param ulptr Conversion structure. @param fipo File to write to. */ void dk4uc2l_recommendations(dk4_uc2l_t *ulptr, FILE *fipo); /** Retrieve internal error status from conversion structure. @param ulptr Conversion structure. @return Error status, 0 or combination of DK4_UC2L_ERROR_MEMORY, DK4_UC2L_ERROR_SYNTAX, DK4_UC2L_ERROR_DECODING, DK4_UC2L_ERROR_PROCESSING, and DK4_UC2L_ERROR_FOPEN. */ int dk4uc2l_retrieve_errors(dk4_uc2l_t *ulptr); #ifdef __cplusplus } #endif %% module #include "dk4conf.h" #include #if DK4_HAVE_STRING_H #ifndef STRING_H_INCLUDED #include #define STRING_H_INCLUDED 1 #endif #endif #include "dk4const.h" #include "dk4uc2l.h" #include "dk4mem.h" #include "dk4mema.h" #include "dk4str8.h" #include "dk4strd.h" #include "dk4maasz.h" #include "dk4filed.h" #include "dk4mpl.h" #include "dk4strm.h" #include "dk4strmr.h" #include "dk4dir.h" #include "dk4dira.h" #include "dk4pathd.h" #include "dk4tsp08.h" #include "dk4enc.h" #include "dk4mai8hdu.h" #include "dk4maodh.h" #include "dk4maodd.h" $!trace-include typedef char *dk4_uc2l_char_ptr; typedef dk4_uc2l_pkg_t *dk4_uc2l_pkg_ptr; typedef dk4_uc2l_pkg_ptr *dk4_uc2l_pkg_ptr_ptr; /** Data passed to the line handler. */ typedef struct { dk4_uc2l_t *ulptr; /**< Conversion structure. */ dk4_app_t *app; /**< Application for diagnostics, may be NULL. */ const dkChar *sfn; /**< Short file name (pass 1). */ dk4_sto_t *s_r; /**< Storage for file ranges. */ dk4_sto_it_t *i_r; /**< Iterator for file ranges. */ int e_d; /**< Decoding errors. */ int e_e; /**< Encoding errors. */ int e_p; /**< Processing errors. */ } dk4_uc2l_lhobj; /** Fixed keywords used by the module, not localized. */ static const dkChar * const dk4uc2l_kwnl[] = { $!string-table macro=dkT # # 0 Preference: Directory containing map data. # dir.charmap # # 1 2 3 Subdirectories to search for. # /dktools/charmap /dk4app/charmap /dk3app/charmap # # 4 5 6 File name suffixes # .t2l .t2l.gz .t2l.bz2 # # 7 Path separator # / # # 8 String table file name. # dk4uc2l.str $!end }; /** Localized texts. */ static const dkChar * const dk4uc2l_kw_def[] = { $!string-table macro=dkT # # 0 Error: Syntax # Syntax error!\n\tHexadecimal number required at start of line! # # 1 Error: Syntax # Syntax error!\n\tHexadecimal value outside allowed 32 bit range! # # 2 3 4 Error: Syntax error! Redefintion of character! # Syntax error!\n\tRedefinition of ! Syntax error! Redefintion of character! # # 5 Error: Failed to set up text processing! # Failed to set up text processing! # # 6 7 Error: Failed to open file for reading: ... # Failed to open file for reading:\n\t" "! # # 8 Error: Syntax error!\n\tIllegal font encoding name! # Syntax error!\n\tIllegal font encoding name! # # 9 Error: Syntax error!\n\t1 to 15 packages names are allowed! # Syntax error!\n\t1 to 15 packages names are allowed! # # 10 Error: Syntax error!\n\tIllegal attribute name! # Syntax error!\n\tIllegal attribute name! # # 11 Error: Syntax error!\n\tAttribute specified without value! # Syntax error!\n\tAttribute specified without value! # # 12 13 14 Error: Conversion to UTF-8 failed for a character! # Conversion to UTF-8 failed for 0x ! Conversion to UTF-8 failed for a character! # # 15 Error: No directory for character mapping information found! # No directory for character mapping information found! # # 16 ... 21 Error messages. # Decoding failed!\n\tByte: Encoding failed!\n\tByte: Processing failed!\n\tByte: ,\n\tCharacter: ,\n\tPosition in line: . # # 22 ... 27 Error messages. # Decoding failed! Encoding failed! Processing failed! Further decoding failures! Further encoding failures! Further processing failures! $!end }; static const char * const dk4uc2l_kw8[] = { $!string-table # # 0 Font encodings # % Font encodings: # # 1 Recommended package # % Recommended package: # # 2 3 4 5 Font encoding names # ot1 t1 t4 t5 # # # $!end }; /** Attribute names. */ static const char * const dk4uc2l_attribute_names[] = { $!string-table b$oth t$ext m$ath e$ncoding p$ackages $!end }; static const char * const dk4uc2l_font_encodings[] = { $!string-table ot1 t1 t4 t5 $!end }; /** Destroy LaTeX package structure, release memory. @param pptr Package to destroy. */ static void dk4uc2l_pkg_close(dk4_uc2l_pkg_t *pptr) { $? "+ dk4uc2l_pkg_close PTR=%d", TR_IPTR(pptr) if (NULL != pptr) { pptr->used = 0x00; dk4mem_release(pptr->pn); dk4mem_free(pptr); } $? "- dk4uc2l_pkg_close" } /** Create LaTeX package structure, allocate memory. @param pn Package name. @param app Application structure for diagnostics, may be NULL. @return Pointer to new structure on success, NULL on error. */ static dk4_uc2l_pkg_t * dk4uc2l_pkg_open(const char *pn, dk4_app_t *app) { dk4_uc2l_pkg_t *back = NULL; $? "+ dk4uc2l_pkg_open" if (NULL != pn) { back = dk4mem_new_app(dk4_uc2l_pkg_t,1,app); if (NULL != back) { back->used = 0x00; back->pn = dk4str8_dup(pn, NULL); if (NULL == back->pn) { dk4uc2l_pkg_close(back); back = NULL; dk4app_log_base1(app, DK4_LL_ERROR, 90); } } } $? "- dk4uc2l_pkg_open PTR=%d", TR_IPTR(back) return back; } /** Compare two LaTeX package structures. @param l Left pointer, always a package structure. @param r Right pointer. @param cr Comparison criteria: 0 if r is a package pointer, 1 if r is just a package name string. @return Comparison result: 1 if l>r, 0 if l==r, -1 if lpn, (const char *)r); } break; default: { pr = (const dk4_uc2l_pkg_t *)r; back = strcmp(pl->pn, pr->pn); } break; } if (-1 > back) back = -1; if ( 1 < back) back = 1; } else { back = 1; } } else { if (NULL != r) { back = -1; } } return back; } /** Destroy file information structure, release memory. @param fptr File information structure to destroy. */ static void dk4uc2l_file_close(dk4_uc2l_file_t *fptr) { $? "+ dk4uc2l_file_close" if (NULL != fptr) { fptr->loaded = 0x00; dk4mem_release(fptr->fn); dk4mem_free(fptr); } $? "- dk4uc2l_file_close" } /** Create file information structure, allocate memory. @param fn File name to create structure for. @param app Application structure for diagnostics, may be NULL. @return Pointer to new structure on success, NULL on error. */ static dk4_uc2l_file_t * dk4uc2l_file_open(const dkChar *fn, dk4_app_t *app) { dk4_uc2l_file_t *back = NULL; $? "+ dk4uc2l_file_open" if (NULL != fn) { back = dk4mem_new_app(dk4_uc2l_file_t,1,app); if (NULL != back) { back->loaded = 0x00; back->fn = dk4str_dup(fn, NULL); if (NULL == back->fn) { dk4uc2l_file_close(back); back = NULL; dk4app_log_base1(app, DK4_LL_ERROR, 90); } } } $? "- dk4uc2l_file_open PTR=%d", TR_IPTR(back) return back; } /** Compare two file information structures. @param l Left pointer, always a file information structure. @param r Right pointer. @param cr Comparison criteria: 0 if the right pointer is a file information structure pointer, 1 if the right pointer is a file name string. @return Comparison result: 0 if file names are equal, -1 if lr. */ static int dk4uc2l_file_compare(const void *l, const void *r, int cr) { const dk4_uc2l_file_t *pl; const dk4_uc2l_file_t *pr; int back = 0; if (NULL != l) { if (NULL != r) { pl = (const dk4_uc2l_file_t *)l; switch (cr) { case 1: { back = dk4str_pathcmp(pl->fn, (const dkChar *)r); } break; default: { pr = (const dk4_uc2l_file_t *)r; back = dk4str_pathcmp(pl->fn, pr->fn); } break; } if (-1 > back) { back = -1; } if ( 1 < back) { back = 1; } } else { back = 1; } } else { if (NULL != r) { back = -1; } } return back; } /** Destroy range structure, release memory. @param rptr Range structure to destroy. */ static void dk4uc2l_range_close(dk4_uc2l_range_t *rptr) { dk4_er_t er; size_t nelem; size_t i; $? "+ dk4uc2l_range_close %lu %lu (0x%lx 0x%lx)", (unsigned long)(rptr->start), (unsigned long)(rptr->end), (unsigned long)(rptr->start), (unsigned long)(rptr->end) #if TRACE_DEBUG if (NULL != rptr->file) { if (NULL != (rptr->file)->fn) { $? ". file name \"%!ds\"", (rptr->file)->fn } } #endif if (NULL != rptr) { /* Overflow check when calculating number of elements in range as end-start+1 */ dk4error_init(&er); nelem = dk4ma_size_t_add( dk4ma_size_t_sub((size_t)(rptr->end), (size_t)(rptr->start), &er), (size_t)1U, &er ); if (DK4_E_NONE == er.ec) { /* Release dynamic memory. */ if (NULL != rptr->both) { for (i = 0; i < nelem; i++) { dk4mem_release((rptr->both)[i]); } dk4mem_release(rptr->both); } if (NULL != rptr->text) { for (i = 0; i < nelem; i++) { dk4mem_release((rptr->text)[i]); } dk4mem_release(rptr->text); } if (NULL != rptr->math) { for (i = 0; i < nelem; i++) { dk4mem_release((rptr->math)[i]); } dk4mem_release(rptr->math); } if (NULL != rptr->pkgs) { for (i = 0; i < nelem; i++) { dk4mem_release((rptr->pkgs)[i]); } dk4mem_release(rptr->pkgs); } if (NULL != rptr->fenc) { dk4mem_release(rptr->fenc); } } rptr->file = NULL; rptr->start = rptr->end = (dk4_c32_t)0UL; rptr->ia = 0x00; dk4mem_free(rptr); } $? "- dk4uc2l_range_close" } /** Create range structure, partially allocate memory. @param c Character, initially start and end of range. @param fptr Pointer to exisiting file information structure. @param app Application structure for diagnostics, may be NULL. @return Pointer to new structure on success, NULL on error. */ static dk4_uc2l_range_t * dk4uc2l_range_open(dk4_c32_t c, dk4_uc2l_file_t *fptr, dk4_app_t *app) { dk4_uc2l_range_t *back = NULL; $? "+ dk4uc2l_range_open" if (NULL != fptr) { back = dk4mem_new_app(dk4_uc2l_range_t,1,app); if (NULL != back) { back->file = fptr; back->both = NULL; back->text = NULL; back->math = NULL; back->pkgs = NULL; back->fenc = NULL; back->start = back->end = c; back->ia = 0x00; } } $? "- dk4uc2l_range_open PTR=%d", TR_IPTR(back) return back; } /** Compare two range informations or a range information against a character. @param l Left pointer, always a range information strucutre. @param r Right pointer. @param cr Comparison criteria: 0 if the right pointer is a range information strucutre, 1 if the right pointer is a pointer to a dk4_c32_t. @return Comparison result: -1 if lr. */ static int dk4uc2l_range_compare(const void *l, const void *r, int cr) { const dk4_uc2l_range_t *pl; const dk4_uc2l_range_t *pr; const dk4_c32_t *pc; int back = 0; if (NULL != l) { if (NULL != r) { pl = (const dk4_uc2l_range_t *)l; switch (cr) { case 1: { pc = (const dk4_c32_t *)r; if (pl->start > *pc) { back = 1; } else { if (pl->end < *pc) { back = -1; } } } break; default: { pr = (const dk4_uc2l_range_t *)r; if (pl->start < pr->start) { back = -1; } else { if (pl->start > pr->start) { back = 1; } } } break; } } else { back = 1; } } else { if (NULL != r) { back = -1; } } return back; } void dk4uc2l_close(dk4_uc2l_t *ulptr) { dk4_uc2l_pkg_t *pptr; dk4_uc2l_file_t *fptr; dk4_uc2l_range_t *rptr; $? "+ dk4uc2l_close" if (NULL != ulptr) { /* Directory name. */ dk4mem_release(ulptr->dname); /* Ranges. */ if (NULL != ulptr->s_ranges) { if (NULL != ulptr->i_ranges) { dk4sto_it_reset(ulptr->i_ranges); do { rptr = (dk4_uc2l_range_t *)dk4sto_it_next(ulptr->i_ranges); if (NULL != rptr) { dk4uc2l_range_close(rptr); } } while (NULL != rptr); dk4sto_it_close(ulptr->i_ranges); } dk4sto_close(ulptr->s_ranges); } /* File information. */ if (NULL != ulptr->s_files) { if (NULL != ulptr->i_files) { dk4sto_it_reset(ulptr->i_files); do { fptr = (dk4_uc2l_file_t *)dk4sto_it_next(ulptr->i_files); if (NULL != fptr) { dk4uc2l_file_close(fptr); } } while (NULL != fptr); dk4sto_it_close(ulptr->i_files); } dk4sto_close(ulptr->s_files); } /* Package information. */ if (NULL != ulptr->s_pkgs) { if (NULL != ulptr->i_pkgs) { dk4sto_it_reset(ulptr->i_pkgs); do { pptr = (dk4_uc2l_pkg_t *)dk4sto_it_next(ulptr->i_pkgs); if (NULL != pptr) { dk4uc2l_pkg_close(pptr); } } while (NULL != pptr); dk4sto_it_close(ulptr->i_pkgs); } dk4sto_close(ulptr->s_pkgs); } ulptr->s_ranges = NULL; ulptr->i_ranges = NULL; ulptr->s_files = NULL; ulptr->i_files = NULL; ulptr->s_pkgs = NULL; ulptr->i_pkgs = NULL; ulptr->msgs = NULL; ulptr->szmsgs = 0; dk4mem_free(ulptr); } $? "- dk4uc2l_close" } static dk4_uc2l_t * dk4uc2l_create(const dkChar *dn, dk4_app_t *app) { dk4_uc2l_t *back = NULL; int ok = 0; $? "+ dk4uc2l_create" if (NULL != dn) { back = dk4mem_new_app(dk4_uc2l_t,1,app); if (NULL != back) { back->dname = NULL; back->s_ranges = NULL; back->i_ranges = NULL; back->s_files = NULL; back->i_files = NULL; back->s_pkgs = NULL; back->i_pkgs = NULL; back->fallowed = 0xFF; back->utf8allowed = 0x00; back->had_error = 0; back->msgs = NULL; back->szmsgs = 0; back->dname = dk4str_dup(dn, NULL); if (NULL != back->dname) { back->s_ranges = dk4sto_open(NULL); if (NULL != back->s_ranges) { dk4sto_set_comp(back->s_ranges, dk4uc2l_range_compare, 0); back->s_files = dk4sto_open(NULL); if (NULL != back->s_files) { dk4sto_set_comp(back->s_files, dk4uc2l_file_compare, 0); back->s_pkgs = dk4sto_open(NULL); if (NULL != back->s_pkgs) { dk4sto_set_comp(back->s_pkgs, dk4uc2l_pkg_compare, 0); back->i_ranges = dk4sto_it_open(back->s_ranges, NULL); if (NULL != back->i_ranges) { back->i_files = dk4sto_it_open(back->s_files, NULL); if (NULL != back->i_files) { back->i_pkgs = dk4sto_it_open(back->s_pkgs, NULL); if (NULL != back->i_pkgs) { if (NULL != app) { back->msgs = dk4app_string_table( app, dk4uc2l_kwnl[8], dk4uc2l_kw_def ); } else { back->msgs = dk4uc2l_kw_def; } back->szmsgs = dk4app_string_table_size(dk4uc2l_kw_def); ok = 1; } } } } } } } if (0 == ok) { dk4uc2l_close(back); back = NULL; /* ERROR: Memory */ dk4app_log_base1(app, DK4_LL_ERROR, 90); } } } $? "- dk4uc2l_create PTR=%d", TR_IPTR(back) return back; } /** 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 dk4uc2l_fp_handler( void *obj, char *line, dk4_um_t lineno, dk4_er_t *erp ) { dkChar numbuf[16*sizeof(dk4_um_t)]; dk4_uc2l_range_t *rptr; dk4_uc2l_range_t *rptrn; dk4_uc2l_file_t *fptr; dk4_uc2l_lhobj *lhobj; dk4_app_t *app; dk4_uc2l_t *ulptr; char *p1; #if VERSION_BEFORE_20150821 char *p2; #endif const char *p3; dk4_um_t um; dk4_c32_t c32; dk4_c32_t c32a; size_t sznumbuf; int back = DK4_TSP_RES_OK; int res = 0; $? "+ dk4uc2l_fp_handler" /* Obtain contents from object. */ lhobj = (dk4_uc2l_lhobj *)obj; ulptr = lhobj->ulptr; app = lhobj->app; dk4app_set_log_source_line(app, lineno); /* Check for any text. Empty lines ok, comments ok. */ p1 = dk4str8_start(line, NULL); if (NULL == p1) { $? ". empty line" goto finished; } if ('#' == *p1) { $? ". comment line" goto finished; } /* Retrieve hexadecimal number. */ #if VERSION_BEFORE_20150821 p2 = dk4str8_next(p1, NULL); #else (void)dk4str8_next(p1, NULL); #endif um = (dk4_um_t)0UL; #if VERSION_BEFORE_20150821 p2 = NULL; #endif p3 = NULL; res = dk4ma_input_c8_hex_dk4_um_t(&um, p1, &p3, 1, NULL); if (0 == res) { $? "! hex number" back = DK4_TSP_RES_ERROR; /* ERROR: Syntax, not a number */ dk4app_log_1(app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, 0); ulptr->had_error |= DK4_UC2L_ERROR_SYNTAX; goto finished; } $? ". %lu", (unsigned long)um c32 = (dk4_c32_t)um; $? ". 0x%lx", (unsigned long)um /* Check numeric range. */ if ((dk4_um_t)0xFFFFFFFFUL < um) { $? "! out of range" back = DK4_TSP_RES_ERROR; /* ERROR: Syntax, out of range */ dk4app_log_1(app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, 1); ulptr->had_error |= DK4_UC2L_ERROR_SYNTAX; goto finished; } /* Check whether number is already contained in a range. */ rptr = (dk4_uc2l_range_t *)dk4sto_it_find_like(lhobj->i_r, &c32, 1); if (NULL != rptr) { $? "! already defined" $? ". start %lu end %lu char %lu", (unsigned long)(rptr->start), (unsigned long)(rptr->end), (unsigned long)c32 back = DK4_TSP_RES_ERROR; /* ERROR: Already defined */ sznumbuf = DK4_SIZEOF(numbuf,dkChar); if (0 != dk4ma_write_hex_unsigned(numbuf,sznumbuf,(dk4_um_t)c32,8,NULL)) { dk4app_log_3(app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, 2, 3, numbuf); } else { dk4app_log_1(app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, 4); } ulptr->had_error |= DK4_UC2L_ERROR_SYNTAX; goto finished; } rptr = (dk4_uc2l_range_t *)dk4sto_it_find_like(ulptr->i_ranges, &c32, 1); if (NULL != rptr) { $? "! already defined" back = DK4_TSP_RES_ERROR; /* ERROR: Already defined */ sznumbuf = DK4_SIZEOF(numbuf,dkChar); if (0 != dk4ma_write_hex_unsigned(numbuf,sznumbuf,(dk4_um_t)c32,8,NULL)) { dk4app_log_3(app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, 2, 3, numbuf); } else { dk4app_log_1(app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, 4); } ulptr->had_error |= DK4_UC2L_ERROR_SYNTAX; goto finished; } /* Check whether we can append to an existing range. */ if (dkC32(0) < c32) { c32a = c32 - dkC32(1); rptr = (dk4_uc2l_range_t *)dk4sto_it_find_like(lhobj->i_r, &c32a, 1); if (NULL != rptr) { rptr->end = c32; $? ". extend existing range" /* Also check if c32 closes a gap between two ranges. */ if (dkC32(0xFFFFFFFF) > c32) { c32a = c32 + dkC32(1); rptrn = (dk4_uc2l_range_t *)dk4sto_it_find_like(lhobj->i_r, &c32a, 1); if (NULL != rptrn) { $? ". close gap" c32a = rptrn->end; dk4sto_remove(lhobj->s_r, rptrn, NULL); rptr->end = c32a; } } goto finished; } } /* Check whether we can prepend before an existing range. */ if (dkC32(0xFFFFFFFF) > c32) { c32a = c32 + dkC32(1); rptr = (dk4_uc2l_range_t *)dk4sto_it_find_like(lhobj->i_r, &c32a, 1); if (NULL != rptr) { $? ". extend existing range" rptr->start = c32; goto finished; } } /* Create a new range. */ $? ". create new range" fptr = (dk4_uc2l_file_t *)dk4sto_it_find_like(ulptr->i_files, lhobj->sfn, 1); if (NULL == fptr) { fptr = dk4uc2l_file_open(lhobj->sfn, app); if (NULL != fptr) { if (0 == dk4sto_add(ulptr->s_files, fptr, NULL)) { dk4uc2l_file_close(fptr); $? "! memory" fptr = NULL; } } } if (NULL == fptr) { $? "! memory" back = DK4_TSP_RES_ERROR; /* ERROR: Memory */ dk4app_log_base1(app, DK4_LL_ERROR, 90); ulptr->had_error |= DK4_UC2L_ERROR_MEMORY; goto finished; } rptr = dk4uc2l_range_open(c32, fptr, app); if (NULL != rptr) { if (0 == dk4sto_add(lhobj->s_r, rptr, NULL)) { dk4uc2l_range_close(rptr); $? "! memory" rptr = NULL; back = DK4_TSP_RES_ERROR; /* ERROR: Memory */ dk4app_log_base1(app, DK4_LL_ERROR, 90); } } else { $? "! memory" back = DK4_TSP_RES_ERROR; /* ERROR: Memory */ dk4app_log_base1(app, DK4_LL_ERROR, 90); ulptr->had_error |= DK4_UC2L_ERROR_MEMORY; } $? ". success" finished: $? "- dk4uc2l_fp_handler %d", back return back; } /** Report an error with position in source line. @param ulptr Conversion structure. @param i1 Index of first message part in array. @param i2 Index of alternative first message part. @param bno Byte processed. @param lno Current line number. @param cno Current character number. @param cil Current character number in line. */ static void dk4uc2l_report_with_pos( dk4_uc2l_t *ulptr, dk4_app_t *app, size_t i1, size_t i2, dk4_um_t bno, dk4_um_t lno, dk4_um_t cno, dk4_um_t cil ) { dkChar b1[8*(1+sizeof(dk4_um_t))]; dkChar b2[8*(1+sizeof(dk4_um_t))]; dkChar b3[8*(1+sizeof(dk4_um_t))]; dk4_um_t oldlogline = (dk4_um_t)0UL; int allbuffersok = 0; oldlogline = dk4app_get_log_source_line(app); dk4app_set_log_source_line(app, lno); if (0 != dk4ma_write_decimal_unsigned(b3,DK4_SIZEOF(b3,dkChar),cil,0,NULL)) { if (0 != dk4ma_write_decimal_unsigned(b2,DK4_SIZEOF(b2,dkChar),cno,0,NULL)) { if (0 != dk4ma_write_decimal_unsigned(b1,DK4_SIZEOF(b1,dkChar),bno,0,NULL)) { allbuffersok = 1; } } } if (0 != allbuffersok) { dk4app_log_7( app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, i1, 19, 20, 21, b1, b2, b3 ); } else { dk4app_log_1( app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, i2 ); } dk4app_set_log_source_line(app, oldlogline); } static void dk4uc2l_report_errors( dk4_uc2l_t *ulptr, dk4_app_t *app, dk4_uc2l_lhobj *obj, dk4_er_t *er_en, dk4_er_t *er_pr ) { if (NULL != er_en) { switch (er_en->ec) { case DK4_E_DECODING_FAILED: { switch (obj->e_d) { case 0: case 1: { dk4uc2l_report_with_pos( ulptr, app, 16, 22, er_en->dt.fpos.byteno, er_en->dt.fpos.lineno, er_en->dt.fpos.charno, er_en->dt.fpos.charinline ); } break; case 2: { dk4app_log_1( app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, 25 ); } break; } if (3 > obj->e_d) { obj->e_d += 1; } } break; case DK4_E_ENCODING_FAILED: { switch (obj->e_e) { case 0: case 1: { dk4uc2l_report_with_pos( ulptr, app, 17, 23, er_en->dt.fpos.byteno, er_en->dt.fpos.lineno, er_en->dt.fpos.charno, er_en->dt.fpos.charinline ); } break; case 2: { dk4app_log_1( app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, 26 ); } break; } if (3 > obj->e_e) { obj->e_e += 1; } } break; } } if (NULL != er_pr) { if (DK4_E_NONE != er_pr->ec) { switch (obj->e_p) { case 0: case 1: { dk4uc2l_report_with_pos( ulptr, app, 18, 24, er_pr->dt.fpos.byteno, er_pr->dt.fpos.lineno, er_pr->dt.fpos.charno, er_pr->dt.fpos.charinline ); } break; case 2: { dk4app_log_1( app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, 27 ); } break; } if (3 > obj->e_p) { obj->e_p += 1; } } } } /** Process data from a stream. @param ulptr Conversion structure. @param rstrm Stream to read data from. @param sfn Short file name. @param app Application structure for diagnostics, may be NULL. @return 1 on success, 0 on error. */ static int dk4uc2l_fp_process_stream( dk4_uc2l_t *ulptr, dk4_stream_t *rstrm, const dkChar *sfn, dk4_app_t *app ) { char buf[256]; /* Line buffer */ dk4_tsp08_t tsp08; /* Text stream processor */ dk4_er_t er; /* General error report */ dk4_er_t er_en; /* Error report for decoding/encoding */ dk4_er_t er_pr; /* Error report for processing */ dk4_uc2l_lhobj obj; /* Processing object */ dk4_uc2l_range_t *rptr; /* Range to append to */ int back = 1; int res; /* Line processing result */ int cc; /* Flag: Can continue */ char c; /* Current character */ $? "+ dk4uc2l_fp_process_stream" dk4error_init(&er); dk4error_init(&er_en); dk4error_init(&er_pr); obj.ulptr = ulptr; obj.app = app; obj.sfn = sfn; obj.e_d = 0; obj.e_e = 0; obj.e_p = 0; obj.s_r = dk4sto_open(NULL); if (NULL != obj.s_r) { dk4sto_set_comp(obj.s_r, dk4uc2l_range_compare, 0); obj.i_r = dk4sto_it_open(obj.s_r, NULL); if (NULL != obj.i_r) { res = dk4tsp08_setup_line( &tsp08, (void *)(&obj), dk4uc2l_fp_handler, buf, sizeof(buf), DK4_ENCODING_ASCII, DK4_ENCODING_ASCII, &er ); if (0 != res) { cc = 1; while (1 == cc) { res = dk4stream_c8_read_byte(&c, rstrm, &er); if (0 != res) { res = dk4tsp08_add_one_byte(&tsp08, (unsigned char)c); switch (res) { case DK4_TSP_RES_ERROR: { back = 0; dk4tsp08_get_errors(&er_en, &er_pr, &tsp08); /* Report errors */ dk4uc2l_report_errors(ulptr, app, &obj, &er_en, &er_pr); dk4error_init(&er_en); dk4error_init(&er_pr); ulptr->had_error |= DK4_UC2L_ERROR_PROCESSING; } break; case DK4_TSP_RES_FATAL: { back = 0; cc = -1; dk4tsp08_get_errors(&er_en, &er_pr, &tsp08); /* Report errors */ dk4uc2l_report_errors(ulptr, app, &obj, &er_en, &er_pr); dk4error_init(&er_en); dk4error_init(&er_pr); ulptr->had_error |= DK4_UC2L_ERROR_PROCESSING; } break; } } else { cc = 0; } } if (0 == cc) { res = dk4tsp08_finish(&tsp08); switch (res) { case DK4_TSP_RES_ERROR: case DK4_TSP_RES_FATAL: { back = 0; dk4tsp08_get_errors(&er_en, &er_pr, &tsp08); /* Report errors */ dk4uc2l_report_errors(ulptr, app, &obj, &er_en, &er_pr); ulptr->had_error |= DK4_UC2L_ERROR_PROCESSING; } break; } } } else { /* ERROR: Failed to set up text processing */ dk4app_log_1(app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, 5); ulptr->had_error |= DK4_UC2L_ERROR_PROCESSING; } /* Move range descriptions from local storage to global storage. */ dk4sto_it_reset(obj.i_r); do { rptr = (dk4_uc2l_range_t *)dk4sto_it_next(obj.i_r); if (NULL != rptr) { if (0 == dk4sto_add(ulptr->s_ranges, rptr, NULL)) { back = 0; /* ERROR: Memory */ dk4app_log_base1(app, DK4_LL_ERROR, 90); dk4uc2l_range_close(rptr); } } } while (NULL != rptr); dk4sto_it_close(obj.i_r); } else { back = 0; /* ERROR: Memory */ dk4app_log_base1(app, DK4_LL_ERROR, 90); ulptr->had_error |= DK4_UC2L_ERROR_MEMORY; } dk4sto_close(obj.s_r); obj.s_r = NULL; } else { back = 0; /* ERROR: Memory */ dk4app_log_base1(app, DK4_LL_ERROR, 90); ulptr->had_error |= DK4_UC2L_ERROR_MEMORY; } $? "- dk4uc2l_fp_process_stream %d", back return back; } /** Process one data file in the first pass. @param ulptr Conversion structure. @param fn Full file name. @param sfn Short file name. @param app Application structure for diagnostics, may be NULL. @return 1 on success, 0 on error. */ static int dk4uc2l_fp_process_file_name( dk4_uc2l_t *ulptr, const dkChar *fn, const dkChar *sfn, dk4_app_t *app ) { dk4_er_t er; /* Error report */ dk4_stream_t *rstrm; /* Stream to read file */ const dkChar *oldsourcefile; /* Previous source file */ dk4_um_t oldsourceline; /* Previous source line */ size_t fnl; /* File name length */ size_t fns; /* Suffix length */ size_t i; /* Traverse suffix list */ int back = 1; int done = 0; /* Flag: Attempted to open file */ $? "+ dk4uc2l_fp_process_file_name \"%!ds\"", fn fnl = dk4str_len(fn); for (i = 4; ((i < 7) && (0 == done)); i++) { $? ". i = %u", (unsigned)i fns = dk4str_len(dk4uc2l_kwnl[i]); if (fnl > fns) { $? ". file name long enough" if (0 == dk4str_pathcmp(&(fn[fnl - fns]), dk4uc2l_kwnl[i])) { done = 1; $? ". suffix ok" dk4error_init(&er); rstrm = dk4stream_open_file_reader(fn, &er); if (NULL != rstrm) { $? ". stream ok" oldsourcefile = dk4app_get_log_source_file(app); oldsourceline = dk4app_get_log_source_line(app); dk4app_set_log_source_file(app, fn); dk4app_set_log_source_line(app, (dk4_um_t)0UL); back = dk4uc2l_fp_process_stream(ulptr, rstrm, sfn, app); dk4app_set_log_source_file(app, oldsourcefile); dk4app_set_log_source_line(app, oldsourceline); dk4stream_close(rstrm, NULL); } else { $? "! stream" back = 0; /* ERROR: Failed to open file */ dk4app_log_3(app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, 6, 7, fn); ulptr->had_error |= DK4_UC2L_ERROR_FOPEN; } } else { $? "! invalid suffix" } } else { $? "! file name too short" } } $? "- dk4uc2l_fp_process_file_name %d", back return back; } /** First pass to fill information to the structure, obtain information which character is handled by which file. @param ulptr New structure to fill with data. @param app Application structure for diagnostics. @return 1 on success, 0 on error. */ static int dk4uc2l_first_pass(dk4_uc2l_t *ulptr, dk4_app_t *app) { dkChar fnb[DK4_MAX_PATH]; /* File name buffer */ const dkChar *pth = NULL; /* Path for directory */ const dkChar *fn = NULL; /* Current file to process */ dk4_dir_t *dir = NULL; /* Traverse the directory */ int back = 0; $? "+ dk4uc2l_first_pass" dir = dk4dir_open_app(ulptr->dname, 0, app); if (NULL != dir) { back = 1; do { fn = dk4dir_next_file(dir); if (NULL != fn) { pth = dk4dir_get_path(dir); if (NULL != pth) { if (dk4str_cpy_s(fnb,DK4_SIZEOF(fnb,dkChar),pth,NULL)) { if (dk4str_cat_s(fnb,DK4_SIZEOF(fnb,dkChar),dk4uc2l_kwnl[7],NULL)) { if (dk4str_cat_s(fnb,DK4_SIZEOF(fnb,dkChar),fn,NULL)) { dk4path_correct_sep(fnb); if (0 == dk4uc2l_fp_process_file_name(ulptr, fnb, fn, app)) { back = 0; } } else { back = 0; /* ERROR: Path too long */ dk4app_log_base3(app, DK4_LL_ERROR, 98, 99, pth); ulptr->had_error |= DK4_UC2L_ERROR_PROCESSING; } } else { back = 0; /* ERROR: Path too long */ dk4app_log_base3(app, DK4_LL_ERROR, 98, 99, pth); ulptr->had_error |= DK4_UC2L_ERROR_PROCESSING; } } else { back = 0; /* ERROR: Path too long */ dk4app_log_base3(app, DK4_LL_ERROR, 98, 99, pth); ulptr->had_error |= DK4_UC2L_ERROR_PROCESSING; } } else { if (0 == dk4uc2l_fp_process_file_name(ulptr, fn, fn, app)) { back = 0; } } } } while (NULL != fn); dk4dir_close(dir); } else { /* ERROR: Failed to open directory */ ulptr->had_error |= DK4_UC2L_ERROR_FOPEN; } $? "- dk4uc2l_first_pass %d", back return back; } static void dk4uc2l_initialize_range( dk4_uc2l_t *ulptr, dk4_uc2l_range_t *rptr, dk4_app_t *app ) { dk4_er_t er; size_t nelem; size_t i; int errmem; dk4error_init(&er); nelem = dk4ma_size_t_add( dk4ma_size_t_sub((size_t)(rptr->end), (size_t)(rptr->start), &er), (size_t)1U, &er ); if (DK4_E_NONE == er.ec) { errmem = 0; rptr->both = dk4mem_new_app(dk4_uc2l_char_ptr,nelem,app); if (NULL != rptr->both) { for (i = 0; i < nelem; i++) { (rptr->both)[i] = NULL; } } else { errmem = 1; } rptr->text = dk4mem_new_app(dk4_uc2l_char_ptr,nelem,app); if (NULL != rptr->text) { for (i = 0; i < nelem; i++) { (rptr->text)[i] = NULL; } } else { errmem = 1; } rptr->math = dk4mem_new_app(dk4_uc2l_char_ptr,nelem,app); if (NULL != rptr->math) { for (i = 0; i < nelem; i++) { (rptr->math)[i] = NULL; } } else { errmem = 1; } rptr->pkgs = dk4mem_new_app(dk4_uc2l_pkg_ptr_ptr, nelem, app); if (NULL != rptr->pkgs) { for (i = 0; i < nelem; i++) { (rptr->pkgs)[i] = NULL; } } else { errmem = 1; } rptr->fenc = dk4mem_new_app(unsigned char,nelem,app); if (NULL != rptr->fenc) { for (i = 0; i < nelem; i++) { (rptr->fenc)[i] = (unsigned char)DK4_UC2L_FONT_ENCODING_ALL; } } else { errmem = 1; } if (0 != errmem) { /* ERROR: Memory */ dk4app_log_base1(app, DK4_LL_ERROR, 90); ulptr->had_error |= DK4_UC2L_ERROR_MEMORY; } } } /** 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 dk4uc2l_sp_handler( void *obj, char *line, dk4_um_t lineno, dk4_er_t *erp ) { dkChar numbuf[16*sizeof(dk4_um_t)]; dk4_er_t er; /* Error report */ dk4_uc2l_lhobj *lhobj; /* Handler object */ dk4_app_t *app; /* Application */ dk4_uc2l_range_t *rptr; /* Current range to modify */ dk4_uc2l_pkg_t *pkgptr; /* Current package to create */ dk4_uc2l_t *ulptr; /* Conversion structure */ char *pkgn[16]; /* Package names */ char *p1; /* Attribute name */ char *p2; /* Next attribute to process */ char *p4; /* Value, current value part */ char *p5; /* Next value part */ const char *pe; /* End of text when reading number */ dk4_um_t um; /* Number read at start of line */ size_t sznumbuf; /* Buffer size */ size_t ind; /* Index of current char in range */ size_t numpkg; /* Number of package names found */ size_t i; /* Current package index */ dk4_c32_t c32; /* Current character to configure */ int numres; /* Number conversion result */ int encadd; /* Flag: Add font encodings */ int res; /* Operation result */ int back = DK4_TSP_RES_OK; $? "+ dk4uc2l_sp_handler" /* Set variables. */ lhobj = (dk4_uc2l_lhobj *)obj; ulptr = lhobj->ulptr; app = lhobj->app; dk4app_set_log_source_line(app, lineno); /* Skip empty lines and comments. */ p1 = dk4str8_start(line, NULL); if (NULL == p1) { goto finished; } if ('#' == *p1) { goto finished; } p2 = dk4str8_next(p1, NULL); /* Retrieve character number. */ um = (dk4_um_t)0UL; pe = NULL; res = dk4ma_input_c8_hex_dk4_um_t(&um, p1, &pe, 1, NULL); if (0 == res) { back = DK4_TSP_RES_ERROR; /* ERROR: Syntax, not a number, already reported in first pass. */ ulptr->had_error |= DK4_UC2L_ERROR_SYNTAX; goto finished; } c32 = (dk4_c32_t)um; if ((dk4_um_t)0xFFFFFFFFUL < um) { back = DK4_TSP_RES_ERROR; /* ERROR: Syntax, out of range, already reported in first pass. */ ulptr->had_error |= DK4_UC2L_ERROR_SYNTAX; goto finished; } /* Find range. */ rptr = (dk4_uc2l_range_t *)dk4sto_it_find_like(ulptr->i_ranges, &c32, 1); if (NULL == rptr) { back = DK4_TSP_RES_ERROR; /* ERROR: Bug, range not found, must be memory problem reported in pass 1 */ ulptr->had_error |= DK4_UC2L_ERROR_PROCESSING; goto finished; } /* Initialize range if not yet done. */ if (0x00 == rptr->ia) { rptr->ia = 0x01; dk4uc2l_initialize_range(ulptr, rptr, app); } ind = (size_t)(c32 - rptr->start); p1 = p2; while (NULL != p1) { p2 = dk4str8_next(p1, NULL); p4 = dk4str8_chr(p1, '='); if (NULL != p4) { *(p4++) = '\0'; p4 = dk4str8_start(p4, NULL); if (NULL != p4) { switch (dk4str8_abbr_index(dk4uc2l_attribute_names, '$', p1, 0)) { case 0: { /* both */ if (NULL != rptr->both) { if (NULL == (rptr->both)[ind]) { (rptr->both)[ind] = dk4str8_dup(p4, NULL); if (NULL == (rptr->both)[ind]) { /* ERROR: Memory */ dk4app_log_base1(app, DK4_LL_ERROR, 90); ulptr->had_error |= DK4_UC2L_ERROR_MEMORY; } } else { /* ERROR: Already defined, reported in first pass */ sznumbuf = DK4_SIZEOF(numbuf,dkChar); numres = dk4ma_write_hex_unsigned( numbuf, sznumbuf, (dk4_um_t)c32, 8, NULL ); if (0 != numres) { dk4app_log_3( app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, 2, 3, numbuf ); } else { dk4app_log_1(app,ulptr->msgs,ulptr->szmsgs,DK4_LL_ERROR,4); } ulptr->had_error |= DK4_UC2L_ERROR_SYNTAX; } } else { /* ERROR */ ulptr->had_error |= DK4_UC2L_ERROR_MEMORY; } } break; case 1: { /* text */ if (NULL != rptr->text) { if (NULL == (rptr->text)[ind]) { (rptr->text)[ind] = dk4str8_dup(p4, NULL); if (NULL == (rptr->text)[ind]) { /* ERROR: Memory */ dk4app_log_base1(app, DK4_LL_ERROR, 90); ulptr->had_error |= DK4_UC2L_ERROR_MEMORY; } } else { /* ERROR: Already defined, reported in first pass */ sznumbuf = DK4_SIZEOF(numbuf,dkChar); numres = dk4ma_write_hex_unsigned( numbuf, sznumbuf, (dk4_um_t)c32, 8, NULL ); if (0 != numres) { dk4app_log_3( app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, 2, 3, numbuf ); } else { dk4app_log_1(app,ulptr->msgs,ulptr->szmsgs,DK4_LL_ERROR,4); } ulptr->had_error |= DK4_UC2L_ERROR_SYNTAX; } } else { /* ERROR */ ulptr->had_error |= DK4_UC2L_ERROR_MEMORY; } } break; case 2: { /* math */ if (NULL != rptr->math) { if (NULL == (rptr->math)[ind]) { (rptr->math)[ind] = dk4str8_dup(p4, NULL); if (NULL == (rptr->math)[ind]) { /* ERROR: Memory */ dk4app_log_base1(app, DK4_LL_ERROR, 90); ulptr->had_error |= DK4_UC2L_ERROR_MEMORY; } } else { /* ERROR: Already defined */ sznumbuf = DK4_SIZEOF(numbuf,dkChar); numres = dk4ma_write_hex_unsigned( numbuf, sznumbuf, (dk4_um_t)c32, 8, NULL ); if (0 != numres) { dk4app_log_3( app, ulptr->msgs,ulptr->szmsgs, DK4_LL_ERROR, 2, 3, numbuf ); } else { dk4app_log_1(app,ulptr->msgs,ulptr->szmsgs,DK4_LL_ERROR,4); } ulptr->had_error |= DK4_UC2L_ERROR_SYNTAX; } } else { /* ERROR */ ulptr->had_error |= DK4_UC2L_ERROR_MEMORY; } } break; case 3: { /* encoding */ encadd = 1; if (NULL != rptr->fenc) { if ('!' == *p4) { p4++; encadd = 0; (rptr->fenc)[ind] = (unsigned char)DK4_UC2L_FONT_ENCODING_ALL; } else { (rptr->fenc)[ind] = 0x00; } while (NULL != p4) { p5 = dk4str8_chr(p4, ','); if (NULL != p5) { *(p5++) = '\0'; } switch (dk4str8_array_index(dk4uc2l_font_encodings, p4, 0)) { case 0: { /* OT1 */ if (0 != encadd) { (rptr->fenc)[ind] |= (unsigned char)DK4_UC2L_FONT_ENCODING_OT1; } else { (rptr->fenc)[ind] &= (~((unsigned char)DK4_UC2L_FONT_ENCODING_OT1)); } } break; case 1: { /* T1 */ if (0 != encadd) { (rptr->fenc)[ind] |= (unsigned char)DK4_UC2L_FONT_ENCODING_T1; } else { (rptr->fenc)[ind] &= (~((unsigned char)DK4_UC2L_FONT_ENCODING_T1)); } } break; case 2: { /* T4 */ if (0 != encadd) { (rptr->fenc)[ind] |= (unsigned char)DK4_UC2L_FONT_ENCODING_T4; } else { (rptr->fenc)[ind] &= (~((unsigned char)DK4_UC2L_FONT_ENCODING_T4)); } } break; case 3: { /* T5 */ if (0 != encadd) { (rptr->fenc)[ind] |= (unsigned char)DK4_UC2L_FONT_ENCODING_T5; } else { (rptr->fenc)[ind] &= (~((unsigned char)DK4_UC2L_FONT_ENCODING_T5)); } } break; default : { /* ERROR: Syntax, undefined encoding */ dk4app_log_1(app,ulptr->msgs,ulptr->szmsgs,DK4_LL_ERROR,8); ulptr->had_error |= DK4_UC2L_ERROR_SYNTAX; } break; } p4 = p5; } } else { /* ERROR */ } } break; case 4: { /* packages */ dk4error_init(&er); numpkg = dk4str8_tokenize(pkgn, 16, p4, ",", &er); if (0 < numpkg) { if (NULL != rptr->pkgs) { if (NULL == (rptr->pkgs)[ind]) { (rptr->pkgs)[ind] = dk4mem_new_app(dk4_uc2l_pkg_ptr,(numpkg+1),app); if (NULL != (rptr->pkgs)[ind]) { for (i = 0; i <= numpkg; i++) { ((rptr->pkgs)[ind])[i] = NULL; } for (i = 0; i < numpkg; i++) { pkgptr = (dk4_uc2l_pkg_t *)dk4sto_it_find_like( ulptr->i_pkgs, pkgn[i], 1 ); if (NULL != pkgptr) { ((rptr->pkgs)[ind])[i] = pkgptr; } else { pkgptr = dk4uc2l_pkg_open(pkgn[i], app); if (NULL != pkgptr) { if (0 == dk4sto_add(ulptr->s_pkgs, pkgptr, NULL)) { dk4uc2l_pkg_close(pkgptr); pkgptr = NULL; } } if (NULL != pkgptr) { ((rptr->pkgs)[ind])[i] = pkgptr; } else { /* ERROR: Memory */ dk4app_log_base1(app, DK4_LL_ERROR, 90); ulptr->had_error |= DK4_UC2L_ERROR_MEMORY; } } } } else { /* ERROR: Memory */ dk4app_log_base1(app, DK4_LL_ERROR, 90); ulptr->had_error |= DK4_UC2L_ERROR_MEMORY; } } else { /* ERROR: Redefinition */ sznumbuf = DK4_SIZEOF(numbuf,dkChar); numres = dk4ma_write_hex_unsigned( numbuf, sznumbuf, (dk4_um_t)c32, 8, NULL ); if (0 != numres) { dk4app_log_3( app, ulptr->msgs,ulptr->szmsgs, DK4_LL_ERROR, 2, 3, numbuf ); } else { dk4app_log_1(app,ulptr->msgs,ulptr->szmsgs,DK4_LL_ERROR,4); } ulptr->had_error |= DK4_UC2L_ERROR_SYNTAX; } } else { /* ERROR: Memory */ dk4app_log_base1(app, DK4_LL_ERROR, 90); ulptr->had_error |= DK4_UC2L_ERROR_MEMORY; } } else { /* ERROR Syntax */ dk4app_log_1(app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, 9); ulptr->had_error |= DK4_UC2L_ERROR_SYNTAX; } } break; default: { /* ERROR: Syntax error */ dk4app_log_1(app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, 10); ulptr->had_error |= DK4_UC2L_ERROR_SYNTAX; } break; } } else { /* ERROR: Syntax */ dk4app_log_1(app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, 11); ulptr->had_error |= DK4_UC2L_ERROR_SYNTAX; } } else { /* ERROR: Syntax */ dk4app_log_1(app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, 11); ulptr->had_error |= DK4_UC2L_ERROR_SYNTAX; } p1 = p2; } finished: $? "- dk4uc2l_sp_handler %d", back return back; } static void dk4uc2l_sp_process_stream( dk4_uc2l_t *ulptr, dk4_stream_t *rstrm, const dkChar *fnb, dk4_app_t *app ) { char buf[256]; /* Line buffer */ dk4_uc2l_lhobj obj; /* Processing object */ dk4_tsp08_t tsp08; /* Text stream processor */ dk4_er_t er; /* General error report */ dk4_er_t er_en; /* Error report encoding/decoding */ dk4_er_t er_pr; /* Error report processing */ int res; /* Operation result */ int cc; /* Flag: Can continue */ char c; /* Current character to process */ obj.ulptr = ulptr; obj.app = app; obj.sfn = fnb; obj.e_d = 0; obj.e_e = 0; obj.e_p = 0; dk4error_init(&er); dk4error_init(&er_en); dk4error_init(&er_pr); res = dk4tsp08_setup_line( &tsp08, (void *)(&obj), dk4uc2l_sp_handler, buf, sizeof(buf), DK4_ENCODING_ASCII, DK4_ENCODING_ASCII, &er ); if (0 != res) { cc = 1; while (1 == cc) { res = dk4stream_c8_read_byte(&c, rstrm, &er); if (0 != res) { res = dk4tsp08_add_one_byte(&tsp08, (unsigned char)c); switch (res) { case DK4_TSP_RES_ERROR: { dk4tsp08_get_errors(&er_en, &er_pr, &tsp08); /* Report errors */ dk4uc2l_report_errors(ulptr, app, &obj, &er_en, &er_pr); dk4error_init(&er_en); dk4error_init(&er_pr); ulptr->had_error |= DK4_UC2L_ERROR_PROCESSING; } break; case DK4_TSP_RES_FATAL: { cc = -1; dk4tsp08_get_errors(&er_en, &er_pr, &tsp08); /* Report errors */ dk4uc2l_report_errors(ulptr, app, &obj, &er_en, &er_pr); dk4error_init(&er_en); dk4error_init(&er_pr); ulptr->had_error |= DK4_UC2L_ERROR_PROCESSING; } break; } } else { cc = 0; } } if (0 == cc) { res = dk4tsp08_finish(&tsp08); switch (res) { case DK4_TSP_RES_ERROR: case DK4_TSP_RES_FATAL: { dk4tsp08_get_errors(&er_en, &er_pr, &tsp08); /* Report errors */ dk4uc2l_report_errors(ulptr, app, &obj, &er_en, &er_pr); ulptr->had_error |= DK4_UC2L_ERROR_PROCESSING; } break; } } } else { ulptr->had_error |= DK4_UC2L_ERROR_PROCESSING; } } /** Attempt to load one data file if not yet loaded. @param ulptr Conversion structure. @param rptr Pointer to range causing the attempt to load. @param app Application structure for diagnostics, may be NULL. */ static void dk4uc2l_load_if_not_yet_done( dk4_uc2l_t *ulptr, dk4_uc2l_range_t *rptr, dk4_app_t *app ) { dkChar fnb[DK4_MAX_PATH]; dk4_er_t er; const dkChar *oldsourcefile; dk4_uc2l_file_t *fptr; dk4_stream_t *rstrm; dk4_um_t oldsourceline; size_t fnbsz; $? "+ dk4uc2l_load_if_not_yet_done" fptr = rptr->file; if (NULL != fptr) { $? ". fptr" if (0x00 == fptr->loaded) { $? ". must load" fptr->loaded = 0x01; fnbsz = DK4_SIZEOF(fnb, dkChar); if (0 != dk4str_cpy_s(fnb, fnbsz, ulptr->dname, NULL)) { if (0 != dk4str_cat_s(fnb, fnbsz, dk4uc2l_kwnl[7], NULL)) { if (0 != dk4str_cat_s(fnb, fnbsz, fptr->fn, NULL)) { dk4path_correct_sep(fnb); dk4error_init(&er); rstrm = dk4stream_open_file_reader(fnb, &er); if (NULL != rstrm) { oldsourcefile = dk4app_get_log_source_file(app); oldsourceline = dk4app_get_log_source_line(app); dk4app_set_log_source_file(app, fnb); dk4app_set_log_source_line(app, (dk4_um_t)0UL); dk4uc2l_sp_process_stream(ulptr, rstrm, fnb, app); dk4app_set_log_source_file(app, oldsourcefile); dk4app_set_log_source_line(app, oldsourceline); dk4stream_close(rstrm, NULL); } else { $? "! stream open" /* ERROR: Failed to open file */ dk4app_log_3( app, ulptr->msgs, ulptr->szmsgs, DK4_LL_ERROR, 6, 7, fnb ); ulptr->had_error |= DK4_UC2L_ERROR_FOPEN; } } else { $? "! path too long" /* ERROR: Path too long */ dk4app_log_base3(app, DK4_LL_ERROR, 98, 99, ulptr->dname); ulptr->had_error |= DK4_UC2L_ERROR_PROCESSING; } } else { $? "! path too long" /* ERROR: Path too long */ dk4app_log_base3(app, DK4_LL_ERROR, 98, 99, ulptr->dname); ulptr->had_error |= DK4_UC2L_ERROR_PROCESSING; } } else { $? "! path too long" /* ERROR: Path too long */ dk4app_log_base3(app, DK4_LL_ERROR, 98, 99, ulptr->dname); ulptr->had_error |= DK4_UC2L_ERROR_PROCESSING; } } else { $? ". already loaded" } } else { $? "! fptr" ulptr->had_error |= DK4_UC2L_ERROR_PROCESSING; } $? "- dk4uc2l_load_if_not_yet_done" } const char * dk4uc2l_find(dk4_uc2l_t *ulptr, dk4_c32_t c, int mm, dk4_app_t *app) { dkChar numbuf[16*sizeof(dk4_um_t)]; dk4_uc2l_range_t *rptr; dk4_uc2l_pkg_t **pptr; dk4_uc2l_pkg_t *xpptr; const char *back = NULL; size_t blgt; size_t sznumbuf; int numres; $? "+ dk4uc2l_find %lx", (unsigned long)c if (NULL != ulptr) { /* Attempt direct output. */ if (0 != dk4uc2lat_direct(c)) { ulptr->buf[0] = (char)c; ulptr->buf[1] = '\0'; back = &(ulptr->buf[0]); goto finished; } /* Attempt to find output string in data. */ rptr = (dk4_uc2l_range_t *)dk4sto_it_find_like( ulptr->i_ranges, &c, 1 ); if (NULL != rptr) { dk4uc2l_load_if_not_yet_done(ulptr, rptr, app); if (0 != mm) { if (NULL != rptr->math) { back = (rptr->math)[(size_t)(c - rptr->start)]; } if (NULL == back) { if (NULL != rptr->both) { back = (rptr->both)[(size_t)(c - rptr->start)]; } } } else { if (NULL != rptr->text) { back = (rptr->text)[(size_t)(c - rptr->start)]; } if (NULL == back) { if (NULL != rptr->both) { back = (rptr->both)[(size_t)(c - rptr->start)]; } } } if (NULL != back) { if (NULL != rptr->fenc) { ulptr->fallowed &= (rptr->fenc)[(size_t)(c - rptr->start)]; } if (NULL != rptr->pkgs) { if (NULL != (rptr->pkgs)[(size_t)(c - rptr->start)]) { pptr = (rptr->pkgs)[(size_t)(c - rptr->start)]; while (NULL != *pptr) { xpptr = *(pptr++); xpptr->used = 0x01; } } } } } if (NULL != back) { goto finished; } /* If everything failed so far, attempt to use UTF-8 output or direct output. */ if (0 != mm) { goto finished; } if (0x00 != ulptr->utf8allowed) { blgt = 15; if (0 != dk4utf8_encode((unsigned char *)(&(ulptr->buf[0])),&blgt,c,NULL)) { ulptr->buf[blgt] = '\0'; back = &(ulptr->buf[0]); } else { /* ERROR: UTF-8 encoding failed */ sznumbuf = DK4_SIZEOF(numbuf,dkChar); numres = dk4ma_write_hex_unsigned( numbuf, sznumbuf, (dk4_um_t)c, 8, NULL ); if (0 != numres) { dk4app_log_3( app, ulptr->msgs,ulptr->szmsgs, DK4_LL_ERROR, 12, 13, numbuf ); } else { dk4app_log_1(app,ulptr->msgs,ulptr->szmsgs,DK4_LL_ERROR,14); } } } else { if (dkC32(0x00000100) > c) { ulptr->buf[0] = (char)c; ulptr->buf[1] = '\0'; back = &(ulptr->buf[0]); } else { /* ERROR: No encoding found */ } } } finished: $? "- dk4uc2l_find PTR=%d", TR_IPTR(back) return back; } dk4_uc2l_t * dk4uc2l_open(const dkChar *dn, dk4_app_t *app) { dk4_er_t er; dk4_uc2l_t *back = NULL; $? "+ dk4uc2l_open" dk4error_init(&er); if (NULL != dn) { if (0 != dk4file_is_directory(dn, &er)) { back = dk4uc2l_create(dn, app); if (NULL != back) { if (0 == dk4uc2l_first_pass(back, app)) { dk4uc2l_close(back); back = NULL; } } } else { /* ERROR: Not a directory */ dk4app_log_base3(app, DK4_LL_ERROR, 152, 153, dn); } } $? "- dk4uc2l_open PTR=%d", TR_IPTR(back) return back; } dk4_uc2l_t * dk4uc2l_open_from_app(dk4_app_t *app) { dkChar fnb[DK4_MAX_PATH]; /* Buffer to construct name */ const dkChar * const *debmsg; dk4_uc2l_t *back = NULL; size_t i = 0; /* Traverse possible dirs */ size_t szdm = 0; int done = 0; /* Flag: Attempted */ int toolo = 0; /* Flag: Name too long */ int res = 0; /* Operation result */ $? "+ dk4uc2l_open_from_app" if (NULL != app) { res = dk4app_pref_get(fnb,DK4_SIZEOF(fnb,dkChar),app,dk4uc2l_kwnl[0],0); if (0 != res) { dk4path_correct_sep(fnb); if (0 != dk4file_is_directory(fnb, NULL)) { done = 1; back = dk4uc2l_open(fnb, app); } } for (i = 1; i < 4; i++) { if ((0 == done) && (NULL == back)) { if (0 != dk4str_cpy_s(fnb,DK4_SIZEOF(fnb,dkChar),app->dir_share,NULL)) { if (0 != dk4str_cat_s(fnb,DK4_SIZEOF(fnb,dkChar),dk4uc2l_kwnl[i],NULL)) { dk4path_correct_sep(fnb); if (0 != dk4file_is_directory(fnb, NULL)) { done = 1; back = dk4uc2l_open(fnb, app); } } else { toolo = 1; } } else { toolo = 1; } } } } if (NULL == back) { if (0 == done) { if (0 != toolo) { /* ERROR: shared directory name too long */ dk4app_log_base3(app, DK4_LL_ERROR, 98, 99, app->dir_share); } else { /* ERROR: No charmap directory found */ debmsg = dk4app_string_table(app, dk4uc2l_kwnl[8], dk4uc2l_kw_def); szdm = dk4app_string_table_size(dk4uc2l_kw_def); dk4app_log_1(app, debmsg, szdm, DK4_LL_ERROR, 15); } } } $? "- dk4uc2l_open_from_app PTR=%d", TR_IPTR(back) return back; } void dk4uc2l_clean_packages_and_fonts(dk4_uc2l_t *ulptr) { dk4_uc2l_pkg_t *pptr; $? "+ dk4uc2l_clean_packages_and_fonts" if (NULL != ulptr) { ulptr->fallowed = DK4_UC2L_FONT_ENCODING_ALL; dk4sto_it_reset(ulptr->i_pkgs); do { pptr = (dk4_uc2l_pkg_t *)dk4sto_it_next(ulptr->i_pkgs); if (NULL != pptr) { pptr->used = 0x00; } } while (NULL != pptr); } $? "- dk4uc2l_clean_packages_and_fonts" } unsigned char dk4uc2l_retrieve_allowed_fe(dk4_uc2l_t *ulptr) { unsigned char back = 0x00; $? "+ dk4uc2l_retrieve_allowed_fe" if (NULL != ulptr) { back = ulptr->fallowed; } $? "- dk4uc2l_retrieve_allowed_fe %u", (unsigned)back return back; } void dk4uc2l_allow_utf8(dk4_uc2l_t *ulptr, int enabled) { if (NULL != ulptr) { ulptr->utf8allowed = ((0 != enabled) ? 0x01 : 0x00); } } int dk4uc2lat_direct(dk4_c32_t c32) { int back = 0; char c; /* 8-bit character version of c32. */ $? "+ dk4uc2lat_direct %lx", (unsigned long)c32 if((unsigned long)c32 < 128UL) { c = (char)c32; switch(c) { case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case ',': case '.': case ':': case ';': case '+': case '-': case '?': case '!': case '|': case '@': case '(': case ')': case '/': case '=': case ' ': { back = 1; } break; } } $? "- dk4uc2lat_direct %d", back return back; } void dk4uc2l_recommendations(dk4_uc2l_t *ulptr, FILE *fipo) { dk4_uc2l_pkg_t *pptr; if ((NULL != ulptr) && (NULL != fipo)) { fputs(dk4uc2l_kw8[0], fipo); if (0x00 != (ulptr->fallowed & (unsigned char)DK4_UC2L_FONT_ENCODING_T1)) { fputs(dk4uc2l_kw8[3], fipo); } if (0x00 != (ulptr->fallowed & (unsigned char)DK4_UC2L_FONT_ENCODING_OT1)) { fputs(dk4uc2l_kw8[2], fipo); } if (0x00 != (ulptr->fallowed & (unsigned char)DK4_UC2L_FONT_ENCODING_T4)) { fputs(dk4uc2l_kw8[4], fipo); } if (0x00 != (ulptr->fallowed & (unsigned char)DK4_UC2L_FONT_ENCODING_T5)) { fputs(dk4uc2l_kw8[5], fipo); } fputc('\n', stdout); dk4sto_it_reset(ulptr->i_pkgs); do { pptr = (dk4_uc2l_pkg_t *)dk4sto_it_next(ulptr->i_pkgs); if (NULL != pptr) { if (0x00 != pptr->used) { fputs(dk4uc2l_kw8[1], fipo); fputs(pptr->pn, fipo); fputc('\n', fipo); } } } while (NULL != pptr); } } int dk4uc2l_retrieve_errors(dk4_uc2l_t *ulptr) { int back = 0; if (NULL != ulptr) { back = ulptr->had_error; } return back; }