1/*
2 * Copyright (C) 2013, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "dictionary/utils/file_utils.h"
18
19#include <cstdio>
20#include <cstring>
21#include <dirent.h>
22#include <fcntl.h>
23#include <libgen.h>
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <unistd.h>
27
28namespace latinime {
29
30// Returns -1 on error.
31/* static */ int FileUtils::getFileSize(const char *const filePath) {
32    const int fd = open(filePath, O_RDONLY);
33    if (fd == -1) {
34        return -1;
35    }
36    struct stat statBuf;
37    if (fstat(fd, &statBuf) != 0) {
38        close(fd);
39        return -1;
40    }
41    close(fd);
42    return static_cast<int>(statBuf.st_size);
43}
44
45/* static */ bool FileUtils::existsDir(const char *const dirPath) {
46    DIR *const dir = opendir(dirPath);
47    if (dir == NULL) {
48        return false;
49    }
50    closedir(dir);
51    return true;
52}
53
54// Remove a directory and all files in the directory.
55/* static */ bool FileUtils::removeDirAndFiles(const char *const dirPath) {
56    return removeDirAndFiles(dirPath, 5 /* maxTries */);
57}
58
59// Remove a directory and all files in the directory, trying up to maxTimes.
60/* static */ bool FileUtils::removeDirAndFiles(const char *const dirPath, const int maxTries) {
61    DIR *const dir = opendir(dirPath);
62    if (dir == NULL) {
63        AKLOGE("Cannot open dir %s.", dirPath);
64        return true;
65    }
66    struct dirent *dirent;
67    while ((dirent = readdir(dir)) != NULL) {
68        if (dirent->d_type == DT_DIR) {
69            continue;
70        }
71        if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0) {
72            continue;
73        }
74        const int filePathBufSize = getFilePathBufSize(dirPath, dirent->d_name);
75        char filePath[filePathBufSize];
76        getFilePath(dirPath, dirent->d_name, filePathBufSize, filePath);
77        if (remove(filePath) != 0) {
78            AKLOGE("Cannot remove file %s.", filePath);
79            closedir(dir);
80            return false;
81        }
82    }
83    closedir(dir);
84    if (remove(dirPath) != 0) {
85        if (maxTries > 0) {
86            // On NFS, deleting files sometimes creates new files. I'm not sure what the
87            // correct way of dealing with this is, but for the time being, this seems to work.
88            removeDirAndFiles(dirPath, maxTries - 1);
89        } else {
90            AKLOGE("Cannot remove directory %s.", dirPath);
91            return false;
92        }
93    }
94    return true;
95}
96
97/* static */ int FileUtils::getFilePathWithSuffixBufSize(const char *const filePath,
98        const char *const suffix) {
99    return strlen(filePath) + strlen(suffix) + 1 /* terminator */;
100}
101
102/* static */ void FileUtils::getFilePathWithSuffix(const char *const filePath,
103        const char *const suffix, const int filePathBufSize, char *const outFilePath) {
104    snprintf(outFilePath, filePathBufSize, "%s%s", filePath, suffix);
105}
106
107/* static */ int FileUtils::getFilePathBufSize(const char *const dirPath,
108        const char *const fileName) {
109    return strlen(dirPath) + 1 /* '/' */ + strlen(fileName) + 1 /* terminator */;
110}
111
112/* static */ void FileUtils::getFilePath(const char *const dirPath, const char *const fileName,
113        const int filePathBufSize, char *const outFilePath) {
114    snprintf(outFilePath, filePathBufSize, "%s/%s", dirPath, fileName);
115}
116
117/* static */ bool FileUtils::getFilePathWithoutSuffix(const char *const filePath,
118        const char *const suffix, const int outDirPathBufSize, char *const outDirPath) {
119    const int filePathLength = strlen(filePath);
120    const int suffixLength = strlen(suffix);
121    if (filePathLength <= suffixLength) {
122        AKLOGE("File path length (%s:%d) is shorter that suffix length (%s:%d).",
123                filePath, filePathLength, suffix, suffixLength);
124        return false;
125    }
126    const int resultFilePathLength = filePathLength - suffixLength;
127    if (outDirPathBufSize <= resultFilePathLength) {
128        AKLOGE("outDirPathBufSize is too small. filePath: %s, suffix: %s, outDirPathBufSize: %d",
129                filePath, suffix, outDirPathBufSize);
130        return false;
131    }
132    if (strncmp(filePath + resultFilePathLength, suffix, suffixLength) != 0) {
133        AKLOGE("File Path %s does not have %s as a suffix", filePath, suffix);
134        return false;
135    }
136    snprintf(outDirPath, resultFilePathLength + 1 /* terminator */, "%s", filePath);
137    return true;
138}
139
140/* static */ void FileUtils::getDirPath(const char *const filePath, const int outDirPathBufSize,
141        char *const outDirPath) {
142    for (int i = strlen(filePath) - 1; i >= 0; --i) {
143        if (filePath[i] == '/') {
144            if (i >= outDirPathBufSize) {
145                AKLOGE("outDirPathBufSize is too small. filePath: %s, outDirPathBufSize: %d",
146                        filePath, outDirPathBufSize);
147                ASSERT(false);
148                return;
149            }
150            snprintf(outDirPath, i + 1 /* terminator */, "%s", filePath);
151            return;
152        }
153    }
154}
155
156/* static */ void FileUtils::getBasename(const char *const filePath,
157        const int outNameBufSize, char *const outName) {
158    const int filePathBufSize = strlen(filePath) + 1 /* terminator */;
159    char filePathBuf[filePathBufSize];
160    snprintf(filePathBuf, filePathBufSize, "%s", filePath);
161    const char *const baseName = basename(filePathBuf);
162    const int baseNameLength = strlen(baseName);
163    if (baseNameLength >= outNameBufSize) {
164        AKLOGE("outNameBufSize is too small. filePath: %s, outNameBufSize: %d",
165                filePath, outNameBufSize);
166        return;
167    }
168    snprintf(outName, baseNameLength + 1 /* terminator */, "%s", baseName);
169}
170
171} // namespace latinime
172