# #
#############################################################################*/
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <fts.h>
#include <linux/limits.h>
#include <stdint.h>
#include <stdio.h>
return pakfire_rmtree(cache_path, 0);
}
-static int pakfire_repo_scan_archive(struct pakfire_repo* repo, const char* path) {
+struct pakfire_repo_scan_ctx {
+ // Progress
+ struct pakfire_progress* progress;
+
+ // Flags
+ int flags;
+
+ // Counter
+ unsigned int num_archives;
+};
+
+typedef int (*pakfire_repo_scan_callback)(
+ struct pakfire_repo* repo, struct pakfire_repo_scan_ctx* scan_ctx, FTSENT* entry);
+
+static int __pakfire_repo_scan(struct pakfire_repo* repo,
+ struct pakfire_repo_scan_ctx* scan_ctx, pakfire_repo_scan_callback callback) {
+ FTSENT* entry = NULL;
+ FTS* fts = NULL;
+ int r;
+
+ // Fetch the repository path
+ const char* path = pakfire_repo_get_path(repo);
+ if (!path)
+ return -EINVAL;
+
+ char* paths[] = {
+ (char*)path, NULL,
+ };
+
+ // Open the path
+ fts = fts_open(paths, FTS_PHYSICAL|FTS_NOCHDIR|FTS_NOSTAT, NULL);
+ if (!fts) {
+ ERROR(repo->ctx, "Could not open %s: %m\n", path);
+ r = -errno;
+ goto ERROR;
+ }
+
+ // Scan for everything
+ for (;;) {
+ entry = fts_read(fts);
+ if (!entry)
+ break;
+
+ // We only care about actual files
+ if (!(entry->fts_info & FTS_F))
+ continue;
+
+ // Skip anything that doesn't have the correct file extension
+ if (!pakfire_path_match("**.pfm", entry->fts_path))
+ continue;
+
+ // Call the callback
+ r = callback(repo, scan_ctx, entry);
+ if (r)
+ goto ERROR;
+ }
+
+ERROR:
+ if (fts)
+ fts_close(fts);
+
+ return r;
+}
+
+static int __pakfire_repo_scan_count(struct pakfire_repo* repo,
+ struct pakfire_repo_scan_ctx* scan_ctx, FTSENT* entry) {
+ ++scan_ctx->num_archives;
+
+ return 0;
+}
+
+static int __pakfire_repo_scan_archive(struct pakfire_repo* repo,
+ struct pakfire_repo_scan_ctx* scan_ctx, FTSENT* entry) {
struct pakfire_archive* archive = NULL;
struct pakfire_package* package = NULL;
int r;
- DEBUG(repo->ctx, "Scanning %s...\n", path);
-
// Open archive
- r = pakfire_archive_open(&archive, repo->pakfire, path);
+ r = pakfire_archive_open(&archive, repo->pakfire, entry->fts_path);
if (r < 0)
goto ERROR;
if (r < 0)
goto ERROR;
+ // Increment progress bar
+ pakfire_progress_increment(scan_ctx->progress, 1);
+
ERROR:
if (package)
pakfire_package_unref(package);
return r;
}
-static int pakfire_repo_scan_filter(const struct dirent* dirent) {
- // Skip anything hidden
- if (*dirent->d_name == '.')
- return 0;
-
- return !!pakfire_path_match("**.pfm", dirent->d_name);
-}
-
PAKFIRE_EXPORT int pakfire_repo_scan(struct pakfire_repo* repo, int flags) {
- struct pakfire_progress* progress = NULL;
- struct dirent** archives = NULL;
- char archive[PATH_MAX];
- int num_archives = 0;
- int fd = -EBADF;
int r;
+ // Scan context
+ struct pakfire_repo_scan_ctx scan_ctx = {
+ .flags = flags,
+ };
+
// Fetch name
const char* name = pakfire_repo_get_name(repo);
if (!path)
return -EINVAL;
- // Open path
- fd = open(path, O_DIRECTORY|O_CLOEXEC);
- if (fd < 0) {
- switch (errno) {
- // If the directory does not exist, there is nothing to scan
- case ENOENT:
- r = 0;
- goto ERROR;
-
- default:
- r = -errno;
- goto ERROR;
- }
- }
-
- // Find all archives in the directory
- num_archives = scandirat(fd, ".", &archives, pakfire_repo_scan_filter, versionsort);
- if (num_archives < 0) {
- r = num_archives;
- goto ERROR;
- }
-
// Create progress indicator
- r = pakfire_progress_create(&progress, repo->ctx,
+ r = pakfire_progress_create(&scan_ctx.progress, repo->ctx,
PAKFIRE_PROGRESS_SHOW_COUNTER|PAKFIRE_PROGRESS_SHOW_ELAPSED_TIME, NULL);
if (r)
goto ERROR;
// Add title to progress
- r = pakfire_progress_set_title(progress, _("Scanning %s"), name);
+ r = pakfire_progress_set_title(scan_ctx.progress, _("Scanning %s"), name);
if (r)
goto ERROR;
- // Start progress
- r = pakfire_progress_start(progress, num_archives);
+ // Start progress (so that we will let the user know something is happening if
+ // scanning for files takes a little bit longer...)
+ r = pakfire_progress_start(scan_ctx.progress, 0);
if (r < 0)
goto ERROR;
- // Scan all packages
- for (int i = 0; i < num_archives; i++) {
- r = pakfire_path_append(archive, path, archives[i]->d_name);
- if (r < 0)
- goto ERROR;
+ // Count how many files we have
+ r = __pakfire_repo_scan(repo, &scan_ctx, __pakfire_repo_scan_count);
+ if (r < 0)
+ goto ERROR;
- r = pakfire_repo_scan_archive(repo, archive);
- if (r < 0)
- goto ERROR;
+ // Update the progressbar
+ pakfire_progress_set_max_value(scan_ctx.progress, scan_ctx.num_archives);
- // Increment progress bar
- pakfire_progress_increment(progress, 1);
- }
+ // Scan all packages
+ r = __pakfire_repo_scan(repo, &scan_ctx, __pakfire_repo_scan_archive);
+ if (r < 0)
+ goto ERROR;
// Mark repository data as changed
pakfire_repo_has_changed(repo);
// Finish the progress
- r = pakfire_progress_finish(progress);
+ r = pakfire_progress_finish(scan_ctx.progress);
if (r < 0)
goto ERROR;
r = 0;
ERROR:
- if (archives) {
- for (int i = 0; i < num_archives; i++)
- free(archives[i]);
- free(archives);
- }
- if (progress)
- pakfire_progress_unref(progress);
- if (fd >= 0)
- close(fd);
+ if (scan_ctx.progress)
+ pakfire_progress_unref(scan_ctx.progress);
return r;
}