Commit 7cbbb401 7cbbb4012d63c86e8e0726a5634bdd880ffe34b3 by Sergey Poznyakoff

Movemail improved.

* movemail/movemail.c: New options: --ignore-errors and --program-id.
(_compare_msgno): Remove now unneded static.
(main): Handle --ignore-errors and --program-id settings.
Optimize the main loop for the case when --uidls is given.
Return 1 on errors.
* doc/texinfo/programs.texi: Document new features.
1 parent 7d8bee8e
...@@ -4369,7 +4369,7 @@ intended as a replacement for @command{movemail} from GNU Emacs. The ...@@ -4369,7 +4369,7 @@ intended as a replacement for @command{movemail} from GNU Emacs. The
4369 module. @xref{Rmail,,,emacs,Reading Mail with Rmail}, for detailed 4369 module. @xref{Rmail,,,emacs,Reading Mail with Rmail}, for detailed
4370 description of @code{Rmail} interface. 4370 description of @code{Rmail} interface.
4371 4371
4372 Mailutils version of @command{movemail} is completely 4372 Mailutils version of @command{movemail} is fully
4373 backward-compatible with its Emacs predecessor, so it should run 4373 backward-compatible with its Emacs predecessor, so it should run
4374 flawlessly with older versions of Emacs. Emacs versions 4374 flawlessly with older versions of Emacs. Emacs versions
4375 starting from 22.1 contain improved @code{Rmail} interface and 4375 starting from 22.1 contain improved @code{Rmail} interface and
...@@ -4401,6 +4401,57 @@ If @var{bool} is @samp{true}, reverse message sorting order. ...@@ -4401,6 +4401,57 @@ If @var{bool} is @samp{true}, reverse message sorting order.
4401 If @var{bool} is @samp{true}, output information used by Emacs rmail interface. 4401 If @var{bool} is @samp{true}, output information used by Emacs rmail interface.
4402 @end deffn 4402 @end deffn
4403 4403
4404 @deffn {Movemail Config} ignore-erros @var{bool}
4405 Continue moving messages after errors. By default,
4406 @command{mailfromd} exits immediately if it cannot copy a message.
4407 @end deffn
4408
4409 @deffn {Movemail Config} program-id @var{fmt}
4410 Set program identifier, i.e. a string which will prefix all
4411 diagnostic messages issued by the program. By default, program
4412 name is used.
4413
4414 The @var{fmt} is a format string that may contain references to the
4415 following macro variables (@FIXME-pxref{macro-variables}):
4416
4417 @table @code
4418 @item progname
4419 The program name.
4420
4421 @item source
4422 URL of the source mailbox.
4423
4424 @item source:user
4425 User part of the source mailbox URL.
4426
4427 @item source:host
4428 Host part of the source mailbox URL.
4429
4430 @item source:path
4431 Path part of the source mailbox URL.
4432
4433 @item dest
4434 URL of the destination mailbox
4435
4436 @item dest:user
4437 User part of the destination mailbox URL.
4438
4439 @item dest:host
4440 Host part of the destination mailbox URL.
4441
4442 @item dest:path
4443 Path part of the destination mailbox URL.
4444 @end table
4445
4446 Setting @code{program-id} may be necessary if several @code{movemail}
4447 instances are run simultaneously (e.g. invoked from a script) to
4448 discern between the instances. For example:
4449
4450 @smallexample
4451 program-id "$@{progname@}: $@{source@} => $@{dest@}"
4452 @end smallexample
4453 @end deffn
4454
4404 @deffn {Movemail Config} uidl @var{bool} 4455 @deffn {Movemail Config} uidl @var{bool}
4405 Avoid copying the message if a message with the same UIDL already 4456 Avoid copying the message if a message with the same UIDL already
4406 exists in the destination mailbox. 4457 exists in the destination mailbox.
...@@ -4494,11 +4545,18 @@ Following is the summary of available command line options: ...@@ -4494,11 +4545,18 @@ Following is the summary of available command line options:
4494 @item --emacs 4545 @item --emacs
4495 Output information used by Emacs rmail interface 4546 Output information used by Emacs rmail interface
4496 4547
4548 @item --ignore-errors
4549 Continue moving messages after an error occurs.
4550
4497 @item -p 4551 @item -p
4498 @itemx --preserve 4552 @itemx --preserve
4499 @itemx --keep-messages 4553 @itemx --keep-messages
4500 Preserve the source mailbox 4554 Preserve the source mailbox
4501 4555
4556 @item --program-id=@var{fmt}
4557 Set program identifier for diagnostic purposes. See @ref{Movemail
4558 Configuration,program-id}, for a detailed discussion of this feature.
4559
4502 @item -r 4560 @item -r
4503 @itemx --reverse 4561 @itemx --reverse
4504 Reverse the sorting order 4562 Reverse the sorting order
......
...@@ -37,13 +37,17 @@ const char *program_version = "movemail (" PACKAGE_STRING ")"; ...@@ -37,13 +37,17 @@ const char *program_version = "movemail (" PACKAGE_STRING ")";
37 static char doc[] = N_("GNU movemail -- move messages across mailboxes."); 37 static char doc[] = N_("GNU movemail -- move messages across mailboxes.");
38 static char args_doc[] = N_("inbox-url destfile [POP-password]"); 38 static char args_doc[] = N_("inbox-url destfile [POP-password]");
39 39
40 #define OPT_EMACS 256 40 enum {
41 EMACS_OPTION=256,
42 IGNORE_ERRORS_OPTION,
43 PROGRAM_ID_OPTION
44 };
41 45
42 static struct argp_option options[] = { 46 static struct argp_option options[] = {
43 { "preserve", 'p', NULL, 0, N_("preserve the source mailbox") }, 47 { "preserve", 'p', NULL, 0, N_("preserve the source mailbox") },
44 { "keep-messages", 0, NULL, OPTION_ALIAS, NULL }, 48 { "keep-messages", 0, NULL, OPTION_ALIAS, NULL },
45 { "reverse", 'r', NULL, 0, N_("reverse the sorting order") }, 49 { "reverse", 'r', NULL, 0, N_("reverse the sorting order") },
46 { "emacs", OPT_EMACS, NULL, 0, 50 { "emacs", EMACS_OPTION, NULL, 0,
47 N_("output information used by Emacs rmail interface") }, 51 N_("output information used by Emacs rmail interface") },
48 { "uidl", 'u', NULL, 0, 52 { "uidl", 'u', NULL, 0,
49 N_("use UIDLs to avoid downloading the same message twice") }, 53 N_("use UIDLs to avoid downloading the same message twice") },
...@@ -51,6 +55,10 @@ static struct argp_option options[] = { ...@@ -51,6 +55,10 @@ static struct argp_option options[] = {
51 N_("increase verbosity level") }, 55 N_("increase verbosity level") },
52 { "owner", 'P', N_("MODELIST"), 0, 56 { "owner", 'P', N_("MODELIST"), 0,
53 N_("control mailbox ownership") }, 57 N_("control mailbox ownership") },
58 { "ignore-errors", IGNORE_ERRORS_OPTION, NULL, 0,
59 N_("try to continue after errors") },
60 { "program-id", PROGRAM_ID_OPTION, N_("FMT"), 0,
61 N_("set program identifier for diagnostics (default: program name)") },
54 { NULL, 0, NULL, 0, NULL, 0 } 62 { NULL, 0, NULL, 0, NULL, 0 }
55 }; 63 };
56 64
...@@ -59,6 +67,8 @@ static int preserve_mail; ...@@ -59,6 +67,8 @@ static int preserve_mail;
59 static int emacs_mode; 67 static int emacs_mode;
60 static int uidl_option; 68 static int uidl_option;
61 static int verbose_option; 69 static int verbose_option;
70 static int ignore_errors;
71 static char *program_id_option;
62 72
63 enum set_ownership_mode 73 enum set_ownership_mode
64 { 74 {
...@@ -135,10 +145,18 @@ parse_opt (int key, char *arg, struct argp_state *state) ...@@ -135,10 +145,18 @@ parse_opt (int key, char *arg, struct argp_state *state)
135 verbose_option++; 145 verbose_option++;
136 break; 146 break;
137 147
138 case OPT_EMACS: 148 case EMACS_OPTION:
139 mu_argp_node_list_new (lst, "emacs", "yes"); 149 mu_argp_node_list_new (lst, "emacs", "yes");
140 break; 150 break;
141 151
152 case IGNORE_ERRORS_OPTION:
153 mu_argp_node_list_new (lst, "ignore-errors", "yes");
154 break;
155
156 case PROGRAM_ID_OPTION:
157 mu_argp_node_list_new (lst, "program-id", arg);
158 break;
159
142 case ARGP_KEY_INIT: 160 case ARGP_KEY_INIT:
143 mu_argp_node_list_init (&lst); 161 mu_argp_node_list_init (&lst);
144 break; 162 break;
...@@ -298,6 +316,10 @@ struct mu_cfg_param movemail_cfg_param[] = { ...@@ -298,6 +316,10 @@ struct mu_cfg_param movemail_cfg_param[] = {
298 N_("Use UIDLs to avoid downloading the same message twice.") }, 316 N_("Use UIDLs to avoid downloading the same message twice.") },
299 { "verbose", mu_cfg_int, &verbose_option, 0, NULL, 317 { "verbose", mu_cfg_int, &verbose_option, 0, NULL,
300 N_("Set verbosity level.") }, 318 N_("Set verbosity level.") },
319 { "ignore-errors", mu_cfg_bool, &ignore_errors, 0, NULL,
320 N_("Continue after an error.") },
321 { "program-id", mu_cfg_string, &program_id_option, 0, NULL,
322 N_("Set program identifier string (default: program name)") },
301 { "mailbox-ownership", mu_cfg_callback, NULL, 0, 323 { "mailbox-ownership", mu_cfg_callback, NULL, 0,
302 cb_mailbox_ownership, 324 cb_mailbox_ownership,
303 N_("Define a list of methods for setting mailbox ownership. Valid " 325 N_("Define a list of methods for setting mailbox ownership. Valid "
...@@ -633,25 +655,69 @@ _compare_uidls (const void *item, const void *value) ...@@ -633,25 +655,69 @@ _compare_uidls (const void *item, const void *value)
633 return strcmp (a->uidl, b->uidl); 655 return strcmp (a->uidl, b->uidl);
634 } 656 }
635 657
636 static int 658 #define __cat2__(a,b) a ## b
637 _compare_msgno (const void *item, const void *value) 659 #define DCL_VTX(what) \
638 { 660 static int \
639 const struct mu_uidl *a = item; 661 __cat2__(_vtx_,what) (const char *name, void *data, char **p) \
640 const struct mu_uidl *b = value; 662 { \
641 663 mu_url_t url = data; \
642 if (a->msgno < b->msgno) 664 int rc = __cat2__(mu_url_aget_,what) (url, p); \
643 return -1; 665 if (rc == MU_ERR_NOENT) \
644 if (a->msgno > b->msgno) 666 { \
645 return 1; 667 *p = strdup (""); \
646 return 0; 668 return 0; \
669 } \
670 return rc; \
647 } 671 }
648 672
649 static int 673 DCL_VTX (host)
650 msgno_in_list (mu_list_t list, size_t num) 674 DCL_VTX (user)
675 DCL_VTX (path)
676
677 static void
678 set_program_id (const char *source_name, const char *dest_name)
651 { 679 {
652 struct mu_uidl t; 680 int rc;
653 t.msgno = num; 681 mu_vartab_t vtab;
654 return mu_list_locate (list, &t, NULL) == 0; 682 char *id;
683 mu_url_t url;
684
685 mu_vartab_create (&vtab);
686 mu_vartab_define (vtab, "progname", mu_program_name, 1);
687 mu_vartab_define (vtab, "source", source_name, 1);
688 rc = mu_mailbox_get_url (source, &url);
689 if (rc)
690 mu_diag_output (MU_DIAG_INFO,
691 _("cannot obtain source mailbox URL: %s"),
692 mu_strerror (rc));
693 else
694 {
695 mu_vartab_define_exp (vtab, "source:user", _vtx_user, NULL, url);
696 mu_vartab_define_exp (vtab, "source:host", _vtx_host, NULL, url);
697 mu_vartab_define_exp (vtab, "source:path", _vtx_path, NULL, url);
698 }
699
700 mu_vartab_define (vtab, "dest", dest_name, 1);
701 rc = mu_mailbox_get_url (dest, &url);
702 if (rc)
703 mu_diag_output (MU_DIAG_INFO,
704 _("cannot obtain destination mailbox URL: %s"),
705 mu_strerror (rc));
706 else
707 {
708 mu_vartab_define_exp (vtab, "dest:user", _vtx_user, NULL, url);
709 mu_vartab_define_exp (vtab, "dest:host", _vtx_host, NULL, url);
710 mu_vartab_define_exp (vtab, "dest:path", _vtx_path, NULL, url);
711 }
712
713 rc = mu_vartab_expand (vtab, program_id_option, &id);
714 mu_vartab_destroy (&vtab);
715 /* asprintf (&id, "%s: %s", mu_program_name, s);
716 free (s);*/
717 /* FIXME: Don't use mu_set_program_name here, because it
718 plays wise with its argument. We need a mu_set_diag_prefix
719 function. */
720 mu_program_name = id;
655 } 721 }
656 722
657 int 723 int
...@@ -660,6 +726,7 @@ main (int argc, char **argv) ...@@ -660,6 +726,7 @@ main (int argc, char **argv)
660 int index; 726 int index;
661 size_t i, total; 727 size_t i, total;
662 int rc = 0; 728 int rc = 0;
729 int errs = 0;
663 char *source_name, *dest_name; 730 char *source_name, *dest_name;
664 int flags; 731 int flags;
665 mu_list_t src_uidl_list = NULL; 732 mu_list_t src_uidl_list = NULL;
...@@ -707,6 +774,9 @@ main (int argc, char **argv) ...@@ -707,6 +774,9 @@ main (int argc, char **argv)
707 774
708 open_mailbox (&dest, dest_name, MU_STREAM_RDWR | MU_STREAM_CREAT, NULL); 775 open_mailbox (&dest, dest_name, MU_STREAM_RDWR | MU_STREAM_CREAT, NULL);
709 776
777 if (program_id_option)
778 set_program_id (source_name, dest_name);
779
710 rc = mu_mailbox_messages_count (source, &total); 780 rc = mu_mailbox_messages_count (source, &total);
711 if (rc) 781 if (rc)
712 { 782 {
...@@ -745,43 +815,67 @@ main (int argc, char **argv) ...@@ -745,43 +815,67 @@ main (int argc, char **argv)
745 } 815 }
746 mu_iterator_destroy (&itr); 816 mu_iterator_destroy (&itr);
747 mu_list_destroy (&dst_uidl_list); 817 mu_list_destroy (&dst_uidl_list);
748 mu_list_set_comparator (src_uidl_list, _compare_msgno); 818 mu_list_set_comparator (src_uidl_list, NULL);
749 } 819 }
750 820
751 if (reverse_order) 821 /* FIXME: Implementing a mailbox iterator would allow to merge the three
822 branches of this conditional. */
823 if (src_uidl_list)
752 { 824 {
753 for (i = total; i > 0; i--) 825 mu_iterator_t itr;
826
827 rc = mu_list_get_iterator (src_uidl_list, &itr);
828 if (rc)
754 { 829 {
755 if (src_uidl_list && !msgno_in_list (src_uidl_list, i)) 830 mu_error(_("cannot get iterator: %s"), mu_strerror (rc));
831 exit (1);
832 }
833 rc = mu_iterator_ctl (itr, mu_itrctl_set_direction, &reverse_order);
834 if (rc)
756 { 835 {
757 if (verbose_option > 1) 836 mu_error(_("cannot set iteration direction: %s"), mu_strerror (rc));
758 mu_diag_output (MU_DIAG_INFO, _("ignoring message %lu"), 837 exit (1);
759 (unsigned long) i);
760 continue;
761 } 838 }
762 rc = move_message (source, dest, i); 839 for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
840 mu_iterator_next (itr))
841 {
842 struct mu_uidl *uidl;
843
844 mu_iterator_current (itr, (void **)&uidl);
845 rc = move_message (source, dest, uidl->msgno);
763 if (rc == 0) 846 if (rc == 0)
764 msg_count++; 847 msg_count++;
848 else if (!ignore_errors)
849 break;
765 else 850 else
851 errs = 1;
852 }
853 mu_iterator_destroy (&itr);
854 }
855 else if (reverse_order)
856 {
857 for (i = total; i > 0; i--)
858 {
859 rc = move_message (source, dest, i);
860 if (rc == 0)
861 msg_count++;
862 else if (!ignore_errors)
766 break; 863 break;
864 else
865 errs = 1;
767 } 866 }
768 } 867 }
769 else 868 else
770 { 869 {
771 for (i = 1; i <= total; i++) 870 for (i = 1; i <= total; i++)
772 { 871 {
773 if (src_uidl_list && !msgno_in_list (src_uidl_list, i))
774 {
775 if (verbose_option > 1)
776 mu_diag_output (MU_DIAG_INFO, _("ignoring message %lu"),
777 (unsigned long) i);
778 continue;
779 }
780 rc = move_message (source, dest, i); 872 rc = move_message (source, dest, i);
781 if (rc == 0) 873 if (rc == 0)
782 msg_count++; 874 msg_count++;
783 else 875 else if (!ignore_errors)
784 break; 876 break;
877 else
878 errs = 1;
785 } 879 }
786 } 880 }
787 881
...@@ -791,7 +885,7 @@ main (int argc, char **argv) ...@@ -791,7 +885,7 @@ main (int argc, char **argv)
791 (unsigned long) msg_count); 885 (unsigned long) msg_count);
792 886
793 if (rc) 887 if (rc)
794 return rc; 888 return !!rc;
795 889
796 mu_mailbox_sync (dest); 890 mu_mailbox_sync (dest);
797 rc = mu_mailbox_close (dest); 891 rc = mu_mailbox_close (dest);
...@@ -804,5 +898,5 @@ main (int argc, char **argv) ...@@ -804,5 +898,5 @@ main (int argc, char **argv)
804 mu_mailbox_close (source); 898 mu_mailbox_close (source);
805 mu_mailbox_destroy (&source); 899 mu_mailbox_destroy (&source);
806 900
807 return rc; 901 return !(rc == 0 && errs == 0);
808 } 902 }
......