Fix latent bug in source cache
[deliverable/binutils-gdb.git] / gdb / source-cache.c
CommitLineData
62f29fda 1/* Cache of styled source file text
42a4f53d 2 Copyright (C) 2018-2019 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"
62f29fda
TT
26
27#ifdef HAVE_SOURCE_HIGHLIGHT
3a350822
EZ
28/* If Gnulib redirects 'open' and 'close' to its replacements
29 'rpl_open' and 'rpl_close' via cpp macros, including <fstream>
30 below with those macros in effect will cause unresolved externals
31 when GDB is linked. Happens, e.g., in the MinGW build. */
32#undef open
33#undef close
62f29fda
TT
34#include <fstream>
35#include <sstream>
36#include <srchilite/sourcehighlight.h>
37#include <srchilite/langmap.h>
38#endif
39
40/* The number of source files we'll cache. */
41
42#define MAX_ENTRIES 5
43
44/* See source-cache.h. */
45
46source_cache g_source_cache;
47
48/* See source-cache.h. */
49
50bool
51source_cache::get_plain_source_lines (struct symtab *s, int first_line,
52 int last_line, std::string *lines)
53{
00df30ae 54 scoped_fd desc (open_source_file_with_line_charpos (s));
62f29fda
TT
55 if (desc.get () < 0)
56 return false;
57
62f29fda
TT
58 if (first_line < 1 || first_line > s->nlines || last_line < 1)
59 return false;
60
61 if (lseek (desc.get (), s->line_charpos[first_line - 1], SEEK_SET) < 0)
62 perror_with_name (symtab_to_filename_for_display (s));
63
64 int last_charpos;
65 if (last_line >= s->nlines)
66 {
67 struct stat st;
68
69 if (fstat (desc.get (), &st) < 0)
70 perror_with_name (symtab_to_filename_for_display (s));
71 /* We could cache this in line_charpos... */
72 last_charpos = st.st_size;
73 }
74 else
75 last_charpos = s->line_charpos[last_line];
76
77 lines->resize (last_charpos - s->line_charpos[first_line - 1]);
78 if (myread (desc.get (), &(*lines)[0], lines->size ()) < 0)
79 perror_with_name (symtab_to_filename_for_display (s));
80
81 return true;
82}
83
269249d9
TT
84/* A helper function for get_plain_source_lines that extracts the
85 desired source lines from TEXT, putting them into LINES_OUT. The
86 arguments are as for get_source_lines. The return value is the
87 desired lines. */
88static std::string
89extract_lines (const std::string &text, int first_line, int last_line)
62f29fda
TT
90{
91 int lineno = 1;
92 std::string::size_type pos = 0;
93 std::string::size_type first_pos = std::string::npos;
94
95 while (pos != std::string::npos && lineno <= last_line)
96 {
269249d9 97 std::string::size_type new_pos = text.find ('\n', pos);
62f29fda
TT
98
99 if (lineno == first_line)
100 first_pos = pos;
101
102 pos = new_pos;
103 if (lineno == last_line || pos == std::string::npos)
104 {
3b336828
TT
105 if (first_pos == std::string::npos)
106 return {};
62f29fda 107 if (pos == std::string::npos)
269249d9
TT
108 pos = text.size ();
109 else
110 ++pos;
111 return text.substr (first_pos, pos - first_pos);
62f29fda
TT
112 }
113 ++lineno;
114 ++pos;
115 }
116
3b336828 117 return {};
62f29fda
TT
118}
119
64c45143
TT
120#ifdef HAVE_SOURCE_HIGHLIGHT
121
62f29fda
TT
122/* Return the Source Highlight language name, given a gdb language
123 LANG. Returns NULL if the language is not known. */
124
125static const char *
126get_language_name (enum language lang)
127{
128 switch (lang)
129 {
130 case language_c:
131 case language_objc:
132 return "c.lang";
133
134 case language_cplus:
135 return "cpp.lang";
136
137 case language_d:
138 return "d.lang";
139
140 case language_go:
141 return "go.lang";
142
143 case language_fortran:
144 return "fortran.lang";
145
146 case language_m2:
147 /* Not handled by Source Highlight. */
148 break;
149
150 case language_asm:
151 return "asm.lang";
152
153 case language_pascal:
154 return "pascal.lang";
155
156 case language_opencl:
157 /* Not handled by Source Highlight. */
158 break;
159
160 case language_rust:
161 /* Not handled by Source Highlight. */
162 break;
163
164 case language_ada:
165 return "ada.lang";
166
167 default:
168 break;
169 }
170
171 return nullptr;
172}
173
64c45143
TT
174#endif /* HAVE_SOURCE_HIGHLIGHT */
175
62f29fda
TT
176/* See source-cache.h. */
177
178bool
179source_cache::get_source_lines (struct symtab *s, int first_line,
180 int last_line, std::string *lines)
181{
182 if (first_line < 1 || last_line < 1 || first_line > last_line)
183 return false;
184
185#ifdef HAVE_SOURCE_HIGHLIGHT
8a522c6c 186 if (source_styling && gdb_stdout->can_emit_style_escape ())
62f29fda
TT
187 {
188 const char *fullname = symtab_to_fullname (s);
189
190 for (const auto &item : m_source_map)
191 {
192 if (item.fullname == fullname)
3b336828 193 {
269249d9 194 *lines = extract_lines (item.contents, first_line, last_line);
3b336828
TT
195 return true;
196 }
62f29fda
TT
197 }
198
199 const char *lang_name = get_language_name (SYMTAB_LANGUAGE (s));
200 if (lang_name != nullptr)
201 {
202 std::ifstream input (fullname);
203 if (input.is_open ())
204 {
dcf37923
TT
205 /* The global source highlight object, or null if one
206 was never constructed. This is stored here rather
207 than in the class so that we don't need to include
208 anything or do conditional compilation in
209 source-cache.h. */
210 static srchilite::SourceHighlight *highlighter;
211
ab42892f
EZ
212 if (s->line_charpos == 0)
213 {
00df30ae 214 scoped_fd desc (open_source_file_with_line_charpos (s));
ab42892f
EZ
215 if (desc.get () < 0)
216 return false;
068ef30e
SM
217
218 /* FULLNAME points to a value owned by the symtab
219 (symtab::fullname). Calling open_source_file reallocates
220 that value, so we must refresh FULLNAME to avoid a
221 use-after-free. */
222 fullname = symtab_to_fullname (s);
ab42892f 223 }
dcf37923
TT
224
225 if (highlighter == nullptr)
226 {
227 highlighter = new srchilite::SourceHighlight ("esc.outlang");
228 highlighter->setStyleFile ("esc.style");
229 }
62f29fda
TT
230
231 std::ostringstream output;
dcf37923 232 highlighter->highlight (input, output, lang_name, fullname);
62f29fda
TT
233
234 source_text result = { fullname, output.str () };
235 m_source_map.push_back (std::move (result));
236
237 if (m_source_map.size () > MAX_ENTRIES)
238 m_source_map.erase (m_source_map.begin ());
239
269249d9
TT
240 *lines = extract_lines (m_source_map.back ().contents,
241 first_line, last_line);
3b336828 242 return true;
62f29fda
TT
243 }
244 }
245 }
246#endif /* HAVE_SOURCE_HIGHLIGHT */
247
248 return get_plain_source_lines (s, first_line, last_line, lines);
249}
269249d9
TT
250
251#if GDB_SELF_TEST
252namespace selftests
253{
254static void extract_lines_test ()
255{
256 std::string input_text = "abc\ndef\nghi\njkl\n";
257
258 SELF_CHECK (extract_lines (input_text, 1, 1) == "abc\n");
259 SELF_CHECK (extract_lines (input_text, 2, 1) == "");
260 SELF_CHECK (extract_lines (input_text, 1, 2) == "abc\ndef\n");
261 SELF_CHECK (extract_lines ("abc", 1, 1) == "abc");
262}
263}
264#endif
265
266void
267_initialize_source_cache ()
268{
269#if GDB_SELF_TEST
270 selftests::register_test ("source-cache", selftests::extract_lines_test);
271#endif
272}
This page took 0.107182 seconds and 4 git commands to generate.