btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / bin / diff_zip.cpp
blobf4d437553665ad0b7b49bc8545984acb7db5e361
1 /*
2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All rights reserved.
4 * Distributed under the terms of the MIT License.
5 */
7 #include <dirent.h>
8 #include <errno.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
15 #include <map>
16 #include <string>
19 using std::string;
20 using std::map;
23 class Directory;
24 class Node;
26 static Node* create_node(Directory* parent, const string& name,
27 const struct stat& st);
30 enum diff_status {
31 DIFF_UNCHANGED,
32 DIFF_REMOVED,
33 DIFF_CHANGED,
34 DIFF_ERROR
38 class EntryWriter {
39 public:
40 EntryWriter(int fd)
41 : fFD(fd)
45 void Write(const char* entry)
47 write(fFD, entry, strlen(entry));
48 write(fFD, "\n", 1);
51 private:
52 int fFD;
56 class Node {
57 public:
58 Node(Directory* parent, const string& name, const struct stat& st)
59 : fParent(parent),
60 fName(name),
61 fStat(st)
65 virtual ~Node()
69 Directory* Parent() const { return fParent; }
70 const string& Name() const { return fName; }
71 const struct stat& Stat() const { return fStat; }
73 string Path() const;
75 bool DoStat(struct stat& st) const
77 string path(Path());
78 if (lstat(path.c_str(), &st) != 0)
79 return false;
80 return true;
83 virtual bool Scan()
85 return true;
88 virtual diff_status CollectDiffEntries(EntryWriter* out) const
90 string path(Path());
91 struct stat st;
93 diff_status status = DiffEntry(path, st);
94 if (status == DIFF_CHANGED)
95 out->Write(path.c_str());
97 return status;
100 virtual void Dump(int level) const
102 printf("%*s%s\n", level, "", fName.c_str());
105 protected:
106 diff_status DiffEntry(const string& path, struct stat& st) const
108 if (lstat(path.c_str(), &st) == 0) {
109 if (st.st_mode != fStat.st_mode
110 || st.st_mtime != fStat.st_mtime
111 || st.st_size != fStat.st_size) {
112 return DIFF_CHANGED;
114 return DIFF_UNCHANGED;
115 } else if (errno == ENOENT) {
116 // that's OK, the entry was removed
117 return DIFF_REMOVED;
118 } else {
119 // some error
120 fprintf(stderr, "Error: Failed to stat \"%s\": %s\n",
121 path.c_str(), strerror(errno));
122 return DIFF_ERROR;
126 private:
127 Directory* fParent;
128 string fName;
129 struct stat fStat;
133 class Directory : public Node {
134 public:
135 Directory(Directory* parent, const string& name, const struct stat& st)
136 : Node(parent, name, st),
137 fEntries()
141 void AddEntry(const char* name, Node* node)
143 fEntries[name] = node;
146 virtual bool Scan()
148 string path(Path());
149 DIR* dir = opendir(path.c_str());
150 if (dir == NULL) {
151 fprintf(stderr, "Error: Failed to open directory \"%s\": %s\n",
152 path.c_str(), strerror(errno));
153 return false;
156 errno = 0;
157 while (dirent* entry = readdir(dir)) {
158 if (strcmp(entry->d_name, ".") == 0
159 || strcmp(entry->d_name, "..") == 0) {
160 continue;
163 string entryPath = path + '/' + entry->d_name;
164 struct stat st;
165 if (lstat(entryPath.c_str(), &st) != 0) {
166 fprintf(stderr, "Error: Failed to stat entry \"%s\": %s\n",
167 entryPath.c_str(), strerror(errno));
168 closedir(dir);
169 return false;
172 Node* node = create_node(this, entry->d_name, st);
173 fEntries[entry->d_name] = node;
175 errno = 0;
178 if (errno != 0) {
179 fprintf(stderr, "Error: Failed to read directory \"%s\": %s\n",
180 path.c_str(), strerror(errno));
181 closedir(dir);
182 return false;
185 closedir(dir);
187 // recursively scan the entries
188 for (EntryMap::iterator it = fEntries.begin(); it != fEntries.end();
189 ++it) {
190 Node* node = it->second;
191 if (!node->Scan())
192 return false;
195 return true;
198 virtual diff_status CollectDiffEntries(EntryWriter* out) const
200 string path(Path());
201 struct stat st;
203 diff_status status = DiffEntry(path, st);
204 if (status == DIFF_REMOVED || status == DIFF_ERROR)
205 return status;
207 // we print it only if it is no longer a directory
208 if (!S_ISDIR(st.st_mode)) {
209 out->Write(path.c_str());
210 return DIFF_CHANGED;
213 // iterate through the "new" entries
214 DIR* dir = opendir(path.c_str());
215 if (dir == NULL) {
216 fprintf(stderr, "Error: Failed to open directory \"%s\": %s\n",
217 path.c_str(), strerror(errno));
218 return DIFF_ERROR;
221 errno = 0;
222 while (dirent* entry = readdir(dir)) {
223 if (strcmp(entry->d_name, ".") == 0
224 || strcmp(entry->d_name, "..") == 0) {
225 continue;
228 EntryMap::const_iterator it = fEntries.find(entry->d_name);
229 if (it == fEntries.end()) {
230 // new entry
231 string entryPath = path + "/" + entry->d_name;
232 out->Write(entryPath.c_str());
233 } else {
234 // old entry -- recurse
235 diff_status entryStatus = it->second->CollectDiffEntries(out);
236 if (entryStatus == DIFF_ERROR) {
237 closedir(dir);
238 return status;
240 if (entryStatus != DIFF_UNCHANGED)
241 status = DIFF_CHANGED;
244 errno = 0;
247 if (errno != 0) {
248 fprintf(stderr, "Error: Failed to read directory \"%s\": %s\n",
249 path.c_str(), strerror(errno));
250 closedir(dir);
251 return DIFF_ERROR;
254 closedir(dir);
255 return status;
258 virtual void Dump(int level) const
260 Node::Dump(level);
262 for (EntryMap::const_iterator it = fEntries.begin();
263 it != fEntries.end(); ++it) {
264 it->second->Dump(level + 1);
269 private:
270 typedef map<string, Node*> EntryMap;
272 EntryMap fEntries;
276 string
277 Node::Path() const
279 if (fParent == NULL)
280 return fName;
282 return fParent->Path() + '/' + fName;
286 static Node*
287 create_node(Directory* parent, const string& name, const struct stat& st)
289 if (S_ISDIR(st.st_mode))
290 return new Directory(parent, name, st);
291 return new Node(parent, name, st);
296 main(int argc, const char* const* argv)
298 // the paths are listed after a "--" argument
299 int argi = 1;
300 for (; argi < argc; argi++) {
301 if (strcmp(argv[argi], "--") == 0)
302 break;
305 if (argi + 1 >= argc) {
306 fprintf(stderr, "Usage: %s <zip arguments> ... -- <paths>\n", argv[0]);
307 exit(1);
310 int zipArgCount = argi;
311 const char* const* paths = argv + argi + 1;
312 int pathCount = argc - argi - 1;
314 // snapshot the hierarchy
315 Node** rootNodes = new Node*[pathCount];
317 for (int i = 0; i < pathCount; i++) {
318 const char* path = paths[i];
319 struct stat st;
320 if (lstat(path, &st) != 0) {
321 fprintf(stderr, "Error: Failed to stat \"%s\": %s\n", path,
322 strerror(errno));
323 exit(1);
326 rootNodes[i] = create_node(NULL, path, st);
327 if (!rootNodes[i]->Scan())
328 exit(1);
331 // create a temp file
332 char tmpFileName[64];
333 sprintf(tmpFileName, "/tmp/diff_zip_%d_XXXXXX", (int)getpid());
334 int tmpFileFD = mkstemp(tmpFileName);
335 if (tmpFileFD < 0) {
336 fprintf(stderr, "Error: Failed create temp file: %s\n",
337 strerror(errno));
338 exit(1);
340 unlink(tmpFileName);
342 // wait
344 printf("Waiting for changes. Press RETURN to continue...");
345 fflush(stdout);
346 char buffer[32];
347 fgets(buffer, sizeof(buffer), stdin);
350 EntryWriter tmpFile(tmpFileFD);
352 for (int i = 0; i < pathCount; i++) {
353 if (rootNodes[i]->CollectDiffEntries(&tmpFile) == DIFF_ERROR)
354 exit(1);
357 // dup the temp file FD to our stdin and exec()
358 dup2(tmpFileFD, 0);
359 close(tmpFileFD);
360 lseek(0, 0, SEEK_SET);
362 char** zipArgs = new char*[zipArgCount + 2];
363 zipArgs[0] = strdup("zip");
364 zipArgs[1] = strdup("-@");
365 for (int i = 1; i < zipArgCount; i++)
366 zipArgs[i + 1] = strdup(argv[i]);
367 zipArgs[zipArgCount + 1] = NULL;
369 execvp("zip", zipArgs);
371 fprintf(stderr, "Error: Failed exec*() zip: %s\n", strerror(errno));
373 return 1;