fmt: start a paragraph only after a blank line
[ctxt.git] / troff.c
bloba99038439596ad5b872d1ad664cc3723e15283d3
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
12 * After printing an inline macro, eatspc_on() is called which forces
13 * troff_put() to ignore all whitespaces until the first
14 * non-space character. Some care is necessary when handling dots; if
15 * a dot appears just after a newline, it is a troff macro, otherwise
16 * it is a printable dot and should be escaped.
18 #include <stdlib.h>
19 #include <string.h>
20 #include <stdio.h>
21 #include "ctxt.h"
23 static int eatspc; /* jump space chars */
24 static int gotnl; /* last output char was a newline */
25 static int inblk; /* inside a block */
27 static void eatspc_on(void)
29 gotnl = 0;
30 eatspc = 1;
33 static void troff_doc_beg(struct doc *doc)
37 static void troff_doc_end(struct doc *doc)
41 static void troff_put(struct doc *doc, char *s)
43 if (!*s)
44 return;
45 if (inblk) {
46 doc_write(doc, s);
47 return;
49 if (eatspc) {
50 while (*s == ' ' || *s == '\t' || *s == '\n')
51 gotnl = *s++ == '\n';
52 eatspc = *s == '\0'; /* more eatspc if s was space */
53 if (!gotnl && *s == '.')
54 doc_write(doc, "\\&");
55 else if (!eatspc) /* exiting eatspc; set gotnl */
56 gotnl = 1;
58 while (gotnl && *s == '\n')
59 s++;
60 while (*s) {
61 char *r = strchr(s, '\n');
62 r = r ? r + 1 : strchr(s, '\0');
63 doc_memcat(doc, s, r - s);
64 s = r;
65 while (*s == '\n')
66 s++;
68 gotnl = s[-1] == '\n';
71 static void troff_head_beg(struct doc *doc, int level)
73 switch (level) {
74 case 0:
75 troff_put(doc, ".NH 1\n");
76 break;
77 case 1:
78 troff_put(doc, ".NH 2\n");
79 break;
80 default:
81 troff_put(doc, ".SH\n");
85 static void troff_head_end(struct doc *doc, int level)
89 static void troff_par_beg(struct doc *doc)
91 troff_put(doc, ".PP\n");
94 static void troff_par_end(struct doc *doc)
98 static int ldepth;
100 static void troff_list_beg(struct doc *doc, int mark)
102 troff_put(doc, "\n.br\n");
103 eatspc_on();
104 ldepth++;
105 if (ldepth > 1)
106 doc_write(doc, ".RS\n");
109 static void troff_list_end(struct doc *doc)
111 if (ldepth > 1)
112 doc_write(doc, ".RE\n");
113 ldepth--;
116 static void troff_item_beg(struct doc *doc, char *head)
118 char b[1024];
119 if (head) {
120 sprintf(b, ".IP \"%s\" 1i\n", head);
121 doc_write(doc, b);
122 } else {
123 doc_write(doc, ".IP \\(bu 2\n");
125 eatspc_on();
128 static void troff_item_end(struct doc *doc)
132 static void troff_block_beg(struct doc *doc, char *beg)
134 inblk = 1;
135 troff_put(doc, beg ? beg : ".DS");
136 troff_put(doc, "\n");
139 static void troff_block_end(struct doc *doc, char *end)
141 troff_put(doc, end ? end : ".DE");
142 troff_put(doc, "\n");
143 inblk = 0;
146 static void troff_put_txt(struct doc *doc, char *s, char *m)
148 char b[1024];
149 switch(m[0]) {
150 case '*':
151 troff_put(doc, "\\f3");
152 troff_put(doc, s);
153 troff_put(doc, "\\fP");
154 break;
155 case '/':
156 troff_put(doc, "\\f2");
157 troff_put(doc, s);
158 troff_put(doc, "\\fP");
159 break;
160 case '%':
161 troff_put(doc, s);
162 break;
163 case '|':
164 troff_put(doc, "\n.[[\n");
165 troff_put(doc, s);
166 troff_put(doc, "\n.]]\n");
167 eatspc_on();
168 break;
169 case '[':
170 troff_put(doc, "\n.FS\n");
171 troff_put(doc, s);
172 troff_put(doc, "\n.FE\n");
173 eatspc_on();
174 break;
175 default:
176 sprintf(b, "%c%s%c", m[0], s, m[1]);
177 troff_put(doc, b);
181 static void troff_table_beg(struct doc *doc, int columns)
183 int i;
184 troff_put(doc, ".TS\n");
185 if (columns) {
186 troff_put(doc, "allbox;\n");
187 for (i = 0; i < columns; i++)
188 troff_put(doc, "c ");
189 troff_put(doc, ".\n");
193 static void troff_table_end(struct doc *doc)
195 troff_put(doc, ".TE\n");
198 /* a hack to identify the first entry in each row */
199 static int entcol;
201 static void troff_row_beg(struct doc *doc)
203 entcol = 0;
206 static void troff_row_end(struct doc *doc)
208 troff_put(doc, "\n");
211 static void troff_entry_beg(struct doc *doc)
213 if (entcol++)
214 troff_put(doc, "\t");
215 troff_put(doc, "T{\n");
218 static void troff_entry_end(struct doc *doc)
220 troff_put(doc, "\nT}");
223 struct fmt_ops troff_ops = {
224 .doc_beg = troff_doc_beg,
225 .doc_end = troff_doc_end,
226 .head_beg = troff_head_beg,
227 .head_end = troff_head_end,
228 .par_beg = troff_par_beg,
229 .par_end = troff_par_end,
230 .list_beg = troff_list_beg,
231 .list_end = troff_list_end,
232 .item_beg = troff_item_beg,
233 .item_end = troff_item_end,
234 .table_beg = troff_table_beg,
235 .table_end = troff_table_end,
236 .row_beg = troff_row_beg,
237 .row_end = troff_row_end,
238 .entry_beg = troff_entry_beg,
239 .entry_end = troff_entry_end,
240 .block_beg = troff_block_beg,
241 .block_end = troff_block_end,
242 .put = troff_put,
243 .put_txt = troff_put_txt