NeoMutt  2022-04-29-247-gc6aae8
Teaching an old dog new tricks
DOXYGEN
ansi.c File Reference

ANSI Colours. More...

#include "config.h"
#include <stddef.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdlib.h>
#include "mutt/lib.h"
#include "gui/lib.h"
#include "ansi.h"
#include "lib.h"
+ Include dependency graph for ansi.c:

Go to the source code of this file.

Functions

static bool ansi_is_end_char (char c)
 Is this the end of a sequence? More...
 
int ansi_skip_sequence (const char *str)
 Skip an unrecognised sequence. More...
 
int ansi_color_seq_length (const char *str)
 Is this an ANSI escape sequence? More...
 
static int ansi_color_parse_single (const char *buf, struct AnsiColor *ansi, bool dry_run)
 Parse a single ANSI escape sequence. More...
 
static void ansi_color_list_add (struct AttrColorList *acl, struct AnsiColor *ansi)
 Add an Ansi colour to the list. More...
 
int ansi_color_parse (const char *str, struct AnsiColor *ansi, struct AttrColorList *acl, bool dry_run)
 Parse a string of ANSI escape sequence. More...
 

Detailed Description

ANSI Colours.

Authors
  • Richard Russon

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file ansi.c.

Function Documentation

◆ ansi_is_end_char()

static bool ansi_is_end_char ( char  c)
inlinestatic

Is this the end of a sequence?

Parameters
cCharacter to test
Return values
trueIs it a valid end char

Definition at line 44 of file ansi.c.

45{
46 return ((c == 'm') || (c == ';'));
47}
+ Here is the caller graph for this function:

◆ ansi_skip_sequence()

int ansi_skip_sequence ( const char *  str)

Skip an unrecognised sequence.

Parameters
strString to examine
Return values
numNumber of characters to skip over

Definition at line 54 of file ansi.c.

55{
56 if (!str || (str[0] == '\0'))
57 return 0;
58
59 int count = 1;
60 while ((str[0] != '\0') && !ansi_is_end_char(str[0]))
61 {
62 str++;
63 count++;
64 }
65
66 return count;
67}
static bool ansi_is_end_char(char c)
Is this the end of a sequence?
Definition: ansi.c:44
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ansi_color_seq_length()

int ansi_color_seq_length ( const char *  str)

Is this an ANSI escape sequence?

Parameters
strString to test
Return values
0No, not an ANSI sequence
>0Length of the ANSI sequence

Match ANSI escape sequences of type 'm', e.g.

  • <esc>[1;32m

Definition at line 78 of file ansi.c.

79{
80 if (!str || !*str)
81 return 0;
82
83 if ((str[0] != '\033') || (str[1] != '[')) // Escape
84 return 0;
85
86 int i = 2;
87 while ((str[i] != '\0') && (isdigit(str[i]) || (str[i] == ';')))
88 {
89 i++;
90 }
91
92 if (str[i] == 'm')
93 return i + 1;
94
95 return 0;
96}
+ Here is the caller graph for this function:

◆ ansi_color_parse_single()

static int ansi_color_parse_single ( const char *  buf,
struct AnsiColor ansi,
bool  dry_run 
)
static

Parse a single ANSI escape sequence.

Parameters
bufString to parse
ansiAnsiColor for the result
dry_runDon't parse anything, just skip over
Return values
numLength of the escape sequence

Parse an ANSI escape sequence into ansi. Calling this function repeatedly, will accumulate sequences in ansi.

Definition at line 108 of file ansi.c.

109{
110 int seq_len = ansi_color_seq_length(buf);
111 if (seq_len == 0)
112 return 0;
113
114 if (dry_run || !ansi)
115 return seq_len;
116
117 int pos = 2; // Skip '<esc>['
118
119 while (pos < seq_len)
120 {
121 if ((buf[pos] == '1') && ansi_is_end_char(buf[pos + 1]))
122 {
123 ansi->attrs |= A_BOLD;
124 pos += 2;
125 }
126 else if ((buf[pos] == '3') && ansi_is_end_char(buf[pos + 1]))
127 {
128 ansi->attrs |= A_ITALIC;
129 pos += 2;
130 }
131 else if ((buf[pos] == '4') && ansi_is_end_char(buf[pos + 1]))
132 {
133 ansi->attrs |= A_UNDERLINE;
134 pos += 2;
135 }
136 else if ((buf[pos] == '5') && ansi_is_end_char(buf[pos + 1]))
137 {
138 ansi->attrs |= A_BLINK;
139 pos += 2;
140 }
141 else if ((buf[pos] == '7') && ansi_is_end_char(buf[pos + 1]))
142 {
143 ansi->attrs |= A_REVERSE;
144 pos += 2;
145 }
146 else if ((buf[pos] == '0') && ansi_is_end_char(buf[pos + 1]))
147 {
148 ansi->fg = COLOR_DEFAULT;
149 ansi->bg = COLOR_DEFAULT;
150 ansi->attrs = 0;
151 ansi->attr_color = NULL;
152 pos += 2;
153 }
154 else if (buf[pos] == '3')
155 {
156 // 30-37 basic fg
157 if ((buf[pos + 1] >= '0') && (buf[pos + 1] < '8') && ansi_is_end_char(buf[pos + 2]))
158 {
159 ansi->fg = buf[pos + 1] - '0';
160 pos += 3;
161 }
162 else if (buf[pos + 1] == '8')
163 {
164 if (mutt_str_startswith(buf + pos, "38;5;") && isdigit(buf[pos + 5]))
165 {
166 // 38;5;n palette fg
167 char *end = NULL;
168 int value = strtoul(buf + pos + 5, &end, 10);
169 if ((value >= 0) && (value < 256) && end && ansi_is_end_char(end[0]))
170 {
171 ansi->fg = value;
172 pos += end - &buf[pos];
173 }
174 else
175 {
176 pos += ansi_skip_sequence(buf + pos);
177 }
178 }
179 else if (mutt_str_startswith(buf + pos, "38;2;") && isdigit(buf[pos + 5]))
180 {
181 // 38;2;R;G;B true colour fg
182 pos += ansi_skip_sequence(buf + pos + 5);
183 pos += ansi_skip_sequence(buf + pos);
184 pos += ansi_skip_sequence(buf + pos);
185 }
186 }
187 else if ((buf[pos + 1] == '9') && ansi_is_end_char(buf[pos + 2]))
188 {
189 // default fg
190 ansi->fg = COLOR_DEFAULT;
191 pos += 2;
192 }
193 else
194 {
195 pos += ansi_skip_sequence(buf + pos);
196 }
197 }
198 else if (buf[pos] == '4')
199 {
200 // 40-47 basic bg
201 if ((buf[pos + 1] >= '0') && (buf[pos + 1] < '8'))
202 {
203 ansi->bg = buf[pos + 1] - '0';
204 pos += 3;
205 }
206 else if (buf[pos + 1] == '8')
207 {
208 if (mutt_str_startswith(buf + pos, "48;5;") && isdigit(buf[pos + 5]))
209 {
210 // 48;5;n palette bg
211 char *end = NULL;
212 int value = strtoul(buf + pos + 5, &end, 10);
213 if ((value >= 0) && (value < 256) && end && ansi_is_end_char(end[0]))
214 {
215 ansi->bg = value;
216 pos += end - &buf[pos];
217 }
218 else
219 {
220 pos += ansi_skip_sequence(buf + pos);
221 }
222 }
223 else if (mutt_str_startswith(buf + pos, "48;2;") && isdigit(buf[pos + 5]))
224 {
225 // 48;2;R;G;B true colour bg
226 pos += ansi_skip_sequence(buf + pos + 5);
227 pos += ansi_skip_sequence(buf + pos);
228 pos += ansi_skip_sequence(buf + pos);
229 }
230 else
231 {
232 pos += ansi_skip_sequence(buf + pos);
233 }
234 }
235 else if ((buf[pos + 1] == '9') && ansi_is_end_char(buf[pos + 2]))
236 {
237 // default bg
238 ansi->bg = COLOR_DEFAULT;
239 pos += 2;
240 }
241 }
242 else
243 {
244 while ((pos < seq_len) && (buf[pos] != ';'))
245 pos++;
246 }
247 }
248
249 return pos;
250}
int ansi_skip_sequence(const char *str)
Skip an unrecognised sequence.
Definition: ansi.c:54
int ansi_color_seq_length(const char *str)
Is this an ANSI escape sequence?
Definition: ansi.c:78
#define COLOR_DEFAULT
Definition: color.h:99
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
int attrs
Attributes, e.g. A_BOLD.
Definition: ansi.h:36
int bg
Background colour.
Definition: ansi.h:38
int fg
Foreground colour.
Definition: ansi.h:37
struct AttrColor * attr_color
Curses colour of text.
Definition: ansi.h:35
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ansi_color_list_add()

static void ansi_color_list_add ( struct AttrColorList *  acl,
struct AnsiColor ansi 
)
static

Add an Ansi colour to the list.

Parameters
aclList of unique colours
ansiColour to add

Keep track of all the unique ANSI colours in a list.

Definition at line 259 of file ansi.c.

260{
261 if (!acl || !ansi)
262 return;
263
264 if ((ansi->fg == COLOR_DEFAULT) && (ansi->bg == COLOR_DEFAULT))
265 {
266 switch (ansi->attrs)
267 {
268 case 0:
269 return;
270 case A_BOLD:
272 return;
273 case A_ITALIC:
275 return;
276 case A_UNDERLINE:
278 return;
279 }
280 }
281
282 struct AttrColor *ac = attr_color_list_find(acl, ansi->fg, ansi->bg, ansi->attrs);
283 if (ac)
284 {
285 ansi->attr_color = ac;
286 return;
287 }
288
289 ac = attr_color_new();
290 ac->attrs = ansi->attrs;
291
292 struct CursesColor *cc = curses_color_new(ansi->fg, ansi->bg);
293 ac->curses_color = cc;
294 ansi->attr_color = ac;
295
296 TAILQ_INSERT_TAIL(acl, ac, entries);
297 attr_color_list_dump(acl, "AnsiColors");
298}
struct AttrColor * attr_color_list_find(struct AttrColorList *acl, uint32_t fg, uint32_t bg, int attrs)
Find an AttrColor in a list.
Definition: attr.c:119
struct AttrColor * attr_color_new(void)
Create a new AttrColor.
Definition: attr.c:80
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition: simple.c:74
@ MT_COLOR_BOLD
Bold text.
Definition: color.h:40
@ MT_COLOR_ITALIC
Italic text.
Definition: color.h:50
@ MT_COLOR_UNDERLINE
Underlined text.
Definition: color.h:74
struct CursesColor * curses_color_new(int fg, int bg)
Create a new CursesColor.
Definition: curses.c:148
void attr_color_list_dump(struct AttrColorList *acl, const char *title)
Dump all the Attr Colours to the log.
Definition: debug.c:225
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:809
A curses colour and its attributes.
Definition: attr.h:35
int attrs
Text attributes, e.g. A_BOLD.
Definition: attr.h:37
struct CursesColor * curses_color
Underlying Curses colour.
Definition: attr.h:36
Colour in the ncurses palette.
Definition: curses2.h:38
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ansi_color_parse()

int ansi_color_parse ( const char *  str,
struct AnsiColor ansi,
struct AttrColorList *  acl,
bool  dry_run 
)

Parse a string of ANSI escape sequence.

Parameters
strString to parse
ansiAnsiColor for the result
aclList to store the unique colours
dry_runIf true, parse but don't save the sequence
Return values
numTotal length of the escape sequences

Parse (multiple) ANSI sequence(s) into ansi. If the colour hasn't been seen before, store the it in acl.

Definition at line 311 of file ansi.c.

313{
314 int seq_len = 0;
315 int total_len = 0;
316
317 while ((seq_len = ansi_color_parse_single(str + total_len, ansi, dry_run)) != 0)
318 {
319 // color_debug(LL_DEBUG5, "seq_len = %d\n", seq_len);
320 total_len += seq_len;
321 }
322
323 ansi_color_list_add(acl, ansi);
324
325 return total_len;
326}
static int ansi_color_parse_single(const char *buf, struct AnsiColor *ansi, bool dry_run)
Parse a single ANSI escape sequence.
Definition: ansi.c:108
static void ansi_color_list_add(struct AttrColorList *acl, struct AnsiColor *ansi)
Add an Ansi colour to the list.
Definition: ansi.c:259
+ Here is the call graph for this function:
+ Here is the caller graph for this function: