summaryrefslogtreecommitdiff
path: root/libmailutils
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2010-10-28 23:20:22 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2010-10-28 23:20:22 +0300
commit4a49f5341ae9005c9ffbd7620efaecffcdfda79f (patch)
tree53ba2d6a638bd9b83747ebd6899931ab89d13ca0 /libmailutils
parent697384c64dadb0a4db97136a6cbcf2166c48897b (diff)
downloadmailutils-4a49f5341ae9005c9ffbd7620efaecffcdfda79f.tar.gz
mailutils-4a49f5341ae9005c9ffbd7620efaecffcdfda79f.tar.bz2
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.
Diffstat (limited to 'libmailutils')
-rw-r--r--libmailutils/auth/mu_auth.c26
-rw-r--r--libmailutils/base/Makefile.am3
-rw-r--r--libmailutils/base/argcv.c112
-rw-r--r--libmailutils/base/argcvfree.c51
-rw-r--r--libmailutils/base/argcvjoin.c116
-rw-r--r--libmailutils/base/argcvrem.c55
-rw-r--r--libmailutils/base/mutil.c19
-rw-r--r--libmailutils/cfg/format.c7
-rw-r--r--libmailutils/cfg/lexer.l4
-rw-r--r--libmailutils/cfg/parser.y77
-rw-r--r--libmailutils/diag/gdebug.c29
-rw-r--r--libmailutils/mailer/mailer.c30
-rw-r--r--libmailutils/server/acl.c18
-rw-r--r--libmailutils/stream/prog_stream.c13
-rw-r--r--libmailutils/string/Makefile.am1
-rw-r--r--libmailutils/string/wordsplit.c1469
-rw-r--r--libmailutils/tests/.gitignore1
-rw-r--r--libmailutils/tests/Makefile.am6
-rw-r--r--libmailutils/tests/argcv.c1
-rw-r--r--libmailutils/tests/listop.c76
-rw-r--r--libmailutils/tests/testsuite.at1
-rw-r--r--libmailutils/tests/wordsplit.at304
-rw-r--r--libmailutils/tests/wsp.c285
-rw-r--r--libmailutils/url/copy.c2
-rw-r--r--libmailutils/url/create.c21
25 files changed, 2483 insertions, 244 deletions
diff --git a/libmailutils/auth/mu_auth.c b/libmailutils/auth/mu_auth.c
index f9f33ca98..f4ea60e11 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 c51c947be..b4f208ed7 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 6e63b23f4..142de6b6a 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/base/argcvfree.c b/libmailutils/base/argcvfree.c
new file mode 100644
index 000000000..737d67db8
--- /dev/null
+++ b/libmailutils/base/argcvfree.c
@@ -0,0 +1,51 @@
+/* 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
+ 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>
+
+/*
+ * 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)
+{
+ 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 000000000..a5877e823
--- /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/base/argcvrem.c b/libmailutils/base/argcvrem.c
new file mode 100644
index 000000000..d6e994dea
--- /dev/null
+++ b/libmailutils/base/argcvrem.c
@@ -0,0 +1,55 @@
+/* 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
+ 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>
+
+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/base/mutil.c b/libmailutils/base/mutil.c
index ffa04dd6a..11232cfbe 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 e7aaa7b2c..1d75b3b52 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 67c119947..52be3901f 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 9fe5b1b2f..cc77fb96f 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 7e6f63cc8..d2d89d556 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 01fb6d31e..fec7cabb4 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 a3e3764f2..d7a13629a 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 437bf01e0..c05c91e43 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 485df4a67..5cb6a2368 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 000000000..301ab005f
--- /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("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",(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 ec7502034..0157222e9 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 f714b587e..e34d2656a 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 88c54a2cb..66e9060a4 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 564e1d301..89e72eb5d 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 d5d02aa35..78fb693d6 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 000000000..dca379252
--- /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 000000000..095ce66b7
--- /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 7190b4212..ee5ad47dd 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 674a52469..0d0cb304d 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] == '/')
{

Return to:

Send suggestions and report system problems to the System administrator.