/* -*- mode: c; indent-tabs-mode: nil; c-indent-level: 4; -*- * $Id$ * * Viewer - a part of Plucker, the open-source, open-format ebook system * Copyright (c) 2002, Bill Janssen * * 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 #include #include #include #include #include #include #include "gtkviewer.h" #define PLKR_TIMEADJUST 2082848400 /* difference in seconds between Palm timebase of 12 AM Jan 1, 1904, and UNIX timebase of 12 AM Jan 1, 1970 */ #define PLUCKER_ID_STAMP "DataPlkr" #define READ_BIGENDIAN_SHORT(p) (((p)[0] << 8)|((p)[1])) #define READ_BIGENDIAN_LONG(p) (((p)[0] << 24)|((p)[1] << 16)|((p)[2] << 8)|((p)[3])) static GTree *PluckerDocs = NULL; static gboolean has_suffix (char *main_string, char *potential_suffix) { int l1 = strlen (main_string); int l2 = strlen (potential_suffix); return ((l1 >= l2) && (strncmp (main_string + (l1 - l2), potential_suffix, l2) == 0)); } static char * is_plucker (int fp) { char filebuf[80]; char *retval = NULL; if (fp >= 0) { if (read (fp, filebuf, 72) == 72) { if (strncmp (&filebuf[60], PLUCKER_ID_STAMP, 8) == 0) { retval = g_strdup (filebuf); } } } return retval; } void enumerate_library (GTraverseFunc func, gpointer user_data) { g_tree_foreach (PluckerDocs, func, user_data); } typedef enum { PLKR_HOME_NAME = 0, PLKR_URLS_INDEX_NAME = 2, PLKR_DEFAULT_CATEGORY_NAME = 3, PLKR_METADATA_NAME = 4, } ReservedRecordName; void get_metadata (int fp, int *nrecords, GSList ** categories_out, int *charset_out, char **title_out, char **author_out, time_t * pubtime_out) { unsigned char databuf[128]; int nrecs, rec_offset, nreserved, i, type, id, j, metadata_record_index = 0, categories_record_index = 0, nrecord_headers, rec_length; // int nread; unsigned long ctime; unsigned char record_offsets_data[6 * 8]; GSList *categories = NULL; /* seek to the start of the record list to find the index record's offset */ lseek (fp, 0, SEEK_SET); read (fp, databuf, 78); ctime = READ_BIGENDIAN_LONG (databuf + 36) - PLKR_TIMEADJUST; nrecs = READ_BIGENDIAN_SHORT (databuf + 76); nrecord_headers = MIN (6, nrecs); read (fp, record_offsets_data, nrecord_headers * 8); /* seek to the index record */ rec_offset = READ_BIGENDIAN_LONG (record_offsets_data); lseek (fp, rec_offset, 0); read (fp, databuf, 128); nreserved = READ_BIGENDIAN_SHORT (databuf + 4); for (id = 0, i = 0; i < nreserved; i++) { type = READ_BIGENDIAN_SHORT (databuf + 8 + (i * 4)); if (type == PLKR_DEFAULT_CATEGORY_NAME) { categories_record_index = READ_BIGENDIAN_SHORT (databuf + 6 + (i * 4)); } else if (type == PLKR_METADATA_NAME) { metadata_record_index = READ_BIGENDIAN_SHORT (databuf + 6 + (i * 4)); } } *nrecords = nrecs; *categories_out = NULL; *title_out = NULL; *author_out = NULL; *charset_out = 0; *pubtime_out = 0; /* seek to the default categories */ if (categories_record_index > 0 || metadata_record_index > 0) { for (i = 1; i < MIN (5, nrecord_headers); i++) { rec_offset = READ_BIGENDIAN_LONG (record_offsets_data + (8 * i)); rec_length = READ_BIGENDIAN_LONG (record_offsets_data + (8 * (i + 1))) - rec_offset; lseek (fp, rec_offset, 0); read (fp, databuf, 10); id = READ_BIGENDIAN_SHORT (databuf); type = *(databuf + 6); if (id == categories_record_index && type == PLKR_DRTYPE_CATEGORY) { char *ptr, *temp; j = rec_length - 8; temp = malloc (j); strncpy (temp, databuf + 8, 2); read (fp, temp + 2, j - 2); for (ptr = temp; ptr < (temp + j);) { categories = g_slist_prepend (categories, g_strdup (ptr)); ptr += strlen (ptr) + 1; } *categories_out = categories; free (temp); categories_record_index = 0; if (metadata_record_index == 0) break; } else if (id == metadata_record_index && type == PLKR_DRTYPE_METADATA) { int npieces = READ_BIGENDIAN_SHORT (databuf + 8); int datalen; for (j = 0; j < npieces; j++) { read (fp, databuf, 4); type = READ_BIGENDIAN_SHORT (databuf); datalen = READ_BIGENDIAN_SHORT (databuf + 2) * 2; if (type == 1) { /* charset */ read (fp, databuf, 2); *charset_out = READ_BIGENDIAN_SHORT (databuf); } else if (type == 4) { /* author */ *author_out = malloc (datalen); read (fp, *author_out, datalen); } else if (type == 5) { *title_out = malloc (datalen); read (fp, *title_out, datalen); } else if (type == 6) { read (fp, databuf, 4); *pubtime_out = READ_BIGENDIAN_LONG (databuf) - PLKR_TIMEADJUST; } else { lseek (fp, datalen, SEEK_CUR); } } metadata_record_index = 0; if (categories_record_index == 0) break; } } } } void initialize_library () { char *p; gchar **dirs; GDir *dir; gchar *fname, *path, *docname, *dirname, *homedir; int i, fp; DocInfo *docinfo; if (PluckerDocs != NULL) return; homedir = (char *) g_getenv ("HOME"); PluckerDocs = g_tree_new ((GCompareFunc) strcmp); p = (char *) g_getenv ("PLUCKERPATH"); if (p == NULL) p = plkr_GetConfigString (NULL, "pluckerpath", NULL); if (p == NULL) p = "."; if (p != NULL) { dirs = g_strsplit (p, ":", 0); for (i = 0; dirs[i] != NULL; i++) { dirname = dirs[i]; /* handle ~ characters */ if (dirname[0] == '~' && dirname[1] == G_DIR_SEPARATOR && homedir) { dirname = g_strjoin (G_DIR_SEPARATOR_S, homedir, dirs[i] + 2, NULL); } if (!g_file_test (dirname, G_FILE_TEST_EXISTS)) { fprintf (stderr, "Non-existent path/file on PLUCKERPATH: %s\n", dirname); continue; } else if (g_file_test (dirname, G_FILE_TEST_IS_DIR)) { if ((dir = g_dir_open (dirname, 0, NULL))) { /* fprintf (stderr, "Looking in directory %s...\n", dirname); */ while ((fname = (char *) g_dir_read_name (dir)) != NULL) { if (has_suffix (fname, ".plkr") || has_suffix (fname, ".pdb")) { path = g_strjoin (G_DIR_SEPARATOR_S, dirname, fname, NULL); fp = open (path, O_RDONLY); if ((docname = is_plucker (fp)) != NULL) { /* OK, it's a plucker document, index it by document name */ /* fprintf (stderr, " adding '%s' (%s)\n", filebuf, fname); */ docinfo = (DocInfo *) malloc (sizeof (DocInfo)); docinfo->name = docname; docinfo->path = path; get_metadata (fp, &docinfo->nrecords, &docinfo->categories, &docinfo->charset, &docinfo->title, &docinfo->author, &docinfo->pubtime); g_tree_insert (PluckerDocs, docinfo->name, docinfo); path = NULL; } close (fp); if (path) free (path); } } g_dir_close (dir); } } else if (g_file_test (dirname, G_FILE_TEST_EXISTS)) { fp = open (dirname, O_RDONLY); if ((docname = is_plucker (fp)) != NULL) { /* OK, it's a plucker document, index it by document name */ /* fprintf (stderr, " adding '%s' (%s)\n", filebuf, fname); */ docinfo = (DocInfo *) malloc (sizeof (DocInfo)); docinfo->name = docname; docinfo->path = g_strdup (dirname); get_metadata (fp, &docinfo->nrecords, &docinfo->categories, &docinfo->charset, &docinfo->title, &docinfo->author, &docinfo->pubtime); g_tree_insert (PluckerDocs, docinfo->name, docinfo); } close (fp); } else { fprintf (stderr, "Non-Plucker, non-directory element on PLUCKERPATH: %s\n", dirname); } if (dirname != dirs[i]) free (dirname); } g_strfreev (dirs); } }