Skip to content

Commit

Permalink
Implement support for autocompletion in the cgi frontend using jquery.
Browse files Browse the repository at this point in the history
Code is raw and needs further refactoring + cleaning!
  • Loading branch information
abhinav-upadhyay committed Apr 23, 2012
1 parent 48c6bba commit 3c82198
Show file tree
Hide file tree
Showing 13 changed files with 759 additions and 148 deletions.
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
MDIST= ${NETBSDSRCDIR}/external/bsd/mdocml/dist
MDOCDIR=${NETBSDSRCDIR}/external/bsd/mdocml

PROGS= makemandb apropos whatis apropos.cgi
PROGS= makemandb apropos whatis apropos.cgi suggest.cgi
SRCS.makemandb= makemandb.c apropos-utils.c
SRCS.apropos= apropos.c apropos-utils.c
SRCS.whatis= whatis.c apropos-utils.c
SRCS.apropos.cgi= apropos_cgi.c apropos-utils.c
SRCS.apropos.cgi= apropos_cgi.c apropos-utils.c cgi-utils.c
SRCS.suggest.cgi= suggest_cgi.c cgi-utils.c apropos-utils.c
MAN.makemandb= makemandb.8
MAN.apropos= apropos.1
MAN.whatis= whatis.1
Expand Down
5 changes: 5 additions & 0 deletions ac.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.autocomplete-w1 { background:url(/shadow.png) no-repeat bottom right; position:absolute; top:0px; left:0px; margin:6px 0 0 6px; /* IE6 fix: */ _background:none; _margin:1px 0 0 0; }
.autocomplete { border:1px solid #999; background:#FFF; cursor:default; text-align:left; max-height:350px; overflow:auto; margin:-6px 6px 6px -6px; /* IE6 specific: */ _height:350px; _margin:0; _overflow-x:hidden; }
.autocomplete .selected { background:#F0F0F0; }
.autocomplete div { padding:2px 5px; white-space:nowrap; overflow:hidden; }
.autocomplete strong { font-weight:normal; color:#3399FF; }
5 changes: 5 additions & 0 deletions ac.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
var options, a;
jQuery(function () {
options = { serviceUrl: '/cgi-bin/suggest.cgi' };
a = $('#query').autocomplete(options);
});
91 changes: 73 additions & 18 deletions apropos-utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -507,28 +507,17 @@ edits1 (char *word)
return candidates;
}

/*
* known_word--
* Pass an array of strings to this function and it will return the word with
* maximum frequency in the dictionary. If no word in the array list is found
* in the dictionary, it returns NULL
* #TODO rename this function
/* Build termlist: a comma separated list of all the words in the list for
* use in the SQL query later.
*/
static char *
known_word(sqlite3 *db, char **list, int n)
build_termlist(char **list, int n)
{
int i, rc;
char *sqlstr;
char *termlist = NULL;
char *correct = NULL;
sqlite3_stmt *stmt;

/* Build termlist: a comma separated list of all the words in the list for
* use in the SQL query later.
*/
int total_len = BUFLEN * 20; /* total bytes allocated to termlist */
termlist = emalloc(total_len);
int offset = 0; /* Next byte to write at in termlist */
int i;
termlist[0] = '(';
offset++;

Expand All @@ -538,12 +527,11 @@ known_word(sqlite3 *db, char **list, int n)
termlist = erealloc(termlist, offset + total_len);
total_len *= 2;
}
memcpy(termlist + offset, "\'", 1);
offset++;
memcpy(termlist + offset++, "\'", 1);
memcpy(termlist + offset, list[i], d);
offset += d;

if (i == n -1) {
if (i == n - 1) {
memcpy(termlist + offset, "\'", 1);
offset++;
}
Expand All @@ -558,6 +546,25 @@ known_word(sqlite3 *db, char **list, int n)
else
concat2(&termlist, ")", 1);

return termlist;
}

/*
* known_word--
* Pass an array of strings to this function and it will return the word with
* maximum frequency in the dictionary. If no word in the array list is found
* in the dictionary, it returns NULL
* #TODO rename this function
*/
static char *
known_word(sqlite3 *db, char **list, int n)
{
int i, rc;
char *sqlstr;
char *correct = NULL;
sqlite3_stmt *stmt;
char *termlist = build_termlist(list, n);

easprintf(&sqlstr, "SELECT word FROM mandb_dict WHERE "
"frequency = (SELECT MAX(frequency) FROM mandb_dict "
"WHERE word IN %s) AND word IN %s", termlist, termlist);
Expand Down Expand Up @@ -643,6 +650,54 @@ spell(sqlite3 *db, char *word)
return correct;
}

char *
get_suggestions(sqlite3 *db, char *query)
{
char *retval = NULL;
char *term;
char *temp;
char *sqlstr;
int count;
int rc;
sqlite3_stmt *stmt;

if ((term = strrchr(query, ' ')) == NULL) {
term = query;
query = NULL;
} else {
*term++ = 0;
}

char **list = edits1(term);
int n = strlen(term);
count = n + n -1 + 26 * n + 26 * (n + 1);
char *termlist = build_termlist(list, count);
easprintf(&sqlstr, "SELECT word FROM mandb_dict "
"WHERE word IN %s ORDER BY frequency DESC LIMIT 10", termlist);
rc = sqlite3_prepare_v2(db, sqlstr, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
warnx("%s", sqlite3_errmsg(db));
return NULL;
}
easprintf(&temp, "{\n{ query:\'%s%s%s\',\n "
"suggestions:[", query ? query : "", query ? " " : "", term);
concat(&retval, temp);
free(temp);
count = 0;
while (sqlite3_step(stmt) == SQLITE_ROW) {
if (count++)
concat(&retval, ",");
easprintf(&temp, "\'%s %s\'\n", query ? query : "",sqlite3_column_text(stmt, 0));
concat(&retval, temp);
free(temp);
}
concat(&retval, "]\n}");
sqlite3_finalize(stmt);
free(sqlstr);
free(termlist);
free_list(list, count);
return retval;
}

/*
* rank_func --
Expand Down
1 change: 1 addition & 0 deletions apropos-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,5 @@ int run_query_html(sqlite3 *, query_args *);
int run_query_pager(sqlite3 *, query_args *);
char *remove_stopwords(const char *);
char *spell(sqlite3*, char *);
char *get_suggestions(sqlite3 *, char *);
#endif
135 changes: 7 additions & 128 deletions apropos_cgi.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@


#include <err.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "apropos-utils.h"
#include "cgi-utils.h"

typedef struct callback_data {
int count;
Expand All @@ -51,6 +51,10 @@ print_form(char *query)
printf("<html>\n");
printf("<head>\n");
printf("<title> NetBSD apropos </title>\n");
printf("<script type=\"text/javascript\" src=\"/jquery.js\"></script>\n");
printf("<script type=\"text/JavaScript\" src=\"/jquery.autocomplete.js\"></script>\n");
printf("<script type=\"text/javascript\" src=\"/ac.js\"></script>\n");
printf("<link href=\"/ac.css\" rel=\"stylesheet\" type=\"text/css\" />\n");
printf("</head>\n");
printf("<body>\n");
printf("<center>\n");
Expand All @@ -59,139 +63,14 @@ print_form(char *query)
printf("<table style=\"%s\">\n", "margin:10px;>\n");
printf("<form action=\"/cgi-bin/apropos.cgi\">\n");
printf("<tr >\n");
printf("<td> <input type=\"text\" name=\"q\" value=\"%s\" size=\"30\"></td>\n",
printf("<td> <input type=\"text\" name=\"q\" value=\"%s\" size=\"30\" id=\"query\"></td>\n",
query ? query : "" );
printf("<td> <input type=\"submit\" value=\"Search\"> </td>\n");
printf("</tr>\n");
printf("</table>");

}

/*
* Replaces all the occurrences of '+' in the given string with a space
*/
static char *
parse_space(char *str)
{
if (str == NULL)
return str;

char *iter = str;
while ((iter = strchr(iter, '+')) != NULL)
*iter = ' ';
return str;
}

static char *
parse_hex(char *str)
{
char *percent_ptr;
char hexcode[2];
char *retval;
retval = malloc(strlen(str) + 1);
int i = 2;
int decval = 0;
size_t offset = 0;
size_t sz;
while ((percent_ptr = strchr(str, '%')) != NULL) {
i = 2;
sz = 0;
sz = percent_ptr - str;
decval = 0;
memcpy(retval + offset, str, sz);
percent_ptr++;
offset += sz;
while (i > 0) {
switch (*percent_ptr) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
decval += (*percent_ptr - 48) * pow(16, --i);
break;
case 'A':
decval += 10 * pow(16, --i);
break;
case 'B':
decval += 11 * pow(16, --i);
break;
case 'C':
decval += 12 * pow(16, --i);
break;
case 'D':
decval += 13 * pow(16, --i);
break;
case 'E':
decval += 14 * pow(16, --i);
break;
case 'F':
decval += 15 * pow(16, --i);
break;
}
percent_ptr++;
}
str = percent_ptr;
retval[offset++] = decval;
}
sz = strlen(str);
memcpy(retval + offset, str, sz + 1);
return retval;
}

/*
* Parse the given query string to extract the parameter pname
* and return to the caller. (Not the best way to do it but
* going for simplicity at the moment.)
*/
static char *
get_param(char *qstr, char *pname)
{
if (qstr == NULL)
return NULL;

char *temp;
char *param = NULL;
char *value = NULL;
size_t sz;
while (*qstr) {
sz = strcspn(qstr, "=&");
if (qstr[sz] == '=') {
qstr[sz] = 0;
param = qstr;
}
qstr += sz + 1;

if (param && strcmp(param, pname) == 0)
break;
else
param = NULL;
}
if (param == NULL)
return NULL;

if ((temp = strchr(qstr, '&')) == NULL)
value = strdup(qstr);
else {
sz = temp - qstr;
value = malloc(sz + 1);
if (value == NULL)
errx(EXIT_FAILURE, "malloc failed");
memcpy(value, qstr, sz);
value[sz] = 0;
}
value = parse_space(value);
char *retval = parse_hex(value);
free(value);
return retval;
}


static int
query_callback(void *data, const char *section, const char *name,
const char *name_desc, const char *snippet, size_t snippet_length)
Expand Down Expand Up @@ -274,7 +153,7 @@ main(int argc, char *argv[])
page = 1;
else
page = atoi(p);
print_form(query);
print_form(query);
search(db, query, &cbdata, page);

char *correct_query;
Expand Down
Loading

0 comments on commit 3c82198

Please sign in to comment.