source: libcaca/trunk/caca/import.c @ 2821

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

Starting refactoring to get rid of libcucul. The initial reason for the
split is rendered moot by the plugin system: when enabled, binaries do
not link directly with libX11 or libGL. I hope this is a step towards
more consisteny and clarity.

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