NeoMutt  2025-09-05-2-g4bf191
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
parse_ansi.c
Go to the documentation of this file.
1
29#include "config.h"
30#include <stdbool.h>
31#include <stddef.h>
32#include <stdlib.h>
33#include "mutt/lib.h"
34#include "gui/lib.h"
35#include "parse_ansi.h"
36#include "ansi.h"
37#include "attr.h"
38#include "color.h"
39
44void ansi_color_reset(struct AnsiColor *ansi)
45{
46 if (!ansi)
47 return;
48
49 ansi->fg.color = COLOR_DEFAULT;
50 ansi->fg.type = CT_SIMPLE;
51
52 ansi->bg.color = COLOR_DEFAULT;
53 ansi->bg.type = CT_SIMPLE;
54
55 ansi->attrs = A_NORMAL;
56 ansi->attr_color = NULL;
57}
58
64static inline bool ansi_is_end_char(char c)
65{
66 return ((c == 'm') || (c == ';'));
67}
68
78int ansi_color_seq_length(const char *str)
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') && (mutt_isdigit(str[i]) || (str[i] == ';')))
88 {
89 i++;
90 }
91
92 if (str[i] == 'm')
93 return i + 1;
94
95 return 0;
96}
97
108int ansi_color_parse_single(const char *buf, struct AnsiColor *ansi, bool dry_run)
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] == '0') && mutt_isdigit(buf[pos + 1]))
122 {
123 pos++; // Skip the leading zero
124 }
125 else if ((buf[pos] == '0') && ansi_is_end_char(buf[pos + 1]))
126 {
127 ansi_color_reset(ansi);
128 pos += 2;
129 }
130 else if ((buf[pos] == '1') && ansi_is_end_char(buf[pos + 1]))
131 {
132 ansi->attrs |= A_BOLD;
133 pos += 2;
134 }
135 else if ((buf[pos] == '2') && mutt_isdigit(buf[pos + 1]) &&
136 ansi_is_end_char(buf[pos + 2]))
137 {
138 char digit = buf[pos + 1];
139 pos += 3;
140 if (digit == '2')
141 {
142 ansi->attrs &= ~A_BOLD; // Clear the flag
143 }
144 else if (digit == '3')
145 {
146 ansi->attrs &= ~A_ITALIC; // Clear the flag
147 }
148 else if (digit == '4')
149 {
150 ansi->attrs &= ~A_UNDERLINE; // Clear the flag
151 }
152 else if (digit == '5')
153 {
154 ansi->attrs &= ~A_BLINK; // Clear the flag
155 }
156 else if (digit == '7')
157 {
158 ansi->attrs &= ~A_REVERSE; // Clear the flag
159 }
160 }
161 else if ((buf[pos] == '3') && ansi_is_end_char(buf[pos + 1]))
162 {
163 ansi->attrs |= A_ITALIC;
164 pos += 2;
165 }
166 else if (buf[pos] == '3')
167 {
168 struct ColorElement *elem = &ansi->fg;
169
170 // 30-37 basic foreground
171 if ((buf[pos + 1] >= '0') && (buf[pos + 1] < '8') && ansi_is_end_char(buf[pos + 2]))
172 {
173 elem->color = buf[pos + 1] - '0';
174 elem->type = CT_SIMPLE;
175 pos += 3;
176 }
177 else if (buf[pos + 1] == '8')
178 {
179 if (mutt_str_startswith(buf + pos, "38;5;") && mutt_isdigit(buf[pos + 5]))
180 {
181 // 38;5;n palette foreground
182 char *end = NULL;
183 unsigned long value = strtoul(buf + pos + 5, &end, 10);
184 if ((value < 256) && end && ansi_is_end_char(end[0]))
185 {
186 elem->color = value;
187 elem->type = CT_PALETTE;
188 pos += end - &buf[pos];
189 }
190 else
191 {
192 return 0;
193 }
194 }
195 else if (mutt_str_startswith(buf + pos, "38;2;") && mutt_isdigit(buf[pos + 5]))
196 {
197 // 38;2;R;G;B true colour foreground
198 long r = -1;
199 long g = -1;
200 long b = -1;
201 char *end = NULL;
202 unsigned long value = 0;
203 pos += 5; // Skip 38;2;
204
205 value = strtoul(buf + pos, &end, 10);
206 if ((value > 255) || !end || (end[0] != ';'))
207 {
208 return 0;
209 }
210 r = value;
211 pos += end - &buf[pos] + 1;
212
213 value = strtoul(buf + pos, &end, 10);
214 if ((value > 255) || !end || (end[0] != ';'))
215 {
216 return 0;
217 }
218 g = value;
219 pos += end - &buf[pos] + 1;
220
221 value = strtoul(buf + pos, &end, 10);
222 if ((value > 255) || !end || (end[0] != 'm'))
223 {
224 return 0;
225 }
226 b = value;
227 pos += end - &buf[pos] + 1;
228
229 elem->color = (r << 16) + (g << 8) + (b << 0);
230 elem->type = CT_RGB;
231 }
232 else
233 {
234 return pos; // LCOV_EXCL_LINE
235 }
236 }
237 else if ((buf[pos + 1] == '9') && ansi_is_end_char(buf[pos + 2]))
238 {
239 // default foreground
240 elem->color = COLOR_DEFAULT;
241 elem->type = CT_SIMPLE;
242 pos += 2;
243 }
244 else
245 {
246 return 0; // LCOV_EXCL_LINE
247 }
248 }
249 else if ((buf[pos] == '4') && ansi_is_end_char(buf[pos + 1]))
250 {
251 ansi->attrs |= A_UNDERLINE;
252 pos += 2;
253 }
254 else if (buf[pos] == '4')
255 {
256 struct ColorElement *elem = &ansi->bg;
257
258 // 40-47 basic background
259 if ((buf[pos + 1] >= '0') && (buf[pos + 1] < '8') && ansi_is_end_char(buf[pos + 2]))
260 {
261 elem->color = buf[pos + 1] - '0';
262 elem->type = CT_SIMPLE;
263 pos += 3;
264 }
265 else if (buf[pos + 1] == '8')
266 {
267 if (mutt_str_startswith(buf + pos, "48;5;") && mutt_isdigit(buf[pos + 5]))
268 {
269 // 48;5;n palette background
270 char *end = NULL;
271 unsigned long value = strtoul(buf + pos + 5, &end, 10);
272 if ((value < 256) && end && ansi_is_end_char(end[0]))
273 {
274 elem->color = value;
275 elem->type = CT_PALETTE;
276 pos += end - &buf[pos];
277 }
278 else
279 {
280 return 0;
281 }
282 }
283 else if (mutt_str_startswith(buf + pos, "48;2;") && mutt_isdigit(buf[pos + 5]))
284 {
285 // 48;2;R;G;B true colour background
286 long r = -1;
287 long g = -1;
288 long b = -1;
289 char *end = NULL;
290 unsigned long value = 0;
291 pos += 5; // Skip 48;2;
292
293 value = strtoul(buf + pos, &end, 10);
294 if ((value > 255) || !end || (end[0] != ';'))
295 {
296 return 0;
297 }
298 r = value;
299 pos += end - &buf[pos] + 1;
300
301 value = strtoul(buf + pos, &end, 10);
302 if ((value > 255) || !end || (end[0] != ';'))
303 {
304 return 0;
305 }
306 g = value;
307 pos += end - &buf[pos] + 1;
308
309 value = strtoul(buf + pos, &end, 10);
310 if ((value > 255) || !end || (end[0] != 'm'))
311 {
312 return 0;
313 }
314 b = value;
315 pos += end - &buf[pos] + 1;
316
317 elem->color = (r << 16) + (g << 8) + (b << 0);
318 elem->type = CT_RGB;
319 }
320 else
321 {
322 return pos; // LCOV_EXCL_LINE
323 }
324 }
325 else if ((buf[pos + 1] == '9') && ansi_is_end_char(buf[pos + 2]))
326 {
327 // default background
328 elem->color = COLOR_DEFAULT;
329 elem->type = CT_SIMPLE;
330 pos += 2;
331 }
332 else
333 {
334 return 0; // LCOV_EXCL_LINE
335 }
336 }
337 else if ((buf[pos] == '5') && ansi_is_end_char(buf[pos + 1]))
338 {
339 ansi->attrs |= A_BLINK;
340 pos += 2;
341 }
342 else if ((buf[pos] == '7') && ansi_is_end_char(buf[pos + 1]))
343 {
344 ansi->attrs |= A_REVERSE;
345 pos += 2;
346 }
347 else if (buf[pos] == ';')
348 {
349 pos++; // LCOV_EXCL_LINE
350 }
351 else
352 {
353 while ((pos < seq_len) && (buf[pos] != ';'))
354 pos++;
355 }
356 }
357
358 return pos;
359}
ANSI Colours.
Colour and attributes.
@ CT_SIMPLE
Simple colour, e.g. "Red".
Definition: attr.h:36
@ CT_PALETTE
Palette colour, e.g. "color207".
Definition: attr.h:37
@ CT_RGB
True colour, e.g. "#11AAFF".
Definition: attr.h:38
Color and attribute parsing.
#define COLOR_DEFAULT
Definition: color.h:103
int digit(const char *s)
bool mutt_isdigit(int arg)
Wrapper for isdigit(3)
Definition: ctype.c:65
Convenience wrapper for the gui headers.
Convenience wrapper for the library headers.
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:231
#define A_ITALIC
Definition: mutt_curses.h:49
int ansi_color_parse_single(const char *buf, struct AnsiColor *ansi, bool dry_run)
Parse a single ANSI escape sequence.
Definition: parse_ansi.c:108
void ansi_color_reset(struct AnsiColor *ansi)
Reset an AnsiColor to uncoloured.
Definition: parse_ansi.c:44
static bool ansi_is_end_char(char c)
Is this the end of a sequence?
Definition: parse_ansi.c:64
int ansi_color_seq_length(const char *str)
Is this an ANSI escape sequence?
Definition: parse_ansi.c:78
Parse ANSI Sequences.
An ANSI escape sequence.
Definition: ansi.h:35
int attrs
Text attributes, e.g. A_BOLD.
Definition: ansi.h:38
const struct AttrColor * attr_color
Curses colour of text.
Definition: ansi.h:39
struct ColorElement bg
Background colour.
Definition: ansi.h:37
struct ColorElement fg
Foreground colour.
Definition: ansi.h:36
One element of a Colour.
Definition: attr.h:56
enum ColorType type
Type of Colour.
Definition: attr.h:58
color_t color
Colour.
Definition: attr.h:57