source: libcaca/trunk/caca/codec/text.c @ 3925

Last change on this file since 3925 was 3492, checked in by Sam Hocevar, 11 years ago

Split import.c and export.c to create a new text.c codec file in order
to keep source files under 1,000 lines of code.

  • Property svn:keywords set to Id
File size: 27.9 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2002-2009 Sam Hocevar <sam@hocevar.net>
4 *                2006 Jean-Yves Lamoureux <jylam@lnxscene.org>
5 *                All Rights Reserved
6 *
7 *  $Id: text.c 3492 2009-05-21 20:54:59Z sam $
8 *
9 *  This library is free software. It comes without any warranty, to
10 *  the extent permitted by applicable law. You can redistribute it
11 *  and/or modify it under the terms of the Do What The Fuck You Want
12 *  To Public License, Version 2, as published by Sam Hocevar. See
13 *  http://sam.zoy.org/wtfpl/COPYING for more details.
14 */
15
16/*
17 *  This file contains text import and export functions
18 */
19
20#include "config.h"
21
22#if !defined(__KERNEL__)
23#   include <stdlib.h>
24#   include <stdio.h>
25#   include <string.h>
26#endif
27
28#include "caca.h"
29#include "caca_internals.h"
30#include "codec.h"
31
32struct import
33{
34    uint32_t clearattr;
35
36    /* ANSI Graphic Rendition Combination Mode */
37    uint8_t fg, bg;   /* ANSI-context fg/bg */
38    uint8_t dfg, dbg; /* Default fg/bg */
39    uint8_t bold, blink, italics, negative, concealed, underline;
40    uint8_t faint, strike, proportional; /* unsupported */
41};
42
43static void ansi_parse_grcm(caca_canvas_t *, struct import *,
44                            unsigned int, unsigned int const *);
45
46ssize_t _import_text(caca_canvas_t *cv, void const *data, size_t size)
47{
48    char const *text = (char const *)data;
49    unsigned int width = 0, height = 0, x = 0, y = 0, i;
50
51    caca_set_canvas_size(cv, width, height);
52
53    for(i = 0; i < size; i++)
54    {
55        unsigned char ch = *text++;
56
57        if(ch == '\r')
58            continue;
59
60        if(ch == '\n')
61        {
62            x = 0;
63            y++;
64            continue;
65        }
66
67        if(x >= width || y >= height)
68        {
69            if(x >= width)
70                width = x + 1;
71
72            if(y >= height)
73                height = y + 1;
74
75            caca_set_canvas_size(cv, width, height);
76        }
77
78        caca_put_char(cv, x, y, ch);
79        x++;
80    }
81
82    if(y > height)
83        caca_set_canvas_size(cv, width, height = y);
84
85    return (ssize_t)size;
86}
87
88ssize_t _import_ansi(caca_canvas_t *cv, void const *data, size_t size, int utf8)
89{
90    struct import im;
91    unsigned char const *buffer = (unsigned char const*)data;
92    unsigned int i, j, skip, growx = 0, growy = 0, dummy = 0;
93    unsigned int width, height;
94    uint32_t savedattr;
95    int x = 0, y = 0, save_x = 0, save_y = 0;
96
97    if(utf8)
98    {
99        width = cv->width;
100        height = cv->height;
101        growx = !width;
102        growy = !height;
103        x = cv->frames[cv->frame].x;
104        y = cv->frames[cv->frame].y;
105    }
106    else
107    {
108        caca_set_canvas_size(cv, width = 80, height = 0);
109        growx = 0;
110        growy = 1;
111    }
112
113    if(utf8)
114    {
115        im.dfg = CACA_DEFAULT;
116        im.dbg = CACA_TRANSPARENT;
117    }
118    else
119    {
120        im.dfg = CACA_LIGHTGRAY;
121        im.dbg = CACA_BLACK;
122    }
123
124    caca_set_color_ansi(cv, im.dfg, im.dbg);
125    im.clearattr = caca_get_attr(cv, -1, -1);
126
127    ansi_parse_grcm(cv, &im, 1, &dummy);
128
129    for(i = 0; i < size; i += skip)
130    {
131        uint32_t ch = 0;
132        int wch = 0;
133
134        skip = 1;
135
136        if(!utf8 && buffer[i] == '\x1a' && i + 7 < size
137           && !memcmp(buffer + i + 1, "SAUCE00", 7))
138            break; /* End before SAUCE data */
139
140        else if(buffer[i] == '\r')
141        {
142            x = 0;
143        }
144
145        else if(buffer[i] == '\n')
146        {
147            x = 0;
148            y++;
149        }
150
151        else if(buffer[i] == '\t')
152        {
153            x = (x + 7) & ~7;
154        }
155
156        else if(buffer[i] == '\x08')
157        {
158            if(x > 0)
159                x--;
160        }
161
162        /* If there are not enough characters to parse the escape sequence,
163         * wait until the next try. We require 3. */
164        else if(buffer[i] == '\033' && i + 2 >= size)
165            break;
166
167        /* XXX: What the fuck is this shit? */
168        else if(buffer[i] == '\033' && buffer[i + 1] == '('
169                 && buffer[i + 2] == 'B')
170        {
171            skip += 2;
172        }
173
174        /* Interpret escape commands, as per Standard ECMA-48 "Control
175         * Functions for Coded Character Sets", 5.4. Control sequences. */
176        else if(buffer[i] == '\033' && buffer[i + 1] == '[')
177        {
178            unsigned int argc = 0, argv[101];
179            unsigned int param, inter, final;
180
181        /* Compute offsets to parameter bytes, intermediate bytes and
182         * to the final byte. Only the final byte is mandatory, there
183         * can be zero of the others.
184         * 0  param=2             inter                 final           final+1
185         * +-----+------------------+---------------------+-----------------+
186         * | CSI | parameter bytes  | intermediate bytes  |   final byte    |
187         * |     |   0x30 - 0x3f    |    0x20 - 0x2f      |   0x40 - 0x7e   |
188         * | ^[[ | 0123456789:;<=>? | SPC !"#$%&'()*+,-./ | azAZ@[\]^_`{|}~ |
189         * +-----+------------------+---------------------+-----------------+
190         */
191            param = 2;
192
193            for(inter = param; i + inter < size; inter++)
194                if(buffer[i + inter] < 0x30 || buffer[i + inter] > 0x3f)
195                    break;
196
197            for(final = inter; i + final < size; final++)
198                if(buffer[i + final] < 0x20 || buffer[i + final] > 0x2f)
199                    break;
200
201            if(i + final >= size
202                || buffer[i + final] < 0x40 || buffer[i + final] > 0x7e)
203                break; /* Invalid Final Byte */
204
205            skip += final;
206
207            /* Sanity checks */
208            if(param < inter && buffer[i + param] >= 0x3c)
209            {
210                /* Private sequence, only parse what we know */
211                debug("ansi import: private sequence \"^[[%.*s\"",
212                      final - param + 1, buffer + i + param);
213                continue; /* Private sequence, skip it entirely */
214            }
215
216            if(final - param > 100)
217                continue; /* Suspiciously long sequence, skip it */
218
219            /* Parse parameter bytes as per ECMA-48 5.4.2: Parameter string
220             * format */
221            if(param < inter)
222            {
223                argv[0] = 0;
224                for(j = param; j < inter; j++)
225                {
226                    if(buffer[i + j] == ';')
227                        argv[++argc] = 0;
228                    else if(buffer[i + j] >= '0' && buffer[i + j] <= '9')
229                        argv[argc] = 10 * argv[argc] + (buffer[i + j] - '0');
230                }
231                argc++;
232            }
233
234            /* Interpret final byte. The code representations are given in
235             * ECMA-48 5.4: Control sequences, and the code definitions are
236             * given in ECMA-48 8.3: Definition of control functions. */
237            switch(buffer[i + final])
238            {
239            case 'H': /* CUP (0x48) - Cursor Position */
240                x = (argc > 1 && argv[1] > 0) ? argv[1] - 1 : 0;
241                y = (argc > 0 && argv[0] > 0) ? argv[0] - 1 : 0;
242                break;
243            case 'A': /* CUU (0x41) - Cursor Up */
244                y -= argc ? argv[0] : 1;
245                if(y < 0)
246                    y = 0;
247                break;
248            case 'B': /* CUD (0x42) - Cursor Down */
249                y += argc ? argv[0] : 1;
250                break;
251            case 'C': /* CUF (0x43) - Cursor Right */
252                x += argc ? argv[0] : 1;
253                break;
254            case 'D': /* CUB (0x44) - Cursor Left */
255                x -= argc ? argv[0] : 1;
256                if(x < 0)
257                    x = 0;
258                break;
259            case 'G': /* CHA (0x47) - Cursor Character Absolute */
260                x = (argc && argv[0] > 0) ? argv[0] - 1 : 0;
261                break;
262            case 'J': /* ED (0x4a) - Erase In Page */
263                savedattr = caca_get_attr(cv, -1, -1);
264                caca_set_attr(cv, im.clearattr);
265                if(!argc || argv[0] == 0)
266                {
267                    caca_draw_line(cv, x, y, width, y, ' ');
268                    caca_fill_box(cv, 0, y + 1, width - 1, height - 1, ' ');
269                }
270                else if(argv[0] == 1)
271                {
272                    caca_fill_box(cv, 0, 0, width - 1, y - 1, ' ');
273                    caca_draw_line(cv, 0, y, x, y, ' ');
274                }
275                else if(argv[0] == 2)
276                    //x = y = 0;
277                    caca_fill_box(cv, 0, 0, width - 1, height - 1, ' ');
278                caca_set_attr(cv, savedattr);
279                break;
280            case 'K': /* EL (0x4b) - Erase In Line */
281                if(!argc || argv[0] == 0)
282                    caca_draw_line(cv, x, y, width, y, ' ');
283                else if(argv[0] == 1)
284                    caca_draw_line(cv, 0, y, x, y, ' ');
285                else if(argv[0] == 2)
286                    if((unsigned int)x < width)
287                        caca_draw_line(cv, x, y, width - 1, y, ' ');
288                //x = width;
289                break;
290            case 'P': /* DCH (0x50) - Delete Character */
291                if(!argc || argv[0] == 0)
292                    argv[0] = 1; /* echo -ne 'foobar\r\e[0P\n' */
293                for(j = 0; (unsigned int)(j + argv[0]) < width; j++)
294                {
295                    caca_put_char(cv, j, y,
296                                   caca_get_char(cv, j + argv[0], y));
297                    caca_put_attr(cv, j, y,
298                                   caca_get_attr(cv, j + argv[0], y));
299                }
300#if 0
301                savedattr = caca_get_attr(cv, -1, -1);
302                caca_set_attr(cv, im.clearattr);
303                for( ; (unsigned int)j < width; j++)
304                    caca_put_char(cv, j, y, ' ');
305                caca_set_attr(cv, savedattr);
306#endif
307            case 'X': /* ECH (0x58) - Erase Character */
308                if(argc && argv[0])
309                {
310                    savedattr = caca_get_attr(cv, -1, -1);
311                    caca_set_attr(cv, im.clearattr);
312                    caca_draw_line(cv, x, y, x + argv[0] - 1, y, ' ');
313                    caca_set_attr(cv, savedattr);
314                }
315            case 'd': /* VPA (0x64) - Line Position Absolute */
316                y = (argc && argv[0] > 0) ? argv[0] - 1 : 0;
317                break;
318            case 'f': /* HVP (0x66) - Character And Line Position */
319                x = (argc > 1 && argv[1] > 0) ? argv[1] - 1 : 0;
320                y = (argc > 0 && argv[0] > 0) ? argv[0] - 1 : 0;
321                break;
322            case 'h': /* SM (0x68) - FIXME */
323                debug("ansi import: set mode %i", argc ? (int)argv[0] : -1);
324                break;
325            case 'l': /* RM (0x6c) - FIXME */
326                debug("ansi import: reset mode %i", argc ? (int)argv[0] : -1);
327                break;
328            case 'm': /* SGR (0x6d) - Select Graphic Rendition */
329                if(argc)
330                    ansi_parse_grcm(cv, &im, argc, argv);
331                else
332                    ansi_parse_grcm(cv, &im, 1, &dummy);
333                break;
334            case 's': /* Private (save cursor position) */
335                save_x = x;
336                save_y = y;
337                break;
338            case 'u': /* Private (reload cursor position) */
339                x = save_x;
340                y = save_y;
341                break;
342            default:
343                debug("ansi import: unknown command \"^[[%.*s\"",
344                      final - param + 1, buffer + i + param);
345                break;
346            }
347        }
348
349        /* Parse OSC stuff. */
350        else if(buffer[i] == '\033' && buffer[i + 1] == ']')
351        {
352            char *string;
353            unsigned int command = 0;
354            unsigned int mode = 2, semicolon, final;
355
356            for(semicolon = mode; i + semicolon < size; semicolon++)
357            {
358                if(buffer[i + semicolon] < '0' || buffer[i + semicolon] > '9')
359                    break;
360                command = 10 * command + (buffer[i + semicolon] - '0');
361            }
362
363            if(i + semicolon >= size || buffer[i + semicolon] != ';')
364                break; /* Invalid Mode */
365
366            for(final = semicolon + 1; i + final < size; final++)
367                if(buffer[i + final] < 0x20)
368                    break;
369
370            if(i + final >= size || buffer[i + final] != '\a')
371                break; /* Not enough data or no bell found */
372                /* FIXME: XTerm also reacts to <ESC><backslash> and <ST> */
373                /* FIXME: differenciate between not enough data (try again)
374                 *        and invalid data (print shit) */
375
376            skip += final;
377
378            string = malloc(final - (semicolon + 1) + 1);
379            memcpy(string, buffer + (semicolon + 1), final - (semicolon + 1));
380            string[final - (semicolon + 1)] = '\0';
381            debug("ansi import: got OSC command %i string '%s'", command,
382                  string);
383            free(string);
384        }
385
386        /* Form feed means a new frame */
387        else if(buffer[i] == '\f' && buffer[i + 1] == '\n')
388        {
389            int f = caca_get_frame_count(cv);
390            caca_create_frame(cv, f);
391            caca_set_frame(cv, f);
392            x = y = 0;
393            skip++;
394        }
395
396        /* Get the character we’re going to paste */
397        else if(utf8)
398        {
399            size_t bytes;
400
401            if(i + 6 < size)
402                ch = caca_utf8_to_utf32((char const *)(buffer + i), &bytes);
403            else
404            {
405                /* Add a trailing zero to what we're going to read */
406                char tmp[7];
407                memcpy(tmp, buffer + i, size - i);
408                tmp[size - i] = '\0';
409                ch = caca_utf8_to_utf32(tmp, &bytes);
410            }
411
412            if(!bytes)
413            {
414                /* If the Unicode is invalid, assume it was latin1. */
415                ch = buffer[i];
416                bytes = 1;
417            }
418            wch = caca_utf32_is_fullwidth(ch) ? 2 : 1;
419            skip += (int)(bytes - 1);
420        }
421        else
422        {
423            ch = caca_cp437_to_utf32(buffer[i]);
424            wch = 1;
425        }
426
427        /* Wrap long lines or grow horizontally */
428        while((unsigned int)x + wch > width)
429        {
430            if(growx)
431            {
432                savedattr = caca_get_attr(cv, -1, -1);
433                caca_set_attr(cv, im.clearattr);
434                caca_set_canvas_size(cv, width = x + wch, height);
435                caca_set_attr(cv, savedattr);
436            }
437            else
438            {
439                x -= width;
440                y++;
441            }
442        }
443
444        /* Scroll or grow vertically */
445        if((unsigned int)y >= height)
446        {
447            savedattr = caca_get_attr(cv, -1, -1);
448            caca_set_attr(cv, im.clearattr);
449            if(growy)
450            {
451                caca_set_canvas_size(cv, width, height = y + 1);
452            }
453            else
454            {
455                int lines = (y - height) + 1;
456
457                for(j = 0; j + lines < height; j++)
458                {
459                    memcpy(cv->attrs + j * cv->width,
460                           cv->attrs + (j + lines) * cv->width, cv->width * 4);
461                    memcpy(cv->chars + j * cv->width,
462                           cv->chars + (j + lines) * cv->width, cv->width * 4);
463                }
464                caca_fill_box(cv, 0, height - lines,
465                                   cv->width - 1, height - 1, ' ');
466                y -= lines;
467            }
468            caca_set_attr(cv, savedattr);
469        }
470
471        /* Now paste our character, if any */
472        if(wch)
473        {
474            caca_put_char(cv, x, y, ch);
475            x += wch;
476        }
477    }
478
479    if(growy && (unsigned int)y > height)
480    {
481        savedattr = caca_get_attr(cv, -1, -1);
482        caca_set_attr(cv, im.clearattr);
483        caca_set_canvas_size(cv, width, height = y);
484        caca_set_attr(cv, savedattr);
485    }
486
487    cv->frames[cv->frame].x = x;
488    cv->frames[cv->frame].y = y;
489
490//    if(utf8)
491//        caca_set_attr(cv, savedattr);
492
493    return i;
494}
495
496/* Generate UTF-8 representation of current canvas. */
497void *_export_utf8(caca_canvas_t const *cv, size_t *bytes, int cr)
498{
499    static uint8_t const palette[] =
500    {
501        0,  4,  2,  6, 1,  5,  3,  7,
502        8, 12, 10, 14, 9, 13, 11, 15
503    };
504
505    char *data, *cur;
506    int x, y;
507
508    /* 23 bytes assumed for max length per pixel ('\e[5;1;3x;4y;9x;10ym' plus
509     * 4 max bytes for a UTF-8 character).
510     * Add height*9 to that (zeroes color at the end and jump to next line) */
511    *bytes = (cv->height * 9) + (cv->width * cv->height * 23);
512    cur = data = malloc(*bytes);
513
514    for(y = 0; y < cv->height; y++)
515    {
516        uint32_t *lineattr = cv->attrs + y * cv->width;
517        uint32_t *linechar = cv->chars + y * cv->width;
518
519        uint8_t prevfg = 0x10;
520        uint8_t prevbg = 0x10;
521
522        for(x = 0; x < cv->width; x++)
523        {
524            uint32_t attr = lineattr[x];
525            uint32_t ch = linechar[x];
526            uint8_t ansifg, ansibg, fg, bg;
527
528            if(ch == CACA_MAGIC_FULLWIDTH)
529                continue;
530
531            ansifg = caca_attr_to_ansi_fg(attr);
532            ansibg = caca_attr_to_ansi_bg(attr);
533
534            fg = ansifg < 0x10 ? palette[ansifg] : 0x10;
535            bg = ansibg < 0x10 ? palette[ansibg] : 0x10;
536
537            /* TODO: the [0 could be omitted in some cases */
538            if(fg != prevfg || bg != prevbg)
539            {
540                cur += sprintf(cur, "\033[0");
541
542                if(fg < 8)
543                    cur += sprintf(cur, ";3%d", fg);
544                else if(fg < 16)
545                    cur += sprintf(cur, ";1;3%d;9%d", fg - 8, fg - 8);
546
547                if(bg < 8)
548                    cur += sprintf(cur, ";4%d", bg);
549                else if(bg < 16)
550                    cur += sprintf(cur, ";5;4%d;10%d", bg - 8, bg - 8);
551
552                cur += sprintf(cur, "m");
553            }
554
555            cur += caca_utf32_to_utf8(cur, ch);
556
557            prevfg = fg;
558            prevbg = bg;
559        }
560
561        if(prevfg != 0x10 || prevbg != 0x10)
562            cur += sprintf(cur, "\033[0m");
563
564        cur += sprintf(cur, cr ? "\r\n" : "\n");
565    }
566
567    /* Crop to really used size */
568    debug("utf8 export: alloc %lu bytes, realloc %lu",
569          (unsigned long int)*bytes, (unsigned long int)(cur - data));
570    *bytes = (uintptr_t)(cur - data);
571    data = realloc(data, *bytes);
572
573    return data;
574}
575
576/* Generate ANSI representation of current canvas. */
577void *_export_ansi(caca_canvas_t const *cv, size_t *bytes)
578{
579    static uint8_t const palette[] =
580    {
581        0,  4,  2,  6, 1,  5,  3,  7,
582        8, 12, 10, 14, 9, 13, 11, 15
583    };
584
585    char *data, *cur;
586    int x, y;
587
588    uint8_t prevfg = -1;
589    uint8_t prevbg = -1;
590
591    /* 16 bytes assumed for max length per pixel ('\e[5;1;3x;4ym' plus
592     * 1 byte for a CP437 character).
593     * Add height*9 to that (zeroes color at the end and jump to next line) */
594    *bytes = (cv->height * 9) + (cv->width * cv->height * 16);
595    cur = data = malloc(*bytes);
596
597    for(y = 0; y < cv->height; y++)
598    {
599        uint32_t *lineattr = cv->attrs + y * cv->width;
600        uint32_t *linechar = cv->chars + y * cv->width;
601
602        for(x = 0; x < cv->width; x++)
603        {
604            uint8_t ansifg = caca_attr_to_ansi_fg(lineattr[x]);
605            uint8_t ansibg = caca_attr_to_ansi_bg(lineattr[x]);
606            uint8_t fg = ansifg < 0x10 ? palette[ansifg] : CACA_LIGHTGRAY;
607            uint8_t bg = ansibg < 0x10 ? palette[ansibg] : CACA_BLACK;
608            uint32_t ch = linechar[x];
609
610            if(ch == CACA_MAGIC_FULLWIDTH)
611                ch = '?';
612
613            if(fg != prevfg || bg != prevbg)
614            {
615                cur += sprintf(cur, "\033[0;");
616
617                if(fg < 8)
618                    if(bg < 8)
619                        cur += sprintf(cur, "3%d;4%dm", fg, bg);
620                    else
621                        cur += sprintf(cur, "5;3%d;4%dm", fg, bg - 8);
622                else
623                    if(bg < 8)
624                        cur += sprintf(cur, "1;3%d;4%dm", fg - 8, bg);
625                    else
626                        cur += sprintf(cur, "5;1;3%d;4%dm", fg - 8, bg - 8);
627            }
628
629            *cur++ = caca_utf32_to_cp437(ch);
630
631            prevfg = fg;
632            prevbg = bg;
633        }
634
635        if(cv->width == 80)
636        {
637            cur += sprintf(cur, "\033[s\n\033[u");
638        }
639        else
640        {
641            cur += sprintf(cur, "\033[0m\r\n");
642            prevfg = -1;
643            prevbg = -1;
644        }
645    }
646
647    /* Crop to really used size */
648    debug("ansi export: alloc %lu bytes, realloc %lu",
649          (unsigned long int)*bytes, (unsigned long int)(cur - data));
650    *bytes = (uintptr_t)(cur - data);
651    data = realloc(data, *bytes);
652
653    return data;
654}
655
656/* Export a text file with IRC colours */
657void *_export_irc(caca_canvas_t const *cv, size_t *bytes)
658{
659    static uint8_t const palette[] =
660    {
661        1, 2, 3, 10, 5, 6, 7, 15, /* Dark */
662        14, 12, 9, 11, 4, 13, 8, 0, /* Light */
663    };
664
665    char *data, *cur;
666    int x, y;
667
668    /* 14 bytes assumed for max length per pixel. Worst case scenario:
669     * ^Cxx,yy   6 bytes
670     * ^B^B      2 bytes
671     * ch        6 bytes
672     * 3 bytes for max length per line. Worst case scenario:
673     * <spc>     1 byte (for empty lines)
674     * \r\n      2 bytes
675     * In real life, the average bytes per pixel value will be around 5.
676     */
677
678    *bytes = 2 + cv->height * (3 + cv->width * 14);
679    cur = data = malloc(*bytes);
680
681    for(y = 0; y < cv->height; y++)
682    {
683        uint32_t *lineattr = cv->attrs + y * cv->width;
684        uint32_t *linechar = cv->chars + y * cv->width;
685
686        uint8_t prevfg = 0x10;
687        uint8_t prevbg = 0x10;
688
689        for(x = 0; x < cv->width; x++)
690        {
691            uint32_t attr = lineattr[x];
692            uint32_t ch = linechar[x];
693            uint8_t ansifg, ansibg, fg, bg;
694
695            if(ch == CACA_MAGIC_FULLWIDTH)
696                continue;
697
698            ansifg = caca_attr_to_ansi_fg(attr);
699            ansibg = caca_attr_to_ansi_bg(attr);
700
701            fg = ansifg < 0x10 ? palette[ansifg] : 0x10;
702            bg = ansibg < 0x10 ? palette[ansibg] : 0x10;
703
704            /* TODO: optimise series of same fg / same bg
705             *       don't change fg value if ch == ' '
706             *       make sure the \x03,%d trick works everywhere */
707            if(bg != prevbg || fg != prevfg)
708            {
709                int need_escape = 0;
710
711                if(bg == 0x10)
712                {
713                    if(fg == 0x10)
714                        cur += sprintf(cur, "\x0f");
715                    else
716                    {
717                        if(prevbg == 0x10)
718                            cur += sprintf(cur, "\x03%d", fg);
719                        else
720                            cur += sprintf(cur, "\x0f\x03%d", fg);
721
722                        if(ch == (uint32_t)',')
723                            need_escape = 1;
724                    }
725                }
726                else
727                {
728                    if(fg == 0x10)
729                        cur += sprintf(cur, "\x0f\x03,%d", bg);
730                    else
731                        cur += sprintf(cur, "\x03%d,%d", fg, bg);
732                }
733
734                if(ch >= (uint32_t)'0' && ch <= (uint32_t)'9')
735                    need_escape = 1;
736
737                if(need_escape)
738                    cur += sprintf(cur, "\x02\x02");
739            }
740
741            cur += caca_utf32_to_utf8(cur, ch);
742            prevfg = fg;
743            prevbg = bg;
744        }
745
746        /* TODO: do the same the day we optimise whole lines above */
747        if(!cv->width)
748            *cur++ = ' ';
749
750        *cur++ = '\r';
751        *cur++ = '\n';
752    }
753
754    /* Crop to really used size */
755    debug("IRC export: alloc %lu bytes, realloc %lu",
756          (unsigned long int)*bytes, (unsigned long int)(cur - data));
757    *bytes = (uintptr_t)(cur - data);
758    data = realloc(data, *bytes);
759
760    return data;
761}
762
763/* XXX : ANSI loader helper */
764
765static void ansi_parse_grcm(caca_canvas_t *cv, struct import *im,
766                            unsigned int argc, unsigned int const *argv)
767{
768    static uint8_t const ansi2caca[] =
769    {
770        CACA_BLACK, CACA_RED, CACA_GREEN, CACA_BROWN,
771        CACA_BLUE, CACA_MAGENTA, CACA_CYAN, CACA_LIGHTGRAY
772    };
773
774    unsigned int j;
775    uint8_t efg, ebg; /* Effective (libcaca) fg/bg */
776
777    for(j = 0; j < argc; j++)
778    {
779        /* Defined in ECMA-48 8.3.117: SGR - SELECT GRAPHIC RENDITION */
780        if(argv[j] >= 30 && argv[j] <= 37)
781            im->fg = ansi2caca[argv[j] - 30];
782        else if(argv[j] >= 40 && argv[j] <= 47)
783            im->bg = ansi2caca[argv[j] - 40];
784        else if(argv[j] >= 90 && argv[j] <= 97)
785            im->fg = ansi2caca[argv[j] - 90] + 8;
786        else if(argv[j] >= 100 && argv[j] <= 107)
787            im->bg = ansi2caca[argv[j] - 100] + 8;
788        else switch(argv[j])
789        {
790        case 0: /* default rendition */
791            im->fg = im->dfg;
792            im->bg = im->dbg;
793            im->bold = im->blink = im->italics = im->negative
794             = im->concealed = im->underline = im->faint = im->strike
795             = im->proportional = 0;
796            break;
797        case 1: /* bold or increased intensity */
798            im->bold = 1;
799            break;
800        case 2: /* faint, decreased intensity or second colour */
801            im->faint = 1;
802            break;
803        case 3: /* italicized */
804            im->italics = 1;
805            break;
806        case 4: /* singly underlined */
807            im->underline = 1;
808            break;
809        case 5: /* slowly blinking (less then 150 per minute) */
810        case 6: /* rapidly blinking (150 per minute or more) */
811            im->blink = 1;
812            break;
813        case 7: /* negative image */
814            im->negative = 1;
815            break;
816        case 8: /* concealed characters */
817            im->concealed = 1;
818            break;
819        case 9: /* crossed-out (characters still legible but marked as to be
820                 * deleted */
821            im->strike = 1;
822            break;
823        case 21: /* doubly underlined */
824            im->underline = 1;
825            break;
826        case 22: /* normal colour or normal intensity (neither bold nor
827                  * faint) */
828            im->bold = im->faint = 0;
829            break;
830        case 23: /* not italicized, not fraktur */
831            im->italics = 0;
832            break;
833        case 24: /* not underlined (neither singly nor doubly) */
834            im->underline = 0;
835            break;
836        case 25: /* steady (not blinking) */
837            im->blink = 0;
838            break;
839        case 26: /* (reserved for proportional spacing as specified in CCITT
840                  * Recommendation T.61) */
841            im->proportional = 1;
842            break;
843        case 27: /* positive image */
844            im->negative = 0;
845            break;
846        case 28: /* revealed characters */
847            im->concealed = 0;
848            break;
849        case 29: /* not crossed out */
850            im->strike = 0;
851            break;
852        case 38: /* (reserved for future standardization, intended for setting
853                  * character foreground colour as specified in ISO 8613-6
854                  * [CCITT Recommendation T.416]) */
855            break;
856        case 39: /* default display colour (implementation-defined) */
857            im->fg = im->dfg;
858            break;
859        case 48: /* (reserved for future standardization, intended for setting
860                  * character background colour as specified in ISO 8613-6
861                  * [CCITT Recommendation T.416]) */
862            break;
863        case 49: /* default background colour (implementation-defined) */
864            im->bg = im->dbg;
865            break;
866        case 50: /* (reserved for cancelling the effect of the rendering
867                  * aspect established by parameter value 26) */
868            im->proportional = 0;
869            break;
870        default:
871            debug("ansi import: unknown sgr %i", argv[j]);
872            break;
873        }
874    }
875
876    if(im->concealed)
877    {
878        efg = ebg = CACA_TRANSPARENT;
879    }
880    else
881    {
882        efg = im->negative ? im->bg : im->fg;
883        ebg = im->negative ? im->fg : im->bg;
884
885        if(im->bold)
886        {
887            if(efg < 8)
888                efg += 8;
889            else if(efg == CACA_DEFAULT)
890                efg = CACA_WHITE;
891        }
892    }
893
894    caca_set_color_ansi(cv, efg, ebg);
895}
896
Note: See TracBrowser for help on using the repository browser.