aboutsummaryrefslogtreecommitdiff
path: root/libps/write.c
diff options
context:
space:
mode:
Diffstat (limited to 'libps/write.c')
-rw-r--r--libps/write.c264
1 files changed, 264 insertions, 0 deletions
diff --git a/libps/write.c b/libps/write.c
new file mode 100644
index 00000000..83be42ab
--- /dev/null
+++ b/libps/write.c
@@ -0,0 +1,264 @@
+/* Ps stream output
+
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "ps.h"
+#include "common.h"
+
+/* True if CH is a `control character'. */
+#define iscntl(ch) ((unsigned)(ch) < 32)
+
+/* *BEG and NEW - 1 are the bounds of a buffer, which write to S (the last
+ character just before NEW isn't included, because something different
+ about it is what caused the flush), and update *BEG to be NEW. True is
+ returned if a write error occurs. */
+static int
+flush (const char **beg, const char *new, FILE *s)
+{
+ const char *b = *beg;
+ if (new > b)
+ *beg = new;
+ if (new - 1 > b)
+ {
+ size_t len = new - 1 - b;
+ int ret = fwrite (b, 1, len, s);
+ if (ret < len)
+ return 1;
+ }
+ return 0;
+}
+
+/* Write T to S, up to MAX characters (unless MAX == 0), making sure not to
+ write any unprintable characters. */
+error_t
+noise_write (const unsigned char *t, ssize_t max, FILE *s)
+{
+ int ch;
+ const char *ok = t;
+ size_t len = 0;
+
+ while ((ch = *t++) && (max < 0 || len < max))
+ if (isgraph (ch) || ch == ' ')
+ len++;
+ else
+ {
+ int is_cntl = iscntl (ch);
+
+ if (flush (&ok, t, s))
+ return errno;
+
+ len += (is_cntl ? 2 : 4);
+ if (max >= 0 && len > max)
+ break;
+
+ if (is_cntl)
+ fprintf (s, "^%c", ch + 'A');
+ else
+ fprintf (s, "\\%03o", ch);
+ }
+
+ if (flush (&ok, t, s))
+ return errno;
+
+ return 0;
+}
+
+/* Return what noise_write would write with arguments of T and MAX. */
+size_t
+noise_len (const char *t, ssize_t max)
+{
+ int ch;
+ size_t len = 0;
+
+ while ((ch = *t++) && (max == 0 || len < max))
+ if (isgraph (ch) || ch == ' ')
+ len++;
+ else
+ {
+ size_t rep_len = iscntl (ch) ? 2 : 4;
+ if (max >= 0 && rep_len + len > max)
+ break;
+ len += rep_len;
+ }
+
+ return len;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Write at most MAX_LEN characters of STRING to STREAM (if MAX_LEN > the
+ length of STRING, then write all of it; if MAX_LEN == -1, then write all
+ of STRING regardless). */
+error_t
+ps_stream_write (struct ps_stream *stream, const char *string, ssize_t max_len)
+{
+ size_t len = noise_len (string, max_len);
+
+ if (len > 0)
+ {
+ error_t err;
+ ssize_t spaces_needed = stream->spaces;
+
+ stream->spaces = 0;
+ while (spaces_needed > 0)
+ {
+ static char spaces[] = " ";
+#define spaces_len (sizeof(spaces) - 1)
+ size_t chunk = spaces_needed > spaces_len ? spaces_len : spaces_needed;
+ error_t err =
+ ps_stream_write (stream, spaces + spaces_len - chunk, chunk);
+ if (err)
+ return err;
+ spaces_needed -= chunk;
+ }
+ stream->spaces = spaces_needed;
+
+ err = noise_write (string, len, stream->stream);
+ if (err)
+ return err;
+
+ stream->pos += len;
+ }
+
+ return 0;
+}
+
+/* Write NUM spaces to STREAM. NUM may be negative, in which case the same
+ number of adjacent spaces (written by other calls to ps_stream_space) are
+ consumed if possible. If an error occurs, the error code is returned,
+ otherwise 0. */
+error_t
+ps_stream_space (struct ps_stream *stream, ssize_t num)
+{
+ stream->spaces += num;
+ return 0;
+}
+
+/* Write as many spaces to STREAM as required to make a field of width SOFAR
+ be at least WIDTH characters wide (the absolute value of WIDTH is used).
+ If an error occurs, the error code is returned, otherwise 0. */
+error_t
+ps_stream_pad (struct ps_stream *stream, ssize_t sofar, ssize_t width)
+{
+ return ps_stream_space (stream, ABS (width) - sofar);
+}
+
+/* Write a newline to STREAM, resetting its position to zero. */
+error_t
+ps_stream_newline (struct ps_stream *stream)
+{
+ putc ('\n', stream->stream);
+ stream->spaces = 0;
+ stream->pos = 0;
+ return 0;
+}
+
+/* Write the string BUF to STREAM, padded on one side with spaces to be at
+ least the absolute value of WIDTH long: if WIDTH >= 0, then on the left
+ side, otherwise on the right side. If an error occurs, the error code is
+ returned, otherwise 0. */
+error_t
+_ps_stream_write_field (struct ps_stream *stream,
+ const char *buf, size_t max_width,
+ int width)
+{
+ error_t err;
+ size_t len;
+
+ while (isspace (*buf))
+ buf++;
+
+ if (stream->spaces < 0 && max_width >= 0)
+ /* Take some of our spacing deficit out of a truncatable field. */
+ max_width += stream->spaces;
+
+ len = noise_len (buf, max_width);
+
+ if (width > 0)
+ {
+ err = ps_stream_write (stream, buf, len);
+ if (!err)
+ err = ps_stream_space (stream, width - len);
+ }
+ else if (width < 0)
+ {
+ err = ps_stream_space (stream, -width - len);
+ if (!err)
+ err = ps_stream_write (stream, buf, len);
+ }
+ else
+ err = ps_stream_write (stream, buf, len);
+
+ return err;
+}
+
+/* Write the string BUF to STREAM, padded on one side with spaces to be at
+ least the absolute value of WIDTH long: if WIDTH >= 0, then on the left
+ side, otherwise on the right side. If an error occurs, the error code is
+ returned, otherwise 0. */
+error_t
+ps_stream_write_field (struct ps_stream *stream, const char *buf, int width)
+{
+ return _ps_stream_write_field (stream, buf, -1, width);
+}
+
+/* Like ps_stream_write_field, but truncates BUF to make it fit into WIDTH. */
+error_t
+ps_stream_write_trunc_field (struct ps_stream *stream,
+ const char *buf, int width)
+{
+ return _ps_stream_write_field (stream, buf, width ? ABS (width) : -1, width);
+}
+
+/* Write the decimal representation of VALUE to STREAM, padded on one side
+ with spaces to be at least the absolute value of WIDTH long: if WIDTH >=
+ 0, then on the left side, otherwise on the right side. If an error
+ occurs, the error code is returned, otherwise 0. */
+error_t
+ps_stream_write_int_field (struct ps_stream *stream, int value, int width)
+{
+ char buf[20];
+ sprintf (buf, "%d", value);
+ return ps_stream_write_field (stream, buf, width);
+}
+
+/* Create a stream outputing to DEST, and return it in STREAM, or an error. */
+error_t
+ps_stream_create (FILE *dest, struct ps_stream **stream)
+{
+ *stream = malloc (sizeof (struct ps_stream));
+ if (! *stream)
+ return ENOMEM;
+ (*stream)->stream = dest;
+ (*stream)->spaces = 0;
+ (*stream)->pos = 0;
+ return 0;
+}
+
+/* Frees STREAM. The destination file is *not* closed. */
+void
+ps_stream_free (struct ps_stream *stream)
+{
+ free (stream);
+}