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
310int mutt_mkdir (char *path, mode_t mode)
311{
312 struct stat sb;
313 char *s;
314 int rv = -1;
315
316 if (stat (path, &sb) >= 0)
317 return 0;
318
319 s = path;
320 do
321 {
322 s = strchr (s + 1, '/');
323 if (s)
324 *s = '\0';
325 if (stat (path, &sb) < 0)
326 {
327 if (errno != ENOENT)
328 goto cleanup;
329 if (mkdir (path, mode) < 0)
330 goto cleanup;
331 }
332 if (s)
333 *s = '/';
334 } while (s);
335
336 rv = 0;
337
338cleanup:
339 if (s)
340 *s = '/';
341
342 return rv;
343}
344
345void mutt_unlink (const char *s)
346{
347 int fd;
348 int flags;
349 FILE *f;
350 struct stat sb, sb2;
351 char buf[2048];
352
353 /* Defend against symlink attacks */
354
355#ifdef O_NOFOLLOW
356 flags = O_RDWR | O_NOFOLLOW;
357#else
358 flags = O_RDWR;
359#endif
360
361 if (lstat (s, &sb) == 0 && S_ISREG(sb.st_mode))
362 {
363 if ((fd = open (s, flags)) < 0)
364 return;
365
366 if ((fstat (fd, &sb2) != 0) || !S_ISREG (sb2.st_mode)
367 || (sb.st_dev != sb2.st_dev) || (sb.st_ino != sb2.st_ino))
368 {
369 close (fd);
370 return;
371 }
372
373 if ((f = fdopen (fd, "r+")))
374 {
375 unlink (s);
376 memset (buf, 0, sizeof (buf));
377 while (sb.st_size > 0)
378 {
379 fwrite (buf, 1, MIN (sizeof (buf), sb.st_size), f);
380 sb.st_size -= MIN (sizeof (buf), sb.st_size);
381 }
382 safe_fclose (&f);
383 }
384 }
385}
386
387int mutt_copy_bytes (FILE *in, FILE *out, size_t size)
388{
389 char buf[2048];
390 size_t chunk;
391
392 while (size > 0)
393 {
394 chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
395 if ((chunk = fread (buf, 1, chunk, in)) < 1)
396 break;
397 if (fwrite (buf, 1, chunk, out) != chunk)
398 {
399 /* dprint (1, (debugfile, "mutt_copy_bytes(): fwrite() returned short byte count\n")); */
400 return (-1);
401 }
402 size -= chunk;
403 }
404
405 return 0;
406}
407
408int mutt_copy_stream (FILE *fin, FILE *fout)
409{
410 size_t l;
411 char buf[LONG_STRING];
412
413 while ((l = fread (buf, 1, sizeof (buf), fin)) > 0)
414 {
415 if (fwrite (buf, 1, l, fout) != l)
416 return (-1);
417 }
418
419 return 0;
420}
421
422int
423compare_stat (struct stat *osb, struct stat *nsb)
424{
425 if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino ||
426 osb->st_rdev != nsb->st_rdev)
427 {
428 return -1;
429 }
430
431 return 0;
432}
433
434
435
436/*
437 * This function is supposed to do nfs-safe renaming of files.
438 *
439 * Warning: We don't check whether src and target are equal.
440 */
441
442int safe_rename (const char *src, const char *target)
443{
444 struct stat ssb, tsb;
445 int link_errno;
446
447 if (!src || !target)
448 return -1;
449
450 if (link (src, target) != 0)
451 {
452 link_errno = errno;
453
454 /*
455 * It is historically documented that link can return -1 if NFS
456 * dies after creating the link. In that case, we are supposed
457 * to use stat to check if the link was created.
458 *
459 * Derek Martin notes that some implementations of link() follow a
460 * source symlink. It might be more correct to use stat() on src.
461 * I am not doing so to minimize changes in behavior: the function
462 * used lstat() further below for 20 years without issue, and I
463 * believe was never intended to be used on a src symlink.
464 */
465 if ((lstat (src, &ssb) == 0) &&
466 (lstat (target, &tsb) == 0) &&
467 (compare_stat (&ssb, &tsb) == 0))
468 {
469 dprint (1, (debugfile,
470 "safe_rename: link (%s, %s) reported failure: %s (%d) but actually succeded\n",
471 src, target, strerror (errno), errno));
472 goto success;
473 }
474
475 errno = link_errno;
476
477 /*
478 * Coda does not allow cross-directory links, but tells
479 * us it's a cross-filesystem linking attempt.
480 *
481 * However, the Coda rename call is allegedly safe to use.
482 *
483 * With other file systems, rename should just fail when
484 * the files reside on different file systems, so it's safe
485 * to try it here.
486 *
487 */
488
489 dprint (1, (debugfile, "safe_rename: link (%s, %s) failed: %s (%d)\n", src, target, strerror (errno), errno));
490
491 /*
492 * FUSE may return ENOSYS. VFAT may return EPERM. FreeBSD's
493 * msdosfs may return EOPNOTSUPP. ENOTSUP can also appear.
494 */
495 if (errno == EXDEV || errno == ENOSYS || errno == EPERM
496#ifdef ENOTSUP
497 || errno == ENOTSUP
498#endif
499#ifdef EOPNOTSUPP
500 || errno == EOPNOTSUPP
501#endif
502 )
503 {
504 dprint (1, (debugfile, "safe_rename: trying rename...\n"));
505 if (rename (src, target) == -1)
506 {
507 dprint (1, (debugfile, "safe_rename: rename (%s, %s) failed: %s (%d)\n", src, target, strerror (errno), errno));
508 return -1;
509 }
510 dprint (1, (debugfile, "safe_rename: rename succeeded.\n"));
511
512 return 0;
513 }
514
515 return -1;
516 }
517
518 /*
519 * Remove the compare_stat() check, because it causes problems with maildir on
520 * filesystems that don't properly support hard links, such as
521 * sshfs. The filesystem creates the link, but the resulting file
522 * is given a different inode number by the sshfs layer. This
523 * results in an infinite loop creating links.
524 */
525#if 0
526 /*
527 * Stat both links and check if they are equal.
528 */
529 if (lstat (src, &ssb) == -1)
530 {
531 dprint (1, (debugfile, "safe_rename: can't stat %s: %s (%d)\n",
532 src, strerror (errno), errno));
533 return -1;
534 }
535
536 if (lstat (target, &tsb) == -1)
537 {
538 dprint (1, (debugfile, "safe_rename: can't stat %s: %s (%d)\n",
539 src, strerror (errno), errno));
540 return -1;
541 }
542
543 /*
544 * pretend that the link failed because the target file
545 * did already exist.
546 */
547 if (compare_stat (&ssb, &tsb) == -1)
548 {
549 dprint (1, (debugfile, "safe_rename: stat blocks for %s and %s diverge; pretending EEXIST.\n", src, target));
550 errno = EEXIST;
551 return -1;
552 }
553#endif
554
555success:
556 /*
557 * Unlink the original link. Should we really ignore the return
558 * value here? XXX
559 */
560 if (unlink (src) == -1)
561 {
562 dprint (1, (debugfile, "safe_rename: unlink (%s) failed: %s (%d)\n",
563 src, strerror (errno), errno));
564 }
565
566
567 return 0;
568}
569
570
571static const char safe_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
572
573void mutt_sanitize_filename (char *f, short slash)
574{
575 if (!f) return;
576
577 for (; *f; f++)
578 {
579 if ((slash && *f == '/') || !strchr (safe_chars, *f))
580 *f = '_';
581 }
582}
583
584/* Read a line from ``fp'' into the dynamically allocated ``s'',
585 * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
586 * If a line ends with "\", this char and the linefeed is removed,
587 * and the next line is read too.
588 */
589char *mutt_read_line (char *s, size_t *size, FILE *fp, int *line, int flags)
590{
591 size_t offset = 0;
592 char *ch;
593
594 if (!s)
595 {
596 s = safe_malloc (STRING);
597 *size = STRING;
598 }
599
600 FOREVER
601 {
602 if (fgets (s + offset, *size - offset, fp) == NULL)
603 {
604 FREE (&s);
605 return NULL;
606 }
607 if ((ch = strchr (s + offset, '\n')) != NULL)
608 {
609 if (line)
610 (*line)++;
611 if (flags & MUTT_EOL)
612 return s;
613 *ch = 0;
614 if (ch > s && *(ch - 1) == '\r')
615 *--ch = 0;
616 if (!(flags & MUTT_CONT) || ch == s || *(ch - 1) != '\\')
617 return s;
618 offset = ch - s - 1;
619 }
620 else
621 {
622 int c;
623 c = getc (fp); /* This is kind of a hack. We want to know if the
624 char at the current point in the input stream is EOF.
625 feof() will only tell us if we've already hit EOF, not
626 if the next character is EOF. So, we need to read in
627 the next character and manually check if it is EOF. */
628 if (c == EOF)
629 {
630 /* The last line of fp isn't \n terminated */
631 if (line)
632 (*line)++;
633 return s;
634 }
635 else
636 {
637 ungetc (c, fp); /* undo our damage */
638 /* There wasn't room for the line -- increase ``s'' */
639 offset = *size - 1; /* overwrite the terminating 0 */
640 *size += STRING;
641 safe_realloc (&s, *size);
642 }
643 }
644 }
645}
646
647char *
648mutt_substrcpy (char *dest, const char *beg, const char *end, size_t destlen)
649{
650 size_t len;
651
652 len = end - beg;
653 if (len > destlen - 1)
654 len = destlen - 1;
655 memcpy (dest, beg, len);
656 dest[len] = 0;
657 return dest;
658}
659
660char *mutt_substrdup (const char *begin, const char *end)
661{
662 size_t len;
663 char *p;
664
665 if (end)
666 len = end - begin;
667 else
668 len = strlen (begin);
669
670 p = safe_malloc (len + 1);
671 memcpy (p, begin, len);
672 p[len] = 0;
673 return p;
674}
675
676/* prepare a file name to survive the shell's quoting rules.
677 * From the Unix programming FAQ by way of Liviu.
678 */
679
680size_t mutt_quote_filename (char *d, size_t l, const char *f)
681{
682 size_t i, j = 0;
683
684 if (!f)
685 {
686 *d = '\0';
687 return 0;
688 }
689
690 /* leave some space for the trailing characters. */
691 l -= 6;
692
693 d[j++] = '\'';
694
695 for (i = 0; j < l && f[i]; i++)
696 {
697 if (f[i] == '\'' || f[i] == '`')
698 {
699 d[j++] = '\'';
700 d[j++] = '\\';
701 d[j++] = f[i];
702 d[j++] = '\'';
703 }
704 else
705 d[j++] = f[i];
706 }
707
708 d[j++] = '\'';
709 d[j] = '\0';
710
711 return j;
712}
713
714/* NULL-pointer aware string comparison functions */
715
716int mutt_strcmp(const char *a, const char *b)
717{
718 return strcmp(NONULL(a), NONULL(b));
719}
720
721int mutt_strcasecmp(const char *a, const char *b)
722{
723 return strcasecmp(NONULL(a), NONULL(b));
724}
725
726int mutt_strncmp(const char *a, const char *b, size_t l)
727{
728 return strncmp(NONULL(a), NONULL(b), l);
729}
730
731int mutt_strncasecmp(const char *a, const char *b, size_t l)
732{
733 return strncasecmp(NONULL(a), NONULL(b), l);
734}
735
736size_t mutt_strlen(const char *a)
737{
738 return a ? strlen (a) : 0;
739}
740
741int mutt_strcoll(const char *a, const char *b)
742{
743 return strcoll(NONULL(a), NONULL(b));
744}
745
746const char *mutt_stristr (const char *haystack, const char *needle)
747{
748 const char *p, *q;
749
750 if (!haystack)
751 return NULL;
752 if (!needle)
753 return (haystack);
754
755 while (*(p = haystack))
756 {
757 for (q = needle;
758 *p && *q &&
759 tolower ((unsigned char) *p) == tolower ((unsigned char) *q);
760 p++, q++)
761 ;
762 if (!*q)
763 return (haystack);
764 haystack++;
765 }
766 return NULL;
767}
768
769char *mutt_skip_whitespace (char *p)
770{
771 SKIPWS (p);
772 return p;
773}
774
775void mutt_remove_trailing_ws (char *s)
776{
777 char *p;
778
779 for (p = s + mutt_strlen (s) - 1 ; p >= s && ISSPACE (*p) ; p--)
780 *p = 0;
781}
782
783char *mutt_concat_path (char *d, const char *dir, const char *fname, size_t l)
784{
785 const char *fmt = "%s/%s";
786
787 if (!*fname || (*dir && dir[strlen(dir)-1] == '/'))
788 fmt = "%s%s";
789
790 snprintf (d, l, fmt, dir, fname);
791 return d;
792}
793
794const char *mutt_basename (const char *f)
795{
796 const char *p = strrchr (f, '/');
797 if (p)
798 return p + 1;
799 else
800 return f;
801}
802
803const char *
804mutt_strsysexit(int e)
805{
806 int i;
807
808 for (i = 0; sysexits_h[i].str; i++)
809 {
810 if (e == sysexits_h[i].v)
811 break;
812 }
813
814 return sysexits_h[i].str;
815}
816
817void mutt_debug (FILE *fp, const char *fmt, ...)
818{
819 va_list ap;
820 time_t now = time (NULL);
821 static char buf[23] = "";
822 static time_t last = 0;
823
824 if (now > last)
825 {
826 strftime (buf, sizeof (buf), "%Y-%m-%d %H:%M:%S", localtime (&now));
827 last = now;
828 }
829 fprintf (fp, "[%s] ", buf);
830 va_start (ap, fmt);
831 vfprintf (fp, fmt, ap);
832 va_end (ap);
833}
834
835int mutt_atos (const char *str, short *dst)
836{
837 int rc;
838 long res;
839 short tmp;
840 short *t = dst ? dst : &tmp;
841
842 *t = 0;
843
844 if ((rc = mutt_atol (str, &res)) < 0)
845 return rc;
846 if ((short) res != res)
847 return -2;
848
849 *t = (short) res;
850 return 0;
851}
852
853int mutt_atoi (const char *str, int *dst)
854{
855 int rc;
856 long res;
857 int tmp;
858 int *t = dst ? dst : &tmp;
859
860 *t = 0;
861
862 if ((rc = mutt_atol (str, &res)) < 0)
863 return rc;
864 if ((int) res != res)
865 return -2;
866
867 *t = (int) res;
868 return 0;
869}
870
871int mutt_atol (const char *str, long *dst)
872{
873 long r;
874 long *res = dst ? dst : &r;
875 char *e = NULL;
876
877 /* no input: 0 */
878 if (!str || !*str)
879 {
880 *res = 0;
881 return 0;
882 }
883
884 errno = 0;
885 *res = strtol (str, &e, 10);
886 if (e && *e != '\0')
887 return -1;
888 if (errno == ERANGE)
889 return -2;
890 return 0;
891}
892
893/* NOTE: this function's return value breaks with the above three functions.
894 * The imap code lexes uint values out of a stream of characters without
895 * tokenization. The above functions return -1 if there is input beyond
896 * the number.
897 *
898 * returns: 1 - successful conversion, with trailing characters
899 * 0 - successful conversion
900 * -1 - invalid input
901 * -2 - input out of range
902 */
903int mutt_atoui (const char *str, unsigned int *dst)
904{
905 int rc;
906 unsigned long res;
907 unsigned int tmp;
908 unsigned int *t = dst ? dst : &tmp;
909
910 *t = 0;
911
912 if ((rc = mutt_atoul (str, &res)) < 0)
913 return rc;
914 if ((unsigned int) res != res)
915 return -2;
916
917 *t = (unsigned int) res;
918 return rc;
919}
920
921/* NOTE: this function's return value is different from mutt_atol.
922 *
923 * returns: 1 - successful conversion, with trailing characters
924 * 0 - successful conversion
925 * -1 - invalid input
926 */
927int mutt_atoul (const char *str, unsigned long *dst)
928{
929 unsigned long r;
930 unsigned long *res = dst ? dst : &r;
931 char *e = NULL;
932
933 /* no input: 0 */
934 if (!str || !*str)
935 {
936 *res = 0;
937 return 0;
938 }
939
940 errno = 0;
941 *res = strtoul (str, &e, 10);
942 if (*res == ULONG_MAX && errno == ERANGE)
943 return -1;
944 if (e && *e != '\0')
945 return 1;
946 return 0;
947}
948
949/* NOTE: this function's return value is different from mutt_atol.
950 *
951 * returns: 1 - successful conversion, with trailing characters
952 * 0 - successful conversion
953 * -1 - invalid input
954 */
955int mutt_atoull (const char *str, unsigned long long *dst)
956{
957 unsigned long long r;
958 unsigned long long *res = dst ? dst : &r;
959 char *e = NULL;
960
961 /* no input: 0 */
962 if (!str || !*str)
963 {
964 *res = 0;
965 return 0;
966 }
967
968 errno = 0;
969 *res = strtoull (str, &e, 10);
970 if (*res == ULLONG_MAX && errno == ERANGE)
971 return -1;
972 if (e && *e != '\0')
973 return 1;
974 return 0;
975}