Allow for engine version lines with a suffix.
[gpgme.git] / gpgme / version.c
index 26d2dae..457945a 100644 (file)
@@ -1,6 +1,6 @@
 /* version.c - Version check routines.
    Copyright (C) 2000 Werner Koch (dd9jn)
-   Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
+   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008 g10 Code GmbH
  
    This file is part of GPGME.
  
 #include <string.h>
 #include <limits.h>
 #include <ctype.h>
+#ifdef HAVE_W32_SYSTEM
+#include <winsock2.h>
+#endif
 
 #include "gpgme.h"
 #include "priv-io.h"
+#include "debug.h"
 
 /* For _gpgme_sema_subsystem_init ().  */
 #include "sema.h"
 
+#ifdef HAVE_ASSUAN_H
+#include "assuan.h"
+#endif
+
 \f
 /* Bootstrap the subsystems needed for concurrent operation.  This
    must be done once at startup.  We can not guarantee this using a
    lock, though, because the semaphore subsystem needs to be
    initialized itself before it can be used.  So we expect that the
-   user performs the necessary syncrhonization.  */
+   user performs the necessary synchronization.  */
 static void
 do_subsystem_inits (void)
 {
@@ -47,7 +55,20 @@ do_subsystem_inits (void)
     return;
 
   _gpgme_sema_subsystem_init ();
+#ifdef HAVE_ASSUAN_H
+  assuan_set_assuan_log_level (0);
+  assuan_set_assuan_err_source (GPG_ERR_SOURCE_GPGME);
+#endif /*HAVE_ASSUAN_H*/
+  _gpgme_debug_subsystem_init ();
+#if defined(HAVE_W32_SYSTEM) && defined(HAVE_ASSUAN_H)
   _gpgme_io_subsystem_init ();
+  /* We need to make sure that the sockets are initialized.  */
+  {
+    WSADATA wsadat;
+    
+    WSAStartup (0x202, &wsadat);
+  }
+#endif /*HAVE_W32_SYSTEM && HAVE_ASSUAN_H*/
 
   done = 1;
 }
@@ -152,12 +173,69 @@ const char *
 gpgme_check_version (const char *req_version)
 {
   do_subsystem_inits ();
+
+  /* Catch-22: We need to get at least the debug subsystem ready
+     before using the tarce facility.  If we won't the tarce would
+     automagically initialize the debug system with out the locks
+     being initialized and missing the assuan log level setting. */
+  TRACE2 (DEBUG_INIT, "gpgme_check_version: ", 0,
+         "req_version=%s, VERSION=%s", req_version, VERSION);
   return _gpgme_compare_versions (VERSION, req_version) ? VERSION : NULL;
 }
 
 \f
 #define LINELENGTH 80
 
+/* Extract the version string of a program from STRING.  The version
+   number is expected to be in GNU style format:
+   
+     foo 1.2.3
+     foo (bar system) 1.2.3
+     foo 1.2.3 cruft
+     foo (bar system) 1.2.3 cruft.
+
+  Spaces and tabs are skipped and used as delimiters, a term in
+  (nested) parenthesis before the version string is skipped, the
+  version string may consist of any non-space and non-tab characters
+  but needs to bstart with a digit.
+*/
+static const char *
+extract_version_string (const char *string, size_t *r_len)
+{
+  const char *s;
+  int count, len;
+
+  for (s=string; *s; s++)
+    if (*s == ' ' || *s == '\t')
+        break;
+  while (*s == ' ' || *s == '\t')
+    s++;
+  if (*s == '(')
+    {
+      for (count=1, s++; count && *s; s++)
+        if (*s == '(')
+          count++;
+        else if (*s == ')')
+          count--;
+    }
+  /* For robustness we look for a digit.  */
+  while ( *s && !(*s >= '0' && *s <= '9') )
+    s++;
+  if (*s >= '0' && *s <= '9')
+    {
+      for (len=0; s[len]; len++)
+        if (s[len] == ' ' || s[len] == '\t')
+          break;
+    }
+  else
+    len = 0;
+
+  *r_len = len;
+  return s;
+}
+
+
 /* Retrieve the version number from the --version output of the
    program FILE_NAME.  */
 char *
@@ -183,7 +261,7 @@ _gpgme_get_program_version (const char *const file_name)
   pfd[0].fd = rp[1];
   cfd[0].fd = rp[1];
 
-  status = _gpgme_io_spawn (file_name, argv, cfd, pfd);
+  status = _gpgme_io_spawn (file_name, argv, cfd, pfd, NULL);
   if (status < 0)
     {
       _gpgme_io_close (rp[0]);
@@ -214,10 +292,18 @@ _gpgme_get_program_version (const char *const file_name)
 
   if (mark)
     {
-      mark = strrchr (line, ' ');
+      size_t len;
+      const char *s;
+
+      s = extract_version_string (line, &len);
+      if (!len)
+        return NULL;
+      mark = malloc (len + 1);
       if (!mark)
        return NULL;
-      return strdup (mark + 1);
+      memcpy (mark, s, len);
+      mark[len] = 0;
+      return mark;
     }
 
   return NULL;