Commit 7dcee33a 7dcee33a0ee8923108dbdfa024d1c7bc1e7d2800 by Sergey Poznyakoff

mh: improve mhn store mode.

* mh/mhn.c (options): Add --build as an alias to --compose.
(options): Likewise.
(store_destination): New enum.
(mhn_store_command): Support all variants of mhn-store-* components.
Return store_destination type.
(store_handler): Rewrite accordingly.
(main): Consistently use mh_expand_name to expand the value of
input_file.
* mh/tests/mhn.at: New file.
* mh/tests/Makefile.am (TESTSUITE_AT): Add mhn.at.
* mh/tests/testsuite.at: Include mhn.at.
1 parent 2ad19f56
...@@ -39,7 +39,11 @@ static struct argp_option options[] = { ...@@ -39,7 +39,11 @@ static struct argp_option options[] = {
39 {N_("MIME editing options"), 0, NULL, OPTION_DOC, NULL, GRID}, 39 {N_("MIME editing options"), 0, NULL, OPTION_DOC, NULL, GRID},
40 {"compose", ARG_COMPOSE, N_("BOOL"), OPTION_ARG_OPTIONAL, 40 {"compose", ARG_COMPOSE, N_("BOOL"), OPTION_ARG_OPTIONAL,
41 N_("compose the MIME message (default)"), GRID+1}, 41 N_("compose the MIME message (default)"), GRID+1},
42 {"nocompose", ARG_NOCOMPOSE, NULL, OPTION_HIDDEN, "", GRID+1}, 42 {"build", 0, NULL, OPTION_ALIAS,
43 NULL, GRID+1 },
44 {"nocompose", ARG_NOCOMPOSE, NULL, OPTION_HIDDEN, NULL, GRID+1},
45 {"nobuild", ARG_NOCOMPOSE, NULL, OPTION_HIDDEN|OPTION_ALIAS,
46 NULL, GRID+1},
43 #undef GRID 47 #undef GRID
44 #define GRID 20 48 #define GRID 20
45 {N_("Listing options"), 0, NULL, OPTION_DOC, NULL, GRID}, 49 {N_("Listing options"), 0, NULL, OPTION_DOC, NULL, GRID},
...@@ -97,6 +101,7 @@ static struct argp_option options[] = { ...@@ -97,6 +101,7 @@ static struct argp_option options[] = {
97 struct mh_option mh_option[] = { 101 struct mh_option mh_option[] = {
98 { "file", MH_OPT_ARG, "filename" }, 102 { "file", MH_OPT_ARG, "filename" },
99 { "compose" }, 103 { "compose" },
104 { "build" },
100 { "list", MH_OPT_BOOL }, 105 { "list", MH_OPT_BOOL },
101 { "headers", MH_OPT_BOOL }, 106 { "headers", MH_OPT_BOOL },
102 { "realsize", MH_OPT_BOOL }, 107 { "realsize", MH_OPT_BOOL },
...@@ -801,18 +806,71 @@ mhn_show_command (mu_message_t msg, msg_part_t part, int *flags, ...@@ -801,18 +806,71 @@ mhn_show_command (mu_message_t msg, msg_part_t part, int *flags,
801 return (char*) str; 806 return (char*) str;
802 } 807 }
803 808
804 char * 809 enum store_destination
805 mhn_store_command (mu_message_t msg, msg_part_t part, char *name) 810 {
811 store_to_folder,
812 store_to_folder_msg,
813 store_to_file,
814 store_to_command,
815 store_to_stdout
816 };
817
818 enum store_destination
819 mhn_store_command (mu_message_t msg, msg_part_t part, const char *name,
820 char **return_string)
806 { 821 {
807 const char *p, *str, *tmp; 822 const char *p, *str, *tmp;
808 char *typestr, *type, *subtype, *typeargs; 823 char *typestr, *type, *subtype, *typeargs;
809 struct obstack stk; 824 struct obstack stk;
810 mu_header_t hdr; 825 mu_header_t hdr;
826 enum store_destination dest;
811 827
812 mu_message_get_header (msg, &hdr); 828 mu_message_get_header (msg, &hdr);
813 _get_content_type (hdr, &typestr, &typeargs); 829 _get_content_type (hdr, &typestr, &typeargs);
814 split_content (typestr, &type, &subtype); 830 split_content (typestr, &type, &subtype);
815 str = _mhn_profile_get ("store", type, subtype, "%m%P.%s"); 831 str = _mhn_profile_get ("store", type, subtype, NULL);
832
833 if (!str)
834 {
835 /* FIXME:
836 [If the mhn-store component] isn't found, mhn will check to see if
837 the content is application/octet-stream with parameter "type=tar".
838 If so, mhn will choose an appropriate filename.
839 */
840
841 if (mu_c_strcasecmp (type, "message") == 0)
842 {
843 /* If the content is not application/octet-stream, then mhn
844 will check to see if the content is a message. If so, mhn
845 will use the value "+". */
846 *return_string = xstrdup (mh_current_folder ());
847 return store_to_folder_msg;
848 }
849 else
850 str = "%m%P.%s";
851 }
852
853 switch (str[0])
854 {
855 case '+':
856 if (str[1])
857 *return_string = xstrdup (str);
858 else
859 *return_string = xstrdup (mh_current_folder ());
860 return store_to_folder;
861
862 case '-':
863 *return_string = NULL;
864 return store_to_stdout;
865
866 case '|':
867 dest = store_to_command;
868 str = mu_str_skip_class (str + 1, MU_CTYPE_SPACE);
869 break;
870
871 default:
872 dest = store_to_file;
873 }
816 874
817 /* Expand macro-notations: 875 /* Expand macro-notations:
818 %m message number 876 %m message number
...@@ -880,12 +938,12 @@ mhn_store_command (mu_message_t msg, msg_part_t part, char *name) ...@@ -880,12 +938,12 @@ mhn_store_command (mu_message_t msg, msg_part_t part, char *name)
880 str = obstack_finish (&stk); 938 str = obstack_finish (&stk);
881 p = mu_str_skip_class (str, MU_CTYPE_SPACE); 939 p = mu_str_skip_class (str, MU_CTYPE_SPACE);
882 if (!*p) 940 if (!*p)
883 str = NULL; 941 *return_string = NULL;
884 else 942 else
885 str = xstrdup (p); 943 *return_string = xstrdup (p);
886 944
887 obstack_free (&stk, NULL); 945 obstack_free (&stk, NULL);
888 return (char*) str; 946 return dest;
889 } 947 }
890 948
891 949
...@@ -1539,13 +1597,14 @@ int ...@@ -1539,13 +1597,14 @@ int
1539 store_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding, 1597 store_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding,
1540 void *data) 1598 void *data)
1541 { 1599 {
1542 char *prefix = data; 1600 const char *prefix = data;
1543 char *name = NULL; 1601 char *name = NULL;
1544 char *tmp; 1602 char *partstr;
1545 int ismime; 1603 int ismime;
1546 int rc; 1604 int rc;
1547 mu_stream_t out; 1605 mu_stream_t out;
1548 const char *dir = mh_global_profile_get ("mhn-storage", NULL); 1606 const char *dir = mh_global_profile_get ("mhn-storage", NULL);
1607 enum store_destination dest = store_to_file;
1549 1608
1550 if (mu_message_is_multipart (msg, &ismime) == 0 && ismime) 1609 if (mu_message_is_multipart (msg, &ismime) == 0 && ismime)
1551 return 0; 1610 return 0;
...@@ -1559,6 +1618,7 @@ store_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding, ...@@ -1559,6 +1618,7 @@ store_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding,
1559 { 1618 {
1560 name = normalize_path (dir, val); 1619 name = normalize_path (dir, val);
1561 free (val); 1620 free (val);
1621 dest = store_to_file;
1562 } 1622 }
1563 else if (rc != MU_ERR_NOENT) 1623 else if (rc != MU_ERR_NOENT)
1564 { 1624 {
...@@ -1571,57 +1631,126 @@ store_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding, ...@@ -1571,57 +1631,126 @@ store_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding,
1571 } 1631 }
1572 1632
1573 if (!name) 1633 if (!name)
1574 { 1634 dest = mhn_store_command (msg, part, prefix, &name);
1575 char *fname = mhn_store_command (msg, part, prefix); 1635
1576 if (dir) 1636 partstr = msg_part_format (part);
1577 name = mh_safe_make_file_name (dir, fname); 1637 /* Set prefix for diagnostic purposes */
1578 else 1638 if (!prefix)
1579 name = fname; 1639 prefix = mu_umaxtostr (0, msg_part_subpart (part, 0));
1580 } 1640 out = NULL;
1581 1641 rc = 0;
1582 tmp = msg_part_format (part); 1642 switch (dest)
1583 if (prefix) 1643 {
1584 printf (_("storing message %s part %s as file %s\n"), 1644 case store_to_folder_msg:
1585 prefix, 1645 case store_to_folder:
1586 tmp, 1646 {
1587 name); 1647 mu_mailbox_t mbox = mh_open_folder (name, 1);
1588 else 1648 size_t uid;
1589 printf (_("storing message %s part %s as file %s\n"), 1649
1590 mu_umaxtostr (0, msg_part_subpart (part, 0)), 1650 mu_mailbox_uidnext (mbox, &uid);
1591 tmp, 1651 printf (_("storing message %s part %s to folder %s as message %lu\n"),
1592 name); 1652 prefix, partstr, name, (unsigned long) uid);
1593 free (tmp); 1653 if (dest == store_to_folder_msg)
1594 1654 {
1595 if (!(mode_options & OPT_QUIET) && access (name, R_OK) == 0) 1655 mu_body_t body;
1596 { 1656 mu_stream_t str;
1597 char *p; 1657 mu_message_t tmpmsg;
1598 int rc; 1658
1659 rc = mu_message_get_body (msg, &body);
1660 if (rc)
1661 {
1662 mu_diag_funcall (MU_DIAG_ERROR, "mu_message_get_body",
1663 NULL, rc);
1664 break;
1665 }
1666 rc = mu_body_get_streamref (body, &str);
1667 if (rc)
1668 {
1669 mu_diag_funcall (MU_DIAG_ERROR, "mu_body_get_stream",
1670 NULL, rc);
1671 break;
1672 }
1673 rc = mu_stream_to_message (str, &tmpmsg);
1674 mu_stream_unref (str);
1675 if (rc)
1676 {
1677 mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_to_message",
1678 NULL, rc);
1679 break;
1680 }
1681
1682 rc = mu_mailbox_append_message (mbox, tmpmsg);
1683 mu_message_destroy (&tmpmsg, mu_message_get_owner (tmpmsg));
1684 }
1685 else
1686 rc = mu_mailbox_append_message (mbox, msg);
1599 1687
1600 mu_asprintf (&p, _("File %s already exists. Rewrite"), name); 1688 if (rc)
1601 rc = mh_getyn (p); 1689 mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_append_message", NULL,
1602 free (p); 1690 rc);
1603 if (!rc) 1691 mu_mailbox_close (mbox);
1692 mu_mailbox_destroy (&mbox);
1693 }
1694 break;
1695
1696 case store_to_file:
1697 printf (_("storing message %s part %s as file %s\n"),
1698 prefix, partstr, name);
1699
1700 if (!(mode_options & OPT_QUIET) && access (name, R_OK) == 0)
1604 { 1701 {
1605 free (name); 1702 char *p;
1606 return 0; 1703 int ok;
1704
1705 mu_asprintf (&p, _("File %s already exists. Rewrite"), name);
1706 ok = mh_getyn (p);
1707 free (p);
1708 if (!ok)
1709 {
1710 free (name);
1711 return 0;
1712 }
1713 unlink (name);
1607 } 1714 }
1608 unlink (name);
1609 }
1610 1715
1611 rc = mu_file_stream_create (&out, name, MU_STREAM_WRITE|MU_STREAM_CREAT); 1716 rc = mu_file_stream_create (&out, name, MU_STREAM_WRITE|MU_STREAM_CREAT);
1612 if (rc) 1717 if (rc)
1718 mu_error (_("cannot create output stream (file %s): %s"),
1719 name, mu_strerror (rc));
1720 break;
1721
1722 case store_to_command:
1723 /* FIXME: Change to homedir, reflect this in the message below.
1724 Chdir should better be implemented within mu_prog_stream_create
1725 Example message:
1726 storing msg 4 part 1 using command (cd /home/gray; less)
1727 */
1728 printf (_("storing msg %s part %s using command %s\n"),
1729 prefix, partstr, name);
1730 rc = mu_prog_stream_create (&out, name, MU_STREAM_WRITE);
1731 if (rc)
1732 mu_diag_funcall (MU_DIAG_ERROR, "mu_prog_stream_create", NULL, rc);
1733 break;
1734
1735 case store_to_stdout:
1736 printf (_("storing msg %s part %s to stdout\n"),
1737 prefix, partstr);
1738 rc = mu_stdio_stream_create (&out, MU_STDOUT_FD, 0);
1739 if (rc)
1740 mu_diag_funcall (MU_DIAG_ERROR, "mu_stdio_stream_create", NULL, rc);
1741 break;
1742 }
1743
1744 if (out)
1613 { 1745 {
1614 mu_error (_("cannot create output stream (file %s): %s"), 1746 show_internal (msg, part, encoding, out);
1615 name, mu_strerror (rc)); 1747 mu_stream_destroy (&out);
1616 free (name);
1617 return rc;
1618 } 1748 }
1619 show_internal (msg, part, encoding, out); 1749
1620
1621 mu_stream_destroy (&out);
1622 free (name); 1750 free (name);
1751 free (partstr);
1623 1752
1624 return 0; 1753 return rc;
1625 } 1754 }
1626 1755
1627 void 1756 void
...@@ -2663,7 +2792,9 @@ main (int argc, char **argv) ...@@ -2663,7 +2792,9 @@ main (int argc, char **argv)
2663 mu_error (_("extra arguments")); 2792 mu_error (_("extra arguments"));
2664 return 1; 2793 return 1;
2665 } 2794 }
2666 message = mh_file_to_message (mu_folder_directory (), input_file); 2795 input_file = mh_expand_name (mu_folder_directory (),
2796 argc == 1 ? argv[0] : "draft", 0);
2797 message = mh_file_to_message (NULL, input_file);
2667 if (!message) 2798 if (!message)
2668 return 1; 2799 return 1;
2669 } 2800 }
...@@ -2674,8 +2805,9 @@ main (int argc, char **argv) ...@@ -2674,8 +2805,9 @@ main (int argc, char **argv)
2674 mu_error (_("extra arguments")); 2805 mu_error (_("extra arguments"));
2675 return 1; 2806 return 1;
2676 } 2807 }
2677 input_file = argc == 1 ? argv[0] : "draft"; 2808 input_file = mh_expand_name (mu_folder_directory (),
2678 message = mh_file_to_message (mu_folder_directory (), input_file); 2809 argc == 1 ? argv[0] : "draft", 0);
2810 message = mh_file_to_message (NULL, input_file);
2679 if (!message) 2811 if (!message)
2680 return 1; 2812 return 1;
2681 } 2813 }
...@@ -2688,9 +2820,6 @@ main (int argc, char **argv) ...@@ -2688,9 +2820,6 @@ main (int argc, char **argv)
2688 switch (mode) 2820 switch (mode)
2689 { 2821 {
2690 case mode_compose: 2822 case mode_compose:
2691 /* Prepare filename for diagnostic purposes */
2692 if (input_file[0] != '/')
2693 input_file = mh_safe_make_file_name (mu_folder_directory (), input_file);
2694 rc = mhn_compose (); 2823 rc = mhn_compose ();
2695 break; 2824 break;
2696 2825
......
...@@ -49,6 +49,7 @@ TESTSUITE_AT = \ ...@@ -49,6 +49,7 @@ TESTSUITE_AT = \
49 install-mh.at\ 49 install-mh.at\
50 mark.at\ 50 mark.at\
51 mhl.at\ 51 mhl.at\
52 mhn.at\
52 mhparam.at\ 53 mhparam.at\
53 mhpath.at\ 54 mhpath.at\
54 mhseq.at\ 55 mhseq.at\
......
1 # This file is part of GNU Mailutils. -*- Autotest -*-
2 # Copyright (C) 2010 Free Software Foundation, Inc.
3 #
4 # GNU Mailutils is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3, or (at
7 # your option) any later version.
8 #
9 # GNU Mailutils is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
16
17 m4_pushdef([MH_KEYWORDS],[mhn])
18
19 dnl -------------------------------------------------------------------
20 dnl 1. List mode
21 dnl -------------------------------------------------------------------
22
23 MH_CHECK([mhn -list],[mhn00 mhn-list],[
24 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
25 mhn +inbox -list all
26 ],
27 [0],
28 [ msg part type/subtype size description
29 1 text/plain 937
30 2 text/plain 215
31 3 multipart/mixed 2K
32 1 text/plain 230 How doth
33 2 application/octet-stream 462 Father William Part I
34 4 multipart/mixed 3K
35 1 text/plain 341 Father William Part I
36 2 multipart/mixed 3K
37 2.1 application/octet-stream 479 Father William Part II
38 2.2 multipart/mixed 2K
39 2.2.1 application/octet-stream 483 Father William Part III
40 2.2.2 application/octet-stream 495 Father William Part IV
41 5 multipart/mixed 355
42 1 text/plain 0 Empty part
43 2 text/plain 1 Single line part
44 ])
45
46 MH_CHECK([mhn -list -realsize],[mhn01 mhn-list-realsize],[
47 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
48 mhn +inbox -list -realsize all
49 ],
50 [0],
51 [ msg part type/subtype size description
52 1 text/plain 937
53 2 text/plain 215
54 3 multipart/mixed 2K
55 1 text/plain 230 How doth
56 2 application/octet-stream 341 Father William Part I
57 4 multipart/mixed 3K
58 1 text/plain 341 Father William Part I
59 2 multipart/mixed 3K
60 2.1 application/octet-stream 352 Father William Part II
61 2.2 multipart/mixed 2K
62 2.2.1 application/octet-stream 357 Father William Part III
63 2.2.2 application/octet-stream 366 Father William Part IV
64 5 multipart/mixed 355
65 1 text/plain 0 Empty part
66 2 text/plain 1 Single line part
67 ])
68
69 dnl -------------------------------------------------------------------
70 dnl 2. Store mode
71 dnl -------------------------------------------------------------------
72
73 MH_CHECK([mhn -store],[mhn02 mhn-store],[
74 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
75 mhn +inbox -store 4 || exit $?
76 for file in 4.1.plain 4.2.1.octet-stream 4.2.2.1.octet-stream 4.2.2.2.octet-stream
77 do
78 echo == $file ==
79 cat $file
80 done
81 ],
82 [0],
83 [storing message 4 part 1 as file 4.1.plain
84 storing message 4 part 2.1 as file 4.2.1.octet-stream
85 storing message 4 part 2.2.1 as file 4.2.2.1.octet-stream
86 storing message 4 part 2.2.2 as file 4.2.2.2.octet-stream
87 == 4.1.plain ==
88 `You are old, Father William,' the young man said,
89 `And your hair has become very white;
90 And yet you incessantly stand on your head--
91 Do you think, at your age, it is right?'
92
93 `In my youth,' Father William replied to his son,
94 `I feared it might injure the brain;
95 But, now that I'm perfectly sure I have none,
96 Why, I do it again and again.'
97
98 == 4.2.1.octet-stream ==
99 `You are old,' said the youth, `as I mentioned before,
100 And have grown most uncommonly fat;
101 Yet you turned a back-somersault in at the door--
102 Pray, what is the reason of that?'
103
104 `In my youth,' said the sage, as he shook his grey locks,
105 `I kept all my limbs very supple
106 By the use of this ointment--one shilling the box--
107 Allow me to sell you a couple?'
108 == 4.2.2.1.octet-stream ==
109 `You are old,' said the youth, `and your jaws are too weak
110 For anything tougher than suet;
111 Yet you finished the goose, with the bones and the beak--
112 Pray how did you manage to do it?'
113
114 `In my youth,' said his father, `I took to the law,
115 And argued each case with my wife;
116 And the muscular strength, which it gave to my jaw,
117 Has lasted the rest of my life.'
118 == 4.2.2.2.octet-stream ==
119 `You are old,' said the youth, `one would hardly suppose
120 That your eye was as steady as ever;
121 Yet you balanced an eel on the end of your nose--
122 What made you so awfully clever?'
123
124 `I have answered three questions, and that is enough,'
125 Said his father; `don't give yourself airs!
126 Do you think I can listen all day to such stuff?
127 Be off, or I'll kick you down stairs!'
128 ])
129
130 m4_popdef[MH_KEYWORDS])
131 # End of mhn.at
...@@ -63,3 +63,4 @@ m4_include([burst.at]) ...@@ -63,3 +63,4 @@ m4_include([burst.at])
63 m4_include([comp.at]) 63 m4_include([comp.at])
64 m4_include([forw.at]) 64 m4_include([forw.at])
65 m4_include([repl.at]) 65 m4_include([repl.at])
66 m4_include([mhn.at])
......