Esempio n. 1
0
static void
scan_lisp_file (const char *filename, const char *mode)
{
  FILE *infile;
  int c;
  char *saved_string = 0;
  /* These are the only files that are loaded uncompiled, and must
     follow the conventions of the doc strings expected by this
     function.  These conventions are automatically followed by the
     byte compiler when it produces the .elc files.  */
  static struct {
    const char *fn;
    int fl;
  } const uncompiled[] = {
    DEF_ELISP_FILE (loaddefs.el),
    DEF_ELISP_FILE (loadup.el),
    DEF_ELISP_FILE (charprop.el),
    DEF_ELISP_FILE (cp51932.el),
    DEF_ELISP_FILE (eucjp-ms.el)
  };
  int i;
  int flen = strlen (filename);

  if (generate_globals)
    fatal ("scanning lisp file when -g specified");
  if (flen > 3 && !strcmp (filename + flen - 3, ".el"))
    {
      bool match = false;
      for (i = 0; i < sizeof (uncompiled) / sizeof (uncompiled[0]); i++)
	{
	  if (uncompiled[i].fl <= flen
	      && !strcmp (filename + flen - uncompiled[i].fl, uncompiled[i].fn)
	      && (flen == uncompiled[i].fl
		  || IS_SLASH (filename[flen - uncompiled[i].fl - 1])))
	    {
	      match = true;
	      break;
	    }
	}
      if (!match)
	fatal ("uncompiled lisp file %s is not supported", filename);
    }

  infile = fopen (filename, mode);
  if (infile == NULL)
    {
      perror (filename);
      exit (EXIT_FAILURE);
    }

  c = '\n';
  while (!feof (infile))
    {
      char buffer[BUFSIZ];
      char type;

      /* If not at end of line, skip till we get to one.  */
      if (c != '\n' && c != '\r')
	{
	  c = getc (infile);
	  continue;
	}
      /* Skip the line break.  */
      while (c == '\n' || c == '\r')
	c = getc (infile);
      /* Detect a dynamic doc string and save it for the next expression.  */
      if (c == '#')
	{
	  c = getc (infile);
	  if (c == '@')
	    {
	      ptrdiff_t length = 0;
	      ptrdiff_t i;

	      /* Read the length.  */
	      while ((c = getc (infile),
		      c >= '0' && c <= '9'))
		{
		  if (INT_MULTIPLY_WRAPV (length, 10, &length)
		      || INT_ADD_WRAPV (length, c - '0', &length)
		      || SIZE_MAX < length)
		    memory_exhausted ();
		}

	      if (length <= 1)
		fatal ("invalid dynamic doc string length");

	      if (c != ' ')
		fatal ("space not found after dynamic doc string length");

	      /* The next character is a space that is counted in the length
		 but not part of the doc string.
		 We already read it, so just ignore it.  */
	      length--;

	      /* Read in the contents.  */
	      free (saved_string);
	      saved_string = xmalloc (length);
	      for (i = 0; i < length; i++)
		saved_string[i] = getc (infile);
	      /* The last character is a ^_.
		 That is needed in the .elc file
		 but it is redundant in DOC.  So get rid of it here.  */
	      saved_string[length - 1] = 0;
	      /* Skip the line break.  */
	      while (c == '\n' || c == '\r')
		c = getc (infile);
	      /* Skip the following line.  */
	      while (c != '\n' && c != '\r')
		c = getc (infile);
	    }
	  continue;
	}

      if (c != '(')
	continue;

      read_lisp_symbol (infile, buffer);

      if (! strcmp (buffer, "defun")
	  || ! strcmp (buffer, "defmacro")
	  || ! strcmp (buffer, "defsubst"))
	{
	  type = 'F';
	  read_lisp_symbol (infile, buffer);

	  /* Skip the arguments: either "nil" or a list in parens.  */

	  c = getc (infile);
	  if (c == 'n') /* nil */
	    {
	      if ((c = getc (infile)) != 'i'
		  || (c = getc (infile)) != 'l')
		{
		  fprintf (stderr, "## unparsable arglist in %s (%s)\n",
			   buffer, filename);
		  continue;
		}
	    }
	  else if (c != '(')
	    {
	      fprintf (stderr, "## unparsable arglist in %s (%s)\n",
		       buffer, filename);
	      continue;
	    }
	  else
	    while (c != ')')
	      c = getc (infile);
	  skip_white (infile);

	  /* If the next three characters aren't `dquote bslash newline'
	     then we're not reading a docstring.
	   */
	  if ((c = getc (infile)) != '"'
	      || (c = getc (infile)) != '\\'
	      || ((c = getc (infile)) != '\n' && c != '\r'))
	    {
#ifdef DEBUG
	      fprintf (stderr, "## non-docstring in %s (%s)\n",
		       buffer, filename);
#endif
	      continue;
	    }
	}

      /* defcustom can only occur in uncompiled Lisp files.  */
      else if (! strcmp (buffer, "defvar")
	       || ! strcmp (buffer, "defconst")
	       || ! strcmp (buffer, "defcustom"))
	{
	  type = 'V';
	  read_lisp_symbol (infile, buffer);

	  if (saved_string == 0)
	    if (!search_lisp_doc_at_eol (infile))
	      continue;
	}

      else if (! strcmp (buffer, "custom-declare-variable")
	       || ! strcmp (buffer, "defvaralias")
	       )
	{
	  type = 'V';

	  c = getc (infile);
	  if (c == '\'')
	    read_lisp_symbol (infile, buffer);
	  else
	    {
	      if (c != '(')
		{
		  fprintf (stderr,
			   "## unparsable name in custom-declare-variable in %s\n",
			   filename);
		  continue;
		}
	      read_lisp_symbol (infile, buffer);
	      if (strcmp (buffer, "quote"))
		{
		  fprintf (stderr,
			   "## unparsable name in custom-declare-variable in %s\n",
			   filename);
		  continue;
		}
	      read_lisp_symbol (infile, buffer);
	      c = getc (infile);
	      if (c != ')')
		{
		  fprintf (stderr,
			   "## unparsable quoted name in custom-declare-variable in %s\n",
			   filename);
		  continue;
		}
	    }

	  if (saved_string == 0)
	    if (!search_lisp_doc_at_eol (infile))
	      continue;
	}

      else if (! strcmp (buffer, "fset") || ! strcmp (buffer, "defalias"))
	{
	  type = 'F';

	  c = getc (infile);
	  if (c == '\'')
	    read_lisp_symbol (infile, buffer);
	  else
	    {
	      if (c != '(')
		{
		  fprintf (stderr, "## unparsable name in fset in %s\n",
			   filename);
		  continue;
		}
	      read_lisp_symbol (infile, buffer);
	      if (strcmp (buffer, "quote"))
		{
		  fprintf (stderr, "## unparsable name in fset in %s\n",
			   filename);
		  continue;
		}
	      read_lisp_symbol (infile, buffer);
	      c = getc (infile);
	      if (c != ')')
		{
		  fprintf (stderr,
			   "## unparsable quoted name in fset in %s\n",
			   filename);
		  continue;
		}
	    }

	  if (saved_string == 0)
	    if (!search_lisp_doc_at_eol (infile))
	      continue;
	}

      else if (! strcmp (buffer, "autoload"))
	{
	  type = 'F';
	  c = getc (infile);
	  if (c == '\'')
	    read_lisp_symbol (infile, buffer);
	  else
	    {
	      if (c != '(')
		{
		  fprintf (stderr, "## unparsable name in autoload in %s\n",
			   filename);
		  continue;
		}
	      read_lisp_symbol (infile, buffer);
	      if (strcmp (buffer, "quote"))
		{
		  fprintf (stderr, "## unparsable name in autoload in %s\n",
			   filename);
		  continue;
		}
	      read_lisp_symbol (infile, buffer);
	      c = getc (infile);
	      if (c != ')')
		{
		  fprintf (stderr,
			   "## unparsable quoted name in autoload in %s\n",
			   filename);
		  continue;
		}
	    }
	  skip_white (infile);
	  if ((c = getc (infile)) != '\"')
	    {
	      fprintf (stderr, "## autoload of %s unparsable (%s)\n",
		       buffer, filename);
	      continue;
	    }
	  read_c_string_or_comment (infile, 0, false, 0);

	  if (saved_string == 0)
	    if (!search_lisp_doc_at_eol (infile))
	      continue;
	}

#ifdef DEBUG
      else if (! strcmp (buffer, "if")
	       || ! strcmp (buffer, "byte-code"))
	continue;
#endif

      else
	{
#ifdef DEBUG
	  fprintf (stderr, "## unrecognized top-level form, %s (%s)\n",
		   buffer, filename);
#endif
	  continue;
	}

      /* At this point, we should either use the previous dynamic doc string in
	 saved_string or gobble a doc string from the input file.
	 In the latter case, the opening quote (and leading backslash-newline)
	 have already been read.  */

      printf ("\037%c%s\n", type, buffer);
      if (saved_string)
	{
	  fputs (saved_string, stdout);
	  /* Don't use one dynamic doc string twice.  */
	  free (saved_string);
	  saved_string = 0;
	}
      else
	read_c_string_or_comment (infile, 1, false, 0);
    }
  free (saved_string);
  if (ferror (infile) || fclose (infile) != 0)
    fatal ("%s: read error", filename);
}
Esempio n. 2
0
static void
scan_c_stream (FILE *infile)
{
  int commas, minargs, maxargs;
  int c = '\n';

  while (!feof (infile))
    {
      bool doc_keyword = false;
      bool defunflag = false;
      bool defvarperbufferflag = false;
      bool defvarflag = false;
      enum global_type type = INVALID;
      static char *name;
      static ptrdiff_t name_size;

      if (c != '\n' && c != '\r')
	{
	  c = getc (infile);
	  continue;
	}
      c = getc (infile);
      if (c == ' ')
	{
	  while (c == ' ')
	    c = getc (infile);
	  if (c != 'D')
	    continue;
	  c = getc (infile);
	  if (c != 'E')
	    continue;
	  c = getc (infile);
	  if (c != 'F')
	    continue;
	  c = getc (infile);
	  if (c == 'S')
	    {
	      c = getc (infile);
	      if (c != 'Y')
		continue;
	      c = getc (infile);
	      if (c != 'M')
		continue;
	      c = getc (infile);
	      if (c != ' ' && c != '\t' && c != '(')
		continue;
	      type = SYMBOL;
	    }
	  else if (c == 'V')
	    {
	      c = getc (infile);
	      if (c != 'A')
		continue;
	      c = getc (infile);
	      if (c != 'R')
		continue;
	      c = getc (infile);
	      if (c != '_')
		continue;

	      defvarflag = true;

	      c = getc (infile);
	      defvarperbufferflag = (c == 'P');
	      if (generate_globals)
		{
		  if (c == 'I')
		    type = EMACS_INTEGER;
		  else if (c == 'L')
		    type = LISP_OBJECT;
		  else if (c == 'B')
		    type = BOOLEAN;
		}

	      c = getc (infile);
	      /* We need to distinguish between DEFVAR_BOOL and
		 DEFVAR_BUFFER_DEFAULTS.  */
	      if (generate_globals && type == BOOLEAN && c != 'O')
		type = INVALID;
	    }
	  else
	    continue;
	}
      else if (c == 'D')
	{
	  c = getc (infile);
	  if (c != 'E')
	    continue;
	  c = getc (infile);
	  if (c != 'F')
	    continue;
	  c = getc (infile);
	  defunflag = c == 'U';
	}
      else continue;

      if (generate_globals
	  && (!defvarflag || defvarperbufferflag || type == INVALID)
	  && !defunflag && type != SYMBOL)
	continue;

      while (c != '(')
	{
	  if (c < 0)
	    goto eof;
	  c = getc (infile);
	}

      if (type != SYMBOL)
	{
	  /* Lisp variable or function name.  */
	  c = getc (infile);
	  if (c != '"')
	    continue;
	  c = read_c_string_or_comment (infile, -1, false, 0);
	}

      if (generate_globals)
	{
	  ptrdiff_t i = 0;
	  char const *svalue = 0;

	  /* Skip "," and whitespace.  */
	  do
	    {
	      c = getc (infile);
	    }
	  while (c == ',' || c == ' ' || c == '\t' || c == '\n' || c == '\r');

	  /* Read in the identifier.  */
	  do
	    {
	      if (c < 0)
		goto eof;
	      input_buffer[i++] = c;
	      c = getc (infile);
	    }
	  while (! (c == ',' || c == ' ' || c == '\t'
		    || c == '\n' || c == '\r'));
	  input_buffer[i] = '\0';

	  if (name_size <= i)
	    {
	      free (name);
	      name_size = i + 1;
	      ptrdiff_t doubled;
	      if (! INT_MULTIPLY_WRAPV (name_size, 2, &doubled)
		  && doubled <= SIZE_MAX)
		name_size = doubled;
	      name = xmalloc (name_size);
	    }
	  memcpy (name, input_buffer, i + 1);

	  if (type == SYMBOL)
	    {
	      do
		c = getc (infile);
	      while (c == ' ' || c == '\t' || c == '\n' || c == '\r');
	      if (c != '"')
		continue;
	      c = read_c_string_or_comment (infile, -1, false, 0);
	      svalue = input_buffer;
	    }

	  if (!defunflag)
	    {
	      add_global (type, name, 0, svalue);
	      continue;
	    }
	}

      if (type == SYMBOL)
	continue;

      /* DEFVAR_LISP ("name", addr, "doc")
	 DEFVAR_LISP ("name", addr /\* doc *\/)
	 DEFVAR_LISP ("name", addr, doc: /\* doc *\/)  */

      if (defunflag)
	commas = generate_globals ? 4 : 5;
      else if (defvarperbufferflag)
	commas = 3;
      else if (defvarflag)
	commas = 1;
      else  /* For DEFSIMPLE and DEFPRED.  */
	commas = 2;

      while (commas)
	{
	  if (c == ',')
	    {
	      commas--;

	      if (defunflag && (commas == 1 || commas == 2))
		{
		  int scanned = 0;
		  do
		    c = getc (infile);
		  while (c == ' ' || c == '\n' || c == '\r' || c == '\t');
		  if (c < 0)
		    goto eof;
		  ungetc (c, infile);
		  if (commas == 2) /* Pick up minargs.  */
		    scanned = fscanf (infile, "%d", &minargs);
		  else /* Pick up maxargs.  */
		    if (c == 'M' || c == 'U') /* MANY || UNEVALLED */
		      {
			if (generate_globals)
			  maxargs = (c == 'M') ? -1 : -2;
			else
			  maxargs = -1;
		      }
		    else
		      scanned = fscanf (infile, "%d", &maxargs);
		  if (scanned < 0)
		    goto eof;
		}
	    }

	  if (c == EOF)
	    goto eof;
	  c = getc (infile);
	}

      if (generate_globals)
	{
	  struct global *g = add_global (FUNCTION, name, maxargs, 0);
	  if (!g)
	    continue;

	  /* The following code tries to recognize function attributes
	     specified after the docstring, e.g.:

	     DEFUN ("foo", Ffoo, Sfoo, X, Y, Z,
		   doc: /\* doc *\/
		   attributes: attribute1 attribute2 ...)
	       (Lisp_Object arg...)

	     Now only 'noreturn' and 'const' attributes are used.  */

	  /* Advance to the end of docstring.  */
	  c = getc (infile);
	  if (c == EOF)
	    goto eof;
	  int d = getc (infile);
	  if (d == EOF)
	    goto eof;
	  while (1)
	    {
	      if (c == '*' && d == '/')
		break;
	      c = d, d = getc (infile);
	      if (d == EOF)
		goto eof;
	    }
	  /* Skip spaces, if any.  */
	  do
	    {
	      c = getc (infile);
	      if (c == EOF)
		goto eof;
	    }
	  while (c == ' ' || c == '\n' || c == '\r' || c == '\t');
	  /* Check for 'attributes:' token.  */
	  if (c == 'a' && stream_match (infile, "ttributes:"))
	    {
	      char *p = input_buffer;
	      /* Collect attributes up to ')'.  */
	      while (1)
		{
		  c = getc (infile);
		  if (c == EOF)
		    goto eof;
		  if (c == ')')
		    break;
		  if (p - input_buffer > sizeof (input_buffer))
		    abort ();
		  *p++ = c;
		}
	      *p = 0;
	      if (strstr (input_buffer, "noreturn"))
		g->flags |= DEFUN_noreturn;
	      if (strstr (input_buffer, "const"))
		g->flags |= DEFUN_const;
	    }
	  continue;
	}

      while (c == ' ' || c == '\n' || c == '\r' || c == '\t')
	c = getc (infile);

      if (c == '"')
	c = read_c_string_or_comment (infile, 0, false, 0);

      while (c != EOF && c != ',' && c != '/')
	c = getc (infile);
      if (c == ',')
	{
	  c = getc (infile);
	  while (c == ' ' || c == '\n' || c == '\r' || c == '\t')
	    c = getc (infile);
	  while ((c >= 'a' && c <= 'z') || (c >= 'Z' && c <= 'Z'))
	    c = getc (infile);
	  if (c == ':')
	    {
	      doc_keyword = true;
	      c = getc (infile);
	      while (c == ' ' || c == '\n' || c == '\r' || c == '\t')
		c = getc (infile);
	    }
	}

      if (c == '"'
	  || (c == '/'
	      && (c = getc (infile),
		  ungetc (c, infile),
		  c == '*')))
	{
	  bool comment = c != '"';
	  bool saw_usage;

	  printf ("\037%c%s\n", defvarflag ? 'V' : 'F', input_buffer);

	  if (comment)
	    getc (infile); 	/* Skip past `*'.  */
	  c = read_c_string_or_comment (infile, 1, comment, &saw_usage);

	  /* If this is a defun, find the arguments and print them.  If
	     this function takes MANY or UNEVALLED args, then the C source
	     won't give the names of the arguments, so we shouldn't bother
	     trying to find them.

	     Various doc-string styles:
	      0: DEFUN (..., "DOC") (args)            [!comment]
	      1: DEFUN (..., /\* DOC *\/ (args))      [comment && !doc_keyword]
	      2: DEFUN (..., doc: /\* DOC *\/) (args) [comment && doc_keyword]
	  */
	  if (defunflag && maxargs != -1 && !saw_usage)
	    {
	      char argbuf[1024], *p = argbuf;

	      if (!comment || doc_keyword)
		while (c != ')')
		  {
		    if (c < 0)
		      goto eof;
		    c = getc (infile);
		  }

	      /* Skip into arguments.  */
	      while (c != '(')
		{
		  if (c < 0)
		    goto eof;
		  c = getc (infile);
		}
	      /* Copy arguments into ARGBUF.  */
	      *p++ = c;
	      do
		*p++ = c = getc (infile);
	      while (c != ')');
	      *p = '\0';
	      /* Output them.  */
	      fputs ("\n\n", stdout);
	      write_c_args (input_buffer, argbuf, minargs, maxargs);
	    }
	  else if (defunflag && maxargs == -1 && !saw_usage)
	    /* The DOC should provide the usage form.  */
	    fprintf (stderr, "Missing 'usage' for function '%s'.\n",
		     input_buffer);
	}
    }
 eof:
  if (ferror (infile) || fclose (infile) != 0)
    fatal ("read error");
}
Esempio n. 3
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.  */

  /* Enough to handle floating point formats with large numbers.  */
  enum { SIZE_BOUND_EXTRA = DBL_MAX_10_EXP + 50 };

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

  /* 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.  */
		  int n = *fmt - '0';
		  bool overflow = false;
		  while (fmt + 1 < format_end
			 && '0' <= fmt[1] && fmt[1] <= '9')
		    {
		      overflow |= INT_MULTIPLY_WRAPV (n, 10, &n);
		      overflow |= INT_ADD_WRAPV (n, fmt[1] - '0', &n);
		      *string++ = *++fmt;
		    }

		  if (overflow
		      || min (PTRDIFF_MAX, SIZE_MAX) - SIZE_BOUND_EXTRA < n)
		    error ("Format width or precision too large");
		  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.  */
	  size_bound += SIZE_BOUND_EXTRA;

	  /* 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';
	      FALLTHROUGH;
	    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 '%':
	      /* Treat this '%' as normal.  */
	      fmt0 = fmt - 1;
	      break;
	    }
	}

      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);
	}
    }