Frank
Frank

Reputation: 3121

how to store printf into a variable?

I want to store a formatted string using something similar to what printf does in C.

char *tmp = (char *)sqlite3_column_text(selectstmt, 2);
const char *sqlAnswers = printf("select key from answer WHERE key = %s LIMIT 5;", tmp);

The latter is an error obviously.

Upvotes: 26

Views: 63292

Answers (6)

Rodrigo
Rodrigo

Reputation: 12683

The Michael Ekstrand code is good, but you will need to copy and paste it various times. I use this code in one function

char *storePrintf (const char *fmt, ...)
{
    va_list arg;
    va_start(arg, fmt);
    size_t sz = snprintf(NULL, 0, fmt, arg);
    char *buf = (char *)malloc(sz + 1);
    vsprintf(buf, fmt, arg);
    va_end (arg);
    return buf;
}

Does it has problem with buffer overflow? Until now I don't have problem with it.

Edit.

Ok, I have a problem because I am working with Arduino. It use memory and don't drop it, so you need to delete it after the use.

Upvotes: 2

Michael Ekstrand
Michael Ekstrand

Reputation: 29090

You can do it with sprintf, but not alone (safely). On a sane system, use snprintf twice, once to find out the size to use and the second time to actually do it. This depends on snprintf returning the number of characters needed when it runs out of room. Linux, BSD, and C99-compatible systems do this; Windows typically does not. In the latter case, you'll need to allocate an initial buffer and allocate a bigger one if snprintf fails (in a loop until snprintf succeeds). But on C99, the following will work:

char *buf;
size_t sz;
sz = snprintf(NULL, 0, "select key from answer WHERE key = %s LIMIT 5;", tmp);
buf = (char *)malloc(sz + 1); /* make sure you check for != NULL in real code */
snprintf(buf, sz+1, "select key from answer WHERE key = %s LIMIT 5;", tmp);

However, for building SQL, it's far better to use prepared statements. They avoid SQL injection vulnerabilities (and frequently the need for sprintf). With them, you would prepare the statement "select key from answer where key = ? limit 5;", and then execute it with the parameter tmp. The SQL engine puts in the string and removes the need to make sure it's properly escaped first.

Upvotes: 44

Frank
Frank

Reputation: 3121

I am actually using sqlite3_bind_text to input my wildcard instead of generating through sprintf:

const char *sql1 = "select id, repA, key from iphone_reponse WHERE question_id = ?;";
sqlite3_stmt *selectstmt1;
if(sqlite3_prepare_v2(database, sql1, -1, &selectstmt1, NULL) == SQLITE_OK) {
    sqlite3_bind_text(selectstmt1, 1, [questionObj.key UTF8String], -1, SQLITE_TRANSIENT);

Upvotes: 2

rerun
rerun

Reputation: 25495

On windows you can use sprintf_s which adds buffer overflow protection like Michael E was saying.

http://msdn.microsoft.com/en-us/library/ce3zzk1k(VS.80).aspx

Upvotes: 0

Scott Wales
Scott Wales

Reputation: 11696

If you're using gnu or BSD libc you may be able to use asprintf, which allocates a buffer of the correct size automatically.

#define _GNU_SOURCE
#include <stdio.h>
// ...
char *sqlAnswers = NULL;
int length = asprintf(&sqlAnswers,"select key from answer WHERE key = %s LIMIT 5;", tmp);
free(sqlAnswers);

Upvotes: 8

Carl Norum
Carl Norum

Reputation: 224864

You want sprintf().

char *sqlAnswers = malloc(SIZE_TO_HOLD_FINAL_STRING);
sprintf(sqlAnswers, "select key from answer WHERE key = %s LIMIT 5;", tmp);

Upvotes: 9

Related Questions