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 година
пре 18 година
пре 18 година
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  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; 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. * This file contains various import functions.
  15. */
  16. #include "config.h"
  17. #include "common.h"
  18. #if !defined __KERNEL__
  19. # include <stdio.h>
  20. # include <stdlib.h>
  21. # include <string.h>
  22. #endif
  23. #include "cucul.h"
  24. #include "cucul_internals.h"
  25. static inline uint32_t sscanu32(void const *s)
  26. {
  27. uint32_t x;
  28. memcpy(&x, s, 4);
  29. return hton32(x);
  30. }
  31. static inline uint16_t sscanu16(void const *s)
  32. {
  33. uint16_t x;
  34. memcpy(&x, s, 2);
  35. return hton16(x);
  36. }
  37. /* ANSI Graphic Rendition Combination Mode */
  38. struct ansi_grcm
  39. {
  40. uint8_t fg, bg; /* ANSI-context fg/bg */
  41. uint8_t efg, ebg; /* Effective (libcucul) fg/bg */
  42. uint8_t bold, negative, concealed;
  43. };
  44. static long int import_caca(cucul_canvas_t *, void const *, unsigned int);
  45. static long int import_text(cucul_canvas_t *, void const *, unsigned int);
  46. static long int import_ansi(cucul_canvas_t *, void const *, unsigned int, int);
  47. static void ansi_parse_grcm(cucul_canvas_t *, struct ansi_grcm *,
  48. unsigned int, unsigned int const *);
  49. /** \brief Import a memory buffer into a canvas
  50. *
  51. * Import a memory buffer into the given libcucul canvas's current
  52. * frame. The current frame is resized accordingly and its contents are
  53. * replaced with the imported data.
  54. *
  55. * Valid values for \c format are:
  56. * - \c "": attempt to autodetect the file format.
  57. * - \c "caca": import native libcaca files.
  58. * - \c "text": import ASCII text files.
  59. * - \c "ansi": import ANSI files.
  60. * - \c "utf8": import UTF-8 files with ANSI colour codes.
  61. *
  62. * The number of bytes read is returned. If the file format is valid, but
  63. * not enough data was available, 0 is returned.
  64. *
  65. * If an error occurs, -1 is returned and \b errno is set accordingly:
  66. * - \c ENOMEM Not enough memory to allocate canvas.
  67. * - \c EINVAL Invalid format requested.
  68. *
  69. * \param cv A libcucul canvas in which to import the file.
  70. * \param data A memory area containing the data to be loaded into the canvas.
  71. * \param len The size in bytes of the memory area.
  72. * \param format A string describing the input format.
  73. * \return The number of bytes read, or 0 if there was not enough data,
  74. * or -1 if an error occurred.
  75. */
  76. long int cucul_import_memory(cucul_canvas_t *cv, void const *data,
  77. unsigned long int len, char const *format)
  78. {
  79. if(!strcasecmp("caca", format))
  80. return import_caca(cv, data, len);
  81. if(!strcasecmp("utf8", format))
  82. return import_ansi(cv, data, len, 1);
  83. if(!strcasecmp("text", format))
  84. return import_text(cv, data, len);
  85. if(!strcasecmp("ansi", format))
  86. return import_ansi(cv, data, len, 0);
  87. /* Autodetection */
  88. if(!strcasecmp("", format))
  89. {
  90. unsigned char const *str = data;
  91. unsigned int i;
  92. /* If 4 first bytes are 0xcaca + 'CV' */
  93. if(len >= 4 && str[0] == 0xca &&
  94. str[1] == 0xca && str[2] == 'C' && str[3] == 'V')
  95. return import_caca(cv, data, len);
  96. /* If we find ESC[ argv, we guess it's an ANSI file */
  97. for(i = 0; i + 1 < len; i++)
  98. if((str[i] == 0x1b) && (str[i + 1] == '['))
  99. return import_ansi(cv, data, len, 0);
  100. /* Otherwise, import it as text */
  101. return import_text(cv, data, len);
  102. }
  103. seterrno(EINVAL);
  104. return -1;
  105. }
  106. /** \brief Import a file into a canvas
  107. *
  108. * Import a file into the given libcucul canvas's current frame. The
  109. * current frame is resized accordingly and its contents are replaced
  110. * with the imported data.
  111. *
  112. * Valid values for \c format are:
  113. * - \c "": attempt to autodetect the file format.
  114. * - \c "caca": import native libcaca files.
  115. * - \c "text": import ASCII text files.
  116. * - \c "ansi": import ANSI files.
  117. * - \c "utf8": import UTF-8 files with ANSI colour codes.
  118. *
  119. * The number of bytes read is returned. If the file format is valid, but
  120. * not enough data was available, 0 is returned.
  121. *
  122. * If an error occurs, -1 is returned and \b errno is set accordingly:
  123. * - \c ENOSYS File access is not implemented on this system.
  124. * - \c ENOMEM Not enough memory to allocate canvas.
  125. * - \c EINVAL Invalid format requested.
  126. * cucul_import_file() may also fail and set \b errno for any of the
  127. * errors specified for the routine fopen().
  128. *
  129. * \param cv A libcucul canvas in which to import the file.
  130. * \param filename The name of the file to load.
  131. * \param format A string describing the input format.
  132. * \return The number of bytes read, or 0 if there was not enough data,
  133. * or -1 if an error occurred.
  134. */
  135. long int cucul_import_file(cucul_canvas_t *cv, char const *filename,
  136. char const *format)
  137. {
  138. #if defined __KERNEL__
  139. seterrno(ENOSYS);
  140. return -1;
  141. #else
  142. FILE *fp;
  143. void *data;
  144. long int size;
  145. int ret;
  146. fp = fopen(filename, "rb");
  147. if(!fp)
  148. return -1; /* fopen already set errno */
  149. fseek(fp, 0, SEEK_END);
  150. size = ftell(fp);
  151. data = malloc(size);
  152. if(!data)
  153. {
  154. fclose(fp);
  155. seterrno(ENOMEM);
  156. return -1;
  157. }
  158. fseek(fp, 0, SEEK_SET);
  159. fread(data, size, 1, fp);
  160. fclose(fp);
  161. ret = cucul_import_memory(cv, data, size, format);
  162. free(data);
  163. return ret;
  164. #endif
  165. }
  166. /** \brief Get available import formats
  167. *
  168. * Return a list of available import formats. The list is a NULL-terminated
  169. * array of strings, interleaving a string containing the internal value for
  170. * the import format, to be used with cucul_import_canvas(), and a string
  171. * containing the natural language description for that import format.
  172. *
  173. * This function never fails.
  174. *
  175. * \return An array of strings.
  176. */
  177. char const * const * cucul_get_import_list(void)
  178. {
  179. static char const * const list[] =
  180. {
  181. "", "autodetect",
  182. "caca", "native libcaca format",
  183. "text", "plain text",
  184. "ansi", "ANSI coloured text",
  185. "utf8", "UTF-8 files with ANSI colour codes",
  186. NULL, NULL
  187. };
  188. return list;
  189. }
  190. /*
  191. * XXX: the following functions are local.
  192. */
  193. static long int import_caca(cucul_canvas_t *cv,
  194. void const *data, unsigned int size)
  195. {
  196. uint8_t const *buf = (uint8_t const *)data;
  197. unsigned int control_size, data_size, expected_size, frames, f, n;
  198. uint16_t version, flags;
  199. if(size < 20)
  200. return 0;
  201. if(buf[0] != 0xca || buf[1] != 0xca || buf[2] != 'C' || buf[3] != 'V')
  202. {
  203. debug("caca import error: expected \\xca\\xcaCV header");
  204. goto invalid_caca;
  205. }
  206. control_size = sscanu32(buf + 4);
  207. data_size = sscanu32(buf + 8);
  208. version = sscanu16(buf + 12);
  209. frames = sscanu32(buf + 14);
  210. flags = sscanu16(buf + 18);
  211. if(size < 4 + control_size + data_size)
  212. return 0;
  213. if(control_size < 16 + frames * 32)
  214. {
  215. debug("caca import error: control size %lu < expected %lu",
  216. (unsigned long int)control_size, 16 + frames * 32);
  217. goto invalid_caca;
  218. }
  219. for(expected_size = 0, f = 0; f < frames; f++)
  220. {
  221. unsigned int width, height, duration;
  222. uint32_t attr;
  223. int x, y, handlex, handley;
  224. width = sscanu32(buf + 4 + 16 + f * 24);
  225. height = sscanu32(buf + 4 + 16 + f * 24 + 4);
  226. duration = sscanu32(buf + 4 + 16 + f * 24 + 8);
  227. attr = sscanu32(buf + 4 + 16 + f * 24 + 12);
  228. x = (int32_t)sscanu32(buf + 4 + 16 + f * 24 + 16);
  229. y = (int32_t)sscanu32(buf + 4 + 16 + f * 24 + 20);
  230. handlex = (int32_t)sscanu32(buf + 4 + 16 + f * 24 + 24);
  231. handley = (int32_t)sscanu32(buf + 4 + 16 + f * 24 + 28);
  232. expected_size += width * height * 8;
  233. }
  234. if(expected_size != data_size)
  235. {
  236. debug("caca import error: data size %lu < expected %lu",
  237. (unsigned long int)data_size, (unsigned long int)expected_size);
  238. goto invalid_caca;
  239. }
  240. /* FIXME: read all frames, not only the first one */
  241. cucul_set_canvas_size(cv, 0, 0);
  242. cucul_set_canvas_size(cv, sscanu32(buf + 4 + 16),
  243. sscanu32(buf + 4 + 16 + 4));
  244. /* FIXME: check for return value */
  245. for(n = sscanu32(buf + 4 + 16) * sscanu32(buf + 4 + 16 + 4); n--; )
  246. {
  247. cv->chars[n] = sscanu32(buf + 4 + control_size + 8 * n);
  248. cv->attrs[n] = sscanu32(buf + 4 + control_size + 8 * n + 4);
  249. }
  250. cv->curattr = sscanu32(buf + 4 + 16 + 12);
  251. cv->frames[0].x = (int32_t)sscanu32(buf + 4 + 16 + 0 * 24 + 16);
  252. cv->frames[0].y = (int32_t)sscanu32(buf + 4 + 16 + 0 * 24 + 20);
  253. cv->frames[0].handlex = (int32_t)sscanu32(buf + 4 + 16 + 0 * 24 + 24);
  254. cv->frames[0].handley = (int32_t)sscanu32(buf + 4 + 16 + 0 * 24 + 28);
  255. return 4 + control_size + data_size;
  256. invalid_caca:
  257. seterrno(EINVAL);
  258. return -1;
  259. }
  260. static long int import_text(cucul_canvas_t *cv,
  261. void const *data, unsigned int size)
  262. {
  263. char const *text = (char const *)data;
  264. unsigned int width = 0, height = 0, x = 0, y = 0, i;
  265. cucul_set_canvas_size(cv, width, height);
  266. for(i = 0; i < size; i++)
  267. {
  268. unsigned char ch = *text++;
  269. if(ch == '\r')
  270. continue;
  271. if(ch == '\n')
  272. {
  273. x = 0;
  274. y++;
  275. continue;
  276. }
  277. if(x >= width || y >= height)
  278. {
  279. if(x >= width)
  280. width = x + 1;
  281. if(y >= height)
  282. height = y + 1;
  283. cucul_set_canvas_size(cv, width, height);
  284. }
  285. cucul_put_char(cv, x, y, ch);
  286. x++;
  287. }
  288. if(y > height)
  289. cucul_set_canvas_size(cv, width, height = y);
  290. return size;
  291. }
  292. static long int import_ansi(cucul_canvas_t *cv,
  293. void const *data, unsigned int size, int utf8)
  294. {
  295. struct ansi_grcm grcm;
  296. unsigned char const *buffer = (unsigned char const*)data;
  297. unsigned int i, j, skip, dummy = 0;
  298. unsigned int width = 0, height = 0, wch = 1;
  299. unsigned long int ch;
  300. int x = 0, y = 0, save_x = 0, save_y = 0;
  301. cucul_set_canvas_size(cv, width, height);
  302. ansi_parse_grcm(cv, &grcm, 1, &dummy);
  303. for(i = 0; i < size; i += skip)
  304. {
  305. skip = 1;
  306. /* Wrap long lines */
  307. if((unsigned int)x >= 80)
  308. {
  309. x = 0;
  310. y++;
  311. }
  312. if(buffer[i] == '\x1a' && size - i >= 8
  313. && !memcmp(buffer + i + 1, "SAUCE00", 7))
  314. break; /* End before SAUCE data */
  315. if(buffer[i] == '\r')
  316. continue; /* DOS sucks */
  317. if(buffer[i] == '\n')
  318. {
  319. x = 0;
  320. y++;
  321. continue;
  322. }
  323. /* Interpret escape commands, as per Standard ECMA-48 "Control
  324. * Functions for Coded Character Sets", 5.4. Control sequences. */
  325. if(buffer[i] == '\x1b' && buffer[i + 1] == '[')
  326. {
  327. unsigned int argc = 0, argv[101];
  328. unsigned int param, inter, final;
  329. /* Compute offsets to parameter bytes, intermediate bytes and
  330. * to the final byte. Only the final byte is mandatory, there
  331. * can be zero of the others.
  332. * 0 param=2 inter final final+1
  333. * +-----+------------------+---------------------+-----------------+
  334. * | CSI | parameter bytes | intermediate bytes | final byte |
  335. * | | 0x30 - 0x3f | 0x20 - 0x2f | 0x40 - 0x7e |
  336. * | ^[[ | 0123456789:;<=>? | SPC !"#$%&'()*+,-./ | azAZ@[\]^_`{|}~ |
  337. * +-----+------------------+---------------------+-----------------+
  338. */
  339. param = 2;
  340. for(inter = param; i + inter < size; inter++)
  341. if(buffer[i + inter] < 0x30 || buffer[i + inter] > 0x3f)
  342. break;
  343. for(final = inter; i + final < size; final++)
  344. if(buffer[i + final] < 0x20 || buffer[i + final] > 0x2f)
  345. break;
  346. if(buffer[i + final] < 0x40 || buffer[i + final] > 0x7e)
  347. break; /* Invalid Final Byte */
  348. skip += final;
  349. /* Sanity checks */
  350. if(param < inter && buffer[i + param] >= 0x3c)
  351. {
  352. fprintf(stderr, "private sequence \"^[[%.*s\"\n",
  353. final - param + 1, buffer + i + param);
  354. continue; /* Private sequence, skip it entirely */
  355. }
  356. if(final - param > 100)
  357. continue; /* Suspiciously long sequence, skip it */
  358. /* Parse parameter bytes as per ECMA-48 5.4.2: Parameter string
  359. * format */
  360. if(param < inter)
  361. {
  362. argv[0] = 0;
  363. for(j = param; j < inter; j++)
  364. {
  365. if(buffer[i + j] == ';')
  366. argv[++argc] = 0;
  367. else if(buffer[i + j] >= '0' && buffer[i + j] <= '9')
  368. argv[argc] = 10 * argv[argc] + (buffer[i + j] - '0');
  369. }
  370. argc++;
  371. }
  372. /* Interpret final byte. The code representations are given in
  373. * ECMA-48 5.4: Control sequences, and the code definitions are
  374. * given in ECMA-48 8.3: Definition of control functions. */
  375. switch(buffer[i + final])
  376. {
  377. case 'f': /* CUP - Cursor Position */
  378. case 'H': /* HVP - Character And Line Position */
  379. x = (argc > 1 && argv[1] > 0) ? argv[1] - 1 : 0;
  380. y = (argc > 0 && argv[0] > 0) ? argv[0] - 1 : 0;
  381. break;
  382. case 'A': /* CUU - Cursor Up */
  383. y -= argc ? argv[0] : 1;
  384. if(y < 0)
  385. y = 0;
  386. break;
  387. case 'B': /* CUD - Cursor Down */
  388. y += argc ? argv[0] : 1;
  389. break;
  390. case 'C': /* CUF - Cursor Right */
  391. x += argc ? argv[0] : 1;
  392. break;
  393. case 'D': /* CUB - Cursor Left */
  394. x -= argc ? argv[0] : 1;
  395. if(x < 0)
  396. x = 0;
  397. break;
  398. case 's': /* Private (save cursor position) */
  399. save_x = x;
  400. save_y = y;
  401. break;
  402. case 'u': /* Private (reload cursor position) */
  403. x = save_x;
  404. y = save_y;
  405. break;
  406. case 'J': /* ED - Erase In Page */
  407. if(argv[0] == 2)
  408. x = y = 0;
  409. break;
  410. case 'K': /* EL - Erase In Line */
  411. if(width < 80)
  412. cucul_set_color_ansi(cv, CUCUL_DEFAULT, CUCUL_TRANSPARENT);
  413. cucul_set_canvas_size(cv, width = 80, height);
  414. for(j = x; j < 80; j++)
  415. cucul_put_char(cv, j, y, ' ');
  416. x = 80;
  417. break;
  418. case 'm': /* SGR - Select Graphic Rendition */
  419. ansi_parse_grcm(cv, &grcm, argc, argv);
  420. break;
  421. default:
  422. fprintf(stderr, "unknown command %c\n", buffer[i + final]);
  423. break;
  424. }
  425. continue;
  426. }
  427. /* Get the character we’re going to paste */
  428. if(utf8)
  429. {
  430. unsigned int bytes;
  431. if(i + 6 < size)
  432. ch = cucul_utf8_to_utf32((char const *)(buffer + i), &bytes);
  433. else
  434. {
  435. /* Add a trailing zero to what we're going to read */
  436. char tmp[7];
  437. memcpy(tmp, buffer + i, size - i);
  438. tmp[size - i] = '\0';
  439. ch = cucul_utf8_to_utf32(tmp, &bytes);
  440. }
  441. if(!bytes)
  442. {
  443. /* If the Unicode is invalid, assume it was latin1. */
  444. ch = buffer[i];
  445. bytes = 1;
  446. }
  447. wch = cucul_utf32_is_fullwidth(ch) ? 2 : 1;
  448. skip += bytes - 1;
  449. }
  450. else
  451. {
  452. ch = cucul_cp437_to_utf32(buffer[i]);
  453. }
  454. /* Make sure the canvas is big enough. */
  455. if((unsigned int)x + wch > width)
  456. {
  457. cucul_set_color_ansi(cv, CUCUL_DEFAULT, CUCUL_TRANSPARENT);
  458. cucul_set_canvas_size(cv, width = x + wch, height);
  459. }
  460. if((unsigned int)y >= height)
  461. {
  462. cucul_set_color_ansi(cv, CUCUL_DEFAULT, CUCUL_TRANSPARENT);
  463. cucul_set_canvas_size(cv, width, height = y + 1);
  464. }
  465. /* Now paste our character */
  466. cucul_set_color_ansi(cv, grcm.efg, grcm.ebg);
  467. cucul_put_char(cv, x, y, ch);
  468. x += wch;
  469. }
  470. if((unsigned int)y > height)
  471. {
  472. cucul_set_color_ansi(cv, CUCUL_DEFAULT, CUCUL_TRANSPARENT);
  473. cucul_set_canvas_size(cv, width, height = y);
  474. }
  475. return size;
  476. }
  477. /* XXX : ANSI loader helper */
  478. static void ansi_parse_grcm(cucul_canvas_t *cv, struct ansi_grcm *g,
  479. unsigned int argc, unsigned int const *argv)
  480. {
  481. static uint8_t const ansi2cucul[] =
  482. {
  483. CUCUL_BLACK, CUCUL_RED, CUCUL_GREEN, CUCUL_BROWN,
  484. CUCUL_BLUE, CUCUL_MAGENTA, CUCUL_CYAN, CUCUL_LIGHTGRAY
  485. };
  486. unsigned int j;
  487. for(j = 0; j < argc; j++)
  488. {
  489. /* Defined in ECMA-48 8.3.117: SGR - SELECT GRAPHIC RENDITION */
  490. if(argv[j] >= 30 && argv[j] <= 37)
  491. g->fg = ansi2cucul[argv[j] - 30];
  492. else if(argv[j] >= 40 && argv[j] <= 47)
  493. g->bg = ansi2cucul[argv[j] - 40];
  494. else if(argv[j] >= 90 && argv[j] <= 97)
  495. g->fg = ansi2cucul[argv[j] - 90] + 8;
  496. else if(argv[j] >= 100 && argv[j] <= 107)
  497. g->bg = ansi2cucul[argv[j] - 100] + 8;
  498. else switch(argv[j])
  499. {
  500. case 0: /* default rendition */
  501. g->fg = CUCUL_DEFAULT;
  502. g->bg = CUCUL_TRANSPARENT;
  503. g->bold = g->negative = g->concealed = 0;
  504. break;
  505. case 1: /* bold or increased intensity */
  506. g->bold = 1;
  507. break;
  508. case 4: /* singly underlined */
  509. break;
  510. case 5: /* slowly blinking (less then 150 per minute) */
  511. break;
  512. case 7: /* negative image */
  513. g->negative = 1;
  514. break;
  515. case 8: /* concealed characters */
  516. g->concealed = 1;
  517. break;
  518. case 22: /* normal colour or normal intensity (neither bold nor faint) */
  519. g->bold = 0;
  520. break;
  521. case 28: /* revealed characters */
  522. g->concealed = 0;
  523. break;
  524. case 39: /* default display colour (implementation-defined) */
  525. g->fg = CUCUL_DEFAULT;
  526. break;
  527. case 49: /* default background colour (implementation-defined) */
  528. g->bg = CUCUL_TRANSPARENT;
  529. break;
  530. default:
  531. fprintf(stderr, "unknown sgr %i\n", argv[j]);
  532. break;
  533. }
  534. }
  535. if(g->concealed)
  536. {
  537. g->efg = g->ebg = CUCUL_TRANSPARENT;
  538. }
  539. else
  540. {
  541. g->efg = g->negative ? g->bg : g->fg;
  542. g->ebg = g->negative ? g->fg : g->bg;
  543. if(g->bold)
  544. {
  545. if(g->efg < 8)
  546. g->efg += 8;
  547. else if(g->efg == CUCUL_DEFAULT)
  548. g->efg = CUCUL_WHITE;
  549. }
  550. }
  551. }