2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All rights reserved.
4 * Distributed under the terms of the MIT License.
26 static Node
* create_node(Directory
* parent
, const string
& name
,
27 const struct stat
& st
);
45 void Write(const char* entry
)
47 write(fFD
, entry
, strlen(entry
));
58 Node(Directory
* parent
, const string
& name
, const struct stat
& st
)
69 Directory
* Parent() const { return fParent
; }
70 const string
& Name() const { return fName
; }
71 const struct stat
& Stat() const { return fStat
; }
75 bool DoStat(struct stat
& st
) const
78 if (lstat(path
.c_str(), &st
) != 0)
88 virtual diff_status
CollectDiffEntries(EntryWriter
* out
) const
93 diff_status status
= DiffEntry(path
, st
);
94 if (status
== DIFF_CHANGED
)
95 out
->Write(path
.c_str());
100 virtual void Dump(int level
) const
102 printf("%*s%s\n", level
, "", fName
.c_str());
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
) {
114 return DIFF_UNCHANGED
;
115 } else if (errno
== ENOENT
) {
116 // that's OK, the entry was removed
120 fprintf(stderr
, "Error: Failed to stat \"%s\": %s\n",
121 path
.c_str(), strerror(errno
));
133 class Directory
: public Node
{
135 Directory(Directory
* parent
, const string
& name
, const struct stat
& st
)
136 : Node(parent
, name
, st
),
141 void AddEntry(const char* name
, Node
* node
)
143 fEntries
[name
] = node
;
149 DIR* dir
= opendir(path
.c_str());
151 fprintf(stderr
, "Error: Failed to open directory \"%s\": %s\n",
152 path
.c_str(), strerror(errno
));
157 while (dirent
* entry
= readdir(dir
)) {
158 if (strcmp(entry
->d_name
, ".") == 0
159 || strcmp(entry
->d_name
, "..") == 0) {
163 string entryPath
= path
+ '/' + entry
->d_name
;
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
));
172 Node
* node
= create_node(this, entry
->d_name
, st
);
173 fEntries
[entry
->d_name
] = node
;
179 fprintf(stderr
, "Error: Failed to read directory \"%s\": %s\n",
180 path
.c_str(), strerror(errno
));
187 // recursively scan the entries
188 for (EntryMap::iterator it
= fEntries
.begin(); it
!= fEntries
.end();
190 Node
* node
= it
->second
;
198 virtual diff_status
CollectDiffEntries(EntryWriter
* out
) const
203 diff_status status
= DiffEntry(path
, st
);
204 if (status
== DIFF_REMOVED
|| status
== DIFF_ERROR
)
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());
213 // iterate through the "new" entries
214 DIR* dir
= opendir(path
.c_str());
216 fprintf(stderr
, "Error: Failed to open directory \"%s\": %s\n",
217 path
.c_str(), strerror(errno
));
222 while (dirent
* entry
= readdir(dir
)) {
223 if (strcmp(entry
->d_name
, ".") == 0
224 || strcmp(entry
->d_name
, "..") == 0) {
228 EntryMap::const_iterator it
= fEntries
.find(entry
->d_name
);
229 if (it
== fEntries
.end()) {
231 string entryPath
= path
+ "/" + entry
->d_name
;
232 out
->Write(entryPath
.c_str());
234 // old entry -- recurse
235 diff_status entryStatus
= it
->second
->CollectDiffEntries(out
);
236 if (entryStatus
== DIFF_ERROR
) {
240 if (entryStatus
!= DIFF_UNCHANGED
)
241 status
= DIFF_CHANGED
;
248 fprintf(stderr
, "Error: Failed to read directory \"%s\": %s\n",
249 path
.c_str(), strerror(errno
));
258 virtual void Dump(int level
) const
262 for (EntryMap::const_iterator it
= fEntries
.begin();
263 it
!= fEntries
.end(); ++it
) {
264 it
->second
->Dump(level
+ 1);
270 typedef map
<string
, Node
*> EntryMap
;
282 return fParent
->Path() + '/' + fName
;
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
300 for (; argi
< argc
; argi
++) {
301 if (strcmp(argv
[argi
], "--") == 0)
305 if (argi
+ 1 >= argc
) {
306 fprintf(stderr
, "Usage: %s <zip arguments> ... -- <paths>\n", argv
[0]);
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
];
320 if (lstat(path
, &st
) != 0) {
321 fprintf(stderr
, "Error: Failed to stat \"%s\": %s\n", path
,
326 rootNodes
[i
] = create_node(NULL
, path
, st
);
327 if (!rootNodes
[i
]->Scan())
331 // create a temp file
332 char tmpFileName
[64];
333 sprintf(tmpFileName
, "/tmp/diff_zip_%d_XXXXXX", (int)getpid());
334 int tmpFileFD
= mkstemp(tmpFileName
);
336 fprintf(stderr
, "Error: Failed create temp file: %s\n",
344 printf("Waiting for changes. Press RETURN to continue...");
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
)
357 // dup the temp file FD to our stdin and exec()
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
));