From bb3973381c762738a5dff2028c5e6edd41b32f65 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 5 Jul 2016 20:33:00 -0400
Subject: [PATCH 07/25] FIXME: WIP on on-demand substring locations

---
 gcc/c-family/c-common.c                            |   4 +-
 gcc/c-family/c-lex.c                               |  30 +-
 gcc/cp/parser.c                                    |   6 +-
 gcc/gcc-rich-location.c                            |   2 +
 gcc/input.c                                        | 475 ++++++-------------
 gcc/input.h                                        |   5 +
 .../diagnostic_plugin_test_string_literals.c       |  14 +-
 gcc/tree-core.h                                    |   3 -
 gcc/tree.c                                         |   3 +-
 gcc/tree.h                                         |   2 -
 libcpp/charset.c                                   | 505 +++++++++++++++++----
 libcpp/directives.c                                |   4 +-
 libcpp/expr.c                                      |   2 +
 libcpp/include/cpplib.h                            |  54 ++-
 libcpp/internal.h                                  |   4 +-
 libcpp/lex.c                                       |   4 +-
 libcpp/macro.c                                     |   2 +-
 17 files changed, 637 insertions(+), 482 deletions(-)

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 18c4856..48c40a6 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -945,7 +945,7 @@ fname_as_string (int pretty_p)
   const char *name = "top level";
   char *namep;
   int vrb = 2, len;
-  cpp_string cstr = { 0, 0, {NULL, 0, 0} }, strname;
+  cpp_string cstr = { 0, 0 }, strname;
 
   if (!pretty_p)
     {
@@ -962,7 +962,7 @@ fname_as_string (int pretty_p)
   snprintf (namep, len, "\"%s\"", name);
   strname.text = (unsigned char *) namep;
   strname.len = len - 1;
-  strname.loc.init_raw (UNKNOWN_LOCATION, len, 1, 1, line_table);
+  //strname.loc.init_raw (UNKNOWN_LOCATION, len, 1, 1, line_table);
 
   if (cpp_interpret_string (parse_in, &strname, 1, &cstr, CPP_STRING))
     {
diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c
index 8a722cf..04f5b95 100644
--- a/gcc/c-family/c-lex.c
+++ b/gcc/c-family/c-lex.c
@@ -165,7 +165,7 @@ cb_ident (cpp_reader * ARG_UNUSED (pfile),
   if (!flag_no_ident)
     {
       /* Convert escapes in the string.  */
-      cpp_string cstr = { 0, 0, { NULL, 0, 0 } };
+      cpp_string cstr = { 0, 0 };
       if (cpp_interpret_string (pfile, str, 1, &cstr, CPP_STRING))
 	{
 	  targetm.asm_out.output_ident ((const char *) cstr.text);
@@ -1072,20 +1072,6 @@ interpret_fixed (const cpp_token *token, unsigned int flags)
   return value;
 }
 
-/* Ensure that src->loc has location data, hardcoding it to a single
-   character at SRC_LOC if data is not already present.  */
-
-static void
-ensure_string_has_location (const cpp_string *str, source_location src_loc)
-{
-  if (!str->loc.m_fragloc_array)
-    {
-      cpp_string *mutstr = const_cast <cpp_string *> (str);
-      cpp_string_location *strloc = &mutstr->loc;
-      strloc->init_raw (src_loc, mutstr->len, 1, 1, line_table);
-    }
-}
-
 /* Convert a series of STRING, WSTRING, STRING16, STRING32 and/or
    UTF8STRING tokens into a tree, performing string constant
    concatenation.  TOK is the first of these.  VALP is the location to
@@ -1117,9 +1103,9 @@ lex_string (const cpp_token *tok, tree *valp, bool objc_string, bool translate)
   /* Try to avoid the overhead of creating and destroying an obstack
      for the common case of just one string.  */
   cpp_string str = tok->val.str;
-  ensure_string_has_location (&str, tok->src_loc);
+  //ensure_string_has_location (&str, tok->src_loc);
   cpp_string *strs = &str;
-  location_t str0_loc = tok->src_loc;
+  //location_t str0_loc = tok->src_loc;
 
   /* objc_at_sign_was_seen is only used when doing Objective-C string
      concatenation.  It is 'true' if we have seen an '@' before the
@@ -1164,12 +1150,12 @@ lex_string (const cpp_token *tok, tree *valp, bool objc_string, bool translate)
       if (!concats)
 	{
 	  gcc_obstack_init (&str_ob);
-	  ensure_string_has_location (&str, str0_loc);
+	  //ensure_string_has_location (&str, str0_loc);
 	  obstack_grow (&str_ob, &str, sizeof (cpp_string));
 	}
 
       concats++;
-      ensure_string_has_location (&tok->val.str, tok->src_loc);
+      //ensure_string_has_location (&tok->val.str, tok->src_loc);
       obstack_grow (&str_ob, &tok->val.str, sizeof (cpp_string));
 
       if (objc_string)
@@ -1190,11 +1176,13 @@ lex_string (const cpp_token *tok, tree *valp, bool objc_string, bool translate)
     warning (OPT_Wtraditional,
 	     "traditional C rejects string constant concatenation");
 
+  // FIXME: record the concatenation here???
+
   if ((translate
        ? cpp_interpret_string : cpp_interpret_string_notranslate)
       (parse_in, strs, concats + 1, &istr, type))
     {
-      value = build_string (istr.len, (const char *) istr.text, &istr.loc);
+      value = build_string (istr.len, (const char *) istr.text);//, &istr.loc);
       free (CONST_CAST (unsigned char *, istr.text));
     }
   else
@@ -1261,7 +1249,7 @@ lex_charconst (const cpp_token *token)
   unsigned int chars_seen;
   int unsignedp = 0;
 
-  ensure_string_has_location (&token->val.str, token->src_loc);
+  //ensure_string_has_location (&token->val.str, token->src_loc);
 
   result = cpp_interpret_charconst (parse_in, token,
 				    &chars_seen, &unsignedp);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index c408bf2..17452f4 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -3878,7 +3878,7 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
 
       str.text = (const unsigned char *)TREE_STRING_POINTER (string_tree);
       str.len = TREE_STRING_LENGTH (string_tree);
-      str.loc.init_raw (tok->location, str.len, 1, 1, line_table);
+      //str.loc.init_raw (tok->location, str.len, 1, 1, line_table);
       count = 1;
 
       if (curr_tok_is_userdef_p)
@@ -3904,7 +3904,7 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
 	  count++;
 	  str.text = (const unsigned char *)TREE_STRING_POINTER (string_tree);
 	  str.len = TREE_STRING_LENGTH (string_tree);
-	  str.loc.init_raw (tok->location, str.len, 1, 1, line_table);
+	  //str.loc.init_raw (tok->location, str.len, 1, 1, line_table);
 
 	  if (curr_tok_is_userdef_p)
 	    {
@@ -3978,7 +3978,7 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
   if ((translate ? cpp_interpret_string : cpp_interpret_string_notranslate)
       (parse_in, strs, count, &istr, type))
     {
-      value = build_string (istr.len, (const char *)istr.text, &istr.loc);
+      value = build_string (istr.len, (const char *)istr.text);//, &istr.loc);
       free (CONST_CAST (unsigned char *, istr.text));
 
       switch (type)
diff --git a/gcc/gcc-rich-location.c b/gcc/gcc-rich-location.c
index 8ac45f9..f073406 100644
--- a/gcc/gcc-rich-location.c
+++ b/gcc/gcc-rich-location.c
@@ -75,6 +75,7 @@ gcc_rich_location::add_fixit_misspelled_id (location_t misspelled_token_loc,
   add_fixit_replace (misspelled_token_range, IDENTIFIER_POINTER (hint_id));
 }
 
+#if 0
 /* Debugging method.  Print a diagnostic showing the given fragment.  */
 
 void
@@ -103,3 +104,4 @@ cpp_string_location::debug () const
       fragment->debug (buf);
     }
 }
+#endif
diff --git a/gcc/input.c b/gcc/input.c
index 3d497dd..50c29f7 100644
--- a/gcc/input.c
+++ b/gcc/input.c
@@ -1139,6 +1139,113 @@ dump_location_info (FILE *stream)
 				MAX_SOURCE_LOCATION + 1, UINT_MAX);
 }
 
+/* FIXME.  */
+
+static bool
+get_substring_ranges_for_loc (cpp_reader *pfile,
+			      location_t strloc,
+			      cpp_substring_ranges &ranges)
+{
+  gcc_assert (pfile);
+
+  if (strloc == UNKNOWN_LOCATION)
+    return false;
+
+  /* FIXME: handle concatenated strings by doing this repeatedly, somehow.
+     We'd need to capture concatenations when they happen.  */
+
+  /* Get range of strloc, use it to locate the start of the literal token.
+     We can't reliably determine the literal length and truncate to it
+     since we might be near or beyond LINE_MAP_MAX_LOCATION_WITH_COLS.
+     Instead we simply read the line until we reach the terminator.  */
+  source_range src_range = get_range_from_loc (line_table, strloc);
+
+#if 0
+  if (src_range.m_start >= LINE_MAP_MAX_LOCATION_WITH_COLS)
+    /* If so, we can't reliably determine where the token started within
+       its line.  */
+    return false;
+#endif
+
+  expanded_location start
+    = expand_location_to_spelling_point (src_range.m_start);
+  expanded_location finish
+    = expand_location_to_spelling_point (src_range.m_finish);
+  if (start.file != finish.file)
+    return false;
+  if (start.line != finish.line)
+    return false;
+  if (start.column > finish.column)
+    return false;
+
+  int line_width;
+  const char *line = location_get_source_line (start.file, start.line,
+					       &line_width);
+  if (line == NULL)
+    return false;
+
+  /* Determine the location of the literal (including quotes
+     and leading FIXME.  */
+  const char *literal = line + start.column - 1;
+
+  /* Rerun cpp_interpret_string, or rather, a modified version of it.  */
+  /* FIXME: CPP_STRING32 captures the unicode in a fixed-width (4-byte
+     per unichar) encoding.  We could then use that to capture per-unichar
+     ranges, and do the UTF-8 thing separately.
+     For now, we simply constrain ourselves to the UTF-8 case (for both
+     source and execution encoding).  */
+  cpp_string from;
+  gcc_assert (line_width >= (literal - line));
+  from.len = line_width - (literal - line);
+  from.text = (const unsigned char *)literal;
+
+  if (!cpp_interpret_string_ranges (pfile, &from, &src_range.m_start, 1,
+				    &ranges))
+    return false;
+
+  /* OK, "ranges" should now contain information on the string.  */
+  return true;
+}
+
+/* FIXME.  */
+
+bool
+get_source_range_for_substring (cpp_reader *pfile, location_t strloc,
+				int start_idx, int end_idx, source_range *out)
+{
+  gcc_checking_assert (start_idx >= 0);
+  gcc_checking_assert (end_idx >= 0);
+  gcc_assert (out);
+
+  cpp_substring_ranges ranges;
+  if (!get_substring_ranges_for_loc (pfile, strloc, ranges))
+    return false;
+
+  if (start_idx >= ranges.get_num_ranges ()
+      || end_idx >= ranges.get_num_ranges ())
+      return false;
+
+  out->m_start = ranges.get_range (start_idx).m_start;
+  out->m_finish = ranges.get_range (end_idx).m_finish;
+  return true;
+}
+
+/* FIXME.  */
+
+bool
+get_num_source_ranges_for_substring (cpp_reader *pfile, location_t strloc,
+				     int *out)
+{
+  gcc_assert (out);
+
+  cpp_substring_ranges ranges;
+  if (!get_substring_ranges_for_loc (pfile, strloc, ranges))
+    return false;
+
+  *out = ranges.get_num_ranges ();
+  return true;
+}
+
 #if CHECKING_P
 
 namespace selftest {
@@ -1358,41 +1465,19 @@ test_builtins ()
 
 /* Verify that locations within string literals are correctly handled.  */
 
-/* Determine if we should verify the packing within cpp_string_location
-   instances.  If any location is above LINE_MAP_MAX_LOCATION_WITH_COLS,
-   the precise packing is likely to be unpredictable: some or all of
-   the characters might be above the threshold.  */
-
-static bool
-string_packing_verifiable_p ()
-{
-  return line_table->highest_location <= LINE_MAP_MAX_LOCATION_WITH_COLS;
-}
-
-/* Determine if we should attempt to verify the range bits within
-   cpp_string_fragment_location instances.
-   If any of the data may use a non default value for range_bits,
-   don't attempt to verify it.  */
-
-static bool
-range_bits_verifiable_p ()
-{
-  return (line_table->highest_location
-	  <= LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES);
-}
-
-/* Verify STRLOC's description of locations within a string literal.
-   Assert that the character at index IDX begins at column EXPECTED_START_COL
-   and ends at EXPECTED_FINISH_COL, unless the locations are beyond
-   LINE_MAP_MAX_LOCATION_WITH_COLS, in which case don't check their
-   columns.  */
+/* FIXME.  */
 
 static void
 assert_char_at_range (const location &loc,
-		      const cpp_string_location *strloc, int idx,
+		      cpp_reader *pfile,
+		      location_t strloc, int idx,
 		      int expected_start_col, int expected_finish_col)
 {
-  source_range actual_range = strloc->get_range_at_index (idx);
+  source_range actual_range;
+  bool result = get_source_range_for_substring (pfile, strloc, idx, idx,
+						&actual_range);
+  ASSERT_TRUE (result);
+
   if (should_have_column_data_p (actual_range.m_start))
     {
       int actual_start_col = LOCATION_COLUMN (actual_range.m_start);
@@ -1408,90 +1493,12 @@ assert_char_at_range (const location &loc,
 /* Macro for calling assert_char_at_range, supplying SELFTEST_LOCATION for
    the effective location of any errors.  */
 
-#define ASSERT_CHAR_AT_RANGE(IDX, EXPECTED_START_COL, EXPECTED_FINISH_COL) \
-  assert_char_at_range (SELFTEST_LOCATION, strloc, (IDX),		\
+#define ASSERT_CHAR_AT_RANGE(CPP_READER, STRLOC, IDX, EXPECTED_START_COL, \
+			     EXPECTED_FINISH_COL)			\
+  assert_char_at_range (SELFTEST_LOCATION, (CPP_READER), (STRLOC), (IDX),	\
 			(EXPECTED_START_COL), (EXPECTED_FINISH_COL))
 
-/* Locate fragment IDX within STRLOC and assert that its various fields
-   equal the provided values.  */
-
-static void
-assert_string_location_fragment (const location &loc,
-				 const cpp_string_location *strloc, int idx,
-				 location_t expected_start_loc,
-				 int expected_len, int expected_cols_per_item,
-				 int expected_bits_per_col,
-				 int expected_bytes_per_item)
-{
-  const cpp_string_fragment_location &fragment = strloc->m_fragloc_array[idx];
-  ASSERT_EQ_AT (loc, fragment.m_loc, expected_start_loc);
-  ASSERT_EQ_AT (loc, fragment.m_len, expected_len);
-  ASSERT_EQ_AT (loc, fragment.m_cols_per_item, expected_cols_per_item);
-  ASSERT_EQ_AT (loc, fragment.m_bits_per_col, expected_bits_per_col);
-  ASSERT_EQ_AT (loc, fragment.m_bytes_per_item, expected_bytes_per_item);
-}
-
-/* Macro for calling assert_string_location_fragment, supplying
-   SELFTEST_LOCATION for the effective location of any errors.  */
-
-#define ASSERT_STRING_LOCATION_FRAGMENT(STRLOC, IDX, EXPECTED_START_LOC, \
-					EXPECTED_LEN, EXPECTED_COLS_PER_CHAR, \
-					EXPECTED_BITS_PER_COL, \
-					EXPECTED_BYTES_PER_ITEM)       \
-  assert_string_location_fragment (SELFTEST_LOCATION, (STRLOC), (IDX), \
-				   (EXPECTED_START_LOC), (EXPECTED_LEN), \
-				   (EXPECTED_COLS_PER_CHAR),		\
-				   (EXPECTED_BITS_PER_COL),		\
-				   (EXPECTED_BYTES_PER_ITEM))
-
-
-/* Verify that locations work within a string literal, for the
-   simple case of a single unbroken run of 1 column-per-character
-   characters.  */
-
-static void
-test_simple_substring_locations (const line_table_case &case_)
-{
-  temp_line_table tmp_lt (case_);
-
-  linemap_add (line_table, LC_ENTER, false,
-	       "test-simple-substring-location.c", 0);
-  linemap_line_start (line_table, 1, 100);
-
-  const int start_column = 5;
-  location_t loc = linemap_position_for_column (line_table, start_column);
-
-  cpp_string_location strloc;
-  const int len = 10;
-  strloc.init_raw (loc, len, 1, 1, line_table);
-
-  /* Ensure that the line table contains all of the locations within the
-     string.  Specifically, ensure that they are all
-       <= line_table->highest_location, so that
-     linemap_location_from_macro_expansion_p returns false.  */
-  linemap_position_for_column (line_table, start_column + len);
-
-  /* Verify that we can correctly retrieve the range for each
-     character.  */
-  for (int i = 0; i < len; i++)
-    {
-      int expected_column = start_column + i;
-      assert_char_at_range (SELFTEST_LOCATION, &strloc, i,
-			    expected_column, expected_column);
-    }
-
-  /* Verify that the data was packed as expected.  */
-  if (string_packing_verifiable_p ())
-    {
-      ASSERT_TRUE (strloc.trivial_p ());
-      ASSERT_EQ (strloc.m_num_fraglocs, 1);
-      if (range_bits_verifiable_p ())
-	ASSERT_STRING_LOCATION_FRAGMENT (&strloc, 0, loc, len, 1,
-					 line_table->default_range_bits, 1);
-    }
-
-  linemap_add (line_table, LC_LEAVE, false, NULL, 0);
-}
+#if 0
 
 /* Verify a complicated example of locations within a string literal.
 
@@ -1670,6 +1677,7 @@ test_complex_substring_locations (const line_table_case &case_)
 
   linemap_add (line_table, LC_LEAVE, false, NULL, 0);
 }
+#endif
 
 /* TODO: a testcase involving more than one linemap.  */
 
@@ -1880,52 +1888,11 @@ test_lexer_string_locations_simple (const line_table_case &case_)
 
   ASSERT_EQ (tok->val.str.len, 12);
 
-  const cpp_string_location *strloc = &tok->val.str.loc;
-
-  /* If any of the data might be in no-columns mode, don't
-     attempt to predict the packing.  */
-  if (string_packing_verifiable_p ())
-    {
-      ASSERT_TRUE (strloc->trivial_p ());
-      ASSERT_EQ (strloc->m_num_fraglocs, 1);
-    }
-
-  /* Verify ranges of individual characters (including the quotes).  */
-  for (int i = 0; i < 12; i++)
-    ASSERT_CHAR_AT_RANGE(i, 9 + i, 9 + i);
-
-  ASSERT_EQ (strloc->m_num_fraglocs, 1);
-  if (range_bits_verifiable_p ())
-    ASSERT_STRING_LOCATION_FRAGMENT
-      (strloc, 0,
-       linemap_position_for_column (line_table, 9),
-       12, 1, case_.m_default_range_bits, 1);
-
-  /* Use cpp_interpret_string to extract the quoted characters
-     into "dst_string".  */
-  cpp_string dst_string;
-  bool result = cpp_interpret_string (test.m_parser, &tok->val.str, 1,
-				      &dst_string, CPP_STRING);
-  ASSERT_TRUE (result);
-
-  /* Verify dst_string.  */
-  strloc = &dst_string.loc;
-
   /* Verify ranges of individual characters.  This no longer includes the
      quotes.  */
   for (int i = 0; i < 9; i++)
-    ASSERT_CHAR_AT_RANGE(i, 10 + i, 10 + i);
+    ASSERT_CHAR_AT_RANGE (test.m_parser, tok->src_loc, i, 10 + i, 10 + i);
 
-  if (string_packing_verifiable_p ())
-    {
-      /* Verify packing.  */
-      ASSERT_EQ (strloc->m_num_fraglocs, 1);
-      if (range_bits_verifiable_p ())
-	ASSERT_STRING_LOCATION_FRAGMENT
-	  (strloc, 0,
-	   linemap_position_for_column (line_table, 10),
-	   10, 1, case_.m_default_range_bits, 1);
-    }
 }
 
 /* Lex a string literal containing a hex-escaped character.
@@ -1955,65 +1922,17 @@ test_lexer_string_locations_hex (const line_table_case &case_)
 
   ASSERT_EQ (tok->val.str.len, 15);
 
-  const cpp_string_location *strloc = &tok->val.str.loc;
-
-  /* Verify ranges of individual characters (including the quotes).  */
-  for (int i = 0; i < 15; i++)
-    ASSERT_CHAR_AT_RANGE(i, 9 + i, 9 + i);
-
-  /* If any of the data might be in no-columns mode, don't
-     attempt to predict the packing.  */
-  if (string_packing_verifiable_p ())
-    {
-      ASSERT_TRUE (strloc->trivial_p ());
-      ASSERT_EQ (strloc->m_num_fraglocs, 1);
-      if (range_bits_verifiable_p ())
-	ASSERT_STRING_LOCATION_FRAGMENT
-	  (strloc, 0,
-	   linemap_position_for_column (line_table, 9),
-	   15, 1, case_.m_default_range_bits, 1);
-    }
-
-  /* Use cpp_interpret_string to extract the quoted characters
-     into "dst_string".  */
-  cpp_string dst_string;
-  bool result = cpp_interpret_string (test.m_parser, &tok->val.str, 1,
-				      &dst_string, CPP_STRING);
-  ASSERT_TRUE (result);
-
-  /* Verify dst_string.  */
-  strloc = &dst_string.loc;
-
   /* Verify ranges of individual characters.  This no longer includes the
      quotes.  */
   for (int i = 0; i < 5; i++)
-    ASSERT_CHAR_AT_RANGE(i, 10 + i, 10 + i);
-  ASSERT_CHAR_AT_RANGE(5, 15, 18);
+    ASSERT_CHAR_AT_RANGE (test.m_parser, tok->src_loc, i, 10 + i, 10 + i);
+  ASSERT_CHAR_AT_RANGE (test.m_parser, tok->src_loc, 5, 15, 18);
   for (int i = 6; i <= 9; i++)
-    ASSERT_CHAR_AT_RANGE(i, 13 + i, 13 + i);
-
-  if (string_packing_verifiable_p ())
-    {
-      /* Verify packing.  */
-      ASSERT_EQ (strloc->m_num_fraglocs, 3);
-      if (range_bits_verifiable_p ())
-	{
-	  ASSERT_STRING_LOCATION_FRAGMENT
-	    (strloc, 0,
-	     linemap_position_for_column (line_table, 10),
-	     5, 1, case_.m_default_range_bits, 1);
-	  ASSERT_STRING_LOCATION_FRAGMENT
-	    (strloc, 1,
-	     linemap_position_for_column (line_table, 15),
-	     1, 4, case_.m_default_range_bits, 1);
-	  ASSERT_STRING_LOCATION_FRAGMENT
-	    (strloc, 2,
-	     linemap_position_for_column (line_table, 19),
-	     4, 1, case_.m_default_range_bits, 1);
-	}
-    }
+    ASSERT_CHAR_AT_RANGE (test.m_parser, tok->src_loc, i, 13 + i, 13 + i);
 }
 
+#if 1
+
 /* Lex a string literal containing an octal-escaped character.
    Verify the substring location data after running cpp_interpret_string
    on it.  */
@@ -2035,6 +1954,7 @@ test_lexer_string_locations_oct (const line_table_case &case_)
   ASSERT_EQ (tok->type, CPP_STRING);
   ASSERT_TOKEN_AS_TEXT_EQ (test.m_parser, tok, "\"01234\\065 789\"");
 
+#if 0
   /* Use cpp_interpret_string to extract the quoted characters
      into "dst_string".  */
   cpp_string dst_string;
@@ -2044,35 +1964,15 @@ test_lexer_string_locations_oct (const line_table_case &case_)
 
   /* Verify dst_string.  */
   const cpp_string_location *strloc = &dst_string.loc;
+#endif
 
   /* Verify ranges of individual characters.  This no longer includes the
      quotes.  */
   for (int i = 0; i < 5; i++)
-    ASSERT_CHAR_AT_RANGE(i, 10 + i, 10 + i);
-  ASSERT_CHAR_AT_RANGE(5, 15, 18);
+    ASSERT_CHAR_AT_RANGE (test.m_parser, tok->src_loc, i, 10 + i, 10 + i);
+  ASSERT_CHAR_AT_RANGE (test.m_parser, tok->src_loc, 5, 15, 18);
   for (int i = 6; i <= 9; i++)
-    ASSERT_CHAR_AT_RANGE(i, 13 + i, 13 + i);
-
-  if (string_packing_verifiable_p ())
-    {
-      /* Verify packing.  */
-      ASSERT_EQ (strloc->m_num_fraglocs, 3);
-      if (range_bits_verifiable_p ())
-	{
-	  ASSERT_STRING_LOCATION_FRAGMENT
-	    (strloc, 0,
-	     linemap_position_for_column (line_table, 10),
-	     5, 1, case_.m_default_range_bits, 1);
-	  ASSERT_STRING_LOCATION_FRAGMENT
-	    (strloc, 1,
-	     linemap_position_for_column (line_table, 15),
-	     1, 4, case_.m_default_range_bits, 1);
-	  ASSERT_STRING_LOCATION_FRAGMENT
-	    (strloc, 2,
-	     linemap_position_for_column (line_table, 19),
-	     4, 1, case_.m_default_range_bits, 1);
-	}
-    }
+    ASSERT_CHAR_AT_RANGE (test.m_parser, tok->src_loc, i, 13 + i, 13 + i);
 }
 
 /* Lex a string literal containing UCN 4 characters.
@@ -2095,16 +1995,6 @@ test_lexer_string_locations_ucn4 (const line_table_case &case_)
   ASSERT_EQ (tok->type, CPP_STRING);
   ASSERT_TOKEN_AS_TEXT_EQ (test.m_parser, tok, "\"01234\\u2174\\u2175789\"");
 
-  /* Use cpp_interpret_string to extract the quoted characters
-     into "dst_string".  */
-  cpp_string dst_string;
-  bool result = cpp_interpret_string (test.m_parser, &tok->val.str, 1,
-				      &dst_string, CPP_STRING);
-  ASSERT_TRUE (result);
-
-  /* Verify dst_string.  */
-  const cpp_string_location *strloc = &dst_string.loc;
-
   /* The string should now be encoded in the execution character
      set.  Assuming that that is UTF-8, we should have the following:
      -----------  ----  -----  -------  ----------------
@@ -2130,40 +2020,16 @@ test_lexer_string_locations_ucn4 (const line_table_case &case_)
      quotes.
      '01234'.  */
   for (int i = 0; i < 4; i++)
-    ASSERT_CHAR_AT_RANGE (i, 10 + i, 10 + i);
+    ASSERT_CHAR_AT_RANGE (test.m_parser, tok->src_loc, i, 10 + i, 10 + i);
   /* U+2174.  */
   for (int i = 5; i < 7; i++)
-    ASSERT_CHAR_AT_RANGE (i, 15, 20);
+    ASSERT_CHAR_AT_RANGE (test.m_parser, tok->src_loc, i, 15, 20);
   /* U+2175.  */
   for (int i = 8; i < 10; i++)
-    ASSERT_CHAR_AT_RANGE (i, 21, 26);
+    ASSERT_CHAR_AT_RANGE (test.m_parser, tok->src_loc, i, 21, 26);
   /* '789'.  */
   for (int i = 11; i <= 13; i++)
-    ASSERT_CHAR_AT_RANGE (i, 16 + i, 16 + i);
-
-  /* We should have the following fragments:
-     fragment 0: 4 items, 1 byte per item, 1 column per item
-     fragment 1: 2 items, 3 bytes per item, 6 columns per item
-     fragment 2: 3 items, 1 byte per item, 1 column per item.  */
-  if (string_packing_verifiable_p ())
-    {
-      ASSERT_EQ (strloc->m_num_fraglocs, 3);
-      if (range_bits_verifiable_p ())
-	{
-	  ASSERT_STRING_LOCATION_FRAGMENT
-	    (strloc, 0,
-	     linemap_position_for_column (line_table, 10),
-	     5, 1, case_.m_default_range_bits, 1);
-	  ASSERT_STRING_LOCATION_FRAGMENT
-	    (strloc, 1,
-	     linemap_position_for_column (line_table, 15),
-	     2, 6, case_.m_default_range_bits, 3);
-	  ASSERT_STRING_LOCATION_FRAGMENT
-	    (strloc, 2,
-	     linemap_position_for_column (line_table, 27),
-	     3, 1, case_.m_default_range_bits, 1);
-	}
-    }
+    ASSERT_CHAR_AT_RANGE (test.m_parser, tok->src_loc, i, 16 + i, 16 + i);
 }
 
 /* Lex a string literal containing UCN 8 characters.
@@ -2186,16 +2052,6 @@ test_lexer_string_locations_ucn8 (const line_table_case &case_)
   ASSERT_TOKEN_AS_TEXT_EQ (test.m_parser, tok,
 			   "\"01234\\U00002174\\U00002175789\"");
 
-  /* Use cpp_interpret_string to extract the quoted characters
-     into "dst_string".  */
-  cpp_string dst_string;
-  bool result = cpp_interpret_string (test.m_parser, &tok->val.str, 1,
-				      &dst_string, CPP_STRING);
-  ASSERT_TRUE (result);
-
-  /* Verify dst_string.  */
-  const cpp_string_location *strloc = &dst_string.loc;
-
   /* The UTF-8 encoding of the string is identical to that from
      the ucn4 testcase above; the only difference is the column
      locations.  */
@@ -2204,36 +2060,16 @@ test_lexer_string_locations_ucn8 (const line_table_case &case_)
      quotes.
      '01234'.  */
   for (int i = 0; i < 4; i++)
-    ASSERT_CHAR_AT_RANGE (i, 10 + i, 10 + i);
+    ASSERT_CHAR_AT_RANGE (test.m_parser, tok->src_loc, i, 10 + i, 10 + i);
   /* U+2174.  */
   for (int i = 5; i < 7; i++)
-    ASSERT_CHAR_AT_RANGE (i, 15, 24);
+    ASSERT_CHAR_AT_RANGE (test.m_parser, tok->src_loc, i, 15, 24);
   /* U+2175.  */
   for (int i = 8; i < 10; i++)
-    ASSERT_CHAR_AT_RANGE (i, 25, 34);
+    ASSERT_CHAR_AT_RANGE (test.m_parser, tok->src_loc, i, 25, 34);
   /* '789' at columns 35-37  */
   for (int i = 11; i <= 13; i++)
-    ASSERT_CHAR_AT_RANGE (i, 24 + i, 24 + i);
-
-  if (string_packing_verifiable_p ())
-    {
-      ASSERT_EQ (strloc->m_num_fraglocs, 3);
-      if (range_bits_verifiable_p ())
-	{
-	  ASSERT_STRING_LOCATION_FRAGMENT
-	    (strloc, 0,
-	     linemap_position_for_column (line_table, 10),
-	     5, 1, case_.m_default_range_bits, 1);
-	  ASSERT_STRING_LOCATION_FRAGMENT
-	    (strloc, 1,
-	     linemap_position_for_column (line_table, 15),
-	     2, 10, case_.m_default_range_bits, 3);
-	  ASSERT_STRING_LOCATION_FRAGMENT
-	    (strloc, 2,
-	     linemap_position_for_column (line_table, 35),
-	     3, 1, case_.m_default_range_bits, 1);
-	}
-    }
+    ASSERT_CHAR_AT_RANGE (test.m_parser, tok->src_loc, i, 24 + i, 24 + i);
 }
 
 /* Lex a u8-string literal.
@@ -2255,32 +2091,12 @@ test_lexer_string_locations_u8 (const line_table_case &case_)
   ASSERT_EQ (tok->type, CPP_UTF8STRING);
   ASSERT_TOKEN_AS_TEXT_EQ (test.m_parser, tok, "u8\"0123456789\"");
 
-  /* Use cpp_interpret_string to extract the quoted characters
-     into "dst_string".  */
-  cpp_string dst_string;
-  bool result = cpp_interpret_string (test.m_parser, &tok->val.str, 1,
-				      &dst_string, CPP_STRING);
-  ASSERT_TRUE (result);
-
-  /* Verify dst_string.  */
-  const cpp_string_location *strloc = &dst_string.loc;
-
   /* Verify ranges of individual characters.  This no longer includes the
      quotes.  */
   for (int i = 0; i < 9; i++)
-    ASSERT_CHAR_AT_RANGE (i, 10 + i, 10 + i);
-
-  if (string_packing_verifiable_p ())
-    {
-      /* Verify packing.  */
-      ASSERT_EQ (strloc->m_num_fraglocs, 1);
-      if (range_bits_verifiable_p ())
-	ASSERT_STRING_LOCATION_FRAGMENT
-	  (strloc, 0,
-	   linemap_position_for_column (line_table, 10),
-	   10, 1, case_.m_default_range_bits, 1);
-    }
+    ASSERT_CHAR_AT_RANGE (test.m_parser, tok->src_loc, i, 10 + i, 10 + i);
 }
+#endif
 
 /* A table of interesting location_t values, giving one axis of our test
    matrix.  */
@@ -2317,6 +2133,10 @@ input_c_tests ()
   test_unknown_location ();
   test_builtins ();
 
+#if 0
+  test_location_column_from_byte_offset ();
+#endif
+
   /* As noted above in the description of struct line_table_case,
      we want to explore a test matrix of interesting line_table
      situations, running various selftests for each case within the
@@ -2339,16 +2159,17 @@ input_c_tests ()
 
 	  /* Run all tests for the given case within the test matrix.  */
 	  test_accessing_ordinary_linemaps (c);
-	  test_simple_substring_locations (c);
+#if 0
 	  test_complex_substring_locations (c);
+#endif
 	  test_lexer (c);
+
 	  test_lexer_string_locations_simple (c);
 	  test_lexer_string_locations_hex (c);
 	  test_lexer_string_locations_oct (c);
 	  test_lexer_string_locations_ucn4 (c);
 	  test_lexer_string_locations_ucn8 (c);
 	  test_lexer_string_locations_u8 (c);
-
 	  num_cases_tested++;
 	}
     }
diff --git a/gcc/input.h b/gcc/input.h
index ae4fecf..0caf962 100644
--- a/gcc/input.h
+++ b/gcc/input.h
@@ -82,4 +82,9 @@ void dump_location_info (FILE *stream);
 
 void diagnostics_file_cache_fini (void);
 
+extern bool get_source_range_for_substring (cpp_reader *pfile,
+					    location_t strloc,
+					    int start_idx, int end_idx,
+					    source_range *out);
+
 #endif
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_string_literals.c b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_string_literals.c
index 44ec480..71e6e7c 100644
--- a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_string_literals.c
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_string_literals.c
@@ -31,6 +31,7 @@
 #include "context.h"
 #include "print-tree.h"
 #include "cpplib.h"
+#include "c-family/c-pragma.h"
 
 int plugin_is_GPL_compatible;
 
@@ -155,11 +156,14 @@ test_string_literals (gimple *stmt)
     }
   int end_idx = TREE_INT_CST_LOW (t_end_idx);
 
-  cpp_string_location *strloc = TREE_STRING_LOCATION (t_string);
-  gcc_assert (strloc);
-  source_range src_range
-    = strloc->get_range_between_indices (start_idx, end_idx);
-  emit_warning (src_range);
+  /* A STRING_CST doesn't have a location, but the ADDR_EXPR does.  */
+  location_t strloc = EXPR_LOCATION (t_addr_string);
+  source_range src_range;
+  if (get_source_range_for_substring (parse_in, strloc,
+				      start_idx, end_idx, &src_range))
+    emit_warning (src_range);
+  else
+    error_at (strloc, "unable to read substring range");
 }
 
 /* Call test_string_literals on every statement within FUN.  */
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index a5fe5be..db5b470 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -1244,12 +1244,9 @@ struct GTY(()) tree_fixed_cst {
   struct fixed_value * fixed_cst_ptr;
 };
 
-struct cpp_string_location;
-
 struct GTY(()) tree_string {
   struct tree_typed typed;
   int length;
-  cpp_string_location *loc;
   char str[1];
 };
 
diff --git a/gcc/tree.c b/gcc/tree.c
index 0c13de1..afb8435 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -2004,13 +2004,13 @@ build_string (int len, const char *str)
   TREE_SET_CODE (s, STRING_CST);
   TREE_CONSTANT (s) = 1;
   TREE_STRING_LENGTH (s) = len;
-  TREE_STRING_LOCATION (s) = NULL;
   memcpy (s->string.str, str, len);
   s->string.str[len] = '\0';
 
   return s;
 }
 
+#if 0
 /* As above, but with per-character location information.  */
 
 struct cpp_string_location_stats cpp_string_location_stats;
@@ -2031,6 +2031,7 @@ build_string (int len, const char *str, cpp_string_location *strloc)
 
   return s;
 }
+#endif
 
 
 /* Return a newly constructed COMPLEX_CST node whose value is
diff --git a/gcc/tree.h b/gcc/tree.h
index b83b9c9..1e34fd0 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1005,7 +1005,6 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int,
 /* In a STRING_CST */
 /* In C terms, this is sizeof, not strlen.  */
 #define TREE_STRING_LENGTH(NODE) (STRING_CST_CHECK (NODE)->string.length)
-#define TREE_STRING_LOCATION(NODE) (STRING_CST_CHECK (NODE)->string.loc)
 #define TREE_STRING_POINTER(NODE) \
   ((const char *)(STRING_CST_CHECK (NODE)->string.str))
 
@@ -3968,7 +3967,6 @@ extern tree build_minus_one_cst (tree);
 extern tree build_all_ones_cst (tree);
 extern tree build_zero_cst (tree);
 extern tree build_string (int, const char *);
-extern tree build_string (int, const char *, cpp_string_location *);
 extern tree build_tree_list_stat (tree, tree MEM_STAT_DECL);
 #define build_tree_list(t, q) build_tree_list_stat (t, q MEM_STAT_INFO)
 extern tree build_tree_list_vec_stat (const vec<tree, va_gc> *MEM_STAT_DECL);
diff --git a/libcpp/charset.c b/libcpp/charset.c
index 27a94da..4169a13 100644
--- a/libcpp/charset.c
+++ b/libcpp/charset.c
@@ -99,7 +99,7 @@ struct _cpp_strbuf
   uchar *text;
   size_t asize;
   size_t len;
-  cpp_string_location loc;
+  //cpp_string_location loc;
 };
 
 /* This is enough to hold any string that fits on a single 80-column
@@ -454,8 +454,8 @@ one_utf16_to_utf8 (iconv_t bigend, const uchar **inbufp, size_t *inbytesleftp,
 static inline bool
 conversion_loop (int (*const one_conversion)(iconv_t, const uchar **, size_t *,
 					     uchar **, size_t *),
-		 iconv_t cd, const uchar *from, size_t flen, struct _cpp_strbuf *to,
-		 cpp_string_location_reader *loc_reader)
+		 iconv_t cd, const uchar *from, size_t flen, struct _cpp_strbuf *to)
+//cpp_string_location_reader *loc_reader)
 {
   const uchar *inbuf;
   uchar *outbuf;
@@ -471,9 +471,10 @@ conversion_loop (int (*const one_conversion)(iconv_t, const uchar **, size_t *,
     {
       do
 	{
-	  size_t old_outbytesleft = outbytesleft;
+	  //	  size_t old_outbytesleft = outbytesleft;
 	  rval = one_conversion (cd, &inbuf, &inbytesleft,
 				 &outbuf, &outbytesleft);
+#if 0
 	  if (loc_reader)
 	    {
 	      size_t bytes_written = outbytesleft - old_outbytesleft;
@@ -481,6 +482,7 @@ conversion_loop (int (*const one_conversion)(iconv_t, const uchar **, size_t *,
 				   bytes_written,
 				   loc_reader->get_line_maps ());
 	    }
+#endif
 	}
       while (inbytesleft && !rval);
 
@@ -516,42 +518,42 @@ conversion_loop (int (*const one_conversion)(iconv_t, const uchar **, size_t *,
 /* These four use the custom conversion code above.  */
 static bool
 convert_utf8_utf16 (iconv_t cd, const uchar *from, size_t flen,
-		    struct _cpp_strbuf *to, cpp_string_location_reader *loc_reader,
-		    bool single_item ATTRIBUTE_UNUSED)
+		    struct _cpp_strbuf *to)//, cpp_string_location_reader *loc_reader,
+//bool single_item ATTRIBUTE_UNUSED)
 {
-  return conversion_loop (one_utf8_to_utf16, cd, from, flen, to, loc_reader);
+  return conversion_loop (one_utf8_to_utf16, cd, from, flen, to);//, loc_reader);
 }
 
 static bool
 convert_utf8_utf32 (iconv_t cd, const uchar *from, size_t flen,
-		    struct _cpp_strbuf *to, cpp_string_location_reader *loc_reader,
-		    bool single_item ATTRIBUTE_UNUSED)
+		    struct _cpp_strbuf *to)//, cpp_string_location_reader *loc_reader,
+//bool single_item ATTRIBUTE_UNUSED)
 {
-  return conversion_loop (one_utf8_to_utf32, cd, from, flen, to, loc_reader);
+  return conversion_loop (one_utf8_to_utf32, cd, from, flen, to);//, loc_reader);
 }
 
 static bool
 convert_utf16_utf8 (iconv_t cd, const uchar *from, size_t flen,
-		    struct _cpp_strbuf *to, cpp_string_location_reader *loc_reader,
-		    bool single_item ATTRIBUTE_UNUSED)
+		    struct _cpp_strbuf *to)//, cpp_string_location_reader *loc_reader,
+//bool single_item ATTRIBUTE_UNUSED)
 {
-  return conversion_loop (one_utf16_to_utf8, cd, from, flen, to, loc_reader);
+  return conversion_loop (one_utf16_to_utf8, cd, from, flen, to);//, loc_reader);
 }
 
 static bool
 convert_utf32_utf8 (iconv_t cd, const uchar *from, size_t flen,
-		    struct _cpp_strbuf *to, cpp_string_location_reader *loc_reader,
-		    bool single_item ATTRIBUTE_UNUSED)
+		    struct _cpp_strbuf *to)//, cpp_string_location_reader *loc_reader,
+//bool single_item ATTRIBUTE_UNUSED)
 {
-  return conversion_loop (one_utf32_to_utf8, cd, from, flen, to, loc_reader);
+  return conversion_loop (one_utf32_to_utf8, cd, from, flen, to);//, loc_reader);
 }
 
 /* Identity conversion, used when we have no alternative.  */
 static bool
 convert_no_conversion (iconv_t cd ATTRIBUTE_UNUSED,
-		       const uchar *from, size_t flen, struct _cpp_strbuf *to,
-		       cpp_string_location_reader *loc_reader,
-		       bool single_item)
+		       const uchar *from, size_t flen, struct _cpp_strbuf *to)
+//cpp_string_location_reader *loc_reader,
+//bool single_item)
 {
   if (to->len + flen > to->asize)
     {
@@ -561,10 +563,12 @@ convert_no_conversion (iconv_t cd ATTRIBUTE_UNUSED,
     }
   memcpy (to->text + to->len, from, flen);
   to->len += flen;
+#if 0
   if (single_item)
     to->loc.add_n_chars_at (1, flen, loc_reader);
   else
     to->loc.add_n_chars_at (flen, 1, loc_reader);
+#endif
   return true;
 }
 
@@ -582,9 +586,9 @@ convert_no_conversion (iconv_t cd ATTRIBUTE_UNUSED,
 
 static bool
 convert_using_iconv (iconv_t cd, const uchar *from, size_t flen,
-		     struct _cpp_strbuf *to,
-		     cpp_string_location_reader */*loc_reader*/,
-		     bool single_item ATTRIBUTE_UNUSED)
+		     struct _cpp_strbuf *to)//,
+//cpp_string_location_reader */*loc_reader*/,
+//bool single_item ATTRIBUTE_UNUSED)
 {
   ICONV_CONST char *inbuf;
   char *outbuf;
@@ -631,8 +635,8 @@ convert_using_iconv (iconv_t cd, const uchar *from, size_t flen,
 /* Arrange for the above custom conversion logic to be used automatically
    when conversion between a suitable pair of character sets is requested.  */
 
-#define APPLY_CONVERSION(CONVERTER, FROM, FLEN, TO, LOC_READER, SINGLE_ITEM) \
-  CONVERTER.func (CONVERTER.cd, FROM, FLEN, TO, LOC_READER, SINGLE_ITEM)
+#define APPLY_CONVERSION(CONVERTER, FROM, FLEN, TO) /*, LOC_READER, SINGLE_ITEM) \*/ \
+  CONVERTER.func (CONVERTER.cd, FROM, FLEN, TO)//, LOC_READER, SINGLE_ITEM)
 
 struct cpp_conversion
 {
@@ -817,9 +821,9 @@ cpp_host_to_exec_charset (cpp_reader *pfile, cppchar_t c)
   tbuf.asize = 1;
   tbuf.text = XNEWVEC (uchar, tbuf.asize);
   tbuf.len = 0;
-  tbuf.loc.init ();
+  //tbuf.loc.init ();
 
-  if (!APPLY_CONVERSION (pfile->narrow_cset_desc, sbuf, 1, &tbuf, NULL, true))
+  if (!APPLY_CONVERSION (pfile->narrow_cset_desc, sbuf, 1, &tbuf))//, NULL, true))
     {
       cpp_errno (pfile, CPP_DL_ICE, "converting to execution character set");
       return 0;
@@ -838,6 +842,50 @@ cpp_host_to_exec_charset (cpp_reader *pfile, cppchar_t c)
 
 
 
+/* FIXME.  */
+
+cpp_substring_ranges::cpp_substring_ranges () :
+  m_ranges (NULL),
+  m_num_ranges (0),
+  m_alloc_ranges (8)
+{
+  m_ranges = XNEWVEC (source_range, m_alloc_ranges);
+}
+
+/* FIXME.  */
+
+cpp_substring_ranges::~cpp_substring_ranges ()
+{
+  free (m_ranges);
+}
+
+/* FIXME.  */
+
+void
+cpp_substring_ranges::add_range (source_range range)
+{
+  if (m_num_ranges >= m_alloc_ranges)
+    {
+      m_alloc_ranges *= 2;
+      m_ranges
+	= (source_range *)xrealloc (m_ranges,
+				    sizeof (source_range) * m_alloc_ranges);
+    }
+  m_ranges[m_num_ranges++] = range;
+}
+
+/* FIXME.  */
+
+void
+cpp_substring_ranges::add_n_ranges (int num,
+				cpp_string_location_reader &loc_reader)
+{
+  for (int i = 0; i < num; i++)
+    add_range (loc_reader.get_next ());
+}
+
+
+
 /* Utility routine that computes a mask of the form 0000...111... with
    WIDTH 1-bits.  */
 static inline size_t
@@ -1120,9 +1168,9 @@ _cpp_valid_ucn (cpp_reader *pfile, const uchar **pstr,
    An advanced pointer is returned.  Issues all relevant diagnostics.  */
 static const uchar *
 convert_ucn (cpp_reader *pfile, const uchar *from, const uchar *limit,
-	     struct _cpp_strbuf *tbuf, struct cset_converter cvt,
-	     source_range char_range,
-	     cpp_string_location_reader *loc_reader)
+	     struct _cpp_strbuf *tbuf, struct cset_converter cvt)//,
+//source_range char_range)//,
+//cpp_string_location_reader *loc_reader)
 {
   cppchar_t ucn;
   uchar buf[6];
@@ -1133,11 +1181,8 @@ convert_ucn (cpp_reader *pfile, const uchar *from, const uchar *limit,
 
   from++;  /* Skip u/U.  */
 
-  /* The u/U is part of the spelling of this character.  */
-  char_range.m_finish = loc_reader->get_next ().m_finish;
-
   _cpp_valid_ucn (pfile, &from, limit, 0, &nst,
-		  &ucn, &char_range, loc_reader);
+		  &ucn, NULL, NULL);
 
   rval = one_cppchar_to_utf8 (ucn, &bufp, &bytesleft);
   if (rval)
@@ -1151,14 +1196,7 @@ convert_ucn (cpp_reader *pfile, const uchar *from, const uchar *limit,
       /* Set up a cpp_string_location_reader to supply a
 	 location for the single character, covering all of
 	 char_range.  */
-      cpp_string_location_reader buf_loc_reader
-	(char_range.m_start,
-	 (((char_range.m_finish - char_range.m_start)
-	   >> loc_reader->get_bits_per_col ()) + 1),
-	 loc_reader->get_bits_per_col (),
-	 loc_reader->get_line_maps ());
-      if (!APPLY_CONVERSION (cvt, buf, 6 - bytesleft, tbuf, &buf_loc_reader,
-			     true))
+      if (!APPLY_CONVERSION (cvt, buf, 6 - bytesleft, tbuf))
 	cpp_errno (pfile, CPP_DL_ERROR,
 		   "converting UCN to execution character set");
     }
@@ -1166,6 +1204,40 @@ convert_ucn (cpp_reader *pfile, const uchar *from, const uchar *limit,
   return from;
 }
 
+#if 1
+static const uchar *
+convert_ucn_ranges (cpp_reader *pfile, const uchar *from, const uchar *limit,
+		    source_range char_range,
+		    cpp_string_location_reader *loc_reader,
+		    cpp_substring_ranges &ranges)
+{
+  cppchar_t ucn;
+  uchar buf[6];
+  uchar *bufp = buf;
+  size_t bytesleft = 6;
+  int rval;
+  struct normalize_state nst = INITIAL_NORMALIZE_STATE;
+
+  from++;  /* Skip u/U.  */
+
+  /* The u/U is part of the spelling of this character.  */
+  char_range.m_finish = loc_reader->get_next ().m_finish;
+
+  _cpp_valid_ucn (pfile, &from, limit, 0, &nst,
+		  &ucn, &char_range, loc_reader);
+
+  rval = one_cppchar_to_utf8 (ucn, &bufp, &bytesleft);
+  if (!rval)
+    {
+      int num_encoded_bytes = 6 - bytesleft;
+      for (int i = 0; i < num_encoded_bytes; i++)
+	ranges.add_range (char_range);
+    }
+
+  return from;
+}
+#endif
+
 /* Subroutine of convert_hex and convert_oct.  N is the representation
    in the execution character set of a numeric escape; write it into the
    string buffer TBUF and update the end-of-string pointer therein.  WIDE
@@ -1224,9 +1296,11 @@ emit_numeric_escape (cpp_reader *pfile, cppchar_t n,
    number.  You can, e.g. generate surrogate pairs this way.  */
 static const uchar *
 convert_hex (cpp_reader *pfile, const uchar *from, const uchar *limit,
-	     struct _cpp_strbuf *tbuf, struct cset_converter cvt,
+	     struct _cpp_strbuf *tbuf, struct cset_converter cvt)//
+#if 0
 	     source_range char_range,
 	     cpp_string_location_reader *loc_reader)
+#endif
 {
   cppchar_t c, n = 0, overflow = 0;
   int digits_found = 0;
@@ -1241,7 +1315,7 @@ convert_hex (cpp_reader *pfile, const uchar *from, const uchar *limit,
   from++;
 
   /* The 'x' is part of the spelling of this character.  */
-  char_range.m_finish = loc_reader->get_next ().m_finish;
+  //char_range.m_finish = loc_reader->get_next ().m_finish;
 
   while (from < limit)
     {
@@ -1249,7 +1323,7 @@ convert_hex (cpp_reader *pfile, const uchar *from, const uchar *limit,
       if (! hex_p (c))
 	break;
       from++;
-      char_range.m_finish = loc_reader->get_next ().m_finish;
+      //char_range.m_finish = loc_reader->get_next ().m_finish;
       overflow |= n ^ (n << 4 >> 4);
       n = (n << 4) + hex_value (c);
       digits_found = 1;
@@ -1271,8 +1345,35 @@ convert_hex (cpp_reader *pfile, const uchar *from, const uchar *limit,
 
   emit_numeric_escape (pfile, n, tbuf, cvt);
 
-  tbuf->loc.add_char_at (char_range, 1,
-			 pfile->line_table);
+  return from;
+}
+
+static const uchar *
+convert_hex_ranges (const uchar *from, const uchar *limit,
+		    source_range char_range,
+		    cpp_string_location_reader *loc_reader,
+		    cpp_substring_ranges &ranges)
+{
+  cppchar_t c, n = 0, overflow = 0;
+
+  /* Skip 'x'.  */
+  from++;
+
+  /* The 'x' is part of the spelling of this character.  */
+  char_range.m_finish = loc_reader->get_next ().m_finish;
+
+  while (from < limit)
+    {
+      c = *from;
+      if (! hex_p (c))
+	break;
+      from++;
+      char_range.m_finish = loc_reader->get_next ().m_finish;
+      overflow |= n ^ (n << 4 >> 4);
+      n = (n << 4) + hex_value (c);
+    }
+
+  ranges.add_range (char_range);
 
   return from;
 }
@@ -1285,9 +1386,11 @@ convert_hex (cpp_reader *pfile, const uchar *from, const uchar *limit,
    number.  */
 static const uchar *
 convert_oct (cpp_reader *pfile, const uchar *from, const uchar *limit,
-	     struct _cpp_strbuf *tbuf, struct cset_converter cvt,
+	     struct _cpp_strbuf *tbuf, struct cset_converter cvt)//
+#if 0
 	     source_range char_range,
 	     cpp_string_location_reader *loc_reader)
+#endif
 {
   size_t count = 0;
   cppchar_t c, n = 0;
@@ -1301,7 +1404,7 @@ convert_oct (cpp_reader *pfile, const uchar *from, const uchar *limit,
       if (c < '0' || c > '7')
 	break;
       from++;
-      char_range.m_finish = loc_reader->get_next ().m_finish;
+      //char_range.m_finish = loc_reader->get_next ().m_finish;
       overflow |= n ^ (n << 3 >> 3);
       n = (n << 3) + c - '0';
     }
@@ -1315,8 +1418,54 @@ convert_oct (cpp_reader *pfile, const uchar *from, const uchar *limit,
 
   emit_numeric_escape (pfile, n, tbuf, cvt);
 
+#if 0
   tbuf->loc.add_char_at (char_range, 1,
 			 pfile->line_table);
+#endif
+
+  return from;
+}
+
+static const uchar *
+convert_oct_ranges (cpp_reader */*pfile*/, const uchar *from, const uchar *limit,
+		    /*struct _cpp_strbuf *tbuf, struct cset_converter cvt, */
+		    source_range char_range,
+		    cpp_string_location_reader *loc_reader,
+		    cpp_substring_ranges &ranges)
+{
+  size_t count = 0;
+  cppchar_t c, n = 0;
+#if 0
+  size_t width = cvt.width;
+  size_t mask = width_to_mask (width);
+  bool overflow = false;
+#endif
+
+  while (from < limit && count++ < 3)
+    {
+      c = *from;
+      if (c < '0' || c > '7')
+	break;
+      from++;
+      char_range.m_finish = loc_reader->get_next ().m_finish;
+      //overflow |= n ^ (n << 3 >> 3);
+      n = (n << 3) + c - '0';
+    }
+
+#if 0
+  if (n != (n & mask))
+    {
+      cpp_error (pfile, CPP_DL_PEDWARN,
+		 "octal escape sequence out of range");
+      n &= mask;
+    }
+#endif
+
+#if 0
+  emit_numeric_escape (pfile, n, tbuf, cvt);
+#endif
+
+  ranges.add_range (char_range);
 
   return from;
 }
@@ -1327,8 +1476,8 @@ convert_oct (cpp_reader *pfile, const uchar *from, const uchar *limit,
    pointer.  Handles all relevant diagnostics.  */
 static const uchar *
 convert_escape (cpp_reader *pfile, const uchar *from, const uchar *limit,
-		struct _cpp_strbuf *tbuf, struct cset_converter cvt,
-		cpp_string_location_reader *loc_reader)
+		struct _cpp_strbuf *tbuf, struct cset_converter cvt)//,
+//cpp_string_location_reader *loc_reader)
 {
   /* Values of \a \b \e \f \n \r \t \v respectively.  */
 #if HOST_CHARSET == HOST_CHARSET_ASCII
@@ -1342,25 +1491,27 @@ convert_escape (cpp_reader *pfile, const uchar *from, const uchar *limit,
   uchar c;
 
   /* Record the location of the backslash.  */
+#if 0
   source_range char_range = loc_reader->get_next ();
+#endif
 
   c = *from;
   switch (c)
     {
       /* UCNs, hex escapes, and octal escapes are processed separately.  */
     case 'u': case 'U':
-      return convert_ucn (pfile, from, limit, tbuf, cvt,
-			  char_range, loc_reader);
+      return convert_ucn (pfile, from, limit, tbuf, cvt);
+      //char_range, loc_reader);
 
     case 'x':
-      return convert_hex (pfile, from, limit, tbuf, cvt,
-			  char_range, loc_reader);
+      return convert_hex (pfile, from, limit, tbuf, cvt);
+      //char_range, loc_reader);
       break;
 
     case '0':  case '1':  case '2':  case '3':
     case '4':  case '5':  case '6':  case '7':
-      return convert_oct (pfile, from, limit, tbuf, cvt,
-			  char_range, loc_reader);
+      return convert_oct (pfile, from, limit, tbuf, cvt);
+      //char_range, loc_reader);
 
       /* Various letter escapes.  Get the appropriate host-charset
 	 value into C.  */
@@ -1413,12 +1564,110 @@ convert_escape (cpp_reader *pfile, const uchar *from, const uchar *limit,
     }
 
   /* Now convert what we have to the execution character set.  */
-  if (!APPLY_CONVERSION (cvt, &c, 1, tbuf, loc_reader, true))
+  if (!APPLY_CONVERSION (cvt, &c, 1, tbuf))//, loc_reader, true))
     cpp_errno (pfile, CPP_DL_ERROR,
 	       "converting escape sequence to execution character set");
 
   return from + 1;
 }
+
+#if 1
+static const uchar *
+convert_escape_ranges (cpp_reader *pfile, const uchar *from, const uchar *limit,
+		       cpp_string_location_reader *loc_reader,
+		       cpp_substring_ranges &ranges)
+{
+  /* Values of \a \b \e \f \n \r \t \v respectively.  */
+#if HOST_CHARSET == HOST_CHARSET_ASCII
+  static const uchar charconsts[] = {  7,  8, 27, 12, 10, 13,  9, 11 };
+#elif HOST_CHARSET == HOST_CHARSET_EBCDIC
+  static const uchar charconsts[] = { 47, 22, 39, 12, 21, 13,  5, 11 };
+#else
+#error "unknown host character set"
+#endif
+
+  uchar c;
+
+  /* Record the location of the backslash.  */
+  source_range char_range = loc_reader->get_next ();
+
+  c = *from;
+  switch (c)
+    {
+      /* UCNs, hex escapes, and octal escapes are processed separately.  */
+    case 'u': case 'U':
+      return convert_ucn_ranges (pfile, from, limit, char_range, loc_reader,
+				 ranges);
+
+    case 'x':
+      return convert_hex_ranges (from, limit, char_range, loc_reader, ranges);
+
+    case '0':  case '1':  case '2':  case '3':
+    case '4':  case '5':  case '6':  case '7':
+      return convert_oct_ranges (pfile, from, limit, char_range, loc_reader,
+				 ranges);
+
+      /* Various letter escapes.  Get the appropriate host-charset
+	 value into C.  */
+    case '\\': case '\'': case '"': case '?': break;
+
+    case '(': case '{': case '[': case '%':
+      /* '\(', etc, can be used at the beginning of a line in a long
+	 string split onto multiple lines with \-newline, to prevent
+	 Emacs or other text editors from getting confused.  '\%' can
+	 be used to prevent SCCS from mangling printf format strings.  */
+      if (CPP_PEDANTIC (pfile))
+	goto unknown;
+      break;
+
+    case 'b': c = charconsts[1];  break;
+    case 'f': c = charconsts[3];  break;
+    case 'n': c = charconsts[4];  break;
+    case 'r': c = charconsts[5];  break;
+    case 't': c = charconsts[6];  break;
+    case 'v': c = charconsts[7];  break;
+
+    case 'a':
+      if (CPP_WTRADITIONAL (pfile))
+	cpp_warning (pfile, CPP_W_TRADITIONAL,
+		     "the meaning of '\\a' is different in traditional C");
+      c = charconsts[0];
+      break;
+
+    case 'e': case 'E':
+      if (CPP_PEDANTIC (pfile))
+	cpp_error (pfile, CPP_DL_PEDWARN,
+		   "non-ISO-standard escape sequence, '\\%c'", (int) c);
+      c = charconsts[2];
+      break;
+
+    default:
+    unknown:
+      if (ISGRAPH (c))
+	cpp_error (pfile, CPP_DL_PEDWARN,
+		   "unknown escape sequence: '\\%c'", (int) c);
+      else
+	{
+	  /* diagnostic.c does not support "%03o".  When it does, this
+	     code can use %03o directly in the diagnostic again.  */
+	  char buf[32];
+	  sprintf(buf, "%03o", (int) c);
+	  cpp_error (pfile, CPP_DL_PEDWARN,
+		     "unknown escape sequence: '\\%s'", buf);
+	}
+    }
+
+#if 0
+  /* Now convert what we have to the execution character set.  */
+  if (!APPLY_CONVERSION (cvt, &c, 1, tbuf))//, loc_reader, true))
+    cpp_errno (pfile, CPP_DL_ERROR,
+	       "converting escape sequence to execution character set");
+#endif
+
+  return from + 1;
+}
+#endif
+
 
 /* TYPE is a token type.  The return value is the conversion needed to
    convert from source to execution character set for the given type. */
@@ -1462,20 +1711,20 @@ cpp_interpret_string (cpp_reader *pfile, const cpp_string *from, size_t count,
   tbuf.asize = MAX (OUTBUF_BLOCK_SIZE, from->len);
   tbuf.text = XNEWVEC (uchar, tbuf.asize);
   tbuf.len = 0;
-  tbuf.loc.init ();
+  //tbuf.loc.init ();
 
   for (i = 0; i < count; i++)
     {
-      cpp_string_location_reader loc_reader (&from[i].loc, pfile->line_table);
+      //cpp_string_location_reader loc_reader (&from[i].loc, pfile->line_table);
       p = from[i].text;
       if (*p == 'u')
 	{
 	  p++;
-	  loc_reader.get_next ();
+	  //loc_reader.get_next ();
 	  if (*p == '8')
 	    {
 	      p++;
-	      loc_reader.get_next ();
+	      //loc_reader.get_next ();
 	    }
 	}
       else if (*p == 'L' || *p == 'U') p++;
@@ -1495,7 +1744,7 @@ cpp_interpret_string (cpp_reader *pfile, const cpp_string *from, size_t count,
 
 	  /* Raw strings are all normal characters; these can be fed
 	     directly to convert_cset.  */
-	  if (!APPLY_CONVERSION (cvt, p, limit - p, &tbuf, &loc_reader, true))
+	  if (!APPLY_CONVERSION (cvt, p, limit - p, &tbuf))//, &loc_reader, true))
 	    goto fail;
 
 	  continue;
@@ -1503,7 +1752,7 @@ cpp_interpret_string (cpp_reader *pfile, const cpp_string *from, size_t count,
 
       /* Skip leading quote.  */
       p++;
-      loc_reader.get_next ();
+      //loc_reader.get_next ();
 
       limit = from[i].text + from[i].len - 1; /* Skip trailing quote.  */
 
@@ -1516,14 +1765,14 @@ cpp_interpret_string (cpp_reader *pfile, const cpp_string *from, size_t count,
 	    {
 	      /* We have a run of normal characters; these can be fed
 		 directly to convert_cset.  */
-	      if (!APPLY_CONVERSION (cvt, base, p - base, &tbuf, &loc_reader,
-				     false))
+	      if (!APPLY_CONVERSION (cvt, base, p - base, &tbuf))//, &loc_reader,
+		//false))
 		goto fail;
 	    }
 	  if (p == limit)
 	    break;
 
-	  p = convert_escape (pfile, p + 1, limit, &tbuf, cvt, &loc_reader);
+	  p = convert_escape (pfile, p + 1, limit, &tbuf, cvt);//, &loc_reader);
 	}
     }
   /* NUL-terminate the 'to' buffer and translate it to a cpp_string
@@ -1532,7 +1781,7 @@ cpp_interpret_string (cpp_reader *pfile, const cpp_string *from, size_t count,
   tbuf.text = XRESIZEVEC (uchar, tbuf.text, tbuf.len);
   to->text = tbuf.text;
   to->len = tbuf.len;
-  to->loc = tbuf.loc;
+  //to->loc = tbuf.loc;
   return true;
 
  fail:
@@ -1541,6 +1790,87 @@ cpp_interpret_string (cpp_reader *pfile, const cpp_string *from, size_t count,
   return false;
 }
 
+/* FIXME.  */
+
+bool
+cpp_interpret_string_ranges (cpp_reader *pfile, const cpp_string *from,
+			     source_location *from_locations,
+			     size_t count,
+			     cpp_substring_ranges *out)
+{
+  /* SOURCE_CHARSET "UTF-8" */
+#if HOST_CHARSET == HOST_CHARSET_ASCII
+  /* We assume UTF-8 to UTF-8 conversion.  */
+
+  const uchar *p, *base, *limit;
+  size_t i;
+
+  for (i = 0; i < count; i++)
+    {
+      cpp_string_location_reader loc_reader (from_locations[i], pfile->line_table);
+      p = from[i].text;
+      if (*p == 'u')
+	{
+	  p++;
+	  loc_reader.get_next ();
+	  if (*p == '8')
+	    {
+	      p++;
+	      loc_reader.get_next ();
+	    }
+	}
+      else if (*p == 'L' || *p == 'U') p++;
+      if (*p == 'R')
+	{
+	  const uchar *prefix;
+
+	  /* Skip over 'R"'.  */
+	  p += 2;
+	  prefix = p;
+	  while (*p != '(')
+	    p++;
+	  p++;
+	  limit = from[i].text + from[i].len;
+	  if (limit >= p + (p - prefix) + 1)
+	    limit -= (p - prefix) + 1;
+
+	  /* Raw strings are all normal characters; assume UTF-8 to UTF-8
+	     conversion, hence these can be all treated as one byte in the result
+	     per column.  */
+	  out->add_n_ranges (limit - p, loc_reader);
+
+	  continue;
+	}
+
+      /* Skip leading quote.  */
+      p++;
+      loc_reader.get_next ();
+
+      limit = from[i].text + from[i].len - 1; /* Skip trailing quote.  */
+
+      for (;;)
+	{
+	  base = p;
+	  while (p < limit && *p != '\\')
+	    p++;
+	  if (p > base)
+	    {
+	      /* We have a run of normal characters; these can be fed
+		 directly to convert_cset.  */
+	      out->add_n_ranges (p - base, loc_reader);
+	    }
+	  if (p == limit)
+	    break;
+
+	  p = convert_escape_ranges (pfile, p + 1, limit, &loc_reader, *out);
+	}
+    }
+  return true;
+#else
+  return false;
+#endif
+}
+
 /* Subroutine of do_line and do_linemarker.  Convert escape sequences
    in a string, but do not perform character set conversion.  */
 bool
@@ -1697,7 +2027,7 @@ cppchar_t
 cpp_interpret_charconst (cpp_reader *pfile, const cpp_token *token,
 			 unsigned int *pchars_seen, int *unsignedp)
 {
-  cpp_string str = { 0, 0, {NULL, 0, 0} };
+  cpp_string str = { 0, 0 };
   bool wide = (token->type != CPP_CHAR && token->type != CPP_UTF8CHAR);
   int u8 = 2 * int(token->type == CPP_UTF8CHAR);
   cppchar_t result;
@@ -1812,16 +2142,16 @@ _cpp_convert_input (cpp_reader *pfile, const char *input_charset,
       to.text = input;
       to.asize = size;
       to.len = len;
-      to.loc.init ();
+      //to.loc.init ();
     }
   else
     {
       to.asize = MAX (65536, len);
       to.text = XNEWVEC (uchar, to.asize);
       to.len = 0;
-      to.loc.init ();
+      //to.loc.init ();
 
-      if (!APPLY_CONVERSION (input_cset, input, len, &to, NULL, false))
+      if (!APPLY_CONVERSION (input_cset, input, len, &to))//, NULL, false))
 	cpp_error (pfile, CPP_DL_ERROR,
 		   "failure to convert %s to %s",
 		   CPP_OPTION (pfile, input_charset), SOURCE_CHARSET);
@@ -1918,6 +2248,7 @@ _cpp_default_encoding (void)
    is a cpp_string_location within struct cpp_string, and the latter
    is within union cpp_token_u within struct cpp_token.  */
 
+#if 0
 void
 cpp_string_location::init ()
 {
@@ -2100,33 +2431,14 @@ cpp_string_location::trivial_p () const
       return true;
   return false;
 }
-
-/* Constructor for iterating through the locations in
-   cpp_string_location.  */
-
-cpp_string_location_reader::
-cpp_string_location_reader (const cpp_string_location *strloc,
-			    line_maps *line_table)
-{
-  /* As an optimization, we require that STRLOC must consist of a
-     single fragment.  */
-  linemap_assert (strloc->m_num_fraglocs == 1);
-  m_loc = strloc->m_fragloc_array[0].m_loc;
-  m_cols_per_char = strloc->m_fragloc_array[0].m_cols_per_item;
-  m_bits_per_col = strloc->m_fragloc_array[0].m_bits_per_col;
-  m_line_table = line_table;
-}
+#endif
 
 /* Constructor for iterating through an arbitrary buffer.  */
 
 cpp_string_location_reader::
 cpp_string_location_reader (source_location src_loc,
-			    int cols_per_char,
-			    int bits_per_col,
 			    line_maps *line_table)
-: m_cols_per_char (cols_per_char),
-  m_bits_per_col (bits_per_col),
-  m_line_table (line_table)
+: m_line_table (line_table)
 {
   /* LOC might be a macro location.  It only makes sense to do
      column-by-column calculations on ordinary maps, so get the
@@ -2134,6 +2446,10 @@ cpp_string_location_reader (source_location src_loc,
   m_loc
     = linemap_resolve_location (line_table, src_loc,
 				LRK_SPELLING_LOCATION, NULL);
+
+  const line_map_ordinary *map
+    = linemap_check_ordinary (linemap_lookup (line_table, m_loc));
+  m_offset_per_column = (1 << map->m_range_bits);
 }
 
 /* FIXME.  */
@@ -2142,7 +2458,8 @@ cpp_string_location_reader::get_next ()
 {
   source_range result;
   result.m_start = m_loc;
-  result.m_finish = m_loc + ((m_cols_per_char - 1) << m_bits_per_col);
-  m_loc += m_cols_per_char << m_bits_per_col;
+  result.m_finish = m_loc;
+  if (m_loc <= LINE_MAP_MAX_LOCATION_WITH_COLS)
+    m_loc += m_offset_per_column;
   return result;
 }
diff --git a/libcpp/directives.c b/libcpp/directives.c
index b44a0af..772b835 100644
--- a/libcpp/directives.c
+++ b/libcpp/directives.c
@@ -950,7 +950,7 @@ do_line (cpp_reader *pfile)
   token = cpp_get_token (pfile);
   if (token->type == CPP_STRING)
     {
-      cpp_string s = { 0, 0, { NULL, 0, 0 } };
+      cpp_string s = { 0, 0 };
       if (cpp_interpret_string_notranslate (pfile, &token->val.str, 1,
 					    &s, CPP_STRING))
 	new_file = (const char *)s.text;
@@ -1007,7 +1007,7 @@ do_linemarker (cpp_reader *pfile)
   token = cpp_get_token (pfile);
   if (token->type == CPP_STRING)
     {
-      cpp_string s = { 0, 0, { NULL, 0, 0 } };
+      cpp_string s = { 0, 0 };
       if (cpp_interpret_string_notranslate (pfile, &token->val.str,
 					    1, &s, CPP_STRING))
 	new_file = (const char *)s.text;
diff --git a/libcpp/expr.c b/libcpp/expr.c
index 072af5b..deaa3c5 100644
--- a/libcpp/expr.c
+++ b/libcpp/expr.c
@@ -1228,8 +1228,10 @@ _cpp_parse_expr (cpp_reader *pfile, bool is_if)
 			      "missing binary operator before token \"%s\"",
 			      cpp_token_as_text (pfile, op.token));
 	  want_value = false;
+#if 0
 	  ((cpp_token *)op.token)->val.str.loc.init_raw (op.loc, 1, 1, 1, /* FIXME */
 							 pfile->line_table);
+#endif
 	  top->value = eval_token (pfile, op.token, op.loc);
 	  continue;
 
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index a6b4420..596be39 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -173,6 +173,9 @@ enum c_lang {CLK_GNUC89 = 0, CLK_GNUC99, CLK_GNUC11,
 	     CLK_GNUCXX, CLK_CXX98, CLK_GNUCXX11, CLK_CXX11,
 	     CLK_GNUCXX14, CLK_CXX14, CLK_GNUCXX1Z, CLK_CXX1Z, CLK_ASM};
 
+#if 1
+//extern source_location cpp_get_substring_location (source_location 
+#else
 /* Location of the individual chars in a cpp_string.
    Specifically, this stores a run of characters of len, starting at loc,
    with a consistent number of columns per char.
@@ -298,33 +301,20 @@ struct GTY(()) cpp_string_location {
   unsigned int m_num_fraglocs;
   unsigned int m_alloc_fraglocs;
 };
+#endif
 
 /* A class for iterating through the source-locations within a
-   string, either from a cpp_string_location, or a temporary buffer.  */
+   string.  */
 class cpp_string_location_reader {
  public:
-  /* Constructor for iterating through the locations in
-     cpp_string_location.
-     As an optimization, we require that STRLOC must consist of a
-     single fragment.  */
-  cpp_string_location_reader (const cpp_string_location *strloc,
-			      line_maps *line_table);
-
-  /* Constructor for iterating through an arbitrary buffer.  */
   cpp_string_location_reader (source_location src_loc,
-			      int cols_per_char,
-			      int bits_per_col,
 			      line_maps *line_table);
 
   source_range get_next ();
 
-  int get_bits_per_col () const { return m_bits_per_col; }
-  line_maps *get_line_maps () const { return m_line_table; }
-
  private:
   source_location m_loc;
-  int m_cols_per_char;
-  int m_bits_per_col;
+  int m_offset_per_column;
   line_maps *m_line_table;
 };
 
@@ -332,7 +322,6 @@ class cpp_string_location_reader {
 struct GTY(()) cpp_string {
   unsigned int len;
   const unsigned char *text;
-  cpp_string_location loc;
 };
 
 /* Flags for the cpp_token structure.  */
@@ -899,6 +888,32 @@ struct GTY(()) cpp_hashnode {
   union _cpp_hashnode_value GTY ((desc ("CPP_HASHNODE_VALUE_IDX (%1)"))) value;
 };
 
+/* FIXME.  */
+// FIXME: GTY marker?
+// FIXME: probably not; it's intended to be temporary.
+
+class cpp_substring_ranges
+{
+ public:
+  cpp_substring_ranges ();
+  ~cpp_substring_ranges ();
+
+  int get_num_ranges () const { return m_num_ranges; }
+  source_range get_range (int idx) const
+  {
+    linemap_assert (idx < m_num_ranges);
+    return m_ranges[idx];
+  }
+
+  void add_range (source_range range);
+  void add_n_ranges (int num, cpp_string_location_reader &loc_reader);
+
+ private:
+  source_range *m_ranges;
+  int m_num_ranges;
+  int m_alloc_ranges;
+};
+
 /* Call this first to get a handle to pass to other functions.
 
    If you want cpplib to manage its own hashtable, pass in a NULL
@@ -985,6 +1000,11 @@ extern cppchar_t cpp_interpret_charconst (cpp_reader *, const cpp_token *,
 extern bool cpp_interpret_string (cpp_reader *,
 				  const cpp_string *, size_t,
 				  cpp_string *, enum cpp_ttype);
+extern bool cpp_interpret_string_ranges (cpp_reader *pfile,
+					 const cpp_string *from,
+					 source_location *from_locations,
+					 size_t count,
+					 cpp_substring_ranges *out);
 extern bool cpp_interpret_string_notranslate (cpp_reader *,
 					      const cpp_string *, size_t,
 					      cpp_string *, enum cpp_ttype);
diff --git a/libcpp/internal.h b/libcpp/internal.h
index 0a6b472..d669415 100644
--- a/libcpp/internal.h
+++ b/libcpp/internal.h
@@ -42,8 +42,8 @@ struct op;
 struct _cpp_strbuf;
 
 typedef bool (*convert_f) (iconv_t, const unsigned char *, size_t,
-			   struct _cpp_strbuf *,
-			   cpp_string_location_reader *loc_reader, bool);
+			   struct _cpp_strbuf *);
+//cpp_string_location_reader *loc_reader, bool);
 struct cset_converter
 {
   convert_f func;
diff --git a/libcpp/lex.c b/libcpp/lex.c
index 0bbd952..0e0fdfd 100644
--- a/libcpp/lex.c
+++ b/libcpp/lex.c
@@ -1414,7 +1414,7 @@ lex_number (cpp_reader *pfile, cpp_string *number,
      has union cpp_token_u with
        desc ("cpp_token_val_index (&%1)")))
      and this gives CPP_TOKEN_FLD_STR for numbers (and strings).  */
-  number->loc.init ();
+  //number->loc.init ();
 
   base = pfile->buffer->cur - 1;
   do
@@ -1455,7 +1455,7 @@ create_literal (cpp_reader *pfile, cpp_token *token, const uchar *base,
   token->type = type;
   token->val.str.len = len;
   token->val.str.text = dest;
-  token->val.str.loc.init_raw (token->src_loc, len, 1, 1, pfile->line_table);
+  //token->val.str.loc.init_raw (token->src_loc, len, 1, 1, pfile->line_table);
 }
 
 /* Subroutine of lex_raw_string: Append LEN chars from BASE to the buffer
diff --git a/libcpp/macro.c b/libcpp/macro.c
index 1a6c869..d003ebe 100644
--- a/libcpp/macro.c
+++ b/libcpp/macro.c
@@ -217,7 +217,7 @@ new_string_token (cpp_reader *pfile, unsigned char *text, unsigned int len)
   token->type = CPP_STRING;
   token->val.str.len = len;
   token->val.str.text = text;
-  token->val.str.loc.init ();
+  //token->val.str.loc.init ();
   token->flags = 0;
   return token;
 }
-- 
1.8.5.3

