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 */