Blame argp/argp-fmtstream.c

Packit 6c4009
/* Word-wrapping and line-truncating streams
Packit 6c4009
   Copyright (C) 1997-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Written by Miles Bader <miles@gnu.ai.mit.edu>.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
/* This package emulates glibc `line_wrap_stream' semantics for systems that
Packit 6c4009
   don't have that.  */
Packit 6c4009
Packit 6c4009
#ifdef HAVE_CONFIG_H
Packit 6c4009
# include <config.h>
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <stdarg.h>
Packit 6c4009
#include <ctype.h>
Packit 6c4009
Packit 6c4009
#include <argp-fmtstream.h>
Packit 6c4009
#include "argp-namefrob.h"
Packit 6c4009
Packit 6c4009
#ifndef ARGP_FMTSTREAM_USE_LINEWRAP
Packit 6c4009
Packit 6c4009
#ifndef isblank
Packit 6c4009
#define isblank(ch) ((ch)==' ' || (ch)=='\t')
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
# include <wchar.h>
Packit 6c4009
# include <libio/libioP.h>
Packit 6c4009
# define __vsnprintf(s, l, f, a) _IO_vsnprintf (s, l, f, a)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#define INIT_BUF_SIZE 200
Packit 6c4009
#define PRINTF_SIZE_GUESS 150
Packit 6c4009

Packit 6c4009
/* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines
Packit 6c4009
   written on it with LMARGIN spaces and limits them to RMARGIN columns
Packit 6c4009
   total.  If WMARGIN >= 0, words that extend past RMARGIN are wrapped by
Packit 6c4009
   replacing the whitespace before them with a newline and WMARGIN spaces.
Packit 6c4009
   Otherwise, chars beyond RMARGIN are simply dropped until a newline.
Packit 6c4009
   Returns NULL if there was an error.  */
Packit 6c4009
argp_fmtstream_t
Packit 6c4009
__argp_make_fmtstream (FILE *stream,
Packit 6c4009
		       size_t lmargin, size_t rmargin, ssize_t wmargin)
Packit 6c4009
{
Packit 6c4009
  argp_fmtstream_t fs;
Packit 6c4009
Packit 6c4009
  fs = (struct argp_fmtstream *) malloc (sizeof (struct argp_fmtstream));
Packit 6c4009
  if (fs != NULL)
Packit 6c4009
    {
Packit 6c4009
      fs->stream = stream;
Packit 6c4009
Packit 6c4009
      fs->lmargin = lmargin;
Packit 6c4009
      fs->rmargin = rmargin;
Packit 6c4009
      fs->wmargin = wmargin;
Packit 6c4009
      fs->point_col = 0;
Packit 6c4009
      fs->point_offs = 0;
Packit 6c4009
Packit 6c4009
      fs->buf = (char *) malloc (INIT_BUF_SIZE);
Packit 6c4009
      if (! fs->buf)
Packit 6c4009
	{
Packit 6c4009
	  free (fs);
Packit 6c4009
	  fs = 0;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  fs->p = fs->buf;
Packit 6c4009
	  fs->end = fs->buf + INIT_BUF_SIZE;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return fs;
Packit 6c4009
}
Packit 6c4009
#if 0
Packit 6c4009
/* Not exported.  */
Packit 6c4009
#ifdef weak_alias
Packit 6c4009
weak_alias (__argp_make_fmtstream, argp_make_fmtstream)
Packit 6c4009
#endif
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Flush FS to its stream, and free it (but don't close the stream).  */
Packit 6c4009
void
Packit 6c4009
__argp_fmtstream_free (argp_fmtstream_t fs)
Packit 6c4009
{
Packit 6c4009
  __argp_fmtstream_update (fs);
Packit 6c4009
  if (fs->p > fs->buf)
Packit 6c4009
    {
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
      __fxprintf (fs->stream, "%.*s", (int) (fs->p - fs->buf), fs->buf);
Packit 6c4009
#else
Packit 6c4009
      fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream);
Packit 6c4009
#endif
Packit 6c4009
    }
Packit 6c4009
  free (fs->buf);
Packit 6c4009
  free (fs);
Packit 6c4009
}
Packit 6c4009
#if 0
Packit 6c4009
/* Not exported.  */
Packit 6c4009
#ifdef weak_alias
Packit 6c4009
weak_alias (__argp_fmtstream_free, argp_fmtstream_free)
Packit 6c4009
#endif
Packit 6c4009
#endif
Packit 6c4009

Packit 6c4009
/* Process FS's buffer so that line wrapping is done from POINT_OFFS to the
Packit 6c4009
   end of its buffer.  This code is mostly from glibc stdio/linewrap.c.  */
Packit 6c4009
void
Packit 6c4009
__argp_fmtstream_update (argp_fmtstream_t fs)
Packit 6c4009
{
Packit 6c4009
  char *buf, *nl;
Packit 6c4009
  size_t len;
Packit 6c4009
Packit 6c4009
  /* Scan the buffer for newlines.  */
Packit 6c4009
  buf = fs->buf + fs->point_offs;
Packit 6c4009
  while (buf < fs->p)
Packit 6c4009
    {
Packit 6c4009
      size_t r;
Packit 6c4009
Packit 6c4009
      if (fs->point_col == 0 && fs->lmargin != 0)
Packit 6c4009
	{
Packit 6c4009
	  /* We are starting a new line.  Print spaces to the left margin.  */
Packit 6c4009
	  const size_t pad = fs->lmargin;
Packit 6c4009
	  if (fs->p + pad < fs->end)
Packit 6c4009
	    {
Packit 6c4009
	      /* We can fit in them in the buffer by moving the
Packit 6c4009
		 buffer text up and filling in the beginning.  */
Packit 6c4009
	      memmove (buf + pad, buf, fs->p - buf);
Packit 6c4009
	      fs->p += pad; /* Compensate for bigger buffer. */
Packit 6c4009
	      memset (buf, ' ', pad); /* Fill in the spaces.  */
Packit 6c4009
	      buf += pad; /* Don't bother searching them.  */
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      /* No buffer space for spaces.  Must flush.  */
Packit 6c4009
	      size_t i;
Packit 6c4009
	      for (i = 0; i < pad; i++)
Packit 6c4009
		{
Packit 6c4009
		  if (_IO_fwide (fs->stream, 0) > 0)
Packit 6c4009
		    putwc_unlocked (L' ', fs->stream);
Packit 6c4009
		  else
Packit 6c4009
		    putc_unlocked (' ', fs->stream);
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	  fs->point_col = pad;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      len = fs->p - buf;
Packit 6c4009
      nl = memchr (buf, '\n', len);
Packit 6c4009
Packit 6c4009
      if (fs->point_col < 0)
Packit 6c4009
	fs->point_col = 0;
Packit 6c4009
Packit 6c4009
      if (!nl)
Packit 6c4009
	{
Packit 6c4009
	  /* The buffer ends in a partial line.  */
Packit 6c4009
Packit 6c4009
	  if (fs->point_col + len < fs->rmargin)
Packit 6c4009
	    {
Packit 6c4009
	      /* The remaining buffer text is a partial line and fits
Packit 6c4009
		 within the maximum line width.  Advance point for the
Packit 6c4009
		 characters to be written and stop scanning.  */
Packit 6c4009
	      fs->point_col += len;
Packit 6c4009
	      break;
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    /* Set the end-of-line pointer for the code below to
Packit 6c4009
	       the end of the buffer.  */
Packit 6c4009
	    nl = fs->p;
Packit 6c4009
	}
Packit 6c4009
      else if (fs->point_col + (nl - buf) < (ssize_t) fs->rmargin)
Packit 6c4009
	{
Packit 6c4009
	  /* The buffer contains a full line that fits within the maximum
Packit 6c4009
	     line width.  Reset point and scan the next line.  */
Packit 6c4009
	  fs->point_col = 0;
Packit 6c4009
	  buf = nl + 1;
Packit 6c4009
	  continue;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* This line is too long.  */
Packit 6c4009
      r = fs->rmargin - 1;
Packit 6c4009
Packit 6c4009
      if (fs->wmargin < 0)
Packit 6c4009
	{
Packit 6c4009
	  /* Truncate the line by overwriting the excess with the
Packit 6c4009
	     newline and anything after it in the buffer.  */
Packit 6c4009
	  if (nl < fs->p)
Packit 6c4009
	    {
Packit 6c4009
	      memmove (buf + (r - fs->point_col), nl, fs->p - nl);
Packit 6c4009
	      fs->p -= buf + (r - fs->point_col) - nl;
Packit 6c4009
	      /* Reset point for the next line and start scanning it.  */
Packit 6c4009
	      fs->point_col = 0;
Packit 6c4009
	      buf += r + 1; /* Skip full line plus \n. */
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      /* The buffer ends with a partial line that is beyond the
Packit 6c4009
		 maximum line width.  Advance point for the characters
Packit 6c4009
		 written, and discard those past the max from the buffer.  */
Packit 6c4009
	      fs->point_col += len;
Packit 6c4009
	      fs->p -= fs->point_col - r;
Packit 6c4009
	      break;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  /* Do word wrap.  Go to the column just past the maximum line
Packit 6c4009
	     width and scan back for the beginning of the word there.
Packit 6c4009
	     Then insert a line break.  */
Packit 6c4009
Packit 6c4009
	  char *p, *nextline;
Packit 6c4009
	  int i;
Packit 6c4009
Packit 6c4009
	  p = buf + (r + 1 - fs->point_col);
Packit 6c4009
	  while (p >= buf && !isblank (*p))
Packit 6c4009
	    --p;
Packit 6c4009
	  nextline = p + 1;	/* This will begin the next line.  */
Packit 6c4009
Packit 6c4009
	  if (nextline > buf)
Packit 6c4009
	    {
Packit 6c4009
	      /* Swallow separating blanks.  */
Packit 6c4009
	      if (p >= buf)
Packit 6c4009
		do
Packit 6c4009
		  --p;
Packit 6c4009
		while (p >= buf && isblank (*p));
Packit 6c4009
	      nl = p + 1;	/* The newline will replace the first blank. */
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      /* A single word that is greater than the maximum line width.
Packit 6c4009
		 Oh well.  Put it on an overlong line by itself.  */
Packit 6c4009
	      p = buf + (r + 1 - fs->point_col);
Packit 6c4009
	      /* Find the end of the long word.  */
Packit 6c4009
	      do
Packit 6c4009
		++p;
Packit 6c4009
	      while (p < nl && !isblank (*p));
Packit 6c4009
	      if (p == nl)
Packit 6c4009
		{
Packit 6c4009
		  /* It already ends a line.  No fussing required.  */
Packit 6c4009
		  fs->point_col = 0;
Packit 6c4009
		  buf = nl + 1;
Packit 6c4009
		  continue;
Packit 6c4009
		}
Packit 6c4009
	      /* We will move the newline to replace the first blank.  */
Packit 6c4009
	      nl = p;
Packit 6c4009
	      /* Swallow separating blanks.  */
Packit 6c4009
	      do
Packit 6c4009
		++p;
Packit 6c4009
	      while (isblank (*p));
Packit 6c4009
	      /* The next line will start here.  */
Packit 6c4009
	      nextline = p;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Note: There are a bunch of tests below for
Packit 6c4009
	     NEXTLINE == BUF + LEN + 1; this case is where NL happens to fall
Packit 6c4009
	     at the end of the buffer, and NEXTLINE is in fact empty (and so
Packit 6c4009
	     we need not be careful to maintain its contents).  */
Packit 6c4009
Packit 6c4009
	  if ((nextline == buf + len + 1
Packit 6c4009
	       ? fs->end - nl < fs->wmargin + 1
Packit 6c4009
	       : nextline - (nl + 1) < fs->wmargin)
Packit 6c4009
	      && fs->p > nextline)
Packit 6c4009
	    {
Packit 6c4009
	      /* The margin needs more blanks than we removed.  */
Packit 6c4009
	      if (fs->end - fs->p > fs->wmargin + 1)
Packit 6c4009
		/* Make some space for them.  */
Packit 6c4009
		{
Packit 6c4009
		  size_t mv = fs->p - nextline;
Packit 6c4009
		  memmove (nl + 1 + fs->wmargin, nextline, mv);
Packit 6c4009
		  nextline = nl + 1 + fs->wmargin;
Packit 6c4009
		  len = nextline + mv - buf;
Packit 6c4009
		  *nl++ = '\n';
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		/* Output the first line so we can use the space.  */
Packit 6c4009
		{
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
		  __fxprintf (fs->stream, "%.*s\n",
Packit 6c4009
			      (int) (nl - fs->buf), fs->buf);
Packit 6c4009
#else
Packit 6c4009
		  if (nl > fs->buf)
Packit 6c4009
		    fwrite_unlocked (fs->buf, 1, nl - fs->buf, fs->stream);
Packit 6c4009
		  putc_unlocked ('\n', fs->stream);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
		  len += buf - fs->buf;
Packit 6c4009
		  nl = buf = fs->buf;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    /* We can fit the newline and blanks in before
Packit 6c4009
	       the next word.  */
Packit 6c4009
	    *nl++ = '\n';
Packit 6c4009
Packit 6c4009
	  if (nextline - nl >= fs->wmargin
Packit 6c4009
	      || (nextline == buf + len + 1 && fs->end - nextline >= fs->wmargin))
Packit 6c4009
	    /* Add blanks up to the wrap margin column.  */
Packit 6c4009
	    for (i = 0; i < fs->wmargin; ++i)
Packit 6c4009
	      *nl++ = ' ';
Packit 6c4009
	  else
Packit 6c4009
	    for (i = 0; i < fs->wmargin; ++i)
Packit 6c4009
	      if (_IO_fwide (fs->stream, 0) > 0)
Packit 6c4009
		putwc_unlocked (L' ', fs->stream);
Packit 6c4009
	      else
Packit 6c4009
		putc_unlocked (' ', fs->stream);
Packit 6c4009
Packit 6c4009
	  /* Copy the tail of the original buffer into the current buffer
Packit 6c4009
	     position.  */
Packit 6c4009
	  if (nl < nextline)
Packit 6c4009
	    memmove (nl, nextline, buf + len - nextline);
Packit 6c4009
	  len -= nextline - buf;
Packit 6c4009
Packit 6c4009
	  /* Continue the scan on the remaining lines in the buffer.  */
Packit 6c4009
	  buf = nl;
Packit 6c4009
Packit 6c4009
	  /* Restore bufp to include all the remaining text.  */
Packit 6c4009
	  fs->p = nl + len;
Packit 6c4009
Packit 6c4009
	  /* Reset the counter of what has been output this line.  If wmargin
Packit 6c4009
	     is 0, we want to avoid the lmargin getting added, so we set
Packit 6c4009
	     point_col to a magic value of -1 in that case.  */
Packit 6c4009
	  fs->point_col = fs->wmargin ? fs->wmargin : -1;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Remember that we've scanned as far as the end of the buffer.  */
Packit 6c4009
  fs->point_offs = fs->p - fs->buf;
Packit 6c4009
}
Packit 6c4009

Packit 6c4009
/* Ensure that FS has space for AMOUNT more bytes in its buffer, either by
Packit 6c4009
   growing the buffer, or by flushing it.  True is returned iff we succeed. */
Packit 6c4009
int
Packit 6c4009
__argp_fmtstream_ensure (struct argp_fmtstream *fs, size_t amount)
Packit 6c4009
{
Packit 6c4009
  if ((size_t) (fs->end - fs->p) < amount)
Packit 6c4009
    {
Packit 6c4009
      ssize_t wrote;
Packit 6c4009
Packit 6c4009
      /* Flush FS's buffer.  */
Packit 6c4009
      __argp_fmtstream_update (fs);
Packit 6c4009
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
      __fxprintf (fs->stream, "%.*s", (int) (fs->p - fs->buf), fs->buf);
Packit 6c4009
      wrote = fs->p - fs->buf;
Packit 6c4009
#else
Packit 6c4009
      wrote = fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream);
Packit 6c4009
#endif
Packit 6c4009
      if (wrote == fs->p - fs->buf)
Packit 6c4009
	{
Packit 6c4009
	  fs->p = fs->buf;
Packit 6c4009
	  fs->point_offs = 0;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  fs->p -= wrote;
Packit 6c4009
	  fs->point_offs -= wrote;
Packit 6c4009
	  memmove (fs->buf, fs->buf + wrote, fs->p - fs->buf);
Packit 6c4009
	  return 0;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if ((size_t) (fs->end - fs->buf) < amount)
Packit 6c4009
	/* Gotta grow the buffer.  */
Packit 6c4009
	{
Packit 6c4009
	  size_t old_size = fs->end - fs->buf;
Packit 6c4009
	  size_t new_size = old_size + amount;
Packit 6c4009
	  char *new_buf;
Packit 6c4009
Packit 6c4009
	  if (new_size < old_size || ! (new_buf = realloc (fs->buf, new_size)))
Packit 6c4009
	    {
Packit 6c4009
	      __set_errno (ENOMEM);
Packit 6c4009
	      return 0;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  fs->buf = new_buf;
Packit 6c4009
	  fs->end = new_buf + new_size;
Packit 6c4009
	  fs->p = fs->buf;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return 1;
Packit 6c4009
}
Packit 6c4009

Packit 6c4009
ssize_t
Packit 6c4009
__argp_fmtstream_printf (struct argp_fmtstream *fs, const char *fmt, ...)
Packit 6c4009
{
Packit 6c4009
  int out;
Packit 6c4009
  size_t avail;
Packit 6c4009
  size_t size_guess = PRINTF_SIZE_GUESS; /* How much space to reserve. */
Packit 6c4009
Packit 6c4009
  do
Packit 6c4009
    {
Packit 6c4009
      va_list args;
Packit 6c4009
Packit 6c4009
      if (! __argp_fmtstream_ensure (fs, size_guess))
Packit 6c4009
	return -1;
Packit 6c4009
Packit 6c4009
      va_start (args, fmt);
Packit 6c4009
      avail = fs->end - fs->p;
Packit 6c4009
      out = __vsnprintf (fs->p, avail, fmt, args);
Packit 6c4009
      va_end (args);
Packit 6c4009
      if ((size_t) out >= avail)
Packit 6c4009
	size_guess = out + 1;
Packit 6c4009
    }
Packit 6c4009
  while ((size_t) out >= avail);
Packit 6c4009
Packit 6c4009
  fs->p += out;
Packit 6c4009
Packit 6c4009
  return out;
Packit 6c4009
}
Packit 6c4009
#if 0
Packit 6c4009
/* Not exported.  */
Packit 6c4009
#ifdef weak_alias
Packit 6c4009
weak_alias (__argp_fmtstream_printf, argp_fmtstream_printf)
Packit 6c4009
#endif
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#endif /* !ARGP_FMTSTREAM_USE_LINEWRAP */