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 (s)); 53 if (desc.get () < 0) 54 return false; 55 56 if (s->line_charpos == 0) 57 find_source_lines (s, desc.get ()); 58 59 if (first_line < 1 || first_line > s->nlines || last_line < 1) 60 return false; 61 62 if (lseek (desc.get (), s->line_charpos[first_line - 1], SEEK_SET) < 0) 63 perror_with_name (symtab_to_filename_for_display (s)); 64 65 int last_charpos; 66 if (last_line >= s->nlines) 67 { 68 struct stat st; 69 70 if (fstat (desc.get (), &st) < 0) 71 perror_with_name (symtab_to_filename_for_display (s)); 72 /* We could cache this in line_charpos... */ 73 last_charpos = st.st_size; 74 } 75 else 76 last_charpos = s->line_charpos[last_line]; 77 78 lines->resize (last_charpos - s->line_charpos[first_line - 1]); 79 if (myread (desc.get (), &(*lines)[0], lines->size ()) < 0) 80 perror_with_name (symtab_to_filename_for_display (s)); 81 82 return true; 83 } 84 85 /* See source-cache.h. */ 86 87 std::string 88 source_cache::extract_lines (const struct source_text &text, int first_line, 89 int last_line) 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 { 97 std::string::size_type new_pos = text.contents.find ('\n', pos); 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 { 105 if (first_pos == std::string::npos) 106 return {}; 107 if (pos == std::string::npos) 108 pos = text.contents.size (); 109 return text.contents.substr (first_pos, pos - first_pos); 110 } 111 ++lineno; 112 ++pos; 113 } 114 115 return {}; 116 } 117 118 #ifdef HAVE_SOURCE_HIGHLIGHT 119 120 /* Return the Source Highlight language name, given a gdb language 121 LANG. Returns NULL if the language is not known. */ 122 123 static const char * 124 get_language_name (enum language lang) 125 { 126 switch (lang) 127 { 128 case language_c: 129 case language_objc: 130 return "c.lang"; 131 132 case language_cplus: 133 return "cpp.lang"; 134 135 case language_d: 136 return "d.lang"; 137 138 case language_go: 139 return "go.lang"; 140 141 case language_fortran: 142 return "fortran.lang"; 143 144 case language_m2: 145 /* Not handled by Source Highlight. */ 146 break; 147 148 case language_asm: 149 return "asm.lang"; 150 151 case language_pascal: 152 return "pascal.lang"; 153 154 case language_opencl: 155 /* Not handled by Source Highlight. */ 156 break; 157 158 case language_rust: 159 /* Not handled by Source Highlight. */ 160 break; 161 162 case language_ada: 163 return "ada.lang"; 164 165 default: 166 break; 167 } 168 169 return nullptr; 170 } 171 172 #endif /* HAVE_SOURCE_HIGHLIGHT */ 173 174 /* See source-cache.h. */ 175 176 bool 177 source_cache::get_source_lines (struct symtab *s, int first_line, 178 int last_line, std::string *lines) 179 { 180 if (first_line < 1 || last_line < 1 || first_line > last_line) 181 return false; 182 183 #ifdef HAVE_SOURCE_HIGHLIGHT 184 if (source_styling && can_emit_style_escape (gdb_stdout)) 185 { 186 const char *fullname = symtab_to_fullname (s); 187 188 for (const auto &item : m_source_map) 189 { 190 if (item.fullname == fullname) 191 { 192 *lines = extract_lines (item, first_line, last_line); 193 return true; 194 } 195 } 196 197 const char *lang_name = get_language_name (SYMTAB_LANGUAGE (s)); 198 if (lang_name != nullptr) 199 { 200 std::ifstream input (fullname); 201 if (input.is_open ()) 202 { 203 if (s->line_charpos == 0) 204 { 205 scoped_fd desc = open_source_file (s); 206 if (desc.get () < 0) 207 return false; 208 find_source_lines (s, desc.get ()); 209 210 /* FULLNAME points to a value owned by the symtab 211 (symtab::fullname). Calling open_source_file reallocates 212 that value, so we must refresh FULLNAME to avoid a 213 use-after-free. */ 214 fullname = symtab_to_fullname (s); 215 } 216 srchilite::SourceHighlight highlighter ("esc.outlang"); 217 highlighter.setStyleFile("esc.style"); 218 219 std::ostringstream output; 220 highlighter.highlight (input, output, lang_name, fullname); 221 222 source_text result = { fullname, output.str () }; 223 m_source_map.push_back (std::move (result)); 224 225 if (m_source_map.size () > MAX_ENTRIES) 226 m_source_map.erase (m_source_map.begin ()); 227 228 *lines = extract_lines (m_source_map.back (), first_line, 229 last_line); 230 return true; 231 } 232 } 233 } 234 #endif /* HAVE_SOURCE_HIGHLIGHT */ 235 236 return get_plain_source_lines (s, first_line, last_line, lines); 237 } 238