Commit dcf9a434 dcf9a434657b6459a8078842ef63c45695866094 by Sergey Poznyakoff

mhn: compatibility fixes in show mode.

* mh/mhn.c (OPT_PAUSE): Remove define.
(pause_option): New variable.
(opt_handler): -pause/-nopause sets pause_option.
If stdin is not a tty, set pause_option to 0.
(check_type): New function.
(mhn_show_command): If mhn-show-<type>[/<subtype>] is not set,
use %pmoreproc '%F' for text/plain and message/rfc822 parts.
If content type is application/octet-stream and type=tar, use
tar to list the archive.
Otherwise, complain and return NULL.
(MHN_CONFIRM): Rename to MHN_PAUSE.
(mhn_pause): New function.
(show_handler): The value of pause_option controls the %p escape.
Rewrite the MHN_PAUSE handling to avoid the use of the fixed-size
buffer.  Implement C-c handling, as in other mhns.
If mhn_show_command returns NULL, do not attempt to show the content.
Remove dubious usage of mu_transport_t.
(mhn_store_command): handle application/octet-stream with type=tar.
* mh/tests/mhn.at: Add a test for show mode.
1 parent 1ae763d4
...@@ -27,7 +27,7 @@ State Nice Utility Comments ...@@ -27,7 +27,7 @@ State Nice Utility Comments
27 * -20 refile --link copies messages 27 * -20 refile --link copies messages
28 + -20 rmm 28 + -20 rmm
29 + -15 folder(s) 29 + -15 folder(s)
30 + -15 mhn 30 * -15 mhn -[rw]?cache, -type
31 + -10 rmf 31 + -10 rmf
32 + 0 install-mh 32 + 0 install-mh
33 * 4 mhl Following format variables are ignored: 33 * 4 mhl Following format variables are ignored:
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
23 #define obstack_chunk_alloc xmalloc 23 #define obstack_chunk_alloc xmalloc
24 #define obstack_chunk_free free 24 #define obstack_chunk_free free
25 #include <obstack.h> 25 #include <obstack.h>
26 #include <setjmp.h>
26 27
27 static char doc[] = N_("GNU MH mhn")"\v" 28 static char doc[] = N_("GNU MH mhn")"\v"
28 N_("Options marked with `*' are not yet implemented.\n\ 29 N_("Options marked with `*' are not yet implemented.\n\
...@@ -141,13 +142,13 @@ static enum mhn_mode mode = mode_compose; ...@@ -141,13 +142,13 @@ static enum mhn_mode mode = mode_compose;
141 142
142 #define OPT_HEADERS 001 143 #define OPT_HEADERS 001
143 #define OPT_REALSIZE 002 144 #define OPT_REALSIZE 002
144 #define OPT_PAUSE 004 145 #define OPT_AUTO 004
145 #define OPT_AUTO 010 146 #define OPT_SERIALONLY 010
146 #define OPT_SERIALONLY 020 147 #define OPT_VERBOSE 020
147 #define OPT_VERBOSE 040 148 #define OPT_QUIET 040
148 #define OPT_QUIET 100
149 149
150 static int mode_options = OPT_HEADERS; 150 static int mode_options = OPT_HEADERS;
151 static int pause_option = -1; /* -pause option. -1 means "not given" */
151 static char *formfile; 152 static char *formfile;
152 static char *content_type; 153 static char *content_type;
153 static char *content_subtype; 154 static char *content_subtype;
...@@ -165,7 +166,7 @@ static msg_part_t req_part; ...@@ -165,7 +166,7 @@ static msg_part_t req_part;
165 #define MHN_EXCLUSIVE_EXEC 001 166 #define MHN_EXCLUSIVE_EXEC 001
166 #define MHN_STDIN 002 167 #define MHN_STDIN 002
167 #define MHN_LISTING 004 168 #define MHN_LISTING 004
168 #define MHN_CONFIRM 010 169 #define MHN_PAUSE 010
169 170
170 void 171 void
171 sfree (char **ptr) 172 sfree (char **ptr)
...@@ -199,6 +200,30 @@ split_content (const char *content, char **type, char **subtype) ...@@ -199,6 +200,30 @@ split_content (const char *content, char **type, char **subtype)
199 } 200 }
200 } 201 }
201 202
203 static void
204 split_args (const char *argstr, size_t len, int *pargc, char ***pargv)
205 {
206 struct mu_wordsplit ws;
207
208 ws.ws_delim = ";";
209 if (mu_wordsplit_len (argstr, len, &ws,
210 MU_WRDSF_DEFFLAGS | MU_WRDSF_DELIM | MU_WRDSF_WS))
211 {
212 mu_error (_("cannot split line `%s': %s"), argstr,
213 mu_wordsplit_strerror (&ws));
214 *pargc = 0;
215 *pargv = NULL;
216 }
217 else
218 {
219 *pargc = ws.ws_wordc;
220 *pargv = ws.ws_wordv;
221 ws.ws_wordc = 0;
222 ws.ws_wordv = NULL;
223 mu_wordsplit_free (&ws);
224 }
225 }
226
202 int 227 int
203 _get_content_type (mu_header_t hdr, char **value, char **rest) 228 _get_content_type (mu_header_t hdr, char **value, char **rest)
204 { 229 {
...@@ -340,14 +365,11 @@ opt_handler (int key, char *arg, struct argp_state *state) ...@@ -340,14 +365,11 @@ opt_handler (int key, char *arg, struct argp_state *state)
340 break; 365 break;
341 366
342 case ARG_PAUSE: 367 case ARG_PAUSE:
343 if (is_true (arg)) 368 pause_option = is_true (arg);
344 {
345 mode_options |= OPT_PAUSE;
346 break; 369 break;
347 } 370
348 /*FALLTHRU*/
349 case ARG_NOPAUSE: 371 case ARG_NOPAUSE:
350 mode_options &= ~OPT_PAUSE; 372 pause_option = 0;
351 break; 373 break;
352 374
353 /* Store options */ 375 /* Store options */
...@@ -399,6 +421,8 @@ opt_handler (int key, char *arg, struct argp_state *state) ...@@ -399,6 +421,8 @@ opt_handler (int key, char *arg, struct argp_state *state)
399 case ARGP_KEY_FINI: 421 case ARGP_KEY_FINI:
400 if (!formfile) 422 if (!formfile)
401 mh_find_file ("mhl.headers", &formfile); 423 mh_find_file ("mhl.headers", &formfile);
424 if (!isatty (0))
425 pause_option = 0;
402 break; 426 break;
403 427
404 default: 428 default:
...@@ -695,6 +719,32 @@ mhn_tempfile_name (char **tempfile, const char *type, const char *subtype) ...@@ -695,6 +719,32 @@ mhn_tempfile_name (char **tempfile, const char *type, const char *subtype)
695 } 719 }
696 } 720 }
697 721
722 static int
723 check_type (const char *typeargs, const char *typeval)
724 {
725 int rc = 1;
726
727 if (typeargs)
728 {
729 int i, argc;
730 char **argv;
731
732 split_args (typeargs, strlen (typeargs), &argc, &argv);
733 for (i = 0; i < argc; i++)
734 {
735 if (strlen (argv[i]) > 5
736 && memcmp (argv[i], "type=", 5) == 0
737 && mu_c_strcasecmp (argv[i] + 5, typeval) == 0)
738 {
739 rc = 0;
740 break;
741 }
742 }
743 mu_argcv_free (argc, argv);
744 }
745 return rc;
746 }
747
698 char * 748 char *
699 mhn_show_command (mu_message_t msg, msg_part_t part, int *flags, 749 mhn_show_command (mu_message_t msg, msg_part_t part, int *flags,
700 char **tempfile) 750 char **tempfile)
...@@ -703,13 +753,51 @@ mhn_show_command (mu_message_t msg, msg_part_t part, int *flags, ...@@ -703,13 +753,51 @@ mhn_show_command (mu_message_t msg, msg_part_t part, int *flags,
703 char *typestr, *type, *subtype, *typeargs; 753 char *typestr, *type, *subtype, *typeargs;
704 struct obstack stk; 754 struct obstack stk;
705 mu_header_t hdr; 755 mu_header_t hdr;
756 char *temp_cmd = NULL;
706 757
707 mu_message_get_header (msg, &hdr); 758 mu_message_get_header (msg, &hdr);
708 _get_content_type (hdr, &typestr, &typeargs); 759 _get_content_type (hdr, &typestr, &typeargs);
709 split_content (typestr, &type, &subtype); 760 split_content (typestr, &type, &subtype);
710 str = _mhn_profile_get ("show", type, subtype, NULL); 761 str = _mhn_profile_get ("show", type, subtype, NULL);
711 if (!str) /* FIXME */ 762 if (!str)
763 {
764 if (mu_c_strcasecmp (typestr, "text/plain") == 0
765 /* FIXME: The following one should produce
766 %pshow -file '%F' */
767 || mu_c_strcasecmp (typestr, "message/rfc822") == 0)
768 {
769 int rc;
770 const char *moreproc = mh_global_profile_get ("moreproc",
771 getenv ("PAGER"));
772 if (!moreproc)
773 return NULL;
774 rc = mu_asprintf (&temp_cmd, "%%p%s '%%F'", moreproc);
775 if (rc)
776 {
777 mu_diag_funcall (MU_DIAG_ERROR, "mu_asprintf", NULL, rc);
778 return NULL;
779 }
780 str = temp_cmd;
781 }
782 else if (mu_c_strcasecmp (typestr, "application/octet-stream") == 0 &&
783 check_type (typeargs, "tar") == 0)
784 /* Use temporary file to get tar a chance to select appropriate
785 decompressor, if the archive is compressed. */
786 str = "tar tvf '%F'";
787 else
788 {
789 char *parts = msg_part_format (part);
790
791 /* FIXME: This message should in fact occupy two lines (exactly
792 as split below), but mu_error malfunctions if a \n appears
793 within the format string */
794 mu_error (_("don't know how to display content"
795 " (content %s in message %lu, part %s)"),
796 typestr, (unsigned long)part->part[0], parts);
797 free (parts);
712 return NULL; 798 return NULL;
799 }
800 }
713 801
714 /* Expand macro-notations: 802 /* Expand macro-notations:
715 %a additional arguments 803 %a additional arguments
...@@ -765,7 +853,7 @@ mhn_show_command (mu_message_t msg, msg_part_t part, int *flags, ...@@ -765,7 +853,7 @@ mhn_show_command (mu_message_t msg, msg_part_t part, int *flags,
765 853
766 case 'p': 854 case 'p':
767 /* %l, and ask for confirmation */ 855 /* %l, and ask for confirmation */
768 *flags |= MHN_LISTING|MHN_CONFIRM; 856 *flags |= MHN_LISTING|MHN_PAUSE;
769 break; 857 break;
770 858
771 case 's': 859 case 's':
...@@ -793,6 +881,7 @@ mhn_show_command (mu_message_t msg, msg_part_t part, int *flags, ...@@ -793,6 +881,7 @@ mhn_show_command (mu_message_t msg, msg_part_t part, int *flags,
793 free (typestr); 881 free (typestr);
794 free (type); 882 free (type);
795 free (subtype); 883 free (subtype);
884 free (temp_cmd);
796 885
797 str = obstack_finish (&stk); 886 str = obstack_finish (&stk);
798 p = mu_str_skip_class (str, MU_CTYPE_SPACE); 887 p = mu_str_skip_class (str, MU_CTYPE_SPACE);
...@@ -831,12 +920,6 @@ mhn_store_command (mu_message_t msg, msg_part_t part, const char *name, ...@@ -831,12 +920,6 @@ mhn_store_command (mu_message_t msg, msg_part_t part, const char *name,
831 920
832 if (!str) 921 if (!str)
833 { 922 {
834 /* FIXME:
835 [If the mhn-store component] isn't found, mhn will check to see if
836 the content is application/octet-stream with parameter "type=tar".
837 If so, mhn will choose an appropriate filename.
838 */
839
840 if (mu_c_strcasecmp (type, "message") == 0) 923 if (mu_c_strcasecmp (type, "message") == 0)
841 { 924 {
842 /* If the content is not application/octet-stream, then mhn 925 /* If the content is not application/octet-stream, then mhn
...@@ -845,6 +928,12 @@ mhn_store_command (mu_message_t msg, msg_part_t part, const char *name, ...@@ -845,6 +928,12 @@ mhn_store_command (mu_message_t msg, msg_part_t part, const char *name,
845 *return_string = xstrdup (mh_current_folder ()); 928 *return_string = xstrdup (mh_current_folder ());
846 return store_to_folder_msg; 929 return store_to_folder_msg;
847 } 930 }
931 else if (mu_c_strcasecmp (typestr, "application/octet-stream") == 0 &&
932 check_type (typeargs, "tar") == 0)
933 /* [If the mhn-store component] isn't found, mhn will check to see if
934 the content is application/octet-stream with parameter "type=tar".
935 If so, mhn will choose an appropriate filename. */
936 str = "%m%P.tar";
848 else 937 else
849 str = "%m%P.%s"; 938 str = "%m%P.%s";
850 } 939 }
...@@ -948,30 +1037,6 @@ mhn_store_command (mu_message_t msg, msg_part_t part, const char *name, ...@@ -948,30 +1037,6 @@ mhn_store_command (mu_message_t msg, msg_part_t part, const char *name,
948 1037
949 /* ************************* Auxiliary functions ************************** */ 1038 /* ************************* Auxiliary functions ************************** */
950 1039
951 static void
952 split_args (const char *argstr, size_t len, int *pargc, char ***pargv)
953 {
954 struct mu_wordsplit ws;
955
956 ws.ws_delim = ";";
957 if (mu_wordsplit_len (argstr, len, &ws,
958 MU_WRDSF_DEFFLAGS | MU_WRDSF_DELIM | MU_WRDSF_WS))
959 {
960 mu_error (_("cannot split line `%s': %s"), argstr,
961 mu_wordsplit_strerror (&ws));
962 *pargc = 0;
963 *pargv = NULL;
964 }
965 else
966 {
967 *pargc = ws.ws_wordc;
968 *pargv = ws.ws_wordv;
969 ws.ws_wordc = 0;
970 ws.ws_wordv = NULL;
971 mu_wordsplit_free (&ws);
972 }
973 }
974
975 int 1040 int
976 _message_is_external_body (mu_message_t msg, char ***env) 1041 _message_is_external_body (mu_message_t msg, char ***env)
977 { 1042 {
...@@ -1310,7 +1375,8 @@ mhn_list () ...@@ -1310,7 +1375,8 @@ mhn_list ()
1310 static mu_list_t mhl_format; 1375 static mu_list_t mhl_format;
1311 1376
1312 int 1377 int
1313 show_internal (mu_message_t msg, msg_part_t part, char *encoding, mu_stream_t out) 1378 show_internal (mu_message_t msg, msg_part_t part, char *encoding,
1379 mu_stream_t out)
1314 { 1380 {
1315 int rc; 1381 int rc;
1316 mu_body_t body = NULL; 1382 mu_body_t body = NULL;
...@@ -1407,6 +1473,41 @@ mhn_run_command (mu_message_t msg, msg_part_t part, ...@@ -1407,6 +1473,41 @@ mhn_run_command (mu_message_t msg, msg_part_t part,
1407 return rc; 1473 return rc;
1408 } 1474 }
1409 1475
1476 static RETSIGTYPE
1477 sigint (int sig)
1478 {
1479 /* nothing */
1480 }
1481
1482 static int
1483 mhn_pause ()
1484 {
1485 int c;
1486 int rc = 0;
1487 RETSIGTYPE (*saved_sig) (int) = signal (SIGINT, sigint);
1488 printf (_("Press <return> to show content..."));
1489 fflush (stdout);
1490 do
1491 {
1492 fd_set set;
1493 int res;
1494
1495 FD_ZERO (&set);
1496 FD_SET (0, &set);
1497 res = select (1, &set, NULL, NULL, NULL);
1498 if (res != 1
1499 || read (0, &c, 1) != 1)
1500 {
1501 putchar ('\n');
1502 rc = 1;
1503 break;
1504 }
1505 }
1506 while (c != '\n');
1507 signal (SIGINT, saved_sig);
1508 return rc;
1509 }
1510
1410 int 1511 int
1411 show_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding, 1512 show_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding,
1412 void *data) 1513 void *data)
...@@ -1414,65 +1515,45 @@ show_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding, ...@@ -1414,65 +1515,45 @@ show_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding,
1414 mu_stream_t out = data; 1515 mu_stream_t out = data;
1415 char *cmd; 1516 char *cmd;
1416 int flags = 0; 1517 int flags = 0;
1417 int fd = 1;
1418 char *tempfile = NULL; 1518 char *tempfile = NULL;
1419 int ismime; 1519 int ismime;
1420 mu_transport_t trans[2];
1421 1520
1422 if (mu_message_is_multipart (msg, &ismime) == 0 && ismime) 1521 if (mu_message_is_multipart (msg, &ismime) == 0 && ismime)
1423 return 0; 1522 return 0;
1424 1523
1425 mu_stream_ioctl (out, MU_IOCTL_GET_TRANSPORT, trans);
1426 fd = (int) trans[0]; /* FIXME */
1427
1428 if (mode_options & OPT_PAUSE)
1429 flags |= MHN_CONFIRM;
1430 cmd = mhn_show_command (msg, part, &flags, &tempfile); 1524 cmd = mhn_show_command (msg, part, &flags, &tempfile);
1431 if (!cmd) 1525 if (!cmd)
1432 flags |= MHN_LISTING; 1526 return 0;
1527 if (pause_option == 0)
1528 flags &= ~MHN_PAUSE;
1529 else if (pause_option > 0)
1530 flags |= MHN_PAUSE;
1531
1433 if (flags & MHN_LISTING) 1532 if (flags & MHN_LISTING)
1434 { 1533 {
1435 char *str; 1534 mu_header_t hdr = NULL;
1436 const char *p; 1535 char *parts;
1536 const char *descr;
1437 mu_off_t size = 0; 1537 mu_off_t size = 0;
1438 1538
1439 str = (char*) _("part ");
1440 mu_stream_write (out, str, strlen (str), NULL);
1441 str = msg_part_format (part);
1442 mu_stream_write (out, str, strlen (str), NULL);
1443 free (str);
1444 mu_stream_write (out, " ", 1, NULL);
1445 mu_stream_write (out, type, strlen (type), NULL);
1446 mhn_message_size (msg, &size); 1539 mhn_message_size (msg, &size);
1447 p = mu_umaxtostr (0, size); 1540 parts = msg_part_format (part);
1448 mu_stream_write (out, p, strlen (p), NULL); 1541 mu_stream_printf (out, _("part %5s %-24.24s %lu"), parts, type,
1542 (unsigned long) size);
1543 free (parts);
1544 if (mu_message_get_header (msg, &hdr) == 0 &&
1545 mu_header_sget_value (hdr, MU_HEADER_CONTENT_DESCRIPTION,
1546 &descr) == 0)
1547 mu_stream_printf (out, " %s", descr);
1449 mu_stream_write (out, "\n", 1, NULL); 1548 mu_stream_write (out, "\n", 1, NULL);
1450 mu_stream_flush (out); 1549 mu_stream_flush (out);
1451 } 1550 }
1452 1551
1453 if (flags & MHN_CONFIRM) 1552 if (!((flags & MHN_PAUSE) && mhn_pause ()))
1454 {
1455 if (isatty (fd) && isatty (0))
1456 {
1457 char buf[64];
1458 printf (_("Press <return> to show content..."));
1459 if (!fgets (buf, sizeof buf, stdin) || buf[0] != '\n')
1460 return 0;
1461 }
1462 }
1463
1464 if (!cmd)
1465 {
1466 const char *pager = mh_global_profile_get ("moreproc", getenv ("PAGER"));
1467 if (pager)
1468 exec_internal (msg, part, encoding, pager, 0);
1469 else
1470 show_internal (msg, part, encoding, out);
1471 }
1472 else
1473 {
1474 mhn_run_command (msg, part, encoding, cmd, flags, tempfile); 1553 mhn_run_command (msg, part, encoding, cmd, flags, tempfile);
1475 free (cmd); 1554 free (cmd);
1555 if (tempfile)
1556 {
1476 unlink (tempfile); 1557 unlink (tempfile);
1477 free (tempfile); 1558 free (tempfile);
1478 } 1559 }
......
...@@ -266,5 +266,59 @@ Why, I do it again and again.' ...@@ -266,5 +266,59 @@ Why, I do it again and again.'
266 storing msg 4 part 1 using command /home/gray/gnu/mailutils/mh/tests/mhed - 266 storing msg 4 part 1 using command /home/gray/gnu/mailutils/mh/tests/mhed -
267 ]) 267 ])
268 268
269 dnl -------------------------------------------------------------------
270 dnl 3. Show mode
271 dnl -------------------------------------------------------------------
272 MH_CHECK([mhn-show msg1],[mhn12 mhn-show-msg1],[
273 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
274 mhn -show 1 | sed /^X-IMAPbase/d
275 ],
276 [0],
277 [Date: Fri, 28 Dec 2001 22:18:08 +0200
278 To: Bar <bar@dontmailme.org>
279 From: Foo Bar <foobar@nonexistent.net>
280 Subject: Jabberwocky
281
282 X-Envelope-Date: Fri Dec 28 22:18:09 2001
283 X-Envelope-Sender: foobar@nonexistent.net
284
285 part text/plain 937
286 `Twas brillig, and the slithy toves
287 Did gyre and gimble in the wabe;
288 All mimsy were the borogoves,
289 And the mome raths outgrabe.
290
291 `Beware the Jabberwock, my son!
292 The jaws that bite, the claws that catch!
293 Beware the Jujub bird, and shun
294 The frumious Bandersnatch!'
295
296 He took his vorpal sword in hand:
297 Long time the manxome foe he sought --
298 So rested he by the Tumtum gree,
299 And stood awhile in thought.
300
301 And as in uffish thought he stood,
302 The Jabberwock, with eyes of flame,
303 Came whiffling through the tulgey wook,
304 And burbled as it came!
305
306 One, two! One, two! And through and through
307 The vorpal blade went snicker-snack!
308 He left it dead, and with its head
309 He went galumphing back.
310
311 `And has thou slain the Jabberwock?
312 Come to my arms, my beamish boy!
313 O frabjous day! Calloh! Callay!
314 He chortled in his joy.
315
316 `Twas brillig, and the slithy toves
317 Did gyre and gimble in the wabe;
318 All mimsy were the borogoves,
319 And the mome raths outgrabe.
320
321 ])
322
269 m4_popdef[MH_KEYWORDS]) 323 m4_popdef[MH_KEYWORDS])
270 # End of mhn.at 324 # End of mhn.at
......