]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cgi-bin/help-index.c
Merge changes from CUPS 1.4svn-r7961.
[thirdparty/cups.git] / cgi-bin / help-index.c
index 0b2e53b092ae8ba7d5734fbd589a467783112540..cae6e5a2679f5c3588118731ee06df245807c0f8 100644 (file)
@@ -1,25 +1,16 @@
 /*
- * "$Id: help-index.c 4863 2005-12-03 04:28:10Z mike $"
+ * "$Id: help-index.c 7717 2008-07-04 02:35:33Z mike $"
  *
- *   On-line help index routines for the Common UNIX Printing System (CUPS).
+ *   Online help index routines for the Common UNIX Printing System (CUPS).
  *
- *   Copyright 1997-2005 by Easy Software Products.
+ *   Copyright 2007-2008 by Apple Inc.
+ *   Copyright 1997-2007 by Easy Software Products.
  *
  *   These coded instructions, statements, and computer programs are the
- *   property of Easy Software Products and are protected by Federal
- *   copyright law.  Distribution and use rights are outlined in the file
- *   "LICENSE.txt" which should have been included with this file.  If this
- *   file is missing or damaged please contact Easy Software Products
- *   at:
- *
- *       Attn: CUPS Licensing Information
- *       Easy Software Products
- *       44141 Airport View Drive, Suite 204
- *       Hollywood, Maryland 20636 USA
- *
- *       Voice: (301) 373-9600
- *       EMail: cups-info@cups.org
- *         WWW: http://www.cups.org
+ *   property of Apple Inc. and are protected by Federal copyright
+ *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ *   which should have been included with this file.  If this file is
+ *   file is missing or damaged, see the license at "http://www.cups.org/".
  *
  * Contents:
  *
  *   helpLoadIndex()            - Load a help index from disk.
  *   helpSaveIndex()            - Save a help index to disk.
  *   helpSearchIndex()          - Search an index.
+ *   help_add_word()            - Add a word to a node.
  *   help_compile_search()      - Convert a search string into a regular expression.
- *   help_create_sorted()       - Create the sorted node array.
  *   help_delete_node()         - Free all memory used by a node.
- *   help_insert_node()         - Insert a node in an index.
+ *   help_delete_word()         - Free all memory used by a word.
  *   help_load_directory()      - Load a directory of files into an index.
  *   help_load_file()           - Load a HTML files into an index.
  *   help_new_node()            - Create a new node and add it to an index.
  *   help_sort_nodes_by_name()  - Sort nodes by section, filename, and anchor.
  *   help_sort_nodes_by_score() - Sort nodes by score and text.
+ *   help_sort_words()          - Sort words alphabetically.
  */
 
 /*
 #include <cups/dir.h>
 
 
+/*
+ * List of common English words that should not be indexed...
+ */
+
+static char            help_common_words[][6] =
+                       {
+                         "about",
+                         "all",
+                         "an",
+                         "and",
+                         "are",
+                         "as",
+                         "at",
+                         "be",
+                         "been",
+                         "but",
+                         "by",
+                         "call",
+                         "can",
+                         "come",
+                         "could",
+                         "day",
+                         "did",
+                         "do",
+                         "down",
+                         "each",
+                         "find",
+                         "first",
+                         "for",
+                         "from",
+                         "go",
+                         "had",
+                         "has",
+                         "have",
+                         "he",
+                         "her",
+                         "him",
+                         "his",
+                         "hot",
+                         "how",
+                         "if",
+                         "in",
+                         "is",
+                         "it",
+                         "know",
+                         "like",
+                         "long",
+                         "look",
+                         "make",
+                         "many",
+                         "may",
+                         "more",
+                         "most",
+                         "my",
+                         "no",
+                         "now",
+                         "of",
+                         "on",
+                         "one",
+                         "or",
+                         "other",
+                         "out",
+                         "over",
+                         "said",
+                         "see",
+                         "she",
+                         "side",
+                         "so",
+                         "some",
+                         "sound",
+                         "than",
+                         "that",
+                         "the",
+                         "their",
+                         "them",
+                         "then",
+                         "there",
+                         "these",
+                         "they",
+                         "thing",
+                         "this",
+                         "time",
+                         "to",
+                         "two",
+                         "up",
+                         "use",
+                         "was",
+                         "water",
+                         "way",
+                         "we",
+                         "were",
+                         "what",
+                         "when",
+                         "which",
+                         "who",
+                         "will",
+                         "with",
+                         "word",
+                         "would",
+                         "write",
+                         "you",
+                         "your"
+                       };
+
+
 /*
  * Local functions...
  */
 
-static void            help_create_sorted(help_index_t *hi);
+static help_word_t     *help_add_word(help_node_t *n, const char *text);
 static void            help_delete_node(help_node_t *n);
-static void            help_insert_node(help_index_t *hi, help_node_t *n);
+static void            help_delete_word(help_word_t *w);
 static int             help_load_directory(help_index_t *hi,
                                            const char *directory,
                                            const char *relative);
@@ -65,8 +162,9 @@ static help_node_t   *help_new_node(const char *filename, const char *anchor,
                                       const char *section, const char *text,
                                       time_t mtime, off_t offset,
                                       size_t length);
-static int             help_sort_by_name(const void *p1, const void *p2);
-static int             help_sort_by_score(const void *p1, const void *p2);
+static int             help_sort_by_name(help_node_t *p1, help_node_t *p2);
+static int             help_sort_by_score(help_node_t *p1, help_node_t *p2);
+static int             help_sort_words(help_word_t *w1, help_word_t *w2);
 
 
 /*
@@ -74,9 +172,9 @@ static int           help_sort_by_score(const void *p1, const void *p2);
  */
 
 void
-helpDeleteIndex(help_index_t *hi)
+helpDeleteIndex(help_index_t *hi)      /* I - Help index */
 {
-  int  i;                              /* Looping var */
+  help_node_t  *node;                  /* Current node */
 
 
   DEBUG_printf(("helpDeleteIndex(hi=%p)\n", hi));
@@ -84,16 +182,16 @@ helpDeleteIndex(help_index_t *hi)
   if (!hi)
     return;
 
-  if (!hi->search)
+  for (node = (help_node_t *)cupsArrayFirst(hi->nodes);
+       node;
+       node = (help_node_t *)cupsArrayNext(hi->nodes))
   {
-    for (i = 0; i < hi->num_nodes; i ++)
-      help_delete_node(hi->nodes[i]);
+    if (!hi->search)
+      help_delete_node(node);
   }
 
-  free(hi->nodes);
-
-  if (hi->sorted)
-    free(hi->sorted);
+  cupsArrayDelete(hi->nodes);
+  cupsArrayDelete(hi->sorted);
 
   free(hi);
 }
@@ -103,13 +201,12 @@ helpDeleteIndex(help_index_t *hi)
  * 'helpFindNode()' - Find a node in an index.
  */
 
-help_node_t **                         /* O - Node pointer or NULL */
+help_node_t *                          /* O - Node pointer or NULL */
 helpFindNode(help_index_t *hi,         /* I - Index */
              const char   *filename,   /* I - Filename */
              const char   *anchor)     /* I - Anchor */
 {
-  help_node_t  key,                    /* Search key */
-               *ptr;                   /* Pointer to key */
+  help_node_t  key;                    /* Search key */
 
 
   DEBUG_printf(("helpFindNode(hi=%p, filename=\"%s\", anchor=\"%s\")\n",
@@ -128,14 +225,12 @@ helpFindNode(help_index_t *hi,            /* I - Index */
 
   key.filename = (char *)filename;
   key.anchor   = (char *)anchor;
-  ptr          = &key;
 
  /*
   * Return any match...
   */
 
-  return ((help_node_t **)bsearch(&ptr, hi->nodes, hi->num_nodes,
-                                  sizeof(help_node_t *), help_sort_by_name));
+  return ((help_node_t *)cupsArrayFind(hi->nodes, &key));
 }
 
 
@@ -160,8 +255,8 @@ helpLoadIndex(const char *hifile,   /* I - Index filename */
   off_t                offset;                 /* Offset into file */
   size_t       length;                 /* Length in bytes */
   int          update;                 /* Update? */
-  int          i;                      /* Looping var */
   help_node_t  *node;                  /* Current node */
+  help_word_t  *word;                  /* Current word */
 
 
   DEBUG_printf(("helpLoadIndex(hifile=\"%s\", directory=\"%s\")\n",
@@ -171,7 +266,19 @@ helpLoadIndex(const char *hifile,  /* I - Index filename */
   * Create a new, empty index.
   */
 
-  hi = (help_index_t *)calloc(1, sizeof(help_index_t));
+  if ((hi = (help_index_t *)calloc(1, sizeof(help_index_t))) == NULL)
+    return (NULL);
+
+  hi->nodes  = cupsArrayNew((cups_array_func_t)help_sort_by_name, NULL);
+  hi->sorted = cupsArrayNew((cups_array_func_t)help_sort_by_score, NULL);
+
+  if (!hi->nodes || !hi->sorted)
+  {
+    cupsArrayDelete(hi->nodes);
+    cupsArrayDelete(hi->sorted);
+    free(hi);
+    return (NULL);
+  }
 
  /*
   * Try loading the existing index file...
@@ -185,12 +292,14 @@ helpLoadIndex(const char *hifile, /* I - Index filename */
 
     cupsFileLock(fp, 1);
 
-    if (cupsFileGets(fp, line, sizeof(line)) && !strcmp(line, "HELPV1"))
+    if (cupsFileGets(fp, line, sizeof(line)) && !strcmp(line, "HELPV2"))
     {
      /*
       * Got a valid header line, now read the data lines...
       */
 
+      node = NULL;
+
       while (cupsFileGets(fp, line, sizeof(line)))
       {
        /*
@@ -198,77 +307,97 @@ helpLoadIndex(const char *hifile, /* I - Index filename */
        *
        *     filename mtime offset length "section" "text"
        *     filename#anchor offset length "text"
+       *     SP count word
        */
 
-       filename = line;
-
-       if ((ptr = strchr(line, ' ')) == NULL)
-          break;
-
-       while (isspace(*ptr & 255))
-          *ptr++ = '\0';
-
-       if ((anchor = strrchr(filename, '#')) != NULL)
+        if (line[0] == ' ')
        {
-          *anchor++ = '\0';
-         mtime = 0;
-       }
-       else
-         mtime = strtol(ptr, &ptr, 10);
-
-       offset = strtoll(ptr, &ptr, 10);
-       length = strtoll(ptr, &ptr, 10);
+        /*
+         * Read a word in the current node...
+         */
 
-       while (isspace(*ptr & 255))
-          ptr ++;
+          if (!node || (ptr = strrchr(line, ' ')) == NULL)
+           continue;
 
-        if (!anchor)
+          if ((word = help_add_word(node, ptr + 1)) != NULL)
+           word->count = atoi(line + 1);
+        }
+       else
        {
         /*
-         * Get section...
+         * Add a node...
          */
 
-          if (*ptr != '\"')
-           break;
-
-          ptr ++;
-         sectptr = ptr;
+         filename = line;
 
-          while (*ptr && *ptr != '\"')
-           ptr ++;
+         if ((ptr = strchr(line, ' ')) == NULL)
+            break;
 
-          if (*ptr != '\"')
-           break;
+         while (isspace(*ptr & 255))
+            *ptr++ = '\0';
 
-          *ptr++ = '\0';
+         if ((anchor = strrchr(filename, '#')) != NULL)
+         {
+            *anchor++ = '\0';
+           mtime = 0;
+         }
+         else
+           mtime = strtol(ptr, &ptr, 10);
 
-          strlcpy(section, sectptr, sizeof(section));
+         offset = strtoll(ptr, &ptr, 10);
+         length = strtoll(ptr, &ptr, 10);
 
          while (isspace(*ptr & 255))
             ptr ++;
-        }
 
-        if (*ptr != '\"')
-         break;
+          if (!anchor)
+         {
+          /*
+           * Get section...
+           */
 
-        ptr ++;
-       text = ptr;
+            if (*ptr != '\"')
+             break;
 
-        while (*ptr && *ptr != '\"')
-         ptr ++;
+            ptr ++;
+           sectptr = ptr;
 
-        if (*ptr != '\"')
-         break;
+            while (*ptr && *ptr != '\"')
+             ptr ++;
+
+            if (*ptr != '\"')
+             break;
+
+            *ptr++ = '\0';
+
+            strlcpy(section, sectptr, sizeof(section));
+
+           while (isspace(*ptr & 255))
+              ptr ++;
+          }
+
+          if (*ptr != '\"')
+           break;
+
+          ptr ++;
+         text = ptr;
+
+          while (*ptr && *ptr != '\"')
+           ptr ++;
+
+          if (*ptr != '\"')
+           break;
 
-        *ptr++ = '\0';
+          *ptr++ = '\0';
 
-       if ((node = help_new_node(filename, anchor, section, text,
-                                 mtime, offset, length)) == NULL)
-          break;
+         if ((node = help_new_node(filename, anchor, section, text,
+                                   mtime, offset, length)) == NULL)
+            break;
 
-       help_insert_node(hi, node);
+         node->score = -1;
 
-       node->score = -1;
+         cupsArrayAdd(hi->nodes, node);
+        }
       }
     }
 
@@ -285,45 +414,34 @@ helpLoadIndex(const char *hifile, /* I - Index filename */
   * Remove any files that are no longer installed...
   */
 
-  for (i = 0; i < hi->num_nodes;)
-  {
-    if (hi->nodes[i]->score < 0)
+  for (node = (help_node_t *)cupsArrayFirst(hi->nodes);
+       node;
+       node = (help_node_t *)cupsArrayNext(hi->nodes))
+    if (node->score < 0)
     {
      /*
       * Delete this node...
       */
 
-      help_delete_node(hi->nodes[i]);
-
-      hi->num_nodes --;
-      if (i < hi->num_nodes)
-        memmove(hi->nodes + i, hi->nodes + i + 1,
-               (hi->num_nodes - i) * sizeof(help_node_t *));
-
-      update = 1;
+      cupsArrayRemove(hi->nodes, node);
+      help_delete_node(node);
     }
-    else
-    {
-     /*
-      * Keep this node...
-      */
-
-      i ++;
-    }
-  }
 
  /*
-  * Save the index if we updated it...
+  * Add nodes to the sorted array...
   */
 
-  if (update)
-    helpSaveIndex(hi, hifile);
+  for (node = (help_node_t *)cupsArrayFirst(hi->nodes);
+       node;
+       node = (help_node_t *)cupsArrayNext(hi->nodes))
+    cupsArrayAdd(hi->sorted, node);
 
  /*
-  * Create the sorted array...
+  * Save the index if we updated it...
   */
 
-  help_create_sorted(hi);
+  if (update)
+    helpSaveIndex(hi, hifile);
 
  /*
   * Return the index...
@@ -342,8 +460,8 @@ helpSaveIndex(help_index_t *hi,             /* I - Index */
               const char   *hifile)    /* I - Index filename */
 {
   cups_file_t  *fp;                    /* Index file */
-  int          i;                      /* Looping var */
   help_node_t  *node;                  /* Current node */
+  help_word_t  *word;                  /* Current word */
 
 
   DEBUG_printf(("helpSaveIndex(hi=%p, hifile=\"%s\")\n", hi, hifile));
@@ -361,16 +479,16 @@ helpSaveIndex(help_index_t *hi,           /* I - Index */
 
   cupsFileLock(fp, 1);
 
-  cupsFilePuts(fp, "HELPV1\n");
+  cupsFilePuts(fp, "HELPV2\n");
 
-  for (i = 0; i < hi->num_nodes; i ++)
+  for (node = (help_node_t *)cupsArrayFirst(hi->nodes);
+       node;
+       node = (help_node_t *)cupsArrayNext(hi->nodes))
   {
    /*
     * Write the current node with/without the anchor...
     */
 
-    node = hi->nodes[i];
-
     if (node->anchor)
     {
       if (cupsFilePrintf(fp, "%s#%s " CUPS_LLFMT " " CUPS_LLFMT " \"%s\"\n",
@@ -382,16 +500,28 @@ helpSaveIndex(help_index_t *hi,           /* I - Index */
     else
     {
       if (cupsFilePrintf(fp, "%s %d " CUPS_LLFMT " " CUPS_LLFMT " \"%s\" \"%s\"\n",
-                         node->filename, node->mtime,
+                         node->filename, (int)node->mtime,
                          CUPS_LLCAST node->offset, CUPS_LLCAST node->length,
                         node->section ? node->section : "", node->text) < 0)
         break;
     }
+
+   /*
+    * Then write the words associated with the node...
+    */
+
+    for (word = (help_word_t *)cupsArrayFirst(node->words);
+         word;
+        word = (help_word_t *)cupsArrayNext(node->words))
+      if (cupsFilePrintf(fp, " %d %s\n", word->count, word->text) < 0)
+        break;
   }
 
+  cupsFileFlush(fp);
+
   if (cupsFileClose(fp) < 0)
     return (-1);
-  else if (i < hi->num_nodes)
+  else if (node)
     return (-1);
   else
     return (0);
@@ -408,9 +538,9 @@ helpSearchIndex(help_index_t *hi,   /* I - Index */
                const char   *section,  /* I - Limit search to this section */
                const char   *filename) /* I - Limit search to this file */
 {
-  int          i;                      /* Looping var */
   help_index_t *search;                /* Search index */
-  help_node_t  **n;                    /* Current node */
+  help_node_t  *node;                  /* Current node */
+  help_word_t  *word;                  /* Current word */
   void         *sc;                    /* Search context */
   int          matches;                /* Number of matches */
 
@@ -426,17 +556,27 @@ helpSearchIndex(help_index_t *hi, /* I - Index */
   if (!hi || !query)
     return (NULL);
 
-  for (i = 0, n = hi->nodes; i < hi->num_nodes; i ++, n ++)
-    n[0]->score = 0;
+ /*
+  * Reset the scores of all nodes to 0...
+  */
+
+  for (node = (help_node_t *)cupsArrayFirst(hi->nodes);
+       node;
+       node = (help_node_t *)cupsArrayNext(hi->nodes))
+    node->score = 0;
+
+ /*
+  * Find the first node to search in...
+  */
 
   if (filename)
   {
-    n = helpFindNode(hi, filename, NULL);
-    if (!n)
+    node = helpFindNode(hi, filename, NULL);
+    if (!node)
       return (NULL);
   }
   else
-    n = hi->nodes;
+    node = (help_node_t *)cupsArrayFirst(hi->nodes);
 
  /*
   * Convert the query into a regular expression...
@@ -457,6 +597,18 @@ helpSearchIndex(help_index_t *hi,  /* I - Index */
     return (NULL);
   }
 
+  search->nodes  = cupsArrayNew((cups_array_func_t)help_sort_by_name, NULL);
+  search->sorted = cupsArrayNew((cups_array_func_t)help_sort_by_score, NULL);
+  
+  if (!search->nodes || !search->sorted)
+  {
+    cupsArrayDelete(search->nodes);
+    cupsArrayDelete(search->sorted);
+    free(search);
+    cgiFreeSearch(sc);
+    return (NULL);
+  }
+
   search->search = 1;
 
  /*
@@ -464,20 +616,32 @@ helpSearchIndex(help_index_t *hi, /* I - Index */
   * search index...
   */
 
-  for (i = n - hi->nodes; i < hi->num_nodes; i ++, n ++)
-    if (section && strcmp(n[0]->section, section))
+  for (; node; node = (help_node_t *)cupsArrayNext(hi->nodes))
+    if (section && strcmp(node->section, section))
       continue;
-    else if (filename && strcmp(n[0]->filename, filename))
+    else if (filename && strcmp(node->filename, filename))
       continue;
-    else if ((matches = cgiDoSearch(sc, n[0]->text)) > 0)
+    else
     {
-     /*
-      * Found a match, add the node to the search index...
-      */
+      matches = cgiDoSearch(sc, node->text);
 
-      help_insert_node(search, *n);
+      for (word = (help_word_t *)cupsArrayFirst(node->words);
+           word;
+          word = (help_word_t *)cupsArrayNext(node->words))
+        if (cgiDoSearch(sc, word->text) > 0)
+          matches += word->count;
 
-      n[0]->score = matches;
+      if (matches > 0)
+      {
+       /*
+       * Found a match, add the node to the search index...
+       */
+
+       node->score = matches;
+
+       cupsArrayAdd(search->nodes, node);      
+       cupsArrayAdd(search->sorted, node);      
+      }
     }
 
  /*
@@ -486,12 +650,6 @@ helpSearchIndex(help_index_t *hi,  /* I - Index */
 
   cgiFreeSearch(sc);
 
- /*
-  * Sort the results...
-  */
-
-  help_create_sorted(search);
-
  /*
   * Return the results...
   */
@@ -501,43 +659,57 @@ helpSearchIndex(help_index_t *hi, /* I - Index */
 
 
 /*
- * 'help_create_sorted()' - Create the sorted node array.
+ * 'help_add_word()' - Add a word to a node.
  */
 
-static void
-help_create_sorted(help_index_t *hi)   /* I - Index */
+static help_word_t *                   /* O - New word */
+help_add_word(help_node_t *n,          /* I - Node */
+              const char  *text)       /* I - Word text */
 {
-  DEBUG_printf(("help_create_sorted(hi=%p)\n", hi));
+  help_word_t  *w,                     /* New word */
+               key;                    /* Search key */
+
+
+  DEBUG_printf(("help_add_word(n=%p, text=\"%s\")\n", n, text));
 
  /*
-  * Free any existing sorted array...
+  * Create the words array as needed...
   */
 
-  if (hi->sorted)
-    free(hi->sorted);
+  if (!n->words)
+    n->words = cupsArrayNew((cups_array_func_t)help_sort_words, NULL);
 
  /*
-  * Create a new sorted array...
+  * See if the word is already added...
   */
 
-  hi->sorted = calloc(hi->num_nodes, sizeof(help_node_t *));
+  key.text = (char *)text;
 
-  if (!hi->sorted)
-    return;
+  if ((w = (help_word_t *)cupsArrayFind(n->words, &key)) == NULL)
+  {
+   /*
+    * Create a new word...
+    */
 
- /*
-  * Copy the nodes to the new array...
-  */
+    if ((w = calloc(1, sizeof(help_word_t))) == NULL)
+      return (NULL);
 
-  memcpy(hi->sorted, hi->nodes, hi->num_nodes * sizeof(help_node_t *));
+    if ((w->text = strdup(text)) == NULL)
+    {
+      free(w);
+      return (NULL);
+    }
+
+    cupsArrayAdd(n->words, w);
+  }
 
  /*
-  * Sort the new array by score and text.
+  * Bump the counter for this word and return it...
   */
 
-  if (hi->num_nodes > 1)
-    qsort(hi->sorted, hi->num_nodes, sizeof(help_node_t *),
-          help_sort_by_score);
+  w->count ++;
+
+  return (w);
 }
 
 
@@ -548,6 +720,9 @@ help_create_sorted(help_index_t *hi)        /* I - Index */
 static void
 help_delete_node(help_node_t *n)       /* I - Node */
 {
+  help_word_t  *w;                     /* Current word */
+
+
   DEBUG_printf(("help_delete_node(n=%p)\n", n));
 
   if (!n)
@@ -565,105 +740,33 @@ help_delete_node(help_node_t *n) /* I - Node */
   if (n->text)
     free(n->text);
 
+  for (w = (help_word_t *)cupsArrayFirst(n->words);
+       w;
+       w = (help_word_t *)cupsArrayNext(n->words))
+    help_delete_word(w);
+
+  cupsArrayDelete(n->words);
+
   free(n);
 }
 
 
 /*
- * 'help_insert_node()' - Insert a node in an index.
+ * 'help_delete_word()' - Free all memory used by a word.
  */
 
 static void
-help_insert_node(help_index_t *hi,     /* I - Index */
-                 help_node_t  *n)      /* I - Node */
+help_delete_word(help_word_t *w)       /* I - Word */
 {
-  int          current,                /* Current node */
-               left,                   /* Left side */
-               right,                  /* Right side */
-               diff;                   /* Difference between nodes */
-  help_node_t  **temp;                 /* Temporary node pointer */
-
-
-  DEBUG_printf(("help_insert_node(hi=%p, n=%p)\n", hi, n));
-
- /*
-  * Allocate memory as needed...
-  */
-
-  if (hi->num_nodes >= hi->alloc_nodes)
-  {
-   /*
-    * Expand the array in 128 node increments...
-    */
-
-    hi->alloc_nodes += 128;
-    if (hi->alloc_nodes == 128)
-      temp = (help_node_t **)malloc(hi->alloc_nodes * sizeof(help_node_t *));
-    else
-      temp = (help_node_t **)realloc(hi->nodes,
-                                     hi->alloc_nodes * sizeof(help_node_t *));
-
-    if (!temp)
-      return;
+  DEBUG_printf(("help_delete_word(w=%p)\n", w));
 
-    hi->nodes = temp;
-  }
-
- /*
-  * Find the insertion point...
-  */
-
-  if (hi->num_nodes == 0 ||
-      help_sort_by_name(&n, hi->nodes + hi->num_nodes - 1) > 0)
-  {
-   /*
-    * Last node...
-    */
-
-    hi->nodes[hi->num_nodes] = n;
-    hi->num_nodes ++;
+  if (!w)
     return;
-  }
-  else if (help_sort_by_name(&n, hi->nodes) < 0)
-  {
-   /*
-    * First node...
-    */
 
-    memmove(hi->nodes + 1, hi->nodes, hi->num_nodes * sizeof(help_node_t *));
-    hi->nodes[0] = n;
-    hi->num_nodes ++;
-    return;
-  }
+  if (w->text)
+    free(w->text);
 
- /*
-  * Otherwise, do a binary insertion...
-  */
-
-  left    = 0;
-  right   = hi->num_nodes - 1;
-
-  do
-  {
-    current = (left + right) / 2;
-    diff    = help_sort_by_name(&n, hi->nodes + current);
-
-    if (!diff)
-      break;
-    else if (diff < 0)
-      right = current;
-    else
-      left = current;
-  }
-  while ((right - left) > 1);
-
-  if (diff > 0)
-    current ++;
-
-  memmove(hi->nodes + current + 1, hi->nodes + current,
-          (hi->num_nodes - current) * sizeof(help_node_t *));
-  hi->nodes[current] = n;
-  hi->num_nodes ++;
+  free(w);
 }
 
 
@@ -677,14 +780,13 @@ help_load_directory(
     const char   *directory,           /* I - Directory */
     const char   *relative)            /* I - Relative path */
 {
-  int          i;                      /* Looping var */
   cups_dir_t   *dir;                   /* Directory file */
   cups_dentry_t        *dent;                  /* Directory entry */
   char         *ext,                   /* Pointer to extension */
                filename[1024],         /* Full filename */
                relname[1024];          /* Relative filename */
   int          update;                 /* Updated? */
-  help_node_t  **node;                 /* Current node */
+  help_node_t  *node;                  /* Current node */
 
 
   DEBUG_printf(("help_load_directory(hi=%p, directory=\"%s\", relative=\"%s\")\n",
@@ -701,6 +803,13 @@ help_load_directory(
 
   while ((dent = cupsDirRead(dir)) != NULL)
   {
+   /*
+    * Skip "." files...
+    */
+
+    if (dent->filename[0] == '.')
+      continue;
+
    /*
     * Get absolute and relative filenames...
     */
@@ -729,16 +838,16 @@ help_load_directory(
        * index is up-to-date...
        */
 
-        if (node[0]->mtime == dent->fileinfo.st_mtime)
+        if (node->mtime == dent->fileinfo.st_mtime)
        {
         /*
          * Same modification time, so mark all of the nodes
          * for this file as up-to-date...
          */
 
-          for (i = node - hi->nodes; i < hi->num_nodes; i ++, node ++)
-           if (!strcmp(node[0]->filename, relname))
-             node[0]->score = 0;
+          for (; node; node = (help_node_t *)cupsArrayNext(hi->nodes))
+           if (!strcmp(node->filename, relname))
+             node->score = 0;
            else
              break;
 
@@ -779,15 +888,17 @@ help_load_file(
     time_t       mtime)                        /* I - Modification time */
 {
   cups_file_t  *fp;                    /* HTML file */
-  help_node_t  *node,                  /* Current node */
-               **n;                    /* Node pointer */
+  help_node_t  *node;                  /* Current node */
   char         line[1024],             /* Line from file */
+               temp[1024],             /* Temporary word */
                 section[1024],         /* Section */
                *ptr,                   /* Pointer into line */
                *anchor,                /* Anchor name */
                *text;                  /* Text for anchor */
   off_t                offset;                 /* File offset */
   char         quote;                  /* Quote character */
+  help_word_t  *word;                  /* Current word */
+  int          wordlen;                /* Length of word */
 
 
   DEBUG_printf(("help_load_file(hi=%p, filename=\"%s\", relative=\"%s\", mtime=%ld)\n",
@@ -924,14 +1035,14 @@ help_load_file(
         break;
       }
 
-      if ((n = helpFindNode(hi, relative, anchor)) != NULL)
+      if ((node = helpFindNode(hi, relative, anchor)) != NULL)
       {
        /*
        * Node already in the index, so replace the text and other
        * data...
        */
 
-        node = n[0];
+        cupsArrayRemove(hi->nodes, node);
 
         if (node->section)
          free(node->section);
@@ -939,6 +1050,17 @@ help_load_file(
        if (node->text)
          free(node->text);
 
+        if (node->words)
+       {
+         for (word = (help_word_t *)cupsArrayFirst(node->words);
+              word;
+              word = (help_word_t *)cupsArrayNext(node->words))
+           help_delete_word(word);
+
+         cupsArrayDelete(node->words);
+         node->words = NULL;
+       }
+
        node->section = section[0] ? strdup(section) : NULL;
        node->text    = strdup(text);
        node->mtime   = mtime;
@@ -952,7 +1074,6 @@ help_load_file(
        */
 
         node = help_new_node(relative, anchor, section, text, mtime, offset, 0);
-       help_insert_node(hi, node);
       }
 
      /*
@@ -964,7 +1085,7 @@ help_load_file(
        if (isspace(*ptr & 255))
        {
          while (isspace(*ptr & 255))
-           *ptr ++;
+           ptr ++;
 
          *text++ = ' ';
         }
@@ -978,9 +1099,104 @@ help_load_file(
 
       *text = '\0';
 
+     /*
+      * (Re)add the node to the array...
+      */
+
+      cupsArrayAdd(hi->nodes, node);
+
+      if (!anchor)
+        node = NULL;
       break;
     }
 
+    if (node)
+    {
+     /*
+      * Scan this line for words...
+      */
+
+      for (ptr = line; *ptr; ptr ++)
+      {
+       /*
+       * Skip HTML stuff...
+       */
+
+       if (*ptr == '<')
+       {
+          if (!strncmp(ptr, "<!--", 4))
+         {
+          /*
+           * Skip HTML comment...
+           */
+
+            if ((text = strstr(ptr + 4, "-->")) == NULL)
+             ptr += strlen(ptr) - 1;
+           else
+             ptr = text + 2;
+         }
+         else
+         {
+          /*
+            * Skip HTML element...
+           */
+
+            for (ptr ++; *ptr && *ptr != '>'; ptr ++)
+           {
+             if (*ptr == '\"' || *ptr == '\'')
+             {
+               for (quote = *ptr++; *ptr && *ptr != quote; ptr ++);
+
+               if (!*ptr)
+                 ptr --;
+             }
+           }
+
+           if (!*ptr)
+             ptr --;
+          }
+
+          continue;
+       }
+       else if (*ptr == '&')
+       {
+        /*
+         * Skip HTML entity...
+         */
+
+         for (ptr ++; *ptr && *ptr != ';'; ptr ++);
+
+         if (!*ptr)
+           ptr --;
+
+         continue;
+       }
+       else if (!isalnum(*ptr & 255))
+          continue;
+
+       /*
+       * Found the start of a word, search until we find the end...
+       */
+
+       for (text = ptr, ptr ++; *ptr && isalnum(*ptr & 255); ptr ++);
+
+       wordlen = ptr - text;
+
+        memcpy(temp, text, wordlen);
+       temp[wordlen] = '\0';
+
+        ptr --;
+
+       if (wordlen > 1 && !bsearch(temp, help_common_words,
+                                   (sizeof(help_common_words) /
+                                    sizeof(help_common_words[0])),
+                                   sizeof(help_common_words[0]),
+                                   (int (*)(const void *, const void *))
+                                       strcasecmp))
+          help_add_word(node, temp);
+      }
+    }
+
    /*
     * Get the offset of the next line...
     */
@@ -1013,9 +1229,11 @@ help_new_node(const char   *filename,    /* I - Filename */
   help_node_t  *n;                     /* Node */
 
 
-  DEBUG_printf(("help_new_node(filename=\"%s\", anchor=\"%s\", text=\"%s\", mtime=%ld, offset=%ld, length=%ld)\n",
+  DEBUG_printf(("help_new_node(filename=\"%s\", anchor=\"%s\", text=\"%s\", "
+                "mtime=%ld, offset=%ld, length=%ld)\n",
                 filename ? filename : "(nil)", anchor ? anchor : "(nil)",
-               text ? text : "(nil)", mtime, offset, length));
+               text ? text : "(nil)", (long)mtime, (long)offset,
+               (long)length));
 
   n = (help_node_t *)calloc(1, sizeof(help_node_t));
   if (!n)
@@ -1038,30 +1256,27 @@ help_new_node(const char   *filename,   /* I - Filename */
  */
 
 static int                             /* O - Difference */
-help_sort_by_name(const void *p1,      /* I - First node */
-                  const void *p2)      /* I - Second node */
+help_sort_by_name(help_node_t *n1,     /* I - First node */
+                  help_node_t *n2)     /* I - Second node */
 {
-  help_node_t  **n1,                   /* First node */
-               **n2;                   /* Second node */
   int          diff;                   /* Difference */
 
 
-  DEBUG_printf(("help_sort_by_name(p1=%p, p2=%p)\n", p1, p2));
-
-  n1 = (help_node_t **)p1;
-  n2 = (help_node_t **)p2;
+  DEBUG_printf(("help_sort_by_name(n1=%p(%s#%s), n2=%p(%s#%s)\n",
+                n1, n1->filename, n1->anchor ? n1->anchor : "",
+               n2, n2->filename, n2->anchor ? n2->anchor : ""));
 
-  if ((diff = strcmp(n1[0]->filename, n2[0]->filename)) != 0)
+  if ((diff = strcmp(n1->filename, n2->filename)) != 0)
     return (diff);
 
-  if (!n1[0]->anchor && !n2[0]->anchor)
+  if (!n1->anchor && !n2->anchor)
     return (0);
-  else if (!n1[0]->anchor)
+  else if (!n1->anchor)
     return (-1);
-  else if (!n2[0]->anchor)
+  else if (!n2->anchor)
     return (1);
   else
-    return (strcmp(n1[0]->anchor, n2[0]->anchor));
+    return (strcmp(n1->anchor, n2->anchor));
 }
 
 
@@ -1070,34 +1285,47 @@ help_sort_by_name(const void *p1,       /* I - First node */
  */
 
 static int                             /* O - Difference */
-help_sort_by_score(const void *p1,     /* I - First node */
-                   const void *p2)     /* I - Second node */
+help_sort_by_score(help_node_t *n1,    /* I - First node */
+                   help_node_t *n2)    /* I - Second node */
 {
-  help_node_t  **n1,                   /* First node */
-               **n2;                   /* Second node */
   int          diff;                   /* Difference */
 
 
-  DEBUG_printf(("help_sort_by_score(p1=%p, p2=%p)\n", p1, p2));
+  DEBUG_printf(("help_sort_by_score(n1=%p(%d \"%s\" \"%s\"), "
+                "n2=%p(%d \"%s\" \"%s\")\n",
+                n1, n1->score, n1->section ? n1->section : "", n1->text,
+                n2, n2->score, n2->section ? n2->section : "", n2->text));
 
-  n1 = (help_node_t **)p1;
-  n2 = (help_node_t **)p2;
+  if (n1->score != n2->score)
+    return (n2->score - n1->score);
 
-  if (n1[0]->score != n2[0]->score)
-    return (n1[0]->score - n2[0]->score);
-
-  if (n1[0]->section && !n2[0]->section)
+  if (n1->section && !n2->section)
     return (1);
-  else if (!n1[0]->section && n2[0]->section)
+  else if (!n1->section && n2->section)
     return (-1);
-  else if (n1[0]->section && n2[0]->section &&
-           (diff = strcmp(n1[0]->section, n2[0]->section)) != 0)
+  else if (n1->section && n2->section &&
+           (diff = strcmp(n1->section, n2->section)) != 0)
     return (diff);
 
-  return (strcasecmp(n1[0]->text, n2[0]->text));
+  return (strcasecmp(n1->text, n2->text));
+}
+
+
+/*
+ * 'help_sort_words()' - Sort words alphabetically.
+ */
+
+static int                             /* O - Difference */
+help_sort_words(help_word_t *w1,       /* I - Second word */
+                help_word_t *w2)       /* I - Second word */
+{
+  DEBUG_printf(("help_sort_words(w1=%p(\"%s\"), w2=%p(\"%s\"))\n",
+                w1, w1->text, w2, w2->text));
+
+  return (strcasecmp(w1->text, w2->text));
 }
 
 
 /*
- * End of "$Id: help-index.c 4863 2005-12-03 04:28:10Z mike $".
+ * End of "$Id: help-index.c 7717 2008-07-04 02:35:33Z mike $".
  */