static bool open_mixed (struct mixed *mixed, RECODE_TASK task) { mixed->input_name = task->input.name; mixed->output_name = task->output.name; task->strategy = RECODE_SEQUENCE_IN_MEMORY; task->byte_order_mark = false; /* Open both files ourselves. */ if (!*(task->input.name)) task->input.file = stdin; else if (task->input.file = fopen (mixed->input_name, "r"), !task->input.file) { recode_perror (NULL, "fopen (%s)", task->input.name); return false; } task->input.name = NULL; if (!*(task->output.name)) task->output.file = stdout; else if (task->output.file = fopen (mixed->output_name, "w"), !task->output.file) { recode_perror (NULL, "fopen (%s)", task->output.name); if (*(task->input.name)) fclose (task->input.file); return false; } task->output.name = NULL; /* Prepare for dynamic plumbing copy. */ memset (&mixed->subtask, 0, sizeof (struct recode_subtask)); mixed->subtask.task = task; mixed->subtask.input = task->input; mixed->subtask.output = task->output; memset (&mixed->buffer, 0, sizeof (struct recode_read_write_text)); return true; }
static bool wrapped_transform (iconv_t conversion, RECODE_SUBTASK subtask) { char output_buffer[BUFFER_SIZE]; char input_buffer[BUFFER_SIZE]; int input_char = get_byte (subtask); char *cursor = input_buffer; bool drain_first = false; while (true) { /* The output buffer is fully avaible at this point. */ char *input = input_buffer; char *output = output_buffer; size_t input_left = 0; size_t output_left = BUFFER_SIZE; int saved_errno = 0; size_t converted; if (drain_first) { /* Drain all accumulated partial state and emit output to return to the initial shift state. */ converted = iconv (conversion, NULL, NULL, &output, &output_left); if (converted == (size_t) -1) saved_errno = errno; } if (saved_errno == 0) { /* Continue filling the input buffer. */ while (input_char != EOF && cursor < input_buffer + BUFFER_SIZE) { *cursor++ = input_char; input_char = get_byte (subtask); } if (cursor == input_buffer) { if (output == output_buffer) { /* All work has been done, just make sure we drained. */ if (drain_first) break; drain_first = true; continue; } } else { /* Convert accumulated input and add it to the output buffer. */ input = input_buffer; input_left = cursor - input_buffer; converted = iconv (conversion, &input, &input_left, &output, &output_left); if (converted == (size_t) -1) saved_errno = errno; } } /* Send the converted result, so freeing the output buffer. */ for (cursor = output_buffer; cursor < output; cursor++) put_byte (*cursor, subtask); /* Act according to the outcome of the iconv call. */ drain_first = false; if (saved_errno != 0 && saved_errno != E2BIG) { if (saved_errno == EILSEQ) { /* Invalid input. Skip one byte. */ RETURN_IF_NOGO (RECODE_INVALID_INPUT, subtask); assert (input_left > 0); input++; input_left--; /* Why is draining required? */ drain_first = true; } else if (saved_errno == EINVAL) { if (input + input_left < input_buffer + BUFFER_SIZE && input_char == EOF) /* Incomplete multibyte sequence at end of input. */ RETURN_IF_NOGO (RECODE_INVALID_INPUT, subtask); } else { recode_perror (subtask->task->request->outer, "iconv ()"); RETURN_IF_NOGO (RECODE_SYSTEM_ERROR, subtask); } } /* Move back any unprocessed part of the input buffer. */ for (cursor = input_buffer; input_left != 0; input_left--) *cursor++ = *input++; } SUBTASK_RETURN (subtask); }