2 * man page backend for Halibut
10 static void man_text(FILE *, word
*, int newline
, int quote_props
);
11 static void man_codepara(FILE *, word
*);
12 static int man_convert(wchar_t *s
, int maxlen
,
13 char **result
, int quote_props
);
22 static manconfig
man_configure(paragraph
*source
) {
29 ret
.headnumbers
= FALSE
;
31 ret
.filename
= dupstr("output.1");
33 for (; source
; source
= source
->next
) {
34 if (source
->type
== para_Config
) {
35 if (!ustricmp(source
->keyword
, L
"man-identity")) {
38 wp
= uadv(source
->keyword
);
43 ret
.th
= mknewa(wchar_t, ep
- wp
+ 1);
44 memcpy(ret
.th
, wp
, (ep
- wp
+ 1) * sizeof(wchar_t));
45 } else if (!ustricmp(source
->keyword
, L
"man-headnumbers")) {
46 ret
.headnumbers
= utob(uadv(source
->keyword
));
47 } else if (!ustricmp(source
->keyword
, L
"man-mindepth")) {
48 ret
.mindepth
= utoi(uadv(source
->keyword
));
49 } else if (!ustricmp(source
->keyword
, L
"man-filename")) {
51 ret
.filename
= utoa_dup(uadv(source
->keyword
));
59 static void man_conf_cleanup(manconfig cf
)
65 paragraph
*man_config_filename(char *filename
)
68 wchar_t *ufilename
, *up
;
72 memset(p
, 0, sizeof(*p
));
73 p
->type
= para_Config
;
75 p
->fpos
.filename
= "<command line>";
76 p
->fpos
.line
= p
->fpos
.col
= -1;
78 ufilename
= ufroma_dup(filename
);
79 len
= ustrlen(ufilename
) + 2 + lenof(L
"man-filename");
80 p
->keyword
= mknewa(wchar_t, len
);
82 ustrcpy(up
, L
"man-filename");
84 ustrcpy(up
, ufilename
);
87 assert(up
- p
->keyword
< len
);
93 #define QUOTE_INITCTRL 1 /* quote initial . and ' on a line */
94 #define QUOTE_QUOTES 2 /* quote double quotes by doubling them */
96 void man_backend(paragraph
*sourceform
, keywordlist
*keywords
,
102 IGNORE(keywords
); /* we don't happen to need this */
103 IGNORE(idx
); /* or this */
105 conf
= man_configure(sourceform
);
108 * Open the output file.
110 fp
= fopen(conf
.filename
, "w");
112 error(err_cantopenw
, conf
.filename
);
116 /* Do the version ID */
117 for (p
= sourceform
; p
; p
= p
->next
)
118 if (p
->type
== para_VersionID
) {
119 fprintf(fp
, ".\\\" ");
120 man_text(fp
, p
->words
, TRUE
, 0);
123 /* .TH name-of-program manual-section */
125 if (conf
.th
&& *conf
.th
) {
129 for (wp
= conf
.th
; *wp
; wp
= uadv(wp
)) {
131 man_convert(wp
, 0, &c
, QUOTE_QUOTES
);
139 fprintf(fp
, ".UC\n");
141 for (p
= sourceform
; p
; p
= p
->next
) switch (p
->type
) {
143 * Things we ignore because we've already processed them or
144 * aren't going to touch them in this pass.
148 case para_Biblio
: /* only touch BiblioCited */
159 case para_UnnumberedChapter
:
165 if (p
->type
== para_Subsect
)
167 else if (p
->type
== para_Heading
)
171 if (depth
>= conf
.mindepth
) {
172 fprintf(fp
, ".SH \"");
173 if (conf
.headnumbers
&& p
->kwtext
) {
174 man_text(fp
, p
->kwtext
, FALSE
, QUOTE_QUOTES
);
177 man_text(fp
, p
->words
, FALSE
, QUOTE_QUOTES
);
187 fprintf(fp
, ".PP\n");
188 man_codepara(fp
, p
->words
);
196 fprintf(fp
, ".PP\n");
197 man_text(fp
, p
->words
, TRUE
, 0);
203 case para_Description
:
204 case para_BiblioCited
:
206 case para_NumberedList
:
207 if (p
->type
== para_Bullet
) {
208 fprintf(fp
, ".IP \"\\fBo\\fP\"\n"); /* FIXME: configurable? */
209 } else if (p
->type
== para_NumberedList
) {
210 fprintf(fp
, ".IP \"");
211 man_text(fp
, p
->kwtext
, FALSE
, QUOTE_QUOTES
);
213 } else if (p
->type
== para_Description
) {
215 * Do nothing; the .xP for this paragraph is the .IP
216 * which has come before it in the DescribedThing.
218 } else if (p
->type
== para_BiblioCited
) {
219 fprintf(fp
, ".IP \"");
220 man_text(fp
, p
->kwtext
, FALSE
, QUOTE_QUOTES
);
223 man_text(fp
, p
->words
, TRUE
, 0);
226 case para_DescribedThing
:
227 fprintf(fp
, ".IP \"");
228 man_text(fp
, p
->words
, FALSE
, QUOTE_QUOTES
);
234 * This isn't terribly good. Anyone who wants to do better
237 fprintf(fp
, ".PP\n----------------------------------------\n");
242 fprintf(fp
, ".RS\n");
246 fprintf(fp
, ".RE\n");
254 man_conf_cleanup(conf
);
258 * Convert a wide string into a string of chars. If `result' is
259 * non-NULL, mallocs the resulting string and stores a pointer to
260 * it in `*result'. If `result' is NULL, merely checks whether all
261 * characters in the string are feasible for the output character
264 * Return is nonzero if all characters are OK. If not all
265 * characters are OK but `result' is non-NULL, a result _will_
266 * still be generated!
268 * FIXME: Here is probably also a good place to do escaping sorts
269 * of things. I know I at least need to escape backslash, and full
270 * stops at the starts of words are probably trouble as well.
272 static int man_convert(wchar_t *s
, int maxlen
,
273 char **result
, int quote_props
) {
275 * FIXME. Currently this is ISO8859-1 only.
277 int doing
= (result
!= 0);
280 int plen
= 0, psize
= 0;
285 for (; *s
&& maxlen
!= 0; s
++, maxlen
--) {
289 if ((c
>= 32 && c
<= 126) ||
290 (c
>= 160 && c
<= 255)) {
294 /* Char is not OK. */
296 outc
= 0xBF; /* approximate the good old DEC `uh?' */
299 if (plen
+3 >= psize
) {
301 p
= resize(p
, psize
);
303 if (plen
== 0 && (outc
== '.' || outc
== '\'') &&
304 (quote_props
& QUOTE_INITCTRL
)) {
306 * Control character (. or ') at the start of a
307 * line. Quote it by putting \& (troff zero-width
312 } else if (outc
== '\\') {
314 * Quote backslashes by doubling them, always.
317 } else if (outc
== '"' && (quote_props
& QUOTE_QUOTES
)) {
319 * Double quote within double quotes. Quote it by
328 p
= resize(p
, plen
+1);
335 static void man_rdaddwc(rdstringc
*rs
, word
*text
, word
*end
,
339 for (; text
&& text
!= end
; text
= text
->next
) switch (text
->type
) {
352 case word_WhiteSpace
:
355 case word_WkCodeSpace
:
359 case word_WkCodeQuote
:
360 assert(text
->type
!= word_CodeQuote
&&
361 text
->type
!= word_WkCodeQuote
);
362 if (towordstyle(text
->type
) == word_Emph
&&
363 (attraux(text
->aux
) == attr_First
||
364 attraux(text
->aux
) == attr_Only
))
366 else if ((towordstyle(text
->type
) == word_Code
||
367 towordstyle(text
->type
) == word_WeakCode
) &&
368 (attraux(text
->aux
) == attr_First
||
369 attraux(text
->aux
) == attr_Only
))
371 if (removeattr(text
->type
) == word_Normal
) {
373 quote_props
&= ~QUOTE_INITCTRL
; /* not at start any more */
374 if (man_convert(text
->text
, 0, &c
, quote_props
))
377 man_rdaddwc(rs
, text
->alt
, NULL
, quote_props
);
379 } else if (removeattr(text
->type
) == word_WhiteSpace
) {
381 } else if (removeattr(text
->type
) == word_Quote
) {
383 if (quote_props
& QUOTE_QUOTES
)
386 if (towordstyle(text
->type
) == word_Emph
&&
387 (attraux(text
->aux
) == attr_Last
||
388 attraux(text
->aux
) == attr_Only
))
390 else if ((towordstyle(text
->type
) == word_Code
||
391 towordstyle(text
->type
) == word_WeakCode
) &&
392 (attraux(text
->aux
) == attr_Last
||
393 attraux(text
->aux
) == attr_Only
))
399 static void man_text(FILE *fp
, word
*text
, int newline
, int quote_props
) {
400 rdstringc t
= { 0, 0, NULL
};
402 man_rdaddwc(&t
, text
, NULL
, quote_props
| QUOTE_INITCTRL
);
403 fprintf(fp
, "%s", t
.text
);
409 static void man_codepara(FILE *fp
, word
*text
) {
410 fprintf(fp
, ".nf\n");
411 for (; text
; text
= text
->next
) if (text
->type
== word_WeakCode
) {
414 int quote_props
= QUOTE_INITCTRL
;
417 if (text
->next
&& text
->next
->type
== word_Emph
) {
418 e
= text
->next
->text
;
423 while (e
&& *e
&& *t
) {
427 for (n
= 0; t
[n
] && e
[n
] && e
[n
] == ec
; n
++);
432 man_convert(t
, n
, &c
, quote_props
);
433 quote_props
&= ~QUOTE_INITCTRL
;
434 fprintf(fp
, "%s", c
);
436 if (ec
== 'i' || ec
== 'b')
441 man_convert(t
, 0, &c
, quote_props
);
442 fprintf(fp
, "%s\n", c
);
445 fprintf(fp
, ".fi\n");