aboutsummaryrefslogtreecommitdiff
path: root/src/main.c
diff options
context:
space:
mode:
authorTristan Riehs <tristan.riehs@inria.fr>2025-12-28 17:29:50 +0100
committerTristan Riehs <tristan.riehs@inria.fr>2025-12-28 17:29:50 +0100
commite9c79ffd4251264cf135ae2996739ceefc914d2d (patch)
treeff49a5a48b732838b49230959fa6b937501e786a /src/main.c
parentec87fd9ef207138824586336330d2b4c7b777896 (diff)
First version of the export command
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c105
1 files changed, 105 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c
index 15b658c..2a666db 100644
--- a/src/main.c
+++ b/src/main.c
@@ -8,6 +8,7 @@
#include <string.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
@@ -199,6 +200,109 @@ static int table_next_id(sqlite3 *db, const char *table)
return last_id + 1;
}
+static void ftag_export_help(void)
+{
+ printf("Usage: ftag export [OPTION]... ARCHIVE\n");
+ printf("Export files from the database to an archive. Files are read from\n");
+ printf("standard input, one per line. Files have to be canonical names that\n");
+ printf("exist in the database.\n");
+ /* TODO: support files outside of the database
+
+ A file name starting with a slash can be considered as a file to take
+ from the filesystem, not from the database. */
+ printf("Available options:\n");
+ printf(" -h print this help\n");
+ printf(" -n TODO: add user name as a suffix\n");
+ printf(" -f TODO: use files' full names\n");
+}
+
+static void ftag_export(int argc, char **argv)
+{
+ if (argc != 1) {
+ ftag_export_help();
+ exit(EXIT_FAILURE);
+ }
+ char *archive_dir = argv[0];
+
+ char archive_file[64];
+ strcpy(archive_file, archive_dir);
+ strcat(archive_file, ".tar.gz");
+
+ /* step 1: create temporary directory having the name of the archive */
+ char tmp_dir[64];
+ strcpy(tmp_dir, "/tmp/");
+ strcat(tmp_dir, archive_dir);
+ int rc = mkdir(tmp_dir, 0755);
+ if (rc == -1) {
+ fprintf(stderr, "mkdir: %s:", tmp_dir);
+ perror("");
+ exit(EXIT_FAILURE);
+ }
+
+ /* step 2: copy archive files to the temporary directory */
+ size_t line_size = 256;
+ ssize_t line_len;
+ char *line = malloc(line_size);
+ char in[512];
+ int in_fd;
+ char out[128];
+ int out_fd;
+ while ((line_len = getline(&line, &line_size, stdin)) != -1) {
+ remove_ending_newline(line);
+ snprintf(in, sizeof(in)-1, "%s/files/%s", FTAG_ROOT, line);
+ snprintf(out, sizeof(out)-1, "%s/%s", tmp_dir, line);
+ in_fd = open(in, O_RDONLY);
+ if (in_fd == -1) {
+ fprintf(stderr, "open: %s:", in);
+ perror("");
+ exit(EXIT_FAILURE);
+ }
+ out_fd = open(out, O_WRONLY | O_CREAT, 0644);
+ if (out_fd == -1) {
+ fprintf(stderr, "open: %s:", out);
+ perror("");
+ exit(EXIT_FAILURE);
+ }
+ ssize_t written_bytes;
+ while ((written_bytes = sendfile(out_fd, in_fd, NULL, 4096)) == 4096)
+ ;
+ close(in_fd);
+ close(out_fd);
+ }
+ free(line);
+
+ /* step 3: invoke tar to build the archive */
+ rc = fork();
+ if (rc == 0) {
+ execlp("tar",
+ "tar",
+ "--directory", "/tmp",
+ "--create",
+ "--gzip",
+ "--file", archive_file,
+ archive_dir,
+ NULL);
+ fprintf(stderr, "exec: tar:");
+ perror("");
+ exit(EXIT_FAILURE);
+ }
+ else if (rc > 0) {
+ int status;
+ wait(&status);
+ if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) {
+ fprintf(stderr, "ftag export: child process exited abnormally\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+ else {
+ perror("fork");
+ exit(EXIT_FAILURE);
+ }
+
+ /* step 4: clean /tmp */
+ execlp("rm", "rm", "--recursive", "--force", tmp_dir, NULL);
+}
+
/* Create ftag's database and directories. */
static void ftag_init(int, char **)
{
@@ -869,6 +973,7 @@ int main(int argc, char *argv[])
}
const struct ftag_command toplevel_commands[] = {
+ {.name = "export", .func = ftag_export},
{.name = "init", .func = ftag_init},
{.name = "file", .func = ftag_file},
{.name = "-h", .func = ftag_help},