NeoMutt  2024-11-14-34-g5aaf0d
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 <ctype.h>
31#include <stdbool.h>
32#include <stddef.h>
33#include <stdlib.h>
34#include "mutt/lib.h"
35#include "gui/lib.h"
36#include "parse_ansi.h"
37#include "ansi.h"
38#include "attr.h"
39#include "color.h"
40
45void ansi_color_reset(struct AnsiColor *ansi)
46{
47 if (!ansi)
48 return;
49
50 ansi->fg.color = COLOR_DEFAULT;
51 ansi->fg.type = CT_SIMPLE;
52
53 ansi->bg.color = COLOR_DEFAULT;
54 ansi->bg.type = CT_SIMPLE;
55
56 ansi->attrs = A_NORMAL;
57 ansi->attr_color = NULL;
58}
59
65static inline bool ansi_is_end_char(char c)
66{
67 return ((c == 'm') || (c == ';'));
68}
69
79int ansi_color_seq_length(const char *str)
80{
81 if (!str || !*str)
82 return 0;
83
84 if ((str[0] != '\033') || (str[1] != '[')) // Escape
85 return 0;
86
87 int i = 2;
88 while ((str[i] != '\0') && (isdigit(str[i]) || (str[i] == ';')))
89 {
90 i++;
91 }
92
93 if (str[i] == 'm')
94 return i + 1;
95
96 return 0;
97}
98
109int ansi_color_parse_single(const char *buf, struct AnsiColor *ansi, bool dry_run)
110{
111 int seq_len = ansi_color_seq_length(buf);
112 if (seq_len == 0)
113 return 0;
114
115 if (dry_run || !ansi)
116 return seq_len;
117
118 int pos = 2; // Skip '<esc>['
119
120 while (pos < seq_len)
121 {
122 if ((buf[pos] == '0') && isdigit(buf[pos + 1]))
123 {
124 pos++; // Skip the leading zero
125 }
126 else if ((buf[pos] == '0') && ansi_is_end_char(buf[pos + 1]))
127 {
128 ansi_color_reset(ansi);
129 pos += 2;
130 }
131 else if ((buf[pos] == '1') && ansi_is_end_char(buf[pos + 1]))
132 {
133 ansi->attrs |= A_BOLD;
134 pos += 2;
135 }
136 else if ((buf[pos] == '2') && isdigit(buf[pos + 1]) && 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;") && 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;") && 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;") && 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;") && 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:100
int digit(const char *s)
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:230
#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:109
void ansi_color_reset(struct AnsiColor *ansi)
Reset an AnsiColor to uncoloured.
Definition: parse_ansi.c:45
static bool ansi_is_end_char(char c)
Is this the end of a sequence?
Definition: parse_ansi.c:65
int ansi_color_seq_length(const char *str)
Is this an ANSI escape sequence?
Definition: parse_ansi.c:79
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