/** file cwrap.c - line wrapping demo with color escapes escape codes: "###..." sequences of (n) hash display as (n-1) hashes "#n" forced linebreak "#=" reset ansi color "#g" ansi brightly black (gray) "#R" ansi bold red "#G" ansi bold green etc... unrecognized char prints a single hash also, hash at end of line won't crash or infinite-loop (anymore, thank goodness!) todo: multiple columns like a newspaper or magazine, each wrapped individually. **/ #include #include int COLUMNS = 75; /* terminal character columns, not paragraphs */ #define DO_FILES #define pass(x) do { printf("%s\n", #x); } while(0) #define LEVEL 100 #define if_debug(x) if((x) && (x) < LEVEL) #define dummy (-1) #define RESET "\x1b[0m" #define GRAY "\x1b[1;30m" #define RED "\x1b[1;31m" #define GREEN "\x1b[1;32m" #define YELLOW "\x1b[1;33m" #define BLUE "\x1b[1;34m" #define MAGENTA "\x1b[1;35m" #define CYAN "\x1b[1;36m" #define WHITE "\x1b[1;37m" typedef enum { reset=0, gray, red, green, yellow, blue, magenta, cyan, white, ansimax=white } ansi_t; const char *ANSI[] = { RESET, GRAY, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; //----------------------------------------------------------------------- int runlen(char *msg, int width, int *run) { int i, bump, stop; int pos, notrun; char prev, c; if(!run) run = ¬run; pos = *run = 0; prev = c = '?'; bump = stop = 0; /* !! looping until the PREVIOUS char in string prevents "...#" bug !! */ /* delayed char kinda sucks reading from a terminal but this is string */ for(i = 0; prev != '\0'; i++) { prev = c; c = msg[i]; if(c == '#' && prev != c) continue; if(prev != '#' || prev == c) pos++; if(!c) bump = i; if(c == ' ' && prev != c) bump = i; /* forced linebreak, i+1 to include the 'n' too, our "previous" */ /* trick wouldn't work without an extra char of context. */ if(c == 'n' && prev == '#') bump = i+1; #ifdef DO_FILES if(c == '\n') bump = i+1; #endif /* OR EQUAL!, seldom seen fencepost error otherwise */ if(pos <= width) { stop = bump; *run = pos; } else break; /* this hasn't proved neccessary but should've been disasterous... */ prev = c; } return stop; } //------------------------------------------------------------------------- int printchar(char *msg, int i) { char prev, c, *t; c = msg[i]; if(0 < i) prev = msg[i-1]; else prev = ' '; if(!c) return 0; #ifdef DO_FILES if(c == '\n') return 1; #endif if(c == '#' && prev != c) return 0; if(prev != '#') putc(c, stdout); else switch(c) { case 'n': return 1; break; case '=': printf( ANSI[reset] ); break; case 'g': printf( ANSI[gray] ); break; case 'R': printf( ANSI[red] ); break; case 'G': printf( ANSI[green] ); break; case 'Y': printf( ANSI[yellow] ); break; case 'B': printf( ANSI[blue] ); break; case 'M': printf( ANSI[magenta] ); break; case 'C': printf( ANSI[cyan] ); break; case 'W': printf( ANSI[white] ); break; default: putc('#', stdout); break; }; return 0; } //------------------------------------------------------------------------- int wrap(char *msg, int width, int pos) { int i, w, len, run, lb; if(!msg || !*msg) { printf("\r\n"); return 0; } while(*msg) { w = width-pos; len = runlen(msg, w, &run); for(i = 0; i < len; i++) lb = printchar(msg, i); if(lb) { printf("\r\n"); pos = 0; } else pos += run; msg += len; if(*msg) { while(*msg == ' ') msg++; /* this is wrapping linebreak only */ if(!lb) printf("\r\n"); pos = 0; } } return pos; } //------------------------------------------------------------------------ int main(int argc, char *argv[]) { int i, len, pos; char *line, buffer[4096]; char *lyrics[] = { "", "Bye bye Miss #RAmer#Wican#B Pie#=, drove my Chevy to the levy 'cause the levy was dry.", " And good ol' boys were drinking wisky and rye, singing: 'This will be the day that I die'...#n", "", "Did you write the Book of Love, and do you have faith in #YGod#= above, if the Bible tells you so.", " Do you believe in #Crock 'n roll#=, can music save, #gyour mortal soul#=, and can you teach me to dance real slow...#n", "", "Well I know that you're in love with him, 'cause I saw you dancing, in the gym.", " You both kicked off your shoes, man I love those rythmly #Bblues!#=", " Ooh! I was a lonely teenage broncho buck, with a #Mpink carnation#= and a pickup truck,", " But I knew I was out of luck, the day, the music, died.#n", "", "I was singing: Bye bye Miss American Pie...#n", "#.#n#", "#.#n#asdf", "abc#nxyz", NULL }; argc--; if(argc) { sscanf(argv[1], "%d", &COLUMNS); argv++; argc--; } if(12 > COLUMNS || COLUMNS > 120) COLUMNS = 75; for(i = 0, pos = 0; lyrics[i] != NULL; i++) { line = lyrics[i]; pos = wrap(line, COLUMNS, pos); } #ifdef DO_FILES while(argc) { freopen(argv[1], "r", stdin); pos = 0; while( fgets(buffer, sizeof(buffer), stdin) ) { pos = wrap(buffer, COLUMNS, pos); } argv++; argc--; } #endif return 0; }