lavu: add escape API
authorStefano Sabatini <stefasab@gmail.com>
Sun, 16 Dec 2012 11:17:23 +0000 (12:17 +0100)
committerStefano Sabatini <stefasab@gmail.com>
Thu, 7 Mar 2013 00:12:04 +0000 (01:12 +0100)
The escape API will be useful to perform escaping programmatically, which
is required when crafting argument strings, and will be used for context
printing as well.

This is based on the ffescape tool code, with a few extensions and fixes.

doc/APIchanges
libavutil/avstring.c
libavutil/avstring.h
libavutil/bprint.c
libavutil/bprint.h
libavutil/version.h
tools/ffescape.c

index 8535d9f..a1e8180 100644 (file)
@@ -15,6 +15,9 @@ libavutil:     2012-10-22
 
 API changes, most recent first:
 
+2013-03-07 - xxxxxx - lavu 52.18.100 - avstring.h,bprint.h
+  Add av_escape() and av_bprint_escape() API.
+
 2013-02-24 - xxxxxx - lavfi 3.41.100 - buffersink.h
   Add sample_rates field to AVABufferSinkParams.
 
index 9ad1e12..45f8d78 100644 (file)
@@ -28,6 +28,7 @@
 #include "common.h"
 #include "mem.h"
 #include "avstring.h"
+#include "bprint.h"
 
 int av_strstart(const char *str, const char *pfx, const char **ptr)
 {
@@ -267,6 +268,23 @@ const char *av_dirname(char *path)
     return path;
 }
 
+int av_escape(char **dst, const char *src, const char *special_chars,
+              enum AVEscapeMode mode, int flags)
+{
+    AVBPrint dstbuf;
+
+    av_bprint_init(&dstbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
+    av_bprint_escape(&dstbuf, src, special_chars, mode, flags);
+
+    if (!av_bprint_is_complete(&dstbuf)) {
+        av_bprint_finalize(&dstbuf, NULL);
+        return AVERROR(ENOMEM);
+    } else {
+        av_bprint_finalize(&dstbuf, dst);
+        return dstbuf.len;
+    }
+}
+
 #ifdef TEST
 
 int main(void)
index 3896b5f..5b078f1 100644 (file)
@@ -266,6 +266,48 @@ const char *av_basename(const char *path);
  */
 const char *av_dirname(char *path);
 
+enum AVEscapeMode {
+    AV_ESCAPE_MODE_AUTO,      ///< Use auto-selected escaping mode.
+    AV_ESCAPE_MODE_BACKSLASH, ///< Use backslash escaping.
+    AV_ESCAPE_MODE_QUOTE,     ///< Use single-quote escaping.
+};
+
+/**
+ * Consider spaces special and escape them even in the middle of the
+ * string.
+ *
+ * This is equivalent to adding the whitespace characters to the special
+ * characters lists, except it is guaranteed to use the exact same list
+ * of whitespace characters as the rest of libavutil.
+ */
+#define AV_ESCAPE_FLAG_WHITESPACE 0x01
+
+/**
+ * Escape only specified special characters.
+ * Without this flag, escape also any characters that may be considered
+ * special by av_get_token(), such as the single quote.
+ */
+#define AV_ESCAPE_FLAG_STRICT 0x02
+
+/**
+ * Escape string in src, and put the escaped string in an allocated
+ * string in *dst, which must be freed with av_free().
+ *
+ * @param dst           pointer where an allocated string is put
+ * @param src           string to escape, must be non-NULL
+ * @param special_chars string containing the special characters which
+ *                      need to be escaped, can be NULL
+ * @param mode          escape mode to employ, see AV_ESCAPE_MODE_* macros.
+ *                      Any unknown value for mode will be considered equivalent to
+ *                      AV_ESCAPE_MODE_BACKSLASH, but this behaviour can change without
+ *                      notice.
+ * @param flags         flags which control how to escape, see AV_ESCAPE_FLAG_ macros
+ * @return the length of the allocated string, or a negative error code in case of error
+ * @see av_bprint_escape()
+ */
+int av_escape(char **dst, const char *src, const char *special_chars,
+              enum AVEscapeMode mode, int flags);
+
 /**
  * @}
  */
index 4684ab4..fd7611a 100644 (file)
@@ -23,6 +23,7 @@
 #include <string.h>
 #include <time.h>
 #include "avassert.h"
+#include "avstring.h"
 #include "bprint.h"
 #include "common.h"
 #include "error.h"
@@ -217,6 +218,50 @@ int av_bprint_finalize(AVBPrint *buf, char **ret_str)
     return ret;
 }
 
+#define WHITESPACES " \n\t"
+
+void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars,
+                      enum AVEscapeMode mode, int flags)
+{
+    const char *src0 = src;
+
+    if (mode == AV_ESCAPE_MODE_AUTO)
+        mode = AV_ESCAPE_MODE_BACKSLASH; /* TODO: implement a heuristic */
+
+    switch (mode) {
+    case AV_ESCAPE_MODE_QUOTE:
+        /* enclose the string between '' */
+        av_bprint_chars(dstbuf, '\'', 1);
+        for (; *src; src++) {
+            if (*src == '\'')
+                av_bprintf(dstbuf, "'\\''");
+            else
+                av_bprint_chars(dstbuf, *src, 1);
+        }
+        av_bprint_chars(dstbuf, '\'', 1);
+        break;
+
+    /* case AV_ESCAPE_MODE_BACKSLASH or unknown mode */
+    default:
+        /* \-escape characters */
+        for (; *src; src++) {
+            int is_first_last       = src == src0 || !*(src+1);
+            int is_ws               = !!strchr(WHITESPACES, *src);
+            int is_strictly_special = special_chars && strchr(special_chars, *src);
+            int is_special          =
+                is_strictly_special || strchr("'\\", *src) ||
+                (is_ws && (flags & AV_ESCAPE_FLAG_WHITESPACE));
+
+            if (is_strictly_special ||
+                (!(flags & AV_ESCAPE_FLAG_STRICT) &&
+                 (is_special || (is_ws && is_first_last))))
+                av_bprint_chars(dstbuf, '\\', 1);
+            av_bprint_chars(dstbuf, *src, 1);
+        }
+        break;
+    }
+}
+
 #ifdef TEST
 
 #undef printf
index f3915fe..df78916 100644 (file)
@@ -22,6 +22,7 @@
 #define AVUTIL_BPRINT_H
 
 #include "attributes.h"
+#include "avstring.h"
 
 /**
  * Define a structure with extra padding to a fixed size
@@ -180,4 +181,20 @@ static inline int av_bprint_is_complete(AVBPrint *buf)
  */
 int av_bprint_finalize(AVBPrint *buf, char **ret_str);
 
+/**
+ * Escape the content in src and append it to dstbuf.
+ *
+ * @param dstbuf        already inited destination bprint buffer
+ * @param src           string containing the text to escape
+ * @param special_chars string containing the special characters which
+ *                      need to be escaped, can be NULL
+ * @param mode          escape mode to employ, see AV_ESCAPE_MODE_* macros.
+ *                      Any unknown value for mode will be considered equivalent to
+ *                      AV_ESCAPE_MODE_BACKSLASH, but this behaviour can change without
+ *                      notice.
+ * @param flags         flags which control how to escape, see AV_ESCAPE_FLAG_* macros
+ */
+void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars,
+                      enum AVEscapeMode mode, int flags);
+
 #endif /* AVUTIL_BPRINT_H */
index cf65fff..4cd8226 100644 (file)
@@ -75,8 +75,8 @@
  */
 
 #define LIBAVUTIL_VERSION_MAJOR  52
-#define LIBAVUTIL_VERSION_MINOR  17
-#define LIBAVUTIL_VERSION_MICRO 103
+#define LIBAVUTIL_VERSION_MINOR  18
+#define LIBAVUTIL_VERSION_MICRO 100
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
                                                LIBAVUTIL_VERSION_MINOR, \
index d777fe4..0530d28 100644 (file)
@@ -42,80 +42,16 @@ static void usage(void)
     printf("\n"
            "Options:\n"
            "-e                echo each input line on output\n"
+           "-f flag           select an escape flag, can assume the values 'whitespace' and 'strict'\n"
            "-h                print this help\n"
            "-i INFILE         set INFILE as input file, stdin if omitted\n"
            "-l LEVEL          set the number of escaping levels, 1 if omitted\n"
-           "-m ESCAPE_MODE    select escape mode between 'full', 'lazy', 'quote', default is 'lazy'\n"
+           "-m ESCAPE_MODE    select escape mode between 'auto', 'backslash', 'quote'\n"
            "-o OUTFILE        set OUTFILE as output file, stdout if omitted\n"
            "-p PROMPT         set output prompt, is '=> ' by default\n"
            "-s SPECIAL_CHARS  set the list of special characters\n");
 }
 
-#define WHITESPACES " \n\t"
-
-enum EscapeMode {
-    ESCAPE_MODE_FULL,
-    ESCAPE_MODE_LAZY,
-    ESCAPE_MODE_QUOTE,
-};
-
-static int escape(char **dst, const char *src, const char *special_chars,
-                  enum EscapeMode mode)
-{
-    AVBPrint dstbuf;
-
-    av_bprint_init(&dstbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
-
-    switch (mode) {
-    case ESCAPE_MODE_FULL:
-    case ESCAPE_MODE_LAZY:
-        /* \-escape characters */
-
-        if (mode == ESCAPE_MODE_LAZY && strchr(WHITESPACES, *src))
-            av_bprintf(&dstbuf, "\\%c", *src++);
-
-        for (; *src; src++) {
-            if ((special_chars && strchr(special_chars, *src)) ||
-                strchr("'\\", *src) ||
-                (mode == ESCAPE_MODE_FULL && strchr(WHITESPACES, *src)))
-                av_bprintf(&dstbuf, "\\%c", *src);
-            else
-                av_bprint_chars(&dstbuf, *src, 1);
-        }
-
-        if (mode == ESCAPE_MODE_LAZY && strchr(WHITESPACES, dstbuf.str[dstbuf.len-1])) {
-            char c = dstbuf.str[dstbuf.len-1];
-            dstbuf.str[dstbuf.len-1] = '\\';
-            av_bprint_chars(&dstbuf, c, 1);
-        }
-        break;
-
-    case ESCAPE_MODE_QUOTE:
-        /* enclose between '' the string */
-        av_bprint_chars(&dstbuf, '\'', 1);
-        for (; *src; src++) {
-            if (*src == '\'')
-                av_bprintf(&dstbuf, "'\\''");
-            else
-                av_bprint_chars(&dstbuf, *src, 1);
-        }
-        av_bprint_chars(&dstbuf, '\'', 1);
-        break;
-
-    default:
-        /* unknown escape mode */
-        return AVERROR(EINVAL);
-    }
-
-    if (!av_bprint_is_complete(&dstbuf)) {
-        av_bprint_finalize(&dstbuf, NULL);
-        return AVERROR(ENOMEM);
-    } else {
-        av_bprint_finalize(&dstbuf, dst);
-        return 0;
-    }
-}
-
 int main(int argc, char **argv)
 {
     AVBPrint src;
@@ -123,13 +59,14 @@ int main(int argc, char **argv)
     const char *outfilename = NULL, *infilename = NULL;
     FILE *outfile = NULL, *infile = NULL;
     const char *prompt = "=> ";
-    enum EscapeMode escape_mode = ESCAPE_MODE_LAZY;
+    enum AVEscapeMode escape_mode = AV_ESCAPE_MODE_AUTO;
+    int escape_flags = 0;
     int level = 1;
     int echo = 0;
     char *special_chars = NULL;
     int c;
 
-    while ((c = getopt(argc, argv, "ehi:l:o:m:p:s:")) != -1) {
+    while ((c = getopt(argc, argv, "ef:hi:l:o:m:p:s:")) != -1) {
         switch (c) {
         case 'e':
             echo = 1;
@@ -140,6 +77,16 @@ int main(int argc, char **argv)
         case 'i':
             infilename = optarg;
             break;
+        case 'f':
+            if      (!strcmp(optarg, "whitespace")) escape_flags |= AV_ESCAPE_FLAG_WHITESPACE;
+            else if (!strcmp(optarg, "strict"))     escape_flags |= AV_ESCAPE_FLAG_STRICT;
+            else {
+                av_log(NULL, AV_LOG_ERROR,
+                       "Invalid value '%s' for option -f, "
+                       "valid arguments are 'whitespace', and 'strict'\n", optarg);
+                return 1;
+            }
+            break;
         case 'l':
         {
             char *tail;
@@ -154,13 +101,13 @@ int main(int argc, char **argv)
             break;
         }
         case 'm':
-            if      (!strcmp(optarg, "full"))  escape_mode = ESCAPE_MODE_FULL;
-            else if (!strcmp(optarg, "lazy"))  escape_mode = ESCAPE_MODE_LAZY;
-            else if (!strcmp(optarg, "quote")) escape_mode = ESCAPE_MODE_QUOTE;
+            if      (!strcmp(optarg, "auto"))      escape_mode = AV_ESCAPE_MODE_AUTO;
+            else if (!strcmp(optarg, "backslash")) escape_mode = AV_ESCAPE_MODE_BACKSLASH;
+            else if (!strcmp(optarg, "quote"))     escape_mode = AV_ESCAPE_MODE_QUOTE;
             else {
                 av_log(NULL, AV_LOG_ERROR,
                        "Invalid value '%s' for option -m, "
-                       "valid arguments are 'full', 'lazy', 'quote'\n", optarg);
+                       "valid arguments are 'backslash', and 'quote'\n", optarg);
                 return 1;
             }
             break;
@@ -219,7 +166,7 @@ int main(int argc, char **argv)
     /* escape */
     dst_buf = src_buf;
     while (level--) {
-        if (escape(&dst_buf, src_buf, special_chars, escape_mode) < 0) {
+        if (av_escape(&dst_buf, src_buf, special_chars, escape_mode, escape_flags) < 0) {
             av_log(NULL, AV_LOG_ERROR, "Could not escape string\n");
             return 1;
         }