2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4 * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
6 * This file is part of mutt-ng, see http://www.muttng.org/.
7 * It's licensed under the GNU General Public License,
8 * please see the file GPL in the top level source directory.
19 #include "mutt_curses.h"
29 #include "imap/mx_imap.h"
32 #include "mutt_crypt.h"
37 #include "lib/debug.h"
48 #include <sys/types.h>
51 BODY *mutt_new_body (void)
53 BODY *p = (BODY *) mem_calloc (1, sizeof (BODY));
55 p->disposition = DISPATTACH;
61 /* Modified by blong to accept a "suggestion" for file name. If
62 * that file exists, then construct one with unique name but
63 * keep any extension. This might fail, I guess.
64 * Renamed to mutt_adv_mktemp so I only have to change where it's
65 * called, and not all possible cases.
67 void mutt_adv_mktemp (char *s, size_t l)
69 char buf[_POSIX_PATH_MAX];
70 char tmp[_POSIX_PATH_MAX];
75 strfcpy (buf, NONULL (Tempdir), sizeof (buf));
76 mutt_expand_path (buf, sizeof (buf));
78 snprintf (s, l, "%s/muttXXXXXX", buf);
82 strfcpy (tmp, s, sizeof (tmp));
83 mutt_sanitize_filename (tmp, 1);
84 snprintf (s, l, "%s/%s", buf, tmp);
85 if (lstat (s, &sb) == -1 && errno == ENOENT)
87 if ((period = strrchr (tmp, '.')) != NULL)
89 snprintf (s, l, "%s/%s.XXXXXX", buf, tmp);
94 strfcpy (s + sl, period, l - sl);
99 /* create a send-mode duplicate from a receive-mode body */
101 int mutt_copy_body (FILE * fp, BODY ** tgt, BODY * src)
103 char tmp[_POSIX_PATH_MAX];
106 PARAMETER *par, **ppar;
112 strfcpy (tmp, src->filename, sizeof (tmp));
119 mutt_adv_mktemp (tmp, sizeof (tmp));
120 if (mutt_save_attachment (fp, src, tmp, 0, NULL) == -1)
123 *tgt = mutt_new_body ();
126 memcpy (b, src, sizeof (BODY));
130 b->filename = str_dup (tmp);
131 b->use_disp = use_disp;
134 if (mutt_is_text_part (b))
137 b->xtype = str_dup (b->xtype);
138 b->subtype = str_dup (b->subtype);
139 b->form_name = str_dup (b->form_name);
140 b->filename = str_dup (b->filename);
141 b->d_filename = str_dup (b->d_filename);
142 b->description = str_dup (b->description);
145 * we don't seem to need the HEADER structure currently.
146 * XXX - this may change in the future
152 /* copy parameters */
153 for (par = b->parameter, ppar = &b->parameter; par;
154 ppar = &(*ppar)->next, par = par->next) {
155 *ppar = mutt_new_parameter ();
156 (*ppar)->attribute = str_dup (par->attribute);
157 (*ppar)->value = str_dup (par->value);
160 mutt_stamp_attachment (b);
167 void mutt_free_body (BODY ** p)
176 mutt_free_parameter (&b->parameter);
177 if (b->unlink && b->filename) {
178 debug_print (1, ("unlinking %s.\n", b->filename));
179 unlink (b->filename);
181 else if (b->filename)
182 debug_print (1, ("not unlinking %s.\n", b->filename));
184 mem_free (&b->filename);
185 mem_free (&b->content);
186 mem_free (&b->xtype);
187 mem_free (&b->subtype);
188 mem_free (&b->description);
189 mem_free (&b->form_name);
192 /* Don't free twice (b->hdr->content = b->parts) */
193 b->hdr->content = NULL;
194 mutt_free_header (&b->hdr);
198 mutt_free_body (&b->parts);
206 void mutt_free_parameter (PARAMETER ** p)
212 mem_free (&t->attribute);
213 mem_free (&t->value);
221 HEADER *mutt_dup_header (HEADER * h)
225 hnew = mutt_new_header ();
226 memcpy (hnew, h, sizeof (HEADER));
230 void mutt_free_header (HEADER ** h)
234 mutt_free_envelope (&(*h)->env);
235 mutt_free_body (&(*h)->content);
236 mem_free (&(*h)->maildir_flags);
237 mem_free (&(*h)->tree);
238 mem_free (&(*h)->path);
240 mutt_free_list (&(*h)->chain);
242 #if defined USE_POP || defined USE_IMAP || defined USE_NNTP
243 mem_free (&(*h)->data);
248 /* returns true if the header contained in "s" is in list "t" */
249 int mutt_matches_ignore (const char *s, LIST * t)
251 for (; t; t = t->next) {
252 if (!ascii_strncasecmp (s, t->data, str_len (t->data))
259 /* prepend the path part of *path to *link */
260 void mutt_expand_link (char *newpath, const char *path, const char *link)
262 const char *lb = NULL;
265 /* link is full path */
267 strfcpy (newpath, link, _POSIX_PATH_MAX);
271 if ((lb = strrchr (path, '/')) == NULL) {
272 /* no path in link */
273 strfcpy (newpath, link, _POSIX_PATH_MAX);
278 memcpy (newpath, path, len);
279 strfcpy (newpath + len, link, _POSIX_PATH_MAX - len);
282 char *mutt_expand_path (char *s, size_t slen)
284 return _mutt_expand_path (s, slen, 0);
287 char *_mutt_expand_path (char *s, size_t slen, int rx)
289 char p[_POSIX_PATH_MAX] = "";
290 char q[_POSIX_PATH_MAX] = "";
291 char tmp[_POSIX_PATH_MAX];
304 if (*(s + 1) == '/' || *(s + 1) == 0) {
305 strfcpy (p, NONULL (Homedir), sizeof (p));
311 if ((t = strchr (s + 1, '/')))
314 if ((pw = getpwnam (s + 1))) {
315 strfcpy (p, pw->pw_dir, sizeof (p));
324 /* user not found! */
338 /* if folder = imap[s]://host/: don't append slash */
339 if (imap_is_magic (NONULL (Maildir), NULL) == M_IMAP &&
340 Maildir[str_len (Maildir) - 1] == '/')
341 strfcpy (p, NONULL (Maildir), sizeof (p));
344 snprintf (p, sizeof (p), "%s/", NONULL (Maildir));
350 /* elm compatibility, @ expands alias to user name */
357 if ((alias = mutt_lookup_alias (s + 1))) {
358 h = mutt_new_header ();
359 h->env = mutt_new_envelope ();
360 h->env->from = h->env->to = alias;
361 mutt_default_save (p, sizeof (p), h);
362 h->env->from = h->env->to = NULL;
363 mutt_free_header (&h);
364 /* Avoid infinite recursion if the resulting folder starts with '@' */
375 strfcpy (p, NONULL (Inbox), sizeof (p));
382 strfcpy (p, NONULL (Outbox), sizeof (p));
389 if (*(s + 1) == '!') {
390 strfcpy (p, NONULL (LastFolder), sizeof (p));
394 strfcpy (p, NONULL (Spoolfile), sizeof (p));
402 strfcpy (p, NONULL (LastFolder), sizeof (p));
409 strfcpy (p, NONULL (CurrentFolder), sizeof (p));
421 if (rx && *p && !recurse) {
422 mutt_rx_sanitize_string (q, sizeof (q), p);
423 snprintf (tmp, sizeof (tmp), "%s%s", q, tail);
426 snprintf (tmp, sizeof (tmp), "%s%s", p, tail);
428 strfcpy (s, tmp, slen);
435 /* Extract the real name from /etc/passwd's GECOS field.
436 * When set, honor the regular expression in GecosMask,
437 * otherwise assume that the GECOS field is a
438 * comma-separated list.
439 * Replace "&" by a capitalized version of the user's login
443 char *mutt_gecos_name (char *dest, size_t destlen, struct passwd *pw)
445 regmatch_t pat_match[1];
450 if (!pw || !pw->pw_gecos)
453 memset (dest, 0, destlen);
456 if (regexec (GecosMask.rx, pw->pw_gecos, 1, pat_match, 0) == 0)
457 strfcpy (dest, pw->pw_gecos + pat_match[0].rm_so,
458 MIN (pat_match[0].rm_eo - pat_match[0].rm_so + 1, destlen));
460 else if ((p = strchr (pw->pw_gecos, ',')))
461 strfcpy (dest, pw->pw_gecos, MIN (destlen, p - pw->pw_gecos + 1));
463 strfcpy (dest, pw->pw_gecos, destlen);
465 pwnl = str_len (pw->pw_name);
467 for (idx = 0; dest[idx]; idx++) {
468 if (dest[idx] == '&') {
469 memmove (&dest[idx + pwnl], &dest[idx + 1],
470 MAX (destlen - idx - pwnl - 1, 0));
471 memcpy (&dest[idx], pw->pw_name, MIN (destlen - idx - 1, pwnl));
472 dest[idx] = toupper ((unsigned char) dest[idx]);
480 char *mutt_get_parameter (const char *s, PARAMETER * p)
482 for (; p; p = p->next)
483 if (ascii_strcasecmp (s, p->attribute) == 0)
489 void mutt_set_parameter (const char *attribute, const char *value,
495 mutt_delete_parameter (attribute, p);
499 for (q = *p; q; q = q->next) {
500 if (ascii_strcasecmp (attribute, q->attribute) == 0) {
501 str_replace (&q->value, value);
506 q = mutt_new_parameter ();
507 q->attribute = str_dup (attribute);
508 q->value = str_dup (value);
513 void mutt_delete_parameter (const char *attribute, PARAMETER ** p)
517 for (q = *p; q; p = &q->next, q = q->next) {
518 if (ascii_strcasecmp (attribute, q->attribute) == 0) {
521 mutt_free_parameter (&q);
527 /* returns 1 if Mutt can't display this type of data, 0 otherwise */
528 int mutt_needs_mailcap (BODY * m)
533 if (!ascii_strcasecmp ("plain", m->subtype) ||
534 !ascii_strcasecmp ("rfc822-headers", m->subtype) ||
535 !ascii_strcasecmp ("enriched", m->subtype))
539 case TYPEAPPLICATION:
540 if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (m))
542 if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime (m))
554 int mutt_is_text_part (BODY * b)
557 char *s = b->subtype;
559 if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b))
565 if (t == TYPEMESSAGE) {
566 if (!ascii_strcasecmp ("delivery-status", s))
570 if ((WithCrypto & APPLICATION_PGP) && t == TYPEAPPLICATION) {
571 if (!ascii_strcasecmp ("pgp-keys", s))
578 void mutt_free_envelope (ENVELOPE ** p)
582 rfc822_free_address (&(*p)->return_path);
583 rfc822_free_address (&(*p)->from);
584 rfc822_free_address (&(*p)->to);
585 rfc822_free_address (&(*p)->cc);
586 rfc822_free_address (&(*p)->bcc);
587 rfc822_free_address (&(*p)->sender);
588 rfc822_free_address (&(*p)->reply_to);
589 rfc822_free_address (&(*p)->mail_followup_to);
591 mem_free (&(*p)->list_post);
592 mem_free (&(*p)->subject);
593 /* real_subj is just an offset to subject and shouldn't be freed */
594 mem_free (&(*p)->message_id);
595 mem_free (&(*p)->supersedes);
596 mem_free (&(*p)->date);
597 mem_free (&(*p)->x_label);
598 mem_free (&(*p)->organization);
600 mem_free (&(*p)->newsgroups);
601 mem_free (&(*p)->xref);
602 mem_free (&(*p)->followup_to);
603 mem_free (&(*p)->x_comment_to);
606 mutt_buffer_free (&(*p)->spam);
607 mutt_free_list (&(*p)->references);
608 mutt_free_list (&(*p)->in_reply_to);
609 mutt_free_list (&(*p)->userhdrs);
613 /* move all the headers from extra not present in base into base */
614 void mutt_merge_envelopes(ENVELOPE* base, ENVELOPE** extra)
616 /* copies each existing element if necessary, and sets the element
617 * to NULL in the source so that mutt_free_envelope doesn't leave us
618 * with dangling pointers. */
619 #define MOVE_ELEM(h) if (!base->h) { base->h = (*extra)->h; (*extra)->h = NULL; }
620 MOVE_ELEM(return_path);
627 MOVE_ELEM(mail_followup_to);
628 MOVE_ELEM(list_post);
629 MOVE_ELEM(message_id);
630 MOVE_ELEM(supersedes);
633 if (!base->refs_changed) {
634 MOVE_ELEM(references);
636 if (!base->irt_changed) {
637 MOVE_ELEM(in_reply_to);
639 /* real_subj is subordinate to subject */
640 if (!base->subject) {
641 base->subject = (*extra)->subject;
642 base->real_subj = (*extra)->real_subj;
643 (*extra)->subject = NULL;
644 (*extra)->real_subj = NULL;
646 /* spam and user headers should never be hashed, and the new envelope may
647 * have better values. Use new versions regardless. */
648 mutt_buffer_free (&base->spam);
649 mutt_free_list (&base->userhdrs);
654 mutt_free_envelope(extra);
657 void _mutt_mktemp (char *s, const char *src, int line)
660 snprintf (s, _POSIX_PATH_MAX, "%s/muttng-%s-%d-%d-%d-%x%x", NONULL (Tempdir),
661 NONULL (Hostname), (int) getuid (), (int) getpid (), Counter++,
662 (unsigned int) rand(), (unsigned int) rand());
663 debug_print (1, ("%s:%d: mutt_mktemp returns \"%s\".\n", src, line, s));
667 void mutt_free_alias (ALIAS ** p)
675 rfc822_free_address (&t->addr);
680 /* collapse the pathname using ~ or = when possible */
681 void mutt_pretty_mailbox (char *s)
687 scheme = url_check_scheme (s);
690 if (scheme == U_IMAP || scheme == U_IMAPS) {
691 imap_pretty_mailbox (s);
696 /* if s is an url, only collapse path component */
697 if (scheme != U_UNKNOWN) {
698 p = strchr (s, ':') + 1;
699 if (!strncmp (p, "//", 2))
700 q = strchr (p + 2, '/');
702 q = strchr (p, '\0');
706 /* first attempt to collapse the pathname */
708 if (*p == '/' && p[1] == '/') {
712 else if (p[0] == '/' && p[1] == '.' && p[2] == '/') {
721 if (str_ncmp (s, Maildir, (len = str_len (Maildir))) == 0 &&
724 memmove (s, s + len, str_len (s + len) + 1);
726 else if (str_ncmp (s, Homedir, (len = str_len (Homedir))) == 0 &&
729 memmove (s, s + len - 1, str_len (s + len - 1) + 1);
733 void mutt_pretty_size (char *s, size_t len, long n)
736 strfcpy (s, "0K", len);
737 else if (n < 10189) /* 0.1K - 9.9K */
738 snprintf (s, len, "%3.1fK", (n < 103) ? 0.1 : n / 1024.0);
739 else if (n < 1023949) { /* 10K - 999K */
740 /* 51 is magic which causes 10189/10240 to be rounded up to 10 */
741 snprintf (s, len, "%ldK", (n + 51) / 1024);
743 else if (n < 10433332) /* 1.0M - 9.9M */
744 snprintf (s, len, "%3.1fM", n / 1048576.0);
747 /* (10433332 + 52428) / 1048576 = 10 */
748 snprintf (s, len, "%ldM", (n + 52428) / 1048576);
752 void mutt_expand_file_fmt (char *dest, size_t destlen, const char *fmt,
755 char tmp[LONG_STRING];
757 mutt_quote_filename (tmp, sizeof (tmp), src);
758 mutt_expand_fmt (dest, destlen, fmt, tmp);
761 void mutt_expand_fmt (char *dest, size_t destlen, const char *fmt,
769 slen = str_len (src);
772 for (p = fmt, d = dest; destlen && *p; p++) {
781 strfcpy (d, src, destlen + 1);
782 d += destlen > slen ? slen : destlen;
783 destlen -= destlen > slen ? slen : destlen;
800 if (!found && destlen > 0) {
801 str_cat (dest, destlen, " ");
802 str_cat (dest, destlen, src);
807 /* return 0 on success, -1 on abort, 1 on error */
808 int mutt_check_overwrite (const char *attname, const char *path,
809 char *fname, size_t flen, int *append,
813 char tmp[_POSIX_PATH_MAX];
816 strfcpy (fname, path, flen);
817 if (access (fname, F_OK) != 0)
819 if (stat (fname, &st) != 0)
821 if (S_ISDIR (st.st_mode)) {
823 switch (mutt_multi_choice
824 (_("File is a directory, save under it? [(y)es, (n)o, (a)ll]"),
827 str_replace (directory, fname);
830 mem_free (directory);
833 mem_free (directory);
836 mem_free (directory);
842 mutt_yesorno (_("File is a directory, save under it?"),
844 return (rc == M_NO) ? 1 : -1;
846 if (!attname || !attname[0]) {
848 if (mutt_get_field (_("File under directory: "), tmp, sizeof (tmp),
849 M_FILE | M_CLEAR) != 0 || !tmp[0])
851 mutt_concat_path (fname, path, tmp, flen);
854 mutt_concat_path (fname, path, mutt_basename (attname), flen);
857 if (*append == 0 && access (fname, F_OK) == 0) {
858 switch (mutt_multi_choice
859 (_("File exists, (o)verwrite, (a)ppend, or (c)ancel?"), _("oac")))
867 *append = M_SAVE_APPEND;
869 case 1: /* overwrite */
870 *append = M_SAVE_OVERWRITE;
877 void mutt_save_path (char *d, size_t dsize, ADDRESS * a)
879 if (a && a->mailbox) {
880 strfcpy (d, a->mailbox, dsize);
881 if (!option (OPTSAVEADDRESS)) {
884 if ((p = strpbrk (d, "%@")))
893 void mutt_safe_path (char *s, size_t l, ADDRESS * a)
897 mutt_save_path (s, l, a);
899 if (*p == '/' || ISSPACE (*p) || !IsPrint ((unsigned char) *p))
903 /* counts how many characters in s can be skipped while none of the
904 * characters of c appears */
905 int mutt_skipchars (const char *s, const char *c)
911 register const char *t = c;
921 return (str_len (p));
924 void mutt_FormatString (char *dest, /* output buffer */
925 size_t destlen, /* output buffer len */
926 const char *src, /* template string */
927 format_t * callback, /* callback for processing */
928 unsigned long data, /* callback data */
930 { /* callback flags */
931 char prefix[SHORT_STRING], buf[LONG_STRING], *cp, *wptr = dest, ch;
932 char ifstring[SHORT_STRING], elsestring[SHORT_STRING];
933 size_t wlen, count, len, col, wid;
936 destlen--; /* save room for the terminal \0 */
937 wlen = (flags & M_FORMAT_ARROWCURSOR && option (OPTARROWCURSOR)) ? 3 : 0;
940 while (*src && wlen < destlen) {
951 flags |= M_FORMAT_OPTIONAL;
955 flags &= ~M_FORMAT_OPTIONAL;
957 /* eat the format string */
960 while (count < sizeof (prefix) &&
961 (isdigit ((unsigned char) *src) || *src == '.' || *src == '-'))
970 break; /* bad format */
972 ch = *src++; /* save the character to switch on */
974 if (flags & M_FORMAT_OPTIONAL) {
976 break; /* bad format */
979 /* eat the `if' part of the string */
982 while (count < sizeof (ifstring) && *src && *src != '?'
989 /* eat the `else' part of the string (optional) */
991 src++; /* skip the & */
994 while (count < sizeof (elsestring) && *src && *src != '?') {
1001 break; /* bad format */
1003 src++; /* move past the trailing `?' */
1006 /* handle generic cases first */
1008 /* right justify to EOL */
1009 ch = *src++; /* pad char */
1010 /* calculate space left on line. if we've already written more data
1011 than will fit on the line, ignore the rest of the line */
1012 if (DrawFullLine || option (OPTSTATUSONTOP))
1013 count = (COLS < destlen ? COLS : destlen);
1016 ((COLS - SidebarWidth) <
1017 destlen ? (COLS - SidebarWidth) : destlen);
1019 count -= col; /* how many columns left on this line */
1020 mutt_FormatString (buf, sizeof (buf), src, callback, data, flags);
1021 wid = str_len (buf);
1023 count -= wid; /* how many chars to pad */
1024 memset (wptr, ch, count);
1028 if (wid + wlen > destlen)
1029 len = destlen - wlen;
1032 memcpy (wptr, buf, len);
1035 col += mutt_strwidth (buf);
1037 break; /* skip rest of input */
1039 else if (ch == '|') {
1044 if (destlen > wlen) {
1045 count = destlen - wlen;
1046 memset (wptr, ch, count);
1049 break; /* skip rest of input */
1055 while (ch == '_' || ch == ':') {
1064 /* use callback function to handle this case */
1066 callback (buf, sizeof (buf), ch, src, prefix, ifstring, elsestring,
1079 if ((len = str_len (buf)) + wlen > destlen)
1080 len = (destlen - wlen > 0) ? (destlen - wlen) : 0;
1082 memcpy (wptr, buf, len);
1085 col += mutt_strwidth (buf);
1088 else if (*src == '\\') {
1117 unsigned int bar = mutt_skipchars (src, "%\\");
1118 char *bar2 = mem_malloc (bar + 1);
1120 strfcpy (bar2, src, bar + 1);
1125 col += mutt_strwidth (bar2);
1132 if (flags & M_FORMAT_MAKEPRINT) {
1133 /* Make sure that the string is printable by changing all non-printable
1134 chars to dots, or spaces for non-printable whitespace */
1135 for (cp = dest; *cp; cp++)
1136 if (!IsPrint (*cp) && !((flags & M_FORMAT_TREE) && (*cp <= M_TREE_MAX)))
1137 *cp = isspace ((unsigned char) *cp) ? ' ' : '.';
1142 /* This function allows the user to specify a command to read stdout from in
1143 place of a normal file. If the last character in the string is a pipe (|),
1144 then we assume it is a commmand to run instead of a normal file. */
1145 FILE *mutt_open_read (const char *path, pid_t * thepid)
1150 int len = str_len (path);
1152 if (path[len - 1] == '|') {
1153 /* read from a pipe */
1155 char *s = str_dup (path);
1159 *thepid = mutt_create_filter (s, NULL, &f, NULL);
1163 if (stat (path, &s) < 0)
1165 if (S_ISDIR (s.st_mode)) {
1169 f = fopen (path, "r");
1175 /* returns 0 if OK to proceed, -1 to abort, 1 to retry */
1176 int mutt_save_confirm (const char *s, struct stat *st)
1178 char tmp[_POSIX_PATH_MAX];
1183 magic = mx_get_magic (s);
1186 if (magic == M_POP) {
1187 mutt_error _("Can't save message to POP mailbox.");
1194 if (magic == M_NNTP) {
1195 mutt_error _("Can't save message to newsserver.");
1201 if (magic > 0 && !mx_access (s, W_OK)) {
1202 if (option (OPTCONFIRMAPPEND) &&
1203 (!TrashPath || (str_cmp (s, TrashPath) != 0))) {
1204 /* if we're appending to the trash, there's no point in asking */
1205 snprintf (tmp, sizeof (tmp), _("Append messages to %s?"), s);
1206 if ((rc = mutt_yesorno (tmp, M_YES)) == M_NO)
1213 if (stat (s, st) != -1) {
1215 mutt_error (_("%s is not a mailbox!"), s);
1221 if (magic != M_IMAP)
1222 #endif /* execute the block unconditionally if we don't use imap */
1227 if (errno == ENOENT) {
1228 if (option (OPTCONFIRMCREATE)) {
1229 snprintf (tmp, sizeof (tmp), _("Create %s?"), s);
1230 if ((rc = mutt_yesorno (tmp, M_YES)) == M_NO)
1243 CLEARLINE (LINES - 1);
1247 void mutt_display_sanitize (char *s)
1255 void mutt_sleep (short s)
1263 /* Decrease a file's modification time by 1 second */
1265 time_t mutt_decrease_mtime (const char *f, struct stat *st)
1267 struct utimbuf utim;
1272 if (stat (f, &_st) == -1)
1277 if ((mtime = st->st_mtime) == time (NULL)) {
1279 utim.actime = mtime;
1280 utim.modtime = mtime;
1287 const char *mutt_make_version (void)
1289 static char vstring[STRING];
1291 snprintf (vstring, sizeof (vstring), "Mutt-ng %s (based on Mutt 1.5.10/%s)",
1292 MUTT_VERSION, ReleaseDate);
1296 void mutt_free_spam_list (SPAM_LIST ** list)
1304 *list = (*list)->next;
1306 mem_free(&p->template);
1311 int mutt_match_spam_list (const char *s, SPAM_LIST * l, char *text, int x)
1313 static regmatch_t *pmatch = NULL;
1314 static int nmatch = 0;
1323 for (; l; l = l->next) {
1324 /* If this pattern needs more matches, expand pmatch. */
1325 if (l->nmatch > nmatch) {
1326 mem_realloc (&pmatch, l->nmatch * sizeof (regmatch_t));
1330 /* Does this pattern match? */
1332 (l->rx->rx, s, (size_t) l->nmatch, (regmatch_t *) pmatch,
1334 debug_print (5, ("%s matches %s\n%d subst", s, l->rx->pattern, l->rx->rx->re_nsub));
1336 /* Copy template into text, with substitutions. */
1337 for (p = l->template; *p;) {
1339 n = atoi (++p); /* find pmatch index */
1340 while (isdigit (*p))
1341 ++p; /* skip subst token */
1342 for (i = pmatch[n].rm_so; (i < pmatch[n].rm_eo) && (tlen < x); i++)
1343 text[tlen++] = s[i];
1346 text[tlen++] = *p++;
1350 debug_print (5, ("\"%s\"\n", text));
1358 int mutt_cmp_header (const HEADER * h1, const HEADER * h2) {
1360 if (h1->received != h2->received ||
1361 h1->date_sent != h2->date_sent ||
1362 h1->content->length != h2->content->length ||
1363 h1->lines != h2->lines ||
1364 h1->zhours != h2->zhours ||
1365 h1->zminutes != h2->zminutes ||
1366 h1->zoccident != h2->zoccident ||
1367 h1->mime != h2->mime ||
1368 !mutt_cmp_env (h1->env, h2->env) ||
1369 !mutt_cmp_body (h1->content, h2->content))
1375 if (h1 == NULL && h2 == NULL)
1382 /* return 1 if address lists are strictly identical */
1383 int mutt_cmp_addr (const ADDRESS * a, const ADDRESS * b)
1386 if (str_cmp (a->mailbox, b->mailbox) ||
1387 str_cmp (a->personal, b->personal))
1399 int mutt_cmp_list (const LIST * a, const LIST * b)
1402 if (str_cmp (a->data, b->data))
1414 int mutt_cmp_env (const ENVELOPE * e1, const ENVELOPE * e2)
1417 if (str_cmp (e1->message_id, e2->message_id) ||
1418 str_cmp (e1->subject, e2->subject) ||
1419 !mutt_cmp_list (e1->references, e2->references) ||
1420 !mutt_cmp_addr (e1->from, e2->from) ||
1421 !mutt_cmp_addr (e1->sender, e2->sender) ||
1422 !mutt_cmp_addr (e1->reply_to, e2->reply_to) ||
1423 !mutt_cmp_addr (e1->to, e2->to) ||
1424 !mutt_cmp_addr (e1->cc, e2->cc) ||
1425 !mutt_cmp_addr (e1->return_path, e2->return_path))
1431 if (e1 == NULL && e2 == NULL)
1438 int mutt_cmp_param (const PARAMETER * p1, const PARAMETER * p2)
1441 if (str_cmp (p1->attribute, p2->attribute) ||
1442 str_cmp (p1->value, p2->value))
1454 int mutt_cmp_body (const BODY * b1, const BODY * b2)
1456 if (b1->type != b2->type ||
1457 b1->encoding != b2->encoding ||
1458 str_cmp (b1->subtype, b2->subtype) ||
1459 str_cmp (b1->description, b2->description) ||
1460 !mutt_cmp_param (b1->parameter, b2->parameter) ||
1461 b1->length != b2->length)