diff options
| -rw-r--r-- | src/main.c | 105 |
1 files changed, 105 insertions, 0 deletions
@@ -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}, |
