Commit c8463abe c8463abee101ad757691e6e2c4e0e5ab25873a17 by Sergey Poznyakoff

Implementation of mhn utility

1 parent 45a1e5a5
Showing 1 changed file with 871 additions and 0 deletions
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2003 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU 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, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17
18 /* MH mhn command */
19
20 #include <mh.h>
21
22 const char *argp_program_version = "mhn (" PACKAGE_STRING ")";
23 static char doc[] = N_("GNU MH mhn\v"
24 "Options marked with `*' are not yet implemented.\n"
25 "Use -help to obtain the list of traditional MH options.");
26 static char args_doc[] = "[msgs]";
27
28 static struct argp_option options[] = {
29 {"folder", ARG_FOLDER, N_("FOLDER"), 0,
30 N_("Specify folder to operate upon"), 0},
31 {"file", ARG_FILE, N_("FILE"), 0,
32 N_("Specify file to operate upon"), 0},
33
34 {N_("MIME editing options"), 0, NULL, OPTION_DOC, NULL, 5},
35 {"compose", ARG_COMPOSE, N_("BOOL"), OPTION_ARG_OPTIONAL,
36 N_("* Compose the MIME message (default)"), 6},
37 {"nocompose", ARG_NOCOMPOSE, NULL, OPTION_HIDDEN, "", 6},
38
39 {N_("Listing options"), 0, NULL, OPTION_DOC, NULL, 0},
40 {"list", ARG_LIST, N_("BOOL"), OPTION_ARG_OPTIONAL,
41 N_("List the table of contents"), 11 },
42 {"nolist", ARG_NOLIST, NULL, OPTION_HIDDEN, "", 11 },
43 {"headers", ARG_HEADER, N_("BOOL"), OPTION_ARG_OPTIONAL,
44 N_("Print the banner above the listing"), 12},
45 {"noheaders", ARG_NOHEADERS, NULL, OPTION_HIDDEN, "", 12 },
46 {"realsize", ARG_REALSIZE, N_("BOOL"), OPTION_ARG_OPTIONAL,
47 N_("List the decoded sizes"), 12},
48 {"norealsize", ARG_NOREALSIZE, NULL, OPTION_HIDDEN, "", 12 },
49
50 {N_("Display options"), 0, NULL, OPTION_DOC, NULL, 20},
51 {"show", ARG_SHOW, N_("BOOL"), OPTION_ARG_OPTIONAL,
52 N_("Display the contents of the messages"), 21},
53 {"noshow", ARG_NOSHOW, NULL, OPTION_HIDDEN, "", 21 },
54 {"serialonly", ARG_SERIALONLY, N_("BOOL"), OPTION_ARG_OPTIONAL,
55 N_("* Display messages serially"), 22},
56 {"noserialonly", ARG_NOSERIALONLY, NULL, OPTION_HIDDEN, "", 22 },
57 {"form", ARG_FORM, N_("FILE"), 0,
58 N_("Read mhl format from FILE"), 22},
59 {"pause", ARG_PAUSE, N_("BOOL"), OPTION_ARG_OPTIONAL,
60 N_("* Pause prior to displaying content"), 22},
61 {"nopause", ARG_NOPAUSE, NULL, OPTION_HIDDEN, "", 22 },
62
63 {N_("Saving options"), 0, NULL, OPTION_DOC, NULL, 30},
64 {"store", ARG_STORE, N_("BOOL"), OPTION_ARG_OPTIONAL,
65 N_("* Store the contents of the messages on disk"), 31},
66 {"nostore", ARG_NOSTORE, NULL, OPTION_HIDDEN, "", 31 },
67 {"auto", ARG_AUTO, N_("BOOL"), OPTION_ARG_OPTIONAL,
68 N_("* Use filenames from the content headers"), 31},
69 {"noauto", ARG_NOAUTO, NULL, OPTION_HIDDEN, "", 31 },
70
71 {N_("Other options"), 0, NULL, OPTION_DOC, NULL, 40},
72 {"part", ARG_PART, N_("PART"), 0,
73 N_("Limit the scope of the operation to the given part"), 41},
74 {"type", ARG_TYPE, N_("CONTENT"), 0,
75 N_("Operate on message part with given multipart content"), 41 },
76 {"verbose", ARG_VERBOSE, N_("BOOL"), OPTION_ARG_OPTIONAL,
77 N_("Print additional information"), 41 },
78 {"noverbose", ARG_NOVERBOSE, NULL, OPTION_HIDDEN, "", 41 },
79 {NULL}
80 };
81
82 /* Traditional MH options */
83 struct mh_option mh_option[] = {
84 {"file", 2, 0, "filename"},
85 {"list", 1, MH_OPT_BOOL, NULL},
86 {"headers", 1, MH_OPT_BOOL, NULL},
87 {"realsize", 1, MH_OPT_BOOL, NULL},
88 {"show", 2, MH_OPT_BOOL, NULL},
89 {"serialonly", 2, MH_OPT_BOOL, NULL},
90 {"form", 2, 0, "formfile"},
91 {"pause", 3, MH_OPT_BOOL, NULL},
92 {"store", 1, MH_OPT_BOOL, NULL},
93 {"auto", 1, MH_OPT_BOOL, NULL},
94 {"part", 3, 0, "part"},
95 {"type", 1, 0, "type"},
96 {"verbose", 1, MH_OPT_BOOL, NULL},
97 {NULL}
98 };
99
100 typedef struct _msg_part *msg_part_t;
101
102 static msg_part_t msg_part_create __P((size_t num));
103 static void msg_part_destroy __P((msg_part_t p));
104 static int msg_part_eq __P((msg_part_t a, msg_part_t b));
105 static void msg_part_incr __P((msg_part_t p));
106 static void msg_part_decr __P((msg_part_t p));
107 static void msg_part_set_subpart __P((msg_part_t p, size_t subpart));
108 static void msg_part_print __P((msg_part_t p, int width));
109 static msg_part_t msg_part_parse __P((char *str));
110 static int msg_part_level __P((msg_part_t p));
111 static size_t msg_part_subpart __P((msg_part_t p, int level));
112
113 enum mhn_mode {
114 mode_compose,
115 mode_list,
116 mode_show,
117 mode_store,
118 };
119
120 static enum mhn_mode mode = mode_compose;
121
122 #define OPT_HEADERS 001
123 #define OPT_REALSIZE 002
124 #define OPT_PAUSE 004
125 #define OPT_AUTO 010
126 #define OPT_SERIALONLY 020
127 #define OPT_VERBOSE 040
128
129 static int mode_options = OPT_HEADERS;
130 static char *formfile;
131 static char *content_type;
132 static char *content_subtype;
133 static char *input_file;
134 static int width = 80;
135
136 static mh_msgset_t msgset;
137 static mailbox_t mbox;
138 static message_t message;
139 static msg_part_t req_part;
140
141 void
142 sfree (char **ptr)
143 {
144 if (!*ptr)
145 return;
146 free (*ptr);
147 *ptr = NULL;
148 }
149
150 void
151 split_content (const char *content, char **type, char **subtype)
152 {
153 char *p = strchr (content, '/');
154 if (p)
155 {
156 int len = p - content;
157 *type = xmalloc (len + 1);
158 memcpy (*type, content, len);
159 (*type)[len] = 0;
160
161 p++;
162 *subtype = xmalloc (strlen (p) + 1);
163 strcpy (*subtype, p);
164 }
165 else
166 {
167 *type = xmalloc (strlen (content) + 1);
168 strcpy (*type, content);
169 *subtype = NULL;
170 }
171 }
172
173 static int
174 opt_handler (int key, char *arg, void *unused, struct argp_state *state)
175 {
176 switch (key)
177 {
178 case ARG_FOLDER:
179 current_folder = arg;
180 break;
181
182 case ARG_FILE:
183 input_file = arg;
184 break;
185
186 /* Operation mode */
187 case ARG_COMPOSE:
188 if (is_true (arg))
189 {
190 mode = mode_compose;
191 break;
192 }
193 /*FALLTHRU*/
194 case ARG_NOCOMPOSE:
195 if (mode == mode_compose)
196 mode = mode_compose;
197 break;
198
199 case ARG_LIST:
200 if (is_true (arg))
201 {
202 mode = mode_list;
203 break;
204 }
205 /*FALLTHRU*/
206 case ARG_NOLIST:
207 if (mode == mode_list)
208 mode = mode_compose;
209 break;
210
211 case ARG_SHOW:
212 if (is_true (arg))
213 {
214 mode = mode_show;
215 break;
216 }
217 /*FALLTHRU*/
218 case ARG_NOSHOW:
219 if (mode == mode_show)
220 mode = mode_compose;
221 break;
222
223 case ARG_STORE:
224 if (is_true (arg))
225 {
226 mode = mode_store;
227 break;
228 }
229 /*FALLTHRU*/
230 case ARG_NOSTORE:
231 if (mode == mode_store)
232 mode = mode_compose;
233 break;
234
235 /* List options */
236 case ARG_HEADER:
237 if (is_true (arg))
238 {
239 mode_options |= OPT_HEADERS;
240 break;
241 }
242 /*FALLTHRU*/
243 case ARG_NOHEADERS:
244 mode_options &= ~OPT_HEADERS;
245 break;
246
247 case ARG_REALSIZE:
248 if (is_true (arg))
249 {
250 mode_options |= OPT_REALSIZE;
251 break;
252 }
253 /*FALLTHRU*/
254 case ARG_NOREALSIZE:
255 mode_options &= ~OPT_REALSIZE;
256 break;
257
258 /* Display options */
259
260 case ARG_SERIALONLY:
261 if (is_true (arg))
262 {
263 mode_options |= OPT_SERIALONLY;
264 break;
265 }
266 /*FALLTHRU*/
267 case ARG_NOSERIALONLY:
268 mode_options &= ~OPT_SERIALONLY;
269 break;
270
271 case ARG_PAUSE:
272 if (is_true (arg))
273 {
274 mode_options |= OPT_PAUSE;
275 break;
276 }
277 /*FALLTHRU*/
278 case ARG_NOPAUSE:
279 mode_options &= ~OPT_PAUSE;
280 break;
281
282 /* Store options */
283 case ARG_AUTO:
284 if (is_true (arg))
285 {
286 mode_options |= OPT_AUTO;
287 break;
288 }
289 /*FALLTHRU*/
290 case ARG_NOAUTO:
291 mode_options &= ~OPT_AUTO;
292 break;
293
294 case ARG_FORM:
295 formfile = arg;
296 break;
297
298 /* Common options */
299 case ARG_VERBOSE:
300 if (is_true (arg))
301 {
302 mode_options |= OPT_VERBOSE;
303 break;
304 }
305 /*FALLTHRU*/
306 case ARG_NOVERBOSE:
307 mode_options &= ~OPT_VERBOSE;
308 break;
309
310 case ARG_TYPE:
311 sfree (&content_type);
312 sfree (&content_subtype);
313 split_content (arg, &content_type, &content_subtype);
314 break;
315
316 case ARG_PART:
317 req_part = msg_part_parse (arg);
318 break;
319
320 default:
321 return 1;
322 }
323 return 0;
324 }
325
326
327 /* *********************** Message part functions ************************* */
328
329 struct _msg_part {
330 int level;
331 int maxlevel;
332 size_t *part;
333 };
334
335 msg_part_t
336 msg_part_create (size_t num)
337 {
338 msg_part_t p = xmalloc (sizeof (*p));
339 p->maxlevel = 16;
340 p->part = xmalloc (sizeof (*p->part) * p->maxlevel);
341 p->part[0] = num;
342 p->level = 0;
343 return p;
344 }
345
346 void
347 msg_part_destroy (msg_part_t p)
348 {
349 free (p->part);
350 free (p);
351 }
352
353 int
354 msg_part_eq (msg_part_t a, msg_part_t b)
355 {
356 int i, level = a->level < b->level ? a->level : b->level;
357
358 for (i = 1; i <= level; i++)
359 if (a->part[i] != b->part[i])
360 return 1;
361 return 0;
362 }
363
364 void
365 msg_part_incr (msg_part_t p)
366 {
367 if (p->level == p->maxlevel)
368 {
369 p->maxlevel += 16;
370 p->part = xrealloc (p->part, sizeof (*p->part) * p->maxlevel);
371 }
372 p->level++;
373 }
374
375 void
376 msg_part_decr (msg_part_t p)
377 {
378 if (p->level <= 0)
379 abort ();
380 p->level--;
381 }
382
383 void
384 msg_part_set_subpart (msg_part_t p, size_t subpart)
385 {
386 p->part[p->level] = subpart;
387 }
388
389 void
390 msg_part_print (msg_part_t p, int max_width)
391 {
392 int i;
393 int width = 0;
394
395 for (i = 1; i <= p->level; i++)
396 {
397 if (i > 1)
398 {
399 printf (".");
400 width++;
401 }
402 width += printf ("%lu", (unsigned long) p->part[i]);
403 }
404 for (; width < max_width; width++)
405 putchar (' ');
406 }
407
408 msg_part_t
409 msg_part_parse (char *str)
410 {
411 msg_part_t p = msg_part_create (0);
412
413 do
414 {
415 char *endp;
416 size_t num = strtoul (str, &endp, 10);
417
418 switch (*endp)
419 {
420 case '.':
421 str = endp + 1;
422 break;
423 case 0:
424 str = endp;
425 break;
426 default:
427 mh_error (_("malformed part specification (near %s)"), endp);
428 exit (1);
429 }
430 msg_part_incr (p);
431 msg_part_set_subpart (p, num);
432 }
433 while (*str);
434 return p;
435 }
436
437 int
438 msg_part_level (msg_part_t p)
439 {
440 return p->level;
441 }
442
443 size_t
444 msg_part_subpart (msg_part_t p, int level)
445 {
446 if (level <= p->level)
447 return p->part[level];
448 return 0;
449 }
450
451
452 /* ************************** Message iterators *************************** */
453
454 int
455 _get_hdr_value (header_t hdr, const char *name, char **value)
456 {
457 int status = header_aget_value (hdr, name, value);
458 if (status == 0)
459 {
460 /* Remove the newlines. */
461 char *nl;
462 while ((nl = strchr (*value, '\n')) != NULL)
463 *nl = ' ';
464 }
465 return status;
466 }
467
468 int
469 _get_content_type (header_t hdr, char **value)
470 {
471 char *type = NULL;
472 _get_hdr_value (hdr, MU_HEADER_CONTENT_TYPE, &type);
473 if (type == NULL || *type == '\0')
474 {
475 if (type)
476 free (type);
477 type = strdup ("text/plain"); /* Default. */
478 }
479 else
480 {
481 char *p = strchr (type, ';');
482 if (p)
483 *p = 0;
484 }
485 *value = type;
486 return 0;
487 }
488
489 static int
490 _get_content_encoding (header_t hdr, char **value)
491 {
492 char *encoding = NULL;
493 _get_hdr_value (hdr, MU_HEADER_CONTENT_TRANSFER_ENCODING, &encoding);
494 if (encoding == NULL || *encoding == '\0')
495 {
496 if (encoding)
497 free (encoding);
498 encoding = strdup ("7bit"); /* Default. */
499 }
500 *value = encoding;
501 return 0;
502 }
503
504 typedef int (*msg_handler_t) __PMT((message_t msg, msg_part_t part,
505 char *type, char *encoding,
506 void *data));
507
508 int
509 match_content (char *content)
510 {
511 int rc;
512 char *type, *subtype;
513
514 if (!content_type && !content_subtype)
515 return 0;
516 if (!content)
517 return 0;
518
519 split_content (content, &type, &subtype);
520
521 if (strcasecmp (content_type, type) == 0)
522 rc = strcasecmp (content_subtype, subtype);
523 else
524 rc = strcasecmp (content_type, subtype);
525
526 free (type);
527 free (subtype);
528 return rc;
529 }
530
531 int
532 call_handler (message_t msg, msg_part_t part, char *type, char *encoding,
533 msg_handler_t fun, void *data)
534 {
535 if (req_part && msg_part_eq (req_part, part))
536 return 0;
537 if (match_content (type))
538 return 0;
539 return fun (msg, part, type, encoding, data);
540 }
541
542 int
543 handle_message (message_t msg, msg_part_t part, msg_handler_t fun, void *data)
544 {
545 header_t hdr;
546 char *type = NULL;
547 char *encoding;
548 int ismime = 0;
549
550 message_get_header (msg, &hdr);
551 _get_content_type (hdr, &type);
552 _get_content_encoding (hdr, &encoding);
553
554 fun (msg, part, type, encoding, data);
555 sfree (&type);
556 sfree (&encoding);
557
558 message_is_multipart (msg, &ismime);
559 if (ismime)
560 {
561 unsigned int i, nparts;
562
563 message_get_num_parts (msg, &nparts);
564
565 msg_part_incr (part);
566 for (i = 1; i <= nparts; i++)
567 {
568 message_t message = NULL;
569
570 if (message_get_part (msg, i, &message) == 0)
571 {
572 message_get_header (message, &hdr);
573 _get_content_type (hdr, &type);
574 _get_content_encoding (hdr, &encoding);
575
576 msg_part_set_subpart (part, i);
577
578 if (strcasecmp (type, "multipart/mixed") == 0)
579 handle_message (message, part, fun, data);
580 else
581 call_handler (message, part, type, encoding, fun, data);
582 sfree (&type);
583 sfree (&encoding);
584 }
585 }
586 msg_part_decr (part);
587 }
588 return 0;
589 }
590
591 int
592 mhn_message_size (message_t msg, size_t *psize)
593 {
594 body_t body;
595 *psize = 0;
596 message_get_body (msg, &body);
597 if (mode_options & OPT_REALSIZE)
598 {
599 stream_t dstr = NULL, bstr = NULL;
600
601 if (body_get_stream (body, &bstr) == 0)
602 {
603 header_t hdr;
604 char *encoding;
605 size_t size = 0;
606 int rc;
607
608 message_get_header (msg, &hdr);
609 _get_content_encoding (hdr, &encoding);
610
611 rc = filter_create(&dstr, bstr, encoding,
612 MU_FILTER_DECODE, MU_STREAM_READ);
613 free (encoding);
614 if (rc == 0)
615 {
616 char buf[512];
617 size_t n;
618
619 while (stream_sequential_read (dstr, buf, sizeof buf, &n) == 0
620 && n > 0)
621 size += n;
622
623 stream_destroy (&dstr, NULL);
624 *psize = size;
625 return 0;
626 }
627 }
628 }
629
630 return body_size (body, psize);
631 }
632
633
634 /* ***************************** List Mode ******************************* */
635
636 int
637 list_handler (message_t msg, msg_part_t part, char *type, char *encoding,
638 void *data)
639 {
640 size_t size;
641 header_t hdr;
642
643 if (msg_part_level (part) == 0)
644 printf ("%3lu ", (unsigned long) msg_part_subpart (part, 0));
645 else
646 {
647 printf (" ");
648 msg_part_print (part, 5);
649 putchar (' ');
650 }
651 printf ("%-26s", type);
652
653 mhn_message_size (msg, &size);
654 printf ("%4lu", (unsigned long) size);
655
656 if (message_get_header (msg, &hdr) == 0)
657 {
658 char *descr;
659 if (header_aget_value (hdr, "Content-Description", &descr) == 0)
660 {
661 printf (" %s", descr);
662 free (descr);
663 }
664 }
665
666 printf ("\n");
667 return 0;
668 }
669
670 int
671 list_message (message_t msg, size_t num)
672 {
673 msg_part_t part = msg_part_create (num);
674 handle_message (msg, part, list_handler, NULL);
675 msg_part_destroy (part);
676 return 0;
677 }
678
679 void
680 list_iterator (mailbox_t mbox, message_t msg, size_t num, void *data)
681 {
682 list_message (msg, num);
683 }
684
685 int
686 mhn_list ()
687 {
688 int rc;
689
690 if (mode_options & OPT_HEADERS)
691 printf (_("msg part type/subtype size description\n"));
692
693 if (message)
694 rc = list_message (message, 0);
695 else
696 rc = mh_iterate (mbox, &msgset, list_iterator, NULL);
697 return rc;
698 }
699
700
701 /* ***************************** Show Mode ******************************* */
702
703 static list_t mhl_format;
704
705 void
706 cat_message (stream_t out, stream_t in)
707 {
708 int rc = 0;
709 char buf[512];
710 size_t n;
711
712 stream_seek (in, 0, SEEK_SET);
713 while (rc == 0
714 && stream_sequential_read (in, buf, sizeof buf, &n) == 0
715 && n > 0)
716 rc = stream_sequential_write (out, buf, n);
717 }
718
719 int
720 show_handler (message_t msg, msg_part_t part, char *type, char *encoding,
721 void *data)
722 {
723 stream_t out = data;
724
725 if (strncasecmp (type, "multipart", 9) == 0)
726 return 0;
727 if (mhl_format)
728 mhl_format_run (mhl_format, width, 0, MHL_DECODE, msg, out);
729 else
730 {
731 int rc;
732 body_t body = NULL;
733 stream_t dstr, bstr;
734
735 if ((rc = message_get_body (msg, &body)))
736 {
737 mh_error (_("%lu: can't get message body: %s"),
738 (unsigned long) msg_part_subpart (part, 0),
739 mu_strerror (rc));
740 return 0;
741 }
742 body_get_stream (body, &bstr);
743 rc = filter_create(&dstr, bstr, encoding,
744 MU_FILTER_DECODE, MU_STREAM_READ);
745 if (rc == 0)
746 bstr = dstr;
747 cat_message (out, bstr);
748 if (dstr)
749 stream_destroy (&dstr, stream_get_owner (dstr));
750 }
751 }
752
753 int
754 show_message (message_t msg, size_t num, void *data)
755 {
756 msg_part_t part = msg_part_create (num);
757 handle_message (msg, part, show_handler, data);
758 msg_part_destroy (part);
759 return 0;
760 }
761
762 void
763 show_iterator (mailbox_t mbox, message_t msg, size_t num, void *data)
764 {
765 show_message (msg, num, data);
766 }
767
768 int
769 mhn_show ()
770 {
771 int rc;
772 stream_t ostr;
773
774 rc = stdio_stream_create (&ostr, stdout, 0);
775 if (rc)
776 {
777 mh_error (_("can't create output stream: %s"), mu_strerror (rc));
778 exit (1);
779 }
780 rc = stream_open (ostr);
781 if (rc)
782 {
783 mh_error (_("can't open output stream: %s"), mu_strerror (rc));
784 exit (1);
785 }
786
787 if (formfile)
788 {
789 char *s = mh_expand_name (MHLIBDIR, "mhl.headers", 0);
790 if (access (s, R_OK) == 0)
791 formfile = "mhl.headers";
792 free (s);
793 }
794
795 if (formfile)
796 {
797 char *s = mh_expand_name (MHLIBDIR, formfile, 0);
798 mhl_format = mhl_format_compile (s);
799 if (!mhl_format)
800 exit (1);
801 free (s);
802 }
803
804 if (message)
805 rc = show_message (message, 0, ostr);
806 else
807 rc = mh_iterate (mbox, &msgset, show_iterator, ostr);
808 return rc;
809 }
810
811
812
813 /* *************************** Main Entry Point ************************** */
814
815 int
816 main (int argc, char **argv)
817 {
818 int rc;
819 int index;
820
821 mu_init_nls ();
822
823 mh_argp_parse (argc, argv, 0, options, mh_option, args_doc, doc,
824 opt_handler, NULL, &index);
825
826 argc -= index;
827 argv += index;
828
829 if (input_file)
830 {
831 if (argc)
832 {
833 mh_error (_("extra arguments"));
834 return 1;
835 }
836 message = mh_file_to_message (mu_path_folder_dir, input_file);
837 if (!message)
838 return 1;
839 }
840 else
841 {
842 mbox = mh_open_folder (current_folder, 0);
843 mh_msgset_parse (mbox, &msgset, argc, argv, "cur");
844 }
845
846 switch (mode)
847 {
848 case mode_compose:
849 mh_error ("mode is not yet implemented");
850 rc = 1;
851 break;
852
853 case mode_list:
854 rc = mhn_list ();
855 break;
856
857 case mode_show:
858 rc = mhn_show ();
859 break;
860
861 case mode_store:
862 mh_error ("mode is not yet implemented");
863 rc = 1;
864 break;
865
866 default:
867 abort ();
868 }
869 return rc ? 1 : 0;
870 }
871