Attached patch (for dpkg-1.15.8.10) adds '--conf-merge-cmd' command line
option, which adds user's favorite configuration files merging helper to
the menu.
Usage example:
# dpkg --conf-merge-cmd='V;Diff/Merge versions with VIM;vim -d %O %N'
install package.deb
or user can drop a file in /etc/dpkg/dpkg.cfg.d to permanently add
his/her favorite merge tool:
# echo 'conf-merge-cmd V;Diff/Merge versions with VIM;vim -d %O %N' >
/etc/dpkg/dpkg.cfg.d/50merge-vim
diff -ru orig/dpkg-1.15.8.10/src/configure.c dpkg-1.15.8.10/src/configure.c
--- orig/dpkg-1.15.8.10/src/configure.c 2011-01-30 22:37:44.000000000 +0300
+++ dpkg-1.15.8.10/src/configure.c 2011-03-23 11:19:07.000000000 +0300
@@ -62,6 +62,7 @@
const char *realold, const char *realnew,
int useredited, int distedited,
enum conffopt what);
+static void run_merge_cmd(const char *cmd, const char *old, const char *new);
static void
deferred_configure_conffile(struct pkginfo *pkg, struct conffile *conff)
@@ -591,6 +592,7 @@
{
const char *s;
int c, cc;
+ unsigned int ind;
if (!(what & cfof_prompt))
return what;
@@ -658,6 +660,9 @@
" N or O : keep your currently-installed version\n"
" D : show the differences between the versions\n"
" Z : start a shell to examine the situation\n"));
+ for(ind = 0; ind < sizeof(confmergecmds)/sizeof(confmergecmds[0]) && confmergecmds[ind].key; ++ind) {
+ fprintf(stderr, _(" %c : %s\n"), confmergecmds[ind].key, confmergecmds[ind].label);
+ }
if (what & cfof_keep)
fprintf(stderr, _(" The default action is to keep your current version.\n"));
@@ -667,8 +672,11 @@
s = strrchr(cfgfile, '/');
if (!s || !*++s)
s = cfgfile;
- fprintf(stderr, "*** %s (Y/I/N/O/D/Z) %s ? ",
- s,
+ fprintf(stderr, "*** %s (Y/I/N/O/D/Z", s);
+ for(ind = 0; ind < sizeof(confmergecmds)/sizeof(confmergecmds[0]) && confmergecmds[ind].key; ++ind) {
+ fprintf(stderr, _("/%c"), confmergecmds[ind].key);
+ }
+ fprintf(stderr, ") %s ? ",
(what & cfof_keep) ? _("[default=N]") :
(what & cfof_install) ? _("[default=Y]") :
_("[no default]"));
@@ -697,6 +705,13 @@
}
}
+ for(ind = 0; ind < sizeof(confmergecmds)/sizeof(confmergecmds[0]) && confmergecmds[ind].key; ++ind) {
+ if(cc == tolower(confmergecmds[ind].key)) {
+ run_merge_cmd(confmergecmds[ind].command, realold, realnew);
+ break;
+ }
+ }
+
/* FIXME: Say something if silently not install. */
if (cc == 'd')
showdiff(realold, realnew);
@@ -727,3 +742,55 @@
return what;
}
+
+static void run_merge_cmd(const char *cmd, const char *old, const char *new)
+{
+ char buf[PATH_MAX];
+ char *tmp;
+ unsigned int ol, nl, bl;
+ pid_t pid;
+
+ ol = strlen(old);
+ nl = strlen(new);
+ strncpy(buf, cmd, sizeof(buf));
+ bl = strlen(buf);
+ tmp = buf;
+ while((tmp = strstr(tmp, "%O"))) {
+ if(bl + ol - 2 > sizeof(buf))
+ ohshite(_("Too long merge command %s\n"), cmd);
+ memmove(tmp + ol, tmp + 2, strlen(tmp + 2) + 1);
+ strncpy(tmp, old, ol);
+ tmp += ol;
+ }
+ tmp = buf;
+ while((tmp = strstr(tmp, "%N"))) {
+ if(bl + nl - 2 > sizeof(buf) - 1)
+ ohshite(_("Too long merge command %s\n"), cmd);
+ memmove(tmp + nl, tmp + 2, strlen(tmp + 2) + 1);
+ strncpy(tmp, new, nl);
+ tmp += nl;
+ }
+ fprintf(stderr, "Executing: %s\n", buf);
+
+ pid = subproc_fork();
+ if (!pid) {
+ /* Child process */
+ const char *shell;
+
+ shell = getenv(SHELLENV);
+ if (!shell || !*shell)
+ shell = DEFAULTSHELL;
+
+ /* Set useful variables for the user. */
+ setenv("DPKG_CONFFILE_OLD", old, 1);
+ setenv("DPKG_CONFFILE_NEW", new, 1);
+
+ execlp(shell, shell, "-c", buf, NULL);
+ ohshite(_("failed to exec shell (%.250s)"), shell);
+ }
+
+ /* Parent process. */
+ subproc_wait(pid, "mergecmd");
+}
+
+
diff -ru orig/dpkg-1.15.8.10/src/main.c dpkg-1.15.8.10/src/main.c
--- orig/dpkg-1.15.8.10/src/main.c 2011-01-30 22:37:44.000000000 +0300
+++ dpkg-1.15.8.10/src/main.c 2011-03-23 12:36:51.000000000 +0300
@@ -144,6 +144,8 @@
" --no-force-...|--refuse-...\n"
" Stop when problems encountered.\n"
" --abort-after <n> Abort after encountering <n> errors.\n"
+" --conf-merge-cmd <cmd> Add configuration file merge command. Example:\n"
+" 'V;Diff/Merge versions with VIM;vim -d %%O %%N'\n"
"\n"), ADMINDIR);
printf(_(
@@ -191,6 +193,7 @@
const char *admindir= ADMINDIR;
const char *instdir= "";
struct pkg_list *ignoredependss = NULL;
+struct conf_merge_cmd confmergecmds[10] = { {0, NULL, NULL} };
static const struct forceinfo {
const char *name;
@@ -307,6 +310,61 @@
free(copy);
}
+static void confmergecmd(const struct cmdinfo *cip, const char *value) {
+ /* parse line */
+ char key;
+ char *label, *cmd;
+ const char *p, *s;
+ unsigned int ind;
+ const char *my_usage = "K;Display label;command args %O %N other args";
+ if(!value)
+ badusage(_("conf-merge-cmd requires argument like this: %s"), my_usage);
+
+ p = value;
+ /* - fetch key */
+ key = *p++;
+ if(!key)
+ badusage(_("null merge command, should be %s"), my_usage);
+ if(*p != ';')
+ badusage(_("bad merge command, should be %s"), my_usage);
+ ++p; /* skip ';' */
+
+ /* - fetch label */
+ while(*p && isspace(*p)) /* skip spaces */
+ ++p;
+ s = p;
+ while(*p && *p != ';') /* find ';' */
+ ++p;
+ if(!p)
+ badusage(_("bad merge command, should be %s"), my_usage);
+ label = strndup(s, p-s);
+
+ /* - fetch command */
+ ++p; /* skip ';' */
+ while(*p && isspace(*p)) /* skip spaces */
+ ++p;
+ if(!p)
+ badusage(_("bad merge command, should be %s"), my_usage);
+ cmd = strdup(p);
+
+ /* find unused table entry */
+ for(ind = 0; ind < sizeof(confmergecmds)/sizeof(confmergecmds[0]); ++ind) {
+ if(!confmergecmds[ind].key)
+ break;
+ }
+ if(ind == sizeof(confmergecmds)/sizeof(confmergecmds[0])) {
+ fprintf(stderr, _("Too many merge commsnds (%d is allowed), ignoring '%s'\n"), sizeof(confmergecmds)/sizeof(confmergecmds[0]), value);
+ free(label);
+ free(cmd);
+ } else {
+ confmergecmds[ind].key = key;
+ confmergecmds[ind].label = label;
+ confmergecmds[ind].command = cmd;
+ if(ind < sizeof(confmergecmds)/sizeof(confmergecmds[0]) - 1)
+ confmergecmds[ind + 1].key = 0; /* terminate list */
+ }
+}
+
static void setinteger(const struct cmdinfo *cip, const char *value) {
unsigned long v;
char *ep;
@@ -539,6 +597,7 @@
{ "debug", 'D', 1, NULL, NULL, setdebug, 0 },
{ "help", 'h', 0, NULL, NULL, usage, 0 },
{ "version", 0, 0, NULL, NULL, printversion, 0 },
+ { "conf-merge-cmd", 0, 1, NULL, NULL, confmergecmd, 0 },
ACTIONBACKEND( "build", 'b', BACKEND),
ACTIONBACKEND( "contents", 'c', BACKEND),
ACTIONBACKEND( "control", 'e', BACKEND),
diff -ru orig/dpkg-1.15.8.10/src/main.h dpkg-1.15.8.10/src/main.h
--- orig/dpkg-1.15.8.10/src/main.h 2011-01-30 22:37:44.000000000 +0300
+++ dpkg-1.15.8.10/src/main.h 2011-03-23 06:49:31.000000000 +0300
@@ -117,6 +117,13 @@
cfo_identical = cfof_keep
};
+struct conf_merge_cmd {
+ char key;
+ const char *label;
+ const char *command;
+};
+extern struct conf_merge_cmd confmergecmds[10];
+
extern const char *const statusstrings[];
extern const struct cmdinfo *cipaction;