fmt: reintroduce simple tables and cleanup inline markup handling
[ctxt.git] / fmt.c
blob407e8496ec77f5f2cec9838683229ad3427aa32c
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, char p, char n)
50 int i;
51 if (p == '\\' || (p && !isspace(p)) || isspace(n))
52 return NULL;
53 for (i = 0; i < LENGTH(markers); i++)
54 if (markers[i][0] == c)
55 return markers[i];
56 return NULL;
59 static char *fillbuf(char *beg, char *end)
61 static char buf[MAXLINE];
62 memcpy(buf, beg, end - beg);
63 buf[end - beg] = '\0';
64 return buf;
67 static void put_text(struct fmt *fmt, char *s)
69 char *_s = s;
70 char *o = s;
71 while (*s) {
72 char *r, *m;
73 while (*s && !marker_char(*s, s == _s ? 0 : s[-1], s[1]))
74 s++;
75 if (!*s)
76 break;
77 m = marker_char(*s, s == _s ? 0 : s[-1], s[1]);
78 r = strchr(s + 1, m[1]);
79 if (r) {
80 fmt->ops->put(fmt->doc, fillbuf(o, s));
81 fmt->ops->put_txt(fmt->doc, fillbuf(s + 1, r), m);
82 o = r + 1;
83 s = r + 1;
84 } else {
85 s++;
88 fmt->ops->put(fmt->doc, o);
91 static void raw_line(struct fmt *fmt, char *s)
93 fmt->ops->put(fmt->doc, s);
96 static void put_line(struct fmt *fmt, int n)
98 put_text(fmt, fmt_line(fmt, n));
99 put_text(fmt, "\n");
102 static void put_lines(struct fmt *fmt, int beg, int end)
104 int i;
105 for (i = beg; i < end; i++)
106 put_line(fmt, i);
109 static void raw_lines(struct fmt *fmt, int beg, int end)
111 int i;
112 for (i = beg; i < end; i++) {
113 raw_line(fmt, fmt_line(fmt, i));
114 raw_line(fmt, "\n");
118 static int indents(char *s)
120 char *r = s;
121 while (isspace(*r))
122 r++;
123 return r - s;
126 static int islist(char *first, char *line)
128 char *signs = "*-+";
129 if (!line) {
130 if (!first || strlen(first) < 2)
131 return 0;
132 if (first[1] != ' ' || !strchr(signs, first[0]))
133 return 0;
134 return 1;
136 if (strlen(line) < 2)
137 return 0;
138 return line[0] == first[0] && line[1] == first[1];
141 static void fmt_handle(struct fmt *fmt, int beg, int end, int level);
143 static int fmt_head(struct fmt *fmt, int beg, int end)
145 char *line = fmt_line(fmt, beg);
146 char *next = fmt_line(fmt, beg + 1);
147 char c;
148 char *s = next;
149 char *signs = "=-~";
150 if (!next || !*line || beg == end || fmt->level)
151 return 0;
152 c = *next;
153 if (!c || !strchr(signs, *next))
154 return 0;
155 while (*++s)
156 if (*s != c)
157 return 0;
158 fmt->ops->head_beg(fmt->doc, strchr(signs, *next) - signs);
159 put_text(fmt, line);
160 fmt->ops->head_end(fmt->doc, strchr(signs, *next) - signs);
161 return 2;
164 static int cmd_line(struct fmt *fmt, int i)
166 return !fmt->level && *fmt_line(fmt, i) == '.';
169 static int par_end(struct fmt *fmt, int beg, int end)
171 int i = beg;
172 char *line;
173 while (i < end && (line = fmt_line(fmt, i))) {
174 if (!*line || indents(line) || islist(line, NULL) || cmd_line(fmt, i))
175 break;
176 i++;
178 return i;
181 static int fmt_par(struct fmt *fmt, int beg, int end)
183 int i;
184 if (indents(fmt_line(fmt, beg)) || cmd_line(fmt, beg))
185 return 0;
186 fmt->ops->par_beg(fmt->doc);
187 i = par_end(fmt, beg, end);
188 put_lines(fmt, beg, i);
189 fmt->ops->par_end(fmt->doc);
190 return i - beg;
193 static int fmt_rawline(struct fmt *fmt, int beg, int end)
195 if (*fmt_line(fmt, beg) != '.')
196 return 0;
197 raw_lines(fmt, beg, beg + 1);
198 return 1;
201 static int min(int a, int b)
203 return a < b ? a : b;
206 static int fmt_deindent(struct fmt *fmt, int n, int indent)
208 char *line;
209 int result = n;
210 while ((line = fmt_line(fmt, n))) {
211 if (*line && indents(line) < indent)
212 break;
213 n++;
214 if (*line)
215 result = n;
217 return result;
220 static int fmt_pre(struct fmt *fmt, int beg, int end)
222 int level = indents(fmt_line(fmt, beg));
223 int next = min(end, fmt_deindent(fmt, beg + 1, level));
224 fmt->level += level;
225 fmt->ops->block_beg(fmt->doc, NULL);
226 raw_lines(fmt, beg, next);
227 fmt->ops->block_end(fmt->doc, NULL);
228 fmt->level -= level;
229 return next - beg;
232 static int fmt_block(struct fmt *fmt, int beg, int end)
234 char *block_beg = NULL;
235 char *block_end = NULL;
236 int next;
237 char *line;
238 int depth = 1;
239 int i;
240 line = fmt_line(fmt, beg);
241 if (indents(line))
242 return fmt_pre(fmt, beg, end);
243 if (line[0] != '.')
244 return 0;
245 for (i = 0; i < LENGTH(blocks); i++) {
246 block_beg = blocks[i][0];
247 block_end = blocks[i][1];
248 if (!strncmp(block_beg, line, strlen(block_beg)))
249 break;
251 if (i == LENGTH(blocks))
252 return 0;
253 next = beg + 1;
254 while (next < end && depth > 0) {
255 line = fmt_line(fmt, next++);
256 if (!strncmp(block_beg, line, strlen(block_beg)))
257 depth++;
258 if (!strncmp(block_end, line, strlen(block_end)))
259 depth--;
261 fmt->ops->block_beg(fmt->doc, fmt_line(fmt, beg));
262 if (beg + 1 < next)
263 raw_lines(fmt, beg + 1, next - 1);
264 fmt->ops->block_end(fmt->doc, fmt_line(fmt, next - 1));
265 return next - beg;
268 static int fmt_list(struct fmt *fmt, int beg, int end)
270 int i = beg;
271 char *first = fmt_line(fmt, i);
272 char *line = first;
273 if (!islist(first, NULL))
274 return 0;
275 fmt->ops->list_beg(fmt->doc);
276 while ((line = fmt_line(fmt, i)) && islist(first, line)) {
277 int next = min(end, fmt_deindent(fmt, i + 1, 2));
278 int head = i;
279 fmt->ops->item_beg(fmt->doc);
281 fmt->level += 2;
282 i = par_end(fmt, i, next);
283 put_lines(fmt, head, i);
284 fmt->level -= 2;
286 fmt_handle(fmt, i, next, 2);
287 i = next;
288 fmt->ops->item_end(fmt->doc);
289 if (i == end)
290 break;
292 fmt->ops->list_end(fmt->doc);
293 return i - beg;
296 /* .T1/.T2 tables */
297 static int fmt_table(struct fmt *fmt, int beg, int end)
299 char *hdr = fmt_line(fmt, beg);
300 int i;
301 int row = 0, col = 0;
302 if (hdr[0] != '.' || hdr[1] != 'T' || hdr[2] != '1')
303 return 0;
304 fmt->ops->table_beg(fmt->doc, 0);
305 for (i = beg + 1; i < end; i++) {
306 char *cur = fmt_line(fmt, i);
307 if (cur[0] == '.' && cur[1] == 'T' && cur[2] == '2')
308 break;
309 if (cur[0] == '%') {
310 raw_line(fmt, cur + 2);
311 raw_line(fmt, "\n");
312 continue;
314 if (strchr("_=\n", cur[0])) {
315 if (col)
316 fmt->ops->entry_end(fmt->doc);
317 if (row)
318 fmt->ops->row_end(fmt->doc);
319 if (cur[0] != '\n') {
320 raw_line(fmt, cur);
321 raw_line(fmt, "\n");
323 col = 0;
324 continue;
326 if (strchr("#*-+", cur[0])) {
327 if (!col) {
328 fmt->ops->row_beg(fmt->doc);
329 row++;
330 } else {
331 fmt->ops->entry_end(fmt->doc);
333 fmt->ops->entry_beg(fmt->doc);
334 col++;
336 fmt->level += 2;
337 put_line(fmt, i);
338 fmt->level -= 2;
340 if (col)
341 fmt->ops->entry_end(fmt->doc);
342 if (row)
343 fmt->ops->row_end(fmt->doc);
344 fmt->ops->table_end(fmt->doc);
345 return i - beg + 1;
348 /* simple tables */
350 static int table_columns(char *line)
352 int n;
353 for (n = 0; *line; n++) {
354 while (*line == '\t')
355 line++;
356 while (*line && *line != '\t')
357 line++;
359 return n;
362 static void table_row(struct fmt *fmt, char *s)
364 if (*s == '=' || *s == '-')
365 return;
366 fmt->ops->row_beg(fmt->doc);
367 while (*s) {
368 char *r = s;
369 while (*s && *s != '\t')
370 s++;
371 fmt->ops->entry_beg(fmt->doc);
372 put_text(fmt, fillbuf(r, s));
373 fmt->ops->entry_end(fmt->doc);
374 while (*s == '\t')
375 s++;
377 fmt->ops->row_end(fmt->doc);
380 static int fmt_tableascii(struct fmt *fmt, int beg, int end)
382 int i;
383 int n;
384 if (*fmt_line(fmt, beg) != '=')
385 return 0;
386 n = table_columns(fmt_line(fmt, beg + 1));
387 fmt->ops->table_beg(fmt->doc, n);
388 for (i = beg + 1; i < end; i++) {
389 if (!fmt->level && !*fmt_line(fmt, i))
390 break;
391 table_row(fmt, fmt_line(fmt, i));
393 fmt->ops->table_end(fmt->doc);
394 return i - beg;
397 /* parsing text */
399 static int (*parts[])(struct fmt *fmt, int beg, int end) = {
400 fmt_head, fmt_list, fmt_table, fmt_tableascii,
401 fmt_block, fmt_rawline, fmt_par
404 static void fmt_handle(struct fmt *fmt, int beg, int end, int level)
406 int line = beg;
407 int i;
408 fmt->level += level;
409 while (line < end) {
410 int c = 0;
411 if (!*fmt_line(fmt, line)) {
412 put_line(fmt, line++);
413 continue;
415 for (i = 0; i < LENGTH(parts); i++)
416 if ((c = parts[i](fmt, line, end)))
417 break;
418 line += c;
419 if (!c)
420 put_line(fmt, line++);
422 fmt->level -= level;
425 void format(struct doc *doc, struct txt *txt, struct fmt_ops *ops)
427 struct fmt *fmt = fmt_alloc(doc, txt, ops);
428 fmt->ops->doc_beg(doc);
429 fmt_handle(fmt, 0, txt->n, 0);
430 fmt->ops->doc_end(doc);
431 fmt_free(fmt);