]>
git.ipfire.org Git - thirdparty/cups.git/blob - cgi-bin/help-index.c
2 * "$Id: help-index.c 5665 2006-06-16 00:59:10Z mike $"
4 * On-line help index routines for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2006 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_delete_node() - Free all memory used by a node.
33 * help_load_directory() - Load a directory of files into an index.
34 * help_load_file() - Load a HTML files into an index.
35 * help_new_node() - Create a new node and add it to an index.
36 * help_sort_nodes_by_name() - Sort nodes by section, filename, and anchor.
37 * help_sort_nodes_by_score() - Sort nodes by score and text.
41 * Include necessary headers...
44 #include "cgi-private.h"
52 static void help_delete_node(help_node_t
*n
);
53 static int help_load_directory(help_index_t
*hi
,
54 const char *directory
,
55 const char *relative
);
56 static int help_load_file(help_index_t
*hi
,
60 static help_node_t
*help_new_node(const char *filename
, const char *anchor
,
61 const char *section
, const char *text
,
62 time_t mtime
, off_t offset
,
64 static int help_sort_by_name(help_node_t
*p1
, help_node_t
*p2
);
65 static int help_sort_by_score(help_node_t
*p1
, help_node_t
*p2
);
69 * 'helpDeleteIndex()' - Delete an index, freeing all memory used.
73 helpDeleteIndex(help_index_t
*hi
) /* I - Help index */
75 help_node_t
*node
; /* Current node */
78 DEBUG_printf(("helpDeleteIndex(hi=%p)\n", hi
));
83 for (node
= (help_node_t
*)cupsArrayFirst(hi
->nodes
);
85 node
= (help_node_t
*)cupsArrayNext(hi
->nodes
))
87 cupsArrayRemove(hi
->nodes
, node
);
88 cupsArrayRemove(hi
->sorted
, node
);
91 help_delete_node(node
);
94 cupsArrayDelete(hi
->nodes
);
95 cupsArrayDelete(hi
->sorted
);
102 * 'helpFindNode()' - Find a node in an index.
105 help_node_t
* /* O - Node pointer or NULL */
106 helpFindNode(help_index_t
*hi
, /* I - Index */
107 const char *filename
, /* I - Filename */
108 const char *anchor
) /* I - Anchor */
110 help_node_t key
; /* Search key */
113 DEBUG_printf(("helpFindNode(hi=%p, filename=\"%s\", anchor=\"%s\")\n",
114 hi
, filename
? filename
: "(nil)", anchor
? anchor
: "(nil)"));
117 * Range check input...
120 if (!hi
|| !filename
)
124 * Initialize the search key...
127 key
.filename
= (char *)filename
;
128 key
.anchor
= (char *)anchor
;
131 * Return any match...
134 return ((help_node_t
*)cupsArrayFind(hi
->nodes
, &key
));
139 * 'helpLoadIndex()' - Load a help index from disk.
142 help_index_t
* /* O - Index pointer or NULL */
143 helpLoadIndex(const char *hifile
, /* I - Index filename */
144 const char *directory
) /* I - Directory that is indexed */
146 help_index_t
*hi
; /* Help index */
147 cups_file_t
*fp
; /* Current file */
148 char line
[2048], /* Line from file */
149 *ptr
, /* Pointer into line */
150 *filename
, /* Filename in line */
151 *anchor
, /* Anchor in line */
152 *sectptr
, /* Section pointer in line */
153 section
[1024], /* Section name */
154 *text
; /* Text in line */
155 time_t mtime
; /* Modification time */
156 off_t offset
; /* Offset into file */
157 size_t length
; /* Length in bytes */
158 int update
; /* Update? */
159 help_node_t
*node
; /* Current node */
162 DEBUG_printf(("helpLoadIndex(hifile=\"%s\", directory=\"%s\")\n",
166 * Create a new, empty index.
169 if ((hi
= (help_index_t
*)calloc(1, sizeof(help_index_t
))) == NULL
)
172 hi
->nodes
= cupsArrayNew((cups_array_func_t
)help_sort_by_name
, NULL
);
173 hi
->sorted
= cupsArrayNew((cups_array_func_t
)help_sort_by_score
, NULL
);
175 if (!hi
->nodes
|| !hi
->sorted
)
177 cupsArrayDelete(hi
->nodes
);
178 cupsArrayDelete(hi
->sorted
);
184 * Try loading the existing index file...
187 if ((fp
= cupsFileOpen(hifile
, "r")) != NULL
)
190 * Lock the file and then read the first line...
195 if (cupsFileGets(fp
, line
, sizeof(line
)) && !strcmp(line
, "HELPV1"))
198 * Got a valid header line, now read the data lines...
201 while (cupsFileGets(fp
, line
, sizeof(line
)))
204 * Each line looks like one of the following:
206 * filename mtime offset length "section" "text"
207 * filename#anchor offset length "text"
212 if ((ptr
= strchr(line
, ' ')) == NULL
)
215 while (isspace(*ptr
& 255))
218 if ((anchor
= strrchr(filename
, '#')) != NULL
)
224 mtime
= strtol(ptr
, &ptr
, 10);
226 offset
= strtoll(ptr
, &ptr
, 10);
227 length
= strtoll(ptr
, &ptr
, 10);
229 while (isspace(*ptr
& 255))
244 while (*ptr
&& *ptr
!= '\"')
252 strlcpy(section
, sectptr
, sizeof(section
));
254 while (isspace(*ptr
& 255))
264 while (*ptr
&& *ptr
!= '\"')
272 if ((node
= help_new_node(filename
, anchor
, section
, text
,
273 mtime
, offset
, length
)) == NULL
)
278 cupsArrayAdd(hi
->nodes
, node
);
286 * Scan for new/updated files...
289 update
= help_load_directory(hi
, directory
, NULL
);
292 * Remove any files that are no longer installed...
295 for (node
= (help_node_t
*)cupsArrayFirst(hi
->nodes
);
297 node
= (help_node_t
*)cupsArrayNext(hi
->nodes
))
301 * Delete this node...
304 cupsArrayRemove(hi
->nodes
, node
);
305 help_delete_node(node
);
309 * Add nodes to the sorted array...
312 for (node
= (help_node_t
*)cupsArrayFirst(hi
->nodes
);
314 node
= (help_node_t
*)cupsArrayNext(hi
->nodes
))
315 cupsArrayAdd(hi
->sorted
, node
);
318 * Save the index if we updated it...
322 helpSaveIndex(hi
, hifile
);
325 * Return the index...
333 * 'helpSaveIndex()' - Save a help index to disk.
336 int /* O - 0 on success, -1 on error */
337 helpSaveIndex(help_index_t
*hi
, /* I - Index */
338 const char *hifile
) /* I - Index filename */
340 cups_file_t
*fp
; /* Index file */
341 help_node_t
*node
; /* Current node */
344 DEBUG_printf(("helpSaveIndex(hi=%p, hifile=\"%s\")\n", hi
, hifile
));
347 * Try creating a new index file...
350 if ((fp
= cupsFileOpen(hifile
, "w9")) == NULL
)
354 * Lock the file while we write it...
359 cupsFilePuts(fp
, "HELPV1\n");
361 for (node
= (help_node_t
*)cupsArrayFirst(hi
->nodes
);
363 node
= (help_node_t
*)cupsArrayNext(hi
->nodes
))
366 * Write the current node with/without the anchor...
371 if (cupsFilePrintf(fp
, "%s#%s " CUPS_LLFMT
" " CUPS_LLFMT
" \"%s\"\n",
372 node
->filename
, node
->anchor
,
373 CUPS_LLCAST node
->offset
, CUPS_LLCAST node
->length
,
379 if (cupsFilePrintf(fp
, "%s %d " CUPS_LLFMT
" " CUPS_LLFMT
" \"%s\" \"%s\"\n",
380 node
->filename
, node
->mtime
,
381 CUPS_LLCAST node
->offset
, CUPS_LLCAST node
->length
,
382 node
->section
? node
->section
: "", node
->text
) < 0)
389 if (cupsFileClose(fp
) < 0)
399 * 'helpSearchIndex()' - Search an index.
402 help_index_t
* /* O - Search index */
403 helpSearchIndex(help_index_t
*hi
, /* I - Index */
404 const char *query
, /* I - Query string */
405 const char *section
, /* I - Limit search to this section */
406 const char *filename
) /* I - Limit search to this file */
408 help_index_t
*search
; /* Search index */
409 help_node_t
*node
; /* Current node */
410 void *sc
; /* Search context */
411 int matches
; /* Number of matches */
414 DEBUG_printf(("helpSearchIndex(hi=%p, query=\"%s\", filename=\"%s\")\n",
415 hi
, query
? query
: "(nil)",
416 filename
? filename
: "(nil)"));
426 * Reset the scores of all nodes to 0...
429 for (node
= (help_node_t
*)cupsArrayFirst(hi
->nodes
);
431 node
= (help_node_t
*)cupsArrayNext(hi
->nodes
))
435 * Find the first node to search in...
440 node
= helpFindNode(hi
, filename
, NULL
);
445 node
= (help_node_t
*)cupsArrayFirst(hi
->nodes
);
448 * Convert the query into a regular expression...
451 sc
= cgiCompileSearch(query
);
456 * Allocate a search index...
459 search
= calloc(1, sizeof(help_index_t
));
466 search
->nodes
= cupsArrayNew((cups_array_func_t
)help_sort_by_name
, NULL
);
467 search
->sorted
= cupsArrayNew((cups_array_func_t
)help_sort_by_score
, NULL
);
469 if (!search
->nodes
|| !search
->sorted
)
471 cupsArrayDelete(search
->nodes
);
472 cupsArrayDelete(search
->sorted
);
481 * Check each node in the index, adding matching nodes to the
485 for (; node
; node
= (help_node_t
*)cupsArrayNext(hi
->nodes
))
486 if (section
&& strcmp(node
->section
, section
))
488 else if (filename
&& strcmp(node
->filename
, filename
))
490 else if ((matches
= cgiDoSearch(sc
, node
->text
)) > 0)
493 * Found a match, add the node to the search index...
496 node
->score
= matches
;
498 cupsArrayAdd(search
->nodes
, node
);
499 cupsArrayAdd(search
->sorted
, node
);
503 * Free the search context...
509 * Return the results...
517 * 'help_delete_node()' - Free all memory used by a node.
521 help_delete_node(help_node_t
*n
) /* I - Node */
523 DEBUG_printf(("help_delete_node(n=%p)\n", n
));
545 * 'help_load_directory()' - Load a directory of files into an index.
548 static int /* O - 0 = success, -1 = error, 1 = updated */
550 help_index_t
*hi
, /* I - Index */
551 const char *directory
, /* I - Directory */
552 const char *relative
) /* I - Relative path */
554 cups_dir_t
*dir
; /* Directory file */
555 cups_dentry_t
*dent
; /* Directory entry */
556 char *ext
, /* Pointer to extension */
557 filename
[1024], /* Full filename */
558 relname
[1024]; /* Relative filename */
559 int update
; /* Updated? */
560 help_node_t
*node
; /* Current node */
563 DEBUG_printf(("help_load_directory(hi=%p, directory=\"%s\", relative=\"%s\")\n",
564 hi
, directory
? directory
: "(nil)", relative
? relative
: "(nil)"));
567 * Open the directory and scan it...
570 if ((dir
= cupsDirOpen(directory
)) == NULL
)
575 while ((dent
= cupsDirRead(dir
)) != NULL
)
581 if (dent
->filename
[0] == '.')
585 * Get absolute and relative filenames...
588 snprintf(filename
, sizeof(filename
), "%s/%s", directory
, dent
->filename
);
590 snprintf(relname
, sizeof(relname
), "%s/%s", relative
, dent
->filename
);
592 strlcpy(relname
, dent
->filename
, sizeof(relname
));
595 * Check if we have a HTML file...
598 if ((ext
= strstr(dent
->filename
, ".html")) != NULL
&&
599 (!ext
[5] || !strcmp(ext
+ 5, ".gz")))
602 * HTML file, see if we have already indexed the file...
605 if ((node
= helpFindNode(hi
, relname
, NULL
)) != NULL
)
608 * File already indexed - check dates to confirm that the
609 * index is up-to-date...
612 if (node
->mtime
== dent
->fileinfo
.st_mtime
)
615 * Same modification time, so mark all of the nodes
616 * for this file as up-to-date...
619 for (; node
; node
= (help_node_t
*)cupsArrayNext(hi
->nodes
))
620 if (!strcmp(node
->filename
, relname
))
631 help_load_file(hi
, filename
, relname
, dent
->fileinfo
.st_mtime
);
633 else if (S_ISDIR(dent
->fileinfo
.st_mode
))
636 * Process sub-directory...
639 if (help_load_directory(hi
, filename
, relname
) == 1)
651 * 'help_load_file()' - Load a HTML files into an index.
654 static int /* O - 0 = success, -1 = error */
656 help_index_t
*hi
, /* I - Index */
657 const char *filename
, /* I - Filename */
658 const char *relative
, /* I - Relative path */
659 time_t mtime
) /* I - Modification time */
661 cups_file_t
*fp
; /* HTML file */
662 help_node_t
*node
; /* Current node */
663 char line
[1024], /* Line from file */
664 section
[1024], /* Section */
665 *ptr
, /* Pointer into line */
666 *anchor
, /* Anchor name */
667 *text
; /* Text for anchor */
668 off_t offset
; /* File offset */
669 char quote
; /* Quote character */
672 DEBUG_printf(("help_load_file(hi=%p, filename=\"%s\", relative=\"%s\", mtime=%ld)\n",
673 hi
, filename
? filename
: "(nil)",
674 relative
? relative
: "(nil)", mtime
));
676 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
682 strcpy(section
, "Other");
684 while (cupsFileGets(fp
, line
, sizeof(line
)))
687 * Look for "<TITLE>", "<A NAME", or "<!-- SECTION:" prefix...
690 if (!strncasecmp(line
, "<!-- SECTION:", 13))
693 * Got section line, copy it!
696 for (ptr
= line
+ 13; isspace(*ptr
& 255); ptr
++);
698 strlcpy(section
, ptr
, sizeof(section
));
699 if ((ptr
= strstr(section
, "-->")) != NULL
)
702 * Strip comment stuff from end of line...
705 for (*ptr
-- = '\0'; ptr
> line
&& isspace(*ptr
& 255); *ptr
-- = '\0');
707 if (isspace(*ptr
& 255))
713 for (ptr
= line
; (ptr
= strchr(ptr
, '<')) != NULL
;)
717 if (!strncasecmp(ptr
, "TITLE>", 6))
726 else if (!strncasecmp(ptr
, "A NAME=", 7))
734 if (*ptr
== '\"' || *ptr
== '\'')
737 * Get quoted anchor...
742 if ((ptr
= strchr(anchor
, quote
)) != NULL
)
750 * Get unquoted anchor...
755 for (ptr
= anchor
; *ptr
&& *ptr
!= '>' && !isspace(*ptr
& 255); ptr
++);
764 * Got the anchor, now lets find the end...
767 while (*ptr
&& *ptr
!= '>')
779 * Now collect text for the link...
783 while ((ptr
= strchr(text
, '<')) == NULL
)
785 ptr
= text
+ strlen(text
);
786 if (ptr
>= (line
+ sizeof(line
) - 2))
791 if (!cupsFileGets(fp
, ptr
, sizeof(line
) - (ptr
- line
) - 1))
798 node
->length
= offset
- node
->offset
;
806 if ((node
= helpFindNode(hi
, relative
, anchor
)) != NULL
)
809 * Node already in the index, so replace the text and other
813 cupsArrayRemove(hi
->nodes
, node
);
821 node
->section
= section
[0] ? strdup(section
) : NULL
;
822 node
->text
= strdup(text
);
824 node
->offset
= offset
;
833 node
= help_new_node(relative
, anchor
, section
, text
, mtime
, offset
, 0);
837 * Go through the text value and replace tabs and newlines with
838 * whitespace and eliminate extra whitespace...
841 for (ptr
= node
->text
, text
= node
->text
; *ptr
;)
842 if (isspace(*ptr
& 255))
844 while (isspace(*ptr
& 255))
849 else if (text
!= ptr
)
860 * (Re)add the node to the array...
863 cupsArrayAdd(hi
->nodes
, node
);
868 * Get the offset of the next line...
871 offset
= cupsFileTell(fp
);
877 node
->length
= offset
- node
->offset
;
884 * 'help_new_node()' - Create a new node and add it to an index.
887 static help_node_t
* /* O - Node pointer or NULL on error */
888 help_new_node(const char *filename
, /* I - Filename */
889 const char *anchor
, /* I - Anchor */
890 const char *section
, /* I - Section */
891 const char *text
, /* I - Text */
892 time_t mtime
, /* I - Modification time */
893 off_t offset
, /* I - Offset in file */
894 size_t length
) /* I - Length in bytes */
896 help_node_t
*n
; /* Node */
899 DEBUG_printf(("help_new_node(filename=\"%s\", anchor=\"%s\", text=\"%s\", "
900 "mtime=%ld, offset=%ld, length=%ld)\n",
901 filename
? filename
: "(nil)", anchor
? anchor
: "(nil)",
902 text
? text
: "(nil)", (long)mtime
, (long)offset
,
905 n
= (help_node_t
*)calloc(1, sizeof(help_node_t
));
909 n
->filename
= strdup(filename
);
910 n
->anchor
= anchor
? strdup(anchor
) : NULL
;
911 n
->section
= (section
&& *section
) ? strdup(section
) : NULL
;
912 n
->text
= strdup(text
);
922 * 'help_sort_nodes_by_name()' - Sort nodes by section, filename, and anchor.
925 static int /* O - Difference */
926 help_sort_by_name(help_node_t
*n1
, /* I - First node */
927 help_node_t
*n2
) /* I - Second node */
929 int diff
; /* Difference */
932 DEBUG_printf(("help_sort_by_name(n1=%p(%s#%s), n2=%p(%s#%s)\n",
933 n1
, n1
->filename
, n1
->anchor
? n1
->anchor
: "",
934 n2
, n2
->filename
, n2
->anchor
? n2
->anchor
: ""));
936 if ((diff
= strcmp(n1
->filename
, n2
->filename
)) != 0)
939 if (!n1
->anchor
&& !n2
->anchor
)
941 else if (!n1
->anchor
)
943 else if (!n2
->anchor
)
946 return (strcmp(n1
->anchor
, n2
->anchor
));
951 * 'help_sort_nodes_by_score()' - Sort nodes by score and text.
954 static int /* O - Difference */
955 help_sort_by_score(help_node_t
*n1
, /* I - First node */
956 help_node_t
*n2
) /* I - Second node */
958 int diff
; /* Difference */
961 DEBUG_printf(("help_sort_by_score(n1=%p(%d \"%s\" \"%s\"), "
962 "n2=%p(%d \"%s\" \"%s\")\n",
963 n1
, n1
->score
, n1
->section
? n1
->section
: "", n1
->text
,
964 n2
, n2
->score
, n2
->section
? n2
->section
: "", n2
->text
));
966 if (n1
->score
!= n2
->score
)
967 return (n1
->score
- n2
->score
);
969 if (n1
->section
&& !n2
->section
)
971 else if (!n1
->section
&& n2
->section
)
973 else if (n1
->section
&& n2
->section
&&
974 (diff
= strcmp(n1
->section
, n2
->section
)) != 0)
977 return (strcasecmp(n1
->text
, n2
->text
));
982 * End of "$Id: help-index.c 5665 2006-06-16 00:59:10Z mike $".