I wrote this a long time ago. I came back to it and decided to clean it up a little.
It uses the preprocessor and C99 anon structs
to make a flexible python-like API for calling its functions.
colorprint.c
#include <stdio.h>
#include <string.h>
#include "colorprint.h"
static const char *numch = "0123456789";
static void
apply_effect(char *out, enum fx fx)
{/*{{{*/
char buf[3] = { numch[fx - 1], ';' };
strcat(out, buf);
}/*}}}*/
static void
apply_fg(char *out, enum color c)
{/*{{{*/
char buf[4] = { '3', numch[c - 1], 'm' };
strcat(out, buf);
}/*}}}*/
static void
apply_bg(char *out, enum color c)
{/*{{{*/
char buf[4] = { '4', numch[c - 1], ';' };
strcat(out, buf);
}/*}}}*/
static void
apply_fx(char *out, enum fx *fx)
{/*{{{*/
for(int i = 0; fx[i]; i++)
apply_effect(out, fx[i]);
}/*}}}*/
static void
apply_colors(char *out, struct cpoptions *cpo)
{/*{{{*/
if(cpo->bg)
apply_bg(out, cpo->bg);
if(cpo->fg)
apply_fg(out, cpo->fg);
}/*}}}*/
void
_cprint(struct cpoptions *cpo)
{/*{{{*/
char options[30] = "";
apply_fx(options, cpo->fx);
apply_colors(options, cpo);
if(!cpo->fg && !cpo->bg && !cpo->fx[0])
fputs(cpo->string, stdout);
else
printf("\e[%s%s\e[0m", options, cpo->string);
}/*}}}*/
void
_cputs(struct cpoptions *cpo)
{/*{{{*/
_cprint(cpo);
putchar('\n');
}/*}}}*/
colorprint.h
#define func(name, type, ...) \
name(&(type){.header = 0, __VA_ARGS__})
enum color {
color_invalid,
color_black,
color_red,
color_green,
color_yellow,
color_blue,
color_purple,
color_aqua,
color_white
};
enum fx {
fx_invalid,
fx_plain,
fx_bold,
fx_dark,
fx_italic,
fx_underline,
fx_light,
fx_light2,
fx_reverse,
fx_blink,
fx_strike
};
struct cpoptions {
char header;
char *string;
enum fx fx[10];
enum color fg;
enum color bg;
};
void _cprint(struct cpoptions *cpo);
void _cputs(struct cpoptions *cpo);
#define cprint(...) func(_cprint, struct cpoptions, __VA_ARGS__)
#define cputs(...) func(_cputs, struct cpoptions, __VA_ARGS__)
/* Example:
* cprint("Hello World", .fg = color_green, .fx = {fx_bold, fx_italic, fx_strike});
* Or:
* struct cpoptions cpo = {
* .string = "Hello World",
* .fg = color_green,
* .fx = {
* fx_bold,
* fx_italic,
* fx_strike
* }
* };
* _cprint(&cpo);
*/
Its usage ranges in complexity from
cputs("Hello World"); // prints "Hello World\n"
to
cputs( .string = "Hello World",
.fg = color_green,
.bg = color_red,
.fx = {
fx_bold,
fx_strike,
fx_italic
}
); // prints "\e[1;9;3;41;32mHello World\e[0m\n", which appears as "Hello World" with a red background, green foreground, with bold+strikethrough+italic effects
In my opinion, this method of function calling is far more type-safe and versatile than the horrid va_args
. It's also nice because it allows colorful output without the overhead of ncurses
or termcap
.