fmt: formula and pre blocks should be raw
[ctxt.git] / fmt.c
blobe34661fef6dd5908e9f1fccc7723ee81c0d63a27
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 put_line(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 raw_line(struct doc *doc, char *s)
144 doc->ops->put(doc->fd, s);
147 static void put_lines(struct doc *doc, int beg, int end)
149 int i;
150 for (i = beg; i < end; i++) {
151 put_line(doc, doc_line(doc, i));
152 put_line(doc, "\n");
156 static void raw_lines(struct doc *doc, int beg, int end)
158 int i;
159 for (i = beg; i < end; i++) {
160 raw_line(doc, doc_line(doc, i));
161 raw_line(doc, "\n");
165 static int indents(char *s)
167 char *r = s;
168 while (isspace(*r))
169 r++;
170 return r - s;
173 static int islist(char *first, char *line)
175 char *signs = "*-+";
176 if (!line) {
177 if (!first || strlen(first) < 2)
178 return 0;
179 if (first[1] != ' ' || !strchr(signs, first[0]))
180 return 0;
181 return 1;
183 if (strlen(line) < 2)
184 return 0;
185 return line[0] == first[0] && line[1] == first[1];
188 static int par_end(struct doc *doc, int beg, int end)
190 int i = beg;
191 char *line;
192 while (i < end && (line = doc_line(doc, i))) {
193 if (!*line || indents(line) || islist(line, NULL))
194 break;
195 i++;
197 return i;
200 static int doc_par(struct doc *doc, int beg, int end)
202 int i;
203 if (indents(doc_line(doc, beg)))
204 return 0;
205 doc->ops->par_beg(doc->fd);
206 i = par_end(doc, beg, end);
207 put_lines(doc, beg, i);
208 doc->ops->par_end(doc->fd);
209 return i - beg;
212 static int min(int a, int b)
214 return a < b ? a : b;
217 static int doc_deindent(struct doc *doc, int n, int indent)
219 char *line;
220 int result = n;
221 while ((line = doc_line(doc, n))) {
222 if (*line && indents(line) < indent)
223 break;
224 n++;
225 if (*line)
226 result = n;
228 return result;
231 static int doc_block(struct doc *doc, int beg, int end)
233 int level = indents(doc_line(doc, beg));
234 int next;
235 if (!level)
236 return 0;
237 next = min(end, doc_deindent(doc, beg + 1, level));
238 doc_handle(doc, beg, next, level);
239 return next - beg;
243 static int doc_list(struct doc *doc, int beg, int end)
245 int i = beg;
246 char *first = doc_line(doc, i);
247 char *line = first;
248 if (!islist(first, NULL))
249 return 0;
250 doc->ops->list_beg(doc->fd);
251 while ((line = doc_line(doc, i)) && islist(first, line)) {
252 int next = min(end, doc_deindent(doc, i + 1, 2));
253 int head = i;
254 doc->ops->item_beg(doc->fd);
256 doc->level += 2;
257 i = par_end(doc, i, next);
258 put_lines(doc, head, i);
259 doc->level -= 2;
261 doc_handle(doc, i, next, 2);
262 i = next;
263 doc->ops->item_end(doc->fd);
264 if (i == end)
265 break;
267 doc->ops->list_end(doc->fd);
268 return i - beg;
271 static int doc_pre(struct doc *doc, int beg, int end)
273 if (!doc->level)
274 return 0;
275 doc->ops->pre_beg(doc->fd);
276 raw_lines(doc, beg, end);
277 doc->ops->pre_end(doc->fd);
278 return end - beg;
281 static int doc_formula(struct doc *doc, int beg, int end)
283 int i;
284 if (!doc->level || *doc_line(doc, beg) != '=')
285 return 0;
286 doc->ops->formula_beg(doc->fd);
287 for (i = beg; i < end; i++) {
288 char *line = doc_line(doc, i);
289 if (i == beg)
290 line++;
291 raw_line(doc, line);
292 if (i != end - 1)
293 raw_line(doc, "\n");
295 doc->ops->formula_end(doc->fd);
296 return i - beg;
299 static int (*parts[])(struct doc *doc, int beg, int end) =
300 {doc_head, doc_list, doc_formula, doc_pre, doc_par, doc_block};
302 static void doc_handle(struct doc *doc, int beg, int end, int level)
304 int line = beg;
305 int i;
306 doc->level += level;
307 while (line < end) {
308 int c = 0;
309 for (i = 0; i < LENGTH(parts); i++)
310 if ((c = parts[i](doc, line, end)))
311 break;
312 line += c;
313 if (!c) {
314 put_lines(doc, line, line + 1);
315 line++;
318 doc->level -= level;
321 void fmt(int fd, struct txt *txt, struct fmt_ops *ops)
323 struct doc *doc = doc_alloc(fd, txt, ops);
324 doc->ops->doc_beg(fd);
325 doc_handle(doc, 0, txt->n, 0);
326 doc->ops->doc_end(fd);
327 doc_free(doc);