source: libcaca/trunk/cucul/import.c @ 2299

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