/* * Copyright (c) 1997-2006 Motoyuki Kasahara * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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 "build-pre.h" #include "eb.h" #include "error.h" #include "font.h" #include "build-post.h" /* * Book ID counter. */ static EB_Book_Code book_counter = 0; /* * Unexported functions. */ static void eb_fix_misleaded_book(EB_Book *book); static EB_Error_Code eb_load_catalog(EB_Book *book); static EB_Error_Code eb_load_catalog_eb(EB_Book *book, const char *catalog_path); static EB_Error_Code eb_load_catalog_epwing(EB_Book *book, const char *catalog_path); static Zio_Code eb_get_hint_zio_code(int catalog_hint_value); static void eb_load_language(EB_Book *book); /* * Initialize `book'. */ void eb_initialize_book(EB_Book *book) { LOG(("in: eb_initialize_book()")); book->code = EB_BOOK_NONE; book->disc_code = EB_DISC_INVALID; book->character_code = EB_CHARCODE_INVALID; book->path = NULL; book->path_length = 0; book->subbooks = NULL; book->subbook_current = NULL; eb_initialize_text_context(book); eb_initialize_binary_context(book); eb_initialize_search_contexts(book); eb_initialize_binary_context(book); LOG(("out: eb_initialize_book()")); } /* * Bind `book' to `path'. */ EB_Error_Code eb_bind(EB_Book *book, const char *path) { EB_Error_Code error_code; char temporary_path[EB_MAX_PATH_LENGTH + 1]; LOG(("in: eb_bind(path=%s)", path)); /* * Clear the book if the book has already been bound. */ if (book->path != NULL) { eb_finalize_book(book); eb_initialize_book(book); } /* * Assign a book code. */ book->code = book_counter++; /* * Set the path of the book. * The length of the file name "/subdir/subsubdir/file.ebz;1" must * be EB_MAX_PATH_LENGTH maximum. */ if (EB_MAX_PATH_LENGTH < strlen(path)) { error_code = EB_ERR_TOO_LONG_FILE_NAME; goto failed; } strcpy(temporary_path, path); error_code = eb_canonicalize_path_name(temporary_path); if (error_code != EB_SUCCESS) goto failed; book->path_length = strlen(temporary_path); if (EB_MAX_PATH_LENGTH < book->path_length + 1 + EB_MAX_RELATIVE_PATH_LENGTH) { error_code = EB_ERR_TOO_LONG_FILE_NAME; goto failed; } book->path = (char *)malloc(book->path_length + 1); if (book->path == NULL) { error_code = EB_ERR_MEMORY_EXHAUSTED; goto failed; } strcpy(book->path, temporary_path); /* * Read information from the `LANGUAGE' file. * If failed to initialize, JIS X 0208 is assumed. */ eb_load_language(book); /* * Read information from the `CATALOG(S)' file. */ error_code = eb_load_catalog(book); if (error_code != EB_SUCCESS) goto failed; LOG(("out: eb_bind(book=%d) = %s", (int)book->code, eb_error_string(EB_SUCCESS))); return EB_SUCCESS; /* * An error occurs... */ failed: eb_finalize_book(book); LOG(("out: eb_bind() = %s", eb_error_string(error_code))); return error_code; } /* * Finish using `book'. */ void eb_finalize_book(EB_Book *book) { LOG(("in: eb_finalize_book(book=%d)", (int)book->code)); eb_unset_subbook(book); if (book->subbooks != NULL) { eb_finalize_subbooks(book); free(book->subbooks); book->subbooks = NULL; } book->subbook_current = NULL; eb_finalize_text_context(book); eb_finalize_binary_context(book); eb_finalize_search_contexts(book); eb_finalize_binary_context(book); if (book->path != NULL) free(book->path); book->code = EB_BOOK_NONE; book->disc_code = EB_DISC_INVALID; book->character_code = EB_CHARCODE_INVALID; book->path = NULL; book->path_length = 0; LOG(("out: eb_finalize_book()")); } /* * There are some books that EB Library sets wrong character code of * the book. They are written in JIS X 0208, but the library sets * ISO 8859-1. * * We fix the character of the books. The following table lists * titles of the first subbook in those books. */ static const char * const misleaded_book_table[] = { /* SONY DataDiskMan (DD-DR1) accessories. */ "%;%s%A%e%j!\\%S%8%M%9!\\%/%i%&%s", /* Shin Eiwa Waei Chujiten (earliest edition) */ "8&5fcode)); for (misleaded = misleaded_book_table; *misleaded != NULL; misleaded++) { if (strcmp(book->subbooks[0].title, *misleaded) == 0) { book->character_code = EB_CHARCODE_JISX0208; for (i = 0, subbook = book->subbooks; i < book->subbook_count; i++, subbook++) { eb_jisx0208_to_euc(subbook->title, subbook->title); } break; } } LOG(("out: eb_fix_misleaded_book()")); } /* * Read information from the `CATALOG(S)' file in 'book'. * Return EB_SUCCESS if it succeeds, error-code otherwise. */ static EB_Error_Code eb_load_catalog(EB_Book *book) { EB_Error_Code error_code; char catalog_file_name[EB_MAX_FILE_NAME_LENGTH + 1]; char catalog_path_name[EB_MAX_PATH_LENGTH + 1]; LOG(("in: eb_load_catalog(book=%d)", (int)book->code)); /* * Find a catalog file. */ if (eb_find_file_name(book->path, "catalog", catalog_file_name) == EB_SUCCESS) { book->disc_code = EB_DISC_EB; } else if (eb_find_file_name(book->path, "catalogs", catalog_file_name) == EB_SUCCESS) { book->disc_code = EB_DISC_EPWING; } else { error_code = EB_ERR_FAIL_OPEN_CAT; goto failed; } eb_compose_path_name(book->path, catalog_file_name, catalog_path_name); /* * Load the catalog file. */ if (book->disc_code == EB_DISC_EB) error_code = eb_load_catalog_eb(book, catalog_path_name); else error_code = eb_load_catalog_epwing(book, catalog_path_name); if (error_code != EB_SUCCESS) goto failed; /* * Fix chachacter-code of the book. */ eb_fix_misleaded_book(book); LOG(("out: eb_load_catalog() = %s", eb_error_string(EB_SUCCESS))); return EB_SUCCESS; /* * An error occurs... */ failed: if (book->subbooks != NULL) { free(book->subbooks); book->subbooks = NULL; } LOG(("out: eb_load_catalog() = %s", eb_error_string(error_code))); return error_code; } /* * Read information from the `CATALOG' file in 'book'. (EB) */ static EB_Error_Code eb_load_catalog_eb(EB_Book *book, const char *catalog_path) { EB_Error_Code error_code; char buffer[EB_SIZE_PAGE]; char *space; EB_Subbook *subbook; Zio zio; Zio_Code zio_code; int i; LOG(("in: eb_load_catalog_eb(book=%d, catalog=%s)", (int)book->code, catalog_path)); zio_initialize(&zio); /* * Open a catalog file. */ eb_path_name_zio_code(catalog_path, ZIO_PLAIN, &zio_code); if (zio_open(&zio, catalog_path, zio_code) < 0) { error_code = EB_ERR_FAIL_OPEN_CAT; goto failed; } /* * Get the number of subbooks in this book. */ if (zio_read(&zio, buffer, 16) != 16) { error_code = EB_ERR_FAIL_READ_CAT; goto failed; } book->subbook_count = eb_uint2(buffer); LOG(("aux: eb_load_catalog_eb(): subbook_count=%d", book->subbook_count)); if (EB_MAX_SUBBOOKS < book->subbook_count) book->subbook_count = EB_MAX_SUBBOOKS; if (book->subbook_count == 0) { error_code = EB_ERR_UNEXP_CAT; goto failed; } /* * Allocate memories for subbook entries. */ book->subbooks = (EB_Subbook *) malloc(sizeof(EB_Subbook) * book->subbook_count); if (book->subbooks == NULL) { error_code = EB_ERR_MEMORY_EXHAUSTED; goto failed; } eb_initialize_subbooks(book); /* * Read information about subbook. */ for (i = 0, subbook = book->subbooks; i < book->subbook_count; i++, subbook++) { /* * Read data from the catalog file. */ if (zio_read(&zio, buffer, EB_SIZE_EB_CATALOG) != EB_SIZE_EB_CATALOG) { error_code = EB_ERR_FAIL_READ_CAT; goto failed; } /* * Set a directory name. */ strncpy(subbook->directory_name, buffer + 2 + EB_MAX_EB_TITLE_LENGTH, EB_MAX_DIRECTORY_NAME_LENGTH); subbook->directory_name[EB_MAX_DIRECTORY_NAME_LENGTH] = '\0'; space = strchr(subbook->directory_name, ' '); if (space != NULL) *space = '\0'; eb_fix_directory_name(book->path, subbook->directory_name); /* * Set an index page. */ subbook->index_page = 1; /* * Set a title. (Convert from JISX0208 to EUC JP) */ strncpy(subbook->title, buffer + 2, EB_MAX_EB_TITLE_LENGTH); subbook->title[EB_MAX_EB_TITLE_LENGTH] = '\0'; if (book->character_code != EB_CHARCODE_ISO8859_1) eb_jisx0208_to_euc(subbook->title, subbook->title); subbook->initialized = 0; subbook->code = i; } /* * Close the catalog file. */ zio_close(&zio); zio_finalize(&zio); /* * Fix chachacter-code of the book. */ eb_fix_misleaded_book(book); LOG(("out: eb_load_catalog_eb() = %s", eb_error_string(EB_SUCCESS))); return EB_SUCCESS; /* * An error occurs... */ failed: zio_close(&zio); zio_initialize(&zio); LOG(("out: eb_load_catalog() = %s", eb_error_string(error_code))); return error_code; } /* * Read information from the `CATALOGS' file in 'book'. (EPWING) */ static EB_Error_Code eb_load_catalog_epwing(EB_Book *book, const char *catalog_path) { EB_Error_Code error_code; char buffer[EB_SIZE_PAGE]; char *buffer_p; char *space; EB_Subbook *subbook; EB_Font *font; Zio zio; Zio_Code zio_code; int epwing_version; int data_types; int i, j; LOG(("in: eb_load_catalog_epwing(book=%d, catalog=%s)", (int)book->code, catalog_path)); zio_initialize(&zio); /* * Open a catalog file. */ eb_path_name_zio_code(catalog_path, ZIO_PLAIN, &zio_code); if (zio_open(&zio, catalog_path, zio_code) < 0) { error_code = EB_ERR_FAIL_OPEN_CAT; goto failed; } /* * Get the number of subbooks in this book. */ if (zio_read(&zio, buffer, 16) != 16) { error_code = EB_ERR_FAIL_READ_CAT; goto failed; } book->subbook_count = eb_uint2(buffer); LOG(("aux: eb_load_catalog_epwing(): subbook_count=%d", book->subbook_count)); if (EB_MAX_SUBBOOKS < book->subbook_count) book->subbook_count = EB_MAX_SUBBOOKS; if (book->subbook_count == 0) { error_code = EB_ERR_UNEXP_CAT; goto failed; } epwing_version = eb_uint2(buffer + 2); LOG(("aux: eb_load_catalog_epwing(): epwing_version=%d", epwing_version)); /* * Allocate memories for subbook entries. */ book->subbooks = (EB_Subbook *) malloc(sizeof(EB_Subbook) * book->subbook_count); if (book->subbooks == NULL) { error_code = EB_ERR_MEMORY_EXHAUSTED; goto failed; } eb_initialize_subbooks(book); /* * Read information about subbook. */ for (i = 0, subbook = book->subbooks; i < book->subbook_count; i++, subbook++) { /* * Read data from the catalog file. */ if (zio_read(&zio, buffer, EB_SIZE_EPWING_CATALOG) != EB_SIZE_EPWING_CATALOG) { error_code = EB_ERR_FAIL_READ_CAT; goto failed; } /* * Set a directory name. */ strncpy(subbook->directory_name, buffer + 2 + EB_MAX_EPWING_TITLE_LENGTH, EB_MAX_DIRECTORY_NAME_LENGTH); subbook->directory_name[EB_MAX_DIRECTORY_NAME_LENGTH] = '\0'; space = strchr(subbook->directory_name, ' '); if (space != NULL) *space = '\0'; eb_fix_directory_name(book->path, subbook->directory_name); /* * Set an index page. */ subbook->index_page = eb_uint2(buffer + 2 + EB_MAX_EPWING_TITLE_LENGTH + EB_MAX_DIRECTORY_NAME_LENGTH + 4); /* * Set a title. (Convert from JISX0208 to EUC JP) */ strncpy(subbook->title, buffer + 2, EB_MAX_EPWING_TITLE_LENGTH); subbook->title[EB_MAX_EPWING_TITLE_LENGTH] = '\0'; if (book->character_code != EB_CHARCODE_ISO8859_1) eb_jisx0208_to_euc(subbook->title, subbook->title); /* * Narrow font file names. */ buffer_p = buffer + 2 + EB_MAX_EPWING_TITLE_LENGTH + 50; for (font = subbook->narrow_fonts, j = 0; j < EB_MAX_FONTS; j++, font++) { /* * Skip this entry if the first character of the file name * is not valid. */ if (*buffer_p == '\0' || 0x80 <= *((unsigned char *)buffer_p)) { buffer_p += EB_MAX_DIRECTORY_NAME_LENGTH; continue; } strncpy(font->file_name, buffer_p, EB_MAX_DIRECTORY_NAME_LENGTH); font->file_name[EB_MAX_DIRECTORY_NAME_LENGTH] = '\0'; font->font_code = j; font->page = 1; space = strchr(font->file_name, ' '); if (space != NULL) *space = '\0'; buffer_p += EB_MAX_DIRECTORY_NAME_LENGTH; } /* * Wide font file names. */ buffer_p = buffer + 2 + EB_MAX_EPWING_TITLE_LENGTH + 18; for (font = subbook->wide_fonts, j = 0; j < EB_MAX_FONTS; j++, font++) { /* * Skip this entry if the first character of the file name * is not valid. */ if (*buffer_p == '\0' || 0x80 <= *((unsigned char *)buffer_p)) { buffer_p += EB_MAX_DIRECTORY_NAME_LENGTH; continue; } strncpy(font->file_name, buffer_p, EB_MAX_DIRECTORY_NAME_LENGTH); font->file_name[EB_MAX_DIRECTORY_NAME_LENGTH] = '\0'; font->font_code = j; font->page = 1; space = strchr(font->file_name, ' '); if (space != NULL) *space = '\0'; buffer_p += EB_MAX_DIRECTORY_NAME_LENGTH; } subbook->initialized = 0; subbook->code = i; } /* * Set default file names and compression types. */ for (i = 0, subbook = book->subbooks; i < book->subbook_count; i++, subbook++) { strcpy(subbook->text_file_name, EB_FILE_NAME_HONMON); strcpy(subbook->graphic_file_name, EB_FILE_NAME_HONMON); strcpy(subbook->sound_file_name, EB_FILE_NAME_HONMON); subbook->text_hint_zio_code = ZIO_PLAIN; subbook->graphic_hint_zio_code = ZIO_PLAIN; subbook->sound_hint_zio_code = ZIO_PLAIN; } if (epwing_version == 1) goto succeeded; /* * Read extra information about subbook. */ for (i = 0, subbook = book->subbooks; i < book->subbook_count; i++, subbook++) { /* * Read data from the catalog file. * * We don't complain about unexpected EOF. In that case, we * return EB_SUCCESS. */ ssize_t read_result = zio_read(&zio, buffer, EB_SIZE_EPWING_CATALOG); if (read_result < 0) { error_code = EB_ERR_FAIL_READ_CAT; goto failed; } else if (read_result != EB_SIZE_EPWING_CATALOG) { break; } if (*(buffer + 4) == '\0') continue; /* * Set a text file name and its compression hint. */ *(subbook->text_file_name) = '\0'; strncpy(subbook->text_file_name, buffer + 4, EB_MAX_DIRECTORY_NAME_LENGTH); subbook->text_file_name[EB_MAX_DIRECTORY_NAME_LENGTH] = '\0'; space = strchr(subbook->text_file_name, ' '); if (space != NULL) *space = '\0'; subbook->text_hint_zio_code = eb_get_hint_zio_code(eb_uint1(buffer + 55)); if (subbook->text_hint_zio_code == ZIO_INVALID) { error_code = EB_ERR_UNEXP_CAT; goto failed; } data_types = eb_uint2(buffer + 41); /* * Set a graphic file name and its compression hint. */ *(subbook->graphic_file_name) = '\0'; if ((data_types & 0x03) == 0x02) { strncpy(subbook->graphic_file_name, buffer + 44, EB_MAX_DIRECTORY_NAME_LENGTH); subbook->graphic_hint_zio_code = eb_get_hint_zio_code(eb_uint1(buffer + 54)); } else if (((data_types >> 8) & 0x03) == 0x02) { strncpy(subbook->graphic_file_name, buffer + 56, EB_MAX_DIRECTORY_NAME_LENGTH); subbook->graphic_hint_zio_code = eb_get_hint_zio_code(eb_uint1(buffer + 53)); } subbook->graphic_file_name[EB_MAX_DIRECTORY_NAME_LENGTH] = '\0'; space = strchr(subbook->graphic_file_name, ' '); if (space != NULL) *space = '\0'; if (*(subbook->graphic_file_name) == '\0') { strcpy(subbook->graphic_file_name, subbook->text_file_name); subbook->graphic_hint_zio_code = subbook->text_hint_zio_code; } if (subbook->graphic_hint_zio_code == ZIO_INVALID) { error_code = EB_ERR_UNEXP_CAT; goto failed; } /* * Set a sound file name and its compression hint. */ *(subbook->sound_file_name) = '\0'; if ((data_types & 0x03) == 0x01) { strncpy(subbook->sound_file_name, buffer + 44, EB_MAX_DIRECTORY_NAME_LENGTH); subbook->sound_hint_zio_code = eb_get_hint_zio_code(eb_uint1(buffer + 54)); } else if (((data_types >> 8) & 0x03) == 0x01) { strncpy(subbook->sound_file_name, buffer + 56, EB_MAX_DIRECTORY_NAME_LENGTH); subbook->sound_hint_zio_code = eb_get_hint_zio_code(eb_uint1(buffer + 53)); } subbook->sound_file_name[EB_MAX_DIRECTORY_NAME_LENGTH] = '\0'; space = strchr(subbook->sound_file_name, ' '); if (space != NULL) *space = '\0'; if (*(subbook->sound_file_name) == '\0') { strcpy(subbook->sound_file_name, subbook->text_file_name); subbook->sound_hint_zio_code = subbook->text_hint_zio_code; } if (subbook->sound_hint_zio_code == ZIO_INVALID) { error_code = EB_ERR_UNEXP_CAT; goto failed; } } /* * Close the catalog file. */ succeeded: zio_close(&zio); zio_finalize(&zio); /* * Fix chachacter-code of the book. */ eb_fix_misleaded_book(book); LOG(("out: eb_load_catalog_epwing() = %s", eb_error_string(EB_SUCCESS))); return EB_SUCCESS; /* * An error occurs... */ failed: zio_close(&zio); zio_initialize(&zio); LOG(("out: eb_load_catalog_epwing() = %s", eb_error_string(error_code))); return error_code; } static Zio_Code eb_get_hint_zio_code(int catalog_hint_value) { switch (catalog_hint_value) { case 0x00: return ZIO_PLAIN; break; case 0x11: return ZIO_EPWING; break; case 0x12: return ZIO_EPWING6; break; } return ZIO_INVALID; } /* * Read information from the `LANGUAGE' file in `book'. */ static void eb_load_language(EB_Book *book) { Zio zio; Zio_Code zio_code; char language_path_name[EB_MAX_PATH_LENGTH + 1]; char language_file_name[EB_MAX_FILE_NAME_LENGTH + 1]; char buffer[16]; LOG(("in: eb_load_language(book=%d)", (int)book->code)); zio_initialize(&zio); book->character_code = EB_CHARCODE_JISX0208; /* * Open the language file. */ if (eb_find_file_name(book->path, "language", language_file_name) != EB_SUCCESS) goto failed; eb_compose_path_name(book->path, language_file_name, language_path_name); eb_path_name_zio_code(language_path_name, ZIO_PLAIN, &zio_code); if (zio_open(&zio, language_path_name, zio_code) < 0) goto failed; /* * Get a character code of the book, and get the number of langueages * in the file. */ if (zio_read(&zio, buffer, 16) != 16) goto failed; book->character_code = eb_uint2(buffer); if (book->character_code != EB_CHARCODE_ISO8859_1 && book->character_code != EB_CHARCODE_JISX0208 && book->character_code != EB_CHARCODE_JISX0208_GB2312) { goto failed; } zio_close(&zio); LOG(("out: eb_load_language()")); return; /* * An error occurs... */ failed: zio_close(&zio); LOG(("out: eb_load_language()")); } /* * Test whether `book' is bound. */ int eb_is_bound(EB_Book *book) { int is_bound; LOG(("in: eb_is_bound(book=%d)", (int)book->code)); /* * Check for the current status. */ is_bound = (book->path != NULL); LOG(("out: eb_is_bound() = %d", is_bound)); return is_bound; } /* * Return the bound path of `book'. */ EB_Error_Code eb_path(EB_Book *book, char *path) { EB_Error_Code error_code; LOG(("in: eb_path(book=%d)", (int)book->code)); /* * Check for the current status. */ if (book->path == NULL) { error_code = EB_ERR_UNBOUND_BOOK; goto failed; } /* * Copy the path to `path'. */ strcpy(path, book->path); LOG(("out: eb_path(path=%s) = %s", path, eb_error_string(EB_SUCCESS))); return EB_SUCCESS; /* * An error occurs... */ failed: *path = '\0'; LOG(("out: eb_path() = %s", eb_error_string(error_code))); return error_code; } /* * Inspect a disc type. */ EB_Error_Code eb_disc_type(EB_Book *book, EB_Disc_Code *disc_code) { EB_Error_Code error_code; LOG(("in: eb_disc_type(book=%d)", (int)book->code)); /* * Check for the current status. */ if (book->path == NULL) { error_code = EB_ERR_UNBOUND_BOOK; goto failed; } /* * Copy the disc code to `disc_code'. */ *disc_code = book->disc_code; LOG(("out: eb_disc_type(disc_code=%d) = %s", (int)*disc_code, eb_error_string(EB_SUCCESS))); return EB_SUCCESS; /* * An error occurs... */ failed: *disc_code = EB_DISC_INVALID; LOG(("out: eb_disc_type() = %s", eb_error_string(error_code))); return error_code; } /* * Inspect a character code used in the book. */ EB_Error_Code eb_character_code(EB_Book *book, EB_Character_Code *character_code) { EB_Error_Code error_code; LOG(("in: eb_character_code(book=%d)", (int)book->code)); /* * Check for the current status. */ if (book->path == NULL) { error_code = EB_ERR_UNBOUND_BOOK; goto failed; } /* * Copy the character code to `character_code'. */ *character_code = book->character_code; LOG(("out: eb_character_code(character_code=%d) = %s", (int)*character_code, eb_error_string(EB_SUCCESS))); return EB_SUCCESS; /* * An error occurs... */ failed: *character_code = EB_CHARCODE_INVALID; LOG(("out: eb_character_code() = %s", eb_error_string(error_code))); return error_code; }