PageRenderTime 52ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/mkd2term.c

https://bitbucket.org/zielmicha/markdown-to-terminal
C | 327 lines | 242 code | 46 blank | 39 comment | 49 complexity | dff4e4ac68e63570775ae2b8e8fb5156 MD5 | raw file
  1. /* -*- mode: c, c-basic-offset: 4 -*- */
  2. /* mkd2term.c - terminal formatted output from markdown text */
  3. /*
  4. * Copyright (c) 2012, Michał Zieliński <michal@zielinscy.org.pl>
  5. * Copyright (c) 2009, Baptiste Daroussin and Natacha Porté
  6. *
  7. * Permission to use, copy, modify, and distribute this software for any
  8. * purpose with or without fee is hereby granted, provided that the above
  9. * copyright notice and this permission notice appear in all copies.
  10. *
  11. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  12. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  13. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  14. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  15. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  16. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  17. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  18. */
  19. #include "markdown.h"
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <errno.h>
  23. #include <string.h>
  24. #include <term.h>
  25. #define READ_UNIT 1024
  26. #define OUTPUT_UNIT 64
  27. /****************************
  28. * MARKDOWN TO TERMINAL RENDERER *
  29. ****************************/
  30. #define TERM_COLOR_H1 2
  31. #define TERM_COLOR_H2 3
  32. #define TERM_COLOR_H3 1
  33. #define INDENT " "
  34. #define NEWLINE_INDENT "\n" INDENT
  35. // not thread safe
  36. struct buf* current_buf;
  37. int term_initialized;
  38. static int buffer_putc(int ch) {
  39. bufputc(current_buf, ch);
  40. return 0;
  41. }
  42. static void put_term(struct buf* ob, char* str) {
  43. current_buf = ob;
  44. tputs(str, 1, buffer_putc);
  45. }
  46. static void put_term_color(struct buf* ob, int color) {
  47. put_term(ob, tparm(tigetstr("setf"), color, 0, 0, 0, 0, 0, 0, 0, 0));
  48. }
  49. static void put_term_normal(struct buf* ob) {
  50. put_term(ob, tigetstr("sgr0"));
  51. }
  52. char translate_entity(char* s) {
  53. if(strcmp(s, "quot") == 0) {
  54. return '\'';
  55. } else if(strcmp(s, "gt") == 0) {
  56. return '>';
  57. } else if(s[0] == '#') {
  58. return (char)atoi(s + 1);
  59. } else {
  60. return 0;
  61. }
  62. }
  63. char get_entity(char* str, int pos, int size) {
  64. int my_pos = pos;
  65. my_pos ++;
  66. for(; my_pos < size && my_pos < pos + 20; my_pos++) {
  67. if(str[my_pos] == ';') {
  68. str[my_pos] = '\0';
  69. char* ent = str + pos + 1;
  70. return translate_entity(ent);
  71. }
  72. }
  73. return 0;
  74. }
  75. void
  76. term_entity(struct buf *ob, struct buf* text, void* opaque) {
  77. char ent = get_entity(text->data, 0, text->size);
  78. if(ent != 0)
  79. bufputc(ob, ent);
  80. else
  81. bufput(ob, text->data, text->size);
  82. }
  83. void
  84. term_text_escape(struct buf *ob, char *src, size_t size) {
  85. // this should actually escape terminal escape character
  86. int i = 0;
  87. while(i < size) {
  88. char ch = src[i];
  89. if(ch == '\n') BUFPUTSL(ob, NEWLINE_INDENT);
  90. else bufputc(ob, ch);
  91. i++;
  92. }
  93. }
  94. static void
  95. term_blockcode(struct buf *ob, struct buf *text, void *opaque) {
  96. BUFPUTSL(ob, "~~~~" NEWLINE_INDENT);
  97. if (text) term_text_escape(ob, text->data, text->size);
  98. BUFPUTSL(ob, "~~~~" NEWLINE_INDENT);
  99. }
  100. static int
  101. term_codespan(struct buf *ob, struct buf *text, void *opaque) {
  102. BUFPUTSL(ob, "`");
  103. if (text) term_text_escape(ob, text->data, text->size);
  104. BUFPUTSL(ob, "'");
  105. return 1;
  106. }
  107. static void
  108. term_header(struct buf *ob, struct buf *text, int level, void *opaque) {
  109. bufputc(ob, '\n');
  110. switch(level) {
  111. case 1:
  112. put_term_color(ob, TERM_COLOR_H1);
  113. break;
  114. case 2:
  115. put_term_color(ob, TERM_COLOR_H2);
  116. break;
  117. case 3:
  118. BUFPUTSL(ob, " ");
  119. put_term_color(ob, TERM_COLOR_H3);
  120. break;
  121. }
  122. put_term(ob, tigetstr("smul"));
  123. put_term(ob, tigetstr("bold"));
  124. if (text) bufput(ob, text->data, text->size);
  125. put_term_normal(ob);
  126. BUFPUTSL(ob, NEWLINE_INDENT);
  127. }
  128. static int
  129. term_double_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) {
  130. if (!text || !text->size) return 0;
  131. put_term(ob, tigetstr("bold"));
  132. put_term(ob, tigetstr("smul"));
  133. bufput(ob, text->data, text->size);
  134. put_term_normal(ob);
  135. return 1;
  136. }
  137. static int
  138. term_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) {
  139. if (!text || !text->size) return 0;
  140. put_term(ob, tigetstr("bold"));
  141. if (text) bufput(ob, text->data, text->size);
  142. put_term_normal(ob);
  143. return 1;
  144. }
  145. static int
  146. term_linebreak(struct buf *ob, void *opaque) {
  147. BUFPUTSL(ob, NEWLINE_INDENT);
  148. return 1;
  149. }
  150. static void
  151. term_paragraph(struct buf *ob, struct buf *text, void *opaque) {
  152. if (ob->size) BUFPUTSL(ob, NEWLINE_INDENT);
  153. // BUFPUTSL(ob, "\n");
  154. if (text) bufput(ob, text->data, text->size);
  155. BUFPUTSL(ob, NEWLINE_INDENT);
  156. }
  157. static void
  158. term_list(struct buf *ob, struct buf *text, int flags, void *opaque) {
  159. BUFPUTSL(ob, INDENT);
  160. if (text) bufput(ob, text->data, text->size);
  161. BUFPUTSL(ob, NEWLINE_INDENT);
  162. }
  163. static void
  164. term_listitem(struct buf *ob, struct buf *text, int flags, void *opaque) {
  165. if (flags & MKD_LIST_ORDERED)
  166. BUFPUTSL(ob, "# ");
  167. else
  168. BUFPUTSL(ob, "* ");
  169. if (text) {
  170. while (text->size && text->data[text->size - 1] == '\n')
  171. text->size -= 1;
  172. int i;
  173. for(i=0; i<text->size; i++) {
  174. char ch = text->data[i];
  175. if(ch == '\n')
  176. BUFPUTSL(ob, NEWLINE_INDENT);
  177. else
  178. bufputc(ob, text->data[i]);
  179. }
  180. }
  181. BUFPUTSL(ob, "");
  182. }
  183. static void
  184. term_hrule(struct buf* ob, void* opaque) {
  185. BUFPUTSL(ob, "\n");
  186. int cols = tgetnum("co");
  187. int i;
  188. for(i=0; i<cols - 1; i ++) bufputc(ob, '-');
  189. BUFPUTSL(ob, "\n");
  190. }
  191. static void
  192. term_blockquote(struct buf *ob, struct buf *text, void *opaque) {
  193. BUFPUTSL(ob, NEWLINE_INDENT);
  194. if (text) {
  195. while (text->size && text->data[text->size - 1] == '\n')
  196. text->size -= 1;
  197. bufput(ob, text->data, text->size);
  198. }
  199. BUFPUTSL(ob, NEWLINE_INDENT);
  200. }
  201. static void
  202. term_normal_text(struct buf *ob, struct buf *text, void *opaque) {
  203. if (text) term_text_escape(ob, text->data, text->size);
  204. }
  205. /* renderer structure */
  206. struct mkd_renderer to_man = {
  207. /* document-level callbacks */
  208. NULL,
  209. NULL,
  210. /* block-level callbacks */
  211. term_blockcode,
  212. term_blockquote,
  213. NULL,
  214. term_header,
  215. term_hrule,
  216. term_list,
  217. term_listitem,
  218. term_paragraph,
  219. NULL,
  220. NULL,
  221. NULL,
  222. /* span-level callbacks */
  223. NULL,
  224. term_codespan,
  225. term_double_emphasis,
  226. term_emphasis,
  227. NULL,
  228. term_linebreak,
  229. NULL,
  230. NULL,
  231. NULL,
  232. /* low-level callbacks */
  233. term_entity,
  234. term_normal_text,
  235. /* renderer data */
  236. 64,
  237. "*_",
  238. NULL
  239. };
  240. /*****************
  241. * MAIN FUNCTION *
  242. *****************/
  243. /* main • main function, interfacing STDIO with the parser */
  244. int
  245. main(int argc, char **argv) {
  246. setupterm(NULL, 1, NULL);
  247. struct buf *ib, *ob;
  248. size_t ret;
  249. FILE *in = stdin;
  250. /* opening the file if given from the command line */
  251. if (argc > 1) {
  252. in = fopen(argv[1], "r");
  253. if (!in) {
  254. fprintf(stderr,"Unable to open input file \"%s\": %s\n",
  255. argv[1], strerror(errno));
  256. return 1; } }
  257. /* reading everything */
  258. ib = bufnew(READ_UNIT);
  259. bufgrow(ib, READ_UNIT);
  260. while ((ret = fread(ib->data + ib->size, 1,
  261. ib->asize - ib->size, in)) > 0) {
  262. ib->size += ret;
  263. bufgrow(ib, ib->size + READ_UNIT); }
  264. if (in != stdin) fclose(in);
  265. /* performing markdown to man */
  266. ob = bufnew(OUTPUT_UNIT);
  267. markdown(ob, ib, &to_man);
  268. /* writing the result to stdout */
  269. printf("%s", INDENT);
  270. ret = fwrite(ob->data, 1, ob->size, stdout);
  271. if (ret < ob->size)
  272. fprintf(stderr, "Warning: only %zu output byte written, "
  273. "out of %zu\n",
  274. ret,
  275. ob->size);
  276. /* cleanup */
  277. bufrelease(ib);
  278. bufrelease(ob);
  279. printf("\n");
  280. return 0;
  281. }