mutt stable branch with some hacks
1/*
2 * Copyright (C) 1996-2000,2007,2010 Michael R. Elkins <me@mutt.org>
3 * Copyright (C) 1999-2004,2006-2007 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., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, 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#define _LIB_C 1
30
31#if HAVE_CONFIG_H
32# include "config.h"
33#endif
34
35#include <string.h>
36#include <ctype.h>
37#include <unistd.h>
38#include <stdlib.h>
39#include <sys/wait.h>
40#include <errno.h>
41#include <sys/stat.h>
42#include <fcntl.h>
43#include <pwd.h>
44#include <sys/types.h>
45#include <dirent.h>
46
47#ifdef HAVE_SYSEXITS_H
48#include <sysexits.h>
49#else /* Make sure EX_OK is defined <philiph@pobox.com> */
50#define EX_OK 0
51#endif
52
53#include "lib.h"
54
55
56static const struct sysexits
57{
58 int v;
59 const char *str;
60}
61sysexits_h[] =
62{
63#ifdef EX_USAGE
64 { 0xff & EX_USAGE, "Bad usage." },
65#endif
66#ifdef EX_DATAERR
67 { 0xff & EX_DATAERR, "Data format error." },
68#endif
69#ifdef EX_NOINPUT
70 { 0xff & EX_NOINPUT, "Cannot open input." },
71#endif
72#ifdef EX_NOUSER
73 { 0xff & EX_NOUSER, "User unknown." },
74#endif
75#ifdef EX_NOHOST
76 { 0xff & EX_NOHOST, "Host unknown." },
77#endif
78#ifdef EX_UNAVAILABLE
79 { 0xff & EX_UNAVAILABLE, "Service unavailable." },
80#endif
81#ifdef EX_SOFTWARE
82 { 0xff & EX_SOFTWARE, "Internal error." },
83#endif
84#ifdef EX_OSERR
85 { 0xff & EX_OSERR, "Operating system error." },
86#endif
87#ifdef EX_OSFILE
88 { 0xff & EX_OSFILE, "System file missing." },
89#endif
90#ifdef EX_CANTCREAT
91 { 0xff & EX_CANTCREAT, "Can't create output." },
92#endif
93#ifdef EX_IOERR
94 { 0xff & EX_IOERR, "I/O error." },
95#endif
96#ifdef EX_TEMPFAIL
97 { 0xff & EX_TEMPFAIL, "Deferred." },
98#endif
99#ifdef EX_PROTOCOL
100 { 0xff & EX_PROTOCOL, "Remote protocol error." },
101#endif
102#ifdef EX_NOPERM
103 { 0xff & EX_NOPERM, "Insufficient permission." },
104#endif
105#ifdef EX_CONFIG
106 { 0xff & EX_NOPERM, "Local configuration error." },
107#endif
108 { S_ERR, "Exec error." },
109 { -1, NULL}
110};
111
112void mutt_nocurses_error (const char *fmt, ...)
113{
114 va_list ap;
115
116 va_start (ap, fmt);
117 vfprintf (stderr, fmt, ap);
118 va_end (ap);
119 fputc ('\n', stderr);
120}
121
122void *safe_calloc (size_t nmemb, size_t size)
123{
124 void *p;
125
126 if (!nmemb || !size)
127 return NULL;
128
129 if (((size_t) -1) / nmemb <= size)
130 {
131 mutt_error _("Integer overflow -- can't allocate memory!");
132 sleep (1);
133 mutt_exit (1);
134 }
135
136 if (!(p = calloc (nmemb, size)))
137 {
138 mutt_error _("Out of memory!");
139 sleep (1);
140 mutt_exit (1);
141 }
142 return p;
143}
144
145void *safe_malloc (size_t siz)
146{
147 void *p;
148
149 if (siz == 0)
150 return 0;
151 if ((p = (void *) malloc (siz)) == 0) /* __MEM_CHECKED__ */
152 {
153 mutt_error _("Out of memory!");
154 sleep (1);
155 mutt_exit (1);
156 }
157 return (p);
158}
159
160void safe_realloc (void *ptr, size_t siz)
161{
162 void *r;
163 void **p = (void **)ptr;
164
165 if (siz == 0)
166 {
167 if (*p)
168 {
169 free (*p); /* __MEM_CHECKED__ */
170 *p = NULL;
171 }
172 return;
173 }
174
175 if (*p)
176 r = (void *) realloc (*p, siz); /* __MEM_CHECKED__ */
177 else
178 {
179 /* realloc(NULL, nbytes) doesn't seem to work under SunOS 4.1.x --- __MEM_CHECKED__ */
180 r = (void *) malloc (siz); /* __MEM_CHECKED__ */
181 }
182
183 if (!r)
184 {
185 mutt_error _("Out of memory!");
186 sleep (1);
187 mutt_exit (1);
188 }
189
190 *p = r;
191}
192
193void safe_free (void *ptr) /* __SAFE_FREE_CHECKED__ */
194{
195 void **p = (void **)ptr;
196 if (*p)
197 {
198 free (*p); /* __MEM_CHECKED__ */
199 *p = 0;
200 }
201}
202
203int safe_fclose (FILE **f)
204{
205 int r = 0;
206
207 if (*f)
208 r = fclose (*f);
209
210 *f = NULL;
211 return r;
212}
213
214int safe_fsync_close (FILE **f)
215{
216 int r = 0;
217
218 if (*f)
219 {
220 if (fflush (*f) || fsync (fileno (*f)))
221 {
222 r = -1;
223 safe_fclose (f);
224 }
225 else
226 r = safe_fclose (f);
227 }
228
229 return r;
230}
231
232char *safe_strdup (const char *s)
233{
234 char *p;
235 size_t l;
236
237 if (!s || !*s)
238 return 0;
239 l = strlen (s) + 1;
240 p = (char *)safe_malloc (l);
241 memcpy (p, s, l);
242 return (p);
243}
244
245char *safe_strcat (char *d, size_t l, const char *s)
246{
247 char *p = d;
248
249 if (!l)
250 return d;
251
252 l--; /* Space for the trailing '\0'. */
253
254 for (; *d && l; l--)
255 d++;
256 for (; *s && l; l--)
257 *d++ = *s++;
258
259 *d = '\0';
260
261 return p;
262}
263
264char *safe_strncat (char *d, size_t l, const char *s, size_t sl)
265{
266 char *p = d;
267
268 if (!l)
269 return d;
270
271 l--; /* Space for the trailing '\0'. */
272
273 for (; *d && l; l--)
274 d++;
275 for (; *s && l && sl; l--, sl--)
276 *d++ = *s++;
277
278 *d = '\0';
279
280 return p;
281}
282
283
284void mutt_str_replace (char **p, const char *s)
285{
286 FREE (p); /* __FREE_CHECKED__ */
287 *p = safe_strdup (s);
288}
289
290void mutt_str_adjust (char **p)
291{
292 if (!p || !*p) return;
293 safe_realloc (p, strlen (*p) + 1);
294}
295
296/* convert all characters in the string to lowercase */
297char *mutt_strlower (char *s)
298{
299 char *p = s;
300
301 while (*p)
302 {
303 *p = tolower ((unsigned char) *p);
304 p++;
305 }
306
307 return (s);
308}
309
310void mutt_unlink (const char *s)
311{
312 int fd;
313 int flags;
314 FILE *f;
315 struct stat sb, sb2;
316 char buf[2048];
317
318 /* Defend against symlink attacks */
319
320#ifdef O_NOFOLLOW
321 flags = O_RDWR | O_NOFOLLOW;
322#else
323 flags = O_RDWR;
324#endif
325
326 if (lstat (s, &sb) == 0 && S_ISREG(sb.st_mode))
327 {
328 if ((fd = open (s, flags)) < 0)
329 return;
330
331 if ((fstat (fd, &sb2) != 0) || !S_ISREG (sb2.st_mode)
332 || (sb.st_dev != sb2.st_dev) || (sb.st_ino != sb2.st_ino))
333 {
334 close (fd);
335 return;
336 }
337
338 if ((f = fdopen (fd, "r+")))
339 {
340 unlink (s);
341 memset (buf, 0, sizeof (buf));
342 while (sb.st_size > 0)
343 {
344 fwrite (buf, 1, MIN (sizeof (buf), sb.st_size), f);
345 sb.st_size -= MIN (sizeof (buf), sb.st_size);
346 }
347 safe_fclose (&f);
348 }
349 }
350}
351
352int mutt_copy_bytes (FILE *in, FILE *out, size_t size)
353{
354 char buf[2048];
355 size_t chunk;
356
357 while (size > 0)
358 {
359 chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
360 if ((chunk = fread (buf, 1, chunk, in)) < 1)
361 break;
362 if (fwrite (buf, 1, chunk, out) != chunk)
363 {
364 /* dprint (1, (debugfile, "mutt_copy_bytes(): fwrite() returned short byte count\n")); */
365 return (-1);
366 }
367 size -= chunk;
368 }
369
370 return 0;
371}
372
373int mutt_copy_stream (FILE *fin, FILE *fout)
374{
375 size_t l;
376 char buf[LONG_STRING];
377
378 while ((l = fread (buf, 1, sizeof (buf), fin)) > 0)
379 {
380 if (fwrite (buf, 1, l, fout) != l)
381 return (-1);
382 }
383
384 return 0;
385}
386
387int
388compare_stat (struct stat *osb, struct stat *nsb)
389{
390 if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino ||
391 osb->st_rdev != nsb->st_rdev)
392 {
393 return -1;
394 }
395
396 return 0;
397}
398
399int safe_symlink(const char *oldpath, const char *newpath)
400{
401 struct stat osb, nsb;
402
403 if(!oldpath || !newpath)
404 return -1;
405
406 if(unlink(newpath) == -1 && errno != ENOENT)
407 return -1;
408
409 if (oldpath[0] == '/')
410 {
411 if (symlink (oldpath, newpath) == -1)
412 return -1;
413 }
414 else
415 {
416 char abs_oldpath[_POSIX_PATH_MAX];
417
418 if ((getcwd (abs_oldpath, sizeof abs_oldpath) == NULL) ||
419 (strlen (abs_oldpath) + 1 + strlen (oldpath) + 1 > sizeof abs_oldpath))
420 return -1;
421
422 strcat (abs_oldpath, "/"); /* __STRCAT_CHECKED__ */
423 strcat (abs_oldpath, oldpath); /* __STRCAT_CHECKED__ */
424 if (symlink (abs_oldpath, newpath) == -1)
425 return -1;
426 }
427
428 if(stat(oldpath, &osb) == -1 || stat(newpath, &nsb) == -1
429 || compare_stat(&osb, &nsb) == -1)
430 {
431 unlink(newpath);
432 return -1;
433 }
434
435 return 0;
436}
437
438
439
440/*
441 * This function is supposed to do nfs-safe renaming of files.
442 *
443 * Warning: We don't check whether src and target are equal.
444 */
445
446int safe_rename (const char *src, const char *target)
447{
448 struct stat ssb, tsb;
449
450 if (!src || !target)
451 return -1;
452
453 if (link (src, target) != 0)
454 {
455
456 /*
457 * Coda does not allow cross-directory links, but tells
458 * us it's a cross-filesystem linking attempt.
459 *
460 * However, the Coda rename call is allegedly safe to use.
461 *
462 * With other file systems, rename should just fail when
463 * the files reside on different file systems, so it's safe
464 * to try it here.
465 *
466 */
467
468 dprint (1, (debugfile, "safe_rename: link (%s, %s) failed: %s (%d)\n", src, target, strerror (errno), errno));
469
470 /*
471 * FUSE may return ENOSYS. VFAT may return EPERM. FreeBSD's
472 * msdosfs may return EOPNOTSUPP. ENOTSUP can also appear.
473 */
474 if (errno == EXDEV || errno == ENOSYS || errno == EPERM
475#ifdef ENOTSUP
476 || errno == ENOTSUP
477#endif
478#ifdef EOPNOTSUPP
479 || errno == EOPNOTSUPP
480#endif
481 )
482 {
483 dprint (1, (debugfile, "safe_rename: trying rename...\n"));
484 if (rename (src, target) == -1)
485 {
486 dprint (1, (debugfile, "safe_rename: rename (%s, %s) failed: %s (%d)\n", src, target, strerror (errno), errno));
487 return -1;
488 }
489 dprint (1, (debugfile, "safe_rename: rename succeeded.\n"));
490
491 return 0;
492 }
493
494 return -1;
495 }
496
497 /*
498 * Stat both links and check if they are equal.
499 */
500
501 if (lstat (src, &ssb) == -1)
502 {
503 dprint (1, (debugfile, "safe_rename: can't stat %s: %s (%d)\n",
504 src, strerror (errno), errno));
505 return -1;
506 }
507
508 if (lstat (target, &tsb) == -1)
509 {
510 dprint (1, (debugfile, "safe_rename: can't stat %s: %s (%d)\n",
511 src, strerror (errno), errno));
512 return -1;
513 }
514
515 /*
516 * pretend that the link failed because the target file
517 * did already exist.
518 */
519
520 if (compare_stat (&ssb, &tsb) == -1)
521 {
522 dprint (1, (debugfile, "safe_rename: stat blocks for %s and %s diverge; pretending EEXIST.\n", src, target));
523 errno = EEXIST;
524 return -1;
525 }
526
527 /*
528 * Unlink the original link. Should we really ignore the return
529 * value here? XXX
530 */
531
532 if (unlink (src) == -1)
533 {
534 dprint (1, (debugfile, "safe_rename: unlink (%s) failed: %s (%d)\n",
535 src, strerror (errno), errno));
536 }
537
538
539 return 0;
540}
541
542
543/* Create a temporary directory next to a file name */
544
545static int mutt_mkwrapdir (const char *path, char *newfile, size_t nflen,
546 char *newdir, size_t ndlen)
547{
548 const char *basename;
549 char parent[_POSIX_PATH_MAX];
550 char *p;
551
552 strfcpy (parent, NONULL (path), sizeof (parent));
553
554 if ((p = strrchr (parent, '/')))
555 {
556 *p = '\0';
557 basename = p + 1;
558 }
559 else
560 {
561 strfcpy (parent, ".", sizeof (parent));
562 basename = path;
563 }
564
565 snprintf (newdir, ndlen, "%s/%s", parent, ".muttXXXXXX");
566 if (mkdtemp(newdir) == NULL)
567 {
568 dprint(1, (debugfile, "mutt_mkwrapdir: mkdtemp() failed\n"));
569 return -1;
570 }
571
572 if (snprintf (newfile, nflen, "%s/%s", newdir, NONULL(basename)) >= nflen)
573 {
574 rmdir(newdir);
575 dprint(1, (debugfile, "mutt_mkwrapdir: string was truncated\n"));
576 return -1;
577 }
578 return 0;
579}
580
581/* remove a directory and everything under it */
582int mutt_rmtree (const char* path)
583{
584 DIR* dirp;
585 struct dirent* de;
586 char cur[_POSIX_PATH_MAX];
587 struct stat statbuf;
588 int rc = 0;
589
590 if (!(dirp = opendir (path)))
591 {
592 dprint (1, (debugfile, "mutt_rmtree: error opening directory %s\n", path));
593 return -1;
594 }
595 while ((de = readdir (dirp)))
596 {
597 if (!strcmp (".", de->d_name) || !strcmp ("..", de->d_name))
598 continue;
599
600 snprintf (cur, sizeof (cur), "%s/%s", path, de->d_name);
601 /* XXX make nonrecursive version */
602
603 if (stat(cur, &statbuf) == -1)
604 {
605 rc = 1;
606 continue;
607 }
608
609 if (S_ISDIR (statbuf.st_mode))
610 rc |= mutt_rmtree (cur);
611 else
612 rc |= unlink (cur);
613 }
614 closedir (dirp);
615
616 rc |= rmdir (path);
617
618 return rc;
619}
620
621static int mutt_put_file_in_place (const char *path, const char *safe_file, const char *safe_dir)
622{
623 int rv;
624
625 rv = safe_rename (safe_file, path);
626 unlink (safe_file);
627 rmdir (safe_dir);
628 return rv;
629}
630
631int safe_open (const char *path, int flags)
632{
633 struct stat osb, nsb;
634 int fd;
635
636 if (flags & O_EXCL)
637 {
638 char safe_file[_POSIX_PATH_MAX];
639 char safe_dir[_POSIX_PATH_MAX];
640
641 if (mutt_mkwrapdir (path, safe_file, sizeof (safe_file),
642 safe_dir, sizeof (safe_dir)) == -1)
643 return -1;
644
645 if ((fd = open (safe_file, flags, 0600)) < 0)
646 {
647 rmdir (safe_dir);
648 return fd;
649 }
650
651 /* NFS and I believe cygwin do not handle movement of open files well */
652 close (fd);
653 if (mutt_put_file_in_place (path, safe_file, safe_dir) == -1)
654 return -1;
655 }
656
657 if ((fd = open (path, flags & ~O_EXCL, 0600)) < 0)
658 return fd;
659
660 /* make sure the file is not symlink */
661 if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 ||
662 compare_stat(&osb, &nsb) == -1)
663 {
664/* dprint (1, (debugfile, "safe_open(): %s is a symlink!\n", path)); */
665 close (fd);
666 return (-1);
667 }
668
669 return (fd);
670}
671
672/* when opening files for writing, make sure the file doesn't already exist
673 * to avoid race conditions.
674 */
675FILE *safe_fopen (const char *path, const char *mode)
676{
677 if (mode[0] == 'w')
678 {
679 int fd;
680 int flags = O_CREAT | O_EXCL;
681
682#ifdef O_NOFOLLOW
683 flags |= O_NOFOLLOW;
684#endif
685
686 if (mode[1] == '+')
687 flags |= O_RDWR;
688 else
689 flags |= O_WRONLY;
690
691 if ((fd = safe_open (path, flags)) < 0)
692 return (NULL);
693
694 return (fdopen (fd, mode));
695 }
696 else
697 return (fopen (path, mode));
698}
699
700static const char safe_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
701
702void mutt_sanitize_filename (char *f, short slash)
703{
704 if (!f) return;
705
706 for (; *f; f++)
707 {
708 if ((slash && *f == '/') || !strchr (safe_chars, *f))
709 *f = '_';
710 }
711}
712
713/* these characters must be escaped in regular expressions */
714
715static const char rx_special_chars[] = "^.[$()|*+?{\\";
716
717int mutt_rx_sanitize_string (char *dest, size_t destlen, const char *src)
718{
719 while (*src && --destlen > 2)
720 {
721 if (strchr (rx_special_chars, *src))
722 {
723 *dest++ = '\\';
724 destlen--;
725 }
726 *dest++ = *src++;
727 }
728
729 *dest = '\0';
730
731 if (*src)
732 return -1;
733 else
734 return 0;
735}
736
737/* Read a line from ``fp'' into the dynamically allocated ``s'',
738 * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
739 * If a line ends with "\", this char and the linefeed is removed,
740 * and the next line is read too.
741 */
742char *mutt_read_line (char *s, size_t *size, FILE *fp, int *line, int flags)
743{
744 size_t offset = 0;
745 char *ch;
746
747 if (!s)
748 {
749 s = safe_malloc (STRING);
750 *size = STRING;
751 }
752
753 FOREVER
754 {
755 if (fgets (s + offset, *size - offset, fp) == NULL)
756 {
757 FREE (&s);
758 return NULL;
759 }
760 if ((ch = strchr (s + offset, '\n')) != NULL)
761 {
762 if (line)
763 (*line)++;
764 if (flags & MUTT_EOL)
765 return s;
766 *ch = 0;
767 if (ch > s && *(ch - 1) == '\r')
768 *--ch = 0;
769 if (!(flags & MUTT_CONT) || ch == s || *(ch - 1) != '\\')
770 return s;
771 offset = ch - s - 1;
772 }
773 else
774 {
775 int c;
776 c = getc (fp); /* This is kind of a hack. We want to know if the
777 char at the current point in the input stream is EOF.
778 feof() will only tell us if we've already hit EOF, not
779 if the next character is EOF. So, we need to read in
780 the next character and manually check if it is EOF. */
781 if (c == EOF)
782 {
783 /* The last line of fp isn't \n terminated */
784 if (line)
785 (*line)++;
786 return s;
787 }
788 else
789 {
790 ungetc (c, fp); /* undo our damage */
791 /* There wasn't room for the line -- increase ``s'' */
792 offset = *size - 1; /* overwrite the terminating 0 */
793 *size += STRING;
794 safe_realloc (&s, *size);
795 }
796 }
797 }
798}
799
800char *
801mutt_substrcpy (char *dest, const char *beg, const char *end, size_t destlen)
802{
803 size_t len;
804
805 len = end - beg;
806 if (len > destlen - 1)
807 len = destlen - 1;
808 memcpy (dest, beg, len);
809 dest[len] = 0;
810 return dest;
811}
812
813char *mutt_substrdup (const char *begin, const char *end)
814{
815 size_t len;
816 char *p;
817
818 if (end)
819 len = end - begin;
820 else
821 len = strlen (begin);
822
823 p = safe_malloc (len + 1);
824 memcpy (p, begin, len);
825 p[len] = 0;
826 return p;
827}
828
829/* prepare a file name to survive the shell's quoting rules.
830 * From the Unix programming FAQ by way of Liviu.
831 */
832
833size_t mutt_quote_filename (char *d, size_t l, const char *f)
834{
835 size_t i, j = 0;
836
837 if(!f)
838 {
839 *d = '\0';
840 return 0;
841 }
842
843 /* leave some space for the trailing characters. */
844 l -= 6;
845
846 d[j++] = '\'';
847
848 for(i = 0; j < l && f[i]; i++)
849 {
850 if(f[i] == '\'' || f[i] == '`')
851 {
852 d[j++] = '\'';
853 d[j++] = '\\';
854 d[j++] = f[i];
855 d[j++] = '\'';
856 }
857 else
858 d[j++] = f[i];
859 }
860
861 d[j++] = '\'';
862 d[j] = '\0';
863
864 return j;
865}
866
867/* NULL-pointer aware string comparison functions */
868
869int mutt_strcmp(const char *a, const char *b)
870{
871 return strcmp(NONULL(a), NONULL(b));
872}
873
874int mutt_strcasecmp(const char *a, const char *b)
875{
876 return strcasecmp(NONULL(a), NONULL(b));
877}
878
879int mutt_strncmp(const char *a, const char *b, size_t l)
880{
881 return strncmp(NONULL(a), NONULL(b), l);
882}
883
884int mutt_strncasecmp(const char *a, const char *b, size_t l)
885{
886 return strncasecmp(NONULL(a), NONULL(b), l);
887}
888
889size_t mutt_strlen(const char *a)
890{
891 return a ? strlen (a) : 0;
892}
893
894int mutt_strcoll(const char *a, const char *b)
895{
896 return strcoll(NONULL(a), NONULL(b));
897}
898
899const char *mutt_stristr (const char *haystack, const char *needle)
900{
901 const char *p, *q;
902
903 if (!haystack)
904 return NULL;
905 if (!needle)
906 return (haystack);
907
908 while (*(p = haystack))
909 {
910 for (q = needle;
911 *p && *q &&
912 tolower ((unsigned char) *p) == tolower ((unsigned char) *q);
913 p++, q++)
914 ;
915 if (!*q)
916 return (haystack);
917 haystack++;
918 }
919 return NULL;
920}
921
922char *mutt_skip_whitespace (char *p)
923{
924 SKIPWS (p);
925 return p;
926}
927
928void mutt_remove_trailing_ws (char *s)
929{
930 char *p;
931
932 for (p = s + mutt_strlen (s) - 1 ; p >= s && ISSPACE (*p) ; p--)
933 *p = 0;
934}
935
936/*
937 * Write the concatened pathname (dir + "/" + fname) into dst.
938 * The slash is omitted when dir or fname is of 0 length.
939 * Returns NULL on error or a pointer to dst otherwise.
940 */
941char *mutt_concatn_path (char *dst, size_t dstlen,
942 const char *dir, size_t dirlen, const char *fname, size_t fnamelen)
943{
944 size_t req;
945 size_t offset = 0;
946
947 if (dstlen == 0)
948 return NULL; /* probably should not mask errors like this */
949
950 /* size check */
951 req = dirlen + fnamelen + 1; /* +1 for the trailing nul */
952 if (dirlen && fnamelen)
953 req++; /* when both components are non-nul, we add a "/" in between */
954 if (req > dstlen) { /* check for condition where the dst length is too short */
955 /* Two options here:
956 * 1) assert(0) or return NULL to signal error
957 * 2) copy as much of the path as will fit
958 * It doesn't appear that the return value is actually checked anywhere mutt_concat_path()
959 * is called, so we should just copy set dst to nul and let the calling function fail later.
960 */
961 dst[0] = 0; /* safe since we bail out early if dstlen == 0 */
962 return NULL;
963 }
964
965 if (dirlen) { /* when dir is not empty */
966 memcpy(dst, dir, dirlen);
967 offset = dirlen;
968 if (fnamelen)
969 dst[offset++] = '/';
970 }
971 if (fnamelen) { /* when fname is not empty */
972 memcpy(dst + offset, fname, fnamelen);
973 offset += fnamelen;
974 }
975 dst[offset] = 0;
976 return dst;
977}
978
979char *mutt_concat_path (char *d, const char *dir, const char *fname, size_t l)
980{
981 const char *fmt = "%s/%s";
982
983 if (!*fname || (*dir && dir[strlen(dir)-1] == '/'))
984 fmt = "%s%s";
985
986 snprintf (d, l, fmt, dir, fname);
987 return d;
988}
989
990const char *mutt_basename (const char *f)
991{
992 const char *p = strrchr (f, '/');
993 if (p)
994 return p + 1;
995 else
996 return f;
997}
998
999const char *
1000mutt_strsysexit(int e)
1001{
1002 int i;
1003
1004 for(i = 0; sysexits_h[i].str; i++)
1005 {
1006 if(e == sysexits_h[i].v)
1007 break;
1008 }
1009
1010 return sysexits_h[i].str;
1011}
1012
1013void mutt_debug (FILE *fp, const char *fmt, ...)
1014{
1015 va_list ap;
1016 time_t now = time (NULL);
1017 static char buf[23] = "";
1018 static time_t last = 0;
1019
1020 if (now > last)
1021 {
1022 strftime (buf, sizeof (buf), "%Y-%m-%d %H:%M:%S", localtime (&now));
1023 last = now;
1024 }
1025 fprintf (fp, "[%s] ", buf);
1026 va_start (ap, fmt);
1027 vfprintf (fp, fmt, ap);
1028 va_end (ap);
1029}
1030
1031int mutt_atos (const char *str, short *dst)
1032{
1033 int rc;
1034 long res;
1035 short tmp;
1036 short *t = dst ? dst : &tmp;
1037
1038 *t = 0;
1039
1040 if ((rc = mutt_atol (str, &res)) < 0)
1041 return rc;
1042 if ((short) res != res)
1043 return -2;
1044
1045 *t = (short) res;
1046 return 0;
1047}
1048
1049int mutt_atoi (const char *str, int *dst)
1050{
1051 int rc;
1052 long res;
1053 int tmp;
1054 int *t = dst ? dst : &tmp;
1055
1056 *t = 0;
1057
1058 if ((rc = mutt_atol (str, &res)) < 0)
1059 return rc;
1060 if ((int) res != res)
1061 return -2;
1062
1063 *t = (int) res;
1064 return 0;
1065}
1066
1067int mutt_atol (const char *str, long *dst)
1068{
1069 long r;
1070 long *res = dst ? dst : &r;
1071 char *e = NULL;
1072
1073 /* no input: 0 */
1074 if (!str || !*str)
1075 {
1076 *res = 0;
1077 return 0;
1078 }
1079
1080 *res = strtol (str, &e, 10);
1081 if ((*res == LONG_MAX && errno == ERANGE) ||
1082 (e && *e != '\0'))
1083 return -1;
1084 return 0;
1085}