eb/ebstopcode/ebstopcode.c
2016-10-28 20:02:30 -07:00

631 lines
16 KiB
C

/* -*- 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 <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#ifdef ENABLE_NLS
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#include <libintl.h>
#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;
}