local copy of assuan and jnlib
authorSteffen Hansen <hansen@kde.org>
Thu, 24 Jan 2002 09:08:28 +0000 (09:08 +0000)
committerSteffen Hansen <hansen@kde.org>
Thu, 24 Jan 2002 09:08:28 +0000 (09:08 +0000)
37 files changed:
Makefile.am
assuan/ChangeLog [new file with mode: 0644]
assuan/Makefile.am [new file with mode: 0644]
assuan/README.1st [new file with mode: 0644]
assuan/assuan-buffer.c [new file with mode: 0644]
assuan/assuan-client.c [new file with mode: 0644]
assuan/assuan-connect.c [new file with mode: 0644]
assuan/assuan-defs.h [new file with mode: 0644]
assuan/assuan-handler.c [new file with mode: 0644]
assuan/assuan-inquire.c [new file with mode: 0644]
assuan/assuan-listen.c [new file with mode: 0644]
assuan/assuan-pipe-connect.c [new file with mode: 0644]
assuan/assuan-pipe-server.c [new file with mode: 0644]
assuan/assuan-socket-connect.c [new file with mode: 0644]
assuan/assuan-socket-server.c [new file with mode: 0644]
assuan/assuan-util.c [new file with mode: 0644]
assuan/assuan.h [new file with mode: 0644]
assuan/mkerrors [new file with mode: 0755]
configure.in.in
jnlib/ChangeLog [new file with mode: 0644]
jnlib/Makefile.am [new file with mode: 0644]
jnlib/argparse.c [new file with mode: 0644]
jnlib/argparse.h [new file with mode: 0644]
jnlib/dotlock.c [new file with mode: 0644]
jnlib/dotlock.h [new file with mode: 0644]
jnlib/libjnlib-config.h [new file with mode: 0644]
jnlib/logging.c [new file with mode: 0644]
jnlib/logging.h [new file with mode: 0644]
jnlib/mischelp.h [new file with mode: 0644]
jnlib/stringhelp.c [new file with mode: 0644]
jnlib/stringhelp.h [new file with mode: 0644]
jnlib/strlist.c [new file with mode: 0644]
jnlib/strlist.h [new file with mode: 0644]
jnlib/types.h [new file with mode: 0644]
jnlib/xmalloc.c [new file with mode: 0644]
jnlib/xmalloc.h [new file with mode: 0644]
kpinentry/Makefile.am

index 7f45a65..66d9da4 100644 (file)
@@ -1,6 +1,6 @@
 ####### kdevelop will overwrite this part!!! (begin)##########
 
-SUBDIRS = kpinentry po doc 
+SUBDIRS = jnlib assuan kpinentry po doc 
 
 EXTRA_DIST = pinentry.kdevprj AUTHORS COPYING ChangeLog INSTALL README TODO pinentry.lsm 
 
diff --git a/assuan/ChangeLog b/assuan/ChangeLog
new file mode 100644 (file)
index 0000000..6ad13b7
--- /dev/null
@@ -0,0 +1,171 @@
+2002-01-23  Werner Koch  <wk@gnupg.org>
+
+       * assuan-socket-connect.c (LOGERRORX): and removed typo.
+
+2002-01-22  Marcus Brinkmann  <marcus@g10code.de>
+
+       * assuan-socket-connect.c (LOGERRORX): Reverse arguments to fputs.
+
+2002-01-21  Werner Koch  <wk@gnupg.org>
+
+       * assuan-connect.c: Move all except assuan_get_pid to...
+       * assuan-pipe-connect.c: this.
+       (assuan_pipe_disconnect): Removed.
+       (do_finish, do_deinit): New 
+       (assuan_pipe_connect): and set them into the context.
+       * assuan-socket-connect.c: New.
+       
+       * assuan-util.c (_assuan_log_sanitized_string): New.
+
+       * assuan-pipe-server.c (assuan_init_pipe_server): Factored most
+       code out to ...
+       (_assuan_new_context): new func.
+       (_assuan_release_context): New
+       * assuan-connect.c (assuan_pipe_connect): Use the new functions.
+
+2002-01-20  Werner Koch  <wk@gnupg.org>
+
+       * assuan.h: Added Invalid Option error code.
+
+       * assuan-handler.c (std_handler_option): New.
+       (std_cmd_tbl): Add OPTION as standard command.
+       (assuan_register_option_handler): New.
+       (dispatch_command): Use case insensitive matching as a fallback.
+       (my_strcasecmp): New.
+
+2002-01-19  Werner Koch  <wk@gnupg.org>
+
+       * assuan-buffer.c (_assuan_read_line): Add output logging.
+       (assuan_write_line): Ditto.
+       (_assuan_cookie_write_data): Ditto.
+       (_assuan_cookie_write_flush): Ditto.
+       * assuan-util.c (_assuan_log_print_buffer): New.
+       (assuan_set_log_stream): New.
+       (assuan_begin_confidential): New.
+       (assuan_end_confidential): New.
+
+       * assuan-defs.h: Add a few handler variables.
+       * assuan-pipe-server.c (assuan_deinit_pipe_server): Removed.
+       (deinit_pipe_server): New.
+       (assuan_deinit_server): New.  Changed all callers to use this.
+       * assuan-listen.c (assuan_accept): Use the accept handler.
+       * assuan-handler.c (process_request): Use the close Handler.
+       * assuan-socket-server.c: New.
+
+2002-01-14  Werner Koch  <wk@gnupg.org>
+
+       * assuan-client.c (_assuan_read_from_server): Skip spaces after
+       the keyword.
+
+2002-01-03  Werner Koch  <wk@gnupg.org>
+
+       * assuan-handler.c (assuan_set_okay_line): New.
+       (process_request): And use it here.
+
+2002-01-02  Werner Koch  <wk@gnupg.org>
+
+       * assuan-inquire.c (init_membuf,put_membuf,get_membuf): Apply a
+       hidden 0 behind the buffer so that the buffer can be used as a
+       string in certain contexts.
+
+2001-12-14  Marcus Brinkmann  <marcus@g10code.de>
+
+       * assuan-connect.c (assuan_pipe_connect): New argument
+       FD_CHILD_LIST.  Don't close those fds.
+       * assuan.h: Likewise for prototype.
+
+2001-12-14  Werner Koch  <wk@gnupg.org>
+
+       * assuan-listen.c (assuan_close_input_fd): New.
+       (assuan_close_output_fd): New.
+       * assuan-handler.c (std_handler_reset): Always close them after a
+       reset command.
+       (std_handler_bye): Likewise.
+
+2001-12-14  Marcus Brinkmann  <marcus@g10code.de>
+
+       * assuan-buffer.c (_assuan_read_line): New variable ATTICLEN, use
+       it to save the length of the attic line.
+       Rediddle the code a bit to make it more clear what happens.
+
+2001-12-14  Marcus Brinkmann  <marcus@g10code.de>
+
+       * assuan-defs.h (LINELENGTH): Define as ASSUAN_LINELENGTH.
+       assuan.h: Define ASSUAN_LINELENGTH.
+
+2001-12-13  Marcus Brinkmann  <marcus@g10code.de>
+
+       * assuan-buffer.c (assuan_read_line): Fix order of execution to
+       get correct return values.
+
+2001-12-13  Werner Koch  <wk@gnupg.org>
+
+       * assuan-handler.c (assuan_get_active_fds): Fixed silly bug,
+       pretty obvious that nobody ever tested this function.
+
+2001-12-12  Werner Koch  <wk@gnupg.org>
+
+       * assuan-connect.c (assuan_pipe_connect): Implemented the inital
+       handshake.
+       * assuan-client.c (read_from_server): Renamed to  
+       (_assuan_read_from_server): this and made external.
+
+       * assuan-listen.c (assuan_set_hello_line): New.
+       (assuan_accept): Use a custom hello line is available.
+
+       * assuan-buffer.c (assuan_read_line): New.
+       (assuan_pending_line): New.
+       (_assuan_write_line): Renamed to ..
+       (assuan_write_line): this, made public and changed all callers.
+
+2001-12-04  Werner Koch  <wk@gnupg.org>
+
+       * assuan-connect.c (assuan_pipe_connect): Add more error reporting.
+       * assuan-client.c: New.
+
+       * assuan-inquire.c: New.
+       * assuan-handler.c (process_request): Check for nested invocations.
+
+2001-11-27  Werner Koch  <wk@gnupg.org>
+
+       * assuan-handler.c (assuan_register_input_notify): New.
+       (assuan_register_output_notify): New.
+
+2001-11-26  Werner Koch  <wk@gnupg.org>
+
+       * assuan.h: Added more status codes.
+
+2001-11-25  Werner Koch  <wk@gnupg.org>
+
+       * assuan-handler.c (assuan_register_bye_notify)
+       (assuan_register_reset_notify)
+       (assuan_register_cancel_notify): New and call them from the
+       standard handlers.
+       (assuan_process): Moved bulk of function to ..
+       (process_request): .. new.
+       (assuan_process_next): One shot version of above.
+       (assuan_get_active_fds): New.
+
+2001-11-24  Werner Koch  <wk@gnupg.org>
+
+       * assuan-connect.c (assuan_get_pid): New.
+
+       * assuan-buffer.c (_assuan_read_line): Deal with reads of more
+       than a line.
+       * assuan-defs.h: Add space in the context for this.
+
+       
+     ***********************************************************
+     * Please note that Assuan is maintained as part of GnuPG. *
+     * You may find it source-copied in other packages.        *
+     ***********************************************************       
+       
+ Copyright 2001, 2002 Free Software Foundation, Inc.
+
+ This file is free software; as a special exception the author gives
+ unlimited permission to copy and/or distribute it, with or without
+ modifications, as long as this notice is preserved.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/assuan/Makefile.am b/assuan/Makefile.am
new file mode 100644 (file)
index 0000000..7508dce
--- /dev/null
@@ -0,0 +1,48 @@
+# Assuan Makefile for test purposes
+# Copyright (C) 2001 Free Software Foundation, Inc.
+#
+# 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 2 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+## Process this file with automake to produce Makefile.in
+
+EXTRA_DIST = mkerrors
+INCLUDES = -I.. -I$(top_srcdir)/include
+BUILT_SOURCES = assuan-errors.c
+
+noinst_LIBRARIES = libassuan.a
+
+
+#libassuan_a_LDFLAGS =
+libassuan_a_SOURCES = \
+       assuan.h \
+       assuan-defs.h \
+       assuan-util.c \
+       assuan-errors.c \
+       assuan-buffer.c \
+       assuan-handler.c \
+       assuan-inquire.c \
+       assuan-listen.c \
+       assuan-connect.c \
+       assuan-client.c \
+       assuan-pipe-server.c \
+       assuan-socket-server.c \
+       assuan-pipe-connect.c \
+       assuan-socket-connect.c 
+
+
+assuan-errors.c : assuan.h
+       $(srcdir)/mkerrors < $(srcdir)/assuan.h > assuan-errors.c
diff --git a/assuan/README.1st b/assuan/README.1st
new file mode 100644 (file)
index 0000000..bb52959
--- /dev/null
@@ -0,0 +1 @@
+Please don't modify it here but in the copy which comes with GnuPG.
\ No newline at end of file
diff --git a/assuan/assuan-buffer.c b/assuan/assuan-buffer.c
new file mode 100644 (file)
index 0000000..bd08817
--- /dev/null
@@ -0,0 +1,416 @@
+/* assuan-buffer.c - read and send data
+ *     Copyright (C) 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include "assuan-defs.h"
+
+
+static int
+writen ( int fd, const char *buffer, size_t length )
+{
+  while (length)
+    {
+      int nwritten = write (fd, buffer, length);
+      
+      if (nwritten < 0)
+        {
+          if (errno == EINTR)
+            continue;
+          return -1; /* write error */
+        }
+      length -= nwritten;
+      buffer += nwritten;
+    }
+  return 0;  /* okay */
+}
+
+/* read an entire line */
+static int
+readline (int fd, char *buf, size_t buflen, int *r_nread, int *eof)
+{
+  size_t nleft = buflen;
+  char *p;
+
+  *eof = 0;
+  *r_nread = 0;
+  while (nleft > 0)
+    {
+      int n = read (fd, buf, nleft);
+      if (n < 0)
+        {
+          if (errno == EINTR)
+            continue;
+          return -1; /* read error */
+        }
+      else if (!n)
+        {
+          *eof = 1;
+          break; /* allow incomplete lines */
+        }
+      p = buf;
+      nleft -= n;
+      buf += n;
+      *r_nread += n;
+      
+      for (; n && *p != '\n'; n--, p++)
+        ;
+      if (n)
+        break; /* at least one full line available - that's enough for now */
+    }
+
+  return 0;
+}
+
+
+int
+_assuan_read_line (ASSUAN_CONTEXT ctx)
+{
+  char *line = ctx->inbound.line;
+  int n, nread, atticlen;
+  int rc;
+
+  if (ctx->inbound.eof)
+    return -1;
+
+  atticlen = ctx->inbound.attic.linelen;
+  if (atticlen)
+    {
+      memcpy (line, ctx->inbound.attic.line, atticlen);
+      ctx->inbound.attic.linelen = 0;
+      for (n=0; n < atticlen && line[n] != '\n'; n++)
+        ;
+      if (n < atticlen)
+       {
+         rc = 0; /* found another line in the attic */
+         nread = atticlen;
+         atticlen = 0;
+       }
+      else
+        { /* read the rest */
+          assert (atticlen < LINELENGTH);
+          rc = readline (ctx->inbound.fd, line + atticlen,
+                        LINELENGTH - atticlen, &nread, &ctx->inbound.eof);
+        }
+    }
+  else
+    rc = readline (ctx->inbound.fd, line, LINELENGTH,
+                   &nread, &ctx->inbound.eof);
+  if (rc)
+    {
+      if (ctx->log_fp)
+        fprintf (ctx->log_fp, "%p <- [Error: %s]\n",
+                 ctx, strerror (errno)); 
+      return ASSUAN_Read_Error;
+    }
+  if (!nread)
+    {
+      assert (ctx->inbound.eof);
+      if (ctx->log_fp)
+        fprintf (ctx->log_fp, "%p <- [EOF]\n", ctx); 
+      return -1; 
+    }
+
+  ctx->inbound.attic.pending = 0;
+  nread += atticlen;
+  for (n=0; n < nread; n++)
+    {
+      if (line[n] == '\n')
+        {
+          if (n+1 < nread)
+            {
+              char *s, *d;
+              int i;
+
+              n++;
+              /* we have to copy the rest because the handlers are
+                 allowed to modify the passed buffer */
+              for (d=ctx->inbound.attic.line, s=line+n, i=nread-n; i; i--)
+                {
+                  if (*s=='\n')
+                    ctx->inbound.attic.pending = 1;
+                  *d++ = *s++;
+                }
+              ctx->inbound.attic.linelen = nread-n;
+              n--;
+            }
+          if (n && line[n-1] == '\r')
+            n--;
+          line[n] = 0;
+          ctx->inbound.linelen = n;
+          if (ctx->log_fp)
+            {
+              fprintf (ctx->log_fp, "%p <- ", ctx); 
+              if (ctx->confidential)
+                fputs ("[Confidential data not shown]", ctx->log_fp);
+              else
+                _assuan_log_print_buffer (ctx->log_fp, 
+                                          ctx->inbound.line,
+                                          ctx->inbound.linelen);
+              putc ('\n', ctx->log_fp);
+            }
+          return 0;
+        }
+    }
+
+  if (ctx->log_fp)
+    fprintf (ctx->log_fp, "%p <- [Invalid line]\n", ctx);
+  *line = 0;
+  ctx->inbound.linelen = 0;
+  return ctx->inbound.eof? ASSUAN_Line_Not_Terminated : ASSUAN_Line_Too_Long;
+}
+
+
+/* Read the next line from the client or server and return a pointer
+   to a buffer with holding that line.  linelen returns the length of
+   the line.  This buffer is valid until another read operation is
+   done on this buffer.  The caller is allowed to modify this buffer.
+   He should only use the buffer if the function returns without an
+   error.
+
+   Returns: 0 on success or an assuan error code
+   See also: assuan_pending_line().
+*/
+AssuanError
+assuan_read_line (ASSUAN_CONTEXT ctx, char **line, size_t *linelen)
+{
+  AssuanError err;
+
+  if (!ctx)
+    return ASSUAN_Invalid_Value;
+
+  err = _assuan_read_line (ctx);
+  *line = ctx->inbound.line;
+  *linelen = ctx->inbound.linelen;
+  return err;
+}
+
+
+/* Return true when a full line is pending for a read, without the need
+   for actual IO */
+int
+assuan_pending_line (ASSUAN_CONTEXT ctx)
+{
+  return ctx && ctx->inbound.attic.pending;
+}
+
+
+AssuanError 
+assuan_write_line (ASSUAN_CONTEXT ctx, const char *line )
+{
+  int rc;
+  
+  if (!ctx)
+    return ASSUAN_Invalid_Value;
+
+  /* fixme: we should do some kind of line buffering */
+  if (ctx->log_fp)
+    {
+      fprintf (ctx->log_fp, "%p -> ", ctx); 
+      if (ctx->confidential)
+        fputs ("[Confidential data not shown]", ctx->log_fp);
+      else
+        _assuan_log_print_buffer (ctx->log_fp, 
+                                  line, strlen (line));
+      putc ('\n', ctx->log_fp);
+    }
+
+  rc = writen (ctx->outbound.fd, line, strlen(line));
+  if (rc)
+    rc = ASSUAN_Write_Error;
+  if (!rc)
+    {
+      rc = writen (ctx->outbound.fd, "\n", 1);
+      if (rc)
+        rc = ASSUAN_Write_Error;
+    }
+
+  return rc;
+}
+
+
+\f
+/* Write out the data in buffer as datalines with line wrapping and
+   percent escaping.  This fucntion is used for GNU's custom streams */
+int
+_assuan_cookie_write_data (void *cookie, const char *buffer, size_t size)
+{
+  ASSUAN_CONTEXT ctx = cookie;
+  char *line;
+  size_t linelen;
+
+  if (ctx->outbound.data.error)
+    return 0;
+
+  line = ctx->outbound.data.line;
+  linelen = ctx->outbound.data.linelen;
+  line += linelen;
+  while (size)
+    {
+      /* insert data line header */
+      if (!linelen)
+        {
+          *line++ = 'D';
+          *line++ = ' ';
+          linelen += 2;
+        }
+      
+      /* copy data, keep some space for the CRLF and to escape one character */
+      while (size && linelen < LINELENGTH-2-2)
+        {
+          if (*buffer == '%' || *buffer == '\r' || *buffer == '\n')
+            {
+              sprintf (line, "%%%02X", *(unsigned char*)buffer);
+              line += 3;
+              linelen += 3;
+              buffer++;
+            }
+          else
+            {
+              *line++ = *buffer++;
+              linelen++;
+            }
+          size--;
+        }
+      
+      if (linelen >= LINELENGTH-2-2)
+        {
+          if (ctx->log_fp)
+            {
+              fprintf (ctx->log_fp, "%p -> ", ctx); 
+              if (ctx->confidential)
+                fputs ("[Confidential data not shown]", ctx->log_fp);
+              else 
+                _assuan_log_print_buffer (ctx->log_fp, 
+                                          ctx->outbound.data.line,
+                                          linelen);
+              putc ('\n', ctx->log_fp);
+            }
+          *line++ = '\n';
+          linelen++;
+          if (writen (ctx->outbound.fd, ctx->outbound.data.line, linelen))
+            {
+              ctx->outbound.data.error = ASSUAN_Write_Error;
+              return 0;
+            }
+          line = ctx->outbound.data.line;
+          linelen = 0;
+        }
+    }
+
+  ctx->outbound.data.linelen = linelen;
+  return 0;
+}
+
+
+/* Write out any buffered data 
+   This fucntion is used for GNU's custom streams */
+int
+_assuan_cookie_write_flush (void *cookie)
+{
+  ASSUAN_CONTEXT ctx = cookie;
+  char *line;
+  size_t linelen;
+
+  if (ctx->outbound.data.error)
+    return 0;
+
+  line = ctx->outbound.data.line;
+  linelen = ctx->outbound.data.linelen;
+  line += linelen;
+  if (linelen)
+    {
+      if (ctx->log_fp)
+        {
+          fprintf (ctx->log_fp, "%p -> ", ctx); 
+          if (ctx->confidential)
+            fputs ("[Confidential data not shown]", ctx->log_fp);
+          else
+            _assuan_log_print_buffer (ctx->log_fp, 
+                                      ctx->outbound.data.line,
+                                      linelen);
+          putc ('\n', ctx->log_fp);
+            }
+      *line++ = '\n';
+      linelen++;
+      if (writen (ctx->outbound.fd, ctx->outbound.data.line, linelen))
+        {
+          ctx->outbound.data.error = ASSUAN_Write_Error;
+          return 0;
+        }
+      ctx->outbound.data.linelen = 0;
+    }
+  return 0;
+}
+
+
+/**
+ * assuan_send_data:
+ * @ctx: An assuan context
+ * @buffer: Data to send or NULL to flush
+ * @length: length of the data to send/
+ * 
+ * This function may be used by the server or the client to send data
+ * lines.  The data will be escaped as required by the Assuan protocol
+ * and may get buffered until a line is full.  To force sending the
+ * data out @buffer may be passed as NULL (in which case @length must
+ * also be 0); however when used by a client this flush operation does
+ * also send the terminating "END" command to terminate the reponse on
+ * a INQUIRE response.  However, when assuan_transact() is used, this
+ * function takes care of sending END itself.
+ * 
+ * Return value: 0 on success or an error code
+ **/
+\f
+AssuanError
+assuan_send_data (ASSUAN_CONTEXT ctx, const void *buffer, size_t length)
+{
+  if (!ctx)
+    return ASSUAN_Invalid_Value;
+  if (!buffer && length)
+    return ASSUAN_Invalid_Value;
+
+  if (!buffer)
+    { /* flush what we have */
+      _assuan_cookie_write_flush (ctx);
+      if (ctx->outbound.data.error)
+        return ctx->outbound.data.error;
+      if (!ctx->is_server)
+        return assuan_write_line (ctx, "END");
+    }
+  else
+    {
+      _assuan_cookie_write_data (ctx, buffer, length);
+      if (ctx->outbound.data.error)
+        return ctx->outbound.data.error;
+    }
+
+  return 0;
+}
+
+
+
+
diff --git a/assuan/assuan-client.c b/assuan/assuan-client.c
new file mode 100644 (file)
index 0000000..d56357d
--- /dev/null
@@ -0,0 +1,186 @@
+/* assuan-client.c - client functions
+ *     Copyright (C) 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include "assuan-defs.h"
+
+#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
+                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
+#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+
+
+AssuanError
+_assuan_read_from_server (ASSUAN_CONTEXT ctx, int *okay, int *off)
+{
+  char *line;
+  int linelen;
+  AssuanError rc;
+
+  *okay = 0;
+  *off = 0;
+  do 
+    {
+      rc = _assuan_read_line (ctx);
+      if (rc)
+        return rc;
+      line = ctx->inbound.line;
+      linelen = ctx->inbound.linelen;
+    }    
+  while (*line == '#' || !linelen);
+
+  if (linelen >= 1
+      && line[0] == 'D' && line[1] == ' ')
+    {
+      *okay = 2; /* data line */
+      *off = 2;
+    }
+  else if (linelen >= 2
+           && line[0] == 'O' && line[1] == 'K'
+           && (line[2] == '\0' || line[2] == ' '))
+    {
+      *okay = 1;
+      *off = 2;
+      while (line[*off] == ' ')
+        ++*off;
+    }
+  else if (linelen >= 3
+           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
+           && (line[3] == '\0' || line[3] == ' '))
+    {
+      *okay = 0;
+      *off = 3;
+      while (line[*off] == ' ')
+        ++*off;
+    }  
+  else if (linelen >= 7
+           && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
+           && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
+           && line[6] == 'E' 
+           && (line[7] == '\0' || line[7] == ' '))
+    {
+      *okay = 3;
+      *off = 7;
+      while (line[*off] == ' ')
+        ++*off;
+    }
+  else
+    rc = ASSUAN_Invalid_Response;
+  return rc;
+}
+
+
+\f
+/**
+ * assuan_transact:
+ * @ctx: The Assuan context
+ * @command: Coimmand line to be send to server
+ * @data_cb: Callback function for data lines
+ * @data_cb_arg: first argument passed to @data_cb
+ * @inquire_cb: Callback function for a inquire response
+ * @inquire_cb_arg: first argument passed to @inquire_cb
+ * 
+ * FIXME: Write documentation
+ * 
+ * Return value: 0 on success or error code.  The error code may be
+ * the one one returned by the server in error lines or from the
+ * callback functions.
+ **/
+AssuanError
+assuan_transact (ASSUAN_CONTEXT ctx,
+                 const char *command,
+                 AssuanError (*data_cb)(void *, const void *, size_t),
+                 void *data_cb_arg,
+                 AssuanError (*inquire_cb)(void*, const char *),
+                 void *inquire_cb_arg)
+{
+  int rc, okay, off;
+  unsigned char *line;
+  int linelen;
+
+  rc = assuan_write_line (ctx, command);
+  if (rc)
+    return rc;
+
+ again:
+  rc = _assuan_read_from_server (ctx, &okay, &off);
+  if (rc)
+    return rc; /* error reading from server */
+
+  line = ctx->inbound.line + off;
+  linelen = ctx->inbound.linelen - off;
+
+  if (!okay)
+    {
+      rc = atoi (line);
+      if (rc < 100)
+        rc = ASSUAN_Server_Fault;
+    }
+  else if (okay == 2)
+    {
+      if (!data_cb)
+        rc = ASSUAN_No_Data_Callback;
+      else 
+        {
+          unsigned char *s, *d;
+
+          for (s=d=line; linelen; linelen--)
+            {
+              if (*s == '%' && linelen > 2)
+                { /* handle escaping */
+                  s++;
+                  *d++ = xtoi_2 (s);
+                  s += 2;
+                  linelen -= 2;
+                }
+              else
+                *d++ = *s++;
+            }
+          *d = 0; /* add a hidden string terminator */
+          rc = data_cb (data_cb_arg, line, d - line);
+          if (!rc)
+            goto again;
+        }
+    }
+  else if (okay == 3)
+    {
+      if (!inquire_cb)
+        {
+          assuan_write_line (ctx, "END"); /* get out of inquire mode */
+          _assuan_read_from_server (ctx, &okay, &off); /* dummy read */
+          rc = ASSUAN_No_Inquire_Callback;
+        }
+      else
+        {
+          rc = inquire_cb (inquire_cb_arg, line);
+          if (!rc)
+            rc = assuan_send_data (ctx, NULL, 0); /* flush and send END */
+          if (!rc)
+            goto again;
+        }
+    }
+
+  return rc;
+}
diff --git a/assuan/assuan-connect.c b/assuan/assuan-connect.c
new file mode 100644 (file)
index 0000000..49d4aac
--- /dev/null
@@ -0,0 +1,54 @@
+/* assuan-connect.c - Establish a connection (client) 
+ *     Copyright (C) 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "assuan-defs.h"
+
+/* Disconnect and release the context CTX. */
+void
+assuan_disconnect (ASSUAN_CONTEXT ctx)
+{
+  if (ctx)
+    {
+      assuan_write_line (ctx, "BYE");
+      ctx->finish_handler (ctx);
+      ctx->deinit_handler (ctx);
+      ctx->deinit_handler = NULL;
+      _assuan_release_context (ctx);
+    }
+}
+
+pid_t
+assuan_get_pid (ASSUAN_CONTEXT ctx)
+{
+  return ctx ? ctx->pid : -1;
+}
diff --git a/assuan/assuan-defs.h b/assuan/assuan-defs.h
new file mode 100644 (file)
index 0000000..7d55aab
--- /dev/null
@@ -0,0 +1,143 @@
+/* assuan-defs.c - Internal definitions to Assuan
+ *     Copyright (C) 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef ASSUAN_DEFS_H
+#define ASSUAN_DEFS_H
+
+#include <sys/types.h>
+#include "assuan.h"
+
+#define LINELENGTH ASSUAN_LINELENGTH
+
+struct cmdtbl_s {
+  const char *name;
+  int cmd_id;
+  int (*handler)(ASSUAN_CONTEXT, char *line);
+};
+
+struct assuan_context_s {
+  AssuanError err_no;
+  const char *err_str;
+  int os_errno;  /* last system error number used with certain error codes*/
+
+  int confidential;
+  int is_server;  /* set if this is context belongs to a server */
+  int in_inquire;
+  char *hello_line;
+  char *okay_line; /* see assan_set_okay_line() */
+  
+  void *user_pointer;  /* for assuan_[gs]et_pointer () */
+
+  FILE *log_fp;
+
+  struct {
+    int fd;
+    int eof;
+    char line[LINELENGTH];
+    int linelen;  /* w/o CR, LF - might not be the same as
+                     strlen(line) due to embedded nuls. However a nul
+                     is always written at this pos */
+    struct {
+      char line[LINELENGTH];
+      int linelen ;
+      int pending; /* i.e. at least one line is available in the attic */
+    } attic;
+  } inbound;
+
+  struct {
+    int fd;
+    struct {
+      FILE *fp;
+      char line[LINELENGTH];
+      int linelen; 
+      int error;
+    } data; 
+  } outbound;
+
+  int pipe_mode;  /* We are in pipe mode, i.e. we can handle just one
+                     connection and must terminate then */
+  pid_t pid;     /* In pipe mode, the pid of the child server process.  
+                     In socket mode, the pid of the server */
+  int listen_fd;  /* The fd we are listening on (used by socket servers) */
+
+  void (*deinit_handler)(ASSUAN_CONTEXT);  
+  int (*accept_handler)(ASSUAN_CONTEXT);
+  int (*finish_handler)(ASSUAN_CONTEXT);
+
+  struct cmdtbl_s *cmdtbl;
+  size_t cmdtbl_used; /* used entries */
+  size_t cmdtbl_size; /* allocated size of table */
+
+  void (*bye_notify_fnc)(ASSUAN_CONTEXT);
+  void (*reset_notify_fnc)(ASSUAN_CONTEXT);
+  void (*cancel_notify_fnc)(ASSUAN_CONTEXT);
+  int  (*option_handler_fnc)(ASSUAN_CONTEXT,const char*, const char*);
+  void (*input_notify_fnc)(ASSUAN_CONTEXT, const char *);
+  void (*output_notify_fnc)(ASSUAN_CONTEXT, const char *);
+
+
+  int input_fd;   /* set by INPUT command */
+  int output_fd;  /* set by OUTPUT command */
+
+};
+
+
+/*-- assuan-pipe-server.c --*/
+int _assuan_new_context (ASSUAN_CONTEXT *r_ctx);
+void _assuan_release_context (ASSUAN_CONTEXT ctx);
+
+
+/*-- assuan-handler.c --*/
+int _assuan_register_std_commands (ASSUAN_CONTEXT ctx);
+
+/*-- assuan-buffer.c --*/
+int _assuan_read_line (ASSUAN_CONTEXT ctx);
+int _assuan_cookie_write_data (void *cookie, const char *buffer, size_t size);
+int _assuan_cookie_write_flush (void *cookie);
+
+/*-- assuan-client.c --*/
+AssuanError _assuan_read_from_server (ASSUAN_CONTEXT ctx, int *okay, int *off);
+
+
+/*-- assuan-util.c --*/
+void *_assuan_malloc (size_t n);
+void *_assuan_calloc (size_t n, size_t m);
+void *_assuan_realloc (void *p, size_t n);
+void  _assuan_free (void *p);
+
+#define xtrymalloc(a)    _assuan_malloc ((a))
+#define xtrycalloc(a,b)  _assuan_calloc ((a),(b))
+#define xtryrealloc(a,b) _assuan_realloc((a),(b))
+#define xfree(a)         _assuan_free ((a))
+
+#define set_error(c,e,t) assuan_set_error ((c), ASSUAN_ ## e, (t))
+
+void _assuan_log_print_buffer (FILE *fp, const void *buffer, size_t  length);
+void _assuan_log_sanitized_string (const char *string);
+
+
+#endif /*ASSUAN_DEFS_H*/
+
+
+
+
+
+
+
diff --git a/assuan/assuan-handler.c b/assuan/assuan-handler.c
new file mode 100644 (file)
index 0000000..1c8aded
--- /dev/null
@@ -0,0 +1,682 @@
+/* assuan-handler.c - dispatch commands 
+ *     Copyright (C) 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "assuan-defs.h"
+
+#define spacep(p)  (*(p) == ' ' || *(p) == '\t')
+#define digitp(a) ((a) >= '0' && (a) <= '9')
+
+
+static int
+dummy_handler (ASSUAN_CONTEXT ctx, char *line)
+{
+  return set_error (ctx, Server_Fault, "no handler registered");
+}
+
+
+static int
+std_handler_nop (ASSUAN_CONTEXT ctx, char *line)
+{
+  return 0; /* okay */
+}
+  
+static int
+std_handler_cancel (ASSUAN_CONTEXT ctx, char *line)
+{
+  if (ctx->cancel_notify_fnc)
+    ctx->cancel_notify_fnc (ctx);
+  return set_error (ctx, Not_Implemented, NULL); 
+}
+
+static int
+std_handler_option (ASSUAN_CONTEXT ctx, char *line)
+{
+  char *key, *value, *p;
+
+  for (key=line; spacep (key); key++)
+    ;
+  if (!*key)
+    return set_error (ctx, Syntax_Error, "argument required");
+  if (*key == '=')
+    return set_error (ctx, Syntax_Error, "no option name given");
+  for (value=key; *value && !spacep (value) && *value != '='; value++)
+    ;
+  if (*value)
+    {
+      if (spacep (value))
+        *value++ = 0; /* terminate key */
+      for (; spacep (value); value++)
+        ;
+      if (*value == '=')
+        {
+          *value++ = 0; /* terminate key */
+          for (; spacep (value); value++)
+            ;
+          if (!*value)
+            return set_error (ctx, Syntax_Error, "option argument expected");
+        }
+      if (*value)
+        {
+          for (p = value + strlen(value) - 1; p > value && spacep (p); p--)
+            ;
+          if (p > value)
+            *++p = 0; /* strip trailing spaces */
+        }
+    }
+
+  if (*key == '-' && key[1] == '-' && key[2])
+    key += 2; /* the double dashes are optional */
+  if (*key == '-')
+    return set_error (ctx, Syntax_Error,
+                      "option should not begin with one dash");
+
+  if (ctx->option_handler_fnc)
+    return ctx->option_handler_fnc (ctx, key, value);
+  return 0;
+}
+  
+static int
+std_handler_bye (ASSUAN_CONTEXT ctx, char *line)
+{
+  if (ctx->bye_notify_fnc)
+    ctx->bye_notify_fnc (ctx);
+  assuan_close_input_fd (ctx);
+  assuan_close_output_fd (ctx);
+  return -1; /* pretty simple :-) */
+}
+  
+static int
+std_handler_auth (ASSUAN_CONTEXT ctx, char *line)
+{
+  return set_error (ctx, Not_Implemented, NULL); 
+}
+  
+static int
+std_handler_reset (ASSUAN_CONTEXT ctx, char *line)
+{
+  if (ctx->reset_notify_fnc)
+    ctx->reset_notify_fnc (ctx);
+  assuan_close_input_fd (ctx);
+  assuan_close_output_fd (ctx);
+  return 0;
+}
+  
+static int
+std_handler_end (ASSUAN_CONTEXT ctx, char *line)
+{
+  return set_error (ctx, Not_Implemented, NULL); 
+}
+
+static int
+parse_cmd_input_output (ASSUAN_CONTEXT ctx, char *line, int *rfd)
+{
+  char *endp;
+
+  if (strncmp (line, "FD=", 3))
+    return set_error (ctx, Syntax_Error, "FD=<n> expected");
+  line += 3;
+  if (!digitp (*line))
+    return set_error (ctx, Syntax_Error, "number required");
+  *rfd = strtoul (line, &endp, 10);
+  /* remove that argument so that a notify handler won't see it */
+  memset (line, ' ', endp? (endp-line):strlen(line));
+
+  if (*rfd == ctx->inbound.fd)
+    return set_error (ctx, Parameter_Conflict, "fd same as inbound fd");
+  if (*rfd == ctx->outbound.fd)
+    return set_error (ctx, Parameter_Conflict, "fd same as outbound fd");
+  return 0;
+}
+
+/* Format is INPUT FD=<n> */
+static int
+std_handler_input (ASSUAN_CONTEXT ctx, char *line)
+{
+  int rc, fd;
+
+  rc = parse_cmd_input_output (ctx, line, &fd);
+  if (rc)
+    return rc;
+  ctx->input_fd = fd;
+  if (ctx->input_notify_fnc)
+    ctx->input_notify_fnc (ctx, line);
+  return 0;
+}
+
+/* Format is OUTPUT FD=<n> */
+static int
+std_handler_output (ASSUAN_CONTEXT ctx, char *line)
+{
+  int rc, fd;
+
+  rc = parse_cmd_input_output (ctx, line, &fd);
+  if (rc)
+    return rc;
+  ctx->output_fd = fd;
+  if (ctx->output_notify_fnc)
+    ctx->output_notify_fnc (ctx, line);
+  return 0;
+}
+
+
+
+  
+
+/* This is a table with the standard commands and handler for them.
+   The table is used to initialize a new context and assuciate strings
+   and handlers with cmd_ids */
+static struct {
+  const char *name;
+  int cmd_id;
+  int (*handler)(ASSUAN_CONTEXT, char *line);
+  int always; /* always initialize this command */
+} std_cmd_table[] = {
+  { "NOP",    ASSUAN_CMD_NOP,    std_handler_nop, 1 },
+  { "CANCEL", ASSUAN_CMD_CANCEL, std_handler_cancel, 1 },
+  { "OPTION", ASSUAN_CMD_OPTION, std_handler_option, 1 },
+  { "BYE",    ASSUAN_CMD_BYE,    std_handler_bye, 1 },
+  { "AUTH",   ASSUAN_CMD_AUTH,   std_handler_auth, 1 },
+  { "RESET",  ASSUAN_CMD_RESET,  std_handler_reset, 1 },
+  { "END",    ASSUAN_CMD_END,    std_handler_end, 1 },
+
+  { "INPUT",  ASSUAN_CMD_INPUT,  std_handler_input },
+  { "OUTPUT", ASSUAN_CMD_OUTPUT, std_handler_output },
+  { "OPTION", ASSUAN_CMD_OPTION, std_handler_option, 1 },
+  { NULL }
+};
+
+
+/**
+ * assuan_register_command:
+ * @ctx: the server context
+ * @cmd_id: An ID value for the command
+ * @cmd_name: A string with the command name
+ * @handler: The handler function to be called
+ * 
+ * Register a handler to be used for a given command.
+ * 
+ * The @cmd_name must be %NULL or an empty string for all @cmd_ids
+ * below %ASSUAN_CMD_USER because predefined values are used.
+ * 
+ * Return value: 
+ **/
+int
+assuan_register_command (ASSUAN_CONTEXT ctx,
+                         int cmd_id, const char *cmd_name,
+                         int (*handler)(ASSUAN_CONTEXT, char *))
+{
+  int i;
+
+  if (cmd_name && !*cmd_name)
+    cmd_name = NULL;
+
+  if (cmd_id < ASSUAN_CMD_USER)
+    { 
+      if (cmd_name)
+        return ASSUAN_Invalid_Value; /* must be NULL for these values*/
+
+      for (i=0; std_cmd_table[i].name; i++)
+        {
+          if (std_cmd_table[i].cmd_id == cmd_id)
+            {
+              cmd_name = std_cmd_table[i].name;
+              if (!handler)
+                handler = std_cmd_table[i].handler;
+              break;
+            }
+        }
+      if (!std_cmd_table[i].name)
+        return ASSUAN_Invalid_Value; /* not a pre-registered one */
+    }
+  
+  if (!handler)
+    handler = dummy_handler;
+
+  if (!cmd_name)
+    return ASSUAN_Invalid_Value;
+
+/*    fprintf (stderr, "DBG-assuan: registering %d as `%s'\n", cmd_id, cmd_name); */
+
+  if (!ctx->cmdtbl)
+    {
+      ctx->cmdtbl_size = 50;
+      ctx->cmdtbl = xtrycalloc ( ctx->cmdtbl_size, sizeof *ctx->cmdtbl);
+      if (!ctx->cmdtbl)
+        return ASSUAN_Out_Of_Core;
+      ctx->cmdtbl_used = 0;
+    }
+  else if (ctx->cmdtbl_used >= ctx->cmdtbl_size)
+    {
+      struct cmdtbl_s *x;
+
+      x = xtryrealloc ( ctx->cmdtbl, (ctx->cmdtbl_size+10) * sizeof *x);
+      if (!x)
+        return ASSUAN_Out_Of_Core;
+      ctx->cmdtbl = x;
+      ctx->cmdtbl_size += 50;
+    }
+
+  ctx->cmdtbl[ctx->cmdtbl_used].name = cmd_name;
+  ctx->cmdtbl[ctx->cmdtbl_used].cmd_id = cmd_id;
+  ctx->cmdtbl[ctx->cmdtbl_used].handler = handler;
+  ctx->cmdtbl_used++;
+  return 0;
+}
+
+int
+assuan_register_bye_notify (ASSUAN_CONTEXT ctx, void (*fnc)(ASSUAN_CONTEXT))
+{
+  if (!ctx)
+    return ASSUAN_Invalid_Value;
+  ctx->bye_notify_fnc = fnc;
+  return 0;
+}
+
+int
+assuan_register_reset_notify (ASSUAN_CONTEXT ctx, void (*fnc)(ASSUAN_CONTEXT))
+{
+  if (!ctx)
+    return ASSUAN_Invalid_Value;
+  ctx->reset_notify_fnc = fnc;
+  return 0;
+}
+
+int
+assuan_register_cancel_notify (ASSUAN_CONTEXT ctx, void (*fnc)(ASSUAN_CONTEXT))
+{
+  if (!ctx)
+    return ASSUAN_Invalid_Value;
+  ctx->cancel_notify_fnc = fnc;
+  return 0;
+}
+
+int
+assuan_register_option_handler (ASSUAN_CONTEXT ctx,
+                               int (*fnc)(ASSUAN_CONTEXT,
+                                          const char*, const char*))
+{
+  if (!ctx)
+    return ASSUAN_Invalid_Value;
+  ctx->option_handler_fnc = fnc;
+  return 0;
+}
+
+int
+assuan_register_input_notify (ASSUAN_CONTEXT ctx,
+                              void (*fnc)(ASSUAN_CONTEXT, const char *))
+{
+  if (!ctx)
+    return ASSUAN_Invalid_Value;
+  ctx->input_notify_fnc = fnc;
+  return 0;
+}
+
+int
+assuan_register_output_notify (ASSUAN_CONTEXT ctx,
+                              void (*fnc)(ASSUAN_CONTEXT, const char *))
+{
+  if (!ctx)
+    return ASSUAN_Invalid_Value;
+  ctx->output_notify_fnc = fnc;
+  return 0;
+}
+
+
+/* Helper to register the standards commands */
+int
+_assuan_register_std_commands (ASSUAN_CONTEXT ctx)
+{
+  int i, rc;
+
+  for (i=0; std_cmd_table[i].name; i++)
+    {
+      if (std_cmd_table[i].always)
+        {
+          rc = assuan_register_command (ctx, std_cmd_table[i].cmd_id,
+                                        NULL, NULL);
+          if (rc)
+            return rc;
+        }
+    } 
+  return 0;
+}
+
+
+\f
+/* Process the special data lines.  The "D " has already been removed
+   from the line.  As all handlers this function may modify the line.  */
+static int
+handle_data_line (ASSUAN_CONTEXT ctx, char *line, int linelen)
+{
+  return set_error (ctx, Not_Implemented, NULL);
+}
+
+/* like ascii_strcasecmp but assume that B is already uppercase */
+static int
+my_strcasecmp (const char *a, const char *b)
+{
+    if (a == b)
+        return 0;
+
+    for (; *a && *b; a++, b++)
+      {
+       if (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) != *b)
+           break;
+      }
+    return *a == *b? 0 : (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) - *b);
+}
+
+/* Parse the line, break out the command, find it in the command
+   table, remove leading and white spaces from the arguments, all the
+   handler with the argument line and return the error */
+static int 
+dispatch_command (ASSUAN_CONTEXT ctx, char *line, int linelen)
+{
+  char *p;
+  const char *s;
+  int shift, i;
+
+  if (*line == 'D' && line[1] == ' ') /* divert to special handler */
+    return handle_data_line (ctx, line+2, linelen-2);
+
+  for (p=line; *p && *p != ' ' && *p != '\t'; p++)
+    ;
+  if (p==line)
+    return set_error (ctx, Invalid_Command, "leading white-space"); 
+  if (*p) 
+    { /* Skip over leading WS after the keyword */
+      *p++ = 0;
+      while ( *p == ' ' || *p == '\t')
+        p++;
+    }
+  shift = p - line;
+
+  for (i=0; (s=ctx->cmdtbl[i].name); i++)
+    {
+      if (!strcmp (line, s))
+        break;
+    }
+  if (!s)
+    { /* and try case insensitive */
+      for (i=0; (s=ctx->cmdtbl[i].name); i++)
+        {
+          if (!my_strcasecmp (line, s))
+            break;
+        }
+    }
+  if (!s)
+    return set_error (ctx, Unknown_Command, NULL);
+  line += shift;
+  linelen -= shift;
+
+/*    fprintf (stderr, "DBG-assuan: processing %s `%s'\n", s, line); */
+  return ctx->cmdtbl[i].handler (ctx, line);
+}
+
+
+
+\f
+static int
+process_request (ASSUAN_CONTEXT ctx)
+{
+  int rc;
+
+  if (ctx->in_inquire)
+    return ASSUAN_Nested_Commands;
+
+  rc = _assuan_read_line (ctx);
+  if (rc)
+    return rc;
+  if (*ctx->inbound.line == '#' || !ctx->inbound.linelen)
+    return 0; /* comment line - ignore */
+
+  ctx->outbound.data.error = 0;
+  ctx->outbound.data.linelen = 0;
+  /* dispatch command and return reply */
+  rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen);
+  /* check from data write errors */
+  if (ctx->outbound.data.fp)
+    { /* Flush the data lines */
+      fclose (ctx->outbound.data.fp);
+      ctx->outbound.data.fp = NULL;
+      if (!rc && ctx->outbound.data.error)
+        rc = ctx->outbound.data.error;
+    }
+  /* Error handling */
+  if (!rc)
+    {
+      rc = assuan_write_line (ctx, ctx->okay_line? ctx->okay_line : "OK");
+    }
+  else if (rc == -1)
+    { /* No error checking because the peer may have already disconnect */ 
+      assuan_write_line (ctx, "OK closing connection");
+      ctx->finish_handler (ctx);
+    }
+  else 
+    {
+      char errline[256];
+
+        if (rc < 100)
+        sprintf (errline, "ERR %d server fault (%.50s)",
+                 ASSUAN_Server_Fault, assuan_strerror (rc));
+      else
+        {
+          const char *text = ctx->err_no == rc? ctx->err_str:NULL;
+
+          sprintf (errline, "ERR %d %.50s%s%.100s",
+                   rc, assuan_strerror (rc), text? " - ":"", text?text:"");
+        }
+      rc = assuan_write_line (ctx, errline);
+    }
+
+  ctx->confidential = 0;
+  if (ctx->okay_line)
+    {
+      xfree (ctx->okay_line);
+      ctx->okay_line = NULL;
+    }
+  return rc;
+}
+
+/**
+ * assuan_process:
+ * @ctx: assuan context
+ * 
+ * This fucntion is used to handle the assuan protocol after a
+ * connection has been established using assuan_accept().  This is the
+ * main protocol handler.
+ * 
+ * Return value: 0 on success or an error code if the assuan operation
+ * failed.  Note, that no error is returned for operational errors.
+ **/
+int
+assuan_process (ASSUAN_CONTEXT ctx)
+{
+  int rc;
+
+  do {
+    rc = process_request (ctx);
+  } while (!rc);
+
+  if (rc == -1)
+    rc = 0;
+
+  return rc;
+}
+
+
+/**
+ * assuan_process_next:
+ * @ctx: Assuan context
+ * 
+ * Same as assuan_process() but the user has to provide the outer
+ * loop.  He should loop as long as the return code is zero and stop
+ * otherwise; -1 is regular end.
+ * 
+ * See also: assuan_get_active_fds()
+ * Return value: -1 for end of server, 0 on success or an error code
+ **/
+int 
+assuan_process_next (ASSUAN_CONTEXT ctx)
+{
+  return process_request (ctx);
+}
+
+
+/**
+ * assuan_get_active_fds:
+ * @ctx: Assuan context
+ * @what: 0 for read fds, 1 for write fds
+ * @fdarray: Caller supplied array to store the FDs
+ * @fdarraysize: size of that array
+ * 
+ * Return all active filedescriptors for the given context.  This
+ * function can be used to select on the fds and call
+ * assuan_process_next() if there is an active one.  The first fd in
+ * the array is the one used for the command connection.
+ *
+ * Note, that write FDs are not yet supported.
+ * 
+ * Return value: number of FDs active and put into @fdarray or -1 on
+ * error which is most likely a too small fdarray.
+ **/
+int 
+assuan_get_active_fds (ASSUAN_CONTEXT ctx, int what,
+                       int *fdarray, int fdarraysize)
+{
+  int n = 0;
+
+  if (!ctx || fdarraysize < 2 || what < 0 || what > 1)
+    return -1;
+
+  if (!what)
+    {
+      if (ctx->inbound.fd != -1)
+        fdarray[n++] = ctx->inbound.fd;
+    }
+  else
+    {
+      if (ctx->outbound.fd != -1)
+        fdarray[n++] = ctx->outbound.fd;
+      if (ctx->outbound.data.fp)
+        fdarray[n++] = fileno (ctx->outbound.data.fp);
+    }
+
+  return n;
+}
+
+/* Return a FP to be used for data output.  The FILE pointer is valid
+   until the end of a handler.  So a close is not needed.  Assuan does
+   all the buffering needed to insert the status line as well as the
+   required line wappping and quoting for data lines.
+
+   We use GNU's custom streams here.  There should be an alternative
+   implementaion for systems w/o a glibc, a simple implementation
+   could use a child process */
+FILE *
+assuan_get_data_fp (ASSUAN_CONTEXT ctx)
+{
+  cookie_io_functions_t cookie_fnc;
+
+  if (ctx->outbound.data.fp)
+    return ctx->outbound.data.fp;
+  
+  cookie_fnc.read = NULL; 
+  cookie_fnc.write = _assuan_cookie_write_data;
+  cookie_fnc.seek = NULL;
+  cookie_fnc.close = _assuan_cookie_write_flush;
+
+  ctx->outbound.data.fp = fopencookie (ctx, "wb", cookie_fnc);
+  ctx->outbound.data.error = 0;
+  return ctx->outbound.data.fp;
+}
+
+
+/* Set the text used for the next OK reponse.  This string is
+   automatically reset to NULL after the next command. */
+AssuanError
+assuan_set_okay_line (ASSUAN_CONTEXT ctx, const char *line)
+{
+  if (!ctx)
+    return ASSUAN_Invalid_Value;
+  if (!line)
+    {
+      xfree (ctx->okay_line);
+      ctx->okay_line = NULL;
+    }
+  else
+    {
+      /* FIXME: we need to use gcry_is_secure() to test whether
+         we should allocate the entire line in secure memory */
+      char *buf = xtrymalloc (3+strlen(line)+1);
+      if (!buf)
+        return ASSUAN_Out_Of_Core;
+      strcpy (buf, "OK ");
+      strcpy (buf+3, line);
+      xfree (ctx->okay_line);
+      ctx->okay_line = buf;
+    }
+  return 0;
+}
+
+
+
+void
+assuan_write_status (ASSUAN_CONTEXT ctx, const char *keyword, const char *text)
+{
+  char buffer[256];
+  char *helpbuf;
+  size_t n;
+
+  if ( !ctx || !keyword)
+    return;
+  if (!text)
+    text = "";
+
+  n = 2 + strlen (keyword) + 1 + strlen (text) + 1;
+  if (n < sizeof (buffer))
+    {
+      strcpy (buffer, "S ");
+      strcat (buffer, keyword);
+      if (*text)
+        {
+          strcat (buffer, " ");
+          strcat (buffer, text);
+        }
+      assuan_write_line (ctx, buffer);
+    }
+  else if ( (helpbuf = xtrymalloc (n)) )
+    {
+      strcpy (helpbuf, "S ");
+      strcat (helpbuf, keyword);
+      if (*text)
+        {
+          strcat (helpbuf, " ");
+          strcat (helpbuf, text);
+        }
+      assuan_write_line (ctx, helpbuf);
+      xfree (helpbuf);
+    }
+}
diff --git a/assuan/assuan-inquire.c b/assuan/assuan-inquire.c
new file mode 100644 (file)
index 0000000..933091e
--- /dev/null
@@ -0,0 +1,223 @@
+/* assuan-inquire.c - handle inquire stuff
+ *     Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "assuan-defs.h"
+
+#define digitp(a) ((a) >= '0' && (a) <= '9')
+#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
+                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
+#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+
+
+struct membuf {
+  size_t len;
+  size_t size;
+  char *buf;
+  int out_of_core;
+  int too_large;
+  size_t maxlen;
+};
+
+
+\f
+/* A simple implemnation of a dynamic buffer.  Use init_membuf() to
+   create a buffer, put_membuf to append bytes and get_membuf to
+   release and return the buffer.  Allocation errors are detected but
+   only returned at the final get_membuf(), this helps not to clutter
+   the code with out of core checks.  */
+
+static void
+init_membuf (struct membuf *mb, int initiallen, size_t maxlen)
+{
+  mb->len = 0;
+  mb->size = initiallen;
+  mb->out_of_core = 0;
+  mb->too_large = 0;
+  mb->maxlen = maxlen;
+  /* we need to allocate one byte more for get_membuf */
+  mb->buf = xtrymalloc (initiallen+1);
+  if (!mb->buf)
+      mb->out_of_core = 1;
+}
+
+static void
+put_membuf (struct membuf *mb, const void *buf, size_t len)
+{
+  if (mb->out_of_core || mb->too_large)
+    return;
+
+  if (mb->maxlen && mb->len + len > mb->maxlen)
+    {
+      mb->too_large = 1;
+      return;
+    }
+
+  if (mb->len + len >= mb->size)
+    {
+      char *p;
+      
+      mb->size += len + 1024;
+      /* we need to allocate one byte more for get_membuf */
+      p = xtryrealloc (mb->buf, mb->size+1);
+      if (!p)
+        {
+          mb->out_of_core = 1;
+          return;
+        }
+      mb->buf = p;
+    }
+  memcpy (mb->buf + mb->len, buf, len);
+  mb->len += len;
+}
+
+static void *
+get_membuf (struct membuf *mb, size_t *len)
+{
+  char *p;
+
+  if (mb->out_of_core || mb->too_large)
+    {
+      xfree (mb->buf);
+      mb->buf = NULL;
+      return NULL;
+    }
+
+  mb->buf[mb->len] = 0; /* there is enough space for the hidden eos */
+  p = mb->buf;
+  *len = mb->len;
+  mb->buf = NULL;
+  mb->out_of_core = 1; /* don't allow a reuse */
+  return p;
+}
+
+static void
+free_membuf (struct membuf *mb)
+{
+  xfree (mb->buf);
+  mb->buf = NULL;
+}
+
+
+/**
+ * assuan_inquire:
+ * @ctx: An assuan context
+ * @keyword: The keyword used for the inquire
+ * @r_buffer: Returns an allocated buffer
+ * @r_length: Returns the length of this buffer
+ * @maxlen: If no 0, the size limit of the inquired data.
+ * 
+ * A Server may use this to Send an inquire
+ * 
+ * Return value: 0 on success or an ASSUAN error code
+ **/
+AssuanError
+assuan_inquire (ASSUAN_CONTEXT ctx, const char *keyword,
+                char **r_buffer, size_t *r_length, size_t maxlen)
+{
+  AssuanError rc;
+  struct membuf mb;
+  char cmdbuf[100];
+  unsigned char *line, *p;
+  int linelen;
+
+  if (!ctx || !keyword || (10 + strlen (keyword) >= sizeof (cmdbuf))
+      || !r_buffer || !r_length )
+    return ASSUAN_Invalid_Value;
+  if (!ctx->is_server)
+    return ASSUAN_Not_A_Server;
+  if (ctx->in_inquire)
+    return ASSUAN_Nested_Commands;
+  
+  ctx->in_inquire = 1;
+  init_membuf (&mb, maxlen? maxlen:1024, maxlen);
+
+  strcpy (stpcpy (cmdbuf, "INQUIRE "), keyword);
+  rc = assuan_write_line (ctx, cmdbuf);
+  if (rc)
+    goto leave;
+
+  for (;;)
+    {
+      do 
+        {
+          rc = _assuan_read_line (ctx);
+          if (rc)
+            goto leave;
+          line = ctx->inbound.line;
+          linelen = ctx->inbound.linelen;
+        }    
+      while (*line == '#' || !linelen);
+      if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
+          && (!line[3] || line[3] == ' '))
+        break; /* END command received*/
+      if (line[0] != 'D' || line[1] != ' ')
+        {
+          rc = ASSUAN_Unexpected_Command;
+          goto leave;
+        }
+      if (linelen < 3)
+        continue;
+      line += 2;
+      linelen -= 2;
+
+      p = line;
+      while (linelen)
+        {
+          for (;linelen && *p != '%'; linelen--, p++)
+            ;
+          put_membuf (&mb, line, p-line);
+          if (linelen > 2)
+            { /* handle escaping */
+              unsigned char tmp[1];
+              p++;
+              *tmp = xtoi_2 (p);
+              p += 2;
+              linelen -= 3;
+              put_membuf (&mb, tmp, 1);
+            }
+          line = p;
+        }
+      if (mb.too_large)
+        {
+          rc = ASSUAN_Too_Much_Data;
+          goto leave;
+        }
+    }
+  
+  *r_buffer = get_membuf (&mb, r_length);
+  if (!*r_buffer)
+    rc = ASSUAN_Out_Of_Core;
+
+ leave:
+  free_membuf (&mb);
+  ctx->in_inquire = 0;
+  return rc;
+}
+
+
+
+
+
+
diff --git a/assuan/assuan-listen.c b/assuan/assuan-listen.c
new file mode 100644 (file)
index 0000000..db63ad2
--- /dev/null
@@ -0,0 +1,132 @@
+/* assuan-listen.c - Wait for a connection (server) 
+ *     Copyright (C) 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "assuan-defs.h"
+
+AssuanError
+assuan_set_hello_line (ASSUAN_CONTEXT ctx, const char *line)
+{
+  if (!ctx)
+    return ASSUAN_Invalid_Value;
+  if (!line)
+    {
+      xfree (ctx->hello_line);
+      ctx->hello_line = NULL;
+    }
+  else
+    {
+      char *buf = xtrymalloc (3+strlen(line)+1);
+      if (!buf)
+        return ASSUAN_Out_Of_Core;
+      strcpy (buf, "OK ");
+      strcpy (buf+3, line);
+      xfree (ctx->hello_line);
+      ctx->hello_line = buf;
+    }
+  return 0;
+}
+
+
+/**
+ * assuan_accept:
+ * @ctx: context
+ * 
+ * Cancel any existing connectiion and wait for a connection from a
+ * client.  The initial handshake is performed which may include an
+ * initial authentication or encryption negotiation.
+ * 
+ * Return value: 0 on success or an error if the connection could for
+ * some reason not be established.
+ **/
+AssuanError
+assuan_accept (ASSUAN_CONTEXT ctx)
+{
+  int rc;
+
+  if (!ctx)
+    return ASSUAN_Invalid_Value;
+
+  if (ctx->pipe_mode > 1)
+    return -1; /* second invocation for pipemode -> terminate */
+  ctx->finish_handler (ctx);
+
+  rc = ctx->accept_handler (ctx);
+  if (rc)
+    return rc;
+
+  /* send the hello */
+  rc = assuan_write_line (ctx, ctx->hello_line? ctx->hello_line
+                                              : "OK Your orders please");
+  if (rc)
+    return rc;
+  
+  if (ctx->pipe_mode)
+    ctx->pipe_mode = 2;
+  
+  return 0;
+}
+
+
+
+int
+assuan_get_input_fd (ASSUAN_CONTEXT ctx)
+{
+  return ctx? ctx->input_fd : -1;
+}
+
+
+int
+assuan_get_output_fd (ASSUAN_CONTEXT ctx)
+{
+  return ctx? ctx->output_fd : -1;
+}
+
+
+/* Close the fd descriptor set by the command INPUT FD=n.  We handle
+   this fd inside assuan so that we can do some initial checks */
+AssuanError
+assuan_close_input_fd (ASSUAN_CONTEXT ctx)
+{
+  if (!ctx || ctx->input_fd == -1)
+    return ASSUAN_Invalid_Value;
+  close (ctx->input_fd);
+  ctx->input_fd = -1;
+  return 0;
+}
+
+/* Close the fd descriptor set by the command OUTPUT FD=n.  We handle
+   this fd inside assuan so that we can do some initial checks */
+AssuanError
+assuan_close_output_fd (ASSUAN_CONTEXT ctx)
+{
+  if (!ctx || ctx->output_fd == -1)
+    return ASSUAN_Invalid_Value;
+
+  close (ctx->output_fd);
+  ctx->output_fd = -1;
+  return 0;
+}
+
diff --git a/assuan/assuan-pipe-connect.c b/assuan/assuan-pipe-connect.c
new file mode 100644 (file)
index 0000000..ccfc1f0
--- /dev/null
@@ -0,0 +1,269 @@
+/* assuan-pipe-connect.c - Establish a pipe connection (client) 
+ *     Copyright (C) 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "assuan-defs.h"
+
+#ifdef _POSIX_OPEN_MAX
+#define MAX_OPEN_FDS _POSIX_OPEN_MAX
+#else
+#define MAX_OPEN_FDS 20
+#endif
+
+#ifdef HAVE_JNLIB_LOGGING
+#include "../jnlib/logging.h"
+#define LOGERROR1(a,b)   log_error ((a), (b))
+#else
+#define LOGERROR1(a,b)   fprintf (stderr, (a), (b))
+#endif
+
+
+
+static int
+writen ( int fd, const char *buffer, size_t length )
+{
+  while (length)
+    {
+      int nwritten = write (fd, buffer, length);
+      
+      if (nwritten < 0)
+        {
+          if (errno == EINTR)
+            continue;
+          return -1; /* write error */
+        }
+      length -= nwritten;
+      buffer += nwritten;
+    }
+  return 0;  /* okay */
+}
+
+
+static int
+do_finish (ASSUAN_CONTEXT ctx)
+{
+  if (ctx->inbound.fd != -1)
+    {
+      close (ctx->inbound.fd);
+      ctx->inbound.fd = -1;
+    }
+  if (ctx->outbound.fd != -1)
+    {
+      close (ctx->outbound.fd);
+      ctx->outbound.fd = -1;
+    }
+  if (ctx->pid != -1)
+    {
+      waitpid (ctx->pid, NULL, 0);  /* FIXME Check return value.  */
+      ctx->pid = -1;
+    }
+  return 0;
+}
+
+static void
+do_deinit (ASSUAN_CONTEXT ctx)
+{
+  do_finish (ctx);
+}
+
+
+
+/* Connect to a server over a pipe, creating the assuan context and
+   returning it in CTX.  The server filename is NAME, the argument
+   vector in ARGV.  FD_CHILD_LIST is a -1 terminated list of file
+   descriptors not to close in the child.  */
+AssuanError
+assuan_pipe_connect (ASSUAN_CONTEXT *ctx, const char *name, char *const argv[],
+                    int *fd_child_list)
+{
+  static int fixed_signals = 0;
+  AssuanError err;
+  int rp[2];
+  int wp[2];
+
+  if (!ctx || !name || !argv || !argv[0])
+    return ASSUAN_Invalid_Value;
+
+  if (!fixed_signals)
+    { 
+      struct sigaction act;
+        
+      sigaction (SIGPIPE, NULL, &act);
+      if (act.sa_handler == SIG_DFL)
+       {
+         act.sa_handler = SIG_IGN;
+         sigemptyset (&act.sa_mask);
+         act.sa_flags = 0;
+         sigaction (SIGPIPE, &act, NULL);
+        }
+      fixed_signals = 1;
+      /* FIXME: This is not MT safe */
+    }
+
+  if (pipe (rp) < 0)
+    return ASSUAN_General_Error;
+
+  if (pipe (wp) < 0)
+    {
+      close (rp[0]);
+      close (rp[1]);
+      return ASSUAN_General_Error;
+    }
+  
+  err = _assuan_new_context (ctx);
+  if (err)
+    {
+      close (rp[0]);
+      close (rp[1]);
+      close (wp[0]);
+      close (wp[1]);
+      return err;
+    }
+  (*ctx)->pipe_mode = 1;
+  (*ctx)->inbound.fd  = rp[0];  /* Our inbound is read end of read pipe. */
+  (*ctx)->outbound.fd = wp[1];  /* Our outbound is write end of write pipe. */
+  (*ctx)->deinit_handler = do_deinit;
+  (*ctx)->finish_handler = do_finish;
+
+  (*ctx)->pid = fork ();
+  if ((*ctx)->pid < 0)
+    {
+      close (rp[0]);
+      close (rp[1]);
+      close (wp[0]);
+      close (wp[1]);
+      _assuan_release_context (*ctx); 
+      return ASSUAN_General_Error;
+    }
+
+  if ((*ctx)->pid == 0)
+    {
+      int i, n;
+      char errbuf[512];
+#ifdef HAVE_JNLIB_LOGGING
+      int log_fd = log_get_fd (); 
+#endif
+      /* close all files which will not be duped but keep stderr
+         and log_stream for now */
+      n = sysconf (_SC_OPEN_MAX);
+      if (n < 0)
+        n = MAX_OPEN_FDS;
+      for (i=0; i < n; i++)
+        {
+         int *fdp = fd_child_list;
+
+         if (fdp)
+           {
+             while (*fdp != -1 && *fdp != i)
+               fdp++;
+           }
+
+          if (!(fdp && *fdp != -1)
+             && i != fileno (stderr) 
+#ifdef HAVE_JNLIB_LOGGING
+              && i != log_fd
+#endif
+              && i != rp[1] && i != wp[0])
+            close(i);
+        }
+      errno = 0;
+
+      /* Dup handles and to stdin/stdout and exec */
+      if (rp[1] != STDOUT_FILENO)
+        {
+          if (dup2 (rp[1], STDOUT_FILENO) == -1)
+            {
+              LOGERROR1 ("dup2 failed in child: %s\n", strerror (errno));
+              _exit (4);
+            }
+          close (rp[1]);
+        }
+      if (wp[0] != STDIN_FILENO)
+        {
+          if (dup2 (wp[0], STDIN_FILENO) == -1)
+            {
+              LOGERROR1 ("dup2 failed in child: %s\n", strerror (errno));
+              _exit (4);
+            }
+          close (wp[0]);
+        }
+
+      execv (name, argv); 
+      /* oops - use the pipe to tell the parent about it */
+      snprintf (errbuf, sizeof(errbuf)-1, "ERR %d can't exec `%s': %.50s\n",
+                ASSUAN_Problem_Starting_Server, name, strerror (errno));
+      errbuf[sizeof(errbuf)-1] = 0;
+      writen (1, errbuf, strlen (errbuf));
+      _exit (4);
+    }
+
+  close (rp[1]);
+  close (wp[0]);
+
+  /* initial handshake */
+  {
+    int okay, off;
+
+    err = _assuan_read_from_server (*ctx, &okay, &off);
+    if (err)
+      {
+        LOGERROR1 ("can't connect server: %s\n", assuan_strerror (err));
+      }
+    else if (okay != 1)
+      {
+        LOGERROR1 ("can't connect server: `%s'\n", (*ctx)->inbound.line);
+        err = ASSUAN_Connect_Failed;
+      }
+  }
+
+  if (err)
+    {
+      assuan_disconnect (*ctx);
+      *ctx = NULL;
+    }
+
+  return err;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assuan/assuan-pipe-server.c b/assuan/assuan-pipe-server.c
new file mode 100644 (file)
index 0000000..d15f54f
--- /dev/null
@@ -0,0 +1,123 @@
+/* assuan-pipe-server.c - Assuan server working over a pipe 
+ *     Copyright (C) 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "assuan-defs.h"
+
+static void
+deinit_pipe_server (ASSUAN_CONTEXT ctx)
+{
+  /* nothing to do for this simple server */
+}
+
+static int
+accept_connection (ASSUAN_CONTEXT ctx)
+{
+  /* This is a NOP for a pipe server */
+  return 0;
+}
+
+static int
+finish_connection (ASSUAN_CONTEXT ctx)
+{
+  /* This is a NOP for a pipe server */
+  return 0;
+}
+
+
+/* Create a new context.  Note that the handlers are set up for a pipe
+   server/client - this wau we don't need extra dummy functions */
+int
+_assuan_new_context (ASSUAN_CONTEXT *r_ctx)
+{
+  ASSUAN_CONTEXT ctx;
+  int rc;
+
+  *r_ctx = NULL;
+  ctx = xtrycalloc (1, sizeof *ctx);
+  if (!ctx)
+    return ASSUAN_Out_Of_Core;
+  ctx->input_fd = -1;
+  ctx->output_fd = -1;
+
+  ctx->inbound.fd = -1;
+  ctx->outbound.fd = -1;
+
+  ctx->listen_fd = -1;
+  /* use the pipe server handler as a default */
+  ctx->deinit_handler = deinit_pipe_server;
+  ctx->accept_handler = accept_connection;
+  ctx->finish_handler = finish_connection;
+
+  rc = _assuan_register_std_commands (ctx);
+  if (rc)
+    xfree (ctx);
+  else
+    *r_ctx = ctx;
+  return rc;
+}
+
+
+
+int
+assuan_init_pipe_server (ASSUAN_CONTEXT *r_ctx, int filedes[2])
+{
+  int rc;
+
+  rc = _assuan_new_context (r_ctx);
+  if (!rc)
+    {
+      ASSUAN_CONTEXT ctx = *r_ctx;
+
+      ctx->is_server = 1;
+      ctx->inbound.fd = filedes[0];
+      ctx->outbound.fd = filedes[1];
+      ctx->pipe_mode = 1;
+    }
+  return rc;
+}
+
+
+void
+_assuan_release_context (ASSUAN_CONTEXT ctx)
+{
+  if (ctx)
+    {
+      xfree (ctx->hello_line);
+      xfree (ctx->okay_line);
+      xfree (ctx);
+    }
+}
+
+void
+assuan_deinit_server (ASSUAN_CONTEXT ctx)
+{
+  if (ctx)
+    {
+      /* We use this function pointer to avoid linking other server
+         when not needed but still allow for a generic deinit function */
+      ctx->deinit_handler (ctx);
+      ctx->deinit_handler = NULL;
+      _assuan_release_context (ctx);
+    }
+}
diff --git a/assuan/assuan-socket-connect.c b/assuan/assuan-socket-connect.c
new file mode 100644 (file)
index 0000000..748a91f
--- /dev/null
@@ -0,0 +1,150 @@
+/* assuan-socket-connect.c - Assuan socket based client
+ *     Copyright (C) 2002 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "assuan-defs.h"
+
+#ifdef HAVE_JNLIB_LOGGING
+#include "../jnlib/logging.h"
+#define LOGERROR(a)      log_error ((a))
+#define LOGERROR1(a,b)   log_error ((a), (b))
+#define LOGERROR2(a,b,c) log_error ((a), (b), (c))
+#define LOGERRORX(a)     log_printf ((a))
+#else
+#define LOGERROR(a)      fprintf (stderr, (a))
+#define LOGERROR1(a,b)   fprintf (stderr, (a), (b))
+#define LOGERROR2(a,b,c) fprintf (stderr, (a), (b), (c))
+#define LOGERRORX(a)     fputs ((a), stderr)
+#endif
+
+
+
+static int
+do_finish (ASSUAN_CONTEXT ctx)
+{
+  if (ctx->inbound.fd != -1)
+    {
+      close (ctx->inbound.fd);
+    }
+  ctx->inbound.fd = -1;
+  ctx->outbound.fd = -1;
+  return 0;
+}
+
+static void
+do_deinit (ASSUAN_CONTEXT ctx)
+{
+  do_finish (ctx);
+}
+
+
+
+/* Make a connection to the Unix domain socket NAME and return a new
+   Assuan context in CTX.  SERVER_PID is currently not used but may
+   becode handy in future. */
+AssuanError
+assuan_socket_connect (ASSUAN_CONTEXT *r_ctx,
+                       const char *name, pid_t server_pid)
+{
+  AssuanError err;
+  ASSUAN_CONTEXT ctx;
+  int fd;
+  struct sockaddr_un srvr_addr;
+  size_t len;
+
+  if (!r_ctx || !name)
+    return ASSUAN_Invalid_Value;
+  *r_ctx = NULL;
+
+  /* we require that the name starts with a slash, so that we can
+     alter reuse this function for other socket types */
+  if (*name != '/')
+    return ASSUAN_Invalid_Value;
+  if (strlen (name)+1 >= sizeof srvr_addr.sun_path)
+    return ASSUAN_Invalid_Value;
+
+  err = _assuan_new_context (&ctx); 
+  if (err)
+      return err;
+  ctx->pid = server_pid; /* save it in case we need it later */
+  ctx->deinit_handler = do_deinit;
+  ctx->finish_handler = do_finish;
+
+  fd = socket (AF_UNIX, SOCK_STREAM, 0);
+  if (fd == -1)
+    {
+      LOGERROR1 ("can't create socket: %s\n", strerror (errno));
+      _assuan_release_context (ctx);
+      return ASSUAN_General_Error;
+    }
+    
+  memset (&srvr_addr, 0, sizeof srvr_addr );
+  srvr_addr.sun_family = AF_UNIX;
+  strcpy (srvr_addr.sun_path, name);
+  len = (offsetof (struct sockaddr_un, sun_path)
+         + strlen (srvr_addr.sun_path) + 1);
+    
+  if (connect (fd, (struct sockaddr*)&srvr_addr, len) == -1)
+    {
+      LOGERROR2 ("can't connect to `%s': %s\n", name, strerror (errno));
+      _assuan_release_context (ctx);
+      close (fd );
+      return ASSUAN_Connect_Failed;
+    }
+
+  ctx->inbound.fd = fd;
+  ctx->outbound.fd = fd;
+
+  /* initial handshake */
+  {
+    int okay, off;
+
+    err = _assuan_read_from_server (ctx, &okay, &off);
+    if (err)
+      {
+        LOGERROR1 ("can't connect server: %s\n", assuan_strerror (err));
+      }
+    else if (okay != 1)
+      {
+        LOGERROR ("can't connect server: `");
+        _assuan_log_sanitized_string (ctx->inbound.line);
+        LOGERRORX ("'\n");
+        err = ASSUAN_Connect_Failed;
+      }
+  }
+
+  if (err)
+    {
+      assuan_disconnect (ctx); 
+    }
+  else
+    *r_ctx = ctx;
+  return 0;
+}
+
+
diff --git a/assuan/assuan-socket-server.c b/assuan/assuan-socket-server.c
new file mode 100644 (file)
index 0000000..6ad6455
--- /dev/null
@@ -0,0 +1,121 @@
+/* assuan-socket-server.c - Assuan socket based server
+ *     Copyright (C) 2002 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "assuan-defs.h"
+
+static int
+accept_connection (ASSUAN_CONTEXT ctx)
+{
+  int fd;
+  struct sockaddr_un clnt_addr;
+  size_t len = sizeof clnt_addr;
+
+  fd = accept (ctx->listen_fd, (struct sockaddr*)&clnt_addr, &len );
+  if (fd == -1)
+    {
+      ctx->os_errno = errno;
+      return ASSUAN_Accept_Failed;
+    }
+
+  ctx->inbound.fd = fd;
+  ctx->inbound.eof = 0;
+  ctx->inbound.linelen = 0;
+  ctx->inbound.attic.linelen = 0;
+  ctx->inbound.attic.pending = 0;
+
+  ctx->outbound.fd = fd;
+  ctx->outbound.data.linelen = 0;
+  ctx->outbound.data.error = 0;
+  
+  ctx->confidential = 0;
+
+  return 0;
+}
+
+static int
+finish_connection (ASSUAN_CONTEXT ctx)
+{
+  if (ctx->inbound.fd != -1)
+    {
+      close (ctx->inbound.fd);
+    }
+  ctx->inbound.fd = -1;
+  ctx->outbound.fd = -1;
+  return 0;
+}
+
+
+static void
+deinit_socket_server (ASSUAN_CONTEXT ctx)
+{
+  finish_connection (ctx);
+}
+
+
+
+/* Initialize a server for the socket LISTEN_FD which has already be
+   put into listen mode */
+int
+assuan_init_socket_server (ASSUAN_CONTEXT *r_ctx, int listen_fd)
+{
+  ASSUAN_CONTEXT ctx;
+  int rc;
+
+  *r_ctx = NULL;
+  ctx = xtrycalloc (1, sizeof *ctx);
+  if (!ctx)
+    return ASSUAN_Out_Of_Core;
+  ctx->is_server = 1;
+  ctx->input_fd = -1;
+  ctx->output_fd = -1;
+
+  ctx->inbound.fd = -1;
+  ctx->outbound.fd = -1;
+
+  ctx->listen_fd = listen_fd;
+  ctx->deinit_handler = deinit_socket_server;
+  ctx->accept_handler = accept_connection;
+  ctx->finish_handler = finish_connection;
+
+  rc = _assuan_register_std_commands (ctx);
+  if (rc)
+    xfree (ctx);
+  else
+    *r_ctx = ctx;
+  return rc;
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/assuan/assuan-util.c b/assuan/assuan-util.c
new file mode 100644 (file)
index 0000000..4153ef8
--- /dev/null
@@ -0,0 +1,196 @@
+/* assuan-util.c - Utility functions for Assuan 
+ *     Copyright (C) 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "assuan-defs.h"
+
+#ifdef HAVE_JNLIB_LOGGING
+#include "../jnlib/logging.h"
+#endif
+
+
+static void *(*alloc_func)(size_t n) = malloc;
+static void *(*realloc_func)(void *p, size_t n) = realloc;
+static void (*free_func)(void*) = free;
+
+
+
+void
+assuan_set_malloc_hooks ( void *(*new_alloc_func)(size_t n),
+                          void *(*new_realloc_func)(void *p, size_t n),
+                          void (*new_free_func)(void*) )
+{
+  alloc_func       = new_alloc_func;
+  realloc_func      = new_realloc_func;
+  free_func        = new_free_func;
+}
+
+void *
+_assuan_malloc (size_t n)
+{
+  return alloc_func (n);
+}
+
+void *
+_assuan_realloc (void *a, size_t n)
+{
+  return realloc_func (a, n);
+}
+
+void *
+_assuan_calloc (size_t n, size_t m)
+{
+  void *p = _assuan_malloc (n*m);
+  if (p)
+    memset (p, 0, n* m);
+  return p;
+}
+
+void
+_assuan_free (void *p)
+{
+  if (p)
+    free_func (p);
+}
+
+
+\f
+/* Store the error in the context so that the error sending function
+  can take out a descriptive text.  Inside the assuan code, use the
+  macro set_error instead of this function. */
+int
+assuan_set_error (ASSUAN_CONTEXT ctx, int err, const char *text)
+{
+  ctx->err_no = err;
+  ctx->err_str = text;
+  return err;
+}
+
+void
+assuan_set_pointer (ASSUAN_CONTEXT ctx, void *pointer)
+{
+  if (ctx)
+    ctx->user_pointer = pointer;
+}
+
+void *
+assuan_get_pointer (ASSUAN_CONTEXT ctx)
+{
+  return ctx? ctx->user_pointer : NULL;
+}
+
+
+void
+assuan_set_log_stream (ASSUAN_CONTEXT ctx, FILE *fp)
+{
+  if (ctx)
+    {
+      if (ctx->log_fp)
+        fflush (ctx->log_fp);
+      ctx->log_fp = fp;
+    }
+}
+
+
+void
+assuan_begin_confidential (ASSUAN_CONTEXT ctx)
+{
+  if (ctx)
+    {
+      ctx->confidential = 1;
+    }
+}
+
+void
+assuan_end_confidential (ASSUAN_CONTEXT ctx)
+{
+  if (ctx)
+    {
+      ctx->confidential = 0;
+    }
+}
+
+void
+_assuan_log_print_buffer (FILE *fp, const void *buffer, size_t length)
+{
+  const unsigned char *s;
+  int n;
+
+  for (n=length,s=buffer; n; n--, s++)
+    {
+      if (*s < ' ' || (*s >= 0x7f && *s <= 0xa0))
+        break;
+    }
+  s = buffer;
+  if (!n && *s != '[')
+    fwrite (buffer, length, 1, fp);
+  else
+    {
+      putc ('[', fp);
+      for (n=0; n < length; n++, s++)
+          fprintf (fp, " %02x", *s);
+      putc (' ', fp);
+      putc (']', fp);
+    }
+}
+
+
+/* print a user supplied string after filtering out potential bad
+   characters*/
+void
+_assuan_log_sanitized_string (const char *string)
+{
+  const unsigned char *s = string;
+#ifdef HAVE_JNLIB_LOGGING
+  FILE *fp = log_get_stream ();
+#else
+  FILE *fp = stderr;
+#endif
+
+  for (; *s; s++)
+    {
+      if (*s < 0x20 || (*s >= 0x7f && *s <= 0xa0))
+        {
+          putc ('\\', fp);
+          if (*s == '\n')
+            putc ('n', fp);
+          else if (*s == '\r')
+            putc ('r', fp);
+          else if (*s == '\f')
+            putc ('f', fp);
+          else if (*s == '\v')
+            putc ('v', fp);
+          else if (*s == '\b')
+            putc ('b', fp);
+          else if (!*s)
+            putc ('0', fp);
+          else
+            fprintf (fp, "x%02x", *s );
+       }
+      else
+        putc (*s, fp);
+    }
+}
+
+
diff --git a/assuan/assuan.h b/assuan/assuan.h
new file mode 100644 (file)
index 0000000..5971d81
--- /dev/null
@@ -0,0 +1,216 @@
+/* assuan.c - Definitions for the Assuna protocol
+ *     Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef ASSUAN_H
+#define ASSUAN_H
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" { 
+#if 0
+ }
+#endif
+#endif
+
+typedef enum {
+  ASSUAN_No_Error = 0,
+  ASSUAN_General_Error = 1,
+  ASSUAN_Out_Of_Core = 2,
+  ASSUAN_Invalid_Value = 3,
+  ASSUAN_Timeout = 4,  
+  ASSUAN_Read_Error = 5,
+  ASSUAN_Write_Error = 6,
+  ASSUAN_Problem_Starting_Server = 7,
+  ASSUAN_Not_A_Server = 8,
+  ASSUAN_Not_A_Client = 9,
+  ASSUAN_Nested_Commands = 10,
+  ASSUAN_Invalid_Response = 11,
+  ASSUAN_No_Data_Callback = 12,
+  ASSUAN_No_Inquire_Callback = 13,
+  ASSUAN_Connect_Failed = 14,
+  ASSUAN_Accept_Failed = 15,
+
+  /* error codes above 99 are meant as status codes */
+  ASSUAN_Not_Implemented = 100,
+  ASSUAN_Server_Fault    = 101,
+  ASSUAN_Invalid_Command = 102,
+  ASSUAN_Unknown_Command = 103,
+  ASSUAN_Syntax_Error    = 104,
+  ASSUAN_Parameter_Error = 105,
+  ASSUAN_Parameter_Conflict = 106,
+  ASSUAN_Line_Too_Long = 107,
+  ASSUAN_Line_Not_Terminated = 108,
+  ASSUAN_No_Input = 109,
+  ASSUAN_No_Output = 110,
+  ASSUAN_Canceled = 111,
+  ASSUAN_Unsupported_Algorithm = 112,
+  ASSUAN_Server_Resource_Problem = 113,
+  ASSUAN_Server_IO_Error = 114,
+  ASSUAN_Server_Bug = 115,
+  ASSUAN_No_Data_Available = 116,
+  ASSUAN_Invalid_Data = 117,
+  ASSUAN_Unexpected_Command = 118,
+  ASSUAN_Too_Much_Data = 119,
+  ASSUAN_Inquire_Unknown = 120,
+  ASSUAN_Inquire_Error = 121,
+  ASSUAN_Invalid_Option = 122,
+
+  ASSUAN_Bad_Certificate = 201,
+  ASSUAN_Bad_Certificate_Path = 202,
+  ASSUAN_Missing_Certificate = 203,
+  ASSUAN_Bad_Signature = 204,
+  ASSUAN_No_Agent = 205,
+  ASSUAN_Agent_Error = 206,
+  ASSUAN_No_Public_Key = 207,
+  ASSUAN_No_Secret_Key = 208,
+  ASSUAN_Invalid_Name = 209,
+
+  ASSUAN_Cert_Revoked = 301,
+  ASSUAN_No_CRL_For_Cert = 302,
+  ASSUAN_CRL_Too_Old = 303,
+  ASSUAN_Not_Trusted = 304,
+
+} AssuanError;
+
+/* This is a list of pre-registered ASSUAN commands */
+typedef enum {
+  ASSUAN_CMD_NOP = 0,
+  ASSUAN_CMD_CANCEL,    /* cancel the current request */
+  ASSUAN_CMD_BYE,
+  ASSUAN_CMD_AUTH,
+  ASSUAN_CMD_RESET,
+  ASSUAN_CMD_OPTION,
+  ASSUAN_CMD_DATA,
+  ASSUAN_CMD_END,
+  ASSUAN_CMD_INPUT,
+  ASSUAN_CMD_OUTPUT,
+
+  ASSUAN_CMD_USER = 256  /* Other commands should be used with this offset*/
+} AssuanCommand;
+
+#define ASSUAN_LINELENGTH 1002 /* 1000 + [CR,]LF */
+
+struct assuan_context_s;
+typedef struct assuan_context_s *ASSUAN_CONTEXT;
+
+/*-- assuan-handler.c --*/
+int assuan_register_command (ASSUAN_CONTEXT ctx,
+                             int cmd_id, const char *cmd_string,
+                             int (*handler)(ASSUAN_CONTEXT, char *));
+int assuan_register_bye_notify (ASSUAN_CONTEXT ctx,
+                                void (*fnc)(ASSUAN_CONTEXT));
+int assuan_register_reset_notify (ASSUAN_CONTEXT ctx,
+                                  void (*fnc)(ASSUAN_CONTEXT));
+int assuan_register_cancel_notify (ASSUAN_CONTEXT ctx,
+                                   void (*fnc)(ASSUAN_CONTEXT));
+int assuan_register_input_notify (ASSUAN_CONTEXT ctx,
+                                  void (*fnc)(ASSUAN_CONTEXT, const char *));
+int assuan_register_output_notify (ASSUAN_CONTEXT ctx,
+                                  void (*fnc)(ASSUAN_CONTEXT, const char *));
+
+int assuan_register_option_handler (ASSUAN_CONTEXT ctx,
+                                    int (*fnc)(ASSUAN_CONTEXT,
+                                               const char*, const char*));
+
+int assuan_process (ASSUAN_CONTEXT ctx);
+int assuan_process_next (ASSUAN_CONTEXT ctx);
+int assuan_get_active_fds (ASSUAN_CONTEXT ctx, int what,
+                           int *fdarray, int fdarraysize);
+
+
+FILE *assuan_get_data_fp (ASSUAN_CONTEXT ctx);
+AssuanError assuan_set_okay_line (ASSUAN_CONTEXT ctx, const char *line);
+void assuan_write_status (ASSUAN_CONTEXT ctx,
+                          const char *keyword, const char *text);
+
+
+/*-- assuan-listen.c --*/
+AssuanError assuan_set_hello_line (ASSUAN_CONTEXT ctx, const char *line);
+AssuanError assuan_accept (ASSUAN_CONTEXT ctx);
+int assuan_get_input_fd (ASSUAN_CONTEXT ctx);
+int assuan_get_output_fd (ASSUAN_CONTEXT ctx);
+AssuanError assuan_close_input_fd (ASSUAN_CONTEXT ctx);
+AssuanError assuan_close_output_fd (ASSUAN_CONTEXT ctx);
+
+
+/*-- assuan-pipe-server.c --*/
+int assuan_init_pipe_server (ASSUAN_CONTEXT *r_ctx, int filedes[2]);
+void assuan_deinit_server (ASSUAN_CONTEXT ctx);
+
+/*-- assuan-socket-server.c --*/
+int assuan_init_socket_server (ASSUAN_CONTEXT *r_ctx, int listen_fd);
+
+
+/*-- assuan-pipe-connect.c --*/
+AssuanError assuan_pipe_connect (ASSUAN_CONTEXT *ctx, const char *name,
+                                 char *const argv[], int *fd_child_list);
+/*-- assuan-socket-connect.c --*/
+AssuanError assuan_socket_connect (ASSUAN_CONTEXT *ctx, const char *name,
+                                   pid_t server_pid);
+
+/*-- assuan-connect.c --*/
+void assuan_disconnect (ASSUAN_CONTEXT ctx);
+pid_t assuan_get_pid (ASSUAN_CONTEXT ctx);
+
+/*-- assuan-client.c --*/
+AssuanError 
+assuan_transact (ASSUAN_CONTEXT ctx,
+                 const char *command,
+                 AssuanError (*data_cb)(void *, const void *, size_t),
+                 void *data_cb_arg,
+                 AssuanError (*inquire_cb)(void*, const char *),
+                 void *inquire_cb_arg);
+
+
+/*-- assuan-inquire.c --*/
+AssuanError assuan_inquire (ASSUAN_CONTEXT ctx, const char *keyword,
+                            char **r_buffer, size_t *r_length, size_t maxlen);
+
+/*-- assuan-buffer.c --*/
+AssuanError assuan_read_line (ASSUAN_CONTEXT ctx,
+                              char **line, size_t *linelen);
+int assuan_pending_line (ASSUAN_CONTEXT ctx);
+AssuanError assuan_write_line (ASSUAN_CONTEXT ctx, const char *line );
+AssuanError assuan_send_data (ASSUAN_CONTEXT ctx,
+                              const void *buffer, size_t length);
+
+
+/*-- assuan-util.c --*/
+void assuan_set_malloc_hooks ( void *(*new_alloc_func)(size_t n),
+                               void *(*new_realloc_func)(void *p, size_t n),
+                               void (*new_free_func)(void*) );
+void assuan_set_log_stream (ASSUAN_CONTEXT ctx, FILE *fp);
+int assuan_set_error (ASSUAN_CONTEXT ctx, int err, const char *text);
+void assuan_set_pointer (ASSUAN_CONTEXT ctx, void *pointer);
+void *assuan_get_pointer (ASSUAN_CONTEXT ctx);
+
+void assuan_begin_confidential (ASSUAN_CONTEXT ctx);
+void assuan_end_confidential (ASSUAN_CONTEXT ctx);
+
+/*-- assuan-errors.c (built) --*/
+const char *assuan_strerror (AssuanError err);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*ASSUAN_H*/
diff --git a/assuan/mkerrors b/assuan/mkerrors
new file mode 100755 (executable)
index 0000000..13eabde
--- /dev/null
@@ -0,0 +1,71 @@
+#!/bin/sh
+# mkerrors - Extract error strings from assuan.h
+#            and create C source for assuan_strerror
+#      Copyright (C) 2001 Free Software Foundation, Inc.
+#
+# 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 2 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+cat <<EOF
+/* Generated automatically by mkerrors */
+/* Do not edit! */
+
+#include <stdio.h>
+#include "assuan.h"
+
+/**
+ * assuan_strerror:
+ * @err:  Error code 
+ * 
+ * This function returns a textual representaion of the given
+ * errorcode. If this is an unknown value, a string with the value
+ * is returned (Beware: it is hold in a static buffer).
+ * 
+ * Return value: String with the error description.
+ **/
+const char *
+assuan_strerror (AssuanError err)
+{
+  const char *s;
+  static char buf[25];
+
+  switch (err)
+    {
+EOF
+
+awk '
+/ASSUAN_No_Error/    { okay=1 }
+!okay              {next}
+/}/                { exit 0 }
+/ASSUAN_[A-Za-z_]*/ { print_code($1) }
+
+
+function print_code( s )
+{
+printf "    case %s: s=\"", s ;
+gsub(/_/, " ", s );
+printf "%s\"; break;\n", tolower(substr(s,8));
+}
+'
+
+cat <<EOF
+    default:  sprintf (buf, "ec=%d", err ); s=buf; break;
+    }
+
+  return s;
+}
+
+EOF
\ No newline at end of file
index 46fe7ef..7c2324f 100644 (file)
@@ -63,9 +63,32 @@ AC_SUBST(ASSUAN_LDFLAGS)
 AC_SUBST(LIBASSUAN)
 ])
 
-AC_PATH_ASSUAN
+dnl AC_PATH_ASSUAN
+AC_SUBST(ASSUAN_INCLUDES)
+AC_SUBST(ASSUAN_LDFLAGS)
+AC_SUBST(LIBASSUAN)
+
+AC_CHECK_GNU_EXTENSIONS
+
+
+AC_CHECK_FUNCS(asprintf,,[
+    AC_MSG_ERROR([[
+***
+*** asprintf(3) is needed to build this package.
+*** We will provide an replacement in a later release.
+***]])])
+AC_CHECK_FUNCS(fopencookie,,[
+    AC_MSG_ERROR([[
+***
+*** fopencookie(3) is needed to build this package.
+*** We will provide an replacement in a later release.
+***]])])
+
+# We use jnlib, so tell other modules about it
+AC_DEFINE(HAVE_JNLIB_LOGGING, 1,
+        [Defined if jnlib style logging fucntions are available])
 
-AC_CHECK_FUNCS(getdelim seteuid strsignal vsnprintf)
+AC_CHECK_FUNCS(getdelim seteuid strsignal vsnprintf memicmp stpcpy strlwr strtoul memmove stricmp strtol)
 AC_REPLACE_FUNCS(asprintf getline setenv strdup)
 
 dnl this one stolen from GnuPG
diff --git a/jnlib/ChangeLog b/jnlib/ChangeLog
new file mode 100644 (file)
index 0000000..b5f723c
--- /dev/null
@@ -0,0 +1,84 @@
+2001-11-01  Marcus Brinkmann  <marcus@g10code.de>
+
+       * logging.c (log_printf): Do not initialize ARG_PTR with 0, we
+       don't know the correct type.  Instead, run va_start and va_end
+       unconditionally.
+       Reported by Jose Carlos Garcia Sogo <jsogo@debian.org>.
+
+2002-01-19  Werner Koch  <wk@gnupg.org>
+
+       * logging.c (log_get_stream): New.
+
+2001-12-05  Werner Koch  <wk@gnupg.org>
+
+       * logging.c (log_set_prefix): New.
+       (do_logv): Include prefix and pid only if enabled. Print time only
+       when explicitly enabled.
+       (log_logv): New.  
+       * logging.h: Include log_logv() only when requested.
+
+2001-11-06  Werner Koch  <wk@gnupg.org>
+
+       * strlist.c, strlist.h: New. Taken from pgnupg/util/strgutil.c
+
+2001-08-30  Werner Koch  <wk@gnupg.org>
+
+       * logging.c (log_printf): Don't pass NULL instead of arg_ptr.
+
+2001-07-19  Werner Koch  <wk@gnupg.org>
+
+       * stringhelp.c (ascii_memistr,ascii_isupper,ascii_islower,
+       ascii_toupper,ascii_tolower, ascii_strcasecmp, ascii_memcasecmp): New.
+
+2000-07-26 10:02:51  Werner Koch  (wk@habibti.openit.de)
+
+  * stringhelp.c.: Add stdarg.h
+  * argparse.h: s/ulong/unsigned long/ although this should be defined
+  by types.h.
+
+2000-06-28 19:40:23  Werner Koch  (wk@habibti.openit.de)
+
+  * Makefile.am: Replaced second logging.c by .h
+
+2000-05-24 08:58:15  Werner Koch  (wk@habibti.openit.de)
+
+  * logging.c (log_get_errorcount): New.
+
+2000-05-24 08:44:47  Werner Koch  (wk@habibti.openit.de)
+
+  * stringhelp.c: Added a few filename related helper functions.
+
+2000-05-11 18:04:43  Werner Koch  (wk@habibti.openit.de)
+
+  * xmalloc.c (xstrcat2):  Replaced stpcpy to quickly address W32
+    problems.
+
+2000-05-02 19:43:38  Werner Koch  (wk@habibti.openit.de)
+
+  * xmalloc.c (xstrcat2): New.
+
+Mon Jan 24 13:04:28 CET 2000  Werner Koch  <wk@gnupg.de>
+
+  * README: New.
+  * Makefile.am: new.
+  * argparse.c argparse.h logging.c logging.h
+    mischelp.h stringhelp.c stringhelp.h xmalloc.c
+    xmalloc.h dotlock.c: Moved from ../util to here.
+  * dotlock.h: New.
+  * libjnlib-config.h: New.
+
+  * logging.c (log_set_file): New.
+  (log_printf): New.
+  (do_logv): Add kludge to insert LFs.
+
+
+ Copyright 2000 Werner Koch (dd9jn)
+ Copyright 2001 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
+ modifications, as long as this notice is preserved.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/jnlib/Makefile.am b/jnlib/Makefile.am
new file mode 100644 (file)
index 0000000..515a49a
--- /dev/null
@@ -0,0 +1,39 @@
+# Copyright (C) 1999, 2000, 2001 Feee Software Soundation, Inc.
+# 
+# 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 2 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+## Process this file with automake to produce Makefile.in
+
+EXTRA_DIST = README
+
+INCLUDES = -I$(top_srcdir)/intl
+
+noinst_LIBRARIES = libjnlib.a
+
+
+#libjnlib_a_LDFLAGS =
+libjnlib_a_SOURCES = \
+       libjnlib-config.h \
+       stringhelp.c stringhelp.h \
+       strlist.c strlist.h \
+       argparse.c argparse.h \
+       logging.c logging.h  \
+       dotlock.c dotlock.h  \
+       types.h mischelp.h
+
+#                   xmalloc.c xmalloc.h       
+
diff --git a/jnlib/argparse.c b/jnlib/argparse.c
new file mode 100644 (file)
index 0000000..0e353e4
--- /dev/null
@@ -0,0 +1,995 @@
+/* [argparse.c wk 17.06.97] Argument Parser for option handling
+ *     Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ *  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 2 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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "libjnlib-config.h"
+#include "mischelp.h"
+#include "stringhelp.h"
+#include "logging.h"
+#include "argparse.h"
+
+
+/*********************************
+ * @Summary arg_parse
+ *  #include <wk/lib.h>
+ *
+ *  typedef struct {
+ *     char *argc;               pointer to argc (value subject to change)
+ *     char ***argv;             pointer to argv (value subject to change)
+ *     unsigned flags;           Global flags (DO NOT CHANGE)
+ *     int err;                  print error about last option
+ *                               1 = warning, 2 = abort
+ *     int r_opt;                return option
+ *     int r_type;               type of return value (0 = no argument found)
+ *     union {
+ *         int   ret_int;
+ *         long  ret_long
+ *         ulong ret_ulong;
+ *         char *ret_str;
+ *     } r;                      Return values
+ *     struct {
+ *         int idx;
+ *         const char *last;
+ *         void *aliases;
+ *     } internal;               DO NOT CHANGE
+ *  } ARGPARSE_ARGS;
+ *
+ *  typedef struct {
+ *     int         short_opt;
+ *     const char *long_opt;
+ *     unsigned flags;
+ *  } ARGPARSE_OPTS;
+ *
+ *  int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
+ *
+ * @Description
+ *  This is my replacement for getopt(). See the example for a typical usage.
+ *  Global flags are:
+ *     Bit 0 : Do not remove options form argv
+ *     Bit 1 : Do not stop at last option but return other args
+ *            with r_opt set to -1.
+ *     Bit 2 : Assume options and real args are mixed.
+ *     Bit 3 : Do not use -- to stop option processing.
+ *     Bit 4 : Do not skip the first arg.
+ *     Bit 5 : allow usage of long option with only one dash
+ *     Bit 6 : ignore --version
+ *     all other bits must be set to zero, this value is modified by the
+ *     function, so assume this is write only.
+ *  Local flags (for each option):
+ *     Bit 2-0 : 0 = does not take an argument
+ *              1 = takes int argument
+ *              2 = takes string argument
+ *              3 = takes long argument
+ *              4 = takes ulong argument
+ *     Bit 3 : argument is optional (r_type will the be set to 0)
+ *     Bit 4 : allow 0x etc. prefixed values.
+ *     Bit 7 : this is a command and not an option
+ *  You stop the option processing by setting opts to NULL, the function will
+ *  then return 0.
+ * @Return Value
+ *   Returns the args.r_opt or 0 if ready
+ *   r_opt may be -2/-7 to indicate an unknown option/command.
+ * @See Also
+ *   ArgExpand
+ * @Notes
+ *  You do not need to process the options 'h', '--help' or '--version'
+ *  because this function includes standard help processing; but if you
+ *  specify '-h', '--help' or '--version' you have to do it yourself.
+ *  The option '--' stops argument processing; if bit 1 is set the function
+ *  continues to return normal arguments.
+ *  To process float args or unsigned args you must use a string args and do
+ *  the conversion yourself.
+ * @Example
+ *
+ *     ARGPARSE_OPTS opts[] = {
+ *     { 'v', "verbose",   0 },
+ *     { 'd', "debug",     0 },
+ *     { 'o', "output",    2 },
+ *     { 'c', "cross-ref", 2|8 },
+ *     { 'm', "my-option", 1|8 },
+ *     { 500, "have-no-short-option-for-this-long-option", 0 },
+ *     {0} };
+ *     ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
+ *
+ *     while( ArgParse( &pargs, &opts) ) {
+ *        switch( pargs.r_opt ) {
+ *          case 'v': opt.verbose++; break;
+ *          case 'd': opt.debug++; break;
+ *          case 'o': opt.outfile = pargs.r.ret_str; break;
+ *          case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
+ *          case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
+ *          case 500: opt.a_long_one++;  break
+ *          default : pargs.err = 1; break; -- force warning output --
+ *        }
+ *     }
+ *     if( argc > 1 )
+ *        log_fatal( "Too many args");
+ *
+ */
+
+typedef struct alias_def_s *ALIAS_DEF;
+struct alias_def_s {
+    ALIAS_DEF next;
+    char *name;   /* malloced buffer with name, \0, value */
+    const char *value; /* ptr into name */
+};
+
+static const char *(*strusage_handler)( int ) = NULL;
+
+static int  set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
+static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
+static void show_version(void);
+
+
+static void
+initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
+{
+    if( !(arg->flags & (1<<15)) ) { /* initialize this instance */
+       arg->internal.idx = 0;
+       arg->internal.last = NULL;
+       arg->internal.inarg = 0;
+       arg->internal.stopped = 0;
+       arg->internal.aliases = NULL;
+       arg->internal.cur_alias = NULL;
+       arg->err = 0;
+       arg->flags |= 1<<15; /* mark initialized */
+       if( *arg->argc < 0 )
+           jnlib_log_bug("Invalid argument for ArgParse\n");
+    }
+
+
+    if( arg->err ) { /* last option was erroneous */
+       const char *s;
+
+       if( filename ) {
+           if( arg->r_opt == -6 )
+               s = "%s:%u: argument not expected\n";
+           else if( arg->r_opt == -5 )
+               s = "%s:%u: read error\n";
+           else if( arg->r_opt == -4 )
+               s = "%s:%u: keyword too long\n";
+           else if( arg->r_opt == -3 )
+               s = "%s:%u: missing argument\n";
+           else if( arg->r_opt == -7 )
+               s = "%s:%u: invalid command\n";
+           else if( arg->r_opt == -10 )
+               s = "%s:%u: invalid alias definition\n";
+           else
+               s = "%s:%u: invalid option\n";
+           jnlib_log_error(s, filename, *lineno );
+       }
+       else {
+           if( arg->r_opt == -3 )
+               s = "Missing argument for option \"%.50s\"\n";
+           else if( arg->r_opt == -6 )
+               s = "Option \"%.50s\" does not expect an argument\n";
+           else if( arg->r_opt == -7 )
+               s = "Invalid command \"%.50s\"\n";
+           else if( arg->r_opt == -8 )
+               s = "Option \"%.50s\" is ambiguous\n";
+           else if( arg->r_opt == -9 )
+               s = "Command \"%.50s\" is ambiguous\n";
+           else
+               s = "Invalid option \"%.50s\"\n";
+           jnlib_log_error(s, arg->internal.last? arg->internal.last:"[??]" );
+       }
+       if( arg->err != 1 )
+           exit(2);
+       arg->err = 0;
+    }
+
+    /* clearout the return value union */
+    arg->r.ret_str = NULL;
+    arg->r.ret_long= 0;
+}
+
+
+static void
+store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
+{
+    /* TODO: replace this dummy function with a rea one
+     * and fix the probelms IRIX has with (ALIAS_DEV)arg..
+     * used as lvalue
+     */
+#if 0
+    ALIAS_DEF a = jnlib_xmalloc( sizeof *a );
+    a->name = name;
+    a->value = value;
+    a->next = (ALIAS_DEF)arg->internal.aliases;
+    (ALIAS_DEF)arg->internal.aliases = a;
+#endif
+}
+
+/****************
+ * Get options from a file.
+ * Lines starting with '#' are comment lines.
+ * Syntax is simply a keyword and the argument.
+ * Valid keywords are all keywords from the long_opt list without
+ * the leading dashes. The special keywords "help", "warranty" and "version"
+ * are not valid here.
+ * The special keyword "alias" may be used to store alias definitions,
+ * which are later expanded like long options.
+ * Caller must free returned strings.
+ * If called with FP set to NULL command line args are parse instead.
+ *
+ * Q: Should we allow the syntax
+ *     keyword = value
+ *    and accept for boolean options a value of 1/0, yes/no or true/false?
+ * Note: Abbreviation of options is here not allowed.
+ */
+int
+optfile_parse( FILE *fp, const char *filename, unsigned *lineno,
+              ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
+{
+    int state, i, c;
+    int idx=0;
+    char keyword[100];
+    char *buffer = NULL;
+    size_t buflen = 0;
+    int inverse=0;
+    int in_alias=0;
+
+    if( !fp ) /* same as arg_parse() in this case */
+       return arg_parse( arg, opts );
+
+    initialize( arg, filename, lineno );
+
+    /* find the next keyword */
+    state = i = 0;
+    for(;;) {
+       c=getc(fp);
+       if( c == '\n' || c== EOF ) {
+           if( c != EOF )
+               ++*lineno;
+           if( state == -1 )
+               break;
+           else if( state == 2 ) {
+               keyword[i] = 0;
+               for(i=0; opts[i].short_opt; i++ )
+                   if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
+                       break;
+               idx = i;
+               arg->r_opt = opts[idx].short_opt;
+               if( inverse ) /* this does not have an effect, hmmm */
+                   arg->r_opt = -arg->r_opt;
+               if( !opts[idx].short_opt )   /* unknown command/option */
+                   arg->r_opt = (opts[idx].flags & 256)? -7:-2;
+               else if( (opts[idx].flags & 8) ) /* no argument */
+                   arg->r_opt = -3;           /* error */
+               else                           /* no or optional argument */
+                   arg->r_type = 0;           /* okay */
+               break;
+           }
+           else if( state == 3 ) {            /* no argument found */
+               if( in_alias )
+                   arg->r_opt = -3;           /* error */
+               else if( !(opts[idx].flags & 7) ) /* does not take an arg */
+                   arg->r_type = 0;           /* okay */
+               else if( (opts[idx].flags & 8) )  /* no optional argument */
+                   arg->r_type = 0;           /* okay */
+               else                           /* no required argument */
+                   arg->r_opt = -3;           /* error */
+               break;
+           }
+           else if( state == 4 ) {     /* have an argument */
+               if( in_alias ) {
+                   if( !buffer )
+                       arg->r_opt = -6;
+                   else {
+                       char *p;
+
+                       buffer[i] = 0;
+                       p = strpbrk( buffer, " \t" );
+                       if( p ) {
+                           *p++ = 0;
+                           trim_spaces( p );
+                       }
+                       if( !p || !*p ) {
+                           jnlib_free( buffer );
+                           arg->r_opt = -10;
+                       }
+                       else {
+                           store_alias( arg, buffer, p );
+                       }
+                   }
+               }
+               else if( !(opts[idx].flags & 7) )  /* does not take an arg */
+                   arg->r_opt = -6;        /* error */
+               else {
+                   char *p;
+                   if( !buffer ) {
+                       keyword[i] = 0;
+                       buffer = jnlib_xstrdup(keyword);
+                   }
+                   else
+                       buffer[i] = 0;
+
+                   trim_spaces( buffer );
+                   p = buffer;
+                   if( *p == '"' ) { /* remove quotes */
+                       p++;
+                       if( *p && p[strlen(p)-1] == '"' )
+                           p[strlen(p)-1] = 0;
+                   }
+                   if( !set_opt_arg(arg, opts[idx].flags, p) )
+                       jnlib_free(buffer);
+               }
+               break;
+           }
+           else if( c == EOF ) {
+               if( ferror(fp) )
+                   arg->r_opt = -5;   /* read error */
+               else
+                   arg->r_opt = 0;    /* eof */
+               break;
+           }
+           state = 0;
+           i = 0;
+       }
+       else if( state == -1 )
+           ; /* skip */
+       else if( !state && isspace(c) )
+           ; /* skip leading white space */
+       else if( !state && c == '#' )
+           state = 1;  /* start of a comment */
+       else if( state == 1 )
+           ; /* skip comments */
+       else if( state == 2 && isspace(c) ) {
+           keyword[i] = 0;
+           for(i=0; opts[i].short_opt; i++ )
+               if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
+                   break;
+           idx = i;
+           arg->r_opt = opts[idx].short_opt;
+           if( !opts[idx].short_opt ) {
+               if( !strcmp( keyword, "alias" ) ) {
+                   in_alias = 1;
+                   state = 3;
+               }
+               else {
+                   arg->r_opt = (opts[idx].flags & 256)? -7:-2;
+                   state = -1;        /* skip rest of line and leave */
+               }
+           }
+           else
+               state = 3;
+       }
+       else if( state == 3 ) { /* skip leading spaces of the argument */
+           if( !isspace(c) ) {
+               i = 0;
+               keyword[i++] = c;
+               state = 4;
+           }
+       }
+       else if( state == 4 ) { /* collect the argument */
+           if( buffer ) {
+               if( i < buflen-1 )
+                   buffer[i++] = c;
+               else {
+                   buflen += 50;
+                   buffer = jnlib_xrealloc(buffer, buflen);
+                   buffer[i++] = c;
+               }
+           }
+           else if( i < DIM(keyword)-1 )
+               keyword[i++] = c;
+           else {
+               buflen = DIM(keyword)+50;
+               buffer = jnlib_xmalloc(buflen);
+               memcpy(buffer, keyword, i);
+               buffer[i++] = c;
+           }
+       }
+       else if( i >= DIM(keyword)-1 ) {
+           arg->r_opt = -4;   /* keyword to long */
+           state = -1;        /* skip rest of line and leave */
+       }
+       else {
+           keyword[i++] = c;
+           state = 2;
+       }
+    }
+
+    return arg->r_opt;
+}
+
+
+
+static int
+find_long_option( ARGPARSE_ARGS *arg,
+                 ARGPARSE_OPTS *opts, const char *keyword )
+{
+    int i;
+    size_t n;
+
+    /* Would be better if we can do a binary search, but it is not
+       possible to reorder our option table because we would mess
+       up our help strings - What we can do is: Build a nice option
+       lookup table wehn this function is first invoked */
+    if( !*keyword )
+       return -1;
+    for(i=0; opts[i].short_opt; i++ )
+       if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
+           return i;
+  #if 0
+    {
+       ALIAS_DEF a;
+       /* see whether it is an alias */
+       for( a = args->internal.aliases; a; a = a->next ) {
+           if( !strcmp( a->name, keyword) ) {
+               /* todo: must parse the alias here */
+               args->internal.cur_alias = a;
+               return -3; /* alias available */
+           }
+       }
+    }
+  #endif
+    /* not found, see whether it is an abbreviation */
+    /* aliases may not be abbreviated */
+    n = strlen( keyword );
+    for(i=0; opts[i].short_opt; i++ ) {
+       if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) {
+           int j;
+           for(j=i+1; opts[j].short_opt; j++ ) {
+               if( opts[j].long_opt
+                   && !strncmp( opts[j].long_opt, keyword, n ) )
+                   return -2;  /* abbreviation is ambiguous */
+           }
+           return i;
+       }
+    }
+    return -1;
+}
+
+int
+arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
+{
+    int idx;
+    int argc;
+    char **argv;
+    char *s, *s2;
+    int i;
+
+    initialize( arg, NULL, NULL );
+    argc = *arg->argc;
+    argv = *arg->argv;
+    idx = arg->internal.idx;
+
+    if( !idx && argc && !(arg->flags & (1<<4)) ) { /* skip the first entry */
+       argc--; argv++; idx++;
+    }
+
+  next_one:
+    if( !argc ) { /* no more args */
+       arg->r_opt = 0;
+       goto leave; /* ready */
+    }
+
+    s = *argv;
+    arg->internal.last = s;
+
+    if( arg->internal.stopped && (arg->flags & (1<<1)) ) {
+       arg->r_opt = -1;  /* not an option but a argument */
+       arg->r_type = 2;
+       arg->r.ret_str = s;
+       argc--; argv++; idx++; /* set to next one */
+    }
+    else if( arg->internal.stopped ) { /* ready */
+       arg->r_opt = 0;
+       goto leave;
+    }
+    else if( *s == '-' && s[1] == '-' ) { /* long option */
+       char *argpos;
+
+       arg->internal.inarg = 0;
+       if( !s[2] && !(arg->flags & (1<<3)) ) { /* stop option processing */
+           arg->internal.stopped = 1;
+           argc--; argv++; idx++;
+           goto next_one;
+       }
+
+       argpos = strchr( s+2, '=' );
+       if( argpos )
+           *argpos = 0;
+       i = find_long_option( arg, opts, s+2 );
+       if( argpos )
+           *argpos = '=';
+
+       if( i < 0 && !strcmp( "help", s+2) )
+           show_help(opts, arg->flags);
+       else if( i < 0 && !strcmp( "version", s+2) ) {
+           if( !(arg->flags & (1<<6)) ) {
+               show_version();
+               exit(0);
+           }
+       }
+       else if( i < 0 && !strcmp( "warranty", s+2) ) {
+           puts( strusage(16) );
+           exit(0);
+       }
+       else if( i < 0 && !strcmp( "dump-options", s+2) ) {
+           for(i=0; opts[i].short_opt; i++ ) {
+               if( opts[i].long_opt )
+                   printf( "--%s\n", opts[i].long_opt );
+           }
+           fputs("--dump-options\n--help\n--version\n--warranty\n", stdout );
+           exit(0);
+       }
+
+       if( i == -2 ) /* ambiguous option */
+           arg->r_opt = -8;
+       else if( i == -1 ) {
+           arg->r_opt = -2;
+           arg->r.ret_str = s+2;
+       }
+       else
+           arg->r_opt = opts[i].short_opt;
+       if( i < 0 )
+           ;
+       else if( (opts[i].flags & 7) ) {
+           if( argpos ) {
+               s2 = argpos+1;
+               if( !*s2 )
+                   s2 = NULL;
+           }
+           else
+               s2 = argv[1];
+           if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
+               arg->r_type = 0;               /* because it is optional */
+           }
+           else if( !s2 ) {
+               arg->r_opt = -3; /* missing argument */
+           }
+           else if( !argpos && *s2 == '-' && (opts[i].flags & 8) ) {
+               /* the argument is optional and the next seems to be
+                * an option. We do not check this possible option
+                * but assume no argument */
+               arg->r_type = 0;
+           }
+           else {
+               set_opt_arg(arg, opts[i].flags, s2);
+               if( !argpos ) {
+                   argc--; argv++; idx++; /* skip one */
+               }
+           }
+       }
+       else { /* does not take an argument */
+           if( argpos )
+               arg->r_type = -6; /* argument not expected */
+           else
+               arg->r_type = 0;
+       }
+       argc--; argv++; idx++; /* set to next one */
+    }
+    else if( (*s == '-' && s[1]) || arg->internal.inarg ) { /* short option */
+       int dash_kludge = 0;
+       i = 0;
+       if( !arg->internal.inarg ) {
+           arg->internal.inarg++;
+           if( arg->flags & (1<<5) ) {
+               for(i=0; opts[i].short_opt; i++ )
+                   if( opts[i].long_opt && !strcmp( opts[i].long_opt, s+1)) {
+                       dash_kludge=1;
+                       break;
+                   }
+           }
+       }
+       s += arg->internal.inarg;
+
+       if( !dash_kludge ) {
+           for(i=0; opts[i].short_opt; i++ )
+               if( opts[i].short_opt == *s )
+                   break;
+       }
+
+       if( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) )
+           show_help(opts, arg->flags);
+
+       arg->r_opt = opts[i].short_opt;
+       if( !opts[i].short_opt ) {
+           arg->r_opt = (opts[i].flags & 256)? -7:-2;
+           arg->internal.inarg++; /* point to the next arg */
+           arg->r.ret_str = s;
+       }
+       else if( (opts[i].flags & 7) ) {
+           if( s[1] && !dash_kludge ) {
+               s2 = s+1;
+               set_opt_arg(arg, opts[i].flags, s2);
+           }
+           else {
+               s2 = argv[1];
+               if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
+                   arg->r_type = 0;               /* because it is optional */
+               }
+               else if( !s2 ) {
+                   arg->r_opt = -3; /* missing argument */
+               }
+               else if( *s2 == '-' && s2[1] && (opts[i].flags & 8) ) {
+                   /* the argument is optional and the next seems to be
+                    * an option. We do not check this possible option
+                    * but assume no argument */
+                   arg->r_type = 0;
+               }
+               else {
+                   set_opt_arg(arg, opts[i].flags, s2);
+                   argc--; argv++; idx++; /* skip one */
+               }
+           }
+           s = "x"; /* so that !s[1] yields false */
+       }
+       else { /* does not take an argument */
+           arg->r_type = 0;
+           arg->internal.inarg++; /* point to the next arg */
+       }
+       if( !s[1] || dash_kludge ) { /* no more concatenated short options */
+           arg->internal.inarg = 0;
+           argc--; argv++; idx++;
+       }
+    }
+    else if( arg->flags & (1<<2) ) {
+       arg->r_opt = -1;  /* not an option but a argument */
+       arg->r_type = 2;
+       arg->r.ret_str = s;
+       argc--; argv++; idx++; /* set to next one */
+    }
+    else {
+       arg->internal.stopped = 1; /* stop option processing */
+       goto next_one;
+    }
+
+  leave:
+    *arg->argc = argc;
+    *arg->argv = argv;
+    arg->internal.idx = idx;
+    return arg->r_opt;
+}
+
+
+
+static int
+set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s)
+{
+    int base = (flags & 16)? 0 : 10;
+
+    switch( arg->r_type = (flags & 7) ) {
+      case 1: /* takes int argument */
+       arg->r.ret_int = (int)strtol(s,NULL,base);
+       return 0;
+      case 3: /* takes long argument   */
+       arg->r.ret_long= strtol(s,NULL,base);
+       return 0;
+      case 4: /* takes ulong argument  */
+       arg->r.ret_ulong= strtoul(s,NULL,base);
+       return 0;
+      case 2: /* takes string argument */
+      default:
+       arg->r.ret_str = s;
+       return 1;
+    }
+}
+
+
+static size_t
+long_opt_strlen( ARGPARSE_OPTS *o )
+{
+    size_t n = strlen(o->long_opt);
+
+    if( o->description && *o->description == '|' ) {
+       const char *s;
+
+       s=o->description+1;
+       if( *s != '=' )
+           n++;
+       for(; *s && *s != '|'; s++ )
+           n++;
+    }
+    return n;
+}
+
+/****************
+ * Print formatted help. The description string has some special
+ * meanings:
+ *  - A description string which is "@" suppresses help output for
+ *    this option
+ *  - a description,ine which starts with a '@' and is followed by
+ *    any other characters is printed as is; this may be used for examples
+ *    ans such.
+ *  - A description which starts with a '|' outputs the string between this
+ *    bar and the next one as arguments of the long option.
+ */
+static void
+show_help( ARGPARSE_OPTS *opts, unsigned flags )
+{
+    const char *s;
+
+    show_version();
+    putchar('\n');
+    s = strusage(41);
+    puts(s);
+    if( opts[0].description ) { /* auto format the option description */
+       int i,j, indent;
+       /* get max. length of long options */
+       for(i=indent=0; opts[i].short_opt; i++ ) {
+           if( opts[i].long_opt )
+               if( !opts[i].description || *opts[i].description != '@' )
+                   if( (j=long_opt_strlen(opts+i)) > indent && j < 35 )
+                        indent = j;
+       }
+       /* example: " -v, --verbose   Viele Sachen ausgeben" */
+       indent += 10;
+       if( *opts[0].description != '@' )
+           puts("Options:");
+       for(i=0; opts[i].short_opt; i++ ) {
+           s = _( opts[i].description );
+           if( s && *s== '@' && !s[1] ) /* hide this line */
+               continue;
+           if( s && *s == '@' ) { /* unindented comment only line */
+               for(s++; *s; s++ ) {
+                   if( *s == '\n' ) {
+                       if( s[1] )
+                           putchar('\n');
+                   }
+                   else
+                       putchar(*s);
+               }
+               putchar('\n');
+               continue;
+           }
+
+           j = 3;
+           if( opts[i].short_opt < 256 ) {
+               printf(" -%c", opts[i].short_opt );
+               if( !opts[i].long_opt ) {
+                   if(s && *s == '|' ) {
+                       putchar(' '); j++;
+                       for(s++ ; *s && *s != '|'; s++, j++ )
+                           putchar(*s);
+                       if( *s )
+                           s++;
+                   }
+               }
+           }
+           else
+               fputs("   ", stdout);
+           if( opts[i].long_opt ) {
+               j += printf("%c --%s", opts[i].short_opt < 256?',':' ',
+                                      opts[i].long_opt );
+               if(s && *s == '|' ) {
+                   if( *++s != '=' ) {
+                       putchar(' ');
+                       j++;
+                   }
+                   for( ; *s && *s != '|'; s++, j++ )
+                       putchar(*s);
+                   if( *s )
+                       s++;
+               }
+               fputs("   ", stdout);
+               j += 3;
+           }
+           for(;j < indent; j++ )
+               putchar(' ');
+           if( s ) {
+               if( *s && j > indent ) {
+                   putchar('\n');
+                   for(j=0;j < indent; j++ )
+                       putchar(' ');
+               }
+               for(; *s; s++ ) {
+                   if( *s == '\n' ) {
+                       if( s[1] ) {
+                           putchar('\n');
+                           for(j=0;j < indent; j++ )
+                               putchar(' ');
+                       }
+                   }
+                   else
+                       putchar(*s);
+               }
+           }
+           putchar('\n');
+       }
+       if( flags & 32 )
+           puts("\n(A single dash may be used instead of the double ones)");
+    }
+    if( (s=strusage(19)) ) {  /* bug reports to ... */
+       putchar('\n');
+       fputs(s, stdout);
+    }
+    fflush(stdout);
+    exit(0);
+}
+
+static void
+show_version()
+{
+    const char *s;
+    int i;
+    /* version line */
+    fputs(strusage(11), stdout);
+    if( (s=strusage(12)) )
+       printf(" (%s)", s );
+    printf(" %s\n", strusage(13) );
+    /* additional version lines */
+    for(i=20; i < 30; i++ )
+       if( (s=strusage(i)) )
+           printf("%s\n", s );
+    /* copyright string */
+    if( (s=strusage(14)) )
+       printf("%s\n", s );
+    /* copying conditions */
+    if( (s=strusage(15)) )
+       fputs(s, stdout);
+    /* thanks */
+    if( (s=strusage(18)) )
+       fputs(s, stdout);
+    /* additional program info */
+    for(i=30; i < 40; i++ )
+       if( (s=strusage(i)) )
+           fputs( (const byte*)s, stdout);
+    fflush(stdout);
+}
+
+
+void
+usage( int level )
+{
+    if( !level ) {
+       fprintf(stderr,"%s %s; %s\n", strusage(11), strusage(13),
+                                                    strusage(14) );
+       fflush(stderr);
+    }
+    else if( level == 1 ) {
+       fputs(strusage(40),stderr);
+       exit(2);
+    }
+    else if( level == 2 ) {
+       puts(strusage(41));
+       exit(0);
+    }
+}
+
+/* Level
+ *     0: Copyright String auf stderr ausgeben
+ *     1: Kurzusage auf stderr ausgeben und beenden
+ *     2: Langusage auf stdout ausgeben und beenden
+ *    11: name of program
+ *    12: optional name of package which includes this program.
+ *    13: version  string
+ *    14: copyright string
+ *    15: Short copying conditions (with LFs)
+ *    16: Long copying conditions (with LFs)
+ *    17: Optional printable OS name
+ *    18: Optional thanks list  (with LFs)
+ *    19: Bug report info
+ *20..29: Additional lib version strings.
+ *30..39: Additional program info (with LFs)
+ *    40: short usage note (with LF)
+ *    41: long usage note (with LF)
+ */
+const char *
+strusage( int level )
+{
+    const char *p = strusage_handler? strusage_handler(level) : NULL;
+
+    if( p )
+       return p;
+
+    switch( level ) {
+      case 11: p = "foo"; break;
+      case 13: p = "0.0"; break;
+      case 14: p = "Copyright (C) 2001 Free Software Foundation, Inc."; break;
+      case 15: p =
+"This program comes with ABSOLUTELY NO WARRANTY.\n"
+"This is free software, and you are welcome to redistribute it\n"
+"under certain conditions. See the file COPYING for details.\n"; break;
+      case 16: p =
+"This is free software; you can redistribute it and/or modify\n"
+"it under the terms of the GNU General Public License as published by\n"
+"the Free Software Foundation; either version 2 of the License, or\n"
+"(at your option) any later version.\n\n"
+"It is distributed in the hope that it will be useful,\n"
+"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
+"GNU General Public License for more details.\n\n"
+"You should have received a copy of the GNU General Public License\n"
+"along with this program; if not, write to the Free Software\n"
+"Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n";
+       break;
+      case 40: /* short and long usage */
+      case 41: p = ""; break;
+    }
+
+    return p;
+}
+
+void
+set_strusage( const char *(*f)( int ) )
+{
+    strusage_handler = f;
+}
+
+
+#ifdef TEST
+static struct {
+    int verbose;
+    int debug;
+    char *outfile;
+    char *crf;
+    int myopt;
+    int echo;
+    int a_long_one;
+}opt;
+
+int
+main(int argc, char **argv)
+{
+    ARGPARSE_OPTS opts[] = {
+    { 'v', "verbose",   0 , "Laut sein"},
+    { 'e', "echo"   ,   0 , "Zeile ausgeben, damit wir sehen, was wir einegegeben haben"},
+    { 'd', "debug",     0 , "Debug\nfalls mal etasws\nSchief geht"},
+    { 'o', "output",    2   },
+    { 'c', "cross-ref", 2|8, "cross-reference erzeugen\n" },
+    { 'm', "my-option", 1|8 },
+    { 500, "a-long-option", 0 },
+    {0} };
+    ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
+    int i;
+
+    while( ArgParse( &pargs, opts) ) {
+       switch( pargs.r_opt ) {
+         case -1 : printf( "arg=`%s'\n", pargs.r.ret_str); break;
+         case 'v': opt.verbose++; break;
+         case 'e': opt.echo++; break;
+         case 'd': opt.debug++; break;
+         case 'o': opt.outfile = pargs.r.ret_str; break;
+         case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
+         case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
+         case 500: opt.a_long_one++;  break;
+         default : pargs.err = 1; break; /* force warning output */
+       }
+    }
+    for(i=0; i < argc; i++ )
+       printf("%3d -> (%s)\n", i, argv[i] );
+    puts("Options:");
+    if( opt.verbose )
+       printf("  verbose=%d\n", opt.verbose );
+    if( opt.debug )
+       printf("  debug=%d\n", opt.debug );
+    if( opt.outfile )
+       printf("  outfile=`%s'\n", opt.outfile );
+    if( opt.crf )
+       printf("  crffile=`%s'\n", opt.crf );
+    if( opt.myopt )
+       printf("  myopt=%d\n", opt.myopt );
+    if( opt.a_long_one )
+       printf("  a-long-one=%d\n", opt.a_long_one );
+    if( opt.echo       )
+       printf("  echo=%d\n", opt.echo );
+    return 0;
+}
+#endif
+
+/**** bottom of file ****/
diff --git a/jnlib/argparse.h b/jnlib/argparse.h
new file mode 100644 (file)
index 0000000..e8922fa
--- /dev/null
@@ -0,0 +1,67 @@
+/* argparse.h
+ *     Copyright (C) 1998,1999,2000,2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef LIBJNLIB_ARGPARSE_H
+#define LIBJNLIB_ARGPARSE_H
+
+#include <stdio.h>
+#include "types.h"
+
+typedef struct {
+     int  *argc;           /* pointer to argc (value subject to change) */
+     char ***argv;         /* pointer to argv (value subject to change) */
+     unsigned flags;       /* Global flags (DO NOT CHANGE) */
+     int err;              /* print error about last option */
+                           /* 1 = warning, 2 = abort */
+     int r_opt;            /* return option */
+     int r_type;           /* type of return value (0 = no argument found)*/
+     union {
+        int   ret_int;
+        long  ret_long;
+        unsigned long ret_ulong;
+        char *ret_str;
+     } r;                  /* Return values */
+     struct {
+        int idx;
+        int inarg;
+        int stopped;
+        const char *last;
+        void *aliases;
+        const void *cur_alias;
+     } internal;           /* DO NOT CHANGE */
+} ARGPARSE_ARGS;
+
+typedef struct {
+    int        short_opt;
+    const char *long_opt;
+    unsigned flags;
+    const char *description; /* optional option description */
+} ARGPARSE_OPTS;
+
+
+
+int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts);
+int optfile_parse( FILE *fp, const char *filename, unsigned *lineno,
+                  ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts);
+void usage( int level );
+const char *strusage( int level );
+void set_strusage( const char *(*f)( int ) );
+
+#endif /*LIBJNLIB_ARGPARSE_H*/
diff --git a/jnlib/dotlock.c b/jnlib/dotlock.c
new file mode 100644 (file)
index 0000000..772c770
--- /dev/null
@@ -0,0 +1,346 @@
+/* dotlock.c - dotfile locking
+ *     Copyright (C) 1998,2000,2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#ifndef  HAVE_DOSISH_SYSTEM
+#include <sys/utsname.h>
+#endif
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include "libjnlib-config.h"
+#include "dotlock.h"
+
+struct dotlock_handle {
+    struct dotlock_handle *next;
+    char *tname;    /* name of lockfile template */
+    char *lockname; /* name of the real lockfile */
+    int locked;     /* lock status */
+};
+
+
+static DOTLOCK all_lockfiles;
+
+static int read_lockfile( const char *name );
+static void remove_lockfiles(void);
+
+/****************
+ * Create a lockfile with the given name and return an object of
+ * type DOTLOCK which may be used later to actually do the lock.
+ * A cleanup routine gets installed to cleanup left over locks
+ * or other files used together with the lockmechanism.
+ * Althoug the function is called dotlock, this does not necessarily
+ * mean that real lockfiles are used - the function may decide to
+ * use fcntl locking.  Calling the function with NULL only install
+ * the atexit handler and maybe used to assure that the cleanup
+ * is called after all other atexit handlers.
+ *
+ * Notes: This function creates a lock file in the same directory
+ *       as file_to_lock with the name "file_to_lock.lock"
+ *       A temporary file ".#lk.<hostname>.pid[.threadid] is used.
+ *       This function does nothing for Windoze.
+ */
+DOTLOCK
+create_dotlock( const char *file_to_lock )
+{
+    static int initialized;
+    DOTLOCK h;
+    int  fd = -1;
+    char pidstr[16];
+  #ifndef  HAVE_DOSISH_SYSTEM
+    struct utsname utsbuf;
+  #endif
+    const char *nodename;
+    const char *dirpart;
+    int dirpartlen;
+
+    if( !initialized ) {
+       atexit( remove_lockfiles );
+       initialized = 1;
+    }
+    if( !file_to_lock )
+       return NULL;
+
+    h = jnlib_xcalloc( 1, sizeof *h );
+#ifndef HAVE_DOSISH_SYSTEM
+    sprintf( pidstr, "%10d\n", (int)getpid() );
+    /* fixme: add the hostname to the second line (FQDN or IP addr?) */
+
+    /* create a temporary file */
+    if( uname( &utsbuf ) )
+       nodename = "unknown";
+    else
+       nodename = utsbuf.nodename;
+
+    if( !(dirpart = strrchr( file_to_lock, '/' )) ) {
+       dirpart = ".";
+       dirpartlen = 1;
+    }
+    else {
+       dirpartlen = dirpart - file_to_lock;
+       dirpart = file_to_lock;
+    }
+
+  #ifdef _REENTRANT
+    /* fixme: aquire mutex on all_lockfiles */
+  #endif
+    h->next = all_lockfiles;
+    all_lockfiles = h;
+
+    h->tname = jnlib_xmalloc( dirpartlen + 6+30+ strlen(nodename) + 11 );
+    sprintf( h->tname, "%.*s/.#lk%p.%s.%d",
+            dirpartlen, dirpart, h, nodename, (int)getpid() );
+
+    do {
+       errno = 0;
+       fd = open( h->tname, O_WRONLY|O_CREAT|O_EXCL,
+                         S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR );
+    } while( fd == -1 && errno == EINTR );
+    if( fd == -1 ) {
+       all_lockfiles = h->next;
+       log_error( "failed to create temporary file `%s': %s\n",
+                                           h->tname, strerror(errno));
+       jnlib_free(h->tname);
+       jnlib_free(h);
+       return NULL;
+    }
+    if( write(fd, pidstr, 11 ) != 11 ) {
+       all_lockfiles = h->next;
+      #ifdef _REENTRANT
+       /* release mutex */
+      #endif
+       log_fatal( "error writing to `%s': %s\n", h->tname, strerror(errno) );
+       close(fd);
+       unlink(h->tname);
+       jnlib_free(h->tname);
+       jnlib_free(h);
+       return NULL;
+    }
+    if( close(fd) ) {
+       all_lockfiles = h->next;
+      #ifdef _REENTRANT
+       /* release mutex */
+      #endif
+       log_error( "error closing `%s': %s\n", h->tname, strerror(errno));
+       unlink(h->tname);
+       jnlib_free(h->tname);
+       jnlib_free(h);
+       return NULL;
+    }
+
+  #ifdef _REENTRANT
+    /* release mutex */
+  #endif
+#endif /* !HAVE_DOSISH_SYSTEM */
+    h->lockname = jnlib_xmalloc( strlen(file_to_lock) + 6 );
+    strcpy(stpcpy(h->lockname, file_to_lock), ".lock");
+    return h;
+}
+
+static int
+maybe_deadlock( DOTLOCK h )
+{
+    DOTLOCK r;
+
+    for( r=all_lockfiles; r; r = r->next ) {
+       if( r != h && r->locked )
+           return 1;
+    }
+    return 0;
+}
+
+/****************
+ * Do a lock on H. A TIMEOUT of 0 returns immediately,
+ * -1 waits forever (hopefully not), other
+ * values are timeouts in milliseconds.
+ * Returns: 0 on success
+ */
+int
+make_dotlock( DOTLOCK h, long timeout )
+{
+#ifdef HAVE_DOSISH_SYSTEM
+    return 0;
+#else
+    int  pid;
+    const char *maybe_dead="";
+    int backoff=0;
+
+    if( h->locked ) {
+       log_debug("oops, `%s' is already locked\n", h->lockname );
+       return 0;
+    }
+
+    for(;;) {
+       if( !link(h->tname, h->lockname) ) {
+           /* fixme: better use stat to check the link count */
+           h->locked = 1;
+           return 0; /* okay */
+       }
+       if( errno != EEXIST ) {
+           log_error( "lock not made: link() failed: %s\n", strerror(errno) );
+           return -1;
+       }
+       if( (pid = read_lockfile(h->lockname)) == -1 ) {
+           if( errno != ENOENT ) {
+               log_info("cannot read lockfile\n");
+               return -1;
+           }
+           log_info( "lockfile disappeared\n");
+           continue;
+       }
+       else if( pid == getpid() ) {
+           log_info( "Oops: lock already hold by us\n");
+           h->locked = 1;
+           return 0; /* okay */
+       }
+       else if( kill(pid, 0) && errno == ESRCH ) {
+           maybe_dead = " - probably dead";
+        #if 0 /* we should not do this without checking the permissions */
+              /* and the hostname */
+           log_info( "removing stale lockfile (created by %d)", pid );
+        #endif
+       }
+       if( timeout == -1 ) {
+           struct timeval tv;
+           log_info( "waiting for lock (hold by %d%s) %s...\n",
+                     pid, maybe_dead, maybe_deadlock(h)? "(deadlock?) ":"");
+
+
+           /* can't use sleep, cause signals may be blocked */
+           tv.tv_sec = 1 + backoff;
+           tv.tv_usec = 0;
+           select(0, NULL, NULL, NULL, &tv);
+           if( backoff < 10 )
+               backoff++ ;
+       }
+       else
+           return -1;
+    }
+    /*not reached */
+#endif /* !HAVE_DOSISH_SYSTEM */
+}
+
+
+/****************
+ * release a lock
+ * Returns: 0 := success
+ */
+int
+release_dotlock( DOTLOCK h )
+{
+#ifdef HAVE_DOSISH_SYSTEM
+    return 0;
+#else
+    int pid;
+
+    if( !h->locked ) {
+       log_debug("oops, `%s' is not locked\n", h->lockname );
+       return 0;
+    }
+
+    pid = read_lockfile( h->lockname );
+    if( pid == -1 ) {
+       log_error( "release_dotlock: lockfile error\n");
+       return -1;
+    }
+    if( pid != getpid() ) {
+       log_error( "release_dotlock: not our lock (pid=%d)\n", pid);
+       return -1;
+    }
+    if( unlink( h->lockname ) ) {
+       log_error( "release_dotlock: error removing lockfile `%s'",
+                                                       h->lockname);
+       return -1;
+    }
+    /* fixme: check that the link count is now 1 */
+    h->locked = 0;
+    return 0;
+#endif /* !HAVE_DOSISH_SYSTEM */
+}
+
+
+/****************
+ * Read the lock file and return the pid, returns -1 on error.
+ */
+static int
+read_lockfile( const char *name )
+{
+  #ifdef HAVE_DOSISH_SYSTEM
+    return 0;
+  #else
+    int fd, pid;
+    char pidstr[16];
+
+    if( (fd = open(name, O_RDONLY)) == -1 ) {
+       int e = errno;
+       log_debug("error opening lockfile `%s': %s\n", name, strerror(errno) );
+       errno = e;
+       return -1;
+    }
+    if( read(fd, pidstr, 10 ) != 10 ) {  /* Read 10 digits w/o newline */
+       log_debug("error reading lockfile `%s'", name );
+       close(fd);
+       errno = 0;
+       return -1;
+    }
+    pidstr[10] = 0;  /* terminate pid string */
+    close(fd);
+    pid = atoi(pidstr);
+    if( !pid || pid == -1 ) {
+       log_error("invalid pid %d in lockfile `%s'", pid, name );
+       errno = 0;
+       return -1;
+    }
+    return pid;
+  #endif
+}
+
+
+static void
+remove_lockfiles()
+{
+  #ifndef HAVE_DOSISH_SYSTEM
+    DOTLOCK h, h2;
+
+    h = all_lockfiles;
+    all_lockfiles = NULL;
+
+    while( h ) {
+       h2 = h->next;
+       if( h->locked )
+           unlink( h->lockname );
+       unlink(h->tname);
+       jnlib_free(h->tname);
+       jnlib_free(h->lockname);
+       jnlib_free(h);
+       h = h2;
+    }
+  #endif
+}
+
diff --git a/jnlib/dotlock.h b/jnlib/dotlock.h
new file mode 100644 (file)
index 0000000..7d45c82
--- /dev/null
@@ -0,0 +1,32 @@
+/* dotlock.h
+ *     Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef LIBJNLIB_DOTLOCK_H
+#define LIBJNLIB_DOTLOCK_H
+
+struct dotlock_handle;
+typedef struct dotlock_handle *DOTLOCK;
+
+DOTLOCK create_dotlock( const char *file_to_lock );
+int make_dotlock( DOTLOCK h, long timeout );
+int release_dotlock( DOTLOCK h );
+
+
+#endif /*LIBJNLIB_DOTLOCK_H*/
diff --git a/jnlib/libjnlib-config.h b/jnlib/libjnlib-config.h
new file mode 100644 (file)
index 0000000..ec31d35
--- /dev/null
@@ -0,0 +1,75 @@
+/* libjnlib-config.h - local configuration of the jnlib functions
+ *     Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+/****************
+ * This header is to be included only by the files in this directory
+ * it should not be used by other modules.
+ */
+
+#ifndef LIBJNLIB_CONFIG_H
+#define LIBJNLIB_CONFIG_H
+
+#include <gcrypt.h> /* gcry_malloc & Cie. */
+#include "logging.h"
+
+#ifdef USE_SIMPLE_GETTEXT
+  int set_gettext_file( const char *filename );
+  const char *gettext( const char *msgid );
+
+  #define _(a) gettext (a)
+  #define N_(a) (a)
+
+#else
+#ifdef HAVE_LOCALE_H
+  #include <locale.h>
+#endif
+
+#ifdef ENABLE_NLS
+  #include <libintl.h>
+  #define _(a) gettext (a)
+  #ifdef gettext_noop
+    #define N_(a) gettext_noop (a)
+  #else
+    #define N_(a) (a)
+  #endif
+#else
+  #define _(a) (a)
+  #define N_(a) (a)
+#endif
+#endif /* !USE_SIMPLE_GETTEXT */
+
+
+#define jnlib_xmalloc(a)    gcry_xmalloc( (a) )
+#define jnlib_xcalloc(a,b)  gcry_xcalloc( (a), (b) )
+#define jnlib_xrealloc(a,n) gcry_xrealloc( (a), (n) )
+#define jnlib_xstrdup(a)    gcry_xstrdup( (a) )
+#define jnlib_free(a)      gcry_free( (a) )
+
+#define jnlib_log_debug    log_debug
+#define jnlib_log_info    log_info
+#define jnlib_log_error    log_error
+#define jnlib_log_fatal    log_fatal
+#define jnlib_log_bug     log_bug
+
+
+#endif /*LIBJNUTIL_CONFIG_H*/
+
+
+
diff --git a/jnlib/logging.c b/jnlib/logging.c
new file mode 100644 (file)
index 0000000..2e0d53a
--- /dev/null
@@ -0,0 +1,305 @@
+/* logging.c - useful logging functions
+ *     Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+
+/* This file should replace logger.c in the future - for now it is not
+ * used by GnuPG but by GPA.
+ * It is a quite simple implemenation but sufficient for most purposes.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef __MINGW32__
+  #include <io.h>
+#endif
+
+#define JNLIB_NEED_LOG_LOGV 1
+#include "libjnlib-config.h"
+#include "logging.h"
+
+
+static FILE *logstream;
+static char prefix_buffer[80];
+static int with_time;
+static int with_prefix;
+static int with_pid;
+
+static int missing_lf;
+static int errorcount;
+
+#if 0
+static void
+write2stderr( const char *s )
+{
+    write( 2, s, strlen(s) );
+}
+
+
+static void
+do_die(int rc, const char *text )
+{
+    write2stderr("\nFatal error: ");
+    write2stderr(text);
+    write2stderr("\n");
+    abort();
+}
+#endif
+
+int
+log_get_errorcount (int clear)
+{
+    int n = errorcount;
+    if( clear )
+       errorcount = 0;
+    return n;
+}
+
+void
+log_set_file( const char *name )
+{
+    FILE *fp = (name && strcmp(name,"-"))? fopen(name, "a") : stderr;
+    if( !fp ) {
+       fprintf(stderr, "failed to open log file `%s': %s\n",
+                                               name, strerror(errno));
+       return;
+    }
+    setvbuf( fp, NULL, _IOLBF, 0 );
+
+    if( logstream && logstream != stderr )
+       fclose( logstream );
+    logstream = fp;
+    missing_lf = 0;
+}
+
+
+void
+log_set_prefix (const char *text, unsigned int flags)
+{
+  if (text)
+    {
+      strncpy (prefix_buffer, text, sizeof (prefix_buffer)-1);
+      prefix_buffer[sizeof (prefix_buffer)-1] = 0;
+    }
+  
+  with_prefix = (flags & 1);
+  with_time = (flags & 2);
+  with_pid  = (flags & 4);
+}
+
+int
+log_get_fd()
+{
+    return fileno(logstream?logstream:stderr);
+}
+
+FILE *
+log_get_stream ()
+{
+    return logstream?logstream:stderr;
+}
+
+
+static void
+do_logv( int level, const char *fmt, va_list arg_ptr )
+{
+  if (!logstream)
+    logstream = stderr;
+
+  if (missing_lf && level != JNLIB_LOG_CONT)
+    putc('\n', logstream );
+  missing_lf = 0;
+
+  if (level != JNLIB_LOG_CONT)
+    { /* Note this does not work for multiple line logging as we would
+       * need to print to a buffer first */
+      if (with_time)
+        {
+          struct tm *tp;
+          time_t atime = time (NULL);
+          
+          tp = localtime (&atime);
+          fprintf (logstream, "%04d-%02d-%02d %02d:%02d:%02d ",
+                   1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
+                   tp->tm_hour, tp->tm_min, tp->tm_sec );
+        }
+      if (with_prefix)
+        fputs (prefix_buffer, logstream);
+      if (with_pid)
+        fprintf (logstream, "[%u]", (unsigned int)getpid ());
+      if (!with_time)
+        putc (':', logstream);
+      putc (' ', logstream);
+    }
+
+  switch (level)
+    {
+    case JNLIB_LOG_BEGIN: break;
+    case JNLIB_LOG_CONT: break;
+    case JNLIB_LOG_INFO: break;
+    case JNLIB_LOG_WARN: break;
+    case JNLIB_LOG_ERROR: break;
+    case JNLIB_LOG_FATAL: fputs("Fatal: ",logstream ); break;
+    case JNLIB_LOG_BUG: fputs("Ohhhh jeeee: ", logstream); break;
+    case JNLIB_LOG_DEBUG: fputs("DBG: ", logstream ); break;
+    default: fprintf(logstream,"[Unknown log level %d]: ", level ); break;
+    }
+
+  if (fmt)
+    {
+      vfprintf(logstream,fmt,arg_ptr) ;
+      if (*fmt && fmt[strlen(fmt)-1] != '\n')
+        missing_lf = 1;
+    }
+
+  if (level == JNLIB_LOG_FATAL)
+    exit(2);
+  if (level == JNLIB_LOG_BUG)
+    abort();
+}
+
+static void
+do_log( int level, const char *fmt, ... )
+{
+    va_list arg_ptr ;
+
+    va_start( arg_ptr, fmt ) ;
+    do_logv( level, fmt, arg_ptr );
+    va_end(arg_ptr);
+}
+
+
+void
+log_logv (int level, const char *fmt, va_list arg_ptr)
+{
+  do_logv (level, fmt, arg_ptr);
+}
+
+void
+log_info( const char *fmt, ... )
+{
+    va_list arg_ptr ;
+
+    va_start( arg_ptr, fmt ) ;
+    do_logv( JNLIB_LOG_INFO, fmt, arg_ptr );
+    va_end(arg_ptr);
+}
+
+void
+log_error( const char *fmt, ... )
+{
+    va_list arg_ptr ;
+
+    va_start( arg_ptr, fmt ) ;
+    do_logv( JNLIB_LOG_ERROR, fmt, arg_ptr );
+    va_end(arg_ptr);
+    /* protect against counter overflow */
+    if( errorcount < 30000 )
+       errorcount++;
+}
+
+
+void
+log_fatal( const char *fmt, ... )
+{
+    va_list arg_ptr ;
+
+    va_start( arg_ptr, fmt ) ;
+    do_logv( JNLIB_LOG_FATAL, fmt, arg_ptr );
+    va_end(arg_ptr);
+    abort(); /* never called, bugs it makes the compiler happy */
+}
+
+void
+log_bug( const char *fmt, ... )
+{
+    va_list arg_ptr ;
+
+    va_start( arg_ptr, fmt ) ;
+    do_logv( JNLIB_LOG_BUG, fmt, arg_ptr );
+    va_end(arg_ptr);
+    abort(); /* never called, but it makes the compiler happy */
+}
+
+void
+log_debug( const char *fmt, ... )
+{
+    va_list arg_ptr ;
+
+    va_start( arg_ptr, fmt ) ;
+    do_logv( JNLIB_LOG_DEBUG, fmt, arg_ptr );
+    va_end(arg_ptr);
+}
+
+
+void
+log_printf (const char *fmt, ...)
+{
+  va_list arg_ptr;
+
+  va_start (arg_ptr, fmt);
+  do_logv (fmt ? JNLIB_LOG_CONT : JNLIB_LOG_BEGIN, fmt, arg_ptr);
+  va_end (arg_ptr);
+}
+
+/* Print a hexdump of BUFFER.  With TEXT of NULL print just the raw
+   dump, with TEXT just an empty string, print a trailing linefeed,
+   otherwise print an entire debug line. */
+void
+log_printhex (const char *text, const void *buffer, size_t length)
+{
+  if (text && *text)
+    log_debug ("%s ", text);
+  if (length)
+    {
+      const unsigned char *p = buffer;
+      log_printf ("%02X", *p);
+      for (length--, p++; length--; p++)
+        log_printf (" %02X", *p);
+    }
+  if (text)
+    log_printf ("\n");
+}
+
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
+void
+bug_at( const char *file, int line, const char *func )
+{
+    do_log( JNLIB_LOG_BUG,
+            ("... this is a bug (%s:%d:%s)\n"), file, line, func );
+    abort(); /* never called, but it makes the compiler happy */
+}
+#else
+void
+bug_at( const char *file, int line )
+{
+    do_log( JNLIB_LOG_BUG,
+            _("you found a bug ... (%s:%d)\n"), file, line);
+    abort(); /* never called, but it makes the compiler happy */
+}
+#endif
+
diff --git a/jnlib/logging.h b/jnlib/logging.h
new file mode 100644 (file)
index 0000000..7b7b8c8
--- /dev/null
@@ -0,0 +1,74 @@
+/* logging.h
+ *     Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef LIBJNLIB_LOGGING_H
+#define LIBJNLIB_LOGGING_H
+
+#include <stdio.h>
+#include "mischelp.h"
+
+
+int  log_get_errorcount (int clear);
+void log_set_file( const char *name );
+void log_set_prefix (const char *text, unsigned int flags);
+int  log_get_fd(void);
+FILE *log_get_stream (void);
+
+#ifdef JNLIB_GCC_M_FUNCTION
+  void bug_at( const char *file, int line, const char *func ) JNLIB_GCC_A_NR;
+# define BUG() bug_at( __FILE__ , __LINE__, __FUNCTION__ )
+#else
+  void bug_at( const char *file, int line );
+# define BUG() bug_at( __FILE__ , __LINE__ )
+#endif
+
+/* To avoid mandatory inclusion of stdarg and other stuff, do it only
+   if explicitly requested to do so. */
+#ifdef JNLIB_NEED_LOG_LOGV
+#include <stdarg.h>
+enum jnlib_log_levels {
+    JNLIB_LOG_BEGIN,
+    JNLIB_LOG_CONT,
+    JNLIB_LOG_INFO,
+    JNLIB_LOG_WARN,
+    JNLIB_LOG_ERROR,
+    JNLIB_LOG_FATAL,
+    JNLIB_LOG_BUG,
+    JNLIB_LOG_DEBUG
+};
+void log_logv (int level, const char *fmt, va_list arg_ptr);
+#endif /*JNLIB_NEED_LOG_LOGV*/
+
+
+void log_bug( const char *fmt, ... )   JNLIB_GCC_A_NR_PRINTF(1,2);
+void log_fatal( const char *fmt, ... ) JNLIB_GCC_A_NR_PRINTF(1,2);
+void log_error( const char *fmt, ... ) JNLIB_GCC_A_PRINTF(1,2);
+void log_info( const char *fmt, ... )  JNLIB_GCC_A_PRINTF(1,2);
+void log_debug( const char *fmt, ... ) JNLIB_GCC_A_PRINTF(1,2);
+void log_printf( const char *fmt, ... ) JNLIB_GCC_A_PRINTF(1,2);
+void log_printhex (const char *text, const void *buffer, size_t length);
+
+
+#endif /*LIBJNLIB_LOGGING_H*/
+
+
+
+
+
diff --git a/jnlib/mischelp.h b/jnlib/mischelp.h
new file mode 100644 (file)
index 0000000..58c9250
--- /dev/null
@@ -0,0 +1,43 @@
+/* mischelp.h
+ *     Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef LIBJNLIB_MISCHELP_H
+#define LIBJNLIB_MISCHHELP_H
+
+
+#define DIM(v)              (sizeof(v)/sizeof((v)[0]))
+#define DIMof(type,member)   DIM(((type *)0)->member)
+
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
+# define JNLIB_GCC_M_FUNCTION 1
+# define JNLIB_GCC_A_NR             __attribute__ ((noreturn))
+# define JNLIB_GCC_A_PRINTF( f, a )  __attribute__ ((format (printf,f,a)))
+# define JNLIB_GCC_A_NR_PRINTF( f, a ) \
+                           __attribute__ ((noreturn, format (printf,f,a)))
+#else
+# define JNLIB_GCC_A_NR
+# define JNLIB_GCC_A_PRINTF( f, a )
+# define JNLIB_GCC_A_NR_PRINTF( f, a )
+#endif
+
+
+
+#endif /*LIBJNLIB_MISCHELP_H*/
diff --git a/jnlib/stringhelp.c b/jnlib/stringhelp.c
new file mode 100644 (file)
index 0000000..0d3035e
--- /dev/null
@@ -0,0 +1,396 @@
+/* stringhelp.c -  standard string helper functions
+ *     Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#include "libjnlib-config.h"
+#include "stringhelp.h"
+
+
+/****************
+ * look for the substring SUB in buffer and return a pointer to that
+ * substring in BUF or NULL if not found.
+ * Comparison is case-insensitive.
+ */
+const char *
+memistr( const char *buf, size_t buflen, const char *sub )
+{
+    const byte *t, *s ;
+    size_t n;
+
+    for( t=buf, n=buflen, s=sub ; n ; t++, n-- )
+       if( toupper(*t) == toupper(*s) ) {
+           for( buf=t++, buflen = n--, s++;
+                n && toupper(*t) == toupper(*s); t++, s++, n-- )
+               ;
+           if( !*s )
+               return buf;
+           t = buf; n = buflen; s = sub ;
+       }
+
+    return NULL ;
+}
+
+/****************
+ * Wie strncpy(), aber es werden maximal n-1 zeichen kopiert und ein
+ * '\0' angehängt. Ist n = 0, so geschieht nichts, ist Destination
+ * gleich NULL, so wird via jnlib_xmalloc Speicher besorgt, ist dann nicht
+ * genügend Speicher vorhanden, so bricht die funktion ab.
+ */
+char *
+mem2str( char *dest , const void *src , size_t n )
+{
+    char *d;
+    const char *s;
+
+    if( n ) {
+       if( !dest )
+           dest = jnlib_xmalloc( n ) ;
+       d = dest;
+       s = src ;
+       for(n--; n && *s; n-- )
+           *d++ = *s++;
+       *d = '\0' ;
+    }
+
+    return dest ;
+}
+
+
+/****************
+ * remove leading and trailing white spaces
+ */
+char *
+trim_spaces( char *str )
+{
+    char *string, *p, *mark;
+
+    string = str;
+    /* find first non space character */
+    for( p=string; *p && isspace( *(byte*)p ) ; p++ )
+       ;
+    /* move characters */
+    for( (mark = NULL); (*string = *p); string++, p++ )
+       if( isspace( *(byte*)p ) ) {
+           if( !mark )
+               mark = string ;
+       }
+       else
+           mark = NULL ;
+    if( mark )
+       *mark = '\0' ;  /* remove trailing spaces */
+
+    return str ;
+}
+
+/****************
+ * remove trailing white spaces
+ */
+char *
+trim_trailing_spaces( char *string )
+{
+    char *p, *mark;
+
+    for( mark = NULL, p = string; *p; p++ ) {
+       if( isspace( *(byte*)p ) ) {
+           if( !mark )
+               mark = p;
+       }
+       else
+           mark = NULL;
+    }
+    if( mark )
+       *mark = '\0' ;
+
+    return string ;
+}
+
+
+
+unsigned
+trim_trailing_chars( byte *line, unsigned len, const char *trimchars )
+{
+    byte *p, *mark;
+    unsigned n;
+
+    for(mark=NULL, p=line, n=0; n < len; n++, p++ ) {
+       if( strchr(trimchars, *p ) ) {
+           if( !mark )
+               mark = p;
+       }
+       else
+           mark = NULL;
+    }
+
+    if( mark ) {
+       *mark = 0;
+       return mark - line;
+    }
+    return len;
+}
+
+/****************
+ * remove trailing white spaces and return the length of the buffer
+ */
+unsigned
+trim_trailing_ws( byte *line, unsigned len )
+{
+    return trim_trailing_chars( line, len, " \t\r\n" );
+}
+
+
+/***************
+ * Extract from a given path the filename component.
+ *
+ */
+char *
+make_basename(const char *filepath)
+{
+    char *p;
+
+    if ( !(p=strrchr(filepath, '/')) )
+      #ifdef HAVE_DRIVE_LETTERS
+       if ( !(p=strrchr(filepath, '\\')) )
+           if ( !(p=strrchr(filepath, ':')) )
+      #endif
+             {
+               return jnlib_xstrdup(filepath);
+             }
+
+    return jnlib_xstrdup(p+1);
+}
+
+
+
+/***************
+ * Extract from a given filename the path prepended to it.
+ * If their isn't a path prepended to the filename, a dot
+ * is returned ('.').
+ *
+ */
+char *
+make_dirname(const char *filepath)
+{
+    char *dirname;
+    int  dirname_length;
+    char *p;
+
+    if ( !(p=strrchr(filepath, '/')) )
+      #ifdef HAVE_DRIVE_LETTERS
+       if ( !(p=strrchr(filepath, '\\')) )
+           if ( !(p=strrchr(filepath, ':')) )
+      #endif
+             {
+               return jnlib_xstrdup(".");
+             }
+
+    dirname_length = p-filepath;
+    dirname = jnlib_xmalloc(dirname_length+1);
+    strncpy(dirname, filepath, dirname_length);
+    dirname[dirname_length] = 0;
+
+    return dirname;
+}
+
+
+
+/****************
+ * Construct a filename from the NULL terminated list of parts.
+ * Tilde expansion is done here.
+ */
+char *
+make_filename( const char *first_part, ... )
+{
+    va_list arg_ptr ;
+    size_t n;
+    const char *s;
+    char *name, *home, *p;
+
+    va_start( arg_ptr, first_part ) ;
+    n = strlen(first_part)+1;
+    while( (s=va_arg(arg_ptr, const char *)) )
+       n += strlen(s) + 1;
+    va_end(arg_ptr);
+
+    home = NULL;
+    if( *first_part == '~' && first_part[1] == '/'
+                          && (home = getenv("HOME")) && *home )
+       n += strlen(home);
+
+    name = jnlib_xmalloc(n);
+    p = home ? stpcpy(stpcpy(name,home), first_part+1)
+            : stpcpy(name, first_part);
+    va_start( arg_ptr, first_part ) ;
+    while( (s=va_arg(arg_ptr, const char *)) )
+       p = stpcpy(stpcpy(p,"/"), s);
+    va_end(arg_ptr);
+
+    return name;
+}
+
+
+int
+compare_filenames( const char *a, const char *b )
+{
+    /* ? check whether this is an absolute filename and
+     * resolve symlinks?
+     */
+  #ifdef HAVE_DRIVE_LETTERS
+    return stricmp(a,b);
+  #else
+    return strcmp(a,b);
+  #endif
+}
+
+
+/****************************************************
+ ******** locale insensitive ctype functions ********
+ ****************************************************/
+/* FIXME: replace them by a table lookup and macros */
+int
+ascii_isupper (int c)
+{
+    return c >= 'A' && c <= 'Z';
+}
+
+int
+ascii_islower (int c)
+{
+    return c >= 'a' && c <= 'z';
+}
+
+int 
+ascii_toupper (int c)
+{
+    if (c >= 'a' && c <= 'z')
+        c &= ~0x20;
+    return c;
+}
+
+int 
+ascii_tolower (int c)
+{
+    if (c >= 'A' && c <= 'Z')
+        c |= 0x20;
+    return c;
+}
+
+
+int
+ascii_strcasecmp( const char *a, const char *b )
+{
+    if (a == b)
+        return 0;
+
+    for (; *a && *b; a++, b++) {
+       if (*a != *b && ascii_toupper(*a) != ascii_toupper(*b))
+           break;
+    }
+    return *a == *b? 0 : (ascii_toupper (*a) - ascii_toupper (*b));
+}
+
+int
+ascii_memcasecmp( const char *a, const char *b, size_t n )
+{
+    if (a == b)
+        return 0;
+    for ( ; n; n--, a++, b++ ) {
+       if( *a != *b  && ascii_toupper (*a) != ascii_toupper (*b) )
+            return *a == *b? 0 : (ascii_toupper (*a) - ascii_toupper (*b));
+    }
+    return 0;
+}
+
+int
+ascii_strcmp( const char *a, const char *b )
+{
+    if (a == b)
+        return 0;
+
+    for (; *a && *b; a++, b++) {
+       if (*a != *b )
+           break;
+    }
+    return *a == *b? 0 : (*(signed char *)a - *(signed char *)b);
+}
+
+
+
+/*********************************************
+ ********** missing string functions *********
+ *********************************************/
+
+#ifndef HAVE_STPCPY
+char *
+stpcpy(char *a,const char *b)
+{
+    while( *b )
+       *a++ = *b++;
+    *a = 0;
+
+    return (char*)a;
+}
+#endif
+
+#ifndef HAVE_STRLWR
+char *
+strlwr(char *s)
+{
+    char *p;
+    for(p=s; *p; p++ )
+       *p = tolower(*p);
+    return s;
+}
+#endif
+
+
+#ifndef HAVE_STRCASECMP
+int
+strcasecmp( const char *a, const char *b )
+{
+    for( ; *a && *b; a++, b++ ) {
+       if( *a != *b && toupper(*a) != toupper(*b) )
+           break;
+    }
+    return *(const byte*)a - *(const byte*)b;
+}
+#endif
+
+
+/****************
+ * mingw32/cpd has a memicmp()
+ */
+#ifndef HAVE_MEMICMP
+int
+memicmp( const char *a, const char *b, size_t n )
+{
+    for( ; n; n--, a++, b++ )
+       if( *a != *b  && toupper(*(const byte*)a) != toupper(*(const byte*)b) )
+           return *(const byte *)a - *(const byte*)b;
+    return 0;
+}
+#endif
+
+
+
diff --git a/jnlib/stringhelp.h b/jnlib/stringhelp.h
new file mode 100644 (file)
index 0000000..17a6ad0
--- /dev/null
@@ -0,0 +1,74 @@
+/* stringhelp.h
+ *     Copyright (C) 1998,1999,2000,2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef LIBJNLIB_STRINGHELP_H
+#define LIBJNLIB_STRINGHELP_H
+
+#include "types.h"
+
+const char *memistr( const char *buf, size_t buflen, const char *sub );
+char *mem2str( char *, const void *, size_t);
+char *trim_spaces( char *string );
+char *trim_trailing_spaces( char *string );
+unsigned int trim_trailing_chars( unsigned char *line, unsigned len,
+                                             const char *trimchars);
+unsigned int trim_trailing_ws( unsigned char *line, unsigned len );
+
+
+char *make_basename(const char *filepath);
+char *make_dirname(const char *filepath);
+char *make_filename( const char *first_part, ... );
+int compare_filenames( const char *a, const char *b );
+
+const char *ascii_memistr( const char *buf, size_t buflen, const char *sub );
+int ascii_isupper (int c);
+int ascii_islower (int c);
+int ascii_toupper (int c);
+int ascii_tolower (int c);
+int ascii_strcasecmp( const char *a, const char *b );
+int ascii_memcasecmp( const char *a, const char *b, size_t n );
+
+
+#ifndef HAVE_MEMICMP
+int memicmp( const char *a, const char *b, size_t n );
+#endif
+#ifndef HAVE_STPCPY
+char *stpcpy(char *a,const char *b);
+#endif
+#ifndef HAVE_STRLWR
+char *strlwr(char *a);
+#endif
+#ifndef HAVE_STRTOUL
+  #define strtoul(a,b,c)  ((unsigned long)strtol((a),(b),(c)))
+#endif
+#ifndef HAVE_MEMMOVE
+  #define memmove(d, s, n) bcopy((s), (d), (n))
+#endif
+#ifndef HAVE_STRICMP
+  #define stricmp(a,b)  strcasecmp( (a), (b) )
+#endif
+
+#ifndef STR
+  #define STR(v) #v
+#endif
+#define STR2(v) STR(v)
+
+
+#endif /*LIBJNLIB_STRINGHELP_H*/
diff --git a/jnlib/strlist.c b/jnlib/strlist.c
new file mode 100644 (file)
index 0000000..7cbaf5e
--- /dev/null
@@ -0,0 +1,133 @@
+/* strlist.c -  string helpers
+ *     Copyright (C) 1998, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#include "libjnlib-config.h"
+#include "strlist.h"
+
+
+void
+free_strlist( STRLIST sl )
+{
+    STRLIST sl2;
+
+    for(; sl; sl = sl2 ) {
+       sl2 = sl->next;
+       jnlib_free(sl);
+    }
+}
+
+
+STRLIST
+add_to_strlist( STRLIST *list, const char *string )
+{
+    STRLIST sl;
+
+    sl = jnlib_xmalloc( sizeof *sl + strlen(string));
+    sl->flags = 0;
+    strcpy(sl->d, string);
+    sl->next = *list;
+    *list = sl;
+    return sl;
+}
+
+#if 0
+/****************
+ * same as add_to_strlist() but if is_utf8 is *not* set a conversion
+ * to UTF8 is done
+ */
+STRLIST
+add_to_strlist2( STRLIST *list, const char *string, int is_utf8 )
+{
+    STRLIST sl;
+
+    if( is_utf8 )
+       sl = add_to_strlist( list, string );
+    else {
+       char *p = native_to_utf8( string );
+       sl = add_to_strlist( list, p );
+       m_free( p );
+    }
+    return sl;
+}
+#endif
+
+STRLIST
+append_to_strlist( STRLIST *list, const char *string )
+{
+    STRLIST r, sl;
+
+    sl = jnlib_xmalloc( sizeof *sl + strlen(string));
+    sl->flags = 0;
+    strcpy(sl->d, string);
+    sl->next = NULL;
+    if( !*list )
+       *list = sl;
+    else {
+       for( r = *list; r->next; r = r->next )
+           ;
+       r->next = sl;
+    }
+    return sl;
+}
+
+#if 0
+STRLIST
+append_to_strlist2( STRLIST *list, const char *string, int is_utf8 )
+{
+    STRLIST sl;
+
+    if( is_utf8 )
+       sl = append_to_strlist( list, string );
+    else {
+       char *p = native_to_utf8( string );
+       sl = append_to_strlist( list, p );
+       m_free( p );
+    }
+    return sl;
+}
+#endif
+
+STRLIST
+strlist_prev( STRLIST head, STRLIST node )
+{
+    STRLIST n;
+
+    for(n=NULL; head && head != node; head = head->next )
+       n = head;
+    return n;
+}
+
+STRLIST
+strlist_last( STRLIST node )
+{
+    if( node )
+       for( ; node->next ; node = node->next )
+           ;
+    return node;
+}
+
+
+
diff --git a/jnlib/strlist.h b/jnlib/strlist.h
new file mode 100644 (file)
index 0000000..53c0bc7
--- /dev/null
@@ -0,0 +1,43 @@
+/* strlist.h
+ *     Copyright (C) 1998, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef LIBJNLIB_STRLIST_H
+#define LIBJNLIB_STRLIST_H
+
+struct string_list {
+  struct string_list *next;
+  unsigned int flags;
+  char d[1];
+};
+typedef struct string_list *STRLIST;
+
+
+void    free_strlist( STRLIST sl );
+STRLIST add_to_strlist( STRLIST *list, const char *string );
+STRLIST add_to_strlist2( STRLIST *list, const char *string, int is_utf8 );
+STRLIST append_to_strlist( STRLIST *list, const char *string );
+STRLIST append_to_strlist2( STRLIST *list, const char *string, int is_utf8 );
+STRLIST strlist_prev( STRLIST head, STRLIST node );
+STRLIST strlist_last( STRLIST node );
+
+#define FREE_STRLIST(a) do { free_strlist((a)); (a) = NULL ; } while(0)
+
+
+#endif /*LIBJNLIB_STRLIST_H*/
diff --git a/jnlib/types.h b/jnlib/types.h
new file mode 100644 (file)
index 0000000..230d150
--- /dev/null
@@ -0,0 +1,101 @@
+/* types.h
+ *     Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef LIBJNLIB_TYPES_H
+#define LIBJNLIB_TYPES_H
+
+/* The AC_CHECK_SIZEOF() in configure fails for some machines.
+ * we provide some fallback values here */
+#if !SIZEOF_UNSIGNED_SHORT
+  #undef SIZEOF_UNSIGNED_SHORT
+  #define SIZEOF_UNSIGNED_SHORT 2
+#endif
+#if !SIZEOF_UNSIGNED_INT
+  #undef SIZEOF_UNSIGNED_INT
+  #define SIZEOF_UNSIGNED_INT 4
+#endif
+#if !SIZEOF_UNSIGNED_LONG
+  #undef SIZEOF_UNSIGNED_LONG
+  #define SIZEOF_UNSIGNED_LONG 4
+#endif
+
+
+#include <sys/types.h>
+
+
+#ifndef HAVE_BYTE_TYPEDEF
+  #undef byte      /* maybe there is a macro with this name */
+  typedef unsigned char byte;
+  #define HAVE_BYTE_TYPEDEF
+#endif
+
+#ifndef HAVE_USHORT_TYPEDEF
+  #undef ushort     /* maybe there is a macro with this name */
+  typedef unsigned short ushort;
+  #define HAVE_USHORT_TYPEDEF
+#endif
+
+#ifndef HAVE_ULONG_TYPEDEF
+  #undef ulong     /* maybe there is a macro with this name */
+  typedef unsigned long ulong;
+  #define HAVE_ULONG_TYPEDEF
+#endif
+
+#ifndef HAVE_U16_TYPEDEF
+  #undef u16       /* maybe there is a macro with this name */
+  #if SIZEOF_UNSIGNED_INT == 2
+    typedef unsigned int   u16;
+  #elif SIZEOF_UNSIGNED_SHORT == 2
+    typedef unsigned short u16;
+  #else
+    #error no typedef for u16
+  #endif
+  #define HAVE_U16_TYPEDEF
+#endif
+
+#ifndef HAVE_U32_TYPEDEF
+  #undef u32       /* maybe there is a macro with this name */
+  #if SIZEOF_UNSIGNED_INT == 4
+    typedef unsigned int u32;
+  #elif SIZEOF_UNSIGNED_LONG == 4
+    typedef unsigned long u32;
+  #else
+    #error no typedef for u32
+  #endif
+  #define HAVE_U32_TYPEDEF
+#endif
+
+#ifndef HAVE_U64_TYPEDEF
+  #undef u64       /* maybe there is a macro with this name */
+  #if SIZEOF_UNSIGNED_INT == 8
+    typedef unsigned int u64;
+    #define HAVE_U64_TYPEDEF
+  #elif SIZEOF_UNSIGNED_LONG == 8
+    typedef unsigned long u64;
+    #define HAVE_U64_TYPEDEF
+  #elif __GNUC__ >= 2 || defined(__SUNPRO_C)
+    typedef unsigned long long u64;
+    #define HAVE_U64_TYPEDEF
+  #endif
+#endif
+
+
+
+#endif /*LIBJNLIB_TYPES_H*/
diff --git a/jnlib/xmalloc.c b/jnlib/xmalloc.c
new file mode 100644 (file)
index 0000000..1cfaab9
--- /dev/null
@@ -0,0 +1,88 @@
+/* xmalloc.c - standard malloc wrappers
+ *     Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "libjnlib-config.h"
+#include "xmalloc.h"
+
+static void
+out_of_core(void)
+{
+    fputs("\nfatal: out of memory\n", stderr );
+    exit(2);
+}
+
+
+void *
+xmalloc( size_t n )
+{
+    void *p = malloc( n );
+    if( !p )
+       out_of_core();
+    return p;
+}
+
+void *
+xrealloc( void *a, size_t n )
+{
+    void *p = realloc( a, n );
+    if( !p )
+       out_of_core();
+    return p;
+}
+
+void *
+xcalloc( size_t n, size_t m )
+{
+    void *p = calloc( n, m );
+    if( !p )
+       out_of_core();
+    return p;
+}
+
+char *
+xstrdup( const char *string )
+{
+    void *p = xmalloc( strlen(string)+1 );
+    strcpy( p, string );
+    return p;
+}
+
+
+char *
+xstrcat2( const char *a, const char *b )
+{
+    size_t n1;
+    char *p;
+
+    if( !b )
+       return xstrdup( a );
+
+    n1 = strlen(a);
+    p = xmalloc( n1 + strlen(b) + 1 );
+    memcpy(p, a, n1 );
+    strcpy(p+n1, b );
+    return p;
+}
+
diff --git a/jnlib/xmalloc.h b/jnlib/xmalloc.h
new file mode 100644 (file)
index 0000000..150ef36
--- /dev/null
@@ -0,0 +1,31 @@
+/* xmalloc.h
+ *     Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef LIBJNLIB_XMALLOC_H
+#define LIBJNLIB_XMALLOC_H
+
+void *xmalloc( size_t n );
+void *xrealloc( void *a, size_t n );
+void *xcalloc( size_t n, size_t m );
+char *xstrdup( const char *string );
+char *xstrcat2( const char *a, const char *b );
+
+
+#endif /*LIBJNLIB_XMALLOC_H*/
index 11a644f..0d9d585 100644 (file)
@@ -29,12 +29,12 @@ uninstall-local:
 # kde_wallpaperdir  Where general wallpapers should go to.
 
 # set the include path for X, qt and KDE
-INCLUDES= $(all_includes) $(ASSUAN_INCLUDES)
+INCLUDES= $(all_includes) -I$(srcdir)/../assuan -I$(srcdir)/../jnlib
 
 METASOURCES = AUTO
 
 # the library search path. 
-kpinentry_LDFLAGS = $(all_libraries) $(ASSUAN_LDFLAGS) $(KDE_RPATH)
+kpinentry_LDFLAGS = $(all_libraries) $(KDE_RPATH) ../assuan/libassuan.a ../jnlib/libjnlib.a
 
 # Uncomment the following two lines if you add a ui.rc file for your application to make use of
 # KDE´s XML GUI builing