Browse code

lavu: add escape API

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.

Stefano Sabatini authored on 2012/12/16 20:17:23
Showing 7 changed files
... ...
@@ -15,6 +15,9 @@ libavutil:     2012-10-22
15 15
 
16 16
 API changes, most recent first:
17 17
 
18
+2013-03-07 - xxxxxx - lavu 52.18.100 - avstring.h,bprint.h
19
+  Add av_escape() and av_bprint_escape() API.
20
+
18 21
 2013-02-24 - xxxxxx - lavfi 3.41.100 - buffersink.h
19 22
   Add sample_rates field to AVABufferSinkParams.
20 23
 
... ...
@@ -28,6 +28,7 @@
28 28
 #include "common.h"
29 29
 #include "mem.h"
30 30
 #include "avstring.h"
31
+#include "bprint.h"
31 32
 
32 33
 int av_strstart(const char *str, const char *pfx, const char **ptr)
33 34
 {
... ...
@@ -267,6 +268,23 @@ const char *av_dirname(char *path)
267 267
     return path;
268 268
 }
269 269
 
270
+int av_escape(char **dst, const char *src, const char *special_chars,
271
+              enum AVEscapeMode mode, int flags)
272
+{
273
+    AVBPrint dstbuf;
274
+
275
+    av_bprint_init(&dstbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
276
+    av_bprint_escape(&dstbuf, src, special_chars, mode, flags);
277
+
278
+    if (!av_bprint_is_complete(&dstbuf)) {
279
+        av_bprint_finalize(&dstbuf, NULL);
280
+        return AVERROR(ENOMEM);
281
+    } else {
282
+        av_bprint_finalize(&dstbuf, dst);
283
+        return dstbuf.len;
284
+    }
285
+}
286
+
270 287
 #ifdef TEST
271 288
 
272 289
 int main(void)
... ...
@@ -266,6 +266,48 @@ const char *av_basename(const char *path);
266 266
  */
267 267
 const char *av_dirname(char *path);
268 268
 
269
+enum AVEscapeMode {
270
+    AV_ESCAPE_MODE_AUTO,      ///< Use auto-selected escaping mode.
271
+    AV_ESCAPE_MODE_BACKSLASH, ///< Use backslash escaping.
272
+    AV_ESCAPE_MODE_QUOTE,     ///< Use single-quote escaping.
273
+};
274
+
275
+/**
276
+ * Consider spaces special and escape them even in the middle of the
277
+ * string.
278
+ *
279
+ * This is equivalent to adding the whitespace characters to the special
280
+ * characters lists, except it is guaranteed to use the exact same list
281
+ * of whitespace characters as the rest of libavutil.
282
+ */
283
+#define AV_ESCAPE_FLAG_WHITESPACE 0x01
284
+
285
+/**
286
+ * Escape only specified special characters.
287
+ * Without this flag, escape also any characters that may be considered
288
+ * special by av_get_token(), such as the single quote.
289
+ */
290
+#define AV_ESCAPE_FLAG_STRICT 0x02
291
+
292
+/**
293
+ * Escape string in src, and put the escaped string in an allocated
294
+ * string in *dst, which must be freed with av_free().
295
+ *
296
+ * @param dst           pointer where an allocated string is put
297
+ * @param src           string to escape, must be non-NULL
298
+ * @param special_chars string containing the special characters which
299
+ *                      need to be escaped, can be NULL
300
+ * @param mode          escape mode to employ, see AV_ESCAPE_MODE_* macros.
301
+ *                      Any unknown value for mode will be considered equivalent to
302
+ *                      AV_ESCAPE_MODE_BACKSLASH, but this behaviour can change without
303
+ *                      notice.
304
+ * @param flags         flags which control how to escape, see AV_ESCAPE_FLAG_ macros
305
+ * @return the length of the allocated string, or a negative error code in case of error
306
+ * @see av_bprint_escape()
307
+ */
308
+int av_escape(char **dst, const char *src, const char *special_chars,
309
+              enum AVEscapeMode mode, int flags);
310
+
269 311
 /**
270 312
  * @}
271 313
  */
... ...
@@ -23,6 +23,7 @@
23 23
 #include <string.h>
24 24
 #include <time.h>
25 25
 #include "avassert.h"
26
+#include "avstring.h"
26 27
 #include "bprint.h"
27 28
 #include "common.h"
28 29
 #include "error.h"
... ...
@@ -217,6 +218,50 @@ int av_bprint_finalize(AVBPrint *buf, char **ret_str)
217 217
     return ret;
218 218
 }
219 219
 
220
+#define WHITESPACES " \n\t"
221
+
222
+void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars,
223
+                      enum AVEscapeMode mode, int flags)
224
+{
225
+    const char *src0 = src;
226
+
227
+    if (mode == AV_ESCAPE_MODE_AUTO)
228
+        mode = AV_ESCAPE_MODE_BACKSLASH; /* TODO: implement a heuristic */
229
+
230
+    switch (mode) {
231
+    case AV_ESCAPE_MODE_QUOTE:
232
+        /* enclose the string between '' */
233
+        av_bprint_chars(dstbuf, '\'', 1);
234
+        for (; *src; src++) {
235
+            if (*src == '\'')
236
+                av_bprintf(dstbuf, "'\\''");
237
+            else
238
+                av_bprint_chars(dstbuf, *src, 1);
239
+        }
240
+        av_bprint_chars(dstbuf, '\'', 1);
241
+        break;
242
+
243
+    /* case AV_ESCAPE_MODE_BACKSLASH or unknown mode */
244
+    default:
245
+        /* \-escape characters */
246
+        for (; *src; src++) {
247
+            int is_first_last       = src == src0 || !*(src+1);
248
+            int is_ws               = !!strchr(WHITESPACES, *src);
249
+            int is_strictly_special = special_chars && strchr(special_chars, *src);
250
+            int is_special          =
251
+                is_strictly_special || strchr("'\\", *src) ||
252
+                (is_ws && (flags & AV_ESCAPE_FLAG_WHITESPACE));
253
+
254
+            if (is_strictly_special ||
255
+                (!(flags & AV_ESCAPE_FLAG_STRICT) &&
256
+                 (is_special || (is_ws && is_first_last))))
257
+                av_bprint_chars(dstbuf, '\\', 1);
258
+            av_bprint_chars(dstbuf, *src, 1);
259
+        }
260
+        break;
261
+    }
262
+}
263
+
220 264
 #ifdef TEST
221 265
 
222 266
 #undef printf
... ...
@@ -22,6 +22,7 @@
22 22
 #define AVUTIL_BPRINT_H
23 23
 
24 24
 #include "attributes.h"
25
+#include "avstring.h"
25 26
 
26 27
 /**
27 28
  * Define a structure with extra padding to a fixed size
... ...
@@ -180,4 +181,20 @@ static inline int av_bprint_is_complete(AVBPrint *buf)
180 180
  */
181 181
 int av_bprint_finalize(AVBPrint *buf, char **ret_str);
182 182
 
183
+/**
184
+ * Escape the content in src and append it to dstbuf.
185
+ *
186
+ * @param dstbuf        already inited destination bprint buffer
187
+ * @param src           string containing the text to escape
188
+ * @param special_chars string containing the special characters which
189
+ *                      need to be escaped, can be NULL
190
+ * @param mode          escape mode to employ, see AV_ESCAPE_MODE_* macros.
191
+ *                      Any unknown value for mode will be considered equivalent to
192
+ *                      AV_ESCAPE_MODE_BACKSLASH, but this behaviour can change without
193
+ *                      notice.
194
+ * @param flags         flags which control how to escape, see AV_ESCAPE_FLAG_* macros
195
+ */
196
+void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars,
197
+                      enum AVEscapeMode mode, int flags);
198
+
183 199
 #endif /* AVUTIL_BPRINT_H */
... ...
@@ -75,8 +75,8 @@
75 75
  */
76 76
 
77 77
 #define LIBAVUTIL_VERSION_MAJOR  52
78
-#define LIBAVUTIL_VERSION_MINOR  17
79
-#define LIBAVUTIL_VERSION_MICRO 103
78
+#define LIBAVUTIL_VERSION_MINOR  18
79
+#define LIBAVUTIL_VERSION_MICRO 100
80 80
 
81 81
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
82 82
                                                LIBAVUTIL_VERSION_MINOR, \
... ...
@@ -42,80 +42,16 @@ static void usage(void)
42 42
     printf("\n"
43 43
            "Options:\n"
44 44
            "-e                echo each input line on output\n"
45
+           "-f flag           select an escape flag, can assume the values 'whitespace' and 'strict'\n"
45 46
            "-h                print this help\n"
46 47
            "-i INFILE         set INFILE as input file, stdin if omitted\n"
47 48
            "-l LEVEL          set the number of escaping levels, 1 if omitted\n"
48
-           "-m ESCAPE_MODE    select escape mode between 'full', 'lazy', 'quote', default is 'lazy'\n"
49
+           "-m ESCAPE_MODE    select escape mode between 'auto', 'backslash', 'quote'\n"
49 50
            "-o OUTFILE        set OUTFILE as output file, stdout if omitted\n"
50 51
            "-p PROMPT         set output prompt, is '=> ' by default\n"
51 52
            "-s SPECIAL_CHARS  set the list of special characters\n");
52 53
 }
53 54
 
54
-#define WHITESPACES " \n\t"
55
-
56
-enum EscapeMode {
57
-    ESCAPE_MODE_FULL,
58
-    ESCAPE_MODE_LAZY,
59
-    ESCAPE_MODE_QUOTE,
60
-};
61
-
62
-static int escape(char **dst, const char *src, const char *special_chars,
63
-                  enum EscapeMode mode)
64
-{
65
-    AVBPrint dstbuf;
66
-
67
-    av_bprint_init(&dstbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
68
-
69
-    switch (mode) {
70
-    case ESCAPE_MODE_FULL:
71
-    case ESCAPE_MODE_LAZY:
72
-        /* \-escape characters */
73
-
74
-        if (mode == ESCAPE_MODE_LAZY && strchr(WHITESPACES, *src))
75
-            av_bprintf(&dstbuf, "\\%c", *src++);
76
-
77
-        for (; *src; src++) {
78
-            if ((special_chars && strchr(special_chars, *src)) ||
79
-                strchr("'\\", *src) ||
80
-                (mode == ESCAPE_MODE_FULL && strchr(WHITESPACES, *src)))
81
-                av_bprintf(&dstbuf, "\\%c", *src);
82
-            else
83
-                av_bprint_chars(&dstbuf, *src, 1);
84
-        }
85
-
86
-        if (mode == ESCAPE_MODE_LAZY && strchr(WHITESPACES, dstbuf.str[dstbuf.len-1])) {
87
-            char c = dstbuf.str[dstbuf.len-1];
88
-            dstbuf.str[dstbuf.len-1] = '\\';
89
-            av_bprint_chars(&dstbuf, c, 1);
90
-        }
91
-        break;
92
-
93
-    case ESCAPE_MODE_QUOTE:
94
-        /* enclose between '' the string */
95
-        av_bprint_chars(&dstbuf, '\'', 1);
96
-        for (; *src; src++) {
97
-            if (*src == '\'')
98
-                av_bprintf(&dstbuf, "'\\''");
99
-            else
100
-                av_bprint_chars(&dstbuf, *src, 1);
101
-        }
102
-        av_bprint_chars(&dstbuf, '\'', 1);
103
-        break;
104
-
105
-    default:
106
-        /* unknown escape mode */
107
-        return AVERROR(EINVAL);
108
-    }
109
-
110
-    if (!av_bprint_is_complete(&dstbuf)) {
111
-        av_bprint_finalize(&dstbuf, NULL);
112
-        return AVERROR(ENOMEM);
113
-    } else {
114
-        av_bprint_finalize(&dstbuf, dst);
115
-        return 0;
116
-    }
117
-}
118
-
119 55
 int main(int argc, char **argv)
120 56
 {
121 57
     AVBPrint src;
... ...
@@ -123,13 +59,14 @@ int main(int argc, char **argv)
123 123
     const char *outfilename = NULL, *infilename = NULL;
124 124
     FILE *outfile = NULL, *infile = NULL;
125 125
     const char *prompt = "=> ";
126
-    enum EscapeMode escape_mode = ESCAPE_MODE_LAZY;
126
+    enum AVEscapeMode escape_mode = AV_ESCAPE_MODE_AUTO;
127
+    int escape_flags = 0;
127 128
     int level = 1;
128 129
     int echo = 0;
129 130
     char *special_chars = NULL;
130 131
     int c;
131 132
 
132
-    while ((c = getopt(argc, argv, "ehi:l:o:m:p:s:")) != -1) {
133
+    while ((c = getopt(argc, argv, "ef:hi:l:o:m:p:s:")) != -1) {
133 134
         switch (c) {
134 135
         case 'e':
135 136
             echo = 1;
... ...
@@ -140,6 +77,16 @@ int main(int argc, char **argv)
140 140
         case 'i':
141 141
             infilename = optarg;
142 142
             break;
143
+        case 'f':
144
+            if      (!strcmp(optarg, "whitespace")) escape_flags |= AV_ESCAPE_FLAG_WHITESPACE;
145
+            else if (!strcmp(optarg, "strict"))     escape_flags |= AV_ESCAPE_FLAG_STRICT;
146
+            else {
147
+                av_log(NULL, AV_LOG_ERROR,
148
+                       "Invalid value '%s' for option -f, "
149
+                       "valid arguments are 'whitespace', and 'strict'\n", optarg);
150
+                return 1;
151
+            }
152
+            break;
143 153
         case 'l':
144 154
         {
145 155
             char *tail;
... ...
@@ -154,13 +101,13 @@ int main(int argc, char **argv)
154 154
             break;
155 155
         }
156 156
         case 'm':
157
-            if      (!strcmp(optarg, "full"))  escape_mode = ESCAPE_MODE_FULL;
158
-            else if (!strcmp(optarg, "lazy"))  escape_mode = ESCAPE_MODE_LAZY;
159
-            else if (!strcmp(optarg, "quote")) escape_mode = ESCAPE_MODE_QUOTE;
157
+            if      (!strcmp(optarg, "auto"))      escape_mode = AV_ESCAPE_MODE_AUTO;
158
+            else if (!strcmp(optarg, "backslash")) escape_mode = AV_ESCAPE_MODE_BACKSLASH;
159
+            else if (!strcmp(optarg, "quote"))     escape_mode = AV_ESCAPE_MODE_QUOTE;
160 160
             else {
161 161
                 av_log(NULL, AV_LOG_ERROR,
162 162
                        "Invalid value '%s' for option -m, "
163
-                       "valid arguments are 'full', 'lazy', 'quote'\n", optarg);
163
+                       "valid arguments are 'backslash', and 'quote'\n", optarg);
164 164
                 return 1;
165 165
             }
166 166
             break;
... ...
@@ -219,7 +166,7 @@ int main(int argc, char **argv)
219 219
     /* escape */
220 220
     dst_buf = src_buf;
221 221
     while (level--) {
222
-        if (escape(&dst_buf, src_buf, special_chars, escape_mode) < 0) {
222
+        if (av_escape(&dst_buf, src_buf, special_chars, escape_mode, escape_flags) < 0) {
223 223
             av_log(NULL, AV_LOG_ERROR, "Could not escape string\n");
224 224
             return 1;
225 225
         }