I am trying to implement the C printf but with instead of %s
, I use {s}
. Instead of %d
... {d}
. Instead of %c
... {c}
(Somewhat like Python/C#/Java's positional string format args but instead of numeric positions there are specifications of the data type). It should also escape additional curly brackets such that {{}
becomes {
. It all works except when I pass in {{
as part of a test. Valgrind informs me that This is probably caused by your program erroneously writing past the end of a heap block and corrupting heap metadata
. This is the code:
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *mr_asprintf(const char *format, ...) {
if (strlen(format) == 0) { return calloc(1, sizeof(char)); }
char buf[strlen(format) * 2];
memset(buf, 0, sizeof(buf));
va_list args;
va_start(args, format);
while (*format != '\0') {
if (*format == '{') {
format++;
if (*format == '{') {
sprintf(buf + strlen(buf), "%c", '{');
} else if (*format == 's') {
sprintf(buf + strlen(buf), "%s", va_arg(args, const char*));
} else if (*format == 'i') {
sprintf(buf + strlen(buf), "%d", va_arg(args, int));
}
if (*format++ == '}') {}
} else if (*format == '}') {
sprintf(buf + strlen(buf), "%c", '}');
} else {
sprintf(buf + strlen(buf), "%c", *format);
}
format++;
}
va_end(args);
return strdup(buf);
}
These are the tests:
printf("'%s'\n", mr_asprintf("Gaius Julius Caesar Augustus Germanicus"));
printf("'%s'\n", mr_asprintf("Nickname: {s}", "Caligula"));
printf("'%s'\n", mr_asprintf("Reign: {i} AD - {i} AD", 37, 41));
printf("'%s'\n",
mr_asprintf("born: {s} {i}, {i} in {s}", "August", 31, 12, "Antium"));
printf("'%s'\n", mr_asprintf("Roman emperor #{i}", 3));
printf("'%s'\n", mr_asprintf("Roman emperor #{i}}", 3));
printf("'%s'\n", mr_asprintf(""));
printf("'%s'\n", mr_asprintf("}bae}ccac {i}bdbb{i}bb{i}}dd b", 394603702, 511917921,
721200806));
printf("'%s'\n", mr_asprintf("{{"));
Expected Output:
'Gaius Julius Caesar Augustus Germanicus'
'Nickname: Caligula'
'Reign: 37 AD - 41 AD'
'born: August 31, 12 in Antium'
'Roman emperor #3'
'Roman emperor #3}'
''
'}bae}ccac 394603702bdbb511917921bb721200806}dd b'
'{'
Actual Output:
'Gaius Julius Caesar Augustus Germanicus'
'Nickname: Caligula'
'Reign: 37 AD - 41 AD'
'born: August 31, 12 in Antium'
'Roman emperor #3'
'Roman emperor #3}'
''
'}bae}ccac 394603702bdbb511917921bb721200806}dd b'
'{Gaius Julius Caesar Augustus Germanicus'
'Gaius Julius Caesar Augustus Germanicus'
'Nickname: Caligula'
'Reign: 37 AD - 41 AD'
'born: August 31, 12 in Antium'
'Roman emperor #3'
'Roman emperor #3}'
''
'}bae}ccac 394603702bdbb511917921bb721200806}dd b'
'{}}'
As you can see, all but the last tests work properly. What could I be doing wrong? Also can you suggest ways to improve my code and cut down on memory leaks?
After a lot of refactoring, what ultimately worked was checking whether the next character is not NUL
, then incrementing the pointer.