commit e0636a2651927691ff939d8cddfc79457cf36b12
parent 75cc5e577f9bff2769b98e2c85645cbbae229c8f
Author: Mohamed Aslan <maslan@sce.carleton.ca>
Date: Fri, 19 Sep 2014 04:02:21 -0600
add basic support for external diff
* some more work is needed on labels before considering this done.
* missing support for ``git'' and/or ``cvs'' patch formats.
Diffstat:
4 files changed, 296 insertions(+), 3 deletions(-)
diff --git a/Makefile b/Makefile
@@ -2,7 +2,8 @@ PROG= baseline
BINDIR= /usr/bin
SRCS= baseline.c config.c common.c session.c objects.c helper.c
-SRCS+= cmd-add.c cmd-branch.c cmd-cat.c cmd-checkout.c cmd-commit.c cmd-help.c cmd-init.c cmd-ls.c cmd-log.c cmd-version.c
+SRCS+= cmd-add.c cmd-branch.c cmd-cat.c cmd-checkout.c cmd-commit.c
+SRCS+= cmd-diff.c cmd-help.c cmd-init.c cmd-ls.c cmd-log.c cmd-version.c
SRCS+= objdb-fs.c
SRCS+= dircache-simple.c
diff --git a/baseline.c b/baseline.c
@@ -48,12 +48,15 @@ main(int argc, char **argv)
else if (!strcmp(argv[1], "commit")) {
cmd_commit(argc - 1, argv + 1);
}
- else if (!strcmp(argv[1], "init")) {
- cmd_init(argc - 1, argv + 1);
+ else if (!strcmp(argv[1], "diff")) {
+ cmd_diff(argc - 1, argv + 1);
}
else if (!strcmp(argv[1], "help")) {
cmd_help(argc - 1, argv + 1);
}
+ else if (!strcmp(argv[1], "init")) {
+ cmd_init(argc - 1, argv + 1);
+ }
else if (!strcmp(argv[1], "ls")) {
cmd_ls(argc - 1, argv + 1);
}
diff --git a/cmd-diff.c b/cmd-diff.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h> /* printf(3) */
+#include <stdlib.h> /* EXIT_SUCCESS */
+#include <string.h> /* strdup(3) */
+#include <fcntl.h> /* open(2) */
+#include <err.h>
+#include <unistd.h> /* read(2), write(2) */
+#include <sys/stat.h> /* S_ISDIR */
+#include <sys/wait.h> /* waitpid(2) */
+
+#include "cmd.h"
+#include "session.h"
+
+#include "objects.h"
+
+#include "common.h"
+
+
+static char *
+make_tmpdir()
+{
+ char *name = NULL;
+ char *old, *new;
+
+ name = strdup("/tmp/baseline.XXXXXX");
+ if (mkdtemp(name) == NULL) {
+ errx(EXIT_FAILURE, "error, failed to create a temporary directory.");
+ }
+ asprintf(&old, "%s/old", name);
+ asprintf(&new, "%s/new", name);
+ if ((mkdir(old, S_IRWXU) < 0) || (mkdir(new, S_IRWXU) < 0))
+ errx(EXIT_FAILURE, "error, failed to write to temporary directory.");
+ free(old);
+ free(new);
+ return name;
+}
+
+static char *
+make_fifo(const char *path, const char *name)
+{
+ char *fifo_name = NULL;
+
+ asprintf(&fifo_name, "%s/%s", path, name);
+ if (mkfifo(fifo_name, S_IRUSR | S_IWUSR) == -1) {
+ errx(EXIT_FAILURE, "error, failed to create FIFO \'%s\'.", fifo_name);
+ }
+ return fifo_name;
+}
+
+static int
+open_fifo(const char *fifo)
+{
+ int fd;
+
+ if ((fd = open(fifo, O_WRONLY)) == -1) {
+ errx(EXIT_FAILURE, "error, failed to open FIFO \'%s\'.", fifo);
+ }
+ return fd;
+}
+
+static void
+copy_to_fifo(struct file *f, int fifo)
+{
+ char buf[2048];
+ ssize_t n;
+
+ while ((n = read(f->fd, buf, sizeof(buf))) > 0) {
+ write(fifo, buf, n);
+ }
+ if (n == -1) {
+ errx(EXIT_FAILURE, "error, failed to read file.");
+ }
+}
+
+static void
+exec_ext_diff(const char *old, const char *old_label, const char *new, const char *new_label)
+{
+ char buf[2048];
+ char *cmd = NULL;
+ size_t n;
+ FILE *fp;
+
+ if (old == NULL && new == NULL)
+ return;
+
+ if (old == NULL)
+ asprintf(&cmd, "diff -u %s %s -L %s -L %s", "/dev/null", new, "/dev/null", new_label == NULL ? new : new_label);
+ else if(new == NULL)
+ asprintf(&cmd, "diff -u %s %s -L %s -L %s", old, "/dev/null", old_label == NULL ? old : old_label, "/dev/null");
+ else
+ asprintf(&cmd, "diff -u %s %s -L %s -L %s", old, new, old_label == NULL ? old : old_label, new_label == NULL ? new : new_label);
+
+ if ((fp = popen(cmd, "r")) == NULL)
+ errx(EXIT_FAILURE, "error, failed to run external diff.");
+ /*
+ while ((n = fread(buf, sizeof(char), sizeof(buf), pf)) > 0)
+ fwrite(buf, sizeof(char), n, stdout);
+ */
+ while ((n = read(fileno(fp), buf, sizeof(buf))) > 0)
+ write(1, buf, n);
+ if (n == -1)
+ errx(EXIT_FAILURE, "error, reading output from external diff.");
+ pclose(fp);
+ free(cmd);
+}
+
+static void
+ext_diff_proc(struct session *s, struct dirent *ent, char *fifo)
+{
+ int fifo_fd;
+ struct file *f;
+
+ fifo_fd = open_fifo(fifo);
+
+ f = baseline_file_new();
+ s->db_ops->select_file(s->db_ctx, ent->id, f);
+
+ copy_to_fifo(f, fifo_fd);
+ close(fifo_fd);
+ unlink(fifo);
+
+ baseline_file_free(f);
+}
+
+static void
+ext_diff(struct session *s, const char *tmpdir, struct dirent *ent1, struct dirent *ent2)
+{
+ char *dir;
+ char *label1, *label2;
+ char *fifo1 = NULL, *fifo2 = NULL;
+ pid_t pid1, pid2;
+
+
+ if (ent1 == NULL && ent2 == NULL)
+ return;
+
+ if (ent1 != NULL) {
+ asprintf(&dir, "%s/old", tmpdir);
+ fifo1 = make_fifo(dir, ent1->name);
+ free(dir);
+ }
+ if(ent2 != NULL) {
+ asprintf(&dir, "%s/new", tmpdir);
+ fifo2 = make_fifo(dir, ent2->name);
+ free(dir);
+ }
+ /* FIXME */
+ label1 = fifo1;
+ label2 = fifo2;
+
+ pid1 = fork();
+ if (pid1 < 0) {
+ errx(EXIT_FAILURE, "error, failed to create a new process.");
+ }
+ else if (pid1 == 0) {
+ exec_ext_diff(fifo1, label1, fifo2, label2);
+ exit(0);
+ }
+ else {
+ if (ent1 != NULL && ent2 != NULL) {
+ pid2 = fork();
+
+ if (pid2 < 0) {
+ errx(EXIT_FAILURE, "error, failed to create a new process.");
+ }
+ else if (pid2 == 0) {
+ ext_diff_proc(s, ent1, fifo1);
+ exit(0);
+ }
+ else {
+ ext_diff_proc(s, ent2, fifo2);
+ }
+ }
+ else {
+ if (ent1 != NULL)
+ ext_diff_proc(s, ent1, fifo1);
+ else
+ ext_diff_proc(s, ent2, fifo2);
+ }
+ waitpid(pid1, NULL, 0);
+ }
+ free(fifo1);
+ free(fifo2);
+}
+
+int
+cmd_diff(int argc, char **argv)
+{
+ char *old, *new;
+ char *tmpdir = NULL;
+ struct session s;
+ struct commit *comm_old, *comm_new;
+ struct dir *dir_old, *dir_new;
+ struct dirent *ent_old, *ent_new;
+
+ if (argc != 3) {
+ errx(EXIT_FAILURE, "wrong number of arguments (%d)\n", argc);
+ }
+ old = strdup(argv[1]);
+ new = strdup(argv[2]);
+
+ baseline_session_begin(&s, 0);
+
+ tmpdir = make_tmpdir();
+
+ /*
+ s.db_ops->branch_get_head(s.db_ctx, s.branch, &head);
+ if (head == NULL)
+ goto ret;
+ */
+
+ comm_old = baseline_commit_new();
+ s.db_ops->select_commit(s.db_ctx, old, comm_old);
+
+ dir_old = baseline_dir_new();
+ s.db_ops->select_dir(s.db_ctx, comm_old->dir, dir_old);
+
+ comm_new = baseline_commit_new();
+ s.db_ops->select_commit(s.db_ctx, new, comm_new);
+
+ dir_new = baseline_dir_new();
+ s.db_ops->select_dir(s.db_ctx, comm_new->dir, dir_new);
+
+ ent_old = dir_old->children;
+ ent_new = dir_new->children;
+ while (1) {
+ if (ent_old == NULL && ent_new == NULL)
+ break;
+ else if (ent_old == NULL) {
+ /* printf("+++ %s\n", ent_new->name); */
+ ext_diff(&s, tmpdir, NULL, ent_new);
+ ent_new = ent_new->next;
+ }
+ else if (ent_new == NULL) {
+ /* printf("--- %s\n", ent_old->name); */
+ ext_diff(&s, tmpdir, ent_old, NULL);
+ ent_old = ent_old->next;
+ }
+ else if (!strcmp(ent_old->name, ent_new->name)) {
+ if (strcmp(ent_old->id, ent_new->id)) {
+ /* printf("*** %s\n", ent_old->name); */
+ ext_diff(&s, tmpdir, ent_old, ent_new);
+ }
+ ent_old = ent_old->next;
+ ent_new = ent_new->next;
+ }
+ else if (strcmp(ent_old->name, ent_new->name) < 0) {
+ /* printf("--- %s\n", ent_old->name); */
+ ext_diff(&s, tmpdir, ent_old, NULL);
+ ent_old = ent_old->next;
+ }
+ else if (strcmp(ent_old->name, ent_new->name) > 0) {
+ /* printf("+++ %s\n", ent_new->name); */
+ ext_diff(&s, tmpdir, NULL, ent_new);
+ ent_new = ent_new->next;
+ }
+ /*
+ if (S_ISDIR(ent->mode))
+ printf("%s/\n", ent->name);
+ else
+ printf("%s\n", ent->name);
+ */
+ }
+
+ baseline_dir_free(dir_old);
+ baseline_dir_free(dir_new);
+
+ remove(tmpdir);
+ free(tmpdir);
+ baseline_session_end(&s);
+ return EXIT_SUCCESS;
+}
+
diff --git a/cmd.h b/cmd.h
@@ -22,6 +22,7 @@ int cmd_branch(int, char **);
int cmd_cat(int, char **);
int cmd_checkout(int, char **);
int cmd_commit(int, char **);
+int cmd_diff(int, char **);
int cmd_help(int, char **);
int cmd_init(int, char **);
int cmd_log(int, char **);