Andreas Krennmair:
[apps/madmutt.git] / lib.c
1 /*
2  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
3  * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
4  * 
5  *     This program is free software; you can redistribute it
6  *     and/or modify it under the terms of the GNU General Public
7  *     License as published by the Free Software Foundation; either
8  *     version 2 of the License, or (at your option) any later
9  *     version.
10  * 
11  *     This program is distributed in the hope that it will be
12  *     useful, but WITHOUT ANY WARRANTY; without even the implied
13  *     warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14  *     PURPOSE.  See the GNU General Public License for more
15  *     details.
16  * 
17  *     You should have received a copy of the GNU General Public
18  *     License along with this program; if not, write to the Free
19  *     Software Foundation, Inc., 59 Temple Place - Suite 330,
20  *     Boston, MA  02111, USA.
21  */ 
22
23 /*
24  * This file used to contain some more functions, namely those
25  * which are now in muttlib.c.  They have been removed, so we have
26  * some of our "standard" functions in external programs, too.
27  */
28
29 #include <string.h>
30 #include <ctype.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <sys/wait.h>
34 #include <errno.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <pwd.h>
38
39 #include "lib.h"
40
41 void mutt_nocurses_error (const char *fmt, ...)
42 {
43   va_list ap;
44
45   va_start (ap, fmt);
46   vfprintf (stderr, fmt, ap);
47   va_end (ap);
48   fputc ('\n', stderr);
49 }
50
51 void *safe_calloc (size_t nmemb, size_t size)
52 {
53   void *p;
54
55   if (!nmemb || !size)
56     return NULL;
57
58   if (((size_t) -1) / nmemb <= size)
59   {
60     mutt_error _("Integer overflow -- can't allocate memory!");
61     sleep (1);
62     mutt_exit (1);
63   }
64   
65   if (!(p = calloc (nmemb, size)))
66   {
67     mutt_error _("Out of memory!");
68     sleep (1);
69     mutt_exit (1);
70   }
71   return p;
72 }
73
74 void *safe_malloc (size_t siz)
75 {
76   void *p;
77
78   if (siz == 0)
79     return 0;
80   if ((p = (void *) malloc (siz)) == 0) /* __MEM_CHECKED__ */
81   {
82     mutt_error _("Out of memory!");
83     sleep (1);
84     mutt_exit (1);
85   }
86   return (p);
87 }
88
89 void safe_realloc (void *ptr, size_t siz)
90 {
91   void *r;
92   void **p = (void **)ptr;
93
94   if (siz == 0)
95   {
96     if (*p)
97     {
98       free (*p);                        /* __MEM_CHECKED__ */
99       *p = NULL;
100     }
101     return;
102   }
103
104   if (*p)
105     r = (void *) realloc (*p, siz);     /* __MEM_CHECKED__ */
106   else
107   {
108     /* realloc(NULL, nbytes) doesn't seem to work under SunOS 4.1.x  --- __MEM_CHECKED__ */
109     r = (void *) malloc (siz);          /* __MEM_CHECKED__ */
110   }
111
112   if (!r)
113   {
114     mutt_error _("Out of memory!");
115     sleep (1);
116     mutt_exit (1);
117   }
118
119   *p = r;
120 }
121
122 void safe_free (void *ptr)
123 {
124   void **p = (void **)ptr;
125   if (*p)
126   {
127     free (*p);                          /* __MEM_CHECKED__ */
128     *p = 0;
129   }
130 }
131
132 int safe_fclose (FILE **f)
133 {
134   int r = 0;
135   
136   if (*f)
137     r = fclose (*f);
138       
139   *f = NULL;
140   return r;
141 }
142
143 char *safe_strdup (const char *s)
144 {
145   char *p;
146   size_t l;
147
148   if (!s || !*s)
149     return 0;
150   l = strlen (s) + 1;
151   p = (char *)safe_malloc (l);
152   memcpy (p, s, l);
153   return (p);
154 }
155
156 void mutt_str_replace (char **p, const char *s)
157 {
158   FREE (p);
159   *p = safe_strdup (s);
160 }
161
162 void mutt_str_adjust (char **p)
163 {
164   if (!p || !*p) return;
165   safe_realloc (p, strlen (*p) + 1);
166 }
167
168 /* convert all characters in the string to lowercase */
169 char *mutt_strlower (char *s)
170 {
171   char *p = s;
172
173   while (*p)
174   {
175     *p = tolower ((unsigned char) *p);
176     p++;
177   }
178
179   return (s);
180 }
181
182 void mutt_unlink (const char *s)
183 {
184   FILE *f;
185   struct stat sb;
186   char buf[2048];
187   
188   if (stat (s, &sb) == 0)
189   {
190     if ((f = fopen (s, "r+")))
191     {
192       unlink (s);
193       memset (buf, 0, sizeof (buf));
194       while (sb.st_size > 0)
195       {
196         fwrite (buf, 1, MIN (sizeof (buf), sb.st_size), f);
197         sb.st_size -= MIN (sizeof (buf), sb.st_size);
198       }
199       fclose (f);
200     }
201   }
202 }
203
204 int mutt_copy_bytes (FILE *in, FILE *out, size_t size)
205 {
206   char buf[2048];
207   size_t chunk;
208
209   while (size > 0)
210   {
211     chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
212     if ((chunk = fread (buf, 1, chunk, in)) < 1)
213       break;
214     if (fwrite (buf, 1, chunk, out) != chunk)
215     {
216       /* dprint (1, (debugfile, "mutt_copy_bytes(): fwrite() returned short byte count\n")); */
217       return (-1);
218     }
219     size -= chunk;
220   }
221
222   return 0;
223 }
224
225 int mutt_copy_stream (FILE *fin, FILE *fout)
226 {
227   size_t l;
228   char buf[LONG_STRING];
229
230   while ((l = fread (buf, 1, sizeof (buf), fin)) > 0)
231   {
232     if (fwrite (buf, 1, l, fout) != l)
233       return (-1);
234   }
235
236   return 0;
237 }
238
239 static int 
240 compare_stat (struct stat *osb, struct stat *nsb)
241 {
242   if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino ||
243       osb->st_rdev != nsb->st_rdev)
244   {
245     return -1;
246   }
247
248   return 0;
249 }
250
251 int safe_symlink(const char *oldpath, const char *newpath)
252 {
253   struct stat osb, nsb;
254
255   if(!oldpath || !newpath)
256     return -1;
257   
258   if(unlink(newpath) == -1 && errno != ENOENT)
259     return -1;
260   
261   if (oldpath[0] == '/')
262   {
263     if (symlink (oldpath, newpath) == -1)
264       return -1;
265   }
266   else
267   {
268     char abs_oldpath[_POSIX_PATH_MAX];
269
270     if ((getcwd (abs_oldpath, sizeof abs_oldpath) == NULL) ||
271         (strlen (abs_oldpath) + 1 + strlen (oldpath) + 1 > sizeof abs_oldpath))
272     return -1;
273   
274     strcat (abs_oldpath, "/");          /* __STRCAT_CHECKED__ */
275     strcat (abs_oldpath, oldpath);      /* __STRCAT_CHECKED__ */
276     if (symlink (abs_oldpath, newpath) == -1)
277       return -1;
278   }
279
280   if(stat(oldpath, &osb) == -1 || stat(newpath, &nsb) == -1
281      || compare_stat(&osb, &nsb) == -1)
282   {
283     unlink(newpath);
284     return -1;
285   }
286   
287   return 0;
288 }
289
290 /* 
291  * This function is supposed to do nfs-safe renaming of files.
292  * 
293  * Warning: We don't check whether src and target are equal.
294  */
295
296 int safe_rename (const char *src, const char *target)
297 {
298   struct stat ssb, tsb;
299
300   if (!src || !target)
301     return -1;
302
303   if (link (src, target) != 0)
304   {
305
306     /*
307      * Coda does not allow cross-directory links, but tells
308      * us it's a cross-filesystem linking attempt.
309      * 
310      * However, the Coda rename call is allegedly safe to use.
311      * 
312      * With other file systems, rename should just fail when 
313      * the files reside on different file systems, so it's safe
314      * to try it here.
315      *
316      */
317
318     if (errno == EXDEV)
319       return rename (src, target);
320     
321     return -1;
322   }
323
324   /*
325    * Stat both links and check if they are equal.
326    */
327   
328   if (stat (src, &ssb) == -1)
329   {
330     return -1;
331   }
332   
333   if (stat (target, &tsb) == -1)
334   {
335     return -1;
336   }
337
338   /* 
339    * pretend that the link failed because the target file
340    * did already exist.
341    */
342
343   if (compare_stat (&ssb, &tsb) == -1)
344   {
345     errno = EEXIST;
346     return -1;
347   }
348
349   /*
350    * Unlink the original link.  Should we really ignore the return
351    * value here? XXX
352    */
353
354   unlink (src);
355
356   return 0;
357 }
358
359 int safe_open (const char *path, int flags)
360 {
361   struct stat osb, nsb;
362   int fd;
363
364   if ((fd = open (path, flags, 0600)) < 0)
365     return fd;
366
367   /* make sure the file is not symlink */
368   if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 ||
369       compare_stat(&osb, &nsb) == -1)
370   {
371 /*    dprint (1, (debugfile, "safe_open(): %s is a symlink!\n", path)); */
372     close (fd);
373     return (-1);
374   }
375
376   return (fd);
377 }
378
379 /* when opening files for writing, make sure the file doesn't already exist
380  * to avoid race conditions.
381  */
382 FILE *safe_fopen (const char *path, const char *mode)
383 {
384   if (mode[0] == 'w')
385   {
386     int fd;
387     int flags = O_CREAT | O_EXCL;
388
389 #ifdef O_NOFOLLOW
390     flags |= O_NOFOLLOW;
391 #endif
392
393     if (mode[1] == '+')
394       flags |= O_RDWR;
395     else
396       flags |= O_WRONLY;
397
398     if ((fd = safe_open (path, flags)) < 0)
399       return (NULL);
400
401     return (fdopen (fd, mode));
402   }
403   else
404     return (fopen (path, mode));
405 }
406
407 static char safe_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
408
409 void mutt_sanitize_filename (char *f, short slash)
410 {
411   if (!f) return;
412
413   for (; *f; f++)
414   {
415     if ((slash && *f == '/') || !strchr (safe_chars, *f))
416       *f = '_';
417   }
418 }
419
420 /* these characters must be escaped in regular expressions */
421
422 static char rx_special_chars[] = "^.[$()|*+?{\\";
423
424 int mutt_rx_sanitize_string (char *dest, size_t destlen, const char *src)
425 {
426   while (*src && --destlen > 2)
427   {
428     if (strchr (rx_special_chars, *src))
429     {
430       *dest++ = '\\';
431       destlen--;
432     }
433     *dest++ = *src++;
434   }
435   
436   *dest = '\0';
437   
438   if (*src)
439     return -1;
440   else
441     return 0;
442 }
443
444 /* Read a line from ``fp'' into the dynamically allocated ``s'',
445  * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
446  * If a line ends with "\", this char and the linefeed is removed,
447  * and the next line is read too.
448  */
449 char *mutt_read_line (char *s, size_t *size, FILE *fp, int *line)
450 {
451   size_t offset = 0;
452   char *ch;
453
454   if (!s)
455   {
456     s = safe_malloc (STRING);
457     *size = STRING;
458   }
459
460   FOREVER
461   {
462     if (fgets (s + offset, *size - offset, fp) == NULL)
463     {
464       FREE (&s);
465       return NULL;
466     }
467     if ((ch = strchr (s + offset, '\n')) != NULL)
468     {
469       (*line)++;
470       *ch = 0;
471       if (ch > s && *(ch - 1) == '\r')
472         *--ch = 0;
473       if (ch == s || *(ch - 1) != '\\')
474         return s;
475       offset = ch - s - 1;
476     }
477     else
478     {
479       int c;
480       c = getc (fp); /* This is kind of a hack. We want to know if the
481                         char at the current point in the input stream is EOF.
482                         feof() will only tell us if we've already hit EOF, not
483                         if the next character is EOF. So, we need to read in
484                         the next character and manually check if it is EOF. */
485       if (c == EOF)
486       {
487         /* The last line of fp isn't \n terminated */
488         (*line)++;
489         return s;
490       }
491       else
492       {
493         ungetc (c, fp); /* undo our dammage */
494         /* There wasn't room for the line -- increase ``s'' */
495         offset = *size - 1; /* overwrite the terminating 0 */
496         *size += STRING;
497         safe_realloc (&s, *size);
498       }
499     }
500   }
501 }
502
503 char *
504 mutt_substrcpy (char *dest, const char *beg, const char *end, size_t destlen)
505 {
506   size_t len;
507
508   len = end - beg;
509   if (len > destlen - 1)
510     len = destlen - 1;
511   memcpy (dest, beg, len);
512   dest[len] = 0;
513   return dest;
514 }
515
516 char *mutt_substrdup (const char *begin, const char *end)
517 {
518   size_t len;
519   char *p;
520
521   if (end)
522     len = end - begin;
523   else
524     len = strlen (begin);
525
526   p = safe_malloc (len + 1);
527   memcpy (p, begin, len);
528   p[len] = 0;
529   return p;
530 }
531
532 /* prepare a file name to survive the shell's quoting rules.
533  * From the Unix programming FAQ by way of Liviu.
534  */
535
536 size_t mutt_quote_filename (char *d, size_t l, const char *f)
537 {
538   size_t i, j = 0;
539
540   if(!f) 
541   {
542     *d = '\0';
543     return 0;
544   }
545
546   /* leave some space for the trailing characters. */
547   l -= 6;
548   
549   d[j++] = '\'';
550   
551   for(i = 0; j < l && f[i]; i++)
552   {
553     if(f[i] == '\'' || f[i] == '`')
554     {
555       d[j++] = '\'';
556       d[j++] = '\\';
557       d[j++] = f[i];
558       d[j++] = '\'';
559     }
560     else
561       d[j++] = f[i];
562   }
563   
564   d[j++] = '\'';
565   d[j]   = '\0';
566   
567   return j;
568 }
569
570 /* NULL-pointer aware string comparison functions */
571
572 int mutt_strcmp(const char *a, const char *b)
573 {
574   return strcmp(NONULL(a), NONULL(b));
575 }
576
577 int mutt_strcasecmp(const char *a, const char *b)
578 {
579   return strcasecmp(NONULL(a), NONULL(b));
580 }
581
582 int mutt_strncmp(const char *a, const char *b, size_t l)
583 {
584   return strncmp(NONULL(a), NONULL(b), l);
585 }
586
587 int mutt_strncasecmp(const char *a, const char *b, size_t l)
588 {
589   return strncasecmp(NONULL(a), NONULL(b), l);
590 }
591
592 size_t mutt_strlen(const char *a)
593 {
594   return a ? strlen (a) : 0;
595 }
596
597 int mutt_strcoll(const char *a, const char *b)
598 {
599   return strcoll(NONULL(a), NONULL(b));
600 }
601
602 const char *mutt_stristr (const char *haystack, const char *needle)
603 {
604   const char *p, *q;
605
606   if (!haystack)
607     return NULL;
608   if (!needle)
609     return (haystack);
610
611   while (*(p = haystack))
612   {
613     for (q = needle;
614          *p && *q &&
615            tolower ((unsigned char) *p) == tolower ((unsigned char) *q);
616          p++, q++)
617       ;
618     if (!*q)
619       return (haystack);
620     haystack++;
621   }
622   return NULL;
623 }
624
625 char *mutt_skip_whitespace (char *p)
626 {
627   SKIPWS (p);
628   return p;
629 }
630
631 void mutt_remove_trailing_ws (char *s)
632 {
633   char *p;
634   
635   for (p = s + mutt_strlen (s) - 1 ; p >= s && ISSPACE (*p) ; p--)
636     *p = 0;
637 }
638
639 char *mutt_concat_path (char *d, const char *dir, const char *fname, size_t l)
640 {
641   const char *fmt = "%s/%s";
642   
643   if (!*fname || (*dir && dir[strlen(dir)-1] == '/'))
644     fmt = "%s%s";
645   
646   snprintf (d, l, fmt, dir, fname);
647   return d;
648 }
649
650 const char *mutt_basename (const char *f)
651 {
652   const char *p = strrchr (f, '/');
653   if (p)
654     return p + 1;
655   else
656     return f;
657 }