fmt: add formula blocks
[ctxt.git] / fmt.c
blob40c139db05ce906902bd33f0d8edbb1aec161ba2
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 doc {
10 int fd;
11 struct txt *txt;
12 struct fmt_ops *ops;
13 int level;
16 static struct doc *doc_alloc(int fd, struct txt *txt, struct fmt_ops *ops)
18 struct doc *doc = xmalloc(sizeof(struct doc));
19 doc->fd = fd;
20 doc->txt = txt;
21 doc->ops = ops;
22 doc->level = 0;
23 return doc;
26 static void doc_free(struct doc *doc)
28 free(doc);
31 static char *doc_line(struct doc *doc, int line)
33 char *s = txt_line(doc->txt, line);
34 int i;
35 for (i = 0; i < doc->level && s && *s; i++)
36 s++;
37 return s;
40 static void doc_handle(struct doc *doc, int beg, int end, int level);
42 static int doc_head(struct doc *doc, int beg, int end)
44 char *line = doc_line(doc, beg);
45 char *next = doc_line(doc, beg + 1);
46 char c;
47 char *s = next;
48 char *signs = "=-~";
49 if (!next || !*line || beg == end || doc->level)
50 return 0;
51 c = *next;
52 if (!c || !strchr(signs, *next))
53 return 0;
54 while (*++s)
55 if (*s != c)
56 return 0;
57 doc->ops->head_beg(doc->fd, strchr(signs, *next) - signs);
58 doc->ops->put(doc->fd, line);
59 doc->ops->head_end(doc->fd);
60 return 2;
63 static char *possible_inline(char *s) {
64 char *signs = "*$@\\";
65 char *home = s;
66 while (*s) {
67 if (strchr(signs, *s) && !isspace(*(s + 1)))
68 if (s == home || isspace(*(s - 1)))
69 return s;
70 s++;
72 return NULL;
75 static void fillbuf(char *buf, int len, char *beg, char *end)
77 int n = len - 1 < end - beg ? len - 1 : end - beg;
78 memcpy(buf, beg, n);
79 buf[n] = '\0';
82 static char *doc_put_inline(struct doc *doc, char *s)
84 char buf[1024];
85 char *r = NULL;
86 switch (*s) {
87 case '*':
88 r = strchr(s + 1, *s);
89 if (r) {
90 fillbuf(buf, LENGTH(buf), s + 1, r);
91 doc->ops->put_emph(doc->fd, buf);
92 r++;
94 return r;
95 case '$':
96 r = strchr(s + 1, *s);
97 if (r) {
98 fillbuf(buf, LENGTH(buf), s, r + 1);
99 doc->ops->put_raw(doc->fd, buf);
100 r++;
102 return r;
103 case '@':
104 for (r = s + 1; isalnum(*r); r++);
105 if (r) {
106 fillbuf(buf, LENGTH(buf), s + 1, r);
107 doc->ops->put_ref(doc->fd, buf);
109 return r;
110 case '\\':
111 if (strchr("*$@\\", *(s + 1))) {
112 fillbuf(buf, LENGTH(buf), s + 1, s + 2);
113 doc->ops->put(doc->fd, buf);
114 return s + 2;
116 return NULL;
117 default:
118 break;
120 return NULL;
123 static void doc_put(struct doc *doc, char *s)
125 char buf[1024];
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 doc->ops->put(doc->fd, buf);
133 s = done = r;
134 if ((r = doc_put_inline(doc, r)))
135 s = done = r;
136 else
137 s++;
139 doc->ops->put(doc->fd, done);
142 static void doc_lines(struct doc *doc, int beg, int end)
144 int i;
145 for (i = beg; i < end; i++) {
146 doc_put(doc, doc_line(doc, i));
147 doc_put(doc, "\n");
151 static int indents(char *s)
153 char *r = s;
154 while (isspace(*r))
155 r++;
156 return r - s;
159 static int islist(char *first, char *line)
161 char *signs = "*-+";
162 if (!line) {
163 if (!first || strlen(first) < 2)
164 return 0;
165 if (first[1] != ' ' || !strchr(signs, first[0]))
166 return 0;
167 return 1;
169 if (strlen(line) < 2)
170 return 0;
171 return line[0] == first[0] && line[1] == first[1];
174 static int par_end(struct doc *doc, int beg, int end)
176 int i = beg;
177 char *line;
178 while (i < end && (line = doc_line(doc, i))) {
179 if (!*line || indents(line) || islist(line, NULL))
180 break;
181 i++;
183 return i;
186 static int doc_par(struct doc *doc, int beg, int end)
188 int i;
189 if (indents(doc_line(doc, beg)))
190 return 0;
191 doc->ops->par_beg(doc->fd);
192 i = par_end(doc, beg, end);
193 doc_lines(doc, beg, i);
194 doc->ops->par_end(doc->fd);
195 return i - beg;
198 static int min(int a, int b)
200 return a < b ? a : b;
203 static int doc_deindent(struct doc *doc, int n, int indent)
205 char *line;
206 int result = n;
207 while ((line = doc_line(doc, n))) {
208 if (*line && indents(line) < indent)
209 break;
210 n++;
211 if (*line)
212 result = n;
214 return result;
217 static int doc_block(struct doc *doc, int beg, int end)
219 int level = indents(doc_line(doc, beg));
220 int next;
221 if (!level)
222 return 0;
223 next = min(end, doc_deindent(doc, beg + 1, level));
224 doc_handle(doc, beg, next, level);
225 return next - beg;
229 static int doc_list(struct doc *doc, int beg, int end)
231 int i = beg;
232 char *first = doc_line(doc, i);
233 char *line = first;
234 if (!islist(first, NULL))
235 return 0;
236 doc->ops->list_beg(doc->fd);
237 while ((line = doc_line(doc, i)) && islist(first, line)) {
238 int next = min(end, doc_deindent(doc, i + 1, 2));
239 int head = i;
240 doc->ops->item_beg(doc->fd);
242 doc->level += 2;
243 i = par_end(doc, i, next);
244 doc_lines(doc, head, i);
245 doc->level -= 2;
247 doc_handle(doc, i, next, 2);
248 i = next;
249 doc->ops->item_end(doc->fd);
250 if (i == end)
251 break;
253 doc->ops->list_end(doc->fd);
254 return i - beg;
257 static int doc_pre(struct doc *doc, int beg, int end)
259 int i;
260 if (!doc->level)
261 return 0;
262 doc->ops->pre_beg(doc->fd);
263 for (i = beg; i < end; i++) {
264 doc_put(doc, doc_line(doc, i));
265 doc_put(doc, "\n");
267 doc->ops->pre_end(doc->fd);
268 return i - beg;
271 static int doc_formula(struct doc *doc, int beg, int end)
273 int i;
274 if (!doc->level || *doc_line(doc, beg) != '=')
275 return 0;
276 doc->ops->formula_beg(doc->fd);
277 for (i = beg; i < end; i++) {
278 char *line = doc_line(doc, i);
279 if (i == beg)
280 line++;
281 doc_put(doc, line);
282 if (i != end - 1)
283 doc_put(doc, "\n");
285 doc->ops->formula_end(doc->fd);
286 return i - beg;
289 static int (*parts[])(struct doc *doc, int beg, int end) =
290 {doc_head, doc_list, doc_formula, doc_pre, doc_par, doc_block};
292 static void doc_handle(struct doc *doc, int beg, int end, int level)
294 int line = beg;
295 int i;
296 doc->level += level;
297 while (line < end) {
298 int c = 0;
299 for (i = 0; i < LENGTH(parts); i++)
300 if ((c = parts[i](doc, line, end)))
301 break;
302 line += c;
303 if (!c) {
304 doc_lines(doc, line, line + 1);
305 line++;
308 doc->level -= level;
311 void fmt(int fd, struct txt *txt, struct fmt_ops *ops)
313 struct doc *doc = doc_alloc(fd, txt, ops);
314 doc->ops->doc_beg(fd);
315 doc_handle(doc, 0, txt->n, 0);
316 doc->ops->doc_end(fd);
317 doc_free(doc);