From f70b0cc19ff28b7dd271130aa45466364ad9c14c Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sat, 9 Jan 2021 14:39:52 -0800 Subject: [PATCH] Don't use eb submodule --- .gitignore | 1 + .gitmodules | 3 - all.c | 143 ++++ appendix.c | 399 ++++++++++ appendix.h | 96 +++ appsub.c | 748 ++++++++++++++++++ bcd.c | 97 +++ binary.c | 1402 +++++++++++++++++++++++++++++++++ binary.h | 69 ++ bitmap.c | 1362 ++++++++++++++++++++++++++++++++ book.c | 954 ++++++++++++++++++++++ booklist.c | 264 +++++++ booklist.h | 56 ++ build-post.h | 356 +++++++++ build-pre.h | 108 +++ build_eb.sh | 7 - copyright.c | 119 +++ cross.c | 188 +++++ defs.h | 1006 ++++++++++++++++++++++++ eb | 1 - eb.c | 76 ++ eb.h | 157 ++++ endword.c | 189 +++++ error.c | 277 +++++++ error.h | 142 ++++ exactword.c | 188 +++++ filename.c | 609 ++++++++++++++ font.c | 539 +++++++++++++ font.h | 196 +++++ hook.c | 319 ++++++++ jacode.c | 108 +++ keyword.c | 189 +++++ linebuf.c | 406 ++++++++++ linebuf.h | 66 ++ log.c | 192 +++++ match.c | 583 ++++++++++++++ menu.c | 206 +++++ multi.c | 839 ++++++++++++++++++++ narwalt.c | 626 +++++++++++++++ narwfont.c | 1097 ++++++++++++++++++++++++++ readtext.c | 2141 ++++++++++++++++++++++++++++++++++++++++++++++++++ search.c | 1650 ++++++++++++++++++++++++++++++++++++++ setword.c | 1310 ++++++++++++++++++++++++++++++ stopcode.c | 111 +++ strcasecmp.c | 105 +++ subbook.c | 1140 +++++++++++++++++++++++++++ text.c | 121 +++ text.h | 162 ++++ widealt.c | 627 +++++++++++++++ widefont.c | 1098 ++++++++++++++++++++++++++ word.c | 185 +++++ zig.go | 3 +- zig.h | 8 +- zio.c | 2014 +++++++++++++++++++++++++++++++++++++++++++++++ zio.h | 232 ++++++ 55 files changed, 25273 insertions(+), 17 deletions(-) create mode 100644 all.c create mode 100644 appendix.c create mode 100644 appendix.h create mode 100644 appsub.c create mode 100644 bcd.c create mode 100644 binary.c create mode 100644 binary.h create mode 100644 bitmap.c create mode 100644 book.c create mode 100644 booklist.c create mode 100644 booklist.h create mode 100644 build-post.h create mode 100644 build-pre.h delete mode 100755 build_eb.sh create mode 100644 copyright.c create mode 100644 cross.c create mode 100644 defs.h delete mode 160000 eb create mode 100644 eb.c create mode 100644 eb.h create mode 100644 endword.c create mode 100644 error.c create mode 100644 error.h create mode 100644 exactword.c create mode 100644 filename.c create mode 100644 font.c create mode 100644 font.h create mode 100644 hook.c create mode 100644 jacode.c create mode 100644 keyword.c create mode 100644 linebuf.c create mode 100644 linebuf.h create mode 100644 log.c create mode 100644 match.c create mode 100644 menu.c create mode 100644 multi.c create mode 100644 narwalt.c create mode 100644 narwfont.c create mode 100644 readtext.c create mode 100644 search.c create mode 100644 setword.c create mode 100644 stopcode.c create mode 100644 strcasecmp.c create mode 100644 subbook.c create mode 100644 text.c create mode 100644 text.h create mode 100644 widealt.c create mode 100644 widefont.c create mode 100644 word.c create mode 100644 zio.c create mode 100644 zio.h diff --git a/.gitignore b/.gitignore index a173085..7f0d6a6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .ccls-cache +zero-epwing diff --git a/.gitmodules b/.gitmodules index 890e79f..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "eb"] - path = eb - url = https://github.com/FooSoft/eb diff --git a/all.c b/all.c new file mode 100644 index 0000000..124834a --- /dev/null +++ b/all.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2016 Alex Yatskov + * Author: Alex Yatskov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "build-pre.h" +#include "eb.h" +#include "error.h" +#include "build-post.h" + +static int eb_match_all(const char word[], const char pattern[], size_t length) { + (void) word; + (void) pattern; + (void) length; + return 0; +} + +static EB_Error_Code eb_search_all(EB_Book* book, EB_Word_Code word_code) { + EB_Error_Code error_code = EB_SUCCESS; + + do { + eb_lock(&book->lock); + LOG(("in: eb_search_all(book=%d)", book->code)); + + /* Current subbook must have been set. */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + break; + } + + /* Initialize search context. */ + eb_reset_search_contexts(book); + EB_Search_Context* context = book->search_contexts; + context->code = EB_SEARCH_ALL; + context->compare_pre = eb_match_all; + context->compare_single = eb_match_all; + context->compare_group = eb_match_all; + + /* Get a page number. */ + switch (word_code) { + case EB_WORD_ALPHABET: + if (book->subbook_current->word_alphabet.start_page != 0) { + context->page = book->subbook_current->word_alphabet.start_page; + } + else { + error_code = EB_ERR_NO_SUCH_SEARCH; + } + break; + case EB_WORD_KANA: + if (book->subbook_current->word_kana.start_page != 0) { + context->page = book->subbook_current->word_kana.start_page; + } + else { + error_code = EB_ERR_NO_SUCH_SEARCH; + } + break; + case EB_WORD_OTHER: + if (book->subbook_current->word_asis.start_page != 0) { + context->page = book->subbook_current->word_asis.start_page; + } + else { + error_code = EB_ERR_NO_SUCH_SEARCH; + } + break; + default: + error_code = EB_ERR_NO_SUCH_SEARCH; + break; + } + + if (error_code != EB_SUCCESS) { + break; + } + + /* Pre-search. */ + error_code = eb_presearch_word(book, context); + if (error_code != EB_SUCCESS) { + break; + } + } + while (0); + + if (error_code != EB_SUCCESS) { + eb_reset_search_contexts(book); + } + + LOG(("out: eb_search_all() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + + return error_code; +} + +int eb_have_all_search(EB_Book* book) { + eb_lock(&book->lock); + LOG(("in: eb_have_all_search(book=%d)", book->code)); + + int result = 0; + for (;;) { + const EB_Subbook * sb = book->subbook_current; + if (sb == NULL) { + break; + } + + if (sb->word_alphabet.start_page == 0 && sb->word_asis.start_page == 0 && sb->word_kana.start_page == 0) { + break; + } + + result = 1; + break; + } + + LOG(("out: eb_have_all_search() = %d", result)); + eb_unlock(&book->lock); + return result; +} + +EB_Error_Code eb_search_all_alphabet(EB_Book* book) { + return eb_search_all(book, EB_WORD_ALPHABET); +} + +EB_Error_Code eb_search_all_kana(EB_Book* book) { + return eb_search_all(book, EB_WORD_KANA); +} + +EB_Error_Code eb_search_all_asis(EB_Book* book) { + return eb_search_all(book, EB_WORD_OTHER); +} diff --git a/appendix.c b/appendix.c new file mode 100644 index 0000000..ae6644d --- /dev/null +++ b/appendix.c @@ -0,0 +1,399 @@ +/* + * 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 "appendix.h" +#include "build-post.h" + +/* + * Appendix ID counter. + */ +static EB_Book_Code appendix_counter = 0; + + +/* + * Unexported functions. + */ +static EB_Error_Code eb_load_appendix_catalog(EB_Appendix *appendix); + + +/* + * Initialize alternation text cache in `appendix'. + */ +void +eb_initialize_alt_caches(EB_Appendix *appendix) +{ + EB_Alternation_Cache *p; + int i; + + LOG(("in: eb_initialize_alt_caches(appendix=%d)", (int)appendix->code)); + + for (i = 0, p = appendix->narrow_cache; + i < EB_MAX_ALTERNATION_CACHE; i++, p++) + p->character_number = -1; + for (i = 0, p = appendix->wide_cache; + i < EB_MAX_ALTERNATION_CACHE; i++, p++) + p->character_number = -1; + + LOG(("out: eb_initialize_alt_caches()")); +} + + +/* + * Finalize alternation text cache in `appendix'. + */ +void +eb_finalize_alt_caches(EB_Appendix *appendix) +{ + LOG(("in+out: eb_finalize_alt_caches(appendix=%d)", (int)appendix->code)); + + /* nothing to be done */ +} + + +/* + * Initialize `appendix'. + */ +void +eb_initialize_appendix(EB_Appendix *appendix) +{ + LOG(("in: eb_initialize_appendix()")); + + appendix->code = EB_BOOK_NONE; + appendix->path = NULL; + appendix->path_length = 0; + appendix->disc_code = EB_DISC_INVALID; + appendix->subbook_count = 0; + appendix->subbooks = NULL; + appendix->subbook_current = NULL; + eb_initialize_lock(&appendix->lock); + eb_initialize_alt_caches(appendix); + + LOG(("out: eb_initialize_appendix()")); +} + + +/* + * Finalize `appendix'. + */ +void +eb_finalize_appendix(EB_Appendix *appendix) +{ + LOG(("in: eb_finalize_appendix(appendix=%d)", (int)appendix->code)); + + appendix->code = EB_BOOK_NONE; + + if (appendix->path != NULL) { + free(appendix->path); + appendix->path = NULL; + } + appendix->path_length = 0; + + appendix->disc_code = EB_DISC_INVALID; + + if (appendix->subbooks != NULL) { + eb_finalize_appendix_subbooks(appendix); + free(appendix->subbooks); + appendix->subbooks = NULL; + appendix->subbook_count = 0; + } + appendix->subbook_current = NULL; + eb_finalize_lock(&appendix->lock); + eb_finalize_alt_caches(appendix); + + LOG(("out: eb_finalize_appendix()")); +} + + +/* + * Bind `appendix' to `path'. + */ +EB_Error_Code +eb_bind_appendix(EB_Appendix *appendix, const char *path) +{ + EB_Error_Code error_code; + char temporary_path[EB_MAX_PATH_LENGTH + 1]; + + eb_lock(&appendix->lock); + LOG(("in: eb_bind_appendix(path=%s)", path)); + + /* + * Reset structure members in the appendix. + */ + if (appendix->path != NULL) { + eb_finalize_appendix(appendix); + eb_initialize_appendix(appendix); + } + + /* + * Assign a book code. + */ + pthread_mutex_lock(&appendix_counter_mutex); + appendix->code = appendix_counter++; + pthread_mutex_unlock(&appendix_counter_mutex); + + /* + * Set path of the appendix. + * The length of the file name "path/subdir/subsubdir/file.;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; + appendix->path_length = strlen(temporary_path); + + if (EB_MAX_PATH_LENGTH + < appendix->path_length + 1 + EB_MAX_RELATIVE_PATH_LENGTH) { + error_code = EB_ERR_TOO_LONG_FILE_NAME; + goto failed; + } + + appendix->path = (char *)malloc(appendix->path_length + 1); + if (appendix->path == NULL) { + error_code = EB_ERR_MEMORY_EXHAUSTED; + goto failed; + } + strcpy(appendix->path, temporary_path); + + /* + * Read information from the catalog file. + */ + error_code = eb_load_appendix_catalog(appendix); + if (error_code != EB_SUCCESS) + goto failed; + + LOG(("out: eb_bind_appendix(appendix=%d) = %s", (int)appendix->code, + eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_finalize_appendix(appendix); + LOG(("out: eb_bind_appendix() = %s", eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} + + +/* + * Read information from the `CATALOG(S)' file in `appendix'. + * Return EB_SUCCESS, if it succeeds, error-code ohtherwise. + */ +static EB_Error_Code +eb_load_appendix_catalog(EB_Appendix *appendix) +{ + EB_Error_Code error_code; + char buffer[EB_SIZE_PAGE]; + char catalog_file_name[EB_MAX_FILE_NAME_LENGTH + 1]; + char catalog_path_name[EB_MAX_PATH_LENGTH + 1]; + char *space; + EB_Appendix_Subbook *subbook; + size_t catalog_size; + size_t title_size; + Zio zio; + Zio_Code zio_code; + int i; + + LOG(("in: eb_load_appendix_catalog(appendix=%d)", (int)appendix->code)); + + zio_initialize(&zio); + + /* + * Find a catalog file. + */ + if (eb_find_file_name(appendix->path, "catalog", catalog_file_name) + == EB_SUCCESS) { + appendix->disc_code = EB_DISC_EB; + catalog_size = EB_SIZE_EB_CATALOG; + title_size = EB_MAX_EB_TITLE_LENGTH; + } else if (eb_find_file_name(appendix->path, "catalogs", catalog_file_name) + == EB_SUCCESS) { + appendix->disc_code = EB_DISC_EPWING; + catalog_size = EB_SIZE_EPWING_CATALOG; + title_size = EB_MAX_EPWING_TITLE_LENGTH; + } else { + error_code = EB_ERR_FAIL_OPEN_CATAPP; + goto failed; + } + + eb_compose_path_name(appendix->path, catalog_file_name, catalog_path_name); + eb_path_name_zio_code(catalog_path_name, ZIO_PLAIN, &zio_code); + + /* + * Open the catalog file. + */ + if (zio_open(&zio, catalog_path_name, zio_code) < 0) { + error_code = EB_ERR_FAIL_OPEN_CATAPP; + goto failed; + } + + /* + * Get the number of subbooks in the appendix. + */ + if (zio_read(&zio, buffer, 16) != 16) { + error_code = EB_ERR_FAIL_READ_CATAPP; + goto failed; + } + appendix->subbook_count = eb_uint2(buffer); + if (EB_MAX_SUBBOOKS < appendix->subbook_count) + appendix->subbook_count = EB_MAX_SUBBOOKS; + if (appendix->subbook_count == 0) { + error_code = EB_ERR_UNEXP_CATAPP; + goto failed; + } + + /* + * Allocate memories for subbook entries. + */ + appendix->subbooks = (EB_Appendix_Subbook *) + malloc(sizeof(EB_Appendix_Subbook) * appendix->subbook_count); + if (appendix->subbooks == NULL) { + error_code = EB_ERR_MEMORY_EXHAUSTED; + goto failed; + } + eb_initialize_appendix_subbooks(appendix); + + /* + * Read subbook information. + */ + for (i = 0, subbook = appendix->subbooks; i < appendix->subbook_count; + i++, subbook++) { + /* + * Read data from the catalog file. + */ + if (zio_read(&zio, buffer, catalog_size) != catalog_size) { + error_code = EB_ERR_FAIL_READ_CAT; + goto failed; + } + + /* + * Set a directory name of the subbook. + */ + strncpy(subbook->directory_name, buffer + 2 + title_size, + 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(appendix->path, subbook->directory_name); + } + + /* + * Close the catalog file. + */ + zio_close(&zio); + zio_finalize(&zio); + LOG(("out: eb_load_appendix_catalog() = %s", eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + zio_close(&zio); + zio_finalize(&zio); + if (appendix->subbooks != NULL) { + free(appendix->subbooks); + appendix->subbooks = NULL; + } + LOG(("out: eb_load_appendix_catalog() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Examine whether `appendix' is bound or not. + */ +int +eb_is_appendix_bound(EB_Appendix *appendix) +{ + int is_bound; + + eb_lock(&appendix->lock); + LOG(("in: eb_is_appendix_bound(appendix=%d)", (int)appendix->code)); + + is_bound = (appendix->path != NULL); + + LOG(("out: eb_is_appendix_bound() = %d", is_bound)); + eb_unlock(&appendix->lock); + + return is_bound; +} + + +/* + * Get the bound path of `appendix'. + */ +EB_Error_Code +eb_appendix_path(EB_Appendix *appendix, char *path) +{ + EB_Error_Code error_code; + + eb_lock(&appendix->lock); + LOG(("in: eb_appendix_path(appendix=%d)", (int)appendix->code)); + + /* + * Check for the current status. + */ + if (appendix->path == NULL) { + error_code = EB_ERR_UNBOUND_APP; + goto failed; + } + + /* + * Copy the path to `path'. + */ + strcpy(path, appendix->path); + + LOG(("out: eb_appendix_path(path=%s) = %s", + path, eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *path = '\0'; + LOG(("out: eb_appendix_path() = %s", eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} diff --git a/appendix.h b/appendix.h new file mode 100644 index 0000000..c6beb31 --- /dev/null +++ b/appendix.h @@ -0,0 +1,96 @@ +/* -*- C -*- + * 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. + */ + +#ifndef EB_APPENDIX_H +#define EB_APPENDIX_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "eb.h" + +/* + * Function declarations. + */ +/* appendix.c */ +void eb_initialize_appendix(EB_Appendix *appendix); +void eb_finalize_appendix(EB_Appendix *appendix); +EB_Error_Code eb_bind_appendix(EB_Appendix *appendix, const char *path); +int eb_is_appendix_bound(EB_Appendix *appendix); +EB_Error_Code eb_appendix_path(EB_Appendix *appendix, char *path); + +/* appsub.c */ +EB_Error_Code eb_load_all_appendix_subbooks(EB_Appendix *appendix); +EB_Error_Code eb_appendix_subbook_list(EB_Appendix *appendix, + EB_Subbook_Code *subbook_list, int *subbook_count); +EB_Error_Code eb_appendix_subbook(EB_Appendix *appendix, + EB_Subbook_Code *subbook_code); +EB_Error_Code eb_appendix_subbook_directory(EB_Appendix *appendix, + char *directory); +EB_Error_Code eb_appendix_subbook_directory2(EB_Appendix *appendix, + EB_Subbook_Code subbook_code, char *directory); +EB_Error_Code eb_set_appendix_subbook(EB_Appendix *appendix, + EB_Subbook_Code subbook_code); +void eb_unset_appendix_subbook(EB_Appendix *appendix); + +/* narwalt.c */ +int eb_have_narrow_alt(EB_Appendix *appendix); +EB_Error_Code eb_narrow_alt_start(EB_Appendix *appendix, int *start); +EB_Error_Code eb_narrow_alt_end(EB_Appendix *appendix, int *end); +EB_Error_Code eb_narrow_alt_character_text(EB_Appendix *appendix, + int character_number, char *text); +EB_Error_Code eb_forward_narrow_alt_character(EB_Appendix *appendix, + int n, int *character_number); +EB_Error_Code eb_backward_narrow_alt_character(EB_Appendix *appendix, + int n, int *character_number); + +/* stopcode.c */ +int eb_have_stop_code(EB_Appendix *appendix); +EB_Error_Code eb_stop_code(EB_Appendix *appendix, int *); + +/* widealt.c */ +int eb_have_wide_alt(EB_Appendix *appendix); +EB_Error_Code eb_wide_alt_start(EB_Appendix *appendix, int *start); +EB_Error_Code eb_wide_alt_end(EB_Appendix *appendix, int *end); +EB_Error_Code eb_wide_alt_character_text(EB_Appendix *appendix, + int character_number, char *text); +EB_Error_Code eb_forward_wide_alt_character(EB_Appendix *appendix, int n, + int *character_number); +EB_Error_Code eb_backward_wide_alt_character(EB_Appendix *appendix, int n, + int *character_number); + +/* for backward compatibility */ +#define eb_suspend_appendix eb_unset_appendix_subbook +#define eb_initialize_all_appendix_subbooks eb_load_all_appendix_subbooks + +#ifdef __cplusplus +} +#endif + +#endif /* not EB_APPENDIX_H */ diff --git a/appsub.c b/appsub.c new file mode 100644 index 0000000..9bad118 --- /dev/null +++ b/appsub.c @@ -0,0 +1,748 @@ +/* + * 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 "appendix.h" +#include "build-post.h" + +/* + * Unexported functions. + */ +static EB_Error_Code eb_load_appendix_subbook(EB_Appendix *appendix); +static EB_Error_Code eb_set_appendix_subbook_eb(EB_Appendix *appendix, + EB_Subbook_Code subbook_code); +static EB_Error_Code eb_set_appendix_subbook_epwing(EB_Appendix *appendix, + EB_Subbook_Code subbook_code); + +/* + * Initialize all subbooks in `appendix'. + */ +void +eb_initialize_appendix_subbooks(EB_Appendix *appendix) +{ + EB_Appendix_Subbook *subbook; + int i; + + LOG(("in: eb_initialize_appendix_subbooks(appendix=%d)", + (int)appendix->code)); + + for (i = 0, subbook = appendix->subbooks; i < appendix->subbook_count; + i++, subbook++) { + subbook->initialized = 0; + subbook->code = i; + subbook->directory_name[0] = '\0'; + subbook->data_directory_name[0] = '\0'; + subbook->file_name[0] = '\0'; + subbook->character_code = EB_CHARCODE_INVALID; + subbook->narrow_start = -1; + subbook->wide_start = -1; + subbook->narrow_end = -1; + subbook->wide_end = -1; + subbook->narrow_page = 0; + subbook->wide_page = 0; + subbook->stop_code0 = 0; + subbook->stop_code1 = 0; + zio_initialize(&subbook->zio); + } + + LOG(("out: eb_initialize_appendix_subbooks()")); +} + + +/* + * Initialize subbooks in `appendix'. + */ +void +eb_finalize_appendix_subbooks(EB_Appendix *appendix) +{ + EB_Appendix_Subbook *subbook; + int i; + + LOG(("in: eb_finalize_appendix_subbooks(appendix=%d)", + (int)appendix->code)); + + for (i = 0, subbook = appendix->subbooks; i < appendix->subbook_count; + i++, subbook++) { + zio_finalize(&appendix->subbooks[i].zio); + } + + LOG(("out: eb_finalize_appendix_subbooks()")); +} + + +/* + * Load all subbooks in `appendix'. + */ +static EB_Error_Code +eb_load_appendix_subbook(EB_Appendix *appendix) +{ + EB_Error_Code error_code; + EB_Appendix_Subbook *subbook; + char buffer[16]; + int stop_code_page; + int character_count; + + LOG(("in: eb_load_appendix_subbook(appendix=%d)", (int)appendix->code)); + + subbook = appendix->subbook_current; + + /* + * Check for the current status. + */ + if (subbook == NULL) { + error_code = EB_ERR_NO_CUR_APPSUB; + goto failed; + } + + /* + * If the subbook has already initialized, return immediately. + */ + if (subbook->initialized != 0) + goto succeeded; + + /* + * Rewind the APPENDIX file. + */ + if (zio_lseek(&subbook->zio, 0, SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_APP; + goto failed; + } + + /* + * Set character code used in the appendix. + */ + if (zio_read(&subbook->zio, buffer, 16) != 16) { + error_code = EB_ERR_FAIL_READ_APP; + goto failed; + } + subbook->character_code = eb_uint2(buffer + 2); + + /* + * Set information about alternation text of wide font. + */ + if (zio_read(&subbook->zio, buffer, 16) != 16) { + error_code = EB_ERR_FAIL_READ_APP; + goto failed; + } + character_count = eb_uint2(buffer + 12); + + if (0 < character_count) { + subbook->narrow_page = eb_uint4(buffer); + subbook->narrow_start = eb_uint2(buffer + 10); + + if (subbook->character_code == EB_CHARCODE_ISO8859_1) { + subbook->narrow_end = subbook->narrow_start + + ((character_count / 0xfe) << 8) + (character_count % 0xfe) + - 1; + if (0xfe < (subbook->narrow_end & 0xff)) + subbook->narrow_end += 3; + + if ((subbook->narrow_start & 0xff) < 0x01 + || 0xfe < (subbook->narrow_start & 0xff) + || subbook->narrow_start < 0x0001 + || 0x1efe < subbook->narrow_end) { + error_code = EB_ERR_UNEXP_APP; + goto failed; + } + } else { + subbook->narrow_end = subbook->narrow_start + + ((character_count / 0x5e) << 8) + (character_count % 0x5e) + - 1; + if (0x7e < (subbook->narrow_end & 0xff)) + subbook->narrow_end += 0xa3; + + if ((subbook->narrow_start & 0xff) < 0x21 + || 0x7e < (subbook->narrow_start & 0xff) + || subbook->narrow_start < 0xa121 + || 0xfe7e < subbook->narrow_end) { + error_code = EB_ERR_UNEXP_APP; + goto failed; + } + } + } + + /* + * Set information about alternation text of wide font. + */ + if (zio_read(&subbook->zio, buffer, 16) != 16) { + error_code = EB_ERR_FAIL_READ_APP; + goto failed; + } + character_count = eb_uint2(buffer + 12); + + if (0 < character_count) { + subbook->wide_page = eb_uint4(buffer); + subbook->wide_start = eb_uint2(buffer + 10); + + if (subbook->character_code == EB_CHARCODE_ISO8859_1) { + subbook->wide_end = subbook->wide_start + + ((character_count / 0xfe) << 8) + (character_count % 0xfe) + - 1; + if (0xfe < (subbook->wide_end & 0xff)) + subbook->wide_end += 3; + + if ((subbook->wide_start & 0xff) < 0x01 + || 0xfe < (subbook->wide_start & 0xff) + || subbook->wide_start < 0x0001 + || 0x1efe < subbook->wide_end) { + error_code = EB_ERR_UNEXP_APP; + goto failed; + } + } else { + subbook->wide_end = subbook->wide_start + + ((character_count / 0x5e) << 8) + (character_count % 0x5e) + - 1; + if (0x7e < (subbook->wide_end & 0xff)) + subbook->wide_end += 0xa3; + + if ((subbook->wide_start & 0xff) < 0x21 + || 0x7e < (subbook->wide_start & 0xff) + || subbook->wide_start < 0xa121 + || 0xfe7e < subbook->wide_end) { + error_code = EB_ERR_UNEXP_APP; + goto failed; + } + } + } + + /* + * Set stop-code. + */ + if (zio_read(&subbook->zio, buffer, 16) != 16) { + error_code = EB_ERR_FAIL_READ_APP; + goto failed; + } + stop_code_page = eb_uint4(buffer); + if (0 < stop_code_page) { + if (zio_lseek(&subbook->zio, ((off_t) stop_code_page - 1) * EB_SIZE_PAGE, + SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_APP; + goto failed; + } + if (zio_read(&subbook->zio, buffer, 16) != 16) { + error_code = EB_ERR_FAIL_READ_APP; + goto failed; + } + if (eb_uint2(buffer) != 0) { + subbook->stop_code0 = eb_uint2(buffer + 2); + subbook->stop_code1 = eb_uint2(buffer + 4); + } + } + + /* + * Rewind the file descriptor, again. + */ + if (zio_lseek(&subbook->zio, 0, SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_APP; + goto failed; + } + + /* + * Initialize the alternation text cache. + */ + eb_initialize_alt_caches(appendix); + + succeeded: + LOG(("out: eb_load_appendix_subbook() = %s", eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_load_appendix_subbook() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Load all subbooks in the book. + */ +EB_Error_Code +eb_load_all_appendix_subbooks(EB_Appendix *appendix) +{ + EB_Error_Code error_code; + EB_Subbook_Code current_subbook_code; + EB_Appendix_Subbook *subbook; + int i; + + eb_lock(&appendix->lock); + LOG(("in: eb_load_all_appendix_subbooks(appendix=%d)", + (int)appendix->code)); + + /* + * The appendix must have been bound. + */ + if (appendix->path == NULL) { + error_code = EB_ERR_UNBOUND_APP; + goto failed; + } + + /* + * Get the current subbook. + */ + if (appendix->subbook_current != NULL) + current_subbook_code = appendix->subbook_current->code; + else + current_subbook_code = -1; + + /* + * Initialize each subbook. + */ + for (i = 0, subbook = appendix->subbooks; + i < appendix->subbook_count; i++, subbook++) { + error_code = eb_set_appendix_subbook(appendix, subbook->code); + if (error_code != EB_SUCCESS) + goto failed; + } + + /* + * Restore the current subbook. + */ + if (current_subbook_code < 0) + eb_unset_appendix_subbook(appendix); + else { + error_code = eb_set_appendix_subbook(appendix, current_subbook_code); + if (error_code != EB_SUCCESS) + goto failed; + } + + LOG(("out: eb_load_all_appendix_subbooks() = %s", + eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_load_all_appendix_subbooks() = %s", + eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} + + +/* + * Get a subbook list in `appendix'. + */ +EB_Error_Code +eb_appendix_subbook_list(EB_Appendix *appendix, EB_Subbook_Code *subbook_list, + int *subbook_count) +{ + EB_Error_Code error_code; + EB_Subbook_Code *list_p; + int i; + + eb_lock(&appendix->lock); + LOG(("in: eb_appendix_subbook_list(appendix=%d)", (int)appendix->code)); + + /* + * Check for the current status. + */ + if (appendix->path == NULL) { + error_code = EB_ERR_UNBOUND_APP; + goto failed; + } + + /* + * Make a subbook list. + */ + for (i = 0, list_p = subbook_list; i < appendix->subbook_count; + i++, list_p++) + *list_p = i; + *subbook_count = appendix->subbook_count; + + LOG(("out: eb_appendix_subbook_list(subbook_count=%d) = %s", + *subbook_count, eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *subbook_count = 0; + LOG(("out: eb_appendix_subbook_list() = %s", eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} + + +/* + * Get the subbook-code of the current subbook in `appendix'. + */ +EB_Error_Code +eb_appendix_subbook(EB_Appendix *appendix, EB_Subbook_Code *subbook_code) +{ + EB_Error_Code error_code; + + eb_lock(&appendix->lock); + LOG(("in: eb_appendix_subbook(appendix=%d)", (int)appendix->code)); + + /* + * Check for the current status. + */ + if (appendix->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_APPSUB; + goto failed; + } + + /* + * Copy the current subbook code to `subbook_code'. + */ + *subbook_code = appendix->subbook_current->code; + + LOG(("out: eb_appendix_subbook(subbook=%d) = %s", (int)*subbook_code, + eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *subbook_code = EB_SUBBOOK_INVALID; + LOG(("out: eb_appendix_subbook() = %s", eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} + + +/* + * Get the directory name of the current subbook in `appendix'. + */ +EB_Error_Code +eb_appendix_subbook_directory(EB_Appendix *appendix, char *directory) +{ + EB_Error_Code error_code; + + eb_lock(&appendix->lock); + LOG(("in: eb_appendix_subbook_directory(appendix=%d)", + (int)appendix->code)); + + /* + * Check for the current status. + */ + if (appendix->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_APPSUB; + goto failed; + } + + /* + * Copy the directory name to `directory'. + */ + strcpy(directory, appendix->subbook_current->directory_name); + + LOG(("out: eb_appendix_subbook_directory(directory=%s) = %s", + directory, eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *directory = '\0'; + LOG(("out: eb_appendix_subbook_directory() = %s", + eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} + + +/* + * Get the directory name of the subbook `subbook_code' in `appendix'. + */ +EB_Error_Code +eb_appendix_subbook_directory2(EB_Appendix *appendix, + EB_Subbook_Code subbook_code, char *directory) +{ + EB_Error_Code error_code; + + eb_lock(&appendix->lock); + LOG(("in: eb_appendix_subbook_directory2(appendix=%d, subbook=%d)", + (int)appendix->code, (int)subbook_code)); + + /* + * Check for the current status. + */ + if (appendix->path == NULL) { + error_code = EB_ERR_UNBOUND_APP; + goto failed; + } + + /* + * Check for `subbook_code'. + */ + if (subbook_code < 0 || appendix->subbook_count <= subbook_code) { + error_code = EB_ERR_NO_SUCH_APPSUB; + goto failed; + } + + /* + * Copy the directory name to `directory'. + */ + strcpy(directory, (appendix->subbooks + subbook_code)->directory_name); + + LOG(("out: eb_appendix_subbook_directory2(directory=%s) = %s", + directory, eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *directory = '\0'; + LOG(("out: eb_appendix_subbook_directory2() = %s", + eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} + + +/* + * Set the subbook `subbook_code' as the current subbook. + */ +EB_Error_Code +eb_set_appendix_subbook(EB_Appendix *appendix, EB_Subbook_Code subbook_code) +{ + EB_Error_Code error_code; + + eb_lock(&appendix->lock); + LOG(("in: eb_set_appendix_subbook(appendix=%d, subbook=%d)", + (int)appendix->code, (int)subbook_code)); + + /* + * Check for the current status. + */ + if (appendix->path == NULL) { + error_code = EB_ERR_UNBOUND_APP; + goto failed; + } + + /* + * Check for `subbook_code'. + */ + if (subbook_code < 0 || appendix->subbook_count <= subbook_code) { + error_code = EB_ERR_NO_SUCH_APPSUB; + goto failed; + } + + /* + * If the current subbook is `subbook_code', return immediately. + * Otherwise close the current subbook and continue. + */ + if (appendix->subbook_current != NULL) { + if (appendix->subbook_current->code == subbook_code) + goto succeeded; + eb_unset_appendix_subbook(appendix); + } + + /* + * Disc type specific section. + */ + if (appendix->disc_code == EB_DISC_EB) + error_code = eb_set_appendix_subbook_eb(appendix, subbook_code); + else + error_code = eb_set_appendix_subbook_epwing(appendix, subbook_code); + + if (error_code != EB_SUCCESS) + goto failed; + + /* + * Load the subbook. + */ + error_code = eb_load_appendix_subbook(appendix); + if (error_code != EB_SUCCESS) + goto failed; + + succeeded: + LOG(("out: eb_set_appendix_subbook() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + if (appendix->subbook_current != NULL) + zio_close(&appendix->subbook_current->zio); + appendix->subbook_current = NULL; + LOG(("out: eb_set_appendix_subbook() = %s", eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} + + +/* + * EB* specific section of eb_set_appendix_subbook(). + */ +static EB_Error_Code +eb_set_appendix_subbook_eb(EB_Appendix *appendix, EB_Subbook_Code subbook_code) +{ + EB_Error_Code error_code; + EB_Appendix_Subbook *subbook; + char appendix_path_name[EB_MAX_PATH_LENGTH + 1]; + Zio_Code zio_code; + + LOG(("in: eb_set_appendix_subbook_eb(appendix=%d, subbook=%d)", + (int)appendix->code, (int)subbook_code)); + + /* + * Set the current subbook. + */ + appendix->subbook_current = appendix->subbooks + subbook_code; + subbook = appendix->subbook_current; + + /* + * Open an appendix file. + */ + if (eb_find_file_name2(appendix->path, subbook->directory_name, + EB_FILE_NAME_APPENDIX, subbook->file_name) != EB_SUCCESS) { + error_code = EB_ERR_FAIL_OPEN_APP; + goto failed; + } + + eb_compose_path_name2(appendix->path, subbook->directory_name, + subbook->file_name, appendix_path_name); + eb_path_name_zio_code(appendix_path_name, ZIO_PLAIN, &zio_code); + + if (zio_open(&subbook->zio, appendix_path_name, zio_code) < 0) { + error_code = EB_ERR_FAIL_OPEN_APP; + goto failed; + } + + LOG(("out: eb_set_appendix_subbook_eb() = %s", + eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_set_appendix_subbook_eb() = %s", + eb_error_string(error_code))); + return error_code; +} + + +/* + * EPWING specific section of eb_set_appendix_subbook(). + */ +static EB_Error_Code +eb_set_appendix_subbook_epwing(EB_Appendix *appendix, + EB_Subbook_Code subbook_code) +{ + EB_Error_Code error_code; + EB_Appendix_Subbook *subbook; + char appendix_path_name[EB_MAX_PATH_LENGTH + 1]; + Zio_Code zio_code; + + LOG(("in: eb_set_appendix_subbook_epwing(appendix=%d, subbook=%d)", + (int)appendix->code, (int)subbook_code)); + + /* + * Set the current subbook. + */ + appendix->subbook_current = appendix->subbooks + subbook_code; + subbook = appendix->subbook_current; + + zio_initialize(&subbook->zio); + + /* + * Adjust a directory name. + */ + strcpy(subbook->data_directory_name, EB_DIRECTORY_NAME_DATA); + eb_fix_directory_name2(appendix->path, subbook->directory_name, + subbook->data_directory_name); + + /* + * Open an appendix file. + */ + if (eb_find_file_name3(appendix->path, subbook->directory_name, + subbook->data_directory_name, EB_FILE_NAME_FUROKU, subbook->file_name) + != EB_SUCCESS) { + error_code = EB_ERR_FAIL_OPEN_APP; + goto failed; + } + + eb_compose_path_name3(appendix->path, subbook->directory_name, + subbook->data_directory_name, subbook->file_name, + appendix_path_name); + eb_path_name_zio_code(appendix_path_name, ZIO_PLAIN, &zio_code); + + if (zio_open(&subbook->zio, appendix_path_name, zio_code) < 0) { + subbook = NULL; + error_code = EB_ERR_FAIL_OPEN_APP; + goto failed; + } + + LOG(("out: eb_set_appendix_subbook_epwing() = %s", + eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_set_appendix_subbook_epwing() = %s", + eb_error_string(error_code))); + return error_code; +} + + +/* + * Unset the current subbook. + */ +void +eb_unset_appendix_subbook(EB_Appendix *appendix) +{ + eb_lock(&appendix->lock); + LOG(("in: eb_unset_appendix_subbook(appendix=%d)", (int)appendix->code)); + + /* + * Close a file for the current subbook. + */ + if (appendix->subbook_current != NULL) { + zio_close(&appendix->subbook_current->zio); + appendix->subbook_current = NULL; + } + + LOG(("out: eb_unset_appendix_subbook()")); + eb_unlock(&appendix->lock); +} diff --git a/bcd.c b/bcd.c new file mode 100644 index 0000000..48fc72b --- /dev/null +++ b/bcd.c @@ -0,0 +1,97 @@ +/* + * 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 "build-post.h" + +/* + * Get a BCD (binary coded decimal) packed integer with 2 bytes + * from an octet stream. + */ +unsigned +eb_bcd2(const char *stream) +{ + unsigned value; + const unsigned char *s = (const unsigned char *)stream; + + value = ((*(s ) >> 4) & 0x0f) * 1000; + value += ((*(s ) ) & 0x0f) * 100; + value += ((*(s + 1) >> 4) & 0x0f) * 10; + value += ((*(s + 1) ) & 0x0f); + + return value; +} + + +/* + * Get a BCD (binary coded decimal) packed integer with 4 bytes + * from an octet stream. + */ +unsigned +eb_bcd4(const char *stream) +{ + unsigned value; + const unsigned char *s = (const unsigned char *)stream; + + value = ((*(s ) >> 4) & 0x0f) * 10000000; + value += ((*(s ) ) & 0x0f) * 1000000; + value += ((*(s + 1) >> 4) & 0x0f) * 100000; + value += ((*(s + 1) ) & 0x0f) * 10000; + value += ((*(s + 2) >> 4) & 0x0f) * 1000; + value += ((*(s + 2) ) & 0x0f) * 100; + value += ((*(s + 3) >> 4) & 0x0f) * 10; + value += ((*(s + 3) ) & 0x0f); + + return value; +} + + +/* + * Get a BCD (binary coded decimal) packed integer with 6 bytes + * from an octet stream. + */ +unsigned +eb_bcd6(const char *stream) +{ + unsigned value; + const unsigned char *s = (const unsigned char *)stream; + + value = ((*(s + 1) ) & 0x0f); + value += ((*(s + 2) >> 4) & 0x0f) * 10; + value += ((*(s + 2) ) & 0x0f) * 100; + value += ((*(s + 3) >> 4) & 0x0f) * 1000; + value += ((*(s + 3) ) & 0x0f) * 10000; + value += ((*(s + 4) >> 4) & 0x0f) * 100000; + value += ((*(s + 4) ) & 0x0f) * 1000000; + value += ((*(s + 5) >> 4) & 0x0f) * 10000000; + value += ((*(s + 5) ) & 0x0f) * 100000000; + + return value; +} diff --git a/binary.c b/binary.c new file mode 100644 index 0000000..6ad9177 --- /dev/null +++ b/binary.c @@ -0,0 +1,1402 @@ +/* -*- C -*- + * Copyright (c) 2001-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 "binary.h" +#include "build-post.h" + +/* + * Unexported function. + */ +static EB_Error_Code eb_read_binary_generic(EB_Book *book, + size_t binary_max_length, char *binary, ssize_t *binary_length); +static EB_Error_Code eb_read_binary_wave(EB_Book *book, + size_t binary_max_length, char *binary, ssize_t *binary_length); +static EB_Error_Code eb_read_binary_mono_graphic(EB_Book *book, + size_t binary_max_length, char *binary, ssize_t *binary_length); +static EB_Error_Code eb_read_binary_gray_graphic(EB_Book *book, + size_t binary_max_length, char *binary, ssize_t *binary_length); + + +/* + * Initialize binary context of `book'. + */ +void +eb_initialize_binary_context(EB_Book *book) +{ + LOG(("in: eb_initialize_binary_context(book=%d)", (int)book->code)); + + book->binary_context.code = EB_BINARY_INVALID; + book->binary_context.zio = NULL; + book->binary_context.location = -1; + book->binary_context.size = 0; + book->binary_context.cache_length = 0; + book->binary_context.cache_offset = 0; + book->binary_context.width = 0; + + LOG(("out: eb_initialize_binary_context()")); +} + + +/* + * Finalize binary context of `book'. + */ +void +eb_finalize_binary_context(EB_Book *book) +{ + LOG(("in+out: eb_finalize_binary_context(book=%d)", (int)book->code)); + + /* nothing to be done */ +} + + +/* + * Reset binary context of `book'. + */ +void +eb_reset_binary_context(EB_Book *book) +{ + LOG(("in: eb_reset_binary_context(book=%d)", (int)book->code)); + + eb_initialize_binary_context(book); + + LOG(("out: eb_reset_binary_context()")); +} + + +/* + * Template of BMP preamble for 2 colors monochrome graphic. + */ +#define MONO_BMP_PREAMBLE_LENGTH 62 + +static const unsigned char mono_bmp_preamble[] = { + /* Type. */ + 'B', 'M', + + /* File size. (set at run time) */ + 0x00, 0x00, 0x00, 0x00, + + /* Reserved. */ + 0x00, 0x00, 0x00, 0x00, + + /* Offset of bitmap bits part. */ + 0x3e, 0x00, 0x00, 0x00, + + /* Size of bitmap info part. */ + 0x28, 0x00, 0x00, 0x00, + + /* Width. (set at run time) */ + 0x00, 0x00, 0x00, 0x00, + + /* Height. (set at run time) */ + 0x00, 0x00, 0x00, 0x00, + + /* Planes. */ + 0x01, 0x00, + + /* Bits per pixels. */ + 0x01, 0x00, + + /* Compression mode. */ + 0x00, 0x00, 0x00, 0x00, + + /* Size of bitmap bits part. (set at run time) */ + 0x00, 0x00, 0x00, 0x00, + + /* X Pixels per meter. */ + 0x6d, 0x0b, 0x00, 0x00, + + /* Y Pixels per meter. */ + 0x6d, 0x0b, 0x00, 0x00, + + /* Colors */ + 0x02, 0x00, 0x00, 0x00, + + /* Important colors */ + 0x02, 0x00, 0x00, 0x00, + + /* RGB quad of color 0 RGB quad of color 1 */ + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +/* + * Set monochrome bitmap picture as the current binary data. + */ +EB_Error_Code +eb_set_binary_mono_graphic(EB_Book *book, const EB_Position *position, + int width, int height) +{ + EB_Error_Code error_code; + EB_Binary_Context *context; + EB_Position real_position; + unsigned char *buffer_p; + size_t line_pad_length; + size_t data_size; + size_t file_size; + + eb_lock(&book->lock); + LOG(("in: eb_set_binary_mono_graphic(book=%d, position={%d,%d}, \ +width=%d, height=%d)", + (int)book->code, position->page, position->offset, width, height)); + + eb_reset_binary_context(book); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Current subbook must have a graphic file. + */ + if (zio_file(&book->subbook_current->text_zio) < 0) { + error_code = EB_ERR_NO_SUCH_BINARY; + goto failed; + } + + /* + * If both width and height are 0, + * we get real width, height and position of the graphic data. + */ + if (position->page <= 0 || position->offset < 0) { + error_code = EB_ERR_FAIL_SEEK_BINARY; + goto failed; + } + + if (width == 0 && height == 0) { + char buffer[22]; + + if (zio_lseek(&book->subbook_current->text_zio, + ((off_t) position->page - 1) * EB_SIZE_PAGE + position->offset, + SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_BINARY; + goto failed; + } + + if (zio_read(&book->subbook_current->text_zio, buffer, 22) != 22) { + error_code = EB_ERR_FAIL_READ_BINARY; + goto failed; + } + if (eb_uint2(buffer) != 0x1f45 || eb_uint2(buffer + 4) != 0x1f31) { + error_code = EB_ERR_UNEXP_BINARY; + goto failed; + } + width = eb_bcd2(buffer + 8); + height = eb_bcd2(buffer + 10); + + if (eb_uint2(buffer + 12) == 0x1f51) { + real_position.page = eb_bcd4(buffer + 14); + real_position.offset = eb_bcd2(buffer + 18); + } else if (eb_uint2(buffer + 14) == 0x1f51) { + real_position.page = eb_bcd4(buffer + 16); + real_position.offset = eb_bcd2(buffer + 20); + } else { + error_code = EB_ERR_UNEXP_BINARY; + goto failed; + } + + position = &real_position; + } + + if (width <= 0 || height <= 0) { + error_code = EB_ERR_NO_SUCH_BINARY; + goto failed; + } + + /* + * BMP requires that the number of bytes in a line must be multiple + * of 4. If not, 0x00 must be padded to end of each line. + * `line_pad_length' (0...3) is the number of bytes to be padded. + * + * In case of EB_BINARY_MONO_GRAPHIC, a pixel is represented with + * a bit. + */ + if (width % 32 == 0) + line_pad_length = 0; + else if (width % 32 <= 8) + line_pad_length = 3; + else if (width % 32 <= 16) + line_pad_length = 2; + else if (width % 32 <= 24) + line_pad_length = 1; + else + line_pad_length = 0; + + data_size = (width / 8 + line_pad_length) * height; + file_size = data_size + MONO_BMP_PREAMBLE_LENGTH; + + /* + * Set binary context. + */ + context = &book->binary_context; + context->code = EB_BINARY_MONO_GRAPHIC; + context->zio = &book->subbook_current->text_zio; + context->location = ((off_t) position->page - 1) * EB_SIZE_PAGE + + position->offset + (width + 7) / 8 * (height - 1); + context->size = (width + 7) / 8 * height; + context->offset = 0; + context->cache_offset = 0; + context->width = width; + + /* + * Set BMP preamble. + */ + context->cache_length = MONO_BMP_PREAMBLE_LENGTH; + memcpy(context->cache_buffer, mono_bmp_preamble, MONO_BMP_PREAMBLE_LENGTH); + + buffer_p = (unsigned char *)context->cache_buffer + 2; + *buffer_p++ = file_size & 0xff; + *buffer_p++ = (file_size >> 8) & 0xff; + *buffer_p++ = (file_size >> 16) & 0xff; + *buffer_p++ = (file_size >> 24) & 0xff; + + buffer_p = (unsigned char *)context->cache_buffer + 18; + *buffer_p++ = width & 0xff; + *buffer_p++ = (width >> 8) & 0xff; + *buffer_p++ = (width >> 16) & 0xff; + *buffer_p++ = (width >> 24) & 0xff; + + *buffer_p++ = height & 0xff; + *buffer_p++ = (height >> 8) & 0xff; + *buffer_p++ = (height >> 16) & 0xff; + *buffer_p++ = (height >> 24) & 0xff; + + buffer_p = (unsigned char *)context->cache_buffer + 34; + *buffer_p++ = data_size & 0xff; + *buffer_p++ = (data_size >> 8) & 0xff; + *buffer_p++ = (data_size >> 16) & 0xff; + *buffer_p++ = (data_size >> 24) & 0xff; + + /* + * Seek graphic file. + */ + if (zio_lseek(context->zio, context->location, SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_BINARY; + goto failed; + } + + LOG(("out: eb_set_binary_mono_graphic() = %s", + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_reset_binary_context(book); + LOG(("out: eb_set_binary_mono_graphic() = %s", + eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Template of BMP preamble for gray scale graphic. + */ +#define GRAY_BMP_PREAMBLE_LENGTH 118 + +static const unsigned char gray_bmp_preamble[] = { + /* Type. */ + 'B', 'M', + + /* File size. (set at run time) */ + 0x00, 0x00, 0x00, 0x00, + + /* Reserved. */ + 0x00, 0x00, 0x00, 0x00, + + /* Offset of bitmap bits part. */ + 0x3e, 0x00, 0x00, 0x00, + + /* Size of bitmap info part. */ + 0x28, 0x00, 0x00, 0x00, + + /* Width. (set at run time) */ + 0x00, 0x00, 0x00, 0x00, + + /* Height. (set at run time) */ + 0x00, 0x00, 0x00, 0x00, + + /* Planes. */ + 0x01, 0x00, + + /* Bits per pixels. */ + 0x04, 0x00, + + /* Compression mode. */ + 0x00, 0x00, 0x00, 0x00, + + /* Size of bitmap bits part. (set at run time) */ + 0x00, 0x00, 0x00, 0x00, + + /* X Pixels per meter. */ + 0x6d, 0x0b, 0x00, 0x00, + + /* Y Pixels per meter. */ + 0x6d, 0x0b, 0x00, 0x00, + + /* Colors */ + 0x10, 0x00, 0x00, 0x00, + + /* Important colors */ + 0x10, 0x00, 0x00, 0x00, + + /* RGB quad of color 0x0 RGB quad of color 0x1 */ + 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x00, + + /* RGB quad of color 0x2 RGB quad of color 0x3 */ + 0x22, 0x22, 0x22, 0x00, 0x33, 0x33, 0x33, 0x00, + + /* RGB quad of color 0x4 RGB quad of color 0x5 */ + 0x44, 0x44, 0x44, 0x00, 0x55, 0x55, 0x55, 0x00, + + /* RGB quad of color 0x6 RGB quad of color 0x7 */ + 0x66, 0x66, 0x66, 0x00, 0x77, 0x77, 0x77, 0x00, + + /* RGB quad of color 0x8 RGB quad of color 0x9 */ + 0x88, 0x88, 0x88, 0x00, 0x99, 0x99, 0x99, 0x00, + + /* RGB quad of color 0xa RGB quad of color 0xb */ + 0xaa, 0xaa, 0xaa, 0x00, 0xbb, 0xbb, 0xbb, 0x00, + + /* RGB quad of color 0xc RGB quad of color 0xd */ + 0xcc, 0xcc, 0xcc, 0x00, 0xdd, 0xdd, 0xdd, 0x00, + + /* RGB quad of color 0xe RGB quad of color 0xf */ + 0xee, 0xee, 0xee, 0x00, 0xff, 0xff, 0xff, 0x00, +}; + +/* + * Set monochrome bitmap picture as the current binary data. + */ +EB_Error_Code +eb_set_binary_gray_graphic(EB_Book *book, const EB_Position *position, + int width, int height) +{ + EB_Error_Code error_code; + EB_Binary_Context *context; + EB_Position real_position; + unsigned char *buffer_p; + size_t line_pad_length; + size_t data_size; + size_t file_size; + + eb_lock(&book->lock); + LOG(("in: eb_set_binary_gray_graphic(book=%d, position={%d,%d}, \ +width=%d, height=%d)", + (int)book->code, position->page, position->offset, width, height)); + + eb_reset_binary_context(book); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Current subbook must have a graphic file. + */ + if (zio_file(&book->subbook_current->text_zio) < 0) { + error_code = EB_ERR_NO_SUCH_BINARY; + goto failed; + } + + /* + * If both width and height are 0, + * we get real width, height and position of the graphic data. + */ + if (position->page <= 0 || position->offset < 0) { + error_code = EB_ERR_FAIL_SEEK_BINARY; + goto failed; + } + + if (width == 0 && height == 0) { + char buffer[22]; + + if (zio_lseek(&book->subbook_current->text_zio, + ((off_t) position->page - 1) * EB_SIZE_PAGE + position->offset, + SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_BINARY; + goto failed; + } + + if (zio_read(&book->subbook_current->text_zio, buffer, 22) != 22) { + error_code = EB_ERR_FAIL_READ_BINARY; + goto failed; + } + if (eb_uint2(buffer) != 0x1f45 + || eb_uint2(buffer + 4) != 0x1f31 + || eb_uint2(buffer + 12) != 0x1f51 + || eb_uint2(buffer + 20) != 0x1f65) { + error_code = EB_ERR_UNEXP_BINARY; + goto failed; + } + + width = eb_bcd2(buffer + 8); + height = eb_bcd2(buffer + 10); + real_position.page = eb_bcd4(buffer + 14); + real_position.offset = eb_bcd2(buffer + 18); + position = &real_position; + } + + if (width <= 0 || height <= 0) { + error_code = EB_ERR_NO_SUCH_BINARY; + goto failed; + } + + /* + * BMP requires that the number of bytes in a line must be multiple + * of 4. If not, 0x00 must be padded to end of each line. + * `line_pad_length' (0...3) is the number of bytes to be padded. + * + * In case of EB_BINARY_GRAY_GRAPHIC, a pixel is represented with + * 4 bits. + */ + if (width % 8 == 0) + line_pad_length = 0; + else if (width % 8 <= 2) + line_pad_length = 3; + else if (width % 8 <= 4) + line_pad_length = 2; + else if (width % 8 <= 6) + line_pad_length = 1; + else + line_pad_length = 0; + + data_size = (width / 2 + line_pad_length) * height; + file_size = data_size + MONO_BMP_PREAMBLE_LENGTH; + + /* + * Set binary context. + */ + context = &book->binary_context; + + context->code = EB_BINARY_GRAY_GRAPHIC; + context->zio = &book->subbook_current->text_zio; + context->location = ((off_t) position->page - 1) * EB_SIZE_PAGE + + position->offset + (width + 1) / 2 * (height - 1); + context->size = (width + 1) / 2 * height; + context->offset = 0; + context->cache_offset = 0; + context->width = width; + + /* + * Set BMP preamble. + */ + context->cache_length = GRAY_BMP_PREAMBLE_LENGTH; + memcpy(context->cache_buffer, gray_bmp_preamble, + GRAY_BMP_PREAMBLE_LENGTH); + + buffer_p = (unsigned char *)context->cache_buffer + 2; + *buffer_p++ = file_size & 0xff; + *buffer_p++ = (file_size >> 8) & 0xff; + *buffer_p++ = (file_size >> 16) & 0xff; + *buffer_p++ = (file_size >> 24) & 0xff; + + buffer_p = (unsigned char *)context->cache_buffer + 18; + *buffer_p++ = width & 0xff; + *buffer_p++ = (width >> 8) & 0xff; + *buffer_p++ = (width >> 16) & 0xff; + *buffer_p++ = (width >> 24) & 0xff; + + *buffer_p++ = height & 0xff; + *buffer_p++ = (height >> 8) & 0xff; + *buffer_p++ = (height >> 16) & 0xff; + *buffer_p++ = (height >> 24) & 0xff; + + buffer_p = (unsigned char *)context->cache_buffer + 34; + *buffer_p++ = data_size & 0xff; + *buffer_p++ = (data_size >> 8) & 0xff; + *buffer_p++ = (data_size >> 16) & 0xff; + *buffer_p++ = (data_size >> 24) & 0xff; + + /* + * Seek graphic file. + */ + if (zio_lseek(context->zio, context->location, SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_BINARY; + goto failed; + } + + LOG(("out: eb_set_binary_gray_graphic() = %s", + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_reset_binary_context(book); + LOG(("out: eb_set_binary_gray_graphic() = %s", + eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Set WAVE sound as the current binary data. + */ +EB_Error_Code +eb_set_binary_wave(EB_Book *book, const EB_Position *start_position, + const EB_Position *end_position) +{ + EB_Error_Code error_code; + EB_Binary_Context *context; + off_t start_location; + off_t end_location; + char temporary_buffer[4]; + + eb_lock(&book->lock); + LOG(("in: eb_set_binary_wave(book=%d, start_position={%d,%d}, \ +end_position={%d,%d})", + (int)book->code, start_position->page, start_position->offset, + end_position->page, end_position->offset)); + + eb_reset_binary_context(book); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Current subbook must have a sound file. + */ + if (zio_file(&book->subbook_current->sound_zio) < 0) { + error_code = EB_ERR_NO_SUCH_BINARY; + goto failed; + } + + /* + * Set binary context. + */ + if (start_position->page <= 0 || start_position->offset < 0) { + error_code = EB_ERR_FAIL_SEEK_BINARY; + goto failed; + } + if (end_position->page <= 0 || end_position->offset < 0) { + error_code = EB_ERR_FAIL_SEEK_BINARY; + goto failed; + } + + start_location = ((off_t) start_position->page - 1) * EB_SIZE_PAGE + + start_position->offset; + end_location = ((off_t) end_position->page - 1) * EB_SIZE_PAGE + + end_position->offset; + + context = &book->binary_context; + + context->code = EB_BINARY_WAVE; + context->zio = &book->subbook_current->sound_zio; + context->location = start_location; + if (start_location < end_location) + context->size = end_location - start_location + 1; + else { + error_code = EB_ERR_UNEXP_BINARY; + goto failed; + } + context->offset = 0; + + /* + * Read 4bytes from the sound file to check whether the sound + * data contains a header part or not. + * + * If the read data is "fmt ", the wave data has a header part. + * Otherwise, we must read a header in another location. + * + * The wave data consists of: + * + * "RIFF" wave-size(4bytes) "WAVE" header-fragment(28bytes) + * data-part-size(4bytes) data + * + * wave-size = "WAVE" + header-fragment + data-part-size + data + * = 4 + 28 + 4 + data + * = 36 + data + * data-part-size = length(data) + */ + if (zio_lseek(context->zio, context->location, SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_BINARY; + goto failed; + } + if (zio_read(context->zio, temporary_buffer, 4) != 4) { + error_code = EB_ERR_FAIL_READ_BINARY; + goto failed; + } + + if (memcmp(temporary_buffer, "fmt ", 4) == 0) { + memcpy(context->cache_buffer + 12, temporary_buffer, 4); + if (zio_read(context->zio, context->cache_buffer + 16, 28) != 28) { + error_code = EB_ERR_FAIL_READ_BINARY; + goto failed; + } + if (context->size >= 32) + context->size -= 32; + else + context->size = 0; + } else { + if (zio_lseek(context->zio, + ((off_t) book->subbook_current->sound.start_page - 1) + * EB_SIZE_PAGE + 32, SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_BINARY; + goto failed; + } + if (zio_read(context->zio, context->cache_buffer + 12, 28) != 28) { + error_code = EB_ERR_FAIL_SEEK_BINARY; + goto failed; + } + + *(unsigned char *)(context->cache_buffer + 40) + = (context->size) & 0xff; + *(unsigned char *)(context->cache_buffer + 41) + = (context->size >> 8) & 0xff; + *(unsigned char *)(context->cache_buffer + 42) + = (context->size >> 16) & 0xff; + *(unsigned char *)(context->cache_buffer + 43) + = (context->size >> 24) & 0xff; + + + /* + * Seek sound file, again. + */ + if (zio_lseek(context->zio, context->location, SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_BINARY; + goto failed; + } + } + context->cache_length = 44; + + /* + * Read and compose a WAVE header. + */ + memcpy(context->cache_buffer, "RIFF", 4); + + *(unsigned char *)(context->cache_buffer + 4) + = (context->size + 36) & 0xff; + *(unsigned char *)(context->cache_buffer + 5) + = ((context->size + 36) >> 8) & 0xff; + *(unsigned char *)(context->cache_buffer + 6) + = ((context->size + 36) >> 16) & 0xff; + *(unsigned char *)(context->cache_buffer + 7) + = ((context->size + 36) >> 24) & 0xff; + + memcpy(context->cache_buffer + 8, "WAVE", 4); + + LOG(("out: eb_set_binary_wave() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_reset_binary_context(book); + LOG(("out: eb_set_binary_wave() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Length of the color graphic header. + */ +#define EB_COLOR_GRAPHIC_HEADER_LENGTH 8 + +/* + * Set color graphic (BMP or JPEG) as the current binary data. + */ +EB_Error_Code +eb_set_binary_color_graphic(EB_Book *book, const EB_Position *position) +{ + EB_Error_Code error_code; + EB_Binary_Context *context; + char buffer[EB_COLOR_GRAPHIC_HEADER_LENGTH]; + + eb_lock(&book->lock); + LOG(("in: eb_set_binary_color_graphic(book=%d, position={%d,%d})", + (int)book->code, position->page, position->offset)); + + eb_reset_binary_context(book); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Current subbook must have a graphic file. + */ + if (zio_file(&book->subbook_current->graphic_zio) < 0) { + error_code = EB_ERR_NO_SUCH_BINARY; + goto failed; + } + + /* + * Set binary context. + */ + if (position->page <= 0 || position->offset < 0) { + error_code = EB_ERR_FAIL_SEEK_BINARY; + goto failed; + } + + context = &book->binary_context; + context->code = EB_BINARY_COLOR_GRAPHIC; + context->zio = &book->subbook_current->graphic_zio; + context->location = ((off_t) position->page - 1) * EB_SIZE_PAGE + + position->offset; + context->offset = 0; + context->cache_length = 0; + context->cache_offset = 0; + + /* + * Seek graphic file. + */ + if (zio_lseek(context->zio, context->location, SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_BINARY; + goto failed; + } + + /* + * Read header of the graphic data. + * Note that EB* JPEG file lacks the header. + */ + if (zio_read(context->zio, buffer, EB_COLOR_GRAPHIC_HEADER_LENGTH) + != EB_COLOR_GRAPHIC_HEADER_LENGTH) { + error_code = EB_ERR_FAIL_READ_BINARY; + goto failed; + } + + if (memcmp(buffer, "data", 4) == 0) { + context->size = eb_uint4_le(buffer + 4); + context->location += EB_COLOR_GRAPHIC_HEADER_LENGTH; + } else { + context->size = 0; + if (zio_lseek(context->zio, context->location, SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_BINARY; + goto failed; + } + } + + LOG(("out: eb_set_binary_color_graphic() = %s", + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_reset_binary_context(book); + LOG(("out: eb_set_binary_color_graphic() = %s", + eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Set MPEG movie as the current binary data. + */ +EB_Error_Code +eb_set_binary_mpeg(EB_Book *book, const unsigned int *argv) +{ + /* + * `movie_file_name' is base name, and `movie_path_name' is absolute + * path of the movie. + */ + char movie_file_name[EB_MAX_FILE_NAME_LENGTH + 1]; + char movie_path_name[EB_MAX_PATH_LENGTH + 1]; + EB_Error_Code error_code; + EB_Subbook *subbook; + Zio_Code zio_code; + + eb_lock(&book->lock); + LOG(("in: eb_set_binary_mpeg(book=%d)", (int)book->code)); + + eb_reset_binary_context(book); + + /* + * Current subbook must have been set. + */ + subbook = book->subbook_current; + if (subbook == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Open the movie file and set binary context. + */ + if (eb_compose_movie_file_name(argv, movie_file_name) != EB_SUCCESS) { + error_code = EB_ERR_NO_SUCH_BINARY; + goto failed; + } + LOG(("aux: eb_set_binary_mpeg(): movie_file_name=%s", movie_file_name)); + + if (eb_find_file_name3(book->path, subbook->directory_name, + subbook->movie_directory_name, movie_file_name, movie_file_name) + != EB_SUCCESS) { + error_code = EB_ERR_NO_SUCH_BINARY; + goto failed; + } + eb_compose_path_name3(book->path, subbook->directory_name, + subbook->movie_directory_name, movie_file_name, movie_path_name); + eb_path_name_zio_code(movie_path_name, ZIO_PLAIN, &zio_code); + + if (zio_open(&subbook->movie_zio, movie_path_name, zio_code) < 0) { + subbook = NULL; + error_code = EB_ERR_FAIL_OPEN_BINARY; + goto failed; + } + + book->binary_context.code = EB_BINARY_MPEG; + book->binary_context.zio = &book->subbook_current->movie_zio; + book->binary_context.location = 0; + book->binary_context.size = 0; + book->binary_context.offset = 0; + book->binary_context.cache_length = 0; + book->binary_context.cache_offset = 0; + + LOG(("out: eb_set_binary_mpeg() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_reset_binary_context(book); + LOG(("out: eb_set_binary_mpeg() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Read binary data. + */ +EB_Error_Code +eb_read_binary(book, binary_max_length, binary, binary_length) + EB_Book *book; + size_t binary_max_length; + char *binary; + ssize_t *binary_length; + +{ + EB_Error_Code error_code; + + eb_lock(&book->lock); + LOG(("in: eb_read_binary(book=%d, binary_max_length=%ld)", + (int)book->code, (long)binary_max_length)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Return immediately if `binary_max_length' is 0. + */ + *binary_length = 0; + + switch (book->binary_context.code) { + case EB_BINARY_COLOR_GRAPHIC: + case EB_BINARY_MPEG: + error_code = eb_read_binary_generic(book, binary_max_length, binary, + binary_length); + break; + case EB_BINARY_WAVE: + error_code = eb_read_binary_wave(book, binary_max_length, + binary, binary_length); + break; + case EB_BINARY_MONO_GRAPHIC: + error_code = eb_read_binary_mono_graphic(book, binary_max_length, + binary, binary_length); + break; + case EB_BINARY_GRAY_GRAPHIC: + error_code = eb_read_binary_gray_graphic(book, binary_max_length, + binary, binary_length); + break; + default: + error_code = EB_ERR_NO_CUR_BINARY; + goto failed; + } + if (error_code != EB_SUCCESS) + goto failed; + + LOG(("out: eb_read_binary(binary_length=%ld) = %s", (long)*binary_length, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *binary_length = -1; + eb_reset_binary_context(book); + LOG(("out: eb_read_binary() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Read generic binary data. + * This function is used for reading JPEG or BMP picture, and data part + * of WAVE sound. + */ +static EB_Error_Code +eb_read_binary_generic(EB_Book *book, size_t binary_max_length, + char *binary, ssize_t *binary_length) +{ + EB_Error_Code error_code; + EB_Binary_Context *context; + char *binary_p = binary; + size_t read_length = 0; + ssize_t read_result; + + LOG(("in: eb_read_binary_generic(book=%d, binary_max_length=%ld)", + (int)book->code, (long)binary_max_length)); + + *binary_length = 0; + context = &book->binary_context; + + /* + * Return immediately if `binary_max_length' is 0. + */ + if (binary_max_length == 0) + goto succeeded; + + /* + * Read binary data if it is remained. + * If context->size is 0, the binary data size is unknown. + */ + if (0 < context->size && context->size <= context->offset) + goto succeeded; + + if (context->size == 0) + read_length = binary_max_length - *binary_length; + else if (binary_max_length - *binary_length + < context->size - context->offset) + read_length = binary_max_length - *binary_length; + else + read_length = context->size - context->offset; + + read_result = zio_read(context->zio, binary_p, read_length); + if ((0 < context->size && read_result != read_length) || read_result < 0) { + error_code = EB_ERR_FAIL_READ_BINARY; + goto failed; + } + + *binary_length += read_result; + context->offset += read_result; + + succeeded: + LOG(("out: eb_read_binary_generic(binary_length=%ld) = %s", + (long)*binary_length, eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_read_binary_generic() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Read WAVE sound data. + */ +static EB_Error_Code +eb_read_binary_wave(EB_Book *book, size_t binary_max_length, char *binary, + ssize_t *binary_length) +{ + EB_Error_Code error_code; + EB_Binary_Context *context; + char *binary_p = binary; + size_t copy_length = 0; + + LOG(("in: eb_read_binary_wave(book=%d, binary_max_length=%ld)", + (int)book->code, (long)binary_max_length)); + + *binary_length = 0; + context = &book->binary_context; + + /* + * Return immediately if `binary_max_length' is 0. + */ + if (binary_max_length == 0) + goto succeeded; + + /* + * Copy cached data (header part) to `binary' if exists. + */ + if (0 < context->cache_length) { + if (binary_max_length < context->cache_length - context->cache_offset) + copy_length = binary_max_length; + else + copy_length = context->cache_length - context->cache_offset; + + memcpy(binary_p, context->cache_buffer + context->cache_offset, + copy_length); + binary_p += copy_length; + context->cache_offset += copy_length; + + if (context->cache_length <= context->cache_offset) + context->cache_length = 0; + + if (binary_max_length <= *binary_length) + goto succeeded; + } + + error_code = eb_read_binary_generic(book, binary_max_length - copy_length, + binary_p, binary_length); + if (error_code !=EB_SUCCESS) + goto failed; + *binary_length += copy_length; + + succeeded: + LOG(("out: eb_read_binary_wave(binary_length=%ld) = %s", + (long)*binary_length, eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_read_binary_wave() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Read monochrome graphic data. + * The function also convert the graphic data to BMP. + */ +static EB_Error_Code +eb_read_binary_mono_graphic(EB_Book *book, size_t binary_max_length, + char *binary, ssize_t *binary_length) +{ + EB_Error_Code error_code; + EB_Binary_Context *context; + unsigned char *binary_p = (unsigned char *)binary; + size_t copy_length = 0; + size_t read_length = 0; + size_t line_length; + size_t line_pad_length; + + LOG(("in: eb_read_binary_mono_graphic(book=%d, binary_max_length=%ld)", + (int)book->code, (long)binary_max_length)); + + *binary_length = 0; + context = &book->binary_context; + + line_length = (context->width + 7) / 8; + + if (context->width % 32 == 0) + line_pad_length = 0; + else if (context->width % 32 <= 8) + line_pad_length = 3; + else if (context->width % 32 <= 16) + line_pad_length = 2; + else if (context->width % 32 <= 24) + line_pad_length = 1; + else + line_pad_length = 0; + + /* + * Return immediately if `binary_max_length' is 0. + */ + if (binary_max_length == 0) + goto succeeded; + + for (;;) { + /* + * Copy cached data to `binary' if exists. + */ + if (0 < context->cache_length) { + if (binary_max_length - *binary_length + < context->cache_length - context->cache_offset) + copy_length = binary_max_length - *binary_length; + else + copy_length = context->cache_length - context->cache_offset; + + memcpy(binary_p, context->cache_buffer + context->cache_offset, + copy_length); + binary_p += copy_length; + *binary_length += copy_length; + context->cache_offset += copy_length; + + if (context->cache_length <= context->cache_offset) + context->cache_length = 0; + + if (binary_max_length <= *binary_length) + goto succeeded; + } + + /* + * Read binary data if it is remained. + * If padding is needed, read each line. + */ + read_length = line_length - context->offset % line_length; + if (context->size - context->offset < read_length) + read_length = context->size - context->offset; + if (binary_max_length - *binary_length < read_length) + read_length = binary_max_length - *binary_length; + if (read_length == 0) + goto succeeded; + + /* + * Read binary data. + */ + if (context->offset != 0 + && context->offset % line_length == 0 + && zio_lseek(context->zio, (off_t) line_length * -2, SEEK_CUR) + < 0) { + error_code = EB_ERR_FAIL_SEEK_BINARY; + goto failed; + } + if (zio_read(context->zio, (char *)binary_p, read_length) + != read_length) { + error_code = EB_ERR_FAIL_READ_BINARY; + goto failed; + } + + *binary_length += read_length; + context->offset += read_length; + binary_p += read_length; + + /* + * Pad 0x00 to BMP if needed. + */ + if (context->offset % line_length == 0) { + if (0 < line_pad_length) { + if (binary_max_length - *binary_length < line_pad_length) { + memset(context->cache_buffer, 0, line_pad_length); + context->cache_length = line_pad_length; + context->cache_offset = 0; + } else { + memset(binary_p, 0, line_pad_length); + binary_p += line_pad_length; + *binary_length += line_pad_length; + } + } + } + } + + succeeded: + LOG(("out: eb_read_binary_mono_graphic(binary_length=%ld) = %s", + (long)*binary_length, eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_read_binary_mono_graphic() = %s", + eb_error_string(error_code))); + return error_code; +} + + +/* + * Read gray scale graphic data. + * The function also convert the graphic data to BMP. + */ +static EB_Error_Code +eb_read_binary_gray_graphic(EB_Book *book, size_t binary_max_length, + char *binary, ssize_t *binary_length) +{ + EB_Error_Code error_code; + EB_Binary_Context *context; + unsigned char *binary_p = (unsigned char *)binary; + size_t copy_length = 0; + size_t read_length = 0; + size_t line_length; + size_t line_pad_length; + + LOG(("in: eb_read_binary_gray_graphic(book=%d, binary_max_length=%ld)", + (int)book->code, (long)binary_max_length)); + + *binary_length = 0; + context = &book->binary_context; + + line_length = (context->width + 1) / 2; + + if (context->width % 8 == 0) + line_pad_length = 0; + else if (context->width % 8 <= 2) + line_pad_length = 3; + else if (context->width % 8 <= 4) + line_pad_length = 2; + else if (context->width % 8 <= 6) + line_pad_length = 1; + else + line_pad_length = 0; + + /* + * Return immediately if `binary_max_length' is 0. + */ + if (binary_max_length == 0) + goto succeeded; + + for (;;) { + /* + * Copy cached data to `binary' if exists. + */ + if (0 < context->cache_length) { + if (binary_max_length - *binary_length + < context->cache_length - context->cache_offset) + copy_length = binary_max_length - *binary_length; + else + copy_length = context->cache_length - context->cache_offset; + + memcpy(binary_p, context->cache_buffer + context->cache_offset, + copy_length); + binary_p += copy_length; + *binary_length += copy_length; + context->cache_offset += copy_length; + + if (context->cache_length <= context->cache_offset) + context->cache_length = 0; + + if (binary_max_length <= *binary_length) + goto succeeded; + } + + /* + * Read binary data if it is remained. + * If padding is needed, read each line. + */ + read_length = line_length - context->offset % line_length; + if (context->size - context->offset < read_length) + read_length = context->size - context->offset; + if (binary_max_length - *binary_length < read_length) + read_length = binary_max_length - *binary_length; + if (read_length == 0) + goto succeeded; + + /* + * Read binary data. + */ + if (context->offset != 0 + && context->offset % line_length == 0 + && zio_lseek(context->zio, (off_t) line_length * -2, SEEK_CUR) + < 0) { + error_code = EB_ERR_FAIL_SEEK_BINARY; + goto failed; + } + if (zio_read(context->zio, (char *)binary_p, read_length) + != read_length) { + error_code = EB_ERR_FAIL_READ_BINARY; + goto failed; + } + + *binary_length += read_length; + context->offset += read_length; + binary_p += read_length; + + /* + * Pad 0x00 to BMP if needed. + */ + if (context->offset % line_length == 0) { + if (0 < line_pad_length) { + if (binary_max_length - *binary_length < line_pad_length) { + memset(context->cache_buffer, 0, line_pad_length); + context->cache_length = line_pad_length; + context->cache_offset = 0; + } else { + memset(binary_p, 0, line_pad_length); + binary_p += line_pad_length; + *binary_length += line_pad_length; + } + } + } + } + + succeeded: + LOG(("out: eb_read_binary_gray_graphic(binary_length=%ld) = %s", + (long)*binary_length, eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_read_binary_gray_graphic() = %s", + eb_error_string(error_code))); + return error_code; +} + + +/* + * Unset current binary. + */ +void +eb_unset_binary(EB_Book *book) +{ + eb_lock(&book->lock); + LOG(("in: eb_unset_binary(book=%d)", (int)book->code)); + + eb_reset_binary_context(book); + + LOG(("out: eb_unset_binary()")); + eb_unlock(&book->lock); +} diff --git a/binary.h b/binary.h new file mode 100644 index 0000000..a8c7de8 --- /dev/null +++ b/binary.h @@ -0,0 +1,69 @@ +/* -*- C -*- + * Copyright (c) 2001-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. + */ + +#ifndef EB_BINARY_H +#define EB_BINARY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "defs.h" + +/* + * Function declarations. + */ +/* binary.c */ +EB_Error_Code eb_set_binary_mono_graphic(EB_Book *book, + const EB_Position *position, int width, int height); +EB_Error_Code eb_set_binary_gray_graphic(EB_Book *book, + const EB_Position *position, int width, int height); +EB_Error_Code eb_set_binary_wave(EB_Book *book, + const EB_Position *start_position, const EB_Position *end_position); +EB_Error_Code eb_set_binary_color_graphic(EB_Book *book, + const EB_Position *position); +EB_Error_Code eb_set_binary_mpeg(EB_Book *book, const unsigned int *argv); +EB_Error_Code eb_read_binary(EB_Book *book, size_t binary_max_length, + char *binary, ssize_t *binary_length); +void eb_unset_binary(EB_Book *book); + +/* filename.c */ +EB_Error_Code eb_compose_movie_file_name(const unsigned int *argv, + char *composed_file_name); +EB_Error_Code eb_compose_movie_path_name(EB_Book *book, + const unsigned int *argv, char *composed_path_name); +EB_Error_Code eb_decompose_movie_file_name(unsigned int *argv, + const char *composed_file_name); + +#ifdef __cplusplus +} +#endif + +#endif /* not EB_BINARY_H */ diff --git a/bitmap.c b/bitmap.c new file mode 100644 index 0000000..508dd41 --- /dev/null +++ b/bitmap.c @@ -0,0 +1,1362 @@ +/* + * 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" + +#include + +/* + * Unexported functions. + */ +static unsigned long png_crc(const char *buf, size_t len); +static int png_compress(const char *src, int width, int height, char *dest, + size_t *dest_len); + + +/* + * Return required buffer size for a narrow font character converted + * to XBM image format. + */ +EB_Error_Code +eb_narrow_font_xbm_size(EB_Font_Code height, size_t *size) +{ + EB_Error_Code error_code; + + LOG(("in: eb_narrow_font_xbm_size(height=%d)", (int)height)); + + switch (height) { + case EB_FONT_16: + *size = EB_SIZE_NARROW_FONT_16_XBM; + break; + case EB_FONT_24: + *size = EB_SIZE_NARROW_FONT_24_XBM; + break; + case EB_FONT_30: + *size = EB_SIZE_NARROW_FONT_30_XBM; + break; + case EB_FONT_48: + *size = EB_SIZE_NARROW_FONT_48_XBM; + break; + default: + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + LOG(("out: eb_narrow_font_xbm_size(size=%ld) = %s", (long)*size, + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *size = 0; + LOG(("out: eb_narrow_font_xbm_size() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Return required buffer size for a narrow font character converted + * to XPM image format. + */ +EB_Error_Code +eb_narrow_font_xpm_size(EB_Font_Code height, size_t *size) +{ + EB_Error_Code error_code; + + LOG(("in: eb_narrow_font_xpm_size(height=%d)", (int)height)); + + switch (height) { + case EB_FONT_16: + *size = EB_SIZE_NARROW_FONT_16_XPM; + break; + case EB_FONT_24: + *size = EB_SIZE_NARROW_FONT_24_XPM; + break; + case EB_FONT_30: + *size = EB_SIZE_NARROW_FONT_30_XPM; + break; + case EB_FONT_48: + *size = EB_SIZE_NARROW_FONT_48_XPM; + break; + default: + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + LOG(("out: eb_narrow_font_xpm_size(size=%ld) = %s", (long)*size, + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *size = 0; + LOG(("out: eb_narrow_font_xpm_size() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Return required buffer size for a narrow font character converted + * to GIF image format. + */ +EB_Error_Code +eb_narrow_font_gif_size(EB_Font_Code height, size_t *size) +{ + EB_Error_Code error_code; + + LOG(("in: eb_narrow_font_gif_size(height=%d)", (int)height)); + + switch (height) { + case EB_FONT_16: + *size = EB_SIZE_NARROW_FONT_16_GIF; + break; + case EB_FONT_24: + *size = EB_SIZE_NARROW_FONT_24_GIF; + break; + case EB_FONT_30: + *size = EB_SIZE_NARROW_FONT_30_GIF; + break; + case EB_FONT_48: + *size = EB_SIZE_NARROW_FONT_48_GIF; + default: + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + LOG(("out: eb_narrow_font_gif_size(size=%ld) = %s", (long)*size, + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *size = 0; + LOG(("out: eb_narrow_font_gif_size() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Return required buffer size for a narrow font character converted + * to BMP image format. + */ +EB_Error_Code +eb_narrow_font_bmp_size(EB_Font_Code height, size_t *size) +{ + EB_Error_Code error_code; + + LOG(("in: eb_narrow_font_bmp_size(height=%d)", (int)height)); + + switch (height) { + case EB_FONT_16: + *size = EB_SIZE_NARROW_FONT_16_BMP; + break; + case EB_FONT_24: + *size = EB_SIZE_NARROW_FONT_24_BMP; + break; + case EB_FONT_30: + *size = EB_SIZE_NARROW_FONT_30_BMP; + break; + case EB_FONT_48: + *size = EB_SIZE_NARROW_FONT_48_BMP; + break; + default: + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + LOG(("out: eb_narrow_font_bmp_size(size=%ld) = %s", (long)*size, + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *size = 0; + LOG(("out: eb_narrow_font_bmp_size() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Return required buffer size for a narrow font character converted + * to PNG image format. + */ +EB_Error_Code +eb_narrow_font_png_size(EB_Font_Code height, size_t *size) +{ + EB_Error_Code error_code; + + LOG(("in: eb_narrow_font_png_size(height=%d)", (int)height)); + + switch (height) { + case EB_FONT_16: + *size = EB_SIZE_NARROW_FONT_16_PNG; + break; + case EB_FONT_24: + *size = EB_SIZE_NARROW_FONT_24_PNG; + break; + case EB_FONT_30: + *size = EB_SIZE_NARROW_FONT_30_PNG; + break; + case EB_FONT_48: + *size = EB_SIZE_NARROW_FONT_48_PNG; + default: + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + LOG(("out: eb_narrow_font_png_size(size=%ld) = %s", (long)*size, + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *size = 0; + LOG(("out: eb_narrow_font_png_size() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Return required buffer size for a wide font character converted + * to XBM image format. + */ +EB_Error_Code +eb_wide_font_xbm_size(EB_Font_Code height, size_t *size) +{ + EB_Error_Code error_code; + + LOG(("in: eb_wide_font_xbm_size(height=%d)", (int)height)); + + switch (height) { + case EB_FONT_16: + *size = EB_SIZE_WIDE_FONT_16_XBM; + break; + case EB_FONT_24: + *size = EB_SIZE_WIDE_FONT_24_XBM; + break; + case EB_FONT_30: + *size = EB_SIZE_WIDE_FONT_30_XBM; + break; + case EB_FONT_48: + *size = EB_SIZE_WIDE_FONT_48_XBM; + break; + default: + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + LOG(("out: eb_wide_font_xbm_size(size=%ld) = %s", (long)*size, + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *size = 0; + LOG(("out: eb_wide_font_xbm_size() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Return required buffer size for a wide font character converted + * to XPM image format. + */ +EB_Error_Code +eb_wide_font_xpm_size(EB_Font_Code height, size_t *size) +{ + EB_Error_Code error_code; + + LOG(("in: eb_wide_font_xpm_size(height=%d)", (int)height)); + + switch (height) { + case EB_FONT_16: + *size = EB_SIZE_WIDE_FONT_16_XPM; + break; + case EB_FONT_24: + *size = EB_SIZE_WIDE_FONT_24_XPM; + break; + case EB_FONT_30: + *size = EB_SIZE_WIDE_FONT_30_XPM; + break; + case EB_FONT_48: + *size = EB_SIZE_WIDE_FONT_48_XPM; + break; + default: + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + LOG(("out: eb_wide_font_xpm_size(size=%ld) = %s", (long)*size, + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *size = 0; + LOG(("out: eb_wide_font_xpm_size() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Return required buffer size for a wide font character converted + * to GIF image format. + */ +EB_Error_Code +eb_wide_font_gif_size(EB_Font_Code height, size_t *size) +{ + EB_Error_Code error_code; + + LOG(("in: eb_wide_font_gif_size(height=%d)", (int)height)); + + switch (height) { + case EB_FONT_16: + *size = EB_SIZE_WIDE_FONT_16_GIF; + break; + case EB_FONT_24: + *size = EB_SIZE_WIDE_FONT_24_GIF; + break; + case EB_FONT_30: + *size = EB_SIZE_WIDE_FONT_30_GIF; + break; + case EB_FONT_48: + *size = EB_SIZE_WIDE_FONT_48_GIF; + break; + default: + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + LOG(("out: eb_wide_font_gif_size(size=%ld) = %s", (long)*size, + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *size = 0; + LOG(("out: eb_wide_font_gif_size() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Return required buffer size for a wide font character converted + * to BMP image format. + */ +EB_Error_Code +eb_wide_font_bmp_size(EB_Font_Code height, size_t *size) +{ + EB_Error_Code error_code; + + LOG(("in: eb_wide_font_bmp_size(height=%d)", (int)height)); + + switch (height) { + case EB_FONT_16: + *size = EB_SIZE_WIDE_FONT_16_BMP; + break; + case EB_FONT_24: + *size = EB_SIZE_WIDE_FONT_24_BMP; + break; + case EB_FONT_30: + *size = EB_SIZE_WIDE_FONT_30_BMP; + break; + case EB_FONT_48: + *size = EB_SIZE_WIDE_FONT_48_BMP; + break; + default: + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + LOG(("out: eb_wide_font_bmp_size(size=%ld) = %s", (long)*size, + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *size = 0; + LOG(("out: eb_wide_font_bmp_size() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Return required buffer size for a wide font character converted + * to PNG image format. + */ +EB_Error_Code +eb_wide_font_png_size(EB_Font_Code height, size_t *size) +{ + EB_Error_Code error_code; + + LOG(("in: eb_wide_font_png_size(height=%d)", (int)height)); + + switch (height) { + case EB_FONT_16: + *size = EB_SIZE_WIDE_FONT_16_PNG; + break; + case EB_FONT_24: + *size = EB_SIZE_WIDE_FONT_24_PNG; + break; + case EB_FONT_30: + *size = EB_SIZE_WIDE_FONT_30_PNG; + break; + case EB_FONT_48: + *size = EB_SIZE_WIDE_FONT_48_PNG; + break; + default: + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + LOG(("out: eb_wide_font_png_size(size=%ld) = %s", (long)*size, + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *size = 0; + LOG(("out: eb_wide_font_png_size() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * The maximum number of octets in a line in a XBM file. + */ +#define XBM_MAX_OCTETS_A_LINE 12 + +/* + * The base name of a XBM file. + */ +#define XBM_BASE_NAME "default" + +/* + * Convert a bitmap image to XBM format. + * + * It requires four arguements. `xbm' is buffer to store the XBM + * image data. `bitmap', `width', and `height' are bitmap data, + * width, and height of the bitmap image. + */ +EB_Error_Code +eb_bitmap_to_xbm(const char *bitmap, int width, int height, char *xbm, + size_t *xbm_length) +{ + char *xbm_p = xbm; + const unsigned char *bitmap_p = (const unsigned char *)bitmap; + int bitmap_size = (width + 7) / 8 * height; + int hex; + int i; + + LOG(("in: eb_bitmap_to_xbm(width=%d, height=%d)", width, height)); + + /* + * Output a header. + */ + sprintf(xbm_p, "#define %s_width %4d\n", XBM_BASE_NAME, width); + xbm_p = strchr(xbm_p, '\n') + 1; + sprintf(xbm_p, "#define %s_height %4d\n", XBM_BASE_NAME, height); + xbm_p = strchr(xbm_p, '\n') + 1; + sprintf(xbm_p, "static unsigned char %s_bits[] = {\n", XBM_BASE_NAME); + xbm_p = strchr(xbm_p, '\n') + 1; + + /* + * Output image data. + */ + for (i = 0; i < bitmap_size; i++) { + hex = 0; + hex |= (*bitmap_p & 0x80) ? 0x01 : 0x00; + hex |= (*bitmap_p & 0x40) ? 0x02 : 0x00; + hex |= (*bitmap_p & 0x20) ? 0x04 : 0x00; + hex |= (*bitmap_p & 0x10) ? 0x08 : 0x00; + hex |= (*bitmap_p & 0x08) ? 0x10 : 0x00; + hex |= (*bitmap_p & 0x04) ? 0x20 : 0x00; + hex |= (*bitmap_p & 0x02) ? 0x40 : 0x00; + hex |= (*bitmap_p & 0x01) ? 0x80 : 0x00; + bitmap_p++; + + if (i % XBM_MAX_OCTETS_A_LINE != 0) { + sprintf(xbm_p, ", 0x%02x", hex); + xbm_p += 6; + } else if (i == 0) { + sprintf(xbm_p, " 0x%02x", hex); + xbm_p += 7; + } else { + sprintf(xbm_p, ",\n 0x%02x", hex); + xbm_p += 9; + } + } + + /* + * Output a footer. + */ + memcpy(xbm_p, "};\n", 3); + xbm_p += 3; + + *xbm_length = xbm_p - xbm; + + LOG(("out: eb_bitmap_to_xbm(xbm_length=%ld) = %s", + (long)(xbm_p - xbm), eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; +} + + +/* + * The base name of a XPM file. + */ +#define XPM_BASE_NAME "default" + +/* + * The foreground and background colors of XPM image. + */ +#define XPM_FOREGROUND_COLOR "Black" +#define XPM_BACKGROUND_COLOR "None" + +/* + * Convert a bitmap image to XPM format. + * + * It requires four arguements. `xpm' is buffer to store the XPM + * image data. `bitmap', `width', and `height' are bitmap data, + * width, and height of the bitmap image. + */ +EB_Error_Code +eb_bitmap_to_xpm(const char *bitmap, int width, int height, char *xpm, + size_t *xpm_length) +{ + char *xpm_p = xpm; + const unsigned char *bitmap_p = (const unsigned char *)bitmap; + int i, j; + + LOG(("in: eb_bitmap_to_xpm(width=%d, height=%d)", width, height)); + + /* + * Output a header. + */ + sprintf(xpm_p, "/* XPM */\n"); + xpm_p = strchr(xpm_p, '\n') + 1; + + sprintf(xpm_p, "static char * %s[] = {\n", XPM_BASE_NAME); + xpm_p = strchr(xpm_p, '\n') + 1; + + sprintf(xpm_p, "\"%d %d 2 1\",\n", width, height); + xpm_p = strchr(xpm_p, '\n') + 1; + + sprintf(xpm_p, "\" c %s\",\n", XPM_BACKGROUND_COLOR); + xpm_p = strchr(xpm_p, '\n') + 1; + + sprintf(xpm_p, "\". c %s\",\n", XPM_FOREGROUND_COLOR); + xpm_p = strchr(xpm_p, '\n') + 1; + + /* + * Output image data. + */ + for (i = 0; i < height; i++) { + if (0 < i) { + strcpy(xpm_p, "\",\n\""); + xpm_p += 4; + } else { + *xpm_p++ = '\"'; + } + + for (j = 0; j + 7 < width; j += 8, bitmap_p++) { + *xpm_p++ = (*bitmap_p & 0x80) ? '.' : ' '; + *xpm_p++ = (*bitmap_p & 0x40) ? '.' : ' '; + *xpm_p++ = (*bitmap_p & 0x20) ? '.' : ' '; + *xpm_p++ = (*bitmap_p & 0x10) ? '.' : ' '; + *xpm_p++ = (*bitmap_p & 0x08) ? '.' : ' '; + *xpm_p++ = (*bitmap_p & 0x04) ? '.' : ' '; + *xpm_p++ = (*bitmap_p & 0x02) ? '.' : ' '; + *xpm_p++ = (*bitmap_p & 0x01) ? '.' : ' '; + } + + if (j < width) { + if (j++ < width) + *xpm_p++ = (*bitmap_p & 0x80) ? '.' : ' '; + if (j++ < width) + *xpm_p++ = (*bitmap_p & 0x40) ? '.' : ' '; + if (j++ < width) + *xpm_p++ = (*bitmap_p & 0x20) ? '.' : ' '; + if (j++ < width) + *xpm_p++ = (*bitmap_p & 0x10) ? '.' : ' '; + if (j++ < width) + *xpm_p++ = (*bitmap_p & 0x08) ? '.' : ' '; + if (j++ < width) + *xpm_p++ = (*bitmap_p & 0x04) ? '.' : ' '; + if (j++ < width) + *xpm_p++ = (*bitmap_p & 0x02) ? '.' : ' '; + if (j++ < width) + *xpm_p++ = (*bitmap_p & 0x01) ? '.' : ' '; + bitmap_p++; + } + } + + /* + * Output a footer. + */ + memcpy(xpm_p, "\"};\n", 4); + xpm_p += 4; + + if (xpm_length != NULL) + *xpm_length = xpm_p - xpm; + + LOG(("out: eb_bitmap_to_xpm(xpm_length=%ld) = %s", + (long)(xpm_p - xpm), eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; +} + + +/* + * The Foreground and background colors of GIF image. + */ +#define GIF_FOREGROUND_COLOR 0x000000 +#define GIF_BACKGROUND_COLOR 0xffffff + +/* + * The preamble of GIF image. + */ +#define GIF_PREAMBLE_LENGTH 38 + +static const unsigned char gif_preamble[GIF_PREAMBLE_LENGTH] = { + /* + * Header. (6 bytes) + */ + 'G', 'I', 'F', '8', '9', 'a', + + /* + * Logical Screen Descriptor. (7 bytes) + * global color table flag = 1. + * color resolution = 1 - 1 = 0. + * sort flag = 0. + * size of global color table = 1 - 1 = 0. + * background color index = 0. + * the pixel aspect ratio = 0 (unused) + * Logical screen width and height are set at run time. + */ + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + + /* + * Global Color Table. (6 bytes) + * These are set at run time. + */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* + * Graphic Control Extension. (8 bytes) + * disposal method = 0. + * user input flag = 0. + * transparency flag = 1. + * delay time = 0. + * transparent color index = 0. + */ + 0x21, 0xf9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, + + /* + * Image Descriptor. (10 bytes) + * image left position = 0. + * image top position = 0. + * local color table flag = 0. + * interlace flag = 0. + * sort flag = 0. + * size of local color table = 0. + * Image width and height are set at run time. + */ + 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* + * Code size. (1byte) + */ + 0x03 +}; + + +/* + * Convert a bitmap image to GIF format. + * + * It requires four arguements. `gif' is buffer to store the GIF + * image data. `bitmap', `width', and `height' are bitmap data, + * width, and height of the bitmap image. + * + * Note: This GIF image doesn't use LZW because of patent. + */ +EB_Error_Code +eb_bitmap_to_gif(const char *bitmap, int width, int height, char *gif, + size_t *gif_length) +{ + unsigned char *gif_p = (unsigned char *)gif; + const unsigned char *bitmap_p = (const unsigned char *)bitmap; + int i, j; + + LOG(("in: eb_bitmap_to_gif(width=%d, height=%d)", width, height)); + + /* + * Copy the default preamble. + */ + memcpy(gif_p, gif_preamble, GIF_PREAMBLE_LENGTH); + + /* + * Set logical screen width and height. + */ + gif_p[6] = width & 0xff; + gif_p[7] = (width >> 8) & 0xff; + gif_p[8] = height & 0xff; + gif_p[9] = (height >> 8) & 0xff; + + /* + * Set global colors. + */ + gif_p[13] = (GIF_BACKGROUND_COLOR >> 16) & 0xff; + gif_p[14] = (GIF_BACKGROUND_COLOR >> 8) & 0xff; + gif_p[15] = GIF_BACKGROUND_COLOR & 0xff; + gif_p[16] = (GIF_FOREGROUND_COLOR >> 16) & 0xff; + gif_p[17] = (GIF_FOREGROUND_COLOR >> 8) & 0xff; + gif_p[18] = GIF_FOREGROUND_COLOR & 0xff; + + /* + * Set image width and height. + */ + gif_p[32] = width & 0xff; + gif_p[33] = (width >> 8) & 0xff; + gif_p[34] = height & 0xff; + gif_p[35] = (height >> 8) & 0xff; + + gif_p += GIF_PREAMBLE_LENGTH; + + /* + * Output image data. + */ + for (i = 0; i < height; i++) { + *gif_p++ = (unsigned char)width; + for (j = 0; j + 7 < width; j += 8, bitmap_p++) { + *gif_p++ = (*bitmap_p & 0x80) ? 0x81 : 0x80; + *gif_p++ = (*bitmap_p & 0x40) ? 0x81 : 0x80; + *gif_p++ = (*bitmap_p & 0x20) ? 0x81 : 0x80; + *gif_p++ = (*bitmap_p & 0x10) ? 0x81 : 0x80; + *gif_p++ = (*bitmap_p & 0x08) ? 0x81 : 0x80; + *gif_p++ = (*bitmap_p & 0x04) ? 0x81 : 0x80; + *gif_p++ = (*bitmap_p & 0x02) ? 0x81 : 0x80; + *gif_p++ = (*bitmap_p & 0x01) ? 0x81 : 0x80; + } + + if (j < width) { + if (j++ < width) + *gif_p++ = (*bitmap_p & 0x80) ? 0x81 : 0x80; + if (j++ < width) + *gif_p++ = (*bitmap_p & 0x40) ? 0x81 : 0x80; + if (j++ < width) + *gif_p++ = (*bitmap_p & 0x20) ? 0x81 : 0x80; + if (j++ < width) + *gif_p++ = (*bitmap_p & 0x10) ? 0x81 : 0x80; + if (j++ < width) + *gif_p++ = (*bitmap_p & 0x08) ? 0x81 : 0x80; + if (j++ < width) + *gif_p++ = (*bitmap_p & 0x04) ? 0x81 : 0x80; + if (j++ < width) + *gif_p++ = (*bitmap_p & 0x02) ? 0x81 : 0x80; + if (j++ < width) + *gif_p++ = (*bitmap_p & 0x01) ? 0x81 : 0x80; + bitmap_p++; + } + } + + /* + * Output a trailer. + */ + memcpy(gif_p, "\001\011\000\073", 4); + gif_p += 4; + + if (gif_length != NULL) + *gif_length = ((char *)gif_p - gif); + + LOG(("out: eb_bitmap_to_gif(gif_length=%ld) = %s", + (long)((char *)gif_p - gif), eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; +} + + +/* + * The preamble of BMP image. + */ +#define BMP_PREAMBLE_LENGTH 62 + +static const unsigned char bmp_preamble[] = { + /* Type. */ + 'B', 'M', + + /* File size. (set at run time) */ + 0x00, 0x00, 0x00, 0x00, + + /* Reserved. */ + 0x00, 0x00, 0x00, 0x00, + + /* Offset of bitmap bits part. */ + 0x3e, 0x00, 0x00, 0x00, + + /* Size of bitmap info part. */ + 0x28, 0x00, 0x00, 0x00, + + /* Width. (set at run time) */ + 0x00, 0x00, 0x00, 0x00, + + /* Height. (set at run time) */ + 0x00, 0x00, 0x00, 0x00, + + /* Planes. */ + 0x01, 0x00, + + /* Bits per pixels. */ + 0x01, 0x00, + + /* Compression mode. */ + 0x00, 0x00, 0x00, 0x00, + + /* Size of bitmap bits part. (set at run time) */ + 0x00, 0x00, 0x00, 0x00, + + /* X Pixels per meter. */ + 0x6d, 0x0b, 0x00, 0x00, + + /* Y Pixels per meter. */ + 0x6d, 0x0b, 0x00, 0x00, + + /* Colors */ + 0x02, 0x00, 0x00, 0x00, + + /* Important colors */ + 0x02, 0x00, 0x00, 0x00, + + /* RGB quad of color 0 RGB quad of color 1 */ + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +/* + * Convert a bitmap image to BMP format. + * + * It requires four arguements. `bmp' is buffer to store the BMP + * image data. `bitmap', `width', and `height' are bitmap data, + * width, and height of the bitmap image. + */ +EB_Error_Code +eb_bitmap_to_bmp(const char *bitmap, int width, int height, char *bmp, + size_t *bmp_length) +{ + unsigned char *bmp_p = (unsigned char *)bmp; + size_t data_size; + size_t file_size; + size_t line_pad_length; + size_t bitmap_line_length; + int i, j; + + LOG(("in: eb_bitmap_to_bmp(width=%d, height=%d)", width, height)); + + if (width % 32 == 0) + line_pad_length = 0; + else if (width % 32 <= 8) + line_pad_length = 3; + else if (width % 32 <= 16) + line_pad_length = 2; + else if (width % 32 <= 24) + line_pad_length = 1; + else + line_pad_length = 0; + + data_size = (width / 2 + line_pad_length) * height; + file_size = data_size + BMP_PREAMBLE_LENGTH; + + /* + * Set BMP preamble. + */ + memcpy(bmp_p, bmp_preamble, BMP_PREAMBLE_LENGTH); + + bmp_p[2] = file_size & 0xff; + bmp_p[3] = (file_size >> 8) & 0xff; + bmp_p[4] = (file_size >> 16) & 0xff; + bmp_p[5] = (file_size >> 24) & 0xff; + + bmp_p[18] = width & 0xff; + bmp_p[19] = (width >> 8) & 0xff; + bmp_p[20] = (width >> 16) & 0xff; + bmp_p[21] = (width >> 24) & 0xff; + + bmp_p[22] = height & 0xff; + bmp_p[23] = (height >> 8) & 0xff; + bmp_p[24] = (height >> 16) & 0xff; + bmp_p[25] = (height >> 24) & 0xff; + + bmp_p[34] = data_size & 0xff; + bmp_p[35] = (data_size >> 8) & 0xff; + bmp_p[36] = (data_size >> 16) & 0xff; + bmp_p[37] = (data_size >> 24) & 0xff; + + bmp_p += BMP_PREAMBLE_LENGTH; + bitmap_line_length = (width + 7) / 8; + + for (i = height - 1; 0 <= i; i--) { + memcpy(bmp_p, bitmap + bitmap_line_length * i, bitmap_line_length); + bmp_p += bitmap_line_length; + for (j = 0; j < line_pad_length; j++, bmp_p++) + *bmp_p = 0x00; + } + + if (bmp_length != NULL) + *bmp_length = ((char *)bmp_p - bmp); + + LOG(("out: eb_bitmap_to_bmp(bmp_length=%ld) = %s", + (long)((char *)bmp_p - bmp), eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; +} + + +/* + * The Foreground and background colors of PNG image. + */ +#define PNG_FOREGROUND_COLOR 0x000000 +#define PNG_BACKGROUND_COLOR 0xffffff + +/* + * The preamble of PNG image. + */ +static const unsigned char png_preamble[] = { + /* + * PNG file signature (8 bytes) + */ + 0x89, 'P', 'N', 'G', '\r', '\n', 0x1a, '\n', + /* + * IHDR(Image Header) Chunk (25 bytes) + */ + /* Size of IHDR. */ + 0x00, 0x00, 0x00, 0x0d, + 'I', 'H', 'D', 'R', + /* Width. (set at run time) */ + 0x00, 0x00, 0x00, 0x00, + /* Height. (set at run time) */ + 0x00, 0x00, 0x00, 0x00, + /* misc. */ + 0x01, 0x03, 0x00, 0x00, 0x00, + /* CRC (set at run time) */ + 0x00, 0x00, 0x00, 0x00, + + /* + * PLTE(Palette) Chunk (18 bytes) + */ + /* Size of PLTE */ + 0x00, 0x00, 0x00, 0x06, + 'P', 'L', 'T', 'E', + /* RGB for palette index 0 */ + 0xff, 0xff, 0xff, + /* RGB for palette index 1 */ + 0x00, 0x00, 0x00, + /* CRC (set at run time) */ + 0x00, 0x00, 0x00, 0x00, + + /* + * tRNS(Transparency) Chunk (13 bytes) + */ + /* Size of tRNS */ + 0x00, 0x00, 0x00, 0x01, + 't', 'R', 'N', 'S', + /* Alpha for palette index 0 */ + 0x00, + /* CRC */ + 0x40, 0xe6, 0xd8, 0x66, + + /* + * IDAT(Image Data) Chunk (12+ bytes) + */ + /* Size of IDAT (set at run time) */ + 0x00, 0x00, 0x00, 0x00, + 'I', 'D', 'A', 'T', +}; + +static const unsigned char png_trailer[] = { + /* CRC (set at run time) */ + 0x00, 0x00, 0x00, 0x00, + /* + * IEND(Image End) Chunk (12 bytes) + */ + /* Size of IEND */ + 0x00, 0x00, 0x00, 0x00, + 'I', 'E', 'N', 'D', + /* CRC */ + 0xae, 0x42, 0x60, 0x82, +}; + +/* + * Table of CRCs of all 8-bit messages. + */ +static const unsigned long png_crc_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, /* 0x00 - 0x03 */ + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, /* 0x04 - 0x07 */ + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, /* 0x08 - 0x0b */ + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, /* 0x0c - 0x0f */ + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, /* 0x10 - 0x13 */ + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, /* 0x14 - 0x17 */ + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, /* 0x18 - 0x1b */ + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, /* 0x1c - 0x1f */ + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, /* 0x20 - 0x23 */ + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, /* 0x24 - 0x27 */ + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, /* 0x28 - 0x2b */ + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, /* 0x2c - 0x2f */ + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, /* 0x30 - 0x33 */ + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, /* 0x34 - 0x37 */ + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, /* 0x38 - 0x3b */ + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, /* 0x3c - 0x3f */ + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, /* 0x40 - 0x43 */ + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, /* 0x44 - 0x47 */ + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, /* 0x48 - 0x4b */ + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, /* 0x4c - 0x4f */ + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, /* 0x50 - 0x53 */ + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, /* 0x54 - 0x57 */ + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, /* 0x58 - 0x5b */ + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, /* 0x5c - 0x5f */ + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, /* 0x60 - 0x63 */ + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, /* 0x64 - 0x67 */ + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, /* 0x68 - 0x6b */ + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, /* 0x6c - 0x6f */ + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, /* 0x70 - 0x73 */ + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, /* 0x74 - 0x77 */ + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, /* 0x78 - 0x7b */ + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, /* 0x7c - 0x7f */ + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, /* 0x80 - 0x83 */ + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, /* 0x84 - 0x87 */ + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, /* 0x88 - 0x8b */ + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, /* 0x8c - 0x8f */ + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, /* 0x90 - 0x93 */ + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, /* 0x94 - 0x97 */ + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, /* 0x98 - 0x9b */ + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, /* 0x9c - 0x9f */ + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, /* 0xa0 - 0xa3 */ + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, /* 0xa4 - 0xa7 */ + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, /* 0xa8 - 0xab */ + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, /* 0xac - 0xaf */ + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, /* 0xb0 - 0xb3 */ + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, /* 0xb4 - 0xb7 */ + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, /* 0xb8 - 0xbb */ + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, /* 0xbc - 0xbf */ + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, /* 0xc0 - 0xc3 */ + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, /* 0xc4 - 0xc7 */ + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, /* 0xc8 - 0xcb */ + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, /* 0xcc - 0xcf */ + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, /* 0xd0 - 0xd3 */ + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, /* 0xd4 - 0xd7 */ + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, /* 0xd8 - 0xdb */ + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, /* 0xdc - 0xdf */ + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, /* 0xe0 - 0xe3 */ + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, /* 0xe4 - 0xe7 */ + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, /* 0xe8 - 0xeb */ + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, /* 0xec - 0xef */ + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, /* 0xf0 - 0xf3 */ + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, /* 0xf4 - 0xf7 */ + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, /* 0xf8 - 0xfb */ + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d /* 0xfc - 0xff */ +}; + +static unsigned long +png_crc(const char *buf, size_t len) +{ + unsigned long c = 0xffffffffL; + int n; + + for (n = 0; n < len; n++) + c = png_crc_table[(c ^ *((unsigned char *)buf + n)) & 0xff] ^ (c >> 8); + return c ^ 0xffffffffL; +} + + +static int +png_compress(const char *src, int width, int height, char *dest, + size_t *dest_len) +{ + int line_size = (width + 7) / 8; + z_stream z; + int z_result; + unsigned char byte_zero = 0x00; + int i; + + z.zalloc = Z_NULL; + z.zfree = Z_NULL; + z.opaque = Z_NULL; + z_result = deflateInit(&z, Z_NO_COMPRESSION); + if (z_result != Z_OK) + return z_result; + + /* + * Exactly to say, `z.avail_out' must be: + * avail_out > (sizeof(src) + 12) * 1.001 + * but we use an approximation here. + */ + z.next_out = (unsigned char *)dest; + z.avail_out = (line_size + 1) * height + 12 + 256; + for (i = 0; i < height - 1; i++) { + z.next_in = &byte_zero; + z.avail_in = 1; + z_result = deflate(&z, Z_NO_FLUSH); + if (z_result != Z_OK || z.avail_in != 0) + goto failed; + + z.next_in = (unsigned char *)src + (line_size * i); + z.avail_in = line_size; + z_result = deflate(&z, Z_NO_FLUSH); + if (z_result != Z_OK || z.avail_in != 0) + goto failed; + } + + z.next_in = &byte_zero; + z.avail_in = 1; + z_result = deflate(&z, Z_NO_FLUSH); + if (z_result != Z_OK || z.avail_in != 0) + goto failed; + + z.next_in = (unsigned char *)src + (line_size * i); + z.avail_in = line_size; + if (deflate(&z, Z_FINISH) != Z_STREAM_END) + goto failed; + + z_result = deflateEnd(&z); + if (z_result != Z_OK) + return z_result; + + *dest_len = (z.next_out - (unsigned char *)dest); + return Z_STREAM_END; + + /* + * An error occurs... + */ + failed: + deflateEnd(&z); + return z_result; +} + + +#define INT2CHARS(p, i) do { \ + *(unsigned char *)(p) = ((i) >> 24) & 0xff; \ + *((unsigned char *)(p) + 1) = ((i) >> 16) & 0xff; \ + *((unsigned char *)(p) + 2) = ((i) >> 8) & 0xff; \ + *((unsigned char *)(p) + 3) = (i) & 0xff; \ +} while (0); + +#define RGB2CHARS(p, i) do { \ + *(unsigned char *)(p) = ((i) >> 16) & 0xff; \ + *((unsigned char *)(p) + 1) = ((i) >> 8) & 0xff; \ + *((unsigned char *)(p) + 2) = (i) & 0xff; \ +} while (0); + +/* + * Convert a bitmap image to PNG format. + * + * It requires four arguements. `png' is buffer to store the PNG + * image data. `bitmap', `width', and `height' are bitmap data, + * width, and height of the bitmap image. + */ +EB_Error_Code +eb_bitmap_to_png(const char *bitmap, int width, int height, char *png, + size_t *png_length) +{ + EB_Error_Code error_code; + char *png_p = png; + char *idat_start; + size_t idat_len; + unsigned long crc; + int z_result; + + LOG(("in: eb_bitmap_to_png(width=%d, height=%d)", width, height)); + + /* + * Copy the default preamble. + */ + memcpy(png_p, png_preamble, sizeof(png_preamble)); + + /* + * Set image width and height. + */ + INT2CHARS(png_p + 16, width); + INT2CHARS(png_p + 20, height); + + crc = png_crc(png_p + 12, 17); + INT2CHARS(png_p + 29, crc); + + /* + * Set global colors. + */ + RGB2CHARS(png_p + 41, PNG_BACKGROUND_COLOR); + RGB2CHARS(png_p + 44, PNG_FOREGROUND_COLOR); + crc = png_crc(png_p + 37, 10); + INT2CHARS(png_p + 47, crc); + + /* + * Output `bitmap'. + * We assume memory allocation error occurs if png_compress() doesn't + * return Z_STREAM_END. + */ + idat_start = png_p + sizeof(png_preamble); + z_result = png_compress(bitmap, width, height, idat_start, &idat_len); + if (z_result != Z_STREAM_END) { + error_code = EB_ERR_MEMORY_EXHAUSTED; + goto failed; + } + INT2CHARS(png_p + 64, idat_len); + crc = png_crc(idat_start - 4, idat_len + 4); + png_p = idat_start + idat_len; + + /* + * Output a trailer. + */ + memcpy(png_p, png_trailer, sizeof(png_trailer)); + INT2CHARS(png_p, crc); + png_p += sizeof(png_trailer); + if (png_length != NULL) + *png_length = ((char *)png_p - png); + + LOG(("out: eb_bitmap_to_png(png_length=%ld) = %s", + (long)((char *)png_p - png), eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_bitmap_to_png(png_length=%ld) = %s", + (long)((char *)png_p - png), eb_error_string(error_code))); + return error_code; +} + +#undef INT2CHARS +#undef RGB2CHARS + + +#ifdef TEST + +#include +#include +#include + +#define test_width 32 +#define test_height 16 +static unsigned char test_bitmap[] = { + 0xff, 0xff, 0xff, 0xff, 0x80, 0x81, 0x83, 0x01, 0x80, 0x81, 0x01, 0x01, + 0x80, 0x81, 0x01, 0x01, 0xe3, 0x8f, 0x11, 0xc7, 0xe3, 0x8f, 0x0f, 0xc7, + 0xe3, 0x81, 0x87, 0xc7, 0xe3, 0x81, 0xc3, 0xc7, 0xe3, 0x81, 0xe1, 0xc7, + 0xe3, 0x8f, 0x11, 0xc7, 0xe3, 0x8f, 0x11, 0xc7, 0xe3, 0x81, 0x01, 0xc7, + 0xe3, 0x81, 0x01, 0xc7, 0xe3, 0x81, 0x83, 0xc7, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff +}; + +int +main(int argc, char *argv[]) +{ + char image[EB_SIZE_FONT_IMAGE]; + size_t image_size; + int file; + + eb_bitmap_to_xbm(test_bitmap, test_width, test_height, image, &image_size); + file = creat("test.xbm", 0644); + if (file < 0) + exit(1); + if (write(file, image, image_size) != image_size) { + close(file); + exit(1); + } + + eb_bitmap_to_xpm(test_bitmap, test_width, test_height, image, &image_size); + file = creat("test.xpm", 0644); + if (file < 0) + exit(1); + if (write(file, image, image_size) != image_size) { + close(file); + exit(1); + } + + eb_bitmap_to_gif(test_bitmap, test_width, test_height, image, &image_size); + file = creat("test.gif", 0644); + if (file < 0) + exit(1); + if (write(file, image, image_size) != image_size) { + close(file); + exit(1); + } + + eb_bitmap_to_bmp(test_bitmap, test_width, test_height, image, &image_size); + file = creat("test.bmp", 0644); + if (file < 0) + exit(1); + if (write(file, image, image_size) != image_size) { + close(file); + exit(1); + } + + eb_bitmap_to_png(test_bitmap, test_width, test_height, image, &image_size); + file = creat("test.png", 0644); + if (file < 0) + exit(1); + if (write(file, image, image_size) != image_size) { + close(file); + exit(1); + } + + return 0; +} + +#endif /* TEST */ diff --git a/book.c b/book.c new file mode 100644 index 0000000..d4ea9e0 --- /dev/null +++ b/book.c @@ -0,0 +1,954 @@ +/* + * 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); + eb_initialize_lock(&book->lock); + + 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]; + + eb_lock(&book->lock); + 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. + */ + pthread_mutex_lock(&book_counter_mutex); + book->code = book_counter++; + pthread_mutex_unlock(&book_counter_mutex); + + /* + * 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))); + eb_unlock(&book->lock); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_finalize_book(book); + LOG(("out: eb_bind() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + 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); + eb_finalize_lock(&book->lock); + + 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; + + eb_lock(&book->lock); + 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)); + eb_unlock(&book->lock); + + return is_bound; +} + + +/* + * Return the bound path of `book'. + */ +EB_Error_Code +eb_path(EB_Book *book, char *path) +{ + EB_Error_Code error_code; + + eb_lock(&book->lock); + 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))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *path = '\0'; + LOG(("out: eb_path() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + 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; + + eb_lock(&book->lock); + 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))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *disc_code = EB_DISC_INVALID; + LOG(("out: eb_disc_type() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + 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; + + eb_lock(&book->lock); + 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))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *character_code = EB_CHARCODE_INVALID; + LOG(("out: eb_character_code() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} diff --git a/booklist.c b/booklist.c new file mode 100644 index 0000000..2d6c9c3 --- /dev/null +++ b/booklist.c @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2003-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 "build-post.h" + +/* + * Initial value of `max_entry_count' in `EB_BookList'. + */ +#define EB_INITIAL_BOOKLIST_MAX_ENTRY_COUNT 16 + +/* + * BookList ID counter. + */ +static EB_Book_Code booklist_counter = 0; + +/* + * Initialize a book list. + */ +void +eb_initialize_booklist(EB_BookList *booklist) +{ + LOG(("in: eb_initialize_booklist()")); + + booklist->entry_count = 0; + booklist->max_entry_count = 0; + booklist->entries = NULL; + eb_initialize_lock(&booklist->lock); + + LOG(("out: eb_initialize_booklist()")); +} + + +/* + * Finalize a book list. + */ +void +eb_finalize_booklist(EB_BookList *booklist) +{ + int i; + + LOG(("in: eb_finalize_booklist()")); + + if (booklist->entries != NULL) { + for (i = 0; i < booklist->entry_count; i++) { + free(booklist->entries[i].name); + free(booklist->entries[i].title); + } + free(booklist->entries); + booklist->entries = NULL; + } + booklist->entry_count = 0; + booklist->max_entry_count = 0; + + LOG(("out: eb_finalize_booklist()")); +} + + +/* + * Add a book entry to `booklist'. + */ +EB_Error_Code +eb_booklist_add_book(EB_BookList *booklist, const char *name, + const char *title) +{ + int new_max_entry_count; + EB_BookList_Entry *new_entries; + char *new_name = NULL; + char *new_title = NULL; + EB_Error_Code error_code; + + LOG(("in: eb_booklist_add_book(name=%s, title=%s)", name, title)); + + if (booklist->entry_count == booklist->max_entry_count) { + if (booklist->max_entry_count == 0) { + new_max_entry_count = EB_INITIAL_BOOKLIST_MAX_ENTRY_COUNT; + new_entries = (EB_BookList_Entry *) + malloc(sizeof(EB_BookList_Entry) * new_max_entry_count); + } else { + new_max_entry_count = booklist->max_entry_count * 2; + new_entries = (EB_BookList_Entry *)realloc(booklist->entries, + sizeof(EB_BookList_Entry) * new_max_entry_count); + } + if (new_entries == NULL) { + error_code = EB_ERR_MEMORY_EXHAUSTED; + goto failed; + } + booklist->max_entry_count = new_max_entry_count; + booklist->entries = new_entries; + } + + new_name = (char *)malloc(strlen(name) + 1); + if (new_name == NULL) { + error_code = EB_ERR_MEMORY_EXHAUSTED; + goto failed; + } + strcpy(new_name, name); + + new_title = (char *)malloc(strlen(title) + 1); + if (new_title == NULL) { + error_code = EB_ERR_MEMORY_EXHAUSTED; + goto failed; + } + strcpy(new_title, title); + + booklist->entries[booklist->entry_count].name = new_name; + booklist->entries[booklist->entry_count].title = new_title; + booklist->entry_count++; + + LOG(("out: eb_booklist_add_book() = %s", eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + if (new_name != NULL) + free(new_name); + if (new_title != NULL) + free(new_title); + + LOG(("out: eb_booklist_book_add() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Return the number of books in `booklist'. + */ +EB_Error_Code +eb_booklist_book_count(EB_BookList *booklist, int *book_count) +{ + EB_Error_Code error_code; + + eb_lock(&booklist->lock); + LOG(("in: eb_booklist_book_count(booklist=%d)", (int)booklist->code)); + + if (booklist->entries == NULL) { + error_code = EB_ERR_UNBOUND_BOOKLIST; + goto failed; + } + *book_count = booklist->entry_count; + + LOG(("out: eb_booklist_book_count(count=%d) = %s", *book_count, + eb_error_string(EB_SUCCESS))); + eb_unlock(&booklist->lock); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_booklist_book_count() = %s", eb_error_string(error_code))); + eb_unlock(&booklist->lock); + return error_code; +} + + +/* + * Return title of a book entry in `booklist'. + */ +EB_Error_Code +eb_booklist_book_name(EB_BookList *booklist, int book_index, char **book_name) +{ + EB_Error_Code error_code; + + eb_lock(&booklist->lock); + LOG(("in: eb_booklist_book_name(booklist=%d,index=%d)", + (int)booklist->code, book_index)); + + if (booklist->entries == NULL) { + error_code = EB_ERR_UNBOUND_BOOKLIST; + goto failed; + } + if (book_index < 0 || booklist->entry_count <= book_index) { + error_code = EB_ERR_NO_SUCH_BOOK; + goto failed; + } + + *book_name = booklist->entries[book_index].name; + + LOG(("out: eb_booklist_book_name(*book_name=%s) = %s", + (*book_name == NULL) ? "NULL" : *book_name, + eb_error_string(EB_SUCCESS))); + + eb_unlock(&booklist->lock); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_booklist_book_name() = %s", eb_error_string(error_code))); + eb_unlock(&booklist->lock); + return error_code; +} + + +/* + * Return name of a book entry in `booklist'. + */ +EB_Error_Code +eb_booklist_book_title(EB_BookList *booklist, int book_index, + char **book_title) +{ + EB_Error_Code error_code; + + eb_lock(&booklist->lock); + LOG(("in: eb_booklist_book_title(booklist=%d,index=%d)", + (int)booklist->code, book_index)); + + if (booklist->entries == NULL) { + error_code = EB_ERR_UNBOUND_BOOKLIST; + goto failed; + } + if (book_index < 0 || booklist->entry_count <= book_index) { + error_code = EB_ERR_NO_SUCH_BOOK; + goto failed; + } + *book_title = booklist->entries[book_index].title; + + LOG(("out: eb_booklist_book_title(*book_title=%s) = %s", + (*book_title == NULL) ? "NULL" : *book_title, + eb_error_string(EB_SUCCESS))); + + eb_unlock(&booklist->lock); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_booklist_book_title() = %s", eb_error_string(error_code))); + eb_unlock(&booklist->lock); + return error_code; +} diff --git a/booklist.h b/booklist.h new file mode 100644 index 0000000..2069479 --- /dev/null +++ b/booklist.h @@ -0,0 +1,56 @@ +/* -*- C -*- + * 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. + */ + +#ifndef EB_BOOKLIST_H +#define EB_BOOKLIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "eb.h" + +/* + * Function declarations. + */ +/* booklist.c */ +void eb_initialize_booklist(EB_BookList *booklist); +void eb_finalize_booklist(EB_BookList *booklist); +EB_Error_Code eb_bind_booklist(EB_BookList *booklist, const char *path); +EB_Error_Code eb_booklist_book_count(EB_BookList *booklist, int *book_count); +EB_Error_Code eb_booklist_book_name(EB_BookList *booklist, int book_index, + char **book_name); +EB_Error_Code eb_booklist_book_title(EB_BookList *booklist, int book_index, + char **book_title); + + +#ifdef __cplusplus +} +#endif + +#endif /* not EB_BOOKLIST_H */ diff --git a/build-post.h b/build-post.h new file mode 100644 index 0000000..9609650 --- /dev/null +++ b/build-post.h @@ -0,0 +1,356 @@ +/* -*- C -*- + * 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. + */ + +#ifndef EB_BUILD_POST_H +#define EB_BUILD_POST_H + +#include "defs.h" + +/* + * Text domain name. + */ +#define EB_TEXT_DOMAIN_NAME "eb" + +/* + * Locale directory. + */ +#ifndef WIN32 +#define EB_LOCALEDIR "@localedir@" +#else +#define EB_LOCALEDIR localedir() +#endif + +/* + * Data size of a book entry in a catalog file. + */ +#define EB_SIZE_EB_CATALOG 40 +#define EB_SIZE_EPWING_CATALOG 164 + +/* + * Maximum number of search titles. + */ +#define EB_MAX_SEARCH_TITLES 14 + +/* + * File names. + */ +#define EB_FILE_NAME_START "start" +#define EB_FILE_NAME_HONMON "honmon" +#define EB_FILE_NAME_FUROKU "furoku" +#define EB_FILE_NAME_APPENDIX "appendix" + +/* + * Directory names. + */ +#define EB_DIRECTORY_NAME_DATA "data" +#define EB_DIRECTORY_NAME_GAIJI "gaiji" +#define EB_DIRECTORY_NAME_STREAM "stream" +#define EB_DIRECTORY_NAME_MOVIE "movie" + +/* + * Search word types. + */ +#define EB_WORD_ALPHABET 0 +#define EB_WORD_KANA 1 +#define EB_WORD_OTHER 2 +#define EB_WORD_INVALID -1 + +/* + * Index Style flags. + */ +#define EB_INDEX_STYLE_CONVERT 0 +#define EB_INDEX_STYLE_ASIS 1 +#define EB_INDEX_STYLE_REVERSED_CONVERT 2 +#define EB_INDEX_STYLE_DELETE 2 + +/* + * Text content currently read. + */ +#define EB_TEXT_MAIN_TEXT 1 +#define EB_TEXT_HEADING 2 +#define EB_TEXT_RAWTEXT 3 +#define EB_TEXT_OPTIONAL_TEXT 4 +#define EB_TEXT_SEEKED 0 +#define EB_TEXT_INVALID -1 + +/* + * Search method currently processed. + */ +#define EB_SEARCH_EXACTWORD 0 +#define EB_SEARCH_WORD 1 +#define EB_SEARCH_ENDWORD 2 +#define EB_SEARCH_KEYWORD 3 +#define EB_SEARCH_MULTI 4 +#define EB_SEARCH_CROSS 5 +#define EB_SEARCH_ALL 6 +#define EB_SEARCH_NONE -1 + +/* + * Arrangement style of entries in a search index page. + */ +#define EB_ARRANGE_FIXED 0 +#define EB_ARRANGE_VARIABLE 1 +#define EB_ARRANGE_INVALID -1 + +/* + * Binary data types. + */ +#define EB_BINARY_MONO_GRAPHIC 0 +#define EB_BINARY_COLOR_GRAPHIC 1 +#define EB_BINARY_WAVE 2 +#define EB_BINARY_MPEG 3 +#define EB_BINARY_GRAY_GRAPHIC 4 +#define EB_BINARY_INVALID -1 + +/* + * Text-stop status. + */ +#define EB_TEXT_STATUS_CONTINUED 0 +#define EB_TEXT_STATUS_SOFT_STOP 1 +#define EB_TEXT_STATUS_HARD_STOP 2 + +/* + * The maximum index depth of search indexes. + */ +#define EB_MAX_INDEX_DEPTH 6 + +/* + * The maximum length of path name relative to top directory of a CD-ROM + * book. An example of the longest relative path is: + * + * "subdir01/subdir02/filename.ebz;1" + */ +#define EB_MAX_RELATIVE_PATH_LENGTH \ + (EB_MAX_DIRECTORY_NAME_LENGTH + 1 \ + + EB_MAX_DIRECTORY_NAME_LENGTH + 1 \ + + EB_MAX_FILE_NAME_LENGTH) + +/* + * The environment variable name to enable/disable debug messages. + */ +#define EB_DEBUG_ENVIRONMENT_VARIABLE "EB_DEBUG" + +/* + * Trace log macro. + */ +#define LOG(x) do {if (eb_log_flag) eb_log x;} while (0) + +/* + * Get an unsigned value from an octet stream buffer. + */ +#define eb_uint1(p) (*(const unsigned char *)(p)) + +#define eb_uint2(p) ((*(const unsigned char *)(p) << 8) \ + + (*(const unsigned char *)((p) + 1))) + +#define eb_uint3(p) ((*(const unsigned char *)(p) << 16) \ + + (*(const unsigned char *)((p) + 1) << 8) \ + + (*(const unsigned char *)((p) + 2))) + +#define eb_uint4(p) ((*(const unsigned char *)(p) << 24) \ + + (*(const unsigned char *)((p) + 1) << 16) \ + + (*(const unsigned char *)((p) + 2) << 8) \ + + (*(const unsigned char *)((p) + 3))) + +#define eb_uint4_le(p) ((*(const unsigned char *)(p)) \ + + (*(const unsigned char *)((p) + 1) << 8) \ + + (*(const unsigned char *)((p) + 2) << 16) \ + + (*(const unsigned char *)((p) + 3) << 24)) + +/* + * Test whether `off_t' represents a large integer. + */ +#define off_t_is_large \ + ((((off_t) 1 << 41) + ((off_t) 1 << 40) + 1) % 9999991 == 7852006) + +/* + * External variable declarations. + */ +/* log.c */ +extern int eb_log_flag; + +/* hook.c */ +extern EB_Hookset eb_default_hookset; + +/* + * Function declarations. + */ +/* appendix.c */ +void eb_initialize_alt_caches(EB_Appendix *appendix); +void eb_finalize_alt_caches(EB_Appendix *appendix); + +/* appsub.c */ +void eb_initialize_appendix_subbooks(EB_Appendix *appendix); +void eb_finalize_appendix_subbooks(EB_Appendix *appendix); + +/* bcd.c */ +unsigned eb_bcd2(const char *stream); +unsigned eb_bcd4(const char *stream); +unsigned eb_bcd6(const char *stream); + +/* binary.c */ +void eb_initialize_binary_context(EB_Book *book); +void eb_reset_binary_context(EB_Book *book); +void eb_finalize_binary_context(EB_Book *book); + +/* booklist.c */ +EB_Error_Code eb_booklist_add_book(EB_BookList *booklist, const char *name, + const char *title); + +/* filename.c */ +EB_Error_Code eb_canonicalize_path_name(char *path_name); +void eb_canonicalize_file_name(char *file_name); +EB_Error_Code eb_fix_directory_name(const char *path, char *directory_name); +EB_Error_Code eb_fix_directory_name2(const char *path, + const char *directory_name, char *sub_directory_name); +void eb_fix_path_name_suffix(char *path_name, const char *suffix); +EB_Error_Code eb_find_file_name(const char *path_name, + const char *target_file_name, char *found_file_name); +EB_Error_Code eb_find_file_name2(const char *path_name, + const char *sub_directory_name, const char *target_file_name, + char *found_file_name); +EB_Error_Code eb_find_file_name3(const char *path_name, + const char *sub_directory_name, const char *sub2_directory_name, + const char *target_file_name, char *found_file_name); +void eb_compose_path_name(const char *path_name, const char *file_name, + char *composed_path_name); +void eb_compose_path_name2(const char *path_name, + const char *sub_directory_name, const char *file_name, + char *composed_path_name); +void eb_compose_path_name3(const char *path_name, + const char *sub_directory_name, const char *sub2_directory_name, + const char *file_name, char *composed_path_name); +void eb_path_name_zio_code(const char *path_name, Zio_Code default_zio_code, + Zio_Code *zio_code); + +/* font.c */ +void eb_initialize_fonts(EB_Book *book); +void eb_load_font_headers(EB_Book *book); +void eb_finalize_fonts(EB_Book *book); + +/* hook.c */ +void eb_initialize_default_hookset(void); + +/* jacode.c */ +void eb_jisx0208_to_euc(char *out_string, const char *in_string); +void eb_sjis_to_euc(char *out_string, const char *in_string); + +/* lock.c */ +#ifdef ENABLE_PTHREAD +void eb_initialize_lock(EB_Lock *lock); +void eb_finalize_lock(EB_Lock *lock); +void eb_lock(EB_Lock *lock); +void eb_unlock(EB_Lock *lock); +#else /* not ENABLE_PTHREAD */ +#define eb_lock(x) +#define eb_unlock(x) +#define eb_initialize_lock(x) +#define eb_finalize_lock(x) +#endif /* not ENABLE_PTHREAD */ + +/* log.c */ +void eb_initialize_log(void); +const char *eb_quoted_stream(const char *stream, size_t stream_length); +const char *eb_quoted_string(const char *string); + +/* match.c */ +int eb_match_word(const char *word, const char *pattern, size_t length); +int eb_pre_match_word(const char *word, const char *pattern, size_t length); +int eb_exact_match_word_jis(const char *word, const char *pattern, + size_t length); +int eb_exact_pre_match_word_jis(const char *word, const char *pattern, + size_t length); +int eb_exact_match_word_latin(const char *word, const char *pattern, + size_t length); +int eb_exact_pre_match_word_latin(const char *word, const char *pattern, + size_t); +int eb_match_word_kana_single(const char *word, const char *pattern, + size_t length); +int eb_match_word_kana_group(const char *word, const char *pattern, + size_t length); +int eb_exact_match_word_kana_single(const char *word, const char *pattern, + size_t length); +int eb_exact_match_word_kana_group(const char *word, const char *pattern, + size_t length); + +/* message.c */ +EB_Error_Code eb_initialize_messages(EB_Book *book); + +/* multi.c */ +EB_Error_Code eb_load_multi_searches(EB_Book *book); +EB_Error_Code eb_load_multi_titles(EB_Book *book); + +/* narwfont.c */ +EB_Error_Code eb_open_narrow_font_file(EB_Book *book, EB_Font_Code font_code); +EB_Error_Code eb_load_narrow_font_header(EB_Book *book, + EB_Font_Code font_code); +EB_Error_Code eb_load_narrow_font_glyphs(EB_Book *book, + EB_Font_Code font_code); + +/* search.c */ +void eb_initialize_search_contexts(EB_Book *book); +void eb_finalize_search_contexts(EB_Book *book); +void eb_reset_search_contexts(EB_Book *book); +void eb_initialize_search(EB_Search *search); +void eb_finalize_search(EB_Search *search); +void eb_initialize_searches(EB_Book *book); +void eb_finalize_searches(EB_Book *book); +EB_Error_Code eb_presearch_word(EB_Book *book, EB_Search_Context *context); + +/* setword.c */ +EB_Error_Code eb_set_word(EB_Book *book, const char *input_word, char *word, + char *canonicalized_word, EB_Word_Code *word_code); +EB_Error_Code eb_set_endword(EB_Book *book, const char *input_word, char *word, + char *canonicalized_word, EB_Word_Code *word_code); +EB_Error_Code eb_set_keyword(EB_Book *book, const char *input_word, char *word, + char *canonicalized_word, EB_Word_Code *word_code); +EB_Error_Code eb_set_multiword(EB_Book *book, EB_Multi_Search_Code multi_id, + EB_Multi_Entry_Code entry_id, const char *input_word, char *word, + char *canonicalized_word, EB_Word_Code *word_code); + +/* subbook.c */ +void eb_initialize_subbooks(EB_Book *book); +void eb_finalize_subbooks(EB_Book *book); + +/* text.c */ +void eb_initialize_text_context(EB_Book *book); +void eb_finalize_text_context(EB_Book *book); +void eb_reset_text_context(EB_Book *book); +void eb_invalidate_text_context(EB_Book *book); +EB_Error_Code eb_forward_heading(EB_Book *book); + +/* widefont.c */ +EB_Error_Code eb_open_wide_font_file(EB_Book *book, EB_Font_Code font_code); +EB_Error_Code eb_load_wide_font_header(EB_Book *book, EB_Font_Code font_code); +EB_Error_Code eb_load_wide_font_glyphs(EB_Book *book, EB_Font_Code font_code); + +/* strcasecmp.c */ +int eb_strcasecmp(const char *string1, const char *string2); +int eb_strncasecmp(const char *string1, const char *string2, size_t n); + +#endif /* not EB_BUILD_POST_H */ diff --git a/build-pre.h b/build-pre.h new file mode 100644 index 0000000..5ac9cd2 --- /dev/null +++ b/build-pre.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2000-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. + */ + +#ifndef EB_BUILD_PRE_H +#define EB_BUILD_PRE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Mutual exclusion lock of Pthreads. + */ +#ifndef ENABLE_PTHREAD +#define pthread_mutex_lock(m) +#define pthread_mutex_unlock(m) +#endif + +/* + * stat() macros. + */ +#ifndef S_ISREG +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#ifndef S_ISDIR +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +/* + * Flags for open(). + */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +/* + * Character type tests and conversions. + */ +#define ASCII_ISDIGIT(c) ('0' <= (c) && (c) <= '9') +#define ASCII_ISUPPER(c) ('A' <= (c) && (c) <= 'Z') +#define ASCII_ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define ASCII_ISALPHA(c) \ + (ASCII_ISUPPER(c) || ASCII_ISLOWER(c)) +#define ASCII_ISALNUM(c) \ + (ASCII_ISUPPER(c) || ASCII_ISLOWER(c) || ASCII_ISDIGIT(c)) +#define ASCII_ISXDIGIT(c) \ + (ASCII_ISDIGIT(c) || ('A' <= (c) && (c) <= 'F') || ('a' <= (c) && (c) <= 'f')) +#define ASCII_TOUPPER(c) (('a' <= (c) && (c) <= 'z') ? (c) - 0x20 : (c)) +#define ASCII_TOLOWER(c) (('A' <= (c) && (c) <= 'Z') ? (c) + 0x20 : (c)) + +/* + * Tricks for gettext. + */ +#ifdef ENABLE_NLS +#define _(string) gettext(string) +#ifdef gettext_noop +#define N_(string) gettext_noop(string) +#else +#define N_(string) (string) +#endif +#else +#define _(string) (string) +#define N_(string) (string) +#endif + +/* + * Fake missing function names. + */ +#ifndef HAVE_STRCASECMP +#define strcasecmp eb_strcasecmp +#define strncasecmp eb_strncasecmp +#endif + +#endif /* EB_BUILD_PRE_H */ diff --git a/build_eb.sh b/build_eb.sh deleted file mode 100755 index fb3b5f0..0000000 --- a/build_eb.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -pushd eb -./configure --disable-shared --disable-ebnet --disable-nls -pushd eb -make -popd -popd diff --git a/copyright.c b/copyright.c new file mode 100644 index 0000000..c996bd2 --- /dev/null +++ b/copyright.c @@ -0,0 +1,119 @@ +/* + * 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 "build-post.h" + +/* + * Examine whether the current subbook in `book' have a copyright + * notice or not. + */ +int +eb_have_copyright(EB_Book *book) +{ + eb_lock(&book->lock); + LOG(("in: eb_have_copyright(book=%d)", (int)book->code)); + + /* + * Check for the current status. + */ + if (book->subbook_current == NULL) + goto failed; + + /* + * Check for the index page of copyright notice. + */ + if (book->subbook_current->copyright.start_page == 0) + goto failed; + + LOG(("out: eb_have_copyright() = %d", 1)); + eb_unlock(&book->lock); + + return 1; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_have_copyright() = %d", 0)); + eb_unlock(&book->lock); + return 0; +} + + +/* + * Get a position of copyright notice. + */ +EB_Error_Code +eb_copyright(EB_Book *book, EB_Position *position) +{ + EB_Error_Code error_code; + int page; + + eb_lock(&book->lock); + LOG(("in: eb_copyright(book=%d)", (int)book->code)); + + /* + * Check for the current status. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Check for the page number of COPYRIGHT NOTICE. + */ + page = book->subbook_current->copyright.start_page; + if (page == 0) { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + + /* + * Copy the position to `position'. + */ + position->page = page; + position->offset = 0; + + LOG(("out: eb_copyright(position={%d,%d}) = %s", + position->page, position->offset, eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_copyright() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} diff --git a/cross.c b/cross.c new file mode 100644 index 0000000..63e3537 --- /dev/null +++ b/cross.c @@ -0,0 +1,188 @@ +/* + * 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 "build-post.h" + +/* + * Examine whether the current subbook in `book' supports `KEYWORD SEARCH' + * or not. + */ +int +eb_have_cross_search(EB_Book *book) +{ + eb_lock(&book->lock); + LOG(("in: eb_have_cross_search(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) + goto failed; + + if (book->subbook_current->cross.start_page == 0) + goto failed; + + LOG(("out: eb_have_cross_search() = %d", 1)); + eb_unlock(&book->lock); + + return 1; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_have_cross_search() = %d", 0)); + eb_unlock(&book->lock); + return 0; +} + + +/* + * Keyword search. + */ +EB_Error_Code +eb_search_cross(EB_Book *book, const char * const input_words[]) +{ + EB_Error_Code error_code; + EB_Search_Context *context; + EB_Word_Code word_code; + int word_count; + int i; + + /* + * Lock the book. + */ + eb_lock(&book->lock); + LOG(("in: eb_search_cross(book=%d, input_words=[below])", + (int)book->code)); + + if (eb_log_flag) { + for (i = 0; i < EB_MAX_KEYWORDS && input_words[i] != NULL; i++) { + LOG((" input_words[%d]=%s", i, + eb_quoted_string(input_words[i]))); + } + LOG((" input_words[%d]=NULL", i)); + } + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Check whether the current subbook has cross search. + */ + if (book->subbook_current->cross.start_page == 0) { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + + /* + * Attach a search context for each word, and pre-search the word. + */ + eb_reset_search_contexts(book); + word_count = 0; + + for (i = 0; i < EB_MAX_KEYWORDS; i++) { + if (input_words[i] == NULL) + break; + + /* + * Initialize search context. + */ + context = book->search_contexts + word_count; + context->code = EB_SEARCH_CROSS; + + /* + * Choose comparison functions. + */ + if (book->character_code == EB_CHARCODE_ISO8859_1) { + context->compare_pre = eb_pre_match_word; + context->compare_single = eb_match_word; + context->compare_group = eb_match_word; + } else { + context->compare_pre = eb_pre_match_word; + context->compare_single = eb_match_word; + context->compare_group = eb_match_word_kana_group; + } + context->page = book->subbook_current->cross.start_page; + + /* + * Make a fixed word and a canonicalized word to search from + * `input_words[i]'. + */ + error_code = eb_set_keyword(book, input_words[i], context->word, + context->canonicalized_word, &word_code); + if (error_code == EB_ERR_EMPTY_WORD) + continue; + else if (error_code != EB_SUCCESS) + goto failed; + + /* + * Pre-search. + */ + error_code = eb_presearch_word(book, context); + if (error_code != EB_SUCCESS) + goto failed; + + word_count++; + } + if (word_count == 0) { + error_code = EB_ERR_NO_WORD; + goto failed; + } else if (EB_MAX_KEYWORDS <= i && input_words[i] != NULL) { + error_code = EB_ERR_TOO_MANY_WORDS; + goto failed; + } + + /* + * Set `EB_SEARCH_NONE' to the rest unused search context. + */ + for (i = word_count; i < EB_MAX_KEYWORDS; i++) + (book->search_contexts + i)->code = EB_SEARCH_NONE; + + LOG(("out: eb_search_cross() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_reset_search_contexts(book); + LOG(("out: eb_search_cross() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} diff --git a/defs.h b/defs.h new file mode 100644 index 0000000..333403d --- /dev/null +++ b/defs.h @@ -0,0 +1,1006 @@ +/* -*- C -*- + * 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. + */ + +#ifndef EB_DEFS_H +#define EB_DEFS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "zio.h" + +#include + +/* + * Disc code + */ +#define EB_DISC_EB 0 +#define EB_DISC_EPWING 1 +#define EB_DISC_INVALID -1 + +/* + * Character codes. + */ +#define EB_CHARCODE_ISO8859_1 1 +#define EB_CHARCODE_JISX0208 2 +#define EB_CHARCODE_JISX0208_GB2312 3 +#define EB_CHARCODE_INVALID -1 + +/* + * Special book ID for cache to represent "no cache data for any book". + */ +#define EB_BOOK_NONE -1 + +/* + * Special disc code, subbook code, multi search ID, and multi search + * entry ID, for representing error state. + */ +#define EB_SUBBOOK_INVALID -1 +#define EB_MULTI_INVALID -1 + +/* + * Size of a page (The term `page' means `block' in JIS X 4081). + */ +#define EB_SIZE_PAGE 2048 + +/* + * Maximum length of a word to be searched. + */ +#define EB_MAX_WORD_LENGTH 255 + +/* + * Maximum length of an EB* book title. + */ +#define EB_MAX_EB_TITLE_LENGTH 30 + +/* + * Maximum length of an EPWING book title. + */ +#define EB_MAX_EPWING_TITLE_LENGTH 80 + +/* + * Maximum length of a book title. + */ +#define EB_MAX_TITLE_LENGTH 80 + +/* + * Maximum length of a word to be searched. + */ +#if defined(PATH_MAX) +#define EB_MAX_PATH_LENGTH PATH_MAX +#elif defined(MAXPATHLEN) +#define EB_MAX_PATH_LENGTH MAXPATHLEN +#else +#define EB_MAX_PATH_LENGTH 1024 +#endif + +/* + * Maximum length of a directory name. + */ +#define EB_MAX_DIRECTORY_NAME_LENGTH 8 + +/* + * Maximum length of a file name under a certain directory. + * prefix(8 chars) + '.' + suffix(3 chars) + ';' + digit(1 char) + */ +#define EB_MAX_FILE_NAME_LENGTH 14 + +/* + * Maximum length of a label for multi-search entry. + */ +#define EB_MAX_MULTI_LABEL_LENGTH 30 + +/* + * Maximum length of alternation text string for a private character. + */ +#define EB_MAX_ALTERNATION_TEXT_LENGTH 31 + +/* + * Maximum length of title for multi search. + */ +#define EB_MAX_MULTI_TITLE_LENGTH 32 + +/* + * Maximum number of font heights in a subbok. + */ +#define EB_MAX_FONTS 4 + +/* + * Maximum number of subbooks in a book. + */ +#define EB_MAX_SUBBOOKS 50 + +/* + * Maximum number of multi-search types in a subbook. + */ +#define EB_MAX_MULTI_SEARCHES 10 + +/* + * Maximum number of entries in a multi-search. + */ +#define EB_MAX_MULTI_ENTRIES 5 + +/* + * Maximum number of entries in a keyword search. + */ +#define EB_MAX_KEYWORDS EB_MAX_MULTI_ENTRIES + +/* + * Maximum number of entries in a cross search. + */ +#define EB_MAX_CROSS_ENTRIES EB_MAX_MULTI_ENTRIES + +/* + * Maximum number of characters for alternation cache. + */ +#define EB_MAX_ALTERNATION_CACHE 16 + +/* + * The number of text hooks. + */ +#define EB_NUMBER_OF_HOOKS 54 + +/* + * The number of search contexts required by a book. + */ +#define EB_NUMBER_OF_SEARCH_CONTEXTS EB_MAX_MULTI_ENTRIES + +/* + * Types for various codes. + */ +typedef int EB_Error_Code; +typedef int EB_Book_Code; +typedef int EB_Disc_Code; +typedef int EB_Case_Code; +typedef int EB_Suffix_Code; +typedef int EB_Character_Code; +typedef int EB_Font_Code; +typedef int EB_Word_Code; +typedef int EB_Subbook_Code; +typedef int EB_Index_Style_Code; +typedef int EB_Search_Code; +typedef int EB_Text_Code; +typedef int EB_Text_Status_Code; +typedef int EB_Multi_Search_Code; +typedef int EB_Hook_Code; +typedef int EB_Binary_Code; + +/* + * Typedef for Structures. + */ +typedef struct EB_Lock_Struct EB_Lock; +typedef struct EB_Position_Struct EB_Position; +typedef struct EB_Alternation_Cache_Struct EB_Alternation_Cache; +typedef struct EB_Appendix_Subbook_Struct EB_Appendix_Subbook; +typedef struct EB_Appendix_Struct EB_Appendix; +typedef struct EB_Font_Struct EB_Font; +typedef struct EB_Search_Struct EB_Search; +typedef struct EB_Multi_Search_Struct EB_Multi_Search; +typedef struct EB_Subbook_Struct EB_Subbook; +typedef struct EB_Text_Context_Struct EB_Text_Context; +typedef struct EB_Binary_Context_Struct EB_Binary_Context; +typedef struct EB_Search_Context_Struct EB_Search_Context; +typedef struct EB_Book_Struct EB_Book; +typedef struct EB_Hit_Struct EB_Hit; +typedef struct EB_Hook_Struct EB_Hook; +typedef struct EB_Hookset_Struct EB_Hookset; +typedef struct EB_BookList_Entry EB_BookList_Entry; +typedef struct EB_BookList EB_BookList; + +/* + * Pthreads lock. + */ +struct EB_Lock_Struct { + /* + * Lock count. (For emulating recursive lock). + */ + int lock_count; + + /* + * Mutex for `lock_count'. + */ + pthread_mutex_t lock_count_mutex; + + /* + * Mutex for struct entity. + */ + pthread_mutex_t entity_mutex; +}; + +/* + * A pair of page and offset. + */ +struct EB_Position_Struct { + /* + * Page. (1, 2, 3 ...) + */ + int page; + + /* + * Offset in `page'. (0 ... 2047) + */ + int offset; +}; + +/* + * Chace of aternation text. + */ +struct EB_Alternation_Cache_Struct { + /* + * Character number. + */ + int character_number; + + /* + * Alternation string for `char_no'. + */ + char text[EB_MAX_ALTERNATION_TEXT_LENGTH + 1]; +}; + +/* + * An appendix for a subbook. + */ +struct EB_Appendix_Subbook_Struct { + + /* + * Initialization flag. + */ + int initialized; + + /* + * Subbook ID. + */ + EB_Subbook_Code code; + + /* + * Directory name. + */ + char directory_name[EB_MAX_DIRECTORY_NAME_LENGTH + 1]; + + /* + * Sub-directory name. (EPWING only) + */ + char data_directory_name[EB_MAX_DIRECTORY_NAME_LENGTH + 1]; + + /* + * File name. + */ + char file_name[EB_MAX_FILE_NAME_LENGTH + 1]; + + /* + * Character code of the book. + */ + EB_Character_Code character_code; + + /* + * Start character number of the narrow/wide font. + */ + int narrow_start; + int wide_start; + + /* + * End character number of the narrow/wide font. + */ + int narrow_end; + int wide_end; + + /* + * Start page number of the narrow/wide font. + */ + int narrow_page; + int wide_page; + + /* + * Stop code (first and second characters). + */ + int stop_code0; + int stop_code1; + + /* + * Compression Information for appendix file. + */ + Zio zio; +}; + +/* + * Additional resources for a book. + */ +struct EB_Appendix_Struct { + /* + * Book ID. + */ + EB_Book_Code code; + + /* + * Path of the book. + */ + char *path; + + /* + * The length of the path. + */ + size_t path_length; + + /* + * Disc type. EB (EB/EBG/EBXA/EBXA-C/S-EBXA) or EPWING. + */ + EB_Disc_Code disc_code; + + /* + * The number of subbooks the book has. + */ + int subbook_count; + + /* + * Subbook list. + */ + EB_Appendix_Subbook *subbooks; + + /* + * Current subbook. + */ + EB_Appendix_Subbook *subbook_current; + + /* + * Lock. + */ + EB_Lock lock; + + /* + * Cache table for alternation text. + */ + EB_Alternation_Cache narrow_cache[EB_MAX_ALTERNATION_CACHE]; + EB_Alternation_Cache wide_cache[EB_MAX_ALTERNATION_CACHE]; +}; + +/* + * A font in a subbook. + */ +struct EB_Font_Struct { + /* + * Font Code. + * This font is not available, if the code is EB_FONT_INVALID. + */ + EB_Font_Code font_code; + + /* + * Whether the object has been initialized. + */ + int initialized; + + /* + * Character numbers of the start and end of the font. + */ + int start; + int end; + + /* + * Page number of the start page of the font data. + * Used in EB* only. (In EPWING, it is alyways 1). + */ + int page; + + /* + * File name of the font. (EPWING only) + */ + char file_name[EB_MAX_FILE_NAME_LENGTH + 1]; + + /* + * Font data cache. + */ + char *glyphs; + + /* + * Compression Information. + */ + Zio zio; +}; + +/* + * Search methods in a subbook. + */ +struct EB_Search_Struct { + /* + * Index ID. + */ + int index_id; + + /* + * Page number of the start page of an index. + * This search method is not available, if `start_page' is 0, + */ + int start_page; + int end_page; + + /* + * Page number of the start page of candidates. + * (for multi search entry) + */ + int candidates_page; + + /* + * Index style flags. + */ + EB_Index_Style_Code katakana; + EB_Index_Style_Code lower; + EB_Index_Style_Code mark; + EB_Index_Style_Code long_vowel; + EB_Index_Style_Code double_consonant; + EB_Index_Style_Code contracted_sound; + EB_Index_Style_Code voiced_consonant; + EB_Index_Style_Code small_vowel; + EB_Index_Style_Code p_sound; + EB_Index_Style_Code space; + + /* + * Label. (for an entry in multi search) + */ + char label[EB_MAX_MULTI_LABEL_LENGTH + 1]; +}; + +/* + * A multi-search entry in a subbook. + */ +struct EB_Multi_Search_Struct { + /* + * Search method information. + */ + EB_Search search; + + /* + * Search title. (EPWING only) + */ + char title[EB_MAX_MULTI_TITLE_LENGTH + 1]; + + /* + * The number of entries the multi search has. + */ + int entry_count; + + /* + * List of Word entry information. + */ + EB_Search entries[EB_MAX_MULTI_ENTRIES]; +}; + +/* + * A subbook in a book. + */ +struct EB_Subbook_Struct { + /* + * Whether the object has been initialized. + */ + int initialized; + + /* + * Index page. + */ + int index_page; + + /* + * Subbook ID. + * This subbook is not available, if the code is EB_SUBBOOK_INVALID. + */ + EB_Subbook_Code code; + + /* + * File descriptor and compression information for text file. + */ + Zio text_zio; + + /* + * File descriptor and compression information for graphic file. + */ + Zio graphic_zio; + + /* + * File descriptor and compression information for sound file. + */ + Zio sound_zio; + + /* + * File descriptor and compression information for movie file. + */ + Zio movie_zio; + + /* + * Title of the subbook. + */ + char title[EB_MAX_TITLE_LENGTH + 1]; + + /* + * Subbook directory name. + */ + char directory_name[EB_MAX_DIRECTORY_NAME_LENGTH + 1]; + + /* + * Sub-directory names. (EPWING only) + */ + char data_directory_name[EB_MAX_DIRECTORY_NAME_LENGTH + 1]; + char gaiji_directory_name[EB_MAX_DIRECTORY_NAME_LENGTH + 1]; + char movie_directory_name[EB_MAX_DIRECTORY_NAME_LENGTH + 1]; + + /* + * File names. + */ + char text_file_name[EB_MAX_FILE_NAME_LENGTH + 1]; + char graphic_file_name[EB_MAX_FILE_NAME_LENGTH + 1]; + char sound_file_name[EB_MAX_FILE_NAME_LENGTH + 1]; + + /* + * Compression hints of Text, graphic and sound files. + * (temporary need, EPWING only). + */ + Zio_Code text_hint_zio_code; + Zio_Code graphic_hint_zio_code; + Zio_Code sound_hint_zio_code; + + /* + * Page number where search method titles are stored. + * (temporary need, EPWING only). + */ + int search_title_page; + + /* + * The top page of search methods. + */ + EB_Search word_alphabet; + EB_Search word_asis; + EB_Search word_kana; + EB_Search endword_alphabet; + EB_Search endword_asis; + EB_Search endword_kana; + EB_Search keyword; + EB_Search menu; + EB_Search image_menu; + EB_Search cross; + EB_Search copyright; + EB_Search text; + EB_Search sound; + + /* + * The number of multi-search methods the subbook has. + */ + int multi_count; + + /* + * The top page of multi search methods. + */ + EB_Multi_Search multis[EB_MAX_MULTI_SEARCHES]; + + /* + * Font list. + */ + EB_Font narrow_fonts[EB_MAX_FONTS]; + EB_Font wide_fonts[EB_MAX_FONTS]; + + /* + * Current narrow and wide fonts. + */ + EB_Font *narrow_current; + EB_Font *wide_current; +}; + +/* + * Length of cache buffer in a binary context. + * It must be greater than 38, size of GIF preamble. + * It must be greater than 44, size of WAVE sound header. + * It must be greater than 118, size of BMP header + info + 16 rgbquads. + */ +#define EB_SIZE_BINARY_CACHE_BUFFER 128 + +/* + * Context parameters for binary data. + */ +struct EB_Binary_Context_Struct { + /* + * Binary type ID. + * The context is not active, if this code is EB_BINARY_INVALID. + */ + EB_Binary_Code code; + + /* + * Compress information. + */ + Zio *zio; + + /* + * Location of the the binary data, relative to the start of the file. + */ + off_t location; + + /* + * Data size. + * Size zero means that the binary has no size information. + */ + size_t size; + + /* + * The current offset of binary data. + */ + size_t offset; + + /* + * Cache buffer. + */ + char cache_buffer[EB_SIZE_BINARY_CACHE_BUFFER]; + + /* + * Length of cached data. + */ + size_t cache_length; + + /* + * Current offset of cached data. + */ + size_t cache_offset; + + /* + * Width of Image. (monochrome graphic only) + */ + int width; +}; + +/* + * Context parameters for text reading. + */ +struct EB_Text_Context_Struct { + /* + * Current text content type. + * The context is not active, if this code is EB_TEXT_INVALID. + */ + EB_Text_Code code; + + /* + * Current offset pointer of the START or HONMON file. + */ + off_t location; + + /* + * The current point of a buffer on which text is written. + */ + char *out; + + /* + * Length of `out'. + */ + size_t out_rest_length; + + /* + * Unprocessed string that a hook function writes on text. + */ + char *unprocessed; + + /* + * Size of `unprocessed'. + */ + size_t unprocessed_size; + + /* + * Length of the current output text phrase. + */ + size_t out_step; + + /* + * Narrow character region flag. + */ + int narrow_flag; + + /* + * Whether a printable character has been appeared in the current + * text content. + */ + int printable_count; + + /* + * EOF flag of the current subbook. + */ + int file_end_flag; + + /* + * Status of the current text processing. + */ + EB_Text_Status_Code text_status; + + /* + * Skip until `skipcode' appears. + */ + int skip_code; + + /* + * Stop-code automatically set by EB Library. + */ + int auto_stop_code; + + /* + * The current candidate word for multi search. + */ + char candidate[EB_MAX_WORD_LENGTH + 1]; + + /* + * Whether the current text point is in the candidate word or not. + */ + int is_candidate; + + /* + * Whether the current text point is in EBXA-C gaiji area. + */ + int ebxac_gaiji_flag; +}; + +/* + * Context parameters for word search. + */ +struct EB_Search_Context_Struct { + /* + * Current search method type. + * The context is not active, if this code is EB_SEARCH_NONE. + */ + EB_Search_Code code; + + /* + * Function which compares word to search and pattern in an index page. + */ + int (*compare_pre)(const char *word, const char *pattern, + size_t length); + int (*compare_single)(const char *word, const char *pattern, + size_t length); + int (*compare_group)(const char *word, const char *pattern, + size_t length); + + /* + * Result of comparison by `compare'. + */ + int comparison_result; + + /* + * Word to search. + */ + char word[EB_MAX_WORD_LENGTH + 1]; + + /* + * Canonicalized word to search. + */ + char canonicalized_word[EB_MAX_WORD_LENGTH + 1]; + + /* + * Page which is searched currently. + */ + int page; + + /* + * Offset which is searched currently in the page. + */ + int offset; + + /* + * Page ID of the current page. + */ + int page_id; + + /* + * How many entries in the current page. + */ + int entry_count; + + /* + * Entry index pointer. + */ + int entry_index; + + /* + * Length of the current entry. + */ + int entry_length; + + /* + * Arrangement style of entries in the current page (fixed or variable). + */ + int entry_arrangement; + + /* + * In a group entry or not. + */ + int in_group_entry; + + /* + * Current heading position (for keyword search). + */ + EB_Position keyword_heading; +}; + +/* + * A book. + */ +struct EB_Book_Struct { + /* + * Book ID. + */ + EB_Book_Code code; + + /* + * Disc type. EB* or EPWING. + */ + EB_Disc_Code disc_code; + + /* + * Character code of the book. + */ + EB_Character_Code character_code; + + /* + * Path of the book. + */ + char *path; + + /* + * The length of the path. + */ + size_t path_length; + + /* + * The number of subbooks the book has. + */ + int subbook_count; + + /* + * Subbook list. + */ + EB_Subbook *subbooks; + + /* + * Current subbook. + */ + EB_Subbook *subbook_current; + + /* + * Context parameters for text reading. + */ + EB_Text_Context text_context; + + /* + * Context parameters for binary reading. + */ + EB_Binary_Context binary_context; + + /* + * Context parameters for text reading. + */ + EB_Search_Context search_contexts[EB_NUMBER_OF_SEARCH_CONTEXTS]; + + /* + * Lock. + */ + EB_Lock lock; +}; + +/* + * In a word search, heading and text locations of a matched entry + * are stored. + */ +struct EB_Hit_Struct { + /* + * Heading position. + */ + EB_Position heading; + + /* + * Text position. + */ + EB_Position text; +}; + +/* + * A text hook. + */ +struct EB_Hook_Struct { + /* + * Hook code. + */ + EB_Hook_Code code; + + /* + * Hook function for the hook code `code'. + */ + EB_Error_Code (*function)(EB_Book *book, EB_Appendix *appendix, + void *container, EB_Hook_Code hook_code, int argc, + const unsigned int *argv); +}; + +/* + * A set of text hooks. + */ +struct EB_Hookset_Struct { + /* + * List of hooks. + */ + EB_Hook hooks[EB_NUMBER_OF_HOOKS]; + + /* + * Lock. + */ + EB_Lock lock; +}; + +/* + * An entry of book list. + */ +struct EB_BookList_Entry { + /* + * name. + */ + char *name; + + /* + * Title. + */ + char *title; +}; + +/* + * Book list. + */ +struct EB_BookList { + /* + * Book List ID. + */ + EB_Book_Code code; + + /* + * The number of book entries this list has. + */ + int entry_count; + + /* + * The maximum number of book entries that `entries' can memory. + */ + int max_entry_count; + + /* + * Book entries. + */ + EB_BookList_Entry *entries; + + /* + * Lock. + */ + EB_Lock lock; +}; + +/* for backward compatibility */ +#define EB_Multi_Entry_Code int + +#ifdef __cplusplus +} +#endif + +#endif /* not EB_DEFS_H */ diff --git a/eb b/eb deleted file mode 160000 index c9d1015..0000000 --- a/eb +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c9d1015f9ec4fdc3936f8d3905ccb7b1145eb1cf diff --git a/eb.c b/eb.c new file mode 100644 index 0000000..d17f53d --- /dev/null +++ b/eb.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2000-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 "build-post.h" + +/* + * Initialize the library. + */ +EB_Error_Code +eb_initialize_library(void) +{ + EB_Error_Code error_code; + + eb_initialize_log(); + + LOG(("in: eb_initialize_library()")); + + eb_initialize_default_hookset(); + if (zio_initialize_library() < 0) { + error_code = EB_ERR_MEMORY_EXHAUSTED; + goto failed; + } + + LOG(("out: eb_initialize_library() = %s", eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_initialize_library() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Finalize the library. + */ +void +eb_finalize_library(void) +{ + LOG(("in: eb_finalize_library()")); + + zio_finalize_library(); + + LOG(("out: eb_finalize_library()")); +} diff --git a/eb.h b/eb.h new file mode 100644 index 0000000..61003d9 --- /dev/null +++ b/eb.h @@ -0,0 +1,157 @@ +/* -*- C -*- + * 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. + */ + +#ifndef EB_EB_H +#define EB_EB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "defs.h" + +#include + +/* + * Function declarations. + */ +/* book.c */ +void eb_initialize_book(EB_Book *book); +EB_Error_Code eb_bind(EB_Book *book, const char *path); +void eb_finalize_book(EB_Book *book); +int eb_is_bound(EB_Book *book); +EB_Error_Code eb_path(EB_Book *book, char *path); +EB_Error_Code eb_disc_type(EB_Book *book, EB_Disc_Code *disc_code); +EB_Error_Code eb_character_code(EB_Book *book, + EB_Character_Code *character_code); + +/* copyright.h */ +int eb_have_copyright(EB_Book *book); +EB_Error_Code eb_copyright(EB_Book *book, EB_Position *position); +EB_Error_Code eb_search_cross(EB_Book *book, + const char * const input_words[]); + +/* cross.c */ +int eb_have_cross_search(EB_Book *book); + +/* eb.c */ +EB_Error_Code eb_initialize_library(void); +void eb_finalize_library(void); + +/* endword.c */ +int eb_have_endword_search(EB_Book *book); +EB_Error_Code eb_search_endword(EB_Book *book, const char *input_word); + +/* exactword.c */ +int eb_have_exactword_search(EB_Book *book); +EB_Error_Code eb_search_exactword(EB_Book *book, const char *input_word); + +/* graphic.c */ +int eb_have_graphic_search(EB_Book *book); + +/* keyword.c */ +int eb_have_keyword_search(EB_Book *book); +EB_Error_Code eb_search_keyword(EB_Book *book, + const char * const input_words[]); + +/* lock.c */ +int eb_pthread_enabled(void); + +/* log.c */ +void eb_set_log_function(void (*function)(const char *message, va_list ap)); +void eb_enable_log(void); +void eb_disable_log(void); +void eb_log(const char *message, ...); +void eb_log_stderr(const char *message, va_list ap); + +/* menu.c */ +int eb_have_menu(EB_Book *book); +EB_Error_Code eb_menu(EB_Book *book, EB_Position *position); +int eb_have_image_menu(EB_Book *book); +EB_Error_Code eb_image_menu(EB_Book *book, EB_Position *position); + +/* multi.c */ +int eb_have_multi_search(EB_Book *book); +EB_Error_Code eb_multi_title(EB_Book *book, EB_Multi_Search_Code multi_id, + char *title); +EB_Error_Code eb_multi_search_list(EB_Book *book, + EB_Multi_Search_Code *search_list, int *search_count); +EB_Error_Code eb_multi_entry_count(EB_Book *book, + EB_Multi_Search_Code multi_id, int *entry_count); +EB_Error_Code eb_multi_entry_list(EB_Book *book, + EB_Multi_Search_Code multi_id, int *entry_list, int *entry_count); +EB_Error_Code eb_multi_entry_label(EB_Book *book, + EB_Multi_Search_Code multi_id, int entry_index, char *label); +int eb_multi_entry_have_candidates(EB_Book *book, + EB_Multi_Search_Code multi_id, int entry_index); +EB_Error_Code eb_multi_entry_candidates(EB_Book *book, + EB_Multi_Search_Code multi_id, int entry_index, EB_Position *position); +EB_Error_Code eb_search_multi(EB_Book *book, EB_Multi_Search_Code multi_id, + const char * const input_words[]); + +/* text.c */ +int eb_have_text(EB_Book *book); +EB_Error_Code eb_text(EB_Book *book, EB_Position *position); + +/* search.c */ +EB_Error_Code eb_hit_list(EB_Book *book, int max_hit_count, EB_Hit *hit_list, + int *hit_count); + +/* subbook.c */ +EB_Error_Code eb_load_all_subbooks(EB_Book *book); +EB_Error_Code eb_subbook_list(EB_Book *book, EB_Subbook_Code *subbook_list, + int *subbook_count); +EB_Error_Code eb_subbook(EB_Book *book, EB_Subbook_Code *subbook_code); +EB_Error_Code eb_subbook_title(EB_Book *book, char *title); +EB_Error_Code eb_subbook_title2(EB_Book *book, EB_Subbook_Code subbook_code, + char *title); +EB_Error_Code eb_subbook_directory(EB_Book *book, char *directory); +EB_Error_Code eb_subbook_directory2(EB_Book *book, + EB_Subbook_Code subbook_code, char *directory); +EB_Error_Code eb_set_subbook(EB_Book *book, EB_Subbook_Code subbook_code); +void eb_unset_subbook(EB_Book *book); + +/* word.c */ +int eb_have_word_search(EB_Book *book); +EB_Error_Code eb_search_word(EB_Book *book, const char *input_word); + +/* all.c */ +int eb_have_all_search(EB_Book *book); +EB_Error_Code eb_search_all_alphabet(EB_Book* book); +EB_Error_Code eb_search_all_kana(EB_Book* book); +EB_Error_Code eb_search_all_asis(EB_Book* book); + +/* for backward compatibility */ +#define eb_suspend eb_unset_subbook +#define eb_initialize_all_subbooks eb_load_all_subbooks + +#ifdef __cplusplus +} +#endif + +#endif /* not EB_EB_H */ diff --git a/endword.c b/endword.c new file mode 100644 index 0000000..e99c1a0 --- /dev/null +++ b/endword.c @@ -0,0 +1,189 @@ +/* + * 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 "build-post.h" + +/* + * Examine whether the current subbook in `book' supports `ENDWORD SEARCH' + * or not. + */ +int +eb_have_endword_search(EB_Book *book) +{ + eb_lock(&book->lock); + LOG(("in: eb_have_endword_search(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) + goto failed; + + /* + * Check for the index page of endword search. + */ + if (book->subbook_current->endword_alphabet.start_page == 0 + && book->subbook_current->endword_asis.start_page == 0 + && book->subbook_current->endword_kana.start_page == 0) + goto failed; + + LOG(("out: eb_have_endword_search() = %d", 1)); + eb_unlock(&book->lock); + + return 1; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_have_endword_search() = %d", 0)); + eb_unlock(&book->lock); + return 0; +} + + +/* + * Endword search. + */ +EB_Error_Code +eb_search_endword(EB_Book *book, const char *input_word) +{ + EB_Error_Code error_code; + EB_Word_Code word_code; + EB_Search_Context *context; + + eb_lock(&book->lock); + LOG(("in: eb_search_endword(book=%d, input_word=%s)", (int)book->code, + eb_quoted_string(input_word))); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Initialize search context. + */ + eb_reset_search_contexts(book); + context = book->search_contexts; + context->code = EB_SEARCH_ENDWORD; + + /* + * Make a fixed word and a canonicalized word to search from + * `input_word'. + */ + error_code = eb_set_endword(book, input_word, context->word, + context->canonicalized_word, &word_code); + if (error_code != EB_SUCCESS) + goto failed; + + /* + * Get a page number. + */ + switch (word_code) { + case EB_WORD_ALPHABET: + if (book->subbook_current->endword_alphabet.start_page != 0) + context->page = book->subbook_current->endword_alphabet.start_page; + else if (book->subbook_current->endword_asis.start_page != 0) + context->page = book->subbook_current->endword_asis.start_page; + else { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + break; + + case EB_WORD_KANA: + if (book->subbook_current->endword_kana.start_page != 0) + context->page = book->subbook_current->endword_kana.start_page; + else if (book->subbook_current->endword_asis.start_page != 0) + context->page = book->subbook_current->endword_asis.start_page; + else { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + break; + + case EB_WORD_OTHER: + if (book->subbook_current->endword_asis.start_page != 0) + context->page = book->subbook_current->endword_asis.start_page; + else { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + break; + + default: + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + + /* + * Choose comparison functions. + */ + + if (book->character_code == EB_CHARCODE_ISO8859_1) { + context->compare_pre = eb_pre_match_word; + context->compare_single = eb_match_word; + context->compare_group = eb_match_word; + } else if (context->page == book->subbook_current->word_kana.start_page) { + context->compare_pre = eb_pre_match_word; + context->compare_single = eb_match_word_kana_single; + context->compare_group = eb_match_word_kana_group; + } else { + context->compare_pre = eb_pre_match_word; + context->compare_single = eb_match_word; + context->compare_group = eb_match_word_kana_group; + } + + /* + * Pre-search. + */ + error_code = eb_presearch_word(book, context); + if (error_code != EB_SUCCESS) + goto failed; + + LOG(("out: eb_search_endword() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_reset_search_contexts(book); + LOG(("out: eb_search_endword() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} diff --git a/error.c b/error.c new file mode 100644 index 0000000..c6a8bbd --- /dev/null +++ b/error.c @@ -0,0 +1,277 @@ +/* + * 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 "build-post.h" + +/* + * Error code strings. + */ +static const char * const error_strings[] = { + /* 0 -- 4 */ + "EB_SUCCESS", + "EB_ERR_MEMORY_EXHAUSTED", + "EB_ERR_EMPTY_FILE_NAME", + "EB_ERR_TOO_LONG_FILE_NAME", + "EB_ERR_BAD_FILE_NAME", + + /* 5 -- 9 */ + "EB_ERR_BAD_DIR_NAME", + "EB_ERR_TOO_LONG_WORD", + "EB_ERR_BAD_WORD", + "EB_ERR_EMPTY_WORD", + "EB_ERR_FAIL_GETCWD", + + /* 10 -- 14 */ + "EB_ERR_FAIL_OPEN_CAT", + "EB_ERR_FAIL_OPEN_CATAPP", + "EB_ERR_FAIL_OPEN_TEXT", + "EB_ERR_FAIL_OPEN_FONT", + "EB_ERR_FAIL_OPEN_APP", + + /* 15 -- 19 */ + "EB_ERR_FAIL_OPEN_BINARY", + "EB_ERR_FAIL_READ_CAT", + "EB_ERR_FAIL_READ_CATAPP", + "EB_ERR_FAIL_READ_TEXT", + "EB_ERR_FAIL_READ_FONT", + + /* 20 -- 24 */ + "EB_ERR_FAIL_READ_APP", + "EB_ERR_FAIL_READ_BINARY", + "EB_ERR_FAIL_SEEK_CAT", + "EB_ERR_FAIL_SEEK_CATAPP", + "EB_ERR_FAIL_SEEK_TEXT", + + /* 25 -- 29 */ + "EB_ERR_FAIL_SEEK_FONT", + "EB_ERR_FAIL_SEEK_APP", + "EB_ERR_FAIL_SEEK_BINARY", + "EB_ERR_UNEXP_CAT", + "EB_ERR_UNEXP_CATAPP", + + /* 30 -- 34 */ + "EB_ERR_UNEXP_TEXT", + "EB_ERR_UNEXP_FONT", + "EB_ERR_UNEXP_APP", + "EB_ERR_UNEXP_BINARY", + "EB_ERR_UNBOUND_BOOK", + + /* 35 -- 39 */ + "EB_ERR_UNBOUND_APP", + "EB_ERR_NO_SUB", + "EB_ERR_NO_APPSUB", + "EB_ERR_NO_FONT", + "EB_ERR_NO_TEXT", + + /* 40 -- 44 */ + "EB_ERR_NO_STOPCODE", + "EB_ERR_NO_ALT", + "EB_ERR_NO_CUR_SUB", + "EB_ERR_NO_CUR_APPSUB", + "EB_ERR_NO_CUR_FONT", + + /* 45 -- 49 */ + "EB_ERR_NO_CUR_BINARY", + "EB_ERR_NO_SUCH_SUB", + "EB_ERR_NO_SUCH_APPSUB", + "EB_ERR_NO_SUCH_FONT", + "EB_ERR_NO_SUCH_CHAR_BMP", + + /* 50 -- 54 */ + "EB_ERR_NO_SUCH_CHAR_TEXT", + "EB_ERR_NO_SUCH_SEARCH", + "EB_ERR_NO_SUCH_HOOK", + "EB_ERR_NO_SUCH_BINARY", + "EB_ERR_DIFF_CONTENT", + + /* 55 -- 59 */ + "EB_ERR_NO_PREV_SEARCH", + "EB_ERR_NO_SUCH_MULTI_ID", + "EB_ERR_NO_SUCH_ENTRY_ID", + "EB_ERR_TOO_MANY_WORDS", + "EB_ERR_NO_WORD", + + /* 60 -- 64 */ + "EB_ERR_NO_CANDIDATES", + "EB_ERR_END_OF_CONTENT", + "EB_ERR_NO_PREV_SEEK", + "EB_ERR_EBNET_UNSUPPORTED", + "EB_ERR_EBNET_FAIL_CONNECT", + + /* 65 -- 69 */ + "EB_ERR_EBNET_SERVER_BUSY", + "EB_ERR_EBNET_NO_PERMISSION", + "EB_ERR_UNBOUND_BOOKLIST", + "EB_ERR_NO_SUCH_BOOK", + + NULL +}; + +/* + * Look up the error message corresponding to the error code `error_code'. + */ +const char * +eb_error_string(EB_Error_Code error_code) +{ + const char *string; + + if (0 <= error_code && error_code < EB_NUMBER_OF_ERRORS) + string = error_strings[error_code]; + else + string = "EB_ERR_UNKNOWN"; + + return string; +} + + +/* + * Error messages. + */ +static const char * const error_messages[] = { + /* 0 -- 4 */ + N_("no error"), + N_("memory exhausted"), + N_("an empty file name"), + N_("too long file name"), + N_("bad file name"), + + /* 5 -- 9 */ + N_("bad directory name"), + N_("too long word"), + N_("a word contains bad character"), + N_("an empty word"), + N_("failed to get the current working directory"), + + /* 10 -- 14 */ + N_("failed to open a catalog file"), + N_("failed to open an appendix catalog file"), + N_("failed to open a text file"), + N_("failed to open a font file"), + N_("failed to open an appendix file"), + + /* 15 -- 19 */ + N_("failed to open a binary file"), + N_("failed to read a catalog file"), + N_("failed to read an appendix catalog file"), + N_("failed to read a text file"), + N_("failed to read a font file"), + + /* 20 -- 24 */ + N_("failed to read an appendix file"), + N_("failed to read a binary file"), + N_("failed to seek a catalog file"), + N_("failed to seek an appendix catalog file"), + N_("failed to seek a text file"), + + /* 25 -- 29 */ + N_("failed to seek a font file"), + N_("failed to seek an appendix file"), + N_("failed to seek a binary file"), + N_("unexpected format in a catalog file"), + N_("unexpected format in an appendix catalog file"), + + /* 30 -- 34 */ + N_("unexpected format in a text file"), + N_("unexpected format in a font file"), + N_("unexpected format in an appendix file"), + N_("unexpected format in a binary file"), + N_("book not bound"), + + /* 35 -- 39 */ + N_("appendix not bound"), + N_("no subbook"), + N_("no subbook in the appendix"), + N_("no font"), + N_("no text file"), + + /* 40 -- 44 */ + N_("no stop-code"), + N_("no alternation string"), + N_("no current subbook"), + N_("no current appendix subbook"), + N_("no current font"), + + /* 45 -- 49 */ + N_("no current binary"), + N_("no such subbook"), + N_("no such appendix subbook"), + N_("no such font"), + N_("no such character bitmap"), + + /* 50 -- 54 */ + N_("no such character text"), + N_("no such search method"), + N_("no such hook"), + N_("no such binary"), + N_("different content type"), + + /* 55 -- 59 */ + N_("no previous search"), + N_("no such multi search"), + N_("no such multi search entry"), + N_("too many words specified"), + N_("no word specified"), + + /* 60 -- 64 */ + N_("no candidates"), + N_("end of content"), + N_("no previous seek"), + N_("ebnet is not supported"), + N_("failed to connect to an ebnet server"), + + /* 65 -- 69 */ + N_("ebnet server is busy"), + N_("no access permission"), + N_("booklist not bound"), + N_("no such book"), + + NULL +}; + +/* + * Look up the error message corresponding to the error code `error_code'. + */ +const char * +eb_error_message(EB_Error_Code error_code) +{ + const char *message; + + if (0 <= error_code && error_code < EB_NUMBER_OF_ERRORS) + message = error_messages[error_code]; + else + message = N_("unknown error"); + +#ifdef ENABLE_NLS + message = dgettext(EB_TEXT_DOMAIN_NAME, message); +#endif /* ENABLE_NLS */ + + return message; +} diff --git a/error.h b/error.h new file mode 100644 index 0000000..0f800ca --- /dev/null +++ b/error.h @@ -0,0 +1,142 @@ +/* -*- C -*- + * 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. + */ + +#ifndef EB_ERROR_H +#define EB_ERROR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "defs.h" + +/* + * Error codes. + */ +#define EB_SUCCESS 0 +#define EB_ERR_MEMORY_EXHAUSTED 1 +#define EB_ERR_EMPTY_FILE_NAME 2 +#define EB_ERR_TOO_LONG_FILE_NAME 3 +#define EB_ERR_BAD_FILE_NAME 4 + +#define EB_ERR_BAD_DIR_NAME 5 +#define EB_ERR_TOO_LONG_WORD 6 +#define EB_ERR_BAD_WORD 7 +#define EB_ERR_EMPTY_WORD 8 +#define EB_ERR_FAIL_GETCWD 9 + +#define EB_ERR_FAIL_OPEN_CAT 10 +#define EB_ERR_FAIL_OPEN_CATAPP 11 +#define EB_ERR_FAIL_OPEN_TEXT 12 +#define EB_ERR_FAIL_OPEN_FONT 13 +#define EB_ERR_FAIL_OPEN_APP 14 + +#define EB_ERR_FAIL_OPEN_BINARY 15 +#define EB_ERR_FAIL_READ_CAT 16 +#define EB_ERR_FAIL_READ_CATAPP 17 +#define EB_ERR_FAIL_READ_TEXT 18 +#define EB_ERR_FAIL_READ_FONT 19 + +#define EB_ERR_FAIL_READ_APP 20 +#define EB_ERR_FAIL_READ_BINARY 21 +#define EB_ERR_FAIL_SEEK_CAT 22 +#define EB_ERR_FAIL_SEEK_CATAPP 23 +#define EB_ERR_FAIL_SEEK_TEXT 24 + +#define EB_ERR_FAIL_SEEK_FONT 25 +#define EB_ERR_FAIL_SEEK_APP 26 +#define EB_ERR_FAIL_SEEK_BINARY 27 +#define EB_ERR_UNEXP_CAT 28 +#define EB_ERR_UNEXP_CATAPP 29 + +#define EB_ERR_UNEXP_TEXT 30 +#define EB_ERR_UNEXP_FONT 31 +#define EB_ERR_UNEXP_APP 32 +#define EB_ERR_UNEXP_BINARY 33 +#define EB_ERR_UNBOUND_BOOK 34 + +#define EB_ERR_UNBOUND_APP 35 +#define EB_ERR_NO_SUB 36 +#define EB_ERR_NO_APPSUB 37 +#define EB_ERR_NO_FONT 38 +#define EB_ERR_NO_TEXT 39 + +#define EB_ERR_NO_STOPCODE 40 +#define EB_ERR_NO_ALT 41 +#define EB_ERR_NO_CUR_SUB 42 +#define EB_ERR_NO_CUR_APPSUB 43 +#define EB_ERR_NO_CUR_FONT 44 + +#define EB_ERR_NO_CUR_BINARY 45 +#define EB_ERR_NO_SUCH_SUB 46 +#define EB_ERR_NO_SUCH_APPSUB 47 +#define EB_ERR_NO_SUCH_FONT 48 +#define EB_ERR_NO_SUCH_CHAR_BMP 49 + +#define EB_ERR_NO_SUCH_CHAR_TEXT 50 +#define EB_ERR_NO_SUCH_SEARCH 51 +#define EB_ERR_NO_SUCH_HOOK 52 +#define EB_ERR_NO_SUCH_BINARY 53 +#define EB_ERR_DIFF_CONTENT 54 + +#define EB_ERR_NO_PREV_SEARCH 55 +#define EB_ERR_NO_SUCH_MULTI_ID 56 +#define EB_ERR_NO_SUCH_ENTRY_ID 57 +#define EB_ERR_TOO_MANY_WORDS 58 +#define EB_ERR_NO_WORD 59 + +#define EB_ERR_NO_CANDIDATES 60 +#define EB_ERR_END_OF_CONTENT 61 +#define EB_ERR_NO_PREV_SEEK 62 + +#define EB_ERR_UNBOUND_BOOKLIST 67 +#define EB_ERR_NO_SUCH_BOOK 68 + + +/* + * The number of error codes. + */ +#define EB_NUMBER_OF_ERRORS 69 + +/* + * The maximum length of an error message. + */ +#define EB_MAX_ERROR_MESSAGE_LENGTH 127 + +/* + * Function declarations. + */ +/* error.c */ +const char *eb_error_string(EB_Error_Code error_code); +const char *eb_error_message(EB_Error_Code error_code); + +#ifdef __cplusplus +} +#endif + +#endif /* not EB_ERROR_H */ diff --git a/exactword.c b/exactword.c new file mode 100644 index 0000000..99190b7 --- /dev/null +++ b/exactword.c @@ -0,0 +1,188 @@ +/* + * 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 "build-post.h" + +/* + * Examine whether the current subbook in `book' supports `EXACTWORD SEARCH' + * or not. + */ +int +eb_have_exactword_search(EB_Book *book) +{ + eb_lock(&book->lock); + LOG(("in: eb_have_exactword_search(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) + goto failed; + + /* + * Check for the index page of word search. + */ + if (book->subbook_current->word_alphabet.start_page == 0 + && book->subbook_current->word_asis.start_page == 0 + && book->subbook_current->word_kana.start_page == 0) + goto failed; + + LOG(("out: eb_have_exactword_search() = %d", 1)); + eb_unlock(&book->lock); + + return 1; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_have_exactword_search() = %d", 0)); + eb_unlock(&book->lock); + return 0; +} + + +/* + * Exactword search. + */ +EB_Error_Code +eb_search_exactword(EB_Book *book, const char *input_word) +{ + EB_Error_Code error_code; + EB_Word_Code word_code; + EB_Search_Context *context; + + eb_lock(&book->lock); + LOG(("in: eb_search_exactword(book=%d, input_word=%s)", (int)book->code, + eb_quoted_string(input_word))); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Initialize search context. + */ + eb_reset_search_contexts(book); + context = book->search_contexts; + context->code = EB_SEARCH_EXACTWORD; + + /* + * Make a fixed word and a canonicalized word to search from + * `input_word'. + */ + error_code = eb_set_word(book, input_word, context->word, + context->canonicalized_word, &word_code); + if (error_code != EB_SUCCESS) + goto failed; + + /* + * Get a page number. + */ + switch (word_code) { + case EB_WORD_ALPHABET: + if (book->subbook_current->word_alphabet.start_page != 0) + context->page = book->subbook_current->word_alphabet.start_page; + else if (book->subbook_current->word_asis.start_page != 0) + context->page = book->subbook_current->word_asis.start_page; + else { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + break; + + case EB_WORD_KANA: + if (book->subbook_current->word_kana.start_page != 0) + context->page = book->subbook_current->word_kana.start_page; + else if (book->subbook_current->word_asis.start_page != 0) + context->page = book->subbook_current->word_asis.start_page; + else { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + break; + + case EB_WORD_OTHER: + if (book->subbook_current->word_asis.start_page != 0) + context->page = book->subbook_current->word_asis.start_page; + else { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + break; + + default: + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + + /* + * Choose comparison functions. + */ + if (book->character_code == EB_CHARCODE_ISO8859_1) { + context->compare_pre = eb_exact_pre_match_word_latin; + context->compare_single = eb_exact_match_word_latin; + context->compare_group = eb_exact_match_word_latin; + } else if (context->page == book->subbook_current->word_kana.start_page) { + context->compare_pre = eb_exact_pre_match_word_jis; + context->compare_single = eb_exact_match_word_kana_single; + context->compare_group = eb_exact_match_word_kana_group; + } else { + context->compare_pre = eb_exact_pre_match_word_jis; + context->compare_single = eb_exact_match_word_jis; + context->compare_group = eb_exact_match_word_kana_group; + } + + /* + * Pre-search. + */ + error_code = eb_presearch_word(book, context); + if (error_code != EB_SUCCESS) + goto failed; + + LOG(("out: eb_search_exactword() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_reset_search_contexts(book); + LOG(("out: eb_search_exactword() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} diff --git a/filename.c b/filename.c new file mode 100644 index 0000000..1b7ac2c --- /dev/null +++ b/filename.c @@ -0,0 +1,609 @@ +/* + * 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 "build-post.h" + +/* + * Canonicalize `path_name' (UNIX version). + * Convert a path name to an absolute path. + */ +EB_Error_Code +eb_canonicalize_path_name(char *path_name) +{ + char cwd[EB_MAX_PATH_LENGTH + 1]; + char temporary_path_name[EB_MAX_PATH_LENGTH + 1]; + char *last_slash; + + if (*path_name != '/') { + /* + * `path_name' is an relative path. Convert to an absolute + * path. + */ + if (getcwd(cwd, EB_MAX_PATH_LENGTH + 1) == NULL) + return EB_ERR_FAIL_GETCWD; + if (EB_MAX_PATH_LENGTH < strlen(cwd) + 1 + strlen(path_name)) + return EB_ERR_TOO_LONG_FILE_NAME; + + if (strcmp(path_name, ".") == 0) { + strcpy(path_name, cwd); + } else if (strncmp(path_name, "./", 2) == 0) { + sprintf(temporary_path_name, "%s/%s", cwd, path_name + 2); + strcpy(path_name, temporary_path_name); + } else { + sprintf(temporary_path_name, "%s/%s", cwd, path_name); + strcpy(path_name, temporary_path_name); + } + } + + /* + * Unless `path_name' is "/", eliminate `/' in the tail of the + * path name. + */ + last_slash = strrchr(path_name, '/'); + if (last_slash != path_name && *(last_slash + 1) == '\0') + *last_slash = '\0'; + + return EB_SUCCESS; +} + + +/* + * Canonicalize file name. + * - Suffix including dot is removed + * - Version including semicolon is removed. + * - Letters are converted to upper case. + * + * We minght fail to load a file after we fix the file name. + * If loading the file is tried again, we need the original file name, + * not fixed file name. Therefore, we get orignal file name from fixed + * file name using this function. + */ +void +eb_canonicalize_file_name(char *file_name) +{ + char *p; + + for (p = file_name; *p != '\0' && *p != '.' && *p != ';'; p++) + *p = ASCII_TOUPPER(*p); + *p = '\0'; +} + + +/* + * Rewrite `directory_name' to a real directory name in the `path' directory. + * + * If a directory matched to `directory_name' exists, then EB_SUCCESS is + * returned, and `directory_name' is rewritten to that name. Otherwise + * EB_ERR_BAD_DIR_NAME is returned. + */ +EB_Error_Code +eb_fix_directory_name(const char *path, char *directory_name) +{ + struct dirent *entry; + DIR *dir; + + /* + * Open the directory `path'. + */ + dir = opendir(path); + if (dir == NULL) + goto failed; + + for (;;) { + /* + * Read the directory entry. + */ + entry = readdir(dir); + if (entry == NULL) + goto failed; + + if (strcasecmp(entry->d_name, directory_name) == 0) + break; + } + + strcpy(directory_name, entry->d_name); + closedir(dir); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + if (dir != NULL) + closedir(dir); + return EB_ERR_BAD_DIR_NAME; +} + + +/* + * Rewrite `sub_directory_name' to a real sub directory name in the + * `path/directory_name' directory. + * + * If a directory matched to `sub_directory_name' exists, then EB_SUCCESS + * is returned, and `directory_name' is rewritten to that name. Otherwise + * EB_ERR_BAD_FILE_NAME is returned. + */ +EB_Error_Code +eb_fix_directory_name2(const char *path, const char *directory_name, + char *sub_directory_name) +{ + char sub_path_name[EB_MAX_PATH_LENGTH + 1]; + + eb_compose_path_name(path, directory_name, sub_path_name); + return eb_fix_directory_name(sub_path_name, sub_directory_name); +} + + +/* + * Fix suffix of `path_name'. + * + * If `suffix' is an empty string, delete suffix from `path_name'. + * Otherwise, add `suffix' to `path_name'. + */ +void +eb_fix_path_name_suffix(char *path_name, const char *suffix) +{ + char *base_name; + char *dot; + char *semicolon; + + base_name = strrchr(path_name, '/'); + if (base_name == NULL) + base_name = path_name; + else + base_name++; + + dot = strchr(base_name, '.'); + semicolon = strchr(base_name, ';'); + + if (*suffix == '\0') { + /* + * Remove `.xxx' from `fixed_file_name': + * foo.xxx --> foo + * foo.xxx;1 --> foo;1 + * foo. --> foo. (unchanged) + * foo.;1 --> foo.;1 (unchanged) + */ + if (dot != NULL && *(dot + 1) != '\0' && *(dot + 1) != ';') { + if (semicolon != NULL) + sprintf(dot, ";%c", *(semicolon + 1)); + else + *dot = '\0'; + } + } else { + /* + * Add `.xxx' to `fixed_file_name': + * foo --> foo.xxx + * foo. --> foo.xxx + * foo;1 --> foo.xxx;1 + * foo.;1 --> foo.xxx;1 + * foo.xxx --> foo.xxx (unchanged) + */ + if (dot != NULL) { + if (semicolon != NULL) + sprintf(dot, "%s;%c", suffix, *(semicolon + 1)); + else + strcpy(dot, suffix); + } else { + if (semicolon != NULL) + sprintf(semicolon, "%s;%c", suffix, *(semicolon + 1)); + else + strcat(base_name, suffix); + } + } +} + + +#define FOUND_NONE 0 +#define FOUND_EBZ 1 +#define FOUND_BASENAME 2 +#define FOUND_ORG 3 + +/* + * Rewrite `found_file_name' to a real file name in the `path_name' + * directory. + * + * If a file matched to `target_file_name' exists, then EB_SUCCESS + * is returned, and `found_file_name' is rewritten to that name. + * Otherwise EB_ERR_BAD_FILE_NAME is returned. + * + * Note that `target_file_name' must not contain `.' or excceed + * EB_MAX_DIRECTORY_NAME_LENGTH characters. + */ +EB_Error_Code +eb_find_file_name(const char *path_name, const char *target_file_name, + char *found_file_name) +{ + char ebz_target_file_name[EB_MAX_FILE_NAME_LENGTH + 1]; + char org_target_file_name[EB_MAX_FILE_NAME_LENGTH + 1]; + char candidate_file_name[EB_MAX_FILE_NAME_LENGTH + 1]; + DIR *dir; + struct dirent *entry; + size_t d_namlen; + int found = FOUND_NONE; + + strcpy(ebz_target_file_name, target_file_name); + strcat(ebz_target_file_name, ".ebz"); + strcpy(org_target_file_name, target_file_name); + strcat(org_target_file_name, ".org"); + candidate_file_name[0] = '\0'; + + /* + * Open the directory `path_name'. + */ + dir = opendir(path_name); + if (dir == NULL) + goto failed; + + for (;;) { + /* + * Read the directory entry. + */ + entry = readdir(dir); + if (entry == NULL) + break; + + /* + * Compare the given file names and the current entry name. + * We consider they are matched when one of the followings + * is true: + * + * == + * +";1' == + * +"." == + * +".;1" == + * +".ebz" == + * +".ebz;1" == + * +".org" == + * +".org;1" == + * + * All the comparisons are done without case sensitivity. + * We support version number ";1" only. + */ + d_namlen = strlen(entry->d_name); + if (2 < d_namlen + && *(entry->d_name + d_namlen - 2) == ';' + && ASCII_ISDIGIT(*(entry->d_name + d_namlen - 1))) { + d_namlen -= 2; + } + if (1 < d_namlen && *(entry->d_name + d_namlen - 1) == '.') + d_namlen--; + + if (strcasecmp(entry->d_name, ebz_target_file_name) == 0 + && *(ebz_target_file_name + d_namlen) == '\0' + && found < FOUND_EBZ) { + strcpy(candidate_file_name, entry->d_name); + found = FOUND_EBZ; + } + if (strncasecmp(entry->d_name, target_file_name, d_namlen) == 0 + && *(target_file_name + d_namlen) == '\0' + && found < FOUND_BASENAME) { + strcpy(candidate_file_name, entry->d_name); + found = FOUND_BASENAME; + } + if (strcasecmp(entry->d_name, org_target_file_name) == 0 + && *(org_target_file_name + d_namlen) == '\0' + && found < FOUND_ORG) { + strcpy(candidate_file_name, entry->d_name); + found = FOUND_ORG; + break; + } + } + + if (found == FOUND_NONE) + goto failed; + + closedir(dir); + strcpy(found_file_name, candidate_file_name); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + if (dir != NULL) + closedir(dir); + return EB_ERR_BAD_FILE_NAME; +} + + +/* + * Rewrite `file_name' to a real file name in the directory + * `path_name/sub_directory_name'. + * + * If a file matched to `file_name' exists, then EB_SUCCESS is returned, + * and `file_name' is rewritten to that name. Otherwise EB_ERR_BAD_FILE_NAME + * is returned. + */ +EB_Error_Code +eb_find_file_name2(const char *path_name, const char *sub_directory_name, + const char *target_file_name, char *found_file_name) +{ + char sub_path_name[EB_MAX_PATH_LENGTH + 1]; + + eb_compose_path_name(path_name, sub_directory_name, sub_path_name); + return eb_find_file_name(sub_path_name, target_file_name, found_file_name); +} + + +EB_Error_Code +eb_find_file_name3(const char *path_name, const char *sub_directory_name, + const char *sub2_directory_name, const char *target_file_name, + char *found_file_name) +{ + char sub2_path_name[EB_MAX_PATH_LENGTH + 1]; + + eb_compose_path_name2(path_name, sub_directory_name, sub2_directory_name, + sub2_path_name); + return eb_find_file_name(sub2_path_name, target_file_name, + found_file_name); +} + + +/* + * Compose a file name + * `path_name/file_name' + * and copy it into `composed_path_name'. + */ +void +eb_compose_path_name(const char *path_name, const char *file_name, + char *composed_path_name) +{ + if (strcmp(path_name, "/") == 0) + sprintf(composed_path_name, "%s%s", path_name, file_name); + else + sprintf(composed_path_name, "%s/%s", path_name, file_name); +} + + +/* + * Compose a file name + * `path_name/sub_directory/file_name' + * and copy it into `composed_path_name'. + */ +void +eb_compose_path_name2(const char *path_name, const char *sub_directory_name, + const char *file_name, char *composed_path_name) +{ + if (strcmp(path_name, "/") == 0) { + sprintf(composed_path_name, "%s%s/%s", + path_name, sub_directory_name, file_name); + } else { + sprintf(composed_path_name, "%s/%s/%s", + path_name, sub_directory_name, file_name); + } +} + + +/* + * Compose a file name + * `path_name/sub_directory/sub2_directory/file_name' + * and copy it into `composed_path_name'. + */ +void +eb_compose_path_name3(const char *path_name, const char *sub_directory_name, + const char *sub2_directory_name, const char *file_name, + char *composed_path_name) +{ + if (strcmp(path_name, "/") == 0) { + sprintf(composed_path_name, "%s%s/%s/%s", + path_name, sub_directory_name, sub2_directory_name, file_name); + } else { + sprintf(composed_path_name, "%s/%s/%s/%s", + path_name, sub_directory_name, sub2_directory_name, file_name); + } +} + + +/* + * Compose movie file name from argv[], and copy the result to + * `composed_file_name'. Note that upper letters are converted to lower + * letters. + * + * If a file `composed_path_name' exists, then EB_SUCCESS is returned. + * Otherwise EB_ERR_BAD_FILE_NAME is returned. + */ +EB_Error_Code +eb_compose_movie_file_name(const unsigned int *argv, char *composed_file_name) +{ + unsigned short jis_characters[EB_MAX_DIRECTORY_NAME_LENGTH]; + const unsigned int *arg_p; + char *composed_p; + unsigned short c; + int i; + + /* + * Initialize `jis_characters[]'. + */ + for (i = 0, arg_p = argv; + i + 1 < EB_MAX_DIRECTORY_NAME_LENGTH; i += 2, arg_p++) { + jis_characters[i] = (*arg_p >> 16) & 0xffff; + jis_characters[i + 1] = (*arg_p) & 0xffff; + } + if (i < EB_MAX_DIRECTORY_NAME_LENGTH) + jis_characters[i] = (*arg_p >> 16) & 0xffff; + + /* + * Compose file name. + */ + for (i = 0, composed_p = composed_file_name; + i < EB_MAX_DIRECTORY_NAME_LENGTH; i++, composed_p++) { + c = jis_characters[i]; + if (c == 0x2121 || c == 0x0000) + break; + if ((0x2330 <= c && c <= 0x2339) || (0x2361 <= c && c <= 0x237a)) + *composed_p = c & 0xff; + else if (0x2341 <= c && c <= 0x235a) + *composed_p = (c | 0x20) & 0xff; + else + return EB_ERR_BAD_FILE_NAME; + } + + *composed_p = '\0'; + + return EB_SUCCESS; +} + + +/* + * This function is similar to eb_compose_movie_file_name(), but it + * returns full path of the movie file name. + */ +EB_Error_Code +eb_compose_movie_path_name(EB_Book *book, const unsigned int *argv, + char *composed_path_name) +{ + EB_Subbook *subbook; + EB_Error_Code error_code; + char composed_file_name[EB_MAX_FILE_NAME_LENGTH + 1]; + + /* + * Lock the book. + */ + eb_lock(&book->lock); + LOG(("in: eb_compose_movie_path_name(book=%d, argv=%x)", + (int)book->code, argv)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + subbook = book->subbook_current; + + error_code = eb_compose_movie_file_name(argv, composed_file_name); + if (error_code != EB_SUCCESS) + goto failed; + + error_code = eb_find_file_name3(book->path, subbook->directory_name, + subbook->movie_directory_name, composed_file_name, composed_file_name); + if (error_code != EB_SUCCESS) + goto failed; + eb_compose_path_name3(book->path, subbook->directory_name, + subbook->movie_directory_name, composed_file_name, composed_path_name); + + LOG(("out: eb_compse_movie_path_name() = %s", + eb_error_string(EB_SUCCESS))); + + eb_unlock(&book->lock); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_compse_movie_path_name() = %s", + eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Decompose movie file name into argv[]. This is the reverse of + * eb_compose_movie_file_name(). Note that lower letters are converted + * to upper letters. + * + * EB_SUCCESS is returned upon success, EB_ERR_BAD_FILE_NAME otherwise. + */ +EB_Error_Code +eb_decompose_movie_file_name(unsigned int *argv, + const char *composed_file_name) +{ + unsigned short jis_characters[EB_MAX_DIRECTORY_NAME_LENGTH]; + unsigned int *arg_p; + const char *composed_p; + int i; + + /* + * Initialize `jis_characters[]'. + */ + for (i = 0; i < EB_MAX_DIRECTORY_NAME_LENGTH; i++) + jis_characters[i] = 0x0000; + + /* + * Set jis_characters[]. + */ + for (i = 0, composed_p = composed_file_name; + i < EB_MAX_DIRECTORY_NAME_LENGTH && *composed_p != '\0'; + i++, composed_p++) { + if ('0' <= *composed_p && *composed_p <= '9') + jis_characters[i] = 0x2330 + (*composed_p - '0'); + else if ('A' <= *composed_p && *composed_p <= 'Z') + jis_characters[i] = 0x2341 + (*composed_p - 'A'); + else if ('a' <= *composed_p && *composed_p <= 'z') + jis_characters[i] = 0x2341 + (*composed_p - 'a'); + else + return EB_ERR_BAD_FILE_NAME; + } + if (*composed_p != '\0') + return EB_ERR_BAD_FILE_NAME; + + /* + * Decompose file name. + */ + for (i = 0, arg_p = argv; + i + 1 < EB_MAX_DIRECTORY_NAME_LENGTH; i += 2, arg_p++) { + *arg_p = (jis_characters[i] << 16) | jis_characters[i + 1]; + } + if (i < EB_MAX_DIRECTORY_NAME_LENGTH) { + *arg_p++ = jis_characters[i] << 16; + } + *arg_p = '\0'; + + return EB_SUCCESS; +} + + +void +eb_path_name_zio_code(const char *path_name, Zio_Code default_zio_code, + Zio_Code *zio_code) +{ + const char *base_name; + const char *dot; + + base_name = strrchr(path_name, '/'); + if (base_name != NULL) + base_name++; + else + base_name = path_name; + + dot = strchr(base_name, '.'); + if (dot != NULL && strncasecmp(dot, ".ebz", 4) == 0) + *zio_code = ZIO_EBZIP1; + else if (dot != NULL && strncasecmp(dot, ".org", 4) == 0) + *zio_code = ZIO_PLAIN; + else + *zio_code = default_zio_code; +} diff --git a/font.c b/font.c new file mode 100644 index 0000000..594028d --- /dev/null +++ b/font.c @@ -0,0 +1,539 @@ +/* + * 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" + +/* + * Initialize all fonts in the current subbook. + */ +void +eb_initialize_fonts(EB_Book *book) +{ + EB_Subbook *subbook; + EB_Font *font; + int i; + + LOG(("in: eb_initialize_fonts(book=%d)", (int)book->code)); + + subbook = book->subbook_current; + + for (i = 0, font = subbook->narrow_fonts; i < EB_MAX_FONTS; i++, font++) { + font->font_code = EB_FONT_INVALID; + font->initialized = 0; + font->start = -1; + font->end = -1; + font->page = 0; + font->glyphs = NULL; + zio_initialize(&font->zio); + } + + for (i = 0, font = subbook->wide_fonts; i < EB_MAX_FONTS; i++, font++) { + font->font_code = EB_FONT_INVALID; + font->initialized = 0; + font->start = -1; + font->end = -1; + font->page = 0; + font->glyphs = NULL; + zio_initialize(&font->zio); + } + + LOG(("out: eb_initialize_fonts()")); +} + + +/* + * Load font files. + */ +void +eb_load_font_headers(EB_Book *book) +{ + EB_Error_Code error_code; + EB_Subbook *subbook; + EB_Font_Code i; + + LOG(("in: eb_load_fonts(book=%d)", (int)book->code)); + + subbook = book->subbook_current; + + /* + * Load narrow font headers. + */ + for (i = 0; i < EB_MAX_FONTS; i++) { + if (subbook->narrow_fonts[i].font_code == EB_FONT_INVALID + || subbook->narrow_fonts[i].initialized) + continue; + + error_code = eb_open_narrow_font_file(book, i); + if (error_code == EB_SUCCESS) + error_code = eb_load_narrow_font_header(book, i); + if (error_code != EB_SUCCESS) + subbook->narrow_fonts[i].font_code = EB_FONT_INVALID; + subbook->narrow_fonts[i].initialized = 1; + zio_close(&subbook->narrow_fonts[i].zio); + } + + /* + * Load wide font header. + */ + for (i = 0; i < EB_MAX_FONTS; i++) { + if (subbook->wide_fonts[i].font_code == EB_FONT_INVALID + || subbook->wide_fonts[i].initialized) + continue; + + error_code = eb_open_wide_font_file(book, i); + if (error_code == EB_SUCCESS) + error_code = eb_load_wide_font_header(book, i); + if (error_code != EB_SUCCESS) + subbook->wide_fonts[i].font_code = EB_FONT_INVALID; + subbook->wide_fonts[i].initialized = 1; + zio_close(&subbook->wide_fonts[i].zio); + } + + LOG(("out: eb_load_font_headers()")); +} + + +/* + * Finalize all fonts in the current subbook. + */ +void +eb_finalize_fonts(EB_Book *book) +{ + EB_Subbook *subbook; + EB_Font *font; + int i; + + LOG(("in: eb_finalize_fonts(book=%d)", (int)book->code)); + + subbook = book->subbook_current; + + for (i = 0, font = subbook->narrow_fonts; i < EB_MAX_FONTS; i++, font++) { + zio_finalize(&font->zio); + if (font->glyphs != NULL) { + free(font->glyphs); + font->glyphs = NULL; + } + } + + for (i = 0, font = subbook->wide_fonts; i < EB_MAX_FONTS; i++, font++) { + zio_finalize(&font->zio); + if (font->glyphs != NULL) { + free(font->glyphs); + font->glyphs = NULL; + } + } + + LOG(("out: eb_finalize_fonts()")); +} + + +/* + * Look up the height of the current font of the current subbook in + * `book'. + */ +EB_Error_Code +eb_font(EB_Book *book, EB_Font_Code *font_code) +{ + EB_Error_Code error_code; + + eb_lock(&book->lock); + LOG(("in: eb_font(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Look up the height of the current font. + */ + if (book->subbook_current->narrow_current != NULL) + *font_code = book->subbook_current->narrow_current->font_code; + else if (book->subbook_current->wide_current != NULL) + *font_code = book->subbook_current->wide_current->font_code; + else { + error_code = EB_ERR_NO_CUR_FONT; + goto failed; + } + + LOG(("out: eb_font(font_code=%d) = %s", (int)*font_code, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *font_code = EB_FONT_INVALID; + LOG(("out: eb_font() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Set the font with `font_code' as the current font of the current + * subbook in `book'. + */ +EB_Error_Code +eb_set_font(EB_Book *book, EB_Font_Code font_code) +{ + EB_Error_Code error_code; + EB_Subbook *subbook; + + eb_lock(&book->lock); + LOG(("in: eb_set_font(book=%d, font_code=%d)", (int)book->code, + (int)font_code)); + + /* + * Check `font_code'. + */ + if (font_code < 0 || EB_MAX_FONTS <= font_code) { + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + /* + * Current subbook must have been set. + */ + subbook = book->subbook_current; + if (subbook == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * If the current font is the font with `font_code', return immediately. + * Otherwise close the current font and continue. + */ + if (subbook->narrow_current != NULL) { + if (subbook->narrow_current->font_code == font_code) + goto succeeded; + if (book->disc_code == EB_DISC_EPWING) + zio_close(&subbook->narrow_current->zio); + subbook->narrow_current = NULL; + } + if (subbook->wide_current != NULL) { + if (subbook->wide_current->font_code == font_code) + goto succeeded; + if (book->disc_code == EB_DISC_EPWING) + zio_close(&subbook->wide_current->zio); + subbook->wide_current = NULL; + } + + /* + * Set the current font. + */ + if (subbook->narrow_fonts[font_code].font_code != EB_FONT_INVALID) + subbook->narrow_current = subbook->narrow_fonts + font_code; + if (subbook->wide_fonts[font_code].font_code != EB_FONT_INVALID) + subbook->wide_current = subbook->wide_fonts + font_code; + + if (subbook->narrow_current == NULL && subbook->wide_current == NULL) { + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + /* + * Initialize current font informtaion. + */ + if (subbook->narrow_current != NULL) { + error_code = eb_open_narrow_font_file(book, font_code); + if (error_code != EB_SUCCESS) + goto failed; + } + if (subbook->wide_current != NULL) { + error_code = eb_open_wide_font_file(book, font_code); + if (error_code != EB_SUCCESS) + goto failed; + } + + + succeeded: + LOG(("out: eb_set_font() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_unset_font(book); + LOG(("out: eb_set_font() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Unset the font in the current subbook in `book'. + */ +void +eb_unset_font(EB_Book *book) +{ + EB_Subbook *subbook; + + eb_lock(&book->lock); + LOG(("in: eb_unset_font(book=%d)", (int)book->code)); + + subbook = book->subbook_current; + + if (subbook == NULL) + goto succeeded; + + /* + * Close font files. + */ + if (subbook->narrow_current != NULL) { + zio_close(&subbook->narrow_current->zio); + if (subbook->narrow_current->glyphs != NULL) { + free(subbook->narrow_current->glyphs); + subbook->narrow_current->glyphs = NULL; + } + } + if (subbook->wide_current != NULL) { + zio_close(&subbook->wide_current->zio); + if (subbook->wide_current->glyphs != NULL) { + free(subbook->wide_current->glyphs); + subbook->wide_current->glyphs = NULL; + } + } + + book->subbook_current->narrow_current = NULL; + book->subbook_current->wide_current = NULL; + + succeeded: + LOG(("out: eb_unset_font()")); + eb_unlock(&book->lock); +} + + +/* + * Make a list of fonts in the current subbook in `book'. + */ +EB_Error_Code +eb_font_list(EB_Book *book, EB_Font_Code *font_list, int *font_count) +{ + EB_Error_Code error_code; + EB_Subbook *subbook; + EB_Font_Code *list_p; + int i; + + eb_lock(&book->lock); + LOG(("in: eb_font_list(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + subbook = book->subbook_current; + if (subbook == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Scan the font table in the book. + */ + subbook = book->subbook_current; + list_p = font_list; + *font_count = 0; + for (i = 0; i < EB_MAX_FONTS; i++) { + if (subbook->narrow_fonts[i].font_code != EB_FONT_INVALID + || subbook->wide_fonts[i].font_code != EB_FONT_INVALID) { + *list_p++ = i; + *font_count += 1; + } + } + + LOG(("out: eb_font(font_count=%d) = %s", *font_count, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_font_list() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Test whether the current subbook in `book' has a font with + * `font_code' or not. + */ +int +eb_have_font(EB_Book *book, EB_Font_Code font_code) +{ + EB_Subbook *subbook; + + eb_lock(&book->lock); + LOG(("in: eb_have_font(book=%d, font_code=%d)", (int)book->code, + (int)font_code)); + + /* + * Check `font_code'. + */ + if (font_code < 0 || EB_MAX_FONTS <= font_code) + goto failed; + + /* + * Current subbook must have been set. + */ + subbook = book->subbook_current; + if (subbook == NULL) + goto failed; + + if (subbook->narrow_fonts[font_code].font_code == EB_FONT_INVALID + && subbook->wide_fonts[font_code].font_code == EB_FONT_INVALID) + goto failed; + + LOG(("out: eb_have_font() = %d", 1)); + eb_unlock(&book->lock); + + return 1; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_have_font() = %d", 0)); + eb_unlock(&book->lock); + return 0; +} + + +/* + * Get height of the font `font_code' in the current subbook of `book'. + */ +EB_Error_Code +eb_font_height(EB_Book *book, int *height) +{ + EB_Error_Code error_code; + EB_Font_Code font_code; + + eb_lock(&book->lock); + LOG(("in: eb_font_height(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * The narrow font must exist in the current subbook. + */ + if (book->subbook_current->narrow_current != NULL) + font_code = book->subbook_current->narrow_current->font_code; + else if (book->subbook_current->wide_current != NULL) + font_code = book->subbook_current->wide_current->font_code; + else { + error_code = EB_ERR_NO_CUR_FONT; + goto failed; + } + + /* + * Calculate height. + */ + error_code = eb_font_height2(font_code, height); + if (error_code != EB_SUCCESS) + goto failed; + + LOG(("out: eb_font_heigt(height=%d) = %s", *height, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *height = 0; + LOG(("out: eb_font_height() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Get height of the font `font_code'. + */ +EB_Error_Code +eb_font_height2(EB_Font_Code font_code, int *height) +{ + EB_Error_Code error_code; + + LOG(("in: eb_font_height2(font_code=%d)", (int)font_code)); + + switch (font_code) { + case EB_FONT_16: + *height = EB_HEIGHT_FONT_16; + break; + case EB_FONT_24: + *height = EB_HEIGHT_FONT_24; + break; + case EB_FONT_30: + *height = EB_HEIGHT_FONT_30; + break; + case EB_FONT_48: + *height = EB_HEIGHT_FONT_48; + break; + default: + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + LOG(("out: eb_font_heigt2(height=%d) = %s", *height, + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *height = 0; + LOG(("out: eb_font_height2() = %s", eb_error_string(error_code))); + return error_code; +} diff --git a/font.h b/font.h new file mode 100644 index 0000000..1340d50 --- /dev/null +++ b/font.h @@ -0,0 +1,196 @@ +/* -*- C -*- + * 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. + */ + +#ifndef EB_FONT_H +#define EB_FONT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "defs.h" + +/* + * Font types. + */ +#define EB_FONT_16 0 +#define EB_FONT_24 1 +#define EB_FONT_30 2 +#define EB_FONT_48 3 +#define EB_FONT_INVALID -1 + +/* + * Font sizes. + */ +#define EB_SIZE_NARROW_FONT_16 16 +#define EB_SIZE_WIDE_FONT_16 32 +#define EB_SIZE_NARROW_FONT_24 48 +#define EB_SIZE_WIDE_FONT_24 72 +#define EB_SIZE_NARROW_FONT_30 60 +#define EB_SIZE_WIDE_FONT_30 120 +#define EB_SIZE_NARROW_FONT_48 144 +#define EB_SIZE_WIDE_FONT_48 288 + +/* + * Font width. + */ +#define EB_WIDTH_NARROW_FONT_16 8 +#define EB_WIDTH_WIDE_FONT_16 16 +#define EB_WIDTH_NARROW_FONT_24 16 +#define EB_WIDTH_WIDE_FONT_24 24 +#define EB_WIDTH_NARROW_FONT_30 16 +#define EB_WIDTH_WIDE_FONT_30 32 +#define EB_WIDTH_NARROW_FONT_48 24 +#define EB_WIDTH_WIDE_FONT_48 48 + +/* + * Font height. + */ +#define EB_HEIGHT_FONT_16 16 +#define EB_HEIGHT_FONT_24 24 +#define EB_HEIGHT_FONT_30 30 +#define EB_HEIGHT_FONT_48 48 + +/* + * Bitmap image sizes. + */ +#define EB_SIZE_NARROW_FONT_16_XBM 184 +#define EB_SIZE_WIDE_FONT_16_XBM 284 +#define EB_SIZE_NARROW_FONT_16_XPM 266 +#define EB_SIZE_WIDE_FONT_16_XPM 395 +#define EB_SIZE_NARROW_FONT_16_GIF 186 +#define EB_SIZE_WIDE_FONT_16_GIF 314 +#define EB_SIZE_NARROW_FONT_16_BMP 126 +#define EB_SIZE_WIDE_FONT_16_BMP 126 +#define EB_SIZE_NARROW_FONT_16_PNG 131 +#define EB_SIZE_WIDE_FONT_16_PNG 147 + +#define EB_SIZE_NARROW_FONT_24_XBM 383 +#define EB_SIZE_WIDE_FONT_24_XBM 533 +#define EB_SIZE_NARROW_FONT_24_XPM 555 +#define EB_SIZE_WIDE_FONT_24_XPM 747 +#define EB_SIZE_NARROW_FONT_24_GIF 450 +#define EB_SIZE_WIDE_FONT_24_GIF 642 +#define EB_SIZE_NARROW_FONT_24_BMP 158 +#define EB_SIZE_WIDE_FONT_24_BMP 158 +#define EB_SIZE_NARROW_FONT_24_PNG 171 +#define EB_SIZE_WIDE_FONT_24_PNG 195 + +#define EB_SIZE_NARROW_FONT_30_XBM 458 +#define EB_SIZE_WIDE_FONT_30_XBM 833 +#define EB_SIZE_NARROW_FONT_30_XPM 675 +#define EB_SIZE_WIDE_FONT_30_XPM 1155 +#define EB_SIZE_NARROW_FONT_30_GIF 552 +#define EB_SIZE_WIDE_FONT_30_GIF 1032 +#define EB_SIZE_NARROW_FONT_30_BMP 182 +#define EB_SIZE_WIDE_FONT_30_BMP 182 +#define EB_SIZE_NARROW_FONT_30_PNG 189 +#define EB_SIZE_WIDE_FONT_30_PNG 249 + +#define EB_SIZE_NARROW_FONT_48_XBM 983 +#define EB_SIZE_WIDE_FONT_48_XBM 1883 +#define EB_SIZE_NARROW_FONT_48_XPM 1419 +#define EB_SIZE_WIDE_FONT_48_XPM 2571 +#define EB_SIZE_NARROW_FONT_48_GIF 1242 +#define EB_SIZE_WIDE_FONT_48_GIF 2394 +#define EB_SIZE_NARROW_FONT_48_BMP 254 +#define EB_SIZE_WIDE_FONT_48_BMP 446 +#define EB_SIZE_NARROW_FONT_48_PNG 291 +#define EB_SIZE_WIDE_FONT_48_PNG 435 + +#define EB_SIZE_FONT_IMAGE EB_SIZE_WIDE_FONT_48_XPM + +/* + * Function declarations. + */ +/* bitmap.c */ +EB_Error_Code eb_narrow_font_xbm_size(EB_Font_Code font_code, size_t *size); +EB_Error_Code eb_narrow_font_xpm_size(EB_Font_Code font_code, size_t *size); +EB_Error_Code eb_narrow_font_gif_size(EB_Font_Code font_code, size_t *size); +EB_Error_Code eb_narrow_font_bmp_size(EB_Font_Code font_code, size_t *size); +EB_Error_Code eb_narrow_font_png_size(EB_Font_Code font_code, size_t *size); +EB_Error_Code eb_wide_font_xbm_size(EB_Font_Code font_code, size_t *size); +EB_Error_Code eb_wide_font_xpm_size(EB_Font_Code font_code, size_t *size); +EB_Error_Code eb_wide_font_gif_size(EB_Font_Code font_code, size_t *size); +EB_Error_Code eb_wide_font_bmp_size(EB_Font_Code font_code, size_t *size); +EB_Error_Code eb_wide_font_png_size(EB_Font_Code font_code, size_t *size); +EB_Error_Code eb_bitmap_to_xbm(const char *bitmap, int width, int height, + char *xbm, size_t *xbm_length); +EB_Error_Code eb_bitmap_to_xpm(const char *bitmap, int width, int height, + char *xpm, size_t *xpm_length); +EB_Error_Code eb_bitmap_to_gif(const char *bitmap, int width, int height, + char *gif, size_t *gif_length); +EB_Error_Code eb_bitmap_to_bmp(const char *bitmap, int width, int height, + char *bmp, size_t *bmp_length); +EB_Error_Code eb_bitmap_to_png(const char *bitmap, int width, int height, + char *png, size_t *png_length); + +/* font.c */ +EB_Error_Code eb_font(EB_Book *book, EB_Font_Code *font_code); +EB_Error_Code eb_set_font(EB_Book *book, EB_Font_Code font_code); +void eb_unset_font(EB_Book *book); +EB_Error_Code eb_font_list(EB_Book *book, EB_Font_Code *font_list, + int *font_count); +int eb_have_font(EB_Book *book, EB_Font_Code font_code); +EB_Error_Code eb_font_height(EB_Book *book, int *height); +EB_Error_Code eb_font_height2(EB_Font_Code font_code, int *height); + +/* narwfont.c */ +int eb_have_narrow_font(EB_Book *book); +EB_Error_Code eb_narrow_font_width(EB_Book *book, int *width); +EB_Error_Code eb_narrow_font_width2(EB_Font_Code font_code, int *width); +EB_Error_Code eb_narrow_font_size(EB_Book *book, size_t *size); +EB_Error_Code eb_narrow_font_size2(EB_Font_Code font_code, size_t *size); +EB_Error_Code eb_narrow_font_start(EB_Book *book, int *start); +EB_Error_Code eb_narrow_font_end(EB_Book *book, int *end); +EB_Error_Code eb_narrow_font_character_bitmap(EB_Book *book, int, char *); +EB_Error_Code eb_forward_narrow_font_character(EB_Book *book, int, int *); +EB_Error_Code eb_backward_narrow_font_character(EB_Book *book, int, int *); + +/* widefont.c */ +int eb_have_wide_font(EB_Book *book); +EB_Error_Code eb_wide_font_width(EB_Book *book, int *width); +EB_Error_Code eb_wide_font_width2(EB_Font_Code font_code, int *width); +EB_Error_Code eb_wide_font_size(EB_Book *book, size_t *size); +EB_Error_Code eb_wide_font_size2(EB_Font_Code font_code, size_t *size); +EB_Error_Code eb_wide_font_start(EB_Book *book, int *start); +EB_Error_Code eb_wide_font_end(EB_Book *book, int *end); +EB_Error_Code eb_wide_font_character_bitmap(EB_Book *book, + int character_number, char *bitmap); +EB_Error_Code eb_forward_wide_font_character(EB_Book *book, int n, + int *character_number); +EB_Error_Code eb_backward_wide_font_character(EB_Book *book, int n, + int *character_number); + +#ifdef __cplusplus +} +#endif + +#endif /* not EB_FONT_H */ diff --git a/hook.c b/hook.c new file mode 100644 index 0000000..85189c9 --- /dev/null +++ b/hook.c @@ -0,0 +1,319 @@ +/* + * 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 "appendix.h" +#include "text.h" +#include "build-post.h" + +/* + * Default hookset. + */ +EB_Hookset eb_default_hookset; + + +/* + * Initialize default hookset. + */ +void +eb_initialize_default_hookset(void) +{ + LOG(("in: eb_initialize_default_hookset()")); + + eb_initialize_hookset(&eb_default_hookset); + + LOG(("out: eb_initialize_default_hookset()")); +} + + +/* + * Initialize a hookset. + */ +void +eb_initialize_hookset(EB_Hookset *hookset) +{ + int i; + + LOG(("in: eb_initialize_hookset()")); + + eb_initialize_lock(&hookset->lock); + + for (i = 0; i < EB_NUMBER_OF_HOOKS; i++) { + hookset->hooks[i].code = i; + hookset->hooks[i].function = NULL; + } + hookset->hooks[EB_HOOK_NARROW_JISX0208].function + = eb_hook_euc_to_ascii; + hookset->hooks[EB_HOOK_NARROW_FONT].function + = eb_hook_narrow_character_text; + hookset->hooks[EB_HOOK_WIDE_FONT].function + = eb_hook_wide_character_text; + hookset->hooks[EB_HOOK_NEWLINE].function + = eb_hook_newline; + + LOG(("out: eb_initialize_hookset()")); +} + + +/* + * Finalize a hookset. + */ +void +eb_finalize_hookset(EB_Hookset *hookset) +{ + int i; + + LOG(("in: eb_finalize_hookset()")); + + for (i = 0; i < EB_NUMBER_OF_HOOKS; i++) { + hookset->hooks[i].code = i; + hookset->hooks[i].function = NULL; + } + eb_finalize_lock(&hookset->lock); + + LOG(("out: eb_finalize_hookset()")); +} + + +/* + * Set a hook. + */ +EB_Error_Code +eb_set_hook(EB_Hookset *hookset, const EB_Hook *hook) +{ + EB_Error_Code error_code; + + eb_lock(&hookset->lock); + LOG(("in: eb_set_hook(hook=%d)", (int)hook->code)); + + /* + * Set a hook. + */ + if (hook->code < 0 || EB_NUMBER_OF_HOOKS <= hook->code) { + error_code = EB_ERR_NO_SUCH_HOOK; + goto failed; + } + hookset->hooks[hook->code].function = hook->function; + + LOG(("out: eb_set_hook() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&hookset->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_set_hook() = %s", eb_error_string(error_code))); + eb_unlock(&hookset->lock); + return error_code; +} + + +/* + * Set a list of hooks. + */ +EB_Error_Code +eb_set_hooks(EB_Hookset *hookset, const EB_Hook *hook) +{ + EB_Error_Code error_code; + const EB_Hook *h; + + eb_lock(&hookset->lock); + LOG(("in: eb_set_hooks(hooks=[below])")); + + if (eb_log_flag) { + for (h = hook; h->code != EB_HOOK_NULL; h++) + LOG((" hook=%d", h->code)); + } + + /* + * Set hooks. + */ + for (h = hook; h->code != EB_HOOK_NULL; h++) { + if (h->code < 0 || EB_NUMBER_OF_HOOKS <= h->code) { + error_code = EB_ERR_NO_SUCH_HOOK; + goto failed; + } + hookset->hooks[h->code].function = h->function; + } + + /* + * Unlock the hookset. + */ + LOG(("out: eb_set_hooks() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&hookset->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_set_hooks() = %s", eb_error_string(error_code))); + eb_unlock(&hookset->lock); + return error_code; +} + + +/* + * EUC JP to ASCII conversion table. + */ +#define EUC_TO_ASCII_TABLE_START 0xa0 +#define EUC_TO_ASCII_TABLE_END 0xff + +static const unsigned char euc_a1_to_ascii_table[] = { + 0x00, 0x20, 0x00, 0x00, 0x2c, 0x2e, 0x00, 0x3a, /* 0xa0 */ + 0x3b, 0x3f, 0x21, 0x00, 0x00, 0x00, 0x60, 0x00, /* 0xa8 */ + 0x5e, 0x7e, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x2f, /* 0xb8 */ + 0x5c, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x27, /* 0xc0 */ + 0x00, 0x22, 0x28, 0x29, 0x00, 0x00, 0x5b, 0x5d, /* 0xc8 */ + 0x7b, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0 */ + 0x00, 0x00, 0x00, 0x00, 0x2b, 0x2d, 0x00, 0x00, /* 0xd8 */ + 0x00, 0x3d, 0x00, 0x3c, 0x3e, 0x00, 0x00, 0x00, /* 0xe0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, /* 0xe8 */ + 0x24, 0x00, 0x00, 0x25, 0x23, 0x26, 0x2a, 0x40, /* 0xf0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8 */ +}; + +static const unsigned char euc_a3_to_ascii_table[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8 */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0xb0 */ + 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8 */ + 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0xc0 */ + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0xc8 */ + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0xd0 */ + 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8 */ + 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0xe0 */ + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0xe8 */ + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0xf0 */ + 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8 */ +}; + + +/* + * Hook which converts a character from EUC-JP to ASCII. + */ +EB_Error_Code +eb_hook_euc_to_ascii(EB_Book *book, EB_Appendix *appendix, void *container, + EB_Hook_Code hook_code, int argc, const unsigned int *argv) +{ + int in_code1, in_code2; + int out_code = 0; + + in_code1 = argv[0] >> 8; + in_code2 = argv[0] & 0xff; + + if (in_code2 < EUC_TO_ASCII_TABLE_START + || EUC_TO_ASCII_TABLE_END < in_code2) { + out_code = 0; + } else if (in_code1 == 0xa1) { + out_code = euc_a1_to_ascii_table[in_code2 - EUC_TO_ASCII_TABLE_START]; + } else if (in_code1 == 0xa3) { + out_code = euc_a3_to_ascii_table[in_code2 - EUC_TO_ASCII_TABLE_START]; + } + + if (out_code == 0) + eb_write_text_byte2(book, in_code1, in_code2); + else + eb_write_text_byte1(book, out_code); + + return EB_SUCCESS; +} + + +/* + * Hook for narrow local character. + */ +EB_Error_Code +eb_hook_narrow_character_text(EB_Book *book, EB_Appendix *appendix, + void *container, EB_Hook_Code hook_code, int argc, + const unsigned int *argv) +{ + char alt_text[EB_MAX_ALTERNATION_TEXT_LENGTH + 1]; + + if (appendix == NULL + || eb_narrow_alt_character_text(appendix, (int)argv[0], alt_text) + != EB_SUCCESS) { + eb_write_text_string(book, ""); + } else { + eb_write_text_string(book, alt_text); + } + + return EB_SUCCESS; +} + + +/* + * Hook for wide local character. + */ +EB_Error_Code +eb_hook_wide_character_text(EB_Book *book, EB_Appendix *appendix, + void *container, EB_Hook_Code hook_code, int argc, + const unsigned int *argv) +{ + char alt_text[EB_MAX_ALTERNATION_TEXT_LENGTH + 1]; + + if (appendix == NULL + || eb_wide_alt_character_text(appendix, (int)argv[0], alt_text) + != EB_SUCCESS) { + eb_write_text_string(book, ""); + } else { + eb_write_text_string(book, alt_text); + } + + return EB_SUCCESS; +} + + +/* + * Hook for a newline character. + */ +EB_Error_Code +eb_hook_newline(EB_Book *book, EB_Appendix *appendix, void *container, + EB_Hook_Code code, int argc, const unsigned int *argv) +{ + eb_write_text_byte1(book, '\n'); + + return EB_SUCCESS; +} + + +/* + * Hook which does nothing. + */ +EB_Error_Code +eb_hook_empty(EB_Book *book, EB_Appendix *appendix, void *container, + EB_Hook_Code hook_code, int argc, const unsigned int *argv) +{ + return EB_SUCCESS; +} diff --git a/jacode.c b/jacode.c new file mode 100644 index 0000000..0b9f50e --- /dev/null +++ b/jacode.c @@ -0,0 +1,108 @@ +/* + * 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" + +/* + * Convert a string from JIS X 0208 to EUC JP. + */ +void +eb_jisx0208_to_euc(char *out_string, const char *in_string) +{ + unsigned char *out_p = (unsigned char *)out_string; + const unsigned char *in_p = (unsigned char *)in_string; + + while (*in_p != '\0') + *out_p++ = ((*in_p++) | 0x80); + + *out_p = '\0'; +} + + +/* + * Convert a string from shift-JIS to EUC JP. + * (Shift-JIS is used only in the `LANGUAGE' file.) + */ +void +eb_sjis_to_euc(char *out_string, const char *in_string) +{ + unsigned char *out_p = (unsigned char *)out_string; + const unsigned char *in_p = (unsigned char *)in_string; + unsigned char c1, c2; + + for (;;) { + /* + * Break at '\0'. + */ + c1 = *in_p++; + if (c1 == '\0') + break; + + if (c1 <= 0x7f) { + /* + * JIS X 0201 Roman character. + */ + *out_p++ = c1; + } else if (0xa1 <= c1 && c1 <= 0xdf) { + /* + * JIS X 0201 Kana. + */ + *out_p++ = ' '; + } else { + /* + * JIS X 0208 character. + */ + c2 = *in_p++; + if (c2 == 0x00) + break; + + if (c2 < 0x9f) { + if (c1 < 0xdf) + c1 = ((c1 - 0x30) << 1) - 1; + else + c1 = ((c1 - 0x70) << 1) - 1; + + if (c2 < 0x7f) + c2 += 0x61; + else + c2 += 0x60; + } else { + if (c1 < 0xdf) + c1 = (c1 - 0x30) << 1; + else + c1 = (c1 - 0x70) << 1; + c2 += 0x02; + } + + *out_p++ = c1; + *out_p++ = c2; + } + } + + *out_p = '\0'; +} diff --git a/keyword.c b/keyword.c new file mode 100644 index 0000000..0fc55ea --- /dev/null +++ b/keyword.c @@ -0,0 +1,189 @@ +/* + * 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 "build-post.h" + +/* + * Examine whether the current subbook in `book' supports `KEYWORD SEARCH' + * or not. + */ +int +eb_have_keyword_search(EB_Book *book) +{ + eb_lock(&book->lock); + LOG(("in: eb_have_keyword_search(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) + goto failed; + + if (book->subbook_current->keyword.start_page == 0) + goto failed; + + LOG(("out: eb_have_keyword_search() = %d", 1)); + eb_unlock(&book->lock); + + return 1; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_have_keyword_search() = %d", 0)); + eb_unlock(&book->lock); + return 0; +} + + +/* + * Keyword search. + */ +EB_Error_Code +eb_search_keyword(EB_Book *book, const char * const input_words[]) +{ + EB_Error_Code error_code; + EB_Search_Context *context; + EB_Word_Code word_code; + int word_count; + int i; + + /* + * Lock the book. + */ + eb_lock(&book->lock); + LOG(("in: eb_search_keyword(book=%d, input_words=[below])", + (int)book->code)); + + if (eb_log_flag) { + for (i = 0; i < EB_MAX_KEYWORDS && input_words[i] != NULL; i++) { + LOG((" input_words[%d]=%s", i, + eb_quoted_string(input_words[i]))); + } + LOG((" input_words[%d]=NULL", i)); + } + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Check whether the current subbook has keyword search. + */ + if (book->subbook_current->keyword.start_page == 0) { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + + /* + * Attach a search context for each keyword, and pre-search the + * keywords. + */ + eb_reset_search_contexts(book); + word_count = 0; + + for (i = 0; i < EB_MAX_KEYWORDS; i++) { + if (input_words[i] == NULL) + break; + + /* + * Initialize search context. + */ + context = book->search_contexts + word_count; + context->code = EB_SEARCH_KEYWORD; + + /* + * Choose comparison functions. + */ + if (book->character_code == EB_CHARCODE_ISO8859_1) { + context->compare_pre = eb_pre_match_word; + context->compare_single = eb_match_word; + context->compare_group = eb_match_word; + } else { + context->compare_pre = eb_pre_match_word; + context->compare_single = eb_match_word; + context->compare_group = eb_match_word_kana_group; + } + context->page = book->subbook_current->keyword.start_page; + + /* + * Make a fixed word and a canonicalized word to search from + * `input_words[i]'. + */ + error_code = eb_set_keyword(book, input_words[i], context->word, + context->canonicalized_word, &word_code); + if (error_code == EB_ERR_EMPTY_WORD) + continue; + else if (error_code != EB_SUCCESS) + goto failed; + + /* + * Pre-search. + */ + error_code = eb_presearch_word(book, context); + if (error_code != EB_SUCCESS) + goto failed; + + word_count++; + } + if (word_count == 0) { + error_code = EB_ERR_NO_WORD; + goto failed; + } else if (EB_MAX_KEYWORDS <= i && input_words[i] != NULL) { + error_code = EB_ERR_TOO_MANY_WORDS; + goto failed; + } + + /* + * Set `EB_SEARCH_NONE' to the rest unused search context. + */ + for (i = word_count; i < EB_MAX_KEYWORDS; i++) + (book->search_contexts + i)->code = EB_SEARCH_NONE; + + LOG(("out: eb_search_keyword() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_reset_search_contexts(book); + LOG(("out: eb_search_keyword() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} diff --git a/linebuf.c b/linebuf.c new file mode 100644 index 0000000..9f28e5b --- /dev/null +++ b/linebuf.c @@ -0,0 +1,406 @@ +/* + * copyright (c) 1997-2005 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 +#include +#include +#include +#include +#include +#include +#include + +#include "linebuf.h" + +/* + * Initialize `linebuffer'. + */ +void +initialize_line_buffer(Line_Buffer *line_buffer) +{ + line_buffer->buffer[0] = '\0'; + line_buffer->file = -1; + line_buffer->cache_length = 0; + line_buffer->timeout = 0; +} + + +/* + * Finalize `line_buffer'. + */ +void +finalize_line_buffer(Line_Buffer *line_buffer) +{ + line_buffer->buffer[0] = '\0'; + line_buffer->file = -1; + line_buffer->cache_length = 0; + line_buffer->timeout = 0; +} + + +/* + * Set timeout seconds. + */ +void +set_line_buffer_timeout(Line_Buffer *line_buffer, int timeout) +{ + line_buffer->timeout = timeout; +} + + +/* + * Bind `file' to `line_buffer'. + */ +void +bind_file_to_line_buffer(Line_Buffer *line_buffer, int file) +{ + if (line_buffer->file < 0) + initialize_line_buffer(line_buffer); + line_buffer->file = file; +} + + +/* + * Return file descriptor bound to `line_buffer'. + * Return -1 when no file is bound. + */ +int +file_bound_to_line_buffer(Line_Buffer *line_buffer) +{ + return line_buffer->file; +} + + +/* + * Discard cache data in `line_buffer'. + */ +void +discard_cache_in_line_buffer(Line_Buffer *line_buffer) +{ + line_buffer->cache_length = 0; +} + + +/* + * Get the length of cache data in `line_buffer'. + */ +size_t +cache_length_in_line_buffer(Line_Buffer *line_buffer) +{ + return line_buffer->cache_length; +} + + +/* + * Read a line from the file bound to `line_buffer', and copy the read + * line to `line'. It reads at most `max_line_length' bytes. + * + * The function recognizes both "\n" and "\r\n" as end of line. + * "\n" or "\r\n" is not copied to `buffer', but "\0" is added, instead. + * + * The function returns the number of characters in the line, upon + * successful. It doesn't count "\n" or "\r\n" in the tail of the line, + * so that 0 is returned for an empty line, and the line length doesn't + * exceed one less than `max_line_length'. + * + * If EOF is received or an error occurs, -1 is returned. If the + * line is too long, `max_line_length' is returned. + */ +ssize_t +read_line_buffer(Line_Buffer *line_buffer, char *line, size_t max_line_length) +{ + char *line_p; + char *newline; + size_t search_length; + size_t additional_length; + size_t line_length; + fd_set fdset; + struct timeval timeval; + int select_result; + ssize_t read_result; + + /* + * Return -1 if no file is bound, or if `max_line_length' is 0. + */ + if (line_buffer->file < 0) + return -1; + if (max_line_length == 0) + return -1; + + /* + * Read a file until newline is appeared. + */ + line_length = 0; + line_p = line; + + for (;;) { + if (0 < line_buffer->cache_length) { + /* + * Find a newline in the cache data. + */ + if (max_line_length - line_length < line_buffer->cache_length) + search_length = max_line_length - line_length; + else + search_length = line_buffer->cache_length; + + newline = (char *)memchr(line_buffer->buffer, '\n', search_length); + + /* + * Append cache data in front of the newline to `line'. + */ + if (newline != NULL) + additional_length = newline - line_buffer->buffer + 1; + else + additional_length = search_length; + memcpy(line_p, line_buffer->buffer, additional_length); + line_p += additional_length; + line_length += additional_length; + line_buffer->cache_length -= additional_length; + + /* + * If cache data not copied to `line' are remained in the + * buffer, we move them to the beginning of the buffer. + */ + memmove(line_buffer->buffer, + line_buffer->buffer + additional_length, + line_buffer->cache_length); + + if (newline != NULL) + break; + } + + /* + * Check for the length of the current line. Return if the + * line is too long. + * + * Note that the following conditional expression can be + * substituted to (line_buffer->cache_length != 0), because + * remained cache data mean that the line is too long. + */ + if (max_line_length <= line_length) + return line_length; + + /* + * Call select(). + */ + errno = 0; + FD_ZERO(&fdset); + FD_SET(line_buffer->file, &fdset); + + if (line_buffer->timeout == 0) { + select_result = select(line_buffer->file + 1, &fdset, NULL, NULL, + NULL); + } else { + timeval.tv_sec = line_buffer->timeout; + timeval.tv_usec = 0; + select_result = select(line_buffer->file + 1, &fdset, NULL, NULL, + &timeval); + } + if (select_result < 0) { + if (errno == EINTR) + continue; + return -1; + } else if (select_result == 0) { + return -1; + } + + /* + * Read from a file. (No cache data are remaind.) + */ + errno = 0; + read_result = recv(line_buffer->file, line_buffer->buffer, + LINEBUF_BUFFER_SIZE, 0); + if (read_result < 0) { + if (errno == EINTR) + continue; + return -1; + } else if (read_result == 0) { + if (line_length == 0) { + return -1; + } + return line_length; + } + line_buffer->cache_length += read_result; + } + + /* + * Overwrite `\n' with `\0'. + */ + line_p--; + *line_p = '\0'; + line_length--; + + /* + * If the line is end with `\r\n', remove not only `\n' but `\r'. + */ + if (0 < line_length && *(line_p - 1) == '\r') { + line_p--; + *line_p = '\0'; + line_length--; + } + + return line_length; +} + + +/* + * Read just `stream_length' bytes from the file bound to `line_buffer', + * and copy the read bytes to `stream'. + * + * Unlike read_line_buffer(), it doesn't append `\0' to the read data, + * nor remove newline character in the read data. + * + * If it succeeds, the number of bytes actually read is returned. + * If EOF is received or an error occurs, -1 is returned. + */ +ssize_t +binary_read_line_buffer(Line_Buffer *line_buffer, char *stream, + size_t stream_length) +{ + char *stream_p; + size_t done_length; + fd_set fdset; + struct timeval timeval; + int select_result; + ssize_t read_result; + + /* + * Return -1 if no file is bound. + */ + if (line_buffer->file < 0) + return -1; + + /* + * Return 0 if `stream_length' is 0. + */ + if (stream_length == 0) + return 0; + + /* + * Test whether cache data are left in `line_buffer->buffer'. + * If they are, copy them to `stream'. + */ + stream_p = stream; + done_length = 0; + + if (0 < line_buffer->cache_length) { + if (stream_length <= line_buffer->cache_length) + done_length = stream_length; + else + done_length = line_buffer->cache_length; + + memcpy(stream_p, line_buffer->buffer, done_length); + stream_p += done_length; + line_buffer->cache_length -= done_length; + memmove(line_buffer->buffer, + line_buffer->buffer + done_length, + line_buffer->cache_length); + } + + /* + * Read the file until the number of read bytes (`done_length') is + * reached to `stream_length'. + */ + while (done_length < stream_length) { + /* + * Call select(). + */ + errno = 0; + FD_ZERO(&fdset); + FD_SET(line_buffer->file, &fdset); + + if (line_buffer->timeout == 0) { + select_result = select(line_buffer->file + 1, NULL, &fdset, NULL, + NULL); + } else { + timeval.tv_sec = line_buffer->timeout; + timeval.tv_usec = 0; + select_result = select(line_buffer->file + 1, NULL, &fdset, NULL, + &timeval); + } + if (select_result < 0) { + if (errno == EINTR) + continue; + return -1; + } else if (select_result == 0) { + return -1; + } + + /* + * Read from a file. + */ + errno = 0; + read_result = recv(line_buffer->file, stream_p, + stream_length - done_length, 0); + if (read_result < 0) { + if (errno == EINTR) + continue; + return read_result; + } else if (read_result == 0) { + if (done_length == 0) { + return -1; + } + return done_length; + } + stream_p += read_result; + done_length += read_result; + } + + return stream_length; +} + + +/* + * Skip too long line read by read_line_buffer(). + * + * If a line read by read_line_buffer() doesn't contain a newline + * character, the line is too long. This function reads and discards + * the rest of the line. + * + * If EOF is received or an error occurs, -1 is returned. + * Otherwise, 0 is returned. + */ +int +skip_line_buffer(Line_Buffer *line_buffer) +{ + ssize_t line_length; + + /* + * Read data until the end of the line is found. + */ + for (;;) { + line_length = read_line_buffer(line_buffer, line_buffer->buffer, + LINEBUF_BUFFER_SIZE); + if (line_length < 0) + return -1; + if (line_length < LINEBUF_BUFFER_SIZE) + break; + } + + return 0; +} diff --git a/linebuf.h b/linebuf.h new file mode 100644 index 0000000..02e5b7d --- /dev/null +++ b/linebuf.h @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#ifndef LINEBUF_H +#define LINEBUF_H + +#include + +/* + * Buffer size of `Line_Buffer' struct. + */ +#define LINEBUF_BUFFER_SIZE 256 + +/* + * Line buffer manager. + */ +typedef struct { + int file; /* file descriptor */ + int timeout; /* idle timeout interval */ + size_t cache_length; /* length of cache data */ + char buffer[LINEBUF_BUFFER_SIZE]; /* buffer */ +} Line_Buffer; + + +/* + * Function declarations. + */ +void initialize_line_buffer(Line_Buffer *line_buffer); +void finalize_line_buffer(Line_Buffer *line_buffer); +void set_line_buffer_timeout(Line_Buffer *line_buffer, int timeout); +void bind_file_to_line_buffer(Line_Buffer *line_buffer, int file); +int file_bound_to_line_buffer(Line_Buffer *line_buffer); +void discard_cache_in_line_buffer(Line_Buffer *line_buffer); +size_t cache_length_in_line_buffer(Line_Buffer *line_buffer); +ssize_t read_line_buffer(Line_Buffer *line_buffer, char *line, + size_t max_line_length); +ssize_t binary_read_line_buffer(Line_Buffer *line_buffer, char *stream, + size_t stream_length); +int skip_line_buffer(Line_Buffer *line_buffer); + +#endif /* not LINEBUF_H */ diff --git a/log.c b/log.c new file mode 100644 index 0000000..0591638 --- /dev/null +++ b/log.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2001-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 "build-post.h" + +#include + +/* + * Initialization flag. + */ +int eb_log_initialized = 0; + +/* + * Debug log flag. + */ +int eb_log_flag = 0; + +/* + * Pointer to log function. + */ +static void (*eb_log_function)(const char *message, va_list) = eb_log_stderr; + + +/* + * Initialize logging sub-system. + */ +void +eb_initialize_log(void) +{ + if (eb_log_initialized) + return; + + eb_log_flag = (getenv(EB_DEBUG_ENVIRONMENT_VARIABLE) != NULL); + eb_log_function = eb_log_stderr; + eb_log_initialized = 1; +} + +/* + * Set log function. + */ +void +eb_set_log_function(void (*function)(const char *message, va_list ap)) +{ + if (!eb_log_initialized) + eb_initialize_log(); + eb_log_function = function; +} + +/* + * Enable logging. + */ +void +eb_enable_log(void) +{ + if (!eb_log_initialized) + eb_initialize_log(); + eb_log_flag = 1; +} + +/* + * Disable logging. + */ +void +eb_disable_log(void) +{ + if (!eb_log_initialized) + eb_initialize_log(); + eb_log_flag = 0; +} + +/* + * Log a message. + */ +void +eb_log(const char *message, ...) +{ + va_list ap; + + va_start(ap, message); + + if (eb_log_flag && eb_log_function != NULL) + eb_log_function(message, ap); + + va_end(ap); +} + +/* + * Output a log message to standard error. + * This is the default log handler. + * + * Currently, this function doesn't work if the system lacks vprintf() + * and dopront(). + */ +void +eb_log_stderr(const char *message, va_list ap) +{ + pthread_mutex_lock(&log_mutex); + + fputs("[EB] ", stderr); + + vfprintf(stderr, message, ap); + fputc('\n', stderr); + fflush(stderr); + + pthread_mutex_unlock(&log_mutex); +} + +#define MAX_QUOTED_STREAM_LENGTH 100 + +/* + * Return Quoted printable string of `stream'. + */ +const char * +eb_quoted_stream(const char *stream, size_t stream_length) +{ + static char quoted_streams[EB_MAX_KEYWORDS][MAX_QUOTED_STREAM_LENGTH + 3]; + static int current_index = 0; + unsigned char *quoted_p; + const unsigned char *stream_p; + size_t quoted_length = 0; + int i; + + current_index = (current_index + 1) % EB_MAX_KEYWORDS; + quoted_p = (unsigned char *)quoted_streams[current_index]; + stream_p = (const unsigned char *)stream; + + if (stream == NULL) + return ""; + + for (i = 0; i < stream_length && *stream_p != '\0'; i++) { + if (0x20 <= *stream_p && *stream_p <= 0x7f && *stream_p != '=') { + if (MAX_QUOTED_STREAM_LENGTH < quoted_length + 1) { + *quoted_p++ = '.'; + *quoted_p++ = '.'; + break; + } + *quoted_p++ = *stream_p; + quoted_length++; + } else { + if (MAX_QUOTED_STREAM_LENGTH < quoted_length + 3) { + *quoted_p++ = '.'; + *quoted_p++ = '.'; + break; + } + *quoted_p++ = '='; + *quoted_p++ = "0123456789ABCDEF" [*stream_p / 0x10]; + *quoted_p++ = "0123456789ABCDEF" [*stream_p % 0x10]; + quoted_length += 3; + } + stream_p++; + } + + *quoted_p = '\0'; + return quoted_streams[current_index]; +} + + +/* + * Return Quoted printable string. + */ +const char * +eb_quoted_string(const char *string) +{ + return eb_quoted_stream(string, strlen(string)); +} diff --git a/match.c b/match.c new file mode 100644 index 0000000..d8b8e63 --- /dev/null +++ b/match.c @@ -0,0 +1,583 @@ +/* + * 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 "build-post.h" + +/* + * Compare `word' and `pattern'. + * `word' must be terminated by `\0' and `pattern' is assumed to be + * `length' characters long. + * + * When `word' is equal to `pattern', or equal to the beginning of + * `pattern', 0 is returned. A positive or negateive integer is + * returned according as `pattern' is greater or less than `word'. + */ +int +eb_match_word(const char *word, const char *pattern, size_t length) +{ + int i = 0; + unsigned char *word_p = (unsigned char *)word; + unsigned char *pattern_p = (unsigned char *)pattern; + int result; + + LOG(("in: eb_match_word(word=%s, pattern=%s)", + eb_quoted_stream(word, EB_MAX_WORD_LENGTH), + eb_quoted_stream(pattern, length))); + + for (;;) { + if (length <= i) { + result = *word_p; + break; + } + if (*word_p == '\0') { + result = 0; + break; + } + + if (*word_p != *pattern_p) { + result = *word_p - *pattern_p; + break; + } + + word_p++; + pattern_p++; + i++; + } + + LOG(("out: eb_match_word() = %d", result)); + return result; +} + + +/* + * Compare `word' and `pattern' for pre-search. + * `word' must be terminated by `\0' and `pattern' is assumed to be + * `length' characters long. + * + * When `word' is equal to `pattern', or equal to the beginning of + * `pattern', 0 is returned. A positive or negateive integer is + * returned according as `pattern' is greater or less than `word'. + */ +int +eb_pre_match_word(const char *word, const char *pattern, size_t length) +{ + int i = 0; + unsigned char *word_p = (unsigned char *)word; + unsigned char *pattern_p = (unsigned char *)pattern; + int result; + + LOG(("in: eb_pre_match_word(word=%s, pattern=%s)", + eb_quoted_stream(word, EB_MAX_WORD_LENGTH), + eb_quoted_stream(pattern, length))); + + for (;;) { + if (length <= i) { + result = 0; + break; + } + if (*word_p == '\0') { + result = 0; + break; + } + + if (*word_p != *pattern_p) { + result = *word_p - *pattern_p; + break; + } + + word_p++; + pattern_p++; + i++; + } + + LOG(("out: eb_pre_match_word() = %d", result)); + return result; +} + + +/* + * Compare `word' and `pattern' in JIS X 0208. + * `word' must be terminated by `\0' and `pattern' is assumed to be + * `length' characters long. + * + * When the word is equal to the pattern, 0 is returned. A positive or + * negateive integer is returned according as `pattern' is greater or + * less than `word'. + */ +int +eb_exact_match_word_jis(const char *word, const char *pattern, size_t length) +{ + int i = 0; + unsigned char *word_p = (unsigned char *)word; + unsigned char *pattern_p = (unsigned char *)pattern; + int result; + + LOG(("in: eb_exact_match_word_jis(word=%s, pattern=%s)", + eb_quoted_stream(word, EB_MAX_WORD_LENGTH), + eb_quoted_stream(pattern, length))); + + for (;;) { + if (length <= i) { + result = *word_p; + break; + } + if (*word_p == '\0') { + /* ignore spaces in the tail of the pattern */ + while (i < length && *pattern_p == '\0') { + pattern_p++; + i++; + } + result = (i - length); + break; + } + if (*word_p != *pattern_p) { + result = *word_p - *pattern_p; + break; + } + + word_p++; + pattern_p++; + i++; + } + + LOG(("out: eb_exact_match_word_jis() = %d", result)); + return result; +} + + +/* + * Compare `word' and `pattern' in JIS X 0208 for pre-search. + * `word' must be terminated by `\0' and `pattern' is assumed to be + * `length' characters long. + * + * When the word is equal to the pattern, 0 is returned. A positive or + * negateive integer is returned according as `pattern' is greater or + * less than `word'. + */ +int +eb_exact_pre_match_word_jis(const char *word, const char *pattern, + size_t length) +{ + int i = 0; + unsigned char *word_p = (unsigned char *)word; + unsigned char *pattern_p = (unsigned char *)pattern; + int result; + + LOG(("in: eb_exact_pre_match_word_jis(word=%s, pattern=%s)", + eb_quoted_stream(word, EB_MAX_WORD_LENGTH), + eb_quoted_stream(pattern, length))); + + for (;;) { + if (length <= i) { + result = 0; + break; + } + if (*word_p == '\0') { + /* ignore spaces in the tail of the pattern */ + while (i < length && *pattern_p == '\0') { + pattern_p++; + i++; + } + result = (i - length); + break; + } + if (*word_p != *pattern_p) { + result = *word_p - *pattern_p; + break; + } + + word_p++; + pattern_p++; + i++; + } + + LOG(("out: eb_exact_pre_match_word_jis() = %d", result)); + return result; +} + + +/* + * Compare `word' and `pattern' in Latin1. + * `word' must be terminated by `\0' and `pattern' is assumed to be + * `length' characters long. + * + * When the word is equal to the pattern, 0 is returned. A positive or + * negateive integer is returned according as `pattern' is greater or + * less than `word'. + */ +int +eb_exact_match_word_latin(const char *word, const char *pattern, size_t length) +{ + int i = 0; + unsigned char *word_p = (unsigned char *)word; + unsigned char *pattern_p = (unsigned char *)pattern; + int result; + + LOG(("in: eb_exact_match_word_latin(word=%s, pattern=%s)", + eb_quoted_stream(word, EB_MAX_WORD_LENGTH), + eb_quoted_stream(pattern, length))); + + for (;;) { + if (length <= i) { + result = *word_p; + break; + } + if (*word_p == '\0') { + /* ignore spaces in the tail of the pattern */ + while (i < length && (*pattern_p == ' ' || *pattern_p == '\0')) { + pattern_p++; + i++; + } + result = (i - length); + break; + } + if (*word_p != *pattern_p) { + result = *word_p - *pattern_p; + break; + } + + word_p++; + pattern_p++; + i++; + } + + LOG(("out: eb_exact_match_word_latin() = %d", result)); + return result; +} + + +/* + * Compare `word' and `pattern' in Latin1 for pre-search. + * `word' must be terminated by `\0' and `pattern' is assumed to be + * `length' characters long. + * + * When the word is equal to the pattern, 0 is returned. A positive or + * negateive integer is returned according as `pattern' is greater or + * less than `word'. + */ +int +eb_exact_pre_match_word_latin(const char *word, const char *pattern, + size_t length) +{ + int i = 0; + unsigned char *word_p = (unsigned char *)word; + unsigned char *pattern_p = (unsigned char *)pattern; + int result; + + LOG(("in: eb_exact_pre_match_word_latin(word=%s, pattern=%s)", + eb_quoted_stream(word, EB_MAX_WORD_LENGTH), + eb_quoted_stream(pattern, length))); + + for (;;) { + if (length <= i) { + result = 0; + break; + } + if (*word_p == '\0') { + /* ignore spaces in the tail of the pattern */ + while (i < length && (*pattern_p == ' ' || *pattern_p == '\0')) { + pattern_p++; + i++; + } + result = (i - length); + break; + } + if (*word_p != *pattern_p) { + result = *word_p - *pattern_p; + break; + } + + word_p++; + pattern_p++; + i++; + } + + LOG(("out: eb_exact_pre_match_word_latin() = %d", result)); + return result; +} + + +/* + * Compare `word' and `pattern' in JIS X 0208. + * + * This function is equivalent to eb_match_word() except that this function + * ignores differences of kana (katakana and hiragana). The order of + * hiragana and katakana characters is: + * + * If `word' and `pattern' differ, the function compares their characters + * with the following rule: + * + * HIRAGANA `KA' < HIRAGANA `GA' < KATAKANA `KA' < KATAKANA `GA' + */ +int +eb_match_word_kana_group(const char *word, const char *pattern, size_t length) +{ + int i = 0; + unsigned char *word_p = (unsigned char *)word; + unsigned char *pattern_p = (unsigned char *)pattern; + unsigned char wc0, wc1, pc0, pc1; + int result; + + LOG(("in: eb_match_word_kana_group(word=%s, pattern=%s)", + eb_quoted_stream(word, EB_MAX_WORD_LENGTH), + eb_quoted_stream(pattern, length))); + + for (;;) { + if (length <= i) { + result = *word_p; + break; + } + if (*word_p == '\0') { + result = 0; + break; + } + if (length <= i + 1 || *(word_p + 1) == '\0') { + result = *word_p - *pattern_p; + break; + } + + wc0 = *word_p; + wc1 = *(word_p + 1); + pc0 = *pattern_p; + pc1 = *(pattern_p + 1); + + if ((wc0 == 0x24 || wc0 == 0x25) && (pc0 == 0x24 || pc0 == 0x25)) { + if (wc1 != pc1) { + result = ((wc0 << 8) + wc1) - ((pc0 << 8) + pc1); + break; + } + } else { + if (wc0 != pc0 || wc1 != pc1) { + result = ((wc0 << 8) + wc1) - ((pc0 << 8) + pc1); + break; + } + } + word_p += 2; + pattern_p += 2; + i += 2; + } + + LOG(("out: eb_match_word_kana_group() = %d", result)); + return result; +} + + +/* + * Compare `word' and `pattern' in JIS X 0208. + * + * This function is equivalent to eb_match_word() except that this function + * ignores differences of kana (katakana and hiragana). The order of + * hiragana and katakana characters is: + * + * If `word' and `pattern' differ, the function compares their characters + * with the following rule: + * + * HIRAGANA `KA' == KATAKANA `KA' < HIRAGANA `GA' == KATAKANA `GA'. + */ +int +eb_match_word_kana_single(const char *word, const char *pattern, size_t length) +{ + int i = 0; + unsigned char *word_p = (unsigned char *)word; + unsigned char *pattern_p = (unsigned char *)pattern; + unsigned char wc0, wc1, pc0, pc1; + int result; + + LOG(("in: eb_match_word_kana_single(word=%s, pattern=%s)", + eb_quoted_stream(word, EB_MAX_WORD_LENGTH), + eb_quoted_stream(pattern, length))); + + for (;;) { + if (length <= i) { + result = *word_p; + break; + } + if (*word_p == '\0') { + result = 0; + break; + } + if (length <= i + 1 || *(word_p + 1) == '\0') { + result = *word_p - *pattern_p; + break; + } + + wc0 = *word_p; + wc1 = *(word_p + 1); + pc0 = *pattern_p; + pc1 = *(pattern_p + 1); + + if ((wc0 == 0x24 || wc0 == 0x25) && (pc0 == 0x24 || pc0 == 0x25)) { + if (wc1 != pc1) { + result = wc1 - pc1; + break; + } + } else { + if (wc0 != pc0 || wc1 != pc1) { + result = ((wc0 << 8) + wc1) - ((pc0 << 8) + pc1); + break; + } + } + word_p += 2; + pattern_p += 2; + i += 2; + } + + LOG(("out: eb_match_word_kana_single() = %d", result)); + return result; +} + + +/* + * Compare `word' and `pattern' in JIS X 0208. + * + * This function is equivalent to eb_exact_match_word_jis() except that + * this function ignores differences of kana (katakana and hiragana). + * + * If `word' and `pattern' differ, the function compares their characters + * with the following rule: + * + * HIRAGANA `KA' < HIRAGANA `GA' < KATAKANA `KA' < KATAKANA `GA' + */ +int +eb_exact_match_word_kana_group(const char *word, const char *pattern, + size_t length) +{ + int i = 0; + unsigned char *word_p = (unsigned char *)word; + unsigned char *pattern_p = (unsigned char *)pattern; + unsigned char wc0, wc1, pc0, pc1; + int result; + + LOG(("in: eb_exact_match_word_kana_group(word=%s, pattern=%s)", + eb_quoted_stream(word, EB_MAX_WORD_LENGTH), + eb_quoted_stream(pattern, length))); + + for (;;) { + if (length <= i) { + result = *word_p; + break; + } + if (*word_p == '\0') { + result = - *pattern_p; + break; + } + if (length <= i + 1 || *(word_p + 1) == '\0') { + result = *word_p - *pattern_p; + break; + } + wc0 = *word_p; + wc1 = *(word_p + 1); + pc0 = *pattern_p; + pc1 = *(pattern_p + 1); + + if ((wc0 == 0x24 || wc0 == 0x25) && (pc0 == 0x24 || pc0 == 0x25)) { + if (wc1 != pc1) { + result = ((wc0 << 8) + wc1) - ((pc0 << 8) + pc1); + break; + } + } else { + if (wc0 != pc0 || wc1 != pc1) { + result = ((wc0 << 8) + wc1) - ((pc0 << 8) + pc1); + break; + } + } + word_p += 2; + pattern_p += 2; + i += 2; + } + + LOG(("out: eb_exact_match_word_kana_group() = %d", result)); + return result; +} + + +/* + * Compare `word' and `pattern' in JIS X 0208. + * + * This function is equivalent to eb_exact_match_word_jis() except that + * this function ignores differences of kana (katakana and hiragana). + * The order of hiragana and katakana characters is: + * + * If `word' and `pattern' differ, the function compares their characters + * with the following rule: + * + * HIRAGANA `KA' == KATAKANA `KA' < HIRAGANA `GA' == KATAKANA `GA'. + */ +int +eb_exact_match_word_kana_single(const char *word, const char *pattern, + size_t length) +{ + int i = 0; + unsigned char *word_p = (unsigned char *)word; + unsigned char *pattern_p = (unsigned char *)pattern; + unsigned char wc0, wc1, pc0, pc1; + int result; + + LOG(("in: eb_exact_match_word_kana_single(word=%s, pattern=%s)", + eb_quoted_stream(word, EB_MAX_WORD_LENGTH), + eb_quoted_stream(pattern, length))); + + for (;;) { + if (length <= i) { + result = *word_p; + break; + } + if (*word_p == '\0') { + result = - *pattern_p; + break; + } + if (length <= i + 1 || *(word_p + 1) == '\0') { + result = *word_p - *pattern_p; + break; + } + wc0 = *word_p; + wc1 = *(word_p + 1); + pc0 = *pattern_p; + pc1 = *(pattern_p + 1); + + if ((wc0 == 0x24 || wc0 == 0x25) && (pc0 == 0x24 || pc0 == 0x25)) { + if (wc1 != pc1) { + result = wc1 - pc1; + break; + } + } else { + if (wc0 != pc0 || wc1 != pc1) { + result = ((wc0 << 8) + wc1) - ((pc0 << 8) + pc1); + break; + } + } + word_p += 2; + pattern_p += 2; + i += 2; + } + + LOG(("out: eb_exact_match_word_kana_single() = %d", result)); + return result; +} diff --git a/menu.c b/menu.c new file mode 100644 index 0000000..80e5c66 --- /dev/null +++ b/menu.c @@ -0,0 +1,206 @@ +/* + * 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 "build-post.h" + +/* + * Examine whether the current subbook in `book' supports `MENU SEARCH' + * or not. + */ +int +eb_have_menu(EB_Book *book) +{ + eb_lock(&book->lock); + LOG(("in: eb_have_menu(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) + goto failed; + + /* + * Check for the index page of menu search. + */ + if (book->subbook_current->menu.start_page == 0) + goto failed; + + LOG(("out: eb_have_menu() = %d", 1)); + eb_unlock(&book->lock); + + return 1; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_have_menu() = %d", 0)); + eb_unlock(&book->lock); + return 0; +} + + +/* + * Menu. + */ +EB_Error_Code +eb_menu(EB_Book *book, EB_Position *position) +{ + EB_Error_Code error_code; + int page; + + eb_lock(&book->lock); + LOG(("in: eb_menu(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Check for the page number of menu search. + */ + page = book->subbook_current->menu.start_page; + if (page == 0) { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + + /* + * Copy the position to `position'. + */ + position->page = page; + position->offset = 0; + + LOG(("out: eb_menu(position={%d,%d}) = %s", + position->page, position->offset, eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_menu() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + +/* + * Examine whether the current subbook in `book' supports `GRAPHIC MENU SEARCH' + * or not. + */ +int +eb_have_image_menu(EB_Book *book) +{ + eb_lock(&book->lock); + LOG(("in: eb_have_image_menu(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) + goto failed; + + /* + * Check for the index page of graphic menu search. + */ + if (book->subbook_current->image_menu.start_page == 0) + goto failed; + + LOG(("out: eb_have_image_menu() = %d", 1)); + eb_unlock(&book->lock); + + return 1; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_have_image_menu() = %d", 0)); + eb_unlock(&book->lock); + return 0; +} + + +/* + * Graphic Menu. + */ +EB_Error_Code +eb_image_menu(EB_Book *book, EB_Position *position) +{ + EB_Error_Code error_code; + int page; + + eb_lock(&book->lock); + LOG(("in: eb_image_menu(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Check for the page number of graphic menu search. + */ + page = book->subbook_current->image_menu.start_page; + if (page == 0) { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + + /* + * Copy the position to `position'. + */ + position->page = page; + position->offset = 0; + + LOG(("out: eb_image_menu(position={%d,%d}) = %s", + position->page, position->offset, eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_image_menu() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} diff --git a/multi.c b/multi.c new file mode 100644 index 0000000..b91a41c --- /dev/null +++ b/multi.c @@ -0,0 +1,839 @@ +/* + * 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 "build-post.h" + +/* + * Get information about the current subbook. + */ +EB_Error_Code +eb_load_multi_searches(EB_Book *book) +{ + EB_Error_Code error_code; + EB_Subbook *subbook; + EB_Multi_Search *multi; + EB_Search *entry; + char buffer[EB_SIZE_PAGE]; + char *buffer_p; + int index_count; + int index_id; + int i, j, k; + + LOG(("in: eb_load_multi_searches(book=%d)", book->code)); + + subbook = book->subbook_current; + + for (i = 0, multi = subbook->multis; i < subbook->multi_count; + i++, multi++) { + /* + * Read the index table page of the multi search. + */ + if (zio_lseek(&subbook->text_zio, + ((off_t) multi->search.start_page - 1) * EB_SIZE_PAGE, SEEK_SET) + < 0) { + error_code = EB_ERR_FAIL_SEEK_TEXT; + goto failed; + } + if (zio_read(&subbook->text_zio, buffer, EB_SIZE_PAGE) + != EB_SIZE_PAGE) { + error_code = EB_ERR_FAIL_READ_TEXT; + goto failed; + } + + /* + * Get the number of entries in this multi search. + */ + multi->entry_count = eb_uint2(buffer); + if (EB_MAX_MULTI_SEARCHES <= multi->entry_count) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + buffer_p = buffer + 16; + for (j = 0, entry = multi->entries; + j < multi->entry_count; j++, entry++) { + /* + * Get the number of indexes in this entry, and title + * of this entry. + */ + index_count = eb_uint1(buffer_p); + strncpy(entry->label, buffer_p + 2, EB_MAX_MULTI_LABEL_LENGTH); + entry->label[EB_MAX_MULTI_LABEL_LENGTH] = '\0'; + eb_jisx0208_to_euc(entry->label, entry->label); + buffer_p += EB_MAX_MULTI_LABEL_LENGTH + 2; + + /* + * Initialize index page information of the entry. + */ + for (k = 0; k < index_count; k++) { + /* + * Get the index page information of the entry. + */ + index_id = eb_uint1(buffer_p); + switch (index_id) { + case 0x71: + case 0x91: + case 0xa1: + if (entry->start_page != 0 && entry->index_id != 0x71) + break; + entry->start_page = eb_uint4(buffer_p + 2); + entry->end_page = entry->start_page + + eb_uint4(buffer_p + 6) - 1; + entry->index_id = index_id; + entry->katakana = EB_INDEX_STYLE_ASIS; + entry->lower = EB_INDEX_STYLE_CONVERT; + entry->mark = EB_INDEX_STYLE_ASIS; + entry->long_vowel = EB_INDEX_STYLE_ASIS; + entry->double_consonant = EB_INDEX_STYLE_ASIS; + entry->contracted_sound = EB_INDEX_STYLE_ASIS; + entry->voiced_consonant = EB_INDEX_STYLE_ASIS; + entry->small_vowel = EB_INDEX_STYLE_ASIS; + entry->p_sound = EB_INDEX_STYLE_ASIS; + entry->space = EB_INDEX_STYLE_ASIS; + break; + case 0x01: + entry->candidates_page = eb_uint4(buffer_p + 2); + break; + } + buffer_p += 16; + } + } + } + + LOG(("out: eb_load_multi_searches() = %s", eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_load_multi_searches() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Default multi search titles (written in JIS X 0208). + */ +static const char *default_multi_titles_jisx0208[] = { + "J#9g8!:w#1", /* Multi search 1. */ + "J#9g8!:w#2", /* Multi search 2. */ + "J#9g8!:w#3", /* Multi search 3. */ + "J#9g8!:w#4", /* Multi search 4. */ + "J#9g8!:w#5", /* Multi search 5. */ + "J#9g8!:w#6", /* Multi search 6. */ + "J#9g8!:w#7", /* Multi search 7. */ + "J#9g8!:w#8", /* Multi search 8. */ + "J#9g8!:w#9", /* Multi search 9. */ + "J#9g8!:w#1#0", /* Multi search 10. */ +}; + +/* + * Default multi search titles (written in ASCII, subset of ISO 8859-1). + */ +static const char *default_multi_titles_latin[] = { + "Multi search 1", + "Multi search 2", + "Multi search 3", + "Multi search 4", + "Multi search 5", + "Multi search 6", + "Multi search 7", + "Multi search 8", + "Multi search 9", + "Multi search 10", +}; + +/* + * Load multi search titles. + */ +EB_Error_Code +eb_load_multi_titles(EB_Book *book) +{ + EB_Error_Code error_code; + EB_Subbook *subbook; + char buffer[EB_SIZE_PAGE]; + int title_count; + char *title; + size_t offset; + int i; + + LOG(("in: eb_load_multi_searches(book=%d)", book->code)); + + subbook = book->subbook_current; + + /* + * Set default titles. + */ + if (book->character_code == EB_CHARCODE_ISO8859_1) { + for (i = 0; i < subbook->multi_count; i++) { + title = subbook->multis[i].title; + strcpy(title, default_multi_titles_latin[i]); + } + } else { + for (i = 0; i < subbook->multi_count; i++) { + title = subbook->multis[i].title; + strcpy(title, default_multi_titles_jisx0208[i]); + eb_jisx0208_to_euc(title, title); + } + } + + if (book->disc_code != EB_DISC_EPWING || subbook->search_title_page == 0) + goto succeeded; + + /* + * Read the page of the multi search. + */ + if (zio_lseek(&subbook->text_zio, + ((off_t) subbook->search_title_page - 1) * EB_SIZE_PAGE, SEEK_SET) + < 0) { + error_code = EB_ERR_FAIL_SEEK_TEXT; + goto failed; + } + if (zio_read(&subbook->text_zio, buffer, EB_SIZE_PAGE) != EB_SIZE_PAGE) { + error_code = EB_ERR_FAIL_READ_TEXT; + goto failed; + } + + title_count = eb_uint2(buffer); + if (EB_MAX_SEARCH_TITLES < title_count) + title_count = EB_MAX_SEARCH_TITLES; + + /* + * We need titles for multi searches only. + * titles[ 0]: title for word and endword searches. + * titles[ 1]: title for keyword search. + * titles[ 2]: common title for all multi searches. + * (we don't need this) + * titles[ 3]: title for multi search 1. + * : + * titles[12]: title for multi search 10. + * titles[13]: title for menu search. + * + * The offset of titles[3] is: + * the number of entries(2bytes) + * + reserved 1 (68bytes) + * + title for word and endword searches (70bytes) + * + title for keyword search (70bytes) + * + common title for all multi searches (70bytes) + * + reserved 2 (70bytes) + * = 2 + 68 + 70 + 70 + 70 + 70 = 350 + */ + for (i = 4, offset = 350; i < EB_MAX_SEARCH_TITLES; i++, offset += 70) { + if (subbook->multi_count <= i - 4) + break; + if (eb_uint2(buffer + offset) != 0x02) + continue; + + /* + * Each titles[] consists of + * parameter (2bytes) + * short title (16bytes) + * long title (32bytes) + * We get long title rather than short one. + */ + title = subbook->multis[i - 4].title; + strncpy(title, buffer + offset + 2 + 16, EB_MAX_MULTI_TITLE_LENGTH); + title[EB_MAX_MULTI_TITLE_LENGTH] = '\0'; + eb_jisx0208_to_euc(title, title); + } + +succeeded: + LOG(("out: eb_load_multi_titles() = %s", eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_load_multi_titles() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Examine whether the current subbook in `book' supports `MULTI SEARCH' + * or not. + */ +int +eb_have_multi_search(EB_Book *book) +{ + eb_lock(&book->lock); + LOG(("in: eb_have_multi_search(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) + goto failed; + + if (book->subbook_current->multi_count == 0) + goto failed; + + LOG(("out: eb_have_multi_search() = %d", 1)); + eb_unlock(&book->lock); + + return 1; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_have_multi_search() = %d", 0)); + eb_unlock(&book->lock); + return 0; +} + + +/* + * Return a title of the multi search `multi_id'. + */ +EB_Error_Code +eb_multi_title(EB_Book *book, EB_Multi_Search_Code multi_id, char *title) +{ + EB_Error_Code error_code; + EB_Subbook *subbook; + + eb_lock(&book->lock); + LOG(("in: eb_multi_title(book=%d, multi_id=%d)", + (int)book->code, (int)multi_id)); + + /* + * The book must have been bound. + */ + if (book->path == NULL) { + error_code = EB_ERR_UNBOUND_BOOK; + goto failed; + } + + /* + * Current subbook must have been set. + */ + subbook = book->subbook_current; + if (subbook == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * `multi_id' must be a valid code. + */ + if (multi_id < 0 || subbook->multi_count <= multi_id) { + error_code = EB_ERR_NO_SUCH_MULTI_ID; + goto failed; + } + + strcpy(title, subbook->multis[multi_id].title); + + LOG(("out: eb_multi_title(title=%s) = %s", title, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *title = '\0'; + LOG(("out: eb_multi_title() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Return a list of multi search ids in `book'. + */ +EB_Error_Code +eb_multi_search_list(EB_Book *book, EB_Multi_Search_Code *search_list, + int *search_count) +{ + EB_Error_Code error_code; + EB_Subbook_Code *list_p; + int i; + + eb_lock(&book->lock); + LOG(("in: eb_multi_search_list(book=%d)", (int)book->code)); + + /* + * The book must have been bound. + */ + if (book->path == NULL) { + error_code = EB_ERR_UNBOUND_BOOK; + goto failed; + } + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + *search_count = book->subbook_current->multi_count; + for (i = 0, list_p = search_list; i < *search_count; i++, list_p++) + *list_p = i; + + LOG(("out: eb_multi_search_list(search_count=%d) = %s", *search_count, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *search_count = 0; + LOG(("out: eb_multi_search_list() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Return the number of entries that the multi search `multi_id' in `book'. + */ +EB_Error_Code +eb_multi_entry_count(EB_Book *book, EB_Multi_Search_Code multi_id, + int *entry_count) +{ + EB_Error_Code error_code; + + eb_lock(&book->lock); + LOG(("in: eb_multi_entry_count(book=%d, multi_id=%d)", (int)book->code, + (int)multi_id)); + + /* + * The book must have been bound. + */ + if (book->path == NULL) { + error_code = EB_ERR_UNBOUND_BOOK; + goto failed; + } + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * `multi_id' must be a valid code. + */ + if (multi_id < 0 || book->subbook_current->multi_count <= multi_id) { + error_code = EB_ERR_NO_SUCH_MULTI_ID; + goto failed; + } + + *entry_count = book->subbook_current->multis[multi_id].entry_count; + + LOG(("out: eb_multi_entry_count(entry_count=%d) = %s", (int)*entry_count, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *entry_count = 0; + LOG(("out: eb_multi_entry_count() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Return a list of entries that the multi search `multi_id' in `book' has. + * (Legacy function) + */ +EB_Error_Code +eb_multi_entry_list(EB_Book *book, EB_Multi_Search_Code multi_id, + int *entry_list, int *entry_count) +{ + EB_Error_Code error_code; + EB_Subbook_Code *list_p; + int i; + + error_code = eb_multi_entry_count(book, multi_id, entry_count); + if (error_code != EB_SUCCESS) + return error_code; + + for (i = 0, list_p = entry_list; i < *entry_count; i++, list_p++) + *list_p = i; + + return EB_SUCCESS; +} + + +/* + * Return a lable of the entry `entry_index' in the multi search `multi_id'. + */ +EB_Error_Code +eb_multi_entry_label(EB_Book *book, EB_Multi_Search_Code multi_id, + int entry_index, char *label) +{ + EB_Error_Code error_code; + EB_Subbook *subbook; + + eb_lock(&book->lock); + LOG(("in: eb_multi_entry_label(book=%d, multi_id=%d, entry_index=%d)", + (int)book->code, (int)multi_id, entry_index)); + + /* + * The book must have been bound. + */ + if (book->path == NULL) { + error_code = EB_ERR_UNBOUND_BOOK; + goto failed; + } + + /* + * Current subbook must have been set. + */ + subbook = book->subbook_current; + if (subbook == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * `multi_id' must be a valid code. + */ + if (multi_id < 0 || subbook->multi_count <= multi_id) { + error_code = EB_ERR_NO_SUCH_MULTI_ID; + goto failed; + } + + /* + * `entry_index' must be a valid code. + */ + if (entry_index < 0 + || subbook->multis[multi_id].entry_count <= entry_index) { + error_code = EB_ERR_NO_SUCH_ENTRY_ID; + goto failed; + } + + strcpy(label, subbook->multis[multi_id].entries[entry_index].label); + + LOG(("out: eb_multi_entry_label(label=%s) = %s", label, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *label = '\0'; + LOG(("out: eb_multi_entry_label() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Whether the entry `entry_index' in the multi search `multi_id' has + * candidates or not. + */ +int +eb_multi_entry_have_candidates(EB_Book *book, EB_Multi_Search_Code multi_id, + int entry_index) +{ + EB_Multi_Search *multi; + + eb_lock(&book->lock); + LOG(("in: eb_multi_entry_have_candidates(book=%d, multi_id=%d, \ +entry_index=%d)", + (int)book->code, (int)multi_id, entry_index)); + + /* + * The book must have been bound. + */ + if (book->path == NULL) + goto failed; + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) + goto failed; + + /* + * `multi_id' must be a valid code. + */ + if (multi_id < 0 || book->subbook_current->multi_count <= multi_id) + goto failed; + + /* + * `entry_index' must be a valid code. + */ + multi = book->subbook_current->multis + multi_id; + if (entry_index < 0 || multi->entry_count <= entry_index) + goto failed; + + if (multi->entries[entry_index].candidates_page == 0) + goto failed; + + LOG(("out: eb_multi_entry_have_candidates() = %d", 1)); + eb_unlock(&book->lock); + + return 1; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_multi_entry_have_candidates() = %d", 0)); + eb_unlock(&book->lock); + return 0; +} + + +/* + * Return a position of candidates for the entry `entry_index' in the multi + * search `multi_id'. + */ +EB_Error_Code +eb_multi_entry_candidates(EB_Book *book, EB_Multi_Search_Code multi_id, + int entry_index, EB_Position *position) +{ + EB_Error_Code error_code; + EB_Multi_Search *multi; + + eb_lock(&book->lock); + LOG(("in: eb_multi_entry_candidates(book=%d, multi_id=%d, entry_index=%d)", + (int)book->code, (int)multi_id, entry_index)); + + /* + * The book must have been bound. + */ + if (book->path == NULL) { + error_code = EB_ERR_UNBOUND_BOOK; + goto failed; + } + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * `multi_id' must be a valid code. + */ + if (multi_id < 0 || book->subbook_current->multi_count <= multi_id) { + error_code = EB_ERR_NO_SUCH_MULTI_ID; + goto failed; + } + + /* + * `entry_index' must be a valid code. + */ + multi = book->subbook_current->multis + multi_id; + if (entry_index < 0 || multi->entry_count <= entry_index) { + error_code = EB_ERR_NO_SUCH_ENTRY_ID; + goto failed; + } + + if (multi->entries[entry_index].candidates_page == 0) { + error_code = EB_ERR_NO_CANDIDATES; + goto failed; + } + + position->page = multi->entries[entry_index].candidates_page; + position->offset = 0; + + LOG(("out: eb_multi_entry_candidates(position={%d,%d}) = %s", + position->page, position->offset, eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_multi_entry_candidates() = %s", + eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Multi search. + */ +EB_Error_Code +eb_search_multi(EB_Book *book, EB_Multi_Search_Code multi_id, + const char * const input_words[]) +{ + EB_Error_Code error_code; + EB_Search_Context *context; + EB_Search *entry; + EB_Word_Code word_code; + int word_count; + int i; + + eb_lock(&book->lock); + LOG(("in: eb_search_multi(book=%d, multi_id=%d, input_words=[below])", + (int)book->code, (int)multi_id)); + + if (eb_log_flag) { + for (i = 0; i < EB_MAX_KEYWORDS && input_words[i] != NULL; i++) { + LOG((" input_words[%d]=%s", i, + eb_quoted_string(input_words[i]))); + } + LOG((" input_words[%d]=NULL", i)); + } + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Check whether the current subbook has keyword search. + */ + if (multi_id < 0 || book->subbook_current->multi_count <= multi_id) { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + + /* + * Attach a search context for each keyword, and pre-search the + * keywords. + */ + eb_reset_search_contexts(book); + word_count = 0; + + for (i = 0, entry = book->subbook_current->multis[multi_id].entries; + i < book->subbook_current->multis[multi_id].entry_count; + i++, entry++) { + + if (input_words[i] == NULL) + break; + + /* + * Initialize search context. + */ + context = book->search_contexts + word_count; + context->code = EB_SEARCH_MULTI; + + /* + * Choose comparison functions. + */ + if (entry->candidates_page == 0) { + if (book->character_code == EB_CHARCODE_ISO8859_1) { + context->compare_pre = eb_pre_match_word; + context->compare_single = eb_match_word; + context->compare_group = eb_match_word; + } else { + context->compare_pre = eb_pre_match_word; + context->compare_single = eb_match_word; + context->compare_group = eb_match_word_kana_group; + } + } else { + if (book->character_code == EB_CHARCODE_ISO8859_1) { + context->compare_pre = eb_exact_pre_match_word_latin; + context->compare_single = eb_exact_match_word_latin; + context->compare_group = eb_exact_match_word_latin; + } else { + context->compare_pre = eb_exact_pre_match_word_jis; + context->compare_single = eb_exact_match_word_jis; + context->compare_group = eb_exact_match_word_kana_group; + } + } + context->page = entry->start_page; + if (context->page == 0) + continue; + + /* + * Make a fixed word and a canonicalized word to search from + * `input_words[i]'. + */ + error_code = eb_set_multiword(book, multi_id, i, input_words[i], + context->word, context->canonicalized_word, &word_code); + if (error_code == EB_ERR_EMPTY_WORD) + continue; + else if (error_code != EB_SUCCESS) + goto failed; + + /* + * Pre-search. + */ + error_code = eb_presearch_word(book, context); + if (error_code != EB_SUCCESS) + goto failed; + + word_count++; + } + if (word_count == 0) { + error_code = EB_ERR_NO_WORD; + goto failed; + } else if (book->subbook_current->multis[multi_id].entry_count <= i + && input_words[i] != NULL) { + error_code = EB_ERR_TOO_MANY_WORDS; + goto failed; + } + + /* + * Set `EB_SEARCH_NONE' to the rest unused search context. + */ + for (i = word_count; i < EB_MAX_KEYWORDS; i++) + (book->search_contexts + i)->code = EB_SEARCH_NONE; + + LOG(("out: eb_search_multi() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_reset_search_contexts(book); + LOG(("out: eb_search_multi() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} diff --git a/narwalt.c b/narwalt.c new file mode 100644 index 0000000..f678b6e --- /dev/null +++ b/narwalt.c @@ -0,0 +1,626 @@ +/* + * 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 "appendix.h" +#include "build-post.h" + +/* + * Unexported functions. + */ +static EB_Error_Code eb_narrow_character_text_jis(EB_Appendix *appendix, + int character_number, char *text); +static EB_Error_Code eb_narrow_character_text_latin(EB_Appendix *appendix, + int character_number, char *text); + +/* + * Hash macro for cache data. + */ +#define EB_HASH_ALT_CACHE(c) ((c) & 0x0f) + + +/* + * Examine whether the current subbook in `book' has a narrow font + * alternation or not. + */ +int +eb_have_narrow_alt(EB_Appendix *appendix) +{ + eb_lock(&appendix->lock); + LOG(("in: eb_have_narrow_alt(appendix=%d)", (int)appendix->code)); + + /* + * Current subbook must have been set. + */ + if (appendix->subbook_current == NULL) + goto failed; + + if (appendix->subbook_current->narrow_page == 0) + goto failed; + + LOG(("out: eb_have_narrow_alt() = %d", 1)); + eb_unlock(&appendix->lock); + + return 1; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_have_narrow_alt() = %d", 0)); + eb_unlock(&appendix->lock); + return 0; +} + + +/* + * Look up the character number of the start of the narrow font alternation + * of the current subbook in `book'. + */ +EB_Error_Code +eb_narrow_alt_start(EB_Appendix *appendix, int *start) +{ + EB_Error_Code error_code; + + eb_lock(&appendix->lock); + LOG(("in: eb_narrow_alt_start(appendix=%d)", (int)appendix->code)); + + /* + * Current subbook must have been set. + */ + if (appendix->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_APPSUB; + goto failed; + } + + if (appendix->subbook_current->narrow_page == 0) { + error_code = EB_ERR_NO_ALT; + goto failed; + } + + *start = appendix->subbook_current->narrow_start; + + LOG(("out: eb_narrow_alt_start(start=%d) = %s", *start, + eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *start = -1; + LOG(("out: eb_narrow_alt_start() = %s", eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} + + +/* + * Return the character number of the end of the narrow font alternation + * of the current subbook in `book'. + */ +EB_Error_Code +eb_narrow_alt_end(EB_Appendix *appendix, int *end) +{ + EB_Error_Code error_code; + + eb_lock(&appendix->lock); + LOG(("in: eb_narrow_alt_end(appendix=%d)", (int)appendix->code)); + + /* + * Current subbook must have been set. + */ + if (appendix->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_APPSUB; + goto failed; + } + + if (appendix->subbook_current->narrow_page == 0) { + error_code = EB_ERR_NO_ALT; + goto failed; + } + + *end = appendix->subbook_current->narrow_end; + + LOG(("out: eb_narrow_alt_end(end=%d) = %s", *end, + eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *end = -1; + LOG(("out: eb_narrow_alt_end() = %s", eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} + + +/* + * Get the alternation text of the character number `character_number'. + */ +EB_Error_Code +eb_narrow_alt_character_text(EB_Appendix *appendix, int character_number, + char *text) +{ + EB_Error_Code error_code; + + eb_lock(&appendix->lock); + LOG(("in: eb_narrow_alt_character_text(appendix=%d, character_number=%d)", + (int)appendix->code, character_number)); + + /* + * Current subbook must have been set. + */ + if (appendix->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_APPSUB; + goto failed; + } + + /* + * The narrow font must exist in the current subbook. + */ + if (appendix->subbook_current->narrow_page == 0) { + error_code = EB_ERR_NO_ALT; + goto failed; + } + + if (appendix->subbook_current->character_code == EB_CHARCODE_ISO8859_1) { + error_code = eb_narrow_character_text_latin(appendix, + character_number, text); + } else { + error_code = eb_narrow_character_text_jis(appendix, character_number, + text); + } + if (error_code != EB_SUCCESS) + goto failed; + + LOG(("out: eb_narrow_alt_character_text(text=%s) = %s", + eb_quoted_string(text), eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *text = '\0'; + LOG(("out: eb_narrow_alt_character_text() = %s", + eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} + + +/* + * Get the alternation text of the character number `character_number'. + */ +static EB_Error_Code +eb_narrow_character_text_jis(EB_Appendix *appendix, int character_number, + char *text) +{ + EB_Error_Code error_code; + int start; + int end; + off_t location; + EB_Alternation_Cache *cachep; + + LOG(("in: eb_narrow_alt_character_text_jis(appendix=%d, \ +character_number=%d)", + (int)appendix->code, character_number)); + + start = appendix->subbook_current->narrow_start; + end = appendix->subbook_current->narrow_end; + + /* + * Check for `character_number'. Is it in a font? + * This test works correctly even when the font doesn't exist in + * the current subbook because `start' and `end' have set to -1 + * in the case. + */ + if (character_number < start + || end < character_number + || (character_number & 0xff) < 0x21 + || 0x7e < (character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + + /* + * Calculate the location of alternation data. + */ + location + = (appendix->subbook_current->narrow_page - 1) * EB_SIZE_PAGE + + (((character_number >> 8) - (start >> 8)) * 0x5e + + (character_number & 0xff) - (start & 0xff)) + * (EB_MAX_ALTERNATION_TEXT_LENGTH + 1); + + /* + * Check for the cache data. + */ + cachep = appendix->narrow_cache + EB_HASH_ALT_CACHE(character_number); + if (cachep->character_number == character_number) { + memcpy(text, cachep->text, EB_MAX_ALTERNATION_TEXT_LENGTH + 1); + goto succeeded; + } + + /* + * Read the alternation data. + */ + if (zio_lseek(&appendix->subbook_current->zio, location, SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_APP; + goto failed; + } + cachep->character_number = -1; + if (zio_read(&appendix->subbook_current->zio, cachep->text, + EB_MAX_ALTERNATION_TEXT_LENGTH + 1) + != EB_MAX_ALTERNATION_TEXT_LENGTH + 1) { + error_code = EB_ERR_FAIL_READ_APP; + goto failed; + } + + /* + * Update cache data. + */ + memcpy(text, cachep->text, EB_MAX_ALTERNATION_TEXT_LENGTH + 1); + cachep->text[EB_MAX_ALTERNATION_TEXT_LENGTH] = '\0'; + cachep->character_number = character_number; + + succeeded: + LOG(("out: eb_narrow_alt_character_text_jis(text=%s) = %s", + eb_quoted_string(text), eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *text = '\0'; + LOG(("out: eb_narrow_alt_character_text_jis() = %s", + eb_error_string(error_code))); + return error_code; +} + + +/* + * Get the alternation text of the character number `character_number'. + */ +static EB_Error_Code +eb_narrow_character_text_latin(EB_Appendix *appendix, int character_number, + char *text) +{ + EB_Error_Code error_code; + int start; + int end; + off_t location; + EB_Alternation_Cache *cache_p; + + LOG(("in: eb_narrow_alt_character_text_latin(appendix=%d, \ +character_number=%d)", + (int)appendix->code, character_number)); + + start = appendix->subbook_current->narrow_start; + end = appendix->subbook_current->narrow_end; + + /* + * Check for `character_number'. Is it in a font? + * This test works correctly even when the font doesn't exist in + * the current subbook because `start' and `end' have set to -1 + * in the case. + */ + if (character_number < start + || end < character_number + || (character_number & 0xff) < 0x01 + || 0xfe < (character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + + /* + * Calculate the location of alternation data. + */ + location + = (appendix->subbook_current->narrow_page - 1) * EB_SIZE_PAGE + + (((character_number >> 8) - (start >> 8)) * 0xfe + + (character_number & 0xff) - (start & 0xff)) + * (EB_MAX_ALTERNATION_TEXT_LENGTH + 1); + + /* + * Check for the cache data. + */ + cache_p = appendix->narrow_cache + EB_HASH_ALT_CACHE(character_number); + if (cache_p->character_number == character_number) { + memcpy(text, cache_p->text, EB_MAX_ALTERNATION_TEXT_LENGTH + 1); + goto succeeded; + } + + /* + * Read the alternation data. + */ + if (zio_lseek(&appendix->subbook_current->zio, location, SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_APP; + goto failed; + } + cache_p->character_number = -1; + if (zio_read(&appendix->subbook_current->zio, cache_p->text, + EB_MAX_ALTERNATION_TEXT_LENGTH + 1) + != EB_MAX_ALTERNATION_TEXT_LENGTH + 1) { + error_code = EB_ERR_FAIL_READ_APP; + goto failed; + } + + /* + * Update cache data. + */ + memcpy(text, cache_p->text, EB_MAX_ALTERNATION_TEXT_LENGTH + 1); + cache_p->text[EB_MAX_ALTERNATION_TEXT_LENGTH] = '\0'; + cache_p->character_number = character_number; + + succeeded: + LOG(("out: eb_narrow_alt_character_text_latin(text=%s) = %s", + eb_quoted_string(text), eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *text = '\0'; + LOG(("out: eb_narrow_alt_character_text_latin() = %s", + eb_error_string(error_code))); + return error_code; +} + + +/* + * Return next `n'th character number from `*character_number'. + */ +EB_Error_Code +eb_forward_narrow_alt_character(EB_Appendix *appendix, int n, + int *character_number) +{ + EB_Error_Code error_code; + int start; + int end; + int i; + + if (n < 0) { + return eb_backward_narrow_alt_character(appendix, -n, + character_number); + } + + eb_lock(&appendix->lock); + LOG(("in: eb_forward_narrow_alt_character(appendix=%d, n=%d, \ +character_number=%d)", + (int)appendix->code, n, *character_number)); + + /* + * Current subbook must have been set. + */ + if (appendix->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_APPSUB; + goto failed; + } + + /* + * The narrow font must exist in the current subbook. + */ + if (appendix->subbook_current->narrow_page == 0) { + error_code = EB_ERR_NO_ALT; + goto failed; + } + + start = appendix->subbook_current->narrow_start; + end = appendix->subbook_current->narrow_end; + + if (appendix->subbook_current->character_code == EB_CHARCODE_ISO8859_1) { + /* + * Check for `*character_number'. (ISO 8859 1) + */ + if (*character_number < start + || end < *character_number + || (*character_number & 0xff) < 0x01 + || 0xfe < (*character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + + /* + * Get character number. (ISO 8859 1) + */ + for (i = 0; i < n; i++) { + if (0xfe <= (*character_number & 0xff)) + *character_number += 3; + else + *character_number += 1; + if (end < *character_number) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + } + } else { + /* + * Check for `*character_number'. (JIS X 0208) + */ + if (*character_number < start + || end < *character_number + || (*character_number & 0xff) < 0x21 + || 0x7e < (*character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + + /* + * Get character number. (JIS X 0208) + */ + for (i = 0; i < n; i++) { + if (0x7e <= (*character_number & 0xff)) + *character_number += 0xa3; + else + *character_number += 1; + if (end < *character_number) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + } + } + + LOG(("out: eb_forkward_narrow_alt_character(character_number=%d) = %s", + *character_number, eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *character_number = -1; + LOG(("out: eb_forward_narrow_alt_character() = %s", + eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} + + +/* + * Return previous `n'th character number from `*character_number'. + */ +EB_Error_Code +eb_backward_narrow_alt_character(EB_Appendix *appendix, int n, + int *character_number) +{ + EB_Error_Code error_code; + int start; + int end; + int i; + + if (n < 0) { + return eb_forward_narrow_alt_character(appendix, -n, character_number); + } + + eb_lock(&appendix->lock); + LOG(("in: eb_backward_narrow_alt_character(appendix=%d, n=%d, \ +character_number=%d)", + (int)appendix->code, n, *character_number)); + + /* + * Current subbook must have been set. + */ + if (appendix->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_APPSUB; + goto failed; + } + + /* + * The narrow font must exist in the current subbook. + */ + if (appendix->subbook_current->narrow_page == 0) { + error_code = EB_ERR_NO_ALT; + goto failed; + } + + start = appendix->subbook_current->narrow_start; + end = appendix->subbook_current->narrow_end; + + if (appendix->subbook_current->character_code == EB_CHARCODE_ISO8859_1) { + /* + * Check for `*character_number'. (ISO 8859 1) + */ + if (*character_number < start + || end < *character_number + || (*character_number & 0xff) < 0x01 + || 0xfe < (*character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + + /* + * Get character number. (ISO 8859 1) + */ + for (i = 0; i < n; i++) { + if ((*character_number & 0xff) <= 0x01) + *character_number -= 3; + else + *character_number -= 1; + if (*character_number < start) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + } + } else { + /* + * Check for `*character_number'. (JIS X 0208) + */ + if (*character_number < start + || end < *character_number + || (*character_number & 0xff) < 0x21 + || 0x7e < (*character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + + /* + * Get character number. (JIS X 0208) + */ + for (i = 0; i < n; i++) { + if ((*character_number & 0xff) <= 0x21) + *character_number -= 0xa3; + else + *character_number -= 1; + if (*character_number < start) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + } + } + + LOG(("out: eb_backward_narrow_alt_character(character_number=%d) = %s", + *character_number, eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *character_number = -1; + LOG(("out: eb_backward_narrow_alt_character() = %s", + eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} diff --git a/narwfont.c b/narwfont.c new file mode 100644 index 0000000..9e1eb37 --- /dev/null +++ b/narwfont.c @@ -0,0 +1,1097 @@ +/* + * 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" + +/* + * Unexported functions. + */ +static EB_Error_Code eb_narrow_character_bitmap_jis(EB_Book *book, + int character_number, char *bitmap); +static EB_Error_Code eb_narrow_character_bitmap_latin(EB_Book *book, + int character_number, char *bitmap); + + +/* + * Open a font file. + */ +EB_Error_Code +eb_open_narrow_font_file(EB_Book *book, EB_Font_Code font_code) +{ + EB_Error_Code error_code; + EB_Subbook *subbook; + EB_Font *narrow_font; + char font_path_name[EB_MAX_PATH_LENGTH + 1]; + Zio_Code zio_code; + + LOG(("in: eb_open_narrow_font(book=%d, font_code=%d)", + (int)book->code, (int)font_code)); + + subbook = book->subbook_current; + narrow_font = subbook->narrow_fonts + font_code; + + if (narrow_font->font_code == EB_FONT_INVALID) { + error_code = EB_ERR_FAIL_OPEN_FONT; + goto failed; + } + if (0 <= zio_file(&narrow_font->zio)) + goto succeeded; + + /* + * If the book is EBWING, open the narrow font file. + * (In EB books, font data are stored in the `START' file.) + */ + zio_code = ZIO_INVALID; + + if (book->disc_code == EB_DISC_EB) { + if (narrow_font->initialized) { + if (zio_mode(&narrow_font->zio) != ZIO_INVALID) + zio_code = ZIO_REOPEN; + } else { + zio_code = zio_mode(&subbook->text_zio); + } + eb_compose_path_name2(book->path, subbook->directory_name, + subbook->text_file_name, font_path_name); + + } else { + if (narrow_font->initialized) { + if (zio_mode(&narrow_font->zio) != ZIO_INVALID) + zio_code = ZIO_REOPEN; + eb_compose_path_name3(book->path, subbook->directory_name, + subbook->gaiji_directory_name, narrow_font->file_name, + font_path_name); + } else { + eb_canonicalize_file_name(narrow_font->file_name); + if (eb_find_file_name3(book->path, subbook->directory_name, + subbook->gaiji_directory_name, narrow_font->file_name, + narrow_font->file_name) != EB_SUCCESS) { + error_code = EB_ERR_FAIL_OPEN_FONT; + goto failed; + } + + eb_compose_path_name3(book->path, subbook->directory_name, + subbook->gaiji_directory_name, narrow_font->file_name, + font_path_name); + eb_path_name_zio_code(font_path_name, ZIO_PLAIN, &zio_code); + } + } + + if (zio_code != ZIO_INVALID + && zio_open(&narrow_font->zio, font_path_name, zio_code) < 0) { + error_code = EB_ERR_FAIL_OPEN_FONT; + goto failed; + } + + succeeded: + LOG(("out: eb_open_narrow_font_file(file=%d) = %s", + zio_file(&narrow_font->zio), eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_open_narrow_font_file() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Read font header. + */ +EB_Error_Code +eb_load_narrow_font_header(EB_Book *book, EB_Font_Code font_code) +{ + EB_Error_Code error_code; + EB_Subbook *subbook; + EB_Font *narrow_font; + char buffer[16]; + int character_count; + Zio *zio; + + LOG(("in: eb_load_narrow_font_header(book=%d, font_code=%d)", + (int)book->code, (int)font_code)); + + subbook = book->subbook_current; + narrow_font = subbook->narrow_fonts + font_code; + zio = &narrow_font->zio; + + if (narrow_font->initialized) + goto succeeded; + + /* + * Read information from the text file. + */ + if (zio_lseek(zio, ((off_t) narrow_font->page - 1) * EB_SIZE_PAGE, + SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_FONT; + goto failed; + } + if (zio_read(zio, buffer, 16) != 16) { + error_code = EB_ERR_FAIL_READ_FONT; + goto failed; + } + + /* + * If the number of characters (`character_count') is 0, the font + * is unavailable. We return EB_ERR_NO_SUCH_FONT. + */ + character_count = eb_uint2(buffer + 12); + if (character_count == 0) { + zio_close(zio); + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + /* + * Set the information. + */ + narrow_font->start = eb_uint2(buffer + 10); + if (book->character_code == EB_CHARCODE_ISO8859_1) { + narrow_font->end = narrow_font->start + + ((character_count / 0xfe) << 8) + (character_count % 0xfe) - 1; + if (0xfe < (narrow_font->end & 0xff)) + narrow_font->end += 3; + } else { + narrow_font->end = narrow_font->start + + ((character_count / 0x5e) << 8) + (character_count % 0x5e) - 1; + if (0x7e < (narrow_font->end & 0xff)) + narrow_font->end += 0xa3; + } + + if (book->character_code == EB_CHARCODE_ISO8859_1) { + if ((narrow_font->start & 0xff) < 0x01 + || 0xfe < (narrow_font->start & 0xff) + || narrow_font->start < 0x0001 + || 0x1efe < narrow_font->end) { + error_code = EB_ERR_UNEXP_FONT; + goto failed; + } + } else { + if ((narrow_font->start & 0xff) < 0x21 + || 0x7e < (narrow_font->start & 0xff) + || narrow_font->start < 0xa121 + || 0xfe7e < narrow_font->end) { + error_code = EB_ERR_UNEXP_FONT; + goto failed; + } + } + + succeeded: + LOG(("out: eb_load_narrow_font_header()", eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_load_narrow_font_header()", eb_error_string(error_code))); + return error_code; +} + + +/* + * Read font glyph data. + */ +EB_Error_Code +eb_load_narrow_font_glyphs(EB_Book *book, EB_Font_Code font_code) +{ + EB_Error_Code error_code; + EB_Subbook *subbook; + EB_Font *narrow_font; + int character_count; + size_t glyph_size; + size_t total_glyph_size; + Zio *zio; + + LOG(("in: eb_load_narrow_font_glyphs(book=%d, font_code=%d)", + (int)book->code, (int)font_code)); + + subbook = book->subbook_current; + narrow_font = subbook->narrow_fonts + font_code; + zio = &narrow_font->zio; + + if (narrow_font->glyphs != NULL) + goto succeeded; + + /* + * Calculate size of glyph data (`total_glyph_size'). + * + * Set the number of local defined characters to `character_count'. + * Set the number of character glpyhs in a page to `page_glyph_count'. + * Set size of glyph data to `total_glyph_size'. + */ + if (book->character_code == EB_CHARCODE_ISO8859_1) { + character_count + = ((narrow_font->end >> 8) - (narrow_font->start >> 8)) * 0xfe + + ((narrow_font->end & 0xff) - (narrow_font->start & 0xff)) + 1; + } else { + character_count + = ((narrow_font->end >> 8) - (narrow_font->start >> 8)) * 0x5e + + ((narrow_font->end & 0xff) - (narrow_font->start & 0xff)) + 1; + } + + eb_narrow_font_size2(font_code, &glyph_size); + total_glyph_size + = (character_count / (1024 / glyph_size)) * 1024 + + (character_count % (1024 / glyph_size)) * glyph_size; + + /* + * Allocate memory for glyph data. + */ + narrow_font->glyphs = (char *) malloc(total_glyph_size); + if (narrow_font->glyphs == NULL) { + error_code = EB_ERR_MEMORY_EXHAUSTED; + goto failed; + } + + /* + * Read glyphs. + */ + if (zio_lseek(zio, (off_t) narrow_font->page * EB_SIZE_PAGE, SEEK_SET) + < 0) { + error_code = EB_ERR_FAIL_SEEK_FONT; + goto failed; + } + if (zio_read(zio, narrow_font->glyphs, total_glyph_size) + != total_glyph_size) { + error_code = EB_ERR_FAIL_READ_FONT; + goto failed; + } + + succeeded: + LOG(("out: eb_load_narrow_font_glyphs()", eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_load_narrow_font_glyphs()", eb_error_string(error_code))); + if (narrow_font->glyphs != NULL) { + free(narrow_font->glyphs); + narrow_font->glyphs = NULL; + } + return error_code; +} + + +/* + * Examine whether the current subbook in `book' has a narrow font. + */ +int +eb_have_narrow_font(EB_Book *book) +{ + int i; + + eb_lock(&book->lock); + LOG(("in: eb_have_narrow_font(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) + goto failed; + + /* + * If the narrow font has already set, the subbook has narrow fonts. + */ + if (book->subbook_current->narrow_current != NULL) + goto succeeded; + + /* + * Scan the font table. + */ + for (i = 0; i < EB_MAX_FONTS; i++) { + if (book->subbook_current->narrow_fonts[i].font_code + != EB_FONT_INVALID) + break; + } + + if (EB_MAX_FONTS <= i) + goto failed; + + succeeded: + LOG(("out: eb_have_narrow_font() = %d", 1)); + eb_unlock(&book->lock); + return 1; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_have_narrow_font() = %d", 0)); + eb_unlock(&book->lock); + return 0; +} + + +/* + * Get width of the font `font_code' in the current subbook of `book'. + */ +EB_Error_Code +eb_narrow_font_width(EB_Book *book, int *width) +{ + EB_Error_Code error_code; + EB_Font_Code font_code; + + eb_lock(&book->lock); + LOG(("in: eb_narrow_font_width(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * The narrow font must exist in the current subbook. + */ + if (book->subbook_current->narrow_current == NULL) { + error_code = EB_ERR_NO_CUR_FONT; + goto failed; + } + + /* + * Calculate width. + */ + font_code = book->subbook_current->narrow_current->font_code; + error_code = eb_narrow_font_width2(font_code, width); + if (error_code != EB_SUCCESS) + goto failed; + + LOG(("out: eb_narrow_font_width(width=%d) = %s", (int)*width, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *width = 0; + LOG(("out: eb_narrow_font_width() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Get width of the font `font_code'. + */ +EB_Error_Code +eb_narrow_font_width2(EB_Font_Code font_code, int *width) +{ + EB_Error_Code error_code; + + LOG(("in: eb_narrow_font_width2(font_code=%d)", (int)font_code)); + + switch (font_code) { + case EB_FONT_16: + *width = EB_WIDTH_NARROW_FONT_16; + break; + case EB_FONT_24: + *width = EB_WIDTH_NARROW_FONT_24; + break; + case EB_FONT_30: + *width = EB_WIDTH_NARROW_FONT_30; + break; + case EB_FONT_48: + *width = EB_WIDTH_NARROW_FONT_48; + break; + default: + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + LOG(("out: eb_narrow_font_width2(width=%d) = %s", *width, + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *width = 0; + LOG(("out: eb_narrow_font_width2() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Get the bitmap size of the font `font_code' in the current subbook + * of `book'. + */ +EB_Error_Code +eb_narrow_font_size(EB_Book *book, size_t *size) +{ + EB_Error_Code error_code; + EB_Font_Code font_code; + int width; + int height; + + eb_lock(&book->lock); + LOG(("in: eb_narrow_font_size(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * The narrow font must exist in the current subbook. + */ + if (book->subbook_current->narrow_current == NULL) { + error_code = EB_ERR_NO_CUR_FONT; + goto failed; + } + + /* + * Calculate size. + */ + font_code = book->subbook_current->narrow_current->font_code; + error_code = eb_narrow_font_width2(font_code, &width); + if (error_code != EB_SUCCESS) + goto failed; + error_code = eb_font_height2(font_code, &height); + if (error_code != EB_SUCCESS) + goto failed; + *size = (width / 8) * height; + + LOG(("out: eb_narrow_font_size(size=%ld) = %s", (long)*size, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *size = 0; + LOG(("out: eb_narrow_font_size() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Get the bitmap size of a character in `font_code' of the current + * subbook. + */ +EB_Error_Code +eb_narrow_font_size2(EB_Font_Code font_code, size_t *size) +{ + EB_Error_Code error_code; + + LOG(("in: eb_narrow_font_size2(font_code=%d)", (int)font_code)); + + switch (font_code) { + case EB_FONT_16: + *size = EB_SIZE_NARROW_FONT_16; + break; + case EB_FONT_24: + *size = EB_SIZE_NARROW_FONT_24; + break; + case EB_FONT_30: + *size = EB_SIZE_NARROW_FONT_30; + break; + case EB_FONT_48: + *size = EB_SIZE_NARROW_FONT_48; + break; + default: + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + LOG(("out: eb_narrow_font_size2(size=%ld) = %s", (long)*size, + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *size = 0; + LOG(("out: eb_narrow_font_size2() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Get the character number of the start of the narrow font of the current + * subbook in `book'. + */ +EB_Error_Code +eb_narrow_font_start(EB_Book *book, int *start) +{ + EB_Error_Code error_code; + + eb_lock(&book->lock); + LOG(("in: eb_narrow_font_start(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * The narrow font must exist in the current subbook. + */ + if (book->subbook_current->narrow_current == NULL) { + error_code = EB_ERR_NO_CUR_FONT; + goto failed; + } + + *start = book->subbook_current->narrow_current->start; + + LOG(("out: eb_narrow_font_start(start=%d) = %s", *start, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_narrow_font_start() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Get the character number of the end of the narrow font of the current + * subbook in `book'. + */ +EB_Error_Code +eb_narrow_font_end(EB_Book *book, int *end) +{ + EB_Error_Code error_code; + + eb_lock(&book->lock); + LOG(("in: eb_narrow_font_end(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * The narrow font must exist in the current subbook. + */ + if (book->subbook_current->narrow_current == NULL) { + error_code = EB_ERR_NO_CUR_FONT; + goto failed; + } + + *end = book->subbook_current->narrow_current->end; + + LOG(("out: eb_narrow_font_end(end=%d) = %s", *end, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_narrow_font_end() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Get bitmap data of the character with character number `character_number' + * in the current narrow font of the current subbook in `book'. + */ +EB_Error_Code +eb_narrow_font_character_bitmap(EB_Book *book, int character_number, + char *bitmap) +{ + EB_Error_Code error_code; + + eb_lock(&book->lock); + LOG(("in: eb_narrow_font_character_bitmap(book=%d, character_number=%d)", + (int)book->code, character_number)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * The narrow font must exist in the current subbook. + */ + if (book->subbook_current->narrow_current == NULL) { + error_code = EB_ERR_NO_CUR_FONT; + goto failed; + } + + if (book->character_code == EB_CHARCODE_ISO8859_1) { + error_code = eb_narrow_character_bitmap_latin(book, character_number, + bitmap); + } else { + error_code = eb_narrow_character_bitmap_jis(book, character_number, + bitmap); + } + if (error_code != EB_SUCCESS) + goto failed; + + LOG(("out: eb_narrow_font_character_bitmap() = %s", + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *bitmap = '\0'; + LOG(("out: eb_narrow_font_character_bitmap() = %s", + eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Get bitmap data of the character with character number `character_number' + * in the current narrow font of the current subbook in `book'. + */ +static EB_Error_Code +eb_narrow_character_bitmap_jis(EB_Book *book, int character_number, + char *bitmap) +{ + EB_Error_Code error_code; + EB_Font *narrow_current; + int start; + int end; + int character_index; + off_t offset; + size_t size; + Zio *zio; + + LOG(("in: eb_narrow_font_character_bitmap_jis(book=%d, \ +character_number=%d)", + (int)book->code, character_number)); + + start = book->subbook_current->narrow_current->start; + end = book->subbook_current->narrow_current->end; + narrow_current = book->subbook_current->narrow_current; + + /* + * Check for `character_number'. Is it in a range of bitmaps? + * This test works correctly even when the font doesn't exist in + * the current subbook because `start' and `end' have set to -1 + * in the case. + */ + if (character_number < start + || end < character_number + || (character_number & 0xff) < 0x21 + || 0x7e < (character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + + /* + * Calculate the size and the location of bitmap data. + */ + error_code = eb_narrow_font_size(book, &size); + if (error_code != EB_SUCCESS) + goto failed; + + character_index = ((character_number >> 8) - (start >> 8)) * 0x5e + + ((character_number & 0xff) - (start & 0xff)); + offset + = (character_index / (1024 / size)) * 1024 + + (character_index % (1024 / size)) * size; + + /* + * Read bitmap data. + */ + if (narrow_current->glyphs == NULL) { + zio = &narrow_current->zio; + + if (zio_lseek(zio, + (off_t) narrow_current->page * EB_SIZE_PAGE + offset, + SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_FONT; + goto failed; + } + if (zio_read(zio, bitmap, size) != size) { + error_code = EB_ERR_FAIL_READ_FONT; + goto failed; + } + } else { + memcpy(bitmap, narrow_current->glyphs + offset, size); + } + + LOG(("out: eb_narrow_font_character_bitmap_jis() = %s", + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *bitmap = '\0'; + LOG(("out: eb_narrow_font_character_bitmap_jis() = %s", + eb_error_string(error_code))); + return error_code; +} + + +/* + * Get bitmap data of the character with character number `character_number' + * in the current narrow font of the current subbook in `book'. + */ +static EB_Error_Code +eb_narrow_character_bitmap_latin(EB_Book *book, int character_number, + char *bitmap) +{ + EB_Error_Code error_code; + EB_Font *narrow_current; + int start; + int end; + int character_index; + off_t offset; + size_t size; + Zio *zio; + + LOG(("in: eb_narrow_font_character_bitmap_latin(book=%d, \ +character_number=%d)", + (int)book->code, character_number)); + + start = book->subbook_current->narrow_current->start; + end = book->subbook_current->narrow_current->end; + narrow_current = book->subbook_current->narrow_current; + + /* + * Check for `ch'. Is it in a range of bitmaps? + * This test works correctly even when the font doesn't exist in + * the current subbook because `start' and `end' have set to -1 + * in the case. + */ + if (character_number < start + || end < character_number + || (character_number & 0xff) < 0x01 + || 0xfe < (character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + + /* + * Calculate the size and the location of bitmap data. + */ + error_code = eb_narrow_font_size(book, &size); + if (error_code != EB_SUCCESS) + goto failed; + + character_index = ((character_number >> 8) - (start >> 8)) * 0xfe + + ((character_number & 0xff) - (start & 0xff)); + offset + = (character_index / (1024 / size)) * 1024 + + (character_index % (1024 / size)) * size; + + /* + * Read bitmap data. + */ + if (narrow_current->glyphs == NULL) { + zio = &narrow_current->zio; + + if (zio_lseek(zio, + (off_t) narrow_current->page * EB_SIZE_PAGE + offset, + SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_FONT; + goto failed; + } + if (zio_read(zio, bitmap, size) != size) { + error_code = EB_ERR_FAIL_READ_FONT; + goto failed; + } + } else { + memcpy(bitmap, narrow_current->glyphs + offset, size); + } + + LOG(("out: eb_narrow_font_character_bitmap_latin() = %s", + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *bitmap = '\0'; + LOG(("out: eb_narrow_font_character_bitmap_latin() = %s", + eb_error_string(error_code))); + return error_code; +} + + +/* + * Return next `n'th character number from `character_number'. + */ +EB_Error_Code +eb_forward_narrow_font_character(EB_Book *book, int n, int *character_number) +{ + EB_Error_Code error_code; + int start; + int end; + int i; + + if (n < 0) + return eb_backward_narrow_font_character(book, -n, character_number); + + eb_lock(&book->lock); + LOG(("in: eb_forward_narrow_font_character(book=%d, n=%d, \ +character_number=%d)", + (int)book->code, n, *character_number)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * The narrow font must exist in the current subbook. + */ + if (book->subbook_current->narrow_current == NULL) { + error_code = EB_ERR_NO_CUR_FONT; + goto failed; + } + + start = book->subbook_current->narrow_current->start; + end = book->subbook_current->narrow_current->end; + + if (book->character_code == EB_CHARCODE_ISO8859_1) { + /* + * Check for `*character_number'. (ISO 8859 1) + */ + if (*character_number < start + || end < *character_number + || (*character_number & 0xff) < 0x01 + || 0xfe < (*character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + + /* + * Get character number. (ISO 8859 1) + */ + for (i = 0; i < n; i++) { + if (0xfe <= (*character_number & 0xff)) + *character_number += 3; + else + *character_number += 1; + if (end < *character_number) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + } + } else { + /* + * Check for `*character_number'. (JIS X 0208) + */ + if (*character_number < start + || end < *character_number + || (*character_number & 0xff) < 0x21 + || 0x7e < (*character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + + /* + * Get character number. (JIS X 0208) + */ + for (i = 0; i < n; i++) { + if (0x7e <= (*character_number & 0xff)) + *character_number += 0xa3; + else + *character_number += 1; + if (end < *character_number) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + } + } + + LOG(("out: eb_forward_narrow_font_character(character_number=%d) = %s", + *character_number, eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *character_number = -1; + LOG(("out: eb_forward_narrow_font_character() = %s", + eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Return previous `n'th character number from `*character_number'. + */ +EB_Error_Code +eb_backward_narrow_font_character(EB_Book *book, int n, int *character_number) +{ + EB_Error_Code error_code; + int start; + int end; + int i; + + if (n < 0) + return eb_forward_narrow_font_character(book, -n, character_number); + + eb_lock(&book->lock); + LOG(("in: eb_backward_narrow_font_character(book=%d, n=%d, \ +character_number=%d)", + (int)book->code, n, *character_number)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * The narrow font must exist in the current subbook. + */ + if (book->subbook_current->narrow_current == NULL) { + error_code = EB_ERR_NO_CUR_FONT; + goto failed; + } + + start = book->subbook_current->narrow_current->start; + end = book->subbook_current->narrow_current->end; + + if (book->character_code == EB_CHARCODE_ISO8859_1) { + /* + * Check for `*character_number'. (ISO 8859 1) + */ + if (*character_number < start + || end < *character_number + || (*character_number & 0xff) < 0x01 + || 0xfe < (*character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + + /* + * Get character number. (ISO 8859 1) + */ + for (i = 0; i < n; i++) { + if ((*character_number & 0xff) <= 0x01) + *character_number -= 3; + else + *character_number -= 1; + if (*character_number < start) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + } + } else { + /* + * Check for `*character_number'. (JIS X 0208) + */ + if (*character_number < start + || end < *character_number + || (*character_number & 0xff) < 0x21 + || 0x7e < (*character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + + /* + * Get character number. (JIS X 0208) + */ + for (i = 0; i < n; i++) { + if ((*character_number & 0xff) <= 0x21) + *character_number -= 0xa3; + else + *character_number -= 1; + if (*character_number < start) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + } + } + + LOG(("out: eb_backward_narrow_font_character(character_number=%d) = %s", + *character_number, eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *character_number = -1; + LOG(("out: eb_backward_narrow_font_character() = %s", + eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} diff --git a/readtext.c b/readtext.c new file mode 100644 index 0000000..4f59e50 --- /dev/null +++ b/readtext.c @@ -0,0 +1,2141 @@ +/* + * 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 "text.h" +#include "build-post.h" + +/* + * The maximum number of arguments for an escape sequence. + */ +#define EB_MAX_ARGV 7 + +/* + * Read next when the length of cached data is shorter than this value. + */ +#define SIZE_FEW_REST 48 + +/* + * Special skip-code that represents `no skip-code is set'. + */ +#define SKIP_CODE_NONE -1 + +/* + * Cache data buffer. + */ +static char cache_buffer[EB_SIZE_PAGE]; + +/* + * Book code of which `cache_buffer' records data. + */ +static EB_Book_Code cache_book_code = EB_BOOK_NONE; + +/* + * Location of cache data loaded in `cache_buffer'. + */ +static off_t cache_location; + +/* + * Length of cache data loaded in `cache_buffer'. + */ +static size_t cache_length; + +/* + * Null hook. + */ +static const EB_Hook null_hook = {EB_HOOK_NULL, NULL}; + +/* + * Unexported functions. + */ +static EB_Error_Code eb_read_text_internal(EB_Book *book, + EB_Appendix *appendix, EB_Hookset *hookset, void *container, + size_t text_max_length, char *text, ssize_t *text_length, + int forward_only); +static int eb_is_stop_code(EB_Book *book, EB_Appendix *appendix, + unsigned int code0, unsigned int code1); + + +/* + * Initialize text context of `book'. + */ +void +eb_initialize_text_context(EB_Book *book) +{ + LOG(("in: eb_initialize_text_context(book=%d)", (int)book->code)); + + book->text_context.code = EB_TEXT_INVALID; + book->text_context.location = -1; + book->text_context.out = NULL; + book->text_context.out_rest_length = 0; + book->text_context.unprocessed = NULL; + book->text_context.unprocessed_size = 0; + book->text_context.out_step = 0; + book->text_context.narrow_flag = 0; + book->text_context.printable_count = 0; + book->text_context.file_end_flag = 0; + book->text_context.text_status = EB_TEXT_STATUS_CONTINUED; + book->text_context.skip_code = SKIP_CODE_NONE; + book->text_context.auto_stop_code = -1; + book->text_context.candidate[0] = '\0'; + book->text_context.is_candidate = 0; + book->text_context.ebxac_gaiji_flag = 0; + + LOG(("out: eb_initialize_text_context()")); +} + + +/* + * Finalize text context of `book'. + */ +void +eb_finalize_text_context(EB_Book *book) +{ + LOG(("in: eb_finalize_text_context(book=%d)", (int)book->code)); + + if (book->text_context.unprocessed != NULL) + free(book->text_context.unprocessed); + + LOG(("out: eb_finalize_text_context()")); +} + + +/* + * Reset text context of `book'. + * Note that `contexxt_code' and `context_location' are unchanged. + */ +void +eb_reset_text_context(EB_Book *book) +{ + LOG(("in: eb_reset_text_context(book=%d)", (int)book->code)); + + eb_finalize_text_context(book); + + book->text_context.out = NULL; + book->text_context.out_rest_length = 0; + book->text_context.unprocessed = NULL; + book->text_context.unprocessed_size = 0; + book->text_context.out_step = 0; + book->text_context.narrow_flag = 0; + book->text_context.printable_count = 0; + book->text_context.file_end_flag = 0; + book->text_context.text_status = EB_TEXT_STATUS_CONTINUED; + book->text_context.skip_code = SKIP_CODE_NONE; + book->text_context.auto_stop_code = -1; + book->text_context.candidate[0] = '\0'; + book->text_context.is_candidate = 0; + book->text_context.ebxac_gaiji_flag = 0; + + LOG(("out: eb_reset_text_context()")); +} + + +/* + * Invalidate text context of `book'. + */ +void +eb_invalidate_text_context(EB_Book *book) +{ + LOG(("in: eb_invalidate_text_context(book=%d)", (int)book->code)); + + eb_finalize_text_context(book); + eb_initialize_text_context(book); + book->text_context.code = EB_TEXT_INVALID; + + LOG(("out: eb_invalidate_text_context()")); +} + + +/* + * Reposition the offset of the subbook file. + */ +EB_Error_Code +eb_seek_text(EB_Book *book, const EB_Position *position) +{ + EB_Error_Code error_code; + + pthread_mutex_lock(&cache_mutex); + eb_lock(&book->lock); + LOG(("in: eb_seek_text(book=%d, position={%d,%d})", (int)book->code, + position->page, position->offset)); + + /* + * Current subbook must have been set and START file must exist. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + if (zio_file(&book->subbook_current->text_zio) < 0) { + error_code = EB_ERR_NO_TEXT; + goto failed; + } + + if (position->page <= 0 || position->offset < 0) { + error_code = EB_ERR_FAIL_SEEK_TEXT; + goto failed; + } + + /* + * Set text-context variables. + */ + eb_reset_text_context(book); + book->text_context.code = EB_TEXT_SEEKED; + book->text_context.location = ((off_t) position->page - 1) * EB_SIZE_PAGE + + position->offset; + + /* + * Unlock cache data and the book. + */ + LOG(("out: eb_seek_text() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + pthread_mutex_unlock(&cache_mutex); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_invalidate_text_context(book); + LOG(("out: eb_seek_text() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + pthread_mutex_unlock(&cache_mutex); + return error_code; +} + + +/* + * Get the current text position of the subbook file. + */ +EB_Error_Code +eb_tell_text(EB_Book *book, EB_Position *position) +{ + EB_Error_Code error_code; + + eb_lock(&book->lock); + LOG(("in: eb_tell_text(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set and START file must exist. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + if (zio_file(&book->subbook_current->text_zio) < 0) { + error_code = EB_ERR_NO_TEXT; + goto failed; + } + + position->page = book->text_context.location / EB_SIZE_PAGE + 1; + position->offset = book->text_context.location % EB_SIZE_PAGE; + + LOG(("out: eb_seek_text(position={%d,%d}) = %s", + position->page, position->offset, eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_invalidate_text_context(book); + LOG(("out: eb_seek_text() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Get text in the current subbook in `book'. + */ +EB_Error_Code +eb_read_text(EB_Book *book, EB_Appendix *appendix, EB_Hookset *hookset, + void *container, size_t text_max_length, char *text, ssize_t *text_length) +{ + EB_Error_Code error_code; + const EB_Hook *hook; + EB_Position position; + + eb_lock(&book->lock); + if (appendix != NULL) + eb_lock(&appendix->lock); + if (hookset != NULL) + eb_lock(&hookset->lock); + LOG(("in: eb_read_text(book=%d, appendix=%d, text_max_length=%ld)", + (int)book->code, (appendix != NULL) ? (int)appendix->code : 0, + (long)text_max_length)); + + /* + * Current subbook must have been set and START file must exist. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + if (zio_file(&book->subbook_current->text_zio) < 0) { + error_code = EB_ERR_NO_TEXT; + goto failed; + } + + /* + * Use `eb_default_hookset' when `hookset' is `NULL'. + */ + if (hookset == NULL) + hookset = &eb_default_hookset; + + /* + * Set text mode to `text'. + */ + if (book->text_context.code == EB_TEXT_INVALID) { + error_code = EB_ERR_NO_PREV_SEEK; + goto failed; + } else if (book->text_context.code == EB_TEXT_SEEKED) { + eb_tell_text(book, &position); + eb_reset_text_context(book); + + if (book->subbook_current->menu.start_page <= position.page + && position.page <= book->subbook_current->menu.end_page) + book->text_context.code = EB_TEXT_OPTIONAL_TEXT; + else if (book->subbook_current->image_menu.start_page <= position.page + && position.page <= book->subbook_current->image_menu.end_page) + book->text_context.code = EB_TEXT_OPTIONAL_TEXT; + else if (book->subbook_current->copyright.start_page <= position.page + && position.page <= book->subbook_current->copyright.end_page) + book->text_context.code = EB_TEXT_OPTIONAL_TEXT; + else + book->text_context.code = EB_TEXT_MAIN_TEXT; + + hook = hookset->hooks + EB_HOOK_INITIALIZE; + if (hook->function != NULL) { + error_code = hook->function(book, appendix, container, + EB_HOOK_INITIALIZE, 0, NULL); + if (error_code != EB_SUCCESS) + goto failed; + } + } else if (book->text_context.code != EB_TEXT_MAIN_TEXT + && book->text_context.code != EB_TEXT_OPTIONAL_TEXT) { + error_code = EB_ERR_DIFF_CONTENT; + goto failed; + } + + error_code = eb_read_text_internal(book, appendix, hookset, container, + text_max_length, text, text_length, 0); + if (error_code != EB_SUCCESS) + goto failed; + + LOG(("out: eb_read_text(text_length=%ld) = %s", (long)*text_length, + eb_error_string(EB_SUCCESS))); + if (hookset != &eb_default_hookset) + eb_unlock(&hookset->lock); + if (appendix != NULL) + eb_unlock(&appendix->lock); + eb_unlock(&book->lock); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_invalidate_text_context(book); + LOG(("out: eb_read_text() = %s", eb_error_string(error_code))); + if (hookset != &eb_default_hookset) + eb_unlock(&hookset->lock); + if (appendix != NULL) + eb_unlock(&appendix->lock); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Get text in the current subbook in `book'. + */ +EB_Error_Code +eb_read_heading(EB_Book *book, EB_Appendix *appendix, EB_Hookset *hookset, + void *container, size_t text_max_length, char *text, ssize_t *text_length) +{ + EB_Error_Code error_code; + const EB_Hook *hook; + + eb_lock(&book->lock); + if (appendix != NULL) + eb_lock(&appendix->lock); + if (hookset != NULL) + eb_lock(&hookset->lock); + LOG(("in: eb_read_heading(book=%d, appendix=%d, text_max_length=%ld)", + (int)book->code, (appendix != NULL) ? (int)appendix->code : 0, + (long)text_max_length)); + + /* + * Current subbook must have been set and START file must exist. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + if (zio_file(&book->subbook_current->text_zio) < 0) { + error_code = EB_ERR_NO_TEXT; + goto failed; + } + + /* + * Use `eb_default_hookset' when `hookset' is `NULL'. + */ + if (hookset == NULL) + hookset = &eb_default_hookset; + + /* + * Set text mode to `heading'. + */ + if (book->text_context.code == EB_TEXT_INVALID) { + error_code = EB_ERR_NO_PREV_SEEK; + goto failed; + } else if (book->text_context.code == EB_TEXT_SEEKED) { + eb_reset_text_context(book); + book->text_context.code = EB_TEXT_HEADING; + + hook = hookset->hooks + EB_HOOK_INITIALIZE; + if (hook->function != NULL) { + error_code = hook->function(book, appendix, container, + EB_HOOK_INITIALIZE, 0, NULL); + if (error_code != EB_SUCCESS) + goto failed; + } + } else if (book->text_context.code != EB_TEXT_HEADING) { + error_code = EB_ERR_DIFF_CONTENT; + goto failed; + } + + error_code = eb_read_text_internal(book, appendix, hookset, container, + text_max_length, text, text_length, 0); + if (error_code != EB_SUCCESS) + goto failed; + + LOG(("out: eb_read_heading(text_length=%ld) = %s", (long)*text_length, + eb_error_string(EB_SUCCESS))); + if (hookset != &eb_default_hookset) + eb_unlock(&hookset->lock); + if (appendix != NULL) + eb_unlock(&appendix->lock); + eb_unlock(&book->lock); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_invalidate_text_context(book); + LOG(("out: eb_read_heading() = %s", eb_error_string(error_code))); + if (hookset != &eb_default_hookset) + eb_unlock(&hookset->lock); + if (appendix != NULL) + eb_unlock(&appendix->lock); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Read data from the subbook file directly. + */ +EB_Error_Code +eb_read_rawtext(EB_Book *book, size_t text_max_length, char *text, + ssize_t *text_length) +{ + EB_Error_Code error_code; + + eb_lock(&book->lock); + LOG(("in: eb_read_rawtext(book=%d, text_max_length=%ld)", + (int)book->code, (long)text_max_length)); + + /* + * Current subbook must have been set and START file must exist. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + if (zio_file(&book->subbook_current->text_zio) < 0) { + error_code = EB_ERR_NO_TEXT; + goto failed; + } + + /* + * Set text mode to `rawtext'. + */ + if (book->text_context.code == EB_TEXT_INVALID) { + error_code = EB_ERR_NO_PREV_SEEK; + goto failed; + } else if (book->text_context.code == EB_TEXT_SEEKED) { + eb_reset_text_context(book); + book->text_context.code = EB_TEXT_RAWTEXT; + } else if (book->text_context.code != EB_TEXT_RAWTEXT) { + error_code = EB_ERR_DIFF_CONTENT; + goto failed; + } + + /* + * Seek START file and read data. + */ + if (zio_lseek(&book->subbook_current->text_zio, + book->text_context.location, SEEK_SET) == -1) { + error_code = EB_ERR_FAIL_SEEK_TEXT; + goto failed; + } + *text_length = zio_read(&book->subbook_current->text_zio, text, + text_max_length); + book->text_context.location += *text_length; + if (*text_length < 0) { + error_code = EB_ERR_FAIL_READ_TEXT; + goto failed; + } + + LOG(("out: eb_read_rawtext(text_length=%ld) = %s", (long)*text_length, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *text_length = -1; + eb_invalidate_text_context(book); + LOG(("out: eb_read_rawtext() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Get text or heading. + */ +static EB_Error_Code +eb_read_text_internal(EB_Book *book, EB_Appendix *appendix, + EB_Hookset *hookset, void *container, size_t text_max_length, char *text, + ssize_t *text_length, int forward_only) +{ + EB_Error_Code error_code; + EB_Text_Context *context; + unsigned char c1, c2; + char *cache_p; + const EB_Hook *hook; + unsigned char *candidate_p; + size_t candidate_length; + size_t cache_rest_length; + size_t in_step; + unsigned int argv[EB_MAX_ARGV]; + int argc; + + pthread_mutex_lock(&cache_mutex); + LOG(("in: eb_read_text_internal(book=%d, appendix=%d, \ +text_max_length=%ld, forward=%d)", + (int)book->code, (appendix != NULL) ? (int)appendix->code : 0, + (long)text_max_length, forward_only)); + + /* + * Initialize variables. + */ + context = &book->text_context; + context->out = text; + context->out_rest_length = text_max_length; + if (context->is_candidate) { + candidate_length = strlen(context->candidate); + candidate_p = (unsigned char *)context->candidate + candidate_length; + } else { + candidate_length = 0; + candidate_p = NULL; + } + + /* + * If unprocessed string are rest in `context->unprocessed', + * copy them to `context->out'. + */ + if (context->unprocessed != NULL) { + if (!forward_only) { + if (context->out_rest_length < context->unprocessed_size) + goto succeeded; + memcpy(context->out, context->unprocessed, + context->unprocessed_size); + context->out += context->unprocessed_size; + context->out_rest_length -= context->unprocessed_size; + } + free(context->unprocessed); + context->unprocessed = NULL; + context->unprocessed_size = 0; + } + + /* + * Return immediately if text-end-flag has been set. + */ + if (context->text_status != EB_TEXT_STATUS_CONTINUED) + goto succeeded; + + /* + * Check for cache data. + * If cache data is not what we need, discard it. + */ + if (book->code == cache_book_code + && cache_location <= context->location + && context->location < cache_location + cache_length) { + cache_p = cache_buffer + (context->location - cache_location); + cache_rest_length = cache_length + - (context->location - cache_location); + } else { + cache_book_code = EB_BOOK_NONE; + cache_p = cache_buffer; + cache_length = 0; + cache_rest_length = 0; + } + + for (;;) { + in_step = 0; + context->out_step = 0; + argc = 1; + + /* + * If it reaches to the near of the end of the cache buffer, + * then moves remaind cache text to the beginning of the cache + * buffer, and reads a next chunk from a file. + */ + if (cache_rest_length < SIZE_FEW_REST && !context->file_end_flag) { + ssize_t read_result; + + if (0 < cache_rest_length) + memmove(cache_buffer, cache_p, cache_rest_length); + if (zio_lseek(&book->subbook_current->text_zio, + context->location + cache_rest_length, SEEK_SET) == -1) { + error_code = EB_ERR_FAIL_SEEK_TEXT; + goto failed; + } + + read_result = zio_read(&book->subbook_current->text_zio, + cache_buffer + cache_rest_length, + EB_SIZE_PAGE - cache_rest_length); + if (read_result < 0) { + error_code = EB_ERR_FAIL_READ_TEXT; + goto failed; + } else if (read_result != EB_SIZE_PAGE - cache_rest_length) + context->file_end_flag = 1; + + cache_book_code = book->code; + cache_location = context->location; + cache_length = cache_rest_length + read_result; + cache_p = cache_buffer; + cache_rest_length = cache_length; + } + + /* + * Get 1 byte from the buffer. + */ + if (cache_rest_length < 1) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + c1 = eb_uint1(cache_p); + + if (c1 == 0x1f) { + hook = &null_hook; + + /* + * This is escape sequences. + */ + if (cache_rest_length < 2) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + argv[0] = eb_uint2(cache_p); + c2 = eb_uint1(cache_p + 1); + + switch (c2) { + case 0x02: + /* beginning of text */ + in_step = 2; + break; + + case 0x03: + /* end of text (don't set `in_step') */ + context->text_status = EB_TEXT_STATUS_HARD_STOP; + if (forward_only) { + error_code = EB_ERR_END_OF_CONTENT; + goto failed; + } + goto succeeded; + + case 0x04: + /* beginning of NARROW */ + in_step = 2; + hook = hookset->hooks + EB_HOOK_BEGIN_NARROW; + context->narrow_flag = 1; + break; + + case 0x05: + /* end of NARROW */ + in_step = 2; + hook = hookset->hooks + EB_HOOK_END_NARROW; + context->narrow_flag = 0; + break; + + case 0x06: + /* beginning of subscript */ + in_step = 2; + hook = hookset->hooks + EB_HOOK_BEGIN_SUBSCRIPT; + break; + + case 0x07: + /* end of subscript */ + in_step = 2; + hook = hookset->hooks + EB_HOOK_END_SUBSCRIPT; + break; + + case 0x09: + /* set indent */ + in_step = 4; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + argc = 2; + argv[1] = eb_uint2(cache_p + 2); + + if (0 < context->printable_count + && context->code == EB_TEXT_MAIN_TEXT) { + if (eb_is_stop_code(book, appendix, argv[0], argv[1])) { + context->text_status = EB_TEXT_STATUS_SOFT_STOP; + goto succeeded; + } + } + + hook = hookset->hooks + EB_HOOK_SET_INDENT; + break; + + case 0x0a: + /* newline */ + in_step = 2; + if (context->code == EB_TEXT_HEADING) { + context->text_status = EB_TEXT_STATUS_SOFT_STOP; + context->location += in_step; + goto succeeded; + } + hook = hookset->hooks + EB_HOOK_NEWLINE; + break; + + case 0x0b: + /* beginning of unicode */ + in_step = 2; + hook = hookset->hooks + EB_HOOK_BEGIN_UNICODE; + break; + + case 0x0c: + /* end of unicode */ + in_step = 2; + hook = hookset->hooks + EB_HOOK_END_UNICODE; + break; + + case 0x0e: + /* beginning of superscript */ + in_step = 2; + hook = hookset->hooks + EB_HOOK_BEGIN_SUPERSCRIPT; + break; + + case 0x0f: + /* end of superscript */ + in_step = 2; + hook = hookset->hooks + EB_HOOK_END_SUPERSCRIPT; + break; + + case 0x10: + /* beginning of newline prohibition */ + in_step = 2; + hook = hookset->hooks + EB_HOOK_BEGIN_NO_NEWLINE; + break; + + case 0x11: + /* end of newline prohibition */ + in_step = 2; + hook = hookset->hooks + EB_HOOK_END_NO_NEWLINE; + break; + + case 0x12: + /* beginning of emphasis */ + in_step = 2; + hook = hookset->hooks + EB_HOOK_BEGIN_EMPHASIS; + break; + + case 0x13: + /* end of emphasis */ + in_step = 2; + hook = hookset->hooks + EB_HOOK_END_EMPHASIS; + break; + + case 0x14: + in_step = 4; + context->skip_code = 0x15; + break; + + case 0x1a: case 0x1b: case 0x1e: case 0x1f: + /* emphasis; described in JIS X 4081-1996 */ + in_step = 4; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + /* Some old EB books don't take an argument. */ + if (book->disc_code != EB_DISC_EPWING + && eb_uint1(cache_p + 2) >= 0x1f) + in_step = 2; + break; + + case 0x1c: + if (book->character_code == EB_CHARCODE_JISX0208_GB2312) { + /* beginning of EBXA-C gaiji */ + in_step = 2; + hook = hookset->hooks + EB_HOOK_BEGIN_EBXAC_GAIJI; + context->ebxac_gaiji_flag = 1; + } else { + in_step = 4; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + /* Some old EB books don't take an argument. */ + if (book->disc_code != EB_DISC_EPWING + && eb_uint1(cache_p + 2) >= 0x1f) + in_step = 2; + } + break; + + case 0x1d: + if (book->character_code == EB_CHARCODE_JISX0208_GB2312) { + /* end of EBXA-C gaiji */ + in_step = 2; + hook = hookset->hooks + EB_HOOK_END_EBXAC_GAIJI; + context->ebxac_gaiji_flag = 0; + } else { + in_step = 4; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + /* Some old EB books don't take an argument. */ + if (book->disc_code != EB_DISC_EPWING + && eb_uint1(cache_p + 2) >= 0x1f) + in_step = 2; + } + break; + + case 0x32: + /* beginning of reference to monochrome graphic */ + in_step = 2; + argc = 4; + argv[1] = 0; + argv[2] = 0; + argv[3] = 0; + hook = hookset->hooks + EB_HOOK_BEGIN_MONO_GRAPHIC; + break; + + case 0x39: + /* beginning of MPEG movie */ + in_step = 46; + argc = 6; + argv[1] = eb_uint4(cache_p + 2); + argv[2] = eb_uint4(cache_p + 22); + argv[3] = eb_uint4(cache_p + 26); + argv[4] = eb_uint4(cache_p + 30); + argv[5] = eb_uint4(cache_p + 34); + hook = hookset->hooks + EB_HOOK_BEGIN_MPEG; + break; + + case 0x3c: + /* beginning of inline color graphic */ + in_step = 20; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + argc = 4; + argv[1] = eb_uint2(cache_p + 2); + argv[2] = eb_bcd4(cache_p + 14); + argv[3] = eb_bcd2(cache_p + 18); + if (argv[1] >> 8 == 0x00) + hook = hookset->hooks + EB_HOOK_BEGIN_IN_COLOR_BMP; + else + hook = hookset->hooks + EB_HOOK_BEGIN_IN_COLOR_JPEG; + break; + + case 0x35: case 0x36: case 0x37: case 0x38: case 0x3a: + case 0x3b: case 0x3d: case 0x3e: case 0x3f: + in_step = 2; + context->skip_code = eb_uint1(cache_p + 1) + 0x20; + break; + + case 0x41: + /* beginning of keyword */ + in_step = 4; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + argc = 2; + argv[1] = eb_uint2(cache_p + 2); + + if (0 < context->printable_count + && context->code == EB_TEXT_MAIN_TEXT) { + if (eb_is_stop_code(book, appendix, argv[0], argv[1])) { + context->text_status = EB_TEXT_STATUS_SOFT_STOP; + goto succeeded; + } + } + if (context->auto_stop_code < 0) + context->auto_stop_code = eb_uint2(cache_p + 2); + + hook = hookset->hooks + EB_HOOK_BEGIN_KEYWORD; + break; + + case 0x42: + /* beginning of reference */ + in_step = 4; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + if (eb_uint1(cache_p + 2) != 0x00) + in_step -= 2; + hook = hookset->hooks + EB_HOOK_BEGIN_REFERENCE; + break; + + case 0x43: + /* beginning of an entry of a candidate */ + in_step = 2; + if (context->skip_code == SKIP_CODE_NONE) { + context->candidate[0] = '\0'; + context->is_candidate = 1; + candidate_length = 0; + candidate_p = (unsigned char *)context->candidate; + } + hook = hookset->hooks + EB_HOOK_BEGIN_CANDIDATE; + break; + + case 0x44: + /* beginning of monochrome graphic */ + in_step = 12; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + argc = 4; + argv[1] = eb_uint2(cache_p + 2); + argv[2] = eb_bcd4(cache_p + 4); + argv[3] = eb_bcd4(cache_p + 8); + if (0 < argv[2] && 0 < argv[3]) + hook = hookset->hooks + EB_HOOK_BEGIN_MONO_GRAPHIC; + break; + + case 0x45: + /* beginning of graphic block */ + in_step = 4; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + if (eb_uint1(cache_p + 2) != 0x1f) { + argc = 2; + argv[1] = eb_bcd4(cache_p + 2); + } else { + in_step = 2; + } + break; + + case 0x4a: + /* beginning of WAVE sound */ + in_step = 18; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + argc = 6; + argv[1] = eb_uint4(cache_p + 2); + argv[2] = eb_bcd4(cache_p + 6); + argv[3] = eb_bcd2(cache_p + 10); + argv[4] = eb_bcd4(cache_p + 12); + argv[5] = eb_bcd2(cache_p + 16); + hook = hookset->hooks + EB_HOOK_BEGIN_WAVE; + break; + + case 0x4b: + /* beginning of paged reference */ + in_step = 8; + if (cache_rest_length < in_step + 2) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + argc = 3; + argv[1] = eb_bcd4(cache_p + 2); + argv[2] = eb_bcd2(cache_p + 6); + if (cache_p[8]==0x1f && cache_p[9]==0x6b) { + context->text_status = EB_TEXT_STATUS_SOFT_STOP; + hook = hookset->hooks + EB_HOOK_GRAPHIC_REFERENCE; + in_step = 10; + } else { + hook = hookset->hooks + EB_HOOK_BEGIN_GRAPHIC_REFERENCE; + } + break; + + case 0x4c: + /* beginning of image page */ + in_step = 4; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + hook = hookset->hooks + EB_HOOK_BEGIN_IMAGE_PAGE; + break; + + case 0x4d: + /* beginning of color graphic (BMP or JPEG) */ + in_step = 20; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + argc = 4; + argv[1] = eb_uint2(cache_p + 2); + argv[2] = eb_bcd4(cache_p + 14); + argv[3] = eb_bcd2(cache_p + 18); + if (argv[1] >> 8 == 0x00) + hook = hookset->hooks + EB_HOOK_BEGIN_COLOR_BMP; + else + hook = hookset->hooks + EB_HOOK_BEGIN_COLOR_JPEG; + break; + + case 0x4f: + /* beginning of clickable area */ + in_step = 34; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + argc = 7; + argv[1] = eb_bcd2(cache_p + 8); + argv[2] = eb_bcd2(cache_p + 10); + argv[3] = eb_bcd2(cache_p + 12); + argv[4] = eb_bcd2(cache_p + 14); + argv[5] = eb_bcd4(cache_p + 28); + argv[6] = eb_bcd2(cache_p + 32); + hook = hookset->hooks + EB_HOOK_BEGIN_CLICKABLE_AREA; + break; + + case 0x49: case 0x4e: + + in_step = 2; + context->skip_code = eb_uint1(cache_p + 1) + 0x20; + break; + + case 0x52: + /* end of reference to monochrome graphic */ + in_step = 8; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + argc = 3; + argv[1] = eb_bcd4(cache_p + 2); + argv[2] = eb_bcd2(cache_p + 6); + hook = hookset->hooks + EB_HOOK_END_MONO_GRAPHIC; + break; + + case 0x53: + /* end of EB sound */ + in_step = 10; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + break; + + case 0x59: + /* end of MPEG movie */ + in_step = 2; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + hook = hookset->hooks + EB_HOOK_END_MPEG; + break; + + case 0x5c: + /* end of inline color graphic */ + in_step = 2; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + hook = hookset->hooks + EB_HOOK_END_IN_COLOR_GRAPHIC; + break; + + case 0x61: + /* end of keyword */ + in_step = 2; + hook = hookset->hooks + EB_HOOK_END_KEYWORD; + break; + + case 0x62: + /* end of reference */ + in_step = 8; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + argc = 3; + argv[1] = eb_bcd4(cache_p + 2); + argv[2] = eb_bcd2(cache_p + 6); + hook = hookset->hooks + EB_HOOK_END_REFERENCE; + break; + + case 0x63: + /* end of an entry of a candidate */ + in_step = 8; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + argc = 3; + argv[1] = eb_bcd4(cache_p + 2); + argv[2] = eb_bcd2(cache_p + 6); + if (argv[1] == 0 && argv[2] == 0) + hook = hookset->hooks + EB_HOOK_END_CANDIDATE_LEAF; + else + hook = hookset->hooks + EB_HOOK_END_CANDIDATE_GROUP; + break; + + case 0x64: + /* end of monochrome graphic */ + in_step = 8; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + argc = 3; + argv[1] = eb_bcd4(cache_p + 2); + argv[2] = eb_bcd2(cache_p + 6); + hook = hookset->hooks + EB_HOOK_END_MONO_GRAPHIC; + break; + + case 0x6b: + /* end of paged reference */ + in_step = 2; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + hook = hookset->hooks + EB_HOOK_END_GRAPHIC_REFERENCE; + break; + + case 0x6a: + /* end of WAVE sound */ + in_step = 2; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + hook = hookset->hooks + EB_HOOK_END_WAVE; + break; + + case 0x6c: + /* end of image page */ + in_step = 2; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + context->text_status = EB_TEXT_STATUS_SOFT_STOP; + hook = hookset->hooks + EB_HOOK_END_IMAGE_PAGE; + break; + + case 0x6d: + /* end of color graphic (BMP or JPEG) */ + in_step = 2; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + hook = hookset->hooks + EB_HOOK_END_COLOR_GRAPHIC; + break; + + case 0x6f: + /* end of clickable area */ + in_step = 2; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + hook = hookset->hooks + EB_HOOK_END_CLICKABLE_AREA; + break; + + case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: + case 0x76: case 0x77: case 0x78: case 0x79: case 0x7a: case 0x7b: + case 0x7c: case 0x7d: case 0x7e: case 0x7f: + case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: + case 0x86: case 0x87: case 0x88: case 0x89: case 0x8a: case 0x8b: + case 0x8c: case 0x8d: case 0x8e: case 0x8f: + in_step = 2; + context->skip_code = eb_uint1(cache_p + 1) + 0x20; + break; + + case 0xe0: + /* character modification */ + in_step = 4; + if (cache_rest_length < in_step) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + argc = 2; + argv[1] = eb_uint2(cache_p + 2); + hook = hookset->hooks + EB_HOOK_BEGIN_DECORATION; + + /* Some old EB books don't take an argument. */ + if (book->disc_code != EB_DISC_EPWING + && eb_uint1(cache_p + 2) >= 0x1f) { + in_step = 2; + hook = &null_hook; + } + break; + + case 0xe1: + in_step = 2; + hook = hookset->hooks + EB_HOOK_END_DECORATION; + break; + + case 0xe4: case 0xe6: case 0xe8: case 0xea: case 0xec: case 0xee: + case 0xf0: case 0xf2: case 0xf4: case 0xf6: case 0xf8: case 0xfa: + case 0xfc: case 0xfe: + in_step = 2; + context->skip_code = eb_uint1(cache_p + 1) + 0x01; + break; + + default: + in_step = 2; + if (context->skip_code == eb_uint1(cache_p + 1)) + context->skip_code = SKIP_CODE_NONE; + break; + } + + if (context->skip_code == SKIP_CODE_NONE + && hook->function != NULL + && !forward_only) { + error_code = hook->function(book, appendix, container, + hook->code, argc, argv); + if (error_code != EB_SUCCESS) + goto failed; + } + + /* + * Post process. Clean a candidate. + */ + if (c2 == 0x63) { + /* end of an entry of candidate */ + context->is_candidate = 0; + } + + } else if (book->character_code == EB_CHARCODE_ISO8859_1) { + /* + * The book is mainly written in ISO 8859 1. + */ + context->printable_count++; + + if ((0x20 <= c1 && c1 < 0x7f) || (0xa0 <= c1 && c1 <= 0xff)) { + /* + * This is an ISO 8859 1 character. + */ + in_step = 1; + argv[0] = eb_uint1(cache_p); + + if (context->skip_code == SKIP_CODE_NONE) { + if (context->is_candidate + && candidate_length < EB_MAX_WORD_LENGTH) { + *candidate_p++ = c1 | 0x80; + *candidate_p = '\0'; + candidate_length++; + } + + hook = hookset->hooks + EB_HOOK_ISO8859_1; + if (forward_only) { + ; /* do nothing */ + } else if (hook->function == NULL) { + error_code = eb_write_text_byte1(book, c1); + if (error_code != EB_SUCCESS) + goto failed; + } else { + error_code = hook->function(book, appendix, container, + EB_HOOK_ISO8859_1, argc, argv); + if (error_code != EB_SUCCESS) + goto failed; + } + } + } else { + /* + * This is a local character. + */ + if (cache_rest_length < 2) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + in_step = 2; + argv[0] = eb_uint2(cache_p); + if (context->skip_code == SKIP_CODE_NONE) { + hook = hookset->hooks + EB_HOOK_NARROW_FONT; + if (forward_only) { + ; /* do nothing */ + } else if (hook->function == NULL) { + error_code = eb_write_text_byte1(book, c1); + if (error_code != EB_SUCCESS) + goto failed; + } else { + error_code = hook->function(book, appendix, container, + EB_HOOK_NARROW_FONT, argc, argv); + if (error_code != EB_SUCCESS) + goto failed; + } + } + } + + } else { + /* + * The book is written in JIS X 0208 or JIS X 0208 + GB 2312. + */ + context->printable_count++; + in_step = 2; + + if (cache_rest_length < 2) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + c2 = eb_uint1(cache_p + 1); + + if (context->skip_code != SKIP_CODE_NONE) { + /* nothing to be done. */ + } else if (0x20 < c1 && c1 < 0x7f && 0x20 < c2 && c2 < 0x7f) { + /* + * This is a JIS X 0208 KANJI character. + */ + argv[0] = eb_uint2(cache_p) | 0x8080; + + if (context->is_candidate + && candidate_length < EB_MAX_WORD_LENGTH - 1) { + *candidate_p++ = c1 | 0x80; + *candidate_p++ = c2 | 0x80; + *candidate_p = '\0'; + candidate_length += 2; + } + + if (context->ebxac_gaiji_flag) { + hook = hookset->hooks + EB_HOOK_EBXAC_GAIJI; + if (forward_only) { + ; /* do nothing */ + } else if (hook->function == NULL) { + error_code = eb_write_text_byte2(book, c1 | 0x80, + c2 | 0x80); + if (error_code != EB_SUCCESS) + goto failed; + } else { + error_code = hook->function(book, appendix, container, + EB_HOOK_EBXAC_GAIJI, 0, argv); + if (error_code != EB_SUCCESS) + goto failed; + } + } else if (context->narrow_flag) { + hook = hookset->hooks + EB_HOOK_NARROW_JISX0208; + if (forward_only) { + ; /* do nothing */ + } else if (hook->function == NULL) { + error_code = eb_write_text_byte2(book, c1 | 0x80, + c2 | 0x80); + if (error_code != EB_SUCCESS) + goto failed; + } else { + error_code = hook->function(book, appendix, container, + EB_HOOK_NARROW_JISX0208, 0, argv); + if (error_code != EB_SUCCESS) + goto failed; + } + } else { + hook = hookset->hooks + EB_HOOK_WIDE_JISX0208; + if (forward_only) { + ; /* do nothing */ + } else if (hook->function == NULL) { + error_code = eb_write_text_byte2(book, c1 | 0x80, + c2 | 0x80); + if (error_code != EB_SUCCESS) + goto failed; + } else { + error_code = hook->function(book, appendix, container, + EB_HOOK_WIDE_JISX0208, argc, argv); + if (error_code != EB_SUCCESS) + goto failed; + } + } + } else if (0x20 < c1 && c1 < 0x7f && 0xa0 < c2 && c2 < 0xff) { + /* + * This is a GB 2312 HANJI character. + */ + argv[0] = eb_uint2(cache_p) | 0x8000; + + if (context->is_candidate + && candidate_length < EB_MAX_WORD_LENGTH - 1) { + *candidate_p++ = c1 | 0x80; + *candidate_p++ = c2; + *candidate_p = '\0'; + candidate_length += 2; + } + + hook = hookset->hooks + EB_HOOK_GB2312; + if (forward_only) { + ; /* do nothing */ + } else if (hook->function == NULL) { + error_code = eb_write_text_byte2(book, c1 | 0x80, c2); + if (error_code != EB_SUCCESS) + goto failed; + } else { + error_code = hook->function(book, appendix, container, + EB_HOOK_GB2312, 0, argv); + if (error_code != EB_SUCCESS) + goto failed; + } + } else if (0xa0 < c1 && c1 < 0xff && 0x20 < c2 && c2 < 0x7f) { + /* + * This is a local character. + */ + argv[0] = eb_uint2(cache_p); + + if (context->narrow_flag) { + hook = hookset->hooks + EB_HOOK_NARROW_FONT; + if (forward_only) { + ; /* do nothing */ + } else if (hook->function == NULL) { + error_code = eb_write_text_byte2(book, c1, c2); + if (error_code != EB_SUCCESS) + goto failed; + } else { + error_code = hook->function(book, appendix, container, + EB_HOOK_NARROW_FONT, argc, argv); + if (error_code != EB_SUCCESS) + goto failed; + } + } else { + hook = hookset->hooks + EB_HOOK_WIDE_FONT; + if (forward_only) { + ; /* do nothing */ + } else if (hook->function == NULL) { + error_code = eb_write_text_byte2(book, c1, c2); + if (error_code != EB_SUCCESS) + goto failed; + } else { + error_code = hook->function(book, appendix, container, + EB_HOOK_WIDE_FONT, argc, argv); + if (error_code != EB_SUCCESS) + goto failed; + } + } + } + } + + /* + * Update variables. + */ + cache_p += in_step; + cache_rest_length -= in_step; + context->location += in_step; + in_step = 0; + + /* + * Break if an unprocessed character is remained. + */ + if (context->unprocessed != NULL) + break; + /* + * Break if EB_TEXT_STATUS_SOFT_STOP is set. + */ + if (context->text_status == EB_TEXT_STATUS_SOFT_STOP) + break; + } + + succeeded: + if (!forward_only) { + *text_length = (context->out - text); + *(context->out) = '\0'; + } + + LOG(("out: eb_read_text_internal(text_length=%ld) = %s", + (text_length == NULL) ? 0L : (long)*text_length, + eb_error_string(EB_SUCCESS))); + pthread_mutex_unlock(&cache_mutex); + + return EB_SUCCESS; + + /* + * An error occurs... + * Discard cache if read error occurs. + */ + failed: + if (!forward_only) { + *text_length = -1; + *text = '\0'; + } + if (error_code == EB_ERR_FAIL_READ_TEXT) + cache_book_code = EB_BOOK_NONE; + LOG(("out: eb_read_text_internal() = %s", eb_error_string(error_code))); + pthread_mutex_unlock(&cache_mutex); + return error_code; +} + + +/* + * Check whether an escape sequence is stop-code or not. + */ +static int +eb_is_stop_code(EB_Book *book, EB_Appendix *appendix, unsigned int code0, + unsigned int code1) +{ + int result; + + if (appendix == NULL + || appendix->subbook_current == NULL + || appendix->subbook_current->stop_code0 == 0) { + result = (code0 == 0x1f41 + && code1 == book->text_context.auto_stop_code); + } else { + result = (code0 == appendix->subbook_current->stop_code0 + && code1 == appendix->subbook_current->stop_code1); + } + + return result; +} + + +/* + * Have the current text context reached the end of text? + */ +int +eb_is_text_stopped(EB_Book *book) +{ + int is_stopped = 0; + + eb_lock(book); + LOG(("in: eb_is_text_stopped(book=%d)", (int)book->code)); + + if (book->subbook_current != NULL) { + if (book->text_context.code == EB_TEXT_HEADING + || book->text_context.code == EB_TEXT_MAIN_TEXT + || book->text_context.code == EB_TEXT_OPTIONAL_TEXT) { + if (book->text_context.text_status != EB_TEXT_STATUS_CONTINUED + && book->text_context.unprocessed == NULL) { + is_stopped = 1; + } + } + } + + LOG(("out: eb_is_text_stopped() = %d", is_stopped)); + return is_stopped; +} + + +/* + * Write a byte to a text buffer. + */ +EB_Error_Code +eb_write_text_byte1(EB_Book *book, int byte1) +{ + EB_Error_Code error_code; + char stream[1]; + + LOG(("in: eb_write_text_byte1(book=%d, byte1=%d)", + (int)book->code, byte1)); + + /* + * If the text buffer has enough space to write `byte1', + * save the byte in `book->text_context.unprocessed'. + */ + if (book->text_context.unprocessed != NULL + || book->text_context.out_rest_length < 1) { + *(unsigned char *)stream = byte1; + error_code = eb_write_text(book, stream, 1); + if (error_code != EB_SUCCESS) + goto failed; + } else { + *(book->text_context.out) = byte1; + book->text_context.out++; + book->text_context.out_rest_length--; + book->text_context.out_step++; + } + + LOG(("out: eb_write_text_byte1() = %s", eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_write_text_byte1() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Write two bytes to a text buffer for output. + */ +EB_Error_Code +eb_write_text_byte2(EB_Book *book, int byte1, int byte2) +{ + EB_Error_Code error_code; + char stream[2]; + + LOG(("in: eb_write_text_byte2(book=%d, byte1=%d, byte2=%d)", + (int)book->code, byte1, byte2)); + + /* + * If the text buffer has enough space to write `byte1' and `byte2', + * save the bytes in `book->text_context.unprocessed'. + */ + if (book->text_context.unprocessed != NULL + || book->text_context.out_rest_length < 2) { + *(unsigned char *)stream = byte1; + *(unsigned char *)(stream + 1) = byte2; + error_code = eb_write_text(book, stream, 2); + if (error_code != EB_SUCCESS) + goto failed; + } else { + *(book->text_context.out) = byte1; + book->text_context.out++; + *(book->text_context.out) = byte2; + book->text_context.out++; + book->text_context.out_rest_length -= 2; + book->text_context.out_step += 2; + } + + LOG(("out: eb_write_text_byte2() = %s", eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_write_text_byte2() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Write a string to a text buffer. + */ +EB_Error_Code +eb_write_text_string(EB_Book *book, const char *string) +{ + EB_Error_Code error_code; + size_t string_length; + + LOG(("in: eb_write_text_string(book=%d, string=%s)", + (int)book->code, eb_quoted_string(string))); + + /* + * If the text buffer has enough space to write `sting', + * save the string in `book->text_context.unprocessed'. + */ + string_length = strlen(string); + + if (book->text_context.unprocessed != NULL + || book->text_context.out_rest_length < string_length) { + error_code = eb_write_text(book, string, string_length); + if (error_code != EB_SUCCESS) + goto failed; + } else { + memcpy(book->text_context.out, string, string_length); + book->text_context.out += string_length; + book->text_context.out_rest_length -= string_length; + book->text_context.out_step += string_length; + } + + LOG(("out: eb_write_text_string() = %s", eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_write_text_string() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Write a stream with `length' bytes to a text buffer. + */ +EB_Error_Code +eb_write_text(EB_Book *book, const char *stream, size_t stream_length) +{ + EB_Error_Code error_code; + char *reallocated; + + LOG(("in: eb_write_text(book=%d, stream=%s)", + (int)book->code, eb_quoted_stream(stream, stream_length))); + + /* + * If the text buffer has enough space to write `stream', + * save the stream in `book->text_context.unprocessed'. + */ + if (book->text_context.unprocessed != NULL) { + reallocated = (char *)realloc(book->text_context.unprocessed, + book->text_context.unprocessed_size + stream_length); + if (reallocated == NULL) { + free(book->text_context.unprocessed); + book->text_context.unprocessed = NULL; + book->text_context.unprocessed_size = 0; + error_code = EB_ERR_MEMORY_EXHAUSTED; + goto failed; + } + memcpy(reallocated + book->text_context.unprocessed_size, stream, + stream_length); + book->text_context.unprocessed = reallocated; + book->text_context.unprocessed_size += stream_length; + + } else if (book->text_context.out_rest_length < stream_length) { + book->text_context.unprocessed + = (char *)malloc(book->text_context.out_step + stream_length); + if (book->text_context.unprocessed == NULL) { + error_code = EB_ERR_MEMORY_EXHAUSTED; + goto failed; + } + book->text_context.unprocessed_size + = book->text_context.out_step + stream_length; + memcpy(book->text_context.unprocessed, + book->text_context.out - book->text_context.out_step, + book->text_context.out_step); + memcpy(book->text_context.unprocessed + book->text_context.out_step, + stream, stream_length); + book->text_context.out -= book->text_context.out_step; + book->text_context.out_step = 0; + + } else { + memcpy(book->text_context.out, stream, stream_length); + book->text_context.out += stream_length; + book->text_context.out_rest_length -= stream_length; + book->text_context.out_step += stream_length; + } + + LOG(("out: eb_write_text() = %s", eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_write_text() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Get the current candidate word for multi search. + */ +const char * +eb_current_candidate(EB_Book *book) +{ + LOG(("in: eb_current_candidate(book=%d)", (int)book->code)); + + if (!book->text_context.is_candidate) + book->text_context.candidate[0] = '\0'; + + LOG(("out: eb_current_candidate() = %s", + eb_quoted_string(book->text_context.candidate))); + + return book->text_context.candidate; +} + + +/* + * Forward text position to the next paragraph. + */ +EB_Error_Code +eb_forward_text(EB_Book *book, EB_Appendix *appendix) +{ + EB_Error_Code error_code; + + eb_lock(&book->lock); + LOG(("in: eb_forward_text(book=%d, appendix=%d)", (int)book->code, + (appendix != NULL) ? (int)appendix->code : 0)); + + /* + * Current subbook must have been set and START file must exist. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + if (zio_file(&book->subbook_current->text_zio) < 0) { + error_code = EB_ERR_NO_TEXT; + goto failed; + } + + if (book->text_context.code == EB_TEXT_SEEKED) { + book->text_context.code = EB_TEXT_MAIN_TEXT; + } else if (book->text_context.code == EB_TEXT_INVALID) { + error_code = EB_ERR_NO_PREV_SEEK; + goto failed; + } else if (book->text_context.code != EB_TEXT_MAIN_TEXT + && book->text_context.code != EB_TEXT_OPTIONAL_TEXT) { + error_code = EB_ERR_DIFF_CONTENT; + goto failed; + } + + if (book->text_context.text_status == EB_TEXT_STATUS_SOFT_STOP) { + book->text_context.text_status = EB_TEXT_STATUS_CONTINUED; + goto succeeded; + } else if (book->text_context.text_status == EB_TEXT_STATUS_HARD_STOP) { + error_code = EB_ERR_END_OF_CONTENT; + goto failed; + } + + /* + * Forward text. + */ + error_code = eb_read_text_internal(book, appendix, &eb_default_hookset, + NULL, EB_SIZE_PAGE, NULL, NULL, 1); + if (error_code != EB_SUCCESS) + goto failed; + + /* + * Unlock the book and hookset. + */ + succeeded: + eb_reset_text_context(book); + LOG(("out: eb_forward_text() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + if (error_code != EB_ERR_END_OF_CONTENT) + eb_invalidate_text_context(book); + LOG(("out: eb_forward_text() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Forward heading position to the next paragraph. + * (for keyword search.) + */ +EB_Error_Code +eb_forward_heading(EB_Book *book) +{ + EB_Error_Code error_code; + + eb_lock(&book->lock); + LOG(("in: eb_forward_heading(book=%d)", (int)book->code)); + + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + if (zio_file(&book->subbook_current->text_zio) < 0) { + error_code = EB_ERR_NO_TEXT; + goto failed; + } + + if (book->text_context.code == EB_TEXT_SEEKED) { + book->text_context.code = EB_TEXT_HEADING; + } else if (book->text_context.code == EB_TEXT_INVALID) { + error_code = EB_ERR_NO_PREV_SEEK; + goto failed; + } else if (book->text_context.code != EB_TEXT_HEADING) { + error_code = EB_ERR_DIFF_CONTENT; + goto failed; + } + + if (book->text_context.text_status == EB_TEXT_STATUS_SOFT_STOP) { + book->text_context.text_status = EB_TEXT_STATUS_CONTINUED; + goto succeeded; + } else if (book->text_context.text_status == EB_TEXT_STATUS_HARD_STOP) { + error_code = EB_ERR_END_OF_CONTENT; + goto failed; + } + + /* + * Forward text. + */ + error_code = eb_read_text_internal(book, NULL, &eb_default_hookset, + NULL, EB_SIZE_PAGE, NULL, NULL, 1); + if (error_code != EB_SUCCESS) + goto failed; + + eb_reset_text_context(book); + + /* + * Unlock cache data. + */ + succeeded: + LOG(("out: eb_forward_heading() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + if (error_code != EB_ERR_END_OF_CONTENT) + eb_invalidate_text_context(book); + LOG(("out: eb_forward_heading() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Backward text position to the previous paragraph. + */ +EB_Error_Code +eb_backward_text(EB_Book *book, EB_Appendix *appendix) +{ + EB_Error_Code error_code; + EB_Text_Context saved_context; + off_t current_location; + off_t forward_location; + off_t read_location; + off_t backward_location = -1; + char text_buffer[EB_SIZE_PAGE]; + char *text_buffer_p; + ssize_t read_result; + int stop_code0, stop_code1; + + eb_lock(&book->lock); + LOG(("in: eb_backward_text(book=%d, appendix=%d)", (int)book->code, + (appendix != NULL) ? (int)appendix->code : 0)); + + /* + * Current subbook must have been set and START file must exist. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + if (zio_file(&book->subbook_current->text_zio) < 0) { + error_code = EB_ERR_NO_TEXT; + goto failed; + } + + if (book->text_context.code == EB_TEXT_SEEKED) { + book->text_context.code = EB_TEXT_MAIN_TEXT; + } else if (book->text_context.code == EB_TEXT_INVALID) { + error_code = EB_ERR_NO_PREV_SEEK; + goto failed; + } else if (book->text_context.code != EB_TEXT_MAIN_TEXT + && book->text_context.code != EB_TEXT_OPTIONAL_TEXT) { + error_code = EB_ERR_DIFF_CONTENT; + goto failed; + } + + /* + * Forward text to get auto-stop-code and location where the current + * text stops. + */ + if (book->text_context.text_status != EB_TEXT_STATUS_CONTINUED) { + forward_location = book->text_context.location; + } else { + memcpy(&saved_context, &book->text_context, sizeof(EB_Text_Context)); + error_code = eb_read_text_internal(book, NULL, &eb_default_hookset, + NULL, EB_SIZE_PAGE, NULL, NULL, 1); + if (error_code != EB_SUCCESS && error_code != EB_ERR_END_OF_CONTENT) + goto failed; + forward_location = book->text_context.location; + saved_context.auto_stop_code = book->text_context.auto_stop_code; + memcpy(&book->text_context, &saved_context, sizeof(EB_Text_Context)); + } + + /* + * Determine stop-code. + */ + if (appendix == NULL + || appendix->subbook_current == NULL + || appendix->subbook_current->stop_code0 == 0) { + stop_code0 = 0x1f41; + stop_code1 = book->text_context.auto_stop_code; + } else { + stop_code0 = appendix->subbook_current->stop_code0; + stop_code1 = appendix->subbook_current->stop_code1; + } + + /* + * If the text locator has pointed to `0x1f02' (beginning of text), + * we cannot backward. + */ + if (zio_lseek(&book->subbook_current->text_zio, + book->text_context.location, SEEK_SET) == -1) { + error_code = EB_ERR_FAIL_SEEK_TEXT; + goto failed; + } + if (zio_read(&book->subbook_current->text_zio, text_buffer, 2) != 2) { + error_code = EB_ERR_FAIL_READ_TEXT; + goto failed; + } + if (eb_uint2(text_buffer) == 0x1f02) { + error_code = EB_ERR_END_OF_CONTENT; + goto failed; + } + + /* + * Backward text. + */ + current_location = book->text_context.location; + + while (0 < book->text_context.location) { + size_t backward_distance; + int i; + + /* + * Seek and read text. + * + * Since a stop code occupies 4 bytes and we start scanning + * stop-code at preceding byte of the current location, we read + * text in front of the current location and following 3 bytes. + * + * start scanning + * | current location + * | | + * [..] [..] [..] [1F] [41] [00] [01] + * =================== + * may be stop-code + */ + if (book->text_context.location < EB_SIZE_PAGE + 3) + read_location = 0; + else + read_location = book->text_context.location - EB_SIZE_PAGE + 3; + backward_distance = book->text_context.location - read_location; + + if (zio_lseek(&book->subbook_current->text_zio, read_location, + SEEK_SET) == -1) { + error_code = EB_ERR_FAIL_SEEK_TEXT; + goto failed; + } + + memset(text_buffer, 0x00, EB_SIZE_PAGE); + read_result = zio_read(&book->subbook_current->text_zio, text_buffer, + EB_SIZE_PAGE); + if (read_result < 0 || read_result < backward_distance) { + error_code = EB_ERR_FAIL_READ_TEXT; + goto failed; + } + + /* + * Scan stop-code. + */ + text_buffer_p = text_buffer + backward_distance - 1; + i = backward_distance - 1; + + while (0 <= i) { + if (eb_uint2(text_buffer_p) == 0x1f02) { + book->text_context.location = read_location + i; + if (current_location <= book->text_context.location + 2) { + error_code = EB_ERR_END_OF_CONTENT; + goto failed; + } + backward_location = book->text_context.location; + goto loop_end; + } + if (book->text_context.code != EB_TEXT_MAIN_TEXT + || eb_uint2(text_buffer_p) != stop_code0 + || eb_uint2(text_buffer_p + 2) != stop_code1) { + text_buffer_p--; + i--; + continue; + } + + eb_reset_text_context(book); + book->text_context.location = read_location + i; + error_code = eb_read_text_internal(book, appendix, + &eb_default_hookset, NULL, EB_SIZE_PAGE, NULL, NULL, 1); + if (error_code != EB_SUCCESS + && error_code != EB_ERR_END_OF_CONTENT) + goto failed; + + if (book->text_context.location >= current_location - 4 + && book->text_context.location <= current_location + 4 + && backward_location < 0) + forward_location = current_location; + if (book->text_context.location >= forward_location - 4 + && book->text_context.location <= forward_location + 4) + backward_location = read_location + i; + else if (book->text_context.location < forward_location) + goto loop_end; + + text_buffer_p--; + i--; + } + + book->text_context.location = read_location - 1; + } + + loop_end: + if (backward_location < 0) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + eb_reset_text_context(book); + + /* + * Unlock the book and hookset. + */ + LOG(("out: eb_forward_text() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_invalidate_text_context(book); + LOG(("out: eb_backward_text() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + diff --git a/search.c b/search.c new file mode 100644 index 0000000..aef9d38 --- /dev/null +++ b/search.c @@ -0,0 +1,1650 @@ +/* + * 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 "text.h" +#include "build-post.h" + +/* + * Page-ID macros. + */ +#define PAGE_ID_IS_LEAF_LAYER(page_id) (((page_id) & 0x80) == 0x80) +#define PAGE_ID_IS_LAYER_START(page_id) (((page_id) & 0x40) == 0x40) +#define PAGE_ID_IS_LAYER_END(page_id) (((page_id) & 0x20) == 0x20) +#define PAGE_ID_HAVE_GROUP_ENTRY(page_id) (((page_id) & 0x10) == 0x10) + +/* + * The maximum number of hit entries for tomporary hit lists. + * This is used in eb_hit_list(). + */ +#define EB_TMP_MAX_HITS 64 + +/* + * Book-code of the book in which you want to search a word. + */ +static EB_Book_Code cache_book_code = EB_BOOK_NONE; + +/* + * Cache buffer for the current page. + */ +static char cache_buffer[EB_SIZE_PAGE]; + +/* + * Cache buffer for the current page. + */ +static int cache_page; + +/* + * Unexported functions. + */ +static EB_Error_Code eb_hit_list_word(EB_Book *book, + EB_Search_Context *context, int max_hit_count, EB_Hit *hit_list, + int *hit_count); +static EB_Error_Code eb_hit_list_keyword(EB_Book *book, + EB_Search_Context *context, int max_hit_count, EB_Hit *hit_list, + int *hit_count); +static EB_Error_Code eb_hit_list_multi(EB_Book *book, + EB_Search_Context *context, int max_hit_count, EB_Hit *hit_list, + int *hit_count); +static void eb_and_hit_lists(EB_Hit and_list[EB_TMP_MAX_HITS], + int *and_count, int max_and_count, int hit_list_count, + EB_Hit hit_lists[EB_NUMBER_OF_SEARCH_CONTEXTS][EB_TMP_MAX_HITS], + int hit_counts[EB_NUMBER_OF_SEARCH_CONTEXTS]); + + +/* + * Intialize search contexts of `book'. + */ +void +eb_initialize_search_contexts(EB_Book *book) +{ + EB_Search_Context *context; + int i; + + LOG(("in: eb_initialize_search_context(book=%d)", (int)book->code)); + + for (i = 0, context = book->search_contexts; + i < EB_NUMBER_OF_SEARCH_CONTEXTS; i++, context++) { + context->code = EB_SEARCH_NONE; + context->compare_pre = NULL; + context->compare_single = NULL; + context->compare_group = NULL; + context->comparison_result = -1; + context->word[0] = '\0'; + context->canonicalized_word[0] = '\0'; + context->page = 0; + context->offset = 0; + context->page_id = 0; + context->entry_count = 0; + context->entry_index = 0; + context->entry_length = 0; + context->entry_arrangement = EB_ARRANGE_INVALID; + context->in_group_entry = 0; + context->keyword_heading.page = 0; + context->keyword_heading.offset = 0; + } + + LOG(("out: eb_initialize_search_context()")); +} + + +/* + * Finalize search contexts of `book'. + */ +void +eb_finalize_search_contexts(EB_Book *book) +{ + LOG(("in+out: eb_finalize_search_context(book=%d)", (int)book->code)); + + /* nothing to be done */ +} + + +/* + * Reset search context of `book'. + */ +void +eb_reset_search_contexts(EB_Book *book) +{ + LOG(("in: eb_reset_search_context(book=%d)", (int)book->code)); + + eb_initialize_search_contexts(book); + + LOG(("out: eb_reset_search_context()")); +} + + +/* + * Intialize a search element. + */ +void +eb_initialize_search(EB_Search *search) +{ + search->index_id = 0; + search->start_page = 0; + search->end_page = 0; + search->candidates_page = 0; + search->katakana = EB_INDEX_STYLE_CONVERT; + search->lower = EB_INDEX_STYLE_CONVERT; + search->mark = EB_INDEX_STYLE_DELETE; + search->long_vowel = EB_INDEX_STYLE_CONVERT; + search->double_consonant = EB_INDEX_STYLE_CONVERT; + search->contracted_sound = EB_INDEX_STYLE_CONVERT; + search->voiced_consonant = EB_INDEX_STYLE_CONVERT; + search->small_vowel = EB_INDEX_STYLE_CONVERT; + search->p_sound = EB_INDEX_STYLE_CONVERT; + search->space = EB_INDEX_STYLE_DELETE; + search->label[0] = '\0'; +} + + +/* + * Finalize a search element. + */ +void +eb_finalize_search(EB_Search *search) +{ + /* nothing to be done */ +} + + +/* + * Initialize all search elements in the current subbook. + */ +void +eb_initialize_searches(EB_Book *book) +{ + EB_Subbook *subbook; + EB_Multi_Search *multi; + EB_Search *entry; + int i, j; + + LOG(("in: eb_initialize_searches(book=%d)", (int)book->code)); + + subbook = book->subbook_current; + + eb_initialize_search(&subbook->word_alphabet); + eb_initialize_search(&subbook->word_asis); + eb_initialize_search(&subbook->word_kana); + eb_initialize_search(&subbook->endword_alphabet); + eb_initialize_search(&subbook->endword_asis); + eb_initialize_search(&subbook->endword_kana); + eb_initialize_search(&subbook->keyword); + eb_initialize_search(&subbook->cross); + eb_initialize_search(&subbook->menu); + eb_initialize_search(&subbook->image_menu); + eb_initialize_search(&subbook->copyright); + eb_initialize_search(&subbook->text); + eb_initialize_search(&subbook->sound); + + for (i = 0, multi = subbook->multis; i < EB_MAX_MULTI_SEARCHES; + i++, multi++) { + eb_initialize_search(&multi->search); + multi->title[0] = '\0'; + multi->entry_count = 0; + for (j = 0, entry = multi->entries; + j < EB_MAX_MULTI_ENTRIES; j++, entry++) { + eb_initialize_search(entry); + } + } + + LOG(("out: eb_initialize_searches(book=%d)", (int)book->code)); +} + + +/* + * Finalize all search elements in the current subbook. + */ +void +eb_finalize_searches(EB_Book *book) +{ + EB_Subbook *subbook; + EB_Multi_Search *multi; + EB_Search *entry; + int i, j; + + LOG(("in: eb_finalize_searches(book=%d)", (int)book->code)); + + subbook = book->subbook_current; + + eb_finalize_search(&subbook->word_alphabet); + eb_finalize_search(&subbook->word_asis); + eb_finalize_search(&subbook->word_kana); + eb_finalize_search(&subbook->endword_alphabet); + eb_finalize_search(&subbook->endword_asis); + eb_finalize_search(&subbook->endword_kana); + eb_finalize_search(&subbook->keyword); + eb_finalize_search(&subbook->menu); + eb_finalize_search(&subbook->image_menu); + eb_finalize_search(&subbook->copyright); + eb_finalize_search(&subbook->text); + eb_finalize_search(&subbook->sound); + + for (i = 0, multi = subbook->multis; i < EB_MAX_KEYWORDS; + i++, multi++) { + eb_finalize_search(&multi->search); + multi->entry_count = 0; + for (j = 0, entry = multi->entries; + j < multi->entry_count; j++, entry++) { + eb_finalize_search(entry); + } + } + + LOG(("out: eb_finalize_searches()")); +} + + +/* + * Pre-search for a word described in the current search context. + * It descends intermediate indexes and reached to a leaf page that + * may have the word. + * If succeeded, 0 is returned. Otherwise -1 is returned. + */ +EB_Error_Code +eb_presearch_word(EB_Book *book, EB_Search_Context *context) +{ + EB_Error_Code error_code; + int next_page; + int index_depth; + char *cache_p; + + pthread_mutex_lock(&cache_mutex); + LOG(("in: eb_presearch_word(book=%d)", (int)book->code)); + + /* + * Discard cache data. + */ + cache_book_code = EB_BOOK_NONE; + + /* + * Search the word in intermediate indexes. + * Find a page number of the leaf index page. + */ + for (index_depth = 0; index_depth < EB_MAX_INDEX_DEPTH; index_depth++) { + next_page = context->page; + + /* + * Seek and read a page. + */ + if (zio_lseek(&book->subbook_current->text_zio, + ((off_t) context->page - 1) * EB_SIZE_PAGE, SEEK_SET) < 0) { + cache_book_code = EB_BOOK_NONE; + error_code = EB_ERR_FAIL_SEEK_TEXT; + goto failed; + } + if (zio_read(&book->subbook_current->text_zio, cache_buffer, + EB_SIZE_PAGE) != EB_SIZE_PAGE) { + cache_book_code = EB_BOOK_NONE; + error_code = EB_ERR_FAIL_READ_TEXT; + goto failed; + } + + /* + * Get some data from the read page. + */ + context->page_id = eb_uint1(cache_buffer); + context->entry_length = eb_uint1(cache_buffer + 1); + if (context->entry_length == 0) + context->entry_arrangement = EB_ARRANGE_VARIABLE; + else + context->entry_arrangement = EB_ARRANGE_FIXED; + context->entry_count = eb_uint2(cache_buffer + 2); + context->offset = 4; + cache_p = cache_buffer + 4; + + LOG(("aux: eb_presearch_word(page=%d, page_id=0x%02x, \ +entry_length=%d, entry_arrangement=%d, entry_count=%d)", + context->page, context->page_id, context->entry_length, + context->entry_arrangement, context->entry_count)); + + /* + * Exit the loop if it reached to the leaf index. + */ + if (PAGE_ID_IS_LEAF_LAYER(context->page_id)) + break; + + /* + * Search a page of next level index. + */ + for (context->entry_index = 0; + context->entry_index < context->entry_count; + context->entry_index++) { + if (EB_SIZE_PAGE < context->offset + context->entry_length + 4) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + if (context->compare_pre(context->canonicalized_word, cache_p, + context->entry_length) <= 0) { + next_page = eb_uint4(cache_p + context->entry_length); + break; + } + cache_p += context->entry_length + 4; + context->offset += context->entry_length + 4; + } + if (context->entry_count <= context->entry_index + || context->page == next_page) { + context->comparison_result = -1; + goto succeeded; + } + context->page = next_page; + } + + /* + * Check for the index depth. + */ + if (index_depth == EB_MAX_INDEX_DEPTH) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + /* + * Update search context and cache information. + */ + context->entry_index = 0; + context->comparison_result = 1; + context->in_group_entry = 0; + cache_book_code = book->code; + cache_page = context->page; + + succeeded: + LOG(("out: eb_presearch_word() = %s", eb_error_string(EB_SUCCESS))); + pthread_mutex_unlock(&cache_mutex); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_presearch_word() = %s", eb_error_string(error_code))); + pthread_mutex_unlock(&cache_mutex); + return error_code; +} + +/* + * Get hit entries of a submitted search request. + */ +EB_Error_Code +eb_hit_list(EB_Book *book, int max_hit_count, EB_Hit *hit_list, int *hit_count) +{ + EB_Error_Code error_code; + EB_Search_Context temporary_context; + EB_Hit temporary_hit_lists[EB_NUMBER_OF_SEARCH_CONTEXTS][EB_TMP_MAX_HITS]; + int temporary_hit_counts[EB_NUMBER_OF_SEARCH_CONTEXTS]; + int more_hit_count; + int i; + + /* + * Lock cache data and the book. + */ + pthread_mutex_lock(&cache_mutex); + eb_lock(&book->lock); + LOG(("in: eb_hit_list(book=%d, max_hit_count=%d)", (int)book->code, + max_hit_count)); + + if (max_hit_count == 0) + goto succeeded; + + *hit_count = 0; + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Get a list of hit entries. + */ + switch (book->search_contexts->code) { + case EB_SEARCH_EXACTWORD: + case EB_SEARCH_WORD: + case EB_SEARCH_ALL: + case EB_SEARCH_ENDWORD: + /* + * In case of exactword, word of endword search. + */ + error_code = eb_hit_list_word(book, book->search_contexts, + max_hit_count, hit_list, hit_count); + if (error_code != EB_SUCCESS) + goto failed; + break; + + case EB_SEARCH_KEYWORD: + case EB_SEARCH_CROSS: + /* + * In case of keyword or cross search. + */ + for (;;) { + int search_is_over = 0; + + for (i = 0; i < EB_MAX_KEYWORDS; i++) { + if (book->search_contexts[i].code != EB_SEARCH_KEYWORD + && book->search_contexts[i].code != EB_SEARCH_CROSS) + break; + memcpy(&temporary_context, book->search_contexts + i, + sizeof(EB_Search_Context)); + error_code = eb_hit_list_keyword(book, &temporary_context, + EB_TMP_MAX_HITS, temporary_hit_lists[i], + temporary_hit_counts + i); + if (error_code != EB_SUCCESS) + goto failed; + if (temporary_hit_counts[i] == 0) { + search_is_over = 1; + break; + } + } + if (search_is_over) + break; + + eb_and_hit_lists(hit_list + *hit_count, &more_hit_count, + max_hit_count - *hit_count, i, temporary_hit_lists, + temporary_hit_counts); + + for (i = 0; i < EB_MAX_MULTI_ENTRIES; i++) { + if (book->search_contexts[i].code != EB_SEARCH_KEYWORD + && book->search_contexts[i].code != EB_SEARCH_CROSS) + break; + error_code = eb_hit_list_keyword(book, + book->search_contexts + i, temporary_hit_counts[i], + temporary_hit_lists[i], temporary_hit_counts + i); + if (error_code != EB_SUCCESS) + goto failed; + } + + *hit_count += more_hit_count; + if (max_hit_count <= *hit_count) + break; + } + break; + + case EB_SEARCH_MULTI: + /* + * In case of multi search. + */ + for (;;) { + int search_is_over = 0; + + for (i = 0; i < EB_MAX_MULTI_ENTRIES; i++) { + if (book->search_contexts[i].code != EB_SEARCH_MULTI) + break; + memcpy(&temporary_context, book->search_contexts + i, + sizeof(EB_Search_Context)); + error_code = eb_hit_list_multi(book, &temporary_context, + EB_TMP_MAX_HITS, temporary_hit_lists[i], + temporary_hit_counts + i); + if (error_code != EB_SUCCESS) + goto failed; + if (temporary_hit_counts[i] == 0) { + search_is_over = 1; + break; + } + } + if (search_is_over) + break; + + eb_and_hit_lists(hit_list + *hit_count, &more_hit_count, + max_hit_count - *hit_count, i, temporary_hit_lists, + temporary_hit_counts); + + for (i = 0; i < EB_MAX_MULTI_ENTRIES; i++) { + if (book->search_contexts[i].code != EB_SEARCH_MULTI) + break; + error_code = eb_hit_list_multi(book, + book->search_contexts + i, temporary_hit_counts[i], + temporary_hit_lists[i], temporary_hit_counts + i); + if (error_code != EB_SUCCESS) + goto failed; + } + + *hit_count += more_hit_count; + if (max_hit_count <= *hit_count) + break; + } + break; + + default: + /* not reached */ + error_code = EB_ERR_NO_PREV_SEARCH; + goto failed; + } + + /* + * Unlock cache data and the book. + */ + succeeded: + LOG(("out: eb_hit_list(hit_count=%d) = %s", + *hit_count, eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + pthread_mutex_unlock(&cache_mutex); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *hit_count = 0; + LOG(("out: eb_hit_list() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + pthread_mutex_unlock(&cache_mutex); + return error_code; +} + + +/* + * Get hit entries of a submitted exactword/word/endword search request. + */ +static EB_Error_Code +eb_hit_list_word(EB_Book *book, EB_Search_Context *context, int max_hit_count, + EB_Hit *hit_list, int *hit_count) +{ + EB_Error_Code error_code; + EB_Hit *hit; + int group_id; + char *cache_p; + + LOG(("in: eb_hit_list_word(book=%d, max_hit_count=%d)", (int)book->code, + max_hit_count)); + + hit = hit_list; + *hit_count = 0; + + /* + * If the result of previous comparison is negative value, all + * matched entries have been found. + */ + if (context->comparison_result < 0 || max_hit_count <= 0) + goto succeeded; + + for (;;) { + /* + * Read a page to search, if the page is not on the cache buffer. + * + * Cache may be missed by the two reasons: + * 1. the search process reaches to the end of an index page, + * and tries to read the next page. + * 2. Someone else used the cache buffer. + * + * At the case of 1, the search process reads the page and update + * the search context. At the case of 2. it reads the page but + * must not update the context! + */ + if (cache_book_code != book->code || cache_page != context->page) { + if (zio_lseek(&book->subbook_current->text_zio, + ((off_t) context->page - 1) * EB_SIZE_PAGE, SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_TEXT; + goto failed; + } + if (zio_read(&book->subbook_current->text_zio, + cache_buffer, EB_SIZE_PAGE) != EB_SIZE_PAGE) { + error_code = EB_ERR_FAIL_READ_TEXT; + goto failed; + } + + /* + * Update search context. + */ + if (context->entry_index == 0) { + context->page_id = eb_uint1(cache_buffer); + context->entry_length = eb_uint1(cache_buffer + 1); + if (context->entry_length == 0) + context->entry_arrangement = EB_ARRANGE_VARIABLE; + else + context->entry_arrangement = EB_ARRANGE_FIXED; + context->entry_count = eb_uint2(cache_buffer + 2); + context->entry_index = 0; + context->offset = 4; + } + + cache_book_code = book->code; + cache_page = context->page; + } + + cache_p = cache_buffer + context->offset; + + LOG(("aux: eb_hit_list_word(page=%d, page_id=0x%02x, \ +entry_length=%d, entry_arrangement=%d, entry_count=%d)", + context->page, context->page_id, context->entry_length, + context->entry_arrangement, context->entry_count)); + + if (!PAGE_ID_IS_LEAF_LAYER(context->page_id)) { + /* + * Not a leaf index. It is an error. + */ + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + if (!PAGE_ID_HAVE_GROUP_ENTRY(context->page_id) + && context->entry_arrangement == EB_ARRANGE_FIXED) { + /* + * The leaf index doesn't have a group entry. + * Find text and heading locations. + */ + while (context->entry_index < context->entry_count) { + if (EB_SIZE_PAGE + < context->offset + context->entry_length + 12) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + /* + * Compare word and pattern. + * If matched, add it to a hit list. + */ + context->comparison_result + = context->compare_single(context->word, cache_p, + context->entry_length); + if (context->comparison_result == 0) { + hit->heading.page + = eb_uint4(cache_p + context->entry_length + 6); + hit->heading.offset + = eb_uint2(cache_p + context->entry_length + 10); + hit->text.page + = eb_uint4(cache_p + context->entry_length); + hit->text.offset + = eb_uint2(cache_p + context->entry_length + 4); + hit++; + *hit_count += 1; + } + context->entry_index++; + context->offset += context->entry_length + 12; + cache_p += context->entry_length + 12; + + if (context->comparison_result < 0 + || max_hit_count <= *hit_count) + goto succeeded; + } + + } else if (!PAGE_ID_HAVE_GROUP_ENTRY(context->page_id) + && context->entry_arrangement == EB_ARRANGE_VARIABLE) { + + /* + * The leaf index doesn't have a group entry. + * Find text and heading locations. + */ + while (context->entry_index < context->entry_count) { + if (EB_SIZE_PAGE < context->offset + 1) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + context->entry_length = eb_uint1(cache_p); + if (EB_SIZE_PAGE + < context->offset + context->entry_length + 13) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + /* + * Compare word and pattern. + * If matched, add it to a hit list. + */ + context->comparison_result + = context->compare_single(context->word, cache_p + 1, + context->entry_length); + if (context->comparison_result == 0) { + hit->heading.page + = eb_uint4(cache_p + context->entry_length + 7); + hit->heading.offset + = eb_uint2(cache_p + context->entry_length + 11); + hit->text.page + = eb_uint4(cache_p + context->entry_length + 1); + hit->text.offset + = eb_uint2(cache_p + context->entry_length + 5); + hit++; + *hit_count += 1; + } + context->entry_index++; + context->offset += context->entry_length + 13; + cache_p += context->entry_length + 13; + + if (context->comparison_result < 0 + || max_hit_count <= *hit_count) + goto succeeded; + } + + } else { + /* + * The leaf index have a group entry. + * Find text and heading locations. + */ + while (context->entry_index < context->entry_count) { + if (EB_SIZE_PAGE < context->offset + 2) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + group_id = eb_uint1(cache_p); + + if (group_id == 0x00) { + /* + * 0x00 -- Single entry. + */ + context->entry_length = eb_uint1(cache_p + 1); + if (EB_SIZE_PAGE + < context->offset + context->entry_length + 14) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + /* + * Compare word and pattern. + * If matched, add it to a hit list. + */ + context->comparison_result + = context->compare_single(context->canonicalized_word, + cache_p + 2, context->entry_length); + if (context->comparison_result == 0) { + hit->heading.page + = eb_uint4(cache_p + context->entry_length + 8); + hit->heading.offset + = eb_uint2(cache_p + context->entry_length + 12); + hit->text.page + = eb_uint4(cache_p + context->entry_length + 2); + hit->text.offset + = eb_uint2(cache_p + context->entry_length + 6); + hit++; + *hit_count += 1; + } + context->in_group_entry = 0; + context->offset += context->entry_length + 14; + cache_p += context->entry_length + 14; + + } else if (group_id == 0x80) { + /* + * 0x80 -- Start of group entry. + */ + context->entry_length = eb_uint1(cache_p + 1); + if (EB_SIZE_PAGE + < context->offset + context->entry_length + 4) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + context->comparison_result + = context->compare_single(context->canonicalized_word, + cache_p + 4, context->entry_length); + context->in_group_entry = 1; + cache_p += context->entry_length + 4; + context->offset += context->entry_length + 4; + + } else if (group_id == 0xc0) { + /* + * Element of the group entry + */ + context->entry_length = eb_uint1(cache_p + 1); + if (EB_SIZE_PAGE < context->offset + 14) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + /* + * Compare word and pattern. + * If matched, add it to a hit list. + */ + if (context->comparison_result == 0 + && context->in_group_entry + && context->compare_group(context->word, cache_p + 2, + context->entry_length) == 0) { + hit->heading.page + = eb_uint4(cache_p + context->entry_length + 8); + hit->heading.offset + = eb_uint2(cache_p + context->entry_length + 12); + hit->text.page + = eb_uint4(cache_p + context->entry_length + 2); + hit->text.offset + = eb_uint2(cache_p + context->entry_length + 6); + hit++; + *hit_count += 1; + } + context->offset += context->entry_length + 14; + cache_p += context->entry_length + 14; + + } else { + /* + * Unknown group ID. + */ + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + context->entry_index++; + if (context->comparison_result < 0 + || max_hit_count <= *hit_count) + goto succeeded; + } + } + + /* + * Go to a next page if available. + */ + if (PAGE_ID_IS_LAYER_END(context->page_id)) { + context->comparison_result = -1; + goto succeeded; + } + context->page++; + context->entry_index = 0; + } + + succeeded: + LOG(("out: eb_hit_list_word(hit_count=%d) = %s", + *hit_count, eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + * Discard cache if read error occurs. + */ + failed: + if (error_code == EB_ERR_FAIL_READ_TEXT) + cache_book_code = EB_BOOK_NONE; + *hit_count = 0; + LOG(("out: eb_hit_list_word() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Get hit entries of a submitted keyword search request. + */ +static EB_Error_Code +eb_hit_list_keyword(EB_Book *book, EB_Search_Context *context, + int max_hit_count, EB_Hit *hit_list, int *hit_count) +{ + EB_Error_Code error_code; + EB_Text_Context text_context; + EB_Hit *hit; + int group_id; + char *cache_p; + + LOG(("in: eb_hit_list_keyword(book=%d, max_hit_count=%d)", + (int)book->code, max_hit_count)); + + hit = hit_list; + *hit_count = 0; + + /* + * Backup the text context in `book'. + */ + memcpy(&text_context, &book->text_context, sizeof(EB_Text_Context)); + + /* + * Seek text file. + */ + if (context->in_group_entry && context->comparison_result == 0) { + error_code = eb_seek_text(book, &context->keyword_heading); + if (error_code != EB_SUCCESS) + goto failed; + } + + /* + * If the result of previous comparison is negative value, all + * matched entries have been found. + */ + if (context->comparison_result < 0 || max_hit_count <= 0) + goto succeeded; + + for (;;) { + /* + * Read a page to search, if the page is not on the cache buffer. + * + * Cache may be missed by the two reasons: + * 1. the search process reaches to the end of an index page, + * and tries to read the next page. + * 2. Someone else used the cache buffer. + * + * At the case of 1, the search process reads the page and update + * the search context. At the case of 2. it reads the page but + * must not update the context! + */ + if (cache_book_code != book->code || cache_page != context->page) { + if (zio_lseek(&book->subbook_current->text_zio, + ((off_t) context->page - 1) * EB_SIZE_PAGE, SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_TEXT; + goto failed; + } + if (zio_read(&book->subbook_current->text_zio, cache_buffer, + EB_SIZE_PAGE) != EB_SIZE_PAGE) { + error_code = EB_ERR_FAIL_READ_TEXT; + goto failed; + } + + /* + * Update search context. + */ + if (context->entry_index == 0) { + context->page_id = eb_uint1(cache_buffer); + context->entry_length = eb_uint1(cache_buffer + 1); + if (context->entry_length == 0) + context->entry_arrangement = EB_ARRANGE_VARIABLE; + else + context->entry_arrangement = EB_ARRANGE_FIXED; + context->entry_count = eb_uint2(cache_buffer + 2); + context->entry_index = 0; + context->offset = 4; + } + + cache_book_code = book->code; + cache_page = context->page; + } + + cache_p = cache_buffer + context->offset; + + LOG(("aux: eb_hit_list_keyword(page=%d, page_id=0x%02x, \ +entry_length=%d, entry_arrangement=%d, entry_count=%d)", + context->page, context->page_id, context->entry_length, + context->entry_arrangement, context->entry_count)); + + if (!PAGE_ID_IS_LEAF_LAYER(context->page_id)) { + /* + * Not a leaf index. It is an error. + */ + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + if (!PAGE_ID_HAVE_GROUP_ENTRY(context->page_id) + && context->entry_arrangement == EB_ARRANGE_FIXED) { + /* + * The leaf index doesn't have a group entry. + * Find text and heading locations. + */ + while (context->entry_index < context->entry_count) { + if (EB_SIZE_PAGE + < context->offset + context->entry_length + 12) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + /* + * Compare word and pattern. + * If matched, add it to a hit list. + */ + context->comparison_result + = context->compare_single(context->word, cache_p, + context->entry_length); + if (context->comparison_result == 0) { + hit->heading.page + = eb_uint4(cache_p + context->entry_length + 6); + hit->heading.offset + = eb_uint2(cache_p + context->entry_length + 10); + hit->text.page + = eb_uint4(cache_p + context->entry_length); + hit->text.offset + = eb_uint2(cache_p + context->entry_length + 4); + hit++; + *hit_count += 1; + } + context->entry_index++; + context->offset += context->entry_length + 12; + cache_p += context->entry_length + 12; + + if (context->comparison_result < 0 + || max_hit_count <= *hit_count) + goto succeeded; + } + + } else if (!PAGE_ID_HAVE_GROUP_ENTRY(context->page_id) + && context->entry_arrangement == EB_ARRANGE_VARIABLE) { + /* + * The leaf index doesn't have a group entry. + * Find text and heading locations. + */ + while (context->entry_index < context->entry_count) { + if (EB_SIZE_PAGE < context->offset + 1) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + context->entry_length = eb_uint1(cache_p); + if (EB_SIZE_PAGE + < context->offset + context->entry_length + 13) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + /* + * Compare word and pattern. + * If matched, add it to a hit list. + */ + context->comparison_result + = context->compare_single(context->word, cache_p + 1, + context->entry_length); + if (context->comparison_result == 0) { + hit->heading.page + = eb_uint4(cache_p + context->entry_length + 7); + hit->heading.offset + = eb_uint2(cache_p + context->entry_length + 11); + hit->text.page + = eb_uint4(cache_p + context->entry_length + 1); + hit->text.offset + = eb_uint2(cache_p + context->entry_length + 5); + hit++; + *hit_count += 1; + } + context->entry_index++; + context->offset += context->entry_length + 13; + cache_p += context->entry_length + 13; + + if (context->comparison_result < 0 + || max_hit_count <= *hit_count) + goto succeeded; + } + + } else { + /* + * The leaf index have a group entry. + * Find text and heading locations. + */ + while (context->entry_index < context->entry_count) { + if (EB_SIZE_PAGE < context->offset + 2) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + group_id = eb_uint1(cache_p); + + if (group_id == 0x00) { + /* + * 0x00 -- Single entry. + */ + context->entry_length = eb_uint1(cache_p + 1); + if (EB_SIZE_PAGE + < context->offset + context->entry_length + 14) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + /* + * Compare word and pattern. + * If matched, add it to a hit list. + */ + context->comparison_result + = context->compare_single(context->canonicalized_word, + cache_p + 2, context->entry_length); + if (context->comparison_result == 0) { + hit->heading.page + = eb_uint4(cache_p + context->entry_length + 8); + hit->heading.offset + = eb_uint2(cache_p + context->entry_length + 12); + hit->text.page + = eb_uint4(cache_p + context->entry_length + 2); + hit->text.offset + = eb_uint2(cache_p + context->entry_length + 6); + hit++; + *hit_count += 1; + } + context->in_group_entry = 0; + context->offset += context->entry_length + 14; + cache_p += context->entry_length + 14; + + } else if (group_id == 0x80) { + /* + * 0x80 -- Start of group entry. + */ + context->entry_length = eb_uint1(cache_p + 1); + if (EB_SIZE_PAGE + < context->offset + context->entry_length + 12) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + context->comparison_result + = context->compare_single(context->word, cache_p + 6, + context->entry_length); + context->keyword_heading.page + = eb_uint4(cache_p + context->entry_length + 6); + context->keyword_heading.offset + = eb_uint2(cache_p + context->entry_length + 10); + context->in_group_entry = 1; + cache_p += context->entry_length + 12; + context->offset += context->entry_length + 12; + + if (context->comparison_result == 0) { + error_code + = eb_seek_text(book, &context->keyword_heading); + if (error_code != EB_SUCCESS) + goto failed; + } + + } else if (group_id == 0xc0) { + /* + * Element of the group entry. + */ + if (EB_SIZE_PAGE < context->offset + 7) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + /* + * Compare word and pattern. + * If matched, add it to a hit list. + */ + if (context->in_group_entry + && context->comparison_result == 0) { + error_code + = eb_tell_text(book, &context->keyword_heading); + if (error_code != EB_SUCCESS) + goto failed; + hit->heading.page = context->keyword_heading.page; + hit->heading.offset = context->keyword_heading.offset; + hit->text.page = eb_uint4(cache_p + 1); + hit->text.offset = eb_uint2(cache_p + 5); + hit++; + *hit_count += 1; + error_code = eb_forward_heading(book); + if (error_code != EB_SUCCESS) + goto failed; + } + context->offset += 7; + cache_p += 7; + + } else { + /* + * Unknown group ID. + */ + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + context->entry_index++; + if (context->comparison_result < 0 + || max_hit_count <= *hit_count) + goto succeeded; + } + } + + /* + * Go to a next page if available. + */ + if (PAGE_ID_IS_LAYER_END(context->page_id)) { + context->comparison_result = -1; + goto succeeded; + } + context->page++; + context->entry_index = 0; + } + + succeeded: + if (context->in_group_entry && context->comparison_result == 0) { + error_code = eb_tell_text(book, &context->keyword_heading); + if (error_code != EB_SUCCESS) + goto failed; + } + + /* + * Restore the text context in `book'. + */ + memcpy(&book->text_context, &text_context, sizeof(EB_Text_Context)); + LOG(("out: eb_hit_list_keyword(hit_count=%d) = %s", + *hit_count, eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + * Discard cache if read error occurs. + */ + failed: + if (error_code == EB_ERR_FAIL_READ_TEXT) + cache_book_code = EB_BOOK_NONE; + *hit_count = 0; + memcpy(&book->text_context, &text_context, sizeof(EB_Text_Context)); + LOG(("out: eb_hit_list_keyword() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Get hit entries of a submitted multi search request. + */ +static EB_Error_Code +eb_hit_list_multi(EB_Book *book, EB_Search_Context *context, int max_hit_count, + EB_Hit *hit_list, int *hit_count) +{ + EB_Error_Code error_code; + EB_Hit *hit; + int group_id; + char *cache_p; + + LOG(("in: eb_hit_list_multi(book=%d, max_hit_count=%d)", (int)book->code, + max_hit_count)); + + hit = hit_list; + *hit_count = 0; + + /* + * If the result of previous comparison is negative value, all + * matched entries have been found. + */ + if (context->comparison_result < 0 || max_hit_count <= 0) + goto succeeded; + + for (;;) { + /* + * Read a page to search, if the page is not on the cache buffer. + * + * Cache may be missed by the two reasons: + * 1. the search process reaches to the end of an index page, + * and tries to read the next page. + * 2. Someone else used the cache buffer. + * + * At the case of 1, the search process reads the page and update + * the search context. At the case of 2. it reads the page but + * must not update the context! + */ + if (cache_book_code != book->code || cache_page != context->page) { + if (zio_lseek(&book->subbook_current->text_zio, + ((off_t) context->page - 1) * EB_SIZE_PAGE, SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_TEXT; + goto failed; + } + if (zio_read(&book->subbook_current->text_zio, cache_buffer, + EB_SIZE_PAGE) != EB_SIZE_PAGE) { + error_code = EB_ERR_FAIL_READ_TEXT; + goto failed; + } + + /* + * Update search context. + */ + if (context->entry_index == 0) { + context->page_id = eb_uint1(cache_buffer); + context->entry_length = eb_uint1(cache_buffer + 1); + if (context->entry_length == 0) + context->entry_arrangement = EB_ARRANGE_VARIABLE; + else + context->entry_arrangement = EB_ARRANGE_FIXED; + context->entry_count = eb_uint2(cache_buffer + 2); + context->entry_index = 0; + context->offset = 4; + } + + cache_book_code = book->code; + cache_page = context->page; + } + + cache_p = cache_buffer + context->offset; + + LOG(("aux: eb_hit_list_multi(page=%d, page_id=0x%02x, \ +entry_length=%d, entry_arrangement=%d, entry_count=%d)", + context->page, context->page_id, context->entry_length, + context->entry_arrangement, context->entry_count)); + + if (!PAGE_ID_IS_LEAF_LAYER(context->page_id)) { + /* + * Not a leaf index. It is an error. + */ + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + if (!PAGE_ID_HAVE_GROUP_ENTRY(context->page_id) + && context->entry_arrangement == EB_ARRANGE_FIXED) { + /* + * The leaf index doesn't have a group entry. + * Find text and heading locations. + */ + while (context->entry_index < context->entry_count) { + if (EB_SIZE_PAGE + < context->offset + context->entry_length + 13) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + /* + * Compare word and pattern. + * If matched, add it to a hit list. + */ + context->comparison_result + = context->compare_single(context->word, cache_p, + context->entry_length); + if (context->comparison_result == 0) { + hit->heading.page + = eb_uint4(cache_p + context->entry_length + 6); + hit->heading.offset + = eb_uint2(cache_p + context->entry_length + 10); + hit->text.page + = eb_uint4(cache_p + context->entry_length); + hit->text.offset + = eb_uint2(cache_p + context->entry_length + 4); + hit++; + *hit_count += 1; + } + context->entry_index++; + context->offset += context->entry_length + 12; + cache_p += context->entry_length + 12; + + if (context->comparison_result < 0 + || max_hit_count <= *hit_count) + goto succeeded; + } + + } else if (!PAGE_ID_HAVE_GROUP_ENTRY(context->page_id) + && context->entry_arrangement == EB_ARRANGE_VARIABLE) { + /* + * The leaf index doesn't have a group entry. + * Find text and heading locations. + */ + while (context->entry_index < context->entry_count) { + if (EB_SIZE_PAGE < context->offset + 1) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + context->entry_length = eb_uint1(cache_p); + if (EB_SIZE_PAGE + < context->offset + context->entry_length + 13) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + /* + * Compare word and pattern. + * If matched, add it to a hit list. + */ + context->comparison_result + = context->compare_single(context->word, cache_p + 1, + context->entry_length); + if (context->comparison_result == 0) { + hit->heading.page + = eb_uint4(cache_p + context->entry_length + 7); + hit->heading.offset + = eb_uint2(cache_p + context->entry_length + 11); + hit->text.page + = eb_uint4(cache_p + context->entry_length + 1); + hit->text.offset + = eb_uint2(cache_p + context->entry_length + 5); + hit++; + *hit_count += 1; + } + context->entry_index++; + context->offset += context->entry_length + 13; + cache_p += context->entry_length + 13; + + if (context->comparison_result < 0 + || max_hit_count <= *hit_count) + goto succeeded; + } + + } else { + /* + * The leaf index have a group entry. + * Find text and heading locations. + */ + while (context->entry_index < context->entry_count) { + if (EB_SIZE_PAGE < context->offset + 2) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + group_id = eb_uint1(cache_p); + + if (group_id == 0x00) { + /* + * 0x00 -- Single entry. + */ + context->entry_length = eb_uint1(cache_p + 1); + if (EB_SIZE_PAGE + < context->offset + context->entry_length + 14) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + /* + * Compare word and pattern. + * If matched, add it to a hit list. + */ + context->comparison_result + = context->compare_single(context->canonicalized_word, + cache_p + 2, context->entry_length); + if (context->comparison_result == 0) { + hit->heading.page + = eb_uint4(cache_p + context->entry_length + 8); + hit->heading.offset + = eb_uint2(cache_p + context->entry_length + 12); + hit->text.page + = eb_uint4(cache_p + context->entry_length + 2); + hit->text.offset + = eb_uint2(cache_p + context->entry_length + 6); + hit++; + *hit_count += 1; + } + context->in_group_entry = 0; + context->offset += context->entry_length + 14; + cache_p += context->entry_length + 14; + + } else if (group_id == 0x80) { + /* + * 0x80 -- Start of group entry. + */ + context->entry_length = eb_uint1(cache_p + 1); + if (EB_SIZE_PAGE + < context->offset + context->entry_length + 6) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + context->comparison_result + = context->compare_single(context->word, cache_p + 6, + context->entry_length); + context->in_group_entry = 1; + cache_p += context->entry_length + 6; + context->offset += context->entry_length + 6; + + } else if (group_id == 0xc0) { + /* + * Element of the group entry. + */ + if (EB_SIZE_PAGE < context->offset + 13) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + /* + * Compare word and pattern. + * If matched, add it to a hit list. + */ + if (context->in_group_entry + && context->comparison_result == 0) { + hit->heading.page = eb_uint4(cache_p + 7); + hit->heading.offset = eb_uint2(cache_p + 11); + hit->text.page = eb_uint4(cache_p + 1); + hit->text.offset = eb_uint2(cache_p + 5); + hit++; + *hit_count += 1; + } + context->offset += 13; + cache_p += 13; + + } else { + /* + * Unknown group ID. + */ + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + context->entry_index++; + if (context->comparison_result < 0 + || max_hit_count <= *hit_count) + goto succeeded; + } + } + + /* + * Go to a next page if available. + */ + if (PAGE_ID_IS_LAYER_END(context->page_id)) { + context->comparison_result = -1; + goto succeeded; + } + context->page++; + context->entry_index = 0; + } + + succeeded: + LOG(("out: eb_hit_list_multi(hit_count=%d) = %s", + *hit_count, eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + * Discard cache if read error occurs. + */ + failed: + if (error_code == EB_ERR_FAIL_READ_TEXT) + cache_book_code = EB_BOOK_NONE; + *hit_count = 0; + LOG(("out: eb_hit_list_multi() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Do AND operation of hit lists. + * and_list = hit_lists[0] AND hit_lists[1] AND ... + */ +static void +eb_and_hit_lists(EB_Hit and_list[EB_TMP_MAX_HITS], int *and_count, + int max_and_count, int hit_list_count, + EB_Hit hit_lists[EB_NUMBER_OF_SEARCH_CONTEXTS][EB_TMP_MAX_HITS], + int hit_counts[EB_NUMBER_OF_SEARCH_CONTEXTS]) +{ + int hit_indexes[EB_NUMBER_OF_SEARCH_CONTEXTS]; + int greatest_list; + int greatest_page; + int greatest_offset; + int current_page; + int current_offset; + int equal_count; + int increment_count; + int i; + + LOG(("in: eb_and_hit_lists(max_and_count=%d, hit_list_count=%d)", + max_and_count, hit_list_count)); + + /* + * Initialize indexes for the hit_lists[]. + */ + for (i = 0; i < hit_list_count; i++) + hit_indexes[i] = 0; + + /* + * Generate the new list `and_list'. + */ + *and_count = 0; + while (*and_count < max_and_count) { + /* + * Initialize variables. + */ + greatest_list = -1; + greatest_page = 0; + greatest_offset = 0; + current_page = 0; + current_offset = 0; + equal_count = 0; + + /* + * Compare the current elements of the lists. + */ + for (i = 0; i < hit_list_count; i++) { + /* + * If we have been reached to the tail of the hit_lists[i], + * skip the list. + */ + if (hit_counts[i] <= hit_indexes[i]) + continue; + + /* + * Compare {current_page, current_offset} and {greatest_page, + * greatest_offset}. + */ + current_page = hit_lists[i][hit_indexes[i]].text.page; + current_offset = hit_lists[i][hit_indexes[i]].text.offset; + + if (greatest_list == -1) { + greatest_page = current_page; + greatest_offset = current_offset; + greatest_list = i; + equal_count++; + } else if (greatest_page < current_page) { + greatest_page = current_page; + greatest_offset = current_offset; + greatest_list = i; + } else if (current_page == greatest_page + && greatest_offset < current_offset) { + greatest_page = current_page; + greatest_offset = current_offset; + greatest_list = i; + } else if (current_page == greatest_page + && current_offset == greatest_offset) { + equal_count++; + } + } + + if (equal_count == hit_list_count) { + /* + * All the current elements of the lists point to the same + * position. This is hit element. Increase indexes of all + * lists. + */ + memcpy(and_list + *and_count, hit_lists[0] + hit_indexes[0], + sizeof(EB_Hit)); + *and_count += 1; + for (i = 0; i < hit_list_count; i++) { + if (hit_counts[i] <= hit_indexes[i]) + continue; + hit_indexes[i]++; + } + } else { + /* + * This is not hit element. Increase indexes of all lists + * except for greatest element(s). If there is no list + * whose index is incremented, our job has been completed. + */ + increment_count = 0; + for (i = 0; i < hit_list_count; i++) { + if (hit_counts[i] <= hit_indexes[i]) + continue; + current_page = hit_lists[i][hit_indexes[i]].text.page; + current_offset = hit_lists[i][hit_indexes[i]].text.offset; + if (current_page != greatest_page + || current_offset != greatest_offset) { + hit_indexes[i]++; + increment_count++; + } + } + if (increment_count == 0) + break; + } + } + + /* + * Update hit_counts[]. + * The hit counts of the lists are set to the current indexes. + */ + for (i = 0; i < hit_list_count; i++) + hit_counts[i] = hit_indexes[i]; + + LOG(("out: eb_and_hit_lists(and_count=%d)", *and_count)); +} diff --git a/setword.c b/setword.c new file mode 100644 index 0000000..c59c90c --- /dev/null +++ b/setword.c @@ -0,0 +1,1310 @@ +/* + * 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 "build-post.h" + +/* + * Unexported functions. + */ +static void eb_fix_word(EB_Book *book, const EB_Search *search, char *word, + char *canonicalized_word); +static EB_Error_Code eb_convert_latin(EB_Book *book, const char *input_word, + char *word, EB_Word_Code *word_code); +static EB_Error_Code eb_convert_euc_jp(EB_Book *book, const char *input_word, + char *word, EB_Word_Code *word_code); +static void eb_convert_katakana_jis(char *word); +static void eb_convert_hiragana_jis(char *word); +static void eb_convert_lower_latin(char *word); +static void eb_convert_lower_jis(char *word); +static void eb_delete_marks_jis(char *word); +static void eb_convert_long_vowels_jis(char *word); +static void eb_delete_long_vowels_jis(char *word); +static void eb_convert_double_consonants_jis(char *word); +static void eb_convert_contracted_sounds_jis(char *word); +static void eb_convert_small_vowels_jis(char *word); +static void eb_convert_voiced_consonants_jis(char *word); +static void eb_convert_p_sounds_jis(char *word); +static void eb_delete_spaces_latin(char *word); +static void eb_delete_spaces_jis(char *word); +static void eb_reverse_word_latin(char *word); +static void eb_reverse_word_jis(char *word); + + +/* + * Make a fixed word and a cannonicalized word for `WORD SEARCH'. + * + * If `inputword' is a KANA word, EB_WORD_KANA is returned. + * If `inputword' is a alphabetic word, EB_WORD_ALPHABET is returned. + * Otherwise, -1 is returned. It means that an error occurs. + */ +EB_Error_Code +eb_set_word(EB_Book *book, const char *input_word, char *word, + char *canonicalized_word, EB_Word_Code *word_code) +{ + EB_Error_Code error_code; + const EB_Search *search; + + LOG(("in: eb_set_word(book=%d, input_word=%s)", (int)book->code, + eb_quoted_string(input_word))); + + /* + * Make a fixed word and a canonicalized word from `input_word'. + */ + if (book->character_code == EB_CHARCODE_ISO8859_1) + error_code = eb_convert_latin(book, input_word, word, word_code); + else + error_code = eb_convert_euc_jp(book, input_word, word, word_code); + if (error_code != EB_SUCCESS) + goto failed; + strcpy(canonicalized_word, word); + + /* + * Determine search method. + */ + switch (*word_code) { + case EB_WORD_ALPHABET: + if (book->subbook_current->word_alphabet.start_page != 0) + search = &book->subbook_current->word_alphabet; + else if (book->subbook_current->word_asis.start_page != 0) + search = &book->subbook_current->word_asis; + else { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + break; + + case EB_WORD_KANA: + if (book->subbook_current->word_kana.start_page != 0) + search = &book->subbook_current->word_kana; + else if (book->subbook_current->word_asis.start_page != 0) + search = &book->subbook_current->word_asis; + else { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + break; + + case EB_WORD_OTHER: + if (book->subbook_current->word_asis.start_page != 0) + search = &book->subbook_current->word_asis; + else { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + break; + + default: + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + + /* + * Fix the word. + */ + eb_fix_word(book, search, word, canonicalized_word); + + LOG(("out: eb_set_word(word=%s, canonicalized_word=%s, word_code=%d) = %s", + eb_quoted_string(word), eb_quoted_string(canonicalized_word), + (int)*word_code, eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *word = '\0'; + *canonicalized_word = '\0'; + *word_code = EB_WORD_INVALID; + LOG(("out: eb_set_word() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Make a fixed word and a cannonicalized word for `ENDWORD SEARCH'. + * + * If `input_word' is a KANA word, EB_WORD_KANA is retuend. + * If `input_word' is a alphabetic word, EB_WORD_ALPHABET is retuend. + * Otherwise, -1 is returned. It means that an error occurs. + */ +EB_Error_Code +eb_set_endword(EB_Book *book, const char *input_word, char *word, + char *canonicalized_word, EB_Word_Code *word_code) +{ + EB_Error_Code error_code; + const EB_Search *search; + + LOG(("in: eb_set_endword(book=%d, input_word=%s)", (int)book->code, + eb_quoted_string(input_word))); + + /* + * Make a fixed word and a canonicalized word from `input_word'. + */ + if (book->character_code == EB_CHARCODE_ISO8859_1) + error_code = eb_convert_latin(book, input_word, word, word_code); + else + error_code = eb_convert_euc_jp(book, input_word, word, word_code); + if (error_code != EB_SUCCESS) + goto failed; + strcpy(canonicalized_word, word); + + /* + * Determine search method. + */ + switch (*word_code) { + case EB_WORD_ALPHABET: + if (book->subbook_current->endword_alphabet.start_page != 0) + search = &book->subbook_current->endword_alphabet; + else if (book->subbook_current->endword_asis.start_page != 0) + search = &book->subbook_current->endword_asis; + else { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + break; + + case EB_WORD_KANA: + if (book->subbook_current->endword_kana.start_page != 0) + search = &book->subbook_current->endword_kana; + else if (book->subbook_current->endword_asis.start_page != 0) + search = &book->subbook_current->endword_asis; + else { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + break; + + case EB_WORD_OTHER: + if (book->subbook_current->endword_asis.start_page != 0) + search = &book->subbook_current->endword_asis; + else { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + break; + + default: + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + + /* + * Fix the word. + */ + eb_fix_word(book, search, word, canonicalized_word); + + /* + * Reverse the word. + */ + if (book->character_code == EB_CHARCODE_ISO8859_1) { + eb_reverse_word_latin(word); + eb_reverse_word_latin(canonicalized_word); + } else { + eb_reverse_word_jis(word); + eb_reverse_word_jis(canonicalized_word); + } + + LOG(("out: eb_set_endword(word=%s, canonicalized_word=%s, word_code=%d) \ += %s", + eb_quoted_string(word), eb_quoted_string(canonicalized_word), + (int)*word_code, eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *word = '\0'; + *canonicalized_word = '\0'; + *word_code = EB_WORD_INVALID; + LOG(("out: eb_set_endword() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Make a fixed word and a cannonicalized word for `KEYWORD SEARCH' + * or `CROSS SEARCH'. + * + * If `inputword' is a KANA word, EB_WORD_KANA is returned. + * If `inputword' is a alphabetic word, EB_WORD_ALPHABET is returned. + * Otherwise, -1 is returned. It means that an error occurs. + */ +EB_Error_Code +eb_set_keyword(EB_Book *book, const char *input_word, char *word, + char *canonicalized_word, EB_Word_Code *word_code) +{ + EB_Error_Code error_code; + + LOG(("in: eb_set_keyword(book=%d, input_word=%s)", (int)book->code, + eb_quoted_string(input_word))); + + /* + * Make a fixed word and a canonicalized word from `input_word'. + */ + if (book->character_code == EB_CHARCODE_ISO8859_1) + error_code = eb_convert_latin(book, input_word, word, word_code); + else + error_code = eb_convert_euc_jp(book, input_word, word, word_code); + if (error_code != EB_SUCCESS) + goto failed; + strcpy(canonicalized_word, word); + + /* + * Fix the word. + */ + eb_fix_word(book, &book->subbook_current->keyword, word, + canonicalized_word); + + LOG(("out: eb_set_keyword(word=%s, canonicalized_word=%s, word_code=%d) \ += %s", + eb_quoted_string(word), eb_quoted_string(canonicalized_word), + (int)*word_code, eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *word = '\0'; + *canonicalized_word = '\0'; + *word_code = EB_WORD_INVALID; + LOG(("out: eb_set_keyword() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Make a fixed word and a cannonicalized word for `MULTI SEARCH'. + * + * If `inputword' is a KANA word, EB_WORD_KANA is returned. + * If `inputword' is a alphabetic word, EB_WORD_ALPHABET is returned. + * Otherwise, -1 is returned. It means that an error occurs. + */ +EB_Error_Code +eb_set_multiword(EB_Book *book, EB_Multi_Search_Code multi_id, + EB_Multi_Entry_Code entry_id, const char *input_word, char *word, + char *canonicalized_word, EB_Word_Code *word_code) +{ + EB_Error_Code error_code; + EB_Search *search; + + LOG(("in: eb_set_multiword(book=%d, input_word=%s)", (int)book->code, + eb_quoted_string(input_word))); + + /* + * Make a fixed word and a canonicalized word from `input_word'. + */ + if (book->character_code == EB_CHARCODE_ISO8859_1) + error_code = eb_convert_latin(book, input_word, word, word_code); + else + error_code = eb_convert_euc_jp(book, input_word, word, word_code); + if (error_code != EB_SUCCESS) + goto failed; + strcpy(canonicalized_word, word); + + /* + * Fix the word. + */ + search = &book->subbook_current->multis[multi_id].entries[entry_id]; + eb_fix_word(book, search, word, canonicalized_word); + + LOG(("out: eb_set_multiword(word=%s, canonicalized_word=%s, word_code=%d) \ += %s", + eb_quoted_string(word), eb_quoted_string(canonicalized_word), + (int)*word_code, eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *word = '\0'; + *canonicalized_word = '\0'; + *word_code = EB_WORD_INVALID; + LOG(("out: eb_set_multiword() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Fix `canonicalized_word' and `word' according with `book->character_code' + * and `search'. + */ +static void +eb_fix_word(EB_Book *book, const EB_Search *search, char *word, + char *canonicalized_word) +{ + LOG(("in: eb_fix_word(book=%d, word=%s, canonicalized_word=%s)", + (int)book->code, eb_quoted_string(word), + eb_quoted_string(canonicalized_word))); + + if (search->index_id == 0xa1 && search->candidates_page != 0) + return; + + if (book->character_code == EB_CHARCODE_ISO8859_1) { + if (search->space == EB_INDEX_STYLE_DELETE) + eb_delete_spaces_latin(canonicalized_word); + + if (search->lower == EB_INDEX_STYLE_CONVERT) + eb_convert_lower_latin(canonicalized_word); + + } else { + if (search->space == EB_INDEX_STYLE_DELETE) + eb_delete_spaces_jis(canonicalized_word); + + if (search->katakana == EB_INDEX_STYLE_CONVERT) + eb_convert_katakana_jis(canonicalized_word); + else if (search->katakana == EB_INDEX_STYLE_REVERSED_CONVERT) + eb_convert_hiragana_jis(canonicalized_word); + + if (search->lower == EB_INDEX_STYLE_CONVERT) + eb_convert_lower_jis(canonicalized_word); + + if (search->mark == EB_INDEX_STYLE_DELETE) + eb_delete_marks_jis(canonicalized_word); + + if (search->long_vowel == EB_INDEX_STYLE_CONVERT) + eb_convert_long_vowels_jis(canonicalized_word); + else if (search->long_vowel == EB_INDEX_STYLE_DELETE) + eb_delete_long_vowels_jis(canonicalized_word); + + if (search->double_consonant == EB_INDEX_STYLE_CONVERT) + eb_convert_double_consonants_jis(canonicalized_word); + + if (search->contracted_sound == EB_INDEX_STYLE_CONVERT) + eb_convert_contracted_sounds_jis(canonicalized_word); + + if (search->small_vowel == EB_INDEX_STYLE_CONVERT) + eb_convert_small_vowels_jis(canonicalized_word); + + if (search->voiced_consonant == EB_INDEX_STYLE_CONVERT) + eb_convert_voiced_consonants_jis(canonicalized_word); + + if (search->p_sound == EB_INDEX_STYLE_CONVERT) + eb_convert_p_sounds_jis(canonicalized_word); + } + + if (search->index_id != 0x70 && search->index_id != 0x90) + strcpy(word, canonicalized_word); + + LOG(("out: eb_fix_word(word=%s, canonicalized_word=%s)", + eb_quoted_string(word), eb_quoted_string(canonicalized_word))); +} + + +/* + * Convert `input_word' to ISO 8859 1 and put it into `word'. + * + * If `input_word' is a valid string to search, EB_WORD_ALPHABET is returned. + * Otherwise, -1 is returned. + */ +static EB_Error_Code +eb_convert_latin(EB_Book *book, const char *input_word, char *word, + EB_Word_Code *word_code) +{ + EB_Error_Code error_code; + unsigned char *wp = (unsigned char *) word; + const unsigned char *inp = (const unsigned char *) input_word; + const unsigned char *tail; + unsigned char c1; + int word_length = 0; + + LOG(("in: eb_convert_latin(book=%d, input_word=%s)", (int)book->code, + eb_quoted_string(input_word))); + + /* + * Find the tail of `input_word'. + */ + tail = (const unsigned char *) input_word + strlen(input_word) - 1; + while ((const unsigned char *)input_word <= tail + && (*tail == ' ' || *tail == '\t')) + tail--; + tail++; + + /* + * Ignore spaces and tabs in the beginning of `input_word'. + */ + while (*inp == ' ' || *inp == '\t') + inp++; + + while (inp < tail) { + /* + * Check for the length of the word. + * If exceeds, return with an error code. + */ + if (EB_MAX_WORD_LENGTH < word_length + 1) { + error_code = EB_ERR_TOO_LONG_WORD; + goto failed; + } + + c1 = *inp++; + + /* + * Tabs are translated to spaces. + */ + if (c1 == '\t') + c1 = ' '; + + *wp++ = c1; + + /* + * Skip successive spaces and tabs. + */ + if (c1 == ' ') { + while (*inp == '\t' || *inp == ' ') + inp++; + } + + word_length++; + } + *wp = '\0'; + + if (word_length == 0) { + error_code = EB_ERR_EMPTY_WORD; + goto failed; + } + *word_code = EB_WORD_ALPHABET; + + LOG(("out: eb_convert_latin(word=%s, word_code=%d) = %s", + eb_quoted_string(word), (int)*word_code, eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *word = '\0'; + *word_code = EB_WORD_INVALID; + LOG(("out: eb_convert_latin() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Table used to convert JIS X 0208 to ASCII. + */ +static const unsigned int jisx0208_table[] = { + /* 0x20 -- 0x2f */ + 0x2121, 0x212a, 0x2149, 0x2174, 0x2170, 0x2173, 0x2175, 0x2147, + 0x214a, 0x214b, 0x2176, 0x215c, 0x2124, 0x215d, 0x2125, 0x213f, + /* 0x30 -- 0x3f */ + 0x2330, 0x2331, 0x2332, 0x2333, 0x2334, 0x2335, 0x2336, 0x2337, + 0x2338, 0x2339, 0x2127, 0x2128, 0x2163, 0x2161, 0x2164, 0x2129, + /* 0x40 -- 0x4f */ + 0x2177, 0x2341, 0x2342, 0x2343, 0x2344, 0x2345, 0x2346, 0x2347, + 0x2348, 0x2349, 0x234a, 0x234b, 0x234c, 0x234d, 0x234e, 0x234f, + /* 0x50 -- 0x5f */ + 0x2350, 0x2351, 0x2352, 0x2353, 0x2354, 0x2355, 0x2356, 0x2357, + 0x2358, 0x2359, 0x235a, 0x214e, 0x2140, 0x214f, 0x2130, 0x2132, + /* 0x60 -- 0x6f */ + 0x2146, 0x2361, 0x2362, 0x2363, 0x2364, 0x2365, 0x2366, 0x2367, + 0x2368, 0x2369, 0x236a, 0x236b, 0x236c, 0x236d, 0x236e, 0x236f, + /* 0x70 -- 0x7e */ + 0x2370, 0x2371, 0x2372, 0x2373, 0x2374, 0x2375, 0x2376, 0x2377, + 0x2378, 0x2379, 0x237a, 0x2150, 0x2143, 0x2151, 0x2141 +}; + +/* + * Table used to convert JIS X 0201 KATAKANA to JIS X 0208. + */ +static const unsigned int jisx0201_table[] = { + /* 0xa0 -- 0xaf */ + 0x0000, 0x2123, 0x2156, 0x2157, 0x2122, 0x2126, 0x2572, 0x2521, + 0x2523, 0x2525, 0x2527, 0x2529, 0x2563, 0x2565, 0x2567, 0x2543, + /* 0xb0 -- 0xbf */ + 0x213c, 0x2522, 0x2524, 0x2526, 0x2528, 0x252a, 0x252b, 0x252d, + 0x252f, 0x2531, 0x2533, 0x2535, 0x2537, 0x2539, 0x253b, 0x253d, + /* 0xc0 -- 0xcf */ + 0x253f, 0x2541, 0x2544, 0x2546, 0x2548, 0x254a, 0x254b, 0x254c, + 0x254d, 0x254e, 0x254f, 0x2552, 0x2555, 0x2558, 0x255b, 0x255e, + /* 0xd0 -- 0xdf */ + 0x255f, 0x2560, 0x2561, 0x2562, 0x2564, 0x2566, 0x2568, 0x2569, + 0x256a, 0x256b, 0x256c, 0x256d, 0x256f, 0x2573, 0x212b, 0x212c +}; + +/* + * Convert `input_word' to JIS X0208 and put it into `word'. + * + * If `input_word' is a valid string to search, EB_WORD_ALPHABET or + * EB_WORD_KANA is returned. + * Otherwise, -1 is returned. + */ +static EB_Error_Code +eb_convert_euc_jp(EB_Book *book, const char *input_word, char *word, + EB_Word_Code *word_code) +{ + EB_Error_Code error_code; + unsigned char *wp = (unsigned char *) word; + const unsigned char *inp = (const unsigned char *) input_word; + const unsigned char *tail; + unsigned char c1 = 0, c2 = 0; + int kana_count = 0; + int alphabet_count = 0; + int kanji_count = 0; + int word_length = 0; + + LOG(("in: eb_convert_euc_jp(book=%d, input_word=%s)", (int)book->code, + eb_quoted_string(input_word))); + + /* + * Find the tail of `input_word'. + */ + tail = (const unsigned char *) input_word + strlen(input_word) - 1; + for (;;) { + if (inp < tail && (*tail == ' ' || *tail == '\t')) + tail--; + else if (inp < tail - 1 && *tail == 0xa1 && *(tail - 1) == 0xa1) + tail -= 2; + else + break; + } + tail++; + + /* + * Ignore spaces and tabs in the beginning of `input_word'. + */ + for (;;) { + if (*inp == ' ' || *inp == '\t') + inp++; + else if (*inp == 0xa1 && *(inp + 1) == 0xa1) + inp += 2; + else + break; + } + + while (inp < tail) { + /* + * Check for the length of the word. + * If exceeds, return with an error code. + */ + if (EB_MAX_WORD_LENGTH < word_length + 2) { + error_code = EB_ERR_TOO_LONG_WORD; + goto failed; + } + + /* + * Tabs are translated to spaces. + */ + c1 = *inp++; + if (c1 == '\t') + c1 = ' '; + + if (0x20 <= c1 && c1 <= 0x7e) { + /* + * `c1' is a character in ASCII. + */ + unsigned int c = jisx0208_table[c1 - 0x20]; + c1 = c >> 8; + c2 = c & 0xff; + } else if (0xa1 <= c1 && c1 <= 0xfe) { + /* + * `c1' is a character in JIS X 0208, or local character. + */ + c2 = *inp++; + + if (0xa1 <= c2 && c2 <= 0xfe) { + c1 &= 0x7f; + c2 &= 0x7f; + } else if (c2 < 0x20 || 0x7e < c2) { + error_code = EB_ERR_BAD_WORD; + goto failed; + } + } else if (c1 == 0x8e) { + /* + * `c1' is SS2. + */ + if (c2 < 0xa1 || 0xdf < c2) { + error_code = EB_ERR_BAD_WORD; + goto failed; + } + c2 = jisx0201_table[c2 - 0xa0]; + c1 = 0x25; + } else { + error_code = EB_ERR_BAD_WORD; + goto failed; + } + + /* + * The following characters are recognized as alphabet. + * 2330 - 2339: `0' .. `9' + * 2341 - 235a: `A' .. `Z' + * 2361 - 237a: `a' .. `z' (convert to upper cases) + */ + *wp++ = c1; + *wp++ = c2; + + if (c1 == 0x23) + alphabet_count++; + else if (c1 == 0x24 || c1 == 0x25) + kana_count++; + else if (c1 != 0x21) + kanji_count++; + + word_length += 2; + } + *wp = '\0'; + + if (word_length == 0) { + error_code = EB_ERR_EMPTY_WORD; + goto failed; + } + if (alphabet_count == 0 && kana_count != 0 && kanji_count == 0) + *word_code = EB_WORD_KANA; + else if (alphabet_count != 0 && kana_count == 0 && kanji_count == 0) + *word_code = EB_WORD_ALPHABET; + else + *word_code = EB_WORD_OTHER; + + LOG(("out: eb_convert_euc_jp(word=%s, word_code=%d) = %s", + eb_quoted_string(word), (int)*word_code, eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *word = '\0'; + *word_code = EB_WORD_INVALID; + LOG(("out: eb_convert_euc_jp() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Convert KATAKANA to HIRAGANA in `word'. + */ +static void +eb_convert_katakana_jis(char *word) +{ + unsigned char *wp = (unsigned char *) word; + unsigned char c1, c2; + + LOG(("in: eb_convert_katakana_jis(word=%s)", eb_quoted_string(word))); + + while (*wp != '\0' && *(wp + 1) != '\0') { + c1 = *wp; + c2 = *(wp + 1); + + if (c1 == 0x25 && 0x21 <= c2 && c2 <= 0x76) { + /* + * This is a KATAKANA. Convert to corresponding HIRAGANA. + */ + *wp = 0x24; + } + wp += 2; + } + *wp = '\0'; + + LOG(("out: eb_convert_katakana_jis()")); +} + + +/* + * Convert HIRAGANA to KATAKANA in `word'. + */ +static void +eb_convert_hiragana_jis(char *word) +{ + unsigned char *wp = (unsigned char *) word; + unsigned char c1, c2; + + LOG(("in: eb_convert_hiragana_jis(word=%s)", eb_quoted_string(word))); + + while (*wp != '\0' && *(wp + 1) != '\0') { + c1 = *wp; + c2 = *(wp + 1); + + if (c1 == 0x24 && 0x21 <= c2 && c2 <= 0x76) { + /* + * This is a HIRAGANA. Convert to corresponding KATAKANA. + */ + *wp = 0x25; + } + wp += 2; + } + *wp = '\0'; + + LOG(("out: eb_convert_hiragana_jis()")); +} + + +/* + * Convert lower case to upper case in `word'. + */ +static void +eb_convert_lower_latin(char *word) +{ + unsigned char *wp = (unsigned char *) word; + + LOG(("in: eb_convert_lower_latin(word=%s)", eb_quoted_string(word))); + + while (*wp != '\0') { + if (('a' <= *wp && *wp <= 'z') + || (0xe0 <= *wp && *wp <= 0xf6) || (0xf8 <= *wp && *wp <= 0xfe)) { + /* + * This is a lower case letter. Convert to upper case. + */ + *wp -= 0x20; + } + wp++; + } + *wp = '\0'; + + LOG(("out: eb_convert_lower_latin()")); +} + + +/* + * Convert lower case to upper case in `word'. + */ +static void +eb_convert_lower_jis(char *word) +{ + unsigned char *wp = (unsigned char *) word; + unsigned char c1, c2; + + LOG(("in: eb_convert_lower_jis(word=%s)", eb_quoted_string(word))); + + while (*wp != '\0' && *(wp + 1) != '\0') { + c1 = *wp; + c2 = *(wp + 1); + + if (c1 == 0x23 && 0x61 <= c2 && c2 <= 0x7a) { + /* + * This is a lower case letter. Convert to upper case. + */ + *(wp + 1) = c2 - 0x20; + } + wp += 2; + } + *wp = '\0'; + + LOG(("out: eb_convert_lower_jis()")); +} + + +/* + * Delete some marks in `word'. + */ +static void +eb_delete_marks_jis(char *word) +{ + unsigned char *in_wp = (unsigned char *) word; + unsigned char *out_wp = (unsigned char *) word; + unsigned char c1, c2; + + LOG(("in: eb_delete_marks_jis(word=%s)", eb_quoted_string(word))); + + while (*in_wp != '\0' && *(in_wp + 1) != '\0') { + c1 = *in_wp; + c2 = *(in_wp + 1); + + if (c1 != 0x21 + || (c2 != 0x26 && c2 != 0x3e && c2 != 0x47 && c2 != 0x5d)) { + /* + * This is not a character to be deleted. + */ + *out_wp = c1; + *(out_wp + 1) = c2; + out_wp += 2; + } + in_wp += 2; + } + *out_wp = '\0'; + + LOG(("out: eb_delete_marks_jis()")); +} + + +/* + * The table is used to convert long vowel marks. + */ +static const char long_vowel_table[] = { + 0x22, /* a(21) -> A(22) */ 0x22, /* A(22) -> A(22) */ + 0x24, /* i(23) -> I(24) */ 0x24, /* I(24) -> I(24) */ + 0x26, /* u(25) -> U(26) */ 0x26, /* U(26) -> U(26) */ + 0x28, /* e(27) -> E(28) */ 0x28, /* E(28) -> E(28) */ + 0x2a, /* o(29) -> O(2a) */ 0x2a, /* O(2a) -> O(2a) */ + 0x22, /* KA(2b) -> A(22) */ 0x22, /* GA(2c) -> A(22) */ + 0x24, /* KI(2d) -> I(24) */ 0x24, /* GI(2e) -> I(24) */ + 0x26, /* KU(2f) -> U(26) */ 0x26, /* GU(30) -> U(26) */ + 0x28, /* KE(31) -> E(28) */ 0x28, /* GE(32) -> E(28) */ + 0x2a, /* KO(33) -> O(2a) */ 0x2a, /* GO(34) -> O(2a) */ + 0x22, /* SA(35) -> A(22) */ 0x22, /* ZA(36) -> A(22) */ + 0x24, /* SI(37) -> I(24) */ 0x24, /* ZI(38) -> I(24) */ + 0x26, /* SU(39) -> U(26) */ 0x26, /* ZU(3a) -> U(26) */ + 0x28, /* SE(3b) -> E(28) */ 0x28, /* ZE(3c) -> E(28) */ + 0x2a, /* SO(3d) -> O(2a) */ 0x2a, /* ZO(3e) -> O(2a) */ + 0x22, /* TA(3f) -> A(22) */ 0x22, /* DA(40) -> A(22) */ + 0x24, /* TI(41) -> I(24) */ 0x24, /* DI(42) -> I(24) */ + 0x26, /* tu(43) -> U(26) */ 0x26, /* TU(44) -> U(26) */ + 0x26, /* DU(45) -> U(26) */ 0x28, /* TE(46) -> E(28) */ + 0x28, /* DE(47) -> E(28) */ 0x2a, /* TO(48) -> O(2a) */ + 0x2a, /* DO(49) -> O(2a) */ 0x22, /* NA(4a) -> A(22) */ + 0x24, /* NI(4b) -> I(24) */ 0x26, /* NU(4c) -> U(26) */ + 0x28, /* NE(4d) -> E(28) */ 0x2a, /* NO(4e) -> O(2a) */ + 0x22, /* HA(4f) -> A(22) */ 0x22, /* BA(50) -> A(22) */ + 0x22, /* PA(51) -> A(22) */ 0x24, /* HI(52) -> I(24) */ + 0x24, /* BI(53) -> I(24) */ 0x24, /* PI(54) -> I(24) */ + 0x26, /* HU(55) -> U(26) */ 0x26, /* BU(56) -> U(26) */ + 0x26, /* PU(57) -> U(26) */ 0x28, /* HE(58) -> E(28) */ + 0x28, /* BE(59) -> E(28) */ 0x28, /* PE(5a) -> E(28) */ + 0x2a, /* HO(5b) -> O(2a) */ 0x2a, /* BO(5c) -> O(2a) */ + 0x2a, /* PO(5d) -> O(2a) */ 0x22, /* MA(5e) -> A(22) */ + 0x24, /* MI(5f) -> I(24) */ 0x26, /* MU(60) -> U(26) */ + 0x28, /* ME(61) -> E(28) */ 0x2a, /* MO(62) -> O(2a) */ + 0x22, /* ya(63) -> A(22) */ 0x22, /* YA(64) -> A(22) */ + 0x26, /* yu(65) -> U(26) */ 0x26, /* YU(66) -> U(26) */ + 0x2a, /* yo(67) -> O(2a) */ 0x2a, /* YO(68) -> O(2a) */ + 0x22, /* RA(69) -> A(22) */ 0x24, /* RI(6a) -> I(24) */ + 0x26, /* RU(6b) -> U(26) */ 0x28, /* RE(6c) -> E(28) */ + 0x2a, /* RO(6d) -> O(2a) */ 0x22, /* wa(6e) -> A(22) */ + 0x22, /* WA(6f) -> A(22) */ 0x24, /* WI(70) -> I(24) */ + 0x28, /* WE(71) -> E(28) */ 0x2a, /* WO(72) -> O(2a) */ + 0x73, /* N (73) -> N(73) */ 0x26, /* VU(74) -> U(26) */ + 0x22, /* ka(75) -> A(22) */ 0x28 /* ke(76) -> E(28) */ +}; + + +/* + * Convert long vowel marks in `word' to the previous vowels. + */ +static void +eb_convert_long_vowels_jis(char *word) +{ + unsigned char *wp = (unsigned char *) word; + unsigned char c1, c2; + unsigned char previous_c1 = '\0', previous_c2 = '\0'; + + LOG(("in: eb_convert_long_vowels_jis(word=%s)", eb_quoted_string(word))); + + while (*wp != '\0' && *(wp + 1) != '\0') { + c1 = *wp; + c2 = *(wp + 1); + + if (c1 == 0x21 && c2 == 0x3c) { + /* + * The is a long vowel mark. + * Convert to a vowel of the prev_ KANA character. + * If prev_ character is not KANA, the conversion is + * not done. + */ + if ((previous_c1 == 0x24 || previous_c1 == 0x25) + && 0x21 <= previous_c2 && previous_c2 <= 0x76) { + *wp = previous_c1; + *(wp + 1) = long_vowel_table[previous_c2 - 0x21]; + } + } + previous_c1 = c1; + previous_c2 = c2; + wp += 2; + } + *wp = '\0'; + + LOG(("out: eb_convert_long_vowels_jis()")); +} + + +/* + * Delete long vowel marks in `word'. + */ +static void +eb_delete_long_vowels_jis(char *word) +{ + unsigned char *in_wp = (unsigned char *) word; + unsigned char *out_wp = (unsigned char *) word; + unsigned char c1, c2; + + LOG(("in: eb_delete_long_vowels_jis(word=%s)", eb_quoted_string(word))); + + while (*in_wp != '\0' && *(in_wp + 1) != '\0') { + c1 = *in_wp; + c2 = *(in_wp + 1); + + if (c1 != 0x21 || c2 != 0x3c) { + /* + * The is not a long vowel mark. + */ + *out_wp = c1; + *(out_wp + 1) = c2; + out_wp += 2; + } + in_wp += 2; + } + *out_wp = '\0'; + + LOG(("out: eb_delete_long_vowels_jis()")); +} + + +/* + * Convert the double consonant mark `tu' to `TU'. + */ +static void +eb_convert_double_consonants_jis(char *word) +{ + unsigned char *wp = (unsigned char *) word; + unsigned char c1, c2; + + LOG(("in: eb_convert_double_consonants_jis(word=%s)", + eb_quoted_string(word))); + + while (*wp != '\0' && *(wp + 1) != '\0') { + c1 = *wp; + c2 = *(wp + 1); + + if ((c1 == 0x24 || c1 == 0x25) && c2 == 0x43) { + /* + * This is a double sound mark. Convert to the corresponding + * sound mark. + */ + *(wp + 1) = c2 + 1; + } + wp += 2; + } + *wp = '\0'; + + LOG(("out: eb_convert_double_consonants_jis()")); +} + + +/* + * Convert the contracted sound marks to the corresponding + * non-contracted sound marks. + * (`ya', `yu', `yo', `wa', `ka', `ke' -> `YA', `YU', `YO', `WA', `KA', `KE') + */ +static void +eb_convert_contracted_sounds_jis(char *word) +{ + unsigned char *wp = (unsigned char *) word; + unsigned char c1, c2; + + LOG(("in: eb_convert_contracted_sounds_jis(word=%s)", + eb_quoted_string(word))); + + while (*wp != '\0' && *(wp + 1) != '\0') { + c1 = *wp; + c2 = *(wp + 1); + + if (c1 == 0x24 || c1 == 0x25) { + /* + * This is HIRAGANA or KANAKANA. + * If this is a contracted sound mark, convert to the + * corresponding uncontracted sound mark. + */ + if (c2 == 0x63 || c2 == 0x65 || c2 == 0x67 || c2 == 0x6e) + *(wp + 1) = c2 + 1; + else if (c2 == 0x75) + *(wp + 1) = 0x2b; + else if (c2 == 0x76) + *(wp + 1) = 0x31; + } + wp += 2; + } + *wp = '\0'; + + LOG(("in: eb_convert_contracted_sounds_jis()")); +} + + +/* + * Convert the small vowels to the normal vowels. + * (`a', `i', `u', `e', `o' -> `A', `I', `U', `E', `O') + */ +static void +eb_convert_small_vowels_jis(char *word) +{ + unsigned char *wp = (unsigned char *) word; + unsigned char c1, c2; + + LOG(("in: eb_convert_small_vowels_jis(word=%s)", eb_quoted_string(word))); + + while (*wp != '\0' && *(wp + 1) != '\0') { + c1 = *wp; + c2 = *(wp + 1); + + if (c1 == 0x24 || c1 == 0x25) { + /* + * This is HIRAGANA or KANAKANA. + * If this is a small vowel mark, convert to a normal vowel. + */ + if (c2 == 0x21 || c2 == 0x23 || c2 == 0x25 || c2 == 0x27 + || c2 == 0x29) + *(wp + 1) = c2 + 1; + } + wp += 2; + } + *wp = '\0'; + + LOG(("out: eb_convert_small_vowels_jis()")); +} + + +/* + * The table is used to convert voiced consonant marks. + */ +static const char voiced_consonant_table[] = { + 0x21, /* a(21) -> a(22) */ 0x22, /* A(22) -> A(22) */ + 0x23, /* i(23) -> i(24) */ 0x24, /* I(24) -> I(24) */ + 0x25, /* u(25) -> u(26) */ 0x26, /* U(26) -> U(26) */ + 0x27, /* e(27) -> e(28) */ 0x28, /* E(28) -> E(28) */ + 0x29, /* o(29) -> o(2a) */ 0x2a, /* O(2a) -> O(2a) */ + 0x2b, /* KA(2b) -> KA(2b) */ 0x2b, /* GA(2c) -> KA(2b) */ + 0x2d, /* KI(2d) -> KI(2d) */ 0x2d, /* GI(2e) -> KI(2d) */ + 0x2f, /* KU(2f) -> KU(2f) */ 0x2f, /* GU(30) -> KU(2f) */ + 0x31, /* KE(31) -> KE(31) */ 0x31, /* GE(32) -> KE(31) */ + 0x33, /* KO(33) -> KO(33) */ 0x33, /* GO(34) -> KO(33) */ + 0x35, /* SA(35) -> SA(35) */ 0x35, /* ZA(36) -> SA(35) */ + 0x37, /* SI(37) -> SI(37) */ 0x37, /* ZI(38) -> SI(37) */ + 0x39, /* SU(39) -> SU(39) */ 0x39, /* ZU(3a) -> SU(39) */ + 0x3b, /* SE(3b) -> SE(3b) */ 0x3b, /* ZE(3c) -> SE(3b) */ + 0x3d, /* SO(3d) -> SO(3d) */ 0x3d, /* ZO(3e) -> SO(3d) */ + 0x3f, /* TA(3f) -> TA(3f) */ 0x3f, /* DA(40) -> TA(3f) */ + 0x41, /* TI(41) -> TI(41) */ 0x41, /* DI(42) -> TI(41) */ + 0x43, /* tu(43) -> TU(43) */ 0x44, /* TU(44) -> TU(44) */ + 0x44, /* DU(45) -> TU(44) */ 0x46, /* TE(46) -> TE(46) */ + 0x46, /* DE(47) -> TE(46) */ 0x48, /* TO(48) -> TO(48) */ + 0x48, /* DO(49) -> TO(48) */ 0x4a, /* NA(4a) -> NA(4a) */ + 0x4b, /* NI(4b) -> NI(4b) */ 0x4c, /* NU(4c) -> NU(4c) */ + 0x4d, /* NE(4d) -> NE(4d) */ 0x4e, /* NO(4e) -> NO(4e) */ + 0x4f, /* HA(4f) -> HA(4f) */ 0x4f, /* BA(50) -> HA(4f) */ + 0x51, /* PA(51) -> PA(51) */ 0x52, /* HI(52) -> HI(52) */ + 0x52, /* BI(53) -> HI(52) */ 0x54, /* PI(54) -> PU(54) */ + 0x55, /* HU(55) -> HU(55) */ 0x55, /* BU(56) -> HU(55) */ + 0x57, /* PU(57) -> PU(57) */ 0x58, /* HE(58) -> HE(58) */ + 0x58, /* BE(59) -> HE(58) */ 0x5a, /* PE(5a) -> PE(5a) */ + 0x5b, /* HO(5b) -> HO(5b) */ 0x5b, /* BO(5c) -> HO(5b) */ + 0x5d, /* PO(5d) -> PO(5d) */ 0x5e, /* MA(5e) -> MA(5e) */ + 0x5f, /* MI(5f) -> MI(5f) */ 0x60, /* MU(60) -> MU(60) */ + 0x61, /* ME(61) -> ME(61) */ 0x62, /* MO(62) -> MO(62) */ + 0x64, /* ya(63) -> ya(63) */ 0x64, /* YA(64) -> YA(64) */ + 0x66, /* yu(65) -> yu(65) */ 0x66, /* YU(66) -> YU(66) */ + 0x68, /* yo(67) -> yo(67) */ 0x68, /* YO(68) -> YO(68) */ + 0x69, /* RA(69) -> TA(69) */ 0x6a, /* RI(6a) -> RI(6a) */ + 0x6b, /* RU(6b) -> RU(6b) */ 0x6c, /* RE(6c) -> RE(6c) */ + 0x6d, /* RO(6d) -> RO(6d) */ 0x6e, /* wa(6e) -> wa(6e) */ + 0x6f, /* WA(6f) -> WA(6f) */ 0x70, /* WI(70) -> WI(70) */ + 0x71, /* WE(71) -> WE(71) */ 0x72, /* WO(72) -> WO(72) */ + 0x73, /* N(73) -> N(73) */ 0x26, /* VU(74) -> U(26) */ + 0x75, /* ka(75) -> ka(75) */ 0x76 /* ke(76) -> ke(76) */ +}; + +/* + * Convert the contracted sound marks to the corresponding + * non-contracted sound marks (e.g. `GA' to `KA'). + */ +static void +eb_convert_voiced_consonants_jis(char *word) +{ + unsigned char *wp = (unsigned char *) word; + unsigned char c1, c2; + + LOG(("in: eb_convert_voiced_consonants_jis(word=%s)", + eb_quoted_string(word))); + + while (*wp != '\0' && *(wp + 1) != '\0') { + c1 = *wp; + c2 = *(wp + 1); + + if ((c1 == 0x24 || c1 == 0x25) && 0x21 <= c2 && c2 <= 0x76) { + /* + * This is a voiced constonat mark. Convert to the + * corresponding unvoiced constonant mark. + */ + *(wp + 1) = voiced_consonant_table[c2 - 0x21]; + } + wp += 2; + } + *wp = '\0'; + + LOG(("out: eb_convert_voiced_consonants_jis()")); +} + + +/* + * Convert the p sound marks + * (`PA', `PI', `PU', `PE', `PO' -> `HA', `HI', `HU', `HE', `HO') + */ +static void +eb_convert_p_sounds_jis(char *word) +{ + unsigned char *wp = (unsigned char *) word; + unsigned char c1, c2; + + LOG(("in: eb_convert_p_sounds_jis(word=%s)", eb_quoted_string(word))); + + while (*wp != '\0' && *(wp + 1) != '\0') { + c1 = *wp; + c2 = *(wp + 1); + + if (c1 == 0x24 || c1 == 0x25) { + /* + * This is HIRAGANA or KANAKANA. + * If this is a p-sound mark, convert to the corresponding + * unvoiced consonant mark. + */ + if (c2 == 0x51 || c2 == 0x54 || c2 == 0x57 || c2 == 0x5a + || c2 == 0x5d) + *(wp + 1) = c2 - 2; + } + wp += 2; + } + *wp = '\0'; + + LOG(("out: eb_convert_p_sounds_jis()")); +} + + +/* + * Delete spaces in `word'. + */ +static void +eb_delete_spaces_latin(char *word) +{ + unsigned char *in_wp = (unsigned char *) word; + unsigned char *out_wp = (unsigned char *) word; + + LOG(("in: eb_delete_space_latin(word=%s)", eb_quoted_string(word))); + + while (*in_wp != '\0') { + if (*in_wp != ' ') { + /* + * This is not a space character of ISO 8859 1. + */ + *out_wp = *in_wp; + out_wp++; + } + in_wp++; + } + *out_wp = '\0'; + + LOG(("out: eb_delete_space_latin()")); +} + + +/* + * Delete spaces in `word'. + */ +static void +eb_delete_spaces_jis(char *word) +{ + unsigned char *in_wp = (unsigned char *) word; + unsigned char *out_wp = (unsigned char *) word; + unsigned char c1, c2; + + LOG(("in: eb_delete_space_jis(word=%s)", eb_quoted_string(word))); + + while (*in_wp != '\0' && *(in_wp + 1) != '\0') { + c1 = *in_wp; + c2 = *(in_wp + 1); + + if (c1 != 0x21 || c2 != 0x21) { + /* + * This is not a space character of JIS X 0208. + */ + *out_wp = c1; + *(out_wp + 1) = c2; + out_wp += 2; + } + in_wp += 2; + } + *out_wp = '\0'; + + LOG(("out: eb_delete_space_jis()")); +} + + +/* + * Reverse a word for ENDWORD SEARCH. + * + * `word' is a word to reverse. It must be an alphabetic word. + * The reversed word is also put into `word'. + */ +static void +eb_reverse_word_latin(char *word) +{ + char *p1, *p2; + int word_length; + char c; + + LOG(("in: eb_reverse_word_latin(word=%s)", eb_quoted_string(word))); + + word_length = strlen(word); + if (word_length == 0) + return; + for (p1 = word, p2 = word + word_length - 1; p1 < p2; p1++, p2--) { + c = *p1; + *p1 = *p2; + *p2 = c; + } + + LOG(("out: eb_reverse_word_latin()")); +} + + +/* + * Reverse a word for ENDWORD SEARCH. + * + * `word' is a word to reverse. It must be a KANA word. + * The reversed word is also put into `word'. + */ +static void +eb_reverse_word_jis(char *word) +{ + char *p1, *p2; + int word_length; + char c; + + LOG(("in: eb_reverse_word_jis(word=%s)", eb_quoted_string(word))); + + word_length = strlen(word); + if (word_length % 2 == 1) { + *(word + word_length - 1) = '\0'; + word_length--; + } + for (p1 = word, p2 = word + word_length - 2; p1 < p2; p1 += 2, p2 -= 2) { + c = *p1; + *p1 = *p2; + *p2 = c; + c = *(p1 + 1); + *(p1 + 1) = *(p2 + 1); + *(p2 + 1) = c; + } + + LOG(("out: eb_reverse_word_jis()")); +} diff --git a/stopcode.c b/stopcode.c new file mode 100644 index 0000000..2dd0e5f --- /dev/null +++ b/stopcode.c @@ -0,0 +1,111 @@ +/* + * 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 "appendix.h" +#include "text.h" +#include "build-post.h" + +/* + * Examine whether the current subbook in `appendix' has a stop-code. + */ +int +eb_have_stop_code(EB_Appendix *appendix) +{ + eb_lock(&appendix->lock); + LOG(("in: eb_have_stop_code(appendix=%d)", (int)appendix->code)); + + /* + * Current subbook must have been set. + */ + if (appendix->subbook_current == NULL) + goto failed; + + if (appendix->subbook_current->stop_code0 == 0) + goto failed; + + LOG(("out: eb_have_stop_code() = %d", 1)); + eb_unlock(&appendix->lock); + + return 1; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_have_stop_code() = %d", 0)); + eb_unlock(&appendix->lock); + return 0; +} + + +/* + * Return the stop-code of the current subbook in `appendix'. + */ +EB_Error_Code +eb_stop_code(EB_Appendix *appendix, int *stop_code) +{ + EB_Error_Code error_code; + + eb_lock(&appendix->lock); + LOG(("in: eb_stop_code(appendix=%d)", (int)appendix->code)); + + /* + * Current subbook must have been set. + */ + if (appendix->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_APPSUB; + goto failed; + } + + if (appendix->subbook_current->stop_code0 == 0) { + error_code = EB_ERR_NO_STOPCODE; + goto failed; + } + + stop_code[0] = appendix->subbook_current->stop_code0; + stop_code[1] = appendix->subbook_current->stop_code1; + + LOG(("out: eb_stop_code(stop_code=%d,%d) = %s", + stop_code[0], stop_code[1], eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + stop_code[0] = -1; + stop_code[1] = -1; + LOG(("out: eb_stop_code() = %s", eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} diff --git a/strcasecmp.c b/strcasecmp.c new file mode 100644 index 0000000..191efcb --- /dev/null +++ b/strcasecmp.c @@ -0,0 +1,105 @@ +/* + * 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. + */ + +/* + * This program requires the following Autoconf macros: + */ + +#include + +/* + * Compare strings. + * Cases in the strings are insensitive. + */ +int +eb_strcasecmp(const char *string1, const char *string2) +{ + const unsigned char *string1_p = (const unsigned char *)string1; + const unsigned char *string2_p = (const unsigned char *)string2; + int c1, c2; + + while (*string1_p != '\0') { + if ('a' <= *string1_p && *string1_p <= 'z') + c1 = *string1_p - ('a' - 'A'); + else + c1 = *string1_p; + + if ('a' <= *string2_p && *string2_p <= 'z') + c2 = *string2_p - ('a' - 'A'); + else + c2 = *string2_p; + + if (c1 != c2) + return c1 - c2; + + string1_p++; + string2_p++; + } + + return -(*string2_p); +} + + +/* + * Compare strings within `n' characters. + * Cases in the strings are insensitive. + */ +int +eb_strncasecmp(const char *string1, const char *string2, size_t n) +{ + const unsigned char *string1_p = (const unsigned char *)string1; + const unsigned char *string2_p = (const unsigned char *)string2; + size_t i = n; + int c1, c2; + + if (i <= 0) + return 0; + + while (*string1_p != '\0') { + if ('a' <= *string1_p && *string1_p <= 'z') + c1 = *string1_p - ('a' - 'A'); + else + c1 = *string1_p; + + if ('a' <= *string2_p && *string2_p <= 'z') + c2 = *string2_p - ('a' - 'A'); + else + c2 = *string2_p; + + if (c1 != c2) + return c1 - c2; + + string1_p++; + string2_p++; + i--; + if (i <= 0) + return 0; + } + + return -(*string2_p); +} diff --git a/subbook.c b/subbook.c new file mode 100644 index 0000000..c5fd19b --- /dev/null +++ b/subbook.c @@ -0,0 +1,1140 @@ +/* + * 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 "binary.h" +#include "font.h" +#include "build-post.h" + +/* + * Unexported functions. + */ +static EB_Error_Code eb_load_subbook(EB_Book *book); +static EB_Error_Code eb_load_subbook_indexes(EB_Book *book); +static EB_Error_Code eb_set_subbook_eb(EB_Book *book, + EB_Subbook_Code subbook_code); +static EB_Error_Code eb_set_subbook_epwing(EB_Book *book, + EB_Subbook_Code subbook_code); + + +/* + * Initialize all subbooks in `book'. + */ +void +eb_initialize_subbooks(EB_Book *book) +{ + EB_Subbook *subbook; + EB_Subbook *saved_subbook_current; + int i; + + LOG(("in: eb_initialize_subbooks(book=%d)", (int)book->code)); + + saved_subbook_current = book->subbook_current; + + for (i = 0, subbook = book->subbooks; i < book->subbook_count; + i++, subbook++) { + book->subbook_current = subbook; + + subbook->initialized = 0; + subbook->index_page = 1; + subbook->code = i; + zio_initialize(&subbook->text_zio); + zio_initialize(&subbook->graphic_zio); + zio_initialize(&subbook->sound_zio); + zio_initialize(&subbook->movie_zio); + subbook->title[0] = '\0'; + subbook->directory_name[0] = '\0'; + subbook->data_directory_name[0] = '\0'; + subbook->gaiji_directory_name[0] = '\0'; + subbook->movie_directory_name[0] = '\0'; + + subbook->text_file_name[0] = '\0'; + subbook->graphic_file_name[0] = '\0'; + subbook->sound_file_name[0] = '\0'; + + subbook->text_hint_zio_code = ZIO_PLAIN; + subbook->graphic_hint_zio_code = ZIO_PLAIN; + subbook->sound_hint_zio_code = ZIO_PLAIN; + + subbook->search_title_page = 0; + eb_initialize_searches(book); + subbook->multi_count = 0; + + eb_initialize_fonts(book); + subbook->narrow_current = NULL; + subbook->wide_current = NULL; + } + + book->subbook_current = saved_subbook_current; + + LOG(("out: eb_initialize_subbooks()")); +} + + +/* + * Finalize all subbooks in `book'. + */ +void +eb_finalize_subbooks(EB_Book *book) +{ + EB_Subbook *subbook; + EB_Subbook *saved_subbook_current; + int i; + + LOG(("in: eb_finalize_subbooks(book=%d)", (int)book->code)); + + saved_subbook_current = book->subbook_current; + + for (i = 0, subbook = book->subbooks; i < book->subbook_count; + i++, subbook++) { + book->subbook_current = subbook; + + zio_finalize(&subbook->text_zio); + zio_finalize(&subbook->graphic_zio); + zio_finalize(&subbook->sound_zio); + zio_finalize(&subbook->movie_zio); + + eb_finalize_searches(book); + eb_finalize_fonts(book); + + subbook->narrow_current = NULL; + subbook->wide_current = NULL; + } + + book->subbook_current = saved_subbook_current; + + LOG(("out: eb_finalize_subbooks()")); +} + + +/* + * Get information about the current subbook. + */ +static EB_Error_Code +eb_load_subbook(EB_Book *book) +{ + EB_Error_Code error_code; + EB_Subbook *subbook; + + LOG(("in: eb_load_subbook(book=%d)", (int)book->code)); + + subbook = book->subbook_current; + + /* + * Current subbook must have been set. + */ + if (subbook == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Reset contexts. + */ + eb_reset_search_contexts(book); + eb_reset_text_context(book); + eb_reset_binary_context(book); + + /* + * If the subbook has already initialized, return immediately. + */ + if (subbook->initialized) + goto succeeded; + + if (0 <= zio_file(&subbook->text_zio)) { + /* + * Read index information. + */ + error_code = eb_load_subbook_indexes(book); + if (error_code != EB_SUCCESS) + goto failed; + + /* + * Read mutli search information. + */ + error_code = eb_load_multi_searches(book); + if (error_code != EB_SUCCESS) + goto failed; + error_code = eb_load_multi_titles(book); + if (error_code != EB_SUCCESS) + goto failed; + + /* + * Rewind the file descriptor of the start file. + */ + if (zio_lseek(&subbook->text_zio, + ((off_t) subbook->index_page - 1) * EB_SIZE_PAGE, SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_TEXT; + goto failed; + } + } + + succeeded: + LOG(("out: eb_load_subbook() = %s", eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_load_subbook() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Get information about all subbooks in the book. + */ +EB_Error_Code +eb_load_all_subbooks(EB_Book *book) +{ + EB_Error_Code error_code; + EB_Subbook *subbook; + int i; + + eb_lock(&book->lock); + LOG(("in: eb_load_all_subbooks(book=%d)", (int)book->code)); + + /* + * The book must have been bound. + */ + if (book->path == NULL) { + error_code = EB_ERR_UNBOUND_BOOK; + goto failed; + } + + /* + * Initialize each subbook. + */ + for (i = 0, subbook = book->subbooks; i < book->subbook_count; + i++, subbook++) { + error_code = eb_set_subbook(book, subbook->code); + if (error_code != EB_SUCCESS) + goto failed; + } + eb_unset_subbook(book); + + LOG(("out: eb_load_all_subbooks() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_unset_subbook(book); + LOG(("out: eb_load_all_subbooks() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Get index information in the current subbook. + * + * If succeeds, the number of indexes is returned. + * Otherwise, -1 is returned. + */ +static EB_Error_Code +eb_load_subbook_indexes(EB_Book *book) +{ + EB_Error_Code error_code; + EB_Subbook *subbook; + EB_Search search; + char buffer[EB_SIZE_PAGE]; + char *buffer_p; + int index_count; + int availability; + int global_availability; + EB_Search sebxa_zip_index; + EB_Search sebxa_zip_text; + int i; + + LOG(("in: eb_load_subbook_indexes(book=%d)", (int)book->code)); + + eb_initialize_search(&sebxa_zip_index); + eb_initialize_search(&sebxa_zip_text); + + subbook = book->subbook_current; + + /* + * Read the index table in the subbook. + */ + if (zio_lseek(&subbook->text_zio, + ((off_t) subbook->index_page - 1) * EB_SIZE_PAGE, SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_TEXT; + goto failed; + } + if (zio_read(&subbook->text_zio, buffer, EB_SIZE_PAGE) != EB_SIZE_PAGE) { + error_code = EB_ERR_FAIL_READ_TEXT; + goto failed; + } + + /* + * Get start page numbers of the indexes in the subbook. + */ + index_count = eb_uint1(buffer + 1); + if (EB_SIZE_PAGE / 16 - 1 <= index_count) { + error_code = EB_ERR_UNEXP_TEXT; + goto failed; + } + + /* + * Get availavility flag of the index information. + */ + global_availability = eb_uint1(buffer + 4); + if (0x02 < global_availability) + global_availability = 0; + + /* + * Set each search method information. + */ + for (i = 0, buffer_p = buffer + 16; i < index_count; i++, buffer_p += 16) { + /* + * Set index style. + */ + eb_initialize_search(&search); + + search.index_id = eb_uint1(buffer_p); + search.start_page = eb_uint4(buffer_p + 2); + search.end_page = search.start_page + eb_uint4(buffer_p + 6) - 1; + + /* + * Set canonicalization flags. + */ + availability = eb_uint1(buffer_p + 10); + if ((global_availability == 0x00 && availability == 0x02) + || global_availability == 0x02) { + unsigned int flags; + + flags = eb_uint3(buffer_p + 11); + search.katakana = (flags & 0xc00000) >> 22; + search.lower = (flags & 0x300000) >> 20; + if ((flags & 0x0c0000) >> 18 == 0) + search.mark = EB_INDEX_STYLE_DELETE; + else + search.mark = EB_INDEX_STYLE_ASIS; + search.long_vowel = (flags & 0x030000) >> 16; + search.double_consonant = (flags & 0x00c000) >> 14; + search.contracted_sound = (flags & 0x003000) >> 12; + search.small_vowel = (flags & 0x000c00) >> 10; + search.voiced_consonant = (flags & 0x000300) >> 8; + search.p_sound = (flags & 0x0000c0) >> 6; + + } else if (search.index_id == 0x70 || search.index_id == 0x90) { + search.katakana = EB_INDEX_STYLE_CONVERT; + search.lower = EB_INDEX_STYLE_CONVERT; + search.mark = EB_INDEX_STYLE_DELETE; + search.long_vowel = EB_INDEX_STYLE_CONVERT; + search.double_consonant = EB_INDEX_STYLE_CONVERT; + search.contracted_sound = EB_INDEX_STYLE_CONVERT; + search.small_vowel = EB_INDEX_STYLE_CONVERT; + search.voiced_consonant = EB_INDEX_STYLE_CONVERT; + search.p_sound = EB_INDEX_STYLE_CONVERT; + + } else { + search.katakana = EB_INDEX_STYLE_ASIS; + search.lower = EB_INDEX_STYLE_CONVERT; + search.mark = EB_INDEX_STYLE_ASIS; + search.long_vowel = EB_INDEX_STYLE_ASIS; + search.double_consonant = EB_INDEX_STYLE_ASIS; + search.contracted_sound = EB_INDEX_STYLE_ASIS; + search.small_vowel = EB_INDEX_STYLE_ASIS; + search.voiced_consonant = EB_INDEX_STYLE_ASIS; + search.p_sound = EB_INDEX_STYLE_ASIS; + } + + if (book->character_code == EB_CHARCODE_ISO8859_1 + || search.index_id == 0x72 + || search.index_id == 0x92) { + search.space = EB_INDEX_STYLE_ASIS; + } else { + search.space = EB_INDEX_STYLE_DELETE; + } + + /* + * Identify search method. + */ + switch (search.index_id) { + case 0x00: + memcpy(&subbook->text, &search, sizeof(EB_Search)); + break; + case 0x01: + memcpy(&subbook->menu, &search, sizeof(EB_Search)); + break; + case 0x02: + memcpy(&subbook->copyright, &search, sizeof(EB_Search)); + break; + case 0x10: + memcpy(&subbook->image_menu, &search, sizeof(EB_Search)); + break; + case 0x16: + if (book->disc_code == EB_DISC_EPWING) + subbook->search_title_page = search.start_page; + break; + case 0x21: + if (book->disc_code == EB_DISC_EB + && zio_mode(&subbook->text_zio) == ZIO_PLAIN) + memcpy(&sebxa_zip_text, &search, sizeof(EB_Search)); + break; + case 0x22: + if (book->disc_code == EB_DISC_EB + && zio_mode(&subbook->text_zio) == ZIO_PLAIN) + memcpy(&sebxa_zip_index, &search, sizeof(EB_Search)); + break; + case 0x70: + memcpy(&subbook->endword_kana, &search, sizeof(EB_Search)); + break; + case 0x71: + memcpy(&subbook->endword_asis, &search, sizeof(EB_Search)); + break; + case 0x72: + memcpy(&subbook->endword_alphabet, &search, sizeof(EB_Search)); + break; + case 0x80: + memcpy(&subbook->keyword, &search, sizeof(EB_Search)); + break; + case 0x81: + memcpy(&subbook->cross, &search, sizeof(EB_Search)); + break; + case 0x90: + memcpy(&subbook->word_kana, &search, sizeof(EB_Search)); + break; + case 0x91: + memcpy(&subbook->word_asis, &search, sizeof(EB_Search)); + break; + case 0x92: + memcpy(&subbook->word_alphabet, &search, sizeof(EB_Search)); + break; + case 0xd8: + memcpy(&subbook->sound, &search, sizeof(EB_Search)); + break; + case 0xf1: + if (book->disc_code == EB_DISC_EB) { + subbook->wide_fonts[EB_FONT_16].page = search.start_page; + subbook->wide_fonts[EB_FONT_16].font_code = EB_FONT_16; + } + break; + case 0xf2: + if (book->disc_code == EB_DISC_EB) { + subbook->narrow_fonts[EB_FONT_16].page = search.start_page; + subbook->narrow_fonts[EB_FONT_16].font_code = EB_FONT_16; + } + break; + case 0xf3: + if (book->disc_code == EB_DISC_EB) { + subbook->wide_fonts[EB_FONT_24].page = search.start_page; + subbook->wide_fonts[EB_FONT_24].font_code = EB_FONT_24; + } + break; + case 0xf4: + if (book->disc_code == EB_DISC_EB) { + subbook->narrow_fonts[EB_FONT_24].page = search.start_page; + subbook->narrow_fonts[EB_FONT_24].font_code = EB_FONT_24; + } + break; + case 0xf5: + if (book->disc_code == EB_DISC_EB) { + subbook->wide_fonts[EB_FONT_30].page = search.start_page; + subbook->wide_fonts[EB_FONT_30].font_code = EB_FONT_30; + } + break; + case 0xf6: + if (book->disc_code == EB_DISC_EB) { + subbook->narrow_fonts[EB_FONT_30].page = search.start_page; + subbook->narrow_fonts[EB_FONT_30].font_code = EB_FONT_30; + } + break; + case 0xf7: + if (book->disc_code == EB_DISC_EB) { + subbook->wide_fonts[EB_FONT_48].page = search.start_page; + subbook->wide_fonts[EB_FONT_48].font_code = EB_FONT_48; + } + break; + case 0xf8: + if (book->disc_code == EB_DISC_EB) { + subbook->narrow_fonts[EB_FONT_48].page = search.start_page; + subbook->narrow_fonts[EB_FONT_48].font_code = EB_FONT_48; + } + break; + case 0xff: + if (subbook->multi_count < EB_MAX_MULTI_SEARCHES) { + memcpy(&subbook->multis[subbook->multi_count].search, &search, + sizeof(EB_Search)); + subbook->multi_count++; + } + break; + } + + eb_finalize_search(&sebxa_zip_text); + } + + /* + * Set S-EBXA compression flag. + */ + if (book->disc_code == EB_DISC_EB + && zio_mode(&subbook->text_zio) == ZIO_PLAIN + && subbook->text.start_page != 0 + && sebxa_zip_index.start_page != 0 + && sebxa_zip_text.start_page != 0) { + zio_set_sebxa_mode(&subbook->text_zio, + (sebxa_zip_index.start_page - 1) * EB_SIZE_PAGE, + (sebxa_zip_text.start_page - 1) * EB_SIZE_PAGE, + (subbook->text.start_page - 1) * EB_SIZE_PAGE, + subbook->text.end_page * EB_SIZE_PAGE - 1); + } + + eb_finalize_search(&sebxa_zip_index); + eb_finalize_search(&sebxa_zip_text); + + LOG(("out: eb_load_subbook_indexes() = %s", eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_finalize_search(&sebxa_zip_index); + eb_finalize_search(&sebxa_zip_text); + LOG(("out: eb_load_subbook_indexes() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Make a subbook list in the book. + */ +EB_Error_Code +eb_subbook_list(EB_Book *book, EB_Subbook_Code *subbook_list, + int *subbook_count) +{ + EB_Error_Code error_code; + EB_Subbook_Code *list_p; + int i; + + eb_lock(&book->lock); + LOG(("in: eb_subbook_list(book=%d)", (int)book->code)); + + /* + * The book must have been bound. + */ + if (book->path == NULL) { + error_code = EB_ERR_UNBOUND_BOOK; + goto failed; + } + + for (i = 0, list_p = subbook_list; i < book->subbook_count; i++, list_p++) + *list_p = i; + *subbook_count = book->subbook_count; + + LOG(("out: eb_subbook_list(subbook_count=%d) = %s", *subbook_count, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *subbook_count = 0; + LOG(("out: eb_subbook_list() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Return a subbook-code of the current subbook. + */ +EB_Error_Code +eb_subbook(EB_Book *book, EB_Subbook_Code *subbook_code) +{ + EB_Error_Code error_code; + + eb_lock(&book->lock); + LOG(("in: eb_subbook(book=%d)", (int)book->code)); + + /* + * The current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + *subbook_code = book->subbook_current->code; + + LOG(("out: eb_subbook(subbook_code=%d) = %s", *subbook_code, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *subbook_code = EB_SUBBOOK_INVALID; + LOG(("out: eb_subbook() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Return a title of the current subbook. + */ +EB_Error_Code +eb_subbook_title(EB_Book *book, char *title) +{ + EB_Error_Code error_code; + + eb_lock(&book->lock); + LOG(("in: eb_subbook_title(book=%d)", (int)book->code)); + + /* + * The current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + strcpy(title, book->subbook_current->title); + + LOG(("out: eb_subbook_title(title=%s) = %s", title, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *title = '\0'; + LOG(("out: eb_subbook_title() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Return a title of the specified subbook `subbook_code'. + */ +EB_Error_Code +eb_subbook_title2(EB_Book *book, EB_Subbook_Code subbook_code, char *title) +{ + EB_Error_Code error_code; + + eb_lock(&book->lock); + LOG(("in: eb_subbook_title2(book=%d, subbook_code=%d)", + (int)book->code, (int)subbook_code)); + + /* + * The book must have been bound. + */ + if (book->path == NULL) { + error_code = EB_ERR_UNBOUND_BOOK; + goto failed; + } + + /* + * Check for the subbook-code. + */ + if (subbook_code < 0 || book->subbook_count <= subbook_code) { + error_code = EB_ERR_NO_SUCH_SUB; + goto failed; + } + + strcpy(title, (book->subbooks + subbook_code)->title); + + LOG(("out: eb_subbook_title2(title=%s) = %s", title, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *title = '\0'; + LOG(("out: eb_subbook_title2() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Return a directory name of the current subbook. + */ +EB_Error_Code +eb_subbook_directory(EB_Book *book, char *directory) +{ + EB_Error_Code error_code; + char *p; + + eb_lock(&book->lock); + LOG(("in: eb_subbook_directory(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Copy directory name. + * Upper letters are converted to lower letters. + */ + strcpy(directory, book->subbook_current->directory_name); + for (p = directory; *p != '\0'; p++) { + if ('A' <= *p && *p <= 'Z') + *p = ASCII_TOLOWER(*p); + } + + LOG(("out: eb_subbook_directory(directory=%s) = %s", directory, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *directory = '\0'; + LOG(("out: eb_subbook_directory() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Return a directory name of the specified subbook `subbook_code'. + */ +EB_Error_Code +eb_subbook_directory2(EB_Book *book, EB_Subbook_Code subbook_code, + char *directory) +{ + EB_Error_Code error_code; + char *p; + + eb_lock(&book->lock); + LOG(("in: eb_subbook_directory2(book=%d, subbook_code=%d)", + (int)book->code, (int)subbook_code)); + + /* + * The book must have been bound. + */ + if (book->path == NULL) { + error_code = EB_ERR_UNBOUND_BOOK; + goto failed; + } + + /* + * Check for the subbook-code. + */ + if (subbook_code < 0 || book->subbook_count <= subbook_code) { + error_code = EB_ERR_NO_SUCH_SUB; + goto failed; + } + + /* + * Copy directory name. + * Upper letters are converted to lower letters. + */ + strcpy(directory, (book->subbooks + subbook_code)->directory_name); + for (p = directory; *p != '\0'; p++) { + if ('A' <= *p && *p <= 'Z') + *p = ASCII_TOLOWER(*p); + } + + LOG(("out: eb_subbook_directory2(directory=%s) = %s", directory, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *directory = '\0'; + LOG(("out: eb_subbook_directory2() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Set the subbook `subbook_code' as the current subbook. + */ +EB_Error_Code +eb_set_subbook(EB_Book *book, EB_Subbook_Code subbook_code) +{ + EB_Error_Code error_code = EB_SUCCESS; + + eb_lock(&book->lock); + LOG(("in: eb_set_subbook(book=%d, subbook_code=%d)", + (int)book->code, (int)subbook_code)); + + /* + * The book must have been bound. + */ + if (book->path == NULL) { + error_code = EB_ERR_UNBOUND_BOOK; + goto failed; + } + + /* + * Check for the subbook-code. + */ + if (subbook_code < 0 || book->subbook_count <= subbook_code) { + error_code = EB_ERR_NO_SUCH_SUB; + goto failed; + } + + /* + * If the subbook has already been set as the current subbook, + * there is nothing to be done. + * Otherwise close the previous subbook. + */ + if (book->subbook_current != NULL) { + if (book->subbook_current->code == subbook_code) + goto succeeded; + eb_unset_subbook(book); + } + + /* + * Set the current subbook. + */ + book->subbook_current = book->subbooks + subbook_code; + + /* + * Dispatch. + */ + if (book->disc_code == EB_DISC_EB) + error_code = eb_set_subbook_eb(book, subbook_code); + else + error_code = eb_set_subbook_epwing(book, subbook_code); + if (error_code != EB_SUCCESS) + goto failed; + + /* + * Load the subbook. + */ + error_code = eb_load_subbook(book); + if (error_code != EB_SUCCESS) + goto failed; + + /* + * Load font files. + */ + eb_load_font_headers(book); + + succeeded: + book->subbook_current->initialized = 1; + LOG(("out: eb_set_subbook() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return error_code; + + /* + * An error occurs... + */ + failed: + if (book->subbook_current != NULL) { + zio_close(&book->subbook_current->text_zio); + zio_close(&book->subbook_current->graphic_zio); + zio_close(&book->subbook_current->sound_zio); + zio_close(&book->subbook_current->movie_zio); + } + book->subbook_current = NULL; + LOG(("out: eb_set_subbook() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Set the subbook `subbook_code' as the current subbook. + */ +static EB_Error_Code +eb_set_subbook_eb(EB_Book *book, EB_Subbook_Code subbook_code) +{ + EB_Error_Code error_code; + EB_Subbook *subbook; + char text_path_name[EB_MAX_PATH_LENGTH + 1]; + char graphic_path_name[EB_MAX_PATH_LENGTH + 1]; + Zio_Code text_zio_code; + Zio_Code graphic_zio_code; + + LOG(("in: eb_set_subbook_eb(book=%d, subbook_code=%d)", + (int)book->code, (int)subbook_code)); + + subbook = book->subbook_current; + + /* + * Open a text file if exists. + */ + text_zio_code = ZIO_INVALID; + + if (subbook->initialized) { + if (zio_mode(&subbook->text_zio) != ZIO_INVALID) + text_zio_code = ZIO_REOPEN; + } else { + eb_canonicalize_file_name(subbook->text_file_name); + if (eb_find_file_name2(book->path, subbook->directory_name, + EB_FILE_NAME_START, subbook->text_file_name) == EB_SUCCESS) { + eb_path_name_zio_code(subbook->text_file_name, ZIO_PLAIN, + &text_zio_code); + } + } + + if (text_zio_code != ZIO_INVALID) { + eb_compose_path_name2(book->path, subbook->directory_name, + subbook->text_file_name, text_path_name); + if (zio_open(&subbook->text_zio, text_path_name, text_zio_code) < 0) { + error_code = EB_ERR_FAIL_OPEN_TEXT; + goto failed; + } + text_zio_code = zio_mode(&subbook->text_zio); + } + + /* + * Open a graphic file if exists. + */ + graphic_zio_code = ZIO_INVALID; + + if (subbook->initialized) { + if (zio_mode(&subbook->graphic_zio) != ZIO_INVALID) + graphic_zio_code = ZIO_REOPEN; + } else if (text_zio_code != ZIO_INVALID) { + strcpy(subbook->graphic_file_name, subbook->text_file_name); + graphic_zio_code = text_zio_code; + } + + if (graphic_zio_code != ZIO_INVALID) { + eb_compose_path_name2(book->path, subbook->directory_name, + subbook->graphic_file_name, graphic_path_name); + if (zio_open(&subbook->graphic_zio, graphic_path_name, + graphic_zio_code) < 0) { + error_code = EB_ERR_FAIL_OPEN_BINARY; + goto failed; + } + graphic_zio_code = zio_mode(&subbook->graphic_zio); + } + + LOG(("out: eb_set_subbook_eb() = %s", eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_set_subbook_eb() = %s", eb_error_string(error_code))); + return error_code; +} + +/* + * Set the subbook `subbook_code' as the current subbook. + */ +static EB_Error_Code +eb_set_subbook_epwing(EB_Book *book, EB_Subbook_Code subbook_code) +{ + EB_Error_Code error_code; + EB_Subbook *subbook; + char text_path_name[EB_MAX_PATH_LENGTH + 1]; + char graphic_path_name[EB_MAX_PATH_LENGTH + 1]; + char sound_path_name[EB_MAX_PATH_LENGTH + 1]; + Zio_Code text_zio_code; + Zio_Code graphic_zio_code; + Zio_Code sound_zio_code; + + LOG(("in: eb_set_subbook_epwing(book=%d, subbook_code=%d)", + (int)book->code, (int)subbook_code)); + + subbook = book->subbook_current; + + if (!subbook->initialized) { + /* + * Adjust directory names. + */ + strcpy(subbook->data_directory_name, EB_DIRECTORY_NAME_DATA); + eb_fix_directory_name2(book->path, subbook->directory_name, + subbook->data_directory_name); + + strcpy(subbook->gaiji_directory_name, EB_DIRECTORY_NAME_GAIJI); + eb_fix_directory_name2(book->path, subbook->directory_name, + subbook->gaiji_directory_name); + + strcpy(subbook->movie_directory_name, EB_DIRECTORY_NAME_MOVIE); + eb_fix_directory_name2(book->path, subbook->directory_name, + subbook->movie_directory_name); + } + + /* + * Open a text file if exists. + * + * If a subbook has stream data only, its index_page has been set + * to 0. In this case, we must not try to open a text file of + * the subbook, since the text file may be for another subbook. + * Remember that subbooks can share a `data' sub-directory. + */ + text_zio_code = ZIO_INVALID; + + if (subbook->initialized) { + if (zio_mode(&subbook->text_zio) != ZIO_INVALID) + text_zio_code = ZIO_REOPEN; + } else if (subbook->index_page > 0) { + eb_canonicalize_file_name(subbook->text_file_name); + if (eb_find_file_name3(book->path, subbook->directory_name, + subbook->data_directory_name, subbook->text_file_name, + subbook->text_file_name) == EB_SUCCESS) { + eb_path_name_zio_code(subbook->text_file_name, + subbook->text_hint_zio_code, &text_zio_code); + } + } + + if (text_zio_code != ZIO_INVALID) { + eb_compose_path_name3(book->path, subbook->directory_name, + subbook->data_directory_name, subbook->text_file_name, + text_path_name); + if (zio_open(&subbook->text_zio, text_path_name, text_zio_code) < 0) { + subbook = NULL; + error_code = EB_ERR_FAIL_OPEN_TEXT; + goto failed; + } + text_zio_code = zio_mode(&subbook->text_zio); + } + + /* + * Open a graphic file if exists. + */ + graphic_zio_code = ZIO_INVALID; + + if (subbook->initialized) { + if (zio_mode(&subbook->graphic_zio) != ZIO_INVALID) + graphic_zio_code = ZIO_REOPEN; + } else if (text_zio_code != ZIO_INVALID) { + eb_canonicalize_file_name(subbook->graphic_file_name); + if (eb_find_file_name3(book->path, subbook->directory_name, + subbook->data_directory_name, subbook->graphic_file_name, + subbook->graphic_file_name) == EB_SUCCESS) { + eb_path_name_zio_code(subbook->graphic_file_name, + subbook->graphic_hint_zio_code, &graphic_zio_code); + } + } + + if (graphic_zio_code != ZIO_INVALID) { + eb_compose_path_name3(book->path, subbook->directory_name, + subbook->data_directory_name, subbook->graphic_file_name, + graphic_path_name); + if (zio_open(&subbook->graphic_zio, graphic_path_name, + graphic_zio_code) < 0) { + error_code = EB_ERR_FAIL_OPEN_BINARY; + goto failed; + } + graphic_zio_code = zio_mode(&subbook->graphic_zio); + } + + /* + * Open a sound file if exists. + */ + sound_zio_code = ZIO_INVALID; + + if (subbook->initialized) { + if (zio_mode(&subbook->sound_zio) != ZIO_INVALID) + sound_zio_code = ZIO_REOPEN; + } else if (text_zio_code != ZIO_INVALID) { + eb_canonicalize_file_name(subbook->sound_file_name); + if (eb_find_file_name3(book->path, subbook->directory_name, + subbook->data_directory_name, subbook->sound_file_name, + subbook->sound_file_name) == EB_SUCCESS) { + eb_path_name_zio_code(subbook->sound_file_name, + subbook->sound_hint_zio_code, &sound_zio_code); + } + } + + if (sound_zio_code != ZIO_INVALID) { + eb_compose_path_name3(book->path, subbook->directory_name, + subbook->data_directory_name, subbook->sound_file_name, + sound_path_name); + if (zio_open(&subbook->sound_zio, sound_path_name, + sound_zio_code) < 0) { + error_code = EB_ERR_FAIL_OPEN_BINARY; + goto failed; + } + sound_zio_code = zio_mode(&subbook->sound_zio); + } + + LOG(("out: eb_set_subbook_epwing() = %s", eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_set_subbook_epwing() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Unset the current subbook. + */ +void +eb_unset_subbook(EB_Book *book) +{ + eb_lock(&book->lock); + LOG(("in: eb_unset_subbook(book=%d)", (int)book->code)); + + /* + * Close the file of the current subbook. + */ + if (book->subbook_current != NULL) { + eb_unset_font(book); + eb_unset_binary(book); + zio_close(&book->subbook_current->text_zio); + zio_close(&book->subbook_current->graphic_zio); + zio_close(&book->subbook_current->sound_zio); + zio_close(&book->subbook_current->movie_zio); + book->subbook_current = NULL; + } + + LOG(("out: eb_unset_subbook()")); + eb_unlock(&book->lock); +} diff --git a/text.c b/text.c new file mode 100644 index 0000000..285612d --- /dev/null +++ b/text.c @@ -0,0 +1,121 @@ +/* + * 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 "build-post.h" + +/* + * Examine whether the current subbook in `book' has text body. + * or not. + */ +int +eb_have_text(EB_Book *book) +{ + eb_lock(&book->lock); + LOG(("in: eb_have_text(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) + goto failed; + + /* + * Check for the index page of text. + */ + if (book->subbook_current->text.start_page == 0) + goto failed; + + LOG(("out: eb_have_text() = %d", 1)); + eb_unlock(&book->lock); + + return 1; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_have_text() = %d", 0)); + eb_unlock(&book->lock); + return 0; +} + + +/* + * Menu. + */ +EB_Error_Code +eb_text(EB_Book *book, EB_Position *position) +{ + EB_Error_Code error_code; + int page; + + eb_lock(&book->lock); + LOG(("in: eb_text(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Check for the page number of text. + */ + page = book->subbook_current->text.start_page; + if (page == 0) { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + + /* + * Copy the position to `position'. + */ + position->page = page; + position->offset = 0; + + LOG(("out: eb_text(position={%d,%d}) = %s", + position->page, position->offset, eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_text() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + diff --git a/text.h b/text.h new file mode 100644 index 0000000..9f1cb2f --- /dev/null +++ b/text.h @@ -0,0 +1,162 @@ +/* -*- C -*- + * 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. + */ + +#ifndef EB_TEXT_H +#define EB_TEXT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "defs.h" + +/* + * Hook codes. + * (When you add or remove a hook, update EB_NUMER_OF_HOOKS in defs.h.) + */ +#define EB_HOOK_NULL -1 +#define EB_HOOK_INITIALIZE 0 +#define EB_HOOK_BEGIN_NARROW 1 +#define EB_HOOK_END_NARROW 2 +#define EB_HOOK_BEGIN_SUBSCRIPT 3 +#define EB_HOOK_END_SUBSCRIPT 4 + +#define EB_HOOK_SET_INDENT 5 +#define EB_HOOK_NEWLINE 6 +#define EB_HOOK_BEGIN_SUPERSCRIPT 7 +#define EB_HOOK_END_SUPERSCRIPT 8 +#define EB_HOOK_BEGIN_NO_NEWLINE 9 + +#define EB_HOOK_END_NO_NEWLINE 10 +#define EB_HOOK_BEGIN_EMPHASIS 11 +#define EB_HOOK_END_EMPHASIS 12 +#define EB_HOOK_BEGIN_CANDIDATE 13 +#define EB_HOOK_END_CANDIDATE_GROUP 14 + +#define EB_HOOK_END_CANDIDATE_LEAF 15 +#define EB_HOOK_BEGIN_REFERENCE 16 +#define EB_HOOK_END_REFERENCE 17 +#define EB_HOOK_BEGIN_KEYWORD 18 +#define EB_HOOK_END_KEYWORD 19 + +#define EB_HOOK_NARROW_FONT 20 +#define EB_HOOK_WIDE_FONT 21 +#define EB_HOOK_ISO8859_1 22 +#define EB_HOOK_NARROW_JISX0208 23 +#define EB_HOOK_WIDE_JISX0208 24 + +#define EB_HOOK_GB2312 25 +#define EB_HOOK_BEGIN_MONO_GRAPHIC 26 +#define EB_HOOK_END_MONO_GRAPHIC 27 +#define EB_HOOK_BEGIN_GRAY_GRAPHIC 28 +#define EB_HOOK_END_GRAY_GRAPHIC 29 + +#define EB_HOOK_BEGIN_COLOR_BMP 30 +#define EB_HOOK_BEGIN_COLOR_JPEG 31 +#define EB_HOOK_BEGIN_IN_COLOR_BMP 32 +#define EB_HOOK_BEGIN_IN_COLOR_JPEG 33 +#define EB_HOOK_END_COLOR_GRAPHIC 34 + +#define EB_HOOK_END_IN_COLOR_GRAPHIC 35 +#define EB_HOOK_BEGIN_WAVE 36 +#define EB_HOOK_END_WAVE 37 +#define EB_HOOK_BEGIN_MPEG 38 +#define EB_HOOK_END_MPEG 39 + +#define EB_HOOK_BEGIN_GRAPHIC_REFERENCE 40 +#define EB_HOOK_END_GRAPHIC_REFERENCE 41 +#define EB_HOOK_GRAPHIC_REFERENCE 42 +#define EB_HOOK_BEGIN_DECORATION 43 +#define EB_HOOK_END_DECORATION 44 + +#define EB_HOOK_BEGIN_IMAGE_PAGE 45 +#define EB_HOOK_END_IMAGE_PAGE 46 +#define EB_HOOK_BEGIN_CLICKABLE_AREA 47 +#define EB_HOOK_END_CLICKABLE_AREA 48 + +#define EB_HOOK_BEGIN_UNICODE 49 +#define EB_HOOK_END_UNICODE 50 +#define EB_HOOK_BEGIN_EBXAC_GAIJI 51 +#define EB_HOOK_END_EBXAC_GAIJI 52 +#define EB_HOOK_EBXAC_GAIJI 53 + +/* + * Function declarations. + */ +/* hook.c */ +void eb_initialize_hookset(EB_Hookset *hookset); +void eb_finalize_hookset(EB_Hookset *hookset); +EB_Error_Code eb_set_hook(EB_Hookset *hookset, const EB_Hook *hook); +EB_Error_Code eb_set_hooks(EB_Hookset *hookset, const EB_Hook *hook); +EB_Error_Code eb_hook_euc_to_ascii(EB_Book *book, EB_Appendix *appendix, + void *container, EB_Hook_Code hook_code, int argc, + const unsigned int *argv); +EB_Error_Code eb_hook_stop_code(EB_Book *book, EB_Appendix *appendix, + void *container, EB_Hook_Code hook_code, int argc, + const unsigned int *argv); +EB_Error_Code eb_hook_narrow_character_text(EB_Book *book, + EB_Appendix *appendix, void *container, EB_Hook_Code hook_code, int argc, + const unsigned int *argv); +EB_Error_Code eb_hook_wide_character_text(EB_Book *book, + EB_Appendix *appendix, void *container, EB_Hook_Code hook_code, int argc, + const unsigned int *argv); +EB_Error_Code eb_hook_newline(EB_Book *book, EB_Appendix *appendix, + void *container, EB_Hook_Code hook_code, int argc, + const unsigned int *argv); +EB_Error_Code eb_hook_empty(EB_Book *book, EB_Appendix *appendix, + void *container, EB_Hook_Code hook_code, int argc, + const unsigned int *argv); + +/* readtext.c */ +EB_Error_Code eb_seek_text(EB_Book *book, const EB_Position *position); +EB_Error_Code eb_tell_text(EB_Book *book, EB_Position *position); +EB_Error_Code eb_read_text(EB_Book *book, EB_Appendix *appendix, + EB_Hookset *hookset, void *container, size_t text_max_length, char *text, + ssize_t *text_length); +EB_Error_Code eb_read_heading(EB_Book *book, EB_Appendix *appendix, + EB_Hookset *hookset, void *container, size_t text_max_length, char *text, + ssize_t *text_length); +EB_Error_Code eb_read_rawtext(EB_Book *book, size_t text_max_length, + char *text, ssize_t *text_length); +int eb_is_text_stopped(EB_Book *book); +EB_Error_Code eb_write_text_byte1(EB_Book *book, int byte1); +EB_Error_Code eb_write_text_byte2(EB_Book *book, int byte1, int byte2); +EB_Error_Code eb_write_text_string(EB_Book *book, const char *string); +EB_Error_Code eb_write_text(EB_Book *book, const char * stream, + size_t stream_length); +const char *eb_current_candidate(EB_Book *book); +EB_Error_Code eb_forward_text(EB_Book *book, EB_Appendix *appendix); +EB_Error_Code eb_backward_text(EB_Book *book, EB_Appendix *appendix); + +#ifdef __cplusplus +} +#endif + +#endif /* not EB_TEXT_H */ diff --git a/widealt.c b/widealt.c new file mode 100644 index 0000000..9f92379 --- /dev/null +++ b/widealt.c @@ -0,0 +1,627 @@ +/* automatically generated from narwalt.c. */ +/* + * 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 "appendix.h" +#include "build-post.h" + +/* + * Unexported functions. + */ +static EB_Error_Code eb_wide_character_text_jis(EB_Appendix *appendix, + int character_number, char *text); +static EB_Error_Code eb_wide_character_text_latin(EB_Appendix *appendix, + int character_number, char *text); + +/* + * Hash macro for cache data. + */ +#define EB_HASH_ALT_CACHE(c) ((c) & 0x0f) + + +/* + * Examine whether the current subbook in `book' has a wide font + * alternation or not. + */ +int +eb_have_wide_alt(EB_Appendix *appendix) +{ + eb_lock(&appendix->lock); + LOG(("in: eb_have_wide_alt(appendix=%d)", (int)appendix->code)); + + /* + * Current subbook must have been set. + */ + if (appendix->subbook_current == NULL) + goto failed; + + if (appendix->subbook_current->wide_page == 0) + goto failed; + + LOG(("out: eb_have_wide_alt() = %d", 1)); + eb_unlock(&appendix->lock); + + return 1; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_have_wide_alt() = %d", 0)); + eb_unlock(&appendix->lock); + return 0; +} + + +/* + * Look up the character number of the start of the wide font alternation + * of the current subbook in `book'. + */ +EB_Error_Code +eb_wide_alt_start(EB_Appendix *appendix, int *start) +{ + EB_Error_Code error_code; + + eb_lock(&appendix->lock); + LOG(("in: eb_wide_alt_start(appendix=%d)", (int)appendix->code)); + + /* + * Current subbook must have been set. + */ + if (appendix->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_APPSUB; + goto failed; + } + + if (appendix->subbook_current->wide_page == 0) { + error_code = EB_ERR_NO_ALT; + goto failed; + } + + *start = appendix->subbook_current->wide_start; + + LOG(("out: eb_wide_alt_start(start=%d) = %s", *start, + eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *start = -1; + LOG(("out: eb_wide_alt_start() = %s", eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} + + +/* + * Return the character number of the end of the wide font alternation + * of the current subbook in `book'. + */ +EB_Error_Code +eb_wide_alt_end(EB_Appendix *appendix, int *end) +{ + EB_Error_Code error_code; + + eb_lock(&appendix->lock); + LOG(("in: eb_wide_alt_end(appendix=%d)", (int)appendix->code)); + + /* + * Current subbook must have been set. + */ + if (appendix->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_APPSUB; + goto failed; + } + + if (appendix->subbook_current->wide_page == 0) { + error_code = EB_ERR_NO_ALT; + goto failed; + } + + *end = appendix->subbook_current->wide_end; + + LOG(("out: eb_wide_alt_end(end=%d) = %s", *end, + eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *end = -1; + LOG(("out: eb_wide_alt_end() = %s", eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} + + +/* + * Get the alternation text of the character number `character_number'. + */ +EB_Error_Code +eb_wide_alt_character_text(EB_Appendix *appendix, int character_number, + char *text) +{ + EB_Error_Code error_code; + + eb_lock(&appendix->lock); + LOG(("in: eb_wide_alt_character_text(appendix=%d, character_number=%d)", + (int)appendix->code, character_number)); + + /* + * Current subbook must have been set. + */ + if (appendix->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_APPSUB; + goto failed; + } + + /* + * The wide font must exist in the current subbook. + */ + if (appendix->subbook_current->wide_page == 0) { + error_code = EB_ERR_NO_ALT; + goto failed; + } + + if (appendix->subbook_current->character_code == EB_CHARCODE_ISO8859_1) { + error_code = eb_wide_character_text_latin(appendix, + character_number, text); + } else { + error_code = eb_wide_character_text_jis(appendix, character_number, + text); + } + if (error_code != EB_SUCCESS) + goto failed; + + LOG(("out: eb_wide_alt_character_text(text=%s) = %s", + eb_quoted_string(text), eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *text = '\0'; + LOG(("out: eb_wide_alt_character_text() = %s", + eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} + + +/* + * Get the alternation text of the character number `character_number'. + */ +static EB_Error_Code +eb_wide_character_text_jis(EB_Appendix *appendix, int character_number, + char *text) +{ + EB_Error_Code error_code; + int start; + int end; + off_t location; + EB_Alternation_Cache *cachep; + + LOG(("in: eb_wide_alt_character_text_jis(appendix=%d, \ +character_number=%d)", + (int)appendix->code, character_number)); + + start = appendix->subbook_current->wide_start; + end = appendix->subbook_current->wide_end; + + /* + * Check for `character_number'. Is it in a font? + * This test works correctly even when the font doesn't exist in + * the current subbook because `start' and `end' have set to -1 + * in the case. + */ + if (character_number < start + || end < character_number + || (character_number & 0xff) < 0x21 + || 0x7e < (character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + + /* + * Calculate the location of alternation data. + */ + location + = (appendix->subbook_current->wide_page - 1) * EB_SIZE_PAGE + + (((character_number >> 8) - (start >> 8)) * 0x5e + + (character_number & 0xff) - (start & 0xff)) + * (EB_MAX_ALTERNATION_TEXT_LENGTH + 1); + + /* + * Check for the cache data. + */ + cachep = appendix->wide_cache + EB_HASH_ALT_CACHE(character_number); + if (cachep->character_number == character_number) { + memcpy(text, cachep->text, EB_MAX_ALTERNATION_TEXT_LENGTH + 1); + goto succeeded; + } + + /* + * Read the alternation data. + */ + if (zio_lseek(&appendix->subbook_current->zio, location, SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_APP; + goto failed; + } + cachep->character_number = -1; + if (zio_read(&appendix->subbook_current->zio, cachep->text, + EB_MAX_ALTERNATION_TEXT_LENGTH + 1) + != EB_MAX_ALTERNATION_TEXT_LENGTH + 1) { + error_code = EB_ERR_FAIL_READ_APP; + goto failed; + } + + /* + * Update cache data. + */ + memcpy(text, cachep->text, EB_MAX_ALTERNATION_TEXT_LENGTH + 1); + cachep->text[EB_MAX_ALTERNATION_TEXT_LENGTH] = '\0'; + cachep->character_number = character_number; + + succeeded: + LOG(("out: eb_wide_alt_character_text_jis(text=%s) = %s", + eb_quoted_string(text), eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *text = '\0'; + LOG(("out: eb_wide_alt_character_text_jis() = %s", + eb_error_string(error_code))); + return error_code; +} + + +/* + * Get the alternation text of the character number `character_number'. + */ +static EB_Error_Code +eb_wide_character_text_latin(EB_Appendix *appendix, int character_number, + char *text) +{ + EB_Error_Code error_code; + int start; + int end; + off_t location; + EB_Alternation_Cache *cache_p; + + LOG(("in: eb_wide_alt_character_text_latin(appendix=%d, \ +character_number=%d)", + (int)appendix->code, character_number)); + + start = appendix->subbook_current->wide_start; + end = appendix->subbook_current->wide_end; + + /* + * Check for `character_number'. Is it in a font? + * This test works correctly even when the font doesn't exist in + * the current subbook because `start' and `end' have set to -1 + * in the case. + */ + if (character_number < start + || end < character_number + || (character_number & 0xff) < 0x01 + || 0xfe < (character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + + /* + * Calculate the location of alternation data. + */ + location + = (appendix->subbook_current->wide_page - 1) * EB_SIZE_PAGE + + (((character_number >> 8) - (start >> 8)) * 0xfe + + (character_number & 0xff) - (start & 0xff)) + * (EB_MAX_ALTERNATION_TEXT_LENGTH + 1); + + /* + * Check for the cache data. + */ + cache_p = appendix->wide_cache + EB_HASH_ALT_CACHE(character_number); + if (cache_p->character_number == character_number) { + memcpy(text, cache_p->text, EB_MAX_ALTERNATION_TEXT_LENGTH + 1); + goto succeeded; + } + + /* + * Read the alternation data. + */ + if (zio_lseek(&appendix->subbook_current->zio, location, SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_APP; + goto failed; + } + cache_p->character_number = -1; + if (zio_read(&appendix->subbook_current->zio, cache_p->text, + EB_MAX_ALTERNATION_TEXT_LENGTH + 1) + != EB_MAX_ALTERNATION_TEXT_LENGTH + 1) { + error_code = EB_ERR_FAIL_READ_APP; + goto failed; + } + + /* + * Update cache data. + */ + memcpy(text, cache_p->text, EB_MAX_ALTERNATION_TEXT_LENGTH + 1); + cache_p->text[EB_MAX_ALTERNATION_TEXT_LENGTH] = '\0'; + cache_p->character_number = character_number; + + succeeded: + LOG(("out: eb_wide_alt_character_text_latin(text=%s) = %s", + eb_quoted_string(text), eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *text = '\0'; + LOG(("out: eb_wide_alt_character_text_latin() = %s", + eb_error_string(error_code))); + return error_code; +} + + +/* + * Return next `n'th character number from `*character_number'. + */ +EB_Error_Code +eb_forward_wide_alt_character(EB_Appendix *appendix, int n, + int *character_number) +{ + EB_Error_Code error_code; + int start; + int end; + int i; + + if (n < 0) { + return eb_backward_wide_alt_character(appendix, -n, + character_number); + } + + eb_lock(&appendix->lock); + LOG(("in: eb_forward_wide_alt_character(appendix=%d, n=%d, \ +character_number=%d)", + (int)appendix->code, n, *character_number)); + + /* + * Current subbook must have been set. + */ + if (appendix->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_APPSUB; + goto failed; + } + + /* + * The wide font must exist in the current subbook. + */ + if (appendix->subbook_current->wide_page == 0) { + error_code = EB_ERR_NO_ALT; + goto failed; + } + + start = appendix->subbook_current->wide_start; + end = appendix->subbook_current->wide_end; + + if (appendix->subbook_current->character_code == EB_CHARCODE_ISO8859_1) { + /* + * Check for `*character_number'. (ISO 8859 1) + */ + if (*character_number < start + || end < *character_number + || (*character_number & 0xff) < 0x01 + || 0xfe < (*character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + + /* + * Get character number. (ISO 8859 1) + */ + for (i = 0; i < n; i++) { + if (0xfe <= (*character_number & 0xff)) + *character_number += 3; + else + *character_number += 1; + if (end < *character_number) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + } + } else { + /* + * Check for `*character_number'. (JIS X 0208) + */ + if (*character_number < start + || end < *character_number + || (*character_number & 0xff) < 0x21 + || 0x7e < (*character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + + /* + * Get character number. (JIS X 0208) + */ + for (i = 0; i < n; i++) { + if (0x7e <= (*character_number & 0xff)) + *character_number += 0xa3; + else + *character_number += 1; + if (end < *character_number) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + } + } + + LOG(("out: eb_forkward_wide_alt_character(character_number=%d) = %s", + *character_number, eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *character_number = -1; + LOG(("out: eb_forward_wide_alt_character() = %s", + eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} + + +/* + * Return previous `n'th character number from `*character_number'. + */ +EB_Error_Code +eb_backward_wide_alt_character(EB_Appendix *appendix, int n, + int *character_number) +{ + EB_Error_Code error_code; + int start; + int end; + int i; + + if (n < 0) { + return eb_forward_wide_alt_character(appendix, -n, character_number); + } + + eb_lock(&appendix->lock); + LOG(("in: eb_backward_wide_alt_character(appendix=%d, n=%d, \ +character_number=%d)", + (int)appendix->code, n, *character_number)); + + /* + * Current subbook must have been set. + */ + if (appendix->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_APPSUB; + goto failed; + } + + /* + * The wide font must exist in the current subbook. + */ + if (appendix->subbook_current->wide_page == 0) { + error_code = EB_ERR_NO_ALT; + goto failed; + } + + start = appendix->subbook_current->wide_start; + end = appendix->subbook_current->wide_end; + + if (appendix->subbook_current->character_code == EB_CHARCODE_ISO8859_1) { + /* + * Check for `*character_number'. (ISO 8859 1) + */ + if (*character_number < start + || end < *character_number + || (*character_number & 0xff) < 0x01 + || 0xfe < (*character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + + /* + * Get character number. (ISO 8859 1) + */ + for (i = 0; i < n; i++) { + if ((*character_number & 0xff) <= 0x01) + *character_number -= 3; + else + *character_number -= 1; + if (*character_number < start) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + } + } else { + /* + * Check for `*character_number'. (JIS X 0208) + */ + if (*character_number < start + || end < *character_number + || (*character_number & 0xff) < 0x21 + || 0x7e < (*character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + + /* + * Get character number. (JIS X 0208) + */ + for (i = 0; i < n; i++) { + if ((*character_number & 0xff) <= 0x21) + *character_number -= 0xa3; + else + *character_number -= 1; + if (*character_number < start) { + error_code = EB_ERR_NO_SUCH_CHAR_TEXT; + goto failed; + } + } + } + + LOG(("out: eb_backward_wide_alt_character(character_number=%d) = %s", + *character_number, eb_error_string(EB_SUCCESS))); + eb_unlock(&appendix->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *character_number = -1; + LOG(("out: eb_backward_wide_alt_character() = %s", + eb_error_string(error_code))); + eb_unlock(&appendix->lock); + return error_code; +} diff --git a/widefont.c b/widefont.c new file mode 100644 index 0000000..707f787 --- /dev/null +++ b/widefont.c @@ -0,0 +1,1098 @@ +/* automatically generated from narwfont.c. */ +/* + * 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" + +/* + * Unexported functions. + */ +static EB_Error_Code eb_wide_character_bitmap_jis(EB_Book *book, + int character_number, char *bitmap); +static EB_Error_Code eb_wide_character_bitmap_latin(EB_Book *book, + int character_number, char *bitmap); + + +/* + * Open a font file. + */ +EB_Error_Code +eb_open_wide_font_file(EB_Book *book, EB_Font_Code font_code) +{ + EB_Error_Code error_code; + EB_Subbook *subbook; + EB_Font *wide_font; + char font_path_name[EB_MAX_PATH_LENGTH + 1]; + Zio_Code zio_code; + + LOG(("in: eb_open_wide_font(book=%d, font_code=%d)", + (int)book->code, (int)font_code)); + + subbook = book->subbook_current; + wide_font = subbook->wide_fonts + font_code; + + if (wide_font->font_code == EB_FONT_INVALID) { + error_code = EB_ERR_FAIL_OPEN_FONT; + goto failed; + } + if (0 <= zio_file(&wide_font->zio)) + goto succeeded; + + /* + * If the book is EBWING, open the wide font file. + * (In EB books, font data are stored in the `START' file.) + */ + zio_code = ZIO_INVALID; + + if (book->disc_code == EB_DISC_EB) { + if (wide_font->initialized) { + if (zio_mode(&wide_font->zio) != ZIO_INVALID) + zio_code = ZIO_REOPEN; + } else { + zio_code = zio_mode(&subbook->text_zio); + } + eb_compose_path_name2(book->path, subbook->directory_name, + subbook->text_file_name, font_path_name); + + } else { + if (wide_font->initialized) { + if (zio_mode(&wide_font->zio) != ZIO_INVALID) + zio_code = ZIO_REOPEN; + eb_compose_path_name3(book->path, subbook->directory_name, + subbook->gaiji_directory_name, wide_font->file_name, + font_path_name); + } else { + eb_canonicalize_file_name(wide_font->file_name); + if (eb_find_file_name3(book->path, subbook->directory_name, + subbook->gaiji_directory_name, wide_font->file_name, + wide_font->file_name) != EB_SUCCESS) { + error_code = EB_ERR_FAIL_OPEN_FONT; + goto failed; + } + + eb_compose_path_name3(book->path, subbook->directory_name, + subbook->gaiji_directory_name, wide_font->file_name, + font_path_name); + eb_path_name_zio_code(font_path_name, ZIO_PLAIN, &zio_code); + } + } + + if (zio_code != ZIO_INVALID + && zio_open(&wide_font->zio, font_path_name, zio_code) < 0) { + error_code = EB_ERR_FAIL_OPEN_FONT; + goto failed; + } + + succeeded: + LOG(("out: eb_open_wide_font_file(file=%d) = %s", + zio_file(&wide_font->zio), eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_open_wide_font_file() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Read font header. + */ +EB_Error_Code +eb_load_wide_font_header(EB_Book *book, EB_Font_Code font_code) +{ + EB_Error_Code error_code; + EB_Subbook *subbook; + EB_Font *wide_font; + char buffer[16]; + int character_count; + Zio *zio; + + LOG(("in: eb_load_wide_font_header(book=%d, font_code=%d)", + (int)book->code, (int)font_code)); + + subbook = book->subbook_current; + wide_font = subbook->wide_fonts + font_code; + zio = &wide_font->zio; + + if (wide_font->initialized) + goto succeeded; + + /* + * Read information from the text file. + */ + if (zio_lseek(zio, ((off_t) wide_font->page - 1) * EB_SIZE_PAGE, + SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_FONT; + goto failed; + } + if (zio_read(zio, buffer, 16) != 16) { + error_code = EB_ERR_FAIL_READ_FONT; + goto failed; + } + + /* + * If the number of characters (`character_count') is 0, the font + * is unavailable. We return EB_ERR_NO_SUCH_FONT. + */ + character_count = eb_uint2(buffer + 12); + if (character_count == 0) { + zio_close(zio); + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + /* + * Set the information. + */ + wide_font->start = eb_uint2(buffer + 10); + if (book->character_code == EB_CHARCODE_ISO8859_1) { + wide_font->end = wide_font->start + + ((character_count / 0xfe) << 8) + (character_count % 0xfe) - 1; + if (0xfe < (wide_font->end & 0xff)) + wide_font->end += 3; + } else { + wide_font->end = wide_font->start + + ((character_count / 0x5e) << 8) + (character_count % 0x5e) - 1; + if (0x7e < (wide_font->end & 0xff)) + wide_font->end += 0xa3; + } + + if (book->character_code == EB_CHARCODE_ISO8859_1) { + if ((wide_font->start & 0xff) < 0x01 + || 0xfe < (wide_font->start & 0xff) + || wide_font->start < 0x0001 + || 0x1efe < wide_font->end) { + error_code = EB_ERR_UNEXP_FONT; + goto failed; + } + } else { + if ((wide_font->start & 0xff) < 0x21 + || 0x7e < (wide_font->start & 0xff) + || wide_font->start < 0xa121 + || 0xfe7e < wide_font->end) { + error_code = EB_ERR_UNEXP_FONT; + goto failed; + } + } + + succeeded: + LOG(("out: eb_load_wide_font_header()", eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_load_wide_font_header()", eb_error_string(error_code))); + return error_code; +} + + +/* + * Read font glyph data. + */ +EB_Error_Code +eb_load_wide_font_glyphs(EB_Book *book, EB_Font_Code font_code) +{ + EB_Error_Code error_code; + EB_Subbook *subbook; + EB_Font *wide_font; + int character_count; + size_t glyph_size; + size_t total_glyph_size; + Zio *zio; + + LOG(("in: eb_load_wide_font_glyphs(book=%d, font_code=%d)", + (int)book->code, (int)font_code)); + + subbook = book->subbook_current; + wide_font = subbook->wide_fonts + font_code; + zio = &wide_font->zio; + + if (wide_font->glyphs != NULL) + goto succeeded; + + /* + * Calculate size of glyph data (`total_glyph_size'). + * + * Set the number of local defined characters to `character_count'. + * Set the number of character glpyhs in a page to `page_glyph_count'. + * Set size of glyph data to `total_glyph_size'. + */ + if (book->character_code == EB_CHARCODE_ISO8859_1) { + character_count + = ((wide_font->end >> 8) - (wide_font->start >> 8)) * 0xfe + + ((wide_font->end & 0xff) - (wide_font->start & 0xff)) + 1; + } else { + character_count + = ((wide_font->end >> 8) - (wide_font->start >> 8)) * 0x5e + + ((wide_font->end & 0xff) - (wide_font->start & 0xff)) + 1; + } + + eb_wide_font_size2(font_code, &glyph_size); + total_glyph_size + = (character_count / (1024 / glyph_size)) * 1024 + + (character_count % (1024 / glyph_size)) * glyph_size; + + /* + * Allocate memory for glyph data. + */ + wide_font->glyphs = (char *) malloc(total_glyph_size); + if (wide_font->glyphs == NULL) { + error_code = EB_ERR_MEMORY_EXHAUSTED; + goto failed; + } + + /* + * Read glyphs. + */ + if (zio_lseek(zio, (off_t) wide_font->page * EB_SIZE_PAGE, SEEK_SET) + < 0) { + error_code = EB_ERR_FAIL_SEEK_FONT; + goto failed; + } + if (zio_read(zio, wide_font->glyphs, total_glyph_size) + != total_glyph_size) { + error_code = EB_ERR_FAIL_READ_FONT; + goto failed; + } + + succeeded: + LOG(("out: eb_load_wide_font_glyphs()", eb_error_string(EB_SUCCESS))); + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_load_wide_font_glyphs()", eb_error_string(error_code))); + if (wide_font->glyphs != NULL) { + free(wide_font->glyphs); + wide_font->glyphs = NULL; + } + return error_code; +} + + +/* + * Examine whether the current subbook in `book' has a wide font. + */ +int +eb_have_wide_font(EB_Book *book) +{ + int i; + + eb_lock(&book->lock); + LOG(("in: eb_have_wide_font(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) + goto failed; + + /* + * If the wide font has already set, the subbook has wide fonts. + */ + if (book->subbook_current->wide_current != NULL) + goto succeeded; + + /* + * Scan the font table. + */ + for (i = 0; i < EB_MAX_FONTS; i++) { + if (book->subbook_current->wide_fonts[i].font_code + != EB_FONT_INVALID) + break; + } + + if (EB_MAX_FONTS <= i) + goto failed; + + succeeded: + LOG(("out: eb_have_wide_font() = %d", 1)); + eb_unlock(&book->lock); + return 1; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_have_wide_font() = %d", 0)); + eb_unlock(&book->lock); + return 0; +} + + +/* + * Get width of the font `font_code' in the current subbook of `book'. + */ +EB_Error_Code +eb_wide_font_width(EB_Book *book, int *width) +{ + EB_Error_Code error_code; + EB_Font_Code font_code; + + eb_lock(&book->lock); + LOG(("in: eb_wide_font_width(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * The wide font must exist in the current subbook. + */ + if (book->subbook_current->wide_current == NULL) { + error_code = EB_ERR_NO_CUR_FONT; + goto failed; + } + + /* + * Calculate width. + */ + font_code = book->subbook_current->wide_current->font_code; + error_code = eb_wide_font_width2(font_code, width); + if (error_code != EB_SUCCESS) + goto failed; + + LOG(("out: eb_wide_font_width(width=%d) = %s", (int)*width, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *width = 0; + LOG(("out: eb_wide_font_width() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Get width of the font `font_code'. + */ +EB_Error_Code +eb_wide_font_width2(EB_Font_Code font_code, int *width) +{ + EB_Error_Code error_code; + + LOG(("in: eb_wide_font_width2(font_code=%d)", (int)font_code)); + + switch (font_code) { + case EB_FONT_16: + *width = EB_WIDTH_WIDE_FONT_16; + break; + case EB_FONT_24: + *width = EB_WIDTH_WIDE_FONT_24; + break; + case EB_FONT_30: + *width = EB_WIDTH_WIDE_FONT_30; + break; + case EB_FONT_48: + *width = EB_WIDTH_WIDE_FONT_48; + break; + default: + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + LOG(("out: eb_wide_font_width2(width=%d) = %s", *width, + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *width = 0; + LOG(("out: eb_wide_font_width2() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Get the bitmap size of the font `font_code' in the current subbook + * of `book'. + */ +EB_Error_Code +eb_wide_font_size(EB_Book *book, size_t *size) +{ + EB_Error_Code error_code; + EB_Font_Code font_code; + int width; + int height; + + eb_lock(&book->lock); + LOG(("in: eb_wide_font_size(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * The wide font must exist in the current subbook. + */ + if (book->subbook_current->wide_current == NULL) { + error_code = EB_ERR_NO_CUR_FONT; + goto failed; + } + + /* + * Calculate size. + */ + font_code = book->subbook_current->wide_current->font_code; + error_code = eb_wide_font_width2(font_code, &width); + if (error_code != EB_SUCCESS) + goto failed; + error_code = eb_font_height2(font_code, &height); + if (error_code != EB_SUCCESS) + goto failed; + *size = (width / 8) * height; + + LOG(("out: eb_wide_font_size(size=%ld) = %s", (long)*size, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *size = 0; + LOG(("out: eb_wide_font_size() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Get the bitmap size of a character in `font_code' of the current + * subbook. + */ +EB_Error_Code +eb_wide_font_size2(EB_Font_Code font_code, size_t *size) +{ + EB_Error_Code error_code; + + LOG(("in: eb_wide_font_size2(font_code=%d)", (int)font_code)); + + switch (font_code) { + case EB_FONT_16: + *size = EB_SIZE_WIDE_FONT_16; + break; + case EB_FONT_24: + *size = EB_SIZE_WIDE_FONT_24; + break; + case EB_FONT_30: + *size = EB_SIZE_WIDE_FONT_30; + break; + case EB_FONT_48: + *size = EB_SIZE_WIDE_FONT_48; + break; + default: + error_code = EB_ERR_NO_SUCH_FONT; + goto failed; + } + + LOG(("out: eb_wide_font_size2(size=%ld) = %s", (long)*size, + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *size = 0; + LOG(("out: eb_wide_font_size2() = %s", eb_error_string(error_code))); + return error_code; +} + + +/* + * Get the character number of the start of the wide font of the current + * subbook in `book'. + */ +EB_Error_Code +eb_wide_font_start(EB_Book *book, int *start) +{ + EB_Error_Code error_code; + + eb_lock(&book->lock); + LOG(("in: eb_wide_font_start(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * The wide font must exist in the current subbook. + */ + if (book->subbook_current->wide_current == NULL) { + error_code = EB_ERR_NO_CUR_FONT; + goto failed; + } + + *start = book->subbook_current->wide_current->start; + + LOG(("out: eb_wide_font_start(start=%d) = %s", *start, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_wide_font_start() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Get the character number of the end of the wide font of the current + * subbook in `book'. + */ +EB_Error_Code +eb_wide_font_end(EB_Book *book, int *end) +{ + EB_Error_Code error_code; + + eb_lock(&book->lock); + LOG(("in: eb_wide_font_end(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * The wide font must exist in the current subbook. + */ + if (book->subbook_current->wide_current == NULL) { + error_code = EB_ERR_NO_CUR_FONT; + goto failed; + } + + *end = book->subbook_current->wide_current->end; + + LOG(("out: eb_wide_font_end(end=%d) = %s", *end, + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_wide_font_end() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Get bitmap data of the character with character number `character_number' + * in the current wide font of the current subbook in `book'. + */ +EB_Error_Code +eb_wide_font_character_bitmap(EB_Book *book, int character_number, + char *bitmap) +{ + EB_Error_Code error_code; + + eb_lock(&book->lock); + LOG(("in: eb_wide_font_character_bitmap(book=%d, character_number=%d)", + (int)book->code, character_number)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * The wide font must exist in the current subbook. + */ + if (book->subbook_current->wide_current == NULL) { + error_code = EB_ERR_NO_CUR_FONT; + goto failed; + } + + if (book->character_code == EB_CHARCODE_ISO8859_1) { + error_code = eb_wide_character_bitmap_latin(book, character_number, + bitmap); + } else { + error_code = eb_wide_character_bitmap_jis(book, character_number, + bitmap); + } + if (error_code != EB_SUCCESS) + goto failed; + + LOG(("out: eb_wide_font_character_bitmap() = %s", + eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *bitmap = '\0'; + LOG(("out: eb_wide_font_character_bitmap() = %s", + eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Get bitmap data of the character with character number `character_number' + * in the current wide font of the current subbook in `book'. + */ +static EB_Error_Code +eb_wide_character_bitmap_jis(EB_Book *book, int character_number, + char *bitmap) +{ + EB_Error_Code error_code; + EB_Font *wide_current; + int start; + int end; + int character_index; + off_t offset; + size_t size; + Zio *zio; + + LOG(("in: eb_wide_font_character_bitmap_jis(book=%d, \ +character_number=%d)", + (int)book->code, character_number)); + + start = book->subbook_current->wide_current->start; + end = book->subbook_current->wide_current->end; + wide_current = book->subbook_current->wide_current; + + /* + * Check for `character_number'. Is it in a range of bitmaps? + * This test works correctly even when the font doesn't exist in + * the current subbook because `start' and `end' have set to -1 + * in the case. + */ + if (character_number < start + || end < character_number + || (character_number & 0xff) < 0x21 + || 0x7e < (character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + + /* + * Calculate the size and the location of bitmap data. + */ + error_code = eb_wide_font_size(book, &size); + if (error_code != EB_SUCCESS) + goto failed; + + character_index = ((character_number >> 8) - (start >> 8)) * 0x5e + + ((character_number & 0xff) - (start & 0xff)); + offset + = (character_index / (1024 / size)) * 1024 + + (character_index % (1024 / size)) * size; + + /* + * Read bitmap data. + */ + if (wide_current->glyphs == NULL) { + zio = &wide_current->zio; + + if (zio_lseek(zio, + (off_t) wide_current->page * EB_SIZE_PAGE + offset, + SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_FONT; + goto failed; + } + if (zio_read(zio, bitmap, size) != size) { + error_code = EB_ERR_FAIL_READ_FONT; + goto failed; + } + } else { + memcpy(bitmap, wide_current->glyphs + offset, size); + } + + LOG(("out: eb_wide_font_character_bitmap_jis() = %s", + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *bitmap = '\0'; + LOG(("out: eb_wide_font_character_bitmap_jis() = %s", + eb_error_string(error_code))); + return error_code; +} + + +/* + * Get bitmap data of the character with character number `character_number' + * in the current wide font of the current subbook in `book'. + */ +static EB_Error_Code +eb_wide_character_bitmap_latin(EB_Book *book, int character_number, + char *bitmap) +{ + EB_Error_Code error_code; + EB_Font *wide_current; + int start; + int end; + int character_index; + off_t offset; + size_t size; + Zio *zio; + + LOG(("in: eb_wide_font_character_bitmap_latin(book=%d, \ +character_number=%d)", + (int)book->code, character_number)); + + start = book->subbook_current->wide_current->start; + end = book->subbook_current->wide_current->end; + wide_current = book->subbook_current->wide_current; + + /* + * Check for `ch'. Is it in a range of bitmaps? + * This test works correctly even when the font doesn't exist in + * the current subbook because `start' and `end' have set to -1 + * in the case. + */ + if (character_number < start + || end < character_number + || (character_number & 0xff) < 0x01 + || 0xfe < (character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + + /* + * Calculate the size and the location of bitmap data. + */ + error_code = eb_wide_font_size(book, &size); + if (error_code != EB_SUCCESS) + goto failed; + + character_index = ((character_number >> 8) - (start >> 8)) * 0xfe + + ((character_number & 0xff) - (start & 0xff)); + offset + = (character_index / (1024 / size)) * 1024 + + (character_index % (1024 / size)) * size; + + /* + * Read bitmap data. + */ + if (wide_current->glyphs == NULL) { + zio = &wide_current->zio; + + if (zio_lseek(zio, + (off_t) wide_current->page * EB_SIZE_PAGE + offset, + SEEK_SET) < 0) { + error_code = EB_ERR_FAIL_SEEK_FONT; + goto failed; + } + if (zio_read(zio, bitmap, size) != size) { + error_code = EB_ERR_FAIL_READ_FONT; + goto failed; + } + } else { + memcpy(bitmap, wide_current->glyphs + offset, size); + } + + LOG(("out: eb_wide_font_character_bitmap_latin() = %s", + eb_error_string(EB_SUCCESS))); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *bitmap = '\0'; + LOG(("out: eb_wide_font_character_bitmap_latin() = %s", + eb_error_string(error_code))); + return error_code; +} + + +/* + * Return next `n'th character number from `character_number'. + */ +EB_Error_Code +eb_forward_wide_font_character(EB_Book *book, int n, int *character_number) +{ + EB_Error_Code error_code; + int start; + int end; + int i; + + if (n < 0) + return eb_backward_wide_font_character(book, -n, character_number); + + eb_lock(&book->lock); + LOG(("in: eb_forward_wide_font_character(book=%d, n=%d, \ +character_number=%d)", + (int)book->code, n, *character_number)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * The wide font must exist in the current subbook. + */ + if (book->subbook_current->wide_current == NULL) { + error_code = EB_ERR_NO_CUR_FONT; + goto failed; + } + + start = book->subbook_current->wide_current->start; + end = book->subbook_current->wide_current->end; + + if (book->character_code == EB_CHARCODE_ISO8859_1) { + /* + * Check for `*character_number'. (ISO 8859 1) + */ + if (*character_number < start + || end < *character_number + || (*character_number & 0xff) < 0x01 + || 0xfe < (*character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + + /* + * Get character number. (ISO 8859 1) + */ + for (i = 0; i < n; i++) { + if (0xfe <= (*character_number & 0xff)) + *character_number += 3; + else + *character_number += 1; + if (end < *character_number) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + } + } else { + /* + * Check for `*character_number'. (JIS X 0208) + */ + if (*character_number < start + || end < *character_number + || (*character_number & 0xff) < 0x21 + || 0x7e < (*character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + + /* + * Get character number. (JIS X 0208) + */ + for (i = 0; i < n; i++) { + if (0x7e <= (*character_number & 0xff)) + *character_number += 0xa3; + else + *character_number += 1; + if (end < *character_number) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + } + } + + LOG(("out: eb_forward_wide_font_character(character_number=%d) = %s", + *character_number, eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *character_number = -1; + LOG(("out: eb_forward_wide_font_character() = %s", + eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} + + +/* + * Return previous `n'th character number from `*character_number'. + */ +EB_Error_Code +eb_backward_wide_font_character(EB_Book *book, int n, int *character_number) +{ + EB_Error_Code error_code; + int start; + int end; + int i; + + if (n < 0) + return eb_forward_wide_font_character(book, -n, character_number); + + eb_lock(&book->lock); + LOG(("in: eb_backward_wide_font_character(book=%d, n=%d, \ +character_number=%d)", + (int)book->code, n, *character_number)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * The wide font must exist in the current subbook. + */ + if (book->subbook_current->wide_current == NULL) { + error_code = EB_ERR_NO_CUR_FONT; + goto failed; + } + + start = book->subbook_current->wide_current->start; + end = book->subbook_current->wide_current->end; + + if (book->character_code == EB_CHARCODE_ISO8859_1) { + /* + * Check for `*character_number'. (ISO 8859 1) + */ + if (*character_number < start + || end < *character_number + || (*character_number & 0xff) < 0x01 + || 0xfe < (*character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + + /* + * Get character number. (ISO 8859 1) + */ + for (i = 0; i < n; i++) { + if ((*character_number & 0xff) <= 0x01) + *character_number -= 3; + else + *character_number -= 1; + if (*character_number < start) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + } + } else { + /* + * Check for `*character_number'. (JIS X 0208) + */ + if (*character_number < start + || end < *character_number + || (*character_number & 0xff) < 0x21 + || 0x7e < (*character_number & 0xff)) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + + /* + * Get character number. (JIS X 0208) + */ + for (i = 0; i < n; i++) { + if ((*character_number & 0xff) <= 0x21) + *character_number -= 0xa3; + else + *character_number -= 1; + if (*character_number < start) { + error_code = EB_ERR_NO_SUCH_CHAR_BMP; + goto failed; + } + } + } + + LOG(("out: eb_backward_wide_font_character(character_number=%d) = %s", + *character_number, eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + *character_number = -1; + LOG(("out: eb_backward_wide_font_character() = %s", + eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} diff --git a/word.c b/word.c new file mode 100644 index 0000000..8b5bf29 --- /dev/null +++ b/word.c @@ -0,0 +1,185 @@ +/* + * 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 "build-post.h" + +/* + * Examine whether the current subbook in `book' supports `WORD SEARCH' + * or not. + */ +int +eb_have_word_search(EB_Book *book) +{ + eb_lock(&book->lock); + LOG(("in: eb_have_word_search(book=%d)", (int)book->code)); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) + goto failed; + + if (book->subbook_current->word_alphabet.start_page == 0 + && book->subbook_current->word_asis.start_page == 0 + && book->subbook_current->word_kana.start_page == 0) + goto failed; + + LOG(("out: eb_have_word_search() = %d", 1)); + eb_unlock(&book->lock); + + return 1; + + /* + * An error occurs... + */ + failed: + LOG(("out: eb_have_word_search() = %d", 0)); + eb_unlock(&book->lock); + return 0; +} + + +/* + * Word search. + */ +EB_Error_Code +eb_search_word(EB_Book *book, const char *input_word) +{ + EB_Error_Code error_code; + EB_Word_Code word_code; + EB_Search_Context *context; + + eb_lock(&book->lock); + LOG(("in: eb_search_word(book=%d, input_word=%s)", (int)book->code, + eb_quoted_string(input_word))); + + /* + * Current subbook must have been set. + */ + if (book->subbook_current == NULL) { + error_code = EB_ERR_NO_CUR_SUB; + goto failed; + } + + /* + * Initialize search context. + */ + eb_reset_search_contexts(book); + context = book->search_contexts; + context->code = EB_SEARCH_WORD; + + /* + * Make a fixed word and a canonicalized word to search from + * `input_word'. + */ + error_code = eb_set_word(book, input_word, context->word, + context->canonicalized_word, &word_code); + if (error_code != EB_SUCCESS) + goto failed; + + /* + * Get a page number. + */ + switch (word_code) { + case EB_WORD_ALPHABET: + if (book->subbook_current->word_alphabet.start_page != 0) + context->page = book->subbook_current->word_alphabet.start_page; + else if (book->subbook_current->word_asis.start_page != 0) + context->page = book->subbook_current->word_asis.start_page; + else { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + break; + + case EB_WORD_KANA: + if (book->subbook_current->word_kana.start_page != 0) + context->page = book->subbook_current->word_kana.start_page; + else if (book->subbook_current->word_asis.start_page != 0) + context->page = book->subbook_current->word_asis.start_page; + else { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + break; + + case EB_WORD_OTHER: + if (book->subbook_current->word_asis.start_page != 0) + context->page = book->subbook_current->word_asis.start_page; + else { + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + break; + + default: + error_code = EB_ERR_NO_SUCH_SEARCH; + goto failed; + } + + /* + * Choose comparison functions. + */ + if (book->character_code == EB_CHARCODE_ISO8859_1) { + context->compare_pre = eb_pre_match_word; + context->compare_single = eb_match_word; + context->compare_group = eb_match_word; + } else if (context->page == book->subbook_current->word_kana.start_page) { + context->compare_pre = eb_pre_match_word; + context->compare_single = eb_match_word_kana_single; + context->compare_group = eb_match_word_kana_group; + } else { + context->compare_pre = eb_pre_match_word; + context->compare_single = eb_match_word; + context->compare_group = eb_match_word_kana_group; + } + + /* + * Pre-search. + */ + error_code = eb_presearch_word(book, context); + if (error_code != EB_SUCCESS) + goto failed; + + LOG(("out: eb_search_word() = %s", eb_error_string(EB_SUCCESS))); + eb_unlock(&book->lock); + + return EB_SUCCESS; + + /* + * An error occurs... + */ + failed: + eb_reset_search_contexts(book); + LOG(("out: eb_search_word() = %s", eb_error_string(error_code))); + eb_unlock(&book->lock); + return error_code; +} diff --git a/zig.go b/zig.go index a148a94..e9fe07e 100644 --- a/zig.go +++ b/zig.go @@ -10,8 +10,7 @@ import ( ) /* -#cgo LDFLAGS: -L"./eb/eb/.libs" -l:libeb.a -lz -#cgo CFLAGS: -I"./eb/" +#cgo LDFLAGS: -lz #include "zig.h" */ import "C" diff --git a/zig.h b/zig.h index f173efd..18069f1 100644 --- a/zig.h +++ b/zig.h @@ -1,8 +1,8 @@ #include -#include -#include -#include -#include +#include "eb.h" +#include "font.h" +#include "text.h" +#include "error.h" extern EB_Error_Code installHook(EB_Hookset* hookset, EB_Hook_Code code); diff --git a/zio.c b/zio.c new file mode 100644 index 0000000..ea2a448 --- /dev/null +++ b/zio.c @@ -0,0 +1,2014 @@ +/* -*- C -*- + * Copyright (c) 1998-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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zio.h" + +/* + * Flags for open(). + */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +/* + * The maximum length of path name. + */ +#ifndef PATH_MAX +#ifdef MAXPATHLEN +#define PATH_MAX MAXPATHLEN +#else /* not MAXPATHLEN */ +#define PATH_MAX 1024 +#endif /* not MAXPATHLEN */ +#endif /* not PATH_MAX */ + +/* + * Mutual exclusion lock of Pthreads. + */ +#ifndef ENABLE_PTHREAD +#define pthread_mutex_lock(m) +#define pthread_mutex_unlock(m) +#endif + +/* + * Debug message handler. + */ +extern int eb_log_flag; +extern void eb_log(const char *, ...); +#define LOG(x) do {if (eb_log_flag) eb_log x;} while (0) + +/* + * Get an unsigned value from an octet stream buffer. + */ +#define zio_uint1(p) (*(const unsigned char *)(p)) + +#define zio_uint2(p) ((*(const unsigned char *)(p) << 8) \ + + (*(const unsigned char *)((p) + 1))) + +#define zio_uint3(p) ((*(const unsigned char *)(p) << 16) \ + + (*(const unsigned char *)((p) + 1) << 8) \ + + (*(const unsigned char *)((p) + 2))) + +#define zio_uint4(p) (((off_t) *(const unsigned char *)(p) << 24) \ + + (*(const unsigned char *)((p) + 1) << 16) \ + + (*(const unsigned char *)((p) + 2) << 8) \ + + (*(const unsigned char *)((p) + 3))) + +#define zio_uint5(p) (((off_t) (*(const unsigned char *)(p)) << 32) \ + + ((off_t) (*(const unsigned char *)((p) + 1)) << 24) \ + + (*(const unsigned char *)((p) + 2) << 16) \ + + (*(const unsigned char *)((p) + 3) << 8) \ + + (*(const unsigned char *)((p) + 4))) + +/* + * Size of a page (The term `page' means `block' in JIS X 4081). + */ +#define ZIO_SIZE_PAGE 2048 + +/* + * Size of a cache buffer. + * It must be large enough to memory an uncompressed slice. + * + * (In EBZIP and EPWING compressions, the size of uncompressed slice + * is 2048. In S-EBXA compression, the size is 4096.) + */ +#define ZIO_CACHE_BUFFER_SIZE (ZIO_SIZE_PAGE << ZIO_MAX_EBZIP_LEVEL) + +/* + * NULL Zio ID. + */ +#define ZIO_ID_NONE -1 + +/* + * Buffer for caching uncompressed data. + */ +static char *cache_buffer = NULL; + +/* + * Zio ID which caches data in `cache_buffer'. + */ +static int cache_zio_id = ZIO_ID_NONE; + +/* + * Offset of the beginning of the cached data `cache_buffer'. + */ +static off_t cache_location; + +/* + * Zio object counter. + */ +static int zio_counter = 0; + +/* + * Test whether `off_t' represents a large integer. + */ +#define off_t_is_large \ + ((((off_t) 1 << 41) + ((off_t) 1 << 40) + 1) % 9999991 == 7852006) + +/* + * Unexported function. + */ +static int zio_reopen(Zio *zio, const char *file_name); +static int zio_open_plain(Zio *zio, const char *file_name); +static int zio_open_ebzip(Zio *zio, const char *file_name); +static int zio_open_epwing(Zio *zio, const char *file_name); +static int zio_open_epwing6(Zio *zio, const char *file_name); +static int zio_make_epwing_huffman_tree(Zio *zio, int leaf_count); +static ssize_t zio_read_ebzip(Zio *zio, char *buffer, size_t length); +static ssize_t zio_read_epwing(Zio *zio, char *buffer, size_t length); +static ssize_t zio_read_sebxa(Zio *zio, char *buffer, size_t length); +static int zio_unzip_slice_ebzip1(Zio *zio, char *out_buffer, + size_t zipped_slice_size); +static int zio_unzip_slice_epwing(Zio *zio, char *out_buffer); +static int zio_unzip_slice_epwing6(Zio *zio, char *out_buffer); +static int zio_unzip_slice_sebxa(Zio *zio, char *out_buffer); +static int zio_open_raw(Zio *zio, const char *file_name); +static void zio_close_raw(Zio *zio); +static off_t zio_lseek_raw(Zio *zio, off_t offset, int whence); +static ssize_t zio_read_raw(Zio *zio, void *buffer, size_t length); + + +/* + * Initialize cache buffer. + */ +int +zio_initialize_library(void) +{ + pthread_mutex_lock(&zio_mutex); + LOG(("in: zio_initialize_library()")); + + /* + * Allocate memory for cache buffer. + */ + if (cache_buffer == NULL) { + cache_buffer = (char *) malloc(ZIO_CACHE_BUFFER_SIZE); + if (cache_buffer == NULL) + goto failed; + } + + LOG(("out: zio_initialize_library() = %d", 0)); + pthread_mutex_unlock(&zio_mutex); + return 0; + + /* + * An error occurs... + */ + failed: + LOG(("out: zio_initialize_library() = %d", -1)); + pthread_mutex_unlock(&zio_mutex); + return -1; +} + + +/* + * Clear cache buffer. + */ +void +zio_finalize_library(void) +{ + pthread_mutex_lock(&zio_mutex); + LOG(("in: zio_finalize_library()")); + + if (cache_buffer != NULL) + free(cache_buffer); + cache_buffer = NULL; + cache_zio_id = ZIO_ID_NONE; + + LOG(("out: zio_finalize_library()")); + pthread_mutex_unlock(&zio_mutex); +} + + +/* + * Initialize `zio'. + */ +void +zio_initialize(Zio *zio) +{ + LOG(("in: zio_initialize()")); + + zio->id = -1; + zio->file = -1; + zio->huffman_nodes = NULL; + zio->huffman_root = NULL; + zio->code = ZIO_INVALID; + zio->file_size = 0; + + LOG(("out: zio_initialize()")); +} + + +/* + * Finalize `zio'. + */ +void +zio_finalize(Zio *zio) +{ + LOG(("in: zio_finalize(zio=%d)", (int)zio->id)); + + zio_close(zio); + if (zio->huffman_nodes != NULL) + free(zio->huffman_nodes); + + zio->id = -1; + zio->huffman_nodes = NULL; + zio->huffman_root = NULL; + zio->code = ZIO_INVALID; + + LOG(("out: zio_finalize()")); +} + + +/* + * Set S-EBXA compression mode. + */ +int +zio_set_sebxa_mode(Zio *zio, off_t index_location, off_t index_base, + off_t zio_start_location, off_t zio_end_location) +{ + LOG(("in: zio_set_sebxa_mode(zio=%d, index_location=%ld, index_base=%ld, \ +zio_start_location=%ld, zio_end_location=%ld)", + (int)zio->id, (long)index_location, (long)index_base, + (long)zio_start_location, (long)zio_end_location)); + + if (zio->code != ZIO_PLAIN) + goto failed; + + zio->code = ZIO_SEBXA; + zio->index_location = index_location; + zio->index_base = index_base; + zio->zio_start_location = zio_start_location; + zio->zio_end_location = zio_end_location; + zio->file_size = zio_end_location; + + LOG(("out: zio_set_sebxa_mode() = %d", 0)); + return 0; + + /* + * An error occurs... + */ + failed: + LOG(("out: zio_set_sebxa_mode() = %d", -1)); + return -1; +} + +/* + * Open `file'. + */ +int +zio_open(Zio *zio, const char *file_name, Zio_Code zio_code) +{ + int result; + + LOG(("in: zio_open(zio=%d, file_name=%s, zio_code=%d)", + (int)zio->id, file_name, zio_code)); + + if (0 <= zio->file) { + if (zio_code == ZIO_REOPEN) { + result = 0; + goto succeeded; + } + zio_finalize(zio); + zio_initialize(zio); + } + + switch (zio_code) { + case ZIO_REOPEN: + result = zio_reopen(zio, file_name); + break; + case ZIO_PLAIN: + result = zio_open_plain(zio, file_name); + break; + case ZIO_EBZIP1: + result = zio_open_ebzip(zio, file_name); + break; + case ZIO_EPWING: + result = zio_open_epwing(zio, file_name); + break; + case ZIO_EPWING6: + result = zio_open_epwing6(zio, file_name); + break; + case ZIO_SEBXA: + result = zio_open_plain(zio, file_name); + break; + default: + result = -1; + } + + succeeded: + LOG(("out: zio_open() = %d", result)); + return result; +} + + +/* + * Reopen a file. + */ +static int +zio_reopen(Zio *zio, const char *file_name) +{ + LOG(("in: zio_reopen(zio=%d, file_name=%s)", (int)zio->id, file_name)); + + if (zio->code == ZIO_INVALID) + goto failed; + + if (zio_open_raw(zio, file_name) < 0) { + zio->code = ZIO_INVALID; + goto failed; + } + zio->location = 0; + + LOG(("out: zio_reopen() = %d", zio->file)); + return zio->file; + + /* + * An error occurs... + */ + failed: + LOG(("out: zio_reopen() = %d", -1)); + return -1; +} + + +/* + * Open an non-compressed file. + */ +static int +zio_open_plain(Zio *zio, const char *file_name) +{ + LOG(("in: zio_open_plain(zio=%d, file_name=%s)", (int)zio->id, file_name)); + + if (zio_open_raw(zio, file_name) < 0) + goto failed; + + zio->code = ZIO_PLAIN; + zio->slice_size = ZIO_SIZE_PAGE; + zio->file_size = zio_lseek_raw(zio, 0, SEEK_END); + if (zio->file_size < 0 || zio_lseek_raw(zio, 0, SEEK_SET) < 0) + goto failed; + + /* + * Assign ID. + */ + pthread_mutex_lock(&zio_mutex); + zio->id = zio_counter++; + pthread_mutex_unlock(&zio_mutex); + + LOG(("out: zio_open_plain(zio=%d) = %d", (int)zio->id, zio->file)); + return zio->file; + + /* + * An error occurs... + */ + failed: + if (0 <= zio->file) + zio_close_raw(zio); + zio->file = -1; + zio->code = ZIO_INVALID; + + LOG(("out: zio_open_plain() = %d", -1)); + return -1; +} + + +/* + * Open an EBZIP compression file. + */ +static int +zio_open_ebzip(Zio *zio, const char *file_name) +{ + char header[ZIO_SIZE_EBZIP_HEADER]; + int ebzip_mode; + + LOG(("in: zio_open_ebzip(zio=%d, file_name=%s)", (int)zio->id, file_name)); + + /* + * Try to open a `*.ebz' file. + */ + if (zio_open_raw(zio, file_name) < 0) + goto failed; + + /* + * Read header part of the ebzip'ped file. + */ + if (zio_read_raw(zio, header, ZIO_SIZE_EBZIP_HEADER) + != ZIO_SIZE_EBZIP_HEADER) + goto failed; + ebzip_mode = zio_uint1(header + 5) >> 4; + zio->code = ZIO_EBZIP1; + zio->zip_level = zio_uint1(header + 5) & 0x0f; + zio->slice_size = ZIO_SIZE_PAGE << zio->zip_level; + zio->file_size = zio_uint5(header + 9); + zio->crc = zio_uint4(header + 14); + zio->mtime = zio_uint4(header + 18); + zio->location = 0; + + if (zio->file_size < (off_t) 1 << 16) + zio->index_width = 2; + else if (zio->file_size < (off_t) 1 << 24) + zio->index_width = 3; + else if (zio->file_size < (off_t) 1 << 32 || !off_t_is_large) + zio->index_width = 4; + else + zio->index_width = 5; + + /* + * Check zio header information. + */ + if (memcmp(header, "EBZip", 5) != 0 + || ZIO_SIZE_PAGE << ZIO_MAX_EBZIP_LEVEL < zio->slice_size) + goto failed; + + if (off_t_is_large) { + if (ebzip_mode != 1 && ebzip_mode != 2) + goto failed; + } else { + if (ebzip_mode != 1) + goto failed; + } + + /* + * Assign ID. + */ + pthread_mutex_lock(&zio_mutex); + zio->id = zio_counter++; + pthread_mutex_unlock(&zio_mutex); + + LOG(("out: zio_open_ebzip(zio=%d) = %d", (int)zio->id, zio->file)); + return zio->file; + + /* + * An error occurs... + */ + failed: + if (0 <= zio->file) + zio_close_raw(zio); + zio->file = -1; + zio->code = ZIO_INVALID; + LOG(("out: zio_open_ebzip() = %d", -1)); + return -1; +} + + +/* + * The buffer size must be 512 bytes, the number of 8 bit nodes. + */ +#define ZIO_EPWING_BUFFER_SIZE 512 + +/* + * Open an EPWING compression file. + */ +static int +zio_open_epwing(Zio *zio, const char *file_name) +{ + int leaf16_count; + int leaf_count; + char buffer[ZIO_EPWING_BUFFER_SIZE]; + char *buffer_p; + ssize_t read_length; + Zio_Huffman_Node *tail_node_p; + int i; + + LOG(("in: zio_open_epwing(zio=%d, file_name=%s)", (int)zio->id, + file_name)); + + zio->code = ZIO_EPWING; + zio->huffman_nodes = NULL; + + /* + * Open `HONMON2'. + */ + if (zio_open_raw(zio, file_name) < 0) + goto failed; + + /* + * Read a header of `HONMON2' (32 bytes). + * When `frequencies_length' is shorter than 512, we assumes the + * file is broken. + */ + if (zio_read_raw(zio, buffer, 32) != 32) + goto failed; + zio->location = 0; + zio->slice_size = ZIO_SIZE_PAGE; + zio->index_location = zio_uint4(buffer); + zio->index_length = zio_uint4(buffer + 4); + zio->frequencies_location = zio_uint4(buffer + 8); + zio->frequencies_length = zio_uint4(buffer + 12); + leaf16_count = (zio->frequencies_length - (256 * 2)) / 4; + leaf_count = leaf16_count + 256 + 1; + if (zio->index_length < 36 || zio->frequencies_length < 512) + goto failed; + + /* + * Check for the length of an uncompressed file. + * + * If the index of the non-first page in the last index group + * is 0x0000, we assumes the data corresponding with the index + * doesn't exist. + */ + if (zio_lseek_raw(zio, zio->index_location + + ((off_t) zio->index_length - 36) / 36 * 36, SEEK_SET) < 0) + goto failed; + if (zio_read_raw(zio, buffer, 36) != 36) + goto failed; + zio->file_size = ((off_t) zio->index_length / 36) * (ZIO_SIZE_PAGE * 16); + for (i = 1, buffer_p = buffer + 4 + 2; i < 16; i++, buffer_p += 2) { + if (zio_uint2(buffer_p) == 0) + break; + } + zio->file_size -= ZIO_SIZE_PAGE * (16 - i); + + /* + * Allocate memory for huffman nodes. + */ + zio->huffman_nodes = (Zio_Huffman_Node *) malloc(sizeof(Zio_Huffman_Node) + * leaf_count * 2); + if (zio->huffman_nodes == NULL) + goto failed; + tail_node_p = zio->huffman_nodes; + + /* + * Make leafs for 16bit character. + */ + read_length = ZIO_EPWING_BUFFER_SIZE - (ZIO_EPWING_BUFFER_SIZE % 4); + if (zio_lseek_raw(zio, zio->frequencies_location, SEEK_SET) < 0) + goto failed; + if (zio_read_raw(zio, buffer, read_length) != read_length) + goto failed; + + buffer_p = buffer; + for (i = 0; i < leaf16_count; i++) { + if (buffer + read_length <= buffer_p) { + if (zio_read_raw(zio, buffer, read_length) != read_length) + goto failed; + buffer_p = buffer; + } + tail_node_p->type = ZIO_HUFFMAN_NODE_LEAF16; + tail_node_p->value = zio_uint2(buffer_p); + tail_node_p->frequency = zio_uint2(buffer_p + 2); + tail_node_p->left = NULL; + tail_node_p->right = NULL; + buffer_p += 4; + tail_node_p++; + } + + /* + * Make leafs for 8bit character. + */ + if (zio_lseek_raw(zio, zio->frequencies_location + leaf16_count * 4, + SEEK_SET) < 0) + goto failed; + if (zio_read_raw(zio, buffer, 512) != 512) + goto failed; + + buffer_p = buffer; + for (i = 0; i < 256; i++) { + tail_node_p->type = ZIO_HUFFMAN_NODE_LEAF8; + tail_node_p->value = i; + tail_node_p->frequency = zio_uint2(buffer_p); + tail_node_p->left = NULL; + tail_node_p->right = NULL; + buffer_p += 2; + tail_node_p++; + } + + /* + * Make a leaf for the end-of-page character. + */ + tail_node_p->type = ZIO_HUFFMAN_NODE_EOF; + tail_node_p->value = 256; + tail_node_p->frequency = 1; + tail_node_p++; + + /* + * Make a huffman tree. + */ + if (zio_make_epwing_huffman_tree(zio, leaf_count) < 0) + goto failed; + + /* + * Assign ID. + */ + pthread_mutex_lock(&zio_mutex); + zio->id = zio_counter++; + pthread_mutex_unlock(&zio_mutex); + + LOG(("out: zio_open_epwing(zio=%d) = %d", (int)zio->id, zio->file)); + return zio->file; + + /* + * An error occurs... + */ + failed: + if (0 <= zio->file) + zio_close_raw(zio); + if (zio->huffman_nodes != NULL) + free(zio->huffman_nodes); + zio->file = -1; + zio->huffman_nodes = NULL; + zio->huffman_root = NULL; + zio->code = ZIO_INVALID; + + LOG(("out: zio_open_epwing() = %d", -1)); + return -1; +} + + +/* + * Open an EPWING compression file. + */ +static int +zio_open_epwing6(Zio *zio, const char *file_name) +{ + int leaf32_count; + int leaf16_count; + int leaf_count; + char buffer[ZIO_EPWING_BUFFER_SIZE]; + char *buffer_p; + ssize_t read_length; + Zio_Huffman_Node *tail_node_p; + int i; + + LOG(("in: zio_open_epwing6(zio=%d, file_name=%s)", (int)zio->id, + file_name)); + + zio->code = ZIO_EPWING6; + zio->huffman_nodes = NULL; + + /* + * Open `HONMON2'. + */ + if (zio_open_raw(zio, file_name) < 0) + goto failed; + + /* + * Read a header of `HONMON2' (48 bytes). + * When `frequencies_length' is shorter than 512, we assumes the + * file is broken. + */ + if (zio_read_raw(zio, buffer, 48) != 48) + goto failed; + zio->location = 0; + zio->slice_size = ZIO_SIZE_PAGE; + zio->index_location = zio_uint4(buffer); + zio->index_length = zio_uint4(buffer + 4); + zio->frequencies_location = zio_uint4(buffer + 8); + zio->frequencies_length = zio_uint4(buffer + 12); + leaf16_count = 0x400; + leaf32_count = (zio->frequencies_length - (leaf16_count * 4) - (256 * 2)) + / 6; + leaf_count = leaf32_count + leaf16_count + 256 + 1; + if (zio->index_length < 36 || zio->frequencies_length < 512) + goto failed; + + /* + * Check for the length of an uncompressed file. + * + * If the index of the non-first page in the last index group + * is 0x0000, we assumes the data corresponding with the index + * doesn't exist. + */ + if (zio_lseek_raw(zio, zio->index_location + + ((off_t) zio->index_length - 36) / 36 * 36, SEEK_SET) < 0) + goto failed; + if (zio_read_raw(zio, buffer, 36) != 36) + goto failed; + zio->file_size = ((off_t) zio->index_length / 36) * (ZIO_SIZE_PAGE * 16); + for (i = 1, buffer_p = buffer + 4 + 2; i < 16; i++, buffer_p += 2) { + if (zio_uint2(buffer_p) == 0) + break; + } + zio->file_size -= ZIO_SIZE_PAGE * (16 - i); + + /* + * Allocate memory for huffman nodes. + */ + zio->huffman_nodes = (Zio_Huffman_Node *) malloc(sizeof(Zio_Huffman_Node) + * leaf_count * 2); + if (zio->huffman_nodes == NULL) + goto failed; + tail_node_p = zio->huffman_nodes; + + /* + * Make leafs for 32bit character. + */ + read_length = ZIO_EPWING_BUFFER_SIZE - (ZIO_EPWING_BUFFER_SIZE % 6); + if (zio_lseek_raw(zio, zio->frequencies_location, SEEK_SET) < 0) + goto failed; + if (zio_read_raw(zio, buffer, read_length) != read_length) + goto failed; + + buffer_p = buffer; + for (i = 0; i < leaf32_count; i++) { + if (buffer + read_length <= buffer_p) { + if (zio_read_raw(zio, buffer, read_length) != read_length) + goto failed; + buffer_p = buffer; + } + tail_node_p->type = ZIO_HUFFMAN_NODE_LEAF32; + tail_node_p->value = zio_uint4(buffer_p); + tail_node_p->frequency = zio_uint2(buffer_p + 4); + tail_node_p->left = NULL; + tail_node_p->right = NULL; + buffer_p += 6; + tail_node_p++; + } + + /* + * Make leafs for 16bit character. + */ + read_length = ZIO_EPWING_BUFFER_SIZE - (ZIO_EPWING_BUFFER_SIZE % 4); + if (zio_lseek_raw(zio, zio->frequencies_location + leaf32_count * 6, + SEEK_SET) < 0) + goto failed; + if (zio_read_raw(zio, buffer, read_length) != read_length) + goto failed; + + buffer_p = buffer; + for (i = 0; i < leaf16_count; i++) { + if (buffer + read_length <= buffer_p) { + if (zio_read_raw(zio, buffer, read_length) != read_length) + goto failed; + buffer_p = buffer; + } + tail_node_p->type = ZIO_HUFFMAN_NODE_LEAF16; + tail_node_p->value = zio_uint2(buffer_p); + tail_node_p->frequency = zio_uint2(buffer_p + 2); + tail_node_p->left = NULL; + tail_node_p->right = NULL; + buffer_p += 4; + tail_node_p++; + } + + /* + * Make leafs for 8bit character. + */ + if (zio_lseek_raw(zio, zio->frequencies_location + leaf32_count * 6 + + leaf16_count * 4, SEEK_SET) < 0) + goto failed; + if (zio_read_raw(zio, buffer, 512) != 512) + goto failed; + + buffer_p = buffer; + for (i = 0; i < 256; i++) { + tail_node_p->type = ZIO_HUFFMAN_NODE_LEAF8; + tail_node_p->value = i; + tail_node_p->frequency = zio_uint2(buffer_p); + tail_node_p->left = NULL; + tail_node_p->right = NULL; + buffer_p += 2; + tail_node_p++; + } + + /* + * Make a leaf for the end-of-page character. + */ + tail_node_p->type = ZIO_HUFFMAN_NODE_EOF; + tail_node_p->value = 256; + tail_node_p->frequency = 1; + tail_node_p++; + + /* + * Make a huffman tree. + */ + if (zio_make_epwing_huffman_tree(zio, leaf_count) < 0) + goto failed; + + /* + * Assign ID. + */ + pthread_mutex_lock(&zio_mutex); + zio->id = zio_counter++; + pthread_mutex_unlock(&zio_mutex); + + LOG(("out: zio_open_epwing6(zio=%d) = %d", (int)zio->id, zio->file)); + return zio->file; + + /* + * An error occurs... + */ + failed: + if (0 <= zio->file) + zio_close_raw(zio); + if (zio->huffman_nodes != NULL) + free(zio->huffman_nodes); + zio->file = -1; + zio->huffman_nodes = NULL; + zio->huffman_root = NULL; + zio->code = ZIO_INVALID; + + LOG(("out: zio_open_epwing6() = %d", -1)); + return -1; +} + + +/* + * Make a huffman tree for decompressing EPWING compression data. + */ +static int +zio_make_epwing_huffman_tree(Zio *zio, int leaf_count) +{ + Zio_Huffman_Node *target_node; + Zio_Huffman_Node *most_node; + Zio_Huffman_Node *node_p; + Zio_Huffman_Node temporary_node; + Zio_Huffman_Node *least_node_p; + Zio_Huffman_Node *tail_node_p; + int i; + int j; + + LOG(("in: zio_make_epwing_huffman_tree(zio=%d, leaf_count=%d)", + (int)zio->id, leaf_count)); + + tail_node_p = zio->huffman_nodes + leaf_count; + + /* + * Sort the leaf nodes in frequency order. + */ + for (i = 0; i < leaf_count - 1; i++) { + target_node = zio->huffman_nodes + i; + most_node = target_node; + node_p = zio->huffman_nodes + i + 1; + + for (j = i + 1; j < leaf_count; j++) { + if (most_node->frequency < node_p->frequency) + most_node = node_p; + node_p++; + } + + temporary_node.type = most_node->type; + temporary_node.value = most_node->value; + temporary_node.frequency = most_node->frequency; + + most_node->type = target_node->type; + most_node->value = target_node->value; + most_node->frequency = target_node->frequency; + + target_node->type = temporary_node.type; + target_node->value = temporary_node.value; + target_node->frequency = temporary_node.frequency; + } + + /* + * Make intermediate nodes of the huffman tree. + * The number of intermediate nodes of the tree is - 1. + */ + for (i = 1; i < leaf_count; i++) { + /* + * Initialize a new intermediate node. + */ + tail_node_p->type = ZIO_HUFFMAN_NODE_INTERMEDIATE; + tail_node_p->left = NULL; + tail_node_p->right = NULL; + + /* + * Find for a least frequent node. + * That node becomes a left child of the new intermediate node. + */ + least_node_p = NULL; + for (node_p = zio->huffman_nodes; node_p < tail_node_p; node_p++) { + if (node_p->frequency == 0) + continue; + if (least_node_p == NULL + || node_p->frequency <= least_node_p->frequency) + least_node_p = node_p; + } + if (least_node_p == NULL) + goto failed; + tail_node_p->left = least_node_p; + tail_node_p->frequency = least_node_p->frequency; + least_node_p->frequency = 0; + + /* + * Find for a next least frequent node. + * That node becomes a right child of the new intermediate node. + */ + least_node_p = NULL; + for (node_p = zio->huffman_nodes; node_p < tail_node_p; node_p++) { + if (node_p->frequency == 0) + continue; + if (least_node_p == NULL + || node_p->frequency <= least_node_p->frequency) + least_node_p = node_p; + } + if (least_node_p == NULL) + goto failed; + tail_node_p->right = least_node_p; + tail_node_p->frequency += least_node_p->frequency; + least_node_p->frequency = 0; + + tail_node_p++; + } + + /* + * Set a root node of the huffman tree. + */ + zio->huffman_root = tail_node_p - 1; + + LOG(("out: zio_make_epwing_huffman_tree() = %d", 0)); + return 0; + + /* + * An error occurs... + */ + failed: + LOG(("out: zio_make_epwing_huffman_tree() = %d", -1)); + return -1; +} + + +/* + * Close `zio'. + */ +void +zio_close(Zio *zio) +{ + pthread_mutex_lock(&zio_mutex); + LOG(("in: zio_close(zio=%d)", (int)zio->id)); + + /* + * If contents of the file is cached, clear the cache. + */ + if (0 <= zio->file) + zio_close_raw(zio); + zio->file = -1; + + LOG(("out: zio_close()")); + pthread_mutex_unlock(&zio_mutex); +} + + +/* + * Return file descriptor of `zio'. + */ +int +zio_file(Zio *zio) +{ + LOG(("in+out: zio_file(zio=%d) = %d", (int)zio->id, zio->file)); + + return zio->file; +} + + +/* + * Return compression mode of `zio'. + */ +Zio_Code +zio_mode(Zio *zio) +{ + LOG(("in+out: zio_mode(zio=%d) = %d", (int)zio->id, zio->code)); + + return zio->code; +} + + +/* + * Seek `zio'. + */ +off_t +zio_lseek(Zio *zio, off_t location, int whence) +{ + off_t result; + + LOG(("in: zio_lseek(zio=%d, location=%ld, whence=%d)", + (int)zio->id, (long)location, whence)); + + if (zio->file < 0) + goto failed; + + if (zio->code == ZIO_PLAIN) { + /* + * If `zio' is not compressed, simply call lseek(). + */ + result = zio_lseek_raw(zio, location, whence); + } else { + /* + * Calculate new location according with `whence'. + */ + switch (whence) { + case SEEK_SET: + zio->location = location; + break; + case SEEK_CUR: + zio->location = zio->location + location; + break; + case SEEK_END: + zio->location = zio->file_size - location; + break; + default: +#ifdef EINVAL + errno = EINVAL; +#endif + goto failed; + } + + /* + * Adjust location. + */ + if (zio->location < 0) + zio->location = 0; + if (zio->file_size < zio->location) + zio->location = zio->file_size; + + /* + * Update `zio->location'. + * (We don't actually seek the file.) + */ + result = zio->location; + } + + LOG(("out: zio_lseek() = %ld", (long)result)); + return result; + + /* + * An error occurs... + */ + failed: + LOG(("out: zio_lseek() = %ld", (long)-1)); + return -1; +} + + +/* + * Read data from `zio' file. + */ +ssize_t +zio_read(Zio *zio, char *buffer, size_t length) +{ + ssize_t read_length; + + pthread_mutex_lock(&zio_mutex); + LOG(("in: zio_read(zio=%d, length=%ld)", (int)zio->id, (long)length)); + + /* + * If the zio `file' is not compressed, call read() and return. + */ + if (zio->file < 0) + goto failed; + + switch (zio->code) { + case ZIO_PLAIN: + read_length = zio_read_raw(zio, buffer, length); + break; + case ZIO_EBZIP1: + read_length = zio_read_ebzip(zio, buffer, length); + break; + case ZIO_EPWING: + case ZIO_EPWING6: + read_length = zio_read_epwing(zio, buffer, length); + break; + case ZIO_SEBXA: + read_length = zio_read_sebxa(zio, buffer, length); + break; + default: + goto failed; + } + + LOG(("out: zio_read() = %ld", (long)read_length)); + pthread_mutex_unlock(&zio_mutex); + + return read_length; + + /* + * An error occurs... + */ + failed: + LOG(("out: zio_read() = %ld", (long)-1)); + return -1; +} + + +/* + * Read data from the `zio' file compressed with the ebzip compression + * format. + */ +static ssize_t +zio_read_ebzip(Zio *zio, char *buffer, size_t length) +{ + char temporary_buffer[8]; + ssize_t read_length = 0; + size_t zipped_slice_size; + off_t slice_location; + off_t next_slice_location; + int n; + + LOG(("in: zio_read_ebzip(zio=%d, length=%ld)", (int)zio->id, + (long)length)); + + /* + * Read data. + */ + while (read_length < length) { + if (zio->file_size <= zio->location) + goto succeeded; + + /* + * If data in `cache_buffer' is out of range, read data from + * `zio->file'. + */ + if (cache_zio_id != zio->id + || zio->location < cache_location + || cache_location + zio->slice_size <= zio->location) { + + cache_zio_id = ZIO_ID_NONE; + cache_location = zio->location - (zio->location % zio->slice_size); + + /* + * Get buffer location and size from index table in `zio->file'. + */ + if (zio_lseek_raw(zio, zio->location / zio->slice_size + * zio->index_width + ZIO_SIZE_EBZIP_HEADER, SEEK_SET) < 0) + goto failed; + if (zio_read_raw(zio, temporary_buffer, zio->index_width * 2) + != zio->index_width * 2) + goto failed; + + switch (zio->index_width) { + case 2: + slice_location = zio_uint2(temporary_buffer); + next_slice_location = zio_uint2(temporary_buffer + 2); + break; + case 3: + slice_location = zio_uint3(temporary_buffer); + next_slice_location = zio_uint3(temporary_buffer + 3); + break; + case 4: + slice_location = zio_uint4(temporary_buffer); + next_slice_location = zio_uint4(temporary_buffer + 4); + break; + case 5: + slice_location = zio_uint5(temporary_buffer); + next_slice_location = zio_uint5(temporary_buffer + 5); + break; + default: + goto failed; + } + zipped_slice_size = next_slice_location - slice_location; + + if (next_slice_location <= slice_location + || zio->slice_size < zipped_slice_size) + goto failed; + + /* + * Read a compressed slice from `zio->file' and uncompress it. + * The data is not compressed if its size is equals to + * slice size. + */ + if (zio_lseek_raw(zio, slice_location, SEEK_SET) < 0) + goto failed; + if (zio_unzip_slice_ebzip1(zio, cache_buffer, zipped_slice_size) + < 0) + goto failed; + + cache_zio_id = zio->id; + } + + /* + * Copy data from `cache_buffer' to `buffer'. + */ + n = zio->slice_size - (zio->location % zio->slice_size); + if (length - read_length < n) + n = length - read_length; + if (zio->file_size - zio->location < n) + n = zio->file_size - zio->location; + memcpy(buffer + read_length, + cache_buffer + (zio->location % zio->slice_size), n); + read_length += n; + zio->location += n; + } + + succeeded: + LOG(("out: zio_read_ebzip() = %ld", (long)read_length)); + return read_length; + + /* + * An error occurs... + */ + failed: + LOG(("out: zio_read_ebzip() = %ld", (long)-1)); + return -1; +} + + +/* + * Read data from the `zio' file compressed with the EPWING or EPWING V6 + * compression format. + */ +static ssize_t +zio_read_epwing(Zio *zio, char *buffer, size_t length) +{ + char temporary_buffer[36]; + ssize_t read_length = 0; + off_t page_location; + int n; + + LOG(("in: zio_read_epwing(zio=%d, length=%ld)", (int)zio->id, + (long)length)); + + /* + * Read data. + */ + while (read_length < length) { + if (zio->file_size <= zio->location) + goto succeeded; + + /* + * If data in `cache_buffer' is out of range, read data from the zio + * file. + */ + if (cache_zio_id != zio->id + || zio->location < cache_location + || cache_location + zio->slice_size <= zio->location) { + cache_zio_id = ZIO_ID_NONE; + cache_location = zio->location - (zio->location % zio->slice_size); + + /* + * Get page location from index table in `zio->file'. + */ + if (zio_lseek_raw(zio, zio->index_location + + zio->location / (ZIO_SIZE_PAGE * 16) * 36, SEEK_SET) < 0) + goto failed; + if (zio_read_raw(zio, temporary_buffer, 36) != 36) + goto failed; + page_location = zio_uint4(temporary_buffer) + + zio_uint2(temporary_buffer + 4 + + (zio->location / ZIO_SIZE_PAGE % 16) * 2); + + /* + * Read a compressed page from `zio->file' and uncompress it. + */ + if (zio_lseek_raw(zio, page_location, SEEK_SET) < 0) + goto failed; + if (zio->code == ZIO_EPWING) { + if (zio_unzip_slice_epwing(zio, cache_buffer) < 0) + goto failed; + } else { + if (zio_unzip_slice_epwing6(zio, cache_buffer) < 0) + goto failed; + } + + cache_zio_id = zio->id; + } + + /* + * Copy data from `cache_buffer' to `buffer'. + */ + n = ZIO_SIZE_PAGE - (zio->location % ZIO_SIZE_PAGE); + if (length - read_length < n) + n = length - read_length; + if (zio->file_size - zio->location < n) + n = zio->file_size - zio->location; + memcpy(buffer + read_length, + cache_buffer + (zio->location - cache_location), n); + read_length += n; + zio->location += n; + } + + succeeded: + LOG(("out: zio_read_epwing() = %ld", (long)read_length)); + return read_length; + + /* + * An error occurs... + */ + failed: + LOG(("out: zio_read_epwing() = %ld", (long)-1)); + return -1; +} + + +#define ZIO_SEBXA_SLICE_LENGTH 4096 + +/* + * Read data from the zio `file' compressed with the S-EBXA compression + * format. + */ +static ssize_t +zio_read_sebxa(Zio *zio, char *buffer, size_t length) +{ + char temporary_buffer[4]; + ssize_t read_length = 0; + off_t slice_location; + ssize_t n; + int slice_index; + + LOG(("in: zio_read_sebxa(zio=%d, length=%ld)", (int)zio->id, + (long)length)); + + /* + * Read data. + */ + while (read_length < length) { + if (zio->file_size <= zio->location) + goto succeeded; + + if (zio->location < zio->zio_start_location) { + /* + * Data is located in front of compressed text. + */ + if (zio->zio_start_location - zio->location < length - read_length) + n = zio->zio_start_location - zio->location; + else + n = length - read_length; + if (zio_lseek_raw(zio, zio->location, SEEK_SET) < 0) + goto failed; + if (zio_read_raw(zio, buffer, n) != n) + goto failed; + read_length += n; + + } else if (zio->zio_end_location <= zio->location) { + /* + * Data is located behind compressed text. + */ + if (zio_lseek_raw(zio, zio->location, SEEK_SET) < 0) + goto failed; + if (zio_read_raw(zio, buffer, length - read_length) + != length - read_length) + goto failed; + read_length = length; + + } else { + /* + * Data is located in compressed text. + * + * If data in `cache_buffer' is out of range, read data from + * `file'. + */ + if (cache_zio_id != zio->id + || zio->location < cache_location + || cache_location + ZIO_SEBXA_SLICE_LENGTH <= zio->location) { + + cache_zio_id = ZIO_ID_NONE; + cache_location = zio->location + - (zio->location % ZIO_SEBXA_SLICE_LENGTH); + + /* + * Get buffer location and size. + */ + slice_index = (zio->location - zio->zio_start_location) + / ZIO_SEBXA_SLICE_LENGTH; + if (slice_index == 0) + slice_location = zio->index_base; + else { + if (zio_lseek_raw(zio, ((off_t) slice_index - 1) * 4 + + zio->index_location, SEEK_SET) < 0) + goto failed; + if (zio_read_raw(zio, temporary_buffer, 4) != 4) + goto failed; + slice_location = zio->index_base + + zio_uint4(temporary_buffer); + } + + /* + * Read a compressed slice from `zio->file' and uncompress it. + */ + if (zio_lseek_raw(zio, slice_location, SEEK_SET) < 0) + goto failed; + if (zio_unzip_slice_sebxa(zio, cache_buffer) < 0) + goto failed; + + cache_zio_id = zio->id; + } + + /* + * Copy data from `cache_buffer' to `buffer'. + */ + n = ZIO_SEBXA_SLICE_LENGTH + - (zio->location % ZIO_SEBXA_SLICE_LENGTH); + if (length - read_length < n) + n = length - read_length; + if (zio->file_size - zio->location < n) + n = zio->file_size - zio->location; + memcpy(buffer + read_length, + cache_buffer + (zio->location - cache_location), n); + read_length += n; + zio->location += n; + } + } + + succeeded: + LOG(("out: zio_read_sebxa() = %ld", (long)read_length)); + return read_length; + + /* + * An error occurs... + */ + failed: + LOG(("out: zio_read_sebxa() = %ld", (long)-1)); + return -1; +} + + +/* + * Uncompress an ebzip'ped slice. + * + * If it succeeds, 0 is returned. Otherwise, -1 is returned. + */ +static int +zio_unzip_slice_ebzip1(Zio *zio, char *out_buffer, size_t zipped_slice_size) +{ + char in_buffer[ZIO_SIZE_PAGE]; + z_stream stream; + size_t read_length; + int z_result; + + LOG(("in: zio_unzip_slice_ebzip1(zio=%d, zipped_slice_size=%ld)", + (int)zio->id, (long)zipped_slice_size)); + + if (zio->slice_size == zipped_slice_size) { + /* + * The input slice is not compressed. + * Read the target page in the slice. + */ + if (zio_read_raw(zio, out_buffer, zipped_slice_size) != zipped_slice_size) + goto failed; + + } else { + /* + * The input slice is compressed. + * Read and uncompress the target page in the slice. + */ + stream.zalloc = NULL; + stream.zfree = NULL; + stream.opaque = NULL; + + if (inflateInit(&stream) != Z_OK) + goto failed; + + stream.next_in = (Bytef *) in_buffer; + stream.avail_in = 0; + stream.next_out = (Bytef *) out_buffer; + stream.avail_out = zio->slice_size; + + while (stream.total_out < zio->slice_size) { + if (0 < stream.avail_in) + memmove(in_buffer, stream.next_in, stream.avail_in); + + if (zipped_slice_size - stream.total_in < ZIO_SIZE_PAGE) { + read_length = zipped_slice_size - stream.total_in + - stream.avail_in; + } else { + read_length = ZIO_SIZE_PAGE - stream.avail_in; + } + + if (zio_read_raw(zio, in_buffer + stream.avail_in, + read_length) != read_length) + goto failed; + + stream.next_in = (Bytef *) in_buffer; + stream.avail_in += read_length; + stream.avail_out = zio->slice_size - stream.total_out; + + z_result = inflate(&stream, Z_SYNC_FLUSH); + if (z_result == Z_STREAM_END) { + break; + } else if (z_result != Z_OK && z_result != Z_BUF_ERROR) { + goto failed; + } + } + + inflateEnd(&stream); + } + + LOG(("out: zio_unzip_slice_ebzip1() = %d", 0)); + return 0; + + /* + * An error occurs... + */ + failed: + LOG(("out: zio_unzip_slice_ebzip1() = %d", -1)); + inflateEnd(&stream); + return -1; +} + + +/* + * Uncompress an EPWING compressed slice. + * The offset of `zio->file' must points to the beginning of the compressed + * slice. Uncompressed data are put into `out_buffer'. + * + * If it succeeds, 0 is returned. Otherwise, -1 is returned. + */ +static int +zio_unzip_slice_epwing(Zio *zio, char *out_buffer) +{ + Zio_Huffman_Node *node_p; + int bit; + char in_buffer[ZIO_SIZE_PAGE]; + unsigned char *in_buffer_p; + ssize_t in_read_length; + int in_bit_index; + unsigned char *out_buffer_p; + size_t out_length; + + LOG(("in: zio_unzip_slice_epwing(zio=%d)", (int)zio->id)); + + in_buffer_p = (unsigned char *)in_buffer; + in_bit_index = 7; + in_read_length = 0; + out_buffer_p = (unsigned char *)out_buffer; + out_length = 0; + + for (;;) { + /* + * Descend the huffman tree until reached to the leaf node. + */ + node_p = zio->huffman_root; + while (node_p->type == ZIO_HUFFMAN_NODE_INTERMEDIATE) { + + /* + * If no data is left in the input buffer, read next chunk. + */ + if ((unsigned char *)in_buffer + in_read_length <= in_buffer_p) { + in_read_length = zio_read_raw(zio, in_buffer, ZIO_SIZE_PAGE); + if (in_read_length <= 0) + goto failed; + in_buffer_p = (unsigned char *)in_buffer; + } + + /* + * Step to a child. + */ + bit = (*in_buffer_p >> in_bit_index) & 0x01; + + if (bit == 1) + node_p = node_p->left; + else + node_p = node_p->right; + if (node_p == NULL) + goto failed; + + if (0 < in_bit_index) + in_bit_index--; + else { + in_bit_index = 7; + in_buffer_p++; + } + } + + if (node_p->type == ZIO_HUFFMAN_NODE_EOF) { + /* + * Fill the rest of the output buffer with NUL, + * when we meet an EOF mark before decode ZIO_SIZE_PAGE bytes. + */ + if (out_length < ZIO_SIZE_PAGE) { + memset(out_buffer_p, '\0', ZIO_SIZE_PAGE - out_length); + out_length = ZIO_SIZE_PAGE; + } + break; + + } else if (node_p->type == ZIO_HUFFMAN_NODE_LEAF16) { + /* + * The leaf is leaf16, decode 2 bytes character. + */ + if (ZIO_SIZE_PAGE <= out_length) + goto failed; + else if (ZIO_SIZE_PAGE <= out_length + 1) { + *out_buffer_p++ = (node_p->value >> 8) & 0xff; + out_length++; + } else { + *out_buffer_p++ = (node_p->value >> 8) & 0xff; + *out_buffer_p++ = node_p->value & 0xff; + out_length += 2; + } + } else { + /* + * The leaf is leaf8, decode 1 byte character. + */ + if (ZIO_SIZE_PAGE <= out_length) + goto failed; + else { + *out_buffer_p++ = node_p->value; + out_length++; + } + } + } + + LOG(("out: zio_unzip_slice_epwing() = %d", 0)); + return 0; + + /* + * An error occurs... + */ + failed: + LOG(("out: zio_unzip_slice_epwing() = %d", -1)); + return -1; +} + + +/* + * Uncompress an EPWING V6 compressed slice. + * The offset of `zio->file' must points to the beginning of the compressed + * slice. Uncompressed data are put into `out_buffer'. + * + * If it succeeds, 0 is returned. Otherwise, -1 is returned. + */ +static int +zio_unzip_slice_epwing6(Zio *zio, char *out_buffer) +{ + Zio_Huffman_Node *node_p; + int bit; + char in_buffer[ZIO_SIZE_PAGE]; + unsigned char *in_buffer_p; + ssize_t in_read_length; + int in_bit_index; + unsigned char *out_buffer_p; + size_t out_length; + int compression_type; + + LOG(("in: zio_unzip_slice_epwing6(zio=%d)", (int)zio->id)); + + in_buffer_p = (unsigned char *)in_buffer; + in_bit_index = 7; + in_read_length = 0; + out_buffer_p = (unsigned char *)out_buffer; + out_length = 0; + + /* + * Get compression type. + */ + if (zio_read_raw(zio, in_buffer, 1) != 1) + goto failed; + compression_type = zio_uint1(in_buffer); + + /* + * If compression type is not 0, this page is not compressed. + */ + if (compression_type != 0) { + if (zio_read_raw(zio, out_buffer, ZIO_SIZE_PAGE) != ZIO_SIZE_PAGE) + goto failed; + goto succeeded; + } + + while (out_length < ZIO_SIZE_PAGE) { + /* + * Descend the huffman tree until reached to the leaf node. + */ + node_p = zio->huffman_root; + while (node_p->type == ZIO_HUFFMAN_NODE_INTERMEDIATE) { + + /* + * If no data is left in the input buffer, read next chunk. + */ + if ((unsigned char *)in_buffer + in_read_length <= in_buffer_p) { + in_read_length = zio_read_raw(zio, in_buffer, ZIO_SIZE_PAGE); + if (in_read_length <= 0) + goto failed; + in_buffer_p = (unsigned char *)in_buffer; + } + + /* + * Step to a child. + */ + bit = (*in_buffer_p >> in_bit_index) & 0x01; + + if (bit == 1) + node_p = node_p->left; + else + node_p = node_p->right; + if (node_p == NULL) + goto failed; + + if (0 < in_bit_index) + in_bit_index--; + else { + in_bit_index = 7; + in_buffer_p++; + } + } + + if (node_p->type == ZIO_HUFFMAN_NODE_EOF) { + /* + * Fill the rest of the output buffer with NUL, + * when we meet an EOF mark before decode ZIO_SIZE_PAGE bytes. + */ + if (out_length < ZIO_SIZE_PAGE) { + memset(out_buffer_p, '\0', ZIO_SIZE_PAGE - out_length); + out_length = ZIO_SIZE_PAGE; + } + break; + + } else if (node_p->type == ZIO_HUFFMAN_NODE_LEAF32) { + /* + * The leaf is leaf32, decode 4 bytes character. + */ + if (ZIO_SIZE_PAGE <= out_length + 1) { + *out_buffer_p++ = (node_p->value >> 24) & 0xff; + out_length++; + } else if (ZIO_SIZE_PAGE <= out_length + 2) { + *out_buffer_p++ = (node_p->value >> 24) & 0xff; + *out_buffer_p++ = (node_p->value >> 16) & 0xff; + out_length += 2; + } else if (ZIO_SIZE_PAGE <= out_length + 3) { + *out_buffer_p++ = (node_p->value >> 24) & 0xff; + *out_buffer_p++ = (node_p->value >> 16) & 0xff; + *out_buffer_p++ = (node_p->value >> 8) & 0xff; + out_length += 3; + } else { + *out_buffer_p++ = (node_p->value >> 24) & 0xff; + *out_buffer_p++ = (node_p->value >> 16) & 0xff; + *out_buffer_p++ = (node_p->value >> 8) & 0xff; + *out_buffer_p++ = node_p->value & 0xff; + out_length += 4; + } + } else if (node_p->type == ZIO_HUFFMAN_NODE_LEAF16) { + /* + * The leaf is leaf16, decode 2 bytes character. + */ + if (ZIO_SIZE_PAGE <= out_length + 1) { + *out_buffer_p++ = (node_p->value >> 8) & 0xff; + out_length++; + } else { + *out_buffer_p++ = (node_p->value >> 8) & 0xff; + *out_buffer_p++ = node_p->value & 0xff; + out_length += 2; + } + } else { + /* + * The leaf is leaf8, decode 1 byte character. + */ + *out_buffer_p++ = node_p->value; + out_length++; + } + } + + succeeded: + LOG(("out: zio_unzip_slice_epwing6() = %d", 0)); + return 0; + + /* + * An error occurs... + */ + failed: + LOG(("out: zio_unzip_slice_epwing6() = %d", -1)); + return -1; +} + +/* + * Uncompress an S-EBXA compressed slice. + * The offset of `zio->file' must points to the beginning of the compressed + * slice. Uncompressed data are put into `out_buffer'. + * + * If it succeeds, 0 is returned. Otherwise, -1 is returned. + */ +static int +zio_unzip_slice_sebxa(Zio *zio, char *out_buffer) +{ + char in_buffer[ZIO_SEBXA_SLICE_LENGTH]; + unsigned char *in_buffer_p; + size_t in_read_rest; + unsigned char *out_buffer_p; + size_t out_length; + int compression_flags[8]; + int copy_offset; + int copy_length; + int i, j; + + LOG(("in: zio_unzip_slice_sebxa(zio=%d)", (int)zio->id)); + + in_buffer_p = (unsigned char *)in_buffer; + in_read_rest = 0; + out_buffer_p = (unsigned char *)out_buffer; + out_length = 0; + + for (;;) { + /* + * If no data is left in the input buffer, read next chunk. + */ + if (in_read_rest <= 0) { + in_read_rest = zio_read_raw(zio, in_buffer, + ZIO_SEBXA_SLICE_LENGTH); + if (in_read_rest <= 0) + goto failed; + in_buffer_p = (unsigned char *)in_buffer; + } + + /* + * The current input byte is recognized as compression flags + * for next 8 chunks. + */ + compression_flags[0] = !(*in_buffer_p & 0x01); + compression_flags[1] = !(*in_buffer_p & 0x02); + compression_flags[2] = !(*in_buffer_p & 0x04); + compression_flags[3] = !(*in_buffer_p & 0x08); + compression_flags[4] = !(*in_buffer_p & 0x10); + compression_flags[5] = !(*in_buffer_p & 0x20); + compression_flags[6] = !(*in_buffer_p & 0x40); + compression_flags[7] = !(*in_buffer_p & 0x80); + in_buffer_p++; + in_read_rest--; + + /* + * Decode 8 chunks. + */ + for (i = 0; i < 8; i++) { + if (compression_flags[i]) { + /* + * This chunk is compressed. + * Copy `copy_length' bytes from `copy_p' to the current + * point. + */ + unsigned char *copy_p; + unsigned char c0, c1; + + if (in_read_rest <= 1) + goto failed; + + /* + * Get 2 bytes from the current input, and recognize + * them as following: + * + * *in_buffer_p *(in_bufer_p + 1) + * bit pattern: [AAAA|BBBB] [CCCC|DDDD] + * + * copy_offset = ([CCCCAAAABBBB] + 18) % 4096 + * copy_length = [DDDD] + 3 + */ + c0 = *(unsigned char *)in_buffer_p; + c1 = *((unsigned char *)in_buffer_p + 1); + copy_offset = (((c1 & 0xf0) << 4) + c0 + 18) + % ZIO_SEBXA_SLICE_LENGTH; + copy_length = (c1 & 0x0f) + 3; + + if (ZIO_SEBXA_SLICE_LENGTH < out_length + copy_length) + copy_length = ZIO_SEBXA_SLICE_LENGTH - out_length; + + copy_p = (unsigned char *)out_buffer + copy_offset; + for (j = 0; j < copy_length; j++) { + if (copy_p < out_buffer_p) + *out_buffer_p++ = *copy_p; + else + *out_buffer_p++ = 0x00; + copy_p++; + if (ZIO_SEBXA_SLICE_LENGTH <= + copy_p - (unsigned char *)out_buffer) + copy_p -= ZIO_SEBXA_SLICE_LENGTH; + } + + in_read_rest -= 2; + in_buffer_p += 2; + out_length += copy_length; + + } else { + /* + * This chunk is not compressed. + * Put the current input byte as a decoded value. + */ + if (in_read_rest <= 0) + goto failed; + in_read_rest -= 1; + *out_buffer_p++ = *in_buffer_p++; + out_length += 1; + } + + /* + * Return if the slice has been uncompressed. + */ + if (ZIO_SEBXA_SLICE_LENGTH <= out_length) + goto succeeded; + } + } + + succeeded: + LOG(("out: zio_unzip_slice_sebxa() = %d", 0)); + return 0; + + /* + * An error occurs... + */ + failed: + LOG(("out: zio_unzip_slice_sebxa() = %d", -1)); + return -1; +} + + +/* + * Low-level open function. + * + * If `file_name' is ebnet URL, it calls ebnet_open(). Otherwise it + * calls the open() system call. + * + * Like open(), it returns file descrptor or -1. + */ +static int +zio_open_raw(Zio *zio, const char *file_name) +{ + zio->file = open(file_name, O_RDONLY | O_BINARY); + + return zio->file; +} + + +/* + * Low-level close function. + * + * If `zio->file' is socket, it calls ebnet_close(). Otherwise it calls + * the close() system call. + */ +static void +zio_close_raw(Zio *zio) +{ + close(zio->file); +} + + +/* + * Low-level seek function. + * + * If `zio->file' is socket, it calls ebnet_close(). Otherwise it calls + * the close() system call. + */ +static off_t +zio_lseek_raw(Zio *zio, off_t offset, int whence) +{ + off_t result; + + result = lseek(zio->file, offset, whence); + + return result; +} + + +/* + * Low-level read function. + * + * If `zio->file' is socket, it calls ebnet_read(). Otherwise it calls + * the read() system call. + */ +static ssize_t +zio_read_raw(Zio *zio, void *buffer, size_t length) +{ + char *buffer_p = buffer; + ssize_t result; + + LOG(("in: zio_read_raw(file=%d, length=%ld)", zio->file, (long)length)); + + /* + * Read from a local file. + */ + ssize_t rest_length = length; + ssize_t n; + + while (0 < rest_length) { + errno = 0; + n = read(zio->file, buffer_p, rest_length); + if (n < 0) { + if (errno == EINTR) + continue; + goto failed; + } else if (n == 0) + break; + else { + rest_length -= n; + buffer_p += n; + } + } + + result = length - rest_length; + + LOG(("out: zio_read_raw() = %ld", (long)result)); + return result; + + /* + * An error occurs... + */ + failed: + LOG(("out: zio_read_raw() = %ld", (long)-1)); + return -1; +} diff --git a/zio.h b/zio.h new file mode 100644 index 0000000..63a8f96 --- /dev/null +++ b/zio.h @@ -0,0 +1,232 @@ +/* -*- C -*- + * Copyright (c) 2001-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. + */ + +#ifndef ZIO_H +#define ZIO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* + * Header size of the ebzip compression file. + */ +#define ZIO_SIZE_EBZIP_HEADER 22 + +/* + * Margin size for ebzip compression buffer. + * (Since compressed data is larger than original in the worst case, + * we must add margin to a compression buffer.) + */ +#define ZIO_SIZE_EBZIP_MARGIN 1024 + +/* + * Maximum ebzio compression level. + */ +#define ZIO_MAX_EBZIP_LEVEL 5 + +/* + * Huffman node types. + */ +#define ZIO_HUFFMAN_NODE_INTERMEDIATE 0 +#define ZIO_HUFFMAN_NODE_EOF 1 +#define ZIO_HUFFMAN_NODE_LEAF8 2 +#define ZIO_HUFFMAN_NODE_LEAF16 3 +#define ZIO_HUFFMAN_NODE_LEAF32 4 + +/* + * Compression type codes. + */ +#define ZIO_PLAIN 0 +#define ZIO_EBZIP1 1 +#define ZIO_EPWING 2 +#define ZIO_EPWING6 3 +#define ZIO_SEBXA 4 +#define ZIO_INVALID -1 +#define ZIO_REOPEN -2 + +/* + * Compression type. + */ +typedef int Zio_Code; + +/* + * A node of static Huffman tree. + */ +typedef struct Zio_Huffman_Node_Struct Zio_Huffman_Node; + +struct Zio_Huffman_Node_Struct { + /* + * node type (ITNERMEDIATE, LEAF8, LEAF16, LEAF32 or EOF). + */ + int type; + + /* + * Value of a leaf node. + */ + unsigned int value; + + /* + * Frequency of a node. + */ + int frequency; + + /* + * Left child. + */ + Zio_Huffman_Node *left; + + /* + * Right child. + */ + Zio_Huffman_Node *right; +}; + +/* + * Compression information of a book. + */ +typedef struct Zio_Struct Zio; + +struct Zio_Struct { + /* + * ID. + */ + int id; + + /* + * Zio type. (PLAIN, EBZIP, EPWING, EPWING6 or SEBXA) + */ + Zio_Code code; + + /* + * File descriptor. + */ + int file; + + /* + * Current location. + */ + off_t location; + + /* + * Size of an uncompressed file. + */ + off_t file_size; + + /* + * Slice size of an EBZIP compressed file. + */ + size_t slice_size; + + /* + * Compression level. (EBZIP compression only) + */ + int zip_level; + + /* + * Length of an index. (EBZIP compression only) + */ + int index_width; + + /* + * Adler-32 check sum of an uncompressed file. (EBZIP compression only) + */ + unsigned int crc; + + /* + * mtime of an uncompressed file. (EBZIP compression only) + */ + time_t mtime; + + /* + * Location of an index table. (EPWING and S-EBXA compression only) + */ + off_t index_location; + + /* + * Length of an index table. (EPWING and S-EBXA compression only) + */ + size_t index_length; + + /* + * Location of a frequency table. (EPWING compression only) + */ + off_t frequencies_location; + + /* + * Length of a frequency table. (EPWING compression only) + */ + size_t frequencies_length; + + /* + * Huffman tree nodes. (EPWING compression only) + */ + Zio_Huffman_Node *huffman_nodes; + + /* + * Root node of a Huffman tree. (EPWING compression only) + */ + Zio_Huffman_Node *huffman_root; + + /* + * Region of compressed pages. (S-EBXA compression only) + */ + off_t zio_start_location; + off_t zio_end_location; + + /* + * Add this value to offset written in index. (S-EBXA compression only) + */ + off_t index_base; +}; + +/* + * Function declarations. + */ +/* zio.c */ +int zio_initialize_library(void); +void zio_finalize_library(void); +void zio_initialize(Zio *zio); +void zio_finalize(Zio *zio); +int zio_set_sebxa_mode(Zio *zio, off_t index_location, off_t index_base, + off_t zio_start_location, off_t zio_end_location); +int zio_open(Zio *zio, const char *file_name, Zio_Code zio_code); +void zio_close(Zio *zio); +int zio_file(Zio *zio); +Zio_Code zio_mode(Zio *zio); +off_t zio_lseek(Zio *zio, off_t offset, int whence); +ssize_t zio_read(Zio *zio, char *buffer, size_t length); + +#ifdef __cplusplus +} +#endif + +#endif /* not ZIO_H */