X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/halibut/blobdiff_plain/93688997ac35ff239dad82a093d3313a4c46ac62..24c1a072a6b3e9bf0f9fba6385ddf61e63b91154:/bk_man.c diff --git a/bk_man.c b/bk_man.c index 6774c2e..21e33f3 100644 --- a/bk_man.c +++ b/bk_man.c @@ -7,22 +7,24 @@ #include #include "halibut.h" -static void man_text(FILE *, word *, - int newline, int quote_props, int charset); -static void man_codepara(FILE *, word *, int charset); -static int man_convert(wchar_t const *s, int maxlen, - char **result, int quote_props, - int charset, charset_state *state); - typedef struct { wchar_t *th; int headnumbers; int mindepth; char *filename; int charset; + wchar_t *bullet, *lquote, *rquote; } manconfig; +static void man_text(FILE *, word *, + int newline, int quote_props, manconfig *conf); +static void man_codepara(FILE *, word *, int charset); +static int man_convert(wchar_t const *s, int maxlen, + char **result, int quote_props, + int charset, charset_state *state); + static manconfig man_configure(paragraph *source) { + paragraph *p; manconfig ret; /* @@ -33,34 +35,73 @@ static manconfig man_configure(paragraph *source) { ret.mindepth = 0; ret.filename = dupstr("output.1"); ret.charset = CS_ASCII; + ret.bullet = L"\x2022\0o\0\0"; + ret.lquote = L"\x2018\0\x2019\0\"\0\"\0\0"; + ret.rquote = uadv(ret.lquote); + + /* + * Two-pass configuration so that we can pick up global config + * (e.g. `quotes') before having it overridden by specific + * config (`man-quotes'), irrespective of the order in which + * they occur. + */ + for (p = source; p; p = p->next) { + if (p->type == para_Config) { + if (!ustricmp(p->keyword, L"quotes")) { + if (*uadv(p->keyword) && *uadv(uadv(p->keyword))) { + ret.lquote = uadv(p->keyword); + ret.rquote = uadv(ret.lquote); + } + } + } + } - for (; source; source = source->next) { - if (source->type == para_Config) { - if (!ustricmp(source->keyword, L"man-identity")) { + for (p = source; p; p = p->next) { + if (p->type == para_Config) { + if (!ustricmp(p->keyword, L"man-identity")) { wchar_t *wp, *ep; - wp = uadv(source->keyword); + wp = uadv(p->keyword); ep = wp; while (*ep) ep = uadv(ep); sfree(ret.th); - ret.th = mknewa(wchar_t, ep - wp + 1); + ret.th = snewn(ep - wp + 1, wchar_t); memcpy(ret.th, wp, (ep - wp + 1) * sizeof(wchar_t)); - } else if (!ustricmp(source->keyword, L"man-charset")) { - char *csname = utoa_dup(uadv(source->keyword), CS_ASCII); - ret.charset = charset_from_localenc(csname); - sfree(csname); - } else if (!ustricmp(source->keyword, L"man-headnumbers")) { - ret.headnumbers = utob(uadv(source->keyword)); - } else if (!ustricmp(source->keyword, L"man-mindepth")) { - ret.mindepth = utoi(uadv(source->keyword)); - } else if (!ustricmp(source->keyword, L"man-filename")) { + } else if (!ustricmp(p->keyword, L"man-charset")) { + ret.charset = charset_from_ustr(&p->fpos, uadv(p->keyword)); + } else if (!ustricmp(p->keyword, L"man-headnumbers")) { + ret.headnumbers = utob(uadv(p->keyword)); + } else if (!ustricmp(p->keyword, L"man-mindepth")) { + ret.mindepth = utoi(uadv(p->keyword)); + } else if (!ustricmp(p->keyword, L"man-filename")) { sfree(ret.filename); - ret.filename = dupstr(adv(source->origkeyword)); + ret.filename = dupstr(adv(p->origkeyword)); + } else if (!ustricmp(p->keyword, L"man-bullet")) { + ret.bullet = uadv(p->keyword); + } else if (!ustricmp(p->keyword, L"man-quotes")) { + if (*uadv(p->keyword) && *uadv(uadv(p->keyword))) { + ret.lquote = uadv(p->keyword); + ret.rquote = uadv(ret.lquote); + } } } } + /* + * Now process fallbacks on quote characters and bullets. + */ + while (*uadv(ret.rquote) && *uadv(uadv(ret.rquote)) && + (!cvt_ok(ret.charset, ret.lquote) || + !cvt_ok(ret.charset, ret.rquote))) { + ret.lquote = uadv(ret.rquote); + ret.rquote = uadv(ret.lquote); + } + + while (*ret.bullet && *uadv(ret.bullet) && + !cvt_ok(ret.charset, ret.bullet)) + ret.bullet = uadv(ret.bullet); + return ret; } @@ -83,6 +124,7 @@ void man_backend(paragraph *sourceform, keywordlist *keywords, paragraph *p; FILE *fp; manconfig conf; + int had_described_thing; IGNORE(unused); IGNORE(keywords); @@ -103,7 +145,7 @@ void man_backend(paragraph *sourceform, keywordlist *keywords, for (p = sourceform; p; p = p->next) if (p->type == para_VersionID) { fprintf(fp, ".\\\" "); - man_text(fp, p->words, TRUE, 0, conf.charset); + man_text(fp, p->words, TRUE, 0, &conf); } /* .TH name-of-program manual-section */ @@ -124,6 +166,13 @@ void man_backend(paragraph *sourceform, keywordlist *keywords, fprintf(fp, ".UC\n"); + had_described_thing = FALSE; +#define cleanup_described_thing do { \ + if (had_described_thing) \ + fprintf(fp, "\n"); \ + had_described_thing = FALSE; \ +} while (0) + for (p = sourceform; p; p = p->next) switch (p->type) { /* * Things we ignore because we've already processed them or @@ -146,6 +195,7 @@ void man_backend(paragraph *sourceform, keywordlist *keywords, case para_Heading: case para_Subsect: + cleanup_described_thing; { int depth; if (p->type == para_Subsect) @@ -155,12 +205,15 @@ void man_backend(paragraph *sourceform, keywordlist *keywords, else depth = 0; if (depth >= conf.mindepth) { - fprintf(fp, ".SH \""); + if (depth > conf.mindepth) + fprintf(fp, ".SS \""); + else + fprintf(fp, ".SH \""); if (conf.headnumbers && p->kwtext) { - man_text(fp, p->kwtext, FALSE, QUOTE_QUOTES, conf.charset); + man_text(fp, p->kwtext, FALSE, QUOTE_QUOTES, &conf); fprintf(fp, " "); } - man_text(fp, p->words, FALSE, QUOTE_QUOTES, conf.charset); + man_text(fp, p->words, FALSE, QUOTE_QUOTES, &conf); fprintf(fp, "\"\n"); } break; @@ -170,6 +223,7 @@ void man_backend(paragraph *sourceform, keywordlist *keywords, * Code paragraphs. */ case para_Code: + cleanup_described_thing; fprintf(fp, ".PP\n"); man_codepara(fp, p->words, conf.charset); break; @@ -179,8 +233,9 @@ void man_backend(paragraph *sourceform, keywordlist *keywords, */ case para_Normal: case para_Copyright: + cleanup_described_thing; fprintf(fp, ".PP\n"); - man_text(fp, p->words, TRUE, 0, conf.charset); + man_text(fp, p->words, TRUE, 0, &conf); break; /* @@ -190,29 +245,48 @@ void man_backend(paragraph *sourceform, keywordlist *keywords, case para_BiblioCited: case para_Bullet: case para_NumberedList: + if (p->type != para_Description) + cleanup_described_thing; + if (p->type == para_Bullet) { - fprintf(fp, ".IP \"\\fBo\\fP\"\n"); /* FIXME: configurable? */ + char *bullettext; + man_convert(conf.bullet, -1, &bullettext, QUOTE_QUOTES, + conf.charset, NULL); + fprintf(fp, ".IP \"\\fB%s\\fP\"\n", bullettext); + sfree(bullettext); } else if (p->type == para_NumberedList) { fprintf(fp, ".IP \""); - man_text(fp, p->kwtext, FALSE, QUOTE_QUOTES, conf.charset); + man_text(fp, p->kwtext, FALSE, QUOTE_QUOTES, &conf); fprintf(fp, "\"\n"); } else if (p->type == para_Description) { - /* - * Do nothing; the .xP for this paragraph is the .IP - * which has come before it in the DescribedThing. - */ + if (had_described_thing) { + /* + * Do nothing; the .xP for this paragraph is the + * .IP which has come before it in the + * DescribedThing. + */ + } else { + /* + * A \dd without a preceding \dt is given a blank + * one. + */ + fprintf(fp, ".IP \"\"\n"); + } } else if (p->type == para_BiblioCited) { fprintf(fp, ".IP \""); - man_text(fp, p->kwtext, FALSE, QUOTE_QUOTES, conf.charset); + man_text(fp, p->kwtext, FALSE, QUOTE_QUOTES, &conf); fprintf(fp, "\"\n"); } - man_text(fp, p->words, TRUE, 0, conf.charset); + man_text(fp, p->words, TRUE, 0, &conf); + had_described_thing = FALSE; break; case para_DescribedThing: + cleanup_described_thing; fprintf(fp, ".IP \""); - man_text(fp, p->words, FALSE, QUOTE_QUOTES, conf.charset); + man_text(fp, p->words, FALSE, QUOTE_QUOTES, &conf); fprintf(fp, "\"\n"); + had_described_thing = TRUE; break; case para_Rule: @@ -220,18 +294,22 @@ void man_backend(paragraph *sourceform, keywordlist *keywords, * This isn't terribly good. Anyone who wants to do better * should feel free! */ + cleanup_described_thing; fprintf(fp, ".PP\n----------------------------------------\n"); break; case para_LcontPush: case para_QuotePush: + cleanup_described_thing; fprintf(fp, ".RS\n"); break; case para_LcontPop: case para_QuotePop: + cleanup_described_thing; fprintf(fp, ".RE\n"); break; } + cleanup_described_thing; /* * Tidy up. @@ -274,7 +352,7 @@ static int man_convert(wchar_t const *s, int maxlen, psize = 384; plen = 0; - p = mknewa(char, psize); + p = snewn(psize, char); err = 0; while (slen > 0) { @@ -284,7 +362,7 @@ static int man_convert(wchar_t const *s, int maxlen, plen += ret; if (psize - plen < 256) { psize = plen + 256; - p = resize(p, psize); + p = sresize(p, psize, char); } } } @@ -332,7 +410,8 @@ static int man_convert(wchar_t const *s, int maxlen, } static void man_rdaddwc(rdstringc *rs, word *text, word *end, - int quote_props, int charset, charset_state *state) { + int quote_props, manconfig *conf, + charset_state *state) { char *c; for (; text && text != end; text = text->next) switch (text->type) { @@ -364,7 +443,7 @@ static void man_rdaddwc(rdstringc *rs, word *text, word *end, attraux(text->aux) == attr_Only)) { if (rs->pos > 0) quote_props &= ~QUOTE_INITCTRL; /* not at start any more */ - man_convert(NULL, 0, &c, quote_props, charset, state); + man_convert(NULL, 0, &c, quote_props, conf->charset, state); rdaddsc(rs, c); sfree(c); *state = charset_init_state; @@ -375,7 +454,7 @@ static void man_rdaddwc(rdstringc *rs, word *text, word *end, attraux(text->aux) == attr_Only)) { if (rs->pos > 0) quote_props &= ~QUOTE_INITCTRL; /* not at start any more */ - man_convert(NULL, 0, &c, quote_props, charset, state); + man_convert(NULL, 0, &c, quote_props, conf->charset, state); rdaddsc(rs, c); sfree(c); *state = charset_init_state; @@ -387,24 +466,26 @@ static void man_rdaddwc(rdstringc *rs, word *text, word *end, if (rs->pos > 0) quote_props &= ~QUOTE_INITCTRL; /* not at start any more */ - if (man_convert(text->text, 0, &c, quote_props, charset, &s2) || + if (man_convert(text->text, 0, &c, quote_props, conf->charset, &s2) || !text->alt) { rdaddsc(rs, c); *state = s2; } else { - man_rdaddwc(rs, text->alt, NULL, quote_props, charset, state); + man_rdaddwc(rs, text->alt, NULL, quote_props, conf, state); } sfree(c); } else if (removeattr(text->type) == word_WhiteSpace) { if (rs->pos > 0) quote_props &= ~QUOTE_INITCTRL; /* not at start any more */ - man_convert(L" ", 1, &c, quote_props, charset, state); + man_convert(L" ", 1, &c, quote_props, conf->charset, state); rdaddsc(rs, c); sfree(c); } else if (removeattr(text->type) == word_Quote) { if (rs->pos > 0) quote_props &= ~QUOTE_INITCTRL; /* not at start any more */ - man_convert(L"\"", 1, &c, quote_props, charset, state); + man_convert(quoteaux(text->aux) == quote_Open ? + conf->lquote : conf->rquote, 0, + &c, quote_props, conf->charset, state); rdaddsc(rs, c); sfree(c); } @@ -413,7 +494,7 @@ static void man_rdaddwc(rdstringc *rs, word *text, word *end, attraux(text->aux) == attr_Only)) { if (rs->pos > 0) quote_props &= ~QUOTE_INITCTRL; /* not at start any more */ - man_convert(NULL, 0, &c, quote_props, charset, state); + man_convert(NULL, 0, &c, quote_props, conf->charset, state); rdaddsc(rs, c); sfree(c); *state = charset_init_state; @@ -421,17 +502,17 @@ static void man_rdaddwc(rdstringc *rs, word *text, word *end, } break; } - man_convert(NULL, 0, &c, quote_props, charset, state); + man_convert(NULL, 0, &c, quote_props, conf->charset, state); rdaddsc(rs, c); sfree(c); } static void man_text(FILE *fp, word *text, int newline, - int quote_props, int charset) { + int quote_props, manconfig *conf) { rdstringc t = { 0, 0, NULL }; charset_state state = CHARSET_INIT_STATE; - man_rdaddwc(&t, text, NULL, quote_props | QUOTE_INITCTRL, charset, &state); + man_rdaddwc(&t, text, NULL, quote_props | QUOTE_INITCTRL, conf, &state); fprintf(fp, "%s", t.text); sfree(t.text); if (newline)