doprnt.c 9.52 KB
Newer Older
Joseph Arceneaux's avatar
Joseph Arceneaux committed
1
/* Output like sprintf to a buffer of specified size.
2 3
   Also takes args differently: pass one pointer to the end
   of the format string in addition to the format string itself.
4
   Copyright (C) 1985, 2001-2011  Free Software Foundation, Inc.
Joseph Arceneaux's avatar
Joseph Arceneaux committed
5 6 7

This file is part of GNU Emacs.

8
GNU Emacs is free software: you can redistribute it and/or modify
Joseph Arceneaux's avatar
Joseph Arceneaux committed
9
it under the terms of the GNU General Public License as published by
10 11
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Joseph Arceneaux's avatar
Joseph Arceneaux committed
12 13 14 15 16 17 18

GNU Emacs 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
19
along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
Joseph Arceneaux's avatar
Joseph Arceneaux committed
20

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
/* If you think about replacing this with some similar standard C function of
   the printf family (such as vsnprintf), please note that this function
   supports the following Emacs-specific features:

   . For %c conversions, it produces a string with the multibyte representation
     of the (`int') argument, suitable for display in an Emacs buffer.

   . For %s and %c, when field width is specified (e.g., %25s), it accounts for
     the diplay width of each character, according to char-width-table.  That
     is, it does not assume that each character takes one column on display.

   . If the size of the buffer is not enough to produce the formatted string in
     its entirety, it makes sure that truncation does not chop the last
     character in the middle of its multibyte sequence, producing an invalid
     sequence.

   . It accepts a pointer to the end of the format string, so the format string
     could include embedded null characters.

   . It signals an error if the length of the formatted string is about to
     overflow MOST_POSITIVE_FIXNUM, to avoid producing strings longer than what
     Emacs can handle.

   OTOH, this function supports only a small subset of the standard C formatted
   output facilities.  E.g., %u and %ll are not supported, and precision is
   largely ignored except for converting floating-point values.  However, this
   is okay, as this function is supposed to be called from `error' and similar
   functions, and thus does not need to support features beyond those in
   `Fformat', which is used by `error' on the Lisp level.  */
Joseph Arceneaux's avatar
Joseph Arceneaux committed
50

Richard M. Stallman's avatar
Richard M. Stallman committed
51
#include <config.h>
Joseph Arceneaux's avatar
Joseph Arceneaux committed
52 53
#include <stdio.h>
#include <ctype.h>
54
#include <setjmp.h>
Joseph Arceneaux's avatar
Joseph Arceneaux committed
55

56
#ifdef STDC_HEADERS
57 58 59
#include <float.h>
#endif

Dave Love's avatar
Dave Love committed
60 61
#include <unistd.h>

62 63 64 65 66
#include <limits.h>
#ifndef SIZE_MAX
# define SIZE_MAX ((size_t) -1)
#endif

67 68
#include "lisp.h"

Karl Heuer's avatar
Karl Heuer committed
69 70 71
/* Since we use the macro CHAR_HEAD_P, we have to include this, but
   don't have to include others because CHAR_HEAD_P does not contains
   another macro.  */
72
#include "character.h"
Karl Heuer's avatar
Karl Heuer committed
73

74 75 76 77
#ifndef DBL_MAX_10_EXP
#define DBL_MAX_10_EXP 308 /* IEEE double */
#endif

78 79 80 81
/* Generate output from a format-spec FORMAT,
   terminated at position FORMAT_END.
   Output goes in BUFFER, which has room for BUFSIZE chars.
   If the output does not fit, truncate it to fit.
Richard M. Stallman's avatar
Richard M. Stallman committed
82
   Returns the number of bytes stored into BUFFER.
83
   ARGS points to the vector of arguments, and NARGS says how many.
Richard M. Stallman's avatar
Richard M. Stallman committed
84 85 86
   A double counts as two arguments.
   String arguments are passed as C strings.
   Integers are passed as C integers.  */
87

88 89
size_t
doprnt (char *buffer, register size_t bufsize, const char *format,
90
	const char *format_end, va_list ap)
Joseph Arceneaux's avatar
Joseph Arceneaux committed
91
{
92
  const char *fmt = format;	/* Pointer into format string */
Joseph Arceneaux's avatar
Joseph Arceneaux committed
93
  register char *bufptr = buffer; /* Pointer into output buffer.. */
94

95
  /* Use this for sprintf unless we need something really big.  */
96
  char tembuf[DBL_MAX_10_EXP + 100];
97

98
  /* Size of sprintf_buffer.  */
99
  size_t size_allocated = sizeof (tembuf);
100

101 102
  /* Buffer to use for sprintf.  Either tembuf or same as BIG_BUFFER.  */
  char *sprintf_buffer = tembuf;
103

104
  /* Buffer we have got with malloc.  */
105
  char *big_buffer = NULL;
106

107
  register size_t tem;
108
  char *string;
109 110
  char fixed_buffer[20];	/* Default buffer for small formatting. */
  char *fmtcpy;
Joseph Arceneaux's avatar
Joseph Arceneaux committed
111
  int minlen;
112
  char charbuf[MAX_MULTIBYTE_LENGTH + 1];	/* Used for %c.  */
Joseph Arceneaux's avatar
Joseph Arceneaux committed
113 114 115 116

  if (format_end == 0)
    format_end = format + strlen (format);

117 118 119
  if ((format_end - format + 1) < sizeof (fixed_buffer))
    fmtcpy = fixed_buffer;
  else
120
    fmtcpy = (char *) alloca (format_end - format + 1);
121

Joseph Arceneaux's avatar
Joseph Arceneaux committed
122
  bufsize--;
123 124 125

  /* Loop until end of format string or buffer full. */
  while (fmt != format_end && bufsize > 0)
Joseph Arceneaux's avatar
Joseph Arceneaux committed
126 127 128
    {
      if (*fmt == '%')	/* Check for a '%' character */
	{
129 130 131
	  size_t size_bound = 0;
	  EMACS_INT width;  /* Columns occupied by STRING on display.  */
	  int long_flag = 0;
132

Joseph Arceneaux's avatar
Joseph Arceneaux committed
133
	  fmt++;
Jim Blandy's avatar
Jim Blandy committed
134
	  /* Copy this one %-spec into fmtcpy.  */
135
	  string = fmtcpy;
Joseph Arceneaux's avatar
Joseph Arceneaux committed
136
	  *string++ = '%';
137
	  while (1)
Joseph Arceneaux's avatar
Joseph Arceneaux committed
138 139
	    {
	      *string++ = *fmt;
140 141 142 143 144 145
	      if ('0' <= *fmt && *fmt <= '9')
		{
		  /* Get an idea of how much space we might need.
		     This might be a field width or a precision; e.g.
		     %1.1000f and %1000.1f both might need 1000+ bytes.
		     Parse the width or precision, checking for overflow.  */
146
		  size_t n = *fmt - '0';
147 148
		  while ('0' <= fmt[1] && fmt[1] <= '9')
		    {
149 150
		      if (n >= SIZE_MAX / 10
			  || n * 10 > SIZE_MAX - (fmt[1] - '0'))
151
			error ("Format width or precision too large");
Andreas Schwab's avatar
Andreas Schwab committed
152
		      n = n * 10 + fmt[1] - '0';
153 154 155 156 157 158
		      *string++ = *++fmt;
		    }

		  if (size_bound < n)
		    size_bound = n;
		}
159
	      else if (*fmt == '-' || *fmt == ' ' || *fmt == '.' || *fmt == '+')
160
		;
161 162 163 164 165 166 167
	      else if (*fmt == 'l')
		{
		  long_flag = 1;
		  if (!strchr ("dox", fmt[1]))
		    /* %l as conversion specifier, not as modifier.  */
		    break;
		}
168
	      else
Joseph Arceneaux's avatar
Joseph Arceneaux committed
169 170 171 172
		break;
	      fmt++;
	    }
	  *string = 0;
173

174 175
	  /* Make the size bound large enough to handle floating point formats
	     with large numbers.  */
176
	  if (size_bound > SIZE_MAX - DBL_MAX_10_EXP - 50)
177
	    error ("Format width or precision too large");
178
	  size_bound += DBL_MAX_10_EXP + 50;
179

180 181 182 183 184 185 186 187 188 189
	  /* Make sure we have that much.  */
	  if (size_bound > size_allocated)
	    {
	      if (big_buffer)
		big_buffer = (char *) xrealloc (big_buffer, size_bound);
	      else
		big_buffer = (char *) xmalloc (size_bound);
	      sprintf_buffer = big_buffer;
	      size_allocated = size_bound;
	    }
Joseph Arceneaux's avatar
Joseph Arceneaux committed
190 191 192 193 194 195 196
	  minlen = 0;
	  switch (*fmt++)
	    {
	    default:
	      error ("Invalid format operation %%%c", fmt[-1]);

/*	    case 'b': */
197
	    case 'l':
Joseph Arceneaux's avatar
Joseph Arceneaux committed
198
	    case 'd':
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
	      {
		int i;
		long l;

		if (long_flag)
		  {
		    l = va_arg(ap, long);
		    sprintf (sprintf_buffer, fmtcpy, l);
		  }
		else
		  {
		    i = va_arg(ap, int);
		    sprintf (sprintf_buffer, fmtcpy, i);
		  }
		/* Now copy into final output, truncating as necessary.  */
		string = sprintf_buffer;
		goto doit;
	      }

Joseph Arceneaux's avatar
Joseph Arceneaux committed
218 219
	    case 'o':
	    case 'x':
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
	      {
		unsigned u;
		unsigned long ul;

		if (long_flag)
		  {
		    ul = va_arg(ap, unsigned long);
		    sprintf (sprintf_buffer, fmtcpy, ul);
		  }
		else
		  {
		    u = va_arg(ap, unsigned);
		    sprintf (sprintf_buffer, fmtcpy, u);
		  }
		/* Now copy into final output, truncating as necessary.  */
		string = sprintf_buffer;
		goto doit;
	      }
Joseph Arceneaux's avatar
Joseph Arceneaux committed
238

239 240 241 242
	    case 'f':
	    case 'e':
	    case 'g':
	      {
243 244
		double d = va_arg(ap, double);
		sprintf (sprintf_buffer, fmtcpy, d);
245
		/* Now copy into final output, truncating as necessary.  */
246
		string = sprintf_buffer;
247 248 249
		goto doit;
	      }

Joseph Arceneaux's avatar
Joseph Arceneaux committed
250 251 252 253 254
	    case 'S':
	      string[-1] = 's';
	    case 's':
	      if (fmtcpy[1] != 's')
		minlen = atoi (&fmtcpy[1]);
255
	      string = va_arg (ap, char *);
256
	      tem = strlen (string);
257 258
	      if (tem > MOST_POSITIVE_FIXNUM)
		error ("String for %%s or %%S format is too long");
Karl Heuer's avatar
Karl Heuer committed
259
	      width = strwidth (string, tem);
Richard M. Stallman's avatar
Richard M. Stallman committed
260 261
	      goto doit1;

Joseph Arceneaux's avatar
Joseph Arceneaux committed
262 263
	      /* Copy string into final output, truncating if no room.  */
	    doit:
Karl Heuer's avatar
Karl Heuer committed
264
	      /* Coming here means STRING contains ASCII only.  */
265 266 267 268
	      tem = strlen (string);
	      if (tem > MOST_POSITIVE_FIXNUM)
		error ("Format width or precision too large");
	      width = tem;
269
	    doit1:
Karl Heuer's avatar
Karl Heuer committed
270 271 272 273
	      /* We have already calculated:
		 TEM -- length of STRING,
		 WIDTH -- columns occupied by STRING when displayed, and
		 MINLEN -- minimum columns of the output.  */
Joseph Arceneaux's avatar
Joseph Arceneaux committed
274 275
	      if (minlen > 0)
		{
Karl Heuer's avatar
Karl Heuer committed
276
		  while (minlen > width && bufsize > 0)
Joseph Arceneaux's avatar
Joseph Arceneaux committed
277 278 279 280 281 282 283 284
		    {
		      *bufptr++ = ' ';
		      bufsize--;
		      minlen--;
		    }
		  minlen = 0;
		}
	      if (tem > bufsize)
Karl Heuer's avatar
Karl Heuer committed
285 286 287
		{
		  /* Truncate the string at character boundary.  */
		  tem = bufsize;
288
		  while (!CHAR_HEAD_P (string[tem - 1])) tem--;
289
		  memcpy (bufptr, string, tem);
Karl Heuer's avatar
Karl Heuer committed
290 291 292 293
		  /* We must calculate WIDTH again.  */
		  width = strwidth (bufptr, tem);
		}
	      else
294
		memcpy (bufptr, string, tem);
Joseph Arceneaux's avatar
Joseph Arceneaux committed
295 296 297 298
	      bufptr += tem;
	      bufsize -= tem;
	      if (minlen < 0)
		{
Karl Heuer's avatar
Karl Heuer committed
299
		  while (minlen < - width && bufsize > 0)
Joseph Arceneaux's avatar
Joseph Arceneaux committed
300 301 302 303 304 305 306 307 308 309
		    {
		      *bufptr++ = ' ';
		      bufsize--;
		      minlen++;
		    }
		  minlen = 0;
		}
	      continue;

	    case 'c':
310
	      {
311 312
		int chr = va_arg(ap, int);
		tem = CHAR_STRING (chr, (unsigned char *) charbuf);
313 314 315 316 317 318 319
		string = charbuf;
		string[tem] = 0;
		width = strwidth (string, tem);
		if (fmtcpy[1] != 'c')
		  minlen = atoi (&fmtcpy[1]);
		goto doit1;
	      }
Joseph Arceneaux's avatar
Joseph Arceneaux committed
320 321 322 323 324

	    case '%':
	      fmt--;    /* Drop thru and this % will be treated as normal */
	    }
	}
Karl Heuer's avatar
Karl Heuer committed
325 326 327 328 329 330 331

      {
	/* Just some character; Copy it if the whole multi-byte form
	   fit in the buffer.  */
	char *save_bufptr = bufptr;

	do { *bufptr++ = *fmt++; }
332 333
	while (--bufsize > 0 && !CHAR_HEAD_P (*fmt));
	if (!CHAR_HEAD_P (*fmt))
Karl Heuer's avatar
Karl Heuer committed
334 335 336 337 338
	  {
	    bufptr = save_bufptr;
	    break;
	  }
      }
Joseph Arceneaux's avatar
Joseph Arceneaux committed
339 340
    };

341
  /* If we had to malloc something, free it.  */
342
  xfree (big_buffer);
343

344
  *bufptr = 0;		/* Make sure our string ends with a '\0' */
Joseph Arceneaux's avatar
Joseph Arceneaux committed
345 346
  return bufptr - buffer;
}