[Patch v2 01/17] hex-escape: (en|de)code strings to/from restricted character set
Jani Nikula
jani at nikula.org
Fri Nov 30 13:43:38 PST 2012
On Sat, 24 Nov 2012, david at tethera.net wrote:
> From: David Bremner <bremner at debian.org>
>
> The character set is chosen to be suitable for pathnames, and the same
> as that used by contrib/nmbug
>
> [With additions by Jani Nikula]
So it must be good. ;)
Just a couple of nitpicks below.
BR,
Jani.
> ---
> util/Makefile.local | 2 +-
> util/hex-escape.c | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++
> util/hex-escape.h | 41 +++++++++++++
> 3 files changed, 210 insertions(+), 1 deletion(-)
> create mode 100644 util/hex-escape.c
> create mode 100644 util/hex-escape.h
>
> diff --git a/util/Makefile.local b/util/Makefile.local
> index c7cae61..3ca623e 100644
> --- a/util/Makefile.local
> +++ b/util/Makefile.local
> @@ -3,7 +3,7 @@
> dir := util
> extra_cflags += -I$(srcdir)/$(dir)
>
> -libutil_c_srcs := $(dir)/xutil.c $(dir)/error_util.c
> +libutil_c_srcs := $(dir)/xutil.c $(dir)/error_util.c $(dir)/hex-escape.c
>
> libutil_modules := $(libutil_c_srcs:.c=.o)
>
> diff --git a/util/hex-escape.c b/util/hex-escape.c
> new file mode 100644
> index 0000000..d8905d0
> --- /dev/null
> +++ b/util/hex-escape.c
> @@ -0,0 +1,168 @@
> +/* hex-escape.c - Manage encoding and decoding of byte strings into path names
> + *
> + * Copyright (c) 2011 David Bremner
> + *
> + * 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/ .
> + *
> + * Author: David Bremner <david at tethera.net>
> + */
> +
> +#include <assert.h>
> +#include <string.h>
> +#include <talloc.h>
> +#include <ctype.h>
> +#include "error_util.h"
> +#include "hex-escape.h"
> +
> +static const size_t default_buf_size = 1024;
> +
> +static const char *output_charset =
> + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-_@=.:,";
> +
> +static const char escape_char = '%';
> +
> +static int
> +is_output (char c)
> +{
> + return (strchr (output_charset, c) != NULL);
> +}
> +
> +static int
> +maybe_realloc (void *ctx, size_t needed, char **out, size_t *out_size)
> +{
> + if (*out_size < needed) {
> +
> + if (*out == NULL)
> + *out = talloc_size (ctx, needed);
> + else
> + *out = talloc_realloc (ctx, *out, char, needed);
> +
> + if (*out == NULL)
> + return 0;
> +
> + *out_size = needed;
> + }
> + return 1;
> +}
> +
> +hex_status_t
> +hex_encode (void *ctx, const char *in, char **out, size_t *out_size)
> +{
> +
> + const unsigned char *p;
The casts to unsigned char * below bother me. Perhaps this should be
just const char *, with the only cast being in the sprintf?
> + char *q;
> +
> + size_t escape_count = 0;
> + size_t len = 0;
> + size_t needed;
> +
> + assert (ctx); assert (in); assert (out); assert (out_size);
> +
> + for (p = (unsigned char *) in; *p; p++) {
> + escape_count += (!is_output (*p));
> + len++;
> + }
> +
> + needed = len + escape_count * 2 + 1;
I wonder if it would be clearer if escape_count and len were ditched,
and the for loop just did:
needed += is_output (*p) ? 1 : 3;
and another needed++ after the loop for NUL. And maybe s/needed/len/
after that.
> +
> + if (*out == NULL)
> + *out_size = 0;
> +
> + if (!maybe_realloc (ctx, needed, out, out_size))
> + return HEX_OUT_OF_MEMORY;
> +
> + q = *out;
> + p = (unsigned char *) in;
> +
> + while (*p) {
> + if (is_output (*p)) {
> + *q++ = *p++;
> + } else {
> + sprintf (q, "%%%02x", *p++);
> + q += 3;
> + }
> + }
> +
> + *q = '\0';
> + return HEX_SUCCESS;
> +}
> +
> +/* Hex decode 'in' to 'out'.
> + *
> + * This must succeed for in == out to support hex_decode_inplace().
> + */
> +static hex_status_t
> +hex_decode_internal (const char *in, unsigned char *out)
> +{
> + char buf[3];
> +
> + while (*in) {
> + if (*in == escape_char) {
> + char *endp;
> +
> + /* This also handles unexpected end-of-string. */
> + if (!isxdigit ((unsigned char) in[1]) ||
> + !isxdigit ((unsigned char) in[2]))
> + return HEX_SYNTAX_ERROR;
> +
> + buf[0] = in[1];
> + buf[1] = in[2];
> + buf[2] = '\0';
> +
> + *out = strtoul (buf, &endp, 16);
> +
> + if (endp != buf + 2)
> + return HEX_SYNTAX_ERROR;
> +
> + in += 3;
> + out++;
> + } else {
> + *out++ = *in++;
> + }
> + }
> +
> + *out = '\0';
> +
> + return HEX_SUCCESS;
> +}
> +
> +hex_status_t
> +hex_decode_inplace (char *s)
> +{
> + /* A decoded string is never longer than the encoded one, so it is
> + * safe to decode a string onto itself. */
> + return hex_decode_internal (s, (unsigned char *) s);
> +}
> +
> +hex_status_t
> +hex_decode (void *ctx, const char *in, char **out, size_t * out_size)
> +{
> + const char *p;
> + size_t escape_count = 0;
> + size_t needed = 0;
> +
> + assert (ctx); assert (in); assert (out); assert (out_size);
> +
> + size_t len = strlen (in);
> +
> + for (p = in; *p; p++)
> + escape_count += (*p == escape_char);
> +
> + needed = len - escape_count * 2 + 1;
Same as above for counting the needed size. It would also save scanning
the input string twice (strlen and for loop).
> +
> + if (!maybe_realloc (ctx, needed, out, out_size))
> + return HEX_OUT_OF_MEMORY;
> +
> + return hex_decode_internal (in, (unsigned char *) *out);
> +}
> diff --git a/util/hex-escape.h b/util/hex-escape.h
> new file mode 100644
> index 0000000..5182042
> --- /dev/null
> +++ b/util/hex-escape.h
> @@ -0,0 +1,41 @@
> +#ifndef _HEX_ESCAPE_H
> +#define _HEX_ESCAPE_H
> +
> +typedef enum hex_status {
> + HEX_SUCCESS = 0,
> + HEX_SYNTAX_ERROR,
> + HEX_OUT_OF_MEMORY
> +} hex_status_t;
> +
> +/*
> + * The API for hex_encode() and hex_decode() is modelled on that for
> + * getline.
> + *
> + * If 'out' points to a NULL pointer a char array of the appropriate
> + * size is allocated using talloc, and out_size is updated.
> + *
> + * If 'out' points to a non-NULL pointer, it assumed to describe an
> + * existing char array, with the size given in *out_size. This array
> + * may be resized by talloc_realloc if needed; in this case *out_size
> + * will also be updated.
> + *
> + * Note that it is an error to pass a NULL pointer for any parameter
> + * of these routines.
> + */
> +
> +hex_status_t
> +hex_encode (void *talloc_ctx, const char *in, char **out,
> + size_t *out_size);
> +
> +hex_status_t
> +hex_decode (void *talloc_ctx, const char *in, char **out,
> + size_t *out_size);
> +
> +/*
> + * Non-allocating hex decode to decode 's' in-place. The length of the
> + * result is always equal to or shorter than the length of the
> + * original.
> + */
> +hex_status_t
> +hex_decode_inplace (char *s);
> +#endif
> --
> 1.7.10.4
>
> _______________________________________________
> notmuch mailing list
> notmuch at notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch
More information about the notmuch
mailing list