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-164-g4a49f53


From: Sergey Poznyakoff
Subject: [SCM] GNU Mailutils branch, master, updated. release-2.2-164-g4a49f53
Date: Thu, 28 Oct 2010 20:21:44 +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=4a49f5341ae9005c9ffbd7620efaecffcdfda79f

The branch, master has been updated
       via  4a49f5341ae9005c9ffbd7620efaecffcdfda79f (commit)
      from  697384c64dadb0a4db97136a6cbcf2166c48897b (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 4a49f5341ae9005c9ffbd7620efaecffcdfda79f
Author: Sergey Poznyakoff <address@hidden>
Date:   Thu Oct 28 23:20:22 2010 +0300

    Add wordsplit module.
    
    The wordsplit module (borrowed from another project of mine, called 'grecs')
    provides a flexible and powerful mechanism for parsing input in a way
    similar to posix-shell.  In particular, the POSIX function wordexp is
    a subset of the wordsplit functionality.
    
    The argcv module is now deprecated.
    
    * include/mailutils/wordsplit.h: New file.
    * include/mailutils/Makefile.am (pkginclude_HEADERS): Add wordsplit.h
    * libmailutils/string/wordsplit.c: New file.
    * libmailutils/string/Makefile.am (libstring_la_SOURCES): Add wordsplit.c.
    * include/mailutils/argcv.h (mu_argcv_join): New proto.
    (mu_argcv_get, mu_argcv_get_n, mu_argcv_get_np)
    (mu_argcv_unquote_char, mu_argcv_quote_char)
    (mu_argcv_quoted_length, mu_argcv_unquote_copy)
    (mu_argcv_quote_copy): Mark as deprecated.
    * include/mailutils/mailutils.h: Include wordsplit.h.
    
    * libmailutils/tests/wsp.c: New file.
    * libmailutils/tests/.gitignore: List wsp.
    * libmailutils/tests/wordsplit.at: New file.
    * libmailutils/tests/Makefile.am (noinst_PROGRAMS): Add wsp
    (TESTSUITE_AT): Add wordsplit.at.
    * libmailutils/tests/argcv.c: Define MU_ARCGV_DEPRECATED to
    avoid deprecation warnings.
    * libmailutils/tests/testsuite.at: Include wordsplit.at.
    
    * libmailutils/base/argcvfree.c: New file.
    * libmailutils/base/argcvjoin.c: New file.
    * libmailutils/base/argcvrem.c: New file.
    * libmailutils/base/Makefile.am (libbase_la_SOURCES): Add new files.
    
    * libmailutils/base/argcv.c: Define MU_ARCGV_DEPRECATED to
    avoid deprecation warnings.
    (argcv_string, argcv_free, argv_free)
    (mu_argcv_remove): Rewrite in separate modules.
    
    * comsat/comsat.h: Include wordsplit.h.
    * imap4d/imap4d.h: Likewise.
    * mail/mail.h: Likewise.
    * mh/mh.h: Likewise.
    * readmsg/readmsg.h: Likewise.
    
    * comsat/action.c: Use wordsplit.
    * imap4d/fetch.c: Likewise.
    * lib/mailcap.c: Likewise.
    * libmailutils/auth/mu_auth.c: Likewise.
    * libmailutils/base/mutil.c: Likewise.
    * libmailutils/cfg/format.c: Likewise.
    * libmailutils/cfg/lexer.l: Likewise.
    * libmailutils/cfg/parser.y: Likewise.
    * libmailutils/diag/gdebug.c: Likewise.
    * libmailutils/mailer/mailer.c: Likewise.
    * libmailutils/server/acl.c: Likewise.
    * libmailutils/stream/prog_stream.c: Likewise.
    * libmailutils/tests/listop.c: Likewise.
    * libmailutils/url/create.c: Likewise.
    * libmu_auth/ldap.c: Likewise.
    * libmu_auth/radius.c: Likewise.
    * libmu_sieve/sieve.l: Likewise.
    * libproto/mailer/mbox.c: Likewise.
    * libproto/mailer/smtp.c: Likewise.
    * libproto/mailer/smtp_gsasl.c: Likewise.
    * mail/mailline.c: Likewise.
    * mail/mailvar.c: Likewise.
    * mail/send.c: Likewise.
    * mail/util.c: Likewise.
    * mh/folder.c: Likewise.
    * mh/mh_alias.y: Likewise.
    * mh/mh_argp.c: Likewise.
    * mh/mh_init.c: Likewise.
    * mh/mh_list.c: Likewise.
    * mh/mh_msgset.c: Likewise.
    * mh/mh_sequence.c: Likewise.
    * mh/mh_whatnow.c: Likewise.
    * mh/mh_sequence.c: Likewise.
    * mh/mh_whatnow.c: Likewise.
    * mh/mhn.c: Likewise.
    * mh/send.c: Likewise.
    * movemail/movemail.c: Likewise.
    * mu/shell.c: Likewise.
    * readmsg/readmsg.c: Likewise.
    * testsuite/smtpsend.c: Likewise.
    
    * pop3d/popauth.c: Use wordsplit.
    Use simplified input format (username and password delimited
    by any amount of whitespace).
    Read/produce old format if invoked with the --compatibility option.
    
    * examples/aclck.c: Use wordsplit instead of argcv.
    * examples/header.c: Likewise.
    * examples/mta.c: Likewise.
    * examples/.gitignore: Remove pop3client.
    * examples/Makefile.am: Remove pop3client.
    
    * po/POTFILES.in: Add new files.

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

Summary of changes:
 comsat/action.c                                    |   40 +-
 comsat/comsat.h                                    |    2 +-
 examples/.gitignore                                |    1 -
 examples/Makefile.am                               |   16 +-
 examples/aclck.c                                   |   29 +-
 examples/header.c                                  |   34 +-
 examples/mta.c                                     |   52 +-
 examples/pop3client.c                              | 1189 ----------------
 imap4d/fetch.c                                     |  106 +-
 imap4d/imap4d.h                                    |    2 +-
 include/mailutils/Makefile.am                      |    3 +-
 include/mailutils/argcv.h                          |   64 +-
 include/mailutils/mailutils.h                      |    1 +
 include/mailutils/wordsplit.h                      |  135 ++
 lib/mailcap.c                                      |   34 +-
 libmailutils/auth/mu_auth.c                        |   26 +-
 libmailutils/base/Makefile.am                      |    3 +
 libmailutils/base/argcv.c                          |  112 +--
 libmailutils/{url/urlstr.c => base/argcvfree.c}    |   36 +-
 libmailutils/base/argcvjoin.c                      |  116 ++
 libmailutils/{url/get-secret.c => base/argcvrem.c} |   53 +-
 libmailutils/base/mutil.c                          |   19 +-
 libmailutils/cfg/format.c                          |    7 +-
 libmailutils/cfg/lexer.l                           |    4 +-
 libmailutils/cfg/parser.y                          |   77 +-
 libmailutils/diag/gdebug.c                         |   29 +-
 libmailutils/mailer/mailer.c                       |   30 +-
 libmailutils/server/acl.c                          |   18 +-
 libmailutils/stream/prog_stream.c                  |   13 +-
 libmailutils/string/Makefile.am                    |    1 +
 libmailutils/string/wordsplit.c                    | 1469 ++++++++++++++++++++
 libmailutils/tests/.gitignore                      |    1 +
 libmailutils/tests/Makefile.am                     |    6 +-
 libmailutils/tests/argcv.c                         |    1 +
 libmailutils/tests/listop.c                        |   76 +-
 libmailutils/tests/testsuite.at                    |    1 +
 libmailutils/tests/wordsplit.at                    |  304 ++++
 libmailutils/tests/wsp.c                           |  285 ++++
 libmailutils/url/copy.c                            |    2 +-
 libmailutils/url/create.c                          |   21 +-
 libmu_argp/muinit.c                                |    1 +
 libmu_auth/ldap.c                                  |   38 +-
 libmu_auth/radius.c                                |   32 +-
 libmu_sieve/sieve.l                                |    4 +-
 libproto/mailer/mbox.c                             |   36 +-
 libproto/mailer/smtp.c                             |   28 +-
 libproto/mailer/smtp_gsasl.c                       |   29 +-
 mail/mail.h                                        |    1 +
 mail/mailline.c                                    |   26 +-
 mail/mailvar.c                                     |   23 +-
 mail/send.c                                        |   19 +-
 mail/util.c                                        |   37 +-
 mh/folder.c                                        |   37 +-
 mh/mh.h                                            |    1 +
 mh/mh_alias.y                                      |   20 +-
 mh/mh_argp.c                                       |   22 +-
 mh/mh_init.c                                       |   25 +-
 mh/mh_list.c                                       |   36 +-
 mh/mh_msgset.c                                     |   17 +-
 mh/mh_sequence.c                                   |   28 +-
 mh/mh_whatnow.c                                    |   16 +-
 mh/mhn.c                                           |   64 +-
 mh/send.c                                          |   60 +-
 movemail/movemail.c                                |   18 +-
 mu/shell.c                                         |   35 +-
 po/POTFILES.in                                     |    6 +
 pop3d/popauth.c                                    |  115 +-
 readmsg/readmsg.c                                  |   32 +-
 readmsg/readmsg.h                                  |    2 +-
 testsuite/smtpsend.c                               |   21 +-
 70 files changed, 3272 insertions(+), 1975 deletions(-)
 delete mode 100644 examples/pop3client.c
 create mode 100644 include/mailutils/wordsplit.h
 copy libmailutils/{url/urlstr.c => base/argcvfree.c} (59%)
 create mode 100644 libmailutils/base/argcvjoin.c
 copy libmailutils/{url/get-secret.c => base/argcvrem.c} (55%)
 create mode 100644 libmailutils/string/wordsplit.c
 create mode 100644 libmailutils/tests/wordsplit.at
 create mode 100644 libmailutils/tests/wsp.c

diff --git a/comsat/action.c b/comsat/action.c
index 0e1e679..4a87013 100644
--- a/comsat/action.c
+++ b/comsat/action.c
@@ -204,7 +204,7 @@ expand_line (const char *str, mu_message_t msg)
          p++;
          if (*p)
            {
-             c = mu_argcv_unquote_char (*p);
+             c = mu_wordsplit_c_unquote_char (*p);
              obstack_1grow (&stk, c);
            }
          break;
@@ -300,7 +300,8 @@ action_exec (FILE *tty, int argc, char **argv)
 
   if (argv[0][0] != '/')
     {
-      mu_diag_output (MU_DIAG_ERROR, _("not an absolute pathname: %s"), 
argv[0]);
+      mu_diag_output (MU_DIAG_ERROR, _("not an absolute pathname: %s"),
+                     argv[0]);
       return;
     }
 
@@ -388,15 +389,14 @@ run_user_action (FILE *tty, const char *cr, mu_message_t 
msg)
       
       while ((n = act_getline (fp, &stmt, &size)))
        {
-         int argc;
-         char **argv;
+         struct mu_wordsplit ws;
 
-         if (mu_argcv_get (stmt, "", NULL, &argc, &argv) == 0
-             && argc
-             && argv[0][0] != '#')
+         ws.ws_comment = "#";
+         if (mu_wordsplit (stmt, &ws, MU_WRDSF_DEFFLAGS | MU_WRDSF_COMMENT)
+             && ws.ws_wordc)
            {
              mu_debug_set_locus (debug, rcname, line);
-             if (strcmp (argv[0], "beep") == 0)
+             if (strcmp (ws.ws_wordv[0], "beep") == 0)
                {
                  /* FIXME: excess arguments are ignored */
                  action_beep (tty);
@@ -406,25 +406,27 @@ run_user_action (FILE *tty, const char *cr, mu_message_t 
msg)
                {
                  /* Rest of actions require keyword expansion */
                  int i;
-                 int n_option = argc > 1 && strcmp (argv[1], "-n") == 0;
+                 int n_option = ws.ws_wordc > 1 &&
+                                strcmp (ws.ws_wordv[1], "-n") == 0;
                  
-                 for (i = 1; i < argc; i++)
+                 for (i = 1; i < ws.ws_wordc; i++)
                    {
-                     char *oldarg = argv[i];
-                     argv[i] = expand_line (argv[i], msg);
+                     char *oldarg = ws.ws_wordv[i];
+                     ws.ws_wordv[i] = expand_line (ws.ws_wordv[i], msg);
                      free (oldarg);
-                     if (!argv[i])
+                     if (!ws.ws_wordv[i])
                        break;
                    }
                  
-                 if (strcmp (argv[0], "echo") == 0)
+                 if (strcmp (ws.ws_wordv[0], "echo") == 0)
                    {
-                     action_echo (tty, cr, n_option, argc - 1, argv + 1);
+                     action_echo (tty, cr, n_option,
+                                  ws.ws_wordc - 1, ws.ws_wordv + 1);
                      nact++;
                    }
-                 else if (strcmp (argv[0], "exec") == 0)
+                 else if (strcmp (ws.ws_wordv[0], "exec") == 0)
                    {
-                     action_exec (tty, argc - 1, argv + 1);
+                     action_exec (tty, ws.ws_wordc - 1, ws.ws_wordv + 1);
                      nact++;
                    }
                  else
@@ -432,12 +434,12 @@ run_user_action (FILE *tty, const char *cr, mu_message_t 
msg)
                      fprintf (tty, _(".biffrc:%d: unknown keyword"), line);
                      fprintf (tty, "\r\n");
                      mu_diag_output (MU_DIAG_ERROR, _("unknown keyword %s"),
-                                     argv[0]);
+                                     ws.ws_wordv[0]);
                      break;
                    }
                } 
            }
-         mu_argcv_free (argc, argv);
+         mu_wordsplit_free (&ws);
          line += n;
        }
       fclose (fp);
diff --git a/comsat/comsat.h b/comsat/comsat.h
index 5148fa8..1c1381a 100644
--- a/comsat/comsat.h
+++ b/comsat/comsat.h
@@ -53,7 +53,7 @@
 #include <mailutils/registrar.h>
 #include <mailutils/stream.h>
 #include <mailutils/mu_auth.h>
-#include <mailutils/argcv.h>
+#include <mailutils/wordsplit.h>
 #include <mailutils/nls.h>
 #include <mailutils/daemon.h>
 #include <mailutils/acl.h>
diff --git a/examples/.gitignore b/examples/.gitignore
index b42a928..af4156b 100644
--- a/examples/.gitignore
+++ b/examples/.gitignore
@@ -20,6 +20,5 @@ muemail
 murun
 musocio
 nntpclient
-pop3client
 sfrom
 url-parse
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 1addf44..45ee0fc 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -15,10 +15,6 @@
 ## You should have received a copy of the GNU General Public License
 ## along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>.
 
-if MU_COND_SUPPORT_POP
-  POP3CLIENT = pop3client
-endif
-
 if MU_COND_SUPPORT_NNTP
   NNTPCLIENT = nntpclient
 endif
@@ -46,10 +42,9 @@ noinst_PROGRAMS = \
  murun\
  musocio\
  $(NNTPCLIENT)\
- $(POP3CLIENT)\
  sfrom
 
-EXTRA_PROGRAMS = pop3client nntpclient
+EXTRA_PROGRAMS = nntpclient
 
 ## NOTE: Numaddr must be an installable target, otherwise libtool
 ## will not create a shared library and `make check' will fail in
@@ -94,15 +89,6 @@ sfrom_LDADD =\
  @address@hidden
  ${MU_LIB_MAILUTILS}
 
-pop3client_CPPFLAGS = @MU_APP_COMMON_INCLUDES@
-pop3client_LDADD = \
- ../lib/libmuaux.a\
- ${MU_LIB_POP}\
- ${MU_LIB_AUTH}\
- @address@hidden
- ${MU_LIB_MAILUTILS}\
- @READLINE_LIBS@
-
 nntpclient_LDADD = \
  ../lib/libmuaux.a\
  ${MU_LIB_NNTP}\
diff --git a/examples/aclck.c b/examples/aclck.c
index d4d9b95..404e87f 100644
--- a/examples/aclck.c
+++ b/examples/aclck.c
@@ -62,8 +62,8 @@ read_rules (FILE *fp)
 {
   char buf[512];
   int line = 0;
-  int argc = 0;
-  char **argv;
+  int wsflags = MU_WRDSF_DEFFLAGS | MU_WRDSF_COMMENT;
+  struct mu_wordsplit ws;
   int rc;
   
   rc = mu_acl_create (&acl);
@@ -72,7 +72,8 @@ read_rules (FILE *fp)
       mu_error ("cannot create acl: %s", mu_strerror (rc));
       exit (1);
     }
-  
+
+  ws.ws_comment = "#";
   while (fgets (buf, sizeof buf, fp))
     {
       unsigned long netmask;
@@ -95,17 +96,20 @@ read_rules (FILE *fp)
       if (buf[0] == '#')
        continue;
 
-      if (argc)
-       mu_argcv_free (argc, argv);
-
-      mu_argcv_get (buf, " \t", "#", &argc, &argv);
-      if (argc < 2)
+      if (mu_wordsplit (buf, &ws, wsflags))
+       {
+         mu_error ("cannot split line `%s': %s", buf,
+                   mu_wordsplit_strerror (&ws));
+         continue;
+       }
+      wsflags |= MU_WRDSF_REUSE;
+      if (ws.ws_wordc < 2)
        {
          mu_error ("%d: invalid input", line);
          continue;
        }
 
-      p = strchr (argv[1], '/');
+      p = strchr (ws.ws_wordv[1], '/');
       if (p)
        {
          char *q;
@@ -144,7 +148,7 @@ read_rules (FILE *fp)
       else
        netmask = 0xfffffffful;
       
-      sa = parse_address (&salen, argv[1]);
+      sa = parse_address (&salen, ws.ws_wordv[1]);
       
       /* accept addr
         deny addr
@@ -152,7 +156,7 @@ read_rules (FILE *fp)
         exec addr [rest ...]
         execif addr rest ....]
       */
-      if (mu_acl_string_to_action (argv[0], &action))
+      if (mu_acl_string_to_action (ws.ws_wordv[0], &action))
        {
          mu_error ("%d: invalid command", line);
          continue;
@@ -167,7 +171,7 @@ read_rules (FILE *fp)
        case mu_acl_log:
        case mu_acl_exec:
        case mu_acl_ifexec:
-         data = strdup (argv[2]);
+         data = strdup (ws.ws_wordv[2]);
        }
 
       rc = mu_acl_append (acl, action, data, sa, salen, netmask);
@@ -175,6 +179,7 @@ read_rules (FILE *fp)
        mu_error ("%d: cannot append acl entry: %s", line,
                  mu_strerror (rc));
     }
+  mu_wordsplit_free (&ws);
 }
 
 int
diff --git a/examples/header.c b/examples/header.c
index 1d6c790..18357a4 100644
--- a/examples/header.c
+++ b/examples/header.c
@@ -489,9 +489,9 @@ main (int argc, char **argv)
 {
   int c;
   char buf[512];
-  char **prevv;
+  char **prevv = NULL;
   int prevc = 0;
-  
+
   interactive = isatty (0);
   while ((c = getopt (argc, argv, "f:h")) != EOF)
     {
@@ -525,36 +525,36 @@ main (int argc, char **argv)
        }
     }
   
-  while (prompt(0), fgets(buf, sizeof buf, stdin))
+  while (prompt (0), fgets (buf, sizeof buf, stdin))
     {
-      int c;
-      char **v;
-      int status;
+      struct mu_wordsplit ws;
 
       line_num++;
-      status = mu_argcv_get (buf, NULL, "#", &c, &v);
-      if (status)
+      ws.ws_comment = "#";
+      if (mu_wordsplit (buf, &ws, MU_WRDSF_DEFFLAGS | MU_WRDSF_COMMENT))
        {
-         mu_error ("%u: cannot parse: %s",
-                  line_num, mu_strerror (status));
+         mu_error ("cannot split line `%s': %s", buf,
+                   mu_wordsplit_strerror (&ws));
          continue;
        }
-
-      if (c == 0)
+      
+      if (ws.ws_wordc == 0)
        {
          if (prevc)
            docmd (prevc, prevv);
-         else
-           mu_argcv_free (c, v);
        }
       else
        {
-         docmd (c, v);
+         docmd (ws.ws_wordc, ws.ws_wordv);
          mu_argcv_free (prevc, prevv);
-         prevc = c;
-         prevv = v;
+         prevc = ws.ws_wordc;
+         prevv = ws.ws_wordv;
+         ws.ws_wordc = 0;
+         ws.ws_wordv = NULL;
        }
+      mu_wordsplit_free (&ws);
     }
+  mu_argcv_free (prevc, prevv);
   exit (0);
 }
 
diff --git a/examples/mta.c b/examples/mta.c
index 705a2b0..bff83d5 100644
--- a/examples/mta.c
+++ b/examples/mta.c
@@ -565,30 +565,31 @@ smtp (mu_stream_t str)
   char *buf = NULL;
   size_t size = 0;
   char *rcpt_addr;
+  int wsflags = MU_WRDSF_DEFFLAGS;
+  struct mu_wordsplit ws;
   
   smtp_reply (str, 220, "Ready");
   for (state = STATE_INIT; state != STATE_QUIT; )
     {
-      char *s;
-      int argc;
-      char **argv;
       int kw;
       size_t len;
       
       if (mu_stream_getline (str, &buf, &size, &len) || len == 0)
        exit (EX_PROTOCOL);
 
-      s = mu_str_stripws (buf);
-      
-      if (mu_argcv_get (s, "", NULL, &argc, &argv))
-       exit (EX_UNAVAILABLE);
+      if (mu_wordsplit (buf, &ws, wsflags))
+       {
+         mu_error ("cannot split line `%s': %s", buf,
+                   mu_wordsplit_strerror (&ws));
+         exit (EX_UNAVAILABLE);
+       }
+      wsflags |= MU_WRDSF_REUSE;
 
-      kw = smtp_kw (argv[0]);
+      kw = smtp_kw (ws.ws_wordv[0]);
       if (kw == KW_QUIT)
        {
          smtp_reply (str, 221, "Done");
          state = STATE_QUIT;
-         mu_argcv_free (argc, argv);
          continue;
        }
       
@@ -599,13 +600,14 @@ smtp (mu_stream_t str)
            {
            case KW_EHLO:
            case KW_HELO:
-             if (argc == 2)
+             if (ws.ws_wordc == 2)
                {
                  smtp_reply (str, 250, "pleased to meet you");
                  state = STATE_EHLO;
                }
              else
-               smtp_reply (str, 501, "%s requires domain address", argv[0]);
+               smtp_reply (str, 501, "%s requires domain address",
+                           ws.ws_wordv[0]);
              break;
 
            default:
@@ -618,10 +620,11 @@ smtp (mu_stream_t str)
          switch (kw)
            {
            case KW_MAIL:
-             if (argc == 2)
-               from_person = check_prefix (argv[1], "from:");
-             else if (argc == 3 && mu_c_strcasecmp (argv[1], "from:") == 0)
-               from_person = argv[2];
+             if (ws.ws_wordc == 2)
+               from_person = check_prefix (ws.ws_wordv[1], "from:");
+             else if (ws.ws_wordc == 3 &&
+                      mu_c_strcasecmp (ws.ws_wordv[1], "from:") == 0)
+               from_person = ws.ws_wordv[2];
              else
                from_person = NULL;
 
@@ -644,10 +647,11 @@ smtp (mu_stream_t str)
          switch (kw)
            {
            case KW_RCPT:
-             if (argc == 2)
-               rcpt_addr = check_prefix (argv[1], "to:");
-             else if (argc == 3 && mu_c_strcasecmp (argv[1], "to:") == 0)
-               rcpt_addr = argv[2];
+             if (ws.ws_wordc == 2)
+               rcpt_addr = check_prefix (ws.ws_wordv[1], "to:");
+             else if (ws.ws_wordc == 3 &&
+                      mu_c_strcasecmp (ws.ws_wordv[1], "to:") == 0)
+               rcpt_addr = ws.ws_wordv[2];
              else
                rcpt_addr = NULL;
              
@@ -674,10 +678,11 @@ smtp (mu_stream_t str)
          switch (kw)
            {
            case KW_RCPT:
-             if (argc == 2)
-               rcpt_addr = check_prefix (argv[1], "to:");
-             else if (argc == 3 && mu_c_strcasecmp (argv[1], "to:") == 0)
-               rcpt_addr = argv[2];
+             if (ws.ws_wordc == 2)
+               rcpt_addr = check_prefix (ws.ws_wordv[1], "to:");
+             else if (ws.ws_wordc == 3 &&
+                      mu_c_strcasecmp (ws.ws_wordv[1], "to:") == 0)
+               rcpt_addr = ws.ws_wordv[2];
              else
                rcpt_addr = NULL;
              
@@ -734,7 +739,6 @@ smtp (mu_stream_t str)
          break;
 
        }
-      mu_argcv_free (argc, argv);
     }
 }
 
diff --git a/examples/pop3client.c b/examples/pop3client.c
deleted file mode 100644
index 7b1f864..0000000
--- a/examples/pop3client.c
+++ /dev/null
@@ -1,1189 +0,0 @@
-/* pop3client.c -- An application which demonstrates how to use the
-   GNU Mailutils pop3 functions.  This application interactively allows users
-   to contact a pop3 server.
-
-   Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2010 Free Software
-   Foundation, Inc.
-
-   GNU Mailutils 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 3, or (at your option)
-   any later version.
-
-   GNU Mailutils 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 GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>  
-#endif 
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <strings.h>
-#include <stdlib.h>
-#include <termios.h>
-#include <signal.h>
-#include <xalloc.h>
-
-#ifdef WITH_READLINE
-# include <readline/readline.h>
-# include <readline/history.h>
-#endif
-
-#include <mailutils/pop3.h>
-#include <mailutils/iterator.h>
-#include <mailutils/error.h>
-#include <mailutils/errno.h>
-#include <mailutils/vartab.h>
-#include <mailutils/argcv.h>
-#include <mailutils/cctype.h>
-#include <mailutils/cstr.h>
-#include <mailutils/tls.h>
-#include <mailutils/util.h>
-
-/* A structure which contains information on the commands this program
-   can understand. */
-
-typedef struct
-{
-  const char *name;            /* User printable name of the function. */
-  int argmin;
-  int argmax;
-  int (*func) (int, char **);  /* Function to call to do the job. */
-  const char *doc;             /* Documentation for this function.  */
-}
-COMMAND;
-
-/* The names of functions that actually do the manipulation. */
-int com_apop (int, char **);
-int com_capa (int, char **);
-int com_disconnect (int, char **);
-int com_dele (int, char **);
-int com_exit (int, char **);
-int com_help (int, char **);
-int com_list (int, char **);
-int com_noop (int, char **);
-int com_connect (int, char **);
-int com_pass (int, char **);
-int com_quit (int, char **);
-int com_retr (int, char **);
-int com_rset (int, char **);
-int com_stat (int, char **);
-int com_top (int, char **);
-int com_uidl (int, char **);
-int com_user (int, char **);
-int com_verbose (int, char **);
-int com_prompt (int, char **);
-int com_stls (int, char **);
-int com_history (int, char **);
-
-COMMAND *find_command (char *);
-
-COMMAND commands[] = {
-  { "apop",       3,  3, com_apop,
-    "Authenticate with APOP: APOP user secret" },
-  { "capa",       1, -1, com_capa,
-    "List capabilities: capa [-reread] [names...]" },
-  { "disconnect", 1, 1,
-    com_disconnect, "Close connection: disconnect" },
-  { "dele",       2, 2, com_dele,
-    "Mark message: DELE msgno" },
-  { "exit",       1, 1, com_exit,
-    "exit program" },
-  { "help",       1, 1, com_help,
-    "Display this text" },
-  { "?",          1, 1, com_help,
-    "Synonym for `help'" },
-  { "list",       1, 2, com_list,
-    "List messages: LIST [msgno]" },
-  { "noop",       1, 1, com_noop,
-    "Send no operation: NOOP" },
-  { "pass",       1, 2, com_pass,
-    "Send passwd: PASS [passwd]" },
-  { "prompt",     -1, -1, com_prompt,
-    "Set command prompt" },
-  { "connect",    1, 4, com_connect,
-    "Open connection: connect [-tls] hostname port" },
-  { "quit",       1, 1, com_quit,
-    "Go to Update state : QUIT" },
-  { "retr",       2, 2, com_retr,
-    "Dowload message: RETR msgno" },
-  { "rset",       1, 1, com_rset,
-    "Unmark all messages: RSET" },
-  { "stat",       1, 1, com_stat,
-    "Get the size and count of mailbox : STAT" },
-  { "stls",       1, 1, com_stls,
-    "Start TLS negotiation" },
-  { "top",        2, 3, com_top,
-    "Get the header of message: TOP msgno [lines]" },
-  { "uidl",       1, 2, com_uidl,
-    "Get the unique id of message: UIDL [msgno]" },
-  { "user",       2, 2, com_user,
-    "send login: USER user" },
-  { "verbose",    1, 4, com_verbose,
-    "Enable Protocol tracing: verbose [on|off|mask|unmask] [x1 [x2]]" },
-#ifdef WITH_READLINE
-  { "history",    1, 1, com_history,
-    "Show command history" },
-#endif
-  { NULL }
-};
-
-/* Global handle for pop3.  */
-mu_pop3_t pop3;
-
-/* Flag if verbosity is needed.  */
-int verbose;
-#define VERBOSE_MASK(n) (1<<((n)+1))
-#define SET_VERBOSE_MASK(n) (verbose |= VERBOSE_MASK (n))
-#define CLR_VERBOSE_MASK(n) (verbose &= VERBOSE_MASK (n))
-#define QRY_VERBOSE_MASK(n) (verbose & VERBOSE_MASK (n))
-#define HAS_VERBOSE_MASK(n) (verbose & ~1)
-#define SET_VERBOSE() (verbose |= 1)
-#define CLR_VERBOSE() (verbose &= ~1)
-#define QRY_VERBOSE() (verbose & 1)
-
-/* When non-zero, this global means the user is done using this program. */
-int done;
-
-enum pop_session_status
-  {
-    pop_session_disconnected,
-    pop_session_connected,
-    pop_session_logged_in
-  };
-
-enum pop_session_status pop_session_status;
-
-int connect_argc;
-char **connect_argv;
-
-/* Host we are connected to. */
-#define host connect_argv[0]
-int port = 110;
-char *username;
-
-/* Command line prompt */
-#define DEFAULT_PROMPT "pop3> "
-char *prompt;
-
-const char *
-pop_session_str (enum pop_session_status stat)
-{
-  switch (stat)
-    {
-    case pop_session_disconnected:
-      return "disconnected";
-      
-    case pop_session_connected:
-      return "connected";
-      
-    case pop_session_logged_in:
-      return "logged in";
-    }
-  return "unknown";
-}
-
-char *
-expand_prompt ()
-{
-  mu_vartab_t vtab;
-  char *str;
-  
-  if (mu_vartab_create (&vtab))
-    return strdup (prompt);
-  mu_vartab_define (vtab, "user",
-                   (pop_session_status == pop_session_logged_in) ?
-                     username : "not logged in", 1);
-  mu_vartab_define (vtab, "host",
-                   (pop_session_status != pop_session_disconnected) ?
-                     host : "not connected", 1);
-  mu_vartab_define (vtab, "program-name", mu_program_name, 1);
-  mu_vartab_define (vtab, "canonical-program-name", "pop3client", 1);
-  mu_vartab_define (vtab, "package", PACKAGE, 1);
-  mu_vartab_define (vtab, "version", PACKAGE_VERSION, 1);
-  mu_vartab_define (vtab, "status", pop_session_str (pop_session_status), 1);
-  
-  if (mu_vartab_expand (vtab, prompt, &str))
-    str = strdup (prompt);
-  mu_vartab_destroy (&vtab);
-  return str;
-}
-
-
-
-#ifdef WITH_READLINE
-#define HISTFILE_SUFFIX "_history"
-
-static char *
-get_history_file_name ()
-{
-  static char *filename = NULL;
-
-  if (!filename)
-    {
-      char *hname;
-      
-      hname = xmalloc(3 + strlen (rl_readline_name) + sizeof HISTFILE_SUFFIX);
-      strcpy (hname, "~/.");
-      strcat (hname, rl_readline_name);
-      strcat (hname, HISTFILE_SUFFIX);
-      filename = mu_tilde_expansion (hname, "/", NULL);
-      free(hname);
-    }
-  return filename;
-}
-
-/* Interface to Readline Completion */
-
-char *command_generator (const char *, int);
-char **pop_completion (char *, int, int);
-
-/* Tell the GNU Readline library how to complete.  We want to try to complete
-   on command names if this is the first word in the line, or on filenames
-   if not. */
-void
-initialize_readline ()
-{
-  /* Allow conditional parsing of the ~/.inputrc file. */
-  rl_readline_name = (char *) "pop3client";
-
-  /* Tell the completer that we want a crack first. */
-  rl_attempted_completion_function = (CPPFunction *) pop_completion;
-
-  read_history (get_history_file_name ());
-}
-
-void
-finish_readline ()
-{
-  write_history (get_history_file_name());
-}
-
-/* Attempt to complete on the contents of TEXT.  START and END bound the
-   region of rl_line_buffer that contains the word to complete.  TEXT is
-   the word to complete.  We can use the entire contents of rl_line_buffer
-   in case we want to do some simple parsing.  Return the array of matches,
-   or NULL if there aren't any. */
-char **
-pop_completion (char *text, int start, int end MU_ARG_UNUSED)
-{
-  char **matches;
-
-  matches = (char **) NULL;
-
-  /* If this word is at the start of the line, then it is a command
-     to complete.  Otherwise it is the name of a file in the current
-     directory. */
-  if (start == 0)
-    matches = rl_completion_matches (text, command_generator);
-
-  return (matches);
-}
-
-/* Generator function for command completion.  STATE lets us know whether
-   to start from scratch; without any state (i.e. STATE == 0), then we
-   start at the top of the list. */
-char *
-command_generator (const char *text, int state)
-{
-  static int list_index, len;
-  const char *name;
-
-  /* If this is a new word to complete, initialize now.  This includes
-     saving the length of TEXT for efficiency, and initializing the index
-     variable to 0. */
-  if (!state)
-    {
-      list_index = 0;
-      len = strlen (text);
-    }
-
-  /* Return the next name which partially matches from the command list. */
-  while ((name = commands[list_index].name))
-    {
-      list_index++;
-
-      if (strncmp (name, text, len) == 0)
-       return xstrdup (name);
-    }
-
-  /* If no names matched, then return NULL. */
-  return NULL;
-}
-
-static char *pre_input_line;
-
-static int
-pre_input (void)
-{
-  rl_insert_text (pre_input_line);
-  free (pre_input_line);
-  rl_pre_input_hook = NULL;
-  rl_redisplay ();
-  return 0;
-}
-
-static int
-retrieve_history (char *str)
-{
-  char *out;
-  int rc;
-
-  rc = history_expand (str, &out);
-  switch (rc)
-    {
-    case -1:
-      mu_error ("%s", out);
-      free (out);
-      return 1;
-
-    case 0:
-      break;
-
-    case 1:
-      pre_input_line = out;
-      rl_pre_input_hook = pre_input;
-      return 1;
-
-    case 2:
-      printf ("%s\n", out);
-      free (out);
-      return 1;
-    }
-  return 0;
-}
-
-int
-com_history (int argc, char **argv)
-{
-  int i;
-  HIST_ENTRY **hlist;
-
-  hlist = history_list ();
-  for (i = 0; i < history_length; i++)
-    printf ("%4d) %s\n", i + 1, hlist[i]->line);
-
-  return 0;
-}
-
-#else
-# define initialize_readline()
-# define finish_readline()
-
-char *
-readline (char *prompt)
-{
-  char buf[255];
-
-  if (prompt)
-    {
-      printf ("%s", prompt);
-      fflush (stdout);
-    }
-
-  if (!fgets (buf, sizeof (buf), stdin))
-    return NULL;
-  return xstrdup (buf);
-}
-
-void
-add_history (const char *s MU_ARG_UNUSED)
-{
-}
-#endif
-
-int
-get_bool (const char *str, int *pb)
-{
-  if (mu_c_strcasecmp (str, "yes") == 0
-      || mu_c_strcasecmp (str, "on") == 0
-      || mu_c_strcasecmp (str, "true") == 0)
-    *pb = 1;
-  else if (mu_c_strcasecmp (str, "no") == 0
-      || mu_c_strcasecmp (str, "off") == 0
-      || mu_c_strcasecmp (str, "false") == 0)
-    *pb = 0;
-  else
-    return 1;
-
-  return 0;
-}
-
-int
-get_port (const char *port_str, int *pn)
-{
-  short port_num;
-  long num;
-  char *p;
-  
-  num = port_num = strtol (port_str, &p, 0);
-  if (*p == 0)
-    {
-      if (num != port_num)
-       {
-         mu_error ("bad port number: %s", port_str);
-         return 1;
-       }
-    }
-  else
-    {
-      struct servent *sp = getservbyname (port_str, "tcp");
-      if (!sp)
-       {
-         mu_error ("unknown port name");
-         return 1;
-       }
-      port_num = ntohs (sp->s_port);
-    }
-  *pn = port_num;
-  return 0;
-}
-
-/* Parse and execute a command line. */
-int
-execute_line (char *line)
-{
-  int argc;
-  char **argv;
-  int status = 0;
-
-  if (mu_argcv_get (line, NULL, "#", &argc, &argv))
-    {
-      mu_error("cannot parse input line");
-      return 0;
-    }
-
-  if (argc >= 0)
-    {
-      COMMAND *command = find_command (argv[0]);
-      
-      if (!command)
-       mu_error ("%s: no such command.", argv[0]);
-      else if (command->argmin > 0 && argc < command->argmin)
-       mu_error ("%s: too few arguments", argv[0]);
-      else if (command->argmax > 0 && argc > command->argmax)
-       mu_error ("%s: too many arguments", argv[0]);
-      else
-       {
-         if (command->argmin <= 0 && argc != 2)
-           {
-             char *word = mu_str_skip_class (line, MU_CTYPE_SPACE);
-             char *arg = mu_str_skip_class_comp (word, MU_CTYPE_SPACE);
-             if (*arg)
-               {
-                 *arg++ = 0;
-                 arg = mu_str_skip_class (arg, MU_CTYPE_SPACE);
-               }
-             
-             mu_argcv_free (argc, argv);
-             argc = 2;
-             argv = xcalloc (argc + 1, sizeof (argv[0]));
-             argv[0] = xstrdup (word);
-             argv[1] = xstrdup (arg);
-             argv[2] = NULL;
-           }
-         status = command->func (argc, argv);
-       }
-    }
-  mu_argcv_free (argc, argv);
-  return status;
-}
-
-/* Look up NAME as the name of a command, and return a pointer to that
-   command.  Return a NULL pointer if NAME isn't a command name. */
-COMMAND *
-find_command (char *name)
-{
-  COMMAND *cp;
-
-  for (cp = commands; cp->name; cp++)
-    if (strcmp (cp->name, name) == 0)
-      return cp;
-
-  return NULL;
-}
-
-static int
-string_to_xlev (const char *name, int *pv)
-{
-  if (strcmp (name, "secure") == 0)
-    *pv = MU_XSCRIPT_SECURE;
-  else if (strcmp (name, "payload") == 0)
-    *pv = MU_XSCRIPT_PAYLOAD;
-  else
-    return 1;
-  return 0;
-}
-
-static int
-change_verbose_mask (int set, int argc, char **argv)
-{
-  int i;
-  
-  for (i = 0; i < argc; i++)
-    {
-      int lev;
-      
-      if (string_to_xlev (argv[i], &lev))
-       {
-         mu_error ("unknown level: %s", argv[i]);
-         return 1;
-       }
-      if (set)
-       SET_VERBOSE_MASK (lev);
-      else
-       CLR_VERBOSE_MASK (lev);
-    }
-  return 0;
-}
-
-void
-set_verbose (mu_pop3_t p)
-{
-  if (p)
-    {
-      if (QRY_VERBOSE ())
-       {
-         mu_pop3_trace (p, MU_POP3_TRACE_SET);
-       }
-      else
-       mu_pop3_trace (p, MU_POP3_TRACE_CLR);
-    }
-}
-
-void
-set_verbose_mask (mu_pop3_t p)
-{
-  if (p)
-    {
-      mu_pop3_trace_mask (p, QRY_VERBOSE_MASK (MU_XSCRIPT_SECURE)
-                                 ? MU_POP3_TRACE_SET : MU_POP3_TRACE_CLR,
-                             MU_XSCRIPT_SECURE);
-      mu_pop3_trace_mask (p, QRY_VERBOSE_MASK (MU_XSCRIPT_PAYLOAD)
-                                 ? MU_POP3_TRACE_SET : MU_POP3_TRACE_CLR,
-                             MU_XSCRIPT_PAYLOAD);
-    }
-}
-
-int
-com_verbose (int argc, char **argv)
-{
-  if (argc == 1)
-    {
-      if (QRY_VERBOSE ())
-       {
-         printf ("verbose is on");
-         if (HAS_VERBOSE_MASK ())
-           {
-             char *delim = " (";
-           
-             if (QRY_VERBOSE_MASK (MU_XSCRIPT_SECURE))
-               {
-                 printf("%ssecure", delim);
-                 delim = ", ";
-               }
-             if (QRY_VERBOSE_MASK (MU_XSCRIPT_PAYLOAD))
-               printf("%spayload", delim);
-             printf (")");
-           }
-         printf ("\n");
-       }
-      else
-       printf ("verbose is off\n");
-    }
-  else
-    {
-      int bv;
-
-      if (get_bool (argv[1], &bv) == 0)
-       {
-         verbose |= bv;
-         if (argc > 2)
-           change_verbose_mask (verbose, argc - 2, argv + 2);
-         set_verbose (pop3);
-       }
-      else if (strcmp (argv[1], "mask") == 0)
-       change_verbose_mask (1, argc - 2, argv + 2);
-      else if (strcmp (argv[1], "unmask") == 0)
-       change_verbose_mask (0, argc - 2, argv + 2);
-      else
-       mu_error ("unknown subcommand");
-      set_verbose_mask (pop3);
-    }
-
-  return 0;
-}
-
-int
-com_user (int argc, char **argv)
-{
-  int status;
-  
-  status = mu_pop3_user (pop3, argv[1]);
-  if (status == 0)
-    username = strdup (argv[1]);
-  return status;
-}
-
-int
-com_apop (int argc, char **argv)
-{
-  int status;
-
-  status = mu_pop3_apop (pop3, argv[1], argv[2]);
-  if (status == 0)
-    {
-      username = strdup (argv[1]);
-      pop_session_status = pop_session_logged_in;
-    }
-  return status;
-}
-
-int
-com_capa (int argc, char **argv)
-{
-  mu_iterator_t iterator = NULL;
-  int status = 0;
-  int reread = 0;
-  int i = 1;
-  
-  for (i = 1; i < argc; i++)
-    {
-      if (strcmp (argv[i], "-reread") == 0)
-       reread = 1;
-      else
-       break;
-    }
-
-  if (i < argc)
-    {
-      if (reread)
-       {
-         status = mu_pop3_capa (pop3, 1, NULL);
-         if (status)
-           return status;
-       }
-      for (; i < argc; i++)
-       {
-         const char *elt;
-         int rc = mu_pop3_capa_test (pop3, argv[i], &elt);
-         switch (rc)
-           {
-           case 0:
-             if (*elt)
-               printf ("%s: %s\n", argv[i], elt);
-             else
-               printf ("%s is set\n", argv[i]);
-             break;
-
-           case MU_ERR_NOENT:
-             printf ("%s is not set\n", argv[i]);
-             break;
-
-           default:
-             return rc;
-           }
-       }
-    }
-  else
-    {
-      status = mu_pop3_capa (pop3, reread, &iterator);
-
-      if (status == 0)
-       {
-         for (mu_iterator_first (iterator);
-              !mu_iterator_is_done (iterator); mu_iterator_next (iterator))
-           {
-             char *capa = NULL;
-             mu_iterator_current (iterator, (void **) &capa);
-             printf ("CAPA: %s\n", capa ? capa : "");
-           }
-         mu_iterator_destroy (&iterator);
-       }
-    }
-  return status;
-}
-
-int
-com_uidl (int argc, char **argv)
-{
-  int status = 0;
-  if (argc == 1)
-    {
-      mu_iterator_t uidl_iterator = NULL;
-      status = mu_pop3_uidl_all (pop3, &uidl_iterator);
-      if (status == 0)
-       {
-         for (mu_iterator_first (uidl_iterator);
-              !mu_iterator_is_done (uidl_iterator);
-              mu_iterator_next (uidl_iterator))
-           {
-             char *uidl = NULL;
-             mu_iterator_current (uidl_iterator, (void **) &uidl);
-             printf ("UIDL: %s\n", uidl ? uidl : "");
-           }
-         mu_iterator_destroy (&uidl_iterator);
-       }
-    }
-  else
-    {
-      char *uidl = NULL;
-      unsigned int msgno = strtoul (argv[1], NULL, 10);
-      status = mu_pop3_uidl (pop3, msgno, &uidl);
-      if (status == 0)
-       printf ("Msg: %d UIDL: %s\n", msgno, uidl ? uidl : "");
-      free (uidl);
-    }
-  return status;
-}
-
-int
-com_list (int argc, char **argv)
-{
-  int status = 0;
-  if (argc == 1)
-    {
-      mu_iterator_t list_iterator;
-      status = mu_pop3_list_all (pop3, &list_iterator);
-      if (status == 0)
-       {
-         for (mu_iterator_first (list_iterator);
-              !mu_iterator_is_done (list_iterator);
-              mu_iterator_next (list_iterator))
-           {
-             char *list = NULL;
-             mu_iterator_current (list_iterator, (void **) &list);
-             printf ("LIST: %s\n", (list) ? list : "");
-           }
-         mu_iterator_destroy (&list_iterator);
-       }
-    }
-  else
-    {
-      size_t size = 0;
-      unsigned int msgno = strtoul (argv[1], NULL, 10);
-      status = mu_pop3_list (pop3, msgno, &size);
-      if (status == 0)
-       printf ("Msg: %u Size: %lu\n", msgno, (unsigned long) size);
-    }
-  return status;
-}
-
-int
-com_noop (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
-{
-  return mu_pop3_noop (pop3);
-}
-
-static void
-echo_off (struct termios *stored_settings)
-{
-  struct termios new_settings;
-  tcgetattr (0, stored_settings);
-  new_settings = *stored_settings;
-  new_settings.c_lflag &= (~ECHO);
-  tcsetattr (0, TCSANOW, &new_settings);
-}
-
-static void
-echo_on (struct termios *stored_settings)
-{
-  tcsetattr (0, TCSANOW, stored_settings);
-}
-
-int
-com_prompt (int argc, char **argv)
-{
-  int quote;
-  size_t size;
-  
-  free (prompt);
-  size = mu_argcv_quoted_length (argv[1], &quote);
-  prompt = malloc (size + 1);
-  if (!prompt)
-    {
-      mu_error ("Memory exhausted");
-      exit (1);
-    }
-  mu_argcv_unquote_copy (prompt, argv[1], size);
-  return 0;
-}
-
-int
-com_pass (int argc, char **argv)
-{
-  int status;
-  char pass[256];
-  char *pwd;
-  
-  if (argc == 1)
-    {
-      struct termios stored_settings;
-
-      printf ("passwd:");
-      fflush (stdout);
-      echo_off (&stored_settings);
-      fgets (pass, sizeof pass, stdin);
-      echo_on (&stored_settings);
-      putchar ('\n');
-      fflush (stdout);
-      pass[strlen (pass) - 1] = '\0';  /* nuke the trailing line.  */
-      pwd = pass;
-    }
-  else
-    pwd = argv[1];
-  status = mu_pop3_pass (pop3, pwd);
-  if (status == 0)
-    pop_session_status = pop_session_logged_in;
-  return status;
-}
-
-int
-com_stat (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
-{
-  size_t count = 0;
-  mu_off_t size = 0;
-  int status = 0;
-
-  status = mu_pop3_stat (pop3, &count, &size);
-  printf ("Mesgs: %lu Size %lu\n",
-         (unsigned long) count, (unsigned long) size);
-  return status;
-}
-
-int
-com_stls (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
-{
-  return mu_pop3_stls (pop3);
-}
-
-int
-com_dele (int argc, char **argv)
-{
-  unsigned msgno;
-  msgno = strtoul (argv[1], NULL, 10);
-  return mu_pop3_dele (pop3, msgno);
-}
-
-/* Print out help for ARG, or for all of the commands if ARG is
-   not present. */
-int
-com_help (int argc, char **argv)
-{
-  int i;
-  int printed = 0;
-  char *name = argv[1];
-  
-  for (i = 0; commands[i].name; i++)
-    {
-      if (!name || (strcmp (name, commands[i].name) == 0))
-       {
-         printf ("%s\t\t%s.\n", commands[i].name, commands[i].doc);
-         printed++;
-       }
-    }
-
-  if (!printed)
-    {
-      printf ("No commands match `%s'.  Possibilties are:\n", name);
-
-      for (i = 0; commands[i].name; i++)
-       {
-         /* Print in six columns. */
-         if (printed == 6)
-           {
-             printed = 0;
-             printf ("\n");
-           }
-
-         printf ("%s\t", commands[i].name);
-         printed++;
-       }
-
-      if (printed)
-       printf ("\n");
-    }
-  return 0;
-}
-
-int
-com_rset (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
-{
-  return mu_pop3_rset (pop3);
-}
-
-int
-com_top (int argc, char **argv)
-{
-  mu_stream_t stream;
-  unsigned int msgno;
-  unsigned int lines;
-  int status;
-
-  msgno = strtoul (argv[1], NULL, 10);
-  if (argc == 3)
-    lines = strtoul (argv[2], NULL, 10);
-  else
-    lines = 5;
-
-  status = mu_pop3_top (pop3, msgno, lines, &stream);
-
-  if (status == 0)
-    {
-      size_t n = 0;
-      char buf[128];
-      while ((mu_stream_readline (stream, buf, sizeof buf, &n) == 0) && n)
-       printf ("%s", buf);
-      mu_stream_destroy (&stream);
-    }
-  return status;
-}
-
-int
-com_retr (int argc, char **argv)
-{
-  mu_stream_t stream;
-  unsigned int msgno;
-  int status;
-
-  msgno = strtoul (argv[1], NULL, 10);
-  status = mu_pop3_retr (pop3, msgno, &stream);
-
-  if (status == 0)
-    {
-      size_t n = 0;
-      char buf[128];
-      while ((mu_stream_readline (stream, buf, sizeof buf, &n) == 0) && n)
-       printf ("%s", buf);
-      mu_stream_destroy (&stream);
-    }
-  return status;
-}
-
-int
-com_connect (int argc, char **argv)
-{
-  int status;
-  int n = 0;
-  int tls = 0;
-  int i = 1;
-  
-  for (i = 1; i < argc; i++)
-    {
-      if (strcmp (argv[i], "-tls") == 0)
-       {
-         if (WITH_TLS)
-           tls = 1;
-         else
-           {
-             mu_error ("TLS not supported");
-             return 0;
-           }
-       }
-      else
-       break;
-    }
-
-  argc -= i;
-  argv += i;
-  
-  if (argc >= 2)
-    {
-      if (get_port (argv[1], &n))
-       return 0;
-    }
-  else if (tls)
-    n = MU_POP3_DEFAULT_SSL_PORT;
-  else
-    n = MU_POP3_DEFAULT_PORT;
-  
-  if (pop_session_status != pop_session_disconnected)
-    com_disconnect (0, NULL);
-  
-  status = mu_pop3_create (&pop3);
-  if (status == 0)
-    {
-      mu_stream_t tcp;
-
-      if (QRY_VERBOSE ())
-       {
-         set_verbose (pop3);
-         set_verbose_mask (pop3);
-       }
-      status = mu_tcp_stream_create (&tcp, argv[0], n, MU_STREAM_READ);
-      if (status == 0)
-       {
-#ifdef WITH_TLS
-         if (tls)
-           {
-             mu_stream_t tlsstream;
-             
-             status = mu_tls_client_stream_create (&tlsstream, tcp, tcp, 0);
-             mu_stream_unref (tcp);
-             if (status)
-               {
-                 mu_error ("cannot create TLS stream: %s",
-                           mu_strerror (status));
-                 return 0;
-               }
-             tcp = tlsstream;
-           }
-#endif
-         mu_pop3_set_carrier (pop3, tcp);
-         status = mu_pop3_connect (pop3);
-       }
-      else
-       {
-         mu_pop3_destroy (&pop3);
-         pop3 = NULL;
-       }
-    }
-
-  if (status)
-    mu_error ("Failed to create pop3: %s", mu_strerror (status));
-  else
-    {
-      connect_argc = argc;
-      connect_argv = xcalloc (argc, sizeof (*connect_argv));
-      for (i = 0; i < argc; i++)
-       connect_argv[i] = xstrdup (argv[i]);
-      connect_argv[i] = NULL;
-      port = n;
-      pop_session_status = pop_session_connected;
-    }
-  
-  return status;
-}
-
-int
-com_disconnect (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
-{
-  if (pop3)
-    {
-      mu_pop3_disconnect (pop3);
-      mu_pop3_destroy (&pop3);
-      pop3 = NULL;
-      
-      mu_argcv_free (connect_argc, connect_argv);
-      connect_argc = 0;
-      connect_argv = NULL;
-      pop_session_status = pop_session_disconnected;
-    }
-  return 0;
-}
-
-int
-com_quit (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
-{
-  int status = 0;
-  if (pop3)
-    {
-      if (mu_pop3_quit (pop3) == 0)
-       {
-         status = com_disconnect (0, NULL);
-       }
-      else
-       {
-         printf ("Try 'exit' to leave %s\n", mu_program_name);
-       }
-    }
-  else
-    printf ("Try 'exit' to leave %s\n", mu_program_name);
-  return status;
-}
-
-int
-com_exit (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
-{
-  if (pop3)
-    {
-      mu_pop3_disconnect (pop3);
-      mu_pop3_destroy (&pop3);
-    }
-  done = 1;
-  return 0;
-}
-
-
-static char *
-input_line_interactive ()
-{
-  char *p = expand_prompt ();
-  char *line = readline (p);
-  free (p);
-  return line;
-}
-
-static char *
-input_line_script ()
-{
-  size_t size = 0;
-  char *buf = NULL;
-  if (getline (&buf, &size, stdin) <= 0)
-    return NULL;
-  return buf;
-}
-
-int
-main (int argc MU_ARG_UNUSED, char **argv)
-{
-  char *line, *s;
-  int interactive = isatty (0);
-  char *(*input_line) () = interactive ?
-                             input_line_interactive : input_line_script;
-
-  mu_set_program_name (argv[0]);
-  prompt = strdup (DEFAULT_PROMPT);
-  if (interactive)
-    initialize_readline ();    /* Bind our completer. */
-
-#ifdef WITH_TLS
-  mu_init_tls_libs ();
-#endif  
-  /* Loop reading and executing lines until the user quits. */
-  while (!done)
-    {
-      line = input_line ();
-      if (!line)
-       break;
-
-      /* Remove leading and trailing whitespace from the line.
-         Then, if there is anything left, add it to the history list
-         and execute it. */
-      s = mu_str_stripws (line);
-
-      if (*s)
-       {
-         int status;
-
-#ifdef WITH_READLINE
-         if (interactive)
-           {
-             if (retrieve_history (s))
-               continue;
-             add_history (s);
-           }
-#endif
-
-         status = execute_line (s);
-         if (status != 0)
-           mu_error ("Error: %s", mu_strerror (status));
-       }
-
-      free (line);
-    }
-  if (interactive)
-    finish_readline ();
-  exit (0);
-}
-
diff --git a/imap4d/fetch.c b/imap4d/fetch.c
index d0475cf..7f38e29 100644
--- a/imap4d/fetch.c
+++ b/imap4d/fetch.c
@@ -162,12 +162,21 @@ fetch_send_header_address (mu_header_t header, const char 
*name,
     fetch_send_address (defval);
 }
 
+static void
+imap4d_ws_alloc_die (struct mu_wordsplit *wsp)
+{
+  imap4d_bye (ERR_NO_MEM);
+}
+
+#define IMAP4D_WS_FLAGS \
+  (MU_WRDSF_DEFFLAGS | MU_WRDSF_DELIM | \
+   MU_WRDSF_ENOMEMABRT | MU_WRDSF_ALLOC_DIE)
+
 /* Send parameter list for the bodystructure.  */
 static void
 send_parameter_list (const char *buffer)
 {
-  int argc = 0;
-  char **argv;
+  struct mu_wordsplit ws;
   
   if (!buffer)
     {
@@ -175,9 +184,16 @@ send_parameter_list (const char *buffer)
       return;
     }
 
-  mu_argcv_get (buffer, " \t\r\n;=", NULL, &argc, &argv);
+  ws.ws_delim = " \t\r\n;=";
+  ws.ws_alloc_die = imap4d_ws_alloc_die;
+  if (mu_wordsplit (buffer, &ws, IMAP4D_WS_FLAGS))
+    {
+      mu_error (_("%s failed: %s"), "mu_wordsplit",
+               mu_wordsplit_strerror (&ws));
+      return; /* FIXME: a better error handling, maybe? */
+    }
   
-  if (argc == 0)
+  if (ws.ws_wordc == 0)
     io_sendf ("NIL");
   else
     {
@@ -185,16 +201,16 @@ send_parameter_list (const char *buffer)
       
       io_sendf ("(");
         
-      p = argv[0];
+      p = ws.ws_wordv[0];
       io_send_qstring (p);
 
-      if (argc > 1)
+      if (ws.ws_wordc > 1)
        {
          int i, space = 0;
          char *lvalue = NULL;
 
          io_sendf ("(");
-         for (i = 1; i < argc; i++)
+         for (i = 1; i < ws.ws_wordc; i++)
            {
              if (lvalue)
                {
@@ -205,22 +221,21 @@ send_parameter_list (const char *buffer)
                  space = 1;
                }
              
-             switch (argv[i][0])
+             switch (ws.ws_wordv[i][0])
                {
                case ';':
                  continue;
                  
                case '=':
-                 if (++i < argc)
+                 if (++i < ws.ws_wordc)
                    {
-                     char *p = argv[i];
                      io_sendf (" ");
-                     io_send_qstring (p);
+                     io_send_qstring (ws.ws_wordv[i]);
                    }
                  break;
                  
                default:
-                 lvalue = argv[i];
+                 lvalue = ws.ws_wordv[i];
                }
            }
          if (lvalue)
@@ -235,7 +250,7 @@ send_parameter_list (const char *buffer)
        io_sendf (" NIL");
       io_sendf (")");
     }
-  mu_argcv_free (argc, argv);
+  mu_wordsplit_free (&ws);
 }
 
 static void
@@ -352,27 +367,33 @@ bodystructure (mu_message_t msg, int extension)
 
   if (mu_header_aget_value (header, MU_HEADER_CONTENT_TYPE, &buffer) == 0)
     {
-      int argc = 0;
-      char **argv;
+      struct mu_wordsplit ws;
       char *s, *p;
          
-      mu_argcv_get (buffer, " \t\r\n;=", NULL, &argc, &argv);
+      ws.ws_delim = " \t\r\n;=";
+      ws.ws_alloc_die = imap4d_ws_alloc_die;
+      if (mu_wordsplit (buffer, &ws, IMAP4D_WS_FLAGS))
+       {
+         mu_error (_("%s failed: %s"), "mu_wordsplit",
+                   mu_wordsplit_strerror (&ws));
+         return RESP_BAD; /* FIXME: a better error handling, maybe? */
+       }
 
-      if (mu_c_strcasecmp (argv[0], "MESSAGE/RFC822") == 0)
+      if (mu_c_strcasecmp (ws.ws_wordv[0], "MESSAGE/RFC822") == 0)
         message_rfc822 = 1;
-      else if (mu_c_strcasecmp (argv[0], "TEXT/PLAIN") == 0)
+      else if (mu_c_strcasecmp (ws.ws_wordv[0], "TEXT/PLAIN") == 0)
         text_plain = 1;
 
-      s = strchr (argv[0], '/');
+      s = strchr (ws.ws_wordv[0], '/');
       if (s)
        *s++ = 0;
-      p = argv[0];
+      p = ws.ws_wordv[0];
       io_send_qstring (p);
       io_sendf (" ");
       io_send_qstring (s);
 
       /* body parameter parenthesized list: Content-type attributes */
-      if (argc > 1 || text_plain)
+      if (ws.ws_wordc > 1 || text_plain)
        {
          int space = 0;
          char *lvalue = NULL;
@@ -380,7 +401,7 @@ bodystructure (mu_message_t msg, int extension)
          int i;
          
          io_sendf (" (");
-         for (i = 1; i < argc; i++)
+         for (i = 1; i < ws.ws_wordc; i++)
            {
              /* body parameter parenthesized list:
                 Content-type parameter list. */
@@ -393,25 +414,23 @@ bodystructure (mu_message_t msg, int extension)
                  space = 1;
                }
              
-             switch (argv[i][0])
+             switch (ws.ws_wordv[i][0])
                {
                case ';':
                  continue;
                  
                case '=':
-                 if (++i < argc)
+                 if (++i < ws.ws_wordc)
                    {
-                     char *p = argv[i];
                      io_sendf (" ");
-                     io_send_qstring (p);
+                     io_send_qstring (ws.ws_wordv[i]);
                    }
                  break;
                  
                default:
-                 lvalue = argv[i];
+                 lvalue = ws.ws_wordv[i];
                  if (mu_c_strcasecmp (lvalue, "charset") == 0)
                    have_charset = 1;
-
                }
            }
          
@@ -432,7 +451,7 @@ bodystructure (mu_message_t msg, int extension)
        }
       else
        io_sendf (" NIL");
-      mu_argcv_free (argc, argv);
+      mu_wordsplit_free (&ws);
       free (buffer);
     }
   else
@@ -553,13 +572,19 @@ fetch_bodystructure0 (mu_message_t message, int extension)
       /* The subtype.  */
       if (mu_header_aget_value (header, MU_HEADER_CONTENT_TYPE, &buffer) == 0)
        {
-         int argc = 0;
-         char **argv;
+         struct mu_wordsplit ws;
          char *s;
-         
-         mu_argcv_get (buffer, " \t\r\n;=", NULL, &argc, &argv);
 
-         s = strchr (argv[0], '/');
+         ws.ws_delim = " \t\r\n;=";
+         ws.ws_alloc_die = imap4d_ws_alloc_die;
+         if (mu_wordsplit (buffer, &ws, IMAP4D_WS_FLAGS))
+           {
+             mu_error (_("%s failed: %s"), "mu_wordsplit",
+                       mu_wordsplit_strerror (&ws));
+             return RESP_BAD; /* FIXME: a better error handling, maybe? */
+           }
+
+         s = strchr (ws.ws_wordv[0], '/');
          if (s)
            s++;
          io_sendf (" ");
@@ -572,7 +597,7 @@ fetch_bodystructure0 (mu_message_t message, int extension)
              char *lvalue = NULL;
              
              io_sendf (" (");
-             for (i = 1; i < argc; i++)
+             for (i = 1; i < ws.ws_wordc; i++)
                {
                  /* body parameter parenthesized list:
                     Content-type parameter list. */
@@ -585,22 +610,21 @@ fetch_bodystructure0 (mu_message_t message, int extension)
                      space = 1;
                    }
 
-                 switch (argv[i][0])
+                 switch (ws.ws_wordv[i][0])
                    {
                    case ';':
                      continue;
                      
                    case '=':
-                     if (++i < argc)
+                     if (++i < ws.ws_wordc)
                        {
-                         char *p = argv[i];
                          io_sendf (" ");
-                         io_send_qstring (p);
+                         io_send_qstring (ws.ws_wordv[i]);
                        }
                      break;
                      
                    default:
-                     lvalue = argv[i];
+                     lvalue = ws.ws_wordv[i];
                    }
                }
              if (lvalue)
@@ -613,7 +637,7 @@ fetch_bodystructure0 (mu_message_t message, int extension)
            }
          else
            io_sendf (" NIL");
-         mu_argcv_free (argc, argv);
+         mu_wordsplit_free (&ws);
           free (buffer);
        }
       else
diff --git a/imap4d/imap4d.h b/imap4d/imap4d.h
index 21d3b72..26e9fc3 100644
--- a/imap4d/imap4d.h
+++ b/imap4d/imap4d.h
@@ -98,7 +98,7 @@
 #include <mailutils/pam.h>
 #include <mailutils/acl.h>
 #include <mailutils/server.h>
-#include <mailutils/argcv.h>
+#include <mailutils/wordsplit.h>
 #include <mailutils/alloc.h>
 #include <mailutils/vartab.h>
 #include <mailutils/cctype.h>
diff --git a/include/mailutils/Makefile.am b/include/mailutils/Makefile.am
index 6f0a67f..787d04e 100644
--- a/include/mailutils/Makefile.am
+++ b/include/mailutils/Makefile.am
@@ -94,7 +94,8 @@ pkginclude_HEADERS = \
  tls.h\
  url.h\
  vartab.h\
- version.h
+ version.h\
+ wordsplit.h
 
 if MU_COND_SUPPORT_CXX
   CPP_DIR = cpp
diff --git a/include/mailutils/argcv.h b/include/mailutils/argcv.h
index dd9482f..49d3bfa 100644
--- a/include/mailutils/argcv.h
+++ b/include/mailutils/argcv.h
@@ -28,31 +28,51 @@
 extern "C" {
 #endif
 
-#define MU_ARGCV_RETURN_DELIMS 0x01
+void mu_argcv_free (size_t argc, char **argv);
+void mu_argv_free (char **argv);
+
+enum mu_argcv_escape
+  {
+    mu_argcv_escape_no,
+    mu_argcv_escape_c
+    /*    mu_argcv_escape_sh */
+  };
+int mu_argcv_join (int argc, char **argv, char *delim,
+                  enum mu_argcv_escape esc,
+                  char **pstring);
+int mu_argcv_string (int argc, char **argv, char **string);
+
+void mu_argcv_remove (int *pargc, char ***pargv,
+                     int (*sel) (const char *, void *), void *);
   
-extern int mu_argcv_get    (const char *command, const char *delim,
-                           const char *cmnt,
-                           int *argc, char ***argv);
-extern int mu_argcv_get_n (const char *command, int len,
-                       const char *delim, const char *cmnt,
-                       int *argc, char ***argv);
-extern int mu_argcv_get_np (const char *command, int len,
-                           const char *delim, const char *cmnt,
-                           int flags,
-                           int *pargc, char ***pargv, char **endp);
+/* Deprecated interfaces */  
+#define MU_ARGCV_RETURN_DELIMS 0x01
+
+#ifndef MU_ARCGV_DEPRECATED
+# define MU_ARCGV_DEPRECATED __attribute__((deprecated))
+#endif  
   
-extern int mu_argcv_string (int argc, char **argv, char **string);
-extern void mu_argcv_free   (int argc, char **argv);
-extern void mu_argv_free (char **argv);
-
-extern int mu_argcv_unquote_char (int c);
-extern int mu_argcv_quote_char   (int c);
-extern size_t mu_argcv_quoted_length (const char *str, int *quote);
-extern void mu_argcv_unquote_copy (char *dst, const char *src, size_t n);
-extern void mu_argcv_quote_copy (char *dst, const char *src);
-extern void mu_argcv_remove (int *pargc, char ***pargv,
-                            int (*sel) (const char *, void *), void *);
+int mu_argcv_get    (const char *command, const char *delim,
+                    const char *cmnt,
+                    int *argc, char ***argv) MU_ARCGV_DEPRECATED;
+int mu_argcv_get_n (const char *command, int len,
+                   const char *delim, const char *cmnt,
+                   int *argc, char ***argv) MU_ARCGV_DEPRECATED;
+int mu_argcv_get_np (const char *command, int len,
+                    const char *delim, const char *cmnt,
+                    int flags,
+                    int *pargc, char ***pargv, char **endp)
+               MU_ARCGV_DEPRECATED;
   
+int mu_argcv_unquote_char (int c) MU_ARCGV_DEPRECATED;
+int mu_argcv_quote_char   (int c) MU_ARCGV_DEPRECATED;
+size_t mu_argcv_quoted_length (const char *str, int *quote)
+                           MU_ARCGV_DEPRECATED;
+void mu_argcv_unquote_copy (char *dst, const char *src, size_t n)
+                           MU_ARCGV_DEPRECATED;
+void mu_argcv_quote_copy (char *dst, const char *src)
+                           MU_ARCGV_DEPRECATED;
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/mailutils/mailutils.h b/include/mailutils/mailutils.h
index cad14b6..cec169f 100644
--- a/include/mailutils/mailutils.h
+++ b/include/mailutils/mailutils.h
@@ -64,5 +64,6 @@
 #include <mailutils/secret.h>
 #include <mailutils/cctype.h>
 #include <mailutils/cstr.h>
+#include <mailutils/wordsplit.h>
 
 /* EOF */
diff --git a/include/mailutils/wordsplit.h b/include/mailutils/wordsplit.h
new file mode 100644
index 0000000..13edb25
--- /dev/null
+++ b/include/mailutils/wordsplit.h
@@ -0,0 +1,135 @@
+/* wordsplit - a word splitter
+   Copyright (C) 2009, 2010 Sergey Poznyakoff
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 3 of the License, or (at your
+   option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef __MAILUTILS_WORDSPLIT_H
+#define __MAILUTILS_WORDSPLIT_H
+
+#include <stddef.h>
+
+struct mu_wordsplit
+{
+  size_t ws_wordc;
+  char **ws_wordv;
+  size_t ws_offs;
+  size_t ws_wordn;
+  int ws_flags;
+  const char *ws_delim;
+  const char *ws_comment;
+  void (*ws_alloc_die) (struct mu_wordsplit *wsp);
+  void (*ws_error) (const char *, ...)
+                   __attribute__ ((__format__ (__printf__, 1, 2)));
+  void (*ws_debug) (const char *, ...)
+                   __attribute__ ((__format__ (__printf__, 1, 2)));
+       
+  const char **ws_env;
+  char *(*ws_getvar) (const char *, size_t); 
+       
+  const char *ws_input;
+  size_t ws_len;
+  size_t ws_endp;
+  int ws_errno;
+  struct mu_wordsplit_node *ws_head, *ws_tail;
+};
+
+/* Append  the words found to the array resulting from a previous
+   call. */
+#define MU_WRDSF_APPEND            0x0000001
+/* Insert we_offs initial NULLs in the array ws_wordv.
+   (These are not counted in the returned ws_wordc.) */
+#define MU_WRDSF_DOOFFS            0x0000002
+/* Don't do command substitution. Reserved for future use. */
+#define MU_WRDSF_NOCMD             0x0000004
+/* The parameter p resulted from a previous call to
+   mu_wordsplit(), and mu_wordsplit_free() was not called. Reuse the
+   allocated storage. */
+#define MU_WRDSF_REUSE             0x0000008
+/* Print errors */
+#define MU_WRDSF_SHOWERR           0x0000010
+/* Consider it an error if an undefined shell variable
+   is expanded. */
+#define MU_WRDSF_UNDEF             0x0000020
+
+/* Don't do variable expansion. */
+#define MU_WRDSF_NOVAR             0x0000040
+/* Abort on ENOMEM error */
+#define MU_WRDSF_ENOMEMABRT        0x0000080
+/* Trim off any leading and trailind whitespace */
+#define MU_WRDSF_WS                0x0000100
+/* Handle quotes and escape directives */
+#define MU_WRDSF_QUOTE             0x0000200
+/* Replace each input sequence of repeated delimiters with a single
+   delimiter */
+#define MU_WRDSF_SQUEEZE_DELIMS    0x0000400
+/* Return delimiters */
+#define MU_WRDSF_RETURN_DELIMS     0x0000800
+/* Treat sed expressions as words */
+#define MU_WRDSF_SED_EXPR          0x0001000
+/* ws_delim field is initialized */
+#define MU_WRDSF_DELIM             0x0002000
+/* ws_comment field is initialized */
+#define MU_WRDSF_COMMENT           0x0004000
+/* ws_alloc_die field is initialized */
+#define MU_WRDSF_ALLOC_DIE         0x0008000
+/* ws_error field is initialized */
+#define MU_WRDSF_ERROR             0x0010000
+/* ws_debug field is initialized */
+#define MU_WRDSF_DEBUG             0x0020000
+/* ws_env field is initialized */
+#define MU_WRDSF_ENV               0x0040000
+/* ws_getvar field is initialized */
+#define MU_WRDSF_GETVAR            0x0080000
+/* enable debugging */
+#define MU_WRDSF_SHOWDBG           0x0100000
+/* Don't split input into words.  Useful for side effects. */
+#define MU_WRDSF_NOSPLIT           0x0200000
+/* Keep undefined variables in place, instead of expanding them to
+   empty string */
+#define MU_WRDSF_KEEPUNDEF         0x0400000
+/* Warn about undefined variables */
+#define MU_WRDSF_WARNUNDEF         0x0800000
+/* Handle C escapes */
+#define MU_WRDSF_CESCAPES          0x1000000
+
+#define MU_WRDSF_DEFFLAGS             \
+  (MU_WRDSF_NOVAR | MU_WRDSF_NOCMD | \
+   MU_WRDSF_QUOTE | MU_WRDSF_SQUEEZE_DELIMS | MU_WRDSF_CESCAPES)
+
+#define MU_WRDSE_EOF        0
+#define MU_WRDSE_QUOTE      1
+#define MU_WRDSE_NOSPACE    2
+#define MU_WRDSE_NOSUPP     3
+#define MU_WRDSE_USAGE      4
+#define MU_WRDSE_CBRACE     5
+#define MU_WRDSE_UNDEF      6
+
+int mu_wordsplit (const char *s, struct mu_wordsplit *p, int flags);
+int mu_wordsplit_len (const char *s, size_t len,
+                     struct mu_wordsplit *p, int flags);
+void mu_wordsplit_free (struct mu_wordsplit *p);
+
+int mu_wordsplit_c_unquote_char (int c);
+int mu_wordsplit_c_quote_char (int c);
+size_t mu_wordsplit_c_quoted_length (const char *str, int quote_hex,
+                                    int *quote);
+void mu_wordsplit_sh_unquote_copy (char *dst, const char *src, size_t n);
+void mu_wordsplit_c_unquote_copy (char *dst, const char *src, size_t n);
+void mu_wordsplit_c_quote_copy (char *dst, const char *src, int quote_hex);
+
+void mu_wordsplit_perror (struct mu_wordsplit *ws);
+const char *mu_wordsplit_strerror (struct mu_wordsplit *ws);
+
+
+#endif
diff --git a/lib/mailcap.c b/lib/mailcap.c
index fc2c3ec..c0396d2 100644
--- a/lib/mailcap.c
+++ b/lib/mailcap.c
@@ -414,21 +414,31 @@ create_filter (char *cmd, int outfd, int *infd)
   if (pid == 0)
     {
       /* Child process */
+      struct mu_wordsplit ws;
       int argc;
       char **argv;
-
+      
       if (need_shell_p (cmd))
        {
+         char *x_argv[4];
          argc = 3;
-         argv = xmalloc ((argc + 1) * sizeof *argv);
+         argv = x_argv;
          argv[0] = getenv ("SHELL");
          argv[1] = "-c";
          argv[2] = cmd;
          argv[3] = NULL;
        }
       else
-       mu_argcv_get (cmd, "", NULL, &argc, &argv);
-      
+       {
+         if (mu_wordsplit (cmd, &ws, MU_WRDSF_DEFFLAGS))
+           {
+             mu_error (_("%s failed: %s"), "mu_wordsplit",
+                       mu_wordsplit_strerror (&ws));
+             _exit (127);
+           }
+         argc = ws.ws_wordc;
+         argv = ws.ws_wordv;
+       }      
       /* Create input channel: */
       if (infd)
        {
@@ -440,7 +450,7 @@ create_filter (char *cmd, int outfd, int *infd)
       /* Create output channel */
       if (outfd != -1 && outfd != 1)
        dup2 (outfd, 1);
-
+      
       execvp (argv[0], argv);
       mu_error (_("cannot execute `%s': %s"), cmd, mu_strerror (errno));
       _exit (127);
@@ -491,8 +501,7 @@ run_test (mu_mailcap_entry_t entry, struct mime_context 
*ctx)
   
   if (mu_mailcap_entry_get_test (entry, NULL, 0, &size) == 0)
     {
-      int argc;
-      char **argv;
+      struct mu_wordsplit ws;
       char *str;
 
       obstack_blank (&expand_stack, size + 1);
@@ -500,11 +509,16 @@ run_test (mu_mailcap_entry_t entry, struct mime_context 
*ctx)
       mu_mailcap_entry_get_test (entry, str, size + 1, NULL);
 
       expand_string (ctx, &str);
-      mu_argcv_get (str, "", NULL, &argc, &argv);
+      if (mu_wordsplit (str, &ws, MU_WRDSF_DEFFLAGS))
+       {
+         mu_error (_("%s failed: %s"), "mu_wordsplit",
+                   mu_wordsplit_strerror (&ws));
+         return 1;
+       }
       
-      if (mu_spawnvp (argv[0], argv, &status))
+      if (mu_spawnvp (ws.ws_wordv[0], ws.ws_wordv, &status))
        status = 1;
-      mu_argcv_free (argc, argv);
+      mu_wordsplit_free (&ws);
     }
   return status;
 }
diff --git a/libmailutils/auth/mu_auth.c b/libmailutils/auth/mu_auth.c
index f9f33ca..f4ea60e 100644
--- a/libmailutils/auth/mu_auth.c
+++ b/libmailutils/auth/mu_auth.c
@@ -40,7 +40,7 @@
 # include <crypt.h>
 #endif
 
-#include <mailutils/argcv.h>
+#include <mailutils/wordsplit.h>
 #include <mailutils/list.h>
 #include <mailutils/iterator.h>
 #include <mailutils/mailbox.h>
@@ -376,32 +376,32 @@ _locate (const char *name)
 static void
 _add_module_list (const char *modlist, int (*fun)(const char *name))
 {
-  int argc;
-  char **argv;
-  int rc, i;
-  
-  rc = mu_argcv_get (modlist, ":", NULL, &argc, &argv);
-  if (rc)
+  struct mu_wordsplit ws;
+  int i;
+
+  ws.ws_delim = ":";
+  if (mu_wordsplit (modlist, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_DELIM))
     {
-      mu_error (_("cannot split line `%s': %s"), modlist, mu_strerror (rc));
+      mu_error (_("cannot split line `%s': %s"), modlist,
+               mu_wordsplit_strerror (&ws));
       exit (1);
     }
 
-  for (i = 0; i < argc; i += 2)
+  for (i = 0; i < ws.ws_wordc; i++)
     {
-      if (fun (argv[i]))
+      if (fun (ws.ws_wordv[i]))
         {
           /* Historically,auth functions used ENOENT. We support this
              return value for backward compatibility. */
           if (errno == ENOENT || errno == MU_ERR_NOENT)
-            mu_error (_("no such module: %s"), argv[i]);
+            mu_error (_("no such module: %s"), ws.ws_wordv[i]);
           else
             mu_error (_("failed to add module %s: %s"),
-                      argv[i], strerror (errno));
+                      ws.ws_wordv[i], strerror (errno));
           exit (1);
         }
     }
-  mu_argcv_free (argc, argv);
+  mu_wordsplit_free (&ws);
 }
 
 
diff --git a/libmailutils/base/Makefile.am b/libmailutils/base/Makefile.am
index c51c947..b4f208e 100644
--- a/libmailutils/base/Makefile.am
+++ b/libmailutils/base/Makefile.am
@@ -21,6 +21,9 @@ libbase_la_SOURCES = \
  alloc.c\
  amd.c\
  argcv.c\
+ argcvfree.c\
+ argcvjoin.c\
+ argcvrem.c\
  assoc.c\
  daemon.c\
  date.c\
diff --git a/libmailutils/base/argcv.c b/libmailutils/base/argcv.c
index 6e63b23..142de6b 100644
--- a/libmailutils/base/argcv.c
+++ b/libmailutils/base/argcv.c
@@ -22,15 +22,13 @@
 
 #include <ctype.h>
 #include <errno.h>
+#define MU_ARCGV_DEPRECATED
 #include <mailutils/argcv.h>
 
 /* Keep mailutils namespace clean */
 #define argcv_get            mu_argcv_get 
 #define argcv_get_n          mu_argcv_get_n 
 #define argcv_get_np         mu_argcv_get_np 
-#define argcv_string         mu_argcv_string
-#define argcv_free           mu_argcv_free
-#define argv_free            mu_argv_free 
 #define argcv_unquote_char   mu_argcv_unquote_char
 #define argcv_quote_char     mu_argcv_quote_char  
 #define argcv_quoted_length  mu_argcv_quoted_length
@@ -404,7 +402,7 @@ argcv_get_np (const char *command, int len,
       argv[i] = calloc (n + 1,  sizeof (char));
       if (argv[i] == NULL)
        {
-         argcv_free (i, argv);
+         mu_argcv_free (i, argv);
          return ENOMEM;
        }
       if (unquote)
@@ -438,111 +436,5 @@ argcv_get (const char *command, const char *delim, const 
char *cmnt,
 }
 
 
-/*
- * frees all elements of an argv array
- * argc is the number of elements
- * argv is the array
- */
-void
-argcv_free (int argc, char **argv)
-{
-  if (argc <= 0)
-    return;
-  while (--argc >= 0)
-    if (argv[argc])
-      free (argv[argc]);
-  free (argv);
-}
-
-void
-argv_free (char **argv)
-{
-  int i;
-
-  for (i = 0; argv[i]; i++)
-    free (argv[i]);
-  free (argv);
-}
-
-/* Make a argv an make string separated by ' '.  */
-
-int
-argcv_string (int argc, char **argv, char **pstring)
-{
-  size_t i, j, len;
-  char *buffer;
-
-  /* No need.  */
-  if (pstring == NULL)
-    return EINVAL;
-
-  buffer = malloc (1);
-  if (buffer == NULL)
-    return ENOMEM;
-  *buffer = '\0';
-
-  for (len = i = j = 0; i < argc; i++)
-    {
-      int quote;
-      int toklen;
-
-      toklen = argcv_quoted_length (argv[i], &quote);
-      
-      len += toklen + 2;
-      if (quote)
-       len += 2;
-      
-      buffer = realloc (buffer, len);
-      if (buffer == NULL)
-        return ENOMEM;
-
-      if (i != 0)
-       buffer[j++] = ' ';
-      if (quote)
-       buffer[j++] = '"';
-      argcv_quote_copy (buffer + j, argv[i]);
-      j += toklen;
-      if (quote)
-       buffer[j++] = '"';
-    }
-
-  for (; j > 0 && isspace (buffer[j-1]); j--)
-    ;
-  buffer[j] = 0;
-  if (pstring)
-    *pstring = buffer;
-  return 0;
-}
-
-void
-mu_argcv_remove (int *pargc, char ***pargv,
-                int (*sel) (const char *, void *), void *data)
-{
-  int i, j;
-  int argc = *pargc;
-  char **argv = *pargv;
-  int cnt = 0;
-  
-  for (i = j = 0; i < argc; i++)
-    {
-      if (sel (argv[i], data))
-       {
-         free (argv[i]);
-         cnt++;
-       }
-      else
-       {
-         if (i != j)
-           argv[j] = argv[i];
-         j++;
-       }
-    }
-  if (i != j)
-    argv[j] = NULL;
-  argc -= cnt;
-
-  *pargc = argc;
-  *pargv = argv;
-}
       
  
diff --git a/libmailutils/url/urlstr.c b/libmailutils/base/argcvfree.c
similarity index 59%
copy from libmailutils/url/urlstr.c
copy to libmailutils/base/argcvfree.c
index 31e5197..737d67d 100644
--- a/libmailutils/url/urlstr.c
+++ b/libmailutils/base/argcvfree.c
@@ -1,5 +1,6 @@
-/* GNU Mailutils -- a suite of utilities for electronic mail
-   Copyright (C) 2010 Free Software Foundation, Inc.
+/* Free an array of string pointers.
+   Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2010 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
@@ -20,12 +21,31 @@
 #endif
 #include <stdlib.h>
 #include <mailutils/types.h>
-#include <mailutils/sys/url.h>
+#include <mailutils/argcv.h>
 
-const char *
-mu_url_to_string (const mu_url_t url)
+/*
+ * frees all elements of an argv array
+ * argc is the number of elements
+ * argv is the array
+ */
+void
+mu_argcv_free (size_t argc, char **argv)
 {
-  if (url == NULL || url->name == NULL)
-    return "";
-  return url->name;
+  size_t i;
+
+  for (i = 0; i < argc; i++)
+    if (argv[i])
+      free (argv[i]);
+  free (argv);
+}
+
+void
+mu_argv_free (char **argv)
+{
+  int i;
+
+  for (i = 0; argv[i]; i++)
+    free (argv[i]);
+  free (argv);
 }
+
diff --git a/libmailutils/base/argcvjoin.c b/libmailutils/base/argcvjoin.c
new file mode 100644
index 0000000..a5877e8
--- /dev/null
+++ b/libmailutils/base/argcvjoin.c
@@ -0,0 +1,116 @@
+/* Join strings from an array into a single string.
+   Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2010 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 <mailutils/types.h>
+#include <mailutils/argcv.h>
+#include <mailutils/errno.h>
+#include <mailutils/wordsplit.h>
+
+int
+mu_argcv_join (int argc, char **argv, char *delim, enum mu_argcv_escape esc,
+              char **pstring)
+{
+  size_t i, j, len;
+  char *buffer;
+  size_t delimlen = strlen (delim);
+  int quote_hex = 0;
+  
+  if (pstring == NULL)
+    return EINVAL;
+
+  buffer = malloc (1);
+  if (buffer == NULL)
+    return ENOMEM;
+  *buffer = '\0';
+
+  for (len = i = j = 0; i < argc; i++)
+    {
+      int quote;
+      int toklen;
+
+      switch (esc)
+       {
+       case mu_argcv_escape_no:
+         toklen = strlen (argv[i]);
+         quote = 0;
+         break;
+
+       case mu_argcv_escape_c:
+         toklen = mu_wordsplit_c_quoted_length (argv[i], quote_hex, &quote);
+         break;
+
+#if 0
+       case mu_argcv_escape_sh:
+         FIXME
+         toklen = mu_wordsplit_sh_quoted_length (argv[i], &quote);
+         break;
+#endif
+       default:
+         return EINVAL;
+       }
+       
+      len += toklen + delimlen;
+      if (quote)
+       len += 2;
+      
+      buffer = realloc (buffer, len);
+      if (buffer == NULL)
+        return ENOMEM;
+
+      if (i != 0)
+       {
+         memcpy (buffer + j, delim, delimlen);
+         j += delimlen;
+       }
+      if (quote)
+       buffer[j++] = '"';
+      
+      switch (esc)
+       {
+       case mu_argcv_escape_no:
+         memcpy (buffer + j, argv[i], toklen);
+         break;
+
+       case mu_argcv_escape_c:
+         mu_wordsplit_c_quote_copy (buffer + j, argv[i], quote_hex);
+         break;
+#if 0
+       case mu_argcv_escape_sh:
+         mu_wordsplit_sh_quote_copy (buffer + j, argv[i]);
+#endif
+       }
+
+      j += toklen;
+      if (quote)
+       buffer[j++] = '"';
+    }
+
+  buffer[j] = 0;
+  *pstring = buffer;
+  return 0;
+}
+
+int
+mu_argcv_string (int argc, char **argv, char **pstring)
+{
+  return mu_argcv_join (argc, argv, " ", mu_argcv_escape_c, pstring);
+}
diff --git a/libmailutils/url/get-secret.c b/libmailutils/base/argcvrem.c
similarity index 55%
copy from libmailutils/url/get-secret.c
copy to libmailutils/base/argcvrem.c
index 34fb229..d6e994d 100644
--- a/libmailutils/url/get-secret.c
+++ b/libmailutils/base/argcvrem.c
@@ -1,5 +1,6 @@
-/* GNU Mailutils -- a suite of utilities for electronic mail
-   Copyright (C) 2010 Free Software Foundation, Inc.
+/* Selectively remove elements from an array of string pointers.
+   Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2010 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
@@ -18,27 +19,37 @@
 #ifdef HAVE_CONFIG_H
 # include <config.h>
 #endif
-
-#include <errno.h>
 #include <stdlib.h>
-#include <string.h>
-#ifdef HAVE_STRINGS_H
-# include <strings.h>
-#endif
-
 #include <mailutils/types.h>
-#include <mailutils/errno.h>
-#include <mailutils/secret.h>
-#include <mailutils/sys/url.h>
+#include <mailutils/argcv.h>
 
-int
-mu_url_get_secret (const mu_url_t url, mu_secret_t *psecret)
+void
+mu_argcv_remove (int *pargc, char ***pargv,
+                int (*sel) (const char *, void *), void *data)
 {
-  if (url->_get_secret)
-    return url->_get_secret (url, psecret);
-  if (url->secret == NULL)
-    return MU_ERR_NOENT;
-  mu_secret_ref (url->secret);
-  *psecret = url->secret;
-  return 0;
+  int i, j;
+  int argc = *pargc;
+  char **argv = *pargv;
+  int cnt = 0;
+  
+  for (i = j = 0; i < argc; i++)
+    {
+      if (sel (argv[i], data))
+       {
+         free (argv[i]);
+         cnt++;
+       }
+      else
+       {
+         if (i != j)
+           argv[j] = argv[i];
+         j++;
+       }
+    }
+  if (i != j)
+    argv[j] = NULL;
+  argc -= cnt;
+
+  *pargc = argc;
+  *pargv = argv;
 }
diff --git a/libmailutils/base/mutil.c b/libmailutils/base/mutil.c
index ffa04dd..11232cf 100644
--- a/libmailutils/base/mutil.c
+++ b/libmailutils/base/mutil.c
@@ -22,8 +22,9 @@
 
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <stdlib.h>
 #include <time.h>
-#include <mailutils/argcv.h>
+#include <mailutils/wordsplit.h>
 #include <mailutils/error.h>
 #include <mailutils/errno.h>
 #include <mailutils/nls.h>
@@ -78,20 +79,20 @@ mutil_parse_field_map (const char *map, mu_assoc_t 
*passoc_tab, int *perr)
 {
   int rc;
   int i;
-  int argc;
-  char **argv;
+  struct mu_wordsplit ws;
   mu_assoc_t assoc_tab = NULL;
 
-  rc = mu_argcv_get (map, ":", NULL, &argc, &argv);
-  if (rc)
+  ws.ws_delim = ":";
+  if (mu_wordsplit (map, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_DELIM))
     {
-      mu_error (_("cannot split line `%s': %s"), map, mu_strerror (rc));
+      mu_error (_("cannot split line `%s': %s"), map,
+               mu_wordsplit_strerror (&ws));
       return rc;
     }
 
-  for (i = 0; i < argc; i += 2)
+  for (i = 0; i < ws.ws_wordc; i++)
     {
-      char *tok = argv[i];
+      char *tok = ws.ws_wordv[i];
       char *p = strchr (tok, '=');
       char *pptr;
       
@@ -123,7 +124,7 @@ mutil_parse_field_map (const char *map, mu_assoc_t 
*passoc_tab, int *perr)
        }
     }
 
-  mu_argcv_free (argc, argv);
+  mu_wordsplit_free (&ws);
   if (rc && perr)
     *perr = i;
   return rc;
diff --git a/libmailutils/cfg/format.c b/libmailutils/cfg/format.c
index e7aaa7b..1d75b3b 100644
--- a/libmailutils/cfg/format.c
+++ b/libmailutils/cfg/format.c
@@ -18,11 +18,12 @@
 #ifdef HAVE_CONFIG_H
 # include <config.h>
 #endif
+#include <stdlib.h>
 #include <mailutils/alloc.h>
 #include <mailutils/stream.h>
 #include <mailutils/error.h>
 #include <mailutils/cfg.h>
-#include <mailutils/argcv.h>
+#include <mailutils/wordsplit.h>
 #include <mailutils/nls.h>
 #include <mailutils/iterator.h>
 #include <ctype.h>
@@ -50,7 +51,7 @@ format_string_value (struct tree_print *tp, const char *str)
   int quote;
   char *p;
 
-  size = mu_argcv_quoted_length (str, &quote);
+  size = mu_wordsplit_c_quoted_length (str, 1, &quote);
   if (quote)
     size += 2;
   size++;
@@ -69,7 +70,7 @@ format_string_value (struct tree_print *tp, const char *str)
       p++;
     }
   tp->buf[size-1] = 0;
-  mu_argcv_quote_copy (p, str);
+  mu_wordsplit_c_quote_copy (p, str, 1);
   mu_stream_write (tp->stream, tp->buf, size - 1, NULL);
 }
 
diff --git a/libmailutils/cfg/lexer.l b/libmailutils/cfg/lexer.l
index 67c1199..52be390 100644
--- a/libmailutils/cfg/lexer.l
+++ b/libmailutils/cfg/lexer.l
@@ -32,7 +32,7 @@
 #include <mailutils/errno.h>
 #include <mailutils/error.h>
 #include <mailutils/debug.h>  
-#include <mailutils/argcv.h>
+#include <mailutils/wordsplit.h>
 #include <mailutils/alloc.h>  
 #include <mailutils/nls.h>
 #include <mailutils/cfg.h>
@@ -172,7 +172,7 @@ unescape_to_line (int c)
 {
   if (c != '\n')
     {
-      char t = mu_argcv_unquote_char (c);
+      char t = mu_wordsplit_c_unquote_char (c);
       if (t == c && t != '\\' && t != '\"')
        mu_cfg_parse_error (_("unknown escape sequence '\\%c'"), c);
       mu_opool_append_char (pool, t);
diff --git a/libmailutils/cfg/parser.y b/libmailutils/cfg/parser.y
index 9fe5b1b..cc77fb9 100644
--- a/libmailutils/cfg/parser.y
+++ b/libmailutils/cfg/parser.y
@@ -27,6 +27,7 @@
 #include <netdb.h>
 #include "intprops.h"
 #include <mailutils/argcv.h>
+#include <mailutils/wordsplit.h>
 #include <mailutils/nls.h>
 #include <mailutils/cfg.h>
 #include <mailutils/alloc.h>
@@ -1579,7 +1580,6 @@ mu_cfg_value_eq (mu_config_value_t *a, mu_config_value_t 
*b)
 static int
 split_cfg_path (const char *path, int *pargc, char ***pargv)
 {
-  int rc;
   int argc;
   char **argv;
   char *delim = MU_CFG_PATH_DELIM_STR;
@@ -1598,25 +1598,36 @@ split_cfg_path (const char *path, int *pargc, char 
***pargv)
        }
       argv[1] = NULL;
       argc = 1;
-      rc = 0;
     }
   else
     {
+      struct mu_wordsplit ws;
+      
       if (mu_ispunct (path[0]))
        {
          delim = static_delim;
          delim[0] = path[0];
          path++;
        }
-      rc = mu_argcv_get_np (path, strlen (path), delim, NULL, 0,
-                           &argc, &argv, NULL);
-    }
-  if (rc == 0)
-    {
-      *pargc = argc;
-      *pargv = argv;
+      ws.ws_delim = delim;
+
+      if (mu_wordsplit (path, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_DELIM))
+       {
+         mu_error (_("cannot split line `%s': %s"), path,
+                   mu_wordsplit_strerror (&ws));
+         return errno;
+       }
+      argc = ws.ws_wordc;
+      argv = ws.ws_wordv;
+      ws.ws_wordc = 0;
+      ws.ws_wordv = NULL;
+      mu_wordsplit_free (&ws);
     }
-  return rc;
+
+  *pargc = argc;
+  *pargv = argv;
+
+  return 0;
 }
 
 struct find_data
@@ -1665,53 +1676,63 @@ static mu_config_value_t *
 parse_label (const char *str)
 {
   mu_config_value_t *val = NULL;
-  int count, i;
-  char **vect;
+  size_t i;
+  struct mu_wordsplit ws;
   size_t len = strlen (str);
   
   if (len > 1 && str[0] == '(' && str[len-1] == ')')
     {
       mu_list_t lst;
-      mu_argcv_get_np (str + 1, len - 2,
-                      ", \t", NULL,
-                      0,
-                      &count, &vect, NULL);
+
+      ws.ws_delim = ",";
+      if (mu_wordsplit_len (str + 1, len - 2, &ws,
+                           MU_WRDSF_DEFFLAGS|MU_WRDSF_DELIM|MU_WRDSF_WS))
+       {
+         mu_error (_("cannot split line `%s': %s"), str,
+                   mu_wordsplit_strerror (&ws));
+         return NULL;
+       }
+
       mu_list_create (&lst);
       mu_list_set_destroy_item (lst, destroy_value);
-      for (i = 0; i < count; i++)
+      for (i = 0; i < ws.ws_wordc; i++)
        {
          mu_config_value_t *p = mu_alloc (sizeof (*p));
          p->type = MU_CFG_STRING;
-         p->v.string = vect[i];
+         p->v.string = ws.ws_wordv[i];
          mu_list_append (lst, p);
        }
-      free (vect);
       val = mu_alloc (sizeof (*val));
       val->type = MU_CFG_LIST;
       val->v.list = lst;
     }
   else
     {      
-      mu_argcv_get (str, NULL, NULL, &count, &vect);
+      if (mu_wordsplit (str, &ws, MU_WRDSF_DEFFLAGS))
+       {
+         mu_error (_("cannot split line `%s': %s"), str,
+                   mu_wordsplit_strerror (&ws));
+         return NULL;
+       }
       val = mu_alloc (sizeof (*val));
-      if (count == 1)
+      if (ws.ws_wordc == 1)
        {
          val->type = MU_CFG_STRING;
-         val->v.string = vect[0];
-         free (vect);
+         val->v.string = ws.ws_wordv[0];
        }
       else
        {
          val->type = MU_CFG_ARRAY;
-         val->v.arg.c = count;
-         val->v.arg.v = mu_alloc (count * sizeof (val->v.arg.v[0]));
-         for (i = 0; i < count; i++)
+         val->v.arg.c = ws.ws_wordc;
+         val->v.arg.v = mu_alloc (ws.ws_wordc * sizeof (val->v.arg.v[0]));
+         for (i = 0; i < ws.ws_wordc; i++)
            {
              val->v.arg.v[i].type = MU_CFG_STRING;
-             val->v.arg.v[i].v.string = vect[i];
+             val->v.arg.v[i].v.string = ws.ws_wordv[i];
            }
-         free (vect);
        }
+      ws.ws_wordc = 0;
+      mu_wordsplit_free (&ws);
     }
   return val;
 }
diff --git a/libmailutils/diag/gdebug.c b/libmailutils/diag/gdebug.c
index 7e6f63c..d2d89d5 100644
--- a/libmailutils/diag/gdebug.c
+++ b/libmailutils/diag/gdebug.c
@@ -28,7 +28,7 @@
 #include <mailutils/assoc.h>
 #include <mailutils/error.h>
 #include <mailutils/errno.h>
-#include <mailutils/argcv.h>
+#include <mailutils/wordsplit.h>
 #include <mailutils/debug.h>
 #include <mailutils/cfg.h>
 #include <mailutils/nls.h>
@@ -167,20 +167,22 @@ mu_debug_level_from_string (const char *string, 
mu_log_level_t *plev,
 int
 mu_global_debug_from_string (const char *string, const char *errpfx)
 {
-  int rc;
-  int argc;
-  char **argv;
-  int i;
-  
-  rc = mu_argcv_get (string, ";", NULL, &argc, &argv);
-  if (rc)
-    return rc;
+  struct mu_wordsplit ws;
+  size_t i;
+
+  ws.ws_delim = ";";
+  if (mu_wordsplit (string, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_DELIM|MU_WRDSF_WS))
+    {
+      mu_error (_("cannot split line `%s': %s"), string,
+               mu_wordsplit_strerror (&ws));
+      return errno;
+    }
 
-  for (i = 0; i < argc; i++)
+  for (i = 0; i < ws.ws_wordc; i++)
     {
       char *p;
       mu_log_level_t level = MU_DEBUG_INHERIT;
-      char *object_name = argv[i];
+      char *object_name = ws.ws_wordv[i];
       
       for (p = object_name; *p && *p != '='; p++)
        ;
@@ -199,7 +201,7 @@ mu_global_debug_from_string (const char *string, const char 
*errpfx)
                  mu_error ("%s: invalid debugging specification `%s': "
                            "expected levels or number after `=', "
                            "but found `%s'",
-                           errpfx, argv[i], p);
+                           errpfx, ws.ws_wordv[i], p);
                  break;
                }
            }
@@ -254,8 +256,7 @@ mu_global_debug_from_string (const char *string, const char 
*errpfx)
       
       mu_global_debug_set_level (object_name, level);
     }
-  
-  mu_argcv_free (argc, argv);
+  mu_wordsplit_free (&ws);
   return 0;
 }
 
diff --git a/libmailutils/mailer/mailer.c b/libmailutils/mailer/mailer.c
index 01fb6d3..fec7cab 100644
--- a/libmailutils/mailer/mailer.c
+++ b/libmailutils/mailer/mailer.c
@@ -29,6 +29,7 @@
 
 #include <sys/time.h>
 
+#include <mailutils/nls.h>
 #include <mailutils/cstr.h>
 #include <mailutils/address.h>
 #include <mailutils/debug.h>
@@ -45,7 +46,7 @@
 #include <mailutils/body.h>
 #include <mailutils/mailbox.h>
 #include <mailutils/message.h>
-#include <mailutils/argcv.h>
+#include <mailutils/wordsplit.h>
 #include <mailutils/util.h>
 #include <mailutils/mime.h>
 #include <mailutils/io.h>
@@ -289,7 +290,8 @@ save_fcc (mu_message_t msg)
 {
   mu_header_t hdr;
   size_t count = 0, i;
-  char buf[512], *fcc;
+  const char *buf;
+  char *fcc;
   
   if (mu_message_get_header (msg, &hdr))
     return;
@@ -302,17 +304,25 @@ save_fcc (mu_message_t msg)
     {
       mu_mailbox_t mbox;
       
-      mu_header_get_field_name (hdr, i, buf, sizeof buf, NULL);
+      mu_header_sget_field_name (hdr, i, &buf);
       if (mu_c_strcasecmp (buf, MU_HEADER_FCC) == 0
          && mu_header_aget_field_value (hdr, i, &fcc) == 0)
        {
-         int i, argc;
-         char **argv;
-         
-         mu_argcv_get (fcc, ",", NULL, &argc, &argv);
-         for (i = 0; i < argc; i += 2)
+         size_t i;
+         struct mu_wordsplit ws;
+
+         ws.ws_delim = ",";
+         if (mu_wordsplit (fcc, &ws,
+                           MU_WRDSF_DEFFLAGS|MU_WRDSF_DELIM|MU_WRDSF_WS))
+           {
+             mu_error (_("cannot split line `%s': %s"), fcc,
+                       mu_wordsplit_strerror (&ws));
+             continue;
+           }
+
+         for (i = 0; i < ws.ws_wordc; i += 2)
            {
-             if (mu_mailbox_create_default (&mbox, argv[i]))
+             if (mu_mailbox_create_default (&mbox, ws.ws_wordv[i]))
                continue; /*FIXME: error message?? */
              if (mu_mailbox_open (mbox,
                                   MU_STREAM_RDWR | MU_STREAM_CREAT
@@ -324,7 +334,7 @@ save_fcc (mu_message_t msg)
              mu_mailbox_close (mbox);
              mu_mailbox_destroy (&mbox);
            }
-         mu_argcv_free (argc, argv);
+         mu_wordsplit_free (&ws);
          free (fcc);
        }
     }
diff --git a/libmailutils/server/acl.c b/libmailutils/server/acl.c
index a3e3764..d7a1362 100644
--- a/libmailutils/server/acl.c
+++ b/libmailutils/server/acl.c
@@ -28,8 +28,9 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
+#include <mailutils/nls.h>
 #include <mailutils/acl.h>
-#include <mailutils/argcv.h>
+#include <mailutils/wordsplit.h>
 #include <mailutils/list.h>
 #include <mailutils/debug.h>
 #include <mailutils/error.h>
@@ -581,14 +582,19 @@ spawn_prog (const char *cmdline, int *pstatus, struct 
run_closure *rp)
   if (pid == 0)
     {
       int i;
-      int argc;
-      char **argv;
+      struct mu_wordsplit ws;
 
-      mu_argcv_get (s, " \t", NULL, &argc, &argv);
+      if (mu_wordsplit (s, &ws, MU_WRDSF_DEFFLAGS))
+       {
+         mu_error (_("cannot split line `%s': %s"), s,
+                   mu_wordsplit_strerror (&ws));
+         _exit (127);
+       }
+      
       for (i = getmaxfd (); i > 2; i--)
        close (i);
-      execvp (argv[0], argv);
-      exit (127);
+      execvp (ws.ws_wordv[0], ws.ws_wordv);
+      _exit (127);
     }
 
   free (s);
diff --git a/libmailutils/stream/prog_stream.c 
b/libmailutils/stream/prog_stream.c
index 437bf01..c05c91e 100644
--- a/libmailutils/stream/prog_stream.c
+++ b/libmailutils/stream/prog_stream.c
@@ -29,6 +29,7 @@
 #include <mailutils/types.h>
 #include <mailutils/alloc.h>
 #include <mailutils/argcv.h>
+#include <mailutils/wordsplit.h>
 #include <mailutils/error.h>
 #include <mailutils/errno.h>
 #include <mailutils/nls.h>
@@ -390,17 +391,25 @@ struct _mu_prog_stream *
 _prog_stream_create (const char *progname, int flags)
 {
   struct _mu_prog_stream *fs;
+  struct mu_wordsplit ws;
   
   fs = (struct _mu_prog_stream *) _mu_stream_create (sizeof (*fs), flags);
   if (!fs)
     return NULL;
 
-  if (mu_argcv_get (progname, "", "#", &fs->argc, &fs->argv))
+  ws.ws_comment = "#";
+  if (mu_wordsplit (progname, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT))
     {
-      mu_argcv_free (fs->argc, fs->argv);
+      mu_error (_("cannot split line `%s': %s"), progname,
+               mu_wordsplit_strerror (&ws));
       free (fs);
       return NULL;
     }
+  fs->argc = ws.ws_wordc;
+  fs->argv = ws.ws_wordv;
+  ws.ws_wordc = 0;
+  ws.ws_wordv = NULL;
+  mu_wordsplit_free (&ws);
 
   fs->stream.read = _prog_read;
   fs->stream.write = _prog_write;
diff --git a/libmailutils/string/Makefile.am b/libmailutils/string/Makefile.am
index 485df4a..5cb6a23 100644
--- a/libmailutils/string/Makefile.am
+++ b/libmailutils/string/Makefile.am
@@ -35,6 +35,7 @@ libstring_la_SOURCES = \
  muctype.c\
  vasnprintf.c\
  mkfilename.c\
+ wordsplit.c\
  xdecode.c
 
 INCLUDES = @MU_LIB_COMMON_INCLUDES@ -I/libmailutils
diff --git a/libmailutils/string/wordsplit.c b/libmailutils/string/wordsplit.c
new file mode 100644
index 0000000..301ab00
--- /dev/null
+++ b/libmailutils/string/wordsplit.c
@@ -0,0 +1,1469 @@
+/* wordsplit - a word splitter
+   Copyright (C) 2009, 2010 Sergey Poznyakoff
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 3 of the License, or (at your
+   option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* This module is shared with Grecs: http://puszcza.gnu.org.ua/projects/grecs.
+   The intent is to keep it as mu-independent as possible, so that the two
+   projects can be easily synchronized.  Some time in the future I'll think
+   about providing a better way of synching, perhaps by integrating Grecs to
+   Mailutils as a submodule. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <mailutils/nls.h>
+#include <mailutils/wordsplit.h>
+
+#define ISWS(c) ((c)==' '||(c)=='\t'||(c)=='\n')
+#define ISDELIM(ws,c) \
+  (strchr ((ws)->ws_delim, (c)) != NULL)
+#define ISPUNCT(c) (strchr("!\"#$%&'()*+,-./:;<=>address@hidden|}~",(c))!=NULL)
+#define ISUPPER(c) ('A' <= ((unsigned) (c)) && ((unsigned) (c)) <= 'Z')
+#define ISLOWER(c) ('a' <= ((unsigned) (c)) && ((unsigned) (c)) <= 'z')
+#define ISALPHA(c) (ISUPPER(c) || ISLOWER(c))
+#define ISDIGIT(c) ('0' <= ((unsigned) (c)) && ((unsigned) (c)) <= '9')
+#define ISXDIGIT(c) (strchr("abcdefABCDEF", c)!=NULL)
+#define ISALNUM(c) (ISALPHA(c) || ISDIGIT(c))
+#define ISPRINT(c) (' ' <= ((unsigned) (c)) && ((unsigned) (c)) <= 127)
+
+#define ALLOC_INIT 128
+#define ALLOC_INCR 128
+
+static void
+_wsplt_alloc_die (struct mu_wordsplit *wsp)
+{
+  wsp->ws_error (_("memory exhausted"));
+  abort ();
+}
+
+static void
+_wsplt_error (const char *fmt, ...)
+{
+  va_list ap;
+
+  va_start (ap, fmt);
+  vfprintf (stderr, fmt, ap);
+  va_end (ap);
+  fputc ('\n', stderr);
+}
+
+static void mu_wordsplit_free_nodes (struct mu_wordsplit *);
+
+static int
+_wsplt_nomem (struct mu_wordsplit *wsp)
+{
+  errno = ENOMEM;
+  wsp->ws_errno = MU_WRDSE_NOSPACE;
+  if (wsp->ws_flags & MU_WRDSF_ENOMEMABRT)
+    wsp->ws_alloc_die (wsp);
+  if (wsp->ws_flags & MU_WRDSF_SHOWERR)
+    mu_wordsplit_perror (wsp);
+  if (!(wsp->ws_flags & MU_WRDSF_REUSE))
+    mu_wordsplit_free (wsp);
+  mu_wordsplit_free_nodes (wsp);
+  return wsp->ws_errno;
+}
+
+static int
+mu_wordsplit_init (struct mu_wordsplit *wsp, const char *input, size_t len,
+                  int flags)
+{
+  wsp->ws_flags = flags;
+
+  if (!(wsp->ws_flags & MU_WRDSF_ALLOC_DIE))
+    wsp->ws_alloc_die = _wsplt_alloc_die;
+  if (!(wsp->ws_flags & MU_WRDSF_ERROR))
+    wsp->ws_error = _wsplt_error;
+
+  if (!(wsp->ws_flags & MU_WRDSF_NOVAR)
+      && !(wsp->ws_flags & (MU_WRDSF_ENV | MU_WRDSF_GETVAR)))
+    {
+      errno = EINVAL;
+      wsp->ws_errno = MU_WRDSE_USAGE;
+      if (wsp->ws_flags & MU_WRDSF_SHOWERR)
+       mu_wordsplit_perror (wsp);
+      return wsp->ws_errno;
+    }
+
+  if (!(wsp->ws_flags & MU_WRDSF_NOCMD))
+    {
+      errno = EINVAL;
+      wsp->ws_errno = MU_WRDSE_NOSUPP;
+      if (wsp->ws_flags & MU_WRDSF_SHOWERR)
+       mu_wordsplit_perror (wsp);
+      return wsp->ws_errno;
+    }
+
+  if (wsp->ws_flags & MU_WRDSF_SHOWDBG)
+    {
+      if (!(wsp->ws_flags & MU_WRDSF_DEBUG))
+       {
+         if (wsp->ws_flags & MU_WRDSF_ERROR)
+           wsp->ws_debug = wsp->ws_error;
+         else if (wsp->ws_flags & MU_WRDSF_SHOWERR)
+           wsp->ws_debug = _wsplt_error;
+         else
+           wsp->ws_flags &= ~MU_WRDSF_SHOWDBG;
+       }
+    }
+
+  wsp->ws_input = input;
+  wsp->ws_len = len;
+
+  if (!(wsp->ws_flags & MU_WRDSF_DOOFFS))
+    wsp->ws_offs = 0;
+
+  if (!(wsp->ws_flags & MU_WRDSF_DELIM))
+    wsp->ws_delim = " \t\n";
+
+  if (!(wsp->ws_flags & MU_WRDSF_COMMENT))
+    wsp->ws_comment = NULL;
+
+  if (wsp->ws_flags & MU_WRDSF_REUSE)
+    {
+      if (!(wsp->ws_flags & MU_WRDSF_APPEND))
+       wsp->ws_wordc = 0;
+    }
+  else
+    {
+      wsp->ws_wordv = NULL;
+      wsp->ws_wordc = 0;
+      wsp->ws_wordn = 0;
+    }
+  if (wsp->ws_flags & MU_WRDSF_DOOFFS)
+    wsp->ws_wordn += wsp->ws_offs;
+
+  wsp->ws_endp = 0;
+  wsp->ws_errno = 0;
+  wsp->ws_head = wsp->ws_tail = NULL;
+  return 0;
+}
+
+static int
+alloc_space (struct mu_wordsplit *wsp, size_t count)
+{
+  size_t offs = (wsp->ws_flags & MU_WRDSF_DOOFFS) ? wsp->ws_offs : 0;
+  char **ptr;
+  size_t newalloc;
+
+  if (wsp->ws_wordv == NULL)
+    {
+      newalloc = offs + count > ALLOC_INIT ? count : ALLOC_INIT;
+      ptr = calloc (newalloc, sizeof (ptr[0]));
+    }
+  else if (wsp->ws_wordn < offs + wsp->ws_wordc + count)
+    {
+      newalloc = offs + wsp->ws_wordc +
+       count > ALLOC_INCR ? count : ALLOC_INCR;
+      ptr = realloc (wsp->ws_wordv, newalloc * sizeof (ptr[0]));
+    }
+  else
+    return 0;
+
+  if (ptr)
+    {
+      wsp->ws_wordn = newalloc;
+      wsp->ws_wordv = ptr;
+    }
+  else
+    return _wsplt_nomem (wsp);
+  return 0;
+}
+
+
+/* Node state flags */
+#define _WSNF_NULL     0x01     /* null node (a noop) */
+#define _WSNF_WORD     0x02    /* node contains word in v.word */
+#define _WSNF_QUOTE    0x04    /* text is quoted */
+#define _WSNF_NOEXPAND 0x08    /* text is not subject to expansion */
+#define _WSNF_JOIN     0x10    /* node must be joined with the next node */
+#define _WSNF_SEXP     0x20    /* is a sed expression */
+
+#define _WSNF_EMPTYOK  0x0100   /* special flag indicating that
+                                  mu_wordsplit_add_segm must add the
+                                  segment even if it is empty */
+
+struct mu_wordsplit_node
+{
+  struct mu_wordsplit_node *prev;      /* Previous element */
+  struct mu_wordsplit_node *next;      /* Next element */
+  int flags;                   /* Node flags */
+  union
+  {
+    struct
+    {
+      size_t beg;              /* Start of word in ws_input */
+      size_t end;              /* End of word in ws_input */
+    } segm;
+    char *word;
+  } v;
+};
+
+static const char *
+wsnode_flagstr (int flags)
+{
+  static char retbuf[6];
+  char *p = retbuf;
+
+  if (flags & _WSNF_WORD)
+    *p++ = 'w';
+  else if (flags & _WSNF_NULL)
+    *p++ = 'n';
+  else
+    *p++ = '-';
+  if (flags & _WSNF_QUOTE)
+    *p++ = 'q';
+  else
+    *p++ = '-';
+  if (flags & _WSNF_NOEXPAND)
+    *p++ = 'E';
+  else
+    *p++ = '-';
+  if (flags & _WSNF_JOIN)
+    *p++ = 'j';
+  else
+    *p++ = '-';
+  if (flags & _WSNF_SEXP)
+    *p++ = 's';
+  else
+    *p++ = '-';
+  *p = 0;
+  return retbuf;
+}
+
+static const char *
+wsnode_ptr (struct mu_wordsplit *wsp, struct mu_wordsplit_node *p)
+{
+  if (p->flags & _WSNF_NULL)
+    return "";
+  else if (p->flags & _WSNF_WORD)
+    return p->v.word;
+  else
+    return wsp->ws_input + p->v.segm.beg;
+}
+
+static size_t
+wsnode_len (struct mu_wordsplit_node *p)
+{
+  if (p->flags & _WSNF_NULL)
+    return 0;
+  else if (p->flags & _WSNF_WORD)
+    return strlen (p->v.word);
+  else
+    return p->v.segm.end - p->v.segm.beg;
+}
+
+static int
+wsnode_new (struct mu_wordsplit *wsp, struct mu_wordsplit_node **pnode)
+{
+  struct mu_wordsplit_node *node = calloc (1, sizeof (*node));
+  if (!node)
+    return _wsplt_nomem (wsp);
+  *pnode = node;
+  return 0;
+}
+
+static void
+wsnode_free (struct mu_wordsplit_node *p)
+{
+  if (p->flags & _WSNF_WORD)
+    free (p->v.word);
+  free (p);
+}
+
+static void
+wsnode_append (struct mu_wordsplit *wsp, struct mu_wordsplit_node *node)
+{
+  node->next = NULL;
+  node->prev = wsp->ws_tail;
+  if (wsp->ws_tail)
+    wsp->ws_tail->next = node;
+  else
+    wsp->ws_head = node;
+  wsp->ws_tail = node;
+}
+
+static void
+wsnode_remove (struct mu_wordsplit *wsp, struct mu_wordsplit_node *node)
+{
+  struct mu_wordsplit_node *p;
+
+  p = node->prev;
+  if (p)
+    p->next = node->next;
+  else
+    wsp->ws_head = node->next;
+
+  p = node->next;
+  if (p)
+    p->prev = node->prev;
+  else
+    wsp->ws_tail = node->prev;
+
+  node->next = node->prev = NULL;
+}
+
+static void
+wsnode_insert (struct mu_wordsplit *wsp, struct mu_wordsplit_node *node,
+              struct mu_wordsplit_node *anchor, int before)
+{
+  if (!wsp->ws_head)
+    {
+      node->next = node->prev = NULL;
+      wsp->ws_head = wsp->ws_tail = node;
+    }
+  else if (before)
+    {
+      if (anchor->prev)
+       wsnode_insert (wsp, node, anchor->prev, 0);
+      else
+       {
+         node->prev = NULL;
+         node->next = anchor;
+         anchor->prev = node;
+         wsp->ws_head = node;
+       }
+    }
+  else
+    {
+      struct mu_wordsplit_node *p;
+
+      p = anchor->next;
+      if (p)
+       p->prev = node;
+      else
+       wsp->ws_tail = node;
+      node->next = p;
+      node->prev = anchor;
+      anchor->next = node;
+    }
+}
+
+static int
+mu_wordsplit_add_segm (struct mu_wordsplit *wsp, size_t beg, size_t end,
+                      int flg)
+{
+  struct mu_wordsplit_node *node;
+  int rc;
+
+  if (end == beg && !(flg & _WSNF_EMPTYOK))
+    return 0;
+  rc = wsnode_new (wsp, &node);
+  if (rc)
+    return rc;
+  node->flags = flg & ~(_WSNF_WORD|_WSNF_EMPTYOK);
+  node->v.segm.beg = beg;
+  node->v.segm.end = end;
+  wsnode_append (wsp, node);
+  return 0;
+}
+
+static void
+mu_wordsplit_free_nodes (struct mu_wordsplit *wsp)
+{
+  struct mu_wordsplit_node *p;
+
+  for (p = wsp->ws_head; p;)
+    {
+      struct mu_wordsplit_node *next = p->next;
+      wsnode_free (p);
+      p = next;
+    }
+  wsp->ws_head = wsp->ws_tail = NULL;
+}
+
+static void
+mu_wordsplit_dump_nodes (struct mu_wordsplit *wsp)
+{
+  struct mu_wordsplit_node *p;
+  int n = 0;
+
+  for (p = wsp->ws_head, n = 0; p; p = p->next, n++)
+    {
+      if (p->flags & _WSNF_WORD)
+       wsp->ws_debug ("%4d: %p: %#04x (%s):%s;",
+                      n, p, p->flags, wsnode_flagstr (p->flags), p->v.word);
+      else
+       wsp->ws_debug ("%4d: %p: %#04x (%s):%.*s;",
+                      n, p, p->flags, wsnode_flagstr (p->flags),
+                      p->v.segm.end - p->v.segm.beg,
+                      wsp->ws_input + p->v.segm.beg);
+    }
+}
+
+static int
+coalesce_segment (struct mu_wordsplit *wsp, struct mu_wordsplit_node *node)
+{
+  struct mu_wordsplit_node *p, *end;
+  size_t len = 0;
+  char *buf, *cur;
+  int stop;
+
+  for (p = node; p && (p->flags & _WSNF_JOIN); p = p->next)
+    {
+      len += wsnode_len (p);
+    }
+  len += wsnode_len (p);
+  end = p;
+
+  buf = malloc (len + 1);
+  if (!buf)
+    return _wsplt_nomem (wsp);
+  cur = buf;
+
+  p = node;
+  for (stop = 0; !stop;)
+    {
+      struct mu_wordsplit_node *next = p->next;
+      const char *str = wsnode_ptr (wsp, p);
+      size_t slen = wsnode_len (p);
+
+      memcpy (cur, str, slen);
+      cur += slen;
+      if (p != node)
+       {
+         wsnode_remove (wsp, p);
+         stop = p == end;
+         wsnode_free (p);
+       }
+      p = next;
+    }
+
+  *cur = 0;
+
+  node->flags &= ~_WSNF_JOIN;
+
+  if (node->flags & _WSNF_WORD)
+    free (node->v.word);
+  else
+    node->flags |= _WSNF_WORD;
+  node->v.word = buf;
+  return 0;
+}
+
+static int
+wsnode_quoteremoval (struct mu_wordsplit *wsp)
+{
+  struct mu_wordsplit_node *p;
+  void (*uqfn) (char *, const char *, size_t) =
+    (wsp->ws_flags & MU_WRDSF_CESCAPES) ?
+    mu_wordsplit_c_unquote_copy : mu_wordsplit_sh_unquote_copy;
+
+  for (p = wsp->ws_head; p; p = p->next)
+    {
+      const char *str = wsnode_ptr (wsp, p);
+      size_t slen = wsnode_len (p);
+      int unquote;
+
+      if (wsp->ws_flags & MU_WRDSF_QUOTE)
+       {
+         unquote = !(p->flags & _WSNF_NOEXPAND);
+       }
+      else
+       unquote = 0;
+
+      if (unquote)
+       {
+         if (!(p->flags & _WSNF_WORD))
+           {
+             char *newstr = malloc (slen + 1);
+             if (!newstr)
+               return _wsplt_nomem (wsp);
+             memcpy (newstr, str, slen);
+             newstr[slen] = 0;
+             p->v.word = newstr;
+             p->flags |= _WSNF_WORD;
+           }
+         uqfn (p->v.word, str, slen);
+       }
+    }
+  return 0;
+}
+
+static int
+wsnode_coalesce (struct mu_wordsplit *wsp)
+{
+  struct mu_wordsplit_node *p;
+
+  for (p = wsp->ws_head; p; p = p->next)
+    {
+      if (p->flags & _WSNF_JOIN)
+       if (coalesce_segment (wsp, p))
+         return 1;
+    }
+  return 0;
+}
+
+static int
+mu_wordsplit_finish (struct mu_wordsplit *wsp)
+{
+  struct mu_wordsplit_node *p;
+  size_t n;
+
+  n = 0;
+
+  for (p = wsp->ws_head; p; p = p->next)
+    n++;
+
+  if (alloc_space (wsp, n + 1))
+    return 1;
+
+  for (p = wsp->ws_head; p; p = p->next)
+    {
+      const char *str = wsnode_ptr (wsp, p);
+      size_t slen = wsnode_len (p);
+      char *newstr = malloc (slen + 1);
+
+      /* Assign newstr first, even if it is NULL.  This way
+         mu_wordsplit_free will work even if we return
+         nomem later. */
+      wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc] = newstr;
+      if (!newstr)
+       return _wsplt_nomem (wsp);
+      memcpy (newstr, str, slen);
+      newstr[slen] = 0;
+
+      wsp->ws_wordc++;
+
+    }
+  wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc] = NULL;
+  return 0;
+}
+
+
+/* Variable expansion */
+static int
+node_split_prefix (struct mu_wordsplit *wsp,
+                  struct mu_wordsplit_node **ptail,
+                  struct mu_wordsplit_node *node,
+                  size_t beg, size_t len, int flg)
+{
+  struct mu_wordsplit_node *newnode;
+
+  if (len == 0)
+    return 0;
+  if (wsnode_new (wsp, &newnode))
+    return 1;
+  wsnode_insert (wsp, newnode, *ptail, 0);
+  if (node->flags & _WSNF_WORD)
+    {
+      const char *str = wsnode_ptr (wsp, node);
+      char *newstr = malloc (len + 1);
+      if (!newstr)
+       return _wsplt_nomem (wsp);
+      memcpy (newstr, str + beg, len);
+      newstr[len] = 0;
+      newnode->flags = _WSNF_WORD;
+      newnode->v.word = newstr;
+    }
+  else
+    {
+      newnode->v.segm.beg = node->v.segm.beg + beg;
+      newnode->v.segm.end = newnode->v.segm.beg + len;
+    }
+  newnode->flags |= flg;
+  *ptail = newnode;
+  return 0;
+}
+
+static int
+find_closing_cbrace (const char *str, size_t i, size_t len, size_t * poff)
+{
+  enum
+  { st_init, st_squote, st_dquote } state = st_init;
+  size_t level = 1;
+
+  for (; i < len; i++)
+    {
+      switch (state)
+       {
+       case st_init:
+         switch (str[i])
+           {
+           case '{':
+             level++;
+             break;
+
+           case '}':
+             if (--level == 0)
+               {
+                 *poff = i;
+                 return 0;
+               }
+             break;
+
+           case '"':
+             state = st_dquote;
+             break;
+
+           case '\'':
+             state = st_squote;
+             break;
+           }
+         break;
+
+       case st_squote:
+         if (str[i] == '\'')
+           state = st_init;
+         break;
+
+       case st_dquote:
+         if (str[i] == '\\')
+           i++;
+         else if (str[i] == '"')
+           state = st_init;
+         break;
+       }
+    }
+  return 1;
+}
+
+static const char *
+mu_wordsplit_find_env (struct mu_wordsplit *wsp, const char *name, size_t len)
+{
+  size_t i;
+
+  if (!(wsp->ws_flags & MU_WRDSF_ENV))
+    return NULL;
+  for (i = 0; wsp->ws_env[i]; i++)
+    {
+      size_t j;
+      const char *var = wsp->ws_env[i];
+
+      for (j = 0; j < len; j++)
+       if (name[j] != var[j])
+         break;
+      if (j == len && var[j] == '=')
+       return var + j + 1;
+    }
+  return NULL;
+}
+
+static int
+expvar (struct mu_wordsplit *wsp, const char *str, size_t len,
+       struct mu_wordsplit_node **ptail, const char **pend, int flg)
+{
+  size_t i = 0;
+  const char *defstr = NULL;
+  char *value;
+  const char *vptr;
+  struct mu_wordsplit_node *newnode;
+  const char *start = str - 1;
+
+  if (ISALPHA (str[0]) || str[0] == '_')
+    {
+      for (i = 1; i < len; i++)
+       if (!(ISALNUM (str[i]) || str[i] == '_'))
+         break;
+      *pend = str + i - 1;
+    }
+  else if (str[0] == '{')
+    {
+      str++;
+      len--;
+      for (i = 1; i < len; i++)
+       if (str[i] == '}' || str[i] == ':')
+         break;
+      if (str[i] == ':')
+       {
+         size_t j;
+
+         defstr = str + i + 1;
+         if (find_closing_cbrace (str, i + 1, len, &j))
+           {
+             wsp->ws_errno = MU_WRDSE_CBRACE;
+             return 1;
+           }
+         *pend = str + j;
+       }
+      else if (str[i] == '}')
+       {
+         defstr = NULL;
+         *pend = str + i;
+       }
+      else
+       {
+         wsp->ws_errno = MU_WRDSE_CBRACE;
+         return 1;
+       }
+    }
+  else
+    {
+      if (wsnode_new (wsp, &newnode))
+       return 1;
+      wsnode_insert (wsp, newnode, *ptail, 0);
+      *ptail = newnode;
+      newnode->flags = _WSNF_WORD | flg;
+      newnode->v.word = malloc (3);
+      if (!newnode->v.word)
+       return _wsplt_nomem (wsp);
+      newnode->v.word[0] = '$';
+      newnode->v.word[1] = str[0];
+      newnode->v.word[2] = 0;
+      *pend = str;
+      return 0;
+    }
+
+  /* Actually expand the variable */
+  /* str - start of the variable name
+     i   - its length
+     defstr - default replacement str */
+
+  vptr = mu_wordsplit_find_env (wsp, str, i);
+  if (vptr)
+    {
+      value = strdup (vptr);
+      if (!value)
+       return _wsplt_nomem (wsp);
+    }
+  else if (wsp->ws_flags & MU_WRDSF_GETVAR)
+    value = wsp->ws_getvar (str, i);
+  else if (wsp->ws_flags & MU_WRDSF_UNDEF)
+    {
+      wsp->ws_errno = MU_WRDSE_UNDEF;
+      if (wsp->ws_flags & MU_WRDSF_SHOWERR)
+       mu_wordsplit_perror (wsp);
+      return 1;
+    }
+  else
+    {
+      if (wsp->ws_flags & MU_WRDSF_WARNUNDEF)
+       wsp->ws_error (_("warning: undefined variable `%.*s'"), i, str);
+      if (wsp->ws_flags & MU_WRDSF_KEEPUNDEF)
+       value = NULL;
+      else
+       value = "";
+    }
+  /* FIXME: handle defstr */
+  if (value)
+    {
+      if (flg & _WSNF_QUOTE)
+       {
+         if (wsnode_new (wsp, &newnode))
+           return 1;
+         wsnode_insert (wsp, newnode, *ptail, 0);
+         *ptail = newnode;
+         newnode->flags = _WSNF_WORD | _WSNF_NOEXPAND | flg;
+         newnode->v.word = strdup (value);
+         if (!newnode->v.word)
+           return _wsplt_nomem (wsp);
+       }
+      else if (*value == 0)
+       {
+         /* Empty string is a special case */
+         if (wsnode_new (wsp, &newnode))
+           return 1;
+         wsnode_insert (wsp, newnode, *ptail, 0);
+         *ptail = newnode;
+         newnode->flags = _WSNF_NULL;
+       }
+      else
+       {
+         struct mu_wordsplit ws;
+         int i;
+
+         ws.ws_delim = wsp->ws_delim;
+         if (mu_wordsplit (value, &ws,
+                           MU_WRDSF_NOVAR | MU_WRDSF_NOCMD |
+                           MU_WRDSF_DELIM | MU_WRDSF_SQUEEZE_DELIMS))
+           {
+             mu_wordsplit_free (&ws);
+             return 1;
+           }
+         for (i = 0; i < ws.ws_wordc; i++)
+           {
+             if (wsnode_new (wsp, &newnode))
+               return 1;
+             wsnode_insert (wsp, newnode, *ptail, 0);
+             *ptail = newnode;
+             newnode->flags = _WSNF_WORD |
+               _WSNF_NOEXPAND |
+               (i + 1 < ws.ws_wordc ? (flg & ~_WSNF_JOIN) : flg);
+             newnode->v.word = strdup (ws.ws_wordv[i]);
+             if (!newnode->v.word)
+               return _wsplt_nomem (wsp);
+           }
+         mu_wordsplit_free (&ws);
+       }
+    }
+  else if (wsp->ws_flags & MU_WRDSF_KEEPUNDEF)
+    {
+      size_t size = *pend - start + 1;
+
+      if (wsnode_new (wsp, &newnode))
+       return 1;
+      wsnode_insert (wsp, newnode, *ptail, 0);
+      *ptail = newnode;
+      newnode->flags = _WSNF_WORD | _WSNF_NOEXPAND | flg;
+      newnode->v.word = malloc (size + 1);
+      if (!newnode->v.word)
+       return _wsplt_nomem (wsp);
+      memcpy (newnode->v.word, start, size);
+      newnode->v.word[size] = 0;
+    }
+  return 0;
+}
+
+static int
+node_expand_vars (struct mu_wordsplit *wsp, struct mu_wordsplit_node *node)
+{
+  const char *str = wsnode_ptr (wsp, node);
+  size_t slen = wsnode_len (node);
+  const char *end = str + slen;
+  const char *p;
+  size_t off = 0;
+  struct mu_wordsplit_node *tail = node;
+
+  for (p = str; p < end; p++)
+    {
+      if (*p == '\\')
+       {
+         p++;
+         continue;
+       }
+      if (*p == '$')
+       {
+         size_t n = p - str;
+
+         if (tail != node)
+           tail->flags |= _WSNF_JOIN;
+         if (node_split_prefix (wsp, &tail, node, off, n, _WSNF_JOIN))
+           return 1;
+         p++;
+         if (expvar (wsp, p, slen - n, &tail, &p,
+                     node->flags & (_WSNF_JOIN | _WSNF_QUOTE)))
+           return 1;
+         off += p - str + 1;
+         str = p + 1;
+       }
+    }
+  if (p > str)
+    {
+      if (tail != node)
+       tail->flags |= _WSNF_JOIN;
+      if (node_split_prefix (wsp, &tail, node, off, p - str,
+                            node->flags & _WSNF_JOIN))
+       return 1;
+    }
+  if (tail != node)
+    {
+      wsnode_remove (wsp, node);
+      wsnode_free (node);
+    }
+  return 0;
+}
+
+static int
+mu_wordsplit_varexp (struct mu_wordsplit *wsp)
+{
+  struct mu_wordsplit_node *p;
+
+  for (p = wsp->ws_head; p;)
+    {
+      struct mu_wordsplit_node *next = p->next;
+      if (!(p->flags & _WSNF_NOEXPAND))
+       if (node_expand_vars (wsp, p))
+         return 1;
+      p = next;
+    }
+
+  /* Remove NULL lists */
+  for (p = wsp->ws_head; p;)
+    {
+      struct mu_wordsplit_node *next = p->next;
+      if (p->flags & _WSNF_NULL)
+       {
+         wsnode_remove (wsp, p);
+         wsnode_free (p);
+       }
+      p = next;
+    }
+
+  return 0;
+}
+
+/* Strip off any leading and trailing whitespace.  This function is called
+   right after the initial scanning, therefore it assumes that every
+   node in the list is a text reference node. */ 
+static void
+mu_wordsplit_trimws (struct mu_wordsplit *wsp)
+{
+  struct mu_wordsplit_node *p;
+
+  for (p = wsp->ws_head; p; p = p->next)
+    {
+      size_t n;
+      
+      /* Skip leading whitespace: */
+      for (n = p->v.segm.beg; n < p->v.segm.end && ISWS (wsp->ws_input[n]);
+          n++)
+       ;
+      p->v.segm.beg = n;
+      /* Trim trailing whitespace */
+      for (n = p->v.segm.end; n > p->v.segm.beg && ISWS (wsp->ws_input[n-1]);
+          n--);
+      p->v.segm.end = n;
+    }
+}
+
+static int
+skip_sed_expr (const char *command, size_t i, size_t len)
+{
+  int state;
+
+  do
+    {
+      int delim;
+
+      if (command[i] == ';')
+       i++;
+      if (!(command[i] == 's' && i + 3 < len && ISPUNCT (command[i + 1])))
+       break;
+
+      delim = command[++i];
+      state = 1;
+      for (i++; i < len; i++)
+       {
+         if (state == 3)
+           {
+             if (command[i] == delim || !ISALNUM (command[i]))
+               break;
+           }
+         else if (command[i] == '\\')
+           i++;
+         else if (command[i] == delim)
+           state++;
+       }
+    }
+  while (state == 3 && i < len && command[i] == ';');
+  return i;
+}
+
+static size_t
+skip_delim (struct mu_wordsplit *wsp)
+{
+  size_t start = wsp->ws_endp;
+  if (wsp->ws_flags & MU_WRDSF_SQUEEZE_DELIMS)
+    {
+      do
+       start++;
+      while (start < wsp->ws_len
+            && ISDELIM (wsp, wsp->ws_input[start]));
+      start--;
+    }
+
+  if (!(wsp->ws_flags & MU_WRDSF_RETURN_DELIMS))
+    start++;
+
+  return start;
+}
+
+#define _MU_WRDS_EOF  0
+#define _MU_WRDS_OK   1
+#define _MU_WRDS_ERR  2
+
+static int
+scan_qstring (struct mu_wordsplit *wsp, size_t start, size_t * end)
+{
+  size_t j;
+  const char *command = wsp->ws_input;
+  size_t len = wsp->ws_len;
+  char q = command[start];
+
+  for (j = start + 1; j < len && command[j] != q; j++)
+    if (q == '"' && command[j] == '\\')
+      j++;
+  if (j < len && command[j] == q)
+    {
+      int flags = _WSNF_QUOTE;
+      if (q == '\'')
+       flags |= _WSNF_NOEXPAND;
+      if (mu_wordsplit_add_segm (wsp, start + 1, j, flags))
+       return _MU_WRDS_ERR;
+      *end = j;
+    }
+  else
+    {
+      wsp->ws_endp = start;
+      wsp->ws_errno = MU_WRDSE_QUOTE;
+      if (wsp->ws_flags & MU_WRDSF_SHOWERR)
+       mu_wordsplit_perror (wsp);
+      return _MU_WRDS_ERR;
+    }
+  return 0;
+}
+
+static int
+scan_word (struct mu_wordsplit *wsp, size_t start)
+{
+  size_t len = wsp->ws_len;
+  const char *command = wsp->ws_input;
+  const char *comment = wsp->ws_comment;
+  int join = 0;
+  int flags = 0;
+
+  size_t i = start;
+
+  if (i >= len)
+    {
+      wsp->ws_errno = MU_WRDSE_EOF;
+      return _MU_WRDS_EOF;
+    }
+
+  start = i;
+
+  if (wsp->ws_flags & MU_WRDSF_SED_EXPR
+      && command[i] == 's' && i + 3 < len && ISPUNCT (command[i + 1]))
+    {
+      flags = _WSNF_SEXP;
+      i = skip_sed_expr (command, i, len);
+    }
+  else if (!ISDELIM (wsp, command[i]))
+    {
+      while (i < len)
+       {
+         if (comment && strchr (comment, command[i]) != NULL)
+           {
+             size_t j;
+             for (j = i + 1; j < len && command[j] != '\n'; j++)
+               ;
+             if (mu_wordsplit_add_segm (wsp, start, i, 0))
+               return _MU_WRDS_ERR;
+             wsp->ws_endp = j;
+             return _MU_WRDS_OK;
+           }
+
+         if (wsp->ws_flags & MU_WRDSF_QUOTE)
+           {
+             if (command[i] == '\\')
+               {
+                 if (++i == len)
+                   break;
+                 i++;
+                 continue;
+               }
+
+             if (command[i] == '\'' || command[i] == '"')
+               {
+                 if (join && wsp->ws_tail)
+                   wsp->ws_tail->flags |= _WSNF_JOIN;
+                 if (mu_wordsplit_add_segm (wsp, start, i, _WSNF_JOIN))
+                   return _MU_WRDS_ERR;
+                 if (scan_qstring (wsp, i, &i))
+                   return _MU_WRDS_ERR;
+                 start = i + 1;
+                 join = 1;
+               }
+           }
+
+         if (ISDELIM (wsp, command[i]))
+           break;
+         else
+           i++;
+       }
+    }
+  else if (wsp->ws_flags & MU_WRDSF_RETURN_DELIMS)
+    {
+      do
+       {
+         i++;
+       }
+      while (i < len && ISDELIM (wsp, command[i]));
+    }
+  else if (!(wsp->ws_flags & MU_WRDSF_SQUEEZE_DELIMS))
+    flags |= _WSNF_EMPTYOK;
+
+  if (join && i > start && wsp->ws_tail)
+    wsp->ws_tail->flags |= _WSNF_JOIN;
+  if (mu_wordsplit_add_segm (wsp, start, i, flags))
+    return _MU_WRDS_ERR;
+  wsp->ws_endp = i;
+
+  return _MU_WRDS_OK;
+}
+
+static char quote_transtab[] = "\\\\a\ab\bf\fn\nr\rt\tv\v";
+
+int
+mu_wordsplit_c_unquote_char (int c)
+{
+  char *p;
+
+  for (p = quote_transtab; *p; p += 2)
+    {
+      if (*p == c)
+       return p[1];
+    }
+  return c;
+}
+
+int
+mu_wordsplit_c_quote_char (int c)
+{
+  char *p;
+
+  for (p = quote_transtab + sizeof (quote_transtab) - 2;
+       p > quote_transtab; p -= 2)
+    {
+      if (*p == c)
+       return p[-1];
+    }
+  return -1;
+}
+
+#define to_num(c) \
+  (ISDIGIT(c) ? c - '0' : (ISXDIGIT(c) ? toupper(c) - 'A' + 10 : 255 ))
+
+static int
+xtonum (int *pval, const char *src, int base, int cnt)
+{
+  int i, val;
+
+  for (i = 0, val = 0; i < cnt; i++, src++)
+    {
+      int n = *(unsigned char *) src;
+      if (n > 127 || (n = to_num (n)) >= base)
+       break;
+      val = val * base + n;
+    }
+  *pval = val;
+  return i;
+}
+
+size_t
+mu_wordsplit_c_quoted_length (const char *str, int quote_hex, int *quote)
+{
+  size_t len = 0;
+
+  *quote = 0;
+  for (; *str; str++)
+    {
+      if (strchr (" \"", *str))
+       *quote = 1;
+      
+      if (*str == ' ')
+       len++;
+      else if (*str == '"')
+       len += 2;
+      else if (*str != '\t' && *str != '\\' && ISPRINT (*str))
+       len++;
+      else if (quote_hex)
+       len += 3;
+      else
+       {
+         if (mu_wordsplit_c_quote_char (*str) != -1)
+           len += 2;
+         else
+           len += 4;
+       }
+    }
+  return len;
+}
+
+void
+mu_wordsplit_sh_unquote_copy (char *dst, const char *src, size_t n)
+{
+  int i;
+
+  for (i = 0; i < n;)
+    {
+      if (src[i] == '\\')
+       i++;
+      *dst++ = src[i++];
+    }
+  *dst = 0;
+}
+
+void
+mu_wordsplit_c_unquote_copy (char *dst, const char *src, size_t n)
+{
+  int i = 0;
+  int c;
+
+  while (i < n)
+    {
+      if (src[i] == '\\')
+       {
+         ++i;
+         if (src[i] == 'x' || src[i] == 'X')
+           {
+             if (n - i < 2)
+               {
+                 *dst++ = '\\';
+                 *dst++ = src[i++];
+               }
+             else
+               {
+                 int off = xtonum (&c, src + i + 1,
+                                   16, 2);
+                 if (off == 0)
+                   {
+                     *dst++ = '\\';
+                     *dst++ = src[i++];
+                   }
+                 else
+                   {
+                     *dst++ = c;
+                     i += off + 1;
+                   }
+               }
+           }
+         else if ((unsigned char) src[i] < 128 && ISDIGIT (src[i]))
+           {
+             if (n - i < 1)
+               {
+                 *dst++ = '\\';
+                 *dst++ = src[i++];
+               }
+             else
+               {
+                 int off = xtonum (&c, src + i, 8, 3);
+                 if (off == 0)
+                   {
+                     *dst++ = '\\';
+                     *dst++ = src[i++];
+                   }
+                 else
+                   {
+                     *dst++ = c;
+                     i += off;
+                   }
+               }
+           }
+         else
+           *dst++ = mu_wordsplit_c_unquote_char (src[i++]);
+       }
+      else
+       *dst++ = src[i++];
+    }
+  *dst = 0;
+}
+
+void
+mu_wordsplit_c_quote_copy (char *dst, const char *src, int quote_hex)
+{
+  for (; *src; src++)
+    {
+      if (*src == '"')
+       {
+         *dst++ = '\\';
+         *dst++ = *src;
+       }
+      else if (*src != '\t' && *src != '\\' && ISPRINT (*src))
+       *dst++ = *src;
+      else
+       {
+         char tmp[4];
+
+         if (quote_hex)
+           {
+             snprintf (tmp, sizeof tmp, "%%%02X", *(unsigned char *) src);
+             memcpy (dst, tmp, 3);
+             dst += 3;
+           }
+         else
+           {
+             int c = mu_wordsplit_c_quote_char (*src);
+             *dst++ = '\\';
+             if (c != -1)
+               *dst++ = c;
+             else
+               {
+                 snprintf (tmp, sizeof tmp, "%03o", *(unsigned char *) src);
+                 memcpy (dst, tmp, 3);
+                 dst += 3;
+               }
+           }
+       }
+    }
+}
+
+int
+mu_wordsplit_len (const char *command, size_t len, struct mu_wordsplit *wsp,
+                 int flags)
+{
+  int rc;
+  size_t start = 0;
+
+  rc = mu_wordsplit_init (wsp, command, len, flags);
+  if (rc)
+    return rc;
+
+  if (wsp->ws_flags & MU_WRDSF_SHOWDBG)
+    wsp->ws_debug ("Input:%.*s;", len, command);
+
+  if (wsp->ws_flags & MU_WRDSF_NOSPLIT)
+    {
+      /* Treat entire input as a quoted argument */
+      if (mu_wordsplit_add_segm (wsp, 0, len, _WSNF_QUOTE))
+       return wsp->ws_errno;
+    }
+  else
+    {
+      while ((rc = scan_word (wsp, start)) == _MU_WRDS_OK)
+       start = skip_delim (wsp);
+    }
+
+  if (wsp->ws_flags & MU_WRDSF_SHOWDBG)
+    {
+      wsp->ws_debug ("Initial list:");
+      mu_wordsplit_dump_nodes (wsp);
+    }
+  if (rc)
+    {
+      mu_wordsplit_free_nodes (wsp);
+      return wsp->ws_errno;
+    }
+
+  if (wsp->ws_flags & MU_WRDSF_WS)
+    {
+      /* Trim leading and trailing whitespace */
+      mu_wordsplit_trimws (wsp);
+      if (wsp->ws_flags & MU_WRDSF_SHOWDBG)
+       {
+         wsp->ws_debug ("After WS trimming:");
+         mu_wordsplit_dump_nodes (wsp);
+       }
+    }
+  
+  /* Expand variables (FIXME: & commands) */
+  if (!(wsp->ws_flags & MU_WRDSF_NOVAR))
+    {
+      if (mu_wordsplit_varexp (wsp))
+       {
+         mu_wordsplit_free_nodes (wsp);
+         return wsp->ws_errno;
+       }
+      if (wsp->ws_flags & MU_WRDSF_SHOWDBG)
+       {
+         wsp->ws_debug ("Expanded list:");
+         mu_wordsplit_dump_nodes (wsp);
+       }
+    }
+
+  do
+    {
+      if (wsnode_quoteremoval (wsp))
+       break;
+      if (wsp->ws_flags & MU_WRDSF_SHOWDBG)
+       {
+         wsp->ws_debug ("After quote removal:");
+         mu_wordsplit_dump_nodes (wsp);
+       }
+
+      if (wsnode_coalesce (wsp))
+       break;
+
+      if (wsp->ws_flags & MU_WRDSF_SHOWDBG)
+       {
+         wsp->ws_debug ("Coalesced list:");
+         mu_wordsplit_dump_nodes (wsp);
+       }
+
+      mu_wordsplit_finish (wsp);
+    }
+  while (0);
+  mu_wordsplit_free_nodes (wsp);
+  return wsp->ws_errno;
+}
+
+int
+mu_wordsplit (const char *command, struct mu_wordsplit *ws, int flags)
+{
+  return mu_wordsplit_len (command, strlen (command), ws, flags);
+}
+
+void
+mu_wordsplit_free (struct mu_wordsplit *ws)
+{
+  size_t i;
+
+  for (i = 0; i < ws->ws_wordc; i++)
+    {
+      char *p = ws->ws_wordv[ws->ws_offs + i];
+      if (p)
+       free (p);
+    }
+  free (ws->ws_wordv);
+  ws->ws_wordv = NULL;
+}
+
+void
+mu_wordsplit_perror (struct mu_wordsplit *wsp)
+{
+  switch (wsp->ws_errno)
+    {
+    case MU_WRDSE_EOF:
+      wsp->ws_error (_("no error"));
+      break;
+
+    case MU_WRDSE_QUOTE:
+      wsp->ws_error (_("missing closing %c (start near #%lu)"),
+                    wsp->ws_input[wsp->ws_endp],
+                    (unsigned long) wsp->ws_endp);
+      break;
+
+    case MU_WRDSE_NOSPACE:
+      wsp->ws_error (_("memory exhausted"));
+      break;
+
+    case MU_WRDSE_NOSUPP:
+      wsp->ws_error (_("command substitution is not yet supported"));
+
+    case MU_WRDSE_USAGE:
+      wsp->ws_error (_("invalid mu_wordsplit usage"));
+      break;
+
+    case MU_WRDSE_CBRACE:
+      wsp->ws_error (_("unbalanced curly brace"));
+      break;
+
+    case MU_WRDSE_UNDEF:
+      wsp->ws_error (_("undefined variable"));
+      break;
+
+    default:
+      wsp->ws_error (_("unknown error"));
+    }
+}
+
+const char *_mu_wordsplit_errstr[] = {
+  N_("no error"),
+  N_("missing closing quote"),
+  N_("memory exhausted"),
+  N_("variable expansion and command substitution " "are not yet supported"),
+  N_("invalid mu_wordsplit usage"),
+  N_("unbalanced curly brace"),
+  N_("undefined variable")
+};
+int _mu_wordsplit_nerrs =
+  sizeof (_mu_wordsplit_errstr) / sizeof (_mu_wordsplit_errstr[0]);
+
+const char *
+mu_wordsplit_strerror (struct mu_wordsplit *ws)
+{
+  if (ws->ws_errno < _mu_wordsplit_nerrs)
+    return _mu_wordsplit_errstr[ws->ws_errno];
+  return N_("unknown error");
+}
diff --git a/libmailutils/tests/.gitignore b/libmailutils/tests/.gitignore
index ec75020..0157222 100644
--- a/libmailutils/tests/.gitignore
+++ b/libmailutils/tests/.gitignore
@@ -13,3 +13,4 @@ listop
 mailcap
 url-parse
 wicket
+wsp
diff --git a/libmailutils/tests/Makefile.am b/libmailutils/tests/Makefile.am
index f714b58..e34d265 100644
--- a/libmailutils/tests/Makefile.am
+++ b/libmailutils/tests/Makefile.am
@@ -48,7 +48,8 @@ noinst_PROGRAMS = \
  listop\
  mailcap\
  url-parse\
- wicket
+ wicket\
+ wsp
 
 LDADD = ${MU_LIB_MAILUTILS}
 
@@ -70,7 +71,8 @@ TESTSUITE_AT = \
  mailcap.at\
  testsuite.at\
  url.at\
- wicket.at
+ wicket.at\
+ wordsplit.at
 
 TESTSUITE = $(srcdir)/testsuite
 M4=m4
diff --git a/libmailutils/tests/argcv.c b/libmailutils/tests/argcv.c
index 88c54a2..66e9060 100644
--- a/libmailutils/tests/argcv.c
+++ b/libmailutils/tests/argcv.c
@@ -22,6 +22,7 @@
 #include <string.h>
 #include <errno.h>
 #include <unistd.h>
+#define MU_ARCGV_DEPRECATED
 #include <mailutils/argcv.h>
 #include <mailutils/errno.h>
 
diff --git a/libmailutils/tests/listop.c b/libmailutils/tests/listop.c
index 564e1d3..89e72eb 100644
--- a/libmailutils/tests/listop.c
+++ b/libmailutils/tests/listop.c
@@ -21,7 +21,6 @@
 #include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <mailutils/argcv.h>
 #include <mailutils/mailutils.h>
 
 static int interactive;
@@ -467,9 +466,8 @@ shell (mu_list_t list)
     {
       char *text = NULL;
       char buf[80];
-      int argc;
-      char **argv;
-
+      struct mu_wordsplit ws;
+      
       if (!itr[num])
        {
          rc = mu_list_get_iterator (list, &itr[num]);
@@ -487,40 +485,44 @@ shell (mu_list_t list)
       if (fgets (buf, sizeof buf, stdin) == NULL)
        return;
 
-      rc = mu_argcv_get (buf, "", "#", &argc, &argv);
-      if (rc)
-       lperror ("mu_argcv_get", rc);
+      ws.ws_comment = "#";
+      if (mu_wordsplit (buf, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT))
+       {
+         mu_error ("cannot split line `%s': %s", buf,
+                   mu_wordsplit_strerror (&ws));
+         exit (1);
+       }
 
-      if (argc > 0)
+      if (ws.ws_wordc > 0)
        {
-         if (strcmp (argv[0], "count") == 0)
+         if (strcmp (ws.ws_wordv[0], "count") == 0)
            count (list);
-         else if (strcmp (argv[0], "next") == 0)
-           next (itr[num], argv[1]);
-         else if (strcmp (argv[0], "first") == 0)
+         else if (strcmp (ws.ws_wordv[0], "next") == 0)
+           next (itr[num], ws.ws_wordv[1]);
+         else if (strcmp (ws.ws_wordv[0], "first") == 0)
            mu_iterator_first (itr[num]);
-         else if (strcmp (argv[0], "del") == 0)
-           delete (list, argc, argv);
-         else if (strcmp (argv[0], "add") == 0)
-           add (list, argc, argv);
-         else if (strcmp (argv[0], "prep") == 0)
-           prep (list, argc, argv);
-         else if (strcmp (argv[0], "ins") == 0)
-           ins (list, argc, argv);
-         else if (strcmp (argv[0], "repl") == 0)
-           repl (list, argc, argv);
-         else if (strcmp (argv[0], "ictl") == 0)
-           ictl (itr[num], argc, argv);
-         else if (strcmp (argv[0], "print") == 0)
+         else if (strcmp (ws.ws_wordv[0], "del") == 0)
+           delete (list, ws.ws_wordc, ws.ws_wordv);
+         else if (strcmp (ws.ws_wordv[0], "add") == 0)
+           add (list, ws.ws_wordc, ws.ws_wordv);
+         else if (strcmp (ws.ws_wordv[0], "prep") == 0)
+           prep (list, ws.ws_wordc, ws.ws_wordv);
+         else if (strcmp (ws.ws_wordv[0], "ins") == 0)
+           ins (list, ws.ws_wordc, ws.ws_wordv);
+         else if (strcmp (ws.ws_wordv[0], "repl") == 0)
+           repl (list, ws.ws_wordc, ws.ws_wordv);
+         else if (strcmp (ws.ws_wordv[0], "ictl") == 0)
+           ictl (itr[num], ws.ws_wordc, ws.ws_wordv);
+         else if (strcmp (ws.ws_wordv[0], "print") == 0)
            print (list);
-         else if (strcmp (argv[0], "cur") == 0)
+         else if (strcmp (ws.ws_wordv[0], "cur") == 0)
            cur (num, itr[num]);
-         else if (strcmp (argv[0], "quit") == 0)
+         else if (strcmp (ws.ws_wordv[0], "quit") == 0)
            return;
-         else if (strcmp (argv[0], "iter") == 0)
+         else if (strcmp (ws.ws_wordv[0], "iter") == 0)
            {
              int n;
-             if (iter (&n, argc, argv) == 0 && !itr[n])
+             if (iter (&n, ws.ws_wordc, ws.ws_wordv) == 0 && !itr[n])
                {
                  rc = mu_list_get_iterator (list, &itr[n]);
                  if (rc)
@@ -529,24 +531,24 @@ shell (mu_list_t list)
                }
              num = n;
            }
-         else if (strcmp (argv[0], "close") == 0)
+         else if (strcmp (ws.ws_wordv[0], "close") == 0)
            {
              int n;
-             if (iter (&n, argc, argv) == 0)
+             if (iter (&n, ws.ws_wordc, ws.ws_wordv) == 0)
                {
                  mu_iterator_destroy (&itr[n]);
                  if (n == num && ++num == NITR)
                    num = 0;
                }
            }
-         else if (strcmp (argv[0], "find") == 0)
-           find (itr[num], argv[1]);
-         else if (strcmp (argv[0], "help") == 0)
+         else if (strcmp (ws.ws_wordv[0], "find") == 0)
+           find (itr[num], ws.ws_wordv[1]);
+         else if (strcmp (ws.ws_wordv[0], "help") == 0)
            help ();
-         else if (argc == 1)
+         else if (ws.ws_wordc == 1)
            {
              char *p;
-             size_t n = strtoul (argv[0], &p, 0);
+             size_t n = strtoul (ws.ws_wordv[0], &p, 0);
              if (*p != 0)
                fprintf (stderr, "?\n");
              else
@@ -561,7 +563,7 @@ shell (mu_list_t list)
          else
            fprintf (stderr, "?\n");
        }
-      mu_argcv_free (argc, argv);
+      mu_wordsplit_free (&ws);
     }
 }
 
diff --git a/libmailutils/tests/testsuite.at b/libmailutils/tests/testsuite.at
index d5d02aa..78fb693 100644
--- a/libmailutils/tests/testsuite.at
+++ b/libmailutils/tests/testsuite.at
@@ -56,6 +56,7 @@ AT_INIT
 m4_include([list.at])
 m4_include([address.at])
 m4_include([argcv.at])
+m4_include([wordsplit.at])
 m4_include([url.at])
 m4_include([mailcap.at])
 m4_include([base64e.at])
diff --git a/libmailutils/tests/wordsplit.at b/libmailutils/tests/wordsplit.at
new file mode 100644
index 0000000..dca3792
--- /dev/null
+++ b/libmailutils/tests/wordsplit.at
@@ -0,0 +1,304 @@
+# This file is part of GNU Mailutils. -*- Autotest -*-
+# Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+#
+# GNU Mailutils 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 3, or (at
+# your option) any later version.
+#
+# GNU Mailutils 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 GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>.
+
+dnl ------------------------------------------------------------
+dnl TESTWSP([NAME], [KW = `'], [OPTS], [INPUT], [STDOUT = `'],
+dnl         [STDERR = `'], [ENV])
+dnl
+m4_pushdef([TESTWSP],[
+m4_pushdef([MU_TEST_GROUP],[Wordsplit])
+m4_pushdef([MU_TEST_KEYWORDS],[wordsplit wsp])
+m4_pushdef([MU_TEST_COMMAND],[$7 wsp $3])
+MU_GENERIC_TEST([$1],[$2],[$4],[],[$5],[$6])
+m4_popdef([MU_TEST_COMMAND])
+m4_popdef([MU_TEST_KEYWORDS])
+m4_popdef([MU_TEST_GROUP])
+])
+
+dnl ------------------------------------------------------------
+dnl The first part reproduces legacy argcv tests
+dnl ------------------------------------------------------------
+
+TESTWSP([simple input],[],[],
+[1 2 3],
+[NF: 3
+0: 1
+1: 2
+2: 3
+])
+
+TESTWSP([quoted space],[],[],
+[quoted\ space],
+[NF: 1
+0: "quoted space"
+])
+
+TESTWSP([tab character],[],[],
+[a "tab        character"],
+[NF: 2
+0: a
+1: tab\tcharacter
+])
+
+TESTWSP([octal and hex escapes],[],[],
+[\157\143\164\141\154\40and\x20\x68\x65\x78],
+[NF: 1
+0: "octal and hex"
+])
+
+TESTWSP([octal and hex escapes 2],[],[],
+[\157\143\164\141\154\40 and \x20\x68\x65\x78],
+[NF: 3
+0: "octal "
+1: and
+2: " hex"
+])
+
+TESTWSP([escape representation],[],[],
+[A\x3-\48\39],
+[NF: 1
+0: A\003-\0048\0039
+])
+
+dnl ------------------------------------------------------------
+dnl Test worsplit-specific behavior
+dnl ------------------------------------------------------------
+TESTWSP([append],[],[append],
+[jeden dwa trzy
+cztery
+piec szesc],
+[NF: 3
+0: jeden
+1: dwa
+2: trzy
+NF: 4
+0: jeden
+1: dwa
+2: trzy
+3: cztery
+NF: 6
+0: jeden
+1: dwa
+2: trzy
+3: cztery
+4: piec
+5: szesc
+])
+
+TESTWSP([dooffs],[],[dooffs 3 jeden dwa trzy],
+[cztery piec],
+[NF: 2 (3)
+(0): jeden
+(1): dwa
+(2): trzy
+3: cztery
+4: piec
+])
+
+TESTWSP([variable substitutions: single var],[],[],
+[a $FOO test],
+[NF: 3
+0: a
+1: bar
+2: test
+],
+[],
+[FOO=bar])
+
+TESTWSP([variable substitutions: concatenated vars],[],[],
+[a $FOO${BAR}ent test],
+[NF: 3
+0: a
+1: stringent
+2: test
+],
+[],
+[FOO=str BAR=ing])
+
+TESTWSP([variable substitutions: field splitting],[],[],
+[a $FOO test],
+[NF: 4
+0: a
+1: variable
+2: substitution
+3: test
+],
+[],
+[FOO="variable substitution"])
+
+TESTWSP([variable substitutions: double-quoted variable],[],[],
+[a "$FOO" test],
+[NF: 3
+0: a
+1: "variable substitution"
+2: test
+],
+[],
+[FOO="variable substitution"])
+
+TESTWSP([variable substitutions: single-quoted variable],[],[],
+[a '$FOO' test],
+[NF: 3
+0: a
+1: $FOO
+2: test
+],
+[],
+[FOO="variable substitution"])
+
+TESTWSP([undefined variables 1],[],[],
+[a $FOO test a${FOO}b],
+[NF: 3
+0: a
+1: test
+2: ab
+],
+[],
+[unset FOO;])
+
+TESTWSP([undefined variables 2],[],[keepundef],
+[a $FOO test a${FOO}b],
+[NF: 4
+0: a
+1: $FOO
+2: test
+3: a${FOO}b
+],
+[],
+[unset FOO;])
+
+TESTWSP([warn about undefined variables],[],[warnundef],
+[$FOO],
+[NF: 0
+],
+[warning: undefined variable `FOO'
+],
+[unset FOO;])
+
+TESTWSP([bail out on undefined variables],[],[undef],
+[$FOO],
+[],
+[undefined variable
+],
+[unset FOO;])
+
+TESTWSP([disable variable expansion],[],[novar],
+[$FOO],
+[NF: 1
+0: $FOO
+],
+[],
+[FOO=bar])
+
+TESTWSP([nosplit with expansion],[],[nosplit],
+[a $FOO test],
+[NF: 1
+0: "a variable expansion test\n"
+],
+[],
+[FOO="variable expansion"])
+
+TESTWSP([nosplit without expansion],[],[nosplit novar],
+[a $FOO test],
+[NF: 1
+0: "a $FOO test\n"
+],
+[],
+[FOO="variable expansion"])
+
+TESTWSP([ignore quotes],[],[-quote],
+["a text"],
+[NF: 2
+0: "\"a"
+1: "text\""
+])
+
+TESTWSP([custom delimiters (squeeze)],[],[delim : -ws trimnl],
+[semicolon: separated::list: of :words],
+[NF: 5
+0: semicolon
+1: " separated"
+2: list
+3: " of "
+4: words
+])
+
+TESTWSP([custom delimiters (no squeeze)],[],[delim : -ws -squeeze_delims 
trimnl],
+[semicolon: separated::list: of :words],
+[NF: 6
+0: semicolon
+1: " separated"
+2: ""
+3: list
+4: " of "
+5: words
+])
+
+TESTWSP([custom, with returned delimiters],[],[delim : -ws trimnl 
return_delims],
+[semicolon: separated::list: of :words],
+[NF: 9
+0: semicolon
+1: :
+2: " separated"
+3: :
+4: list
+5: :
+6: " of "
+7: :
+8: words
+])
+
+TESTWSP([custom, with returned & squeezed delimiters],[],[delim : -ws trimnl 
return_delims -squeeze_delims],
+[semicolon: separated::list: of :words],
+[NF: 9
+0: semicolon
+1: :
+2: " separated"
+3: ::
+4: list
+5: :
+6: " of "
+7: :
+8: words
+])
+
+TESTWSP([sed expressions],[],[sed],
+[arg1 s/foo/bar/g;s/bar baz/quz quux/ arg2],
+[NF: 3
+0: arg1
+1: "s/foo/bar/g;s/bar baz/quz quux/"
+2: arg2
+])
+
+TESTWSP([C escapes on],[],[cescapes],
+[a\ttab form\ffeed and new\nline],
+[NF: 4
+0: a\ttab
+1: form\ffeed
+2: and
+3: new\nline
+])
+
+TESTWSP([C escapes off],[],[-cescapes],
+[a\ttab form\ffeed and new\nline],
+[NF: 4
+0: attab
+1: formffeed
+2: and
+3: newnline
+])
+
+m4_popdef([TESTWSP])
diff --git a/libmailutils/tests/wsp.c b/libmailutils/tests/wsp.c
new file mode 100644
index 0000000..095ce66
--- /dev/null
+++ b/libmailutils/tests/wsp.c
@@ -0,0 +1,285 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 2005, 2007, 2009, 2010 Free Software Foundation, Inc.
+
+   GNU Mailutils 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 3, or (at your option)
+   any later version.
+
+   GNU Mailutils 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 GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif 
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <mailutils/wordsplit.h>
+#include <mailutils/kwd.h>
+#include <mailutils/errno.h>
+#include <mailutils/error.h>
+#include <mailutils/cstr.h>
+
+extern char **environ;
+
+struct mu_kwd bool_keytab[] = {
+  { "append", MU_WRDSF_APPEND },
+  /*{ "reuse",  MU_WRDSF_REUSE },*/
+  { "undef",  MU_WRDSF_UNDEF },
+  { "novar",  MU_WRDSF_NOVAR },
+  { "ws",     MU_WRDSF_WS },
+  { "quote",  MU_WRDSF_QUOTE },
+  { "squeeze_delims", MU_WRDSF_SQUEEZE_DELIMS },
+  { "return_delims",  MU_WRDSF_RETURN_DELIMS },
+  { "sed",     MU_WRDSF_SED_EXPR },
+  { "debug",   MU_WRDSF_SHOWDBG },
+  { "nosplit",  MU_WRDSF_NOSPLIT },
+  { "keepundef", MU_WRDSF_KEEPUNDEF },
+  { "warnundef", MU_WRDSF_WARNUNDEF },
+  { "cescapes", MU_WRDSF_CESCAPES },
+  { "default", MU_WRDSF_DEFFLAGS },
+  { NULL, 0 }
+};
+
+struct mu_kwd string_keytab[] = {
+  { "delim",   MU_WRDSF_DELIM },
+  { "comment", MU_WRDSF_COMMENT },
+  { NULL, 0 }
+};
+
+static void
+help ()
+{
+  size_t i;
+  
+  printf ("usage: wsp [options]\n");
+  printf ("options are:\n");
+  printf (" [-]trimnl\n");
+  printf (" [-]plaintext\n");
+  putchar ('\n');
+  for (i = 0; bool_keytab[i].name; i++)
+    printf (" [-]%s\n", bool_keytab[i].name);
+  putchar ('\n');
+  for (i = 0; string_keytab[i].name; i++)
+    {
+      printf (" -%s\n", bool_keytab[i].name);
+      printf (" %s ARG\n", bool_keytab[i].name);
+    }
+  putchar ('\n');
+  printf (" -dooffs\n");
+  printf (" dooffs COUNT ARGS...\n");
+  exit (0);
+}
+
+void
+print_qword (const char *word, int plaintext)
+{
+  static char *qbuf = NULL;
+  static size_t qlen = 0;
+  int quote;
+  size_t size = mu_wordsplit_c_quoted_length (word, 0, &quote);
+
+  if (plaintext)
+    {
+      printf ("%s", word);
+      return;
+    }
+
+  if (*word == 0)
+    quote = 1;
+  
+  if (size >= qlen)
+    {
+      qlen = size + 1;
+      qbuf = realloc (qbuf, qlen);
+      if (!qbuf)
+       {
+         mu_error ("not enough memory");
+         abort ();
+       }
+    }
+  mu_wordsplit_c_quote_copy (qbuf, word, 0);
+  qbuf[size] = 0;
+  if (quote)
+    printf ("\"%s\"", qbuf);
+  else
+    printf ("%s", qbuf);
+}
+
+int
+main (int argc, char **argv)
+{
+  char buf[1024];
+  int i, offarg = 0;
+  int trimnl_option = 0;
+  int plaintext_option = 0;
+  int wsflags = (MU_WRDSF_DEFFLAGS & ~MU_WRDSF_NOVAR) |
+                 MU_WRDSF_ENOMEMABRT |
+                 MU_WRDSF_ENV | MU_WRDSF_SHOWERR;
+  struct mu_wordsplit ws;
+
+  for (i = 1; i < argc; i++)
+    {
+      char *opt = argv[i];
+      int negate;
+      int flag;
+
+      if (opt[0] == '-')
+       {
+         negate = 1;
+         opt++;
+       }
+      else if (opt[0] == '+')
+       {
+         negate = 0;
+         opt++;
+       }
+      else
+       negate = 0;
+
+      if (strcmp (opt, "h") == 0 ||
+         strcmp (opt, "help") == 0 ||
+         strcmp (opt, "-help") == 0)
+       {
+         help ();
+       }
+         
+      if (strcmp (opt, "trimnl") == 0)
+       {
+         trimnl_option = !negate;
+         continue;
+       }
+
+      if (strcmp (opt, "plaintext") == 0)
+       {
+         plaintext_option = !negate;
+         continue;
+       }
+
+      if (mu_kwd_xlat_name (bool_keytab, opt, &flag) == 0)
+       {
+         if (negate)
+           wsflags &= ~flag;
+         else
+           wsflags |= flag;
+         continue;
+       }
+
+      if (mu_kwd_xlat_name (string_keytab, opt, &flag) == 0)
+       {
+         if (negate)
+           wsflags &= ~flag;
+         else
+           {
+             i++;
+             if (i == argc)
+               {
+                 mu_error ("%s missing argument", opt);
+                 exit (1);
+               }
+             
+             switch (flag)
+               {
+               case MU_WRDSF_DELIM:
+                 ws.ws_delim = argv[i];
+                 break;
+
+               case MU_WRDSF_COMMENT:
+                 ws.ws_comment = argv[i];
+                 break;
+               }
+             
+             wsflags |= flag;
+           }
+         continue;
+       }
+
+      if (strcmp (opt, "dooffs") == 0)
+       {
+         if (negate)
+           wsflags &= ~MU_WRDSF_DOOFFS;
+         else
+           {
+             char *p;
+
+             i++;
+             
+             if (i == argc)
+               {
+                 mu_error ("%s missing arguments", opt);
+                 exit (1);
+               }
+             ws.ws_offs = strtoul (argv[i], &p, 10);
+             if (*p)
+               {
+                 mu_error ("invalid number: %s", argv[i]);
+                 exit (1);
+               }
+
+             i++;
+             if (i + ws.ws_offs > argc)
+               {
+                 mu_error ("%s: not enough arguments", opt);
+                 exit (1);
+               }
+             offarg = i;
+             i += ws.ws_offs - 1;
+             wsflags |= MU_WRDSF_DOOFFS;
+           }
+         continue;
+       }
+
+      mu_error ("%s: unrecognized argument", opt);
+      exit (1);
+    }
+
+  ws.ws_env = (const char **) environ;
+       
+  while (fgets (buf, sizeof (buf), stdin))
+    {
+      int rc;
+      size_t i;
+      
+      if (trimnl_option)
+       mu_rtrim_cset (buf, "\n");
+      rc = mu_wordsplit (buf, &ws, wsflags);
+      if (rc)
+       {
+         if (!(wsflags & MU_WRDSF_SHOWERR))
+           mu_wordsplit_perror (&ws);
+         continue;
+       }
+         
+      if (offarg)
+       {
+         for (i = 0; i < ws.ws_offs; i++)
+           ws.ws_wordv[i] = argv[offarg + i];
+         offarg = 0;
+       }
+      
+      wsflags |= MU_WRDSF_REUSE;
+      printf ("NF: %lu", (unsigned long) ws.ws_wordc);
+      if (wsflags & MU_WRDSF_DOOFFS)
+       printf (" (%lu)", (unsigned long) ws.ws_offs);
+      putchar ('\n');
+      for (i = 0; i < ws.ws_offs; i++)
+       {
+         printf ("(%lu): ", (unsigned long) i);
+         print_qword (ws.ws_wordv[i], plaintext_option);
+         putchar ('\n');
+       }
+      for (; i < ws.ws_offs + ws.ws_wordc; i++)
+       {
+         printf ("%lu: ", (unsigned long) i);
+         print_qword (ws.ws_wordv[i], plaintext_option);
+         putchar ('\n');
+       }
+    }
+  return 0;
+}
diff --git a/libmailutils/url/copy.c b/libmailutils/url/copy.c
index 7190b42..ee5ad47 100644
--- a/libmailutils/url/copy.c
+++ b/libmailutils/url/copy.c
@@ -24,7 +24,7 @@
 #ifdef HAVE_STRINGS_H
 # include <strings.h>
 #endif
-
+#include <stdlib.h>
 #include <mailutils/types.h>
 #include <mailutils/argcv.h>
 #include <mailutils/secret.h>
diff --git a/libmailutils/url/create.c b/libmailutils/url/create.c
index 674a524..0d0cb30 100644
--- a/libmailutils/url/create.c
+++ b/libmailutils/url/create.c
@@ -26,10 +26,12 @@
 #ifdef HAVE_STRINGS_H
 # include <strings.h>
 #endif
+#include <stdlib.h>
 #include <netdb.h>
 #include <arpa/inet.h>
 #include <limits.h>
 
+#include <mailutils/wordsplit.h>
 #include <mailutils/util.h>
 #include <mailutils/errno.h>
 #include <mailutils/argcv.h>
@@ -375,19 +377,24 @@ _mu_url_create_internal (struct mu_url_ctx *ctx, mu_url_t 
hint)
 
   if ((ctx->flags & MU_URL_PARSE_PIPE) && ctx->input[0] == '|')
     {
+      struct mu_wordsplit ws;
+      
       rc = str_assign (&url->scheme, "prog");
       if (rc)
        return rc;
       url->flags |= MU_URL_SCHEME;
       ctx->flags &= ~MU_URL_PARSE_HEXCODE;
-      rc = mu_argcv_get (ctx->input + 1, NULL, NULL, &url->qargc, &url->qargv);
+      if (mu_wordsplit (ctx->input + 1, &ws, MU_WRDSF_DEFFLAGS))
+       return errno;
+      url->qargc = ws.ws_wordc;
+      url->qargv = ws.ws_wordv;
+      ws.ws_wordc = 0;
+      ws.ws_wordv = NULL;
+      mu_wordsplit_free (&ws);
+      url->flags |= MU_URL_QUERY;
+      rc = str_assign (&url->path, url->qargv[0]);
       if (rc == 0)
-       {
-         url->flags |= MU_URL_QUERY;
-         rc = str_assign (&url->path, url->qargv[0]);
-         if (rc == 0)
-           url->flags |= MU_URL_PATH;
-       }
+       url->flags |= MU_URL_PATH;
     }
   else if ((ctx->flags & MU_URL_PARSE_SLASH) && ctx->input[0] == '/')
     {
diff --git a/libmu_argp/muinit.c b/libmu_argp/muinit.c
index ab6e9c9..7bffdea 100644
--- a/libmu_argp/muinit.c
+++ b/libmu_argp/muinit.c
@@ -22,6 +22,7 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <mailutils/stream.h>
+#include <mailutils/io.h>
 #include "xalloc.h"
 #include <string.h>
 #ifdef MU_ALPHA_RELEASE
diff --git a/libmu_auth/ldap.c b/libmu_auth/ldap.c
index 250aa40..435c99d 100644
--- a/libmu_auth/ldap.c
+++ b/libmu_auth/ldap.c
@@ -19,15 +19,18 @@
 #ifdef HAVE_CONFIG_H
 # include <config.h>
 #endif
+#include <stdlib.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <ctype.h>
 
 #include <mailutils/mu_auth.h>
 #include <mailutils/cstr.h>
+#include <mailutils/io.h>
 
 #ifdef WITH_LDAP
 #include "mailutils/argcv.h"
+#include "mailutils/wordsplit.h"
 #include "mailutils/assoc.h"
 #include "mailutils/list.h"
 #include "mailutils/iterator.h"
@@ -129,10 +132,9 @@ _mu_conn_setup (LDAP **pld)
            {
              /* if no host but a DN is provided, try DNS SRV to gather the
                 host list */
-             char      *domain = NULL, *hostlist = NULL, **hosts = NULL;
-             int hostcnt;
-             int i;
-             int len_proto = strlen(lud->lud_scheme);
+             char *domain = NULL, *hostlist = NULL;
+             size_t i;
+             struct mu_wordsplit ws;
              
              if (ldap_dn2domain (lud->lud_dn, &domain) || !domain)
                {
@@ -148,16 +150,15 @@ _mu_conn_setup (LDAP **pld)
                            domain);
                  goto dnssrv_free;
                }
-             
-             rc = mu_argcv_get (hostlist, " ", NULL, &hostcnt, &hosts);
-             if (rc)
+
+             if (mu_wordsplit (hostlist, &ws, MU_WRDSF_DEFFLAGS))
                {
                  mu_error (_("DNS SRV: could not parse hostlist=\"%s\": %s"),
-                           hostlist, mu_strerror (rc));
+                           hostlist, mu_wordsplit_strerror (&ws));
                  goto dnssrv_free;
                }
              
-             tmp = realloc (urls, sizeof(char *) * (nurls + hostcnt + 1));
+             tmp = realloc (urls, sizeof(char *) * (nurls + ws.ws_wordc + 1));
              if (!tmp)
                {
                  mu_error ("DNS SRV %s", mu_strerror (errno));
@@ -167,28 +168,23 @@ _mu_conn_setup (LDAP **pld)
              urls = tmp;
              urls[nurls] = NULL;
              
-             for (i = 0; i < hostcnt; i++)
+             for (i = 0; i < ws.ws_wordc; i++)
                {
-                 size_t len = len_proto + sizeof "://" - 1
-                              + strlen (hosts[i])
-                              + 1;
-
                  urls[nurls + i + 1] = NULL;
-                 urls[nurls + i] = malloc (len);
-                 if (!urls[nurls + i])
+                 rc = mu_asprintf (&urls[nurls + i],
+                                   "%s://%s",
+                                   lud->lud_scheme, ws.ws_wordv[i]);
+                 if (rc)
                    {
-                     mu_error ("DNS SRV %s", mu_strerror (errno));
+                     mu_error ("DNS SRV %s", mu_strerror (rc));
                      goto dnssrv_free;
                    }
-                 
-                 snprintf (urls[nurls + i], len, "%s://%s",
-                           lud->lud_scheme, hosts[i]);
                }
              
              nurls += i;
              
            dnssrv_free:
-             mu_argcv_free (hostcnt, hosts);
+             mu_wordsplit_free (&ws);
              ber_memfree (hostlist);
              ber_memfree (domain);
            }
diff --git a/libmu_auth/radius.c b/libmu_auth/radius.c
index 048a9c7..8c8a648 100644
--- a/libmu_auth/radius.c
+++ b/libmu_auth/radius.c
@@ -35,13 +35,14 @@
 #include <mailutils/iterator.h>
 #include <mailutils/mailbox.h>
 #include <mailutils/radius.h>
-#include <mailutils/argcv.h>
+#include <mailutils/wordsplit.h>
 #include <mailutils/mu_auth.h>
 #include <mailutils/error.h>
 #include <mailutils/errno.h>
 #include <mailutils/nls.h>
 #include <mailutils/vartab.h>
 #include <mailutils/io.h>
+#include <mailutils/cctype.h>
 
 #ifdef ENABLE_RADIUS
 
@@ -87,9 +88,8 @@ enum parse_state
 int
 parse_pairlist (grad_avp_t **plist, char *input)
 {
-  int rc;
-  int i, argc;
-  char **argv;
+  size_t i;
+  struct mu_wordsplit ws;
   enum parse_state state;
   grad_locus_t loc;
   char *name;
@@ -98,34 +98,38 @@ parse_pairlist (grad_avp_t **plist, char *input)
   if (!input)
     return 1;
 
-  if ((rc = mu_argcv_get (input, ",", NULL, &argc, &argv)))
+  ws.ws_delim = ",";
+  if (mu_wordsplit (input, &ws,
+                   MU_WRDSF_DEFFLAGS|MU_WRDSF_DELIM|MU_WRDSF_RETURN_DELIMS))
     {
-      mu_error (_("cannot parse input `%s': %s"), input, mu_strerror (rc));
+      mu_error (_("cannot parse input `%s': %s"), input,
+               mu_wordsplit_strerror (&ws));
       return 1;
     }
 
   loc.file = "<configuration>"; /*FIXME*/
   loc.line = 0;
 
-  for (i = 0, state = state_lhs; i < argc; i++)
+  for (i = 0, state = state_lhs; i < ws.ws_wordc; i++)
     {
       grad_avp_t *pair;
 
       switch (state)
        {
        case state_lhs:
-         name = argv[i];
+         name = ws.ws_wordv[i];
          state = state_op;
          break;
 
        case state_op:
-         op = argv[i];
+         op = ws.ws_wordv[i];
          state = state_rhs;
          break;
 
        case state_rhs:
          loc.line = i; /* Just to keep track of error location */
-         pair = grad_create_pair (&loc, name, grad_operator_equal, argv[i]);
+         pair = grad_create_pair (&loc, name, grad_operator_equal,
+                                  ws.ws_wordv[i]);
          if (!pair)
            {
              mu_error (_("cannot create radius A/V pair `%s'"), name);
@@ -136,22 +140,22 @@ parse_pairlist (grad_avp_t **plist, char *input)
          break;
 
        case state_delim:
-         if (strcmp (argv[i], ","))
+         if (strcmp (ws.ws_wordv[i], ","))
            {
-             mu_error (_("expected `,' but found `%s'"), argv[i]);
+             mu_error (_("expected `,' but found `%s'"), ws.ws_wordv[i]);
              return 1;
            }
          state = state_lhs;
        }
     }
-
+  mu_wordsplit_free (&ws);
+  
   if (state != state_delim && state != state_delim)
     {
       mu_error (_("malformed radius A/V list"));
       return 1;
     }
 
-  mu_argcv_free (argc, argv);
   return 0;
 }
 
diff --git a/libmu_sieve/sieve.l b/libmu_sieve/sieve.l
index 2f60e13..27a92d4 100644
--- a/libmu_sieve/sieve.l
+++ b/libmu_sieve/sieve.l
@@ -31,7 +31,7 @@
 #include <errno.h>
 #include <string.h>
 #include <mailutils/cctype.h>
-#include <mailutils/argcv.h>
+#include <mailutils/wordsplit.h>
 #include <sieve-priv.h>
 #include <sieve-gram.h>
   
@@ -640,7 +640,7 @@ str_unescape (char *text, size_t len)
 {
   char *str = mu_sieve_alloc (len);
   memcpy (str, text, len - 2);
-  str[len - 2] = mu_argcv_unquote_char (text[len - 1]);
+  str[len - 2] = mu_wordsplit_c_unquote_char (text[len - 1]);
   str[len - 1] = 0;
   return str;
 }
diff --git a/libproto/mailer/mbox.c b/libproto/mailer/mbox.c
index 1353d42..132e3d8 100644
--- a/libproto/mailer/mbox.c
+++ b/libproto/mailer/mbox.c
@@ -29,7 +29,7 @@
 #include <mailutils/mailer.h>
 #include <mailutils/url.h>
 #include <mailutils/util.h>
-#include <mailutils/argcv.h>
+#include <mailutils/wordsplit.h>
 #include <mailutils/message.h>
 #include <mailutils/envelope.h>
 #include <mailutils/header.h>
@@ -124,8 +124,8 @@ static int
 parse_received (mu_header_t hdr, char **sptr)
 {
   const char *recv;
-  int wc, i;
-  char **ws;
+  size_t i;
+  struct mu_wordsplit ws;
   enum { rcv_init, rcv_from, rcv_by, rcv_for } state;
   int status;
   char *s;
@@ -134,27 +134,26 @@ parse_received (mu_header_t hdr, char **sptr)
   status = mu_header_sget_value (hdr, MU_HEADER_RECEIVED, &recv);
   if (status)
     return status;
-  status = mu_argcv_get (recv, NULL, NULL, &wc, &ws);
-  if (status)
+  if (mu_wordsplit (recv, &ws, MU_WRDSF_DEFFLAGS))
     return status;
 
   state = rcv_init;
-  for (i = 0; i < wc && state != rcv_for; i++)
+  for (i = 0; i < ws.ws_wordc && state != rcv_for; i++)
     {
       switch (state)
        {
        case rcv_init:
-         if (strcmp (ws[i], "from") == 0)
+         if (strcmp (ws.ws_wordv[i], "from") == 0)
            state = rcv_from;
          break;
          
        case rcv_from:
-         if (strcmp (ws[i], "by") == 0)
+         if (strcmp (ws.ws_wordv[i], "by") == 0)
            state = rcv_by;
          break;
          
        case rcv_by:
-         if (strcmp (ws[i], "for") == 0)
+         if (strcmp (ws.ws_wordv[i], "for") == 0)
            state = rcv_for;
          break;
 
@@ -163,10 +162,10 @@ parse_received (mu_header_t hdr, char **sptr)
        }
     }
 
-  if (state != rcv_for || ws[i] == NULL)
+  if (state != rcv_for || ws.ws_wordv[i] == NULL)
     return MU_ERR_NOENT;
 
-  s = ws[i];
+  s = ws.ws_wordv[i];
   len = strlen (s);
   if (s[len - 1] == ';')
     len--;
@@ -183,7 +182,7 @@ parse_received (mu_header_t hdr, char **sptr)
       memcpy (*sptr, s, len);
       (*sptr)[len - 1] = 0;
     }
-  mu_argcv_free (wc, ws);
+  mu_wordsplit_free (&ws);
   return status;
 } 
   
@@ -262,6 +261,7 @@ remote_mbox_append_message (mu_mailbox_t mbox, mu_message_t 
msg)
          const char *hstr;
          int hc; 
          char **hv;
+         struct mu_wordsplit ws;
          
          if (mu_url_sget_param (mbox->url, "recipient-headers", &hstr) == 0)
            {
@@ -272,10 +272,12 @@ remote_mbox_append_message (mu_mailbox_t mbox, 
mu_message_t msg)
                }
              else
                {
-                 status = mu_argcv_get_np (hstr, strlen (hstr), ",", NULL, 0,
-                                           &hc, &hv, NULL);
-                 if (status)
-                   return status;
+                 ws.ws_delim = ",";
+                 if (mu_wordsplit (hstr, &ws,
+                                   
MU_WRDSF_DEFFLAGS|MU_WRDSF_DELIM|MU_WRDSF_WS))
+                   return errno;
+                 hc = 1;
+                 hv = ws.ws_wordv;
                }
            }
          else
@@ -286,7 +288,7 @@ remote_mbox_append_message (mu_mailbox_t mbox, mu_message_t 
msg)
          
          status = guess_message_recipient (msg, hv, &rcpt);
          if (hc)
-           mu_argcv_free (hc, hv);
+           mu_wordsplit_free (&ws);
        }
 
       if (status != MU_ERR_NOENT)
diff --git a/libproto/mailer/smtp.c b/libproto/mailer/smtp.c
index 7196288..d4c5879 100644
--- a/libproto/mailer/smtp.c
+++ b/libproto/mailer/smtp.c
@@ -31,8 +31,9 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <mailutils/nls.h>
 #include <mailutils/address.h>
-#include <mailutils/argcv.h>
+#include <mailutils/wordsplit.h>
 #include <mailutils/debug.h>
 #include <mailutils/errno.h>
 #include <mailutils/header.h>
@@ -95,18 +96,19 @@ struct _smtp_mailer
 static void
 smtp_mailer_add_auth_mech (struct _smtp_mailer *smtp_mailer, const char *str)
 {
-  int mc, i, rc;
-  char **mv;
-  
-  rc = mu_argcv_get_np (str, strlen (str),
-                       ",", NULL,
-                       0,
-                       &mc, &mv, NULL);
-  if (rc == 0)
-    for (i = 0; i < mc; i++)
-      mu_smtp_add_auth_mech (smtp_mailer->smtp, mv[i]);
-  
-  free (mv);
+  struct mu_wordsplit ws;
+
+  ws.ws_delim = ",";
+  if (mu_wordsplit (str, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_DELIM|MU_WRDSF_WS))
+    mu_error (_("cannot split line `%s': %s"), str,
+             mu_wordsplit_strerror (&ws));
+  else
+    {
+      size_t i;
+      for (i = 0; i < ws.ws_wordc; i++)
+       mu_smtp_add_auth_mech (smtp_mailer->smtp, ws.ws_wordv[i]);
+      mu_wordsplit_free (&ws);
+    }
 }
 
 static int
diff --git a/libproto/mailer/smtp_gsasl.c b/libproto/mailer/smtp_gsasl.c
index b8e2638..ccf1a7f 100644
--- a/libproto/mailer/smtp_gsasl.c
+++ b/libproto/mailer/smtp_gsasl.c
@@ -21,7 +21,8 @@
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
-#include <mailutils/argcv.h>
+#include <mailutils/nls.h>
+#include <mailutils/wordsplit.h>
 #include <mailutils/diag.h>
 #include <mailutils/errno.h>
 #include <mailutils/cctype.h>
@@ -39,9 +40,8 @@ get_implemented_mechs (Gsasl *ctx, mu_list_t *plist)
   char *listmech;
   mu_list_t supp = NULL;
   int rc;
-  int mechc;
-  char **mechv;
-    
+  struct mu_wordsplit ws;
+  
   rc =  gsasl_server_mechlist (ctx, &listmech);
   if (rc != GSASL_OK)
     {
@@ -51,19 +51,26 @@ get_implemented_mechs (Gsasl *ctx, mu_list_t *plist)
       return 1;
     }
 
-  rc = mu_argcv_get (listmech, "", NULL, &mechc, &mechv);
-  if (rc == 0)
+  if (mu_wordsplit (listmech, &ws, MU_WRDSF_DEFFLAGS))
     {
-      int i;
-
+      mu_error (_("cannot split line `%s': %s"), listmech,
+               mu_wordsplit_strerror (&ws));
+      rc = errno;
+    }
+  else
+    {
+      size_t i;
+      
       rc = mu_list_create (&supp);
       if (rc == 0)
        {
          mu_list_set_destroy_item (supp, mu_list_free_item);
-         for (i = 0; i < mechc; i++) 
-           mu_list_append (supp, mechv[i]);
+         for (i = 0; i < ws.ws_wordc; i++) 
+           mu_list_append (supp, ws.ws_wordv[i]);
        }
-      free (mechv);
+      ws.ws_wordc = 0;
+      ws.ws_wordv = NULL;
+      mu_wordsplit_free (&ws);
     }
   free (listmech);
   *plist = supp;
diff --git a/mail/mail.h b/mail/mail.h
index 3b33a52..0c344ea 100644
--- a/mail/mail.h
+++ b/mail/mail.h
@@ -77,6 +77,7 @@
 #include <mailutils/nls.h>
 #include <mailutils/tls.h>
 #include <mailutils/argcv.h>
+#include <mailutils/wordsplit.h>
 #include <mailutils/cctype.h>
 #include <mailutils/cstr.h>
 #include <mailutils/io.h>
diff --git a/mail/mailline.c b/mail/mailline.c
index 95e3243..c420757 100644
--- a/mail/mailline.c
+++ b/mail/mailline.c
@@ -230,32 +230,38 @@ ml_reread (const char *prompt, char **text)
 char **
 ml_command_completion (char *cmd, int start, int end)
 {
-  int argc;
-  char **argv;
   char **ret;
   char *p;
-
+  struct mu_wordsplit ws;
+  
   for (p = rl_line_buffer; p < rl_line_buffer + start && mu_isblank (*p); p++)
     ;
-  
-  if (mu_argcv_get_n (p, end, NULL, NULL, &argc, &argv))
-    return NULL;
+
+  if (mu_wordsplit_len (p, end, &ws, MU_WRDSF_DEFFLAGS))
+    {
+      mu_error (_("mu_wordsplit_len failed: %s"),
+               mu_wordsplit_strerror (&ws));
+      return NULL;
+    }
   rl_completion_append_character = ' ';
   
-  if (argc == 0 || (argc == 1 && strlen (argv[0]) <= end - start))
+  if (ws.ws_wordc == 0 ||
+      (ws.ws_wordc == 1 && strlen (ws.ws_wordv[0]) <= end - start))
     {
       ret = rl_completion_matches (cmd, ml_command_generator);
       rl_attempted_completion_over = 1;
     }
   else
     {
-      const struct mail_command_entry *entry = mail_find_command (argv[0]);
+      const struct mail_command_entry *entry =
+       mail_find_command (ws.ws_wordv[0]);
       if (entry && entry->command_completion)
-       ret = entry->command_completion (argc, argv, start == end);
+       ret = entry->command_completion (ws.ws_wordc, ws.ws_wordv,
+                                        start == end);
       else
        ret = NULL;
     }
-  mu_argcv_free (argc, argv);
+  mu_wordsplit_free (&ws);
   return ret;
 }
 
diff --git a/mail/mailvar.c b/mail/mailvar.c
index 7f163ae..4980d4c 100644
--- a/mail/mailvar.c
+++ b/mail/mailvar.c
@@ -579,9 +579,7 @@ set_mailbox_debug_level (mu_log_level_t level)
 static void
 set_debug (struct mailvar_variable *var)
 {
-  int rc;
-  int argc;
-  char **argv;
+  struct mu_wordsplit ws;
   int i;
   mu_debug_t dbg;
 
@@ -600,19 +598,25 @@ set_debug (struct mailvar_variable *var)
     }
   
   mu_diag_get_debug (&dbg);
-  
-  rc = mu_argcv_get (var->value.string, ";", NULL, &argc, &argv);
-  if (rc)
+
+  ws.ws_delim = ";";
+
+  if (mu_wordsplit (var->value.string, &ws,
+                   MU_WRDSF_NOCMD | MU_WRDSF_NOVAR |
+                   MU_WRDSF_SQUEEZE_DELIMS |
+                   MU_WRDSF_DELIM | MU_WRDSF_WS |
+                   MU_WRDSF_ERROR))
     {
-      mu_error (_("Cannot parse string: %s"), mu_strerror (rc));
+      mu_error (_("%s failed: %s"), "mu_wordsplit",
+               mu_wordsplit_strerror (&ws));
       return;
     }
 
-  for (i = 0; i < argc; i++)
+  for (i = 0; i < ws.ws_wordc; i++)
     {
       char *p;
       mu_log_level_t level = MU_DEBUG_INHERIT;
-      char *object_name = argv[i];
+      char *object_name = ws.ws_wordv[i];
       
       for (p = object_name; *p && *p != '='; p++)
        ;
@@ -629,6 +633,7 @@ set_debug (struct mailvar_variable *var)
        set_mailbox_debug_level (level);
       mu_global_debug_set_level (object_name, level);
     }
+  mu_wordsplit_free (&ws);
 }
 
 
diff --git a/mail/send.c b/mail/send.c
index 618f6a8..4594309 100644
--- a/mail/send.c
+++ b/mail/send.c
@@ -504,32 +504,33 @@ mail_send0 (compose_env_t * env, int save_to)
            }
          else
            {
-             int argc;
-             char **argv;
+             struct mu_wordsplit ws;
              int status;
 
              ofile = env->file;
 
-             if (mu_argcv_get (buf + 1, "", NULL, &argc, &argv) == 0)
+             if (mu_wordsplit (buf + 1, &ws, MU_WRDSF_DEFFLAGS) == 0)
                {
-                 if (argc > 0)
+                 if (ws.ws_wordc > 0)
                    {
                      const struct mail_escape_entry *entry = 
-                                   mail_find_escape (argv[0]);
+                       mail_find_escape (ws.ws_wordv[0]);
 
                      if (entry)
-                       status = (*entry->escfunc) (argc, argv, env);
+                       status = (*entry->escfunc) (ws.ws_wordc, ws.ws_wordv,
+                                                   env);
                      else
-                       util_error (_("Unknown escape %s"), argv[0]);
+                       util_error (_("Unknown escape %s"), ws.ws_wordv[0]);
                    }
                  else
                    util_error (_("Unfinished escape"));
+                 mu_wordsplit_free (&ws);
                }
              else
                {
-                 util_error (_("Cannot parse escape sequence"));
+                 util_error (_("Cannot parse escape sequence: %s"),
+                             mu_wordsplit_strerror (&ws));
                }
-             mu_argcv_free (argc, argv);
 
              ofile = env->ofile;
            }
diff --git a/mail/util.c b/mail/util.c
index f337fe7..393504a 100644
--- a/mail/util.c
+++ b/mail/util.c
@@ -40,8 +40,9 @@
 int
 util_do_command (const char *fmt, ...)
 {
-  int argc = 0;
-  char **argv = NULL;
+  struct mu_wordsplit ws;
+  int argc;
+  char **argv;
   int status = 0;
   const struct mail_command_entry *entry = NULL;
   char *cmd = NULL;
@@ -76,19 +77,29 @@ util_do_command (const char *fmt, ...)
            return 0;
        }
 
-      if (mu_argcv_get (cmd, NULL, NULL, &argc, &argv) == 0 && argc > 0)
+      ws.ws_offs = 1; /* Keep one extra slot in case we need it
+                        for expansion (see below) */
+      if (mu_wordsplit (cmd, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_DOOFFS))
+       {
+         mu_error (_("%s failed: %s"), "mu_wordsplit",
+                   mu_wordsplit_strerror (&ws));
+       }
+      else
        {
          char *p;
 
+         argc = ws.ws_wordc;
+         argv = ws.ws_wordv + 1;
+         
          /* Special case: a number alone implies "print" */
          if (argc == 1
              && ((strtoul (argv[0], &p, 10) > 0 && *p == 0)
                  || (argv[0][1] == 0 && strchr("^$", argv[0][0]))))
            {
-             mu_asprintf (&p, "print %s", argv[0]);
-             mu_argcv_free (argc, argv);
-             mu_argcv_get (p, NULL, NULL, &argc, &argv);
-             free (p);
+             /* Use the extra slot for "print" command */
+             argc++;
+             argv--;
+             argv[0] = "print";
            }
 
          entry = mail_find_command (argv[0]);
@@ -114,12 +125,16 @@ util_do_command (const char *fmt, ...)
       
       if (strlen (p))
        {
+         /* Expand contracted form.  That's what we have kept an extra
+            ws slot for. */
          argc++;
-         argv = xrealloc (argv, (argc + 1)*sizeof argv[0]);
-         if (argc > 2)
-           memmove (argv + 2, argv + 1, (argc - 2)*sizeof argv[0]);
+         argv--;
+         argv[0] = argv[1];
          argv[1] = xstrdup (p);
          *p = 0;
+         /* Register the new entry in WS */
+         ws.ws_wordc++;
+         ws.ws_offs = 0;
        }
       
       entry = mail_find_command (argv[0]);
@@ -140,7 +155,7 @@ util_do_command (const char *fmt, ...)
       status = 1;
     }
 
-  mu_argcv_free (argc, argv);
+  mu_wordsplit_free (&ws);
   return status;
 }
 
diff --git a/mh/folder.c b/mh/folder.c
index 6175594..5dba885 100644
--- a/mh/folder.c
+++ b/mh/folder.c
@@ -519,18 +519,27 @@ action_list ()
 static void
 get_stack (int *pc, char ***pv)
 {
-  int status;
+  struct mu_wordsplit ws;
   const char *stack = mh_global_context_get ("Folder-Stack", NULL);
   if (!stack)
     {
       *pc = 0;
       *pv = NULL;
     }
-  else if ((status = mu_argcv_get (stack, NULL, "#", pc, pv)) != 0)
+  else if (mu_wordsplit (stack, &ws, MU_WRDSF_DEFFLAGS))
     {
-      mu_diag_funcall (MU_DIAG_ERROR, "mu_argcv_get", stack, status);
+      mu_error (_("cannot split line `%s': %s"), stack,
+               mu_wordsplit_strerror (&ws));
       exit (1);
     }
+  else
+    {
+      *pc = ws.ws_wordc;
+      *pv = ws.ws_wordv;
+      ws.ws_wordc = 0;
+      ws.ws_wordv = NULL;
+      mu_wordsplit_free (&ws);
+    }
 }
 
 static void
@@ -762,26 +771,32 @@ pack_xlate (struct pack_tab *pack_tab, size_t count, 
size_t n)
 static int
 _fixup (const char *name, const char *value, struct fixup_data *fd, int flags)
 {
-  int i, j, argc;
-  char **argv;
+  size_t i, j;
+  struct mu_wordsplit ws;
   mh_msgset_t msgset;
 
   if (verbose)
     fprintf (stderr, "Sequence `%s'...\n", name);
-  
-  if (mu_argcv_get (value, "", NULL, &argc, &argv))
-    return 0;
 
-  msgset.list = xcalloc (argc, sizeof msgset.list[0]);
-  for (i = j = 0; i < argc; i++)
+  if (mu_wordsplit (value, &ws, MU_WRDSF_DEFFLAGS))
+    {
+      mu_error (_("cannot split line `%s': %s"), value,
+               mu_wordsplit_strerror (&ws));
+      return 0;
+    }
+
+  msgset.list = xcalloc (ws.ws_wordc, sizeof msgset.list[0]);
+  for (i = j = 0; i < ws.ws_wordc; i++)
     {
       size_t n = pack_xlate (fd->pack_tab, fd->count,
-                            strtoul (argv[i], NULL, 0));
+                            strtoul (ws.ws_wordv[i], NULL, 0));
       if (n)
        msgset.list[j++] = n;
     }
   msgset.count = j;
 
+  mu_wordsplit_free (&ws);
+  
   mh_seq_add (name, &msgset, flags | SEQ_ZERO);
   free (msgset.list);
 
diff --git a/mh/mh.h b/mh/mh.h
index 0ed009c..17289c0 100644
--- a/mh/mh.h
+++ b/mh/mh.h
@@ -49,6 +49,7 @@
 #include <mailutils/errno.h>
 #include <mailutils/nls.h>
 #include <mailutils/argcv.h>
+#include <mailutils/wordsplit.h>
 #include <mailutils/debug.h>
 #include <mailutils/mailer.h>
 #include <mailutils/envelope.h>
diff --git a/mh/mh_alias.y b/mh/mh_alias.y
index e34509c..d89f24a 100644
--- a/mh/mh_alias.y
+++ b/mh/mh_alias.y
@@ -496,16 +496,20 @@ mh_read_aliases ()
   p = mh_global_profile_get ("Aliasfile", NULL);
   if (p)
     {
-      int argc;
-      char **argv;
-      int rc = mu_argcv_get (p, NULL, NULL, &argc, &argv);
-      if (rc == 0)
+      struct mu_wordsplit ws;
+
+      if (mu_wordsplit (p, &ws, MU_WRDSF_DEFFLAGS))
+       {
+         mu_error (_("cannot split line `%s': %s"), p,
+                   mu_wordsplit_strerror (&ws));
+       }
+      else
        {
-         int i;
-         for (i = 0; i < argc; i++) 
-           mh_alias_read (argv[i], 1);
+         size_t i;
+         for (i = 0; i < ws.ws_wordc; i++) 
+           mh_alias_read (ws.ws_wordv[i], 1);
+         mu_wordsplit_free (&ws);
        }
-      mu_argcv_free (argc, argv);
     }
   mh_alias_read (DEFAULT_ALIAS_FILE, 0);
   return 0;
diff --git a/mh/mh_argp.c b/mh/mh_argp.c
index 8978d4c..dff019b 100644
--- a/mh/mh_argp.c
+++ b/mh/mh_argp.c
@@ -219,24 +219,31 @@ mh_argp_parse (int *pargc, char **pargv[],
     {
       int argc;
       char **argv;
-      int xargc;
-      char **xargv;
       int i, j;
+      struct mu_wordsplit ws;
       
-      mu_argcv_get (val, "", NULL, &xargc, &xargv);
+      if (mu_wordsplit (val, &ws, MU_WRDSF_DEFFLAGS))
+       {
+         mu_error (_("cannot split line `%s': %s"), val,
+                   mu_wordsplit_strerror (&ws));
+         exit (1);
+       }
 
-      argc = *pargc + xargc;
-      argv = calloc (argc+1, sizeof *argv);
+      argc = *pargc + ws.ws_wordc;
+      argv = calloc (argc + 1, sizeof *argv);
       if (!argv)
         mh_err_memory (1);
 
       i = 0;
       argv[i++] = (*pargv)[0];
-      for (j = 0; j < xargc; i++, j++)
-       argv[i] = xargv[j];
+      for (j = 0; j < ws.ws_wordc; i++, j++)
+       argv[i] = ws.ws_wordv[j];
       for (j = 1; i < argc; i++, j++)
        argv[i] = (*pargv)[j];
       argv[i] = NULL;
+
+      ws.ws_wordc = 0;
+      mu_wordsplit_free (&ws);
       
       mh_argv_preproc (argc, argv, &data);
 
@@ -246,7 +253,6 @@ mh_argp_parse (int *pargc, char **pargv[],
 
       *pargc = argc;
       *pargv = argv;
-      free (xargv);
     }
   else
     {
diff --git a/mh/mh_init.c b/mh/mh_init.c
index 1f64cc3..8fc0bda 100644
--- a/mh/mh_init.c
+++ b/mh/mh_init.c
@@ -539,33 +539,36 @@ mh_iterate (mu_mailbox_t mbox, mh_msgset_t *msgset,
 int
 mh_spawnp (const char *prog, const char *file)
 {
-  int argc, i, rc, status;
-  char **argv, **xargv;
+  struct mu_wordsplit ws;
+  size_t i;
+  int rc, status;
+  char **xargv;
 
-  if ((rc = mu_argcv_get (prog, "", "#", &argc, &argv)) != 0)
+  ws.ws_comment = "#";
+  if (mu_wordsplit (prog, &ws, MU_WRDSF_DEFFLAGS | MU_WRDSF_COMMENT))
     {
-      mu_diag_funcall (MU_DIAG_ERROR, "mu_argcv_get", prog, rc);
-      mu_argcv_free (argc, argv);
+      mu_error (_("cannot split line `%s': %s"), prog,
+               mu_wordsplit_strerror (&ws));
       return 1;
     }
 
-  xargv = calloc (argc + 2, sizeof (*xargv));
+  xargv = calloc (ws.ws_wordc + 2, sizeof (*xargv));
   if (!xargv)
     {
       mh_err_memory (0);
-      mu_argcv_free (argc, argv);
+      mu_wordsplit_free (&ws);
       return 1;
     }
 
-  for (i = 0; i < argc; i++)
-    xargv[i] = argv[i];
+  for (i = 0; i < ws.ws_wordc; i++)
+    xargv[i] = ws.ws_wordv[i];
   xargv[i++] = (char*) file;
   xargv[i++] = NULL;
-
+  
   rc = mu_spawnvp (xargv[0], xargv, &status);
 
   free (xargv);
-  mu_argcv_free (argc, argv);
+  mu_wordsplit_free (&ws);
 
   return rc;
 }
diff --git a/mh/mh_list.c b/mh/mh_list.c
index a414574..86b3c96 100644
--- a/mh/mh_list.c
+++ b/mh/mh_list.c
@@ -149,42 +149,45 @@ parse_component (locus_t *loc, mu_list_t formlist, char 
*compname, char *str)
 static void
 parse_variable (locus_t *loc, mu_list_t formlist, char *str)
 {
-  int i, rc;
-  int argc;
-  char **argv;
+  size_t i;
+  struct mu_wordsplit ws;
   mh_format_t fmt;
-  
-  if ((rc = mu_argcv_get (str, ",=", NULL, &argc, &argv)) != 0)
+
+  ws.ws_delim = ",=";
+  if (mu_wordsplit (str, &ws,
+                   MU_WRDSF_DEFFLAGS|MU_WRDSF_DELIM|
+                   MU_WRDSF_WS|MU_WRDSF_RETURN_DELIMS))
     {
-      mu_error ("%s:%d: mu_argcv_get(%s): %s",
+      mu_error ("%s:%d: mu_wordsplit(%s): %s",
                loc->filename,
                loc->line,
                str,
-               mu_strerror (rc));
+               mu_wordsplit_strerror (&ws));
       exit (1);
     }
 
-  for (i = 0; i < argc; i++)
+  for (i = 0; i < ws.ws_wordc; i++)
     {
-      mhl_stmt_t *stmt = stmt_alloc (stmt_variable);
-      char *name = argv[i];
+      mhl_stmt_t *stmt;
+      char *name = ws.ws_wordv[i];
       char *value = NULL;
       mhl_variable_t *var;
-      
+
+      stmt = stmt_alloc (stmt_variable);
       var = variable_lookup (name);
       if (!var)
        {
          mu_error (_("%s:%d: unknown variable: %s"),
                    loc->filename,
                    loc->line,
-                   argv[i]);
+                   name);
          exit (1);
        }
 
-      if (i + 1 < argc && argv[i+1][0] == '=')
+      if (i + 1 < ws.ws_wordc && ws.ws_wordv[i+1][0] == '=')
        {
          i++;
-         value = argv[++i];
+         value = ws.ws_wordv[++i];
        }
 
       if ((var->type == dt_flag && value)
@@ -227,14 +230,13 @@ parse_variable (locus_t *loc, mu_list_t formlist, char 
*str)
       mu_list_append (formlist, stmt);
 
       i++;
-      if (i < argc && argv[i][0] != ',')
+      if (i < ws.ws_wordc && ws.ws_wordv[i][0] != ',')
        {
          mu_error (_("%s:%d: syntax error"), loc->filename, loc->line);
          exit (1);
        }
     }
-
-  mu_argcv_free (argc, argv);
+  mu_wordsplit_free (&ws);
 }
 
 static int
diff --git a/mh/mh_msgset.c b/mh/mh_msgset.c
index fdf5df2..41eb036 100644
--- a/mh/mh_msgset.c
+++ b/mh/mh_msgset.c
@@ -235,8 +235,7 @@ static int _mh_msgset_parse (mu_mailbox_t mbox, mh_msgset_t 
*msgset,
 int
 expand_user_seq (mu_mailbox_t mbox, mh_msgset_t *msgset, char *arg)
 {
-  int argc;
-  char **argv;
+  struct mu_wordsplit ws;
   char *p;
   const char *listp;
   int rc = 1;
@@ -260,10 +259,18 @@ expand_user_seq (mu_mailbox_t mbox, mh_msgset_t *msgset, 
char *arg)
       if (!listp)
        return 1;
     }
+
+  if (mu_wordsplit (listp, &ws, MU_WRDSF_DEFFLAGS))
+    {
+      mu_error (_("cannot split line `%s': %s"), listp,
+               mu_wordsplit_strerror (&ws));
+    }
+  else
+    {
+      rc = _mh_msgset_parse (mbox, msgset, ws.ws_wordc, ws.ws_wordv);
+      mu_wordsplit_free (&ws);
+    }
   
-  if (mu_argcv_get (listp, "", NULL, &argc, &argv) == 0)
-    rc = _mh_msgset_parse (mbox, msgset, argc, argv);
-  mu_argcv_free (argc, argv);
   if (rc)
     return rc;
 
diff --git a/mh/mh_sequence.c b/mh/mh_sequence.c
index 737461a..f80b855 100644
--- a/mh/mh_sequence.c
+++ b/mh/mh_sequence.c
@@ -124,19 +124,23 @@ mh_seq_delete (const char *name, mh_msgset_t *mset, int 
flags)
   const char *value = mh_seq_read (name, flags);
   char *new_val;
   char *p;
-  int argc, i, count;
-  char **argv;
+  size_t i, count;
+  struct mu_wordsplit ws;
   
   if (!value)
     return 0;
 
-  if (mu_argcv_get (value, "", NULL, &argc, &argv))
-    return 0;
+  if (mu_wordsplit (value, &ws, MU_WRDSF_DEFFLAGS))
+    {
+      mu_error (_("cannot split line `%s': %s"), value,
+               mu_wordsplit_strerror (&ws));
+      return 0;
+    }
 
-  for (i = 0; i < argc; i++)
+  for (i = 0; i < ws.ws_wordc; i++)
     {
       char *p;
-      size_t num = strtoul (argv[i], &p, 10);
+      size_t num = strtoul (ws.ws_wordv[i], &p, 10);
 
       if (*p)
        continue;
@@ -144,19 +148,19 @@ mh_seq_delete (const char *name, mh_msgset_t *mset, int 
flags)
       if (bsearch (&num, mset->list, mset->count, sizeof (mset->list[0]),
                   cmp_msgnum))
        {
-         free (argv[i]);
-         argv[i] = NULL;
+         free (ws.ws_wordv[i]);
+         ws.ws_wordv[i] = NULL;
        }
     }
 
   new_val = xstrdup (value);
   p = new_val;
   count = 0;
-  for (i = 0; i < argc; i++)
+  for (i = 0; i < ws.ws_wordc; i++)
     {
-      if (argv[i])
+      if (ws.ws_wordv[i])
        {
-         strcpy (p, argv[i]);
+         strcpy (p, ws.ws_wordv[i]);
          p += strlen (p);
          *p++ = ' ';
          count++;
@@ -164,7 +168,7 @@ mh_seq_delete (const char *name, mh_msgset_t *mset, int 
flags)
     }
   *p = 0;
   write_sequence (name, count > 0 ? new_val : NULL, flags & SEQ_PRIVATE);
-  mu_argcv_free (argc, argv);
+  mu_wordsplit_free (&ws);
   free (new_val);
   
   return 0;
diff --git a/mh/mh_whatnow.c b/mh/mh_whatnow.c
index 6724810..6582fd8 100644
--- a/mh/mh_whatnow.c
+++ b/mh/mh_whatnow.c
@@ -328,28 +328,30 @@ _whatnow (struct mh_whatnow_env *wh, struct action_tab 
*tab)
     {
       char *line = NULL;
       size_t size = 0;
-      int argc;
-      char **argv;
+      struct mu_wordsplit ws;
       handler_fp fun;
       
       printf ("%s ", wh->prompt);
       getline (&line, &size, stdin);
       if (!line)
        continue;
-      rc = mu_argcv_get (line, "", "#", &argc, &argv);
+
+      ws.ws_comment = "#";
+      rc = mu_wordsplit (line, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT);
       free (line);
       if (rc)
        {
-         mu_argcv_free (argc, argv);
+         mu_error (_("cannot split line `%s': %s"), line,
+                   mu_wordsplit_strerror (&ws));
          break;
        }
 
-      fun = func (tab, argv[0]);
+      fun = func (tab, ws.ws_wordv[0]);
       if (fun)
-       rc = fun (wh, argc, argv, &status);
+       rc = fun (wh, ws.ws_wordc, ws.ws_wordv, &status);
       else
        rc = 0;
-      mu_argcv_free (argc, argv);
+      mu_wordsplit_free (&ws);
     }
   while (rc == 0);
   return status;
diff --git a/mh/mhn.c b/mh/mhn.c
index 5aef07f..bc240cb 100644
--- a/mh/mhn.c
+++ b/mh/mhn.c
@@ -881,26 +881,24 @@ mhn_store_command (mu_message_t msg, msg_part_t part, 
char *name)
 static void
 split_args (char *argstr, int *pargc, char ***pargv)
 {
-  int argc;
-  char **argv;
+  struct mu_wordsplit ws;
 
-  if (mu_argcv_get (argstr, ";", NULL, &argc, &argv) == 0)
+  ws.ws_delim = ";";
+  if (mu_wordsplit (argstr, &ws,
+                   MU_WRDSF_DEFFLAGS | MU_WRDSF_DELIM | MU_WRDSF_WS))
     {
-      int i, j;
-      
-      for (i = j = 0; i < argc; i++)
-       {
-         if (argv[i][0] != ';')
-           argv[j++] = argv[i];
-       }
-      argv[j] = NULL;
-      *pargc = j;
-      *pargv = argv;
+      mu_error (_("cannot split line `%s': %s"), argstr,
+               mu_wordsplit_strerror (&ws));
+      *pargc = 0;
+      *pargv = NULL;
     }
   else
     {
-      *pargc = 0;
-      *pargv = NULL;
+      *pargc = ws.ws_wordc;
+      *pargv = ws.ws_wordv;
+      ws.ws_wordc = 0;
+      ws.ws_wordv = NULL;
+      mu_wordsplit_free (&ws);
     }
 }
 
@@ -1303,13 +1301,14 @@ mhn_run_command (mu_message_t msg, msg_part_t part,
     {
       /* pass content via a tempfile */
       int status;
-      int argc;
-      char **argv;
       mu_stream_t tmp;
-      
-      if (mu_argcv_get (cmd, "", "#", &argc, &argv))
+      struct mu_wordsplit ws;
+
+      ws.ws_comment = "#";
+      if (mu_wordsplit (cmd, &ws, MU_WRDSF_DEFFLAGS | MU_WRDSF_COMMENT))
        {
-         mu_error (_("cannot parse command line `%s'"), cmd);
+         mu_error (_("cannot parse command line `%s': %s"), cmd,
+                   mu_wordsplit_strerror (&ws));
          return ENOSYS;
        }
 
@@ -1318,15 +1317,15 @@ mhn_run_command (mu_message_t msg, msg_part_t part,
        {
          mu_error (_("cannot create temporary stream (file %s): %s"),
                    tempfile, mu_strerror (rc));
-         mu_argcv_free (argc, argv);
+         mu_wordsplit_free (&ws);
          return rc;
        }
       show_internal (msg, part, encoding, tmp);      
       mu_stream_destroy (&tmp);
-      rc = mu_spawnvp (argv[0], argv, &status);
+      rc = mu_spawnvp (ws.ws_wordv[0], ws.ws_wordv, &status);
       if (status)
        rc = status;
-      mu_argcv_free (argc, argv);
+      mu_wordsplit_free (&ws);
     }
   else
     rc = exec_internal (msg, part, encoding, cmd, flags);
@@ -2044,8 +2043,8 @@ edit_forw (char *cmd, struct compose_env *env, 
mu_message_t *pmsg, int level)
 {
   char *sp, *id = NULL, *descr = NULL;
   int stop = 0, status = 0;
-  int i, argc;
-  char **argv;
+  size_t i;
+  struct mu_wordsplit ws;
   mu_header_t hdr;
   mu_mime_t mime;
   mu_message_t msg;
@@ -2090,18 +2089,21 @@ edit_forw (char *cmd, struct compose_env *env, 
mu_message_t *pmsg, int level)
   if (status)
     return status;
 
-  if (mu_argcv_get (cmd, "\n", NULL, &argc, &argv))
+  ws.ws_delim = "\n";
+  if (mu_wordsplit (cmd, &ws,
+                   MU_WRDSF_DEFFLAGS | MU_WRDSF_DELIM | MU_WRDSF_WS))
     {
-      mu_error (_("%s:%lu: syntax error"),
+      mu_error (_("%s:%lu: cannot split line: %s"),
                input_file,
-               (unsigned long) mhn_error_loc (env));
+               (unsigned long) mhn_error_loc (env),
+               mu_wordsplit_strerror (&ws));
       return 1;
     }
 
   mu_mime_create (&mime, NULL, 0);
   
-  mbox = mh_open_folder (argv[0], 0);
-  for (i = 1; i < argc; i++)
+  mbox = mh_open_folder (ws.ws_wordv[0], 0);
+  for (i = 1; i < ws.ws_wordc; i++)
     {
       mu_message_t input_msg;
       if (mh_get_message (mbox, i, &input_msg) == 0)
@@ -2117,7 +2119,7 @@ edit_forw (char *cmd, struct compose_env *env, 
mu_message_t *pmsg, int level)
        break;
       mu_mime_add_part (mime, msg);
     }
-  mu_argcv_free (argc, argv);
+  mu_wordsplit_free (&ws);
 
   if (*pmsg)
     {
diff --git a/mh/send.c b/mh/send.c
index ef39d84..faece4e 100644
--- a/mh/send.c
+++ b/mh/send.c
@@ -497,49 +497,53 @@ fix_fcc (mu_message_t msg)
   mu_message_get_header (msg, &hdr);
   if (mu_header_aget_value (hdr, MU_HEADER_FCC, &fcc) == 0)
     {
-      int i, argc;
-      char **argv;
+      struct mu_wordsplit ws;
       int need_fixup = 0;
       size_t fixup_len = 0;
-      
-      mu_argcv_get (fcc, ",", NULL, &argc, &argv);
-      for (i = 0; i < argc; i += 2)
+
+      ws.ws_delim = ",";
+      if (mu_wordsplit (fcc, &ws,
+                       MU_WRDSF_DEFFLAGS | MU_WRDSF_DELIM | MU_WRDSF_WS))
        {
-         if (strchr ("+%~/=", argv[i][0]) == NULL)
-           {
-             need_fixup++;
-             fixup_len ++;
-           }
-         fixup_len += strlen (argv[i]);
+         mu_error (_("cannot split line `%s': %s"), fcc,
+                   mu_wordsplit_strerror (&ws));
        }
-
-      if (need_fixup)
+      else
        {
-         char *p;
-
-         /* the new fcc string contains: folder names - fixup_len characters
-            long, (argc - 2)/2 comma-space pairs and a terminating
-            nul */
-         fcc = realloc (fcc, fixup_len + argc - 2 + 1);
-         for (i = 0, p = fcc; i < argc; i++)
+         size_t i;
+         
+         for (i = 0; i < ws.ws_wordc; i += 2)
            {
-             if (i % 2 == 0)
+             if (strchr ("+%~/=", ws.ws_wordv[i][0]) == NULL)
                {
-                 if (strchr ("+%~/=", argv[i][0]) == NULL)
-                   *p++ = '+';
-                 strcpy (p, argv[i]);
-                 p += strlen (argv[i]);
+                 need_fixup++;
+                 fixup_len ++;
                }
-             else
+             fixup_len += strlen (ws.ws_wordv[i]);
+           }
+
+         if (need_fixup)
+           {
+             char *p;
+             
+             /* the new fcc string contains: folder names - fixup_len
+                characters, ws.ws_wordc - 1 comma-space pairs and a
+                terminating nul */
+             fcc = realloc (fcc, fixup_len + ws.ws_wordc - 1 + 1);
+             for (i = 0, p = fcc; i < ws.ws_wordc; i++)
                {
+                 if (strchr ("+%~/=", ws.ws_wordv[i][0]) == NULL)
+                   *p++ = '+';
+                 strcpy (p, ws.ws_wordv[i]);
+                 p += strlen (p);
                  *p++ = ',';
                  *p++ = ' ';
                }
+             *p = 0;
            }
-         *p = 0;
        }
 
-      mu_argcv_free (argc, argv);
+      mu_wordsplit_free (&ws);
 
       if (need_fixup)
        {
diff --git a/movemail/movemail.c b/movemail/movemail.c
index d030828..61daad6 100644
--- a/movemail/movemail.c
+++ b/movemail/movemail.c
@@ -267,23 +267,21 @@ cb_mailbox_ownership (mu_debug_t debug, void *data, 
mu_config_value_t *val)
        return _cb_mailbox_ownership (debug, str);
       else
        {
-         int argc;
-         char **argv;
+         struct mu_wordsplit ws;
 
-         if (mu_argcv_get_np (str, strlen (str), ",", NULL, 0,
-                              &argc, &argv, NULL))
+         ws.ws_delim = ",";
+         if (mu_wordsplit (str, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_DELIM))
            {
              mu_cfg_format_error (debug, MU_DEBUG_ERROR, 
-                                  _("cannot parse %s"),
-                                  str);
+                                  _("cannot parse %s: %s"),
+                                  str, mu_wordsplit_strerror (&ws));
              return 1;
            }
 
-         for (i = 0; i < argc; i++)
-           if (_cb_mailbox_ownership (debug, argv[i]))
+         for (i = 0; i < ws.ws_wordc; i++)
+           if (_cb_mailbox_ownership (debug, ws.ws_wordv[i]))
              return 1;
-
-         mu_argcv_free (argc, argv);
+         mu_wordsplit_free (&ws);
          return 0;
        }
     }
diff --git a/mu/shell.c b/mu/shell.c
index 9497b3d..910a13a 100644
--- a/mu/shell.c
+++ b/mu/shell.c
@@ -238,13 +238,12 @@ shell_help (int argc, char **argv)
 static int
 shell_prompt (int argc, char **argv)
 {
-  int quote;
   size_t size;
   
   free (mutool_shell_prompt);
-  size = mu_argcv_quoted_length (argv[1], &quote);
+  size = strlen (argv[1]);
   mutool_shell_prompt = xmalloc (size + 1);
-  mu_argcv_unquote_copy (mutool_shell_prompt, argv[1], size);
+  mu_wordsplit_c_unquote_copy (mutool_shell_prompt, argv[1], size);
   return 0;
 }
 
@@ -460,16 +459,22 @@ add_history (const char *s MU_ARG_UNUSED)
 int
 execute_line (char *line)
 {
+  struct mu_wordsplit ws;
   int argc;
   char **argv;
   int status = 0;
 
-  if (mu_argcv_get (line, NULL, "#", &argc, &argv))
+  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))
     {
-      mu_error("cannot parse input line");
+      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)
     {
       struct mutool_command *cmd = find_command (argv[0]);
@@ -484,6 +489,7 @@ execute_line (char *line)
        {
          if (cmd->argmin <= 0 && argc != 2)
            {
+             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)
@@ -491,18 +497,19 @@ execute_line (char *line)
                  *arg++ = 0;
                  arg = mu_str_skip_class (arg, MU_CTYPE_SPACE);
                }
-             
-             mu_argcv_free (argc, argv);
-             argc = 2;
-             argv = xcalloc (argc + 1, sizeof (argv[0]));
-             argv[0] = xstrdup (word);
-             argv[1] = xstrdup (arg);
-             argv[2] = NULL;
+             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);
        }
     }
-  mu_argcv_free (argc, argv);
+  mu_wordsplit_free (&ws);
   return status;
 }
 
diff --git a/po/POTFILES.in b/po/POTFILES.in
index fc1e8b1..2201ad1 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -129,7 +129,10 @@ libmailutils/diag/diag.c
 libmailutils/diag/errors
 libmailutils/diag/gdebug.c
 
+libmailutils/server/acl.c
+
 libmailutils/stream/file_stream.c
+libmailutils/stream/prog_stream.c
 
 libmailutils/filter/filter_iconv.c
 
@@ -137,6 +140,9 @@ libmailutils/server/ipsrv.c
 libmailutils/server/msrv.c
 
 libmailutils/mailbox/message.c
+libmailutils/mailer/mailer.c
+libmailutils/mailer/smtp.c
+libmailutils/mailer/smtp_gsasl.c
 
 libmailutils/auth/mu_auth.c
 
diff --git a/pop3d/popauth.c b/pop3d/popauth.c
index b22cb66..2c9d75e 100644
--- a/pop3d/popauth.c
+++ b/pop3d/popauth.c
@@ -16,7 +16,6 @@
    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include "pop3d.h"
-#include <mailutils/argcv.h>
 #include <xalloc.h>
 #include "mailutils/libargp.h"
 
@@ -30,6 +29,7 @@ int db_make (char *input_name, char *output_name);
 #define ACT_CHPASS  4
 
 static int permissions = 0600;
+static int compatibility_option = 0;
 
 struct action_data {
   int action;
@@ -60,6 +60,8 @@ static error_t popauth_parse_opt  (int key, char *arg,
 
 void popauth_version (FILE *stream, struct argp_state *state);
 
+#define COMPATIBILITY_OPTION 256
+
 static struct argp_option options[] = 
 {
   { NULL, 0, NULL, 0, N_("Actions are:"), 1 },
@@ -80,6 +82,8 @@ static struct argp_option options[] =
   { "password", 'p', N_("STRING"), 0, N_("specify user's password"), 3 },
   { "user", 'u', N_("USERNAME"), 0, N_("specify user name"), 3 },
   { "permissions", 'P', N_("PERM"), 0, N_("force given permissions on the 
database"), 3 },
+  { "compatibility", COMPATIBILITY_OPTION, NULL, 0,
+    N_("backward compatibility mode") },
   { NULL, }
 };
 
@@ -171,7 +175,11 @@ popauth_parse_opt (int key, char *arg, struct argp_state 
*astate)
     case 'P':
       set_db_perms (astate, arg, &permissions);
       break;
-      
+
+    case COMPATIBILITY_OPTION:
+      compatibility_option = 1;
+      break;
+       
     case ARGP_KEY_FINI:
       if (ap->action == -1)
        {
@@ -269,6 +277,23 @@ check_user_perm (int action, struct action_data *ap)
   return 1;
 }
 
+static void
+print_entry (FILE *fp, DBM_DATUM key, DBM_DATUM contents)
+{
+  if (compatibility_option)
+    fprintf (fp, "%.*s: %.*s\n",
+            (int) MU_DATUM_SIZE (key),
+            (char*) MU_DATUM_PTR (key),
+            (int) MU_DATUM_SIZE (contents),
+            (char*) MU_DATUM_PTR (contents));
+  else
+    fprintf (fp, "%.*s %.*s\n",
+            (int) MU_DATUM_SIZE (key),
+            (char*) MU_DATUM_PTR (key),
+            (int) MU_DATUM_SIZE (contents),
+            (char*) MU_DATUM_PTR (contents));
+}
+
 int
 action_list (struct action_data *ap)
 {
@@ -309,11 +334,7 @@ action_list (struct action_data *ap)
        }
       else
        {
-         fprintf (fp, "%.*s: %.*s\n",
-                  (int) MU_DATUM_SIZE (key),
-                  (char*) MU_DATUM_PTR (key),
-                  (int) MU_DATUM_SIZE (contents),
-                  (char*) MU_DATUM_PTR (contents));
+         print_entry (fp, key, contents);
          mu_dbm_datum_free (&contents);
        }
     }
@@ -324,11 +345,7 @@ action_list (struct action_data *ap)
        {
          memset (&contents, 0, sizeof contents);
          mu_dbm_fetch (db, key, &contents);
-         fprintf (fp, "%.*s: %.*s\n",
-                  (int) MU_DATUM_SIZE (key),
-                  (char*) MU_DATUM_PTR (key),
-                  (int) MU_DATUM_SIZE (contents),
-                  (char*) MU_DATUM_PTR (contents));
+         print_entry (fp, key, contents);
          mu_dbm_datum_free (&contents);
        }
     }
@@ -341,85 +358,89 @@ action_list (struct action_data *ap)
 int
 action_create (struct action_data *ap)
 {
-  FILE *fp;
+  int rc;
+  mu_stream_t in;
   DBM_FILE db;
   DBM_DATUM key;
   DBM_DATUM contents;
-  char buf[256];
+  char *buf = NULL;
+  size_t size = 0, len;
   int line = 0;
-
+  
   /* Make sure we have proper privileges if popauth is setuid */
   setuid (getuid ());
   
   if (ap->input_name)
     {
-      fp = fopen (ap->input_name, "r");
-      if (!fp)
+      rc = mu_file_stream_create (&in, ap->input_name, MU_STREAM_READ);
+      if (rc)
        {
          mu_error (_("cannot open file %s: %s"),
-                   ap->input_name, mu_strerror (errno));
+                   ap->input_name, mu_strerror (rc));
          return 1;
        }
     }
   else
     {
       ap->input_name = "";
-      fp = stdin;
+      rc = mu_stdio_stream_create (&in, MU_STDIN_FD, MU_STREAM_READ);
+      if (rc)
+       {
+         mu_error (_("cannot open standard input: %s"),
+                   mu_strerror (rc));
+         return 1;
+       }
     }
   
   if (!ap->output_name)
     ap->output_name = APOP_PASSFILE;
   if (mu_dbm_open (ap->output_name, &db, MU_STREAM_CREAT, permissions))
     {
-      mu_error (_("cannot create database %s: %s"), ap->output_name, 
mu_strerror (errno));
+      mu_error (_("cannot create database %s: %s"),
+               ap->output_name, mu_strerror (errno));
       return 1;
     }
 
   line = 0;
-  while (fgets (buf, sizeof buf - 1, fp))
+  while ((rc = mu_stream_getline (in, &buf, &size, &len)) == 0
+        && len > 0)
     {
-      int len;
-      int argc;
-      char **argv;
+      char *str, *pass;
 
-      len = strlen (buf);
-      if (buf[len-1] == '\n')
-       buf[--len] = 0;
-      
       line++;
-      if (mu_argcv_get (buf, ":", NULL, &argc, &argv))
-       {
-         mu_argcv_free (argc, argv);
-         continue;
-       }
-
-      if (argc == 0 || argv[0][0] == '#')
+      str = mu_str_stripws (buf);
+      if (*str == 0 || *str == '#')
+       continue;
+      pass = mu_str_skip_class_comp (str, MU_CTYPE_SPACE);
+      if (*pass == 0)
        {
-         mu_argcv_free (argc, argv);
+         mu_error (_("%s:%d: malformed line"), ap->input_name, line);
          continue;
        }
-      
-      if (argc != 3 || argv[1][0] != ':' || argv[1][1] != 0)
+      /* Strip trailing semicolon, when in compatibility mode. */
+      if (compatibility_option && pass > str && pass[-1] == ':')
+       pass[-1] = 0;
+      *pass++ = 0;
+      pass = mu_str_skip_class (pass, MU_CTYPE_SPACE);
+      if (*pass == 0)
        {
          mu_error (_("%s:%d: malformed line"), ap->input_name, line);
-         mu_argcv_free (argc, argv);
          continue;
        }
-
+      
       memset (&key, 0, sizeof key);
       memset (&contents, 0, sizeof contents);
-      MU_DATUM_PTR (key) = argv[0];
-      MU_DATUM_SIZE (key) = strlen (argv[0]);
-      MU_DATUM_PTR (contents) = argv[2];
-      MU_DATUM_SIZE (contents) = strlen (argv[2]);
+      MU_DATUM_PTR (key) = str;
+      MU_DATUM_SIZE (key) = strlen (str);
+      MU_DATUM_PTR (contents) = pass;
+      MU_DATUM_SIZE (contents) = strlen (pass);
 
       if (mu_dbm_insert (db, key, contents, 1))
        mu_error (_("%s:%d: cannot store datum"), ap->input_name, line);
-
-      mu_argcv_free (argc, argv);
     }
+  free (buf);
   mu_dbm_close (db);
-  fclose (fp);
+  mu_stream_destroy (&in);
   return 0;
 }
 
diff --git a/readmsg/readmsg.c b/readmsg/readmsg.c
index db60329..a354070 100644
--- a/readmsg/readmsg.c
+++ b/readmsg/readmsg.c
@@ -297,6 +297,7 @@ main (int argc, char **argv)
   int i;
   int index;
   mu_mailbox_t mbox = NULL;
+  struct mu_wordsplit ws;
   char **weedv;
   int weedc;
   int unix_header = 0;
@@ -351,27 +352,32 @@ main (int argc, char **argv)
   if (weedlist == NULL)
     weedlist = "Date To Cc Subject From Apparently-";
 
-  status = mu_argcv_get (weedlist, WEEDLIST_SEPARATOR, NULL,
-                        &weedc, &weedv);
+  ws.ws_delim = WEEDLIST_SEPARATOR;
+  status = mu_wordsplit (weedlist, &ws, MU_WRDSF_DEFFLAGS | MU_WRDSF_DELIM);
   if (status)
     {
-      mu_error (_("cannot parse weedlist: %s"), mu_strerror (status));
+      mu_error (_("cannot parse weedlist: %s"), mu_wordsplit_strerror (&ws));
       exit (2);
     }
 
-  for (i = 0; i < weedc; i++)
+  if (ws.ws_wordc)
     {
-      if (mu_c_strcasecmp (weedv[i], "From_") == 0)
+      for (i = 0; i < ws.ws_wordc; i++)
        {
-         int j;
-         unix_header = 1;
-         free (weedv[i]);
-         for (j = i; j < weedc; j++)
-           weedv[j] = weedv[j+1];
-         weedc--;
-         if (weedc == 0 && !all_header)
-           no_header = 1;
+         if (mu_c_strcasecmp (ws.ws_wordv[i], "From_") == 0)
+           {
+             int j;
+             unix_header = 1;
+             free (ws.ws_wordv[i]);
+             for (j = i; j < ws.ws_wordc; j++)
+               ws.ws_wordv[j] = ws.ws_wordv[j+1];
+             ws.ws_wordc--;
+             if (ws.ws_wordc == 0 && !all_header)
+               no_header = 1;
+           }
        }
+      weedc = ws.ws_wordc;
+      weedv = ws.ws_wordv;
     }
   
   if (all_header)
diff --git a/readmsg/readmsg.h b/readmsg/readmsg.h
index e0fde21..1fc97da 100644
--- a/readmsg/readmsg.h
+++ b/readmsg/readmsg.h
@@ -48,7 +48,7 @@
 #include <mailutils/tls.h>
 #include <mailutils/error.h>
 #include <mailutils/envelope.h>
-#include <mailutils/argcv.h>
+#include <mailutils/wordsplit.h>
 #include <mailutils/util.h>
 
 int msglist (mu_mailbox_t mbox, int show_all, int argc, char **argv, int 
**set, int *n);
diff --git a/testsuite/smtpsend.c b/testsuite/smtpsend.c
index a716c3d..c77e703 100644
--- a/testsuite/smtpsend.c
+++ b/testsuite/smtpsend.c
@@ -51,8 +51,8 @@ send_rcpt_command (void *item, void *data)
 static void
 update_list (mu_list_t *plist, const char *arg)
 {
-  int mc, j;
-  char **mv;
+  size_t j;
+  struct mu_wordsplit ws;
   mu_list_t list = *plist;
   
   if (!list)
@@ -61,13 +61,16 @@ update_list (mu_list_t *plist, const char *arg)
       *plist = list;
     }
 
-  MU_ASSERT (mu_argcv_get_np (arg, strlen (arg),
-                             ",", NULL,
-                             0,
-                             &mc, &mv, NULL));
-  for (j = 0; j < mc; j++)
-    MU_ASSERT (mu_list_append (list, mv[j]));
-  free (mv);
+  ws.ws_delim = ",";
+  if (mu_wordsplit (arg, &ws, MU_WRDSF_DEFFLAGS | MU_WRDSF_DELIM))
+    {
+      mu_error ("mu_wordsplit: %s", mu_wordsplit_strerror (&ws));
+      exit (1);
+    }
+  for (j = 0; j < ws.ws_wordc; j++)
+    MU_ASSERT (mu_list_append (list, ws.ws_wordv[j]));
+  ws.ws_wordc = 0;
+  mu_wordsplit_free (&ws);
 }
 
 static int


hooks/post-receive
-- 
GNU Mailutils



reply via email to

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