Using ANSI color codes in gcc compiled program

I have put some yellow color codes and works well.

I call the funstion using

print_usage(stderr, 0);

I would like to know if there is any way, to store the ansi color codes in variables and then call them inside fprintf.
Or have a format followed by the strings I want to output.

void print_usage(FILE* stream, int exit_code) {

  fprintf (stream,
      "\033[1;33mUSAGE\e[0m\n"
      "   %s options\n\n", program_name);

  fprintf (stream,
    "\033[1;33mACTIONS\e[0m\n"
    "    --cmod=FILE   Velocity model file (required)\n"
    "    --vdx=DV      Interval in x direction in velocity file\n"
    "    --nx=NUM      Interval calculated with vdx = Lx / Nx (default: 20)\n"
    "    --vdz=DV      Interval in z direction in velocity file or\n"
    "    --nz=NUM      Interval calculated with vdz = Lz / Nz (default: 20)\n"
    "    --vdi=DV      Interval along interface\n"
    "    --ni=NUM      Interval calculated with vdi = Li / Ni (default: 20)\n"
    "    --vel=FILE    File with P wave velocity in 'x z v' format\n"
    "    --velp=FILE   File with P wave velocity in 'x z v' format\n"
    "    --vels=FILE   File with S wave velocity in 'x z v' format\n"
    "    --lay=ON/OFF  If --lay=ON, output files are in 'x z v l' format\n"
    "    --intf=       Output file for interfaces in 'x z' format, separated by '>'\n"
    "    -h  --help    Display this usage information\n"
    "    -o  --output=FILE  Write output to file\n"
    "    --vb=LEVEL    Print verbose messages\n"
    "\n");

  exit (exit_code);

}

---------- Post updated at 11:49 PM ---------- Previous update was at 09:56 PM ----------

I have now coded the following which is working well.

How can I simplify this code. For example, it would be good to construct a format, followed by the things I want to print.

Something similar to the following

frmt_sv = "nRd%snWh=uGn%srcl"
frmt_desc="    %s\n"

fprintf (stream, frmt_sv, "--nx", "NUM");
fprintf (stream, frmt_desc, "interval calculated with vdx = Lx / Nx (default: 20)");

The code is currently as follows

void print_usage(FILE* stream, int exit_code) {

  const char* progname;
  const char* bYl;
  const char* nRd;
  const char* uGn;
  const char* nWh;
  const char* rcl;
  char* frArgm_sv;

  progname = "getveltest";

  bYl = "\e[1;33m";
  nRd = "\e[0;31m";
  uGn = "\e[4;32m";
  nWh = "\e[0;37m";
  rcl = "\e[0m";

  fprintf (stream,"%s%s%s\n",bYl,"USAGE",rcl);
  fprintf (stream,"    %s%s %s%s%s\n\n",nRd,progname,uGn,"ACTIONS",rcl);

  fprintf (stream,"%s%s%s\n",bYl,"ACTIONS",rcl);
  fprintf (stream,"    %s%s%s%s%s%s%s",nRd,"--cmod",nWh,"=",uGn,"FILE",rcl);
  fprintf (stream,"  %s\n"," sound speed model (mandatory)");

  fprintf (stream,"    %s%s%s%s%s%s%s",nRd,"--vdx",nWh,"=",uGn,"DX",rcl);
  fprintf (stream,"  %s\n","    interval in x direction in velocity file");

  fprintf (stream,"    %s%s%s%s%s%s%s",nRd,"--nx",nWh,"=",uGn,"NUM",rcl);
  fprintf (stream,"  %s\n","    interval calculated with vdx = Lx / Nx (default: 20)");

  fprintf (stream,"    %s%s%s%s%s%s%s",nRd,"--vdz",nWh,"=",uGn,"DZ",rcl);
  fprintf (stream,"  %s\n","    interval in z direction in velocity file");

  fprintf (stream,"    %s%s%s%s%s%s%s",nRd,"--nz",nWh,"=",uGn,"NUM",rcl);
  fprintf (stream,"  %s\n","    interval calculated with vdz = Lz / Nz (default: 20)");

  fprintf (stream,"    %s%s%s%s%s%s%s",nRd,"--vdi",nWh,"=",uGn,"DI",rcl);
  fprintf (stream,"  %s\n","    interval along interface");

  fprintf (stream,"    %s%s%s%s%s%s%s",nRd,"--ni",nWh,"=",uGn,"NUM",rcl);
  fprintf (stream,"  %s\n","    interval calculated with vdi = Li / Ni (default: 20)");

  fprintf (stream,"    %s%s%s%s%s%s%s",nRd,"--vel",nWh,"=",uGn,"FILE",rcl);
  fprintf (stream,"  %s\n","  file with P wave velocity in 'x z v' format");

  fprintf (stream,"    %s%s%s%s%s%s%s",nRd,"--velp",nWh,"=",uGn,"FILE",rcl);
  fprintf (stream,"  %s\n"," file with P wave velocity in 'x z v' format");

  fprintf (stream,"    %s%s%s%s%s%s%s",nRd,"--vels",nWh,"=",uGn,"FILE",rcl);
  fprintf (stream,"  %s\n"," file with S wave velocity in 'x z v' format");

  fprintf (stream,"    %s%s%s%s%s%s%s",nRd,"--lay",nWh,"=",uGn,"ON/OFF",rcl);
  fprintf (stream,"  %s\n","if --lay=ON, output files are in 'x z v l' format");

  fprintf (stream,"    %s%s%s%s%s%s%s",nRd,"--intf",nWh,"=",uGn,"FILE",rcl);
  fprintf (stream,"  %s\n"," output file for interfaces in 'x z' format, separated by '>'");

  fprintf (stream,"    %s%s%s%s%s%s%s",nRd,"--vb",nWh,"=",uGn,"LEVEL",rcl);
  fprintf (stream,"  %s\n","  print verbose messages");

  fprintf (stream,"    %s%s%s%s%s%s%s",nRd,"-u",nWh," ",nRd,"--usage",rcl);
  fprintf (stream,"  %s\n","  displays usage");

  fprintf (stream,"    %s%s%s%s%s%s%s",nRd,"-h",nWh," ",nRd,"--help",rcl);
  fprintf (stream,"  %s\n","   display this usage information");

  exit (exit_code);

}

I wrote some routines for printing ANSI colors inside printf like

 afprintf(stdout, "This word is ~&110RED~&R!\n");

It's just a translator built on top of standard printf, so should handle any of the usual % sequences. I do not claim that it's ideal or bug-free. Colors cannot be in arguments, only in the command string.

Source code:

/* ansi.h */
#ifndef __ANSI_H__
#define __ANSI_H__

#include <stdio.h>
#include <stdarg.h>

/**
 *      Simple ANSI sequences in C.
 *      example:  aprintf("This word is ~&110RED~&R!\n");
 *
 *      Commands:
 *      ~&R     Reset to defaults
 *      ~&C     Clear screen
 *      ~&111   Sets colors with attr,fg,bg.
 *              attr 0 is normal, attr 1 is bold, others are terminal-dependent.
 *              Colors are
 *              1=red 2=green 4=blue 3=yellow 5=mauve 6=cyan 7=grey/white
 */

int vafprintf(FILE *fp, const char *cmdstr, va_list ap);
int vasprintf(char *buf, const char *cmdstr, va_list ap);
int asprintf(char *buf, const char *cmdstr, ...);
int afprintf(FILE *fp, const char *cmdstr, ...);
int aprintf(const char *cmdstr, ...);
int aprintf(const char *cmdstr, ...);

#endif/*__ANSI_H__*/
/* ansi.c */

#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>

#include "ansi.h"

#define ESC     "\x1b["

#if 0
int main(void){ afprintf(stdout, "This word is ~&110RED~&R!\n"); }
#endif

// Convert ~&R into \x1b[0m, etc
static void translate_ansi(char *lcmdstr, const char *cmdstr)
{
        int opos=0;

        while(*cmdstr)
        {
                const char *esc=NULL;
                int a,f,b;

                if(cmdstr[0] == '~')
                if(cmdstr[1] == '&')
                {
                        switch(toupper(cmdstr[2]))
                        {
                        // Can add other single-char sequences here
                        case 'R':       esc=ESC"0m";    break;
                        case 'C':       esc=ESC"2J";    break;
                        default:                        break;
                        }

                        if(esc)
                        {
                                opos+=sprintf(lcmdstr+opos, "%s", esc);
                                cmdstr+=3;
                                continue;
                        }

                        if(sscanf(cmdstr, "~&%01d%01d%01d", &a, &f, &b) == 3)
                        {
                                opos+=sprintf(lcmdstr+opos,
                                        ESC "%d;3%d;4%dm",
                                        a, f%8, b%8);
                                cmdstr += 5;
                                continue;
                        }
                }

                lcmdstr[opos++]=*(cmdstr++);
        }

        lcmdstr[opos]='\0';
}

int vafprintf(FILE *fp, const char *cmdstr, va_list ap)
{
        char lcmdstr[4096];
        translate_ansi(lcmdstr, cmdstr);
        return(vfprintf(fp, lcmdstr, ap));
}


int vasprintf(char *buf, const char *cmdstr, va_list ap)
{
        char lcmdstr[16384];
        translate_ansi(lcmdstr, cmdstr);
        return(vsprintf(buf, lcmdstr, ap));
}

int asprintf(char *buf, const char *cmdstr, ...)
{
        int ret;
        va_list ap;
        va_start(ap, cmdstr);
                ret=vasprintf(buf, cmdstr, ap);
        va_end(ap);

        return(ret);
}

int afprintf(FILE *fp, const char *cmdstr, ...)
{
        char buf[16384];
        int ret;
        va_list ap;

        va_start(ap, cmdstr);
                ret=vafprintf(fp, cmdstr, ap);
        va_end(ap);

        return(ret);
}

int aprintf(const char *cmdstr, ...)
{
        int ret;
        va_list ap;

        va_start(ap, cmdstr);
                ret=vafprintf(stdout, cmdstr, ap);
        va_end(ap);

        return(ret);
}
1 Like

I had coded the following using the letters s, v and d.

s stands for string argument
v stands for value
d stands for description

If I have a number NUM in front of the letter, it means that there are NUM arguments of that type.

Example

prUsge4_sv ( stdout, "--cmod", "FILE" );

The 4 means a shift to the right by four characters

prints

    --cmod=FILE

Another example

prUsge4_2sv ( stdout, "--bracd", "--mindacc", "DIST" );

gives

    --bracd, --mindacc=DIST

I would like to use the following function calls, but try to have the colors in each function more managable using
a method where I can declare what color I want to use when calling fprintf. Or something similar to what you have done.

The code is as follows:

#ifndef clorfncs_hh
#define clorfncs_hh

#include <stdlib.h>

void prNwln (
  FILE*  stream ) {

  fprintf ( stream, "\n", "" );

}

void prTitl (
  FILE*  stream,
  const char*  titl ) {

  const char* frTitl;
  frTitl = "\e[1;33m%s\e[0m\n";
  fprintf ( stream, frTitl, titl );

}

void prTitl4 (
  FILE*  stream,
  const char*  titl ) {

  const char* frTitl;
  frTitl = "\e[1;33m%s\e[0m\n";
  fprintf ( stream, frTitl, titl );

}

void prUsge_sv (
  FILE*  stream,
  const char*  prog,
  const char*  value ) {

  const char* frUsge_sv;
  frUsge_sv = "\e[0;31m%s\e[0m \e[4;32m%s\e[0m\n";
  fprintf ( stream, frUsge_sv, prog, value );

}

void prUsge4_sv (
  FILE*  stream,
  const char*  prog,
  const char*  value ) {

  const char* frUsge4_sv;
  frUsge4_sv = "    \e[0;31m%s\e[0m \e[4;32m%s\e[0m\n";
  fprintf ( stream, frUsge4_sv, prog, value );

}

void prUsge_s2v (
  FILE*  stream,
  const char*  prog,
  const char*  value1,
  const char*  value2 ) {

  const char* frUsge_s2v;
  frUsge_s2v = "\e[0;31m%s\e[0m \e[4;32m%s\e[0m \e[4;32m%s\e[0m\n";
  fprintf ( stream, frUsge_s2v, prog, value1, value2 );

}

void prUsge4_s2v (
  FILE*  stream,
  const char*  prog,
  const char*  value1,
  const char*  value2 ) {

  const char* frUsge4_s2v;
  frUsge4_s2v = "    \e[0;31m%s\e[0m \e[4;32m%s\e[0m \e[4;32m%s\e[0m\n";
  fprintf ( stream, frUsge4_s2v, prog, value1, value2 );

}

void prArgm_sv (
  FILE*  stream,
  const char*  actn,
  const char*  value ) {

  const char* frArgm_sv;
  frArgm_sv = "\e[0;31m%s\e[0m\e[0;37m=\e[4;32m%s\e[0m\n";
  fprintf ( stream, frArgm_sv, actn, value );

}

void prArgm4_sv (
  FILE*  stream,
  const char*  actn,
  const char*  value ) {

  const char* frArgm4_sv;
  frArgm4_sv = "    \e[0;31m%s\e[0m\e[0;37m=\e[4;32m%s\e[0m\n";
  fprintf ( stream, frArgm4_sv, actn, value );

}

void prArgm_2s (
  FILE*  stream,
  const char*  actn1,
  const char*  actn2 ) {

  const char* frArgm_2s;
  frArgm_2s = "\e[0;31m%s\e[0;37m, \e[0;31m%s\e[0m\n";
  fprintf ( stream, frArgm_2s, actn1, actn2 );

}

void prArgm4_2s (
  FILE*  stream,
  const char*  actn1,
  const char*  actn2 ) {

  const char* frArgm4_2s;
  frArgm4_2s = "    \e[0;31m%s\e[0;37m, \e[0;31m%s\e[0m\n";
  fprintf ( stream, frArgm4_2s, actn1, actn2 );

}

void prArgm_2sd (
  FILE*  stream,
  const char*  actn1,
  const char*  actn2,
  const char*  desc ) {

  const char* frArgm_2sd;
  frArgm_2sd = "\e[0;31m%s\e[0;37m, \e[0;31m%s\e[0m  \e[0;37m%s\e[0m\n";
  fprintf ( stream, frArgm_2sd, actn1, actn2, desc );

}

void prArgm4_2sd (
  FILE*  stream,
  const char*  actn1,
  const char*  actn2,
  const char*  desc ) {

  const char* frArgm4_2sd;
  frArgm4_2sd = "    \e[0;31m%s\e[0;37m, \e[0;31m%s\e[0m  \e[0;37m%s\e[0m\n";
  fprintf ( stream, frArgm4_2sd, actn1, actn2, desc );

}

void prArgm_svd (
  FILE*  stream,
  const char*  actn,
  const char*  value,
  const char*  desc ) {

  const char* frArgm_svd;
  frArgm_svd = "\e[0;31m%s\e[0m\e[0;37m=\e[4;32m%s\e[0m  \e[0;37m%s\e[0m\n";
  fprintf ( stream, frArgm_svd, actn, value, desc );

}

void prArgm4_svd (
  FILE*  stream,
  const char*  actn,
  const char*  value,
  const char*  desc ) {

  const char* frArgm4_svd;
  frArgm4_svd = "    \e[0;31m%s\e[0m\e[0;37m=\e[4;32m%s\e[0m  \e[0;37m%s\e[0m\n";
  fprintf ( stream, frArgm4_svd, actn, value, desc );

}

void prArgm8_svd (
  FILE*  stream,
  const char*  actn,
  const char*  value,
  const char*  desc ) {

  const char* frArgm8_svd;
  frArgm8_svd = "        \e[0;31m%s\e[0m\e[0;37m=\e[4;32m%s\e[0m  \e[0;37m%s\e[0m\n";
  fprintf ( stream, frArgm8_svd, actn, value, desc );

}

void prDesc (
  FILE*  stream,
  const char*  desc ) {

  const char* frDesc;
  frDesc = "\e[0;37m%s\e[0m\n";
  fprintf ( stream, frDesc, desc );

}

void prDesc4 (
  FILE*  stream,
  const char*  desc ) {

  const char* frDesc4;
  frDesc4 = "    \e[0;37m%s\e[0m\n";
  fprintf ( stream, frDesc4, desc );

}

void prUsgeMscl (
  FILE*  stream ) {

  const char* desc;

  prNwln ( stream );
  prTitl ( stream, "MISCELLANEOUS" );

  desc="print verbose messages";
  prArgm8_svd ( stream, "--vb", "LEVEL", desc );

  desc="   displays usage";
  prArgm4_2sd ( stream, "-u", "--usage", desc );

  desc="   prints some examples using getveltest";
  prArgm4_2sd ( stream, "-e", "--examples", desc );

  desc="    display this usage information";
  prArgm4_2sd ( stream, "-h", "--help", desc );

}

#endif

Do you think I should not use something like

void prDesc4 (
  FILE*  stream,
  const char*  desc ) {

  const char* frDesc4;
  frDesc4 = "    \e[0;37m%s\e[0m\n";
  fprintf ( stream, frDesc4, desc );

}

I really don't know what you want anymore. You don't want to pass them into the function because it becomes so many arguments, but you don't want to embed them in the printf command string either. One way or another, you need to tell it which parts you want to be what color and intensity.

I'd just use the aprintf I gave you instead of making 20 very slightly-different printing functions:

afprintf(stdout, "~&110--bracd~&R, ~&110--mindacc~&R=~&120DIST~&R\n");
// Or perhaps
afprintf(stdout, "~&110%s~&R, ~&110--mindacc~&R=~&120%s~&R\n", "--bradc", "DIST");

You could make macros for various colors, since you can stick strings together like

#define RED "~&110"
#define RESET "~&R"

afprintf(stdout, "This word is " RED "red!" RESET "\n");

Short of building an HTML parser in your code so you can do [RED]stuff[/RED] I don't know how to make it more convenient for you.

I agree with you. Instead of calling fprintf, I will call your routines inside the functions I have created. Took me some time to decide how to approach things. I do like your method.

---------- Post updated at 11:11 AM ---------- Previous update was at 11:05 AM ----------

I wrote the different functions so that they take some standard color sequence instead of specifying the color everytime. Internally I will set the color sequence and then call your function to print the actual colored output.

That's the kind of thing I'd make macros for. It helps you can stack strings like "This" "is" "a" "string" and the compiler will put it together like "Thisisastring", you can assemble your command string out of string literals passed into a macro.

#define RESET "~&R"
#define COLORWORD(COLOR, STRING) afprintf(stdout, "This word is " COLOR "%s!" RESET "\n", STRING)

// Note the "~&110" here must be a literal "string" in double quotes, and never a pointer or var.
// "red", etc on the other hand can be whatever you want.
COLORWORD("~&110", "red");
COLORWORD("~&120", "green");
COLORWORD("~&140", "blue");