source: libcaca/trunk/caca/string.c @ 4840

Last change on this file since 4840 was 4840, checked in by Sam Hocevar, 9 years ago

string: fix a potential memory corruption with caca_printf() calls done
outside the canvas.

  • Property svn:keywords set to Id
File size: 18.8 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2002-2012 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  This library is free software. It comes without any warranty, to
7 *  the extent permitted by applicable law. You can redistribute it
8 *  and/or modify it under the terms of the Do What The Fuck You Want
9 *  To Public License, Version 2, as published by Sam Hocevar. See
10 *  http://sam.zoy.org/wtfpl/COPYING for more details.
11 */
12
13/*
14 *  This file contains various canvas handling functions such as character
15 *  and string drawing.
16 */
17
18#include "config.h"
19
20#if !defined(__KERNEL__)
21#   include <stdio.h> /* BUFSIZ */
22#   include <string.h>
23#   include <stdlib.h>
24#   include <stdarg.h>
25#   if defined(HAVE_UNISTD_H)
26#       include <unistd.h>
27#   endif
28#   if defined(HAVE_SIGNAL_H)
29#       include <signal.h>
30#   endif
31#   if defined(HAVE_SYS_IOCTL_H)
32#       include <sys/ioctl.h>
33#   endif
34#endif
35
36#include "caca.h"
37#include "caca_internals.h"
38
39#if defined _WIN32 && defined __GNUC__ && __GNUC__ >= 3
40int vsnprintf_s(char *s, size_t n, size_t c,
41                const char *fmt, va_list ap) CACA_WEAK;
42int vsnprintf(char *s, size_t n, const char *fmt, va_list ap) CACA_WEAK;
43#endif
44
45/** \brief Set cursor position.
46 *
47 *  Put the cursor at the given coordinates. Functions making use of the
48 *  cursor will use the new values. Setting the cursor position outside the
49 *  canvas is legal but the cursor will not be shown.
50 *
51 *  This function never fails.
52 *
53 *  \param cv A handle to the libcaca canvas.
54 *  \param x X cursor coordinate.
55 *  \param y Y cursor coordinate.
56 *  \return This function always returns 0.
57 */
58int caca_gotoxy(caca_canvas_t *cv, int x, int y)
59{
60    cv->frames[cv->frame].x = x;
61    cv->frames[cv->frame].y = y;
62
63    return 0;
64}
65
66/** \brief Get X cursor position.
67 *
68 *  Retrieve the X coordinate of the cursor's position.
69 *
70 *  This function never fails.
71 *
72 *  \param cv A handle to the libcaca canvas.
73 *  \return The cursor's X coordinate.
74 */
75int caca_wherex(caca_canvas_t const *cv)
76{
77    return cv->frames[cv->frame].x;
78}
79
80/** \brief Get Y cursor position.
81 *
82 *  Retrieve the Y coordinate of the cursor's position.
83 *
84 *  This function never fails.
85 *
86 *  \param cv A handle to the libcaca canvas.
87 *  \return The cursor's Y coordinate.
88 */
89int caca_wherey(caca_canvas_t const *cv)
90{
91    return cv->frames[cv->frame].y;
92}
93
94/** \brief Print an ASCII or Unicode character.
95 *
96 *  Print an ASCII or Unicode character at the given coordinates, using
97 *  the default foreground and background colour values.
98 *
99 *  If the coordinates are outside the canvas boundaries, nothing is printed.
100 *  If a fullwidth Unicode character gets overwritten, its remaining visible
101 *  parts are replaced with spaces. If the canvas' boundaries would split the
102 *  fullwidth character in two, a space is printed instead.
103 *
104 *  The behaviour when printing non-printable characters or invalid UTF-32
105 *  characters is undefined. To print a sequence of bytes forming an UTF-8
106 *  character instead of an UTF-32 character, use the caca_put_str() function.
107 *
108 *  This function returns the width of the printed character. If it is a
109 *  fullwidth character, 2 is returned. Otherwise, 1 is returned.
110 *
111 *  This function never fails.
112 *
113 *  \param cv A handle to the libcaca canvas.
114 *  \param x X coordinate.
115 *  \param y Y coordinate.
116 *  \param ch The character to print.
117 *  \return The width of the printed character: 2 for a fullwidth character,
118 *          1 otherwise.
119 */
120int caca_put_char(caca_canvas_t *cv, int x, int y, uint32_t ch)
121{
122    uint32_t *curchar, *curattr, attr;
123    int fullwidth, xmin, xmax, ret;
124
125    if(ch == CACA_MAGIC_FULLWIDTH)
126        return 1;
127
128    fullwidth = caca_utf32_is_fullwidth(ch);
129    ret = fullwidth ? 2 : 1;
130
131    if(x >= (int)cv->width || y < 0 || y >= (int)cv->height)
132        return ret;
133
134    if(x == -1 && fullwidth)
135    {
136        x = 0;
137        ch = ' ';
138        fullwidth = 0;
139    }
140    else if(x < 0)
141        return ret;
142
143    curchar = cv->chars + x + y * cv->width;
144    curattr = cv->attrs + x + y * cv->width;
145    attr = cv->curattr;
146
147    xmin = xmax = x;
148
149    /* When overwriting the right part of a fullwidth character,
150     * replace its left part with a space. */
151    if(x && curchar[0] == CACA_MAGIC_FULLWIDTH)
152    {
153        curchar[-1] = ' ';
154        xmin--;
155    }
156
157    if(fullwidth)
158    {
159        if(x + 1 == (int)cv->width)
160            ch = ' ';
161        else
162        {
163            xmax++;
164
165            /* When overwriting the left part of a fullwidth character,
166             * replace its right part with a space. */
167            if(x + 2 < (int)cv->width && curchar[2] == CACA_MAGIC_FULLWIDTH)
168            {
169                curchar[2] = ' ';
170                xmax++;
171            }
172
173            curchar[1] = CACA_MAGIC_FULLWIDTH;
174            curattr[1] = attr;
175        }
176    }
177    else
178    {
179        /* When overwriting the left part of a fullwidth character,
180         * replace its right part with a space. */
181        if(x + 1 != (int)cv->width && curchar[1] == CACA_MAGIC_FULLWIDTH)
182        {
183            curchar[1] = ' ';
184            xmax++;
185        }
186    }
187
188    /* Only add a dirty rectangle if we are pasting a different character
189     * or attribute at that place. This does not account for inconsistencies
190     * in the canvas, ie. if CACA_MAGIC_FULLWIDTH lies at illegal places,
191     * but it's the caller's responsibility not to corrupt the contents. */
192    if(!cv->dirty_disabled
193        && (curchar[0] != ch || curattr[0] != attr))
194        caca_add_dirty_rect(cv, xmin, y, xmax - xmin + 1, 1);
195
196    curchar[0] = ch;
197    curattr[0] = attr;
198
199    return ret;
200}
201
202/** \brief Get the Unicode character at the given coordinates.
203 *
204 *  Get the ASCII or Unicode value of the character at the given
205 *  coordinates. If the value is less or equal to 127 (0x7f),
206 *  the character can be printed as ASCII. Otherise, it must be handled
207 *  as a UTF-32 value.
208 *
209 *  If the coordinates are outside the canvas boundaries, a space (0x20)
210 *  is returned.
211 *
212 *  A special exception is when CACA_MAGIC_FULLWIDTH is returned. This
213 *  value is guaranteed not to be a valid Unicode character, and indicates
214 *  that the character at the left of the requested one is a fullwidth
215 *  character.
216 *
217 *  This function never fails.
218 *
219 *  \param cv A handle to the libcaca canvas.
220 *  \param x X coordinate.
221 *  \param y Y coordinate.
222 *  \return The Unicode character at the given coordinates.
223 */
224uint32_t caca_get_char(caca_canvas_t const *cv, int x, int y)
225{
226    if(x < 0 || x >= (int)cv->width || y < 0 || y >= (int)cv->height)
227        return ' ';
228
229    return cv->chars[x + y * cv->width];
230}
231
232/** \brief Print a string.
233 *
234 *  Print an UTF-8 string at the given coordinates, using the default
235 *  foreground and background values. The coordinates may be outside the
236 *  canvas boundaries (eg. a negative Y coordinate) and the string will
237 *  be cropped accordingly if it is too long.
238 *
239 *  See caca_put_char() for more information on how fullwidth characters
240 *  are handled when overwriting each other or at the canvas' boundaries.
241 *
242 *  This function returns the number of cells printed by the string. It is
243 *  not the number of characters printed, because fullwidth characters
244 *  account for two cells.
245 *
246 *  This function never fails.
247 *
248 *  \param cv A handle to the libcaca canvas.
249 *  \param x X coordinate.
250 *  \param y Y coordinate.
251 *  \param s The string to print.
252 *  \return The number of cells printed.
253 */
254int caca_put_str(caca_canvas_t *cv, int x, int y, char const *s)
255{
256    size_t rd;
257    int len = 0;
258
259    if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
260    {
261        while(*s)
262        {
263            len += caca_utf32_is_fullwidth(caca_utf8_to_utf32(s, &rd)) ? 2 : 1;
264            s += rd;
265        }
266        return len;
267    }
268
269    while(*s)
270    {
271        uint32_t ch = caca_utf8_to_utf32(s, &rd);
272
273        if(x + len >= -1 && x + len < (int)cv->width)
274            caca_put_char(cv, x + len, y, ch);
275
276        len += caca_utf32_is_fullwidth(ch) ? 2 : 1;
277        s += rd;
278    }
279
280    return len;
281}
282
283/** \brief Print a formated string.
284 *
285 *  Format a string at the given coordinates, using the default foreground
286 *  and background values. The coordinates may be outside the canvas
287 *  boundaries (eg. a negative Y coordinate) and the string will be cropped
288 *  accordingly if it is too long. The syntax of the format string is the
289 *  same as for the C printf() function.
290 *
291 *  This function returns the number of cells printed by the string. It is
292 *  not the number of characters printed, because fullwidth characters
293 *  account for two cells.
294 *
295 *  This function never fails.
296 *
297 *  \param cv A handle to the libcaca canvas.
298 *  \param x X coordinate.
299 *  \param y Y coordinate.
300 *  \param format The format string to print.
301 *  \param ... Arguments to the format string.
302 *  \return The number of cells printed.
303 */
304int caca_printf(caca_canvas_t *cv, int x, int y, char const *format, ...)
305{
306    va_list args;
307    int ret;
308    va_start(args, format);
309    ret = caca_vprintf(cv, x, y, format, args);
310    va_end(args);
311    return ret;
312}
313
314/** \brief Print a formated string (va_list version).
315 *
316 *  Format a string at the given coordinates, using the default foreground
317 *  and background values. The coordinates may be outside the canvas
318 *  boundaries (eg. a negative X coordinate) and the string will be cropped
319 *  accordingly if it is too long. The syntax of the format string is the
320 *  same as for the C vprintf() function.
321 *
322 *  This function returns the number of cells printed by the string. It is
323 *  not the number of characters printed, because fullwidth characters
324 *  account for two cells.
325 *
326 *  This function never fails.
327 *
328 *  \param cv A handle to the libcaca canvas.
329 *  \param x X coordinate.
330 *  \param y Y coordinate.
331 *  \param format The format string to print.
332 *  \param args A va_list containting the arguments to the format string.
333 *  \return The number of cells printed.
334 */
335int caca_vprintf(caca_canvas_t *cv, int x, int y, char const *format,
336                 va_list args)
337{
338    char tmp[BUFSIZ];
339    char *buf = tmp;
340    int bufsize = BUFSIZ, ret;
341
342    if(cv->width - x + 1 > BUFSIZ)
343    {
344        bufsize = cv->width - x + 1;
345        buf = malloc(bufsize);
346    }
347
348#if defined(HAVE_VSNPRINTF_S)
349    vsnprintf_s(buf, bufsize, _TRUNCATE, format, args);
350#elif defined(HAVE_VSNPRINTF)
351    vsnprintf(buf, bufsize, format, args);
352#else
353    vsprintf(buf, format, args);
354#endif
355    buf[bufsize - 1] = '\0';
356
357    ret = caca_put_str(cv, x, y, buf);
358
359    if(buf != tmp)
360        free(buf);
361
362    return ret;
363}
364
365/** \brief Clear the canvas.
366 *
367 *  Clear the canvas using the current foreground and background colours.
368 *
369 *  This function never fails.
370 *
371 *  \param cv The canvas to clear.
372 *  \return This function always returns 0.
373 */
374int caca_clear_canvas(caca_canvas_t *cv)
375{
376    uint32_t attr = cv->curattr;
377    int n;
378
379    for(n = cv->width * cv->height; n--; )
380    {
381        cv->chars[n] = (uint32_t)' ';
382        cv->attrs[n] = attr;
383    }
384
385    if(!cv->dirty_disabled)
386        caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
387
388    return 0;
389}
390
391/** \brief Set cursor handle.
392 *
393 *  Set the canvas' handle. Blitting functions will use the handle value
394 *  to put the canvas at the proper coordinates.
395 *
396 *  This function never fails.
397 *
398 *  \param cv A handle to the libcaca canvas.
399 *  \param x X handle coordinate.
400 *  \param y Y handle coordinate.
401 *  \return This function always returns 0.
402 */
403int caca_set_canvas_handle(caca_canvas_t *cv, int x, int y)
404{
405    cv->frames[cv->frame].handlex = x;
406    cv->frames[cv->frame].handley = y;
407
408    return 0;
409}
410
411/** \brief Get X handle position.
412 *
413 *  Retrieve the X coordinate of the canvas' handle.
414 *
415 *  This function never fails.
416 *
417 *  \param cv A handle to the libcaca canvas.
418 *  \return The canvas' handle's X coordinate.
419 */
420int caca_get_canvas_handle_x(caca_canvas_t const *cv)
421{
422    return cv->frames[cv->frame].handlex;
423}
424
425/** \brief Get Y handle position.
426 *
427 *  Retrieve the Y coordinate of the canvas' handle.
428 *
429 *  This function never fails.
430 *
431 *  \param cv A handle to the libcaca canvas.
432 *  \return The canvas' handle's Y coordinate.
433 */
434int caca_get_canvas_handle_y(caca_canvas_t const *cv)
435{
436    return cv->frames[cv->frame].handley;
437}
438
439/** \brief Blit a canvas onto another one.
440 *
441 *  Blit a canvas onto another one at the given coordinates.
442 *  An optional mask canvas can be used.
443 *
444 *  If an error occurs, -1 is returned and \b errno is set accordingly:
445 *  - \c EINVAL A mask was specified but the mask size and source canvas
446 *    size do not match.
447 *
448 *  \param dst The destination canvas.
449 *  \param x X coordinate.
450 *  \param y Y coordinate.
451 *  \param src The source canvas.
452 *  \param mask The mask canvas.
453 *  \return 0 in case of success, -1 if an error occurred.
454 */
455int caca_blit(caca_canvas_t *dst, int x, int y,
456              caca_canvas_t const *src, caca_canvas_t const *mask)
457{
458    int i, j, starti, startj, endi, endj, stride, bleed_left, bleed_right;
459
460    if(mask && (src->width != mask->width || src->height != mask->height))
461    {
462        seterrno(EINVAL);
463        return -1;
464    }
465
466    x -= src->frames[src->frame].handlex;
467    y -= src->frames[src->frame].handley;
468
469    starti = x < 0 ? -x : 0;
470    startj = y < 0 ? -y : 0;
471    endi = (x + src->width >= dst->width) ? dst->width - x : src->width;
472    endj = (y + src->height >= dst->height) ? dst->height - y : src->height;
473    stride = endi - starti;
474
475    if(starti > src->width || startj > src->height
476        || starti >= endi || startj >= endj)
477        return 0;
478
479    bleed_left = bleed_right = 0;
480
481    for(j = startj; j < endj; j++)
482    {
483        int dstix = (j + y) * dst->width + starti + x;
484        int srcix = j * src->width + starti;
485
486        /* FIXME: we are ignoring the mask here */
487        if((starti + x) && dst->chars[dstix] == CACA_MAGIC_FULLWIDTH)
488        {
489            dst->chars[dstix - 1] = ' ';
490            bleed_left = 1;
491        }
492
493        if(endi + x < dst->width
494                && dst->chars[dstix + stride] == CACA_MAGIC_FULLWIDTH)
495        {
496            dst->chars[dstix + stride] = ' ';
497            bleed_right = 1;
498        }
499
500        if(mask)
501        {
502            for(i = 0; i < stride; i++)
503            {
504                if(mask->chars[srcix + i] == (uint32_t)' ')
505                    continue;
506
507                if(dst->chars[dstix + i] != src->chars[srcix + i] ||
508                   dst->attrs[dstix + i] != src->attrs[srcix + i])
509                {
510                    dst->chars[dstix + i] = src->chars[srcix + i];
511                    dst->attrs[dstix + i] = src->attrs[srcix + i];
512                    if(!dst->dirty_disabled)
513                        caca_add_dirty_rect(dst, x + starti + i, y + j, 1, 1);
514                }
515            }
516        }
517        else
518        {
519            if(memcmp(dst->chars + dstix, src->chars + srcix, stride * 4) ||
520               memcmp(dst->attrs + dstix, src->attrs + srcix, stride * 4))
521            {
522                /* FIXME be more precise ? */
523                memcpy(dst->chars + dstix, src->chars + srcix, stride * 4);
524                memcpy(dst->attrs + dstix, src->attrs + srcix, stride * 4);
525                if(!dst->dirty_disabled)
526                    caca_add_dirty_rect(dst, x + starti, y + j, stride, 1);
527            }
528        }
529
530        /* Fix split fullwidth chars */
531        if(src->chars[srcix] == CACA_MAGIC_FULLWIDTH)
532            dst->chars[dstix] = ' ';
533
534        if(endi < src->width && src->chars[endi] == CACA_MAGIC_FULLWIDTH)
535            dst->chars[dstix + stride - 1] = ' ';
536    }
537
538
539    return 0;
540}
541
542/** \brief Set a canvas' new boundaries.
543 *
544 *  Set new boundaries for a canvas. This function can be used to crop a
545 *  canvas, to expand it or for combinations of both actions. All frames
546 *  are affected by this function.
547 *
548 *  If an error occurs, -1 is returned and \b errno is set accordingly:
549 *  - \c EINVAL Specified width or height is invalid.
550 *  - \c EBUSY The canvas is in use by a display driver and cannot be resized.
551 *  - \c ENOMEM Not enough memory for the requested canvas size. If this
552 *    happens, the canvas handle becomes invalid and should not be used.
553 *
554 *  \param cv The canvas to crop.
555 *  \param x X coordinate of the top-left corner.
556 *  \param y Y coordinate of the top-left corner.
557 *  \param w The width of the cropped area.
558 *  \param h The height of the cropped area.
559 *  \return 0 in case of success, -1 if an error occurred.
560 */
561int caca_set_canvas_boundaries(caca_canvas_t *cv, int x, int y, int w, int h)
562{
563    caca_canvas_t *new;
564    int f, saved_f, framecount;
565
566    if(cv->refcount)
567    {
568        seterrno(EBUSY);
569        return -1;
570    }
571
572    if(w < 0 || h < 0)
573    {
574        seterrno(EINVAL);
575        return -1;
576    }
577
578    new = caca_create_canvas(w, h);
579
580    framecount = caca_get_frame_count(cv);
581    saved_f = cv->frame;
582
583    for(f = 0; f < framecount; f++)
584    {
585        if(f)
586            caca_create_frame(new, framecount);
587
588        caca_set_frame(cv, f);
589        caca_set_frame(new, f);
590        caca_blit(new, -x, -y, cv, NULL);
591        free(cv->frames[f].chars);
592        free(cv->frames[f].attrs);
593    }
594    free(cv->frames);
595
596    cv->frames = new->frames;
597    free(new);
598
599    caca_set_frame(cv, saved_f);
600    _caca_load_frame_info(cv);
601
602    /* FIXME: this may be optimised somewhat */
603    if(!cv->dirty_disabled)
604        caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
605
606    return 0;
607}
608
609/*
610 * Functions for the mingw32 runtime
611 */
612
613#if defined _WIN32 && defined __GNUC__ && __GNUC__ >= 3
614int vsnprintf_s(char *s, size_t n, size_t c, const char *fmt, va_list ap)
615{
616    return vsnprintf(s, n, fmt, ap);
617}
618
619int vsnprintf(char *s, size_t n, const char *fmt, va_list ap)
620{
621    return 0;
622}
623#endif
624
625/*
626 * XXX: The following functions are aliases.
627 */
628
629int cucul_gotoxy(cucul_canvas_t *, int, int) CACA_ALIAS(caca_gotoxy);
630int cucul_get_cursor_x(cucul_canvas_t const *) CACA_ALIAS(caca_wherex);
631int cucul_get_cursor_y(cucul_canvas_t const *) CACA_ALIAS(caca_wherey);
632int caca_get_cursor_x(caca_canvas_t const *) CACA_ALIAS(caca_wherex);
633int caca_get_cursor_y(caca_canvas_t const *) CACA_ALIAS(caca_wherey);
634int cucul_put_char(cucul_canvas_t *, int, int, uint32_t)
635         CACA_ALIAS(caca_put_char);
636uint32_t cucul_get_char(cucul_canvas_t const *, int, int)
637         CACA_ALIAS(caca_get_char);
638int cucul_put_str(cucul_canvas_t *, int, int, char const *)
639         CACA_ALIAS(caca_put_str);
640int cucul_printf(cucul_canvas_t *, int, int, char const *, ...)
641         CACA_ALIAS(caca_printf);
642int cucul_clear_canvas(cucul_canvas_t *) CACA_ALIAS(caca_clear_canvas);
643int cucul_set_canvas_handle(cucul_canvas_t *, int, int)
644         CACA_ALIAS(caca_set_canvas_handle);
645int cucul_get_canvas_handle_x(cucul_canvas_t const *)
646         CACA_ALIAS(caca_get_canvas_handle_x);
647int cucul_get_canvas_handle_y(cucul_canvas_t const *)
648         CACA_ALIAS(caca_get_canvas_handle_y);
649int cucul_blit(cucul_canvas_t *, int, int, cucul_canvas_t const *,
650                        cucul_canvas_t const *) CACA_ALIAS(caca_blit);
651int cucul_set_canvas_boundaries(cucul_canvas_t *, int, int, int, int)
652         CACA_ALIAS(caca_set_canvas_boundaries);
653
Note: See TracBrowser for help on using the repository browser.