1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ `(un)?colour' commands, and anything working with it.
4 * Copyright (c) 2014 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 #ifndef HAVE_AMALGAMATION
28 /* Not needed publically, but extends a set from nail.h */
29 #define n_COLOUR_TAG_ERR ((char*)-1)
30 #define a_COLOUR_TAG_IS_SPECIAL(P) (PTR2SIZE(P) >= PTR2SIZE(-3))
38 enum a_colour_tag_type
{
40 a_COLOUR_TT_DOT
= 1<<0, /* "dot" */
41 a_COLOUR_TT_OLDER
= 1<<1, /* "older" */
42 a_COLOUR_TT_HEADERS
= 1<<2, /* Comma-separated list of headers allowed */
44 a_COLOUR_TT_SUM
= a_COLOUR_TT_DOT
| a_COLOUR_TT_OLDER
,
45 a_COLOUR_TT_VIEW
= a_COLOUR_TT_HEADERS
48 struct a_colour_map_id
{
49 ui8_t cmi_group
; /* enum n_colour_group */
50 ui8_t cmi_id
; /* enum n_colour_id */
51 ui8_t cmi_tt
; /* enum a_colour_tag_type */
52 char const cmi_name
[13];
54 CTA(n__COLOUR_IDS
<= UI8_MAX
);
57 struct str cp_dat
; /* Pre-prepared ISO 6429 escape sequence */
60 struct a_colour_map
/* : public n_colour_pen */{
61 struct n_colour_pen cm_pen
; /* Points into .cm_buf */
62 struct a_colour_map
*cm_next
;
63 char const *cm_tag
; /* Colour tag or NULL for default (last) */
64 struct a_colour_map_id
const *cm_cmi
;
68 ui32_t cm_refcnt
; /* Beware of reference drops in recursions */
69 ui32_t cm_user_off
; /* User input offset in .cm_buf */
70 char cm_buf
[VFIELD_SIZE(0)];
74 /* TODO cg_has_env not used, we have to go for PS_COLOUR_ACTIVE */
76 ui8_t cg_group
; /* If .cg_has_env, enum n_colour_group */
78 struct a_colour_map
*cg_active
; /* The currently active colour */
79 /* Active mapping: .cg_colour*_maps on colour terminal, _mono_ otherwise */
80 struct a_colour_map
*(*cg_maps
)[n__COLOUR_GROUPS
][n__COLOUR_IDS
];
81 struct n_colour_pen cg_reset
; /* The reset sequence */
82 struct a_colour_map
*cg_colour256_maps
[n__COLOUR_GROUPS
][n__COLOUR_IDS
];
83 struct a_colour_map
*cg_colour_maps
[n__COLOUR_GROUPS
][n__COLOUR_IDS
];
84 struct a_colour_map
*cg_mono_maps
[n__COLOUR_GROUPS
][n__COLOUR_IDS
];
85 char cg_reset_buf
[sizeof("\033[0m")];
88 /* TODO The colour environment simply should be a pointer into an
89 * TODO carrier structure in equal spirit to the fio.c stack, which gets
90 * TODO created for each execute() cycle (long in TODO), and carries along
91 * TODO all the information, memory allocations and also output (filter)
92 * TODO chains, so that we could actually decide wether we could simply
93 * TODO suspend output for a chain, need to place reset sequences, etc.
94 * TODO For now, since we have no such carrier to know where colour
95 * TODO sequences have to be written, creating a colour environment requires
96 * TODO that the current colour state is "reset", because we wouldn't know
97 * TODO where to place reset sequences and ditto, reestablish colour.
98 * TODO This should be no problem in practice, however */
100 struct a_colour_env
*ce_last
;
101 ui8_t ce_group
; /* enum n_colour_group active upon switch */
102 bool_t ce_is_active
; /* Was colour active in outer level? */
106 /* C99: use [INDEX]={} */
107 CTA(n_COLOUR_GROUP_SUM
== 0);
108 CTA(n_COLOUR_GROUP_VIEW
== 1);
109 static char const a_colour_group_prefixes
[n__COLOUR_GROUPS
][8] = {
113 static struct a_colour_map_id
const
114 a_colour_map_ids
[n__COLOUR_GROUPS
][n__COLOUR_IDS
] = {{
115 {n_COLOUR_GROUP_SUM
, n_COLOUR_ID_SUM_DOTMARK
, a_COLOUR_TT_SUM
, "dotmark"},
116 {n_COLOUR_GROUP_SUM
, n_COLOUR_ID_SUM_HEADER
, a_COLOUR_TT_SUM
, "header"},
117 {n_COLOUR_GROUP_SUM
, n_COLOUR_ID_SUM_THREAD
, a_COLOUR_TT_SUM
, "thread"},
119 {n_COLOUR_GROUP_VIEW
, n_COLOUR_ID_VIEW_FROM_
, a_COLOUR_TT_NONE
, "from_"},
120 {n_COLOUR_GROUP_VIEW
, n_COLOUR_ID_VIEW_HEADER
, a_COLOUR_TT_VIEW
, "header"},
121 {n_COLOUR_GROUP_VIEW
, n_COLOUR_ID_VIEW_MSGINFO
, a_COLOUR_TT_NONE
, "msginfo"},
122 {n_COLOUR_GROUP_VIEW
, n_COLOUR_ID_VIEW_PARTINFO
, a_COLOUR_TT_NONE
,
125 #define a_COLOUR_MAP_SHOW_FIELDWIDTH \
126 (int)(sizeof("view-")-1 + sizeof("partinfo")-1)
128 static struct a_colour_g
*a_colour_g
;
129 static struct a_colour_env
*a_colour_env
;
131 static void a_colour_init(void);
132 DBG( static void a_colour_atexit(void); )
134 /* Find the type or -1 */
135 static enum a_colour_type
a_colour_type_find(char const *name
);
137 /* `(un)?colour' implementations */
138 static bool_t
a_colour_mux(char **argv
);
139 static bool_t
a_colour_unmux(char **argv
);
141 static bool_t
a_colour__show(enum a_colour_type ct
);
142 /* (regexpp may be NULL) */
143 static char const *a_colour__tag_identify(struct a_colour_map_id
const *cmip
,
144 char const *ctag
, void **regexpp
);
146 /* Try to find a mapping identity for user given slotname */
147 static struct a_colour_map_id
const *a_colour_map_id_find(char const *slotname
);
149 /* Find an existing mapping for the given combination */
150 static struct a_colour_map
*a_colour_map_find(enum n_colour_id cid
,
153 /* In-/Decrement reference counter, destroy if counts gets zero */
154 #define a_colour_map_ref(SELF) do{ ++(SELF)->cm_refcnt; }while(0)
155 static void a_colour_map_unref(struct a_colour_map
*self
);
157 /* Create an ISO 6429 (ECMA-48/ANSI) terminal control escape sequence from user
158 * input spec, store it or on error message in *store */
159 static bool_t
a_colour_iso6429(enum a_colour_type ct
, char **store
,
164 char const *term
, *cp
;
168 a_colour_g
= scalloc(1, sizeof *a_colour_g
);
170 if((term
= env_vlook("TERM", FAL0
)) == NULL
|| !asccasecmp(term
, "dumb"))
173 memcpy(a_colour_g
->cg_reset
.cp_dat
.s
= a_colour_g
->cg_reset_buf
, "\033[0m",
174 a_colour_g
->cg_reset
.cp_dat
.l
= sizeof("\033[0m") -1); /* (calloc) */
176 /* terminfo rocks: if we find "color", assume it's right; don't case care */
177 a_colour_g
->cg_maps
= &a_colour_g
->cg_colour256_maps
;
178 if(asccasestr(term
, "256color") != NULL
) /* FIXME temporary hack */
181 a_colour_g
->cg_maps
= &a_colour_g
->cg_colour_maps
;
183 if(asccasestr(term
, "color") != NULL
)
186 if((cp
= ok_vlook(colour_terms
)) == NULL
)
188 okterms
= savestr(cp
);
189 while((cp
= n_strsep(&okterms
, ',', TRU1
)) != NULL
)
190 if(!asccasecmp(cp
, term
))
193 a_colour_g
->cg_maps
= &a_colour_g
->cg_mono_maps
;
195 DBG( atexit(&a_colour_atexit
); ) /* TODO prog-global atexit event */
202 a_colour_atexit(void){
204 if(a_colour_env
!= NULL
)
205 n_colour_env_pop(TRU1
);
212 static enum a_colour_type
213 a_colour_type_find(char const *name
){
218 {"256", a_COLOUR_T_COLOUR256
},
219 {"8", a_COLOUR_T_COLOUR
},
220 {"iso", a_COLOUR_T_COLOUR
},
221 {"ansi", a_COLOUR_T_COLOUR
},
222 {"1", a_COLOUR_T_MONO
},
223 {"mono", a_COLOUR_T_MONO
}
225 enum a_colour_type rv
;
229 do if(!asccasecmp(tp
->name
, name
)){
232 }while(PTRCMP(++tp
, !=, ta
+ NELEM(ta
)));
234 rv
= (enum a_colour_type
)-1;
241 a_colour_mux(char **argv
){
243 char const *mapname
, *ctag
;
244 struct a_colour_map
*(*mapp
)[n__COLOUR_GROUPS
][n__COLOUR_IDS
], **cmap
,
246 struct a_colour_map_id
const *cmip
;
248 enum a_colour_type ct
;
251 if((ct
= a_colour_type_find(*argv
++)) == (enum a_colour_type
)-1){
252 n_err(_("`colour': invalid colour type \"%s\"\n"), argv
[-1]);
257 if(a_colour_g
== NULL
)
261 rv
= a_colour__show(ct
);
268 case a_COLOUR_T_COLOUR256
:
269 mapp
= &a_colour_g
->cg_colour256_maps
;
271 case a_COLOUR_T_COLOUR
:
272 mapp
= &a_colour_g
->cg_colour_maps
;
275 mapp
= &a_colour_g
->cg_mono_maps
;
279 if((cmip
= a_colour_map_id_find(mapname
= argv
[0])) == NULL
){
280 n_err(_("`colour': non-existing mapping: \"%s\"\n"), mapname
);
285 n_err(_("`colour': \"%s\": missing attribute argument\n"), mapname
);
289 /* Check wether preconditions are at all allowed, verify them as far as
290 * possible as necessary */
291 if((ctag
= argv
[2]) != NULL
){
294 if(cmip
->cmi_tt
== a_COLOUR_TT_NONE
){
295 n_err(_("`colour': \"%s\" doesn't support preconditions\n"), mapname
);
297 }else if((xtag
= a_colour__tag_identify(cmip
, ctag
, ®exp
)) ==
299 /* I18N: ..of colour mapping */
300 n_err(_("`colour': \"%s\": invalid precondition: \"%s\"\n"),
307 /* At this time we have all the information to be able to query wether such
308 * a mapping is yet established. If so, destroy it */
309 for(blcmp
= lcmp
= NULL
,
310 cmp
= *(cmap
= &(*mapp
)[cmip
->cmi_group
][cmip
->cmi_id
]);
311 cmp
!= NULL
; blcmp
= lcmp
, lcmp
= cmp
, cmp
= cmp
->cm_next
){
312 char const *xctag
= cmp
->cm_tag
;
315 (ctag
!= NULL
&& !a_COLOUR_TAG_IS_SPECIAL(ctag
) &&
316 xctag
!= NULL
&& !a_COLOUR_TAG_IS_SPECIAL(xctag
) &&
317 !strcmp(xctag
, ctag
))){
319 *cmap
= cmp
->cm_next
;
321 lcmp
->cm_next
= cmp
->cm_next
;
322 a_colour_map_unref(cmp
);
332 if(!a_colour_iso6429(ct
, &cp
, argv
[1])){
333 /* I18N: colour command: mapping: error message: user argument */
334 n_err(_("`colour': \"%s\": %s: \"%s\"\n"), mapname
, cp
, argv
[1]);
338 tl
= (ctag
!= NULL
&& !a_COLOUR_TAG_IS_SPECIAL(ctag
)) ? strlen(ctag
) : 0;
339 cmp
= smalloc(sizeof(*cmp
) - VFIELD_SIZEOF(struct a_colour_map
, cm_buf
) +
340 tl
+1 + (ul
= strlen(argv
[1])) +1 + (cl
= strlen(cp
)) +1);
343 cmp
->cm_pen
.cp_dat
.s
= bp
= cmp
->cm_buf
;
344 cmp
->cm_pen
.cp_dat
.l
= cl
;
345 memcpy(bp
, cp
, ++cl
);
348 cmp
->cm_user_off
= (ui32_t
)PTR2SIZE(bp
- cmp
->cm_buf
);
349 memcpy(bp
, argv
[1], ++ul
);
354 memcpy(bp
, ctag
, ++tl
);
359 /* Non-buf stuff; default mapping */
361 /* Default mappings must be last */
363 while(lcmp
->cm_next
!= NULL
)
364 lcmp
= lcmp
->cm_next
;
365 }else if(lcmp
->cm_next
== NULL
&& lcmp
->cm_tag
== NULL
){
366 if((lcmp
= blcmp
) == NULL
)
369 cmp
->cm_next
= lcmp
->cm_next
;
373 cmp
->cm_next
= *cmap
;
378 cmp
->cm_regex
= regexp
;
381 a_colour_map_ref(cmp
);
390 a_colour_unmux(char **argv
){
391 char const *mapname
, *ctag
, *xtag
;
392 struct a_colour_map
*(*mapp
)[n__COLOUR_GROUPS
][n__COLOUR_IDS
], **cmap
,
394 struct a_colour_map_id
const *cmip
;
396 enum a_colour_type ct
;
399 if((ct
= a_colour_type_find(*argv
++)) == (enum a_colour_type
)-1){
400 n_err(_("`uncolour': invalid colour type \"%s\"\n"), argv
[-1]);
406 ctag
= (mapname
!= NULL
) ? argv
[1] : mapname
;
409 if(a_colour_g
== NULL
)
414 case a_COLOUR_T_COLOUR256
:
415 mapp
= &a_colour_g
->cg_colour256_maps
;
417 case a_COLOUR_T_COLOUR
:
418 mapp
= &a_colour_g
->cg_colour_maps
;
421 mapp
= &a_colour_g
->cg_mono_maps
;
425 /* Delete anything? */
426 if(ctag
== NULL
&& mapname
[0] == '*' && mapname
[1] == '\0'){
428 struct a_colour_map
*tmp
;
430 for(i1
= 0; i1
< n__COLOUR_GROUPS
; ++i1
)
431 for(i2
= 0; i2
< n__COLOUR_IDS
; ++i2
)
432 for(cmp
= *(cmap
= &(*mapp
)[i1
][i2
]), *cmap
= NULL
; cmp
!= NULL
;){
435 a_colour_map_unref(tmp
);
438 if(a_colour_g
== NULL
){
440 /* I18N: colour command, mapping and precondition (option in quotes) */
441 n_err(_("`uncolour': non-existing mapping: \"%s\"%s%s%s\n"),
442 mapname
, (ctag
== NULL
? "" : " \""),
443 (ctag
== NULL
? "" : ctag
), (ctag
== NULL
? "" : "\""));
447 if((cmip
= a_colour_map_id_find(mapname
)) == NULL
)
450 if((xtag
= ctag
) != NULL
){
451 if(cmip
->cmi_tt
== a_COLOUR_TT_NONE
){
452 n_err(_("`uncolour': \"%s\" doesn't support preconditions\n"),
455 }else if((xtag
= a_colour__tag_identify(cmip
, ctag
, NULL
)) ==
457 n_err(_("`uncolour': \"%s\": invalid precondition: \"%s\"\n"),
461 /* (Improve user experience) */
462 if(xtag
!= NULL
&& !a_COLOUR_TAG_IS_SPECIAL(xtag
))
467 cmp
= *(cmap
= &(*mapp
)[cmip
->cmi_group
][cmip
->cmi_id
]);
473 if((xctag
= cmp
->cm_tag
) == ctag
)
475 if(ctag
!= NULL
&& !a_COLOUR_TAG_IS_SPECIAL(ctag
) &&
476 xctag
!= NULL
&& !a_COLOUR_TAG_IS_SPECIAL(xctag
) &&
477 !strcmp(xctag
, ctag
))
484 *cmap
= cmp
->cm_next
;
486 lcmp
->cm_next
= cmp
->cm_next
;
487 a_colour_map_unref(cmp
);
496 a_colour__show(enum a_colour_type ct
){
498 struct a_colour_map
*(*mapp
)[n__COLOUR_GROUPS
][n__COLOUR_IDS
], *cmp
;
504 case a_COLOUR_T_COLOUR256
:
506 mapp
= &a_colour_g
->cg_colour256_maps
;
508 case a_COLOUR_T_COLOUR
:
510 mapp
= &a_colour_g
->cg_colour_maps
;
514 mapp
= &a_colour_g
->cg_mono_maps
;
518 for(i1
= 0; i1
< n__COLOUR_GROUPS
; ++i1
)
519 for(i2
= 0; i2
< n__COLOUR_IDS
; ++i2
){
520 if((cmp
= (*mapp
)[i1
][i2
]) == NULL
)
524 char const *tagann
, *tag
;
527 if((tag
= cmp
->cm_tag
) == NULL
)
529 else if(tag
== n_COLOUR_TAG_SUM_DOT
)
531 else if(tag
== n_COLOUR_TAG_SUM_OLDER
)
534 else if(cmp
->cm_regex
!= NULL
)
537 printf("colour %s %-*s %s %s%s\n",
538 type
, a_COLOUR_MAP_SHOW_FIELDWIDTH
,
539 savecat(a_colour_group_prefixes
[i1
],
540 a_colour_map_ids
[i1
][i2
].cmi_name
),
541 (char const*)cmp
->cm_buf
+ cmp
->cm_user_off
,
552 a_colour__tag_identify(struct a_colour_map_id
const *cmip
, char const *ctag
,
557 if((cmip
->cmi_tt
& a_COLOUR_TT_DOT
) && !asccasecmp(ctag
, "dot"))
558 ctag
= n_COLOUR_TAG_SUM_DOT
;
559 else if((cmip
->cmi_tt
& a_COLOUR_TT_OLDER
) && !asccasecmp(ctag
, "older"))
560 ctag
= n_COLOUR_TAG_SUM_OLDER
;
561 else if(cmip
->cmi_tt
& a_COLOUR_TT_HEADERS
){
565 /* Can this be a valid list of headers? However, with regular expressions
566 * simply use the input as such if it appears to be a regex */
568 if(is_maybe_regex(ctag
)){
569 if(regexpp
!= NULL
&& regcomp(*regexpp
= smalloc(sizeof(regex_t
)),
570 ctag
, REG_EXTENDED
| REG_ICASE
| REG_NOSUB
)){
577 /* Normalize to lowercase and strip any whitespace before use */
581 for(i
= 0; (c
= *ctag
++) != '\0';){
582 bool_t isblspc
= blankspacechar(c
);
584 if(!isblspc
&& !alnumchar(c
) && c
!= '-' && c
!= ',')
586 /* Since we compare header names as they come from the message this
587 * lowercasing is however redundant: we need to asccasecmp() them */
589 cp
[i
++] = lowerconv(c
);
596 ctag
= n_COLOUR_TAG_ERR
;
601 static struct a_colour_map_id
const *
602 a_colour_map_id_find(char const *cp
){
604 struct a_colour_map_id
const (*cmip
)[n__COLOUR_IDS
], *rv
;
610 if(i
== n__COLOUR_IDS
)
613 size_t j
= strlen(a_colour_group_prefixes
[i
]);
614 if(!ascncasecmp(cp
, a_colour_group_prefixes
[i
], j
)){
620 cmip
= &a_colour_map_ids
[i
];
623 if(i
== n__COLOUR_IDS
|| (rv
= &(*cmip
)[i
])->cmi_name
[0] == '\0'){
627 if(!asccasecmp(cp
, rv
->cmi_name
))
635 static struct a_colour_map
*
636 a_colour_map_find(enum n_colour_id cid
, char const *ctag
){
637 struct a_colour_map
*cmp
;
640 for(cmp
= (*a_colour_g
->cg_maps
)[a_colour_g
->cg_group
][cid
]; cmp
!= NULL
;
642 char const *xtag
= cmp
->cm_tag
;
648 if(ctag
== NULL
|| a_COLOUR_TAG_IS_SPECIAL(ctag
))
651 if(cmp
->cm_regex
!= NULL
){
652 if(regexec(cmp
->cm_regex
, ctag
, 0,NULL
, 0) != REG_NOMATCH
)
656 if(cmp
->cm_cmi
->cmi_tt
& a_COLOUR_TT_HEADERS
){
657 char *hlist
= savestr(xtag
), *cp
;
659 while((cp
= n_strsep(&hlist
, ',', TRU1
)) != NULL
){
660 if(!asccasecmp(cp
, ctag
))
672 a_colour_map_unref(struct a_colour_map
*self
){
674 if(--self
->cm_refcnt
== 0){
676 if(self
->cm_regex
!= NULL
){
677 regfree(self
->cm_regex
);
678 free(self
->cm_regex
);
687 a_colour_iso6429(enum a_colour_type ct
, char **store
, char const *spec
){
692 {"bold", '1'}, {"underline", '4'}, {"reverse", '7'}
694 {"black", '0'}, {"red", '1'}, {"green", '2'}, {"brown", '3'},
695 {"blue", '4'}, {"magenta", '5'}, {"cyan", '6'}, {"white", '7'}
697 char *xspec
, *cp
, fg
[3], cfg
[2 + 2*sizeof("255")];
698 ui8_t ftno_base
, ftno
;
703 /* 0/1 indicate usage, thereafter possibly 256 color sequences */
706 /* Since we use salloc(), reuse the n_strsep() buffer also for the return
707 * value, ensure we have enough room for that */
709 size_t i
= strlen(spec
) +1;
710 xspec
= salloc(MAX(i
, sizeof("\033[1;4;7;38;5;255;48;5;255m")));
711 memcpy(xspec
, spec
, i
);
715 /* Iterate over the colour spec */
717 while((cp
= n_strsep(&xspec
, ',', TRU1
)) != NULL
){
718 char *y
, *x
= strchr(cp
, '=');
721 *store
= UNCONST(_("invalid attribute list"));
726 if(!asccasecmp(cp
, "ft")){
727 if(!asccasecmp(x
, "inverse")){
728 OBSOLETE(_("please use \"reverse\" not \"inverse\" for ft= fonts"));
729 x
= UNCONST("reverse");
731 for(idp
= fta
;; ++idp
)
732 if(idp
== fta
+ NELEM(fta
)){
733 *store
= UNCONST(_("invalid font attribute"));
735 }else if(!asccasecmp(x
, idp
->id_name
)){
737 fg
[ftno
++] = idp
->id_modc
;
739 *store
= UNCONST(_("too many font attributes"));
744 }else if(!asccasecmp(cp
, "fg")){
747 }else if(!asccasecmp(cp
, "bg")){
750 if(ct
== a_COLOUR_T_MONO
){
751 *store
= UNCONST(_("colours are not allowed"));
754 /* Maybe 256 color spec */
758 if(ct
== a_COLOUR_T_COLOUR
){
759 *store
= UNCONST(_("invalid colour for 8-colour mode"));
763 xv
= strtol(x
, &cp
, 10);
764 if(xv
< 0 || xv
> 255 || *cp
!= '\0' || PTRCMP(&x
[3], <, cp
)){
765 *store
= UNCONST(_("invalid 256-colour specification"));
769 memcpy((y
== &cfg
[0] ? y
+ 2 : y
+ 1 + sizeof("255")), x
,
770 (x
[1] == '\0' ? 2 : (x
[2] == '\0' ? 3 : 4)));
771 }else for(idp
= ca
;; ++idp
)
772 if(idp
== ca
+ NELEM(ca
)){
773 *store
= UNCONST(_("invalid colour attribute"));
775 }else if(!asccasecmp(x
, idp
->id_name
)){
784 /* Restore our salloc() buffer, create return value */
785 xspec
= UNCONST(spec
);
786 if(ftno
> 0 || cfg
[0] || cfg
[1]){ /* TODO unite/share colour setters */
791 for(ftno_base
= ftno
; ftno
> 0;){
792 if(ftno
-- != ftno_base
)
805 memcpy(xspec
+ 1, "8;5;", 4);
807 for(ftno
= 2; cfg
[ftno
] != '\0'; ++ftno
)
808 *xspec
++ = cfg
[ftno
];
813 if(ftno_base
> 0 || cfg
[0])
820 memcpy(xspec
+ 1, "8;5;", 4);
822 for(ftno
= 2 + sizeof("255"); cfg
[ftno
] != '\0'; ++ftno
)
823 *xspec
++ = cfg
[ftno
];
830 *store
= UNCONST(spec
);
842 rv
= !a_colour_mux(v
);
852 rv
= !a_colour_unmux(v
);
858 n_colour_env_create(enum n_colour_group cgrp
, bool_t pager_used
){
860 if(!(options
& OPT_INTERACTIVE
))
863 if (ok_blook(colour_disable
) || (pager_used
&& !ok_blook(colour_pager
))){
864 n_colour_env_push(); /* FIXME lex.c only pops (always env */
868 if(a_colour_g
== NULL
)
870 if(a_colour_g
->cg_maps
== NULL
)
873 a_colour_g
->cg_group
= cgrp
;
874 a_colour_g
->cg_active
= NULL
;
875 pstate
|= PS_COLOUR_ACTIVE
;
881 n_colour_env_push(void){
882 struct a_colour_env
*cep
;
885 if(!(options
& OPT_INTERACTIVE
))
888 cep
= smalloc(sizeof *cep
);
889 cep
->ce_last
= a_colour_env
;
890 if(a_colour_g
!= NULL
){
891 cep
->ce_group
= a_colour_g
->cg_group
;
892 a_colour_g
->cg_active
= NULL
;
894 cep
->ce_is_active
= ((pstate
& PS_COLOUR_ACTIVE
) != 0);
897 pstate
&= ~PS_COLOUR_ACTIVE
;
903 n_colour_env_pop(bool_t any_env_till_root
){
905 if(!(options
& OPT_INTERACTIVE
))
908 while(a_colour_env
!= NULL
){
909 struct a_colour_env
*cep
;
911 if((cep
= a_colour_env
)->ce_is_active
)
912 pstate
|= PS_COLOUR_ACTIVE
;
914 pstate
&= ~PS_COLOUR_ACTIVE
;
916 if(a_colour_g
!= NULL
){
917 a_colour_g
->cg_active
= NULL
;
918 a_colour_g
->cg_group
= cep
->ce_group
;
920 a_colour_env
= cep
->ce_last
;
923 if(!any_env_till_root
)
927 if(any_env_till_root
&& a_colour_g
!= NULL
&& (pstate
& PS_COLOUR_ACTIVE
)){
928 pstate
&= ~PS_COLOUR_ACTIVE
;
929 a_colour_g
->cg_active
= NULL
;
936 n_colour_env_gut(FILE *fp
){
938 if((options
& OPT_INTERACTIVE
) && (pstate
& PS_COLOUR_ACTIVE
)){
939 pstate
&= ~PS_COLOUR_ACTIVE
;
941 if(a_colour_g
->cg_active
!= NULL
){
942 a_colour_g
->cg_active
= NULL
;
944 fwrite(a_colour_g
->cg_reset
.cp_dat
.s
, a_colour_g
->cg_reset
.cp_dat
.l
,
952 n_colour_put(FILE *fp
, enum n_colour_id cid
, char const *ctag
){
954 if(pstate
& PS_COLOUR_ACTIVE
){
955 if(a_colour_g
->cg_active
!= NULL
)
956 fwrite(a_colour_g
->cg_reset
.cp_dat
.s
, a_colour_g
->cg_reset
.cp_dat
.l
, 1,
958 if((a_colour_g
->cg_active
= a_colour_map_find(cid
, ctag
)) != NULL
)
959 fwrite(a_colour_g
->cg_active
->cm_pen
.cp_dat
.s
,
960 a_colour_g
->cg_active
->cm_pen
.cp_dat
.l
, 1, fp
);
966 n_colour_reset(FILE *fp
){
968 if((pstate
& PS_COLOUR_ACTIVE
) && a_colour_g
->cg_active
!= NULL
){
969 a_colour_g
->cg_active
= NULL
;
970 fwrite(a_colour_g
->cg_reset
.cp_dat
.s
, a_colour_g
->cg_reset
.cp_dat
.l
, 1,
976 FL
struct str
const *
977 n_colour_reset_to_str(void){
981 if(pstate
& PS_COLOUR_ACTIVE
)
982 rv
= &a_colour_g
->cg_reset
.cp_dat
;
989 FL
struct n_colour_pen
*
990 n_colour_pen_create(enum n_colour_id cid
, char const *ctag
){
991 struct a_colour_map
*cmp
;
992 struct n_colour_pen
*rv
;
995 if((pstate
& PS_COLOUR_ACTIVE
) &&
996 (cmp
= a_colour_map_find(cid
, ctag
)) != NULL
){
997 union {void *vp
; char *cp
; struct n_colour_pen
*cpp
;} u
;
1007 n_colour_pen_put(struct n_colour_pen
*self
, FILE *fp
){
1009 if(pstate
& PS_COLOUR_ACTIVE
){
1010 union {void *vp
; char *cp
; struct a_colour_map
*cmp
;} u
;
1013 if(u
.cmp
!= a_colour_g
->cg_active
){
1014 if(a_colour_g
->cg_active
!= NULL
)
1015 fwrite(a_colour_g
->cg_reset
.cp_dat
.s
, a_colour_g
->cg_reset
.cp_dat
.l
,
1018 fwrite(self
->cp_dat
.s
, self
->cp_dat
.l
, 1, fp
);
1019 a_colour_g
->cg_active
= u
.cmp
;
1025 FL
struct str
const *
1026 n_colour_pen_to_str(struct n_colour_pen
*self
){
1030 if((pstate
& PS_COLOUR_ACTIVE
) && self
!= NULL
)
1037 #endif /* HAVE_COLOUR */