Commit 0e2ce333 0e2ce333aa40b5d4ee3959b5b2b020b569d2d2f7 by Sergey Poznyakoff

Implement --pack, --verbose, --dry-run (just in case).

1 parent ad28db8b
Showing 1 changed file with 321 additions and 5 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,6 +78,10 @@ static struct argp_option options[] = { ...@@ -76,6 +78,10 @@ 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, ""},
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},
79 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},
...@@ -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 */
...@@ -116,7 +123,10 @@ int fast_mode = 0; /* Fast operation mode. (--fast) */ ...@@ -116,7 +123,10 @@ 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;
...@@ -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 {
......