]>
git.ipfire.org Git - thirdparty/cups.git/blob - cgi-bin/help-index.c
2 * "$Id: help-index.c 4863 2005-12-03 04:28:10Z mike $"
4 * On-line help index routines for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2005 by Easy Software Products.
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
26 * helpDeleteIndex() - Delete an index, freeing all memory used.
27 * helpFindNode() - Find a node in an index.
28 * helpLoadIndex() - Load a help index from disk.
29 * helpSaveIndex() - Save a help index to disk.
30 * helpSearchIndex() - Search an index.
31 * help_compile_search() - Convert a search string into a regular expression.
32 * help_create_sorted() - Create the sorted node array.
33 * help_delete_node() - Free all memory used by a node.
34 * help_insert_node() - Insert a node in an index.
35 * help_load_directory() - Load a directory of files into an index.
36 * help_load_file() - Load a HTML files into an index.
37 * help_new_node() - Create a new node and add it to an index.
38 * help_sort_nodes_by_name() - Sort nodes by section, filename, and anchor.
39 * help_sort_nodes_by_score() - Sort nodes by score and text.
43 * Include necessary headers...
46 #include "cgi-private.h"
54 static void help_create_sorted(help_index_t
*hi
);
55 static void help_delete_node(help_node_t
*n
);
56 static void help_insert_node(help_index_t
*hi
, help_node_t
*n
);
57 static int help_load_directory(help_index_t
*hi
,
58 const char *directory
,
59 const char *relative
);
60 static int help_load_file(help_index_t
*hi
,
64 static help_node_t
*help_new_node(const char *filename
, const char *anchor
,
65 const char *section
, const char *text
,
66 time_t mtime
, off_t offset
,
68 static int help_sort_by_name(const void *p1
, const void *p2
);
69 static int help_sort_by_score(const void *p1
, const void *p2
);
73 * 'helpDeleteIndex()' - Delete an index, freeing all memory used.
77 helpDeleteIndex(help_index_t
*hi
)
79 int i
; /* Looping var */
82 DEBUG_printf(("helpDeleteIndex(hi=%p)\n", hi
));
89 for (i
= 0; i
< hi
->num_nodes
; i
++)
90 help_delete_node(hi
->nodes
[i
]);
103 * 'helpFindNode()' - Find a node in an index.
106 help_node_t
** /* O - Node pointer or NULL */
107 helpFindNode(help_index_t
*hi
, /* I - Index */
108 const char *filename
, /* I - Filename */
109 const char *anchor
) /* I - Anchor */
111 help_node_t key
, /* Search key */
112 *ptr
; /* Pointer to key */
115 DEBUG_printf(("helpFindNode(hi=%p, filename=\"%s\", anchor=\"%s\")\n",
116 hi
, filename
? filename
: "(nil)", anchor
? anchor
: "(nil)"));
119 * Range check input...
122 if (!hi
|| !filename
)
126 * Initialize the search key...
129 key
.filename
= (char *)filename
;
130 key
.anchor
= (char *)anchor
;
134 * Return any match...
137 return ((help_node_t
**)bsearch(&ptr
, hi
->nodes
, hi
->num_nodes
,
138 sizeof(help_node_t
*), help_sort_by_name
));
143 * 'helpLoadIndex()' - Load a help index from disk.
146 help_index_t
* /* O - Index pointer or NULL */
147 helpLoadIndex(const char *hifile
, /* I - Index filename */
148 const char *directory
) /* I - Directory that is indexed */
150 help_index_t
*hi
; /* Help index */
151 cups_file_t
*fp
; /* Current file */
152 char line
[2048], /* Line from file */
153 *ptr
, /* Pointer into line */
154 *filename
, /* Filename in line */
155 *anchor
, /* Anchor in line */
156 *sectptr
, /* Section pointer in line */
157 section
[1024], /* Section name */
158 *text
; /* Text in line */
159 time_t mtime
; /* Modification time */
160 off_t offset
; /* Offset into file */
161 size_t length
; /* Length in bytes */
162 int update
; /* Update? */
163 int i
; /* Looping var */
164 help_node_t
*node
; /* Current node */
167 DEBUG_printf(("helpLoadIndex(hifile=\"%s\", directory=\"%s\")\n",
171 * Create a new, empty index.
174 hi
= (help_index_t
*)calloc(1, sizeof(help_index_t
));
177 * Try loading the existing index file...
180 if ((fp
= cupsFileOpen(hifile
, "r")) != NULL
)
183 * Lock the file and then read the first line...
188 if (cupsFileGets(fp
, line
, sizeof(line
)) && !strcmp(line
, "HELPV1"))
191 * Got a valid header line, now read the data lines...
194 while (cupsFileGets(fp
, line
, sizeof(line
)))
197 * Each line looks like one of the following:
199 * filename mtime offset length "section" "text"
200 * filename#anchor offset length "text"
205 if ((ptr
= strchr(line
, ' ')) == NULL
)
208 while (isspace(*ptr
& 255))
211 if ((anchor
= strrchr(filename
, '#')) != NULL
)
217 mtime
= strtol(ptr
, &ptr
, 10);
219 offset
= strtoll(ptr
, &ptr
, 10);
220 length
= strtoll(ptr
, &ptr
, 10);
222 while (isspace(*ptr
& 255))
237 while (*ptr
&& *ptr
!= '\"')
245 strlcpy(section
, sectptr
, sizeof(section
));
247 while (isspace(*ptr
& 255))
257 while (*ptr
&& *ptr
!= '\"')
265 if ((node
= help_new_node(filename
, anchor
, section
, text
,
266 mtime
, offset
, length
)) == NULL
)
269 help_insert_node(hi
, node
);
279 * Scan for new/updated files...
282 update
= help_load_directory(hi
, directory
, NULL
);
285 * Remove any files that are no longer installed...
288 for (i
= 0; i
< hi
->num_nodes
;)
290 if (hi
->nodes
[i
]->score
< 0)
293 * Delete this node...
296 help_delete_node(hi
->nodes
[i
]);
299 if (i
< hi
->num_nodes
)
300 memmove(hi
->nodes
+ i
, hi
->nodes
+ i
+ 1,
301 (hi
->num_nodes
- i
) * sizeof(help_node_t
*));
316 * Save the index if we updated it...
320 helpSaveIndex(hi
, hifile
);
323 * Create the sorted array...
326 help_create_sorted(hi
);
329 * Return the index...
337 * 'helpSaveIndex()' - Save a help index to disk.
340 int /* O - 0 on success, -1 on error */
341 helpSaveIndex(help_index_t
*hi
, /* I - Index */
342 const char *hifile
) /* I - Index filename */
344 cups_file_t
*fp
; /* Index file */
345 int i
; /* Looping var */
346 help_node_t
*node
; /* Current node */
349 DEBUG_printf(("helpSaveIndex(hi=%p, hifile=\"%s\")\n", hi
, hifile
));
352 * Try creating a new index file...
355 if ((fp
= cupsFileOpen(hifile
, "w9")) == NULL
)
359 * Lock the file while we write it...
364 cupsFilePuts(fp
, "HELPV1\n");
366 for (i
= 0; i
< hi
->num_nodes
; i
++)
369 * Write the current node with/without the anchor...
376 if (cupsFilePrintf(fp
, "%s#%s " CUPS_LLFMT
" " CUPS_LLFMT
" \"%s\"\n",
377 node
->filename
, node
->anchor
,
378 CUPS_LLCAST node
->offset
, CUPS_LLCAST node
->length
,
384 if (cupsFilePrintf(fp
, "%s %d " CUPS_LLFMT
" " CUPS_LLFMT
" \"%s\" \"%s\"\n",
385 node
->filename
, node
->mtime
,
386 CUPS_LLCAST node
->offset
, CUPS_LLCAST node
->length
,
387 node
->section
? node
->section
: "", node
->text
) < 0)
392 if (cupsFileClose(fp
) < 0)
394 else if (i
< hi
->num_nodes
)
402 * 'helpSearchIndex()' - Search an index.
405 help_index_t
* /* O - Search index */
406 helpSearchIndex(help_index_t
*hi
, /* I - Index */
407 const char *query
, /* I - Query string */
408 const char *section
, /* I - Limit search to this section */
409 const char *filename
) /* I - Limit search to this file */
411 int i
; /* Looping var */
412 help_index_t
*search
; /* Search index */
413 help_node_t
**n
; /* Current node */
414 void *sc
; /* Search context */
415 int matches
; /* Number of matches */
418 DEBUG_printf(("helpSearchIndex(hi=%p, query=\"%s\", filename=\"%s\")\n",
419 hi
, query
? query
: "(nil)",
420 filename
? filename
: "(nil)"));
429 for (i
= 0, n
= hi
->nodes
; i
< hi
->num_nodes
; i
++, n
++)
434 n
= helpFindNode(hi
, filename
, NULL
);
442 * Convert the query into a regular expression...
445 sc
= cgiCompileSearch(query
);
450 * Allocate a search index...
453 search
= calloc(1, sizeof(help_index_t
));
463 * Check each node in the index, adding matching nodes to the
467 for (i
= n
- hi
->nodes
; i
< hi
->num_nodes
; i
++, n
++)
468 if (section
&& strcmp(n
[0]->section
, section
))
470 else if (filename
&& strcmp(n
[0]->filename
, filename
))
472 else if ((matches
= cgiDoSearch(sc
, n
[0]->text
)) > 0)
475 * Found a match, add the node to the search index...
478 help_insert_node(search
, *n
);
480 n
[0]->score
= matches
;
484 * Free the search context...
490 * Sort the results...
493 help_create_sorted(search
);
496 * Return the results...
504 * 'help_create_sorted()' - Create the sorted node array.
508 help_create_sorted(help_index_t
*hi
) /* I - Index */
510 DEBUG_printf(("help_create_sorted(hi=%p)\n", hi
));
513 * Free any existing sorted array...
520 * Create a new sorted array...
523 hi
->sorted
= calloc(hi
->num_nodes
, sizeof(help_node_t
*));
529 * Copy the nodes to the new array...
532 memcpy(hi
->sorted
, hi
->nodes
, hi
->num_nodes
* sizeof(help_node_t
*));
535 * Sort the new array by score and text.
538 if (hi
->num_nodes
> 1)
539 qsort(hi
->sorted
, hi
->num_nodes
, sizeof(help_node_t
*),
545 * 'help_delete_node()' - Free all memory used by a node.
549 help_delete_node(help_node_t
*n
) /* I - Node */
551 DEBUG_printf(("help_delete_node(n=%p)\n", n
));
573 * 'help_insert_node()' - Insert a node in an index.
577 help_insert_node(help_index_t
*hi
, /* I - Index */
578 help_node_t
*n
) /* I - Node */
580 int current
, /* Current node */
581 left
, /* Left side */
582 right
, /* Right side */
583 diff
; /* Difference between nodes */
584 help_node_t
**temp
; /* Temporary node pointer */
587 DEBUG_printf(("help_insert_node(hi=%p, n=%p)\n", hi
, n
));
590 * Allocate memory as needed...
593 if (hi
->num_nodes
>= hi
->alloc_nodes
)
596 * Expand the array in 128 node increments...
599 hi
->alloc_nodes
+= 128;
600 if (hi
->alloc_nodes
== 128)
601 temp
= (help_node_t
**)malloc(hi
->alloc_nodes
* sizeof(help_node_t
*));
603 temp
= (help_node_t
**)realloc(hi
->nodes
,
604 hi
->alloc_nodes
* sizeof(help_node_t
*));
613 * Find the insertion point...
616 if (hi
->num_nodes
== 0 ||
617 help_sort_by_name(&n
, hi
->nodes
+ hi
->num_nodes
- 1) > 0)
623 hi
->nodes
[hi
->num_nodes
] = n
;
627 else if (help_sort_by_name(&n
, hi
->nodes
) < 0)
633 memmove(hi
->nodes
+ 1, hi
->nodes
, hi
->num_nodes
* sizeof(help_node_t
*));
640 * Otherwise, do a binary insertion...
644 right
= hi
->num_nodes
- 1;
648 current
= (left
+ right
) / 2;
649 diff
= help_sort_by_name(&n
, hi
->nodes
+ current
);
658 while ((right
- left
) > 1);
663 memmove(hi
->nodes
+ current
+ 1, hi
->nodes
+ current
,
664 (hi
->num_nodes
- current
) * sizeof(help_node_t
*));
665 hi
->nodes
[current
] = n
;
671 * 'help_load_directory()' - Load a directory of files into an index.
674 static int /* O - 0 = success, -1 = error, 1 = updated */
676 help_index_t
*hi
, /* I - Index */
677 const char *directory
, /* I - Directory */
678 const char *relative
) /* I - Relative path */
680 int i
; /* Looping var */
681 cups_dir_t
*dir
; /* Directory file */
682 cups_dentry_t
*dent
; /* Directory entry */
683 char *ext
, /* Pointer to extension */
684 filename
[1024], /* Full filename */
685 relname
[1024]; /* Relative filename */
686 int update
; /* Updated? */
687 help_node_t
**node
; /* Current node */
690 DEBUG_printf(("help_load_directory(hi=%p, directory=\"%s\", relative=\"%s\")\n",
691 hi
, directory
? directory
: "(nil)", relative
? relative
: "(nil)"));
694 * Open the directory and scan it...
697 if ((dir
= cupsDirOpen(directory
)) == NULL
)
702 while ((dent
= cupsDirRead(dir
)) != NULL
)
705 * Get absolute and relative filenames...
708 snprintf(filename
, sizeof(filename
), "%s/%s", directory
, dent
->filename
);
710 snprintf(relname
, sizeof(relname
), "%s/%s", relative
, dent
->filename
);
712 strlcpy(relname
, dent
->filename
, sizeof(relname
));
715 * Check if we have a HTML file...
718 if ((ext
= strstr(dent
->filename
, ".html")) != NULL
&&
719 (!ext
[5] || !strcmp(ext
+ 5, ".gz")))
722 * HTML file, see if we have already indexed the file...
725 if ((node
= helpFindNode(hi
, relname
, NULL
)) != NULL
)
728 * File already indexed - check dates to confirm that the
729 * index is up-to-date...
732 if (node
[0]->mtime
== dent
->fileinfo
.st_mtime
)
735 * Same modification time, so mark all of the nodes
736 * for this file as up-to-date...
739 for (i
= node
- hi
->nodes
; i
< hi
->num_nodes
; i
++, node
++)
740 if (!strcmp(node
[0]->filename
, relname
))
751 help_load_file(hi
, filename
, relname
, dent
->fileinfo
.st_mtime
);
753 else if (S_ISDIR(dent
->fileinfo
.st_mode
))
756 * Process sub-directory...
759 if (help_load_directory(hi
, filename
, relname
) == 1)
771 * 'help_load_file()' - Load a HTML files into an index.
774 static int /* O - 0 = success, -1 = error */
776 help_index_t
*hi
, /* I - Index */
777 const char *filename
, /* I - Filename */
778 const char *relative
, /* I - Relative path */
779 time_t mtime
) /* I - Modification time */
781 cups_file_t
*fp
; /* HTML file */
782 help_node_t
*node
, /* Current node */
783 **n
; /* Node pointer */
784 char line
[1024], /* Line from file */
785 section
[1024], /* Section */
786 *ptr
, /* Pointer into line */
787 *anchor
, /* Anchor name */
788 *text
; /* Text for anchor */
789 off_t offset
; /* File offset */
790 char quote
; /* Quote character */
793 DEBUG_printf(("help_load_file(hi=%p, filename=\"%s\", relative=\"%s\", mtime=%ld)\n",
794 hi
, filename
? filename
: "(nil)",
795 relative
? relative
: "(nil)", mtime
));
797 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
803 strcpy(section
, "Other");
805 while (cupsFileGets(fp
, line
, sizeof(line
)))
808 * Look for "<TITLE>", "<A NAME", or "<!-- SECTION:" prefix...
811 if (!strncasecmp(line
, "<!-- SECTION:", 13))
814 * Got section line, copy it!
817 for (ptr
= line
+ 13; isspace(*ptr
& 255); ptr
++);
819 strlcpy(section
, ptr
, sizeof(section
));
820 if ((ptr
= strstr(section
, "-->")) != NULL
)
823 * Strip comment stuff from end of line...
826 for (*ptr
-- = '\0'; ptr
> line
&& isspace(*ptr
& 255); *ptr
-- = '\0');
828 if (isspace(*ptr
& 255))
834 for (ptr
= line
; (ptr
= strchr(ptr
, '<')) != NULL
;)
838 if (!strncasecmp(ptr
, "TITLE>", 6))
847 else if (!strncasecmp(ptr
, "A NAME=", 7))
855 if (*ptr
== '\"' || *ptr
== '\'')
858 * Get quoted anchor...
863 if ((ptr
= strchr(anchor
, quote
)) != NULL
)
871 * Get unquoted anchor...
876 for (ptr
= anchor
; *ptr
&& *ptr
!= '>' && !isspace(*ptr
& 255); ptr
++);
885 * Got the anchor, now lets find the end...
888 while (*ptr
&& *ptr
!= '>')
900 * Now collect text for the link...
904 while ((ptr
= strchr(text
, '<')) == NULL
)
906 ptr
= text
+ strlen(text
);
907 if (ptr
>= (line
+ sizeof(line
) - 2))
912 if (!cupsFileGets(fp
, ptr
, sizeof(line
) - (ptr
- line
) - 1))
919 node
->length
= offset
- node
->offset
;
927 if ((n
= helpFindNode(hi
, relative
, anchor
)) != NULL
)
930 * Node already in the index, so replace the text and other
942 node
->section
= section
[0] ? strdup(section
) : NULL
;
943 node
->text
= strdup(text
);
945 node
->offset
= offset
;
954 node
= help_new_node(relative
, anchor
, section
, text
, mtime
, offset
, 0);
955 help_insert_node(hi
, node
);
959 * Go through the text value and replace tabs and newlines with
960 * whitespace and eliminate extra whitespace...
963 for (ptr
= node
->text
, text
= node
->text
; *ptr
;)
964 if (isspace(*ptr
& 255))
966 while (isspace(*ptr
& 255))
971 else if (text
!= ptr
)
985 * Get the offset of the next line...
988 offset
= cupsFileTell(fp
);
994 node
->length
= offset
- node
->offset
;
1001 * 'help_new_node()' - Create a new node and add it to an index.
1004 static help_node_t
* /* O - Node pointer or NULL on error */
1005 help_new_node(const char *filename
, /* I - Filename */
1006 const char *anchor
, /* I - Anchor */
1007 const char *section
, /* I - Section */
1008 const char *text
, /* I - Text */
1009 time_t mtime
, /* I - Modification time */
1010 off_t offset
, /* I - Offset in file */
1011 size_t length
) /* I - Length in bytes */
1013 help_node_t
*n
; /* Node */
1016 DEBUG_printf(("help_new_node(filename=\"%s\", anchor=\"%s\", text=\"%s\", mtime=%ld, offset=%ld, length=%ld)\n",
1017 filename
? filename
: "(nil)", anchor
? anchor
: "(nil)",
1018 text
? text
: "(nil)", mtime
, offset
, length
));
1020 n
= (help_node_t
*)calloc(1, sizeof(help_node_t
));
1024 n
->filename
= strdup(filename
);
1025 n
->anchor
= anchor
? strdup(anchor
) : NULL
;
1026 n
->section
= (section
&& *section
) ? strdup(section
) : NULL
;
1027 n
->text
= strdup(text
);
1037 * 'help_sort_nodes_by_name()' - Sort nodes by section, filename, and anchor.
1040 static int /* O - Difference */
1041 help_sort_by_name(const void *p1
, /* I - First node */
1042 const void *p2
) /* I - Second node */
1044 help_node_t
**n1
, /* First node */
1045 **n2
; /* Second node */
1046 int diff
; /* Difference */
1049 DEBUG_printf(("help_sort_by_name(p1=%p, p2=%p)\n", p1
, p2
));
1051 n1
= (help_node_t
**)p1
;
1052 n2
= (help_node_t
**)p2
;
1054 if ((diff
= strcmp(n1
[0]->filename
, n2
[0]->filename
)) != 0)
1057 if (!n1
[0]->anchor
&& !n2
[0]->anchor
)
1059 else if (!n1
[0]->anchor
)
1061 else if (!n2
[0]->anchor
)
1064 return (strcmp(n1
[0]->anchor
, n2
[0]->anchor
));
1069 * 'help_sort_nodes_by_score()' - Sort nodes by score and text.
1072 static int /* O - Difference */
1073 help_sort_by_score(const void *p1
, /* I - First node */
1074 const void *p2
) /* I - Second node */
1076 help_node_t
**n1
, /* First node */
1077 **n2
; /* Second node */
1078 int diff
; /* Difference */
1081 DEBUG_printf(("help_sort_by_score(p1=%p, p2=%p)\n", p1
, p2
));
1083 n1
= (help_node_t
**)p1
;
1084 n2
= (help_node_t
**)p2
;
1086 if (n1
[0]->score
!= n2
[0]->score
)
1087 return (n1
[0]->score
- n2
[0]->score
);
1089 if (n1
[0]->section
&& !n2
[0]->section
)
1091 else if (!n1
[0]->section
&& n2
[0]->section
)
1093 else if (n1
[0]->section
&& n2
[0]->section
&&
1094 (diff
= strcmp(n1
[0]->section
, n2
[0]->section
)) != 0)
1097 return (strcasecmp(n1
[0]->text
, n2
[0]->text
));
1102 * End of "$Id: help-index.c 4863 2005-12-03 04:28:10Z mike $".