1/*
2 * Copyright (C) 2016 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 "util.h"
18
19#include <sys/types.h>
20#include <sys/stat.h>
21#include <dirent.h>
22#include <string.h>
23#include <unistd.h>
24
25
26FileInfo::FileInfo()
27{
28    memset(this, 0, sizeof(FileInfo));
29}
30
31FileInfo::FileInfo(const FileInfo& that)
32{
33    memcpy(this, &that, sizeof(FileInfo));
34}
35
36FileInfo::FileInfo(const string& filename)
37{
38    struct stat st;
39    int err = stat(filename.c_str(), &st);
40    if (err != 0) {
41        memset(this, 0, sizeof(FileInfo));
42    } else {
43        exists = true;
44        mtime = st.st_mtime;
45        ctime = st.st_ctime;
46        size = st.st_size;
47    }
48}
49
50bool
51FileInfo::operator==(const FileInfo& that) const
52{
53    return exists == that.exists
54            && mtime == that.mtime
55            && ctime == that.ctime
56            && size == that.size;
57}
58
59bool
60FileInfo::operator!=(const FileInfo& that) const
61{
62    return exists != that.exists
63            || mtime != that.mtime
64            || ctime != that.ctime
65            || size != that.size;
66}
67
68FileInfo::~FileInfo()
69{
70}
71
72TrackedFile::TrackedFile()
73    :filename(),
74     fileInfo()
75{
76}
77
78TrackedFile::TrackedFile(const TrackedFile& that)
79{
80    filename = that.filename;
81    fileInfo = that.fileInfo;
82}
83
84TrackedFile::TrackedFile(const string& file)
85    :filename(file),
86     fileInfo(file)
87{
88}
89
90TrackedFile::~TrackedFile()
91{
92}
93
94bool
95TrackedFile::HasChanged() const
96{
97    FileInfo updated(filename);
98    return !updated.exists || fileInfo != updated;
99}
100
101void
102get_directory_contents(const string& name, map<string,FileInfo>* results)
103{
104    DIR* dir = opendir(name.c_str());
105    if (dir == NULL) {
106        return;
107    }
108
109    dirent* entry;
110    while ((entry = readdir(dir)) != NULL) {
111        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
112            continue;
113        }
114        if (entry->d_type == DT_DIR) {
115            string subdir = name + "/" + entry->d_name;
116            get_directory_contents(subdir, results);
117        } else if (entry->d_type == DT_LNK || entry->d_type == DT_REG) {
118            string filename(name + "/" + entry->d_name);
119            (*results)[filename] = FileInfo(filename);
120        }
121    }
122
123    closedir(dir);
124}
125
126bool
127directory_contents_differ(const map<string,FileInfo>& before, const map<string,FileInfo>& after)
128{
129    if (before.size() != after.size()) {
130        return true;
131    }
132    map<string,FileInfo>::const_iterator b = before.begin();
133    map<string,FileInfo>::const_iterator a = after.begin();
134    while (b != before.end() && a != after.end()) {
135        if (b->first != a->first) {
136            return true;
137        }
138        if (a->second != b->second) {
139            return true;
140        }
141        a++;
142        b++;
143    }
144    return false;
145}
146
147string
148escape_quotes(const char* str)
149{
150    string result;
151    while (*str) {
152        if (*str == '"') {
153            result += '\\';
154            result += '"';
155        } else {
156            result += *str;
157        }
158    }
159    return result;
160}
161
162string
163escape_for_commandline(const char* str)
164{
165    if (strchr(str, '"') != NULL || strchr(str, ' ') != NULL
166            || strchr(str, '\t') != NULL) {
167        return escape_quotes(str);
168    } else {
169        return str;
170    }
171}
172
173static bool
174spacechr(char c)
175{
176    return c == ' ' || c == '\t' || c == '\n' || c == '\r';
177}
178
179string
180trim(const string& str)
181{
182    const ssize_t N = (ssize_t)str.size();
183    ssize_t begin = 0;
184    while (begin < N && spacechr(str[begin])) {
185        begin++;
186    }
187    ssize_t end = N - 1;
188    while (end >= begin && spacechr(str[end])) {
189        end--;
190    }
191    return string(str, begin, end-begin+1);
192}
193
194bool
195starts_with(const string& str, const string& prefix)
196{
197    return str.compare(0, prefix.length(), prefix) == 0;
198}
199
200bool
201ends_with(const string& str, const string& suffix)
202{
203    if (str.length() < suffix.length()) {
204        return false;
205    } else {
206        return str.compare(str.length()-suffix.length(), suffix.length(), suffix) == 0;
207    }
208}
209
210void
211split_lines(vector<string>* result, const string& str)
212{
213    const int N = str.length();
214    int begin = 0;
215    int end = 0;
216    for (; end < N; end++) {
217        const char c = str[end];
218        if (c == '\r' || c == '\n') {
219            if (begin != end) {
220                result->push_back(string(str, begin, end-begin));
221            }
222            begin = end+1;
223        }
224    }
225    if (begin != end) {
226        result->push_back(string(str, begin, end-begin));
227    }
228}
229
230string
231read_file(const string& filename)
232{
233    FILE* file = fopen(filename.c_str(), "r");
234    if (file == NULL) {
235        return string();
236    }
237
238    fseek(file, 0, SEEK_END);
239    int size = ftell(file);
240    fseek(file, 0, SEEK_SET);
241
242    char* buf = (char*)malloc(size);
243    if ((size_t) size != fread(buf, 1, size, file)) {
244        return string();
245    }
246
247    string result(buf, size);
248
249    free(buf);
250    fclose(file);
251
252    return result;
253}
254
255
256