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 
FileInfo()26 FileInfo::FileInfo()
27 {
28     memset(this, 0, sizeof(FileInfo));
29 }
30 
FileInfo(const FileInfo & that)31 FileInfo::FileInfo(const FileInfo& that)
32 {
33     memcpy(this, &that, sizeof(FileInfo));
34 }
35 
FileInfo(const string & filename)36 FileInfo::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 
50 bool
operator ==(const FileInfo & that) const51 FileInfo::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 
59 bool
operator !=(const FileInfo & that) const60 FileInfo::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 
~FileInfo()68 FileInfo::~FileInfo()
69 {
70 }
71 
TrackedFile()72 TrackedFile::TrackedFile()
73     :filename(),
74      fileInfo()
75 {
76 }
77 
TrackedFile(const TrackedFile & that)78 TrackedFile::TrackedFile(const TrackedFile& that)
79 {
80     filename = that.filename;
81     fileInfo = that.fileInfo;
82 }
83 
TrackedFile(const string & file)84 TrackedFile::TrackedFile(const string& file)
85     :filename(file),
86      fileInfo(file)
87 {
88 }
89 
~TrackedFile()90 TrackedFile::~TrackedFile()
91 {
92 }
93 
94 bool
HasChanged() const95 TrackedFile::HasChanged() const
96 {
97     FileInfo updated(filename);
98     return !updated.exists || fileInfo != updated;
99 }
100 
101 void
get_directory_contents(const string & name,map<string,FileInfo> * results)102 get_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 
126 bool
directory_contents_differ(const map<string,FileInfo> & before,const map<string,FileInfo> & after)127 directory_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 
147 string
escape_quotes(const char * str)148 escape_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 
162 string
escape_for_commandline(const char * str)163 escape_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 
173 static bool
spacechr(char c)174 spacechr(char c)
175 {
176     return c == ' ' || c == '\t' || c == '\n' || c == '\r';
177 }
178 
179 string
trim(const string & str)180 trim(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 
194 bool
starts_with(const string & str,const string & prefix)195 starts_with(const string& str, const string& prefix)
196 {
197     return str.compare(0, prefix.length(), prefix) == 0;
198 }
199 
200 bool
ends_with(const string & str,const string & suffix)201 ends_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 
210 void
split_lines(vector<string> * result,const string & str)211 split_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 
230 string
read_file(const string & filename)231 read_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         free(buf);
245         fclose(file);
246         return string();
247     }
248 
249     string result(buf, size);
250 
251     free(buf);
252     fclose(file);
253 
254     return result;
255 }
256 
257 bool
is_executable(const string & filename)258 is_executable(const string& filename)
259 {
260     int err;
261     struct stat st;
262 
263     err = stat(filename.c_str(), &st);
264     if (err != 0) {
265         return false;
266     }
267 
268     return (st.st_mode & S_IXUSR) != 0;
269 }
270 
271 string
dirname(const string & filename)272 dirname(const string& filename)
273 {
274     size_t slash = filename.rfind('/');
275     if (slash == string::npos) {
276         return "";
277     } else if (slash == 0) {
278         return "/";
279     } else {
280         return string(filename, 0, slash);
281     }
282 }
283 
284 string
leafname(const string & filename)285 leafname(const string& filename)
286 {
287     size_t slash = filename.rfind('/');
288     if (slash == string::npos) {
289         return filename;
290     } else if (slash == filename.length() - 1) {
291         return "";
292     } else {
293         return string(filename, slash + 1);
294     }
295 }
296