Text::Markdown::Discount
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.

357 lines
6.6 KiB

  1. /*
  2. * mkdio -- markdown front end input functions
  3. *
  4. * Copyright (C) 2007 David L Parsons.
  5. * The redistribution terms are provided in the COPYRIGHT file that must
  6. * be distributed with this source code.
  7. */
  8. #include "config.h"
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <ctype.h>
  12. #include "cstring.h"
  13. #include "markdown.h"
  14. #include "amalloc.h"
  15. typedef ANCHOR(Line) LineAnchor;
  16. /* create a new blank Document
  17. */
  18. static Document*
  19. new_Document()
  20. {
  21. Document *ret = calloc(sizeof(Document), 1);
  22. if ( ret ) {
  23. if (( ret->ctx = calloc(sizeof(MMIOT), 1) )) {
  24. ret->magic = VALID_DOCUMENT;
  25. return ret;
  26. }
  27. free(ret);
  28. }
  29. return 0;
  30. }
  31. /* add a line to the markdown input chain, expanding tabs and
  32. * noting the presence of special characters as we go.
  33. */
  34. static void
  35. queue(Document* a, Cstring *line)
  36. {
  37. Line *p = calloc(sizeof *p, 1);
  38. unsigned char c;
  39. int xp = 0;
  40. int size = S(*line);
  41. unsigned char *str = (unsigned char*)T(*line);
  42. CREATE(p->text);
  43. ATTACH(a->content, p);
  44. while ( size-- ) {
  45. if ( (c = *str++) == '\t' ) {
  46. /* expand tabs into ->tabstop spaces. We use ->tabstop
  47. * because the ENTIRE FREAKING COMPUTER WORLD uses editors
  48. * that don't do ^T/^D, but instead use tabs for indentation,
  49. * and, of course, set their tabs down to 4 spaces
  50. */
  51. do {
  52. EXPAND(p->text) = ' ';
  53. } while ( ++xp % a->tabstop );
  54. }
  55. else if ( c >= ' ' ) {
  56. if ( c == '|' )
  57. p->flags |= PIPECHAR;
  58. EXPAND(p->text) = c;
  59. ++xp;
  60. }
  61. }
  62. EXPAND(p->text) = 0;
  63. S(p->text)--;
  64. p->dle = mkd_firstnonblank(p);
  65. }
  66. /* trim leading blanks from a header line
  67. */
  68. static void
  69. header_dle(Line *p)
  70. {
  71. CLIP(p->text, 0, 1);
  72. p->dle = mkd_firstnonblank(p);
  73. }
  74. /* build a Document from any old input.
  75. */
  76. typedef int (*getc_func)(void*);
  77. Document *
  78. populate(getc_func getc, void* ctx, int flags)
  79. {
  80. Cstring line;
  81. Document *a = new_Document();
  82. int c;
  83. int pandoc = 0;
  84. if ( !a ) return 0;
  85. a->tabstop = (flags & MKD_TABSTOP) ? 4 : TABSTOP;
  86. CREATE(line);
  87. while ( (c = (*getc)(ctx)) != EOF ) {
  88. if ( c == '\n' ) {
  89. if ( pandoc != EOF && pandoc < 3 ) {
  90. if ( S(line) && (T(line)[0] == '%') )
  91. pandoc++;
  92. else
  93. pandoc = EOF;
  94. }
  95. queue(a, &line);
  96. S(line) = 0;
  97. }
  98. else if ( isprint(c) || isspace(c) || (c & 0x80) )
  99. EXPAND(line) = c;
  100. }
  101. if ( S(line) )
  102. queue(a, &line);
  103. DELETE(line);
  104. if ( (pandoc == 3) && !(flags & (MKD_NOHEADER|MKD_STRICT)) ) {
  105. /* the first three lines started with %, so we have a header.
  106. * clip the first three lines out of content and hang them
  107. * off header.
  108. */
  109. Line *headers = T(a->content);
  110. a->title = headers; header_dle(a->title);
  111. a->author= headers->next; header_dle(a->author);
  112. a->date = headers->next->next; header_dle(a->date);
  113. T(a->content) = headers->next->next->next;
  114. }
  115. return a;
  116. }
  117. /* convert a file into a linked list
  118. */
  119. Document *
  120. mkd_in(FILE *f, DWORD flags)
  121. {
  122. return populate((getc_func)fgetc, f, flags & INPUT_MASK);
  123. }
  124. /* return a single character out of a buffer
  125. */
  126. struct string_ctx {
  127. const char *data; /* the unread data */
  128. int size; /* and how much is there? */
  129. } ;
  130. static int
  131. strget(struct string_ctx *in)
  132. {
  133. if ( !in->size ) return EOF;
  134. --(in->size);
  135. return *(in->data)++;
  136. }
  137. /* convert a block of text into a linked list
  138. */
  139. Document *
  140. mkd_string(const char *buf, int len, DWORD flags)
  141. {
  142. struct string_ctx about;
  143. about.data = buf;
  144. about.size = len;
  145. return populate((getc_func)strget, &about, flags & INPUT_MASK);
  146. }
  147. /* write the html to a file (xmlified if necessary)
  148. */
  149. int
  150. mkd_generatehtml(Document *p, FILE *output)
  151. {
  152. char *doc;
  153. int szdoc;
  154. if ( (szdoc = mkd_document(p, &doc)) != EOF ) {
  155. if ( p->ctx->flags & MKD_CDATA )
  156. mkd_generatexml(doc, szdoc, output);
  157. else
  158. fwrite(doc, szdoc, 1, output);
  159. putc('\n', output);
  160. return 0;
  161. }
  162. return -1;
  163. }
  164. /* convert some markdown text to html
  165. */
  166. int
  167. markdown(Document *document, FILE *out, int flags)
  168. {
  169. if ( mkd_compile(document, flags) ) {
  170. mkd_generatehtml(document, out);
  171. mkd_cleanup(document);
  172. return 0;
  173. }
  174. return -1;
  175. }
  176. /* write out a Cstring, mangled into a form suitable for `<a href=` or `<a id=`
  177. */
  178. void
  179. mkd_string_to_anchor(char *s, int len, mkd_sta_function_t outchar,
  180. void *out, int labelformat)
  181. {
  182. unsigned char c;
  183. int i, size;
  184. char *line;
  185. size = mkd_line(s, len, &line, IS_LABEL);
  186. if ( labelformat && (size>0) && !isalpha(line[0]) )
  187. (*outchar)('L',out);
  188. for ( i=0; i < size ; i++ ) {
  189. c = line[i];
  190. if ( labelformat ) {
  191. if ( isalnum(c) || (c == '_') || (c == ':') || (c == '-') || (c == '.' ) )
  192. (*outchar)(c, out);
  193. else
  194. (*outchar)('.', out);
  195. }
  196. else
  197. (*outchar)(c,out);
  198. }
  199. if (line)
  200. free(line);
  201. }
  202. /* ___mkd_reparse() a line
  203. */
  204. static void
  205. mkd_parse_line(char *bfr, int size, MMIOT *f, int flags)
  206. {
  207. ___mkd_initmmiot(f, 0);
  208. f->flags = flags & USER_FLAGS;
  209. ___mkd_reparse(bfr, size, 0, f);
  210. ___mkd_emblock(f);
  211. }
  212. /* ___mkd_reparse() a line, returning it in malloc()ed memory
  213. */
  214. int
  215. mkd_line(char *bfr, int size, char **res, DWORD flags)
  216. {
  217. MMIOT f;
  218. int len;
  219. mkd_parse_line(bfr, size, &f, flags);
  220. if ( len = S(f.out) ) {
  221. /* kludge alert; we know that T(f.out) is malloced memory,
  222. * so we can just steal it away. This is awful -- there
  223. * should be an opaque method that transparently moves
  224. * the pointer out of the embedded Cstring.
  225. */
  226. EXPAND(f.out) = 0;
  227. *res = T(f.out);
  228. T(f.out) = 0;
  229. S(f.out) = ALLOCATED(f.out) = 0;
  230. }
  231. else {
  232. *res = 0;
  233. len = EOF;
  234. }
  235. ___mkd_freemmiot(&f, 0);
  236. return len;
  237. }
  238. /* ___mkd_reparse() a line, writing it to a FILE
  239. */
  240. int
  241. mkd_generateline(char *bfr, int size, FILE *output, DWORD flags)
  242. {
  243. MMIOT f;
  244. mkd_parse_line(bfr, size, &f, flags);
  245. if ( flags & MKD_CDATA )
  246. mkd_generatexml(T(f.out), S(f.out), output);
  247. else
  248. fwrite(T(f.out), S(f.out), 1, output);
  249. ___mkd_freemmiot(&f, 0);
  250. return 0;
  251. }
  252. /* set the url display callback
  253. */
  254. void
  255. mkd_e_url(Document *f, mkd_callback_t edit)
  256. {
  257. if ( f )
  258. f->cb.e_url = edit;
  259. }
  260. /* set the url options callback
  261. */
  262. void
  263. mkd_e_flags(Document *f, mkd_callback_t edit)
  264. {
  265. if ( f )
  266. f->cb.e_flags = edit;
  267. }
  268. /* set the url display/options deallocator
  269. */
  270. void
  271. mkd_e_free(Document *f, mkd_free_t dealloc)
  272. {
  273. if ( f )
  274. f->cb.e_free = dealloc;
  275. }
  276. /* set the url display/options context data field
  277. */
  278. void
  279. mkd_e_data(Document *f, void *data)
  280. {
  281. if ( f )
  282. f->cb.e_data = data;
  283. }
  284. /* set the href prefix for markdown extra style footnotes
  285. */
  286. void
  287. mkd_ref_prefix(Document *f, char *data)
  288. {
  289. if ( f )
  290. f->ref_prefix = data;
  291. }