diff --git a/src/ht-builtin-fsck.c b/src/ht-builtin-fsck.c index 61737c5f..31ef7740 100644 --- a/src/ht-builtin-fsck.c +++ b/src/ht-builtin-fsck.c @@ -86,13 +86,20 @@ object_iter_callback (HacktreeRepo *repo, } gboolean -hacktree_builtin_fsck (int argc, const char **argv, const char *prefix, GError **error) +hacktree_builtin_fsck (int argc, char **argv, const char *prefix, GError **error) { + GOptionContext *context; HtFsckData data; gboolean ret = FALSE; HacktreeRepo *repo = NULL; int i; + context = g_option_context_new ("- Check the repository for consistency"); + g_option_context_add_main_entries (context, options, NULL); + + if (!g_option_context_parse (context, &argc, &argv, error)) + goto out; + if (repo_path == NULL) repo_path = "."; @@ -109,6 +116,8 @@ hacktree_builtin_fsck (int argc, const char **argv, const char *prefix, GError * ret = TRUE; out: + if (context) + g_option_context_free (context); g_clear_object (&repo); return ret; } diff --git a/src/ht-builtin-init.c b/src/ht-builtin-init.c index 18f031d0..6c22f4df 100644 --- a/src/ht-builtin-init.c +++ b/src/ht-builtin-init.c @@ -33,14 +33,21 @@ static GOptionEntry options[] = { }; gboolean -hacktree_builtin_init (int argc, const char **argv, const char *prefix, GError **error) +hacktree_builtin_init (int argc, char **argv, const char *prefix, GError **error) { + GOptionContext *context = NULL; gboolean ret = FALSE; char *htdir_path = NULL; char *objects_path = NULL; GFile *htdir = NULL; GFile *objects_dir = NULL; + context = g_option_context_new ("- Check the repository for consistency"); + g_option_context_add_main_entries (context, options, NULL); + + if (!g_option_context_parse (context, &argc, &argv, error)) + goto out; + if (repo_path == NULL) repo_path = "."; @@ -57,6 +64,8 @@ hacktree_builtin_init (int argc, const char **argv, const char *prefix, GError * ret = TRUE; out: + if (context) + g_option_context_free (context); g_free (htdir_path); g_clear_object (&htdir); return ret; diff --git a/src/ht-builtin-link-file.c b/src/ht-builtin-link-file.c index 5944565d..80f511d9 100644 --- a/src/ht-builtin-link-file.c +++ b/src/ht-builtin-link-file.c @@ -27,18 +27,30 @@ #include static char *repo_path; +static gboolean ignore_exists; +static gboolean force; + static GOptionEntry options[] = { - { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", NULL }, + { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" }, + { "ignore-exists", 'n', 0, G_OPTION_ARG_NONE, &ignore_exists, "Don't error if file exists", NULL }, + { "force", 'f', 0, G_OPTION_ARG_NONE, &force, "If object exists, relink file", NULL }, { NULL } }; gboolean -hacktree_builtin_link_file (int argc, const char **argv, const char *prefix, GError **error) +hacktree_builtin_link_file (int argc, char **argv, const char *prefix, GError **error) { + GOptionContext *context; gboolean ret = FALSE; HacktreeRepo *repo = NULL; int i; + context = g_option_context_new ("- Create a new hard link in the repository"); + g_option_context_add_main_entries (context, options, NULL); + + if (!g_option_context_parse (context, &argc, &argv, error)) + goto out; + if (repo_path == NULL) repo_path = "."; @@ -46,14 +58,16 @@ hacktree_builtin_link_file (int argc, const char **argv, const char *prefix, GEr if (!hacktree_repo_check (repo, error)) goto out; - for (i = 0; i < argc; i++) + for (i = 0; i < argc-1; i++) { - if (!hacktree_repo_link_file (repo, argv[i], error)) + if (!hacktree_repo_link_file (repo, argv[i+1], ignore_exists, force, error)) goto out; } ret = TRUE; out: + if (context) + g_option_context_free (context); g_clear_object (&repo); return ret; } diff --git a/src/ht-builtins.h b/src/ht-builtins.h index 58e69144..f9d8798f 100644 --- a/src/ht-builtins.h +++ b/src/ht-builtins.h @@ -32,13 +32,13 @@ typedef enum { typedef struct { const char *name; - gboolean (*fn) (int argc, const char **argv, const char *prefix, GError **error); + gboolean (*fn) (int argc, char **argv, const char *prefix, GError **error); int flags; /* HacktreeBuiltinFlags */ } HacktreeBuiltin; -gboolean hacktree_builtin_init (int argc, const char **argv, const char *prefix, GError **error); -gboolean hacktree_builtin_link_file (int argc, const char **argv, const char *prefix, GError **error); -gboolean hacktree_builtin_fsck (int argc, const char **argv, const char *prefix, GError **error); +gboolean hacktree_builtin_init (int argc, char **argv, const char *prefix, GError **error); +gboolean hacktree_builtin_link_file (int argc, char **argv, const char *prefix, GError **error); +gboolean hacktree_builtin_fsck (int argc, char **argv, const char *prefix, GError **error); G_END_DECLS diff --git a/src/libhacktree/hacktree-repo.c b/src/libhacktree/hacktree-repo.c index a95f92d6..7f6fce09 100644 --- a/src/libhacktree/hacktree-repo.c +++ b/src/libhacktree/hacktree-repo.c @@ -194,12 +194,14 @@ prepare_dir_for_checksum_get_object_path (HacktreeRepo *self, } static gboolean -link_one_file (HacktreeRepo *self, const char *path, GError **error) +link_one_file (HacktreeRepo *self, const char *path, + gboolean ignore_exists, gboolean force, GError **error) { HacktreeRepoPrivate *priv = GET_PRIVATE (self); char *src_basename = NULL; char *src_dirname = NULL; char *dest_basename = NULL; + char *tmp_dest_basename = NULL; char *dest_dirname = NULL; GChecksum *id = NULL; DIR *src_dir = NULL; @@ -234,11 +236,33 @@ link_one_file (HacktreeRepo *self, const char *path, GError **error) ht_util_set_error_from_errno (error, errno); goto out; } - - if (linkat (dirfd (src_dir), src_basename, dirfd (dest_dir), dest_basename, 0) < 0) + + if (force) { - ht_util_set_error_from_errno (error, errno); - goto out; + tmp_dest_basename = g_strconcat (dest_basename, ".tmp", NULL); + (void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0); + } + else + tmp_dest_basename = g_strdup (dest_basename); + + if (linkat (dirfd (src_dir), src_basename, dirfd (dest_dir), tmp_dest_basename, 0) < 0) + { + if (errno != EEXIST || !ignore_exists) + { + ht_util_set_error_from_errno (error, errno); + goto out; + } + } + + if (force) + { + if (renameat (dirfd (dest_dir), tmp_dest_basename, + dirfd (dest_dir), dest_basename) < 0) + { + ht_util_set_error_from_errno (error, errno); + goto out; + } + (void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0); } ret = TRUE; @@ -252,6 +276,7 @@ link_one_file (HacktreeRepo *self, const char *path, GError **error) g_free (src_basename); g_free (src_dirname); g_free (dest_basename); + g_free (tmp_dest_basename); g_free (dest_dirname); return ret; } @@ -259,13 +284,15 @@ link_one_file (HacktreeRepo *self, const char *path, GError **error) gboolean hacktree_repo_link_file (HacktreeRepo *self, const char *path, + gboolean ignore_exists, + gboolean force, GError **error) { HacktreeRepoPrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (priv->inited, FALSE); - return link_one_file (self, path, error); + return link_one_file (self, path, ignore_exists, force, error); } static gboolean diff --git a/src/libhacktree/hacktree-repo.h b/src/libhacktree/hacktree-repo.h index 08992959..633771cf 100644 --- a/src/libhacktree/hacktree-repo.h +++ b/src/libhacktree/hacktree-repo.h @@ -55,6 +55,8 @@ gboolean hacktree_repo_check (HacktreeRepo *repo, GError **error); gboolean hacktree_repo_link_file (HacktreeRepo *repo, const char *path, + gboolean ignore_exists, + gboolean force, GError **error); typedef void (*HacktreeRepoObjectIter) (HacktreeRepo *repo, const char *path, diff --git a/src/main.c b/src/main.c index 7ff1c24d..d39ef880 100644 --- a/src/main.c +++ b/src/main.c @@ -68,6 +68,8 @@ main (int argc, g_type_init (); + g_set_prgname (argv[0]); + builtin = builtins; if (argc < 2) @@ -80,12 +82,24 @@ main (int argc, GError *error = NULL; if (strcmp (cmd, builtin->name) == 0) { - if (!builtin->fn (argc - 2, (const char**)argv + 2, NULL, &error)) + int i; + int tmp_argc; + char **tmp_argv; + + tmp_argc = argc - 1; + tmp_argv = g_new0 (char *, tmp_argc + 1); + + tmp_argv[0] = (char*)builtin->name; + for (i = 0; i < tmp_argc; i++) + tmp_argv[i+1] = argv[i+2]; + if (!builtin->fn (tmp_argc, tmp_argv, NULL, &error)) { + g_free (tmp_argv); g_printerr ("%s\n", error->message); g_clear_error (&error); return 1; } + g_free (tmp_argv); return 0; } builtin++;