]> git.cameronkatri.com Git - mandoc.git/blobdiff - mansearch.c
Remember which names are in the NAME section.
[mandoc.git] / mansearch.c
index 3787f454f4adda08762fe4488d9d8df9279d257a..656dcad832ea15973dbe6123a2490db9256db3d5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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)) \
@@ -70,63 +74,12 @@ struct      expr {
 
 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 *);
@@ -153,9 +106,9 @@ mansearch(const struct mansearch *search,
                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;
@@ -189,9 +142,12 @@ mansearch(const struct mansearch *search,
 
        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;
                        }
                }
@@ -279,7 +235,7 @@ mansearch(const struct mansearch *search,
                 * 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);
@@ -289,11 +245,7 @@ mansearch(const struct mansearch *search,
 
                        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);
                }
 
@@ -303,7 +255,8 @@ mansearch(const struct mansearch *search,
                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));
@@ -323,18 +276,12 @@ mansearch(const struct mansearch *search,
                                        (*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++;
                }
@@ -354,41 +301,98 @@ out:
        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 *
@@ -412,11 +416,8 @@ buildoutput(sqlite3 *db, sqlite3_stmt *s, uint64_t id, uint64_t outbit)
                        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;
        }
@@ -588,10 +589,7 @@ exprspec(struct expr *cur, uint64_t key, const char *value,
        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;
@@ -612,8 +610,8 @@ exprterm(const struct mansearch *search, char *buf, int cs)
        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);
@@ -642,6 +640,8 @@ exprterm(const struct mansearch *search, char *buf, int cs)
                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));
@@ -661,15 +661,22 @@ exprterm(const struct mansearch *search, char *buf, int cs)
        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);