[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.1 and 1.32.2.1

version 1.1, 2011/11/13 10:12:05 version 1.32.2.1, 2013/10/02 21:03:26
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>
  *   *
  * 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 14 
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(__linux__)
   # include <endian.h>
 # include <db_185.h>  # include <db_185.h>
   #elif defined(__APPLE__)
   # include <libkern/OSByteOrder.h>
   # include <db.h>
 #else  #else
   # include <sys/endian.h>
 # include <db.h>  # include <db.h>
 #endif  #endif
   
   #include "mandocdb.h"
 #include "apropos_db.h"  #include "apropos_db.h"
 #include "mandoc.h"  #include "mandoc.h"
   
 enum    match {  #define RESFREE(_x) \
         MATCH_REGEX,          do { \
         MATCH_REGEXCASE,                  free((_x)->file); \
         MATCH_STR,                  free((_x)->cat); \
         MATCH_STRCASE                  free((_x)->title); \
 };                  free((_x)->arch); \
                   free((_x)->desc); \
                   free((_x)->matches); \
           } while (/*CONSTCOND*/0)
   
 struct  expr {  struct  expr {
         enum match       match;          int              regex; /* is regex? */
         int              mask;          int              index; /* index in match array */
         char            *v;          uint64_t         mask; /* type-mask */
         regex_t          re;          int              and; /* is rhs of logical AND? */
           char            *v; /* search value */
           regex_t          re; /* compiled re, if regex */
           struct expr     *next; /* next in sequence */
           struct expr     *subexpr;
 };  };
   
 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_NAME, "name" },          { TYPE_An, "An" },
         { TYPE_FUNCTION, "func" },          { TYPE_Ar, "Ar" },
         { TYPE_UTILITY, "utility" },          { TYPE_At, "At" },
         { TYPE_INCLUDES, "incl" },          { TYPE_Bsx, "Bsx" },
         { TYPE_VARIABLE, "var" },          { TYPE_Bx, "Bx" },
         { TYPE_STANDARD, "stand" },          { TYPE_Cd, "Cd" },
         { TYPE_AUTHOR, "auth" },          { TYPE_Cm, "Cm" },
         { TYPE_CONFIG, "conf" },          { TYPE_Dv, "Dv" },
         { TYPE_DESC, "desc" },          { TYPE_Dx, "Dx" },
         { TYPE_XREF, "xref" },          { TYPE_Em, "Em" },
         { TYPE_PATH, "path" },          { TYPE_Er, "Er" },
         { TYPE_ENV, "env" },          { TYPE_Ev, "Ev" },
         { TYPE_ERR, "err" },          { TYPE_Fa, "Fa" },
         { INT_MAX, "all" },          { 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" },
           { UINT64_MAX, "any" },
         { 0, NULL }          { 0, NULL }
 };  };
   
 static  DB      *btree_open(void);  static  DB      *btree_open(void);
 static  int      btree_read(const DBT *, const struct mchars *, char **);  static  int      btree_read(const DBT *, const DBT *,
 static  int      exprexec(const struct expr *, char *);                          const struct mchars *,
                           uint64_t *, recno_t *, char **);
   static  int      expreval(const struct expr *, int *);
   static  void     exprexec(const struct expr *,
                           const char *, uint64_t, struct res *);
   static  int      exprmark(const struct expr *,
                           const char *, uint64_t, int *);
   static  struct expr *exprexpr(int, char *[], int *, int *, size_t *);
   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  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 87  btree_open(void)
Line 151  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 101  btree_open(void)
Line 166  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 164  norm_utf8(unsigned int cp, char out[7])
Line 236  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 182  norm_string(const char *val, const struct mchars *mc, 
Line 254  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_HYPH, '\0' };                                  ASCII_NBRSP, ASCII_HYPH, '\0' };
   
         /* Pre-allocate by the length of the input */          /* Pre-allocate by the length of the input */
Line 228  norm_string(const char *val, const struct mchars *mc, 
Line 300  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 274  index_open(void)
Line 346  index_open(void)
 {  {
         DB              *db;          DB              *db;
   
         db = dbopen("mandoc.index", O_RDONLY, 0, DB_RECNO, NULL);          db = dbopen(MANDOC_IDX, O_RDONLY, 0, DB_RECNO, NULL);
         if (NULL != db)          if (NULL != db)
                 return(db);                  return(db);
   
Line 286  index_open(void)
Line 358  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 301  index_read(const DBT *key, const DBT *val, 
Line 374  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->rec = *(recno_t *)key->data;          cp = val->data;
           assert(sizeof(recno_t) == key->size);
           memcpy(&rec->rec, key->data, key->size);
           rec->volume = index;
   
           if ('d' == (type = *cp++))
                   rec->type = RESTYPE_MDOC;
           else if ('a' == type)
                   rec->type = RESTYPE_MAN;
           else if ('c' == type)
                   rec->type = RESTYPE_CAT;
           else
                   return(0);
   
           left--;
         INDEX_BREAD(rec->file);          INDEX_BREAD(rec->file);
         INDEX_BREAD(rec->cat);          INDEX_BREAD(rec->cat);
         INDEX_BREAD(rec->title);          INDEX_BREAD(rec->title);
Line 315  index_read(const DBT *key, const DBT *val, 
Line 401  index_read(const DBT *key, const DBT *val, 
 }  }
   
 /*  /*
  * 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.
  */   */
 void  int
 apropos_search(const struct opts *opts, const struct expr *expr,  apropos_search(int pathsz, char **paths, const struct opts *opts,
                 void *arg, void (*res)(struct rec *, size_t, void *))                  const struct expr *expr, size_t terms, void *arg,
                   size_t *sz, struct res **resp,
                   void (*res)(struct res *, size_t, void *))
 {  {
         int              i, len, root, leaf;          struct rectree   tree;
           struct mchars   *mc;
           int              i, rc;
   
           memset(&tree, 0, sizeof(struct rectree));
   
           rc = 0;
           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;  
         int              ch;  
         char            *buf;          char            *buf;
           struct res      *rs;
           struct res       r;
           uint64_t         mask;
         recno_t          rec;          recno_t          rec;
         struct rec      *recs;  
         struct rec       srec;  
   
         root    = -1;          root    = -1;
         leaf    = -1;          leaf    = -1;
         btree   = NULL;          btree   = NULL;
         idx     = NULL;          idx     = NULL;
         mc      = NULL;  
         buf     = NULL;          buf     = NULL;
         recs    = NULL;          rs      = tree->node;
         len     = 0;  
   
         memset(&srec, 0, sizeof(struct rec));          memset(&r, 0, sizeof(struct res));
   
         /* XXX: error out with bad regexp? */          if (NULL == (btree = btree_open()))
                   return(1);
   
         mc = mchars_alloc();          if (NULL == (idx = index_open())) {
                   (*btree->close)(btree);
                   return(1);
           }
   
         /* XXX: return fact that we've errored? */  
   
         if (NULL == (btree = btree_open()))  
                 goto out;  
         if (NULL == (idx = index_open()))  
                 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;  
   
                 if ( ! exprexec(expr, buf))                  /*
                    * See if this keyword record matches any of the
                    * expressions we have stored.
                    */
                   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 378  apropos_search(const struct opts *opts, const struct e
Line 496  apropos_search(const struct opts *opts, const struct e
                  */                   */
   
                 for (leaf = root; leaf >= 0; )                  for (leaf = root; leaf >= 0; )
                         if (rec > recs[leaf].rec && recs[leaf].rhs >= 0)                          if (rec > rs[leaf].rec &&
                                 leaf = recs[leaf].rhs;                                          rs[leaf].rhs >= 0)
                         else if (rec < recs[leaf].rec && recs[leaf].lhs >= 0)                                  leaf = rs[leaf].rhs;
                                 leaf = recs[leaf].lhs;                          else if (rec < rs[leaf].rec &&
                         else                                          rs[leaf].lhs >= 0)
                                   leaf = rs[leaf].lhs;
                           else
                                 break;                                  break;
   
                 if (leaf >= 0 && recs[leaf].rec == rec)                  /*
                    * If we find a record, see if it has already evaluated
                    * to true.  If it has, great, just keep going.  If not,
                    * try to evaluate it now and continue anyway.
                    */
   
                   if (leaf >= 0 && rs[leaf].rec == rec) {
                           if (0 == rs[leaf].matched)
                                   exprexec(expr, buf, mask, &rs[leaf]);
                         continue;                          continue;
                   }
   
                 /*                  /*
                  * Now we actually extract the manpage's metadata from                   * We have a new file to examine.
                  * the index database.                   * Extract the manpage's metadata from the index
                    * database, then begin partial evaluation.
                  */                   */
   
                 key.data = &rec;                  key.data = &rec;
Line 399  apropos_search(const struct opts *opts, const struct e
Line 529  apropos_search(const struct opts *opts, const struct e
                 if (0 != (*idx->get)(idx, &key, &val, 0))                  if (0 != (*idx->get)(idx, &key, &val, 0))
                         break;                          break;
   
                 srec.lhs = srec.rhs = -1;                  r.lhs = r.rhs = -1;
                 if ( ! index_read(&key, &val, mc, &srec))                  if ( ! index_read(&key, &val, vol, mc, &r))
                         break;                          break;
   
                 if (opts->cat && strcasecmp(opts->cat, srec.cat))                  /* XXX: this should be elsewhere, I guess? */
   
                   if (opts->cat && strcasecmp(opts->cat, r.cat))
                         continue;                          continue;
                 if (opts->arch && strcasecmp(opts->arch, srec.arch))  
                         continue;  
   
                 recs = mandoc_realloc                  if (opts->arch && *r.arch)
                         (recs, (len + 1) * sizeof(struct rec));                          if (strcasecmp(opts->arch, r.arch))
                                   continue;
   
                 memcpy(&recs[len], &srec, sizeof(struct rec));                  tree->node = rs = mandoc_realloc
                           (rs, (tree->len + 1) * sizeof(struct res));
   
                   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 > recs[leaf].rec)                          if (rec > rs[leaf].rec)
                                 recs[leaf].rhs = len;                                  rs[leaf].rhs = tree->len;
                         else                          else
                                 recs[leaf].lhs = len;                                  rs[leaf].lhs = tree->len;
                 } else                  } else
                         root = len;                          root = tree->len;
   
                 memset(&srec, 0, sizeof(struct rec));                  tree->len++;
                 len++;  
         }          }
   
         if (1 == ch)          (*btree->close)(btree);
                 (*res)(recs, len, arg);          (*idx->close)(idx);
   
         /* XXX: else?  corrupt database error? */          free(buf);
 out:          RESFREE(&r);
         for (i = 0; i < len; i++) {          return(1 == ch);
                 free(recs[i].file);  }
                 free(recs[i].cat);  
                 free(recs[i].title);  
                 free(recs[i].arch);  
                 free(recs[i].desc);  
         }  
   
         free(srec.file);  void
         free(srec.cat);  resfree(struct res *rec, size_t sz)
         free(srec.title);  {
         free(srec.arch);          size_t           i;
         free(srec.desc);  
   
         if (mc)          for (i = 0; i < sz; i++)
                 mchars_free(mc);                  RESFREE(&rec[i]);
         if (btree)          free(rec);
                 (*btree->close)(btree);  }
         if (idx)  
                 (*idx->close)(idx);  
   
   /*
    * 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;
   
           buf = NULL;
           e = NULL;
           *tt = 0;
   
           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);          free(buf);
         free(recs);          return(e);
 }  }
   
   /*
    * Compile a sequence of logical expressions.
    * See apropos.1 for a grammar of this sequence.
    */
 struct expr *  struct expr *
 exprcomp(int cs, char *argv[], int argc)  exprcomp(int argc, char *argv[], size_t *tt)
 {  {
         struct expr     *p;          int              pos, lvl;
         struct expr      e;          struct expr     *e;
         int              i, pos, ch;  
   
         pos = 0;          pos = lvl = 0;
           *tt = 0;
   
         if (pos > argc)          e = exprexpr(argc, argv, &pos, &lvl, tt);
                 return(NULL);  
   
         for (i = 0; 0 != types[i].mask; i++)          if (0 == lvl && pos >= argc)
                 if (0 == strcmp(types[i].name, argv[pos]))                  return(e);
   
           exprfree(e);
           return(NULL);
   }
   
   /*
    * Compile an array of tokens into an expression.
    * An informal expression grammar is defined in apropos(1).
    * Return NULL if we fail doing so.  All memory will be cleaned up.
    * Return the root of the expression sequence if alright.
    */
   static struct expr *
   exprexpr(int argc, char *argv[], int *pos, int *lvl, size_t *tt)
   {
           struct expr     *e, *first, *next;
           int              log;
   
           first = next = NULL;
   
           for ( ; *pos < argc; (*pos)++) {
                   e = next;
   
                   /*
                    * Close out a subexpression.
                    */
   
                   if (NULL != e && 0 == strcmp(")", argv[*pos])) {
                           if (--(*lvl) < 0)
                                   goto err;
                         break;                          break;
                   }
   
         if (0 == (e.mask = types[i].mask))                  /*
                 return(NULL);                   * Small note: if we're just starting, don't let "-a"
                    * and "-o" be considered logical operators: they're
                    * just tokens unless pairwise joining, in which case we
                    * record their existence (or assume "OR").
                    */
                   log = 0;
   
         if (++pos > argc--)                  if (NULL != e && 0 == strcmp("-a", argv[*pos]))
                 return(NULL);                          log = 1;
                   else if (NULL != e && 0 == strcmp("-o", argv[*pos]))
                           log = 2;
   
         if ('-' != *argv[pos])                  if (log > 0 && ++(*pos) >= argc)
                 e.match = cs ? MATCH_STRCASE : MATCH_STR;                          goto err;
         else if (0 == strcmp("-eq", argv[pos]))  
                 e.match = cs ? MATCH_STRCASE : MATCH_STR;  
         else if (0 == strcmp("-ieq", argv[pos]))  
                 e.match = MATCH_STRCASE;  
         else if (0 == strcmp("-re", argv[pos]))  
                 e.match = cs ? MATCH_REGEXCASE : MATCH_REGEX;  
         else if (0 == strcmp("-ire", argv[pos]))  
                 e.match = MATCH_REGEXCASE;  
         else  
                 return(NULL);  
   
         if ('-' == *argv[pos])                  /*
                 pos++;                   * Now we parse the term part.  This can begin with
                    * "-i", in which case the expression is case
                    * insensitive.
                    */
   
         if (pos > argc--)                  if (0 == strcmp("(", argv[*pos])) {
                 return(NULL);                          ++(*pos);
                           ++(*lvl);
                           next = mandoc_calloc(1, sizeof(struct expr));
                           next->subexpr = exprexpr(argc, argv, pos, lvl, tt);
                           if (NULL == next->subexpr) {
                                   free(next);
                                   next = NULL;
                           }
                   } else if (0 == strcmp("-i", argv[*pos])) {
                           if (++(*pos) >= argc)
                                   goto err;
                           next = exprterm(argv[*pos], 0);
                   } else
                           next = exprterm(argv[*pos], 1);
   
         e.v = mandoc_strdup(argv[pos]);                  if (NULL == next)
                           goto err;
   
         if (MATCH_REGEX == e.match || MATCH_REGEXCASE == e.match) {                  next->and = log == 1;
                 ch = REG_EXTENDED | REG_NOSUB;                  next->index = (int)(*tt)++;
                 if (MATCH_REGEXCASE == e.match)  
                         ch |= REG_ICASE;                  /* Append to our chain of expressions. */
                 if (regcomp(&e.re, e.v, ch))  
                   if (NULL == first) {
                           assert(NULL == e);
                           first = next;
                   } else {
                           assert(NULL != e);
                           e->next = next;
                   }
           }
   
           return(first);
   err:
           exprfree(first);
           return(NULL);
   }
   
   /*
    * Parse a terminal expression with the grammar as defined in
    * apropos(1).
    * Return NULL if we fail the parse.
    */
   static struct expr *
   exprterm(char *buf, int cs)
   {
           struct expr      e;
           struct expr     *p;
           char            *key;
           int              i;
   
           memset(&e, 0, sizeof(struct expr));
   
           /* Choose regex or substring match. */
   
           if (NULL == (e.v = strpbrk(buf, "=~"))) {
                   e.regex = 0;
                   e.v = buf;
           } else {
                   e.regex = '~' == *e.v;
                   *e.v++ = '\0';
           }
   
           /* Determine the record types to search for. */
   
           e.mask = 0;
           if (buf < e.v) {
                   while (NULL != (key = strsep(&buf, ","))) {
                           i = 0;
                           while (types[i].mask &&
                                           strcmp(types[i].name, key))
                                   i++;
                           e.mask |= types[i].mask;
                   }
           }
           if (0 == e.mask)
                   e.mask = TYPE_Nm | TYPE_Nd;
   
           if (e.regex) {
                   i = REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE);
                   if (regcomp(&e.re, e.v, i))
                         return(NULL);                          return(NULL);
         }          }
   
           e.v = mandoc_strdup(e.v);
   
         p = mandoc_calloc(1, sizeof(struct expr));          p = mandoc_calloc(1, sizeof(struct expr));
         memcpy(p, &e, sizeof(struct expr));          memcpy(p, &e, sizeof(struct expr));
         return(p);          return(p);
Line 516  exprcomp(int cs, char *argv[], int argc)
Line 788  exprcomp(int cs, char *argv[], int argc)
 void  void
 exprfree(struct expr *p)  exprfree(struct expr *p)
 {  {
           struct expr     *pp;
   
         if (NULL == p)          while (NULL != p) {
                 return;                  if (p->subexpr)
                           exprfree(p->subexpr);
                   if (p->regex)
                           regfree(&p->re);
                   free(p->v);
                   pp = p->next;
                   free(p);
                   p = pp;
           }
   }
   
         if (MATCH_REGEX == p->match)  static int
                 regfree(&p->re);  exprmark(const struct expr *p, const char *cp,
                   uint64_t mask, int *ms)
   {
   
         free(p->v);          for ( ; p; p = p->next) {
         free(p);                  if (p->subexpr) {
                           if (exprmark(p->subexpr, cp, mask, ms))
                                   return(1);
                           continue;
                   } else if ( ! (mask & p->mask))
                           continue;
   
                   if (p->regex) {
                           if (regexec(&p->re, cp, 0, NULL, 0))
                                   continue;
                   } else if (NULL == strcasestr(cp, p->v))
                           continue;
   
                   if (NULL == ms)
                           return(1);
                   else
                           ms[p->index] = 1;
           }
   
           return(0);
 }  }
   
 static int  static int
 exprexec(const struct expr *p, char *cp)  expreval(const struct expr *p, int *ms)
 {  {
           int              match;
   
         if (MATCH_STR == p->match)          /*
                 return(0 == strcmp(p->v, cp));           * AND has precedence over OR.  Analysis is left-right, though
         else if (MATCH_STRCASE == p->match)           * it doesn't matter because there are no side-effects.
                 return(0 == strcasecmp(p->v, cp));           * Thus, step through pairwise ANDs and accumulate their Boolean
            * evaluation.  If we encounter a single true AND collection or
            * standalone term, the whole expression is true (by definition
            * of OR).
            */
   
         assert(MATCH_REGEX == p->match);          for (match = 0; p && ! match; p = p->next) {
         return(0 == regexec(&p->re, cp, 0, NULL, 0));                  /* Evaluate a subexpression, if applicable. */
                   if (p->subexpr && ! ms[p->index])
                           ms[p->index] = expreval(p->subexpr, ms);
   
                   match = ms[p->index];
                   for ( ; p->next && p->next->and; p = p->next) {
                           /* Evaluate a subexpression, if applicable. */
                           if (p->next->subexpr && ! ms[p->next->index])
                                   ms[p->next->index] =
                                           expreval(p->next->subexpr, ms);
                           match = match && ms[p->next->index];
                   }
           }
   
           return(match);
   }
   
   /*
    * First, update the array of terms for which this expression evaluates
    * to true.
    * Second, logically evaluate all terms over the updated array of truth
    * values.
    * If this evaluates to true, mark the expression as satisfied.
    */
   static void
   exprexec(const struct expr *e, const char *cp,
                   uint64_t mask, struct res *r)
   {
   
           assert(0 == r->matched);
           exprmark(e, cp, mask, r->matches);
           r->matched = expreval(e, r->matches);
 }  }

Legend:
Removed from v.1.1  
changed lines
  Added in v.1.32.2.1

CVSweb