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.

351 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. Document*
  19. __mkd_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. void
  35. __mkd_enqueue(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. void
  69. __mkd_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 = __mkd_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. __mkd_enqueue(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. __mkd_enqueue(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; __mkd_header_dle(a->title);
  111. a->author= headers->next; __mkd_header_dle(a->author);
  112. a->date = headers->next->next; __mkd_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. int
  127. __mkd_io_strget(struct string_stream *in)
  128. {
  129. if ( !in->size ) return EOF;
  130. --(in->size);
  131. return *(in->data)++;
  132. }
  133. /* convert a block of text into a linked list
  134. */
  135. Document *
  136. mkd_string(const char *buf, int len, DWORD flags)
  137. {
  138. struct string_stream about;
  139. about.data = buf;
  140. about.size = len;
  141. return populate((getc_func)__mkd_io_strget, &about, flags & INPUT_MASK);
  142. }
  143. /* write the html to a file (xmlified if necessary)
  144. */
  145. int
  146. mkd_generatehtml(Document *p, FILE *output)
  147. {
  148. char *doc;
  149. int szdoc;
  150. if ( (szdoc = mkd_document(p, &doc)) != EOF ) {
  151. if ( p->ctx->flags & MKD_CDATA )
  152. mkd_generatexml(doc, szdoc, output);
  153. else
  154. fwrite(doc, szdoc, 1, output);
  155. putc('\n', output);
  156. return 0;
  157. }
  158. return -1;
  159. }
  160. /* convert some markdown text to html
  161. */
  162. int
  163. markdown(Document *document, FILE *out, int flags)
  164. {
  165. if ( mkd_compile(document, flags) ) {
  166. mkd_generatehtml(document, out);
  167. mkd_cleanup(document);
  168. return 0;
  169. }
  170. return -1;
  171. }
  172. /* write out a Cstring, mangled into a form suitable for `<a href=` or `<a id=`
  173. */
  174. void
  175. mkd_string_to_anchor(char *s, int len, mkd_sta_function_t outchar,
  176. void *out, int labelformat)
  177. {
  178. unsigned char c;
  179. int i, size;
  180. char *line;
  181. size = mkd_line(s, len, &line, IS_LABEL);
  182. if ( labelformat && (size>0) && !isalpha(line[0]) )
  183. (*outchar)('L',out);
  184. for ( i=0; i < size ; i++ ) {
  185. c = line[i];
  186. if ( labelformat ) {
  187. if ( isalnum(c) || (c == '_') || (c == ':') || (c == '-') || (c == '.' ) )
  188. (*outchar)(c, out);
  189. else
  190. (*outchar)('.', out);
  191. }
  192. else
  193. (*outchar)(c,out);
  194. }
  195. if (line)
  196. free(line);
  197. }
  198. /* ___mkd_reparse() a line
  199. */
  200. static void
  201. mkd_parse_line(char *bfr, int size, MMIOT *f, int flags)
  202. {
  203. ___mkd_initmmiot(f, 0);
  204. f->flags = flags & USER_FLAGS;
  205. ___mkd_reparse(bfr, size, 0, f, 0);
  206. ___mkd_emblock(f);
  207. }
  208. /* ___mkd_reparse() a line, returning it in malloc()ed memory
  209. */
  210. int
  211. mkd_line(char *bfr, int size, char **res, DWORD flags)
  212. {
  213. MMIOT f;
  214. int len;
  215. mkd_parse_line(bfr, size, &f, flags);
  216. if ( len = S(f.out) ) {
  217. /* kludge alert; we know that T(f.out) is malloced memory,
  218. * so we can just steal it away. This is awful -- there
  219. * should be an opaque method that transparently moves
  220. * the pointer out of the embedded Cstring.
  221. */
  222. EXPAND(f.out) = 0;
  223. *res = T(f.out);
  224. T(f.out) = 0;
  225. S(f.out) = ALLOCATED(f.out) = 0;
  226. }
  227. else {
  228. *res = 0;
  229. len = EOF;
  230. }
  231. ___mkd_freemmiot(&f, 0);
  232. return len;
  233. }
  234. /* ___mkd_reparse() a line, writing it to a FILE
  235. */
  236. int
  237. mkd_generateline(char *bfr, int size, FILE *output, DWORD flags)
  238. {
  239. MMIOT f;
  240. mkd_parse_line(bfr, size, &f, flags);
  241. if ( flags & MKD_CDATA )
  242. mkd_generatexml(T(f.out), S(f.out), output);
  243. else
  244. fwrite(T(f.out), S(f.out), 1, output);
  245. ___mkd_freemmiot(&f, 0);
  246. return 0;
  247. }
  248. /* set the url display callback
  249. */
  250. void
  251. mkd_e_url(Document *f, mkd_callback_t edit)
  252. {
  253. if ( f )
  254. f->cb.e_url = edit;
  255. }
  256. /* set the url options callback
  257. */
  258. void
  259. mkd_e_flags(Document *f, mkd_callback_t edit)
  260. {
  261. if ( f )
  262. f->cb.e_flags = edit;
  263. }
  264. /* set the url display/options deallocator
  265. */
  266. void
  267. mkd_e_free(Document *f, mkd_free_t dealloc)
  268. {
  269. if ( f )
  270. f->cb.e_free = dealloc;
  271. }
  272. /* set the url display/options context data field
  273. */
  274. void
  275. mkd_e_data(Document *f, void *data)
  276. {
  277. if ( f )
  278. f->cb.e_data = data;
  279. }
  280. /* set the href prefix for markdown extra style footnotes
  281. */
  282. void
  283. mkd_ref_prefix(Document *f, char *data)
  284. {
  285. if ( f )
  286. f->ref_prefix = data;
  287. }