Beispiel #1
0
int
doprnt (char *buffer, register int bufsize, const char *format,
        const char *format_end, va_list ap)
{
    const char *fmt = format;	/* Pointer into format string */
    register char *bufptr = buffer; /* Pointer into output buffer.. */

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

    /* Size of sprintf_buffer.  */
    unsigned size_allocated = sizeof (tembuf);

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

    /* Buffer we have got with malloc.  */
    char *big_buffer = 0;

    register int tem;
    unsigned char *string;
    char fixed_buffer[20];	/* Default buffer for small formatting. */
    char *fmtcpy;
    int minlen;
    unsigned char charbuf[MAX_MULTIBYTE_LENGTH + 1];	/* Used for %c.  */

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

    if ((format_end - format + 1) < sizeof (fixed_buffer))
        fmtcpy = fixed_buffer;
    else
        fmtcpy = (char *) alloca (format_end - format + 1);

    bufsize--;

    /* Loop until end of format string or buffer full. */
    while (fmt != format_end && bufsize > 0)
    {
        if (*fmt == '%')	/* Check for a '%' character */
        {
            unsigned size_bound = 0;
            int width;		/* Columns occupied by STRING.  */

            fmt++;
            /* Copy this one %-spec into fmtcpy.  */
            string = (unsigned char *) fmtcpy;
            *string++ = '%';
            while (1)
            {
                *string++ = *fmt;
                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.  */
                    unsigned n = *fmt - '0';
                    while ('0' <= fmt[1] && fmt[1] <= '9')
                    {
                        if (n * 10 + fmt[1] - '0' < n)
                            error ("Format width or precision too large");
                        n = n * 10 + fmt[1] - '0';
                        *string++ = *++fmt;
                    }

                    if (size_bound < n)
                        size_bound = n;
                }
                else if (*fmt == '-' || *fmt == ' ' || *fmt == '.' || *fmt == '+')
                    ;
                else
                    break;
                fmt++;
            }
            *string = 0;

            /* Make the size bound large enough to handle floating point formats
               with large numbers.  */
            if (size_bound + DBL_MAX_10_EXP + 50 < size_bound)
                error ("Format width or precision too large");
            size_bound += DBL_MAX_10_EXP + 50;

            /* 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;
            }
            minlen = 0;
            switch (*fmt++)
            {
            default:
                error ("Invalid format operation %%%c", fmt[-1]);

            /*	    case 'b': */
            case 'd':
            case 'o':
            case 'x':
                if (sizeof (int) == sizeof (EMACS_INT))
                    ;
                else if (sizeof (long) == sizeof (EMACS_INT))
                    /* Insert an `l' the right place.  */
                    string[1] = string[0],
                                string[0] = string[-1],
                                            string[-1] = 'l',
                                                         string++;
                else
                    abort ();
                sprintf (sprintf_buffer, fmtcpy, va_arg(ap, char *));
                /* Now copy into final output, truncating as nec.  */
                string = (unsigned char *) sprintf_buffer;
                goto doit;

            case 'f':
            case 'e':
            case 'g':
            {
                double d = va_arg(ap, double);
                sprintf (sprintf_buffer, fmtcpy, d);
                /* Now copy into final output, truncating as nec.  */
                string = (unsigned char *) sprintf_buffer;
                goto doit;
            }

            case 'S':
                string[-1] = 's';
            case 's':
                if (fmtcpy[1] != 's')
                    minlen = atoi (&fmtcpy[1]);
                string = va_arg(ap, unsigned char *);
                tem = strlen (string);
                width = strwidth (string, tem);
                goto doit1;

                /* Copy string into final output, truncating if no room.  */
doit:
                /* Coming here means STRING contains ASCII only.  */
                width = tem = strlen (string);
doit1:
                /* We have already calculated:
                TEM -- length of STRING,
                 WIDTH -- columns occupied by STRING when displayed, and
                 MINLEN -- minimum columns of the output.  */
                if (minlen > 0)
                {
                    while (minlen > width && bufsize > 0)
                    {
                        *bufptr++ = ' ';
                        bufsize--;
                        minlen--;
                    }
                    minlen = 0;
                }
                if (tem > bufsize)
                {
                    /* Truncate the string at character boundary.  */
                    tem = bufsize;
                    while (!CHAR_HEAD_P (string[tem - 1])) tem--;
                    memcpy (bufptr, string, tem);
                    /* We must calculate WIDTH again.  */
                    width = strwidth (bufptr, tem);
                }
                else
                    memcpy (bufptr, string, tem);
                bufptr += tem;
                bufsize -= tem;
                if (minlen < 0)
                {
                    while (minlen < - width && bufsize > 0)
                    {
                        *bufptr++ = ' ';
                        bufsize--;
                        minlen++;
                    }
                    minlen = 0;
                }
                continue;

            case 'c':
            {
                /* Sometimes for %c we pass a char, which would widen
                   to int.  Sometimes we pass XFASTINT() or XINT()
                   values, which would be EMACS_INT.  Let's hope that
                   both are passed the same way, otherwise we'll need
                   to rewrite callers.  */
                EMACS_INT chr = va_arg(ap, EMACS_INT);
                tem = CHAR_STRING ((int) chr, charbuf);
                string = charbuf;
                string[tem] = 0;
                width = strwidth (string, tem);
                if (fmtcpy[1] != 'c')
                    minlen = atoi (&fmtcpy[1]);
                goto doit1;
            }

            case '%':
                fmt--;    /* Drop thru and this % will be treated as normal */
            }
        }

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

            do {
                *bufptr++ = *fmt++;
            }
            while (--bufsize > 0 && !CHAR_HEAD_P (*fmt));
            if (!CHAR_HEAD_P (*fmt))
            {
                bufptr = save_bufptr;
                break;
            }
        }
    };
Beispiel #2
0
ptrdiff_t
doprnt (char *buffer, ptrdiff_t bufsize, const char *format,
	const char *format_end, va_list ap)
{
  const char *fmt = format;	/* Pointer into format string.  */
  char *bufptr = buffer;	/* Pointer into output buffer.  */

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

  /* Size of sprintf_buffer.  */
  ptrdiff_t size_allocated = sizeof (tembuf);

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

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

  enum text_quoting_style quoting_style = text_quoting_style ();
  ptrdiff_t tem = -1;
  char *string;
  char fixed_buffer[20];	/* Default buffer for small formatting. */
  char *fmtcpy;
  int minlen;
  char charbuf[MAX_MULTIBYTE_LENGTH + 1];	/* Used for %c.  */
  USE_SAFE_ALLOCA;

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

  fmtcpy = (format_end - format < sizeof (fixed_buffer) - 1
	    ? fixed_buffer
	    : SAFE_ALLOCA (format_end - format + 1));

  bufsize--;

  /* Loop until end of format string or buffer full. */
  while (fmt < format_end && bufsize > 0)
    {
      char const *fmt0 = fmt;
      char fmtchar = *fmt++;
      if (fmtchar == '%')
	{
	  ptrdiff_t size_bound = 0;
	  ptrdiff_t width;  /* Columns occupied by STRING on display.  */
	  enum {
	    pDlen = sizeof pD - 1,
	    pIlen = sizeof pI - 1,
	    pMlen = sizeof pMd - 2
	  };
	  enum {
	    no_modifier, long_modifier, pD_modifier, pI_modifier, pM_modifier
	  } length_modifier = no_modifier;
	  static char const modifier_len[] = { 0, 1, pDlen, pIlen, pMlen };
	  int maxmlen = max (max (1, pDlen), max (pIlen, pMlen));
	  int mlen;

	  /* Copy this one %-spec into fmtcpy.  */
	  string = fmtcpy;
	  *string++ = '%';
	  while (fmt < format_end)
	    {
	      *string++ = *fmt;
	      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.  */
		  ptrdiff_t n = *fmt - '0';
		  while (fmt + 1 < format_end
			 && '0' <= fmt[1] && fmt[1] <= '9')
		    {
		      /* Avoid ptrdiff_t, size_t, and int overflow, as
			 many sprintfs mishandle widths greater than INT_MAX.
			 This test is simple but slightly conservative: e.g.,
			 (INT_MAX - INT_MAX % 10) is reported as an overflow
			 even when it's not.  */
		      if (n >= min (INT_MAX, min (PTRDIFF_MAX, SIZE_MAX)) / 10)
			error ("Format width or precision too large");
		      n = n * 10 + fmt[1] - '0';
		      *string++ = *++fmt;
		    }

		  if (size_bound < n)
		    size_bound = n;
		}
	      else if (! (*fmt == '-' || *fmt == ' ' || *fmt == '.'
			  || *fmt == '+'))
		break;
	      fmt++;
	    }

	  /* Check for the length modifiers in textual length order, so
	     that longer modifiers override shorter ones.  */
	  for (mlen = 1; mlen <= maxmlen; mlen++)
	    {
	      if (format_end - fmt < mlen)
		break;
	      if (mlen == 1 && *fmt == 'l')
		length_modifier = long_modifier;
	      if (mlen == pDlen && memcmp (fmt, pD, pDlen) == 0)
		length_modifier = pD_modifier;
	      if (mlen == pIlen && memcmp (fmt, pI, pIlen) == 0)
		length_modifier = pI_modifier;
	      if (mlen == pMlen && memcmp (fmt, pMd, pMlen) == 0)
		length_modifier = pM_modifier;
	    }

	  mlen = modifier_len[length_modifier];
	  memcpy (string, fmt + 1, mlen);
	  string += mlen;
	  fmt += mlen;
	  *string = 0;

	  /* Make the size bound large enough to handle floating point formats
	     with large numbers.  */
	  if (size_bound > min (PTRDIFF_MAX, SIZE_MAX) - DBL_MAX_10_EXP - 50)
	    error ("Format width or precision too large");
	  size_bound += DBL_MAX_10_EXP + 50;

	  /* Make sure we have that much.  */
	  if (size_bound > size_allocated)
	    {
	      if (big_buffer)
		xfree (big_buffer);
	      big_buffer = xmalloc (size_bound);
	      sprintf_buffer = big_buffer;
	      size_allocated = size_bound;
	    }
	  minlen = 0;
	  switch (*fmt++)
	    {
	    default:
	      error ("Invalid format operation %s", fmtcpy);

/*	    case 'b': */
	    case 'l':
	    case 'd':
	      switch (length_modifier)
		{
		case no_modifier:
		  {
		    int v = va_arg (ap, int);
		    tem = sprintf (sprintf_buffer, fmtcpy, v);
		  }
		  break;
		case long_modifier:
		  {
		    long v = va_arg (ap, long);
		    tem = sprintf (sprintf_buffer, fmtcpy, v);
		  }
		  break;
		case pD_modifier:
		signed_pD_modifier:
		  {
		    ptrdiff_t v = va_arg (ap, ptrdiff_t);
		    tem = sprintf (sprintf_buffer, fmtcpy, v);
		  }
		  break;
		case pI_modifier:
		  {
		    EMACS_INT v = va_arg (ap, EMACS_INT);
		    tem = sprintf (sprintf_buffer, fmtcpy, v);
		  }
		  break;
		case pM_modifier:
		  {
		    intmax_t v = va_arg (ap, intmax_t);
		    tem = sprintf (sprintf_buffer, fmtcpy, v);
		  }
		  break;
		}
	      /* Now copy into final output, truncating as necessary.  */
	      string = sprintf_buffer;
	      goto doit;

	    case 'o':
	    case 'x':
	      switch (length_modifier)
		{
		case no_modifier:
		  {
		    unsigned v = va_arg (ap, unsigned);
		    tem = sprintf (sprintf_buffer, fmtcpy, v);
		  }
		  break;
		case long_modifier:
		  {
		    unsigned long v = va_arg (ap, unsigned long);
		    tem = sprintf (sprintf_buffer, fmtcpy, v);
		  }
		  break;
		case pD_modifier:
		  goto signed_pD_modifier;
		case pI_modifier:
		  {
		    EMACS_UINT v = va_arg (ap, EMACS_UINT);
		    tem = sprintf (sprintf_buffer, fmtcpy, v);
		  }
		  break;
		case pM_modifier:
		  {
		    uintmax_t v = va_arg (ap, uintmax_t);
		    tem = sprintf (sprintf_buffer, fmtcpy, v);
		  }
		  break;
		}
	      /* Now copy into final output, truncating as necessary.  */
	      string = sprintf_buffer;
	      goto doit;

	    case 'f':
	    case 'e':
	    case 'g':
	      {
		double d = va_arg (ap, double);
		tem = sprintf (sprintf_buffer, fmtcpy, d);
		/* Now copy into final output, truncating as necessary.  */
		string = sprintf_buffer;
		goto doit;
	      }

	    case 'S':
	      string[-1] = 's';
	    case 's':
	      if (fmtcpy[1] != 's')
		minlen = atoi (&fmtcpy[1]);
	      string = va_arg (ap, char *);
	      tem = strlen (string);
	      if (STRING_BYTES_BOUND < tem)
		error ("String for %%s or %%S format is too long");
	      width = strwidth (string, tem);
	      goto doit1;

	      /* Copy string into final output, truncating if no room.  */
	    doit:
	      eassert (0 <= tem);
	      /* Coming here means STRING contains ASCII only.  */
	      if (STRING_BYTES_BOUND < tem)
		error ("Format width or precision too large");
	      width = tem;
	    doit1:
	      /* We have already calculated:
		 TEM -- length of STRING,
		 WIDTH -- columns occupied by STRING when displayed, and
		 MINLEN -- minimum columns of the output.  */
	      if (minlen > 0)
		{
		  while (minlen > width && bufsize > 0)
		    {
		      *bufptr++ = ' ';
		      bufsize--;
		      minlen--;
		    }
		  minlen = 0;
		}
	      if (tem > bufsize)
		{
		  /* Truncate the string at character boundary.  */
		  tem = bufsize;
		  do
		    {
		      tem--;
		      if (CHAR_HEAD_P (string[tem]))
			{
			  if (BYTES_BY_CHAR_HEAD (string[tem]) <= bufsize - tem)
			    tem = bufsize;
			  break;
			}
		    }
		  while (tem != 0);

		  memcpy (bufptr, string, tem);
		  bufptr[tem] = 0;
		  /* Trigger exit from the loop, but make sure we
		     return to the caller a value which will indicate
		     that the buffer was too small.  */
		  bufptr += bufsize;
		  bufsize = 0;
		  continue;
		}
	      memcpy (bufptr, string, tem);
	      bufptr += tem;
	      bufsize -= tem;
	      if (minlen < 0)
		{
		  while (minlen < - width && bufsize > 0)
		    {
		      *bufptr++ = ' ';
		      bufsize--;
		      minlen++;
		    }
		  minlen = 0;
		}
	      continue;

	    case 'c':
	      {
		int chr = va_arg (ap, int);
		tem = CHAR_STRING (chr, (unsigned char *) charbuf);
		string = charbuf;
		string[tem] = 0;
		width = strwidth (string, tem);
		if (fmtcpy[1] != 'c')
		  minlen = atoi (&fmtcpy[1]);
		goto doit1;
	      }

	    case '%':
	      fmt--;    /* Drop thru and this % will be treated as normal */
	    }
	}

      char const *src;
      ptrdiff_t srclen;
      if (quoting_style == CURVE_QUOTING_STYLE && fmtchar == '`')
	src = uLSQM, srclen = sizeof uLSQM - 1;
      else if (quoting_style == CURVE_QUOTING_STYLE && fmtchar == '\'')
	src = uRSQM, srclen = sizeof uRSQM - 1;
      else if (quoting_style == STRAIGHT_QUOTING_STYLE && fmtchar == '`')
	src = "'", srclen = 1;
      else
	{
	  while (fmt < format_end && !CHAR_HEAD_P (*fmt))
	    fmt++;
	  src = fmt0, srclen = fmt - fmt0;
	}

      if (bufsize < srclen)
	{
	  /* Truncate, but return value that will signal to caller
	     that the buffer was too small.  */
	  do
	    *bufptr++ = '\0';
	  while (--bufsize != 0);
	}
      else
	{
	  do
	    *bufptr++ = *src++;
	  while (--srclen != 0);
	}
    }