You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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