troff: don't skip spaces after troff_list_beg()
[ctxt.git] / troff.c
blob623fe6abd9469d07a891d6b4d9b3ba1317d77b70
1 /*
2 * The main challenge in troff backend is managing unnecessary spaces:
3 * + no newline should appear in the output, except in blocks
4 * + no excess whitespace should appear after inline dot commands
5 * like footnotes
7 * troff_put() manages white spaces:
8 * + gotnl: is set when the last character is a newline
9 * + eatspc: when one, ignore as much space as possible
10 * + inblk: we are in a block and no whitespace processing should be done
11 * + refer: right after .]] refer macro
13 * After printing an inline macro, eatspc_on() is called which forces
14 * troff_put() to ignore all whitespaces until the first
15 * non-space character. Some care is necessary when handling dots; if
16 * a dot appears just after a newline, it is a troff macro, otherwise
17 * it is a printable dot and should be escaped.
19 #include <ctype.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <stdio.h>
23 #include "ctxt.h"
25 static int eatspc; /* jump space chars */
26 static int gotnl; /* last output char was a newline */
27 static int inblk; /* inside a block */
28 static int refer; /* right after refer .]] */
30 static void eatspc_on(void)
32 gotnl = 0;
33 eatspc = 1;
36 static void troff_doc_beg(struct doc *doc)
40 static void troff_doc_end(struct doc *doc)
44 static void troff_put(struct doc *doc, char *s)
46 if (!*s)
47 return;
48 if (inblk) {
49 doc_write(doc, s);
50 return;
52 if (refer) {
53 char *r = s;
54 while (*s && !isspace(*s))
55 s++;
56 doc_memcat(doc, r, s - r);
57 doc_write(doc, "\n");
58 refer = 0;
60 if (eatspc) {
61 while (*s == ' ' || *s == '\t' || *s == '\n')
62 gotnl = *s++ == '\n';
63 eatspc = *s == '\0'; /* more eatspc if s was space */
64 if (!gotnl && *s == '.')
65 doc_write(doc, "\\&");
66 else if (!eatspc) /* exiting eatspc; set gotnl */
67 gotnl = 1;
69 while (gotnl && *s == '\n')
70 s++;
71 while (*s) {
72 char *r = strchr(s, '\n');
73 r = r ? r + 1 : strchr(s, '\0');
74 doc_memcat(doc, s, r - s);
75 s = r;
76 while (*s == '\n')
77 s++;
79 gotnl = s[-1] == '\n';
82 static void troff_head_beg(struct doc *doc, int level)
84 switch (level) {
85 case 0:
86 troff_put(doc, ".NH 1\n");
87 break;
88 case 1:
89 troff_put(doc, ".NH 2\n");
90 break;
91 default:
92 troff_put(doc, ".SH\n");
96 static void troff_head_end(struct doc *doc, int level)
100 static void troff_par_beg(struct doc *doc)
102 troff_put(doc, ".PP\n");
105 static void troff_par_end(struct doc *doc)
109 static int ldepth;
111 static void troff_list_beg(struct doc *doc, int mark)
113 troff_put(doc, "\n.br\n");
114 ldepth++;
115 if (ldepth > 1)
116 troff_put(doc, ".RS\n");
119 static void troff_list_end(struct doc *doc)
121 if (ldepth > 1)
122 troff_put(doc, ".RE\n");
123 ldepth--;
126 static void troff_item_beg(struct doc *doc, char *head)
128 char b[1024];
129 if (head) {
130 sprintf(b, ".IP \"%s\" 1i\n", head);
131 troff_put(doc, b);
132 } else {
133 troff_put(doc, ".IP \\(bu 2\n");
135 eatspc_on();
138 static void troff_item_end(struct doc *doc)
142 static void troff_block_beg(struct doc *doc, char *beg)
144 inblk = 1;
145 troff_put(doc, beg ? beg : ".DS");
146 troff_put(doc, "\n");
149 static void troff_block_end(struct doc *doc, char *end)
151 troff_put(doc, end ? end : ".DE");
152 troff_put(doc, "\n");
153 inblk = 0;
156 static void troff_put_txt(struct doc *doc, char *s, char *m)
158 char b[1024];
159 switch(m[0]) {
160 case '*':
161 troff_put(doc, "\\f3");
162 troff_put(doc, s);
163 troff_put(doc, "\\fP");
164 break;
165 case '/':
166 troff_put(doc, "\\f2");
167 troff_put(doc, s);
168 troff_put(doc, "\\fP");
169 break;
170 case '%':
171 troff_put(doc, s);
172 break;
173 case '|':
174 troff_put(doc, "\n.[[\n");
175 troff_put(doc, s);
176 troff_put(doc, "\n.]]");
177 eatspc_on();
178 refer = 1;
179 break;
180 case '[':
181 troff_put(doc, "\n.FS\n");
182 troff_put(doc, s);
183 troff_put(doc, "\n.FE\n");
184 eatspc_on();
185 break;
186 default:
187 sprintf(b, "%c%s%c", m[0], s, m[1]);
188 troff_put(doc, b);
192 static void troff_table_beg(struct doc *doc, int columns)
194 int i;
195 troff_put(doc, ".TS\n");
196 if (columns) {
197 troff_put(doc, "allbox;\n");
198 for (i = 0; i < columns; i++)
199 troff_put(doc, "c ");
200 troff_put(doc, ".\n");
204 static void troff_table_end(struct doc *doc)
206 troff_put(doc, ".TE\n");
209 /* a hack to identify the first entry in each row */
210 static int entcol;
212 static void troff_row_beg(struct doc *doc)
214 entcol = 0;
217 static void troff_row_end(struct doc *doc)
219 troff_put(doc, "\n");
222 static void troff_entry_beg(struct doc *doc)
224 if (entcol++)
225 troff_put(doc, "\t");
226 troff_put(doc, "T{\n");
229 static void troff_entry_end(struct doc *doc)
231 troff_put(doc, "\nT}");
234 struct fmt_ops troff_ops = {
235 .doc_beg = troff_doc_beg,
236 .doc_end = troff_doc_end,
237 .head_beg = troff_head_beg,
238 .head_end = troff_head_end,
239 .par_beg = troff_par_beg,
240 .par_end = troff_par_end,
241 .list_beg = troff_list_beg,
242 .list_end = troff_list_end,
243 .item_beg = troff_item_beg,
244 .item_end = troff_item_end,
245 .table_beg = troff_table_beg,
246 .table_end = troff_table_end,
247 .row_beg = troff_row_beg,
248 .row_end = troff_row_end,
249 .entry_beg = troff_entry_beg,
250 .entry_end = troff_entry_end,
251 .block_beg = troff_block_beg,
252 .block_end = troff_block_end,
253 .put = troff_put,
254 .put_txt = troff_put_txt