static void man_text(FILE *, word *, int newline, int quote_props);
static void man_codepara(FILE *, word *);
+static int man_convert(wchar_t *s, int maxlen,
+ char **result, int quote_props);
+
+typedef struct {
+ wchar_t *th;
+ int headnumbers;
+ int mindepth;
+ char *filename;
+} manconfig;
+
+static manconfig man_configure(paragraph *source) {
+ manconfig ret;
+
+ /*
+ * Defaults.
+ */
+ ret.th = NULL;
+ ret.headnumbers = FALSE;
+ ret.mindepth = 0;
+ ret.filename = dupstr("output.1");
+
+ for (; source; source = source->next) {
+ if (source->type == para_Config) {
+ if (!ustricmp(source->keyword, L"man-identity")) {
+ wchar_t *wp, *ep;
+
+ wp = uadv(source->keyword);
+ ep = wp;
+ while (*ep)
+ ep = uadv(ep);
+ sfree(ret.th);
+ ret.th = mknewa(wchar_t, ep - wp + 1);
+ memcpy(ret.th, wp, (ep - wp + 1) * sizeof(wchar_t));
+ } 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")) {
+ sfree(ret.filename);
+ ret.filename = utoa_dup(uadv(source->keyword));
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void man_conf_cleanup(manconfig cf)
+{
+ sfree(cf.th);
+ sfree(cf.filename);
+}
+
+paragraph *man_config_filename(char *filename)
+{
+ paragraph *p;
+ wchar_t *ufilename, *up;
+ int len;
+
+ p = mknew(paragraph);
+ memset(p, 0, sizeof(*p));
+ p->type = para_Config;
+ p->next = NULL;
+ p->fpos.filename = "<command line>";
+ p->fpos.line = p->fpos.col = -1;
+
+ ufilename = ufroma_dup(filename);
+ len = ustrlen(ufilename) + 2 + lenof(L"man-filename");
+ p->keyword = mknewa(wchar_t, len);
+ up = p->keyword;
+ ustrcpy(up, L"man-filename");
+ up = uadv(up);
+ ustrcpy(up, ufilename);
+ up = uadv(up);
+ *up = L'\0';
+ assert(up - p->keyword < len);
+ sfree(ufilename);
+
+ return p;
+}
#define QUOTE_INITCTRL 1 /* quote initial . and ' on a line */
#define QUOTE_QUOTES 2 /* quote double quotes by doubling them */
void man_backend(paragraph *sourceform, keywordlist *keywords,
- indexdata *idx) {
+ indexdata *idx, void *unused) {
paragraph *p;
FILE *fp;
- char const *sep;
+ manconfig conf;
- IGNORE(keywords); /* we don't happen to need this */
- IGNORE(idx); /* or this */
+ IGNORE(unused);
+ IGNORE(keywords);
+ IGNORE(idx);
+
+ conf = man_configure(sourceform);
/*
- * Determine the output file name, and open the output file
- *
- * FIXME: want configurable output file names here. For the
- * moment, we'll just call it `output.1'.
+ * Open the output file.
*/
- fp = fopen("output.1", "w");
+ fp = fopen(conf.filename, "w");
if (!fp) {
- error(err_cantopenw, "output.1");
+ error(err_cantopenw, conf.filename);
return;
}
man_text(fp, p->words, TRUE, 0);
}
- /* FIXME: .TH name-of-program manual-section */
- fprintf(fp, ".TH FIXME 1\n");
-
- fprintf(fp, ".UC\n");
+ /* .TH name-of-program manual-section */
+ fprintf(fp, ".TH");
+ if (conf.th && *conf.th) {
+ char *c;
+ wchar_t *wp;
- /* Do the preamble and copyright */
- sep = "";
- for (p = sourceform; p; p = p->next)
- if (p->type == para_Preamble) {
- fprintf(fp, "%s", sep);
- man_text(fp, p->words, TRUE, 0);
- sep = "\n";
- }
- for (p = sourceform; p; p = p->next)
- if (p->type == para_Copyright) {
- fprintf(fp, "%s", sep);
- man_text(fp, p->words, TRUE, 0);
- sep = "\n";
+ for (wp = conf.th; *wp; wp = uadv(wp)) {
+ fputs(" \"", fp);
+ man_convert(wp, 0, &c, QUOTE_QUOTES);
+ fputs(c, fp);
+ sfree(c);
+ fputc('"', fp);
}
+ }
+ fputc('\n', fp);
+
+ fprintf(fp, ".UC\n");
- /*
- * FIXME:
- *
- * - figure out precisely what needs to be escaped.
- * * A dot or apostrophe at the start of a line wants to be
- * preceded by `\&', which is a zero-width space.
- * * Literal backslashes always want doubling.
- * * Within double quotes, a double quote needs doubling
- * too.
- *
- * - work out what to do about hyphens / minuses...
- */
for (p = sourceform; p; p = p->next) switch (p->type) {
/*
* Things we ignore because we've already processed them or
case para_BR:
case para_Biblio: /* only touch BiblioCited */
case para_VersionID:
- case para_Copyright:
- case para_Preamble:
case para_NoCite:
case para_Title:
break;
case para_UnnumberedChapter:
case para_Heading:
case para_Subsect:
- fprintf(fp, ".SH \"");
- /* FIXME: disable this, at _least_ by default */
- if (p->kwtext)
- man_text(fp, p->kwtext, FALSE, QUOTE_QUOTES);
- fprintf(fp, " ");
- man_text(fp, p->words, FALSE, QUOTE_QUOTES);
- fprintf(fp, "\"\n");
- break;
+
+ {
+ int depth;
+ if (p->type == para_Subsect)
+ depth = p->aux + 2;
+ else if (p->type == para_Heading)
+ depth = 1;
+ else
+ depth = 0;
+ if (depth >= conf.mindepth) {
+ fprintf(fp, ".SH \"");
+ if (conf.headnumbers && p->kwtext) {
+ man_text(fp, p->kwtext, FALSE, QUOTE_QUOTES);
+ fprintf(fp, " ");
+ }
+ man_text(fp, p->words, FALSE, QUOTE_QUOTES);
+ fprintf(fp, "\"\n");
+ }
+ break;
+ }
/*
* Code paragraphs.
* Normal paragraphs.
*/
case para_Normal:
+ case para_Copyright:
fprintf(fp, ".PP\n");
man_text(fp, p->words, TRUE, 0);
break;
case para_Rule:
/*
- * FIXME.
+ * This isn't terribly good. Anyone who wants to do better
+ * should feel free!
*/
+ fprintf(fp, ".PP\n----------------------------------------\n");
break;
case para_LcontPush:
+ case para_QuotePush:
fprintf(fp, ".RS\n");
break;
case para_LcontPop:
+ case para_QuotePop:
fprintf(fp, ".RE\n");
break;
}
* Tidy up.
*/
fclose(fp);
+ man_conf_cleanup(conf);
}
/*
* of things. I know I at least need to escape backslash, and full
* stops at the starts of words are probably trouble as well.
*/
-static int man_convert(wchar_t *s, char **result, int quote_props) {
+static int man_convert(wchar_t *s, int maxlen,
+ char **result, int quote_props) {
/*
* FIXME. Currently this is ISO8859-1 only.
*/
char *p = NULL;
int plen = 0, psize = 0;
- for (; *s; s++) {
+ if (maxlen <= 0)
+ maxlen = -1;
+
+ for (; *s && maxlen != 0; s++, maxlen--) {
wchar_t c = *s;
char outc;
(attraux(text->aux) == attr_First ||
attraux(text->aux) == attr_Only))
rdaddsc(rs, "\\fI");
- else if (towordstyle(text->type) == word_Code &&
+ else if ((towordstyle(text->type) == word_Code ||
+ towordstyle(text->type) == word_WeakCode) &&
(attraux(text->aux) == attr_First ||
attraux(text->aux) == attr_Only))
rdaddsc(rs, "\\fB");
if (removeattr(text->type) == word_Normal) {
if (rs->pos > 0)
quote_props &= ~QUOTE_INITCTRL; /* not at start any more */
- if (man_convert(text->text, &c, quote_props))
+ if (man_convert(text->text, 0, &c, quote_props))
rdaddsc(rs, c);
else
man_rdaddwc(rs, text->alt, NULL, quote_props);
} else if (removeattr(text->type) == word_WhiteSpace) {
rdaddc(rs, ' ');
} else if (removeattr(text->type) == word_Quote) {
- rdaddc(rs, quoteaux(text->aux) == quote_Open ? '`' : '\'');
- /* FIXME: configurability */
+ rdaddc(rs, '"');
+ if (quote_props & QUOTE_QUOTES)
+ rdaddc(rs, '"');
}
if (towordstyle(text->type) == word_Emph &&
(attraux(text->aux) == attr_Last ||
attraux(text->aux) == attr_Only))
rdaddsc(rs, "\\fP");
- else if (towordstyle(text->type) == word_Code &&
+ else if ((towordstyle(text->type) == word_Code ||
+ towordstyle(text->type) == word_WeakCode) &&
(attraux(text->aux) == attr_Last ||
attraux(text->aux) == attr_Only))
rdaddsc(rs, "\\fP");
fprintf(fp, ".nf\n");
for (; text; text = text->next) if (text->type == word_WeakCode) {
char *c;
- man_convert(text->text, &c, QUOTE_INITCTRL);
+ wchar_t *t, *e;
+ int quote_props = QUOTE_INITCTRL;
+
+ t = text->text;
+ if (text->next && text->next->type == word_Emph) {
+ e = text->next->text;
+ text = text->next;
+ } else
+ e = NULL;
+
+ while (e && *e && *t) {
+ int n;
+ int ec = *e;
+
+ for (n = 0; t[n] && e[n] && e[n] == ec; n++);
+ if (ec == 'i')
+ fprintf(fp, "\\fI");
+ else if (ec == 'b')
+ fprintf(fp, "\\fB");
+ man_convert(t, n, &c, quote_props);
+ quote_props &= ~QUOTE_INITCTRL;
+ fprintf(fp, "%s", c);
+ sfree(c);
+ if (ec == 'i' || ec == 'b')
+ fprintf(fp, "\\fP");
+ t += n;
+ e += n;
+ }
+ man_convert(t, 0, &c, quote_props);
fprintf(fp, "%s\n", c);
sfree(c);
}