|  | /* | 
|  | * Copyright (C) 2016 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include "util.h" | 
|  |  | 
|  | #include <sys/types.h> | 
|  | #include <sys/stat.h> | 
|  | #include <dirent.h> | 
|  | #include <string.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  |  | 
|  | FileInfo::FileInfo() | 
|  | { | 
|  | memset(this, 0, sizeof(FileInfo)); | 
|  | } | 
|  |  | 
|  | FileInfo::FileInfo(const FileInfo& that) | 
|  | { | 
|  | memcpy(this, &that, sizeof(FileInfo)); | 
|  | } | 
|  |  | 
|  | FileInfo::FileInfo(const string& filename) | 
|  | { | 
|  | struct stat st; | 
|  | int err = stat(filename.c_str(), &st); | 
|  | if (err != 0) { | 
|  | memset(this, 0, sizeof(FileInfo)); | 
|  | } else { | 
|  | exists = true; | 
|  | mtime = st.st_mtime; | 
|  | ctime = st.st_ctime; | 
|  | size = st.st_size; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool | 
|  | FileInfo::operator==(const FileInfo& that) const | 
|  | { | 
|  | return exists == that.exists | 
|  | && mtime == that.mtime | 
|  | && ctime == that.ctime | 
|  | && size == that.size; | 
|  | } | 
|  |  | 
|  | bool | 
|  | FileInfo::operator!=(const FileInfo& that) const | 
|  | { | 
|  | return exists != that.exists | 
|  | || mtime != that.mtime | 
|  | || ctime != that.ctime | 
|  | || size != that.size; | 
|  | } | 
|  |  | 
|  | FileInfo::~FileInfo() | 
|  | { | 
|  | } | 
|  |  | 
|  | TrackedFile::TrackedFile() | 
|  | :filename(), | 
|  | fileInfo() | 
|  | { | 
|  | } | 
|  |  | 
|  | TrackedFile::TrackedFile(const TrackedFile& that) | 
|  | { | 
|  | filename = that.filename; | 
|  | fileInfo = that.fileInfo; | 
|  | } | 
|  |  | 
|  | TrackedFile::TrackedFile(const string& file) | 
|  | :filename(file), | 
|  | fileInfo(file) | 
|  | { | 
|  | } | 
|  |  | 
|  | TrackedFile::~TrackedFile() | 
|  | { | 
|  | } | 
|  |  | 
|  | bool | 
|  | TrackedFile::HasChanged() const | 
|  | { | 
|  | FileInfo updated(filename); | 
|  | return !updated.exists || fileInfo != updated; | 
|  | } | 
|  |  | 
|  | void | 
|  | get_directory_contents(const string& name, map<string,FileInfo>* results) | 
|  | { | 
|  | DIR* dir = opendir(name.c_str()); | 
|  | if (dir == NULL) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | dirent* entry; | 
|  | while ((entry = readdir(dir)) != NULL) { | 
|  | if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { | 
|  | continue; | 
|  | } | 
|  | if (entry->d_type == DT_DIR) { | 
|  | string subdir = name + "/" + entry->d_name; | 
|  | get_directory_contents(subdir, results); | 
|  | } else if (entry->d_type == DT_LNK || entry->d_type == DT_REG) { | 
|  | string filename(name + "/" + entry->d_name); | 
|  | (*results)[filename] = FileInfo(filename); | 
|  | } | 
|  | } | 
|  |  | 
|  | closedir(dir); | 
|  | } | 
|  |  | 
|  | bool | 
|  | directory_contents_differ(const map<string,FileInfo>& before, const map<string,FileInfo>& after) | 
|  | { | 
|  | if (before.size() != after.size()) { | 
|  | return true; | 
|  | } | 
|  | map<string,FileInfo>::const_iterator b = before.begin(); | 
|  | map<string,FileInfo>::const_iterator a = after.begin(); | 
|  | while (b != before.end() && a != after.end()) { | 
|  | if (b->first != a->first) { | 
|  | return true; | 
|  | } | 
|  | if (a->second != b->second) { | 
|  | return true; | 
|  | } | 
|  | a++; | 
|  | b++; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | string | 
|  | escape_quotes(const char* str) | 
|  | { | 
|  | string result; | 
|  | while (*str) { | 
|  | if (*str == '"') { | 
|  | result += '\\'; | 
|  | result += '"'; | 
|  | } else { | 
|  | result += *str; | 
|  | } | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | string | 
|  | escape_for_commandline(const char* str) | 
|  | { | 
|  | if (strchr(str, '"') != NULL || strchr(str, ' ') != NULL | 
|  | || strchr(str, '\t') != NULL) { | 
|  | return escape_quotes(str); | 
|  | } else { | 
|  | return str; | 
|  | } | 
|  | } | 
|  |  | 
|  | static bool | 
|  | spacechr(char c) | 
|  | { | 
|  | return c == ' ' || c == '\t' || c == '\n' || c == '\r'; | 
|  | } | 
|  |  | 
|  | string | 
|  | trim(const string& str) | 
|  | { | 
|  | const ssize_t N = (ssize_t)str.size(); | 
|  | ssize_t begin = 0; | 
|  | while (begin < N && spacechr(str[begin])) { | 
|  | begin++; | 
|  | } | 
|  | ssize_t end = N - 1; | 
|  | while (end >= begin && spacechr(str[end])) { | 
|  | end--; | 
|  | } | 
|  | return string(str, begin, end-begin+1); | 
|  | } | 
|  |  | 
|  | bool | 
|  | starts_with(const string& str, const string& prefix) | 
|  | { | 
|  | return str.compare(0, prefix.length(), prefix) == 0; | 
|  | } | 
|  |  | 
|  | bool | 
|  | ends_with(const string& str, const string& suffix) | 
|  | { | 
|  | if (str.length() < suffix.length()) { | 
|  | return false; | 
|  | } else { | 
|  | return str.compare(str.length()-suffix.length(), suffix.length(), suffix) == 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | split_lines(vector<string>* result, const string& str) | 
|  | { | 
|  | const int N = str.length(); | 
|  | int begin = 0; | 
|  | int end = 0; | 
|  | for (; end < N; end++) { | 
|  | const char c = str[end]; | 
|  | if (c == '\r' || c == '\n') { | 
|  | if (begin != end) { | 
|  | result->push_back(string(str, begin, end-begin)); | 
|  | } | 
|  | begin = end+1; | 
|  | } | 
|  | } | 
|  | if (begin != end) { | 
|  | result->push_back(string(str, begin, end-begin)); | 
|  | } | 
|  | } | 
|  |  | 
|  | string | 
|  | read_file(const string& filename) | 
|  | { | 
|  | FILE* file = fopen(filename.c_str(), "r"); | 
|  | if (file == NULL) { | 
|  | return string(); | 
|  | } | 
|  |  | 
|  | fseek(file, 0, SEEK_END); | 
|  | int size = ftell(file); | 
|  | fseek(file, 0, SEEK_SET); | 
|  |  | 
|  | char* buf = (char*)malloc(size); | 
|  | if ((size_t) size != fread(buf, 1, size, file)) { | 
|  | free(buf); | 
|  | fclose(file); | 
|  | return string(); | 
|  | } | 
|  |  | 
|  | string result(buf, size); | 
|  |  | 
|  | free(buf); | 
|  | fclose(file); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | bool | 
|  | is_executable(const string& filename) | 
|  | { | 
|  | int err; | 
|  | struct stat st; | 
|  |  | 
|  | err = stat(filename.c_str(), &st); | 
|  | if (err != 0) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return (st.st_mode & S_IXUSR) != 0; | 
|  | } | 
|  |  | 
|  | string | 
|  | dirname(const string& filename) | 
|  | { | 
|  | size_t slash = filename.rfind('/'); | 
|  | if (slash == string::npos) { | 
|  | return ""; | 
|  | } else if (slash == 0) { | 
|  | return "/"; | 
|  | } else { | 
|  | return string(filename, 0, slash); | 
|  | } | 
|  | } | 
|  |  | 
|  | string | 
|  | leafname(const string& filename) | 
|  | { | 
|  | size_t slash = filename.rfind('/'); | 
|  | if (slash == string::npos) { | 
|  | return filename; | 
|  | } else if (slash == filename.length() - 1) { | 
|  | return ""; | 
|  | } else { | 
|  | return string(filename, slash + 1); | 
|  | } | 
|  | } |