I applied the patch from Vasily i. Redkin to version 1.17.10 (and moved a
redundant part into an own function). BTW his patch works great. I did this in
preparation for a possible threeway merge improvement. Is there still any
interest in such a feature? Is there any reason why this patch was never
integrated?
diff --git a/src/configure.c b/src/configure.c
index e4dfa8a..f72938a 100644
--- a/src/configure.c
+++ b/src/configure.c
@@ -82,12 +82,16 @@ static int conffoptcells[2][2] = {
{ CFO_KEEP, CFO_PROMPT_KEEP }, /* User edited. */
};
+static void run_merge_cmd(const char *cmd, const char *confold, const char *confnew);
+
static int
show_prompt(const char *cfgfile, const char *realold, const char *realnew,
int useredited, int distedited, enum conffopt what)
{
const char *s;
int c, cc;
+ unsigned int ind;
+ unsigned int num_cmds;
/* Flush the terminal's input in case the user involuntarily
* typed some characters. */
@@ -151,6 +155,13 @@ show_prompt(const char *cfgfile, const char *realold, const char *realnew,
" D : show the differences between the versions\n"
" Z : start a shell to examine the situation\n"));
+ /* output the available custom merge commands */
+ num_cmds = sizeof(confmergecmds) / sizeof(confmergecmds[0]);
+ for (ind = 0; ind < num_cmds && 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"));
@@ -159,7 +170,14 @@ show_prompt(const char *cfgfile, const char *realold, const char *realnew,
_(" The default action is to install the new version.\n"));
s = path_basename(cfgfile);
- fprintf(stderr, "*** %s (Y/I/N/O/D/Z) %s ? ", s,
+ fprintf(stderr, "*** %s (Y/I/N/O/D/Z", s);
+
+ /* output the keys to execute custom merge commands */
+ for(ind = 0; ind < num_cmds && 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]"));
@@ -275,6 +293,8 @@ promptconfaction(struct pkginfo *pkg, const char *cfgfile,
int useredited, int distedited, enum conffopt what)
{
int cc;
+ unsigned int ind;
+ unsigned int num_cmds;
if (!(what & CFOF_PROMPT))
return what;
@@ -287,6 +307,14 @@ promptconfaction(struct pkginfo *pkg, const char *cfgfile,
cc = show_prompt(cfgfile, realold, realnew,
useredited, distedited, what);
+ num_cmds = sizeof(confmergecmds)/sizeof(confmergecmds[0]);
+ for(ind = 0; ind < num_cmds && 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')
show_diff(realold, realnew);
@@ -836,3 +864,67 @@ md5hash(struct pkginfo *pkg, char *hashbuf, const char *fn)
strcpy(hashbuf, EMPTYHASHFLAG);
}
}
+
+static int
+replace_token(char *text, const char *token, const char *replacement, unsigned int max_size)
+{
+ unsigned int token_len, replacement_len, text_len;
+ char *tmp;
+ char *after_token;
+
+ token_len = strlen(token);
+ replacement_len = strlen(replacement);
+ text_len = strlen(text);
+
+ tmp = text;
+ while((tmp = strstr(tmp, token))) {
+ if(text_len + replacement_len - token_len > max_size)
+ return -1;
+
+ after_token = tmp + token_len;
+
+ /* copy everything after the replacement */
+ memmove(tmp + replacement_len, after_token, strlen(after_token) + 1);
+
+ /* replace the token occurence */
+ strncpy(tmp, replacement, replacement_len);
+
+ tmp += replacement_len;
+ }
+
+ return 0;
+}
+
+static void
+run_merge_cmd(const char *cmd, const char *confold, const char *confnew)
+{
+ char buf[PATH_MAX];
+ int rc;
+ pid_t pid;
+
+ strncpy(buf, cmd, sizeof(buf));
+
+ rc = replace_token(buf, "%O", confold, sizeof(buf));
+ if (rc < 0) ohshit(_("path %s too long"), confold);
+
+ replace_token(buf, "%N", confnew, sizeof(buf));
+ if (rc < 0) ohshit(_("path %s too long"), confnew);
+
+
+ fprintf(stderr, "Executing: %s\n", buf);
+
+ pid = subproc_fork();
+ if (!pid) {
+ /* Child process */
+ setenv("DPKG_SHELL_REASON", "conffile-prompt", 1);
+ setenv("DPKG_CONFFILE_OLD", confold, 1);
+ setenv("DPKG_CONFFILE_NEW", confnew, 1);
+
+ command_shell(buf, "custom merge command");
+ }
+
+ /* Parent process. */
+ subproc_wait(pid, "mergecmd");
+}
+
+
diff --git a/src/main.c b/src/main.c
index 73e7460..b52b98d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -155,6 +155,8 @@ usage(const struct cmdinfo *ci, const char *value)
" --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(_(
@@ -199,6 +201,8 @@ static const char *admindir = ADMINDIR;
const char *instdir= "";
struct pkg_list *ignoredependss = NULL;
+struct conf_merge_cmd confmergecmds[10] = { {0, NULL, NULL} };
+
static const char *
forcetype_str(char type)
{
@@ -300,6 +304,64 @@ static const struct debuginfo {
};
static void
+confmergecmd(const struct cmdinfo *cip, const char *value) {
+ /* parse line */
+ char key;
+ char *label, *cmd;
+ const char *p, *s;
+ unsigned int ind;
+ unsigned int num_cmds;
+ 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 */
+ num_cmds = sizeof(confmergecmds)/sizeof(confmergecmds[0]);
+ for(ind = 0; ind < num_cmds; ++ind) {
+ if(!confmergecmds[ind].key)
+ break;
+ }
+ if(ind == num_cmds) {
+ fprintf(stderr, _("Too many merge commsnds (%d is allowed), ignoring '%s'\n"), num_cmds, value);
+ free(label);
+ free(cmd);
+ } else {
+ confmergecmds[ind].key = key;
+ confmergecmds[ind].label = label;
+ confmergecmds[ind].command = cmd;
+ if(ind < num_cmds - 1)
+ confmergecmds[ind + 1].key = 0; /* terminate list */
+ }
+}
+
+static void
set_debug(const struct cmdinfo *cpi, const char *value)
{
char *endp;
@@ -717,6 +779,7 @@ static const struct cmdinfo cmdinfos[]= {
{ "debug", 'D', 1, NULL, NULL, set_debug, 0 },
{ "help", '?', 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 --git a/src/main.h b/src/main.h
index 10014ef..f149a00 100644
--- a/src/main.h
+++ b/src/main.h
@@ -118,6 +118,15 @@ enum action {
act_forgetold,
};
+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 int f_pending, f_recursive, f_alsoselect, f_skipsame, f_noact;