file-ops.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 1996-2012 John W. Eaton
00004 
00005 This file is part of Octave.
00006 
00007 Octave is free software; you can redistribute it and/or modify it
00008 under the terms of the GNU General Public License as published by the
00009 Free Software Foundation; either version 3 of the License, or (at your
00010 option) any later version.
00011 
00012 Octave is distributed in the hope that it will be useful, but WITHOUT
00013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00015 for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Octave; see the file COPYING.  If not, see
00019 <http://www.gnu.org/licenses/>.
00020 
00021 */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include <config.h>
00025 #endif
00026 
00027 #include <cerrno>
00028 #include <cstdio>
00029 #include <cstdlib>
00030 #include <cstring>
00031 
00032 #include <iostream>
00033 #include <vector>
00034 
00035 #include <sys/stat.h>
00036 #include <sys/types.h>
00037 #include <unistd.h>
00038 
00039 #include "pathmax.h"
00040 
00041 #include "dir-ops.h"
00042 #include "file-ops.h"
00043 #include "file-stat.h"
00044 #include "oct-env.h"
00045 #include "oct-locbuf.h"
00046 #include "oct-passwd.h"
00047 #include "pathlen.h"
00048 #include "quit.h"
00049 #include "singleton-cleanup.h"
00050 #include "str-vec.h"
00051 
00052 file_ops *file_ops::instance = 0;
00053 
00054 bool
00055 file_ops::instance_ok (void)
00056 {
00057   bool retval = true;
00058 
00059   if (! instance)
00060     {
00061 #if (defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) && ! defined (OCTAVE_HAVE_POSIX_FILESYSTEM))
00062       char system_dir_sep_char = '\\';
00063       std::string system_dir_sep_str = "\\";
00064 #else
00065       char system_dir_sep_char = '/';
00066       std::string system_dir_sep_str = "/";
00067 #endif
00068 #if defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM)
00069       std::string system_dir_sep_chars = "/\\";
00070 #else
00071       std::string system_dir_sep_chars = system_dir_sep_str;
00072 #endif
00073 
00074       instance = new file_ops (system_dir_sep_char, system_dir_sep_str,
00075                                system_dir_sep_chars);
00076 
00077       if (instance)
00078         singleton_cleanup_list::add (cleanup_instance);
00079     }
00080 
00081   if (! instance)
00082     {
00083       (*current_liboctave_error_handler)
00084         ("unable to create file_ops object!");
00085 
00086       retval = false;
00087     }
00088 
00089   return retval;
00090 }
00091 
00092 // The following tilde-expansion code was stolen and adapted from
00093 // readline.
00094 
00095 // The default value of tilde_additional_prefixes.  This is set to
00096 // whitespace preceding a tilde so that simple programs which do not
00097 // perform any word separation get desired behaviour.
00098 static const char *default_prefixes[] = { " ~", "\t~", ":~", 0 };
00099 
00100 // The default value of tilde_additional_suffixes.  This is set to
00101 // whitespace or newline so that simple programs which do not perform
00102 // any word separation get desired behaviour.
00103 static const char *default_suffixes[] = { " ", "\n", ":", 0 };
00104 
00105 // If non-null, this contains the address of a function that the
00106 // application wants called before trying the standard tilde
00107 // expansions.  The function is called with the text sans tilde, and
00108 // returns a malloc()'ed string which is the expansion, or a NULL
00109 // pointer if the expansion fails.
00110 file_ops::tilde_expansion_hook file_ops::tilde_expansion_preexpansion_hook = 0;
00111 
00112 // If non-null, this contains the address of a function to call if the
00113 // standard meaning for expanding a tilde fails.  The function is
00114 // called with the text (sans tilde, as in "foo"), and returns a
00115 // malloc()'ed string which is the expansion, or a NULL pointer if
00116 // there is no expansion.
00117 file_ops::tilde_expansion_hook file_ops::tilde_expansion_failure_hook = 0;
00118 
00119 // When non-null, this is a NULL terminated array of strings which are
00120 // duplicates for a tilde prefix.  Bash uses this to expand '=~' and
00121 // ':~'.
00122 string_vector file_ops::tilde_additional_prefixes = default_prefixes;
00123 
00124 // When non-null, this is a NULL terminated array of strings which
00125 // match the end of a username, instead of just "/".  Bash sets this
00126 // to ':' and '=~'.
00127 string_vector file_ops::tilde_additional_suffixes = default_suffixes;
00128 
00129 // Find the start of a tilde expansion in S, and return the index
00130 // of the tilde which starts the expansion.  Place the length of the
00131 // text which identified this tilde starter in LEN, excluding the
00132 // tilde itself.
00133 
00134 static size_t
00135 tilde_find_prefix (const std::string& s, size_t& len)
00136 {
00137   len = 0;
00138 
00139   size_t s_len = s.length ();
00140 
00141   if (s_len == 0 || s[0] == '~')
00142     return 0;
00143 
00144   string_vector prefixes = file_ops::tilde_additional_prefixes;
00145 
00146   if (! prefixes.empty ())
00147     {
00148       for (size_t i = 0; i < s_len; i++)
00149         {
00150           for (int j = 0; j < prefixes.length (); j++)
00151             {
00152               size_t pfx_len = prefixes[j].length ();
00153 
00154               if (prefixes[j].compare (s.substr (i, pfx_len)) == 0)
00155                 {
00156                   len = pfx_len - 1;
00157                   return i + len;
00158                 }
00159             }
00160         }
00161     }
00162 
00163   return s_len;
00164 }
00165 
00166 // Find the end of a tilde expansion in S, and return the index
00167 // of the character which ends the tilde definition.
00168 
00169 static size_t
00170 tilde_find_suffix (const std::string& s)
00171 {
00172   size_t s_len = s.length ();
00173 
00174   string_vector suffixes = file_ops::tilde_additional_suffixes;
00175 
00176   size_t i = 0;
00177 
00178   for ( ; i < s_len; i++)
00179     {
00180       if (file_ops::is_dir_sep (s[i]))
00181         break;
00182 
00183       if (! suffixes.empty ())
00184         {
00185           for (int j = 0; j < suffixes.length (); j++)
00186             {
00187               size_t sfx_len = suffixes[j].length ();
00188 
00189               if (suffixes[j].compare (s.substr (i, sfx_len)) == 0)
00190                 return i;
00191             }
00192         }
00193     }
00194 
00195   return i;
00196 }
00197 
00198 // Take FNAME and return the tilde prefix we want expanded.
00199 
00200 static std::string
00201 isolate_tilde_prefix (const std::string& fname)
00202 {
00203   size_t f_len = fname.length ();
00204 
00205   size_t len = 1;
00206 
00207   while (len < f_len && ! file_ops::is_dir_sep (fname[len]))
00208     len++;
00209 
00210   return fname.substr (1, len);
00211 }
00212 
00213 // Do the work of tilde expansion on FILENAME.  FILENAME starts with a
00214 // tilde.
00215 
00216 static std::string
00217 tilde_expand_word (const std::string& filename)
00218 {
00219   size_t f_len = filename.length ();
00220 
00221   if (f_len == 0 || filename[0] != '~')
00222     return filename;
00223 
00224   // A leading '~/' or a bare '~' is *always* translated to the value
00225   // of $HOME or the home directory of the current user, regardless of
00226   // any preexpansion hook.
00227 
00228   if (f_len == 1 || file_ops::is_dir_sep (filename[1]))
00229     return octave_env::get_home_directory () + filename.substr (1);
00230 
00231   std::string username = isolate_tilde_prefix (filename);
00232 
00233   size_t user_len = username.length ();
00234 
00235   std::string dirname;
00236 
00237   if (file_ops::tilde_expansion_preexpansion_hook)
00238     {
00239       std::string expansion
00240         = file_ops::tilde_expansion_preexpansion_hook (username);
00241 
00242       if (! expansion.empty ())
00243         return expansion + filename.substr (user_len+1);
00244     }
00245 
00246   // No preexpansion hook, or the preexpansion hook failed.  Look in the
00247   // password database.
00248 
00249   octave_passwd pw = octave_passwd::getpwnam (username);
00250 
00251   if (! pw)
00252     {
00253       // If the calling program has a special syntax for expanding tildes,
00254       // and we couldn't find a standard expansion, then let them try.
00255 
00256       if (file_ops::tilde_expansion_failure_hook)
00257         {
00258           std::string expansion
00259             = file_ops::tilde_expansion_failure_hook (username);
00260 
00261           if (! expansion.empty ())
00262             dirname = expansion + filename.substr (user_len+1);
00263         }
00264 
00265       // If we don't have a failure hook, or if the failure hook did not
00266       // expand the tilde, return a copy of what we were passed.
00267 
00268       if (dirname.length () == 0)
00269         dirname = filename;
00270     }
00271   else
00272     dirname = pw.dir () + filename.substr (user_len+1);
00273 
00274   return dirname;
00275 }
00276 
00277 // If NAME has a leading ~ or ~user, Unix-style, expand it to the
00278 // user's home directory.  If no ~, or no <pwd.h>, just return NAME.
00279 
00280 std::string
00281 file_ops::tilde_expand (const std::string& name)
00282 {
00283   if (name.find ('~') == std::string::npos)
00284     return name;
00285   else
00286     {
00287       std::string result;
00288 
00289       size_t name_len = name.length ();
00290 
00291       // Scan through S expanding tildes as we come to them.
00292 
00293       size_t pos = 0;
00294 
00295       while (1)
00296         {
00297           if (pos > name_len)
00298             break;
00299 
00300           size_t len;
00301 
00302           // Make START point to the tilde which starts the expansion.
00303 
00304           size_t start = tilde_find_prefix (name.substr (pos), len);
00305 
00306           result.append (name.substr (pos, start));
00307 
00308           // Advance STRING to the starting tilde.
00309 
00310           pos += start;
00311 
00312           // Make FINI be the index of one after the last character of the
00313           // username.
00314 
00315           size_t fini = tilde_find_suffix (name.substr (pos));
00316 
00317           // If both START and FINI are zero, we are all done.
00318 
00319           if (! (start || fini))
00320             break;
00321 
00322           // Expand the entire tilde word, and copy it into RESULT.
00323 
00324           std::string tilde_word = name.substr (pos, fini);
00325 
00326           pos += fini;
00327 
00328           std::string expansion = tilde_expand_word (tilde_word);
00329 
00330           result.append (expansion);
00331         }
00332 
00333       return result;
00334     }
00335 }
00336 
00337 // A vector version of the above.
00338 
00339 string_vector
00340 file_ops::tilde_expand (const string_vector& names)
00341 {
00342   string_vector retval;
00343 
00344   int n = names.length ();
00345 
00346   retval.resize (n);
00347 
00348   for (int i = 0; i < n; i++)
00349     retval[i] = tilde_expand (names[i]);
00350 
00351   return retval;
00352 }
00353 
00354 std::string
00355 file_ops::concat (const std::string& dir, const std::string& file)
00356 {
00357   return dir.empty ()
00358     ? file
00359     : (is_dir_sep (dir[dir.length()-1])
00360        ? dir + file
00361        : dir + dir_sep_char () + file);
00362 }
00363 
00364 
00365 int
00366 octave_mkdir (const std::string& nm, mode_t md)
00367 {
00368   std::string msg;
00369   return octave_mkdir (nm, md, msg);
00370 }
00371 
00372 int
00373 octave_mkdir (const std::string& name, mode_t mode, std::string& msg)
00374 {
00375   msg = std::string ();
00376 
00377   int status = -1;
00378 
00379   status = gnulib::mkdir (name.c_str (), mode);
00380 
00381   if (status < 0)
00382     msg = gnulib::strerror (errno);
00383 
00384   return status;
00385 }
00386 
00387 int
00388 octave_mkfifo (const std::string& nm, mode_t md)
00389 {
00390   std::string msg;
00391   return octave_mkfifo (nm, md, msg);
00392 }
00393 
00394 int
00395 octave_mkfifo (const std::string& name, mode_t mode, std::string& msg)
00396 {
00397   msg = std::string ();
00398 
00399   int status = -1;
00400 
00401   // With gnulib we will always have mkfifo, but some systems like MinGW
00402   // don't have working mkfifo functions.  On those systems, mkfifo will
00403   // always return -1 and set errno.
00404 
00405   status = gnulib::mkfifo (name.c_str (), mode);
00406 
00407   if (status < 0)
00408     msg = gnulib::strerror (errno);
00409 
00410   return status;
00411 }
00412 
00413 int
00414 octave_link (const std::string& old_name, const std::string& new_name)
00415 {
00416   std::string msg;
00417   return octave_link (old_name, new_name, msg);
00418 }
00419 
00420 int
00421 octave_link (const std::string& old_name,
00422                 const std::string& new_name, std::string& msg)
00423 {
00424   msg = std::string ();
00425 
00426   int status = -1;
00427 
00428   status = gnulib::link (old_name.c_str (), new_name.c_str ());
00429 
00430   if (status < 0)
00431     msg = gnulib::strerror (errno);
00432 
00433   return status;
00434 }
00435 
00436 int
00437 octave_symlink (const std::string& old_name, const std::string& new_name)
00438 {
00439   std::string msg;
00440   return octave_symlink (old_name, new_name, msg);
00441 }
00442 
00443 int
00444 octave_symlink (const std::string& old_name,
00445                    const std::string& new_name, std::string& msg)
00446 {
00447   msg = std::string ();
00448 
00449   int status = -1;
00450 
00451   status = gnulib::symlink (old_name.c_str (), new_name.c_str ());
00452 
00453   if (status < 0)
00454     msg = gnulib::strerror (errno);
00455 
00456   return status;
00457 }
00458 
00459 int
00460 octave_readlink (const std::string& path, std::string& result)
00461 {
00462   std::string msg;
00463   return octave_readlink (path, result, msg);
00464 }
00465 
00466 int
00467 octave_readlink (const std::string& path, std::string& result,
00468                     std::string& msg)
00469 {
00470   int status = -1;
00471 
00472   msg = std::string ();
00473 
00474   char buf[MAXPATHLEN+1];
00475 
00476   status = gnulib::readlink (path.c_str (), buf, MAXPATHLEN);
00477 
00478   if (status < 0)
00479     msg = gnulib::strerror (errno);
00480   else
00481     {
00482       buf[status] = '\0';
00483       result = std::string (buf);
00484       status = 0;
00485     }
00486 
00487   return status;
00488 }
00489 
00490 int
00491 octave_rename (const std::string& from, const std::string& to)
00492 {
00493   std::string msg;
00494   return octave_rename (from, to, msg);
00495 }
00496 
00497 int
00498 octave_rename (const std::string& from, const std::string& to,
00499                   std::string& msg)
00500 {
00501   int status = -1;
00502 
00503   msg = std::string ();
00504 
00505   status = gnulib::rename (from.c_str (), to.c_str ());
00506 
00507   if (status < 0)
00508     msg = gnulib::strerror (errno);
00509 
00510   return status;
00511 }
00512 
00513 int
00514 octave_rmdir (const std::string& name)
00515 {
00516   std::string msg;
00517   return octave_rmdir (name, msg);
00518 }
00519 
00520 int
00521 octave_rmdir (const std::string& name, std::string& msg)
00522 {
00523   msg = std::string ();
00524 
00525   int status = -1;
00526 
00527   status = gnulib::rmdir (name.c_str ());
00528 
00529   if (status < 0)
00530     msg = gnulib::strerror (errno);
00531 
00532   return status;
00533 }
00534 
00535 // And a version that works recursively.
00536 
00537 int
00538 octave_recursive_rmdir (const std::string& name)
00539 {
00540   std::string msg;
00541   return octave_recursive_rmdir (name, msg);
00542 }
00543 
00544 int
00545 octave_recursive_rmdir (const std::string& name, std::string& msg)
00546 {
00547   msg = std::string ();
00548 
00549   int status = 0;
00550 
00551   dir_entry dir (name);
00552 
00553   if (dir)
00554     {
00555       string_vector dirlist = dir.read ();
00556 
00557       for (octave_idx_type i = 0; i < dirlist.length (); i++)
00558         {
00559           octave_quit ();
00560 
00561           std::string nm = dirlist[i];
00562 
00563           // Skip current directory and parent.
00564           if (nm == "." || nm == "..")
00565             continue;
00566 
00567           std::string fullnm = name + file_ops::dir_sep_str () + nm;
00568 
00569           // Get info about the file.  Don't follow links.
00570           file_stat fs (fullnm, false);
00571 
00572           if (fs)
00573             {
00574               if (fs.is_dir ())
00575                 {
00576                   status = octave_recursive_rmdir (fullnm, msg);
00577 
00578                   if (status < 0)
00579                     break;
00580                 }
00581               else
00582                 {
00583                   status = octave_unlink (fullnm, msg);
00584 
00585                   if (status < 0)
00586                     break;
00587                 }
00588             }
00589           else
00590             {
00591               msg = fs.error ();
00592               break;
00593             }
00594         }
00595 
00596       if (status >= 0)
00597         {
00598           dir.close ();
00599           status = octave_rmdir (name, msg);
00600         }
00601     }
00602   else
00603     {
00604       status = -1;
00605 
00606       msg = dir.error ();
00607     }
00608 
00609   return status;
00610 }
00611 
00612 int
00613 octave_umask (mode_t mode)
00614 {
00615 #if defined (HAVE_UMASK)
00616   return umask (mode);
00617 #else
00618   return 0;
00619 #endif
00620 }
00621 
00622 int
00623 octave_unlink (const std::string& name)
00624 {
00625   std::string msg;
00626   return octave_unlink (name, msg);
00627 }
00628 
00629 int
00630 octave_unlink (const std::string& name, std::string& msg)
00631 {
00632   msg = std::string ();
00633 
00634   int status = -1;
00635 
00636   status = gnulib::unlink (name.c_str ());
00637 
00638   if (status < 0)
00639     msg = gnulib::strerror (errno);
00640 
00641   return status;
00642 }
00643 
00644 std::string
00645 octave_tempnam (const std::string& dir, const std::string& pfx)
00646 {
00647   std::string msg;
00648   return octave_tempnam (dir, pfx, msg);
00649 }
00650 
00651 std::string
00652 octave_tempnam (const std::string& dir, const std::string& pfx,
00653                 std::string& msg)
00654 {
00655   msg = std::string ();
00656 
00657   std::string retval;
00658 
00659   const char *pdir = dir.empty () ? 0 : dir.c_str ();
00660 
00661   const char *ppfx = pfx.empty () ? 0 : pfx.c_str ();
00662 
00663   char *tmp = tempnam (pdir, ppfx);
00664 
00665   if (tmp)
00666     {
00667       retval = tmp;
00668 
00669       free (tmp);
00670     }
00671   else
00672     msg = gnulib::strerror (errno);
00673 
00674   return retval;
00675 }
00676 
00677 std::string
00678 octave_canonicalize_file_name (const std::string& name)
00679 {
00680   std::string msg;
00681   return octave_canonicalize_file_name (name, msg);
00682 }
00683 
00684 std::string
00685 octave_canonicalize_file_name (const std::string& name, std::string& msg)
00686 {
00687   msg = std::string ();
00688 
00689   std::string retval;
00690 
00691 #if defined (HAVE_CANONICALIZE_FILE_NAME)
00692 
00693   char *tmp = gnulib::canonicalize_file_name (name.c_str ());
00694 
00695   if (tmp)
00696     {
00697       retval = tmp;
00698       free (tmp);
00699     }
00700 
00701 #elif defined (HAVE_RESOLVEPATH)
00702 
00703 #if !defined (errno)
00704 extern int errno;
00705 #endif
00706 
00707 #if !defined (__set_errno)
00708 # define __set_errno(Val) errno = (Val)
00709 #endif
00710 
00711   if (name.empty ())
00712     {
00713       __set_errno (ENOENT);
00714       return retval;
00715     }
00716 
00717   // All known hosts with resolvepath (e.g. Solaris 7) don't turn
00718   // relative names into absolute ones, so prepend the working
00719   // directory if the path is not absolute.
00720 
00721   std::string absolute_name = octave_env::make_absolute (name);
00722 
00723   size_t resolved_size = absolute_name.length ();
00724 
00725   while (true)
00726     {
00727       resolved_size = 2 * resolved_size + 1;
00728 
00729       OCTAVE_LOCAL_BUFFER (char, resolved, resolved_size);
00730 
00731       int resolved_len
00732         = resolvepath (absolute_name.c_str (), resolved, resolved_size);
00733 
00734       if (resolved_len < 0)
00735         break;
00736 
00737       if (resolved_len < resolved_size)
00738         {
00739           retval = resolved;
00740           break;
00741         }
00742     }
00743 
00744 #elif defined (__WIN32__)
00745 
00746   int n = 1024;
00747 
00748   std::string win_path (n, '\0');
00749 
00750   while (true)
00751     {
00752       int status = GetFullPathName (name.c_str (), n, &win_path[0], 0);
00753 
00754       if (status == 0)
00755         break;
00756       else if (status < n)
00757         {
00758           win_path.resize (status);
00759           retval = win_path;
00760           break;
00761         }
00762       else
00763         {
00764           n *= 2;
00765           win_path.resize (n);
00766         }
00767     }
00768 
00769 #elif defined (HAVE_REALPATH)
00770 
00771 #if !defined (__set_errno)
00772 # define __set_errno(Val) errno = (Val)
00773 #endif
00774 
00775   if (name.empty ())
00776     {
00777       __set_errno (ENOENT);
00778       return retval;
00779     }
00780 
00781   OCTAVE_LOCAL_BUFFER (char, buf, PATH_MAX);
00782 
00783   if (::realpath (name.c_str (), buf))
00784     retval = buf;
00785 
00786 #else
00787 
00788   // FIXME -- provide replacement here...
00789   retval = name;
00790 
00791 #endif
00792 
00793   if (retval.empty ())
00794     msg = gnulib::strerror (errno);
00795 
00796   return retval;
00797 }
00798 
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines