GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
oct-env.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1996-2024 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 /*
27 
28 The functions listed below were adapted from a similar functions
29 from GNU Bash, the Bourne Again SHell, copyright (C) 1987, 1989, 1991
30 Free Software Foundation, Inc.
31 
32  octave::sys::env::do_absolute_pathname
33  octave::sys::env::do_base_pathname
34  octave::sys::env::do_chdir
35  octave::sys::env::do_getcwd
36  octave::sys::env::do_make_absolute
37  octave::sys::env::do_polite_directory_format
38  octave::sys::env::pathname_backup
39 
40 */
41 
42 #if defined (HAVE_CONFIG_H)
43 # include "config.h"
44 #endif
45 
46 #include <cctype>
47 #include <cstdlib>
48 #include <cstring>
49 
50 #include <string>
51 
52 #include "file-ops.h"
53 #include "lo-error.h"
54 #include "lo-sysdep.h"
55 #include "lo-utils.h"
56 #include "oct-env.h"
57 #include "oct-password.h"
58 #include "oct-syscalls.h"
60 #include "singleton-cleanup.h"
61 #include "unistd-wrappers.h"
62 
63 #if defined (OCTAVE_USE_WINDOWS_API)
64 # include <windows.h>
65 # include <shlobj.h>
66 #endif
67 
69 
71 
72 env::env ()
73  : m_follow_symbolic_links (true), m_verbatim_pwd (true),
74  m_current_directory (), m_prog_name (), m_prog_invocation_name (),
75  m_user_name (), m_host_name ()
76 {
77  // Get a real value for the current directory.
78  do_getcwd ();
79 
80  // Etc.
81  do_get_user_name ();
82 
83  do_get_host_name ();
84 }
85 
86 env *env::s_instance = nullptr;
87 
88 bool
89 env::instance_ok ()
90 {
91  bool retval = true;
92 
93  if (! s_instance)
94  {
95  s_instance = new env ();
96  singleton_cleanup_list::add (cleanup_instance);
97  }
98 
99  return retval;
100 }
101 
102 std::string
103 env::polite_directory_format (const std::string& name)
104 {
105  return (instance_ok ())
106  ? s_instance->do_polite_directory_format (name) : "";
107 }
108 
109 bool
110 env::absolute_pathname (const std::string& s)
111 {
112  return (instance_ok ())
113  ? s_instance->do_absolute_pathname (s) : false;
114 }
115 
116 bool
117 env::rooted_relative_pathname (const std::string& s)
118 {
119  return (instance_ok ())
120  ? s_instance->do_rooted_relative_pathname (s) : false;
121 }
122 
123 std::string
124 env::base_pathname (const std::string& s)
125 {
126  return (instance_ok ())
127  ? s_instance->do_base_pathname (s) : "";
128 }
129 
130 std::string
131 env::make_absolute (const std::string& s, const std::string& dot_path)
132 {
133  return (instance_ok ())
134  ? s_instance->do_make_absolute (s, dot_path) : "";
135 }
136 
137 std::string
139 {
140  return (instance_ok ())
141  ? s_instance->do_getcwd () : "";
142 }
143 
144 std::string
146 {
147  return (instance_ok ())
148  ? s_instance->do_get_home_directory () : "";
149 }
150 
151 std::string
153 {
154  return (instance_ok ())
155  ? s_instance->do_get_temp_directory () : "";
156 }
157 
158 std::string
160 {
161  return (instance_ok ())
162  ? s_instance->do_get_user_config_directory () : "";
163 }
164 
165 std::string
167 {
168  return (instance_ok ())
169  ? s_instance->do_get_user_data_directory () : "";
170 }
171 
172 std::string
174 {
175  return (instance_ok ())
176  ? s_instance->m_prog_name : "";
177 }
178 
179 std::string
181 {
182  return (instance_ok ())
183  ? s_instance->m_prog_invocation_name : "";
184 }
185 
186 void
187 env::set_program_name (const std::string& s)
188 {
189  if (instance_ok ())
190  s_instance->do_set_program_name (s);
191 }
192 
193 std::string
195 {
196  return (instance_ok ())
197  ? s_instance->do_get_user_name () : "";
198 }
199 
200 std::string
202 {
203  return (instance_ok ())
204  ? s_instance->do_get_host_name () : "";
205 }
206 
207 std::string
208 env::do_get_temp_directory () const
209 {
210  std::string tempd = do_getenv ("TMPDIR");
211 
212 #if defined (__MINGW32__) || defined (_MSC_VER)
213 
214  if (tempd.empty ())
215  tempd = do_getenv ("TEMP");
216 
217  if (tempd.empty ())
218  tempd = do_getenv ("TMP");
219 
220 #if defined (P_tmpdir)
221  if (tempd.empty ())
222  tempd = P_tmpdir;
223 #endif
224 
225  // Some versions of MinGW and MSVC either don't define P_tmpdir, or
226  // define it to a single backslash. In such cases just use C:\temp.
227  if (tempd.empty () || tempd == R"(\)")
228  tempd = R"(c:\temp)";
229 
230 #else
231 
232  if (tempd.empty ())
233  tempd = do_getenv ("TMP");
234 
235 #if defined (P_tmpdir)
236  if (tempd.empty ())
237  tempd = P_tmpdir;
238 #else
239  if (tempd.empty ())
240  tempd = "/tmp";
241 #endif
242 
243 #endif
244 
245  return tempd;
246 }
247 
248 std::string
249 env::do_get_user_config_directory ()
250 {
251  std::string cfg_dir;
252 
253 #if defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) && defined (OCTAVE_USE_WINDOWS_API)
254  wchar_t path[MAX_PATH+1];
255  if (SHGetFolderPathW (nullptr, CSIDL_APPDATA | CSIDL_FLAG_DONT_VERIFY,
256  nullptr, SHGFP_TYPE_CURRENT, path) == S_OK)
257  cfg_dir = u8_from_wstring (path);
258 #else
259  cfg_dir = do_getenv ("XDG_CONFIG_HOME");
260 #endif
261 
262  if (cfg_dir.empty ())
263  cfg_dir = do_get_home_directory () + sys::file_ops::dir_sep_str ()
264  + ".config";
265 
266  return cfg_dir;
267 }
268 
269 std::string
270 env::do_get_user_data_directory ()
271 {
272  std::string data_dir;
273 
274 #if defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) && defined (OCTAVE_USE_WINDOWS_API)
275  wchar_t path[MAX_PATH+1];
276  if (SHGetFolderPathW (nullptr, CSIDL_APPDATA | CSIDL_FLAG_DONT_VERIFY,
277  nullptr, SHGFP_TYPE_CURRENT, path) == S_OK)
278  data_dir = u8_from_wstring (path);
279 #else
280  data_dir = do_getenv ("XDG_DATA_HOME");
281 #endif
282 
283  if (data_dir.empty ())
284  data_dir = do_get_home_directory () + sys::file_ops::dir_sep_str ()
285  + ".local" + sys::file_ops::dir_sep_str () + "share";
286 
287  return data_dir;
288 }
289 
290 
291 // FIXME: this leaves no way to distinguish between a
292 // variable that is not set and one that is set to the empty string.
293 // Is this a problem?
294 
295 std::string
296 env::getenv (const std::string& name)
297 {
298  return (instance_ok ())
299  ? s_instance->do_getenv (name) : "";
300 }
301 
302 bool
303 env::isenv (const std::string& name)
304 {
305  return isenv_wrapper (name);
306 }
307 
308 void
309 env::putenv (const std::string& name, const std::string& value)
310 {
311  putenv_wrapper (name, value);
312 }
313 
314 bool
316 {
317  std::string display = getenv ("DISPLAY");
318 
319  return ! display.empty ();
320 }
321 
322 bool
323 env::chdir (const std::string& newdir)
324 {
325  return (instance_ok ())
326  ? s_instance->do_chdir (newdir) : false;
327 }
328 
329 void
330 env::do_set_program_name (const std::string& s)
331 {
332  static bool initialized = false;
333 
334  if (! initialized)
335  {
336  // octave_set_program_name_wrapper returns a cleaned up
337  // version of the program name (stripping libtool's "lt-"
338  // prefix, for example).
339 
340  // The string passed to gnulib's ::set_program_name function must
341  // exist for the duration of the program so allocate a copy here
342  // instead of passing S.c_str () which only exists as long as the
343  // string object S.
344 
345  m_prog_invocation_name
346  = octave_set_program_name_wrapper (strsave (s.c_str ()));
347 
348  std::size_t pos
349  = m_prog_invocation_name.find_last_of (sys::file_ops::dir_sep_chars ());
350 
351  // Also keep a shortened version of the program name.
352  m_prog_name = (pos == std::string::npos
353  ? m_prog_invocation_name
354  : m_prog_invocation_name.substr (pos+1));
355 
356  initialized = true;
357  }
358 }
359 
360 // Return a pretty pathname. If the first part of the pathname is the
361 // same as $HOME, then replace that with '~'.
362 
363 std::string
364 env::do_polite_directory_format (const std::string& name)
365 {
366  std::string retval;
367 
368  std::string home_dir = do_get_home_directory ();
369 
370  std::size_t len = home_dir.length ();
371 
372  if (len > 1 && home_dir == name.substr (0, len)
373  && (name.length () == len || sys::file_ops::is_dir_sep (name[len])))
374  {
375  retval = "~";
376  retval.append (name.substr (len));
377  }
378  else
379  retval = name;
380 
381  return retval;
382 }
383 
384 bool
385 env::do_absolute_pathname (const std::string& s) const
386 {
387  std::size_t len = s.length ();
388 
389  if (len == 0)
390  return false;
391 
392  if (sys::file_ops::is_dir_sep (s[0]))
393  return true;
394 
395 #if defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM)
396  if ((len == 2 && isalpha (s[0]) && s[1] == ':')
397  || (len > 2 && isalpha (s[0]) && s[1] == ':'
398  && sys::file_ops::is_dir_sep (s[2])))
399  return true;
400 #endif
401 
402  return false;
403 }
404 
405 bool
406 env::do_rooted_relative_pathname (const std::string& s) const
407 {
408  std::size_t len = s.length ();
409 
410  if (len == 0)
411  return false;
412 
413  if (len == 1 && s[0] == '.')
414  return true;
415 
416  if (len > 1 && s[0] == '.' && sys::file_ops::is_dir_sep (s[1]))
417  return true;
418 
419  if (len == 2 && s[0] == '.' && s[1] == '.')
420  return true;
421 
422  if (len > 2 && s[0] == '.' && s[1] == '.'
423  && sys::file_ops::is_dir_sep (s[2]))
424  return true;
425 
426  return false;
427 }
428 
429 // Return the 'basename' of the pathname in STRING (the stuff after
430 // the last directory separator). If STRING is not a full pathname,
431 // simply return it.
432 
433 std::string
434 env::do_base_pathname (const std::string& s) const
435 {
436  if (! (do_absolute_pathname (s) || do_rooted_relative_pathname (s)))
437  return s;
438 
439  std::size_t pos = s.find_last_of (sys::file_ops::dir_sep_chars ());
440 
441  if (pos == std::string::npos)
442  return s;
443  else
444  return s.substr (pos+1);
445 }
446 
447 // Turn STRING (a pathname) into an absolute pathname, assuming that
448 // DOT_PATH contains the symbolic location of the current directory.
449 
450 std::string
451 env::do_make_absolute (const std::string& s,
452  const std::string& dot_path) const
453 {
454  if (dot_path.empty () || s.empty () || do_absolute_pathname (s))
455  return s;
456 
457  // Optimization: every time Octave returns to the prompt it calls
458  // make_absolute_filename with '.' as argument.
459  if (s == ".")
460  return dot_path;
461 
462  std::string current_dir = dot_path;
463 
464  if (! sys::file_ops::is_dir_sep (current_dir.back ()))
465  current_dir.append (sys::file_ops::dir_sep_str ());
466 
467  std::size_t i = 0;
468  std::size_t slen = s.length ();
469 
470  while (i < slen)
471  {
472  if (s[i] == '.')
473  {
474  if (i + 1 == slen)
475  break;
476 
477  if (sys::file_ops::is_dir_sep (s[i+1]))
478  {
479  i += 2;
480  continue;
481  }
482 
483  if (s[i+1] == '.'
484  && (i + 2 == slen
485  || sys::file_ops::is_dir_sep (s[i+2])))
486  {
487  i += 2;
488  if (i != slen)
489  i++;
490 
491  pathname_backup (current_dir, 1);
492 
493  continue;
494  }
495  }
496 
497  std::size_t sep_pos;
498  sep_pos = s.find_first_of (sys::file_ops::dir_sep_chars (), i);
499 
500  if (sep_pos == std::string::npos)
501  {
502  current_dir.append (s, i, sep_pos-i);
503  break;
504  }
505  else if (sep_pos == i)
506  {
507  /* Two separators in a row, skip adding 2nd separator */
508  i++;
509  }
510  else
511  {
512  current_dir.append (s, i, sep_pos-i+1);
513  i = sep_pos + 1;
514  }
515  }
516 
517  // Strip any trailing directory separator
518  if (sys::file_ops::is_dir_sep (current_dir.back ()))
519  current_dir.pop_back ();
520 
521  return current_dir;
522 }
523 
524 // Return a string which is the current working directory.
525 
526 std::string
527 env::do_getcwd ()
528 {
529  if (! m_follow_symbolic_links)
530  m_current_directory = "";
531 
532  if (m_verbatim_pwd || m_current_directory.empty ())
533  m_current_directory = sys::getcwd ();
534 
535  return m_current_directory;
536 }
537 
538 // This value is not cached because it can change while Octave is
539 // running.
540 
541 std::string
542 env::do_get_home_directory ()
543 {
544  std::string hd = do_getenv ("HOME");
545 
546 #if defined (__MINGW32__) || defined (_MSC_VER)
547  // Maybe we are started directly from cmd.exe.
548  if (hd.empty ())
549  {
550  std::string drv = do_getenv ("HOMEDRIVE");
551  if (drv.empty ())
552  hd = do_getenv ("HOMEPATH");
553  else
554  hd = drv + do_getenv ("HOMEPATH");
555  }
556 #endif
557 
558  if (hd.empty ())
559  {
560  sys::password pw = sys::password::getpwuid (sys::getuid ());
561 
562  hd = (pw ? pw.dir () : std::string (sys::file_ops::dir_sep_str ()));
563  }
564 
565  return hd;
566 }
567 
568 std::string
569 env::do_get_user_name ()
570 {
571  if (m_user_name.empty ())
572  {
573  sys::password pw = sys::password::getpwuid (sys::getuid ());
574 
575  m_user_name = (pw ? pw.name () : "unknown");
576  }
577 
578  return m_user_name;
579 }
580 
581 std::string
582 env::do_get_host_name ()
583 {
584  if (m_host_name.empty ())
585  {
586  char hostname[1024];
587 
588  int status = octave_gethostname_wrapper (hostname, 1023);
589 
590  m_host_name = (status < 0) ? "unknown" : hostname;
591  }
592 
593  return m_host_name;
594 }
595 
596 std::string
597 env::do_getenv (const std::string& name) const
598 {
599  return getenv_wrapper (name);
600 }
601 
602 // Do the work of changing to the directory NEWDIR.
603 // Handle symbolic link following, etc.
604 
605 bool
606 env::do_chdir (const std::string& newdir)
607 {
608  bool retval = false;
609 
610  std::string tmp;
611 
612  if (m_follow_symbolic_links)
613  {
614  if (m_current_directory.empty ())
615  do_getcwd ();
616 
617  if (m_current_directory.empty ())
618  tmp = newdir;
619  else
620  tmp = do_make_absolute (newdir, m_current_directory);
621 
622  // Get rid of trailing directory separator.
623  if (tmp.length () > 1 && sys::file_ops::is_dir_sep (tmp.back ()))
624  tmp.pop_back ();
625 
626  if (! sys::chdir (tmp))
627  {
628  m_current_directory = tmp;
629  retval = true;
630  }
631  }
632  else
633  retval = (! sys::chdir (newdir));
634 
635  return retval;
636 }
637 
638 // Remove the last N directories from PATH.
639 
640 void
641 env::pathname_backup (std::string& path, int n) const
642 {
643  if (path.empty ())
644  return;
645 
646  std::size_t i = path.length () - 1;
647 
648  while (n--)
649  {
650  while (sys::file_ops::is_dir_sep (path[i]) && i > 0)
651  i--;
652 
653 #if defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM)
654  // Don't strip file letter part.
655  if (i == 1 && path[i] == ':')
656  {
657  // Keep path separator if present.
658  i = std::min (i+2, path.length ());
659  break;
660  }
661 #endif
662 
663  while (! sys::file_ops::is_dir_sep (path[i]) && i > 0)
664  i--;
665 
666  i++;
667  }
668 
669  path.resize (i);
670 }
671 
672 void
673 env::error (int err_num) const
674 {
675  (*current_liboctave_error_handler) ("%s", std::strerror (err_num));
676 }
677 
678 void
679 env::error (const std::string& s) const
680 {
681  (*current_liboctave_error_handler) ("%s", s.c_str ());
682 }
683 
684 OCTAVE_END_NAMESPACE(sys)
685 OCTAVE_END_NAMESPACE(octave)
charNDArray min(char d, const charNDArray &m)
Definition: chNDArray.cc:207
Definition: oct-env.h:40
static std::string base_pathname(const std::string &s)
Definition: oct-env.cc:124
static std::string get_user_name()
Definition: oct-env.cc:194
static std::string getenv(const std::string &name)
Definition: oct-env.cc:296
static std::string get_home_directory()
Definition: oct-env.cc:145
static bool absolute_pathname(const std::string &s)
Definition: oct-env.cc:110
static bool have_x11_display()
Definition: oct-env.cc:315
env()
Definition: oct-env.cc:72
static std::string get_program_invocation_name()
Definition: oct-env.cc:180
static bool rooted_relative_pathname(const std::string &s)
Definition: oct-env.cc:117
static bool chdir(const std::string &newdir)
Definition: oct-env.cc:323
static std::string get_user_config_directory()
Definition: oct-env.cc:159
static std::string make_absolute(const std::string &s, const std::string &dot_path=get_current_directory())
Definition: oct-env.cc:131
static std::string get_temp_directory()
Definition: oct-env.cc:152
static void putenv(const std::string &name, const std::string &value)
Definition: oct-env.cc:309
static std::string get_current_directory()
Definition: oct-env.cc:138
static std::string get_user_data_directory()
Definition: oct-env.cc:166
static bool isenv(const std::string &name)
Definition: oct-env.cc:303
static std::string polite_directory_format(const std::string &name)
Definition: oct-env.cc:103
static std::string get_host_name()
Definition: oct-env.cc:201
static std::string get_program_name()
Definition: oct-env.cc:173
static void set_program_name(const std::string &s)
Definition: oct-env.cc:187
static void add(fptr f)
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
std::string data_dir()
bool is_dir_sep(char c)
std::string dir_sep_str()
std::string dir_sep_chars()
std::string u8_from_wstring(const std::wstring &wchar_string)
Definition: lo-sysdep.cc:746
std::string getenv_wrapper(const std::string &name)
Definition: lo-sysdep.cc:685
bool isenv_wrapper(const std::string &name)
Definition: lo-sysdep.cc:711
int chdir(const std::string &path_arg)
Definition: lo-sysdep.cc:107
std::string getcwd()
Definition: lo-sysdep.cc:72
void putenv_wrapper(const std::string &name, const std::string &value)
Definition: lo-sysdep.cc:649
octave_idx_type n
Definition: mx-inlines.cc:761
uid_t getuid()
const char * octave_set_program_name_wrapper(const char *pname)
int octave_gethostname_wrapper(char *nm, size_t len)
F77_RET_T len
Definition: xerbla.cc:61