summaryrefslogtreecommitdiff
path: root/tbdiff/tbdiff-stat.c
diff options
context:
space:
mode:
Diffstat (limited to 'tbdiff/tbdiff-stat.c')
-rw-r--r--tbdiff/tbdiff-stat.c265
1 files changed, 265 insertions, 0 deletions
diff --git a/tbdiff/tbdiff-stat.c b/tbdiff/tbdiff-stat.c
new file mode 100644
index 0000000..fd8964e
--- /dev/null
+++ b/tbdiff/tbdiff-stat.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2011-2012 Codethink Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <tbdiff/tbdiff-stat.h>
+
+static tbd_stat_t*
+tbd_stat_from_path(const char *name,
+ const char *path)
+{
+ struct stat info;
+
+ if(lstat(path, &info) != 0)
+ return NULL;
+
+ size_t nlen = strlen(name);
+ tbd_stat_t *ret = (tbd_stat_t*)malloc(sizeof(tbd_stat_t) + (nlen + 1));
+ if(ret == NULL)
+ return NULL;
+
+ ret->parent = NULL;
+ ret->size = 0;
+ ret->name = (char*)((uintptr_t)ret + sizeof(tbd_stat_t));
+ memcpy(ret->name, name, (nlen + 1));
+
+ if(S_ISREG(info.st_mode)) {
+ ret->type = TBD_STAT_TYPE_FILE;
+ ret->size = info.st_size;
+ } else if(S_ISDIR(info.st_mode)) {
+ ret->type = TBD_STAT_TYPE_DIR;
+ DIR *dp = opendir(path);
+
+ if(dp == NULL) {
+ free(ret);
+ return NULL;
+ }
+
+ /* FIXME: Remove the need for directory size? */
+ struct dirent *ds;
+ for(ds = readdir(dp); ds != NULL; ds = readdir(dp)) {
+ if((strcmp(ds->d_name, ".") == 0)
+ || (strcmp(ds->d_name, "..") == 0))
+ continue;
+
+ ret->size++;
+ }
+ closedir(dp);
+ } else if(S_ISLNK(info.st_mode))
+ ret->type = TBD_STAT_TYPE_SYMLINK;
+ else if(S_ISCHR(info.st_mode))
+ ret->type = TBD_STAT_TYPE_CHRDEV;
+ else if(S_ISBLK(info.st_mode))
+ ret->type = TBD_STAT_TYPE_BLKDEV;
+ else if(S_ISFIFO(info.st_mode))
+ ret->type = TBD_STAT_TYPE_FIFO;
+ else if(S_ISSOCK(info.st_mode))
+ ret->type = TBD_STAT_TYPE_SOCKET;
+ else {
+ free(ret);
+ return NULL;
+ }
+
+ ret->rdev = (uint32_t)info.st_rdev;
+ ret->uid = (uid_t)info.st_uid;
+ ret->gid = (gid_t)info.st_gid;
+ ret->mode = (mode_t)info.st_mode;
+ ret->mtime = (time_t)info.st_mtime;
+ return ret;
+}
+
+tbd_stat_t*
+tbd_stat(const char *path)
+{
+ tbd_stat_t *ret = tbd_stat_from_path(path, path);
+ return ret;
+}
+
+void
+tbd_stat_free(tbd_stat_t *file)
+{
+ free(file);
+}
+
+void
+tbd_stat_print(tbd_stat_t *file)
+{
+ (void)file;
+}
+
+tbd_stat_t*
+tbd_stat_entry(tbd_stat_t *file, uint32_t entry)
+{
+ if((file == NULL)
+ || (file->type != TBD_STAT_TYPE_DIR)
+ || (entry >= file->size))
+ return NULL;
+
+ char *path = tbd_stat_path(file);
+ DIR *dp = opendir(path);
+ free (path);
+
+ if(dp == NULL)
+ return NULL;
+
+ uintptr_t i;
+ struct dirent *ds;
+ for(i = 0; i <= entry; i++) {
+ ds = readdir(dp);
+ if(ds == NULL) {
+ closedir(dp);
+ return NULL;
+ }
+
+ if((strcmp(ds->d_name, ".") == 0) ||
+ (strcmp(ds->d_name, "..") == 0))
+ i--;
+ }
+ char *name = strndup(ds->d_name, ds->d_reclen-offsetof(struct dirent, d_name));
+ closedir (dp);
+
+ char *spath = tbd_stat_subpath(file, name);
+ if(spath == NULL)
+ return NULL;
+
+ tbd_stat_t *ret = tbd_stat_from_path(name, (const char*)spath);
+
+ free(name);
+ free(spath);
+
+ if (ret == NULL)
+ return NULL;
+
+ ret->parent = file;
+ return ret;
+}
+
+tbd_stat_t*
+tbd_stat_entry_find(tbd_stat_t *file,
+ const char *name)
+{
+ if((file == NULL)
+ || (file->type != TBD_STAT_TYPE_DIR))
+ return NULL;
+
+ char *path = tbd_stat_path (file);
+ DIR *dp = opendir(path);
+ free (path);
+
+ if(dp == NULL)
+ return NULL;
+
+ struct dirent *ds;
+ for(ds = readdir(dp); ds != NULL; ds = readdir(dp)) {
+ if(strcmp(ds->d_name, name) == 0) {
+ char *spath = tbd_stat_subpath(file, ds->d_name);
+
+ if(spath == NULL) {
+ closedir (dp);
+ return NULL;
+ }
+
+ tbd_stat_t *ret = tbd_stat_from_path(ds->d_name, (const char*)spath);
+ free(spath);
+ ret->parent = file;
+
+ closedir (dp);
+ return ret;
+ }
+ }
+
+ closedir (dp);
+ return NULL;
+}
+
+char*
+tbd_stat_subpath(tbd_stat_t *file,
+ const char *entry)
+{
+ if(file == NULL)
+ return NULL;
+
+ size_t elen = ((entry == NULL) ? 0 : (strlen(entry) + 1));
+ size_t plen;
+
+ tbd_stat_t *root;
+ for(root = file, plen = 0;
+ root != NULL;
+ plen += (strlen(root->name) + 1), root = (tbd_stat_t*)root->parent);
+
+ plen += elen;
+
+ char *path = (char*)malloc(plen);
+ if(path == NULL)
+ return NULL;
+ char *ptr = &path[plen];
+
+ if(entry != NULL) {
+ ptr = (char*)((uintptr_t)ptr - elen);
+ memcpy(ptr, entry, elen);
+ }
+
+ for(root = file; root != NULL; root = (tbd_stat_t*)root->parent) {
+ size_t rlen = strlen(root->name) + 1;
+ ptr = (char*)((uintptr_t)ptr - rlen);
+ memcpy(ptr, root->name, rlen);
+ if((file != root) || (entry != NULL))
+ ptr[rlen - 1] = '/';
+ }
+
+ return path;
+}
+
+char*
+tbd_stat_path(tbd_stat_t *file)
+{
+ return tbd_stat_subpath(file, NULL);
+}
+
+int
+tbd_stat_open(tbd_stat_t *file, int flags)
+{
+ char *path = tbd_stat_path(file);
+ if(path == NULL)
+ return -1;
+ int fd = open(path, flags);
+ free(path);
+ return fd;
+}
+
+FILE*
+tbd_stat_fopen(tbd_stat_t *file,
+ const char *mode)
+{
+ char *path = tbd_stat_path(file);
+ if(path == NULL)
+ return NULL;
+ FILE *fp = fopen(path, mode);
+ free(path);
+ return fp;
+}