1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ `colour' and `mono' 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 CTA(n_COLOUR_ID_RESET
> _n_COLOUR_ID_VIEW_USER_HEADERS
);
29 CTA(n_COLOUR_ID_RESET
> n_COLOUR_ID_HSUM_THREAD
);
31 /* Create an ISO 6429 (ECMA-48/ANSI) terminal control escape sequence */
32 static char * _colour_iso6429(char const *wish
);
35 _colour_iso6429(char const *wish
)
41 {"bold", '1'}, {"underline", '4'}, {"reverse", '7'}
43 {"black", '0'}, {"red", '1'}, {"green", '2'}, {"brown", '3'},
44 {"blue", '4'}, {"magenta", '5'}, {"cyan", '6'}, {"white", '7'}
46 char const * const wish_orig
= wish
;
47 char *xwish
, *cp
, fg
[3], cfg
[3] = {0, 0, 0};
48 ui8_t ftno_base
, ftno
;
51 /* Since we use salloc(), reuse the n_strsep() buffer also for the return
52 * value, ensure we have enough room for that */
54 size_t i
= strlen(wish
) +1;
55 xwish
= salloc(MAX(i
, sizeof("\033[1;4;7;30;40m")));
56 memcpy(xwish
, wish
, i
);
60 /* Iterate over the colour spec */
62 while ((cp
= n_strsep(&xwish
, ',', TRU1
)) != NULL
) {
63 char *y
, *x
= strchr(cp
, '=');
66 n_err(_("Invalid colour specification \"%s\": %s\n"), wish_orig
, cp
);
71 if (!asccasecmp(cp
, "ft")) {
72 if (!asccasecmp(x
, "inverse")) {
73 OBSOLETE(_("please use \"reverse\" not \"inverse\" for ft= fonts"));
74 x
= UNCONST("reverse");
76 for (idp
= fta
;; ++idp
)
77 if (idp
== fta
+ NELEM(fta
))
79 else if (!asccasecmp(x
, idp
->id_name
)) {
81 fg
[ftno
++] = idp
->id_modc
;
86 } else if (!asccasecmp(cp
, "fg")) {
89 } else if (!asccasecmp(cp
, "bg")) {
92 for (idp
= ca
;; ++idp
)
93 if (idp
== ca
+ NELEM(ca
))
95 else if (!asccasecmp(x
, idp
->id_name
)) {
103 /* Restore our salloc() buffer, create return value */
104 xwish
= UNCONST(wish
);
105 if (ftno
> 0 || cfg
[1] || cfg
[2]) {
110 for (ftno_base
= ftno
; ftno
> 0;) {
111 if (ftno
-- != ftno_base
)
125 if (ftno_base
> 0 || cfg
[1])
136 return UNCONST(wish
);
140 n_colour_table_create(bool_t pager_used
, bool_t headerview
)
142 union {char *cp
; char const *ccp
; void *vp
; struct n_colour_table
*ctp
;} u
;
144 struct n_colour_table
*ct
;
147 if (ok_blook(colour_disable
) || (pager_used
&& !ok_blook(colour_pager
)))
150 char *term
, *okterms
;
152 if ((term
= env_vlook("TERM", FAL0
)) == NULL
)
154 /* terminfo rocks: if we find "color", assume it's right */
155 if (strstr(term
, "color") != NULL
)
157 if ((okterms
= ok_vlook(colour_terms
)) == NULL
)
158 okterms
= UNCONST(n_COLOUR_TERMS
);
159 okterms
= savestr(okterms
);
162 while ((u
.cp
= n_strsep(&okterms
, ',', TRU1
)) != NULL
)
163 if (!strncmp(u
.cp
, term
, i
))
169 n_colour_table
= ct
= salloc(sizeof *ct
); /* XXX lex.c yet resets (FILTER!) */
172 enum n_colour_id cid
;
175 /* Header summary set */
177 {ok_v_colour_hsum_current
,
178 n_COLOUR_ID_HSUM_CURRENT
, n_COLOUR_HSUM_CURRENT
},
179 {ok_v_colour_hsum_dot
,
180 n_COLOUR_ID_HSUM_DOT
, n_COLOUR_HSUM_DOT
},
181 {ok_v_colour_hsum_dot_mark
,
182 n_COLOUR_ID_HSUM_DOT_MARK
, n_COLOUR_HSUM_DOT_MARK
},
183 {ok_v_colour_hsum_dot_thread
,
184 n_COLOUR_ID_HSUM_DOT_THREAD
, n_COLOUR_HSUM_DOT_THREAD
},
185 {ok_v_colour_hsum_older
,
186 n_COLOUR_ID_HSUM_OLDER
, n_COLOUR_HSUM_OLDER
},
187 {ok_v_colour_hsum_thread
,
188 n_COLOUR_ID_HSUM_THREAD
, n_COLOUR_HSUM_THREAD
}
190 {ok_v_colour_view_msginfo
,
191 n_COLOUR_ID_VIEW_MSGINFO
, n_COLOUR_VIEW_MSGINFO
},
192 {ok_v_colour_view_partinfo
,
193 n_COLOUR_ID_VIEW_PARTINFO
, n_COLOUR_VIEW_PARTINFO
},
194 {ok_v_colour_view_from_
,
195 n_COLOUR_ID_VIEW_FROM_
, n_COLOUR_VIEW_FROM_
},
196 {ok_v_colour_view_header
,
197 n_COLOUR_ID_VIEW_HEADER
, n_COLOUR_VIEW_HEADER
},
198 {ok_v_colour_view_uheader
,
199 n_COLOUR_ID_VIEW_UHEADER
, n_COLOUR_VIEW_UHEADER
},
200 {ok_v_colour_view_user_headers
,
201 _n_COLOUR_ID_VIEW_USER_HEADERS
, n_COLOUR_VIEW_USER_HEADERS
}
202 }, oview_map
[] = { /* TODO Message display set, !*v15-compat* */
203 {ok_v_colour_msginfo
,
204 n_COLOUR_ID_VIEW_MSGINFO
, n_COLOUR_VIEW_MSGINFO
},
205 {ok_v_colour_partinfo
,
206 n_COLOUR_ID_VIEW_PARTINFO
, n_COLOUR_VIEW_PARTINFO
},
208 n_COLOUR_ID_VIEW_FROM_
, n_COLOUR_VIEW_FROM_
},
210 n_COLOUR_ID_VIEW_HEADER
, n_COLOUR_VIEW_HEADER
},
211 {ok_v_colour_uheader
,
212 n_COLOUR_ID_VIEW_UHEADER
, n_COLOUR_VIEW_UHEADER
},
213 {ok_v_colour_user_headers
,
214 _n_COLOUR_ID_VIEW_USER_HEADERS
, n_COLOUR_VIEW_USER_HEADERS
}
217 enum okeys v_nocolor
; /* *-user-headers* is a string list */
218 bool_t v15noted
; /* TODO v15-compat */
222 nelem
= NELEM(hsum_map
);
224 } else if (ok_blook(v15_compat
)) {
226 nelem
= NELEM(view_map
);
227 v_nocolor
= ok_v_colour_view_user_headers
;
231 nelem
= NELEM(oview_map
);
232 v_nocolor
= ok_v_colour_user_headers
;
236 for (i
= 0; i
< nelem
; ++i
) {
237 if ((u
.cp
= _var_oklook(map
[i
].okey
)) == NULL
)
238 u
.ccp
= map
[i
].defval
;
239 else if (!v15noted
) {
240 OBSOLETE(_("please use *colour-view-XY* instead of *colour-XY*"));
243 if (u
.ccp
[0] != '\0') {
244 if (headerview
|| map
[i
].okey
!= v_nocolor
)
245 u
.cp
= _colour_iso6429(u
.ccp
);
247 u
.cp
= savestr(u
.cp
);
248 if ((ct
->ct_csinfo
[map
[i
].cid
].l
= strlen(u
.cp
)) == 0)
250 ct
->ct_csinfo
[map
[i
].cid
].s
= u
.cp
;
252 ct
->ct_csinfo
[map
[i
].cid
].l
= 0;
253 ct
->ct_csinfo
[map
[i
].cid
].s
= NULL
;
258 /* XXX using [0m is hard, we should selectively turn off what is on */
259 ct
->ct_csinfo
[n_COLOUR_ID_RESET
].l
= sizeof("\033[0m") -1;
260 ct
->ct_csinfo
[n_COLOUR_ID_RESET
].s
= UNCONST("\033[0m");
266 n_colour_put(FILE *fp
, enum n_colour_id cid
)
269 if (n_colour_table
!= NULL
) {
270 struct str
const *cp
= n_colour_get(cid
);
273 fwrite(cp
->s
, cp
->l
, 1, fp
);
279 n_colour_put_user_header(FILE *fp
, char const *name
)
281 enum n_colour_id cid
= n_COLOUR_ID_VIEW_HEADER
;
282 struct str
const *uheads
;
283 char *cp
, *cp_base
, *x
;
287 if (n_colour_table
== NULL
)
290 /* Normal header colours if there are no user headers */
291 uheads
= n_colour_table
->ct_csinfo
+ _n_COLOUR_ID_VIEW_USER_HEADERS
;
292 if (uheads
->s
== NULL
)
295 /* Iterate over all entries in the *colour-user-headers* list */
296 cp
= ac_alloc(uheads
->l
+1);
297 memcpy(cp
, uheads
->s
, uheads
->l
+1);
299 namelen
= strlen(name
);
300 while ((x
= n_strsep(&cp
, ',', TRU1
)) != NULL
) {
301 size_t l
= (cp
!= NULL
) ? PTR2SIZE(cp
- x
) - 1 : strlen(x
);
302 if (l
== namelen
&& !ascncasecmp(x
, name
, namelen
)) {
303 cid
= n_COLOUR_ID_VIEW_UHEADER
;
309 n_colour_put(fp
, cid
);
315 n_colour_reset(FILE *fp
)
318 if (n_colour_table
!= NULL
)
319 fwrite("\033[0m", 4, 1, fp
);
323 FL
struct str
const *
324 n_colour_get(enum n_colour_id cid
)
326 struct str
const *rv
;
329 if (n_colour_table
!= NULL
) {
330 if ((rv
= n_colour_table
->ct_csinfo
+ cid
)->s
== NULL
)
332 assert(cid
!= n_COLOUR_ID_RESET
|| rv
!= NULL
);
338 #endif /* HAVE_COLOUR */