1/* Copyright (C) 2021 Free Software Foundation, Inc.
2   Contributed by Oracle.
3
4   This file is part of GNU Binutils.
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, or (at your option)
9   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, write to the Free Software
18   Foundation, 51 Franklin Street - Fifth Floor, Boston,
19   MA 02110-1301, USA.  */
20
21#include "config.h"
22#include <unistd.h>
23
24#include "util.h"
25#include "DbeSession.h"
26#include "Function.h"
27#include "SourceFile.h"
28#include "DefaultMap.h"
29#include "DbeFile.h"
30#include "LoadObject.h"
31#include "Module.h"
32
33int SourceFile::curId = 0;
34
35SourceFile::SourceFile (const char *file_name)
36{
37  status = OS_NOTREAD;
38  srcLines = NULL;
39  srcInode = -1;
40  lines = NULL;
41  dbeLines = NULL;
42  functions = new DefaultMap<Function *, Function *>();
43  dbeFile = new DbeFile (file_name);
44  dbeFile->filetype |= DbeFile::F_SOURCE | DbeFile::F_FILE;
45  set_name ((char *) file_name);
46  srcMTime = (time_t) 0;
47  isTmpFile = false;
48  flags = 0;
49  read_stabs = false;
50  id = (uint64_t) ((Histable::SOURCEFILE << 24) + curId) << 32;
51  curId++;
52}
53
54SourceFile::~SourceFile ()
55{
56  destroy_map (DbeLine *, dbeLines);
57  delete functions;
58  delete dbeFile;
59  if (lines)
60    {
61      lines->destroy ();
62      delete lines;
63    }
64  if (srcLines)
65    {
66      free (srcLines->get (0));
67      delete srcLines;
68    }
69  if (isTmpFile)
70    unlink (name);
71}
72
73void
74SourceFile::set_name (char* _name)
75{
76  name = dbe_strdup (_name);
77}
78
79char*
80SourceFile::get_name (NameFormat)
81{
82  return name;
83}
84
85bool
86SourceFile::readSource ()
87{
88  if (srcLines)
89    return true;
90  status = OS_NOSRC;
91  char *location = dbeFile->get_location ();
92  if (location == NULL)
93    return false;
94  if (!isTmpFile)
95    srcMTime = dbeFile->sbuf.st_mtime;
96  srcInode = dbeFile->sbuf.st_ino;
97  size_t srcLen = dbeFile->sbuf.st_size;
98  int fd = open64 (location, O_RDONLY);
99  if (fd == -1)
100    {
101      status = OS_NOSRC;
102      return false;
103    }
104  char *srcMap = (char *) malloc (srcLen + 1);
105  int64_t sz = read_from_file (fd, srcMap, srcLen);
106  if (sz != (int64_t) srcLen)
107    append_msg (CMSG_ERROR, GTXT ("%s: Can read only %lld bytes instead %lld"),
108		location, (long long) sz, (long long) srcLen);
109  srcMap[sz] = 0;
110  close (fd);
111
112  // Count the number of lines in the file, converting <nl> to zero
113  srcLines = new Vector<char*>();
114  srcLines->append (srcMap);
115  for (int64_t i = 0; i < sz; i++)
116    {
117      if (srcMap[i] == '\r')
118	{ // Window style
119	  srcMap[i] = 0;
120	  if (i + 1 < sz && srcMap[i + 1] != '\n')
121	    srcLines->append (srcMap + i + 1);
122	}
123      else if (srcMap[i] == '\n')
124	{
125	  srcMap[i] = '\0';
126	  if (i + 1 < sz)
127	    srcLines->append (srcMap + i + 1);
128	}
129    }
130  if (dbeLines)
131    {
132      Vector<DbeLine *> *v = dbeLines->values ();
133      for (long i = 0, sz1 = v ? v->size () : 0; i < sz1; i++)
134	{
135	  DbeLine *p = v->get (i);
136	  if (p->lineno >= srcLines->size ())
137	    append_msg (CMSG_ERROR, GTXT ("Wrong line number %d. '%s' has only %d lines"),
138			p->lineno, dbeFile->get_location (), srcLines->size ());
139	}
140      delete v;
141    }
142  status = OS_OK;
143  return true;
144}
145
146char *
147SourceFile::getLine (int lineno)
148{
149  assert (srcLines != NULL);
150  if (lineno > 0 && lineno <= srcLines->size ())
151    return srcLines->get (lineno - 1);
152  return NTXT ("");
153}
154
155DbeLine *
156SourceFile::find_dbeline (Function *func, int lineno)
157{
158  if (lineno < 0 || (lineno == 0 && func == NULL))
159    return NULL;
160  DbeLine *dbeLine = NULL;
161  if (lines)
162    { // the source is available
163      if (lineno > lines->size ())
164	{
165	  if (dbeLines)
166	    dbeLine = dbeLines->get (lineno);
167	  if (dbeLine == NULL)
168	    append_msg (CMSG_ERROR,
169			GTXT ("Wrong line number %d. '%s' has only %d lines"),
170			lineno, dbeFile->get_location (), lines->size ());
171	}
172      else
173	{
174	  dbeLine = lines->fetch (lineno);
175	  if (dbeLine == NULL)
176	    {
177	      dbeLine = new DbeLine (NULL, this, lineno);
178	      lines->store (lineno, dbeLine);
179	    }
180	}
181    }
182  if (dbeLine == NULL)
183    { // the source is not yet read or lineno is wrong
184      if (dbeLines == NULL)
185	dbeLines = new DefaultMap<int, DbeLine *>();
186      dbeLine = dbeLines->get (lineno);
187      if (dbeLine == NULL)
188	{
189	  dbeLine = new DbeLine (NULL, this, lineno);
190	  dbeLines->put (lineno, dbeLine);
191	}
192    }
193
194  for (DbeLine *last = dbeLine;; last = last->dbeline_func_next)
195    {
196      if (last->func == func)
197	return last;
198      if (last->dbeline_func_next == NULL)
199	{
200	  DbeLine *dl = new DbeLine (func, this, lineno);
201	  if (functions->get (func) == NULL)
202	    functions->put (func, func);
203	  last->dbeline_func_next = dl;
204	  dl->dbeline_base = dbeLine;
205	  return dl;
206	}
207    }
208}
209
210Vector<Function *> *
211SourceFile::get_functions ()
212{
213  if (!read_stabs)
214    {
215      // Create all DbeLines for this Source
216      read_stabs = true;
217      Vector<LoadObject *> *lobjs = dbeSession->get_LoadObjects ();
218      for (long i = 0, sz = VecSize (lobjs); i < sz; i++)
219	{
220	  LoadObject *lo = lobjs->get (i);
221	  for (long i1 = 0, sz1 = VecSize (lo->seg_modules); i1 < sz1; i1++)
222	    {
223	      Module *mod = lo->seg_modules->get (i1);
224	      mod->read_stabs ();
225	    }
226	}
227    }
228  return functions->keySet ();
229}
230