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
This commit is contained in:
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, [email protected]. All rights reserved.
|
||||
*
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
|
||||
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<string, Node*> 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 <zip arguments> ... -- <paths>\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;
|
||||
}
|
||||
Reference in New Issue
Block a user