GNU Octave 7.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
file-ops.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 1996-2022 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#if defined (HAVE_CONFIG_H)
27# include "config.h"
28#endif
29
30#include <cerrno>
31#include <cstdio>
32#include <cstdlib>
33#include <cstring>
34
35#include <vector>
36
37#if defined (OCTAVE_USE_WINDOWS_API)
38# include <windows.h>
39# include "unwind-prot.h"
40#else
42#endif
43
44#include "areadlink-wrapper.h"
45#include "dir-ops.h"
46#include "file-ops.h"
47#include "file-stat.h"
49#include "lo-sysdep.h"
50#include "oct-env.h"
51#include "oct-locbuf.h"
52#include "oct-password.h"
53#include "quit.h"
54#include "stat-wrappers.h"
55#include "str-vec.h"
56#include "unistd-wrappers.h"
57
58namespace octave
59{
60 // The following tilde-expansion code was stolen and adapted from
61 // readline.
62
63 // The default value of tilde_additional_prefixes. This is set to
64 // whitespace preceding a tilde so that simple programs which do not
65 // perform any word separation get desired behavior.
66 static const char *default_prefixes[] = { " ~", "\t~", ":~", nullptr };
67
68 // The default value of tilde_additional_suffixes. This is set to
69 // whitespace or newline so that simple programs which do not perform
70 // any word separation get desired behavior.
71 static const char *default_suffixes[] = { " ", "\n", ":", nullptr };
72
73 static std::size_t
74 tilde_find_prefix (const std::string& s, std::size_t& len)
75 {
76 len = 0;
77
78 std::size_t s_len = s.length ();
79
80 if (s_len == 0 || s[0] == '~')
81 return 0;
82
84
85 if (! prefixes.empty ())
86 {
87 for (std::size_t i = 0; i < s_len; i++)
88 {
89 for (int j = 0; j < prefixes.numel (); j++)
90 {
91 std::size_t pfx_len = prefixes[j].length ();
92
93 if (prefixes[j] == s.substr (i, pfx_len))
94 {
95 len = pfx_len - 1;
96 return i + len;
97 }
98 }
99 }
100 }
101
102 return s_len;
103 }
104
105 // Find the end of a tilde expansion in S, and return the index
106 // of the character which ends the tilde definition.
107
108 static std::size_t
109 tilde_find_suffix (const std::string& s)
110 {
111 std::size_t s_len = s.length ();
112
114
115 std::size_t i = 0;
116
117 for ( ; i < s_len; i++)
118 {
119 if (sys::file_ops::is_dir_sep (s[i]))
120 break;
121
122 if (! suffixes.empty ())
123 {
124 for (int j = 0; j < suffixes.numel (); j++)
125 {
126 std::size_t sfx_len = suffixes[j].length ();
127
128 if (suffixes[j] == s.substr (i, sfx_len))
129 return i;
130 }
131 }
132 }
133
134 return i;
135 }
136
137 // Take FNAME and return the tilde prefix we want expanded.
138
139 static std::string
140 isolate_tilde_prefix (const std::string& fname)
141 {
142 std::size_t f_len = fname.length ();
143
144 std::size_t len = 1;
145
146 while (len < f_len && ! sys::file_ops::is_dir_sep (fname[len]))
147 len++;
148
149 return fname.substr (1, len);
150 }
151
152 // Do the work of tilde expansion on FILENAME. FILENAME starts with a
153 // tilde.
154
155 static std::string
156 tilde_expand_word (const std::string& filename)
157 {
158 std::size_t f_len = filename.length ();
159
160 if (f_len == 0 || filename[0] != '~')
161 return std::string (filename);
162
163 // A leading '~/' or a bare '~' is *always* translated to the value
164 // of $HOME or the home directory of the current user, regardless of
165 // any preexpansion hook.
166
167 if (f_len == 1 || sys::file_ops::is_dir_sep (filename[1]))
168 return sys::env::get_home_directory () + filename.substr (1);
169
170 std::string username = isolate_tilde_prefix (filename);
171
172 std::size_t user_len = username.length ();
173
174 std::string dirname;
175
177 {
178 std::string expansion
180
181 if (! expansion.empty ())
182 return expansion + filename.substr (user_len+1);
183 }
184
185 // No preexpansion hook, or the preexpansion hook failed. Look in the
186 // password database.
187
189
190 if (! pw)
191 {
192 // If the calling program has a special syntax for expanding tildes,
193 // and we couldn't find a standard expansion, then let them try.
194
196 {
197 std::string expansion
199
200 if (! expansion.empty ())
201 dirname = expansion + filename.substr (user_len+1);
202 }
203
204 // If we don't have a failure hook, or if the failure hook did not
205 // expand the tilde, return a copy of what we were passed.
206
207 if (dirname.empty ())
208 dirname = filename;
209 }
210 else
211 dirname = pw.dir () + filename.substr (user_len+1);
212
213 return dirname;
214 }
215
216 namespace sys
217 {
218 namespace file_ops
219 {
220 char dev_sep_char (void)
221 {
222#if (defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) && ! defined (OCTAVE_HAVE_POSIX_FILESYSTEM))
223 return ':';
224#else
225 return 0;
226#endif
227 }
228
229 char dir_sep_char (void)
230 {
231#if (defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) && ! defined (OCTAVE_HAVE_POSIX_FILESYSTEM))
232 return '\\';
233#else
234 return '/';
235#endif
236 }
237
238 std::string dir_sep_str (void)
239 {
240#if (defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) && ! defined (OCTAVE_HAVE_POSIX_FILESYSTEM))
241 return R"(\)";
242#else
243 return "/";
244#endif
245 }
246
247 std::string dir_sep_chars (void)
248 {
249#if defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM)
250 return R"(/\)";
251#else
252 return dir_sep_str ();
253#endif
254 }
255
257
259
261
263
264 bool is_dev_sep (char c)
265 {
266#if (defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) && ! defined (OCTAVE_HAVE_POSIX_FILESYSTEM))
267 return c == dev_sep_char ();
268#else
269 octave_unused_parameter (c);
270
271 return false;
272#endif
273 }
274
275 bool is_dir_sep (char c)
276 {
277 std::string tmp = dir_sep_chars ();
278 return tmp.find (c) != std::string::npos;
279 }
280
281 std::string tilde_expand (const std::string& name)
282 {
283 if (name.find ('~') == std::string::npos)
284 return std::string (name);
285 else
286 {
287 std::string result;
288
289 std::size_t name_len = name.length ();
290
291 // Scan through S expanding tildes as we come to them.
292
293 std::size_t pos = 0;
294
295 while (1)
296 {
297 if (pos > name_len)
298 break;
299
300 std::size_t len;
301
302 // Make START point to the tilde which starts the expansion.
303
304 std::size_t start = tilde_find_prefix (name.substr (pos), len);
305
306 result.append (name.substr (pos, start));
307
308 // Advance STRING to the starting tilde.
309
310 pos += start;
311
312 // Make FINI be the index of one after the last character of the
313 // username.
314
315 std::size_t fini = tilde_find_suffix (name.substr (pos));
316
317 // If both START and FINI are zero, we are all done.
318
319 if (! (start || fini))
320 break;
321
322 // Expand the entire tilde word, and copy it into RESULT.
323
324 std::string tilde_word = name.substr (pos, fini);
325
326 pos += fini;
327
328 std::string expansion = tilde_expand_word (tilde_word);
329
330 result.append (expansion);
331 }
332
333 return result;
334 }
335 }
336
338 {
339 int n = names.numel ();
340
341 string_vector retval (n);
342
343 for (int i = 0; i < n; i++)
344 retval[i] = tilde_expand (names[i]);
345
346 return retval;
347 }
348
349 std::string concat (const std::string& dir, const std::string& file)
350 {
351 return dir.empty ()
352 ? file
353 : (is_dir_sep (dir.back ())
354 ? dir + file
355 : dir + dir_sep_char () + file);
356 }
357
358 std::string dirname (const std::string& path)
359 {
360 std::size_t ipos = path.find_last_of (dir_sep_chars ());
361
362 return (ipos != std::string::npos) ? path.substr (0, ipos) : "";
363 }
364
365 std::string tail (const std::string& path)
366 {
367 std::size_t ipos = path.find_last_of (dir_sep_chars ());
368
369 if (ipos != std::string::npos)
370 ipos++;
371 else
372 ipos = 0;
373
374 return path.substr (ipos);
375 }
376
377 std::string native_separator_path (const std::string& path)
378 {
379 std::string retval;
380
381 if (dir_sep_char () == '/')
382 retval = path;
383 else
384 {
385 std::size_t n = path.length ();
386 for (std::size_t i = 0; i < n; i++)
387 {
388 if (path[i] == '/')
389 retval += dir_sep_char();
390 else
391 retval += path[i];
392 }
393 }
394
395 return retval;
396 }
397 }
398
399 int mkdir (const std::string& nm, mode_t md)
400 {
401 std::string msg;
402 return mkdir (nm, md, msg);
403 }
404
405 int mkdir (const std::string& name, mode_t mode, std::string& msg)
406 {
407 msg = "";
408
409 int status = octave_mkdir_wrapper (name.c_str (), mode);
410
411 if (status < 0)
412 msg = std::strerror (errno);
413
414 return status;
415 }
416
417 int mkfifo (const std::string& nm, mode_t md)
418 {
419 std::string msg;
420 return mkfifo (nm, md, msg);
421 }
422
423 int mkfifo (const std::string& name, mode_t mode, std::string& msg)
424 {
425 msg = "";
426
427 int status = octave_mkfifo_wrapper (name.c_str (), mode);
428
429 if (status < 0)
430 msg = std::strerror (errno);
431
432 return status;
433 }
434
435 int link (const std::string& old_name, const std::string& new_name)
436 {
437 std::string msg;
438 return link (old_name, new_name, msg);
439 }
440
441 int link (const std::string& old_name, const std::string& new_name,
442 std::string& msg)
443 {
444 msg = "";
445
446 int status = -1;
447
448 status = octave_link_wrapper (old_name.c_str (), new_name.c_str ());
449
450 if (status < 0)
451 msg = std::strerror (errno);
452
453 return status;
454 }
455
456 int symlink (const std::string& old_name, const std::string& new_name)
457 {
458 std::string msg;
459 return symlink (old_name, new_name, msg);
460 }
461
462 int symlink (const std::string& old_name, const std::string& new_name,
463 std::string& msg)
464 {
465 msg = "";
466
467 int status = -1;
468
469 status = octave_symlink_wrapper (old_name.c_str (), new_name.c_str ());
470
471 if (status < 0)
472 msg = std::strerror (errno);
473
474 return status;
475 }
476
477 int readlink (const std::string& path, std::string& result)
478 {
479 std::string msg;
480 return readlink (path, result, msg);
481 }
482
483 int readlink (const std::string& path, std::string& result, std::string& msg)
484 {
485 int status = -1;
486
487 msg = "";
488
489 char *buf = octave_areadlink_wrapper (path.c_str ());
490
491 if (! buf)
492 msg = std::strerror (errno);
493 else
494 {
495 result = buf;
496 ::free (buf);
497 status = 0;
498 }
499
500 return status;
501 }
502
503 int rename (const std::string& from, const std::string& to)
504 {
505 std::string msg;
506 return rename (from, to, msg);
507 }
508
509 int rename (const std::string& from, const std::string& to,
510 std::string& msg)
511 {
512 int status = -1;
513
514 msg = "";
515
516#if defined (OCTAVE_USE_WINDOWS_API)
517 std::wstring wfrom = u8_to_wstring (from);
518 std::wstring wto = u8_to_wstring (to);
519 status = _wrename (wfrom.c_str (), wto.c_str ());
520#else
521 status = std::rename (from.c_str (), to.c_str ());
522#endif
523
524 if (status < 0)
525 msg = std::strerror (errno);
526
527 return status;
528 }
529
530 int rmdir (const std::string& name)
531 {
532 std::string msg;
533 return rmdir (name, msg);
534 }
535
536 int rmdir (const std::string& name, std::string& msg)
537 {
538 msg = "";
539
540 int status = -1;
541
542 status = octave_rmdir_wrapper (name.c_str ());
543
544 if (status < 0)
545 msg = std::strerror (errno);
546
547 return status;
548 }
549
550 // And a version that works recursively.
551
552 int recursive_rmdir (const std::string& name)
553 {
554 std::string msg;
555 return recursive_rmdir (name, msg);
556 }
557
558 int recursive_rmdir (const std::string& name, std::string& msg)
559 {
560 msg = "";
561
562 int status = 0;
563
564 string_vector dirlist;
565
566 if (get_dirlist (name, dirlist, msg))
567 {
568 for (octave_idx_type i = 0; i < dirlist.numel (); i++)
569 {
570 octave_quit ();
571
572 std::string nm = dirlist[i];
573
574 // Skip current directory and parent.
575 if (nm == "." || nm == "..")
576 continue;
577
578 std::string fullnm = name + file_ops::dir_sep_str () + nm;
579
580 // Get info about the file. Don't follow links.
581 file_stat fs (fullnm, false);
582
583 if (fs)
584 {
585 if (fs.is_dir ())
586 {
587 status = recursive_rmdir (fullnm, msg);
588
589 if (status < 0)
590 break;
591 }
592 else
593 {
594 status = unlink (fullnm, msg);
595
596 if (status < 0)
597 break;
598 }
599 }
600 else
601 {
602 msg = fs.error ();
603 break;
604 }
605 }
606
607 if (status >= 0)
608 status = rmdir (name, msg);
609 }
610 else
611 status = -1;
612
613 return status;
614 }
615
616 int umask (mode_t mode)
617 {
618 return octave_umask_wrapper (mode);
619 }
620
621 int unlink (const std::string& name)
622 {
623 std::string msg;
624 return unlink (name, msg);
625 }
626
627 int unlink (const std::string& name, std::string& msg)
628 {
629 msg = "";
630
631 int status = -1;
632
633 status = octave_unlink_wrapper (name.c_str ());
634
635 if (status < 0)
636 msg = std::strerror (errno);
637
638 return status;
639 }
640
641 std::string tempnam (const std::string& dir, const std::string& pfx)
642 {
643 std::string msg;
644 return tempnam (dir, pfx, msg);
645 }
646
647 std::string tempnam (const std::string& dir, const std::string& pfx,
648 std::string& msg)
649 {
650 msg = "";
651
652 std::string retval;
653
654 // get dir path to use for template
655 std::string templatename;
656 if (dir.empty ())
657 templatename = env::get_temp_directory ();
658 else if (! file_stat (dir, false).is_dir ())
659 templatename = env::get_temp_directory ();
660 else
661 templatename = dir;
662
663 // add dir sep char if it is not there
664 if (*templatename.rbegin () != file_ops::dir_sep_char ())
665 templatename += file_ops::dir_sep_char ();
666
667 if (pfx.empty ())
668 templatename += "file";
669 else
670 templatename += pfx;
671
672 // add the required XXXXXX for the template
673 templatename += "XXXXXX";
674
675 // create and copy template to char array for call to gen_tempname
676 char tname [templatename.length () + 1];
677
678 strcpy (tname, templatename.c_str ());
679
680 if (octave_gen_tempname_wrapper (tname) == -1)
681 msg = std::strerror (errno);
682 else
683 retval = tname;
684
685 return retval;
686 }
687
688 std::string canonicalize_file_name (const std::string& name)
689 {
690 std::string msg;
691 return canonicalize_file_name (name, msg);
692 }
693
694 std::string canonicalize_file_name (const std::string& name, std::string& msg)
695 {
696 msg = "";
697
698 std::string retval;
699
700 // FIXME: Consider replacing this with std::filesystem::canonical on all
701 // platforms once we allow using C++17.
702
703#if defined (OCTAVE_USE_WINDOWS_API)
704 // open file handle
705 std::wstring wname = u8_to_wstring (name);
706 HANDLE h_file = CreateFileW (wname.c_str (), GENERIC_READ,
707 FILE_SHARE_READ, nullptr, OPEN_EXISTING,
708 FILE_FLAG_BACKUP_SEMANTICS, nullptr);
709
710 if (h_file == INVALID_HANDLE_VALUE)
711 {
712 msg = "Unable to open file \"" + name + "\"";
713 return retval;
714 }
715
716 unwind_action close_file_handle (CloseHandle, h_file);
717
718 const std::size_t buf_size = 32767;
719 wchar_t buffer[buf_size] = L"";
720
721 // query canonical name
722 DWORD len = GetFinalPathNameByHandleW (h_file, buffer, buf_size,
723 FILE_NAME_NORMALIZED);
724 if (len >= buf_size)
725 {
726 msg = "Error querying normalized name for \"" + name + "\"";
727 return retval;
728 }
729
730 retval = u8_from_wstring (std::wstring (buffer, len));
731
732 // remove prefix
733 // "Normal" paths are prefixed by "\\?\".
734 // UNC paths are prefixed by "\\?\UNC\".
735 if (retval.compare (0, 8, R"(\\?\UNC\)") == 0)
736 retval = retval.erase (2, 6);
737 else if (retval.compare (0, 4, R"(\\?\)") == 0)
738 retval = retval.erase (0, 4);
739#else
740 char *tmp = octave_canonicalize_file_name_wrapper (name.c_str ());
741
742 if (tmp)
743 {
744 retval = tmp;
745 free (tmp);
746 }
747
748 if (retval.empty ())
749 msg = std::strerror (errno);
750#endif
751
752 return retval;
753 }
754 }
755}
char * octave_canonicalize_file_name_wrapper(const char *name)
std::string error(void) const
Definition: file-stat.h:149
bool is_dir(void) const
Definition: file-stat.cc:65
static std::string get_temp_directory(void)
Definition: oct-env.cc:152
static std::string get_home_directory(void)
Definition: oct-env.cc:145
std::string dir(void) const
Definition: oct-password.cc:99
static password getpwnam(const std::string &nm)
octave_idx_type numel(void) const
Definition: str-vec.h:100
bool empty(void) const
Definition: str-vec.h:77
int octave_gen_tempname_wrapper(char *tmpl)
QString path
QString name
std::string native_separator_path(const std::string &path)
Definition: file-ops.cc:377
string_vector tilde_additional_suffixes
Definition: file-ops.cc:262
tilde_expansion_hook tilde_expansion_failure_hook
Definition: file-ops.cc:258
std::string concat(const std::string &dir, const std::string &file)
Definition: file-ops.cc:349
std::string dirname(const std::string &path)
Definition: file-ops.cc:358
std::string(* tilde_expansion_hook)(const std::string &)
Definition: file-ops.h:43
std::string tilde_expand(const std::string &name)
Definition: file-ops.cc:281
char dev_sep_char(void)
Definition: file-ops.cc:220
char dir_sep_char(void)
Definition: file-ops.cc:229
std::string dir_sep_chars(void)
Definition: file-ops.cc:247
std::string dir_sep_str(void)
Definition: file-ops.cc:238
tilde_expansion_hook tilde_expansion_preexpansion_hook
Definition: file-ops.cc:256
bool is_dev_sep(char c)
Definition: file-ops.cc:264
std::string tail(const std::string &path)
Definition: file-ops.cc:365
bool is_dir_sep(char c)
Definition: file-ops.cc:275
string_vector tilde_additional_prefixes
Definition: file-ops.cc:260
int link(const std::string &old_name, const std::string &new_name)
Definition: file-ops.cc:435
std::string tempnam(const std::string &dir, const std::string &pfx)
Definition: file-ops.cc:641
int readlink(const std::string &path, std::string &result)
Definition: file-ops.cc:477
std::string canonicalize_file_name(const std::string &name)
Definition: file-ops.cc:688
std::string u8_from_wstring(const std::wstring &wchar_string)
Definition: lo-sysdep.cc:513
int rename(const std::string &from, const std::string &to)
Definition: file-ops.cc:503
int mkdir(const std::string &nm, mode_t md)
Definition: file-ops.cc:399
int symlink(const std::string &old_name, const std::string &new_name)
Definition: file-ops.cc:456
int rmdir(const std::string &name)
Definition: file-ops.cc:530
int recursive_rmdir(const std::string &name)
Definition: file-ops.cc:552
std::wstring u8_to_wstring(const std::string &utf8_string)
Definition: lo-sysdep.cc:490
int umask(mode_t mode)
Definition: file-ops.cc:616
int unlink(const std::string &name)
Definition: file-ops.cc:621
int mkfifo(const std::string &nm, mode_t md)
Definition: file-ops.cc:417
bool get_dirlist(const std::string &dirname, string_vector &dirlist, std::string &msg)
Definition: lo-sysdep.cc:119
int rename(const std::string &from, const std::string &to, std::string &msg)
Definition: file-ops.cc:509
static std::string tilde_expand_word(const std::string &filename)
Definition: file-ops.cc:156
static std::size_t tilde_find_suffix(const std::string &s)
Definition: file-ops.cc:109
static const char * default_suffixes[]
Definition: file-ops.cc:71
static std::size_t tilde_find_prefix(const std::string &s, std::size_t &len)
Definition: file-ops.cc:74
static std::string isolate_tilde_prefix(const std::string &fname)
Definition: file-ops.cc:140
static const char * default_prefixes[]
Definition: file-ops.cc:66
void free(void *)
int octave_mkdir_wrapper(const char *name, mode_t mode)
Definition: stat-wrappers.c:49
int octave_mkfifo_wrapper(const char *name, mode_t mode)
Definition: stat-wrappers.c:63
int octave_umask_wrapper(mode_t mode)
Definition: stat-wrappers.c:69
int octave_rmdir_wrapper(const char *nm)
int octave_unlink_wrapper(const char *nm)
int octave_symlink_wrapper(const char *nm1, const char *nm2)
int octave_link_wrapper(const char *nm1, const char *nm2)
F77_RET_T len
Definition: xerbla.cc:61