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.

1334 lines
26 KiB

  1. /* markdown: a C implementation of John Gruber's Markdown markup language.
  2. *
  3. * Copyright (C) 2007 David L Parsons.
  4. * The redistribution terms are provided in the COPYRIGHT file that must
  5. * be distributed with this source code.
  6. */
  7. #include "config.h"
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <stdarg.h>
  11. #include <stdlib.h>
  12. #include <time.h>
  13. #include <ctype.h>
  14. #include "cstring.h"
  15. #include "markdown.h"
  16. #include "amalloc.h"
  17. #include "tags.h"
  18. typedef int (*stfu)(const void*,const void*);
  19. typedef ANCHOR(Paragraph) ParagraphRoot;
  20. static Paragraph *Pp(ParagraphRoot *, Line *, int);
  21. static Paragraph *compile(Line *, int, MMIOT *);
  22. /* case insensitive string sort for Footnote tags.
  23. */
  24. int
  25. __mkd_footsort(Footnote *a, Footnote *b)
  26. {
  27. int i;
  28. char ac, bc;
  29. if ( S(a->tag) != S(b->tag) )
  30. return S(a->tag) - S(b->tag);
  31. for ( i=0; i < S(a->tag); i++) {
  32. ac = tolower(T(a->tag)[i]);
  33. bc = tolower(T(b->tag)[i]);
  34. if ( isspace(ac) && isspace(bc) )
  35. continue;
  36. if ( ac != bc )
  37. return ac - bc;
  38. }
  39. return 0;
  40. }
  41. /* find the first blank character after position <i>
  42. */
  43. static int
  44. nextblank(Line *t, int i)
  45. {
  46. while ( (i < S(t->text)) && !isspace(T(t->text)[i]) )
  47. ++i;
  48. return i;
  49. }
  50. /* find the next nonblank character after position <i>
  51. */
  52. static int
  53. nextnonblank(Line *t, int i)
  54. {
  55. while ( (i < S(t->text)) && isspace(T(t->text)[i]) )
  56. ++i;
  57. return i;
  58. }
  59. /* find the first nonblank character on the Line.
  60. */
  61. int
  62. mkd_firstnonblank(Line *p)
  63. {
  64. return nextnonblank(p,0);
  65. }
  66. static inline int
  67. blankline(Line *p)
  68. {
  69. return ! (p && (S(p->text) > p->dle) );
  70. }
  71. static Line *
  72. skipempty(Line *p)
  73. {
  74. while ( p && (p->dle == S(p->text)) )
  75. p = p->next;
  76. return p;
  77. }
  78. void
  79. ___mkd_tidy(Cstring *t)
  80. {
  81. while ( S(*t) && isspace(T(*t)[S(*t)-1]) )
  82. --S(*t);
  83. }
  84. static struct kw comment = { "!--", 3, 0 };
  85. static struct kw *
  86. isopentag(Line *p)
  87. {
  88. int i=0, len;
  89. char *line;
  90. if ( !p ) return 0;
  91. line = T(p->text);
  92. len = S(p->text);
  93. if ( len < 3 || line[0] != '<' )
  94. return 0;
  95. if ( line[1] == '!' && line[2] == '-' && line[3] == '-' )
  96. /* comments need special case handling, because
  97. * the !-- doesn't need to end in a whitespace
  98. */
  99. return &comment;
  100. /* find how long the tag is so we can check to see if
  101. * it's a block-level tag
  102. */
  103. for ( i=1; i < len && T(p->text)[i] != '>'
  104. && T(p->text)[i] != '/'
  105. && !isspace(T(p->text)[i]); ++i )
  106. ;
  107. return mkd_search_tags(T(p->text)+1, i-1);
  108. }
  109. typedef struct _flo {
  110. Line *t;
  111. int i;
  112. } FLO;
  113. #define floindex(x) (x.i)
  114. static int
  115. flogetc(FLO *f)
  116. {
  117. if ( f && f->t ) {
  118. if ( f->i < S(f->t->text) )
  119. return T(f->t->text)[f->i++];
  120. f->t = f->t->next;
  121. f->i = 0;
  122. return flogetc(f);
  123. }
  124. return EOF;
  125. }
  126. static void
  127. splitline(Line *t, int cutpoint)
  128. {
  129. if ( t && (cutpoint < S(t->text)) ) {
  130. Line *tmp = calloc(1, sizeof *tmp);
  131. tmp->next = t->next;
  132. t->next = tmp;
  133. tmp->dle = t->dle;
  134. SUFFIX(tmp->text, T(t->text)+cutpoint, S(t->text)-cutpoint);
  135. S(t->text) = cutpoint;
  136. }
  137. }
  138. #define UNCHECK(l) ((l)->flags &= ~CHECKED)
  139. #define UNLESS_FENCED(t) if (fenced) { \
  140. other = 1; l->count += (c == ' ' ? 0 : -1); \
  141. } else { t; }
  142. /*
  143. * walk a line, seeing if it's any of half a dozen interesting regular
  144. * types.
  145. */
  146. static void
  147. checkline(Line *l)
  148. {
  149. int eol, i;
  150. int dashes = 0, spaces = 0,
  151. equals = 0, underscores = 0,
  152. stars = 0, tildes = 0, other = 0,
  153. backticks = 0, fenced = 0;
  154. l->flags |= CHECKED;
  155. l->kind = chk_text;
  156. l->count = 0;
  157. if (l->dle >= 4) { l->kind=chk_code; return; }
  158. for ( eol = S(l->text); eol > l->dle && isspace(T(l->text)[eol-1]); --eol )
  159. ;
  160. for (i=l->dle; i<eol; i++) {
  161. register int c = T(l->text)[i];
  162. if ( c != ' ' ) l->count++;
  163. switch (c) {
  164. case '-': UNLESS_FENCED(dashes = 1); break;
  165. case ' ': UNLESS_FENCED(spaces = 1); break;
  166. case '=': equals = 1; break;
  167. case '_': UNLESS_FENCED(underscores = 1); break;
  168. case '*': stars = 1; break;
  169. #if WITH_FENCED_CODE
  170. case '~': if (other) return; fenced = 1; tildes = 1; break;
  171. case '`': if (other) return; fenced = 1; backticks = 1; break;
  172. #endif
  173. default:
  174. other = 1;
  175. l->count--;
  176. if (!fenced) return;
  177. }
  178. }
  179. if ( dashes + equals + underscores + stars + tildes + backticks > 1 )
  180. return;
  181. if ( spaces ) {
  182. if ( (underscores || stars || dashes) )
  183. l->kind = chk_hr;
  184. return;
  185. }
  186. if ( stars || underscores ) { l->kind = chk_hr; }
  187. else if ( dashes ) { l->kind = chk_dash; }
  188. else if ( equals ) { l->kind = chk_equal; }
  189. #if WITH_FENCED_CODE
  190. else if ( tildes ) { l->kind = chk_tilde; }
  191. else if ( backticks ) { l->kind = chk_backtick; }
  192. #endif
  193. }
  194. static Line *
  195. commentblock(Paragraph *p, int *unclosed)
  196. {
  197. Line *t, *ret;
  198. char *end;
  199. for ( t = p->text; t ; t = t->next) {
  200. if ( end = strstr(T(t->text), "-->") ) {
  201. splitline(t, 3 + (end - T(t->text)) );
  202. ret = t->next;
  203. t->next = 0;
  204. return ret;
  205. }
  206. }
  207. *unclosed = 1;
  208. return t;
  209. }
  210. static Line *
  211. htmlblock(Paragraph *p, struct kw *tag, int *unclosed)
  212. {
  213. Line *ret;
  214. FLO f = { p->text, 0 };
  215. int c;
  216. int i, closing, depth=0;
  217. *unclosed = 0;
  218. if ( tag == &comment )
  219. return commentblock(p, unclosed);
  220. if ( tag->selfclose ) {
  221. ret = f.t->next;
  222. f.t->next = 0;
  223. return ret;
  224. }
  225. while ( (c = flogetc(&f)) != EOF ) {
  226. if ( c == '<' ) {
  227. /* tag? */
  228. c = flogetc(&f);
  229. if ( c == '!' ) { /* comment? */
  230. if ( flogetc(&f) == '-' && flogetc(&f) == '-' ) {
  231. /* yes */
  232. while ( (c = flogetc(&f)) != EOF ) {
  233. if ( c == '-' && flogetc(&f) == '-'
  234. && flogetc(&f) == '>')
  235. /* consumed whole comment */
  236. break;
  237. }
  238. }
  239. }
  240. else {
  241. if ( closing = (c == '/') ) c = flogetc(&f);
  242. for ( i=0; i < tag->size; c=flogetc(&f) ) {
  243. if ( tag->id[i++] != toupper(c) )
  244. break;
  245. }
  246. if ( (i == tag->size) && !isalnum(c) ) {
  247. depth = depth + (closing ? -1 : 1);
  248. if ( depth == 0 ) {
  249. while ( c != EOF && c != '>' ) {
  250. /* consume trailing gunk in close tag */
  251. c = flogetc(&f);
  252. }
  253. if ( c == EOF )
  254. break;
  255. if ( !f.t )
  256. return 0;
  257. splitline(f.t, floindex(f));
  258. ret = f.t->next;
  259. f.t->next = 0;
  260. return ret;
  261. }
  262. }
  263. }
  264. }
  265. }
  266. *unclosed = 1;
  267. return 0;
  268. }
  269. /* footnotes look like ^<whitespace>{0,3}[stuff]: <content>$
  270. */
  271. static int
  272. isfootnote(Line *t)
  273. {
  274. int i;
  275. if ( ( (i = t->dle) > 3) || (T(t->text)[i] != '[') )
  276. return 0;
  277. for ( ++i; i < S(t->text) ; ++i ) {
  278. if ( T(t->text)[i] == '[' )
  279. return 0;
  280. else if ( T(t->text)[i] == ']' )
  281. return ( T(t->text)[i+1] == ':' ) ;
  282. }
  283. return 0;
  284. }
  285. static inline int
  286. isquote(Line *t)
  287. {
  288. return (t->dle < 4 && T(t->text)[t->dle] == '>');
  289. }
  290. static inline int
  291. iscode(Line *t)
  292. {
  293. return (t->dle >= 4);
  294. }
  295. static inline int
  296. ishr(Line *t)
  297. {
  298. if ( ! (t->flags & CHECKED) )
  299. checkline(t);
  300. if ( t->count > 2 )
  301. return t->kind == chk_hr || t->kind == chk_dash || t->kind == chk_equal;
  302. return 0;
  303. }
  304. static int
  305. issetext(Line *t, int *htyp)
  306. {
  307. Line *n;
  308. /* check for setext-style HEADER
  309. * ======
  310. */
  311. if ( (n = t->next) ) {
  312. if ( !(n->flags & CHECKED) )
  313. checkline(n);
  314. if ( n->kind == chk_dash || n->kind == chk_equal ) {
  315. *htyp = SETEXT;
  316. return 1;
  317. }
  318. }
  319. return 0;
  320. }
  321. static int
  322. ishdr(Line *t, int *htyp)
  323. {
  324. /* ANY leading `#`'s make this into an ETX header
  325. */
  326. if ( (t->dle == 0) && (S(t->text) > 1) && (T(t->text)[0] == '#') ) {
  327. *htyp = ETX;
  328. return 1;
  329. }
  330. /* And if not, maybe it's a SETEXT header instead
  331. */
  332. return issetext(t, htyp);
  333. }
  334. static inline int
  335. end_of_block(Line *t)
  336. {
  337. int dummy;
  338. if ( !t )
  339. return 0;
  340. return ( (S(t->text) <= t->dle) || ishr(t) || ishdr(t, &dummy) );
  341. }
  342. static Line*
  343. is_discount_dt(Line *t, int *clip)
  344. {
  345. #if USE_DISCOUNT_DL
  346. if ( t && t->next
  347. && (S(t->text) > 2)
  348. && (t->dle == 0)
  349. && (T(t->text)[0] == '=')
  350. && (T(t->text)[S(t->text)-1] == '=') ) {
  351. if ( t->next->dle >= 4 ) {
  352. *clip = 4;
  353. return t;
  354. }
  355. else
  356. return is_discount_dt(t->next, clip);
  357. }
  358. #endif
  359. return 0;
  360. }
  361. static int
  362. is_extra_dd(Line *t)
  363. {
  364. return (t->dle < 4) && (T(t->text)[t->dle] == ':')
  365. && isspace(T(t->text)[t->dle+1]);
  366. }
  367. static Line*
  368. is_extra_dt(Line *t, int *clip)
  369. {
  370. #if USE_EXTRA_DL
  371. if ( t && t->next && S(t->text) && T(t->text)[0] != '='
  372. && T(t->text)[S(t->text)-1] != '=') {
  373. Line *x;
  374. if ( iscode(t) || end_of_block(t) )
  375. return 0;
  376. if ( (x = skipempty(t->next)) && is_extra_dd(x) ) {
  377. *clip = x->dle+2;
  378. return t;
  379. }
  380. if ( x=is_extra_dt(t->next, clip) )
  381. return x;
  382. }
  383. #endif
  384. return 0;
  385. }
  386. static Line*
  387. isdefinition(Line *t, int *clip, int *kind)
  388. {
  389. Line *ret;
  390. *kind = 1;
  391. if ( ret = is_discount_dt(t,clip) )
  392. return ret;
  393. *kind=2;
  394. return is_extra_dt(t,clip);
  395. }
  396. static int
  397. islist(Line *t, int *clip, DWORD flags, int *list_type)
  398. {
  399. int i, j;
  400. char *q;
  401. if ( end_of_block(t) )
  402. return 0;
  403. if ( !(flags & (MKD_NODLIST|MKD_STRICT)) && isdefinition(t,clip,list_type) )
  404. return DL;
  405. if ( strchr("*-+", T(t->text)[t->dle]) && isspace(T(t->text)[t->dle+1]) ) {
  406. i = nextnonblank(t, t->dle+1);
  407. *clip = (i > 4) ? 4 : i;
  408. *list_type = UL;
  409. return AL;
  410. }
  411. if ( (j = nextblank(t,t->dle)) > t->dle ) {
  412. if ( T(t->text)[j-1] == '.' ) {
  413. if ( !(flags & (MKD_NOALPHALIST|MKD_STRICT))
  414. && (j == t->dle + 2)
  415. && isalpha(T(t->text)[t->dle]) ) {
  416. j = nextnonblank(t,j);
  417. *clip = (j > 4) ? 4 : j;
  418. *list_type = AL;
  419. return AL;
  420. }
  421. strtoul(T(t->text)+t->dle, &q, 10);
  422. if ( (q > T(t->text)+t->dle) && (q == T(t->text) + (j-1)) ) {
  423. j = nextnonblank(t,j);
  424. /* *clip = j; */
  425. *clip = (j > 4) ? 4 : j;
  426. *list_type = OL;
  427. return AL;
  428. }
  429. }
  430. }
  431. return 0;
  432. }
  433. static Line *
  434. headerblock(Paragraph *pp, int htyp)
  435. {
  436. Line *ret = 0;
  437. Line *p = pp->text;
  438. int i, j;
  439. switch (htyp) {
  440. case SETEXT:
  441. /* p->text is header, p->next->text is -'s or ='s
  442. */
  443. pp->hnumber = (T(p->next->text)[0] == '=') ? 1 : 2;
  444. ret = p->next->next;
  445. ___mkd_freeLine(p->next);
  446. p->next = 0;
  447. break;
  448. case ETX:
  449. /* p->text is ###header###, so we need to trim off
  450. * the leading and trailing `#`'s
  451. */
  452. for (i=0; (T(p->text)[i] == T(p->text)[0]) && (i < S(p->text)-1)
  453. && (i < 6); i++)
  454. ;
  455. pp->hnumber = i;
  456. while ( (i < S(p->text)) && isspace(T(p->text)[i]) )
  457. ++i;
  458. CLIP(p->text, 0, i);
  459. UNCHECK(p);
  460. for (j=S(p->text); (j > 1) && (T(p->text)[j-1] == '#'); --j)
  461. ;
  462. while ( j && isspace(T(p->text)[j-1]) )
  463. --j;
  464. S(p->text) = j;
  465. ret = p->next;
  466. p->next = 0;
  467. break;
  468. }
  469. return ret;
  470. }
  471. static Line *
  472. codeblock(Paragraph *p)
  473. {
  474. Line *t = p->text, *r;
  475. for ( ; t; t = r ) {
  476. CLIP(t->text,0,4);
  477. t->dle = mkd_firstnonblank(t);
  478. if ( !( (r = skipempty(t->next)) && iscode(r)) ) {
  479. ___mkd_freeLineRange(t,r);
  480. t->next = 0;
  481. return r;
  482. }
  483. }
  484. return t;
  485. }
  486. #ifdef WITH_FENCED_CODE
  487. static int
  488. iscodefence(Line *r, int size, line_type kind)
  489. {
  490. if ( !(r->flags & CHECKED) )
  491. checkline(r);
  492. if ( kind )
  493. return (r->kind == kind) && (r->count >= size);
  494. else
  495. return (r->kind == chk_tilde || r->kind == chk_backtick) && (r->count >= size);
  496. }
  497. static Paragraph *
  498. fencedcodeblock(ParagraphRoot *d, Line **ptr)
  499. {
  500. Line *first, *r;
  501. Paragraph *ret;
  502. first = (*ptr);
  503. /* don't allow zero-length code fences
  504. */
  505. if ( (first->next == 0) || iscodefence(first->next, first->count, 0) )
  506. return 0;
  507. /* find the closing fence, discard the fences,
  508. * return a Paragraph with the contents
  509. */
  510. for ( r = first; r && r->next; r = r->next )
  511. if ( iscodefence(r->next, first->count, first->kind) ) {
  512. (*ptr) = r->next->next;
  513. ret = Pp(d, first->next, CODE);
  514. if (S(first->text) - first->count > 0) {
  515. char *lang_attr = T(first->text) + first->count;
  516. while ( *lang_attr != 0 && *lang_attr == ' ' ) lang_attr++;
  517. ret->lang = strdup(lang_attr);
  518. }
  519. else {
  520. ret->lang = 0;
  521. }
  522. ___mkd_freeLine(first);
  523. ___mkd_freeLine(r->next);
  524. r->next = 0;
  525. return ret;
  526. }
  527. return 0;
  528. }
  529. #endif
  530. static int
  531. centered(Line *first, Line *last)
  532. {
  533. if ( first&&last ) {
  534. int len = S(last->text);
  535. if ( (len > 2) && (strncmp(T(first->text), "->", 2) == 0)
  536. && (strncmp(T(last->text)+len-2, "<-", 2) == 0) ) {
  537. CLIP(first->text, 0, 2);
  538. S(last->text) -= 2;
  539. return CENTER;
  540. }
  541. }
  542. return 0;
  543. }
  544. static int
  545. endoftextblock(Line *t, int toplevelblock, DWORD flags)
  546. {
  547. int z;
  548. if ( end_of_block(t) || isquote(t) )
  549. return 1;
  550. /* HORRIBLE STANDARDS KLUDGES:
  551. * 1. non-toplevel paragraphs absorb adjacent code blocks
  552. * 2. Toplevel paragraphs eat absorb adjacent list items,
  553. * but sublevel blocks behave properly.
  554. * (What this means is that we only need to check for code
  555. * blocks at toplevel, and only check for list items at
  556. * nested levels.)
  557. */
  558. return toplevelblock ? 0 : islist(t,&z,flags,&z);
  559. }
  560. static Line *
  561. textblock(Paragraph *p, int toplevel, DWORD flags)
  562. {
  563. Line *t, *next;
  564. for ( t = p->text; t ; t = next ) {
  565. if ( ((next = t->next) == 0) || endoftextblock(next, toplevel, flags) ) {
  566. p->align = centered(p->text, t);
  567. t->next = 0;
  568. return next;
  569. }
  570. }
  571. return t;
  572. }
  573. /* length of the id: or class: kind in a special div-not-quote block
  574. */
  575. static int
  576. szmarkerclass(char *p)
  577. {
  578. if ( strncasecmp(p, "id:", 3) == 0 )
  579. return 3;
  580. if ( strncasecmp(p, "class:", 6) == 0 )
  581. return 6;
  582. return 0;
  583. }
  584. /*
  585. * check if the first line of a quoted block is the special div-not-quote
  586. * marker %[kind:]name%
  587. */
  588. #define iscsschar(c) (isalpha(c) || (c == '-') || (c == '_') )
  589. static int
  590. isdivmarker(Line *p, int start, DWORD flags)
  591. {
  592. char *s;
  593. int last, i;
  594. if ( flags & (MKD_NODIVQUOTE|MKD_STRICT) )
  595. return 0;
  596. start = nextnonblank(p, start);
  597. last= S(p->text) - (1 + start);
  598. s = T(p->text) + start;
  599. if ( (last <= 0) || (*s != '%') || (s[last] != '%') )
  600. return 0;
  601. i = szmarkerclass(s+1);
  602. if ( !iscsschar(s[i+1]) )
  603. return 0;
  604. while ( ++i < last )
  605. if ( !(isdigit(s[i]) || iscsschar(s[i])) )
  606. return 0;
  607. return 1;
  608. }
  609. /*
  610. * accumulate a blockquote.
  611. *
  612. * one sick horrible thing about blockquotes is that even though
  613. * it just takes ^> to start a quote, following lines, if quoted,
  614. * assume that the prefix is ``> ''. This means that code needs
  615. * to be indented *5* spaces from the leading '>', but *4* spaces
  616. * from the start of the line. This does not appear to be
  617. * documented in the reference implementation, but it's the
  618. * way the markdown sample web form at Daring Fireball works.
  619. */
  620. static Line *
  621. quoteblock(Paragraph *p, DWORD flags)
  622. {
  623. Line *t, *q;
  624. int qp;
  625. for ( t = p->text; t ; t = q ) {
  626. if ( isquote(t) ) {
  627. /* clip leading spaces */
  628. for (qp = 0; T(t->text)[qp] != '>'; qp ++)
  629. /* assert: the first nonblank character on this line
  630. * will be a >
  631. */;
  632. /* clip '>' */
  633. qp++;
  634. /* clip next space, if any */
  635. if ( T(t->text)[qp] == ' ' )
  636. qp++;
  637. CLIP(t->text, 0, qp);
  638. UNCHECK(t);
  639. t->dle = mkd_firstnonblank(t);
  640. }
  641. q = skipempty(t->next);
  642. if ( (q == 0) || ((q != t->next) && (!isquote(q) || isdivmarker(q,1,flags))) ) {
  643. ___mkd_freeLineRange(t, q);
  644. t = q;
  645. break;
  646. }
  647. }
  648. if ( isdivmarker(p->text,0,flags) ) {
  649. char *prefix = "class";
  650. int i;
  651. q = p->text;
  652. p->text = p->text->next;
  653. if ( (i = szmarkerclass(1+T(q->text))) == 3 )
  654. /* and this would be an "%id:" prefix */
  655. prefix="id";
  656. if ( p->ident = malloc(4+strlen(prefix)+S(q->text)) )
  657. sprintf(p->ident, "%s=\"%.*s\"", prefix, S(q->text)-(i+2),
  658. T(q->text)+(i+1) );
  659. ___mkd_freeLine(q);
  660. }
  661. return t;
  662. }
  663. typedef int (*linefn)(Line *);
  664. /*
  665. * pull in a list block. A list block starts with a list marker and
  666. * runs until the next list marker, the next non-indented paragraph,
  667. * or EOF. You do not have to indent nonblank lines after the list
  668. * marker, but multiple paragraphs need to start with a 4-space indent.
  669. */
  670. static Line *
  671. listitem(Paragraph *p, int indent, DWORD flags, linefn check)
  672. {
  673. Line *t, *q;
  674. int clip = indent;
  675. int z;
  676. for ( t = p->text; t ; t = q) {
  677. CLIP(t->text, 0, clip);
  678. UNCHECK(t);
  679. t->dle = mkd_firstnonblank(t);
  680. if ( (q = skipempty(t->next)) == 0 ) {
  681. ___mkd_freeLineRange(t,q);
  682. return 0;
  683. }
  684. /* after a blank line, the next block needs to start with a line
  685. * that's indented 4(? -- reference implementation allows a 1
  686. * character indent, but that has unfortunate side effects here)
  687. * spaces, but after that the line doesn't need any indentation
  688. */
  689. if ( q != t->next ) {
  690. if (q->dle < indent) {
  691. q = t->next;
  692. t->next = 0;
  693. return q;
  694. }
  695. /* indent at least 2, and at most as
  696. * as far as the initial line was indented. */
  697. indent = clip ? clip : 2;
  698. }
  699. if ( (q->dle < indent) && (ishr(q) || islist(q,&z,flags,&z)
  700. || (check && (*check)(q)))
  701. && !issetext(q,&z) ) {
  702. q = t->next;
  703. t->next = 0;
  704. return q;
  705. }
  706. clip = (q->dle > indent) ? indent : q->dle;
  707. }
  708. return t;
  709. }
  710. static Line *
  711. definition_block(Paragraph *top, int clip, MMIOT *f, int kind)
  712. {
  713. ParagraphRoot d = { 0, 0 };
  714. Paragraph *p;
  715. Line *q = top->text, *text = 0, *labels;
  716. int z, para;
  717. while (( labels = q )) {
  718. if ( (q = isdefinition(labels, &z, &kind)) == 0 )
  719. break;
  720. if ( (text = skipempty(q->next)) == 0 )
  721. break;
  722. if ( para = (text != q->next) )
  723. ___mkd_freeLineRange(q, text);
  724. q->next = 0;
  725. if ( kind == 1 /* discount dl */ )
  726. for ( q = labels; q; q = q->next ) {
  727. CLIP(q->text, 0, 1);
  728. UNCHECK(q);
  729. S(q->text)--;
  730. }
  731. dd_block:
  732. p = Pp(&d, text, LISTITEM);
  733. text = listitem(p, clip, f->flags, (kind==2) ? is_extra_dd : 0);
  734. p->down = compile(p->text, 0, f);
  735. p->text = labels; labels = 0;
  736. if ( para && p->down ) p->down->align = PARA;
  737. if ( (q = skipempty(text)) == 0 )
  738. break;
  739. if ( para = (q != text) ) {
  740. Line anchor;
  741. anchor.next = text;
  742. ___mkd_freeLineRange(&anchor,q);
  743. text = q;
  744. }
  745. if ( kind == 2 && is_extra_dd(q) )
  746. goto dd_block;
  747. }
  748. top->text = 0;
  749. top->down = T(d);
  750. return text;
  751. }
  752. static Line *
  753. enumerated_block(Paragraph *top, int clip, MMIOT *f, int list_class)
  754. {
  755. ParagraphRoot d = { 0, 0 };
  756. Paragraph *p;
  757. Line *q = top->text, *text;
  758. int para = 0, z;
  759. while (( text = q )) {
  760. p = Pp(&d, text, LISTITEM);
  761. text = listitem(p, clip, f->flags, 0);
  762. p->down = compile(p->text, 0, f);
  763. p->text = 0;
  764. if ( para && p->down ) p->down->align = PARA;
  765. if ( (q = skipempty(text)) == 0
  766. || islist(q, &clip, f->flags, &z) != list_class )
  767. break;
  768. if ( para = (q != text) ) {
  769. Line anchor;
  770. anchor.next = text;
  771. ___mkd_freeLineRange(&anchor, q);
  772. if ( p->down ) p->down->align = PARA;
  773. }
  774. }
  775. top->text = 0;
  776. top->down = T(d);
  777. return text;
  778. }
  779. static int
  780. tgood(char c)
  781. {
  782. switch (c) {
  783. case '\'':
  784. case '"': return c;
  785. case '(': return ')';
  786. }
  787. return 0;
  788. }
  789. /*
  790. * add a new (image or link) footnote to the footnote table
  791. */
  792. static Line*
  793. addfootnote(Line *p, MMIOT* f)
  794. {
  795. int j, i;
  796. int c;
  797. Line *np = p->next;
  798. Footnote *foot = &EXPAND(*f->footnotes);
  799. CREATE(foot->tag);
  800. CREATE(foot->link);
  801. CREATE(foot->title);
  802. foot->flags = foot->height = foot->width = 0;
  803. for (j=i=p->dle+1; T(p->text)[j] != ']'; j++)
  804. EXPAND(foot->tag) = T(p->text)[j];
  805. EXPAND(foot->tag) = 0;
  806. S(foot->tag)--;
  807. j = nextnonblank(p, j+2);
  808. if ( (f->flags & MKD_EXTRA_FOOTNOTE) && (T(foot->tag)[0] == '^') ) {
  809. while ( j < S(p->text) )
  810. EXPAND(foot->title) = T(p->text)[j++];
  811. goto skip_to_end;
  812. }
  813. while ( (j < S(p->text)) && !isspace(T(p->text)[j]) )
  814. EXPAND(foot->link) = T(p->text)[j++];
  815. EXPAND(foot->link) = 0;
  816. S(foot->link)--;
  817. j = nextnonblank(p,j);
  818. if ( T(p->text)[j] == '=' ) {
  819. sscanf(T(p->text)+j, "=%dx%d", &foot->width, &foot->height);
  820. while ( (j < S(p->text)) && !isspace(T(p->text)[j]) )
  821. ++j;
  822. j = nextnonblank(p,j);
  823. }
  824. if ( (j >= S(p->text)) && np && np->dle && tgood(T(np->text)[np->dle]) ) {
  825. ___mkd_freeLine(p);
  826. p = np;
  827. np = p->next;
  828. j = p->dle;
  829. }
  830. if ( (c = tgood(T(p->text)[j])) ) {
  831. /* Try to take the rest of the line as a comment; read to
  832. * EOL, then shrink the string back to before the final
  833. * quote.
  834. */
  835. ++j; /* skip leading quote */
  836. while ( j < S(p->text) )
  837. EXPAND(foot->title) = T(p->text)[j++];
  838. while ( S(foot->title) && T(foot->title)[S(foot->title)-1] != c )
  839. --S(foot->title);
  840. if ( S(foot->title) ) /* skip trailing quote */
  841. --S(foot->title);
  842. EXPAND(foot->title) = 0;
  843. --S(foot->title);
  844. }
  845. skip_to_end:
  846. ___mkd_freeLine(p);
  847. return np;
  848. }
  849. /*
  850. * allocate a paragraph header, link it to the
  851. * tail of the current document
  852. */
  853. static Paragraph *
  854. Pp(ParagraphRoot *d, Line *ptr, int typ)
  855. {
  856. Paragraph *ret = calloc(sizeof *ret, 1);
  857. ret->text = ptr;
  858. ret->typ = typ;
  859. return ATTACH(*d, ret);
  860. }
  861. static Line*
  862. consume(Line *ptr, int *eaten)
  863. {
  864. Line *next;
  865. int blanks=0;
  866. for (; ptr && blankline(ptr); ptr = next, blanks++ ) {
  867. next = ptr->next;
  868. ___mkd_freeLine(ptr);
  869. }
  870. if ( ptr ) *eaten = blanks;
  871. return ptr;
  872. }
  873. /*
  874. * top-level compilation; break the document into
  875. * style, html, and source blocks with footnote links
  876. * weeded out.
  877. */
  878. static Paragraph *
  879. compile_document(Line *ptr, MMIOT *f)
  880. {
  881. ParagraphRoot d = { 0, 0 };
  882. ANCHOR(Line) source = { 0, 0 };
  883. Paragraph *p = 0;
  884. struct kw *tag;
  885. int eaten, unclosed;
  886. while ( ptr ) {
  887. if ( !(f->flags & MKD_NOHTML) && (tag = isopentag(ptr)) ) {
  888. int blocktype;
  889. /* If we encounter a html/style block, compile and save all
  890. * of the cached source BEFORE processing the html/style.
  891. */
  892. if ( T(source) ) {
  893. E(source)->next = 0;
  894. p = Pp(&d, 0, SOURCE);
  895. p->down = compile(T(source), 1, f);
  896. T(source) = E(source) = 0;
  897. }
  898. if ( f->flags & MKD_NOSTYLE )
  899. blocktype = HTML;
  900. else
  901. blocktype = strcmp(tag->id, "STYLE") == 0 ? STYLE : HTML;
  902. p = Pp(&d, ptr, blocktype);
  903. ptr = htmlblock(p, tag, &unclosed);
  904. if ( unclosed ) {
  905. p->typ = SOURCE;
  906. p->down = compile(p->text, 1, f);
  907. p->text = 0;
  908. }
  909. }
  910. else if ( isfootnote(ptr) ) {
  911. /* footnotes, like cats, sleep anywhere; pull them
  912. * out of the input stream and file them away for
  913. * later processing
  914. */
  915. ptr = consume(addfootnote(ptr, f), &eaten);
  916. }
  917. else {
  918. /* source; cache it up to wait for eof or the
  919. * next html/style block
  920. */
  921. ATTACH(source,ptr);
  922. ptr = ptr->next;
  923. }
  924. }
  925. if ( T(source) ) {
  926. /* if there's any cached source at EOF, compile
  927. * it now.
  928. */
  929. E(source)->next = 0;
  930. p = Pp(&d, 0, SOURCE);
  931. p->down = compile(T(source), 1, f);
  932. }
  933. return T(d);
  934. }
  935. static int
  936. first_nonblank_before(Line *j, int dle)
  937. {
  938. return (j->dle < dle) ? j->dle : dle;
  939. }
  940. static int
  941. actually_a_table(MMIOT *f, Line *pp)
  942. {
  943. Line *r;
  944. int j;
  945. int c;
  946. /* tables need to be turned on */
  947. if ( f->flags & (MKD_STRICT|MKD_NOTABLES) )
  948. return 0;
  949. /* tables need three lines */
  950. if ( !(pp && pp->next && pp->next->next) ) {
  951. return 0;
  952. }
  953. /* all lines must contain |'s */
  954. for (r = pp; r; r = r->next )
  955. if ( !(r->flags & PIPECHAR) ) {
  956. return 0;
  957. }
  958. /* if the header has a leading |, all lines must have leading |'s */
  959. if ( T(pp->text)[pp->dle] == '|' ) {
  960. for ( r = pp; r; r = r->next )
  961. if ( T(r->text)[first_nonblank_before(r,pp->dle)] != '|' ) {
  962. return 0;
  963. }
  964. }
  965. /* second line must be only whitespace, -, |, or : */
  966. r = pp->next;
  967. for ( j=r->dle; j < S(r->text); ++j ) {
  968. c = T(r->text)[j];
  969. if ( !(isspace(c)||(c=='-')||(c==':')||(c=='|')) ) {
  970. return 0;
  971. }
  972. }
  973. return 1;
  974. }
  975. /*
  976. * break a collection of markdown input into
  977. * blocks of lists, code, html, and text to
  978. * be marked up.
  979. */
  980. static Paragraph *
  981. compile(Line *ptr, int toplevel, MMIOT *f)
  982. {
  983. ParagraphRoot d = { 0, 0 };
  984. Paragraph *p = 0;
  985. Line *r;
  986. int para = toplevel;
  987. int blocks = 0;
  988. int hdr_type, list_type, list_class, indent;
  989. ptr = consume(ptr, &para);
  990. while ( ptr ) {
  991. if ( iscode(ptr) ) {
  992. p = Pp(&d, ptr, CODE);
  993. if ( f->flags & MKD_1_COMPAT) {
  994. /* HORRIBLE STANDARDS KLUDGE: the first line of every block
  995. * has trailing whitespace trimmed off.
  996. */
  997. ___mkd_tidy(&p->text->text);
  998. }
  999. ptr = codeblock(p);
  1000. }
  1001. #if WITH_FENCED_CODE
  1002. else if ( iscodefence(ptr,3,0) && (p=fencedcodeblock(&d, &ptr)) )
  1003. /* yay, it's already done */ ;
  1004. #endif
  1005. else if ( ishr(ptr) ) {
  1006. p = Pp(&d, 0, HR);
  1007. r = ptr;
  1008. ptr = ptr->next;
  1009. ___mkd_freeLine(r);
  1010. }
  1011. else if ( list_class = islist(ptr, &indent, f->flags, &list_type) ) {
  1012. if ( list_class == DL ) {
  1013. p = Pp(&d, ptr, DL);
  1014. ptr = definition_block(p, indent, f, list_type);
  1015. }
  1016. else {
  1017. p = Pp(&d, ptr, list_type);
  1018. ptr = enumerated_block(p, indent, f, list_class);
  1019. }
  1020. }
  1021. else if ( isquote(ptr) ) {
  1022. p = Pp(&d, ptr, QUOTE);
  1023. ptr = quoteblock(p, f->flags);
  1024. p->down = compile(p->text, 1, f);
  1025. p->text = 0;
  1026. }
  1027. else if ( ishdr(ptr, &hdr_type) ) {
  1028. p = Pp(&d, ptr, HDR);
  1029. ptr = headerblock(p, hdr_type);
  1030. }
  1031. else {
  1032. p = Pp(&d, ptr, MARKUP);
  1033. ptr = textblock(p, toplevel, f->flags);
  1034. /* tables are a special kind of paragraph */
  1035. if ( actually_a_table(f, p->text) )
  1036. p->typ = TABLE;
  1037. }
  1038. if ( (para||toplevel) && !p->align )
  1039. p->align = PARA;
  1040. blocks++;
  1041. para = toplevel || (blocks > 1);
  1042. ptr = consume(ptr, &para);
  1043. if ( para && !p->align )
  1044. p->align = PARA;
  1045. }
  1046. return T(d);
  1047. }
  1048. /*
  1049. * the guts of the markdown() function, ripped out so I can do
  1050. * debugging.
  1051. */
  1052. /*
  1053. * prepare and compile `text`, returning a Paragraph tree.
  1054. */
  1055. int
  1056. mkd_compile(Document *doc, DWORD flags)
  1057. {
  1058. if ( !doc )
  1059. return 0;
  1060. if ( doc->compiled )
  1061. return 1;
  1062. doc->compiled = 1;
  1063. memset(doc->ctx, 0, sizeof(MMIOT) );
  1064. doc->ctx->ref_prefix= doc->ref_prefix;
  1065. doc->ctx->cb = &(doc->cb);
  1066. doc->ctx->flags = flags & USER_FLAGS;
  1067. CREATE(doc->ctx->in);
  1068. doc->ctx->footnotes = malloc(sizeof doc->ctx->footnotes[0]);
  1069. CREATE(*doc->ctx->footnotes);
  1070. mkd_initialize();
  1071. doc->code = compile_document(T(doc->content), doc->ctx);
  1072. qsort(T(*doc->ctx->footnotes), S(*doc->ctx->footnotes),
  1073. sizeof T(*doc->ctx->footnotes)[0],
  1074. (stfu)__mkd_footsort);
  1075. memset(&doc->content, 0, sizeof doc->content);
  1076. return 1;
  1077. }