commit-mailutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SCM] GNU Mailutils branch, master, updated. release-2.2-488-gb848706


From: Sergey Poznyakoff
Subject: [SCM] GNU Mailutils branch, master, updated. release-2.2-488-gb848706
Date: Wed, 30 Nov 2011 16:10:13 +0000

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

http://git.savannah.gnu.org/cgit/mailutils.git/commit/?id=b848706b89934be5a4419ab2ec382157d2f30aba

The branch, master has been updated
       via  b848706b89934be5a4419ab2ec382157d2f30aba (commit)
       via  29021e954aaec4a5db1c50a505ec914fe8bbae69 (commit)
      from  7ca7546248a32a53049e1d59889013925c01ed85 (commit)

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

- Log -----------------------------------------------------------------
commit b848706b89934be5a4419ab2ec382157d2f30aba
Author: Sergey Poznyakoff <address@hidden>
Date:   Wed Nov 30 18:00:38 2011 +0200

    imap client: initial implementation of fetch.
    
    * include/mailutils/imap.h (mu_imap_fetch): New proto.
    (MU_IMAP_CB_FETCH): New callback code.
    (MU_IMAP_FETCH_BODY,MU_IMAP_FETCH_BODYSTRUCTURE)
    (MU_IMAP_FETCH_ENVELOPE,MU_IMAP_FETCH_FLAGS)
    (MU_IMAP_FETCH_INTERNALDATE,MU_IMAP_FETCH_RFC822_SIZE)
    (MU_IMAP_FETCH_UID): New constants.
    (mu_imap_fetch_body,mu_imap_fetch_bodystructure)
    (mu_imap_fetch_envelope,mu_imap_fetch_flags)
    (mu_imap_fetch_internaldate,mu_imap_fetch_rfc822_size)
    (mu_imap_fetch_uid,mu_imap_fetch_response): New structures.
    * include/mailutils/sys/imap.h (MU_IMAP_SET_XSCRIPT_MASK)
    (MU_IMAP_CLR_XSCRIPT_MASK): New macros.
    (mu_imap_client_state)<MU_IMAP_FETCH_RX>: New state.
    (_mu_imap_list_nth_element_is_string): New proto.
    * libproto/imap/fetch.c: New file.
    * libproto/imap/Makefile.am (libmu_imap_la_SOURCES): Add fetch.c
    * libproto/imap/carrier.c (mu_imap_set_carrier): Update call to
    mu_imapio_create.
    * libproto/imap/resplist.c (_mu_imap_list_nth_element_is_string): New
    function.
    * libproto/imap/resproc.c (_process_unsolicited_response): Handle FETCH
    response.
    * libproto/imap/trace.c (mu_imap_trace_mask): Call mu_imapio_trace_payload
    if needed.
    * mu/imap.c (imap_bad_callback): New function.
    (com_disconnect): Register imap_bad_callback.
    (com_fetch): New function.
    (imap_comtab): Add the "fetch" command.
    * mu/mu.h (mutool_command) <flags>: New member.
    * mu/pop.c (pop_comtab): Update initializers.
    * mu/shell.c (default_comtab): Update initializers.
    (execute_line): Rewrite using incremental wordsplit.  This allows for
    optionally coalescing arguments with indices >= argmax into one argument.
    This is useful for such commands as "prompt" and "fetch".

commit 29021e954aaec4a5db1c50a505ec914fe8bbae69
Author: Sergey Poznyakoff <address@hidden>
Date:   Wed Nov 30 17:59:24 2011 +0200

    Add a function for creating an empty email address object.
    
    * include/mailutils/address.h (mu_address_create_null): New proto.
    * libmailutils/address/address.c (mu_address_create_null): New function.

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

Summary of changes:
 imap4d/fetch.c                 |    1 -
 include/mailutils/address.h    |    2 +
 include/mailutils/imap.h       |  103 ++++++-
 include/mailutils/sys/imap.h   |   17 +-
 libmailutils/address/address.c |   10 +
 libproto/imap/Makefile.am      |    1 +
 libproto/imap/carrier.c        |    2 +-
 libproto/imap/fetch.c          |  647 ++++++++++++++++++++++++++++++++++++++++
 libproto/imap/resplist.c       |    9 +
 libproto/imap/resproc.c        |   32 ++-
 libproto/imap/trace.c          |   11 +-
 mu/imap.c                      |   58 +++-
 mu/mu.h                        |    6 +-
 mu/pop.c                       |   34 +-
 mu/shell.c                     |  118 +++++---
 15 files changed, 969 insertions(+), 82 deletions(-)
 create mode 100644 libproto/imap/fetch.c

diff --git a/imap4d/fetch.c b/imap4d/fetch.c
index 5781769..bf6ad9e 100644
--- a/imap4d/fetch.c
+++ b/imap4d/fetch.c
@@ -560,7 +560,6 @@ fetch_bodystructure0 (mu_message_t message, int extension)
 
       mu_message_get_header (message, &header);
 
-
       /* The subtype.  */
       if (mu_header_aget_value (header, MU_HEADER_CONTENT_TYPE, &buffer) == 0)
        {
diff --git a/include/mailutils/address.h b/include/mailutils/address.h
index 65d6209..96640be 100644
--- a/include/mailutils/address.h
+++ b/include/mailutils/address.h
@@ -62,6 +62,8 @@ struct mu_address
   struct mu_address *next;
 };
 
+extern int mu_address_create_null (mu_address_t *);
+  
 extern int mu_address_create_hint (mu_address_t *, const char *,
                                   mu_address_t, int);
   
diff --git a/include/mailutils/imap.h b/include/mailutils/imap.h
index cb679d0..cfb879c 100644
--- a/include/mailutils/imap.h
+++ b/include/mailutils/imap.h
@@ -21,6 +21,7 @@
 #include <mailutils/iterator.h>
 #include <mailutils/debug.h>
 #include <mailutils/stream.h>
+#include <mailutils/util.h>
 #include <mailutils/kwd.h>
 
 #ifdef __cplusplus
@@ -58,6 +59,8 @@ int mu_imap_id (mu_imap_t imap, char **idenv, mu_assoc_t 
*passoc);
 
 int mu_imap_noop (mu_imap_t imap);
 
+int mu_imap_fetch (mu_imap_t imap, const char *msgset, const char *items);
+  
 int mu_imap_set_carrier (mu_imap_t imap, mu_stream_t carrier);
 int mu_imap_get_carrier (mu_imap_t imap, mu_stream_t *pcarrier);
 
@@ -104,7 +107,7 @@ extern struct mu_kwd _mu_imap_status_name_table[];
 
   /* The following five callbacks correspond to members of struct
      mu_imap_stat and take a pointer to struct mu_imap_stat as their
-     only argument. */
+     PDAT argument.  SDAT is always 0. */
 #define MU_IMAP_CB_PERMANENT_FLAGS  0
 #define MU_IMAP_CB_MESSAGE_COUNT    1
 #define MU_IMAP_CB_RECENT_COUNT     2
@@ -113,15 +116,20 @@ extern struct mu_kwd _mu_imap_status_name_table[];
 #define MU_IMAP_CB_UIDVALIDITY      5
 
   /* The following callbacks correspond to server responses and take two
-     argument: a response code (see MU_IMAP_RESPONSE, below), and
-     human-readable text string as returned by the server.  The latter can
-     be NULL. */
+     arguments: a response code (see MU_IMAP_RESPONSE, below) in SDAT, and
+     human-readable text string as returned by the server in PDAT.  The
+     latter can be NULL. */
 #define MU_IMAP_CB_OK               6
 #define MU_IMAP_CB_NO               7
 #define MU_IMAP_CB_BAD              8
 #define MU_IMAP_CB_BYE              9
 #define MU_IMAP_CB_PREAUTH         10
-#define _MU_IMAP_CB_MAX            11
+
+  /* FETCH callback.  Arguments: SDAT - message sequence number, PDAT - a
+     list (mu_list_t) of union mu_imap_fetch_response (see below). */
+#define MU_IMAP_CB_FETCH           11
+
+#define _MU_IMAP_CB_MAX            12
 
 typedef void (*mu_imap_callback_t) (void *, int code, size_t sdat, void *pdat);
   
@@ -144,7 +152,92 @@ void mu_imap_register_callback_function (mu_imap_t imap, 
int code,
 #define MU_IMAP_RESPONSE_UNSEEN          10
 
 extern struct mu_kwd mu_imap_response_codes[];  
+
+  /* FETCH Response Codes */
+
+  /* BODY[<section>]<<origin octet>> */
+#define MU_IMAP_FETCH_BODY                 0
+  /* BODY & BODYSTRUCTURE */
+#define MU_IMAP_FETCH_BODYSTRUCTURE        1
+  /* ENVELOPE */
+#define MU_IMAP_FETCH_ENVELOPE             2 
+  /* FLAGS */
+#define MU_IMAP_FETCH_FLAGS                3
+  /* INTERNALDATE */
+#define MU_IMAP_FETCH_INTERNALDATE         4
+  /* RFC822.SIZE */
+#define MU_IMAP_FETCH_RFC822_SIZE          5
+  /* UID */
+#define MU_IMAP_FETCH_UID                  6
+
+struct mu_imap_fetch_body
+{
+  int type;
+  size_t *partv;
+  size_t partc;
+  char *key;
+  char *text;
+};
+
+struct mu_imap_fetch_bodystructure
+{
+  int type;
+  //FIXME?
+};
   
+struct mu_imap_fetch_envelope
+{
+  int type;
+  struct tm date;
+  struct mu_timezone tz;
+  char *subject;
+  mu_address_t from;
+  mu_address_t sender;
+  mu_address_t reply_to;
+  mu_address_t to;
+  mu_address_t cc;
+  mu_address_t bcc;
+  char *in_reply_to;
+  char *message_id;
+};
+
+struct mu_imap_fetch_flags
+{
+  int type;
+  int flags;
+};
+
+struct mu_imap_fetch_internaldate
+{
+  int type;
+  struct tm tm;
+  struct mu_timezone tz;
+};
+  
+struct mu_imap_fetch_rfc822_size
+{
+  int type;
+  size_t size;
+};
+  
+struct mu_imap_fetch_uid
+{
+  int type;
+  size_t uid;
+};
+
+union mu_imap_fetch_response
+{
+  int type;
+  struct mu_imap_fetch_body body;
+  struct mu_imap_fetch_bodystructure bodystructure;
+  struct mu_imap_fetch_envelope envelope;
+  struct mu_imap_fetch_flags flags;
+  struct mu_imap_fetch_internaldate internaldate;
+  struct mu_imap_fetch_rfc822_size rfc822_size;
+  struct mu_imap_fetch_uid uid;
+};
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/mailutils/sys/imap.h b/include/mailutils/sys/imap.h
index 58bffd4..ff23a91 100644
--- a/include/mailutils/sys/imap.h
+++ b/include/mailutils/sys/imap.h
@@ -32,7 +32,17 @@ extern "C" {
 #define MU_IMAP_RESP  0x01
 #define MU_IMAP_TRACE 0x02
 #define MU_IMAP_XSCRIPT_MASK(n) (1<<((n)+1))
-
+#define MU_IMAP_SET_XSCRIPT_MASK(imap,n)       \
+  do                                           \
+    (imap)->flags |= MU_IMAP_XSCRIPT_MASK (n); \
+  while (0)
+#define MU_IMAP_CLR_XSCRIPT_MASK(imap,n)       \
+  do                                           \
+    (imap)->flags &= ~MU_IMAP_XSCRIPT_MASK (n);        \
+  while (0)
+#define MU_IMAP_IS_XSCRIPT_MASK(imap,n)         \
+  ((imap)->flags & MU_IMAP_XSCRIPT_MASK (n))
+  
 enum mu_imap_client_state
   {
     MU_IMAP_NO_STATE,
@@ -47,6 +57,7 @@ enum mu_imap_client_state
     MU_IMAP_SELECT_RX,
     MU_IMAP_STATUS_RX,
     MU_IMAP_NOOP_RX,
+    MU_IMAP_FETCH_RX,
     MU_IMAP_CLOSING
   };
 
@@ -174,10 +185,14 @@ int _mu_imap_response (mu_imap_t imap, 
mu_imap_response_action_t fun,
   
 int _mu_imap_list_element_is_string (struct imap_list_element *elt,
                                     const char *str);
+int _mu_imap_list_nth_element_is_string (mu_list_t list, size_t n,
+                                        const char *str);
+  
 int _mu_imap_collect_flags (struct imap_list_element *arg, int *res);
 
 struct imap_list_element *_mu_imap_list_at (mu_list_t list, int idx);
   
+int _mu_imap_parse_fetch_response (mu_list_t resp, mu_list_t *result_list);
   
 # ifdef __cplusplus
 }
diff --git a/libmailutils/address/address.c b/libmailutils/address/address.c
index 69b105d..117d10e 100644
--- a/libmailutils/address/address.c
+++ b/libmailutils/address/address.c
@@ -36,6 +36,16 @@
 #include <mailutils/address.h>
 #include <mailutils/cstr.h>
 
+int
+mu_address_create_null (mu_address_t *pa)
+{
+  mu_address_t a = calloc (1, sizeof (*a));
+  if (!a)
+    return ENOMEM;
+  *pa = a;
+  return 0;
+}
+
 /* Get email addresses from rfc822 address.  */
 int
 mu_address_create_hint (mu_address_t *a, const char *s, mu_address_t hint,
diff --git a/libproto/imap/Makefile.am b/libproto/imap/Makefile.am
index c14b76d..fbd14bd 100644
--- a/libproto/imap/Makefile.am
+++ b/libproto/imap/Makefile.am
@@ -28,6 +28,7 @@ libmu_imap_la_LIBADD = ${MU_LIB_AUTH} ${MU_LIB_MAILUTILS} 
@INTLLIBS@
 #  url.c
 libmu_imap_la_SOURCES = \
  fake-folder.c\
+ fetch.c\
  callback.c\
  capability.c\
  capatst.c\
diff --git a/libproto/imap/carrier.c b/libproto/imap/carrier.c
index afb8563..ecc170c 100644
--- a/libproto/imap/carrier.c
+++ b/libproto/imap/carrier.c
@@ -37,7 +37,7 @@ mu_imap_set_carrier (mu_imap_t imap, mu_stream_t carrier)
   if (imap == NULL)
     return EINVAL;
 
-  rc = mu_imapio_create (&io, carrier);
+  rc = mu_imapio_create (&io, carrier, MU_IMAPIO_CLIENT);
   if (rc)
     return rc;
   if (imap->io)
diff --git a/libproto/imap/fetch.c b/libproto/imap/fetch.c
new file mode 100644
index 0000000..4b6e65d
--- /dev/null
+++ b/libproto/imap/fetch.c
@@ -0,0 +1,647 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 2011 Free Software Foundation, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 3 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General
+   Public License along with this library.  If not, see
+   <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/errno.h>
+#include <mailutils/address.h>
+#include <mailutils/cstr.h>
+#include <mailutils/cctype.h>
+#include <mailutils/imap.h>
+#include <mailutils/sys/imap.h>
+
+int
+mu_imap_fetch (mu_imap_t imap, const char *msgset, const char *items)
+{
+  int status;
+  
+  if (imap == NULL)
+    return EINVAL;
+  if (!imap->io)
+    return MU_ERR_NO_TRANSPORT;
+  if (imap->state != MU_IMAP_CONNECTED)
+    return MU_ERR_SEQ;
+
+  if (imap->imap_state != MU_IMAP_STATE_SELECTED)
+    return MU_ERR_SEQ;
+  
+  switch (imap->state)
+    {
+    case MU_IMAP_CONNECTED:
+      status = _mu_imap_tag_next (imap);
+      MU_IMAP_CHECK_EAGAIN (imap, status);
+      status = mu_imapio_printf (imap->io, "%s FETCH %s %s\r\n",
+                                imap->tag_str, msgset, items);
+      MU_IMAP_CHECK_ERROR (imap, status);
+      MU_IMAP_FCLR (imap, MU_IMAP_RESP);
+      imap->state = MU_IMAP_FETCH_RX;
+
+    case MU_IMAP_FETCH_RX:
+      status = _mu_imap_response (imap, NULL, NULL);
+      MU_IMAP_CHECK_EAGAIN (imap, status);
+      switch (imap->resp_code)
+       {
+       case MU_IMAP_OK:
+         status = 0;
+         break;
+
+       case MU_IMAP_NO:
+         status = MU_ERR_FAILURE;
+         break;
+
+       case MU_IMAP_BAD:
+         status = MU_ERR_BADREPLY;
+         break;
+       }
+      imap->state = MU_IMAP_CONNECTED;
+      break;
+
+    default:
+      status = EINPROGRESS;
+    }
+  return status;
+}
+
+static void
+_free_fetch_response (void *ptr)
+{
+  union mu_imap_fetch_response *resp = ptr;
+  switch (resp->type)
+    {
+    case MU_IMAP_FETCH_BODY:
+      free (resp->body.partv);
+      free (resp->body.key);
+      free (resp->body.text);
+      break;
+      
+    case MU_IMAP_FETCH_BODYSTRUCTURE:
+      /* FIXME */
+      break;
+      
+    case MU_IMAP_FETCH_ENVELOPE:
+      free (resp->envelope.subject);
+      mu_address_destroy (&resp->envelope.from);
+      mu_address_destroy (&resp->envelope.sender);
+      mu_address_destroy (&resp->envelope.reply_to);
+      mu_address_destroy (&resp->envelope.to);
+      mu_address_destroy (&resp->envelope.cc);
+      mu_address_destroy (&resp->envelope.bcc);
+      free (resp->envelope.in_reply_to);
+      free (resp->envelope.message_id);
+      break;
+      
+    case MU_IMAP_FETCH_FLAGS:
+    case MU_IMAP_FETCH_INTERNALDATE:
+    case MU_IMAP_FETCH_RFC822_SIZE:
+    case MU_IMAP_FETCH_UID:
+      break;
+    }
+  free (resp);
+}
+
+static int
+alloc_response (union mu_imap_fetch_response **resp, int type)
+{
+  static size_t sizetab[] = {
+    sizeof (struct mu_imap_fetch_body),
+    sizeof (struct mu_imap_fetch_bodystructure),
+    sizeof (struct mu_imap_fetch_envelope),
+    sizeof (struct mu_imap_fetch_flags),
+    sizeof (struct mu_imap_fetch_internaldate),
+    sizeof (struct mu_imap_fetch_rfc822_size),
+    sizeof (struct mu_imap_fetch_uid)
+  };
+  union mu_imap_fetch_response *p;
+  
+  if (type < 0 || type >= MU_ARRAY_SIZE (sizetab))
+    return EINVAL;
+  p = calloc (1, sizetab[type]);
+  if (!p)
+    return ENOMEM;
+  p->type = type;
+  *resp = p;
+  return 0;
+}
+
+static int
+_uid_mapper (struct imap_list_element **elt,
+            union mu_imap_fetch_response **return_resp)
+{
+  union mu_imap_fetch_response *resp;
+  int rc;
+  char *p;
+  size_t uid;
+  
+  if (elt[1]->type != imap_eltype_string)
+    return MU_ERR_FAILURE;
+  uid = strtoul (elt[1]->v.string, &p, 0);
+  if (*p)
+    return MU_ERR_FAILURE;
+  rc = alloc_response (&resp, MU_IMAP_FETCH_UID);
+  if (rc)
+    return rc;
+  resp->uid.uid = uid;
+  *return_resp = resp;
+  return 0;
+}
+                       
+static int
+_size_mapper (struct imap_list_element **elt,
+             union mu_imap_fetch_response **return_resp)
+{
+  union mu_imap_fetch_response *resp;
+  int rc;
+  char *p;
+  size_t size;
+  
+  if (elt[1]->type != imap_eltype_string)
+    return MU_ERR_FAILURE;
+  size = strtoul (elt[1]->v.string, &p, 0);
+  if (*p)
+    return MU_ERR_FAILURE;
+  rc = alloc_response (&resp, MU_IMAP_FETCH_RFC822_SIZE);
+  if (rc)
+    return rc;
+  resp->rfc822_size.size = size;
+  *return_resp = resp;
+  return 0;
+}
+
+static int
+_body_mapper (struct imap_list_element **elt,
+             union mu_imap_fetch_response **return_resp)
+{
+  union mu_imap_fetch_response *resp;
+  int rc;
+  char *section, *p;
+  size_t partc = 0;
+  size_t *partv = NULL;
+  
+  if (elt[1]->type != imap_eltype_string)
+    return MU_ERR_FAILURE;
+  rc = alloc_response (&resp, MU_IMAP_FETCH_BODY);
+  if (rc)
+    return rc;
+
+  section = strchr (elt[0]->v.string, '[');
+  if (section)
+    {
+      p = section;
+      while (1)
+       {
+         p = strchr (p, '.');
+         if (*p)
+           {
+             p++;
+             if (mu_isdigit (p[1]))
+               {
+                 partc++;
+                 continue;
+               }
+           }
+         
+         break;
+       }
+    }
+  
+  if (partc)
+    {
+      size_t i;
+      
+      partv = calloc (partc, sizeof (partv[0]));
+      for (i = 0, p = section; i < partc; i++)
+       {
+         partv[i] = strtoul (p, &p, 10);
+         p++;
+       }
+    }
+
+  resp->body.partc = partc;
+  resp->body.partv = partv;
+
+  if (p)
+    {
+      size_t len = strlen (p);
+      resp->body.key = malloc (len);
+      if (!resp->body.key)
+       {
+         free (resp->body.partv);
+         free (resp);
+         return ENOMEM;
+       }
+      len--;
+      memcpy (resp->body.key, p, len);
+      resp->body.key[len] = 0;
+    }
+  
+  resp->body.text = strdup (elt[1]->v.string);
+  if (!resp->body.text)
+    {
+      free (resp->body.key);
+      free (resp->body.partv);
+      free (resp);
+      return ENOMEM;
+    }
+  *return_resp = resp;
+  return 0;
+}
+
+static int
+_rfc822_mapper (const char *key, struct imap_list_element *elt,
+               union mu_imap_fetch_response **return_resp)
+{
+  union mu_imap_fetch_response *resp;
+  int rc;
+  
+  if (elt->type != imap_eltype_string)
+    return MU_ERR_FAILURE;
+  rc = alloc_response (&resp, MU_IMAP_FETCH_BODY);
+  if (rc)
+    return rc;
+
+  resp->body.partc = 0;
+  resp->body.partv = NULL;
+
+  resp->body.key = strdup (key);
+  if (!resp->body.key)
+    {
+      free (resp);
+      return ENOMEM;
+    }
+  
+  resp->body.text = strdup (elt->v.string);
+  if (!resp->body.text)
+    {
+      free (resp->body.key);
+      free (resp->body.partv);
+      free (resp);
+      return ENOMEM;
+    }
+  *return_resp = resp;
+  return 0;
+}
+
+static int
+_rfc822_header_mapper (struct imap_list_element **elt,
+                      union mu_imap_fetch_response **return_resp)
+{
+  return _rfc822_mapper ("HEADER", elt[1], return_resp);
+}
+
+static int
+_rfc822_text_mapper (struct imap_list_element **elt,
+                    union mu_imap_fetch_response **return_resp)
+{
+  return _rfc822_mapper ("TEXT", elt[1], return_resp);
+}
+
+static int
+_flags_mapper (struct imap_list_element **elt,
+              union mu_imap_fetch_response **return_resp)
+{
+  union mu_imap_fetch_response *resp;
+  int rc;
+  int flags;
+  
+  if (elt[1]->type != imap_eltype_list)
+    return MU_ERR_FAILURE;
+  if (_mu_imap_collect_flags (elt[1], &flags))
+    return MU_ERR_FAILURE;
+
+  rc = alloc_response (&resp, MU_IMAP_FETCH_FLAGS);
+  if (rc)
+    return rc;
+  resp->flags.flags = flags;
+  *return_resp = resp;
+  return 0;
+}
+
+static int
+_date_mapper (struct imap_list_element **elt,
+             union mu_imap_fetch_response **return_resp)
+{
+  struct mu_imap_fetch_internaldate idate;
+  union mu_imap_fetch_response *resp;
+  int rc;
+  const char *p;
+  
+  if (elt[1]->type != imap_eltype_string)
+    return MU_ERR_FAILURE;
+  p = elt[1]->v.string;
+  if (mu_parse_imap_date_time (&p, &idate.tm, &idate.tz))
+    return MU_ERR_FAILURE;
+  rc = alloc_response (&resp, MU_IMAP_FETCH_INTERNALDATE);
+  if (rc)
+    return rc;
+  resp->internaldate = idate;
+  *return_resp = resp;
+  return 0;
+}
+
+struct fill_env
+{
+  struct mu_imap_fetch_envelope *envelope;
+  size_t n;
+};
+
+enum env_index
+  {
+    env_date,
+    env_subject,
+    env_from,
+    env_sender,
+    env_reply_to,
+    env_to,
+    env_cc,
+    env_bcc,
+    env_in_reply_to,
+    env_message_id
+  };
+
+static int
+elt_to_string (struct imap_list_element *elt, char **pstr)
+{
+  char *p;
+  
+  if (elt->type != imap_eltype_string)
+    return EINVAL;
+  if (mu_c_strcasecmp (elt->v.string, "NIL") == 0)
+    p = NULL;
+  else
+    {
+      p = strdup (elt->v.string);
+      if (!p)
+       return ENOMEM;
+    }
+  *pstr = p;
+  return 0;
+}
+
+struct addr_env
+{
+  mu_address_t addr;
+  size_t n;
+};
+
+static int
+_fill_subaddr (void *item, void *data)
+{
+  struct addr_env *addr_env = data;
+  struct imap_list_element *elt = item, *arg;
+  const char *domain = NULL, *local = NULL, *personal = NULL;
+  
+  if (elt->type != imap_eltype_list)
+    return 0;
+  
+  arg = _mu_imap_list_at (elt->v.list, 0);
+  if (arg && arg->type == imap_eltype_string && strcmp (arg->v.string, "NIL"))
+    personal = arg->v.string;
+  arg = _mu_imap_list_at (elt->v.list, 2);
+  if (arg && arg->type == imap_eltype_string && strcmp (arg->v.string, "NIL"))
+    local = arg->v.string;
+  arg = _mu_imap_list_at (elt->v.list, 3);
+  if (arg && arg->type == imap_eltype_string && strcmp (arg->v.string, "NIL"))
+    domain = arg->v.string;
+
+  if (domain && local)
+    {
+      if (!addr_env->addr)
+       {
+         int rc = mu_address_create_null (&addr_env->addr);
+         if (rc)
+           return rc;
+       }
+      mu_address_set_local_part (addr_env->addr, addr_env->n, local);
+      mu_address_set_domain (addr_env->addr, addr_env->n, domain);
+      mu_address_set_personal (addr_env->addr, addr_env->n, personal);
+      addr_env->n++;
+    }
+  return 0;
+}
+
+static int
+elt_to_address (struct imap_list_element *elt, mu_address_t *paddr)
+{
+  if (elt->type != imap_eltype_list)
+    {
+      if (mu_c_strcasecmp (elt->v.string, "NIL") == 0)
+       *paddr = NULL;
+      else
+       return EINVAL;
+    }
+  else
+    {
+      struct addr_env addr_env;
+      addr_env.addr = NULL;
+      addr_env.n = 1;
+      mu_list_foreach (elt->v.list, _fill_subaddr, &addr_env);
+      *paddr = addr_env.addr;
+    }
+  return 0;
+}
+
+static int
+_fill_response (void *item, void *data)
+{
+  int rc;
+  struct imap_list_element *elt = item;
+  struct fill_env *env = data;
+
+  switch (env->n++)
+    {
+    case env_date:
+      if (elt->type != imap_eltype_string)
+       rc = MU_ERR_FAILURE;
+      else
+       {
+         char const *p = elt->v.string;
+         if (mu_parse_imap_date_time (&p,
+                                      &env->envelope->date,
+                                      &env->envelope->tz))
+           rc = MU_ERR_FAILURE;
+         else
+           rc = 0;
+       }
+      break;
+      
+    case env_subject:
+      rc = elt_to_string (elt, &env->envelope->subject);
+      break;
+
+    case env_from:
+      rc = elt_to_address (elt, &env->envelope->from);
+      break;
+       
+    case env_sender:
+      rc = elt_to_address (elt, &env->envelope->sender);
+      break;
+      
+    case env_reply_to:
+      rc = elt_to_address (elt, &env->envelope->reply_to);
+      break;
+      
+    case env_to:
+      rc = elt_to_address (elt, &env->envelope->to);
+      break;
+      
+    case env_cc:
+      rc = elt_to_address (elt, &env->envelope->cc);
+      break;
+      
+    case env_bcc:
+      rc = elt_to_address (elt, &env->envelope->bcc);
+      break;
+      
+    case env_in_reply_to:
+      rc = elt_to_string (elt, &env->envelope->in_reply_to);
+      break;
+      
+    case env_message_id:
+      rc = elt_to_string (elt, &env->envelope->message_id);
+      break;
+    }
+  return rc;
+}
+  
+static int
+_envelope_mapper (struct imap_list_element **elt,
+                 union mu_imap_fetch_response **return_resp)
+{
+  union mu_imap_fetch_response *resp;
+  int rc;
+  struct fill_env env;
+  
+  if (elt[1]->type != imap_eltype_list)
+    return MU_ERR_FAILURE;
+  rc = alloc_response (&resp, MU_IMAP_FETCH_ENVELOPE);
+  if (rc)
+    return rc;
+  env.envelope = &resp->envelope;
+  env.n = 0;
+  mu_list_foreach (elt[1]->v.list, _fill_response, &env);
+  
+  *return_resp = resp;
+  return 0;
+}
+
+struct mapper_tab
+{
+  char *name;
+  size_t size;
+  int prefix;
+  int (*mapper) (struct imap_list_element **, union mu_imap_fetch_response **);
+};
+  
+static struct mapper_tab mapper_tab[] = {
+#define S(s) s, (sizeof (s) - 1)
+  { S("BODYSTRUCTURE"), 0, },
+  { S("BODY["),         1, _body_mapper },
+  { S("BODY"),          0, _body_mapper },
+  { S("ENVELOPE"),      0, _envelope_mapper },
+  { S("FLAGS"),         0, _flags_mapper },
+  { S("INTERNALDATE"),  0, _date_mapper },
+  { S("RFC822"),        0, _body_mapper},
+  { S("RFC822.HEADER"), 0, _rfc822_header_mapper }, 
+  { S("RFC822.SIZE"),   0, _size_mapper },
+  { S("RFC822.TEXT"),   0, _rfc822_text_mapper },
+  { S("UID"),           0, _uid_mapper },
+#undef S
+  { NULL }
+};
+  
+static int
+_fetch_mapper (void **itmv, size_t itmc, void *call_data)
+{
+  int *status = call_data;
+  struct mapper_tab *mt;
+  struct imap_list_element *elt[2];
+  char *kw;
+  size_t kwlen;
+  union mu_imap_fetch_response *resp;
+
+  elt[0] = itmv[0];
+  elt[1] = itmv[1];
+  
+  if (elt[0]->type != imap_eltype_string)
+    {
+      *status = MU_ERR_FAILURE;
+      return MU_LIST_MAP_STOP|MU_LIST_MAP_SKIP;
+    }
+  kw = elt[0]->v.string;
+  kwlen = strlen (kw);
+  for (mt = mapper_tab; mt->name; mt++)
+    {
+      if (mt->prefix)
+       {
+         if (mt->size >= kwlen && memcmp (mt->name, kw, kwlen) == 0)
+           break;
+       }
+      else if (mt->size == kwlen && memcmp (mt->name, kw, kwlen) == 0)
+       break;
+    }
+
+  if (!mt->name)
+    {
+      mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_TRACE9,
+               ("ignoring unknown FETCH item '%s'", kw));
+      return MU_LIST_MAP_SKIP;
+    }
+
+  if (mt->mapper)
+    {
+      int rc = mt->mapper (elt, &resp);
+      if (rc == 0)
+       {
+         itmv[0] = resp;
+         return MU_LIST_MAP_OK;
+       }
+      else
+       {
+         *status = rc;
+         return MU_LIST_MAP_STOP|MU_LIST_MAP_SKIP;
+       }
+    }
+      
+  return MU_LIST_MAP_SKIP;
+}
+
+int
+_mu_imap_parse_fetch_response (mu_list_t input, mu_list_t *result_list)
+{
+  mu_list_t result;
+  int status;
+
+  status = mu_list_create (&result);
+  if (status)
+    {
+      mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR,
+               ("mu_list_create: %s", mu_strerror (status)));
+      return 1;
+    }
+  mu_list_set_destroy_item (result, _free_fetch_response);
+  mu_list_map (input, _fetch_mapper, &status, 2, &result);
+  if (status)
+    mu_list_destroy (&result);
+  else
+    *result_list = result;
+  
+  return status;
+}
diff --git a/libproto/imap/resplist.c b/libproto/imap/resplist.c
index 4a42633..98b8a22 100644
--- a/libproto/imap/resplist.c
+++ b/libproto/imap/resplist.c
@@ -270,4 +270,13 @@ _mu_imap_list_at (mu_list_t list, int idx)
   return arg;
 }
 
+int
+_mu_imap_list_nth_element_is_string (mu_list_t list, size_t n,
+                                    const char *str)
+{
+  struct imap_list_element *elt = _mu_imap_list_at (list, n);
+  return elt && elt->type == imap_eltype_string &&
+        strcmp (elt->v.string, str) == 0;
+}
+
 
diff --git a/libproto/imap/resproc.c b/libproto/imap/resproc.c
index d939690..373e258 100644
--- a/libproto/imap/resproc.c
+++ b/libproto/imap/resproc.c
@@ -304,6 +304,34 @@ _process_unsolicited_response (mu_imap_t imap, mu_list_t 
resp)
          return 0;
        }
     }
+  else if (count == 3 &&
+          _mu_imap_list_nth_element_is_string (resp, 1, "FETCH"))
+    {
+      size_t msgno;
+
+      arg = _mu_imap_list_at (resp, 0);
+      if (arg && arg->type == imap_eltype_string)
+       {
+         char *p;
+         msgno = strtoul (arg->v.string, &p, 10);
+         if (*p)
+           return 1;
+         
+         arg = _mu_imap_list_at (resp, 2);
+         if (arg->type == imap_eltype_list)
+           {
+             mu_list_t list;
+             
+             if (_mu_imap_parse_fetch_response (arg->v.list, &list) == 0)
+               {
+                 mu_imap_callback (imap, MU_IMAP_CB_FETCH, msgno, list);
+                 mu_list_destroy (&list);
+               }
+             return 0;
+           }
+       }
+    }
+
   return 1;
 }
 
@@ -317,8 +345,8 @@ _mu_imap_process_untagged_response (mu_imap_t imap, 
mu_list_t list,
       if (fun)
        fun (imap, list, data);
       else
-       mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
-                 ("ignoring unexpected response"));
+       mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE9,
+                 ("ignoring unrecognized response"));
     }
   return 0;
 }
diff --git a/libproto/imap/trace.c b/libproto/imap/trace.c
index 152e9df..68daa61 100644
--- a/libproto/imap/trace.c
+++ b/libproto/imap/trace.c
@@ -89,21 +89,26 @@ mu_imap_trace_mask (mu_imap_t imap, int op, int lev)
   switch (op)
     {
     case MU_IMAP_TRACE_SET:
-      imap->flags |= MU_IMAP_XSCRIPT_MASK (lev);
+      MU_IMAP_SET_XSCRIPT_MASK (imap, lev);
+      if (lev & MU_XSCRIPT_PAYLOAD)
+       mu_imapio_trace_payload (imap->io, 1);
       break;
       
     case MU_IMAP_TRACE_CLR:
-      imap->flags &= ~MU_IMAP_XSCRIPT_MASK (lev);
+      MU_IMAP_CLR_XSCRIPT_MASK (imap, lev);
+      if (lev & MU_XSCRIPT_PAYLOAD)
+       mu_imapio_trace_payload (imap->io, 0);
       break;
       
     case MU_IMAP_TRACE_QRY:
-      if (imap->flags & MU_IMAP_XSCRIPT_MASK (lev))
+      if (MU_IMAP_IS_XSCRIPT_MASK (imap, lev))
        break;
       return MU_ERR_NOENT;
       
     default:
       return EINVAL;
     }
+  
   return 0;
 }
 
diff --git a/mu/imap.c b/mu/imap.c
index 4edce2d..44a6cdb 100644
--- a/mu/imap.c
+++ b/mu/imap.c
@@ -182,6 +182,13 @@ imap_bye_callback (void *data, int code, size_t sdat, void 
*pdat)
     mu_diag_output (MU_DIAG_INFO, _("server is closing connection"));
 }
 
+static void
+imap_bad_callback (void *data, int code, size_t sdat, void *pdat)
+{
+  const char *text = pdat;
+  mu_diag_output (MU_DIAG_CRIT, "SERVER ALERT: %s", text);
+}
+
 
 static int
 com_disconnect (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
@@ -282,6 +289,9 @@ com_connect (int argc, char **argv)
          mu_imap_register_callback_function (imap, MU_IMAP_CB_BYE,
                                              imap_bye_callback,
                                              NULL);
+         mu_imap_register_callback_function (imap, MU_IMAP_CB_BAD,
+                                             imap_bad_callback,
+                                             NULL);
 
          
          status = mu_imap_connect (imap);
@@ -587,44 +597,68 @@ com_noop (int argc MU_ARG_UNUSED, char **argv 
MU_ARG_UNUSED)
   return 0;
 }
 
+static int
+com_fetch (int argc, char **argv)
+{
+  int status = mu_imap_fetch (imap, argv[1], argv[2]);
+  if (status)
+    report_failure ("fetch", status);
+  return 0;  
+}
+
 struct mutool_command imap_comtab[] = {
-  { "capability", 1, -1, com_capability,
+  { "capability", 1, -1, 0,
+    com_capability,
     /* TRANSLATORS: -reread is a keyword; do not translate. */
     N_("[-reread] [NAME...]"),
     N_("list server capabilities") },
-  { "verbose",    1, 4, com_verbose,
+  { "verbose",    1, 4, 0,
+    com_verbose,
     "[on|off|mask|unmask] [secure [payload]]",
     N_("control the protocol tracing") },
-  { "connect",    1, 4, com_connect,
+  { "connect",    1, 4, 0,
+    com_connect,
     /* TRANSLATORS: --tls is a keyword. */
     N_("[-tls] HOSTNAME [PORT]"),
     N_("open connection") },
-  { "disconnect", 1, 1,
+  { "disconnect", 1, 1, 0,
     com_disconnect,
     NULL,
     N_("close connection") },
-  { "login",        2, 3, com_login,
+  { "login",        2, 3, 0,
+    com_login,
     N_("USER [PASS]"),
     N_("login to the server") },
-  { "logout",       1, 1, com_logout,
+  { "logout",       1, 1, 0,
+    com_logout,
     NULL,
     N_("quit imap session") },
-  { "id",           1, -1, com_id,
+  { "id",           1, -1, 0,
+    com_id,
     N_("[-test KW] [ARG [ARG...]]"),
     N_("send ID command") },
-  { "noop",         1, 1, com_noop,
+  { "noop",         1, 1, 0,
+    com_noop,
     NULL,
     N_("no operation (keepalive)") },
-  { "select",       1, 2, com_select,
+  { "select",       1, 2, 0,
+    com_select,
     N_("[MBOX]"),
     N_("select a mailbox") },
-  { "examine",      1, 2, com_examine,
+  { "examine",      1, 2, 0,
+    com_examine,
     N_("[MBOX]"),
     N_("examine a mailbox") },
-  { "status",       3, -1, com_status,
+  { "status",       3, -1, 0,
+    com_status,
     N_("MBOX KW [KW...]"),
     N_("get mailbox status") },
-  { "quit",         1, 1, com_logout,
+  { "fetch",        3, 3, CMD_COALESCE_EXTRA_ARGS,
+    com_fetch,
+    N_("MSGSET ITEMS"),
+    N_("fetch message data") },
+  { "quit",         1, 1, 0,
+    com_logout,
     NULL,
     N_("same as `logout'") },
   { NULL }
diff --git a/mu/mu.h b/mu/mu.h
index cfb24a2..74544b5 100644
--- a/mu/mu.h
+++ b/mu/mu.h
@@ -18,13 +18,15 @@
 
 typedef int (*mutool_action_t) (int argc, char **argv);
 
+#define CMD_COALESCE_EXTRA_ARGS 0x01
+
 struct mutool_command
 {
   const char *name;    /* User printable name of the function. */
-  int argmin;           /* Min. acceptable number of arguments (-1 means
-                          pass all arguments as a single string */ 
+  int argmin;           /* Min. acceptable number of arguments (> 1) */ 
   int argmax;           /* Max. allowed number of arguments (-1 means not
                           limited */
+  int flags;
   mutool_action_t func;        /* Function to call to do the job. */
   const char *argdoc;   /* Documentation for the arguments */
   const char *docstring;/* Documentation for this function.  */
diff --git a/mu/pop.c b/mu/pop.c
index 088ebfe..da1452c 100644
--- a/mu/pop.c
+++ b/mu/pop.c
@@ -611,59 +611,59 @@ com_quit (int argc MU_ARG_UNUSED, char **argv 
MU_ARG_UNUSED)
 }
 
 struct mutool_command pop_comtab[] = {
-  { "apop",       2,  3, com_apop,
+  { "apop",       2,  3, 0, com_apop,
     N_("USER [PASS]"),
     N_("authenticate with APOP") },
-  { "capa",       1, -1, com_capa,
+  { "capa",       1, -1, 0, com_capa,
     /* TRANSLATORS: -reread is a keyword; do not translate. */
     N_("[-reread] [NAME...]"),
     N_("list server capabilities") },
-  { "disconnect", 1, 1,
+  { "disconnect", 1, 1, 0,
     com_disconnect,
     NULL,
     N_("close connection") },
-  { "dele",       2, 2, com_dele,
+  { "dele",       2, 2, 0, com_dele,
     N_("NUMBER"),
     N_("mark message for deletion") },
-  { "list",       1, 2, com_list,
+  { "list",       1, 2, 0, com_list,
     N_("[NUMBER]"),
     N_("list messages") },
-  { "noop",       1, 1, com_noop,
+  { "noop",       1, 1, 0, com_noop,
     NULL,
     N_("send a \"no operation\"") },
-  { "pass",       1, 2, com_pass,
+  { "pass",       1, 2, 0, com_pass,
     N_("[PASSWORD]"),
     N_("send password") },
-  { "connect",    1, 4, com_connect,
+  { "connect",    1, 4, 0, com_connect,
     /* TRANSLATORS: --tls is a keyword. */
     N_("[-tls] HOSTNAME [PORT]"),
     N_("open connection") },
-  { "quit",       1, 1, com_quit,
+  { "quit",       1, 1, 0, com_quit,
     NULL,
     N_("quit pop3 session") },
-  { "retr",       2, 2, com_retr,
+  { "retr",       2, 2, 0, com_retr,
     "NUMBER",
     N_("retrieve a message") },
-  { "rset",       1, 1, com_rset,
+  { "rset",       1, 1, 0, com_rset,
     NULL,
     N_("remove deletion marks") },
-  { "stat",       1, 1, com_stat,
+  { "stat",       1, 1, 0, com_stat,
     NULL,
     N_("get the mailbox size and number of messages in it") },
-  { "stls",       1, 1, com_stls,
+  { "stls",       1, 1, 0, com_stls,
     NULL,
     N_("start TLS negotiation") },
-  { "top",        2, 3, com_top,
+  { "top",        2, 3, 0, com_top,
     "MSGNO [NUMBER]",
     N_("display message headers and first NUMBER (default 5) lines of"
        " its body") },
-  { "uidl",       1, 2, com_uidl,
+  { "uidl",       1, 2, 0, com_uidl,
     N_("[NUMBER]"),
     N_("show unique message identifiers") },
-  { "user",       2, 2, com_user,
+  { "user",       2, 2, 0, com_user,
     N_("NAME"),
     N_("send login") },
-  { "verbose",    1, 4, com_verbose,
+  { "verbose",    1, 4, 0, com_verbose,
     "[on|off|mask|unmask] [secure [payload]]",
     N_("control the protocol tracing") },
   { NULL }
diff --git a/mu/shell.c b/mu/shell.c
index 39081c1..0426788 100644
--- a/mu/shell.c
+++ b/mu/shell.c
@@ -71,18 +71,18 @@ static int shell_history (int, char **);
 #endif
 
 struct mutool_command default_comtab[] = {
-  { "prompt",   -1, -1, shell_prompt,
+  { "prompt",     1, 2, CMD_COALESCE_EXTRA_ARGS, shell_prompt,
     N_("STRING"),
     N_("set command prompt") },
-  { "exit",       1, 1, shell_exit,    NULL,        N_("exit program") },
-  { "help",       1, 2, shell_help,
+  { "exit",       1, 1, 0, shell_exit,    NULL,        N_("exit program") },
+  { "help",       1, 2, 0, shell_help,
     N_("[COMMAND]"),
     N_("display this text") },
-  { "?",          1, 1, shell_help,
+  { "?",          1, 1, 0, shell_help,
     N_("[COMMAND]"),
     N_("synonym for `help'") },
 #ifdef WITH_READLINE
-  { "history",    1, 1, shell_history,
+  { "history",    1, 1, 0, shell_history,
     NULL,
     N_("show command history") },
 #endif
@@ -492,59 +492,101 @@ add_history (const char *s MU_ARG_UNUSED)
 }
 #endif
 
+static int
+next_arg (struct mu_wordsplit *ws)
+{
+  int rc = mu_wordsplit (NULL, ws, MU_WRDSF_INCREMENTAL);
+  if (rc == MU_WRDSE_NOINPUT)
+    {
+      mu_error ("%s: too few arguments", ws->ws_wordv[0]);
+      mu_wordsplit_free (ws);
+      return -1;
+    }
+  else if (rc)
+    {
+      mu_error ("cannot parse input line: %s",
+               mu_wordsplit_strerror (ws));
+      return 1;
+    }
+  return 0;
+}
+
 /* Parse and execute a command line. */
 int
 execute_line (char *line)
 {
+  int rc;
   struct mu_wordsplit ws;
-  int argc;
-  char **argv;
+  struct mutool_command *cmd;
   int status = 0;
-
+  
   ws.ws_comment = "#";
-  ws.ws_offs = 1; /* Keep extra slot for expansion in case when argmin == -1 */
-  if (mu_wordsplit (line, &ws,
-                   MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT|MU_WRDSF_DOOFFS))
+  rc = mu_wordsplit (line, &ws,
+                    MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT|
+                    MU_WRDSF_INCREMENTAL|MU_WRDSF_APPEND);
+  if (rc == MU_WRDSE_NOINPUT)
+    {
+      mu_wordsplit_free (&ws);
+      return 0;
+    }
+  else if (rc)
     {
-      mu_error("cannot parse input line: %s", mu_wordsplit_strerror (&ws));
+      mu_error ("cannot parse input line: %s", mu_wordsplit_strerror (&ws));
       return 0;
     }
-  argc = ws.ws_wordc;
-  argv = ws.ws_wordv + 1;
   
-  if (argc >= 0)
+  if (ws.ws_wordc)
     {
-      struct mutool_command *cmd = find_command (argv[0]);
-      
+      int argmin;
+      cmd = find_command (ws.ws_wordv[0]);
+
       if (!cmd)
-       mu_error ("%s: no such command.", argv[0]);
-      else if (cmd->argmin > 0 && argc < cmd->argmin)
-       mu_error ("%s: too few arguments", argv[0]);
-      else if (cmd->argmax > 0 && argc > cmd->argmax)
-       mu_error ("%s: too many arguments", argv[0]);
+       {
+         mu_error ("%s: no such command.", ws.ws_wordv[0]);
+         mu_wordsplit_free (&ws);
+         return 0;
+       }
+
+      argmin = cmd->argmin;
+      if (cmd->flags & CMD_COALESCE_EXTRA_ARGS)
+       --argmin;
+      while (ws.ws_wordc < argmin)
+       {
+         if (next_arg (&ws))
+           return 0;
+       }
+
+      if (cmd->flags & CMD_COALESCE_EXTRA_ARGS)
+       {
+         ws.ws_flags |= MU_WRDSF_NOSPLIT;
+         if (next_arg (&ws))
+           return 0;
+       }
       else
        {
-         if (cmd->argmin <= 0 && argc != 2)
+         for (;;)
            {
-             size_t i;
-             char *word = mu_str_skip_class (line, MU_CTYPE_SPACE);
-             char *arg = mu_str_skip_class_comp (word, MU_CTYPE_SPACE);
-             if (*arg)
+             if (cmd->argmax > 0 && ws.ws_wordc > cmd->argmax)
+               {
+                 mu_error ("%s: too many arguments", ws.ws_wordv[0]);
+                 mu_wordsplit_free (&ws);
+                 return 0;
+               }
+             rc = mu_wordsplit (NULL, &ws, MU_WRDSF_INCREMENTAL);
+             if (rc == 0)
+               continue;
+             else if (rc == MU_WRDSE_NOINPUT)
+               break;
+             else
                {
-                 *arg++ = 0;
-                 arg = mu_str_skip_class (arg, MU_CTYPE_SPACE);
+                 mu_error ("cannot parse input line: %s",
+                           mu_wordsplit_strerror (&ws));
+                 return 0;
                }
-             for (i = 0; i < ws.ws_wordc; i++)
-               free (ws.ws_wordv[i + 1]);
-             ws.ws_wordv[0] = xstrdup (word);
-             ws.ws_wordv[1] = xstrdup (arg);
-             ws.ws_wordv[2] = NULL;
-             ws.ws_wordc = 2;
-             argc = ws.ws_wordc;
-             argv = ws.ws_wordv;
            }
-         status = cmd->func (argc, argv);
        }
+
+      status = cmd->func (ws.ws_wordc, ws.ws_wordv);
     }
   mu_wordsplit_free (&ws);
   return status;


hooks/post-receive
-- 
GNU Mailutils



reply via email to

[Prev in Thread] Current Thread [Next in Thread]