when available, dpkg will now spawn a debconf-based frontend (which can be
found in /usr/share/dpkg/dpkg-promptconf.sh) for resolving conffile conflict

see TODO.debconf for a list of open questions and/or possible issues.
---
 TODO.debconf               |   70 +++++++++++++++++++++++++++++++
 debian/control             |    2 +-
 debian/dpkg.install        |    1 +
 debian/dpkg.templates      |   13 ++++++
 debian/po/POTFILES.in      |    1 +
 debian/po/templates.pot    |   59 ++++++++++++++++++++++++++
 debian/rules               |    2 +
 lib/dpkg.h                 |    2 +
 scripts/dpkg-confprompt.sh |   98 ++++++++++++++++++++++++++++++++++++++++++++
 src/configure.c            |   75 +++++++++++++++++++++++----------
 10 files changed, 299 insertions(+), 24 deletions(-)
 create mode 100644 TODO.debconf
 create mode 100644 debian/dpkg.templates
 create mode 100644 debian/po/POTFILES.in
 create mode 100644 debian/po/templates.pot
 create mode 100755 scripts/dpkg-confprompt.sh

diff --git a/TODO.debconf b/TODO.debconf
new file mode 100644
index 0000000..6db3177
--- /dev/null
+++ b/TODO.debconf
@@ -0,0 +1,70 @@
+- could my debconf "detection" be done better?
+
+  currently i just call debconf via system(), and i catch the particular
+  failure case where debconf fails to execute (return status = 127)
+
+- should i be using m_malloc() or varbuf functions for my string mangling?
+  
+  more of a general question.  currently i'm doing m_malloc() for stuff like
+  assembling a cmdline to pass to system, etc.  but i wonder if i should be 
+  using the varbuf related funcitons instead.
+
+- figure out how the templates should be managed within the source package
+
+  currently it's installed via the standard dh_installdebconf, but this is
+  a little awkward in the situation of dpkg, since dpkg itself does not use
+  the templates in the maintainer scripts and thus extra actions need to be
+  taken to register the templates files.  perhaps the file shouldn't be
+  traditionally installed at all, and instead kept somewhere outside of
+  <admindir>/info, since we rely on it existing somewhere and it doesn't
+  feel right poking around in there.  
+  
+  also, this means that there is a second po directory in debian, which
+  makes life a little more difficult for translators, and removes the
+  ability to share a common set of strings between the debconf stuff
+  and the rest of dpkg.
+
+- debconf script implementation
+
+  currently this is done with a small shell script.  while it uses mostly
+  built-in functions, it does mean at least 3 extra exec() calls per conffile
+  conflict (sh -c from system(), debconf, and then the script itself, plus
+  any non-builtin functions that these call).  this could be made slightly
+  better in a few ways i can think of:
+
+  - use an exec() family function instead of system, saving one sh -c exec()
+  - enhance the program to persist over the invocation of dpkg, talking
+    over a socket or similar.
+  
+  the script could also be re-implemented in C, and it's relatively simple
+  since it's basically printf/fgets calls wrapped in some logic,  but i don't
+  see it being particularly faster--the only advantage i see is that it would
+  allow to share a bit of code (it would still need to be executed seperately,
+  as far as i can tell, due to needing to interface with debconf as an external
+  program
+
+- setting and respecting defaults in debconf frontend
+
+  currently the debconf frontend always defaults to "keep installed version",
+  but in some cases the default has been changed and needs to be communicated
+  into the frontend wrapper.
+
+- per-package/file registration of templates?
+
+  currently we use a single template that is subtituted over and over again
+  with the various questions.  however, it's worth consideration if we want
+  to register the same template multiple times to different locations
+  (i.e. register dpkg/confprompt dpkg/confprompt/foo/etc/foo.cfg), so that
+  the answers to questions can persist like other debconf questions.
+
+- what should happen when someone selects "cancel"?
+
+  currently the window goes away, and pops up again :)
+
+- depends/recommends on debconf?
+
+  do we want to force/recommend having debconf available?  also, the frontend
+  script uses the X_LOADTEMPLATEFILE interface, which is only available
+  after debconf 1.5.19 and cdebconf 0.106.  however, if anything goes wrong
+  with the new frontend (lack of debconf, or internal failure of the
+  frontend), dpkg falls back to the "traditional" prompt.
diff --git a/debian/control b/debian/control
index 8393bf4..7bb3ef3 100644
--- a/debian/control
+++ b/debian/control
@@ -13,7 +13,7 @@ Standards-Version: 3.7.3
 Build-Depends: debhelper (>= 4.1.81), pkg-config, po4a (>= 0.23),
  libncursesw5-dev, zlib1g-dev (>= 1:1.1.3-19.1), libbz2-dev,
  libselinux1-dev (>= 1.28-4) [!hurd-i386 !kfreebsd-i386 !kfreebsd-amd64],
- libtimedate-perl, libio-string-perl
+ libtimedate-perl, libio-string-perl, po-debconf
 
 Package: dpkg
 Architecture: any
diff --git a/debian/dpkg.install b/debian/dpkg.install
index 2f54618..bcace45 100644
--- a/debian/dpkg.install
+++ b/debian/dpkg.install
@@ -1,5 +1,6 @@
 ../dpkg.cfg etc/dpkg
 ../archtable usr/share/dpkg
+../../scripts/dpkg-confprompt.sh usr/share/dpkg
 
 etc/alternatives
 etc/dpkg/origins
diff --git a/debian/dpkg.templates b/debian/dpkg.templates
new file mode 100644
index 0000000..8778993
--- /dev/null
+++ b/debian/dpkg.templates
@@ -0,0 +1,13 @@
+Template: dpkg/promptconfaction
+Type: select
+_Choices: Install the package maintainer's version, Keep the currently 
installed version, Show the differences between the current and new versions, 
Invoke a shell to investigate
+Default: Keep the currently installed version
+_Description: Select how dpkg should resolve the conflicts for ${file}
+ Conflicts have been detected while updating a conffile in the package ${pkg}.
+ .
+ File: ${file}
+ .
+ Reason: ${reason}
+ .
+ Select from the provided options which action should be taken to resolve
+ the conflicts.
diff --git a/debian/po/POTFILES.in b/debian/po/POTFILES.in
new file mode 100644
index 0000000..b775c53
--- /dev/null
+++ b/debian/po/POTFILES.in
@@ -0,0 +1 @@
+[type: gettext/rfc822deb] dpkg.templates
diff --git a/debian/po/templates.pot b/debian/po/templates.pot
new file mode 100644
index 0000000..b45a96d
--- /dev/null
+++ b/debian/po/templates.pot
@@ -0,0 +1,59 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <[EMAIL PROTECTED]>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: [EMAIL PROTECTED]"
+"POT-Creation-Date: 2008-03-15 11:59+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <[EMAIL PROTECTED]>\n"
+"Language-Team: LANGUAGE <[EMAIL PROTECTED]>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Type: select
+#. Choices
+#: ../dpkg.templates:1001
+msgid ""
+"Install the package maintainer's version, Keep the currently installed "
+"version, Show the differences between the current and new versions, Invoke a "
+"shell to investigate"
+msgstr ""
+
+#. Type: select
+#. Description
+#: ../dpkg.templates:1002
+msgid "Select how dpkg should resolve the conflicts for ${file}"
+msgstr ""
+
+#. Type: select
+#. Description
+#: ../dpkg.templates:1002
+msgid ""
+"Conflicts have been detected while updating a conffile in the package ${pkg}."
+msgstr ""
+
+#. Type: select
+#. Description
+#: ../dpkg.templates:1002
+msgid "File: ${file}"
+msgstr ""
+
+#. Type: select
+#. Description
+#: ../dpkg.templates:1002
+msgid "Reason: ${reason}"
+msgstr ""
+
+#. Type: select
+#. Description
+#: ../dpkg.templates:1002
+msgid ""
+"Select from the provided options which action should be taken to resolve the "
+"conflicts."
+msgstr ""
diff --git a/debian/rules b/debian/rules
index 73855d8..edbbc3e 100755
--- a/debian/rules
+++ b/debian/rules
@@ -84,6 +84,7 @@ binary-arch: install
 
        dh_installchangelogs -a ChangeLog
        dh_installdocs -a
+       dh_installdebconf -a
 
        install -d debian/dpkg/usr/share/lintian/overrides
        install -m 644 debian/dpkg.lintian-overrides \
@@ -131,6 +132,7 @@ clean:
 
        rm -rf build-tree
        dh_clean
+       debconf-updatepo
 
 
 .PHONY: build install binary-arch binary-indep binary clean
diff --git a/lib/dpkg.h b/lib/dpkg.h
index 7e2e428..10fbb46 100644
--- a/lib/dpkg.h
+++ b/lib/dpkg.h
@@ -146,6 +146,8 @@
 #define FIND           "find"
 #define SHELL          "sh"
 #define DIFF           "diff"
+#define DEBCONF                "debconf"
+#define DEBCONF_PROMPTCONF     "/usr/share/dpkg/dpkg-confprompt.sh"
 
 #define SHELLENVIR     "SHELL"
 
diff --git a/scripts/dpkg-confprompt.sh b/scripts/dpkg-confprompt.sh
new file mode 100755
index 0000000..2e9b2d0
--- /dev/null
+++ b/scripts/dpkg-confprompt.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+# internal script for debconf-based management of conffiles
+# calling convention:
+#      dpkg-confprompt.sh <pkg> <cfgfile> "<reason>"
+# initial implementation by sean finney
+# copyright (c) 2008 sean finney <[EMAIL PROTECTED]>
+# this file may be used in accordance with the dpkg software copyright
+
+
+# debug function 
+say(){
+       [ -z "$DPKG_DEBCONF_DEBUG" ] || echo "$@" >&2
+}
+
+# properly escape newlines and other junk from pre-translated strings
+# sent to us by the dpkg program
+escape(){
+       local foo nonempty
+       echo "$1" | sed -e 's/^ *//g' -e 's/ *$//g' -e 's/ *==> *//g' | \
+       while read foo; do
+               if [ -n "$foo" ]; then 
+                       if [ -n "$nonempty" ]; then
+                               printf "\\\\n"
+                       else
+                               nonempty="yup"
+                       fi
+                       printf "$foo"
+               fi
+       done
+       echo
+}
+
+# read the response from the debconf system, return the debconf status code
+# the (global) variable res stores the resulting string
+readres(){
+       local status
+       read status res
+       say "readres = status: $status res: $res"
+       return $status
+}
+
+pkg="$1"
+file="$2"
+reason="`escape \"$3 \"`"
+# XXX maybe we don't want this
+#defact="`escape \"$3 \"`"
+
+# force-register the templates, since dpkg will not do this itself due
+# to not using debconf in any of the maintainer scripts
+echo x_loadtemplatefile /var/lib/dpkg/info/dpkg.templates dpkg
+readres || exit 255
+
+# reset the value, since we share this template across multiple packages
+# and conffiles the stored value is worthless
+echo reset dpkg/promptconfaction
+readres || exit 255
+
+# perform some substitutions
+echo subst dpkg/promptconfaction pkg "$pkg"
+readres || exit 255
+echo subst dpkg/promptconfaction file "$file"
+readres || exit 255
+echo subst dpkg/promptconfaction reason "$reason"
+readres || exit 255
+# XXX maybe we don't want this
+#echo subst dpkg/promptconfaction defact "$defact"
+#readres || exit 255
+
+# ask the answer and get the response
+echo input high dpkg/promptconfaction
+readres || exit 255
+echo go
+readres || exit 255
+echo get dpkg/promptconfaction
+readres || exit 255
+
+# based on the answer, return the integer value of the equivalent character,
+# had dpkg handled the question itself from the cmdline.  any non-ascii value
+# (> 127) implies something has gone wrong internally.
+case $res in
+"Install the package maintainer's version")
+       code=121 # 'y'
+       ;;
+"Keep the currently installed version")
+       code=110 # 'n'
+       ;;
+"Show the differences between the current and new versions")
+       code=100 # 'd'
+       ;;
+"Invoke a shell to investigate")
+       code=122 # 'z'
+       ;;
+*)
+       say "wtf am i supposed to do with '$res'?"
+       code=255
+       ;;
+esac
+exit $code
diff --git a/src/configure.c b/src/configure.c
index c6cba8e..8da09cf 100644
--- a/src/configure.c
+++ b/src/configure.c
@@ -54,6 +54,8 @@ static void suspend(void);
 static enum conffopt promptconfaction(const char* package, const char* cfgfile,
                const char* realold, const char* realnew, int useredited, 
                int distedited, enum conffopt what);
+static int debconf_promptconf(const char *pkg, const char *conffile, 
+                              const char *reason);
 
 void deferred_configure(struct pkginfo *pkg) {
        /* The algorithm for deciding what to configure first is as follows:
@@ -571,7 +573,7 @@ static enum conffopt promptconfaction(const char* package, 
const char* cfgfile,
                                        _("\n ==> Modified (by you or by a 
script) since installation.\n") :
                                        _("\n ==> Deleted (by you or by a 
script) since installation.\n"));
 
-                       fprintf(stderr, distedited ?
+                       reason = ( distedited ?
                                        _(" ==> Package distributor has shipped 
an updated version.\n") :
                                        _("     Version in package is the same 
as at last installation.\n"));
                }
@@ -603,38 +605,45 @@ static enum conffopt promptconfaction(const char* 
package, const char* cfgfile,
                                cc = 'y';
                                break;
                        }
-               }
-
-
-               fprintf(stderr,
+               } 
+
+               /* try to use debconf to resolve the problem */
+               cc = debconf_promptconf(package, cfgfile, reason);
+               /* if 127 or higher is returned then there was a problem 
executing
+                * the script (or other internall error) and we fall back to 
the 
+                * oldschool way */
+               if (cc >= 127) {
+                       cc= 0;
+                       fprintf(stderr,
                                _("   What would you like to do about it ?  
Your options are:\n"
                                        "    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"
                                        "      Z     : background this process 
to examine the situation\n"));
 
-               if (what & cfof_keep)
-                       fprintf(stderr, _(" The default action is to keep your 
current version.\n"));
-               else if (what & cfof_install)
-                       fprintf(stderr, _(" The default action is to install 
the new version.\n"));
+                       if (what & cfof_keep)
+                               fprintf(stderr, _(" The default action is to 
keep your current version.\n"));
+                       else if (what & cfof_install)
+                               fprintf(stderr, _(" The default action is to 
install the new version.\n"));
 
-               s= strrchr(cfgfile,'/');
-               if (!s || !*++s) s= cfgfile;
-               fprintf(stderr, "*** %s (Y/I/N/O/D/Z) %s ? ",
-                               s,
-                               (what & cfof_keep) ? _("[default=N]") :
-                               (what & cfof_install) ? _("[default=Y]") : 
_("[no default]"));
+                       s= strrchr(cfgfile,'/');
+                       if (!s || !*++s) s= cfgfile;
+                       fprintf(stderr, "*** %s (Y/I/N/O/D/Z) %s ? ",
+                                       s,
+                                       (what & cfof_keep) ? _("[default=N]") :
+                                       (what & cfof_install) ? 
_("[default=Y]") : _("[no default]"));
 
-               if (ferror(stderr))
-                       ohshite(_("error writing to stderr, discovered before 
conffile prompt"));
+                       if (ferror(stderr))
+                               ohshite(_("error writing to stderr, discovered 
before conffile prompt"));
 
-               cc= 0;
-               while ((c= getchar()) != EOF && c != '\n')
-                       if (!isspace(c) && !cc) cc= tolower(c);
+                       while ((c= getchar()) != EOF && c != '\n')
+                               if (!isspace(c) && !cc) cc= tolower(c);
 
-               if (c == EOF) {
-                       if (ferror(stdin)) ohshite(_("read error on stdin at 
conffile prompt"));
-                       ohshit(_("EOF on stdin at conffile prompt"));
+                       if (c == EOF) {
+                               if (ferror(stdin)) 
+                                       ohshite(_("read error on stdin at 
conffile prompt"));
+                               ohshit(_("EOF on stdin at conffile prompt"));
+                       }
                }
 
                if (!cc) {
@@ -673,3 +682,23 @@ static enum conffopt promptconfaction(const char* package, 
const char* cfgfile,
 
        return what;
 }
+
+static int debconf_promptconf(const char *pkg, const char *cfgfile, 
+               const char *reason){
+       char *cmd=NULL;
+       size_t cmdsz=0;
+       int ret=0;
+
+       /* <debconf> <confpromptscript> <pkg> <cfgfile> "<reason>"\0 */
+       cmdsz = strlen(DEBCONF" "DEBCONF_PROMPTCONF" ")+strlen(pkg)+1
+               + strlen(cfgfile) + 2 + strlen(reason) + 2;
+       cmd = m_malloc(cmdsz);
+       snprintf(cmd, cmdsz, "%s %s %s %s \"%s\"", DEBCONF, DEBCONF_PROMPTCONF,
+                pkg, cfgfile, reason);
+       debug(dbg_conff, "debconf_promptconf %s %s = ", pkg, cfgfile);
+       ret=WEXITSTATUS(system(cmd));
+       if(ret <= 127) debug(dbg_conff, "%d ('%c')\n", ret, (char)ret);
+       else debug(dbg_conff, "%d\n", ret);
+       free(cmd);
+       return ret;
+}
-- 
1.5.4.3


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

Reply via email to