troff: escape dots after inline markups
[ctxt.git] / fmt.c
blob616de2e72f4db09fbd1030d86947253fe8bdc597
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 #define PAIR(c1, c2) (((c2) << 8) | (c1))
23 static struct block {
24 int beg;
25 int end;
26 int id;
27 } blocks[] = {
28 {PAIR('E', 'Q'), PAIR('E', 'N'), B_RAW},
29 {PAIR('P', 'S'), PAIR('P', 'E'), B_RAW},
30 {PAIR('T', 'S'), PAIR('T', 'E'), B_RAW},
33 struct fmt {
34 struct doc *doc;
35 struct txt *txt;
36 struct fmt_ops *ops;
37 int level;
40 static struct fmt *fmt_alloc(struct doc *doc, struct txt *txt,
41 struct fmt_ops *ops)
43 struct fmt *fmt = xmalloc(sizeof(struct fmt));
44 fmt->doc = doc;
45 fmt->txt = txt;
46 fmt->ops = ops;
47 fmt->level = 0;
48 return fmt;
51 static void fmt_free(struct fmt *fmt)
53 free(fmt);
56 static char *fmt_line(struct fmt *fmt, int line)
58 char *s = txt_line(fmt->txt, line);
59 int i;
60 for (i = 0; i < fmt->level && s && *s; i++)
61 s++;
62 return s;
65 static struct marker *marker_char(char c)
67 int i;
68 for (i = 0; i < LENGTH(markers); i++)
69 if (markers[i].beg == c)
70 return &markers[i];
71 return NULL;
74 static char *possible_inline(char *s)
76 char *home = s;
77 struct marker *m = NULL;
78 while (*s) {
79 if (*s == '\\')
80 return s;
81 if ((s == home || isspace(*(s - 1))) && !isspace(*(s + 1)))
82 if ((m = marker_char(*s)))
83 return s;
84 s++;
86 return NULL;
89 static void fillbuf(char *buf, int len, char *beg, char *end)
91 int n = len - 1 < end - beg ? len - 1 : end - beg;
92 memcpy(buf, beg, n);
93 buf[n] = '\0';
96 static char *fmt_put_inline(struct fmt *fmt, char *s)
98 char buf[MAXLINE];
99 char *r = NULL;
100 struct marker *marker;
101 if (*s == '\\') {
102 if (marker_char(*(s + 1))) {
103 fillbuf(buf, LENGTH(buf), s + 1, s + 2);
104 fmt->ops->put(fmt->doc, buf);
105 return s + 2;
107 return NULL;
109 marker = marker_char(*s);
110 if (marker && marker->end)
111 r = strchr(s + 1, marker->end);
112 if (marker && !marker->end)
113 for (r = s + 1; isalnum(*r); r++);
114 if (r) {
115 fillbuf(buf, LENGTH(buf), s + 1, r);
116 fmt->ops->put_txt(fmt->doc, buf, marker->id);
117 if (marker->end)
118 r++;
120 return r;
123 static void put_text(struct fmt *fmt, char *s)
125 char buf[MAXLINE];
126 char *done = s;
127 while (*s) {
128 char *r = possible_inline(s);
129 if (!r)
130 break;
131 fillbuf(buf, LENGTH(buf), done, r);
132 fmt->ops->put(fmt->doc, buf);
133 s = done = r;
134 if ((r = fmt_put_inline(fmt, r)))
135 s = done = r;
136 else
137 s++;
139 fmt->ops->put(fmt->doc, done);
142 static void raw_line(struct fmt *fmt, char *s)
144 fmt->ops->put(fmt->doc, s);
147 static void put_line(struct fmt *fmt, int n)
149 put_text(fmt, fmt_line(fmt, n));
150 put_text(fmt, "\n");
153 static void put_lines(struct fmt *fmt, int beg, int end)
155 int i;
156 for (i = beg; i < end; i++)
157 put_line(fmt, i);
160 static void raw_lines(struct fmt *fmt, int beg, int end)
162 int i;
163 for (i = beg; i < end; i++) {
164 raw_line(fmt, fmt_line(fmt, i));
165 raw_line(fmt, "\n");
169 static int indents(char *s)
171 char *r = s;
172 while (isspace(*r))
173 r++;
174 return r - s;
177 static int islist(char *first, char *line)
179 char *signs = "*-+";
180 if (!line) {
181 if (!first || strlen(first) < 2)
182 return 0;
183 if (first[1] != ' ' || !strchr(signs, first[0]))
184 return 0;
185 return 1;
187 if (strlen(line) < 2)
188 return 0;
189 return line[0] == first[0] && line[1] == first[1];
192 static void fmt_handle(struct fmt *fmt, int beg, int end, int level);
194 static int fmt_head(struct fmt *fmt, int beg, int end)
196 char *line = fmt_line(fmt, beg);
197 char *next = fmt_line(fmt, beg + 1);
198 char c;
199 char *s = next;
200 char *signs = "=-~";
201 if (!next || !*line || beg == end || fmt->level)
202 return 0;
203 c = *next;
204 if (!c || !strchr(signs, *next))
205 return 0;
206 while (*++s)
207 if (*s != c)
208 return 0;
209 fmt->ops->head_beg(fmt->doc, strchr(signs, *next) - signs);
210 put_text(fmt, line);
211 fmt->ops->head_end(fmt->doc, strchr(signs, *next) - signs);
212 return 2;
215 static int cmd_line(struct fmt *fmt, int i)
217 return !fmt->level && *fmt_line(fmt, i) == '.';
220 static int par_end(struct fmt *fmt, int beg, int end)
222 int i = beg;
223 char *line;
224 while (i < end && (line = fmt_line(fmt, i))) {
225 if (!*line || indents(line) || islist(line, NULL) || cmd_line(fmt, i))
226 break;
227 i++;
229 return i;
232 static int fmt_par(struct fmt *fmt, int beg, int end)
234 int i;
235 if (indents(fmt_line(fmt, beg)) || cmd_line(fmt, beg))
236 return 0;
237 fmt->ops->par_beg(fmt->doc);
238 i = par_end(fmt, beg, end);
239 put_lines(fmt, beg, i);
240 fmt->ops->par_end(fmt->doc);
241 return i - beg;
244 static int fmt_rawline(struct fmt *fmt, int beg, int end)
246 if (*fmt_line(fmt, beg) != '.')
247 return 0;
248 raw_lines(fmt, beg, beg + 1);
249 return 1;
252 static int min(int a, int b)
254 return a < b ? a : b;
257 static int fmt_deindent(struct fmt *fmt, int n, int indent)
259 char *line;
260 int result = n;
261 while ((line = fmt_line(fmt, n))) {
262 if (*line && indents(line) < indent)
263 break;
264 n++;
265 if (*line)
266 result = n;
268 return result;
271 static int fmt_pre(struct fmt *fmt, int beg, int end)
273 int level = indents(fmt_line(fmt, beg));
274 int next = min(end, fmt_deindent(fmt, beg + 1, level));
275 fmt->level += level;
276 fmt->ops->block_beg(fmt->doc, fmt_line(fmt, beg), B_PRE);
277 raw_lines(fmt, beg, next);
278 fmt->ops->block_end(fmt->doc, fmt_line(fmt, next), B_PRE);
279 fmt->level -= level;
280 return next - beg;
283 static int fmt_block(struct fmt *fmt, int beg, int end)
285 struct block *block = NULL;
286 int next;
287 int i;
288 char *line;
289 line = fmt_line(fmt, beg);
290 if (indents(line))
291 return fmt_pre(fmt, beg, end);
292 if (line[0] != '.')
293 return 0;
294 for (i = 0; i < LENGTH(blocks); i++) {
295 int c1 = line[1];
296 int c2 = line[2];
297 if (blocks[i].beg == c1 || blocks[i].beg == PAIR(c1, c2)) {
298 block = &blocks[i];
299 break;
302 if (!block)
303 return 0;
304 next = beg + 1;
305 while (next < end) {
306 int c1, c2;
307 line = fmt_line(fmt, next++);
308 c1 = line[1];
309 c2 = line[2];
310 if (block->end == c1 || block->end == PAIR(c1, c2))
311 break;
313 fmt->ops->block_beg(fmt->doc, fmt_line(fmt, beg), block->id);
314 if (beg + 1 < next)
315 raw_lines(fmt, beg + 1, next - 1);
316 fmt->ops->block_end(fmt->doc, fmt_line(fmt, next - 1), block->id);
317 return next - beg;
320 static int fmt_list(struct fmt *fmt, int beg, int end)
322 int i = beg;
323 char *first = fmt_line(fmt, i);
324 char *line = first;
325 if (!islist(first, NULL))
326 return 0;
327 fmt->ops->list_beg(fmt->doc);
328 while ((line = fmt_line(fmt, i)) && islist(first, line)) {
329 int next = min(end, fmt_deindent(fmt, i + 1, 2));
330 int head = i;
331 fmt->ops->item_beg(fmt->doc);
333 fmt->level += 2;
334 i = par_end(fmt, i, next);
335 put_lines(fmt, head, i);
336 fmt->level -= 2;
338 fmt_handle(fmt, i, next, 2);
339 i = next;
340 fmt->ops->item_end(fmt->doc);
341 if (i == end)
342 break;
344 fmt->ops->list_end(fmt->doc);
345 return i - beg;
348 static int table_columns(char *line)
350 int n;
351 for (n = 0; *line; n++) {
352 while (*line == '\t')
353 line++;
354 while (*line && *line != '\t')
355 line++;
357 return n;
360 static void table_row(struct fmt *fmt, char *s)
362 char buf[MAXLINE];
363 if (*s == '=' || *s == '-')
364 return;
365 fmt->ops->row_beg(fmt->doc);
366 while (*s) {
367 char *r = s;
368 while (*s && *s != '\t')
369 s++;
370 fmt->ops->entry_beg(fmt->doc);
371 fillbuf(buf, LENGTH(buf), r, s);
372 put_text(fmt, buf);
373 fmt->ops->entry_end(fmt->doc);
374 while (*s == '\t')
375 s++;
377 fmt->ops->row_end(fmt->doc);
380 static int fmt_table(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 static int (*parts[])(struct fmt *fmt, int beg, int end) =
398 {fmt_head, fmt_list, fmt_table, fmt_block, fmt_rawline, fmt_par};
400 static void fmt_handle(struct fmt *fmt, int beg, int end, int level)
402 int line = beg;
403 int i;
404 fmt->level += level;
405 while (line < end) {
406 int c = 0;
407 if (!*fmt_line(fmt, line)) {
408 put_line(fmt, line++);
409 continue;
411 for (i = 0; i < LENGTH(parts); i++)
412 if ((c = parts[i](fmt, line, end)))
413 break;
414 line += c;
415 if (!c)
416 put_line(fmt, line++);
418 fmt->level -= level;
421 void format(struct doc *doc, struct txt *txt, struct fmt_ops *ops)
423 struct fmt *fmt = fmt_alloc(doc, txt, ops);
424 fmt->ops->doc_beg(doc);
425 fmt_handle(fmt, 0, txt->n, 0);
426 fmt->ops->doc_end(doc);
427 fmt_free(fmt);