[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#697350: Patchpatched <erfolgreich auf Virenfreiheit geprueft>



char *strncpy(char *dest, const char *src, size_t n);

dest and src size were swapped. This patch works for me but needs review
(one of the topics I hate c - I am afraid of a memory glitch)

Description: <short summary of the patch>
 TODO: Put a short summary on the line above and replace this paragraph
 with a longer explanation of this change. Complete the meta-information
 with other relevant fields (see below for details). To make it easier, the
 information below has been extracted from the changelog. Adjust it or drop
 it.
 .
 ebook-speaker (2.0-2) unstable; urgency=low
 .
   [ Samuel Thibault ]
   * debian/patches/ncx.patch: New patch to fix parsing ncx files
     (Closes: Bug#660302).
   * control: Bump Standards-Version to 3.9.3 (no changes).
   * debian/patches/realname.patch: New patch to fix realname to really return
     a valid string.
 .
   [ Paul Gevers ]
   * Update d/copyright file with new format URL
Author: Paul Gevers <paul@climbing.nl>
Bug-Debian: http://bugs.debian.org/660302

---
The information above should follow the Patch Tagging Guidelines, please
checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
are templates for supplementary fields that you might want to add:

Origin: <vendor|upstream|other>, <url of original patch>
Bug: <url in upstream bugtracker>
Bug-Debian: http://bugs.debian.org/<bugnumber>
Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber>
Forwarded: <no|not-needed|url proving that it has been forwarded>
Reviewed-By: <name and email of someone who approved the patch>
Last-Update: <YYYY-MM-DD>

--- ebook-speaker-2.0.orig/eBook-speaker.c
+++ ebook-speaker-2.0/eBook-speaker.c
@@ -1325,9 +1325,9 @@ void read_rc ()
          p += 9;
          while (*p == ' ' || *p == '\t' || *p == '\n')
             p++;
-         strncpy (dc_language, p, 5);
+         strncpy (dc_language, p, sizeof(p));
          p = dc_language;
-         while (*p != ' ' && *p != '\t' && *p != 0)
+         while (*p != ' ' && *p != '\t' && *p != 0 && p < dc_language+sizeof(dc_language)-1)
             p++;
          *p = 0;
       } // if
@@ -1954,7 +1954,8 @@ void set_language ()
       fflush (stdout);
       exit (1);
    } // if
-   strncpy (dc_language, label, 5);
+   strncpy (dc_language, label, sizeof(label)-1);
+   dc_language[sizeof(dc_language)-1] = 0;
    zip_fclose (opf);
 } // set_language
 
@@ -1989,7 +1990,8 @@ int main (int argc, char *argv[])
       switch (opt)
       {
       case 'l':
-         strncpy (dc_language, optarg, 5);
+         strncpy (dc_language, optarg, sizeof(dc_language)-1);
+         dc_language[sizeof(dc_language)-1] = 0;
          break;
       default:
          usage (prog_name);
--- /dev/null
+++ ebook-speaker-2.0/eBook-speaker.c.orig
@@ -0,0 +1,2110 @@
+/* eBook-speaker - read aloud an eBook using a speech synthesizer
+ *  Copyright (C) 2011 J. Lemmens
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#define _GNU_SOURCE
+
+#include "eBook-speaker.h"
+
+#define VERSION "2.0"
+
+WINDOW *screenwin, *titlewin;
+struct zip_file *text_file_fd;
+int discinfo_fp, discinfo = 00, multi = 0, displaying = 0;
+int playing, just_this_item, phrase_nr;
+int bytes_read, current_page_number, total_pages;
+char label[255], bookmark_title[255], dc_language[10], prefix[255];
+char item_title[255];
+char tag[1024], element[1024], search_str[30], tmp_ncx[255], tmp_wav[255];
+char daisy_version[25];
+pid_t player_pid;
+char eBook_title[255], prog_name[255];
+struct zip *eBook;
+struct
+{
+   char a[255],
+        class[255],
+        content[255],
+        dc_title[255],
+        dtb_depth[255],
+        dtb_totalPageCount[255],
+        href[255],
+        id[255],
+        idref[255],
+        media_type[255],
+        name[255],
+        ncc_depth[255],
+        ncc_maxPageNormal[255],
+        ncc_totalTime[255],
+        playorder[255],
+        src[255],
+        title[255],
+        toc[255],
+        type[255],
+        version[255];
+} attribute;
+
+int current, max_y, max_x, total_items, level, depth, speed, pich;
+double audio_total_length;
+char OPF[255], discinfo_html[255], ncc_totalTime[10], NCX[255];
+char sound_dev[16], eBook_mp[255];
+time_t start_time;
+DIR *dir;
+struct dirent *dirent;
+
+void html_entities_to_utf8 (char *s)
+{                     
+  int e_flag, x;
+  char entity[10], *e, new[255], *n, *orig, *s2;
+
+  orig = s;
+  n = new;
+  while (*s)
+  {
+    if (*s == '&')
+    {
+      e_flag = 0;
+      e = entity;
+      s2 = s;
+      s++;
+      while (*s != ';')
+      {
+        *e++ = *s++;
+        if (e - entity == 9)
+        {
+          s = s2 + 1;
+          *n++ = '&';
+          e_flag = 1;
+          break;
+        } // if
+      } // while
+      if (e_flag)
+        continue;
+      *e = 0;
+      *n = ' ';
+      for (x = 0;
+           x < sizeof (unicode_entities) / sizeof (UC_entity_info); x++)
+      {
+        if (strcmp (unicode_entities[x].name, entity) == 0)
+        {
+          char buf[10];
+          int num;
+
+          num = stringprep_unichar_to_utf8 (unicode_entities[x].code, buf);
+          strncpy (n, buf, num);
+          n += num;
+        } // if
+      } // for
+      if (! *(++s))
+        break;
+    } // if (*s == '&')
+    else
+      *n++ = *s++;
+  } // while
+  *n = 0;
+  strncpy (orig, new, 250);
+} // html_entities_to_utf8
+
+int read_text (int item, int phrase_nr)
+{
+   char cmd[255], tmp_txt[255], str[255];
+   int nr, w;
+
+   open_text_file (eBook_struct[item].text_file,
+                   eBook_struct[item].anchor);
+   nr = 1;
+   while (1)
+   {
+      if (get_tag_or_label (text_file_fd) == EOF ||
+          (*eBook_struct[item + 1].anchor &&
+             strcasestr (element, eBook_struct[item + 1].anchor)) ||
+          (*eBook_struct[item + 1].anchor &&
+             strcasestr (label, eBook_struct[item + 1].anchor)))
+      {
+         if (item >= total_items - 1)
+         {
+            quit_eBook_reader ();
+            snprintf (str, 250, "%s/.eBook-speaker/%s",
+                      getenv ("HOME"), bookmark_title);
+            unlink (str);
+            _exit (0);
+         } // if
+         return EOF;
+      } // if
+      if (*label)
+         if (nr++ == phrase_nr)
+            break;
+   } // while
+   switch (player_pid = fork ())
+   {
+   case -1:
+      endwin ();
+      playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+      puts ("fork()");
+      fflush (stdout);
+      _exit (1);
+   case 0:
+      player_pid = setsid ();
+      break;
+   default:
+      return ! EOF;
+   } // switch
+
+   wattron (screenwin, A_BOLD);
+   mvwprintw (screenwin, eBook_struct[item].y, 71, "%4d %4d",
+              phrase_nr - 1, eBook_struct[item].n_phrases - phrase_nr + 1);
+   wattroff (screenwin, A_BOLD);
+   wmove (screenwin, eBook_struct[displaying].y,
+                     eBook_struct[displaying].x - 1);
+   wrefresh (screenwin);
+
+   snprintf (tmp_txt, 200, "/tmp/eBook-speaker.XXXXXX");
+   mkstemp (tmp_txt);
+   unlink (tmp_txt);
+   strcat (tmp_txt, ".txt");
+   if ((w = open (tmp_txt, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR)) == -1)
+   {
+      endwin ();
+      playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+      printf ("Can't make a temp file %s\n", tmp_txt);
+      fflush (stdout);
+      kill (getppid (), SIGINT);
+   } // if
+   html_entities_to_utf8 (label);
+   if (write (w, label, strlen (label)) == -1)
+   {
+      endwin ();
+      playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+      printf ("write (\"%s\"): failed.\n", label);
+      fflush (stdout);
+      kill (getppid (), SIGINT);
+   } // if
+   close (w);
+   snprintf (cmd, 250, "espeak -s %d -f %s -v %s", speed, tmp_txt, dc_language);
+   system (cmd);
+   unlink (tmp_txt);
+   _exit (0);
+} // read_text
+
+void playfile (char *filename, char *tempo)
+{
+  sox_format_t *in, *out; /* input and output files */
+  sox_effects_chain_t *chain;
+  sox_effect_t *e;
+  char *args[255], str[255];
+
+  sox_globals.verbosity = 0;
+  sox_init();
+  in = sox_open_read (filename, NULL, NULL, NULL);
+  while (! (out =
+         sox_open_write (sound_dev, &in->signal, NULL, "alsa", NULL, NULL)))
+  {
+    strncpy (sound_dev, "default", 8);
+    save_rc ();
+    if (out)
+      sox_close (out);
+  } // while
+
+  chain = sox_create_effects_chain (&in->encoding, &out->encoding);
+
+  e = sox_create_effect (sox_find_effect ("input"));
+  args[0] = (char *) in, sox_effect_options (e, 1, args);
+  sox_add_effect (chain, e, &in->signal, &in->signal);
+
+  e = sox_create_effect (sox_find_effect ("tempo"));
+  args[0] = tempo, sox_effect_options (e, 1, args);
+  sox_add_effect (chain, e, &in->signal, &in->signal);
+
+  snprintf (str, 90, "%lf", out->signal.rate);
+  e = sox_create_effect (sox_find_effect ("rate"));
+  args[0] = str, sox_effect_options (e, 1, args);
+  sox_add_effect (chain, e, &in->signal, &in->signal);
+
+  snprintf (str, 90, "%i", out->signal.channels);
+  e = sox_create_effect (sox_find_effect ("channels"));
+  args[0] = str, sox_effect_options (e, 1, args);
+  sox_add_effect (chain, e, &in->signal, &in->signal);
+
+  e = sox_create_effect (sox_find_effect ("output"));
+  args[0] = (char *) out, sox_effect_options (e, 1, args);
+  sox_add_effect (chain, e, &in->signal, &out->signal);
+
+  sox_flow_effects (chain, NULL, NULL);
+  sox_delete_effects_chain (chain);
+  sox_close (out);
+  sox_close (in);
+  sox_quit ();
+} // playfile
+
+char *realname (char *name)
+{
+   if (! (dir = opendir (eBook_mp)))
+   {
+      endwin ();
+      playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+      printf (gettext ("\nCannot read %s\n"), eBook_mp);
+      fflush (stdout);
+      _exit (1);
+   } // if
+   while ((dirent = readdir (dir)) != NULL)
+   {
+      if (strcasecmp (dirent->d_name, name) == 0)
+      {
+         char *newname = strdup(dirent->d_name);
+         closedir (dir);
+         return newname;
+      } // if
+   } // while
+   closedir (dir);
+   return strdup(name);
+} // realname
+
+double read_time (char *p)
+{
+   char *h, *m, *s;
+
+   s = strrchr (p, ':') + 1;
+   *(s - 1) = 0;
+   if (strchr (p, ':'))
+   {
+      m = strrchr (p, ':') + 1;
+      *(m - 1) = 0;
+      h = p;
+   }
+   else
+   {
+      h = "0";
+      m = p;
+   } // if
+   return atoi (h) * 3600 + atoi (m) * 60 + atof (s);
+} // read_time
+
+void put_bookmark ()
+{
+   int w;
+   char str[255];
+
+   snprintf (str, 250, "%s/.eBook-speaker", getenv ("HOME"));
+   mkdir (str, 0755);
+   snprintf (str, 250, "%s/.eBook-speaker/%s",
+                       getenv ("HOME"), bookmark_title);
+   if ((w = creat (str, 0644)) != -1)
+   {
+      dprintf (w, "%d\n", current);
+      dprintf (w, "%d\n", --phrase_nr);
+      dprintf (w, "%d\n", level);
+      close (w);
+   } // if
+} // put_bookmark
+
+void get_bookmark ()
+{
+   char str[255];
+   FILE *r;
+
+   snprintf (str, 250, "%s/.eBook-speaker/%s",
+                       getenv ("HOME"), bookmark_title);
+   if ((r = fopen (str, "r")) == NULL)
+      return;
+   fscanf (r, "%d", &current);
+   open_text_file (eBook_struct[current].text_file,
+                   eBook_struct[current].anchor);
+   fscanf (r, "%d", &phrase_nr);
+   if (phrase_nr < 1)
+      phrase_nr = 1;
+   fscanf (r, "%d", &level);
+   fclose (r);
+   if (level < 1)
+      level = 1;
+   displaying = playing = current;
+   just_this_item = -1;
+} // get_bookmark
+
+void get_attributes (char *p)
+{
+   char buffer[1024], *name, *value, *begin;
+   int break2;
+
+   *attribute.class = 0;
+   *attribute.content = 0;
+   *attribute.dc_title = 0;
+   *attribute.dtb_depth = 0,
+   *attribute.dtb_totalPageCount = 0;
+   *attribute.href = 0;
+   *attribute.id = 0;
+   *attribute.idref = 0;
+   *attribute.media_type = 0;
+   *attribute.name = 0;
+   *attribute.ncc_depth = 0;
+   *attribute.ncc_maxPageNormal = 0,
+   *attribute.ncc_totalTime = 0;
+   *attribute.playorder = 0;
+   *attribute.src = 0;
+   *attribute.title = 0;
+   *attribute.toc = 0;
+   *attribute.type = 0;
+   begin = p;
+
+// skip to first attribute
+   while (! isspace (*p))
+   {
+      if (*p == '>' || *p == '?')
+         return;
+      if (p - begin > 1000)
+      {
+         *p = 0;
+         return;
+      } // if
+      p++;
+   } // while
+   break2 = 0;
+   strncpy (buffer, p, 1000);
+   p = buffer;
+   while (1)
+   {
+      while (isspace (*++p))
+      {
+         if (*p == '>' || *p == '?')
+         {
+            break2 = 1;
+            break;
+         } // if
+         if (p - buffer > 1000)
+         {
+            *p = 0;
+            break2 = 1;
+            break;
+         } // if
+      } // while
+      if (break2)
+        break;
+      name = p;
+      p = name;
+      while (! isspace (*p) && *p != '=')
+      {
+         if (*p == '>' || *p == '?')
+         {
+            break2 = 1;
+            break;
+         } // if
+         if (p - buffer > 1000)
+         {
+            *p = 0;
+            break2 = 1;
+            break;
+         } // if
+         p++;
+      } // while
+      if (break2)
+         break;
+      *p = 0;
+      while (*p != '"')
+      {
+         if (*p == '>' || *p == '?')
+         {
+            break2 = 1;
+            break;
+         } // if
+         if (p - buffer > 1000)
+         {
+            *p = 0;
+            break2 = 1;
+            return; 
+         } // if
+         p++;
+      } // while
+      if (break2)
+         break;
+      p++;
+
+      value = p;
+      p = value;
+      while (*p != '"' && *p != '>' && *p != '?')
+      {
+         if (p - buffer > 1000)
+         {
+            *p = 0;
+            break2 = 1;
+            break;
+         } // if
+         p++;
+      } // while
+      if (break2)
+         break;
+      *p = 0;
+
+      if (strcasecmp (name, "class") == 0)
+         strncpy (attribute.class, value, 90);
+      if (strcasecmp (name, "content") == 0)
+         strncpy (attribute.content, value, 90);
+      if (strcasecmp (name, "href") == 0)
+         strncpy (attribute.href, value, 90);
+      if (strcasecmp (name, "id") == 0)
+         strncpy (attribute.id, value, 90);
+      if (strcasecmp (name, "idref") == 0)
+         strncpy (attribute.idref, value, 90);
+      if (strcasecmp (name, "media-type") == 0)
+         strncpy (attribute.media_type, value, 90);
+      if (strcasecmp (name, "name") == 0)
+      {
+         if (strcasecmp (value, "dc:title") == 0)
+            strncpy (attribute.dc_title, "prepare for content", 90);
+         if (strcasecmp (value, "dtb:depth") == 0)
+            strncpy (attribute.dtb_depth, "prepare for content", 90);
+         if (strcasecmp (value, "dtb:totalPageCount") == 0)
+            strncpy (attribute.ncc_maxPageNormal, "prepare for content", 90);
+         if (strcasecmp (value, "dtb:totalTime") == 0)
+            strncpy (attribute.ncc_totalTime, "prepare for content", 90);
+         if (strcasecmp (value, "ncc:depth") == 0)
+            strncpy (attribute.ncc_depth, "prepare for content", 90);
+         if (strcasecmp (value, "ncc:maxPageNormal") == 0)
+            strncpy (attribute.ncc_maxPageNormal, "prepare for content", 90);
+         if (strcasecmp (value, "ncc:totalTime") == 0)
+            strncpy (attribute.ncc_totalTime, "prepare for content", 90);
+      } // if
+      if (strcasecmp (name, "playorder") == 0)
+         strncpy (attribute.playorder, value, 90);
+      if (strcasecmp (name, "src") == 0)
+         strncpy (attribute.src, value, 250);
+      if (strcasecmp (name, "toc") == 0)
+         strncpy (attribute.toc, value, 90);
+      if (strcasecmp (name, "title") == 0)
+         strncpy (attribute.title, value, 90);
+      if (strcasecmp (name, "type") == 0)
+         strncpy (attribute.type, value, 90);
+      if (strcasecmp (name, "version") == 0)
+         strncpy (attribute.version, value, 90);
+   } // while
+   if (*attribute.dc_title)
+      strncpy (attribute.dc_title, attribute.content, 90);
+   if (*attribute.dtb_depth)
+      depth = atoi (attribute.content);
+   if (*attribute.dtb_totalPageCount)
+      total_pages = atoi (attribute.content);
+   if (*attribute.ncc_depth)
+      depth = atoi (attribute.content);
+   if (*attribute.ncc_maxPageNormal)
+      total_pages = atoi (attribute.content);
+   if (*attribute.ncc_totalTime)
+   {
+      strncpy (attribute.ncc_totalTime, attribute.content, 90);
+      if (strchr (attribute.ncc_totalTime, '.'))
+         *strchr (attribute.ncc_totalTime, '.') = 0;
+   } // if
+} // get_attributes
+
+int get_tag_or_label (struct zip_file *r)
+{
+   char *p, h;
+   static char read_flag = 0;
+
+   p = element;
+   do
+   {
+      switch (zip_fread (r, p, 1))
+      {
+      case -1:
+         endwin ();
+         playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+         printf ("%s: \n", zip_file_strerror (r));
+         fflush (stdout);
+         _exit (1);
+      case 0:
+         return EOF;
+      } // switch
+   } while (isspace (*p));
+   h = *p;
+
+   if (read_flag)
+   {
+      *p++ = '<';
+      *p = h;
+   } // if
+   if (*p == '<' || read_flag)
+   {
+      read_flag = 0;
+      *label = 0;
+      do
+      {
+         if (p - element > 250)
+         {
+            *p = 0;
+            strncpy (tag, element + 1, 250);
+            get_tag ();
+            get_attributes (element);
+            return 0;
+         } // if
+         switch (zip_fread (r, ++p, 1))
+         {
+         case -1:
+            endwin ();
+            playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+            printf ("get_tag_or_label: %s\n", p);
+            fflush (stdout);
+            _exit (1);
+         case 0:
+            *++p = 0;
+            strncpy (tag, element + 1, 250);
+            get_tag ();
+            get_attributes (element);
+            return EOF;
+         } // switch
+      } while (*p != '>');
+      *++p = 0;
+      strncpy (tag, element + 1, 250);
+      get_tag ();
+      get_attributes (element);
+      return 0;
+   } // if
+   *label = *p;
+   *element = 0;
+   p = label;
+   do
+   {
+      if (p - label > 250)
+      {
+         *p = 0;
+         return 0;
+      } // if
+      switch (zip_fread (r, ++p, 1))
+      {
+      case -1:
+         endwin ();
+         playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+         puts (gettext ("Maybe a read-error occurred!"));
+         fflush (stdout);
+         _exit (1);
+      case 0:
+         *p = 0;
+         return EOF;
+      } // switch
+      if (*p == '\n')
+         p--;
+   } while (*p != '<');
+   read_flag = 1;
+   *p = 0;
+   strncpy (tag, element + 1, 250);
+   get_tag ();
+   get_attributes (element);
+   return 0;
+} // get_tag_or_label
+
+void get_tag ()
+{
+   char *p;
+
+   p = tag;
+   while (*p != ' ' && *p != '>' && p - tag <= 250)
+     p++;
+   *p = 0;
+} // get_tag
+
+void get_page_number ()
+{
+   struct zip_file *fd;
+   char file[255], *anchor;
+
+   if (strstr (daisy_version, "2.02"))
+   {
+      if (! strcasestr (element, attribute.src))
+         return;
+      strncpy (file, attribute.src, 250);
+      if (strchr (file, '#'))
+      {
+         anchor = strchr (file, '#') + 1;
+         *strchr (file, '#') = 0;
+      } // if
+      if (! (fd = zip_fopen (eBook, file, ZIP_FL_UNCHANGED)))
+      {                          
+         endwin ();
+         playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+         printf ("get_page_number(): %s\n", file);
+         fflush (stdout);
+         _exit (1);
+      } // if
+      while (1)
+      {
+         if (get_tag_or_label (fd) == EOF)
+         {
+            zip_fclose (fd);
+            return;
+         } // if
+         if (strcasecmp (attribute.id, anchor) == 0)
+            break;
+      } // while
+      while (1)
+      {
+         if (get_tag_or_label (fd) == EOF)
+         {
+            zip_fclose (fd);
+            return;
+         } // if
+         if (strcasecmp (tag, "span") == 0)
+            break;
+         if (tolower (tag[0]) == 'h' && isdigit (tag[1]))
+         {
+            zip_fclose (fd);
+            return;
+         } // if
+      } // while
+      while (1)
+      {
+         if (get_tag_or_label (fd) == EOF)
+         {
+            zip_fclose (fd);
+            return;
+         } // if
+         if (isdigit (*label))
+         {
+            current_page_number = atoi (label);
+            zip_fclose (fd);
+            return;
+         } // if
+      } // while
+      zip_fclose (fd);
+      return;
+   } // if
+   if (strcmp (daisy_version, "3") == 0)
+   {
+      fd = zip_fopen (eBook, OPF, ZIP_FL_UNCHANGED);
+      do      
+      {
+         if (get_tag_or_label (fd) == EOF)
+            break;
+      } while (atoi (attribute.playorder) != current);
+      do
+      {
+         if (get_tag_or_label (fd) == EOF)
+            break;
+      } while (! *label);
+      current_page_number = atoi (label);
+      zip_fclose (fd);
+      return;
+   } // if
+} // get_page_number
+
+int count_phrases (char *f_file, char *f_anchor,
+                   char *t_file, char *t_anchor)
+{
+   int n_phrases;
+   struct zip_file *r;
+
+   if (! (r = zip_fopen (eBook, f_file, ZIP_FL_UNCHANGED)))
+      return EOF;
+   n_phrases = 0;
+   if (*f_anchor)
+   {
+      while (1)
+      {
+         if (get_tag_or_label (r) == EOF)
+// if the given anchor is not there reopen the file to read from the start
+         {
+            zip_fclose (r);
+            r = zip_fopen (eBook, f_file, ZIP_FL_UNCHANGED);
+            break;
+         } // if
+         if (strcasecmp (attribute.id, f_anchor) == 0)
+            break;
+      } // while
+   } // if
+// start counting
+   while (1)
+   {
+      if (get_tag_or_label (r) == EOF)
+      {
+         zip_fclose (r);
+         return n_phrases;
+      } // if
+      if (*t_anchor && strcasecmp (f_file, t_file) == 0)
+      {
+         if (strcasecmp (attribute.id, t_anchor) == 0)
+         {
+            zip_fclose (r);
+            return n_phrases;
+         } // if
+      } // if
+      if (*label)
+         n_phrases++;
+   } // while
+} // count_phrases
+
+void view_screen ()
+{
+   int i, x, l;
+
+   mvwaddstr (titlewin, 1, 0, "----------------------------------------");
+   waddstr (titlewin, "----------------------------------------");
+   mvwprintw (titlewin, 1, 0, gettext ("'h' for help "));
+   if (! discinfo)
+   {
+      mvwprintw (titlewin, 1, 28,
+                 gettext (" level: %d of %d "), level, depth);
+      if (total_pages)
+         mvwprintw (titlewin, 1, 15, gettext (" %d pages "), total_pages);
+      mvwprintw (titlewin, 1, 74, " %d/%d ", \
+              current / max_y + 1, (total_items - 1) / max_y + 1);
+   } // if
+   wrefresh (titlewin);
+
+   wclear (screenwin);
+   for (i = 0; eBook_struct[i].screen != eBook_struct[current].screen; i++);
+   do
+   {
+      if (*eBook_struct[i].label == 0)
+         snprintf (eBook_struct[i].label, 5, "%d",
+                 i + eBook_struct[i].screen * max_y + 1);
+      mvwprintw (screenwin, eBook_struct[i].y, eBook_struct[i].x,
+                            eBook_struct[i].label);
+      l = strlen (eBook_struct[i].label);
+      if (l / 2 * 2 != l)
+         waddstr (screenwin, " ");
+      for (x = l; x < 61; x += 2)
+         waddstr (screenwin, " .");
+      if (eBook_struct[i].page_number)
+         mvwprintw (screenwin, eBook_struct[i].y, 65,
+                    "(%3d)", eBook_struct[i].page_number);
+      l = eBook_struct[i].n_phrases;
+      x = i + 1;
+      while (eBook_struct[x].level > level)
+         l += eBook_struct[x++].n_phrases;
+      if (eBook_struct[i].level <= level)
+         mvwprintw (screenwin, eBook_struct[i].y, 76, "%4d", l);
+      if (i >= total_items - 1)
+         break;
+   } while (eBook_struct[++i].screen == eBook_struct[current].screen);
+   if (just_this_item != -1 &&
+       eBook_struct[displaying].screen == eBook_struct[playing].screen)
+      mvwprintw (screenwin, eBook_struct[current].y, 0, "J");
+   wmove (screenwin, eBook_struct[current].y, eBook_struct[current].x - 1);
+   wrefresh (screenwin);
+} // view_screen
+
+void get_label (int item, int indent)
+{
+   html_entities_to_utf8 (label);
+   strncpy (eBook_struct[item].label, label, 80);
+   eBook_struct[item].label[64 - eBook_struct[item].x] = 0;
+   if (displaying == max_y)
+      displaying = 0;
+   if (strcasecmp (eBook_struct[item].class, "pagenum") == 0)
+      eBook_struct[item].x = 0;
+   else
+      if (eBook_struct[item].x == 0)
+         eBook_struct[item].x = indent + 3;
+} // get_label
+
+static char *convert (char *s)
+{
+   int x = 0, n = 0;
+   static char new[255];
+
+   do
+   {
+      if (s[x] == '%')
+      {
+         char hex[10];
+
+         x++;
+         hex[0] = '0';
+         hex[1] = 'x';
+         hex[2] = s[x++];
+         hex[3] = s[x++];
+         hex[4] = 0;
+         new[n++] = strtod (hex, NULL);
+      }
+      else
+         new[n++] = s[x++];
+   } while (s[x - 1]);
+   return new;
+} // convert
+
+int fill_struct_from_ncx (struct zip_file *ncx, int item)
+{
+   eBook_struct[item].level = 0;
+   while (1)
+   {
+      if (get_tag_or_label (ncx) == EOF)
+         return EOF;
+      if (strcasecmp (tag, "navpoint") == 0)
+      {
+         level++;
+         depth = level;
+      } // if
+      if (strcasecmp (tag, "/navpoint") == 0)
+         level--;
+      if (strcasecmp (tag, "navlabel") == 0)
+      {
+         eBook_struct[item].page_number = 0;
+         do
+         {
+            if (get_tag_or_label (ncx) == EOF)
+               return EOF;
+         } while (*label == 0 && strcasecmp (tag, "/navlabel") != 0);
+         eBook_struct[item].x = level * 3 - 1;
+         if (*label)
+            get_label (item, eBook_struct[item].x);
+         do
+         {
+            if (get_tag_or_label (ncx) == EOF)
+               return EOF;
+         } while (strcasecmp (tag, "content") != 0);
+         strncpy (attribute.src, convert (attribute.src), 250);
+         if (strcasecmp (prefix, ".") != 0)
+            snprintf (eBook_struct[item].text_file, 250,
+                      "%s/%s", prefix, attribute.src);
+         else
+            snprintf (eBook_struct[item].text_file, 250, "%s", attribute.src);
+         if (strchr (eBook_struct[item].text_file, '#'))
+         {
+            strncpy (eBook_struct[item].anchor,
+                     strchr (eBook_struct[item].text_file, '#') + 1, 250);
+            *strchr (eBook_struct[item].text_file, '#') = 0;
+         } // if
+         break;
+      } // if (strcasecmp (tag, "navlabel") == 0)
+      if (strcasecmp (tag, "style") == 0)
+      {
+         while (strcasecmp (tag, "/style") != 0)
+            get_tag_or_label (ncx);
+      } // if (strcasecmp (tag, "style") == 0)
+   } // while
+   return 0;
+} // fill_struct_from_ncx
+
+void read_ncx (char *id)
+{
+   int item;
+   struct zip_file *ncx, *opf;
+   char str[255];
+
+   strncpy (str, id, 90);
+   opf = zip_fopen (eBook, OPF, ZIP_FL_UNCHANGED);
+   while (1)
+   {
+      get_tag_or_label (opf);
+      if (strcasecmp (tag, "item") == 0)
+         if (strcasecmp (str, attribute.id) == 0)
+            break;
+   } // while
+   zip_fclose (opf);
+   if (strcasecmp (prefix, ".") != 0)
+      snprintf (NCX, 90, "%s/%s", prefix, attribute.href);
+   else
+      snprintf (NCX, 90, "%s", attribute.href);
+   if ((ncx = zip_fopen (eBook, NCX, ZIP_FL_UNCHANGED)) == NULL)
+   {
+      endwin ();
+      playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+      printf (gettext ("Corrupt eBook structure %s\n"), NCX);
+      fflush (stdout);
+      _exit (1);
+   } // if
+   item = 0;
+   level = 0;
+   while (1)
+   {
+      if (fill_struct_from_ncx (ncx, item) == EOF)
+         break;
+      eBook_struct[item].level = level;
+      eBook_struct[item].screen = item / max_y;
+      eBook_struct[item].y = item - (eBook_struct[item].screen * max_y);
+      item++;
+   } // while
+   total_items = item;
+   zip_fclose (ncx);
+} // read_ncx
+
+void read_manifest (char *idref, int item)
+{
+   struct zip_file *manifest;
+
+   manifest = zip_fopen (eBook, OPF, ZIP_FL_UNCHANGED);
+   while (1)
+   {
+      if (get_tag_or_label (manifest) == EOF)
+         break;
+      if (strcasecmp (attribute.id, idref) == 0)
+      {
+         snprintf (eBook_struct[item].label, 90, "%d", item + 1);
+         strncpy (eBook_struct[item].text_file, attribute.href, 250);
+         eBook_struct[item].screen = item / max_y;
+         eBook_struct[item].y = item - eBook_struct[item].screen * max_y;
+         if (strchr (eBook_struct[item].text_file, '#'))
+         {
+            strncpy (eBook_struct[item].anchor,
+                     strchr (eBook_struct[item].text_file, '#') + 1, 250);
+            *strchr (eBook_struct[item].text_file, '#') = 0;
+         } // if
+         eBook_struct[item].level = 1;
+         eBook_struct[item].x = eBook_struct[item].level * 3 - 1;
+         break;
+      } // if
+   } // while
+   zip_fclose (manifest);
+}  // read_manifest
+
+void read_tours (struct zip_file *opf)
+{
+   int item = 0;
+
+   depth = 1;
+   while (1)
+   {
+      if (get_tag_or_label (opf) == EOF)
+         break;
+      if (strcasecmp (tag, "site") == 0)
+      {
+         strncpy (eBook_struct[item].label, attribute.title, 90);
+         strncpy (eBook_struct[item].text_file, attribute.href, 250);
+         eBook_struct[item].screen = item / max_y;
+         eBook_struct[item].y = item - eBook_struct[item].screen * max_y;
+         if (strchr (eBook_struct[item].text_file, '#'))
+         {
+            strncpy (eBook_struct[item].anchor,
+                     strchr (eBook_struct[item].text_file, '#') + 1, 250);
+            *strchr (eBook_struct[item].text_file, '#') = 0;
+         } // if
+         eBook_struct[item].level = 1;
+         eBook_struct[item].x = eBook_struct[item].level * 3 - 1;
+         item++;
+      } // if
+      if (strcasecmp (tag, "/tours") == 0)
+         break;
+   } // while
+   total_items = item;
+}  // read_tours
+
+void read_opf (struct zip_file *opf)
+{
+   int item = 0;
+
+   depth = 1;
+   while (1)
+   {
+      if (get_tag_or_label (opf) == EOF)
+         break;
+      if (strcasecmp (tag, "itemref") == 0)
+      {
+         char idref[255];
+
+         strncpy (idref, attribute.idref, 90);
+         read_manifest (idref, item);
+         item++;
+      } // if
+      if (strcasecmp (tag, "/spine") == 0)
+         total_items = item;
+      if (strcasecmp (tag, "tours") == 0)
+      {
+// read tours tag and forget manifest
+         read_tours (opf);
+         break;
+      } // if
+   } // while
+} // read_opf
+
+void read_eBook_struct ()
+{
+   int item = 0;
+   struct zip_file *opf;
+
+   if ((opf = zip_fopen (eBook, OPF, ZIP_FL_UNCHANGED)) == NULL)
+   {
+      endwin ();
+      playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+      printf (gettext ("Corrupt eBook structure %s\n"), OPF);
+      fflush (stdout);
+      _exit (1);
+   } // if
+   while (1)
+   {
+      if (get_tag_or_label (opf) == EOF)
+         break;
+      if (strcasecmp (tag, "spine") == 0)
+      {
+         if (*attribute.toc)
+         {
+            char *id = strdup(attribute.toc);
+            read_ncx (id);
+            free(id);
+// if toc is nevertheless empty, read opf
+            if (total_items == 0)
+               read_opf (opf);
+            break;
+         } // if
+         read_opf (opf);
+      } // if
+   } // while
+   zip_fclose (opf);
+   for (item = 0; item < total_items; item++)
+      eBook_struct[item].n_phrases = count_phrases
+         (eBook_struct[item].text_file, eBook_struct[item].anchor,
+          eBook_struct[item + 1].text_file, eBook_struct[item + 1].anchor);
+} // read_eBook_struct
+
+void player_ended ()
+{
+   wait (NULL);
+} // player_ended
+
+void open_text_file (char *text_file, char *anchor)
+{
+   if (text_file_fd)
+      zip_fclose (text_file_fd);
+   if (! (text_file_fd = zip_fopen (eBook, text_file, ZIP_FL_UNCHANGED)))
+   {
+      endwin ();
+      playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+      printf ("open_text_file(): %s\n", realname (text_file));
+      fflush (stdout);
+      _exit (1);
+   } // if
+   do
+   {
+      if (get_tag_or_label (text_file_fd) == EOF)
+         break;
+   } while (strcasecmp (tag, "body") != 0);
+
+// look if anchor exists in this text_file
+   if (*anchor != 0)
+   {
+      strncpy (item_title, anchor, 250);
+      while (1)
+      {
+         if (get_tag_or_label (text_file_fd) == EOF)
+            break;
+         if (strcasecmp (anchor, attribute.id) == 0 ||
+             (*label && strcasecmp (anchor, label) == 0))
+            break;
+      } // while
+   } // if
+} // open_text_file
+
+void pause_resume ()
+{
+   if (playing != -1)
+      playing = -1;
+   else
+      playing = displaying;
+   if (text_file_fd == NULL)
+         return;
+   else
+   {
+      if (playing != -1)
+      {
+         kill_player ();
+         read_text (playing, phrase_nr - 1);
+      }
+      else
+         kill_player ();
+   } // if
+} // pause_resume
+
+void write_wav (char *infile, char *outfile)
+{
+  sox_format_t *out = NULL;
+  sox_format_t *in;
+  sox_sample_t samples[2048];
+  size_t number_read;
+  char str[255];
+
+  in = sox_open_read (infile, NULL, NULL, NULL);
+  snprintf (str, 250, "%s/%s", getenv ("HOME"), outfile);
+  out = sox_open_write (str, &in->signal, NULL, NULL, NULL, NULL);
+  while ((number_read = sox_read (in, samples, 2048)))
+    sox_write (out, samples, number_read);
+  sox_close (in);
+  sox_close (out);
+} // write_wav
+
+void help ()
+{
+   int y, x;
+
+   getyx (screenwin, y, x);
+   wclear (screenwin);
+   waddstr (screenwin, gettext ("\nThese commands are available in this version:\n"));
+   waddstr (screenwin, "========================================");
+   waddstr (screenwin, "========================================\n\n");
+   waddstr (screenwin, gettext ("cursor down     - move cursor to the next item\n"));
+   waddstr (screenwin, gettext ("cursor up       - move cursor to the previous item\n"));
+   waddstr (screenwin, gettext ("cursor right    - skip to next phrase\n"));
+   waddstr (screenwin, gettext ("cursor left     - skip to previous phrase\n"));
+   waddstr (screenwin, gettext ("page-down       - view next page\n"));
+   waddstr (screenwin, gettext ("page-up         - view previous page\n"));
+   waddstr (screenwin, gettext ("enter           - start playing\n"));
+   waddstr (screenwin, gettext ("space           - pause/resume playing\n"));
+   waddstr (screenwin, gettext ("home            - play on normal speed\n"));
+   waddstr (screenwin, "\n");
+   waddstr (screenwin, gettext ("Press any key for next page..."));
+   nodelay (screenwin, FALSE);
+   wgetch (screenwin);
+   nodelay (screenwin, TRUE);
+   wclear (screenwin);
+   waddstr (screenwin, gettext ("\n/               - search for a label\n"));
+   waddstr (screenwin, gettext ("D               - decrease playing speed\n"));
+   waddstr (screenwin, gettext ("f               - find the currently playing item and place the cursor there\n"));
+   waddstr (screenwin, gettext ("g               - go to page number (if any)\n"));
+   waddstr (screenwin, gettext ("h or ?          - give this help\n"));
+   waddstr (screenwin, gettext ("j               - just play current item\n"));
+   waddstr (screenwin, gettext ("l               - switch to next level\n"));
+   waddstr (screenwin, gettext ("L               - switch to previous level\n"));
+   waddstr (screenwin, gettext ("n               - search forwards\n"));
+   waddstr (screenwin, gettext ("N               - search backwards\n"));
+   waddstr (screenwin, gettext ("o               - select next output sound device\n"));
+   waddstr (screenwin, gettext ("p               - place a bookmark\n"));
+   waddstr (screenwin, gettext ("q               - quit eBook-speaker and place a bookmark\n"));
+   waddstr (screenwin, gettext ("s               - stop playing\n"));
+   waddstr (screenwin, gettext ("U               - increase playing speed\n"));
+   waddstr (screenwin, gettext ("\nPress any key to leave help..."));
+   nodelay (screenwin, FALSE);
+   wgetch (screenwin);
+   nodelay (screenwin, TRUE);
+   view_screen ();
+   wmove (screenwin, y, x);
+} // help
+
+void previous_item ()
+{
+   do
+   {
+      if (--current < 0)
+      {
+         displaying = current = 0;
+         beep ();
+         break;
+      } // if
+   } while (eBook_struct[current].level > level);
+   displaying = current;
+   phrase_nr = eBook_struct[playing].n_phrases - 3;
+   view_screen ();
+} // previous_item
+
+void next_item ()
+{
+   do
+   {
+      if (++current >= total_items)
+      {
+         displaying = current = total_items - 1;
+         beep ();
+         break;
+      } // if
+      displaying = current;
+   } while (eBook_struct[current].level > level);
+   view_screen ();
+} // next_item
+
+void skip_left ()
+{
+   int nr;
+
+   if (playing == -1)
+   {
+      beep ();
+      return;
+   } // if
+   kill_player ();
+   open_text_file (eBook_struct[playing].text_file,
+                   eBook_struct[playing].anchor);
+   nr = 0;
+   while (1)
+   {
+      if (get_tag_or_label (text_file_fd) == EOF)
+         break;
+      if (*label)
+      {
+         if (phrase_nr == 2)
+         {
+            current = playing--;
+            phrase_nr = eBook_struct[playing].n_phrases - 1;
+            previous_item ();
+            break;
+         } // if
+/* jos
+         if (phrase_nr == eBook_struct[playing].n_phrases)
+         {
+            phrase_nr = eBook_struct[playing].n_phrases - 3;
+            break;
+         } // if
+*/
+         if (nr == phrase_nr)
+         {
+            phrase_nr = nr - 2;
+            break;
+         } // if
+         nr++;
+      } // if
+   } // while
+} // skip_left
+
+void skip_right ()
+{
+   if (playing == -1)
+   {
+      beep ();
+      return;
+   } // if
+   kill_player ();
+} // skip_right
+
+void change_level (char key)
+{
+
+   if (key == 'l')
+      if (++level > depth)
+         level = 1;
+   if (key == 'L')
+      if (--level < 1)
+         level = depth;
+   if (eBook_struct[playing].level > level)
+      previous_item ();
+   view_screen ();
+} // change_level
+
+void read_rc ()
+{
+   FILE *r;
+   char line[255], *p;
+   struct passwd *pw = getpwuid (geteuid ());
+
+   chdir (pw->pw_dir);
+   strncpy (sound_dev, "default", 8);
+   if ((r = fopen (".eBook-speaker.rc", "r")) == NULL)
+      return;
+   while (fgets (line, 250, r))
+   {
+      if (strchr (line, '#'))
+         *strchr (line, '#') = 0;
+      if ((p = strstr (line, "sound_dev")))
+      {
+         p += 8;
+         while (*++p != 0)
+         {
+            if (*p == '=')
+            {
+               while (! *++p);
+               if (*p == 0)
+                  break;
+               strncpy (sound_dev, p, 15);
+               sound_dev[15] = 0;
+               break;
+            } // if
+         } // while
+      } // if
+      if ((p = strstr (line, "speed")))
+      {
+         p += 4;
+         while (*++p != 0)
+         {
+            if (*p == '=')
+            {
+               while (! isdigit (*++p))
+                  if (*p == 0)
+                     return;
+               speed = atoi (p);
+               break;
+            } // if
+         } // while
+      } // if
+      if ((p = strstr (line, "language")))
+      {
+         p += 9;
+         while (*p == ' ' || *p == '\t' || *p == '\n')
+            p++;
+         strncpy (dc_language, p, 5);
+         p = dc_language;
+         while (*p != ' ' && *p != '\t' && *p != 0)
+            p++;
+         *p = 0;
+      } // if
+   } // while
+   fclose (r);
+} // read_rc
+
+void save_rc ()
+{
+   FILE *w;
+   struct passwd *pw = getpwuid (geteuid ());
+   chdir (pw->pw_dir);
+   if ((w = fopen (".eBook-speaker.rc", "w")) == NULL)
+      return;
+   fputs ("# This file contains the name of the desired audio device and  the\n", w);
+   fputs ("# desired playing speed.\n", w);
+   fputs ("#\n", w);
+   fputs ("# WARNING\n", w);
+   fputs ("# If you edit this file by hand, be sure there is no eBook-speaker active\n", w);
+   fputs ("# otherwise your changes will be lost.\n", w);
+   fputs ("#\n", w);
+   fputs ("# On which ALSA-audio device should eBook-speaker read the book?\n", w);
+   fputs ("# default: sound_dev=default\n", w);
+   fprintf (w, "sound_dev=%s\n", sound_dev);
+   fputs ("#\n", w);
+   fputs ("# At wich speed should the book be read?\n", w);
+   fputs ("# default: speed=160\n", w);
+   fprintf (w, "speed=%i\n", speed);                             
+   fputs ("#\n", w);
+   fputs ("# What should the language be if it is not specified in the book?\n", w);
+   fprintf (w, "language=%s\n", dc_language);
+   fclose (w);
+} // save_rc
+
+void quit_eBook_reader ()
+{
+   endwin ();
+   kill_player ();
+   wait (NULL);
+   put_bookmark ();
+   save_rc ();
+   if (text_file_fd)
+      zip_fclose (text_file_fd);
+   if (*tmp_ncx)
+      unlink (tmp_ncx);
+   unlink (tmp_wav);
+} // quit_eBook_reader
+
+void search (int start, char mode)
+{
+   int c, found = 0;
+
+   if (mode == '/')
+   {
+      if (playing != -1)
+         kill_player ();
+      mvwaddstr (titlewin, 1, 0, "----------------------------------------");
+      waddstr (titlewin, "----------------------------------------");
+      mvwprintw (titlewin, 1, 0, gettext ("What do you search?                           "));
+      echo ();
+      wmove (titlewin, 1, 20);
+      wgetnstr (titlewin, search_str, 25);
+      noecho ();
+   } // if
+   if (mode == '/' || mode == 'n')
+   {
+      for (c = start; c < total_items; c++)
+      {
+         if (strcasestr (eBook_struct[c].label, search_str))
+         {
+            found = 1;
+            break;
+         } // if
+      } // for
+      if (! found)
+      {
+         for (c = 0; c < start; c++)
+         {
+            if (strcasestr (eBook_struct[c].label, search_str))
+            {
+               found = 1;
+               break;
+            } // if
+         } // for
+      } // if
+   }
+   else
+   { // mode == 'N'
+      for (c = start; c >= 0; c--)
+      {
+         if (strcasestr (eBook_struct[c].label, search_str))
+         {
+            found = 1;
+            break;
+         } // if
+      } // for
+      if (! found)
+      {
+         for (c = total_items + 1; c > start; c--)
+         {
+            if (strcasestr (eBook_struct[c].label, search_str))
+            {
+               found = 1;
+               break;
+            } // if
+         } // for
+      } // if
+   } // if
+   if (found)
+   {
+      current = c;
+      phrase_nr = 1;
+      just_this_item = -1;
+      displaying = playing = current;
+      if (playing != -1)
+         kill_player ();
+      open_text_file (eBook_struct[current].text_file,
+                      eBook_struct[current].anchor);
+   }
+   else
+   {
+      beep ();
+      kill_player ();
+      read_text (playing, phrase_nr - 1);
+   } // if
+   view_screen ();
+} // search
+
+void kill_player ()
+{
+   killpg (player_pid, SIGKILL);
+} // kill_player
+
+void go_to_page_number ()
+{
+   struct zip_file *fd;
+   char *p, filename[255], anchor[255], pre_filename[255], pre_anchor[255];
+   char pn[15];
+
+   kill_player ();
+   mvwaddstr (titlewin, 1, 0, "----------------------------------------");
+   waddstr (titlewin, "----------------------------------------");
+   mvwprintw (titlewin, 1, 0, gettext ("Go to page number:        "));
+   echo ();
+   wmove (titlewin, 1, 19);
+   wgetnstr (titlewin, pn, 5);
+   noecho ();
+   view_screen ();
+   if (! *pn || ! isdigit (*pn))
+   {
+      beep (); 
+      skip_left ();
+      return;
+   } // if
+   if (! (fd = zip_fopen (eBook, OPF, ZIP_FL_UNCHANGED)))
+   {
+      endwin ();
+      playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+      printf (gettext ("Something is wrong with the %s file\n"), OPF);
+      fflush (stdout);
+      _exit (1);
+   } // if
+   do
+   {
+      if (get_tag_or_label (fd) == EOF)
+      {
+         beep ();
+         zip_fclose (fd);
+         return;
+      } // if
+      if (strcasecmp (tag, "a") == 0 && ! *label)
+      {
+         strncpy (pre_filename, filename, 90);
+         strncpy (pre_anchor, anchor, 90);
+         strncpy (filename, attribute.href, 90);
+         p = filename;
+         while (*p != 0 && *p != '"' && *p != '\'' && *p != '>' && *p != '#')
+            p++;
+         *p++ = 0;
+         strncpy (anchor, p, 90);
+         p = anchor;
+         while (*p != 0 && *p != '"' && *p != '\'' && *p != '>' && *p != '#')
+            p++;
+         *p = 0;
+      } // if "a"
+      if (strcmp (label, pn) == 0)
+      {
+         zip_fclose (fd);
+         for (current = 0; current <= total_items; current++)
+         {
+            if (strcasecmp (eBook_struct[current].text_file, pre_filename) == 0)
+               break;
+         } // for
+         view_screen ();
+         playing = current;
+         just_this_item = -1;
+         open_text_file (pre_filename, pre_anchor);
+         return;
+      } // if
+   } while (strcasecmp (tag, "/html") != 0);
+   beep ();
+} // go_to_page_number
+
+void select_next_output_device ()
+{
+   FILE *r;
+   int n, y;
+   char list[10][255], trash[255];
+
+   wclear (screenwin);
+   wprintw (screenwin, "\nSelect an soundcard:\n\n");
+   if (! (r = fopen ("/proc/asound/cards", "r")))
+   {
+      endwin ();
+      playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+      puts (gettext ("Cannot read /proc/asound/cards"));
+      fflush (stdout);
+      _exit (1);
+   } // if
+   for (n = 0; n < 10; n++)
+   {
+      *list[n] = 0;
+      fgets (list[n], 250, r);
+      fgets (trash, 250, r);
+      if (! *list[n])
+         break;
+      wprintw (screenwin, "   %s", list[n]);
+   } // for
+   fclose (r);
+   y = 3;
+   nodelay (screenwin, FALSE);
+   for (;;)
+   {
+      wmove (screenwin, y, 2);
+      switch (wgetch (screenwin))
+      {
+      case 13:
+         snprintf (sound_dev, 15, "default:%i", y - 3);
+         view_screen ();
+         nodelay (screenwin, TRUE);
+         return;
+      case KEY_DOWN:
+         if (++y == n + 3)
+            y = 3;
+         break;
+      case KEY_UP:
+         if (--y == 2)
+           y = n + 2;
+         break;
+      default:
+         view_screen ();
+         nodelay (screenwin, TRUE);
+         return;
+      } // switch
+   } // for
+} // select_next_output_device
+
+void browse ()
+{
+   int old;
+
+   current = 0;
+   just_this_item = playing = -1;
+   text_file_fd = NULL;
+   get_bookmark ();
+   view_screen ();
+   nodelay (screenwin, TRUE);
+
+   for (;;)
+   {
+      signal (SIGCHLD, player_ended);
+      switch (wgetch (screenwin))
+      {
+      case 13:
+         playing = displaying = current;
+         phrase_nr = 1;
+         just_this_item = -1;
+         view_screen ();
+         displaying = playing = current;
+         kill_player ();
+         open_text_file (eBook_struct[current].text_file,
+                         eBook_struct[current].anchor);
+         break;
+      case '/':
+         search (current + 1, '/');
+         break;
+      case ' ':
+         pause_resume ();
+         break;
+      case 'f':
+         if (playing == -1)
+         {
+            beep ();
+            break;
+         } // if
+         current = displaying= playing;
+         view_screen ();
+         break;
+      case 'g':
+         if (total_pages)
+            go_to_page_number ();
+         else
+            beep ();
+         break;
+      case 'h':
+      case '?':
+         kill_player ();
+         help ();
+         kill_player ();
+         if (playing != -1)
+            read_text (playing, phrase_nr - 1);
+         break;
+      case 'j':
+         if (just_this_item != -1)
+            just_this_item = -1;
+         else
+         {
+            if (playing == -1)
+            {
+               phrase_nr = 1;
+               strncpy (item_title, eBook_struct[current].anchor, 250);
+            } // if
+            playing = just_this_item = current;
+         } // if
+         mvwprintw (screenwin, eBook_struct[current].y, 0, " ");
+         if (playing == -1)
+         {
+            just_this_item = displaying = playing = current;
+            phrase_nr = 1;
+            kill_player ();
+            open_text_file (eBook_struct[current].text_file,
+                            eBook_struct[current].anchor);
+         } // if
+         if (just_this_item != -1 &&
+             eBook_struct[displaying].screen == eBook_struct[playing].screen)
+            mvwprintw (screenwin, eBook_struct[current].y, 0, "J");
+         else
+            mvwprintw (screenwin, eBook_struct[current].y, 0, " ");
+         wrefresh (screenwin);
+         break;
+      case 'l':
+         change_level ('l');
+         break;
+      case 'L':
+         change_level ('L');
+         break;
+      case 'n':
+         search (current + 1, 'n');
+         break;
+      case 'N':
+         search (current - 1, 'N');                   
+         break;
+      case 'o':
+         if (playing != -1)
+            kill_player ();
+         select_next_output_device ();
+         if (playing != -1)
+            kill_player ();
+         break;
+      case 'p':
+         put_bookmark();
+         break;
+      case 'q':
+         quit_eBook_reader ();
+         _exit (0);
+      case 's':
+         playing = just_this_item = -1;
+         view_screen ();
+         kill_player ();
+         break;
+      case KEY_DOWN:
+         next_item ();
+         break;
+      case KEY_UP:
+         previous_item ();
+         break;
+      case KEY_RIGHT:
+         skip_right ();
+         break;
+      case KEY_LEFT:
+         skip_left ();
+         break;
+      case KEY_NPAGE:
+         if (current / max_y == (total_items - 1) / max_y)
+         {
+            beep ();
+            break;
+         } // if
+         old = current / max_y;
+         while (current / max_y == old)
+            next_item ();
+         view_screen ();
+         break;
+      case KEY_PPAGE:
+         if (current / max_y == 0)
+         {
+            beep ();
+            break;
+         } // if
+         old = current / max_y - 1;
+         current = 0;
+         while (current / max_y != old)
+            next_item ();
+         view_screen ();
+         break;
+      case ERR:
+         break;
+      case 'U':
+         if (speed >= 300)
+         {
+            beep ();
+            break;
+         } // if
+         speed += 10;
+         if (playing == -1)
+            break;
+         kill_player ();
+         read_text (playing, phrase_nr - 1);
+         break;
+      case 'D':
+         if (speed <= 50)
+         {
+            beep ();
+            break;
+         } // if
+         speed -= 10;
+         if (playing == -1)
+            break;
+         kill_player ();
+         read_text (playing, phrase_nr - 1);
+         break;
+      case KEY_HOME:
+         speed = 160;
+         if (playing == -1)
+            break;
+         kill_player ();
+         read_text (playing, phrase_nr - 1);
+         break;
+      default:
+         beep ();
+         break;
+      } // switch
+      if (playing != -1 && kill (player_pid, 0) != 0)
+      {
+         if (read_text (playing, phrase_nr++) == EOF)
+         {
+            phrase_nr = 1;
+            playing++;
+            if (eBook_struct[playing].level <= level)
+               current = displaying = playing;
+            if (just_this_item != -1 && eBook_struct[playing].level <= level)
+               playing = just_this_item = -1;
+            view_screen ();
+         } // if
+      } // if
+
+      fd_set rfds;
+      struct timeval tv;
+
+      FD_ZERO (&rfds);
+      FD_SET (0, &rfds);
+      tv.tv_sec = 0;
+      tv.tv_usec = 100000;
+// pause till a key has been pressed or 0.1 seconds are elapsed
+      select (1, &rfds, NULL, NULL, &tv);
+   } // for
+} // browse
+
+void usage (char *argv)
+{
+   printf (gettext ("eBook-speaker - Version %s\n"), VERSION);
+   puts ("(C)2011 J. Lemmens");
+   playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+   printf (gettext ("\nUsage: %s eBook_file [-l language]\n"),
+           basename (argv));
+   fflush (stdout);
+   _exit (1);
+} // usage
+
+char *sort_by_playorder ()
+{
+   int n, w;
+   struct zip_file *r;
+
+   snprintf (tmp_ncx, 200, "/tmp/eBook-speaker.XXXXXX");
+   mkstemp (tmp_ncx);
+   unlink (tmp_ncx);
+   strcat (tmp_ncx, ".ncx");
+   if (! (r = zip_fopen (eBook, OPF, ZIP_FL_UNCHANGED)))
+   {
+      endwin ();
+      playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+      printf (gettext ("Something is wrong with the %s file\n"), OPF);
+      fflush (stdout);
+      _exit (1);
+   } // if
+   if ((w = open (tmp_ncx, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR)) == -1)
+   {
+      endwin ();
+      playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+      printf ("sort_by_playorder(%s)\n", tmp_ncx);
+      fflush (stdout);
+      _exit (1);
+   } // if
+   do
+   {
+      if (get_tag_or_label (r) == EOF)
+         break;
+      if (*element)
+         dprintf (w, "%s\n", element);
+      else
+         dprintf (w, "%s\n", label);
+   } while (strcasecmp (tag, "navmap") != 0);
+   n = 1;
+   do
+   {
+      *attribute.playorder = 0;
+      if (get_tag_or_label (r) == EOF)
+         break;
+      if (atoi (attribute.playorder) == n)
+      {
+         dprintf (w, "%s\n", element);
+         do
+         {
+            if (get_tag_or_label (r) == EOF)
+               break;
+            if (*element)
+               dprintf (w, "%s\n", element);
+            else
+               dprintf (w, "%s\n", label);
+         } while (strcasecmp (tag, "content") != 0);
+         n++;
+      } // if
+   } while (strcasecmp (tag, "/ncx") != 0);
+   close (w);
+   zip_fclose (r);
+   return tmp_ncx;
+} // sort_by_playorder
+
+char *open_eBook (char *file)
+{
+   int index;
+   char *p;
+   struct zip_file *opf;
+
+   if (! (eBook = zip_open (file, 0, NULL)))
+   {
+      endwin ();
+      printf ("%s is not an eBook!\n", file);
+      playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+      _exit (1);
+   } // if
+   index = 0;
+   do
+   {
+      if (! (p = (char *)zip_get_name (eBook, index, ZIP_FL_UNCHANGED)))
+         break;
+      if (strcasecmp (p + strlen (p) - 4, ".opf") == 0)
+         break;
+   } while (index++ < zip_get_num_files (eBook));
+   if (! p)
+   {
+      endwin ();
+      printf ("File %s doesn't contain an eBook!\n", file);
+      playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+      _exit (1);
+   } // if
+   if (! (opf = zip_fopen (eBook, p, ZIP_FL_UNCHANGED)))
+   {
+      endwin ();
+      printf ("Cannot read: %s: %s\n", p, strerror (errno));
+      playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+      _exit (1);
+   } // if
+   while (1)
+   {
+      if (get_tag_or_label (opf) == EOF)
+         break;
+      if (*attribute.ncc_totalTime)
+         strncpy (ncc_totalTime, attribute.ncc_totalTime, 8);
+      if (strcasecmp (tag, "dc:title") == 0)
+      {
+         do
+         {
+            get_tag_or_label (opf);
+         } while (! *label);
+         strncpy (eBook_title, label, 90);
+      } // if
+   } // while
+   zip_fclose (opf);
+   strncpy (prefix, p, 90);
+   strncpy (prefix, dirname (prefix), 90);
+   return p;
+} // open_eBook
+
+void set_language ()
+{
+   struct zip_file *opf;
+
+   opf = zip_fopen (eBook, OPF, ZIP_FL_UNCHANGED);
+   do
+   {
+      if (get_tag_or_label (opf) == EOF)
+         break;
+   } while (strcasecmp (tag, "dc:language") != 0);
+   do
+   {
+      if (get_tag_or_label (opf) == EOF ||
+          strcasecmp (tag, "/dc:language") == 0)
+         break;
+   } while (! *label);
+   if (strcasecmp (label, "dut") == 0)
+      strncpy (label, "nl", 5);
+   if (! *label || strcasecmp (label, "UND") == 0)
+   {
+      endwin ();
+      playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+      printf ("Cannot determine the language of this eBook.\n");
+      printf ("Please select one yourself:\n\n");
+      printf ("   %s -l <language-code> <eBook>\n\n", prog_name);
+      printf ("Do:\n\n");
+      printf ("   espeak --voices\n\n");
+      printf ("to get a list of available languages.\n");
+      fflush (stdout);
+      exit (1);
+   } // if
+   strncpy (dc_language, label, 5);
+   zip_fclose (opf);
+} // set_language
+
+int main (int argc, char *argv[])
+{
+   struct zip_file *opf;
+   int opt;
+   char str[255], file[255];
+
+   fclose (stderr); // discard SoX messages
+   strncpy (prog_name, basename (argv[0]), 90);
+   if (argc == 1)
+      usage (prog_name);
+   if (system ("espeak -h > /dev/null") > 0)
+   {
+      playfile (PREFIX"share/daisy-player/error.wav", "1");
+      printf (gettext ("eBook-speaker needs the \"espeak\" programme.\n"));
+      printf (gettext ("Please install it and try again.\n"));
+      _exit (1);
+   } // if
+   speed = 160;
+   atexit (quit_eBook_reader);
+   read_rc ();
+   setlocale (LC_ALL, getenv ("LANG"));
+   setlocale (LC_NUMERIC, "C");
+   textdomain (prog_name);
+   bindtextdomain (prog_name, PREFIX"share/locale");
+   textdomain (prog_name);
+   opterr = 0;
+   while ((opt = getopt (argc, argv, "l:")) != -1)
+   {
+      switch (opt)
+      {
+      case 'l':
+         strncpy (dc_language, optarg, 5);
+         break;
+      default:
+         usage (prog_name);
+      } // switch
+   } // while
+   puts ("(C)2011 J. Lemmens");
+   printf (gettext ("eBook-speaker - Version %s\n"), VERSION);
+   puts (gettext ("Plays eBooks on Linux"));
+   fflush (stdout);
+
+// check if arg is an eBook
+   struct stat st_buf;
+   if (*argv[optind] == '/')
+      snprintf (file, 250, "%s", argv[optind]);
+   else
+      snprintf (file, 250, "%s/%s", getenv ("PWD"), argv[optind]);
+   if (stat (file, &st_buf) == -1)
+   {
+      printf ("stat: %s: %s\n", strerror (errno), file);
+      playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+      _exit (1);
+   } // if
+
+// determine filetype
+   magic_t myt;
+
+   myt = magic_open (MAGIC_CONTINUE | MAGIC_MIME_TYPE);
+   magic_load (myt, NULL);
+   if (strcasecmp (magic_file (myt, file), "application/x-ms-reader") == 0)
+   {
+      char cmd[512], tmpname[255];
+
+      chdir ("/tmp");
+      strncpy (tmpname, tempnam (".", "eBook"), 200);
+      snprintf (cmd, 500, "clit %s %s/ > /dev/null; \
+                           cd %s; \
+                           zip -q -1 -r ../%s.zip *", \
+               file, tmpname, tmpname, tmpname);
+      system (cmd);
+      snprintf (file, 200, "/tmp/%s.zip", tmpname);
+   } // if
+   strncpy (OPF, open_eBook (file), 250);
+   magic_close (myt);
+
+   strcpy (daisy_version, "3");
+
+   initscr ();
+   titlewin = newwin (2, 80,  0, 0);
+   screenwin = newwin (23, 80, 2, 0);
+   getmaxyx (screenwin, max_y, max_x);
+   max_y--;
+   keypad (screenwin, TRUE);
+   meta (screenwin,       TRUE);
+   nonl ();
+   noecho ();
+   player_pid = -2;
+   wattron (titlewin, A_BOLD);
+   snprintf (str, 250, gettext ("eBook-speaker - Version %s - (C)2011 J. Lemmens"), VERSION);
+   wprintw (titlewin, str);
+   wrefresh (titlewin);
+
+   if (strstr (daisy_version, "2.02"))
+   {
+      endwin ();
+      puts ("daisy 2.02 is not supported by eBook-speaker");
+      _exit (0);
+   } // if
+   if (strcmp (daisy_version, "3") == 0)
+   {
+      if ((opf = zip_fopen (eBook, OPF, ZIP_FL_NOCASE)) == NULL)
+      {
+         endwin ();
+         playfile (PREFIX"share/eBook-speaker/error.wav", "1");
+         printf (gettext ("\nCannot read %s\n"), OPF);
+         fflush (stdout);
+         _exit (1);
+      } // if
+      while (1)
+      {
+         if (get_tag_or_label (opf) == EOF)
+            break;
+         if (strcasecmp (tag, "dc:title") == 0)
+         {
+            do
+            {
+               if (get_tag_or_label (opf) == EOF)
+                  break;
+            } while (! *label);
+
+            int i = 0;
+
+            do
+            {
+               if (label[i] == '/')
+                  label[i] = '_';
+            } while (label[i++]);
+            strncpy (bookmark_title, label, 90);
+         } // if
+      } // while
+      zip_fclose (opf);
+   } // if
+   read_eBook_struct ();
+
+   if (strlen (eBook_title) + strlen (str) >= 80)
+      mvwprintw (titlewin, 0, 80 - strlen (eBook_title) - 4, "... ");
+   mvwprintw (titlewin, 0, 80 - strlen (eBook_title), "%s", eBook_title);
+   wrefresh (titlewin);
+   mvwaddstr (titlewin, 1, 0, "----------------------------------------");
+   waddstr (titlewin, "----------------------------------------");
+   mvwprintw (titlewin, 1, 0, gettext ("Press 'h' for help "));
+   wrefresh (titlewin);
+   level = 1;
+   *search_str = 0;
+   if (! *dc_language)
+      set_language ();
+   browse ();
+   return 0;                                              
+} // main

Reply to: