Implement --pack, --verbose, --dry-run (just in case).
Showing
1 changed file
with
325 additions
and
9 deletions
... | @@ -47,6 +47,8 @@ static struct argp_option options[] = { | ... | @@ -47,6 +47,8 @@ static struct argp_option options[] = { |
47 | N_("List the folders (default)"), 1 }, | 47 | N_("List the folders (default)"), 1 }, |
48 | {"list", ARG_LIST, NULL, 0, | 48 | {"list", ARG_LIST, NULL, 0, |
49 | N_("List the contents of the folder stack"), 1}, | 49 | N_("List the contents of the folder stack"), 1}, |
50 | {"pack", ARG_PACK, N_("NUMBER"), OPTION_ARG_OPTIONAL, | ||
51 | N_("Remove holes in message numbering. Begin numbering from NUMBER (default: first message number"), 1}, | ||
50 | {"push", ARG_PUSH, N_("FOLDER"), OPTION_ARG_OPTIONAL, | 52 | {"push", ARG_PUSH, N_("FOLDER"), OPTION_ARG_OPTIONAL, |
51 | N_("Push the folder on the folder stack. If FOLDER is specified, it is pushed. " | 53 | N_("Push the folder on the folder stack. If FOLDER is specified, it is pushed. " |
52 | "Otherwise, if a folder is given in the command line (via + or --folder), " | 54 | "Otherwise, if a folder is given in the command line (via + or --folder), " |
... | @@ -76,7 +78,11 @@ static struct argp_option options[] = { | ... | @@ -76,7 +78,11 @@ static struct argp_option options[] = { |
76 | {"total", ARG_TOTAL, N_("BOOL"), OPTION_ARG_OPTIONAL, | 78 | {"total", ARG_TOTAL, N_("BOOL"), OPTION_ARG_OPTIONAL, |
77 | N_("Output the total statistics"), 3}, | 79 | N_("Output the total statistics"), 3}, |
78 | {"nototal", ARG_NOTOTAL, NULL, OPTION_HIDDEN, ""}, | 80 | {"nototal", ARG_NOTOTAL, NULL, OPTION_HIDDEN, ""}, |
79 | 81 | {"verbose", ARG_VERBOSE, NULL, 0, | |
82 | N_("Verbosely list actions taken"), 3}, | ||
83 | {"dry-run", ARG_DRY_RUN, NULL, 0, | ||
84 | N_("Do nothing, print what would be done (with --pack)"), 3}, | ||
85 | |||
80 | {"license", ARG_LICENSE, 0, 0, | 86 | {"license", ARG_LICENSE, 0, 0, |
81 | N_("Display software license"), -1}, | 87 | N_("Display software license"), -1}, |
82 | 88 | ||
... | @@ -90,6 +96,7 @@ struct mh_option mh_option[] = { | ... | @@ -90,6 +96,7 @@ struct mh_option mh_option[] = { |
90 | {"push", 2, 0, NULL }, | 96 | {"push", 2, 0, NULL }, |
91 | {"pop", 2, 0, NULL }, | 97 | {"pop", 2, 0, NULL }, |
92 | {"all", 1, 0, NULL }, | 98 | {"all", 1, 0, NULL }, |
99 | {"pack", 2, 0, NULL }, | ||
93 | {"create", 1, MH_OPT_BOOL, NULL}, | 100 | {"create", 1, MH_OPT_BOOL, NULL}, |
94 | {"fast", 1, MH_OPT_BOOL, NULL}, | 101 | {"fast", 1, MH_OPT_BOOL, NULL}, |
95 | {"header", 1, MH_OPT_BOOL, NULL}, | 102 | {"header", 1, MH_OPT_BOOL, NULL}, |
... | @@ -104,7 +111,7 @@ static int action_print (); | ... | @@ -104,7 +111,7 @@ static int action_print (); |
104 | static int action_list (); | 111 | static int action_list (); |
105 | static int action_push (); | 112 | static int action_push (); |
106 | static int action_pop (); | 113 | static int action_pop (); |
107 | 114 | static int action_pack (); | |
108 | static folder_action action = action_print; | 115 | static folder_action action = action_print; |
109 | 116 | ||
110 | int show_all = 0; /* List all folders. Raised by --all switch */ | 117 | int show_all = 0; /* List all folders. Raised by --all switch */ |
... | @@ -114,9 +121,12 @@ int create_flag = -1; /* Create non-existent folders (--create). | ... | @@ -114,9 +121,12 @@ int create_flag = -1; /* Create non-existent folders (--create). |
114 | 1: Always create without prompting */ | 121 | 1: Always create without prompting */ |
115 | int fast_mode = 0; /* Fast operation mode. (--fast) */ | 122 | int fast_mode = 0; /* Fast operation mode. (--fast) */ |
116 | int print_header = 0; /* Display the header line (--header) */ | 123 | int print_header = 0; /* Display the header line (--header) */ |
117 | int recurse = 0; /* Recurse sub-folders */ | 124 | int recurse = 0; /* Recurse sub-folders */ |
118 | int print_total; /* Display total stats */ | 125 | int print_total; /* Display total stats */ |
119 | 126 | int verbose = 0; /* Verbosely list actions taken */ | |
127 | size_t pack_start; /* Number to be assigned to the first message in packed | ||
128 | folder. 0 means do not change first message number. */ | ||
129 | int dry_run; /* Dry run mode */ | ||
120 | char *push_folder; /* Folder name to push on stack */ | 130 | char *push_folder; /* Folder name to push on stack */ |
121 | 131 | ||
122 | char *mh_seq_name; /* Name of the mh sequence file (defaults to | 132 | char *mh_seq_name; /* Name of the mh sequence file (defaults to |
... | @@ -127,6 +137,21 @@ opt_handler (int key, char *arg, void *unused, struct argp_state *state) | ... | @@ -127,6 +137,21 @@ opt_handler (int key, char *arg, void *unused, struct argp_state *state) |
127 | { | 137 | { |
128 | switch (key) | 138 | switch (key) |
129 | { | 139 | { |
140 | case ARG_DRY_RUN: | ||
141 | dry_run++; | ||
142 | break; | ||
143 | |||
144 | case ARG_PACK: | ||
145 | action = action_pack; | ||
146 | if (arg) | ||
147 | { | ||
148 | char *p; | ||
149 | pack_start = strtoul (arg, &p, 10); | ||
150 | if (*p) | ||
151 | argp_error (state, _("Invalid number")); | ||
152 | } | ||
153 | break; | ||
154 | |||
130 | case ARG_PRINT: | 155 | case ARG_PRINT: |
131 | action = action_print; | 156 | action = action_print; |
132 | break; | 157 | break; |
... | @@ -134,7 +159,7 @@ opt_handler (int key, char *arg, void *unused, struct argp_state *state) | ... | @@ -134,7 +159,7 @@ opt_handler (int key, char *arg, void *unused, struct argp_state *state) |
134 | case ARG_LIST: | 159 | case ARG_LIST: |
135 | action = action_list; | 160 | action = action_list; |
136 | break; | 161 | break; |
137 | 162 | ||
138 | case ARG_PUSH: | 163 | case ARG_PUSH: |
139 | action = action_push; | 164 | action = action_push; |
140 | if (arg) | 165 | if (arg) |
... | @@ -200,13 +225,22 @@ opt_handler (int key, char *arg, void *unused, struct argp_state *state) | ... | @@ -200,13 +225,22 @@ opt_handler (int key, char *arg, void *unused, struct argp_state *state) |
200 | mh_license (argp_program_version); | 225 | mh_license (argp_program_version); |
201 | break; | 226 | break; |
202 | 227 | ||
228 | case ARG_VERBOSE: | ||
229 | verbose++; | ||
230 | break; | ||
231 | |||
203 | default: | 232 | default: |
204 | return 1; | 233 | return 1; |
205 | } | 234 | } |
206 | return 0; | 235 | return 0; |
207 | } | 236 | } |
208 | 237 | ||
209 | struct folder_info { | 238 | |
239 | /* ************************************************************* */ | ||
240 | /* Printing */ | ||
241 | |||
242 | struct folder_info | ||
243 | { | ||
210 | char *name; /* Folder name */ | 244 | char *name; /* Folder name */ |
211 | size_t message_count; /* Number of messages in this folder */ | 245 | size_t message_count; /* Number of messages in this folder */ |
212 | size_t min; /* First used sequence number (=uid) */ | 246 | size_t min; /* First used sequence number (=uid) */ |
... | @@ -224,7 +258,6 @@ size_t message_count; /* Total number of messages */ | ... | @@ -224,7 +258,6 @@ size_t message_count; /* Total number of messages */ |
224 | 258 | ||
225 | int name_prefix_len; /* Length of the mu_path_folder_dir */ | 259 | int name_prefix_len; /* Length of the mu_path_folder_dir */ |
226 | 260 | ||
227 | |||
228 | void | 261 | void |
229 | install_folder_info (const char *name, struct folder_info *info) | 262 | install_folder_info (const char *name, struct folder_info *info) |
230 | { | 263 | { |
... | @@ -418,7 +451,7 @@ print_fast () | ... | @@ -418,7 +451,7 @@ print_fast () |
418 | static int | 451 | static int |
419 | action_print () | 452 | action_print () |
420 | { | 453 | { |
421 | char *folder_dir = mu_folder_directory (); | 454 | const char *folder_dir = mu_folder_directory (); |
422 | mh_seq_name = mh_global_profile_get ("mh-sequences", MH_SEQUENCES_FILE); | 455 | mh_seq_name = mh_global_profile_get ("mh-sequences", MH_SEQUENCES_FILE); |
423 | 456 | ||
424 | name_prefix_len = strlen (folder_dir); | 457 | name_prefix_len = strlen (folder_dir); |
... | @@ -470,6 +503,10 @@ action_print () | ... | @@ -470,6 +503,10 @@ action_print () |
470 | return 0; | 503 | return 0; |
471 | } | 504 | } |
472 | 505 | ||
506 | |||
507 | /* ************************************************************* */ | ||
508 | /* Listing */ | ||
509 | |||
473 | static int | 510 | static int |
474 | action_list () | 511 | action_list () |
475 | { | 512 | { |
... | @@ -482,6 +519,10 @@ action_list () | ... | @@ -482,6 +519,10 @@ action_list () |
482 | return 0; | 519 | return 0; |
483 | } | 520 | } |
484 | 521 | ||
522 | |||
523 | /* ************************************************************* */ | ||
524 | /* Push & pop */ | ||
525 | |||
485 | static char * | 526 | static char * |
486 | make_stack (char *folder, char *old_stack) | 527 | make_stack (char *folder, char *old_stack) |
487 | { | 528 | { |
... | @@ -538,6 +579,281 @@ action_pop () | ... | @@ -538,6 +579,281 @@ action_pop () |
538 | return 0; | 579 | return 0; |
539 | } | 580 | } |
540 | 581 | ||
582 | |||
583 | /* ************************************************************* */ | ||
584 | /* Packing */ | ||
585 | |||
586 | struct pack_tab | ||
587 | { | ||
588 | size_t orig; | ||
589 | size_t new; | ||
590 | }; | ||
591 | |||
592 | static int | ||
593 | pack_rename (struct pack_tab *tab, int reverse) | ||
594 | { | ||
595 | int rc; | ||
596 | char s1[64]; | ||
597 | char s2[64]; | ||
598 | char *from, *to; | ||
599 | |||
600 | snprintf (s1, sizeof s1, "%lu", (unsigned long) tab->orig); | ||
601 | snprintf (s2, sizeof s2, "%lu", (unsigned long) tab->new); | ||
602 | |||
603 | if (!reverse) | ||
604 | { | ||
605 | from = s1; | ||
606 | to = s2; | ||
607 | } | ||
608 | else | ||
609 | { | ||
610 | from = s2; | ||
611 | to = s1; | ||
612 | } | ||
613 | |||
614 | if (verbose) | ||
615 | fprintf (stderr, _("Renaming %s to %s\n"), from, to); | ||
616 | |||
617 | if (!dry_run) | ||
618 | { | ||
619 | if ((rc = rename (from, to))) | ||
620 | mh_error (_("cannot rename `%s' to `%s': %s"), | ||
621 | from, to, mu_strerror (errno)); | ||
622 | } | ||
623 | else | ||
624 | rc = 0; | ||
625 | |||
626 | return rc; | ||
627 | } | ||
628 | |||
629 | /* Reverse ordering of COUNT entries in array TAB */ | ||
630 | static void | ||
631 | reverse (struct pack_tab *tab, size_t count) | ||
632 | { | ||
633 | size_t i, j; | ||
634 | |||
635 | for (i = 0, j = count-1; i < j; i++, j--) | ||
636 | { | ||
637 | size_t tmp; | ||
638 | tmp = tab[i].orig; | ||
639 | tab[i].orig = tab[j].orig; | ||
640 | tab[j].orig = tmp; | ||
641 | |||
642 | tmp = tab[i].new; | ||
643 | tab[i].new = tab[j].new; | ||
644 | tab[j].new = tmp; | ||
645 | } | ||
646 | } | ||
647 | |||
648 | static void | ||
649 | roll_back (const char *folder_name, struct pack_tab *pack_tab, size_t i) | ||
650 | { | ||
651 | size_t start; | ||
652 | |||
653 | if (i == 0) | ||
654 | return; | ||
655 | |||
656 | start = i - 1; | ||
657 | mh_error (_("Rolling back changes...")); | ||
658 | while (--i >= 0) | ||
659 | if (pack_rename (pack_tab + i, 1)) | ||
660 | { | ||
661 | mh_error (_("CRITICAL ERROR: Folder `%s' left in an inconsistent state, because an error\n" | ||
662 | "occurred while trying to roll back the changes.\n" | ||
663 | "Message range %lu-%lu has been renamed to %lu-%lu."), | ||
664 | folder_name, | ||
665 | pack_tab[0].orig, pack_tab[start].orig, | ||
666 | pack_tab[0].new, pack_tab[start].new); | ||
667 | mh_error (_("You will have to fix it manually.")); | ||
668 | exit (1); | ||
669 | } | ||
670 | mh_error (_("Folder `%s' restored successfully"), folder_name); | ||
671 | } | ||
672 | |||
673 | struct fixup_data | ||
674 | { | ||
675 | const char *folder_dir; | ||
676 | struct pack_tab *pack_tab; | ||
677 | size_t count; | ||
678 | }; | ||
679 | |||
680 | static int | ||
681 | pack_cmp (const void *a, const void *b) | ||
682 | { | ||
683 | const struct pack_tab *pa = a; | ||
684 | const struct pack_tab *pb = b; | ||
685 | |||
686 | if (pa->orig < pb->orig) | ||
687 | return -1; | ||
688 | else if (pa->orig > pb->orig) | ||
689 | return 1; | ||
690 | return 0; | ||
691 | } | ||
692 | |||
693 | static size_t | ||
694 | pack_xlate (struct pack_tab *pack_tab, size_t count, size_t n) | ||
695 | { | ||
696 | struct pack_tab key, *p; | ||
697 | |||
698 | key.orig = n; | ||
699 | p = bsearch (&key, pack_tab, count, sizeof pack_tab[0], pack_cmp); | ||
700 | return p ? p->new : 0; | ||
701 | } | ||
702 | |||
703 | static int | ||
704 | _fixup (char *name, char *value, struct fixup_data *fd, int flags) | ||
705 | { | ||
706 | int i, j, argc; | ||
707 | char **argv; | ||
708 | mh_msgset_t msgset; | ||
709 | |||
710 | if (verbose) | ||
711 | fprintf (stderr, "Sequence `%s'...\n", name); | ||
712 | |||
713 | if (mu_argcv_get (value, "", NULL, &argc, &argv)) | ||
714 | return 0; | ||
715 | |||
716 | msgset.list = xcalloc (argc, sizeof msgset.list[0]); | ||
717 | for (i = j = 0; i < argc; i++) | ||
718 | { | ||
719 | size_t n = pack_xlate (fd->pack_tab, fd->count, | ||
720 | strtoul (argv[i], NULL, 0)); | ||
721 | if (n) | ||
722 | msgset.list[j++] = n; | ||
723 | } | ||
724 | msgset.count = j; | ||
725 | |||
726 | mh_seq_add (name, &msgset, flags | SEQ_ZERO); | ||
727 | free (msgset.list); | ||
728 | |||
729 | if (verbose) | ||
730 | { | ||
731 | char *p = mh_seq_read (name, flags); | ||
732 | fprintf (stderr, "Sequence %s: %s\n", name, p); | ||
733 | } | ||
734 | |||
735 | return 0; | ||
736 | } | ||
737 | |||
738 | static int | ||
739 | fixup_global (char *name, char *value, void *data) | ||
740 | { | ||
741 | return _fixup (name, value, data, 0); | ||
742 | } | ||
743 | |||
744 | static int | ||
745 | fixup_private (char *name, char *value, void *data) | ||
746 | { | ||
747 | struct fixup_data *fd = data; | ||
748 | int nlen = strlen (name); | ||
749 | if (nlen < 4 || memcmp (name, "atr-", 4)) | ||
750 | return 0; | ||
751 | name += 4; | ||
752 | |||
753 | nlen = strlen (name) - strlen (fd->folder_dir); | ||
754 | if (nlen > 0 && strcmp (name + nlen, fd->folder_dir) == 0) | ||
755 | { | ||
756 | name[nlen-1] = 0; | ||
757 | return _fixup (name, value, fd, SEQ_PRIVATE); | ||
758 | } | ||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | int | ||
763 | action_pack () | ||
764 | { | ||
765 | const char *folder_dir = mh_expand_name (NULL, current_folder, 0); | ||
766 | mu_mailbox_t mbox = mh_open_folder (current_folder, 0); | ||
767 | struct pack_tab *pack_tab; | ||
768 | size_t i, count, start; | ||
769 | int status; | ||
770 | struct fixup_data fd; | ||
771 | |||
772 | /* Allocate pack table */ | ||
773 | if (mu_mailbox_messages_count (mbox, &count)) | ||
774 | { | ||
775 | mh_error (_("Cannot read input mailbox: %s"), mu_strerror (errno)); | ||
776 | return 1; | ||
777 | } | ||
778 | pack_tab = xcalloc (count, sizeof pack_tab[0]); /* Never freed. No use to | ||
779 | try to. */ | ||
780 | |||
781 | /* Populate it with message numbers */ | ||
782 | if (verbose) | ||
783 | fprintf (stderr, _("Getting message numbers.\n")); | ||
784 | |||
785 | for (i = 0; i < count; i++) | ||
786 | { | ||
787 | mu_message_t msg; | ||
788 | status = mu_mailbox_get_message (mbox, i + 1, &msg); | ||
789 | if (status) | ||
790 | { | ||
791 | mh_error (_("%d: cannot get message: %s"), i, mu_strerror (status)); | ||
792 | return 1; | ||
793 | } | ||
794 | mh_message_number (msg, &pack_tab[i].orig); | ||
795 | } | ||
796 | if (verbose) | ||
797 | fprintf (stderr, ngettext ("%lu message number collected.\n", | ||
798 | "%lu message numbers collected.\n", | ||
799 | count), | ||
800 | (unsigned long) count); | ||
801 | |||
802 | mu_mailbox_close (mbox); | ||
803 | mu_mailbox_destroy (&mbox); | ||
804 | |||
805 | /* Compute new message numbers */ | ||
806 | if (pack_start == 0) | ||
807 | pack_start = pack_tab[0].orig; | ||
808 | |||
809 | for (i = 0, start = pack_start; i < count; i++) | ||
810 | pack_tab[i].new = start++; | ||
811 | |||
812 | if (pack_start > pack_tab[0].orig) | ||
813 | { | ||
814 | if (verbose) | ||
815 | fprintf (stderr, _("Reverting pack table.\n")); | ||
816 | reverse (pack_tab, i); | ||
817 | } | ||
818 | |||
819 | /* Change to the folder directory and rename messages */ | ||
820 | status = chdir (folder_dir); | ||
821 | if (status) | ||
822 | { | ||
823 | mh_error (_("cannot change to directory `%s': %s"), | ||
824 | folder_dir, mu_strerror (status)); | ||
825 | return 1; | ||
826 | } | ||
827 | |||
828 | for (i = 0; i < count; i++) | ||
829 | { | ||
830 | if (pack_rename (pack_tab + i, 0)) | ||
831 | { | ||
832 | roll_back (folder_dir, pack_tab, i); | ||
833 | return 1; | ||
834 | } | ||
835 | } | ||
836 | |||
837 | if (verbose) | ||
838 | fprintf (stderr, _("Finished packing messages.\n")); | ||
839 | |||
840 | /* Fix-up sequences */ | ||
841 | fd.folder_dir = folder_dir; | ||
842 | fd.pack_tab = pack_tab; | ||
843 | fd.count = count; | ||
844 | if (verbose) | ||
845 | fprintf (stderr, _("Fixing global sequences\n")); | ||
846 | mh_global_sequences_iterate (fixup_global, &fd); | ||
847 | if (verbose) | ||
848 | fprintf (stderr, _("Fixing private sequences\n")); | ||
849 | mh_global_context_iterate (fixup_private, &fd); | ||
850 | |||
851 | if (!dry_run) | ||
852 | mh_global_save_state (); | ||
853 | |||
854 | return 0; | ||
855 | } | ||
856 | |||
541 | int | 857 | int |
542 | main (int argc, char **argv) | 858 | main (int argc, char **argv) |
543 | { | 859 | { | ... | ... |
-
Please register or sign in to post a comment