Tomek Sowiński
Tomek Sowiński

Reputation: 863

How to continue if snprintf stops in the middle of a format specifier?

The snprintf function is perfect when the output buffer may not be big enough to hold the formatted string. But if a call to snprintf stopped due to insufficient buffer length, how to continue printing to another buffer?

char buf1[16] = {0};
char buf2[16] = {0};
int n = snprintf(buf1, sizeof buf1, "Lorem ipsum %d dolor sit", 123456);
assert(strcmp(buf1, "Lorem ipsum 123") == 0);  // ok

// Insert solution here

assert(strcmp(buf2, "456 dolor sit") == 0);  // expected result

P.S. I'm not hellbound on snprintf, any solution confined to the standard C library will do.

Upvotes: 1

Views: 607

Answers (3)

Dmitry Grigoryev
Dmitry Grigoryev

Reputation: 3203

You could create a pipe, use fprintf to write the whole string in there and use a second thread to read from the pipe with as small chunks as you like. This way fprintf function will block and save the state until you dispatch the data.

The implementation obviously won't fit in your template, but if your data is too big to fit in a single memory buffer, you'll have to generate and dispatch data at the same time. Otherwise, just allocate a single sufficiently large buffer like Mooing Duck suggested.

Upvotes: 0

Read documentation of snprintf(3). It returns the total number of needed characters (and you could even invoke it with a NULL buffer of size 0 to get the number of needed characters first). And on Linux with Gnu glibc, you might also use asprintf(3).

So you might code:

 int nbc = snprintf(buf1, sizeof(buf1),
    "Lorem ipsum %d dolor sit", 123456);
 if (nbc<sizeof(buf1)) 
    behappywith(buf1);
 else {
    memset(buf2, 0, sizeof(buf2));
    char* dynbuf = malloc(nbc+1);
    if (!dynbuf) { perror("malloc dynbuf"); exit(EXIT_FAILURE); };
    snprintf(dynbuf, nbc+1,
             "Lorem ipsum %d dolor sit", 123456);
    strncpy(buf2, dynbuf+sizeof(buf1), sizeof(buf2));
    buf2[sizeof(buf2)-1] = '\0';
    free (dynbuf);
    behappywithboth(buf1,buf2);
 }

In practice, it is better to call snprintf with a reasonably large enough buffer -to usually avoid calling malloc then snprintf a second time- (so 16 bytes is not enough, but in your case 64 bytes would have been reasonable), and redo it only in rare cases. If possible, use asprintf

Upvotes: 2

Mooing Duck
Mooing Duck

Reputation: 66951

no. snprintf is stateless, it can't simply "resume" where it stopped. The closest would be to allocate a bigger buffer, print the entire message to that, then strcpy the desired substring to the target buffer.

char buf1[16] = {0};
char buf2[16] = {0};
int n = snprintf(buf1, sizeof(buf1), "Lorem ipsum %d dolor sit", 123456);

if (n > 15) {
    char* t = malloc(n+1);
    if (t) {
        n = snprintf(t, n, "Lorem ipsum %d dolor sit", 123456);
        strncpy(buf2, t+sizeof(buf1)-1, sizeof(buf2)-1);
        free(t);
    }
    //might fail the subsequent assert if malloc failed
}

assert(strcmp(buf1, "Lorem ipsum 123") == 0);  // ok
assert(strcmp(buf2, "456 dolor sit") == 0);  // expected result

Upvotes: 3

Related Questions