mutt stable branch with some hacks
at jcs 3876 lines 92 kB view raw
1/* 2 * Copyright (C) 1996-2002,2010,2013,2016 Michael R. Elkins <me@mutt.org> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 */ 18 19#if HAVE_CONFIG_H 20# include "config.h" 21#endif 22 23#include "version.h" 24#include "mutt.h" 25#include "mapping.h" 26#include "mutt_curses.h" 27#include "mutt_menu.h" 28#include "mutt_regex.h" 29#include "history.h" 30#include "keymap.h" 31#include "mbyte.h" 32#include "charset.h" 33#include "mutt_crypt.h" 34#include "mutt_idna.h" 35#include "group.h" 36 37#if defined(USE_SSL) 38#include "mutt_ssl.h" 39#endif 40 41 42 43#include "mx.h" 44#include "init.h" 45#include "mailbox.h" 46 47#include <ctype.h> 48#include <stdlib.h> 49#include <unistd.h> 50#include <string.h> 51#include <sys/utsname.h> 52#include <errno.h> 53#include <sys/wait.h> 54#include <sys/time.h> 55 56#define CHECK_PAGER \ 57 if ((CurrentMenu == MENU_PAGER) && (idx >= 0) && \ 58 (MuttVars[idx].flags & R_RESORT)) \ 59 { \ 60 snprintf (err->data, err->dsize, "%s", \ 61 _("Not available in this menu.")); \ 62 return (-1); \ 63 } 64 65typedef struct myvar 66{ 67 char *name; 68 char *value; 69 struct myvar* next; 70} myvar_t; 71 72static myvar_t* MyVars; 73 74static int var_to_string (int idx, char* val, size_t len); 75 76static void myvar_set (const char* var, const char* val); 77static const char* myvar_get (const char* var); 78static void myvar_del (const char* var); 79 80extern char **envlist; 81 82static void toggle_quadoption (int opt) 83{ 84 int n = opt/4; 85 int b = (opt % 4) * 2; 86 87 QuadOptions[n] ^= (1 << b); 88} 89 90void set_quadoption (int opt, int flag) 91{ 92 int n = opt/4; 93 int b = (opt % 4) * 2; 94 95 QuadOptions[n] &= ~(0x3 << b); 96 QuadOptions[n] |= (flag & 0x3) << b; 97} 98 99int quadoption (int opt) 100{ 101 int n = opt/4; 102 int b = (opt % 4) * 2; 103 104 return (QuadOptions[n] >> b) & 0x3; 105} 106 107int query_quadoption (int opt, const char *prompt) 108{ 109 int v = quadoption (opt); 110 111 switch (v) 112 { 113 case MUTT_YES: 114 case MUTT_NO: 115 return (v); 116 117 default: 118 v = mutt_yesorno (prompt, (v == MUTT_ASKYES)); 119 mutt_window_clearline (MuttMessageWindow, 0); 120 return (v); 121 } 122 123 /* not reached */ 124} 125 126/* given the variable ``s'', return the index into the rc_vars array which 127 matches, or -1 if the variable is not found. */ 128static int mutt_option_index (char *s) 129{ 130 int i; 131 132 for (i = 0; MuttVars[i].option; i++) 133 if (mutt_strcmp (s, MuttVars[i].option) == 0) 134 return (MuttVars[i].type == DT_SYN ? mutt_option_index ((char *) MuttVars[i].data.p) : i); 135 return (-1); 136} 137 138int mutt_extract_token (BUFFER *dest, BUFFER *tok, int flags) 139{ 140 char ch; 141 char qc = 0; /* quote char */ 142 char *pc; 143 144 mutt_buffer_clear (dest); 145 146 SKIPWS (tok->dptr); 147 while ((ch = *tok->dptr)) 148 { 149 if (!qc) 150 { 151 if ((ISSPACE (ch) && !(flags & MUTT_TOKEN_SPACE)) || 152 (ch == '#' && !(flags & MUTT_TOKEN_COMMENT)) || 153 (ch == '=' && (flags & MUTT_TOKEN_EQUAL)) || 154 (ch == ';' && !(flags & MUTT_TOKEN_SEMICOLON)) || 155 ((flags & MUTT_TOKEN_PATTERN) && strchr ("~%=!|", ch))) 156 break; 157 } 158 159 tok->dptr++; 160 161 if (ch == qc) 162 qc = 0; /* end of quote */ 163 else if (!qc && (ch == '\'' || ch == '"') && !(flags & MUTT_TOKEN_QUOTE)) 164 qc = ch; 165 else if (ch == '\\' && qc != '\'') 166 { 167 if (!*tok->dptr) 168 return -1; /* premature end of token */ 169 switch (ch = *tok->dptr++) 170 { 171 case 'c': 172 case 'C': 173 if (!*tok->dptr) 174 return -1; /* premature end of token */ 175 mutt_buffer_addch (dest, (toupper ((unsigned char) *tok->dptr) 176 - '@') & 0x7f); 177 tok->dptr++; 178 break; 179 case 'r': 180 mutt_buffer_addch (dest, '\r'); 181 break; 182 case 'n': 183 mutt_buffer_addch (dest, '\n'); 184 break; 185 case 't': 186 mutt_buffer_addch (dest, '\t'); 187 break; 188 case 'f': 189 mutt_buffer_addch (dest, '\f'); 190 break; 191 case 'e': 192 mutt_buffer_addch (dest, '\033'); 193 break; 194 default: 195 if (isdigit ((unsigned char) ch) && 196 isdigit ((unsigned char) *tok->dptr) && 197 isdigit ((unsigned char) *(tok->dptr + 1))) 198 { 199 200 mutt_buffer_addch (dest, (ch << 6) + (*tok->dptr << 3) + *(tok->dptr + 1) - 3504); 201 tok->dptr += 2; 202 } 203 else 204 mutt_buffer_addch (dest, ch); 205 } 206 } 207 else if (ch == '^' && (flags & MUTT_TOKEN_CONDENSE)) 208 { 209 if (!*tok->dptr) 210 return -1; /* premature end of token */ 211 ch = *tok->dptr++; 212 if (ch == '^') 213 mutt_buffer_addch (dest, ch); 214 else if (ch == '[') 215 mutt_buffer_addch (dest, '\033'); 216 else if (isalpha ((unsigned char) ch)) 217 mutt_buffer_addch (dest, toupper ((unsigned char) ch) - '@'); 218 else 219 { 220 mutt_buffer_addch (dest, '^'); 221 mutt_buffer_addch (dest, ch); 222 } 223 } 224 else if (ch == '`' && (!qc || qc == '"')) 225 { 226 FILE *fp; 227 pid_t pid; 228 char *cmd, *ptr; 229 size_t expnlen; 230 BUFFER expn; 231 int line = 0; 232 233 pc = tok->dptr; 234 do 235 { 236 if ((pc = strpbrk (pc, "\\`"))) 237 { 238 /* skip any quoted chars */ 239 if (*pc == '\\') 240 pc += 2; 241 } 242 } while (pc && *pc != '`'); 243 if (!pc) 244 { 245 dprint (1, (debugfile, "mutt_get_token: mismatched backticks\n")); 246 return (-1); 247 } 248 cmd = mutt_substrdup (tok->dptr, pc); 249 if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0) 250 { 251 dprint (1, (debugfile, "mutt_get_token: unable to fork command: %s", cmd)); 252 FREE (&cmd); 253 return (-1); 254 } 255 FREE (&cmd); 256 257 tok->dptr = pc + 1; 258 259 /* read line */ 260 mutt_buffer_init (&expn); 261 expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line, 0); 262 safe_fclose (&fp); 263 mutt_wait_filter (pid); 264 265 /* if we got output, make a new string consisting of the shell output 266 plus whatever else was left on the original line */ 267 /* BUT: If this is inside a quoted string, directly add output to 268 * the token */ 269 if (expn.data && qc) 270 { 271 mutt_buffer_addstr (dest, expn.data); 272 FREE (&expn.data); 273 } 274 else if (expn.data) 275 { 276 expnlen = mutt_strlen (expn.data); 277 tok->dsize = expnlen + mutt_strlen (tok->dptr) + 1; 278 ptr = safe_malloc (tok->dsize); 279 memcpy (ptr, expn.data, expnlen); 280 strcpy (ptr + expnlen, tok->dptr); /* __STRCPY_CHECKED__ */ 281 if (tok->destroy) 282 FREE (&tok->data); 283 tok->data = ptr; 284 tok->dptr = ptr; 285 tok->destroy = 1; /* mark that the caller should destroy this data */ 286 ptr = NULL; 287 FREE (&expn.data); 288 } 289 } 290 else if (ch == '$' && (!qc || qc == '"') && (*tok->dptr == '{' || isalpha ((unsigned char) *tok->dptr))) 291 { 292 const char *env = NULL; 293 char *var = NULL; 294 int idx; 295 296 if (*tok->dptr == '{') 297 { 298 tok->dptr++; 299 if ((pc = strchr (tok->dptr, '}'))) 300 { 301 var = mutt_substrdup (tok->dptr, pc); 302 tok->dptr = pc + 1; 303 } 304 } 305 else 306 { 307 for (pc = tok->dptr; isalnum ((unsigned char) *pc) || *pc == '_'; pc++) 308 ; 309 var = mutt_substrdup (tok->dptr, pc); 310 tok->dptr = pc; 311 } 312 if (var) 313 { 314 if ((env = getenv (var)) || (env = myvar_get (var))) 315 mutt_buffer_addstr (dest, env); 316 else if ((idx = mutt_option_index (var)) != -1) 317 { 318 /* expand settable mutt variables */ 319 char val[LONG_STRING]; 320 321 if (var_to_string (idx, val, sizeof (val))) 322 mutt_buffer_addstr (dest, val); 323 } 324 FREE (&var); 325 } 326 } 327 else 328 mutt_buffer_addch (dest, ch); 329 } 330 mutt_buffer_addch (dest, 0); /* terminate the string */ 331 SKIPWS (tok->dptr); 332 return 0; 333} 334 335static void mutt_free_opt (struct option_t* p) 336{ 337 REGEXP* pp; 338 339 switch (p->type & DT_MASK) 340 { 341 case DT_ADDR: 342 rfc822_free_address ((ADDRESS**)p->data.p); 343 break; 344 case DT_RX: 345 pp = (REGEXP*)p->data.p; 346 FREE (&pp->pattern); 347 if (pp->rx) 348 { 349 regfree (pp->rx); 350 FREE (&pp->rx); 351 } 352 break; 353 case DT_PATH: 354 case DT_STR: 355 FREE ((char**)p->data.p); /* __FREE_CHECKED__ */ 356 break; 357 } 358} 359 360/* clean up before quitting */ 361void mutt_free_opts (void) 362{ 363 int i; 364 365 for (i = 0; MuttVars[i].option; i++) 366 mutt_free_opt (MuttVars + i); 367 368 mutt_free_rx_list (&Alternates); 369 mutt_free_rx_list (&UnAlternates); 370 mutt_free_rx_list (&MailLists); 371 mutt_free_rx_list (&UnMailLists); 372 mutt_free_rx_list (&SubscribedLists); 373 mutt_free_rx_list (&UnSubscribedLists); 374 mutt_free_rx_list (&NoSpamList); 375} 376 377static void add_to_list (LIST **list, const char *str) 378{ 379 LIST *t, *last = NULL; 380 381 /* don't add a NULL or empty string to the list */ 382 if (!str || *str == '\0') 383 return; 384 385 /* check to make sure the item is not already on this list */ 386 for (last = *list; last; last = last->next) 387 { 388 if (ascii_strcasecmp (str, last->data) == 0) 389 { 390 /* already on the list, so just ignore it */ 391 last = NULL; 392 break; 393 } 394 if (!last->next) 395 break; 396 } 397 398 if (!*list || last) 399 { 400 t = (LIST *) safe_calloc (1, sizeof (LIST)); 401 t->data = safe_strdup (str); 402 if (last) 403 last->next = t; 404 else 405 *list = t; 406 } 407} 408 409int mutt_add_to_rx_list (RX_LIST **list, const char *s, int flags, BUFFER *err) 410{ 411 RX_LIST *t, *last = NULL; 412 REGEXP *rx; 413 414 if (!s || !*s) 415 return 0; 416 417 if (!(rx = mutt_compile_regexp (s, flags))) 418 { 419 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s); 420 return -1; 421 } 422 423 /* check to make sure the item is not already on this list */ 424 for (last = *list; last; last = last->next) 425 { 426 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) 427 { 428 /* already on the list, so just ignore it */ 429 last = NULL; 430 break; 431 } 432 if (!last->next) 433 break; 434 } 435 436 if (!*list || last) 437 { 438 t = mutt_new_rx_list(); 439 t->rx = rx; 440 if (last) 441 last->next = t; 442 else 443 *list = t; 444 } 445 else /* duplicate */ 446 mutt_free_regexp (&rx); 447 448 return 0; 449} 450 451static int remove_from_replace_list (REPLACE_LIST **list, const char *pat); 452 453static int add_to_replace_list (REPLACE_LIST **list, const char *pat, const char *templ, BUFFER *err) 454{ 455 REPLACE_LIST *t = NULL, *last = NULL; 456 REGEXP *rx; 457 int n; 458 const char *p; 459 460 if (!pat || !*pat || !templ) 461 return 0; 462 463 if (!(rx = mutt_compile_regexp (pat, REG_ICASE))) 464 { 465 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat); 466 return -1; 467 } 468 469 /* check to make sure the item is not already on this list */ 470 for (last = *list; last; last = last->next) 471 { 472 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) 473 { 474 /* Already on the list. Formerly we just skipped this case, but 475 * now we're supporting removals, which means we're supporting 476 * re-adds conceptually. So we probably want this to imply a 477 * removal, then do an add. We can achieve the removal by freeing 478 * the template, and leaving t pointed at the current item. 479 */ 480 t = last; 481 FREE(&t->template); 482 break; 483 } 484 if (!last->next) 485 break; 486 } 487 488 /* If t is set, it's pointing into an extant REPLACE_LIST* that we want to 489 * update. Otherwise we want to make a new one to link at the list's end. 490 */ 491 if (!t) 492 { 493 t = mutt_new_replace_list(); 494 t->rx = rx; 495 if (last) 496 last->next = t; 497 else 498 *list = t; 499 } 500 501 /* Now t is the REPLACE_LIST* that we want to modify. It is prepared. */ 502 t->template = safe_strdup(templ); 503 504 /* Find highest match number in template string */ 505 t->nmatch = 0; 506 for (p = templ; *p;) 507 { 508 if (*p == '%') 509 { 510 n = atoi(++p); 511 if (n > t->nmatch) 512 t->nmatch = n; 513 while (*p && isdigit((int)*p)) 514 ++p; 515 } 516 else 517 ++p; 518 } 519 520 if (t->nmatch > t->rx->rx->re_nsub) 521 { 522 snprintf (err->data, err->dsize, "%s", _("Not enough subexpressions for " 523 "template")); 524 remove_from_replace_list(list, pat); 525 return -1; 526 } 527 528 t->nmatch++; /* match 0 is always the whole expr */ 529 530 return 0; 531} 532 533static int remove_from_replace_list (REPLACE_LIST **list, const char *pat) 534{ 535 REPLACE_LIST *cur, *prev; 536 int nremoved = 0; 537 538 /* Being first is a special case. */ 539 cur = *list; 540 if (!cur) 541 return 0; 542 if (cur->rx && !mutt_strcmp(cur->rx->pattern, pat)) 543 { 544 *list = cur->next; 545 mutt_free_regexp(&cur->rx); 546 FREE(&cur->template); 547 FREE(&cur); 548 return 1; 549 } 550 551 prev = cur; 552 for (cur = prev->next; cur;) 553 { 554 if (!mutt_strcmp(cur->rx->pattern, pat)) 555 { 556 prev->next = cur->next; 557 mutt_free_regexp(&cur->rx); 558 FREE(&cur->template); 559 FREE(&cur); 560 cur = prev->next; 561 ++nremoved; 562 } 563 else 564 cur = cur->next; 565 } 566 567 return nremoved; 568} 569 570 571static void remove_from_list (LIST **l, const char *str) 572{ 573 LIST *p, *last = NULL; 574 575 if (mutt_strcmp ("*", str) == 0) 576 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */ 577 else 578 { 579 p = *l; 580 last = NULL; 581 while (p) 582 { 583 if (ascii_strcasecmp (str, p->data) == 0) 584 { 585 FREE (&p->data); 586 if (last) 587 last->next = p->next; 588 else 589 (*l) = p->next; 590 FREE (&p); 591 } 592 else 593 { 594 last = p; 595 p = p->next; 596 } 597 } 598 } 599} 600 601static void free_mbchar_table (mbchar_table **t) 602{ 603 if (!t || !*t) 604 return; 605 606 FREE (&(*t)->chars); 607 FREE (&(*t)->segmented_str); 608 FREE (&(*t)->orig_str); 609 FREE (t); /* __FREE_CHECKED__ */ 610} 611 612static mbchar_table *parse_mbchar_table (const char *s) 613{ 614 mbchar_table *t; 615 size_t slen, k; 616 mbstate_t mbstate; 617 char *d; 618 619 t = safe_calloc (1, sizeof (mbchar_table)); 620 slen = mutt_strlen (s); 621 if (!slen) 622 return t; 623 624 t->orig_str = safe_strdup (s); 625 /* This could be more space efficient. However, being used on tiny 626 * strings (Tochars and StChars), the overhead is not great. */ 627 t->chars = safe_calloc (slen, sizeof (char *)); 628 d = t->segmented_str = safe_calloc (slen * 2, sizeof (char)); 629 630 memset (&mbstate, 0, sizeof (mbstate)); 631 while (slen && (k = mbrtowc (NULL, s, slen, &mbstate))) 632 { 633 if (k == (size_t)(-1) || k == (size_t)(-2)) 634 { 635 dprint (1, (debugfile, 636 "parse_mbchar_table: mbrtowc returned %d converting %s in %s\n", 637 (k == (size_t)(-1)) ? -1 : -2, 638 s, t->orig_str)); 639 if (k == (size_t)(-1)) 640 memset (&mbstate, 0, sizeof (mbstate)); 641 k = (k == (size_t)(-1)) ? 1 : slen; 642 } 643 644 slen -= k; 645 t->chars[t->len++] = d; 646 while (k--) 647 *d++ = *s++; 648 *d++ = '\0'; 649 } 650 651 return t; 652} 653 654static int parse_unignore (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 655{ 656 do 657 { 658 mutt_extract_token (buf, s, 0); 659 660 /* don't add "*" to the unignore list */ 661 if (strcmp (buf->data, "*")) 662 add_to_list (&UnIgnore, buf->data); 663 664 remove_from_list (&Ignore, buf->data); 665 } 666 while (MoreArgs (s)); 667 668 return 0; 669} 670 671static int parse_ignore (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 672{ 673 do 674 { 675 mutt_extract_token (buf, s, 0); 676 remove_from_list (&UnIgnore, buf->data); 677 add_to_list (&Ignore, buf->data); 678 } 679 while (MoreArgs (s)); 680 681 return 0; 682} 683 684static int parse_list (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 685{ 686 LIST **data = udata.p; 687 do 688 { 689 mutt_extract_token (buf, s, 0); 690 add_to_list (data, buf->data); 691 } 692 while (MoreArgs (s)); 693 694 return 0; 695} 696 697static int parse_echo (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 698{ 699 if (!MoreArgs (s)) 700 { 701 strfcpy (err->data, _("not enough arguments"), err->dsize); 702 return -1; 703 } 704 mutt_extract_token (buf, s, 0); 705 set_option (OPTFORCEREFRESH); 706 mutt_message ("%s", buf->data); 707 unset_option (OPTFORCEREFRESH); 708 mutt_sleep (0); 709 710 return 0; 711} 712 713static void _alternates_clean (void) 714{ 715 int i; 716 if (Context && Context->msgcount) 717 { 718 for (i = 0; i < Context->msgcount; i++) 719 Context->hdrs[i]->recip_valid = 0; 720 } 721} 722 723static int parse_alternates (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 724{ 725 group_context_t *gc = NULL; 726 727 _alternates_clean(); 728 729 do 730 { 731 mutt_extract_token (buf, s, 0); 732 733 if (parse_group_context (&gc, buf, s, err) == -1) 734 goto bail; 735 736 mutt_remove_from_rx_list (&UnAlternates, buf->data); 737 738 if (mutt_add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0) 739 goto bail; 740 741 if (mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0) 742 goto bail; 743 } 744 while (MoreArgs (s)); 745 746 mutt_group_context_destroy (&gc); 747 return 0; 748 749bail: 750 mutt_group_context_destroy (&gc); 751 return -1; 752} 753 754static int parse_unalternates (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 755{ 756 _alternates_clean(); 757 do 758 { 759 mutt_extract_token (buf, s, 0); 760 mutt_remove_from_rx_list (&Alternates, buf->data); 761 762 if (mutt_strcmp (buf->data, "*") && 763 mutt_add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0) 764 return -1; 765 766 } 767 while (MoreArgs (s)); 768 769 return 0; 770} 771 772static int parse_replace_list (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 773{ 774 REPLACE_LIST **list = (REPLACE_LIST **)udata.p; 775 BUFFER templ; 776 777 memset(&templ, 0, sizeof(templ)); 778 779 /* First token is a regexp. */ 780 if (!MoreArgs(s)) 781 { 782 strfcpy(err->data, _("not enough arguments"), err->dsize); 783 return -1; 784 } 785 mutt_extract_token(buf, s, 0); 786 787 /* Second token is a replacement template */ 788 if (!MoreArgs(s)) 789 { 790 strfcpy(err->data, _("not enough arguments"), err->dsize); 791 return -1; 792 } 793 mutt_extract_token(&templ, s, 0); 794 795 if (add_to_replace_list(list, buf->data, templ.data, err) != 0) 796 { 797 FREE(&templ.data); 798 return -1; 799 } 800 FREE(&templ.data); 801 802 return 0; 803} 804 805static int parse_unreplace_list (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 806{ 807 REPLACE_LIST **list = (REPLACE_LIST **)udata.p; 808 809 /* First token is a regexp. */ 810 if (!MoreArgs(s)) 811 { 812 strfcpy(err->data, _("not enough arguments"), err->dsize); 813 return -1; 814 } 815 816 mutt_extract_token(buf, s, 0); 817 818 /* "*" is a special case. */ 819 if (!mutt_strcmp (buf->data, "*")) 820 { 821 mutt_free_replace_list (list); 822 return 0; 823 } 824 825 remove_from_replace_list(list, buf->data); 826 return 0; 827} 828 829 830static void clear_subject_mods (void) 831{ 832 int i; 833 if (Context && Context->msgcount) 834 { 835 for (i = 0; i < Context->msgcount; i++) 836 FREE(&Context->hdrs[i]->env->disp_subj); 837 } 838} 839 840 841static int parse_subjectrx_list (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 842{ 843 int rc; 844 845 rc = parse_replace_list(buf, s, udata, err); 846 if (rc == 0) 847 clear_subject_mods(); 848 return rc; 849} 850 851 852static int parse_unsubjectrx_list (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 853{ 854 int rc; 855 856 rc = parse_unreplace_list(buf, s, udata, err); 857 if (rc == 0) 858 clear_subject_mods(); 859 return rc; 860} 861 862 863static int parse_spam_list (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 864{ 865 BUFFER templ; 866 long data = udata.l; 867 868 mutt_buffer_init (&templ); 869 870 /* Insist on at least one parameter */ 871 if (!MoreArgs(s)) 872 { 873 if (data == MUTT_SPAM) 874 strfcpy(err->data, _("spam: no matching pattern"), err->dsize); 875 else 876 strfcpy(err->data, _("nospam: no matching pattern"), err->dsize); 877 return -1; 878 } 879 880 /* Extract the first token, a regexp */ 881 mutt_extract_token (buf, s, 0); 882 883 /* data should be either MUTT_SPAM or MUTT_NOSPAM. MUTT_SPAM is for spam commands. */ 884 if (data == MUTT_SPAM) 885 { 886 /* If there's a second parameter, it's a template for the spam tag. */ 887 if (MoreArgs(s)) 888 { 889 mutt_extract_token (&templ, s, 0); 890 891 /* Add to the spam list. */ 892 if (add_to_replace_list (&SpamList, buf->data, templ.data, err) != 0) 893 { 894 FREE(&templ.data); 895 return -1; 896 } 897 FREE(&templ.data); 898 } 899 900 /* If not, try to remove from the nospam list. */ 901 else 902 { 903 mutt_remove_from_rx_list(&NoSpamList, buf->data); 904 } 905 906 return 0; 907 } 908 909 /* MUTT_NOSPAM is for nospam commands. */ 910 else if (data == MUTT_NOSPAM) 911 { 912 /* nospam only ever has one parameter. */ 913 914 /* "*" is a special case. */ 915 if (!mutt_strcmp(buf->data, "*")) 916 { 917 mutt_free_replace_list (&SpamList); 918 mutt_free_rx_list (&NoSpamList); 919 return 0; 920 } 921 922 /* If it's on the spam list, just remove it. */ 923 if (remove_from_replace_list(&SpamList, buf->data) != 0) 924 return 0; 925 926 /* Otherwise, add it to the nospam list. */ 927 if (mutt_add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0) 928 return -1; 929 930 return 0; 931 } 932 933 /* This should not happen. */ 934 strfcpy(err->data, "This is no good at all.", err->dsize); 935 return -1; 936} 937 938 939static int parse_unlist (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 940{ 941 LIST **data = udata.p; 942 do 943 { 944 mutt_extract_token (buf, s, 0); 945 /* 946 * Check for deletion of entire list 947 */ 948 if (mutt_strcmp (buf->data, "*") == 0) 949 { 950 mutt_free_list (data); 951 break; 952 } 953 remove_from_list (data, buf->data); 954 } 955 while (MoreArgs (s)); 956 957 return 0; 958} 959 960/* These two functions aren't sidebar-specific, but they are currently only 961 * used by the sidebar_whitelist/unsidebar_whitelist commands. 962 * Putting in an #ifdef to silence an unused function warning when the sidebar 963 * is disabled. 964 */ 965#ifdef USE_SIDEBAR 966static int parse_path_list (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 967{ 968 BUFFER *path; 969 LIST **data = udata.p; 970 971 path = mutt_buffer_pool_get (); 972 do 973 { 974 mutt_extract_token (path, s, 0); 975 mutt_buffer_expand_path (path); 976 add_to_list (data, mutt_b2s (path)); 977 } 978 while (MoreArgs (s)); 979 mutt_buffer_pool_release (&path); 980 981 return 0; 982} 983 984static int parse_path_unlist (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 985{ 986 BUFFER *path; 987 LIST **data = udata.p; 988 989 path = mutt_buffer_pool_get (); 990 do 991 { 992 mutt_extract_token (path, s, 0); 993 /* 994 * Check for deletion of entire list 995 */ 996 if (mutt_strcmp (mutt_b2s (path), "*") == 0) 997 { 998 mutt_free_list (data); 999 break; 1000 } 1001 mutt_buffer_expand_path (path); 1002 remove_from_list (data, mutt_b2s (path)); 1003 } 1004 while (MoreArgs (s)); 1005 mutt_buffer_pool_release (&path); 1006 1007 return 0; 1008} 1009#endif /* USE_SIDEBAR */ 1010 1011static int parse_lists (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 1012{ 1013 group_context_t *gc = NULL; 1014 1015 do 1016 { 1017 mutt_extract_token (buf, s, 0); 1018 1019 if (parse_group_context (&gc, buf, s, err) == -1) 1020 goto bail; 1021 1022 mutt_remove_from_rx_list (&UnMailLists, buf->data); 1023 1024 if (mutt_add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0) 1025 goto bail; 1026 1027 if (mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0) 1028 goto bail; 1029 } 1030 while (MoreArgs (s)); 1031 1032 mutt_group_context_destroy (&gc); 1033 return 0; 1034 1035bail: 1036 mutt_group_context_destroy (&gc); 1037 return -1; 1038} 1039 1040typedef enum group_state_t { 1041 NONE, RX, ADDR 1042} group_state_t; 1043 1044static int parse_group (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 1045{ 1046 group_context_t *gc = NULL; 1047 group_state_t state = NONE; 1048 ADDRESS *addr = NULL; 1049 char *estr = NULL; 1050 long data = udata.l; 1051 1052 do 1053 { 1054 mutt_extract_token (buf, s, 0); 1055 if (parse_group_context (&gc, buf, s, err) == -1) 1056 goto bail; 1057 1058 if (data == MUTT_UNGROUP && !mutt_strcasecmp (buf->data, "*")) 1059 { 1060 if (mutt_group_context_clear (&gc) < 0) 1061 goto bail; 1062 goto out; 1063 } 1064 1065 if (!mutt_strcasecmp (buf->data, "-rx")) 1066 state = RX; 1067 else if (!mutt_strcasecmp (buf->data, "-addr")) 1068 state = ADDR; 1069 else 1070 { 1071 switch (state) 1072 { 1073 case NONE: 1074 snprintf (err->data, err->dsize, _("%sgroup: missing -rx or -addr."), 1075 data == MUTT_UNGROUP ? "un" : ""); 1076 goto bail; 1077 1078 case RX: 1079 if (data == MUTT_GROUP && 1080 mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0) 1081 goto bail; 1082 else if (data == MUTT_UNGROUP && 1083 mutt_group_context_remove_rx (gc, buf->data) < 0) 1084 goto bail; 1085 break; 1086 1087 case ADDR: 1088 if ((addr = mutt_parse_adrlist (NULL, buf->data)) == NULL) 1089 goto bail; 1090 if (mutt_addrlist_to_intl (addr, &estr)) 1091 { 1092 snprintf (err->data, err->dsize, _("%sgroup: warning: bad IDN '%s'.\n"), 1093 data == MUTT_UNGROUP ? "un" : "", estr); 1094 FREE (&estr); 1095 rfc822_free_address (&addr); 1096 goto bail; 1097 } 1098 if (data == MUTT_GROUP) 1099 mutt_group_context_add_adrlist (gc, addr); 1100 else if (data == MUTT_UNGROUP) 1101 mutt_group_context_remove_adrlist (gc, addr); 1102 rfc822_free_address (&addr); 1103 break; 1104 } 1105 } 1106 } while (MoreArgs (s)); 1107 1108out: 1109 mutt_group_context_destroy (&gc); 1110 return 0; 1111 1112bail: 1113 mutt_group_context_destroy (&gc); 1114 return -1; 1115} 1116 1117/* always wise to do what someone else did before */ 1118static void _attachments_clean (void) 1119{ 1120 int i; 1121 if (Context && Context->msgcount) 1122 { 1123 for (i = 0; i < Context->msgcount; i++) 1124 Context->hdrs[i]->attach_valid = 0; 1125 } 1126} 1127 1128static int parse_attach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err) 1129{ 1130 ATTACH_MATCH *a; 1131 LIST *listp, *lastp; 1132 char *p; 1133 char *tmpminor; 1134 int len; 1135 int ret; 1136 1137 /* Find the last item in the list that data points to. */ 1138 lastp = NULL; 1139 dprint(5, (debugfile, "parse_attach_list: ldata = %p, *ldata = %p\n", 1140 (void *)ldata, (void *)*ldata)); 1141 for (listp = *ldata; listp; listp = listp->next) 1142 { 1143 a = (ATTACH_MATCH *)listp->data; 1144 dprint(5, (debugfile, "parse_attach_list: skipping %s/%s\n", 1145 a->major, a->minor)); 1146 lastp = listp; 1147 } 1148 1149 do 1150 { 1151 mutt_extract_token (buf, s, 0); 1152 1153 if (!buf->data || *buf->data == '\0') 1154 continue; 1155 1156 a = safe_malloc(sizeof(ATTACH_MATCH)); 1157 1158 /* some cheap hacks that I expect to remove */ 1159 if (!ascii_strcasecmp(buf->data, "any")) 1160 a->major = safe_strdup("*/.*"); 1161 else if (!ascii_strcasecmp(buf->data, "none")) 1162 a->major = safe_strdup("cheap_hack/this_should_never_match"); 1163 else 1164 a->major = safe_strdup(buf->data); 1165 1166 if ((p = strchr(a->major, '/'))) 1167 { 1168 *p = '\0'; 1169 ++p; 1170 a->minor = p; 1171 } 1172 else 1173 { 1174 a->minor = "unknown"; 1175 } 1176 1177 len = strlen(a->minor); 1178 tmpminor = safe_malloc(len+3); 1179 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */ 1180 tmpminor[0] = '^'; 1181 tmpminor[len+1] = '$'; 1182 tmpminor[len+2] = '\0'; 1183 1184 a->major_int = mutt_check_mime_type(a->major); 1185 ret = REGCOMP(&a->minor_rx, tmpminor, REG_ICASE); 1186 1187 FREE(&tmpminor); 1188 1189 if (ret) 1190 { 1191 regerror(ret, &a->minor_rx, err->data, err->dsize); 1192 FREE(&a->major); 1193 FREE(&a); 1194 return -1; 1195 } 1196 1197 dprint(5, (debugfile, "parse_attach_list: added %s/%s [%d]\n", 1198 a->major, a->minor, a->major_int)); 1199 1200 listp = safe_malloc(sizeof(LIST)); 1201 listp->data = (char *)a; 1202 listp->next = NULL; 1203 if (lastp) 1204 { 1205 lastp->next = listp; 1206 } 1207 else 1208 { 1209 *ldata = listp; 1210 } 1211 lastp = listp; 1212 } 1213 while (MoreArgs (s)); 1214 1215 _attachments_clean(); 1216 return 0; 1217} 1218 1219static int parse_unattach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err) 1220{ 1221 ATTACH_MATCH *a; 1222 LIST *lp, *lastp, *newlp; 1223 char *tmp; 1224 int major; 1225 char *minor; 1226 1227 do 1228 { 1229 mutt_extract_token (buf, s, 0); 1230 1231 if (!ascii_strcasecmp(buf->data, "any")) 1232 tmp = safe_strdup("*/.*"); 1233 else if (!ascii_strcasecmp(buf->data, "none")) 1234 tmp = safe_strdup("cheap_hack/this_should_never_match"); 1235 else 1236 tmp = safe_strdup(buf->data); 1237 1238 if ((minor = strchr(tmp, '/'))) 1239 { 1240 *minor = '\0'; 1241 ++minor; 1242 } 1243 else 1244 { 1245 minor = "unknown"; 1246 } 1247 major = mutt_check_mime_type(tmp); 1248 1249 /* We must do our own walk here because remove_from_list() will only 1250 * remove the LIST->data, not anything pointed to by the LIST->data. */ 1251 lastp = NULL; 1252 for (lp = *ldata; lp; ) 1253 { 1254 a = (ATTACH_MATCH *)lp->data; 1255 dprint(5, (debugfile, "parse_unattach_list: check %s/%s [%d] : %s/%s [%d]\n", 1256 a->major, a->minor, a->major_int, tmp, minor, major)); 1257 if (a->major_int == major && !mutt_strcasecmp(minor, a->minor)) 1258 { 1259 dprint(5, (debugfile, "parse_unattach_list: removed %s/%s [%d]\n", 1260 a->major, a->minor, a->major_int)); 1261 regfree(&a->minor_rx); 1262 FREE(&a->major); 1263 1264 /* Relink backward */ 1265 if (lastp) 1266 lastp->next = lp->next; 1267 else 1268 *ldata = lp->next; 1269 1270 newlp = lp->next; 1271 FREE(&lp->data); /* same as a */ 1272 FREE(&lp); 1273 lp = newlp; 1274 continue; 1275 } 1276 1277 lastp = lp; 1278 lp = lp->next; 1279 } 1280 1281 } 1282 while (MoreArgs (s)); 1283 1284 FREE(&tmp); 1285 _attachments_clean(); 1286 return 0; 1287} 1288 1289static int print_attach_list (LIST *lp, char op, char *name) 1290{ 1291 while (lp) 1292 { 1293 printf("attachments %c%s %s/%s\n", op, name, 1294 ((ATTACH_MATCH *)lp->data)->major, 1295 ((ATTACH_MATCH *)lp->data)->minor); 1296 lp = lp->next; 1297 } 1298 1299 return 0; 1300} 1301 1302 1303static int parse_attachments (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 1304{ 1305 char op, *category; 1306 LIST **listp; 1307 1308 mutt_extract_token(buf, s, 0); 1309 if (!buf->data || *buf->data == '\0') 1310 { 1311 strfcpy(err->data, _("attachments: no disposition"), err->dsize); 1312 return -1; 1313 } 1314 1315 category = buf->data; 1316 op = *category++; 1317 1318 if (op == '?') 1319 { 1320 mutt_endwin (NULL); 1321 fflush (stdout); 1322 printf("\nCurrent attachments settings:\n\n"); 1323 print_attach_list(AttachAllow, '+', "A"); 1324 print_attach_list(AttachExclude, '-', "A"); 1325 print_attach_list(InlineAllow, '+', "I"); 1326 print_attach_list(InlineExclude, '-', "I"); 1327 mutt_any_key_to_continue (NULL); 1328 return 0; 1329 } 1330 1331 if (op != '+' && op != '-') 1332 { 1333 op = '+'; 1334 category--; 1335 } 1336 if (!ascii_strncasecmp(category, "attachment", strlen(category))) 1337 { 1338 if (op == '+') 1339 listp = &AttachAllow; 1340 else 1341 listp = &AttachExclude; 1342 } 1343 else if (!ascii_strncasecmp(category, "inline", strlen(category))) 1344 { 1345 if (op == '+') 1346 listp = &InlineAllow; 1347 else 1348 listp = &InlineExclude; 1349 } 1350 else 1351 { 1352 strfcpy(err->data, _("attachments: invalid disposition"), err->dsize); 1353 return -1; 1354 } 1355 1356 return parse_attach_list(buf, s, listp, err); 1357} 1358 1359/* 1360 * Cleanup a single LIST item who's data is an ATTACH_MATCH 1361 */ 1362static void free_attachments_data (char **data) 1363{ 1364 if (data == NULL || *data == NULL) return; 1365 ATTACH_MATCH *a = (ATTACH_MATCH *) *data; 1366 regfree(&a->minor_rx); 1367 FREE (&a->major); 1368 FREE (data); /*__FREE_CHECKED__*/ 1369} 1370 1371static int parse_unattachments (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 1372{ 1373 char op, *p; 1374 LIST **listp; 1375 1376 mutt_extract_token(buf, s, 0); 1377 if (!buf->data || *buf->data == '\0') 1378 { 1379 strfcpy(err->data, _("unattachments: no disposition"), err->dsize); 1380 return -1; 1381 } 1382 1383 p = buf->data; 1384 op = *p++; 1385 1386 if (op == '*') 1387 { 1388 mutt_free_list_generic(&AttachAllow, free_attachments_data); 1389 mutt_free_list_generic(&AttachExclude, free_attachments_data); 1390 mutt_free_list_generic(&InlineAllow, free_attachments_data); 1391 mutt_free_list_generic(&InlineExclude, free_attachments_data); 1392 _attachments_clean(); 1393 return 0; 1394 } 1395 1396 if (op != '+' && op != '-') 1397 { 1398 op = '+'; 1399 p--; 1400 } 1401 if (!ascii_strncasecmp(p, "attachment", strlen(p))) 1402 { 1403 if (op == '+') 1404 listp = &AttachAllow; 1405 else 1406 listp = &AttachExclude; 1407 } 1408 else if (!ascii_strncasecmp(p, "inline", strlen(p))) 1409 { 1410 if (op == '+') 1411 listp = &InlineAllow; 1412 else 1413 listp = &InlineExclude; 1414 } 1415 else 1416 { 1417 strfcpy(err->data, _("unattachments: invalid disposition"), err->dsize); 1418 return -1; 1419 } 1420 1421 return parse_unattach_list(buf, s, listp, err); 1422} 1423 1424static int parse_unlists (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 1425{ 1426 hash_destroy (&AutoSubscribeCache, NULL); 1427 do 1428 { 1429 mutt_extract_token (buf, s, 0); 1430 mutt_remove_from_rx_list (&SubscribedLists, buf->data); 1431 mutt_remove_from_rx_list (&MailLists, buf->data); 1432 1433 if (mutt_strcmp (buf->data, "*") && 1434 mutt_add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0) 1435 return -1; 1436 } 1437 while (MoreArgs (s)); 1438 1439 return 0; 1440} 1441 1442static int parse_subscribe (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 1443{ 1444 group_context_t *gc = NULL; 1445 1446 do 1447 { 1448 mutt_extract_token (buf, s, 0); 1449 1450 if (parse_group_context (&gc, buf, s, err) == -1) 1451 goto bail; 1452 1453 mutt_remove_from_rx_list (&UnMailLists, buf->data); 1454 mutt_remove_from_rx_list (&UnSubscribedLists, buf->data); 1455 1456 if (mutt_add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0) 1457 goto bail; 1458 if (mutt_add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0) 1459 goto bail; 1460 if (mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0) 1461 goto bail; 1462 } 1463 while (MoreArgs (s)); 1464 1465 mutt_group_context_destroy (&gc); 1466 return 0; 1467 1468bail: 1469 mutt_group_context_destroy (&gc); 1470 return -1; 1471} 1472 1473static int parse_unsubscribe (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 1474{ 1475 hash_destroy (&AutoSubscribeCache, NULL); 1476 do 1477 { 1478 mutt_extract_token (buf, s, 0); 1479 mutt_remove_from_rx_list (&SubscribedLists, buf->data); 1480 1481 if (mutt_strcmp (buf->data, "*") && 1482 mutt_add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0) 1483 return -1; 1484 } 1485 while (MoreArgs (s)); 1486 1487 return 0; 1488} 1489 1490static int parse_unalias (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 1491{ 1492 ALIAS *tmp, *last = NULL; 1493 1494 do 1495 { 1496 mutt_extract_token (buf, s, 0); 1497 1498 if (mutt_strcmp ("*", buf->data) == 0) 1499 { 1500 if (CurrentMenu == MENU_ALIAS) 1501 { 1502 for (tmp = Aliases; tmp ; tmp = tmp->next) 1503 tmp->del = 1; 1504 mutt_set_current_menu_redraw_full (); 1505 } 1506 else 1507 mutt_free_alias (&Aliases); 1508 break; 1509 } 1510 else 1511 for (tmp = Aliases; tmp; tmp = tmp->next) 1512 { 1513 if (mutt_strcasecmp (buf->data, tmp->name) == 0) 1514 { 1515 if (CurrentMenu == MENU_ALIAS) 1516 { 1517 tmp->del = 1; 1518 mutt_set_current_menu_redraw_full (); 1519 break; 1520 } 1521 1522 if (last) 1523 last->next = tmp->next; 1524 else 1525 Aliases = tmp->next; 1526 tmp->next = NULL; 1527 mutt_free_alias (&tmp); 1528 break; 1529 } 1530 last = tmp; 1531 } 1532 } 1533 while (MoreArgs (s)); 1534 return 0; 1535} 1536 1537static int parse_alias (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 1538{ 1539 ALIAS *tmp = Aliases; 1540 ALIAS *last = NULL; 1541 char *estr = NULL; 1542 group_context_t *gc = NULL; 1543 1544 if (!MoreArgs (s)) 1545 { 1546 strfcpy (err->data, _("alias: no address"), err->dsize); 1547 return (-1); 1548 } 1549 1550 mutt_extract_token (buf, s, 0); 1551 1552 if (parse_group_context (&gc, buf, s, err) == -1) 1553 return -1; 1554 1555 /* check to see if an alias with this name already exists */ 1556 for (; tmp; tmp = tmp->next) 1557 { 1558 if (!mutt_strcasecmp (tmp->name, buf->data)) 1559 break; 1560 last = tmp; 1561 } 1562 1563 if (!tmp) 1564 { 1565 /* create a new alias */ 1566 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS)); 1567 tmp->self = tmp; 1568 tmp->name = safe_strdup (buf->data); 1569 /* give the main addressbook code a chance */ 1570 if (CurrentMenu == MENU_ALIAS) 1571 set_option (OPTMENUCALLER); 1572 } 1573 else 1574 { 1575 mutt_alias_delete_reverse (tmp); 1576 /* override the previous value */ 1577 rfc822_free_address (&tmp->addr); 1578 if (CurrentMenu == MENU_ALIAS) 1579 mutt_set_current_menu_redraw_full (); 1580 } 1581 1582 mutt_extract_token (buf, s, MUTT_TOKEN_QUOTE | MUTT_TOKEN_SPACE | MUTT_TOKEN_SEMICOLON); 1583 dprint (3, (debugfile, "parse_alias: Second token is '%s'.\n", 1584 buf->data)); 1585 1586 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data); 1587 1588 if (last) 1589 last->next = tmp; 1590 else 1591 Aliases = tmp; 1592 if (mutt_addrlist_to_intl (tmp->addr, &estr)) 1593 { 1594 snprintf (err->data, err->dsize, _("Warning: Bad IDN '%s' in alias '%s'.\n"), 1595 estr, tmp->name); 1596 FREE (&estr); 1597 goto bail; 1598 } 1599 1600 mutt_group_context_add_adrlist (gc, tmp->addr); 1601 mutt_alias_add_reverse (tmp); 1602 1603#ifdef DEBUG 1604 if (debuglevel >= 2) 1605 { 1606 ADDRESS *a; 1607 /* A group is terminated with an empty address, so check a->mailbox */ 1608 for (a = tmp->addr; a && a->mailbox; a = a->next) 1609 { 1610 if (!a->group) 1611 dprint (3, (debugfile, "parse_alias: %s\n", 1612 a->mailbox)); 1613 else 1614 dprint (3, (debugfile, "parse_alias: Group %s\n", 1615 a->mailbox)); 1616 } 1617 } 1618#endif 1619 mutt_group_context_destroy (&gc); 1620 return 0; 1621 1622bail: 1623 mutt_group_context_destroy (&gc); 1624 return -1; 1625} 1626 1627static int 1628parse_unmy_hdr (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 1629{ 1630 LIST *last = NULL; 1631 LIST *tmp = UserHeader; 1632 LIST *ptr; 1633 size_t l; 1634 1635 do 1636 { 1637 mutt_extract_token (buf, s, 0); 1638 if (mutt_strcmp ("*", buf->data) == 0) 1639 mutt_free_list (&UserHeader); 1640 else 1641 { 1642 tmp = UserHeader; 1643 last = NULL; 1644 1645 l = mutt_strlen (buf->data); 1646 if (buf->data[l - 1] == ':') 1647 l--; 1648 1649 while (tmp) 1650 { 1651 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0 && tmp->data[l] == ':') 1652 { 1653 ptr = tmp; 1654 if (last) 1655 last->next = tmp->next; 1656 else 1657 UserHeader = tmp->next; 1658 tmp = tmp->next; 1659 ptr->next = NULL; 1660 mutt_free_list (&ptr); 1661 } 1662 else 1663 { 1664 last = tmp; 1665 tmp = tmp->next; 1666 } 1667 } 1668 } 1669 } 1670 while (MoreArgs (s)); 1671 return 0; 1672} 1673 1674static int parse_my_hdr (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err) 1675{ 1676 LIST *tmp; 1677 size_t keylen; 1678 char *p; 1679 1680 mutt_extract_token (buf, s, MUTT_TOKEN_SPACE | MUTT_TOKEN_QUOTE); 1681 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') 1682 { 1683 strfcpy (err->data, _("invalid header field"), err->dsize); 1684 return (-1); 1685 } 1686 keylen = p - buf->data + 1; 1687 1688 if (UserHeader) 1689 { 1690 for (tmp = UserHeader; ; tmp = tmp->next) 1691 { 1692 /* see if there is already a field by this name */ 1693 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) 1694 { 1695 /* replace the old value */ 1696 FREE (&tmp->data); 1697 tmp->data = buf->data; 1698 mutt_buffer_init (buf); 1699 return 0; 1700 } 1701 if (!tmp->next) 1702 break; 1703 } 1704 tmp->next = mutt_new_list (); 1705 tmp = tmp->next; 1706 } 1707 else 1708 { 1709 tmp = mutt_new_list (); 1710 UserHeader = tmp; 1711 } 1712 tmp->data = buf->data; 1713 mutt_buffer_init (buf); 1714 return 0; 1715} 1716 1717static int 1718parse_sort (short *val, const char *s, const struct mapping_t *map, BUFFER *err) 1719{ 1720 int i, flags = 0; 1721 1722 if (mutt_strncmp ("reverse-", s, 8) == 0) 1723 { 1724 s += 8; 1725 flags = SORT_REVERSE; 1726 } 1727 1728 if (mutt_strncmp ("last-", s, 5) == 0) 1729 { 1730 s += 5; 1731 flags |= SORT_LAST; 1732 } 1733 1734 if ((i = mutt_getvaluebyname (s, map)) == -1) 1735 { 1736 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s); 1737 return (-1); 1738 } 1739 1740 *val = i | flags; 1741 1742 return 0; 1743} 1744 1745static void mutt_set_default (struct option_t *p) 1746{ 1747 switch (p->type & DT_MASK) 1748 { 1749 case DT_STR: 1750 case DT_PATH: 1751 if (!p->init.p && *((char **) p->data.p)) 1752 p->init.p = safe_strdup (* ((char **) p->data.p)); 1753 break; 1754 case DT_ADDR: 1755 if (!p->init.p && *((ADDRESS **) p->data.p)) 1756 { 1757 char tmp[HUGE_STRING]; 1758 *tmp = '\0'; 1759 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data.p), 0); 1760 p->init.p = safe_strdup (tmp); 1761 } 1762 break; 1763 case DT_RX: 1764 { 1765 REGEXP *pp = (REGEXP *) p->data.p; 1766 if (!p->init.p && pp->pattern) 1767 p->init.p = safe_strdup (pp->pattern); 1768 break; 1769 } 1770 } 1771} 1772 1773static void mutt_restore_default (struct option_t *p) 1774{ 1775 switch (p->type & DT_MASK) 1776 { 1777 case DT_STR: 1778 mutt_str_replace ((char **) p->data.p, (char *) p->init.p); 1779 break; 1780 case DT_MBCHARTBL: 1781 free_mbchar_table ((mbchar_table **)p->data.p); 1782 *((mbchar_table **) p->data.p) = parse_mbchar_table ((char *) p->init.p); 1783 break; 1784 case DT_PATH: 1785 FREE((char **) p->data.p); /* __FREE_CHECKED__ */ 1786 if (p->init.p) 1787 { 1788 BUFFER *path; 1789 path = mutt_buffer_pool_get (); 1790 mutt_buffer_strcpy (path, (char *) p->init.p); 1791 mutt_buffer_expand_path (path); 1792 *((char **) p->data.p) = safe_strdup (mutt_b2s (path)); 1793 mutt_buffer_pool_release (&path); 1794 } 1795 break; 1796 case DT_ADDR: 1797 rfc822_free_address ((ADDRESS **) p->data.p); 1798 if (p->init.p) 1799 *((ADDRESS **) p->data.p) = rfc822_parse_adrlist (NULL, (char *) p->init.p); 1800 break; 1801 case DT_BOOL: 1802 if (p->init.l) 1803 set_option (p->data.l); 1804 else 1805 unset_option (p->data.l); 1806 break; 1807 case DT_QUAD: 1808 set_quadoption (p->data.l, p->init.l); 1809 break; 1810 case DT_NUM: 1811 case DT_SORT: 1812 case DT_MAGIC: 1813 *((short *) p->data.p) = p->init.l; 1814 break; 1815 case DT_LNUM: 1816 *((long *) p->data.p) = p->init.l; 1817 break; 1818 case DT_RX: 1819 { 1820 REGEXP *pp = (REGEXP *) p->data.p; 1821 int flags = 0; 1822 1823 FREE (&pp->pattern); 1824 if (pp->rx) 1825 { 1826 regfree (pp->rx); 1827 FREE (&pp->rx); 1828 } 1829 1830 if (p->init.p) 1831 { 1832 char *s = (char *) p->init.p; 1833 1834 pp->rx = safe_calloc (1, sizeof (regex_t)); 1835 pp->pattern = safe_strdup ((char *) p->init.p); 1836 if (mutt_strcmp (p->option, "mask") != 0) 1837 flags |= mutt_which_case ((const char *) p->init.p); 1838 if (mutt_strcmp (p->option, "mask") == 0 && *s == '!') 1839 { 1840 s++; 1841 pp->not = 1; 1842 } 1843 if (REGCOMP (pp->rx, s, flags) != 0) 1844 { 1845 fprintf (stderr, _("mutt_restore_default(%s): error in regexp: %s\n"), 1846 p->option, pp->pattern); 1847 FREE (&pp->pattern); 1848 FREE (&pp->rx); 1849 } 1850 } 1851 } 1852 break; 1853 } 1854 1855 if (p->flags & R_INDEX) 1856 mutt_set_menu_redraw_full (MENU_MAIN); 1857 if (p->flags & R_PAGER) 1858 mutt_set_menu_redraw_full (MENU_PAGER); 1859 if (p->flags & R_PAGER_FLOW) 1860 { 1861 mutt_set_menu_redraw_full (MENU_PAGER); 1862 mutt_set_menu_redraw (MENU_PAGER, REDRAW_FLOW); 1863 } 1864 if (p->flags & R_RESORT_SUB) 1865 set_option (OPTSORTSUBTHREADS); 1866 if (p->flags & R_RESORT) 1867 set_option (OPTNEEDRESORT); 1868 if (p->flags & R_RESORT_INIT) 1869 set_option (OPTRESORTINIT); 1870 if (p->flags & R_TREE) 1871 set_option (OPTREDRAWTREE); 1872 if (p->flags & R_REFLOW) 1873 mutt_reflow_windows (); 1874#ifdef USE_SIDEBAR 1875 if (p->flags & R_SIDEBAR) 1876 mutt_set_current_menu_redraw (REDRAW_SIDEBAR); 1877#endif 1878 if (p->flags & R_MENU) 1879 mutt_set_current_menu_redraw_full (); 1880} 1881 1882static size_t escape_string (char *dst, size_t len, const char* src) 1883{ 1884 char* p = dst; 1885 1886 if (!len) 1887 return 0; 1888 len--; /* save room for \0 */ 1889#define ESC_CHAR(C) do { *p++ = '\\'; if (p - dst < len) *p++ = C; } while (0) 1890 while (p - dst < len && src && *src) 1891 { 1892 switch (*src) 1893 { 1894 case '\n': 1895 ESC_CHAR('n'); 1896 break; 1897 case '\r': 1898 ESC_CHAR('r'); 1899 break; 1900 case '\t': 1901 ESC_CHAR('t'); 1902 break; 1903 default: 1904 if ((*src == '\\' || *src == '"') && p - dst < len - 1) 1905 *p++ = '\\'; 1906 *p++ = *src; 1907 } 1908 src++; 1909 } 1910#undef ESC_CHAR 1911 *p = '\0'; 1912 return p - dst; 1913} 1914 1915static void pretty_var (char *dst, size_t len, const char *option, const char *val) 1916{ 1917 char *p; 1918 1919 if (!len) 1920 return; 1921 1922 strfcpy (dst, option, len); 1923 len--; /* save room for \0 */ 1924 p = dst + mutt_strlen (dst); 1925 1926 if (p - dst < len) 1927 *p++ = '='; 1928 if (p - dst < len) 1929 *p++ = '"'; 1930 p += escape_string (p, len - (p - dst) + 1, val); /* \0 terminate it */ 1931 if (p - dst < len) 1932 *p++ = '"'; 1933 *p = 0; 1934} 1935 1936static int check_charset (struct option_t *opt, const char *val) 1937{ 1938 char *p, *q = NULL, *s = safe_strdup (val); 1939 int rc = 0, strict = strcmp (opt->option, "send_charset") == 0; 1940 1941 /* $charset should be nonempty, and a single value - not a colon 1942 * delimited list */ 1943 if (mutt_strcmp (opt->option, "charset") == 0) 1944 { 1945 if (!s || (strchr (s, ':') != NULL)) 1946 { 1947 FREE (&s); 1948 return -1; 1949 } 1950 } 1951 1952 /* other values can be empty */ 1953 if (!s) 1954 return rc; 1955 1956 for (p = strtok_r (s, ":", &q); p; p = strtok_r (NULL, ":", &q)) 1957 { 1958 if (!*p) 1959 continue; 1960 if (mutt_check_charset (p, strict) < 0) 1961 { 1962 rc = -1; 1963 break; 1964 } 1965 } 1966 1967 FREE(&s); 1968 return rc; 1969} 1970 1971char **mutt_envlist (void) 1972{ 1973 return envlist; 1974} 1975 1976/* Helper function for parse_setenv(). 1977 * It's broken out because some other parts of mutt (filter.c) need 1978 * to set/overwrite environment variables in envlist before execing. 1979 */ 1980void mutt_envlist_set (const char *name, const char *value, int overwrite) 1981{ 1982 char **envp = envlist; 1983 char work[LONG_STRING]; 1984 int count, len; 1985 1986 len = mutt_strlen (name); 1987 1988 /* Look for current slot to overwrite */ 1989 count = 0; 1990 while (envp && *envp) 1991 { 1992 if (!mutt_strncmp (name, *envp, len) && (*envp)[len] == '=') 1993 { 1994 if (!overwrite) 1995 return; 1996 break; 1997 } 1998 envp++; 1999 count++; 2000 } 2001 2002 /* Format var=value string */ 2003 snprintf (work, sizeof(work), "%s=%s", NONULL (name), NONULL (value)); 2004 2005 /* If slot found, overwrite */ 2006 if (envp && *envp) 2007 mutt_str_replace (envp, work); 2008 2009 /* If not found, add new slot */ 2010 else 2011 { 2012 safe_realloc (&envlist, sizeof(char *) * (count + 2)); 2013 envlist[count] = safe_strdup (work); 2014 envlist[count + 1] = NULL; 2015 } 2016} 2017 2018static int parse_setenv(BUFFER *tmp, BUFFER *s, union pointer_long_t udata, BUFFER *err) 2019{ 2020 int query, unset, len; 2021 char *name, **save, **envp = envlist; 2022 int count = 0; 2023 long data = udata.l; 2024 2025 query = 0; 2026 unset = data & MUTT_SET_UNSET; 2027 2028 if (!MoreArgs (s)) 2029 { 2030 strfcpy (err->data, _("too few arguments"), err->dsize); 2031 return -1; 2032 } 2033 2034 if (*s->dptr == '?') 2035 { 2036 query = 1; 2037 s->dptr++; 2038 } 2039 2040 /* get variable name */ 2041 mutt_extract_token (tmp, s, MUTT_TOKEN_EQUAL); 2042 len = strlen (tmp->data); 2043 2044 if (query) 2045 { 2046 int found = 0; 2047 while (envp && *envp) 2048 { 2049 if (!mutt_strncmp (tmp->data, *envp, len)) 2050 { 2051 if (!found) 2052 { 2053 mutt_endwin (NULL); 2054 found = 1; 2055 } 2056 puts (*envp); 2057 } 2058 envp++; 2059 } 2060 2061 if (found) 2062 { 2063 mutt_any_key_to_continue (NULL); 2064 return 0; 2065 } 2066 2067 snprintf (err->data, err->dsize, _("%s is unset"), tmp->data); 2068 return -1; 2069 } 2070 2071 if (unset) 2072 { 2073 count = 0; 2074 while (envp && *envp) 2075 { 2076 if (!mutt_strncmp (tmp->data, *envp, len) && (*envp)[len] == '=') 2077 { 2078 /* shuffle down */ 2079 save = envp++; 2080 while (*envp) 2081 { 2082 *save++ = *envp++; 2083 count++; 2084 } 2085 *save = NULL; 2086 safe_realloc (&envlist, sizeof(char *) * (count+1)); 2087 return 0; 2088 } 2089 envp++; 2090 count++; 2091 } 2092 return -1; 2093 } 2094 2095 /* set variable */ 2096 2097 if (*s->dptr == '=') 2098 { 2099 s->dptr++; 2100 SKIPWS (s->dptr); 2101 } 2102 2103 if (!MoreArgs (s)) 2104 { 2105 strfcpy (err->data, _("too few arguments"), err->dsize); 2106 return -1; 2107 } 2108 2109 name = safe_strdup (tmp->data); 2110 mutt_extract_token (tmp, s, 0); 2111 mutt_envlist_set (name, tmp->data, 1); 2112 FREE (&name); 2113 2114 return 0; 2115} 2116 2117static int parse_set (BUFFER *tmp, BUFFER *s, union pointer_long_t udata, BUFFER *err) 2118{ 2119 int query, unset, inv, reset, r = 0; 2120 int idx = -1; 2121 const char *p; 2122 BUFFER *scratch = NULL; 2123 char* myvar; 2124 long data = udata.l; 2125 2126 while (MoreArgs (s)) 2127 { 2128 /* reset state variables */ 2129 query = 0; 2130 unset = data & MUTT_SET_UNSET; 2131 inv = data & MUTT_SET_INV; 2132 reset = data & MUTT_SET_RESET; 2133 myvar = NULL; 2134 2135 if (*s->dptr == '?') 2136 { 2137 query = 1; 2138 s->dptr++; 2139 } 2140 else if (mutt_strncmp ("no", s->dptr, 2) == 0) 2141 { 2142 s->dptr += 2; 2143 unset = !unset; 2144 } 2145 else if (mutt_strncmp ("inv", s->dptr, 3) == 0) 2146 { 2147 s->dptr += 3; 2148 inv = !inv; 2149 } 2150 else if (*s->dptr == '&') 2151 { 2152 reset = 1; 2153 s->dptr++; 2154 } 2155 2156 /* get the variable name */ 2157 mutt_extract_token (tmp, s, MUTT_TOKEN_EQUAL); 2158 2159 if (!mutt_strncmp ("my_", tmp->data, 3)) 2160 myvar = tmp->data; 2161 else if ((idx = mutt_option_index (tmp->data)) == -1 && 2162 !(reset && !mutt_strcmp ("all", tmp->data))) 2163 { 2164 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data); 2165 return (-1); 2166 } 2167 SKIPWS (s->dptr); 2168 2169 if (reset) 2170 { 2171 if (query || unset || inv) 2172 { 2173 snprintf (err->data, err->dsize, "%s", _("prefix is illegal with reset")); 2174 return (-1); 2175 } 2176 2177 if (s && *s->dptr == '=') 2178 { 2179 snprintf (err->data, err->dsize, "%s", _("value is illegal with reset")); 2180 return (-1); 2181 } 2182 2183 if (!mutt_strcmp ("all", tmp->data)) 2184 { 2185 if (CurrentMenu == MENU_PAGER) 2186 { 2187 snprintf (err->data, err->dsize, "%s", _("Not available in this menu.")); 2188 return (-1); 2189 } 2190 for (idx = 0; MuttVars[idx].option; idx++) 2191 mutt_restore_default (&MuttVars[idx]); 2192 mutt_set_current_menu_redraw_full (); 2193 set_option (OPTSORTSUBTHREADS); 2194 set_option (OPTNEEDRESORT); 2195 set_option (OPTRESORTINIT); 2196 set_option (OPTREDRAWTREE); 2197 return 0; 2198 } 2199 else 2200 { 2201 CHECK_PAGER; 2202 if (myvar) 2203 myvar_del (myvar); 2204 else 2205 mutt_restore_default (&MuttVars[idx]); 2206 } 2207 } 2208 else if (!myvar && DTYPE (MuttVars[idx].type) == DT_BOOL) 2209 { 2210 if (s && *s->dptr == '=') 2211 { 2212 if (unset || inv || query) 2213 { 2214 snprintf (err->data, err->dsize, "%s", _("Usage: set variable=yes|no")); 2215 return (-1); 2216 } 2217 2218 s->dptr++; 2219 mutt_extract_token (tmp, s, 0); 2220 if (ascii_strcasecmp ("yes", tmp->data) == 0) 2221 unset = inv = 0; 2222 else if (ascii_strcasecmp ("no", tmp->data) == 0) 2223 unset = 1; 2224 else 2225 { 2226 snprintf (err->data, err->dsize, "%s", _("Usage: set variable=yes|no")); 2227 return (-1); 2228 } 2229 } 2230 2231 if (query) 2232 { 2233 snprintf (err->data, err->dsize, option (MuttVars[idx].data.l) 2234 ? _("%s is set") : _("%s is unset"), tmp->data); 2235 return 0; 2236 } 2237 2238 CHECK_PAGER; 2239 if (unset) 2240 unset_option (MuttVars[idx].data.l); 2241 else if (inv) 2242 toggle_option (MuttVars[idx].data.l); 2243 else 2244 set_option (MuttVars[idx].data.l); 2245 } 2246 else if (myvar || DTYPE (MuttVars[idx].type) == DT_STR || 2247 DTYPE (MuttVars[idx].type) == DT_PATH || 2248 DTYPE (MuttVars[idx].type) == DT_ADDR || 2249 DTYPE (MuttVars[idx].type) == DT_MBCHARTBL) 2250 { 2251 if (unset) 2252 { 2253 CHECK_PAGER; 2254 if (myvar) 2255 myvar_del (myvar); 2256 else if (DTYPE (MuttVars[idx].type) == DT_ADDR) 2257 rfc822_free_address ((ADDRESS **) MuttVars[idx].data.p); 2258 else if (DTYPE (MuttVars[idx].type) == DT_MBCHARTBL) 2259 free_mbchar_table ((mbchar_table **) MuttVars[idx].data.p); 2260 else 2261 /* MuttVars[idx].data.p is already 'char**' (or some 'void**') or... 2262 * so cast to 'void*' is okay */ 2263 FREE (MuttVars[idx].data.p); /* __FREE_CHECKED__ */ 2264 } 2265 else if (query || *s->dptr != '=') 2266 { 2267 char _tmp[LONG_STRING]; 2268 const char *val = NULL; 2269 2270 if (myvar) 2271 { 2272 if ((val = myvar_get (myvar))) 2273 { 2274 pretty_var (err->data, err->dsize, myvar, val); 2275 break; 2276 } 2277 else 2278 { 2279 snprintf (err->data, err->dsize, _("%s: unknown variable"), myvar); 2280 return (-1); 2281 } 2282 } 2283 else if (DTYPE (MuttVars[idx].type) == DT_ADDR) 2284 { 2285 _tmp[0] = '\0'; 2286 rfc822_write_address (_tmp, sizeof (_tmp), *((ADDRESS **) MuttVars[idx].data.p), 0); 2287 val = _tmp; 2288 } 2289 else if (DTYPE (MuttVars[idx].type) == DT_PATH) 2290 { 2291 _tmp[0] = '\0'; 2292 strfcpy (_tmp, NONULL(*((char **) MuttVars[idx].data.p)), sizeof (_tmp)); 2293 mutt_pretty_mailbox (_tmp, sizeof (_tmp)); 2294 val = _tmp; 2295 } 2296 else if (DTYPE (MuttVars[idx].type) == DT_MBCHARTBL) 2297 { 2298 mbchar_table *mbt = (*((mbchar_table **) MuttVars[idx].data.p)); 2299 val = mbt ? NONULL (mbt->orig_str) : ""; 2300 } 2301 else 2302 val = *((char **) MuttVars[idx].data.p); 2303 2304 /* user requested the value of this variable */ 2305 pretty_var (err->data, err->dsize, MuttVars[idx].option, NONULL(val)); 2306 break; 2307 } 2308 else 2309 { 2310 CHECK_PAGER; 2311 s->dptr++; 2312 2313 if (myvar) 2314 { 2315 /* myvar is a pointer to tmp and will be lost on extract_token */ 2316 myvar = safe_strdup (myvar); 2317 myvar_del (myvar); 2318 } 2319 2320 mutt_extract_token (tmp, s, 0); 2321 2322 if (myvar) 2323 { 2324 myvar_set (myvar, tmp->data); 2325 FREE (&myvar); 2326 myvar="don't resort"; 2327 } 2328 else if (DTYPE (MuttVars[idx].type) == DT_PATH) 2329 { 2330 /* MuttVars[idx].data is already 'char**' (or some 'void**') or... 2331 * so cast to 'void*' is okay */ 2332 FREE (MuttVars[idx].data.p); /* __FREE_CHECKED__ */ 2333 2334 scratch = mutt_buffer_pool_get (); 2335 mutt_buffer_strcpy (scratch, tmp->data); 2336 mutt_buffer_expand_path (scratch); 2337 *((char **) MuttVars[idx].data.p) = safe_strdup (mutt_b2s (scratch)); 2338 mutt_buffer_pool_release (&scratch); 2339 } 2340 else if (DTYPE (MuttVars[idx].type) == DT_STR) 2341 { 2342 if (strstr (MuttVars[idx].option, "charset") && 2343 check_charset (&MuttVars[idx], tmp->data) < 0) 2344 { 2345 snprintf (err->data, err->dsize, _("Invalid value for option %s: \"%s\""), 2346 MuttVars[idx].option, tmp->data); 2347 return (-1); 2348 } 2349 2350 FREE (MuttVars[idx].data.p); /* __FREE_CHECKED__ */ 2351 *((char **) MuttVars[idx].data.p) = safe_strdup (tmp->data); 2352 if (mutt_strcmp (MuttVars[idx].option, "charset") == 0) 2353 mutt_set_charset (Charset); 2354 } 2355 else if (DTYPE (MuttVars[idx].type) == DT_MBCHARTBL) 2356 { 2357 free_mbchar_table ((mbchar_table **) MuttVars[idx].data.p); 2358 *((mbchar_table **) MuttVars[idx].data.p) = parse_mbchar_table (tmp->data); 2359 } 2360 else 2361 { 2362 rfc822_free_address ((ADDRESS **) MuttVars[idx].data.p); 2363 *((ADDRESS **) MuttVars[idx].data.p) = rfc822_parse_adrlist (NULL, tmp->data); 2364 } 2365 } 2366 } 2367 else if (DTYPE(MuttVars[idx].type) == DT_RX) 2368 { 2369 REGEXP *ptr = (REGEXP *) MuttVars[idx].data.p; 2370 regex_t *rx; 2371 int e, flags = 0; 2372 2373 if (query || *s->dptr != '=') 2374 { 2375 /* user requested the value of this variable */ 2376 pretty_var (err->data, err->dsize, MuttVars[idx].option, NONULL(ptr->pattern)); 2377 break; 2378 } 2379 2380 if (option(OPTATTACHMSG) && !mutt_strcmp(MuttVars[idx].option, "reply_regexp")) 2381 { 2382 snprintf (err->data, err->dsize, "Operation not permitted when in attach-message mode."); 2383 r = -1; 2384 break; 2385 } 2386 2387 CHECK_PAGER; 2388 s->dptr++; 2389 2390 /* copy the value of the string */ 2391 mutt_extract_token (tmp, s, 0); 2392 2393 if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0) 2394 { 2395 int not = 0; 2396 2397 /* $mask is case-sensitive */ 2398 if (mutt_strcmp (MuttVars[idx].option, "mask") != 0) 2399 flags |= mutt_which_case (tmp->data); 2400 2401 p = tmp->data; 2402 if (mutt_strcmp (MuttVars[idx].option, "mask") == 0) 2403 { 2404 if (*p == '!') 2405 { 2406 not = 1; 2407 p++; 2408 } 2409 } 2410 2411 rx = (regex_t *) safe_malloc (sizeof (regex_t)); 2412 if ((e = REGCOMP (rx, p, flags)) != 0) 2413 { 2414 regerror (e, rx, err->data, err->dsize); 2415 FREE (&rx); 2416 break; 2417 } 2418 2419 /* get here only if everything went smootly */ 2420 if (ptr->pattern) 2421 { 2422 FREE (&ptr->pattern); 2423 regfree ((regex_t *) ptr->rx); 2424 FREE (&ptr->rx); 2425 } 2426 2427 ptr->pattern = safe_strdup (tmp->data); 2428 ptr->rx = rx; 2429 ptr->not = not; 2430 2431 /* $reply_regexp and $alterantes require special treatment */ 2432 2433 if (Context && Context->msgcount && 2434 mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0) 2435 { 2436 regmatch_t pmatch[1]; 2437 int i; 2438 2439#define CUR_ENV Context->hdrs[i]->env 2440 for (i = 0; i < Context->msgcount; i++) 2441 { 2442 if (CUR_ENV && CUR_ENV->subject) 2443 { 2444 CUR_ENV->real_subj = 2445 (regexec (ReplyRegexp.rx, CUR_ENV->subject, 1, pmatch, 0)) ? 2446 CUR_ENV->subject : 2447 CUR_ENV->subject + pmatch[0].rm_eo; 2448 } 2449 } 2450#undef CUR_ENV 2451 } 2452 } 2453 } 2454 else if (DTYPE(MuttVars[idx].type) == DT_MAGIC) 2455 { 2456 if (query || *s->dptr != '=') 2457 { 2458 switch (DefaultMagic) 2459 { 2460 case MUTT_MBOX: 2461 p = "mbox"; 2462 break; 2463 case MUTT_MMDF: 2464 p = "MMDF"; 2465 break; 2466 case MUTT_MH: 2467 p = "MH"; 2468 break; 2469 case MUTT_MAILDIR: 2470 p = "Maildir"; 2471 break; 2472 default: 2473 p = "unknown"; 2474 break; 2475 } 2476 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p); 2477 break; 2478 } 2479 2480 CHECK_PAGER; 2481 s->dptr++; 2482 2483 /* copy the value of the string */ 2484 mutt_extract_token (tmp, s, 0); 2485 if (mx_set_magic (tmp->data)) 2486 { 2487 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"), tmp->data); 2488 r = -1; 2489 break; 2490 } 2491 } 2492 else if (DTYPE(MuttVars[idx].type) == DT_NUM) 2493 { 2494 short *ptr = (short *) MuttVars[idx].data.p; 2495 short val; 2496 int rc; 2497 2498 if (query || *s->dptr != '=') 2499 { 2500 val = *ptr; 2501 /* compatibility alias */ 2502 if (mutt_strcmp (MuttVars[idx].option, "wrapmargin") == 0) 2503 val = *ptr < 0 ? -*ptr : 0; 2504 2505 /* user requested the value of this variable */ 2506 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, val); 2507 break; 2508 } 2509 2510 CHECK_PAGER; 2511 s->dptr++; 2512 2513 mutt_extract_token (tmp, s, 0); 2514 rc = mutt_atos (tmp->data, (short *) &val); 2515 2516 if (rc < 0 || !*tmp->data) 2517 { 2518 snprintf (err->data, err->dsize, _("%s: invalid value (%s)"), tmp->data, 2519 rc == -1 ? _("format error") : _("number overflow")); 2520 r = -1; 2521 break; 2522 } 2523 else 2524 *ptr = val; 2525 2526 /* these ones need a sanity check */ 2527 if (mutt_strcmp (MuttVars[idx].option, "history") == 0) 2528 { 2529 if (*ptr < 0) 2530 *ptr = 0; 2531 mutt_init_history (); 2532 } 2533 else if (mutt_strcmp (MuttVars[idx].option, "error_history") == 0) 2534 { 2535 if (*ptr < 0) 2536 *ptr = 0; 2537 mutt_error_history_init (); 2538 } 2539 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0) 2540 { 2541 if (*ptr < 0) 2542 *ptr = 0; 2543 } 2544 else if (mutt_strcmp (MuttVars[idx].option, "wrapmargin") == 0) 2545 { 2546 if (*ptr < 0) 2547 *ptr = 0; 2548 else 2549 *ptr = -*ptr; 2550 } 2551#ifdef USE_IMAP 2552 else if (mutt_strcmp (MuttVars[idx].option, "imap_pipeline_depth") == 0) 2553 { 2554 if (*ptr < 0) 2555 *ptr = 0; 2556 } 2557#endif 2558 } 2559 else if (DTYPE(MuttVars[idx].type) == DT_LNUM) 2560 { 2561 long *ptr = (long *) MuttVars[idx].data.p; 2562 long val; 2563 int rc; 2564 2565 if (query || *s->dptr != '=') 2566 { 2567 val = *ptr; 2568 2569 /* user requested the value of this variable */ 2570 snprintf (err->data, err->dsize, "%s=%ld", MuttVars[idx].option, val); 2571 break; 2572 } 2573 2574 CHECK_PAGER; 2575 s->dptr++; 2576 2577 mutt_extract_token (tmp, s, 0); 2578 rc = mutt_atol (tmp->data, (long *) &val); 2579 2580 if (rc < 0 || !*tmp->data) 2581 { 2582 snprintf (err->data, err->dsize, _("%s: invalid value (%s)"), tmp->data, 2583 rc == -1 ? _("format error") : _("number overflow")); 2584 r = -1; 2585 break; 2586 } 2587 else 2588 *ptr = val; 2589 2590 } 2591 else if (DTYPE (MuttVars[idx].type) == DT_QUAD) 2592 { 2593 if (query) 2594 { 2595 static const char * const vals[] = { "no", "yes", "ask-no", "ask-yes" }; 2596 2597 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, 2598 vals [ quadoption (MuttVars[idx].data.l) ]); 2599 break; 2600 } 2601 2602 CHECK_PAGER; 2603 if (*s->dptr == '=') 2604 { 2605 s->dptr++; 2606 mutt_extract_token (tmp, s, 0); 2607 if (ascii_strcasecmp ("yes", tmp->data) == 0) 2608 set_quadoption (MuttVars[idx].data.l, MUTT_YES); 2609 else if (ascii_strcasecmp ("no", tmp->data) == 0) 2610 set_quadoption (MuttVars[idx].data.l, MUTT_NO); 2611 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0) 2612 set_quadoption (MuttVars[idx].data.l, MUTT_ASKYES); 2613 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0) 2614 set_quadoption (MuttVars[idx].data.l, MUTT_ASKNO); 2615 else 2616 { 2617 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data); 2618 r = -1; 2619 break; 2620 } 2621 } 2622 else 2623 { 2624 if (inv) 2625 toggle_quadoption (MuttVars[idx].data.l); 2626 else if (unset) 2627 set_quadoption (MuttVars[idx].data.l, MUTT_NO); 2628 else 2629 set_quadoption (MuttVars[idx].data.l, MUTT_YES); 2630 } 2631 } 2632 else if (DTYPE (MuttVars[idx].type) == DT_SORT) 2633 { 2634 const struct mapping_t *map = NULL; 2635 2636 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) 2637 { 2638 case DT_SORT_ALIAS: 2639 map = SortAliasMethods; 2640 break; 2641 case DT_SORT_BROWSER: 2642 map = SortBrowserMethods; 2643 break; 2644 case DT_SORT_KEYS: 2645 if ((WithCrypto & APPLICATION_PGP)) 2646 map = SortKeyMethods; 2647 break; 2648 case DT_SORT_AUX: 2649 map = SortAuxMethods; 2650 break; 2651 case DT_SORT_SIDEBAR: 2652 map = SortSidebarMethods; 2653 break; 2654 default: 2655 map = SortMethods; 2656 break; 2657 } 2658 2659 if (!map) 2660 { 2661 snprintf (err->data, err->dsize, _("%s: Unknown type."), MuttVars[idx].option); 2662 r = -1; 2663 break; 2664 } 2665 2666 if (query || *s->dptr != '=') 2667 { 2668 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data.p) & SORT_MASK, map); 2669 2670 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option, 2671 (*((short *) MuttVars[idx].data.p) & SORT_REVERSE) ? "reverse-" : "", 2672 (*((short *) MuttVars[idx].data.p) & SORT_LAST) ? "last-" : "", 2673 p); 2674 return 0; 2675 } 2676 CHECK_PAGER; 2677 s->dptr++; 2678 mutt_extract_token (tmp, s , 0); 2679 2680 if (parse_sort ((short *) MuttVars[idx].data.p, tmp->data, map, err) == -1) 2681 { 2682 r = -1; 2683 break; 2684 } 2685 } 2686 else 2687 { 2688 snprintf (err->data, err->dsize, _("%s: unknown type"), MuttVars[idx].option); 2689 r = -1; 2690 break; 2691 } 2692 2693 if (!myvar) 2694 { 2695 if (MuttVars[idx].flags & R_INDEX) 2696 mutt_set_menu_redraw_full (MENU_MAIN); 2697 if (MuttVars[idx].flags & R_PAGER) 2698 mutt_set_menu_redraw_full (MENU_PAGER); 2699 if (MuttVars[idx].flags & R_PAGER_FLOW) 2700 { 2701 mutt_set_menu_redraw_full (MENU_PAGER); 2702 mutt_set_menu_redraw (MENU_PAGER, REDRAW_FLOW); 2703 } 2704 if (MuttVars[idx].flags & R_RESORT_SUB) 2705 set_option (OPTSORTSUBTHREADS); 2706 if (MuttVars[idx].flags & R_RESORT) 2707 set_option (OPTNEEDRESORT); 2708 if (MuttVars[idx].flags & R_RESORT_INIT) 2709 set_option (OPTRESORTINIT); 2710 if (MuttVars[idx].flags & R_TREE) 2711 set_option (OPTREDRAWTREE); 2712 if (MuttVars[idx].flags & R_REFLOW) 2713 mutt_reflow_windows (); 2714#ifdef USE_SIDEBAR 2715 if (MuttVars[idx].flags & R_SIDEBAR) 2716 mutt_set_current_menu_redraw (REDRAW_SIDEBAR); 2717#endif 2718 if (MuttVars[idx].flags & R_MENU) 2719 mutt_set_current_menu_redraw_full (); 2720 } 2721 } 2722 return (r); 2723} 2724 2725#define MAXERRS 128 2726 2727/* reads the specified initialization file. returns -1 if errors were found 2728 so that we can pause to let the user know... */ 2729static int source_rc (const char *rcfile, BUFFER *err) 2730{ 2731 FILE *f; 2732 int line = 0, rc = 0, conv = 0; 2733 BUFFER token; 2734 char *linebuf = NULL; 2735 char *currentline = NULL; 2736 size_t buflen; 2737 pid_t pid; 2738 2739 dprint (2, (debugfile, "Reading configuration file '%s'.\n", 2740 rcfile)); 2741 2742 if ((f = mutt_open_read (rcfile, &pid)) == NULL) 2743 { 2744 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno)); 2745 return (-1); 2746 } 2747 2748 mutt_buffer_init (&token); 2749 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line, MUTT_CONT)) != NULL) 2750 { 2751 conv=ConfigCharset && Charset; 2752 if (conv) 2753 { 2754 currentline=safe_strdup(linebuf); 2755 if (!currentline) continue; 2756 mutt_convert_string(&currentline, ConfigCharset, Charset, 0); 2757 } 2758 else 2759 currentline=linebuf; 2760 2761 if (mutt_parse_rc_line (currentline, &token, err) == -1) 2762 { 2763 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data); 2764 if (--rc < -MAXERRS) 2765 { 2766 if (conv) FREE(&currentline); 2767 break; 2768 } 2769 } 2770 else 2771 { 2772 if (rc < 0) 2773 rc = -1; 2774 } 2775 if (conv) 2776 FREE(&currentline); 2777 } 2778 FREE (&token.data); 2779 FREE (&linebuf); 2780 safe_fclose (&f); 2781 if (pid != -1) 2782 mutt_wait_filter (pid); 2783 if (rc) 2784 { 2785 /* the muttrc source keyword */ 2786 snprintf (err->data, err->dsize, rc >= -MAXERRS ? _("source: errors in %s") 2787 : _("source: reading aborted due to too many errors in %s"), rcfile); 2788 rc = -1; 2789 } 2790 return (rc); 2791} 2792 2793#undef MAXERRS 2794 2795static int parse_source (BUFFER *tmp, BUFFER *s, union pointer_long_t udata, BUFFER *err) 2796{ 2797 BUFFER *path; 2798 int rc; 2799 2800 if (mutt_extract_token (tmp, s, 0) != 0) 2801 { 2802 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr); 2803 return (-1); 2804 } 2805 if (MoreArgs (s)) 2806 { 2807 strfcpy (err->data, _("source: too many arguments"), err->dsize); 2808 return (-1); 2809 } 2810 2811 path = mutt_buffer_new (); 2812 mutt_buffer_strcpy (path, tmp->data); 2813 mutt_buffer_expand_path (path); 2814 rc = source_rc (mutt_b2s (path), err); 2815 mutt_buffer_free (&path); 2816 2817 return rc; 2818} 2819 2820/* line command to execute 2821 2822 token scratch buffer to be used by parser. caller should free 2823 token->data when finished. the reason for this variable is 2824 to avoid having to allocate and deallocate a lot of memory 2825 if we are parsing many lines. the caller can pass in the 2826 memory to use, which avoids having to create new space for 2827 every call to this function. 2828 2829 err where to write error messages */ 2830int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err) 2831{ 2832 int i, r = -1; 2833 BUFFER expn; 2834 2835 if (!line || !*line) 2836 return 0; 2837 2838 mutt_buffer_init (&expn); 2839 expn.data = expn.dptr = line; 2840 expn.dsize = mutt_strlen (line); 2841 2842 *err->data = 0; 2843 2844 SKIPWS (expn.dptr); 2845 while (*expn.dptr) 2846 { 2847 if (*expn.dptr == '#') 2848 break; /* rest of line is a comment */ 2849 if (*expn.dptr == ';') 2850 { 2851 expn.dptr++; 2852 continue; 2853 } 2854 mutt_extract_token (token, &expn, 0); 2855 for (i = 0; Commands[i].name; i++) 2856 { 2857 if (!mutt_strcmp (token->data, Commands[i].name)) 2858 { 2859 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0) 2860 goto finish; 2861 break; 2862 } 2863 } 2864 if (!Commands[i].name) 2865 { 2866 snprintf (err->data, err->dsize, _("%s: unknown command"), NONULL (token->data)); 2867 goto finish; 2868 } 2869 } 2870 r = 0; 2871finish: 2872 if (expn.destroy) 2873 FREE (&expn.data); 2874 return (r); 2875} 2876 2877 2878#define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0])) 2879#define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0])) 2880/* initial string that starts completion. No telling how much crap 2881 * the user has typed so far. Allocate LONG_STRING just to be sure! */ 2882static char User_typed [LONG_STRING] = {0}; 2883 2884static int Num_matched = 0; /* Number of matches for completion */ 2885static char Completed [STRING] = {0}; /* completed string (command or variable) */ 2886static const char **Matches; 2887/* this is a lie until mutt_init runs: */ 2888static int Matches_listsize = MAX(NUMVARS,NUMCOMMANDS) + 10; 2889 2890static void matches_ensure_morespace(int current) 2891{ 2892 int base_space, extra_space, space; 2893 2894 if (current > Matches_listsize - 2) 2895 { 2896 base_space = MAX(NUMVARS,NUMCOMMANDS) + 1; 2897 extra_space = Matches_listsize - base_space; 2898 extra_space *= 2; 2899 space = base_space + extra_space; 2900 safe_realloc (&Matches, space * sizeof (char *)); 2901 memset (&Matches[current + 1], 0, space - current); 2902 Matches_listsize = space; 2903 } 2904} 2905 2906/* helper function for completion. Changes the dest buffer if 2907 necessary/possible to aid completion. 2908 dest == completion result gets here. 2909 src == candidate for completion. 2910 try == user entered data for completion. 2911 len == length of dest buffer. 2912*/ 2913static void candidate (char *dest, char *try, const char *src, int len) 2914{ 2915 int l; 2916 2917 if (strstr (src, try) == src) 2918 { 2919 matches_ensure_morespace (Num_matched); 2920 Matches[Num_matched++] = src; 2921 if (dest[0] == 0) 2922 strfcpy (dest, src, len); 2923 else 2924 { 2925 for (l = 0; src[l] && src[l] == dest[l]; l++); 2926 dest[l] = 0; 2927 } 2928 } 2929} 2930 2931int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs) 2932{ 2933 char *pt = buffer; 2934 int num; 2935 int spaces; /* keep track of the number of leading spaces on the line */ 2936 myvar_t *myv; 2937 2938 SKIPWS (buffer); 2939 spaces = buffer - pt; 2940 2941 pt = buffer + pos - spaces; 2942 while ((pt > buffer) && !isspace ((unsigned char) *pt)) 2943 pt--; 2944 2945 if (pt == buffer) /* complete cmd */ 2946 { 2947 /* first TAB. Collect all the matches */ 2948 if (numtabs == 1) 2949 { 2950 Num_matched = 0; 2951 strfcpy (User_typed, pt, sizeof (User_typed)); 2952 memset (Matches, 0, Matches_listsize); 2953 memset (Completed, 0, sizeof (Completed)); 2954 for (num = 0; Commands[num].name; num++) 2955 candidate (Completed, User_typed, Commands[num].name, sizeof (Completed)); 2956 matches_ensure_morespace (Num_matched); 2957 Matches[Num_matched++] = User_typed; 2958 2959 /* All matches are stored. Longest non-ambiguous string is "" 2960 * i.e. don't change 'buffer'. Fake successful return this time */ 2961 if (User_typed[0] == 0) 2962 return 1; 2963 } 2964 2965 if (Completed[0] == 0 && User_typed[0]) 2966 return 0; 2967 2968 /* Num_matched will _always_ be at least 1 since the initial 2969 * user-typed string is always stored */ 2970 if (numtabs == 1 && Num_matched == 2) 2971 snprintf(Completed, sizeof(Completed),"%s", Matches[0]); 2972 else if (numtabs > 1 && Num_matched > 2) 2973 /* cycle thru all the matches */ 2974 snprintf(Completed, sizeof(Completed), "%s", 2975 Matches[(numtabs - 2) % Num_matched]); 2976 2977 /* return the completed command */ 2978 strncpy (buffer, Completed, len - spaces); 2979 } 2980 else if (!mutt_strncmp (buffer, "set", 3) 2981 || !mutt_strncmp (buffer, "unset", 5) 2982 || !mutt_strncmp (buffer, "reset", 5) 2983 || !mutt_strncmp (buffer, "toggle", 6)) 2984 { /* complete variables */ 2985 static const char * const prefixes[] = { "no", "inv", "?", "&", 0 }; 2986 2987 pt++; 2988 /* loop through all the possible prefixes (no, inv, ...) */ 2989 if (!mutt_strncmp (buffer, "set", 3)) 2990 { 2991 for (num = 0; prefixes[num]; num++) 2992 { 2993 if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num]))) 2994 { 2995 pt += mutt_strlen (prefixes[num]); 2996 break; 2997 } 2998 } 2999 } 3000 3001 /* first TAB. Collect all the matches */ 3002 if (numtabs == 1) 3003 { 3004 Num_matched = 0; 3005 strfcpy (User_typed, pt, sizeof (User_typed)); 3006 memset (Matches, 0, Matches_listsize); 3007 memset (Completed, 0, sizeof (Completed)); 3008 for (num = 0; MuttVars[num].option; num++) 3009 candidate (Completed, User_typed, MuttVars[num].option, sizeof (Completed)); 3010 for (myv = MyVars; myv; myv = myv->next) 3011 candidate (Completed, User_typed, myv->name, sizeof (Completed)); 3012 matches_ensure_morespace (Num_matched); 3013 Matches[Num_matched++] = User_typed; 3014 3015 /* All matches are stored. Longest non-ambiguous string is "" 3016 * i.e. don't change 'buffer'. Fake successful return this time */ 3017 if (User_typed[0] == 0) 3018 return 1; 3019 } 3020 3021 if (Completed[0] == 0 && User_typed[0]) 3022 return 0; 3023 3024 /* Num_matched will _always_ be at least 1 since the initial 3025 * user-typed string is always stored */ 3026 if (numtabs == 1 && Num_matched == 2) 3027 snprintf(Completed, sizeof(Completed),"%s", Matches[0]); 3028 else if (numtabs > 1 && Num_matched > 2) 3029 /* cycle thru all the matches */ 3030 snprintf(Completed, sizeof(Completed), "%s", 3031 Matches[(numtabs - 2) % Num_matched]); 3032 3033 strncpy (pt, Completed, buffer + len - pt - spaces); 3034 } 3035 else if (!mutt_strncmp (buffer, "exec", 4)) 3036 { 3037 const struct binding_t *menu = km_get_table (CurrentMenu); 3038 3039 if (!menu && CurrentMenu != MENU_PAGER) 3040 menu = OpGeneric; 3041 3042 pt++; 3043 /* first TAB. Collect all the matches */ 3044 if (numtabs == 1) 3045 { 3046 Num_matched = 0; 3047 strfcpy (User_typed, pt, sizeof (User_typed)); 3048 memset (Matches, 0, Matches_listsize); 3049 memset (Completed, 0, sizeof (Completed)); 3050 for (num = 0; menu[num].name; num++) 3051 candidate (Completed, User_typed, menu[num].name, sizeof (Completed)); 3052 /* try the generic menu */ 3053 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) 3054 { 3055 menu = OpGeneric; 3056 for (num = 0; menu[num].name; num++) 3057 candidate (Completed, User_typed, menu[num].name, sizeof (Completed)); 3058 } 3059 matches_ensure_morespace (Num_matched); 3060 Matches[Num_matched++] = User_typed; 3061 3062 /* All matches are stored. Longest non-ambiguous string is "" 3063 * i.e. don't change 'buffer'. Fake successful return this time */ 3064 if (User_typed[0] == 0) 3065 return 1; 3066 } 3067 3068 if (Completed[0] == 0 && User_typed[0]) 3069 return 0; 3070 3071 /* Num_matched will _always_ be at least 1 since the initial 3072 * user-typed string is always stored */ 3073 if (numtabs == 1 && Num_matched == 2) 3074 snprintf(Completed, sizeof(Completed),"%s", Matches[0]); 3075 else if (numtabs > 1 && Num_matched > 2) 3076 /* cycle thru all the matches */ 3077 snprintf(Completed, sizeof(Completed), "%s", 3078 Matches[(numtabs - 2) % Num_matched]); 3079 3080 strncpy (pt, Completed, buffer + len - pt - spaces); 3081 } 3082 else 3083 return 0; 3084 3085 return 1; 3086} 3087 3088int mutt_var_value_complete (char *buffer, size_t len, int pos) 3089{ 3090 char var[STRING], *pt = buffer; 3091 int spaces; 3092 3093 if (buffer[0] == 0) 3094 return 0; 3095 3096 SKIPWS (buffer); 3097 spaces = buffer - pt; 3098 3099 pt = buffer + pos - spaces; 3100 while ((pt > buffer) && !isspace ((unsigned char) *pt)) 3101 pt--; 3102 pt++; /* move past the space */ 3103 if (*pt == '=') /* abort if no var before the '=' */ 3104 return 0; 3105 3106 if (mutt_strncmp (buffer, "set", 3) == 0) 3107 { 3108 int idx; 3109 char val[LONG_STRING]; 3110 const char *myvarval; 3111 3112 strfcpy (var, pt, sizeof (var)); 3113 /* ignore the trailing '=' when comparing */ 3114 var[mutt_strlen (var) - 1] = 0; 3115 if ((idx = mutt_option_index (var)) == -1) 3116 { 3117 if ((myvarval = myvar_get(var)) != NULL) 3118 { 3119 pretty_var (pt, len - (pt - buffer), var, myvarval); 3120 return 1; 3121 } 3122 return 0; /* no such variable. */ 3123 } 3124 else if (var_to_string (idx, val, sizeof (val))) 3125 { 3126 snprintf (pt, len - (pt - buffer), "%s=\"%s\"", var, val); 3127 return 1; 3128 } 3129 } 3130 return 0; 3131} 3132 3133static int var_to_string (int idx, char* val, size_t len) 3134{ 3135 char tmp[LONG_STRING]; 3136 static const char * const vals[] = { "no", "yes", "ask-no", "ask-yes" }; 3137 3138 tmp[0] = '\0'; 3139 3140 if ((DTYPE(MuttVars[idx].type) == DT_STR) || 3141 (DTYPE(MuttVars[idx].type) == DT_PATH) || 3142 (DTYPE(MuttVars[idx].type) == DT_RX)) 3143 { 3144 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data.p)), sizeof (tmp)); 3145 if (DTYPE (MuttVars[idx].type) == DT_PATH) 3146 mutt_pretty_mailbox (tmp, sizeof (tmp)); 3147 } 3148 else if (DTYPE (MuttVars[idx].type) == DT_MBCHARTBL) 3149 { 3150 mbchar_table *mbt = (*((mbchar_table **) MuttVars[idx].data.p)); 3151 strfcpy (tmp, mbt ? NONULL (mbt->orig_str) : "", sizeof (tmp)); 3152 } 3153 else if (DTYPE (MuttVars[idx].type) == DT_ADDR) 3154 { 3155 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data.p), 0); 3156 } 3157 else if (DTYPE (MuttVars[idx].type) == DT_QUAD) 3158 strfcpy (tmp, vals[quadoption (MuttVars[idx].data.l)], sizeof (tmp)); 3159 else if (DTYPE (MuttVars[idx].type) == DT_NUM) 3160 { 3161 short sval = *((short *) MuttVars[idx].data.p); 3162 3163 /* avert your eyes, gentle reader */ 3164 if (mutt_strcmp (MuttVars[idx].option, "wrapmargin") == 0) 3165 sval = sval > 0 ? 0 : -sval; 3166 3167 snprintf (tmp, sizeof (tmp), "%d", sval); 3168 } 3169 else if (DTYPE (MuttVars[idx].type) == DT_LNUM) 3170 { 3171 long sval = *((long *) MuttVars[idx].data.p); 3172 3173 snprintf (tmp, sizeof (tmp), "%ld", sval); 3174 } 3175 else if (DTYPE (MuttVars[idx].type) == DT_SORT) 3176 { 3177 const struct mapping_t *map; 3178 const char *p; 3179 3180 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) 3181 { 3182 case DT_SORT_ALIAS: 3183 map = SortAliasMethods; 3184 break; 3185 case DT_SORT_BROWSER: 3186 map = SortBrowserMethods; 3187 break; 3188 case DT_SORT_KEYS: 3189 if ((WithCrypto & APPLICATION_PGP)) 3190 map = SortKeyMethods; 3191 else 3192 map = SortMethods; 3193 break; 3194 default: 3195 map = SortMethods; 3196 break; 3197 } 3198 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data.p) & SORT_MASK, map); 3199 snprintf (tmp, sizeof (tmp), "%s%s%s", 3200 (*((short *) MuttVars[idx].data.p) & SORT_REVERSE) ? "reverse-" : "", 3201 (*((short *) MuttVars[idx].data.p) & SORT_LAST) ? "last-" : "", 3202 p); 3203 } 3204 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) 3205 { 3206 char *p; 3207 3208 switch (DefaultMagic) 3209 { 3210 case MUTT_MBOX: 3211 p = "mbox"; 3212 break; 3213 case MUTT_MMDF: 3214 p = "MMDF"; 3215 break; 3216 case MUTT_MH: 3217 p = "MH"; 3218 break; 3219 case MUTT_MAILDIR: 3220 p = "Maildir"; 3221 break; 3222 default: 3223 p = "unknown"; 3224 } 3225 strfcpy (tmp, p, sizeof (tmp)); 3226 } 3227 else if (DTYPE (MuttVars[idx].type) == DT_BOOL) 3228 strfcpy (tmp, option (MuttVars[idx].data.l) ? "yes" : "no", sizeof (tmp)); 3229 else 3230 return 0; 3231 3232 escape_string (val, len - 1, tmp); 3233 3234 return 1; 3235} 3236 3237/* Implement the -Q command line flag */ 3238int mutt_query_variables (LIST *queries) 3239{ 3240 LIST *p; 3241 3242 char command[STRING]; 3243 3244 BUFFER err, token; 3245 3246 mutt_buffer_init (&err); 3247 mutt_buffer_init (&token); 3248 3249 err.dsize = STRING; 3250 err.data = safe_malloc (err.dsize); 3251 3252 for (p = queries; p; p = p->next) 3253 { 3254 snprintf (command, sizeof (command), "set ?%s\n", p->data); 3255 if (mutt_parse_rc_line (command, &token, &err) == -1) 3256 { 3257 fprintf (stderr, "%s\n", err.data); 3258 FREE (&token.data); 3259 FREE (&err.data); 3260 3261 return 1; 3262 } 3263 printf ("%s\n", err.data); 3264 } 3265 3266 FREE (&token.data); 3267 FREE (&err.data); 3268 3269 return 0; 3270} 3271 3272/* dump out the value of all the variables we have */ 3273int mutt_dump_variables (void) 3274{ 3275 int i; 3276 3277 char command[STRING]; 3278 3279 BUFFER err, token; 3280 3281 mutt_buffer_init (&err); 3282 mutt_buffer_init (&token); 3283 3284 err.dsize = STRING; 3285 err.data = safe_malloc (err.dsize); 3286 3287 for (i = 0; MuttVars[i].option; i++) 3288 { 3289 if (MuttVars[i].type == DT_SYN) 3290 continue; 3291 3292 snprintf (command, sizeof (command), "set ?%s\n", MuttVars[i].option); 3293 if (mutt_parse_rc_line (command, &token, &err) == -1) 3294 { 3295 fprintf (stderr, "%s\n", err.data); 3296 FREE (&token.data); 3297 FREE (&err.data); 3298 3299 return 1; 3300 } 3301 printf("%s\n", err.data); 3302 } 3303 3304 FREE (&token.data); 3305 FREE (&err.data); 3306 3307 return 0; 3308} 3309 3310const char *mutt_getnamebyvalue (int val, const struct mapping_t *map) 3311{ 3312 int i; 3313 3314 for (i=0; map[i].name; i++) 3315 if (map[i].value == val) 3316 return (map[i].name); 3317 return NULL; 3318} 3319 3320int mutt_getvaluebyname (const char *name, const struct mapping_t *map) 3321{ 3322 int i; 3323 3324 for (i = 0; map[i].name; i++) 3325 if (ascii_strcasecmp (map[i].name, name) == 0) 3326 return (map[i].value); 3327 return (-1); 3328} 3329 3330#ifdef DEBUG 3331static void start_debug (void) 3332{ 3333 int i; 3334 BUFFER *buf, *buf2; 3335 3336 buf = mutt_buffer_pool_get (); 3337 buf2 = mutt_buffer_pool_get (); 3338 3339 /* rotate the old debug logs */ 3340 for (i=3; i>=0; i--) 3341 { 3342 mutt_buffer_printf (buf, "%s/.muttdebug%d", NONULL(Homedir), i); 3343 mutt_buffer_printf (buf2, "%s/.muttdebug%d", NONULL(Homedir), i+1); 3344 rename (mutt_b2s (buf), mutt_b2s (buf2)); 3345 } 3346 if ((debugfile = safe_fopen(mutt_b2s (buf), "w")) != NULL) 3347 { 3348 setbuf (debugfile, NULL); /* don't buffer the debugging output! */ 3349 dprint(1,(debugfile,"Mutt/%s (%s) debugging at level %d\n", 3350 MUTT_VERSION, ReleaseDate, debuglevel)); 3351 } 3352 3353 mutt_buffer_pool_release (&buf); 3354 mutt_buffer_pool_release (&buf2); 3355} 3356#endif 3357 3358static int mutt_execute_commands (LIST *p) 3359{ 3360 BUFFER err, token; 3361 3362 mutt_buffer_init (&err); 3363 err.dsize = STRING; 3364 err.data = safe_malloc (err.dsize); 3365 mutt_buffer_init (&token); 3366 for (; p; p = p->next) 3367 { 3368 if (mutt_parse_rc_line (p->data, &token, &err) != 0) 3369 { 3370 fprintf (stderr, _("Error in command line: %s\n"), err.data); 3371 FREE (&token.data); 3372 FREE (&err.data); 3373 3374 return -1; 3375 } 3376 } 3377 FREE (&token.data); 3378 FREE (&err.data); 3379 3380 return 0; 3381} 3382 3383static void mutt_srandom (void) 3384{ 3385 struct timeval tv; 3386 unsigned seed; 3387 3388 gettimeofday(&tv, NULL); 3389 /* POSIX.1-2008 states that seed is 'unsigned' without specifying its width. 3390 * Use as many of the lower order bits from the current time of day as the seed. 3391 * If the upper bound is truncated, that is fine. 3392 * 3393 * tv_sec is integral of type integer or float. Cast to 'long long' before 3394 * bitshift in case it is a float. 3395 */ 3396 seed = ((LONGLONG) tv.tv_sec << 20) | tv.tv_usec; 3397 srandom(seed); 3398} 3399 3400static char* mutt_find_cfg (const char *home, const char *xdg_cfg_home) 3401{ 3402 const char* names[] = 3403 { 3404 "muttrc-" MUTT_VERSION, 3405 "muttrc", 3406 NULL, 3407 }; 3408 3409 const char* locations[][2] = 3410 { 3411 { home, ".", }, 3412 { home, ".mutt/" }, 3413 { xdg_cfg_home, "mutt/", }, 3414 { NULL, NULL }, 3415 }; 3416 3417 int i; 3418 3419 for (i = 0; locations[i][0] || locations[i][1]; i++) 3420 { 3421 int j; 3422 3423 if (!locations[i][0]) 3424 continue; 3425 3426 for (j = 0; names[j]; j++) 3427 { 3428 char buffer[STRING]; 3429 3430 snprintf (buffer, sizeof (buffer), 3431 "%s/%s%s", locations[i][0], locations[i][1], names[j]); 3432 if (access (buffer, F_OK) == 0) 3433 return safe_strdup(buffer); 3434 } 3435 } 3436 3437 return NULL; 3438} 3439 3440void mutt_init (int skip_sys_rc, LIST *commands) 3441{ 3442 struct passwd *pw; 3443 struct utsname utsname; 3444 char *p, buffer[STRING]; 3445 char *domain = NULL; 3446 int i, need_pause = 0; 3447 BUFFER err; 3448 3449 mutt_buffer_init (&err); 3450 mutt_buffer_increase_size (&err, STRING); 3451 3452 Groups = hash_create (1031, 0); 3453 /* reverse alias keys need to be strdup'ed because of idna conversions */ 3454 ReverseAlias = hash_create (1031, MUTT_HASH_STRCASECMP | MUTT_HASH_STRDUP_KEYS | 3455 MUTT_HASH_ALLOW_DUPS); 3456 3457 mutt_menu_init (); 3458 mutt_srandom (); 3459 mutt_buffer_pool_init (); 3460 3461 /* 3462 * XXX - use something even more difficult to predict? 3463 */ 3464 snprintf (AttachmentMarker, sizeof (AttachmentMarker), 3465 "\033]9;%ld\a", (long) time (NULL)); 3466 snprintf (ProtectedHeaderMarker, sizeof (ProtectedHeaderMarker), 3467 "\033]8;%ld\a", (long) time (NULL)); 3468 3469 /* on one of the systems I use, getcwd() does not return the same prefix 3470 as is listed in the passwd file */ 3471 if ((p = getenv ("HOME"))) 3472 Homedir = safe_strdup (p); 3473 3474 /* Get some information about the user */ 3475 if ((pw = getpwuid (getuid ()))) 3476 { 3477 char rnbuf[STRING]; 3478 3479 Username = safe_strdup (pw->pw_name); 3480 if (!Homedir) 3481 Homedir = safe_strdup (pw->pw_dir); 3482 3483 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw)); 3484 Shell = safe_strdup (pw->pw_shell); 3485 endpwent (); 3486 } 3487 else 3488 { 3489 if (!Homedir) 3490 { 3491 mutt_endwin (NULL); 3492 fputs (_("unable to determine home directory"), stderr); 3493 exit (1); 3494 } 3495 if ((p = getenv ("USER"))) 3496 Username = safe_strdup (p); 3497 else 3498 { 3499 mutt_endwin (NULL); 3500 fputs (_("unable to determine username"), stderr); 3501 exit (1); 3502 } 3503 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh"); 3504 } 3505 3506#ifdef DEBUG 3507 /* Start up debugging mode if requested */ 3508 if (debuglevel > 0) 3509 start_debug (); 3510#endif 3511 3512 /* And about the host... */ 3513 3514#ifdef DOMAIN 3515 domain = safe_strdup (DOMAIN); 3516#endif /* DOMAIN */ 3517 3518 /* 3519 * The call to uname() shouldn't fail, but if it does, the system is horribly 3520 * broken, and the system's networking configuration is in an unreliable 3521 * state. We should bail. 3522 */ 3523 if ((uname (&utsname)) == -1) 3524 { 3525 mutt_endwin (NULL); 3526 perror (_("unable to determine nodename via uname()")); 3527 exit (1); 3528 } 3529 3530 /* some systems report the FQDN instead of just the hostname */ 3531 if ((p = strchr (utsname.nodename, '.'))) 3532 Hostname = mutt_substrdup (utsname.nodename, p); 3533 else 3534 Hostname = safe_strdup (utsname.nodename); 3535 3536 /* now get FQDN. Use configured domain first, DNS next, then uname */ 3537 if (domain) 3538 { 3539 /* we have a compile-time domain name, use that for Fqdn */ 3540 Fqdn = safe_malloc (mutt_strlen (domain) + mutt_strlen (Hostname) + 2); 3541 sprintf (Fqdn, "%s.%s", NONULL(Hostname), domain); /* __SPRINTF_CHECKED__ */ 3542 } 3543 else if (!(getdnsdomainname (buffer, sizeof buffer))) 3544 { 3545 Fqdn = safe_malloc (mutt_strlen (buffer) + mutt_strlen (Hostname) + 2); 3546 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer); /* __SPRINTF_CHECKED__ */ 3547 } 3548 else 3549 /* 3550 * DNS failed, use the nodename. Whether or not the nodename had a '.' in 3551 * it, we can use the nodename as the FQDN. On hosts where DNS is not 3552 * being used, e.g. small network that relies on hosts files, a short host 3553 * name is all that is required for SMTP to work correctly. It could be 3554 * wrong, but we've done the best we can, at this point the onus is on the 3555 * user to provide the correct hostname if the nodename won't work in their 3556 * network. 3557 */ 3558 Fqdn = safe_strdup(utsname.nodename); 3559 3560 3561 if ((p = getenv ("MAIL"))) 3562 Spoolfile = safe_strdup (p); 3563 else if ((p = getenv ("MAILDIR"))) 3564 Spoolfile = safe_strdup (p); 3565 else 3566 { 3567#ifdef HOMESPOOL 3568 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer)); 3569#else 3570 mutt_concat_path (buffer, MAILPATH, NONULL(Username), sizeof (buffer)); 3571#endif 3572 Spoolfile = safe_strdup (buffer); 3573 } 3574 3575 if ((p = getenv ("MAILCAPS"))) 3576 MailcapPath = safe_strdup (p); 3577 else 3578 { 3579 /* Default search path from RFC1524 */ 3580 MailcapPath = safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap"); 3581 } 3582 3583 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp"); 3584 3585 p = getenv ("VISUAL"); 3586 if (!p) 3587 { 3588 p = getenv ("EDITOR"); 3589 if (!p) 3590 p = "vi"; 3591 } 3592 Editor = safe_strdup (p); 3593 Visual = safe_strdup (p); 3594 3595 if ((p = getenv ("REPLYTO")) != NULL) 3596 { 3597 BUFFER buf, token; 3598 union pointer_long_t udata = {.l=0}; 3599 3600 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p); 3601 3602 mutt_buffer_init (&buf); 3603 buf.data = buf.dptr = buffer; 3604 buf.dsize = mutt_strlen (buffer); 3605 3606 mutt_buffer_init (&token); 3607 parse_my_hdr (&token, &buf, udata, &err); 3608 FREE (&token.data); 3609 } 3610 3611 if ((p = getenv ("EMAIL")) != NULL) 3612 From = rfc822_parse_adrlist (NULL, p); 3613 3614 mutt_set_langinfo_charset (); 3615 mutt_set_charset (Charset); 3616 3617 Matches = safe_calloc (Matches_listsize, sizeof (char *)); 3618 3619 /* Set standard defaults */ 3620 for (i = 0; MuttVars[i].option; i++) 3621 { 3622 mutt_set_default (&MuttVars[i]); 3623 mutt_restore_default (&MuttVars[i]); 3624 } 3625 3626 CurrentMenu = MENU_MAIN; 3627 3628 3629#ifndef LOCALES_HACK 3630 /* Do we have a locale definition? */ 3631 if (((p = getenv ("LC_ALL")) != NULL && p[0]) || 3632 ((p = getenv ("LANG")) != NULL && p[0]) || 3633 ((p = getenv ("LC_CTYPE")) != NULL && p[0])) 3634 set_option (OPTLOCALES); 3635#endif 3636 3637#ifdef HAVE_GETSID 3638 /* Unset suspend by default if we're the session leader */ 3639 if (getsid(0) == getpid()) 3640 unset_option (OPTSUSPEND); 3641#endif 3642 3643 mutt_init_history (); 3644 mutt_error_history_init (); 3645 3646 /* RFC2368, "4. Unsafe headers" 3647 * The creator of a mailto URL cannot expect the resolver of a URL to 3648 * understand more than the "subject" and "body" headers. Clients that 3649 * resolve mailto URLs into mail messages should be able to correctly 3650 * create RFC 822-compliant mail messages using the "subject" and "body" 3651 * headers. 3652 */ 3653 add_to_list(&MailtoAllow, "body"); 3654 add_to_list(&MailtoAllow, "subject"); 3655 3656 if (!Muttrc) 3657 { 3658 char *xdg_cfg_home = getenv ("XDG_CONFIG_HOME"); 3659 3660 if (!xdg_cfg_home && Homedir) 3661 { 3662 snprintf (buffer, sizeof (buffer), "%s/.config", Homedir); 3663 xdg_cfg_home = buffer; 3664 } 3665 3666 Muttrc = mutt_find_cfg (Homedir, xdg_cfg_home); 3667 } 3668 else 3669 { 3670 strfcpy (buffer, Muttrc, sizeof (buffer)); 3671 FREE (&Muttrc); 3672 mutt_expand_path (buffer, sizeof (buffer)); 3673 Muttrc = safe_strdup (buffer); 3674 if (access (Muttrc, F_OK)) 3675 { 3676 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno)); 3677 mutt_endwin (buffer); 3678 exit (1); 3679 } 3680 } 3681 3682 if (Muttrc) 3683 { 3684 FREE (&AliasFile); 3685 AliasFile = safe_strdup (Muttrc); 3686 } 3687 3688 /* Process the global rc file if it exists and the user hasn't explicitly 3689 requested not to via "-n". */ 3690 if (!skip_sys_rc) 3691 { 3692 snprintf (buffer, sizeof(buffer), "%s/Muttrc-%s", SYSCONFDIR, MUTT_VERSION); 3693 if (access (buffer, F_OK) == -1) 3694 snprintf (buffer, sizeof(buffer), "%s/Muttrc", SYSCONFDIR); 3695 if (access (buffer, F_OK) == -1) 3696 snprintf (buffer, sizeof (buffer), "%s/Muttrc-%s", PKGDATADIR, MUTT_VERSION); 3697 if (access (buffer, F_OK) == -1) 3698 snprintf (buffer, sizeof (buffer), "%s/Muttrc", PKGDATADIR); 3699 if (access (buffer, F_OK) != -1) 3700 { 3701 if (source_rc (buffer, &err) != 0) 3702 { 3703 fputs (err.data, stderr); 3704 fputc ('\n', stderr); 3705 need_pause = 1; 3706 } 3707 } 3708 } 3709 3710 /* Read the user's initialization file. */ 3711 if (Muttrc) 3712 { 3713 if (!option (OPTNOCURSES)) 3714 endwin (); 3715 if (source_rc (Muttrc, &err) != 0) 3716 { 3717 fputs (err.data, stderr); 3718 fputc ('\n', stderr); 3719 need_pause = 1; 3720 } 3721 } 3722 3723 if (mutt_execute_commands (commands) != 0) 3724 need_pause = 1; 3725 3726 if (need_pause && !option (OPTNOCURSES)) 3727 { 3728 if (mutt_any_key_to_continue (NULL) == -1) 3729 mutt_exit(1); 3730 } 3731 3732 mutt_read_histfile (); 3733 3734 FREE (&err.data); 3735} 3736 3737int mutt_get_hook_type (const char *name) 3738{ 3739 const struct command_t *c; 3740 3741 for (c = Commands ; c->name ; c++) 3742 if ((c->func == mutt_parse_hook || c->func == mutt_parse_idxfmt_hook) && 3743 ascii_strcasecmp (c->name, name) == 0) 3744 return c->data.l; 3745 return 0; 3746} 3747 3748static int parse_group_context (group_context_t **ctx, BUFFER *buf, BUFFER *s, BUFFER *err) 3749{ 3750 while (!mutt_strcasecmp (buf->data, "-group")) 3751 { 3752 if (!MoreArgs (s)) 3753 { 3754 strfcpy (err->data, _("-group: no group name"), err->dsize); 3755 goto bail; 3756 } 3757 3758 mutt_extract_token (buf, s, 0); 3759 3760 mutt_group_context_add (ctx, mutt_pattern_group (buf->data)); 3761 3762 if (!MoreArgs (s)) 3763 { 3764 strfcpy (err->data, _("out of arguments"), err->dsize); 3765 goto bail; 3766 } 3767 3768 mutt_extract_token (buf, s, 0); 3769 } 3770 3771 return 0; 3772 3773bail: 3774 mutt_group_context_destroy (ctx); 3775 return -1; 3776} 3777 3778static void myvar_set (const char* var, const char* val) 3779{ 3780 myvar_t** cur; 3781 3782 for (cur = &MyVars; *cur; cur = &((*cur)->next)) 3783 if (!mutt_strcmp ((*cur)->name, var)) 3784 break; 3785 3786 if (!*cur) 3787 *cur = safe_calloc (1, sizeof (myvar_t)); 3788 3789 if (!(*cur)->name) 3790 (*cur)->name = safe_strdup (var); 3791 3792 mutt_str_replace (&(*cur)->value, val); 3793} 3794 3795static void myvar_del (const char* var) 3796{ 3797 myvar_t **cur; 3798 myvar_t *tmp; 3799 3800 3801 for (cur = &MyVars; *cur; cur = &((*cur)->next)) 3802 if (!mutt_strcmp ((*cur)->name, var)) 3803 break; 3804 3805 if (*cur) 3806 { 3807 tmp = (*cur)->next; 3808 FREE (&(*cur)->name); 3809 FREE (&(*cur)->value); 3810 FREE (cur); /* __FREE_CHECKED__ */ 3811 *cur = tmp; 3812 } 3813} 3814 3815static const char* myvar_get (const char* var) 3816{ 3817 myvar_t* cur; 3818 3819 for (cur = MyVars; cur; cur = cur->next) 3820 if (!mutt_strcmp (cur->name, var)) 3821 return NONULL(cur->value); 3822 3823 return NULL; 3824} 3825 3826int mutt_label_complete (char *buffer, size_t len, int numtabs) 3827{ 3828 char *pt = buffer; 3829 int spaces; /* keep track of the number of leading spaces on the line */ 3830 3831 if (!Context || !Context->label_hash) 3832 return 0; 3833 3834 SKIPWS (buffer); 3835 spaces = buffer - pt; 3836 3837 /* first TAB. Collect all the matches */ 3838 if (numtabs == 1) 3839 { 3840 struct hash_elem *entry; 3841 struct hash_walk_state state; 3842 3843 Num_matched = 0; 3844 strfcpy (User_typed, buffer, sizeof (User_typed)); 3845 memset (Matches, 0, Matches_listsize); 3846 memset (Completed, 0, sizeof (Completed)); 3847 memset (&state, 0, sizeof(state)); 3848 while ((entry = hash_walk(Context->label_hash, &state))) 3849 candidate (Completed, User_typed, entry->key.strkey, sizeof (Completed)); 3850 matches_ensure_morespace (Num_matched); 3851 qsort(Matches, Num_matched, sizeof(char *), (sort_t *) mutt_strcasecmp); 3852 Matches[Num_matched++] = User_typed; 3853 3854 /* All matches are stored. Longest non-ambiguous string is "" 3855 * i.e. dont change 'buffer'. Fake successful return this time */ 3856 if (User_typed[0] == 0) 3857 return 1; 3858 } 3859 3860 if (Completed[0] == 0 && User_typed[0]) 3861 return 0; 3862 3863 /* Num_matched will _always_ be atleast 1 since the initial 3864 * user-typed string is always stored */ 3865 if (numtabs == 1 && Num_matched == 2) 3866 snprintf(Completed, sizeof(Completed), "%s", Matches[0]); 3867 else if (numtabs > 1 && Num_matched > 2) 3868 /* cycle thru all the matches */ 3869 snprintf(Completed, sizeof(Completed), "%s", 3870 Matches[(numtabs - 2) % Num_matched]); 3871 3872 /* return the completed label */ 3873 strncpy (buffer, Completed, len - spaces); 3874 3875 return 1; 3876}