Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A better sprintf?

Tags:

c

linux

I am newish to C. Lately I have been working on a system that gets inputs from a bunch of other systems via sockets, MQSeries, and other avenues. Basically my job is to pull these various sources together and put them into a homegrown XML-like format and either store them in the appropriate database or pass them along to some other system. Consequently I am creating formatted strings all the time. Very simplified they look like this:

"<tag>lotsa string data</tag>...repeat ad ad nauseam"

So I started using sprintf and vsprintf and it was easy to work up a routine to concat the format string for sprintf together but the problem is that I have no advanced knowledge of the length of the strings I am receiving so it is difficult know the size of the buffer to allocate. I get strings that can be 25 bytes long or 250k bytes. No telling. So of course I overran a buffer once or twice. That led me to use snprintf which is OK but just passes the buck back to me to realloc buffers when they get truncated and try again.

So I know this isn't the biggest problem in the universe and I can add up the lengths myself but is there a way to make this a little easier in C or should I just stop whining and figure out some kind of recursive function to add up all the string lengths in advance?

like image 811
user3075387 Avatar asked Oct 02 '22 04:10

user3075387


1 Answers

As in many things, someone before you whined and then scratched the itch. asprintf and vasprintf to the rescue. They don't do a whole lot more than than sprintf but they do exactly what is bothering you, namely, figure out how much space is needed and then allocate it, format the string, and pass it back to you.

A couple of caveats:

  • they will return -1 if something went wrong and the length of the string if it didn't

  • You do need to remember to free the string when you are done with it.

  • They aren't part of C or POSIX so your platform may not have them.

  • Since you are using linux they are available but you need to define the _GNU_SOURCE feature macro

A semi-ridiculous example:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>

void doSomething(char *str)
{
    printf("%s\n", str);
}

int main(int argc, char *argv[])
{
    char *opentag = "bold";
    char *str1 = "first string ";
    char *str2 = "second string ";
    char *str3 = "third string ";
    char *closetag = "/bold";
    char *output_string;

    asprintf(&output_string, "<%s>%s%s%s<%s>", opentag, str1, str2, str3, closetag);

    doSomething(output_string);

    free(output_string);

    return(0);
}
like image 80
Duck Avatar answered Oct 23 '22 00:10

Duck