]>
Commit | Line | Data |
---|---|---|
ef416fc2 | 1 | /* |
c07d5b2d | 2 | * "$Id: help-index.c 181 2006-06-22 20:01:18Z jlovell $" |
ef416fc2 | 3 | * |
4 | * On-line help index routines for the Common UNIX Printing System (CUPS). | |
5 | * | |
ecdc0628 | 6 | * Copyright 1997-2006 by Easy Software Products. |
ef416fc2 | 7 | * |
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 | |
13 | * at: | |
14 | * | |
15 | * Attn: CUPS Licensing Information | |
16 | * Easy Software Products | |
17 | * 44141 Airport View Drive, Suite 204 | |
18 | * Hollywood, Maryland 20636 USA | |
19 | * | |
20 | * Voice: (301) 373-9600 | |
21 | * EMail: cups-info@cups.org | |
22 | * WWW: http://www.cups.org | |
23 | * | |
24 | * Contents: | |
25 | * | |
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. | |
ef416fc2 | 32 | * help_delete_node() - Free all memory used by a node. |
ef416fc2 | 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. | |
38 | */ | |
39 | ||
40 | /* | |
41 | * Include necessary headers... | |
42 | */ | |
43 | ||
44 | #include "cgi-private.h" | |
45 | #include <cups/dir.h> | |
46 | ||
47 | ||
48 | /* | |
49 | * Local functions... | |
50 | */ | |
51 | ||
ef416fc2 | 52 | static void help_delete_node(help_node_t *n); |
ef416fc2 | 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, | |
57 | const char *filename, | |
58 | const char *relative, | |
59 | time_t mtime); | |
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, | |
63 | size_t length); | |
ecdc0628 | 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); | |
ef416fc2 | 66 | |
67 | ||
68 | /* | |
69 | * 'helpDeleteIndex()' - Delete an index, freeing all memory used. | |
70 | */ | |
71 | ||
72 | void | |
ecdc0628 | 73 | helpDeleteIndex(help_index_t *hi) /* I - Help index */ |
ef416fc2 | 74 | { |
ecdc0628 | 75 | help_node_t *node; /* Current node */ |
ef416fc2 | 76 | |
77 | ||
78 | DEBUG_printf(("helpDeleteIndex(hi=%p)\n", hi)); | |
79 | ||
80 | if (!hi) | |
81 | return; | |
82 | ||
ecdc0628 | 83 | for (node = (help_node_t *)cupsArrayFirst(hi->nodes); |
84 | node; | |
85 | node = (help_node_t *)cupsArrayNext(hi->nodes)) | |
ef416fc2 | 86 | { |
ecdc0628 | 87 | cupsArrayRemove(hi->nodes, node); |
88 | cupsArrayRemove(hi->sorted, node); | |
ef416fc2 | 89 | |
ecdc0628 | 90 | if (!hi->search) |
91 | help_delete_node(node); | |
92 | } | |
ef416fc2 | 93 | |
ecdc0628 | 94 | cupsArrayDelete(hi->nodes); |
95 | cupsArrayDelete(hi->sorted); | |
ef416fc2 | 96 | |
97 | free(hi); | |
98 | } | |
99 | ||
100 | ||
101 | /* | |
102 | * 'helpFindNode()' - Find a node in an index. | |
103 | */ | |
104 | ||
ecdc0628 | 105 | help_node_t * /* O - Node pointer or NULL */ |
ef416fc2 | 106 | helpFindNode(help_index_t *hi, /* I - Index */ |
107 | const char *filename, /* I - Filename */ | |
108 | const char *anchor) /* I - Anchor */ | |
109 | { | |
ecdc0628 | 110 | help_node_t key; /* Search key */ |
ef416fc2 | 111 | |
112 | ||
113 | DEBUG_printf(("helpFindNode(hi=%p, filename=\"%s\", anchor=\"%s\")\n", | |
114 | hi, filename ? filename : "(nil)", anchor ? anchor : "(nil)")); | |
115 | ||
116 | /* | |
117 | * Range check input... | |
118 | */ | |
119 | ||
120 | if (!hi || !filename) | |
121 | return (NULL); | |
122 | ||
123 | /* | |
124 | * Initialize the search key... | |
125 | */ | |
126 | ||
127 | key.filename = (char *)filename; | |
128 | key.anchor = (char *)anchor; | |
ef416fc2 | 129 | |
130 | /* | |
131 | * Return any match... | |
132 | */ | |
133 | ||
ecdc0628 | 134 | return ((help_node_t *)cupsArrayFind(hi->nodes, &key)); |
ef416fc2 | 135 | } |
136 | ||
137 | ||
138 | /* | |
139 | * 'helpLoadIndex()' - Load a help index from disk. | |
140 | */ | |
141 | ||
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 */ | |
145 | { | |
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? */ | |
ef416fc2 | 159 | help_node_t *node; /* Current node */ |
160 | ||
161 | ||
162 | DEBUG_printf(("helpLoadIndex(hifile=\"%s\", directory=\"%s\")\n", | |
163 | hifile, directory)); | |
164 | ||
165 | /* | |
166 | * Create a new, empty index. | |
167 | */ | |
168 | ||
ecdc0628 | 169 | if ((hi = (help_index_t *)calloc(1, sizeof(help_index_t))) == NULL) |
170 | return (NULL); | |
171 | ||
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); | |
174 | ||
175 | if (!hi->nodes || !hi->sorted) | |
176 | { | |
177 | cupsArrayDelete(hi->nodes); | |
178 | cupsArrayDelete(hi->sorted); | |
179 | free(hi); | |
180 | return (NULL); | |
181 | } | |
ef416fc2 | 182 | |
183 | /* | |
184 | * Try loading the existing index file... | |
185 | */ | |
186 | ||
187 | if ((fp = cupsFileOpen(hifile, "r")) != NULL) | |
188 | { | |
189 | /* | |
190 | * Lock the file and then read the first line... | |
191 | */ | |
192 | ||
193 | cupsFileLock(fp, 1); | |
194 | ||
195 | if (cupsFileGets(fp, line, sizeof(line)) && !strcmp(line, "HELPV1")) | |
196 | { | |
197 | /* | |
198 | * Got a valid header line, now read the data lines... | |
199 | */ | |
200 | ||
201 | while (cupsFileGets(fp, line, sizeof(line))) | |
202 | { | |
203 | /* | |
204 | * Each line looks like one of the following: | |
205 | * | |
206 | * filename mtime offset length "section" "text" | |
207 | * filename#anchor offset length "text" | |
208 | */ | |
209 | ||
210 | filename = line; | |
211 | ||
212 | if ((ptr = strchr(line, ' ')) == NULL) | |
213 | break; | |
214 | ||
215 | while (isspace(*ptr & 255)) | |
216 | *ptr++ = '\0'; | |
217 | ||
218 | if ((anchor = strrchr(filename, '#')) != NULL) | |
219 | { | |
220 | *anchor++ = '\0'; | |
221 | mtime = 0; | |
222 | } | |
223 | else | |
224 | mtime = strtol(ptr, &ptr, 10); | |
225 | ||
226 | offset = strtoll(ptr, &ptr, 10); | |
227 | length = strtoll(ptr, &ptr, 10); | |
228 | ||
229 | while (isspace(*ptr & 255)) | |
230 | ptr ++; | |
231 | ||
232 | if (!anchor) | |
233 | { | |
234 | /* | |
235 | * Get section... | |
236 | */ | |
237 | ||
238 | if (*ptr != '\"') | |
239 | break; | |
240 | ||
241 | ptr ++; | |
242 | sectptr = ptr; | |
243 | ||
244 | while (*ptr && *ptr != '\"') | |
245 | ptr ++; | |
246 | ||
247 | if (*ptr != '\"') | |
248 | break; | |
249 | ||
250 | *ptr++ = '\0'; | |
251 | ||
252 | strlcpy(section, sectptr, sizeof(section)); | |
253 | ||
254 | while (isspace(*ptr & 255)) | |
255 | ptr ++; | |
256 | } | |
257 | ||
258 | if (*ptr != '\"') | |
259 | break; | |
260 | ||
261 | ptr ++; | |
262 | text = ptr; | |
263 | ||
264 | while (*ptr && *ptr != '\"') | |
265 | ptr ++; | |
266 | ||
267 | if (*ptr != '\"') | |
268 | break; | |
269 | ||
270 | *ptr++ = '\0'; | |
271 | ||
272 | if ((node = help_new_node(filename, anchor, section, text, | |
273 | mtime, offset, length)) == NULL) | |
274 | break; | |
275 | ||
ef416fc2 | 276 | node->score = -1; |
ecdc0628 | 277 | |
278 | cupsArrayAdd(hi->nodes, node); | |
ef416fc2 | 279 | } |
280 | } | |
281 | ||
282 | cupsFileClose(fp); | |
283 | } | |
284 | ||
285 | /* | |
286 | * Scan for new/updated files... | |
287 | */ | |
288 | ||
289 | update = help_load_directory(hi, directory, NULL); | |
290 | ||
291 | /* | |
292 | * Remove any files that are no longer installed... | |
293 | */ | |
294 | ||
ecdc0628 | 295 | for (node = (help_node_t *)cupsArrayFirst(hi->nodes); |
296 | node; | |
297 | node = (help_node_t *)cupsArrayNext(hi->nodes)) | |
298 | if (node->score < 0) | |
ef416fc2 | 299 | { |
300 | /* | |
301 | * Delete this node... | |
302 | */ | |
303 | ||
ecdc0628 | 304 | cupsArrayRemove(hi->nodes, node); |
305 | help_delete_node(node); | |
ef416fc2 | 306 | } |
ef416fc2 | 307 | |
308 | /* | |
ecdc0628 | 309 | * Add nodes to the sorted array... |
ef416fc2 | 310 | */ |
311 | ||
ecdc0628 | 312 | for (node = (help_node_t *)cupsArrayFirst(hi->nodes); |
313 | node; | |
314 | node = (help_node_t *)cupsArrayNext(hi->nodes)) | |
315 | cupsArrayAdd(hi->sorted, node); | |
ef416fc2 | 316 | |
317 | /* | |
ecdc0628 | 318 | * Save the index if we updated it... |
ef416fc2 | 319 | */ |
320 | ||
ecdc0628 | 321 | if (update) |
322 | helpSaveIndex(hi, hifile); | |
ef416fc2 | 323 | |
324 | /* | |
325 | * Return the index... | |
326 | */ | |
327 | ||
328 | return (hi); | |
329 | } | |
330 | ||
331 | ||
332 | /* | |
333 | * 'helpSaveIndex()' - Save a help index to disk. | |
334 | */ | |
335 | ||
336 | int /* O - 0 on success, -1 on error */ | |
337 | helpSaveIndex(help_index_t *hi, /* I - Index */ | |
338 | const char *hifile) /* I - Index filename */ | |
339 | { | |
340 | cups_file_t *fp; /* Index file */ | |
ef416fc2 | 341 | help_node_t *node; /* Current node */ |
342 | ||
343 | ||
344 | DEBUG_printf(("helpSaveIndex(hi=%p, hifile=\"%s\")\n", hi, hifile)); | |
345 | ||
346 | /* | |
347 | * Try creating a new index file... | |
348 | */ | |
349 | ||
350 | if ((fp = cupsFileOpen(hifile, "w9")) == NULL) | |
351 | return (-1); | |
352 | ||
353 | /* | |
354 | * Lock the file while we write it... | |
355 | */ | |
356 | ||
357 | cupsFileLock(fp, 1); | |
358 | ||
359 | cupsFilePuts(fp, "HELPV1\n"); | |
360 | ||
ecdc0628 | 361 | for (node = (help_node_t *)cupsArrayFirst(hi->nodes); |
362 | node; | |
363 | node = (help_node_t *)cupsArrayNext(hi->nodes)) | |
ef416fc2 | 364 | { |
365 | /* | |
366 | * Write the current node with/without the anchor... | |
367 | */ | |
368 | ||
ef416fc2 | 369 | if (node->anchor) |
370 | { | |
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, | |
374 | node->text) < 0) | |
375 | break; | |
376 | } | |
377 | else | |
378 | { | |
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) | |
383 | break; | |
384 | } | |
385 | } | |
386 | ||
ecdc0628 | 387 | cupsFileFlush(fp); |
388 | ||
ef416fc2 | 389 | if (cupsFileClose(fp) < 0) |
390 | return (-1); | |
ecdc0628 | 391 | else if (node) |
ef416fc2 | 392 | return (-1); |
393 | else | |
394 | return (0); | |
395 | } | |
396 | ||
397 | ||
398 | /* | |
399 | * 'helpSearchIndex()' - Search an index. | |
400 | */ | |
401 | ||
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 */ | |
407 | { | |
ef416fc2 | 408 | help_index_t *search; /* Search index */ |
ecdc0628 | 409 | help_node_t *node; /* Current node */ |
ef416fc2 | 410 | void *sc; /* Search context */ |
411 | int matches; /* Number of matches */ | |
412 | ||
413 | ||
414 | DEBUG_printf(("helpSearchIndex(hi=%p, query=\"%s\", filename=\"%s\")\n", | |
415 | hi, query ? query : "(nil)", | |
416 | filename ? filename : "(nil)")); | |
417 | ||
418 | /* | |
419 | * Range check... | |
420 | */ | |
421 | ||
422 | if (!hi || !query) | |
423 | return (NULL); | |
424 | ||
ecdc0628 | 425 | /* |
426 | * Reset the scores of all nodes to 0... | |
427 | */ | |
428 | ||
429 | for (node = (help_node_t *)cupsArrayFirst(hi->nodes); | |
430 | node; | |
431 | node = (help_node_t *)cupsArrayNext(hi->nodes)) | |
432 | node->score = 0; | |
433 | ||
434 | /* | |
435 | * Find the first node to search in... | |
436 | */ | |
ef416fc2 | 437 | |
438 | if (filename) | |
439 | { | |
ecdc0628 | 440 | node = helpFindNode(hi, filename, NULL); |
441 | if (!node) | |
ef416fc2 | 442 | return (NULL); |
443 | } | |
444 | else | |
ecdc0628 | 445 | node = (help_node_t *)cupsArrayFirst(hi->nodes); |
ef416fc2 | 446 | |
447 | /* | |
448 | * Convert the query into a regular expression... | |
449 | */ | |
450 | ||
451 | sc = cgiCompileSearch(query); | |
452 | if (!sc) | |
453 | return (NULL); | |
454 | ||
455 | /* | |
456 | * Allocate a search index... | |
457 | */ | |
458 | ||
459 | search = calloc(1, sizeof(help_index_t)); | |
460 | if (!search) | |
461 | { | |
462 | cgiFreeSearch(sc); | |
463 | return (NULL); | |
464 | } | |
465 | ||
ecdc0628 | 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); | |
468 | ||
469 | if (!search->nodes || !search->sorted) | |
470 | { | |
471 | cupsArrayDelete(search->nodes); | |
472 | cupsArrayDelete(search->sorted); | |
473 | free(search); | |
474 | cgiFreeSearch(sc); | |
475 | return (NULL); | |
476 | } | |
477 | ||
ef416fc2 | 478 | search->search = 1; |
479 | ||
480 | /* | |
481 | * Check each node in the index, adding matching nodes to the | |
482 | * search index... | |
483 | */ | |
484 | ||
ecdc0628 | 485 | for (; node; node = (help_node_t *)cupsArrayNext(hi->nodes)) |
486 | if (section && strcmp(node->section, section)) | |
ef416fc2 | 487 | continue; |
ecdc0628 | 488 | else if (filename && strcmp(node->filename, filename)) |
ef416fc2 | 489 | continue; |
ecdc0628 | 490 | else if ((matches = cgiDoSearch(sc, node->text)) > 0) |
ef416fc2 | 491 | { |
492 | /* | |
493 | * Found a match, add the node to the search index... | |
494 | */ | |
495 | ||
ecdc0628 | 496 | node->score = matches; |
ef416fc2 | 497 | |
ecdc0628 | 498 | cupsArrayAdd(search->nodes, node); |
499 | cupsArrayAdd(search->sorted, node); | |
ef416fc2 | 500 | } |
501 | ||
502 | /* | |
503 | * Free the search context... | |
504 | */ | |
505 | ||
506 | cgiFreeSearch(sc); | |
507 | ||
ef416fc2 | 508 | /* |
509 | * Return the results... | |
510 | */ | |
511 | ||
512 | return (search); | |
513 | } | |
514 | ||
515 | ||
ef416fc2 | 516 | /* |
517 | * 'help_delete_node()' - Free all memory used by a node. | |
518 | */ | |
519 | ||
520 | static void | |
521 | help_delete_node(help_node_t *n) /* I - Node */ | |
522 | { | |
523 | DEBUG_printf(("help_delete_node(n=%p)\n", n)); | |
524 | ||
525 | if (!n) | |
526 | return; | |
527 | ||
528 | if (n->filename) | |
529 | free(n->filename); | |
530 | ||
531 | if (n->anchor) | |
532 | free(n->anchor); | |
533 | ||
534 | if (n->section) | |
535 | free(n->section); | |
536 | ||
537 | if (n->text) | |
538 | free(n->text); | |
539 | ||
540 | free(n); | |
541 | } | |
542 | ||
543 | ||
ef416fc2 | 544 | /* |
545 | * 'help_load_directory()' - Load a directory of files into an index. | |
546 | */ | |
547 | ||
548 | static int /* O - 0 = success, -1 = error, 1 = updated */ | |
549 | help_load_directory( | |
550 | help_index_t *hi, /* I - Index */ | |
551 | const char *directory, /* I - Directory */ | |
552 | const char *relative) /* I - Relative path */ | |
553 | { | |
ef416fc2 | 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? */ | |
ecdc0628 | 560 | help_node_t *node; /* Current node */ |
ef416fc2 | 561 | |
562 | ||
563 | DEBUG_printf(("help_load_directory(hi=%p, directory=\"%s\", relative=\"%s\")\n", | |
564 | hi, directory ? directory : "(nil)", relative ? relative : "(nil)")); | |
565 | ||
566 | /* | |
567 | * Open the directory and scan it... | |
568 | */ | |
569 | ||
570 | if ((dir = cupsDirOpen(directory)) == NULL) | |
571 | return (0); | |
572 | ||
573 | update = 0; | |
574 | ||
575 | while ((dent = cupsDirRead(dir)) != NULL) | |
576 | { | |
ecdc0628 | 577 | /* |
578 | * Skip "." files... | |
579 | */ | |
580 | ||
581 | if (dent->filename[0] == '.') | |
582 | continue; | |
583 | ||
ef416fc2 | 584 | /* |
585 | * Get absolute and relative filenames... | |
586 | */ | |
587 | ||
588 | snprintf(filename, sizeof(filename), "%s/%s", directory, dent->filename); | |
589 | if (relative) | |
590 | snprintf(relname, sizeof(relname), "%s/%s", relative, dent->filename); | |
591 | else | |
592 | strlcpy(relname, dent->filename, sizeof(relname)); | |
593 | ||
594 | /* | |
595 | * Check if we have a HTML file... | |
596 | */ | |
597 | ||
598 | if ((ext = strstr(dent->filename, ".html")) != NULL && | |
599 | (!ext[5] || !strcmp(ext + 5, ".gz"))) | |
600 | { | |
601 | /* | |
602 | * HTML file, see if we have already indexed the file... | |
603 | */ | |
604 | ||
605 | if ((node = helpFindNode(hi, relname, NULL)) != NULL) | |
606 | { | |
607 | /* | |
608 | * File already indexed - check dates to confirm that the | |
609 | * index is up-to-date... | |
610 | */ | |
611 | ||
ecdc0628 | 612 | if (node->mtime == dent->fileinfo.st_mtime) |
ef416fc2 | 613 | { |
614 | /* | |
615 | * Same modification time, so mark all of the nodes | |
616 | * for this file as up-to-date... | |
617 | */ | |
618 | ||
ecdc0628 | 619 | for (; node; node = (help_node_t *)cupsArrayNext(hi->nodes)) |
620 | if (!strcmp(node->filename, relname)) | |
621 | node->score = 0; | |
ef416fc2 | 622 | else |
623 | break; | |
624 | ||
625 | continue; | |
626 | } | |
627 | } | |
628 | ||
629 | update = 1; | |
630 | ||
631 | help_load_file(hi, filename, relname, dent->fileinfo.st_mtime); | |
632 | } | |
633 | else if (S_ISDIR(dent->fileinfo.st_mode)) | |
634 | { | |
635 | /* | |
636 | * Process sub-directory... | |
637 | */ | |
638 | ||
639 | if (help_load_directory(hi, filename, relname) == 1) | |
640 | update = 1; | |
641 | } | |
642 | } | |
643 | ||
644 | cupsDirClose(dir); | |
645 | ||
646 | return (update); | |
647 | } | |
648 | ||
649 | ||
650 | /* | |
651 | * 'help_load_file()' - Load a HTML files into an index. | |
652 | */ | |
653 | ||
654 | static int /* O - 0 = success, -1 = error */ | |
655 | help_load_file( | |
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 */ | |
660 | { | |
661 | cups_file_t *fp; /* HTML file */ | |
ecdc0628 | 662 | help_node_t *node; /* Current node */ |
ef416fc2 | 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 */ | |
670 | ||
671 | ||
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)); | |
675 | ||
676 | if ((fp = cupsFileOpen(filename, "r")) == NULL) | |
677 | return (-1); | |
678 | ||
679 | node = NULL; | |
680 | offset = 0; | |
681 | ||
682 | strcpy(section, "Other"); | |
683 | ||
684 | while (cupsFileGets(fp, line, sizeof(line))) | |
685 | { | |
686 | /* | |
687 | * Look for "<TITLE>", "<A NAME", or "<!-- SECTION:" prefix... | |
688 | */ | |
689 | ||
690 | if (!strncasecmp(line, "<!-- SECTION:", 13)) | |
691 | { | |
692 | /* | |
693 | * Got section line, copy it! | |
694 | */ | |
695 | ||
696 | for (ptr = line + 13; isspace(*ptr & 255); ptr ++); | |
697 | ||
698 | strlcpy(section, ptr, sizeof(section)); | |
699 | if ((ptr = strstr(section, "-->")) != NULL) | |
700 | { | |
701 | /* | |
702 | * Strip comment stuff from end of line... | |
703 | */ | |
704 | ||
705 | for (*ptr-- = '\0'; ptr > line && isspace(*ptr & 255); *ptr-- = '\0'); | |
706 | ||
707 | if (isspace(*ptr & 255)) | |
708 | *ptr = '\0'; | |
709 | } | |
710 | continue; | |
711 | } | |
712 | ||
713 | for (ptr = line; (ptr = strchr(ptr, '<')) != NULL;) | |
714 | { | |
715 | ptr ++; | |
716 | ||
717 | if (!strncasecmp(ptr, "TITLE>", 6)) | |
718 | { | |
719 | /* | |
720 | * Found the title... | |
721 | */ | |
722 | ||
723 | anchor = NULL; | |
724 | ptr += 6; | |
725 | } | |
726 | else if (!strncasecmp(ptr, "A NAME=", 7)) | |
727 | { | |
728 | /* | |
729 | * Found an anchor... | |
730 | */ | |
731 | ||
732 | ptr += 7; | |
733 | ||
734 | if (*ptr == '\"' || *ptr == '\'') | |
735 | { | |
736 | /* | |
737 | * Get quoted anchor... | |
738 | */ | |
739 | ||
740 | quote = *ptr; | |
741 | anchor = ptr + 1; | |
742 | if ((ptr = strchr(anchor, quote)) != NULL) | |
743 | *ptr++ = '\0'; | |
744 | else | |
745 | break; | |
746 | } | |
747 | else | |
748 | { | |
749 | /* | |
750 | * Get unquoted anchor... | |
751 | */ | |
752 | ||
753 | anchor = ptr + 1; | |
754 | ||
755 | for (ptr = anchor; *ptr && *ptr != '>' && !isspace(*ptr & 255); ptr ++); | |
756 | ||
757 | if (*ptr) | |
758 | *ptr++ = '\0'; | |
759 | else | |
760 | break; | |
761 | } | |
762 | ||
763 | /* | |
764 | * Got the anchor, now lets find the end... | |
765 | */ | |
766 | ||
767 | while (*ptr && *ptr != '>') | |
768 | ptr ++; | |
769 | ||
770 | if (*ptr != '>') | |
771 | break; | |
772 | ||
773 | ptr ++; | |
774 | } | |
775 | else | |
776 | continue; | |
777 | ||
778 | /* | |
779 | * Now collect text for the link... | |
780 | */ | |
781 | ||
782 | text = ptr; | |
783 | while ((ptr = strchr(text, '<')) == NULL) | |
784 | { | |
785 | ptr = text + strlen(text); | |
786 | if (ptr >= (line + sizeof(line) - 2)) | |
787 | break; | |
788 | ||
789 | *ptr++ = ' '; | |
790 | ||
791 | if (!cupsFileGets(fp, ptr, sizeof(line) - (ptr - line) - 1)) | |
792 | break; | |
793 | } | |
794 | ||
795 | *ptr = '\0'; | |
796 | ||
797 | if (node) | |
798 | node->length = offset - node->offset; | |
799 | ||
800 | if (!*text) | |
801 | { | |
802 | node = NULL; | |
803 | break; | |
804 | } | |
805 | ||
ecdc0628 | 806 | if ((node = helpFindNode(hi, relative, anchor)) != NULL) |
ef416fc2 | 807 | { |
808 | /* | |
809 | * Node already in the index, so replace the text and other | |
810 | * data... | |
811 | */ | |
812 | ||
ecdc0628 | 813 | cupsArrayRemove(hi->nodes, node); |
ef416fc2 | 814 | |
815 | if (node->section) | |
816 | free(node->section); | |
817 | ||
818 | if (node->text) | |
819 | free(node->text); | |
820 | ||
821 | node->section = section[0] ? strdup(section) : NULL; | |
822 | node->text = strdup(text); | |
823 | node->mtime = mtime; | |
824 | node->offset = offset; | |
825 | node->score = 0; | |
826 | } | |
827 | else | |
828 | { | |
829 | /* | |
830 | * New node... | |
831 | */ | |
832 | ||
833 | node = help_new_node(relative, anchor, section, text, mtime, offset, 0); | |
ef416fc2 | 834 | } |
835 | ||
836 | /* | |
837 | * Go through the text value and replace tabs and newlines with | |
838 | * whitespace and eliminate extra whitespace... | |
839 | */ | |
840 | ||
841 | for (ptr = node->text, text = node->text; *ptr;) | |
842 | if (isspace(*ptr & 255)) | |
843 | { | |
844 | while (isspace(*ptr & 255)) | |
ed486911 | 845 | ptr ++; |
ef416fc2 | 846 | |
847 | *text++ = ' '; | |
848 | } | |
849 | else if (text != ptr) | |
850 | *text++ = *ptr++; | |
851 | else | |
852 | { | |
853 | text ++; | |
854 | ptr ++; | |
855 | } | |
856 | ||
857 | *text = '\0'; | |
858 | ||
ecdc0628 | 859 | /* |
860 | * (Re)add the node to the array... | |
861 | */ | |
862 | ||
863 | cupsArrayAdd(hi->nodes, node); | |
ef416fc2 | 864 | break; |
865 | } | |
866 | ||
867 | /* | |
868 | * Get the offset of the next line... | |
869 | */ | |
870 | ||
871 | offset = cupsFileTell(fp); | |
872 | } | |
873 | ||
874 | cupsFileClose(fp); | |
875 | ||
876 | if (node) | |
877 | node->length = offset - node->offset; | |
878 | ||
879 | return (0); | |
880 | } | |
881 | ||
882 | ||
883 | /* | |
884 | * 'help_new_node()' - Create a new node and add it to an index. | |
885 | */ | |
886 | ||
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 */ | |
895 | { | |
896 | help_node_t *n; /* Node */ | |
897 | ||
898 | ||
ecdc0628 | 899 | DEBUG_printf(("help_new_node(filename=\"%s\", anchor=\"%s\", text=\"%s\", " |
900 | "mtime=%ld, offset=%ld, length=%ld)\n", | |
ef416fc2 | 901 | filename ? filename : "(nil)", anchor ? anchor : "(nil)", |
ecdc0628 | 902 | text ? text : "(nil)", (long)mtime, (long)offset, |
903 | (long)length)); | |
ef416fc2 | 904 | |
905 | n = (help_node_t *)calloc(1, sizeof(help_node_t)); | |
906 | if (!n) | |
907 | return (NULL); | |
908 | ||
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); | |
913 | n->mtime = mtime; | |
914 | n->offset = offset; | |
915 | n->length = length; | |
916 | ||
917 | return (n); | |
918 | } | |
919 | ||
920 | ||
921 | /* | |
922 | * 'help_sort_nodes_by_name()' - Sort nodes by section, filename, and anchor. | |
923 | */ | |
924 | ||
925 | static int /* O - Difference */ | |
ecdc0628 | 926 | help_sort_by_name(help_node_t *n1, /* I - First node */ |
927 | help_node_t *n2) /* I - Second node */ | |
ef416fc2 | 928 | { |
ef416fc2 | 929 | int diff; /* Difference */ |
930 | ||
931 | ||
ecdc0628 | 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 : "")); | |
ef416fc2 | 935 | |
ecdc0628 | 936 | if ((diff = strcmp(n1->filename, n2->filename)) != 0) |
ef416fc2 | 937 | return (diff); |
938 | ||
ecdc0628 | 939 | if (!n1->anchor && !n2->anchor) |
ef416fc2 | 940 | return (0); |
ecdc0628 | 941 | else if (!n1->anchor) |
ef416fc2 | 942 | return (-1); |
ecdc0628 | 943 | else if (!n2->anchor) |
ef416fc2 | 944 | return (1); |
945 | else | |
ecdc0628 | 946 | return (strcmp(n1->anchor, n2->anchor)); |
ef416fc2 | 947 | } |
948 | ||
949 | ||
950 | /* | |
951 | * 'help_sort_nodes_by_score()' - Sort nodes by score and text. | |
952 | */ | |
953 | ||
954 | static int /* O - Difference */ | |
ecdc0628 | 955 | help_sort_by_score(help_node_t *n1, /* I - First node */ |
956 | help_node_t *n2) /* I - Second node */ | |
ef416fc2 | 957 | { |
ef416fc2 | 958 | int diff; /* Difference */ |
959 | ||
960 | ||
ecdc0628 | 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)); | |
ef416fc2 | 965 | |
ecdc0628 | 966 | if (n1->score != n2->score) |
967 | return (n1->score - n2->score); | |
ef416fc2 | 968 | |
ecdc0628 | 969 | if (n1->section && !n2->section) |
ef416fc2 | 970 | return (1); |
ecdc0628 | 971 | else if (!n1->section && n2->section) |
ef416fc2 | 972 | return (-1); |
ecdc0628 | 973 | else if (n1->section && n2->section && |
974 | (diff = strcmp(n1->section, n2->section)) != 0) | |
ef416fc2 | 975 | return (diff); |
976 | ||
ecdc0628 | 977 | return (strcasecmp(n1->text, n2->text)); |
ef416fc2 | 978 | } |
979 | ||
980 | ||
981 | /* | |
c07d5b2d | 982 | * End of "$Id: help-index.c 181 2006-06-22 20:01:18Z jlovell $". |
ef416fc2 | 983 | */ |