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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913
  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$
  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 "cucul.h"
  23. #include "cucul_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(cucul_canvas_t *, void const *, size_t);
  46. static ssize_t import_text(cucul_canvas_t *, void const *, size_t);
  47. static ssize_t import_ansi(cucul_canvas_t *, void const *, size_t, int);
  48. static void ansi_parse_grcm(cucul_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 libcucul 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 libcucul 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 cucul_import_memory(cucul_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 libcucul 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. * cucul_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 libcucul 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 cucul_import_file(cucul_canvas_t *cv, char const *filename,
  137. char const *format)
  138. {
  139. #if defined __KERNEL__
  140. seterrno(ENOSYS);
  141. return -1;
  142. #else
  143. cucul_file_t *f;
  144. char *data = NULL;
  145. ssize_t size = 0;
  146. int ret;
  147. f = cucul_file_open(filename, "rb");
  148. if(!f)
  149. return -1; /* fopen already set errno */
  150. while(!cucul_file_eof(f))
  151. {
  152. data = realloc(data, size + 1024);
  153. if(!data)
  154. {
  155. cucul_file_close(f);
  156. seterrno(ENOMEM);
  157. return -1;
  158. }
  159. ret = cucul_file_read(f, data + size, 1024);
  160. if(ret >= 0)
  161. size += ret;
  162. }
  163. cucul_file_close(f);
  164. ret = cucul_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 cucul_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 * cucul_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(cucul_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. cucul_set_canvas_size(cv, 0, 0);
  253. cucul_set_canvas_size(cv, xmax - xmin, ymax - ymin);
  254. for (f = cucul_get_frame_count(cv); f--; )
  255. {
  256. cucul_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. cucul_create_frame(cv, f);
  264. cucul_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. cucul_put_char(cv, x, y, sscanu32(buf + 4 + control_size
  276. + offset + 8 * n));
  277. cucul_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. cucul_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(cucul_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. cucul_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. cucul_set_canvas_size(cv, width, height);
  315. }
  316. cucul_put_char(cv, x, y, ch);
  317. x++;
  318. }
  319. if(y > height)
  320. cucul_set_canvas_size(cv, width, height = y);
  321. return size;
  322. }
  323. static ssize_t import_ansi(cucul_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. cucul_set_canvas_size(cv, width = 80, height = 0);
  344. growx = 0;
  345. growy = 1;
  346. }
  347. if(utf8)
  348. {
  349. im.dfg = CUCUL_DEFAULT;
  350. im.dbg = CUCUL_TRANSPARENT;
  351. }
  352. else
  353. {
  354. im.dfg = CUCUL_LIGHTGRAY;
  355. im.dbg = CUCUL_BLACK;
  356. }
  357. cucul_set_color_ansi(cv, im.dfg, im.dbg);
  358. im.clearattr = cucul_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 = cucul_get_attr(cv, -1, -1);
  477. cucul_set_attr(cv, im.clearattr);
  478. if(!argc || argv[0] == 0)
  479. {
  480. cucul_draw_line(cv, x, y, width, y, ' ');
  481. cucul_fill_box(cv, 0, y + 1, width - 1, height - 1, ' ');
  482. }
  483. else if(argv[0] == 1)
  484. {
  485. cucul_fill_box(cv, 0, 0, width - 1, y - 1, ' ');
  486. cucul_draw_line(cv, 0, y, x, y, ' ');
  487. }
  488. else if(argv[0] == 2)
  489. //x = y = 0;
  490. cucul_fill_box(cv, 0, 0, width - 1, height - 1, ' ');
  491. cucul_set_attr(cv, savedattr);
  492. break;
  493. case 'K': /* EL (0x4b) - Erase In Line */
  494. if(!argc || argv[0] == 0)
  495. cucul_draw_line(cv, x, y, width, y, ' ');
  496. else if(argv[0] == 1)
  497. cucul_draw_line(cv, 0, y, x, y, ' ');
  498. else if(argv[0] == 2)
  499. if((unsigned int)x < width)
  500. cucul_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. cucul_put_char(cv, j, y,
  509. cucul_get_char(cv, j + argv[0], y));
  510. cucul_put_attr(cv, j, y,
  511. cucul_get_attr(cv, j + argv[0], y));
  512. }
  513. #if 0
  514. savedattr = cucul_get_attr(cv, -1, -1);
  515. cucul_set_attr(cv, im.clearattr);
  516. for( ; (unsigned int)j < width; j++)
  517. cucul_put_char(cv, j, y, ' ');
  518. cucul_set_attr(cv, savedattr);
  519. #endif
  520. case 'X': /* ECH (0x58) - Erase Character */
  521. if(argc && argv[0])
  522. {
  523. savedattr = cucul_get_attr(cv, -1, -1);
  524. cucul_set_attr(cv, im.clearattr);
  525. cucul_draw_line(cv, x, y, x + argv[0] - 1, y, ' ');
  526. cucul_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 = cucul_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 = cucul_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 = cucul_utf32_is_fullwidth(ch) ? 2 : 1;
  612. skip += bytes - 1;
  613. }
  614. else
  615. {
  616. ch = cucul_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 = cucul_get_attr(cv, -1, -1);
  625. cucul_set_attr(cv, im.clearattr);
  626. cucul_set_canvas_size(cv, width = x + wch, height);
  627. cucul_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 = cucul_get_attr(cv, -1, -1);
  639. cucul_set_attr(cv, im.clearattr);
  640. if(growy)
  641. {
  642. cucul_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. cucul_fill_box(cv, 0, height - lines,
  655. cv->width - 1, height - 1, ' ');
  656. y -= lines;
  657. }
  658. cucul_set_attr(cv, savedattr);
  659. }
  660. /* Now paste our character, if any */
  661. if(wch)
  662. {
  663. cucul_put_char(cv, x, y, ch);
  664. x += wch;
  665. }
  666. }
  667. if(growy && (unsigned int)y > height)
  668. {
  669. savedattr = cucul_get_attr(cv, -1, -1);
  670. cucul_set_attr(cv, im.clearattr);
  671. cucul_set_canvas_size(cv, width, height = y);
  672. cucul_set_attr(cv, savedattr);
  673. }
  674. cv->frames[cv->frame].x = x;
  675. cv->frames[cv->frame].y = y;
  676. // if(utf8)
  677. // cucul_set_attr(cv, savedattr);
  678. return i;
  679. }
  680. /* XXX : ANSI loader helper */
  681. static void ansi_parse_grcm(cucul_canvas_t *cv, struct import *im,
  682. unsigned int argc, unsigned int const *argv)
  683. {
  684. static uint8_t const ansi2cucul[] =
  685. {
  686. CUCUL_BLACK, CUCUL_RED, CUCUL_GREEN, CUCUL_BROWN,
  687. CUCUL_BLUE, CUCUL_MAGENTA, CUCUL_CYAN, CUCUL_LIGHTGRAY
  688. };
  689. unsigned int j;
  690. uint8_t efg, ebg; /* Effective (libcucul) 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 = ansi2cucul[argv[j] - 30];
  696. else if(argv[j] >= 40 && argv[j] <= 47)
  697. im->bg = ansi2cucul[argv[j] - 40];
  698. else if(argv[j] >= 90 && argv[j] <= 97)
  699. im->fg = ansi2cucul[argv[j] - 90] + 8;
  700. else if(argv[j] >= 100 && argv[j] <= 107)
  701. im->bg = ansi2cucul[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 = CUCUL_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 == CUCUL_DEFAULT)
  802. efg = CUCUL_WHITE;
  803. }
  804. }
  805. cucul_set_color_ansi(cv, efg, ebg);
  806. }