Thursday, 21 May 2015

[Linux] A progress bar implemented in C on Linux and MacOS X

A progress bar implemented in C on Linux and MacOS X

Implementations

  • CR - carriage return (\r)
  • CSI_1 - move up one row and back to column 1, and then erase display from cursor to end of display (\033[1F\033[J)
  • CSI_2 - move up one row and back to column 1, and then erase display from start of display to cursor (\033[1F\033[1J)
  • CSI_3 - move up one row and then erase whole line (\033[1A\033[2K)
CSI Sequence Reference:

Source code


// An implementation of progress bar in C on Linux
//
// Usage:
//    ./prog [type]
//        type: CR, CSI_1, CSI_2, CSI_3
//
// Bar style: 
//  0 - 10% [====                                                 ] 
//  1 -     |====.................................................| 10%
//  2 -     [||||.................................................] 10%
//
// Implementations
//  CR    - carriage return (\r) 
//  CSI_1 - move up one row and back to column 1, and then 
//          erase display from cursor to end of display (\033[1F\033[J)
//  CSI_2 - move up one row and back to column 1, and then 
//          erase display from start of display to cursor (\033[1F\033[1J)
//  CSI_3 - move up one row and then erase whole line (\033[1A\033[2K)
//  
//  Reference for CSI sequence: man console_codes(4)
//

#include 
#include 
//#include  // usleep deprecated
#include    // nanosleep
#include    // strcmp

//#define BAR_STYLE   0
//#define BAR_STYLE   1
#define BAR_STYLE   2

typedef enum
{
    T_CR,
    T_CSI_1,
    T_CSI_2,
    T_CSI_3,
}TYPE_OF_CONTROL;

void usage()
{
    printf("./prog [type]\n");
    printf("    type: CR, CSI_1, CSI_2, CSI_3\n");
}

void prog_bar (int done, int all, int width, TYPE_OF_CONTROL t)
{
    float ration = (done*100)/(float)all;
    int d = (int)(ration * width)/100;
    int i;

#if BAR_STYLE == 0
    printf("%d%% [", (int)ration); 
    for(i = 0; i < d; i++)
        printf("=");

    for(i = d; i < width; i++)
        printf(" ");

    printf("]"); 
#elif BAR_STYLE == 1
    printf("|"); 
    for(i = 0; i < d; i++)
        printf("=");

    for(i = d; i < width; i++)
        printf(".");

    printf("| %d%% ", (int)ration); 
#elif BAR_STYLE == 2
    printf("["); 
    for(i = 0; i < d; i++)
        printf("|");

    for(i = d; i < width; i++)
        printf(".");

    printf("] %d%% ", (int)ration); 
#endif
    switch(t)
    {
        case T_CR: 
            printf("\r");
            fflush(stdout);
            break;
        case T_CSI_1:
            // move cursor up for 1 line and then erase from cursor to end of display 
            printf("\n\033[1F\033[J");
            break;
        case T_CSI_2:
            // move cursor up for 1 line and then erase from start of display to cursor
            printf("\n\033[1F\033[1J");
            break;
        case T_CSI_3:
            // move cursor up for 1 line and then erase whole line. 
            printf("\n\033[1A\033[2K");
            break;
        default:
            break;
    }
}

int main(int argc, char *argv[])
{
    int i = 0;
    const struct timespec t = {0, 100000000}; //100ms
    TYPE_OF_CONTROL ctrl_type = T_CR;

    if(0 == isatty(1))
    {
        printf("Not a terminal.\n");
        fflush(stdout);
    }

    switch (argc)
    {
        case 1:
            ctrl_type = T_CR;
            break;
        case 2:
            if (strcmp(argv[1], "CR") == 0)
            {
                ctrl_type = T_CR;
            }
            else if (strcmp(argv[1], "CSI_1") == 0)
            {
                ctrl_type = T_CSI_1;
            }
            else if (strcmp(argv[1], "CSI_2") == 0)
            {
                ctrl_type = T_CSI_2;
            }
            else if (strcmp(argv[1], "CSI_3") == 0)
            {
                ctrl_type = T_CSI_3;
            }
            else
            {
                usage(); 
                exit(-1);
            }
            break;
        default:
            usage();
            exit(-1);
    }

    for(i = 0; i <= 100; i++)
    { 
        if(isatty(1))
        { 
            prog_bar(i, 100, 100, ctrl_type); 
        } 
        nanosleep(&t, NULL); //0.1 sec
    }

    exit(0);
}

Test

MacOS X

Different terminal emulators
Apple Terminal
  • Works: CR, CSI_1, CSI_2, CSI_3
  • $echo $TERM is xterm-color
iTerm2
  • Works: CR, CSI_1, CSI_2, CSI_3
  • $echo $TERM is xterm-color
xterm
  • Works: CR, CSI_1, CSI_2, CSI_3
  • $echo $TERM is xterm

Ubuntu

virtual terminal (tty)
  • Works: CR, CSI_1, CSI_2, CSI_3
  • $echo $TERM is linux
gnome-terminal
  • Works: CR, CSI_1, CSI_2, CSI_3
  • $echo $TERM is gnome-terminal
xterm
  • Works: CR, CSI_1, CSI_2, CSI_3
  • $echo $TERM is xterm
ssh from iTerm2 on MacOS X
  • Works: CR, CSI_2, CSI_3
  • Doesn’t work: CSI_1
  • $echo $TERM is xterm-color
ssh from xterm on MacOS X
  • Works: CR, CSI_1, CSI_2, CSI_3
  • $echo $TERM is xterm

No comments :

Post a Comment