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.
 
 
 
 
 
 

464 lines
16 KiB

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