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

Last change on this file since 913 was 913, checked in by Sam Hocevar, 15 years ago
  • Minor code reorganisation and documenting.
  • Property svn:keywords set to Id
File size: 13.3 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 913 2006-04-26 14:36:11Z sam $
7 *
8 *  This library is free software; you can redistribute it and/or
9 *  modify it under the terms of the Do What The Fuck You Want To
10 *  Public License, Version 2, as published by Sam Hocevar. See
11 *  http://sam.zoy.org/wtfpl/COPYING for more details.
12 */
13
14/*
15 *  This file contains various import functions.
16 */
17
18#include "config.h"
19#include "common.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 cucul_canvas_t *import_caca(void const *, unsigned int);
31static cucul_canvas_t *import_text(void const *, unsigned int);
32static cucul_canvas_t *import_ansi(void const *, unsigned int);
33
34/** \brief Import a buffer into a canvas
35 *
36 *  This function imports a libcucul buffer (cucul_load_memory()/cucul_load_file())
37 *  into an internal libcucul canvas.
38 *
39 *  Valid values for \c format are:
40 *
41 *  \li \c "": attempt to autodetect the file format.
42 *
43 *  \li \c "ansi": import ANSI files.
44 *
45 *  \li \c "caca": import native libcaca files.
46 *
47 *  \param buffer A \e libcucul buffer containing the data to be loaded
48 *         into a canvas.
49 *  \param format A string describing the input format.
50 *  \return A libcucul canvas, or NULL in case of error.
51 */
52cucul_canvas_t * cucul_import_canvas(cucul_buffer_t *buffer, char const *format)
53{
54    char const *buf = (char const*)buffer->data;
55
56    if(buffer->size == 0 || buffer->data == NULL)
57        return NULL;
58
59    if(!strcasecmp("caca", format))
60        return import_caca(buffer->data, buffer->size);
61    if(!strcasecmp("text", format))
62        return import_text(buffer->data, buffer->size);
63    if(!strcasecmp("ansi", format))
64        return import_ansi(buffer->data, buffer->size);
65
66    /* Autodetection */
67    if(!strcasecmp("", format))
68    {
69        unsigned int i=0;
70        /* if 4 first letters are CACA */
71        if(buffer->size >= 4 &&
72            buf[0] == 'C' && buf[1] == 'A' && buf[2] == 'C' && buf[3] != 'A')
73            return import_caca(buffer->data, buffer->size);
74
75        /* If we find ESC[ argv, we guess it's an ANSI file */
76        while(i < buffer->size - 1)
77        {
78            if((buf[i] == 0x1b) && (buf[i+1] == '['))
79                return import_ansi(buffer->data, buffer->size);
80            i++;
81        }
82
83        /* Otherwise, import it as text */
84        return import_text(buffer->data, buffer->size);
85    }
86    return NULL;
87}
88
89/** \brief Get available import formats
90 *
91 *  Return a list of available import formats. The list is a NULL-terminated
92 *  array of strings, interleaving a string containing the internal value for
93 *  the import format, to be used with cucul_import_canvas(), and a string
94 *  containing the natural language description for that import format.
95 *
96 *  \return An array of strings.
97 */
98char const * const * cucul_get_import_list(void)
99{
100    static char const * const list[] =
101    {
102        "", "autodetect",
103        "text", "plain text",
104        "caca", "native libcaca format",
105        "ansi", "ANSI coloured text",
106        NULL, NULL
107    };
108
109    return list;
110}
111
112/*
113 * XXX: the following functions are local.
114 */
115
116static cucul_canvas_t *import_caca(void const *data, unsigned int size)
117{
118    cucul_canvas_t *cv;
119    uint8_t const *buf = (uint8_t const *)data;
120    unsigned int width, height, n;
121
122    if(size < 16)
123        return NULL;
124
125    if(buf[0] != 'C' || buf[1] != 'A' || buf[2] != 'C' || buf[3] != 'A')
126        return NULL;
127
128    if(buf[4] != 'C' || buf[5] != 'A' || buf[6] != 'N' || buf[7] != 'V')
129        return NULL;
130
131    width = ((uint32_t)buf[8] << 24) | ((uint32_t)buf[9] << 16)
132           | ((uint32_t)buf[10] << 8) | (uint32_t)buf[11];
133    height = ((uint32_t)buf[12] << 24) | ((uint32_t)buf[13] << 16)
134            | ((uint32_t)buf[14] << 8) | (uint32_t)buf[15];
135
136    if(!width || !height)
137        return NULL;
138
139    if(size != 16 + width * height * 8)
140        return NULL;
141
142    cv = cucul_create_canvas(width, height);
143
144    if(!cv)
145        return NULL;
146
147    for(n = height * width; n--; )
148    {
149        cv->chars[n] = ((uint32_t)buf[16 + 0 + 8 * n] << 24)
150                     | ((uint32_t)buf[16 + 1 + 8 * n] << 16)
151                     | ((uint32_t)buf[16 + 2 + 8 * n] << 8)
152                     | (uint32_t)buf[16 + 3 + 8 * n];
153        cv->attr[n] = ((uint32_t)buf[16 + 4 + 8 * n] << 24)
154                    | ((uint32_t)buf[16 + 5 + 8 * n] << 16)
155                    | ((uint32_t)buf[16 + 6 + 8 * n] << 8)
156                    | (uint32_t)buf[16 + 7 + 8 * n];
157    }
158
159    return cv;
160}
161
162static cucul_canvas_t *import_text(void const *data, unsigned int size)
163{
164    cucul_canvas_t *cv;
165    char const *text = (char const *)data;
166    unsigned int width = 1, height = 1, x = 0, y = 0, i;
167
168    cv = cucul_create_canvas(width, height);
169    cucul_set_color(cv, CUCUL_COLOR_DEFAULT, CUCUL_COLOR_TRANSPARENT);
170
171    for(i = 0; i < size; i++)
172    {
173        unsigned char ch = *text++;
174
175        if(ch == '\r')
176            continue;
177
178        if(ch == '\n')
179        {
180            x = 0;
181            y++;
182            continue;
183        }
184
185        if(x >= width || y >= height)
186        {
187            if(x >= width)
188                width = x + 1;
189
190            if(y >= height)
191                height = y + 1;
192
193            cucul_set_canvas_size(cv, width, height);
194        }
195
196        cucul_putchar(cv, x, y, ch);
197        x++;
198    }
199
200    return cv;
201}
202
203/* Graphic Rendition Combination Mode */
204struct grcm
205{
206    uint8_t fg, bg;
207    uint8_t bold, negative, concealed;
208};
209
210static void manage_modifiers(struct grcm *, int);
211
212static cucul_canvas_t *import_ansi(void const *data, unsigned int size)
213{
214    struct grcm grcm;
215    unsigned char const *buffer = (unsigned char const*)data;
216    cucul_canvas_t *cv;
217    unsigned int i, j, skip;
218    unsigned int width = 80, height = 25;
219    int x = 0, y = 0, save_x = 0, save_y = 0;
220
221    manage_modifiers(&grcm, 0);
222
223    cv = cucul_create_canvas(width, height);
224    cucul_set_color(cv, CUCUL_COLOR_DEFAULT, CUCUL_COLOR_DEFAULT);
225
226    for(i = 0; i < size; i += skip)
227    {
228        skip = 1;
229
230        if(buffer[i] == '\x1a' && size - i >= 8
231           && !memcmp(buffer + i + 1, "SAUCE00", 7))
232            break; /* End before SAUCE data */
233
234        if(buffer[i] == '\r')
235            continue; /* DOS sucks */
236
237        if(buffer[i] == '\n')
238        {
239            x = 0;
240            y++;
241            continue;
242        }
243
244        /* Interpret escape commands, as per Standard ECMA-48 "Control
245         * Functions for Coded Character Sets", 5.4. Control sequences. */
246        if(buffer[i] == '\x1b' && buffer[i + 1] == '[')
247        {
248            unsigned int argc = 0, argv[101];
249            unsigned int param, inter, final;
250
251        /* Compute offsets to parameter bytes, intermediate bytes and
252         * to the final byte. Only the final byte is mandatory, there
253         * can be zero of the others.
254         * 0  param=2             inter                 final           final+1
255         * +-----+------------------+---------------------+-----------------+
256         * | CSI | parameter bytes  | intermediate bytes  |   final byte    |
257         * |     |   0x30 - 0x3f    |    0x20 - 0x2f      |   0x40 - 0x7e   |
258         * | ^[[ | 0123456789:;<=>? | SPC !"#$%&'()*+,-./ | azAZ@[\]^_`{|}~ |
259         * +-----+------------------+---------------------+-----------------+
260         */
261            param = 2;
262
263            for(inter = param; i + inter < size; inter++)
264                if(buffer[i + inter] < 0x30 || buffer[i + inter] > 0x3f)
265                    break;
266
267            for(final = inter; i + final < size; final++)
268                if(buffer[i + final] < 0x20 || buffer[i + final] > 0x2f)
269                    break;
270
271            if(buffer[i + final] < 0x40 || buffer[i + final] > 0x7e)
272                break; /* Invalid Final Byte */
273
274            skip += final;
275
276            /* Sanity checks */
277            if(param < inter && buffer[i + param] >= 0x3c)
278            {
279                //fprintf(stderr, "private sequence \"^[[%.*s\"\n",
280                //                final - param + 1, buffer + i + param);
281                continue; /* Private sequence, skip it entirely */
282            }
283
284            if(final - param > 100)
285                continue; /* Suspiciously long sequence, skip it */
286
287            /* Parse parameter bytes as per ECMA-48 5.4.2: Parameter string
288             * format */
289            if(param < inter)
290            {
291                argv[0] = 0;
292                for(j = param; j < inter; j++)
293                {
294                    if(buffer[i + j] == ';')
295                        argv[++argc] = 0;
296                    else if(buffer[i + j] >= '0' && buffer[i + j] <= '9')
297                        argv[argc] = 10 * argv[argc] + (buffer[i + j] - '0');
298                }
299                argc++;
300            }
301
302            /* Interpret final byte. The code representations are given in
303             * ECMA-48 5.4: Control sequences, and the code definitions are
304             * given in ECMA-48 8.3: Definition of control functions. */
305            switch(buffer[i + final])
306            {
307            case 'f': /* CUP - Cursor Position */
308            case 'H': /* HVP - Character And Line Position */
309                x = (argc > 1) ? argv[1] - 1 : 0;
310                y = (argc > 0) ? argv[0] - 1 : 0;
311                break;
312            case 'A': /* CUU - Cursor Up */
313                y -= argc ? argv[0] : 1;
314                if(y < 0)
315                    y = 0;
316                break;
317            case 'B': /* CUD - Cursor Down */
318                y += argc ? argv[0] : 1;
319                break;
320            case 'C': /* CUF - Cursor Right */
321                x += argc ? argv[0] : 1;
322                break;
323            case 'D': /* CUB - Cursor Left */
324                x -= argc ? argv[0] : 1;
325                if(x < 0)
326                    x = 0;
327                break;
328            case 's': /* Private (save cursor position) */
329                save_x = x;
330                save_y = y;
331                break;
332            case 'u': /* Private (reload cursor positin) */
333                x = save_x;
334                y = save_y;
335                break;
336            case 'J': /* ED - Erase In Page */
337                if(argv[0] == 2)
338                    x = y = 0;
339                break;
340            case 'K': /* EL - Erase In Line */
341                for(j = x; j < width; j++)
342                    _cucul_putchar32(cv, j, y, (uint32_t)' ');
343                x = width;
344                break;
345            case 'm': /* SGR - Select Graphic Rendition */
346                for(j = 0; j < argc; j++)
347                    manage_modifiers(&grcm, argv[j]);
348                if(grcm.concealed)
349                    cucul_set_color(cv, CUCUL_COLOR_TRANSPARENT,
350                                        CUCUL_COLOR_TRANSPARENT);
351                else if(grcm.negative)
352                    cucul_set_color(cv, (grcm.bold && grcm.bg < 8) ?
353                                        grcm.bg + 8 : grcm.bg, grcm.fg);
354                else
355                    cucul_set_color(cv, (grcm.bold && grcm.fg < 8) ?
356                                        grcm.fg + 8 : grcm.fg, grcm.bg);
357                break;
358            default:
359                break;
360            }
361
362            continue;
363        }
364
365        /* We're going to paste a character. First make sure the canvas
366         * is big enough. */
367        if((unsigned int)x >= width)
368        {
369            x = 0;
370            y++;
371        }
372
373        if((unsigned int)y >= height)
374        {
375            height = y + 1;
376            cucul_set_canvas_size(cv, width, height);
377        }
378
379        /* Now paste our character */
380        _cucul_putchar32(cv, x, y,_cucul_cp437_to_utf32(buffer[i]));
381        x++;
382    }
383
384    return cv;
385}
386
387/* XXX : ANSI loader helper */
388
389static void manage_modifiers(struct grcm *g, int i)
390{
391    static uint8_t const ansi2cucul[] =
392    {
393        CUCUL_COLOR_BLACK, CUCUL_COLOR_RED,
394        CUCUL_COLOR_GREEN, CUCUL_COLOR_BROWN,
395        CUCUL_COLOR_BLUE, CUCUL_COLOR_MAGENTA,
396        CUCUL_COLOR_CYAN, CUCUL_COLOR_LIGHTGRAY
397    };
398
399    /* Defined in ECMA-48 8.3.117: SGR - SELECT GRAPHIC RENDITION */
400    if(i >= 30 && i <= 37)
401        g->fg = ansi2cucul[i - 30];
402    else if(i >= 40 && i <= 47)
403        g->bg = ansi2cucul[i - 40];
404    else if(i >= 90 && i <= 97)
405        g->fg = ansi2cucul[i - 90] + 8;
406    else if(i >= 100 && i <= 107)
407        g->bg = ansi2cucul[i - 100] + 8;
408    else switch(i)
409    {
410    case 0: /* default rendition */
411        g->fg = CUCUL_COLOR_DEFAULT;
412        g->bg = CUCUL_COLOR_DEFAULT;
413        g->bold = g->negative = g->concealed = 0;
414        break;
415    case 1: /* bold or increased intensity */
416        g->bold = 1;
417        break;
418    case 4: /* singly underlined */
419        break;
420    case 5: /* slowly blinking (less then 150 per minute) */
421        break;
422    case 7: /* negative image */
423        g->negative = 1;
424        break;
425    case 8: /* concealed characters */
426        g->concealed = 1;
427        break;
428    case 22: /* normal colour or normal intensity (neither bold nor faint) */
429        g->bold = 0;
430        break;
431    case 28: /* revealed characters */
432        g->concealed = 0;
433        break;
434    case 39: /* default display colour (implementation-defined) */
435        g->fg = CUCUL_COLOR_DEFAULT;
436        break;
437    case 49: /* default background colour (implementation-defined) */
438        g->bg = CUCUL_COLOR_DEFAULT;
439        break;
440    default:
441        //fprintf(stderr, "unknown sgr %i\n", i);
442        break;
443    }
444}
445
Note: See TracBrowser for help on using the repository browser.