Fix bug #3380. Initialize widget before callback handler for 'clicked'
[claws.git] / src / plugins / mailmbox / mmapstring.c
1 /*
2  * libEtPan! -- a mail stuff library
3  *
4  * Copyright (C) 2001, 2002 - DINH Viet Hoa
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the libEtPan! project nor the names of its
16  *    contributors may be used to endorse or promote products derived
17  *    from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 /*
33  * $Id$
34  */
35
36 #include "mmapstring.h"
37
38 #include "chash.h"
39
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <sys/mman.h>
43 #include <string.h>
44 #include <pthread.h>
45 #include <limits.h>
46
47 #include "utils.h"
48
49 #define MAX(a, b) ((a) > (b) ? (a) : (b))
50 #define MIN(a, b) ((a) < (b) ? (a) : (b))
51
52 #define MMAP_STRING_DEFAULT_CEIL (8 * 1024 * 1024)
53
54 #define DEFAULT_TMP_PATH "/tmp"
55
56 static char tmpdir[PATH_MAX] = DEFAULT_TMP_PATH;
57
58 static size_t mmap_string_ceil = MMAP_STRING_DEFAULT_CEIL;
59
60 /* MMAPString references */
61
62 static pthread_mutex_t mmapstring_lock = PTHREAD_MUTEX_INITIALIZER;
63 static chash * mmapstring_hashtable = NULL;
64
65 static void mmapstring_hashtable_init()
66 {
67   mmapstring_hashtable = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
68 }
69
70 void mmap_string_set_tmpdir(char * directory)
71 {
72   strncpy(tmpdir, directory, PATH_MAX);
73   tmpdir[PATH_MAX - 1] = 0;
74 }
75
76
77 int mmap_string_ref(MMAPString * string)
78 {
79   chash * ht;
80   int r;
81   chashdatum key;
82   chashdatum data;
83   
84   pthread_mutex_lock(&mmapstring_lock);
85   if (mmapstring_hashtable == NULL) {
86     mmapstring_hashtable_init();
87   }
88   ht = mmapstring_hashtable;
89   
90   if (ht == NULL) {
91     pthread_mutex_unlock(&mmapstring_lock);
92     return -1;
93   }
94   
95   key.data = &string->str;
96   key.len = sizeof(string->str);
97   data.data = string;
98   data.len = 0;
99   
100   r = chash_set(mmapstring_hashtable, &key, &data, NULL);
101   pthread_mutex_unlock(&mmapstring_lock);
102   
103   if (r < 0)
104     return r;
105
106   return 0;
107 }
108
109 int mmap_string_unref(char * str)
110 {
111   MMAPString * string;
112   chash * ht;
113   chashdatum key;
114   chashdatum data;
115   int r;
116
117   pthread_mutex_lock(&mmapstring_lock);
118   ht = mmapstring_hashtable;
119   
120   if (ht == NULL) {
121     pthread_mutex_unlock(&mmapstring_lock);
122     return -1;
123   }
124   
125   key.data = &str;
126   key.len = sizeof(str);
127
128   r = chash_get(ht, &key, &data);
129   if (r < 0)
130     string = NULL;
131   else
132     string = data.data;
133   
134   if (string != NULL) {
135     chash_delete(ht, &key, NULL);
136     if (chash_count(ht) == 0) {
137       chash_free(ht);
138       mmapstring_hashtable = NULL;
139     }
140   }
141   
142   pthread_mutex_unlock(&mmapstring_lock);
143
144   if (string != NULL) {
145     mmap_string_free(string);
146     return 0;
147   }
148   else
149     return -1;
150 }
151
152
153
154 /* MMAPString */
155
156 #define MY_MAXSIZE ((size_t) -1)
157
158 static inline size_t
159 nearest_power (size_t base, size_t num)    
160 {
161   if (num > MY_MAXSIZE / 2) {
162     return MY_MAXSIZE;
163   }
164   else {
165     size_t n = base;
166     
167     while (n < num)
168       n <<= 1;
169     
170     return n;
171   }
172 }
173
174 void mmap_string_set_ceil(size_t ceil)
175 {
176   mmap_string_ceil = ceil;
177 }
178
179 /* Strings.
180  */
181
182 static MMAPString * mmap_string_realloc_file(MMAPString * string)
183 {
184   char * data;
185
186   if (string->fd == -1) {
187     char tmpfilename[PATH_MAX];
188     int fd;
189
190     * tmpfilename = 0;
191     strcat(tmpfilename, tmpdir);
192     strcat(tmpfilename, "/libetpan-mmapstring-XXXXXX");
193
194     fd = mkstemp(tmpfilename);
195     if (fd == -1)
196       return NULL;
197
198     if (unlink(tmpfilename) == -1) {
199       close(fd);
200       return NULL;
201     }
202
203     if (ftruncate(fd, string->allocated_len) == -1) {
204       close(fd);
205       return NULL;
206     }
207
208     data = mmap(NULL, string->allocated_len, PROT_WRITE | PROT_READ,
209                 MAP_SHARED, fd, 0);
210
211     if (data == MAP_FAILED) {
212       close(fd);
213       return NULL;
214     }
215
216     if (string->str != NULL)
217       memcpy(data, string->str, string->len + 1);
218
219     string->fd = fd;
220     string->mmapped_size = string->allocated_len;
221     free(string->str);
222     string->str = data;
223   }
224   else {
225     if (munmap(string->str, string->mmapped_size) == -1)
226       return NULL;
227
228     if (ftruncate(string->fd, string->allocated_len) == -1)
229       return NULL;
230     
231     data = mmap(NULL, string->allocated_len, PROT_WRITE | PROT_READ,
232                 MAP_SHARED, string->fd, 0);
233
234     if (data == MAP_FAILED)
235       return NULL;
236
237     string->mmapped_size = string->allocated_len;
238     string->str = data;
239   }
240   
241   return string;
242 }
243
244 static MMAPString * mmap_string_realloc_memory(MMAPString * string)
245 {
246   char * tmp;
247
248   tmp =  realloc (string->str, string->allocated_len);
249   
250   if (tmp == NULL)
251     string = NULL;
252   else
253     string->str = tmp;
254
255   return string;
256 }
257
258 static MMAPString *
259 mmap_string_maybe_expand (MMAPString* string,
260                           size_t len) 
261 {
262   if (string->len + len >= string->allocated_len)
263     {
264       size_t old_size;
265       MMAPString * newstring;
266
267       old_size = string->allocated_len;
268
269       string->allocated_len = nearest_power (1, string->len + len + 1);
270       
271 #ifndef MMAP_UNAVAILABLE
272       if (string->allocated_len > mmap_string_ceil)
273         newstring = mmap_string_realloc_file(string);
274       else {
275 #endif
276         newstring = mmap_string_realloc_memory(string);
277 #ifndef MMAP_UNAVAILABLE
278         if (newstring == NULL)
279           newstring = mmap_string_realloc_file(string);
280       }
281 #endif
282
283       if (newstring == NULL)
284         string->allocated_len = old_size;
285     }
286
287   return string;
288 }
289
290 MMAPString*
291 mmap_string_sized_new (size_t dfl_size)
292 {
293   MMAPString *string;
294  
295   string = malloc(sizeof(* string));
296   if (string == NULL)
297     return NULL;
298
299   string->allocated_len = 0;
300   string->len   = 0;
301   string->str   = NULL;
302   string->fd    = -1;
303   string->mmapped_size = 0;
304
305   if (mmap_string_maybe_expand (string, MAX (dfl_size, 2)) == NULL)
306     return NULL;
307
308   string->str[0] = 0;
309
310   return string;
311 }
312
313 MMAPString*
314 mmap_string_new (const char *init)
315 {
316   MMAPString *string;
317
318   string = mmap_string_sized_new (init ? strlen (init) + 2 : 2);
319   if (string == NULL)
320     return NULL;
321
322   if (init)
323     mmap_string_append (string, init);
324
325   return string;
326 }
327
328 MMAPString*
329 mmap_string_new_len (const char *init,
330                      size_t len)    
331 {
332   MMAPString *string;
333
334   if (len <= 0)
335     return mmap_string_new (init);
336   else
337     {
338       string = mmap_string_sized_new (len);
339       
340       if (init)
341         mmap_string_append_len (string, init, len);
342       
343       return string;
344     }
345 }
346
347 void
348 mmap_string_free (MMAPString *string)
349 {
350   if (string == NULL)
351     return;
352
353   if (string->fd != -1) {
354     munmap(string->str, string->mmapped_size);
355     close(string->fd);
356   }
357   else {
358     free (string->str);
359   }
360   free(string);
361 }
362
363 MMAPString*
364 mmap_string_assign (MMAPString     *string,
365                     const char *rval)
366 {
367   mmap_string_truncate (string, 0);
368   if (mmap_string_append (string, rval) == NULL)
369     return NULL;
370
371   return string;
372 }
373
374 MMAPString*
375 mmap_string_truncate (MMAPString *string,
376                       size_t    len)    
377 {
378   string->len = MIN (len, string->len);
379   string->str[string->len] = 0;
380
381   return string;
382 }
383
384 /**
385  * mmap_string_set_size:
386  * @string: a #MMAPString
387  * @len: the new length
388  * 
389  * Sets the length of a #MMAPString. If the length is less than
390  * the current length, the string will be truncated. If the
391  * length is greater than the current length, the contents
392  * of the newly added area are undefined. (However, as
393  * always, string->str[string->len] will be a nul byte.) 
394  * 
395  * Return value: @string
396  **/
397 MMAPString*
398 mmap_string_set_size (MMAPString *string,
399                       size_t    len)    
400 {
401   if (len >= string->allocated_len)
402     if (mmap_string_maybe_expand (string, len - string->len) == NULL)
403       return NULL;
404   
405   string->len = len;
406   string->str[len] = 0;
407
408   return string;
409 }
410
411 /*
412 static int in_mapped_zone(MMAPString * string, char * val)
413 {
414   return (val >= string->str) && (val < string->str + string->mmapped_size);
415 }
416 */
417
418 MMAPString*
419 mmap_string_insert_len (MMAPString     *string,
420                         size_t       pos,    
421                         const char *val,
422                         size_t       len)    
423 {
424   if (mmap_string_maybe_expand (string, len) == NULL)
425     return NULL;
426     
427   if (pos < string->len)
428     memmove (string->str + pos + len, string->str + pos, string->len - pos);
429
430   /* insert the new string */
431   memmove (string->str + pos, val, len);
432
433   string->len += len;
434
435   string->str[string->len] = 0;
436
437   return string;
438 }
439
440 MMAPString*
441 mmap_string_append (MMAPString     *string,
442                     const char *val)
443 {  
444   return mmap_string_insert_len (string, string->len, val, strlen(val));
445 }
446
447 MMAPString*
448 mmap_string_append_len (MMAPString       *string,
449                         const char *val,
450                         size_t       len)    
451 {
452   return mmap_string_insert_len (string, string->len, val, len);
453 }
454
455 MMAPString*
456 mmap_string_append_c (MMAPString *string,
457                       char    c)
458 {
459   return mmap_string_insert_c (string, string->len, c);
460 }
461
462 MMAPString*
463 mmap_string_prepend (MMAPString     *string,
464                      const char *val)
465 {
466   return mmap_string_insert_len (string, 0, val, strlen(val));
467 }
468
469 MMAPString*
470 mmap_string_prepend_len (MMAPString       *string,
471                          const char *val,
472                          size_t       len)    
473 {
474   return mmap_string_insert_len (string, 0, val, len);
475 }
476
477 MMAPString*
478 mmap_string_prepend_c (MMAPString *string,
479                        char    c)
480 {  
481   return mmap_string_insert_c (string, 0, c);
482 }
483
484 MMAPString*
485 mmap_string_insert (MMAPString     *string,
486                     size_t       pos,    
487                     const char *val)
488 {
489   return mmap_string_insert_len (string, pos, val, strlen(val));
490 }
491
492 MMAPString*
493 mmap_string_insert_c (MMAPString *string,
494                       size_t   pos,    
495                       char    c)
496 {
497   if (mmap_string_maybe_expand (string, 1) == NULL)
498     return NULL;
499   
500   /* If not just an append, move the old stuff */
501   if (pos < string->len)
502     memmove (string->str + pos + 1, string->str + pos, string->len - pos);
503
504   string->str[pos] = c;
505
506   string->len += 1;
507
508   string->str[string->len] = 0;
509
510   return string;
511 }
512
513 MMAPString*
514 mmap_string_erase (MMAPString *string,
515                    size_t    pos,    
516                    size_t    len)    
517 {
518   if ((pos + len) < string->len)
519     memmove (string->str + pos, string->str + pos + len,
520              string->len - (pos + len));
521   
522   string->len -= len;
523   
524   string->str[string->len] = 0;
525
526   return string;
527 }