This is a followup of the last review to comply with meta's rules.In the last, I posted a small library called colorprint and got some helpful pointers from chux in his answer.colorprint.c#include colorprint.h#include <stdio.h>#include <string.h>#define CP_BUFSZ ((3 * 2) + (2 * (CP_FX_END - 1)) + 1)enum validate_error {    VE_OK,    VE_FXTOOLARGE,    VE_TOOMANYFX,    VE_FGTOOLARGE,    VE_BGTOOLARGE,    VE_BADSTRING,    VE_END};const char *validate_error_str[VE_END] = {    [VE_FXTOOLARGE] = (.fx) one or more effect values are out of bounds,    [VE_TOOMANYFX]  = (.fx) too many effects (the \trap\ effect was set),    [VE_FGTOOLARGE] = (.fg) foreground color out of bounds,    [VE_BGTOOLARGE] = (.bg) background color out of bounds,    [VE_BADSTRING]  = (.string) no string provided};static voidapply_effect(char *out, enum fx fx){    char buf[3] = { '0' + (fx - 1), ';' };    strcat(out, buf);}static voidapply_fg(char *out, enum color c){    char buf[4] = { '3', '0' + (c - 1), 'm' };    strcat(out, buf);}static voidapply_bg(char *out, enum color c){    char buf[4] = { '4', '0' + (c - 1), ';' };    strcat(out, buf);}static voidapply_fx(char *out, enum fx *fx){    for(int i = 0; i < CP_FX_END - 1 && fx[i]; i++)        apply_effect(out, fx[i]);}static voidapply_colors(char *out, struct cpoptions *cpo){    if(cpo->bg)        apply_bg(out, cpo->bg);    if(cpo->fg)        apply_fg(out, cpo->fg);}static enum validate_errorvalidate_cpo(struct cpoptions *cpo){    for(int i = 0; i < CP_FX_END - 1 && cpo->fx[i]; i++)        if(cpo->fx[i] >= CP_FX_END)            return VE_FXTOOLARGE;    if(cpo->fx[CP_FX_END - 1] != CP_FX_INVALID)        return VE_TOOMANYFX;    if(cpo->fg >= CP_COLOR_END)        return VE_FGTOOLARGE;    if(cpo->bg >= CP_COLOR_END)        return VE_BGTOOLARGE;    if(cpo->string == NULL)        return VE_BADSTRING;    return VE_OK;}intcprint_default(struct cpoptions *cpo){    char options[CP_BUFSZ] = ;    enum validate_error ve;    if((ve = validate_cpo(cpo)) != VE_OK){        fprintf(stderr, colorprint: An error occurred. Details: E%03u (%s)\n, ve, validate_error_str[ve]);        return -1;    }    apply_fx(options, cpo->fx);    apply_colors(options, cpo);    if(!cpo->fg && !cpo->bg && !cpo->fx[0])        return fputs(cpo->string, stdout);    else        return printf(\033[%s%s\033[0m, options, cpo->string);}intcputs_default(struct cpoptions *cpo){    int ret = cprint_default(cpo);    putchar('\n');    return ret;}colorprint.h#ifndef COLORPRINT_H#define COLORPRINT_H#define CPFUNC(name, type, ...) \    name(&(type){__VA_ARGS__})enum color {    CP_COLOR_INVALID,    CP_COLOR_BLACK,    CP_COLOR_RED,     CP_COLOR_GREEN,    CP_COLOR_YELLOW,    CP_COLOR_BLUE,    CP_COLOR_PURPLE,    CP_COLOR_AQUA,     CP_COLOR_WHITE,    CP_COLOR_END};enum fx {    CP_FX_INVALID,    CP_FX_PLAIN,    CP_FX_BOLD,    CP_FX_DARK,    CP_FX_ITALIC,    CP_FX_UNDERLINE,    CP_FX_LIGHT,    CP_FX_LIGHT2,    CP_FX_REVERSE,    CP_FX_BLINK,    CP_FX_STRIKE,    CP_FX_END};struct cpoptions {    char *string;    enum fx fx[CP_FX_END];    enum color fg;    enum color bg;};int cprint_default(struct cpoptions *cpo);int cputs_default(struct cpoptions *cpo);#define cprint(...) CPFUNC(cprint_default, struct cpoptions, __VA_ARGS__)#define cputs(...) CPFUNC(cputs_default, struct cpoptions, __VA_ARGS__)#if 0Example:     cprint(Hello World, .fg = CP_COLOR_GREEN, .fx = {CP_FX_BOLD, CP_FX_ITALIC, CP_FX_STRIKE});Or...     struct cpoptions cpo = {        .string = Hello World,        .fg = CP_COLOR_GREEN,        .fx = {            CP_FX_BOLD,            CP_FX_ITALIC,            CP_FX_STRIKE        }    };    cprint_default(&cpo);#endif#endifI will say that I am not a fan of the #if 0 style comment suggested by chux in his answer. I personally find this much easier to read:/* * *  Example:  * *      cprint(Hello World, .fg = CP_COLOR_GREEN, .fx = {CP_FX_BOLD, CP_FX_ITALIC, CP_FX_STRIKE}); * *  Or...  * *      struct cpoptions cpo = { *          .string = Hello World, *          .fg = CP_COLOR_GREEN, *          .fx = { *              CP_FX_BOLD, *              CP_FX_ITALIC, *              CP_FX_STRIKE *          } *      }; * *      cprint_default(&cpo); * */Because the * on the left side creates a margin that makes it easy to scan vertically and see where the comment begins and ends.So I will probably change it back at some point.
colorprint - part 2
c;console;formatting;c99