aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/term_ps.c
diff options
context:
space:
mode:
authorKristaps Dzonsons <kristaps@bsd.lv>2010-06-09 08:07:13 +0000
committerKristaps Dzonsons <kristaps@bsd.lv>2010-06-09 08:07:13 +0000
commitdbcb21471c73a93f9436d4a342a5cc07d74fbf47 (patch)
treeaf1d82c5a84624d1e711a284999c60edd3fff340 /term_ps.c
parent08669f3b61bca274d2da8109ff9079c3c9d33a6c (diff)
downloadmandoc-dbcb21471c73a93f9436d4a342a5cc07d74fbf47.tar.gz
mandoc-dbcb21471c73a93f9436d4a342a5cc07d74fbf47.tar.zst
mandoc-dbcb21471c73a93f9436d4a342a5cc07d74fbf47.zip
Have the standard manpage header and footer print on every page of -Tps
output. This is more tricky than you may think: we can't just call the header function out-of-state (i.e., before a flushln has occured) because we'd clobber our current state. Thus, we call at the beginning and dump the output into an auxiliary buffer. For the record, I don't think there's any other clean way to do this. The only other Way That Works is to copy-aside *all* termp state, zero it, and do the necessary headf/footf. This is just as complex, as memory needs to be alloc'd and free'd per margin. Unfortunately, this prohibits page numbering (the margin is only printed once), so I'll probably end up re-writing this down the line.
Diffstat (limited to 'term_ps.c')
-rw-r--r--term_ps.c194
1 files changed, 139 insertions, 55 deletions
diff --git a/term_ps.c b/term_ps.c
index 1ba93bda..0d424035 100644
--- a/term_ps.c
+++ b/term_ps.c
@@ -1,4 +1,4 @@
-/* $Id: term_ps.c,v 1.3 2010/06/08 15:06:01 kristaps Exp $ */
+/* $Id: term_ps.c,v 1.4 2010/06/09 08:07:13 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
*
@@ -18,9 +18,13 @@
#include "config.h"
#endif
+#include <sys/param.h>
+
#include <assert.h>
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include "out.h"
#include "main.h"
@@ -34,12 +38,28 @@
#define PS_CHAR_BOTMARG 24
#define PS_CHAR_BOT (PS_CHAR_BOTMARG + 36)
+#define PS_BUFSLOP 128
+#define PS_GROWBUF(p, sz) \
+ do if ((p)->engine.ps.psmargcur + (sz) > \
+ (p)->engine.ps.psmargsz) { \
+ (p)->engine.ps.psmargsz += /* CONSTCOND */ \
+ MAX(PS_BUFSLOP, (sz)); \
+ (p)->engine.ps.psmarg = realloc \
+ ((p)->engine.ps.psmarg, \
+ (p)->engine.ps.psmargsz); \
+ if (NULL == (p)->engine.ps.psmarg) { \
+ perror(NULL); \
+ exit(EXIT_FAILURE); \
+ } \
+ } while (/* CONSTCOND */ 0)
+
static void ps_letter(struct termp *, char);
static void ps_begin(struct termp *);
static void ps_end(struct termp *);
-static void ps_pageopen(struct termp *);
static void ps_advance(struct termp *, size_t);
static void ps_endline(struct termp *);
+static void ps_printf(struct termp *, const char *, ...);
+static void ps_putchar(struct termp *, char);
void *
@@ -63,8 +83,68 @@ ps_alloc(void)
void
ps_free(void *arg)
{
+ struct termp *p;
+
+ p = (struct termp *)arg;
+
+ if (p->engine.ps.psmarg)
+ free(p->engine.ps.psmarg);
+
+ term_free(p);
+}
+
+
+static void
+ps_printf(struct termp *p, const char *fmt, ...)
+{
+ va_list ap;
+ int pos;
+
+ va_start(ap, fmt);
+
+ /*
+ * If we're running in regular mode, then pipe directly into
+ * vprintf(). If we're processing margins, then push the data
+ * into our growable margin buffer.
+ */
+
+ if ( ! (PS_MARGINS & p->engine.ps.psstate)) {
+ vprintf(fmt, ap);
+ va_end(ap);
+ return;
+ }
+
+ /*
+ * XXX: I assume that the in-margin print won't exceed
+ * PS_BUFSLOP (128 bytes), which is reasonable but still an
+ * assumption that will cause pukeage if it's not the case.
+ */
+
+ PS_GROWBUF(p, PS_BUFSLOP);
+
+ pos = (int)p->engine.ps.psmargcur;
+ vsnprintf(&p->engine.ps.psmarg[pos], PS_BUFSLOP, fmt, ap);
+ p->engine.ps.psmargcur = strlen(p->engine.ps.psmarg);
+}
+
+
+static void
+ps_putchar(struct termp *p, char c)
+{
+ int pos;
+
+ /* See ps_printf(). */
+
+ if ( ! (PS_MARGINS & p->engine.ps.psstate)) {
+ putchar(c);
+ return;
+ }
+
+ PS_GROWBUF(p, 2);
- term_free((struct termp *)arg);
+ pos = (int)p->engine.ps.psmargcur++;
+ p->engine.ps.psmarg[pos] = c;
+ p->engine.ps.psmarg[pos] = '\0';
}
@@ -73,6 +153,15 @@ static void
ps_end(struct termp *p)
{
+ /*
+ * At the end of the file, do one last showpage. This is the
+ * same behaviour as groff(1) and works for multiple pages as
+ * well as just one.
+ */
+
+ assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
+ printf("%s", p->engine.ps.psmarg);
+ printf("showpage\n");
printf("%s\n", "%%EOF");
}
@@ -90,9 +179,47 @@ ps_begin(struct termp *p)
printf("%s\n", "/Courier");
printf("%s\n", "10 selectfont");
- p->engine.ps.pspage = 1;
p->engine.ps.psstate = 0;
- ps_pageopen(p);
+
+ if (p->engine.ps.psmarg) {
+ assert(p->engine.ps.psmargsz);
+ p->engine.ps.psmarg[0] = '\0';
+ }
+
+ p->engine.ps.psmargcur = 0;
+
+ /*
+ * Now dump the margins into our margin buffer. If we don't do
+ * this, we'd break any current state to run the header and
+ * footer with each and evern new page.
+ */
+
+ p->engine.ps.pscol = PS_CHAR_LEFT;
+ p->engine.ps.psrow = PS_CHAR_TOPMARG;
+
+ p->engine.ps.psstate |= PS_MARGINS;
+
+ (*p->headf)(p, p->argf);
+ (*p->endline)(p);
+
+ p->engine.ps.psstate &= ~PS_MARGINS;
+ assert(0 == p->engine.ps.psstate);
+
+ p->engine.ps.pscol = PS_CHAR_LEFT;
+ p->engine.ps.psrow = PS_CHAR_BOTMARG;
+ p->engine.ps.psstate |= PS_MARGINS;
+
+ (*p->footf)(p, p->argf);
+ (*p->endline)(p);
+
+ p->engine.ps.psstate &= ~PS_MARGINS;
+ assert(0 == p->engine.ps.psstate);
+
+ p->engine.ps.pscol = PS_CHAR_LEFT;
+ p->engine.ps.psrow = PS_CHAR_TOP;
+
+ assert(p->engine.ps.psmarg);
+ assert('\0' != p->engine.ps.psmarg[0]);
}
@@ -105,10 +232,9 @@ ps_letter(struct termp *p, char c)
* If we're not in a PostScript "word" context, then
* open one now at the current cursor.
*/
- printf("%zu %zu moveto\n",
+ ps_printf(p, "%zu %zu moveto\n(",
p->engine.ps.pscol,
p->engine.ps.psrow);
- putchar('(');
p->engine.ps.psstate |= PS_INLINE;
}
@@ -125,64 +251,25 @@ ps_letter(struct termp *p, char c)
case (')'):
/* FALLTHROUGH */
case ('\\'):
- putchar('\\');
+ ps_putchar(p, '\\');
break;
default:
break;
}
/* Write the character and adjust where we are on the page. */
- putchar(c);
+ ps_putchar(p, c);
p->engine.ps.pscol += PS_CHAR_WIDTH;
}
-/*
- * Open a page. This is only used for -Tps at the moment. It opens a
- * page context, printing the header and the footer. THE OUTPUT BUFFER
- * MUST BE EMPTY. If it is not, output will ghost on the next line and
- * we'll be all gross and out of state.
- */
-static void
-ps_pageopen(struct termp *p)
-{
-
- assert(TERMTYPE_PS == p->type);
- assert(0 == p->engine.ps.psstate);
-
- p->engine.ps.pscol = PS_CHAR_LEFT;
- p->engine.ps.psrow = PS_CHAR_TOPMARG;
- p->engine.ps.psstate |= PS_MARGINS;
-
- (*p->headf)(p, p->argf);
- (*p->endline)(p);
-
- p->engine.ps.psstate &= ~PS_MARGINS;
- assert(0 == p->engine.ps.psstate);
-
- p->engine.ps.pscol = PS_CHAR_LEFT;
- p->engine.ps.psrow = PS_CHAR_BOTMARG;
- p->engine.ps.psstate |= PS_MARGINS;
-
- (*p->footf)(p, p->argf);
- (*p->endline)(p);
-
- p->engine.ps.psstate &= ~PS_MARGINS;
- assert(0 == p->engine.ps.psstate);
-
- p->engine.ps.pscol = PS_CHAR_LEFT;
- p->engine.ps.psrow = PS_CHAR_TOP;
-
-}
-
-
static void
ps_advance(struct termp *p, size_t len)
{
if (PS_INLINE & p->engine.ps.psstate) {
/* Dump out any existing line scope. */
- printf(") show\n");
+ ps_printf(p, ") show\n");
p->engine.ps.psstate &= ~PS_INLINE;
}
@@ -195,7 +282,7 @@ ps_endline(struct termp *p)
{
if (PS_INLINE & p->engine.ps.psstate) {
- printf(") show\n");
+ ps_printf(p, ") show\n");
p->engine.ps.psstate &= ~PS_INLINE;
}
@@ -208,11 +295,8 @@ ps_endline(struct termp *p)
return;
}
- /*
- * XXX: can't run pageopen() until we're certain a flushln() has
- * occured, else the buf will reopen in an awkward state on the
- * next line.
- */
+ assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
+ printf("%s", p->engine.ps.psmarg);
printf("showpage\n");
p->engine.ps.psrow = PS_CHAR_TOP;
}