GNU Octave  3.8.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
dirfns.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 1994-2013 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 <cstddef>
30 #include <cstdlib>
31 #include <cstring>
32 
33 #include <sstream>
34 #include <string>
35 
36 #include <sys/types.h>
37 #include <unistd.h>
38 
39 #include "file-ops.h"
40 #include "file-stat.h"
41 #include "glob-match.h"
42 #include "oct-env.h"
43 #include "pathsearch.h"
44 #include "str-vec.h"
45 
46 #include "Cell.h"
47 #include "defun.h"
48 #include "dir-ops.h"
49 #include "dirfns.h"
50 #include "error.h"
51 #include "gripes.h"
52 #include "input.h"
53 #include "load-path.h"
54 #include "octave-link.h"
55 #include "oct-obj.h"
56 #include "pager.h"
57 #include "procstream.h"
58 #include "sysdep.h"
59 #include "toplev.h"
60 #include "unwind-prot.h"
61 #include "utils.h"
62 #include "variables.h"
63 
64 // TRUE means we ask for confirmation before recursively removing a
65 // directory tree.
66 static bool Vconfirm_recursive_rmdir = true;
67 
68 // The time we last time we changed directories.
70 
71 static int
72 octave_change_to_directory (const std::string& newdir)
73 {
74  std::string xdir = file_ops::tilde_expand (newdir);
75 
76  int cd_ok = octave_env::chdir (xdir);
77 
78  if (cd_ok)
79  {
80  Vlast_chdir_time.stamp ();
81 
82  // FIXME: should these actions be handled as a list of functions
83  // to call so users can add their own chdir handlers?
84 
86 
88  }
89  else
90  error ("%s: %s", newdir.c_str (), gnulib::strerror (errno));
91 
92  return cd_ok;
93 }
94 
95 DEFUN (cd, args, nargout,
96  "-*- texinfo -*-\n\
97 @deftypefn {Command} {} cd @var{dir}\n\
98 @deftypefnx {Command} {} cd\n\
99 @deftypefnx {Built-in Function} {@var{old_dir} =} cd @var{dir}\n\
100 @deftypefnx {Command} {} chdir @dots{}\n\
101 Change the current working directory to @var{dir}.\n\
102 \n\
103 If @var{dir} is omitted, the current directory is changed to the user's home\n\
104 directory (@qcode{\"~\"}).\n\
105 \n\
106 For example,\n\
107 \n\
108 @example\n\
109 cd ~/octave\n\
110 @end example\n\
111 \n\
112 @noindent\n\
113 changes the current working directory to @file{~/octave}. If the\n\
114 directory does not exist, an error message is printed and the working\n\
115 directory is not changed.\n\
116 \n\
117 @code{chdir} is an alias for @code{cd} and can be used in all of the same\n\
118 calling formats.\n\
119 \n\
120 Compatibility Note: When called with no arguments, @sc{matlab} prints the\n\
121 present working directory rather than changing to the user's home directory.\n\
122 @seealso{pwd, mkdir, rmdir, dir, ls}\n\
123 @end deftypefn")
124 {
125  octave_value_list retval;
126 
127  int argc = args.length () + 1;
128 
129  string_vector argv = args.make_argv ("cd");
130 
131  if (error_state)
132  return retval;
133 
134  if (nargout > 0)
136 
137  if (argc > 1)
138  {
139  std::string dirname = argv[1];
140 
141  if (dirname.length () > 0)
142  octave_change_to_directory (dirname);
143  }
144  else
145  {
146  std::string home_dir = octave_env::get_home_directory ();
147 
148  if (! home_dir.empty ())
149  octave_change_to_directory (home_dir);
150  }
151 
152  return retval;
153 }
154 
155 DEFALIAS (chdir, cd);
156 
157 DEFUN (pwd, , ,
158  "-*- texinfo -*-\n\
159 @deftypefn {Built-in Function} {} pwd ()\n\
160 @deftypefnx {Built-in Function} {@var{dir} =} pwd ()\n\
161 Return the current working directory.\n\
162 @seealso{cd, dir, ls, mkdir, rmdir}\n\
163 @end deftypefn")
164 {
166 }
167 
168 DEFUN (readdir, args, ,
169  "-*- texinfo -*-\n\
170 @deftypefn {Built-in Function} {@var{files} =} readdir (@var{dir})\n\
171 @deftypefnx {Built-in Function} {[@var{files}, @var{err}, @var{msg}] =} readdir (@var{dir})\n\
172 Return the names of files in the directory @var{dir} as a cell array of\n\
173 strings.\n\
174 \n\
175 If an error occurs, return an empty cell array in @var{files}.\n\
176 If successful, @var{err} is 0 and @var{msg} is an empty string.\n\
177 Otherwise, @var{err} is nonzero and @var{msg} contains a\n\
178 system-dependent error message.\n\
179 @seealso{ls, dir, glob, what}\n\
180 @end deftypefn")
181 {
182  octave_value_list retval;
183 
184  retval(2) = std::string ();
185  retval(1) = -1.0;
186  retval(0) = Cell ();
187 
188  if (args.length () == 1)
189  {
190  std::string dirname = args(0).string_value ();
191 
192  if (error_state)
193  gripe_wrong_type_arg ("readdir", args(0));
194  else
195  {
196  dir_entry dir (dirname);
197 
198  if (dir)
199  {
200  string_vector dirlist = dir.read ();
201  retval(1) = 0.0;
202  retval(0) = Cell (dirlist.sort ());
203  }
204  else
205  {
206  retval(2) = dir.error ();
207  }
208  }
209  }
210  else
211  print_usage ();
212 
213  return retval;
214 }
215 
216 // FIXME: should maybe also allow second arg to specify mode?
217 // OTOH, that might cause trouble with compatibility later...
218 
219 DEFUNX ("mkdir", Fmkdir, args, ,
220  "-*- texinfo -*-\n\
221 @deftypefn {Built-in Function} {} mkdir @var{dir}\n\
222 @deftypefnx {Built-in Function} {} mkdir (@var{parent}, @var{dir})\n\
223 @deftypefnx {Built-in Function} {[@var{status}, @var{msg}, @var{msgid}] =} mkdir (@dots{})\n\
224 Create a directory named @var{dir} in the directory @var{parent}.\n\
225 \n\
226 If no @var{parent} directory is specified the present working directory is\n\
227 used.\n\
228 \n\
229 If successful, @var{status} is 1, and @var{msg}, @var{msgid} are empty\n\
230 character strings (""). Otherwise, @var{status} is 0, @var{msg} contains a\n\
231 system-dependent error message, and @var{msgid} contains a unique message\n\
232 identifier.\n\
233 @seealso{rmdir, pwd, cd}\n\
234 @end deftypefn")
235 {
236  octave_value_list retval;
237 
238  retval(2) = std::string ();
239  retval(1) = std::string ();
240  retval(0) = false;
241 
242  int nargin = args.length ();
243 
244  std::string dirname;
245 
246  if (nargin == 2)
247  {
248  std::string parent = args(0).string_value ();
249  std::string dir = args(1).string_value ();
250 
251  if (error_state)
252  {
253  gripe_wrong_type_arg ("mkdir", args(0));
254  return retval;
255  }
256  else
257  dirname = file_ops::concat (parent, dir);
258  }
259  else if (nargin == 1)
260  {
261  dirname = args(0).string_value ();
262 
263  if (error_state)
264  {
265  gripe_wrong_type_arg ("mkdir", args(0));
266  return retval;
267  }
268  }
269 
270  if (nargin == 1 || nargin == 2)
271  {
272  std::string msg;
273 
274  dirname = file_ops::tilde_expand (dirname);
275 
276  file_stat fs (dirname);
277 
278  if (fs && fs.is_dir ())
279  {
280  // For compatibility with Matlab, we return true when the
281  // directory already exists.
282 
283  retval(2) = "mkdir";
284  retval(1) = "directory exists";
285  retval(0) = true;
286  }
287  else
288  {
289  int status = octave_mkdir (dirname, 0777, msg);
290 
291  if (status < 0)
292  {
293  retval(2) = "mkdir";
294  retval(1) = msg;
295  }
296  else
297  retval(0) = true;
298  }
299  }
300  else
301  print_usage ();
302 
303  return retval;
304 }
305 
306 DEFUNX ("rmdir", Frmdir, args, ,
307  "-*- texinfo -*-\n\
308 @deftypefn {Built-in Function} {} rmdir @var{dir}\n\
309 @deftypefnx {Built-in Function} {} rmdir (@var{dir}, \"s\")\n\
310 @deftypefnx {Built-in Function} {[@var{status}, @var{msg}, @var{msgid}] =} rmdir (@dots{})\n\
311 Remove the directory named @var{dir}.\n\
312 \n\
313 If successful, @var{status} is 1, and @var{msg}, @var{msgid} are empty\n\
314 character strings (""). Otherwise, @var{status} is 0, @var{msg} contains a\n\
315 system-dependent error message, and @var{msgid} contains a unique message\n\
316 identifier.\n\
317 \n\
318 If the optional second parameter is supplied with value @qcode{\"s\"},\n\
319 recursively remove all subdirectories as well.\n\
320 @seealso{mkdir, confirm_recursive_rmdir, pwd}\n\
321 @end deftypefn")
322 {
323  octave_value_list retval;
324 
325  retval(2) = std::string ();
326  retval(1) = std::string ();
327  retval(0) = false;
328 
329  int nargin = args.length ();
330 
331  if (nargin == 1 || nargin == 2)
332  {
333  std::string dirname = args(0).string_value ();
334 
335  if (error_state)
336  gripe_wrong_type_arg ("rmdir", args(0));
337  else
338  {
339  std::string fulldir = file_ops::tilde_expand (dirname);
340  int status = -1;
341  std::string msg;
342 
343  if (nargin == 2)
344  {
345  if (args(1).string_value () == "s")
346  {
347  bool doit = true;
348 
349  if (interactive && Vconfirm_recursive_rmdir)
350  {
351  std::string prompt
352  = "remove entire contents of " + fulldir + "? ";
353 
354  doit = octave_yes_or_no (prompt);
355  }
356 
357  if (doit)
358  status = octave_recursive_rmdir (fulldir, msg);
359  }
360  else
361  error ("rmdir: expecting second argument to be \"s\"");
362  }
363  else
364  status = octave_rmdir (fulldir, msg);
365 
366  if (status < 0)
367  {
368  retval(2) = "rmdir";
369  retval(1) = msg;
370  }
371  else
372  retval(0) = true;
373  }
374  }
375  else
376  print_usage ();
377 
378  return retval;
379 }
380 
381 DEFUNX ("link", Flink, args, ,
382  "-*- texinfo -*-\n\
383 @deftypefn {Built-in Function} {} link @var{old} @var{new}\n\
384 @deftypefnx {Built-in Function} {[@var{err}, @var{msg}] =} link (@var{old}, @var{new})\n\
385 Create a new link (also known as a hard link) to an existing file.\n\
386 \n\
387 If successful, @var{err} is 0 and @var{msg} is an empty string.\n\
388 Otherwise, @var{err} is nonzero and @var{msg} contains a\n\
389 system-dependent error message.\n\
390 @seealso{symlink, unlink, readlink, lstat}\n\
391 @end deftypefn")
392 {
393  octave_value_list retval;
394 
395  retval(1) = std::string ();
396  retval(0) = -1.0;
397 
398  if (args.length () == 2)
399  {
400  std::string from = args(0).string_value ();
401 
402  if (error_state)
403  gripe_wrong_type_arg ("link", args(0));
404  else
405  {
406  std::string to = args(1).string_value ();
407 
408  if (error_state)
409  gripe_wrong_type_arg ("link", args(1));
410  else
411  {
412  std::string msg;
413 
414  int status = octave_link (from, to, msg);
415 
416  if (status < 0)
417  retval(1) = msg;
418  retval(0) = status;
419  }
420  }
421  }
422  else
423  print_usage ();
424 
425  return retval;
426 }
427 
428 DEFUNX ("symlink", Fsymlink, args, ,
429  "-*- texinfo -*-\n\
430 @deftypefn {Built-in Function} {} symlink @var{old} @var{new}\n\
431 @deftypefnx {Built-in Function} {[@var{err}, @var{msg}] =} symlink (@var{old}, @var{new})\n\
432 Create a symbolic link @var{new} which contains the string @var{old}.\n\
433 \n\
434 If successful, @var{err} is 0 and @var{msg} is an empty string.\n\
435 Otherwise, @var{err} is nonzero and @var{msg} contains a\n\
436 system-dependent error message.\n\
437 @seealso{link, unlink, readlink, lstat}\n\
438 @end deftypefn")
439 {
440  octave_value_list retval;
441 
442  retval(1) = std::string ();
443  retval(0) = -1.0;
444 
445  if (args.length () == 2)
446  {
447  std::string from = args(0).string_value ();
448 
449  if (error_state)
450  gripe_wrong_type_arg ("symlink", args(0));
451  else
452  {
453  std::string to = args(1).string_value ();
454 
455  if (error_state)
456  gripe_wrong_type_arg ("symlink", args(1));
457  else
458  {
459  std::string msg;
460 
461  int status = octave_symlink (from, to, msg);
462 
463  if (status < 0)
464  retval(1) = msg;
465  retval(0) = status;
466  }
467  }
468  }
469  else
470  print_usage ();
471 
472  return retval;
473 }
474 
475 DEFUNX ("readlink", Freadlink, args, ,
476  "-*- texinfo -*-\n\
477 @deftypefn {Built-in Function} {} readlink @var{symlink}\n\
478 @deftypefnx {Built-in Function} {[@var{result}, @var{err}, @var{msg}] =} readlink (@var{symlink})\n\
479 Read the value of the symbolic link @var{symlink}.\n\
480 \n\
481 If successful, @var{result} contains the contents of the symbolic link\n\
482 @var{symlink}, @var{err} is 0, and @var{msg} is an empty string.\n\
483 Otherwise, @var{err} is nonzero and @var{msg} contains a system-dependent\n\
484 error message.\n\
485 @seealso{lstat, symlink, link, unlink, delete}\n\
486 @end deftypefn")
487 {
488  octave_value_list retval;
489 
490  retval(2) = std::string ();
491  retval(1) = -1.0;
492  retval(0) = std::string ();
493 
494  if (args.length () == 1)
495  {
496  std::string symlink = args(0).string_value ();
497 
498  if (error_state)
499  gripe_wrong_type_arg ("readlink", args(0));
500  else
501  {
502  std::string result;
503  std::string msg;
504 
505  int status = octave_readlink (symlink, result, msg);
506 
507  if (status < 0)
508  retval(2) = msg;
509  retval(1) = status;
510  retval(0) = result;
511  }
512  }
513  else
514  print_usage ();
515 
516  return retval;
517 }
518 
519 DEFUNX ("rename", Frename, args, ,
520  "-*- texinfo -*-\n\
521 @deftypefn {Built-in Function} {} rename @var{old} @var{new}\n\
522 @deftypefnx {Built-in Function} {[@var{err}, @var{msg}] =} rename (@var{old}, @var{new})\n\
523 Change the name of file @var{old} to @var{new}.\n\
524 \n\
525 If successful, @var{err} is 0 and @var{msg} is an empty string.\n\
526 Otherwise, @var{err} is nonzero and @var{msg} contains a\n\
527 system-dependent error message.\n\
528 @seealso{movefile, copyfile, ls, dir}\n\
529 @end deftypefn")
530 {
531  octave_value_list retval;
532 
533  retval(1) = std::string ();
534  retval(0) = -1.0;
535 
536  if (args.length () == 2)
537  {
538  std::string from = args(0).string_value ();
539 
540  if (error_state)
541  gripe_wrong_type_arg ("rename", args(0));
542  else
543  {
544  std::string to = args(1).string_value ();
545 
546  if (error_state)
547  gripe_wrong_type_arg ("rename", args(1));
548  else
549  {
550  std::string msg;
551 
552  int status = octave_rename (from, to, msg);
553 
554  if (status < 0)
555  retval(1) = msg;
556  retval(0) = status;
557  }
558  }
559  }
560  else
561  print_usage ();
562 
563  return retval;
564 }
565 
566 DEFUN (glob, args, ,
567  "-*- texinfo -*-\n\
568 @deftypefn {Built-in Function} {} glob (@var{pattern})\n\
569 Given an array of pattern strings (as a char array or a cell array) in\n\
570 @var{pattern}, return a cell array of file names that match any of\n\
571 them, or an empty cell array if no patterns match. The pattern strings are\n\
572 interpreted as filename globbing patterns (as they are used by Unix shells).\n\
573 Within a pattern\n\
574 \n\
575 @table @code\n\
576 @item *\n\
577 matches any string, including the null string,\n\
578 \n\
579 @item ?\n\
580 matches any single character, and\n\
581 \n\
582 @item [@dots{}]\n\
583 matches any of the enclosed characters.\n\
584 @end table\n\
585 \n\
586 Tilde expansion is performed on each of the patterns before looking for\n\
587 matching file names. For example:\n\
588 \n\
589 @example\n\
590 ls\n\
591  @result{}\n\
592  file1 file2 file3 myfile1 myfile1b\n\
593 glob (\"*file1\")\n\
594  @result{}\n\
595  @{\n\
596  [1,1] = file1\n\
597  [2,1] = myfile1\n\
598  @}\n\
599 glob (\"myfile?\")\n\
600  @result{}\n\
601  @{\n\
602  [1,1] = myfile1\n\
603  @}\n\
604 glob (\"file[12]\")\n\
605  @result{}\n\
606  @{\n\
607  [1,1] = file1\n\
608  [2,1] = file2\n\
609  @}\n\
610 @end example\n\
611 @seealso{ls, dir, readdir, what, fnmatch}\n\
612 @end deftypefn")
613 {
614  octave_value retval;
615 
616  if (args.length () == 1)
617  {
618  string_vector pat = args(0).all_strings ();
619 
620  if (error_state)
621  gripe_wrong_type_arg ("glob", args(0));
622  else
623  {
624  glob_match pattern (file_ops::tilde_expand (pat));
625 
626  retval = Cell (pattern.glob ());
627  }
628  }
629  else
630  print_usage ();
631 
632  return retval;
633 }
634 
635 /*
636 %!test
637 %! tmpdir = tmpnam;
638 %! filename = {"file1", "file2", "file3", "myfile1", "myfile1b"};
639 %! if (mkdir (tmpdir))
640 %! cwd = pwd;
641 %! cd (tmpdir);
642 %! if (strcmp (canonicalize_file_name (pwd), canonicalize_file_name (tmpdir)))
643 %! a = 0;
644 %! for n = 1:5
645 %! save (filename{n}, "a");
646 %! endfor
647 %! else
648 %! rmdir (tmpdir);
649 %! error ("Couldn't change to temporary dir");
650 %! endif
651 %! else
652 %! error ("Couldn't create temporary directory");
653 %! endif
654 %! result1 = glob ("*file1");
655 %! result2 = glob ("myfile?");
656 %! result3 = glob ("file[12]");
657 %! for n = 1:5
658 %! delete (filename{n});
659 %! endfor
660 %! cd (cwd);
661 %! rmdir (tmpdir);
662 %! assert (result1, {"file1"; "myfile1"});
663 %! assert (result2, {"myfile1"});
664 %! assert (result3, {"file1"; "file2"});
665 */
666 
667 DEFUNX ("fnmatch", Ffnmatch, args, ,
668  "-*- texinfo -*-\n\
669 @deftypefn {Built-in Function} {} fnmatch (@var{pattern}, @var{string})\n\
670 Return true or false for each element of @var{string} that matches any of\n\
671 the elements of the string array @var{pattern}, using the rules of\n\
672 filename pattern matching. For example:\n\
673 \n\
674 @example\n\
675 @group\n\
676 fnmatch (\"a*b\", @{\"ab\"; \"axyzb\"; \"xyzab\"@})\n\
677  @result{} [ 1; 1; 0 ]\n\
678 @end group\n\
679 @end example\n\
680 @seealso{glob, regexp}\n\
681 @end deftypefn")
682 {
683  octave_value retval;
684 
685  if (args.length () == 2)
686  {
687  string_vector pat = args(0).all_strings ();
688  string_vector str = args(1).all_strings ();
689 
690  if (error_state)
691  gripe_wrong_type_arg ("fnmatch", args(0));
692  else
693  {
694  glob_match pattern (file_ops::tilde_expand (pat));
695 
696  retval = pattern.match (str);
697  }
698  }
699  else
700  print_usage ();
701 
702  return retval;
703 }
704 
705 DEFUN (filesep, args, ,
706  "-*- texinfo -*-\n\
707 @deftypefn {Built-in Function} {} filesep ()\n\
708 @deftypefnx {Built-in Function} {} filesep (\"all\")\n\
709 Return the system-dependent character used to separate directory names.\n\
710 \n\
711 If @qcode{\"all\"} is given, the function returns all valid file separators\n\
712 in the form of a string. The list of file separators is system-dependent.\n\
713 It is @samp{/} (forward slash) under UNIX or @w{Mac OS X}, @samp{/} and\n\
714 @samp{\\} (forward and backward slashes) under Windows.\n\
715 @seealso{pathsep}\n\
716 @end deftypefn")
717 {
718  octave_value retval;
719 
720  if (args.length () == 0)
721  retval = file_ops::dir_sep_str ();
722  else if (args.length () == 1)
723  {
724  std::string s = args(0).string_value ();
725 
726  if (! error_state)
727  {
728  if (s == "all")
729  retval = file_ops::dir_sep_chars ();
730  else
731  gripe_wrong_type_arg ("filesep", args(0));
732  }
733  else
734  gripe_wrong_type_arg ("filesep", args(0));
735  }
736  else
737  print_usage ();
738 
739  return retval;
740 }
741 
742 DEFUN (pathsep, args, nargout,
743  "-*- texinfo -*-\n\
744 @deftypefn {Built-in Function} {@var{val} =} pathsep ()\n\
745 @deftypefnx {Built-in Function} {@var{old_val} =} pathsep (@var{new_val})\n\
746 Query or set the character used to separate directories in a path.\n\
747 @seealso{filesep}\n\
748 @end deftypefn")
749 {
750  octave_value retval;
751 
752  int nargin = args.length ();
753 
754  if (nargout > 0 || nargin == 0)
755  retval = dir_path::path_sep_str ();
756 
757  if (nargin == 1)
758  {
759  std::string sval = args(0).string_value ();
760 
761  if (! error_state)
762  {
763  switch (sval.length ())
764  {
765  case 1:
766  dir_path::path_sep_char (sval[0]);
767  break;
768 
769  case 0:
771  break;
772 
773  default:
774  error ("pathsep: argument must be a single character");
775  break;
776  }
777  }
778  else
779  error ("pathsep: argument must be a single character");
780  }
781  else if (nargin > 1)
782  print_usage ();
783 
784  return retval;
785 }
786 
787 DEFUN (confirm_recursive_rmdir, args, nargout,
788  "-*- texinfo -*-\n\
789 @deftypefn {Built-in Function} {@var{val} =} confirm_recursive_rmdir ()\n\
790 @deftypefnx {Built-in Function} {@var{old_val} =} confirm_recursive_rmdir (@var{new_val})\n\
791 @deftypefnx {Built-in Function} {} confirm_recursive_rmdir (@var{new_val}, \"local\")\n\
792 Query or set the internal variable that controls whether Octave\n\
793 will ask for confirmation before recursively removing a directory tree.\n\
794 \n\
795 When called from inside a function with the @qcode{\"local\"} option, the\n\
796 variable is changed locally for the function and any subroutines it calls. \n\
797 The original variable value is restored when exiting the function.\n\
798 @seealso{rmdir}\n\
799 @end deftypefn")
800 {
801  return SET_INTERNAL_VARIABLE (confirm_recursive_rmdir);
802 }