/** 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 <stdio.h>
#include <stdlib.h>
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;
}