Commit 9767ec6b authored by Stefano Sabatini's avatar Stefano Sabatini

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.
parent 38d40ac1
...@@ -15,6 +15,9 @@ libavutil: 2012-10-22 ...@@ -15,6 +15,9 @@ libavutil: 2012-10-22
API changes, most recent first: 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 2013-02-24 - xxxxxx - lavfi 3.41.100 - buffersink.h
Add sample_rates field to AVABufferSinkParams. Add sample_rates field to AVABufferSinkParams.
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "common.h" #include "common.h"
#include "mem.h" #include "mem.h"
#include "avstring.h" #include "avstring.h"
#include "bprint.h"
int av_strstart(const char *str, const char *pfx, const char **ptr) int av_strstart(const char *str, const char *pfx, const char **ptr)
{ {
...@@ -267,6 +268,23 @@ const char *av_dirname(char *path) ...@@ -267,6 +268,23 @@ const char *av_dirname(char *path)
return 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 #ifdef TEST
int main(void) int main(void)
......
...@@ -266,6 +266,48 @@ const char *av_basename(const char *path); ...@@ -266,6 +266,48 @@ const char *av_basename(const char *path);
*/ */
const char *av_dirname(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);
/** /**
* @} * @}
*/ */
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include "avassert.h" #include "avassert.h"
#include "avstring.h"
#include "bprint.h" #include "bprint.h"
#include "common.h" #include "common.h"
#include "error.h" #include "error.h"
...@@ -217,6 +218,50 @@ int av_bprint_finalize(AVBPrint *buf, char **ret_str) ...@@ -217,6 +218,50 @@ int av_bprint_finalize(AVBPrint *buf, char **ret_str)
return ret; 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 #ifdef TEST
#undef printf #undef printf
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#define AVUTIL_BPRINT_H #define AVUTIL_BPRINT_H
#include "attributes.h" #include "attributes.h"
#include "avstring.h"
/** /**
* Define a structure with extra padding to a fixed size * Define a structure with extra padding to a fixed size
...@@ -180,4 +181,20 @@ static inline int av_bprint_is_complete(AVBPrint *buf) ...@@ -180,4 +181,20 @@ static inline int av_bprint_is_complete(AVBPrint *buf)
*/ */
int av_bprint_finalize(AVBPrint *buf, char **ret_str); 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 */ #endif /* AVUTIL_BPRINT_H */
...@@ -75,8 +75,8 @@ ...@@ -75,8 +75,8 @@
*/ */
#define LIBAVUTIL_VERSION_MAJOR 52 #define LIBAVUTIL_VERSION_MAJOR 52
#define LIBAVUTIL_VERSION_MINOR 17 #define LIBAVUTIL_VERSION_MINOR 18
#define LIBAVUTIL_VERSION_MICRO 103 #define LIBAVUTIL_VERSION_MICRO 100
#define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
LIBAVUTIL_VERSION_MINOR, \ LIBAVUTIL_VERSION_MINOR, \
......
...@@ -42,80 +42,16 @@ static void usage(void) ...@@ -42,80 +42,16 @@ static void usage(void)
printf("\n" printf("\n"
"Options:\n" "Options:\n"
"-e echo each input line on output\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" "-h print this help\n"
"-i INFILE set INFILE as input file, stdin if omitted\n" "-i INFILE set INFILE as input file, stdin if omitted\n"
"-l LEVEL set the number of escaping levels, 1 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" "-o OUTFILE set OUTFILE as output file, stdout if omitted\n"
"-p PROMPT set output prompt, is '=> ' by default\n" "-p PROMPT set output prompt, is '=> ' by default\n"
"-s SPECIAL_CHARS set the list of special characters\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) int main(int argc, char **argv)
{ {
AVBPrint src; AVBPrint src;
...@@ -123,13 +59,14 @@ int main(int argc, char **argv) ...@@ -123,13 +59,14 @@ int main(int argc, char **argv)
const char *outfilename = NULL, *infilename = NULL; const char *outfilename = NULL, *infilename = NULL;
FILE *outfile = NULL, *infile = NULL; FILE *outfile = NULL, *infile = NULL;
const char *prompt = "=> "; 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 level = 1;
int echo = 0; int echo = 0;
char *special_chars = NULL; char *special_chars = NULL;
int c; 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) { switch (c) {
case 'e': case 'e':
echo = 1; echo = 1;
...@@ -140,6 +77,16 @@ int main(int argc, char **argv) ...@@ -140,6 +77,16 @@ int main(int argc, char **argv)
case 'i': case 'i':
infilename = optarg; infilename = optarg;
break; 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': case 'l':
{ {
char *tail; char *tail;
...@@ -154,13 +101,13 @@ int main(int argc, char **argv) ...@@ -154,13 +101,13 @@ int main(int argc, char **argv)
break; break;
} }
case 'm': case 'm':
if (!strcmp(optarg, "full")) escape_mode = ESCAPE_MODE_FULL; if (!strcmp(optarg, "auto")) escape_mode = AV_ESCAPE_MODE_AUTO;
else if (!strcmp(optarg, "lazy")) escape_mode = ESCAPE_MODE_LAZY; else if (!strcmp(optarg, "backslash")) escape_mode = AV_ESCAPE_MODE_BACKSLASH;
else if (!strcmp(optarg, "quote")) escape_mode = ESCAPE_MODE_QUOTE; else if (!strcmp(optarg, "quote")) escape_mode = AV_ESCAPE_MODE_QUOTE;
else { else {
av_log(NULL, AV_LOG_ERROR, av_log(NULL, AV_LOG_ERROR,
"Invalid value '%s' for option -m, " "Invalid value '%s' for option -m, "
"valid arguments are 'full', 'lazy', 'quote'\n", optarg); "valid arguments are 'backslash', and 'quote'\n", optarg);
return 1; return 1;
} }
break; break;
...@@ -219,7 +166,7 @@ int main(int argc, char **argv) ...@@ -219,7 +166,7 @@ int main(int argc, char **argv)
/* escape */ /* escape */
dst_buf = src_buf; dst_buf = src_buf;
while (level--) { 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"); av_log(NULL, AV_LOG_ERROR, "Could not escape string\n");
return 1; return 1;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment