-/* $Id: mansearch.c,v 1.16 2014/01/05 03:25:51 schwarze Exp $ */
+/* $Id: mansearch.c,v 1.25 2014/03/28 19:17:12 schwarze Exp $ */
/*
* Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
#include <sqlite3.h>
#include "mandoc.h"
+#include "mandoc_aux.h"
#include "manpath.h"
#include "mansearch.h"
+extern int mansearch_keymax;
+extern const char *const mansearch_keynames[];
+
#define SQL_BIND_TEXT(_db, _s, _i, _v) \
do { if (SQLITE_OK != sqlite3_bind_text \
((_s), (_i)++, (_v), -1, SQLITE_STATIC)) \
struct match {
uint64_t id; /* identifier in database */
- char *file; /* relative filepath of manpage */
- char *desc; /* description of manpage */
int form; /* 0 == catpage */
};
-struct type {
- uint64_t bits;
- const char *name;
-};
-
-static const struct type types[] = {
- { TYPE_An, "An" },
- { TYPE_Ar, "Ar" },
- { TYPE_At, "At" },
- { TYPE_Bsx, "Bsx" },
- { TYPE_Bx, "Bx" },
- { TYPE_Cd, "Cd" },
- { TYPE_Cm, "Cm" },
- { TYPE_Dv, "Dv" },
- { TYPE_Dx, "Dx" },
- { TYPE_Em, "Em" },
- { TYPE_Er, "Er" },
- { TYPE_Ev, "Ev" },
- { TYPE_Fa, "Fa" },
- { TYPE_Fl, "Fl" },
- { TYPE_Fn, "Fn" },
- { TYPE_Fn, "Fo" },
- { TYPE_Ft, "Ft" },
- { TYPE_Fx, "Fx" },
- { TYPE_Ic, "Ic" },
- { TYPE_In, "In" },
- { TYPE_Lb, "Lb" },
- { TYPE_Li, "Li" },
- { TYPE_Lk, "Lk" },
- { TYPE_Ms, "Ms" },
- { TYPE_Mt, "Mt" },
- { TYPE_Nd, "Nd" },
- { TYPE_Nm, "Nm" },
- { TYPE_Nx, "Nx" },
- { TYPE_Ox, "Ox" },
- { TYPE_Pa, "Pa" },
- { TYPE_Rs, "Rs" },
- { TYPE_Sh, "Sh" },
- { TYPE_Ss, "Ss" },
- { TYPE_St, "St" },
- { TYPE_Sy, "Sy" },
- { TYPE_Tn, "Tn" },
- { TYPE_Va, "Va" },
- { TYPE_Va, "Vt" },
- { TYPE_Xr, "Xr" },
- { TYPE_sec, "sec" },
- { TYPE_arch,"arch" },
- { ~0ULL, "any" },
- { 0ULL, NULL }
-};
-
-static char *buildnames(sqlite3 *, sqlite3_stmt *, uint64_t);
+static void buildnames(struct manpage *, sqlite3 *,
+ sqlite3_stmt *, uint64_t,
+ const char *, int form);
static char *buildoutput(sqlite3 *, sqlite3_stmt *,
uint64_t, uint64_t);
static void *hash_alloc(size_t, void *);
const char *outkey,
struct manpage **res, size_t *sz)
{
- int fd, rc, c, ibit;
+ int fd, rc, c, indexbit;
int64_t id;
- uint64_t outbit;
+ uint64_t outbit, iterbit;
char buf[PATH_MAX];
char *sql;
struct manpage *mpage;
outbit = 0;
if (NULL != outkey) {
- for (ibit = 0; types[ibit].bits; ibit++) {
- if (0 == strcasecmp(types[ibit].name, outkey)) {
- outbit = types[ibit].bits;
+ for (indexbit = 0, iterbit = 1;
+ indexbit < mansearch_keymax;
+ indexbit++, iterbit <<= 1) {
+ if (0 == strcasecmp(outkey,
+ mansearch_keynames[indexbit])) {
+ outbit = iterbit;
break;
}
}
* distribution of buckets in the table.
*/
while (SQLITE_ROW == (c = sqlite3_step(s))) {
- id = sqlite3_column_int64(s, 3);
+ id = sqlite3_column_int64(s, 1);
idx = ohash_lookup_memory
(&htab, (char *)&id,
sizeof(uint64_t), (uint32_t)id);
mp = mandoc_calloc(1, sizeof(struct match));
mp->id = id;
- mp->file = mandoc_strdup
- ((char *)sqlite3_column_text(s, 0));
- mp->desc = mandoc_strdup
- ((char *)sqlite3_column_text(s, 1));
- mp->form = sqlite3_column_int(s, 2);
+ mp->form = sqlite3_column_int(s, 0);
ohash_insert(&htab, idx, mp);
}
sqlite3_finalize(s);
c = sqlite3_prepare_v2(db,
- "SELECT * FROM mlinks WHERE pageid=?",
+ "SELECT * FROM mlinks WHERE pageid=?"
+ " ORDER BY sec, arch, name",
-1, &s, NULL);
if (SQLITE_OK != c)
fprintf(stderr, "%s\n", sqlite3_errmsg(db));
(*res, maxres * sizeof(struct manpage));
}
mpage = *res + cur;
- if (-1 == asprintf(&mpage->file, "%s/%s",
- paths->paths[i], mp->file)) {
- perror(0);
- exit((int)MANDOCLEVEL_SYSERR);
- }
- mpage->desc = mp->desc;
mpage->form = mp->form;
- mpage->names = buildnames(db, s, mp->id);
+ buildnames(mpage, db, s, mp->id,
+ paths->paths[i], mp->form);
mpage->output = outbit ?
buildoutput(db, s2, mp->id, outbit) : NULL;
- free(mp->file);
free(mp);
cur++;
}
return(rc);
}
-static char *
-buildnames(sqlite3 *db, sqlite3_stmt *s, uint64_t id)
+static void
+buildnames(struct manpage *mpage, sqlite3 *db, sqlite3_stmt *s,
+ uint64_t id, const char *path, int form)
{
- char *names, *newnames;
- const char *oldnames, *sep1, *name, *sec, *sep2, *arch;
+ char *newnames, *prevsec, *prevarch;
+ const char *oldnames, *sep1, *name, *sec, *sep2, *arch, *fsec;
size_t i;
int c;
- names = NULL;
+ mpage->file = NULL;
+ mpage->names = NULL;
+ prevsec = prevarch = NULL;
i = 1;
SQL_BIND_INT64(db, s, i, id);
while (SQLITE_ROW == (c = sqlite3_step(s))) {
- if (NULL == names) {
+
+ /* Decide whether we already have some names. */
+
+ if (NULL == mpage->names) {
oldnames = "";
sep1 = "";
} else {
- oldnames = names;
+ oldnames = mpage->names;
sep1 = ", ";
}
- sec = sqlite3_column_text(s, 1);
- arch = sqlite3_column_text(s, 2);
- name = sqlite3_column_text(s, 3);
- sep2 = '\0' == *arch ? "" : "/";
- if (-1 == asprintf(&newnames, "%s%s%s(%s%s%s)",
- oldnames, sep1, name, sec, sep2, arch)) {
- perror(0);
- exit((int)MANDOCLEVEL_SYSERR);
+
+ /* Fetch the next name. */
+
+ sec = sqlite3_column_text(s, 0);
+ arch = sqlite3_column_text(s, 1);
+ name = sqlite3_column_text(s, 2);
+
+ /* If the section changed, append the old one. */
+
+ if (NULL != prevsec &&
+ (strcmp(sec, prevsec) ||
+ strcmp(arch, prevarch))) {
+ sep2 = '\0' == *prevarch ? "" : "/";
+ mandoc_asprintf(&newnames, "%s(%s%s%s)",
+ oldnames, prevsec, sep2, prevarch);
+ free(mpage->names);
+ oldnames = mpage->names = newnames;
+ free(prevsec);
+ free(prevarch);
+ prevsec = prevarch = NULL;
+ }
+
+ /* Save the new section, to append it later. */
+
+ if (NULL == prevsec) {
+ prevsec = mandoc_strdup(sec);
+ prevarch = mandoc_strdup(arch);
+ }
+
+ /* Append the new name. */
+
+ mandoc_asprintf(&newnames, "%s%s%s",
+ oldnames, sep1, name);
+ free(mpage->names);
+ mpage->names = newnames;
+
+ /* Also save the first file name encountered. */
+
+ if (NULL != mpage->file)
+ continue;
+
+ if (form) {
+ sep1 = "man";
+ fsec = sec;
+ } else {
+ sep1 = "cat";
+ fsec = "0";
}
- free(names);
- names = newnames;
+ sep2 = '\0' == *arch ? "" : "/";
+ mandoc_asprintf(&mpage->file, "%s/%s%s%s%s/%s.%s",
+ path, sep1, sec, sep2, arch, name, fsec);
}
if (SQLITE_DONE != c)
fprintf(stderr, "%s\n", sqlite3_errmsg(db));
sqlite3_reset(s);
- return(names);
+
+ /* Append one final section to the names. */
+
+ if (NULL != prevsec) {
+ sep2 = '\0' == *prevarch ? "" : "/";
+ mandoc_asprintf(&newnames, "%s(%s%s%s)",
+ mpage->names, prevsec, sep2, prevarch);
+ free(mpage->names);
+ mpage->names = newnames;
+ free(prevsec);
+ free(prevarch);
+ }
}
static char *
sep1 = " # ";
}
data = sqlite3_column_text(s, 1);
- if (-1 == asprintf(&newoutput, "%s%s%s",
- oldoutput, sep1, data)) {
- perror(0);
- exit((int)MANDOCLEVEL_SYSERR);
- }
+ mandoc_asprintf(&newoutput, "%s%s%s",
+ oldoutput, sep1, data);
free(output);
output = newoutput;
}
if (NULL == value)
return(cur);
- if (-1 == asprintf(&cp, format, value)) {
- perror(0);
- exit((int)MANDOCLEVEL_SYSERR);
- }
+ mandoc_asprintf(&cp, format, value);
cur->next = mandoc_calloc(1, sizeof(struct expr));
cur = cur->next;
cur->and = 1;
char errbuf[BUFSIZ];
struct expr *e;
char *key, *v;
- size_t i;
- int irc;
+ uint64_t iterbit;
+ int i, irc;
if ('\0' == *buf)
return(NULL);
e->bits = search->deftype;
if ('~' == *v++) {
+ if (NULL != strstr(buf, "arch"))
+ cs = 0;
if (0 != (irc = regcomp(&e->regexp, v,
REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE)))) {
regerror(irc, &e->regexp, errbuf, sizeof(errbuf));
while (NULL != (key = strsep(&buf, ","))) {
if ('\0' == *key)
continue;
- i = 0;
- while (types[i].bits &&
- strcasecmp(types[i].name, key))
- i++;
- if (0 == types[i].bits) {
- free(e);
- return(NULL);
+ for (i = 0, iterbit = 1;
+ i < mansearch_keymax;
+ i++, iterbit <<= 1) {
+ if (0 == strcasecmp(key,
+ mansearch_keynames[i])) {
+ e->bits |= iterbit;
+ break;
+ }
+ }
+ if (i == mansearch_keymax) {
+ if (strcasecmp(key, "any")) {
+ free(e);
+ return(NULL);
+ }
+ e->bits |= ~0ULL;
}
- e->bits |= types[i].bits;
}
return(e);