chaptastic-rdiscount 1.4.1

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