-/* $Id: mdocterm.c,v 1.16 2009/02/27 08:20:15 kristaps Exp $ */
+/* $Id: mdocterm.c,v 1.24 2009/03/02 12:09:32 kristaps Exp $ */
/*
* Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
*
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+#include <sys/utsname.h>
+
#include <assert.h>
#include <ctype.h>
#include <err.h>
#define TERMSYM_LARROW "<-"
#define TERMSYM_RARROW "->"
#define TERMSYM_UARROW "^"
+#define TERMSYM_DARROW "v"
#define TERMSYM_LSQUOTE "`"
#define TERMSYM_RSQUOTE "\'"
#define TERMSYM_SQUOTE "\'"
#define TERMSYM_GRAVE "`"
#define TERMSYM_PI "pi"
#define TERMSYM_PLUSMINUS "+="
-#define TERMSYM_INFINITY "infinity"
+#define TERMSYM_INF "oo"
+#define TERMSYM_INF2 "infinity"
#define TERMSYM_NAN "NaN"
#define TERMSYM_BAR "|"
#define TERMSYM_BULLET "o"
static void pword(struct termp *, const char *, size_t);
static void pescape(struct termp *,
const char *, size_t *, size_t);
+static void pgraph(struct termp *, char);
static void nescape(struct termp *,
const char *, size_t);
static void chara(struct termp *, char);
if (NULL == (mdoc = mmain_mdoc(p)))
mmain_exit(p, 1);
- termp.maxrmargin = 80; /* XXX */
+ termp.maxrmargin = 78; /* XXX */
termp.rmargin = termp.maxrmargin;
termp.maxcols = 1024;
termp.offset = termp.col = 0;
for (i = 0; i < p->col; i++) {
/*
* Count up visible word characters. Control sequences
- * (starting with the CSI) aren't counted.
+ * (starting with the CSI) aren't counted. A space
+ * generates a non-printing word, which is valid (the
+ * space is printed according to regular spacing rules).
*/
- assert( ! xisspace(p->buf[i]));
/* LINTED */
for (j = i, vsz = 0; j < p->col; j++) {
} else
vsz++;
}
- assert(vsz > 0);
/*
+ * If we're breaking normally...
+ *
* If a word is too long and we're within a line, put it
* on the next line. Puke if we're being asked to write
* something that will exceed the right margin (i.e.,
- * from a fresh line or when we're not allowed to break
- * the line with TERMP_NOBREAK).
+ * from a fresh line).
+ *
+ * If we're not breaking...
+ *
+ * Don't let the visible size exceed the full right
+ * margin.
*/
- /* FIXME: allow selective right-margin breaking. */
-
- if (vis && vis + vsz > maxvis) {
- if (p->flags & TERMP_NOBREAK)
+ if ( ! (TERMP_NOBREAK & p->flags)) {
+ if (vis && vis + vsz > maxvis) {
+ putchar('\n');
+ for (j = 0; j < p->offset; j++)
+ putchar(' ');
+ vis = 0;
+ } else if (vis + vsz > maxvis)
errx(1, "word breaks right margin");
+ } else if (vis + vsz > p->maxrmargin - p->offset) {
putchar('\n');
- for (j = 0; j < p->offset; j++)
+ for (j = 0; j < p->rmargin; j++)
putchar(' ');
- vis = 0;
- } else if (vis + vsz > maxvis)
- errx(1, "word breaks right margin");
+ vis = p->rmargin;
+ }
/*
* Write out the word and a trailing space. Omit the
}
}
+ if ((TERMP_NOBREAK & p->flags) && vis >= maxvis) {
+ putchar('\n');
+ for (i = 0; i < p->rmargin; i++)
+ putchar(' ');
+ p->col = 0;
+ return;
+ }
+
/*
* If we're not to right-marginalise it (newline), then instead
* pad to the right margin and stay off.
*/
if (p->flags & TERMP_NOBREAK) {
- if ( ! (p->flags & TERMP_NORPAD))
- for ( ; vis < maxvis; vis++)
- putchar(' ');
+ for ( ; vis < maxvis; vis++)
+ putchar(' ');
} else
putchar('\n');
stringa(p, TERMSYM_RBRACK);
else if ('l' == word[0] && 'B' == word[1])
stringa(p, TERMSYM_LBRACK);
+ else if ('l' == word[0] && 'q' == word[1])
+ stringa(p, TERMSYM_LDQUOTE);
+ else if ('r' == word[0] && 'q' == word[1])
+ stringa(p, TERMSYM_RDQUOTE);
+ else if ('o' == word[0] && 'q' == word[1])
+ stringa(p, TERMSYM_LSQUOTE);
+ else if ('a' == word[0] && 'q' == word[1])
+ stringa(p, TERMSYM_RSQUOTE);
else if ('<' == word[0] && '-' == word[1])
stringa(p, TERMSYM_LARROW);
else if ('-' == word[0] && '>' == word[1])
stringa(p, TERMSYM_RARROW);
- else if ('l' == word[0] && 'q' == word[1])
- stringa(p, TERMSYM_DQUOTE);
- else if ('r' == word[0] && 'q' == word[1])
- stringa(p, TERMSYM_DQUOTE);
else if ('b' == word[0] && 'u' == word[1])
stringa(p, TERMSYM_BULLET);
- else if ('L' == word[0] && 'e' == word[1])
- stringa(p, TERMSYM_LE);
else if ('<' == word[0] && '=' == word[1])
stringa(p, TERMSYM_LE);
- else if ('G' == word[0] && 'e' == word[1])
- stringa(p, TERMSYM_GE);
else if ('>' == word[0] && '=' == word[1])
stringa(p, TERMSYM_GE);
- else if ('R' == word[0] && 'q' == word[1])
- stringa(p, TERMSYM_RDQUOTE);
- else if ('L' == word[0] && 'q' == word[1])
- stringa(p, TERMSYM_LDQUOTE);
+ else if ('=' == word[0] && '=' == word[1])
+ stringa(p, TERMSYM_EQ);
+ else if ('+' == word[0] && '-' == word[1])
+ stringa(p, TERMSYM_PLUSMINUS);
else if ('u' == word[0] && 'a' == word[1])
stringa(p, TERMSYM_UARROW);
+ else if ('d' == word[0] && 'a' == word[1])
+ stringa(p, TERMSYM_DARROW);
else if ('a' == word[0] && 'a' == word[1])
stringa(p, TERMSYM_ACUTE);
else if ('g' == word[0] && 'a' == word[1])
stringa(p, TERMSYM_GRAVE);
- else if ('P' == word[0] && 'i' == word[1])
- stringa(p, TERMSYM_PI);
- else if ('N' == word[0] && 'e' == word[1])
+ else if ('!' == word[0] && '=' == word[1])
stringa(p, TERMSYM_NEQ);
- else if ('L' == word[0] && 't' == word[1])
- stringa(p, TERMSYM_LT);
+ else if ('i' == word[0] && 'f' == word[1])
+ stringa(p, TERMSYM_INF);
+ else if ('n' == word[0] && 'a' == word[1])
+ stringa(p, TERMSYM_NAN);
+ else if ('b' == word[0] && 'a' == word[1])
+ stringa(p, TERMSYM_BAR);
+
+ /* Deprecated forms. */
+ else if ('B' == word[0] && 'a' == word[1])
+ stringa(p, TERMSYM_BAR);
+ else if ('I' == word[0] && 'f' == word[1])
+ stringa(p, TERMSYM_INF2);
+ else if ('G' == word[0] && 'e' == word[1])
+ stringa(p, TERMSYM_GE);
else if ('G' == word[0] && 't' == word[1])
stringa(p, TERMSYM_GT);
- else if ('P' == word[0] && 'm' == word[1])
- stringa(p, TERMSYM_PLUSMINUS);
- else if ('I' == word[0] && 'f' == word[1])
- stringa(p, TERMSYM_INFINITY);
+ else if ('L' == word[0] && 'e' == word[1])
+ stringa(p, TERMSYM_LE);
+ else if ('L' == word[0] && 'q' == word[1])
+ stringa(p, TERMSYM_LDQUOTE);
+ else if ('L' == word[0] && 't' == word[1])
+ stringa(p, TERMSYM_LT);
else if ('N' == word[0] && 'a' == word[1])
stringa(p, TERMSYM_NAN);
- else if ('B' == word[0] && 'a' == word[1])
- stringa(p, TERMSYM_BAR);
+ else if ('N' == word[0] && 'e' == word[1])
+ stringa(p, TERMSYM_NEQ);
+ else if ('P' == word[0] && 'i' == word[1])
+ stringa(p, TERMSYM_PI);
+ else if ('P' == word[0] && 'm' == word[1])
+ stringa(p, TERMSYM_PLUSMINUS);
+ else if ('R' == word[0] && 'q' == word[1])
+ stringa(p, TERMSYM_RDQUOTE);
break;
default:
break;
}
+static void
+pgraph(struct termp *p, char byte)
+{
+ int i;
+
+ switch (byte) {
+ case (' '):
+ chara(p, ' ');
+ break;
+ case ('\t'):
+ for (i = 0; i < INDENT; i++)
+ chara(p, ' ');
+ break;
+ default:
+ warnx("unknown non-graphing character");
+ break;
+ }
+}
+
+
static void
pescape(struct termp *p, const char *word, size_t *i, size_t len)
{
/* FALLTHROUGH */
case ('.'):
chara(p, word[*i]);
+ break;
+ case ('e'):
+ chara(p, '\\');
+ break;
default:
break;
}
{
size_t i;
- /*assert(len > 0);*/ /* Can be, if literal. */
-
/*
* Handle pwords, partial words, which may be either a single
* word or a phrase that cannot be broken down (such as a
pescape(p, word, &i, len);
continue;
}
+ if ( ! isgraph((int)word[i])) {
+ pgraph(p, word[i]);
+ continue;
+ }
chara(p, word[i]);
}
if ( ! xisspace(word[i])) {
j++;
continue;
+ }
+
+ /* Escaped spaces don't delimit... */
+ if (i > 0 && xisspace(word[i]) && '\\' == word[i - 1]) {
+ j++;
+ continue;
}
+
if (0 == j)
continue;
assert(i >= j);
static void
header(struct termp *p, const struct mdoc_meta *meta)
{
- char *buf, *title, *bufp;
+ char *buf, *title, *bufp, *vbuf;
const char *pp;
+ struct utsname uts;
+
+ p->rmargin = p->maxrmargin;
+ p->offset = 0;
if (NULL == (buf = malloc(p->rmargin)))
err(1, "malloc");
if (NULL == (title = malloc(p->rmargin)))
err(1, "malloc");
+ if (NULL == (vbuf = malloc(p->rmargin)))
+ err(1, "malloc");
- if (NULL == (pp = mdoc_vol2a(meta->vol)))
+ if (NULL == (pp = mdoc_vol2a(meta->vol))) {
switch (meta->msec) {
case (MSEC_1):
/* FALLTHROUGH */
pp = mdoc_vol2a(VOL_KM);
break;
default:
- /* FIXME: capitalise. */
- if (NULL == (pp = mdoc_msec2a(meta->msec)))
- pp = mdoc_msec2a(MSEC_local);
break;
}
+ }
+ vbuf[0] = 0;
+
+ if (pp) {
+ if (-1 == uname(&uts))
+ err(1, "uname");
+ (void)strlcat(vbuf, uts.sysname, p->rmargin);
+ (void)strlcat(vbuf, " ", p->rmargin);
+ } else if (NULL == (pp = mdoc_msec2a(meta->msec)))
+ pp = mdoc_msec2a(MSEC_local);
+
+ (void)strlcat(vbuf, pp, p->rmargin);
/*
* The header is strange. It has three components, which are
if (mdoc_arch2a(meta->arch))
(void)snprintf(buf, p->rmargin, "%s (%s)",
- pp, mdoc_arch2a(meta->arch));
+ vbuf, mdoc_arch2a(meta->arch));
else
- (void)strlcpy(buf, pp, p->rmargin);
+ (void)strlcpy(buf, vbuf, p->rmargin);
pp = mdoc_msec2a(meta->msec);
p->flags &= ~TERMP_NOSPACE;
free(title);
+ free(vbuf);
free(buf);
}