GNU Octave  4.0.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
file-ops.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 1996-2015 John W. Eaton
4 
5 This file is part of Octave.
6 
7 Octave is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <http://www.gnu.org/licenses/>.
20 
21 */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <cerrno>
28 #include <cstdio>
29 #include <cstdlib>
30 #include <cstring>
31 
32 #include <iostream>
33 #include <vector>
34 
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <unistd.h>
38 
39 #include "pathmax.h"
40 #include "canonicalize.h"
41 
42 extern "C" {
43 #include <tempname.h>
44 }
45 
46 #include "dir-ops.h"
47 #include "file-ops.h"
48 #include "file-stat.h"
49 #include "oct-env.h"
50 #include "oct-locbuf.h"
51 #include "oct-passwd.h"
52 #include "pathlen.h"
53 #include "quit.h"
54 #include "singleton-cleanup.h"
55 #include "str-vec.h"
56 
58 
59 bool
61 {
62  bool retval = true;
63 
64  if (! instance)
65  {
66 #if (defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) && ! defined (OCTAVE_HAVE_POSIX_FILESYSTEM))
67  char system_dir_sep_char = '\\';
68  std::string system_dir_sep_str = "\\";
69 #else
70  char system_dir_sep_char = '/';
71  std::string system_dir_sep_str = "/";
72 #endif
73 #if defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM)
74  std::string system_dir_sep_chars = "/\\";
75 #else
76  std::string system_dir_sep_chars = system_dir_sep_str;
77 #endif
78 
79  instance = new file_ops (system_dir_sep_char, system_dir_sep_str,
80  system_dir_sep_chars);
81 
82  if (instance)
84  }
85 
86  if (! instance)
87  {
88  (*current_liboctave_error_handler)
89  ("unable to create file_ops object!");
90 
91  retval = false;
92  }
93 
94  return retval;
95 }
96 
97 // The following tilde-expansion code was stolen and adapted from
98 // readline.
99 
100 // The default value of tilde_additional_prefixes. This is set to
101 // whitespace preceding a tilde so that simple programs which do not
102 // perform any word separation get desired behaviour.
103 static const char *default_prefixes[] = { " ~", "\t~", ":~", 0 };
104 
105 // The default value of tilde_additional_suffixes. This is set to
106 // whitespace or newline so that simple programs which do not perform
107 // any word separation get desired behaviour.
108 static const char *default_suffixes[] = { " ", "\n", ":", 0 };
109 
110 // If non-null, this contains the address of a function that the
111 // application wants called before trying the standard tilde
112 // expansions. The function is called with the text sans tilde, and
113 // returns a malloc()'ed string which is the expansion, or a NULL
114 // pointer if the expansion fails.
116 
117 // If non-null, this contains the address of a function to call if the
118 // standard meaning for expanding a tilde fails. The function is
119 // called with the text (sans tilde, as in "foo"), and returns a
120 // malloc()'ed string which is the expansion, or a NULL pointer if
121 // there is no expansion.
123 
124 // When non-null, this is a NULL terminated array of strings which are
125 // duplicates for a tilde prefix. Bash uses this to expand '=~' and
126 // ':~'.
128 
129 // When non-null, this is a NULL terminated array of strings which
130 // match the end of a username, instead of just "/". Bash sets this
131 // to ':' and '=~'.
133 
134 // Find the start of a tilde expansion in S, and return the index
135 // of the tilde which starts the expansion. Place the length of the
136 // text which identified this tilde starter in LEN, excluding the
137 // tilde itself.
138 
139 static size_t
140 tilde_find_prefix (const std::string& s, size_t& len)
141 {
142  len = 0;
143 
144  size_t s_len = s.length ();
145 
146  if (s_len == 0 || s[0] == '~')
147  return 0;
148 
150 
151  if (! prefixes.empty ())
152  {
153  for (size_t i = 0; i < s_len; i++)
154  {
155  for (int j = 0; j < prefixes.length (); j++)
156  {
157  size_t pfx_len = prefixes[j].length ();
158 
159  if (prefixes[j].compare (s.substr (i, pfx_len)) == 0)
160  {
161  len = pfx_len - 1;
162  return i + len;
163  }
164  }
165  }
166  }
167 
168  return s_len;
169 }
170 
171 // Find the end of a tilde expansion in S, and return the index
172 // of the character which ends the tilde definition.
173 
174 static size_t
175 tilde_find_suffix (const std::string& s)
176 {
177  size_t s_len = s.length ();
178 
180 
181  size_t i = 0;
182 
183  for ( ; i < s_len; i++)
184  {
185  if (file_ops::is_dir_sep (s[i]))
186  break;
187 
188  if (! suffixes.empty ())
189  {
190  for (int j = 0; j < suffixes.length (); j++)
191  {
192  size_t sfx_len = suffixes[j].length ();
193 
194  if (suffixes[j].compare (s.substr (i, sfx_len)) == 0)
195  return i;
196  }
197  }
198  }
199 
200  return i;
201 }
202 
203 // Take FNAME and return the tilde prefix we want expanded.
204 
205 static std::string
206 isolate_tilde_prefix (const std::string& fname)
207 {
208  size_t f_len = fname.length ();
209 
210  size_t len = 1;
211 
212  while (len < f_len && ! file_ops::is_dir_sep (fname[len]))
213  len++;
214 
215  return fname.substr (1, len);
216 }
217 
218 // Do the work of tilde expansion on FILENAME. FILENAME starts with a
219 // tilde.
220 
221 static std::string
222 tilde_expand_word (const std::string& filename)
223 {
224  size_t f_len = filename.length ();
225 
226  if (f_len == 0 || filename[0] != '~')
227  return filename;
228 
229  // A leading '~/' or a bare '~' is *always* translated to the value
230  // of $HOME or the home directory of the current user, regardless of
231  // any preexpansion hook.
232 
233  if (f_len == 1 || file_ops::is_dir_sep (filename[1]))
234  return octave_env::get_home_directory () + filename.substr (1);
235 
236  std::string username = isolate_tilde_prefix (filename);
237 
238  size_t user_len = username.length ();
239 
240  std::string dirname;
241 
243  {
244  std::string expansion
246 
247  if (! expansion.empty ())
248  return expansion + filename.substr (user_len+1);
249  }
250 
251  // No preexpansion hook, or the preexpansion hook failed. Look in the
252  // password database.
253 
254  octave_passwd pw = octave_passwd::getpwnam (username);
255 
256  if (! pw)
257  {
258  // If the calling program has a special syntax for expanding tildes,
259  // and we couldn't find a standard expansion, then let them try.
260 
262  {
263  std::string expansion
265 
266  if (! expansion.empty ())
267  dirname = expansion + filename.substr (user_len+1);
268  }
269 
270  // If we don't have a failure hook, or if the failure hook did not
271  // expand the tilde, return a copy of what we were passed.
272 
273  if (dirname.length () == 0)
274  dirname = filename;
275  }
276  else
277  dirname = pw.dir () + filename.substr (user_len+1);
278 
279  return dirname;
280 }
281 
282 // If NAME has a leading ~ or ~user, Unix-style, expand it to the
283 // user's home directory. If no ~, or no <pwd.h>, just return NAME.
284 
285 std::string
286 file_ops::tilde_expand (const std::string& name)
287 {
288  if (name.find ('~') == std::string::npos)
289  return name;
290  else
291  {
292  std::string result;
293 
294  size_t name_len = name.length ();
295 
296  // Scan through S expanding tildes as we come to them.
297 
298  size_t pos = 0;
299 
300  while (1)
301  {
302  if (pos > name_len)
303  break;
304 
305  size_t len;
306 
307  // Make START point to the tilde which starts the expansion.
308 
309  size_t start = tilde_find_prefix (name.substr (pos), len);
310 
311  result.append (name.substr (pos, start));
312 
313  // Advance STRING to the starting tilde.
314 
315  pos += start;
316 
317  // Make FINI be the index of one after the last character of the
318  // username.
319 
320  size_t fini = tilde_find_suffix (name.substr (pos));
321 
322  // If both START and FINI are zero, we are all done.
323 
324  if (! (start || fini))
325  break;
326 
327  // Expand the entire tilde word, and copy it into RESULT.
328 
329  std::string tilde_word = name.substr (pos, fini);
330 
331  pos += fini;
332 
333  std::string expansion = tilde_expand_word (tilde_word);
334 
335  result.append (expansion);
336  }
337 
338  return result;
339  }
340 }
341 
342 // A vector version of the above.
343 
346 {
347  string_vector retval;
348 
349  int n = names.length ();
350 
351  retval.resize (n);
352 
353  for (int i = 0; i < n; i++)
354  retval[i] = tilde_expand (names[i]);
355 
356  return retval;
357 }
358 
359 std::string
360 file_ops::concat (const std::string& dir, const std::string& file)
361 {
362  return dir.empty ()
363  ? file
364  : (is_dir_sep (dir[dir.length ()-1])
365  ? dir + file
366  : dir + dir_sep_char () + file);
367 }
368 
369 std::string
370 file_ops::native_separator_path (const std::string& path)
371 {
372  std::string retval;
373 
374  if (dir_sep_char () == '/')
375  retval = path;
376  else
377  {
378  size_t n = path.length ();
379  for (size_t i = 0; i < n; i++)
380  {
381  if (path[i] == '/')
382  retval += dir_sep_char();
383  else
384  retval += path[i];
385  }
386  }
387 
388  return retval;
389 }
390 
391 int
392 octave_mkdir (const std::string& nm, mode_t md)
393 {
394  std::string msg;
395  return octave_mkdir (nm, md, msg);
396 }
397 
398 int
399 octave_mkdir (const std::string& name, mode_t mode, std::string& msg)
400 {
401  msg = std::string ();
402 
403  int status = -1;
404 
405  status = gnulib::mkdir (name.c_str (), mode);
406 
407  if (status < 0)
408  msg = gnulib::strerror (errno);
409 
410  return status;
411 }
412 
413 int
414 octave_mkfifo (const std::string& nm, mode_t md)
415 {
416  std::string msg;
417  return octave_mkfifo (nm, md, msg);
418 }
419 
420 int
421 octave_mkfifo (const std::string& name, mode_t mode, std::string& msg)
422 {
423  msg = std::string ();
424 
425  int status = -1;
426 
427  // With gnulib we will always have mkfifo, but some systems like MinGW
428  // don't have working mkfifo functions. On those systems, mkfifo will
429  // always return -1 and set errno.
430 
431  status = gnulib::mkfifo (name.c_str (), mode);
432 
433  if (status < 0)
434  msg = gnulib::strerror (errno);
435 
436  return status;
437 }
438 
439 int
440 octave_link (const std::string& old_name, const std::string& new_name)
441 {
442  std::string msg;
443  return octave_link (old_name, new_name, msg);
444 }
445 
446 int
447 octave_link (const std::string& old_name,
448  const std::string& new_name, std::string& msg)
449 {
450  msg = std::string ();
451 
452  int status = -1;
453 
454  status = gnulib::link (old_name.c_str (), new_name.c_str ());
455 
456  if (status < 0)
457  msg = gnulib::strerror (errno);
458 
459  return status;
460 }
461 
462 int
463 octave_symlink (const std::string& old_name, const std::string& new_name)
464 {
465  std::string msg;
466  return octave_symlink (old_name, new_name, msg);
467 }
468 
469 int
470 octave_symlink (const std::string& old_name,
471  const std::string& new_name, std::string& msg)
472 {
473  msg = std::string ();
474 
475  int status = -1;
476 
477  status = gnulib::symlink (old_name.c_str (), new_name.c_str ());
478 
479  if (status < 0)
480  msg = gnulib::strerror (errno);
481 
482  return status;
483 }
484 
485 int
486 octave_readlink (const std::string& path, std::string& result)
487 {
488  std::string msg;
489  return octave_readlink (path, result, msg);
490 }
491 
492 int
493 octave_readlink (const std::string& path, std::string& result,
494  std::string& msg)
495 {
496  int status = -1;
497 
498  msg = std::string ();
499 
500  char buf[MAXPATHLEN+1];
501 
502  status = gnulib::readlink (path.c_str (), buf, MAXPATHLEN);
503 
504  if (status < 0)
505  msg = gnulib::strerror (errno);
506  else
507  {
508  buf[status] = '\0';
509  result = std::string (buf);
510  status = 0;
511  }
512 
513  return status;
514 }
515 
516 int
517 octave_rename (const std::string& from, const std::string& to)
518 {
519  std::string msg;
520  return octave_rename (from, to, msg);
521 }
522 
523 int
524 octave_rename (const std::string& from, const std::string& to,
525  std::string& msg)
526 {
527  int status = -1;
528 
529  msg = std::string ();
530 
531  status = gnulib::rename (from.c_str (), to.c_str ());
532 
533  if (status < 0)
534  msg = gnulib::strerror (errno);
535 
536  return status;
537 }
538 
539 int
540 octave_rmdir (const std::string& name)
541 {
542  std::string msg;
543  return octave_rmdir (name, msg);
544 }
545 
546 int
547 octave_rmdir (const std::string& name, std::string& msg)
548 {
549  msg = std::string ();
550 
551  int status = -1;
552 
553  status = gnulib::rmdir (name.c_str ());
554 
555  if (status < 0)
556  msg = gnulib::strerror (errno);
557 
558  return status;
559 }
560 
561 // And a version that works recursively.
562 
563 int
564 octave_recursive_rmdir (const std::string& name)
565 {
566  std::string msg;
567  return octave_recursive_rmdir (name, msg);
568 }
569 
570 int
571 octave_recursive_rmdir (const std::string& name, std::string& msg)
572 {
573  msg = std::string ();
574 
575  int status = 0;
576 
577  dir_entry dir (name);
578 
579  if (dir)
580  {
581  string_vector dirlist = dir.read ();
582 
583  for (octave_idx_type i = 0; i < dirlist.length (); i++)
584  {
585  octave_quit ();
586 
587  std::string nm = dirlist[i];
588 
589  // Skip current directory and parent.
590  if (nm == "." || nm == "..")
591  continue;
592 
593  std::string fullnm = name + file_ops::dir_sep_str () + nm;
594 
595  // Get info about the file. Don't follow links.
596  file_stat fs (fullnm, false);
597 
598  if (fs)
599  {
600  if (fs.is_dir ())
601  {
602  status = octave_recursive_rmdir (fullnm, msg);
603 
604  if (status < 0)
605  break;
606  }
607  else
608  {
609  status = octave_unlink (fullnm, msg);
610 
611  if (status < 0)
612  break;
613  }
614  }
615  else
616  {
617  msg = fs.error ();
618  break;
619  }
620  }
621 
622  if (status >= 0)
623  {
624  dir.close ();
625  status = octave_rmdir (name, msg);
626  }
627  }
628  else
629  {
630  status = -1;
631 
632  msg = dir.error ();
633  }
634 
635  return status;
636 }
637 
638 int
640 {
641 #if defined (HAVE_UMASK)
642  return umask (mode);
643 #else
644  return 0;
645 #endif
646 }
647 
648 int
649 octave_unlink (const std::string& name)
650 {
651  std::string msg;
652  return octave_unlink (name, msg);
653 }
654 
655 int
656 octave_unlink (const std::string& name, std::string& msg)
657 {
658  msg = std::string ();
659 
660  int status = -1;
661 
662  status = gnulib::unlink (name.c_str ());
663 
664  if (status < 0)
665  msg = gnulib::strerror (errno);
666 
667  return status;
668 }
669 
670 std::string
671 octave_tempnam (const std::string& dir, const std::string& pfx)
672 {
673  std::string msg;
674  return octave_tempnam (dir, pfx, msg);
675 }
676 
677 std::string
678 octave_tempnam (const std::string& dir, const std::string& pfx,
679  std::string& msg)
680 {
681  msg = std::string ();
682 
683  std::string retval;
684 
685  // get dir path to use for template
686  std::string templatename;
687  if (dir.empty ())
688  templatename = octave_env::get_temp_directory ();
689  else if (! file_stat (dir, false).is_dir ())
690  templatename = octave_env::get_temp_directory ();
691  else
692  templatename = dir;
693 
694  // add dir sep char if it is not there
695  if (*templatename.rbegin () != file_ops::dir_sep_char ())
696  templatename += file_ops::dir_sep_char ();
697 
698  if (pfx.empty ())
699  templatename += "file";
700  else
701  templatename += pfx;
702 
703  // add the required XXXXXX for the template
704  templatename += "XXXXXX";
705 
706  // create and copy template to char array for call to gen_tempname
707  char tname [templatename.length () + 1];
708 
709  strcpy (tname, templatename.c_str ());
710 
711  if (gen_tempname (tname, 0, 0, GT_NOCREATE) == -1)
712  msg = gnulib::strerror (errno);
713  else
714  retval = tname;
715 
716  return retval;
717 }
718 
719 std::string
720 octave_canonicalize_file_name (const std::string& name)
721 {
722  std::string msg;
723  return octave_canonicalize_file_name (name, msg);
724 }
725 
726 std::string
727 octave_canonicalize_file_name (const std::string& name, std::string& msg)
728 {
729  msg = std::string ();
730 
731  std::string retval;
732 
733  char *tmp = gnulib::canonicalize_file_name (name.c_str ());
734 
735  if (tmp)
736  {
737  retval = tmp;
738  free (tmp);
739  }
740 
741  if (retval.empty ())
742  msg = gnulib::strerror (errno);
743 
744  return retval;
745 }
746 
int octave_unlink(const std::string &name)
Definition: file-ops.cc:649
int octave_rename(const std::string &from, const std::string &to)
Definition: file-ops.cc:517
std::string error(void) const
Definition: file-stat.h:136
static tilde_expansion_hook tilde_expansion_preexpansion_hook
Definition: file-ops.h:50
static std::string native_separator_path(const std::string &path)
Definition: file-ops.cc:370
string_vector read(void)
Definition: dir-ops.cc:70
int octave_mkfifo(const std::string &nm, mode_t md)
Definition: file-ops.cc:414
static size_t tilde_find_suffix(const std::string &s)
Definition: file-ops.cc:175
bool empty(void) const
Definition: str-vec.h:73
int octave_symlink(const std::string &old_name, const std::string &new_name)
Definition: file-ops.cc:463
int octave_link(const std::string &old_name, const std::string &new_name)
Definition: file-ops.cc:440
static string_vector names(const map_type &lst)
Definition: help.cc:782
int octave_mkdir(const std::string &nm, mode_t md)
Definition: file-ops.cc:392
std::string dir(void) const
Definition: oct-passwd.cc:85
static std::string tilde_expand(const std::string &)
Definition: file-ops.cc:286
static octave_idx_type link(octave_idx_type s, octave_idx_type t, octave_idx_type *pp)
Definition: colamd.cc:104
static std::string concat(const std::string &, const std::string &)
Definition: file-ops.cc:360
static tilde_expansion_hook tilde_expansion_failure_hook
Definition: file-ops.h:52
static const char * default_suffixes[]
Definition: file-ops.cc:108
static void add(fptr f)
void resize(octave_idx_type n, const std::string &rfv=std::string())
Definition: str-vec.h:91
#define MAXPATHLEN
Definition: pathlen.h:31
#define mode_t
Definition: statdefs.h:34
static const char * default_prefixes[]
Definition: file-ops.cc:103
static size_t tilde_find_prefix(const std::string &s, size_t &len)
Definition: file-ops.cc:140
static std::string tilde_expand_word(const std::string &filename)
Definition: file-ops.cc:222
void close(void)
Definition: dir-ops.cc:95
static void cleanup_instance(void)
Definition: file-ops.h:105
static file_ops * instance
Definition: file-ops.h:103
std::string octave_canonicalize_file_name(const std::string &name)
Definition: file-ops.cc:720
static std::string isolate_tilde_prefix(const std::string &fname)
Definition: file-ops.cc:206
static string_vector tilde_additional_suffixes
Definition: file-ops.h:56
int octave_readlink(const std::string &path, std::string &result)
Definition: file-ops.cc:486
int octave_umask(mode_t mode)
Definition: file-ops.cc:639
std::string error(void) const
Definition: dir-ops.h:71
octave_idx_type length(void) const
Number of elements in the array.
Definition: Array.h:267
std::string(* tilde_expansion_hook)(const std::string &)
Definition: file-ops.h:48
void free(void *)
static string_vector tilde_additional_prefixes
Definition: file-ops.h:54
static bool is_dir_sep(char c)
Definition: file-ops.h:73
int octave_rmdir(const std::string &name)
Definition: file-ops.cc:540
static bool instance_ok(void)
Definition: file-ops.cc:60
bool is_dir(void) const
Definition: file-stat.cc:56
Definition: dir-ops.h:30
static std::string dir_sep_str(void)
Definition: file-ops.h:63
static octave_passwd getpwnam(const std::string &nm)
Definition: oct-passwd.cc:141
static std::string get_temp_directory(void)
Definition: oct-env.cc:153
std::string octave_tempnam(const std::string &dir, const std::string &pfx)
Definition: file-ops.cc:671
int octave_recursive_rmdir(const std::string &name)
Definition: file-ops.cc:564
file_ops(char dir_sep_char_arg=0, const std::string &dir_sep_str_arg=std::string("/"), const std::string &dir_sep_chars_arg=std::string("/"))
Definition: file-ops.h:42
static char dir_sep_char(void)
Definition: file-ops.h:58
static std::string get_home_directory(void)
Definition: oct-env.cc:146