[BACK]Return to apropos_db.c CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / mandoc

Diff for /mandoc/Attic/apropos_db.c between version 1.5 and 1.32.2.6

version 1.5, 2011/11/18 07:02:19 version 1.32.2.6, 2014/04/23 21:31:38
Line 1 
Line 1 
 /*      $Id$ */  /*      $Id$ */
 /*  /*
  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>   * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>   * Copyright (c) 2011, 2014 Ingo Schwarze <schwarze@openbsd.org>
  *   *
  * Permission to use, copy, modify, and distribute this software for any   * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above   * purpose with or without fee is hereby granted, provided that the above
Line 15 
Line 15 
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */   */
   #ifdef HAVE_CONFIG_H
   #include "config.h"
   #endif
   
   #include <sys/param.h>
   
 #include <assert.h>  #include <assert.h>
 #include <fcntl.h>  #include <fcntl.h>
 #include <regex.h>  #include <regex.h>
 #include <stdarg.h>  #include <stdarg.h>
   #include <stdint.h>
 #include <stdlib.h>  #include <stdlib.h>
 #include <string.h>  #include <string.h>
   #include <unistd.h>
   
 #ifdef __linux__  #if defined(__APPLE__)
   # include <libkern/OSByteOrder.h>
   #elif defined(__linux__)
   # include <endian.h>
   #elif defined(__sun)
   # include <sys/byteorder.h>
   #else
   # include <sys/endian.h>
   #endif
   
   #if defined(__linux__) || defined(__sun)
 # include <db_185.h>  # include <db_185.h>
 #else  #else
 # include <db.h>  # include <db.h>
Line 31 
Line 49 
 #include "mandocdb.h"  #include "mandocdb.h"
 #include "apropos_db.h"  #include "apropos_db.h"
 #include "mandoc.h"  #include "mandoc.h"
   #include "mandoc_aux.h"
   
 struct  rec {  #define RESFREE(_x) \
         struct res       res; /* resulting record info */          do { \
         /*                  free((_x)->file); \
          * Maintain a binary tree for checking the uniqueness of `rec'                  free((_x)->cat); \
          * when adding elements to the results array.                  free((_x)->title); \
          * Since the results array is dynamic, use offset in the array                  free((_x)->arch); \
          * instead of a pointer to the structure.                  free((_x)->desc); \
          */                  free((_x)->matches); \
         int              lhs;          } while (/*CONSTCOND*/0)
         int              rhs;  
         int              matched; /* expression is true */  
         int             *matches; /* partial truth evaluations */  
 };  
   
 struct  expr {  struct  expr {
         int              regex; /* is regex? */          int              regex; /* is regex? */
         int              index; /* index in match array */          int              index; /* index in match array */
         int              mask; /* type-mask */          uint64_t         mask; /* type-mask */
         int              cs; /* is case-sensitive? */  
         int              and; /* is rhs of logical AND? */          int              and; /* is rhs of logical AND? */
         char            *v; /* search value */          char            *v; /* search value */
         regex_t          re; /* compiled re, if regex */          regex_t          re; /* compiled re, if regex */
Line 59  struct expr {
Line 73  struct expr {
 };  };
   
 struct  type {  struct  type {
         int              mask;          uint64_t         mask;
         const char      *name;          const char      *name;
 };  };
   
   struct  rectree {
           struct res      *node; /* record array for dir tree */
           int              len; /* length of record array */
   };
   
 static  const struct type types[] = {  static  const struct type types[] = {
         { TYPE_An, "An" },          { TYPE_An, "An" },
           { TYPE_Ar, "Ar" },
           { TYPE_At, "At" },
           { TYPE_Bsx, "Bsx" },
           { TYPE_Bx, "Bx" },
         { TYPE_Cd, "Cd" },          { TYPE_Cd, "Cd" },
           { TYPE_Cm, "Cm" },
           { TYPE_Dv, "Dv" },
           { TYPE_Dx, "Dx" },
           { TYPE_Em, "Em" },
         { TYPE_Er, "Er" },          { TYPE_Er, "Er" },
         { TYPE_Ev, "Ev" },          { TYPE_Ev, "Ev" },
           { TYPE_Fa, "Fa" },
           { TYPE_Fl, "Fl" },
         { TYPE_Fn, "Fn" },          { TYPE_Fn, "Fn" },
         { TYPE_Fn, "Fo" },          { TYPE_Fn, "Fo" },
           { TYPE_Ft, "Ft" },
           { TYPE_Fx, "Fx" },
           { TYPE_Ic, "Ic" },
         { TYPE_In, "In" },          { TYPE_In, "In" },
           { TYPE_Lb, "Lb" },
           { TYPE_Li, "Li" },
           { TYPE_Lk, "Lk" },
           { TYPE_Ms, "Ms" },
           { TYPE_Mt, "Mt" },
         { TYPE_Nd, "Nd" },          { TYPE_Nd, "Nd" },
         { TYPE_Nm, "Nm" },          { TYPE_Nm, "Nm" },
           { TYPE_Nx, "Nx" },
           { TYPE_Ox, "Ox" },
         { TYPE_Pa, "Pa" },          { TYPE_Pa, "Pa" },
           { TYPE_Rs, "Rs" },
           { TYPE_Sh, "Sh" },
           { TYPE_Ss, "Ss" },
         { TYPE_St, "St" },          { TYPE_St, "St" },
           { TYPE_Sy, "Sy" },
           { TYPE_Tn, "Tn" },
         { TYPE_Va, "Va" },          { TYPE_Va, "Va" },
         { TYPE_Va, "Vt" },          { TYPE_Va, "Vt" },
         { TYPE_Xr, "Xr" },          { TYPE_Xr, "Xr" },
         { INT_MAX, "any" },          { UINT64_MAX, "any" },
         { 0, NULL }          { 0, NULL }
 };  };
   
 static  DB      *btree_open(void);  static  DB      *btree_open(void);
 static  int      btree_read(const DBT *,  static  int      btree_read(const DBT *, const DBT *,
                         const struct mchars *, char **);                          const struct mchars *,
                           uint64_t *, recno_t *, char **);
 static  int      expreval(const struct expr *, int *);  static  int      expreval(const struct expr *, int *);
 static  void     exprexec(const struct expr *,  static  void     exprexec(const struct expr *,
                         const char *, int, struct rec *);                          const char *, uint64_t, struct res *);
 static  int      exprmark(const struct expr *,  static  int      exprmark(const struct expr *,
                         const char *, int, int *);                          const char *, uint64_t, int *);
 static  struct expr *exprexpr(int, char *[], int *, int *, size_t *);  static  struct expr *exprexpr(int, char *[], int *, int *, size_t *);
 static  struct expr *exprterm(char *, int);  static  struct expr *exprterm(char *, int);
 static  DB      *index_open(void);  static  DB      *index_open(void);
 static  int      index_read(const DBT *, const DBT *,  static  int      index_read(const DBT *, const DBT *, int,
                         const struct mchars *, struct rec *);                          const struct mchars *, struct res *);
 static  void     norm_string(const char *,  static  void     norm_string(const char *,
                         const struct mchars *, char **);                          const struct mchars *, char **);
 static  size_t   norm_utf8(unsigned int, char[7]);  static  size_t   norm_utf8(unsigned int, char[7]);
 static  void     recfree(struct rec *);  static  int      single_search(struct rectree *, const struct opts *,
                           const struct expr *, size_t terms,
                           struct mchars *, int);
   
 /*  /*
  * Open the keyword mandoc-db database.   * Open the keyword mandoc-db database.
Line 110  btree_open(void)
Line 157  btree_open(void)
         DB              *db;          DB              *db;
   
         memset(&info, 0, sizeof(BTREEINFO));          memset(&info, 0, sizeof(BTREEINFO));
           info.lorder = 4321;
         info.flags = R_DUP;          info.flags = R_DUP;
   
         db = dbopen(MANDOC_DB, O_RDONLY, 0, DB_BTREE, &info);          db = dbopen(MANDOC_DB, O_RDONLY, 0, DB_BTREE, &info);
         if (NULL != db)          if (NULL != db)
                 return(db);                  return(db);
   
         return(NULL);          return(NULL);
Line 124  btree_open(void)
Line 172  btree_open(void)
  * Return 0 if the database is insane, else 1.   * Return 0 if the database is insane, else 1.
  */   */
 static int  static int
 btree_read(const DBT *v, const struct mchars *mc, char **buf)  btree_read(const DBT *k, const DBT *v, const struct mchars *mc,
                   uint64_t *mask, recno_t *rec, char **buf)
 {  {
           uint64_t         vbuf[2];
   
         /* Sanity: are we nil-terminated? */          /* Are our sizes sane? */
           if (k->size < 2 || sizeof(vbuf) != v->size)
                   return(0);
   
         assert(v->size > 0);          /* Is our string nil-terminated? */
         if ('\0' != ((char *)v->data)[(int)v->size - 1])          if ('\0' != ((const char *)k->data)[(int)k->size - 1])
                 return(0);                  return(0);
   
         norm_string((char *)v->data, mc, buf);          norm_string((const char *)k->data, mc, buf);
           memcpy(vbuf, v->data, v->size);
           *mask = betoh64(vbuf[0]);
           *rec  = betoh64(vbuf[1]);
         return(1);          return(1);
 }  }
   
 /*  /*
  * Take a Unicode codepoint and produce its UTF-8 encoding.   * Take a Unicode codepoint and produce its UTF-8 encoding.
  * This isn't the best way to do this, but it works.   * This isn't the best way to do this, but it works.
  * The magic numbers are from the UTF-8 packaging.   * The magic numbers are from the UTF-8 packaging.
  * They're not as scary as they seem: read the UTF-8 spec for details.   * They're not as scary as they seem: read the UTF-8 spec for details.
  */   */
 static size_t  static size_t
 norm_utf8(unsigned int cp, char out[7])  norm_utf8(unsigned int cp, char out[7])
 {  {
         size_t           rc;          int              rc;
   
         rc = 0;          rc = 0;
   
Line 187  norm_utf8(unsigned int cp, char out[7])
Line 242  norm_utf8(unsigned int cp, char out[7])
                 return(0);                  return(0);
   
         out[rc] = '\0';          out[rc] = '\0';
         return(rc);          return((size_t)rc);
 }  }
   
 /*  /*
Line 205  norm_string(const char *val, const struct mchars *mc, 
Line 260  norm_string(const char *val, const struct mchars *mc, 
         const char       *seq, *cpp;          const char       *seq, *cpp;
         int               len, u, pos;          int               len, u, pos;
         enum mandoc_esc   esc;          enum mandoc_esc   esc;
         static const char res[] = { '\\', '\t',          static const char res[] = { '\\', '\t', ASCII_NBRSP,
                                 ASCII_NBRSP, ASCII_HYPH, '\0' };                          ASCII_HYPH, ASCII_BREAK, '\0' };
   
         /* Pre-allocate by the length of the input */          /* Pre-allocate by the length of the input */
   
Line 226  norm_string(const char *val, const struct mchars *mc, 
Line 281  norm_string(const char *val, const struct mchars *mc, 
                         val += (int)sz;                          val += (int)sz;
                 }                  }
   
                 if (ASCII_HYPH == *val) {                  switch (*val) {
                   case (ASCII_HYPH):
                         (*buf)[pos++] = '-';                          (*buf)[pos++] = '-';
                         val++;                          val++;
                         continue;                          continue;
                 } else if ('\t' == *val || ASCII_NBRSP == *val) {                  case ('\t'):
                           /* FALLTHROUGH */
                   case (ASCII_NBRSP):
                         (*buf)[pos++] = ' ';                          (*buf)[pos++] = ' ';
                         val++;                          val++;
                           /* FALLTHROUGH */
                   case (ASCII_BREAK):
                         continue;                          continue;
                 } else if ('\\' != *val)                  default:
                         break;                          break;
                   }
                   if ('\\' != *val)
                           break;
   
                 /* Read past the slash. */                  /* Read past the slash. */
   
Line 251  norm_string(const char *val, const struct mchars *mc, 
Line 314  norm_string(const char *val, const struct mchars *mc, 
                 if (ESCAPE_ERROR == esc)                  if (ESCAPE_ERROR == esc)
                         break;                          break;
   
                 /*                  /*
                  * XXX - this just does UTF-8, but we need to know                   * XXX - this just does UTF-8, but we need to know
                  * beforehand whether we should do text substitution.                   * beforehand whether we should do text substitution.
                  */                   */
Line 309  index_open(void)
Line 372  index_open(void)
  * Returns 1 if an entry was unpacked, 0 if the database is insane.   * Returns 1 if an entry was unpacked, 0 if the database is insane.
  */   */
 static int  static int
 index_read(const DBT *key, const DBT *val,  index_read(const DBT *key, const DBT *val, int index,
                 const struct mchars *mc, struct rec *rec)                  const struct mchars *mc, struct res *rec)
 {  {
         size_t           left;          size_t           left;
         char            *np, *cp;          char            *np, *cp;
           char             type;
   
 #define INDEX_BREAD(_dst) \  #define INDEX_BREAD(_dst) \
         do { \          do { \
Line 324  index_read(const DBT *key, const DBT *val, 
Line 388  index_read(const DBT *key, const DBT *val, 
                 cp = np + 1; \                  cp = np + 1; \
         } while (/* CONSTCOND */ 0)          } while (/* CONSTCOND */ 0)
   
         left = val->size;          if (0 == (left = val->size))
         cp = (char *)val->data;                  return(0);
   
         rec->res.rec = *(recno_t *)key->data;          cp = val->data;
           assert(sizeof(recno_t) == key->size);
           memcpy(&rec->rec, key->data, key->size);
           rec->volume = index;
   
         INDEX_BREAD(rec->res.file);          if ('d' == (type = *cp++))
         INDEX_BREAD(rec->res.cat);                  rec->type = RESTYPE_MDOC;
         INDEX_BREAD(rec->res.title);          else if ('a' == type)
         INDEX_BREAD(rec->res.arch);                  rec->type = RESTYPE_MAN;
         INDEX_BREAD(rec->res.desc);          else if ('c' == type)
                   rec->type = RESTYPE_CAT;
           else
                   return(0);
   
           left--;
           INDEX_BREAD(rec->file);
           INDEX_BREAD(rec->cat);
           INDEX_BREAD(rec->title);
           INDEX_BREAD(rec->arch);
           INDEX_BREAD(rec->desc);
         return(1);          return(1);
 }  }
   
 /*  /*
  * Search the mandocdb database for the expression "expr".   * Search mandocdb databases in paths for expression "expr".
  * Filter out by "opts".   * Filter out by "opts".
  * Call "res" with the results, which may be zero.   * Call "res" with the results, which may be zero.
  * Return 0 if there was a database error, else return 1.   * Return 0 if there was a database error, else return 1.
  */   */
 int  int
 apropos_search(const struct opts *opts, const struct expr *expr,  apropos_search(int pathsz, char **paths, const struct opts *opts,
                 size_t terms, void *arg,                  const struct expr *expr, size_t terms, void *arg,
                   size_t *sz, struct res **resp,
                 void (*res)(struct res *, size_t, void *))                  void (*res)(struct res *, size_t, void *))
 {  {
         int              i, rsz, root, leaf, mask, mlen, rc, ch;          struct rectree   tree;
           struct mchars   *mc;
           int              i;
   
           memset(&tree, 0, sizeof(struct rectree));
   
           mc = mchars_alloc();
           *sz = 0;
           *resp = NULL;
   
           /*
            * Main loop.  Change into the directory containing manpage
            * databases.  Run our expession over each database in the set.
            */
   
           for (i = 0; i < pathsz; i++) {
                   assert('/' == paths[i][0]);
                   if (chdir(paths[i]))
                           continue;
                   if (single_search(&tree, opts, expr, terms, mc, i))
                           continue;
   
                   resfree(tree.node, tree.len);
                   mchars_free(mc);
                   return(0);
           }
   
           (*res)(tree.node, tree.len, arg);
           *sz = tree.len;
           *resp = tree.node;
           mchars_free(mc);
           return(1);
   }
   
   static int
   single_search(struct rectree *tree, const struct opts *opts,
                   const struct expr *expr, size_t terms,
                   struct mchars *mc, int vol)
   {
           int              root, leaf, ch;
         DBT              key, val;          DBT              key, val;
         DB              *btree, *idx;          DB              *btree, *idx;
         struct mchars   *mc;  
         char            *buf;          char            *buf;
           struct res      *rs;
           struct res       r;
           uint64_t         mask;
         recno_t          rec;          recno_t          rec;
         struct rec      *rs;  
         struct res      *ress;  
         struct rec       r;  
   
         rc      = 0;  
         root    = -1;          root    = -1;
         leaf    = -1;          leaf    = -1;
         btree   = NULL;          btree   = NULL;
         idx     = NULL;          idx     = NULL;
         mc      = NULL;  
         buf     = NULL;          buf     = NULL;
         rs      = NULL;          rs      = tree->node;
         rsz     = 0;  
   
         memset(&r, 0, sizeof(struct rec));          memset(&r, 0, sizeof(struct res));
   
         mc = mchars_alloc();          if (NULL == (btree = btree_open()))
                   return(1);
   
         if (NULL == (btree = btree_open()))          if (NULL == (idx = index_open())) {
                 goto out;                  (*btree->close)(btree);
         if (NULL == (idx = index_open()))                  return(1);
                 goto out;          }
   
         while (0 == (ch = (*btree->seq)(btree, &key, &val, R_NEXT))) {          while (0 == (ch = (*btree->seq)(btree, &key, &val, R_NEXT))) {
                 /*                  if ( ! btree_read(&key, &val, mc, &mask, &rec, &buf))
                  * Low-water mark for key and value.  
                  * The key must have something in it, and the value must  
                  * have the correct tags/recno mix.  
                  */  
                 if (key.size < 2 || 8 != val.size)  
                         break;                          break;
                 if ( ! btree_read(&key, mc, &buf))  
                         break;  
   
                 mask = *(int *)val.data;  
   
                 /*                  /*
                  * See if this keyword record matches any of the                   * See if this keyword record matches any of the
                  * expressions we have stored.                   * expressions we have stored.
Line 397  apropos_search(const struct opts *opts, const struct e
Line 502  apropos_search(const struct opts *opts, const struct e
                 if ( ! exprmark(expr, buf, mask, NULL))                  if ( ! exprmark(expr, buf, mask, NULL))
                         continue;                          continue;
   
                 memcpy(&rec, val.data + 4, sizeof(recno_t));  
   
                 /*                  /*
                  * O(log n) scan for prior records.  Since a record                   * O(log n) scan for prior records.  Since a record
                  * number is unbounded, this has decent performance over                   * number is unbounded, this has decent performance over
Line 406  apropos_search(const struct opts *opts, const struct e
Line 509  apropos_search(const struct opts *opts, const struct e
                  */                   */
   
                 for (leaf = root; leaf >= 0; )                  for (leaf = root; leaf >= 0; )
                         if (rec > rs[leaf].res.rec &&                          if (rec > rs[leaf].rec &&
                                         rs[leaf].rhs >= 0)                                          rs[leaf].rhs >= 0)
                                 leaf = rs[leaf].rhs;                                  leaf = rs[leaf].rhs;
                         else if (rec < rs[leaf].res.rec &&                          else if (rec < rs[leaf].rec &&
                                         rs[leaf].lhs >= 0)                                          rs[leaf].lhs >= 0)
                                 leaf = rs[leaf].lhs;                                  leaf = rs[leaf].lhs;
                         else                          else
                                 break;                                  break;
   
                 /*                  /*
Line 421  apropos_search(const struct opts *opts, const struct e
Line 524  apropos_search(const struct opts *opts, const struct e
                  * try to evaluate it now and continue anyway.                   * try to evaluate it now and continue anyway.
                  */                   */
   
                 if (leaf >= 0 && rs[leaf].res.rec == rec) {                  if (leaf >= 0 && rs[leaf].rec == rec) {
                         if (0 == rs[leaf].matched)                          if (0 == rs[leaf].matched)
                                 exprexec(expr, buf, mask, &rs[leaf]);                                  exprexec(expr, buf, mask, &rs[leaf]);
                         continue;                          continue;
Line 440  apropos_search(const struct opts *opts, const struct e
Line 543  apropos_search(const struct opts *opts, const struct e
                         break;                          break;
   
                 r.lhs = r.rhs = -1;                  r.lhs = r.rhs = -1;
                 if ( ! index_read(&key, &val, mc, &r))                  if ( ! index_read(&key, &val, vol, mc, &r))
                         break;                          break;
   
                 /* XXX: this should be elsewhere, I guess? */                  /* XXX: this should be elsewhere, I guess? */
   
                 if (opts->cat && strcasecmp(opts->cat, r.res.cat))                  if (opts->cat && strcasecmp(opts->cat, r.cat))
                         continue;                          continue;
                 if (opts->arch && strcasecmp(opts->arch, r.res.arch))  
                         continue;  
   
                 rs = mandoc_realloc                  if (opts->arch && *r.arch)
                         (rs, (rsz + 1) * sizeof(struct rec));                          if (strcasecmp(opts->arch, r.arch))
                                   continue;
   
                 memcpy(&rs[rsz], &r, sizeof(struct rec));                  tree->node = rs = mandoc_reallocarray(rs,
                 rs[rsz].matches = mandoc_calloc(terms, sizeof(int));                      tree->len + 1, sizeof(struct res));
   
                 exprexec(expr, buf, mask, &rs[rsz]);                  memcpy(&rs[tree->len], &r, sizeof(struct res));
                   memset(&r, 0, sizeof(struct res));
                   rs[tree->len].matches =
                           mandoc_calloc(terms, sizeof(int));
   
                   exprexec(expr, buf, mask, &rs[tree->len]);
   
                 /* Append to our tree. */                  /* Append to our tree. */
   
                 if (leaf >= 0) {                  if (leaf >= 0) {
                         if (rec > rs[leaf].res.rec)                          if (rec > rs[leaf].rec)
                                 rs[leaf].rhs = rsz;                                  rs[leaf].rhs = tree->len;
                         else                          else
                                 rs[leaf].lhs = rsz;                                  rs[leaf].lhs = tree->len;
                 } else                  } else
                         root = rsz;                          root = tree->len;
   
                 memset(&r, 0, sizeof(struct rec));  
                 rsz++;  
         }  
   
         /*  
          * If we haven't encountered any database errors, then construct  
          * an array of results and push them to the caller.  
          */  
   
         if (1 == ch) {                  tree->len++;
                 for (mlen = i = 0; i < rsz; i++)  
                         if (rs[i].matched)  
                                 mlen++;  
                 ress = mandoc_malloc(mlen * sizeof(struct res));  
                 for (mlen = i = 0; i < rsz; i++)  
                         if (rs[i].matched)  
                                 memcpy(&ress[mlen++], &rs[i].res,  
                                                 sizeof(struct res));  
                 (*res)(ress, mlen, arg);  
                 free(ress);  
                 rc = 1;  
         }          }
   
 out:          (*btree->close)(btree);
         for (i = 0; i < rsz; i++)          (*idx->close)(idx);
                 recfree(&rs[i]);  
   
         recfree(&r);          free(buf);
           RESFREE(&r);
           return(1 == ch);
   }
   
         if (mc)  void
                 mchars_free(mc);  resfree(struct res *rec, size_t sz)
         if (btree)  {
                 (*btree->close)(btree);          size_t           i;
         if (idx)  
                 (*idx->close)(idx);  
   
         free(buf);          for (i = 0; i < sz; i++)
         free(rs);                  RESFREE(&rec[i]);
         return(rc);          free(rec);
 }  }
   
 static void  /*
 recfree(struct rec *rec)   * Compile a list of straight-up terms.
    * The arguments are re-written into ~[[:<:]]term[[:>:]], or "term"
    * surrounded by word boundaries, then pumped through exprterm().
    * Terms are case-insensitive.
    * This emulates whatis(1) behaviour.
    */
   struct expr *
   termcomp(int argc, char *argv[], size_t *tt)
 {  {
           char            *buf;
           int              pos;
           struct expr     *e, *next;
           size_t           sz;
   
         free(rec->res.file);          buf = NULL;
         free(rec->res.cat);          e = NULL;
         free(rec->res.title);          *tt = 0;
         free(rec->res.arch);  
         free(rec->res.desc);  
   
         free(rec->matches);          for (pos = argc - 1; pos >= 0; pos--) {
                   sz = strlen(argv[pos]) + 18;
                   buf = mandoc_realloc(buf, sz);
                   strlcpy(buf, "Nm~[[:<:]]", sz);
                   strlcat(buf, argv[pos], sz);
                   strlcat(buf, "[[:>:]]", sz);
                   if (NULL == (next = exprterm(buf, 0))) {
                           free(buf);
                           exprfree(e);
                           return(NULL);
                   }
                   next->next = e;
                   e = next;
                   (*tt)++;
           }
   
           free(buf);
           return(e);
 }  }
   
   /*
    * Compile a sequence of logical expressions.
    * See apropos.1 for a grammar of this sequence.
    */
 struct expr *  struct expr *
 exprcomp(int argc, char *argv[], size_t *tt)  exprcomp(int argc, char *argv[], size_t *tt)
 {  {
Line 575  exprexpr(int argc, char *argv[], int *pos, int *lvl, s
Line 693  exprexpr(int argc, char *argv[], int *pos, int *lvl, s
                 log = 0;                  log = 0;
   
                 if (NULL != e && 0 == strcmp("-a", argv[*pos]))                  if (NULL != e && 0 == strcmp("-a", argv[*pos]))
                         log = 1;                          log = 1;
                 else if (NULL != e && 0 == strcmp("-o", argv[*pos]))                  else if (NULL != e && 0 == strcmp("-o", argv[*pos]))
                         log = 2;                          log = 2;
   
Line 592  exprexpr(int argc, char *argv[], int *pos, int *lvl, s
Line 710  exprexpr(int argc, char *argv[], int *pos, int *lvl, s
                         ++(*pos);                          ++(*pos);
                         ++(*lvl);                          ++(*lvl);
                         next = mandoc_calloc(1, sizeof(struct expr));                          next = mandoc_calloc(1, sizeof(struct expr));
                         next->cs = 1;  
                         next->subexpr = exprexpr(argc, argv, pos, lvl, tt);                          next->subexpr = exprexpr(argc, argv, pos, lvl, tt);
                         if (NULL == next->subexpr) {                          if (NULL == next->subexpr) {
                                 free(next);                                  free(next);
Line 643  exprterm(char *buf, int cs)
Line 760  exprterm(char *buf, int cs)
   
         memset(&e, 0, sizeof(struct expr));          memset(&e, 0, sizeof(struct expr));
   
         e.cs = cs;  
   
         /* Choose regex or substring match. */          /* Choose regex or substring match. */
   
         if (NULL == (e.v = strpbrk(buf, "=~"))) {          if (NULL == (e.v = strpbrk(buf, "=~"))) {
Line 671  exprterm(char *buf, int cs)
Line 786  exprterm(char *buf, int cs)
                 e.mask = TYPE_Nm | TYPE_Nd;                  e.mask = TYPE_Nm | TYPE_Nd;
   
         if (e.regex) {          if (e.regex) {
                 i = REG_EXTENDED | REG_NOSUB | cs ? 0 : REG_ICASE;                  i = REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE);
                 if (regcomp(&e.re, e.v, i))                  if (regcomp(&e.re, e.v, i))
                         return(NULL);                          return(NULL);
         }          }
Line 687  void
Line 802  void
 exprfree(struct expr *p)  exprfree(struct expr *p)
 {  {
         struct expr     *pp;          struct expr     *pp;
   
         while (NULL != p) {          while (NULL != p) {
                 if (p->subexpr)                  if (p->subexpr)
                         exprfree(p->subexpr);                          exprfree(p->subexpr);
Line 701  exprfree(struct expr *p)
Line 816  exprfree(struct expr *p)
 }  }
   
 static int  static int
 exprmark(const struct expr *p, const char *cp, int mask, int *ms)  exprmark(const struct expr *p, const char *cp,
                   uint64_t mask, int *ms)
 {  {
   
         for ( ; p; p = p->next) {          for ( ; p; p = p->next) {
Line 715  exprmark(const struct expr *p, const char *cp, int mas
Line 831  exprmark(const struct expr *p, const char *cp, int mas
                 if (p->regex) {                  if (p->regex) {
                         if (regexec(&p->re, cp, 0, NULL, 0))                          if (regexec(&p->re, cp, 0, NULL, 0))
                                 continue;                                  continue;
                 } else if (p->cs) {                  } else if (NULL == strcasestr(cp, p->v))
                         if (NULL == strstr(cp, p->v))                          continue;
                                 continue;  
                 } else {  
                         if (NULL == strcasestr(cp, p->v))  
                                 continue;  
                 }  
   
                 if (NULL == ms)                  if (NULL == ms)
                         return(1);                          return(1);
Line 755  expreval(const struct expr *p, int *ms)
Line 866  expreval(const struct expr *p, int *ms)
                 for ( ; p->next && p->next->and; p = p->next) {                  for ( ; p->next && p->next->and; p = p->next) {
                         /* Evaluate a subexpression, if applicable. */                          /* Evaluate a subexpression, if applicable. */
                         if (p->next->subexpr && ! ms[p->next->index])                          if (p->next->subexpr && ! ms[p->next->index])
                                 ms[p->next->index] =                                  ms[p->next->index] =
                                         expreval(p->next->subexpr, ms);                                          expreval(p->next->subexpr, ms);
                         match = match && ms[p->next->index];                          match = match && ms[p->next->index];
                 }                  }
Line 772  expreval(const struct expr *p, int *ms)
Line 883  expreval(const struct expr *p, int *ms)
  * If this evaluates to true, mark the expression as satisfied.   * If this evaluates to true, mark the expression as satisfied.
  */   */
 static void  static void
 exprexec(const struct expr *p, const char *cp, int mask, struct rec *r)  exprexec(const struct expr *e, const char *cp,
                   uint64_t mask, struct res *r)
 {  {
   
         assert(0 == r->matched);          assert(0 == r->matched);
         exprmark(p, cp, mask, r->matches);          exprmark(e, cp, mask, r->matches);
         r->matched = expreval(p, r->matches);          r->matched = expreval(e, r->matches);
 }  }

Legend:
Removed from v.1.5  
changed lines
  Added in v.1.32.2.6

CVSweb