moredown 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,43 @@
1
+ /*
2
+ * docheader -- get values from the document header
3
+ *
4
+ * Copyright (C) 2007 David L Parsons.
5
+ * The redistribution terms are provided in the COPYRIGHT file that must
6
+ * be distributed with this source code.
7
+ */
8
+ #include "config.h"
9
+ #include <stdio.h>
10
+ #include <stdlib.h>
11
+ #include <ctype.h>
12
+
13
+ #include "cstring.h"
14
+ #include "markdown.h"
15
+ #include "amalloc.h"
16
+
17
+ #define afterdle(t) (T((t)->text) + (t)->dle)
18
+
19
+ char *
20
+ mkd_doc_title(Document *doc)
21
+ {
22
+ if ( doc && doc->headers )
23
+ return afterdle(doc->headers);
24
+ return 0;
25
+ }
26
+
27
+
28
+ char *
29
+ mkd_doc_author(Document *doc)
30
+ {
31
+ if ( doc && doc->headers && doc->headers->next )
32
+ return afterdle(doc->headers->next);
33
+ return 0;
34
+ }
35
+
36
+
37
+ char *
38
+ mkd_doc_date(Document *doc)
39
+ {
40
+ if ( doc && doc->headers && doc->headers->next && doc->headers->next->next )
41
+ return afterdle(doc->headers->next->next);
42
+ return 0;
43
+ }
@@ -0,0 +1,147 @@
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 "markdown.h"
9
+ #include "cstring.h"
10
+ #include "amalloc.h"
11
+
12
+ struct frame {
13
+ int indent;
14
+ char c;
15
+ };
16
+
17
+ typedef STRING(struct frame) Stack;
18
+
19
+ static char *
20
+ Pptype(int typ)
21
+ {
22
+ switch (typ) {
23
+ case WHITESPACE: return "whitespace";
24
+ case CODE : return "code";
25
+ case QUOTE : return "quote";
26
+ case MARKUP : return "markup";
27
+ case HTML : return "html";
28
+ case DL : return "dl";
29
+ case UL : return "ul";
30
+ case OL : return "ol";
31
+ case LISTITEM : return "item";
32
+ case HDR : return "header";
33
+ case HR : return "HR";
34
+ default : return "mystery node!";
35
+ }
36
+ }
37
+
38
+ static void
39
+ pushpfx(int indent, char c, Stack *sp)
40
+ {
41
+ struct frame *q = &EXPAND(*sp);
42
+
43
+ q->indent = indent;
44
+ q->c = c;
45
+ }
46
+
47
+
48
+ static void
49
+ poppfx(Stack *sp)
50
+ {
51
+ S(*sp)--;
52
+ }
53
+
54
+
55
+ static void
56
+ changepfx(Stack *sp, char c)
57
+ {
58
+ char ch;
59
+
60
+ if ( !S(*sp) ) return;
61
+
62
+ ch = T(*sp)[S(*sp)-1].c;
63
+
64
+ if ( ch == '+' || ch == '|' )
65
+ T(*sp)[S(*sp)-1].c = c;
66
+ }
67
+
68
+
69
+ static void
70
+ printpfx(Stack *sp, FILE *f)
71
+ {
72
+ int i;
73
+ char c;
74
+
75
+ if ( !S(*sp) ) return;
76
+
77
+ c = T(*sp)[S(*sp)-1].c;
78
+
79
+ if ( c == '+' || c == '-' ) {
80
+ fprintf(f, "--%c", c);
81
+ T(*sp)[S(*sp)-1].c = (c == '-') ? ' ' : '|';
82
+ }
83
+ else
84
+ for ( i=0; i < S(*sp); i++ ) {
85
+ if ( i )
86
+ fprintf(f, " ");
87
+ fprintf(f, "%*s%c", T(*sp)[i].indent + 2, " ", T(*sp)[i].c);
88
+ if ( T(*sp)[i].c == '`' )
89
+ T(*sp)[i].c = ' ';
90
+ }
91
+ fprintf(f, "--");
92
+ }
93
+
94
+
95
+ static void
96
+ dumptree(Paragraph *pp, Stack *sp, FILE *f)
97
+ {
98
+ int count;
99
+ Line *p;
100
+ int d;
101
+ static char *Begin[] = { 0, "P", "center" };
102
+
103
+ while ( pp ) {
104
+ if ( !pp->next )
105
+ changepfx(sp, '`');
106
+ printpfx(sp, f);
107
+
108
+ d = fprintf(f, "[%s", Pptype(pp->typ));
109
+ if ( pp->align )
110
+ d += fprintf(f, ", <%s>", Begin[pp->align]);
111
+
112
+ for (count=0, p=pp->text; p; ++count, (p = p->next) )
113
+ ;
114
+
115
+ if ( count )
116
+ d += fprintf(f, ", %d line%s", count, (count==1)?"":"s");
117
+
118
+ d += fprintf(f, "]");
119
+
120
+ if ( pp->down ) {
121
+ pushpfx(d, pp->down->next ? '+' : '-', sp);
122
+ dumptree(pp->down, sp, f);
123
+ poppfx(sp);
124
+ }
125
+ else fputc('\n', f);
126
+ pp = pp->next;
127
+ }
128
+ }
129
+
130
+
131
+ int
132
+ mkd_dump(Document *doc, FILE *out, int flags, char *title)
133
+ {
134
+ Stack stack;
135
+
136
+ if (mkd_compile(doc, flags) ) {
137
+
138
+ CREATE(stack);
139
+ pushpfx(fprintf(out, "%s", title), doc->code->next ? '+' : '-', &stack);
140
+ dumptree(doc->code, &stack, out);
141
+ DELETE(stack);
142
+
143
+ mkd_cleanup(doc);
144
+ return 0;
145
+ }
146
+ return -1;
147
+ }
@@ -0,0 +1,8 @@
1
+ require 'mkmf'
2
+
3
+ dir_config('rdiscount')
4
+
5
+ HAVE_RANDOM = have_func('random')
6
+ HAVE_SRANDOM = have_func('srandom')
7
+
8
+ create_makefile('rdiscount')
@@ -0,0 +1,1389 @@
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
+ /* prefixes for <automatic links>
21
+ */
22
+ static char *autoprefix[] = { "http://", "https://", "ftp://", "news://" };
23
+ #define SZAUTOPREFIX (sizeof autoprefix / sizeof autoprefix[0])
24
+
25
+ typedef int (*stfu)(const void*,const void*);
26
+
27
+
28
+ /* forward declarations */
29
+ static int iscodeblock(MMIOT*);
30
+ static void code(int, MMIOT*);
31
+ static void text(MMIOT *f);
32
+ static Paragraph *display(Paragraph*, MMIOT*);
33
+
34
+ /* externals from markdown.c */
35
+ int __mkd_footsort(Footnote *, Footnote *);
36
+
37
+ /*
38
+ * push text into the generator input buffer
39
+ */
40
+ static void
41
+ push(char *bfr, int size, MMIOT *f)
42
+ {
43
+ while ( size-- > 0 )
44
+ EXPAND(f->in) = *bfr++;
45
+ }
46
+
47
+
48
+ /* look <i> characters ahead of the cursor.
49
+ */
50
+ static int
51
+ peek(MMIOT *f, int i)
52
+ {
53
+
54
+ i += (f->isp-1);
55
+
56
+ return (i >= 0) && (i < S(f->in)) ? T(f->in)[i] : EOF;
57
+ }
58
+
59
+
60
+ /* pull a byte from the input buffer
61
+ */
62
+ static int
63
+ pull(MMIOT *f)
64
+ {
65
+ return ( f->isp < S(f->in) ) ? T(f->in)[f->isp++] : EOF;
66
+ }
67
+
68
+
69
+ /* return a pointer to the current position in the input buffer.
70
+ */
71
+ static char*
72
+ cursor(MMIOT *f)
73
+ {
74
+ return T(f->in) + f->isp;
75
+ }
76
+
77
+
78
+ static int
79
+ isthisspace(MMIOT *f, int i)
80
+ {
81
+ int c = peek(f, i);
82
+
83
+ return isspace(c) || (c == EOF);
84
+ }
85
+
86
+
87
+ static int
88
+ isthisalnum(MMIOT *f, int i)
89
+ {
90
+ int c = peek(f, i);
91
+
92
+ return (c != EOF) && isalnum(c);
93
+ }
94
+
95
+
96
+ static int
97
+ isthisnonword(MMIOT *f, int i)
98
+ {
99
+ return isthisspace(f, i) || ispunct(peek(f,i));
100
+ }
101
+
102
+
103
+ /* return/set the current cursor position
104
+ */
105
+ #define mmiotseek(f,x) (f->isp = x)
106
+ #define mmiottell(f) (f->isp)
107
+
108
+
109
+ /* move n characters forward ( or -n characters backward) in the input buffer.
110
+ */
111
+ static void
112
+ shift(MMIOT *f, int i)
113
+ {
114
+ if (f->isp + i >= 0 )
115
+ f->isp += i;
116
+ }
117
+
118
+
119
+ /* Qchar()
120
+ */
121
+ static void
122
+ Qchar(char c, MMIOT *f)
123
+ {
124
+ block *cur;
125
+
126
+ if ( S(f->Q) == 0 ) {
127
+ cur = &EXPAND(f->Q);
128
+ memset(cur, 0, sizeof *cur);
129
+ cur->b_type = bTEXT;
130
+ }
131
+ else
132
+ cur = &T(f->Q)[S(f->Q)-1];
133
+
134
+ EXPAND(cur->b_text) = c;
135
+
136
+ }
137
+
138
+
139
+ /* Qstring()
140
+ */
141
+ static void
142
+ Qstring(char *s, MMIOT *f)
143
+ {
144
+ while (*s)
145
+ Qchar(*s++, f);
146
+ }
147
+
148
+
149
+ /* Qwrite()
150
+ */
151
+ static void
152
+ Qwrite(char *s, int size, MMIOT *f)
153
+ {
154
+ while (size-- > 0)
155
+ Qchar(*s++, f);
156
+ }
157
+
158
+
159
+ /* Qprintf()
160
+ */
161
+ static void
162
+ Qprintf(MMIOT *f, char *fmt, ...)
163
+ {
164
+ char bfr[80];
165
+ va_list ptr;
166
+
167
+ va_start(ptr,fmt);
168
+ vsnprintf(bfr, sizeof bfr, fmt, ptr);
169
+ va_end(ptr);
170
+ Qstring(bfr, f);
171
+ }
172
+
173
+
174
+ /* Qem()
175
+ */
176
+ static void
177
+ Qem(MMIOT *f, char c, int count)
178
+ {
179
+ block *p = &EXPAND(f->Q);
180
+
181
+ memset(p, 0, sizeof *p);
182
+ p->b_type = (c == '*') ? bSTAR : bUNDER;
183
+ p->b_char = c;
184
+ p->b_count = count;
185
+
186
+ memset(&EXPAND(f->Q), 0, sizeof(block));
187
+ }
188
+
189
+
190
+ /* empair()
191
+ */
192
+ static int
193
+ empair(MMIOT *f, int go, int level)
194
+ {
195
+
196
+ int i;
197
+ block *begin, *p;
198
+
199
+ begin = &T(f->Q)[go];
200
+ for (i=go+1; i < S(f->Q); i++) {
201
+ p = &T(f->Q)[i];
202
+
203
+ if ( (p->b_type != bTEXT) && (p->b_count <= 0) )
204
+ break;
205
+
206
+ if ( p->b_type == begin->b_type ) {
207
+ if ( p->b_count == level ) /* exact match */
208
+ return i-go;
209
+
210
+ if ( p->b_count > 2 ) /* fuzzy match */
211
+ return i-go;
212
+ }
213
+ }
214
+ return EOF;
215
+ }
216
+
217
+
218
+
219
+ static struct emtags {
220
+ char open[10];
221
+ char close[10];
222
+ int size;
223
+ } emtags[] = { { "<em>" , "</em>", 5 }, { "<strong>", "</strong>", 9 } };
224
+
225
+
226
+ static void
227
+ emclose(Cstring *s, int level)
228
+ {
229
+ PREFIX(*s, emtags[level-1].close, emtags[level-1].size);
230
+ }
231
+
232
+
233
+ static void
234
+ emopen(Cstring *s, int level)
235
+ {
236
+ SUFFIX(*s, emtags[level-1].open, emtags[level-1].size-1);
237
+ }
238
+
239
+
240
+ /* emmatch()
241
+ */
242
+ static void
243
+ emmatch(MMIOT *f, int go)
244
+ {
245
+ block *start = &T(f->Q)[go], *end;
246
+ int e, e2, i, match;
247
+
248
+ while ( start->b_count ) {
249
+ switch (start->b_count) {
250
+ case 2: e = empair(f,go,match=2);
251
+ if ( e != EOF ) break;
252
+ case 1: e = empair(f,go,match=1); break;
253
+ default:
254
+ e = empair(f,go,1);
255
+ e2= empair(f,go,2);
256
+
257
+ if ( e == EOF || ((e2 != EOF) && (e2 >= e)) ) {
258
+ e = e2;
259
+ match = 2;
260
+ }
261
+ else
262
+ match = 1;
263
+ }
264
+ if ( e != EOF ) {
265
+ end = &T(f->Q)[go+e];
266
+ emclose(&end->b_post, match);
267
+ emopen(&start->b_text, match);
268
+ end->b_count -= match;
269
+ }
270
+ else {
271
+ for (i=0; i < match; i++)
272
+ EXPAND(start->b_text) = start->b_char;
273
+ }
274
+
275
+ start->b_count -= match;
276
+ }
277
+ }
278
+
279
+
280
+ /* ___mkd_emblock()
281
+ */
282
+ void
283
+ ___mkd_emblock(MMIOT *f)
284
+ {
285
+ int i;
286
+ block *p;
287
+
288
+ for (i=0; i < S(f->Q); i++) {
289
+ p = &T(f->Q)[i];
290
+
291
+ if ( p->b_type != bTEXT ) emmatch(f, i);
292
+
293
+ if ( S(p->b_post) ) { SUFFIX(f->out, T(p->b_post), S(p->b_post));
294
+ DELETE(p->b_post); }
295
+ if ( S(p->b_text) ) { SUFFIX(f->out, T(p->b_text), S(p->b_text));
296
+ DELETE(p->b_text); }
297
+ }
298
+ S(f->Q) = 0;
299
+ }
300
+
301
+
302
+ /* generate html from a markup fragment
303
+ */
304
+ void
305
+ ___mkd_reparse(char *bfr, int size, int flags, MMIOT *f)
306
+ {
307
+ MMIOT sub;
308
+
309
+ ___mkd_initmmiot(&sub, f->footnotes);
310
+
311
+ sub.flags = f->flags | flags;
312
+ sub.base = f->base;
313
+
314
+ push(bfr, size, &sub);
315
+ EXPAND(sub.in) = 0;
316
+ S(sub.in)--;
317
+
318
+ text(&sub);
319
+ ___mkd_emblock(&sub);
320
+
321
+ Qwrite(T(sub.out), S(sub.out), f);
322
+
323
+ ___mkd_freemmiot(&sub, f->footnotes);
324
+ }
325
+
326
+
327
+ /*
328
+ * write out a url, escaping problematic characters
329
+ */
330
+ static void
331
+ puturl(char *s, int size, MMIOT *f)
332
+ {
333
+ unsigned char c;
334
+
335
+ while ( size-- > 0 ) {
336
+ c = *s++;
337
+
338
+ if ( c == '&' )
339
+ Qstring("&amp;", f);
340
+ else if ( c == '<' )
341
+ Qstring("&lt;", f);
342
+ else if ( isalnum(c) || ispunct(c) )
343
+ Qchar(c, f);
344
+ else
345
+ Qprintf(f, "%%%02X", c);
346
+ }
347
+ }
348
+
349
+
350
+ /* advance forward until the next character is not whitespace
351
+ */
352
+ static int
353
+ eatspace(MMIOT *f)
354
+ {
355
+ int c;
356
+
357
+ for ( ; ((c=peek(f, 1)) != EOF) && isspace(c); pull(f) )
358
+ ;
359
+ return c;
360
+ }
361
+
362
+
363
+ /* (match (a (nested (parenthetical (string.)))))
364
+ */
365
+ static int
366
+ parenthetical(int in, int out, MMIOT *f)
367
+ {
368
+ int size, indent, c;
369
+
370
+ for ( indent=1,size=0; indent; size++ ) {
371
+ if ( (c = pull(f)) == EOF )
372
+ return EOF;
373
+ else if ( c == in )
374
+ ++indent;
375
+ else if ( c == out )
376
+ --indent;
377
+ }
378
+ return size-1;
379
+ }
380
+
381
+
382
+ /* extract a []-delimited label from the input stream.
383
+ */
384
+ static char *
385
+ linkylabel(MMIOT *f, int *sizep)
386
+ {
387
+ char *ptr = cursor(f);
388
+
389
+ if ( (*sizep = parenthetical('[',']',f)) != EOF )
390
+ return ptr;
391
+ return 0;
392
+ }
393
+
394
+
395
+ /* extract a (-prefixed url from the input stream.
396
+ * the label is either of the format `<link>`, where I
397
+ * extract until I find a >, or it is of the format
398
+ * `text`, where I extract until I reach a ')' or
399
+ * whitespace.
400
+ */
401
+ static char*
402
+ linkyurl(MMIOT *f, int *sizep)
403
+ {
404
+ int size = 0;
405
+ char *ptr;
406
+ int c;
407
+
408
+ if ( (c = eatspace(f)) == EOF )
409
+ return 0;
410
+
411
+ ptr = cursor(f);
412
+
413
+ if ( c == '<' ) {
414
+ pull(f);
415
+ ptr++;
416
+ if ( (size = parenthetical('<', '>', f)) == EOF )
417
+ return 0;
418
+ }
419
+ else {
420
+ for ( ; ((c=pull(f)) != ')') && !isspace(c); size++)
421
+ if ( c == EOF ) return 0;
422
+ if ( c == ')' )
423
+ shift(f, -1);
424
+ }
425
+ *sizep = size;
426
+ return ptr;
427
+ }
428
+
429
+
430
+ /* extract a =HHHxWWW size from the input stream
431
+ */
432
+ static int
433
+ linkysize(MMIOT *f, int *heightp, int *widthp)
434
+ {
435
+ int height=0, width=0;
436
+ int c;
437
+
438
+ *heightp = 0;
439
+ *widthp = 0;
440
+
441
+ if ( (c = eatspace(f)) != '=' )
442
+ return (c != EOF);
443
+ pull(f); /* eat '=' */
444
+
445
+ for ( c = pull(f); isdigit(c); c = pull(f))
446
+ width = (width * 10) + (c - '0');
447
+
448
+ if ( c == 'x' ) {
449
+ for ( c = pull(f); isdigit(c); c = pull(f))
450
+ height = (height*10) + (c - '0');
451
+
452
+ if ( c != EOF ) {
453
+ if ( !isspace(c) ) shift(f, -1);
454
+ *heightp = height;
455
+ *widthp = width;
456
+ return 1;
457
+ }
458
+ }
459
+ return 0;
460
+ }
461
+
462
+
463
+ /* extract a )-terminated title from the input stream.
464
+ */
465
+ static char*
466
+ linkytitle(MMIOT *f, int *sizep)
467
+ {
468
+ int countq=0, qc, c, size;
469
+ char *ret, *lastqc = 0;
470
+
471
+ eatspace(f);
472
+ if ( (qc=pull(f)) != '"' && qc != '\'' && qc != '(' )
473
+ return 0;
474
+
475
+ if ( qc == '(' ) qc = ')';
476
+
477
+ for ( ret = cursor(f); (c = pull(f)) != EOF; ) {
478
+ if ( (c == ')') && countq ) {
479
+ size = (lastqc ? lastqc : cursor(f)) - ret;
480
+ *sizep = size-1;
481
+ return ret;
482
+ }
483
+ else if ( c == qc ) {
484
+ lastqc = cursor(f);
485
+ countq++;
486
+ }
487
+ }
488
+ return 0;
489
+ }
490
+
491
+
492
+ /* look up (or construct) a footnote from the [xxx] link
493
+ * at the head of the stream.
494
+ */
495
+ static int
496
+ linkykey(int image, Footnote *val, MMIOT *f)
497
+ {
498
+ Footnote *ret;
499
+ Cstring mylabel;
500
+ int here;
501
+
502
+ memset(val, 0, sizeof *val);
503
+
504
+ if ( (T(val->tag) = linkylabel(f, &S(val->tag))) == 0 )
505
+ return 0;
506
+
507
+ here = mmiottell(f);
508
+ eatspace(f);
509
+ switch ( pull(f) ) {
510
+ case '(':
511
+ /* embedded link */
512
+ if ( (T(val->link) = linkyurl(f,&S(val->link))) == 0 )
513
+ return 0;
514
+
515
+ if ( image && !linkysize(f, &val->height, &val->width) )
516
+ return 0;
517
+
518
+ T(val->title) = linkytitle(f, &S(val->title));
519
+
520
+ return peek(f,0) == ')';
521
+
522
+ case '[': /* footnote links /as defined in the standard/ */
523
+ default: /* footnote links -- undocumented extension */
524
+ /* footnote link */
525
+ mylabel = val->tag;
526
+ if ( peek(f,0) == '[' ) {
527
+ if ( (T(val->tag) = linkylabel(f, &S(val->tag))) == 0 )
528
+ return 0;
529
+
530
+ if ( !S(val->tag) )
531
+ val->tag = mylabel;
532
+ }
533
+ else if ( f->flags & MKD_1_COMPAT )
534
+ break;
535
+ else
536
+ mmiotseek(f,here);
537
+
538
+ ret = bsearch(val, T(*f->footnotes), S(*f->footnotes),
539
+ sizeof *val, (stfu)__mkd_footsort);
540
+
541
+ if ( ret ) {
542
+ val->tag = mylabel;
543
+ val->link = ret->link;
544
+ val->title = ret->title;
545
+ val->height = ret->height;
546
+ val->width = ret->width;
547
+ return 1;
548
+ }
549
+ }
550
+ return 0;
551
+ }
552
+
553
+
554
+ /*
555
+ * all the tag types that linkylinky can produce are
556
+ * defined by this structure.
557
+ */
558
+ typedef struct linkytype {
559
+ char *pat;
560
+ int szpat;
561
+ char *link_pfx; /* tag prefix and link pointer (eg: "<a href="\"" */
562
+ char *link_sfx; /* link suffix (eg: "\"" */
563
+ int WxH; /* this tag allows width x height arguments */
564
+ char *text_pfx; /* text prefix (eg: ">" */
565
+ char *text_sfx; /* text suffix (eg: "</a>" */
566
+ int flags; /* reparse flags */
567
+ } linkytype;
568
+
569
+ static linkytype imaget = { 0, 0, "<img src=\"", "\"",
570
+ 1, " alt=\"", "\" />", DENY_IMG|INSIDE_TAG };
571
+ static linkytype linkt = { 0, 0, "<a href=\"", "\"",
572
+ 0, ">", "</a>", DENY_A };
573
+
574
+ /*
575
+ * pseudo-protocols for [][];
576
+ *
577
+ * id: generates <a id="link">tag</a>
578
+ * class: generates <span class="link">tag</span>
579
+ * raw: just dump the link without any processing
580
+ */
581
+ static linkytype specials[] = {
582
+ { "id:", 3, "<a id=\"", "\"", 0, ">", "</a>", 0 },
583
+ { "class:", 6, "<span class=\"", "\"", 0, ">", "</span>", 0 },
584
+ { "raw:", 4, 0, 0, 0, 0, 0, 0 },
585
+ } ;
586
+
587
+ #define NR(x) (sizeof x / sizeof x[0])
588
+
589
+ /* see if t contains one of our pseudo-protocols.
590
+ */
591
+ static linkytype *
592
+ extratag(Cstring t)
593
+ {
594
+ int i;
595
+ linkytype *r;
596
+
597
+ for ( i=0; i < NR(specials); i++ ) {
598
+ r = &specials[i];
599
+ if ( (S(t) > r->szpat) && (strncasecmp(T(t), r->pat, r->szpat) == 0) )
600
+ return r;
601
+ }
602
+ return 0;
603
+ }
604
+
605
+
606
+ /*
607
+ * process embedded links and images
608
+ */
609
+ static int
610
+ linkylinky(int image, MMIOT *f)
611
+ {
612
+ int start = mmiottell(f);
613
+ Footnote link;
614
+ linkytype *tag;
615
+
616
+ if ( !linkykey(image, &link, f) ) {
617
+ mmiotseek(f, start);
618
+ return 0;
619
+ }
620
+
621
+ if ( image )
622
+ tag = &imaget;
623
+ else if ( (f->flags & NO_PSEUDO_PROTO) || (tag = extratag(link.link)) == 0 )
624
+ tag = &linkt;
625
+
626
+ if ( f->flags & tag-> flags ) {
627
+ mmiotseek(f, start);
628
+ return 0;
629
+ }
630
+
631
+ if ( tag->link_pfx ) {
632
+ Qstring(tag->link_pfx, f);
633
+ if ( f->base && (T(link.link)[tag->szpat] == '/') )
634
+ puturl(f->base, strlen(f->base), f);
635
+ puturl(T(link.link) + tag->szpat, S(link.link) - tag->szpat, f);
636
+ Qstring(tag->link_sfx, f);
637
+
638
+ if ( tag->WxH && link.height && link.width ) {
639
+ Qprintf(f," height=\"%d\"", link.height);
640
+ Qprintf(f, " width=\"%d\"", link.width);
641
+ }
642
+
643
+ if ( S(link.title) ) {
644
+ Qstring(" title=\"", f);
645
+ ___mkd_reparse(T(link.title), S(link.title), INSIDE_TAG, f);
646
+ Qchar('"', f);
647
+ }
648
+
649
+ Qstring(tag->text_pfx, f);
650
+ ___mkd_reparse(T(link.tag), S(link.tag), tag->flags, f);
651
+ Qstring(tag->text_sfx, f);
652
+ }
653
+ else
654
+ Qwrite(T(link.link) + tag->szpat, S(link.link) - tag->szpat, f);
655
+
656
+ return 1;
657
+ }
658
+
659
+
660
+ /* write a character to output, doing text escapes ( & -> &amp;,
661
+ * > -> &gt; < -> &lt; )
662
+ */
663
+ static void
664
+ cputc(int c, MMIOT *f)
665
+ {
666
+ switch (c) {
667
+ case '&': Qstring("&amp;", f); break;
668
+ case '>': Qstring("&gt;", f); break;
669
+ case '<': Qstring("&lt;", f); break;
670
+ default : Qchar(c, f); break;
671
+ }
672
+ }
673
+
674
+
675
+ /*
676
+ * convert an email address to a string of nonsense
677
+ */
678
+ static void
679
+ mangle(char *s, int len, MMIOT *f)
680
+ {
681
+ while ( len-- > 0 ) {
682
+ Qstring("&#", f);
683
+ Qprintf(f, COINTOSS() ? "x%02x;" : "%02d;", *((unsigned char*)(s++)) );
684
+ }
685
+ }
686
+
687
+
688
+ /* before letting a tag through, validate against
689
+ * DENY_A and DENY_IMG
690
+ */
691
+ static int
692
+ forbidden_tag(MMIOT *f)
693
+ {
694
+ int c = toupper(peek(f, 1));
695
+
696
+ if ( f->flags & DENY_HTML )
697
+ return 1;
698
+
699
+ if ( c == 'A' && (f->flags & DENY_A) && !isthisalnum(f,2) )
700
+ return 1;
701
+ if ( c == 'I' && (f->flags & DENY_IMG)
702
+ && strncasecmp(cursor(f)+1, "MG", 2) == 0
703
+ && !isthisalnum(f,4) )
704
+ return 1;
705
+ return 0;
706
+ }
707
+
708
+
709
+
710
+ /* a < may be just a regular character, the start of an embedded html
711
+ * tag, or the start of an <automatic link>. If it's an automatic
712
+ * link, we also need to know if it's an email address because if it
713
+ * is we need to mangle it in our futile attempt to cut down on the
714
+ * spaminess of the rendered page.
715
+ */
716
+ static int
717
+ maybe_tag_or_link(MMIOT *f)
718
+ {
719
+ char *text;
720
+ int c, size, i;
721
+ int maybetag=1, maybeaddress=0;
722
+ int mailto;
723
+
724
+ if ( f->flags & INSIDE_TAG )
725
+ return 0;
726
+
727
+ for ( size=0; ((c = peek(f,size+1)) != '>') && !isspace(c); size++ ) {
728
+ if ( ! (c == '/' || isalnum(c) || c == '~') )
729
+ maybetag=0;
730
+ if ( c == '@' )
731
+ maybeaddress=1;
732
+ else if ( c == EOF )
733
+ return 0;
734
+ }
735
+
736
+ if ( size == 0 )
737
+ return 0;
738
+
739
+ if ( maybetag || (size >= 3 && strncmp(cursor(f), "!--", 3) == 0) ) {
740
+ Qstring(forbidden_tag(f) ? "&lt;" : "<", f);
741
+ while ( ((c = peek(f, 1)) != EOF) && (c != '>') )
742
+ cputc(pull(f), f);
743
+ return 1;
744
+ }
745
+
746
+ if ( f->flags & DENY_A ) return 0;
747
+
748
+ text = cursor(f);
749
+ shift(f, size+1);
750
+
751
+ for ( i=0; i < SZAUTOPREFIX; i++ )
752
+ if ( strncasecmp(text, autoprefix[i], strlen(autoprefix[i])) == 0 ) {
753
+ Qstring("<a href=\"", f);
754
+ puturl(text,size,f);
755
+ Qstring("\">", f);
756
+ puturl(text,size,f);
757
+ Qstring("</a>", f);
758
+ return 1;
759
+ }
760
+ if ( maybeaddress ) {
761
+
762
+ Qstring("<a href=\"", f);
763
+ if ( (size > 7) && strncasecmp(text, "mailto:", 7) == 0 )
764
+ mailto = 7;
765
+ else {
766
+ mailto = 0;
767
+ /* supply a mailto: protocol if one wasn't attached */
768
+ mangle("mailto:", 7, f);
769
+ }
770
+
771
+ mangle(text, size, f);
772
+ Qstring("\">", f);
773
+ mangle(text+mailto, size-mailto, f);
774
+ Qstring("</a>", f);
775
+ return 1;
776
+ }
777
+
778
+ shift(f, -(size+1));
779
+ return 0;
780
+ } /* maybe_tag_or_link */
781
+
782
+
783
+ /* smartyquote code that's common for single and double quotes
784
+ */
785
+ static int
786
+ smartyquote(int *flags, char typeofquote, MMIOT *f)
787
+ {
788
+ int bit = (typeofquote == 's') ? 0x01 : 0x02;
789
+
790
+ if ( bit & (*flags) ) {
791
+ if ( isthisnonword(f,1) ) {
792
+ Qprintf(f, "&r%cquo;", typeofquote);
793
+ (*flags) &= ~bit;
794
+ return 1;
795
+ }
796
+ }
797
+ else if ( isthisnonword(f,-1) && peek(f,1) != EOF ) {
798
+ Qprintf(f, "&l%cquo;", typeofquote);
799
+ (*flags) |= bit;
800
+ return 1;
801
+ }
802
+ return 0;
803
+ }
804
+
805
+
806
+ static int
807
+ islike(MMIOT *f, char *s)
808
+ {
809
+ int len;
810
+ int i;
811
+
812
+ if ( s[0] == '<' ) {
813
+ if ( !isthisnonword(f, -1) )
814
+ return 0;
815
+ ++s;
816
+ }
817
+
818
+ if ( !(len = strlen(s)) )
819
+ return 0;
820
+
821
+ if ( s[len-1] == '>' ) {
822
+ if ( !isthisnonword(f,len-1) )
823
+ return 0;
824
+ len--;
825
+ }
826
+
827
+ for (i=1; i < len; i++)
828
+ if (tolower(peek(f,i)) != s[i])
829
+ return 0;
830
+ return 1;
831
+ }
832
+
833
+
834
+ static struct smarties {
835
+ char c0;
836
+ char *pat;
837
+ char *entity;
838
+ int shift;
839
+ } smarties[] = {
840
+ { '\'', "'s>", "rsquo", 0 },
841
+ { '\'', "'t>", "rsquo", 0 },
842
+ { '\'', "'re>", "rsquo", 0 },
843
+ { '\'', "'ll>", "rsquo", 0 },
844
+ { '-', "--", "mdash", 1 },
845
+ { '-', "<->", "ndash", 0 },
846
+ { '.', "...", "hellip", 2 },
847
+ { '.', ". . .", "hellip", 4 },
848
+ { '(', "(c)", "copy", 2 },
849
+ { '(', "(r)", "reg", 2 },
850
+ { '(', "(tm)", "trade", 3 },
851
+ { '3', "<3/4>", "frac34", 2 },
852
+ { '3', "<3/4ths>", "frac34", 2 },
853
+ { '1', "<1/2>", "frac12", 2 },
854
+ { '1', "<1/4>", "frac14", 2 },
855
+ { '1', "<1/4th>", "frac14", 2 },
856
+ { '&', "&#0;", 0, 3 },
857
+ } ;
858
+ #define NRSMART ( sizeof smarties / sizeof smarties[0] )
859
+
860
+
861
+ /* Smarty-pants-style chrome for quotes, -, ellipses, and (r)(c)(tm)
862
+ */
863
+ static int
864
+ smartypants(int c, int *flags, MMIOT *f)
865
+ {
866
+ int i;
867
+
868
+ if ( f->flags & DENY_SMARTY )
869
+ return 0;
870
+
871
+ for ( i=0; i < NRSMART; i++)
872
+ if ( (c == smarties[i].c0) && islike(f, smarties[i].pat) ) {
873
+ if ( smarties[i].entity )
874
+ Qprintf(f, "&%s;", smarties[i].entity);
875
+ shift(f, smarties[i].shift);
876
+ return 1;
877
+ }
878
+
879
+ switch (c) {
880
+ case '<' : return 0;
881
+ case '\'': if ( smartyquote(flags, 's', f) ) return 1;
882
+ break;
883
+
884
+ case '"': if ( smartyquote(flags, 'd', f) ) return 1;
885
+ break;
886
+
887
+ case '`': if ( peek(f, 1) == '`' ) {
888
+ int j = 2;
889
+
890
+ while ( (c=peek(f,j)) != EOF ) {
891
+ if ( c == '\\' )
892
+ j += 2;
893
+ else if ( c == '`' )
894
+ break;
895
+ else if ( c == '\'' && peek(f, j+1) == '\'' ) {
896
+ Qstring("&ldquo;", f);
897
+ ___mkd_reparse(cursor(f)+1, j-2, 0, f);
898
+ Qstring("&rdquo;", f);
899
+ shift(f,j+1);
900
+ return 1;
901
+ }
902
+ else ++j;
903
+ }
904
+
905
+ }
906
+ break;
907
+ }
908
+ return 0;
909
+ } /* smartypants */
910
+
911
+
912
+ #define tag_text(f) (f->flags & INSIDE_TAG)
913
+
914
+
915
+ static void
916
+ text(MMIOT *f)
917
+ {
918
+ int c, j;
919
+ int rep;
920
+ int smartyflags = 0;
921
+
922
+ while ( (c = pull(f)) != EOF ) {
923
+ if ( smartypants(c, &smartyflags, f) )
924
+ continue;
925
+ switch (c) {
926
+ case 0: break;
927
+
928
+ case '>': if ( tag_text(f) )
929
+ Qstring("&gt;", f);
930
+ else
931
+ Qchar(c, f);
932
+ break;
933
+
934
+ case '"': if ( tag_text(f) )
935
+ Qstring("&quot;", f);
936
+ else
937
+ Qchar(c, f);
938
+ break;
939
+
940
+ case '!': if ( peek(f,1) == '[' ) {
941
+ pull(f);
942
+ if ( tag_text(f) || !linkylinky(1, f) )
943
+ Qstring("![", f);
944
+ }
945
+ else
946
+ Qchar(c, f);
947
+ break;
948
+ case '[': if ( tag_text(f) || !linkylinky(0, f) )
949
+ Qchar(c, f);
950
+ break;
951
+ #if SUPERSCRIPT
952
+ /* A^B -> A<sup>B</sup> */
953
+ case '^': if ( (f->flags & (STRICT|INSIDE_TAG)) || isthisspace(f,-1) || isthisspace(f,1) )
954
+ Qchar(c,f);
955
+ else {
956
+ char *sup = cursor(f);
957
+ int len = 0;
958
+ Qstring("<sup>",f);
959
+ while ( !isthisspace(f,1+len) ) {
960
+ ++len;
961
+ }
962
+ shift(f,len);
963
+ ___mkd_reparse(sup, len, 0, f);
964
+ Qstring("</sup>", f);
965
+ }
966
+ break;
967
+ #endif
968
+ case '_':
969
+ #if RELAXED_EMPHASIS
970
+ /* Underscores don't count if they're in the middle of a word */
971
+ if ( (!(f->flags & STRICT))
972
+ && ((isthisspace(f,-1) && isthisspace(f,1))
973
+ || (isthisalnum(f,-1) && isthisalnum(f,1))) ){
974
+ Qchar(c, f);
975
+ break;
976
+ }
977
+ /* else fall into the regular old emphasis case */
978
+ #endif
979
+ case '*': if ( tag_text(f) )
980
+ Qchar(c, f);
981
+ else {
982
+ for (rep = 1; peek(f,1) == c; pull(f) )
983
+ ++rep;
984
+ Qem(f,c,rep);
985
+ }
986
+ break;
987
+
988
+ case '`': if ( tag_text(f) || !iscodeblock(f) )
989
+ Qchar(c, f);
990
+ else {
991
+ Qstring("<code>", f);
992
+ if ( peek(f, 1) == '`' ) {
993
+ pull(f);
994
+ code(2, f);
995
+ }
996
+ else
997
+ code(1, f);
998
+ Qstring("</code>", f);
999
+ }
1000
+ break;
1001
+
1002
+ case '\\': switch ( c = pull(f) ) {
1003
+ case '&': Qstring("&amp;", f);
1004
+ break;
1005
+ case '<': Qstring("&lt;", f);
1006
+ break;
1007
+ case '\\':
1008
+ case '>': case '#': case '.': case '-':
1009
+ case '+': case '{': case '}': case ']':
1010
+ case '(': case ')': case '"': case '\'':
1011
+ case '!': case '[': case '*': case '_':
1012
+ case '`': Qchar(c, f);
1013
+ break;
1014
+ default:
1015
+ Qchar('\\', f);
1016
+ if ( c != EOF )
1017
+ shift(f,-1);
1018
+ break;
1019
+ }
1020
+ break;
1021
+
1022
+ case '<': if ( !maybe_tag_or_link(f) )
1023
+ Qstring("&lt;", f);
1024
+ break;
1025
+
1026
+ case '&': j = (peek(f,1) == '#' ) ? 2 : 1;
1027
+ while ( isthisalnum(f,j) )
1028
+ ++j;
1029
+
1030
+ if ( peek(f,j) != ';' )
1031
+ Qstring("&amp;", f);
1032
+ else
1033
+ Qchar(c, f);
1034
+ break;
1035
+
1036
+ default: Qchar(c, f);
1037
+ break;
1038
+ }
1039
+ }
1040
+ /* truncate the input string after we've finished processing it */
1041
+ S(f->in) = f->isp = 0;
1042
+ } /* text */
1043
+
1044
+
1045
+ static int
1046
+ iscodeblock(MMIOT *f)
1047
+ {
1048
+ int i=1, single = 1, c;
1049
+
1050
+ if ( peek(f,i) == '`' ) {
1051
+ single=0;
1052
+ i++;
1053
+ }
1054
+ while ( (c=peek(f,i)) != EOF ) {
1055
+ if ( (c == '`') && (single || peek(f,i+1) == '`') )
1056
+ return 1;
1057
+ else if ( c == '\\' )
1058
+ i++;
1059
+ i++;
1060
+ }
1061
+ return 0;
1062
+
1063
+ }
1064
+
1065
+ static int
1066
+ endofcode(int escape, int offset, MMIOT *f)
1067
+ {
1068
+ switch (escape) {
1069
+ case 2: if ( peek(f, offset+1) == '`' ) {
1070
+ shift(f,1);
1071
+ case 1: shift(f,offset);
1072
+ return 1;
1073
+ }
1074
+ default:return 0;
1075
+ }
1076
+ }
1077
+
1078
+
1079
+ /* the only characters that have special meaning in a code block are
1080
+ * `<' and `&' , which are /always/ expanded to &lt; and &amp;
1081
+ */
1082
+ static void
1083
+ code(int escape, MMIOT *f)
1084
+ {
1085
+ int c;
1086
+
1087
+ if ( escape && (peek(f,1) == ' ') )
1088
+ shift(f,1);
1089
+
1090
+ while ( (c = pull(f)) != EOF ) {
1091
+ switch (c) {
1092
+ case ' ': if ( peek(f,1) == '`' && endofcode(escape, 1, f) )
1093
+ return;
1094
+ Qchar(c, f);
1095
+ break;
1096
+
1097
+ case '`': if ( endofcode(escape, 0, f) )
1098
+ return;
1099
+ Qchar(c, f);
1100
+ break;
1101
+
1102
+ case '\\': cputc(c, f);
1103
+ if ( peek(f,1) == '>' || (c = pull(f)) == EOF )
1104
+ break;
1105
+
1106
+ default: cputc(c, f);
1107
+ break;
1108
+ }
1109
+ }
1110
+ } /* code */
1111
+
1112
+
1113
+ /* print a header block
1114
+ */
1115
+ static void
1116
+ printheader(Paragraph *pp, MMIOT *f)
1117
+ {
1118
+ Qprintf(f, "<h%d", pp->hnumber);
1119
+ if ( f->flags & TOC ) {
1120
+ Qprintf(f, " id=\"", pp->hnumber);
1121
+ mkd_string_to_anchor(T(pp->text->text), S(pp->text->text), Qchar, f);
1122
+ Qchar('"', f);
1123
+ }
1124
+ Qchar('>', f);
1125
+ push(T(pp->text->text), S(pp->text->text), f);
1126
+ text(f);
1127
+ Qprintf(f, "</h%d>", pp->hnumber);
1128
+ }
1129
+
1130
+
1131
+ static int
1132
+ printblock(Paragraph *pp, MMIOT *f)
1133
+ {
1134
+ Line *t = pp->text;
1135
+ static char *Begin[] = { "", "<p>", "<center>" };
1136
+ static char *End[] = { "", "</p>","</center>" };
1137
+
1138
+ while (t) {
1139
+ if ( S(t->text) ) {
1140
+ if ( S(t->text) > 2 && T(t->text)[S(t->text)-2] == ' '
1141
+ && T(t->text)[S(t->text)-1] == ' ') {
1142
+ push(T(t->text), S(t->text)-2, f);
1143
+ push("<br/>\n", 6, f);
1144
+ }
1145
+ else {
1146
+ ___mkd_tidy(t);
1147
+ push(T(t->text), S(t->text), f);
1148
+ if ( t->next )
1149
+ push("\n", 1, f);
1150
+ }
1151
+ }
1152
+ t = t->next;
1153
+ }
1154
+ Qstring(Begin[pp->align], f);
1155
+ text(f);
1156
+ Qstring(End[pp->align], f);
1157
+ return 1;
1158
+ }
1159
+
1160
+
1161
+ static void
1162
+ printcode(Line *t, MMIOT *f)
1163
+ {
1164
+ int blanks;
1165
+
1166
+ for ( blanks = 0; t ; t = t->next )
1167
+ if ( S(t->text) > t->dle ) {
1168
+ while ( blanks ) {
1169
+ push("\n", 1, f);
1170
+ --blanks;
1171
+ }
1172
+ push(T(t->text), S(t->text), f);
1173
+ push("\n", 1, f);
1174
+ }
1175
+ else blanks++;
1176
+
1177
+ Qstring("<pre><code>", f);
1178
+ code(0, f);
1179
+ Qstring("</code></pre>", f);
1180
+ }
1181
+
1182
+
1183
+ static void
1184
+ printhtml(Line *t, MMIOT *f)
1185
+ {
1186
+ int blanks;
1187
+
1188
+ for ( blanks=0; t ; t = t->next )
1189
+ if ( S(t->text) ) {
1190
+ for ( ; blanks; --blanks )
1191
+ Qchar('\n', f);
1192
+
1193
+ Qwrite(T(t->text), S(t->text), f);
1194
+ Qchar('\n', f);
1195
+ }
1196
+ else
1197
+ blanks++;
1198
+ }
1199
+
1200
+
1201
+ static void
1202
+ htmlify(Paragraph *p, char *block, char *arguments, MMIOT *f)
1203
+ {
1204
+ ___mkd_emblock(f);
1205
+ if ( block )
1206
+ Qprintf(f, arguments ? "<%s %s>" : "<%s>", block, arguments);
1207
+ ___mkd_emblock(f);
1208
+
1209
+ while (( p = display(p, f) )) {
1210
+ ___mkd_emblock(f);
1211
+ Qstring("\n\n", f);
1212
+ }
1213
+
1214
+ if ( block )
1215
+ Qprintf(f, "</%s>", block);
1216
+ ___mkd_emblock(f);
1217
+ }
1218
+
1219
+
1220
+ #if DL_TAG_EXTENSION
1221
+ static void
1222
+ definitionlist(Paragraph *p, MMIOT *f)
1223
+ {
1224
+ Line *tag;
1225
+
1226
+ if ( p ) {
1227
+ Qstring("<dl>\n", f);
1228
+
1229
+ for ( ; p ; p = p->next) {
1230
+ for ( tag = p->text; tag; tag = tag->next ) {
1231
+ Qstring("<dt>", f);
1232
+ ___mkd_reparse(T(tag->text), S(tag->text), 0, f);
1233
+ Qstring("</dt>\n", f);
1234
+ }
1235
+
1236
+ htmlify(p->down, "dd", p->ident, f);
1237
+ }
1238
+
1239
+ Qstring("</dl>", f);
1240
+ }
1241
+ }
1242
+ #endif
1243
+
1244
+
1245
+ static void
1246
+ listdisplay(int typ, Paragraph *p, MMIOT* f)
1247
+ {
1248
+ if ( p ) {
1249
+ Qprintf(f, "<%cl", (typ==UL)?'u':'o');
1250
+ if ( typ == AL )
1251
+ Qprintf(f, " type=a");
1252
+ Qprintf(f, ">\n");
1253
+
1254
+ for ( ; p ; p = p->next ) {
1255
+ htmlify(p->down, "li", p->ident, f);
1256
+ Qchar('\n', f);
1257
+ }
1258
+
1259
+ Qprintf(f, "</%cl>\n", (typ==UL)?'u':'o');
1260
+ }
1261
+ }
1262
+
1263
+
1264
+ /* dump out a Paragraph in the desired manner
1265
+ */
1266
+ static Paragraph*
1267
+ display(Paragraph *p, MMIOT *f)
1268
+ {
1269
+ if ( !p ) return 0;
1270
+
1271
+ switch ( p->typ ) {
1272
+ case STYLE:
1273
+ case WHITESPACE:
1274
+ break;
1275
+
1276
+ case HTML:
1277
+ printhtml(p->text, f);
1278
+ break;
1279
+
1280
+ case CODE:
1281
+ printcode(p->text, f);
1282
+ break;
1283
+
1284
+ case QUOTE:
1285
+ htmlify(p->down, p->ident ? "div" : "blockquote", p->ident, f);
1286
+ break;
1287
+
1288
+ case UL:
1289
+ case OL:
1290
+ case AL:
1291
+ listdisplay(p->typ, p->down, f);
1292
+ break;
1293
+
1294
+ #if DL_TAG_EXTENSION
1295
+ case DL:
1296
+ definitionlist(p->down, f);
1297
+ break;
1298
+ #endif
1299
+
1300
+ case HR:
1301
+ Qstring("<hr />", f);
1302
+ break;
1303
+
1304
+ case HDR:
1305
+ printheader(p, f);
1306
+ break;
1307
+
1308
+ default:
1309
+ printblock(p, f);
1310
+ break;
1311
+ }
1312
+ return p->next;
1313
+ }
1314
+
1315
+
1316
+ /*
1317
+ * dump out stylesheet sections.
1318
+ */
1319
+ static int
1320
+ stylesheets(Paragraph *p, FILE *f)
1321
+ {
1322
+ Line* q;
1323
+
1324
+ for ( ; p ; p = p->next ) {
1325
+ if ( p->typ == STYLE ) {
1326
+ for ( q = p->text; q ; q = q->next )
1327
+ if ( fwrite(T(q->text), S(q->text), 1, f) == 1 )
1328
+ putc('\n', f);
1329
+ else
1330
+ return EOF;
1331
+ }
1332
+ if ( p->down && (stylesheets(p->down, f) == EOF) )
1333
+ return EOF;
1334
+ }
1335
+ return 0;
1336
+ }
1337
+
1338
+
1339
+ /* return a pointer to the compiled markdown
1340
+ * document.
1341
+ */
1342
+ int
1343
+ mkd_document(Document *p, char **res)
1344
+ {
1345
+ if ( p && p->compiled ) {
1346
+ if ( ! p->html ) {
1347
+ htmlify(p->code, 0, 0, p->ctx);
1348
+ p->html = 1;
1349
+ }
1350
+
1351
+ *res = T(p->ctx->out);
1352
+ return S(p->ctx->out);
1353
+ }
1354
+ return EOF;
1355
+ }
1356
+
1357
+
1358
+ /* public interface for ___mkd_reparse()
1359
+ */
1360
+ int
1361
+ mkd_text(char *bfr, int size, FILE *output, int flags)
1362
+ {
1363
+ MMIOT f;
1364
+
1365
+ ___mkd_initmmiot(&f, 0);
1366
+ f.flags = flags & USER_FLAGS;
1367
+
1368
+ ___mkd_reparse(bfr, size, 0, &f);
1369
+ ___mkd_emblock(&f);
1370
+ if ( flags & CDATA_OUTPUT )
1371
+ ___mkd_xml(T(f.out), S(f.out), output);
1372
+ else
1373
+ fwrite(T(f.out), S(f.out), 1, output);
1374
+
1375
+ ___mkd_freemmiot(&f, 0);
1376
+ return 0;
1377
+ }
1378
+
1379
+
1380
+ /* dump any embedded styles
1381
+ */
1382
+ int
1383
+ mkd_style(Document *d, FILE *f)
1384
+ {
1385
+ if ( d && d->compiled )
1386
+ return stylesheets(d->code, f);
1387
+ return EOF;
1388
+ }
1389
+