[Patch v3 1/6] hex-escape: (en|de)code strings to/from restricted character set
authordavid <david@tethera.net>
Sun, 19 Aug 2012 13:18:29 +0000 (15:18 +0200)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:49:07 +0000 (09:49 -0800)
ad/1d0f5fee25912c1dcda662d27c5083728accc7 [new file with mode: 0644]

diff --git a/ad/1d0f5fee25912c1dcda662d27c5083728accc7 b/ad/1d0f5fee25912c1dcda662d27c5083728accc7
new file mode 100644 (file)
index 0000000..9db049a
--- /dev/null
@@ -0,0 +1,301 @@
+Return-Path: <bremner@tesseract.cs.unb.ca>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+       by olra.theworths.org (Postfix) with ESMTP id 9D01E431FBF\r
+       for <notmuch@notmuchmail.org>; Mon, 20 Aug 2012 00:18:40 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: 0\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none]\r
+       autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+       by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+       with ESMTP id Xy3gQrXicApC for <notmuch@notmuchmail.org>;\r
+       Mon, 20 Aug 2012 00:18:39 -0700 (PDT)\r
+Received: from tesseract.cs.unb.ca (tesseract.cs.unb.ca [131.202.240.238])\r
+       (using TLSv1 with cipher AES256-SHA (256/256 bits))\r
+       (No client certificate requested)\r
+       by olra.theworths.org (Postfix) with ESMTPS id 5A562431FBD\r
+       for <notmuch@notmuchmail.org>; Mon, 20 Aug 2012 00:18:38 -0700 (PDT)\r
+Received: from remotemail by tesseract.cs.unb.ca with local (Exim 4.72)\r
+       (envelope-from <bremner@tesseract.cs.unb.ca>)\r
+       id 1T3MFx-0002G4-Lc; Mon, 20 Aug 2012 04:18:37 -0300\r
+Received: (nullmailer pid 7809 invoked by uid 1000);\r
+       Sun, 19 Aug 2012 13:19:07 -0000\r
+From: david@tethera.net\r
+To: notmuch@notmuchmail.org\r
+Subject: [Patch v3 1/6] hex-escape: (en|de)code strings to/from restricted\r
+       character set\r
+Date: Sun, 19 Aug 2012 15:18:29 +0200\r
+Message-Id: <1345382314-5330-2-git-send-email-david@tethera.net>\r
+X-Mailer: git-send-email 1.7.10.4\r
+In-Reply-To: <1345382314-5330-1-git-send-email-david@tethera.net>\r
+References: <1345382314-5330-1-git-send-email-david@tethera.net>\r
+Cc: David Bremner <bremner@debian.org>\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+       <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Mon, 20 Aug 2012 07:18:40 -0000\r
+\r
+From: David Bremner <bremner@debian.org>\r
+\r
+The character set is chosen to be suitable for pathnames, and the same\r
+as that used by contrib/nmbug\r
+\r
+[With additions by Jani Nikula]\r
+---\r
+ util/Makefile.local |    2 +-\r
+ util/hex-escape.c   |  168 +++++++++++++++++++++++++++++++++++++++++++++++++++\r
+ util/hex-escape.h   |   41 +++++++++++++\r
+ 3 files changed, 210 insertions(+), 1 deletion(-)\r
+ create mode 100644 util/hex-escape.c\r
+ create mode 100644 util/hex-escape.h\r
+\r
+diff --git a/util/Makefile.local b/util/Makefile.local\r
+index c7cae61..3ca623e 100644\r
+--- a/util/Makefile.local\r
++++ b/util/Makefile.local\r
+@@ -3,7 +3,7 @@\r
+ dir := util\r
+ extra_cflags += -I$(srcdir)/$(dir)\r
\r
+-libutil_c_srcs := $(dir)/xutil.c $(dir)/error_util.c\r
++libutil_c_srcs := $(dir)/xutil.c $(dir)/error_util.c $(dir)/hex-escape.c\r
\r
+ libutil_modules := $(libutil_c_srcs:.c=.o)\r
\r
+diff --git a/util/hex-escape.c b/util/hex-escape.c\r
+new file mode 100644\r
+index 0000000..d8905d0\r
+--- /dev/null\r
++++ b/util/hex-escape.c\r
+@@ -0,0 +1,168 @@\r
++/* hex-escape.c -  Manage encoding and decoding of byte strings into path names\r
++ *\r
++ * Copyright (c) 2011 David Bremner\r
++ *\r
++ * This program is free software: you can redistribute it and/or modify\r
++ * it under the terms of the GNU General Public License as published by\r
++ * the Free Software Foundation, either version 3 of the License, or\r
++ * (at your option) any later version.\r
++ *\r
++ * This program is distributed in the hope that it will be useful,\r
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
++ * GNU General Public License for more details.\r
++ *\r
++ * You should have received a copy of the GNU General Public License\r
++ * along with this program.  If not, see http://www.gnu.org/licenses/ .\r
++ *\r
++ * Author: David Bremner <david@tethera.net>\r
++ */\r
++\r
++#include <assert.h>\r
++#include <string.h>\r
++#include <talloc.h>\r
++#include <ctype.h>\r
++#include "error_util.h"\r
++#include "hex-escape.h"\r
++\r
++static const size_t default_buf_size = 1024;\r
++\r
++static const char *output_charset =\r
++    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-_@=.:,";\r
++\r
++static const char escape_char = '%';\r
++\r
++static int\r
++is_output (char c)\r
++{\r
++    return (strchr (output_charset, c) != NULL);\r
++}\r
++\r
++static int\r
++maybe_realloc (void *ctx, size_t needed, char **out, size_t *out_size)\r
++{\r
++    if (*out_size < needed) {\r
++\r
++      if (*out == NULL)\r
++          *out = talloc_size (ctx, needed);\r
++      else\r
++          *out = talloc_realloc (ctx, *out, char, needed);\r
++\r
++      if (*out == NULL)\r
++          return 0;\r
++\r
++      *out_size = needed;\r
++    }\r
++    return 1;\r
++}\r
++\r
++hex_status_t\r
++hex_encode (void *ctx, const char *in, char **out, size_t *out_size)\r
++{\r
++\r
++    const unsigned char *p;\r
++    char *q;\r
++\r
++    size_t escape_count = 0;\r
++    size_t len = 0;\r
++    size_t needed;\r
++\r
++    assert (ctx); assert (in); assert (out); assert (out_size);\r
++\r
++    for (p = (unsigned char *) in; *p; p++) {\r
++      escape_count += (!is_output (*p));\r
++      len++;\r
++    }\r
++\r
++    needed = len + escape_count * 2 + 1;\r
++\r
++    if (*out == NULL)\r
++      *out_size = 0;\r
++\r
++    if (!maybe_realloc (ctx, needed, out, out_size))\r
++      return HEX_OUT_OF_MEMORY;\r
++\r
++    q = *out;\r
++    p = (unsigned char *) in;\r
++\r
++    while (*p) {\r
++      if (is_output (*p)) {\r
++          *q++ = *p++;\r
++      } else {\r
++          sprintf (q, "%%%02x", *p++);\r
++          q += 3;\r
++      }\r
++    }\r
++\r
++    *q = '\0';\r
++    return HEX_SUCCESS;\r
++}\r
++\r
++/* Hex decode 'in' to 'out'.\r
++ *\r
++ * This must succeed for in == out to support hex_decode_inplace().\r
++ */\r
++static hex_status_t\r
++hex_decode_internal (const char *in, unsigned char *out)\r
++{\r
++    char buf[3];\r
++\r
++    while (*in) {\r
++      if (*in == escape_char) {\r
++          char *endp;\r
++\r
++          /* This also handles unexpected end-of-string. */\r
++          if (!isxdigit ((unsigned char) in[1]) ||\r
++              !isxdigit ((unsigned char) in[2]))\r
++              return HEX_SYNTAX_ERROR;\r
++\r
++          buf[0] = in[1];\r
++          buf[1] = in[2];\r
++          buf[2] = '\0';\r
++\r
++          *out = strtoul (buf, &endp, 16);\r
++\r
++          if (endp != buf + 2)\r
++              return HEX_SYNTAX_ERROR;\r
++\r
++          in += 3;\r
++          out++;\r
++      } else {\r
++          *out++ = *in++;\r
++      }\r
++    }\r
++\r
++    *out = '\0';\r
++\r
++    return HEX_SUCCESS;\r
++}\r
++\r
++hex_status_t\r
++hex_decode_inplace (char *s)\r
++{\r
++    /* A decoded string is never longer than the encoded one, so it is\r
++     * safe to decode a string onto itself. */\r
++    return hex_decode_internal (s, (unsigned char *) s);\r
++}\r
++\r
++hex_status_t\r
++hex_decode (void *ctx, const char *in, char **out, size_t * out_size)\r
++{\r
++    const char *p;\r
++    size_t escape_count = 0;\r
++    size_t needed = 0;\r
++\r
++    assert (ctx); assert (in); assert (out); assert (out_size);\r
++\r
++    size_t len = strlen (in);\r
++\r
++    for (p = in; *p; p++)\r
++      escape_count += (*p == escape_char);\r
++\r
++    needed = len - escape_count * 2 + 1;\r
++\r
++    if (!maybe_realloc (ctx, needed, out, out_size))\r
++      return HEX_OUT_OF_MEMORY;\r
++\r
++    return hex_decode_internal (in, (unsigned char *) *out);\r
++}\r
+diff --git a/util/hex-escape.h b/util/hex-escape.h\r
+new file mode 100644\r
+index 0000000..5182042\r
+--- /dev/null\r
++++ b/util/hex-escape.h\r
+@@ -0,0 +1,41 @@\r
++#ifndef _HEX_ESCAPE_H\r
++#define _HEX_ESCAPE_H\r
++\r
++typedef enum hex_status {\r
++    HEX_SUCCESS = 0,\r
++    HEX_SYNTAX_ERROR,\r
++    HEX_OUT_OF_MEMORY\r
++} hex_status_t;\r
++\r
++/*\r
++ * The API for hex_encode() and hex_decode() is modelled on that for\r
++ * getline.\r
++ *\r
++ * If 'out' points to a NULL pointer a char array of the appropriate\r
++ * size is allocated using talloc, and out_size is updated.\r
++ *\r
++ * If 'out' points to a non-NULL pointer, it assumed to describe an\r
++ * existing char array, with the size given in *out_size.  This array\r
++ * may be resized by talloc_realloc if needed; in this case *out_size\r
++ * will also be updated.\r
++ *\r
++ * Note that it is an error to pass a NULL pointer for any parameter\r
++ * of these routines.\r
++ */\r
++\r
++hex_status_t\r
++hex_encode (void *talloc_ctx, const char *in, char **out,\r
++            size_t *out_size);\r
++\r
++hex_status_t\r
++hex_decode (void *talloc_ctx, const char *in, char **out,\r
++            size_t *out_size);\r
++\r
++/*\r
++ * Non-allocating hex decode to decode 's' in-place. The length of the\r
++ * result is always equal to or shorter than the length of the\r
++ * original.\r
++ */\r
++hex_status_t\r
++hex_decode_inplace (char *s);\r
++#endif\r
+-- \r
+1.7.10.4\r
+\r