Update copyright year range in all GDB files.
[deliverable/binutils-gdb.git] / gdb / source-cache.c
CommitLineData
62f29fda 1/* Cache of styled source file text
b811d2c2 2 Copyright (C) 2018-2020 Free Software Foundation, Inc.
62f29fda
TT
3
4 This file is part of GDB.
5
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.
10
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.
15
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/>. */
18
19#include "defs.h"
20#include "source-cache.h"
268a13a5 21#include "gdbsupport/scoped_fd.h"
62f29fda
TT
22#include "source.h"
23#include "cli/cli-style.h"
0d12e84c 24#include "symtab.h"
269249d9 25#include "gdbsupport/selftest.h"
cb44333d
TT
26#include "objfiles.h"
27#include "exec.h"
62f29fda
TT
28
29#ifdef HAVE_SOURCE_HIGHLIGHT
3a350822
EZ
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. */
34#undef open
35#undef close
62f29fda
TT
36#include <sstream>
37#include <srchilite/sourcehighlight.h>
38#include <srchilite/langmap.h>
39#endif
40
41/* The number of source files we'll cache. */
42
43#define MAX_ENTRIES 5
44
45/* See source-cache.h. */
46
47source_cache g_source_cache;
48
49/* See source-cache.h. */
50
cb44333d
TT
51std::string
52source_cache::get_plain_source_lines (struct symtab *s,
53 const std::string &fullname)
62f29fda 54{
cb44333d 55 scoped_fd desc (open_source_file (s));
62f29fda 56 if (desc.get () < 0)
cb44333d 57 perror_with_name (symtab_to_filename_for_display (s));
62f29fda 58
872dceaa 59 struct stat st;
872dceaa 60 if (fstat (desc.get (), &st) < 0)
62f29fda
TT
61 perror_with_name (symtab_to_filename_for_display (s));
62
cb44333d
TT
63 std::string lines;
64 lines.resize (st.st_size);
65 if (myread (desc.get (), &lines[0], lines.size ()) < 0)
62f29fda
TT
66 perror_with_name (symtab_to_filename_for_display (s));
67
cb44333d
TT
68 time_t mtime = 0;
69 if (SYMTAB_OBJFILE (s) != NULL && SYMTAB_OBJFILE (s)->obfd != NULL)
70 mtime = SYMTAB_OBJFILE (s)->mtime;
71 else if (exec_bfd)
72 mtime = exec_bfd_mtime;
62f29fda 73
cb44333d
TT
74 if (mtime && mtime < st.st_mtime)
75 warning (_("Source file is more recent than executable."));
62f29fda 76
cb44333d
TT
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))
62f29fda 82 {
cb44333d
TT
83 ++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);
62f29fda
TT
89 }
90
cb44333d
TT
91 offsets.shrink_to_fit ();
92 m_offset_cache.emplace (fullname, std::move (offsets));
93
94 return lines;
62f29fda
TT
95}
96
64c45143
TT
97#ifdef HAVE_SOURCE_HIGHLIGHT
98
62f29fda
TT
99/* Return the Source Highlight language name, given a gdb language
100 LANG. Returns NULL if the language is not known. */
101
102static const char *
103get_language_name (enum language lang)
104{
105 switch (lang)
106 {
107 case language_c:
108 case language_objc:
109 return "c.lang";
110
111 case language_cplus:
112 return "cpp.lang";
113
114 case language_d:
115 return "d.lang";
116
117 case language_go:
118 return "go.lang";
119
120 case language_fortran:
121 return "fortran.lang";
122
123 case language_m2:
124 /* Not handled by Source Highlight. */
125 break;
126
127 case language_asm:
128 return "asm.lang";
129
130 case language_pascal:
131 return "pascal.lang";
132
133 case language_opencl:
134 /* Not handled by Source Highlight. */
135 break;
136
137 case language_rust:
d806ea2d 138 return "rust.lang";
62f29fda
TT
139
140 case language_ada:
141 return "ada.lang";
142
143 default:
144 break;
145 }
146
147 return nullptr;
148}
149
64c45143
TT
150#endif /* HAVE_SOURCE_HIGHLIGHT */
151
62f29fda
TT
152/* See source-cache.h. */
153
154bool
cb44333d 155source_cache::ensure (struct symtab *s)
62f29fda 156{
872dceaa 157 std::string fullname = symtab_to_fullname (s);
62f29fda 158
cb44333d
TT
159 size_t size = m_source_map.size ();
160 for (int i = 0; i < size; ++i)
872dceaa 161 {
cb44333d 162 if (m_source_map[i].fullname == fullname)
62f29fda 163 {
cb44333d
TT
164 /* This should always hold, because we create the file
165 offsets when reading the file, and never free them
166 without also clearing the contents cache. */
167 gdb_assert (m_offset_cache.find (fullname)
168 != m_offset_cache.end ());
169 /* Not strictly LRU, but at least ensure that the most
170 recently used entry is always the last candidate for
171 deletion. Note that this property is relied upon by at
172 least one caller. */
173 if (i != size - 1)
174 std::swap (m_source_map[i], m_source_map[size - 1]);
872dceaa 175 return true;
62f29fda 176 }
872dceaa 177 }
62f29fda 178
cb44333d 179 std::string contents = get_plain_source_lines (s, fullname);
872dceaa
TT
180
181#ifdef HAVE_SOURCE_HIGHLIGHT
182 if (source_styling && gdb_stdout->can_emit_style_escape ())
183 {
62f29fda
TT
184 const char *lang_name = get_language_name (SYMTAB_LANGUAGE (s));
185 if (lang_name != nullptr)
186 {
872dceaa
TT
187 /* The global source highlight object, or null if one was
188 never constructed. This is stored here rather than in
189 the class so that we don't need to include anything or do
190 conditional compilation in source-cache.h. */
191 static srchilite::SourceHighlight *highlighter;
192
d806ea2d
TT
193 try
194 {
f64e2f40
TV
195 if (highlighter == nullptr)
196 {
197 highlighter = new srchilite::SourceHighlight ("esc.outlang");
198 highlighter->setStyleFile ("esc.style");
199 }
200
d806ea2d
TT
201 std::istringstream input (contents);
202 std::ostringstream output;
203 highlighter->highlight (input, output, lang_name, fullname);
204 contents = output.str ();
205 }
206 catch (...)
207 {
208 /* Source Highlight will throw an exception if
209 highlighting fails. One possible reason it can fail
210 is if the language is unknown -- which matters to gdb
211 because Rust support wasn't added until after 3.1.8.
212 Ignore exceptions here and fall back to
213 un-highlighted text. */
214 }
62f29fda
TT
215 }
216 }
217#endif /* HAVE_SOURCE_HIGHLIGHT */
218
872dceaa
TT
219 source_text result = { std::move (fullname), std::move (contents) };
220 m_source_map.push_back (std::move (result));
221
222 if (m_source_map.size () > MAX_ENTRIES)
223 m_source_map.erase (m_source_map.begin ());
224
872dceaa 225 return true;
62f29fda 226}
269249d9 227
cb44333d
TT
228/* See source-cache.h. */
229
230bool
231source_cache::get_line_charpos (struct symtab *s,
232 const std::vector<off_t> **offsets)
233{
67f3ed6a
AB
234 try
235 {
236 std::string fullname = symtab_to_fullname (s);
237
238 auto iter = m_offset_cache.find (fullname);
239 if (iter == m_offset_cache.end ())
240 {
241 ensure (s);
242 iter = m_offset_cache.find (fullname);
243 /* cache_source_text ensured this was entered. */
244 gdb_assert (iter != m_offset_cache.end ());
245 }
cb44333d 246
67f3ed6a
AB
247 *offsets = &iter->second;
248 return true;
249 }
250 catch (const gdb_exception_error &e)
cb44333d 251 {
67f3ed6a 252 return false;
cb44333d 253 }
cb44333d
TT
254}
255
256/* A helper function that extracts the desired source lines from TEXT,
257 putting them into LINES_OUT. The arguments are as for
258 get_source_lines. Returns true on success, false if the line
259 numbers are invalid. */
260
261static bool
262extract_lines (const std::string &text, int first_line, int last_line,
263 std::string *lines_out)
264{
265 int lineno = 1;
266 std::string::size_type pos = 0;
267 std::string::size_type first_pos = std::string::npos;
268
269 while (pos != std::string::npos && lineno <= last_line)
270 {
271 std::string::size_type new_pos = text.find ('\n', pos);
272
273 if (lineno == first_line)
274 first_pos = pos;
275
276 pos = new_pos;
277 if (lineno == last_line || pos == std::string::npos)
278 {
279 /* A newline at the end does not start a new line. */
280 if (first_pos == std::string::npos
281 || first_pos == text.size ())
282 return false;
283 if (pos == std::string::npos)
284 pos = text.size ();
285 else
286 ++pos;
287 *lines_out = text.substr (first_pos, pos - first_pos);
288 return true;
289 }
290 ++lineno;
291 ++pos;
292 }
293
294 return false;
295}
296
297/* See source-cache.h. */
298
299bool
300source_cache::get_source_lines (struct symtab *s, int first_line,
301 int last_line, std::string *lines)
302{
303 if (first_line < 1 || last_line < 1 || first_line > last_line)
304 return false;
305
306 if (!ensure (s))
307 return false;
308
309 return extract_lines (m_source_map.back ().contents,
310 first_line, last_line, lines);
311}
312
269249d9
TT
313#if GDB_SELF_TEST
314namespace selftests
315{
316static void extract_lines_test ()
317{
318 std::string input_text = "abc\ndef\nghi\njkl\n";
cb44333d
TT
319 std::string result;
320
321 SELF_CHECK (extract_lines (input_text, 1, 1, &result)
322 && result == "abc\n");
323 SELF_CHECK (!extract_lines (input_text, 2, 1, &result));
324 SELF_CHECK (extract_lines (input_text, 1, 2, &result)
325 && result == "abc\ndef\n");
326 SELF_CHECK (extract_lines ("abc", 1, 1, &result)
327 && result == "abc");
269249d9
TT
328}
329}
330#endif
331
332void
333_initialize_source_cache ()
334{
335#if GDB_SELF_TEST
336 selftests::register_test ("source-cache", selftests::extract_lines_test);
337#endif
338}
This page took 0.163937 seconds and 4 git commands to generate.