ARM: at91/dt: Fix sama5d3x typos
[cascardo/linux.git] / lib / string_helpers.c
index 29033f3..58b78ba 100644 (file)
@@ -8,6 +8,8 @@
 #include <linux/math64.h>
 #include <linux/export.h>
 #include <linux/ctype.h>
+#include <linux/errno.h>
+#include <linux/string.h>
 #include <linux/string_helpers.h>
 
 /**
@@ -168,6 +170,44 @@ static bool unescape_special(char **src, char **dst)
        return true;
 }
 
+/**
+ * string_unescape - unquote characters in the given string
+ * @src:       source buffer (escaped)
+ * @dst:       destination buffer (unescaped)
+ * @size:      size of the destination buffer (0 to unlimit)
+ * @flags:     combination of the flags (bitwise OR):
+ *     %UNESCAPE_SPACE:
+ *             '\f' - form feed
+ *             '\n' - new line
+ *             '\r' - carriage return
+ *             '\t' - horizontal tab
+ *             '\v' - vertical tab
+ *     %UNESCAPE_OCTAL:
+ *             '\NNN' - byte with octal value NNN (1 to 3 digits)
+ *     %UNESCAPE_HEX:
+ *             '\xHH' - byte with hexadecimal value HH (1 to 2 digits)
+ *     %UNESCAPE_SPECIAL:
+ *             '\"' - double quote
+ *             '\\' - backslash
+ *             '\a' - alert (BEL)
+ *             '\e' - escape
+ *     %UNESCAPE_ANY:
+ *             all previous together
+ *
+ * Description:
+ * The function unquotes characters in the given string.
+ *
+ * Because the size of the output will be the same as or less than the size of
+ * the input, the transformation may be performed in place.
+ *
+ * Caller must provide valid source and destination pointers. Be aware that
+ * destination buffer will always be NULL-terminated. Source string must be
+ * NULL-terminated as well.
+ *
+ * Return:
+ * The amount of the characters processed to the destination buffer excluding
+ * trailing '\0' is returned.
+ */
 int string_unescape(char *src, char *dst, size_t size, unsigned int flags)
 {
        char *out = dst;
@@ -202,3 +242,275 @@ int string_unescape(char *src, char *dst, size_t size, unsigned int flags)
        return out - dst;
 }
 EXPORT_SYMBOL(string_unescape);
+
+static int escape_passthrough(unsigned char c, char **dst, size_t *osz)
+{
+       char *out = *dst;
+
+       if (*osz < 1)
+               return -ENOMEM;
+
+       *out++ = c;
+
+       *dst = out;
+       *osz -= 1;
+
+       return 1;
+}
+
+static int escape_space(unsigned char c, char **dst, size_t *osz)
+{
+       char *out = *dst;
+       unsigned char to;
+
+       if (*osz < 2)
+               return -ENOMEM;
+
+       switch (c) {
+       case '\n':
+               to = 'n';
+               break;
+       case '\r':
+               to = 'r';
+               break;
+       case '\t':
+               to = 't';
+               break;
+       case '\v':
+               to = 'v';
+               break;
+       case '\f':
+               to = 'f';
+               break;
+       default:
+               return 0;
+       }
+
+       *out++ = '\\';
+       *out++ = to;
+
+       *dst = out;
+       *osz -= 2;
+
+       return 1;
+}
+
+static int escape_special(unsigned char c, char **dst, size_t *osz)
+{
+       char *out = *dst;
+       unsigned char to;
+
+       if (*osz < 2)
+               return -ENOMEM;
+
+       switch (c) {
+       case '\\':
+               to = '\\';
+               break;
+       case '\a':
+               to = 'a';
+               break;
+       case '\e':
+               to = 'e';
+               break;
+       default:
+               return 0;
+       }
+
+       *out++ = '\\';
+       *out++ = to;
+
+       *dst = out;
+       *osz -= 2;
+
+       return 1;
+}
+
+static int escape_null(unsigned char c, char **dst, size_t *osz)
+{
+       char *out = *dst;
+
+       if (*osz < 2)
+               return -ENOMEM;
+
+       if (c)
+               return 0;
+
+       *out++ = '\\';
+       *out++ = '0';
+
+       *dst = out;
+       *osz -= 2;
+
+       return 1;
+}
+
+static int escape_octal(unsigned char c, char **dst, size_t *osz)
+{
+       char *out = *dst;
+
+       if (*osz < 4)
+               return -ENOMEM;
+
+       *out++ = '\\';
+       *out++ = ((c >> 6) & 0x07) + '0';
+       *out++ = ((c >> 3) & 0x07) + '0';
+       *out++ = ((c >> 0) & 0x07) + '0';
+
+       *dst = out;
+       *osz -= 4;
+
+       return 1;
+}
+
+static int escape_hex(unsigned char c, char **dst, size_t *osz)
+{
+       char *out = *dst;
+
+       if (*osz < 4)
+               return -ENOMEM;
+
+       *out++ = '\\';
+       *out++ = 'x';
+       *out++ = hex_asc_hi(c);
+       *out++ = hex_asc_lo(c);
+
+       *dst = out;
+       *osz -= 4;
+
+       return 1;
+}
+
+/**
+ * string_escape_mem - quote characters in the given memory buffer
+ * @src:       source buffer (unescaped)
+ * @isz:       source buffer size
+ * @dst:       destination buffer (escaped)
+ * @osz:       destination buffer size
+ * @flags:     combination of the flags (bitwise OR):
+ *     %ESCAPE_SPACE:
+ *             '\f' - form feed
+ *             '\n' - new line
+ *             '\r' - carriage return
+ *             '\t' - horizontal tab
+ *             '\v' - vertical tab
+ *     %ESCAPE_SPECIAL:
+ *             '\\' - backslash
+ *             '\a' - alert (BEL)
+ *             '\e' - escape
+ *     %ESCAPE_NULL:
+ *             '\0' - null
+ *     %ESCAPE_OCTAL:
+ *             '\NNN' - byte with octal value NNN (3 digits)
+ *     %ESCAPE_ANY:
+ *             all previous together
+ *     %ESCAPE_NP:
+ *             escape only non-printable characters (checked by isprint)
+ *     %ESCAPE_ANY_NP:
+ *             all previous together
+ *     %ESCAPE_HEX:
+ *             '\xHH' - byte with hexadecimal value HH (2 digits)
+ * @esc:       NULL-terminated string of characters any of which, if found in
+ *             the source, has to be escaped
+ *
+ * Description:
+ * The process of escaping byte buffer includes several parts. They are applied
+ * in the following sequence.
+ *     1. The character is matched to the printable class, if asked, and in
+ *        case of match it passes through to the output.
+ *     2. The character is not matched to the one from @esc string and thus
+ *        must go as is to the output.
+ *     3. The character is checked if it falls into the class given by @flags.
+ *        %ESCAPE_OCTAL and %ESCAPE_HEX are going last since they cover any
+ *        character. Note that they actually can't go together, otherwise
+ *        %ESCAPE_HEX will be ignored.
+ *
+ * Caller must provide valid source and destination pointers. Be aware that
+ * destination buffer will not be NULL-terminated, thus caller have to append
+ * it if needs.
+ *
+ * Return:
+ * The amount of the characters processed to the destination buffer, or
+ * %-ENOMEM if the size of buffer is not enough to put an escaped character is
+ * returned.
+ *
+ * Even in the case of error @dst pointer will be updated to point to the byte
+ * after the last processed character.
+ */
+int string_escape_mem(const char *src, size_t isz, char **dst, size_t osz,
+                     unsigned int flags, const char *esc)
+{
+       char *out = *dst, *p = out;
+       bool is_dict = esc && *esc;
+       int ret = 0;
+
+       while (isz--) {
+               unsigned char c = *src++;
+
+               /*
+                * Apply rules in the following sequence:
+                *      - the character is printable, when @flags has
+                *        %ESCAPE_NP bit set
+                *      - the @esc string is supplied and does not contain a
+                *        character under question
+                *      - the character doesn't fall into a class of symbols
+                *        defined by given @flags
+                * In these cases we just pass through a character to the
+                * output buffer.
+                */
+               if ((flags & ESCAPE_NP && isprint(c)) ||
+                   (is_dict && !strchr(esc, c))) {
+                       /* do nothing */
+               } else {
+                       if (flags & ESCAPE_SPACE) {
+                               ret = escape_space(c, &p, &osz);
+                               if (ret < 0)
+                                       break;
+                               if (ret > 0)
+                                       continue;
+                       }
+
+                       if (flags & ESCAPE_SPECIAL) {
+                               ret = escape_special(c, &p, &osz);
+                               if (ret < 0)
+                                       break;
+                               if (ret > 0)
+                                       continue;
+                       }
+
+                       if (flags & ESCAPE_NULL) {
+                               ret = escape_null(c, &p, &osz);
+                               if (ret < 0)
+                                       break;
+                               if (ret > 0)
+                                       continue;
+                       }
+
+                       /* ESCAPE_OCTAL and ESCAPE_HEX always go last */
+                       if (flags & ESCAPE_OCTAL) {
+                               ret = escape_octal(c, &p, &osz);
+                               if (ret < 0)
+                                       break;
+                               continue;
+                       }
+                       if (flags & ESCAPE_HEX) {
+                               ret = escape_hex(c, &p, &osz);
+                               if (ret < 0)
+                                       break;
+                               continue;
+                       }
+               }
+
+               ret = escape_passthrough(c, &p, &osz);
+               if (ret < 0)
+                       break;
+       }
+
+       *dst = p;
+
+       if (ret < 0)
+               return ret;
+
+       return p - out;
+}
+EXPORT_SYMBOL(string_escape_mem);