2003-04-24 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / version.c
index 27d720b..d64e67a 100644 (file)
-/* version.c -  version check
*     Copyright (C) 2000 Werner Koch (dd9jn)
*      Copyright (C) 2001 g10 Code GmbH
- *
* This file is part of GPGME.
- *
- * GPGME 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 2 of the License, or
* (at your option) any later version.
- *
- * GPGME 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, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
+/* version.c - Version check routines.
+   Copyright (C) 2000 Werner Koch (dd9jn)
  Copyright (C) 2001, 2002, 2003 g10 Code GmbH
  This file is part of GPGME.
+   GPGME 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 2 of the License, or
  (at your option) any later version.
+   GPGME 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 GPGME; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#if HAVE_CONFIG_H
 #include <config.h>
-#include <stdio.h>
-#include <stdlib.h>
+#endif
 #include <string.h>
+#include <limits.h>
 #include <ctype.h>
 
 #include "gpgme.h"
-#include "context.h"
-#include "rungpg.h"
-#include "sema.h"
-#include "util.h"
-#include "key.h" /* for key_cache_init */
 #include "io.h"
 
-static const char *get_engine_info (void);
+/* For _gpgme_sema_subsystem_init ().  */
+#include "sema.h"
 
+\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.  */
 static void
 do_subsystem_inits (void)
 {
-    static int done = 0;
+  static int done = 0;
+
+  if (done)
+    return;
 
-    if (done)
-        return;
-    _gpgme_sema_subsystem_init ();
-    _gpgme_key_cache_init ();
+  _gpgme_sema_subsystem_init ();
+  done = 1;
 }
 
-static const char*
-parse_version_number ( const char *s, int *number )
+
+/* Read the next number in the version string STR and return it in
+   *NUMBER.  Return a pointer to the tail of STR after parsing, or
+   *NULL if the version string was invalid.  */
+static const char *
+parse_version_number (const char *str, int *number)
 {
-    int val = 0;
+#define MAXVAL ((INT_MAX - 10) / 10)
+  int val = 0;
+
+  /* Leading zeros are not allowed.  */
+  if (*str == '0' && isdigit(str[1]))
+    return NULL;
 
-    if ( *s == '0' && isdigit(s[1]) )
-       return NULL; /* leading zeros are not allowed */
-    for ( ; isdigit(*s); s++ ) {
-       val *= 10;
-       val += *s - '0';
+  while (isdigit (*str) && val <= MAXVAL)
+    {
+      val *= 10;
+      val += *(str++) - '0';
     }
-    *number = val;
-    return val < 0? NULL : s;
+  *number = val;
+  return val > MAXVAL ? NULL : str;
 }
 
+
+/* Parse the version string STR in the format MAJOR.MINOR.MICRO (for
+   example, 9.3.2) and return the components in MAJOR, MINOR and MICRO
+   as integers.  The function returns the tail of the string that
+   follows the version number.  This might be te empty string if there
+   is nothing following the version number, or a patchlevel.  The
+   function returns NULL if the version string is not valid.  */
 static const char *
-parse_version_string( const char *s, int *major, int *minor, int *micro )
+parse_version_string (const char *str, int *major, int *minor, int *micro)
 {
-    s = parse_version_number ( s, major );
-    if ( !s || *s != '.' )
-       return NULL;
-    s++;
-    s = parse_version_number ( s, minor );
-    if ( !s || *s != '.' )
-       return NULL;
-    s++;
-    s = parse_version_number ( s, micro );
-    if ( !s )
-       return NULL;
-    return s; /* patchlevel */
+  str = parse_version_number (str, major);
+  if (!str || *str != '.')
+    return NULL;
+  str++;
+
+  str = parse_version_number (str, minor);
+  if (!str || *str != '.')
+    return NULL;
+  str++;
+
+  str = parse_version_number (str, micro);
+  if (!str)
+    return NULL;
+
+  /* A patchlevel might follow.  */
+  return str;
 }
 
+
 const char *
 _gpgme_compare_versions (const char *my_version,
-                        const char *req_version)
+                        const char *rq_version)
 {
   int my_major, my_minor, my_micro;
   int rq_major, rq_minor, rq_micro;
   const char *my_plvl, *rq_plvl;
 
-  if (!req_version)
+  if (!rq_version)
     return my_version;
   if (!my_version)
     return NULL;
 
   my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro);
   if (!my_plvl)
-    return NULL;       /* Very strange: our own version is bogus.  */
-  rq_plvl = parse_version_string(req_version,
-                                &rq_major, &rq_minor, &rq_micro);
+    return NULL;
+
+  rq_plvl = parse_version_string (rq_version, &rq_major, &rq_minor, &rq_micro);
   if (!rq_plvl)
-    return NULL;       /* Requested version string is invalid.  */
+    return NULL;
 
   if (my_major > rq_major
-       || (my_major == rq_major && my_minor > rq_minor)
+      || (my_major == rq_major && my_minor > rq_minor)
       || (my_major == rq_major && my_minor == rq_minor 
          && my_micro > rq_micro)
       || (my_major == rq_major && my_minor == rq_minor
-         && my_micro == rq_micro
-         && strcmp( my_plvl, rq_plvl ) >= 0))
-    {
-      return my_version;
-    }
+         && my_micro == rq_micro && strcmp (my_plvl, rq_plvl) >= 0))
+    return my_version;
+
   return NULL;
 }
 
-/**
- * gpgme_check_version:
- * @req_version: A string with a version
- * 
- * Check that the the version of the library is at minimum the requested one
- * and return the version string; return NULL if the condition is not
- * met.  If a NULL is passed to this function, no check is done and
- * the version string is simply returned.  It is a pretty good idea to
- * run this function as soon as possible, because it also intializes 
- * some subsystems.  In a multithreaded environment if should be called
- * before the first thread is created.
- * 
- * Return value: The version string or NULL
- **/
+
+/* Check that the the version of the library is at minimum the
+   requested one and return the version string; return NULL if the
+   condition is not met.  If a NULL is passed to this function, no
+   check is done and the version string is simply returned.
+
+   This function must be run once at startup, as it also initializes
+   some subsystems.  Its invocation must be synchronized against
+   calling any of the other functions in a multi-threaded
+   environments.  */
 const char *
 gpgme_check_version (const char *req_version)
 {
@@ -133,95 +150,27 @@ gpgme_check_version (const char *req_version)
   return _gpgme_compare_versions (VERSION, req_version);
 }
 
-/**
- * gpgme_get_engine_info:
- *  
- * Return information about the underlying crypto engines.  This is an
- * XML string with various information.  A string is always returned
- * even if the crypto engines is not installed; in this case a XML
- * string with some error information is returned.
- * 
- * Return value: A XML string with information about the crypto
- * engines.
- **/
-const char *
-gpgme_get_engine_info ()
-{
-  static const char *engine_info;
-  const char *openpgp_info = _gpgme_engine_get_info (GPGME_PROTOCOL_OpenPGP);
-  const char *cms_info = _gpgme_engine_get_info (GPGME_PROTOCOL_CMS);
-  char *info;
-
-  /* FIXME: Make sure that only one instance does run.  */
-  if (engine_info)
-    return engine_info;
-
-  if (!openpgp_info && !cms_info)
-    info = "<EngineInfo>\n</EngineInfo>\n";
-  else if (!openpgp_info || !cms_info)
-    {
-      const char *fmt = "<EngineInfo>\n"
-        "%s"
-        "</EngineInfo>\n";
-
-      info = xtrymalloc (strlen(fmt) + strlen(openpgp_info
-                                              ? openpgp_info : cms_info) + 1);
-      if (info)
-        sprintf (info, fmt, openpgp_info ? openpgp_info : cms_info);
-    }
-  else
-    {
-      const char *fmt = "<EngineInfo>\n"
-        "%s%s"
-        "</EngineInfo>\n";
-      info = xtrymalloc (strlen(fmt) + strlen(openpgp_info)
-                         + strlen (cms_info) + 1);
-      if (info)
-        sprintf (info, fmt, openpgp_info, cms_info);
-    }
-  if (!info)
-    info = "<EngineInfo>\n"
-      "  <error>Out of core</error>\n"
-      "</EngineInfo>\n";
-  engine_info = info;
-  return engine_info;
-}
-
-/**
- * gpgme_check_engine:
- * 
- * Check whether the installed crypto engine for the OpenPGP protocol
- * matches the requirement of GPGME.  This function is deprecated,
- * instead use gpgme_engine_get_info() with the specific protocol you
- * need.
- *
- * Return value: 0 or an error code.
- **/
-GpgmeError
-gpgme_check_engine ()
-{
-  return gpgme_engine_check_version (GPGME_PROTOCOL_OpenPGP);
-}
-
 \f
 #define LINELENGTH 80
 
+/* Retrieve the version number from the --version output of the
+   program FILE_NAME.  */
 char *
-_gpgme_get_program_version (const char *const path)
+_gpgme_get_program_version (const char *const file_name)
 {
   char line[LINELENGTH] = "";
   int linelen = 0;
   char *mark = NULL;
   int rp[2];
-  pid_t pid;
   int nread;
-  char *argv[] = {(char *) path, "--version", 0};
+  char *argv[] = {NULL /* file_name */, "--version", 0};
   struct spawn_fd_item_s pfd[] = { {0, -1}, {-1, -1} };
   struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */}, {-1, -1} };
-  int status, signal;
+  int status;
 
-  if (!path)
+  if (!file_name)
     return NULL;
+  argv[0] = (char *) file_name;
 
   if (_gpgme_io_pipe (rp, 1) < 0)
     return NULL;
@@ -229,8 +178,8 @@ _gpgme_get_program_version (const char *const path)
   pfd[0].fd = rp[1];
   cfd[0].fd = rp[1];
 
-  pid = _gpgme_io_spawn (path, argv, cfd, pfd);
-  if (pid < 0)
+  status = _gpgme_io_spawn (file_name, argv, cfd, pfd);
+  if (status < 0)
     {
       _gpgme_io_close (rp[0]);
       _gpgme_io_close (rp[1]);
@@ -255,14 +204,13 @@ _gpgme_get_program_version (const char *const path)
   while (nread > 0 && linelen < LINELENGTH - 1);
 
   _gpgme_io_close (rp[0]);
-  _gpgme_io_waitpid (pid, 1, &status, &signal);
 
   if (mark)
     {
       mark = strrchr (line, ' ');
       if (!mark)
        return NULL;
-      return xtrystrdup (mark + 1);
+      return strdup (mark + 1);
     }
 
   return NULL;