Initial revision
[mmh] / sbr / folder_addmsg.c
1
2 /*
3  * folder_addmsg.c -- Link message into folder
4  *
5  * $Id$
6  */
7
8 #include <h/mh.h>
9 #include <fcntl.h>
10 #include <errno.h>
11
12 /*
13  * Link message into a folder.  Return the new number
14  * of the message.  If an error occurs, return -1.
15  */
16
17 int
18 folder_addmsg (struct msgs **mpp, char *msgfile, int selected,
19                int unseen, int preserve)
20 {
21     int infd, outfd, linkerr, first_time, msgnum;
22     char *nmsg, newmsg[BUFSIZ];
23     struct msgs *mp;
24     struct stat st1, st2;
25
26     first_time = 1;     /* this is first attempt */
27     mp = *mpp;
28
29     /*
30      * We might need to make several attempts
31      * in order to add the message to the folder.
32      */
33     for (;;) {
34         /*
35          * Get the message number we will attempt to add.
36          */
37         if (first_time) {
38             /* should we preserve the numbering of the message? */
39             if (preserve && (msgnum = m_atoi (msgfile)) > 0) {
40                 ;
41             } else if (mp->nummsg == 0) {
42                 /* check if we are adding to empty folder */
43                 msgnum = 1;
44             } else {
45                 /* else use highest message number + 1 */
46                 msgnum = mp->hghmsg + 1;
47             }
48             first_time = 0;
49         } else {
50             /* another attempt, so try next higher message number */
51             msgnum++;
52         }
53
54         /*
55          * See if we need more space.  If we need space at the
56          * end, then we allocate space for an addition 100 messages.
57          * If we need space at the beginning of the range, then just
58          * extend message status range to cover this message number.
59          */
60         if (msgnum > mp->hghoff) {
61             if ((mp = folder_realloc (mp, mp->lowoff, msgnum + 100)))
62                 *mpp = mp;
63             else {
64                 advise (NULL, "unable to allocate folder storage");
65                 return -1;
66             }
67         } else if (msgnum < mp->lowoff) {
68             if ((mp = folder_realloc (mp, msgnum, mp->hghoff)))
69                 *mpp = mp;
70             else {
71                 advise (NULL, "unable to allocate folder storage");
72                 return -1;
73             }
74         }
75
76         /*
77          * If a message is already in that slot,
78          * then loop to next available slot.
79          */
80         if (does_exist (mp, msgnum))
81             continue;
82
83         /* setup the bit flags for this message */
84         clear_msg_flags (mp, msgnum);
85         set_exists (mp, msgnum);
86
87         /* should we set the SELECT_UNSEEN bit? */
88         if (unseen) {
89             set_unseen (mp, msgnum);
90         }
91
92         /* should we set the SELECTED bit? */
93         if (selected) {
94             set_selected (mp, msgnum);
95
96             /* check if highest or lowest selected */
97             if (mp->numsel == 0) {
98                 mp->lowsel = msgnum;
99                 mp->hghsel = msgnum;
100             } else {
101                 if (msgnum < mp->lowsel)
102                     mp->lowsel = msgnum;
103                 if (msgnum > mp->hghsel)
104                     mp->hghsel = msgnum;
105             }
106
107             /* increment number selected */
108             mp->numsel++;
109         }
110
111         /*
112          * check if this is highest or lowest message
113          */
114         if (mp->nummsg == 0) {
115             mp->lowmsg = msgnum;
116             mp->hghmsg = msgnum;
117         } else {
118             if (msgnum < mp->lowmsg)
119                 mp->lowmsg = msgnum;
120             if (msgnum > mp->hghmsg)
121                 mp->hghmsg = msgnum;
122         }
123
124         /* increment message count */
125         mp->nummsg++;
126
127         nmsg = m_name (msgnum);
128         snprintf (newmsg, sizeof(newmsg), "%s/%s", mp->foldpath, nmsg);
129
130         /*
131          * Now try to link message into folder
132          */
133         if (link (msgfile, newmsg) != -1) {
134             return msgnum;
135         } else {
136             linkerr = errno;
137
138 #ifdef EISREMOTE
139             if (linkerr == EISREMOTE)
140                 linkerr = EXDEV;
141 #endif /* EISREMOTE */
142
143             /*
144              * Check if the file in our desired location is the same
145              * as the source file.  If so, then just leave it alone
146              * and return.  Otherwise, we will continue the main loop
147              * and try again at another slot (hghmsg+1).
148              */
149             if (linkerr == EEXIST) {
150                 if (stat (msgfile, &st2) == 0 && stat (newmsg, &st1) == 0
151                     && st2.st_ino == st1.st_ino) {
152                     return msgnum;
153                 } else {
154                     continue;
155                 }
156             }
157
158             /*
159              * If link failed because we are trying to link
160              * across devices, then check if there is a message
161              * already in the desired location.  If so, then return
162              * error, else just copy the message.
163              */
164             if (linkerr == EXDEV) {
165                 if (stat (newmsg, &st1) == 0) {
166                     advise (NULL, "message %s:%s already exists", newmsg);
167                     return -1;
168                 } else {
169                     if ((infd = open (msgfile, O_RDONLY)) == -1) {
170                         advise (msgfile, "unable to open message %s");
171                         return -1;
172                     }
173                     fstat (infd, &st1);
174                     if ((outfd = creat (newmsg, (int) st1.st_mode & 0777)) == -1) {
175                         advise (newmsg, "unable to create");
176                         close (infd);
177                         return -1;
178                     }
179                     cpydata (infd, outfd, msgfile, newmsg);
180                     close (infd);
181                     close (outfd);
182                     return msgnum;
183                 }
184             }
185
186             /*
187              * Else, some other type of link error,
188              * so just return error.
189              */
190             advise (newmsg, "error linking %s to", msgfile);
191             return -1;
192         }
193     }
194 }