major cleanup and improved block handling
[ctxt.git] / fmt.c
blobe83b349682d2aece5d2621761cdf4f862ace2abb
1 #include <ctype.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include "ctxt.h"
7 #define MAXLINE 1024
8 #define LENGTH(vars) (sizeof(vars) / sizeof(vars[0]))
10 static char *markers[] = {"**", "$$", "%%", "[]", "||", "//", "''", "``"};
11 static char *blocks[][2] = {
12 {".EQ", ".EN"}, {".PS", ".PE"}, {".TS", ".TE"},
13 {".DS", ".DE"}, {".G1", ".G2"}, {".[", ".]"},
16 struct fmt {
17 struct doc *doc;
18 struct txt *txt;
19 struct fmt_ops *ops;
20 int level;
23 static struct fmt *fmt_alloc(struct doc *doc, struct txt *txt,
24 struct fmt_ops *ops)
26 struct fmt *fmt = xmalloc(sizeof(struct fmt));
27 fmt->doc = doc;
28 fmt->txt = txt;
29 fmt->ops = ops;
30 fmt->level = 0;
31 return fmt;
34 static void fmt_free(struct fmt *fmt)
36 free(fmt);
39 static char *fmt_line(struct fmt *fmt, int line)
41 char *s = txt_line(fmt->txt, line);
42 int i;
43 for (i = 0; i < fmt->level && s && *s; i++)
44 s++;
45 return s;
48 static char *marker_char(char c)
50 int i;
51 for (i = 0; i < LENGTH(markers); i++)
52 if (markers[i][0] == c)
53 return markers[i];
54 return NULL;
57 static char *possible_inline(char *s)
59 char *home = s;
60 char *m = NULL;
61 while (*s) {
62 if (*s == '\\')
63 return s;
64 if ((s == home || isspace(*(s - 1))) && !isspace(*(s + 1)))
65 if ((m = marker_char(*s)))
66 return s;
67 s++;
69 return NULL;
72 static char *fillbuf(char *beg, char *end)
74 static char buf[MAXLINE];
75 memcpy(buf, beg, end - beg);
76 buf[end - beg] = '\0';
77 return buf;
80 static char *fmt_put_inline(struct fmt *fmt, char *s)
82 char *r = NULL;
83 char *marker;
84 if (*s == '\\') {
85 if (marker_char(*(s + 1))) {
86 fmt->ops->put(fmt->doc, fillbuf(s + 1, s + 2));
87 return s + 2;
89 return NULL;
91 marker = marker_char(*s);
92 if (marker)
93 r = strchr(s + 1, marker[1]);
94 if (r) {
95 fmt->ops->put_txt(fmt->doc, fillbuf(s + 1, r), marker);
96 r++;
98 return r;
101 static void put_text(struct fmt *fmt, char *s)
103 char *done = s;
104 while (*s) {
105 char *r = possible_inline(s);
106 if (!r)
107 break;
108 fmt->ops->put(fmt->doc, fillbuf(done, r));
109 s = done = r;
110 if ((r = fmt_put_inline(fmt, r)))
111 s = done = r;
112 else
113 s++;
115 fmt->ops->put(fmt->doc, done);
118 static void raw_line(struct fmt *fmt, char *s)
120 fmt->ops->put(fmt->doc, s);
123 static void put_line(struct fmt *fmt, int n)
125 put_text(fmt, fmt_line(fmt, n));
126 put_text(fmt, "\n");
129 static void put_lines(struct fmt *fmt, int beg, int end)
131 int i;
132 for (i = beg; i < end; i++)
133 put_line(fmt, i);
136 static void raw_lines(struct fmt *fmt, int beg, int end)
138 int i;
139 for (i = beg; i < end; i++) {
140 raw_line(fmt, fmt_line(fmt, i));
141 raw_line(fmt, "\n");
145 static int indents(char *s)
147 char *r = s;
148 while (isspace(*r))
149 r++;
150 return r - s;
153 static int islist(char *first, char *line)
155 char *signs = "*-+";
156 if (!line) {
157 if (!first || strlen(first) < 2)
158 return 0;
159 if (first[1] != ' ' || !strchr(signs, first[0]))
160 return 0;
161 return 1;
163 if (strlen(line) < 2)
164 return 0;
165 return line[0] == first[0] && line[1] == first[1];
168 static void fmt_handle(struct fmt *fmt, int beg, int end, int level);
170 static int fmt_head(struct fmt *fmt, int beg, int end)
172 char *line = fmt_line(fmt, beg);
173 char *next = fmt_line(fmt, beg + 1);
174 char c;
175 char *s = next;
176 char *signs = "=-~";
177 if (!next || !*line || beg == end || fmt->level)
178 return 0;
179 c = *next;
180 if (!c || !strchr(signs, *next))
181 return 0;
182 while (*++s)
183 if (*s != c)
184 return 0;
185 fmt->ops->head_beg(fmt->doc, strchr(signs, *next) - signs);
186 put_text(fmt, line);
187 fmt->ops->head_end(fmt->doc, strchr(signs, *next) - signs);
188 return 2;
191 static int cmd_line(struct fmt *fmt, int i)
193 return !fmt->level && *fmt_line(fmt, i) == '.';
196 static int par_end(struct fmt *fmt, int beg, int end)
198 int i = beg;
199 char *line;
200 while (i < end && (line = fmt_line(fmt, i))) {
201 if (!*line || indents(line) || islist(line, NULL) || cmd_line(fmt, i))
202 break;
203 i++;
205 return i;
208 static int fmt_par(struct fmt *fmt, int beg, int end)
210 int i;
211 if (indents(fmt_line(fmt, beg)) || cmd_line(fmt, beg))
212 return 0;
213 fmt->ops->par_beg(fmt->doc);
214 i = par_end(fmt, beg, end);
215 put_lines(fmt, beg, i);
216 fmt->ops->par_end(fmt->doc);
217 return i - beg;
220 static int fmt_rawline(struct fmt *fmt, int beg, int end)
222 if (*fmt_line(fmt, beg) != '.')
223 return 0;
224 raw_lines(fmt, beg, beg + 1);
225 return 1;
228 static int min(int a, int b)
230 return a < b ? a : b;
233 static int fmt_deindent(struct fmt *fmt, int n, int indent)
235 char *line;
236 int result = n;
237 while ((line = fmt_line(fmt, n))) {
238 if (*line && indents(line) < indent)
239 break;
240 n++;
241 if (*line)
242 result = n;
244 return result;
247 static int fmt_pre(struct fmt *fmt, int beg, int end)
249 int level = indents(fmt_line(fmt, beg));
250 int next = min(end, fmt_deindent(fmt, beg + 1, level));
251 fmt->level += level;
252 fmt->ops->block_beg(fmt->doc, NULL);
253 raw_lines(fmt, beg, next);
254 fmt->ops->block_end(fmt->doc, NULL);
255 fmt->level -= level;
256 return next - beg;
259 static int fmt_block(struct fmt *fmt, int beg, int end)
261 char *block_beg = NULL;
262 char *block_end = NULL;
263 int next;
264 char *line;
265 int depth = 1;
266 int i;
267 line = fmt_line(fmt, beg);
268 if (indents(line))
269 return fmt_pre(fmt, beg, end);
270 if (line[0] != '.')
271 return 0;
272 for (i = 0; i < LENGTH(blocks); i++) {
273 block_beg = blocks[i][0];
274 block_end = blocks[i][1];
275 if (!strncmp(block_beg, line, strlen(block_beg)))
276 break;
278 if (i == LENGTH(blocks))
279 return 0;
280 next = beg + 1;
281 while (next < end && depth > 0) {
282 line = fmt_line(fmt, next++);
283 if (!strncmp(block_beg, line, strlen(block_beg)))
284 depth++;
285 if (!strncmp(block_end, line, strlen(block_end)))
286 depth--;
288 fmt->ops->block_beg(fmt->doc, fmt_line(fmt, beg));
289 if (beg + 1 < next)
290 raw_lines(fmt, beg + 1, next - 1);
291 fmt->ops->block_end(fmt->doc, fmt_line(fmt, next - 1));
292 return next - beg;
295 static int fmt_list(struct fmt *fmt, int beg, int end)
297 int i = beg;
298 char *first = fmt_line(fmt, i);
299 char *line = first;
300 if (!islist(first, NULL))
301 return 0;
302 fmt->ops->list_beg(fmt->doc);
303 while ((line = fmt_line(fmt, i)) && islist(first, line)) {
304 int next = min(end, fmt_deindent(fmt, i + 1, 2));
305 int head = i;
306 fmt->ops->item_beg(fmt->doc);
308 fmt->level += 2;
309 i = par_end(fmt, i, next);
310 put_lines(fmt, head, i);
311 fmt->level -= 2;
313 fmt_handle(fmt, i, next, 2);
314 i = next;
315 fmt->ops->item_end(fmt->doc);
316 if (i == end)
317 break;
319 fmt->ops->list_end(fmt->doc);
320 return i - beg;
323 static int fmt_table(struct fmt *fmt, int beg, int end)
325 char *hdr = fmt_line(fmt, beg);
326 int i;
327 int row = 0, col = 0;
328 if (hdr[0] != '.' || hdr[1] != 'T' || hdr[2] != '1')
329 return 0;
330 fmt->ops->table_beg(fmt->doc, 0);
331 for (i = beg + 1; i < end; i++) {
332 char *cur = fmt_line(fmt, i);
333 if (cur[0] == '.' && cur[1] == 'T' && cur[2] == '2')
334 break;
335 if (cur[0] == '%') {
336 raw_line(fmt, cur + 2);
337 raw_line(fmt, "\n");
338 continue;
340 if (strchr("_=\n", cur[0])) {
341 if (col)
342 fmt->ops->entry_end(fmt->doc);
343 if (row)
344 fmt->ops->row_end(fmt->doc);
345 if (cur[0] != '\n') {
346 raw_line(fmt, cur);
347 raw_line(fmt, "\n");
349 col = 0;
350 continue;
352 if (strchr("#*-+", cur[0])) {
353 if (!col) {
354 fmt->ops->row_beg(fmt->doc);
355 row++;
356 } else {
357 fmt->ops->entry_end(fmt->doc);
359 fmt->ops->entry_beg(fmt->doc);
360 col++;
362 fmt->level += 2;
363 put_line(fmt, i);
364 fmt->level -= 2;
366 if (col)
367 fmt->ops->entry_end(fmt->doc);
368 if (row)
369 fmt->ops->row_end(fmt->doc);
370 fmt->ops->table_end(fmt->doc);
371 return i - beg + 1;
374 static int (*parts[])(struct fmt *fmt, int beg, int end) =
375 {fmt_head, fmt_list, fmt_table, fmt_block, fmt_rawline, fmt_par};
377 static void fmt_handle(struct fmt *fmt, int beg, int end, int level)
379 int line = beg;
380 int i;
381 fmt->level += level;
382 while (line < end) {
383 int c = 0;
384 if (!*fmt_line(fmt, line)) {
385 put_line(fmt, line++);
386 continue;
388 for (i = 0; i < LENGTH(parts); i++)
389 if ((c = parts[i](fmt, line, end)))
390 break;
391 line += c;
392 if (!c)
393 put_line(fmt, line++);
395 fmt->level -= level;
398 void format(struct doc *doc, struct txt *txt, struct fmt_ops *ops)
400 struct fmt *fmt = fmt_alloc(doc, txt, ops);
401 fmt->ops->doc_beg(doc);
402 fmt_handle(fmt, 0, txt->n, 0);
403 fmt->ops->doc_end(doc);
404 fmt_free(fmt);