LCOV - code coverage report
Current view: top level - common - fileutils.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core c2b6f1024d3a Lines: 29 32 90.6 %
Date: 2019-05-16 09:13:18 Functions: 4 4 100.0 %
Branches: 30 68 44.1 %

           Branch data     Line data    Source code
       1                 :            : /** @file fileutils.cc
       2                 :            :  *  @brief File and path manipulation routines.
       3                 :            :  */
       4                 :            : /* Copyright (C) 2008 Lemur Consulting Ltd
       5                 :            :  * Copyright (C) 2008,2009,2010,2012 Olly Betts
       6                 :            :  *
       7                 :            :  * This program is free software; you can redistribute it and/or modify
       8                 :            :  * it under the terms of the GNU General Public License as published by
       9                 :            :  * the Free Software Foundation; either version 2 of the License, or
      10                 :            :  * (at your option) any later version.
      11                 :            :  *
      12                 :            :  * This program is distributed in the hope that it will be useful,
      13                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15                 :            :  * GNU General Public License for more details.
      16                 :            :  *
      17                 :            :  * You should have received a copy of the GNU General Public License
      18                 :            :  * along with this program; if not, write to the Free Software
      19                 :            :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
      20                 :            :  */
      21                 :            : 
      22                 :            : #include <config.h>
      23                 :            : 
      24                 :            : #include "fileutils.h"
      25                 :            : 
      26                 :            : #include "xapian/error.h"
      27                 :            : #include "safedirent.h"
      28                 :            : #include "safeunistd.h"
      29                 :            : 
      30                 :            : #include <cerrno>
      31                 :            : #include <cstring>
      32                 :            : #include <string>
      33                 :            : #include <sys/types.h>
      34                 :            : 
      35                 :            : using namespace std;
      36                 :            : 
      37                 :            : class dircloser {
      38                 :            :     DIR * dir;
      39                 :            :   public:
      40                 :          9 :     dircloser(DIR * dir_) : dir(dir_) {}
      41                 :          9 :     ~dircloser() {
      42         [ +  - ]:          9 :         if (dir != NULL) {
      43                 :          9 :             closedir(dir);
      44                 :          9 :             dir = NULL;
      45                 :            :         }
      46                 :          9 :     }
      47                 :            : };
      48                 :            : 
      49                 :            : void
      50                 :         18 : removedir(const string &dirname)
      51                 :            : {
      52                 :            :     DIR * dir;
      53                 :            : 
      54                 :         18 :     dir = opendir(dirname.c_str());
      55         [ +  + ]:         18 :     if (dir == NULL) {
      56         [ +  - ]:         18 :         if (errno == ENOENT) return;
      57 [ #  # ][ #  # ]:          0 :         throw Xapian::DatabaseError("Cannot open directory '" + dirname + "'", errno);
                 [ #  # ]
      58                 :            :     }
      59                 :            : 
      60                 :            :     {
      61                 :          9 :         dircloser dc(dir);
      62                 :            :         while (true) {
      63                 :         69 :             errno = 0;
      64         [ +  - ]:         69 :             struct dirent * entry = readdir(dir);
      65         [ +  + ]:         69 :             if (entry == NULL) {
      66         [ +  - ]:          9 :                 if (errno == 0)
      67                 :          9 :                     break;
      68 [ #  # ][ #  # ]:          0 :                 throw Xapian::DatabaseError("Cannot read entry from directory at '" + dirname + "'", errno);
                 [ #  # ]
      69                 :            :             }
      70         [ +  - ]:         60 :             string name(entry->d_name);
      71 [ +  - ][ +  + ]:         60 :             if (name == "." || name == "..")
         [ +  - ][ +  + ]
                 [ +  + ]
      72                 :         18 :                 continue;
      73 [ +  - ][ +  - ]:         42 :             if (unlink((dirname + "/" + name).c_str())) {
                 [ -  + ]
      74 [ #  # ][ #  # ]:         60 :                 throw Xapian::DatabaseError("Cannot remove file '" + string(entry->d_name) + "'", errno);
         [ #  # ][ #  # ]
                 [ +  + ]
      75                 :            :             }
      76                 :         69 :         }
      77                 :            :     }
      78         [ -  + ]:          9 :     if (rmdir(dirname.c_str())) {
      79 [ #  # ][ #  # ]:          0 :         throw Xapian::DatabaseError("Cannot remove directory '" + dirname + "'", errno);
                 [ #  # ]
      80                 :            :     }
      81                 :            : }
      82                 :            : 
      83                 :            : #ifdef __WIN32__
      84                 :            : /// Return true iff a path starts with a drive letter.
      85                 :            : static bool
      86                 :            : has_drive(const string &path)
      87                 :            : {
      88                 :            :     return (path.size() >= 2 && path[1] == ':');
      89                 :            : }
      90                 :            : 
      91                 :            : /// Return true iff path is a UNCW path.
      92                 :            : static bool
      93                 :            : uncw_path(const string & path)
      94                 :            : {
      95                 :            :     return (path.size() >= 4 && memcmp(path.data(), "\\\\?\\", 4) == 0);
      96                 :            : }
      97                 :            : 
      98                 :            : static inline bool slash(char ch)
      99                 :            : {
     100                 :            :     return ch == '/' || ch == '\\';
     101                 :            : }
     102                 :            : #endif
     103                 :            : 
     104                 :            : void
     105                 :        990 : resolve_relative_path(string & path, const string & base)
     106                 :            : {
     107                 :            : #ifndef __WIN32__
     108 [ +  - ][ +  + ]:        990 :     if (path.empty() || path[0] != '/') {
                 [ +  + ]
     109                 :            :         // path is relative.
     110                 :        978 :         string::size_type last_slash = base.rfind('/');
     111         [ +  + ]:        978 :         if (last_slash != string::npos)
     112                 :        978 :             path.insert(0, base, 0, last_slash + 1);
     113                 :            :     }
     114                 :            : #else
     115                 :            :     // Microsoft Windows paths may begin with a drive letter but still be
     116                 :            :     // relative within that drive.
     117                 :            :     bool drive = has_drive(path);
     118                 :            :     string::size_type p = (drive ? 2 : 0);
     119                 :            :     bool absolute = (p != path.size() && slash(path[p]));
     120                 :            : 
     121                 :            :     if (absolute) {
     122                 :            :         // If path is absolute and has a drive specifier, just return it.
     123                 :            :         if (drive)
     124                 :            :             return;
     125                 :            : 
     126                 :            :         // If base has a drive specifier prepend that to path.
     127                 :            :         if (has_drive(base)) {
     128                 :            :             path.insert(0, base, 0, 2);
     129                 :            :             return;
     130                 :            :         }
     131                 :            : 
     132                 :            :         // If base has a UNC (\\SERVER\\VOLUME) or \\?\ prefix, prepend that
     133                 :            :         // to path.
     134                 :            :         if (uncw_path(base)) {
     135                 :            :             string::size_type sl = 0;
     136                 :            :             if (base.size() >= 7 && memcmp(base.data() + 5, ":\\", 2) == 0) {
     137                 :            :                 // "\\?\X:\"
     138                 :            :                 sl = 6;
     139                 :            :             } else if (base.size() >= 8 &&
     140                 :            :                        memcmp(base.data() + 4, "UNC\\", 4) == 0) {
     141                 :            :                 // "\\?\UNC\server\volume\"
     142                 :            :                 sl = base.find('\\', 8);
     143                 :            :                 if (sl != string::npos)
     144                 :            :                     sl = base.find('\\', sl + 1);
     145                 :            :             }
     146                 :            :             if (sl) {
     147                 :            :                 // With the \\?\ prefix, '/' isn't recognised so change it
     148                 :            :                 // to '\' in path.
     149                 :            :                 string::iterator i;
     150                 :            :                 for (i = path.begin(); i != path.end(); ++i) {
     151                 :            :                     if (*i == '/')
     152                 :            :                         *i = '\\';
     153                 :            :                 }
     154                 :            :                 path.insert(0, base, 0, sl);
     155                 :            :             }
     156                 :            :         } else if (base.size() >= 5 && slash(base[0]) && slash(base[1])) {
     157                 :            :             // Handle UNC base.
     158                 :            :             string::size_type sl = base.find_first_of("/\\", 2);
     159                 :            :             if (sl != string::npos) {
     160                 :            :                 sl = base.find_first_of("/\\", sl + 1);
     161                 :            :                 path.insert(0, base, 0, sl);
     162                 :            :             }
     163                 :            :         }
     164                 :            :         return;
     165                 :            :     }
     166                 :            : 
     167                 :            :     // path is relative, so if it has no drive specifier or the same drive
     168                 :            :     // specifier as base, then we want to qualify it using base.
     169                 :            :     bool base_drive = has_drive(base);
     170                 :            :     if (!drive || (base_drive && (path[0] | 32) == (base[0] | 32))) {
     171                 :            :         string::size_type last_slash = base.find_last_of("/\\");
     172                 :            :         if (last_slash == string::npos && !drive && base_drive)
     173                 :            :             last_slash = 1;
     174                 :            :         if (last_slash != string::npos) {
     175                 :            :             string::size_type b = (drive && base_drive ? 2 : 0);
     176                 :            :             if (uncw_path(base)) {
     177                 :            :                 // With the \\?\ prefix, '/' isn't recognised so change it
     178                 :            :                 // to '\' in path.
     179                 :            :                 string::iterator i;
     180                 :            :                 for (i = path.begin(); i != path.end(); ++i) {
     181                 :            :                     if (*i == '/')
     182                 :            :                         *i = '\\';
     183                 :            :                 }
     184                 :            :             }
     185                 :            :             path.insert(b, base, b, last_slash + 1 - b);
     186                 :            :         }
     187                 :            :     }
     188                 :            : #endif
     189                 :        990 : }

Generated by: LCOV version 1.11