Passing printf formatting parameters as variables

Hi,

I haven't programed in C in a few years. I have been doing a lot of shell scripting, I.E. not really programming anything heavy. :o

That said, I have a script that gives hourly usage statistics for our email server. It runs w-a-y to slow as a script for my impatience, and needs to be converted to C. That and some Windows admins have expressed interest in it. Porting to C would be easier to cross compile that a script. :rolleyes:

In the shell script I have an output function where I can pass text color, formatting (4.3f, 6s, etc.) and value.
Shell Code

output{
    printf "\033[$1m%$2\033[0m" $3 
}

Here's what I have in C so far.

int output(int clr,float val){  // Add in the format if possible
   switch (clr) {
      case 0: printf("%c[%dm %2.3f ", 0x1B, 33, val); // print Yellow (Lower than average usage)
        break;
      case 1: printf("%c[%dm %2.3f ", 0x1B, 32, val); // print Green (Medium)
        break;
      case 2: printf("%c[%dm %2.3f ", 0x1B, 31, val); // print Red  (High)
        break;
      case 3: printf("%c[%dm %2.3f ", 0x1B, 0, val); // print White  (All else)
        break;
    }
}

Is there a way to pass the format parameter as a variable? I.E. pearl uses something like this %${fmt}f. Can this be done in C?

There are 5 columns, each can have their own color, width, format(d,s,f...) and value.
Obviously, the colors won't show up here. But you get the idea.

Hour Count APM AMBPM  MBytes
----+-----+---+-----+-------
  00   485   8  0.64   38.45
  01   511   9  0.06    3.51
  02   517   9  0.07    4.43
...

It's not a show stopper, but it would save me from having to create routines for every different format.

Any help would be greatly appreciated.

Thank You

I am just giving small example here, since you said you know programming probably you can experiment and can write your own function, the code you shown doesn't clear color I guess.

akshay@Aix:~/Desktop$ cat color.c
#include <stdio.h>

/* Following header files are not needed stdio.h is enough 
 #include <stdlib.h>
 #include <string.h> */

#define red     "\x1b[31m"
#define green   "\x1b[32m"
#define yell    "\x1b[33m"
#define blue    "\x1b[34m"
#define magneta "\x1b[35m"
#define cyan    "\x1b[36m"
#define reset   "\x1b[0m"

int main () {
	
        // Your value to be printed
	float testvalue = 125.25; 
      
        // precision
	int prec = 5;  
       
        // number of digits after decimal            
	int dig  = 3;   
       
        // 'f' for float           
	char c = 'f';   

        // this holds your format string...("%s...%f") etc          
	char format[10];  

        // Here we are creating your fmt string         
	sprintf(format, "%%s%%%d.%d%c%%s\n", prec, dig,c);
  	
        // Finally printing with different colors
	printf(format,red,testvalue,reset);
	printf(format,blue,testvalue,reset);
  	
}
akshay@Aix:~/Desktop$ gcc color.c 
akshay@Aix:~/Desktop$ ./a.out 
125.250
125.250
1 Like

Thanks! Never thought about breaking it down further. I told ya I haven't programmed in C in years. LOL

I've got an appointment to get to, but I'll try it tonight.

Case 3: should print in white. This project is just getting started, when I get further along I'll have to find a way to get the current color and reset it upon leaving the function.

You say case 3 should print white characters on a white background, but using color code 0 (as given in your sample code) prints BLACK characters on a white background.

Have you considered making your output() function use variable arguments like printf() ? In the example below, output() uses the last character in the given format string argument to determine the type of the value to be retrieved from the argument list and passed on to printf() . It sets the selected color, uses the format given to print the final argument, and then resets the color back to the defaults.

#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char    format[128];    // internal printf format string
int     colors[] = {33/*yellow*/, 32/*green*/, 31/*red*/, 0/*black*/};

int
output(int clr, const char *fmt, ...) {
        va_list         ap;             // variable arguments list
        double          argd;           // double from argument list
        int             argi;           // int from argument list
        const char *    args;           // pointer to string from argument list
        char            cc;             // conversion character from fmt
        int             rc = 0;         // return code

        va_start(ap, fmt);
        cc = fmt[strlen(fmt) - 1];
        snprintf(format, sizeof(format), "\e[%dm%s\e[0m", colors[clr], fmt);
        switch(cc) {
        case 'd':       argi = va_arg(ap, int);
                        printf(format, argi);
                        break;
        case 'f':       argd = va_arg(ap, double);
                        printf(format, (float)argd);
                        break;
        case 's':       args = va_arg(ap, const char *);
                        printf(format, args);
                        break;
        default:        rc = 1;
                        break;
        }
        va_end(ap);
        return(rc);
}

int
main(int argc, char *argv[]) {
        int     APM;    // input APM
        float   AMBPM;  // input AMBPM
        int     Count;  // input count
        FILE    *fp;    // Input file pointer
        char    Hour[3];// input hour
        float   MBytes; // input MBytes
        int     n;      // # items read from input line

        if((fp = fopen(argc > 1 ? argv[1] : "file", "r")) == NULL) {
                snprintf(format, sizeof(format), "%s: fopen(%s) failed",
                        argv[0], argc > 1 ? argv[1] : "file");
                perror(format);
                exit(1);
        }
        printf("Hour Count APM AMBPM  MBytes\n");
        printf("----+-----+---+-----+-------\n");
        while((n = fscanf(fp, "%2s,%d,%d,%f,%f\n",
                Hour, &Count, &APM, &AMBPM, &MBytes)) != EOF) {
                output(0, "%4s", Hour);
                output(3, " %5d", Count);
                output(1, " %3d", APM);
                output(2, " %5.2f", AMBPM);
                output(3, " %7.2f", MBytes);
                printf("\n");
        }
        exit(0);
}

This certainly isn't robust code and just uses different colors for the different output columns (instead of ranges of values within columns), but it may help you see an alternative approach.

With an input file named file containing:

00,485,8,0.64,38.45
01,511,9,0.06,3.51
02,517,9,0.07,4.43

it produces the output:

Hour Count APM AMBPM  MBytes
----+-----+---+-----+-------
  00   485   8  0.64   38.45
  01   511   9  0.06    3.51
  02   517   9  0.07    4.43

Don,

Thanks for the input. Sorry, I should have been more specific. case 3 "does" print white... because I've only tested this on a tty with black bg and white fg. :slight_smile: When it is compiled on the Windoze side, they'll be using a black and white terminal, so it's not that big of a deal, but that's no reason for sloppy code. That said, I have about a half dozen other boxes I work on, each having their own bg / fg color combos. Just so I know what machine I'm working on. I'll have to see what output they yield.

Like I said, I haven't used C for years. This project is just getting the juices flowing. I've been pulling snippets from some of my old code. Heck, I look back at some of it, some 15+ years old, and say. I DID THAT?!?!?

For now I'm taking the KIS (Keep It Simple) approach. Your example is a bit much to digest at the moment. However, reading through it does help!

Windows terminal can do color, but ansi.sys ceased to work after Windows 98, leaving no option but direct terminal system calls.

The ANSI terminal escape sequence you're using for case 3 produce black text on a white background. Having your terminal set to invert foreground and background colors makes it appear as white text on a black background when it is rendered on your screen.

I fully understand and appreciate KISS principles.

Here you have a choice between having an output function to print string arguments, an output function to print integer arguments, and an output function to print floating point arguments; or a single function that can handle all three. If you need to worry about various types of integers (int, long, long long) and floating point (float, double, long double) arguments then the processing gets more complicated. If you want to pass this routine a format string that ends with anything other than a printf() conversion specifier character, then the processing gets considerably more complicated (as in you'd have to actually interpret the entire format string argument instead of just the last character). There is also the question of what needs to be simple? Does it need to be simple for the person writing the output*() function (or functions), or does it need to be simple for the person using those functions? (Of course, in this case both of those might be the same person!)

Programming is full of trade-offs. I just wanted to give you some options to consider.

First I've gotta get it workig on the system I know. Then... I can insert code to check what OS it's running on and make the appropreate discisions.

I do have code to make the color work in windows. That's one of the first things I looked for so I could see if this was worth my while or not.

---------- Post updated at 06:07 PM ---------- Previous update was at 06:00 PM ----------

Don,

Most definetly for the same person... ME! :slight_smile:

Thanks!