Add support for tab-completion when selecting by rule
[alpine.git] / pico / osdep / color.c
blob9b9f40d64fc14909ab3b36a8cf10b822891c0f70
1 /*
2 * ========================================================================
3 * Copyright 2006-2007 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
16 #include <system.h>
17 #include <general.h>
19 #include "../headers.h"
21 #include "terminal.h"
22 #include "color.h"
23 #include "../../pith/osdep/color.h"
24 #include "../../pith/osdep/collate.h"
26 #ifndef _WINDOWS
28 struct color_name_list {
29 char *name;
30 int namelen;
31 struct color_name_list *next;
34 struct color_table {
35 struct color_name_list *names;
36 char *rgb;
37 int red, green, blue;
38 int val;
42 /* useful definitions */
43 #define ANSI8_COLOR() (color_options & COLOR_ANSI8_OPT)
44 #define ANSI16_COLOR() (color_options & COLOR_ANSI16_OPT)
45 #define ANSI256_COLOR() (color_options & COLOR_ANSI256_OPT)
46 #define ANSI_COLOR() (color_options & (COLOR_ANSI8_OPT | COLOR_ANSI16_OPT | COLOR_ANSI256_OPT))
47 /* transparent (default) color is possible */
48 #define COL_TRANS() ((color_options & COLOR_TRANS_OPT) ? 1 : 0) /* transparent */
49 #define END_PSEUDO_REVERSE "EndInverse"
50 #define A_UNKNOWN -1
53 /* local globals */
54 static unsigned color_options;
55 static COLOR_PAIR *the_rev_color, *the_normal_color;
56 static COLOR_PAIR *color_blasted_by_attrs;
57 static int pinvstate; /* physical state of inverse (standout) attribute */
58 static int pboldstate; /* physical state of bold attribute */
59 static int pulstate; /* physical state of underline attribute */
60 static int rev_color_state;
62 static int boldstate; /* should-be state of bold attribute */
63 static int ulstate; /* should-be state of underline attribute */
64 static int invstate; /* should-be state of Inverse, which could be a color
65 change or an actual setinverse */
66 static int _color_inited, _using_color;
67 static char *_nfcolor, *_nbcolor, *_rfcolor, *_rbcolor;
68 static char *_last_fg_color, *_last_bg_color;
69 static int _force_fg_color_change;
70 static int _force_bg_color_change;
72 static struct color_table *color_tbl;
74 /* external references */
75 extern char *_op, *_oc, *_setaf, *_setab, *_setf, *_setb, *_scp;
76 extern int _bce, _colors;
79 /* internal prototypes */
80 void flip_rev_color(int);
81 void flip_bold(int);
82 void flip_ul(int);
83 void reset_attr_state(void);
84 void add_to_color_name_list(struct color_table *t, char *name);
85 void free_color_name_list(struct color_name_list **nl);
88 #if HAS_TERMINFO
89 extern char *tparm(char *, ...);
90 #endif
91 extern char *tgoto();
92 void tinitcolor(void);
93 int tfgcolor(int);
94 int tbgcolor(int);
95 struct color_table *init_color_table(void);
96 void free_color_table(struct color_table **);
97 int color_to_val(char *);
102 * Start or end bold mode
104 * Result: escape sequence to go into or out of reverse color is output
106 * (This is only called when there is a reverse color.)
108 * Arg state = ON set bold
109 * OFF set normal
111 void
112 flip_rev_color(int state)
114 if((rev_color_state = state) == TRUE)
115 (void)pico_set_colorp(the_rev_color, PSC_NONE);
116 else
117 pico_set_normal_color();
122 * Start or end bold mode
124 * Result: escape sequence to go into or out of bold is output
126 * Arg state = ON set bold
127 * OFF set normal
129 void
130 flip_bold(int state)
132 extern char *_setbold, *_clearallattr;
134 if((pboldstate = state) == TRUE){
135 if(_setbold != NULL)
136 putpad(_setbold);
138 else{
139 if(_clearallattr != NULL){
140 if(!color_blasted_by_attrs)
141 color_blasted_by_attrs = pico_get_cur_color();
143 _force_fg_color_change = _force_bg_color_change = 1;
144 putpad(_clearallattr);
145 pinvstate = state;
146 pulstate = state;
147 rev_color_state = state;
154 * Start or end inverse mode
156 * Result: escape sequence to go into or out of inverse is output
158 * Arg state = ON set inverse
159 * OFF set normal
161 void
162 flip_inv(int state)
164 extern char *_setinverse, *_clearinverse;
166 if((pinvstate = state) == TRUE){
167 if(_setinverse != NULL)
168 putpad(_setinverse);
170 else{
172 * Unfortunately, some termcap entries configure end standout to
173 * be clear all attributes.
175 if(_clearinverse != NULL){
176 if(!color_blasted_by_attrs)
177 color_blasted_by_attrs = pico_get_cur_color();
179 _force_fg_color_change = _force_bg_color_change = 1;
180 putpad(_clearinverse);
181 pboldstate = (pboldstate == FALSE) ? pboldstate : A_UNKNOWN;
182 pulstate = (pulstate == FALSE) ? pulstate : A_UNKNOWN;
183 rev_color_state = A_UNKNOWN;
190 * Start or end underline mode
192 * Result: escape sequence to go into or out of underline is output
194 * Arg state = ON set underline
195 * OFF set normal
197 void
198 flip_ul(int state)
200 extern char *_setunderline, *_clearunderline;
202 if((pulstate = state) == TRUE){
203 if(_setunderline != NULL)
204 putpad(_setunderline);
206 else{
208 * Unfortunately, some termcap entries configure end underline to
209 * be clear all attributes.
211 if(_clearunderline != NULL){
212 if(!color_blasted_by_attrs)
213 color_blasted_by_attrs = pico_get_cur_color();
215 _force_fg_color_change = _force_bg_color_change = 1;
216 putpad(_clearunderline);
217 pboldstate = (pboldstate == FALSE) ? pboldstate : A_UNKNOWN;
218 pinvstate = (pinvstate == FALSE) ? pinvstate : A_UNKNOWN;
219 rev_color_state = A_UNKNOWN;
225 void
226 StartInverse(void)
228 invstate = TRUE;
229 reset_attr_state();
233 void
234 EndInverse(void)
236 invstate = FALSE;
237 reset_attr_state();
242 InverseState(void)
244 return(invstate);
249 StartBold(void)
251 extern char *_setbold;
253 boldstate = TRUE;
254 reset_attr_state();
255 return(_setbold != NULL);
259 void
260 EndBold(void)
262 boldstate = FALSE;
263 reset_attr_state();
267 void
268 StartUnderline(void)
270 ulstate = TRUE;
271 reset_attr_state();
275 void
276 EndUnderline(void)
278 ulstate = FALSE;
279 reset_attr_state();
283 void
284 reset_attr_state(void)
287 * If we have to turn some attributes off, do that first since that
288 * may turn off other attributes as a side effect.
290 if(boldstate == FALSE && pboldstate != boldstate)
291 flip_bold(boldstate);
293 if(ulstate == FALSE && pulstate != ulstate)
294 flip_ul(ulstate);
296 if(invstate == FALSE){
297 if(pico_get_rev_color()){
298 if(rev_color_state != invstate)
299 flip_rev_color(invstate);
301 else{
302 if(pinvstate != invstate)
303 flip_inv(invstate);
308 * Now turn everything on that needs turning on.
310 if(boldstate == TRUE && pboldstate != boldstate)
311 flip_bold(boldstate);
313 if(ulstate == TRUE && pulstate != ulstate)
314 flip_ul(ulstate);
316 if(invstate == TRUE){
317 if(pico_get_rev_color()){
318 if(rev_color_state != invstate)
319 flip_rev_color(invstate);
321 else{
322 if(pinvstate != invstate)
323 flip_inv(invstate);
327 if(color_blasted_by_attrs){
328 (void)pico_set_colorp(color_blasted_by_attrs, PSC_NONE);
329 free_color_pair(&color_blasted_by_attrs);
335 void
336 tinitcolor(void)
338 if(_color_inited || panicking())
339 return;
341 if(ANSI_COLOR() || (_colors > 0 && ((_setaf && _setab) || (_setf && _setb)
342 /**** not sure how to do this yet
343 || _scp
344 ****/
345 ))){
346 _color_inited = 1;
347 color_tbl = init_color_table();
349 if(ANSI_COLOR())
350 putpad("\033[39;49m");
351 else{
352 if(_op)
353 putpad(_op);
354 if(_oc)
355 putpad(_oc);
361 #if HAS_TERMCAP
363 * Treading on thin ice here. Tgoto wasn't designed for this. It takes
364 * arguments column and row. We only use one of them, so we put it in
365 * the row argument. The 0 is just a placeholder.
367 #define tparm(s, c) tgoto(s, 0, c)
368 #endif
371 tfgcolor(int color)
373 if(!_color_inited)
374 tinitcolor();
376 if(!_color_inited)
377 return(-1);
379 if((ANSI8_COLOR() && (color < 0 || color >= 8+COL_TRANS())) ||
380 (ANSI16_COLOR() && (color < 0 || color >= 16+COL_TRANS())) ||
381 (ANSI256_COLOR() && (color < 0 || color >= 256+COL_TRANS())) ||
382 (!ANSI_COLOR() && (color < 0 || color >= _colors+COL_TRANS())))
383 return(-1);
385 if(ANSI_COLOR()){
386 char buf[20];
388 if(COL_TRANS() && color == pico_count_in_color_table()-1)
389 snprintf(buf, sizeof(buf), "\033[39m");
390 else if(ANSI256_COLOR())
391 snprintf(buf, sizeof(buf), "\033[38;5;%dm", color);
392 else{
393 if(color < 8)
394 snprintf(buf, sizeof(buf), "\033[3%cm", color + '0');
395 else
396 snprintf(buf, sizeof(buf), "\033[9%cm", (color-8) + '0');
399 putpad(buf);
401 else if(COL_TRANS() && color == pico_count_in_color_table()-1 && _op){
402 char bg_color_was[MAXCOLORLEN+1];
404 bg_color_was[0] = '\0';
407 * Setting transparent (default) foreground color.
408 * We don't know how to set only the default foreground color,
409 * _op sets both foreground and background. So we need to
411 * get current background color
412 * set default colors
413 * if (current background was not default) reset it
415 if(_last_bg_color && strncmp(_last_bg_color, MATCH_TRAN_COLOR, RGBLEN)){
416 strncpy(bg_color_was, _last_bg_color, sizeof(bg_color_was));
417 bg_color_was[sizeof(bg_color_was)-1] = '\0';
420 putpad(_op);
421 if(bg_color_was[0]){
422 _force_bg_color_change = 1;
423 pico_set_bg_color(bg_color_was);
426 else if(_setaf)
427 putpad(tparm(_setaf, color));
428 else if(_setf)
429 putpad(tparm(_setf, color));
430 else if(_scp){
431 /* set color pair method */
434 return(0);
439 tbgcolor(int color)
441 if(!_color_inited)
442 tinitcolor();
444 if(!_color_inited)
445 return(-1);
447 if((ANSI8_COLOR() && (color < 0 || color >= 8+COL_TRANS())) ||
448 (ANSI16_COLOR() && (color < 0 || color >= 16+COL_TRANS())) ||
449 (ANSI256_COLOR() && (color < 0 || color >= 256+COL_TRANS())) ||
450 (!ANSI_COLOR() && (color < 0 || color >= _colors+COL_TRANS())))
451 return(-1);
453 if(ANSI_COLOR()){
454 char buf[20];
456 if(COL_TRANS() && color == pico_count_in_color_table()-1)
457 snprintf(buf, sizeof(buf), "\033[49m");
458 else if(ANSI256_COLOR())
459 snprintf(buf, sizeof(buf), "\033[48;5;%dm", color);
460 else{
461 if(color < 8)
462 snprintf(buf, sizeof(buf), "\033[4%cm", color + '0');
463 else
464 snprintf(buf, sizeof(buf), "\033[10%cm", (color-8) + '0');
467 putpad(buf);
469 else if(COL_TRANS() && color == pico_count_in_color_table()-1 && _op){
470 char fg_color_was[MAXCOLORLEN+1];
472 fg_color_was[0] = '\0';
475 * Setting transparent (default) background color.
476 * We don't know how to set only the default background color,
477 * _op sets both foreground and background. So we need to
479 * get current foreground color
480 * set default colors
481 * if (current foreground was not default) reset it
483 if(_last_fg_color && strncmp(_last_fg_color, MATCH_TRAN_COLOR, RGBLEN)){
484 strncpy(fg_color_was, _last_fg_color, sizeof(fg_color_was));
485 fg_color_was[sizeof(fg_color_was)-1] = '\0';
488 putpad(_op);
489 if(fg_color_was[0]){
490 _force_fg_color_change = 1;
491 pico_set_fg_color(fg_color_was);
494 else if(_setab)
495 putpad(tparm(_setab, color));
496 else if(_setb)
497 putpad(tparm(_setb, color));
498 else if(_scp){
499 /* set color pair method */
502 return(0);
508 * We're not actually using the RGB value other than as a string which
509 * maps into the color.
510 * In fact, on some systems color 1 and color 4 are swapped, and color 3
511 * and color 6 are swapped. So don't believe the RGB values.
512 * Still, it feels right to have them be the canonical values, so we do that.
514 * Actually we are using them more now. In color_to_val we map to the closest
515 * color if we don't get an exact match. We ignore values over 255.
517 * More than one "name" can map to the same "rgb".
518 * More than one "name" can map to the same "val".
519 * The "val" for a "name" and for its "rgb" are the same.
521 struct color_table *
522 init_color_table(void)
524 struct color_table *ct = NULL, *t;
525 int i, count;
526 char colorname[22];
528 count = pico_count_in_color_table();
530 if(count > 0 && count <= 256+COL_TRANS()){
531 int ind, graylevel;
532 struct {
533 char rgb[RGBLEN+1];
534 int red, green, blue;
535 } cube[256];
537 ct = (struct color_table *) fs_get((count+1) * sizeof(struct color_table));
538 if(ct)
539 memset(ct, 0, (count+1) * sizeof(struct color_table));
542 * We boldly assume that 256 colors means xterm 256-color
543 * color cube and grayscale.
545 if(ANSI_COLOR() && (count == 256+COL_TRANS())){
546 int r, g, b, gray;
548 for(r = 0; r < 6; r++)
549 for(g = 0; g < 6; g++)
550 for(b = 0; b < 6; b++){
551 ind = 16 + 36*r + 6*g + b;
552 cube[ind].red = r ? (40*r + 55) : 0;
553 cube[ind].green = g ? (40*g + 55) : 0;
554 cube[ind].blue = b ? (40*b + 55) : 0;
555 snprintf(cube[ind].rgb, sizeof(cube[ind].rgb), "%3.3d,%3.3d,%3.3d",
556 cube[ind].red, cube[ind].green, cube[ind].blue);
559 for(gray = 0; gray < 24; gray++){
560 ind = gray + 232;
561 graylevel = 10*gray + 8;
562 cube[ind].red = graylevel;
563 cube[ind].green = graylevel;
564 cube[ind].blue = graylevel;
565 snprintf(cube[ind].rgb, sizeof(cube[ind].rgb), "%3.3d,%3.3d,%3.3d",
566 graylevel, graylevel, graylevel);
570 for(i = 0, t = ct; t && i < count; i++, t++){
571 t->val = i;
573 switch(i){
574 case COL_BLACK:
575 strncpy(colorname, "black", sizeof(colorname));
576 colorname[sizeof(colorname)-1] = '\0';
577 break;
578 case COL_RED:
579 strncpy(colorname, "red", sizeof(colorname));
580 colorname[sizeof(colorname)-1] = '\0';
581 break;
582 case COL_GREEN:
583 strncpy(colorname, "green", sizeof(colorname));
584 colorname[sizeof(colorname)-1] = '\0';
585 break;
586 case COL_YELLOW:
587 strncpy(colorname, "yellow", sizeof(colorname));
588 colorname[sizeof(colorname)-1] = '\0';
589 break;
590 case COL_BLUE:
591 strncpy(colorname, "blue", sizeof(colorname));
592 colorname[sizeof(colorname)-1] = '\0';
593 break;
594 case COL_MAGENTA:
595 strncpy(colorname, "magenta", sizeof(colorname));
596 colorname[sizeof(colorname)-1] = '\0';
597 break;
598 case COL_CYAN:
599 strncpy(colorname, "cyan", sizeof(colorname));
600 colorname[sizeof(colorname)-1] = '\0';
601 break;
602 case COL_WHITE:
603 strncpy(colorname, "white", sizeof(colorname));
604 colorname[sizeof(colorname)-1] = '\0';
605 break;
606 default:
607 snprintf(colorname, sizeof(colorname), "color%3.3d", i);
608 break;
611 if(COL_TRANS() && i == count-1){
612 strncpy(colorname, MATCH_TRAN_COLOR, sizeof(colorname));
613 colorname[sizeof(colorname)-1] = '\0';
616 add_to_color_name_list(t, colorname);
618 if(count == 8+COL_TRANS()){
619 if(COL_TRANS() && i == count-1){
620 t->red = t->green = t->blue = -1;
622 else
623 switch(i){
624 case COL_BLACK:
625 t->red = t->green = t->blue = 0;
626 add_to_color_name_list(t, "color008");
627 add_to_color_name_list(t, "colordgr");
628 add_to_color_name_list(t, "colormgr");
629 break;
630 case COL_RED:
631 t->red = 255;
632 t->green = t->blue = 0;
633 add_to_color_name_list(t, "color009");
634 break;
635 case COL_GREEN:
636 t->green = 255;
637 t->red = t->blue = 0;
638 add_to_color_name_list(t, "color010");
639 break;
640 case COL_YELLOW:
641 t->red = t->green = 255;
642 t->blue = 0;
643 add_to_color_name_list(t, "color011");
644 break;
645 case COL_BLUE:
646 t->red = t->green = 0;
647 t->blue = 255;
648 add_to_color_name_list(t, "color012");
649 break;
650 case COL_MAGENTA:
651 t->red = t->blue = 255;
652 t->green = 0;
653 add_to_color_name_list(t, "color013");
654 break;
655 case COL_CYAN:
656 t->red = 0;
657 t->green = t->blue = 255;
658 add_to_color_name_list(t, "color014");
659 break;
660 case COL_WHITE:
661 t->red = t->green = t->blue = 255;
662 add_to_color_name_list(t, "color015");
663 add_to_color_name_list(t, "colorlgr");
664 break;
667 else if(count == 16+COL_TRANS() || count == 256+COL_TRANS()){
668 if(COL_TRANS() && i == count-1){
669 t->red = t->green = t->blue = -1;
671 else
673 * This set of RGB values seems to come close to describing
674 * what a 16-color xterm gives you.
676 switch(i){
677 case COL_BLACK:
678 t->red = t->green = t->blue = 0;
679 break;
680 case COL_RED: /* actually dark red */
681 t->red = 174;
682 t->green = t->blue = 0;
683 break;
684 case COL_GREEN: /* actually dark green */
685 t->green = 174;
686 t->red = t->blue = 0;
687 break;
688 case COL_YELLOW: /* actually dark yellow */
689 t->blue = 0;
690 t->red = t->green = 174;
691 break;
692 case COL_BLUE: /* actually dark blue */
693 t->blue = 174;
694 t->red = t->green = 0;
695 break;
696 case COL_MAGENTA: /* actually dark magenta */
697 t->green = 0;
698 t->red = t->blue = 174;
699 break;
700 case COL_CYAN: /* actually dark cyan */
701 t->red = 0;
702 t->green = t->blue = 174;
703 break;
704 case COL_WHITE: /* actually light gray */
705 t->red = t->green = t->blue = 174;
706 if(count == 16)
707 add_to_color_name_list(t, "colorlgr");
709 break;
710 case 8: /* dark gray */
711 t->red = t->green = t->blue = 64;
712 if(count == 16){
713 add_to_color_name_list(t, "colordgr");
714 add_to_color_name_list(t, "colormgr");
717 break;
718 case 9: /* red */
719 t->red = 255;
720 t->green = t->blue = 0;
721 break;
722 case 10: /* green */
723 t->green = 255;
724 t->red = t->blue = 0;
725 break;
726 case 11: /* yellow */
727 t->blue = 0;
728 t->red = t->green = 255;
729 break;
730 case 12: /* blue */
731 t->blue = 255;
732 t->red = t->green = 0;
733 break;
734 case 13: /* magenta */
735 t->green = 0;
736 t->red = t->blue = 255;
737 break;
738 case 14: /* cyan */
739 t->red = 0;
740 t->green = t->blue = 255;
741 break;
742 case 15: /* white */
743 t->red = t->green = t->blue = 255;
744 break;
745 default:
746 t->red = cube[i].red;
747 t->green = cube[i].green;
748 t->blue = cube[i].blue;
749 switch(i){
750 case 238:
751 add_to_color_name_list(t, "colordgr");
752 break;
754 case 244:
755 add_to_color_name_list(t, "colormgr");
756 break;
758 case 250:
759 add_to_color_name_list(t, "colorlgr");
760 break;
763 break;
766 else{
767 if(COL_TRANS() && i == count-1){
768 t->red = t->green = t->blue = -1;
770 else
771 switch(i){
772 case COL_BLACK:
773 t->red = t->green = t->blue = 0;
774 break;
775 case COL_RED: /* actually dark red */
776 t->red = 255;
777 t->green = t->blue = 0;
778 break;
779 case COL_GREEN: /* actually dark green */
780 t->green = 255;
781 t->red = t->blue = 0;
782 break;
783 case COL_YELLOW: /* actually dark yellow */
784 t->blue = 0;
785 t->red = t->green = 255;
786 break;
787 case COL_BLUE: /* actually dark blue */
788 t->blue = 255;
789 t->red = t->green = 0;
790 break;
791 case COL_MAGENTA: /* actually dark magenta */
792 t->green = 0;
793 t->red = t->blue = 255;
794 break;
795 case COL_CYAN: /* actually dark cyan */
796 t->red = 0;
797 t->green = t->blue = 255;
798 break;
799 case COL_WHITE: /* actually light gray */
800 t->red = t->green = t->blue = 255;
801 break;
802 default:
803 /* this will just be a string match */
804 t->red = t->green = t->blue = 256+i;
805 break;
810 for(i = 0, t = ct; t && i < count; i++, t++){
811 t->rgb = (char *)fs_get((RGBLEN+1) * sizeof(char));
812 if(COL_TRANS() && i == count-1)
813 snprintf(t->rgb, RGBLEN+1, MATCH_TRAN_COLOR);
814 else
815 snprintf(t->rgb, RGBLEN+1, "%3.3d,%3.3d,%3.3d", t->red, t->green, t->blue);
819 return(ct);
823 void
824 add_to_color_name_list(struct color_table *t, char *name)
826 if(t && name && *name){
827 struct color_name_list *new_name;
829 new_name = (struct color_name_list *) fs_get(sizeof(struct color_name_list));
830 if(new_name){
831 memset(new_name, 0, sizeof(*new_name));
832 new_name->namelen = strlen(name);
834 new_name->name = (char *) fs_get((new_name->namelen+1) * sizeof(char));
835 if(new_name->name){
836 strncpy(new_name->name, name, new_name->namelen+1);
837 new_name->name[new_name->namelen] = '\0';
839 if(t->names){
840 struct color_name_list *nl;
841 for(nl = t->names; nl->next; nl = nl->next)
844 nl->next = new_name;
846 else
847 t->names = new_name;
854 void
855 free_color_name_list(struct color_name_list **nl)
857 if(nl && *nl){
858 if((*nl)->next)
859 free_color_name_list(&(*nl)->next);
861 if((*nl)->name)
862 fs_give((void **) &(*nl)->name);
864 fs_give((void **) nl);
870 * Map from integer color value to canonical color name.
872 char *
873 colorx(int color)
875 struct color_table *ct;
876 static char cbuf[12];
878 /* before we get set up, we still need to use this a bit */
879 if(!color_tbl){
880 switch(color){
881 case COL_BLACK:
882 return("black");
883 case COL_RED:
884 return("red");
885 case COL_GREEN:
886 return("green");
887 case COL_YELLOW:
888 return("yellow");
889 case COL_BLUE:
890 return("blue");
891 case COL_MAGENTA:
892 return("magenta");
893 case COL_CYAN:
894 return("cyan");
895 case COL_WHITE:
896 return("white");
897 default:
898 snprintf(cbuf, sizeof(cbuf), "color%3.3d", color);
899 return(cbuf);
903 for(ct = color_tbl; ct->names; ct++)
904 if(ct->val == color)
905 break;
907 /* rgb _is_ the canonical name */
908 if(ct->names)
909 return(ct->rgb);
911 /* not supposed to get here */
912 snprintf(cbuf, sizeof(cbuf), "color%3.3d", color);
913 return(cbuf);
918 * Argument is a color name which could be an RGB string, a name like "blue",
919 * or a name like "color011".
921 * Returns a pointer to the canonical name of the color.
923 char *
924 color_to_canonical_name(char *s)
926 struct color_table *ct;
927 struct color_name_list *nl;
928 int done;
930 if(!s || !color_tbl)
931 return(NULL);
933 if(*s == ' ' || isdigit(*s)){
934 /* check for rgb string instead of name */
935 for(ct = color_tbl; ct->rgb; ct++)
936 if(!strncmp(ct->rgb, s, RGBLEN))
937 break;
939 else{
940 for(done=0, ct = color_tbl; !done && ct->names; ct++){
941 for(nl = ct->names; !done && nl; nl = nl->next)
942 if(nl->name && !struncmp(nl->name, s, nl->namelen))
943 done++;
945 if(done)
946 break;
950 /* rgb is the canonical name */
951 if(ct->names)
952 return(ct->rgb);
953 else if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN) || !struncmp(s, MATCH_NONE_COLOR, RGBLEN))
954 return(s);
956 return("");
961 * Argument is the name of a color or an RGB value that we recognize.
962 * The table should be set up so that the val returned is the same whether
963 * or not we choose the canonical name.
965 * Returns the integer value for the color.
968 color_to_val(char *s)
970 struct color_table *ct;
971 struct color_name_list *nl;
972 int done;
974 if(!s || !color_tbl)
975 return(-1);
977 if(*s == ' ' || isdigit(*s)){
978 /* check for rgb string instead of name */
979 for(ct = color_tbl; ct->rgb; ct++)
980 if(!strncmp(ct->rgb, s, RGBLEN))
981 break;
984 * Didn't match any. Find "closest" to match.
986 if(!ct->rgb){
987 int r = -1, g = -1, b = -1;
988 char *p, *comma, scopy[RGBLEN+1];
990 strncpy(scopy, s, sizeof(scopy));
991 scopy[sizeof(scopy)-1] = '\0';
993 p = scopy;
994 comma = strchr(p, ',');
995 if(comma){
996 *comma = '\0';
997 r = atoi(p);
998 p = comma+1;
999 if(r >= 0 && r <= 255 && *p){
1000 comma = strchr(p, ',');
1001 if(comma){
1002 *comma = '\0';
1003 g = atoi(p);
1004 p = comma+1;
1005 if(g >= 0 && g <= 255 && *p){
1006 b = atoi(p);
1012 if(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255){
1013 struct color_table *closest = NULL;
1014 int closest_value = 1000000;
1015 int cv;
1017 for(ct = color_tbl; ct->rgb; ct++){
1019 if(ct->red >= 0 && ct->red <= 255
1020 && ct->green >= 0 && ct->green <= 255
1021 && ct->blue >= 0 && ct->blue <= 255){
1022 cv = (ct->red - r) * (ct->red - r) +
1023 (ct->green - g) * (ct->green - g) +
1024 (ct->blue - b) * (ct->blue - b);
1025 if(cv < closest_value){
1026 closest_value = cv;
1027 closest = ct;
1032 if(closest)
1033 ct = closest;
1037 else{
1038 for(done=0, ct = color_tbl; !done && ct->names; ct++){
1039 for(nl = ct->names; !done && nl; nl = nl->next)
1040 if(nl->name && !struncmp(nl->name, s, nl->namelen))
1041 done++;
1043 if(done)
1044 break;
1048 if(ct->names)
1049 return(ct->val);
1050 else
1051 return(-1);
1055 void
1056 free_color_table(struct color_table **ctbl)
1058 struct color_table *t;
1060 if(ctbl && *ctbl){
1061 for(t = *ctbl; t->names; t++){
1062 free_color_name_list(&t->names);
1064 if(t->rgb)
1065 fs_give((void **) &t->rgb);
1068 fs_give((void **) ctbl);
1074 pico_count_in_color_table(void)
1076 return(
1077 (ANSI_COLOR()
1078 ? (ANSI8_COLOR() ? 8 : ANSI16_COLOR() ? 16 : 256)
1079 : _colors)
1080 + COL_TRANS());
1084 void
1085 pico_nfcolor(char *s)
1087 if(_nfcolor)
1088 fs_give((void **) &_nfcolor);
1090 if(s){
1091 size_t len;
1093 len = strlen(s);
1094 _nfcolor = (char *) fs_get((len+1) * sizeof(char));
1095 if(_nfcolor){
1096 strncpy(_nfcolor, s, len+1);
1097 _nfcolor[len] = '\0';
1100 if(the_normal_color){
1101 strncpy(the_normal_color->fg, _nfcolor, MAXCOLORLEN+1);
1102 the_normal_color->fg[MAXCOLORLEN] = '\0';
1105 else if(the_normal_color)
1106 free_color_pair(&the_normal_color);
1110 void
1111 pico_nbcolor(char *s)
1113 if(_nbcolor)
1114 fs_give((void **) &_nbcolor);
1116 if(s){
1117 size_t len;
1119 len = strlen(s);
1120 _nbcolor = (char *) fs_get((len+1) * sizeof(char));
1121 if(_nbcolor){
1122 strncpy(_nbcolor, s, len+1);
1123 _nbcolor[len] = '\0';
1126 if(the_normal_color){
1127 strncpy(the_normal_color->bg, _nbcolor, MAXCOLORLEN+1);
1128 the_normal_color->bg[MAXCOLORLEN] = '\0';
1131 else if(the_normal_color)
1132 free_color_pair(&the_normal_color);
1135 void
1136 pico_rfcolor(char *s)
1138 if(_rfcolor)
1139 fs_give((void **) &_rfcolor);
1141 if(s){
1142 size_t len;
1144 len = strlen(s);
1145 _rfcolor = (char *) fs_get((len+1) * sizeof(char));
1146 if(_rfcolor){
1147 strncpy(_rfcolor, s, len+1);
1148 _rfcolor[len] = '\0';
1151 if(the_rev_color){
1152 strncpy(the_rev_color->fg, _rfcolor, MAXCOLORLEN+1);
1153 the_rev_color->fg[MAXCOLORLEN] = '\0';
1156 else if(the_rev_color)
1157 free_color_pair(&the_rev_color);
1160 void
1161 pico_rbcolor(char *s)
1163 if(_rbcolor)
1164 fs_give((void **) &_rbcolor);
1166 if(s){
1167 size_t len;
1169 len = strlen(s);
1170 _rbcolor = (char *) fs_get((len+1) * sizeof(char));
1171 if(_rbcolor){
1172 strncpy(_rbcolor, s, len+1);
1173 _rbcolor[len] = '\0';
1176 if(the_rev_color){
1177 strncpy(the_rev_color->bg, _rbcolor, MAXCOLORLEN+1);
1178 the_rev_color->bg[MAXCOLORLEN] = '\0';
1181 else if(the_rev_color)
1182 free_color_pair(&the_rev_color);
1187 pico_hascolor(void)
1189 if(!_color_inited)
1190 tinitcolor();
1192 return(_color_inited);
1197 pico_usingcolor(void)
1199 return(_using_color && pico_hascolor());
1204 * This should only be called when we're using the
1205 * unix termdef color, as opposed to the ANSI defined
1206 * color stuff or the Windows stuff.
1209 pico_trans_color(void)
1211 return(_bce && _op);
1215 void
1216 pico_toggle_color(int on)
1218 if(on){
1219 if(pico_hascolor())
1220 _using_color = 1;
1222 else{
1223 _using_color = 0;
1224 if(_color_inited){
1225 _color_inited = 0;
1226 if(!panicking())
1227 free_color_table(&color_tbl);
1229 if(ANSI_COLOR())
1230 putpad("\033[39;49m");
1231 else{
1232 if(_op)
1233 putpad(_op);
1234 if(_oc)
1235 putpad(_oc);
1242 unsigned
1243 pico_get_color_options(void)
1245 return(color_options);
1250 pico_trans_is_on(void)
1252 return(COL_TRANS());
1257 * Absolute set of options. Caller has to OR things together and so forth.
1259 void
1260 pico_set_color_options(unsigned flags)
1262 color_options = flags;
1265 void
1266 pico_endcolor(void)
1268 pico_toggle_color(0);
1269 if(panicking())
1270 return;
1272 if(_nfcolor)
1273 fs_give((void **) &_nfcolor);
1275 if(_nbcolor)
1276 fs_give((void **) &_nbcolor);
1278 if(_rfcolor)
1279 fs_give((void **) &_rfcolor);
1281 if(_rbcolor)
1282 fs_give((void **) &_rbcolor);
1284 if(_last_fg_color)
1285 fs_give((void **) &_last_fg_color);
1287 if(_last_bg_color)
1288 fs_give((void **) &_last_bg_color);
1290 if(the_rev_color)
1291 free_color_pair(&the_rev_color);
1293 if(the_normal_color)
1294 free_color_pair(&the_normal_color);
1298 void
1299 pico_set_nfg_color(void)
1301 if(_nfcolor)
1302 (void)pico_set_fg_color(_nfcolor);
1306 void
1307 pico_set_nbg_color(void)
1309 if(_nbcolor)
1310 (void)pico_set_bg_color(_nbcolor);
1314 void
1315 pico_set_normal_color(void)
1317 if(!_nfcolor || !_nbcolor ||
1318 !pico_set_fg_color(_nfcolor) || !pico_set_bg_color(_nbcolor)){
1319 (void)pico_set_fg_color(DEFAULT_NORM_FORE_RGB);
1320 (void)pico_set_bg_color(DEFAULT_NORM_BACK_RGB);
1326 * If inverse is a color, returns a pointer to that color.
1327 * If not, returns NULL.
1329 * NOTE: Don't free this!
1331 COLOR_PAIR *
1332 pico_get_rev_color(void)
1334 if(pico_usingcolor() && _rfcolor && _rbcolor &&
1335 pico_is_good_color(_rfcolor) && pico_is_good_color(_rbcolor)){
1336 if(!the_rev_color)
1337 the_rev_color = new_color_pair(_rfcolor, _rbcolor);
1339 return(the_rev_color);
1341 else
1342 return(NULL);
1347 * Returns a pointer to the normal color.
1349 * NOTE: Don't free this!
1351 COLOR_PAIR *
1352 pico_get_normal_color(void)
1354 if(pico_usingcolor() && _nfcolor && _nbcolor &&
1355 pico_is_good_color(_nfcolor) && pico_is_good_color(_nbcolor)){
1356 if(!the_normal_color)
1357 the_normal_color = new_color_pair(_nfcolor, _nbcolor);
1359 return(the_normal_color);
1361 else
1362 return(NULL);
1367 * Sets color to (fg,bg).
1368 * Flags == PSC_NONE No alternate default if fg,bg fails.
1369 * == PSC_NORM Set it to Normal color on failure.
1370 * == PSC_REV Set it to Reverse color on failure.
1372 * If flag PSC_RET is set, returns an allocated copy of the previous
1373 * color pair, otherwise returns NULL.
1375 COLOR_PAIR *
1376 pico_set_colors(char *fg, char *bg, int flags)
1378 int uc;
1379 COLOR_PAIR *cp = NULL, *rev = NULL;
1381 if(flags & PSC_RET)
1382 cp = pico_get_cur_color();
1384 if(fg && !strcmp(fg, END_PSEUDO_REVERSE)){
1385 EndInverse();
1386 if(cp)
1387 free_color_pair(&cp);
1389 else if(!((uc=pico_usingcolor()) && fg && bg &&
1390 pico_set_fg_color(fg) && pico_set_bg_color(bg))){
1392 if(uc && flags & PSC_NORM)
1393 pico_set_normal_color();
1394 else if(flags & PSC_REV){
1395 if((rev = pico_get_rev_color()) != NULL){
1396 pico_set_fg_color(rev->fg); /* these will succeed */
1397 pico_set_bg_color(rev->bg);
1399 else{
1400 StartInverse();
1401 if(cp){
1402 strncpy(cp->fg, END_PSEUDO_REVERSE, MAXCOLORLEN+1);
1403 cp->fg[MAXCOLORLEN] = '\0';
1404 strncpy(cp->bg, END_PSEUDO_REVERSE, MAXCOLORLEN+1);
1405 cp->bg[MAXCOLORLEN] = '\0';
1411 return(cp);
1416 pico_is_good_color(char *s)
1418 struct color_table *ct;
1419 struct color_name_list *nl;
1420 int done;
1422 if(!s || !color_tbl)
1423 return(FALSE);
1425 if(!strcmp(s, END_PSEUDO_REVERSE))
1426 return(TRUE);
1427 else if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN) || !struncmp(s, MATCH_NONE_COLOR, RGBLEN))
1428 return(TRUE);
1429 else if(*s == ' ' || isdigit(*s)){
1430 /* check for rgb string instead of name */
1431 for(ct = color_tbl; ct->rgb; ct++)
1432 if(!strncmp(ct->rgb, s, RGBLEN))
1433 break;
1435 /* if no match it's still ok if rgb */
1436 if(!ct->rgb){
1437 int r = -1, g = -1, b = -1;
1438 char *p, *comma, scopy[RGBLEN+1];
1440 strncpy(scopy, s, sizeof(scopy));
1441 scopy[sizeof(scopy)-1] = '\0';
1443 p = scopy;
1444 comma = strchr(p, ',');
1445 if(comma){
1446 *comma = '\0';
1447 r = atoi(p);
1448 p = comma+1;
1449 if(r >= 0 && r <= 255 && *p){
1450 comma = strchr(p, ',');
1451 if(comma){
1452 *comma = '\0';
1453 g = atoi(p);
1454 p = comma+1;
1455 if(g >= 0 && g <= 255 && *p){
1456 b = atoi(p);
1462 if(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255)
1463 ct = color_tbl; /* to force TRUE */
1466 else{
1467 for(done=0, ct = color_tbl; !done && ct->names; ct++){
1468 for(nl = ct->names; !done && nl; nl = nl->next)
1469 if(nl->name && !struncmp(nl->name, s, nl->namelen))
1470 done++;
1472 if(done)
1473 break;
1477 return(ct->names ? TRUE : FALSE);
1482 * Return TRUE on success.
1485 pico_set_fg_color(char *s)
1487 int val;
1489 if(!s || !color_tbl)
1490 return(FALSE);
1492 if(!strcmp(s, END_PSEUDO_REVERSE)){
1493 EndInverse();
1494 return(TRUE);
1497 if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN))
1498 s = _nfcolor;
1499 else if(!struncmp(s, MATCH_NONE_COLOR, RGBLEN))
1500 return(TRUE);
1502 if((val = color_to_val(s)) >= 0){
1503 size_t len;
1504 int changed;
1506 changed = !_last_fg_color || strcmp(_last_fg_color,colorx(val));
1508 /* already set correctly */
1509 if(!_force_fg_color_change && !changed)
1510 return(TRUE);
1512 _force_fg_color_change = 0;
1514 if(changed){
1515 if(_last_fg_color)
1516 fs_give((void **) &_last_fg_color);
1518 len = strlen(colorx(val));
1519 if((_last_fg_color = (char *) fs_get((len+1) * sizeof(char))) != NULL){
1520 strncpy(_last_fg_color, colorx(val), len+1);
1521 _last_fg_color[len] = '\0';
1525 tfgcolor(val);
1526 return(TRUE);
1528 else
1529 return(FALSE);
1534 pico_set_bg_color(char *s)
1536 int val;
1538 if(!s || !color_tbl)
1539 return(FALSE);
1541 if(!strcmp(s, END_PSEUDO_REVERSE)){
1542 EndInverse();
1543 return(TRUE);
1546 if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN))
1547 s = _nbcolor;
1548 else if(!struncmp(s, MATCH_NONE_COLOR, RGBLEN))
1549 return(TRUE);
1551 if((val = color_to_val(s)) >= 0){
1552 size_t len;
1553 int changed;
1555 changed = !_last_bg_color || strcmp(_last_bg_color,colorx(val));
1557 /* already set correctly */
1558 if(!_force_bg_color_change && !changed)
1559 return(TRUE);
1561 _force_bg_color_change = 0;
1563 if(changed){
1564 if(_last_bg_color)
1565 fs_give((void **) &_last_bg_color);
1567 len = strlen(colorx(val));
1568 if((_last_bg_color = (char *) fs_get((len+1) * sizeof(char))) != NULL){
1569 strncpy(_last_bg_color, colorx(val), len+1);
1570 _last_bg_color[len] = '\0';
1574 tbgcolor(val);
1575 return(TRUE);
1577 else
1578 return(FALSE);
1583 * Return a pointer to an rgb string for the input color. The output is 11
1584 * characters long and looks like rrr,ggg,bbb.
1586 * Args colorName -- The color to convert to ascii rgb.
1588 * Returns Pointer to a static buffer containing the rgb string. Can use up
1589 * to three returned values at once before the first is overwritten.
1591 char *
1592 color_to_asciirgb(char *colorName)
1594 static char c_to_a_buf[3][RGBLEN+1];
1595 static int whichbuf = 0;
1596 struct color_table *ct;
1597 struct color_name_list *nl;
1598 int done;
1600 whichbuf = (whichbuf + 1) % 3;
1602 c_to_a_buf[whichbuf][0] = '\0';
1604 for(done=0, ct = color_tbl; !done && ct->names; ct++){
1605 for(nl = ct->names; !done && nl; nl = nl->next)
1606 if(nl->name && !struncmp(nl->name, colorName, nl->namelen))
1607 done++;
1609 if(done)
1610 break;
1613 if(ct && ct->names){
1614 strncpy(c_to_a_buf[whichbuf], ct->rgb, sizeof(c_to_a_buf[0]));
1615 c_to_a_buf[whichbuf][sizeof(c_to_a_buf[0])-1] = '\0';
1617 else if(*colorName == ' ' || isdigit(*colorName)){
1618 /* check for rgb string instead of name */
1619 for(ct = color_tbl; ct->rgb; ct++)
1620 if(!strncmp(ct->rgb, colorName, RGBLEN))
1621 break;
1623 /* if no match it's still ok if rgb */
1624 if(!ct->rgb){
1625 int r = -1, g = -1, b = -1;
1626 char *p, *comma, scopy[RGBLEN+1];
1628 strncpy(scopy, colorName, sizeof(scopy));
1629 scopy[sizeof(scopy)-1] = '\0';
1631 p = scopy;
1632 comma = strchr(p, ',');
1633 if(comma){
1634 *comma = '\0';
1635 r = atoi(p);
1636 p = comma+1;
1637 if(r >= 0 && r <= 255 && *p){
1638 comma = strchr(p, ',');
1639 if(comma){
1640 *comma = '\0';
1641 g = atoi(p);
1642 p = comma+1;
1643 if(g >= 0 && g <= 255 && *p){
1644 b = atoi(p);
1650 if(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255){
1651 /* it was already RGB */
1652 snprintf(c_to_a_buf[whichbuf], sizeof(c_to_a_buf[0]), "%3.3d,%3.3d,%3.3d", r, g, b);
1655 else{
1656 strncpy(c_to_a_buf[whichbuf], ct->rgb, sizeof(c_to_a_buf[0]));
1657 c_to_a_buf[whichbuf][sizeof(c_to_a_buf[0])-1] = '\0';
1661 if(!c_to_a_buf[whichbuf][0]){
1662 int l;
1665 * If we didn't find the color it could be that it is the
1666 * normal color (MATCH_NORM_COLOR) or the none color
1667 * (MATCH_NONE_COLOR). If that is the case, this strncpy thing
1668 * will work out correctly because those two strings are
1669 * RGBLEN long. Otherwise we're in a bit of trouble. This
1670 * most likely means that the user is using the same pinerc on
1671 * two terminals, one with more colors than the other. We didn't
1672 * find a match because this color isn't present on this terminal.
1673 * Since the return value of this function is assumed to be
1674 * RGBLEN long, we'd better make it that long.
1675 * It still won't work correctly because colors will be screwed up,
1676 * but at least the embedded colors in filter.c will get properly
1677 * sucked up when they're encountered.
1679 strcpy(c_to_a_buf[whichbuf], "xxxxxxxxxxx");
1680 l = strlen(colorName);
1681 strncpy(c_to_a_buf[whichbuf], colorName, (l < RGBLEN) ? l : RGBLEN);
1682 c_to_a_buf[whichbuf][RGBLEN] = '\0';
1685 return(c_to_a_buf[whichbuf]);
1689 char *
1690 pico_get_last_fg_color(void)
1692 char *ret = NULL;
1694 if(_last_fg_color){
1695 size_t len;
1697 len = strlen(_last_fg_color);
1698 if((ret = (char *) fs_get((len+1) * sizeof(char))) != NULL){
1699 strncpy(ret, _last_fg_color, len+1);
1700 ret[len] = '\0';
1704 return(ret);
1708 char *
1709 pico_get_last_bg_color(void)
1711 char *ret = NULL;
1713 if(_last_bg_color){
1714 size_t len;
1716 len = strlen(_last_bg_color);
1717 if((ret = (char *) fs_get((len+1) * sizeof(char))) != NULL){
1718 strncpy(ret, _last_bg_color, len+1);
1719 ret[len] = '\0';
1723 return(ret);
1727 COLOR_PAIR *
1728 pico_get_cur_color(void)
1730 return(new_color_pair(_last_fg_color, _last_bg_color));
1733 #else /* _WINDOWS */
1734 static short _in_inverse, _in_bold, _in_uline;
1737 pico_trans_is_on(void)
1739 return(0);
1742 void
1743 StartInverse()
1745 if(!_in_inverse)
1746 mswin_rev(_in_inverse = 1);
1749 void
1750 EndInverse()
1752 if(_in_inverse)
1753 mswin_rev(_in_inverse = 0);
1757 InverseState()
1759 return(_in_inverse);
1762 void
1763 StartUnderline()
1765 if(!_in_uline)
1766 mswin_uline(_in_uline = 1);
1769 void
1770 EndUnderline()
1772 if(_in_uline)
1773 mswin_uline(_in_uline = 0);
1777 StartBold()
1779 if(!_in_bold)
1780 mswin_bold(_in_bold = 1);
1782 return(1);
1785 void
1786 EndBold()
1788 if(_in_bold)
1789 mswin_bold(_in_bold = 0);
1793 #endif /* _WINDOWS */