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