this is still kinda proof-of-conceptish, most notably
two things need to be resolved before finalizing:

- ensure that the conffile db references the right paths
  during all accesses (i.e. the packaged conffile vs the
  "resolved" conffile name).
- decide on the behavior/interface.  currently if the
  merge is succesful it goes ahead and copies it over
  and pretends that the user said n/k.  maybe merging
  should involve an extra inspection step.
---
 lib/dpkg.h      |    1 +
 src/conffiles.c |   51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/conffiles.h |    6 ++++++
 src/configure.c |   19 ++++++++++++++++---
 4 files changed, 74 insertions(+), 3 deletions(-)

diff --git a/lib/dpkg.h b/lib/dpkg.h
index de46930..a9e00f8 100644
--- a/lib/dpkg.h
+++ b/lib/dpkg.h
@@ -145,6 +145,7 @@
 #define FIND           "find"
 #define SHELL          "sh"
 #define DIFF           "diff"
+#define DIFF3          "diff3"
 
 #define SHELLENVIR     "SHELL"
 
diff --git a/src/conffiles.c b/src/conffiles.c
index fd9b47d..fd444ec 100644
--- a/src/conffiles.c
+++ b/src/conffiles.c
@@ -143,3 +143,54 @@ void conff_db_remove(const char *pkg, conff_flag f){
   ensure_pathname_nonexisting(cfgdb);
   free(cfgdb);
 }
+
+int conff_automerge(const char *pkg, const char *pathname){
+  int res, tmpfd, mrgfd;
+  char tmpfn[] = "dpkg-merge.XXXXXX"; /* temporary file name */
+  size_t cmdsz=0;
+  char *cmdbuf = NULL, *cnfold = NULL, *cnfnew = NULL;
+
+  /* the 'old' version is actually the current dist version in the db */
+  cnfold=conff_db_path(pkg, pathname, conff_db_cur);
+  cnfnew=conff_db_path(pkg, pathname, conff_db_new);
+
+  if( (tmpfd = mkstemp(tmpfn)) == -1 ){
+    ohshite("conff_automerge: mkstemp failed");
+  }
+  if( fchmod(tmpfd, (S_IRUSR|S_IWUSR)) || close(tmpfd) ) {
+    ohshite("conff_automerge: chmod/close failed");
+  }
+
+  /* DIFF3 + " -m " + new + " " + old + " " + cur + " > " + tmpfile + '\0' */
+  cmdsz = strlen(DIFF3) + 4 + strlen(cnfnew) + 1 + strlen(cnfold) 
+          + 1 + strlen(pathname) + 3 + strlen(tmpfn) + 1;
+  cmdbuf = m_malloc(cmdsz);
+  snprintf(cmdbuf, cmdsz, "%s -m %s %s %s > %s", 
+           DIFF3, cnfnew, cnfold, pathname, tmpfn);
+
+  /* let's give it a go! */
+  if( (res = system(cmdbuf)) == 0 ){
+    /* copy the merge result by fd->fd, to respect symlinks */
+    if( (tmpfd = open(tmpfn, O_RDONLY)) == -1 ) {
+      ohshite("conff_automerge: re-opening merge tmpfile %s failed", tmpfn);
+    }
+    if( (mrgfd = open(pathname, O_WRONLY)) == -1 ) {
+      ohshite("conff_automerge: opening %s for merging failed", pathname);
+    }
+    fd_fd_copy(tmpfd, mrgfd, -1, "merging conffile %s", pathname);
+    if( close(tmpfd) || close(mrgfd) ){
+      ohshite("conff_automerge: closing merge files for %s failed", pathname);
+    }
+  }
+
+  if(unlink(tmpfn)){
+    ohshite("conff_automerge: unlink merge tmpfile failed");
+  }
+
+  free(cmdbuf);
+  free(cnfnew);
+  free(cnfold);
+
+  return res;
+}
+
diff --git a/src/conffiles.h b/src/conffiles.h
index 2de5eed..484034f 100644
--- a/src/conffiles.h
+++ b/src/conffiles.h
@@ -44,5 +44,11 @@ void conff_db_remove(const char *pkg, conff_flag f);
  */
 void conff_commit_new(const char *pkg);
 
+/* attempt to automagically merge the 3-way delta for conffile at pathname
+ * passes the return value of diff3, aka nonzero on merge failure
+ */
+/* XXX remember to check for symlink corner cases */
+int conff_automerge(const char *pkg, const char *pathname);
+
 #endif /* CONFFILES_H */
 
diff --git a/src/configure.c b/src/configure.c
index e0afb6d..901f511 100644
--- a/src/configure.c
+++ b/src/configure.c
@@ -52,7 +52,7 @@ static void md5hash(struct pkginfo *pkg, char **hashbuf, 
const char *fn);
 static void copyfileperm(const char* source, const char* target);
 static void showdiff(const char* old, const char* new);
 static void suspend(void);
-static enum conffopt promptconfaction(const char* cfgfile, const char* realold,
+static enum conffopt promptconfaction(const char* pkg, const char* cfgfile, 
const char* realold,
                const char* realnew, int useredited, int distedited,
                enum conffopt what);
 
@@ -224,7 +224,7 @@ void deferred_configure(struct pkginfo *pkg) {
                                        "deferred_configure `%s' (= `%s') 
useredited=%d distedited=%d what=%o",
                                        conff->name, cdr.buf, useredited, 
distedited, what);
 
-                       what=promptconfaction(conff->name, cdr.buf, cdr2.buf, 
useredited, distedited, what);
+                       what=promptconfaction(pkg->name, conff->name, cdr.buf, 
cdr2.buf, useredited, distedited, what);
 
                        switch (what & ~(cfof_isnew|cfof_userrmd)) {
                                case cfo_keep | cfof_backup:
@@ -526,7 +526,7 @@ static void suspend(void) {
 
 /* Select what to do with a configuration file.
  */
-static enum conffopt promptconfaction(const char* cfgfile, const char* realold,
+static enum conffopt promptconfaction(const char* pkg, const char* cfgfile, 
const char* realold,
                const char* realnew, int useredited, int distedited,
                enum conffopt what) {
        const char *s;
@@ -615,6 +615,7 @@ static enum conffopt promptconfaction(const char* cfgfile, 
const char* realold,
                                        "    Y or I  : install the package 
maintainer's version\n"
                                        "    N or O  : keep your 
currently-installed version\n"
                                        "      D     : show the differences 
between the versions\n"
+                                       "      M     : attempt to automatically 
merge the differences\n"
                                        "      Z     : background this process 
to examine the situation\n"));
 
                if (what & cfof_keep)
@@ -650,6 +651,18 @@ static enum conffopt promptconfaction(const char* cfgfile, 
const char* realold,
                if (cc == 'd')
                        showdiff(realold, realnew);
 
+               if (cc == 'm') {
+                       int res;
+                       printf("automagic merging of %s aka %s\n", cfgfile, 
realold);
+                       if( conff_automerge(pkg, cfgfile) == 0){
+                           printf("yay! merge worked :)\n");
+                               /* so let's just pretend they said "keep my 
version" ;) */
+                               cc = 'n';
+                       } else {
+                               printf("boo! merge failed :(\n");
+                       }
+               }
+
                if (cc == 'z')
                        suspend();
 
-- 
1.5.4.3


-- 
To UNSUBSCRIBE, email to [EMAIL PROTECTED]
with a subject of "unsubscribe". Trouble? Contact [EMAIL PROTECTED]

Reply via email to