gen_build_files.sh uses bashism, document it
[oweals/busybox.git] / archival / dpkg.c
index 9f58b54e60e62c21378d3c80e23e809d501083cd..219512b9ab81c3d1a6756a75cc7cb7445c6a3e35 100644 (file)
@@ -245,7 +245,7 @@ static int version_compare(const unsigned ver1, const unsigned ver2)
 {
        char *ch_ver1 = name_hashtable[ver1];
        char *ch_ver2 = name_hashtable[ver2];
-       unsigned long epoch1 = 0, epoch2 = 0;
+       unsigned epoch1 = 0, epoch2 = 0;
        char *colon;
        char *deb_ver1, *deb_ver2;
        char *upstream_ver1;
@@ -278,17 +278,16 @@ static int version_compare(const unsigned ver1, const unsigned ver2)
        deb_ver1 = strrchr(upstream_ver1, '-');
        deb_ver2 = strrchr(upstream_ver2, '-');
        if (deb_ver1) {
-               deb_ver1[0] = '\0';
-               deb_ver1++;
+               *deb_ver1++ = '\0';
        }
        if (deb_ver2) {
-               deb_ver2[0] = '\0';
-               deb_ver2++;
+               *deb_ver2++ = '\0';
        }
        result = version_compare_part(upstream_ver1, upstream_ver2);
-       if (!result)
+       if (result == 0) {
                /* Compare debian versions */
                result = version_compare_part(deb_ver1, deb_ver2);
+       }
 
        free(upstream_ver1);
        free(upstream_ver2);
@@ -858,8 +857,9 @@ static void write_status_file(deb_file_t **deb_file)
                                                if (field_name == NULL) {
                                                        break;
                                                }
-                                               if ((strcmp(field_name, "Priority") == 0) ||
-                                                       (strcmp(field_name, "Section") == 0)) {
+                                               if ((strcmp(field_name, "Priority") == 0)
+                                                || (strcmp(field_name, "Section") == 0)
+                                               ) {
                                                        fprintf(new_status_file, "%s: %s\n", field_name, field_value);
                                                }
                                        }
@@ -1079,8 +1079,9 @@ static int check_deps(deb_file_t **deb_file, int deb_start /*, int dep_max_count
 
                        package_num = search_package_hashtable(package_edge->name, package_edge->version, package_edge->operator);
 
-                       if (package_edge->type == EDGE_PRE_DEPENDS ||
-                           package_edge->type == EDGE_DEPENDS) {
+                       if (package_edge->type == EDGE_PRE_DEPENDS
+                        || package_edge->type == EDGE_DEPENDS
+                       ) {
                                int result=1;
                                status_num = 0;
 
@@ -1306,7 +1307,7 @@ static void list_packages(const char *pattern)
                        name_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->name];
                        vers_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->version];
 
-                       if (pattern && fnmatch(pattern, name_str, 0))
+                       if (pattern && fnmatch(pattern, name_str, 0) != 0)
                                continue;
 
                        /* get abbreviation for status field 1 */
@@ -1489,6 +1490,58 @@ static char *deb_extract_control_file_to_buffer(archive_handle_t *ar_handle, lli
        return ar_handle->dpkg__sub_archive->dpkg__buffer;
 }
 
+static void append_control_file_to_llist(const char *package_name, const char *control_name, llist_t **ll)
+{
+       FILE *fp;
+       char *filename, *line;
+
+       filename = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, control_name);
+       fp = fopen_for_read(filename);
+       free(filename);
+       if (fp != NULL) {
+               while ((line = xmalloc_fgetline(fp)) != NULL)
+                       llist_add_to(ll, line);
+               fclose(fp);
+       }
+}
+
+static char FAST_FUNC filter_rename_config(archive_handle_t *archive_handle)
+{
+       int fd;
+       char *name_ptr = archive_handle->file_header->name + 1;
+
+       /* Is this file marked as config file? */
+       if (!find_list_entry(archive_handle->accept, name_ptr))
+               return EXIT_SUCCESS; /* no */
+
+       fd = open(name_ptr, O_RDONLY);
+       if (fd >= 0) {
+               md5_ctx_t md5;
+               char *md5line, *buf;
+               int count;
+
+               /* Calculate MD5 of existing file */
+               buf = xmalloc(4096);
+               md5_begin(&md5);
+               while ((count = safe_read(fd, buf, 4096)) > 0)
+                       md5_hash(buf, count, &md5);
+               md5_end(buf, &md5); /* using buf as result storage */
+               close(fd);
+
+               md5line = xmalloc(16 * 2 + 2 + strlen(name_ptr) + 1);
+               sprintf(bin2hex(md5line, buf, 16), "  %s", name_ptr);
+               free(buf);
+
+               /* Is it changed after install? */
+               if (find_list_entry(archive_handle->accept, md5line) == NULL) {
+                       printf("Warning: Creating %s as %s.dpkg-new\n", name_ptr, name_ptr);
+                       archive_handle->file_header->name = xasprintf("%s.dpkg-new", archive_handle->file_header->name);
+               }
+               free(md5line);
+       }
+       return EXIT_SUCCESS;
+}
+
 static void FAST_FUNC data_extract_all_prefix(archive_handle_t *archive_handle)
 {
        char *name_ptr = archive_handle->file_header->name;
@@ -1498,7 +1551,7 @@ static void FAST_FUNC data_extract_all_prefix(archive_handle_t *archive_handle)
                name_ptr++;
        /* Skip all leading "./" and "../" */
        while (name_ptr[0] == '.') {
-               if (name_ptr[1] == '.' && name_ptr[2] == '/')
+               if (name_ptr[1] == '.')
                        name_ptr++;
                if (name_ptr[1] != '/')
                        break;
@@ -1508,9 +1561,29 @@ static void FAST_FUNC data_extract_all_prefix(archive_handle_t *archive_handle)
        if (name_ptr[0] != '\0') {
                archive_handle->file_header->name = xasprintf("%s%s", archive_handle->dpkg__buffer, name_ptr);
                data_extract_all(archive_handle);
+               if (fnmatch("*.dpkg-new", archive_handle->file_header->name, 0) == 0) {
+                       /* remove .dpkg-new suffix */
+                       archive_handle->file_header->name[strlen(archive_handle->file_header->name) - 9] = '\0';
+               }
        }
 }
 
+enum {
+       /* Commands */
+       OPT_configure            = (1 << 0),
+       OPT_install              = (1 << 1),
+       OPT_list_installed       = (1 << 2),
+       OPT_purge                = (1 << 3),
+       OPT_remove               = (1 << 4),
+       OPT_unpack               = (1 << 5),
+       OPTMASK_cmd              = (1 << 6) - 1,
+       /* Options */
+       OPT_force                = (1 << 6),
+       OPT_force_ignore_depends = (1 << 7),
+       OPT_force_confnew        = (1 << 8),
+       OPT_force_confold        = (1 << 9),
+};
+
 static void unpack_package(deb_file_t *deb_file)
 {
        const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name];
@@ -1521,14 +1594,21 @@ static void unpack_package(deb_file_t *deb_file)
        archive_handle_t *archive_handle;
        FILE *out_stream;
        llist_t *accept_list;
+       llist_t *conffile_list;
        int i;
 
        /* If existing version, remove it first */
+       conffile_list = NULL;
        if (strcmp(name_hashtable[get_status(status_num, 3)], "installed") == 0) {
                /* Package is already installed, remove old version first */
                printf("Preparing to replace %s %s (using %s)...\n", package_name,
                        name_hashtable[package_hashtable[status_package_num]->version],
                        deb_file->filename);
+
+               /* Read md5sums from old package */
+               if (!(option_mask32 & OPT_force_confold))
+                       append_control_file_to_llist(package_name, "md5sums", &conffile_list);
+
                remove_package(status_package_num, 0);
        } else {
                printf("Unpacking %s (from %s)...\n", package_name, deb_file->filename);
@@ -1556,9 +1636,15 @@ static void unpack_package(deb_file_t *deb_file)
        /* Run the preinst prior to extracting */
        run_package_script_or_die(package_name, "preinst");
 
+       /* Don't overwrite existing config files */
+       if (!(option_mask32 & OPT_force_confnew))
+               append_control_file_to_llist(package_name, "conffiles", &conffile_list);
+
        /* Extract data.tar.gz to the root directory */
        archive_handle = init_archive_deb_ar(deb_file->filename);
        init_archive_deb_data(archive_handle);
+       archive_handle->dpkg__sub_archive->accept = conffile_list;
+       archive_handle->dpkg__sub_archive->filter = filter_rename_config;
        archive_handle->dpkg__sub_archive->action_data = data_extract_all_prefix;
        archive_handle->dpkg__sub_archive->dpkg__buffer = (char*)"/"; /* huh? */
        archive_handle->dpkg__sub_archive->ah_flags |= ARCHIVE_UNLINK_OLD;
@@ -1612,40 +1698,57 @@ int dpkg_main(int argc UNUSED_PARAM, char **argv)
        int state_status;
        int status_num;
        int i;
-       enum {
-               OPT_configure = 0x1,
-               OPT_force_ignore_depends = 0x2,
-               OPT_install = 0x4,
-               OPT_list_installed = 0x8,
-               OPT_purge = 0x10,
-               OPT_remove = 0x20,
-               OPT_unpack = 0x40,
-       };
+#if ENABLE_LONG_OPTS
+       static const char dpkg_longopts[] ALIGN1 =
+// FIXME: we use -C non-compatibly, should be:
+// "-C|--audit Check for broken package(s)"
+               "configure\0"      No_argument        "C"
+               "force\0"          Required_argument  "F"
+               "install\0"        No_argument        "i"
+               "list\0"           No_argument        "l"
+               "purge\0"          No_argument        "P"
+               "remove\0"         No_argument        "r"
+               "unpack\0"         No_argument        "u"
+               "force-depends\0"  No_argument        "\xff"
+               "force-confnew\0"  No_argument        "\xfe"
+               "force-confold\0"  No_argument        "\xfd"
+               ;
+#endif
 
        INIT_G();
 
-       opt = getopt32(argv, "CF:ilPru", &str_f);
+       IF_LONG_OPTS(applet_long_options = dpkg_longopts);
+       opt = getopt32(argv, "CilPruF:", &str_f);
+       argv += optind;
        //if (opt & OPT_configure) ... // -C
-       if (opt & OPT_force_ignore_depends) { // -F (--force in official dpkg)
-               if (strcmp(str_f, "depends"))
-                       opt &= ~OPT_force_ignore_depends;
+       if (opt & OPT_force) { // -F (--force in official dpkg)
+               if (strcmp(str_f, "depends") == 0)
+                       opt |= OPT_force_ignore_depends;
+               else if (strcmp(str_f, "confnew") == 0)
+                       opt |= OPT_force_confnew;
+               else if (strcmp(str_f, "confold") == 0)
+                       opt |= OPT_force_confold;
+               else
+                       bb_show_usage();
+               option_mask32 = opt;
        }
        //if (opt & OPT_install) ... // -i
        //if (opt & OPT_list_installed) ... // -l
        //if (opt & OPT_purge) ... // -P
        //if (opt & OPT_remove) ... // -r
        //if (opt & OPT_unpack) ... // -u (--unpack in official dpkg)
-       argv += optind;
-       /* check for non-option argument if expected  */
-       if (!opt || (!argv[0] && !(opt && OPT_list_installed)))
+       if (!(opt & OPTMASK_cmd) /* no cmd */
+        || ((opt & OPTMASK_cmd) & ((opt & OPTMASK_cmd)-1)) /* more than one cmd */
+       ) {
                bb_show_usage();
+       }
 
 /*     puts("(Reading database ... xxxxx files and directories installed.)"); */
        index_status_file("/var/lib/dpkg/status");
 
        /* if the list action was given print the installed packages and exit */
        if (opt & OPT_list_installed) {
-               list_packages(argv[0]);
+               list_packages(argv[0]); /* param can be NULL */
                return EXIT_SUCCESS;
        }