Home | History | Annotate | Download | only in files
      1 // Copyright 2014 The Android Open Source Project
      2 //
      3 // This software is licensed under the terms of the GNU General Public
      4 // License version 2, as published by the Free Software Foundation, and
      5 // may be copied, distributed, and modified under those terms.
      6 //
      7 // This program is distributed in the hope that it will be useful,
      8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
      9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     10 // GNU General Public License for more details.
     11 
     12 #include "android/base/files/PathUtils.h"
     13 
     14 #include <string.h>
     15 
     16 namespace android {
     17 namespace base {
     18 
     19 // static
     20 bool PathUtils::isDirSeparator(int ch, HostType hostType) {
     21     return (ch == '/') || (hostType == HOST_WIN32 && ch == '\\');
     22 }
     23 
     24 
     25 // static
     26 bool PathUtils::isPathSeparator(int ch, HostType hostType) {
     27     return (hostType == HOST_POSIX && ch == ':') ||
     28             (hostType == HOST_WIN32 && ch == ';');
     29 }
     30 
     31 // static
     32 size_t PathUtils::rootPrefixSize(const char* path, HostType hostType) {
     33     if (!path || !path[0])
     34         return 0;
     35 
     36     if (hostType != HOST_WIN32)
     37         return (path[0] == '/') ? 1U : 0U;
     38 
     39     size_t result = 0;
     40     if (path[1] == ':') {
     41         int ch = path[0];
     42         if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
     43             result = 2U;
     44     } else if (!strncmp(path, "\\\\.\\", 4) ||
     45             !strncmp(path, "\\\\?\\", 4)) {
     46         // UNC prefixes.
     47         return 4U;
     48     } else if (isDirSeparator(path[0], hostType)) {
     49         result = 1;
     50         if (isDirSeparator(path[1], hostType)) {
     51             result = 2;
     52             while (path[result] && !isDirSeparator(path[result], HOST_WIN32))
     53                 result++;
     54         }
     55     }
     56     if (result && path[result] && isDirSeparator(path[result], HOST_WIN32))
     57         result++;
     58 
     59     return result;
     60 }
     61 
     62 // static
     63 bool PathUtils::isAbsolute(const char* path, HostType hostType) {
     64     size_t prefixSize = rootPrefixSize(path, hostType);
     65     if (!prefixSize) {
     66         return false;
     67     }
     68     if (hostType != HOST_WIN32) {
     69         return true;
     70     }
     71     return isDirSeparator(path[prefixSize - 1], HOST_WIN32);
     72 }
     73 
     74 // static
     75 StringVector PathUtils::decompose(const char* path, HostType hostType) {
     76     StringVector result;
     77     if (!path || !path[0])
     78         return result;
     79 
     80     size_t prefixLen = rootPrefixSize(path, hostType);
     81     if (prefixLen) {
     82         result.push_back(String(path, prefixLen));
     83         path += prefixLen;
     84     }
     85     for (;;) {
     86         const char* p = path;
     87         while (*p && !isDirSeparator(*p, hostType))
     88             p++;
     89         if (p > path) {
     90             result.push_back(String(path, p - path));
     91         }
     92         if (!*p) {
     93             break;
     94         }
     95         path = p + 1;
     96     }
     97     return result;
     98 }
     99 
    100 // static
    101 String PathUtils::recompose(const StringVector& components,
    102                             HostType hostType) {
    103     const char dirSeparator = (hostType == HOST_WIN32) ? '\\' : '/';
    104     String result;
    105     size_t capacity = 0;
    106     // To reduce memory allocations, compute capacity before doing the
    107     // real append.
    108     for (size_t n = 0; n < components.size(); ++n) {
    109         if (n)
    110             capacity++;
    111         capacity += components[n].size();
    112     }
    113 
    114     result.reserve(capacity);
    115 
    116     bool addSeparator = false;
    117     for (size_t n = 0; n < components.size(); ++n) {
    118         const String& component = components[n];
    119         if (addSeparator)
    120             result += dirSeparator;
    121         addSeparator = true;
    122         if (n == 0) {
    123             size_t prefixLen = rootPrefixSize(component.c_str(), hostType);
    124             if (prefixLen > 0) {
    125                 addSeparator = false;
    126             }
    127         }
    128         result += components[n];
    129     }
    130     return result;
    131 }
    132 
    133 // static
    134 void PathUtils::simplifyComponents(StringVector* components) {
    135     StringVector stack;
    136     for (StringVector::const_iterator it = components->begin();
    137             it != components->end();
    138             ++it) {
    139         if (*it == ".") {
    140             // Ignore any instance of '.' from the list.
    141             continue;
    142         }
    143         if (*it == "..") {
    144             // Handling of '..' is specific: if there is a item on the
    145             // stack that is not '..', then remove it, otherwise push
    146             // the '..'.
    147             if (!stack.empty() && stack[stack.size() -1U] != "..") {
    148                 stack.resize(stack.size() - 1U);
    149             } else {
    150                 stack.push_back(*it);
    151             }
    152             continue;
    153         }
    154         // If not a '..', just push on the stack.
    155         stack.push_back(*it);
    156     }
    157     if (stack.empty())
    158         stack.push_back(".");
    159 
    160     components->swap(&stack);
    161 }
    162 
    163 }  // namespace base
    164 }  // namespace android
    165