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