+typedef struct {
+ wchar_t *th;
+ int headnumbers;
+ int mindepth;
+ char *filename;
+ int charset;
+ wchar_t *bullet, *rule, *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);
+
+/*
+ * My TROFF reference is "NROFF/TROFF User's Manual", Joseph
+ * F. Ossana, October 11 1976.
+ *
+ * not yet used:
+ * \(ru rule
+ * \(pl math plus
+ * \(mi math minus
+ * \(eq math equals
+ * \(ga grave accent
+ * \(ul underrule
+ * \(sl slash (matching bakslash)
+ * \(br box vertical rule
+ * \(br Bell System logo
+ * \(or or
+ * all characters for constructing large brackets
+ */
+
+static struct {
+ unsigned short uni;
+ char const *troff;
+} const man_charmap[] = {
+ {0x00A2, "\\(ct"}, {0x00A7, "\\(sc"}, {0x00A9, "\\(co"}, {0x00AC, "\\(no"},
+ {0x00AE, "\\(rg"}, {0x00B0, "\\(de"}, {0x00B1, "\\(+-"}, {0x00B4, "\\(aa"},
+ {0x00BC, "\\(14"}, {0x00BD, "\\(12"}, {0x00BE, "\\(34"}, {0x00D7, "\\(mu"},
+ {0x00F7, "\\(di"},
+
+ {0x0391, "\\(*A"}, {0x0392, "\\(*B"}, {0x0393, "\\(*G"}, {0x0394, "\\(*D"},
+ {0x0395, "\\(*E"}, {0x0396, "\\(*Z"}, {0x0397, "\\(*Y"}, {0x0398, "\\(*H"},
+ {0x0399, "\\(*I"}, {0x039A, "\\(*K"}, {0x039B, "\\(*L"}, {0x039C, "\\(*M"},
+ {0x039D, "\\(*N"}, {0x039E, "\\(*C"}, {0x039F, "\\(*O"}, {0x03A0, "\\(*P"},
+ {0x03A1, "\\(*R"}, {0x03A3, "\\(*S"}, {0x03A4, "\\(*T"}, {0x03A5, "\\(*U"},
+ {0x03A6, "\\(*F"}, {0x03A7, "\\(*X"}, {0x03A8, "\\(*Q"}, {0x03A9, "\\(*W"},
+ {0x03B1, "\\(*a"}, {0x03B2, "\\(*b"}, {0x03B3, "\\(*g"}, {0x03B4, "\\(*d"},
+ {0x03B5, "\\(*e"}, {0x03B6, "\\(*z"}, {0x03B7, "\\(*y"}, {0x03B8, "\\(*h"},
+ {0x03B9, "\\(*i"}, {0x03BA, "\\(*k"}, {0x03BB, "\\(*l"}, {0x03BC, "\\(*m"},
+ {0x03BD, "\\(*n"}, {0x03BE, "\\(*c"}, {0x03BF, "\\(*o"}, {0x03C0, "\\(*p"},
+ {0x03C1, "\\(*r"}, {0x03C2, "\\(ts"}, {0x03C3, "\\(*s"}, {0x03C4, "\\(*t"},
+ {0x03C5, "\\(*u"}, {0x03C6, "\\(*f"}, {0x03C7, "\\(*x"}, {0x03C8, "\\(*q"},
+ {0x03C9, "\\(*w"},
+
+ {0x2014, "\\(em"}, {0x2018, "`"}, {0x2019, "'"}, {0x2020, "\\(dg"},
+ {0x2021, "\\(dd"}, {0x2022, "\\(bu"}, {0x2032, "\\(fm"},
+
+ {0x2190, "\\(<-"}, {0x2191, "\\(ua"}, {0x2192, "\\(->"}, {0x2193, "\\(da"},
+
+ {0x2202, "\\(pd"}, {0x2205, "\\(es"}, {0x2207, "\\(gr"}, {0x2208, "\\(mo"},
+ {0x2212, "\\-"}, {0x2217, "\\(**"}, {0x221A, "\\(sr"}, {0x221D, "\\(pt"},
+ {0x221E, "\\(if"}, {0x2229, "\\(ca"}, {0x222A, "\\(cu"}, {0x222B, "\\(is"},
+ {0x223C, "\\(ap"}, {0x2245, "\\(~="}, {0x2260, "\\(!="}, {0x2261, "\\(=="},
+ {0x2264, "\\(<="}, {0x2265, "\\(>="}, {0x2282, "\\(sb"}, {0x2283, "\\(sp"},
+ {0x2286, "\\(ib"}, {0x2287, "\\(ip"},
+
+ {0x25A1, "\\(sq"}, {0x25CB, "\\(ci"},
+
+ {0x261C, "\\(lh"}, {0x261E, "\\(rh"},
+};
+
+static char const *troffchar(int unichar) {
+ int i, j, k;
+
+ i = -1;
+ j = lenof(man_charmap);
+ while (j-i > 1) {
+ k = (i + j) / 2;
+ if (man_charmap[k].uni == unichar)
+ return man_charmap[k].troff;
+ else if (man_charmap[k].uni > unichar)
+ j = k;
+ else
+ i = k;
+ }
+ return NULL;
+}
+
+/*
+ * Return TRUE if we can represent the whole of the given string either
+ * in the output charset or as named characters; FALSE otherwise.
+ */
+static int troff_ok(int charset, wchar_t *string) {
+ wchar_t test[2];
+ while (*string) {
+ test[0] = *string;
+ test[1] = 0;
+ if (!cvt_ok(charset, test) && !troffchar(*string))
+ return FALSE;
+ string++;
+ }
+ return TRUE;
+}
+
+static manconfig man_configure(paragraph *source) {
+ paragraph *p;
+ manconfig ret;
+
+ /*
+ * Defaults.
+ */
+ ret.th = NULL;
+ ret.headnumbers = FALSE;
+ ret.mindepth = 0;
+ ret.filename = dupstr("output.1");
+ ret.charset = CS_ASCII;
+ ret.bullet = L"\x2022\0o\0\0";
+ ret.rule = L"\x2500\0-\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 (p = source; p; p = p->next) {
+ if (p->type == para_Config) {
+ if (!ustricmp(p->keyword, L"man-identity")) {
+ wchar_t *wp, *ep;
+
+ wp = uadv(p->keyword);
+ ep = wp;
+ while (*ep)
+ ep = uadv(ep);
+ sfree(ret.th);
+ ret.th = snewn(ep - wp + 1, wchar_t);
+ memcpy(ret.th, wp, (ep - wp + 1) * sizeof(wchar_t));
+ } 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(p->origkeyword));
+ } else if (!ustricmp(p->keyword, L"man-bullet")) {
+ ret.bullet = uadv(p->keyword);
+ } else if (!ustricmp(p->keyword, L"man-rule")) {
+ ret.rule = 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, bullets, and the
+ * rule character.
+ */
+ while (*uadv(ret.rquote) && *uadv(uadv(ret.rquote)) &&
+ (!troff_ok(ret.charset, ret.lquote) ||
+ !troff_ok(ret.charset, ret.rquote))) {
+ ret.lquote = uadv(ret.rquote);
+ ret.rquote = uadv(ret.lquote);
+ }
+
+ while (*ret.bullet && *uadv(ret.bullet) &&
+ !troff_ok(ret.charset, ret.bullet))
+ ret.bullet = uadv(ret.bullet);
+
+ while (*ret.rule && *uadv(ret.rule) &&
+ !troff_ok(ret.charset, ret.rule))
+ ret.rule = uadv(ret.rule);
+
+ return ret;
+}
+
+static void man_conf_cleanup(manconfig cf)
+{
+ sfree(cf.th);
+ sfree(cf.filename);
+}
+
+paragraph *man_config_filename(char *filename)
+{
+ return cmdline_cfg_simple("man-filename", filename, NULL);
+}