diff --git a/doc/APIchanges b/doc/APIchanges index 8535d9fe36..a1e81808e7 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -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. diff --git a/libavutil/avstring.c b/libavutil/avstring.c index 9ad1e12711..45f8d78172 100644 --- a/libavutil/avstring.c +++ b/libavutil/avstring.c @@ -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) diff --git a/libavutil/avstring.h b/libavutil/avstring.h index 3896b5f2bb..5b078f15ae 100644 --- a/libavutil/avstring.h +++ b/libavutil/avstring.h @@ -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); + /** * @} */ diff --git a/libavutil/bprint.c b/libavutil/bprint.c index 4684ab4979..fd7611aaed 100644 --- a/libavutil/bprint.c +++ b/libavutil/bprint.c @@ -23,6 +23,7 @@ #include #include #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 diff --git a/libavutil/bprint.h b/libavutil/bprint.h index f3915fe7b1..df78916f4a 100644 --- a/libavutil/bprint.h +++ b/libavutil/bprint.h @@ -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 */ diff --git a/libavutil/version.h b/libavutil/version.h index cf65fff5f3..4cd82268db 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -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, \ diff --git a/tools/ffescape.c b/tools/ffescape.c index d777fe4572..0530d28c6d 100644 --- a/tools/ffescape.c +++ b/tools/ffescape.c @@ -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; }