rdiscount-dsc 1.6.9

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