rdiscount_wm 3.0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ext/generate.c ADDED
@@ -0,0 +1,1809 @@
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 <stdio.h>
8
+ #include <string.h>
9
+ #include <stdarg.h>
10
+ #include <stdlib.h>
11
+ #include <time.h>
12
+ #include <ctype.h>
13
+
14
+ #include "config.h"
15
+
16
+ #include "cstring.h"
17
+ #include "markdown.h"
18
+ #include "amalloc.h"
19
+
20
+ typedef int (*stfu)(const void*,const void*);
21
+ typedef void (*spanhandler)(MMIOT*,int);
22
+
23
+ /* forward declarations */
24
+ static void text(MMIOT *f);
25
+ static Paragraph *display(Paragraph*, MMIOT*);
26
+
27
+ /* externals from markdown.c */
28
+ int __mkd_footsort(Footnote *, Footnote *);
29
+
30
+ /*
31
+ * push text into the generator input buffer
32
+ */
33
+ static void
34
+ push(char *bfr, int size, MMIOT *f)
35
+ {
36
+ while ( size-- > 0 )
37
+ EXPAND(f->in) = *bfr++;
38
+ }
39
+
40
+
41
+ /* look <i> characters ahead of the cursor.
42
+ */
43
+ static inline int
44
+ peek(MMIOT *f, int i)
45
+ {
46
+
47
+ i += (f->isp-1);
48
+
49
+ return (i >= 0) && (i < S(f->in)) ? T(f->in)[i] : EOF;
50
+ }
51
+
52
+
53
+ /* pull a byte from the input buffer
54
+ */
55
+ static inline int
56
+ pull(MMIOT *f)
57
+ {
58
+ return ( f->isp < S(f->in) ) ? T(f->in)[f->isp++] : EOF;
59
+ }
60
+
61
+
62
+ /* return a pointer to the current position in the input buffer.
63
+ */
64
+ static inline char*
65
+ cursor(MMIOT *f)
66
+ {
67
+ return T(f->in) + f->isp;
68
+ }
69
+
70
+
71
+ static inline int
72
+ isthisspace(MMIOT *f, int i)
73
+ {
74
+ int c = peek(f, i);
75
+
76
+ if ( c == EOF )
77
+ return 1;
78
+ if ( c & 0x80 )
79
+ return 0;
80
+ return isspace(c) || (c < ' ');
81
+ }
82
+
83
+
84
+ static inline int
85
+ isthisalnum(MMIOT *f, int i)
86
+ {
87
+ int c = peek(f, i);
88
+
89
+ return (c != EOF) && isalnum(c);
90
+ }
91
+
92
+
93
+ static inline int
94
+ isthisnonword(MMIOT *f, int i)
95
+ {
96
+ return isthisspace(f, i) || ispunct(peek(f,i));
97
+ }
98
+
99
+
100
+ /* return/set the current cursor position
101
+ */
102
+ #define mmiotseek(f,x) (f->isp = x)
103
+ #define mmiottell(f) (f->isp)
104
+
105
+
106
+ /* move n characters forward ( or -n characters backward) in the input buffer.
107
+ */
108
+ static void
109
+ shift(MMIOT *f, int i)
110
+ {
111
+ if (f->isp + i >= 0 )
112
+ f->isp += i;
113
+ }
114
+
115
+
116
+ /* Qchar()
117
+ */
118
+ static void
119
+ Qchar(int c, MMIOT *f)
120
+ {
121
+ block *cur;
122
+
123
+ if ( S(f->Q) == 0 ) {
124
+ cur = &EXPAND(f->Q);
125
+ memset(cur, 0, sizeof *cur);
126
+ cur->b_type = bTEXT;
127
+ }
128
+ else
129
+ cur = &T(f->Q)[S(f->Q)-1];
130
+
131
+ EXPAND(cur->b_text) = c;
132
+
133
+ }
134
+
135
+
136
+ /* Qstring()
137
+ */
138
+ static void
139
+ Qstring(char *s, MMIOT *f)
140
+ {
141
+ while (*s)
142
+ Qchar(*s++, f);
143
+ }
144
+
145
+
146
+ /* Qwrite()
147
+ */
148
+ static void
149
+ Qwrite(char *s, int size, MMIOT *f)
150
+ {
151
+ while (size-- > 0)
152
+ Qchar(*s++, f);
153
+ }
154
+
155
+
156
+ /* Qprintf()
157
+ */
158
+ static void
159
+ Qprintf(MMIOT *f, char *fmt, ...)
160
+ {
161
+ char bfr[80];
162
+ va_list ptr;
163
+
164
+ va_start(ptr,fmt);
165
+ vsnprintf(bfr, sizeof bfr, fmt, ptr);
166
+ va_end(ptr);
167
+ Qstring(bfr, f);
168
+ }
169
+
170
+
171
+ /* Qem()
172
+ */
173
+ static void
174
+ Qem(MMIOT *f, char c, int count)
175
+ {
176
+ block *p = &EXPAND(f->Q);
177
+
178
+ memset(p, 0, sizeof *p);
179
+ p->b_type = (c == '*') ? bSTAR : bUNDER;
180
+ p->b_char = c;
181
+ p->b_count = count;
182
+
183
+ memset(&EXPAND(f->Q), 0, sizeof(block));
184
+ }
185
+
186
+
187
+ /* generate html from a markup fragment
188
+ */
189
+ void
190
+ ___mkd_reparse(char *bfr, int size, int flags, MMIOT *f, char *esc)
191
+ {
192
+ MMIOT sub;
193
+ struct escaped e;
194
+
195
+ ___mkd_initmmiot(&sub, f->footnotes);
196
+
197
+ sub.flags = f->flags | flags;
198
+ sub.cb = f->cb;
199
+ sub.ref_prefix = f->ref_prefix;
200
+
201
+ if ( esc ) {
202
+ sub.esc = &e;
203
+ e.up = f->esc;
204
+ e.text = esc;
205
+ }
206
+ else
207
+ sub.esc = f->esc;
208
+
209
+ push(bfr, size, &sub);
210
+ EXPAND(sub.in) = 0;
211
+ S(sub.in)--;
212
+
213
+ text(&sub);
214
+ ___mkd_emblock(&sub);
215
+
216
+ Qwrite(T(sub.out), S(sub.out), f);
217
+
218
+ ___mkd_freemmiot(&sub, f->footnotes);
219
+ }
220
+
221
+
222
+ /*
223
+ * check the escape list for special cases
224
+ */
225
+ static int
226
+ escaped(MMIOT *f, char c)
227
+ {
228
+ struct escaped *thing = f->esc;
229
+
230
+ while ( thing ) {
231
+ if ( strchr(thing->text, c) )
232
+ return 1;
233
+ thing = thing->up;
234
+ }
235
+ return 0;
236
+ }
237
+
238
+
239
+ /*
240
+ * write out a url, escaping problematic characters
241
+ */
242
+ static void
243
+ puturl(char *s, int size, MMIOT *f, int display)
244
+ {
245
+ unsigned char c;
246
+
247
+ while ( size-- > 0 ) {
248
+ c = *s++;
249
+
250
+ if ( c == '\\' && size-- > 0 ) {
251
+ c = *s++;
252
+
253
+ if ( !( ispunct(c) || isspace(c) ) )
254
+ Qchar('\\', f);
255
+ }
256
+
257
+ if ( c == '&' )
258
+ Qstring("&amp;", f);
259
+ else if ( c == '<' )
260
+ Qstring("&lt;", f);
261
+ else if ( c == '"' )
262
+ Qstring("%22", f);
263
+ else if ( isalnum(c) || ispunct(c) || (display && isspace(c)) )
264
+ Qchar(c, f);
265
+ else if ( c == 003 ) /* untokenize ^C */
266
+ Qstring(" ", f);
267
+ else
268
+ Qprintf(f, "%%%02X", c);
269
+ }
270
+ }
271
+
272
+
273
+ /* advance forward until the next character is not whitespace
274
+ */
275
+ static int
276
+ eatspace(MMIOT *f)
277
+ {
278
+ int c;
279
+
280
+ for ( ; ((c=peek(f, 1)) != EOF) && isspace(c); pull(f) )
281
+ ;
282
+ return c;
283
+ }
284
+
285
+
286
+ /* (match (a (nested (parenthetical (string.)))))
287
+ */
288
+ static int
289
+ parenthetical(int in, int out, MMIOT *f)
290
+ {
291
+ int size, indent, c;
292
+
293
+ for ( indent=1,size=0; indent; size++ ) {
294
+ if ( (c = pull(f)) == EOF )
295
+ return EOF;
296
+ else if ( (c == '\\') && (peek(f,1) == out || peek(f,1) == in) ) {
297
+ ++size;
298
+ pull(f);
299
+ }
300
+ else if ( c == in )
301
+ ++indent;
302
+ else if ( c == out )
303
+ --indent;
304
+ }
305
+ return size ? (size-1) : 0;
306
+ }
307
+
308
+
309
+ /* extract a []-delimited label from the input stream.
310
+ */
311
+ static int
312
+ linkylabel(MMIOT *f, Cstring *res)
313
+ {
314
+ char *ptr = cursor(f);
315
+ int size;
316
+
317
+ if ( (size = parenthetical('[',']',f)) != EOF ) {
318
+ T(*res) = ptr;
319
+ S(*res) = size;
320
+ return 1;
321
+ }
322
+ return 0;
323
+ }
324
+
325
+
326
+ /* see if the quote-prefixed linky segment is actually a title.
327
+ */
328
+ static int
329
+ linkytitle(MMIOT *f, char quote, Footnote *ref)
330
+ {
331
+ int whence = mmiottell(f);
332
+ char *title = cursor(f);
333
+ char *e;
334
+ register int c;
335
+
336
+ while ( (c = pull(f)) != EOF ) {
337
+ e = cursor(f);
338
+ if ( c == quote ) {
339
+ if ( (c = eatspace(f)) == ')' ) {
340
+ T(ref->title) = 1+title;
341
+ S(ref->title) = (e-title)-2;
342
+ return 1;
343
+ }
344
+ }
345
+ }
346
+ mmiotseek(f, whence);
347
+ return 0;
348
+ }
349
+
350
+
351
+ /* extract a =HHHxWWW size from the input stream
352
+ */
353
+ static int
354
+ linkysize(MMIOT *f, Footnote *ref)
355
+ {
356
+ int height=0, width=0;
357
+ int whence = mmiottell(f);
358
+ int c;
359
+
360
+ if ( isspace(peek(f,0)) ) {
361
+ pull(f); /* eat '=' */
362
+
363
+ for ( c = pull(f); isdigit(c); c = pull(f))
364
+ width = (width * 10) + (c - '0');
365
+
366
+ if ( c == 'x' ) {
367
+ for ( c = pull(f); isdigit(c); c = pull(f))
368
+ height = (height*10) + (c - '0');
369
+
370
+ if ( isspace(c) )
371
+ c = eatspace(f);
372
+
373
+ if ( (c == ')') || ((c == '\'' || c == '"') && linkytitle(f, c, ref)) ) {
374
+ ref->height = height;
375
+ ref->width = width;
376
+ return 1;
377
+ }
378
+ }
379
+ }
380
+ mmiotseek(f, whence);
381
+ return 0;
382
+ }
383
+
384
+
385
+ /* extract a <...>-encased url from the input stream.
386
+ * (markdown 1.0.2b8 compatibility; older versions
387
+ * of markdown treated the < and > as syntactic
388
+ * sugar that didn't have to be there. 1.0.2b8
389
+ * requires a closing >, and then falls into the
390
+ * title or closing )
391
+ */
392
+ static int
393
+ linkybroket(MMIOT *f, int image, Footnote *p)
394
+ {
395
+ int c;
396
+ int good = 0;
397
+
398
+ T(p->link) = cursor(f);
399
+ for ( S(p->link)=0; (c = pull(f)) != '>'; ++S(p->link) ) {
400
+ /* pull in all input until a '>' is found, or die trying.
401
+ */
402
+ if ( c == EOF )
403
+ return 0;
404
+ else if ( (c == '\\') && ispunct(peek(f,2)) ) {
405
+ ++S(p->link);
406
+ pull(f);
407
+ }
408
+ }
409
+
410
+ c = eatspace(f);
411
+
412
+ /* next nonspace needs to be a title, a size, or )
413
+ */
414
+ if ( ( c == '\'' || c == '"' ) && linkytitle(f,c,p) )
415
+ good=1;
416
+ else if ( image && (c == '=') && linkysize(f,p) )
417
+ good=1;
418
+ else
419
+ good=( c == ')' );
420
+
421
+ if ( good ) {
422
+ if ( peek(f, 1) == ')' )
423
+ pull(f);
424
+
425
+ ___mkd_tidy(&p->link);
426
+ }
427
+
428
+ return good;
429
+ } /* linkybroket */
430
+
431
+
432
+ /* extract a (-prefixed url from the input stream.
433
+ * the label is either of the format `<link>`, where I
434
+ * extract until I find a >, or it is of the format
435
+ * `text`, where I extract until I reach a ')', a quote,
436
+ * or (if image) a '='
437
+ */
438
+ static int
439
+ linkyurl(MMIOT *f, int image, Footnote *p)
440
+ {
441
+ int c;
442
+ int mayneedtotrim=0;
443
+
444
+ if ( (c = eatspace(f)) == EOF )
445
+ return 0;
446
+
447
+ if ( c == '<' ) {
448
+ pull(f);
449
+ if ( !(f->flags & MKD_1_COMPAT) )
450
+ return linkybroket(f,image,p);
451
+ mayneedtotrim=1;
452
+ }
453
+
454
+ T(p->link) = cursor(f);
455
+ for ( S(p->link)=0; (c = peek(f,1)) != ')'; ++S(p->link) ) {
456
+ if ( c == EOF )
457
+ return 0;
458
+ else if ( (c == '"' || c == '\'') && linkytitle(f, c, p) )
459
+ break;
460
+ else if ( image && (c == '=') && linkysize(f, p) )
461
+ break;
462
+ else if ( (c == '\\') && ispunct(peek(f,2)) ) {
463
+ ++S(p->link);
464
+ pull(f);
465
+ }
466
+ pull(f);
467
+ }
468
+ if ( peek(f, 1) == ')' )
469
+ pull(f);
470
+
471
+ ___mkd_tidy(&p->link);
472
+
473
+ if ( mayneedtotrim && (T(p->link)[S(p->link)-1] == '>') )
474
+ --S(p->link);
475
+
476
+ return 1;
477
+ }
478
+
479
+
480
+
481
+ /* prefixes for <automatic links>
482
+ */
483
+ static struct _protocol {
484
+ char *name;
485
+ int nlen;
486
+ } protocol[] = {
487
+ #define _aprotocol(x) { x, (sizeof x)-1 }
488
+ _aprotocol( "https:" ),
489
+ _aprotocol( "http:" ),
490
+ _aprotocol( "news:" ),
491
+ _aprotocol( "ftp:" ),
492
+ #undef _aprotocol
493
+ };
494
+ #define NRPROTOCOLS (sizeof protocol / sizeof protocol[0])
495
+
496
+
497
+ static int
498
+ isautoprefix(char *text, int size)
499
+ {
500
+ int i;
501
+ struct _protocol *p;
502
+
503
+ for (i=0, p=protocol; i < NRPROTOCOLS; i++, p++)
504
+ if ( (size >= p->nlen) && strncasecmp(text, p->name, p->nlen) == 0 )
505
+ return 1;
506
+ return 0;
507
+ }
508
+
509
+
510
+ /*
511
+ * all the tag types that linkylinky can produce are
512
+ * defined by this structure.
513
+ */
514
+ typedef struct linkytype {
515
+ char *pat;
516
+ int szpat;
517
+ char *link_pfx; /* tag prefix and link pointer (eg: "<a href="\"" */
518
+ char *link_sfx; /* link suffix (eg: "\"" */
519
+ int WxH; /* this tag allows width x height arguments */
520
+ char *text_pfx; /* text prefix (eg: ">" */
521
+ char *text_sfx; /* text suffix (eg: "</a>" */
522
+ int flags; /* reparse flags */
523
+ int kind; /* tag is url or something else? */
524
+ #define IS_URL 0x01
525
+ } linkytype;
526
+
527
+ static linkytype imaget = { 0, 0, "<img src=\"", "\"",
528
+ 1, " alt=\"", "\" />", MKD_NOIMAGE|MKD_TAGTEXT, IS_URL };
529
+ static linkytype linkt = { 0, 0, "<a href=\"", "\"",
530
+ 0, ">", "</a>", MKD_NOLINKS, IS_URL };
531
+
532
+ /*
533
+ * pseudo-protocols for [][];
534
+ *
535
+ * id: generates <a id="link">tag</a>
536
+ * class: generates <span class="link">tag</span>
537
+ * raw: just dump the link without any processing
538
+ */
539
+ static linkytype specials[] = {
540
+ { "id:", 3, "<span id=\"", "\"", 0, ">", "</span>", 0, 0 },
541
+ { "raw:", 4, 0, 0, 0, 0, 0, MKD_NOHTML, 0 },
542
+ { "lang:", 5, "<span lang=\"", "\"", 0, ">", "</span>", 0, 0 },
543
+ { "abbr:", 5, "<abbr title=\"", "\"", 0, ">", "</abbr>", 0, 0 },
544
+ { "class:", 6, "<span class=\"", "\"", 0, ">", "</span>", 0, 0 },
545
+ } ;
546
+
547
+ #define NR(x) (sizeof x / sizeof x[0])
548
+
549
+ /* see if t contains one of our pseudo-protocols.
550
+ */
551
+ static linkytype *
552
+ pseudo(Cstring t)
553
+ {
554
+ int i;
555
+ linkytype *r;
556
+
557
+ for ( i=0, r=specials; i < NR(specials); i++,r++ ) {
558
+ if ( (S(t) > r->szpat) && (strncasecmp(T(t), r->pat, r->szpat) == 0) )
559
+ return r;
560
+ }
561
+ return 0;
562
+ }
563
+
564
+
565
+ /* print out the start of an `img' or `a' tag, applying callbacks as needed.
566
+ */
567
+ static void
568
+ printlinkyref(MMIOT *f, linkytype *tag, char *link, int size)
569
+ {
570
+ char *edit;
571
+
572
+ if ( f->flags & IS_LABEL )
573
+ return;
574
+
575
+ Qstring(tag->link_pfx, f);
576
+
577
+ if ( tag->kind & IS_URL ) {
578
+ if ( f->cb && f->cb->e_url && (edit = (*f->cb->e_url)(link, size, f->cb->e_data)) ) {
579
+ puturl(edit, strlen(edit), f, 0);
580
+ if ( f->cb->e_free ) (*f->cb->e_free)(edit, f->cb->e_data);
581
+ }
582
+ else
583
+ puturl(link + tag->szpat, size - tag->szpat, f, 0);
584
+ }
585
+ else
586
+ ___mkd_reparse(link + tag->szpat, size - tag->szpat, MKD_TAGTEXT, f, 0);
587
+
588
+ Qstring(tag->link_sfx, f);
589
+
590
+ if ( f->cb && f->cb->e_flags && (edit = (*f->cb->e_flags)(link, size, f->cb->e_data)) ) {
591
+ Qchar(' ', f);
592
+ Qstring(edit, f);
593
+ if ( f->cb->e_free ) (*f->cb->e_free)(edit, f->cb->e_data);
594
+ }
595
+ } /* printlinkyref */
596
+
597
+
598
+ /* helper function for php markdown extra footnotes; allow the user to
599
+ * define a prefix tag instead of just `fn`
600
+ */
601
+ static char *
602
+ p_or_nothing(p)
603
+ MMIOT *p;
604
+ {
605
+ return p->ref_prefix ? p->ref_prefix : "fn";
606
+ }
607
+
608
+
609
+ /* php markdown extra/daring fireball style print footnotes
610
+ */
611
+ static int
612
+ extra_linky(MMIOT *f, Cstring text, Footnote *ref)
613
+ {
614
+ if ( ref->flags & REFERENCED )
615
+ return 0;
616
+
617
+ if ( f->flags & IS_LABEL )
618
+ ___mkd_reparse(T(text), S(text), linkt.flags, f, 0);
619
+ else {
620
+ ref->flags |= REFERENCED;
621
+ ref->refnumber = ++ f->reference;
622
+ Qprintf(f, "<sup id=\"%sref:%d\"><a href=\"#%s:%d\" rel=\"footnote\">%d</a></sup>",
623
+ p_or_nothing(f), ref->refnumber,
624
+ p_or_nothing(f), ref->refnumber, ref->refnumber);
625
+ }
626
+ return 1;
627
+ } /* extra_linky */
628
+
629
+
630
+ /* print out a linky (or fail if it's Not Allowed)
631
+ */
632
+ static int
633
+ linkyformat(MMIOT *f, Cstring text, int image, Footnote *ref)
634
+ {
635
+ linkytype *tag;
636
+
637
+
638
+ if ( image )
639
+ tag = &imaget;
640
+ else if ( tag = pseudo(ref->link) ) {
641
+ if ( f->flags & (MKD_NO_EXT|MKD_SAFELINK) )
642
+ return 0;
643
+ }
644
+ else if ( (f->flags & MKD_SAFELINK) && T(ref->link)
645
+ && (T(ref->link)[0] != '/')
646
+ && !isautoprefix(T(ref->link), S(ref->link)) )
647
+ /* if MKD_SAFELINK, only accept links that are local or
648
+ * a well-known protocol
649
+ */
650
+ return 0;
651
+ else
652
+ tag = &linkt;
653
+
654
+ if ( f->flags & tag->flags )
655
+ return 0;
656
+
657
+ if ( f->flags & IS_LABEL )
658
+ ___mkd_reparse(T(text), S(text), tag->flags, f, 0);
659
+ else if ( tag->link_pfx ) {
660
+ printlinkyref(f, tag, T(ref->link), S(ref->link));
661
+
662
+ if ( tag->WxH ) {
663
+ if ( ref->height ) Qprintf(f," height=\"%d\"", ref->height);
664
+ if ( ref->width ) Qprintf(f, " width=\"%d\"", ref->width);
665
+ }
666
+
667
+ if ( S(ref->title) ) {
668
+ Qstring(" title=\"", f);
669
+ ___mkd_reparse(T(ref->title), S(ref->title), MKD_TAGTEXT, f, 0);
670
+ Qchar('"', f);
671
+ }
672
+
673
+ Qstring(tag->text_pfx, f);
674
+ ___mkd_reparse(T(text), S(text), tag->flags, f, 0);
675
+ Qstring(tag->text_sfx, f);
676
+ }
677
+ else
678
+ Qwrite(T(ref->link) + tag->szpat, S(ref->link) - tag->szpat, f);
679
+
680
+ return 1;
681
+ } /* linkyformat */
682
+
683
+
684
+ /*
685
+ * process embedded links and images
686
+ */
687
+ static int
688
+ linkylinky(int image, MMIOT *f)
689
+ {
690
+ int start = mmiottell(f);
691
+ Cstring name;
692
+ Footnote key, *ref;
693
+
694
+ int status = 0;
695
+ int extra_footnote = 0;
696
+
697
+ CREATE(name);
698
+ memset(&key, 0, sizeof key);
699
+
700
+ if ( linkylabel(f, &name) ) {
701
+ if ( peek(f,1) == '(' ) {
702
+ pull(f);
703
+ if ( linkyurl(f, image, &key) )
704
+ status = linkyformat(f, name, image, &key);
705
+ }
706
+ else {
707
+ int goodlink, implicit_mark = mmiottell(f);
708
+
709
+ if ( isspace(peek(f,1)) )
710
+ pull(f);
711
+
712
+ if ( peek(f,1) == '[' ) {
713
+ pull(f); /* consume leading '[' */
714
+ goodlink = linkylabel(f, &key.tag);
715
+ }
716
+ else {
717
+ /* new markdown implicit name syntax doesn't
718
+ * require a second []
719
+ */
720
+ mmiotseek(f, implicit_mark);
721
+ goodlink = !(f->flags & MKD_1_COMPAT);
722
+
723
+ if ( (f->flags & MKD_EXTRA_FOOTNOTE) && (!image) && S(name) && T(name)[0] == '^' )
724
+ extra_footnote = 1;
725
+ }
726
+
727
+ if ( goodlink ) {
728
+ if ( !S(key.tag) ) {
729
+ DELETE(key.tag);
730
+ T(key.tag) = T(name);
731
+ S(key.tag) = S(name);
732
+ }
733
+
734
+ if ( ref = bsearch(&key, T(*f->footnotes), S(*f->footnotes),
735
+ sizeof key, (stfu)__mkd_footsort) ) {
736
+ if ( extra_footnote )
737
+ status = extra_linky(f,name,ref);
738
+ else
739
+ status = linkyformat(f, name, image, ref);
740
+ }
741
+ }
742
+ }
743
+ }
744
+
745
+ DELETE(name);
746
+ ___mkd_freefootnote(&key);
747
+
748
+ if ( status == 0 )
749
+ mmiotseek(f, start);
750
+
751
+ return status;
752
+ }
753
+
754
+
755
+ /* write a character to output, doing text escapes ( & -> &amp;,
756
+ * > -> &gt; < -> &lt; )
757
+ */
758
+ static void
759
+ cputc(int c, MMIOT *f)
760
+ {
761
+ switch (c) {
762
+ case '&': Qstring("&amp;", f); break;
763
+ case '>': Qstring("&gt;", f); break;
764
+ case '<': Qstring("&lt;", f); break;
765
+ default : Qchar(c, f); break;
766
+ }
767
+ }
768
+
769
+
770
+ /*
771
+ * convert an email address to a string of nonsense
772
+ */
773
+ static void
774
+ mangle(char *s, int len, MMIOT *f)
775
+ {
776
+ while ( len-- > 0 ) {
777
+ Qstring("&#", f);
778
+ Qprintf(f, COINTOSS() ? "x%02x;" : "%02d;", *((unsigned char*)(s++)) );
779
+ }
780
+ }
781
+
782
+
783
+ /* nrticks() -- count up a row of tick marks
784
+ */
785
+ static int
786
+ nrticks(int offset, int tickchar, MMIOT *f)
787
+ {
788
+ int tick = 0;
789
+
790
+ while ( peek(f, offset+tick) == tickchar ) tick++;
791
+
792
+ return tick;
793
+ } /* nrticks */
794
+
795
+
796
+ /* matchticks() -- match a certain # of ticks, and if that fails
797
+ * match the largest subset of those ticks.
798
+ *
799
+ * if a subset was matched, return the # of ticks
800
+ * that were matched.
801
+ */
802
+ static int
803
+ matchticks(MMIOT *f, int tickchar, int ticks, int *endticks)
804
+ {
805
+ int size, count, c;
806
+ int subsize=0, subtick=0;
807
+
808
+ *endticks = ticks;
809
+ for (size = 0; (c=peek(f,size+ticks)) != EOF; size ++) {
810
+ if ( (c == tickchar) && ( count = nrticks(size+ticks,tickchar,f)) ) {
811
+ if ( count == ticks )
812
+ return size;
813
+ else if ( count ) {
814
+ if ( (count > subtick) && (count < ticks) ) {
815
+ subsize = size;
816
+ subtick = count;
817
+ }
818
+ size += count;
819
+ }
820
+ }
821
+ }
822
+ if ( subsize ) {
823
+ *endticks = subtick;
824
+ return subsize;
825
+ }
826
+ return 0;
827
+ } /* matchticks */
828
+
829
+
830
+ /* code() -- write a string out as code. The only characters that have
831
+ * special meaning in a code block are * `<' and `&' , which
832
+ * are /always/ expanded to &lt; and &amp;
833
+ */
834
+ static void
835
+ code(MMIOT *f, char *s, int length)
836
+ {
837
+ int i,c;
838
+
839
+ for ( i=0; i < length; i++ )
840
+ if ( (c = s[i]) == 003) /* ^C: expand back to 2 spaces */
841
+ Qstring(" ", f);
842
+ else if ( c == '\\' && (i < length-1) && escaped(f, s[i+1]) )
843
+ cputc(s[++i], f);
844
+ else
845
+ cputc(c, f);
846
+ } /* code */
847
+
848
+
849
+ /* delspan() -- write out a chunk of text, blocking with <del>...</del>
850
+ */
851
+ static void
852
+ delspan(MMIOT *f, int size)
853
+ {
854
+ Qstring("<del>", f);
855
+ ___mkd_reparse(cursor(f)-1, size, 0, f, 0);
856
+ Qstring("</del>", f);
857
+ }
858
+
859
+
860
+ /* codespan() -- write out a chunk of text as code, trimming one
861
+ * space off the front and/or back as appropriate.
862
+ */
863
+ static void
864
+ codespan(MMIOT *f, int size)
865
+ {
866
+ int i=0;
867
+
868
+ if ( size > 1 && peek(f, size-1) == ' ' ) --size;
869
+ if ( peek(f,i) == ' ' ) ++i, --size;
870
+
871
+ Qstring("<code>", f);
872
+ code(f, cursor(f)+(i-1), size);
873
+ Qstring("</code>", f);
874
+ } /* codespan */
875
+
876
+
877
+ /* before letting a tag through, validate against
878
+ * MKD_NOLINKS and MKD_NOIMAGE
879
+ */
880
+ static int
881
+ forbidden_tag(MMIOT *f)
882
+ {
883
+ int c = toupper(peek(f, 1));
884
+
885
+ if ( f->flags & MKD_NOHTML )
886
+ return 1;
887
+
888
+ if ( c == 'A' && (f->flags & MKD_NOLINKS) && !isthisalnum(f,2) )
889
+ return 1;
890
+ if ( c == 'I' && (f->flags & MKD_NOIMAGE)
891
+ && strncasecmp(cursor(f)+1, "MG", 2) == 0
892
+ && !isthisalnum(f,4) )
893
+ return 1;
894
+ return 0;
895
+ }
896
+
897
+
898
+ /* Check a string to see if it looks like a mail address
899
+ * "looks like a mail address" means alphanumeric + some
900
+ * specials, then a `@`, then alphanumeric + some specials,
901
+ * but with a `.`
902
+ */
903
+ static int
904
+ maybe_address(char *p, int size)
905
+ {
906
+ int ok = 0;
907
+
908
+ for ( ;size && (isalnum(*p) || strchr("._-+*", *p)); ++p, --size)
909
+ ;
910
+
911
+ if ( ! (size && *p == '@') )
912
+ return 0;
913
+
914
+ --size, ++p;
915
+
916
+ if ( size && *p == '.' ) return 0;
917
+
918
+ for ( ;size && (isalnum(*p) || strchr("._-+", *p)); ++p, --size )
919
+ if ( *p == '.' && size > 1 ) ok = 1;
920
+
921
+ return size ? 0 : ok;
922
+ }
923
+
924
+
925
+ /* The size-length token at cursor(f) is either a mailto:, an
926
+ * implicit mailto:, one of the approved url protocols, or just
927
+ * plain old text. If it's a mailto: or an approved protocol,
928
+ * linkify it, otherwise say "no"
929
+ */
930
+ static int
931
+ process_possible_link(MMIOT *f, int size)
932
+ {
933
+ int address= 0;
934
+ int mailto = 0;
935
+ char *text = cursor(f);
936
+
937
+ if ( f->flags & MKD_NOLINKS ) return 0;
938
+
939
+ if ( (size > 7) && strncasecmp(text, "mailto:", 7) == 0 ) {
940
+ /* if it says it's a mailto, it's a mailto -- who am
941
+ * I to second-guess the user?
942
+ */
943
+ address = 1;
944
+ mailto = 7; /* 7 is the length of "mailto:"; we need this */
945
+ }
946
+ else
947
+ address = maybe_address(text, size);
948
+
949
+ if ( address ) {
950
+ Qstring("<a href=\"", f);
951
+ if ( !mailto ) {
952
+ /* supply a mailto: protocol if one wasn't attached */
953
+ mangle("mailto:", 7, f);
954
+ }
955
+ mangle(text, size, f);
956
+ Qstring("\">", f);
957
+ mangle(text+mailto, size-mailto, f);
958
+ Qstring("</a>", f);
959
+ return 1;
960
+ }
961
+ else if ( isautoprefix(text, size) ) {
962
+ printlinkyref(f, &linkt, text, size);
963
+ Qchar('>', f);
964
+ puturl(text,size,f, 1);
965
+ Qstring("</a>", f);
966
+ return 1;
967
+ }
968
+ return 0;
969
+ } /* process_possible_link */
970
+
971
+
972
+ /* a < may be just a regular character, the start of an embedded html
973
+ * tag, or the start of an <automatic link>. If it's an automatic
974
+ * link, we also need to know if it's an email address because if it
975
+ * is we need to mangle it in our futile attempt to cut down on the
976
+ * spaminess of the rendered page.
977
+ */
978
+ static int
979
+ maybe_tag_or_link(MMIOT *f)
980
+ {
981
+ int c, size;
982
+ int maybetag = 1;
983
+
984
+ if ( f->flags & MKD_TAGTEXT )
985
+ return 0;
986
+
987
+ for ( size=0; (c = peek(f, size+1)) != '>'; size++) {
988
+ if ( c == EOF )
989
+ return 0;
990
+ else if ( c == '\\' ) {
991
+ maybetag=0;
992
+ if ( peek(f, size+2) != EOF )
993
+ size++;
994
+ }
995
+ else if ( isspace(c) )
996
+ break;
997
+ #if WITH_GITHUB_TAGS
998
+ else if ( ! (c == '/' || c == '-' || c == '_' || isalnum(c) ) )
999
+ #else
1000
+ else if ( ! (c == '/' || isalnum(c) ) )
1001
+ #endif
1002
+ maybetag=0;
1003
+ }
1004
+
1005
+ if ( size ) {
1006
+ if ( maybetag || (size >= 3 && strncmp(cursor(f), "!--", 3) == 0) ) {
1007
+
1008
+ /* It is not a html tag unless we find the closing '>' in
1009
+ * the same block.
1010
+ */
1011
+ while ( (c = peek(f, size+1)) != '>' )
1012
+ if ( c == EOF )
1013
+ return 0;
1014
+ else
1015
+ size++;
1016
+
1017
+ if ( forbidden_tag(f) )
1018
+ return 0;
1019
+
1020
+ Qchar('<', f);
1021
+ while ( ((c = peek(f, 1)) != EOF) && (c != '>') )
1022
+ Qchar(pull(f), f);
1023
+ return 1;
1024
+ }
1025
+ else if ( !isspace(c) && process_possible_link(f, size) ) {
1026
+ shift(f, size+1);
1027
+ return 1;
1028
+ }
1029
+ }
1030
+
1031
+ return 0;
1032
+ }
1033
+
1034
+
1035
+ /* autolinking means that all inline html is <a href'ified>. A
1036
+ * autolink url is alphanumerics, slashes, periods, underscores,
1037
+ * the at sign, colon, and the % character.
1038
+ */
1039
+ static int
1040
+ maybe_autolink(MMIOT *f)
1041
+ {
1042
+ register int c;
1043
+ int size;
1044
+
1045
+ /* greedily scan forward for the end of a legitimate link.
1046
+ */
1047
+ for ( size=0; (c=peek(f, size+1)) != EOF; size++ )
1048
+ if ( c == '\\' ) {
1049
+ if ( peek(f, size+2) != EOF )
1050
+ ++size;
1051
+ }
1052
+ else if ( isspace(c) || strchr("'\"()[]{}<>`", c) )
1053
+ break;
1054
+
1055
+ if ( (size > 1) && process_possible_link(f, size) ) {
1056
+ shift(f, size);
1057
+ return 1;
1058
+ }
1059
+ return 0;
1060
+ }
1061
+
1062
+
1063
+ /* smartyquote code that's common for single and double quotes
1064
+ */
1065
+ static int
1066
+ smartyquote(int *flags, char typeofquote, MMIOT *f)
1067
+ {
1068
+ int bit = (typeofquote == 's') ? 0x01 : 0x02;
1069
+
1070
+ if ( bit & (*flags) ) {
1071
+ if ( isthisnonword(f,1) ) {
1072
+ Qprintf(f, "&r%cquo;", typeofquote);
1073
+ (*flags) &= ~bit;
1074
+ return 1;
1075
+ }
1076
+ }
1077
+ else if ( isthisnonword(f,-1) && peek(f,1) != EOF ) {
1078
+ Qprintf(f, "&l%cquo;", typeofquote);
1079
+ (*flags) |= bit;
1080
+ return 1;
1081
+ }
1082
+ return 0;
1083
+ }
1084
+
1085
+
1086
+ static int
1087
+ islike(MMIOT *f, char *s)
1088
+ {
1089
+ int len;
1090
+ int i;
1091
+
1092
+ if ( s[0] == '|' ) {
1093
+ if ( !isthisnonword(f, -1) )
1094
+ return 0;
1095
+ ++s;
1096
+ }
1097
+
1098
+ if ( !(len = strlen(s)) )
1099
+ return 0;
1100
+
1101
+ if ( s[len-1] == '|' ) {
1102
+ if ( !isthisnonword(f,len-1) )
1103
+ return 0;
1104
+ len--;
1105
+ }
1106
+
1107
+ for (i=1; i < len; i++)
1108
+ if (tolower(peek(f,i)) != s[i])
1109
+ return 0;
1110
+ return 1;
1111
+ }
1112
+
1113
+
1114
+ static struct smarties {
1115
+ char c0;
1116
+ char *pat;
1117
+ char *entity;
1118
+ int shift;
1119
+ } smarties[] = {
1120
+ { '\'', "'s|", "rsquo", 0 },
1121
+ { '\'', "'t|", "rsquo", 0 },
1122
+ { '\'', "'re|", "rsquo", 0 },
1123
+ { '\'', "'ll|", "rsquo", 0 },
1124
+ { '\'', "'ve|", "rsquo", 0 },
1125
+ { '\'', "'m|", "rsquo", 0 },
1126
+ { '\'', "'d|", "rsquo", 0 },
1127
+ { '-', "---", "mdash", 2 },
1128
+ { '-', "--", "ndash", 1 },
1129
+ { '.', "...", "hellip", 2 },
1130
+ { '.', ". . .", "hellip", 4 },
1131
+ { '(', "(c)", "copy", 2 },
1132
+ { '(', "(r)", "reg", 2 },
1133
+ { '(', "(tm)", "trade", 3 },
1134
+ { '3', "|3/4|", "frac34", 2 },
1135
+ { '3', "|3/4ths|", "frac34", 2 },
1136
+ { '1', "|1/2|", "frac12", 2 },
1137
+ { '1', "|1/4|", "frac14", 2 },
1138
+ { '1', "|1/4th|", "frac14", 2 },
1139
+ { '&', "&#0;", 0, 3 },
1140
+ } ;
1141
+ #define NRSMART ( sizeof smarties / sizeof smarties[0] )
1142
+
1143
+
1144
+ /* Smarty-pants-style chrome for quotes, -, ellipses, and (r)(c)(tm)
1145
+ */
1146
+ static int
1147
+ smartypants(int c, int *flags, MMIOT *f)
1148
+ {
1149
+ int i;
1150
+
1151
+ if ( f->flags & (MKD_NOPANTS|MKD_TAGTEXT|IS_LABEL) )
1152
+ return 0;
1153
+
1154
+ for ( i=0; i < NRSMART; i++)
1155
+ if ( (c == smarties[i].c0) && islike(f, smarties[i].pat) ) {
1156
+ if ( smarties[i].entity )
1157
+ Qprintf(f, "&%s;", smarties[i].entity);
1158
+ shift(f, smarties[i].shift);
1159
+ return 1;
1160
+ }
1161
+
1162
+ switch (c) {
1163
+ case '<' : return 0;
1164
+ case '\'': if ( smartyquote(flags, 's', f) ) return 1;
1165
+ break;
1166
+
1167
+ case '"': if ( smartyquote(flags, 'd', f) ) return 1;
1168
+ break;
1169
+
1170
+ case '`': if ( peek(f, 1) == '`' ) {
1171
+ int j = 2;
1172
+
1173
+ while ( (c=peek(f,j)) != EOF ) {
1174
+ if ( c == '\\' )
1175
+ j += 2;
1176
+ else if ( c == '`' )
1177
+ break;
1178
+ else if ( c == '\'' && peek(f, j+1) == '\'' ) {
1179
+ Qstring("&ldquo;", f);
1180
+ ___mkd_reparse(cursor(f)+1, j-2, 0, f, 0);
1181
+ Qstring("&rdquo;", f);
1182
+ shift(f,j+1);
1183
+ return 1;
1184
+ }
1185
+ else ++j;
1186
+ }
1187
+
1188
+ }
1189
+ break;
1190
+ }
1191
+ return 0;
1192
+ } /* smartypants */
1193
+
1194
+
1195
+ /* process a body of text encased in some sort of tick marks. If it
1196
+ * works, generate the output and return 1, otherwise just return 0 and
1197
+ * let the caller figure it out.
1198
+ */
1199
+ static int
1200
+ tickhandler(MMIOT *f, int tickchar, int minticks, int allow_space, spanhandler spanner)
1201
+ {
1202
+ int endticks, size;
1203
+ int tick = nrticks(0, tickchar, f);
1204
+
1205
+ if ( !allow_space && isspace(peek(f,tick)) )
1206
+ return 0;
1207
+
1208
+ if ( (tick >= minticks) && (size = matchticks(f,tickchar,tick,&endticks)) ) {
1209
+ if ( endticks < tick ) {
1210
+ size += (tick - endticks);
1211
+ tick = endticks;
1212
+ }
1213
+
1214
+ shift(f, tick);
1215
+ (*spanner)(f,size);
1216
+ shift(f, size+tick-1);
1217
+ return 1;
1218
+ }
1219
+ return 0;
1220
+ }
1221
+
1222
+ #define tag_text(f) (f->flags & MKD_TAGTEXT)
1223
+
1224
+
1225
+ static void
1226
+ text(MMIOT *f)
1227
+ {
1228
+ int c, j;
1229
+ int rep;
1230
+ int smartyflags = 0;
1231
+
1232
+ while (1) {
1233
+ if ( (f->flags & MKD_AUTOLINK) && isalpha(peek(f,1)) && !tag_text(f) )
1234
+ maybe_autolink(f);
1235
+
1236
+ c = pull(f);
1237
+
1238
+ if (c == EOF)
1239
+ break;
1240
+
1241
+ if ( smartypants(c, &smartyflags, f) )
1242
+ continue;
1243
+ switch (c) {
1244
+ case 0: break;
1245
+
1246
+ case 3: Qstring(tag_text(f) ? " " : "<br/>", f);
1247
+ break;
1248
+
1249
+ case '>': if ( tag_text(f) )
1250
+ Qstring("&gt;", f);
1251
+ else
1252
+ Qchar(c, f);
1253
+ break;
1254
+
1255
+ case '"': if ( tag_text(f) )
1256
+ Qstring("&quot;", f);
1257
+ else
1258
+ Qchar(c, f);
1259
+ break;
1260
+
1261
+ case '!': if ( peek(f,1) == '[' ) {
1262
+ pull(f);
1263
+ if ( tag_text(f) || !linkylinky(1, f) )
1264
+ Qstring("![", f);
1265
+ }
1266
+ else
1267
+ Qchar(c, f);
1268
+ break;
1269
+ case '[': if ( tag_text(f) || !linkylinky(0, f) )
1270
+ Qchar(c, f);
1271
+ break;
1272
+ /* A^B -> A<sup>B</sup> */
1273
+ case '^': if ( (f->flags & (MKD_NOSUPERSCRIPT|MKD_STRICT|MKD_TAGTEXT))
1274
+ || (isthisnonword(f,-1) && peek(f,-1) != ')')
1275
+ || isthisspace(f,1) )
1276
+ Qchar(c,f);
1277
+ else {
1278
+ char *sup = cursor(f);
1279
+ int len = 0;
1280
+
1281
+ if ( peek(f,1) == '(' ) {
1282
+ int here = mmiottell(f);
1283
+ pull(f);
1284
+
1285
+ if ( (len = parenthetical('(',')',f)) <= 0 ) {
1286
+ mmiotseek(f,here);
1287
+ Qchar(c, f);
1288
+ break;
1289
+ }
1290
+ sup++;
1291
+ }
1292
+ else {
1293
+ while ( isthisalnum(f,1+len) )
1294
+ ++len;
1295
+ if ( !len ) {
1296
+ Qchar(c,f);
1297
+ break;
1298
+ }
1299
+ shift(f,len);
1300
+ }
1301
+ Qstring("<sup>",f);
1302
+ ___mkd_reparse(sup, len, 0, f, "()");
1303
+ Qstring("</sup>", f);
1304
+ }
1305
+ break;
1306
+ case '_':
1307
+ /* Underscores don't count if they're in the middle of a word */
1308
+ if ( !(f->flags & (MKD_NORELAXED|MKD_STRICT))
1309
+ && isthisalnum(f,-1)
1310
+ && isthisalnum(f,1) ) {
1311
+ Qchar(c, f);
1312
+ break;
1313
+ }
1314
+ case '*':
1315
+ /* Underscores & stars don't count if they're out in the middle
1316
+ * of whitespace */
1317
+ if ( isthisspace(f,-1) && isthisspace(f,1) ) {
1318
+ Qchar(c, f);
1319
+ break;
1320
+ }
1321
+ /* else fall into the regular old emphasis case */
1322
+ if ( tag_text(f) )
1323
+ Qchar(c, f);
1324
+ else {
1325
+ for (rep = 1; peek(f,1) == c; pull(f) )
1326
+ ++rep;
1327
+ Qem(f,c,rep);
1328
+ }
1329
+ break;
1330
+
1331
+ case '~': if ( (f->flags & (MKD_NOSTRIKETHROUGH|MKD_TAGTEXT|MKD_STRICT)) || ! tickhandler(f,c,2,0, delspan) )
1332
+ Qchar(c, f);
1333
+ break;
1334
+
1335
+ case '`': if ( tag_text(f) || !tickhandler(f,c,1,1,codespan) )
1336
+ Qchar(c, f);
1337
+ break;
1338
+
1339
+ case '\\': switch ( c = pull(f) ) {
1340
+ case '&': Qstring("&amp;", f);
1341
+ break;
1342
+ case '<': c = peek(f,1);
1343
+ if ( (c == EOF) || isspace(c) )
1344
+ Qstring("&lt;", f);
1345
+ else {
1346
+ /* Markdown.pl does not escape <[nonwhite]
1347
+ * sequences */
1348
+ Qchar('\\', f);
1349
+ shift(f, -1);
1350
+ }
1351
+
1352
+ break;
1353
+ case '^': if ( f->flags & (MKD_STRICT|MKD_NOSUPERSCRIPT) ) {
1354
+ Qchar('\\', f);
1355
+ shift(f,-1);
1356
+ break;
1357
+ }
1358
+ Qchar(c, f);
1359
+ break;
1360
+
1361
+ case ':': case '|':
1362
+ if ( f->flags & MKD_NOTABLES ) {
1363
+ Qchar('\\', f);
1364
+ shift(f,-1);
1365
+ break;
1366
+ }
1367
+ Qchar(c, f);
1368
+ break;
1369
+
1370
+ case EOF: Qchar('\\', f);
1371
+ break;
1372
+
1373
+ default: if ( escaped(f,c) ||
1374
+ strchr(">#.-+{}]![*_\\()`", c) )
1375
+ Qchar(c, f);
1376
+ else {
1377
+ Qchar('\\', f);
1378
+ shift(f, -1);
1379
+ }
1380
+ break;
1381
+ }
1382
+ break;
1383
+
1384
+ case '<': if ( !maybe_tag_or_link(f) )
1385
+ Qstring("&lt;", f);
1386
+ break;
1387
+
1388
+ case '&': j = (peek(f,1) == '#' ) ? 2 : 1;
1389
+ while ( isthisalnum(f,j) )
1390
+ ++j;
1391
+
1392
+ if ( peek(f,j) != ';' )
1393
+ Qstring("&amp;", f);
1394
+ else
1395
+ Qchar(c, f);
1396
+ break;
1397
+
1398
+ default: Qchar(c, f);
1399
+ break;
1400
+ }
1401
+ }
1402
+ /* truncate the input string after we've finished processing it */
1403
+ S(f->in) = f->isp = 0;
1404
+ } /* text */
1405
+
1406
+
1407
+ /* print a header block
1408
+ */
1409
+ static void
1410
+ printheader(Paragraph *pp, MMIOT *f)
1411
+ {
1412
+ #if WITH_ID_ANCHOR
1413
+ Qprintf(f, "<h%d", pp->hnumber);
1414
+ if ( f->flags & MKD_TOC ) {
1415
+ Qstring(" id=\"", f);
1416
+ mkd_string_to_anchor(T(pp->text->text),
1417
+ S(pp->text->text),
1418
+ (mkd_sta_function_t)Qchar, f, 1);
1419
+ Qchar('"', f);
1420
+ }
1421
+ Qchar('>', f);
1422
+ #else
1423
+ if ( f->flags & MKD_TOC ) {
1424
+ Qstring("<a name=\"", f);
1425
+ mkd_string_to_anchor(T(pp->text->text),
1426
+ S(pp->text->text),
1427
+ (mkd_sta_function_t)Qchar, f, 1);
1428
+ Qstring("\"></a>\n", f);
1429
+ }
1430
+ Qprintf(f, "<h%d>", pp->hnumber);
1431
+ #endif
1432
+ push(T(pp->text->text), S(pp->text->text), f);
1433
+ text(f);
1434
+ Qprintf(f, "</h%d>", pp->hnumber);
1435
+ }
1436
+
1437
+
1438
+ enum e_alignments { a_NONE, a_CENTER, a_LEFT, a_RIGHT };
1439
+
1440
+ static char* alignments[] = { "", " style=\"text-align:center;\"",
1441
+ " style=\"text-align:left;\"",
1442
+ " style=\"text-align:right;\"" };
1443
+
1444
+ typedef STRING(int) Istring;
1445
+
1446
+ static int
1447
+ splat(Line *p, char *block, Istring align, int force, MMIOT *f)
1448
+ {
1449
+ int first,
1450
+ idx = p->dle,
1451
+ colno = 0;
1452
+
1453
+
1454
+ ___mkd_tidy(&p->text);
1455
+ if ( T(p->text)[S(p->text)-1] == '|' )
1456
+ --S(p->text);
1457
+
1458
+ Qstring("<tr>\n", f);
1459
+ while ( idx < S(p->text) ) {
1460
+ first = idx;
1461
+ if ( force && (colno >= S(align)-1) )
1462
+ idx = S(p->text);
1463
+ else
1464
+ while ( (idx < S(p->text)) && (T(p->text)[idx] != '|') ) {
1465
+ if ( T(p->text)[idx] == '\\' )
1466
+ ++idx;
1467
+ ++idx;
1468
+ }
1469
+
1470
+ Qprintf(f, "<%s%s>",
1471
+ block,
1472
+ alignments[ (colno < S(align)) ? T(align)[colno] : a_NONE ]);
1473
+ ___mkd_reparse(T(p->text)+first, idx-first, 0, f, "|");
1474
+ Qprintf(f, "</%s>\n", block);
1475
+ idx++;
1476
+ colno++;
1477
+ }
1478
+ if ( force )
1479
+ while (colno < S(align) ) {
1480
+ Qprintf(f, "<%s></%s>\n", block, block);
1481
+ ++colno;
1482
+ }
1483
+ Qstring("</tr>\n", f);
1484
+ return colno;
1485
+ }
1486
+
1487
+
1488
+ static int
1489
+ printtable(Paragraph *pp, MMIOT *f)
1490
+ {
1491
+ /* header, dashes, then lines of content */
1492
+
1493
+ Line *hdr, *dash, *body;
1494
+ Istring align;
1495
+ int hcols,start;
1496
+ char *p;
1497
+ enum e_alignments it;
1498
+
1499
+ hdr = pp->text;
1500
+ dash= hdr->next;
1501
+ body= dash->next;
1502
+
1503
+ if ( T(hdr->text)[hdr->dle] == '|' ) {
1504
+ /* trim leading pipe off all lines
1505
+ */
1506
+ Line *r;
1507
+ for ( r = pp->text; r; r = r->next )
1508
+ r->dle ++;
1509
+ }
1510
+
1511
+ /* figure out cell alignments */
1512
+
1513
+ CREATE(align);
1514
+
1515
+ for (p=T(dash->text), start=dash->dle; start < S(dash->text); ) {
1516
+ char first, last;
1517
+ int end;
1518
+
1519
+ last=first=0;
1520
+ for (end=start ; (end < S(dash->text)) && p[end] != '|'; ++ end ) {
1521
+ if ( p[end] == '\\' )
1522
+ ++ end;
1523
+ else if ( !isspace(p[end]) ) {
1524
+ if ( !first) first = p[end];
1525
+ last = p[end];
1526
+ }
1527
+ }
1528
+ it = ( first == ':' ) ? (( last == ':') ? a_CENTER : a_LEFT)
1529
+ : (( last == ':') ? a_RIGHT : a_NONE );
1530
+
1531
+ EXPAND(align) = it;
1532
+ start = 1+end;
1533
+ }
1534
+
1535
+ Qstring("<table>\n", f);
1536
+ Qstring("<thead>\n", f);
1537
+ hcols = splat(hdr, "th", align, 0, f);
1538
+ Qstring("</thead>\n", f);
1539
+
1540
+ if ( hcols < S(align) )
1541
+ S(align) = hcols;
1542
+ else
1543
+ while ( hcols > S(align) )
1544
+ EXPAND(align) = a_NONE;
1545
+
1546
+ Qstring("<tbody>\n", f);
1547
+ for ( ; body; body = body->next)
1548
+ splat(body, "td", align, 1, f);
1549
+ Qstring("</tbody>\n", f);
1550
+ Qstring("</table>\n", f);
1551
+
1552
+ DELETE(align);
1553
+ return 1;
1554
+ }
1555
+
1556
+
1557
+ static int
1558
+ printblock(Paragraph *pp, MMIOT *f)
1559
+ {
1560
+ Line *t = pp->text;
1561
+ static char *Begin[] = { "", "<p>", "<p style=\"text-align:center;\">" };
1562
+ static char *End[] = { "", "</p>","</p>" };
1563
+
1564
+ while (t) {
1565
+ if ( S(t->text) ) {
1566
+ if ( t->next && S(t->text) > 2
1567
+ && T(t->text)[S(t->text)-2] == ' '
1568
+ && T(t->text)[S(t->text)-1] == ' ' ) {
1569
+ push(T(t->text), S(t->text)-2, f);
1570
+ push("\003\n", 2, f);
1571
+ }
1572
+ else {
1573
+ ___mkd_tidy(&t->text);
1574
+ push(T(t->text), S(t->text), f);
1575
+ if ( t->next )
1576
+ push("\n", 1, f);
1577
+ }
1578
+ }
1579
+ t = t->next;
1580
+ }
1581
+ Qstring(Begin[pp->align], f);
1582
+ text(f);
1583
+ Qstring(End[pp->align], f);
1584
+ return 1;
1585
+ }
1586
+
1587
+
1588
+ static void
1589
+ printcode(Line *t, char *lang, MMIOT *f)
1590
+ {
1591
+ int blanks;
1592
+
1593
+ Qstring("<pre><code", f);
1594
+ if (lang) {
1595
+ Qstring(" class=\"", f);
1596
+ Qstring(lang, f);
1597
+ Qstring("\"", f);
1598
+ }
1599
+ Qstring(">", f);
1600
+ for ( blanks = 0; t ; t = t->next ) {
1601
+ if ( S(t->text) > t->dle ) {
1602
+ while ( blanks ) {
1603
+ Qchar('\n', f);
1604
+ --blanks;
1605
+ }
1606
+ code(f, T(t->text), S(t->text));
1607
+ Qchar('\n', f);
1608
+ }
1609
+ else blanks++;
1610
+ }
1611
+ Qstring("</code></pre>", f);
1612
+ }
1613
+
1614
+
1615
+ static void
1616
+ printhtml(Line *t, MMIOT *f)
1617
+ {
1618
+ int blanks;
1619
+
1620
+ for ( blanks=0; t ; t = t->next )
1621
+ if ( S(t->text) ) {
1622
+ for ( ; blanks; --blanks )
1623
+ Qchar('\n', f);
1624
+
1625
+ Qwrite(T(t->text), S(t->text), f);
1626
+ Qchar('\n', f);
1627
+ }
1628
+ else
1629
+ blanks++;
1630
+ }
1631
+
1632
+
1633
+ static void
1634
+ htmlify(Paragraph *p, char *block, char *arguments, MMIOT *f)
1635
+ {
1636
+ ___mkd_emblock(f);
1637
+ if ( block )
1638
+ Qprintf(f, arguments ? "<%s %s>" : "<%s>", block, arguments);
1639
+ ___mkd_emblock(f);
1640
+
1641
+ while (( p = display(p, f) )) {
1642
+ ___mkd_emblock(f);
1643
+ Qstring("\n\n", f);
1644
+ }
1645
+
1646
+ if ( block )
1647
+ Qprintf(f, "</%s>", block);
1648
+ ___mkd_emblock(f);
1649
+ }
1650
+
1651
+
1652
+ static void
1653
+ definitionlist(Paragraph *p, MMIOT *f)
1654
+ {
1655
+ Line *tag;
1656
+
1657
+ if ( p ) {
1658
+ Qstring("<dl>\n", f);
1659
+
1660
+ for ( ; p ; p = p->next) {
1661
+ for ( tag = p->text; tag; tag = tag->next ) {
1662
+ Qstring("<dt>", f);
1663
+ ___mkd_reparse(T(tag->text), S(tag->text), 0, f, 0);
1664
+ Qstring("</dt>\n", f);
1665
+ }
1666
+
1667
+ htmlify(p->down, "dd", p->ident, f);
1668
+ Qchar('\n', f);
1669
+ }
1670
+
1671
+ Qstring("</dl>", f);
1672
+ }
1673
+ }
1674
+
1675
+
1676
+ static void
1677
+ listdisplay(int typ, Paragraph *p, MMIOT* f)
1678
+ {
1679
+ if ( p ) {
1680
+ Qprintf(f, "<%cl", (typ==UL)?'u':'o');
1681
+ if ( typ == AL )
1682
+ Qprintf(f, " type=\"a\"");
1683
+ Qprintf(f, ">\n");
1684
+
1685
+ for ( ; p ; p = p->next ) {
1686
+ htmlify(p->down, "li", p->ident, f);
1687
+ Qchar('\n', f);
1688
+ }
1689
+
1690
+ Qprintf(f, "</%cl>\n", (typ==UL)?'u':'o');
1691
+ }
1692
+ }
1693
+
1694
+
1695
+ /* dump out a Paragraph in the desired manner
1696
+ */
1697
+ static Paragraph*
1698
+ display(Paragraph *p, MMIOT *f)
1699
+ {
1700
+ if ( !p ) return 0;
1701
+
1702
+ switch ( p->typ ) {
1703
+ case STYLE:
1704
+ case WHITESPACE:
1705
+ break;
1706
+
1707
+ case HTML:
1708
+ printhtml(p->text, f);
1709
+ break;
1710
+
1711
+ case CODE:
1712
+ printcode(p->text, p->lang, f);
1713
+ break;
1714
+
1715
+ case QUOTE:
1716
+ htmlify(p->down, p->ident ? "div" : "blockquote", p->ident, f);
1717
+ break;
1718
+
1719
+ case UL:
1720
+ case OL:
1721
+ case AL:
1722
+ listdisplay(p->typ, p->down, f);
1723
+ break;
1724
+
1725
+ case DL:
1726
+ definitionlist(p->down, f);
1727
+ break;
1728
+
1729
+ case HR:
1730
+ Qstring("<hr />", f);
1731
+ break;
1732
+
1733
+ case HDR:
1734
+ printheader(p, f);
1735
+ break;
1736
+
1737
+ case TABLE:
1738
+ printtable(p, f);
1739
+ break;
1740
+
1741
+ case SOURCE:
1742
+ htmlify(p->down, 0, 0, f);
1743
+ break;
1744
+
1745
+ default:
1746
+ printblock(p, f);
1747
+ break;
1748
+ }
1749
+ return p->next;
1750
+ }
1751
+
1752
+
1753
+ /* dump out a list of footnotes
1754
+ */
1755
+ static void
1756
+ mkd_extra_footnotes(MMIOT *m)
1757
+ {
1758
+ int j, i;
1759
+ Footnote *t;
1760
+
1761
+ if ( m->reference == 0 )
1762
+ return;
1763
+
1764
+ Csprintf(&m->out, "\n<div class=\"footnotes\">\n<hr/>\n<ol>\n");
1765
+
1766
+ for ( i=1; i <= m->reference; i++ ) {
1767
+ for ( j=0; j < S(*m->footnotes); j++ ) {
1768
+ t = &T(*m->footnotes)[j];
1769
+ if ( (t->refnumber == i) && (t->flags & REFERENCED) ) {
1770
+ Csprintf(&m->out, "<li id=\"%s:%d\">\n<p>",
1771
+ p_or_nothing(m), t->refnumber);
1772
+ Csreparse(&m->out, T(t->title), S(t->title), 0);
1773
+ Csprintf(&m->out, "<a href=\"#%sref:%d\" rev=\"footnote\">&#8617;</a>",
1774
+ p_or_nothing(m), t->refnumber);
1775
+ Csprintf(&m->out, "</p></li>\n");
1776
+ }
1777
+ }
1778
+ }
1779
+ Csprintf(&m->out, "</ol>\n</div>\n");
1780
+ }
1781
+
1782
+
1783
+ /* return a pointer to the compiled markdown
1784
+ * document.
1785
+ */
1786
+ int
1787
+ mkd_document(Document *p, char **res)
1788
+ {
1789
+ int size;
1790
+
1791
+ if ( p && p->compiled ) {
1792
+ if ( ! p->html ) {
1793
+ htmlify(p->code, 0, 0, p->ctx);
1794
+ if ( p->ctx->flags & MKD_EXTRA_FOOTNOTE )
1795
+ mkd_extra_footnotes(p->ctx);
1796
+ p->html = 1;
1797
+ }
1798
+
1799
+ size = S(p->ctx->out);
1800
+
1801
+ if ( (size == 0) || T(p->ctx->out)[size-1] )
1802
+ EXPAND(p->ctx->out) = 0;
1803
+
1804
+ *res = T(p->ctx->out);
1805
+ return size;
1806
+ }
1807
+ return EOF;
1808
+ }
1809
+