diff src/editfns.c @ 50499:0abd4951c0fb

(Fformat): Translate positions of text properties in the format string to apply them to the result.
author Richard M. Stallman <rms@gnu.org>
date Wed, 09 Apr 2003 01:28:44 +0000
parents 4d74ea083bd2
children 37d227b879ad
line wrap: on
line diff
--- a/src/editfns.c	Wed Apr 09 00:32:23 2003 +0000
+++ b/src/editfns.c	Wed Apr 09 01:28:44 2003 +0000
@@ -3201,7 +3201,7 @@
   register int n;		/* The number of the next arg to substitute */
   register int total;		/* An estimate of the final length */
   char *buf, *p;
-  register unsigned char *format, *end;
+  register unsigned char *format, *end, *format_start;
   int nchars;
   /* Nonzero if the output should be a multibyte string,
      which is true if any of the inputs is one.  */
@@ -3220,9 +3220,20 @@
   int *precision = (int *) (alloca(nargs * sizeof (int)));
   int longest_format;
   Lisp_Object val;
+  int arg_intervals = 0;
+
+  /* discarded[I] is 1 if byte I of the format
+     string was not copied into the output.
+     It is 2 if byte I was not the first byte of its character.  */
+  char *discarded;
+
+  /* Each element records, for one argument,
+     the start and end bytepos in the output string,
+     and whether the argument is a string with intervals.
+     info[0] is unused.  Unused elements have -1 for start.  */
   struct info
   {
-    int start, end;
+    int start, end, intervals;
   } *info = 0;
 
   /* It should not be necessary to GCPRO ARGS, because
@@ -3232,12 +3243,13 @@
      This is not always right; sometimes the result needs to be multibyte
      because of an object that we will pass through prin1,
      and in that case, we won't know it here.  */
-  for (n = 0; n < nargs; n++) {
-    if (STRINGP (args[n]) && STRING_MULTIBYTE (args[n]))
-      multibyte = 1;
-    /* Piggyback on this loop to initialize precision[N]. */
-    precision[n] = -1;
-  }
+  for (n = 0; n < nargs; n++)
+    {
+      if (STRINGP (args[n]) && STRING_MULTIBYTE (args[n]))
+	multibyte = 1;
+      /* Piggyback on this loop to initialize precision[N]. */
+      precision[n] = -1;
+    }
 
   CHECK_STRING (args[0]);
   /* We may have to change "%S" to "%s". */
@@ -3248,12 +3260,25 @@
  retry:
 
   format = SDATA (args[0]);
+  format_start = format;
   end = format + SBYTES (args[0]);
   longest_format = 0;
 
   /* Make room in result for all the non-%-codes in the control string.  */
   total = 5 + CONVERTED_BYTE_SIZE (multibyte, args[0]);
 
+  /* Allocate the info and discarded tables.  */ 
+  {
+    int nbytes = nargs * sizeof *info;
+    int i;
+    info = (struct info *) alloca (nbytes);
+    bzero (info, nbytes);
+    for (i = 0; i <= nargs; i++)
+      info[i].start = -1;
+    discarded = (char *) alloca (SBYTES (args[0]));
+    bzero (discarded, SBYTES (args[0]));
+  }
+
   /* Add to TOTAL enough space to hold the converted arguments.  */
 
   n = 0;
@@ -3458,6 +3483,7 @@
 	  int negative = 0;
 	  unsigned char *this_format_start = format;
 
+	  discarded[format - format_start] = 1;
 	  format++;
 
 	  /* Process a numeric arg and skip it.  */
@@ -3471,7 +3497,10 @@
              fixed. */
 	  while ((*format >= '0' && *format <= '9')
 		 || *format == '-' || *format == ' ' || *format == '.')
-	    format++;
+	    {
+	      discarded[format - format_start] = 1;
+	      format++;
+	    }
 
 	  if (*format++ == '%')
 	    {
@@ -3482,6 +3511,9 @@
 
 	  ++n;
 
+	  discarded[format - format_start - 1] = 1;
+	  info[n].start = nchars;
+
 	  if (STRINGP (args[n]))
 	    {
 	      /* handle case (precision[n] >= 0) */
@@ -3541,17 +3573,7 @@
 	      /* If this argument has text properties, record where
 		 in the result string it appears.  */
 	      if (STRING_INTERVALS (args[n]))
-		{
-		  if (!info)
-		    {
-		      int nbytes = nargs * sizeof *info;
-		      info = (struct info *) alloca (nbytes);
-		      bzero (info, nbytes);
-		    }
-
-		  info[n].start = start;
-		  info[n].end = end;
-		}
+		info[n].intervals = arg_intervals = 1;
 	    }
 	  else if (INTEGERP (args[n]) || FLOATP (args[n]))
 	    {
@@ -3578,6 +3600,8 @@
 		p += this_nchars;
 	      nchars += this_nchars;
 	    }
+
+	  info[n].end = nchars;
 	}
       else if (STRING_MULTIBYTE (args[0]))
 	{
@@ -3588,7 +3612,11 @@
 	      && !CHAR_HEAD_P (*format))
 	    maybe_combine_byte = 1;
 	  *p++ = *format++;
-	  while (! CHAR_HEAD_P (*format)) *p++ = *format++;
+	  while (! CHAR_HEAD_P (*format))
+	    {
+	      discarded[format - format_start] = 2;
+	      *p++ = *format++;
+	    }
 	  nchars++;
 	}
       else if (multibyte)
@@ -3619,7 +3647,7 @@
      arguments has text properties, set up text properties of the
      result string.  */
 
-  if (STRING_INTERVALS (args[0]) || info)
+  if (STRING_INTERVALS (args[0]) || arg_intervals)
     {
       Lisp_Object len, new_len, props;
       struct gcpro gcpro1;
@@ -3631,15 +3659,75 @@
 
       if (CONSP (props))
 	{
-	  new_len = make_number (SCHARS (val));
-	  extend_property_ranges (props, len, new_len);
+	  int bytepos = 0, position = 0, translated = 0, argn = 1;
+	  Lisp_Object list;
+
+	  /* Adjust the bounds of each text property
+	     to the proper start and end in the output string.  */
+	  /* We take advantage of the fact that the positions in PROPS
+	     are in increasing order, so that we can do (effectively)
+	     one scan through the position space of the format string.
+
+	     BYTEPOS is the byte position in the format string,
+	     POSITION is the untranslated char position in it,
+	     TRANSLATED is the translated char position in BUF,
+	     and ARGN is the number of the next arg we will come to.  */
+	  for (list = props; CONSP (list); list = XCDR (list))
+	    {
+	      Lisp_Object item, pos;
+
+	      item = XCAR (list);
+
+	      /* First adjust the property start position.  */
+	      pos = XINT (XCAR (item));
+
+	      /* Advance BYTEPOS, POSITION, TRANSLATED and ARGN
+		 up to this position.  */
+	      for (; position < pos; bytepos++)
+		{
+		  if (! discarded[bytepos])
+		    position++, translated++;
+		  else if (discarded[bytepos] == 1)
+		    {
+		      position++;
+		      if (translated == info[argn].start)
+			{
+			  translated += info[argn].end - info[argn].start;
+			  argn++;
+			}
+		    }
+		}
+
+	      XSETCAR (item, make_number (translated));
+
+	      /* Likewise adjust the property end position.  */
+	      pos = XINT (XCAR (XCDR (item)));
+
+	      for (; bytepos < pos; bytepos++)
+		{
+		  if (! discarded[bytepos])
+		    position++, translated++;
+		  else if (discarded[bytepos] == 1)
+		    {
+		      position++;
+		      if (translated == info[argn].start)
+			{
+			  translated += info[argn].end - info[argn].start;
+			  argn++;
+			}
+		    }
+		}
+
+	      XSETCAR (XCDR (item), make_number (translated));
+	    }
+
 	  add_text_properties_from_list (val, props, make_number (0));
 	}
 
       /* Add text properties from arguments.  */
-      if (info)
+      if (arg_intervals)
 	for (n = 1; n < nargs; ++n)
-	  if (info[n].end)
+	  if (info[n].intervals)
 	    {
 	      len = make_number (SCHARS (args[n]));
 	      new_len = make_number (info[n].end - info[n].start);