#include "cgi-private.h"
#include <cups/dir.h>
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"
};
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_delete_word(help_word_t *w);
static int help_load_directory(help_index_t *hi,
const char *directory,
const char *relative);
static int help_load_file(help_index_t *hi,
const char *filename,
const char *relative,
time_t mtime);
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(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);
void
helpDeleteIndex(help_index_t *hi)
{
help_node_t *node;
DEBUG_printf(("helpDeleteIndex(hi=%p)\n", hi));
if (!hi)
return;
for (node = (help_node_t *)cupsArrayFirst(hi->nodes);
node;
node = (help_node_t *)cupsArrayNext(hi->nodes))
{
if (!hi->search)
help_delete_node(node);
}
cupsArrayDelete(hi->nodes);
cupsArrayDelete(hi->sorted);
free(hi);
}
help_node_t *
helpFindNode(help_index_t *hi,
const char *filename,
const char *anchor)
{
help_node_t key;
DEBUG_printf(("helpFindNode(hi=%p, filename=\"%s\", anchor=\"%s\")\n",
hi, filename ? filename : "(nil)", anchor ? anchor : "(nil)"));
if (!hi || !filename)
return (NULL);
key.filename = (char *)filename;
key.anchor = (char *)anchor;
return ((help_node_t *)cupsArrayFind(hi->nodes, &key));
}
help_index_t *
helpLoadIndex(const char *hifile,
const char *directory)
{
help_index_t *hi;
cups_file_t *fp;
char line[2048],
*ptr,
*filename,
*anchor,
*sectptr,
section[1024],
*text;
time_t mtime;
off_t offset;
size_t length;
int update;
help_node_t *node;
help_word_t *word;
DEBUG_printf(("helpLoadIndex(hifile=\"%s\", directory=\"%s\")\n",
hifile, directory));
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);
}
if ((fp = cupsFileOpen(hifile, "r")) != NULL)
{
cupsFileLock(fp, 1);
if (cupsFileGets(fp, line, sizeof(line)) && !strcmp(line, "HELPV2"))
{
node = NULL;
while (cupsFileGets(fp, line, sizeof(line)))
{
if (line[0] == ' ')
{
if (!node || (ptr = strrchr(line, ' ')) == NULL)
continue;
if ((word = help_add_word(node, ptr + 1)) != NULL)
word->count = atoi(line + 1);
}
else
{
filename = line;
if ((ptr = strchr(line, ' ')) == NULL)
break;
while (isspace(*ptr & 255))
*ptr++ = '\0';
if ((anchor = strrchr(filename, '#')) != NULL)
{
*anchor++ = '\0';
mtime = 0;
}
else
mtime = strtol(ptr, &ptr, 10);
offset = strtoll(ptr, &ptr, 10);
length = strtoll(ptr, &ptr, 10);
while (isspace(*ptr & 255))
ptr ++;
if (!anchor)
{
if (*ptr != '\"')
break;
ptr ++;
sectptr = ptr;
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';
if ((node = help_new_node(filename, anchor, section, text,
mtime, offset, length)) == NULL)
break;
node->score = -1;
cupsArrayAdd(hi->nodes, node);
}
}
}
cupsFileClose(fp);
}
update = help_load_directory(hi, directory, NULL);
for (node = (help_node_t *)cupsArrayFirst(hi->nodes);
node;
node = (help_node_t *)cupsArrayNext(hi->nodes))
if (node->score < 0)
{
cupsArrayRemove(hi->nodes, node);
help_delete_node(node);
}
for (node = (help_node_t *)cupsArrayFirst(hi->nodes);
node;
node = (help_node_t *)cupsArrayNext(hi->nodes))
cupsArrayAdd(hi->sorted, node);
if (update)
helpSaveIndex(hi, hifile);
return (hi);
}
int
helpSaveIndex(help_index_t *hi,
const char *hifile)
{
cups_file_t *fp;
help_node_t *node;
help_word_t *word;
DEBUG_printf(("helpSaveIndex(hi=%p, hifile=\"%s\")\n", hi, hifile));
if ((fp = cupsFileOpen(hifile, "w9")) == NULL)
return (-1);
cupsFileLock(fp, 1);
cupsFilePuts(fp, "HELPV2\n");
for (node = (help_node_t *)cupsArrayFirst(hi->nodes);
node;
node = (help_node_t *)cupsArrayNext(hi->nodes))
{
if (node->anchor)
{
if (cupsFilePrintf(fp, "%s#%s " CUPS_LLFMT " " CUPS_LLFMT " \"%s\"\n",
node->filename, node->anchor,
CUPS_LLCAST node->offset, CUPS_LLCAST node->length,
node->text) < 0)
break;
}
else
{
if (cupsFilePrintf(fp, "%s %d " CUPS_LLFMT " " CUPS_LLFMT " \"%s\" \"%s\"\n",
node->filename, (int)node->mtime,
CUPS_LLCAST node->offset, CUPS_LLCAST node->length,
node->section ? node->section : "", node->text) < 0)
break;
}
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 (node)
return (-1);
else
return (0);
}
help_index_t *
helpSearchIndex(help_index_t *hi,
const char *query,
const char *section,
const char *filename)
{
help_index_t *search;
help_node_t *node;
help_word_t *word;
void *sc;
int matches;
DEBUG_printf(("helpSearchIndex(hi=%p, query=\"%s\", filename=\"%s\")\n",
hi, query ? query : "(nil)",
filename ? filename : "(nil)"));
if (!hi || !query)
return (NULL);
for (node = (help_node_t *)cupsArrayFirst(hi->nodes);
node;
node = (help_node_t *)cupsArrayNext(hi->nodes))
node->score = 0;
if (filename)
{
node = helpFindNode(hi, filename, NULL);
if (!node)
return (NULL);
}
else
node = (help_node_t *)cupsArrayFirst(hi->nodes);
sc = cgiCompileSearch(query);
if (!sc)
return (NULL);
search = calloc(1, sizeof(help_index_t));
if (!search)
{
cgiFreeSearch(sc);
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;
for (; node; node = (help_node_t *)cupsArrayNext(hi->nodes))
if (section && strcmp(node->section, section))
continue;
else if (filename && strcmp(node->filename, filename))
continue;
else
{
matches = cgiDoSearch(sc, node->text);
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;
if (matches > 0)
{
node->score = matches;
cupsArrayAdd(search->nodes, node);
cupsArrayAdd(search->sorted, node);
}
}
cgiFreeSearch(sc);
return (search);
}
static help_word_t *
help_add_word(help_node_t *n,
const char *text)
{
help_word_t *w,
key;
DEBUG_printf(("help_add_word(n=%p, text=\"%s\")\n", n, text));
if (!n->words)
n->words = cupsArrayNew((cups_array_func_t)help_sort_words, NULL);
key.text = (char *)text;
if ((w = (help_word_t *)cupsArrayFind(n->words, &key)) == NULL)
{
if ((w = calloc(1, sizeof(help_word_t))) == NULL)
return (NULL);
if ((w->text = strdup(text)) == NULL)
{
free(w);
return (NULL);
}
cupsArrayAdd(n->words, w);
}
w->count ++;
return (w);
}
static void
help_delete_node(help_node_t *n)
{
help_word_t *w;
DEBUG_printf(("help_delete_node(n=%p)\n", n));
if (!n)
return;
if (n->filename)
free(n->filename);
if (n->anchor)
free(n->anchor);
if (n->section)
free(n->section);
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);
}
static void
help_delete_word(help_word_t *w)
{
DEBUG_printf(("help_delete_word(w=%p)\n", w));
if (!w)
return;
if (w->text)
free(w->text);
free(w);
}
static int
help_load_directory(
help_index_t *hi,
const char *directory,
const char *relative)
{
cups_dir_t *dir;
cups_dentry_t *dent;
char *ext,
filename[1024],
relname[1024];
int update;
help_node_t *node;
DEBUG_printf(("help_load_directory(hi=%p, directory=\"%s\", relative=\"%s\")\n",
hi, directory ? directory : "(nil)", relative ? relative : "(nil)"));
if ((dir = cupsDirOpen(directory)) == NULL)
return (0);
update = 0;
while ((dent = cupsDirRead(dir)) != NULL)
{
if (dent->filename[0] == '.')
continue;
snprintf(filename, sizeof(filename), "%s/%s", directory, dent->filename);
if (relative)
snprintf(relname, sizeof(relname), "%s/%s", relative, dent->filename);
else
strlcpy(relname, dent->filename, sizeof(relname));
if ((ext = strstr(dent->filename, ".html")) != NULL &&
(!ext[5] || !strcmp(ext + 5, ".gz")))
{
if ((node = helpFindNode(hi, relname, NULL)) != NULL)
{
if (node->mtime == dent->fileinfo.st_mtime)
{
for (; node; node = (help_node_t *)cupsArrayNext(hi->nodes))
if (!strcmp(node->filename, relname))
node->score = 0;
else
break;
continue;
}
}
update = 1;
help_load_file(hi, filename, relname, dent->fileinfo.st_mtime);
}
else if (S_ISDIR(dent->fileinfo.st_mode))
{
if (help_load_directory(hi, filename, relname) == 1)
update = 1;
}
}
cupsDirClose(dir);
return (update);
}
static int
help_load_file(
help_index_t *hi,
const char *filename,
const char *relative,
time_t mtime)
{
cups_file_t *fp;
help_node_t *node;
char line[1024],
temp[1024],
section[1024],
*ptr,
*anchor,
*text;
off_t offset;
char quote;
help_word_t *word;
int wordlen;
DEBUG_printf(("help_load_file(hi=%p, filename=\"%s\", relative=\"%s\", mtime=%ld)\n",
hi, filename ? filename : "(nil)",
relative ? relative : "(nil)", mtime));
if ((fp = cupsFileOpen(filename, "r")) == NULL)
return (-1);
node = NULL;
offset = 0;
strcpy(section, "Other");
while (cupsFileGets(fp, line, sizeof(line)))
{
if (!_cups_strncasecmp(line, "<!-- SECTION:", 13))
{
for (ptr = line + 13; isspace(*ptr & 255); ptr ++);
strlcpy(section, ptr, sizeof(section));
if ((ptr = strstr(section, "-->")) != NULL)
{
for (*ptr-- = '\0'; ptr > line && isspace(*ptr & 255); *ptr-- = '\0');
if (isspace(*ptr & 255))
*ptr = '\0';
}
continue;
}
for (ptr = line; (ptr = strchr(ptr, '<')) != NULL;)
{
ptr ++;
if (!_cups_strncasecmp(ptr, "TITLE>", 6))
{
anchor = NULL;
ptr += 6;
}
else if (!_cups_strncasecmp(ptr, "A NAME=", 7))
{
ptr += 7;
if (*ptr == '\"' || *ptr == '\'')
{
quote = *ptr;
anchor = ptr + 1;
if ((ptr = strchr(anchor, quote)) != NULL)
*ptr++ = '\0';
else
break;
}
else
{
anchor = ptr + 1;
for (ptr = anchor; *ptr && *ptr != '>' && !isspace(*ptr & 255); ptr ++);
if (*ptr)
*ptr++ = '\0';
else
break;
}
while (*ptr && *ptr != '>')
ptr ++;
if (*ptr != '>')
break;
ptr ++;
}
else
continue;
text = ptr;
while ((ptr = strchr(text, '<')) == NULL)
{
ptr = text + strlen(text);
if (ptr >= (line + sizeof(line) - 2))
break;
*ptr++ = ' ';
if (!cupsFileGets(fp, ptr, sizeof(line) - (ptr - line) - 1))
break;
}
*ptr = '\0';
if (node)
node->length = offset - node->offset;
if (!*text)
{
node = NULL;
break;
}
if ((node = helpFindNode(hi, relative, anchor)) != NULL)
{
cupsArrayRemove(hi->nodes, node);
if (node->section)
free(node->section);
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;
node->offset = offset;
node->score = 0;
}
else
{
node = help_new_node(relative, anchor, section, text, mtime, offset, 0);
}
for (ptr = node->text, text = node->text; *ptr;)
if (isspace(*ptr & 255))
{
while (isspace(*ptr & 255))
ptr ++;
*text++ = ' ';
}
else if (text != ptr)
*text++ = *ptr++;
else
{
text ++;
ptr ++;
}
*text = '\0';
cupsArrayAdd(hi->nodes, node);
if (!anchor)
node = NULL;
break;
}
if (node)
{
for (ptr = line; *ptr; ptr ++)
{
if (*ptr == '<')
{
if (!strncmp(ptr, "<!--", 4))
{
if ((text = strstr(ptr + 4, "-->")) == NULL)
ptr += strlen(ptr) - 1;
else
ptr = text + 2;
}
else
{
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 == '&')
{
for (ptr ++; *ptr && *ptr != ';'; ptr ++);
if (!*ptr)
ptr --;
continue;
}
else if (!isalnum(*ptr & 255))
continue;
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 *))
_cups_strcasecmp))
help_add_word(node, temp);
}
}
offset = cupsFileTell(fp);
}
cupsFileClose(fp);
if (node)
node->length = offset - node->offset;
return (0);
}
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)
{
help_node_t *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)", (long)mtime, (long)offset,
(long)length));
n = (help_node_t *)calloc(1, sizeof(help_node_t));
if (!n)
return (NULL);
n->filename = strdup(filename);
n->anchor = anchor ? strdup(anchor) : NULL;
n->section = (section && *section) ? strdup(section) : NULL;
n->text = strdup(text);
n->mtime = mtime;
n->offset = offset;
n->length = length;
return (n);
}
static int
help_sort_by_name(help_node_t *n1,
help_node_t *n2)
{
int diff;
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->filename, n2->filename)) != 0)
return (diff);
if (!n1->anchor && !n2->anchor)
return (0);
else if (!n1->anchor)
return (-1);
else if (!n2->anchor)
return (1);
else
return (strcmp(n1->anchor, n2->anchor));
}
static int
help_sort_by_score(help_node_t *n1,
help_node_t *n2)
{
int diff;
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));
if (n1->score != n2->score)
return (n2->score - n1->score);
if (n1->section && !n2->section)
return (1);
else if (!n1->section && n2->section)
return (-1);
else if (n1->section && n2->section &&
(diff = strcmp(n1->section, n2->section)) != 0)
return (diff);
return (_cups_strcasecmp(n1->text, n2->text));
}
static int
help_sort_words(help_word_t *w1,
help_word_t *w2)
{
DEBUG_printf(("help_sort_words(w1=%p(\"%s\"), w2=%p(\"%s\"))\n",
w1, w1->text, w2, w2->text));
return (_cups_strcasecmp(w1->text, w2->text));
}