/* Copyright (c) 2000-2010, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above opyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Dirk Krause nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "dk.h" #include "dksf.h" #include "dkmem.h" #include "dkenc.h" #include "dksto.h" #include "dkstr.h" #include "dkapp.h" #include "dkstream.h" #include "dklogc.h" #include "dkerror.h" #if DK_HAVE_STDLIB_H #include #endif #if DK_HAVE_UNISTD_H #include #endif #if DK_HAVE_CTYPE_H #include #endif #include "dklic.h" #include "dktools-version.h" $(trace-include) #ifndef INBUFFERSIZE #define INBUFFERSIZE 16384 #endif #if DK_HAVE_FEATURE_BACKSLASH static char fn_sep[] = { "\\" }; #else static char fn_sep[] = { "/" }; #endif static char sysconfdir[] = { DK_SYSCONFDIR }; static char packagename[] = { "dktools" }; static char *error_messages[24]; static dk_string_finder_t error_finder[] = { { (char *)"/e/00", &(error_messages[0]), (char *)"No entry for key\"" }, { (char *)"/e/01", &(error_messages[1]), (char *)"\" in language \"" }, { (char *)"/e/02", &(error_messages[2]), (char *)"\"!" }, { (char *)"/e/03", &(error_messages[3]), (char *)"Not enough memory!" }, { (char *)"/e/04", &(error_messages[4]), (char *)"Empty language spec!" }, { (char *)"/e/05", &(error_messages[5]), (char *)"" }, { (char *)"/e/06", &(error_messages[6]), (char *)"No value!" }, { (char *)"/e/07", &(error_messages[7]), (char *)"" }, { (char *)"/e/08", &(error_messages[8]), (char *)"Missing \"=\"!" }, { (char *)"/e/09", &(error_messages[9]), (char *)"" }, { (char *)"/e/10", &(error_messages[10]), (char *)"Failed to write to file \"" }, { (char *)"/e/11", &(error_messages[11]), (char *)"\"!" }, { (char *)"/e/12", &(error_messages[12]), (char *)"Failed to create directory \"" }, { (char *)"/e/13", &(error_messages[13]), (char *)"\"!" }, { (char *)"/e/14", &(error_messages[14]), (char *)"Unfinished entry name." }, { (char *)"/e/15", &(error_messages[15]), (char *)"Entry value should be quoted." }, { (char *)"/e/16", &(error_messages[16]), (char *)"Missing final quote sign." }, { (char *)"/m/cc", &(error_messages[17]), (char *)"Current configuration:" }, { (char *)"/m/on", &(error_messages[18]), (char *)"on" }, { (char *)"/m/off", &(error_messages[19]), (char *)"off" }, { (char *)"/m/plain", &(error_messages[20]), (char *)"force plain output" }, { (char *)"/e/17", &(error_messages[21]), (char *)"Language_Region.Encoding string too long!" }, { (char *)"/e/18", &(error_messages[22]), (char *)"Failed to UTF-8 decode string!" }, { (char *)"/e/19", &(error_messages[23]), (char *)"UTF-8 encoded string contains characters above 0xFF!" }, { NULL, NULL, NULL} }; static char str_pref_plain[] = { "/plain" }; static char str_pref_on[] = { "on" }; static char str_pref_off[] = { "off" }; static char str_pref_auto_lang[] = { "/languages/mostly-ascii7" }; static char str_r[] = { "r" }; static int exval = 0; dk_app_t *app = NULL; static char inbuffer[INBUFFERSIZE]; typedef struct { char *lang; char *value; unsigned long lineno; } entry_for_lang; static void string_lower(char *str) { char *ptr; ptr = str; while(*ptr) { if(isupper(*ptr)) { *ptr = tolower(*ptr); } ptr++; } } static void free_entry_for_lang(entry_for_lang *e) { char *cptr; cptr = e->value; $? ". deleting entry for lang %s val %s", TR_STR(e->lang), TR_STR(cptr) if(cptr) dk_delete(cptr) ; dk_delete(e); } typedef struct { char *key; dk_storage_t *e; dk_storage_iterator_t *ei; entry_for_lang *current; unsigned long lineno; } entry_for_key; static void free_entry_for_key(entry_for_key *e) { char *cptr; entry_for_lang *el; cptr = e->key; $? ". deleting entry for key %s", TR_STR(cptr) if(cptr) dk_delete(cptr) ; if((e->e) && (e->ei)) { dksto_it_reset(e->ei); while((el = (entry_for_lang *)dksto_it_next(e->ei)) != NULL) { free_entry_for_lang(el); } } if(e->e) { dksto_close(e->e); } e->key = NULL; e->e = NULL; e->ei = NULL; e->current = NULL; dk_delete(e) ; } int compare_languages(void *p1, void *p2, int cr) { int back = 0; if(p1 && p2) { back = strcmp((char *)p1, (char *)p2); if(back < 0) back = -1; if(back > 0) back = 1; } return back; } int compare_entries_for_lang(void *p1, void *p2, int cr) { int back = 0; entry_for_lang *e1, *e2; if(p1 && p2) { switch(cr) { case 1: { e1 = (entry_for_lang *)p1; if(e1->lang) { back = strcmp((e1->lang), (char *)p2); } } break; default: { e1 = (entry_for_lang *)p1; e2 = (entry_for_lang *)p2; if((e1->lang) && (e2->lang)) { back = strcmp((e1->lang), (e2->lang)); } } break; } } if(back > 0) back = 1; if(back < 0) back = -1; return back; } int compare_entries_for_key(void *p1, void *p2, int cr) { int back = 0; entry_for_key *e1, *e2; if(p1 && p2) { switch(cr) { case 1: { e1 = (entry_for_key *)p1; if(e1->key) { back = strcmp((e1->key),(char *)p2); } } break; default: { e1 = (entry_for_key *)p1; e2 = (entry_for_key *)p2; if((e1->key) && (e2->key)) { back = strcmp((e1->key),(e2->key)); } } break; } } if(back > 0) back = 1; if(back < 0) back = -1; return back; } static entry_for_key *new_entry_for_key(char *name, unsigned long lineno) { entry_for_key *back = NULL; back = dk_new(entry_for_key,1); if(back) { back->key = dkstr_dup(name); back->e = dksto_open(0); back->ei = NULL; back->current = NULL; back->lineno = lineno; if(back->e) { back->ei = dksto_it_open(back->e); if(back->ei) { dksto_set_comp(back->e, compare_entries_for_lang, 0); } } back->current = NULL; if(!((back->key) && (back->e) && (back->ei))) { free_entry_for_key(back); back = NULL; } } return back; } typedef struct { char *filename; char *dirname; dk_storage_t *l; /* list of languages */ dk_storage_iterator_t *li; dk_storage_t *e; /* list of entries sorted by key */ dk_storage_iterator_t *ei; FILE *inputfile; dk_uword vers_major; dk_uword vers_minor; dk_uword number; entry_for_key *cefk; size_t output_filename_length; size_t output_language_length; char *output_filename_buffer; char *language_buffer; char *current_language; char *language_test; char *language_comp; int force_plain; char *language_auto; char *language_aut2; } StcCommand; static void cleanup(StcCommand *c) { entry_for_key *efk; char *cptr; if(c->language_auto) { dk_delete(c->language_auto); } if(c->language_aut2) { dk_delete(c->language_aut2); } c->language_auto = c->language_aut2 = NULL; if(c->e) { if(c->ei) { dksto_it_reset(c->ei); while((efk = (entry_for_key *)dksto_it_next(c->ei)) != NULL) { free_entry_for_key(efk); } } dksto_close(c->e); } if(c->l) { if(c->li) { dksto_it_reset(c->li); while((cptr = (char *)dksto_it_next(c->li)) != NULL) { $? ". deleting language %s", TR_STR(cptr) dk_delete(cptr); } } dksto_close(c->l); } } static char auto_complete_languages[] = { "de en fr nl be sp pt pl cs hu sv no da" }; /** Initialize stc job. @param c Stc job. @return 1 on success, 0 on error. */ static int initialize DK_P1(StcCommand *,c) { int back = 0; char auto_lang_buffer[4096]; c->l = dksto_open(0); if(c->l) { c->li = dksto_it_open(c->l); dksto_set_comp(c->l, compare_languages, 0); } c->e = dksto_open(0); if(c->e) { c->ei = dksto_it_open(c->e); dksto_set_comp(c->e, compare_entries_for_key, 0); } if(dkapp_get_pref(app, str_pref_auto_lang, auto_lang_buffer, sizeof(auto_lang_buffer), 0)) { c->language_auto = dkstr_dup(auto_lang_buffer); c->language_aut2 = dkstr_dup(auto_lang_buffer); } else { c->language_auto = dkstr_dup(auto_complete_languages); c->language_aut2 = dkstr_dup(auto_complete_languages); } if((c->l) && (c->li) && (c->e) && (c->ei)) { if((c->language_auto) && (c->language_aut2)) { back = 1; c->vers_major = c->vers_minor = 1; $? ". language auto = \"%s\" language aut2 = \"%s\"", c->language_auto, c->language_aut2 } } if(!back) { /* ERROR: Not enough memory */ dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, &(error_messages[3]), 1); } return back; } /** Copy contents of string one place to the left. @param str String to modify. */ static void copy_to_left DK_P1(char *,str) { char *xptr, *yptr; xptr = str; if(*xptr) { yptr = xptr; yptr++; while(*yptr) { *xptr = *yptr; xptr++; yptr++; } *xptr = '\0'; } } /** Squeeze a string. @param str String to squeeze. */ static void squeeze_string DK_P1(char *,str) { char *ptr; ptr = str; while(*ptr) { if(*ptr == '\\') { switch(ptr[1]) { case '\0' : { *ptr = '\0'; } break; case 'n' : { *ptr = '\n'; ptr++; copy_to_left(ptr); } break; case 't' : { *ptr = '\t'; ptr++; copy_to_left(ptr); } break; case 'b' : { *ptr = '\b'; ptr++; copy_to_left(ptr); } break; case 'r' : { *ptr = '\r'; ptr++; copy_to_left(ptr); } break; default : { *ptr = ptr[1]; ptr++; copy_to_left(ptr); } break; } } else { ptr++; } } } /** String: opening bracket. */ static char str_bropen[] = { "(" }; /** String: Closing bracket. */ static char str_brclose[] = { ")" }; /** String: space. */ static char str_space[] = { " " }; /** Set of separators. */ static char str_separators[] = { " \t.," }; /** Read input file. @param c Stc job. @return 1 on success, 0 on error. */ static int read_input DK_P1(StcCommand *,c) { int back = 0; int can_continue; char *cptr, *linestart, *valstart; entry_for_key *efk; entry_for_lang *efl; unsigned long lineno; char *errmsgs[5]; if(app) { c->inputfile = dkapp_fopen(app, c->filename, str_r); } else { c->inputfile = dksf_fopen(c->filename, str_r); } if(c->inputfile) { lineno = 0UL; back = 1; can_continue = 1; c->cefk = NULL; dkapp_set_source_filename(app, c->filename); while(can_continue && back) { if(fgets(inbuffer,sizeof(inbuffer),(c->inputfile))) { lineno++; dkapp_set_source_lineno(app, lineno); cptr = dkstr_chr(inbuffer, '#'); if(cptr) *cptr = '\0'; linestart = dkstr_start(inbuffer, NULL); if(linestart) { switch(*linestart) { case '$' : { /* special instruction */ linestart++; linestart = dkstr_start(linestart, NULL); if(linestart) { cptr = dkstr_next(linestart, NULL); if(strcmp(linestart, "version") == 0) { linestart = dkstr_next(cptr, str_separators); if(linestart) { unsigned u1; if(sscanf(cptr, "%u", &u1) == 1) { c->vers_major = u1; } if(sscanf(linestart, "%u", &u1) == 1) { c->vers_minor = u1; } } } } } break; case '"' : { /* new key */ dkstr_chomp(linestart, NULL); linestart++; cptr = dkstr_rchr(linestart, '"'); if(cptr) { *cptr = '\0'; } else { errmsgs[0] = error_messages[14]; errmsgs[1] = str_space; errmsgs[2] = str_bropen; errmsgs[3] = dkstr_start(inbuffer, NULL); if(!(errmsgs[3])) { errmsgs[3] = str_space; } errmsgs[4] = str_brclose; dkapp_log_msg(app,DK_LOG_LEVEL_WARNING,errmsgs,5); } squeeze_string(linestart); efk = (entry_for_key *)dksto_it_find_like(c->ei, linestart, 1); if(!efk) { efk = new_entry_for_key(linestart, lineno); if(efk) { efk->lineno = lineno; if(dksto_add((c->e),(void *)efk)) { c->cefk = efk; } else { /* ERROR NOT ENOUGH MEMORY */ errmsgs[0] = error_messages[3]; dkapp_log_msg(app,DK_LOG_LEVEL_ERROR,errmsgs,1); back = 0; } } else { /* ERROR NOT ENOUGH MEMORY */ errmsgs[0] = error_messages[3]; dkapp_log_msg(app,DK_LOG_LEVEL_ERROR,errmsgs,1); back = 0; } } } break; default : { /* value */ valstart = dkstr_chr(linestart, '='); if(valstart) { *(valstart++) = '\0'; valstart = dkstr_start(valstart, NULL); if(valstart) { linestart = dkstr_start(linestart, NULL); if(linestart) { dkstr_chomp(linestart, NULL); dkstr_chomp(valstart, NULL); if(*valstart == '"') { valstart++; cptr = dkstr_rchr(valstart, '"'); if(cptr) { *cptr = '\0'; } else { errmsgs[0] = error_messages[16]; dkapp_log_msg(app,DK_LOG_LEVEL_WARNING,errmsgs,1); } } else { errmsgs[0] = error_messages[15]; dkapp_log_msg(app,DK_LOG_LEVEL_WARNING,errmsgs,1); } string_lower(linestart); cptr = (char *)dksto_it_find_like(c->li, linestart, 1); if(!cptr) { cptr = dkstr_dup(linestart); if(cptr) { if(!(dksto_add(c->l, (void *)cptr))) { /* ERROR NOT ENOUGH MEMORY */ errmsgs[0] = error_messages[3]; dkapp_log_msg(app,DK_LOG_LEVEL_ERROR,errmsgs,1); back = 0; } } else { /* ERROR NOT ENOUGH MEMORY */ errmsgs[0] = error_messages[3]; dkapp_log_msg(app,DK_LOG_LEVEL_ERROR,errmsgs,1); back = 0; } } if(back) { squeeze_string(valstart); efl = dk_new(entry_for_lang,1); if(efl) { efl->lang = cptr; efl->value = dkstr_dup(valstart); efl->lineno = lineno; if(efl->value) { if(!(dksto_add((c->cefk)->e, (void *)efl))) { /* ERROR NOT ENOUGH MEMORY */ errmsgs[0] = error_messages[3]; dkapp_log_msg(app,DK_LOG_LEVEL_ERROR,errmsgs,1); back = 0; dk_delete(efl->value); efl->value = NULL; efl->lang = NULL; dk_delete(efl); efl = NULL; } } else { /* ERROR NOT ENOUGH MEMORY */ errmsgs[0] = error_messages[3]; dkapp_log_msg(app,DK_LOG_LEVEL_ERROR,errmsgs,1); back = 0; efl->lang = NULL; dk_delete(efl); efl = NULL; } } else { /* ERROR NOT ENOUGH MEMORY */ errmsgs[0] = error_messages[3]; dkapp_log_msg(app,DK_LOG_LEVEL_ERROR,errmsgs,1); back = 0; } } } else { /* ERROR EMPTY KEY */ errmsgs[0] = error_messages[4]; dkapp_log_msg(app,DK_LOG_LEVEL_ERROR,errmsgs,1); back = 0; } } else { /* ERROR NO VALUE IN LINE */ errmsgs[0] = error_messages[6]; dkapp_log_msg(app,DK_LOG_LEVEL_ERROR,errmsgs,1); back = 0; } } else { /* ERROR WRONG INPUT LINE, MISSING = */ errmsgs[0] = error_messages[8]; dkapp_log_msg(app,DK_LOG_LEVEL_ERROR,errmsgs,1); back = 0; } } break; } } } else { can_continue = 0; } } dkapp_set_source_lineno(app, 0UL); dkapp_set_source_filename(app, NULL); fclose(c->inputfile); } else { dkapp_err_fopenr(app, c->filename); } return back; } /** Find needed buffer length (length of larget entry). @param c Stc job. @return Needed buffer length. */ static size_t find_buffer_length DK_P1(StcCommand *,c) { size_t back = 0; size_t lgt; char *x; dksto_it_reset(c->li); while((x = (char *)dksto_it_next(c->li)) != NULL) { lgt = strlen(x); if(lgt > back) { back = lgt; } } c->output_language_length = (back + 1); x = dkstr_rchr(c->filename, fn_sep[0]); if(!x) x = c->filename; else x++; back += strlen(x) + 4; back += strlen(c->dirname) + 1; return back; } /** Check whether directory \a s exists, create if necessary. @param s Directory name. @return 1 on success (directory can be used), 0 on error. */ static int check_directory DK_P1(char *,s) { int back = 1; dk_stat_t *st; char *errmsgs[5]; st = dkstat_open(s); if(st) { $? ". already exists %s", TR_STR(s) if(((st->filetype) & (~(DK_FT_SYMLINK))) != DK_FT_DIR) { $? ". wrong filetype" /* ERROR WRONG FILETYPE */ back = 0; } } else { $? ". %s does not exist", TR_STR(s) if(!dksf_mkdir(s, 0755)) { $? ". mkdir failed" /* ERROR MKDIR FAILED */ back = 0; } } if(!back) { errmsgs[0] = error_messages[12]; errmsgs[1] = s; errmsgs[2] = error_messages[13]; dkapp_log_msg(app,DK_LOG_LEVEL_ERROR,errmsgs,3); } $? "= check_directory %s %d", TR_STR(s), back return back; } /** Create directories for language/name/encoding. @param c Stc job. @return 1 on success, 0 on error. */ static int create_directories DK_P1(StcCommand *,c) { int back = 1; char *buffer, *lbuffer; char *x; char *lang, *reg, *enc; $? "+ create_directories" if(!check_directory(c->dirname)) { back = 0; } buffer = c->output_filename_buffer; lbuffer = c->language_buffer; if(back) { dksto_it_reset(c->li); while((x = (char *)dksto_it_next(c->li)) != NULL) { strcpy(lbuffer,x); lang = lbuffer; enc = dkstr_chr(lang, '.'); if(enc) { *(enc++) = '\0'; } reg = dkstr_chr(lang, '_'); if(reg) { *(reg++) = '\0'; } if(lang) { strcpy(buffer, c->dirname); strcat(buffer, fn_sep); strcat(buffer, lang); if(!check_directory(buffer)) { back = 0; } if(reg) { strcpy(buffer, c->dirname); strcat(buffer, fn_sep); strcat(buffer, lang); strcat(buffer, fn_sep); strcat(buffer, reg); if(!check_directory(buffer)) { back = 0; } if(enc) { strcpy(buffer, c->dirname); strcat(buffer, fn_sep); strcat(buffer, lang); strcat(buffer, fn_sep); strcat(buffer, reg); strcat(buffer, fn_sep); strcat(buffer, enc); if(!check_directory(buffer)) { back = 0; } } } else { if(enc) { strcpy(buffer, c->dirname); strcat(buffer, fn_sep); strcat(buffer, lang); strcat(buffer, fn_sep); strcat(buffer, enc); if(!check_directory(buffer)) { back = 0; } } } } } } $? "- create_directories %d", back return back; } /** Retrieve file name component from a full path name. @param fullname Full path name. @return File name or NULL. */ static char * get_filename DK_P1(char *,fullname) { char *back = NULL; back = dkstr_rchr(fullname, fn_sep[0]); if(back) back++; else back = fullname; return back; } /** File header for the binary string tables. */ static char file_header[] = { "STRT-2" }; /** Output file name suffix. */ static char stt_suffix[] = { ".stt" }; /** Empty string. */ static char empty_string[] = { "" }; /** File open mode. */ static char str_wb[] = { "wb" }; /** Write entries for the current language. @param c Stc job. @return 1 on success, 0 on error. */ static int write_language DK_P1(StcCommand *,c) { int back = 1; char *filename, *lc, *lt, *currentlang, *lang, *reg, *enc; entry_for_key *efk; dk_stream_t *strm; dk_uword uw; char *errmsgs[5]; /* Prepare the filename */ strcpy(c->output_filename_buffer, c->dirname); strcat(c->output_filename_buffer, fn_sep); filename = c->output_filename_buffer; while(*filename) filename++; strcpy(filename, c->current_language); while(*filename) { if(*filename == '_') *filename = fn_sep[0]; if(*filename == '.') *filename = fn_sep[0]; filename++; } filename = get_filename(c->filename); strcat(c->output_filename_buffer, fn_sep); strcat(c->output_filename_buffer, filename); filename = dksf_get_file_type_dot(c->output_filename_buffer); if(filename) { strcpy(filename, stt_suffix); } else { strcat(c->output_filename_buffer, stt_suffix); } /* Set current pointer for all entries */ dksto_it_reset(c->ei); lc = c->language_comp; lt = c->language_test; currentlang = c->current_language; strcpy(lc, currentlang); lang = lc; enc = dkstr_chr(lang, '.'); if(enc) { *(enc++) = '\0'; } reg = dkstr_chr(lang, '_'); if(reg) { *(reg++) = '\0'; } while((efk = (entry_for_key *)dksto_it_next(c->ei)) != NULL) { efk->current = (entry_for_lang *)dksto_it_find_like((efk->ei),currentlang,1); if(!(efk->current)) { /* lang/reg */ if(lang && reg) { strcpy(lt, lang); strcat(lt, "_"); strcat(lt, reg); efk->current = (entry_for_lang *)dksto_it_find_like((efk->ei),lt,1); } } if(!(efk->current)) { /* lang/enc */ if(lang && enc) { strcpy(lt, lang); strcat(lt, "."); strcat(lt, enc); efk->current = (entry_for_lang *)dksto_it_find_like((efk->ei),lt,1); } } if(!(efk->current)) { /* lang */ if(lang) { strcpy(lt, lang); efk->current = (entry_for_lang *)dksto_it_find_like((efk->ei),lt,1); } } if(!(efk->current)) { /* any */ dksto_it_reset(efk->ei); efk->current = (entry_for_lang *)dksto_it_next(efk->ei); /* WARNING NO MATCHING ENTRY FOUND */ errmsgs[0] = error_messages[0]; errmsgs[1] = efk->key; errmsgs[2] = error_messages[1]; errmsgs[3] = c->current_language; errmsgs[4] = error_messages[2]; dkapp_set_source_lineno(app, efk->lineno); dkapp_set_source_filename(app, c->filename); dkapp_log_msg(app,DK_LOG_LEVEL_WARNING,errmsgs,5); dkapp_set_source_lineno(app, 0UL); dkapp_set_source_filename(app, NULL); } } /* Now write */ if(c->force_plain) { strm = dkapp_stream_openfile(app, c->output_filename_buffer, str_wb); } else { int reason; reason = 0; strm = dkapp_write_file(app, c->output_filename_buffer); } if(strm) { dkstream_write(strm, file_header, sizeof(file_header)); uw = c->vers_major; dkstream_wb_uword(strm, uw); uw = c->vers_minor; dkstream_wb_uword(strm, uw); uw = c->number; dkstream_wb_uword(strm, uw); uw = 0; dkstream_wb_uword(strm, uw); dksto_it_reset(c->ei); while((efk = (entry_for_key *)dksto_it_next(c->ei)) != NULL) { dkstream_wb_string(strm, efk->key); } dksto_it_reset(c->ei); while((efk = (entry_for_key *)dksto_it_next(c->ei)) != NULL) { filename = empty_string; if(efk->current) { if((efk->current)->value) { filename = (efk->current)->value; } } $? ". writing language entry %s %s", c->current_language, TR_STR(filename) dkstream_wb_string(strm, filename); } dkstream_close(strm); } else { /* ERROR FAILED TO WRITE TO FILE */ errmsgs[0] = error_messages[10]; errmsgs[1] = c->output_filename_buffer; errmsgs[2] = error_messages[11]; dkapp_log_msg(app, DK_LOG_LEVEL_ERROR,errmsgs,3); back = 0; } return back; } /** Write entries for all languages to output. @param c Stc job. @return 1 on success, 0 on errors. */ static int write_all_languages DK_P1(StcCommand *,c) { int back = 1; dk_uword uw; char *efl; uw = 0; dksto_it_reset(c->ei); while(dksto_it_next(c->ei)) { uw++; } c->number = uw; dksto_it_reset(c->li); while((efl = (char *)dksto_it_next(c->li)) != NULL) { c->current_language = efl; if(!write_language(c)) { back = 0; } } return back; } /** Write output file. @param c Stc job. @return 1 on success, 0 on error. */ static int write_output DK_P1(StcCommand *,c) { int back = 0; size_t lgt; char *buffer, *langbuffer, *langtest, *langcomp, *errmsgs[5]; lgt = c->output_filename_length = find_buffer_length(c); buffer = dk_new(char,lgt); if(buffer) { c->output_filename_buffer = buffer; lgt = c->output_language_length; langbuffer = dk_new(char,lgt); langtest = dk_new(char,lgt); langcomp = dk_new(char,lgt); if(langbuffer && langtest && langcomp) { c->language_buffer = langbuffer; c->language_test = langtest; c->language_comp = langcomp; if(create_directories(c)) { back = write_all_languages(c); } c->language_buffer = NULL; c->language_test = NULL; c->language_comp = NULL; } else { errmsgs[0] = error_messages[3]; dkapp_log_msg(app,DK_LOG_LEVEL_ERROR,errmsgs,1); } if(langbuffer) dk_delete(langbuffer); if(langtest) dk_delete(langtest); if(langcomp) dk_delete(langcomp); c->output_filename_buffer = NULL; dk_delete(buffer); } else { errmsgs[0] = error_messages[3]; dkapp_log_msg(app,DK_LOG_LEVEL_ERROR,errmsgs,1); } return back; } /** Encoding suffix for UTF-8. */ static char str_utf8[] = { ".utf-8" }; /** Check whether a language/region/encoding needs handling (data is incomplete). @param c Stc command. @param n Language/region/encoding. @return 1=yes, 0=no. */ static int language_needs_handling DK_P2(StcCommand *,c, char *,n) { int back = 0; char buffer[32]; /* 12 should be sufficient */ char *cptr, *cp1, *cp2; $? "+ language_needs_handling %s", TR_STR(n) if(n) { if(strlen(n) < sizeof(buffer)) { strcpy(buffer, n); cptr = dkstr_chr(buffer, '_'); if(cptr) { *cptr = '\0'; } cptr = dkstr_chr(buffer, '.'); if(cptr) { *cptr = '\0'; } $? ". language is %s", buffer strcpy(c->language_aut2, c->language_auto); $? ". complete list = \"%s\"", c->language_aut2 cp1 = dkstr_start(c->language_aut2, NULL); while((!back) && (cp1)) { cp2 = dkstr_next(cp1, NULL); $? ". check \"%s\"=\"%s\"", cp1, buffer if(dkstr_casecmp(cp1, buffer) == 0) { back = 1; $? ". found" } if(!back) { cp1 = cp2; } } } else { $? "! language/region/encoding string too long" /* ERROR: language/region/encoding string too long */ dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, &(error_messages[21]), 1); } } $? "- language_needs_handling %d", back return back; } /** Create counterpart name for language/region/encoding. Examples: de_DE.UTF-8 -> de_DE, de_DE -> de_DE.UTF-8. @param l Original language/region/encoding. @param b Result buffer. @param sz Size of \a b (bytes). @param enc Current encoding. @return 1 on success, 0 on error. */ static int create_counterpart_name DK_P4(char *,l, char *,b, size_t, sz, int *,enc) { int back = 0; size_t sz_appendix, sz_buffer; $? "+ create_counterpart_name %s", TR_STR(l) if(strlen(l) < sz) { strcpy(b, l); back = 1; sz_appendix = strlen(str_utf8); sz_buffer = strlen(b); *enc = 0; if(sz_buffer > sz_appendix) { if(dkstr_casecmp(&(b[sz_buffer - sz_appendix]),str_utf8) == 0) { *enc = 1; } } if(*enc) { b[sz_buffer - sz_appendix] = '\0'; } else { if((sz_buffer + sz_appendix) < sz) { strcat(b, str_utf8); } else { back = 0; /* ERROR: language/encoding/region string too long */ $? "! language/encoding/region string too long" dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, &(error_messages[21]), 1); } } } $? "- create_counterpart_name %d", back return back; } /** Add counter part in other encoding. @param c Stc job. @param efk Entry for key. @param el Entry for language @param nc Counterpart language/region/encoding name. @param se Source encoding. @return 1 on success, 0 on error. */ static int add_counterpart DK_P5(StcCommand *,c, entry_for_key *,efk, entry_for_lang *,el, char *,nc, int,se) { int back = 1; /* function result */ char *newname; /* new name, new string */ char *languagename; /* the language processed */ int ec; /* error code from conversion */ $? "+ add_counterpart" /* add counterpart language to list of languages if necessary */ if((languagename = (char *)dksto_it_find_like(c->li, nc, 1)) == NULL) { $? ". must add new language" newname = dkstr_dup(nc); if(newname) { if(!dksto_add(c->l, (void *)newname)) { back = 0; /* ERROR: Not enough memory */ dk_delete(newname); dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, &(error_messages[3]), 1); $? "! failed to add new language" } else { $? ". new language added successfully" languagename = newname; } } else { back = 0; /* ERROR: Not enough memory */ $? ". failed to create copy of language name" dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, &(error_messages[3]), 1); } } /* create and add new entry */ if((back) && (languagename)) { newname = NULL; ec = 0; if(se) { newname = dkenc_str_utf82bits8(el->value, &ec); } else { newname = dkenc_str_bits82utf8(el->value); if(!newname) { ec = DK_ERR_NOMEM; } } if(newname) { entry_for_lang *newefl; if(ec == DK_ERR_MATH_OOR) { dkapp_log_msg(app, DK_LOG_LEVEL_WARNING, &(error_messages[23]), 1); } newefl = dk_new(entry_for_lang,1); if(newefl) { newefl->lang = languagename; newefl->value = newname; newefl->lineno = el->lineno; if(!dksto_add(efk->e, (void *)newefl)) { newefl->lang = NULL; newefl->value = NULL; dk_delete(newname); dk_delete(newefl); newname = NULL; newefl = NULL; } } else { back = 0; /* ERROR: Not enough memory */ $? "! Failed to create new efl" dk_delete(newname); newname = NULL; dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, &(error_messages[3]), 1); } } else { back = 0; $? "! Failed to create re-encoded copy" switch(ec) { case DK_ERR_INVALID_ARGS: { dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, &(error_messages[22]), 1); } break; case DK_ERR_MATH_OOR: { dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, &(error_messages[23]), 1); } break; default: { dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, &(error_messages[3]), 1); } break; } } } $? "- add_counterpart %d", back return back; } /** Handle one language for an entry. @param c Stc job. @param efk Entry for key. @param el Entry for language. @return 1 on success, 0 on error. */ static int handle_language_for_entry DK_P3(StcCommand *,c, entry_for_key *,efk, entry_for_lang *,el) { int back = 1; char buffer[32]; int src_encoding; $? "+ handle_language_for_entry" src_encoding = 0; if(create_counterpart_name(el->lang, buffer, sizeof(buffer), &src_encoding)) { $? ". create_counterpart -> \"%s\"", buffer if(!dksto_it_find_like(efk->ei,buffer,1)) { $? ". no counterpart entry yet for %s", buffer if(!add_counterpart(c,efk,el,buffer,src_encoding)) { back = 0; } } else { $? ". counterpart entry already exists for %s", buffer } } else { back = 0; /* ERROR: language/encoding/region string too long */ dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, &(error_messages[21]), 1); } $? "- handle_language_for_entry %d", back return back; } /** Automatically complete an entry if necessary and possible. @param c Stc job. @param efk Entry for one key. @return 1 on success, 0 on error. */ static int auto_complete_entry DK_P2(StcCommand *,c, entry_for_key *,efk) { int back = 0; dk_storage_t *e; /* list of entries */ dk_storage_iterator_t *ei; /* iterator for all entries */ entry_for_lang *el; $? "+ auto_complete_entry %s", TR_STR(efk->key) e = dksto_open(0); ei = NULL; if(e) { ei = dksto_it_open(e); if(ei) { back = 1; dksto_set_comp(e, compare_entries_for_lang, 0); dksto_it_reset(efk->ei); while((el = (entry_for_lang *)dksto_it_next(efk->ei)) != NULL) { $? ". found language %s", TR_STR(el->lang) if(!dksto_add(e, (void *)el)) { back = 0; } } if(back) { dksto_it_reset(ei); while((el = (entry_for_lang *)dksto_it_next(ei)) != NULL) { dkapp_set_source_lineno(app, el->lineno); if(language_needs_handling(c, el->lang)) { if(!handle_language_for_entry(c,efk,el)) { back = 0; } } } } else { /* ERROR: Not enough memory to register language entry */ dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, &(error_messages[3]), 1); } dksto_it_close(ei); } else { /* ERROR: Not enough memory for iterator */ dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, &(error_messages[3]), 1); } dksto_close(e); e = NULL; } else { /* ERROR: Not enough memory for list */ dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, &(error_messages[3]), 1); } $? "- auto_complete_entry %d", back return back; } /** Complete missing encodings if necessary and possible. @param c Stc job. @return 1 on success, 0 on error. */ static int auto_complete_encoding DK_P1(StcCommand *,c) { int back = 0; entry_for_key *efk; if((c->e) && (c->ei)) { back = 1; dkapp_set_source_filename(app, c->filename); dksto_it_reset(c->ei); while((efk = (entry_for_key *)dksto_it_next(c->ei)) != NULL) { $? ". checking entry %s", TR_STR(efk->key) dkapp_set_source_lineno(app, efk->lineno); if(!auto_complete_entry(c, efk)) { $? "! problem in completion" back = 0; } } dkapp_set_source_filename(app, NULL); dkapp_set_source_lineno(app, 0UL); } else { $? "! internal setup problem" } return back; } /** Run normally. @param filename Input file name. @param dirname Output directory name. @param force_plain Flag: force plain output. */ static void run DK_P3(char *,filename, char *,dirname, int,force_plain) { StcCommand c; c.filename = filename; c.dirname = dirname; c.l = NULL; c.li = NULL; c.e = NULL; c.ei = NULL; c.inputfile = NULL; c.force_plain = force_plain; if(initialize(&c)) { if(read_input(&c)) { $? ". version %u.%u", c.vers_major, c.vers_minor if(auto_complete_encoding(&c)) { write_output(&c); } else { $? "! error in encoding auto completion" } } } cleanup(&c); } /** Version number. */ static char the_version_number[] = { VERSNUMB } ; /** License terms. */ static char *license_terms[] = { "Redistribution and use in source and binary forms, with or without", "modification, are permitted provided that the following conditions are met:", "* Redistributions of source code must retain the above copyright notice, this", " list of conditions and the following disclaimer.", "* Redistributions in binary form must reproduce the above copyright notice,", " this list of conditions and the following disclaimer in the documentation", " and/or other materials provided with the distribution.", "* Neither the name of the Dirk Krause nor the names of other contributors may", " be used to endorse or promote products derived from this software without", " specific prior written permission.", "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"", "AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE", "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE", "ARE DISCLAIMED.", "IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY", "DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES", "(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;", "LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND", "ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT", "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS", "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.", NULL }; /** Print version number and license information. */ static void print_version DK_P0() { char **ptr; printf("\n"); printf( "stc 2 - string table compiler (part of the dklibs collection, version %s)\n", the_version_number ); printf("Copyright (C) 2001-2010 Dipl.-Ing. D. Krause\n"); printf("http://dktools.sourceforge.net/stc.html\n\n"); ptr = license_terms; while(*ptr) { printf("%s\n", *(ptr++)); } printf("\nLibraries used:\n\n"); ptr = dklic_get(); while(*ptr) { printf("%s\n", *(ptr++)); } printf("\n"); } /** Default help text if help text file is not found. */ static char *help_strings[] = { (char *)"stc ", (char *)"", (char *)"This program compiles a text-form string table into a set", (char *)"of binary string tables.", (char *)"The binary string tables are stored in and its", (char *)"subdirectories depending on languages, regions and encodings", (char *)"used in the string tables.", (char *)"", NULL }; /** Name of help text file. */ static char str_filename_stc_txt[] = { "stc.txt" }; /** Print help text. */ static void print_help DK_P0() { dkapp_help(app, str_filename_stc_txt, help_strings); } /** String table name. */ static char str_table_name_stc[] = { "stc" }; /** Long options handled by the program. */ static char *long_options[] = { /* 00 */ "p$lain", /* 01 */ "c$onfigure", /* 02 */ "u$nconfigure", /* 03 */ "r$eset", /* 04 */ "sh$ow-configuration", /* 05 */ "v$ersion", /* 06 */ "h$elp", NULL }; /** Convert text to boolean value. @param str Text to convert. @return 1 for true, 0 for false. */ static int decide DK_P1(char *,str) { int back = 0; if(str) { switch(*str) { case '\0': case '+': back = 1; break; case '-': back = 0; break; default: back = dkstr_is_on(str); break; } } return back; } /** Show current configuration. @param app Application @param fp Flag: Force plain output. */ static void show_configuration DK_P2(dk_app_t *,app, int, fp) { dkapp_stdout(app, error_messages[17]); fputc('\n', stdout); dkapp_stdout(app, "-p "); dkapp_stdout(app, error_messages[fp ? 18 : 19]); dkapp_stdout(app, " "); dkapp_stdout(app, error_messages[20]); fputc('\n', stdout); } /** The main() function of the stc program. @param argc Number of command line arguments. @param argv Command line arguments array. @return 0 on success, any other value indicates an error. */ #if DK_HAVE_PROTOTYPES int main(int argc, char *argv[]) #else int main(argc, argv) int argc; char *argv[]; #endif { int xargc, lfd, force_plain; char *thisarg, **xargv, **argptr; int helponly, versiononly, found, do_configure, do_unconfigure, do_showconf; char *infilename, *dirname; dk_fne_t *fne; int a_filename_was_found; $(trace-init stc.deb) app = dkapp_open_ext1(argc,argv,packagename,sysconfdir,0,0); do_configure = do_unconfigure = do_showconf = 0; if(app) { force_plain = 0; if(dkapp_get_pref(app, str_pref_plain, inbuffer, sizeof(inbuffer),0)) { if(dkstr_is_on(inbuffer)) { force_plain = 1; } else { force_plain = 0; } } xargc = dkapp_get_argc(app); xargv = dkapp_get_argv(app); dkapp_find_multi(app,error_finder,str_table_name_stc); helponly = versiononly = 0; infilename = dirname = NULL; argptr = xargv; lfd = 0; argptr++; lfd++; while(lfd < xargc) { thisarg = *argptr; if(*thisarg == '-') { thisarg++; switch(*thisarg) { case '-': { char *optarg; $? ". long option" thisarg++; optarg = dkstr_chr(thisarg, '='); if(optarg) { *(optarg++) = '\0'; } found = dkstr_array_abbr(long_options, thisarg, '$', 1); $? ". found = %d", found switch(found) { case 0: { /* plain */ if(optarg) force_plain = decide(optarg); else force_plain = 1; } break; case 1: { /* configure */ if(optarg) do_configure = decide(optarg); else do_configure = 1; } break; case 2: { /* unconfigure */ if(optarg) do_unconfigure = decide(optarg); else do_unconfigure = 1; if(do_unconfigure) do_configure = 1; } break; case 3: { /* reset */ if(optarg) { if(decide(optarg)) force_plain = 0; } else { force_plain = 0; } } break; case 4: { /* show-configura */ if(optarg) do_showconf = decide(optarg); else do_showconf = 1; } break; case 5: { /* version */ $? ". version" if(optarg) versiononly = decide(optarg); else versiononly = 1; } break; case 6: { /* help */ if(optarg) helponly = decide(optarg); else helponly = 1; } break; } } break; case 'c': { do_configure = decide(++thisarg); } break; case 'u': { do_unconfigure = decide(++thisarg); if(do_unconfigure) do_configure = 1; } break; case 'r': { force_plain = 0; } break; case 'C': { do_showconf = decide(++thisarg); } break; case 'h': { helponly = decide(++thisarg); } break; case 'v': { versiononly = decide(++thisarg); } break; case 'p': { force_plain = decide(++thisarg); } break; default: { } break; } } else { if(infilename) { if(!dirname) { dirname = thisarg; } else { helponly = 1; } } else { infilename = thisarg; } } lfd++; argptr++; } if(!(infilename && dirname)) { if(!(helponly || versiononly)) { helponly = 1; } } if(!(helponly || versiononly || do_configure || do_showconf)) { if(dksf_must_expand_filename(infilename)) { a_filename_was_found = 0; fne = dkfne_open(infilename,1,0); if(fne) { while(dkfne_next(fne)) { a_filename_was_found = 1; run(dkfne_get_fullname(fne), dirname, force_plain); } dkfne_close(fne); if(!a_filename_was_found) { dkapp_err_matchfile(app, infilename); } } else { run(infilename, dirname, force_plain); } } else { run(infilename, dirname, force_plain); } } else { if(do_configure || do_showconf) { if(do_unconfigure) { if(app) dkapp_unconfigure(app); } else { if(do_configure) { char *k; k = str_pref_off; if(force_plain) k = str_pref_on; if(app) dkapp_set_pref(app, str_pref_plain, k); } show_configuration(app, force_plain); } } else { print_version(); if(helponly) { print_help(); } } } dkapp_close(app); } else { exval = 1; } $? ". exit-code=%d", exval $(trace-end) exit(exval); return exval; }