changelog shortlog tags changeset files revisions annotate raw

src/dirfns.cc

changeset 10289: 4b124317dc38
parent:4d433bd2d4dc
author: John W. Eaton <jwe@octave.org>
date: Tue Feb 09 20:58:55 2010 -0500 (65 minutes ago)
permissions: -rw-r--r--
description: base_properties::set_children: account for hidden children
1/*
2
3Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2002, 2003,
4 2004, 2005, 2006, 2007, 2008, 2009 John W. Eaton
5
6This file is part of Octave.
7
8Octave is free software; you can redistribute it and/or modify it
9under the terms of the GNU General Public License as published by the
10Free Software Foundation; either version 3 of the License, or (at your
11option) any later version.
12
13Octave is distributed in the hope that it will be useful, but WITHOUT
14ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License
19along with Octave; see the file COPYING. If not, see
20<http://www.gnu.org/licenses/>.
21
22*/
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <cerrno>
29#include <cstdio>
30#include <cstddef>
31#include <cstdlib>
32#include <cstring>
33
34#include <sstream>
35#include <string>
36
37#include <sys/types.h>
38#include <unistd.h>
39
40#include "file-ops.h"
41#include "file-stat.h"
42#include "glob-match.h"
43#include "oct-env.h"
44#include "pathsearch.h"
45#include "str-vec.h"
46
47#include "Cell.h"
48#include "defun.h"
49#include "dir-ops.h"
50#include "dirfns.h"
51#include "error.h"
52#include "gripes.h"
53#include "input.h"
54#include "load-path.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.
66static bool Vconfirm_recursive_rmdir = true;
67
68// The time we last time we changed directories.
69octave_time Vlast_chdir_time = 0.0;
70
71static int
72octave_change_to_directory (const std::string& newdir)
73{
74 int cd_ok = octave_env::chdir (file_ops::tilde_expand (newdir));
75
76 if (cd_ok)
77 {
78 Vlast_chdir_time.stamp ();
79
80 // FIXME -- should this be handled as a list of functions
81 // to call so users can add their own chdir handlers?
82
83 load_path::update ();
84 }
85 else
86 {
87 using namespace std;
88
89 error ("%s: %s", newdir.c_str (), strerror (errno));
90 }
91
92 return cd_ok;
93}
94
95DEFUN (cd, args, ,
96 "-*- texinfo -*-\n\
97@deffn {Command} cd dir\n\
98@deffnx {Command} chdir dir\n\
99Change the current working directory to @var{dir}. If @var{dir} is\n\
100omitted, the current directory is changed to the user's home\n\
101directory. For example,\n\
102\n\
103@example\n\
104cd ~/octave\n\
105@end example\n\
106\n\
107@noindent\n\
108Changes the current working directory to @file{~/octave}. If the\n\
109directory does not exist, an error message is printed and the working\n\
110directory is not changed.\n\
111@seealso{mkdir, rmdir, dir}\n\
112@end deffn")
113{
114 octave_value_list retval;
115
116 int argc = args.length () + 1;
117
118 string_vector argv = args.make_argv ("cd");
119
120 if (error_state)
121 return retval;
122
123 if (argc > 1)
124 {
125 std::string dirname = argv[1];
126
127 if (dirname.length () > 0
128 && ! octave_change_to_directory (dirname))
129 {
130 return retval;
131 }
132 }
133 else
134 {
135 std::string home_dir = octave_env::get_home_directory ();
136
137 if (home_dir.empty () || ! octave_change_to_directory (home_dir))
138 return retval;
139 }
140
141 return retval;
142}
143
144DEFALIAS (chdir, cd);
145
146DEFUN (pwd, , ,
147 "-*- texinfo -*-\n\
148@deftypefn {Built-in Function} {} pwd ()\n\
149Return the current working directory.\n\
150@seealso{dir, ls}\n\
151@end deftypefn")
152{
153 return octave_value (octave_env::get_current_directory ());
154}
155
156DEFUN (readdir, args, ,
157 "-*- texinfo -*-\n\
158@deftypefn {Built-in Function} {[@var{files}, @var{err}, @var{msg}] =} readdir (@var{dir})\n\
159Return names of the files in the directory @var{dir} as a cell array of\n\
160strings. If an error occurs, return an empty cell array in @var{files}.\n\
161\n\
162If successful, @var{err} is 0 and @var{msg} is an empty string.\n\
163Otherwise, @var{err} is nonzero and @var{msg} contains a\n\
164system-dependent error message.\n\
165@seealso{dir, glob}\n\
166@end deftypefn")
167{
168 octave_value_list retval;
169
170 retval(2) = std::string ();
171 retval(1) = -1.0;
172 retval(0) = Cell ();
173
174 if (args.length () == 1)
175 {
176 std::string dirname = args(0).string_value ();
177
178 if (error_state)
179 gripe_wrong_type_arg ("readdir", args(0));
180 else
181 {
182 dir_entry dir (dirname);
183
184 if (dir)
185 {
186 string_vector dirlist = dir.read ();
187 retval(0) = Cell (dirlist.sort ());
188 retval(1) = 0.0;
189 }
190 else
191 {
192 retval(2) = dir.error ();
193 }
194 }
195 }
196 else
197 print_usage ();
198
199 return retval;
200}
201
202// FIXME -- should maybe also allow second arg to specify
203// mode? OTOH, that might cause trouble with compatibility later...
204
205DEFUNX ("mkdir", Fmkdir, args, ,
206 "-*- texinfo -*-\n\
207@deftypefn {Built-in Function} {[@var{status}, @var{msg}, @var{msgid}] =} mkdir (@var{dir})\n\
208@deftypefnx {Built-in Function} {[@var{status}, @var{msg}, @var{msgid}] =} mkdir (@var{parent}, @var{dir})\n\
209Create a directory named @var{dir} in the directory @var{parent}.\n\
210\n\
211If successful, @var{status} is 1, with @var{msg} and @var{msgid} empty\n\
212character strings. Otherwise, @var{status} is 0, @var{msg} contains a\n\
213system-dependent error message, and @var{msgid} contains a unique\n\
214message identifier.\n\
215@seealso{rmdir}\n\
216@end deftypefn")
217{
218 octave_value_list retval;
219
220 retval(2) = std::string ();
221 retval(1) = std::string ();
222 retval(0) = false;
223
224 int nargin = args.length ();
225
226 std::string dirname;
227
228 if (nargin == 2)
229 {
230 std::string parent = args(0).string_value ();
231 std::string dir = args(1).string_value ();
232
233 if (error_state)
234 {
235 gripe_wrong_type_arg ("mkdir", args(0));
236 return retval;
237 }
238 else
239 dirname = file_ops::concat (parent, dir);
240 }
241 else if (nargin == 1)
242 {
243 dirname = args(0).string_value ();
244
245 if (error_state)
246 {
247 gripe_wrong_type_arg ("mkdir", args(0));
248 return retval;
249 }
250 }
251
252 if (nargin == 1 || nargin == 2)
253 {
254 std::string msg;
255
256 dirname = file_ops::tilde_expand (dirname);
257
258 file_stat fs (dirname);
259
260 if (fs && fs.is_dir ())
261 {
262 // For compatibility with Matlab, we return true when the
263 // directory already exists.
264
265 retval(2) = "mkdir";
266 retval(1) = "directory exists";
267 retval(0) = true;
268 }
269 else
270 {
271 int status = octave_mkdir (dirname, 0777, msg);
272
273 if (status < 0)
274 {
275 retval(2) = "mkdir";
276 retval(1) = msg;
277 }
278 else
279 retval(0) = true;
280 }
281 }
282 else
283 print_usage ();
284
285 return retval;
286}
287
288DEFUNX ("rmdir", Frmdir, args, ,
289 "-*- texinfo -*-\n\
290@deftypefn {Built-in Function} {[@var{status}, @var{msg}, @var{msgid}] =} rmdir (@var{dir})\n\
291@deftypefnx {Built-in Function} {[@var{status}, @var{msg}, @var{msgid}] =} rmdir (@var{dir}, @code{\"s\"})\n\
292Remove the directory named @var{dir}.\n\
293\n\
294If successful, @var{status} is 1, with @var{msg} and @var{msgid} empty\n\
295character strings. Otherwise, @var{status} is 0, @var{msg} contains a\n\
296system-dependent error message, and @var{msgid} contains a unique\n\
297message identifier.\n\
298\n\
299If the optional second parameter is supplied with value @code{\"s\"},\n\
300recursively remove all subdirectories as well.\n\
301@seealso{mkdir, confirm_recursive_rmdir}\n\
302@end deftypefn")
303{
304 octave_value_list retval;
305
306 retval(2) = std::string ();
307 retval(1) = std::string ();
308 retval(0) = false;
309
310 int nargin = args.length ();
311
312 if (nargin == 1 || nargin == 2)
313 {
314 std::string dirname = args(0).string_value ();
315
316 if (error_state)
317 gripe_wrong_type_arg ("rmdir", args(0));
318 else
319 {
320 std::string fulldir = file_ops::tilde_expand (dirname);
321 int status = -1;
322 std::string msg;
323
324 if (nargin == 2)
325 {
326 if (args(1).string_value () == "s")
327 {
328 bool doit = true;
329
330 if (interactive && Vconfirm_recursive_rmdir)
331 {
332 std::string prompt
333 = "remove entire contents of " + fulldir + "? ";
334
335 doit = octave_yes_or_no (prompt);
336 }
337
338 if (doit)
339 status = octave_recursive_rmdir (fulldir, msg);
340 }
341 else
342 error ("rmdir: expecting second argument to be \"s\"");
343 }
344 else
345 status = octave_rmdir (fulldir, msg);
346
347 if (status < 0)
348 {
349 retval(2) = "rmdir";
350 retval(1) = msg;
351 }
352 else
353 retval(0) = true;
354 }
355 }
356 else
357 print_usage ();
358
359 return retval;
360}
361
362DEFUNX ("link", Flink, args, ,
363 "-*- texinfo -*-\n\
364@deftypefn {Built-in Function} {[@var{err}, @var{msg}] =} link (@var{old}, @var{new})\n\
365Create a new link (also known as a hard link) to an existing file.\n\
366\n\
367If successful, @var{err} is 0 and @var{msg} is an empty string.\n\
368Otherwise, @var{err} is nonzero and @var{msg} contains a\n\
369system-dependent error message.\n\
370@seealso{symlink}\n\
371@end deftypefn")
372{
373 octave_value_list retval;
374
375 retval(1) = std::string ();
376 retval(0) = -1.0;
377
378 if (args.length () == 2)
379 {
380 std::string from = args(0).string_value ();
381
382 if (error_state)
383 gripe_wrong_type_arg ("link", args(0));
384 else
385 {
386 std::string to = args(1).string_value ();
387
388 if (error_state)
389 gripe_wrong_type_arg ("link", args(1));
390 else
391 {
392 std::string msg;
393
394 int status = octave_link (from, to, msg);
395
396 retval(0) = status;
397
398 if (status < 0)
399 retval(1) = msg;
400 }
401 }
402 }
403 else
404 print_usage ();
405
406 return retval;
407}
408
409DEFUNX ("symlink", Fsymlink, args, ,
410 "-*- texinfo -*-\n\
411@deftypefn {Built-in Function} {[@var{err}, @var{msg}] =} symlink (@var{old}, @var{new})\n\
412Create a symbolic link @var{new} which contains the string @var{old}.\n\
413\n\
414If successful, @var{err} is 0 and @var{msg} is an empty string.\n\
415Otherwise, @var{err} is nonzero and @var{msg} contains a\n\
416system-dependent error message.\n\
417@seealso{link, readlink}\n\
418@end deftypefn")
419{
420 octave_value_list retval;
421
422 retval(1) = std::string ();
423 retval(0) = -1.0;
424
425 if (args.length () == 2)
426 {
427 std::string from = args(0).string_value ();
428
429 if (error_state)
430 gripe_wrong_type_arg ("symlink", args(0));
431 else
432 {
433 std::string to = args(1).string_value ();
434
435 if (error_state)
436 gripe_wrong_type_arg ("symlink", args(1));
437 else
438 {
439 std::string msg;
440
441 int status = octave_symlink (from, to, msg);
442
443 retval(0) = status;
444
445 if (status < 0)
446 retval(1) = msg;
447 }
448 }
449 }
450 else
451 print_usage ();
452
453 return retval;
454}
455
456DEFUNX ("readlink", Freadlink, args, ,
457 "-*- texinfo -*-\n\
458@deftypefn {Built-in Function} {[@var{result}, @var{err}, @var{msg}] =} readlink (@var{symlink})\n\
459Read the value of the symbolic link @var{symlink}.\n\
460\n\
461If successful, @var{result} contains the contents of the symbolic link\n\
462@var{symlink}, @var{err} is 0 and @var{msg} is an empty string.\n\
463Otherwise, @var{err} is nonzero and @var{msg} contains a\n\
464system-dependent error message.\n\
465@seealso{link, symlink}\n\
466@end deftypefn")
467{
468 octave_value_list retval;
469
470 retval(2) = std::string ();
471 retval(1) = -1.0;
472 retval(0) = std::string ();
473
474 if (args.length () == 1)
475 {
476 std::string symlink = args(0).string_value ();
477
478 if (error_state)
479 gripe_wrong_type_arg ("readlink", args(0));
480 else
481 {
482 std::string result;
483 std::string msg;
484
485 int status = octave_readlink (symlink, result, msg);
486
487 retval(0) = result;
488
489 retval(1) = status;
490
491 if (status < 0)
492 retval(2) = msg;
493 }
494 }
495 else
496 print_usage ();
497
498 return retval;
499}
500
501DEFUNX ("rename", Frename, args, ,
502 "-*- texinfo -*-\n\
503@deftypefn {Built-in Function} {[@var{err}, @var{msg}] =} rename (@var{old}, @var{new})\n\
504Change the name of file @var{old} to @var{new}.\n\
505\n\
506If successful, @var{err} is 0 and @var{msg} is an empty string.\n\
507Otherwise, @var{err} is nonzero and @var{msg} contains a\n\
508system-dependent error message.\n\
509@seealso{ls, dir}\n\
510@end deftypefn")
511{
512 octave_value_list retval;
513
514 retval(1) = std::string ();
515 retval(0) = -1.0;
516
517 if (args.length () == 2)
518 {
519 std::string from = args(0).string_value ();
520
521 if (error_state)
522 gripe_wrong_type_arg ("rename", args(0));
523 else
524 {
525 std::string to = args(1).string_value ();
526
527 if (error_state)
528 gripe_wrong_type_arg ("rename", args(1));
529 else
530 {
531 std::string msg;
532
533 int status = octave_rename (from, to, msg);
534
535 retval(0) = status;
536
537 if (status < 0)
538 retval(1) = msg;
539 }
540 }
541 }
542 else
543 print_usage ();
544
545 return retval;
546}
547
548DEFUN (glob, args, ,
549 "-*- texinfo -*-\n\
550@deftypefn {Built-in Function} {} glob (@var{pattern})\n\
551Given an array of strings (as a char array or a cell array) in\n\
552@var{pattern}, return a cell array of file names that match any of\n\
553them, or an empty cell array if no patterns match. Tilde expansion\n\
554is performed on each of the patterns before looking for matching file\n\
555names. For example,\n\
556\n\
557@example\n\
558@group\n\
559glob (\"/vm*\")\n\
560 @result{} \"/vmlinuz\"\n\
561@end group\n\
562@end example\n\
563@seealso{dir, ls, stat, readdir}\n\
564@end deftypefn")
565{
566 octave_value retval;
567
568 if (args.length () == 1)
569 {
570 string_vector pat = args(0).all_strings ();
571
572 if (error_state)
573 gripe_wrong_type_arg ("glob", args(0));
574 else
575 {
576 glob_match pattern (file_ops::tilde_expand (pat));
577
578 retval = Cell (pattern.glob ());
579 }
580 }
581 else
582 print_usage ();
583
584 return retval;
585}
586
587DEFUN (fnmatch, args, ,
588 "-*- texinfo -*-\n\
589@deftypefn {Built-in Function} {} fnmatch (@var{pattern}, @var{string})\n\
590Return 1 or zero for each element of @var{string} that matches any of\n\
591the elements of the string array @var{pattern}, using the rules of\n\
592filename pattern matching. For example,\n\
593\n\
594@example\n\
595@group\n\
596fnmatch (\"a*b\", @{\"ab\"; \"axyzb\"; \"xyzab\"@})\n\
597 @result{} [ 1; 1; 0 ]\n\
598@end group\n\
599@end example\n\
600@end deftypefn")
601{
602 octave_value retval;
603
604 if (args.length () == 2)
605 {
606 string_vector pat = args(0).all_strings ();
607 string_vector str = args(1).all_strings ();
608
609 if (error_state)
610 gripe_wrong_type_arg ("fnmatch", args(0));
611 else
612 {
613 glob_match pattern (file_ops::tilde_expand (pat));
614
615 retval = pattern.match (str);
616 }
617 }
618 else
619 print_usage ();
620
621 return retval;
622}
623
624DEFUN (filesep, args, ,
625 "-*- texinfo -*-\n\
626@deftypefn {Built-in Function} {} filesep ()\n\
627@deftypefnx {Built-in Function} {} filesep ('all')\n\
628Return the system-dependent character used to separate directory names.\n\
629\n\
630If 'all' is given, the function return all valid file separators in\n\
631the form of a string. The list of file separators is system-dependent.\n\
632It is / (forward slash) under UNIX or Mac OS X, / and \\ (forward and\n\
633backward slashes) under Windows.\n\
634@seealso{pathsep, dir, ls}\n\
635@end deftypefn")
636{
637 octave_value retval;
638
639 if (args.length () == 0)
640 retval = file_ops::dir_sep_str ();
641 else if (args.length () == 1)
642 {
643 std::string s = args(0).string_value ();
644
645 if (! error_state)
646 {
647 if (s == "all")
648 retval = file_ops::dir_sep_chars ();
649 else
650 gripe_wrong_type_arg ("filesep", args(0));
651 }
652 else
653 gripe_wrong_type_arg ("filesep", args(0));
654 }
655 else
656 print_usage ();
657
658 return retval;
659}
660
661DEFUN (pathsep, args, nargout,
662 "-*- texinfo -*-\n\
663@deftypefn {Built-in Function} {@var{val} =} pathsep ()\n\
664@deftypefnx {Built-in Function} {@var{old_val} =} pathsep (@var{new_val})\n\
665Query or set the character used to separate directories in\n\
666a path.\n\
667@seealso{filesep, dir, ls}\n\
668@end deftypefn")
669{
670 octave_value retval;
671
672 int nargin = args.length ();
673
674 if (nargout > 0 || nargin == 0)
675 retval = dir_path::path_sep_str ();
676
677 if (nargin == 1)
678 {
679 std::string sval = args(0).string_value ();
680
681 if (! error_state)
682 {
683 switch (sval.length ())
684 {
685 case 1:
686 dir_path::path_sep_char (sval[0]);
687 break;
688
689 case 0:
690 dir_path::path_sep_char ('\0');
691 break;
692
693 default:
694 error ("pathsep: argument must be a single character");
695 break;
696 }
697 }
698 else
699 error ("pathsep: argument must be a single character");
700 }
701 else if (nargin > 1)
702 print_usage ();
703
704 return retval;
705}
706
707DEFUN (confirm_recursive_rmdir, args, nargout,
708 "-*- texinfo -*-\n\
709@deftypefn {Built-in Function} {@var{val} =} confirm_recursive_rmdir ()\n\
710@deftypefnx {Built-in Function} {@var{old_val} =} confirm_recursive_rmdir (@var{new_val})\n\
711Query or set the internal variable that controls whether Octave\n\
712will ask for confirmation before recursively removing a directory tree.\n\
713@end deftypefn")
714{
715 return SET_INTERNAL_VARIABLE (confirm_recursive_rmdir);
716}