%% options copyright owner = Dirk Krause copyright year = 2015-xxxx license = bsd %% header /** @file dk4wxpref.h Preferences as wxChar strings. */ #ifndef DK4CONF_H_INCLUDED #include "dk4conf.h" #endif #ifndef DK4TYPES_H_INCLUDED #include "dk4types.h" #endif #ifndef DK4CONST_H_INCLUDED #include "dk4const.h" #endif #ifndef DK4ERROR_H_INCLUDED #include "dk4error.h" #endif #ifndef DK4STO_H_INCLUDED #include "dk4sto.h" #endif #ifndef DK4STRM_H_INCLUDED #include "dk4strm.h" #endif #ifndef DK4STRMR_H_INCLUDED #include "dk4strmr.h" #endif #ifndef WX_WXPREC_H_INCLUDED #include #define WX_WXPREC_H_INCLUDED 1 #endif #ifdef __BORLANDC__ #pragma hdrstop #endif #ifndef WX_PRECOMP #if DK4_HAVE_WX_CHARTYPE_H #ifndef WX_CHARTYPE_H_INCLUDED #include #define WX_CHARTYPE_H_INCLUDED 1 #endif #else #if DK4_HAVE_WX_WXCHAR_H #ifndef WX_WXCHAR_H_INCLUDED #include #define WX_WXCHAR_H_INCLUDED 1 #endif #else #ifndef WX_WX_H_INCLUDED #include #define WX_WX_H_INCLUDED #endif #endif #endif #endif /** Preferences data. */ typedef struct { wxChar *name; /**< Name. */ wxChar *value; /**< Value. */ } dk4_wx_pref_t; #ifdef __cplusplus extern "C" { #endif /** Create preferences structure, allocate memory. @param name Preference name. @param value Preference value. @param erp Error report, may be NULL. @return Pointer to new preferences object on success, NULL on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if name or value is NULL, - DK4_E_MEMORY_ALLOCATION_FAILED
if a memory allocation failed due to insufficient memory. */ dk4_wx_pref_t * dk4wxpref_open(const wxChar *name, const wxChar *value, dk4_er_t *erp); /** Destroy preference structure, release memory. @param ptr Preference structure to destroy. */ void dk4wxpref_close(dk4_wx_pref_t *ptr); /** Comparison function. @param l Left pointer. @param r Right pointer. @param cr Comparison criteria: 0=structure/structure, 1=structure/name. @return Comparison result. */ int dk4wxpref_compare(const void *l, const void *r, int cr); /** Add dk4pref.conf file for one pass. @param spref Preferences storage. @param ipref Iterator for preferences storage. @param nameApp Application name. @param nameGrp Application group name. @param nameLog Users login name. @param nameHost Host name. @param language Language. @param region Region. @param dirHome Users home directory. @param dirEtc System configuration directory. @param dirShare Data file directory. @param encWx Encoding for wxChar strings. @param encDk Encoding for dkChar strings. @param encFile Expected encoding for files. @param pn Pass number. */ void dk4wxpref_one_pass( dk4_sto_t *spref, dk4_sto_it_t *ipref, const dkChar *nameApp, const dkChar *nameGrp, const dkChar *nameLog, const dkChar *nameHost, const dkChar *language, const dkChar *region, const dkChar *dirHome, const dkChar *dirEtc, const dkChar *dirShare, int encWx, int encDk, int encFile, int pn ); #ifdef __cplusplus } #endif %% module #include "dk4wxpref.h" #include "dk4strd.h" #include "dk4strx.h" #include "dk4recwx.h" #include "dk4mem.h" #include "dk4mpl.h" #include "dk4tspdk.h" #include "dk4fs.h" $!trace-include /** Preference reading helper structure. */ typedef struct { dk4_sto_t *spref; /**< Preference storage. */ dk4_sto_it_t *ipref; /**< Preferences iterator. */ const dkChar *nameApp; /**< Application name. */ const dkChar *nameGrp; /**< Application group name. */ const dkChar *nameLog; /**< Users login name. */ const dkChar *nameHost; /**< Host name. */ const dkChar *language; /**< Users language. */ const dkChar *region; /**< Users region. */ wxChar *wx1; /**< One wxChar buffer. */ wxChar *wx2; /**< Other wxChar buffer. */ size_t szwx; /**< Size of wxChar buffers. */ int encWx; /**< Encoding for wxChar. */ int encDk; /**< Encoding for dkChar. */ int compat; /**< Flag: Compatible mode. */ int insc; /**< Flag: In scope. */ int lasc; /**< Flag: Previous line was scope. */ } dk4_wx_pref_rd_t; /** Constant keywords used by the module, not localized. */ static const dkChar * const dk4wxpref_kw_dk[] = { $!string-table macro=dkT # # 0 1 Configuration file names. # dk3pref.conf dk4pref.conf # # 2 Asterisk as wildcard matches all. # * $!end }; /** Names for scope settings. */ static const dkChar * const dk4wxpref_scope_names[] = { $!string-table macro=dkT # # 0 # user # # 1 # application # # 2 # application-group # # 3 # host # # 4 # windows # # 5 # non-windows # # 6 # language # # 7 # region $!end }; void dk4wxpref_close(dk4_wx_pref_t *ptr) { if (NULL != ptr) { dk4mem_release(ptr->name); dk4mem_release(ptr->value); dk4mem_free(ptr); } } dk4_wx_pref_t * dk4wxpref_open(const wxChar *name, const wxChar *value, dk4_er_t *erp) { dk4_wx_pref_t *back = NULL; if ((NULL != name) && (NULL != value)) { back = dk4mem_new(dk4_wx_pref_t,1,erp); if (NULL != back) { back->name = back->value = NULL; back->name = dk4strx_dup(name, erp); back->value = dk4strx_dup(value, erp); if ((NULL == back->name) || (NULL == back->value)) { dk4wxpref_close(back); back = NULL; } else { dk4error_set_simple_error_code(erp, DK4_E_MEMORY_ALLOCATION_FAILED); } } else { dk4error_set_simple_error_code(erp, DK4_E_MEMORY_ALLOCATION_FAILED); } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } return back; } int dk4wxpref_compare(const void *l, const void *r, int cr) { const dk4_wx_pref_t *lptr; const dk4_wx_pref_t *rptr; int back = 0; if (NULL != l) { if (NULL != r) { lptr = (const dk4_wx_pref_t *)l; switch (cr) { case 1: { if (NULL != lptr->name) { back = dk4strx_cmp(lptr->name, (const wxChar *)r); } else back = -1; } break; default: { rptr = (const dk4_wx_pref_t *)r; if (NULL != lptr->name) { if (NULL != rptr->name) { back = dk4strx_cmp(lptr->name, rptr->name); } else back = 1; } else { if (NULL != rptr->name) back = -1; } } break; } if (-1 > back) back = -1; if ( 1 < back) back = 1; } else back = 1; } else { if (NULL != r) back = -1; } return back; } #if DK4_HAVE_COMPATDKTOOLS3 /** Modify version 3 preference names to version 4 names. I.e. /a/b/c -> a.b.c @param pstr String to modify. */ static void dk4wxpref_modify_compatible(dkChar *pstr) { if (dkT('/') == *pstr) { dk4str_cpy_to_left(pstr, &(pstr[1])); } while (dkT('\0') != *pstr) { if (dkT('/') == *pstr) { *pstr = dkT('.'); } pstr++; } } #endif /** Test one scope entry. @param rd Reader object. @param scn Scope part name. @return 1 if test succeeded, 0 otherwise. */ static int dk4wxpref_check_one_scope_entry(dk4_wx_pref_rd_t *rd, dkChar *scn) { dkChar *scv = NULL; int back = 1; scn = dk4str_start(scn, NULL); if (NULL != scn) { scv = dk4str_chr(scn, dkT('=')); if (NULL != scv) { *(scv++) = dkT('\0'); scv = dk4str_start(scv, NULL); } dk4str_normalize(scn, NULL); if (NULL != scv) { dk4str_normalize(scv, NULL); } back = 0; switch (dk4str_array_index(dk4wxpref_scope_names, scn, 0)) { case 0: { /* user */ if (NULL != scv) { if (0 == dk4str_cmp(dk4wxpref_kw_dk[2], scv)) { back = 1; } else { if (NULL != rd->nameLog) { #if DK4_HAVE_CASE_INSENSITIVE_USERNAMES if (0 == dk4str_casecmp(rd->nameLog, scv)) #else if (0 == dk4str_cmp(rd->nameLog, scv)) #endif { back = 1; } } } } } break; case 1: { /* application */ if (NULL != scv) { if (0 == dk4str_cmp(dk4wxpref_kw_dk[2], scv)) { back = 1; } else { if (NULL != rd->nameApp) { if (0 == dk4str_cmp(rd->nameApp, scv)) { back = 1; } } } } } break; case 2: { /* application group */ if (NULL != scv) { if (0 == dk4str_cmp(dk4wxpref_kw_dk[2], scv)) { back = 1; } else { if (NULL != rd->nameGrp) { if (0 == dk4str_cmp(rd->nameGrp, scv)) { back = 1; } } } } } break; case 3: { /* host */ if (NULL != scv) { if (0 == dk4str_cmp(dk4wxpref_kw_dk[2], scv)) { back = 1; } else { if (NULL != rd->nameHost) { if (0 == dk4str_casecmp(rd->nameHost, scv)) { back = 1; } } } } } break; case 4: { /* windows */ if (NULL != scv) { if (0 != dk4str_is_on(scv)) { #if DK4_ON_WINDOWS back = 1; #endif } else { #if !(DK4_ON_WINDOWS) back = 1; #endif } } else { #if DK4_ON_WINDOWS back = 1; #endif } } break; case 5: { /* non-windows */ if (NULL != scv) { if (0 != dk4str_is_on(scv)) { #if !(DK4_ON_WINDOWS) back = 1; #endif } else { #if DK4_ON_WINDOWS back = 1; #endif } } else { #if !(DK4_ON_WINDOWS) back = 1; #endif } } break; case 6: { /* language */ if (NULL != scv) { if (0 == dk4str_cmp(dk4wxpref_kw_dk[2], scv)) { back = 1; } else { if (NULL != rd->language) { if (0 == dk4str_casecmp(rd->language, scv)) { back = 1; } } } } } break; case 7: { /* region */ if (NULL != scv) { if (0 == dk4str_cmp(dk4wxpref_kw_dk[2], scv)) { back = 1; } else { if (NULL != rd->region) { if (0 == dk4str_casecmp(rd->region, scv)) { back = 1; } } } } } break; } } return back; } /** Process one input line. @param obj Preferences reader object. @param line Current line to process. @param lineno Line number. @param erp Error report, may be NULL. @return DK4_TSP_RES_OK if processing succeeded, DK4_TSP_RES_ERROR on errors (can continue), DK4_TSP_RES_FATAL on fatal errors (can not continue). */ static int dk4wxpref_line_handler( void *obj, dkChar *line, dk4_um_t lineno, dk4_er_t *erp ) { dk4_wx_pref_rd_t *rd; /* Reader object */ dk4_wx_pref_t *pptr; /* New pref */ dkChar *p1; /* Pref name */ dkChar *p2; /* Pref value */ wxChar *nv; /* New value */ int isneg; /* Flag: Not in scope */ int ok; /* Flag: Tests passed */ int res; /* Operation result */ int back = DK4_TSP_RES_OK; /* Function result */ rd = (dk4_wx_pref_rd_t *)obj; dk4str_delnl(line); p1 = dk4str_start(line, NULL); if (NULL != p1) { if (dkT('#') != *p1) { if (dkT('[') == *p1) { $? ". scope line" if (0 == rd->lasc) { rd->insc = 0; } p2 = dk4str_chr(p1, dkT(']')); if (NULL != p2) { *p2 = dkT('\0'); } p1++; if (dkT('-') == *p1) { isneg = 1; p1++; } else { isneg = 0; } ok = 1; while (NULL != p1) { p2 = dk4str_chr(p1, dkT(',')); if (NULL != p2) { *(p2++) = dkT('\0'); p2 = dk4str_start(p2, NULL); } if (0 == dk4wxpref_check_one_scope_entry(rd, p1)) { ok = 0; } p1 = p2; } if (1 == ok) { if (0 != isneg) { rd->insc = 0; } else { rd->insc = 1; } } rd->lasc = 1; } else { $? ". data line" if (rd->insc) { p2 = dk4str_chr(p1, dkT('=')); if (NULL != p2) { *(p2++) = dkT('\0'); p2 = dk4str_start(p2, NULL); } if ((NULL != p1) && (NULL != p2)) { dk4str_normalize(p1, NULL); dk4str_rtwh(p2, NULL); #if DK4_HAVE_COMPATDKTOOLS3 if (0 != rd->compat) { dk4wxpref_modify_compatible(p1); } #endif res = dk4recwx_dkchar_to_wxchar( rd->wx1, rd->szwx, rd->encWx, p1, rd->encDk, NULL ); if (0 < res) { res = dk4recwx_dkchar_to_wxchar( rd->wx2, rd->szwx, rd->encWx, p2, rd->encDk, NULL ); if (0 < res) { pptr = (dk4_wx_pref_t *)dk4sto_it_find_like( rd->ipref, rd->wx1, 1 ); if (NULL != pptr) { nv = dk4strx_dup(rd->wx2, NULL); if (NULL != nv) { dk4mem_release(pptr->value); pptr->value = nv; } } else { pptr = dk4wxpref_open(rd->wx1, rd->wx2, NULL); if (NULL != pptr) { if (0 == dk4sto_add(rd->spref, pptr, NULL)) { dk4wxpref_close(pptr); } } } } } } } rd->lasc = 0; } } else { $? ". comment line" } } else { $? ". empty line" } return back; } /** Use one file. @param spref Preferences storage. @param ipref Iterator for preferences storage. @param nameApp Application name. @param nameGrp Application group name. @param nameLog Users login name. @param nameHost Host name. @param language Language. @param region Region. @param fn File name. @param encWx Encoding for wxChar strings. @param encDk Encoding for dkChar strings. @param encFile Expected encoding for files. @param compat Flag: In compatibility mode. */ static void dk4wxpref_one_file( dk4_sto_t *spref, dk4_sto_it_t *ipref, const dkChar *nameApp, const dkChar *nameGrp, const dkChar *nameLog, const dkChar *nameHost, const dkChar *language, const dkChar *region, const dkChar *fn, int encWx, int encDk, int encFile, int compat ) { char ib[4096]; /* Input buffer for stream read */ wxChar wx1[1024]; /* Buffer for conversion */ wxChar wx2[DK4_SIZEOF(wx1,wxChar)]; /* too */ dkChar il[1024]; /* Input line */ dk4_wx_pref_rd_t rd; /* Reader object */ dk4_er_t er; /* Error report */ dk4_tspdk_t tsp; /* Text stream processing */ dk4_stream_t *istrm; /* Input stream */ size_t szil = DK4_SIZEOF(il,dkChar); /* Buffer size */ size_t szwx = DK4_SIZEOF(wx1,wxChar); /* Buffer size */ size_t rb; /* Bytes read */ int res; /* Operation result */ int cc; /* Flag: Can continue */ dk4error_init(&er); istrm = dk4stream_open_file_reader(fn, &er); if (NULL != istrm) { rd.spref = spref; rd.ipref = ipref; rd.nameApp = nameApp; rd.nameGrp = nameGrp; rd.nameLog = nameLog; rd.nameHost = nameHost; rd.language = language; rd.region = region; rd.compat = compat; rd.encWx = encWx; rd.encDk = encDk; rd.insc = 1; rd.lasc = 0; rd.wx1 = wx1; rd.wx2 = wx2; rd.szwx = szwx; res = dk4tspdk_setup_line( &tsp, &rd, (dk4_dk_line_handler_t *)dk4wxpref_line_handler, il, szil, encDk, encFile, &er ); if (0 != res) { cc = 1; while (1 == cc) { cc = 0; rb = sizeof(ib); if (0 != dk4stream_read(ib, &rb, istrm, &er)) { if (0 < rb) { cc = 1; res = dk4tspdk_add_bytes(&tsp, (const unsigned char *)ib, rb); if (DK4_TSP_RES_FATAL == res) { cc = -1; } } } } if (0 == cc) { #if VERSION_BEFORE_20150821 res = dk4tspdk_finish(&tsp); #else (void)dk4tspdk_finish(&tsp); #endif } } dk4stream_close(istrm, NULL); } } void dk4wxpref_one_pass( dk4_sto_t *spref, dk4_sto_it_t *ipref, const dkChar *nameApp, const dkChar *nameGrp, const dkChar *nameLog, const dkChar *nameHost, const dkChar *language, const dkChar *region, const dkChar *dirHome, const dkChar *dirEtc, const dkChar *dirShare, int encWx, int encDk, int encFile, int pn ) { dkChar fnb[DK4_MAX_PATH]; dk4_er_t er; size_t szfnb = DK4_SIZEOF(fnb,dkChar); int res; #if DK4_HAVE_COMPATDKTOOLS3 dk4error_init(&er); res = dk4fs_config_compat_one( fnb, szfnb, dk4wxpref_kw_dk[0], dirShare, dirEtc, dirHome, nameApp, nameGrp, pn, 1, 1, &er ); if (0 != res) { dk4wxpref_one_file( spref, ipref, nameApp, nameGrp, nameLog, nameHost, language, region, fnb, encWx, encDk, encFile, 1 ); } #endif dk4error_init(&er); res = dk4fs_config_one( fnb, szfnb, dk4wxpref_kw_dk[1], dirShare, dirEtc, dirHome, nameApp, nameGrp, pn, 1, &er ); if (0 != res) { dk4wxpref_one_file( spref, ipref, nameApp, nameGrp, nameLog, nameHost, language, region, fnb, encWx, encDk, encFile, 0 ); } }