/*  Copyright (C) 2001-2002  Kenichi Suto
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include "defs.h"
#include "global.h"

#include "eb.h"
#include "jcode.h"

#include "dictheading.h"
#include "dialog.h"

#ifdef __FreeBSD__
#include <pthread.h>
#endif

#include <iconv.h>
#include <langinfo.h>
#include <wchar.h>

#define MAX_HITS            50
#define MAXLEN_HEADING     65535
#define MAXLEN_TEXT      65535

#define EBOOK_MAX_KEYWORDS 256

#define MAX_BUFSIZE	65535

extern GList *group_list;
extern GList *book_list;
extern gint global_multi_code;

static GtkWidget *cancel_dialog;
static GtkWidget *label_match;
static GtkWidget *progress;
static pthread_t tid;
static gint tag_timeout;
static gint thread_running = 0;
static gint hit_count=0;
static gfloat search_progress;


gint ebook_simple_search(BOOK_INFO *binfo, char *word, gint method);
static gint ebook_ending_search(BOOK_INFO *binfo, char *word, gint method);

static EB_Hookset text_hookset;
static EB_Hookset heading_hookset;
static EB_Hookset candidate_hookset;

static gboolean ebook_initialized = FALSE;

EB_Error_Code ebook_set_subbook(BOOK_INFO *binfo);

static EB_Error_Code hook_initialize(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_narrow_font(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_wide_font(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_indent(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_newline(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_no_newline(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_reference(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_candidate(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_narrow(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_superscript(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_subscript(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_emphasis(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_keyword(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_modification(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_euc_to_ascii(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_color(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_mono(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_gray(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_wave(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_mpeg(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);

static EB_Error_Code hook_graphic_reference(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv);


static EB_Hook text_hooks[] = {
//  {EB_HOOK_INITIALIZE,             hook_initialize},
//  {EB_HOOK_STOP_CODE,              eb_hook_stop_code},
//  {EB_HOOK_STOP_CODE,              hook_stopcode},
  {EB_HOOK_SET_INDENT,                hook_indent},
  {EB_HOOK_NEWLINE,                hook_newline},
  {EB_HOOK_NARROW_FONT,            hook_narrow_font},
  {EB_HOOK_WIDE_FONT,	           hook_wide_font},
  {EB_HOOK_BEGIN_REFERENCE,        hook_reference},
  {EB_HOOK_END_REFERENCE,          hook_reference},
  {EB_HOOK_BEGIN_CANDIDATE,        hook_candidate},
  {EB_HOOK_END_CANDIDATE_LEAF,     hook_candidate},
  {EB_HOOK_END_CANDIDATE_GROUP,    hook_candidate},
//  {EB_HOOK_NARROW_JISX0208,        hook_euc_to_ascii},
  {EB_HOOK_BEGIN_COLOR_BMP,        hook_color},
  {EB_HOOK_BEGIN_COLOR_JPEG,       hook_color},
  {EB_HOOK_END_COLOR_GRAPHIC,      hook_color},
  {EB_HOOK_BEGIN_MONO_GRAPHIC,     hook_mono},
  {EB_HOOK_END_MONO_GRAPHIC,       hook_mono},
  {EB_HOOK_BEGIN_GRAY_GRAPHIC,     hook_gray},
  {EB_HOOK_END_GRAY_GRAPHIC,       hook_gray},
  {EB_HOOK_BEGIN_WAVE,             hook_wave},
  {EB_HOOK_END_WAVE,               hook_wave},
  {EB_HOOK_BEGIN_MPEG,             hook_mpeg},
  {EB_HOOK_END_MPEG,               hook_mpeg},
//  {EB_HOOK_BEGIN_GRAPHIC_REFERENCE,hook_graphic_reference},
//  {EB_HOOK_END_GRAPHIC_REFERENCE,  hook_graphic_reference},
//  {EB_HOOK_GRAPHIC_REFERENCE,      hook_graphic_reference},
//  {EB_HOOK_BEGIN_NO_NEWLINE,       hook_no_newline},
//  {EB_HOOK_END_NO_NEWLINE,         hook_no_newline},
//  {EB_HOOK_BEGIN_NARROW,           hook_narrow},
//  {EB_HOOK_END_NARROW,             hook_narrow},
  {EB_HOOK_BEGIN_SUPERSCRIPT,      hook_superscript},
  {EB_HOOK_END_SUPERSCRIPT,        hook_superscript},
  {EB_HOOK_BEGIN_SUBSCRIPT,        hook_subscript},
  {EB_HOOK_END_SUBSCRIPT,          hook_subscript},
  {EB_HOOK_BEGIN_EMPHASIS,         hook_emphasis},
  {EB_HOOK_END_EMPHASIS,           hook_emphasis},
  {EB_HOOK_BEGIN_KEYWORD,          hook_keyword},
  {EB_HOOK_END_KEYWORD,            hook_keyword},
#ifdef EB_HOOK_BEGIN_DECORATION
  {EB_HOOK_BEGIN_DECORATION,       hook_modification},
#endif
#ifdef EB_HOOK_END_DECORATION
  {EB_HOOK_END_DECORATION,         hook_modification},
#endif
  {EB_HOOK_NULL, NULL},
};


static EB_Hook heading_hooks[] = {
  {EB_HOOK_NEWLINE,                hook_newline},
  {EB_HOOK_NARROW_FONT,            hook_narrow_font},
  {EB_HOOK_WIDE_FONT,	           hook_wide_font},
//  {EB_HOOK_BEGIN_CANDIDATE,        hook_candidate},
//  {EB_HOOK_END_CANDIDATE_LEAF,     hook_candidate},
//  {EB_HOOK_END_CANDIDATE_GROUP,    hook_candidate},
  {EB_HOOK_NARROW_JISX0208,        eb_hook_euc_to_ascii},
  {EB_HOOK_BEGIN_SUPERSCRIPT,      hook_superscript},
  {EB_HOOK_END_SUPERSCRIPT,        hook_superscript},
  {EB_HOOK_BEGIN_SUBSCRIPT,        hook_subscript},
  {EB_HOOK_END_SUBSCRIPT,          hook_subscript},
  {EB_HOOK_NULL, NULL},
};

static EB_Hook candidate_hooks[] = {
  {EB_HOOK_NARROW_FONT,            hook_narrow_font},
  {EB_HOOK_WIDE_FONT,	           hook_wide_font},
  {EB_HOOK_BEGIN_CANDIDATE,        hook_candidate},
  {EB_HOOK_END_CANDIDATE_LEAF,     hook_candidate},
  {EB_HOOK_END_CANDIDATE_GROUP,    hook_candidate},
  {EB_HOOK_NULL, NULL},
};

static EB_Error_Code hook_initialize(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
	return EB_SUCCESS;
}

static EB_Error_Code hook_narrow_font(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{

	char text[64];

	sprintf (text, "<gaiji code=h%04x>", argv[0]);
	eb_write_text_string(book, text);

	return EB_SUCCESS;
}

static EB_Error_Code hook_wide_font(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
	char text[64];

	sprintf (text, "<gaiji code=z%04x>", argv[0]);
	eb_write_text_string(book, text);

	return EB_SUCCESS;
}

static EB_Error_Code hook_indent(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
	char text[64];

	sprintf (text, "<indent position=%d>", argv[1]);
	eb_write_text_string(book, text);
/*
	for(i = 0 ; i < argv[1] ; i ++){
		eb_write_text_string(book, "  ");
	}
*/
	return EB_SUCCESS;
}

static EB_Error_Code hook_newline(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{

        eb_write_text_string(book, "\n");
	return EB_SUCCESS;
}


static EB_Error_Code hook_narrow(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
	switch (code) {
	case EB_HOOK_BEGIN_NARROW:
//		eb_write_text_string(book, "<narrow>");
		break;
	case EB_HOOK_END_NARROW:
//		eb_write_text_string(book, "</narrow>");
		break;
	}
	return EB_SUCCESS;

}

static EB_Error_Code hook_no_newline(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
	switch (code) {
	case EB_HOOK_BEGIN_NO_NEWLINE:
//		eb_write_text_string(book, "<nonewline>");
		break;
	case EB_HOOK_END_NO_NEWLINE:
//		eb_write_text_string(book, "</nonewline>");
		break;
	}
	return EB_SUCCESS;

}

static EB_Error_Code hook_reference(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
	gchar text[64];


	switch (code) {
	case EB_HOOK_BEGIN_REFERENCE:
		eb_write_text_string(book, "<reference>");
		break;
	case EB_HOOK_END_REFERENCE:
		sprintf (text, "</reference page=0x%x offset=0x%x>", argv[1], argv[2]);
		eb_write_text_string(book, text);
		break;
	}
	return EB_SUCCESS;

}

static EB_Error_Code hook_candidate(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
	gchar text[64];


	switch (code) {
	case EB_HOOK_BEGIN_CANDIDATE:
		eb_write_text_string(book, "<candidate>");
		break;
	case EB_HOOK_END_CANDIDATE_LEAF:
		sprintf (text, "</candidate>", argv[1], argv[2]);
		eb_write_text_string(book, text);
		break;
	case EB_HOOK_END_CANDIDATE_GROUP:
		sprintf (text, "</candidate page=0x%x offset=0x%x>", argv[1], argv[2]);
		eb_write_text_string(book, text);
		break;
	}
	return EB_SUCCESS;

}

static EB_Error_Code hook_superscript(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
	gchar text[64];


	switch (code) {
	case EB_HOOK_BEGIN_SUPERSCRIPT:
		eb_write_text_string(book, "<superscript>");
		break;
	case EB_HOOK_END_SUPERSCRIPT:
		sprintf (text, "</superscript>");
		eb_write_text_string(book, text);
		break;
	}
	return EB_SUCCESS;
}

static EB_Error_Code hook_subscript(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
	gchar text[64];


	switch (code) {
	case EB_HOOK_BEGIN_SUBSCRIPT:
		eb_write_text_string(book, "<subscript>");
		break;
	case EB_HOOK_END_SUBSCRIPT:
		sprintf (text, "</subscript>");
		eb_write_text_string(book, text);
		break;
	}
	return EB_SUCCESS;
}

static EB_Error_Code hook_emphasis(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
	gchar text[64];


	switch (code) {
	case EB_HOOK_BEGIN_EMPHASIS:
		eb_write_text_string(book, "<emphasis>");
		break;
	case EB_HOOK_END_EMPHASIS:
		sprintf (text, "</emphasis>");
		eb_write_text_string(book, text);
		break;
	}
	return EB_SUCCESS;
}

static EB_Error_Code hook_keyword(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
	gchar text[64];


	switch (code) {
	case EB_HOOK_BEGIN_KEYWORD:
		sprintf (text, "<keyword argv1=%x>", argv[1]);
		eb_write_text_string(book, text);
		break;
	case EB_HOOK_END_KEYWORD:
		sprintf (text, "</keyword>");
		eb_write_text_string(book, text);
		break;
	}
	return EB_SUCCESS;
}


static EB_Error_Code hook_modification(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
	gchar text[64];

#ifdef EB_HOOK_BEGIN_DECORATION
#ifdef EB_HOOK_END_DECORATION

	switch (code) {
	case EB_HOOK_BEGIN_DECORATION:
		sprintf (text, "<modification method=%d>", argv[1]);
		eb_write_text_string(book, text);
		break;
	case EB_HOOK_END_DECORATION:
		sprintf (text, "</modification>");
		eb_write_text_string(book, text);
		break;
	}

#endif
#endif
	return EB_SUCCESS;
}


/*
 * Hook for a reference to color graphic data.
 */
static EB_Error_Code hook_color(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
	gchar text[64];

	switch (code) {
	case EB_HOOK_BEGIN_COLOR_JPEG:

		sprintf (text, "<jpeg page=0x%x offset=0x%x>", argv[2], argv[3]);
		eb_write_text_string(book, text);
		break;
	case EB_HOOK_BEGIN_COLOR_BMP:
		sprintf (text, "<bmp page=0x%x offset=0x%x>", argv[2], argv[3]);
		eb_write_text_string(book, text);
		break;
/*
	case EB_HOOK_END_COLOR_GRAPHIC:
		sprintf (text, "</jpeg=0x%x:0x%x>", argv[2], argv[3]);
		eb_write_text_string(book, text);
		break;
*/
	}
	return EB_SUCCESS;
}


/*
 * Hook for a reference to MONO graphic data.
 */
static EB_Error_Code hook_mono(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
	gchar text[64];

	switch (code) {
	case EB_HOOK_BEGIN_MONO_GRAPHIC:
		// argv[2] : height
		// argv[3] : width

		sprintf (text, "<mono width=%d height=%d>", argv[3], argv[2]);
		eb_write_text_string(book, text);
		break;

	case EB_HOOK_END_MONO_GRAPHIC:
		// argv[1] : block
		// argv[2] : offset

		sprintf (text, "</mono page=0x%x offset=0x%x>", argv[1], argv[2]);
		eb_write_text_string(book, text);
		break;
	}
	return EB_SUCCESS;
}

/*
 * Hook for a reference to GRAY graphic data.
 */
static EB_Error_Code hook_gray(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
	gchar text[64];

	switch (code) {
	case EB_HOOK_BEGIN_GRAY_GRAPHIC:

		sprintf (text, "<gray page=0x%x offset=0x%x>", argv[2], argv[3]);
		eb_write_text_string(book, text);
		break;
/*
	case EB_HOOK_END_GRAY_GRAPHIC:
		sprintf (text, "</gray=0x%x:0x%x>", argv[2], argv[3]);
		eb_write_text_string(book, text);
		break;
*/
	}
	return EB_SUCCESS;
}


/*
 * Hook for a reference to WAVE sound data.
 */
static EB_Error_Code hook_wave(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
	off_t start_location;
	off_t end_location;
	size_t data_size;
	gchar text[64];

	/*
	 * Set binary context.
	 */
	start_location = (off_t)(argv[2] - 1) * EB_SIZE_PAGE + argv[3];
	end_location   = (off_t)(argv[4] - 1) * EB_SIZE_PAGE + argv[5];
	data_size = end_location - start_location;

	switch (code) {
	case EB_HOOK_BEGIN_WAVE:
		eb_write_text_string(book, "<wave>");
		break;
	case EB_HOOK_END_WAVE:
//		sprintf (text, "</wave start_page=0x%x start_offset=0x%x end_page=0x%x end_offset=0x%x>", argv[2], argv[3], argv[4], argv[5]);
		sprintf (text, "</wave page=0x%x offset=0x%x size=%d>", argv[2], argv[3], data_size);
		eb_write_text_string(book, text);
		break;
	}
	return EB_SUCCESS;
}


/*
 * Hook for a reference to MPEG sound data.
 */
static EB_Error_Code hook_mpeg(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
    char file_name[EB_MAX_DIRECTORY_NAME_LENGTH + 1];
    char text[256];

	switch (code) {
	case EB_HOOK_BEGIN_MPEG:
		break;
	case EB_HOOK_END_MPEG:
		if (eb_compose_movie_file_name(argv + 2, file_name) != EB_SUCCESS)
			return EB_SUCCESS;
		sprintf(text, "<mpeg filename=%s>", file_name);
		eb_write_text_string(book, text);
		break;
	}
    return EB_SUCCESS;
}


/*
 * Hook for a reference to graphic reference.
 */
static EB_Error_Code hook_graphic_reference(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
/*    char file_name[EB_MAX_DIRECTORY_NAME_LENGTH + 1]; */
    char text[256];

	switch (code) {
	case EB_HOOK_GRAPHIC_REFERENCE:
	case EB_HOOK_BEGIN_GRAPHIC_REFERENCE:
		sprintf (text, "<graphic_reference page=0x%x offset=0x%x>", argv[1], argv[2]);
		eb_write_text_string(book, text);
		break;
	case EB_HOOK_END_GRAPHIC_REFERENCE:
		break;
	}
    return EB_SUCCESS;
}

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

/*
 * Latin-1 character to entity reference table.
 * (e.g. 'a  --> &aacute;)
 */
const char *latin1_entity_name_table[] = {
    NULL, NULL, NULL,   NULL, NULL, NULL, NULL,  NULL,	/* 0x00 */
    NULL, NULL, NULL,   NULL, NULL, NULL, NULL,  NULL,	/* 0x08 */
    NULL, NULL, NULL,   NULL, NULL, NULL, NULL,  NULL,	/* 0x10 */
    NULL, NULL, NULL,   NULL, NULL, NULL, NULL,  NULL,	/* 0x18 */
    NULL, NULL, "quot", NULL, NULL, NULL, "amp", NULL,	/* 0x20 */
    NULL, NULL, NULL,   NULL, NULL, NULL, NULL,  NULL,	/* 0x28 */
    NULL, NULL, NULL,   NULL, NULL, NULL, NULL,  NULL,	/* 0x30 */
    NULL, NULL, NULL,   NULL, "lt", NULL, "gt",  NULL,	/* 0x38 */
    NULL, NULL, NULL,   NULL, NULL, NULL, NULL,  NULL,	/* 0x40 */
    NULL, NULL, NULL,   NULL, NULL, NULL, NULL,  NULL,	/* 0x48 */
    NULL, NULL, NULL,   NULL, NULL, NULL, NULL,  NULL,	/* 0x50 */
    NULL, NULL, NULL,   NULL, NULL, NULL, NULL,  NULL,	/* 0x58 */
    NULL, NULL, NULL,   NULL, NULL, NULL, NULL,  NULL,	/* 0x60 */
    NULL, NULL, NULL,   NULL, NULL, NULL, NULL,  NULL,	/* 0x68 */
    NULL, NULL, NULL,   NULL, NULL, NULL, NULL,  NULL,	/* 0x70 */
    NULL, NULL, NULL,   NULL, NULL, NULL, NULL,  NULL,	/* 0x78 */
    NULL, NULL, NULL,   NULL, NULL, NULL, NULL,  NULL,	/* 0x80 */
    NULL, NULL, NULL,   NULL, NULL, NULL, NULL,  NULL,	/* 0x88 */
    NULL, NULL, NULL,   NULL, NULL, NULL, NULL,  NULL,	/* 0x90 */
    NULL, NULL, NULL,   NULL, NULL, NULL, NULL,  NULL,	/* 0x98 */
    "nbsp",   "iexcl",  "cent",   "pound",	/* 0xa0 */
    "curren", "yen",    "brvbar", "sect",	/* 0xa4 */
    "uml",    "copy",   "ordf",   "laquo",	/* 0xa8 */
    "not",    "shy",    "reg",    "macr",	/* 0xac */
    "deg",    "plusmn", "sup2",   "sup3",	/* 0xb0 */
    "acute",  "micro",  "para",   "middot",	/* 0xb4 */
    "cedil",  "sup1",   "ordm",   "requo",	/* 0xb8 */
    "frac14", "frac12", "farc34", "iquest",	/* 0xbc */
    "Agrave", "Aacute", "Acirc",  "Atilde",	/* 0xc0 */
    "Auml",   "Aring",  "AElig",  "Ccedil",	/* 0xc4 */
    "Egrave", "Eacute", "Ecirc",  "Euml",	/* 0xc8 */
    "Igrave", "Iacute", "Icirc",  "Iuml",	/* 0xcc */
    "ETH",    "Ntilde", "Ograve", "Oacute",	/* 0xd0 */
    "Ocirc",  "Otilde", "Ouml",   "times",	/* 0xd4 */
    "Oslash", "Ugrave", "Uacute", "Ucirc",	/* 0xd8 */
    "Uuml",   "Yacute", "THORN",  "szlig",	/* 0xdc */
    "agrave", "aacute", "acirc",  "atilde",	/* 0xe0 */
    "auml",   "aring",  "aelig",  "ccedil",	/* 0xe4 */
    "egrave", "eacute", "ecirc",  "euml",	/* 0xe8 */
    "igrave", "iacute", "icirc",  "iuml",	/* 0xec */
    "eth",    "ntilde", "ograve", "oacute",	/* 0xf0 */
    "ocirc",  "otilde", "ouml",   "divide",	/* 0xf4 */
    "oslash", "ugrave", "uacute", "ucirc",	/* 0xf8 */
    "uuml",   "yacute", "thorn",  "yuml"	/* 0xfc */
};

/*
 * Hook which converts a character from EUC-JP to ASCII.
 */

static EB_Error_Code hook_euc_to_ascii(EB_Book *book, EB_Appendix *appendix, void *container, EB_Hook_Code code, int argc, const unsigned int *argv)
{
    int in_code1, in_code2;
    int out_code = 0;
    const char *entity;

    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 {
	entity = latin1_entity_name_table[out_code];
	if (entity != NULL) {
	    eb_write_text_byte1(book, '&');
	    eb_write_text_string(book, entity);
	    eb_write_text_byte1(book, ';');
	} else {
	    eb_write_text_byte1(book, out_code);
	}
    }
	
    return EB_SUCCESS;
}





gint ebook_search_method(){
	char *text;
	int i;

	text = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(combo_method)->entry));
	for(i=0 ; search_method[i].name != 0 ; i ++){
		if(strcmp(text, search_method[i].name) == 0)
			return(search_method[i].code);
	}

	return(SEARCH_METHOD_UNKNOWN);
}

BOOK_INFO *load_book(char *book_path, int subbook_no, char *appendix_path, int appendix_subbook_no)
{
	EB_Error_Code error_code;
	BOOK_INFO *binfo;
	EB_Subbook_Code sublist[EB_MAX_SUBBOOKS];
/*	EB_Multi_Search_Code multi_list[EB_MAX_MULTI_SEARCHES]; */
/*	int multi_count; */
	int subcount /* , i */;
	char buff[512];
	GList *book_item;

	// $B$9$G$KF1$8=q@R$,$J$$$+8!:w$9$k(B

//	g_print("load_book(%s, %d, %s, %d\n", book_path, subbook_no, appendix_path, appendix_subbook_no);

	book_item = g_list_first(book_list);
	while(book_item != NULL){
		binfo = (BOOK_INFO *)(book_item->data);
		if((strcmp(binfo->book_path, book_path) == 0) && 
		   (binfo->subbook_no == subbook_no)) {
			if((binfo->appendix_path != NULL) &&
			   (appendix_path != NULL) &&
			   (strcmp(binfo->appendix_path, appendix_path) == 0) && 
			   (binfo->appendix_subbook_no == appendix_subbook_no)) {
				return(binfo);
			} else {
				unload_book(binfo);
				g_free(binfo);
				break;
			}
		}
		book_item = g_list_next(book_item);
	}

	binfo = (BOOK_INFO *)calloc(sizeof(BOOK_INFO),1);
	if(binfo == NULL){
		fprintf(stderr, "No memory\n");
		exit(1);
	}

	binfo->book_path = strdup(book_path);
	binfo->subbook_no = subbook_no;
	if(appendix_path){
		binfo->appendix_path = strdup(appendix_path);
		binfo->appendix_subbook_no = appendix_subbook_no;
	}

	binfo->book = (EB_Book *) malloc(sizeof(EB_Book));
	eb_initialize_book(binfo->book);
	error_code = eb_bind(binfo->book, 
			     binfo->book_path);
	if(error_code != EB_SUCCESS){
		fprintf(stderr, "Failed to bind the book : %s\n",
			eb_error_message(error_code));
		goto FAILED;
	}

	error_code = eb_subbook_list(
		binfo->book, 
		sublist, 
		&subcount);
	if(error_code != EB_SUCCESS){
		fprintf(stderr, "Failed to get a subbook list : %s\n", 
			eb_error_message(error_code));
		goto FAILED;
	}


	error_code = eb_subbook_directory2(
		binfo->book, 
		sublist[binfo->subbook_no], 
		buff);
	if (error_code != EB_SUCCESS){
		fprintf(stderr, "failed to get the directory : %s\n",
			eb_error_message(error_code));
		goto FAILED;
	}
	binfo->subbook_dir = strdup(buff);

	error_code = eb_subbook_title2(
		binfo->book, 
		sublist[binfo->subbook_no], 
		buff);
	if (error_code != EB_SUCCESS){
		fprintf(stderr, "failed to get the title : %s\n",
			eb_error_message(error_code));
		goto FAILED;
	}
	binfo->subbook_title = strdup(buff);

	if(binfo->appendix_path != NULL){
		binfo->appendix = (EB_Appendix *) malloc(sizeof(EB_Appendix));
		eb_initialize_appendix(binfo->appendix);

		error_code = eb_bind_appendix(binfo->appendix,
					      binfo->appendix_path);
		if(error_code != EB_SUCCESS){
			fprintf(stderr, "Failed to bind appendix : %s\n",
				eb_error_message(error_code));
			goto FAILED;
		}
	}


	ebook_set_subbook(binfo);

	binfo->available = TRUE;

	if(eb_have_word_search(binfo->book)){
		binfo->search_method[SEARCH_METHOD_WORD] = TRUE;
	}

	if(eb_have_endword_search(binfo->book)){
		binfo->search_method[SEARCH_METHOD_ENDWORD] = TRUE;
	}

	if(eb_have_exactword_search(binfo->book)){
		binfo->search_method[SEARCH_METHOD_EXACTWORD] = TRUE;
	}
	
	if(eb_have_keyword_search(binfo->book)){
		binfo->search_method[SEARCH_METHOD_KEYWORD] = TRUE;
	}
/*	
	eb_multi_search_list(binfo->book, multi_list, &multi_count);
	for (i = 0; i < multi_count; i++) {
		binfo->search_method[SEARCH_METHOD_MULTI1+i] = TRUE;
	}
*/
	if(eb_have_multi_search(binfo->book)){
		binfo->search_method[SEARCH_METHOD_MULTI] = TRUE;
	}
	
	if(eb_have_menu(binfo->book)){
		binfo->search_method[SEARCH_METHOD_MENU] = TRUE;
	}

	if(eb_have_copyright(binfo->book)){
		binfo->search_method[SEARCH_METHOD_COPYRIGHT] = TRUE;
	}

	if (eb_have_font(binfo->book, EB_FONT_16)){
		error_code = eb_set_font(binfo->book, EB_FONT_16);
		if (error_code != EB_SUCCESS) {
			fprintf(stderr, "failed to set font : subbook=%s\n%s\n",
				binfo->subbook_title, 
				eb_error_message(error_code));
//				return(error_code);
		}
	}

	book_list = g_list_append(book_list, binfo);

	return(binfo);

 FAILED:
	//	if((binfo != NULL) && (binfo->book_path != NULL))
	//		free(binfo->book_path);
	//	if((binfo != NULL) && (binfo->book != NULL))
	//		free(binfo->book);
	//	if(binfo != NULL)
	//		free(binfo);
	binfo->available = FALSE;
	return(binfo);
}

static void free_gaiji(BOOK_INFO *binfo){
    /*    GAIJI_CACHE *gaiji  ,  *tmp_g ; */
	
	GList *lists[8];
	GList *item;
	gint i;

	lists[0] = binfo->gaiji_narrow16;
	lists[1] = binfo->gaiji_narrow24;
	lists[2] = binfo->gaiji_narrow30;
	lists[3] = binfo->gaiji_narrow48;
	lists[4] = binfo->gaiji_wide16;
	lists[5] = binfo->gaiji_wide24;
	lists[6] = binfo->gaiji_wide30;
	lists[7] = binfo->gaiji_wide48;


	
	for(i=0; i < 8 ; i ++){
		item = g_list_first(lists[i]);
		while(item){
			free(item->data);
			item = g_list_next(item);
		}
		g_list_free(lists[i]);
	}

	binfo->gaiji_narrow16 = NULL;
	binfo->gaiji_narrow24 = NULL;
	binfo->gaiji_narrow30 = NULL;
	binfo->gaiji_narrow48 = NULL;
	binfo->gaiji_wide16 = NULL;
	binfo->gaiji_wide24 = NULL;
	binfo->gaiji_wide30 = NULL;
	binfo->gaiji_wide48 = NULL;

}

void unload_book(BOOK_INFO *binfo)
{

    /*    GAIJI_CACHE *gaiji, *tmp_g */;


	eb_unset_subbook(binfo->book);
	eb_finalize_book(binfo->book);
	free(binfo->book_path);
	free(binfo->appendix_path);
	free(binfo->subbook_dir);
	free(binfo->subbook_title);

	free_gaiji(binfo);

	book_list = g_list_remove(book_list, binfo);

	return;
}

void check_search_method()
{
	BOOK_INFO *binfo;
	GList *book_item;
	gint method_count=0;

/*	char buff[512]; */
/*	int i; */


	menu_word_search = FALSE;
	menu_endword_search = FALSE;
	menu_exactword_search = FALSE;
	menu_keyword_search = FALSE;
	menu_menu = FALSE;
	menu_copyright = FALSE;
	menu_multi_search = FALSE;

	book_item = g_list_first(book_list);
	while(book_item != NULL){
		binfo = (BOOK_INFO *)(book_item->data);
		if(binfo->search_method[SEARCH_METHOD_WORD] == TRUE)
			menu_word_search = TRUE;
		if(binfo->search_method[SEARCH_METHOD_ENDWORD] == TRUE)
			menu_endword_search = TRUE;
		if(binfo->search_method[SEARCH_METHOD_EXACTWORD] == TRUE)
			menu_exactword_search = TRUE;
		if(binfo->search_method[SEARCH_METHOD_KEYWORD] == TRUE)
			menu_keyword_search = TRUE;
		if(binfo->search_method[SEARCH_METHOD_MENU] == TRUE)
			menu_menu = TRUE;
		if(binfo->search_method[SEARCH_METHOD_COPYRIGHT] == TRUE)
			menu_copyright = TRUE;
		if(binfo->search_method[SEARCH_METHOD_MULTI] == TRUE)
			menu_multi_search = TRUE;

		book_item = g_list_next(book_item);
	}

	method_count = 0;

	search_method[method_count].code = SEARCH_METHOD_AUTOMATIC;
	search_method[method_count].name = strdup(_("Automatic Search"));
	method_count ++;

	if(menu_exactword_search == TRUE){
		search_method[method_count].code = SEARCH_METHOD_EXACTWORD;
		search_method[method_count].name = strdup(_("Exactword Search"));
		method_count ++;
	}

	if(menu_word_search == TRUE){
		search_method[method_count].code = SEARCH_METHOD_WORD;
		search_method[method_count].name = strdup(_("Forward Search"));
		method_count ++;
	}

	if(menu_endword_search == TRUE){
		search_method[method_count].code = SEARCH_METHOD_ENDWORD;
		search_method[method_count].name = strdup(_("Backward Search"));
		method_count ++;
	}

	if(menu_keyword_search == TRUE){
		search_method[method_count].code = SEARCH_METHOD_KEYWORD;
		search_method[method_count].name = strdup(_("Keyword Search"));
		method_count ++;
	}

	if(menu_multi_search == TRUE){
		search_method[method_count].code = SEARCH_METHOD_MULTI;
		search_method[method_count].name = strdup(_("Multiword Search"));
		method_count ++;
	}

/*
	if(menu_menu == TRUE){
		search_method[method_count].code = SEARCH_METHOD_MENU;
		search_method[method_count].name = strdup(_("Menu"));
		method_count ++;
	}

	if(menu_copyright == TRUE){
		search_method[method_count].code = SEARCH_METHOD_COPYRIGHT;
		search_method[method_count].name = strdup(_("Copyright"));
		method_count ++;
	}
*/

	search_method[method_count].code = SEARCH_METHOD_FULL_TEXT;
	search_method[method_count].name = strdup(_("Full Text Search"));
	method_count ++;

	search_method[method_count].code = SEARCH_METHOD_INTERNET;
	search_method[method_count].name = strdup(_("Internet Search"));
	method_count ++;


	search_method[method_count].name = NULL;

}

gint ebook_start(){
	EB_Error_Code error_code;

	error_code = eb_initialize_library();
	if(error_code != EB_SUCCESS){
		fprintf(stderr, "Failed to initialize library : %s\n",
			eb_error_message(error_code));
		return(1);
	}


	eb_initialize_hookset (&text_hookset);

	error_code = eb_set_hooks (&text_hookset, text_hooks);
	if(error_code != EB_SUCCESS){
		fprintf(stderr, "Failed to set hookset(text) : %s\n",
			 eb_error_message(error_code));
		return(1);
	}


	eb_initialize_hookset (&heading_hookset);

	error_code = eb_set_hooks (&heading_hookset, heading_hooks);
	if(error_code != EB_SUCCESS){
		fprintf(stderr, "Failed to set hookset(heading) : %s\n",
			 eb_error_message(error_code));
		return(1);
	}

	eb_initialize_hookset (&candidate_hookset);

	error_code = eb_set_hooks (&candidate_hookset, candidate_hooks);
	if(error_code != EB_SUCCESS){
		fprintf(stderr, "Failed to set hookset(candidate) : %s\n",
			 eb_error_message(error_code));
		return(1);
	}

	ebook_initialized = TRUE;

	return(0);

}

gint ebook_end(){

    /*   GAIJI_CACHE *gaiji, *tmp_g ; */
	BOOK_INFO *binfo;
	GList *book_item;

	if(ebook_initialized == TRUE) {
		return(0);
	}

	book_item = g_list_first(book_list);
	while(book_item != NULL){
		binfo = (BOOK_INFO *)(book_item->data);
		eb_unset_subbook(binfo->book);
		eb_finalize_book(binfo->book);
	        free(binfo->book_path);
	        free(binfo->appendix_path);
	        free(binfo->subbook_dir);
	        free(binfo->subbook_title);
		
		free_gaiji(binfo);

		g_list_remove(book_list, book_item->data);
		book_item = g_list_next(book_item);
	}

	book_list = NULL;
	ebook_initialized = FALSE;

	return(0);
}

void split_word(gchar *word, gchar **keywords)
{
	gint i,j;
	gchar *p;
	gchar buff[512];

	p = word;
	i = 0;
	j = 0;
	keywords[0] = NULL;

	if(strlen(word) > 512){
		return;
	}
	
	while(1){
		switch(*p){
		case ' ':
		case '\t':
		case '\n':
		case '\0':
//			if(j != 0){
				buff[j] = '\0';
				keywords[i] = strdup(buff);
				i ++;
				keywords[i] = NULL;
				j = 0;
//			}
			break;
		default:
			buff[j] = *p;
			j ++;
		}
		p++;
		if((*p == '\0') || (i == EBOOK_MAX_KEYWORDS))
			break;
		
	}
	
	buff[j] = '\0';
	if(strlen(buff) != 0){
		keywords[i] = strdup(buff);
		i ++;
	}
	keywords[i] = NULL;
}

void cat_word(char *string, char **words){
	gint i;
	gint len = 0;

	sprintf(string, "%s", words[0]);	
	len = strlen(words[0]);

	for(i=1 ; words[i] != NULL ; i++){
		strcat(string, " ");
		strcat(string, words[i]);
		len = len + strlen(words[i]) + 1;
	}
	string[len] = '\0';
}

void free_words(char **words){
	gint i;

	for(i=0; words[i] != NULL ; i++)
		free(words[i]);
}

gboolean iseuckanji(guchar *buff){
	g_assert(buff != NULL);

	if((buff[0] >= 0xb0) && (buff[0] <= 0xf4) && 
	   (buff[1] >= 0xa1) && (buff[1] <= 0xfe))
		return(TRUE);
	return(FALSE);
}

gboolean iseuc(guchar *buff){
	g_assert(buff != NULL);

	if((buff[0] >= 0xa1) && (buff[0] <= 0xf4) && 
	   (buff[1] >= 0xa1) && (buff[1] <= 0xfe))
		return(TRUE);
	return(FALSE);
}

static gchar *locale2jis(gchar *inbuf){
	iconv_t cd;
	const char* ocode = "euc-jp";
	const char* icode;
//	const char* ocode = "iso-2022-jp";
//	const char* ocode = "shift_jis";

	int r = 0;
	size_t isize;
	size_t osize;
	char *outbuf;

	guchar *eucbuf=NULL;
	guchar *euc_p;
	guchar *jisbuf=NULL;
	guchar *jis_p;

	gint i;


	icode = nl_langinfo(CODESET);

	if((strcasecmp(icode, "EUC-JP") == 0) ||
	   (strcasecmp(icode, "EUCJP") == 0) ){
		euc_p = inbuf;
	} else {

		cd = iconv_open( ocode, icode );
		if( (int)cd == -1 )
			return(NULL);		
		
		isize = strlen(inbuf);
		osize = isize * 4;
		eucbuf = outbuf = malloc(osize);
#ifdef __FreeBSD__
		r = iconv(cd, (const char **)&inbuf, &isize, &outbuf, &osize);
#else		
		r = iconv(cd, &inbuf, &isize, &outbuf, &osize);
#endif
		iconv_close(cd);
		
		if(r == 1){
			perror("iconv");
			return(NULL);
		}
		euc_p = eucbuf;
	}

	jis_p = jisbuf = malloc(strlen(euc_p)*2);

	while(*euc_p != '\0'){

		if(isalnum(*euc_p)){
			*jis_p = 0x23;
			jis_p ++;
			*jis_p = *euc_p;
			jis_p ++;
		}
		else if(iseuc(euc_p)){
			*jis_p = *euc_p - 0x80;
			jis_p ++;
			euc_p++;
			*jis_p = *euc_p - 0x80;
			jis_p ++;
		}

		else if(*euc_p == 0x20){
			*jis_p = 0x21;
			jis_p ++;
			*jis_p = 0x21;
			jis_p ++;
		}

		else {
			*jis_p = 0x21;
			jis_p ++;
			*jis_p = 0x29;
			jis_p ++;
		}
			
		euc_p ++;
	}
	*jis_p = '\0';

	if(eucbuf != NULL)
		free(eucbuf);

	for(i=0;;i++){
		if(jisbuf[i] == '\0')
			break;
	}
	
	return(jisbuf);

}


static int check_match(char *haystack, char *needle){

/*	wchar_t *l_haystack; */
/*	wchar_t *l_needle; */
	char *rp;
	gint len;

	len = strlen(needle);

	rp = haystack;
	while(*rp != '\0'){
		if(strncasecmp(rp, needle, len) == 0)
			return(0);

		if(isascii(*rp))
			rp += 1;
		else 
			rp += 2;
	}

	return(1);

/*
	l_haystack = any_to_utf8(haystack);
	l_needle = any_to_utf8(needle);

	rp = wcsstr(l_haystack, l_needle);
	free(l_haystack);
	free(l_needle);

	if(rp == NULL)
		return(1);
	else
		return(0);
*/
	
}

gboolean full_text_search_ignore_case = TRUE;

static int jiscmp(unsigned char *haystack, unsigned char *needle){

	unsigned char *p1, *p2;

	p1 = haystack;
	p2 = needle;

	while(1) {
		if(isjisp(p1) != TRUE){
			p1 +=2;
			continue;
		}
			
		if(*p2 == '\0')
			return(0);
		else if(*p1 == '\0')
			return(1);
		else if((*p1 == *p2) && (*(p1+1) == *(p2+1))) {
			p1 +=2;
			p2 +=2;
			continue;
		} else if(full_text_search_ignore_case){
			guint c1, c2;

			c1 = eb_uint2(p1);
			c2 = eb_uint2(p2);

			if ((0x2341 <= c1) && (c1 <= 0x237a) &&
			    (0x2341 <= c2) && (c2 <= 0x237a)){

				if(((0x2361 <= c1) ? (c1 - 0x20) : c1) == ((0x2361 <= c2) ? (c2 - 0x20) : c2)){
					p1 += 2;
					p2 += 2;
					continue;
				}
			}
			return(1);
		} else {
			return(1);
		}
	}
}

static gboolean check_duplicate_hit(EB_Position pos){
	GList *l;
	SEARCH_RESULT *rp;

	l = search_result;
	while(l != NULL){
		rp = (SEARCH_RESULT *)(l->data);
		if((rp->pos_text.page == pos.page) &&
		   (rp->pos_text.offset == pos.offset))
			return(TRUE);
		l = g_list_next(l);
	}
	return(FALSE);
}

static gint count_result(GList *result)
{
	return(g_list_length(result));
}

EB_Error_Code ebook_my_backward_text(BOOK_INFO *binfo)
{

	EB_Error_Code error_code=EB_SUCCESS;
	EB_Position text_position;
	char data[EB_SIZE_PAGE+4];
/*	char *jisword; */
/*	char *word_p; */
	int i, length;
/*	char *p; */
/*	char heading[MAXLEN_TEXT + 1]; */
/*	SEARCH_RESULT *rp; */

	int start_page;
	int end_page;
	int current_page;
	int current_offset;
	int offset;
	int read_page;

	int stop_code;

/*	int page_count=0; */


	error_code = ebook_set_subbook(binfo);
	if(error_code != EB_SUCCESS){
		fprintf(stderr, "failed to set subbook : %s\n",
			eb_error_message(error_code));
		return(error_code);
	}

	start_page = binfo->book->subbook_current->text.start_page;
	end_page = binfo->book->subbook_current->text.end_page;

	error_code = eb_tell_text(binfo->book, &text_position);
	if(error_code != EB_SUCCESS){
		fprintf(stderr, "failed to tell text : %s\n",
			eb_error_message(error_code));
		return(error_code);
	}

	current_page = text_position.page;
	current_offset = text_position.offset;

	offset = current_offset;

	stop_code = binfo->book->text_context.auto_stop_code;

	for(read_page = current_page ; read_page >= start_page ; read_page --){

		text_position.page = read_page;
		text_position.offset = 0;

		error_code = eb_seek_text(binfo->book, &text_position);
		if (error_code != EB_SUCCESS) {
			fprintf(stderr, "failed to seek text, %s\n",
				eb_error_message(error_code));
			return(error_code);
		}

		memset(data, 0, EB_SIZE_PAGE + 4);

		error_code = eb_read_rawtext(binfo->book, 
					     EB_SIZE_PAGE+2, 
					     data,
					     &length);
		if (error_code != EB_SUCCESS || length != EB_SIZE_PAGE+2){
			fprintf(stderr, "failed to read rawtext, %s\n",
				eb_error_message(error_code));
			return(error_code);
		}

		if(stop_code != -1)
			binfo->book->text_context.auto_stop_code = stop_code;


		for(i=offset-2 ; i >= 0 ; i -=2){
			if(((binfo->appendix != NULL) &&
			    (eb_uint2(&data[i]) == binfo->appendix->subbook_current->stop_code0) &&
			    (eb_uint2(&data[i+2]) == binfo->appendix->subbook_current->stop_code1)) || 

			   ((binfo->appendix == NULL) &&
			    (eb_uint2(&data[i]) == 0x1f41) &&
			    (eb_uint1(&data[i+2]) == 0x01)) || 
			   (eb_uint2(&data[i]) == 0x1f02))
#if 0
			   ((eb_uint2(&data[i]) == 0x1f41) &&
			    (eb_uint2(&data[i+2]) == binfo->book->text_context.auto_stop_code)))
#endif
			{

				text_position.page = read_page;
				text_position.offset = i;

				error_code = eb_seek_text(binfo->book, &text_position);
				if (error_code != EB_SUCCESS) {
					fprintf(stderr, "failed to seek text, %s\n",
						eb_error_message(error_code));
					return(error_code);
				}

				if(stop_code != -1)
					binfo->book->text_context.auto_stop_code = stop_code;

				return(EB_SUCCESS);

			}

		}

		offset = EB_SIZE_PAGE + 2;
	}

	text_position.page = current_page;
	text_position.offset = current_offset;

	error_code = eb_seek_text(binfo->book, &text_position);
	if (error_code != EB_SUCCESS) {
		fprintf(stderr, "failed to seek text, %s\n",
			eb_error_message(error_code));
		return(error_code);
	}

	return(EB_ERR_FAIL_SEEK_TEXT);

}

static gint ebook_full_search(BOOK_INFO *binfo, char *word, gint method)
{

	EB_Error_Code error_code=EB_SUCCESS;
	EB_Position text_position;
	char data[EB_SIZE_PAGE];
	char *jisword;
	char *word_p;
	int i, length;
	char *p;
	char heading[MAXLEN_TEXT + 1];
	SEARCH_RESULT *rp;

	int start_page;
	int end_page;
	int current_page;

	int stop_code;

	int page_count=0;


	if((error_code = ebook_set_subbook(binfo)) != EB_SUCCESS)
		return(error_code);

	start_page = binfo->book->subbook_current->text.start_page;
	end_page = binfo->book->subbook_current->text.end_page;

//	g_print("start_page = 0x%0x end_page = 0x%0x\n", 
//		start_page, end_page);

	// auto_stop_code $B$r<hF@$9$k$?$a$K$$$C$?$sFI$`(B
	p = ebook_get_text(binfo, start_page, 0);
	if(p)
		free(p);
	stop_code = binfo->book->text_context.auto_stop_code;

	jisword = locale2jis(word);
	word_p = jisword;

	for(current_page = start_page ; current_page <= end_page ; current_page ++){

		search_progress = (float)(current_page - start_page) / (end_page - start_page + 1);
		page_count ++;

		text_position.page = current_page;
		text_position.offset = 0;

		error_code = eb_seek_text(binfo->book, &text_position);
		if (error_code != EB_SUCCESS) {
			fprintf(stderr, "failed to seek text, %s\n",
				eb_error_message(error_code));
			return(error_code);
		}

		error_code = eb_read_rawtext(binfo->book, 
					     EB_SIZE_PAGE, 
					     data,
					     &length);
		if (error_code != EB_SUCCESS || length != EB_SIZE_PAGE){
			fprintf(stderr, "failed to read rawtext, %s\n",
				eb_error_message(error_code));
			return(error_code);
		}

		for(i=0 ; i < EB_SIZE_PAGE ; i +=2){

			// $B@)8fJ8;z$OFI$_$H$P$9(B
			if(isjisp(&data[i]) != TRUE){
				continue;

			// $B0lCW(B
			} else if((data[i] == *word_p) && 
				  (data[i+1] == *(word_p+1))){

				// $B8!:w8l$O$^$@B3$$$F$$$k$+(B
				if (*(word_p+2) != '\0'){
					word_p += 2;
					continue;
				}
			// $BBgJ8;z$H>.J8;z$r6hJL$7$J$$>l9g(B
			} else if(full_text_search_ignore_case){
				guint c1, c2;

				c1 = eb_uint2(&data[i]);
				c2 = eb_uint2(word_p);

				// $B%"%k%U%!%Y%C%H$J$i(B
				if ((0x2341 <= c1) && (c1 <= 0x237a) &&
				    (0x2341 <= c2) && (c2 <= 0x237a)){
					// $B>.J8;z$K$7$F$+$i%^%C%A$5$;$k(B
					if(((0x2361 <= c1) ? (c1 - 0x20) : c1) == ((0x2361 <= c2) ? (c2 - 0x20) : c2)){
						// $B8!:w8l$O$^$@B3$$$F$$$k$+(B
						if (*(word_p+2) != '\0'){
							word_p += 2;
							continue;
						}
					} else {
						goto NO_MATCH;
					}
				} else {
					goto NO_MATCH;
				}
			} else {
				goto NO_MATCH;
			}


			// $B0lCW$7$?>l9g(B
			// $BFI$_9~$_=hM}(B

			text_position.page = current_page;
			text_position.offset = i;
//			g_print("found at (0x%x, 0x%x)0x%x\n", current_page, i, (current_page - 1) * EB_SIZE_PAGE + i);

			error_code = eb_seek_text(binfo->book, &text_position);
			if (error_code != EB_SUCCESS) {
				fprintf(stderr, "failed to seek text, %s\n",
					eb_error_message(error_code));
				// $B$J$+$C$?$3$H$K(B...
				goto NO_MATCH;
			}

			// eb_seek_text() $B$G(B auto_stop_code$B$,%/%j%"$5$l$k$?$a!"(B
			// $B0JA0$NCM$rI|85$9$k!#(B
			if(stop_code != -1)
				binfo->book->text_context.auto_stop_code = stop_code;

//			error_code = eb_backward_text(binfo->book, binfo->appendix);
			error_code = ebook_my_backward_text(binfo);
			if(error_code != EB_SUCCESS){
				fprintf(stderr, "failed to back text : %s\n",
					eb_error_message(error_code));
				// $B$J$+$C$?$3$H$K(B...
				goto NO_MATCH;
			}

				
			error_code = eb_tell_text(binfo->book, &text_position);
			if(error_code != EB_SUCCESS){
				fprintf(stderr, "failed to tell text : %s\n",
					eb_error_message(error_code));
				// $B$J$+$C$?$3$H$K(B...
				goto NO_MATCH;
			}

//			g_print("rewind to (0x%x, 0x%x)\n", text_position.page, text_position.offset);

			if(check_duplicate_hit(text_position) == TRUE){
				// $B$J$+$C$?$3$H$K(B...
//				g_print("duplicate\n");
				goto NO_MATCH;
			}


			error_code = eb_read_text(binfo->book, binfo->appendix, &heading_hookset, 
						  NULL, MAXLEN_TEXT, heading, &length);
			if (error_code != EB_SUCCESS) {
				fprintf(stderr, "failed to read text : %s\n",
					eb_error_message(error_code));
				// $B$J$+$C$?$3$H$K(B...
				goto NO_MATCH;
			}
			heading[length] = '\0';

			p = strchr(heading, '\n');
			if(p != NULL)
				*p = '\0';

			rp = (SEARCH_RESULT *)calloc(sizeof(SEARCH_RESULT),1);
			if(rp == NULL){
				fprintf(stderr, "No memory\n");
				exit(1);
			}

			rp->heading = strdup(heading);
			rp->book_info = binfo;
			rp->search_method = method;
		
			rp->pos_heading = text_position;
			rp->pos_text = text_position;
			
			search_result = g_list_append(search_result, rp);


			if((binfo->book->text_context.auto_stop_code != stop_code) &&
			   (binfo->book->text_context.auto_stop_code != -1))
				stop_code = binfo->book->text_context.auto_stop_code;


			hit_count ++;


		NO_MATCH:
			word_p = jisword;
			continue;
		}
	}

	free(jisword);

	return(error_code);
}

static gint ebook_full_heading_search(binfo, word){
return 0 ;
}

gint ebook_search2(char *g_word, DICT_GROUP *group){

	int j;
	int method;
	EB_Error_Code error_code=EB_SUCCESS;
	BOOK_INFO *binfo;

	DICT_MEMBER *member;
	GList *member_item=NULL;

	gchar *word  = strdup(g_word);

	clear_search_result();

	method = ebook_search_method();

	member_item = group->member;

	while(member_item != NULL){
		member = (DICT_MEMBER *)(member_item->data);
		if(member->active != TRUE) {
			member_item = g_list_next(member_item);
			continue;
		}
		if(member->binfo == NULL) {
			member_item = g_list_next(member_item);
			continue;
		}
		if(member->binfo->available == FALSE) {
			member_item = g_list_next(member_item);
			continue;
		}
		binfo = member->binfo;
		if(method == SEARCH_METHOD_AUTOMATIC){
			for(j=SEARCH_METHOD_MIN ; j<=SEARCH_METHOD_MAX ; j++){

				//$BA0J}0lCW!"8eJ}0lCW$O$N$>$/(B
				if((j == SEARCH_METHOD_WORD) ||
				   (j == SEARCH_METHOD_ENDWORD))
					continue;

				if(binfo->search_method[j] == TRUE){
					if(bending_correction == 0){
						error_code = ebook_simple_search(binfo, word, j);
					} else {
						error_code = ebook_ending_search(binfo, word, j);
					}
					if (error_code != EB_SUCCESS){
						if(error_code == EB_ERR_TOO_MANY_WORDS)
							continue;
						fprintf(stderr, "failed to search : %s\n",
							eb_error_message(error_code));
//						return(error_code);
					}
				}
			}
		} else if((method == SEARCH_METHOD_FULL_TEXT) || 
			  (method == SEARCH_METHOD_FULL_HEADING)){
			error_code = ebook_full_search(binfo, word, method);
		} else {
			if(binfo->search_method[method] == TRUE){
				if(bending_correction == 0){
					ebook_simple_search(binfo, word, method);
				} else {
					ebook_ending_search(binfo, word, method);				}
			}
		}

		member_item = g_list_next(member_item);
	}

	return 0;
}

static void *ebook_search_thread(void *arg)
{
	GList *group_item;
	DICT_GROUP *group;
	char word[MAX_BUFSIZE];
	
	strncpy(word, arg, sizeof(word)-1);

	group_item = g_list_first(group_list);
	while(group_item != NULL){
		group = (DICT_GROUP *)(group_item->data);
		if(group->active == TRUE){
			break;
		}
		group_item = g_list_next(group_item);
	}

	if(group_item == NULL) {
		return(0);
	}

	ebook_search2(word, group);
	thread_running = 0;
	return NULL ;
}


static void cancel_thread(GtkWidget *widget, gpointer *data)
{
	gtk_timeout_remove(tag_timeout);

	gtk_grab_remove(cancel_dialog);
	gtk_widget_destroy(cancel_dialog);

	pthread_cancel(tid);
	thread_running = 0;

	show_result_tree();

}

static gint show_cancel_dialog()
{
	GtkWidget *button;
	GtkWidget *label;
	GtkAdjustment *adj;
/*	int i; */

	
	cancel_dialog = gtk_dialog_new();
	gtk_widget_set_usize(cancel_dialog, 200, -1);
	gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG(cancel_dialog)->vbox), 10);
	
	button = gtk_button_new_with_label(_("Cancel"));
	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (cancel_dialog)->action_area), button,
				    TRUE, TRUE, 0);
	gtk_signal_connect (GTK_OBJECT (button), "clicked",
			    GTK_SIGNAL_FUNC (cancel_thread), (gpointer)NULL);
	
	label = gtk_label_new (_("Searching"));
	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (cancel_dialog)->vbox), label, TRUE,TRUE, 5);

//	progress = gtk_progress_bar_new();
	adj = (GtkAdjustment *) gtk_adjustment_new (0, 0, 1, 0, 0, 0);
	progress = gtk_progress_bar_new_with_adjustment (adj);
/*
	gtk_progress_set_activity_mode(GTK_PROGRESS(progress), TRUE);
	gtk_progress_bar_set_activity_step(GTK_PROGRESS_BAR(progress), 5);
	gtk_progress_bar_set_activity_blocks(GTK_PROGRESS_BAR(progress), 5);
*/

	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (cancel_dialog)->vbox), progress, TRUE,TRUE, 5);

	label_match = gtk_label_new ("0 hit");
	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (cancel_dialog)->vbox), label_match, TRUE,TRUE, 5);

//	gtk_window_set_position(GTK_WINDOW(cancel_dialog), GTK_WIN_POS_CENTER_ALWAYS);
	gtk_widget_show_all(cancel_dialog); 
	center_dialog(window, cancel_dialog);
	gtk_grab_add(cancel_dialog);
	return 0 ;
}

static gint watch_thread(gpointer data){

	char msg[256];
/*	gchar *label; */
	GtkAdjustment *adj;
	gfloat new_val;

	if(thread_running){
		sprintf(msg, _("%d hit"), hit_count);
		gtk_label_set_text(GTK_LABEL(label_match), msg);


		adj = GTK_PROGRESS (progress)->adjustment;
		new_val = adj->value + 1;
		if (new_val > adj->upper)
			new_val = adj->lower;
//		gtk_progress_set_value (GTK_PROGRESS (progress), new_val);
		gtk_progress_set_value (GTK_PROGRESS (progress), search_progress);

		return(1);
	}
	gtk_timeout_remove(tag_timeout);

	show_result_tree();
	gtk_grab_remove(cancel_dialog);
	gtk_widget_destroy(cancel_dialog);

	return(0);
}

gint ebook_search(char *word)
{
	gint rc;
	gint method;
	void *p ;
	size_t size ;
	pthread_attr_t thread_attr ;

	thread_running = 1;
	hit_count = 0;

	pthread_attr_init (&thread_attr) ;
	pthread_attr_setstacksize (&thread_attr, 512*1024) ;
	
	rc = pthread_create(&tid, &thread_attr, ebook_search_thread, (void *)word);
	if(rc != 0){
		perror("pthread_create");
		exit(1);
	}

	pthread_attr_destroy(&thread_attr);

	method = ebook_search_method();
	if(method == SEARCH_METHOD_FULL_TEXT){
		show_cancel_dialog();
		tag_timeout = gtk_timeout_add(100, watch_thread, NULL);
	} else {
		pthread_join(tid, &p);
		thread_running = 0;
		show_result_tree();
	}
	return 0 ;
}

gint ebook_search_auto(char *g_word)
{
	GList *group_item;
	DICT_GROUP *group;
	gint method;

	method = ebook_search_method();
	if(method == SEARCH_METHOD_FULL_TEXT)
		return(0);

	group_item = g_list_first(group_list);
	while(group_item != NULL){
		group = (DICT_GROUP *)(group_item->data);
		if(strcasecmp(group->name, "selection") == 0){
			break;
		}
		group_item = g_list_next(group_item);
	}

	if(group_item == NULL) {
		group_item = g_list_first(group_list);
		while(group_item != NULL){
			group = (DICT_GROUP *)(group_item->data);
			if(group->active == TRUE){
				break;
			}
			group_item = g_list_next(group_item);
		}
	}

	if(group_item == NULL)
		return(0);

	return ebook_search2(g_word, group);
}


gint ebook_simple_search(BOOK_INFO *binfo, char *word, gint method)
{
	EB_Error_Code error_code=EB_SUCCESS;
	int i, len, total_hits=0;
	EB_Hit hits[MAX_HITS];
	int hitcount;
	char heading[MAXLEN_HEADING + 1];
	char *keywords[EBOOK_MAX_KEYWORDS + 1];
	SEARCH_RESULT *rp;

	if((error_code = ebook_set_subbook(binfo)) != EB_SUCCESS)
		return(error_code);

	switch(method){
	case SEARCH_METHOD_WORD:
		error_code = eb_search_word(binfo->book, word);		
		break;
	case SEARCH_METHOD_ENDWORD:
		error_code = eb_search_endword(binfo->book, word);		
		break;
	case SEARCH_METHOD_EXACTWORD:
		error_code = eb_search_exactword(binfo->book, word);		
		break;
	case SEARCH_METHOD_KEYWORD:
		split_word(word, keywords);
		error_code = eb_search_keyword(binfo->book,
					       (const char * const *)keywords);
		free_words(keywords);
		break;
	case SEARCH_METHOD_MULTI:
		split_word(word, keywords);

		for(i=0;i<EBOOK_MAX_KEYWORDS;i++){
			if(keywords[i] == NULL)
				break;
		}
		
		error_code = eb_search_multi(binfo->book,
					       global_multi_code,
					       (const char * const *)keywords);
		free_words(keywords);
		break;
	default:
		error_code = EB_ERR_NO_SUCH_SEARCH;
		break;
	}
	if (error_code != EB_SUCCESS){
		fprintf(stderr, "failed to search : %s\n",
			eb_error_message(error_code));
		return(error_code);
	}

	total_hits = count_result(search_result);

	while(1){
		error_code = eb_hit_list(binfo->book, 
					 MAX_HITS, 
					 hits, 
					 &hitcount);
		if(error_code != EB_SUCCESS){
/*
			fprintf(stderr, "an error occurred. : %s\n",
				eb_error_message(error_code));
*/
			return(error_code);
		}
		if(hitcount == 0)
			break;

		for(i = 0 ; i < hitcount ; i ++){
			if(total_hits >= max_search)
				return(EB_SUCCESS);

			if(check_duplicate_hit(hits[i].text) == TRUE)
				continue;

			rp = (SEARCH_RESULT *)calloc(sizeof(SEARCH_RESULT),1);
			if(rp == NULL){
				fprintf(stderr, "No memory\n");
				exit(1);
			}

			error_code = eb_seek_text(binfo->book, 
						  &(hits[i].heading));

			if(error_code != EB_SUCCESS){
/*
				fprintf(stderr, "failed to seek the subbook, %s\n",
					eb_error_message(error_code));
*/
				return(error_code);
			}

			error_code = eb_read_heading(binfo->book, 
						     binfo->appendix, 
						     &heading_hookset, 
						     NULL, 
						     MAXLEN_HEADING, 
						     heading, 
						     &len);
			if (error_code != EB_SUCCESS) {
/*
				fprintf(stderr, "failed to read the subbook, %s\n",
					eb_error_message(error_code));
*/
				return(error_code);
			}
			heading[len] = '\0';
			rp->heading = strdup(heading);
			rp->book_info = binfo;
			rp->search_method = method;

			rp->pos_heading = hits[i].heading;
			rp->pos_text = hits[i].text;

			search_result = g_list_append(search_result, rp);

			total_hits ++;

		}
	}

	return(error_code);
}

static gint ebook_ending_search(BOOK_INFO *binfo, char *word, gint method)
{
	EB_Error_Code error_code=EB_SUCCESS;
	gint i;
	gint len_word, len_ending;
	char *keywords[EBOOK_MAX_KEYWORDS + 1];
	char new_word[MAX_BUFSIZE];
	char new_key[256];
	char *save_key;
	ENDING *ending;
	GList *list;


	g_assert(binfo != NULL);
	g_assert(word != NULL);

	error_code = ebook_simple_search(binfo, word, method);
	if((bending_only_nohit == 1) && (search_result != NULL)){
		return(error_code);
	}

	split_word(word, keywords);

	for(i=0; keywords[i] != NULL ; i++){
		list = g_list_first(ending_list);
		while(list){
			ending = (ENDING *)(list->data);
			len_word = strlen(keywords[i]);
			len_ending = strlen(ending->pattern);
			if(len_word < len_ending){
				list = g_list_next(list);
				continue;
			}
			if(strcmp(&keywords[i][len_word - len_ending],ending->pattern) == 0){
				memcpy(new_key, keywords[i], len_word - len_ending);
				sprintf(&new_key[len_word - len_ending],"%s", ending->normal);
				save_key = keywords[i];
				keywords[i] = new_key;
				cat_word(new_word, keywords);
				error_code = ebook_simple_search(binfo, new_word, method);				
				keywords[i] = save_key;
				if(error_code != EB_SUCCESS){
					return(error_code);
				}
			}
			list = g_list_next(list);
		}
	}

	// $BF|K\8l$N8lHxJd@5(B
	// $BF|K\8l$OJ#?t$NC18l$K$O$J$C$F$$$J$$$O$:(B
	if((keywords[1] == NULL) &&
	   (iseuc(keywords[0]))){

		if((count_result(search_result) != 0) &&
		   (bending_only_nohit == 1))
			goto END;

		list = g_list_first(ending_list_ja);
		while(list){
			ending = (ENDING *)(list->data);
			len_word = strlen(keywords[0]);
			len_ending = strlen(ending->pattern);
			if(len_word < len_ending){
				list = g_list_next(list);
				continue;
			}
			for(i=0; i<len_word; i+=2){
				if(strncmp(&keywords[0][i], ending->pattern, len_ending) == 0){
					memcpy(new_key, keywords[0], i);
					sprintf(&new_key[i],"%s", ending->normal);
					error_code = ebook_simple_search(binfo, new_key, method);				
					if(error_code != EB_SUCCESS){
						return(error_code);
					}
				}
			}
			list = g_list_next(list);
		}

		// $B$=$l$G$b0l7o$b%^%C%A$7$J$+$C$?$i!"4A;z$NItJ,$@$1$G8!:w$9$k!#(B
		if((count_result(search_result) == 0) && 
		   (iseuckanji(keywords[0]))){
			len_word = strlen(keywords[0]);
			strcpy(new_key, keywords[0]);
			for(i=0; i<len_word; i+=2){
				if(!iseuckanji(&new_key[i])) {
					new_key[i] = '\0';
					break;
				}
			}

			error_code = ebook_simple_search(binfo, new_key, method);				
			if(error_code != EB_SUCCESS){
				return(error_code);
			}
		}
	}

 END:
	free_words(keywords);

	return(error_code);
}




void clear_search_result()
{
	SEARCH_RESULT *rp;
	GList *l;

	if (!search_result)
	    return ;
	
	l = g_list_first(search_result);
	while(l != NULL){
		rp = (SEARCH_RESULT *)(l->data);
		if(rp->heading != NULL)
			free(rp->heading);
		l = g_list_next(l);
	}
	search_result = NULL;
}


gchar *ebook_get_heading(BOOK_INFO *binfo, int page, int offset)
{
	EB_Error_Code error_code;
	int len;
	char heading[MAXLEN_HEADING + 1];
	EB_Position position;
	gchar *p;

	if((error_code = ebook_set_subbook(binfo)) != EB_SUCCESS)
		return(NULL);

	error_code = eb_seek_text(binfo->book, &position);
	if(error_code != EB_SUCCESS){
		fprintf(stderr, "Failed to seek text : %s\n",
		       eb_error_message(error_code));
		exit(1);
	}

	error_code = eb_read_heading(binfo->book, binfo->appendix, &text_hookset, NULL, MAXLEN_HEADING, heading, &len);
	if (error_code != EB_SUCCESS) {
		fprintf(stderr, "Failed to read heading : %s\n",
		       eb_error_message(error_code));
		exit(1);
	}
	heading[len] = '\0';

	p = malloc(len+1);
	memcpy(p, heading, len+1);
	return(p);
}

gchar *ebook_get_text(BOOK_INFO *binfo, int page, int offset)
{
	EB_Error_Code error_code;
	int len;
	char text[MAXLEN_TEXT + 1];
	EB_Position position;
	gchar *p;


	if((error_code = ebook_set_subbook(binfo)) != EB_SUCCESS)
		return(NULL);


	position.page = page;
	position.offset = offset;

	error_code = eb_seek_text(binfo->book, &position);
	if(error_code != EB_SUCCESS){
		fprintf(stderr, "failed to seek text : %s\n",
		       eb_error_message(error_code));
		return(NULL);
	}


	error_code = eb_read_text(binfo->book, binfo->appendix, &text_hookset, 
				  NULL, MAXLEN_TEXT, text, &len);
	if (error_code != EB_SUCCESS) {
		fprintf(stderr, "failed to read text : %s\n",
		       eb_error_message(error_code));
		return(NULL);
	}
	text[len] = '\0';

	p = malloc(len+1);
	memcpy(p, text, len+1);
	return(p);
}

gchar *ebook_get_candidate(BOOK_INFO *binfo, int page, int offset)
{
	EB_Error_Code error_code;
	int len;
	char text[MAXLEN_TEXT + 1];
	EB_Position position;
	gchar *p;


	if((error_code = ebook_set_subbook(binfo)) != EB_SUCCESS)
		return(NULL);


	position.page = page;
	position.offset = offset;

	error_code = eb_seek_text(binfo->book, &position);
	if(error_code != EB_SUCCESS){
		fprintf(stderr, "failed to seek text : %s\n",
		       eb_error_message(error_code));
		return(NULL);
	}

	error_code = eb_read_text(binfo->book, binfo->appendix, &candidate_hookset, 
				  NULL, MAXLEN_TEXT, text, &len);
	if (error_code != EB_SUCCESS) {
		fprintf(stderr, "failed to read text : %s\n",
		       eb_error_message(error_code));
		return(NULL);
	}
	text[len] = '\0';

	p = malloc(len+1);
	memcpy(p, text, len+1);
	return(p);
}

EB_Error_Code ebook_forward_text(BOOK_INFO *binfo)
{
	EB_Error_Code error_code;

	if((error_code = ebook_set_subbook(binfo)) != EB_SUCCESS)
		return(error_code);

	error_code = eb_seek_text(binfo->book, &current_position);
	if(error_code != EB_SUCCESS){
		fprintf(stderr, "failed to seek text : %s\n",
		       eb_error_message(error_code));
		return(error_code);
	}

	error_code = eb_forward_text(binfo->book, binfo->appendix);
	if(error_code != EB_SUCCESS){
		fprintf(stderr, "failed to forward text : %s\n",
		       eb_error_message(error_code));
		return(error_code);
	}

	return(EB_SUCCESS);
}

EB_Error_Code ebook_backward_text(BOOK_INFO *binfo)
{
	EB_Error_Code error_code;
	int stop_code = -1;

	if((error_code = ebook_set_subbook(binfo)) != EB_SUCCESS)
		return(error_code);

	stop_code = binfo->book->text_context.auto_stop_code;

	error_code = eb_seek_text(binfo->book, &current_position);
	if(error_code != EB_SUCCESS){
		fprintf(stderr, "failed to seek text : %s\n",
		       eb_error_message(error_code));
		return(error_code);
	}

	if(stop_code != -1)
		binfo->book->text_context.auto_stop_code = stop_code;

//	error_code = eb_backward_text(binfo->book, binfo->appendix);
	error_code = ebook_my_backward_text(binfo);
	if(error_code != EB_SUCCESS){
		fprintf(stderr, "failed to back text : %s\n",
		       eb_error_message(error_code));
		return(error_code);
	}

	return(EB_SUCCESS);
}

void ebook_tell_text(BOOK_INFO *binfo, gint *page, gint *offset)
{
	EB_Error_Code error_code;
	EB_Position position;

//	if((error_code = ebook_set_subbook(binfo)) != EB_SUCCESS)
//		return;


	error_code = eb_tell_text(binfo->book, &position);
	if(error_code != EB_SUCCESS){
		fprintf(stderr, "failed to tell text : %s\n",
		       eb_error_message(error_code));
		exit(1);
	}

	*page = position.page;
	*offset = position.offset;

	return;
}

void ebook_menu(BOOK_INFO *binfo, EB_Position *pos)
{
	EB_Error_Code error_code=EB_SUCCESS;

	error_code = eb_menu(binfo->book, pos);
	if(error_code != EB_SUCCESS) {
		fprintf(stderr, "failed to get menu position : %s\n",
		       eb_error_message(error_code));
	}
}

void ebook_copyright(BOOK_INFO *binfo, EB_Position *pos)
{
	EB_Error_Code error_code=EB_SUCCESS;

	error_code = eb_copyright(binfo->book, pos);
	if(error_code != EB_SUCCESS) {
		fprintf(stderr, "failed to get copyright position : %s\n",
		       eb_error_message(error_code));
	}
}

static void ebook_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;

    for (i = 0; i < bitmap_size; i++) {
	hex = 0;
        if (*bitmap_p & 0x80)
            hex |= 0x01;
        if (*bitmap_p & 0x40)
            hex |= 0x02;
        if (*bitmap_p & 0x20)
            hex |= 0x04;
        if (*bitmap_p & 0x10)
            hex |= 0x08;
        if (*bitmap_p & 0x08)
            hex |= 0x10;
        if (*bitmap_p & 0x04)
            hex |= 0x20;
        if (*bitmap_p & 0x02)
            hex |= 0x40;
        if (*bitmap_p & 0x01)
            hex |= 0x80;
	bitmap_p++;

	*xbm_p = hex;
	xbm_p ++;
    }
    
    *xbm_length = (xbm_p - xbm);
}

gint check_gaiji_size(BOOK_INFO *binfo, gint prefered_size){
	gint size;

	size = prefered_size;

	switch(size){
	case 48:
		if (eb_have_font(binfo->book, EB_FONT_48)){
			size = 48;
			break;
		}
		size = 30;
	case 30:
		if (eb_have_font(binfo->book, EB_FONT_30)){
			size = 30;
			break;
		}
		size = 24;
	case 24:
		if (eb_have_font(binfo->book, EB_FONT_24)){
			size = 24;
			break;
		}
		size = 16;
	case 16:
		if (eb_have_font(binfo->book, EB_FONT_16)){
			size = 16;
			break;
		}
		fprintf(stderr, "failed to find 16 dot gaiji : subbook=%s\n",
			binfo->subbook_title);
		return(-1);

	}

	if(size != prefered_size){
		fprintf(stderr, "Cannot find %d dot gaiji. Use %d dot instead\n",
			prefered_size, size);
	}

	return(size);
	
}

void set_gaiji_size(gint font_size){
	BOOK_INFO *binfo;
	GList *book_item;

	book_item = g_list_first(book_list);
	while(book_item != NULL){
		binfo = (BOOK_INFO *)(book_item->data);
		book_item = g_list_next(book_item);
	}


}

guchar *read_gaiji_as_bitmap(BOOK_INFO *binfo, gchar *name, gint size, gint *width, gint *height)
{
	EB_Error_Code error_code;
	gint char_no;
	gchar bitmap_data[EB_SIZE_WIDE_FONT_48];
//	char image_data[EB_SIZE_FONT_IMAGE];
	guchar *image_data;
	size_t image_size;
	int image_width;
	int image_height;
	EB_Subbook *subbook;


//	*pixmap = NULL;
//	*bitmap = NULL;

	char_no = strtol(&name[1], NULL, 16);

	subbook = binfo->book->subbook_current;

	switch (size) {
	case 16:
		error_code = eb_set_font(binfo->book, EB_FONT_16);
		break;
	case 24:
		error_code = eb_set_font(binfo->book, EB_FONT_24);
		break;
	case 30:
		error_code = eb_set_font(binfo->book, EB_FONT_30);
		break;
	case 48:
		error_code = eb_set_font(binfo->book, EB_FONT_48);
		break;
	}

	if (error_code != EB_SUCCESS) {
		fprintf(stderr, "failed to set font : subbook=%s\n%s\n",
			binfo->subbook_title, 
			eb_error_message(error_code));
				return(NULL);
	}


	error_code = eb_font_height(binfo->book, &image_height);
	if (error_code != EB_SUCCESS) {
		fprintf(stderr, "failed to get font height : subbook=%s\n%s\n",
			binfo->subbook_title, 
			eb_error_message(error_code));
		return(NULL);
	}
	
	
	if(name[0] == 'h'){
		if (!eb_have_narrow_font(binfo->book)){
			fprintf(stderr, "%s does not have narrow font\n",
				binfo->subbook_title);
			return(NULL);
		}
		error_code = eb_narrow_font_width(binfo->book, &image_width);
		if (error_code != EB_SUCCESS) {
			fprintf(stderr, "failed to get font width : subbook=%s\n%s\n",
				binfo->subbook_title, 
				eb_error_message(error_code));
			return(NULL);
		}
		error_code = eb_narrow_font_character_bitmap(
			binfo->book,
			char_no,
			bitmap_data);
		if (error_code != EB_SUCCESS) {
			fprintf(stderr, "failed to read narrow font : subbook=%s, character=0x%04x\n%s\n",
				binfo->subbook_title, 
				char_no,
				eb_error_message(error_code));
			return(NULL);

		}
	} else {
		if (!eb_have_wide_font(binfo->book)){
			fprintf(stderr, "%s does not have wide font\n",
				binfo->subbook_title);
			return(NULL);
		}
		error_code = eb_wide_font_width(binfo->book, &image_width);
		if (error_code != EB_SUCCESS) {
			fprintf(stderr, "failed to get font width : subbook=%s\n%s\n",
				binfo->subbook_title, 
				eb_error_message(error_code));
			return(NULL);
		}
		error_code = eb_wide_font_character_bitmap(
			binfo->book,
			char_no,
			bitmap_data);
		if (error_code != EB_SUCCESS) {
			fprintf(stderr, "failed to read wide font : subbook=%s, character=0x%04x\n%s\n",
				binfo->subbook_title, 
				char_no,
				eb_error_message(error_code));
			return(NULL);
		}
	}

	image_data = malloc(image_width * image_height / 8);
	ebook_bitmap_to_xbm(bitmap_data, image_width,
			    image_height, image_data, &image_size);
	
	*width = image_width;
	*height = image_height;

	return(image_data);
	
}

#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
};


static void ebook_bitmap_to_gif(bitmap, width, height, gif, gif_length, fg, bg)
    const char *bitmap;
    int width;
    int height;
    char *gif;
    size_t *gif_length;
    uint fg;
    uint bg;
{
    unsigned char *gif_p = (unsigned char *)gif;
    const unsigned char *bitmap_p = (const unsigned char *)bitmap;
    int i, j;


    /*
     * 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] = (bg >> 16) & 0xff;
    gif_p[14] = (bg >> 8) & 0xff;
    gif_p[15] = bg & 0xff;
    gif_p[16] = (fg >> 16) & 0xff;
    gif_p[17] = (fg >> 8) & 0xff;
    gif_p[18] = fg & 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);

}


guchar *read_gaiji_as_xbm(BOOK_INFO *binfo, gchar *name, gchar *fname, guint fg, guint bg)
{
	EB_Error_Code error_code;
	gint char_no;
	gchar bitmap_data[EB_SIZE_WIDE_FONT_48];
	guchar image_data[EB_SIZE_FONT_IMAGE];
//	guchar *image_data;
	size_t image_size;
	int image_width;
	int image_height;
	EB_Subbook *subbook;
	FILE *fp;


	if((error_code = ebook_set_subbook(binfo)) != EB_SUCCESS)
		return(NULL);

	char_no = strtol(&name[1], NULL, 16);

	subbook = binfo->book->subbook_current;

	error_code = eb_font_height(binfo->book, &image_height);
	if (error_code != EB_SUCCESS) {
		fprintf(stderr, "failed to get font height : subbook=%s\n%s\n",
			binfo->subbook_title, 
			eb_error_message(error_code));
		return(NULL);
	}
	
	
	if(name[0] == 'h'){
		if (!eb_have_narrow_font(binfo->book)){
			fprintf(stderr, "%s does not have narrow font\n",
				binfo->subbook_title);
			return(NULL);
		}
		error_code = eb_narrow_font_width(binfo->book, &image_width);
		if (error_code != EB_SUCCESS) {
			fprintf(stderr, "failed to get font width : subbook=%s\n%s\n",
				binfo->subbook_title, 
				eb_error_message(error_code));
			return(NULL);
		}
		error_code = eb_narrow_font_character_bitmap(
			binfo->book,
			char_no,
			bitmap_data);
		if (error_code != EB_SUCCESS) {
			fprintf(stderr, "failed to read narrow font : subbook=%s, character=0x%04x\n%s\n",
				binfo->subbook_title, 
				char_no,
				eb_error_message(error_code));
			return(NULL);

		}
	} else {
		if (!eb_have_wide_font(binfo->book)){
			fprintf(stderr, "%s does not have wide font\n",
				binfo->subbook_title);
			return(NULL);
		}
		error_code = eb_wide_font_width(binfo->book, &image_width);
		if (error_code != EB_SUCCESS) {
			fprintf(stderr, "failed to get font width : subbook=%s\n%s\n",
				binfo->subbook_title, 
				eb_error_message(error_code));
			return(NULL);
		}
		error_code = eb_wide_font_character_bitmap(
			binfo->book,
			char_no,
			bitmap_data);
		if (error_code != EB_SUCCESS) {
			fprintf(stderr, "failed to read wide font : subbook=%s, character=0x%04x\n%s\n",
				binfo->subbook_title, 
				char_no,
				eb_error_message(error_code));
			return(NULL);
		}
	}


	ebook_bitmap_to_gif(bitmap_data, image_width,
			    image_height, image_data, &image_size, fg, bg);


	fp = fopen(fname, "w");
	if(fp == NULL){
		fprintf(stderr, "file open failed : %s\n", fname);
	}
	fwrite(image_data, image_size, 1, fp);	
	fclose(fp);

	return(NULL);
	
}

EB_Error_Code ebook_output_wave(BOOK_INFO *binfo, gchar *filename, gint page, gint offset, gint size)
//EB_Error_Code ebook_output_wave(BOOK_INFO *binfo, gchar *filename, gint start_page, gint start_offset, gint end_page, gint end_offset)
{
	EB_Position pos;
	char binary_data[EB_SIZE_PAGE];
	EB_Error_Code error_code;
	EB_Position end_position;
	ssize_t read_length;
	FILE *fp;

	if((error_code = ebook_set_subbook(binfo)) != EB_SUCCESS)
		return(error_code);

	fp = fopen(filename, "w");
	if(fp == NULL){
		fprintf(stderr, "failed to open file : %s\n",
			filename);
		return(EB_ERR_BAD_FILE_NAME);
	}
	pos.page = page;
	pos.offset = offset;

	end_position.page = pos.page
		+ (size / EB_SIZE_PAGE);
	end_position.offset = pos.offset
		+ (size % EB_SIZE_PAGE);
	if (EB_SIZE_PAGE <= end_position.offset) {
		end_position.offset -= EB_SIZE_PAGE;
		end_position.page++;
	}

	/*
	 * Read sound data.
	 */
	error_code = eb_set_binary_wave(binfo->book, 
					&pos, &end_position);
	if (error_code != EB_SUCCESS){
		fprintf(stderr, "failed to set binary wave : %s\n",
				eb_error_message(error_code));
		return(error_code);
	}

	for (;;) {
		error_code = eb_read_binary(binfo->book, 
					    EB_SIZE_PAGE,
					    binary_data, &read_length);
		if (error_code != EB_SUCCESS || read_length == 0){
			fclose(fp);
			return(error_code);
		}

		// fmt$B%A%c%s%/$NA0$KITMW$J%G!<%?(B(32$B%P%$%H(B)$B$,$"$C$?$i:o=|$9$k(B
		if((strncmp("fmt ", &binary_data[44], 4) == 0) &&
		   (strncmp("fmt ", &binary_data[12], 4) != 0)){
			fprintf(stderr, "Warning: extra header found in WAVE data.\n");
			fwrite(binary_data, 12, 1, fp);
			fwrite(&binary_data[44], read_length - 44, 1, fp);
		} else {
			fwrite(binary_data, read_length, 1, fp);
		}
	}

	/* not reached */
	return(EB_SUCCESS);
}

EB_Error_Code ebook_output_mpeg(BOOK_INFO *binfo, gchar *srcname, gchar *destname)
{
	char binary_data[EB_SIZE_PAGE];
	guint argv[4];
	EB_Error_Code error_code;
	ssize_t read_length;
	FILE *fp;

	if((error_code = ebook_set_subbook(binfo)) != EB_SUCCESS)
		return(error_code);

	if((error_code = eb_decompose_movie_file_name(argv, srcname)) != EB_SUCCESS)
		return(error_code);

	fp = fopen(destname, "w");
	if(fp == NULL){
		fprintf(stderr, "failed to open file : %s\n",
			destname);
		return(EB_ERR_BAD_FILE_NAME);
	}

	/*
	 * Read sound data.
	 */
	error_code = eb_set_binary_mpeg(binfo->book, argv);
	if (error_code != EB_SUCCESS){
		fprintf(stderr, "failed to set binary mpeg : %s\n",
				eb_error_message(error_code));
		return(error_code);
	}

	for (;;) {
		error_code = eb_read_binary(binfo->book, 
					    EB_SIZE_PAGE,
					    binary_data, &read_length);
		if (error_code != EB_SUCCESS || read_length == 0){
			fclose(fp);
			return(error_code);
		}
		fwrite(binary_data, read_length, 1, fp);
	}

	/* not reached */
	return(EB_SUCCESS);
}

EB_Error_Code ebook_output_color(BOOK_INFO *binfo, gchar *filename, gint page, gint offset)
{
	EB_Position pos;
	char binary_data[EB_SIZE_PAGE];
	EB_Error_Code error_code;
	ssize_t read_length;
	FILE *fp;
	
	fp = fopen(filename, "w");
	if(fp == NULL){
		fprintf(stderr, "failed to open file : %s\n",
			filename);
		return(EB_ERR_BAD_FILE_NAME);
	}

	pos.page = page;
	pos.offset = offset;

	if((error_code = ebook_set_subbook(binfo)) != EB_SUCCESS)
		return(error_code);

	error_code = eb_set_binary_color_graphic(binfo->book, 
						 &pos);

	// $B%9!<%Q!<E}9g<-=q(B2000$B$N4A;z8;$N>l9g$K$O?^HG%G!<%?$,(B
	// Honmon2$B$KF~$C$F$$$k$h$&$J$N$G!"(BNO_SUCH_BINARY$B$H$J$k!#(B
	// $B$H$j$"$($:$O<+NO$GFI$`$3$H$K$9$k!#(B
	if (error_code == EB_ERR_NO_SUCH_BINARY){
		gchar *data;
		gint file_size;

		eb_seek_text(binfo->book, &pos);
		error_code = eb_read_rawtext(binfo->book, 
					     8, 
					     binary_data,
					     &read_length);
		if (error_code != EB_SUCCESS || read_length == 0){
			return(error_code);
		}
		if(strncmp(binary_data, "data", 4) == 0){
			file_size = *(gint *)&binary_data[4];
			data = malloc(file_size);
			error_code = eb_read_rawtext(binfo->book, 
						     file_size,
						     data,
						     &read_length);
			if (error_code != EB_SUCCESS || read_length == 0){
				return(error_code);
			}
			fwrite(data, read_length, 1, fp);
			fclose(fp);
			free(data);
		}

		return(EB_SUCCESS);
	}


	if (error_code != EB_SUCCESS){
		fprintf(stderr, "failed to set binary color graphic : %s\n",
			eb_error_message(error_code));
		return(error_code);
	}
	
	for (;;) {
		error_code = eb_read_binary(binfo->book, EB_SIZE_PAGE,
					    binary_data, &read_length);
		if (error_code != EB_SUCCESS || read_length == 0){
			fclose(fp);
			return(error_code);
		}
//		output_data(property, binary_data, read_length);
		fwrite(binary_data, read_length, 1, fp);
	}

	/* not reached */
	return(EB_SUCCESS);
}


EB_Error_Code ebook_output_gray(BOOK_INFO *binfo, gchar *filename, gint page, gint offset, gint width, gint height)
{
	EB_Position pos;
	char binary_data[EB_SIZE_PAGE];
	EB_Error_Code error_code;
	ssize_t read_length;
	FILE *fp;
	
	fp = fopen(filename, "w");
	if(fp == NULL){
		fprintf(stderr, "failed to open file : %s\n",
			filename);
		return(EB_ERR_BAD_FILE_NAME);
	}

	pos.page = page;
	pos.offset = offset;

	if((error_code = ebook_set_subbook(binfo)) != EB_SUCCESS)
		return(error_code);

	error_code = eb_set_binary_gray_graphic(binfo->book, 
						 &pos, width, height);
	if (error_code != EB_SUCCESS){
		fprintf(stderr, "failed to set binary gray graphic : %s\n",
			eb_error_message(error_code));
		return(error_code);
	}
	
	for (;;) {
		error_code = eb_read_binary(binfo->book, EB_SIZE_PAGE,
					    binary_data, &read_length);
		if (error_code != EB_SUCCESS || read_length == 0){
			fclose(fp);
			return(error_code);
		}
//		output_data(property, binary_data, read_length);
		fwrite(binary_data, read_length, 1, fp);
	}

	/* not reached */
	return(EB_SUCCESS);
}

extern CONTENT_AREA *dict_area;

EB_Error_Code ebook_output_mono(BOOK_INFO *binfo, gchar *filename, gint page, gint offset, gint width, gint height)
{
	FILE *fp;
	EB_Error_Code error_code;
	EB_Position pos;
	char *binary_data;
	ssize_t read_length;
	gint data_size;
	gchar *bmp_data;
	gint bmp_length;

#ifdef COLOR_HACK
	
	guchar fg[4];
	guchar bg[4];
	GdkColor color;

//	color = window->style->fg[GTK_STATE_NORMAL];
	color = dict_area->area->style->fg[GTK_STATE_NORMAL];
	fg[0] = (guchar)color.red;
	fg[1] = (guchar)color.green;
	fg[2] = (guchar)color.blue;
	fg[3] = 0x0;

//	fg = ((guchar)color.red << 16) | ((guchar)color.green << 8) | ((guchar)color.blue) ;

//	color = window->style->bg[GTK_STATE_NORMAL];
	color = dict_area->area->style->bg[GTK_STATE_NORMAL];
	bg[0] = (guchar)color.red;
	bg[1] = (guchar)color.green;
	bg[2] = (guchar)color.blue;
	bg[3] = 0x0;

//	bg = ((guchar)color.red << 16) | ((guchar)color.green << 8) | ((guchar)color.blue) ;

#endif

	if((error_code = ebook_set_subbook(binfo)) != EB_SUCCESS)
		return(error_code);

	fp = fopen(filename, "w");
	if(fp == NULL){
		fprintf(stderr, "failed to open file : %s\n",
			filename);
		return(EB_ERR_BAD_FILE_NAME);
	}

	pos.page = page;
	pos.offset = offset;

	eb_seek_text(binfo->book, &pos);

	error_code = eb_set_binary_mono_graphic(binfo->book, 
					&pos, width, height);
//					&pos, 0, 0);

	// $B%9!<%Q!<E}9g<-=q(B2000$B$N4A;z8;$N>l9g$K$O?^HG%G!<%?$,(B
	// Honmon2$B$KF~$C$F$$$k$h$&$J$N$G!"(BNO_SUCH_BINARY$B$H$J$k!#(B
	// $B$H$j$"$($:$O<+NO$GFI$`$3$H$K$9$k!#(B
	// eb-3.3$B$G$O=$@5:Q$_(B
//	if ((error_code == EB_ERR_NO_SUCH_BINARY) || 
//	    (error_code == EB_ERR_FAIL_READ_BINARY)){
	if (error_code != EB_SUCCESS){

		if((width % 8) != 0)
			width = (width / 8)*8 + 8;

		data_size = width * height / 8;

		binary_data = malloc(data_size);
		bmp_data = malloc(data_size*10);


		pos.page = page;
		pos.offset = offset;

		eb_seek_text(binfo->book, &pos);
		error_code = eb_read_rawtext(binfo->book, 
					     data_size, 
					     binary_data,
					     &read_length);
		if (error_code != EB_SUCCESS || read_length == 0){
			return(error_code);
		}

//		 eb_bitmap_to_xbm(binary_data,
		 eb_bitmap_to_bmp(binary_data,
				  width,
				  height,
				  bmp_data,
				  &bmp_length);

		fwrite(bmp_data, bmp_length, 1, fp);

#ifdef COLOR_HACK
		fseek(fp, 54, SEEK_SET);
		fwrite(bg, 4, 1, fp);
		fseek(fp, 58, SEEK_SET);
		fwrite(fg, 4, 1, fp);
#endif
		fclose(fp);
		free(binary_data);
		free(bmp_data);
		return(EB_SUCCESS);
	}


	if (error_code != EB_SUCCESS){
		fprintf(stderr, "failed to set binary mono : %s\n",
				eb_error_message(error_code));
		return(error_code);
	}

	for (;;) {
		char binary_data[EB_SIZE_PAGE];

		error_code = eb_read_binary(binfo->book, 
					    EB_SIZE_PAGE,
					    binary_data, &read_length);
		if (error_code != EB_SUCCESS || read_length == 0){
			fclose(fp);
			return(error_code);
		}
#ifdef COLOR_HACK
		memcpy(&binary_data[54], bg, 4);
		memcpy(&binary_data[58], fg, 4);
#endif
		fwrite(binary_data, read_length, 1, fp);
	}

	/* not reached */
	fclose(fp);
	return(EB_SUCCESS);
}

gchar *ebook_get_rawtext(BOOK_INFO *binfo, gint page, gint offset)
{
	EB_Position pos;
	char *binary_data;
	EB_Error_Code error_code;
	ssize_t read_length;

	binary_data = malloc(EB_SIZE_PAGE);
	
	if((error_code = ebook_set_subbook(binfo)) != EB_SUCCESS)
		return(NULL);

	pos.page = page;
	pos.offset = offset;

	eb_seek_text(binfo->book, &pos);
	error_code = eb_read_rawtext(binfo->book, 
				     EB_SIZE_PAGE, 
				     binary_data, 
				     &read_length);
	if (error_code != EB_SUCCESS || read_length == 0){
		return(NULL);
	}

	return(binary_data);
}

EB_Error_Code ebook_set_subbook(BOOK_INFO *binfo)
{

	EB_Error_Code error_code;

	error_code = eb_set_subbook(binfo->book, binfo->subbook_no); 
	if (error_code != EB_SUCCESS){
		fprintf(stderr, "failed to set subbook %s, %d: %s\n",
			binfo->book_path, binfo->subbook_no,
			eb_error_message(error_code));
		return(error_code);
	}


	if(binfo->appendix != NULL){
		error_code = eb_set_appendix_subbook(binfo->appendix, 
					    binfo->appendix_subbook_no); 
		if (error_code != EB_SUCCESS){
			fprintf(stderr, "failed to set appendix subbook : %s\n",
				eb_error_message(error_code));
			return(error_code);
		}
	}
	return(EB_SUCCESS);

}



syntax highlighted by Code2HTML, v. 0.9.1