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