Rename common to gdbsupport
[external/binutils.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 "gdbsupport/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 }