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 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; }