mutt stable branch with some hacks
at master 1085 lines 22 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 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}