html: put a new line after body
[ctxt.git] / fmt.c
bloba87ff1a34de4d0d7ce8fb199eab035c53139e35e
1 #include <ctype.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "ctxt.h"
5 #include "util.h"
7 #define MAXLINE 1024
9 static struct marker {
10 int beg;
11 int end;
12 int id;
13 } markers[] = {
14 {'*', '*', M_EMPH},
15 {'$', '$', M_MATH},
16 {'%', '%', M_RAW},
17 {'[', ']', M_FOOT},
18 {'|', '|', M_LINK}
21 static struct block {
22 int sign;
23 int id;
24 } blocks[] = {
25 {'$', B_MATH},
26 {'%', B_RAW},
27 {0, B_PRE}
30 struct fmt {
31 struct doc *doc;
32 struct txt *txt;
33 struct fmt_ops *ops;
34 int level;
37 static struct fmt *fmt_alloc(struct doc *doc, struct txt *txt,
38 struct fmt_ops *ops)
40 struct fmt *fmt = xmalloc(sizeof(struct fmt));
41 fmt->doc = doc;
42 fmt->txt = txt;
43 fmt->ops = ops;
44 fmt->level = 0;
45 return fmt;
48 static void fmt_free(struct fmt *fmt)
50 free(fmt);
53 static char *fmt_line(struct fmt *fmt, int line)
55 char *s = txt_line(fmt->txt, line);
56 int i;
57 for (i = 0; i < fmt->level && s && *s; i++)
58 s++;
59 return s;
62 static struct marker *marker_char(char c)
64 int i;
65 for (i = 0; i < LENGTH(markers); i++)
66 if (markers[i].beg == c)
67 return &markers[i];
68 return NULL;
71 static char *possible_inline(char *s)
73 char *home = s;
74 struct marker *m = NULL;
75 while (*s) {
76 if (*s == '\\')
77 return s;
78 if ((s == home || isspace(*(s - 1))) && !isspace(*(s + 1)))
79 if ((m = marker_char(*s)))
80 return s;
81 s++;
83 return NULL;
86 static void fillbuf(char *buf, int len, char *beg, char *end)
88 int n = len - 1 < end - beg ? len - 1 : end - beg;
89 memcpy(buf, beg, n);
90 buf[n] = '\0';
93 static char *fmt_put_inline(struct fmt *fmt, char *s)
95 char buf[MAXLINE];
96 char *r = NULL;
97 struct marker *marker;
98 if (*s == '\\') {
99 if (marker_char(*(s + 1))) {
100 fillbuf(buf, LENGTH(buf), s + 1, s + 2);
101 fmt->ops->put(fmt->doc, buf);
102 return s + 2;
104 return NULL;
106 marker = marker_char(*s);
107 if (marker && marker->end)
108 r = strchr(s + 1, marker->end);
109 if (marker && !marker->end)
110 for (r = s + 1; isalnum(*r); r++);
111 if (r) {
112 fillbuf(buf, LENGTH(buf), s + 1, r);
113 fmt->ops->put_txt(fmt->doc, buf, marker->id);
114 if (marker->end)
115 r++;
117 return r;
120 static void put_text(struct fmt *fmt, char *s)
122 char buf[MAXLINE];
123 char *done = s;
124 while (*s) {
125 char *r = possible_inline(s);
126 if (!r)
127 break;
128 fillbuf(buf, LENGTH(buf), done, r);
129 fmt->ops->put(fmt->doc, buf);
130 s = done = r;
131 if ((r = fmt_put_inline(fmt, r)))
132 s = done = r;
133 else
134 s++;
136 fmt->ops->put(fmt->doc, done);
139 static void raw_line(struct fmt *fmt, char *s)
141 fmt->ops->put(fmt->doc, s);
144 static void put_line(struct fmt *fmt, int n)
146 put_text(fmt, fmt_line(fmt, n));
147 put_text(fmt, "\n");
150 static void put_lines(struct fmt *fmt, int beg, int end)
152 int i;
153 for (i = beg; i < end; i++)
154 put_line(fmt, i);
157 static void raw_lines(struct fmt *fmt, int beg, int end)
159 int i;
160 for (i = beg; i < end; i++) {
161 raw_line(fmt, fmt_line(fmt, i));
162 raw_line(fmt, "\n");
166 static int indents(char *s)
168 char *r = s;
169 while (isspace(*r))
170 r++;
171 return r - s;
174 static int islist(char *first, char *line)
176 char *signs = "*-+";
177 if (!line) {
178 if (!first || strlen(first) < 2)
179 return 0;
180 if (first[1] != ' ' || !strchr(signs, first[0]))
181 return 0;
182 return 1;
184 if (strlen(line) < 2)
185 return 0;
186 return line[0] == first[0] && line[1] == first[1];
189 static void fmt_handle(struct fmt *fmt, int beg, int end, int level);
191 static int fmt_head(struct fmt *fmt, int beg, int end)
193 char *line = fmt_line(fmt, beg);
194 char *next = fmt_line(fmt, beg + 1);
195 char c;
196 char *s = next;
197 char *signs = "=-~";
198 if (!next || !*line || beg == end || fmt->level)
199 return 0;
200 c = *next;
201 if (!c || !strchr(signs, *next))
202 return 0;
203 while (*++s)
204 if (*s != c)
205 return 0;
206 fmt->ops->head_beg(fmt->doc, strchr(signs, *next) - signs);
207 put_text(fmt, line);
208 fmt->ops->head_end(fmt->doc, strchr(signs, *next) - signs);
209 return 2;
212 static int par_end(struct fmt *fmt, int beg, int end)
214 int i = beg;
215 char *line;
216 while (i < end && (line = fmt_line(fmt, i))) {
217 if (!*line || indents(line) || islist(line, NULL))
218 break;
219 i++;
221 return i;
224 static int fmt_par(struct fmt *fmt, int beg, int end)
226 int i;
227 if (indents(fmt_line(fmt, beg)))
228 return 0;
229 fmt->ops->par_beg(fmt->doc);
230 i = par_end(fmt, beg, end);
231 put_lines(fmt, beg, i);
232 fmt->ops->par_end(fmt->doc);
233 return i - beg;
236 static int min(int a, int b)
238 return a < b ? a : b;
241 static int fmt_deindent(struct fmt *fmt, int n, int indent)
243 char *line;
244 int result = n;
245 while ((line = fmt_line(fmt, n))) {
246 if (*line && indents(line) < indent)
247 break;
248 n++;
249 if (*line)
250 result = n;
252 return result;
255 static int fmt_block(struct fmt *fmt, int beg, int end)
257 int level = indents(fmt_line(fmt, beg));
258 struct block *block = NULL;
259 int next;
260 int i;
261 char *line;
262 if (!level)
263 return 0;
264 next = min(end, fmt_deindent(fmt, beg + 1, level));
265 line = fmt_line(fmt, beg);
266 for (i = 0; i < LENGTH(blocks); i++) {
267 if (blocks[i].sign == line[level] && isspace(line[level + 1])) {
268 block = &blocks[i];
269 level += 2;
270 break;
272 if (!blocks[i].sign) {
273 block = &blocks[i];
274 break;
277 fmt->level += level;
278 fmt->ops->block_beg(fmt->doc, block->id);
279 raw_lines(fmt, beg, next);
280 fmt->ops->block_end(fmt->doc, block->id);
281 fmt->level -= level;
282 return next - beg;
285 static int fmt_list(struct fmt *fmt, int beg, int end)
287 int i = beg;
288 char *first = fmt_line(fmt, i);
289 char *line = first;
290 if (!islist(first, NULL))
291 return 0;
292 fmt->ops->list_beg(fmt->doc);
293 while ((line = fmt_line(fmt, i)) && islist(first, line)) {
294 int next = min(end, fmt_deindent(fmt, i + 1, 2));
295 int head = i;
296 fmt->ops->item_beg(fmt->doc);
298 fmt->level += 2;
299 i = par_end(fmt, i, next);
300 put_lines(fmt, head, i);
301 fmt->level -= 2;
303 fmt_handle(fmt, i, next, 2);
304 i = next;
305 fmt->ops->item_end(fmt->doc);
306 if (i == end)
307 break;
309 fmt->ops->list_end(fmt->doc);
310 return i - beg;
313 static int table_columns(char *line)
315 int n;
316 for (n = 0; *line; n++) {
317 while (*line == '\t')
318 line++;
319 while (*line && *line != '\t')
320 line++;
322 return n;
325 static void table_row(struct fmt *fmt, char *s)
327 char buf[MAXLINE];
328 if (*s == '=')
329 return;
330 fmt->ops->row_beg(fmt->doc);
331 while (*s) {
332 char *r = s;
333 while (*s && *s != '\t')
334 s++;
335 fmt->ops->entry_beg(fmt->doc);
336 fillbuf(buf, LENGTH(buf), r, s);
337 put_text(fmt, buf);
338 fmt->ops->entry_end(fmt->doc);
339 while (*s == '\t')
340 s++;
342 fmt->ops->row_end(fmt->doc);
345 static int fmt_table(struct fmt *fmt, int beg, int end)
347 int i;
348 int n;
349 if (*fmt_line(fmt, beg) != '=')
350 return 0;
351 n = table_columns(fmt_line(fmt, beg + 1));
352 fmt->ops->table_beg(fmt->doc, n);
353 for (i = beg + 1; i < end; i++) {
354 if (!fmt->level && !*fmt_line(fmt, i))
355 break;
356 table_row(fmt, fmt_line(fmt, i));
358 fmt->ops->table_end(fmt->doc);
359 return i - beg;
362 static int (*parts[])(struct fmt *fmt, int beg, int end) =
363 {fmt_head, fmt_list, fmt_table, fmt_block, fmt_par};
365 static void fmt_handle(struct fmt *fmt, int beg, int end, int level)
367 int line = beg;
368 int i;
369 fmt->level += level;
370 while (line < end) {
371 int c = 0;
372 if (!*fmt_line(fmt, line)) {
373 put_line(fmt, line++);
374 continue;
376 for (i = 0; i < LENGTH(parts); i++)
377 if ((c = parts[i](fmt, line, end)))
378 break;
379 line += c;
380 if (!c)
381 put_line(fmt, line++);
383 fmt->level -= level;
386 void format(struct doc *doc, struct txt *txt, struct fmt_ops *ops)
388 struct fmt *fmt = fmt_alloc(doc, txt, ops);
389 fmt->ops->doc_beg(doc);
390 fmt_handle(fmt, 0, txt->n, 0);
391 fmt->ops->doc_end(doc);
392 fmt_free(fmt);