/* -*- C -*- * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #ifdef ENABLE_NLS #ifdef HAVE_LOCALE_H #include #endif #include #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 */ #include "eb/eb.h" #include "eb/error.h" #include "eb/text.h" #include "eb/appendix.h" #include "getopt.h" #include "ebutils.h" /* * 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 /* * 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)) /* * Default maximum length of text. */ #define DEFAULT_MAX_TEXT_LENGTH EB_SIZE_PAGE /* * Default book directory. */ #define DEFAULT_BOOK_DIRECTORY "." /* * Dummy stop code. * * Application cannot order EB Library not to use stop code. * EB Library guesses stop code automatically when appendix is not * given by application. * * Instead, we use dummy stop code. The code is never used in text * of CD-ROM book. */ #define DUMMY_STOP_CODE0 0x1f00 #define DUMMY_STOP_CODE1 0x0000 /* * Command line options. */ static const char *short_options = "c:hl:np:v"; static struct option long_options[] = { {"code", required_argument, NULL, 'c'}, {"help", no_argument, NULL, 'h'}, {"text-length", required_argument, NULL, 'l'}, {"no-candidates", no_argument, NULL, 'n'}, {"text-position", required_argument, NULL, 'p'}, {"version", no_argument, NULL, 'v'}, {NULL, 0, NULL, 0} }; /* * Program name and version. */ static const char *program_name = "ebstopcode"; static const char *program_version = VERSION; static const char *invoked_name; /* * Unexported functions. */ static int parse_stop_code_argument(const char *argument, unsigned int *stop_code0, unsigned int *stop_code1); static int parse_text_length_argument(const char *argument, ssize_t *text_length); static int parse_text_position_argument(const char *argument, EB_Position *text_position); static void output_help(void); static int scan_subbook_text(const char *book_directory, const char *subbook_name, EB_Position *text_position, ssize_t max_text_length, int show_stop_code_flag, unsigned int stop_code0, unsigned int stop_code1); static EB_Error_Code hook_stop_code(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv); int main(int argc, char *argv[]) { const char *book_directory; ssize_t max_text_length; const char *subbook_name; int show_stop_code_flag; EB_Position text_position; unsigned int stop_code0, stop_code1; int ch; invoked_name = argv[0]; max_text_length = DEFAULT_MAX_TEXT_LENGTH; show_stop_code_flag = 1; stop_code0 = DUMMY_STOP_CODE0; stop_code1 = DUMMY_STOP_CODE1; text_position.page = 0; text_position.offset = 0; /* * Initialize locale data. */ #ifdef ENABLE_NLS #ifdef HAVE_SETLOCALE setlocale(LC_ALL, ""); #endif bindtextdomain(TEXT_DOMAIN_NAME, LOCALEDIR); textdomain(TEXT_DOMAIN_NAME); #endif /* * Parse command line options. */ for (;;) { ch = getopt_long(argc, argv, short_options, long_options, NULL); if (ch == -1) break; switch (ch) { case 'c': /* * Option `-c'. Specify stop code manually. */ if (parse_stop_code_argument(optarg, &stop_code0, &stop_code1) < 0) goto die; max_text_length = 0; break; case 'h': /* * Option `-h'. Display help message, then exit. */ output_help(); exit(0); case 'l': /* * Option `-l'. Specify maximum length of text. */ if (parse_text_length_argument(optarg, &max_text_length) < 0) goto die; stop_code0 = DUMMY_STOP_CODE0; stop_code1 = DUMMY_STOP_CODE1; break; case 'n': /* * Option `-n'. Do not output stop code candidates. */ show_stop_code_flag = 0; break; case 'p': /* * Option `-p'. Specify text position. */ if (parse_text_position_argument(optarg, &text_position) < 0) goto die; break; case 'v': /* * Option `-v'. Display version number, then exit. */ output_version(program_name, program_version); exit(0); default: output_try_help(invoked_name); goto die; } } /* * Check the number of rest arguments. */ if (argc - optind < 2) { fprintf(stderr, _("%s: too few argument\n"), invoked_name); output_try_help(invoked_name); goto die; } if (2 < argc - optind) { fprintf(stderr, _("%s: too many arguments\n"), invoked_name); output_try_help(invoked_name); goto die; } if (argc - optind == 2) { book_directory = argv[optind]; subbook_name = argv[optind + 1]; } else { book_directory = DEFAULT_BOOK_DIRECTORY; subbook_name = argv[optind]; } /* * Scan stop code in text. */ if (scan_subbook_text(book_directory, subbook_name, &text_position, max_text_length, show_stop_code_flag, stop_code0, stop_code1) < 0) goto die; return 0; die: fflush(stdout); exit(1); } /* * Parse stop code given as argument of the `-c' option. */ static int parse_stop_code_argument(const char *argument, unsigned int *stop_code0, unsigned int *stop_code1) { const char *p = argument; const char *foundp; /* * Parse stop_code0. */ while (*p == ' ' || *p == '\t') p++; if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X')) p += 2; foundp = p; while (ASCII_ISXDIGIT(*p)) p++; if (p == foundp || (*p != ' ' && *p != '\t')) goto failed; *stop_code0 = strtol(foundp, NULL, 16); if (*stop_code0 != 0x1f09 && *stop_code0 != 0x1f41) goto failed; /* * Parse stop_code1. */ while (*p == ' ' || *p == '\t') p++; if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X')) p += 2; foundp = p; while (ASCII_ISXDIGIT(*p)) p++; if (p == foundp || (*p != ' ' && *p != '\t' && *p != '\0')) goto failed; *stop_code1 = strtol(foundp, NULL, 16); if (0xffff < *stop_code1) goto failed; while (*p == ' ' || *p == '\t') p++; if (*p != '\0') goto failed; return 0; /* * An error occurs... */ failed: fprintf(stderr, _("%s: invalid stop code `%s'\n"), invoked_name, argument); return -1; } /* * Parse stop code given as argument of the `-t' option. */ static int parse_text_length_argument(const char *argument, ssize_t *text_length) { const char *p = argument; const char *foundp; while (*p == ' ' || *p == '\t') p++; foundp = p; while (ASCII_ISDIGIT(*p)) p++; if (p == foundp) goto failed; while (*p == ' ' || *p == '\t') p++; if (*p != '\0') goto failed; *text_length = strtol(foundp, NULL, 10); return 0; /* * An error occurs... */ failed: fprintf(stderr, _("%s: invalid text length `%s'\n"), invoked_name, argument); return -1; } /* * Parse text position given as argument of the `-p' option. */ static int parse_text_position_argument(const char *argument, EB_Position *text_position) { const char *p = argument; const char *end_p; int page; int offset; /* * Parse page. */ while (*p == ' ' || *p == '\t') p++; if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X')) p += 2; page = strtol(p, (char **)&end_p, 16); if (end_p == p || *end_p != ':') goto failed; if (page <= 0) goto failed; /* * Parse offset. */ p = end_p + 1; if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X')) p += 2; offset = strtol(p, (char **)&end_p, 16); if (end_p == p) goto failed; while (*end_p == ' ' || *end_p == '\t') end_p++; if (*end_p != '\0') goto failed; if (offset < 0 || EB_SIZE_PAGE <= offset) goto failed; text_position->page = page; text_position->offset = offset; return 0; /* * An error occurs... */ failed: fprintf(stderr, _("%s: invalid text position `%s'\n"), invoked_name, argument); return -1; } /* * Output help message to standard out, then exit. */ static void output_help(void) { printf(_("Usage: %s [option...] [book-directory] subbook\n"), program_name); printf(_("Options:\n")); printf(_(" -c CODE, --code CODE\n")); printf(_(" set stop code manually\n")); printf(_(" -h --help display this help, then exit\n")); printf(_(" -l LENGTH, --text-length LENGTH\n")); printf(_(" maximum length of output text\n")); printf(_(" (default: %d)\n"), DEFAULT_MAX_TEXT_LENGTH); printf(_(" -n --no-candidates suppress stop code candidates\n")); printf(_(" -p PAGE:OFFSET, --text-position PAGE:OFFSET\n")); printf(_(" start position of text\n")); printf(_(" -v --version display version number, then exit\n")); printf(_("\nArgument:\n")); printf(_(" book-directory top directory of a CD-ROM book\n")); printf(_(" (default: %s)\n"), DEFAULT_BOOK_DIRECTORY); printf(_("\nReport bugs to %s.\n"), MAILING_ADDRESS); fflush(stdout); } /* * Scan stop code in text. */ static int scan_subbook_text(const char *book_directory, const char *subbook_name, EB_Position *text_position, ssize_t max_text_length, int show_stop_code_flag, unsigned int stop_code0, unsigned int stop_code1) { EB_Error_Code error_code; EB_Book book; EB_Hookset hookset; EB_Hook hook; EB_Appendix appendix; EB_Appendix_Subbook appendix_subbook; EB_Subbook_Code subbook_code; char text[EB_SIZE_PAGE]; ssize_t text_length; /* * Initialize EB Library, `book', `appendix' and `hookset'. */ eb_initialize_library(); eb_initialize_book(&book); eb_initialize_appendix(&appendix); eb_initialize_hookset(&hookset); /* * Bind `book'. */ error_code = eb_bind(&book, book_directory); if (error_code != EB_SUCCESS) { fprintf(stderr, _("%s: failed to bind the book, %s: %s\n"), program_name, eb_error_message(error_code), book_directory); goto die; } /* * Get a subbook code from the subbook name. */ error_code = find_subbook(&book, subbook_name, &subbook_code); if (error_code != EB_SUCCESS) { fprintf(stderr, "%s: %s: %s\n", program_name, eb_error_message(error_code), subbook_name); goto die; } /* * Set current subbook. */ if (eb_set_subbook(&book, subbook_code) < 0) { fprintf(stderr, _("%s: failed to set the current subbook, %s\n"), program_name, eb_error_message(error_code)); goto die; } /* * Set stop-code manually. * (we hack `appendix' directly.) */ appendix.code = subbook_code; appendix.subbook_current = &appendix_subbook; appendix_subbook.code = subbook_code; appendix_subbook.stop_code0 = stop_code0; appendix_subbook.stop_code1 = stop_code1; /* * Set text hooks (for 0x1f41 and 0x1f09). */ if (show_stop_code_flag) { hook.code = EB_HOOK_BEGIN_KEYWORD; hook.function = hook_stop_code; eb_set_hook(&hookset, &hook); hook.code = EB_HOOK_SET_INDENT; hook.function = hook_stop_code; eb_set_hook(&hookset, &hook); } /* * Get a position where the text data starts, if text_position * is {page=0, offset=0}. */ if (text_position->page == 0 && text_position->offset == 0) { error_code = eb_text(&book, text_position); if (error_code != EB_SUCCESS) { fprintf(stderr, _("%s: failed to get text information, %s\n"), program_name, eb_error_message(error_code)); goto die; } } /* * Read text. */ error_code = eb_seek_text(&book, text_position); if (error_code != EB_SUCCESS) { fprintf(stderr, "%s: %s\n", program_name, eb_error_message(error_code)); goto die; } text_length = 0; while (max_text_length == 0 || text_length < max_text_length) { ssize_t result_length; size_t read_length; if (max_text_length == 0 || sizeof(text) - 1 <= max_text_length - text_length) { read_length = sizeof(text) - 1; } else { read_length = max_text_length - text_length; } error_code = eb_read_text(&book, &appendix, &hookset, NULL, read_length, text, &result_length); if (error_code != EB_SUCCESS) { fprintf(stderr, _("%s: failed to read text, %s\n"), program_name, eb_error_message(error_code)); goto die; } if (result_length <= 0) break; fputs_eucjp_to_locale(text, stdout); text_length += result_length; } /* * Finalize `hookset', `appendix', `book' and EB Library. */ eb_finalize_hookset(&hookset); eb_finalize_appendix(&appendix); eb_finalize_book(&book); eb_finalize_library(); return 0; /* * An error occurs... */ die: eb_finalize_hookset(&hookset); eb_finalize_appendix(&appendix); eb_finalize_book(&book); eb_finalize_library(); return -1; } /* * Hook function for EB_HOOK_BEGIN_KEYWORD and EB_HOK_SET_INDENT. */ static EB_Error_Code hook_stop_code(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv) { char string[EB_SIZE_PAGE]; if (0 < book->text_context.printable_count) { sprintf(string, "\n=== stop-code?: 0x%04x 0x%04x ===\n", argv[0], argv[1]); eb_write_text_string(book, string); } return EB_SUCCESS; }