mutt stable branch with some hacks
at jcs 975 lines 19 kB view raw
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}