]> git.cameronkatri.com Git - mandoc.git/blobdiff - mansearch.c
MANWIDTH
[mandoc.git] / mansearch.c
index 6e689bd3586262dfffb4e4f3e5952c3f42aeaff8..59a35771970cb4ab0bae0c4a9b6cbbfd9eaadc3e 100644 (file)
@@ -1,7 +1,7 @@
-/*     $OpenBSD: mansearch.c,v 1.50 2016/07/09 15:23:36 schwarze Exp $ */
+/*     $Id: mansearch.c,v 1.82 2019/07/01 22:56:24 schwarze Exp $ */
 /*
  * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2013, 2014, 2015, 2016 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2013-2018 Ingo Schwarze <schwarze@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -36,7 +36,6 @@
 #include <string.h>
 #include <unistd.h>
 
-#include "mandoc.h"
 #include "mandoc_aux.h"
 #include "mandoc_ohash.h"
 #include "manconf.h"
@@ -67,9 +66,9 @@ static        struct ohash    *manmerge_term(struct expr *, struct ohash *);
 static struct ohash    *manmerge_or(struct expr *, struct ohash *);
 static struct ohash    *manmerge_and(struct expr *, struct ohash *);
 static char            *buildnames(const struct dbm_page *);
-static char            *buildoutput(size_t, int32_t);
-static size_t           lstlen(const char *);
-static void             lstcat(char *, size_t *, const char *);
+static char            *buildoutput(size_t, struct dbm_page *);
+static size_t           lstlen(const char *, size_t);
+static void             lstcat(char *, size_t *, const char *, const char *);
 static int              lstmatch(const char *, const char *);
 static struct expr     *exprcomp(const struct mansearch *,
                                int, char *[], int *);
@@ -104,7 +103,8 @@ mansearch(const struct mansearch *search,
        }
 
        cur = maxres = 0;
-       *res = NULL;
+       if (res != NULL)
+               *res = NULL;
 
        outkey = KEY_Nd;
        if (search->outkey != NULL)
@@ -155,7 +155,8 @@ mansearch(const struct mansearch *search,
                chdir_status = 1;
 
                if (dbm_open(MANDOC_DB) == -1) {
-                       warn("%s/%s", paths->paths[i], MANDOC_DB);
+                       if (errno != ENOENT)
+                               warn("%s/%s", paths->paths[i], MANDOC_DB);
                        continue;
                }
 
@@ -169,9 +170,15 @@ mansearch(const struct mansearch *search,
                        page = dbm_page_get(rp->page);
 
                        if (lstmatch(search->sec, page->sect) == 0 ||
-                           lstmatch(search->arch, page->arch) == 0)
+                           lstmatch(search->arch, page->arch) == 0 ||
+                           (search->argmode == ARG_NAME &&
+                            rp->bits <= (int32_t)(NAME_SYN & NAME_MASK)))
                                continue;
 
+                       if (res == NULL) {
+                               cur = 1;
+                               break;
+                       }
                        if (cur + 1 > maxres) {
                                maxres += 1024;
                                *res = mandoc_reallocarray(*res,
@@ -180,12 +187,20 @@ mansearch(const struct mansearch *search,
                        mpage = *res + cur;
                        mandoc_asprintf(&mpage->file, "%s/%s",
                            paths->paths[i], page->file + 1);
+                       if (access(chdir_status ? page->file + 1 :
+                           mpage->file, R_OK) == -1) {
+                               warn("%s", mpage->file);
+                               warnx("outdated mandoc.db contains "
+                                   "bogus %s entry, run makewhatis %s",
+                                   page->file + 1, paths->paths[i]);
+                               free(mpage->file);
+                               free(rp);
+                               continue;
+                       }
                        mpage->names = buildnames(page);
-                       mpage->output = (int)outkey == KEY_Nd ?
-                           mandoc_strdup(page->desc) :
-                           buildoutput(outkey, page->addr);
+                       mpage->output = buildoutput(outkey, page);
+                       mpage->bits = search->firstmatch ? rp->bits : 0;
                        mpage->ipath = i;
-                       mpage->bits = rp->bits;
                        mpage->sec = *page->sect - '0';
                        if (mpage->sec < 0 || mpage->sec > 9)
                                mpage->sec = 10;
@@ -205,12 +220,13 @@ mansearch(const struct mansearch *search,
                if (cur && search->firstmatch)
                        break;
        }
-       qsort(*res, cur, sizeof(struct manpage), manpage_compare);
+       if (res != NULL)
+               qsort(*res, cur, sizeof(struct manpage), manpage_compare);
        if (chdir_status && getcwd_status && chdir(buf) == -1)
                warn("%s", buf);
        exprfree(e);
        *sz = cur;
-       return 1;
+       return res != NULL || cur;
 }
 
 /*
@@ -389,13 +405,29 @@ static int
 manpage_compare(const void *vp1, const void *vp2)
 {
        const struct manpage    *mp1, *mp2;
+       const char              *cp1, *cp2;
+       size_t                   sz1, sz2;
        int                      diff;
 
        mp1 = vp1;
        mp2 = vp2;
-       return (diff = mp2->bits - mp1->bits) ? diff :
-           (diff = mp1->sec - mp2->sec) ? diff :
-           strcasecmp(mp1->names, mp2->names);
+       if ((diff = mp2->bits - mp1->bits) ||
+           (diff = mp1->sec - mp2->sec))
+               return diff;
+
+       /* Fall back to alphabetic ordering of names. */
+       sz1 = strcspn(mp1->names, "(");
+       sz2 = strcspn(mp2->names, "(");
+       if (sz1 < sz2)
+               sz1 = sz2;
+       if ((diff = strncasecmp(mp1->names, mp2->names, sz1)))
+               return diff;
+
+       /* For identical names and sections, prefer arch-dependent. */
+       cp1 = strchr(mp1->names + sz1, '/');
+       cp2 = strchr(mp2->names + sz2, '/');
+       return cp1 != NULL && cp2 != NULL ? strcasecmp(cp1, cp2) :
+           cp1 != NULL ? -1 : cp2 != NULL ? 1 : 0;
 }
 
 static char *
@@ -404,16 +436,16 @@ buildnames(const struct dbm_page *page)
        char    *buf;
        size_t   i, sz;
 
-       sz = lstlen(page->name) + 1 + lstlen(page->sect) +
-           (page->arch == NULL ? 0 : 1 + lstlen(page->arch)) + 2;
+       sz = lstlen(page->name, 2) + 1 + lstlen(page->sect, 2) +
+           (page->arch == NULL ? 0 : 1 + lstlen(page->arch, 2)) + 2;
        buf = mandoc_malloc(sz);
        i = 0;
-       lstcat(buf, &i, page->name);
+       lstcat(buf, &i, page->name, ", ");
        buf[i++] = '(';
-       lstcat(buf, &i, page->sect);
+       lstcat(buf, &i, page->sect, ", ");
        if (page->arch != NULL) {
                buf[i++] = '/';
-               lstcat(buf, &i, page->arch);
+               lstcat(buf, &i, page->arch, ", ");
        }
        buf[i++] = ')';
        buf[i++] = '\0';
@@ -423,43 +455,75 @@ buildnames(const struct dbm_page *page)
 
 /*
  * Count the buffer space needed to print the NUL-terminated
- * list of NUL-terminated strings, when printing two separator
+ * list of NUL-terminated strings, when printing sep separator
  * characters between strings.
  */
 static size_t
-lstlen(const char *cp)
+lstlen(const char *cp, size_t sep)
 {
        size_t   sz;
 
-       for (sz = 0;; sz++) {
-               if (cp[0] == '\0') {
-                       if (cp[1] == '\0')
-                               break;
+       for (sz = 0; *cp != '\0'; cp++) {
+
+               /* Skip names appearing only in the SYNOPSIS. */
+               if (*cp <= (char)(NAME_SYN & NAME_MASK)) {
+                       while (*cp != '\0')
+                               cp++;
+                       continue;
+               }
+
+               /* Skip name class markers. */
+               if (*cp < ' ')
+                       cp++;
+
+               /* Print a separator before each but the first string. */
+               if (sz)
+                       sz += sep;
+
+               /* Copy one string. */
+               while (*cp != '\0') {
                        sz++;
-               } else if (cp[0] < ' ')
-                       sz--;
-               cp++;
+                       cp++;
+               }
        }
        return sz;
 }
 
 /*
  * Print the NUL-terminated list of NUL-terminated strings
- * into the buffer, seperating strings with a comma and a blank.
+ * into the buffer, seperating strings with sep.
  */
 static void
-lstcat(char *buf, size_t *i, const char *cp)
+lstcat(char *buf, size_t *i, const char *cp, const char *sep)
 {
-       for (;;) {
-               if (cp[0] == '\0') {
-                       if (cp[1] == '\0')
-                               break;
-                       buf[(*i)++] = ',';
-                       buf[(*i)++] = ' ';
-               } else if (cp[0] >= ' ')
-                       buf[(*i)++] = cp[0];
-               cp++;
+       const char      *s;
+       size_t           i_start;
+
+       for (i_start = *i; *cp != '\0'; cp++) {
+
+               /* Skip names appearing only in the SYNOPSIS. */
+               if (*cp <= (char)(NAME_SYN & NAME_MASK)) {
+                       while (*cp != '\0')
+                               cp++;
+                       continue;
+               }
+
+               /* Skip name class markers. */
+               if (*cp < ' ')
+                       cp++;
+
+               /* Print a separator before each but the first string. */
+               if (*i > i_start) {
+                       s = sep;
+                       while (*s != '\0')
+                               buf[(*i)++] = *s++;
+               }
+
+               /* Copy one string. */
+               while (*cp != '\0')
+                       buf[(*i)++] = *cp++;
        }
+
 }
 
 /*
@@ -482,17 +546,46 @@ lstmatch(const char *want, const char *have)
 }
 
 /*
- * Build a list of values taken by the macro im
- * in the manual page with big-endian address addr.
+ * Build a list of values taken by the macro im in the manual page.
  */
 static char *
-buildoutput(size_t im, int32_t addr)
+buildoutput(size_t im, struct dbm_page *page)
 {
-       const char      *oldoutput, *sep;
+       const char      *oldoutput, *sep, *input;
        char            *output, *newoutput, *value;
+       size_t           sz, i;
+
+       switch (im) {
+       case KEY_Nd:
+               return mandoc_strdup(page->desc);
+       case KEY_Nm:
+               input = page->name;
+               break;
+       case KEY_sec:
+               input = page->sect;
+               break;
+       case KEY_arch:
+               input = page->arch;
+               if (input == NULL)
+                       input = "all\0";
+               break;
+       default:
+               input = NULL;
+               break;
+       }
+
+       if (input != NULL) {
+               sz = lstlen(input, 3) + 1;
+               output = mandoc_malloc(sz);
+               i = 0;
+               lstcat(output, &i, input, " # ");
+               output[i++] = '\0';
+               assert(i == sz);
+               return output;
+       }
 
        output = NULL;
-       dbm_macro_bypage(im - 2, addr);
+       dbm_macro_bypage(im - 2, page->addr);
        while ((value = dbm_macro_next()) != NULL) {
                if (output == NULL) {
                        oldoutput = "";
@@ -642,6 +735,12 @@ exprterm(const struct mansearch *search, int argc, char *argv[], int *argi)
                return e;
        }
 
+       if (strcmp("-i", argv[*argi]) == 0 && *argi + 1 < argc) {
+               cs = 0;
+               ++*argi;
+       } else
+               cs = 1;
+
        e = mandoc_calloc(1, sizeof(*e));
        e->type = EXPR_TERM;
        e->bits = 0;
@@ -674,8 +773,9 @@ exprterm(const struct mansearch *search, int argc, char *argv[], int *argi)
                cs = 0;
        } else if ((val = strpbrk(argv[*argi], "=~")) == NULL) {
                e->bits = TYPE_Nm | TYPE_Nd;
-               e->match.type = DBM_SUB;
-               e->match.str = argv[*argi];
+               e->match.type = DBM_REGEX;
+               val = argv[*argi];
+               cs = 0;
        } else {
                if (val == argv[*argi])
                        e->bits = TYPE_Nm | TYPE_Nd;