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