#if !defined(lint) && !defined(DOS) static char rcsid[] = "$Id: color.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $"; #endif /* * ======================================================================== * Copyright 2006-2007 University of Washington * Copyright 2013-2021 Eduardo Chappa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * ======================================================================== */ #include #include #include "../headers.h" #include "terminal.h" #include "color.h" #include "../../pith/osdep/color.h" #include "../../pith/osdep/collate.h" #ifndef _WINDOWS struct color_name_list { char *name; int namelen; struct color_name_list *next; }; struct color_table { struct color_name_list *names; char *rgb; int red, green, blue; int val; }; /* useful definitions */ #define ANSI8_COLOR() (color_options & COLOR_ANSI8_OPT) #define ANSI16_COLOR() (color_options & COLOR_ANSI16_OPT) #define ANSI256_COLOR() (color_options & COLOR_ANSI256_OPT) #define ANSI_COLOR() (color_options & (COLOR_ANSI8_OPT | COLOR_ANSI16_OPT | COLOR_ANSI256_OPT)) /* transparent (default) color is possible */ #define COL_TRANS() ((color_options & COLOR_TRANS_OPT) ? 1 : 0) /* transparent */ #define END_PSEUDO_REVERSE "EndInverse" #define A_UNKNOWN -1 /* local globals */ static unsigned color_options; static COLOR_PAIR *the_rev_color, *the_normal_color; static COLOR_PAIR *color_blasted_by_attrs; static int pinvstate; /* physical state of inverse (standout) attribute */ static int pboldstate; /* physical state of bold attribute */ static int pulstate; /* physical state of underline attribute */ static int rev_color_state; static int boldstate; /* should-be state of bold attribute */ static int ulstate; /* should-be state of underline attribute */ static int invstate; /* should-be state of Inverse, which could be a color change or an actual setinverse */ static int _color_inited, _using_color; static char *_nfcolor, *_nbcolor, *_rfcolor, *_rbcolor; static char *_last_fg_color, *_last_bg_color; static int _force_fg_color_change; static int _force_bg_color_change; static struct color_table *color_tbl; /* external references */ extern char *_op, *_oc, *_setaf, *_setab, *_setf, *_setb, *_scp; extern int _bce, _colors; /* internal prototypes */ void flip_rev_color(int); void flip_bold(int); void flip_ul(int); void reset_attr_state(void); void add_to_color_name_list(struct color_table *t, char *name); void free_color_name_list(struct color_name_list **nl); #if HAS_TERMINFO extern char *tparm(char *, ...); #endif extern char *tgoto(); void tinitcolor(void); int tfgcolor(int); int tbgcolor(int); struct color_table *init_color_table(void); void free_color_table(struct color_table **); int color_to_val(char *); /* * Start or end bold mode * * Result: escape sequence to go into or out of reverse color is output * * (This is only called when there is a reverse color.) * * Arg state = ON set bold * OFF set normal */ void flip_rev_color(int state) { if((rev_color_state = state) == TRUE) (void)pico_set_colorp(the_rev_color, PSC_NONE); else pico_set_normal_color(); } /* * Start or end bold mode * * Result: escape sequence to go into or out of bold is output * * Arg state = ON set bold * OFF set normal */ void flip_bold(int state) { extern char *_setbold, *_clearallattr; if((pboldstate = state) == TRUE){ if(_setbold != NULL) putpad(_setbold); } else{ if(_clearallattr != NULL){ if(!color_blasted_by_attrs) color_blasted_by_attrs = pico_get_cur_color(); _force_fg_color_change = _force_bg_color_change = 1; putpad(_clearallattr); pinvstate = state; pulstate = state; rev_color_state = state; } } } /* * Start or end inverse mode * * Result: escape sequence to go into or out of inverse is output * * Arg state = ON set inverse * OFF set normal */ void flip_inv(int state) { extern char *_setinverse, *_clearinverse; if((pinvstate = state) == TRUE){ if(_setinverse != NULL) putpad(_setinverse); } else{ /* * Unfortunately, some termcap entries configure end standout to * be clear all attributes. */ if(_clearinverse != NULL){ if(!color_blasted_by_attrs) color_blasted_by_attrs = pico_get_cur_color(); _force_fg_color_change = _force_bg_color_change = 1; putpad(_clearinverse); pboldstate = (pboldstate == FALSE) ? pboldstate : A_UNKNOWN; pulstate = (pulstate == FALSE) ? pulstate : A_UNKNOWN; rev_color_state = A_UNKNOWN; } } } /* * Start or end underline mode * * Result: escape sequence to go into or out of underline is output * * Arg state = ON set underline * OFF set normal */ void flip_ul(int state) { extern char *_setunderline, *_clearunderline; if((pulstate = state) == TRUE){ if(_setunderline != NULL) putpad(_setunderline); } else{ /* * Unfortunately, some termcap entries configure end underline to * be clear all attributes. */ if(_clearunderline != NULL){ if(!color_blasted_by_attrs) color_blasted_by_attrs = pico_get_cur_color(); _force_fg_color_change = _force_bg_color_change = 1; putpad(_clearunderline); pboldstate = (pboldstate == FALSE) ? pboldstate : A_UNKNOWN; pinvstate = (pinvstate == FALSE) ? pinvstate : A_UNKNOWN; rev_color_state = A_UNKNOWN; } } } void StartInverse(void) { invstate = TRUE; reset_attr_state(); } void EndInverse(void) { invstate = FALSE; reset_attr_state(); } int InverseState(void) { return(invstate); } int StartBold(void) { extern char *_setbold; boldstate = TRUE; reset_attr_state(); return(_setbold != NULL); } void EndBold(void) { boldstate = FALSE; reset_attr_state(); } void StartUnderline(void) { ulstate = TRUE; reset_attr_state(); } void EndUnderline(void) { ulstate = FALSE; reset_attr_state(); } void reset_attr_state(void) { /* * If we have to turn some attributes off, do that first since that * may turn off other attributes as a side effect. */ if(boldstate == FALSE && pboldstate != boldstate) flip_bold(boldstate); if(ulstate == FALSE && pulstate != ulstate) flip_ul(ulstate); if(invstate == FALSE){ if(pico_get_rev_color()){ if(rev_color_state != invstate) flip_rev_color(invstate); } else{ if(pinvstate != invstate) flip_inv(invstate); } } /* * Now turn everything on that needs turning on. */ if(boldstate == TRUE && pboldstate != boldstate) flip_bold(boldstate); if(ulstate == TRUE && pulstate != ulstate) flip_ul(ulstate); if(invstate == TRUE){ if(pico_get_rev_color()){ if(rev_color_state != invstate) flip_rev_color(invstate); } else{ if(pinvstate != invstate) flip_inv(invstate); } } if(color_blasted_by_attrs){ (void)pico_set_colorp(color_blasted_by_attrs, PSC_NONE); free_color_pair(&color_blasted_by_attrs); } } void tinitcolor(void) { if(_color_inited || panicking()) return; if(ANSI_COLOR() || (_colors > 0 && ((_setaf && _setab) || (_setf && _setb) /**** not sure how to do this yet || _scp ****/ ))){ _color_inited = 1; color_tbl = init_color_table(); if(ANSI_COLOR()) putpad("\033[39;49m"); else{ if(_op) putpad(_op); if(_oc) putpad(_oc); } } } #if HAS_TERMCAP /* * Treading on thin ice here. Tgoto wasn't designed for this. It takes * arguments column and row. We only use one of them, so we put it in * the row argument. The 0 is just a placeholder. */ #define tparm(s, c) tgoto(s, 0, c) #endif int tfgcolor(int color) { if(!_color_inited) tinitcolor(); if(!_color_inited) return(-1); if((ANSI8_COLOR() && (color < 0 || color >= 8+COL_TRANS())) || (ANSI16_COLOR() && (color < 0 || color >= 16+COL_TRANS())) || (ANSI256_COLOR() && (color < 0 || color >= 256+COL_TRANS())) || (!ANSI_COLOR() && (color < 0 || color >= _colors+COL_TRANS()))) return(-1); if(ANSI_COLOR()){ char buf[20]; if(COL_TRANS() && color == pico_count_in_color_table()-1) snprintf(buf, sizeof(buf), "\033[39m"); else if(ANSI256_COLOR()) snprintf(buf, sizeof(buf), "\033[38;5;%dm", color); else{ if(color < 8) snprintf(buf, sizeof(buf), "\033[3%cm", color + '0'); else snprintf(buf, sizeof(buf), "\033[9%cm", (color-8) + '0'); } putpad(buf); } else if(COL_TRANS() && color == pico_count_in_color_table()-1 && _op){ char bg_color_was[MAXCOLORLEN+1]; bg_color_was[0] = '\0'; /* * Setting transparent (default) foreground color. * We don't know how to set only the default foreground color, * _op sets both foreground and background. So we need to * * get current background color * set default colors * if (current background was not default) reset it */ if(_last_bg_color && strncmp(_last_bg_color, MATCH_TRAN_COLOR, RGBLEN)){ strncpy(bg_color_was, _last_bg_color, sizeof(bg_color_was)); bg_color_was[sizeof(bg_color_was)-1] = '\0'; } putpad(_op); if(bg_color_was[0]){ _force_bg_color_change = 1; pico_set_bg_color(bg_color_was); } } else if(_setaf) putpad(tparm(_setaf, color)); else if(_setf) putpad(tparm(_setf, color)); else if(_scp){ /* set color pair method */ } return(0); } int tbgcolor(int color) { if(!_color_inited) tinitcolor(); if(!_color_inited) return(-1); if((ANSI8_COLOR() && (color < 0 || color >= 8+COL_TRANS())) || (ANSI16_COLOR() && (color < 0 || color >= 16+COL_TRANS())) || (ANSI256_COLOR() && (color < 0 || color >= 256+COL_TRANS())) || (!ANSI_COLOR() && (color < 0 || color >= _colors+COL_TRANS()))) return(-1); if(ANSI_COLOR()){ char buf[20]; if(COL_TRANS() && color == pico_count_in_color_table()-1) snprintf(buf, sizeof(buf), "\033[49m"); else if(ANSI256_COLOR()) snprintf(buf, sizeof(buf), "\033[48;5;%dm", color); else{ if(color < 8) snprintf(buf, sizeof(buf), "\033[4%cm", color + '0'); else snprintf(buf, sizeof(buf), "\033[10%cm", (color-8) + '0'); } putpad(buf); } else if(COL_TRANS() && color == pico_count_in_color_table()-1 && _op){ char fg_color_was[MAXCOLORLEN+1]; fg_color_was[0] = '\0'; /* * Setting transparent (default) background color. * We don't know how to set only the default background color, * _op sets both foreground and background. So we need to * * get current foreground color * set default colors * if (current foreground was not default) reset it */ if(_last_fg_color && strncmp(_last_fg_color, MATCH_TRAN_COLOR, RGBLEN)){ strncpy(fg_color_was, _last_fg_color, sizeof(fg_color_was)); fg_color_was[sizeof(fg_color_was)-1] = '\0'; } putpad(_op); if(fg_color_was[0]){ _force_fg_color_change = 1; pico_set_fg_color(fg_color_was); } } else if(_setab) putpad(tparm(_setab, color)); else if(_setb) putpad(tparm(_setb, color)); else if(_scp){ /* set color pair method */ } return(0); } /* * We're not actually using the RGB value other than as a string which * maps into the color. * In fact, on some systems color 1 and color 4 are swapped, and color 3 * and color 6 are swapped. So don't believe the RGB values. * Still, it feels right to have them be the canonical values, so we do that. * * Actually we are using them more now. In color_to_val we map to the closest * color if we don't get an exact match. We ignore values over 255. * * More than one "name" can map to the same "rgb". * More than one "name" can map to the same "val". * The "val" for a "name" and for its "rgb" are the same. */ struct color_table * init_color_table(void) { struct color_table *ct = NULL, *t; int i, count; char colorname[12]; count = pico_count_in_color_table(); if(count > 0 && count <= 256+COL_TRANS()){ int ind, graylevel; struct { char rgb[RGBLEN+1]; int red, green, blue; } cube[256]; ct = (struct color_table *) fs_get((count+1) * sizeof(struct color_table)); if(ct) memset(ct, 0, (count+1) * sizeof(struct color_table)); /* * We boldly assume that 256 colors means xterm 256-color * color cube and grayscale. */ if(ANSI_COLOR() && (count == 256+COL_TRANS())){ int r, g, b, gray; for(r = 0; r < 6; r++) for(g = 0; g < 6; g++) for(b = 0; b < 6; b++){ ind = 16 + 36*r + 6*g + b; cube[ind].red = r ? (40*r + 55) : 0; cube[ind].green = g ? (40*g + 55) : 0; cube[ind].blue = b ? (40*b + 55) : 0; snprintf(cube[ind].rgb, sizeof(cube[ind].rgb), "%3.3d,%3.3d,%3.3d", cube[ind].red, cube[ind].green, cube[ind].blue); } for(gray = 0; gray < 24; gray++){ ind = gray + 232; graylevel = 10*gray + 8; cube[ind].red = graylevel; cube[ind].green = graylevel; cube[ind].blue = graylevel; snprintf(cube[ind].rgb, sizeof(cube[ind].rgb), "%3.3d,%3.3d,%3.3d", graylevel, graylevel, graylevel); } } for(i = 0, t = ct; t && i < count; i++, t++){ t->val = i; switch(i){ case COL_BLACK: strncpy(colorname, "black", sizeof(colorname)); colorname[sizeof(colorname)-1] = '\0'; break; case COL_RED: strncpy(colorname, "red", sizeof(colorname)); colorname[sizeof(colorname)-1] = '\0'; break; case COL_GREEN: strncpy(colorname, "green", sizeof(colorname)); colorname[sizeof(colorname)-1] = '\0'; break; case COL_YELLOW: strncpy(colorname, "yellow", sizeof(colorname)); colorname[sizeof(colorname)-1] = '\0'; break; case COL_BLUE: strncpy(colorname, "blue", sizeof(colorname)); colorname[sizeof(colorname)-1] = '\0'; break; case COL_MAGENTA: strncpy(colorname, "magenta", sizeof(colorname)); colorname[sizeof(colorname)-1] = '\0'; break; case COL_CYAN: strncpy(colorname, "cyan", sizeof(colorname)); colorname[sizeof(colorname)-1] = '\0'; break; case COL_WHITE: strncpy(colorname, "white", sizeof(colorname)); colorname[sizeof(colorname)-1] = '\0'; break; default: snprintf(colorname, sizeof(colorname), "color%3.3d", i); break; } if(COL_TRANS() && i == count-1){ strncpy(colorname, MATCH_TRAN_COLOR, sizeof(colorname)); colorname[sizeof(colorname)-1] = '\0'; } add_to_color_name_list(t, colorname); if(count == 8+COL_TRANS()){ if(COL_TRANS() && i == count-1){ t->red = t->green = t->blue = -1; } else switch(i){ case COL_BLACK: t->red = t->green = t->blue = 0; add_to_color_name_list(t, "color008"); add_to_color_name_list(t, "colordgr"); add_to_color_name_list(t, "colormgr"); break; case COL_RED: t->red = 255; t->green = t->blue = 0; add_to_color_name_list(t, "color009"); break; case COL_GREEN: t->green = 255; t->red = t->blue = 0; add_to_color_name_list(t, "color010"); break; case COL_YELLOW: t->red = t->green = 255; t->blue = 0; add_to_color_name_list(t, "color011"); break; case COL_BLUE: t->red = t->green = 0; t->blue = 255; add_to_color_name_list(t, "color012"); break; case COL_MAGENTA: t->red = t->blue = 255; t->green = 0; add_to_color_name_list(t, "color013"); break; case COL_CYAN: t->red = 0; t->green = t->blue = 255; add_to_color_name_list(t, "color014"); break; case COL_WHITE: t->red = t->green = t->blue = 255; add_to_color_name_list(t, "color015"); add_to_color_name_list(t, "colorlgr"); break; } } else if(count == 16+COL_TRANS() || count == 256+COL_TRANS()){ if(COL_TRANS() && i == count-1){ t->red = t->green = t->blue = -1; } else /* * This set of RGB values seems to come close to describing * what a 16-color xterm gives you. */ switch(i){ case COL_BLACK: t->red = t->green = t->blue = 0; break; case COL_RED: /* actually dark red */ t->red = 174; t->green = t->blue = 0; break; case COL_GREEN: /* actually dark green */ t->green = 174; t->red = t->blue = 0; break; case COL_YELLOW: /* actually dark yellow */ t->blue = 0; t->red = t->green = 174; break; case COL_BLUE: /* actually dark blue */ t->blue = 174; t->red = t->green = 0; break; case COL_MAGENTA: /* actually dark magenta */ t->green = 0; t->red = t->blue = 174; break; case COL_CYAN: /* actually dark cyan */ t->red = 0; t->green = t->blue = 174; break; case COL_WHITE: /* actually light gray */ t->red = t->green = t->blue = 174; if(count == 16) add_to_color_name_list(t, "colorlgr"); break; case 8: /* dark gray */ t->red = t->green = t->blue = 64; if(count == 16){ add_to_color_name_list(t, "colordgr"); add_to_color_name_list(t, "colormgr"); } break; case 9: /* red */ t->red = 255; t->green = t->blue = 0; break; case 10: /* green */ t->green = 255; t->red = t->blue = 0; break; case 11: /* yellow */ t->blue = 0; t->red = t->green = 255; break; case 12: /* blue */ t->blue = 255; t->red = t->green = 0; break; case 13: /* magenta */ t->green = 0; t->red = t->blue = 255; break; case 14: /* cyan */ t->red = 0; t->green = t->blue = 255; break; case 15: /* white */ t->red = t->green = t->blue = 255; break; default: t->red = cube[i].red; t->green = cube[i].green; t->blue = cube[i].blue; switch(i){ case 238: add_to_color_name_list(t, "colordgr"); break; case 244: add_to_color_name_list(t, "colormgr"); break; case 250: add_to_color_name_list(t, "colorlgr"); break; } break; } } else{ if(COL_TRANS() && i == count-1){ t->red = t->green = t->blue = -1; } else switch(i){ case COL_BLACK: t->red = t->green = t->blue = 0; break; case COL_RED: /* actually dark red */ t->red = 255; t->green = t->blue = 0; break; case COL_GREEN: /* actually dark green */ t->green = 255; t->red = t->blue = 0; break; case COL_YELLOW: /* actually dark yellow */ t->blue = 0; t->red = t->green = 255; break; case COL_BLUE: /* actually dark blue */ t->blue = 255; t->red = t->green = 0; break; case COL_MAGENTA: /* actually dark magenta */ t->green = 0; t->red = t->blue = 255; break; case COL_CYAN: /* actually dark cyan */ t->red = 0; t->green = t->blue = 255; break; case COL_WHITE: /* actually light gray */ t->red = t->green = t->blue = 255; break; default: /* this will just be a string match */ t->red = t->green = t->blue = 256+i; break; } } } for(i = 0, t = ct; t && i < count; i++, t++){ t->rgb = (char *)fs_get((RGBLEN+1) * sizeof(char)); if(COL_TRANS() && i == count-1) snprintf(t->rgb, RGBLEN+1, MATCH_TRAN_COLOR); else snprintf(t->rgb, RGBLEN+1, "%3.3d,%3.3d,%3.3d", t->red, t->green, t->blue); } } return(ct); } void add_to_color_name_list(struct color_table *t, char *name) { if(t && name && *name){ struct color_name_list *new_name; new_name = (struct color_name_list *) fs_get(sizeof(struct color_name_list)); if(new_name){ memset(new_name, 0, sizeof(*new_name)); new_name->namelen = strlen(name); new_name->name = (char *) fs_get((new_name->namelen+1) * sizeof(char)); if(new_name->name){ strncpy(new_name->name, name, new_name->namelen+1); new_name->name[new_name->namelen] = '\0'; if(t->names){ struct color_name_list *nl; for(nl = t->names; nl->next; nl = nl->next) ; nl->next = new_name; } else t->names = new_name; } } } } void free_color_name_list(struct color_name_list **nl) { if(nl && *nl){ if((*nl)->next) free_color_name_list(&(*nl)->next); if((*nl)->name) fs_give((void **) &(*nl)->name); fs_give((void **) nl); } } /* * Map from integer color value to canonical color name. */ char * colorx(int color) { struct color_table *ct; static char cbuf[12]; /* before we get set up, we still need to use this a bit */ if(!color_tbl){ switch(color){ case COL_BLACK: return("black"); case COL_RED: return("red"); case COL_GREEN: return("green"); case COL_YELLOW: return("yellow"); case COL_BLUE: return("blue"); case COL_MAGENTA: return("magenta"); case COL_CYAN: return("cyan"); case COL_WHITE: return("white"); default: snprintf(cbuf, sizeof(cbuf), "color%3.3d", color); return(cbuf); } } for(ct = color_tbl; ct->names; ct++) if(ct->val == color) break; /* rgb _is_ the canonical name */ if(ct->names) return(ct->rgb); /* not supposed to get here */ snprintf(cbuf, sizeof(cbuf), "color%3.3d", color); return(cbuf); } /* * Argument is a color name which could be an RGB string, a name like "blue", * or a name like "color011". * * Returns a pointer to the canonical name of the color. */ char * color_to_canonical_name(char *s) { struct color_table *ct; struct color_name_list *nl; int done; if(!s || !color_tbl) return(NULL); if(*s == ' ' || isdigit(*s)){ /* check for rgb string instead of name */ for(ct = color_tbl; ct->rgb; ct++) if(!strncmp(ct->rgb, s, RGBLEN)) break; } else{ for(done=0, ct = color_tbl; !done && ct->names; ct++){ for(nl = ct->names; !done && nl; nl = nl->next) if(nl->name && !struncmp(nl->name, s, nl->namelen)) done++; if(done) break; } } /* rgb is the canonical name */ if(ct->names) return(ct->rgb); else if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN) || !struncmp(s, MATCH_NONE_COLOR, RGBLEN)) return(s); return(""); } /* * Argument is the name of a color or an RGB value that we recognize. * The table should be set up so that the val returned is the same whether * or not we choose the canonical name. * * Returns the integer value for the color. */ int color_to_val(char *s) { struct color_table *ct; struct color_name_list *nl; int done; if(!s || !color_tbl) return(-1); if(*s == ' ' || isdigit(*s)){ /* check for rgb string instead of name */ for(ct = color_tbl; ct->rgb; ct++) if(!strncmp(ct->rgb, s, RGBLEN)) break; /* * Didn't match any. Find "closest" to match. */ if(!ct->rgb){ int r = -1, g = -1, b = -1; char *p, *comma, scopy[RGBLEN+1]; strncpy(scopy, s, sizeof(scopy)); scopy[sizeof(scopy)-1] = '\0'; p = scopy; comma = strchr(p, ','); if(comma){ *comma = '\0'; r = atoi(p); p = comma+1; if(r >= 0 && r <= 255 && *p){ comma = strchr(p, ','); if(comma){ *comma = '\0'; g = atoi(p); p = comma+1; if(g >= 0 && g <= 255 && *p){ b = atoi(p); } } } } if(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255){ struct color_table *closest = NULL; int closest_value = 1000000; int cv; for(ct = color_tbl; ct->rgb; ct++){ if(ct->red >= 0 && ct->red <= 255 && ct->green >= 0 && ct->green <= 255 && ct->blue >= 0 && ct->blue <= 255){ cv = (ct->red - r) * (ct->red - r) + (ct->green - g) * (ct->green - g) + (ct->blue - b) * (ct->blue - b); if(cv < closest_value){ closest_value = cv; closest = ct; } } } if(closest) ct = closest; } } } else{ for(done=0, ct = color_tbl; !done && ct->names; ct++){ for(nl = ct->names; !done && nl; nl = nl->next) if(nl->name && !struncmp(nl->name, s, nl->namelen)) done++; if(done) break; } } if(ct->names) return(ct->val); else return(-1); } void free_color_table(struct color_table **ctbl) { struct color_table *t; if(ctbl && *ctbl){ for(t = *ctbl; t->names; t++){ free_color_name_list(&t->names); if(t->rgb) fs_give((void **) &t->rgb); } fs_give((void **) ctbl); } } int pico_count_in_color_table(void) { return( (ANSI_COLOR() ? (ANSI8_COLOR() ? 8 : ANSI16_COLOR() ? 16 : 256) : _colors) + COL_TRANS()); } void pico_nfcolor(char *s) { if(_nfcolor) fs_give((void **) &_nfcolor); if(s){ size_t len; len = strlen(s); _nfcolor = (char *) fs_get((len+1) * sizeof(char)); if(_nfcolor){ strncpy(_nfcolor, s, len+1); _nfcolor[len] = '\0'; } if(the_normal_color){ strncpy(the_normal_color->fg, _nfcolor, MAXCOLORLEN+1); the_normal_color->fg[MAXCOLORLEN] = '\0'; } } else if(the_normal_color) free_color_pair(&the_normal_color); } void pico_nbcolor(char *s) { if(_nbcolor) fs_give((void **) &_nbcolor); if(s){ size_t len; len = strlen(s); _nbcolor = (char *) fs_get((len+1) * sizeof(char)); if(_nbcolor){ strncpy(_nbcolor, s, len+1); _nbcolor[len] = '\0'; } if(the_normal_color){ strncpy(the_normal_color->bg, _nbcolor, MAXCOLORLEN+1); the_normal_color->bg[MAXCOLORLEN] = '\0'; } } else if(the_normal_color) free_color_pair(&the_normal_color); } void pico_rfcolor(char *s) { if(_rfcolor) fs_give((void **) &_rfcolor); if(s){ size_t len; len = strlen(s); _rfcolor = (char *) fs_get((len+1) * sizeof(char)); if(_rfcolor){ strncpy(_rfcolor, s, len+1); _rfcolor[len] = '\0'; } if(the_rev_color){ strncpy(the_rev_color->fg, _rfcolor, MAXCOLORLEN+1); the_rev_color->fg[MAXCOLORLEN] = '\0'; } } else if(the_rev_color) free_color_pair(&the_rev_color); } void pico_rbcolor(char *s) { if(_rbcolor) fs_give((void **) &_rbcolor); if(s){ size_t len; len = strlen(s); _rbcolor = (char *) fs_get((len+1) * sizeof(char)); if(_rbcolor){ strncpy(_rbcolor, s, len+1); _rbcolor[len] = '\0'; } if(the_rev_color){ strncpy(the_rev_color->bg, _rbcolor, MAXCOLORLEN+1); the_rev_color->bg[MAXCOLORLEN] = '\0'; } } else if(the_rev_color) free_color_pair(&the_rev_color); } int pico_hascolor(void) { if(!_color_inited) tinitcolor(); return(_color_inited); } int pico_usingcolor(void) { return(_using_color && pico_hascolor()); } /* * This should only be called when we're using the * unix termdef color, as opposed to the ANSI defined * color stuff or the Windows stuff. */ int pico_trans_color(void) { return(_bce && _op); } void pico_toggle_color(int on) { if(on){ if(pico_hascolor()) _using_color = 1; } else{ _using_color = 0; if(_color_inited){ _color_inited = 0; if(!panicking()) free_color_table(&color_tbl); if(ANSI_COLOR()) putpad("\033[39;49m"); else{ if(_op) putpad(_op); if(_oc) putpad(_oc); } } } } unsigned pico_get_color_options(void) { return(color_options); } int pico_trans_is_on(void) { return(COL_TRANS()); } /* * Absolute set of options. Caller has to OR things together and so forth. */ void pico_set_color_options(unsigned flags) { color_options = flags; } void pico_endcolor(void) { pico_toggle_color(0); if(panicking()) return; if(_nfcolor) fs_give((void **) &_nfcolor); if(_nbcolor) fs_give((void **) &_nbcolor); if(_rfcolor) fs_give((void **) &_rfcolor); if(_rbcolor) fs_give((void **) &_rbcolor); if(_last_fg_color) fs_give((void **) &_last_fg_color); if(_last_bg_color) fs_give((void **) &_last_bg_color); if(the_rev_color) free_color_pair(&the_rev_color); if(the_normal_color) free_color_pair(&the_normal_color); } void pico_set_nfg_color(void) { if(_nfcolor) (void)pico_set_fg_color(_nfcolor); } void pico_set_nbg_color(void) { if(_nbcolor) (void)pico_set_bg_color(_nbcolor); } void pico_set_normal_color(void) { if(!_nfcolor || !_nbcolor || !pico_set_fg_color(_nfcolor) || !pico_set_bg_color(_nbcolor)){ (void)pico_set_fg_color(DEFAULT_NORM_FORE_RGB); (void)pico_set_bg_color(DEFAULT_NORM_BACK_RGB); } } /* * If inverse is a color, returns a pointer to that color. * If not, returns NULL. * * NOTE: Don't free this! */ COLOR_PAIR * pico_get_rev_color(void) { if(pico_usingcolor() && _rfcolor && _rbcolor && pico_is_good_color(_rfcolor) && pico_is_good_color(_rbcolor)){ if(!the_rev_color) the_rev_color = new_color_pair(_rfcolor, _rbcolor); return(the_rev_color); } else return(NULL); } /* * Returns a pointer to the normal color. * * NOTE: Don't free this! */ COLOR_PAIR * pico_get_normal_color(void) { if(pico_usingcolor() && _nfcolor && _nbcolor && pico_is_good_color(_nfcolor) && pico_is_good_color(_nbcolor)){ if(!the_normal_color) the_normal_color = new_color_pair(_nfcolor, _nbcolor); return(the_normal_color); } else return(NULL); } /* * Sets color to (fg,bg). * Flags == PSC_NONE No alternate default if fg,bg fails. * == PSC_NORM Set it to Normal color on failure. * == PSC_REV Set it to Reverse color on failure. * * If flag PSC_RET is set, returns an allocated copy of the previous * color pair, otherwise returns NULL. */ COLOR_PAIR * pico_set_colors(char *fg, char *bg, int flags) { int uc; COLOR_PAIR *cp = NULL, *rev = NULL; if(flags & PSC_RET) cp = pico_get_cur_color(); if(fg && !strcmp(fg, END_PSEUDO_REVERSE)){ EndInverse(); if(cp) free_color_pair(&cp); } else if(!((uc=pico_usingcolor()) && fg && bg && pico_set_fg_color(fg) && pico_set_bg_color(bg))){ if(uc && flags & PSC_NORM) pico_set_normal_color(); else if(flags & PSC_REV){ if((rev = pico_get_rev_color()) != NULL){ pico_set_fg_color(rev->fg); /* these will succeed */ pico_set_bg_color(rev->bg); } else{ StartInverse(); if(cp){ strncpy(cp->fg, END_PSEUDO_REVERSE, MAXCOLORLEN+1); cp->fg[MAXCOLORLEN] = '\0'; strncpy(cp->bg, END_PSEUDO_REVERSE, MAXCOLORLEN+1); cp->bg[MAXCOLORLEN] = '\0'; } } } } return(cp); } int pico_is_good_color(char *s) { struct color_table *ct; struct color_name_list *nl; int done; if(!s || !color_tbl) return(FALSE); if(!strcmp(s, END_PSEUDO_REVERSE)) return(TRUE); else if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN) || !struncmp(s, MATCH_NONE_COLOR, RGBLEN)) return(TRUE); else if(*s == ' ' || isdigit(*s)){ /* check for rgb string instead of name */ for(ct = color_tbl; ct->rgb; ct++) if(!strncmp(ct->rgb, s, RGBLEN)) break; /* if no match it's still ok if rgb */ if(!ct->rgb){ int r = -1, g = -1, b = -1; char *p, *comma, scopy[RGBLEN+1]; strncpy(scopy, s, sizeof(scopy)); scopy[sizeof(scopy)-1] = '\0'; p = scopy; comma = strchr(p, ','); if(comma){ *comma = '\0'; r = atoi(p); p = comma+1; if(r >= 0 && r <= 255 && *p){ comma = strchr(p, ','); if(comma){ *comma = '\0'; g = atoi(p); p = comma+1; if(g >= 0 && g <= 255 && *p){ b = atoi(p); } } } } if(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) ct = color_tbl; /* to force TRUE */ } } else{ for(done=0, ct = color_tbl; !done && ct->names; ct++){ for(nl = ct->names; !done && nl; nl = nl->next) if(nl->name && !struncmp(nl->name, s, nl->namelen)) done++; if(done) break; } } return(ct->names ? TRUE : FALSE); } /* * Return TRUE on success. */ int pico_set_fg_color(char *s) { int val; if(!s || !color_tbl) return(FALSE); if(!strcmp(s, END_PSEUDO_REVERSE)){ EndInverse(); return(TRUE); } if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN)) s = _nfcolor; else if(!struncmp(s, MATCH_NONE_COLOR, RGBLEN)) return(TRUE); if((val = color_to_val(s)) >= 0){ size_t len; int changed; changed = !_last_fg_color || strcmp(_last_fg_color,colorx(val)); /* already set correctly */ if(!_force_fg_color_change && !changed) return(TRUE); _force_fg_color_change = 0; if(changed){ if(_last_fg_color) fs_give((void **) &_last_fg_color); len = strlen(colorx(val)); if((_last_fg_color = (char *) fs_get((len+1) * sizeof(char))) != NULL){ strncpy(_last_fg_color, colorx(val), len+1); _last_fg_color[len] = '\0'; } } tfgcolor(val); return(TRUE); } else return(FALSE); } int pico_set_bg_color(char *s) { int val; if(!s || !color_tbl) return(FALSE); if(!strcmp(s, END_PSEUDO_REVERSE)){ EndInverse(); return(TRUE); } if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN)) s = _nbcolor; else if(!struncmp(s, MATCH_NONE_COLOR, RGBLEN)) return(TRUE); if((val = color_to_val(s)) >= 0){ size_t len; int changed; changed = !_last_bg_color || strcmp(_last_bg_color,colorx(val)); /* already set correctly */ if(!_force_bg_color_change && !changed) return(TRUE); _force_bg_color_change = 0; if(changed){ if(_last_bg_color) fs_give((void **) &_last_bg_color); len = strlen(colorx(val)); if((_last_bg_color = (char *) fs_get((len+1) * sizeof(char))) != NULL){ strncpy(_last_bg_color, colorx(val), len+1); _last_bg_color[len] = '\0'; } } tbgcolor(val); return(TRUE); } else return(FALSE); } /* * Return a pointer to an rgb string for the input color. The output is 11 * characters long and looks like rrr,ggg,bbb. * * Args colorName -- The color to convert to ascii rgb. * * Returns Pointer to a static buffer containing the rgb string. Can use up * to three returned values at once before the first is overwritten. */ char * color_to_asciirgb(char *colorName) { static char c_to_a_buf[3][RGBLEN+1]; static int whichbuf = 0; struct color_table *ct; struct color_name_list *nl; int done; whichbuf = (whichbuf + 1) % 3; c_to_a_buf[whichbuf][0] = '\0'; for(done=0, ct = color_tbl; !done && ct->names; ct++){ for(nl = ct->names; !done && nl; nl = nl->next) if(nl->name && !struncmp(nl->name, colorName, nl->namelen)) done++; if(done) break; } if(ct && ct->names){ strncpy(c_to_a_buf[whichbuf], ct->rgb, sizeof(c_to_a_buf[0])); c_to_a_buf[whichbuf][sizeof(c_to_a_buf[0])-1] = '\0'; } else if(*colorName == ' ' || isdigit(*colorName)){ /* check for rgb string instead of name */ for(ct = color_tbl; ct->rgb; ct++) if(!strncmp(ct->rgb, colorName, RGBLEN)) break; /* if no match it's still ok if rgb */ if(!ct->rgb){ int r = -1, g = -1, b = -1; char *p, *comma, scopy[RGBLEN+1]; strncpy(scopy, colorName, sizeof(scopy)); scopy[sizeof(scopy)-1] = '\0'; p = scopy; comma = strchr(p, ','); if(comma){ *comma = '\0'; r = atoi(p); p = comma+1; if(r >= 0 && r <= 255 && *p){ comma = strchr(p, ','); if(comma){ *comma = '\0'; g = atoi(p); p = comma+1; if(g >= 0 && g <= 255 && *p){ b = atoi(p); } } } } if(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255){ /* it was already RGB */ snprintf(c_to_a_buf[whichbuf], sizeof(c_to_a_buf[0]), "%3.3d,%3.3d,%3.3d", r, g, b); } } else{ strncpy(c_to_a_buf[whichbuf], ct->rgb, sizeof(c_to_a_buf[0])); c_to_a_buf[whichbuf][sizeof(c_to_a_buf[0])-1] = '\0'; } } if(!c_to_a_buf[whichbuf][0]){ int l; /* * If we didn't find the color it could be that it is the * normal color (MATCH_NORM_COLOR) or the none color * (MATCH_NONE_COLOR). If that is the case, this strncpy thing * will work out correctly because those two strings are * RGBLEN long. Otherwise we're in a bit of trouble. This * most likely means that the user is using the same pinerc on * two terminals, one with more colors than the other. We didn't * find a match because this color isn't present on this terminal. * Since the return value of this function is assumed to be * RGBLEN long, we'd better make it that long. * It still won't work correctly because colors will be screwed up, * but at least the embedded colors in filter.c will get properly * sucked up when they're encountered. */ strncpy(c_to_a_buf[whichbuf], "xxxxxxxxxxx", RGBLEN); /* RGBLEN is 11 */ l = strlen(colorName); strncpy(c_to_a_buf[whichbuf], colorName, (l < RGBLEN) ? l : RGBLEN); c_to_a_buf[whichbuf][RGBLEN] = '\0'; } return(c_to_a_buf[whichbuf]); } char * pico_get_last_fg_color(void) { char *ret = NULL; if(_last_fg_color){ size_t len; len = strlen(_last_fg_color); if((ret = (char *) fs_get((len+1) * sizeof(char))) != NULL){ strncpy(ret, _last_fg_color, len+1); ret[len] = '\0'; } } return(ret); } char * pico_get_last_bg_color(void) { char *ret = NULL; if(_last_bg_color){ size_t len; len = strlen(_last_bg_color); if((ret = (char *) fs_get((len+1) * sizeof(char))) != NULL){ strncpy(ret, _last_bg_color, len+1); ret[len] = '\0'; } } return(ret); } COLOR_PAIR * pico_get_cur_color(void) { return(new_color_pair(_last_fg_color, _last_bg_color)); } #else /* _WINDOWS */ static short _in_inverse, _in_bold, _in_uline; int pico_trans_is_on(void) { return(0); } void StartInverse() { if(!_in_inverse) mswin_rev(_in_inverse = 1); } void EndInverse() { if(_in_inverse) mswin_rev(_in_inverse = 0); } int InverseState() { return(_in_inverse); } void StartUnderline() { if(!_in_uline) mswin_uline(_in_uline = 1); } void EndUnderline() { if(_in_uline) mswin_uline(_in_uline = 0); } int StartBold() { if(!_in_bold) mswin_bold(_in_bold = 1); return(1); } void EndBold() { if(_in_bold) mswin_bold(_in_bold = 0); } #endif /* _WINDOWS */