scripts/mk: On dpkg-build-api >= 1 include buildtools.mk in default.mk
[dpkg.git] / src / divert / main.c
blob31ba4c080e476ebc818d1a7810a9f8275ba1f364
1 /*
2 * dpkg-divert - override a package's version of a file
4 * Copyright © 1995 Ian Jackson
5 * Copyright © 2000, 2001 Wichert Akkerman
6 * Copyright © 2006-2015, 2017-2018 Guillem Jover <guillem@debian.org>
7 * Copyright © 2011 Linaro Limited
8 * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <https://www.gnu.org/licenses/>.
24 #include <config.h>
25 #include <compat.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
30 #include <errno.h>
31 #if HAVE_LOCALE_H
32 #include <locale.h>
33 #endif
34 #include <fcntl.h>
35 #include <fnmatch.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <unistd.h>
40 #include <dpkg/i18n.h>
41 #include <dpkg/dpkg.h>
42 #include <dpkg/dpkg-db.h>
43 #include <dpkg/debug.h>
44 #include <dpkg/arch.h>
45 #include <dpkg/file.h>
46 #include <dpkg/glob.h>
47 #include <dpkg/buffer.h>
48 #include <dpkg/options.h>
49 #include <dpkg/db-fsys.h>
52 static const char printforhelp[] = N_(
53 "Use --help for help about diverting files.");
55 static bool opt_pkgname_match_any = true;
56 static const char *opt_pkgname = NULL;
57 static const char *opt_divertto = NULL;
59 static int opt_verbose = 1;
60 static int opt_test = 0;
61 static int opt_rename = -1;
64 static int
65 printversion(const char *const *argv)
67 printf(_("Debian %s version %s.\n"), dpkg_get_progname(),
68 PACKAGE_RELEASE);
70 printf(_(
71 "This is free software; see the GNU General Public License version 2 or\n"
72 "later for copying conditions. There is NO warranty.\n"));
74 m_output(stdout, _("<standard output>"));
76 return 0;
79 static int
80 usage(const char *const *argv)
82 printf(_(
83 "Usage: %s [<option>...] <command>\n"
84 "\n"), dpkg_get_progname());
86 printf(_(
87 "Commands:\n"
88 " [--add] <file> add a diversion.\n"
89 " --remove <file> remove the diversion.\n"
90 " --list [<glob-pattern>] show file diversions.\n"
91 " --listpackage <file> show what package diverts the file.\n"
92 " --truename <file> return the diverted file.\n"
93 "\n"));
95 printf(_(
96 "Options:\n"
97 " --package <package> name of the package whose copy of <file> will not\n"
98 " be diverted.\n"
99 " --local all packages' versions are diverted.\n"
100 " --divert <divert-to> the name used by other packages' versions.\n"
101 " --rename actually move the file aside (or back).\n"
102 " --no-rename do not move the file aside (or back) (default).\n"
103 " --admindir <directory> set the directory with the diversions file.\n"
104 " --instdir <directory> set the root directory, but not the admin dir.\n"
105 " --root <directory> set the directory of the root filesystem.\n"
106 " --test don't do anything, just demonstrate.\n"
107 " --quiet quiet operation, minimal output.\n"
108 " --help show this help message.\n"
109 " --version show the version.\n"
110 "\n"));
112 printf(_(
113 "When adding, default is --local and --divert <original>.distrib.\n"
114 "When removing, --package or --local and --divert must match if specified.\n"
115 "Package preinst/postrm scripts should always specify --package and --divert.\n"));
117 m_output(stdout, _("<standard output>"));
119 return 0;
122 static void
123 opt_rename_setup(void)
125 if (opt_rename >= 0)
126 return;
128 opt_rename = 0;
129 warning(_("please specify --no-rename explicitly, the default "
130 "will change to --rename in 1.20.x"));
133 struct file {
134 char *name;
135 enum {
136 FILE_STAT_INVALID,
137 FILE_STAT_VALID,
138 FILE_STAT_NOFILE,
139 } stat_state;
140 struct stat stat;
143 static void
144 file_init(struct file *f, const char *filename)
146 struct varbuf usefilename = VARBUF_INIT;
148 varbuf_add_str(&usefilename, dpkg_fsys_get_dir());
149 varbuf_add_str(&usefilename, filename);
150 varbuf_end_str(&usefilename);
152 f->name = varbuf_detach(&usefilename);
153 f->stat_state = FILE_STAT_INVALID;
156 static void
157 file_destroy(struct file *f)
159 free(f->name);
162 static void
163 file_stat(struct file *f)
165 int ret;
167 if (f->stat_state != FILE_STAT_INVALID)
168 return;
170 ret = lstat(f->name, &f->stat);
171 if (ret && errno != ENOENT)
172 ohshite(_("cannot stat file '%s'"), f->name);
174 if (ret == 0)
175 f->stat_state = FILE_STAT_VALID;
176 else
177 f->stat_state = FILE_STAT_NOFILE;
180 static void
181 check_writable_dir(struct file *f)
183 char *tmpname;
184 int tmpfd;
186 tmpname = str_fmt("%s%s", f->name, ".dpkg-divert.tmp");
188 tmpfd = creat(tmpname, 0600);
189 if (tmpfd < 0)
190 ohshite(_("error checking '%s'"), f->name);
191 close(tmpfd);
192 (void)unlink(tmpname);
194 free(tmpname);
197 static bool
198 check_rename(struct file *src, struct file *dst)
200 file_stat(src);
202 /* If the source file is not present and we are not going to do
203 * the rename anyway there's no point in checking any further. */
204 if (src->stat_state == FILE_STAT_NOFILE)
205 return false;
207 file_stat(dst);
210 * Unfortunately we have to check for write access in both places,
211 * just having +w is not enough, since people do mount things RO,
212 * and we need to fail before we start mucking around with things.
213 * So we open a file with the same name as the diversions but with
214 * an extension that (hopefully) won't overwrite anything. If it
215 * succeeds, we assume a writable filesystem.
218 check_writable_dir(src);
219 check_writable_dir(dst);
221 if (src->stat_state == FILE_STAT_VALID &&
222 dst->stat_state == FILE_STAT_VALID &&
223 !(src->stat.st_dev == dst->stat.st_dev &&
224 src->stat.st_ino == dst->stat.st_ino))
225 ohshit(_("rename involves overwriting '%s' with\n"
226 " different file '%s', not allowed"),
227 dst->name, src->name);
229 return true;
232 static void
233 file_copy(const char *src, const char *dst)
235 struct dpkg_error err;
236 char *tmp;
237 int srcfd, dstfd;
239 srcfd = open(src, O_RDONLY);
240 if (srcfd < 0)
241 ohshite(_("unable to open file '%s'"), src);
243 tmp = str_fmt("%s%s", dst, ".dpkg-divert.tmp");
244 dstfd = creat(tmp, 0600);
245 if (dstfd < 0)
246 ohshite(_("unable to create file '%s'"), tmp);
248 push_cleanup(cu_filename, ~ehflag_normaltidy, 1, tmp);
250 if (fd_fd_copy(srcfd, dstfd, -1, &err) < 0)
251 ohshit(_("cannot copy '%s' to '%s': %s"), src, tmp, err.str);
253 close(srcfd);
255 if (fsync(dstfd))
256 ohshite(_("unable to sync file '%s'"), tmp);
257 if (close(dstfd))
258 ohshite(_("unable to close file '%s'"), tmp);
260 file_copy_perms(src, tmp);
262 if (rename(tmp, dst) != 0)
263 ohshite(_("cannot rename '%s' to '%s'"), tmp, dst);
265 free(tmp);
267 pop_cleanup(ehflag_normaltidy);
270 static void
271 file_rename(struct file *src, struct file *dst)
273 if (src->stat_state == FILE_STAT_NOFILE)
274 return;
276 if (dst->stat_state == FILE_STAT_VALID) {
277 if (unlink(src->name))
278 ohshite(_("rename: remove duplicate old link '%s'"),
279 src->name);
280 } else {
281 if (rename(src->name, dst->name) == 0)
282 return;
284 /* If a rename didn't work try moving the file instead. */
285 file_copy(src->name, dst->name);
287 if (unlink(src->name))
288 ohshite(_("unable to remove copied source file '%s'"),
289 src->name);
293 static void
294 diversion_check_filename(const char *filename)
296 if (filename[0] != '/')
297 badusage(_("filename \"%s\" is not absolute"), filename);
298 if (strchr(filename, '\n') != NULL)
299 badusage(_("file may not contain newlines"));
302 static const char *
303 diversion_pkg_name(struct fsys_diversion *d)
305 if (d->pkgset == NULL)
306 return ":";
307 else
308 return d->pkgset->name;
311 static const char *
312 varbuf_diversion(struct varbuf *str, const char *pkgname,
313 const char *filename, const char *divertto)
315 varbuf_reset(str);
317 if (pkgname == NULL) {
318 if (divertto == NULL)
319 varbuf_printf(str, _("local diversion of %s"), filename);
320 else
321 varbuf_printf(str, _("local diversion of %s to %s"),
322 filename, divertto);
323 } else {
324 if (divertto == NULL)
325 varbuf_printf(str, _("diversion of %s by %s"),
326 filename, pkgname);
327 else
328 varbuf_printf(str, _("diversion of %s to %s by %s"),
329 filename, divertto, pkgname);
332 return str->buf;
335 static const char *
336 diversion_current(const char *filename)
338 static struct varbuf str = VARBUF_INIT;
340 if (opt_pkgname_match_any) {
341 varbuf_reset(&str);
343 if (opt_divertto == NULL)
344 varbuf_printf(&str, _("any diversion of %s"), filename);
345 else
346 varbuf_printf(&str, _("any diversion of %s to %s"),
347 filename, opt_divertto);
348 } else {
349 return varbuf_diversion(&str, opt_pkgname, filename, opt_divertto);
352 return str.buf;
355 static const char *
356 diversion_describe(struct fsys_diversion *d)
358 static struct varbuf str = VARBUF_INIT;
359 const char *pkgname;
360 const char *name_from, *name_to;
362 if (d->camefrom) {
363 name_from = d->camefrom->name;
364 name_to = d->camefrom->divert->useinstead->name;
365 } else {
366 name_from = d->useinstead->divert->camefrom->name;
367 name_to = d->useinstead->name;
370 if (d->pkgset == NULL)
371 pkgname = NULL;
372 else
373 pkgname = d->pkgset->name;
375 return varbuf_diversion(&str, pkgname, name_from, name_to);
378 static void
379 divertdb_write(void)
381 char *dbname;
382 struct atomic_file *file;
383 struct fsys_hash_iter *iter;
384 struct fsys_namenode *namenode;
386 dbname = dpkg_db_get_path(DIVERSIONSFILE);
388 file = atomic_file_new(dbname, ATOMIC_FILE_BACKUP);
389 atomic_file_open(file);
391 iter = fsys_hash_iter_new();
392 while ((namenode = fsys_hash_iter_next(iter))) {
393 struct fsys_diversion *d = namenode->divert;
395 if (d == NULL || d->useinstead == NULL)
396 continue;
398 fprintf(file->fp, "%s\n%s\n%s\n",
399 d->useinstead->divert->camefrom->name,
400 d->useinstead->name,
401 diversion_pkg_name(d));
403 fsys_hash_iter_free(iter);
405 atomic_file_sync(file);
406 atomic_file_close(file);
407 atomic_file_commit(file);
408 atomic_file_free(file);
410 free(dbname);
413 static bool
414 diversion_is_essential(struct fsys_namenode *namenode)
416 struct pkginfo *pkg;
417 struct pkg_hash_iter *pkg_iter;
418 struct fsys_node_pkgs_iter *iter;
419 bool essential = false;
421 pkg_iter = pkg_hash_iter_new();
422 while ((pkg = pkg_hash_iter_next_pkg(pkg_iter))) {
423 if (pkg->installed.essential)
424 ensure_packagefiles_available(pkg);
426 pkg_hash_iter_free(pkg_iter);
428 iter = fsys_node_pkgs_iter_new(namenode);
429 while ((pkg = fsys_node_pkgs_iter_next(iter))) {
430 if (pkg->installed.essential) {
431 essential = true;
432 break;
435 fsys_node_pkgs_iter_free(iter);
437 return essential;
440 static bool
441 diversion_is_owned_by_self(struct pkgset *set, struct fsys_namenode *namenode)
443 struct pkginfo *pkg;
444 struct fsys_node_pkgs_iter *iter;
445 bool owned = false;
447 if (set == NULL)
448 return false;
450 for (pkg = &set->pkg; pkg; pkg = pkg->arch_next)
451 ensure_packagefiles_available(pkg);
453 iter = fsys_node_pkgs_iter_new(namenode);
454 while ((pkg = fsys_node_pkgs_iter_next(iter))) {
455 if (pkg->set == set) {
456 owned = true;
457 break;
460 fsys_node_pkgs_iter_free(iter);
462 return owned;
465 static int
466 diversion_add(const char *const *argv)
468 const char *filename = argv[0];
469 struct file file_from, file_to;
470 struct fsys_diversion *contest, *altname;
471 struct fsys_namenode *fnn_from, *fnn_to;
472 struct pkgset *pkgset;
474 opt_pkgname_match_any = false;
475 opt_rename_setup();
477 /* Handle filename. */
478 if (!filename || argv[1])
479 badusage(_("--%s needs a single argument"), cipaction->olong);
481 diversion_check_filename(filename);
483 modstatdb_open(msdbrw_readonly);
484 ensure_diversions();
486 file_init(&file_from, filename);
487 file_stat(&file_from);
489 if (file_from.stat_state == FILE_STAT_VALID &&
490 S_ISDIR(file_from.stat.st_mode))
491 badusage(_("cannot divert directories"));
493 fnn_from = fsys_hash_find_node(filename, 0);
495 /* Handle divertto. */
496 if (opt_divertto == NULL)
497 opt_divertto = str_fmt("%s.distrib", filename);
499 if (strcmp(filename, opt_divertto) == 0)
500 badusage(_("cannot divert file '%s' to itself"), filename);
502 file_init(&file_to, opt_divertto);
504 fnn_to = fsys_hash_find_node(opt_divertto, 0);
506 /* Handle package name. */
507 if (opt_pkgname == NULL)
508 pkgset = NULL;
509 else
510 pkgset = pkg_hash_find_set(opt_pkgname);
512 /* Check we are not stomping over an existing diversion. */
513 if (fnn_from->divert || fnn_to->divert) {
514 if (fnn_to->divert && fnn_to->divert->camefrom &&
515 strcmp(fnn_to->divert->camefrom->name, filename) == 0 &&
516 fnn_from->divert && fnn_from->divert->useinstead &&
517 strcmp(fnn_from->divert->useinstead->name, opt_divertto) == 0 &&
518 fnn_from->divert->pkgset == pkgset) {
519 if (opt_verbose > 0)
520 printf(_("Leaving '%s'\n"),
521 diversion_describe(fnn_from->divert));
523 file_destroy(&file_from);
524 file_destroy(&file_to);
526 modstatdb_shutdown();
528 return 0;
531 ohshit(_("'%s' clashes with '%s'"),
532 diversion_current(filename),
533 fnn_from->divert ?
534 diversion_describe(fnn_from->divert) :
535 diversion_describe(fnn_to->divert));
538 /* Create new diversion. */
539 contest = nfmalloc(sizeof(*contest));
540 altname = nfmalloc(sizeof(*altname));
542 altname->camefrom = fnn_from;
543 altname->camefrom->divert = contest;
544 altname->useinstead = NULL;
545 altname->pkgset = pkgset;
547 contest->useinstead = fnn_to;
548 contest->useinstead->divert = altname;
549 contest->camefrom = NULL;
550 contest->pkgset = pkgset;
552 /* Update database and file system if needed. */
553 if (opt_verbose > 0)
554 printf(_("Adding '%s'\n"), diversion_describe(contest));
555 if (opt_rename)
556 opt_rename = check_rename(&file_from, &file_to);
557 /* Check we are not renaming a file owned by the diverting pkgset. */
558 if (opt_rename && diversion_is_owned_by_self(pkgset, fnn_from)) {
559 if (opt_verbose > 0)
560 printf(_("Ignoring request to rename file '%s' "
561 "owned by diverting package '%s'\n"),
562 filename, pkgset->name);
563 opt_rename = false;
565 if (opt_rename && diversion_is_essential(fnn_from))
566 warning(_("diverting file '%s' from an Essential package with "
567 "rename is dangerous, use --no-rename"), filename);
568 if (!opt_test) {
569 divertdb_write();
570 if (opt_rename)
571 file_rename(&file_from, &file_to);
574 file_destroy(&file_from);
575 file_destroy(&file_to);
577 modstatdb_shutdown();
579 return 0;
582 static bool
583 diversion_is_shared(struct pkgset *set, struct fsys_namenode *namenode)
585 const char *archname;
586 struct pkginfo *pkg;
587 struct dpkg_arch *arch;
588 struct fsys_node_pkgs_iter *iter;
589 bool shared = false;
591 if (set == NULL)
592 return false;
594 archname = getenv("DPKG_MAINTSCRIPT_ARCH");
595 arch = dpkg_arch_find(archname);
596 if (arch->type == DPKG_ARCH_NONE || arch->type == DPKG_ARCH_EMPTY)
597 return false;
599 for (pkg = &set->pkg; pkg; pkg = pkg->arch_next)
600 ensure_packagefiles_available(pkg);
602 iter = fsys_node_pkgs_iter_new(namenode);
603 while ((pkg = fsys_node_pkgs_iter_next(iter))) {
604 if (pkg->set == set && pkg->installed.arch != arch) {
605 shared = true;
606 break;
609 fsys_node_pkgs_iter_free(iter);
611 return shared;
614 static int
615 diversion_remove(const char *const *argv)
617 const char *filename = argv[0];
618 struct fsys_namenode *namenode;
619 struct fsys_diversion *contest, *altname;
620 struct file file_from, file_to;
621 struct pkgset *pkgset;
623 opt_rename_setup();
625 if (!filename || argv[1])
626 badusage(_("--%s needs a single argument"), cipaction->olong);
628 diversion_check_filename(filename);
630 modstatdb_open(msdbrw_readonly);
631 ensure_diversions();
633 namenode = fsys_hash_find_node(filename, FHFF_NONEW);
635 if (namenode == NULL || namenode->divert == NULL ||
636 namenode->divert->useinstead == NULL) {
637 if (opt_verbose > 0)
638 printf(_("No diversion '%s', none removed.\n"),
639 diversion_current(filename));
640 modstatdb_shutdown();
641 return 0;
644 if (opt_pkgname == NULL)
645 pkgset = NULL;
646 else
647 pkgset = pkg_hash_find_set(opt_pkgname);
649 contest = namenode->divert;
650 altname = contest->useinstead->divert;
652 if (opt_divertto != NULL &&
653 strcmp(opt_divertto, contest->useinstead->name) != 0)
654 ohshit(_("mismatch on divert-to\n"
655 " when removing '%s'\n"
656 " found '%s'"),
657 diversion_current(filename),
658 diversion_describe(contest));
660 if (!opt_pkgname_match_any && pkgset != contest->pkgset)
661 ohshit(_("mismatch on package\n"
662 " when removing '%s'\n"
663 " found '%s'"),
664 diversion_current(filename),
665 diversion_describe(contest));
667 /* Ignore removal request if the diverted file is still owned
668 * by another package in the same set. */
669 if (diversion_is_shared(pkgset, namenode)) {
670 if (opt_verbose > 0)
671 printf(_("Ignoring request to remove shared diversion '%s'.\n"),
672 diversion_describe(contest));
673 modstatdb_shutdown();
674 return 0;
677 if (opt_verbose > 0)
678 printf(_("Removing '%s'\n"), diversion_describe(contest));
680 file_init(&file_from, altname->camefrom->name);
681 file_init(&file_to, contest->useinstead->name);
683 /* Remove entries from database. */
684 contest->useinstead->divert = NULL;
685 altname->camefrom->divert = NULL;
687 if (opt_rename)
688 opt_rename = check_rename(&file_to, &file_from);
689 if (opt_rename && !opt_test)
690 file_rename(&file_to, &file_from);
692 if (!opt_test)
693 divertdb_write();
695 file_destroy(&file_from);
696 file_destroy(&file_to);
698 modstatdb_shutdown();
700 return 0;
703 static int
704 diversion_list(const char *const *argv)
706 struct fsys_hash_iter *iter;
707 struct fsys_namenode *namenode;
708 struct glob_node *glob_list = NULL;
709 const char *pattern;
711 modstatdb_open(msdbrw_readonly);
712 ensure_diversions();
714 while ((pattern = *argv++))
715 glob_list_prepend(&glob_list, m_strdup(pattern));
717 if (glob_list == NULL)
718 glob_list_prepend(&glob_list, m_strdup("*"));
720 iter = fsys_hash_iter_new();
721 while ((namenode = fsys_hash_iter_next(iter))) {
722 struct glob_node *g;
723 struct fsys_diversion *contest = namenode->divert;
724 struct fsys_diversion *altname;
725 const char *pkgname;
727 if (contest == NULL || contest->useinstead == NULL)
728 continue;
730 altname = contest->useinstead->divert;
732 pkgname = diversion_pkg_name(contest);
734 for (g = glob_list; g; g = g->next) {
735 if (fnmatch(g->pattern, pkgname, 0) == 0 ||
736 fnmatch(g->pattern, contest->useinstead->name, 0) == 0 ||
737 fnmatch(g->pattern, altname->camefrom->name, 0) == 0) {
738 printf("%s\n", diversion_describe(contest));
739 break;
743 fsys_hash_iter_free(iter);
745 glob_list_free(glob_list);
747 modstatdb_shutdown();
749 return 0;
752 static int
753 diversion_truename(const char *const *argv)
755 const char *filename = argv[0];
756 struct fsys_namenode *namenode;
758 if (!filename || argv[1])
759 badusage(_("--%s needs a single argument"), cipaction->olong);
761 diversion_check_filename(filename);
763 modstatdb_open(msdbrw_readonly);
764 ensure_diversions();
766 namenode = fsys_hash_find_node(filename, FHFF_NONEW);
768 /* Print the given name if file is not diverted. */
769 if (namenode && namenode->divert && namenode->divert->useinstead)
770 printf("%s\n", namenode->divert->useinstead->name);
771 else
772 printf("%s\n", filename);
774 modstatdb_shutdown();
776 return 0;
779 static int
780 diversion_listpackage(const char *const *argv)
782 const char *filename = argv[0];
783 struct fsys_namenode *namenode;
785 if (!filename || argv[1])
786 badusage(_("--%s needs a single argument"), cipaction->olong);
788 diversion_check_filename(filename);
790 modstatdb_open(msdbrw_readonly);
791 ensure_diversions();
793 namenode = fsys_hash_find_node(filename, FHFF_NONEW);
795 /* Print nothing if file is not diverted. */
796 if (namenode == NULL || namenode->divert == NULL)
797 return 0;
799 if (namenode->divert->pkgset == NULL)
800 /* Indicate package is local using something not in package
801 * namespace. */
802 printf("LOCAL\n");
803 else
804 printf("%s\n", namenode->divert->pkgset->name);
806 modstatdb_shutdown();
808 return 0;
811 static void
812 set_package(const struct cmdinfo *cip, const char *value)
814 opt_pkgname_match_any = false;
816 /* If value is NULL we are being called from --local. */
817 opt_pkgname = value;
819 if (opt_pkgname && strchr(opt_pkgname, '\n') != NULL)
820 badusage(_("package may not contain newlines"));
823 static void
824 set_divertto(const struct cmdinfo *cip, const char *value)
826 opt_divertto = value;
828 if (opt_divertto[0] != '/')
829 badusage(_("filename \"%s\" is not absolute"), opt_divertto);
830 if (strchr(opt_divertto, '\n') != NULL)
831 badusage(_("divert-to may not contain newlines"));
834 static const struct cmdinfo cmdinfo_add =
835 ACTION("add", 0, 0, diversion_add);
837 static const struct cmdinfo cmdinfos[] = {
838 ACTION("add", 0, 0, diversion_add),
839 ACTION("remove", 0, 0, diversion_remove),
840 ACTION("list", 0, 0, diversion_list),
841 ACTION("listpackage", 0, 0, diversion_listpackage),
842 ACTION("truename", 0, 0, diversion_truename),
843 ACTION("help", '?', 0, usage),
844 ACTION("version", 0, 0, printversion),
846 { "admindir", 0, 1, NULL, NULL, set_admindir, 0 },
847 { "instdir", 0, 1, NULL, NULL, set_instdir, 0 },
848 { "root", 0, 1, NULL, NULL, set_root, 0 },
849 { "divert", 0, 1, NULL, NULL, set_divertto },
850 { "package", 0, 1, NULL, NULL, set_package },
851 { "local", 0, 0, NULL, NULL, set_package },
852 { "quiet", 0, 0, &opt_verbose, NULL, NULL, 0 },
853 { "rename", 0, 0, &opt_rename, NULL, NULL, 1 },
854 { "no-rename", 0, 0, &opt_rename, NULL, NULL, 0 },
855 { "test", 0, 0, &opt_test, NULL, NULL, 1 },
856 { NULL, 0 }
860 main(int argc, const char * const *argv)
862 const char *env_pkgname;
863 int ret;
865 dpkg_locales_init(PACKAGE);
866 dpkg_program_init("dpkg-divert");
867 dpkg_options_parse(&argv, cmdinfos, printforhelp);
869 debug(dbg_general, "root=%s admindir=%s", dpkg_fsys_get_dir(), dpkg_db_get_dir());
871 env_pkgname = getenv("DPKG_MAINTSCRIPT_PACKAGE");
872 if (opt_pkgname_match_any && env_pkgname)
873 set_package(NULL, env_pkgname);
875 if (!cipaction)
876 setaction(&cmdinfo_add, NULL);
878 ret = cipaction->action(argv);
880 dpkg_program_done();
881 dpkg_locales_done();
883 return ret;