views:

376

answers:

5

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 following is an error obviously, I am sure alot of c programmers out there would give me a quick answer for this, thanks in adavance.

+6  A: 

You want sprintf().

char *sqlAnswers = malloc(SIZE_TO_HOLD_FINAL_STRING);
sprintf(sqlAnswers, "select key from answer WHERE key = %s LIMIT 5;", tmp);
Carl Norum
Always use `snprintf()` for safety.
Greg Hewgill
I can agree with that; +1.
Carl Norum
+18  A: 

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 = 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.

Michael E
+1 for prepared statements.
Noldorin
@Noldorin, hardly a prepared statement,you could still assign '3;--drop table answer' to temp.
Byron Whitlock
Sane being a C99 Standard compliant system! Some C89 implementations offer their own `snprintf` that doesn't behave like C99 describes (return value isn't necessarily the required length).
pmg
@pmg thanks, I've updated the answer to reflect this.
Michael E
+1 for correct answer, prepared statements, buffer safety, and the one upvote you needed to have 2000 rep. You're welcome.
rascher
@Byron Whitlock: the text for the proposed 'prepared statement' was secure against a key of '`3;drop table answer;--`' as well as your version of the attempted (but failed) SQL injection attack.
Jonathan Leffler
+4  A: 

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);
Scott Wales
`asprintf` is a convenient shortcut for the `sprintf(malloc(snprintf(...)))` trick -- I vote for using it and providing a fallback `asprintf` definition if you ever have to deal with a sad, outdated platform lacking it.
ephemient
A: 

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%28VS.80%29.aspx

rerun
It appears that `sprintf_s` does not return the number of bytes required if the buffer is too small; GNU and BSD `snprintf` both do. That was the key behavior I was depending on.
Michael E
A: 

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);
Frank