gs \ -sOutputFile="BW-$1" \ -sDEVICE=pdfwrite \ -sColorConversionStrategy=Gray \ -dProcessColorModel=/DeviceGray \ -dCompatibilityLevel=1.4 \ -dNOPAUSE \ -dBATCH "$1"
Writes sample.pdf to BW-sample.pdf
gs \ -sOutputFile="BW-$1" \ -sDEVICE=pdfwrite \ -sColorConversionStrategy=Gray \ -dProcessColorModel=/DeviceGray \ -dCompatibilityLevel=1.4 \ -dNOPAUSE \ -dBATCH "$1"
asprintf is the way sprintf should work, but not everyone has it, and so folk either cart around an asprintf implementation, or enjoy getting the tedium of snprintf(NULL, 0, ...) or the worse error-prone tedium of adding up buffer sizes.asprintf and corresponding free() make a much simpler 1-2 lines of use.asprintf use alloca as it's allocator...alloca returns space from the current function stack frame (not scope) and is automatically freed when the function returns.alloca can't allocate enough memory, then who knows what might happen, but on the other hand If asprintf or malloc can't allocate enough memory, how does your program handle it?alloca a buffer of the right size, and then sprintf into that.asprintf.#define asprintfa(PTR, ...) sprintf( (*(PTR)) = alloca(1 + snprintf(NULL, 0, __VA_ARGS__)), __VA_ARGS__)
char* x; asprintfa(&x, "a %s b %d c\n", "<>", -1);
char* x = asprintfa("a %s b %d c\n", "<>", -1);
alloca a buffer of the right size, and the right size is measured with 1 + nprintf(NULL, 0, ...). So we define a helpful macro:#define measure_printf(...) snprintf(NULL, 0, __VA_ARGS__)
alloca and call sprintf(alloc, ...) to populate our alloc'd buffer. usual warning that macro arguments might get evaluated twice, but I don't see any other way around that without using a function call to stash the values, and require the heap or static buffers to hold them.#define asprintfa(...) sprintf(alloca(1+ measure_printf(__VA_ARGS__)), __VA_ARGS__);
alloca pointer, and maybe return via a special GNU macro, like this:#define asprintfa(...) ({ char* x=alloca(1 + measure_printf(__VA_ARGS__)); sprintf(x, __VA_ARGS__); x })
__VA_ARGS__. And relying on alloca is bad enough without insisting on GNU extensions.#define asprintfa(...) ( char* x=alloca(measure_printf(__VA_ARGS__)), sprintf(x, __VA_ARGS__), x )
x still might clash with one of the __VA_ARGS__ and now we are declaring a variable in the middle of a scope.snprintf (or vsprintf ) that returns the buffer instead of the size. (Although perhaps a simple assembler push/pop would have saved the value nicely).static char* a_sprintf(char* v, char* format, ...)
{
if (! v) return NULL; // However alloca is undefined if it fails...
va_list args;
va_start(args, format);
vsprintf(v, format, args);
va_end(args);
return v;
}
#define measure_printf(...) snprintf(NULL, 0, __VA_ARGS__)
#define asprintfa(...) a_sprintf(alloca(measure_printf(__VA_ARGS__)), __VA_ARGS__);
/* sprintf wrapper that returns the buffer address.
Does not check size, intended to be used on a
properly allocated buffer as part of aasprintf */
char* a_sprintf(char* v, char* format, ...)
{
if (! v) return NULL; // However alloca is undefined if it fails...
va_list args;
va_start(args, format);
vsprintf(v, format, args);
va_end(args);
return v;
}
char* x = asprintfa("a %s b %d c\n", "<>", -1);
#define asprintfa(...) ({\
char* __buf__;\
sprintf(__buf__ = alloca(1 + snprintf(NULL, 0, __VA_ARGS__), __VAR_ARGS__);\
__buf__; })
char asprintfa(x, "a %s b %d c\n", "<>", -1);
char x[1 + snprintf(NULL, 0, "a %s b %d c\n", "<>", -1)];
sprintf[x, sprintf("a %s b %d c\n", "<>", -1);
#define asprintfa(name, ...) name[1 + snprintf(NULL, 0, __VA_ARGS__)]; \ sprintf(name, __VA_ARGS__);
freeing.snprintf that you either:snprintf buffer arithmetic wrongfree the memoryasprintfaasprintf for that.sprintf was designed to get wrong, and snprint was designed to also get the arithmetic wrong.asprintf (now tested):int asprintf(char **v, const char* format, ...)
{
int len = 0;
if (! v) return -1;
va_list args;
va_start(args, format);
len = vsnprintf(NULL, 0, format, args);
va_end(args);
va_start(args, format);
*v = malloc(len + 1);
if (! *v) return -1;
len = vsnprintf(*v, len + 1, format, args);
va_end(args);
return len;
}