chaptastic-rdiscount 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ext/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
+