Commit 1b691c26 1b691c267b818bab719f32fb3fc134c30d02ff04 by Alain Magloire

* mailbox/mbx_mbox.c (mbox_expunge0): New function.

	Move all the previous mbox_expunge() to mbox_expunge0()
	with a new argument deciding whether or not to remove
	messages set deleted.
	(mbox_save_attributes): Stub calling mbox_expunge0().
	(mbox_expunge): Stub calling mbox_expunge0()

	* mailbox/mbx_mh.c: Provide a prototype for mh_message_number()
	there is no provision for it in the current Mailbox API.
1 parent 45be40f7
1 2001-10-08 Alain Magloire
2
3 * mailbox/mbx_mbox.c (mbox_expunge0): New function.
4 Move all the previous mbox_expunge() to mbox_expunge0()
5 with a new argument deciding whether or not to remove
6 messages set deleted.
7 (mbox_save_attributes): Stub calling mbox_expunge0().
8 (mbox_expunge): Stub calling mbox_expunge0()
9
10 * mailbox/mbx_mh.c: Provide a prototype for mh_message_number()
11 there is no provision for it in the current Mailbox API.
12
1 2001-10-08 Sergey Poznyakoff 13 2001-10-08 Sergey Poznyakoff
2 14
3 * mail/z.c: Bugfix: Did not display the last message when the 15 * mail/z.c: Bugfix: Did not display the last message when the
4 number of lines in last page was less than pagelines. 16 number of lines in last page was less than pagelines.
5 * mailbox/mbx_pop.c: Use ENOSYS instead of ENOTSUP. 17 * mailbox/mbx_pop.c: Use ENOSYS instead of ENOTSUP.
6 * mh/mh_format.c: Fixed memory leak in strobj_free(). 18 * mh/mh_format.c: Fixed memory leak in strobj_free().
7 19
8 2001-10-05 Alain Magloire 20 2001-10-05 Alain Magloire
9 21
10 * mailbox/folder_imap.c (imap_flags): Bailout at the matching 22 * mailbox/folder_imap.c (imap_flags): Bailout at the matching
......
...@@ -171,6 +171,7 @@ static int mbox_append_message __P ((mailbox_t, message_t)); ...@@ -171,6 +171,7 @@ static int mbox_append_message __P ((mailbox_t, message_t));
171 static int mbox_messages_count __P ((mailbox_t, size_t *)); 171 static int mbox_messages_count __P ((mailbox_t, size_t *));
172 static int mbox_messages_recent __P ((mailbox_t, size_t *)); 172 static int mbox_messages_recent __P ((mailbox_t, size_t *));
173 static int mbox_message_unseen __P ((mailbox_t, size_t *)); 173 static int mbox_message_unseen __P ((mailbox_t, size_t *));
174 static int mbox_expunge0 __P ((mailbox_t, int));
174 static int mbox_expunge __P ((mailbox_t)); 175 static int mbox_expunge __P ((mailbox_t));
175 static int mbox_save_attributes __P ((mailbox_t)); 176 static int mbox_save_attributes __P ((mailbox_t));
176 static int mbox_uidvalidity __P ((mailbox_t, unsigned long *)); 177 static int mbox_uidvalidity __P ((mailbox_t, unsigned long *));
...@@ -540,305 +541,6 @@ mbox_tmpfile (mailbox_t mailbox, char **pbox) ...@@ -540,305 +541,6 @@ mbox_tmpfile (mailbox_t mailbox, char **pbox)
540 return fd; 541 return fd;
541 } 542 }
542 543
543 /* FIXME: This is the code of _expunge(), duplicating code like this is really
544 not elegant ... but since we are moving with a new API of the mailbox,
545 this hack will suffice, for now.
546 - Check if messages/attributeds are modified
547 - create a temporary mailbox
548 - copy the messages to the temporay mailbox
549 - copy the temporary mailbox back to the original
550 - truncate ()
551 - re-scan ()
552 */
553 static int
554 mbox_save_attributes (mailbox_t mailbox)
555 {
556 mbox_data_t mud = mailbox->data;
557 mbox_message_t mum;
558 int status = 0;
559 sigset_t signalset;
560 int tempfile;
561 size_t i, dirty; /* dirty will indicate the first modified message. */
562 off_t marker = 0; /* marker will be the position to truncate. */
563 off_t total = 0;
564 char *tmpmboxname = NULL;
565 mailbox_t tmpmailbox = NULL;
566 size_t save_imapbase = 0; /* uidvalidity is save in the first message. */
567 #ifdef WITH_PTHREAD
568 int state;
569 #endif
570
571 if (mud == NULL)
572 return EINVAL;
573
574 MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mbox_save_attribute (%s)\n",
575 mud->name);
576
577 /* Noop. */
578 if (mud->messages_count == 0)
579 return 0;
580
581 /* Find the first dirty(modified) message. */
582 for (dirty = 0; dirty < mud->messages_count; dirty++)
583 {
584 mum = mud->umessages[dirty];
585 /* Attribute may have been tampered, break here. */
586 if ((mum->attr_flags & MU_ATTRIBUTE_MODIFIED)
587 || (mum->message && message_is_modified (mum->message)))
588 break;
589 }
590
591 /* Did something change ? */
592 if (dirty == mud->messages_count)
593 return 0; /* Noop. Nothing change, bail out. */
594
595 /* Create a temporary file, for our temp mailbox. */
596 tempfile = mbox_tmpfile (mailbox, &tmpmboxname);
597 if (tempfile == -1)
598 {
599 if (tmpmboxname)
600 free (tmpmboxname);
601 mu_error ("Failed to create temporary file when saving attributes.\n");
602 return errno;
603 }
604
605 /* Create the temporary mailbox_t. */
606 {
607 mbox_data_t tmp_mud;
608 char *m = alloca (5 + strlen (tmpmboxname) + 1);
609
610 /* Try via the mbox: protocol. */
611 sprintf (m, "mbox:%s", tmpmboxname);
612 status = mailbox_create (&tmpmailbox, m);
613 if (status != 0)
614 {
615 /* Do not give up just yet, maybe they register the path_record. */
616 sprintf (m, "%s", tmpmboxname);
617 status = mailbox_create (&tmpmailbox, m);
618 if (status != 0)
619 {
620 /* Ok give up now. */
621 close (tempfile);
622 remove (tmpmboxname);
623 free (tmpmboxname);
624 return status;
625 }
626 }
627
628 /* Caution:Must be flag CREATE if not the mailbox_open will try to mmap()
629 the file. */
630 status = mailbox_open (tmpmailbox, MU_STREAM_CREAT | MU_STREAM_RDWR);
631 if (status != 0)
632 {
633 close (tempfile);
634 remove (tmpmboxname);
635 free (tmpmboxname);
636 return status;
637 }
638 close (tempfile); /* This one is useless the mailbox have its own. */
639 tmp_mud = tmpmailbox->data;
640 /* May need when appending. */
641 tmp_mud->uidvalidity = mud->uidvalidity;
642 tmp_mud->uidnext = mud->uidnext;
643 }
644
645 /* Get the File lock. */
646 if (locker_lock (mailbox->locker, MU_LOCKER_WRLOCK) != 0)
647 {
648 mailbox_close (tmpmailbox);
649 mailbox_destroy (&tmpmailbox);
650 remove (tmpmboxname);
651 free (tmpmboxname);
652 mu_error ("Failed to grab the lock\n");
653 return ENOLCK;
654 }
655
656 /* Critical section, we can not allowed signal here. */
657 #ifdef WITH_PTHREAD
658 pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state);
659 #endif
660 sigemptyset (&signalset);
661 sigaddset (&signalset, SIGTERM);
662 sigaddset (&signalset, SIGHUP);
663 sigaddset (&signalset, SIGTSTP);
664 sigaddset (&signalset, SIGINT);
665 sigaddset (&signalset, SIGWINCH);
666 sigprocmask (SIG_BLOCK, &signalset, 0);
667
668 /* Set the marker position. */
669 marker = mud->umessages[dirty]->header_from;
670 total = 0;
671
672 /* Copy to the temporary mailbox all messages starting from the dirty. */
673 for (i = dirty; i < mud->messages_count; i++)
674 {
675 mum = mud->umessages[i];
676
677 /* Do the expensive mbox_append_message0() only if mark dirty. */
678 if ((mum->attr_flags & MU_ATTRIBUTE_MODIFIED)
679 || (mum->message && message_is_modified (mum->message)))
680 {
681 /* The message was not instanciated, probably the dirty attribute
682 flag was set by mbox_scan(), create one here. */
683 if (mum->message == 0)
684 {
685 message_t msg;
686 status = mbox_get_message (mailbox, i + 1, &msg);
687 if (status != 0)
688 {
689 mu_error ("Error expunge:%d: %s", __LINE__,
690 strerror (status));
691 goto bailout0;
692 }
693 }
694 status = mbox_append_message0 (tmpmailbox, mum->message,
695 &total, 1, (i == save_imapbase));
696 if (status != 0)
697 {
698 mu_error ("Error expunge:%d: %s", __LINE__,
699 strerror (status));
700 goto bailout0;
701 }
702 /* Clear the dirty bits. */
703 mum->attr_flags &= ~MU_ATTRIBUTE_MODIFIED;
704 message_clear_modified (mum->message);
705 }
706 else
707 {
708 /* Nothing changed, copy the message straight. */
709 char buffer [1024];
710 size_t n;
711 off_t offset = mum->header_from;
712 size_t len = mum->body_end - mum->header_from;
713 while (len > 0)
714 {
715 n = (len < sizeof (buffer)) ? len : sizeof (buffer);
716 if ((status = stream_read (mailbox->stream, buffer, n, offset,
717 &n) != 0)
718 || (status = stream_write (tmpmailbox->stream, buffer, n,
719 total, &n) != 0))
720 {
721 mu_error ("Error expunge:%d: %s", __LINE__,
722 strerror (status));
723 goto bailout0;
724 }
725 len -= n;
726 total += n;
727 offset += n;
728 }
729 /* Add the newline separator. */
730 status = stream_write (tmpmailbox->stream, "\n", 1, total, &n);
731 if (status != 0)
732 {
733 mu_error ("Error expunge:%d: %s", __LINE__,
734 strerror (status));
735 goto bailout0;
736 }
737 total++;
738 }
739 } /* for (;;) */
740
741 /* Caution: before ftruncate()ing the file see
742 - if we've receive new mails. Some programs may not respect the lock,
743 - or the lock was held for too long.
744 - The mailbox may not have been properly updated before expunging. */
745 {
746 off_t size = 0;
747 if (stream_size (mailbox->stream, &size) == 0)
748 {
749 off_t len = size - mud->size;
750 off_t offset = mud->size;
751 char buffer [1024];
752 size_t n = 0;
753 if (len > 0 )
754 {
755 while ((status = stream_read (mailbox->stream, buffer,
756 sizeof (buffer), offset, &n)) == 0
757 && n > 0)
758 {
759 status = stream_write (tmpmailbox->stream, buffer, n,
760 total, &n);
761 if (status != 0)
762 {
763 mu_error ("Error expunge:%d: %s", __LINE__,
764 strerror (status));
765 goto bailout0;
766 }
767 total += n;
768 offset += n;
769 }
770 }
771 else if (len < 0)
772 {
773 /* Corrupted mailbox. */
774 mu_error ("Error expunge:%d: %s", __LINE__,
775 strerror (status));
776 goto bailout0;
777 }
778 }
779 } /* End of precaution. */
780
781 /* Seek and rewrite it. */
782 if (total > 0)
783 {
784 char buffer [1024];
785 size_t n = 0;
786 off_t off = 0;
787 off_t offset = marker;
788 while ((status = stream_read (tmpmailbox->stream, buffer,
789 sizeof (buffer), off, &n)) == 0
790 && n > 0)
791 {
792 status = stream_write (mailbox->stream, buffer, n, offset, &n);
793 if (status != 0)
794 {
795 mu_error ("Error expunge:%d: %s\n", __LINE__,
796 strerror (status));
797 goto bailout;
798 }
799 off += n;
800 offset += n;
801 }
802 }
803
804 /* Flush/truncation. Need to flush before truncate. */
805 stream_flush (mailbox->stream);
806 status = stream_truncate (mailbox->stream, total + marker);
807 if (status != 0)
808 {
809 mu_error ("Error expunging:%d: %s\n", __LINE__,
810 strerror (status));
811 goto bailout;
812 }
813
814 /* Don't remove the tmp mbox in case of errors, when writing back. */
815 bailout0:
816 remove (tmpmboxname);
817
818 bailout:
819
820 free (tmpmboxname);
821 /* Release the File lock. */
822 locker_unlock (mailbox->locker);
823 mailbox_close (tmpmailbox);
824 mailbox_destroy (&tmpmailbox);
825
826 /* Reenable interruption. */
827 #ifdef WITH_PTHREAD
828 pthread_setcancelstate (state, &state);
829 #endif
830 sigprocmask (SIG_UNBLOCK, &signalset, 0);
831
832 /* We need to readjust the pointers i.e the offsets of the msg structures. */
833 if (status == 0)
834 {
835 /* This should reset the messages_count, the last argument 0 means
836 not to send event notification. */
837 status = mbox_scan0 (mailbox, dirty, NULL, 0);
838 }
839 return status;
840 }
841
842 /* For the expunge bits we took a very cautionnary approach, meaning 544 /* For the expunge bits we took a very cautionnary approach, meaning
843 we create a temporary mailbox in the tmpdir copy all the message not mark 545 we create a temporary mailbox in the tmpdir copy all the message not mark
844 deleted(Actually we copy all the message that may have been modified 546 deleted(Actually we copy all the message that may have been modified
...@@ -855,7 +557,7 @@ mbox_save_attributes (mailbox_t mailbox) ...@@ -855,7 +557,7 @@ mbox_save_attributes (mailbox_t mailbox)
855 the temporary file may be left in /tmp, which is not all that bad 557 the temporary file may be left in /tmp, which is not all that bad
856 because at least, we have something to recuperate when failure. */ 558 because at least, we have something to recuperate when failure. */
857 static int 559 static int
858 mbox_expunge (mailbox_t mailbox) 560 mbox_expunge0 (mailbox_t mailbox, int remove_deleted)
859 { 561 {
860 mbox_data_t mud = mailbox->data; 562 mbox_data_t mud = mailbox->data;
861 mbox_message_t mum; 563 mbox_message_t mum;
...@@ -909,11 +611,14 @@ mbox_expunge (mailbox_t mailbox) ...@@ -909,11 +611,14 @@ mbox_expunge (mailbox_t mailbox)
909 /* This is redundant, we go to the loop again. But it's more secure here 611 /* This is redundant, we go to the loop again. But it's more secure here
910 since we don't want to be disturb when expunging. Destroy all the 612 since we don't want to be disturb when expunging. Destroy all the
911 messages mark for deletion. */ 613 messages mark for deletion. */
912 for (j = 0; j < mud->messages_count; j++) 614 if (remove_deleted)
913 { 615 {
914 mum = mud->umessages[j]; 616 for (j = 0; j < mud->messages_count; j++)
915 if (mum && mum->message && ATTRIBUTE_IS_DELETED (mum->attr_flags)) 617 {
916 message_destroy (&(mum->message), mum); 618 mum = mud->umessages[j];
619 if (mum && mum->message && ATTRIBUTE_IS_DELETED (mum->attr_flags))
620 message_destroy (&(mum->message), mum);
621 }
917 } 622 }
918 623
919 /* Create temporary mailbox_t. */ 624 /* Create temporary mailbox_t. */
...@@ -988,7 +693,7 @@ mbox_expunge (mailbox_t mailbox) ...@@ -988,7 +693,7 @@ mbox_expunge (mailbox_t mailbox)
988 mum = mud->umessages[i]; 693 mum = mud->umessages[i];
989 694
990 /* Skip it, if mark for deletion. */ 695 /* Skip it, if mark for deletion. */
991 if (ATTRIBUTE_IS_DELETED (mum->attr_flags)) 696 if (remove_deleted && ATTRIBUTE_IS_DELETED (mum->attr_flags))
992 { 697 {
993 /* We save the uidvalidity in the first message, if it is being 698 /* We save the uidvalidity in the first message, if it is being
994 deleted we need to move the uidvalidity to the first available 699 deleted we need to move the uidvalidity to the first available
...@@ -1172,7 +877,7 @@ mbox_expunge (mailbox_t mailbox) ...@@ -1172,7 +877,7 @@ mbox_expunge (mailbox_t mailbox)
1172 /* Clear all the references, any attach messages been already 877 /* Clear all the references, any attach messages been already
1173 destroy above. */ 878 destroy above. */
1174 mum = mud->umessages[j]; 879 mum = mud->umessages[j];
1175 if (ATTRIBUTE_IS_DELETED (mum->attr_flags)) 880 if (remove_deleted && ATTRIBUTE_IS_DELETED (mum->attr_flags))
1176 { 881 {
1177 if ((j + 1) <= dlast) 882 if ((j + 1) <= dlast)
1178 { 883 {
...@@ -1230,6 +935,18 @@ mbox_expunge (mailbox_t mailbox) ...@@ -1230,6 +935,18 @@ mbox_expunge (mailbox_t mailbox)
1230 } 935 }
1231 936
1232 static int 937 static int
938 mbox_expunge (mailbox_t mailbox)
939 {
940 return mbox_expunge0 (mailbox, 1);
941 }
942
943 static int
944 mbox_save_attributes (mailbox_t mailbox)
945 {
946 return mbox_expunge0 (mailbox, 0);
947 }
948
949 static int
1233 mbox_message_uid (message_t msg, size_t *puid) 950 mbox_message_uid (message_t msg, size_t *puid)
1234 { 951 {
1235 mbox_message_t mum = message_get_owner (msg); 952 mbox_message_t mum = message_get_owner (msg);
......
...@@ -72,9 +72,9 @@ struct _mh_message ...@@ -72,9 +72,9 @@ struct _mh_message
72 int deleted; /* Was the message originally deleted */ 72 int deleted; /* Was the message originally deleted */
73 73
74 time_t mtime; /* Time of last modification */ 74 time_t mtime; /* Time of last modification */
75 size_t header_lines; /* Number of lines in the header part */ 75 size_t header_lines; /* Number of lines in the header part */
76 size_t body_lines; /* Number of lines in the body */ 76 size_t body_lines; /* Number of lines in the body */
77 77
78 message_t message; /* Corresponding message_t */ 78 message_t message; /* Corresponding message_t */
79 struct _mh_data *mhd; /* Back pointer. */ 79 struct _mh_data *mhd; /* Back pointer. */
80 }; 80 };
...@@ -150,6 +150,9 @@ static int mh_envelope_date __P((envelope_t envelope, char *buf, size_t len, ...@@ -150,6 +150,9 @@ static int mh_envelope_date __P((envelope_t envelope, char *buf, size_t len,
150 static int mh_envelope_sender __P((envelope_t envelope, char *buf, size_t len, 150 static int mh_envelope_sender __P((envelope_t envelope, char *buf, size_t len,
151 size_t *psize)); 151 size_t *psize));
152 152
153 /* Should be in an other header file. */
154 extern int mh_message_number __P ((message_t msg, size_t *pnum));
155
153 /* Return filename for the message. 156 /* Return filename for the message.
154 NOTE: Allocates memory. */ 157 NOTE: Allocates memory. */
155 static char * 158 static char *
...@@ -490,7 +493,7 @@ _mh_message_save (struct _mh_data *mhd, struct _mh_message *mhm, int expunge) ...@@ -490,7 +493,7 @@ _mh_message_save (struct _mh_data *mhd, struct _mh_message *mhm, int expunge)
490 int status; 493 int status;
491 attribute_t attr; 494 attribute_t attr;
492 body_t body; 495 body_t body;
493 496
494 if (expunge) 497 if (expunge)
495 fp = _mh_tempfile (mhm->mhd, &name); 498 fp = _mh_tempfile (mhm->mhd, &name);
496 else 499 else
...@@ -499,7 +502,7 @@ _mh_message_save (struct _mh_data *mhd, struct _mh_message *mhm, int expunge) ...@@ -499,7 +502,7 @@ _mh_message_save (struct _mh_data *mhd, struct _mh_message *mhm, int expunge)
499 fp = fopen (msg_name, "w"); 502 fp = fopen (msg_name, "w");
500 free (msg_name); 503 free (msg_name);
501 } 504 }
502 505
503 if (!fp) 506 if (!fp)
504 { 507 {
505 free (mhm); 508 free (mhm);
...@@ -531,7 +534,7 @@ _mh_message_save (struct _mh_data *mhd, struct _mh_message *mhm, int expunge) ...@@ -531,7 +534,7 @@ _mh_message_save (struct _mh_data *mhd, struct _mh_message *mhm, int expunge)
531 break; 534 break;
532 535
533 nlines++; 536 nlines++;
534 537
535 if (!(strncasecmp (buf, "status:", 7) == 0 538 if (!(strncasecmp (buf, "status:", 7) == 0
536 || strncasecmp (buf, "x-imapbase:", 11) == 0 539 || strncasecmp (buf, "x-imapbase:", 11) == 0
537 || strncasecmp (buf, "x-iud:", 6) == 0)) 540 || strncasecmp (buf, "x-iud:", 6) == 0))
...@@ -556,7 +559,7 @@ _mh_message_save (struct _mh_data *mhd, struct _mh_message *mhm, int expunge) ...@@ -556,7 +559,7 @@ _mh_message_save (struct _mh_data *mhd, struct _mh_message *mhm, int expunge)
556 fprintf (fp, "\n"); 559 fprintf (fp, "\n");
557 560
558 /* Copy message body */ 561 /* Copy message body */
559 562
560 message_get_body (msg, &body); 563 message_get_body (msg, &body);
561 body_get_stream (body, &stream); 564 body_get_stream (body, &stream);
562 off = 0; 565 off = 0;
...@@ -584,7 +587,7 @@ _mh_message_save (struct _mh_data *mhd, struct _mh_message *mhm, int expunge) ...@@ -584,7 +587,7 @@ _mh_message_save (struct _mh_data *mhd, struct _mh_message *mhm, int expunge)
584 free (name); 587 free (name);
585 free (msg_name); 588 free (msg_name);
586 } 589 }
587 590
588 return 0; 591 return 0;
589 } 592 }
590 593
...@@ -597,11 +600,11 @@ mh_append_message (mailbox_t mailbox, message_t msg) ...@@ -597,11 +600,11 @@ mh_append_message (mailbox_t mailbox, message_t msg)
597 600
598 if (!mailbox || !msg) 601 if (!mailbox || !msg)
599 return EINVAL; 602 return EINVAL;
600 603
601 mhm = calloc (1, sizeof(*mhm)); 604 mhm = calloc (1, sizeof(*mhm));
602 if (!mhm) 605 if (!mhm)
603 return ENOMEM; 606 return ENOMEM;
604 607
605 mhm->mhd = mhd; 608 mhm->mhd = mhd;
606 mhm->seq_number = _mh_next_seq (mhd); 609 mhm->seq_number = _mh_next_seq (mhd);
607 mhm->uid = mhd->uidnext++; 610 mhm->uid = mhd->uidnext++;
...@@ -902,8 +905,8 @@ mh_scan_message (struct _mh_message *mhm) ...@@ -902,8 +905,8 @@ mh_scan_message (struct _mh_message *mhm)
902 return 0; 905 return 0;
903 } 906 }
904 free (msg_name); 907 free (msg_name);
905 } 908 }
906 909
907 while ((status = stream_readline (stream, buf, sizeof (buf), off, &n) == 0) 910 while ((status = stream_readline (stream, buf, sizeof (buf), off, &n) == 0)
908 && n != 0) 911 && n != 0)
909 { 912 {
...@@ -1220,7 +1223,7 @@ mh_readstream (struct _mh_message *mhm, char *buffer, size_t buflen, ...@@ -1220,7 +1223,7 @@ mh_readstream (struct _mh_message *mhm, char *buffer, size_t buflen,
1220 if (ln > 0) 1223 if (ln > 0)
1221 { 1224 {
1222 /* Position the file pointer and the buffer. */ 1225 /* Position the file pointer and the buffer. */
1223 nread = ((size_t)ln < buflen) ? ln : buflen; 1226 nread = ((size_t)ln < buflen) ? (size_t)ln : buflen;
1224 if (isreadline) 1227 if (isreadline)
1225 status = stream_readline (mhm->stream, buffer, buflen, 1228 status = stream_readline (mhm->stream, buffer, buflen,
1226 start + off, &nread); 1229 start + off, &nread);
...@@ -1482,5 +1485,3 @@ mh_message_number (message_t msg, size_t *pnum) ...@@ -1482,5 +1485,3 @@ mh_message_number (message_t msg, size_t *pnum)
1482 *pnum = mhm->seq_number; 1485 *pnum = mhm->seq_number;
1483 return 0; 1486 return 0;
1484 } 1487 }
1485
1486
......