From bd31302c3c0196f2597a9a36dc2d16f2097be435 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Sun, 6 Apr 2008 23:40:12 +0000 Subject: [PATCH] Added small tool diff_zip that I used for building the latest optional packages. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24849 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/bin/Jamfile | 1 + src/bin/diff_zip.cpp | 374 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 375 insertions(+) create mode 100644 src/bin/diff_zip.cpp diff --git a/src/bin/Jamfile b/src/bin/Jamfile index 65bd3d5c06..025b6204d5 100644 --- a/src/bin/Jamfile +++ b/src/bin/Jamfile @@ -23,6 +23,7 @@ StdBinCommands clear.c clockconfig.c # csplit.c + diff_zip.cpp driveinfo.c # echo.c eject.c diff --git a/src/bin/diff_zip.cpp b/src/bin/diff_zip.cpp new file mode 100644 index 0000000000..f4d4375536 --- /dev/null +++ b/src/bin/diff_zip.cpp @@ -0,0 +1,374 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All rights reserved. + * + * Distributed under the terms of the MIT License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +using std::string; +using std::map; + + +class Directory; +class Node; + +static Node* create_node(Directory* parent, const string& name, + const struct stat& st); + + +enum diff_status { + DIFF_UNCHANGED, + DIFF_REMOVED, + DIFF_CHANGED, + DIFF_ERROR +}; + + +class EntryWriter { +public: + EntryWriter(int fd) + : fFD(fd) + { + } + + void Write(const char* entry) + { + write(fFD, entry, strlen(entry)); + write(fFD, "\n", 1); + } + +private: + int fFD; +}; + + +class Node { +public: + Node(Directory* parent, const string& name, const struct stat& st) + : fParent(parent), + fName(name), + fStat(st) + { + } + + virtual ~Node() + { + } + + Directory* Parent() const { return fParent; } + const string& Name() const { return fName; } + const struct stat& Stat() const { return fStat; } + + string Path() const; + + bool DoStat(struct stat& st) const + { + string path(Path()); + if (lstat(path.c_str(), &st) != 0) + return false; + return true; + } + + virtual bool Scan() + { + return true; + } + + virtual diff_status CollectDiffEntries(EntryWriter* out) const + { + string path(Path()); + struct stat st; + + diff_status status = DiffEntry(path, st); + if (status == DIFF_CHANGED) + out->Write(path.c_str()); + + return status; + } + + virtual void Dump(int level) const + { + printf("%*s%s\n", level, "", fName.c_str()); + } + +protected: + diff_status DiffEntry(const string& path, struct stat& st) const + { + if (lstat(path.c_str(), &st) == 0) { + if (st.st_mode != fStat.st_mode + || st.st_mtime != fStat.st_mtime + || st.st_size != fStat.st_size) { + return DIFF_CHANGED; + } + return DIFF_UNCHANGED; + } else if (errno == ENOENT) { + // that's OK, the entry was removed + return DIFF_REMOVED; + } else { + // some error + fprintf(stderr, "Error: Failed to stat \"%s\": %s\n", + path.c_str(), strerror(errno)); + return DIFF_ERROR; + } + } + +private: + Directory* fParent; + string fName; + struct stat fStat; +}; + + +class Directory : public Node { +public: + Directory(Directory* parent, const string& name, const struct stat& st) + : Node(parent, name, st), + fEntries() + { + } + + void AddEntry(const char* name, Node* node) + { + fEntries[name] = node; + } + + virtual bool Scan() + { + string path(Path()); + DIR* dir = opendir(path.c_str()); + if (dir == NULL) { + fprintf(stderr, "Error: Failed to open directory \"%s\": %s\n", + path.c_str(), strerror(errno)); + return false; + } + + errno = 0; + while (dirent* entry = readdir(dir)) { + if (strcmp(entry->d_name, ".") == 0 + || strcmp(entry->d_name, "..") == 0) { + continue; + } + + string entryPath = path + '/' + entry->d_name; + struct stat st; + if (lstat(entryPath.c_str(), &st) != 0) { + fprintf(stderr, "Error: Failed to stat entry \"%s\": %s\n", + entryPath.c_str(), strerror(errno)); + closedir(dir); + return false; + } + + Node* node = create_node(this, entry->d_name, st); + fEntries[entry->d_name] = node; + + errno = 0; + } + + if (errno != 0) { + fprintf(stderr, "Error: Failed to read directory \"%s\": %s\n", + path.c_str(), strerror(errno)); + closedir(dir); + return false; + } + + closedir(dir); + + // recursively scan the entries + for (EntryMap::iterator it = fEntries.begin(); it != fEntries.end(); + ++it) { + Node* node = it->second; + if (!node->Scan()) + return false; + } + + return true; + } + + virtual diff_status CollectDiffEntries(EntryWriter* out) const + { + string path(Path()); + struct stat st; + + diff_status status = DiffEntry(path, st); + if (status == DIFF_REMOVED || status == DIFF_ERROR) + return status; + + // we print it only if it is no longer a directory + if (!S_ISDIR(st.st_mode)) { + out->Write(path.c_str()); + return DIFF_CHANGED; + } + + // iterate through the "new" entries + DIR* dir = opendir(path.c_str()); + if (dir == NULL) { + fprintf(stderr, "Error: Failed to open directory \"%s\": %s\n", + path.c_str(), strerror(errno)); + return DIFF_ERROR; + } + + errno = 0; + while (dirent* entry = readdir(dir)) { + if (strcmp(entry->d_name, ".") == 0 + || strcmp(entry->d_name, "..") == 0) { + continue; + } + + EntryMap::const_iterator it = fEntries.find(entry->d_name); + if (it == fEntries.end()) { + // new entry + string entryPath = path + "/" + entry->d_name; + out->Write(entryPath.c_str()); + } else { + // old entry -- recurse + diff_status entryStatus = it->second->CollectDiffEntries(out); + if (entryStatus == DIFF_ERROR) { + closedir(dir); + return status; + } + if (entryStatus != DIFF_UNCHANGED) + status = DIFF_CHANGED; + } + + errno = 0; + } + + if (errno != 0) { + fprintf(stderr, "Error: Failed to read directory \"%s\": %s\n", + path.c_str(), strerror(errno)); + closedir(dir); + return DIFF_ERROR; + } + + closedir(dir); + return status; + } + + virtual void Dump(int level) const + { + Node::Dump(level); + + for (EntryMap::const_iterator it = fEntries.begin(); + it != fEntries.end(); ++it) { + it->second->Dump(level + 1); + } + } + + +private: + typedef map EntryMap; + + EntryMap fEntries; +}; + + +string +Node::Path() const +{ + if (fParent == NULL) + return fName; + + return fParent->Path() + '/' + fName; +} + + +static Node* +create_node(Directory* parent, const string& name, const struct stat& st) +{ + if (S_ISDIR(st.st_mode)) + return new Directory(parent, name, st); + return new Node(parent, name, st); +} + + +int +main(int argc, const char* const* argv) +{ + // the paths are listed after a "--" argument + int argi = 1; + for (; argi < argc; argi++) { + if (strcmp(argv[argi], "--") == 0) + break; + } + + if (argi + 1 >= argc) { + fprintf(stderr, "Usage: %s ... -- \n", argv[0]); + exit(1); + } + + int zipArgCount = argi; + const char* const* paths = argv + argi + 1; + int pathCount = argc - argi - 1; + + // snapshot the hierarchy + Node** rootNodes = new Node*[pathCount]; + + for (int i = 0; i < pathCount; i++) { + const char* path = paths[i]; + struct stat st; + if (lstat(path, &st) != 0) { + fprintf(stderr, "Error: Failed to stat \"%s\": %s\n", path, + strerror(errno)); + exit(1); + } + + rootNodes[i] = create_node(NULL, path, st); + if (!rootNodes[i]->Scan()) + exit(1); + } + + // create a temp file + char tmpFileName[64]; + sprintf(tmpFileName, "/tmp/diff_zip_%d_XXXXXX", (int)getpid()); + int tmpFileFD = mkstemp(tmpFileName); + if (tmpFileFD < 0) { + fprintf(stderr, "Error: Failed create temp file: %s\n", + strerror(errno)); + exit(1); + } + unlink(tmpFileName); + + // wait + { + printf("Waiting for changes. Press RETURN to continue..."); + fflush(stdout); + char buffer[32]; + fgets(buffer, sizeof(buffer), stdin); + } + + EntryWriter tmpFile(tmpFileFD); + + for (int i = 0; i < pathCount; i++) { + if (rootNodes[i]->CollectDiffEntries(&tmpFile) == DIFF_ERROR) + exit(1); + } + + // dup the temp file FD to our stdin and exec() + dup2(tmpFileFD, 0); + close(tmpFileFD); + lseek(0, 0, SEEK_SET); + + char** zipArgs = new char*[zipArgCount + 2]; + zipArgs[0] = strdup("zip"); + zipArgs[1] = strdup("-@"); + for (int i = 1; i < zipArgCount; i++) + zipArgs[i + 1] = strdup(argv[i]); + zipArgs[zipArgCount + 1] = NULL; + + execvp("zip", zipArgs); + + fprintf(stderr, "Error: Failed exec*() zip: %s\n", strerror(errno)); + + return 1; +}