NeoMutt  2024-04-25-1-g3de005
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
parse_ansi.c File Reference

Parse ANSI Sequences. 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 "parse_ansi.h"
#include "ansi.h"
#include "attr.h"
#include "color.h"
+ Include dependency graph for parse_ansi.c:

Go to the source code of this file.

Functions

void ansi_color_reset (struct AnsiColor *ansi)
 Reset an AnsiColor to uncoloured.
 
static bool ansi_is_end_char (char c)
 Is this the end of a sequence?
 
int ansi_color_seq_length (const char *str)
 Is this an ANSI escape sequence?
 
int ansi_color_parse_single (const char *buf, struct AnsiColor *ansi, bool dry_run)
 Parse a single ANSI escape sequence.
 

Detailed Description

Parse ANSI Sequences.

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 parse_ansi.c.

Function Documentation

◆ ansi_color_reset()

void ansi_color_reset ( struct AnsiColor ansi)

Reset an AnsiColor to uncoloured.

Parameters
ansiAnsiColor to reset

Definition at line 45 of file parse_ansi.c.

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}
@ CT_SIMPLE
Simple colour, e.g. "Red".
Definition: attr.h:36
#define COLOR_DEFAULT
Definition: color.h:100
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
enum ColorType type
Type of Colour.
Definition: attr.h:58
color_t color
Colour.
Definition: attr.h:57
+ Here is the caller graph for this function:

◆ 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 65 of file parse_ansi.c.

66{
67 return ((c == 'm') || (c == ';'));
68}
+ 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 79 of file parse_ansi.c.

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}
+ Here is the caller graph for this function:

◆ ansi_color_parse_single()

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

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 109 of file parse_ansi.c.

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}
@ CT_PALETTE
Palette colour, e.g. "color207".
Definition: attr.h:37
@ CT_RGB
True colour, e.g. "#11AAFF".
Definition: attr.h:38
int digit(const char *s)
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
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
One element of a Colour.
Definition: attr.h:56
+ Here is the call graph for this function:
+ Here is the caller graph for this function: