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