diff options
Diffstat (limited to 'libps/write.c')
-rw-r--r-- | libps/write.c | 264 |
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); +} |