rdiscount-dsc 1.6.9

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.
@@ -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
+