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 "util.h"
23#include "DbeSession.h"
24#include "Experiment.h"
25#include "DbeFile.h"
26#include "ExpGroup.h"
27#include "DbeJarFile.h"
28
29DbeFile::DbeFile (const char *filename)
30{
31  filetype = 0;
32  name = dbe_strdup (filename);
33  name = canonical_path (name);
34  orig_location = NULL;
35  location = NULL;
36  location_info = NULL;
37  jarFile = NULL;
38  container = NULL;
39  need_refind = true;
40  inArchive = false;
41  sbuf.st_atim.tv_sec = 0;
42  experiment = NULL;
43}
44
45DbeFile::~DbeFile ()
46{
47  free (name);
48  free (location);
49  free (orig_location);
50  free (location_info);
51}
52
53void
54DbeFile::set_need_refind (bool val)
55{
56  if (val != need_refind)
57    {
58      free (location_info);
59      location_info = NULL;
60      need_refind = val;
61    }
62}
63
64void
65DbeFile::set_location (const char *filename)
66{
67  free (location);
68  location = NULL;
69  if (filename)
70    {
71      if (strncmp (filename, NTXT ("./"), 2) == 0)
72	filename += 2;
73      location = canonical_path (dbe_strdup (filename));
74    }
75  free (location_info);
76  location_info = NULL;
77  set_need_refind (false);
78}
79
80char *
81DbeFile::get_location_info ()
82{
83  if (location_info == NULL)
84    {
85      char *fnm = get_name ();
86      char *loc = get_location ();
87      Dprintf (DEBUG_DBE_FILE, NTXT ("DbeFile::get_location_info: %s %s\n"),
88	       STR (fnm), STR (loc));
89      if (loc == NULL)
90	{
91	  if (filetype & F_FICTION)
92	    location_info = dbe_strdup (fnm);
93	  else
94	    location_info = dbe_sprintf (GTXT ("%s (not found)"),
95					 get_relative_path (fnm));
96	}
97      else
98	{
99	  char *r_fnm = get_relative_path (fnm);
100	  char *r_loc = get_relative_path (loc);
101	  if (strcmp (r_fnm, r_loc) == 0)
102	    location_info = dbe_strdup (r_fnm);
103	  else
104	    {
105	      char *bname = get_basename (r_fnm);
106	      if (strcmp (bname, r_loc) == 0)  // found in current directory
107		location_info = dbe_strdup (bname);
108	      else
109		location_info = dbe_sprintf (GTXT ("%s (found as %s)"), bname, r_loc);
110	    }
111	}
112    }
113  return location_info;
114}
115
116char *
117DbeFile::getResolvedPath ()
118{
119  if (get_location ())
120    return location;
121  return name;
122}
123
124DbeFile *
125DbeFile::getJarDbeFile (char *fnm, int sym)
126{
127  Dprintf (DEBUG_DBE_FILE, NTXT ("DbeFile::getJarDbeFile: %s fnm='%s' sym=%d\n"),
128	   STR (name), STR (fnm), sym);
129  DbeFile *df = NULL;
130  if (sym)
131    {
132      char *s = strchr (fnm, sym);
133      if (s)
134	{
135	  s = dbe_strndup (fnm, s - fnm);
136	  df = dbeSession->getDbeFile (s, F_JAR_FILE | F_FILE);
137	  free (s);
138	}
139    }
140  if (df == NULL)
141    df = dbeSession->getDbeFile (fnm, F_JAR_FILE | F_FILE);
142  if (df && (df->experiment == NULL))
143    df->experiment = experiment;
144  return df;
145}
146
147char *
148DbeFile::get_location (bool find_needed)
149{
150  Dprintf (DEBUG_DBE_FILE, NTXT ("get_location 0x%x %s\n"), filetype, STR (name));
151  if (!find_needed)
152    return need_refind ? NULL : location;
153  if (location || !need_refind)
154    return location;
155  set_need_refind (false);
156  if ((filetype & F_FICTION) != 0)
157    return NULL;
158  if (filetype == F_DIR_OR_JAR)
159    {
160      find_in_archives (name);
161      if (location)
162	{
163	  filetype |= F_JAR_FILE | F_FILE;
164	  return location;
165	}
166      find_in_pathmap (name);
167      if (location)
168	return location;
169      if (check_access (name) == F_DIRECTORY)
170	{
171	  filetype |= F_DIRECTORY;
172	  set_location (name);
173	  return location;
174	}
175    }
176
177  if ((filetype & F_FILE) != 0)
178    {
179      if (experiment)
180	{
181	  char *fnm = experiment->checkFileInArchive (name, false);
182	  if (fnm)
183	    {
184	      set_location (fnm);
185	      inArchive = true;
186	      sbuf.st_mtime = 0; // Don't check timestamps
187	      free (fnm);
188	      return location;
189	    }
190	  if ((filetype & F_JAVACLASS) != 0)
191	    {
192	      if (orig_location)
193		{
194		  Dprintf (DEBUG_DBE_FILE, NTXT ("DbeFile::get_location:%d name='%s' orig_location='%s'\n"),
195			   (int) __LINE__, name, orig_location);
196		  // Parse a fileName attribute. There are 4 possibilities:
197		  //   file:<Class_Name>
198		  //   file:<name_of_jar_or_zip_file>
199		  //   jar:file:<name_of_jar_or_zip_file>!<Class_Name>
200		  //   zip:<name_of_jar_or_zip_file>!<Class_Name>
201		  DbeFile *jar_df = NULL;
202		  if (strncmp (orig_location, NTXT ("zip:"), 4) == 0)
203		    jar_df = getJarDbeFile (orig_location + 4, '!');
204		  else if (strncmp (orig_location, NTXT ("jar:file:"), 9) == 0)
205		    jar_df = getJarDbeFile (orig_location + 9, '!');
206		  else if (strncmp (orig_location, NTXT ("file:"), 5) == 0
207			   && isJarOrZip (orig_location + 5))
208		    jar_df = getJarDbeFile (orig_location + 5, 0);
209		  if (jar_df)
210		    {
211		      if (find_in_jar_file (name, jar_df->get_jar_file ()))
212			{
213			  Dprintf (DEBUG_DBE_FILE, NTXT ("DbeFile::get_location:%d FOUND name='%s' location='%s' jar='%s'\n"),
214				   (int) __LINE__, name, STR (location), STR (jar_df->get_location ()));
215			  inArchive = jar_df->inArchive;
216			  container = jar_df;
217			  return location;
218			}
219		    }
220		  if (strncmp (orig_location, NTXT ("file:"), 5) == 0
221		      && !isJarOrZip (orig_location + 5))
222		    {
223		      DbeFile *df = new DbeFile (orig_location + 5);
224		      df->filetype = DbeFile::F_FILE;
225		      df->experiment = experiment;
226		      fnm = df->get_location ();
227		      if (fnm)
228			{
229			  set_location (fnm);
230			  inArchive = df->inArchive;
231			  sbuf.st_mtime = df->sbuf.st_mtime;
232			  Dprintf (DEBUG_DBE_FILE, NTXT ("DbeFile::get_location:%d FOUND name='%s' orig_location='%s' location='%s'\n"),
233				   (int) __LINE__, name, orig_location, fnm);
234			  delete df;
235			  return location;
236			}
237		      delete df;
238		    }
239		}
240	      fnm = dbe_sprintf (NTXT ("%s/%s/%s"), experiment->get_expt_name (), SP_DYNAMIC_CLASSES, name);
241	      if (find_file (fnm))
242		{
243		  inArchive = true;
244		  sbuf.st_mtime = 0; // Don't check timestamps
245		  Dprintf (DEBUG_DBE_FILE, NTXT ("DbeFile::get_location:%d FOUND name='%s' location='%s'\n"),
246			   (int) __LINE__, name, fnm);
247		  free (fnm);
248		  return location;
249		}
250	      free (fnm);
251	    }
252	}
253    }
254
255  if (dbeSession->archive_mode)
256    {
257      find_file (name);
258      if (location)
259	return location;
260    }
261
262  bool inPathMap = find_in_pathmap (name);
263  if (location)
264    return location;
265  find_in_setpath (name, dbeSession->get_search_path ());
266  if (location)
267    return location;
268  if ((filetype & (F_JAVACLASS | F_JAVA_SOURCE)) != 0)
269    {
270      find_in_classpath (name, dbeSession->get_classpath ());
271      if (location)
272	return location;
273    }
274  if (!inPathMap)
275    find_file (name);
276  Dprintf (DEBUG_DBE_FILE && (location == NULL),
277	   "DbeFile::get_location:%d NOT FOUND name='%s'\n", __LINE__, name);
278  return location;
279}
280
281int
282DbeFile::check_access (const char *filename)
283{
284  if (filename == NULL)
285    return F_NOT_FOUND;
286  int st = dbe_stat (filename, &sbuf);
287  Dprintf (DEBUG_DBE_FILE, NTXT ("check_access: %d 0x%x %s\n"), st, filetype, filename);
288  if (st == 0)
289    {
290      if (S_ISDIR (sbuf.st_mode))
291	return F_DIRECTORY;
292      else if (S_ISREG (sbuf.st_mode))
293	return F_FILE;
294      return F_UNKNOWN; // Symbolic link or unknown type of file
295    }
296  sbuf.st_atim.tv_sec = 0;
297  sbuf.st_mtime = 0; // Don't check timestamps
298  return F_NOT_FOUND; // File not found
299}
300
301bool
302DbeFile::isJarOrZip (const char *fnm)
303{
304  size_t len = strlen (fnm) - 4;
305  return len > 0 && (strcmp (fnm + len, NTXT (".jar")) == 0
306		     || strcmp (fnm + len, NTXT (".zip")) == 0);
307}
308
309char *
310DbeFile::find_file (const char *filename)
311{
312  switch (check_access (filename))
313    {
314    case F_DIRECTORY:
315      if (filetype == F_DIR_OR_JAR)
316	filetype |= F_DIRECTORY;
317      if ((filetype & F_DIRECTORY) != 0)
318	set_location (filename);
319      break;
320    case F_FILE:
321      if (filetype == F_DIR_OR_JAR)
322	{
323	  filetype |= F_FILE;
324	  if (isJarOrZip (filename))
325	    filetype |= F_JAR_FILE;
326	}
327      if ((filetype & F_DIRECTORY) == 0)
328	set_location (filename);
329      break;
330    }
331  return location;
332}
333
334DbeJarFile *
335DbeFile::get_jar_file ()
336{
337  if (jarFile == NULL)
338    {
339      char *fnm = get_location ();
340      if (fnm)
341	jarFile = dbeSession->get_JarFile (fnm);
342    }
343  return jarFile;
344}
345
346char *
347DbeFile::find_package_name (const char *filename, const char *dirname)
348{
349  char *nm = dbe_sprintf (NTXT ("%s/%s"), dirname, filename);
350  if (!find_in_pathmap (nm))
351    find_file (nm);
352  free (nm);
353  return location;
354}
355
356char *
357DbeFile::find_in_directory (const char *filename, const char *dirname)
358{
359  if (filename && dirname)
360    {
361      char *nm = dbe_sprintf (NTXT ("%s/%s"), dirname, filename);
362      find_file (nm);
363      free (nm);
364    }
365  return location;
366}
367
368char *
369DbeFile::find_in_jar_file (const char *filename, DbeJarFile *jfile)
370{
371  // Read .jar or .zip
372  if (jfile == NULL)
373    return NULL;
374  int entry = jfile->get_entry (filename);
375  if (entry >= 0)
376    {
377      char *fnm = dbeSession->get_tmp_file_name (filename, true);
378      long long fsize = jfile->copy (fnm, entry);
379      if (fsize >= 0)
380	{
381	  dbeSession->tmp_files->append (fnm);
382	  set_location (fnm);
383	  sbuf.st_size = fsize;
384	  sbuf.st_mtime = 0; // Don't check timestamps
385	  fnm = NULL;
386	}
387      free (fnm);
388    }
389  return location;
390}
391
392bool
393DbeFile::find_in_pathmap (char *filename)
394{
395  Vector<pathmap_t*> *pathmaps = dbeSession->get_pathmaps ();
396  bool inPathMap = false;
397  if (strncmp (filename, NTXT ("./"), 2) == 0)
398    filename += 2;
399  for (int i = 0, sz = pathmaps ? pathmaps->size () : 0; i < sz; i++)
400    {
401      pathmap_t *pmp = pathmaps->fetch (i);
402      size_t len = strlen (pmp->old_prefix);
403      if (strncmp (pmp->old_prefix, filename, len) == 0
404	  && (filename[len] == '/' || filename[len] == '\0'))
405	{
406	  inPathMap = true;
407	  if (find_in_directory (filename + len, pmp->new_prefix))
408	    {
409	      return inPathMap;
410	    }
411	}
412    }
413  return inPathMap;
414}
415
416void
417DbeFile::find_in_archives (char *filename)
418{
419  for (int i1 = 0, sz1 = dbeSession->expGroups->size (); i1 < sz1; i1++)
420    {
421      ExpGroup *gr = dbeSession->expGroups->fetch (i1);
422      if (gr->founder)
423	{
424	  char *nm = gr->founder->checkFileInArchive (filename, false);
425	  if (nm)
426	    {
427	      find_file (nm);
428	      if (location)
429		{
430		  sbuf.st_mtime = 0; // Don't check timestamps
431		  return;
432		}
433	    }
434	}
435    }
436}
437
438void
439DbeFile::find_in_setpath (char *filename, Vector<char*> *searchPath)
440{
441  char *base = get_basename (filename);
442  for (int i = 0, sz = searchPath ? searchPath->size () : 0; i < sz; i++)
443    {
444      char *spath = searchPath->fetch (i);
445      // Check file in each experiment directory
446      if (streq (spath, "$") || streq (spath, NTXT ("$expts")))
447	{
448	  // find only in founders and only LoadObj.
449	  for (int i1 = 0, sz1 = dbeSession->expGroups->size (); i1 < sz1; i1++)
450	    {
451	      ExpGroup *gr = dbeSession->expGroups->fetch (i1);
452	      char *exp_name = gr->founder->get_expt_name ();
453	      if (gr->founder)
454		{
455		  if ((filetype & (F_JAVACLASS | F_JAVA_SOURCE)) != 0)
456		    {
457		      // Find with the package name
458		      if (find_in_directory (filename, exp_name))
459			 return;
460		    }
461		  if (find_in_directory (base, exp_name))
462		    return;
463		}
464	    }
465	  continue;
466	}
467      DbeFile *df = dbeSession->getDbeFile (spath, DbeFile::F_DIR_OR_JAR);
468      if (df->get_location () == NULL)
469	continue;
470      if ((filetype & (F_JAVACLASS | F_JAVA_SOURCE)) != 0)
471	{
472	  if ((df->filetype & F_JAR_FILE) != 0)
473	    {
474	      if (find_in_jar_file (filename, df->get_jar_file ()))
475		{
476		  container = df;
477		  return;
478		}
479	      continue;
480	    }
481	  else if ((df->filetype & F_DIRECTORY) != 0)
482	    // Find with the package name
483	    if (find_package_name (filename, spath))
484	      return;
485	}
486      if ((df->filetype & F_DIRECTORY) != 0)
487	if (find_in_directory (base, df->get_location ()))
488	  return;
489    }
490}
491
492void
493DbeFile::find_in_classpath (char *filename, Vector<DbeFile*> *classPath)
494{
495  for (int i = 0, sz = classPath ? classPath->size () : 0; i < sz; i++)
496    {
497      DbeFile *df = classPath->fetch (i);
498      if (df->get_location () == NULL)
499	continue;
500      if ((df->filetype & F_JAR_FILE) != 0)
501	{
502	  if (find_in_jar_file (filename, df->get_jar_file ()))
503	    {
504	      container = df;
505	      return;
506	    }
507	}
508      else if ((df->filetype & F_DIRECTORY) != 0)
509	// Find with the package name
510	if (find_package_name (filename, df->get_name ()))
511	  return;
512    }
513}
514
515struct stat64 *
516DbeFile::get_stat ()
517{
518  if (sbuf.st_atim.tv_sec == 0)
519    {
520      int st = check_access (get_location (false));
521      if (st == F_NOT_FOUND)
522	return NULL;
523    }
524  return &sbuf;
525}
526
527bool
528DbeFile::compare (DbeFile *df)
529{
530  if (df == NULL)
531    return false;
532  struct stat64 *st1 = get_stat ();
533  struct stat64 *st2 = df->get_stat ();
534  if (st1 == NULL || st2 == NULL)
535    return false;
536  if (st1->st_size != st2->st_size)
537    return false;
538  if (st1->st_mtim.tv_sec != st2->st_mtim.tv_sec)
539    return false;
540  return true;
541}
542