[git] GnuPG - branch, master, updated. gnupg-2.1.13-99-g5137bf7

by Werner Koch cvs at cvs.gnupg.org
Fri Jul 1 16:30:59 CEST 2016


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "The GNU Privacy Guard".

The branch, master has been updated
       via  5137bf73ccc98a72c2eeac148e4d4b5d58f0a854 (commit)
       via  f015552374d69e28292a12f2b91ab34d65c9b457 (commit)
       via  d8bce478be3ae9e401841a77d189ef3c81ccb757 (commit)
       via  681c6ef757a73fc1a63a552186e038db179494aa (commit)
       via  6446a6b3dfd3b2e68b4285870f902ed1f86b0866 (commit)
      from  49fdd0887c84ed7f7b858b9e7ffa146fcb7f1e87 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 5137bf73ccc98a72c2eeac148e4d4b5d58f0a854
Author: Werner Koch <wk at gnupg.org>
Date:   Fri Jul 1 16:24:04 2016 +0200

    gpg: New option --import-filter
    
    * g10/gpg.c (oImportFilter): New.
    (opts): Add --import-filter.
    (main): Handle option.
    * g10/import.c: Include recsel.h, init.h, and mbox-util.h.
    (import_keep_uid): New global var.
    (cleanup_import_globals): New.
    (parse_and_set_import_filter): New.
    (filter_getval): New.
    (apply_keep_uid_filter): New.
    (import_one): Apply filter if set.
    --
    
    Funny new option.  It can for example be used to export a key with
    only one user id:
    
      gpg --no-options --import --import-options import-export \
          --import-filter keep-uid='mbox=wk at gnupg.org'         \
         < full-key.pub > key-with-one-uid.pub
    
    More features will eventually be added.
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/doc/gpg.texi b/doc/gpg.texi
index 6f0249a..9a06221 100644
--- a/doc/gpg.texi
+++ b/doc/gpg.texi
@@ -2218,6 +2218,45 @@ opposite meaning. The options are:
   Defaults to no.
 @end table
 
+ at item --import-filter @code{@var{name}=@var{expr}}
+ at opindex import-filter
+This option defines an import filter which is implied to the imported
+keyblock right before it will be stored.  @var{name} defines the type
+of filter to use, @var{expr} the expression to evaluate.  The option
+can be used several times which then appends more expression to the
+same @var{name}.
+
+ at noindent
+The available filter types are:
+
+ at table @asis
+
+  @item keep-uid
+  This filter will keep a user id packet and its dependent packets in
+  the keyblock if the expression evaluates to true.
+
+ at end table
+
+The syntax for the expression is defined in the appendix (FIXME).  The
+property names for the expressions depend on the actual filter type
+and are indicated in the following table.
+
+The available properties are:
+
+ at table @asis
+
+  @item uid
+  A string with the user id.  (keep-uid)
+
+  @item mbox
+  The addr-spec part of a user id with mailbox or the empty string.
+  (keep-uid)
+
+  @item primary
+  Boolean indicating whether the user id is the primary one.  (keep-uid)
+
+ at end table
+
 @item --export-options @code{parameters}
 @opindex export-options
 This is a space or comma delimited string that gives options for
diff --git a/g10/gpg.c b/g10/gpg.c
index b1d6c34..009b84c 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -300,6 +300,7 @@ enum cmd_and_opt_values
     oKeyServer,
     oKeyServerOptions,
     oImportOptions,
+    oImportFilter,
     oExportOptions,
     oListOptions,
     oVerifyOptions,
@@ -572,6 +573,7 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_s_s (oKeyServer, "keyserver", "@"),
   ARGPARSE_s_s (oKeyServerOptions, "keyserver-options", "@"),
   ARGPARSE_s_s (oImportOptions, "import-options", "@"),
+  ARGPARSE_s_s (oImportFilter,  "import-filter", "@"),
   ARGPARSE_s_s (oExportOptions, "export-options", "@"),
   ARGPARSE_s_s (oListOptions,   "list-options", "@"),
   ARGPARSE_s_s (oVerifyOptions, "verify-options", "@"),
@@ -2033,6 +2035,7 @@ parse_tofu_db_format (const char *db_format)
     }
 }
 
+
 /* This function called to initialized a new control object.  It is
    assumed that this object has been zeroed out before calling this
    function. */
@@ -3031,6 +3034,11 @@ main (int argc, char **argv)
 		  log_error(_("invalid import options\n"));
 	      }
 	    break;
+	  case oImportFilter:
+	    rc = parse_and_set_import_filter (pargs.r.ret_str);
+	    if (rc)
+              log_error (_("invalid filter option: %s\n"), gpg_strerror (rc));
+	    break;
 	  case oExportOptions:
 	    if(!parse_export_options(pargs.r.ret_str,&opt.export_options,1))
 	      {
diff --git a/g10/import.c b/g10/import.c
index 332e266..deb2787 100644
--- a/g10/import.c
+++ b/g10/import.c
@@ -1,6 +1,6 @@
 /* import.c - import a key into our key storage.
  * Copyright (C) 1998-2007, 2010-2011 Free Software Foundation, Inc.
- * Copyright (C) 2014  Werner Koch
+ * Copyright (C) 2014, 2016  Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -35,9 +35,13 @@
 #include "i18n.h"
 #include "ttyio.h"
 #include "status.h"
+#include "recsel.h"
 #include "keyserver-internal.h"
 #include "call-agent.h"
 #include "../common/membuf.h"
+#include "../common/init.h"
+#include "../common/mbox-util.h"
+
 
 struct import_stats_s
 {
@@ -60,6 +64,16 @@ struct import_stats_s
 };
 
 
+/* A global variable to store the selector created from
+ * --import-filter keep-uid=EXPR.
+ *
+ * FIXME: We should put this into the CTRL object but that requires a
+ * lot more changes right now.
+ */
+static recsel_expr_t import_keep_uid;
+
+
+
 static int import (ctrl_t ctrl,
                    IOBUF inp, const char* fname, struct import_stats_s *stats,
 		   unsigned char **fpr, size_t *fpr_len, unsigned int options,
@@ -95,6 +109,16 @@ static int merge_sigs (kbnode_t dst, kbnode_t src, int *n_sigs,
 static int merge_keysigs (kbnode_t dst, kbnode_t src, int *n_sigs,
 			     const char *fname, u32 *keyid );
 
+
+

+static void
+cleanup_import_globals (void)
+{
+  recsel_release (import_keep_uid);
+  import_keep_uid = NULL;
+}
+
+
 int
 parse_import_options(char *str,unsigned int *options,int noisy)
 {
@@ -143,6 +167,39 @@ parse_import_options(char *str,unsigned int *options,int noisy)
 }
 
 
+/* Parse and set an import filter from string.  STRING has the format
+ * "NAME=EXPR" with NAME being the name of the filter.  Spaces before
+ * and after NAME are not allowed.  If this function is all called
+ * several times all expressions for the same NAME are concatenated.
+ * Supported filter names are:
+ *
+ *  - keep-uid :: If the expression evaluates to true for a certain
+ *                user ID packet, that packet and all it dependencies
+ *                will be imported.  The expression may use these
+ *                variables:
+ *
+ *                - uid  :: The entire user ID.
+ *                - mbox :: The mail box part of the user ID.
+ *                - primary :: Evaluate to true for the primary user ID.
+ */
+gpg_error_t
+parse_and_set_import_filter (const char *string)
+{
+  gpg_error_t err;
+
+  /* Auto register the cleanup function.  */
+  register_mem_cleanup_func (cleanup_import_globals);
+
+  if (!strncmp (string, "keep-uid=", 9))
+    err = recsel_parse_expr (&import_keep_uid, string+9);
+  else
+    err = gpg_error (GPG_ERR_INV_NAME);
+
+  return err;
+}
+
+
+
 import_stats_t
 import_new_stats_handle (void)
 {
@@ -983,6 +1040,74 @@ check_prefs (ctrl_t ctrl, kbnode_t keyblock)
 }
 
 
+/* Helper for apply_keep_uid_filter.  */
+static const char *
+filter_getval (void *cookie, const char *propname)
+{
+  kbnode_t node = cookie;
+  const char *result;
+
+  if (node->pkt->pkttype == PKT_USER_ID)
+    {
+      if (!strcmp (propname, "uid"))
+        result = node->pkt->pkt.user_id->name;
+      else if (!strcmp (propname, "mbox"))
+        {
+          if (!node->pkt->pkt.user_id->mbox)
+            {
+              node->pkt->pkt.user_id->mbox
+                = mailbox_from_userid (node->pkt->pkt.user_id->name);
+            }
+          return node->pkt->pkt.user_id->mbox;
+        }
+      else if (!strcmp (propname, "primary"))
+        result = node->pkt->pkt.user_id->is_primary? "1":"0";
+      else
+        result = NULL;
+    }
+  else
+    result = NULL;
+
+  return result;
+}
+
+/*
+ * Apply the keep-uid filter to the keyblock.  The deleted nodes are
+ * marked and thus the caller should call commit_kbnode afterwards.
+ * KEYBLOCK must not have any blocks marked as deleted.
+ */
+static void
+apply_keep_uid_filter (kbnode_t keyblock, recsel_expr_t selector)
+{
+  kbnode_t node;
+
+  for (node = keyblock->next; node; node = node->next )
+    {
+      if (node->pkt->pkttype == PKT_USER_ID)
+        {
+          if (!recsel_select (selector, filter_getval, node))
+            {
+
+              /* log_debug ("keep-uid: deleting '%s'\n", */
+              /*            node->pkt->pkt.user_id->name); */
+              /* The UID packet and all following packets up to the
+               * next UID or a subkey.  */
+              delete_kbnode (node);
+              for (; node->next
+                     && node->next->pkt->pkttype != PKT_USER_ID
+                     && node->next->pkt->pkttype != PKT_PUBLIC_SUBKEY
+                     && node->next->pkt->pkttype != PKT_SECRET_SUBKEY ;
+                   node = node->next)
+                delete_kbnode (node->next);
+	    }
+          /* else */
+          /*   log_debug ("keep-uid: keeping '%s'\n", */
+          /*              node->pkt->pkt.user_id->name); */
+        }
+    }
+}
+
+
 /*
  * Try to import one keyblock. Return an error only in serious cases,
  * but never for an invalid keyblock.  It uses log_error to increase
@@ -1116,6 +1241,14 @@ import_one (ctrl_t ctrl,
   /* Get rid of deleted nodes.  */
   commit_kbnode (&keyblock);
 
+  /* Apply import filter.  */
+  if (import_keep_uid)
+    {
+      apply_keep_uid_filter (keyblock, import_keep_uid);
+      commit_kbnode (&keyblock);
+    }
+
+
   /* Show the key in the form it is merged or inserted.  We skip this
    * if "import-export" is also active without --armor or the output
    * file has explicily been given. */
diff --git a/g10/main.h b/g10/main.h
index 322f43c..58f2a73 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -349,6 +349,7 @@ typedef struct import_stats_s *import_stats_t;
 typedef gpg_error_t (*import_screener_t)(kbnode_t keyblock, void *arg);
 
 int parse_import_options(char *str,unsigned int *options,int noisy);
+gpg_error_t parse_and_set_import_filter (const char *string);
 void import_keys (ctrl_t ctrl, char **fnames, int nnames,
 		  import_stats_t stats_hd, unsigned int options);
 int import_keys_stream (ctrl_t ctrl, iobuf_t inp, import_stats_t stats_hd,

commit f015552374d69e28292a12f2b91ab34d65c9b457
Author: Werner Koch <wk at gnupg.org>
Date:   Fri Jul 1 15:40:56 2016 +0200

    gpg: Allow to cache the mbox in a user id struct.
    
    * g10/packet.h (PKT_user_id): Add field 'mbox'.
    * g10/free-packet.c (free_user_id): Free that.
    --
    
    This will be required by the coming import filter.
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/g10/free-packet.c b/g10/free-packet.c
index 3883f87..516e9a1 100644
--- a/g10/free-packet.c
+++ b/g10/free-packet.c
@@ -311,6 +311,7 @@ free_user_id (PKT_user_id *uid)
     free_attributes(uid);
     xfree (uid->prefs);
     xfree (uid->namehash);
+    xfree (uid->mbox);
     xfree (uid);
 }
 
diff --git a/g10/packet.h b/g10/packet.h
index a496c4b..08e2cb7 100644
--- a/g10/packet.h
+++ b/g10/packet.h
@@ -291,6 +291,7 @@ typedef struct
     unsigned int ks_modify:1;
     unsigned int compacted:1;
   } flags;
+  char *mbox;   /* NULL or the result of mailbox_from_userid.  */
   /* The text contained in the user id packet, which is normally the
      name and email address of the key holder (See RFC 4880 5.11).
      (Serialized.). For convenience an extra Nul is always appended.  */

commit d8bce478be3ae9e401841a77d189ef3c81ccb757
Author: Werner Koch <wk at gnupg.org>
Date:   Fri Jul 1 15:18:59 2016 +0200

    gpg: Make sure a user ID packet has always a terminating Nul in memory.
    
    * g10/keygen.c (write_uid): Avoid overflow.
    --
    
    Also the actual length if the user ID is given by LEN, using NAME
    diretcly is often more convenient.

diff --git a/g10/keygen.c b/g10/keygen.c
index 3a9a8e7..2b3d328 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -202,7 +202,7 @@ write_uid( KBNODE root, const char *s )
     size_t n = strlen(s);
 
     pkt->pkttype = PKT_USER_ID;
-    pkt->pkt.user_id = xmalloc_clear( sizeof *pkt->pkt.user_id + n - 1 );
+    pkt->pkt.user_id = xmalloc_clear (sizeof *pkt->pkt.user_id + n);
     pkt->pkt.user_id->len = n;
     pkt->pkt.user_id->ref = 1;
     strcpy(pkt->pkt.user_id->name, s);
diff --git a/g10/packet.h b/g10/packet.h
index 0ff28c8..a496c4b 100644
--- a/g10/packet.h
+++ b/g10/packet.h
@@ -293,7 +293,7 @@ typedef struct
   } flags;
   /* The text contained in the user id packet, which is normally the
      name and email address of the key holder (See RFC 4880 5.11).
-     (Serialized.)  */
+     (Serialized.). For convenience an extra Nul is always appended.  */
   char name[1];
 } PKT_user_id;
 

commit 681c6ef757a73fc1a63a552186e038db179494aa
Author: Werner Koch <wk at gnupg.org>
Date:   Thu Jun 30 20:25:46 2016 +0200

    common: Add function to select records etc.
    
    * common/recsel.c, common/recsel.h: New.
    * common/t-recsel.c: New.
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/common/Makefile.am b/common/Makefile.am
index 2451689..6f9d96d 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -91,7 +91,8 @@ common_sources = \
 	call-gpg.c call-gpg.h \
 	exectool.c exectool.h \
 	server-help.c server-help.h \
-	name-value.c name-value.h
+	name-value.c name-value.h \
+	recsel.c recsel.h
 
 if HAVE_W32_SYSTEM
 common_sources += w32-reg.c w32-afunix.c w32-afunix.h
@@ -157,7 +158,7 @@ module_tests = t-stringhelp t-timestuff \
                t-convert t-percent t-gettime t-sysutils t-sexputil \
 	       t-session-env t-openpgp-oid t-ssh-utils \
 	       t-mapstrings t-zb32 t-mbox-util t-iobuf t-strlist \
-	       t-name-value t-ccparray
+	       t-name-value t-ccparray t-recsel
 if !HAVE_W32CE_SYSTEM
 module_tests += t-exechelp
 endif
@@ -208,6 +209,7 @@ t_iobuf_LDADD = $(t_common_ldadd)
 t_strlist_LDADD = $(t_common_ldadd)
 t_name_value_LDADD = $(t_common_ldadd)
 t_ccparray_LDADD = $(t_common_ldadd)
+t_recsel_LDADD = $(t_common_ldadd)
 
 # System specific test
 if HAVE_W32_SYSTEM
diff --git a/common/recsel.c b/common/recsel.c
new file mode 100644
index 0000000..b35574f
--- /dev/null
+++ b/common/recsel.c
@@ -0,0 +1,571 @@
+/* recsel.c - Record selection
+ * Copyright (C) 2014, 2016 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of either
+ *
+ *   - the GNU Lesser General Public License as published by the Free
+ *     Software Foundation; either version 3 of the License, or (at
+ *     your option) any later version.
+ *
+ * or
+ *
+ *   - the GNU General Public License as published by the Free
+ *     Software Foundation; either version 2 of the License, or (at
+ *     your option) any later version.
+ *
+ * or both in parallel, as here.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "util.h"
+#include "recsel.h"
+
+/* Select operators.  */
+typedef enum
+  {
+    SELECT_SAME,
+    SELECT_SUB,
+    SELECT_NONEMPTY,
+    SELECT_ISTRUE,
+    SELECT_EQ, /* Numerically equal.  */
+    SELECT_LE,
+    SELECT_GE,
+    SELECT_LT,
+    SELECT_GT
+  } select_op_t;
+
+
+/* Definition for a select expression.  */
+struct recsel_expr_s
+{
+  recsel_expr_t next;
+  select_op_t op;       /* Operation code.  */
+  unsigned int not:1;   /* Negate operators. */
+  unsigned int disjun:1;/* Start of a disjunction.  */
+  unsigned int xcase:1; /* String match is case sensitive.  */
+  const char *value;    /* (Points into NAME.)  */
+  long numvalue;        /* strtol of VALUE.  */
+  char name[1];         /* Name of the property.  */
+};
+
+
+/* Helper */
+static inline gpg_error_t
+my_error_from_syserror (void)
+{
+  return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+}
+
+/* Helper */
+static inline gpg_error_t
+my_error (gpg_err_code_t ec)
+{
+  return gpg_err_make (default_errsource, ec);
+}
+
+
+/* This is a case-sensitive version of our memistr.  I wonder why no
+ * standard function memstr exists but I better do not use the name
+ * memstr to avoid future conflicts.
+ *
+ * FIXME: Move this to a stringhelp.c
+ */
+static const char *
+my_memstr (const void *buffer, size_t buflen, const char *sub)
+{
+  const unsigned char *buf = buffer;
+  const unsigned char *t = (const unsigned char *)buf;
+  const unsigned char *s = (const unsigned char *)sub;
+  size_t n = buflen;
+
+  for ( ; n ; t++, n-- )
+    {
+      if (*t == *s)
+        {
+          for (buf = t++, buflen = n--, s++; n && *t ==*s; t++, s++, n--)
+            ;
+          if (!*s)
+            return (const char*)buf;
+          t = (const unsigned char *)buf;
+          s = (const unsigned char *)sub ;
+          n = buflen;
+	}
+    }
+  return NULL;
+}
+
+
+/* Return a pointer to the next logical connection operator or NULL if
+ * none.  */
+static char *
+find_next_lc (char *string)
+{
+  char *p1, *p2;
+
+  p1 = strchr (string, '&');
+  if (p1 && p1[1] != '&')
+    p1 = NULL;
+  p2 = strchr (string, '|');
+  if (p2 && p2[1] != '|')
+    p2 = NULL;
+  if (p1 && !p2)
+    return p1;
+  if (!p1)
+    return p2;
+  return p1 < p2 ? p1 : p2;
+}
+
+
+/* Parse an expression.  The expression symtax is:
+ *
+ *   [<lc>] {{<flag>} PROPNAME <op> VALUE [<lc>]}
+ *
+ * A [] indicates an optional part, a {} a repetition.  PROPNAME and
+ * VALUE may not be the empty string.  White space between the
+ * elements is ignored.  Numerical values are computed as long int;
+ * standard C notation applies.  <lc> is the logical connection
+ * operator; either "&&" for a conjunction or "||" for a disjunction.
+ * A conjunction is assumed at the begin of an expression and
+ * conjunctions have higher precedence than disjunctions.  If VALUE
+ * starts with one of the characters used in any <op> a space after
+ * the <op> is required.  A VALUE is terminated by an <lc> unless the
+ * "--" <flag> is used in which case the VALUE spans to the end of the
+ * expression.  <op> may be any of
+ *
+ *   =~  Substring must match
+ *   !~  Substring must not match
+ *   =   The full string must match
+ *   <>  The full string must not match
+ *   ==  The numerical value must match
+ *   !=  The numerical value must not match
+ *   <=  The numerical value of the field must be LE than the value.
+ *   <   The numerical value of the field must be LT than the value.
+ *   >=  The numerical value of the field must be GT than the value.
+ *   >=  The numerical value of the field must be GE than the value.
+ *   -n  True if value is not empty (no VALUE parameter allowed).
+ *   -z  True if value is empty (no VALUE parameter allowed).
+ *   -t  Alias for "NAME != 0" (no VALUE parameter allowed).
+ *   -f  Alias for "NAME == 0" (no VALUE parameter allowed).
+ *
+ * Values for <flag> must be space separated and any of:
+ *
+ *   --  VALUE spans to the end of the expression.
+ *   -c  The string match in this part is done case-sensitive.
+ *
+ * For example four calls to recsel_parse_expr() with these values for
+ * EXPR
+ *
+ *  "uid =~ Alfa"
+ *  "&& uid !~ Test"
+ *  "|| uid =~ Alpha"
+ *  "uid !~ Test"
+ *
+ * or the equivalent expression
+ *
+ *  "uid =~ Alfa" && uid !~ Test" || uid =~ Alpha" && "uid !~ Test"
+ *
+ * are making a selector for records where the "uid" property contains
+ * the strings "Alfa" or "Alpha" but not the String "test".
+ *
+ * The caller must pass the address of a selector variable to this
+ * function and initialize the value of the function to NULL before
+ * the first call.  recset_release needs to be called to free the
+ * selector.
+ */
+gpg_error_t
+recsel_parse_expr (recsel_expr_t *selector, const char *expression)
+{
+  recsel_expr_t se_head = NULL;
+  recsel_expr_t se, se2;
+  char *expr_buffer;
+  char *expr;
+  char *s0, *s;
+  int toend = 0;
+  int xcase = 0;
+  int disjun = 0;
+  char *next_lc = NULL;
+
+  while (*expression == ' ' || *expression == '\t')
+    expression++;
+
+  expr_buffer = xtrystrdup (expression);
+  if (!expr_buffer)
+    return my_error_from_syserror ();
+  expr = expr_buffer;
+
+  if (*expr == '|' && expr[1] == '|')
+    {
+      disjun = 1;
+      expr += 2;
+    }
+  else if (*expr == '&' && expr[1] == '&')
+    expr += 2;
+
+ next_term:
+  while (*expr == ' ' || *expr == '\t')
+    expr++;
+
+  while (*expr == '-')
+    {
+      switch (*++expr)
+        {
+        case '-': toend = 1; break;
+        case 'c': xcase = 1; break;
+        default:
+          log_error ("invalid flag '-%c' in expression\n", *expr);
+          recsel_release (se_head);
+          xfree (expr_buffer);
+          return my_error (GPG_ERR_INV_FLAG);
+        }
+      expr++;
+      while (*expr == ' ' || *expr == '\t')
+        expr++;
+    }
+
+  next_lc = toend? NULL : find_next_lc (expr);
+  if (next_lc)
+    *next_lc = 0;  /* Terminate this term.  */
+
+  se = xtrymalloc (sizeof *se + strlen (expr));
+  if (!se)
+    return my_error_from_syserror ();
+  strcpy (se->name, expr);
+  se->next = NULL;
+  se->not = 0;
+  se->disjun = disjun;
+  se->xcase = xcase;
+
+  if (!se_head)
+    se_head = se;
+  else
+    {
+      for (se2 = se_head; se2->next; se2 = se2->next)
+        ;
+      se2->next = se;
+    }
+
+
+  s = strpbrk (expr, "=<>!~-");
+  if (!s || s == expr )
+    {
+      log_error ("no field name given in expression\n");
+      recsel_release (se_head);
+      xfree (expr_buffer);
+      return my_error (GPG_ERR_NO_NAME);
+    }
+  s0 = s;
+
+  if (!strncmp (s, "=~", 2))
+    {
+      se->op = SELECT_SUB;
+      s += 2;
+    }
+  else if (!strncmp (s, "!~", 2))
+    {
+      se->op = SELECT_SUB;
+      se->not = 1;
+      s += 2;
+    }
+  else if (!strncmp (s, "<>", 2))
+    {
+      se->op = SELECT_SAME;
+      se->not = 1;
+      s += 2;
+    }
+  else if (!strncmp (s, "==", 2))
+    {
+      se->op = SELECT_EQ;
+      s += 2;
+    }
+  else if (!strncmp (s, "!=", 2))
+    {
+      se->op = SELECT_EQ;
+      se->not = 1;
+      s += 2;
+    }
+  else if (!strncmp (s, "<=", 2))
+    {
+      se->op = SELECT_LE;
+      s += 2;
+    }
+  else if (!strncmp (s, ">=", 2))
+    {
+      se->op = SELECT_GE;
+      s += 2;
+    }
+  else if (!strncmp (s, "<", 1))
+    {
+      se->op = SELECT_LT;
+      s += 1;
+    }
+  else if (!strncmp (s, ">", 1))
+    {
+      se->op = SELECT_GT;
+      s += 1;
+    }
+  else if (!strncmp (s, "=", 1))
+    {
+      se->op = SELECT_SAME;
+      s += 1;
+    }
+  else if (!strncmp (s, "-z", 2))
+    {
+      se->op = SELECT_NONEMPTY;
+      se->not = 1;
+      s += 2;
+    }
+  else if (!strncmp (s, "-n", 2))
+    {
+      se->op = SELECT_NONEMPTY;
+      s += 2;
+    }
+  else if (!strncmp (s, "-f", 2))
+    {
+      se->op = SELECT_ISTRUE;
+      se->not = 1;
+      s += 2;
+    }
+  else if (!strncmp (s, "-t", 2))
+    {
+      se->op = SELECT_ISTRUE;
+      s += 2;
+    }
+  else
+    {
+      log_error ("invalid operator in expression\n");
+      recsel_release (se_head);
+      xfree (expr_buffer);
+      return my_error (GPG_ERR_INV_OP);
+    }
+
+  /* We require that a space is used if the value starts with any of
+     the operator characters.  */
+  if (se->op == SELECT_NONEMPTY || se->op == SELECT_ISTRUE)
+    ;
+  else if (strchr ("=<>!~", *s))
+    {
+      log_error ("invalid operator in expression\n");
+      recsel_release (se_head);
+      xfree (expr_buffer);
+      return my_error (GPG_ERR_INV_OP);
+    }
+
+  while (*s == ' ' || *s == '\t')
+    s++;
+
+  if (se->op == SELECT_NONEMPTY || se->op == SELECT_ISTRUE)
+    {
+      if (*s)
+        {
+          log_error ("value given for -n or -z\n");
+          recsel_release (se_head);
+          xfree (expr_buffer);
+          return my_error (GPG_ERR_SYNTAX);
+        }
+    }
+  else
+    {
+      if (!*s)
+        {
+          log_error ("no value given in expression\n");
+          recsel_release (se_head);
+          xfree (expr_buffer);
+          return my_error (GPG_ERR_MISSING_VALUE);
+        }
+    }
+
+  se->name[s0 - expr] = 0;
+  trim_spaces (se->name);
+  if (!se->name[0])
+    {
+      log_error ("no field name given in expression\n");
+      recsel_release (se_head);
+      xfree (expr_buffer);
+      return my_error (GPG_ERR_NO_NAME);
+    }
+
+  trim_spaces (se->name + (s - expr));
+  se->value = se->name + (s - expr);
+  if (!se->value[0] && !(se->op == SELECT_NONEMPTY || se->op == SELECT_ISTRUE))
+    {
+      log_error ("no value given in expression\n");
+      recsel_release (se_head);
+      xfree (expr_buffer);
+      return my_error (GPG_ERR_MISSING_VALUE);
+    }
+
+  se->numvalue = strtol (se->value, NULL, 0);
+
+  if (next_lc)
+    {
+      disjun = next_lc[1] == '|';
+      expr = next_lc + 2;
+      goto next_term;
+    }
+
+  /* Read:y Append to passes last selector.  */
+  if (!*selector)
+    *selector = se_head;
+  else
+    {
+      for (se2 = *selector; se2->next; se2 = se2->next)
+        ;
+      se2->next = se_head;
+    }
+
+  xfree (expr_buffer);
+  return 0;
+}
+
+
+void
+recsel_release (recsel_expr_t a)
+{
+  while (a)
+    {
+      recsel_expr_t tmp = a->next;
+      xfree (a);
+      a = tmp;
+    }
+}
+
+
+void
+recsel_dump (recsel_expr_t selector)
+{
+  recsel_expr_t se;
+
+  log_debug ("--- Begin selectors ---\n");
+  for (se = selector; se; se = se->next)
+    {
+      log_debug ("%s %s %s %s '%s'\n",
+                 se==selector? "  ": (se->disjun? "||":"&&"),
+                 se->xcase?  "-c":"  ",
+                 se->name,
+                 se->op == SELECT_SAME?    (se->not? "<>":"= "):
+                 se->op == SELECT_SUB?     (se->not? "!~":"=~"):
+                 se->op == SELECT_NONEMPTY?(se->not? "-z":"-n"):
+                 se->op == SELECT_ISTRUE?  (se->not? "-f":"-t"):
+                 se->op == SELECT_EQ?      (se->not? "!=":"=="):
+                 se->op == SELECT_LT?      "< ":
+                 se->op == SELECT_LE?      "<=":
+                 se->op == SELECT_GT?      "> ":
+                 se->op == SELECT_GE?      ">=":"[oops]",
+                 se->value);
+    }
+  log_debug ("--- End selectors ---\n");
+}
+
+
+/* Return true if the record RECORD has been selected.  The GETVAL
+ * function is called with COOKIE and the NAME of a property used in
+ * the expression.  */
+int
+recsel_select (recsel_expr_t selector,
+               const char *(*getval)(void *cookie, const char *propname),
+               void *cookie)
+{
+  recsel_expr_t se;
+  const char *value;
+  size_t selen, valuelen;
+  long numvalue;
+  int result = 1;
+
+  se = selector;
+  while (se)
+    {
+      value = getval? getval (cookie, se->name) : NULL;
+      if (!value)
+        value = "";
+
+      if (!*value)
+        {
+          /* Field is empty.  */
+          result = 0;
+        }
+      else /* Field has a value.  */
+        {
+          valuelen = strlen (value);
+          numvalue = strtol (value, NULL, 0);
+          selen = strlen (se->value);
+
+          switch (se->op)
+            {
+            case SELECT_SAME:
+              if (se->xcase)
+                result = (valuelen==selen && !memcmp (value,se->value,selen));
+              else
+                result = (valuelen==selen && !memicmp (value,se->value,selen));
+              break;
+            case SELECT_SUB:
+              if (se->xcase)
+                result = !!my_memstr (value, valuelen, se->value);
+              else
+                result = !!memistr (value, valuelen, se->value);
+              break;
+            case SELECT_NONEMPTY:
+              result = !!valuelen;
+              break;
+            case SELECT_ISTRUE:
+              result = !!numvalue;
+              break;
+            case SELECT_EQ:
+              result = (numvalue == se->numvalue);
+              break;
+            case SELECT_GT:
+              result = (numvalue > se->numvalue);
+              break;
+            case SELECT_GE:
+              result = (numvalue >= se->numvalue);
+              break;
+            case SELECT_LT:
+              result = (numvalue < se->numvalue);
+              break;
+            case SELECT_LE:
+              result = (numvalue <= se->numvalue);
+              break;
+            }
+        }
+
+      if (se->not)
+        result = !result;
+
+      if (result)
+        {
+          /* This expression evaluated to true.  See wether there are
+             remaining expressions in this conjunction.  */
+          if (!se->next || se->next->disjun)
+            break; /* All expressions are true.  Return True.  */
+          se = se->next;  /* Test the next.  */
+        }
+      else
+        {
+          /* This expression evaluated to false and thus the
+           * conjuction evaluates to false.  We skip over the
+           * remaining expressions of this conjunction and continue
+           * with the next disjunction if any.  */
+          do
+            se = se->next;
+          while (se && !se->disjun);
+        }
+    }
+
+  return result;
+}
diff --git a/common/recsel.h b/common/recsel.h
new file mode 100644
index 0000000..be67afc
--- /dev/null
+++ b/common/recsel.h
@@ -0,0 +1,43 @@
+/* recsel.c - Record selection
+ * Copyright (C) 2016 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of either
+ *
+ *   - the GNU Lesser General Public License as published by the Free
+ *     Software Foundation; either version 3 of the License, or (at
+ *     your option) any later version.
+ *
+ * or
+ *
+ *   - the GNU General Public License as published by the Free
+ *     Software Foundation; either version 2 of the License, or (at
+ *     your option) any later version.
+ *
+ * or both in parallel, as here.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef GNUPG_COMMON_RECSEL_H
+#define GNUPG_COMMON_RECSEL_H
+
+struct recsel_expr_s;
+typedef struct recsel_expr_s *recsel_expr_t;
+
+gpg_error_t recsel_parse_expr (recsel_expr_t *selector, const char *expr);
+void recsel_release (recsel_expr_t a);
+void recsel_dump (recsel_expr_t selector);
+int recsel_select (recsel_expr_t selector,
+                   const char *(*getval)(void *cookie, const char *propname),
+                   void *cookie);
+
+
+#endif /*GNUPG_COMMON_RECSEL_H*/
diff --git a/common/t-recsel.c b/common/t-recsel.c
new file mode 100644
index 0000000..fe2a7b9
--- /dev/null
+++ b/common/t-recsel.c
@@ -0,0 +1,405 @@
+/* t-recsel.c - Module test for recsel.c
+ * Copyright (C) 2016 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+#include "init.h"
+#include "recsel.h"
+
+#define PGM  "t-recsel"
+
+#define pass()  do { ; } while(0)
+#define fail(a,e)  do { log_error ("line %d: test %d failed: %s\n",     \
+                                   __LINE__, (a), gpg_strerror ((e)));  \
+                       exit (1);                                        \
+                    } while(0)
+
+static int verbose;
+static int debug;
+
+
+#define FREEEXPR() do { recsel_release (se); se = NULL; } while (0)
+#define ADDEXPR(a) do {                         \
+    err = recsel_parse_expr (&se, (a));         \
+    if (err)                                    \
+      fail (0, err);                            \
+  } while (0)
+
+
+static const char *
+test_1_getval (void *cookie, const char *name)
+{
+  if (strcmp (name, "uid"))
+    fail (0, 0);
+  return cookie;
+}
+
+static void
+run_test_1 (void)
+{
+  static const char *expr[] = {
+    "uid =~ Alfa",
+    "&& uid !~   Test  ",
+    "|| uid =~  Alpha",
+    " uid  !~ Test"
+  };
+  gpg_error_t err;
+  recsel_expr_t se = NULL;
+  int i;
+
+  for (i=0; i < DIM (expr); i++)
+    {
+      err = recsel_parse_expr (&se, expr[i]);
+      if (err)
+        fail (i, err);
+    }
+
+  if (debug)
+    recsel_dump (se);
+
+  /* The example from recsel.c in several variants. */
+  if (!recsel_select (se, test_1_getval, "Alfa"))
+    fail (0, 0);
+  if (!recsel_select (se, test_1_getval, "Alpha"))
+    fail (0, 0);
+  if (recsel_select (se, test_1_getval, "Alfa Test"))
+    fail (0, 0);
+  if (recsel_select (se, test_1_getval, "Alpha Test"))
+    fail (0, 0);
+
+  /* Some modified versions from above.  */
+  if (!recsel_select (se, test_1_getval, " AlfA Tes"))
+    fail (0, 0);
+  if (!recsel_select (se, test_1_getval, " AlfA Tes "))
+    fail (0, 0);
+  if (!recsel_select (se, test_1_getval, " Tes  AlfA"))
+    fail (0, 0);
+  if (!recsel_select (se, test_1_getval, "TesAlfA"))
+    fail (0, 0);
+
+  /* Simple cases. */
+  if (recsel_select (se, NULL, NULL))
+    fail (0, 0);
+  if (recsel_select (se, test_1_getval, NULL))
+    fail (0, 0);
+  if (recsel_select (se, test_1_getval, ""))
+    fail (0, 0);
+
+  FREEEXPR();
+}
+
+
+/* Same as test1 but using a combined expression.. */
+static void
+run_test_1b (void)
+{
+  gpg_error_t err;
+  recsel_expr_t se = NULL;
+
+  err = recsel_parse_expr
+    (&se, "uid =~ Alfa && uid !~   Test  || uid =~  Alpha && uid  !~ Test" );
+  if (err)
+    fail (0, err);
+
+  if (debug)
+    recsel_dump (se);
+
+  /* The example from recsel.c in several variants. */
+  if (!recsel_select (se, test_1_getval, "Alfa"))
+    fail (0, 0);
+  if (!recsel_select (se, test_1_getval, "Alpha"))
+    fail (0, 0);
+  if (recsel_select (se, test_1_getval, "Alfa Test"))
+    fail (0, 0);
+  if (recsel_select (se, test_1_getval, "Alpha Test"))
+    fail (0, 0);
+
+  /* Some modified versions from above.  */
+  if (!recsel_select (se, test_1_getval, " AlfA Tes"))
+    fail (0, 0);
+  if (!recsel_select (se, test_1_getval, " AlfA Tes "))
+    fail (0, 0);
+  if (!recsel_select (se, test_1_getval, " Tes  AlfA"))
+    fail (0, 0);
+  if (!recsel_select (se, test_1_getval, "TesAlfA"))
+    fail (0, 0);
+
+  /* Simple cases. */
+  if (recsel_select (se, NULL, NULL))
+    fail (0, 0);
+  if (recsel_select (se, test_1_getval, NULL))
+    fail (0, 0);
+  if (recsel_select (se, test_1_getval, ""))
+    fail (0, 0);
+
+  FREEEXPR();
+}
+
+
+static const char *
+test_2_getval (void *cookie, const char *name)
+{
+  if (!strcmp (name, "uid"))
+    return "foo at example.org";
+  else if (!strcmp (name, "keyid"))
+    return "0x12345678";
+  else if (!strcmp (name, "zero"))
+    return "0";
+  else if (!strcmp (name, "one"))
+    return "1";
+  else if (!strcmp (name, "blanks"))
+    return "    ";
+  else if (!strcmp (name, "letters"))
+    return "abcde";
+  else
+    return cookie;
+}
+
+static void
+run_test_2 (void)
+{
+  gpg_error_t err;
+  recsel_expr_t se = NULL;
+
+  ADDEXPR ("uid = foo at example.org");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("uid = Foo at example.org");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("-c uid = Foo at example.org");
+  if (recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+
+  FREEEXPR();
+  ADDEXPR ("uid =~ foo at example.org");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("uid =~ Foo at example.org");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("-c uid =~ Foo at example.org");
+  if (recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+
+  FREEEXPR();
+  ADDEXPR ("uid !~ foo at example.org");
+  if (recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("uid !~ Foo at example.org");
+  if (recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("-c uid !~ Foo at example.org");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+
+  FREEEXPR();
+  ADDEXPR ("uid =~ @");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("uid =~ @");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+
+  FREEEXPR();
+  ADDEXPR ("keyid == 0x12345678");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("keyid != 0x12345678");
+  if (recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("keyid >= 0x12345678");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("keyid <= 0x12345678");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("keyid > 0x12345677");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("keyid < 0x12345679");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+
+  FREEEXPR();
+  ADDEXPR ("keyid > 0x12345678");
+  if (recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("keyid < 0x12345678");
+  if (recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+
+
+  FREEEXPR();
+  ADDEXPR ("uid -n");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("uid -z");
+  if (recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+
+  FREEEXPR();
+  ADDEXPR ("nothing -z");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("nothing -n");
+  if (recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+
+  FREEEXPR();
+  ADDEXPR ("blanks -n");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("blanks -z");
+  if (recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+
+  FREEEXPR();
+  ADDEXPR ("letters -n");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("letters -z");
+  if (recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+
+
+  FREEEXPR();
+  ADDEXPR ("nothing -f");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("nothing -t");
+  if (recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+
+  FREEEXPR();
+  ADDEXPR ("zero -f");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("zero -t");
+  if (recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+
+  FREEEXPR();
+  ADDEXPR ("one -t");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("one -f");
+  if (recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+
+  FREEEXPR();
+  ADDEXPR ("blanks -f");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("blanks -t");
+  if (recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+
+  FREEEXPR();
+  ADDEXPR ("letter -f");
+  if (!recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+  FREEEXPR();
+  ADDEXPR ("letters -t");
+  if (recsel_select (se, test_2_getval, NULL))
+    fail (0, 0);
+
+
+  FREEEXPR();
+}
+
+
+
+int
+main (int argc, char **argv)
+{
+  int last_argc = -1;
+
+  log_set_prefix (PGM, GPGRT_LOG_WITH_PREFIX);
+  init_common_subsystems (&argc, &argv);
+
+  if (argc)
+    { argc--; argv++; }
+  while (argc && last_argc != argc )
+    {
+      last_argc = argc;
+      if (!strcmp (*argv, "--"))
+        {
+          argc--; argv++;
+          break;
+        }
+      else if (!strcmp (*argv, "--help"))
+        {
+          fputs ("usage: " PGM " [options]\n"
+                 "Options:\n"
+                 "  --verbose       print timings etc.\n"
+                 "  --debug         flyswatter\n",
+                 stdout);
+          exit (0);
+        }
+      else if (!strcmp (*argv, "--verbose"))
+        {
+          verbose++;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--debug"))
+        {
+          verbose += 2;
+          debug++;
+          argc--; argv++;
+        }
+      else if (!strncmp (*argv, "--", 2))
+        {
+          log_error ("unknown option '%s'\n", *argv);
+          exit (2);
+        }
+    }
+
+  run_test_1 ();
+  run_test_1b ();
+  run_test_2 ();
+  /* Fixme: We should add test for complex conditions.  */
+
+  return 0;
+}

commit 6446a6b3dfd3b2e68b4285870f902ed1f86b0866
Author: Werner Koch <wk at gnupg.org>
Date:   Fri Jul 1 14:42:18 2016 +0200

    common: Smart up register_mem_cleanup_func.
    
    * common/init.c (register_mem_cleanup_func): Avoid double registration.
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/common/init.c b/common/init.c
index c68a4e6..c406ffe 100644
--- a/common/init.c
+++ b/common/init.c
@@ -106,6 +106,10 @@ register_mem_cleanup_func (void (*func)(void))
 {
   mem_cleanup_item_t item;
 
+  for (item = mem_cleanup_list; item; item = item->next)
+    if (item->func == func)
+      return; /* Function has already been registered.  */
+
   item = malloc (sizeof *item);
   if (item)
     {

-----------------------------------------------------------------------

Summary of changes:
 common/Makefile.am             |   6 +-
 common/init.c                  |   4 +
 common/recsel.c                | 571 +++++++++++++++++++++++++++++++++++++++++
 common/{userids.h => recsel.h} |  22 +-
 common/t-recsel.c              | 405 +++++++++++++++++++++++++++++
 doc/gpg.texi                   |  39 +++
 g10/free-packet.c              |   1 +
 g10/gpg.c                      |   8 +
 g10/import.c                   | 135 +++++++++-
 g10/keygen.c                   |   2 +-
 g10/main.h                     |   1 +
 g10/packet.h                   |   3 +-
 12 files changed, 1183 insertions(+), 14 deletions(-)
 create mode 100644 common/recsel.c
 copy common/{userids.h => recsel.h} (63%)
 create mode 100644 common/t-recsel.c


hooks/post-receive
-- 
The GNU Privacy Guard
http://git.gnupg.org




More information about the Gnupg-commits mailing list