static locale_module *get_locale_module(z_ucs *locale_name, z_ucs *module_name) { stringmap *module_map; locale_module *result = NULL; TRACE_LOG("Getting module '"); TRACE_LOG_Z_UCS(module_name); TRACE_LOG("' for locale '"); TRACE_LOG_Z_UCS(locale_name); TRACE_LOG("'.\n"); if ( (locale_modules != NULL) && ((module_map = (stringmap*)get_stringmap_value(locale_modules, locale_name)) != NULL) ) result = (locale_module*)get_stringmap_value(module_map, module_name); if (result == NULL) result = create_locale_module(locale_name, module_name); return result; }
static int get_stringmap_value_index(stringmap *map, z_ucs *name) { size_t i; TRACE_LOG("Looking for value with key '"); TRACE_LOG_Z_UCS(name); TRACE_LOG("'.\n"); if ( (map == NULL) || (name == NULL) ) return -1; for (i=0; i<map->nof_elements_stored; i++) { TRACE_LOG("Found '"); TRACE_LOG_Z_UCS(map->elements[i]->name); TRACE_LOG("'.\n"); if (z_ucs_cmp(map->elements[i]->name, name) == 0) { TRACE_LOG("Successfully found key at index %ld.\n", (long int)i); return i; } } return -1; }
int add_stringmap_element(stringmap *map, z_ucs *name, void *value) { stringmap_element *element_ptr; stringmap_element **element_ptr_ptr; if (get_stringmap_value_index(map, name) >= 0) return -1; if (map->nof_elements_stored == map->space_available) { if ((element_ptr_ptr = realloc( map->elements, sizeof(stringmap_element) * map->space_available + MAP_INC_SIZE)) == NULL) return -1; map->elements = element_ptr_ptr; map->space_available += MAP_INC_SIZE; } if ((element_ptr = malloc(sizeof(stringmap_element))) == NULL) return -1; if ((element_ptr->name = z_ucs_dup(name)) == NULL) { free(element_ptr); return -1; } element_ptr->value = value; map->elements[map->nof_elements_stored] = element_ptr; TRACE_LOG("Stored new value with key '"); TRACE_LOG_Z_UCS(element_ptr->name); TRACE_LOG("'.\n"); map->nof_elements_stored++; return 0; }
void freetype_wrap_z_ucs(true_type_wordwrapper *wrapper, z_ucs *input, bool end_line_after_end_of_input) { z_ucs *hyphenated_word, *input_index = input; z_ucs current_char, last_char, buf; long wrap_width_position, end_index, hyph_index; long buf_index, last_valid_hyph_index, output_metadata_index; long last_valid_hyph_position, hyph_position; int hyph_font_dash_bitmap_width, hyph_font_dash_advance; int metadata_index = 0, advance, bitmap_width; true_type_font *hyph_font; bool process_line_end = end_line_after_end_of_input; // In order to build an algorithm most suitable to both enabled and // disabled hyphenation, we'll collect input until we'll find the first // space or newline behind the right margin and then initiate a buffer // flush, which will then break according to the given situation. // Invoking a separate flush method makes sense since it also allows // a simple external triggering of the flush. // To minimize calculations, we keeping track of the last complete word's // rightmost char in last_word_end_index and the "word's last advance // position" in last_word_end_advance_position. // An "advance position" sums up all the "horiAdvance" distances for all // glyphs in the current word. // last_word_end_advance_position contains the advance position of // the last complete word/char which still fits into the current line. // This is updated every time we hit a space (so we can easily find the last // valid position to break). // We'll start by fetching the last char from the buffer (if possible) for // purposes of kerning. last_char = (wrapper->current_buffer_index > 0) ? wrapper->input_buffer[wrapper->current_buffer_index - 1] : 0; while ( ((input != NULL) && (*input_index != 0)) || (process_line_end == true) ) { if ((input != NULL) && (*input_index != 0)) { current_char = *input_index; ensure_additional_buffer_capacity(wrapper, 1); wrapper->input_buffer[wrapper->current_buffer_index] = current_char; TRACE_LOG("buffer-add: %c / %ld / %p / cap:%ld / lweap:%ld \n", (char)current_char, wrapper->current_buffer_index, wrapper->input_buffer + wrapper->current_buffer_index, wrapper->current_advance_position, wrapper->last_word_end_advance_position); wrapper->current_buffer_index++; tt_get_glyph_size(wrapper->current_font, current_char, &advance, &bitmap_width); wrapper->current_width_position = wrapper->current_advance_position + bitmap_width; //printf("current_width_position: %ld for '%c'\n", // wrapper->current_width_position, current_char); wrapper->current_advance_position += advance; /* printf("check:'%c',font:%p,advpos,ll,lweim,cwwp,bmw: %ld/%d/%ld/%ld/%d\n", current_char, wrapper->current_font, wrapper->current_advance_position, wrapper->line_length, wrapper->last_word_end_index, wrapper->current_width_position, bitmap_width); printf("bmw, lwp, ll: %d, %ld, %d.\n", bitmap_width, wrapper->last_width_position, wrapper->line_length); */ } else { current_char = 0; bitmap_width = 0; advance = 0; } if ( ( (bitmap_width == 0) && (wrapper->last_width_position >= wrapper->line_length) ) || (wrapper->current_width_position >= wrapper->line_length) ) { wrapper->last_width_position = wrapper->current_width_position; wrapper->last_chars_font = wrapper->current_font; /* printf("linebehind: %ld, %d.\n", wrapper->current_width_position, wrapper->line_length); */ TRACE_LOG("Behind past line, match on space|newline, breaking.\n"); // In case we're past the right margin, we'll take a look how // to best break the line. if ((current_char == Z_UCS_SPACE) || (current_char == Z_UCS_NEWLINE) || (((input == NULL) || (*input_index == 0)) && (process_line_end == true)) ) { // Here we've found a completed word past the lind end. At this // point we'll break the line without exception. /* printf("last_word_end_width_position: %ld, line_length: %d.\n", wrapper->last_word_end_width_position, wrapper->line_length); */ if (wrapper->enable_hyphenation == true) { end_index = wrapper->current_buffer_index - 2; TRACE_LOG("end_index: %d / %c, lwei: %d\n", end_index, wrapper->input_buffer[end_index], wrapper->last_word_end_index); while ( (end_index >= 0) && (end_index > wrapper->last_word_end_index) && ( (wrapper->input_buffer[end_index] == Z_UCS_COMMA) || (wrapper->input_buffer[end_index] == Z_UCS_DOT) ) ) { end_index--; } TRACE_LOG("end end_index: %d / %c, lwei: %d\n", end_index, wrapper->input_buffer[end_index], wrapper->last_word_end_index); if (end_index > wrapper->last_word_end_index) { end_index++; buf = wrapper->input_buffer[end_index]; wrapper->input_buffer[end_index] = 0; if ((hyphenated_word = hyphenate(wrapper->input_buffer + wrapper->last_word_end_index + 1)) == NULL) { TRACE_LOG("Error hyphenating.\n"); } else { TRACE_LOG("hyphenated word: \""); TRACE_LOG_Z_UCS(hyphenated_word); TRACE_LOG("\".\n"); wrap_width_position = wrapper->last_word_end_advance_position + wrapper->space_bitmap_width; hyph_index = wrapper->last_word_end_index + 1; buf_index = 0; last_valid_hyph_index = -1; // We'll now have to find the correct font for the start // of our hyphenated word. For that we'll remember the current // font at buffer start and iterate though all the metadata // until we're at the hyphenated word's beginning. hyph_font = wrapper->font_at_buffer_start; tt_get_glyph_size(hyph_font, Z_UCS_MINUS, &hyph_font_dash_bitmap_width, &hyph_font_dash_advance); metadata_index = 0; while ( (buf_index < z_ucs_len(hyphenated_word)) && (wrap_width_position + hyph_font_dash_bitmap_width <= wrapper->line_length) ) { /* printf("Checking buf char %ld / %c, hi:%ld\n", buf_index, hyphenated_word[buf_index], hyph_index); */ TRACE_LOG("Checking buf char %ld / %c, hi:%ld\n", buf_index, hyphenated_word[buf_index], hyph_index); while (metadata_index < wrapper->metadata_index) { //printf("metadata: %d of %d.\n", // metadata_index, wrapper->metadata_index); output_metadata_index = wrapper->metadata[metadata_index].output_index; //printf("output_metadata_index: %ld, hyph_index: %ld\n", // output_metadata_index, hyph_index); //printf("hyph_font: %p.\n", hyph_font); if (output_metadata_index <= hyph_index) { if (wrapper->metadata[metadata_index].font != NULL) { hyph_font = wrapper->metadata[metadata_index].font; tt_get_glyph_size(hyph_font, Z_UCS_MINUS, &hyph_font_dash_bitmap_width, &hyph_font_dash_advance); } metadata_index++; } else { break; } } if (hyphenated_word[buf_index] == Z_UCS_SOFT_HYPEN) { last_valid_hyph_index = hyph_index + 1; } else if (hyphenated_word[buf_index] == Z_UCS_MINUS) { last_valid_hyph_index = hyph_index; } if (hyphenated_word[buf_index] != Z_UCS_SOFT_HYPEN) { //printf("hyph_font: %p.\n", hyph_font); tt_get_glyph_size(hyph_font, hyphenated_word[buf_index], &advance, &bitmap_width); //wrap_width_position += bitmap_width; wrap_width_position += advance; hyph_index++; } if ( (hyphenated_word[buf_index] == Z_UCS_SOFT_HYPEN) || (hyphenated_word[buf_index] == Z_UCS_MINUS) ) { last_valid_hyph_position = wrap_width_position; } buf_index++; /* printf("(%ld + %d) = %ld <= %d\n", wrap_width_position, hyph_font_dash_bitmap_width, wrap_width_position + hyph_font_dash_bitmap_width, wrapper->line_length); */ } free(hyphenated_word); if (last_valid_hyph_index != -1) { /* printf("Found valid hyph pos at %ld / %c.\n", last_valid_hyph_index, wrapper->input_buffer[last_valid_hyph_index]); */ TRACE_LOG("Found valid hyph pos at %ld / %c.\n", last_valid_hyph_index, wrapper->input_buffer[last_valid_hyph_index]); hyph_index = last_valid_hyph_index; hyph_position = last_valid_hyph_position; } else { hyph_index = wrapper->last_word_end_index; hyph_position = wrapper->last_word_end_advance_position; //printf("no valid hyph, hyph_index: %ld.\n", hyph_index); } } wrapper->input_buffer[end_index] = buf; } // endif (end_index > wrapper->last_work_end_index) else { hyph_index = end_index; //printf("Hyph at %ld.\n", hyph_index); } } // endif (wrapper->enable_hyphentation == true) else { // Check for dashes inside the last word. // Example: "first-class car", where the word end we've now // found is between "first-class" and "car". wrap_width_position = wrapper->current_width_position; hyph_index = wrapper->current_buffer_index - 2; while ( (hyph_index >= 0) && (hyph_index > wrapper->last_word_end_index)) { /* printf("hyph_index: %ld / '%c'.\n", hyph_index, wrapper->input_buffer[hyph_index]); */ if ( (wrapper->input_buffer[hyph_index] == Z_UCS_MINUS) && (wrap_width_position <= wrapper->line_length) ) { // Found a dash to break on break; } tt_get_glyph_size(wrapper->current_font, wrapper->input_buffer[hyph_index], &advance, &bitmap_width); wrap_width_position -= bitmap_width; hyph_index--; } hyph_position = wrap_width_position; } //printf("breaking on char %ld / %c.\n", // hyph_index, wrapper->input_buffer[hyph_index]); TRACE_LOG("breaking on char %ld / %c.\n", hyph_index, wrapper->input_buffer[hyph_index]); if (wrapper->input_buffer[hyph_index] == Z_UCS_MINUS) { // We're wrappring on a in-word-dash. flush_line(wrapper, hyph_index, false, true); } else if (wrapper->input_buffer[hyph_index] == Z_UCS_SPACE) { flush_line(wrapper, hyph_index - 1, false, true); // In case we're wrapping between words without hyphenation or // in-word-dashes we'll have to get rid of the remaining leading // space-char in the input buffer. forget_first_char_in_buffer(wrapper); } else { if (wrapper->input_buffer[hyph_index - 2] == Z_UCS_MINUS) { flush_line(wrapper, hyph_index - 3, true, true); forget_first_char_in_buffer(wrapper); } else { flush_line(wrapper, hyph_index - 2, true, true); } } wrapper->current_advance_position -= hyph_position - wrapper->dash_bitmap_width; /* wrapper->last_width_position = wrapper->current_width_position - wrapper->last_word_end_advance_position; */ wrapper->current_width_position = wrapper->current_advance_position; wrapper->last_word_end_advance_position = wrapper->current_advance_position; wrapper->last_word_end_width_position = wrapper->current_width_position; wrapper->last_word_end_index = -1; } else if (wrapper->current_advance_position > wrapper->line_length * 2) { // In case we haven't found a word end we'll only force a line // break in case we've filled two full lines of text. /* printf("flush on: %c\n", wrapper->input_buffer[ wrapper->current_buffer_index - 2]); */ flush_line(wrapper, wrapper->current_buffer_index - 2, false, true); wrapper->current_advance_position = advance; wrapper->current_width_position = wrapper->current_advance_position + bitmap_width; wrapper->last_word_end_index = -1; wrapper->last_word_end_advance_position = 0; wrapper->last_word_end_width_position = 0; //printf("first buf: %c\n", wrapper->input_buffer[0]); /* printf("current_buffer_index: %ld\n", wrapper->current_buffer_index); printf("current_advance_position: %ld\n", wrapper->current_advance_position); printf("break at %ld, 1\n", wrapper->last_char_in_line_index); old_index = wrapper->current_buffer_index; flush_line(wrapper, wrapper->last_char_in_line_index, false, true); chars_sent_count = old_index - wrapper->current_buffer_index; printf("chars_sent_count: %ld\n", chars_sent_count); printf("current_buffer_index: %ld\n", wrapper->current_buffer_index); tt_get_glyph_size(wrapper->last_chars_in_line_font, wrapper->input_buffer[wrapper->current_buffer_index], &advance, &bitmap_width); wrapper->current_advance_position -= wrapper->last_chars_in_line_advance_position; wrapper->last_width_position = wrapper->current_advance_position + bitmap_width; wrapper->last_char_in_line_index = 0; wrapper->last_word_end_advance_position -= wrapper->last_chars_in_line_advance_position; wrapper->last_word_end_width_position -= wrapper->last_chars_in_line_advance_position; wrapper->last_word_end_index -= chars_sent_count; printf("current_advance_position: %ld\n", wrapper->current_advance_position); */ } else { // Otherwise, we'll do nothing and keep collecting chars until // we later find a word and or exceed the double line length. } } else { wrapper->last_width_position = wrapper->current_width_position; } if (current_char == Z_UCS_NEWLINE) { TRACE_LOG("Flushing on newline at current position.\n"); // In case everything fits into the current line and the current // char is a newline, we can simply flush at the current position. flush_line(wrapper, wrapper->current_buffer_index - 1, false, false); wrapper->last_word_end_advance_position = 0; wrapper->last_word_end_width_position = 0; wrapper->current_advance_position = 0; wrapper->current_width_position = 0; wrapper->last_width_position = 0; wrapper->last_word_end_index = -1; } if ( (current_char == Z_UCS_SPACE) && (last_char != Z_UCS_SPACE) ) { TRACE_LOG("lweap-at-space: %ld\n", wrapper->last_word_end_advance_position); wrapper->last_word_end_advance_position = wrapper->current_advance_position; wrapper->last_word_end_width_position = wrapper->current_width_position; wrapper->last_word_end_index = wrapper->current_buffer_index - 1; } if ( (input != NULL) && (*input_index != 0) ) { input_index++; last_char = current_char; } else if (process_line_end == true) { process_line_end = false; } } }
void flush_line(true_type_wordwrapper *wrapper, long flush_index, bool append_minus, bool append_newline) { z_ucs buf_1; //, buf_2; size_t chars_sent; int chars_to_move; int metadata_index = 0; long output_metadata_index, output_index; struct freetype_wordwrap_metadata *metadata_entry; int i; if (flush_index == -1) { flush_index = wrapper->current_buffer_index - 1; } else { TRACE_LOG("flush on: %c %d \n", (char)wrapper->input_buffer[flush_index], wrapper->input_buffer[flush_index]); } // Ensure there's enough space to place a terminating 0. ensure_additional_buffer_capacity(wrapper, 1); output_index = 0; while (metadata_index < wrapper->metadata_index) { TRACE_LOG("Found some metadata, starting at metadata index %d of %d.\n", metadata_index, wrapper->metadata_index); // Look which buffer position is affected by the next metadata entry. output_metadata_index = wrapper->metadata[metadata_index].output_index; TRACE_LOG("mdoutput: mdindex: %d, flushindex: %d, output_index:%d\n", output_metadata_index, flush_index, output_index); // In case we're not flushing everything and the next buffer position is // behind the current flush position, there's nothing more to do. if ( (flush_index >= 0) && (output_metadata_index > flush_index) ) { break; } // In case current output position is before the metadata entry's // output position, we'll flush everything until this position. if (output_index < output_metadata_index) { TRACE_LOG("Flusing up to next metadata entry at %ld\n", output_metadata_index); buf_1 = wrapper->input_buffer[output_metadata_index]; wrapper->input_buffer[output_metadata_index] = 0; wrapper->wrapped_text_output_destination( wrapper->input_buffer + output_index, wrapper->destination_parameter); wrapper->input_buffer[output_metadata_index] = buf_1; output_index = output_metadata_index; } TRACE_LOG("metadata_index: %d,output_index: %d,output_metadata_index:%d\n", metadata_index, output_index, output_metadata_index); TRACE_LOG("wrapper->metadata[%d].output_index: %d\n", metadata_index, wrapper->metadata[metadata_index].output_index); TRACE_LOG("wrapper->metadata_index: %d\n", wrapper->metadata_index); // We can now flush all the metadata entries at the current position. TRACE_LOG("%d / %d\n", wrapper->metadata[metadata_index].output_index + 1, output_metadata_index + 1); while ( (metadata_index < wrapper->metadata_index) && (wrapper->metadata[metadata_index].output_index == output_metadata_index) ) { metadata_entry = &wrapper->metadata[metadata_index]; TRACE_LOG("Output metadata prm %d at buffer position %ld.\n", metadata_entry->int_parameter, output_metadata_index); metadata_entry->metadata_output_function( metadata_entry->ptr_parameter, metadata_entry->int_parameter); if (metadata_entry->font != NULL) { wrapper->font_at_buffer_start = metadata_entry->font; } metadata_index++; } } if (metadata_index > 0) { TRACE_LOG("Removing %d metadata entries.\n", metadata_index); memmove( wrapper->metadata, &wrapper->metadata[metadata_index], (wrapper->metadata_index - metadata_index) * sizeof( struct freetype_wordwrap_metadata)); wrapper->metadata_index -= metadata_index; } /* if (append_newline == true) { buf_1 = wrapper->input_buffer[flush_index + 1]; buf_2 = wrapper->input_buffer[flush_index + 2]; wrapper->input_buffer[flush_index + 1] = Z_UCS_NEWLINE; wrapper->input_buffer[flush_index + 2] = 0; TRACE_LOG("flush-index: %d\n", flush_index); TRACE_LOG("flush-line (%d / %p): \"", z_ucs_len(wrapper->input_buffer), wrapper->input_buffer); TRACE_LOG_Z_UCS(wrapper->input_buffer); TRACE_LOG("\"\n"); wrapper->wrapped_text_output_destination( wrapper->input_buffer + output_index, wrapper->destination_parameter); wrapper->input_buffer[flush_index + 1] = buf_1; wrapper->input_buffer[flush_index + 2] = buf_2; } else { buf_1 = wrapper->input_buffer[flush_index + 1]; wrapper->input_buffer[flush_index + 1] = 0; TRACE_LOG("flush-index: %d\n", flush_index); TRACE_LOG("flush-line (%d / %p): \"", z_ucs_len(wrapper->input_buffer), wrapper->input_buffer); TRACE_LOG_Z_UCS(wrapper->input_buffer); TRACE_LOG("\"\n"); wrapper->wrapped_text_output_destination( wrapper->input_buffer + output_index, wrapper->destination_parameter); wrapper->input_buffer[flush_index + 1] = buf_1; } */ buf_1 = wrapper->input_buffer[flush_index + 1]; wrapper->input_buffer[flush_index + 1] = 0; TRACE_LOG("flush-index: %d\n", flush_index); TRACE_LOG("flush-line (%d / %p): \"", z_ucs_len(wrapper->input_buffer), wrapper->input_buffer); TRACE_LOG_Z_UCS(wrapper->input_buffer); TRACE_LOG("\"\n"); wrapper->wrapped_text_output_destination( wrapper->input_buffer + output_index, wrapper->destination_parameter); wrapper->input_buffer[flush_index + 1] = buf_1; if (append_minus == true) { wrapper->wrapped_text_output_destination( minus_string, wrapper->destination_parameter); } if (append_newline == true) { wrapper->wrapped_text_output_destination( newline_string, wrapper->destination_parameter); } chars_sent = flush_index + 1; TRACE_LOG("chars_sent: %ld, dst: %p, src: %p, bufindex: %ld\n", chars_sent, wrapper->input_buffer, wrapper->input_buffer + flush_index, wrapper->current_buffer_index); if ((chars_to_move = wrapper->current_buffer_index - chars_sent) > 0) { memmove( wrapper->input_buffer, wrapper->input_buffer + flush_index + 1, chars_to_move * sizeof(z_ucs)); } for (i=0; i<wrapper->metadata_index; i++) wrapper->metadata[i].output_index -= chars_sent; wrapper->current_buffer_index -= chars_sent; }
static void flush_input_buffer(WORDWRAP *wrapper, bool force_flush) { z_ucs *index = NULL, *ptr, *hyphenated_word, *last_hyphen, *word_start; z_ucs *word_end, *input = wrapper->input_buffer, *first_space_or_newline; z_ucs buf=0, buf2; // buf initialized to avoid compiler warning z_ucs buf3 = '-'; // buf3 initialized to avoid compiler warning long len, chars_sent = 0; int metadata_offset = 0, i, chars_left_on_line; struct wordwrap_metadata *metadata_entry; int current_line_length = wrapper->line_length - wrapper->chars_already_on_line; input[wrapper->input_index] = 0; TRACE_LOG("input-index: %ld\n", wrapper->input_index); TRACE_LOG("metadata stored: %d.\n", wrapper->metadata_index); for (;;) { TRACE_LOG("Processing flush for line-length %d, already in line: %d.\n", current_line_length, wrapper->chars_already_on_line); if (*input != 0) { TRACE_LOG("flush wordwrap-buffer at %p: \"", input); TRACE_LOG_Z_UCS(input); TRACE_LOG("\".\n"); } if ((index = z_ucs_chr(input, Z_UCS_NEWLINE)) != NULL) { len = index - input; } else { len = z_ucs_len(input); TRACE_LOG("len:%ld, force:%d.\n", len, force_flush); if (len == 0) { if (force_flush == true) { // Force flush metadata behind end of output. while (metadata_offset < wrapper->metadata_index) { TRACE_LOG("flush post-output metadata at: %ld.\n", wrapper->metadata[metadata_offset].output_index); metadata_entry = &wrapper->metadata[metadata_offset]; TRACE_LOG("Output metadata prm %d.\n", metadata_entry->int_parameter); metadata_entry->metadata_output_function( metadata_entry->ptr_parameter, metadata_entry->int_parameter); metadata_offset++; } } wrapper->chars_already_on_line = 0; break; } if (len <= current_line_length) { if (force_flush == false) { // We're quitting on len == current_line_length since we can only // determine wether we can break cleanly is if a space follows // immediately after the last char. wrapper->chars_already_on_line = 0; break; } wrapper->chars_already_on_line = len; } // FIXME: Add break in case hyph is enabled and a word longer than // the line is not terminated with a space. } TRACE_LOG("wordwrap-flush-len: %ld.\n", len); if (len <= current_line_length) { // Line fits on screen. TRACE_LOG("Line fits on screen.\n"); if (index != NULL) { index++; len++; buf = *index; *index = 0; } chars_sent += len; output_buffer(wrapper, input, &metadata_offset); if (wrapper->left_side_padding != 0) wrapper->wrapped_text_output_destination( wrapper->padding_buffer, wrapper->destination_parameter); if (index != NULL) *index = buf; else { //wrapper->input_index = 0; break; } } else if (wrapper->enable_hyphenation == true) { // Line does not fit on screen and hyphenation is enabled, so we'll // try to hyphenate. // In this section we'll set "index" to the point where the line // should be split and "last_hyphen" to the word position where // hyphenation should take place -- if possible, otherwise NULL. // In case hyphenation is active we're looking at the word overruning // the line end. It has to be completed in order to make hyphenation // work. TRACE_LOG("to wrap/hyphenate (force:%d) to length %d : \"", force_flush, current_line_length); TRACE_LOG_Z_UCS(input); TRACE_LOG("\".\n"); // Get the char at the current line end. if (input[current_line_length] == Z_UCS_SPACE) { // Fine, we can wrap right here at this space. index = input + current_line_length; last_hyphen = NULL; } else { if ( ((first_space_or_newline = z_ucs_chrs( input + current_line_length, word_split_chars)) == NULL) && (force_flush == false) ) { // In case we can't find any space the word may not have been // completely written to the buffer. Wait until we've got more // input. TRACE_LOG("No word end found.\n"); break; } else { if (first_space_or_newline == NULL) { word_end = input + current_line_length; while (*(word_end + 1) != 0) { word_end++; } } else { // We've found a space behind the overrunning word so we're // able to correctly split the current line. word_end = first_space_or_newline - 1; } // Before hyphentation, check for dashes inside the last word. // Example: "first-class car", where the word end we've now // found is between "first-class" and "car". word_start = word_end - 1; while (word_start > input) { TRACE_LOG("examining word end: \"%c\".\n", *word_start); if (*word_start == Z_UCS_MINUS) { if (input + current_line_length > word_start) { // Found a dash to break on word_start++; break; } } else if (*word_start == Z_UCS_SPACE) { // We just passed the word-start. word_start++; break; } word_start--; } // FIXME: Do we need a space left from here? TRACE_LOG("word-start: %c\n", *word_start); TRACE_LOG("word-end: %c\n", *word_end); last_hyphen = NULL; if (word_end >= input + current_line_length) { // We only have to hyphenate in case the line is still too long. buf = *(word_end+ 1); *(word_end+ 1) = 0; TRACE_LOG("buffer terminated at word end: \""); TRACE_LOG_Z_UCS(input); TRACE_LOG("\".\n"); index = word_start; if ((hyphenated_word = hyphenate(index)) == NULL) { TRACE_LOG("Error hyphenating.\n"); i18n_translate_and_exit( libfizmo_module_name, i18n_libfizmo_UNKNOWN_ERROR_CASE, -1); } TRACE_LOG("hyphenated word: \""); TRACE_LOG_Z_UCS(hyphenated_word); TRACE_LOG("\".\n"); *(word_end + 1) = buf; chars_left_on_line = current_line_length - (index - input); TRACE_LOG("chars left on line: %d\n", chars_left_on_line); ptr = hyphenated_word; while ((chars_left_on_line > 0) && (*ptr != 0)) { TRACE_LOG("Testing %c for soft-hyphen.\n", *ptr); if (*ptr == Z_UCS_SOFT_HYPEN) { last_hyphen = input + (current_line_length - chars_left_on_line); } else { chars_left_on_line--; } ptr++; } free(hyphenated_word); if (last_hyphen != NULL) { TRACE_LOG("Last hyphen at %ld.\n", last_hyphen - input); buf3 = *last_hyphen; *last_hyphen = '-'; index = last_hyphen + 1; } else { // We couldn't find a possibility to hyphenate the last // word in the line. TRACE_LOG("No hyphen found.\n"); if (index > input) { if (*(index-1) != Z_UCS_MINUS) { // In case the char before the last word is not a dash, // we'll skip the space before this word by moving back // the index by one. index--; } } else { // In case the current word is so long that it doesn't fit // on the line -- this may be the case if we're supposed // to display ASCII art and the linesize is to short -- we // have to advance the index to the line end. TRACE_LOG("This is the first word in the line, hard break.\n"); index = input + current_line_length; } } } else { index = word_end; last_hyphen = NULL; } } } // Output everything before *index and a newline after. TRACE_LOG("Input (%p, %p): \"", input, index); TRACE_LOG_Z_UCS(input); TRACE_LOG("\".\n"); buf2 = *index; *index = Z_UCS_NEWLINE; buf = *(index + 1); *(index + 1) = 0; output_buffer(wrapper, input, &metadata_offset); if (wrapper->left_side_padding != 0) wrapper->wrapped_text_output_destination( wrapper->padding_buffer, wrapper->destination_parameter); *(index + 1) = buf; *index = buf2; if (last_hyphen != NULL) { *last_hyphen = buf3; index--; } // if (*index == Z_UCS_SPACE) { while (*index == Z_UCS_SPACE) { index++; } len = index - input; chars_sent += len; TRACE_LOG("Processed %ld chars in hyphenated wordwrap.\n", len); } else { // Line won't fit completely and hyphenation is disabled. // Find the end of the last word or dash in it before the end of line // (opposed to looking at the word overring the line end in case of // hyphentation). TRACE_LOG("linelength: %d.\n", current_line_length); ptr = input + current_line_length - 1; while (ptr > input) { if (*ptr == Z_UCS_SPACE) { index = ptr; break; } else if (*ptr == Z_UCS_MINUS) { index = ptr + 1; break; } ptr--; } if (ptr == input) { // We couldn't find any space or dash in the whole line, so we're // forced to flush everything. index = input + current_line_length; } buf = *index; *index = Z_UCS_NEWLINE; buf2 = *(index+1); *(index+1) = 0; TRACE_LOG("Output from %p.\n", input); output_buffer(wrapper, input, &metadata_offset); if (wrapper->left_side_padding != 0) wrapper->wrapped_text_output_destination( wrapper->padding_buffer, wrapper->destination_parameter); *(index+1) = buf2; *index = buf; //if (*index == Z_UCS_SPACE) { while (*index == Z_UCS_SPACE) { index++; } len = index - input; chars_sent += len; } TRACE_LOG("len-after: %ld.\n", len); if (index != NULL) { TRACE_LOG("index: \""); TRACE_LOG_Z_UCS(index); TRACE_LOG("\".\n"); } input += len; current_line_length = wrapper->line_length; } TRACE_LOG("chars sent: %ld, moving: %ld.\n", chars_sent, wrapper->input_index - chars_sent + 1); TRACE_LOG("chars_already_on_line: %d\n", wrapper->chars_already_on_line); index = z_ucs_rchr(wrapper->input_buffer, Z_UCS_NEWLINE); memmove( wrapper->input_buffer, input, sizeof(z_ucs) * (wrapper->input_index - chars_sent + 1)); wrapper->input_index -= chars_sent; if (metadata_offset > 0) { memmove( wrapper->metadata, wrapper->metadata + metadata_offset, sizeof(struct wordwrap_metadata) * (wrapper->metadata_index - metadata_offset)); wrapper->metadata_index -= metadata_offset; TRACE_LOG("metadata stored: %d.\n", wrapper->metadata_index); } for (i=0; i<wrapper->metadata_index; i++) wrapper->metadata[i].output_index -= chars_sent; }
static void output_buffer(WORDWRAP *wrapper, z_ucs *buffer_start, int *metadata_offset) { long index, end_index, next_index; struct wordwrap_metadata *metadata_entry; z_ucs buf; TRACE_LOG("Output buffer: \""); TRACE_LOG_Z_UCS(buffer_start); TRACE_LOG("\""); if (*metadata_offset == wrapper->metadata_index) { wrapper->wrapped_text_output_destination( buffer_start, wrapper->destination_parameter); } else { index = buffer_start - wrapper->input_buffer; end_index = index + z_ucs_len(buffer_start); while (index < end_index) { TRACE_LOG("index: %ld, end_index: %ld.\n", index, end_index); TRACE_LOG("metadata-offset: %d.\n", *metadata_offset); if (*metadata_offset < wrapper->metadata_index) { TRACE_LOG("next metadata-index: %ld.\n", wrapper->metadata[*metadata_offset].output_index); while ( (*metadata_offset < wrapper->metadata_index) && (wrapper->metadata[*metadata_offset].output_index == index) ) { metadata_entry = &wrapper->metadata[*metadata_offset]; TRACE_LOG("Output metadata prm %d at %ld.\n", metadata_entry->int_parameter, index); metadata_entry->metadata_output_function( metadata_entry->ptr_parameter, metadata_entry->int_parameter); (*metadata_offset)++; } } next_index = ( (*metadata_offset < wrapper->metadata_index) && (wrapper->metadata[*metadata_offset].output_index < end_index) ) ? wrapper->metadata[*metadata_offset].output_index : end_index; TRACE_LOG("next-index: %ld.\n", next_index); buf = wrapper->input_buffer[next_index]; wrapper->input_buffer[next_index] = 0; wrapper->wrapped_text_output_destination( wrapper->input_buffer + index, wrapper->destination_parameter); wrapper->input_buffer[next_index] = buf; index = next_index; } } }
static void flush_input_buffer(WORDWRAP *wrapper, bool force_flush) { z_ucs *index = NULL, *ptr, *hyphenated_word, *last_hyphen, *word_start; z_ucs *word_end_without_split_chars, *word_end_with_split_chars; z_ucs buf=0, buf2; // buf initialized to avoid compiler warning z_ucs buf3 = '-'; // buf3 initialized to avoid compiler warning long len, chars_sent = 0; z_ucs *input = wrapper->input_buffer; bool minus_found; int metadata_offset = 0; int i, chars_left_on_line; //int hyphen_offset; struct wordwrap_metadata *metadata_entry; input[wrapper->input_index] = 0; TRACE_LOG("input-index: %ld\n", wrapper->input_index); TRACE_LOG("metadata stored: %d.\n", wrapper->metadata_index); for (;;) { TRACE_LOG("Processing flush.\n"); if (*input != 0) { TRACE_LOG("flush wordwrap-buffer: \""); TRACE_LOG_Z_UCS(input); TRACE_LOG("\".\n"); } if ((index = z_ucs_chr(input, Z_UCS_NEWLINE)) != NULL) len = index - input; else { len = z_ucs_len(input); TRACE_LOG("len:%ld, force:%d.\n", len, force_flush); if (len == 0) { if (force_flush == true) { // Force flush metadata behind end of output. while (metadata_offset < wrapper->metadata_index) { TRACE_LOG("flush post-output metadata at: %ld.\n", wrapper->metadata[metadata_offset].output_index); metadata_entry = &wrapper->metadata[metadata_offset]; TRACE_LOG("Output metadata prm %d.\n", metadata_entry->int_parameter); metadata_entry->metadata_output_function( metadata_entry->ptr_parameter, metadata_entry->int_parameter); metadata_offset++; } } break; } if ( (len <= wrapper->line_length) && (force_flush == false) ) // We're quitting on len == wrapper->line_length since we can only // determine wether we can break cleanly is if a space follows // immediately after the last char. break; // FIXME: Add break in case hyph is enabled and a word longer than // the line is not terminated with a space. } TRACE_LOG("wordwrap-flush-len: %ld.\n", len); if (len <= wrapper->line_length) { // Line fits on screen. TRACE_LOG("Line fits on screen.\n"); if (index != NULL) { index++; len++; buf = *index; *index = 0; } chars_sent += len; output_buffer(wrapper, input, &metadata_offset); if (wrapper->left_side_padding != 0) wrapper->wrapped_text_output_destination( wrapper->padding_buffer, wrapper->destination_parameter); if (index != NULL) *index = buf; else { //wrapper->input_index = 0; break; } } else if (wrapper->enable_hyphenation == true) { TRACE_LOG("to wrap/hyphenate (force:%d): \"", force_flush); TRACE_LOG_Z_UCS(input); TRACE_LOG("\".\n"); if ((word_end_without_split_chars = z_ucs_chrs( input + wrapper->line_length, word_split_chars)) == NULL) { if (force_flush == true) word_end_without_split_chars = input + len; else { TRACE_LOG("No word end found.\n"); break; } } // We'll remeber the current position of the word-split-char we've // just found in "word_end_without_split_chars" and advance this pointer // until we won't find any more word-split-chars. Thus, if we've just // found the start dot of an ellipsis "..." we'll end up with the // position of the last dot. word_end_with_split_chars = word_end_without_split_chars; while ((ptr = z_ucs_chr( word_split_chars, *word_end_with_split_chars)) != NULL) word_end_with_split_chars++; // Now we've stored the end of the word in "word_end_without_split_chars". // We've now found a word boundary. This char may however not be part // of the actual last word (we might have hit a minus representing // a n- or m-dash--like this--or an elipsis like "..."). So we'll // rewind further if necessary. TRACE_LOG("examining split end:\"%c\".\n", *word_end_without_split_chars); //while (word_end_without_split_chars - 1 > input + wrapper->line_length) while (word_end_without_split_chars > input) { TRACE_LOG("Checking for split-char: %c/%d.\n", *word_end_without_split_chars, *word_end_without_split_chars); if (z_ucs_chr( word_split_chars, *(word_end_without_split_chars - 1)) == NULL) break; word_end_without_split_chars--; } if ( (word_end_without_split_chars != word_end_with_split_chars) && (*word_end_without_split_chars == Z_UCS_SPACE) && (word_end_without_split_chars < input + wrapper->line_length) ) { // In this case we have skipped a number of stand-alone word-split- // chars (like the elipsis in "Powers -- these") and are now so far // back that we can split right at the space before these chars. index = word_end_without_split_chars; last_hyphen = NULL; } else { word_start = NULL; // In case we've now found a word end, check for dashes before it. // Example: "first-class car", where the word end we've now found is // between "first-class" and "car". word_start = word_end_without_split_chars - 1; while (word_start > input) { TRACE_LOG("examining word end: \"%c\".\n", *word_start); if (*word_start == Z_UCS_MINUS) { if (input + wrapper->line_length > word_start) { // Found a dash to break on word_start++; break; } } else if ((ptr = z_ucs_chr(word_split_chars, *word_start)) != NULL) { // In case we've found a non-dash separator, we've found the // start of the word. word_start++; break; } word_start--; } // FIXME: Do we need a space left from here? TRACE_LOG("word-start: %c\n", *word_start); TRACE_LOG("%p / %p\n", word_end_without_split_chars, word_end_with_split_chars); TRACE_LOG("%c / %c\n", *word_end_without_split_chars, *word_end_with_split_chars); TRACE_LOG("%p / %p\n", word_end_without_split_chars, input + wrapper->line_length); last_hyphen = NULL; if (word_end_with_split_chars > input + wrapper->line_length) { // We only have to hyphenate in case the line is still too long. buf = *word_end_without_split_chars; *word_end_without_split_chars = 0; TRACE_LOG("buffer terminated at word end: \""); TRACE_LOG_Z_UCS(input); TRACE_LOG("\".\n"); index = word_start; if ((hyphenated_word = hyphenate(index)) == NULL) { TRACE_LOG("Error hyphenating.\n"); i18n_translate_and_exit( libfizmo_module_name, i18n_libfizmo_UNKNOWN_ERROR_CASE, -1); } *word_end_without_split_chars = buf; chars_left_on_line = wrapper->line_length - (index - input); TRACE_LOG("chars left on line: %d\n", chars_left_on_line); //hyphen_offset = index - input; ptr = hyphenated_word; while (chars_left_on_line > 0) { TRACE_LOG("Testing %c for hyphen.\n", *ptr); if (*ptr == Z_UCS_SOFT_HYPEN) last_hyphen = input + (wrapper->line_length - chars_left_on_line); else chars_left_on_line--; ptr++; } free(hyphenated_word); if (last_hyphen != NULL) { TRACE_LOG("Last hyphen at %ld.\n", last_hyphen - input); buf3 = *last_hyphen; *last_hyphen = '-'; index = last_hyphen + 1; } else if ( (word_start != input) && (*(word_start-1) != Z_UCS_MINUS) ) { TRACE_LOG("No hyphen found.\n"); index--; } } else { index = word_end_with_split_chars; } } // Output everything before *index and a newline after. buf2 = *index; *index = Z_UCS_NEWLINE; buf = *(index + 1); *(index + 1) = 0; output_buffer(wrapper, input, &metadata_offset); if (wrapper->left_side_padding != 0) wrapper->wrapped_text_output_destination( wrapper->padding_buffer, wrapper->destination_parameter); *(index + 1) = buf; *index = buf2; if (last_hyphen != NULL) { *last_hyphen = buf3; index--; } if (*index == Z_UCS_SPACE) index++; len = index - input; chars_sent += len; TRACE_LOG("Processed %ld chars in hyphenated wordwrap.\n", len); } else { // Line won't fit completely. Find the end of the last word. TRACE_LOG("linelength: %d.\n", wrapper->line_length); //word_end_with_split_chars = NULL; ptr = input + wrapper->line_length; if (*ptr == Z_UCS_SPACE) ptr++; TRACE_LOG("%p/%p/%c\n", input, ptr, *ptr); buf = *ptr; *ptr = 0; TRACE_LOG("examine buffer: \""); TRACE_LOG_Z_UCS(input); TRACE_LOG("\".\n"); index = z_ucs_rchrs(input, word_split_chars); if (*index == Z_UCS_MINUS) { minus_found = true; index++; } else minus_found = false; TRACE_LOG("Found word-split at %p from %p.\n", index, input); *ptr = buf; if (index == NULL) index = input + wrapper->line_length; buf = *index; *index = Z_UCS_NEWLINE; buf2 = *(index+1); *(index+1) = 0; TRACE_LOG("Output from %p.\n", input); output_buffer(wrapper, input, &metadata_offset); if (wrapper->left_side_padding != 0) wrapper->wrapped_text_output_destination( wrapper->padding_buffer, wrapper->destination_parameter); *(index+1) = buf2; *index = buf; if (minus_found == false) index++; len = index - input; chars_sent += len; } TRACE_LOG("len-after: %ld.\n", len); if (index != NULL) { TRACE_LOG("index: \""); TRACE_LOG_Z_UCS(index); TRACE_LOG("\".\n"); } input += len; } TRACE_LOG("chars sent: %ld, moving: %ld.\n", chars_sent, wrapper->input_index - chars_sent + 1); memmove( wrapper->input_buffer, input, sizeof(z_ucs) * (wrapper->input_index - chars_sent + 1)); wrapper->input_index -= chars_sent; if (metadata_offset > 0) { memmove( wrapper->metadata, wrapper->metadata + metadata_offset, sizeof(struct wordwrap_metadata) * (wrapper->metadata_index - metadata_offset)); wrapper->metadata_index -= metadata_offset; TRACE_LOG("metadata stored: %d.\n", wrapper->metadata_index); } for (i=0; i<wrapper->metadata_index; i++) wrapper->metadata[i].output_index -= chars_sent; }
// "string_code" is one of the codes defined in "utf8.h". // "ap" is the va_list initialized in the various i18n-methods. // "output_mode" is either "i18n_OUTPUT_MODE_DEV_NULL" for no output // at all (useful for string length measuring), "i18n_OUTPUT_MODE_STREAMS" // for sending output to "streams_utf8_output" and "i18n_OUTPUT_MODE_STRING" // to write the output to a string. static long i18n_translate_from_va_list(z_ucs *module_name, int string_code, va_list ap, int output_mode, z_ucs *string_target) { z_ucs *locale_name; locale_module *module; z_ucs *index; char parameter_types[11]; // May each contain 's', 'z' or 'd'. Using 11 // instead of ten so that a null byte may be // placed after a char to print the error // message as string. char *string_parameters[10]; // pointers to the parameters. z_ucs *z_ucs_string_parameters[10]; char formatted_parameters[10][MAXIMUM_FORMATTED_PARAMTER_LENGTH + 1]; z_ucs *start; uint8_t match_stage; int i,k; int n; z_ucs buf; size_t length; z_ucs *start_index; char index_char; z_ucs z_ucs_buffer[LATIN1_TO_Z_UCS_BUFFER_SIZE]; char *ptr; locale_name = current_locale_name != NULL ? current_locale_name : default_locale_name; TRACE_LOG("Trying to get module '"); TRACE_LOG_Z_UCS(module_name); TRACE_LOG("' for locale '"); TRACE_LOG_Z_UCS(locale_name); TRACE_LOG("'.\n"); if ((module = get_locale_module(locale_name, module_name)) == NULL) { TRACE_LOG("Module not found.\n"); i18n_exit(-1, NULL); } TRACE_LOG("Got module at %p with %d messages at %p.\n", module, module->nof_messages, module->messages); if (string_code >= module->nof_messages) { TRACE_LOG("String %d code too large, exiting.\n", string_code); i18n_exit(-1, NULL); } index = module->messages[string_code]; if (index == NULL) return -1; TRACE_LOG("Translating string code %d at %p.\n", string_code, index); n = 0; while ((index = z_ucs_chr(index, (z_ucs)'\\')) != NULL) { index++; start_index = index; index_char = zucs_char_to_latin1_char(*index); if (index_char == '{') { TRACE_LOG("'{' found.\n"); index++; index_char = zucs_char_to_latin1_char(*index); } else { index = start_index; continue; } if ((index_char >= '0') && (index_char <= '9')) { TRACE_LOG("'[0-9]' found.\n"); index++; index_char = zucs_char_to_latin1_char(*index); } else { index = start_index; continue; } if ( (index_char == 's') || (index_char == 'z') || (index_char == 'd') || (index_char == 'x') ) { TRACE_LOG("'[szdx]' found.\n"); parameter_types[n] = index_char; index++; index_char = zucs_char_to_latin1_char(*index); } else { index = start_index; continue; } if (index_char == '}') { TRACE_LOG("'}' found.\n"); index++; index_char = zucs_char_to_latin1_char(*index); n++; } else { index = start_index; continue; } } TRACE_LOG("Found %d parameter codes.\n", n); if (n == 0) { TRACE_LOG("No parameter.\n"); // In case we don't have a single parameter, we can just print // everything right away and quit. if (i18n_send_output( module->messages[string_code], output_mode, (string_target != NULL ? &string_target : NULL)) != 0) return -1; else return z_ucs_len(module->messages[string_code]); } length = 0; for (i=0; i<n; i++) { // parameter_types[0-n] are always defined, thus using "usedef" is okay. if (parameter_types[i] == 's') { string_parameters[i] = va_arg(ap, char*); TRACE_LOG("p#%d: %s\n", i, string_parameters[i]); length += strlen(string_parameters[i]); } else if (parameter_types[i] == 'z')
static locale_module *create_locale_module(z_ucs *locale_name, z_ucs *module_name) { z_dir *dir; locale_module *result = NULL; char *ptr; struct z_dir_ent z_dir_entry; char *module_name_utf8, *locale_dir_name = NULL; stringmap *module_map; TRACE_LOG("Creating module '"); TRACE_LOG_Z_UCS(module_name); TRACE_LOG("' for locale '"); TRACE_LOG_Z_UCS(locale_name); TRACE_LOG("'.\n"); if ((locale_dir_name = get_path_for_locale(locale_name)) == NULL) { // No directory containing an entry named equal to local name was foudn. TRACE_LOG("locale_dir_name is NULL.\n"); return NULL; } // open-resource: module_name_utf8 = dup_zucs_string_to_utf8_string(module_name); if ((ptr = realloc(module_name_utf8, strlen(module_name_utf8) + 9)) == NULL) { TRACE_LOG("realloc() returned NULL.\n"); free(module_name_utf8); free(locale_dir_name); return NULL; } module_name_utf8 = ptr; strcat(module_name_utf8, "_i18n.txt"); // open-resource: if ((dir = fsi->open_dir(locale_dir_name)) != NULL) { TRACE_LOG("Trying file \"%s\".\n", module_name_utf8); while (fsi->read_dir(&z_dir_entry, dir) == 0) { if (strcasecmp(module_name_utf8, z_dir_entry.d_name) == 0) { TRACE_LOG("File matches \"%s\".\n", module_name_utf8); if ((result = parse_locale_file( module_name, locale_dir_name, module_name_utf8)) == NULL) { TRACE_LOG("parse_locale_file() returned NULL.\n"); fsi->close_dir(dir); free(module_name_utf8); free(locale_dir_name); return NULL; } } } // close-resource: fsi->close_dir(dir); } // close-resource: free(module_name_utf8); // close-resource: free(locale_dir_name); if (result == NULL) { TRACE_LOG("Could not find module'"); TRACE_LOG_Z_UCS(module_name); TRACE_LOG("'.\n"); return NULL; } if (locale_modules == NULL) locale_modules = create_stringmap(); if ((module_map = (stringmap*)get_stringmap_value(locale_modules, locale_name)) == NULL) { module_map = create_stringmap(); if ((add_stringmap_element(locale_modules, locale_name, module_map)) != 0) { TRACE_LOG("add_stringmap_element() failed.\n"); delete_locale_module(result); } } if (add_stringmap_element(module_map, module_name, result) != 0) { TRACE_LOG("add_stringmap_element() failed.\n"); delete_locale_module(result); } return result; }
// Returns malloc()ed string which needs to be free()d later. static char *get_path_for_locale(z_ucs *locale_name) { z_dir *dir; char *search_path; z_ucs *search_path_zucs, *path_ptr, *colon_index, *zucs_ptr; z_ucs *zucs_buf = NULL; size_t bufsize = 0, len, dirname_len; struct z_dir_ent z_dir_entry; char *dirname; char *locale_name_utf8, *locale_dir_name = NULL; search_path = locale_search_path == NULL ? default_search_path : locale_search_path; search_path_zucs = dup_utf8_string_to_zucs_string(search_path); path_ptr = search_path_zucs; TRACE_LOG("Search path: \""); TRACE_LOG_Z_UCS(search_path_zucs); TRACE_LOG("'.\n"); // open-resource: locale_name_utf8 = dup_zucs_string_to_utf8_string(locale_name); while (*path_ptr != 0) { colon_index = z_ucs_chr(path_ptr, Z_UCS_COLON); len = colon_index == NULL ? z_ucs_len(path_ptr) : (size_t)(colon_index - path_ptr); TRACE_LOG("len: %ld\n", (long)len); if (len > 0) { if (bufsize < len + 1) { TRACE_LOG("realloc buf for %ld chars / %ld bytes.\n", (long)len + 1, (long)sizeof(z_ucs) * (len + 1)); // open-resource: if ((zucs_ptr = realloc(zucs_buf, sizeof(z_ucs) * (len + 1))) == NULL) { // exit-point: TRACE_LOG("realloc() returned NULL.\n"); free(zucs_buf); free(locale_name_utf8); free(path_ptr); return NULL; } bufsize = len + 1; zucs_buf = zucs_ptr; } z_ucs_ncpy(zucs_buf, path_ptr, len); zucs_buf[len] = 0; // open-resource: if ((dirname = dup_zucs_string_to_utf8_string(zucs_buf)) == NULL) { // exit-point: TRACE_LOG("dup_zucs_string_to_utf8_string() returned NULL.\n"); free(zucs_buf); free(locale_name_utf8); free(path_ptr); return NULL; } TRACE_LOG("Path: '%s'\n", dirname); // open-resource: if ((dir = fsi->open_dir(dirname)) != NULL) { while (fsi->read_dir(&z_dir_entry, dir) == 0) { TRACE_LOG("Processing \"%s\".\n", z_dir_entry.d_name); if (strcasecmp(locale_name_utf8, z_dir_entry.d_name) == 0) { dirname_len = strlen(dirname) + strlen(z_dir_entry.d_name); TRACE_LOG("dirname_len: %ld.\n", (long)dirname_len); // open-resource: if ((locale_dir_name = malloc(dirname_len + 2)) == NULL) { // exit-point: TRACE_LOG("malloc() returned NULL.\n"); fsi->close_dir(dir); free(dirname); free(zucs_buf); free(locale_name_utf8); free(path_ptr); return NULL; } strcpy(locale_dir_name, dirname); strcat(locale_dir_name, "/"); strcat(locale_dir_name, z_dir_entry.d_name); break; } } // close-resource: fsi->close_dir(dir); } // close-resource: free(dirname); } if (locale_dir_name != NULL) break; path_ptr += len + (colon_index != NULL ? 1 : 0); } free(search_path_zucs); // close-resource: free(locale_name_utf8); // close-resource: free(zucs_buf); //TRACE_LOG("res:'"); //TRACE_LOG_Z_UCS(locale_dir_name); //TRACE_LOG("\n"); return locale_dir_name; }