tags:

views:

167

answers:

7

Is there a standard C library function to escape C-strings?

For example, if I had the C string:

char example[] = "first line\nsecond line: \"inner quotes\"";

And I wanted to print

"first line\nsecond line: \"inner quotes\""

Is there a library function that will do that transformation for me? Rolling my own just seems a little silly.

Bonus points if I can give it a length to escape (so it stops before or beyond the \0).

A: 

No standard C function, but not too hard to roll your own

nothing too pretty but :-

void escape_str(char *dest, char *src)
{
     *dest = 0;
     while(*src)
     {
         switch(*src)
         {
             case '\n' : strcat(dest++, "\\n"); break;
             case '\"' : strcat(dest++, "\\\""); break;
             default:  *dest = *src;
         }
         *src++;
         *dest++;
         *dest = 0;                     
     }
}
Keith Nicholas
I revoked the vote I've given when your answer had no code…
Michael Krelin - hacker
ok..............
Keith Nicholas
A: 
while(*src++)
{
  if(*src == '\\' || *src == '\"' || *src == '\'')
    *dest++ = '\\';

  *dest++ = *src++;
}
Pavel Radzivilovsky
this code lacks \r\n\f\a\b\t and other nonprintables handling
Michael Krelin - hacker
don't forget leading and trailing double quotes, checking `isprint()` and producing appropriate escapes for common non-printables and whitespace (`\n`, `\t`) and octal escapes for others (`\0`, `\377`).
rampion
that won't work as the escape characters don't actually make it into the string
Keith Nicholas
not true michael.
Pavel Radzivilovsky
Pavel, how is it not true? consider the "\r\033\n" string, what will become of it? (but the downvote is not mine)
Michael Krelin - hacker
@Michael: what will become of it is this: "\\r\\033\\n" which is exactly what is needed.
Pavel Radzivilovsky
No, I'm talking about the string, consisting of carriage return, escape and line feed, not backslash r, backslash zero three three and backslash n.
Michael Krelin - hacker
+1  A: 

If you were writing GPL stuff you might use http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob;f=lib/quotearg.c;hb=HEAD

pixelbeat
marking this as accepted because it suggested a library function (even if not in the standard load), and didn't roll its own (which really wasn't what I was asking for).
rampion
+1  A: 

You just mentioned that you wanted to print the string.

char example[] = "first line\nsecond line: \"inner quotes\"";
size_t len = strlen(example);
size_t i;

static const char *simple = "\\\'\"";
static const char *complex = "\a\b\f\n\r\t\v";
static const char *complexMap = "abfnrtv";

for (i = 0; i < length; i++)
{
    char *p;
    if (strchr(simple, example[i]))
    {
        putchar('\\');
        putchar(example[i]);
    }
    else if ((p = strchr(complex, example[i]))
    {
        size_t idx = p - complex;
        putchar('\\');
        putchar(complexMap[idx]);
    }
    else if (isprint(example[i]))
    {
        putchar(example[i]);
    }
    else
    {
        printf("\\%03o", example[i]);
    }
}
dreamlax
What about other nonprintables? \033, for instance?
Michael Krelin - hacker
@Michael Krelin: Untested, but now prints unprintables as octal escapes.
dreamlax
You get my vote as the first and only who actually took are of the issue ;-)
Michael Krelin - hacker
+1  A: 

There is no standard C library function for this.

When you use the declaration

char example[] = "first line\nsecond line: \"inner quotes\"";

the escape sequences will be interpreted and replaced by the compiler. You will have to "un-interpret" the characters that C escapes. Here's a quick-n-dirty example:

void print_unescaped(char* pStr, int len) {
    char* ptr = pStr;
    int   i = 0;
    if (pStr == NULL || len == 0) return;
    while (ptr != '\0' && i < len) {
        switch (*ptr) {
            case '\0': printf("\\0");     break;
            case '\a': printf("\\a");     break;
            case '\b': printf("\\b");     break;
            case '\f': printf("\\f");     break;
            case '\n': printf("\\n");     break;
            case '\r': printf("\\r");     break;
            case '\t': printf("\\t");     break;
            case '\v': printf("\\v");     break;
            case '\\': printf("\\\\");    break;
            case '\?': printf("\\\?");    break;
            case '\'': printf("\\\'");    break;
            case '\"': printf("\\\"");    break;
            default  : printf("%c",*ptr); break;
        }
        ++i;
        ++ptr;
    }
}
bta
I'm not sure if you're aware, but `puts` appends a trailing newline.
dreamlax
what the hell is `else` for a `switch`?
Michael Krelin - hacker
Ha! That takes me back to VB days with `Select Case` and `Case Else`.
dreamlax
@dreamlax: Good point, I should have noted that in my answer. I used `gets` because it was cleaner to read than two `putchar` calls (given the space provided) and since I was trying to communicate the concept more than the implementation. I will edit my answer to clarify this. @Michael Krelin: It's latin for 'default', I translated it back though. Thanks for catching that.
bta
A: 
#include <string.h>    
/* int c_quote(const char* src, char* dest, int maxlen)
 *
 * Quotes the string given so that it will be parseable by a c compiler.
 * Return the number of chars copied to the resulting string (including any nulls)
 *
 * if dest is NULL, no copying is performed, but the number of chars required to 
 * copy will be returned.
 *
 * maxlen characters are copied. If maxlen is negative, 
 * strlen is used to find the length of the source string, and the whole string
 * including the NULL-terminator is copied.
 *
 * Note that this function will not null-terminate the string in dest.
 * If the string in src is not null-terminated, or maxlen is specified to not
 * include the whole src, remember to null-terminate dest afterwards.
 *
 */
int c_quote(const char* src, char* dest, int maxlen) {
    int count = 0;
    if(maxlen < 0) {
        maxlen = strlen(src)+1;      /* add 1 for NULL-terminator */
    }

    while(src  && maxlen > 0) {
        switch(*src) {

            /* these normal, printable chars just need a slash appended */
            case '\\':
            case '\"':
            case '\'':
                if(dest) {
                    *dest++ = '\\';
                    *dest++ = *src;
                }
                count += 2;
                break; 

            /* newlines/tabs and unprintable characters need a special code.
             * Use the macro CASE_CHAR defined below.
             * The first arg for the macro is the char to compare to,
             * the 2nd arg is the char to put in the result string, after the '\' */
#define CASE_CHAR(c, d) case c:\
    if(dest) {\
        *dest++ = '\\'; *dest++ = (d);\
        }\
count += 2;\
break;
            /* --------------  */
            CASE_CHAR('\n', 'n');
            CASE_CHAR('\t', 't');
            CASE_CHAR('\b', 'b');
            /*  ------------- */

#undef CASE_CHAR


            /* by default, just copy the char over */
            default:
                if(dest) {
                    *dest++ = *src;
                }
                count++;
        }

        ++src;
        --maxlen;
    }
    return count;
}
gnud
A: 
dreamlax