A progress bar implemented in C on Linux and MacOS X
- Modified based on Creating a progress bar in C/C++.
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:
- console_codes(4)
$man 4 console_codes
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