fmt: add % to the list of escapable chars
[ctxt.git] / fmt.c
blobad4d984506a225aa8ff7c3408c9cebd0546768d0
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 char *possible_inline(char *s) {
41 char *signs = "*$@%\\h";
42 char *home = s;
43 while (*s) {
44 if (strchr(signs, *s) && !isspace(*(s + 1)))
45 if (s == home || isspace(*(s - 1)))
46 return s;
47 s++;
49 return NULL;
52 static void fillbuf(char *buf, int len, char *beg, char *end)
54 int n = len - 1 < end - beg ? len - 1 : end - beg;
55 memcpy(buf, beg, n);
56 buf[n] = '\0';
59 static int startswith(char *heystack, char *needle)
61 while (*needle != '\0' && *needle == *heystack++)
62 needle++;
63 return !*needle;
66 static char *doc_put_inline(struct doc *doc, char *s)
68 char buf[1024];
69 char *r = NULL;
70 switch (*s) {
71 case '*':
72 r = strchr(s + 1, *s);
73 if (r) {
74 fillbuf(buf, LENGTH(buf), s + 1, r);
75 doc->ops->put_emph(doc->fd, buf);
76 r++;
78 return r;
79 case '$':
80 r = strchr(s + 1, *s);
81 if (r) {
82 fillbuf(buf, LENGTH(buf), s, r + 1);
83 doc->ops->put_raw(doc->fd, buf);
84 r++;
86 return r;
87 case '%':
88 r = strchr(s + 1, *s);
89 if (r) {
90 fillbuf(buf, LENGTH(buf), s + 1, r);
91 doc->ops->put_raw(doc->fd, buf);
92 r++;
94 return r;
95 case '@':
96 for (r = s + 1; isalnum(*r); r++);
97 if (r) {
98 fillbuf(buf, LENGTH(buf), s + 1, r);
99 doc->ops->put_ref(doc->fd, buf);
101 return r;
102 case '\\':
103 if (strchr("*$@%\\", *(s + 1))) {
104 fillbuf(buf, LENGTH(buf), s + 1, s + 2);
105 doc->ops->put(doc->fd, buf);
106 return s + 2;
108 return NULL;
109 case 'h':
110 if (startswith(s, "http://") || startswith(s, "ftp://")) {
111 char desc[1024] = "";
112 for (r = s; *r && !isspace(*r); r++);
113 fillbuf(buf, LENGTH(buf), s, r);
114 if (*r && (*r == '(' || *++r == '(')) {
115 char *d = strchr(r, ')');
116 if (d) {
117 fillbuf(desc, LENGTH(desc),
118 r + 1, d);
119 r = d + 1;
122 doc->ops->put_url(doc->fd, buf, desc);
123 return r;
125 return NULL;
126 default:
127 break;
129 return NULL;
132 static void put_text(struct doc *doc, char *s)
134 char buf[1024];
135 char *done = s;
136 while (*s) {
137 char *r = possible_inline(s);
138 if (!r)
139 break;
140 fillbuf(buf, LENGTH(buf), done, r);
141 doc->ops->put(doc->fd, buf);
142 s = done = r;
143 if ((r = doc_put_inline(doc, r)))
144 s = done = r;
145 else
146 s++;
148 doc->ops->put(doc->fd, done);
151 static void raw_line(struct doc *doc, char *s)
153 doc->ops->put(doc->fd, s);
156 static void put_line(struct doc *doc, int n)
158 put_text(doc, doc_line(doc, n));
159 put_text(doc, "\n");
162 static void put_lines(struct doc *doc, int beg, int end)
164 int i;
165 for (i = beg; i < end; i++)
166 put_line(doc, i);
169 static void raw_lines(struct doc *doc, int beg, int end)
171 int i;
172 for (i = beg; i < end; i++) {
173 raw_line(doc, doc_line(doc, i));
174 raw_line(doc, "\n");
178 static int indents(char *s)
180 char *r = s;
181 while (isspace(*r))
182 r++;
183 return r - s;
186 static int islist(char *first, char *line)
188 char *signs = "*-+";
189 if (!line) {
190 if (!first || strlen(first) < 2)
191 return 0;
192 if (first[1] != ' ' || !strchr(signs, first[0]))
193 return 0;
194 return 1;
196 if (strlen(line) < 2)
197 return 0;
198 return line[0] == first[0] && line[1] == first[1];
201 static void doc_handle(struct doc *doc, int beg, int end, int level);
203 static int doc_head(struct doc *doc, int beg, int end)
205 char *line = doc_line(doc, beg);
206 char *next = doc_line(doc, beg + 1);
207 char c;
208 char *s = next;
209 char *signs = "=-~";
210 if (!next || !*line || beg == end || doc->level)
211 return 0;
212 c = *next;
213 if (!c || !strchr(signs, *next))
214 return 0;
215 while (*++s)
216 if (*s != c)
217 return 0;
218 doc->ops->head_beg(doc->fd, strchr(signs, *next) - signs);
219 put_text(doc, line);
220 doc->ops->head_end(doc->fd, strchr(signs, *next) - signs);
221 return 2;
224 static int par_end(struct doc *doc, int beg, int end)
226 int i = beg;
227 char *line;
228 while (i < end && (line = doc_line(doc, i))) {
229 if (!*line || indents(line) || islist(line, NULL))
230 break;
231 i++;
233 return i;
236 static int doc_par(struct doc *doc, int beg, int end)
238 int i;
239 if (indents(doc_line(doc, beg)))
240 return 0;
241 doc->ops->par_beg(doc->fd);
242 i = par_end(doc, beg, end);
243 put_lines(doc, beg, i);
244 doc->ops->par_end(doc->fd);
245 return i - beg;
248 static int min(int a, int b)
250 return a < b ? a : b;
253 static int doc_deindent(struct doc *doc, int n, int indent)
255 char *line;
256 int result = n;
257 while ((line = doc_line(doc, n))) {
258 if (*line && indents(line) < indent)
259 break;
260 n++;
261 if (*line)
262 result = n;
264 return result;
267 static int doc_block(struct doc *doc, int beg, int end)
269 int level = indents(doc_line(doc, beg));
270 int next;
271 if (!level)
272 return 0;
273 next = min(end, doc_deindent(doc, beg + 1, level));
274 doc_handle(doc, beg, next, level);
275 return next - beg;
278 static int doc_list(struct doc *doc, int beg, int end)
280 int i = beg;
281 char *first = doc_line(doc, i);
282 char *line = first;
283 if (!islist(first, NULL))
284 return 0;
285 doc->ops->list_beg(doc->fd);
286 while ((line = doc_line(doc, i)) && islist(first, line)) {
287 int next = min(end, doc_deindent(doc, i + 1, 2));
288 int head = i;
289 doc->ops->item_beg(doc->fd);
291 doc->level += 2;
292 i = par_end(doc, i, next);
293 put_lines(doc, head, i);
294 doc->level -= 2;
296 doc_handle(doc, i, next, 2);
297 i = next;
298 doc->ops->item_end(doc->fd);
299 if (i == end)
300 break;
302 doc->ops->list_end(doc->fd);
303 return i - beg;
306 static int doc_pre(struct doc *doc, int beg, int end)
308 if (!doc->level)
309 return 0;
310 doc->ops->pre_beg(doc->fd);
311 raw_lines(doc, beg, end);
312 doc->ops->pre_end(doc->fd);
313 return end - beg;
316 static int doc_formula(struct doc *doc, int beg, int end)
318 int i;
319 if (!doc->level || *doc_line(doc, beg) != '$')
320 return 0;
321 doc->ops->formula_beg(doc->fd);
322 for (i = beg; i < end; i++) {
323 char *line = doc_line(doc, i);
324 if (i == beg)
325 line++;
326 raw_line(doc, line);
327 if (i != end - 1)
328 raw_line(doc, "\n");
330 doc->ops->formula_end(doc->fd);
331 return end - beg;
334 static int doc_raw(struct doc *doc, int beg, int end)
336 if (!doc->level || *doc_line(doc, beg) != '%')
337 return 0;
338 raw_line(doc, doc_line(doc, beg) + 1);
339 raw_line(doc, "\n");
340 raw_lines(doc, beg + 1, end);
341 return end - beg;
344 static int (*parts[])(struct doc *doc, int beg, int end) =
345 {doc_head, doc_list, doc_formula, doc_raw,
346 doc_pre, doc_par, doc_block};
348 static void doc_handle(struct doc *doc, int beg, int end, int level)
350 int line = beg;
351 int i;
352 doc->level += level;
353 while (line < end) {
354 int c = 0;
355 if (!*doc_line(doc, line)) {
356 put_line(doc, line++);
357 continue;
359 for (i = 0; i < LENGTH(parts); i++)
360 if ((c = parts[i](doc, line, end)))
361 break;
362 line += c;
363 if (!c)
364 put_line(doc, line++);
366 doc->level -= level;
369 void fmt(int fd, struct txt *txt, struct fmt_ops *ops)
371 struct doc *doc = doc_alloc(fd, txt, ops);
372 doc->ops->doc_beg(fd);
373 doc_handle(doc, 0, txt->n, 0);
374 doc->ops->doc_end(fd);
375 doc_free(doc);