Merge branch 'ical'
[alpine.git] / pico / osdep / color.c
blob8906794dd88b8b0211e7fab7586382dc5bea3808
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: color.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2007 University of Washington
8 * Copyright 2013-2017 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
20 #include <system.h>
21 #include <general.h>
23 #include "../headers.h"
25 #include "terminal.h"
26 #include "color.h"
27 #include "../../pith/osdep/color.h"
28 #include "../../pith/osdep/collate.h"
30 #ifndef _WINDOWS
32 struct color_name_list {
33 char *name;
34 int namelen;
35 struct color_name_list *next;
38 struct color_table {
39 struct color_name_list *names;
40 char *rgb;
41 int red, green, blue;
42 int val;
46 /* useful definitions */
47 #define ANSI8_COLOR() (color_options & COLOR_ANSI8_OPT)
48 #define ANSI16_COLOR() (color_options & COLOR_ANSI16_OPT)
49 #define ANSI256_COLOR() (color_options & COLOR_ANSI256_OPT)
50 #define ANSI_COLOR() (color_options & (COLOR_ANSI8_OPT | COLOR_ANSI16_OPT | COLOR_ANSI256_OPT))
51 /* transparent (default) color is possible */
52 #define COL_TRANS() ((color_options & COLOR_TRANS_OPT) ? 1 : 0) /* transparent */
53 #define END_PSEUDO_REVERSE "EndInverse"
54 #define A_UNKNOWN -1
57 /* local globals */
58 static unsigned color_options;
59 static COLOR_PAIR *the_rev_color, *the_normal_color;
60 static COLOR_PAIR *color_blasted_by_attrs;
61 static int pinvstate; /* physical state of inverse (standout) attribute */
62 static int pboldstate; /* physical state of bold attribute */
63 static int pulstate; /* physical state of underline attribute */
64 static int rev_color_state;
66 static int boldstate; /* should-be state of bold attribute */
67 static int ulstate; /* should-be state of underline attribute */
68 static int invstate; /* should-be state of Inverse, which could be a color
69 change or an actual setinverse */
70 static int _color_inited, _using_color;
71 static char *_nfcolor, *_nbcolor, *_rfcolor, *_rbcolor;
72 static char *_last_fg_color, *_last_bg_color;
73 static int _force_fg_color_change;
74 static int _force_bg_color_change;
76 static struct color_table *color_tbl;
78 /* external references */
79 extern char *_op, *_oc, *_setaf, *_setab, *_setf, *_setb, *_scp;
80 extern int _bce, _colors;
83 /* internal prototypes */
84 void flip_rev_color(int);
85 void flip_bold(int);
86 void flip_ul(int);
87 void reset_attr_state(void);
88 void add_to_color_name_list(struct color_table *t, char *name);
89 void free_color_name_list(struct color_name_list **nl);
92 #if HAS_TERMINFO
93 extern char *tparm(char *, ...);
94 #endif
95 extern char *tgoto();
96 void tinitcolor(void);
97 int tfgcolor(int);
98 int tbgcolor(int);
99 struct color_table *init_color_table(void);
100 void free_color_table(struct color_table **);
101 int color_to_val(char *);
106 * Start or end bold mode
108 * Result: escape sequence to go into or out of reverse color is output
110 * (This is only called when there is a reverse color.)
112 * Arg state = ON set bold
113 * OFF set normal
115 void
116 flip_rev_color(int state)
118 if((rev_color_state = state) == TRUE)
119 (void)pico_set_colorp(the_rev_color, PSC_NONE);
120 else
121 pico_set_normal_color();
126 * Start or end bold mode
128 * Result: escape sequence to go into or out of bold is output
130 * Arg state = ON set bold
131 * OFF set normal
133 void
134 flip_bold(int state)
136 extern char *_setbold, *_clearallattr;
138 if((pboldstate = state) == TRUE){
139 if(_setbold != NULL)
140 putpad(_setbold);
142 else{
143 if(_clearallattr != NULL){
144 if(!color_blasted_by_attrs)
145 color_blasted_by_attrs = pico_get_cur_color();
147 _force_fg_color_change = _force_bg_color_change = 1;
148 putpad(_clearallattr);
149 pinvstate = state;
150 pulstate = state;
151 rev_color_state = state;
158 * Start or end inverse mode
160 * Result: escape sequence to go into or out of inverse is output
162 * Arg state = ON set inverse
163 * OFF set normal
165 void
166 flip_inv(int state)
168 extern char *_setinverse, *_clearinverse;
170 if((pinvstate = state) == TRUE){
171 if(_setinverse != NULL)
172 putpad(_setinverse);
174 else{
176 * Unfortunately, some termcap entries configure end standout to
177 * be clear all attributes.
179 if(_clearinverse != NULL){
180 if(!color_blasted_by_attrs)
181 color_blasted_by_attrs = pico_get_cur_color();
183 _force_fg_color_change = _force_bg_color_change = 1;
184 putpad(_clearinverse);
185 pboldstate = (pboldstate == FALSE) ? pboldstate : A_UNKNOWN;
186 pulstate = (pulstate == FALSE) ? pulstate : A_UNKNOWN;
187 rev_color_state = A_UNKNOWN;
194 * Start or end underline mode
196 * Result: escape sequence to go into or out of underline is output
198 * Arg state = ON set underline
199 * OFF set normal
201 void
202 flip_ul(int state)
204 extern char *_setunderline, *_clearunderline;
206 if((pulstate = state) == TRUE){
207 if(_setunderline != NULL)
208 putpad(_setunderline);
210 else{
212 * Unfortunately, some termcap entries configure end underline to
213 * be clear all attributes.
215 if(_clearunderline != NULL){
216 if(!color_blasted_by_attrs)
217 color_blasted_by_attrs = pico_get_cur_color();
219 _force_fg_color_change = _force_bg_color_change = 1;
220 putpad(_clearunderline);
221 pboldstate = (pboldstate == FALSE) ? pboldstate : A_UNKNOWN;
222 pinvstate = (pinvstate == FALSE) ? pinvstate : A_UNKNOWN;
223 rev_color_state = A_UNKNOWN;
229 void
230 StartInverse(void)
232 invstate = TRUE;
233 reset_attr_state();
237 void
238 EndInverse(void)
240 invstate = FALSE;
241 reset_attr_state();
246 InverseState(void)
248 return(invstate);
253 StartBold(void)
255 extern char *_setbold;
257 boldstate = TRUE;
258 reset_attr_state();
259 return(_setbold != NULL);
263 void
264 EndBold(void)
266 boldstate = FALSE;
267 reset_attr_state();
271 void
272 StartUnderline(void)
274 ulstate = TRUE;
275 reset_attr_state();
279 void
280 EndUnderline(void)
282 ulstate = FALSE;
283 reset_attr_state();
287 void
288 reset_attr_state(void)
291 * If we have to turn some attributes off, do that first since that
292 * may turn off other attributes as a side effect.
294 if(boldstate == FALSE && pboldstate != boldstate)
295 flip_bold(boldstate);
297 if(ulstate == FALSE && pulstate != ulstate)
298 flip_ul(ulstate);
300 if(invstate == FALSE){
301 if(pico_get_rev_color()){
302 if(rev_color_state != invstate)
303 flip_rev_color(invstate);
305 else{
306 if(pinvstate != invstate)
307 flip_inv(invstate);
312 * Now turn everything on that needs turning on.
314 if(boldstate == TRUE && pboldstate != boldstate)
315 flip_bold(boldstate);
317 if(ulstate == TRUE && pulstate != ulstate)
318 flip_ul(ulstate);
320 if(invstate == TRUE){
321 if(pico_get_rev_color()){
322 if(rev_color_state != invstate)
323 flip_rev_color(invstate);
325 else{
326 if(pinvstate != invstate)
327 flip_inv(invstate);
331 if(color_blasted_by_attrs){
332 (void)pico_set_colorp(color_blasted_by_attrs, PSC_NONE);
333 free_color_pair(&color_blasted_by_attrs);
339 void
340 tinitcolor(void)
342 if(_color_inited || panicking())
343 return;
345 if(ANSI_COLOR() || (_colors > 0 && ((_setaf && _setab) || (_setf && _setb)
346 /**** not sure how to do this yet
347 || _scp
348 ****/
349 ))){
350 _color_inited = 1;
351 color_tbl = init_color_table();
353 if(ANSI_COLOR())
354 putpad("\033[39;49m");
355 else{
356 if(_op)
357 putpad(_op);
358 if(_oc)
359 putpad(_oc);
365 #if HAS_TERMCAP
367 * Treading on thin ice here. Tgoto wasn't designed for this. It takes
368 * arguments column and row. We only use one of them, so we put it in
369 * the row argument. The 0 is just a placeholder.
371 #define tparm(s, c) tgoto(s, 0, c)
372 #endif
375 tfgcolor(int color)
377 if(!_color_inited)
378 tinitcolor();
380 if(!_color_inited)
381 return(-1);
383 if((ANSI8_COLOR() && (color < 0 || color >= 8+COL_TRANS())) ||
384 (ANSI16_COLOR() && (color < 0 || color >= 16+COL_TRANS())) ||
385 (ANSI256_COLOR() && (color < 0 || color >= 256+COL_TRANS())) ||
386 (!ANSI_COLOR() && (color < 0 || color >= _colors+COL_TRANS())))
387 return(-1);
389 if(ANSI_COLOR()){
390 char buf[20];
392 if(COL_TRANS() && color == pico_count_in_color_table()-1)
393 snprintf(buf, sizeof(buf), "\033[39m");
394 else if(ANSI256_COLOR())
395 snprintf(buf, sizeof(buf), "\033[38;5;%dm", color);
396 else{
397 if(color < 8)
398 snprintf(buf, sizeof(buf), "\033[3%cm", color + '0');
399 else
400 snprintf(buf, sizeof(buf), "\033[9%cm", (color-8) + '0');
403 putpad(buf);
405 else if(COL_TRANS() && color == pico_count_in_color_table()-1 && _op){
406 char bg_color_was[MAXCOLORLEN+1];
408 bg_color_was[0] = '\0';
411 * Setting transparent (default) foreground color.
412 * We don't know how to set only the default foreground color,
413 * _op sets both foreground and background. So we need to
415 * get current background color
416 * set default colors
417 * if (current background was not default) reset it
419 if(_last_bg_color && strncmp(_last_bg_color, MATCH_TRAN_COLOR, RGBLEN)){
420 strncpy(bg_color_was, _last_bg_color, sizeof(bg_color_was));
421 bg_color_was[sizeof(bg_color_was)-1] = '\0';
424 putpad(_op);
425 if(bg_color_was[0]){
426 _force_bg_color_change = 1;
427 pico_set_bg_color(bg_color_was);
430 else if(_setaf)
431 putpad(tparm(_setaf, color));
432 else if(_setf)
433 putpad(tparm(_setf, color));
434 else if(_scp){
435 /* set color pair method */
438 return(0);
443 tbgcolor(int color)
445 if(!_color_inited)
446 tinitcolor();
448 if(!_color_inited)
449 return(-1);
451 if((ANSI8_COLOR() && (color < 0 || color >= 8+COL_TRANS())) ||
452 (ANSI16_COLOR() && (color < 0 || color >= 16+COL_TRANS())) ||
453 (ANSI256_COLOR() && (color < 0 || color >= 256+COL_TRANS())) ||
454 (!ANSI_COLOR() && (color < 0 || color >= _colors+COL_TRANS())))
455 return(-1);
457 if(ANSI_COLOR()){
458 char buf[20];
460 if(COL_TRANS() && color == pico_count_in_color_table()-1)
461 snprintf(buf, sizeof(buf), "\033[49m");
462 else if(ANSI256_COLOR())
463 snprintf(buf, sizeof(buf), "\033[48;5;%dm", color);
464 else{
465 if(color < 8)
466 snprintf(buf, sizeof(buf), "\033[4%cm", color + '0');
467 else
468 snprintf(buf, sizeof(buf), "\033[10%cm", (color-8) + '0');
471 putpad(buf);
473 else if(COL_TRANS() && color == pico_count_in_color_table()-1 && _op){
474 char fg_color_was[MAXCOLORLEN+1];
476 fg_color_was[0] = '\0';
479 * Setting transparent (default) background color.
480 * We don't know how to set only the default background color,
481 * _op sets both foreground and background. So we need to
483 * get current foreground color
484 * set default colors
485 * if (current foreground was not default) reset it
487 if(_last_fg_color && strncmp(_last_fg_color, MATCH_TRAN_COLOR, RGBLEN)){
488 strncpy(fg_color_was, _last_fg_color, sizeof(fg_color_was));
489 fg_color_was[sizeof(fg_color_was)-1] = '\0';
492 putpad(_op);
493 if(fg_color_was[0]){
494 _force_fg_color_change = 1;
495 pico_set_fg_color(fg_color_was);
498 else if(_setab)
499 putpad(tparm(_setab, color));
500 else if(_setb)
501 putpad(tparm(_setb, color));
502 else if(_scp){
503 /* set color pair method */
506 return(0);
512 * We're not actually using the RGB value other than as a string which
513 * maps into the color.
514 * In fact, on some systems color 1 and color 4 are swapped, and color 3
515 * and color 6 are swapped. So don't believe the RGB values.
516 * Still, it feels right to have them be the canonical values, so we do that.
518 * Actually we are using them more now. In color_to_val we map to the closest
519 * color if we don't get an exact match. We ignore values over 255.
521 * More than one "name" can map to the same "rgb".
522 * More than one "name" can map to the same "val".
523 * The "val" for a "name" and for its "rgb" are the same.
525 struct color_table *
526 init_color_table(void)
528 struct color_table *ct = NULL, *t;
529 int i, count;
530 char colorname[12];
532 count = pico_count_in_color_table();
534 if(count > 0 && count <= 256+COL_TRANS()){
535 int ind, graylevel;
536 struct {
537 char rgb[RGBLEN+1];
538 int red, green, blue;
539 } cube[256];
541 ct = (struct color_table *) fs_get((count+1) * sizeof(struct color_table));
542 if(ct)
543 memset(ct, 0, (count+1) * sizeof(struct color_table));
546 * We boldly assume that 256 colors means xterm 256-color
547 * color cube and grayscale.
549 if(ANSI_COLOR() && (count == 256+COL_TRANS())){
550 int r, g, b, gray;
552 for(r = 0; r < 6; r++)
553 for(g = 0; g < 6; g++)
554 for(b = 0; b < 6; b++){
555 ind = 16 + 36*r + 6*g + b;
556 cube[ind].red = r ? (40*r + 55) : 0;
557 cube[ind].green = g ? (40*g + 55) : 0;
558 cube[ind].blue = b ? (40*b + 55) : 0;
559 snprintf(cube[ind].rgb, sizeof(cube[ind].rgb), "%3.3d,%3.3d,%3.3d",
560 cube[ind].red, cube[ind].green, cube[ind].blue);
563 for(gray = 0; gray < 24; gray++){
564 ind = gray + 232;
565 graylevel = 10*gray + 8;
566 cube[ind].red = graylevel;
567 cube[ind].green = graylevel;
568 cube[ind].blue = graylevel;
569 snprintf(cube[ind].rgb, sizeof(cube[ind].rgb), "%3.3d,%3.3d,%3.3d",
570 graylevel, graylevel, graylevel);
574 for(i = 0, t = ct; t && i < count; i++, t++){
575 t->val = i;
577 switch(i){
578 case COL_BLACK:
579 strncpy(colorname, "black", sizeof(colorname));
580 colorname[sizeof(colorname)-1] = '\0';
581 break;
582 case COL_RED:
583 strncpy(colorname, "red", sizeof(colorname));
584 colorname[sizeof(colorname)-1] = '\0';
585 break;
586 case COL_GREEN:
587 strncpy(colorname, "green", sizeof(colorname));
588 colorname[sizeof(colorname)-1] = '\0';
589 break;
590 case COL_YELLOW:
591 strncpy(colorname, "yellow", sizeof(colorname));
592 colorname[sizeof(colorname)-1] = '\0';
593 break;
594 case COL_BLUE:
595 strncpy(colorname, "blue", sizeof(colorname));
596 colorname[sizeof(colorname)-1] = '\0';
597 break;
598 case COL_MAGENTA:
599 strncpy(colorname, "magenta", sizeof(colorname));
600 colorname[sizeof(colorname)-1] = '\0';
601 break;
602 case COL_CYAN:
603 strncpy(colorname, "cyan", sizeof(colorname));
604 colorname[sizeof(colorname)-1] = '\0';
605 break;
606 case COL_WHITE:
607 strncpy(colorname, "white", sizeof(colorname));
608 colorname[sizeof(colorname)-1] = '\0';
609 break;
610 default:
611 snprintf(colorname, sizeof(colorname), "color%3.3d", i);
612 break;
615 if(COL_TRANS() && i == count-1){
616 strncpy(colorname, MATCH_TRAN_COLOR, sizeof(colorname));
617 colorname[sizeof(colorname)-1] = '\0';
620 add_to_color_name_list(t, colorname);
622 if(count == 8+COL_TRANS()){
623 if(COL_TRANS() && i == count-1){
624 t->red = t->green = t->blue = -1;
626 else
627 switch(i){
628 case COL_BLACK:
629 t->red = t->green = t->blue = 0;
630 add_to_color_name_list(t, "color008");
631 add_to_color_name_list(t, "colordgr");
632 add_to_color_name_list(t, "colormgr");
633 break;
634 case COL_RED:
635 t->red = 255;
636 t->green = t->blue = 0;
637 add_to_color_name_list(t, "color009");
638 break;
639 case COL_GREEN:
640 t->green = 255;
641 t->red = t->blue = 0;
642 add_to_color_name_list(t, "color010");
643 break;
644 case COL_YELLOW:
645 t->red = t->green = 255;
646 t->blue = 0;
647 add_to_color_name_list(t, "color011");
648 break;
649 case COL_BLUE:
650 t->red = t->green = 0;
651 t->blue = 255;
652 add_to_color_name_list(t, "color012");
653 break;
654 case COL_MAGENTA:
655 t->red = t->blue = 255;
656 t->green = 0;
657 add_to_color_name_list(t, "color013");
658 break;
659 case COL_CYAN:
660 t->red = 0;
661 t->green = t->blue = 255;
662 add_to_color_name_list(t, "color014");
663 break;
664 case COL_WHITE:
665 t->red = t->green = t->blue = 255;
666 add_to_color_name_list(t, "color015");
667 add_to_color_name_list(t, "colorlgr");
668 break;
671 else if(count == 16+COL_TRANS() || count == 256+COL_TRANS()){
672 if(COL_TRANS() && i == count-1){
673 t->red = t->green = t->blue = -1;
675 else
677 * This set of RGB values seems to come close to describing
678 * what a 16-color xterm gives you.
680 switch(i){
681 case COL_BLACK:
682 t->red = t->green = t->blue = 0;
683 break;
684 case COL_RED: /* actually dark red */
685 t->red = 174;
686 t->green = t->blue = 0;
687 break;
688 case COL_GREEN: /* actually dark green */
689 t->green = 174;
690 t->red = t->blue = 0;
691 break;
692 case COL_YELLOW: /* actually dark yellow */
693 t->blue = 0;
694 t->red = t->green = 174;
695 break;
696 case COL_BLUE: /* actually dark blue */
697 t->blue = 174;
698 t->red = t->green = 0;
699 break;
700 case COL_MAGENTA: /* actually dark magenta */
701 t->green = 0;
702 t->red = t->blue = 174;
703 break;
704 case COL_CYAN: /* actually dark cyan */
705 t->red = 0;
706 t->green = t->blue = 174;
707 break;
708 case COL_WHITE: /* actually light gray */
709 t->red = t->green = t->blue = 174;
710 if(count == 16)
711 add_to_color_name_list(t, "colorlgr");
713 break;
714 case 8: /* dark gray */
715 t->red = t->green = t->blue = 64;
716 if(count == 16){
717 add_to_color_name_list(t, "colordgr");
718 add_to_color_name_list(t, "colormgr");
721 break;
722 case 9: /* red */
723 t->red = 255;
724 t->green = t->blue = 0;
725 break;
726 case 10: /* green */
727 t->green = 255;
728 t->red = t->blue = 0;
729 break;
730 case 11: /* yellow */
731 t->blue = 0;
732 t->red = t->green = 255;
733 break;
734 case 12: /* blue */
735 t->blue = 255;
736 t->red = t->green = 0;
737 break;
738 case 13: /* magenta */
739 t->green = 0;
740 t->red = t->blue = 255;
741 break;
742 case 14: /* cyan */
743 t->red = 0;
744 t->green = t->blue = 255;
745 break;
746 case 15: /* white */
747 t->red = t->green = t->blue = 255;
748 break;
749 default:
750 t->red = cube[i].red;
751 t->green = cube[i].green;
752 t->blue = cube[i].blue;
753 switch(i){
754 case 238:
755 add_to_color_name_list(t, "colordgr");
756 break;
758 case 244:
759 add_to_color_name_list(t, "colormgr");
760 break;
762 case 250:
763 add_to_color_name_list(t, "colorlgr");
764 break;
767 break;
770 else{
771 if(COL_TRANS() && i == count-1){
772 t->red = t->green = t->blue = -1;
774 else
775 switch(i){
776 case COL_BLACK:
777 t->red = t->green = t->blue = 0;
778 break;
779 case COL_RED: /* actually dark red */
780 t->red = 255;
781 t->green = t->blue = 0;
782 break;
783 case COL_GREEN: /* actually dark green */
784 t->green = 255;
785 t->red = t->blue = 0;
786 break;
787 case COL_YELLOW: /* actually dark yellow */
788 t->blue = 0;
789 t->red = t->green = 255;
790 break;
791 case COL_BLUE: /* actually dark blue */
792 t->blue = 255;
793 t->red = t->green = 0;
794 break;
795 case COL_MAGENTA: /* actually dark magenta */
796 t->green = 0;
797 t->red = t->blue = 255;
798 break;
799 case COL_CYAN: /* actually dark cyan */
800 t->red = 0;
801 t->green = t->blue = 255;
802 break;
803 case COL_WHITE: /* actually light gray */
804 t->red = t->green = t->blue = 255;
805 break;
806 default:
807 /* this will just be a string match */
808 t->red = t->green = t->blue = 256+i;
809 break;
814 for(i = 0, t = ct; t && i < count; i++, t++){
815 t->rgb = (char *)fs_get((RGBLEN+1) * sizeof(char));
816 if(COL_TRANS() && i == count-1)
817 snprintf(t->rgb, RGBLEN+1, MATCH_TRAN_COLOR);
818 else
819 snprintf(t->rgb, RGBLEN+1, "%3.3d,%3.3d,%3.3d", t->red, t->green, t->blue);
823 return(ct);
827 void
828 add_to_color_name_list(struct color_table *t, char *name)
830 if(t && name && *name){
831 struct color_name_list *new_name;
833 new_name = (struct color_name_list *) fs_get(sizeof(struct color_name_list));
834 if(new_name){
835 memset(new_name, 0, sizeof(*new_name));
836 new_name->namelen = strlen(name);
838 new_name->name = (char *) fs_get((new_name->namelen+1) * sizeof(char));
839 if(new_name->name){
840 strncpy(new_name->name, name, new_name->namelen+1);
841 new_name->name[new_name->namelen] = '\0';
843 if(t->names){
844 struct color_name_list *nl;
845 for(nl = t->names; nl->next; nl = nl->next)
848 nl->next = new_name;
850 else
851 t->names = new_name;
858 void
859 free_color_name_list(struct color_name_list **nl)
861 if(nl && *nl){
862 if((*nl)->next)
863 free_color_name_list(&(*nl)->next);
865 if((*nl)->name)
866 fs_give((void **) &(*nl)->name);
868 fs_give((void **) nl);
874 * Map from integer color value to canonical color name.
876 char *
877 colorx(int color)
879 struct color_table *ct;
880 static char cbuf[12];
882 /* before we get set up, we still need to use this a bit */
883 if(!color_tbl){
884 switch(color){
885 case COL_BLACK:
886 return("black");
887 case COL_RED:
888 return("red");
889 case COL_GREEN:
890 return("green");
891 case COL_YELLOW:
892 return("yellow");
893 case COL_BLUE:
894 return("blue");
895 case COL_MAGENTA:
896 return("magenta");
897 case COL_CYAN:
898 return("cyan");
899 case COL_WHITE:
900 return("white");
901 default:
902 snprintf(cbuf, sizeof(cbuf), "color%3.3d", color);
903 return(cbuf);
907 for(ct = color_tbl; ct->names; ct++)
908 if(ct->val == color)
909 break;
911 /* rgb _is_ the canonical name */
912 if(ct->names)
913 return(ct->rgb);
915 /* not supposed to get here */
916 snprintf(cbuf, sizeof(cbuf), "color%3.3d", color);
917 return(cbuf);
922 * Argument is a color name which could be an RGB string, a name like "blue",
923 * or a name like "color011".
925 * Returns a pointer to the canonical name of the color.
927 char *
928 color_to_canonical_name(char *s)
930 struct color_table *ct;
931 struct color_name_list *nl;
932 int done;
934 if(!s || !color_tbl)
935 return(NULL);
937 if(*s == ' ' || isdigit(*s)){
938 /* check for rgb string instead of name */
939 for(ct = color_tbl; ct->rgb; ct++)
940 if(!strncmp(ct->rgb, s, RGBLEN))
941 break;
943 else{
944 for(done=0, ct = color_tbl; !done && ct->names; ct++){
945 for(nl = ct->names; !done && nl; nl = nl->next)
946 if(nl->name && !struncmp(nl->name, s, nl->namelen))
947 done++;
949 if(done)
950 break;
954 /* rgb is the canonical name */
955 if(ct->names)
956 return(ct->rgb);
957 else if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN) || !struncmp(s, MATCH_NONE_COLOR, RGBLEN))
958 return(s);
960 return("");
965 * Argument is the name of a color or an RGB value that we recognize.
966 * The table should be set up so that the val returned is the same whether
967 * or not we choose the canonical name.
969 * Returns the integer value for the color.
972 color_to_val(char *s)
974 struct color_table *ct;
975 struct color_name_list *nl;
976 int done;
978 if(!s || !color_tbl)
979 return(-1);
981 if(*s == ' ' || isdigit(*s)){
982 /* check for rgb string instead of name */
983 for(ct = color_tbl; ct->rgb; ct++)
984 if(!strncmp(ct->rgb, s, RGBLEN))
985 break;
988 * Didn't match any. Find "closest" to match.
990 if(!ct->rgb){
991 int r = -1, g = -1, b = -1;
992 char *p, *comma, scopy[RGBLEN+1];
994 strncpy(scopy, s, sizeof(scopy));
995 scopy[sizeof(scopy)-1] = '\0';
997 p = scopy;
998 comma = strchr(p, ',');
999 if(comma){
1000 *comma = '\0';
1001 r = atoi(p);
1002 p = comma+1;
1003 if(r >= 0 && r <= 255 && *p){
1004 comma = strchr(p, ',');
1005 if(comma){
1006 *comma = '\0';
1007 g = atoi(p);
1008 p = comma+1;
1009 if(g >= 0 && g <= 255 && *p){
1010 b = atoi(p);
1016 if(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255){
1017 struct color_table *closest = NULL;
1018 int closest_value = 1000000;
1019 int cv;
1021 for(ct = color_tbl; ct->rgb; ct++){
1023 if(ct->red >= 0 && ct->red <= 255
1024 && ct->green >= 0 && ct->green <= 255
1025 && ct->blue >= 0 && ct->blue <= 255){
1026 cv = (ct->red - r) * (ct->red - r) +
1027 (ct->green - g) * (ct->green - g) +
1028 (ct->blue - b) * (ct->blue - b);
1029 if(cv < closest_value){
1030 closest_value = cv;
1031 closest = ct;
1036 if(closest)
1037 ct = closest;
1041 else{
1042 for(done=0, ct = color_tbl; !done && ct->names; ct++){
1043 for(nl = ct->names; !done && nl; nl = nl->next)
1044 if(nl->name && !struncmp(nl->name, s, nl->namelen))
1045 done++;
1047 if(done)
1048 break;
1052 if(ct->names)
1053 return(ct->val);
1054 else
1055 return(-1);
1059 void
1060 free_color_table(struct color_table **ctbl)
1062 struct color_table *t;
1064 if(ctbl && *ctbl){
1065 for(t = *ctbl; t->names; t++){
1066 free_color_name_list(&t->names);
1068 if(t->rgb)
1069 fs_give((void **) &t->rgb);
1072 fs_give((void **) ctbl);
1078 pico_count_in_color_table(void)
1080 return(
1081 (ANSI_COLOR()
1082 ? (ANSI8_COLOR() ? 8 : ANSI16_COLOR() ? 16 : 256)
1083 : _colors)
1084 + COL_TRANS());
1088 void
1089 pico_nfcolor(char *s)
1091 if(_nfcolor)
1092 fs_give((void **) &_nfcolor);
1094 if(s){
1095 size_t len;
1097 len = strlen(s);
1098 _nfcolor = (char *) fs_get((len+1) * sizeof(char));
1099 if(_nfcolor){
1100 strncpy(_nfcolor, s, len+1);
1101 _nfcolor[len] = '\0';
1104 if(the_normal_color){
1105 strncpy(the_normal_color->fg, _nfcolor, MAXCOLORLEN+1);
1106 the_normal_color->fg[MAXCOLORLEN] = '\0';
1109 else if(the_normal_color)
1110 free_color_pair(&the_normal_color);
1114 void
1115 pico_nbcolor(char *s)
1117 if(_nbcolor)
1118 fs_give((void **) &_nbcolor);
1120 if(s){
1121 size_t len;
1123 len = strlen(s);
1124 _nbcolor = (char *) fs_get((len+1) * sizeof(char));
1125 if(_nbcolor){
1126 strncpy(_nbcolor, s, len+1);
1127 _nbcolor[len] = '\0';
1130 if(the_normal_color){
1131 strncpy(the_normal_color->bg, _nbcolor, MAXCOLORLEN+1);
1132 the_normal_color->bg[MAXCOLORLEN] = '\0';
1135 else if(the_normal_color)
1136 free_color_pair(&the_normal_color);
1139 void
1140 pico_rfcolor(char *s)
1142 if(_rfcolor)
1143 fs_give((void **) &_rfcolor);
1145 if(s){
1146 size_t len;
1148 len = strlen(s);
1149 _rfcolor = (char *) fs_get((len+1) * sizeof(char));
1150 if(_rfcolor){
1151 strncpy(_rfcolor, s, len+1);
1152 _rfcolor[len] = '\0';
1155 if(the_rev_color){
1156 strncpy(the_rev_color->fg, _rfcolor, MAXCOLORLEN+1);
1157 the_rev_color->fg[MAXCOLORLEN] = '\0';
1160 else if(the_rev_color)
1161 free_color_pair(&the_rev_color);
1164 void
1165 pico_rbcolor(char *s)
1167 if(_rbcolor)
1168 fs_give((void **) &_rbcolor);
1170 if(s){
1171 size_t len;
1173 len = strlen(s);
1174 _rbcolor = (char *) fs_get((len+1) * sizeof(char));
1175 if(_rbcolor){
1176 strncpy(_rbcolor, s, len+1);
1177 _rbcolor[len] = '\0';
1180 if(the_rev_color){
1181 strncpy(the_rev_color->bg, _rbcolor, MAXCOLORLEN+1);
1182 the_rev_color->bg[MAXCOLORLEN] = '\0';
1185 else if(the_rev_color)
1186 free_color_pair(&the_rev_color);
1191 pico_hascolor(void)
1193 if(!_color_inited)
1194 tinitcolor();
1196 return(_color_inited);
1201 pico_usingcolor(void)
1203 return(_using_color && pico_hascolor());
1208 * This should only be called when we're using the
1209 * unix termdef color, as opposed to the ANSI defined
1210 * color stuff or the Windows stuff.
1213 pico_trans_color(void)
1215 return(_bce && _op);
1219 void
1220 pico_toggle_color(int on)
1222 if(on){
1223 if(pico_hascolor())
1224 _using_color = 1;
1226 else{
1227 _using_color = 0;
1228 if(_color_inited){
1229 _color_inited = 0;
1230 if(!panicking())
1231 free_color_table(&color_tbl);
1233 if(ANSI_COLOR())
1234 putpad("\033[39;49m");
1235 else{
1236 if(_op)
1237 putpad(_op);
1238 if(_oc)
1239 putpad(_oc);
1246 unsigned
1247 pico_get_color_options(void)
1249 return(color_options);
1254 pico_trans_is_on(void)
1256 return(COL_TRANS());
1261 * Absolute set of options. Caller has to OR things together and so forth.
1263 void
1264 pico_set_color_options(unsigned flags)
1266 color_options = flags;
1269 void
1270 pico_endcolor(void)
1272 pico_toggle_color(0);
1273 if(panicking())
1274 return;
1276 if(_nfcolor)
1277 fs_give((void **) &_nfcolor);
1279 if(_nbcolor)
1280 fs_give((void **) &_nbcolor);
1282 if(_rfcolor)
1283 fs_give((void **) &_rfcolor);
1285 if(_rbcolor)
1286 fs_give((void **) &_rbcolor);
1288 if(_last_fg_color)
1289 fs_give((void **) &_last_fg_color);
1291 if(_last_bg_color)
1292 fs_give((void **) &_last_bg_color);
1294 if(the_rev_color)
1295 free_color_pair(&the_rev_color);
1297 if(the_normal_color)
1298 free_color_pair(&the_normal_color);
1302 void
1303 pico_set_nfg_color(void)
1305 if(_nfcolor)
1306 (void)pico_set_fg_color(_nfcolor);
1310 void
1311 pico_set_nbg_color(void)
1313 if(_nbcolor)
1314 (void)pico_set_bg_color(_nbcolor);
1318 void
1319 pico_set_normal_color(void)
1321 if(!_nfcolor || !_nbcolor ||
1322 !pico_set_fg_color(_nfcolor) || !pico_set_bg_color(_nbcolor)){
1323 (void)pico_set_fg_color(DEFAULT_NORM_FORE_RGB);
1324 (void)pico_set_bg_color(DEFAULT_NORM_BACK_RGB);
1330 * If inverse is a color, returns a pointer to that color.
1331 * If not, returns NULL.
1333 * NOTE: Don't free this!
1335 COLOR_PAIR *
1336 pico_get_rev_color(void)
1338 if(pico_usingcolor() && _rfcolor && _rbcolor &&
1339 pico_is_good_color(_rfcolor) && pico_is_good_color(_rbcolor)){
1340 if(!the_rev_color)
1341 the_rev_color = new_color_pair(_rfcolor, _rbcolor);
1343 return(the_rev_color);
1345 else
1346 return(NULL);
1351 * Returns a pointer to the normal color.
1353 * NOTE: Don't free this!
1355 COLOR_PAIR *
1356 pico_get_normal_color(void)
1358 if(pico_usingcolor() && _nfcolor && _nbcolor &&
1359 pico_is_good_color(_nfcolor) && pico_is_good_color(_nbcolor)){
1360 if(!the_normal_color)
1361 the_normal_color = new_color_pair(_nfcolor, _nbcolor);
1363 return(the_normal_color);
1365 else
1366 return(NULL);
1371 * Sets color to (fg,bg).
1372 * Flags == PSC_NONE No alternate default if fg,bg fails.
1373 * == PSC_NORM Set it to Normal color on failure.
1374 * == PSC_REV Set it to Reverse color on failure.
1376 * If flag PSC_RET is set, returns an allocated copy of the previous
1377 * color pair, otherwise returns NULL.
1379 COLOR_PAIR *
1380 pico_set_colors(char *fg, char *bg, int flags)
1382 int uc;
1383 COLOR_PAIR *cp = NULL, *rev = NULL;
1385 if(flags & PSC_RET)
1386 cp = pico_get_cur_color();
1388 if(fg && !strcmp(fg, END_PSEUDO_REVERSE)){
1389 EndInverse();
1390 if(cp)
1391 free_color_pair(&cp);
1393 else if(!((uc=pico_usingcolor()) && fg && bg &&
1394 pico_set_fg_color(fg) && pico_set_bg_color(bg))){
1396 if(uc && flags & PSC_NORM)
1397 pico_set_normal_color();
1398 else if(flags & PSC_REV){
1399 if((rev = pico_get_rev_color()) != NULL){
1400 pico_set_fg_color(rev->fg); /* these will succeed */
1401 pico_set_bg_color(rev->bg);
1403 else{
1404 StartInverse();
1405 if(cp){
1406 strncpy(cp->fg, END_PSEUDO_REVERSE, MAXCOLORLEN+1);
1407 cp->fg[MAXCOLORLEN] = '\0';
1408 strncpy(cp->bg, END_PSEUDO_REVERSE, MAXCOLORLEN+1);
1409 cp->bg[MAXCOLORLEN] = '\0';
1415 return(cp);
1420 pico_is_good_color(char *s)
1422 struct color_table *ct;
1423 struct color_name_list *nl;
1424 int done;
1426 if(!s || !color_tbl)
1427 return(FALSE);
1429 if(!strcmp(s, END_PSEUDO_REVERSE))
1430 return(TRUE);
1431 else if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN) || !struncmp(s, MATCH_NONE_COLOR, RGBLEN))
1432 return(TRUE);
1433 else if(*s == ' ' || isdigit(*s)){
1434 /* check for rgb string instead of name */
1435 for(ct = color_tbl; ct->rgb; ct++)
1436 if(!strncmp(ct->rgb, s, RGBLEN))
1437 break;
1439 /* if no match it's still ok if rgb */
1440 if(!ct->rgb){
1441 int r = -1, g = -1, b = -1;
1442 char *p, *comma, scopy[RGBLEN+1];
1444 strncpy(scopy, s, sizeof(scopy));
1445 scopy[sizeof(scopy)-1] = '\0';
1447 p = scopy;
1448 comma = strchr(p, ',');
1449 if(comma){
1450 *comma = '\0';
1451 r = atoi(p);
1452 p = comma+1;
1453 if(r >= 0 && r <= 255 && *p){
1454 comma = strchr(p, ',');
1455 if(comma){
1456 *comma = '\0';
1457 g = atoi(p);
1458 p = comma+1;
1459 if(g >= 0 && g <= 255 && *p){
1460 b = atoi(p);
1466 if(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255)
1467 ct = color_tbl; /* to force TRUE */
1470 else{
1471 for(done=0, ct = color_tbl; !done && ct->names; ct++){
1472 for(nl = ct->names; !done && nl; nl = nl->next)
1473 if(nl->name && !struncmp(nl->name, s, nl->namelen))
1474 done++;
1476 if(done)
1477 break;
1481 return(ct->names ? TRUE : FALSE);
1486 * Return TRUE on success.
1489 pico_set_fg_color(char *s)
1491 int val;
1493 if(!s || !color_tbl)
1494 return(FALSE);
1496 if(!strcmp(s, END_PSEUDO_REVERSE)){
1497 EndInverse();
1498 return(TRUE);
1501 if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN))
1502 s = _nfcolor;
1503 else if(!struncmp(s, MATCH_NONE_COLOR, RGBLEN))
1504 return(TRUE);
1506 if((val = color_to_val(s)) >= 0){
1507 size_t len;
1508 int changed;
1510 changed = !_last_fg_color || strcmp(_last_fg_color,colorx(val));
1512 /* already set correctly */
1513 if(!_force_fg_color_change && !changed)
1514 return(TRUE);
1516 _force_fg_color_change = 0;
1518 if(changed){
1519 if(_last_fg_color)
1520 fs_give((void **) &_last_fg_color);
1522 len = strlen(colorx(val));
1523 if((_last_fg_color = (char *) fs_get((len+1) * sizeof(char))) != NULL){
1524 strncpy(_last_fg_color, colorx(val), len+1);
1525 _last_fg_color[len] = '\0';
1529 tfgcolor(val);
1530 return(TRUE);
1532 else
1533 return(FALSE);
1538 pico_set_bg_color(char *s)
1540 int val;
1542 if(!s || !color_tbl)
1543 return(FALSE);
1545 if(!strcmp(s, END_PSEUDO_REVERSE)){
1546 EndInverse();
1547 return(TRUE);
1550 if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN))
1551 s = _nbcolor;
1552 else if(!struncmp(s, MATCH_NONE_COLOR, RGBLEN))
1553 return(TRUE);
1555 if((val = color_to_val(s)) >= 0){
1556 size_t len;
1557 int changed;
1559 changed = !_last_bg_color || strcmp(_last_bg_color,colorx(val));
1561 /* already set correctly */
1562 if(!_force_bg_color_change && !changed)
1563 return(TRUE);
1565 _force_bg_color_change = 0;
1567 if(changed){
1568 if(_last_bg_color)
1569 fs_give((void **) &_last_bg_color);
1571 len = strlen(colorx(val));
1572 if((_last_bg_color = (char *) fs_get((len+1) * sizeof(char))) != NULL){
1573 strncpy(_last_bg_color, colorx(val), len+1);
1574 _last_bg_color[len] = '\0';
1578 tbgcolor(val);
1579 return(TRUE);
1581 else
1582 return(FALSE);
1587 * Return a pointer to an rgb string for the input color. The output is 11
1588 * characters long and looks like rrr,ggg,bbb.
1590 * Args colorName -- The color to convert to ascii rgb.
1592 * Returns Pointer to a static buffer containing the rgb string. Can use up
1593 * to three returned values at once before the first is overwritten.
1595 char *
1596 color_to_asciirgb(char *colorName)
1598 static char c_to_a_buf[3][RGBLEN+1];
1599 static int whichbuf = 0;
1600 struct color_table *ct;
1601 struct color_name_list *nl;
1602 int done;
1604 whichbuf = (whichbuf + 1) % 3;
1606 c_to_a_buf[whichbuf][0] = '\0';
1608 for(done=0, ct = color_tbl; !done && ct->names; ct++){
1609 for(nl = ct->names; !done && nl; nl = nl->next)
1610 if(nl->name && !struncmp(nl->name, colorName, nl->namelen))
1611 done++;
1613 if(done)
1614 break;
1617 if(ct && ct->names){
1618 strncpy(c_to_a_buf[whichbuf], ct->rgb, sizeof(c_to_a_buf[0]));
1619 c_to_a_buf[whichbuf][sizeof(c_to_a_buf[0])-1] = '\0';
1621 else if(*colorName == ' ' || isdigit(*colorName)){
1622 /* check for rgb string instead of name */
1623 for(ct = color_tbl; ct->rgb; ct++)
1624 if(!strncmp(ct->rgb, colorName, RGBLEN))
1625 break;
1627 /* if no match it's still ok if rgb */
1628 if(!ct->rgb){
1629 int r = -1, g = -1, b = -1;
1630 char *p, *comma, scopy[RGBLEN+1];
1632 strncpy(scopy, colorName, sizeof(scopy));
1633 scopy[sizeof(scopy)-1] = '\0';
1635 p = scopy;
1636 comma = strchr(p, ',');
1637 if(comma){
1638 *comma = '\0';
1639 r = atoi(p);
1640 p = comma+1;
1641 if(r >= 0 && r <= 255 && *p){
1642 comma = strchr(p, ',');
1643 if(comma){
1644 *comma = '\0';
1645 g = atoi(p);
1646 p = comma+1;
1647 if(g >= 0 && g <= 255 && *p){
1648 b = atoi(p);
1654 if(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255){
1655 /* it was already RGB */
1656 snprintf(c_to_a_buf[whichbuf], sizeof(c_to_a_buf[0]), "%3.3d,%3.3d,%3.3d", r, g, b);
1659 else{
1660 strncpy(c_to_a_buf[whichbuf], ct->rgb, sizeof(c_to_a_buf[0]));
1661 c_to_a_buf[whichbuf][sizeof(c_to_a_buf[0])-1] = '\0';
1665 if(!c_to_a_buf[whichbuf][0]){
1666 int l;
1669 * If we didn't find the color it could be that it is the
1670 * normal color (MATCH_NORM_COLOR) or the none color
1671 * (MATCH_NONE_COLOR). If that is the case, this strncpy thing
1672 * will work out correctly because those two strings are
1673 * RGBLEN long. Otherwise we're in a bit of trouble. This
1674 * most likely means that the user is using the same pinerc on
1675 * two terminals, one with more colors than the other. We didn't
1676 * find a match because this color isn't present on this terminal.
1677 * Since the return value of this function is assumed to be
1678 * RGBLEN long, we'd better make it that long.
1679 * It still won't work correctly because colors will be screwed up,
1680 * but at least the embedded colors in filter.c will get properly
1681 * sucked up when they're encountered.
1683 strncpy(c_to_a_buf[whichbuf], "xxxxxxxxxxx", RGBLEN); /* RGBLEN is 11 */
1684 l = strlen(colorName);
1685 strncpy(c_to_a_buf[whichbuf], colorName, (l < RGBLEN) ? l : RGBLEN);
1686 c_to_a_buf[whichbuf][RGBLEN] = '\0';
1689 return(c_to_a_buf[whichbuf]);
1693 char *
1694 pico_get_last_fg_color(void)
1696 char *ret = NULL;
1698 if(_last_fg_color){
1699 size_t len;
1701 len = strlen(_last_fg_color);
1702 if((ret = (char *) fs_get((len+1) * sizeof(char))) != NULL){
1703 strncpy(ret, _last_fg_color, len+1);
1704 ret[len] = '\0';
1708 return(ret);
1712 char *
1713 pico_get_last_bg_color(void)
1715 char *ret = NULL;
1717 if(_last_bg_color){
1718 size_t len;
1720 len = strlen(_last_bg_color);
1721 if((ret = (char *) fs_get((len+1) * sizeof(char))) != NULL){
1722 strncpy(ret, _last_bg_color, len+1);
1723 ret[len] = '\0';
1727 return(ret);
1731 COLOR_PAIR *
1732 pico_get_cur_color(void)
1734 return(new_color_pair(_last_fg_color, _last_bg_color));
1737 #else /* _WINDOWS */
1738 static short _in_inverse, _in_bold, _in_uline;
1741 pico_trans_is_on(void)
1743 return(0);
1746 void
1747 StartInverse()
1749 if(!_in_inverse)
1750 mswin_rev(_in_inverse = 1);
1753 void
1754 EndInverse()
1756 if(_in_inverse)
1757 mswin_rev(_in_inverse = 0);
1761 InverseState()
1763 return(_in_inverse);
1766 void
1767 StartUnderline()
1769 if(!_in_uline)
1770 mswin_uline(_in_uline = 1);
1773 void
1774 EndUnderline()
1776 if(_in_uline)
1777 mswin_uline(_in_uline = 0);
1781 StartBold()
1783 if(!_in_bold)
1784 mswin_bold(_in_bold = 1);
1786 return(1);
1789 void
1790 EndBold()
1792 if(_in_bold)
1793 mswin_bold(_in_bold = 0);
1797 #endif /* _WINDOWS */