1 /* Cache of styled source file text
2 Copyright (C) 2018-2019 Free Software Foundation, Inc.
4 This file is part of GDB.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 #include "source-cache.h"
21 #include "gdbsupport/scoped_fd.h"
23 #include "cli/cli-style.h"
25 #include "gdbsupport/selftest.h"
29 #ifdef HAVE_SOURCE_HIGHLIGHT
30 /* If Gnulib redirects 'open' and 'close' to its replacements
31 'rpl_open' and 'rpl_close' via cpp macros, including <fstream>
32 below with those macros in effect will cause unresolved externals
33 when GDB is linked. Happens, e.g., in the MinGW build. */
37 #include <srchilite/sourcehighlight.h>
38 #include <srchilite/langmap.h>
41 /* The number of source files we'll cache. */
45 /* See source-cache.h. */
47 source_cache g_source_cache
;
49 /* See source-cache.h. */
52 source_cache::get_plain_source_lines (struct symtab
*s
,
53 const std::string
&fullname
)
55 scoped_fd
desc (open_source_file (s
));
57 perror_with_name (symtab_to_filename_for_display (s
));
60 if (fstat (desc
.get (), &st
) < 0)
61 perror_with_name (symtab_to_filename_for_display (s
));
64 lines
.resize (st
.st_size
);
65 if (myread (desc
.get (), &lines
[0], lines
.size ()) < 0)
66 perror_with_name (symtab_to_filename_for_display (s
));
69 if (SYMTAB_OBJFILE (s
) != NULL
&& SYMTAB_OBJFILE (s
)->obfd
!= NULL
)
70 mtime
= SYMTAB_OBJFILE (s
)->mtime
;
72 mtime
= exec_bfd_mtime
;
74 if (mtime
&& mtime
< st
.st_mtime
)
75 warning (_("Source file is more recent than executable."));
77 std::vector
<off_t
> offsets
;
78 offsets
.push_back (0);
79 for (size_t offset
= lines
.find ('\n');
80 offset
!= std::string::npos
;
81 offset
= lines
.find ('\n', offset
))
84 /* A newline at the end does not start a new line. It would
85 seem simpler to just strip the newline in this function, but
86 then "list" won't print the final newline. */
87 if (offset
!= lines
.size ())
88 offsets
.push_back (offset
);
91 offsets
.shrink_to_fit ();
92 m_offset_cache
.emplace (fullname
, std::move (offsets
));
97 #ifdef HAVE_SOURCE_HIGHLIGHT
99 /* Return the Source Highlight language name, given a gdb language
100 LANG. Returns NULL if the language is not known. */
103 get_language_name (enum language lang
)
120 case language_fortran
:
121 return "fortran.lang";
124 /* Not handled by Source Highlight. */
130 case language_pascal
:
131 return "pascal.lang";
133 case language_opencl
:
134 /* Not handled by Source Highlight. */
138 /* Not handled by Source Highlight. */
151 #endif /* HAVE_SOURCE_HIGHLIGHT */
153 /* See source-cache.h. */
156 source_cache::ensure (struct symtab
*s
)
158 std::string fullname
= symtab_to_fullname (s
);
160 size_t size
= m_source_map
.size ();
161 for (int i
= 0; i
< size
; ++i
)
163 if (m_source_map
[i
].fullname
== fullname
)
165 /* This should always hold, because we create the file
166 offsets when reading the file, and never free them
167 without also clearing the contents cache. */
168 gdb_assert (m_offset_cache
.find (fullname
)
169 != m_offset_cache
.end ());
170 /* Not strictly LRU, but at least ensure that the most
171 recently used entry is always the last candidate for
172 deletion. Note that this property is relied upon by at
175 std::swap (m_source_map
[i
], m_source_map
[size
- 1]);
180 std::string contents
= get_plain_source_lines (s
, fullname
);
182 #ifdef HAVE_SOURCE_HIGHLIGHT
183 if (source_styling
&& gdb_stdout
->can_emit_style_escape ())
185 const char *lang_name
= get_language_name (SYMTAB_LANGUAGE (s
));
186 if (lang_name
!= nullptr)
188 /* The global source highlight object, or null if one was
189 never constructed. This is stored here rather than in
190 the class so that we don't need to include anything or do
191 conditional compilation in source-cache.h. */
192 static srchilite::SourceHighlight
*highlighter
;
194 if (highlighter
== nullptr)
196 highlighter
= new srchilite::SourceHighlight ("esc.outlang");
197 highlighter
->setStyleFile ("esc.style");
200 std::istringstream
input (contents
);
201 std::ostringstream output
;
202 highlighter
->highlight (input
, output
, lang_name
, fullname
);
204 contents
= output
.str ();
207 #endif /* HAVE_SOURCE_HIGHLIGHT */
209 source_text result
= { std::move (fullname
), std::move (contents
) };
210 m_source_map
.push_back (std::move (result
));
212 if (m_source_map
.size () > MAX_ENTRIES
)
213 m_source_map
.erase (m_source_map
.begin ());
218 /* See source-cache.h. */
221 source_cache::get_line_charpos (struct symtab
*s
,
222 const std::vector
<off_t
> **offsets
)
224 std::string fullname
= symtab_to_fullname (s
);
226 auto iter
= m_offset_cache
.find (fullname
);
227 if (iter
== m_offset_cache
.end ())
230 iter
= m_offset_cache
.find (fullname
);
231 /* cache_source_text ensured this was entered. */
232 gdb_assert (iter
!= m_offset_cache
.end ());
235 *offsets
= &iter
->second
;
239 /* A helper function that extracts the desired source lines from TEXT,
240 putting them into LINES_OUT. The arguments are as for
241 get_source_lines. Returns true on success, false if the line
242 numbers are invalid. */
245 extract_lines (const std::string
&text
, int first_line
, int last_line
,
246 std::string
*lines_out
)
249 std::string::size_type pos
= 0;
250 std::string::size_type first_pos
= std::string::npos
;
252 while (pos
!= std::string::npos
&& lineno
<= last_line
)
254 std::string::size_type new_pos
= text
.find ('\n', pos
);
256 if (lineno
== first_line
)
260 if (lineno
== last_line
|| pos
== std::string::npos
)
262 /* A newline at the end does not start a new line. */
263 if (first_pos
== std::string::npos
264 || first_pos
== text
.size ())
266 if (pos
== std::string::npos
)
270 *lines_out
= text
.substr (first_pos
, pos
- first_pos
);
280 /* See source-cache.h. */
283 source_cache::get_source_lines (struct symtab
*s
, int first_line
,
284 int last_line
, std::string
*lines
)
286 if (first_line
< 1 || last_line
< 1 || first_line
> last_line
)
292 return extract_lines (m_source_map
.back ().contents
,
293 first_line
, last_line
, lines
);
299 static void extract_lines_test ()
301 std::string input_text
= "abc\ndef\nghi\njkl\n";
304 SELF_CHECK (extract_lines (input_text
, 1, 1, &result
)
305 && result
== "abc\n");
306 SELF_CHECK (!extract_lines (input_text
, 2, 1, &result
));
307 SELF_CHECK (extract_lines (input_text
, 1, 2, &result
)
308 && result
== "abc\ndef\n");
309 SELF_CHECK (extract_lines ("abc", 1, 1, &result
)
316 _initialize_source_cache ()
319 selftests::register_test ("source-cache", selftests::extract_lines_test
);