Tweak previous, it added a bad memory access
[s-mailx.git] / colour.c
blob45f354a7ecff073a3ce767ff7ffe26f43f2630b0
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ `(un)?colour' commands, and anything working with it.
3 *@ TODO n_colour_env should be objects, n_COLOUR_IS_ACTIVE() should take
4 *@ TODO such an object! We still should work together with n_go_data,
5 *@ TODO but only for cleanup purposes. No stack, that is.
7 * Copyright (c) 2014 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
9 * Permission to use, copy, modify, and/or distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 #undef n_FILE
22 #define n_FILE colour
24 #ifndef HAVE_AMALGAMATION
25 # include "nail.h"
26 #endif
28 EMPTY_FILE()
29 #ifdef HAVE_COLOUR
31 /* Not needed publically, but extends a set from nail.h */
32 #define n_COLOUR_TAG_ERR ((char*)-1)
33 #define a_COLOUR_TAG_IS_SPECIAL(P) (PTR2SIZE(P) >= PTR2SIZE(-3))
35 enum a_colour_type{
36 a_COLOUR_T_256,
37 a_COLOUR_T_8,
38 a_COLOUR_T_1,
39 a_COLOUR_T_NONE, /* EQ largest real colour + 1! */
40 a_COLOUR_T_UNKNOWN /* Initial value: real one queried before 1st use */
43 enum a_colour_tag_type{
44 a_COLOUR_TT_NONE,
45 a_COLOUR_TT_DOT = 1<<0, /* "dot" */
46 a_COLOUR_TT_OLDER = 1<<1, /* "older" */
47 a_COLOUR_TT_HEADERS = 1<<2, /* Comma-separated list of headers allowed */
49 a_COLOUR_TT_SUM = a_COLOUR_TT_DOT | a_COLOUR_TT_OLDER,
50 a_COLOUR_TT_VIEW = a_COLOUR_TT_HEADERS
53 struct a_colour_type_map{
54 ui8_t ctm_type; /* a_colour_type */
55 char ctm_name[7];
58 struct a_colour_map_id{
59 ui8_t cmi_ctx; /* enum n_colour_ctx */
60 ui8_t cmi_id; /* enum n_colour_id */
61 ui8_t cmi_tt; /* enum a_colour_tag_type */
62 char const cmi_name[13];
64 n_CTA(n__COLOUR_IDS <= UI8_MAX, "Enumeration exceeds storage datatype");
66 struct n_colour_pen{
67 struct str cp_dat; /* Pre-prepared ISO 6429 escape sequence */
70 struct a_colour_map /* : public n_colour_pen */{
71 struct n_colour_pen cm_pen; /* Points into .cm_buf */
72 struct a_colour_map *cm_next;
73 char const *cm_tag; /* Colour tag or NULL for default (last) */
74 struct a_colour_map_id const *cm_cmi;
75 #ifdef HAVE_REGEX
76 regex_t *cm_regex;
77 #endif
78 ui32_t cm_refcnt; /* Beware of reference drops in recursions */
79 ui32_t cm_user_off; /* User input offset in .cm_buf */
80 char cm_buf[n_VFIELD_SIZE(0)];
83 struct a_colour_g{
84 bool_t cg_is_init;
85 ui8_t cg_type; /* a_colour_type */
86 ui8_t __cg_pad[6];
87 struct n_colour_pen cg_reset; /* The reset sequence */
88 struct a_colour_map
89 *cg_maps[a_COLOUR_T_NONE][n__COLOUR_CTX_MAX1][n__COLOUR_IDS];
90 char cg__reset_buf[n_ALIGN_SMALL(sizeof("\033[0m"))];
93 /* C99: use [INDEX]={} */
94 /* */
95 n_CTA(a_COLOUR_T_256 == 0, "Unexpected value of constant");
96 n_CTA(a_COLOUR_T_8 == 1, "Unexpected value of constant");
97 n_CTA(a_COLOUR_T_1 == 2, "Unexpected value of constant");
98 static char const a_colour_types[][8] = {"256", "iso", "mono"};
100 static struct a_colour_type_map const a_colour_type_maps[] = {
101 {a_COLOUR_T_256, "256"},
102 {a_COLOUR_T_8, "8"}, {a_COLOUR_T_8, "iso"}, {a_COLOUR_T_8, "ansi"},
103 {a_COLOUR_T_1, "1"}, {a_COLOUR_T_1, "mono"}
106 n_CTA(n_COLOUR_CTX_SUM == 0, "Unexpected value of constant");
107 n_CTA(n_COLOUR_CTX_VIEW == 1, "Unexpected value of constant");
108 n_CTA(n_COLOUR_CTX_MLE == 2, "Unexpected value of constant");
109 static char const a_colour_ctx_prefixes[n__COLOUR_CTX_MAX1][8] = {
110 "sum-", "view-", "mle-"
113 static struct a_colour_map_id const
114 a_colour_map_ids[n__COLOUR_CTX_MAX1][n__COLOUR_IDS] = {{
115 {n_COLOUR_CTX_SUM, n_COLOUR_ID_SUM_DOTMARK, a_COLOUR_TT_SUM, "dotmark"},
116 {n_COLOUR_CTX_SUM, n_COLOUR_ID_SUM_HEADER, a_COLOUR_TT_SUM, "header"},
117 {n_COLOUR_CTX_SUM, n_COLOUR_ID_SUM_THREAD, a_COLOUR_TT_SUM, "thread"},
118 }, {
119 {n_COLOUR_CTX_VIEW, n_COLOUR_ID_VIEW_FROM_, a_COLOUR_TT_NONE, "from_"},
120 {n_COLOUR_CTX_VIEW, n_COLOUR_ID_VIEW_HEADER, a_COLOUR_TT_VIEW, "header"},
121 {n_COLOUR_CTX_VIEW, n_COLOUR_ID_VIEW_MSGINFO, a_COLOUR_TT_NONE, "msginfo"},
122 {n_COLOUR_CTX_VIEW, n_COLOUR_ID_VIEW_PARTINFO, a_COLOUR_TT_NONE, "partinfo"},
123 }, {
124 {n_COLOUR_CTX_MLE, n_COLOUR_ID_MLE_POSITION, a_COLOUR_TT_NONE, "position"},
125 {n_COLOUR_CTX_MLE, n_COLOUR_ID_MLE_PROMPT, a_COLOUR_TT_NONE, "prompt"},
127 #define a_COLOUR_MAP_SHOW_FIELDWIDTH \
128 (int)(sizeof("view-")-1 + sizeof("partinfo")-1)
130 static struct a_colour_g a_colour_g;
132 /* */
133 static void a_colour_init(void);
135 /* Find the type or -1 */
136 static enum a_colour_type a_colour_type_find(char const *name);
138 /* `(un)?colour' implementations */
139 static bool_t a_colour_mux(char **argv);
140 static bool_t a_colour_unmux(char **argv);
142 static bool_t a_colour__show(enum a_colour_type ct);
143 /* (regexpp may be NULL) */
144 static char const *a_colour__tag_identify(struct a_colour_map_id const *cmip,
145 char const *ctag, void **regexpp);
147 /* Try to find a mapping identity for user given slotname */
148 static struct a_colour_map_id const *a_colour_map_id_find(char const *slotname);
150 /* Find an existing mapping for the given combination */
151 static struct a_colour_map *a_colour_map_find(enum n_colour_id cid,
152 enum n_colour_ctx cctx, char const *ctag);
154 /* In-/Decrement reference counter, destroy if counts gets zero */
155 #define a_colour_map_ref(SELF) do{ ++(SELF)->cm_refcnt; }while(0)
156 static void a_colour_map_unref(struct a_colour_map *self);
158 /* Create an ISO 6429 (ECMA-48/ANSI) terminal control escape sequence from user
159 * input spec, store it or on error message in *store */
160 static bool_t a_colour_iso6429(enum a_colour_type ct, char **store,
161 char const *spec);
163 static void
164 a_colour_init(void){
165 NYD2_ENTER;
166 a_colour_g.cg_is_init = TRU1;
167 memcpy(a_colour_g.cg_reset.cp_dat.s = a_colour_g.cg__reset_buf, "\033[0m",
168 a_colour_g.cg_reset.cp_dat.l = sizeof("\033[0m") -1); /* (calloc) */
169 a_colour_g.cg_type = a_COLOUR_T_UNKNOWN;
170 NYD2_LEAVE;
173 static enum a_colour_type
174 a_colour_type_find(char const *name){
175 struct a_colour_type_map const *ctmp;
176 enum a_colour_type rv;
177 NYD2_ENTER;
179 ctmp = a_colour_type_maps;
180 do if(!asccasecmp(ctmp->ctm_name, name)){
181 rv = ctmp->ctm_type;
182 goto jleave;
183 }while(PTRCMP(++ctmp, !=, a_colour_type_maps + n_NELEM(a_colour_type_maps)));
185 rv = (enum a_colour_type)-1;
186 jleave:
187 NYD2_LEAVE;
188 return rv;
191 static bool_t
192 a_colour_mux(char **argv){
193 void *regexp;
194 char const *mapname, *ctag;
195 struct a_colour_map **cmap, *blcmp, *lcmp, *cmp;
196 struct a_colour_map_id const *cmip;
197 bool_t rv;
198 enum a_colour_type ct;
199 NYD2_ENTER;
201 if((ct = a_colour_type_find(*argv++)) == (enum a_colour_type)-1 &&
202 (*argv != NULL || !n_is_all_or_aster(argv[-1]))){
203 n_err(_("`colour': invalid colour type %s\n"),
204 n_shexp_quote_cp(argv[-1], FAL0));
205 rv = FAL0;
206 goto jleave;
209 if(!a_colour_g.cg_is_init)
210 a_colour_init();
212 if(*argv == NULL){
213 rv = a_colour__show(ct);
214 goto jleave;
217 rv = FAL0;
218 regexp = NULL;
220 if((cmip = a_colour_map_id_find(mapname = argv[0])) == NULL){
221 n_err(_("`colour': non-existing mapping: %s\n"),
222 n_shexp_quote_cp(mapname, FAL0));
223 goto jleave;
226 if(argv[1] == NULL){
227 n_err(_("`colour': %s: missing attribute argument\n"),
228 n_shexp_quote_cp(mapname, FAL0));
229 goto jleave;
232 /* Check whether preconditions are at all allowed, verify them as far as
233 * possible as necessary. For shell_quote() simplicity let's just ignore an
234 * empty precondition */
235 if((ctag = argv[2]) != NULL && *ctag != '\0'){
236 char const *xtag;
238 if(cmip->cmi_tt == a_COLOUR_TT_NONE){
239 n_err(_("`colour': %s does not support preconditions\n"),
240 n_shexp_quote_cp(mapname, FAL0));
241 goto jleave;
242 }else if((xtag = a_colour__tag_identify(cmip, ctag, &regexp)) ==
243 n_COLOUR_TAG_ERR){
244 /* I18N: ..of colour mapping */
245 n_err(_("`colour': %s: invalid precondition: %s\n"),
246 n_shexp_quote_cp(mapname, FAL0), n_shexp_quote_cp(ctag, FAL0));
247 goto jleave;
249 ctag = xtag;
252 /* At this time we have all the information to be able to query whether such
253 * a mapping is yet established. If so, destroy it */
254 for(blcmp = lcmp = NULL,
255 cmp = *(cmap =
256 &a_colour_g.cg_maps[ct][cmip->cmi_ctx][cmip->cmi_id]);
257 cmp != NULL; blcmp = lcmp, lcmp = cmp, cmp = cmp->cm_next){
258 char const *xctag = cmp->cm_tag;
260 if(xctag == ctag ||
261 (ctag != NULL && !a_COLOUR_TAG_IS_SPECIAL(ctag) &&
262 xctag != NULL && !a_COLOUR_TAG_IS_SPECIAL(xctag) &&
263 !strcmp(xctag, ctag))){
264 if(lcmp == NULL)
265 *cmap = cmp->cm_next;
266 else
267 lcmp->cm_next = cmp->cm_next;
268 a_colour_map_unref(cmp);
269 break;
273 /* Create mapping */
274 /* C99 */{
275 size_t tl, ul, cl;
276 char *bp, *cp;
278 if(!a_colour_iso6429(ct, &cp, argv[1])){
279 /* I18N: colour command: mapping: error message: user argument */
280 n_err(_("`colour': %s: %s: %s\n"), n_shexp_quote_cp(mapname, FAL0),
281 cp, n_shexp_quote_cp(argv[1], FAL0));
282 goto jleave;
285 tl = (ctag != NULL && !a_COLOUR_TAG_IS_SPECIAL(ctag)) ? strlen(ctag) : 0;
286 cmp = smalloc(n_VSTRUCT_SIZEOF(struct a_colour_map, cm_buf) +
287 tl +1 + (ul = strlen(argv[1])) +1 + (cl = strlen(cp)) +1);
289 /* .cm_buf stuff */
290 cmp->cm_pen.cp_dat.s = bp = cmp->cm_buf;
291 cmp->cm_pen.cp_dat.l = cl;
292 memcpy(bp, cp, ++cl);
293 bp += cl;
295 cmp->cm_user_off = (ui32_t)PTR2SIZE(bp - cmp->cm_buf);
296 memcpy(bp, argv[1], ++ul);
297 bp += ul;
299 if(tl > 0){
300 cmp->cm_tag = bp;
301 memcpy(bp, ctag, ++tl);
302 /*bp += tl;*/
303 }else
304 cmp->cm_tag = ctag;
306 /* Non-buf stuff; default mapping */
307 if(lcmp != NULL){
308 /* Default mappings must be last */
309 if(ctag == NULL){
310 while(lcmp->cm_next != NULL)
311 lcmp = lcmp->cm_next;
312 }else if(lcmp->cm_next == NULL && lcmp->cm_tag == NULL){
313 if((lcmp = blcmp) == NULL)
314 goto jlinkhead;
316 cmp->cm_next = lcmp->cm_next;
317 lcmp->cm_next = cmp;
318 }else{
319 jlinkhead:
320 cmp->cm_next = *cmap;
321 *cmap = cmp;
323 cmp->cm_cmi = cmip;
324 #ifdef HAVE_REGEX
325 cmp->cm_regex = regexp;
326 #endif
327 cmp->cm_refcnt = 0;
328 a_colour_map_ref(cmp);
330 rv = TRU1;
331 jleave:
332 NYD2_LEAVE;
333 return rv;
336 static bool_t
337 a_colour_unmux(char **argv){
338 char const *mapname, *ctag, *xtag;
339 struct a_colour_map **cmap, *lcmp, *cmp;
340 struct a_colour_map_id const *cmip;
341 enum a_colour_type ct;
342 bool_t aster, rv;
343 NYD2_ENTER;
345 rv = TRU1;
346 aster = FAL0;
348 if((ct = a_colour_type_find(*argv++)) == (enum a_colour_type)-1){
349 if(!n_is_all_or_aster(argv[-1])){
350 n_err(_("`uncolour': invalid colour type %s\n"),
351 n_shexp_quote_cp(argv[-1], FAL0));
352 rv = FAL0;
353 goto j_leave;
355 aster = TRU1;
356 ct = 0;
359 mapname = argv[0];
360 ctag = (mapname != NULL) ? argv[1] : mapname;
362 if(!a_colour_g.cg_is_init)
363 goto jemap;
365 /* Delete anything? */
366 jredo:
367 if(ctag == NULL && mapname[0] == '*' && mapname[1] == '\0'){
368 size_t i1, i2;
369 struct a_colour_map *tmp;
371 for(i1 = 0; i1 < n__COLOUR_CTX_MAX1; ++i1)
372 for(i2 = 0; i2 < n__COLOUR_IDS; ++i2)
373 for(cmp = *(cmap = &a_colour_g.cg_maps[ct][i1][i2]), *cmap = NULL;
374 cmp != NULL;){
375 tmp = cmp;
376 cmp = cmp->cm_next;
377 a_colour_map_unref(tmp);
379 }else{
380 if((cmip = a_colour_map_id_find(mapname)) == NULL){
381 rv = FAL0;
382 jemap:
383 /* I18N: colour command, mapping and precondition (option in quotes) */
384 n_err(_("`uncolour': non-existing mapping: %s%s%s\n"),
385 n_shexp_quote_cp(mapname, FAL0), (ctag == NULL ? n_empty : " "),
386 (ctag == NULL ? n_empty : n_shexp_quote_cp(ctag, FAL0)));
387 goto jleave;
390 if((xtag = ctag) != NULL){
391 if(cmip->cmi_tt == a_COLOUR_TT_NONE){
392 n_err(_("`uncolour': %s does not support preconditions\n"),
393 n_shexp_quote_cp(mapname, FAL0));
394 rv = FAL0;
395 goto jleave;
396 }else if((xtag = a_colour__tag_identify(cmip, ctag, NULL)) ==
397 n_COLOUR_TAG_ERR){
398 n_err(_("`uncolour': %s: invalid precondition: %s\n"),
399 n_shexp_quote_cp(mapname, FAL0), n_shexp_quote_cp(ctag, FAL0));
400 rv = FAL0;
401 goto jleave;
403 /* (Improve user experience) */
404 if(xtag != NULL && !a_COLOUR_TAG_IS_SPECIAL(xtag))
405 ctag = xtag;
408 lcmp = NULL;
409 cmp = *(cmap = &a_colour_g.cg_maps[ct][cmip->cmi_ctx][cmip->cmi_id]);
410 for(;;){
411 char const *xctag;
413 if(cmp == NULL){
414 rv = FAL0;
415 goto jemap;
417 if((xctag = cmp->cm_tag) == ctag)
418 break;
419 if(ctag != NULL && !a_COLOUR_TAG_IS_SPECIAL(ctag) &&
420 xctag != NULL && !a_COLOUR_TAG_IS_SPECIAL(xctag) &&
421 !strcmp(xctag, ctag))
422 break;
423 lcmp = cmp;
424 cmp = cmp->cm_next;
427 if(lcmp == NULL)
428 *cmap = cmp->cm_next;
429 else
430 lcmp->cm_next = cmp->cm_next;
431 a_colour_map_unref(cmp);
434 jleave:
435 if(aster && ++ct != a_COLOUR_T_NONE)
436 goto jredo;
437 j_leave:
438 NYD2_LEAVE;
439 return rv;
442 static bool_t
443 a_colour__show(enum a_colour_type ct){
444 struct a_colour_map *cmp;
445 size_t i1, i2;
446 bool_t rv;
447 NYD2_ENTER;
449 /* Show all possible types? */
450 if((rv = (ct == (enum a_colour_type)-1 ? TRU1 : FAL0)))
451 ct = 0;
452 jredo:
453 for(i1 = 0; i1 < n__COLOUR_CTX_MAX1; ++i1)
454 for(i2 = 0; i2 < n__COLOUR_IDS; ++i2){
455 if((cmp = a_colour_g.cg_maps[ct][i1][i2]) == NULL)
456 continue;
458 while(cmp != NULL){
459 char const *tagann, *tag;
461 tagann = n_empty;
462 if((tag = cmp->cm_tag) == NULL)
463 tag = n_empty;
464 else if(tag == n_COLOUR_TAG_SUM_DOT)
465 tag = "dot";
466 else if(tag == n_COLOUR_TAG_SUM_OLDER)
467 tag = "older";
468 #ifdef HAVE_REGEX
469 else if(cmp->cm_regex != NULL)
470 tagann = "[rx] ";
471 #endif
472 fprintf(n_stdout, "colour %s %-*s %s %s%s\n",
473 a_colour_types[ct], a_COLOUR_MAP_SHOW_FIELDWIDTH,
474 savecat(a_colour_ctx_prefixes[i1],
475 a_colour_map_ids[i1][i2].cmi_name),
476 (char const*)cmp->cm_buf + cmp->cm_user_off,
477 tagann, n_shexp_quote_cp(tag, TRU1));
478 cmp = cmp->cm_next;
482 if(rv && ++ct != a_COLOUR_T_NONE)
483 goto jredo;
484 rv = TRU1;
485 NYD2_LEAVE;
486 return rv;
489 static char const *
490 a_colour__tag_identify(struct a_colour_map_id const *cmip, char const *ctag,
491 void **regexpp){
492 NYD2_ENTER;
493 n_UNUSED(regexpp);
495 if((cmip->cmi_tt & a_COLOUR_TT_DOT) && !asccasecmp(ctag, "dot"))
496 ctag = n_COLOUR_TAG_SUM_DOT;
497 else if((cmip->cmi_tt & a_COLOUR_TT_OLDER) && !asccasecmp(ctag, "older"))
498 ctag = n_COLOUR_TAG_SUM_OLDER;
499 else if(cmip->cmi_tt & a_COLOUR_TT_HEADERS){
500 char *cp, c;
501 size_t i;
503 /* Can this be a valid list of headers? However, with regular expressions
504 * simply use the input as such if it appears to be a regex */
505 #ifdef HAVE_REGEX
506 if(n_is_maybe_regex(ctag)){
507 int s;
509 if(regexpp != NULL &&
510 (s = regcomp(*regexpp = smalloc(sizeof(regex_t)), ctag,
511 REG_EXTENDED | REG_ICASE | REG_NOSUB)) != 0){
512 n_err(_("`colour': invalid regular expression: %s: %s\n"),
513 n_shexp_quote_cp(ctag, FAL0), n_regex_err_to_doc(*regexpp, s));
514 free(*regexpp);
515 goto jetag;
517 }else
518 #endif
520 /* Normalize to lowercase and strip any whitespace before use */
521 i = strlen(ctag);
522 cp = salloc(i +1);
524 for(i = 0; (c = *ctag++) != '\0';){
525 bool_t isblspc = blankspacechar(c);
527 if(!isblspc && !alnumchar(c) && c != '-' && c != ',')
528 goto jetag;
529 /* Since we compare header names as they come from the message this
530 * lowercasing is however redundant: we need to asccasecmp() them */
531 if(!isblspc)
532 cp[i++] = lowerconv(c);
534 cp[i] = '\0';
535 ctag = cp;
537 }else
538 jetag:
539 ctag = n_COLOUR_TAG_ERR;
540 NYD2_LEAVE;
541 return ctag;
544 static struct a_colour_map_id const *
545 a_colour_map_id_find(char const *cp){
546 size_t i;
547 struct a_colour_map_id const (*cmip)[n__COLOUR_IDS], *rv;
548 NYD2_ENTER;
550 rv = NULL;
552 for(i = 0;; ++i){
553 if(i == n__COLOUR_IDS)
554 goto jleave;
555 else{
556 size_t j = strlen(a_colour_ctx_prefixes[i]);
557 if(!ascncasecmp(cp, a_colour_ctx_prefixes[i], j)){
558 cp += j;
559 break;
563 cmip = &a_colour_map_ids[i];
565 for(i = 0;; ++i){
566 if(i == n__COLOUR_IDS || (rv = &(*cmip)[i])->cmi_name[0] == '\0'){
567 rv = NULL;
568 break;
570 if(!asccasecmp(cp, rv->cmi_name))
571 break;
573 jleave:
574 NYD2_LEAVE;
575 return rv;
578 static struct a_colour_map *
579 a_colour_map_find(enum n_colour_id cid, enum n_colour_ctx cctx,
580 char const *ctag){
581 struct a_colour_map *cmp;
582 NYD2_ENTER;
584 cmp = a_colour_g.cg_maps[a_colour_g.cg_type][cctx][cid];
585 for(; cmp != NULL; cmp = cmp->cm_next){
586 char const *xtag = cmp->cm_tag;
588 if(xtag == ctag)
589 break;
590 if(xtag == NULL)
591 break;
592 if(ctag == NULL || a_COLOUR_TAG_IS_SPECIAL(ctag))
593 continue;
594 #ifdef HAVE_REGEX
595 if(cmp->cm_regex != NULL){
596 if(regexec(cmp->cm_regex, ctag, 0,NULL, 0) != REG_NOMATCH)
597 break;
598 }else
599 #endif
600 if(cmp->cm_cmi->cmi_tt & a_COLOUR_TT_HEADERS){
601 char *hlist = savestr(xtag), *cp;
603 while((cp = n_strsep(&hlist, ',', TRU1)) != NULL){
604 if(!asccasecmp(cp, ctag))
605 break;
607 if(cp != NULL)
608 break;
611 NYD2_LEAVE;
612 return cmp;
615 static void
616 a_colour_map_unref(struct a_colour_map *self){
617 NYD2_ENTER;
618 if(--self->cm_refcnt == 0){
619 #ifdef HAVE_REGEX
620 if(self->cm_regex != NULL){
621 regfree(self->cm_regex);
622 free(self->cm_regex);
624 #endif
625 free(self);
627 NYD2_LEAVE;
630 static bool_t
631 a_colour_iso6429(enum a_colour_type ct, char **store, char const *spec){
632 struct isodesc{
633 char id_name[15];
634 char id_modc;
635 } const fta[] = {
636 {"bold", '1'}, {"underline", '4'}, {"reverse", '7'}
637 }, ca[] = {
638 {"black", '0'}, {"red", '1'}, {"green", '2'}, {"brown", '3'},
639 {"blue", '4'}, {"magenta", '5'}, {"cyan", '6'}, {"white", '7'}
640 }, *idp;
641 char *xspec, *cp, fg[3], cfg[2 + 2*sizeof("255")];
642 ui8_t ftno_base, ftno;
643 bool_t rv;
644 NYD_ENTER;
646 rv = FAL0;
647 /* 0/1 indicate usage, thereafter possibly 256 color sequences */
648 cfg[0] = cfg[1] = 0;
650 /* Since we use salloc(), reuse the n_strsep() buffer also for the return
651 * value, ensure we have enough room for that */
652 /* C99 */{
653 size_t i = strlen(spec) +1;
654 xspec = salloc(n_MAX(i, sizeof("\033[1;4;7;38;5;255;48;5;255m")));
655 memcpy(xspec, spec, i);
656 spec = xspec;
659 /* Iterate over the colour spec */
660 ftno = 0;
661 while((cp = n_strsep(&xspec, ',', TRU1)) != NULL){
662 char *y, *x = strchr(cp, '=');
663 if(x == NULL){
664 jbail:
665 *store = n_UNCONST(_("invalid attribute list"));
666 goto jleave;
668 *x++ = '\0';
670 if(!asccasecmp(cp, "ft")){
671 if(!asccasecmp(x, "inverse")){
672 n_OBSOLETE(_("please use reverse for ft= fonts, not inverse"));
673 x = n_UNCONST("reverse");
675 for(idp = fta;; ++idp)
676 if(idp == fta + n_NELEM(fta)){
677 *store = n_UNCONST(_("invalid font attribute"));
678 goto jleave;
679 }else if(!asccasecmp(x, idp->id_name)){
680 if(ftno < n_NELEM(fg))
681 fg[ftno++] = idp->id_modc;
682 else{
683 *store = n_UNCONST(_("too many font attributes"));
684 goto jleave;
686 break;
688 }else if(!asccasecmp(cp, "fg")){
689 y = cfg + 0;
690 goto jiter_colour;
691 }else if(!asccasecmp(cp, "bg")){
692 y = cfg + 1;
693 jiter_colour:
694 if(ct == a_COLOUR_T_1){
695 *store = n_UNCONST(_("colours are not allowed"));
696 goto jleave;
698 /* Maybe 256 color spec */
699 if(digitchar(x[0])){
700 ui8_t xv;
702 if(ct == a_COLOUR_T_8){
703 *store = n_UNCONST(_("invalid colour for 8-colour mode"));
704 goto jleave;
707 if((n_idec_ui8_cp(&xv, x, 10, NULL
708 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
709 ) != n_IDEC_STATE_CONSUMED){
710 *store = n_UNCONST(_("invalid 256-colour specification"));
711 goto jleave;
713 y[0] = 5;
714 memcpy((y == &cfg[0] ? y + 2 : y + 1 + sizeof("255")), x,
715 (x[1] == '\0' ? 2 : (x[2] == '\0' ? 3 : 4)));
716 }else for(idp = ca;; ++idp)
717 if(idp == ca + n_NELEM(ca)){
718 *store = n_UNCONST(_("invalid colour attribute"));
719 goto jleave;
720 }else if(!asccasecmp(x, idp->id_name)){
721 y[0] = 1;
722 y[2] = idp->id_modc;
723 break;
725 }else
726 goto jbail;
729 /* Restore our salloc() buffer, create return value */
730 xspec = n_UNCONST(spec);
731 if(ftno > 0 || cfg[0] || cfg[1]){ /* TODO unite/share colour setters */
732 xspec[0] = '\033';
733 xspec[1] = '[';
734 xspec += 2;
736 for(ftno_base = ftno; ftno > 0;){
737 if(ftno-- != ftno_base)
738 *xspec++ = ';';
739 *xspec++ = fg[ftno];
742 if(cfg[0]){
743 if(ftno_base > 0)
744 *xspec++ = ';';
745 xspec[0] = '3';
746 if(cfg[0] == 1){
747 xspec[1] = cfg[2];
748 xspec += 2;
749 }else{
750 memcpy(xspec + 1, "8;5;", 4);
751 xspec += 5;
752 for(ftno = 2; cfg[ftno] != '\0'; ++ftno)
753 *xspec++ = cfg[ftno];
757 if(cfg[1]){
758 if(ftno_base > 0 || cfg[0])
759 *xspec++ = ';';
760 xspec[0] = '4';
761 if(cfg[1] == 1){
762 xspec[1] = cfg[3];
763 xspec += 2;
764 }else{
765 memcpy(xspec + 1, "8;5;", 4);
766 xspec += 5;
767 for(ftno = 2 + sizeof("255"); cfg[ftno] != '\0'; ++ftno)
768 *xspec++ = cfg[ftno];
772 *xspec++ = 'm';
774 *xspec = '\0';
775 *store = n_UNCONST(spec);
776 rv = TRU1;
777 jleave:
778 NYD_LEAVE;
779 return rv;
782 FL int
783 c_colour(void *v){
784 int rv;
785 NYD_ENTER;
787 rv = !a_colour_mux(v);
788 NYD_LEAVE;
789 return rv;
792 FL int
793 c_uncolour(void *v){
794 int rv;
795 NYD_ENTER;
797 rv = !a_colour_unmux(v);
798 NYD_LEAVE;
799 return rv;
802 FL void
803 n_colour_stack_del(void *vp){
804 struct n_colour_env *cep;
805 NYD_ENTER;
807 if(vp == NULL){
808 vp = n_go_data->gdc_colour;
809 n_go_data->gdc_colour = NULL;
810 n_go_data->gdc_colour_active = FAL0;
813 while((cep = vp) != NULL){
814 vp = cep->ce_last;
816 if(cep->ce_current != NULL && cep->ce_outfp == n_stdout){
817 n_sighdl_t hdl;
819 hdl = n_signal(SIGPIPE, SIG_IGN);
820 fwrite(a_colour_g.cg_reset.cp_dat.s, a_colour_g.cg_reset.cp_dat.l, 1,
821 cep->ce_outfp);
822 fflush(cep->ce_outfp);
823 n_signal(SIGPIPE, hdl);
826 NYD_LEAVE;
829 FL void
830 n_colour_env_create(enum n_colour_ctx cctx, FILE *fp, bool_t pager_used){
831 struct n_colour_env *cep;
832 NYD_ENTER;
834 if(!(n_psonce & n_PSO_INTERACTIVE))
835 goto jleave;
837 if(!a_colour_g.cg_is_init)
838 a_colour_init();
840 /* TODO reset the outer level? Iff ce_outfp==fp? */
841 cep = salloc(sizeof *cep);
842 cep->ce_last = n_go_data->gdc_colour;
843 cep->ce_enabled = FAL0;
844 cep->ce_ctx = cctx;
845 cep->ce_ispipe = pager_used;
846 cep->ce_outfp = fp;
847 cep->ce_current = NULL;
848 n_go_data->gdc_colour_active = FAL0;
849 n_go_data->gdc_colour = cep;
851 if(ok_blook(colour_disable) || (pager_used && !ok_blook(colour_pager)))
852 goto jleave;
854 if(n_UNLIKELY(a_colour_g.cg_type == a_COLOUR_T_UNKNOWN)){
855 struct n_termcap_value tv;
857 if(!n_termcap_query(n_TERMCAP_QUERY_colors, &tv)){
858 a_colour_g.cg_type = a_COLOUR_T_NONE;
859 goto jleave;
860 }else
861 switch(tv.tv_data.tvd_numeric){
862 case 256: a_colour_g.cg_type = a_COLOUR_T_256; break;
863 case 8: a_colour_g.cg_type = a_COLOUR_T_8; break;
864 case 1: a_colour_g.cg_type = a_COLOUR_T_1; break;
865 default:
866 if(n_poption & n_PO_D_V)
867 n_err(_("Ignoring unsupported termcap entry for Co(lors)\n"));
868 /* FALLTHRU */
869 case 0:
870 a_colour_g.cg_type = a_COLOUR_T_NONE;
871 goto jleave;
875 if(a_colour_g.cg_type == a_COLOUR_T_NONE)
876 goto jleave;
878 n_go_data->gdc_colour_active = cep->ce_enabled = TRU1;
879 jleave:
880 NYD_LEAVE;
883 FL void
884 n_colour_env_gut(void){
885 struct n_colour_env *cep;
886 NYD_ENTER;
888 if(!(n_psonce & n_PSO_INTERACTIVE))
889 goto jleave;
891 /* TODO v15: Could happen because of jump, causing _stack_del().. */
892 if((cep = n_go_data->gdc_colour) == NULL)
893 goto jleave;
894 n_go_data->gdc_colour_active = ((n_go_data->gdc_colour = cep->ce_last
895 ) != NULL && cep->ce_last->ce_enabled);
897 if(cep->ce_current != NULL){
898 n_sighdl_t hdl;
900 hdl = n_signal(SIGPIPE, SIG_IGN);
901 fwrite(a_colour_g.cg_reset.cp_dat.s, a_colour_g.cg_reset.cp_dat.l, 1,
902 cep->ce_outfp);
903 n_signal(SIGPIPE, hdl);
905 jleave:
906 NYD_LEAVE;
909 FL void
910 n_colour_put(enum n_colour_id cid, char const *ctag){
911 NYD_ENTER;
912 if(n_COLOUR_IS_ACTIVE()){
913 struct n_colour_env *cep;
915 cep = n_go_data->gdc_colour;
917 if(cep->ce_current != NULL)
918 fwrite(a_colour_g.cg_reset.cp_dat.s, a_colour_g.cg_reset.cp_dat.l, 1,
919 cep->ce_outfp);
921 if((cep->ce_current = a_colour_map_find(cid, cep->ce_ctx, ctag)) != NULL)
922 fwrite(cep->ce_current->cm_pen.cp_dat.s,
923 cep->ce_current->cm_pen.cp_dat.l, 1, cep->ce_outfp);
925 NYD_LEAVE;
928 FL void
929 n_colour_reset(void){
930 NYD_ENTER;
931 if(n_COLOUR_IS_ACTIVE()){
932 struct n_colour_env *cep;
934 cep = n_go_data->gdc_colour;
936 if(cep->ce_current != NULL){
937 cep->ce_current = NULL;
938 fwrite(a_colour_g.cg_reset.cp_dat.s, a_colour_g.cg_reset.cp_dat.l, 1,
939 cep->ce_outfp);
942 NYD_LEAVE;
945 FL struct str const *
946 n_colour_reset_to_str(void){
947 struct str *rv;
948 NYD_ENTER;
950 if(n_COLOUR_IS_ACTIVE())
951 rv = &a_colour_g.cg_reset.cp_dat;
952 else
953 rv = NULL;
954 NYD_LEAVE;
955 return rv;
958 FL struct n_colour_pen *
959 n_colour_pen_create(enum n_colour_id cid, char const *ctag){
960 struct a_colour_map *cmp;
961 struct n_colour_pen *rv;
962 NYD_ENTER;
964 if(n_COLOUR_IS_ACTIVE() &&
965 (cmp = a_colour_map_find(cid, n_go_data->gdc_colour->ce_ctx, ctag)
966 ) != NULL){
967 union {void *vp; char *cp; struct n_colour_pen *cpp;} u;
969 u.vp = cmp;
970 rv = u.cpp;
971 }else
972 rv = NULL;
973 NYD_LEAVE;
974 return rv;
977 FL void
978 n_colour_pen_put(struct n_colour_pen *self){
979 NYD_ENTER;
980 if(n_COLOUR_IS_ACTIVE()){
981 union {void *vp; char *cp; struct a_colour_map *cmp;} u;
982 struct n_colour_env *cep;
984 cep = n_go_data->gdc_colour;
985 u.vp = self;
987 if(u.cmp != cep->ce_current){
988 if(cep->ce_current != NULL)
989 fwrite(a_colour_g.cg_reset.cp_dat.s, a_colour_g.cg_reset.cp_dat.l,
990 1, cep->ce_outfp);
992 if(u.cmp != NULL)
993 fwrite(self->cp_dat.s, self->cp_dat.l, 1, cep->ce_outfp);
994 cep->ce_current = u.cmp;
997 NYD_LEAVE;
1000 FL struct str const *
1001 n_colour_pen_to_str(struct n_colour_pen *self){
1002 struct str *rv;
1003 NYD_ENTER;
1005 if(n_COLOUR_IS_ACTIVE() && self != NULL)
1006 rv = &self->cp_dat;
1007 else
1008 rv = NULL;
1009 NYD_LEAVE;
1010 return rv;
1012 #endif /* HAVE_COLOUR */
1014 /* s-it-mode */