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