hold inline markers in a struct marker array
[ctxt.git] / fmt.c
blob5a4795828407a107a75f0acba9f91e516f6570b0
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 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 {'@', 0, M_LINK}
21 struct fmt {
22 struct doc *doc;
23 struct txt *txt;
24 struct fmt_ops *ops;
25 int level;
28 static struct fmt *fmt_alloc(struct doc *doc, struct txt *txt,
29 struct fmt_ops *ops)
31 struct fmt *fmt = xmalloc(sizeof(struct fmt));
32 fmt->doc = doc;
33 fmt->txt = txt;
34 fmt->ops = ops;
35 fmt->level = 0;
36 return fmt;
39 static void fmt_free(struct fmt *fmt)
41 free(fmt);
44 static char *fmt_line(struct fmt *fmt, int line)
46 char *s = txt_line(fmt->txt, line);
47 int i;
48 for (i = 0; i < fmt->level && s && *s; i++)
49 s++;
50 return s;
53 static struct marker *marker_char(char c)
55 int i;
56 for (i = 0; i < LENGTH(markers); i++)
57 if (markers[i].beg == c)
58 return &markers[i];
59 return NULL;
62 static char *possible_inline(char *s)
64 char *home = s;
65 struct marker *m = NULL;
66 while (*s) {
67 if (*s == '\\')
68 return s;
69 if ((s == home || isspace(*(s - 1))) && !isspace(*(s + 1)))
70 if ((m = marker_char(*s)))
71 return s;
72 s++;
74 return NULL;
77 static void fillbuf(char *buf, int len, char *beg, char *end)
79 int n = len - 1 < end - beg ? len - 1 : end - beg;
80 memcpy(buf, beg, n);
81 buf[n] = '\0';
84 static char *fmt_put_inline(struct fmt *fmt, char *s)
86 char buf[MAXLINE];
87 char *r = NULL;
88 struct marker *marker;
89 if (*s == '\\') {
90 if (marker_char(*(s + 1))) {
91 fillbuf(buf, LENGTH(buf), s + 1, s + 2);
92 fmt->ops->put(fmt->doc, buf);
93 return s + 2;
95 return NULL;
97 marker = marker_char(*s);
98 if (marker && marker->end)
99 r = strchr(s + 1, marker->end);
100 if (marker && !marker->end)
101 for (r = s + 1; isalnum(*r); r++);
102 if (r) {
103 fillbuf(buf, LENGTH(buf), s + 1, r);
104 fmt->ops->put_txt(fmt->doc, buf, marker->id);
105 if (marker->end)
106 r++;
108 return r;
111 static void put_text(struct fmt *fmt, char *s)
113 char buf[MAXLINE];
114 char *done = s;
115 while (*s) {
116 char *r = possible_inline(s);
117 if (!r)
118 break;
119 fillbuf(buf, LENGTH(buf), done, r);
120 fmt->ops->put(fmt->doc, buf);
121 s = done = r;
122 if ((r = fmt_put_inline(fmt, r)))
123 s = done = r;
124 else
125 s++;
127 fmt->ops->put(fmt->doc, done);
130 static void raw_line(struct fmt *fmt, char *s)
132 fmt->ops->put(fmt->doc, s);
135 static void put_line(struct fmt *fmt, int n)
137 put_text(fmt, fmt_line(fmt, n));
138 put_text(fmt, "\n");
141 static void put_lines(struct fmt *fmt, int beg, int end)
143 int i;
144 for (i = beg; i < end; i++)
145 put_line(fmt, i);
148 static void raw_lines(struct fmt *fmt, int beg, int end)
150 int i;
151 for (i = beg; i < end; i++) {
152 raw_line(fmt, fmt_line(fmt, i));
153 raw_line(fmt, "\n");
157 static int indents(char *s)
159 char *r = s;
160 while (isspace(*r))
161 r++;
162 return r - s;
165 static int islist(char *first, char *line)
167 char *signs = "*-+";
168 if (!line) {
169 if (!first || strlen(first) < 2)
170 return 0;
171 if (first[1] != ' ' || !strchr(signs, first[0]))
172 return 0;
173 return 1;
175 if (strlen(line) < 2)
176 return 0;
177 return line[0] == first[0] && line[1] == first[1];
180 static void fmt_handle(struct fmt *fmt, int beg, int end, int level);
182 static int fmt_head(struct fmt *fmt, int beg, int end)
184 char *line = fmt_line(fmt, beg);
185 char *next = fmt_line(fmt, beg + 1);
186 char c;
187 char *s = next;
188 char *signs = "=-~";
189 if (!next || !*line || beg == end || fmt->level)
190 return 0;
191 c = *next;
192 if (!c || !strchr(signs, *next))
193 return 0;
194 while (*++s)
195 if (*s != c)
196 return 0;
197 fmt->ops->head_beg(fmt->doc, strchr(signs, *next) - signs);
198 put_text(fmt, line);
199 fmt->ops->head_end(fmt->doc, strchr(signs, *next) - signs);
200 return 2;
203 static int par_end(struct fmt *fmt, int beg, int end)
205 int i = beg;
206 char *line;
207 while (i < end && (line = fmt_line(fmt, i))) {
208 if (!*line || indents(line) || islist(line, NULL))
209 break;
210 i++;
212 return i;
215 static int fmt_par(struct fmt *fmt, int beg, int end)
217 int i;
218 if (indents(fmt_line(fmt, beg)))
219 return 0;
220 fmt->ops->par_beg(fmt->doc);
221 i = par_end(fmt, beg, end);
222 put_lines(fmt, beg, i);
223 fmt->ops->par_end(fmt->doc);
224 return i - beg;
227 static int min(int a, int b)
229 return a < b ? a : b;
232 static int fmt_deindent(struct fmt *fmt, int n, int indent)
234 char *line;
235 int result = n;
236 while ((line = fmt_line(fmt, n))) {
237 if (*line && indents(line) < indent)
238 break;
239 n++;
240 if (*line)
241 result = n;
243 return result;
246 static int fmt_block(struct fmt *fmt, int beg, int end)
248 int level = indents(fmt_line(fmt, beg));
249 int next;
250 if (!level)
251 return 0;
252 next = min(end, fmt_deindent(fmt, beg + 1, level));
253 fmt_handle(fmt, beg, next, level);
254 return next - beg;
257 static int fmt_list(struct fmt *fmt, int beg, int end)
259 int i = beg;
260 char *first = fmt_line(fmt, i);
261 char *line = first;
262 if (!islist(first, NULL))
263 return 0;
264 fmt->ops->list_beg(fmt->doc);
265 while ((line = fmt_line(fmt, i)) && islist(first, line)) {
266 int next = min(end, fmt_deindent(fmt, i + 1, 2));
267 int head = i;
268 fmt->ops->item_beg(fmt->doc);
270 fmt->level += 2;
271 i = par_end(fmt, i, next);
272 put_lines(fmt, head, i);
273 fmt->level -= 2;
275 fmt_handle(fmt, i, next, 2);
276 i = next;
277 fmt->ops->item_end(fmt->doc);
278 if (i == end)
279 break;
281 fmt->ops->list_end(fmt->doc);
282 return i - beg;
285 static int fmt_pre(struct fmt *fmt, int beg, int end)
287 if (!fmt->level)
288 return 0;
289 fmt->ops->pre_beg(fmt->doc);
290 raw_lines(fmt, beg, end);
291 fmt->ops->pre_end(fmt->doc);
292 return end - beg;
295 static int fmt_formula(struct fmt *fmt, int beg, int end)
297 int i;
298 if (!fmt->level || *fmt_line(fmt, beg) != '$')
299 return 0;
300 fmt->ops->formula_beg(fmt->doc);
301 for (i = beg; i < end; i++) {
302 char *line = fmt_line(fmt, i);
303 if (i == beg)
304 line++;
305 raw_line(fmt, line);
306 if (i != end - 1)
307 raw_line(fmt, "\n");
309 fmt->ops->formula_end(fmt->doc);
310 return end - beg;
313 static int fmt_raw(struct fmt *fmt, int beg, int end)
315 if (!fmt->level || *fmt_line(fmt, beg) != '%')
316 return 0;
317 raw_line(fmt, fmt_line(fmt, beg) + 1);
318 raw_line(fmt, "\n");
319 raw_lines(fmt, beg + 1, end);
320 return end - beg;
323 static int table_columns(char *line)
325 int n;
326 for (n = 0; *line; n++) {
327 while (*line == '\t')
328 line++;
329 while (*line && *line != '\t')
330 line++;
332 return n;
335 static void table_row(struct fmt *fmt, char *s)
337 char buf[MAXLINE];
338 if (*s == '=')
339 return;
340 fmt->ops->row_beg(fmt->doc);
341 while (*s) {
342 char *r = s;
343 while (*s && *s != '\t')
344 s++;
345 fmt->ops->entry_beg(fmt->doc);
346 fillbuf(buf, LENGTH(buf), r, s);
347 put_text(fmt, buf);
348 fmt->ops->entry_end(fmt->doc);
349 while (*s == '\t')
350 s++;
352 fmt->ops->row_end(fmt->doc);
355 static int fmt_table(struct fmt *fmt, int beg, int end)
357 int i;
358 int n;
359 if (*fmt_line(fmt, beg) != '=')
360 return 0;
361 n = table_columns(fmt_line(fmt, beg + 1));
362 fmt->ops->table_beg(fmt->doc, n);
363 for (i = beg + 1; i < end; i++) {
364 if (!fmt->level && !*fmt_line(fmt, i))
365 break;
366 table_row(fmt, fmt_line(fmt, i));
368 fmt->ops->table_end(fmt->doc);
369 return i - beg;
372 static int (*parts[])(struct fmt *fmt, int beg, int end) =
373 {fmt_head, fmt_list, fmt_formula, fmt_raw,
374 fmt_table, fmt_pre, fmt_par, fmt_block};
376 static void fmt_handle(struct fmt *fmt, int beg, int end, int level)
378 int line = beg;
379 int i;
380 fmt->level += level;
381 while (line < end) {
382 int c = 0;
383 if (!*fmt_line(fmt, line)) {
384 put_line(fmt, line++);
385 continue;
387 for (i = 0; i < LENGTH(parts); i++)
388 if ((c = parts[i](fmt, line, end)))
389 break;
390 line += c;
391 if (!c)
392 put_line(fmt, line++);
394 fmt->level -= level;
397 void format(struct doc *doc, struct txt *txt, struct fmt_ops *ops)
399 struct fmt *fmt = fmt_alloc(doc, txt, ops);
400 fmt->ops->doc_beg(doc);
401 fmt_handle(fmt, 0, txt->n, 0);
402 fmt->ops->doc_end(doc);
403 fmt_free(fmt);