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 <stdlib.h>
23#include <strings.h>
24#include <sys/param.h>
25#include <sys/stat.h>
26#include <unistd.h>
27
28#include "Application.h"
29#include "Settings.h"
30#include "i18n.h"
31#include "util.h"
32
33Application::ProgressFunc Application::progress_func = NULL;
34Application *theApplication;
35
36Application::Application (int argc, char *argv[], char *fdhome)
37{
38  theApplication = this;
39  cur_dir = NULL;
40  prog_version = dbe_strdup (VERSION);
41  set_name (strchr (argv[0], '/') ? argv[0] : NULL);
42  whoami = get_basename (get_name ());
43
44  // set up a queue for comments
45  commentq = new Emsgqueue (NTXT ("app_commentq"));
46
47  // Locate where the binaries are installed
48  set_run_dir (fdhome);
49
50  // Initialize I18N
51  init_locale (run_dir);
52
53  // Initialize licensing data
54  lic_found = 0;
55  lic_err = NULL;
56
57  // Initialize worker threads
58  number_of_worker_threads = 1;
59#if DEBUG
60  char *use_worker_threads = getenv (NTXT ("SP_USE_WORKER_THREADS"));
61  if ((NULL != use_worker_threads) && (0 == strcasecmp (use_worker_threads, NTXT ("no"))))
62    {
63      number_of_worker_threads = 0;
64    }
65#endif /* DEBUG */
66  settings = new Settings (this);
67}
68
69Application::~Application ()
70{
71  delete commentq;
72  delete settings;
73  free (prog_version);
74  free (cur_dir);
75  free (prog_name);
76  free (run_dir);
77}
78
79// Set the name of the application (for messages)
80void
81Application::set_name (const char *_name)
82{
83  prog_name = get_realpath (_name);
84}
85
86char *
87Application::get_realpath (const char *_name)
88{
89  if (_name == NULL)
90    _name = "/proc/self/exe";
91  char *exe_name = realpath (_name, NULL);
92  if (exe_name)
93    return exe_name;
94  if (strchr (_name, '/') == NULL)
95    {
96      char *path = getenv ("PATH");
97      if (path)
98	for (char *s = path;; s++)
99	  if (*s == ':' || *s == 0)
100	    {
101	      if (path != s)
102		{
103		  char *nm = dbe_sprintf (NTXT ("%.*s/%s"), (int) (path - s - 1), path, _name);
104		  exe_name = realpath (nm, NULL);
105		  free (nm);
106		  if (exe_name)
107		    return exe_name;
108		}
109	      if (*s == 0)
110		break;
111	      path = s + 1;
112	    }
113    }
114  return strdup (_name);
115}
116
117// Set the directory where all binaries are found
118void
119Application::set_run_dir (char *fdhome)
120{
121  run_dir_with_spaces = NULL;
122  if (fdhome)
123    {
124      char *path = dbe_sprintf ("%s/bin", fdhome);
125      struct stat sbuf;
126      if (stat (path, &sbuf) != -1)
127	run_dir = path;
128      else
129	{
130	  free (path);
131	  run_dir = dbe_strdup (fdhome);
132	}
133    }
134  else
135    {
136      run_dir = realpath (prog_name, NULL);
137      if (run_dir == NULL)
138	{
139	  fprintf (stderr, // I18N won't work here -- not catopen yet.
140		   GTXT ("Can't find location of %s\n"), prog_name);
141	  run_dir = dbe_strdup (get_cur_dir ());
142	}
143      else
144	{
145	  char *d = strrchr (run_dir, '/');
146	  if (d)
147	    *d = 0;
148	  // Check if the installation path contains spaces
149	  if (strchr (run_dir, ' ') != NULL)
150	    {
151	      // Create a symbolic link without spaces
152	      const char *dir = NTXT ("/tmp/.gprofngLinks");
153	      char *symbolic_link = dbe_create_symlink_to_path (run_dir, dir);
154	      if (NULL != symbolic_link)
155		{
156		  // Save old path to avoid memory leak
157		  run_dir_with_spaces = run_dir;
158		  // Use the path through symbolic link
159		  run_dir = symbolic_link;
160		}
161	    }
162	}
163    }
164}
165
166char *
167Application::get_cur_dir ()
168{
169  if (cur_dir == NULL)
170    {
171      char cwd[MAXPATHLEN];
172      if (getcwd (cwd, sizeof (cwd)) == NULL)
173	{
174	  perror (prog_name);
175	  exit (1);
176	}
177      cur_dir = dbe_strdup (canonical_path (cwd));
178    }
179  return cur_dir;
180}
181
182/**
183 * Get number of worker threads
184 * This is used to decide if it is ok to use worker threads for stat()
185 * and other actions that can hang for a long time
186 * @return number_of_worker_threads
187 */
188int
189Application::get_number_of_worker_threads ()
190{
191  return number_of_worker_threads;
192}
193
194int
195Application::check_args (int argc, char *argv[])
196{
197  int opt;
198  // Parsing the command line
199  opterr = 0;
200  while ((opt = getopt (argc, argv, "V")) != EOF)
201    switch (opt)
202      {
203      case 'V':
204// Ruud
205	Application::print_version_info ();
206/*
207	printf (NTXT ("GNU %s version %s\n"), get_basename (prog_name), VERSION);
208*/
209	exit (0);
210      default:
211	usage ();
212      }
213  return optind;
214}
215
216Emsg *
217Application::fetch_comments ()
218{
219  if (commentq == NULL)
220    return NULL;
221  return commentq->fetch ();
222}
223
224void
225Application::queue_comment (Emsg *m)
226{
227  commentq->append (m);
228}
229
230void
231Application::delete_comments ()
232{
233  if (commentq != NULL)
234    {
235      delete commentq;
236      commentq = new Emsgqueue (NTXT ("app_commentq"));
237    }
238}
239
240int
241Application::set_progress (int percentage, const char *proc_str)
242{
243  if (progress_func != NULL)
244    return progress_func (percentage, proc_str);
245  return 0;
246}
247
248// Ruud
249void
250Application::print_version_info ()
251{
252  printf ( GTXT (
253    "GNU %s binutils version %s\n"
254    "Copyright (C) 2021 Free Software Foundation, Inc.\n"
255    "License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.\n"
256    "This is free software: you are free to change and redistribute it.\n"
257    "There is NO WARRANTY, to the extent permitted by law.\n"),
258    get_basename (prog_name), VERSION);
259}
260