Cleanups to complete the man page backend. Also, an additional new
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Wed, 24 Mar 2004 19:23:21 +0000 (19:23 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Wed, 24 Mar 2004 19:23:21 +0000 (19:23 +0000)
markup feature: a \c line in a code paragraph can now be followed by
an optional \e line indicating emphasised bits of its preceding \c.
This allows discretionary bolding and (italic/underline) emphasis
within code paragraphs, but without introducing an escape character
or breaking any existing input files. Users are warned that not all
backends are required to actually render these hints, and so they
should avoid depending on them 100% to convey semantic information
unless they know they're writing for a restricted range of backends.

git-svn-id: svn://svn.tartarus.org/sgt/halibut@3965 cda61777-01e9-0310-a592-d414129be87e

bk_man.c
bk_whlp.c
bk_xhtml.c
input.c
inputs/test.but

index 02d942a..876ecb5 100644 (file)
--- a/bk_man.c
+++ b/bk_man.c
@@ -9,6 +9,51 @@
 
 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;
+} manconfig;
+
+static manconfig man_configure(paragraph *source) {
+    manconfig ret;
+
+    /*
+     * Defaults.
+     */
+    ret.th = NULL;
+    ret.headnumbers = FALSE;
+    ret.mindepth = 0;
+
+    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);
+               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));
+           }
+       }
+    }
+
+    return ret;
+}
+
+static void man_conf_cleanup(manconfig cf)
+{
+    sfree(cf.th);
+}
 
 #define QUOTE_INITCTRL 1 /* quote initial . and ' on a line */
 #define QUOTE_QUOTES   2 /* quote double quotes by doubling them */
@@ -18,10 +63,13 @@ void man_backend(paragraph *sourceform, keywordlist *keywords,
     paragraph *p;
     FILE *fp;
     char const *sep;
+    manconfig conf;
 
     IGNORE(keywords);                 /* we don't happen to need this */
     IGNORE(idx);                      /* or this */
 
+    conf = man_configure(sourceform);
+
     /*
      * Determine the output file name, and open the output file
      *
@@ -41,8 +89,23 @@ void man_backend(paragraph *sourceform, keywordlist *keywords,
            man_text(fp, p->words, TRUE, 0);
        }
 
-    /* FIXME: .TH name-of-program manual-section */
-    fprintf(fp, ".TH FIXME 1\n");
+    /* .TH name-of-program manual-section */
+    {
+       char *c;
+       if (conf.th && *conf.th) {
+           wchar_t *wp;
+           fprintf(fp, ".TH");
+
+           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");
 
@@ -61,18 +124,6 @@ void man_backend(paragraph *sourceform, keywordlist *keywords,
            sep = "\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
@@ -96,14 +147,25 @@ void man_backend(paragraph *sourceform, keywordlist *keywords,
       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.
@@ -155,8 +217,10 @@ void man_backend(paragraph *sourceform, keywordlist *keywords,
 
       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:
@@ -171,6 +235,7 @@ void man_backend(paragraph *sourceform, keywordlist *keywords,
      * Tidy up.
      */
     fclose(fp);
+    man_conf_cleanup(conf);
 }
 
 /*
@@ -188,7 +253,8 @@ void man_backend(paragraph *sourceform, keywordlist *keywords,
  * 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.
      */
@@ -197,7 +263,10 @@ static int man_convert(wchar_t *s, char **result, int quote_props) {
     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;
 
@@ -278,14 +347,15 @@ static void man_rdaddwc(rdstringc *rs, word *text, word *end,
            (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);
@@ -293,14 +363,16 @@ static void man_rdaddwc(rdstringc *rs, word *text, word *end,
        } 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");
@@ -322,7 +394,35 @@ static void man_codepara(FILE *fp, word *text) {
     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);
     }
index a21ac04..85b51eb 100644 (file)
--- a/bk_whlp.c
+++ b/bk_whlp.c
@@ -28,6 +28,8 @@ enum {
     FONT_NORMAL,
     FONT_EMPH,
     FONT_CODE,
+    FONT_ITAL_CODE,
+    FONT_BOLD_CODE,
     FONT_TITLE,
     FONT_TITLE_EMPH,
     FONT_TITLE_CODE,
@@ -35,7 +37,8 @@ enum {
 };
 
 static void whlp_rdaddwc(rdstringc *rs, word *text);
-static int whlp_convert(wchar_t *s, char **result, int hard_spaces);
+static int whlp_convert(wchar_t *s, int maxlen,
+                       char **result, int hard_spaces);
 static void whlp_mkparagraph(struct bk_whlp_state *state,
                             int font, word *text, int subsidiary);
 static void whlp_navmenu(struct bk_whlp_state *state, paragraph *p);
@@ -73,6 +76,10 @@ void whlp_backend(paragraph *sourceform, keywordlist *keywords,
                     WHLP_FONT_ITALIC, 0, 0, 0);
     whlp_create_font(h, "Courier New", WHLP_FONTFAM_FIXED, 24,
                     0, 0, 0, 0);
+    whlp_create_font(h, "Courier New", WHLP_FONTFAM_FIXED, 24,
+                    WHLP_FONT_ITALIC, 0, 0, 0);
+    whlp_create_font(h, "Courier New", WHLP_FONTFAM_FIXED, 24,
+                    WHLP_FONT_BOLD, 0, 0, 0);
     whlp_create_font(h, "Arial", WHLP_FONTFAM_SERIF, 30,
                     WHLP_FONT_BOLD, 0, 0, 0);
     whlp_create_font(h, "Arial", WHLP_FONTFAM_SERIF, 30,
@@ -91,7 +98,7 @@ void whlp_backend(paragraph *sourceform, keywordlist *keywords,
        if (p->type == para_Config && p->parent) {
            if (!ustricmp(p->keyword, L"winhelp-topic")) {
                char *topicname;
-               whlp_convert(uadv(p->keyword), &topicname, 0);
+               whlp_convert(uadv(p->keyword), 0, &topicname, 0);
                /* Store the topic name in the private_data field of the
                 * containing section. */
                p->parent->private_data = topicname;
@@ -396,14 +403,41 @@ void whlp_backend(paragraph *sourceform, keywordlist *keywords,
         */
        {
            word *w;
+           wchar_t *t, *e;
            char *c;
-           for (w = p->words; w; w = w->next) {
+
+           for (w = p->words; w; w = w->next) if (w->type == word_WeakCode) {
+               t = w->text;
+               if (w->next && w->next->type == word_Emph) {
+                   w = w->next;
+                   e = w->text;
+               } else
+                   e = NULL;
+
                if (!w->next)
                    whlp_para_attr(h, WHLP_PARA_SPACEBELOW, 12);
+
                whlp_para_attr(h, WHLP_PARA_LEFTINDENT, 72*nesting);
                whlp_begin_para(h, WHLP_PARA_SCROLL);
+               while (e && *e && *t) {
+                   int n;
+                   int ec = *e;
+
+                   for (n = 0; t[n] && e[n] && e[n] == ec; n++);
+                   if (ec == 'i')
+                       whlp_set_font(h, FONT_ITAL_CODE);
+                   else if (ec == 'b')
+                       whlp_set_font(h, FONT_BOLD_CODE);
+                   else
+                       whlp_set_font(h, FONT_CODE);
+                   whlp_convert(t, n, &c, FALSE);
+                   whlp_text(h, c);
+                   sfree(c);
+                   t += n;
+                   e += n;
+               }
                whlp_set_font(h, FONT_CODE);
-               whlp_convert(w->text, &c, FALSE);
+               whlp_convert(t, 0, &c, FALSE);
                whlp_text(h, c);
                sfree(c);
                whlp_end_para(h);
@@ -546,7 +580,7 @@ static void whlp_mkparagraph(struct bk_whlp_state *state,
            whlp_set_font(state->h, newfont);
        }
        if (removeattr(text->type) == word_Normal) {
-           if (whlp_convert(text->text, &c, TRUE))
+           if (whlp_convert(text->text, 0, &c, TRUE))
                whlp_text(state->h, c);
            else
                whlp_mkparagraph(state, deffont, text->alt, FALSE);
@@ -589,7 +623,7 @@ static void whlp_rdaddwc(rdstringc *rs, word *text) {
        assert(text->type != word_CodeQuote &&
               text->type != word_WkCodeQuote);
        if (removeattr(text->type) == word_Normal) {
-           if (whlp_convert(text->text, &c, FALSE))
+           if (whlp_convert(text->text, 0, &c, FALSE))
                rdaddsc(rs, c);
            else
                whlp_rdaddwc(rs, text->alt);
@@ -615,7 +649,8 @@ static void whlp_rdaddwc(rdstringc *rs, word *text) {
  * characters are OK but `result' is non-NULL, a result _will_
  * still be generated!
  */
-static int whlp_convert(wchar_t *s, char **result, int hard_spaces) {
+static int whlp_convert(wchar_t *s, int maxlen,
+                       char **result, int hard_spaces) {
     /*
      * FIXME. Currently this is ISO8859-1 only.
      */
@@ -624,7 +659,10 @@ static int whlp_convert(wchar_t *s, char **result, int hard_spaces) {
     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;
 
index 053bf33..3024664 100644 (file)
@@ -94,7 +94,7 @@ static void xhtml_utostr(wchar_t *, char **);
 static int xhtml_para_level(paragraph *);
 static int xhtml_reservedchar(int);
 
-static int xhtml_convert(wchar_t *, char **, int);
+static int xhtml_convert(wchar_t *, int, char **, int);
 static void xhtml_rdaddwc(rdstringc *, word *, word *);
 static void xhtml_para(FILE *, word *);
 static void xhtml_codepara(FILE *, word *);
@@ -1331,13 +1331,17 @@ static int xhtml_reservedchar(int c)
  * characters are OK but `result' is non-NULL, a result _will_
  * still be generated!
  */
-static int xhtml_convert(wchar_t *s, char **result, int hard_spaces) {
+static int xhtml_convert(wchar_t *s, int maxlen, char **result,
+                        int hard_spaces) {
     int doing = (result != 0);
     int ok = TRUE;
     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;
 
 #define ensure_size(i) if (i>=psize) { psize = i+256; p = resize(p, psize); }
@@ -1504,7 +1508,7 @@ static void xhtml_rdaddwc(rdstringc *rs, word *text, word *end) {
            rdaddsc(rs, "<code>");
 
        if (removeattr(text->type) == word_Normal) {
-         if (xhtml_convert(text->text, &c, TRUE)) /* spaces in the word are hard */
+         if (xhtml_convert(text->text, 0, &c, TRUE)) /* spaces in the word are hard */
            rdaddsc(rs, c);
          else
            xhtml_rdaddwc(rs, text->alt, NULL);
@@ -1564,7 +1568,7 @@ static void xhtml_heading(FILE *fp, paragraph *p)
        xhtml_rdaddwc(&t, nprefix, NULL);
        if (fmt) {
            char *c;
-           if (xhtml_convert(fmt->number_suffix, &c, FALSE)) {
+           if (xhtml_convert(fmt->number_suffix, 0, &c, FALSE)) {
                rdaddsc(&t, c);
                sfree(c);
            }
@@ -1573,7 +1577,7 @@ static void xhtml_heading(FILE *fp, paragraph *p)
        xhtml_rdaddwc(&t, tprefix, NULL);
        if (fmt) {
            char *c;
-           if (xhtml_convert(fmt->number_suffix, &c, FALSE)) {
+           if (xhtml_convert(fmt->number_suffix, 0, &c, FALSE)) {
                rdaddsc(&t, c);
                sfree(c);
            }
@@ -1616,10 +1620,49 @@ static void xhtml_codepara(FILE *fp, word *text)
 {
   fprintf(fp, "<pre>");
     for (; text; text = text->next) if (text->type == word_WeakCode) {
+       word *here, *next;
        char *c;
-       xhtml_convert(text->text, &c, FALSE);
-       fprintf(fp, "%s\n", c);
-       sfree(c);
+
+       /*
+        * See if this WeakCode is followed by an Emph to indicate
+        * emphasis.
+        */
+       here = text;
+       if (text->next && text->next->type == word_Emph) {
+           next = text = text->next;
+       } else
+           next = NULL;
+
+       if (next) {
+           wchar_t *t, *e;
+           int n;
+
+           t = here->text;
+           e = next->text;
+
+           while (*e) {
+               int ec = *e;
+
+               for (n = 0; t[n] && e[n] && e[n] == ec; n++);
+               xhtml_convert(t, n, &c, FALSE);
+               fprintf(fp, "%s%s%s",
+                       (ec == 'i' ? "<em>" : ec == 'b' ? "<b>" : ""),
+                       c,
+                       (ec == 'i' ? "</em>" : ec == 'b' ? "</b>" : ""));
+               sfree(c);
+
+               t += n;
+               e += n;
+           }
+
+           xhtml_convert(t, 0, &c, FALSE);
+           fprintf(fp, "%s\n", c);
+           sfree(c);
+       } else {
+           xhtml_convert(here->text, 0, &c, FALSE);
+           fprintf(fp, "%s\n", c);
+           sfree(c);
+       }
     }
   fprintf(fp, "</pre>\n");
 }
diff --git a/input.c b/input.c
index 6cad0f7..0535495 100644 (file)
--- a/input.c
+++ b/input.c
@@ -569,11 +569,13 @@ static void read_file(paragraph ***ret, input *in, indexdata *idx) {
         * Parse code paragraphs separately.
         */
        if (t.type == tok_cmd && t.cmd == c_c && !isbrace(in)) {
+           int wtype = word_WeakCode;
+
            par.type = para_Code;
            par.fpos = t.pos;
            while (1) {
                dtor(t), t = get_codepar_token(in);
-               wd.type = word_WeakCode;
+               wd.type = wtype;
                wd.breaks = FALSE;     /* shouldn't need this... */
                wd.text = ustrdup(t.text);
                wd.alt = NULL;
@@ -588,7 +590,12 @@ static void read_file(paragraph ***ret, input *in, indexdata *idx) {
                }
                if (t.type == tok_eop || t.type == tok_eof)
                    break;
-               else if (t.type != tok_cmd || t.cmd != c_c) {
+               else if (t.type == tok_cmd && t.cmd == c_c)
+                   wtype = word_WeakCode;
+               else if (t.type == tok_cmd && t.cmd == c_e &&
+                        wtype == word_WeakCode)
+                   wtype = word_Emph;
+               else {
                    error(err_brokencodepara, &t.pos);
                    prev_para_type = par.type;
                    addpara(par, ret);
index 059567e..cfefbdb 100644 (file)
@@ -3,8 +3,8 @@ See If Wrapping Titles Works OK. In Fact This Title Will Span Three
 Lines, Not Just Two. How's That For Ludicrous?
 
 \cfg{xhtml-leaf-smallest-contents}{2}
-
 \cfg{xhtml-leaf-contains-contents}{true}
+\cfg{man-headnumbers}{true}
 
 \preamble This manual is a small joke effort, designed to use every
 feature \#{ comment } that Halibut's input format supports. Creation
@@ -19,7 +19,7 @@ a bit]
 
 \define{coopt} co\u00F6{-o}pt
 
-\versionid $Id: test.but,v 1.20 2004/03/23 20:10:23 simon Exp $
+\versionid $Id: test.but,v 1.21 2004/03/24 19:23:21 simon Exp $
 
 \C{ch\\ap} First chapter title; for similar wrapping reasons this
 chapter title will be ludicrously long. I wonder how much more
@@ -57,6 +57,9 @@ Here's a code paragraph:
 \c Two blank lines precede this one.
 \c   Two leading spaces
 \c We can use \ { and } with impunity here.
+\c We can use discretionary bold and italic in code paragraphs!
+\e                          bbbb     iiiiii
+\c Isn't that ludicrous?
 
 This is a list: