2007-09-07 Marcus Brinkmann <marcus@g10code.de>
authorMarcus Brinkmann <mb@g10code.com>
Thu, 6 Sep 2007 22:41:11 +0000 (22:41 +0000)
committerMarcus Brinkmann <mb@g10code.com>
Thu, 6 Sep 2007 22:41:11 +0000 (22:41 +0000)
* configure.ac: Check for C++, Qt and support --enable-w32-qt.
* m4/pkg.m4: New file.

gpgme/
2007-09-07  Marcus Brinkmann  <marcus@g10code.de>

* kdpipeiodevice.h, kdpipeiodevice.cpp, moc_kdpipeiodevice.cpp,
kdpipeiodevice.moc, w32-qt-io.c: New files.
* Makefile.am (ltlib_gpgme_extra): Rename to ltlib_gpgme_glib.
(ltlib_gpgme_qt): New variable.
(lib_LTLIBRARIES): Add $(ltlib_gpgme_qt).
(libgpgme_qt_la_SOURCES): New variable.
(AM_CPPFLAGS): Add @QT4_CORE_INCLUDES@
(AM_CFLAGS): Add @QT4_CORE_CFLAGS@.
(libgpgme_qt_la_LDFLAGS, libgpgme_qt_la_DEPENDENCIES)
(libgpgme_qt_la_LIBADD): New variables.
* sema.h (struct critsect_s): Rename "private" to "priv" to make
C++ users happy.  Change users.
* posix-sema.c (_gpgme_sema_cs_enter, _gpgme_sema_cs_leave)
(_gpgme_sema_cs_destroy): Likewise.
* w32-sema.c (critsect_init, _gpgme_sema_cs_enter)
(_gpgme_sema_cs_leave, _gpgme_sema_cs_destroy): Likewise.
* w32-glib-io.c (gpgme_get_giochannel): Change return type to
void*.
(gpgme_get_fdptr): New function.
* w32-io.c (gpgme_get_fdptr): New function
* gpgme.def: Add gpgme_get_fdptr.

16 files changed:
trunk/ChangeLog
trunk/configure.ac
trunk/gpgme/ChangeLog
trunk/gpgme/Makefile.am
trunk/gpgme/gpgme.def
trunk/gpgme/kdpipeiodevice.cpp [new file with mode: 0644]
trunk/gpgme/kdpipeiodevice.h [new file with mode: 0644]
trunk/gpgme/kdpipeiodevice.moc [new file with mode: 0644]
trunk/gpgme/moc_kdpipeiodevice.cpp [new file with mode: 0644]
trunk/gpgme/posix-sema.c
trunk/gpgme/sema.h
trunk/gpgme/w32-glib-io.c
trunk/gpgme/w32-io.c
trunk/gpgme/w32-qt-io.cpp [new file with mode: 0644]
trunk/gpgme/w32-sema.c
trunk/m4/pkg.m4 [new file with mode: 0644]

index 187fd19892fe012a8ffa80f8dd1ead1ed6d4c739..f45f6e322defcc61443f86f814e05b5957414e2d 100644 (file)
@@ -1,3 +1,8 @@
+2007-09-07  Marcus Brinkmann  <marcus@g10code.de>
+
+       * configure.ac: Check for C++, Qt and support --enable-w32-qt.
+       * m4/pkg.m4: New file.
+
 2007-08-21  Marcus Brinkmann  <marcus@g10code.de>
 
        * configure.ac (--enable-w32-glib): Use --enableval, not
        * autogen.sh: Added option --build-w32.
 
 
- Copyright 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
+ Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007 g10 Code GmbH
 
  This file is free software; as a special exception the author gives
  unlimited permission to copy and/or distribute it, with or without
index 923792c02e65829f5f472ebc52a449ed96dd64f7..a21aaa9bd329df5fbef41a9768ca37ab44e2528d 100644 (file)
@@ -83,6 +83,7 @@ AH_VERBATIM([_REENTRANT],
 #endif])
 
 AC_PROG_CC
+AC_PROG_CXX
 
 AC_SUBST(LIBGPGME_LT_CURRENT)
 AC_SUBST(LIBGPGME_LT_AGE)
@@ -162,6 +163,13 @@ AC_ARG_ENABLE(w32-glib,
            build_w32_glib=$enableval)
 AM_CONDITIONAL(BUILD_W32_GLIB, test "$build_w32_glib" = yes)
 
+build_w32_qt=no
+PKG_CHECK_MODULES(QT4_CORE, QtCore)
+AC_ARG_ENABLE(w32-qt,
+           AC_HELP_STRING([--enable-w32-qt], [build GPGME Qt for W32]),
+           build_w32_qt=$enableval)
+AM_CONDITIONAL(BUILD_W32_QT, test "$build_w32_qt" = yes)
+
 AM_CONDITIONAL(HAVE_PTH, test "$have_pth" = "yes")
 AM_CONDITIONAL(HAVE_PTHREAD, test "$have_pthread" = "yes")
 
index 29f496d101d39d48159adefb84175bf226a5bd46..7d3ae586fabde15d48f8ef211ef11827e0da3af3 100644 (file)
@@ -1,3 +1,27 @@
+2007-09-07  Marcus Brinkmann  <marcus@g10code.de>
+
+       * kdpipeiodevice.h, kdpipeiodevice.cpp, moc_kdpipeiodevice.cpp,
+       kdpipeiodevice.moc, w32-qt-io.c: New files.
+       * Makefile.am (ltlib_gpgme_extra): Rename to ltlib_gpgme_glib.
+       (ltlib_gpgme_qt): New variable.
+       (lib_LTLIBRARIES): Add $(ltlib_gpgme_qt).
+       (libgpgme_qt_la_SOURCES): New variable.
+       (AM_CPPFLAGS): Add @QT4_CORE_INCLUDES@
+       (AM_CFLAGS): Add @QT4_CORE_CFLAGS@.
+       (libgpgme_qt_la_LDFLAGS, libgpgme_qt_la_DEPENDENCIES)
+       (libgpgme_qt_la_LIBADD): New variables.
+       * sema.h (struct critsect_s): Rename "private" to "priv" to make
+       C++ users happy.  Change users.
+       * posix-sema.c (_gpgme_sema_cs_enter, _gpgme_sema_cs_leave)
+       (_gpgme_sema_cs_destroy): Likewise.
+       * w32-sema.c (critsect_init, _gpgme_sema_cs_enter)
+       (_gpgme_sema_cs_leave, _gpgme_sema_cs_destroy): Likewise.
+       * w32-glib-io.c (gpgme_get_giochannel): Change return type to
+       void*.
+       (gpgme_get_fdptr): New function.
+       * w32-io.c (gpgme_get_fdptr): New function
+       * gpgme.def: Add gpgme_get_fdptr.
+
 2007-08-22  Marcus Brinkmann  <marcus@g10code.de>
 
        * w32-io.c (_gpgme_io_write): Return early if COUNT is zero.
index 9c6254fd4f2ae9d65fd0b128383cb393a9c36a05..df233bba722fbca45cafb7a5dae00e7d7dd290d3 100644 (file)
@@ -1,5 +1,5 @@
 # Copyright (C) 2000 Werner Koch (dd9jn)
-# Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
+# Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007 g10 Code GmbH
 # 
 # This file is part of GPGME.
 # 
@@ -40,12 +40,18 @@ ltlib_gpgme_pth =
 endif
 
 if BUILD_W32_GLIB
-ltlib_gpgme_extra = libgpgme-glib.la
+ltlib_gpgme_glib = libgpgme-glib.la
 else
-ltlib_gpgme_extra =
+ltlib_gpgme_glib =
 endif
 
-lib_LTLIBRARIES = libgpgme.la $(ltlib_gpgme_extra) \
+if BUILD_W32_QT
+ltlib_gpgme_qt = libgpgme-qt.la
+else
+ltlib_gpgme_qt =
+endif
+
+lib_LTLIBRARIES = libgpgme.la $(ltlib_gpgme_glib) $(ltlib_gpgme_qt) \
        $(ltlib_gpgme_pthread) $(ltlib_gpgme_pth)
 
 if HAVE_LD_VERSION_SCRIPT
@@ -107,10 +113,24 @@ if BUILD_W32_GLIB
 libgpgme_glib_la_SOURCES = $(main_sources) ath.h ath.c w32-glib-io.c
 endif
 
+if BUILD_W32_QT
+libgpgme_qt_la_SOURCES = $(main_sources) ath.h ath.c w32-qt-io.cpp \
+       kdpipeiodevice.h kdpipeiodevice.cpp moc_kdpipeiodevice.cpp \
+       kdpipeiodevice.moc
+
+# These are built sources (normally).
+# moc_kdpipeiodevice.cpp: kdpipeiodevice.h
+#      $(MOC4) -o $@ $<
+#
+# kdpipeiodevice.moc: kdpipeiodevice.cpp
+#      $(MOC4) -o $@ $<
+endif
+
 # We use a global CFLAGS and CPPFLAGS setting for all library
 # versions, because then every object file is only compiled once.
-AM_CPPFLAGS = $(assuan_cppflags) @GPG_ERROR_CFLAGS@ @PTH_CPPFLAGS@
-AM_CFLAGS = @PTH_CFLAGS@ @GLIB_CFLAGS@
+AM_CPPFLAGS = $(assuan_cppflags) @GPG_ERROR_CFLAGS@ @PTH_CPPFLAGS@ \
+       @QT4_CORE_CFLAGS@
+AM_CFLAGS = @PTH_CFLAGS@ @GLIB_CFLAGS@ @QT4_CORE_CFLAGS@
 
 if HAVE_W32_SYSTEM
 
@@ -181,6 +201,16 @@ libgpgme_glib_la_LIBADD = $(assuan_libobjs) @LTLIBOBJS@ \
        @GPG_ERROR_LIBS@ @GLIB_LIBS@ @NETLIBS@
 endif
 
+if BUILD_W32_QT
+libgpgme_qt_la_LDFLAGS = $(gpgme_res_ldflag) $(no_undefined) \
+       $(export_symbols) $(libgpgme_version_script_cmd) -version-info \
+       @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@
+libgpgme_qt_la_DEPENDENCIES = $(assuan_libobjs) \
+       @LTLIBOBJS@ $(srcdir)/libgpgme.vers $(gpgme_deps)
+libgpgme_qt_la_LIBADD = $(assuan_libobjs) @LTLIBOBJS@ \
+       @GPG_ERROR_LIBS@ @QT4_CORE_LIB@ @NETLIBS@
+endif
+
 status-table.h : gpgme.h
        $(srcdir)/mkstatus < $(srcdir)/gpgme.h > status-table.h
 
index f4aa1b5aa091306a7d0b59d24ce639f904950b6c..bb2caf263efe779c38aad8312973648d9f9be3b1 100644 (file)
@@ -154,5 +154,7 @@ EXPORTS
     gpgme_free                           @120
 
     gpgme_get_giochannel                 @121
+    gpgme_get_fdptr                      @122
+
 ; END
 
diff --git a/trunk/gpgme/kdpipeiodevice.cpp b/trunk/gpgme/kdpipeiodevice.cpp
new file mode 100644 (file)
index 0000000..f4acebe
--- /dev/null
@@ -0,0 +1,760 @@
+/*
+  Copyright (C) 2007 Klarälvdalens Datakonsult AB
+
+  KDPipeIODevice is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  KDPipeIODevice 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 Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with KDPipeIODevice; see the file COPYING.LIB.  If not, write to the
+  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+*/
+
+#include "kdpipeiodevice.h"
+
+#include <QtCore>
+
+#include <cassert>
+#include <memory>
+#include <algorithm>
+
+#ifdef Q_OS_WIN32
+# define NOMINMAX
+# include <windows.h>
+# include <io.h>
+#else
+# include <unistd.h>
+# include <errno.h>
+#endif
+
+#ifndef KDAB_CHECK_THIS
+# define KDAB_CHECK_CTOR (void)1
+# define KDAB_CHECK_DTOR KDAB_CHECK_CTOR
+# define KDAB_CHECK_THIS KDAB_CHECK_CTOR
+#endif
+
+#define LOCKED( d ) const QMutexLocker locker( &d->mutex )
+#define synchronized( d ) if ( int i = 0 ) {} else for ( const QMutexLocker locker( &d->mutex ) ; !i ; ++i )
+
+const unsigned int BUFFER_SIZE = 4096;
+const bool ALLOW_QIODEVICE_BUFFERING = true;
+
+// comment to get trace output:
+#define qDebug if(1){}else qDebug
+
+namespace {
+class Reader : public QThread {
+    Q_OBJECT
+public:
+    Reader( int fd, Qt::HANDLE handle );
+    ~Reader();
+
+    qint64 readData( char * data, qint64 maxSize );
+
+    unsigned int bytesInBuffer() const {
+        return ( wptr + sizeof buffer - rptr ) % sizeof buffer ;
+    }
+
+    bool bufferFull() const {
+        return bytesInBuffer() == sizeof buffer - 1;
+    }
+
+    bool bufferEmpty() const {
+       return bytesInBuffer() == 0;
+    }
+
+    bool bufferContains( char ch ) {
+       const unsigned int bib = bytesInBuffer();
+       for ( unsigned int i = rptr ; i < rptr + bib ; ++i )
+           if ( buffer[i%sizeof buffer] == ch )
+               return true;
+       return false;
+    }
+       
+Q_SIGNALS:
+    void readyRead();
+
+protected:
+    /* reimp */ void run();
+
+private:
+    int fd;
+    Qt::HANDLE handle;
+public:
+    QMutex mutex;
+    QWaitCondition bufferNotFullCondition;
+    QWaitCondition bufferNotEmptyCondition;
+    QWaitCondition hasStarted;
+    bool cancel;
+    bool eof;
+    bool error;
+    bool eofShortCut;
+    int errorCode;
+private:
+    unsigned int rptr, wptr;
+    char buffer[BUFFER_SIZE+1]; // need to keep one byte free to detect empty state
+};
+
+
+Reader::Reader( int fd_, Qt::HANDLE handle_ )
+    : QThread(),
+      fd( fd_ ),
+      handle( handle_ ),
+      mutex(),
+      bufferNotFullCondition(),
+      bufferNotEmptyCondition(),
+      hasStarted(),
+      cancel( false ),
+      eof( false ),
+      error( false ),
+      eofShortCut( false ),
+      errorCode( 0 ),
+      rptr( 0 ), wptr( 0 )
+{
+    
+}
+
+Reader::~Reader() {}
+
+
+class Writer : public QThread {
+    Q_OBJECT
+public:
+    Writer( int fd, Qt::HANDLE handle );
+    ~Writer();
+
+    qint64 writeData( const char * data, qint64 size );
+
+    unsigned int bytesInBuffer() const { return numBytesInBuffer; }
+
+    bool bufferFull() const {
+        return numBytesInBuffer == sizeof buffer;
+    }
+
+    bool bufferEmpty() const {
+       return numBytesInBuffer == 0;
+    }
+
+Q_SIGNALS:
+    void bytesWritten( qint64 );
+
+protected:
+    /* reimp */ void run();
+
+private:
+    int fd;
+    Qt::HANDLE handle;
+public:
+    QMutex mutex;
+    QWaitCondition bufferEmptyCondition;
+    QWaitCondition bufferNotEmptyCondition;
+    QWaitCondition hasStarted;
+    bool cancel;
+    bool error;
+    int errorCode;
+private:
+    unsigned int numBytesInBuffer;
+    char buffer[BUFFER_SIZE];
+};
+}
+
+Writer::Writer( int fd_, Qt::HANDLE handle_ )
+    : QThread(),
+      fd( fd_ ),
+      handle( handle_ ),
+      mutex(),
+      bufferEmptyCondition(),
+      bufferNotEmptyCondition(),
+      hasStarted(),
+      cancel( false ),
+      error( false ),
+      errorCode( 0 ),
+      numBytesInBuffer( 0 )
+{
+
+}
+
+Writer::~Writer() {}
+
+
+class KDPipeIODevice::Private {
+    friend class ::KDPipeIODevice;
+    KDPipeIODevice * const q;
+public:
+    explicit Private( KDPipeIODevice * qq );
+    ~Private();
+
+    bool doOpen( int, Qt::HANDLE, OpenMode );
+
+private:
+    int fd;
+    Qt::HANDLE handle;
+    Reader * reader;
+    Writer * writer;
+};
+
+KDPipeIODevice::Private::Private( KDPipeIODevice * qq )
+    : q( qq ),
+      fd( -1 ),
+      handle( 0 ),
+      reader( 0 ),
+      writer( 0 )
+{
+
+}
+
+
+KDPipeIODevice::Private::~Private() {}
+
+
+KDPipeIODevice::KDPipeIODevice( QObject * p )
+    : QIODevice( p ), d( new Private( this ) )
+{
+    KDAB_CHECK_CTOR;
+}
+
+KDPipeIODevice::KDPipeIODevice( int fd, OpenMode mode, QObject * p )
+    : QIODevice( p ), d( new Private( this ) )
+{
+    KDAB_CHECK_CTOR;
+    open( fd, mode );
+}
+
+KDPipeIODevice::KDPipeIODevice( Qt::HANDLE handle, OpenMode mode, QObject * p )
+    : QIODevice( p ), d( new Private( this ) )
+{
+    KDAB_CHECK_CTOR;
+    open( handle, mode );
+}
+
+KDPipeIODevice::~KDPipeIODevice() { KDAB_CHECK_DTOR;
+    if ( isOpen() )
+       close();
+    delete d; d = 0;
+}
+
+
+bool KDPipeIODevice::open( int fd, OpenMode mode ) { KDAB_CHECK_THIS;
+
+#ifdef Q_OS_WIN32
+    return d->doOpen( fd, (HANDLE)_get_osfhandle( fd ), mode );
+#else
+    return d->doOpen( fd, 0, mode );
+#endif
+
+}
+
+bool KDPipeIODevice::open( Qt::HANDLE h, OpenMode mode ) { KDAB_CHECK_THIS;
+
+#ifdef Q_OS_WIN32
+    return d->doOpen( 0, h, mode );
+#else
+    Q_UNUSED( h );
+    Q_UNUSED( mode );
+    assert( !"KDPipeIODevice::open( Qt::HANDLE, OpenMode ) should never be called except on Windows." );
+#endif
+
+}
+
+bool KDPipeIODevice::Private::doOpen( int fd_, Qt::HANDLE handle_, OpenMode mode_ ) {
+
+    if ( q->isOpen() || fd_ < 0 )
+       return false;
+
+#ifdef Q_OS_WIN32
+    if ( !handle_ )
+       return false;
+#endif
+
+    if ( !(mode_ & ReadWrite) )
+       return false; // need to have at least read -or- write
+
+    fd = fd_;
+    handle = handle_;
+
+    std::auto_ptr<Reader> reader_;
+    std::auto_ptr<Writer> writer_;
+
+    if ( mode_ & ReadOnly ) {
+       reader_.reset( new Reader( fd_, handle_ ) );
+       LOCKED( reader_ );
+       reader_->start( QThread::HighestPriority );
+       if ( !reader_->hasStarted.wait( &reader_->mutex, 1000 ) )
+           return false;
+       connect( reader_.get(), SIGNAL(readyRead()), q, SIGNAL(readyRead()), Qt::QueuedConnection );
+    }
+    if ( mode_ & WriteOnly ) {
+       writer_.reset( new Writer( fd_, handle_ ) );
+       LOCKED( writer_ );
+       writer_->start( QThread::HighestPriority );
+       if ( !writer_->hasStarted.wait( &writer_->mutex, 1000 ) )
+           return false;
+       connect( writer_.get(), SIGNAL(bytesWritten(qint64)), q, SIGNAL(bytesWritten(qint64)), Qt::QueuedConnection );
+    }
+
+    // commit to *this:
+    fd = fd_;
+    handle = handle_;
+    reader = reader_.release();
+    writer = writer_.release();
+
+    q->setOpenMode( mode_|Unbuffered );
+
+    return true;
+}
+
+int KDPipeIODevice::descriptor() const { KDAB_CHECK_THIS;
+    return d->fd;
+}
+
+Qt::HANDLE KDPipeIODevice::handle() const { KDAB_CHECK_THIS;
+    return d->handle;
+}
+
+qint64 KDPipeIODevice::bytesAvailable() const { KDAB_CHECK_THIS;
+    const qint64 base = QIODevice::bytesAvailable();
+    if ( d->reader )
+       synchronized( d->reader ) return base + d->reader->bytesInBuffer();
+    return base;
+}
+
+qint64 KDPipeIODevice::bytesToWrite() const { KDAB_CHECK_THIS;
+    const qint64 base = QIODevice::bytesToWrite();
+    if ( d->writer )
+       synchronized( d->writer ) return base + d->writer->bytesInBuffer();
+    return base;
+}
+
+bool KDPipeIODevice::canReadLine() const { KDAB_CHECK_THIS;
+    if ( QIODevice::canReadLine() )
+       return true;
+    if ( d->reader )
+       synchronized( d->reader ) return d->reader->bufferContains( '\n' );
+    return true;
+}
+
+bool KDPipeIODevice::isSequential() const {
+    return true;
+}
+
+bool KDPipeIODevice::atEnd() const { KDAB_CHECK_THIS;
+    if ( !QIODevice::atEnd() ) {
+       qDebug( "KDPipeIODevice::atEnd returns false since QIODevice::atEnd does (with bytesAvailable=%ld)", static_cast<long>(bytesAvailable()) );
+       return false;
+    }
+    if ( !isOpen() )
+       return true;
+    if ( d->reader->eofShortCut )
+       return true;
+    LOCKED( d->reader );
+    const bool eof = ( d->reader->error || d->reader->eof ) && d->reader->bufferEmpty();
+    if ( !eof ) {
+       if ( !d->reader->error && !d->reader->eof )
+           qDebug( "KDPipeIODevice::atEnd returns false since !reader->error && !reader->eof" );
+       if ( !d->reader->bufferEmpty() )
+           qDebug( "KDPipeIODevice::atEnd returns false since !reader->bufferEmpty()" );
+    }
+    return eof;
+}
+
+bool KDPipeIODevice::waitForBytesWritten( int msecs ) { KDAB_CHECK_THIS;
+    Writer * const w = d->writer;
+    if ( !w )
+       return true;
+    LOCKED( w );
+    return w->bufferEmpty() || w->error || w->bufferEmptyCondition.wait( &w->mutex, msecs ) ;
+}
+
+bool KDPipeIODevice::waitForReadyRead( int msecs ) { KDAB_CHECK_THIS;
+    if ( ALLOW_QIODEVICE_BUFFERING ) {
+       if ( bytesAvailable() > 0 )
+           return true;
+    }
+    Reader * const r = d->reader;
+    if ( !r || r->eofShortCut )
+       return true;
+    LOCKED( r );
+    return r->bytesInBuffer() != 0 || r->eof || r->error || r->bufferNotEmptyCondition.wait( &r->mutex, msecs ) ;
+}
+
+qint64 KDPipeIODevice::readData( char * data, qint64 maxSize ) { KDAB_CHECK_THIS;
+
+    qDebug( "KDPipeIODevice::readData: data=%p, maxSize=%lld", data, maxSize );
+
+    Reader * const r = d->reader;
+
+    assert( r );
+    //assert( r->isRunning() ); // wrong (might be eof, error)
+    assert( data || maxSize == 0 );
+    assert( maxSize >= 0 );
+
+    if ( r->eofShortCut ) {
+       qDebug( "KDPipeIODevice::readData: hit eofShortCut, returning 0" );
+       return 0;
+    }
+
+    if ( maxSize < 0 )
+       maxSize = 0;
+
+    if ( ALLOW_QIODEVICE_BUFFERING ) {
+       if ( bytesAvailable() > 0 )
+           maxSize = std::min( maxSize, bytesAvailable() ); // don't block
+    }
+
+    LOCKED( r );
+    if ( /* maxSize > 0 && */ r->bufferEmpty() && !r->error && !r->eof ) { // ### block on maxSize == 0?
+       qDebug( "KDPipeIODevice::readData: waiting for bufferNotEmptyCondition" );
+       r->bufferNotEmptyCondition.wait( &r->mutex );
+    }
+
+    if ( r->bufferEmpty() ) {
+       qDebug( "KDPipeIODevice::readData: got empty buffer, signal eof" );
+       // woken with an empty buffer must mean either EOF or error:
+       assert( r->eof || r->error );
+       r->eofShortCut = true;
+       return r->eof ? 0 : -1 ;
+    }
+
+    qDebug( "KDPipeIODevice::readData: got bufferNotEmptyCondition, trying to read %lld bytes", maxSize );
+    const qint64 bytesRead = r->readData( data, maxSize );
+    qDebug( "KDPipeIODevice::readData: read %lld bytes", bytesRead );
+    return bytesRead;
+}
+
+qint64 Reader::readData( char * data, qint64 maxSize ) {
+
+    qint64 numRead = rptr < wptr ? wptr - rptr : sizeof buffer - rptr ;
+    if ( numRead > maxSize )
+       numRead = maxSize;
+
+    qDebug( "KDPipeIODevice::readData: data=%p, maxSize=%lld; rptr=%u, wptr=%u (bytesInBuffer=%u); -> numRead=%lld",
+           data, maxSize, rptr, wptr, bytesInBuffer(), numRead );
+
+    std::memcpy( data, buffer + rptr, numRead );
+
+    rptr = ( rptr + numRead ) % sizeof buffer ;
+
+    if ( !bufferFull() ) {
+       qDebug( "KDPipeIODevice::readData: signal bufferNotFullCondition" );
+       bufferNotFullCondition.wakeAll();
+    }
+
+    return numRead;
+}
+
+qint64 KDPipeIODevice::writeData( const char * data, qint64 size ) { KDAB_CHECK_THIS;
+
+    Writer * const w = d->writer;
+
+    assert( w );
+    assert( w->error || w->isRunning() );
+    assert( data || size == 0 );
+    assert( size >= 0 );
+
+    LOCKED( w );
+
+    while ( !w->error && !w->bufferEmpty() )
+       w->bufferEmptyCondition.wait( &w->mutex );
+
+    if ( w->error )
+       return -1;
+
+    assert( w->bufferEmpty() );
+
+    return w->writeData( data, size );
+}
+
+qint64 Writer::writeData( const char * data, qint64 size ) {
+
+    assert( bufferEmpty() );
+
+    if ( size > static_cast<qint64>( sizeof buffer ) )
+       size = sizeof buffer;
+
+    std::memcpy( buffer, data, size );
+    
+    numBytesInBuffer = size;
+
+    if ( !bufferEmpty() )
+       bufferNotEmptyCondition.wakeAll();
+
+    return size;
+}
+
+void KDPipeIODevice::close() { KDAB_CHECK_THIS;
+
+    if ( !isOpen() )
+       return;
+
+    // tell clients we're about to close:
+    emit aboutToClose();
+
+    if ( d->writer && bytesToWrite() > 0 )
+       waitForBytesWritten( -1 );
+
+    assert( bytesToWrite() == 0 );
+
+    if ( Reader * & r = d->reader ) {
+       synchronized( r ) {
+           // tell thread to cancel:
+           r->cancel = true;
+           // and wake it, so it can terminate:
+           r->bufferNotFullCondition.wakeAll();
+       }
+       r->wait();
+       delete r; r = 0;
+    }
+    if ( Writer * & w = d->writer ) {
+       synchronized( w ) {
+           // tell thread to cancel:
+           w->cancel = true;
+           // and wake it, so it can terminate:
+           w->bufferNotEmptyCondition.wakeAll();
+       }
+       w->wait();
+       delete w; w = 0;
+    }
+
+#ifdef Q_OS_WIN32
+    CloseHandle( d->handle );
+#else
+    ::close( d->fd );
+#endif
+
+    setOpenMode( NotOpen );
+    d->fd = -1;
+    d->handle = 0;
+}
+
+void Reader::run() {
+
+    LOCKED( this );
+
+    // too bad QThread doesn't have that itself; a signal isn't enough
+    hasStarted.wakeAll();
+
+    qDebug( "Reader::run: started" );
+
+    while ( true ) {
+
+       while ( !cancel && bufferFull() ) {
+           bufferNotEmptyCondition.wakeAll();
+           qDebug( "Reader::run: buffer is full, going to sleep" );
+           bufferNotFullCondition.wait( &mutex );
+           qDebug( "Reader::run: woke up" );
+       }
+
+       if ( cancel ) {
+           qDebug( "Reader::run: detected cancel" );
+           goto leave;
+       }
+
+       if ( rptr == wptr ) // optimize for larger chunks in case the buffer is empty
+           rptr = wptr = 0;
+
+       unsigned int numBytes = ( rptr + sizeof buffer - wptr - 1 ) % sizeof buffer;
+       if ( numBytes > sizeof buffer - wptr )
+           numBytes = sizeof buffer - wptr;
+
+       qDebug( "Reader::run: rptr=%d, wptr=%d -> numBytes=%d", rptr, wptr, numBytes );
+
+       assert( numBytes > 0 );
+
+       qDebug( "Reader::run: trying to read %d bytes", numBytes );
+#ifdef Q_OS_WIN32
+       DWORD numRead;
+       mutex.unlock();
+       const bool ok = ReadFile( handle, buffer + wptr, numBytes, &numRead, 0 );
+       mutex.lock();
+       if ( !ok ) {
+           errorCode = static_cast<int>( GetLastError() );
+           if ( errorCode == ERROR_BROKEN_PIPE ) {
+               qDebug( "Reader::run: got eof" );
+               eof = true;
+           } else {
+               qDebug( "Reader::run: got error: %d", errorCode );
+               error = true;
+           }
+           goto leave;
+       }
+#else
+       qint64 numRead;
+       mutex.unlock();
+       do {
+           numRead = ::read( fd, buffer + wptr, numBytes );
+       } while ( numRead == -1 && errno == EINTR );
+       mutex.lock();
+
+       if ( numRead < 0 ) {
+           errorCode = errno;
+           error = true;
+           qDebug( "Reader::run: got error: %d", errorCode );
+           goto leave;
+       }
+#endif
+       qDebug( "Reader::run: read %ld bytes", static_cast<long>(numRead) );
+       if ( numRead == 0 ) {
+           qDebug( "Reader::run: eof detected" );
+           eof = true;
+           goto leave;
+       }
+
+       if ( cancel ) {
+           qDebug( "Reader::run: detected cancel" );
+           goto leave;
+       }
+       qDebug( "Reader::run: buffer before: rptr=%4d, wptr=%4d", rptr, wptr );
+       wptr = ( wptr + numRead ) % sizeof buffer;
+       qDebug( "Reader::run: buffer after:  rptr=%4d, wptr=%4d", rptr, wptr );
+       if ( !bufferEmpty() ) {
+           qDebug( "Reader::run: buffer no longer empty, waking everyone" );
+           bufferNotEmptyCondition.wakeAll();
+           emit readyRead();
+       }
+    }
+ leave:
+    qDebug( "Reader::run: terminating" );
+    bufferNotEmptyCondition.wakeAll();
+    emit readyRead();
+}
+
+void Writer::run() {
+
+    LOCKED( this );
+
+    // too bad QThread doesn't have that itself; a signal isn't enough
+    hasStarted.wakeAll();
+
+    qDebug( "Writer::run: started" );
+
+    while ( true ) {
+
+       while ( !cancel && bufferEmpty() ) {
+           bufferEmptyCondition.wakeAll();
+           qDebug( "Writer::run: buffer is empty, going to sleep" );
+           bufferNotEmptyCondition.wait( &mutex );
+           qDebug( "Writer::run: woke up" );
+       }
+
+       if ( cancel ) {
+           qDebug( "Writer::run: detected cancel" );
+           goto leave;
+       }
+
+       assert( numBytesInBuffer > 0 );
+
+       qDebug( "Writer::run: Trying to write %u bytes", numBytesInBuffer );
+       qint64 totalWritten = 0;
+       do { 
+           mutex.unlock();
+#ifdef Q_OS_WIN32
+           DWORD numWritten;
+           if ( !WriteFile( handle, buffer + totalWritten, numBytesInBuffer - totalWritten, &numWritten, 0 ) ) {
+               mutex.lock();
+               errorCode = static_cast<int>( GetLastError() );
+               qDebug( "Writer::run: got error code: %d", errorCode );
+               error = true;
+               goto leave;
+           }
+#else
+           qint64 numWritten;
+           do {
+               numWritten = ::write( fd, buffer + totalWritten, numBytesInBuffer - totalWritten );
+           } while ( numWritten == -1 && errno == EINTR );
+
+           if ( numWritten < 0 ) {
+               mutex.lock();
+               errorCode = errno;
+               qDebug( "Writer::run: got error code: %d", errorCode );
+               error = true;
+               goto leave;
+           }
+#endif
+           totalWritten += numWritten;
+           mutex.lock();
+       } while ( totalWritten < numBytesInBuffer );
+
+       qDebug( "Writer::run: wrote %lld bytes", totalWritten );
+
+       numBytesInBuffer = 0;
+       bufferEmptyCondition.wakeAll();
+       emit bytesWritten( totalWritten );
+    }
+ leave:
+    qDebug( "Writer::run: terminating" );
+    numBytesInBuffer = 0;
+    bufferEmptyCondition.wakeAll();
+    emit bytesWritten( 0 );
+}
+
+// static 
+std::pair<KDPipeIODevice*,KDPipeIODevice*> KDPipeIODevice::makePairOfConnectedPipes() {
+    KDPipeIODevice * read = 0;
+    KDPipeIODevice * write = 0;
+#ifdef Q_OS_WIN32
+    HANDLE rh;
+    HANDLE wh;
+    SECURITY_ATTRIBUTES sa;
+    memset( &sa, 0, sizeof(sa) );
+    sa.nLength = sizeof(sa);
+    sa.bInheritHandle = TRUE;
+       if ( CreatePipe( &rh, &wh, &sa, BUFFER_SIZE ) ) {
+       read = new KDPipeIODevice;
+       read->open( rh, ReadOnly );
+       write = new KDPipeIODevice;
+       write->open( wh, WriteOnly );
+    }
+#else
+    int fds[2];
+    if ( pipe( fds ) == 0 ) {
+       read = new KDPipeIODevice;
+       read->open( fds[0], ReadOnly );
+       write = new KDPipeIODevice;
+       write->open( fds[1], WriteOnly );
+    }
+#endif
+    return std::make_pair( read, write );
+}
+
+#ifdef KDAB_DEFINE_CHECKS
+KDAB_DEFINE_CHECKS( KDPipeIODevice ) {
+    if ( !isOpen() ) {
+       assert( openMode() == NotOpen );
+       assert( !d->reader );
+       assert( !d->writer );
+#ifdef Q_OS_WIN32
+       assert( !d->handle );
+#else
+       assert( d->fd < 0 );
+#endif
+    } else {
+       assert( openMode() != NotOpen );
+       assert( openMode() & ReadWrite );
+       if ( openMode() & ReadOnly ) {
+           assert( d->reader );
+           synchronized( d->reader )
+               assert( d->reader->eof || d->reader->error || d->reader->isRunning() );
+       }
+       if ( openMode() & WriteOnly ) {
+           assert( d->writer );
+           synchronized( d->writer )
+               assert( d->writer->error || d->writer->isRunning() );
+       }
+#ifdef Q_OS_WIN32
+       assert( d->handle );
+#else
+       assert( d->fd >= 0 );
+#endif
+    }
+}
+#endif // KDAB_DEFINE_CHECKS
+
+#include "moc_kdpipeiodevice.cpp"
+#include "kdpipeiodevice.moc"
diff --git a/trunk/gpgme/kdpipeiodevice.h b/trunk/gpgme/kdpipeiodevice.h
new file mode 100644 (file)
index 0000000..ce2873f
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+  Copyright (C) 2007 Klarälvdalens Datakonsult AB
+
+  KDPipeIODevice is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  KDPipeIODevice 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 Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with KDPipeIODevice; see the file COPYING.LIB.  If not, write to the
+  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KDTOOLSCORE_KDPIPEIODEVICE_H__
+#define __KDTOOLSCORE_KDPIPEIODEVICE_H__
+
+#include <QIODevice>
+
+#include <utility>
+
+//#include "checker.h"
+
+class KDPipeIODevice : public QIODevice {
+    Q_OBJECT
+    //KDAB_MAKE_CHECKABLE( KDPipeIODevice )
+public:
+    explicit KDPipeIODevice( QObject * parent=0 );
+    explicit KDPipeIODevice( int fd, OpenMode=ReadOnly, QObject * parent=0 );
+    explicit KDPipeIODevice( Qt::HANDLE handle, OpenMode=ReadOnly, QObject * parent=0 );
+    ~KDPipeIODevice();
+
+    static std::pair<KDPipeIODevice*, KDPipeIODevice*> makePairOfConnectedPipes();
+
+    bool open( int fd, OpenMode mode=ReadOnly );
+    bool open( Qt::HANDLE handle, OpenMode mode=ReadOnly );
+
+    Qt::HANDLE handle() const;
+    int descriptor() const;
+
+    /* reimp */ qint64 bytesAvailable() const;
+    /* reimp */ qint64 bytesToWrite() const;
+    /* reimp */ bool canReadLine() const;
+    /* reimp */ void close();
+    /* reimp */ bool isSequential() const;
+    /* reimp */ bool atEnd() const;
+
+    /* reimp */ bool waitForBytesWritten( int msecs );
+    /* reimp */ bool waitForReadyRead( int msecs );
+
+protected:
+    /* reimp */ qint64 readData( char * data, qint64 maxSize );
+    /* reimp */ qint64 writeData( const char * data, qint64 maxSize );
+
+private:
+    class Private;
+    Private * d;
+};
+
+#endif /* __KDTOOLSCORE_KDPIPEIODEVICE_H__ */
+
diff --git a/trunk/gpgme/kdpipeiodevice.moc b/trunk/gpgme/kdpipeiodevice.moc
new file mode 100644 (file)
index 0000000..e00d0bc
--- /dev/null
@@ -0,0 +1,132 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'kdpipeiodevice.cpp'
+**
+** Created: Mon Aug 27 15:17:18 2007
+**      by: The Qt Meta Object Compiler version 59 (Qt 4.3.0)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'kdpipeiodevice.cpp' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 59
+#error "This file was generated using the moc from 4.3.0. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+static const uint qt_meta_data_Reader[] = {
+
+ // content:
+       1,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       1,   10, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+
+ // signals: signature, parameters, type, tag, flags
+       8,    7,    7,    7, 0x05,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_Reader[] = {
+    "Reader\0\0readyRead()\0"
+};
+
+const QMetaObject Reader::staticMetaObject = {
+    { &QThread::staticMetaObject, qt_meta_stringdata_Reader,
+      qt_meta_data_Reader, 0 }
+};
+
+const QMetaObject *Reader::metaObject() const
+{
+    return &staticMetaObject;
+}
+
+void *Reader::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_Reader))
+       return static_cast<void*>(const_cast< Reader*>(this));
+    return QThread::qt_metacast(_clname);
+}
+
+int Reader::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QThread::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        switch (_id) {
+        case 0: readyRead(); break;
+        }
+        _id -= 1;
+    }
+    return _id;
+}
+
+// SIGNAL 0
+void Reader::readyRead()
+{
+    QMetaObject::activate(this, &staticMetaObject, 0, 0);
+}
+static const uint qt_meta_data_Writer[] = {
+
+ // content:
+       1,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       1,   10, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+
+ // signals: signature, parameters, type, tag, flags
+       8,    7,    7,    7, 0x05,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_Writer[] = {
+    "Writer\0\0bytesWritten(qint64)\0"
+};
+
+const QMetaObject Writer::staticMetaObject = {
+    { &QThread::staticMetaObject, qt_meta_stringdata_Writer,
+      qt_meta_data_Writer, 0 }
+};
+
+const QMetaObject *Writer::metaObject() const
+{
+    return &staticMetaObject;
+}
+
+void *Writer::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_Writer))
+       return static_cast<void*>(const_cast< Writer*>(this));
+    return QThread::qt_metacast(_clname);
+}
+
+int Writer::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QThread::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        switch (_id) {
+        case 0: bytesWritten((*reinterpret_cast< qint64(*)>(_a[1]))); break;
+        }
+        _id -= 1;
+    }
+    return _id;
+}
+
+// SIGNAL 0
+void Writer::bytesWritten(qint64 _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 0, _a);
+}
diff --git a/trunk/gpgme/moc_kdpipeiodevice.cpp b/trunk/gpgme/moc_kdpipeiodevice.cpp
new file mode 100644 (file)
index 0000000..eac7b23
--- /dev/null
@@ -0,0 +1,60 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'kdpipeiodevice.h'
+**
+** Created: Mon Aug 27 15:17:18 2007
+**      by: The Qt Meta Object Compiler version 59 (Qt 4.3.0)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "kdpipeiodevice.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'kdpipeiodevice.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 59
+#error "This file was generated using the moc from 4.3.0. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+static const uint qt_meta_data_KDPipeIODevice[] = {
+
+ // content:
+       1,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       0,    0, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_KDPipeIODevice[] = {
+    "KDPipeIODevice\0"
+};
+
+const QMetaObject KDPipeIODevice::staticMetaObject = {
+    { &QIODevice::staticMetaObject, qt_meta_stringdata_KDPipeIODevice,
+      qt_meta_data_KDPipeIODevice, 0 }
+};
+
+const QMetaObject *KDPipeIODevice::metaObject() const
+{
+    return &staticMetaObject;
+}
+
+void *KDPipeIODevice::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_KDPipeIODevice))
+       return static_cast<void*>(const_cast< KDPipeIODevice*>(this));
+    return QIODevice::qt_metacast(_clname);
+}
+
+int KDPipeIODevice::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QIODevice::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    return _id;
+}
index fa2b8d37ba97c7cfc8a420bae267942b9c0550da..4ec3abb0b4b9482ad54d3c844736c22b992370db 100644 (file)
@@ -1,6 +1,6 @@
 /* posix-sema.c 
    Copyright (C) 2001 Werner Koch (dd9jn)
-   Copyright (C) 2001, 2002, 2004 g10 Code GmbH
+   Copyright (C) 2001, 2002, 2004, 2007 g10 Code GmbH
 
    This file is part of GPGME.
  
@@ -45,18 +45,18 @@ _gpgme_sema_subsystem_init ()
 void
 _gpgme_sema_cs_enter (struct critsect_s *s)
 {
-  _gpgme_ath_mutex_lock (&s->private);
+  _gpgme_ath_mutex_lock (&s->priv);
 }
 
 void
 _gpgme_sema_cs_leave (struct critsect_s *s)
 {
-  _gpgme_ath_mutex_unlock (&s->private);
+  _gpgme_ath_mutex_unlock (&s->priv);
 }
 
 void
 _gpgme_sema_cs_destroy (struct critsect_s *s)
 {
-  _gpgme_ath_mutex_destroy (&s->private);
-  s->private = NULL;
+  _gpgme_ath_mutex_destroy (&s->priv);
+  s->priv = NULL;
 }
index 0ee20742ef4ffaf713eb2bcc10d4bf0cc4ac0582..7d3870e97ce71f723d33fd10e4792ec5f13efbd3 100644 (file)
@@ -1,6 +1,6 @@
 /* sema.h - Definitions for semaphores.
    Copyright (C) 2000 Werner Koch (dd9jn)
-   Copyright (C) 2001, 2003, 2004 g10 Code GmbH
+   Copyright (C) 2001, 2003, 2004, 2007 g10 Code GmbH
 
    This file is part of GPGME.
  
@@ -25,7 +25,7 @@
 struct critsect_s
 {
   const char *name;
-  void *private;
+  void *priv;
 };
 
 #define DEFINE_GLOBAL_LOCK(name) \
@@ -39,7 +39,7 @@ struct critsect_s
   do                                   \
     {                                  \
       (a).name = #a;                   \
-      (a).private = NULL;              \
+      (a).priv = NULL;                 \
     }                                  \
   while (0)
 #define DESTROY_LOCK(name) _gpgme_sema_cs_destroy (&(name))
index 232ed533a9e7eb362bbc4a2cd8b1899570a0530e..a0301b67d8db52bf78cbb6710e48edfe5ac474f8 100644 (file)
@@ -97,14 +97,23 @@ find_channel (int fd, int create)
   return giochannel_table[fd];
 }
 
-/* Look up the giochannel for "file descriptor" FD.  */
-GIOChannel *
+
+/* Compatibility interface.  Obsolete.  */
+void *
 gpgme_get_giochannel (int fd)
 {
   return find_channel (fd, 0);
 }
 
 
+/* Look up the giochannel for "file descriptor" FD.  */
+void *
+gpgme_get_fdptr (int fd)
+{
+  return find_channel (fd, 0);
+}
+
+
 /* Write the printable version of FD to the buffer BUF of length
    BUFLEN.  The printable version is the representation on the command
    line that the child process expects.  */
index dfdf45e5bf04687b624a2d1519ed8d53ac06d6a9..a4a425698824d5bcfe39ef727854df4b8f57bc5e 100644 (file)
@@ -1403,9 +1403,9 @@ _gpgme_io_dup (int fd)
 }
 
 \f
-/* The following interface is only useful for GPGME Glib.  */
+/* The following interface is only useful for GPGME Glib and Qt.  */
 
-/* Look up the giochannel for file descriptor FD.  */
+/* Compatibility interface, obsolete.  */
 void *
 gpgme_get_giochannel (int fd)
 {
@@ -1413,3 +1413,9 @@ gpgme_get_giochannel (int fd)
 }
 
 
+/* Look up the giochannel or qiodevice for file descriptor FD.  */
+void *
+gpgme_get_fdptr (int fd)
+{
+  return NULL;
+}
diff --git a/trunk/gpgme/w32-qt-io.cpp b/trunk/gpgme/w32-qt-io.cpp
new file mode 100644 (file)
index 0000000..9117066
--- /dev/null
@@ -0,0 +1,609 @@
+/* w32-glib-io.c - W32 Glib I/O functions
+   Copyright (C) 2000 Werner Koch (dd9jn)
+   Copyright (C) 2001, 2002, 2004, 2005, 2007 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 Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 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
+   Lesser General Public License for more details.
+   
+   You should have received a copy of the GNU Lesser 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.  */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <windows.h>
+#include <io.h>
+
+#include "kdpipeiodevice.h"
+
+extern "C"
+{
+#include "util.h"
+#include "priv-io.h"
+#include "sema.h"
+#include "debug.h"
+}
+
+#ifndef O_BINARY
+#ifdef _O_BINARY
+#define O_BINARY       _O_BINARY
+#else
+#define O_BINARY       0
+#endif
+#endif
+
+\f
+/* This file is an ugly hack to get GPGME working with Qt on Windows
+   targets.  On Windows, you can not select() on file descriptors.
+   The only way to check if there is something to read is to read
+   something.  This means that GPGME can not let Qt check for data
+   without letting Qt also handle the data on Windows targets.
+
+   The ugly consequence is that we need to work on QIODevices in
+   GPGME, creating a Qt dependency.  Also, we need to export an
+   interface for the application to get at GPGME's QIODevices.  There
+   is no good way to abstract all this with callbacks, because the
+   whole thing is also interconnected with the creation of pipes and
+   child processes.
+
+   The following rule applies only to this I/O backend:
+
+   * ALL operations must use the user defined event loop.  GPGME can
+   not anymore provide its own event loop.  This is mostly a sanity
+   requirement: Although we have in theory all information we need to
+   make the GPGME W32 code for select still work, it would be a big
+   complication and require changes throughout GPGME.
+
+   Eventually, we probably have to bite the bullet and make some
+   really nice callback interfaces to let the user control all this at
+   a per-context level.  */
+
+\f
+#define MAX_SLAFD 256
+
+QIODevice *iodevice_table[MAX_SLAFD];
+
+
+static QIODevice *
+find_channel (int fd, int create)
+{
+  if (fd < 0 || fd >= MAX_SLAFD)
+    return NULL;
+
+  if (create && !iodevice_table[fd])
+    iodevice_table[fd] = new KDPipeIODevice
+      (fd, QIODevice::ReadOnly|QIODevice::Unbuffered);
+
+  return iodevice_table[fd];
+}
+
+
+/* Write the printable version of FD to the buffer BUF of length
+   BUFLEN.  The printable version is the representation on the command
+   line that the child process expects.  */
+int
+_gpgme_io_fd2str (char *buf, int buflen, int fd)
+{
+  return snprintf (buf, buflen, "%ld", (long) _get_osfhandle (fd));
+}
+
+\f
+void
+_gpgme_io_subsystem_init (void)
+{
+}
+
+\f
+static struct
+{
+  _gpgme_close_notify_handler_t handler;
+  void *value;
+} notify_table[MAX_SLAFD];
+
+
+int
+_gpgme_io_read (int fd, void *buffer, size_t count)
+{
+  int saved_errno = 0;
+  qint64 nread;
+  QIODevice *chan;
+  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
+             "buffer=%p, count=%u", buffer, count);
+
+  chan = find_channel (fd, 0);
+  if (!chan)
+    {
+      TRACE_LOG ("no channel registered");
+      errno = EINVAL;
+      return TRACE_SYSRES (-1);
+    }
+  TRACE_LOG1 ("channel %p", chan);
+
+  {
+//  GError *err = NULL;
+//  status = g_io_channel_read_chars (chan, (gchar *) buffer,
+//                                   count, &nread, &err);
+    nread = chan->read( buffer, count );
+    if ( nread < 0 ) {
+        TRACE_LOG1 ("err %s", qPrintable( chan->errorString() ) );
+        saved_errno = EIO;
+        nread = -1;
+    }
+  }
+
+  TRACE_LOGBUF (buffer, nread);
+
+  errno = saved_errno;
+  return TRACE_SYSRES (nread);
+}
+
+
+int
+_gpgme_io_write (int fd, const void *buffer, size_t count)
+{
+  qint64 nwritten;
+  QIODevice *chan;
+  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
+             "buffer=%p, count=%u", buffer, count);
+  TRACE_LOGBUF (buffer, count);
+
+  chan = find_channel (fd, 0);
+  if (!chan)
+    {
+      TRACE_LOG ("fd %d: no channel registered");
+      errno = EINVAL;
+      return -1;
+    }
+
+  nwritten = chan->write( buffer, count );
+
+  if (nwritten < 0)
+    {
+      nwritten = -1;
+      errno = EIO;
+      return TRACE_SYSRES(-1)
+    }
+  errno = 0;
+  return TRACE_SYSRES (nwritten);
+}
+
+
+int
+_gpgme_io_pipe (int filedes[2], int inherit_idx)
+{
+  QIODevice *chan;
+  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
+             "inherit_idx=%i (GPGME uses it for %s)",
+             inherit_idx, inherit_idx ? "writing" : "reading");
+
+#define PIPEBUF_SIZE  4096
+  if (_pipe (filedes, PIPEBUF_SIZE, O_NOINHERIT | O_BINARY) == -1)
+    return TRACE_SYSRES (-1);
+
+  /* Make one end inheritable. */
+  if (inherit_idx == 0)
+    {
+      int new_read;
+
+      new_read = _dup (filedes[0]);
+      _close (filedes[0]);
+      filedes[0] = new_read;
+
+      if (new_read < 0)
+       {
+         _close (filedes[1]);
+         return TRACE_SYSRES (-1);
+       }
+    }
+  else if (inherit_idx == 1)
+    {
+      int new_write;
+
+      new_write = _dup (filedes[1]);
+      _close (filedes[1]);
+      filedes[1] = new_write;
+
+      if (new_write < 0)
+       {
+         _close (filedes[0]);
+         return TRACE_SYSRES (-1);
+       }
+    }
+
+  /* Now we have a pipe with the right end inheritable.  The other end
+     should have a giochannel.  */
+  chan = find_channel (filedes[1 - inherit_idx], 1);
+  if (!chan)
+    {
+      int saved_errno = errno;
+      _close (filedes[0]);
+      _close (filedes[1]);
+      errno = saved_errno;
+      return TRACE_SYSRES (-1);
+    }
+
+  return TRACE_SUC5 ("read=0x%x/%p, write=0x%x/%p, channel=%p",
+         filedes[0], (HANDLE) _get_osfhandle (filedes[0]),
+         filedes[1], (HANDLE) _get_osfhandle (filedes[1]),
+         chan);
+}
+
+
+int
+_gpgme_io_close (int fd)
+{
+  QIODevice *chan;
+  TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
+
+  if (fd < 0 || fd >= MAX_SLAFD)
+    {
+      errno = EBADF;
+      return TRACE_SYSRES (-1);
+    }
+
+  /* First call the notify handler.  */
+  if (notify_table[fd].handler)
+    {
+      notify_table[fd].handler (fd, notify_table[fd].value);
+      notify_table[fd].handler = NULL;
+      notify_table[fd].value = NULL;
+    }
+
+  /* Then do the close.  */    
+  chan = iodevice_table[fd];
+  if (chan)
+    {
+      chan->close();
+      delete chan;
+      iodevice_table[fd] = NULL;
+    }
+  else
+    _close (fd);
+
+  return 0;
+}
+
+
+int
+_gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
+                           void *value)
+{
+  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
+             "close_handler=%p/%p", handler, value);
+
+  assert (fd != -1);
+
+  if (fd < 0 || fd >= (int) DIM (notify_table))
+    {
+      errno = EINVAL;
+      return TRACE_SYSRES (-1);
+    }
+  notify_table[fd].handler = handler;
+  notify_table[fd].value = value;
+  return TRACE_SYSRES (0);
+}
+
+
+int
+_gpgme_io_set_nonblocking (int fd)
+{
+  /* Qt always uses non-blocking IO, except for files, maybe, but who
+     uses that?  */
+  TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
+
+  return TRACE_SYSRES (0);
+}
+
+
+static char *
+build_commandline (char **argv)
+{
+  int i;
+  int n = 0;
+  char *buf;
+  char *p;
+  
+  /* We have to quote some things because under Windows the program
+     parses the commandline and does some unquoting.  We enclose the
+     whole argument in double-quotes, and escape literal double-quotes
+     as well as backslashes with a backslash.  We end up with a
+     trailing space at the end of the line, but that is harmless.  */
+  for (i = 0; argv[i]; i++)
+    {
+      p = argv[i];
+      /* The leading double-quote.  */
+      n++;
+      while (*p)
+       {
+         /* An extra one for each literal that must be escaped.  */
+         if (*p == '\\' || *p == '"')
+           n++;
+         n++;
+         p++;
+       }
+      /* The trailing double-quote and the delimiter.  */
+      n += 2;
+    }
+  /* And a trailing zero.  */
+  n++;
+
+  buf = p = malloc (n);
+  if (!buf)
+    return NULL;
+  for (i = 0; argv[i]; i++)
+    {
+      char *argvp = argv[i];
+
+      *(p++) = '"';
+      while (*argvp)
+       {
+         if (*argvp == '\\' || *argvp == '"')
+           *(p++) = '\\';
+         *(p++) = *(argvp++);
+       }
+      *(p++) = '"';
+      *(p++) = ' ';
+    }
+  *(p++) = 0;
+
+  return buf;
+}
+
+
+int
+_gpgme_io_spawn (const char *path, char **argv,
+                struct spawn_fd_item_s *fd_child_list,
+                struct spawn_fd_item_s *fd_parent_list)
+{
+  SECURITY_ATTRIBUTES sec_attr;
+  PROCESS_INFORMATION pi =
+    {
+      NULL,      /* returns process handle */
+      0,         /* returns primary thread handle */
+      0,         /* returns pid */
+      0         /* returns tid */
+    };
+  STARTUPINFO si;
+  char *envblock = NULL;
+  int cr_flags = CREATE_DEFAULT_ERROR_MODE
+    | GetPriorityClass (GetCurrentProcess ());
+  int i;
+  char *arg_string;
+  int duped_stdin = 0;
+  int duped_stderr = 0;
+  HANDLE hnul = INVALID_HANDLE_VALUE;
+  /* FIXME.  */
+  int debug_me = 0;
+  TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
+             "path=%s", path);
+  i = 0;
+  while (argv[i])
+    {
+      TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
+      i++;
+    }
+  
+  memset (&sec_attr, 0, sizeof sec_attr);
+  sec_attr.nLength = sizeof sec_attr;
+  sec_attr.bInheritHandle = FALSE;
+  
+  arg_string = build_commandline (argv);
+  if (!arg_string)
+    return TRACE_SYSRES (-1);
+  
+  memset (&si, 0, sizeof si);
+  si.cb = sizeof (si);
+  si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+  si.wShowWindow = debug_me? SW_SHOW : SW_HIDE;
+  si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
+  si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
+  si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
+  
+  for (i = 0; fd_child_list[i].fd != -1; i++)
+    {
+      if (fd_child_list[i].dup_to == 0)
+       {
+         si.hStdInput = (HANDLE) _get_osfhandle (fd_child_list[i].fd);
+         TRACE_LOG2 ("using 0x%x/%p for stdin", fd_child_list[i].fd,
+                     _get_osfhandle (fd_child_list[i].fd));
+         duped_stdin = 1;
+        }
+      else if (fd_child_list[i].dup_to == 1)
+       {
+         si.hStdOutput = (HANDLE) _get_osfhandle (fd_child_list[i].fd);
+         TRACE_LOG2 ("using 0x%x/%p for stdout", fd_child_list[i].fd,
+                     _get_osfhandle (fd_child_list[i].fd));
+       }
+      else if (fd_child_list[i].dup_to == 2)
+       {
+         si.hStdError = (HANDLE) _get_osfhandle (fd_child_list[i].fd);
+         TRACE_LOG2 ("using 0x%x/%p for stderr", fd_child_list[i].fd,
+                     _get_osfhandle (fd_child_list[i].fd));
+         duped_stderr = 1;
+        }
+    }
+  
+  if (!duped_stdin || !duped_stderr)
+    {
+      SECURITY_ATTRIBUTES sa;
+      
+      memset (&sa, 0, sizeof sa);
+      sa.nLength = sizeof sa;
+      sa.bInheritHandle = TRUE;
+      hnul = CreateFile ("nul",
+                        GENERIC_READ|GENERIC_WRITE,
+                        FILE_SHARE_READ|FILE_SHARE_WRITE,
+                        &sa,
+                        OPEN_EXISTING,
+                        FILE_ATTRIBUTE_NORMAL,
+                        NULL);
+      if (hnul == INVALID_HANDLE_VALUE)
+       {
+         TRACE_LOG1 ("CreateFile (\"nul\") failed: ec=%d",
+                     (int) GetLastError ());
+         free (arg_string);
+         /* FIXME: Should translate the error code.  */
+         errno = EIO;
+         return TRACE_SYSRES (-1);
+        }
+      /* Make sure that the process has a connected stdin.  */
+      if (!duped_stdin)
+       {
+         si.hStdInput = hnul;
+         TRACE_LOG1 ("using 0x%x for dummy stdin", (int) hnul);
+       }
+      /* We normally don't want all the normal output.  */
+      if (!duped_stderr)
+       {
+         si.hStdError = hnul;
+         TRACE_LOG1 ("using %d for dummy stderr", (int)hnul);
+       }
+    }
+  
+  cr_flags |= CREATE_SUSPENDED;
+  cr_flags |= DETACHED_PROCESS;
+  if (!CreateProcessA (path,
+                      arg_string,
+                      &sec_attr,     /* process security attributes */
+                      &sec_attr,     /* thread security attributes */
+                      TRUE,          /* inherit handles */
+                      cr_flags,      /* creation flags */
+                      envblock,      /* environment */
+                      NULL,          /* use current drive/directory */
+                      &si,           /* startup information */
+                      &pi))          /* returns process information */
+    {
+      TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
+      free (arg_string);
+      /* FIXME: Should translate the error code.  */
+      errno = EIO;
+      return TRACE_SYSRES (-1);
+    }
+  
+  /* Close the /dev/nul handle if used.  */
+  if (hnul != INVALID_HANDLE_VALUE)
+    {
+      if (!CloseHandle (hnul))
+       TRACE_LOG1 ("CloseHandle (hnul) failed: ec=%d (ignored)",
+                   (int) GetLastError ());
+    }
+  
+  /* Close the other ends of the pipes.  */
+  for (i = 0; fd_parent_list[i].fd != -1; i++)
+    _gpgme_io_close (fd_parent_list[i].fd);
+  
+  TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
+             "dwProcessID=%d, dwThreadId=%d",
+             pi.hProcess, pi.hThread, 
+             (int) pi.dwProcessId, (int) pi.dwThreadId);
+  
+  if (ResumeThread (pi.hThread) < 0)
+    TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
+  
+  if (!CloseHandle (pi.hThread))
+    TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
+               (int) GetLastError ());
+
+  TRACE_SUC1 ("process=%p", pi.hProcess);
+  return 0;
+}
+
+
+/* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
+   nothing to select, > 0 = number of signaled fds.  */
+int
+_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
+{
+  int i;
+  int count;
+  /* Use a 1s timeout.  */
+
+  void *dbg_help = NULL;
+  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
+             "nfds=%u, nonblock=%u", nfds, nonblock);
+
+  // we only implement the special case of nonblock == true
+  assert( nonblock );
+
+  count = 0;
+
+  TRACE_SEQ (dbg_help, "select on [ ");
+  for (i = 0; i < nfds; i++)
+    {
+      if (fds[i].fd == -1)
+        {
+          fds[i].signaled = 0;
+        }
+      else if (fds[i].frozen)
+        {
+          TRACE_ADD1 (dbg_help, "f0x%x ", fds[i].fd);
+          fds[i].signaled = 0;
+        }
+      else if (fds[i].for_read )
+        {
+          const QIODevice * const chan = find_channel (fds[i].fd, 0);
+          assert (chan);
+          fds[i].signaled = chan->bytesAvailable() > 0 ? 1 : 0 ;
+          TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
+          count++;
+        }
+      else if (fds[i].for_write)
+        {
+          const QIODevice * const chan = find_channel (fds[i].fd, 0);
+          assert (chan);
+          fds[i].signaled = chan->bytesToWrite() > 0 ? 0 : 1 ;
+          TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
+          count++;
+        }
+    }
+  TRACE_END (dbg_help, "]"); 
+
+  return TRACE_SYSRES (count);
+}
+
+
+int
+_gpgme_io_dup (int fd)
+{
+  return _dup (fd);
+}
+
+\f
+/* Look up the qiodevice for file descriptor FD.  */
+extern "C"
+void *
+gpgme_get_fdptr (int fd)
+{
+  return find_channel (fd, 0);
+}
+
+
+/* Obsolete compatibility interface.  */
+extern "C"
+void *
+gpgme_get_giochannel (int fd)
+{
+  return NULL;
+}
+
index c20f1a7b7e62f738d62790aa89ed0e328ac46e24..5228cc7cf9cc1bb98478f4017c153c87f203ef47 100644 (file)
@@ -1,6 +1,6 @@
 /* w32-sema.c 
    Copyright (C) 2001 Werner Koch (dd9jn)
-   Copyright (C) 2001, 2002, 2004 g10 Code GmbH
+   Copyright (C) 2001, 2002, 2004, 2007 g10 Code GmbH
 
    This file is part of GPGME.
  
@@ -65,7 +65,7 @@ critsect_init (struct critsect_s *s)
 
     /* first test whether it is really not initialized */
     EnterCriticalSection (&init_lock);
-    if ( s->private ) {
+    if ( s->priv ) {
         LeaveCriticalSection (&init_lock);
         return;
     }
@@ -76,7 +76,7 @@ critsect_init (struct critsect_s *s)
         sema_fatal ("out of core while creating critical section lock");
     }
     InitializeCriticalSection (mp);
-    s->private = mp;
+    s->priv = mp;
     LeaveCriticalSection (&init_lock);
 }
 
@@ -91,25 +91,25 @@ _gpgme_sema_subsystem_init ()
 void
 _gpgme_sema_cs_enter ( struct critsect_s *s )
 {
-    if (!s->private)
+    if (!s->priv)
         critsect_init (s);
-    EnterCriticalSection ( (CRITICAL_SECTION*)s->private );
+    EnterCriticalSection ( (CRITICAL_SECTION*)s->priv );
 }
 
 void
 _gpgme_sema_cs_leave (struct critsect_s *s)
 {
-    if (!s->private)
+    if (!s->priv)
         critsect_init (s);
-    LeaveCriticalSection ((CRITICAL_SECTION*)s->private);
+    LeaveCriticalSection ((CRITICAL_SECTION*)s->priv);
 }
 
 void
 _gpgme_sema_cs_destroy ( struct critsect_s *s )
 {
-    if (s && s->private) {
-        DeleteCriticalSection ((CRITICAL_SECTION*)s->private);
-        free (s->private);
-        s->private = NULL;
+    if (s && s->priv) {
+        DeleteCriticalSection ((CRITICAL_SECTION*)s->priv);
+        free (s->priv);
+        s->priv = NULL;
     }
 }
diff --git a/trunk/m4/pkg.m4 b/trunk/m4/pkg.m4
new file mode 100644 (file)
index 0000000..c29b6c0
--- /dev/null
@@ -0,0 +1,157 @@
+# pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*-
+# 
+# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
+#
+# This program 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.
+#
+# This program 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.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# PKG_PROG_PKG_CONFIG([MIN-VERSION])
+# ----------------------------------
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+       AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+       _pkg_min_version=m4_default([$1], [0.9.0])
+       AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+       if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+               AC_MSG_RESULT([yes])
+       else
+               AC_MSG_RESULT([no])
+               PKG_CONFIG=""
+       fi
+               
+fi[]dnl
+])# PKG_PROG_PKG_CONFIG
+
+# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+#
+# Check to see whether a particular set of modules exists.  Similar
+# to PKG_CHECK_MODULES(), but does not set variables or print errors.
+#
+#
+# Similar to PKG_CHECK_MODULES, make sure that the first instance of
+# this or PKG_CHECK_MODULES is called, or make sure to call
+# PKG_CHECK_EXISTS manually
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+    AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+  m4_ifval([$2], [$2], [:])
+m4_ifvaln([$3], [else
+  $3])dnl
+fi])
+
+
+# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+# ---------------------------------------------
+m4_define([_PKG_CONFIG],
+[if test -n "$PKG_CONFIG"; then
+    if test -n "$$1"; then
+        pkg_cv_[]$1="$$1"
+    else
+        PKG_CHECK_EXISTS([$3],
+                         [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`],
+                        [pkg_failed=yes])
+    fi
+else
+       pkg_failed=untried
+fi[]dnl
+])# _PKG_CONFIG
+
+# _PKG_SHORT_ERRORS_SUPPORTED
+# -----------------------------
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi[]dnl
+])# _PKG_SHORT_ERRORS_SUPPORTED
+
+
+# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+# [ACTION-IF-NOT-FOUND])
+#
+#
+# Note that if there is a possibility the first call to
+# PKG_CHECK_MODULES might not happen, you should be sure to include an
+# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+#
+#
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $1])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+        _PKG_SHORT_ERRORS_SUPPORTED
+        if test $_pkg_short_errors_supported = yes; then
+               $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"`
+        else 
+               $1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+       ifelse([$4], , [AC_MSG_ERROR(dnl
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT
+])],
+               [AC_MSG_RESULT([no])
+                $4])
+elif test $pkg_failed = untried; then
+       ifelse([$4], , [AC_MSG_FAILURE(dnl
+[The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://www.freedesktop.org/software/pkgconfig>.])],
+               [$4])
+else
+       $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+       $1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+        AC_MSG_RESULT([yes])
+       ifelse([$3], , :, [$3])
+fi[]dnl
+])# PKG_CHECK_MODULES