diff -rc alpine-2.00/alpine/adrbkcmd.c alpine-2.00.I.USE/alpine/adrbkcmd.c *** alpine-2.00/alpine/adrbkcmd.c 2008-05-30 09:52:41.000000000 -0700 --- alpine-2.00.I.USE/alpine/adrbkcmd.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 57,62 **** --- 57,64 ---- #include "../pith/send.h" #include "../pith/list.h" #include "../pith/busy.h" + #include "../pith/icache.h" + #include "../pith/osdep/color.h" /* internal prototypes */ *************** *** 1146,1151 **** --- 1148,1155 ---- q_status_message1(SM_INFO, 0, 2, _("Address book %s cancelled"), cmd); } else if(editor_result & COMP_EXIT){ + if(pico_usingcolor()) + clear_index_cache(ps_global->mail_stream, 0); removing_leading_and_trailing_white_space(nick); removing_leading_and_trailing_white_space(full); removing_leading_and_trailing_white_space(fcc); *************** *** 4123,4128 **** --- 4127,4134 ---- * won't do anything, but will cause compose_mail to think there's * already a role so that it won't try to confirm the default. */ + if (ps_global->role) + fs_give((void **)&ps_global->role); if(role) role = copy_action(role); else{ *************** *** 4130,4135 **** --- 4136,4142 ---- memset((void *)role, 0, sizeof(*role)); role->nick = cpystr("Default Role"); } + ps_global->role = cpystr(role->nick); } compose_mail(addr, fcc, role, NULL, NULL); diff -rc alpine-2.00/alpine/alpine.c alpine-2.00.I.USE/alpine/alpine.c *** alpine-2.00/alpine/alpine.c 2008-06-03 15:31:05.000000000 -0700 --- alpine-2.00.I.USE/alpine/alpine.c 2011-02-07 20:33:45.000000000 -0800 *************** *** 193,198 **** --- 193,199 ---- #endif status_message_lock_init(); + inverse_itokens(); #if HAVE_SRANDOM /* *************** *** 293,298 **** --- 294,300 ---- exit(-1); } + mail_parameters(NULL, SET_QUOTA, (void *) pine_parse_quota); /* set some default timeouts in case pinerc is remote */ mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long)30); mail_parameters(NULL, SET_READTIMEOUT, (void *)(long)15); *************** *** 303,308 **** --- 305,311 ---- mail_parameters(NULL, SET_SENDCOMMAND, (void *) pine_imap_cmd_happened); mail_parameters(NULL, SET_FREESTREAMSPAREP, (void *) sp_free_callback); mail_parameters(NULL, SET_FREEELTSPAREP, (void *) free_pine_elt); + mail_parameters(NULL, SET_ERASEPASSWORD, (void *) pine_delete_pwd); #ifdef SMIME mail_parameters(NULL, SET_FREEBODYSPAREP, (void *) free_smime_body_sparep); #endif *************** *** 454,459 **** --- 457,467 ---- convert_args_to_utf8(pine_state, &args); + if (args.action == aaFolder && !args.data.folder && + ps_global->send_immediately){ + printf(_("No value for To: field specified\n")); + exit(-1); + } if(args.action == aaFolder){ pine_state->beginning_of_month = first_run_of_month(); pine_state->beginning_of_year = first_run_of_year(); *************** *** 462,467 **** --- 470,476 ---- /* Set up optional for user-defined display filtering */ pine_state->tools.display_filter = dfilter; pine_state->tools.display_filter_trigger = dfilter_trigger; + pine_state->tools.exec_rule = exec_function_rule; #ifdef _WINDOWS if(ps_global->install_flag){ *************** *** 553,558 **** --- 562,572 ---- if(F_ON(F_MAILDROPS_PRESERVE_STATE, ps_global)) mail_parameters(NULL, SET_SNARFPRESERVE, (void *) TRUE); + #ifndef _WINDOWS + mail_parameters(NULL,SET_COURIERSTYLE, + (void *)(F_ON(F_COURIER_FOLDER_LIST, ps_global) ? 1 : 0)); + #endif + rvl = 0L; if(pine_state->VAR_NNTPRANGE){ if(!SVAR_NNTPRANGE(pine_state, rvl, tmp_20k_buf, SIZEOF_20KBUF)) *************** *** 672,677 **** --- 686,692 ---- /*--- output side ---*/ + if (!ps_global->send_immediately){ rv = config_screen(&(pine_state->ttyo)); #ifndef _WINDOWS /* always succeeds under _WINDOWS */ if(rv){ *************** *** 712,723 **** /* initialize titlebar in case we use it */ set_titlebar("", NULL, NULL, NULL, NULL, 0, FolderName, 0, 0, NULL); - /* - * Prep storage object driver for PicoText - */ - so_register_external_driver(pine_pico_get, pine_pico_give, pine_pico_writec, pine_pico_readc, - pine_pico_puts, pine_pico_seek, NULL, NULL); - #ifdef DEBUG if(ps_global->debug_imap > 4 || debug > 9){ q_status_message(SM_ORDER | SM_DING, 5, 9, --- 727,732 ---- *************** *** 725,730 **** --- 734,752 ---- flush_status_messages(0); } #endif + } + else{ + fake_config_screen(&(pine_state->ttyo)); + init_folders(pine_state); /* digest folder spec's */ + } + + /* + * Prep storage object driver for PicoText + */ + so_register_external_driver(pine_pico_get, pine_pico_give, + (args.noutf8 == 0 ? pine_pico_writec : pine_pico_writec_noucs), + (args.noutf8 == 0 ? pine_pico_readc : pine_pico_readc_noucs), + (args.noutf8 == 0 ? pine_pico_puts : pine_pico_puts_noucs), pine_pico_seek, NULL, NULL); if(args.action == aaPrcCopy || args.action == aaAbookCopy){ int exit_val = -1; *************** *** 900,905 **** --- 922,933 ---- int len, good_addr = 1; int exit_val = 0; BUILDER_ARG fcc; + ACTION_S *role = NULL; + + if (pine_state->in_init_seq && pine_state->send_immediately + && (char) *pine_state->initial_cmds++ == '#' + && ++pine_state->initial_cmds_offset) + role_select_screen(pine_state, &role, 1); if(pine_state->in_init_seq){ pine_state->in_init_seq = pine_state->save_in_init_seq = 0; *************** *** 938,944 **** memset(&fcc, 0, sizeof(fcc)); if(good_addr){ ! compose_mail(addr, fcc.tptr, NULL, args.data.mail.attachlist, stdin_getc); } else{ --- 966,972 ---- memset(&fcc, 0, sizeof(fcc)); if(good_addr){ ! compose_mail(addr, fcc.tptr, role, args.data.mail.attachlist, stdin_getc); } else{ *************** *** 972,977 **** --- 1000,1006 ---- pine_state->mail_stream = NULL; pine_state->mangled_screen = 1; + pine_state->subject = NULL; if(args.action == aaURL){ url_tool_t f; *************** *** 1087,1092 **** --- 1116,1122 ---- } } + if (!pine_state->send_immediately) fflush(stdout); #if !defined(_WINDOWS) && !defined(LEAVEOUTFIFO) *************** *** 2975,2984 **** if(i > 0){ ps->initial_cmds = (int *)fs_get((i+1) * sizeof(int)); ps->free_initial_cmds = ps->initial_cmds; for(j = 0; j < i; j++) ! ps->initial_cmds[j] = i_cmds[j]; ! ! ps->initial_cmds[i] = 0; ps->in_init_seq = ps->save_in_init_seq = 1; } } --- 3005,3019 ---- if(i > 0){ ps->initial_cmds = (int *)fs_get((i+1) * sizeof(int)); ps->free_initial_cmds = ps->initial_cmds; + ps->initial_cmds_backup = (int *)fs_get((i+1) * sizeof(int)); + ps->free_initial_cmds_backup = ps->initial_cmds_backup; for(j = 0; j < i; j++) ! ps->initial_cmds[j] = ps->initial_cmds_backup[j] = i_cmds[j]; ! #define ctrl_x 24 ! if (i > 1) ! ps->send_immediately = i_cmds[i - 2] == ctrl_x ! && ((i_cmds[i - 1] == 'y') || (i_cmds[i-1] == 'Y')); ! ps->initial_cmds[i] = ps->initial_cmds_backup[i] = 0; ps->in_init_seq = ps->save_in_init_seq = 1; } } *************** *** 3134,3139 **** --- 3169,3177 ---- extern KBESC_T *kbesc; dprint((2, "goodnight_gracey:\n")); + strncpy(pine_state->cur_folder, pine_state->inbox_name, + sizeof(pine_state->cur_folder)); + pine_state->cur_folder[sizeof(pine_state->cur_folder) - 1] = '\0'; /* We want to do this here before we close up the streams */ trim_remote_adrbks(); diff -rc alpine-2.00/alpine/arg.c alpine-2.00.I.USE/alpine/arg.c *** alpine-2.00/alpine/arg.c 2008-01-04 14:49:15.000000000 -0800 --- alpine-2.00.I.USE/alpine/arg.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 59,64 **** --- 59,65 ---- static char args_err_non_abs_passfile[] = N_("argument to \"-passfile\" should be fully-qualified"); #endif static char args_err_missing_sort[] = N_("missing argument for option \"-sort\""); + static char args_err_missing_thread_sort[] = N_("missing argument for option \"-threadsort\""); static char args_err_missing_flag_arg[] = N_("missing argument for flag \"%c\""); static char args_err_missing_flag_num[] = N_("Non numeric argument for flag \"%c\""); static char args_err_missing_debug_num[] = N_("Non numeric argument for \"%s\""); *************** *** 102,112 **** --- 103,115 ---- N_(" -z \t\tSuspend - allow use of ^Z suspension"), N_(" -r \t\tRestricted - can only send mail to oneself"), N_(" -sort \tSort - Specify sort order of folder:"), + N_(" -threadsort \tSort - Specify sort order of thread index screen:"), N_("\t\t\tarrival, subject, threaded, orderedsubject, date,"), N_("\t\t\tfrom, size, score, to, cc, /reverse"), N_(" -i\t\tIndex - Go directly to index, bypassing main menu"), N_(" -I Initial keystrokes to be executed"), N_(" -n \tEntry in index to begin on"), + N_(" -noutf8\t Warns Alpine that piped input is not encoded in utf-8"), N_(" -o \t\tReadOnly - Open first folder read-only"), N_(" -conf\t\tConfiguration - Print out fresh global configuration. The"), N_("\t\tvalues of your global configuration affect all Alpine users"), *************** *** 191,196 **** --- 194,200 ---- char *cmd_list = NULL; char *debug_str = NULL; char *sort = NULL; + char *threadsort = NULL; char *pinerc_file = NULL; char *lc = NULL; int do_help = 0; *************** *** 362,367 **** --- 366,382 ---- goto Loop; } + else if(strcmp(*av, "threadsort") == 0){ + if(--ac){ + threadsort = *++av; + COM_THREAD_SORT_KEY = cpystr(threadsort); + } + else{ + display_args_err(_(args_err_missing_thread_sort), NULL, 1); + ++usage; + } + goto Loop; + } else if(strcmp(*av, "url") == 0){ if(args->action == aaFolder && !args->data.folder){ args->action = aaURL; *************** *** 468,473 **** --- 483,492 ---- goto Loop; } + else if(strcmp(*av, "noutf8") == 0){ + args->noutf8++; + goto Loop; + } else if(strcmp(*av, "bail") == 0){ pine_state->exit_if_no_pinerc = 1; goto Loop; *************** *** 476,481 **** --- 495,506 ---- do_version = 1; goto Loop; } + else if(strcmp(*av, "subject") == 0){ + if(--ac){ + pine_state->subject = cpystr(*++av); + } + goto Loop; + } #ifdef _WINDOWS else if(strcmp(*av, "install") == 0){ pine_state->install_flag = 1; *************** *** 826,839 **** exit(-1); if(do_version){ ! extern char datestamp[], hoststamp[]; char rev[128]; ! snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Alpine %s (%s %s) built %s on %s", ALPINE_VERSION, SYSTYPE ? SYSTYPE : "?", get_alpine_revision_string(rev, sizeof(rev)), ! datestamp, hoststamp); tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; display_args_err(tmp_20k_buf, NULL, 0); exit(0); --- 851,864 ---- exit(-1); if(do_version){ ! extern char datestamp[], hoststamp[], plevstamp[]; char rev[128]; ! snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Alpine %s (%s %s) built %s on %s, using patchlevel %s.", ALPINE_VERSION, SYSTYPE ? SYSTYPE : "?", get_alpine_revision_string(rev, sizeof(rev)), ! datestamp, hoststamp, plevstamp); tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; display_args_err(tmp_20k_buf, NULL, 0); exit(0); diff -rc alpine-2.00/alpine/arg.h alpine-2.00.I.USE/alpine/arg.h *** alpine-2.00/alpine/arg.h 2006-09-22 13:06:05.000000000 -0700 --- alpine-2.00.I.USE/alpine/arg.h 2011-02-07 20:33:45.000000000 -0800 *************** *** 27,32 **** --- 27,33 ---- typedef struct argdata { enum {aaFolder = 0, aaMore, aaURL, aaMail, aaPrcCopy, aaAbookCopy} action; + int noutf8; union { char *folder; char *file; diff -rc alpine-2.00/alpine/busy.c alpine-2.00.I.USE/alpine/busy.c *** alpine-2.00/alpine/busy.c 2007-10-15 14:02:56.000000000 -0700 --- alpine-2.00.I.USE/alpine/busy.c 2011-02-07 20:33:42.000000000 -0800 *************** *** 194,200 **** add_review_message(buf, -1); } ! else{ q_status_message(SM_ORDER, 0, 1, progress); /* --- 194,200 ---- add_review_message(buf, -1); } ! else if (!ps_global->send_immediately){ q_status_message(SM_ORDER, 0, 1, progress); /* *************** *** 206,213 **** */ display_message('x'); } ! ! fflush(stdout); } /* --- 206,213 ---- */ display_message('x'); } ! if (!ps_global->send_immediately) ! fflush(stdout); } /* *************** *** 255,261 **** (*ap)->cf = done_busy_cue; ap = &(*ap)->next; ! start_after(a); /* launch cue handler */ #ifdef _WINDOWS mswin_setcursor(MSWIN_CURSOR_BUSY); --- 255,262 ---- (*ap)->cf = done_busy_cue; ap = &(*ap)->next; ! if(!ps_global->send_immediately) ! start_after(a); /* launch cue handler */ #ifdef _WINDOWS mswin_setcursor(MSWIN_CURSOR_BUSY); *************** *** 404,409 **** --- 405,415 ---- { int space_left, slots_used; + if (ps_global->send_immediately){ + mark_status_dirty(); + return; + } + if(final_message && final_message_pri >= 0){ char progress[MAX_SCREEN_COLS+1]; diff -rc alpine-2.00/alpine/colorconf.c alpine-2.00.I.USE/alpine/colorconf.c *** alpine-2.00/alpine/colorconf.c 2008-02-08 10:39:55.000000000 -0800 --- alpine-2.00.I.USE/alpine/colorconf.c 2011-02-07 20:33:45.000000000 -0800 *************** *** 28,33 **** --- 28,34 ---- #include "../pith/color.h" #include "../pith/icache.h" #include "../pith/mailcmd.h" + #include "../pith/mailindx.h" #include "../pith/list.h" *************** *** 39,45 **** SAVED_CONFIG_S *save_color_config_vars(struct pine *); void free_saved_color_config(struct pine *, SAVED_CONFIG_S **); void color_config_init_display(struct pine *, CONF_S **, CONF_S **); ! void add_header_color_line(struct pine *, CONF_S **, char *, int); int is_rgb_color(char *); char *new_color_line(char *, int, int, int); int color_text_tool(struct pine *, int, CONF_S **, unsigned); --- 40,46 ---- SAVED_CONFIG_S *save_color_config_vars(struct pine *); void free_saved_color_config(struct pine *, SAVED_CONFIG_S **); void color_config_init_display(struct pine *, CONF_S **, CONF_S **); ! void add_header_color_line(struct pine *, CONF_S **, char *, int, int); int is_rgb_color(char *); char *new_color_line(char *, int, int, int); int color_text_tool(struct pine *, int, CONF_S **, unsigned); *************** *** 141,147 **** if((v && v->name && srchstr(v->name, "-foreground-color") && pvalfg && pvalfg[0] && pvalbg && pvalbg[0]) || ! (v == &ps->vars[V_VIEW_HDR_COLORS] || v == &ps->vars[V_KW_COLORS])) ret = SAMP1; return(ret); --- 142,149 ---- if((v && v->name && srchstr(v->name, "-foreground-color") && pvalfg && pvalfg[0] && pvalbg && pvalbg[0]) || ! (v == &ps->vars[V_INDEX_TOKEN_COLORS] || ! v == &ps->vars[V_VIEW_HDR_COLORS] || v == &ps->vars[V_KW_COLORS])) ret = SAMP1; return(ret); *************** *** 486,491 **** --- 488,513 ---- } /* + * custom index tokens colors + */ + vtmp = &ps->vars[V_INDEX_TOKEN_COLORS]; + lval = LVAL(vtmp, ew); + + if(lval && lval[0] && lval[0][0]){ + for(i = 0; lval && lval[i]; i++) + add_header_color_line(ps, ctmp, lval[i], i, V_INDEX_TOKEN_COLORS); + } + else{ + new_confline(ctmp); /* Blank line */ + (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE; + new_confline(ctmp); + (*ctmp)->help = NO_HELP; + (*ctmp)->flags |= CF_NOSELECT; + (*ctmp)->value = cpystr(_(ADDINDEXTOKEN_COMMENT)); + (*ctmp)->valoffset = COLOR_INDENT; + } + + /* * custom header colors */ new_confline(ctmp); /* Blank line */ *************** *** 497,503 **** new_confline(ctmp); (*ctmp)->help = NO_HELP; (*ctmp)->flags |= CF_NOSELECT; ! (*ctmp)->value = cpystr(_("HEADER COLORS")); new_confline(ctmp); (*ctmp)->help = NO_HELP; (*ctmp)->flags |= CF_NOSELECT; --- 519,525 ---- new_confline(ctmp); (*ctmp)->help = NO_HELP; (*ctmp)->flags |= CF_NOSELECT; ! (*ctmp)->value = cpystr(_(HDR_COLORS)); new_confline(ctmp); (*ctmp)->help = NO_HELP; (*ctmp)->flags |= CF_NOSELECT; *************** *** 526,532 **** if(lval && lval[0] && lval[0][0]){ for(i = 0; lval && lval[i]; i++) ! add_header_color_line(ps, ctmp, lval[i], i); } else{ new_confline(ctmp); /* Blank line */ --- 548,554 ---- if(lval && lval[0] && lval[0][0]){ for(i = 0; lval && lval[i]; i++) ! add_header_color_line(ps, ctmp, lval[i], i, V_VIEW_HDR_COLORS); } else{ new_confline(ctmp); /* Blank line */ *************** *** 696,701 **** --- 718,724 ---- char **lval, *ret = ""; if(var == &ps_global->vars[V_VIEW_HDR_COLORS] + || var == &ps_global->vars[V_INDEX_TOKEN_COLORS] || var == &ps_global->vars[V_KW_COLORS]){ norm = (LVAL(var, Main) != NULL); exc = (LVAL(var, ps_global->ew_for_except_vars) != NULL); *************** *** 722,736 **** void ! add_header_color_line(struct pine *ps, CONF_S **ctmp, char *val, int which) { struct variable *vtmp; SPEC_COLOR_S *hc; char tmp[100+1]; int l; ! vtmp = &ps->vars[V_VIEW_HDR_COLORS]; ! l = strlen(HEADER_WORD); /* Blank line */ new_confline(ctmp); --- 745,765 ---- void ! add_header_color_line(struct pine *ps, CONF_S **ctmp, char *val, int which, int varnum) { struct variable *vtmp; SPEC_COLOR_S *hc; char tmp[100+1]; int l; ! hc = spec_color_from_var(val, 0); ! if(varnum == V_INDEX_TOKEN_COLORS){ ! if(hc == NULL || hc->spec == NULL ! || itoktype(hc->spec, FOR_INDEX) == NULL) ! return; ! } ! vtmp = &ps->vars[varnum]; ! l = strlen(varnum == V_VIEW_HDR_COLORS ? HEADER_WORD : TOKEN_WORD); /* Blank line */ new_confline(ctmp); *************** *** 748,754 **** /* which is an index into the variable list */ (*ctmp)->varmem = CFC_SET_COLOR(which, 0); - hc = spec_color_from_var(val, 0); if(hc && hc->inherit) (*ctmp)->flags = (CF_NOSELECT | CF_INHERIT); else{ --- 777,782 ---- *************** *** 759,765 **** * with all this. It probably doesn't happen in real life. */ utf8_snprintf(tmp, sizeof(tmp), "%s%c%.*w Color%*w %s%s", ! HEADER_WORD, (hc && hc->spec) ? (islower((unsigned char)hc->spec[0]) ? toupper((unsigned char)hc->spec[0]) : hc->spec[0]) : '?', --- 787,793 ---- * with all this. It probably doesn't happen in real life. */ utf8_snprintf(tmp, sizeof(tmp), "%s%c%.*w Color%*w %s%s", ! (varnum == V_VIEW_HDR_COLORS ? HEADER_WORD : TOKEN_WORD), (hc && hc->spec) ? (islower((unsigned char)hc->spec[0]) ? toupper((unsigned char)hc->spec[0]) : hc->spec[0]) : '?', *************** *** 1139,1144 **** --- 1167,1173 ---- return(var && var->name && (srchstr(var->name, "-foreground-color") || srchstr(var->name, "-background-color") || + var == &ps->vars[V_INDEX_TOKEN_COLORS] || var == &ps->vars[V_VIEW_HDR_COLORS] || var == &ps->vars[V_KW_COLORS])); } *************** *** 1395,1400 **** --- 1424,1430 ---- if(hcolors) free_spec_colors(&hcolors); + fix_side_effects(ps, (*cl)->var, 0); set_current_color_vals(ps); ClearScreen(); rv = ps->mangled_screen = 1; *************** *** 1803,1808 **** --- 1833,1839 ---- /* get rid of all user set colors */ for(v = ps->vars; v->name; v++){ if(!color_holding_var(ps, v) + || v == &ps->vars[V_INDEX_TOKEN_COLORS] || v == &ps->vars[V_VIEW_HDR_COLORS] || v == &ps->vars[V_KW_COLORS]) continue; *************** *** 1856,1861 **** --- 1887,1930 ---- free_spec_colors(&hcolors); } + /* same for index token colors */ + alval = ALVAL(&ps->vars[V_INDEX_TOKEN_COLORS], ew); + if(alval && *alval){ + SPEC_COLOR_S *global_hcolors = NULL, *hcg; + + v = &ps->vars[V_INDEX_TOKEN_COLORS]; + if(v->global_val.l && v->global_val.l[0]) + global_hcolors = spec_colors_from_varlist(v->global_val.l, 0); + + hcolors = spec_colors_from_varlist(*alval, 0); + for(hc = hcolors; hc; hc = hc->next){ + if(hc->fg) + fs_give((void **)&hc->fg); + if(hc->bg) + fs_give((void **)&hc->bg); + + for(hcg = global_hcolors; hcg; hcg = hcg->next){ + if(hc->spec && hcg->spec && !strucmp(hc->spec, hcg->spec)){ + hc->fg = hcg->fg; + hcg->fg = NULL; + hc->bg = hcg->bg; + hcg->bg = NULL; + if(hc->val && !hcg->val) + fs_give((void **) &hc->val); + } + } + + if(global_hcolors) + free_spec_colors(&global_hcolors); + } + + free_list_array(alval); + *alval = varlist_from_spec_colors(hcolors); + + if(hcolors) + free_spec_colors(&hcolors); + } + /* * Same for keyword colors. */ *************** *** 1898,1912 **** rv = 1; break; case MC_ADD : /* add custom header color */ /* get header field name */ help = NO_HELP; while(1){ i = optionally_enter(sval, -FOOTER_ROWS(ps), 0, sizeof(sval), ! _("Enter the name of the header field to be added: "), NULL, help, NULL); ! if(i == 0) break; else if(i == 1){ cmd_cancelled("Add"); cancel = 1; --- 1967,1989 ---- rv = 1; break; + case MC_ADDHEADER: /* add custom index token color */ case MC_ADD : /* add custom header color */ /* get header field name */ help = NO_HELP; while(1){ i = optionally_enter(sval, -FOOTER_ROWS(ps), 0, sizeof(sval), ! (cmd == MC_ADD ? _("Enter the name of the header field to be added: ") ! : _("Enter the name of the index token to be added: ")), NULL, help, NULL); ! if(i == 0){ ! if(cmd == MC_ADDHEADER && itoktype(sval, FOR_INDEX) == NULL){ ! q_status_message1(SM_ORDER, 1, 3, ! _("token \"%s\" not recognized"), sval); ! continue; ! } break; + } else if(i == 1){ cmd_cancelled("Add"); cancel = 1; *************** *** 1937,1943 **** confline = var_from_spec_color(new_hcolor); /* add it to end of list */ ! alval = ALVAL(&ps->vars[V_VIEW_HDR_COLORS], ew); if(alval){ /* get rid of possible empty value first */ if((t = *alval) && t[0] && !t[0][0] && !(t+1)[0]) --- 2014,2020 ---- confline = var_from_spec_color(new_hcolor); /* add it to end of list */ ! alval = ALVAL(&ps->vars[(cmd == MC_ADD ? V_VIEW_HDR_COLORS : V_INDEX_TOKEN_COLORS)], ew); if(alval){ /* get rid of possible empty value first */ if((t = *alval) && t[0] && !t[0][0] && !(t+1)[0]) *************** *** 1962,1968 **** ; /* back up to the KEYWORD COLORS title line */ ! for(; ctmp && (!ctmp->value || strcmp(ctmp->value, KW_COLORS_HDR)) && ctmp->prev; ctmp = prev_confline(ctmp)) ; --- 2039,2045 ---- ; /* back up to the KEYWORD COLORS title line */ ! for(; ctmp && (!ctmp->value || strcmp(ctmp->value, (cmd == MC_ADD ? KW_COLORS_HDR : HDR_COLORS))) && ctmp->prev; ctmp = prev_confline(ctmp)) ; *************** *** 2000,2006 **** free_conflines(&beg); } ! add_header_color_line(ps, cl, confline, i); /* be sure current is on selectable line */ for(; *cl && ((*cl)->flags & CF_NOSELECT); *cl = next_confline(*cl)) --- 2077,2084 ---- free_conflines(&beg); } ! add_header_color_line(ps, cl, confline, i, cmd == MC_ADD ! ? V_VIEW_HDR_COLORS : V_INDEX_TOKEN_COLORS); /* be sure current is on selectable line */ for(; *cl && ((*cl)->flags & CF_NOSELECT); *cl = next_confline(*cl)) *************** *** 2012,2024 **** break; case MC_DELETE : /* delete custom header color */ ! if((*cl)->var != &ps->vars[V_VIEW_HDR_COLORS]){ q_status_message(SM_ORDER, 0, 2, _("Can't delete this color setting")); break; } ! if(want_to(_("Really delete header color from config"), 'y', 'n', NO_HELP, WT_NORM) != 'y'){ cmd_cancelled("Delete"); return(rv); --- 2090,2105 ---- break; case MC_DELETE : /* delete custom header color */ ! if((*cl)->var != &ps->vars[V_VIEW_HDR_COLORS] ! && (*cl)->var != &ps->vars[V_INDEX_TOKEN_COLORS]){ q_status_message(SM_ORDER, 0, 2, _("Can't delete this color setting")); break; } ! if(want_to(((*cl)->var == &ps->vars[V_VIEW_HDR_COLORS] ! ? _("Really delete header color from config") ! : _("Really delete index token color from config")), 'y', 'n', NO_HELP, WT_NORM) != 'y'){ cmd_cancelled("Delete"); return(rv); *************** *** 2054,2072 **** another = 0; /* reset current line */ if(end && end->next && end->next->next && ! end->next->next->var == &ps->vars[V_VIEW_HDR_COLORS]){ *cl = end->next->next; /* next Header Color */ another++; } else if(beg && beg->prev && ! beg->prev->var == &ps->vars[V_VIEW_HDR_COLORS]){ *cl = beg->prev; /* prev Header Color */ another++; } /* adjust SPEC_COLOR_S index (varmem) values */ for(ctmp = end; ctmp; ctmp = next_confline(ctmp)) ! if(ctmp->var == &ps->vars[V_VIEW_HDR_COLORS]) ctmp->varmem = CFC_ICUST_DEC(ctmp); /* --- 2135,2156 ---- another = 0; /* reset current line */ if(end && end->next && end->next->next && ! end->next->next->var == &ps->vars[(*cl)->var == &ps->vars[V_VIEW_HDR_COLORS] ! ? V_VIEW_HDR_COLORS : V_INDEX_TOKEN_COLORS]){ *cl = end->next->next; /* next Header Color */ another++; } else if(beg && beg->prev && ! beg->prev->var == &ps->vars[(*cl)->var == &ps->vars[V_VIEW_HDR_COLORS] ! ? V_VIEW_HDR_COLORS : V_INDEX_TOKEN_COLORS]){ *cl = beg->prev; /* prev Header Color */ another++; } /* adjust SPEC_COLOR_S index (varmem) values */ for(ctmp = end; ctmp; ctmp = next_confline(ctmp)) ! if(ctmp->var == &ps->vars[(*cl)->var == &ps->vars[V_VIEW_HDR_COLORS] ! ? V_VIEW_HDR_COLORS : V_INDEX_TOKEN_COLORS]) ctmp->varmem = CFC_ICUST_DEC(ctmp); /* *************** *** 2098,2104 **** end->flags = CF_NOSELECT; end->help = NO_HELP; ! end->value = cpystr(_(ADDHEADER_COMMENT)); end->valoffset = COLOR_INDENT; end->varnamep = NULL; end->varmem = 0; --- 2182,2189 ---- end->flags = CF_NOSELECT; end->help = NO_HELP; ! end->value = cpystr((*cl)->var == &ps->vars[V_VIEW_HDR_COLORS] ! ? _(ADDHEADER_COMMENT) : _(ADDINDEXTOKEN_COMMENT)); end->valoffset = COLOR_INDENT; end->varnamep = NULL; end->varmem = 0; *************** *** 2556,2562 **** OPT_SCREEN_S screen, *saved_screen; CONF_S *ctmp = NULL, *first_line = NULL, *ctmpb; int rv, is_index = 0, is_hdrcolor = 0, indent = 12; ! int is_general = 0, is_keywordcol = 0; char tmp[1200+1], name[1200], *p; struct variable *vtmp, v; int i, def; --- 2641,2647 ---- OPT_SCREEN_S screen, *saved_screen; CONF_S *ctmp = NULL, *first_line = NULL, *ctmpb; int rv, is_index = 0, is_hdrcolor = 0, indent = 12; ! int is_general = 0, is_keywordcol = 0, is_idxtokcol = 0; char tmp[1200+1], name[1200], *p; struct variable *vtmp, v; int i, def; *************** *** 2567,2572 **** --- 2652,2659 ---- vtmp = (*cl)->var; if(vtmp == &ps->vars[V_VIEW_HDR_COLORS]) is_hdrcolor++; + else if(vtmp == &ps->vars[V_INDEX_TOKEN_COLORS]) + is_idxtokcol++; else if(vtmp == &ps->vars[V_KW_COLORS]) is_keywordcol++; else if(color_holding_var(ps, vtmp)){ *************** *** 2618,2623 **** --- 2705,2728 ---- name[i] = toupper((unsigned char) name[i]); } } + else if(is_idxtokcol){ + char **lval; + + lval = LVAL(vtmp, ew); + hcolors = spec_colors_from_varlist(lval, 0); + + for(hc = hcolors, i = 0; hc; hc = hc->next, i++) + if(CFC_ICUST(*cl) == i) + break; + + if(hc){ + snprintf(name, sizeof(name), "%s%s", TOKEN_WORD, hc->spec); + name[sizeof(name)-1] = '\0'; + i = sizeof(TOKEN_WORD) - 1; + if(islower((unsigned char) name[i])) + name[i] = toupper((unsigned char) name[i]); + } + } else if(is_keywordcol){ char **lval; *************** *** 2663,2669 **** ctmp->flags |= (CF_STARTITEM | CF_NOSELECT); ctmp->keymenu = &color_changing_keymenu; ! if(is_hdrcolor){ char **apval; def = !(hc && hc->fg && hc->fg[0] && hc->bg && hc->bg[0]); --- 2768,2774 ---- ctmp->flags |= (CF_STARTITEM | CF_NOSELECT); ctmp->keymenu = &color_changing_keymenu; ! if(is_hdrcolor || is_idxtokcol){ char **apval; def = !(hc && hc->fg && hc->fg[0] && hc->bg && hc->bg[0]); diff -rc alpine-2.00/alpine/colorconf.h alpine-2.00.I.USE/alpine/colorconf.h *** alpine-2.00/alpine/colorconf.h 2007-08-16 15:25:10.000000000 -0700 --- alpine-2.00.I.USE/alpine/colorconf.h 2011-02-07 20:33:45.000000000 -0800 *************** *** 23,30 **** --- 23,33 ---- #define HEADER_WORD "Header " + #define TOKEN_WORD "Index Token " #define KW_COLORS_HDR "KEYWORD COLORS" + #define HDR_COLORS "HEADER COLORS" #define ADDHEADER_COMMENT N_("[ Use the AddHeader command to add colored headers in MESSAGE TEXT screen ]") + #define ADDINDEXTOKEN_COMMENT N_("[ Use the IndxHdr command to add colored tokens in MESSAGE INDEX screen ]") #define EQ_COL 37 #define SPACE_BETWEEN_DOUBLEVARS 3 diff -rc alpine-2.00/alpine/confscroll.c alpine-2.00.I.USE/alpine/confscroll.c *** alpine-2.00/alpine/confscroll.c 2008-08-21 15:14:45.000000000 -0700 --- alpine-2.00.I.USE/alpine/confscroll.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 50,55 **** --- 50,56 ---- #include "../pith/tempfile.h" #include "../pith/pattern.h" #include "../pith/charconv/utf8.h" + #include "../pith/rules.h" #define CONFIG_SCREEN_HELP_TITLE _("HELP FOR SETUP CONFIGURATION") *************** *** 138,144 **** char *radio_pretty_value(struct pine *, CONF_S *); char *sigfile_pretty_value(struct pine *, CONF_S *); char *color_pretty_value(struct pine *, CONF_S *); ! char *sort_pretty_value(struct pine *, CONF_S *); int longest_feature_name(void); COLOR_PAIR *sample_color(struct pine *, struct variable *); COLOR_PAIR *sampleexc_color(struct pine *, struct variable *); --- 139,145 ---- char *radio_pretty_value(struct pine *, CONF_S *); char *sigfile_pretty_value(struct pine *, CONF_S *); char *color_pretty_value(struct pine *, CONF_S *); ! char *sort_pretty_value(struct pine *, CONF_S *, int); int longest_feature_name(void); COLOR_PAIR *sample_color(struct pine *, struct variable *); COLOR_PAIR *sampleexc_color(struct pine *, struct variable *); *************** *** 286,292 **** CONF_S *ctmp; if(!(cl && *cl && ! ((*cl)->var == &ps->vars[V_SORT_KEY] || standard_radio_var(ps, (*cl)->var) || (*cl)->var == startup_ptr))) return; --- 287,294 ---- CONF_S *ctmp; if(!(cl && *cl && ! (((*cl)->var == &ps->vars[V_SORT_KEY]) || ! ((*cl)->var == &ps->vars[V_THREAD_SORT_KEY]) || standard_radio_var(ps, (*cl)->var) || (*cl)->var == startup_ptr))) return; *************** *** 462,468 **** char tmp[MAXPATH+1]; char *utf8str; UCS ch = 'x'; ! int cmd, i, j, done = 0, changes = 0; int retval = 0; int km_popped = 0, stay_in_col = 0; struct key_menu *km = NULL; --- 464,470 ---- char tmp[MAXPATH+1]; char *utf8str; UCS ch = 'x'; ! int cmd, i, j, k = 1, done = 0, changes = 0; int retval = 0; int km_popped = 0, stay_in_col = 0; struct key_menu *km = NULL; *************** *** 491,499 **** --- 493,504 ---- opt_screen = screen; ps->mangled_screen = 1; + ps->resize_for_pico= 0; ps->redrawer = option_screen_redrawer; while(!done){ + if(ps->resize_for_pico) + ps->mangled_screen = ps->resize_for_pico; ps->user_says_cancel = 0; if(km_popped){ km_popped--; *************** *** 507,516 **** ps->mangled_header = 1; ps->mangled_footer = 1; ps->mangled_body = 1; ! ps->mangled_screen = 0; } /*----------- Check for new mail -----------*/ if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0) ps->mangled_header = 1; --- 512,522 ---- ps->mangled_header = 1; ps->mangled_footer = 1; ps->mangled_body = 1; ! ps->mangled_screen = ps->resize_for_pico = 0; } /*----------- Check for new mail -----------*/ + if (!ps->send_immediately){ if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0) ps->mangled_header = 1; *************** *** 540,545 **** --- 546,552 ---- mark_status_unknown(); } + } /* send_immediately */ if(ps->mangled_footer || km != screen->current->keymenu){ bitmap_t bitmap; *************** *** 611,616 **** --- 618,624 ---- } } + if(!ps_global->send_immediately){ MoveCursor(cursor_pos.row, cursor_pos.col); #ifdef MOUSE mouse_in_content(KEY_MOUSE, -1, -1, 0, 0); /* prime the handler */ *************** *** 629,634 **** --- 637,650 ---- #ifdef _WINDOWS mswin_setscrollcallback(NULL); #endif + } /* send_immediately */ + + if (ps->send_immediately){ + ps_global->dont_use_init_cmds = 0; + process_config_input(&ch); + if (ch == '\030') /* ^X, bye */ + goto end; + } cmd = menu_command(ch, km); *************** *** 1382,1388 **** break; } } ! screen->current = first_confline(screen->current); free_conflines(&screen->current); return(retval); --- 1398,1404 ---- break; } } ! end: screen->current = first_confline(screen->current); free_conflines(&screen->current); return(retval); *************** *** 2426,2431 **** --- 2442,2450 ---- * Now go and set the current_val based on user_val changes * above. Turn off command line settings... */ + set_current_val((*cl)->var, + (strcmp((*cl)->var->name,"key-definition-rules") ? TRUE : FALSE), + FALSE); set_current_val((*cl)->var, TRUE, FALSE); fix_side_effects(ps, (*cl)->var, 0); *************** *** 2922,2928 **** } set_current_val((*cl)->var, TRUE, TRUE); ! if(decode_sort(ps->VAR_SORT_KEY, &def_sort, &def_sort_rev) != -1){ ps->def_sort = def_sort; ps->def_sort_rev = def_sort_rev; } --- 2941,2947 ---- } set_current_val((*cl)->var, TRUE, TRUE); ! if(decode_sort(ps->VAR_SORT_KEY, &def_sort, &def_sort_rev,0) != -1){ ps->def_sort = def_sort; ps->def_sort_rev = def_sort_rev; } *************** *** 2931,2936 **** --- 2950,2986 ---- ps->mangled_body = 1; /* BUG: redraw it all for now? */ rv = 1; } + else if((*cl)->var == &ps->vars[V_THREAD_SORT_KEY]){ + SortOrder thread_def_sort; + int thread_def_sort_rev; + + thread_def_sort_rev = (*cl)->varmem >= (short) EndofList; + thread_def_sort = (SortOrder) ((*cl)->varmem - (thread_def_sort_rev + * EndofList)); + sprintf(tmp_20k_buf, "%s%s", sort_name(thread_def_sort), + (thread_def_sort_rev) ? "/Reverse" : ""); + + if((*cl)->var->cmdline_val.p) + fs_give((void **)&(*cl)->var->cmdline_val.p); + + if(apval){ + if(*apval) + fs_give((void **)apval); + + *apval = cpystr(tmp_20k_buf); + } + + set_current_val((*cl)->var, TRUE, TRUE); + if(decode_sort(ps->VAR_THREAD_SORT_KEY, &thread_def_sort, + &thread_def_sort_rev, 1) != -1){ + ps->thread_def_sort = thread_def_sort; + ps->thread_def_sort_rev = thread_def_sort_rev; + } + + set_radio_pretty_vals(ps, cl); + ps->mangled_body = 1; /* BUG: redraw it all for now? */ + rv = 1; + } else q_status_message(SM_ORDER | SM_DING, 3, 6, "Programmer botch! Unknown radiobutton type."); *************** *** 3369,3375 **** ptr = sample; /* then the color sample */ ! if(ctmp->var == &ps->vars[V_VIEW_HDR_COLORS]){ SPEC_COLOR_S *hc, *hcolors; lastc = newc = NULL; --- 3419,3426 ---- ptr = sample; /* then the color sample */ ! if(ctmp->var == &ps->vars[V_VIEW_HDR_COLORS] ! || ctmp->var == &ps->vars[V_INDEX_TOKEN_COLORS]){ SPEC_COLOR_S *hc, *hcolors; lastc = newc = NULL; *************** *** 3787,3793 **** else if(standard_radio_var(ps, v) || v == startup_ptr) return(radio_pretty_value(ps, cl)); else if(v == &ps->vars[V_SORT_KEY]) ! return(sort_pretty_value(ps, cl)); else if(v == &ps->vars[V_SIGNATURE_FILE]) return(sigfile_pretty_value(ps, cl)); else if(v == &ps->vars[V_USE_ONLY_DOMAIN_NAME]) --- 3838,3846 ---- else if(standard_radio_var(ps, v) || v == startup_ptr) return(radio_pretty_value(ps, cl)); else if(v == &ps->vars[V_SORT_KEY]) ! return(sort_pretty_value(ps, cl, 0)); ! else if(v == &ps->vars[V_THREAD_SORT_KEY]) ! return(sort_pretty_value(ps, cl, 1)); else if(v == &ps->vars[V_SIGNATURE_FILE]) return(sigfile_pretty_value(ps, cl)); else if(v == &ps->vars[V_USE_ONLY_DOMAIN_NAME]) *************** *** 4318,4331 **** char * ! sort_pretty_value(struct pine *ps, CONF_S *cl) { ! return(generalized_sort_pretty_value(ps, cl, 1)); } char * ! generalized_sort_pretty_value(struct pine *ps, CONF_S *cl, int default_ok) { char tmp[6*MAXPATH]; char *pvalnorm, *pvalexc, *pval; --- 4371,4384 ---- char * ! sort_pretty_value(struct pine *ps, CONF_S *cl, int thread) { ! return(generalized_sort_pretty_value(ps, cl, 1, thread)); } char * ! generalized_sort_pretty_value(struct pine *ps, CONF_S *cl, int default_ok, int thread) { char tmp[6*MAXPATH]; char *pvalnorm, *pvalexc, *pval; *************** *** 4375,4381 **** } else if(fixed){ pval = v->fixed_val.p; ! decode_sort(pval, &var_sort, &var_sort_rev); is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort); utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s", --- 4428,4434 ---- } else if(fixed){ pval = v->fixed_val.p; ! decode_sort(pval, &var_sort, &var_sort_rev, thread); is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort); utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s", *************** *** 4386,4394 **** is_the_one ? " (value is fixed)" : ""); } else if(is_set_for_this_level){ ! decode_sort(pval, &var_sort, &var_sort_rev); is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort); ! decode_sort(pvalexc, &exc_sort, &exc_sort_rev); the_exc_one = (editing_normal_which_isnt_except && pvalexc && exc_sort_rev == line_sort_rev && exc_sort == line_sort); utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s", --- 4439,4447 ---- is_the_one ? " (value is fixed)" : ""); } else if(is_set_for_this_level){ ! decode_sort(pval, &var_sort, &var_sort_rev, thread); is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort); ! decode_sort(pvalexc, &exc_sort, &exc_sort_rev, thread); the_exc_one = (editing_normal_which_isnt_except && pvalexc && exc_sort_rev == line_sort_rev && exc_sort == line_sort); utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s", *************** *** 4406,4412 **** } else{ if(pvalexc){ ! decode_sort(pvalexc, &exc_sort, &exc_sort_rev); is_the_one = (exc_sort_rev == line_sort_rev && exc_sort == line_sort); utf8_snprintf(tmp, sizeof(tmp), "( ) %s%-*w%*s%s", --- 4459,4465 ---- } else{ if(pvalexc){ ! decode_sort(pvalexc, &exc_sort, &exc_sort_rev, thread); is_the_one = (exc_sort_rev == line_sort_rev && exc_sort == line_sort); utf8_snprintf(tmp, sizeof(tmp), "( ) %s%-*w%*s%s", *************** *** 4417,4423 **** } else{ pval = v->current_val.p; ! decode_sort(pval, &var_sort, &var_sort_rev); is_the_one = ((pval || default_ok) && var_sort_rev == line_sort_rev && var_sort == line_sort); --- 4470,4476 ---- } else{ pval = v->current_val.p; ! decode_sort(pval, &var_sort, &var_sort_rev, thread); is_the_one = ((pval || default_ok) && var_sort_rev == line_sort_rev && var_sort == line_sort); *************** *** 5154,5159 **** --- 5207,5236 ---- var == &ps->vars[V_ABOOK_FORMATS]){ addrbook_reset(); } + else if(var == &ps->vars[V_INDEX_RULES]){ + if(ps_global->rule_list) + free_parsed_rule_list(&ps_global->rule_list); + create_rule_list(ps->vars); + reset_index_format(); + clear_index_cache(ps->mail_stream, 0); + } + else if(var == &ps->vars[V_COMPOSE_RULES] || + var == &ps->vars[V_FORWARD_RULES] || + var == &ps->vars[V_KEY_RULES] || + var == &ps->vars[V_REPLACE_RULES] || + var == &ps->vars[V_REPLY_INDENT_RULES] || + var == &ps->vars[V_REPLY_LEADIN_RULES] || + var == &ps->vars[V_RESUB_RULES] || + var == &ps->vars[V_SAVE_RULES] || + var == &ps->vars[V_SMTP_RULES] || + var == &ps->vars[V_SORT_RULES] || + var == &ps->vars[V_STARTUP_RULES] || + var == &ps->vars[V_THREAD_DISP_STYLE_RULES] || + var == &ps->vars[V_THREAD_INDEX_STYLE_RULES]){ + if(ps_global->rule_list) + free_parsed_rule_list(&ps_global->rule_list); + create_rule_list(ps->vars); + } else if(var == &ps->vars[V_INDEX_FORMAT]){ reset_index_format(); clear_index_cache(ps->mail_stream, 0); *************** *** 5176,5181 **** --- 5253,5261 ---- clear_index_cache(ps->mail_stream, 0); } + else if(var == &ps->vars[V_SPECIAL_TEXT]){ + regex_pattern(ps->VAR_SPECIAL_TEXT); + } else if(var == &ps->vars[V_INIT_CMD_LIST]){ if(!revert) q_status_message(SM_ASYNC, 0, 3, *************** *** 5489,5494 **** --- 5569,5580 ---- (void *)var->current_val.p); } #endif + #ifndef _WINDOWS + else if(var == &ps->vars[V_MAILDIR_LOCATION]){ + if(var->current_val.p && var->current_val.p[0]) + mail_parameters(NULL, SET_MDINBOXPATH, (void *)var->current_val.p); + } + #endif else if(revert && standard_radio_var(ps, var)){ cur_rule_value(var, TRUE, FALSE); *************** *** 5538,5546 **** else if(revert && var == &ps->vars[V_SORT_KEY]){ int def_sort_rev; ! decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev); ps->def_sort_rev = def_sort_rev; } else if(var == &ps->vars[V_THREAD_MORE_CHAR] || var == &ps->vars[V_THREAD_EXP_CHAR] || var == &ps->vars[V_THREAD_LASTREPLY_CHAR]){ --- 5624,5638 ---- else if(revert && var == &ps->vars[V_SORT_KEY]){ int def_sort_rev; ! decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev, 0); ps->def_sort_rev = def_sort_rev; } + else if(revert && var == &ps->vars[V_THREAD_SORT_KEY]){ + int thread_def_sort_rev; + + decode_sort(VAR_THREAD_SORT_KEY, &ps->thread_def_sort, &thread_def_sort_rev, 1); + ps->thread_def_sort_rev = thread_def_sort_rev; + } else if(var == &ps->vars[V_THREAD_MORE_CHAR] || var == &ps->vars[V_THREAD_EXP_CHAR] || var == &ps->vars[V_THREAD_LASTREPLY_CHAR]){ *************** *** 5607,5612 **** --- 5699,5705 ---- } } else if(var == &ps->vars[V_KW_COLORS] || + var == &ps->vars[V_INDEX_TOKEN_COLORS] || var == &ps->vars[V_IND_PLUS_FORE_COLOR] || var == &ps->vars[V_IND_IMP_FORE_COLOR] || var == &ps->vars[V_IND_DEL_FORE_COLOR] || diff -rc alpine-2.00/alpine/confscroll.h alpine-2.00.I.USE/alpine/confscroll.h *** alpine-2.00/alpine/confscroll.h 2007-11-09 13:13:47.000000000 -0800 --- alpine-2.00.I.USE/alpine/confscroll.h 2011-02-07 20:33:41.000000000 -0800 *************** *** 94,100 **** int radiobutton_tool(struct pine *, int, CONF_S **, unsigned); int yesno_tool(struct pine *, int, CONF_S **, unsigned); int text_toolit(struct pine *, int, CONF_S **, unsigned, int); ! char *generalized_sort_pretty_value(struct pine *, CONF_S *, int); int exclude_config_var(struct pine *, struct variable *, int); int config_exit_cmd(unsigned); int simple_exit_cmd(unsigned); --- 94,100 ---- int radiobutton_tool(struct pine *, int, CONF_S **, unsigned); int yesno_tool(struct pine *, int, CONF_S **, unsigned); int text_toolit(struct pine *, int, CONF_S **, unsigned, int); ! char *generalized_sort_pretty_value(struct pine *, CONF_S *, int, int); int exclude_config_var(struct pine *, struct variable *, int); int config_exit_cmd(unsigned); int simple_exit_cmd(unsigned); diff -rc alpine-2.00/alpine/dispfilt.c alpine-2.00.I.USE/alpine/dispfilt.c *** alpine-2.00/alpine/dispfilt.c 2008-03-18 10:24:31.000000000 -0700 --- alpine-2.00.I.USE/alpine/dispfilt.c 2011-02-07 20:33:47.000000000 -0800 *************** *** 56,68 **** dfilter(char *rawcmd, STORE_S *input_so, gf_io_t output_pc, FILTLIST_S *aux_filters) { char *status = NULL, *cmd, *resultf = NULL, *tmpfile = NULL; ! int key = 0; ! if((cmd = expand_filter_tokens(rawcmd,NULL,&tmpfile,&resultf,NULL,&key,NULL)) != NULL){ suspend_busy_cue(); #ifndef _WINDOWS ! ps_global->mangled_screen = 1; ! ClearScreen(); fflush(stdout); #endif --- 56,70 ---- dfilter(char *rawcmd, STORE_S *input_so, gf_io_t output_pc, FILTLIST_S *aux_filters) { char *status = NULL, *cmd, *resultf = NULL, *tmpfile = NULL; ! int key = 0, silent = 0; ! if((cmd = expand_filter_tokens(rawcmd,NULL,&tmpfile,&resultf,NULL,&key,NULL, &silent)) != NULL){ suspend_busy_cue(); #ifndef _WINDOWS ! if(!silent){ ! ps_global->mangled_screen = 1; ! ClearScreen(); ! } fflush(stdout); #endif *************** *** 99,105 **** /* prepare the terminal in case the filter uses it */ if(status == NULL){ if((filter_pipe = open_system_pipe(cmd, NULL, NULL, ! PIPE_USER | PIPE_RESET, 0, pipe_callback, NULL)) != NULL){ if(close_system_pipe(&filter_pipe, NULL, pipe_callback) == 0){ /* pull result out of tmp file */ --- 101,108 ---- /* prepare the terminal in case the filter uses it */ if(status == NULL){ if((filter_pipe = open_system_pipe(cmd, NULL, NULL, ! PIPE_USER | (silent ? PIPE_SILENT : ! (F_ON(F_DISABLE_TERM_RESET_DISP, ps_global) ? 0 : PIPE_RESET)), 0, pipe_callback, NULL)) != NULL){ if(close_system_pipe(&filter_pipe, NULL, pipe_callback) == 0){ /* pull result out of tmp file */ *************** *** 130,136 **** status = "Can't open display filter tmp file"; } else if((status = gf_filter(cmd, key ? filter_session_key() : NULL, ! input_so, output_pc, aux_filters, F_ON(F_DISABLE_TERM_RESET_DISP, ps_global), pipe_callback)) != NULL){ unsigned long ch; --- 133,139 ---- status = "Can't open display filter tmp file"; } else if((status = gf_filter(cmd, key ? filter_session_key() : NULL, ! input_so, output_pc, aux_filters, silent, F_ON(F_DISABLE_TERM_RESET_DISP, ps_global), pipe_callback)) != NULL){ unsigned long ch; *************** *** 150,156 **** resume_busy_cue(0); #ifndef _WINDOWS ! ClearScreen(); #endif fs_give((void **)&cmd); } --- 153,160 ---- resume_busy_cue(0); #ifndef _WINDOWS ! if(!silent) ! ClearScreen(); #endif fs_give((void **)&cmd); } *************** *** 165,171 **** */ char * expand_filter_tokens(char *filter, ENVELOPE *env, char **tmpf, char **resultf, ! char **mtypef, int *key, int *hdrs) { char **array, **q; char *bp, *cmd = NULL, *p = NULL, --- 169,175 ---- */ char * expand_filter_tokens(char *filter, ENVELOPE *env, char **tmpf, char **resultf, ! char **mtypef, int *key, int *hdrs, int *silent) { char **array, **q; char *bp, *cmd = NULL, *p = NULL, *************** *** 238,243 **** --- 242,260 ---- fs_give((void **)q); *q = rl ? rl : cpystr(""); } + else if(!strcmp(*q, "_ADDRESS_")){ + char *r = NULL; + + if(env && env->from && env->from->mailbox && env->from->host){ + size_t l; + l = strlen(env->from->mailbox) + strlen(env->from->host) + 1; + r = (char *) fs_get((l+1) * sizeof(char)); + snprintf(r, l+1, "%s@%s", env->from->mailbox, env->from->host); + } + + fs_give((void **)q); + *q = r ? r : cpystr(""); + } else if(!strcmp(*q, "_TMPFILE_")){ if(!tfn){ tfn = temp_nam(NULL, "sf"); /* send filter file */ *************** *** 312,317 **** --- 329,339 ---- if(hdrs) *hdrs = 1; } + else if(!strcmp(*q, "_SILENT_")){ + (*q)[0] = '\0'; + if(silent) + *silent = 1; + } } /* count up required length */ *************** *** 451,453 **** --- 473,535 ---- return(passed); } + + char * + exec_function_rule(char *rawcmd, gf_io_t input_gc, gf_io_t output_pc) + { + char *status = NULL, *cmd, *tmpfile = NULL; + + if((cmd = expand_filter_tokens(rawcmd,NULL,&tmpfile,NULL,NULL,NULL,NULL, NULL)) != NULL){ + suspend_busy_cue(); + ps_global->mangled_screen = 1; + if(tmpfile){ + PIPE_S *filter_pipe; + FILE *fp; + gf_io_t gc, pc; + STORE_S *tmpf_so; + + /* write the tmp file */ + if((tmpf_so = so_get(FileStar, tmpfile, WRITE_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){ + /* copy input to tmp file */ + gf_set_so_writec(&pc, tmpf_so); + gf_filter_init(); + status = gf_pipe(input_gc, pc); + gf_clear_so_writec(tmpf_so); + if(so_give(&tmpf_so) != 0 && status == NULL) + status = error_description(errno); + + /* prepare the terminal in case the filter uses it */ + if(status == NULL){ + if((filter_pipe = open_system_pipe(cmd, NULL, NULL, + PIPE_USER|PIPE_PROT|PIPE_NOSHELL|PIPE_SILENT, + 0, pipe_callback, NULL)) != NULL){ + if(close_system_pipe(&filter_pipe, NULL, pipe_callback) == 0){ + /* pull result out of tmp file */ + if((fp = our_fopen(tmpfile, "rb")) != NULL){ + gf_set_readc(&gc, fp, 0L, FileStar, READ_FROM_LOCALE); + gf_filter_init(); + status = gf_pipe(gc, output_pc); + fclose(fp); + } + else + status = "Can't read result of EXEC command"; + } + else + status = "EXEC command command returned error."; + } + else + status = "Can't open pipe for EXEC command"; + } + + our_unlink(tmpfile); + } + else + status = "Can't open EXEC command tmp file"; + } + + resume_busy_cue(0); + fs_give((void **)&cmd); + } + + return(status); + } diff -rc alpine-2.00/alpine/dispfilt.h alpine-2.00.I.USE/alpine/dispfilt.h *** alpine-2.00/alpine/dispfilt.h 2006-09-26 12:30:49.000000000 -0700 --- alpine-2.00.I.USE/alpine/dispfilt.h 2011-02-07 20:33:45.000000000 -0800 *************** *** 21,30 **** /* exported protoypes */ char *dfilter(char *, STORE_S *, gf_io_t, FILTLIST_S *); char *dfilter_trigger(BODY *, char *, size_t); ! char *expand_filter_tokens(char *, ENVELOPE *, char **, char **, char **, int *, int *); char *filter_session_key(void); char *filter_data_file(int); ! #endif /* PINE_DISPFILT_INCLUDED */ --- 21,30 ---- /* exported protoypes */ char *dfilter(char *, STORE_S *, gf_io_t, FILTLIST_S *); char *dfilter_trigger(BODY *, char *, size_t); ! char *expand_filter_tokens(char *, ENVELOPE *, char **, char **, char **, int *, int *, int *); char *filter_session_key(void); char *filter_data_file(int); ! char *exec_function_rule(char *, gf_io_t, gf_io_t); #endif /* PINE_DISPFILT_INCLUDED */ diff -rc alpine-2.00/alpine/folder.c alpine-2.00.I.USE/alpine/folder.c *** alpine-2.00/alpine/folder.c 2008-08-13 17:51:47.000000000 -0700 --- alpine-2.00.I.USE/alpine/folder.c 2011-02-07 20:33:45.000000000 -0800 *************** *** 77,83 **** #define FLW_LIST 0x04 /* Allow for ListMode for subscribing */ #define FLW_UNSEEN 0x08 /* Add (unseen) */ ! /*---------------------------------------------------------------------- The data needed to redraw the folders screen, including the case where the --- 77,88 ---- #define FLW_LIST 0x04 /* Allow for ListMode for subscribing */ #define FLW_UNSEEN 0x08 /* Add (unseen) */ ! /* folder colors */ ! #define CLR_UNSEEN 0x01 /* color folder with unseen/new messages */ ! #define CLR_FOLDER 0x02 /* color a name of folder or directory */ ! #define CLR_DIRECT 0x04 /* color a separator of a directory */ ! #define CLR_FLDRLT 0x08 /* color of explanatory text in list scrn*/ ! #define CLR_NORMAL 0x10 /* use normal color */ /*---------------------------------------------------------------------- The data needed to redraw the folders screen, including the case where the *************** *** 173,179 **** int folder_list_write_prefix(FOLDER_S *, int, gf_io_t); int folder_list_write_middle(FOLDER_S *fp, CONTEXT_S *ctxt, gf_io_t pc, HANDLE_S *); int folder_list_write_suffix(FOLDER_S *, int, gf_io_t); ! int color_monitored_unseen(FOLDER_S *, int); int folder_list_ith(int, CONTEXT_S *); char *folder_list_center_space(char *, int); HANDLE_S *folder_list_handle(FSTATE_S *, HANDLE_S *); --- 178,187 ---- int folder_list_write_prefix(FOLDER_S *, int, gf_io_t); int folder_list_write_middle(FOLDER_S *fp, CONTEXT_S *ctxt, gf_io_t pc, HANDLE_S *); int folder_list_write_suffix(FOLDER_S *, int, gf_io_t); ! int color_monitored(FOLDER_S *, int, int); ! int color_test_for_folder(char *, char *); ! int color_write_for_folder(gf_io_t pc, int testtype); ! int use_color_for_folder(FOLDER_S *fp); int folder_list_ith(int, CONTEXT_S *); char *folder_list_center_space(char *, int); HANDLE_S *folder_list_handle(FSTATE_S *, HANDLE_S *); *************** *** 238,244 **** dprint((1, "=== folder_screen called ====\n")); mailcap_free(); /* free resources we won't be using for a while */ ps->next_screen = SCREEN_FUN_NULL; ! /* Initialize folder state and dispatches */ memset(&fs, 0, sizeof(FSTATE_S)); fs.context = cntxt; --- 246,252 ---- dprint((1, "=== folder_screen called ====\n")); mailcap_free(); /* free resources we won't be using for a while */ ps->next_screen = SCREEN_FUN_NULL; ! strcpy(ps->screen_name, "folder"); /* Initialize folder state and dispatches */ memset(&fs, 0, sizeof(FSTATE_S)); fs.context = cntxt; *************** *** 335,340 **** --- 343,349 ---- pine_mail_close(*fs.cache_streamp); ps->prev_screen = folder_screen; + strcpy(ps->screen_name, "unknown"); } *************** *** 1615,1622 **** --- 1624,1633 ---- if(c_list->prev) gf_puts("\n", pc); /* blank line */ + color_write_for_folder(pc, CLR_FLDRLT); gf_puts(repeat_char(cols, '-'), pc); gf_puts("\n", pc); + color_write_for_folder(pc, CLR_NORMAL); } /* nickname or description */ *************** *** 1625,1630 **** --- 1636,1642 ---- || F_ON(F_CMBND_SUBDIR_DISP, ps_global))){ char buf[6*MAX_SCREEN_COLS + 1]; + color_write_for_folder(pc, CLR_FLDRLT); if(cols < 40){ snprintf(buf, sizeof(buf), "%.*s", cols, strsquish(tmp_20k_buf, SIZEOF_20KBUF, *************** *** 1651,1656 **** --- 1663,1669 ---- gf_puts(buf, pc); gf_puts("\n", pc); + color_write_for_folder(pc, CLR_NORMAL); } else if(c_list->label){ if(utf8_width(c_list->label) > ps_global->ttyo->screen_cols) *************** *** 1660,1668 **** --- 1673,1683 ---- lbuf[sizeof(lbuf)-1] = '\0'; + color_write_for_folder(pc, CLR_FLDRLT); gf_puts(folder_list_center_space(lbuf, cols), pc); gf_puts(lbuf, pc); gf_puts("\n", pc); + color_write_for_folder(pc, CLR_NORMAL); } if(c_list->comment){ *************** *** 1673,1681 **** --- 1688,1698 ---- lbuf[sizeof(lbuf)-1] = '\0'; + color_write_for_folder(pc, CLR_FLDRLT); gf_puts(folder_list_center_space(lbuf, cols), pc); gf_puts(lbuf, pc); gf_puts("\n", pc); + color_write_for_folder(pc, CLR_NORMAL); } if(c_list->dir->desc){ *************** *** 1684,1692 **** --- 1701,1711 ---- strncpy(buf, strsquish(tmp_20k_buf,SIZEOF_20KBUF,c_list->dir->desc,cols), sizeof(buf)-1); buf[sizeof(buf)-1] = '\0'; + color_write_for_folder(pc, CLR_FLDRLT); gf_puts(folder_list_center_space(buf, cols), pc); gf_puts(buf, pc); gf_puts("\n", pc); + color_write_for_folder(pc, CLR_NORMAL); } if(c_list->use & CNTXT_ZOOM){ *************** *** 1703,1720 **** --- 1722,1744 ---- lbuf[sizeof(lbuf)-1] = '\0'; + color_write_for_folder(pc, CLR_FLDRLT); gf_puts(folder_list_center_space(lbuf, cols), pc); gf_puts(lbuf, pc); gf_puts("\n", pc); + color_write_for_folder(pc, CLR_NORMAL); } + color_write_for_folder(pc, CLR_FLDRLT); gf_puts(repeat_char(cols, '-'), pc); gf_puts("\n\n", pc); + color_write_for_folder(pc, CLR_NORMAL); } if(shown){ /* Run thru list formatting as necessary */ if((ftotal = folder_total(FOLDERS(c_list))) != 0){ + int use_color; /* If previously selected, mark members of new list */ selected = selected_folders(c_list); *************** *** 1729,1738 **** continue; fcount++; ! width = utf8_width(FLDR_NAME(f)); if(f->isdir) ! width += (f->isfolder) ? 3 : 1; if(NEWS_TEST(c_list) && (c_list->use & CNTXT_FINDALL)) /* assume listmode so we don't have to reformat */ --- 1753,1762 ---- continue; fcount++; ! use_color = use_color_for_folder(f); width = utf8_width(FLDR_NAME(f)); if(f->isdir) ! width += (f->isfolder) ? (use_color ? 1 : 3) : (use_color ? 0 : 1); if(NEWS_TEST(c_list) && (c_list->use & CNTXT_FINDALL)) /* assume listmode so we don't have to reformat */ *************** *** 1866,1874 **** --- 1890,1900 ---- lbuf[sizeof(lbuf)-1] = '\0'; + color_write_for_folder(pc, CLR_FLDRLT); gf_puts(folder_list_center_space(lbuf, cols), pc); (void) folder_list_write(pc, handlesp, c_list, -1, lbuf, (handlesp) ? FLW_LUNK : FLW_NONE); + color_write_for_folder(pc, CLR_NORMAL); } } else if(fp->fs->combined_view *************** *** 1883,1891 **** --- 1909,1919 ---- lbuf[sizeof(lbuf)-1] = '\0'; + color_write_for_folder(pc, CLR_FLDRLT); gf_puts(folder_list_center_space(lbuf, cols), pc); (void) folder_list_write(pc, handlesp, c_list, -1, lbuf, (handlesp) ? FLW_LUNK : FLW_NONE); + color_write_for_folder(pc, CLR_NORMAL); } gf_puts("\n", pc); /* blank line */ *************** *** 1932,1948 **** if(h1){ /* color unseen? */ ! if(color_monitored_unseen(fp, flags)){ h1->color_unseen = 1; if(h2) h2->color_unseen = 1; } } /* embed handle pointer */ ! if(((h1 && h1->color_unseen) ? ! gf_puts(color_embed(ps_global->VAR_INCUNSEEN_FORE_COLOR, ! ps_global->VAR_INCUNSEEN_BACK_COLOR), pc) : 1) && (h1 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE) && (*pc)(strlen(buf)) && gf_puts(buf, pc)) : 1) && (fp ? ((lprefix = folder_list_write_prefix(fp, flags, pc)) >= 0 --- 1960,1983 ---- if(h1){ /* color unseen? */ ! if(color_monitored(fp, flags, CLR_UNSEEN)){ h1->color_unseen = 1; if(h2) h2->color_unseen = 1; } + /* color folder? */ + if(color_monitored(fp, flags, CLR_FOLDER)){ + h1->color_folder = 1; + if(h2) + h2->color_folder = 1; + } } /* embed handle pointer */ ! if(((h1 && h1->color_unseen) ? color_write_for_folder(pc, CLR_UNSEEN) ! : ((h1 && h1->color_folder) ! ? color_write_for_folder(pc, fp->isfolder ? CLR_FOLDER : CLR_DIRECT) ! : 1)) && (h1 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE) && (*pc)(strlen(buf)) && gf_puts(buf, pc)) : 1) && (fp ? ((lprefix = folder_list_write_prefix(fp, flags, pc)) >= 0 *************** *** 1951,1959 **** : (alt_name ? gf_puts(alt_name, pc) : 0)) && (h1 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF) && (*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)) : 1) ! && ((h1 && h1->color_unseen) ? ! gf_puts(color_embed(ps_global->VAR_NORM_FORE_COLOR, ! ps_global->VAR_NORM_BACK_COLOR), pc) : 1)){ if(fp) width = lprefix + lmiddle + lsuffix; else if(alt_name) --- 1986,1993 ---- : (alt_name ? gf_puts(alt_name, pc) : 0)) && (h1 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF) && (*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)) : 1) ! && ((h1 && (h1->color_unseen || h1->color_folder)) ? ! color_write_for_folder(pc, CLR_NORMAL): 1)){ if(fp) width = lprefix + lmiddle + lsuffix; else if(alt_name) *************** *** 1992,2002 **** return(rv); } - int folder_list_write_middle(FOLDER_S *fp, CONTEXT_S *ctxt, gf_io_t pc, HANDLE_S *h2) { ! int rv = -1; char buf[256]; if(h2){ --- 2026,2035 ---- return(rv); } int folder_list_write_middle(FOLDER_S *fp, CONTEXT_S *ctxt, gf_io_t pc, HANDLE_S *h2) { ! int rv = -1, use_color; char buf[256]; if(h2){ *************** *** 2007,2023 **** if(!fp) return(rv); if(gf_puts(FLDR_NAME(fp), pc) && (h2 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF) /* tie off handle 1 */ && (*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)) : 1) && (h2 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE) /* start handle 2 */ && (*pc)(strlen(buf)) && gf_puts(buf, pc)) : 1) ! && ((fp->isdir && fp->isfolder) ? (*pc)('[') : 1) ! && ((fp->isdir) ? (*pc)(ctxt->dir->delim) : 1) ! && ((fp->isdir && fp->isfolder) ? (*pc)(']') : 1)){ rv = utf8_width(FLDR_NAME(fp)); if(fp->isdir) ! rv += (fp->isfolder) ? 3 : 1; } return(rv); --- 2040,2062 ---- if(!fp) return(rv); + use_color = use_color_for_folder(fp); + if(gf_puts(FLDR_NAME(fp), pc) && (h2 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF) /* tie off handle 1 */ && (*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)) : 1) + && ((use_color && fp->isdir && fp->isfolder) + ? color_write_for_folder(pc, CLR_DIRECT) : 1) && (h2 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE) /* start handle 2 */ && (*pc)(strlen(buf)) && gf_puts(buf, pc)) : 1) ! && use_color ! ? (fp->isdir && fp->isfolder ? (*pc)(ctxt->dir->delim) : 1) ! : (((fp->isdir && fp->isfolder) ? (*pc)('[') : 1) ! && ((fp->isdir) ? (*pc)(ctxt->dir->delim) : 1) ! && ((fp->isdir && fp->isfolder) ? (*pc)(']') : 1))){ rv = utf8_width(FLDR_NAME(fp)); if(fp->isdir) ! rv += (fp->isfolder) ? (use_color ? 1 : 3) : (use_color ? 0 : 1); } return(rv); *************** *** 2065,2084 **** return(rv); } int ! color_monitored_unseen(FOLDER_S *f, int flags) { ! return((flags & FLW_UNSEEN) && f && f->unseen_valid && ((F_ON(F_INCOMING_CHECKING_RECENT, ps_global) && f->new > 0L) || (F_OFF(F_INCOMING_CHECKING_RECENT, ps_global) && f->unseen > 0L)) ! && pico_usingcolor() ! && pico_is_good_color(ps_global->VAR_INCUNSEEN_FORE_COLOR) ! && pico_is_good_color(ps_global->VAR_INCUNSEEN_BACK_COLOR) ! && (colorcmp(ps_global->VAR_INCUNSEEN_FORE_COLOR, ! ps_global->VAR_NORM_FORE_COLOR) ! || colorcmp(ps_global->VAR_INCUNSEEN_BACK_COLOR, ! ps_global->VAR_NORM_BACK_COLOR))); } --- 2104,2188 ---- return(rv); } + int + color_write_for_folder(gf_io_t pc, int testtype) + { + int rv; + if(!pico_usingcolor()) + return 1; + switch (testtype){ + case CLR_UNSEEN: + rv = gf_puts(color_embed(ps_global->VAR_INCUNSEEN_FORE_COLOR, + ps_global->VAR_INCUNSEEN_BACK_COLOR), pc); + break; + case CLR_FOLDER: + rv = gf_puts(color_embed(ps_global->VAR_FOLDER_FORE_COLOR, + ps_global->VAR_FOLDER_BACK_COLOR ), pc); + break; + case CLR_FLDRLT: + rv = gf_puts(color_embed(ps_global->VAR_FOLDER_LIST_FORE_COLOR, + ps_global->VAR_FOLDER_LIST_BACK_COLOR ), pc); + break; + case CLR_DIRECT: + rv = gf_puts(color_embed(ps_global->VAR_DIRECTORY_FORE_COLOR, + ps_global->VAR_DIRECTORY_BACK_COLOR), pc); + break; + case CLR_NORMAL: + rv = gf_puts(color_embed(ps_global->VAR_NORM_FORE_COLOR, + ps_global->VAR_NORM_BACK_COLOR), pc); + break; + default: + rv = 0; /* fail */ + break; + } + return rv; + } + int ! color_test_for_folder(char *color_fore, char *color_back) { ! return pico_usingcolor() ! && pico_is_good_color(color_fore) ! && pico_is_good_color(color_back) ! && (colorcmp(color_fore, ps_global->VAR_NORM_FORE_COLOR) ! || colorcmp(color_back, ! ps_global->VAR_NORM_BACK_COLOR)) ? 1 : 0; ! ! } ! ! ! int ! use_color_for_folder(FOLDER_S *fp) ! { ! int test1, test2; ! if(fp->isdir) ! test1 = color_test_for_folder(ps_global->VAR_DIRECTORY_FORE_COLOR, ! ps_global->VAR_DIRECTORY_BACK_COLOR); ! if(fp->isfolder) ! test2 = color_test_for_folder(ps_global->VAR_FOLDER_FORE_COLOR, ! ps_global->VAR_FOLDER_BACK_COLOR); ! return (fp->isdir && fp->isfolder) ? test1 + test2 ! : (fp->isdir ? test1 : (fp->isfolder? test2 : 0)); ! } ! ! ! int ! color_monitored(FOLDER_S *f, int flags, int testtype) ! { ! int rv; ! switch(testtype){ ! case CLR_UNSEEN: rv = (flags & FLW_UNSEEN) && f && f->unseen_valid && ((F_ON(F_INCOMING_CHECKING_RECENT, ps_global) && f->new > 0L) || (F_OFF(F_INCOMING_CHECKING_RECENT, ps_global) && f->unseen > 0L)) ! && color_test_for_folder(ps_global->VAR_INCUNSEEN_FORE_COLOR, ! ps_global->VAR_INCUNSEEN_BACK_COLOR) ? 1 : 0; ! break; ! case CLR_FOLDER: rv = f ? use_color_for_folder(f) : 0; ! break; ! default: rv = 0; ! } ! return rv; } *************** *** 6223,6233 **** char * next_folder(MAILSTREAM **streamp, char *next, size_t nextlen, char *current, CONTEXT_S *cntxt, long int *find_recent, int *did_cancel) { ! int index, recent = 0, failed_status = 0, try_fast; char prompt[128]; FOLDER_S *f = NULL; char tmp[MAILTMPLEN]; ! /* note: find_folders may assign "stream" */ build_folder_list(streamp, cntxt, NULL, NULL, --- 6327,6343 ---- char * next_folder(MAILSTREAM **streamp, char *next, size_t nextlen, char *current, CONTEXT_S *cntxt, long int *find_recent, int *did_cancel) { ! int index, recent = 0, failed_status = 0, try_fast, done = 0; char prompt[128]; FOLDER_S *f = NULL; char tmp[MAILTMPLEN]; ! char *test_current = cpystr(current); ! int cur_indx = folder_index(ps_global->cur_folder, cntxt, FI_FOLDER); ! int loop = !strcmp(next, ps_global->cur_folder) ? 0 : ! (folder_index(test_current, cntxt, FI_FOLDER) <= cur_indx ! ? 1 : 0); ! int last = !strcmp(ps_global->cur_folder, ps_global->inbox_name) ! ? 1 : cur_indx; /* note: find_folders may assign "stream" */ build_folder_list(streamp, cntxt, NULL, NULL, *************** *** 6238,6244 **** if(find_recent) *find_recent = 0L; ! for(index = folder_index(current, cntxt, FI_FOLDER) + 1; index > 0 && index < folder_total(FOLDERS(cntxt)) && (f = folder_entry(index, FOLDERS(cntxt))) --- 6348,6356 ---- if(find_recent) *find_recent = 0L; ! ! find_new_message: ! for(index = folder_index(test_current, cntxt, FI_FOLDER) + 1; index > 0 && index < folder_total(FOLDERS(cntxt)) && (f = folder_entry(index, FOLDERS(cntxt))) *************** *** 6249,6254 **** --- 6361,6371 ---- int rv, we_cancel = 0, match; char msg_buf[MAX_BM+1]; + if (loop && (index == last)){ + done++; + break; + } + /* must be a folder and it can't be the current one */ if(ps_global->context_current == ps_global->context_list && !strcmp(ps_global->cur_folder, FLDR_NAME(f))) *************** *** 6416,6427 **** --- 6533,6556 ---- if(f && (!find_recent || recent)){ strncpy(next, FLDR_NAME(f), nextlen); next[nextlen-1] = '\0'; + done++; } else if(nextlen > 0) *next = '\0'; + if (!done && F_ON(F_AUTO_CIRCULAR_TAB,ps_global) + && strcmp(test_current,ps_global->inbox_name)){ + done++; loop++; + if (test_current) + fs_give((void **)&test_current); + test_current = cpystr(ps_global->inbox_name); + goto find_new_message; + } + /* BUG: how can this be made smarter so we cache the list? */ free_folder_list(cntxt); + if (test_current) + fs_give((void **)&test_current); return((*next) ? next : NULL); } diff -rc alpine-2.00/alpine/help.c alpine-2.00.I.USE/alpine/help.c *** alpine-2.00/alpine/help.c 2008-04-10 09:50:54.000000000 -0700 --- alpine-2.00.I.USE/alpine/help.c 2011-02-07 20:33:44.000000000 -0800 *************** *** 390,395 **** --- 390,396 ---- { int rv = 0; char message[64]; + struct help_texts *t; switch(cmd){ /*----------- Print all the help ------------*/ *************** *** 407,412 **** --- 408,430 ---- break; + case MC_EXPORT: /* reuse old definition, so as not to patch pine.h */ + {char help_name[40]; + help_name[0] = '\0'; + for(t = h_texts; t->help_text != NO_HELP; t++) + if(t->help_text == ((HELP_SCROLL_S *)sparms->proc.data.p)->help_source){ + strcpy(help_name,t->tag); + break; + } + if(help_name[0]) + q_status_message1(SM_ORDER, 0, 2, + "Internal Name: x-alpine-help:%s", help_name); + else + q_status_message(SM_ORDER|SM_DING, 1, 2, + "Can not find link for text help"); + } + break; + case MC_FINISH : rv = 1; break; *************** *** 572,578 **** int url_local_helper(char *url) { ! if(!struncmp(url, "x-alpine-help:", 14) && *(url += 14)){ char *frag; HelpType newhelp; --- 590,597 ---- int url_local_helper(char *url) { ! if((!struncmp(url, "x-alpine-help:", 14) && *(url += 14)) ! || (!struncmp(url, "x-pine-help:", 12) && *(url += 12))){ char *frag; HelpType newhelp; diff -rc alpine-2.00/alpine/imap.c alpine-2.00.I.USE/alpine/imap.c *** alpine-2.00/alpine/imap.c 2008-04-10 14:26:20.000000000 -0700 --- alpine-2.00.I.USE/alpine/imap.c 2011-02-07 20:33:47.000000000 -0800 *************** *** 94,100 **** * Internal prototypes */ void mm_login_alt_cue(NETMBX *); ! long pine_tcptimeout_noscreen(long, long); int answer_cert_failure(int, MSGNO_S *, SCROLL_S *); #ifdef LOCAL_PASSWD_CACHE --- 94,100 ---- * Internal prototypes */ void mm_login_alt_cue(NETMBX *); ! long pine_tcptimeout_noscreen(long, long, char *); int answer_cert_failure(int, MSGNO_S *, SCROLL_S *); #ifdef LOCAL_PASSWD_CACHE *************** *** 350,355 **** --- 350,356 ---- HelpType help ; int len, rc, q_line, flags; int oespace, avail, need, save_dont_use; + int save_in_init; struct servent *sv; #if defined(_WINDOWS) || defined(LOCAL_PASSWD_CACHE) int preserve_password = -1; *************** *** 364,369 **** --- 365,372 ---- altuserforcache ? altuserforcache : "")); q_line = -(ps_global->ttyo ? ps_global->ttyo->footer_rows : 3); + save_in_init = ps_global->in_init_seq; + ps_global->in_init_seq = 0; ps_global->no_newmail_check_from_optionally_enter = 1; /* make sure errors are seen */ *************** *** 440,448 **** /* try last working password associated with this host. */ if(imap_get_passwd(mm_login_list, pwd, user, hostlist, ! (mb->sslflag||mb->tlsflag))){ dprint((9, "mm_login: found a password to try\n")); ps_global->no_newmail_check_from_optionally_enter = 0; return; } --- 443,452 ---- /* try last working password associated with this host. */ if(imap_get_passwd(mm_login_list, pwd, user, hostlist, ! (mb->sslflag||mb->tlsflag)) && pwd[0] != '\0'){ dprint((9, "mm_login: found a password to try\n")); ps_global->no_newmail_check_from_optionally_enter = 0; + ps_global->in_init_seq = save_in_init; return; } *************** *** 456,461 **** --- 460,466 ---- (mb->sslflag||mb->tlsflag)); dprint((9, "mm_login: found a password in passfile to try\n")); ps_global->no_newmail_check_from_optionally_enter = 0; + ps_global->in_init_seq = save_in_init; return; } #endif /* LOCAL_PASSWD_CACHE */ *************** *** 484,489 **** --- 489,495 ---- "mm_login: found a password for user=%s to try\n", user ? user : "?")); ps_global->no_newmail_check_from_optionally_enter = 0; + ps_global->in_init_seq = save_in_init; return; } *************** *** 499,504 **** --- 505,511 ---- "mm_login: found a password for user=%s in passfile to try\n", user ? user : "?")); ps_global->no_newmail_check_from_optionally_enter = 0; + ps_global->in_init_seq = save_in_init; return; } #endif /* LOCAL_PASSWD_CACHE */ *************** *** 519,524 **** --- 526,532 ---- (mb->sslflag||mb->tlsflag))){ dprint((9, "mm_login:ui: found a password to try\n")); ps_global->no_newmail_check_from_optionally_enter = 0; + ps_global->in_init_seq = save_in_init; return; } *************** *** 532,537 **** --- 540,546 ---- (mb->sslflag||mb->tlsflag)); dprint((9, "mm_login:ui: found a password in passfile to try\n")); ps_global->no_newmail_check_from_optionally_enter = 0; + ps_global->in_init_seq = save_in_init; return; } #endif /* LOCAL_PASSWD_CACHE */ *************** *** 750,755 **** --- 759,765 ---- if(!(user[0] || altuserforcache)){ ps_global->no_newmail_check_from_optionally_enter = 0; + ps_global->in_init_seq = save_in_init; return; } *************** *** 762,767 **** --- 772,778 ---- if(imap_get_passwd(mm_login_list, pwd, user, hostlist, (mb->sslflag||mb->tlsflag))){ ps_global->no_newmail_check_from_optionally_enter = 0; + ps_global->in_init_seq = save_in_init; return; } *************** *** 771,776 **** --- 782,788 ---- imap_set_passwd(&mm_login_list, pwd, user, hostlist, (mb->sslflag||mb->tlsflag), 0, 0); ps_global->no_newmail_check_from_optionally_enter = 0; + ps_global->in_init_seq = save_in_init; return; } #endif /* LOCAL_PASSWD_CACHE */ *************** *** 779,784 **** --- 791,797 ---- if(imap_get_passwd(mm_login_list, pwd, altuserforcache, hostlist, (mb->sslflag||mb->tlsflag))){ ps_global->no_newmail_check_from_optionally_enter = 0; + ps_global->in_init_seq = save_in_init; return; } *************** *** 788,793 **** --- 801,807 ---- imap_set_passwd(&mm_login_list, pwd, altuserforcache, hostlist, (mb->sslflag||mb->tlsflag), 0, 0); ps_global->no_newmail_check_from_optionally_enter = 0; + ps_global->in_init_seq = save_in_init; return; } #endif /* LOCAL_PASSWD_CACHE */ *************** *** 976,981 **** --- 990,996 ---- ps_global->user_says_cancel = (rc == 1); user[0] = pwd[0] = '\0'; ps_global->no_newmail_check_from_optionally_enter = 0; + ps_global->in_init_seq = save_in_init; return; } *************** *** 1181,1187 **** long ! pine_tcptimeout_noscreen(long int elapsed, long int sincelast) { long rv = 1L; char pmt[128]; --- 1196,1202 ---- long ! pine_tcptimeout_noscreen(long int elapsed, long int sincelast, char *host) { long rv = 1L; char pmt[128]; *************** *** 1192,1199 **** if(elapsed >= (long)ps_global->tcp_query_timeout){ snprintf(pmt, sizeof(pmt), ! _("Waited %s seconds for server reply. Break connection to server"), ! long2string(elapsed)); pmt[sizeof(pmt)-1] = '\0'; if(want_to(pmt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){ ps_global->user_says_cancel = 1; --- 1207,1214 ---- if(elapsed >= (long)ps_global->tcp_query_timeout){ snprintf(pmt, sizeof(pmt), ! _("No reply in %s seconds from server %s. Break connection"), ! long2string(elapsed), host); pmt[sizeof(pmt)-1] = '\0'; if(want_to(pmt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){ ps_global->user_says_cancel = 1; *************** *** 1216,1222 **** * pine_tcptimeout - C-client callback to handle tcp-related timeouts. */ long ! pine_tcptimeout(long int elapsed, long int sincelast) { long rv = 1L; /* keep trying by default */ unsigned long ch; --- 1231,1237 ---- * pine_tcptimeout - C-client callback to handle tcp-related timeouts. */ long ! pine_tcptimeout(long int elapsed, long int sincelast, char *host) { long rv = 1L; /* keep trying by default */ unsigned long ch; *************** *** 1236,1242 **** return(rv); if(!ps_global->ttyo) ! return(pine_tcptimeout_noscreen(elapsed, sincelast)); suspend_busy_cue(); --- 1251,1257 ---- return(rv); if(!ps_global->ttyo) ! return(pine_tcptimeout_noscreen(elapsed, sincelast, host)); suspend_busy_cue(); *************** *** 1253,1261 **** Writechar(BELL, 0); ! PutLine1(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global), 0, ! _("Waited %s seconds for server reply. Break connection to server? "), ! long2string(elapsed)); CleartoEOLN(); fflush(stdout); flush_input(); --- 1268,1276 ---- Writechar(BELL, 0); ! PutLine2(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global), 0, ! _("No reply in %s seconds from server %s. Break connection?"), ! long2string(elapsed), host); CleartoEOLN(); fflush(stdout); flush_input(); *************** *** 1272,1280 **** } if(rv == 1L){ /* just warn 'em something's up */ ! q_status_message1(SM_ORDER, 0, 0, ! _("Waited %s seconds for server reply. Still Waiting..."), ! long2string(elapsed)); flush_status_messages(0); /* make sure it's seen */ } --- 1287,1295 ---- } if(rv == 1L){ /* just warn 'em something's up */ ! q_status_message2(SM_ORDER, 0, 0, ! _("No reply in %s seconds from server %s. Still Waiting..."), ! long2string(elapsed), host); flush_status_messages(0); /* make sure it's seen */ } *************** *** 1285,1290 **** --- 1300,1335 ---- return(rv); } + QUOTALIST *pine_quotalist_copy (pquota) + QUOTALIST *pquota; + { + QUOTALIST *cquota = NULL; + + if(pquota){ + cquota = mail_newquotalist(); + if (pquota->name && *pquota->name){ + cquota->name = (char *) fs_get((strlen(pquota->name) + 1)*sizeof(char)); + cquota->name = cpystr(pquota->name); + } + cquota->usage = pquota->usage; + cquota->limit = pquota->limit; + if (pquota->next) + cquota->next = pine_quotalist_copy(pquota->next); + } + return cquota; + } + + + /* C-client callback to handle quota */ + + void + pine_parse_quota (stream, msg, pquota) + MAILSTREAM *stream; + unsigned char *msg; + QUOTALIST *pquota; + { + ps_global->quota = pine_quotalist_copy (pquota); + } /* * C-client callback to handle SSL/TLS certificate validation failures diff -rc alpine-2.00/alpine/imap.h alpine-2.00.I.USE/alpine/imap.h *** alpine-2.00/alpine/imap.h 2008-04-23 19:00:26.000000000 -0700 --- alpine-2.00.I.USE/alpine/imap.h 2011-02-07 20:33:45.000000000 -0800 *************** *** 21,28 **** /* exported protoypes */ void *pine_block_notify(int, void *); ! long pine_tcptimeout(long, long); long pine_sslcertquery(char *, char *, char *); char *pine_newsrcquery(MAILSTREAM *, char *, char *); int url_local_certdetails(char *); --- 21,30 ---- /* exported protoypes */ + void pine_parse_quota (MAILSTREAM *, unsigned char *, QUOTALIST *); + QUOTALIST *pine_quotalist_copy (QUOTALIST *); void *pine_block_notify(int, void *); ! long pine_tcptimeout(long, long, char *); long pine_sslcertquery(char *, char *, char *); char *pine_newsrcquery(MAILSTREAM *, char *, char *); int url_local_certdetails(char *); diff -rc alpine-2.00/alpine/keymenu.c alpine-2.00.I.USE/alpine/keymenu.c *** alpine-2.00/alpine/keymenu.c 2008-06-03 08:54:15.000000000 -0700 --- alpine-2.00.I.USE/alpine/keymenu.c 2011-02-07 20:33:47.000000000 -0800 *************** *** 314,319 **** --- 314,333 ---- WHEREIS_MENU}; INST_KEY_MENU(c_fcc_km, context_fcc_keys); + static struct key quota_keys[] = + {HELP_MENU, + NULL_MENU, + {"E","Exit",{MC_EXIT,3,{'e','i',ctrl('C')}},KS_EXITMODE}, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU}; + INST_KEY_MENU(pine_quota_keymenu, quota_keys); struct key folder_keys[] = {HELP_MENU, *************** *** 482,488 **** NEXTPAGE_MENU, PRYNTMSG_MENU, {"Z",N_("Print All"),{MC_PRINTALL,1,{'z'}},KS_NONE}, ! NULL_MENU, WHEREIS_MENU, HELP_MENU, --- 496,502 ---- NEXTPAGE_MENU, PRYNTMSG_MENU, {"Z",N_("Print All"),{MC_PRINTALL,1,{'z'}},KS_NONE}, ! {"N",N_("Name"),{MC_EXPORT,1,{'n'}},KS_NONE}, WHEREIS_MENU, HELP_MENU, *************** *** 650,661 **** RCOMPOSE_MENU, HOMEKEY_MENU, ENDKEY_MENU, ! NULL_MENU, /* TRANSLATORS: toggles a collapsed view or an expanded view of a message thread on and off */ {"/",N_("Collapse/Expand"),{MC_COLLAPSE,1,{'/'}},KS_NONE}, ! NULL_MENU, NULL_MENU}; INST_KEY_MENU(index_keymenu, index_keys); --- 664,691 ---- RCOMPOSE_MENU, HOMEKEY_MENU, ENDKEY_MENU, ! {"K","Sort Thread",{MC_SORTHREAD,1,{'k'}},KS_NONE}, /* TRANSLATORS: toggles a collapsed view or an expanded view of a message thread on and off */ {"/",N_("Collapse/Expand"),{MC_COLLAPSE,1,{'/'}},KS_NONE}, ! /* TRANSLATORS: Collapse all threads */ ! {"{",N_("Collapse All"),{MC_KOLAPSE,1,{'{'}},KS_NONE}, ! /* TRANSLATORS: Expand all threads */ ! {"}",N_("Expand All"), {MC_EXPTHREAD,1,{'}'}},KS_NONE}, ! ! HELP_MENU, ! OTHER_MENU, ! {")","Next Threa",{MC_NEXTHREAD,1,{')'}},KS_NONE}, ! {"(","Prev Threa",{MC_PRETHREAD,1,{'('}},KS_NONE}, ! {"^D","Delete Thr",{MC_DELTHREAD,1,{ctrl('D')}},KS_NONE}, ! {"^U","Undel Thre",{MC_UNDTHREAD,1,{ctrl('U')}},KS_NONE}, ! {"^T","Select Thr",{MC_SELTHREAD,1,{ctrl('T')}},KS_NONE}, ! NULL_MENU, ! {"[","Close Thre",{MC_CTHREAD,1,{'['}},KS_NONE}, ! {"]","Open Threa",{MC_OTHREAD,1,{']'}},KS_NONE}, ! {"@", N_("Quota"), {MC_QUOTA,1,{'@'}}, KS_NONE}, NULL_MENU}; + INST_KEY_MENU(index_keymenu, index_keys); *************** *** 728,736 **** RCOMPOSE_MENU, HOMEKEY_MENU, ENDKEY_MENU, ! NULL_MENU, {"/",N_("Collapse/Expand"),{MC_COLLAPSE,1,{'/'}},KS_NONE}, NULL_MENU, NULL_MENU}; INST_KEY_MENU(thread_keymenu, thread_keys); --- 758,779 ---- RCOMPOSE_MENU, HOMEKEY_MENU, ENDKEY_MENU, ! {"]",N_("Open Thread"),{MC_OTHREAD,1,{']'}},KS_NONE}, {"/",N_("Collapse/Expand"),{MC_COLLAPSE,1,{'/'}},KS_NONE}, + {")",N_("Next Thread"),{MC_NEXTHREAD,1,{')'}},KS_NONE}, + {"(",N_("Prev Thread"),{MC_PRETHREAD,1,{'('}},KS_NONE}, + + HELP_MENU, + OTHER_MENU, + {"@", N_("Quota"), {MC_QUOTA,1,{'@'}}, KS_NONE}, + NULL_MENU, + {"^D",N_("Delete Thread"),{MC_DELTHREAD,1,{ctrl('D')}},KS_NONE}, + {"^U",N_("Undelete Thread"),{MC_UNDTHREAD,1,{ctrl('U')}},KS_NONE}, + {"^T",N_("SelecT Thread"),{MC_SELTHREAD,1,{ctrl('T')}},KS_NONE}, + NULL_MENU, + NULL_MENU, NULL_MENU, + {"K","Sort Thread",{MC_SORTHREAD,1,{'k'}},KS_NONE}, NULL_MENU}; INST_KEY_MENU(thread_keymenu, thread_keys); *************** *** 880,886 **** NULL_MENU, NULL_MENU, NULL_MENU, ! NULL_MENU}; INST_KEY_MENU(view_keymenu, view_keys); --- 923,942 ---- NULL_MENU, NULL_MENU, NULL_MENU, ! NULL_MENU, ! ! HELP_MENU, ! OTHER_MENU, ! NULL_MENU, ! NULL_MENU, ! NULL_MENU, ! NULL_MENU, ! NULL_MENU, ! {"(",N_("Prev Thread"),{MC_PRETHREAD,1,{'('}},KS_NONE}, ! {")",N_("Next Thread"),{MC_NEXTHREAD,1,{')'}},KS_NONE}, ! {"^D",N_("Delete Thread"),{MC_DELTHREAD,1,{ctrl('D')}},KS_NONE}, ! {"^U",N_("Undelete Thread"),{MC_UNDTHREAD,1,{ctrl('U')}},KS_NONE}, ! {"^T",N_("selecT Thread"),{MC_SELTHREAD,1,{ctrl('T')}},KS_NONE}}; INST_KEY_MENU(view_keymenu, view_keys); *************** *** 2452,2458 **** HELP_MENU, OTHER_MENU, ! NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, --- 2508,2514 ---- HELP_MENU, OTHER_MENU, ! {"I", N_("IndxHdr"), {MC_ADDHEADER,1,{'i'}}, KS_NONE}, NULL_MENU, NULL_MENU, NULL_MENU, *************** *** 2481,2487 **** HELP_MENU, OTHER_MENU, ! NULL_MENU, NULL_MENU, {"D", N_("DeleteHdr"), {MC_DELETE,1,{'d'}}, KS_NONE}, /* TRANSLATORS: shuffle headers (change the order of headers) */ --- 2537,2543 ---- HELP_MENU, OTHER_MENU, ! {"I", N_("IndxHdr"), {MC_ADDHEADER,1,{'i'}}, KS_NONE}, NULL_MENU, {"D", N_("DeleteHdr"), {MC_DELETE,1,{'d'}}, KS_NONE}, /* TRANSLATORS: shuffle headers (change the order of headers) */ *************** *** 2527,2533 **** HELP_MENU, OTHER_MENU, ! NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, --- 2583,2589 ---- HELP_MENU, OTHER_MENU, ! {"I", N_("IndxHdr"), {MC_ADDHEADER,1,{'i'}}, KS_NONE}, NULL_MENU, NULL_MENU, NULL_MENU, diff -rc alpine-2.00/alpine/keymenu.h alpine-2.00.I.USE/alpine/keymenu.h *** alpine-2.00/alpine/keymenu.h 2008-06-03 08:54:15.000000000 -0700 --- alpine-2.00.I.USE/alpine/keymenu.h 2011-02-07 20:33:47.000000000 -0800 *************** *** 212,218 **** #define MC_COLLAPSE 800 #define MC_CHK_RECENT 801 #define MC_DECRYPT 802 ! /* * Some standard Key/Command Bindings --- 212,232 ---- #define MC_COLLAPSE 800 #define MC_CHK_RECENT 801 #define MC_DECRYPT 802 ! #define MC_CTHREAD 803 ! #define MC_OTHREAD 804 ! #define MC_DELTHREAD 805 ! #define MC_UNDTHREAD 806 ! #define MC_SELTHREAD 807 ! #define MC_SSUTHREAD 808 ! #define MC_DSUTHREAD 809 ! #define MC_USUTHREAD 810 ! #define MC_SORTHREAD 811 ! #define MC_NEXTHREAD 812 ! #define MC_KOLAPSE 813 ! #define MC_EXPTHREAD 814 ! #define MC_PRETHREAD 815 ! #define MC_QUOTA 816 ! #define MC_ADDHEADER 817 /* * Some standard Key/Command Bindings *************** *** 559,564 **** --- 573,579 ---- c_cfg_km, c_sel_km, c_fcc_km, + pine_quota_keymenu, folder_km, folder_sel_km, folder_sela_km, diff -rc alpine-2.00/alpine/mailcmd.c alpine-2.00.I.USE/alpine/mailcmd.c *** alpine-2.00/alpine/mailcmd.c 2008-08-21 15:14:45.000000000 -0700 --- alpine-2.00.I.USE/alpine/mailcmd.c 2011-02-07 20:33:44.000000000 -0800 *************** *** 72,77 **** --- 72,78 ---- #include "../pith/tempfile.h" #include "../pith/search.h" #include "../pith/margin.h" + #include "../pith/rules.h" #ifdef _WINDOWS #include "../pico/osdep/mswin.h" #endif *************** *** 112,118 **** char *choose_a_rule(int); int select_by_keyword(MAILSTREAM *, SEARCHSET **); char *choose_a_keyword(void); ! int select_sort(struct pine *, int, SortOrder *, int *); int print_index(struct pine *, MSGNO_S *, int); --- 113,119 ---- char *choose_a_rule(int); int select_by_keyword(MAILSTREAM *, SEARCHSET **); char *choose_a_keyword(void); ! int select_sort(struct pine *, int, SortOrder *, int *, int); int print_index(struct pine *, MSGNO_S *, int); *************** *** 253,258 **** --- 254,260 ---- {'r', 'r', "R", N_("Recipient")}, {'p', 'p', "P", N_("Participant")}, {'b', 'b', "B", N_("Body")}, + {'h', 'h', "H", N_("Header")}, {-1, 0, NULL, NULL} }; *************** *** 972,978 **** state->context_current, &recent_cnt, F_ON(F_TAB_NO_CONFIRM,state) ? NULL : &did_cancel))){ ! if(!in_inbox){ static ESCKEY_S inbox_opt[] = { {'y', 'y', "Y", N_("Yes")}, {'n', 'n', "N", N_("No")}, --- 974,980 ---- state->context_current, &recent_cnt, F_ON(F_TAB_NO_CONFIRM,state) ? NULL : &did_cancel))){ ! if(!in_inbox && F_OFF(F_AUTO_CIRCULAR_TAB,state)){ static ESCKEY_S inbox_opt[] = { {'y', 'y', "Y", N_("Yes")}, {'n', 'n', "N", N_("No")}, *************** *** 1333,1339 **** if(any_messages(msgmap, NULL, NULL)){ if(any_lflagged(msgmap, MN_SLCT) > 0L){ if(apply_command(state, stream, msgmap, 0, ! AC_NONE, question_line)){ if(F_ON(F_AUTO_UNSELECT, state)){ agg_select_all(stream, msgmap, NULL, 0); unzoom_index(state, stream, msgmap); --- 1335,1341 ---- if(any_messages(msgmap, NULL, NULL)){ if(any_lflagged(msgmap, MN_SLCT) > 0L){ if(apply_command(state, stream, msgmap, 0, ! AC_NONE, question_line, 1)){ if(F_ON(F_AUTO_UNSELECT, state)){ agg_select_all(stream, msgmap, NULL, 0); unzoom_index(state, stream, msgmap); *************** *** 1351,1373 **** /*-------- Sort command -------*/ case MC_SORT : { int were_threading = THREADING(); SortOrder sort = mn_get_sort(msgmap); int rev = mn_get_revsort(msgmap); dprint((1,"MAIL_CMD: sort\n")); ! if(select_sort(state, question_line, &sort, &rev)){ /* $ command reinitializes threading collapsed/expanded info */ if(SORT_IS_THREADED(msgmap) && !SEP_THRDINDX()) erase_threading_info(stream, msgmap); if(ps_global && ps_global->ttyo){ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0); ps_global->mangled_footer = 1; } ! sort_folder(stream, msgmap, sort, rev, SRT_VRB|SRT_MAN); } state->mangled_footer = 1; --- 1353,1387 ---- /*-------- Sort command -------*/ case MC_SORT : + case MC_SORTHREAD: { int were_threading = THREADING(); SortOrder sort = mn_get_sort(msgmap); int rev = mn_get_revsort(msgmap); + int thread = (command == MC_SORT) ? 0 : 1; dprint((1,"MAIL_CMD: sort\n")); ! if(sort == SortThread) ! sort = ps_global->thread_cur_sort; ! if(select_sort(state, question_line, &sort, &rev, thread)){ /* $ command reinitializes threading collapsed/expanded info */ if(SORT_IS_THREADED(msgmap) && !SEP_THRDINDX()) erase_threading_info(stream, msgmap); + if(command == MC_SORTHREAD){ + ps_global->thread_cur_sort = sort; + sort = SortThread; + } + else if(sort == SortThread) /* command = MC_SORT */ + ps_global->thread_cur_sort = F_ON(F_THREAD_SORTS_BY_ARRIVAL, ps_global) + ? SortArrival : ps_global->thread_def_sort; + if(ps_global && ps_global->ttyo){ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0); ps_global->mangled_footer = 1; } ! sort_folder(stream, msgmap, sort, rev, SRT_VRB|SRT_MAN, 1); } state->mangled_footer = 1; *************** *** 2368,2373 **** --- 2382,2388 ---- dprint((4, "\n - saving message -\n")); + saved_stream = stream; /* ugly hack! */ state->ugly_consider_advancing_bit = 0; if(F_OFF(F_SAVE_PARTIAL_WO_CONFIRM, state) && msgno_any_deletedparts(stream, msgmap) *************** *** 2592,2597 **** --- 2607,2615 ---- role->nick = cpystr("Default Role"); } + if(state->role) + fs_give((void **)&state->role); + state->role = cpystr(role->nick); /* remember the role */ state->redrawer = NULL; switch(action){ case 'c': *************** *** 2642,2653 **** char *nmsgs, ENVELOPE *env, long int rawmsgno, char *section, SaveDel *dela, SavePreserveOrder *prea) { ! int rc, ku = -1, n, flags, last_rc = 0, saveable_count = 0, done = 0; int delindex, preindex, r; char prompt[6*MAX_SCREEN_COLS+1], *p, expanded[MAILTMPLEN]; char *buf = tmp_20k_buf; char shortbuf[200]; ! char *folder; HelpType help; SaveDel del = DontAsk; SavePreserveOrder pre = DontAskPreserve; --- 2660,2671 ---- char *nmsgs, ENVELOPE *env, long int rawmsgno, char *section, SaveDel *dela, SavePreserveOrder *prea) { ! int rc, ku = -1, n = 0, flags, last_rc = 0, saveable_count = 0, done = 0; int delindex, preindex, r; char prompt[6*MAX_SCREEN_COLS+1], *p, expanded[MAILTMPLEN]; char *buf = tmp_20k_buf; char shortbuf[200]; ! char *folder, folder2[MAXPATH]; HelpType help; SaveDel del = DontAsk; SavePreserveOrder pre = DontAskPreserve; *************** *** 2655,2660 **** --- 2673,2681 ---- static HISTORY_S *history = NULL; CONTEXT_S *tc; ESCKEY_S ekey[10]; + RULE_RESULT *rule; + + saved_stream = state->mail_stream; if(!cntxt) panic("no context ptr in save_prompt"); *************** *** 2664,2669 **** --- 2685,2699 ---- if(!(folder = save_get_default(state, env, rawmsgno, section, cntxt))) return(0); /* message expunged! */ + if (rule = get_result_rule(V_SAVE_RULES, FOR_SAVE, env)){ + strncpy(folder2,rule->result,sizeof(folder2)-1); + folder2[sizeof(folder2)-1] = '\0'; + folder = folder2; + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + /* how many context's can be saved to... */ for(tc = state->context_list; tc; tc = tc->next) if(!NEWS_TEST(tc)) *************** *** 3172,3177 **** --- 3202,3211 ---- if(SORT_IS_THREADED(msgmap)) refresh_sort(stream, msgmap, SRT_NON); + if (msgmap->nmsgs + && F_ON(F_ENHANCED_THREAD, state) && COLL_THRDS()) + kolapse_thread(state, stream, msgmap, '[', 0); + state->mangled_body = 1; state->mangled_header = 1; q_status_message2(SM_ORDER, 0, 4, *************** *** 3266,3271 **** --- 3300,3308 ---- */ if(SORT_IS_THREADED(msgmap)) refresh_sort(stream, msgmap, SRT_NON); + if (msgmap->nmsgs + && F_ON(F_ENHANCED_THREAD, state) && COLL_THRDS()) + kolapse_thread(state, stream, msgmap, '[', 0); } else{ if(del_count) *************** *** 3347,3352 **** --- 3384,3392 ---- {-1, 0, NULL, NULL} }; + if(F_ON(F_IGNORE_SIZE, ps_global)) + return 'y'; + if(flags & SSCP_INIT || flags & SSCP_END){ if(flags & SSCP_END && possible_corruption) q_status_message(SM_ORDER, 3, 3, "There is possible data corruption, check the results"); *************** *** 6933,6939 **** * Maybe it makes sense to zoom after a select but not after a colon * command even though they are very similar. */ ! thread_command(state, state->mail_stream, msgmap, ':', -FOOTER_ROWS(state)); } else{ if((all_selected = --- 6973,6979 ---- * Maybe it makes sense to zoom after a select but not after a colon * command even though they are very similar. */ ! thread_command(state, state->mail_stream, msgmap, ':', -FOOTER_ROWS(state), 1); } else{ if((all_selected = *************** *** 6989,6995 **** ----*/ int apply_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, ! UCS preloadkeystroke, int flags, int q_line) { int i = 8, /* number of static entries in sel_opts3 */ rv = 0, --- 7029,7035 ---- ----*/ int apply_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, ! UCS preloadkeystroke, int flags, int q_line, int display) { int i = 8, /* number of static entries in sel_opts3 */ rv = 0, *************** *** 7141,7149 **** collapse_or_expand(state, stream, msgmap, F_ON(F_SLASH_COLL_ENTIRE, ps_global) ? 0L ! : mn_get_cur(msgmap)); break; case ':' : select_thread_stmp(state, stream, msgmap); break; --- 7181,7199 ---- collapse_or_expand(state, stream, msgmap, F_ON(F_SLASH_COLL_ENTIRE, ps_global) ? 0L ! : mn_get_cur(msgmap), ! display); break; + case '[' : + collapse_this_thread(state, stream, msgmap, display, 0); + break; + + case ']' : + expand_this_thread(state, stream, msgmap, display, 0); + break; + + case ':' : select_thread_stmp(state, stream, msgmap); break; *************** *** 7670,7676 **** int not = 0, me = 0; char sstring[80], savedsstring[80], tmp[128]; char *p, *sval = NULL; ! char buftmp[MAILTMPLEN]; ESCKEY_S ekey[8]; ENVELOPE *env = NULL; HelpType help; --- 7720,7726 ---- int not = 0, me = 0; char sstring[80], savedsstring[80], tmp[128]; char *p, *sval = NULL; ! char buftmp[MAILTMPLEN], namehdr[80]; ESCKEY_S ekey[8]; ENVELOPE *env = NULL; HelpType help; *************** *** 7757,7762 **** --- 7807,7846 ---- sval = "BODYTEXT"; break; + case 'h' : + strcpy(tmp, "Name of HEADER to match : "); + flags = OE_APPEND_CURRENT; + namehdr[0] = '\0'; + r = 'x'; + while (r == 'x'){ + int done = 0; + + r = optionally_enter(namehdr, -FOOTER_ROWS(ps_global), 0, + sizeof(namehdr), tmp, ekey, NO_HELP, &flags); + if (r == 1){ + cmd_cancelled("Selection by text"); + return(1); + } + removing_leading_white_space(namehdr); + while(!done){ + while ((namehdr[0] != '\0') && /* remove trailing ":" */ + (namehdr[strlen(namehdr) - 1] == ':')) + namehdr[strlen(namehdr) - 1] = '\0'; + if ((namehdr[0] != '\0') + && isspace((unsigned char) namehdr[strlen(namehdr) - 1])) + removing_trailing_white_space(namehdr); + else + done++; + } + if (strchr(namehdr,' ') || strchr(namehdr,'\t') || + strchr(namehdr,':')) + namehdr[0] = '\0'; + if (namehdr[0] == '\0') + r = 'x'; + } + sval = namehdr; + break; + case 'x': break; *************** *** 7953,7959 **** flagsforhist = (not ? 0x1 : 0) + (me ? 0x2 : 0); save_hist(history, sstring, flagsforhist, NULL); ! rv = agg_text_select(stream, msgmap, type, not, me, sstring, "utf-8", limitsrch); if(we_cancel) cancel_busy_cue(0); --- 8037,8043 ---- flagsforhist = (not ? 0x1 : 0) + (me ? 0x2 : 0); save_hist(history, sstring, flagsforhist, NULL); ! rv = agg_text_select(stream, msgmap, type, namehdr, not, me, sstring, "utf-8", limitsrch); if(we_cancel) cancel_busy_cue(0); *************** *** 8874,8879 **** --- 8958,9102 ---- return(ret); } + int process_quota_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms) + { + return cmd; + } + + void cmd_quota (struct pine *state) + { + QUOTALIST *imapquota; + NETMBX mb; + unsigned long len, storageuse, storagelim, messageuse, messagelim; + STORE_S *store; + SCROLL_S sargs; + char *linedata, *storageq = NULL, *messageq = NULL; + int storage=0, message=0, other=0, storagelen = 0, messagelen = 0; + + if(!state->mail_stream || !is_imap_stream(state->mail_stream)){ + q_status_message(SM_ORDER, 1, 5, "Quota only available for IMAP folders"); + return; + } + + if (state->mail_stream + && !sp_dead_stream(state->mail_stream) + && state->mail_stream->mailbox + && *state->mail_stream->mailbox + && mail_valid_net_parse(state->mail_stream->mailbox, &mb)) + imap_getquotaroot(state->mail_stream, mb.mailbox); + + if(!state->quota) /* failed ? */ + return; /* go back... */ + + if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){ + q_status_message(SM_ORDER | SM_DING, 3, 3, "Error allocating space."); + return; + } + + if (state->mail_stream && state->mail_stream->original_mailbox){ + len = strlen(state->mail_stream->original_mailbox) + 19; + linedata = (char *) fs_get(len*sizeof(char)); + sprintf(linedata,"Quota Report for %s\n", + state->mail_stream->original_mailbox); + so_puts(store,linedata); + if (linedata) + fs_give((void **)&linedata); + } + else + so_puts(store,"Quota Report:\n"); + so_puts(store,"\n"); + + for (imapquota = state->quota; imapquota; imapquota = imapquota->next){ + if(!strucmp(imapquota->name,"STORAGE")){ + storage++; + storagelen = strlen(long2string(imapquota->limit)) + 1+3; + storageuse = imapquota->usage; + storagelim = imapquota->limit; + } + if(!strucmp(imapquota->name,"MESSAGE")){ + message++; + messagelen = strlen(long2string(imapquota->limit)) + 1+9; + messageuse = imapquota->usage; + messagelim = imapquota->limit; + } + other += strucmp(imapquota->name,"STORAGE") && + strucmp(imapquota->name,"MESSAGE") ? 0 : 1; + } + + storageq = (char *) fs_get(storagelen*sizeof(char)); + if (storage) + sprintf(storageq, "%lu KB", storageuse); + messageq = (char *) fs_get(messagelen*sizeof(char)); + if (message) + sprintf(messageq, "%lu message%s", messageuse, plural(messageuse)); + len = strlen("Usage: ") + storagelen + messagelen + 9; + linedata = (char *) fs_get(len*sizeof(char)); + sprintf(linedata,"Usage: %s%s%s%s\n", (storage ? storageq : ""), + (message ? (storage ? " (" : "") : ""), + (message ? messageq : ""), + (message ? (storage ? ")" : "") : "")); + so_puts(store, linedata); + if (storageq) + fs_give((void **)&storageq); + if (messageq) + fs_give((void **)&messageq); + if (linedata) + fs_give((void **)&linedata); + + storageq = (char *) fs_get(storagelen*sizeof(char)); + if (storage) + sprintf(storageq, "%lu KB", storagelim); + messageq = (char *) fs_get(messagelen*sizeof(char)); + if (message) + sprintf(messageq, "%lu message%s", messagelim, plural(messagelim)); + len = strlen("Limit: ") + storagelen + messagelen + 9; + linedata = (char *) fs_get(len*sizeof(char)); + sprintf(linedata,"Limit: %s%s%s%s", (storage ? storageq : ""), + (message ? (storage ? " (" : "") : ""), + (message ? messageq : ""), + (message ? (storage ? ")" : "") : "")); + so_puts(store, linedata); + if (storageq) + fs_give((void **)&storageq); + if (messageq) + fs_give((void **)&messageq); + if (linedata) + fs_give((void **)&linedata); + + for (imapquota = state->quota; other && imapquota; + imapquota = imapquota->next){ + if (strucmp(imapquota->name,"STORAGE") && + strucmp(imapquota->name,"MESSAGE")){ + len = (imapquota->name ? strlen(imapquota->name) : strlen("No Name")); + linedata = (char *) fs_get(len*sizeof(char)); + sprintf(linedata, + "Resource : %s\nUsage : %lu (%lu%%)\nLimit : %lu\n\n", + (imapquota->name ? imapquota->name : "No Name"), + imapquota->usage, 100*imapquota->usage/imapquota->limit, + imapquota->limit); + so_puts(store,linedata); + if (linedata) + fs_give((void **)&linedata); + } + } + + memset(&sargs, 0, sizeof(SCROLL_S)); + sargs.text.text = so_text(store); + sargs.text.src = CharStar; + sargs.text.desc = _("Quota Resources Summary"); + sargs.bar.title = _("QUOTA SUMMARY"); + sargs.proc.tool = process_quota_cmd; + sargs.help.text = NO_HELP; + sargs.help.title = NULL; + sargs.keys.menu = &pine_quota_keymenu; + setbitmap(sargs.keys.bitmap); + + scrolltool(&sargs); + so_give(&store); + + if (state->quota) + mail_free_quotalist(&(state->quota)); + } /*---------------------------------------------------------------------- Prompt the user for the type of sort he desires *************** *** 8884,8893 **** Returns 0 if it was cancelled, 1 otherwise. ----*/ int ! select_sort(struct pine *state, int ql, SortOrder *sort, int *rev) { char prompt[200], tmp[3], *p; ! int s, i; int deefault = 'a', retval = 1; HelpType help; ESCKEY_S sorts[14]; --- 9107,9116 ---- Returns 0 if it was cancelled, 1 otherwise. ----*/ int ! select_sort(struct pine *state, int ql, SortOrder *sort, int *rev, int thread) { char prompt[200], tmp[3], *p; ! int s, i, j; int deefault = 'a', retval = 1; HelpType help; ESCKEY_S sorts[14]; *************** *** 8920,8936 **** strncpy(prompt, _("Choose type of sort, or 'R' to reverse current sort : "), sizeof(prompt)); ! for(i = 0; state->sort_types[i] != EndofList; i++) { ! sorts[i].rval = i; ! p = sorts[i].label = sort_name(state->sort_types[i]); ! while(*(p+1) && islower((unsigned char)*p)) ! p++; ! ! sorts[i].ch = tolower((unsigned char)(tmp[0] = *p)); ! sorts[i].name = cpystr(tmp); ! ! if(mn_get_sort(state->msgmap) == state->sort_types[i]) ! deefault = sorts[i].rval; } sorts[i].ch = 'r'; --- 9143,9168 ---- strncpy(prompt, _("Choose type of sort, or 'R' to reverse current sort : "), sizeof(prompt)); ! for(i = 0, j = 0; state->sort_types[i] != EndofList; i++) { ! sorts[i].rval = i; ! sorts[i].name = cpystr(""); ! sorts[i].label = ""; ! sorts[i].ch = -2; ! if (!thread || allowed_thread_key(state->sort_types[i])){ ! p = sorts[j].label = sort_name(state->sort_types[i]); ! while(*(p+1) && islower((unsigned char)*p)) ! p++; ! sorts[j].ch = tolower((unsigned char)(tmp[0] = *p)); ! sorts[j++].name = cpystr(tmp); ! } ! ! if (thread){ ! if (state->thread_def_sort == state->sort_types[i]) ! deefault = sorts[j-1].rval; ! } ! else ! if(mn_get_sort(state->msgmap) == state->sort_types[i]) ! deefault = sorts[i].rval; } sorts[i].ch = 'r'; *************** *** 8954,8961 **** state->mangled_body = 1; /* signal screen's changed */ if(s == 'r') *rev = !mn_get_revsort(state->msgmap); ! else *sort = state->sort_types[s]; if(F_ON(F_SHOW_SORT, ps_global)) ps_global->mangled_header = 1; --- 9186,9202 ---- state->mangled_body = 1; /* signal screen's changed */ if(s == 'r') *rev = !mn_get_revsort(state->msgmap); ! else{ ! if(thread){ ! for(i = 0; state->sort_types[i] != EndofList; i++){ ! if(struncmp(sort_name(state->sort_types[i]), ! sorts[s].label, strlen(sorts[s].label)) == 0) ! break; ! } ! s = i; ! } *sort = state->sort_types[s]; + } if(F_ON(F_SHOW_SORT, ps_global)) ps_global->mangled_header = 1; *************** *** 9340,9342 **** --- 9581,9958 ---- } #endif /* _WINDOWS */ + + void + cmd_delete_this_thread(state, stream, msgmap) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + { + unsigned long rawno, top, save_kolapsed; + PINETHRD_S *thrd = NULL, *nxthrd; + + if(!stream) + return; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + move_top_this_thread(stream, msgmap, rawno); + top = mn_m2raw(msgmap, mn_get_cur(msgmap)); + if(top) + thrd = fetch_thread(stream, top); + + if(!thrd) + return; + + save_kolapsed = this_thread_is_kolapsed(state, stream, msgmap, top); + collapse_this_thread(state, stream, msgmap, 0, 0); + thread_command(state, stream, msgmap, 'd', -FOOTER_ROWS(state), 1); + if (!save_kolapsed) + expand_this_thread(state, stream, msgmap, 0, 0); + } + + void + cmd_delete_thread(state, stream, msgmap) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + { + unsigned long rawno, top, orig_top, topnxt, save_kolapsed; + PINETHRD_S *thrd = NULL, *nxthrd; + int done = 0, count; + + if(!stream) + return; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + move_top_thread(stream, msgmap, rawno); + top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); + if(top) + thrd = fetch_thread(stream, top); + + if(!thrd) + return; + + while (!done){ + cmd_delete_this_thread(state, stream, msgmap); + if (F_OFF(F_ENHANCED_THREAD, state) + || (move_next_this_thread(state, stream, msgmap, 0) <= 0) + || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) + || (orig_top != top_thread(stream, top))) + done++; + } + mn_set_cur(msgmap,mn_raw2m(msgmap, rawno)); + cmd_delete(state, msgmap, MCMD_NONE, cmd_delete_index); + count = count_thread(state, stream, msgmap, rawno); + q_status_message2(SM_ORDER, 0, 1, "%s message%s marked deleted", + int2string(count), plural(count)); + } + + int + collapse_this_thread(state, stream, msgmap, display, special) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + int display; + int special; + { + int collapsed, rv = 1, done = 0; + PINETHRD_S *thrd = NULL, *nthrd; + unsigned long rawno, orig, msgno; + + if(!stream) + return 0; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return rv; + + collapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno); + + if (special && collapsed){ + expand_this_thread(state, stream, msgmap, 0, 0); + collapsed = 0; + } + + clear_index_cache_ent(stream, rawno, 0); + + if (!collapsed && thrd->next){ + if (thrd->rawno == top_thread(stream, thrd->rawno)) + collapse_or_expand(state, stream, msgmap, mn_get_cur(msgmap), display); + else{ + set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno), MN_COLL, 1); + set_thread_subtree(stream, thrd, msgmap, 1, MN_CHID); + } + } + else{ + if (!collapsed && special + && ((F_OFF(F_ENHANCED_THREAD, state) && !thrd->next) + || F_ON(F_ENHANCED_THREAD, state))){ + if (thrd->toploose){ + if (thrd->rawno != thrd->toploose) + set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_CHID, + 1); + else + set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_COLL, + 1); + } + } + else{ + rv = 0; + if (display) + q_status_message(SM_ORDER, 0, 1, "Thread already collapsed"); + } + } + return rv; + } + + void + collapse_thread(state, stream, msgmap, display) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + int display; + { + int collapsed, rv = 1, done = 0; + PINETHRD_S *thrd = NULL; + unsigned long orig, orig_top, top; + + if(!stream) + return; + + expand_this_thread(state, stream, msgmap, display, 1); + orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); + move_top_thread(stream, msgmap,orig); + top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); + + if(top) + thrd = fetch_thread(stream, top); + + if(!thrd) + return; + + while (!done){ + collapse_this_thread(state, stream, msgmap, display, 1); + if (F_OFF(F_ENHANCED_THREAD, state) + || (move_next_this_thread(state, stream, msgmap, 0) <= 0) + || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) + || (orig_top != top_thread(stream, top))) + done++; + } + mn_set_cur(msgmap,mn_raw2m(msgmap, orig_top)); + } + + int + expand_this_thread(state, stream, msgmap, display, special) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + int display; + int special; + { + int collapsed, rv = 1, done = 0; + PINETHRD_S *thrd = NULL, *nthrd; + unsigned long rawno, orig, msgno; + + if(!stream) + return 0; + + orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); + move_top_this_thread(stream, msgmap,orig); + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return rv; + + collapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno); + + if (special && !collapsed){ + collapse_this_thread(state, stream, msgmap, 0, 0); + collapsed = 1; + } + + clear_index_cache_ent(stream, rawno, 0); + + if (collapsed && thrd->next){ + if (thrd->rawno == top_thread(stream, thrd->rawno)) + collapse_or_expand(state, stream, msgmap, mn_get_cur(msgmap), display); + else{ + set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno), MN_COLL, 0); + set_thread_subtree(stream, thrd, msgmap, 0, MN_CHID); + } + } + else{ + if (collapsed && special + && ((F_OFF(F_ENHANCED_THREAD, state) && !thrd->next) + || F_ON(F_ENHANCED_THREAD, state))){ + if (thrd->toploose) + if (thrd->rawno != thrd->toploose) + set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_CHID, 0); + else + set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_COLL, 0); + } + else{ + rv = 0; + if (display) + q_status_message(SM_ORDER, 0, 1, "Thread already expanded"); + } + } + return rv; + } + + void + expand_thread(state, stream, msgmap, display) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + int display; + { + int collapsed, rv = 1, done = 0; + PINETHRD_S *thrd = NULL; + unsigned long orig, orig_top, top; + + if(!stream) + return; + + orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); + top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); + + if(top) + thrd = fetch_thread(stream, top); + + if(!thrd) + return; + + while (!done){ + expand_this_thread(state, stream, msgmap, display, 1); + if (F_OFF(F_ENHANCED_THREAD, state) + || (move_next_this_thread(state, stream, msgmap, 0) <= 0) + || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) + || (orig_top != top_thread(stream, top))) + done++; + } + mn_set_cur(msgmap,mn_raw2m(msgmap, orig_top)); + } + + + void + cmd_undelete_this_thread(state, stream, msgmap) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + { + unsigned long rawno; + int save_kolapsed; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + save_kolapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno); + collapse_this_thread(state, stream, msgmap, 0, 0); + thread_command(state, stream, msgmap, 'u', -FOOTER_ROWS(state), 1); + if (!save_kolapsed) + expand_this_thread(state, stream, msgmap, 0, 0); + } + + void + cmd_undelete_thread(state, stream, msgmap) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + { + PINETHRD_S *thrd = NULL; + unsigned long rawno, top, orig_top; + int done = 0, count; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + move_top_thread(stream, msgmap, rawno); + top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); + if(top) + thrd = fetch_thread(stream, top); + + if(!thrd) + return; + + while (!done){ + cmd_undelete_this_thread(state, stream, msgmap); + if (F_OFF(F_ENHANCED_THREAD, state) + || (move_next_this_thread(state, stream, msgmap, 0) <= 0) + || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) + || (orig_top != top_thread(stream, top))) + done++; + } + mn_set_cur(msgmap,mn_raw2m(msgmap, rawno)); + count = count_thread(state, stream, msgmap, rawno); + q_status_message2(SM_ORDER, 0, 1, "Deletion mark removed from %s message%s", + int2string(count), plural(count)); + } + + void + kolapse_thread(state, stream, msgmap, ch, display) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + char ch; + int display; + { + PINETHRD_S *thrd = NULL; + unsigned long rawno; + int rv = 1, done = 0; + + if(!stream) + return; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return; + + clear_index_cache(stream, 0); + mn_set_cur(msgmap,1); /* go to the first message */ + while (!done){ + if (ch == '[') + collapse_thread(state, stream, msgmap, display); + else + expand_thread(state, stream, msgmap, display); + if ((rv = move_next_thread(state, stream, msgmap, 0)) <= 0) + done++; + } + + if (rv < 0){ + if (display) + q_status_message(SM_ORDER, 0, 1, (ch == '[') + ? "Error while collapsing thread" + : "Error while expanding thread"); + } + else + if(display) + q_status_message(SM_ORDER, 0, 1, (ch == '[') + ? "All threads collapsed. Use \"}\" to expand them" + : "All threads expanded. Use \"{\" to collapse them"); + + mn_set_cur(msgmap,mn_raw2m(msgmap, top_thread(stream,rawno))); + } + + void + cmd_select_thread(state, stream, msgmap) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + { + unsigned long rawno; + int save_kolapsed; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + save_kolapsed = thread_is_kolapsed(state, stream, msgmap, rawno); + collapse_thread(state, stream, msgmap, 0); + thread_command(state, stream, msgmap, ':', -FOOTER_ROWS(state), 1); + if (!save_kolapsed) + expand_thread(state, stream, msgmap, 0); + } + diff -rc alpine-2.00/alpine/mailcmd.h alpine-2.00.I.USE/alpine/mailcmd.h *** alpine-2.00/alpine/mailcmd.h 2008-03-19 12:43:03.000000000 -0700 --- alpine-2.00.I.USE/alpine/mailcmd.h 2011-02-07 20:33:41.000000000 -0800 *************** *** 83,89 **** int ask_mailbox_reopen(struct pine *, int *); void visit_folder(struct pine *, char *, CONTEXT_S *, MAILSTREAM *, unsigned long); int select_by_current(struct pine *, MSGNO_S *, CmdWhere); ! int apply_command(struct pine *, MAILSTREAM *, MSGNO_S *, UCS, int, int); char **choose_list_of_keywords(void); char *choose_a_charset(int); char **choose_list_of_charsets(void); --- 83,89 ---- int ask_mailbox_reopen(struct pine *, int *); void visit_folder(struct pine *, char *, CONTEXT_S *, MAILSTREAM *, unsigned long); int select_by_current(struct pine *, MSGNO_S *, CmdWhere); ! int apply_command(struct pine *, MAILSTREAM *, MSGNO_S *, UCS, int, int, int); char **choose_list_of_keywords(void); char *choose_a_charset(int); char **choose_list_of_charsets(void); *************** *** 101,106 **** int flag_callback(int, long); MPopup *flag_submenu(MESSAGECACHE *); #endif ! #endif /* PINE_MAILCMD_INCLUDED */ --- 101,115 ---- int flag_callback(int, long); MPopup *flag_submenu(MESSAGECACHE *); #endif ! void cmd_delete_thread(struct pine *, MAILSTREAM *, MSGNO_S *); ! void cmd_delete_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *); ! void cmd_undelete_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *); ! void cmd_undelete_thread(struct pine *, MAILSTREAM *, MSGNO_S *); ! void cmd_select_thread(struct pine *, MAILSTREAM *, MSGNO_S *); ! void kolapse_thread(struct pine *, MAILSTREAM *, MSGNO_S *, char, int); ! void collapse_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); ! void expand_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); ! int collapse_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int, int); ! int expand_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int, int); #endif /* PINE_MAILCMD_INCLUDED */ diff -rc alpine-2.00/alpine/mailindx.c alpine-2.00.I.USE/alpine/mailindx.c *** alpine-2.00/alpine/mailindx.c 2008-07-09 22:01:13.000000000 -0700 --- alpine-2.00.I.USE/alpine/mailindx.c 2011-02-07 20:33:44.000000000 -0800 *************** *** 228,233 **** --- 228,235 ---- state->prev_screen = mail_index_screen; state->next_screen = SCREEN_FUN_NULL; + setup_threading_display_style(); + if(THRD_AUTO_VIEW() && sp_viewing_a_thread(state->mail_stream) && state->view_skipped_index *************** *** 239,248 **** --- 241,254 ---- adjust_cur_to_visible(state->mail_stream, state->msgmap); + strcpy(state->screen_name,"index"); + if(THRD_INDX()) thread_index_screen(state); else index_index_screen(state); + + strcpy(state->screen_name,"unknown"); } *************** *** 560,565 **** --- 566,572 ---- /*---------- Scroll line up ----------*/ case MC_CHARUP : + previtem: (void) process_cmd(state, stream, msgmap, MC_PREVITEM, (style == MsgIndex || style == MultiMsgIndex *************** *** 577,582 **** --- 584,590 ---- /*---------- Scroll line down ----------*/ case MC_CHARDOWN : + nextitem: /* * Special Page framing handling here. If we * did something that should scroll-by-a-line, frame *************** *** 794,799 **** --- 802,808 ---- case MC_THRDINDX : + mc_thrdindx: msgmap->top = msgmap->top_after_thrd; if(unview_thread(state, stream, msgmap)){ state->next_screen = mail_index_screen; *************** *** 844,850 **** && mp.col == id.plus_col && style != ThreadIndex){ collapse_or_expand(state, stream, msgmap, ! mn_get_cur(msgmap)); } else if (mp.doubleclick){ if(mp.button == M_BUTTON_LEFT){ --- 853,859 ---- && mp.col == id.plus_col && style != ThreadIndex){ collapse_or_expand(state, stream, msgmap, ! mn_get_cur(msgmap), 1); } else if (mp.doubleclick){ if(mp.button == M_BUTTON_LEFT){ *************** *** 923,928 **** --- 932,939 ---- reset_index_border(); break; + case MC_QUOTA: + cmd_quota(state); /*---------- Redraw ----------*/ case MC_REPAINT : *************** *** 951,959 **** case MC_COLLAPSE : ! thread_command(state, stream, msgmap, ch, -FOOTER_ROWS(state)); break; case MC_DELETE : case MC_UNDELETE : case MC_REPLY : --- 962,1066 ---- case MC_COLLAPSE : ! thread_command(state, stream, msgmap, ch, -FOOTER_ROWS(state), 1); break; + case MC_CTHREAD : + if (SEP_THRDINDX()) + goto mc_thrdindx; + else + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, + "to collapse a thread")) + collapse_thread(state, stream,msgmap, 1); + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; + + case MC_OTHREAD : + if (SEP_THRDINDX()) + goto view_a_thread; + else + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, "to expand a thread")) + expand_thread(state, stream,msgmap, 1); + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; + + case MC_NEXTHREAD: + case MC_PRETHREAD: + if (THRD_INDX()){ + if (cmd == MC_NEXTHREAD) + goto nextitem; + else + goto previtem; + } + else + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, + "to move to other thread")) + move_thread(state, stream, msgmap, + cmd == MC_NEXTHREAD ? 1 : -1); + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; + + case MC_KOLAPSE: + case MC_EXPTHREAD: + if (SEP_THRDINDX()){ + q_status_message(SM_ORDER, 0, 1, + "Command not available in this screen"); + } + else{ + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, + cmd == MC_KOLAPSE ? "to collapse" : "to expand")) + kolapse_thread(state, stream, msgmap, + (cmd == MC_KOLAPSE) ? '[' : ']', 1); + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + } + break; + + case MC_DELTHREAD: + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, "to delete")) + cmd_delete_thread(state, stream, msgmap); + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; + + case MC_UNDTHREAD: + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, "to undelete")) + cmd_undelete_thread(state, stream, msgmap); + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; + + case MC_SELTHREAD: + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, "to undelete")) + cmd_select_thread(state, stream, msgmap); + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; + case MC_DELETE : case MC_UNDELETE : case MC_REPLY : *************** *** 974,986 **** if(rawno) thrd = fetch_thread(stream, rawno); ! collapsed = thrd && thrd->next ! && get_lflag(stream, NULL, rawno, MN_COLL); } if(collapsed){ thread_command(state, stream, msgmap, ! ch, -FOOTER_ROWS(state)); /* increment current */ if(cmd == MC_DELETE){ advance_cur_after_delete(state, stream, msgmap, --- 1081,1092 ---- if(rawno) thrd = fetch_thread(stream, rawno); ! collapsed = thread_is_kolapsed(ps_global, stream, msgmap, rawno); } if(collapsed){ thread_command(state, stream, msgmap, ! ch, -FOOTER_ROWS(state),1); /* increment current */ if(cmd == MC_DELETE){ advance_cur_after_delete(state, stream, msgmap, *************** *** 2672,2677 **** --- 2778,2784 ---- n = mn_raw2m(msgs, thrd->rawno); while(thrd){ + unsigned long branch; if(!msgline_hidden(stream, msgs, n, 0) && (++m % lines_per_page) == 1L) t = n; *************** *** 2740,2750 **** /* n is the end of this thread */ while(thrd){ n = mn_raw2m(msgs, thrd->rawno); ! if(thrd->branch) ! thrd = fetch_thread(stream, thrd->branch); ! else if(thrd->next) ! thrd = fetch_thread(stream, thrd->next); else thrd = NULL; } --- 2847,2858 ---- /* n is the end of this thread */ while(thrd){ + unsigned long next = 0L, branch = 0L; n = mn_raw2m(msgs, thrd->rawno); ! if(branch = get_branch(stream,thrd)) ! thrd = fetch_thread(stream, branch); ! else if(next = get_next(stream,thrd)) ! thrd = fetch_thread(stream, next); else thrd = NULL; } *************** *** 2852,2858 **** void thread_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, ! UCS preloadkeystroke, int q_line) { PINETHRD_S *thrd = NULL; unsigned long rawno, save_branch; --- 2960,2966 ---- void thread_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, ! UCS preloadkeystroke, int q_line, int display) { PINETHRD_S *thrd = NULL; unsigned long rawno, save_branch; *************** *** 2901,2907 **** cancel_busy_cue(0); (void ) apply_command(state, stream, msgmap, preloadkeystroke, flags, ! q_line); /* restore the original flags */ copy_lflags(stream, msgmap, MN_STMP, MN_SLCT); --- 3009,3015 ---- cancel_busy_cue(0); (void ) apply_command(state, stream, msgmap, preloadkeystroke, flags, ! q_line, display); /* restore the original flags */ copy_lflags(stream, msgmap, MN_STMP, MN_SLCT); *************** *** 3395,3401 **** if(set){ sort_folder(ps_global->mail_stream, ps_global->msgmap, order & 0x000000ff, ! (order & 0x00000100) != 0, SRT_VRB); mswin_beginupdate(); update_titlebar_message(); update_titlebar_status(); --- 3503,3509 ---- if(set){ sort_folder(ps_global->mail_stream, ps_global->msgmap, order & 0x000000ff, ! (order & 0x00000100) != 0, SRT_VRB, 1); mswin_beginupdate(); update_titlebar_message(); update_titlebar_status(); diff -rc alpine-2.00/alpine/mailindx.h alpine-2.00.I.USE/alpine/mailindx.h *** alpine-2.00/alpine/mailindx.h 2007-10-15 14:02:56.000000000 -0700 --- alpine-2.00.I.USE/alpine/mailindx.h 2011-02-07 20:33:41.000000000 -0800 *************** *** 102,108 **** void paint_index_hline(MAILSTREAM *, long, ICE_S *); void setup_index_state(int); void warn_other_cmds(void); ! void thread_command(struct pine *, MAILSTREAM *, MSGNO_S *, UCS, int); COLOR_PAIR *apply_rev_color(COLOR_PAIR *, int); #ifdef _WINDOWS int index_sort_callback(int, long); --- 102,108 ---- void paint_index_hline(MAILSTREAM *, long, ICE_S *); void setup_index_state(int); void warn_other_cmds(void); ! void thread_command(struct pine *, MAILSTREAM *, MSGNO_S *, UCS, int, int); COLOR_PAIR *apply_rev_color(COLOR_PAIR *, int); #ifdef _WINDOWS int index_sort_callback(int, long); diff -rc alpine-2.00/alpine/mailpart.c alpine-2.00.I.USE/alpine/mailpart.c *** alpine-2.00/alpine/mailpart.c 2008-06-03 08:54:15.000000000 -0700 --- alpine-2.00.I.USE/alpine/mailpart.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 2145,2150 **** --- 2145,2155 ---- return(1); } + /* ok, we have a filename. Now check if there is a template, and if + * so, rename the file accordingly + */ + filename = mc_template(filename, a->body, a->can_display & MCD_EXT_PROMPT); + if((store = so_get(FileStar, filename, WRITE_ACCESS|OWNER_ONLY)) == NULL){ q_status_message2(SM_ORDER | SM_DING, 3, 5, _("Error \"%s\", Can't write file %s"), *************** *** 3307,3313 **** /* * For consistency, the first question is always "include text?" */ ! if((include_text = reply_text_query(ps_global, 1, &prefix)) >= 0 && reply_news_test(a->body->nested.msg->env, outgoing) > 0 && reply_harvest(ps_global, msgno, a->number, a->body->nested.msg->env, &saved_from, --- 3312,3318 ---- /* * For consistency, the first question is always "include text?" */ ! if((include_text = reply_text_query(ps_global, 1, NULL, &prefix)) >= 0 && reply_news_test(a->body->nested.msg->env, outgoing) > 0 && reply_harvest(ps_global, msgno, a->number, a->body->nested.msg->env, &saved_from, diff -rc alpine-2.00/alpine/mailview.c alpine-2.00.I.USE/alpine/mailview.c *** alpine-2.00/alpine/mailview.c 2008-08-01 17:32:26.000000000 -0700 --- alpine-2.00.I.USE/alpine/mailview.c 2011-02-07 20:33:47.000000000 -0800 *************** *** 44,49 **** --- 44,50 ---- #include "dispfilt.h" #include "busy.h" #include "smime.h" + #include "roleconf.h" #include "../pith/conf.h" #include "../pith/filter.h" #include "../pith/msgno.h" *************** *** 129,134 **** --- 130,136 ---- #define SS_CUR 2 #define SS_FREE 3 + static ACTION_S *role_chosen = NULL; /* * Handle hints. *************** *** 203,209 **** --- 205,219 ---- int pcpine_view_cursor(int, long); #endif + static char *prefix; + #define NO_FLOWED 0x0000 + #define IS_FLOWED 0x0001 + #define DELETEQUO 0x0010 + #define COLORAQUO 0x0100 + #define RAWSTRING 0x1000 + int is_word (char *, int, int); + int is_mailbox(char *, int, int); /*---------------------------------------------------------------------- Format a buffer with the text of the current message for browser *************** *** 242,247 **** --- 252,259 ---- ps->prev_screen = mail_view_screen; ps->force_prefer_plain = ps->force_no_prefer_plain = 0; + strcpy(ps->screen_name, "text"); + if(ps->ttyo->screen_rows - HEADER_ROWS(ps) - FOOTER_ROWS(ps) < 1){ q_status_message(SM_ORDER | SM_DING, 0, 3, _("Screen too small to view message")); *************** *** 294,299 **** --- 306,322 ---- else ps->unseen_in_view = !mc->seen; + prefix = reply_quote_str(env); + /* Make sure the prefix is not only made of spaces, so that we do not + * paint the screen incorrectly + */ + if (prefix && *prefix){ + int i; + for (i = 0; isspace((unsigned char) prefix[i]); i++); + if (i == strlen(prefix)) + fs_give((void **)&prefix); + } + init_handles(&handles); store = so_get(src, NULL, EDIT_ACCESS); *************** *** 478,483 **** --- 501,510 ---- } while(ps->next_screen == SCREEN_FUN_NULL); + strcpy(ps->screen_name, "unknown"); + + if (prefix && *prefix) + fs_give((void **)&prefix); if(we_cancel) cancel_busy_cue(-1); *************** *** 732,741 **** {0, 'a', "A", N_("editApp")}, {-1, 0, NULL, NULL}}; if(handle->type == URL){ launch_opts[4].ch = 'u'; ! if(!(local_h = !struncmp(handle->h.url.path, "x-alpine-", 9)) && (handle->h.url.tool || ((local_h = url_local_handler(handle->h.url.path) != NULL) && (handle->h.url.tool = url_external_handler(handle,1))) --- 759,771 ---- {0, 'a', "A", N_("editApp")}, {-1, 0, NULL, NULL}}; + if (role_chosen) + free_action(&role_chosen); if(handle->type == URL){ launch_opts[4].ch = 'u'; ! if((!(local_h = !struncmp(handle->h.url.path, "x-alpine-", 9)) ! || !(local_h = !struncmp(handle->h.url.path, "x-pine-help", 11))) && (handle->h.url.tool || ((local_h = url_local_handler(handle->h.url.path) != NULL) && (handle->h.url.tool = url_external_handler(handle,1))) *************** *** 833,839 **** if(force || (handle->type == URL ! && !struncmp(handle->h.url.path, "x-alpine-", 9))) return(1); while(1){ --- 863,870 ---- if(force || (handle->type == URL ! && !struncmp(handle->h.url.path, "x-alpine-", 9) ! || !struncmp(handle->h.url.path, "x-pine-help", 11))) return(1); while(1){ *************** *** 844,855 **** * sense if you just say View selected URL ... */ if(handle->type == URL && ! !struncmp(handle->h.url.path, "mailto:", 7)) ! snprintf(prompt, sizeof(prompt), "Compose mail to \"%.*s%s\" ? ", ! MIN(MAX(0,sc - 25), sizeof(prompt)-50), handle->h.url.path+7, ! (strlen(handle->h.url.path+7) > MAX(0,sc-25)) ? "..." : ""); ! else ! snprintf(prompt, sizeof(prompt), "View selected %s %s%.*s%s ? ", (handle->type == URL) ? "URL" : "Attachment", (handle->type == URL) ? "\"" : "", MIN(MAX(0,sc-27), sizeof(prompt)-50), --- 875,916 ---- * sense if you just say View selected URL ... */ if(handle->type == URL && ! !struncmp(handle->h.url.path, "mailto:", 7)){ ! int rolenick = role_chosen ? strlen(role_chosen->nick) : 0; ! int offset = 25 + (role_chosen ? 20 : 0); ! int offset2 = max(0, sc - offset) - strlen(handle->h.url.path+7); ! int offset3 = sc - strlen(handle->h.url.path+7) - rolenick - offset; ! int laddress = min(max(0,sc - offset), sizeof(prompt)-50); ! int lrole = rolenick; ! ! if (offset3 < 0){ ! lrole = rolenick; ! laddress = sc - offset - lrole; ! offset3 = laddress - 20; /* redefine offset3 */ ! if (offset3 < 0){ ! laddress = 20; ! lrole = sc - offset - laddress; ! } ! } ! launch_opts[2].ch = 'r'; ! launch_opts[2].rval = 'r'; ! launch_opts[2].name = "R"; ! launch_opts[2].label = N_("Set Role"); ! snprintf(prompt, sizeof(prompt), "Compose mail to \"%.*s%s\" %s%.*s%s%s? ", ! laddress, handle->h.url.path+7, ! (offset2 < 0 ? "..." : ""), ! (role_chosen ? "using role \"" : ""), ! (role_chosen ? lrole : 0), ! (role_chosen ? role_chosen->nick : ""), ! (role_chosen ? (rolenick > lrole ? "..." : "") : ""), ! (role_chosen ? "\" " : "")); ! } ! else{ ! launch_opts[2].ch = -2; ! launch_opts[2].rval = 0; ! launch_opts[2].name = NULL; ! launch_opts[2].label = NULL; ! snprintf(prompt, sizeof(prompt), "View selected %s %s%.*s%s ? ", (handle->type == URL) ? "URL" : "Attachment", (handle->type == URL) ? "\"" : "", MIN(MAX(0,sc-27), sizeof(prompt)-50), *************** *** 857,862 **** --- 918,924 ---- (handle->type == URL) ? ((strlen(handle->h.url.path) > MAX(0,sc-27)) ? "...\"" : "\"") : ""); + } prompt[sizeof(prompt)-1] = '\0'; *************** *** 865,870 **** --- 927,955 ---- case 'y' : return(1); + case 'r': + { + void (*prev_screen)(struct pine *) = ps_global->prev_screen, + (*redraw)(void) = ps_global->redrawer; + ps_global->redrawer = NULL; + ps_global->next_screen = SCREEN_FUN_NULL; + if(role_select_screen(ps_global, &role_chosen, 1) < 0){ + cmd_cancelled("Compose"); + ps_global->next_screen = prev_screen; + ps_global->redrawer = redraw; + if(ps_global->redrawer) + (*ps_global->redrawer)(); + return 0; + } + ps_global->next_screen = prev_screen; + ps_global->redrawer = redraw; + if(role_chosen) + role_chosen = combine_inherited_role(role_chosen); + if(ps_global->redrawer) + (*ps_global->redrawer)(); + break; + } + case 'u' : strncpy(tmp, handle->h.url.path, sizeof(tmp)-1); tmp[sizeof(tmp)-1] = '\0'; *************** *** 1697,1702 **** --- 1782,1788 ---- {"news:", 5, url_local_news}, {"x-alpine-gripe:", 15, gripe_gripe_to}, {"x-alpine-help:", 14, url_local_helper}, + {"x-pine-help:", 12, url_local_helper}, {"x-alpine-phone-home:", 20, url_local_phone_home}, {"x-alpine-config:", 16, url_local_config}, {"x-alpine-cert:", 14, url_local_certdetails}, *************** *** 1812,1818 **** fs_give((void **) &urlp); rflags = ROLE_COMPOSE; ! if(nonempty_patterns(rflags, &dummy)){ role = set_role_from_msg(ps_global, rflags, -1L, NULL); if(confirm_role(rflags, &role)) role = combine_inherited_role(role); --- 1898,1904 ---- fs_give((void **) &urlp); rflags = ROLE_COMPOSE; ! if(!(role = copy_action(role_chosen)) && nonempty_patterns(rflags, &dummy)){ role = set_role_from_msg(ps_global, rflags, -1L, NULL); if(confirm_role(rflags, &role)) role = combine_inherited_role(role); *************** *** 1888,1893 **** --- 1974,1980 ---- free_redraft_pos(&redraft_pos); free_action(&role); + free_action(&role_chosen); return(rv); } *************** *** 2603,2608 **** --- 2690,2696 ---- } if(ps_global->noticed_change_in_unseen){ + ps_global->noticed_change_in_unseen = 0; /* redraw only once */ cmd = MC_RESIZE; /* causes cursor to be saved in folder_lister */ done = 1; continue; *************** *** 3449,3454 **** --- 3537,3588 ---- print_to_printer(sparms); break; + case MC_NEXTHREAD: + case MC_PRETHREAD: + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, + "to move to other thread")) + move_thread(ps_global, ps_global->mail_stream, ps_global->msgmap, + cmd == MC_NEXTHREAD ? 1 : -1); + done = 1; + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; + + case MC_DELTHREAD: + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, "to delete")) + cmd_delete_thread(ps_global, ps_global->mail_stream, ps_global->msgmap); + done = 1; + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; + + case MC_UNDTHREAD: + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, "to undelete")) + cmd_undelete_thread(ps_global, ps_global->mail_stream, ps_global->msgmap); + done = 1; + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; + + case MC_SELTHREAD: + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, "to undelete")) + cmd_select_thread(ps_global, ps_global->mail_stream, ps_global->msgmap); + done = 1; + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; /* ------- First handle on Line ------ */ case MC_GOTOBOL : diff -rc alpine-2.00/alpine/Makefile.am alpine-2.00.I.USE/alpine/Makefile.am *** alpine-2.00/alpine/Makefile.am 2008-06-03 08:54:15.000000000 -0700 --- alpine-2.00.I.USE/alpine/Makefile.am 2011-02-07 20:33:46.000000000 -0800 *************** *** 46,48 **** --- 46,49 ---- date.c: echo "char datestamp[]="\"`date`\"";" > date.c echo "char hoststamp[]="\"`hostname`\"";" >> date.c + cat ../patchlevel >> date.c diff -rc alpine-2.00/alpine/Makefile.in alpine-2.00.I.USE/alpine/Makefile.in *** alpine-2.00/alpine/Makefile.in 2008-06-03 08:54:15.000000000 -0700 --- alpine-2.00.I.USE/alpine/Makefile.in 2011-02-07 20:33:46.000000000 -0800 *************** *** 724,729 **** --- 724,730 ---- date.c: echo "char datestamp[]="\"`date`\"";" > date.c echo "char hoststamp[]="\"`hostname`\"";" >> date.c + cat ../patchlevel >> date.c # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: diff -rc alpine-2.00/alpine/osdep/debuging.c alpine-2.00.I.USE/alpine/osdep/debuging.c *** alpine-2.00/alpine/osdep/debuging.c 2008-01-04 14:49:15.000000000 -0800 --- alpine-2.00.I.USE/alpine/osdep/debuging.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 104,109 **** --- 104,110 ---- if(debugfile != NULL){ char rev[128]; + extern char plevstamp[]; time_t now = time((time_t *)0); if(ps_global->debug_flush) setvbuf(debugfile, (char *)NULL, _IOLBF, BUFSIZ); *************** *** 126,131 **** --- 127,134 ---- get_alpine_revision_string(rev, sizeof(rev)), ctime(&now))); + dprint((0, "This version uses all.patch:\n%s\n\n", plevstamp)); + dprint((0, "Starting after the reading_pinerc calls, the data in this file should\nbe encoded as UTF-8. Before that it will be in the user's native charset.\n")); if(dfile && (debug > DEFAULT_DEBUG || ps_global->debug_imap > 0 || diff -rc alpine-2.00/alpine/osdep/termin.gen.c alpine-2.00.I.USE/alpine/osdep/termin.gen.c *** alpine-2.00/alpine/osdep/termin.gen.c 2008-03-26 10:27:45.000000000 -0700 --- alpine-2.00.I.USE/alpine/osdep/termin.gen.c 2011-02-07 20:33:42.000000000 -0800 *************** *** 32,37 **** --- 32,39 ---- #include "../../pith/newmail.h" #include "../../pith/conf.h" #include "../../pith/busy.h" + #include "../../pith/list.h" + #include "../../pith/rules.h" #include "../../pico/estruct.h" #include "../../pico/pico.h" *************** *** 66,77 **** int pcpine_oe_cursor(int, long); #endif /* * Generic tty input routines */ ! /*---------------------------------------------------------------------- Read a character from keyboard with timeout Input: none --- 68,97 ---- int pcpine_oe_cursor(int, long); #endif + void + fake_config_screen(tt) + struct ttyo **tt; + { + struct ttyo *ttyo; + + ttyo = (struct ttyo *)fs_get(sizeof (struct ttyo)); + + ttyo->header_rows = 2; + ttyo->footer_rows = 3; + ttyo->screen_rows = 24; + ttyo->screen_cols = 80; + + *tt = ttyo; + + } + /* * Generic tty input routines */ ! void process_init_cmds(struct pine *, char **); ! void queue_init_errors(struct pine *); /*---------------------------------------------------------------------- Read a character from keyboard with timeout Input: none *************** *** 113,118 **** --- 133,173 ---- *utf8str = NULL; ucs = read_char(tm); + if(!ps_global->initial_cmds){ + RULE_RESULT *rule; + char **list = NULL, *error = NULL; + int commas = 0, k; /* From args.c */ + + ps_global->pressed_key = cpystr(pretty_command(ucs)); + rule = (RULE_RESULT *)get_result_rule(V_KEY_RULES, FOR_KEY, NULL); + if(ps_global->pressed_key) + fs_give((void **)&ps_global->pressed_key); + if (rule){ + for(k = 0; rule->result[k]; k++) + if(rule->result[k] == ',') commas++; + list = parse_list(rule->result, commas+1, 0, &error); + if(error) + sprintf(tmp_20k_buf, "Error in parsing command list: %s, %s", + rule->result, error); + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + if(error){ + q_status_message(SM_ORDER | SM_DING, 0, 2, tmp_20k_buf); + return (NO_OP_COMMAND); + } + process_init_cmds(ps_global, list); + if(ps_global->init_errs){ + queue_init_errors(ps_global); + return (NO_OP_COMMAND); + } + ucs = read_char(tm); + ps_global->in_init_seq = 1; /* no output please */ + for(k = 0; k < commas; k++) + if(list[k]) fs_give((void **)&list[k]); + if (list) fs_give((void **)list); + } + } if(ucs != NO_OP_COMMAND && ucs != NO_OP_IDLE && ucs != KEY_RESIZE) zero_new_mail_count(); *************** *** 306,312 **** (escape_list && escape_list[0].ch != -1 && escape_list[0].label) ? escape_list[0].label: "")); ! if(!ps_global->ttyo) return(pre_screen_config_opt_enter(utf8string, utf8string_size, utf8prompt, escape_list, help, flags)); --- 361,367 ---- (escape_list && escape_list[0].ch != -1 && escape_list[0].label) ? escape_list[0].label: "")); ! if(!ps_global->ttyo || ps_global->send_immediately) return(pre_screen_config_opt_enter(utf8string, utf8string_size, utf8prompt, escape_list, help, flags)); *************** *** 1153,1162 **** } } } ! if(!*ps_global->initial_cmds && ps_global->free_initial_cmds){ fs_give((void **) &ps_global->free_initial_cmds); ps_global->initial_cmds = NULL; } return(ret); --- 1208,1218 ---- } } } ! ps_global->initial_cmds_offset++; if(!*ps_global->initial_cmds && ps_global->free_initial_cmds){ fs_give((void **) &ps_global->free_initial_cmds); ps_global->initial_cmds = NULL; + firsttime = (char) 1; } return(ret); diff -rc alpine-2.00/alpine/osdep/termin.gen.h alpine-2.00.I.USE/alpine/osdep/termin.gen.h *** alpine-2.00/alpine/osdep/termin.gen.h 2007-12-18 10:29:24.000000000 -0800 --- alpine-2.00.I.USE/alpine/osdep/termin.gen.h 2011-02-07 20:33:42.000000000 -0800 *************** *** 32,37 **** --- 32,38 ---- int key_recorder(int); int key_playback(int); int recent_keystroke(int *, char *, size_t); + void fake_config_screen(struct ttyo **); int init_tty_driver(struct pine *); void end_tty_driver(struct pine *); int PineRaw(int); diff -rc alpine-2.00/alpine/osdep/termin.unx.c alpine-2.00.I.USE/alpine/osdep/termin.unx.c *** alpine-2.00/alpine/osdep/termin.unx.c 2008-03-26 10:27:45.000000000 -0700 --- alpine-2.00.I.USE/alpine/osdep/termin.unx.c 2011-02-07 20:33:42.000000000 -0800 *************** *** 110,115 **** --- 110,117 ---- int init_tty_driver(struct pine *ps) { + if(ps->send_immediately) + return 0; #ifdef MOUSE if(F_ON(F_ENABLE_MOUSE, ps_global)) init_mouse(); *************** *** 676,681 **** --- 678,686 ---- void init_keyboard(int use_fkeys) { + if (ps_global->send_immediately) + return; + if(use_fkeys && (!strucmp(term_name,"vt102") || !strucmp(term_name,"vt100"))) printf("\033\133\071\071\150"); *************** *** 693,698 **** --- 698,706 ---- void end_keyboard(int use_fkeys) { + if(ps_global->send_immediately) + return; + if(use_fkeys && (!strcmp(term_name, "vt102") || !strcmp(term_name, "vt100"))){ printf("\033\133\071\071\154"); diff -rc alpine-2.00/alpine/osdep/termout.unx.c alpine-2.00.I.USE/alpine/osdep/termout.unx.c *** alpine-2.00/alpine/osdep/termout.unx.c 2008-03-04 12:41:47.000000000 -0800 --- alpine-2.00.I.USE/alpine/osdep/termout.unx.c 2011-02-07 20:33:42.000000000 -0800 *************** *** 205,210 **** --- 205,213 ---- void init_screen(void) { + if(ps_global->send_immediately) + return; + if(_termcap_init) /* init using termcap's rule */ tputs(_termcap_init, 1, outchar); *************** *** 312,317 **** --- 315,323 ---- { int footer_rows_was_one = 0; + if(ps_global->send_immediately) + return; + if(!panicking()){ dprint((9, "end_screen called\n")); *************** *** 366,372 **** _line = 0; /* clear leaves us at top... */ _col = 0; ! if(ps_global->in_init_seq) return; mark_status_unknown(); --- 372,378 ---- _line = 0; /* clear leaves us at top... */ _col = 0; ! if(ps_global->in_init_seq || ps_global->send_immediately) return; mark_status_unknown(); diff -rc alpine-2.00/alpine/radio.c alpine-2.00.I.USE/alpine/radio.c *** alpine-2.00/alpine/radio.c 2007-08-16 15:25:10.000000000 -0700 --- alpine-2.00.I.USE/alpine/radio.c 2011-02-07 20:33:42.000000000 -0800 *************** *** 121,127 **** int rv, width; size_t len; ! if(!ps_global->ttyo) return(pre_screen_config_want_to(question, dflt, on_ctrl_C)); #ifdef _WINDOWS if (mswin_usedialog ()) { --- 121,127 ---- int rv, width; size_t len; ! if(!ps_global->ttyo || ps_global->send_immediately) return(pre_screen_config_want_to(question, dflt, on_ctrl_C)); #ifdef _WINDOWS if (mswin_usedialog ()) { diff -rc alpine-2.00/alpine/reply.c alpine-2.00.I.USE/alpine/reply.c *** alpine-2.00/alpine/reply.c 2008-06-03 08:54:15.000000000 -0700 --- alpine-2.00.I.USE/alpine/reply.c 2011-02-07 20:33:47.000000000 -0800 *************** *** 61,67 **** #include "../pith/tempfile.h" #include "../pith/busy.h" #include "../pith/ablookup.h" ! /* * Internal Prototypes --- 61,68 ---- #include "../pith/tempfile.h" #include "../pith/busy.h" #include "../pith/ablookup.h" ! #include "../pith/copyaddr.h" ! #include "../pith/rules.h" /* * Internal Prototypes *************** *** 108,118 **** long msgno, j, totalm, rflags, *seq = NULL; int i, include_text = 0, times = -1, warned = 0, rv = 0, flags = RSF_QUERY_REPLY_ALL, reply_raw_body = 0; ! int rolemsg = 0, copytomsg = 0; gf_io_t pc; PAT_STATE dummy; REDRAFT_POS_S *redraft_pos = NULL; ACTION_S *role = NULL, *nrole; #if defined(DOS) && !defined(_WINDOWS) char *reserve; #endif --- 109,120 ---- long msgno, j, totalm, rflags, *seq = NULL; int i, include_text = 0, times = -1, warned = 0, rv = 0, flags = RSF_QUERY_REPLY_ALL, reply_raw_body = 0; ! int rolemsg = 0, copytomsg = 0, do_role_early = 0; gf_io_t pc; PAT_STATE dummy; REDRAFT_POS_S *redraft_pos = NULL; ACTION_S *role = NULL, *nrole; + RULE_RESULT *rule; #if defined(DOS) && !defined(_WINDOWS) char *reserve; #endif *************** *** 138,143 **** --- 140,208 ---- && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global)) reply_raw_body = 1; + /* Setup possible role */ + if(role_arg) + role = copy_action(role_arg); + + if(!role && F_ON(F_ENABLE_EDIT_REPLY_INDENT, pine_state)){ + for(msgno = mn_first_cur(pine_state->msgmap); + msgno > 0L; msgno = mn_next_cur(pine_state->msgmap)){ + + env = pine_mail_fetchstructure(pine_state->mail_stream, + mn_m2raw(pine_state->msgmap, msgno), + NULL); + if(!env) { + q_status_message1(SM_ORDER,3,4, + _("Error fetching message %s. Can't reply to it."), + long2string(msgno)); + goto done_early; + } + + if(rule = get_result_rule(V_REPLY_INDENT_RULES, FOR_COMPOSE , env)){ + RULELIST *list = get_rulelist_from_code(V_REPLY_INDENT_RULES, + ps_global->rule_list); + RULE_S *prule = get_rule(list, rule->number); + if(condition_contains_token(prule->condition, ROLE_TOKEN)) + do_role_early++; + if(rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + } + } + + if(do_role_early){ + rflags = ROLE_REPLY; + if(!role && nonempty_patterns(rflags, &dummy)){ + /* setup default role */ + nrole = NULL; + j = mn_first_cur(pine_state->msgmap); + do { + role = nrole; + nrole = set_role_from_msg(pine_state, rflags, + mn_m2raw(pine_state->msgmap, j), + NULL); + } while(nrole && (!role || nrole == role) + && (j=mn_next_cur(pine_state->msgmap)) > 0L); + + if(!role || nrole == role) + role = nrole; + else + role = NULL; + + if(confirm_role(rflags, &role)) + role = combine_inherited_role(role); + else{ /* cancel reply */ + role = NULL; + cmd_cancelled("Reply"); + goto done_early; + } + } + } + + if (role) + ps_global->role = cpystr(role->nick); /* remember the role */ + /* * We may have to loop through first to figure out what default * reply-indent-string to offer... *************** *** 229,235 **** if(!times){ /* only first time */ char *p = cpystr(prefix); ! if((include_text=reply_text_query(pine_state,totalm,&prefix)) < 0) goto done_early; /* edited prefix? */ --- 294,300 ---- if(!times){ /* only first time */ char *p = cpystr(prefix); ! if((include_text=reply_text_query(pine_state,totalm,env,&prefix)) < 0) goto done_early; /* edited prefix? */ *************** *** 285,292 **** outgoing->subject = cpystr("Re: several messages"); } } ! else ! outgoing->subject = reply_subject(env->subject, NULL, 0); } /* fill reply header */ --- 350,367 ---- outgoing->subject = cpystr("Re: several messages"); } } ! else{ ! RULE_RESULT *rule; ! rule = get_result_rule(V_RESUB_RULES,FOR_RESUB|FOR_TRIM , env); ! if (rule){ ! outgoing->subject = reply_subject(rule->result, NULL, 0); ! if (rule->result) ! fs_give((void **)&rule->result); ! fs_give((void **)&rule); ! } ! else ! outgoing->subject = reply_subject(env->subject, NULL, 0); ! } } /* fill reply header */ *************** *** 305,315 **** if(sp_expunge_count(pine_state->mail_stream)) /* cur msg expunged */ goto done_early; ! /* Setup possible role */ ! if(role_arg) ! role = copy_action(role_arg); ! if(!role){ rflags = ROLE_REPLY; if(nonempty_patterns(rflags, &dummy)){ /* setup default role */ --- 380,392 ---- if(sp_expunge_count(pine_state->mail_stream)) /* cur msg expunged */ goto done_early; ! if (ps_global->reply.role_chosen){ ! if(role_arg) ! free_action(&role); ! role = ps_global->reply.role_chosen; ! } ! if(!role && !do_role_early){ rflags = ROLE_REPLY; if(nonempty_patterns(rflags, &dummy)){ /* setup default role */ *************** *** 545,553 **** goto done_early; } ! if(orig_body == NULL || orig_body->type == TYPETEXT || reply_raw_body) { reply_delimiter(env, role, pc); ! if(F_ON(F_INCLUDE_HEADER, pine_state)) reply_forward_header(pine_state->mail_stream, mn_m2raw(pine_state->msgmap,msgno), NULL, env, pc, prefix); --- 622,631 ---- goto done_early; } ! if(orig_body == NULL || orig_body->type == TYPETEXT || reply_raw_body ! || !ps_global->reply.attach) { reply_delimiter(env, role, pc); ! if(ps_global->reply.inchdr) reply_forward_header(pine_state->mail_stream, mn_m2raw(pine_state->msgmap,msgno), NULL, env, pc, prefix); *************** *** 566,572 **** && orig_body->nested.part->body.type == TYPETEXT) { /*---- First part of the message is text -----*/ reply_delimiter(env, role, pc); ! if(F_ON(F_INCLUDE_HEADER, pine_state)) reply_forward_header(pine_state->mail_stream, mn_m2raw(pine_state->msgmap, msgno), --- 644,650 ---- && orig_body->nested.part->body.type == TYPETEXT) { /*---- First part of the message is text -----*/ reply_delimiter(env, role, pc); ! if(ps_global->reply.inchdr) reply_forward_header(pine_state->mail_stream, mn_m2raw(pine_state->msgmap, msgno), *************** *** 720,725 **** --- 798,806 ---- if(prefix) fs_give((void **)&prefix); + if (ps_global->role) + fs_give((void **)&ps_global->role); + if(fcc) fs_give((void **) &fcc); *************** *** 875,881 **** prompt[sizeof(prompt)-1] = '\0'; ! cmd = radio_buttons(prompt, -FOOTER_ROWS(ps_global), ekey, 'y', 'x', help, RB_NORM); switch(cmd){ --- 956,963 ---- prompt[sizeof(prompt)-1] = '\0'; ! cmd = ps_global->send_immediately ? 'n' : ! radio_buttons(prompt, -FOOTER_ROWS(ps_global), ekey, 'y', 'x', help, RB_NORM); switch(cmd){ *************** *** 930,937 **** int reply_to_all_query(int *flagp) { ! switch(want_to("Reply to all recipients", ! 'n', 'x', NO_HELP, WT_SEQ_SENSITIVE)){ case 'x' : return(-1); --- 1012,1047 ---- int reply_to_all_query(int *flagp) { ! char prompt[80]; ! ESCKEY_S ekey[4]; ! char cmd; ! ! ekey[0].name = "Y"; ! ekey[0].ch = 'y'; ! ekey[0].rval = 'y'; ! ekey[0].label = N_("Yes"); ! ekey[1].name = "N"; ! ekey[1].ch = 'n'; ! ekey[1].rval = 'n'; ! ekey[1].label = N_("No"); ! ekey[2].name = "P"; ! ekey[2].ch = 'p'; ! ekey[2].rval = 'p'; ! ekey[3].ch = -1; ! ! ps_global->preserve = F_ON(F_PRESERVE_ORIGINAL_FIELD, ps_global); ! ! ! loop: ! ekey[2].label = ps_global->preserve ? N_("Not Preserve") : N_("Preserve"); ! snprintf(prompt, sizeof(prompt), _("Reply to all recipients%s"), ! ps_global->preserve ? _(" (preserving fields)? ") : "? "); ! ! prompt[sizeof(prompt)-1] = '\0'; ! ! switch(cmd = radio_buttons(prompt, -FOOTER_ROWS(ps_global), ekey, ! 'n', 'x', h_preserve_field, RB_NORM)){ ! case 'x' : return(-1); *************** *** 942,947 **** --- 1052,1062 ---- case 'n' : /* clear reply-all bit */ (*flagp) &= ~RSF_FORCE_REPLY_ALL; break; + + case 'p' : + ps_global->preserve = !ps_global->preserve; + goto loop; /* ugly, but saves me a variable */ + break; } return(0); *************** *** 969,1016 **** * 0 if we're NOT to include the text * -1 on cancel or error */ int ! reply_text_query(struct pine *ps, long int many, char **prefix) { int ret, edited = 0; ! static ESCKEY_S rtq_opts[] = { ! {'y', 'y', "Y", N_("Yes")}, ! {'n', 'n', "N", N_("No")}, ! {-1, 0, NULL, NULL}, /* may be overridden below */ ! {-1, 0, NULL, NULL} ! }; if(F_ON(F_AUTO_INCLUDE_IN_REPLY, ps) ! && F_OFF(F_ENABLE_EDIT_REPLY_INDENT, ps)) return(1); ! while(1){ ! if(many > 1L) ! /* TRANSLATORS: The final three %s's can probably be safely ignored */ ! snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Include %s original messages in Reply%s%s%s? "), ! comatose(many), ! F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? " (using \"" : "", ! F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? *prefix : "", ! F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? "\")" : ""); ! else ! snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Include original message in Reply%s%s%s? "), F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? " (using \"" : "", F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? *prefix : "", F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? "\")" : ""); ! if(F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps)){ ! rtq_opts[2].ch = ctrl('R'); ! rtq_opts[2].rval = 'r'; ! rtq_opts[2].name = "^R"; ! rtq_opts[2].label = N_("Edit Indent String"); ! } ! else ! rtq_opts[2].ch = -1; switch(ret = radio_buttons(tmp_20k_buf, ps->ttyo->screen_rows > 4 ? -FOOTER_ROWS(ps_global) : -1, ! rtq_opts, (edited || F_ON(F_AUTO_INCLUDE_IN_REPLY, ps)) ? 'y' : 'n', 'x', NO_HELP, RB_SEQ_SENSITIVE)){ --- 1084,1195 ---- * 0 if we're NOT to include the text * -1 on cancel or error */ + #define MAX_REPLY_OPTIONS 8 int ! reply_text_query(struct pine *ps, long int many, ENVELOPE *env, char **prefix) { int ret, edited = 0; ! static ESCKEY_S compose_style[MAX_REPLY_OPTIONS]; ! int ekey_num; ! int orig_sf; ! ! orig_sf = *prefix && **prefix ? (F_OFF(F_QUELL_FLOWED_TEXT, ps) ! && F_OFF(F_STRIP_WS_BEFORE_SEND, ps) ! && (strcmp(*prefix, "> ") == 0 ! || strcmp(*prefix, ">") == 0)) : 0; ! ! ps_global->reply.no_send_flowed = !orig_sf; ! ps_global->reply.role_chosen = NULL; ! ps_global->reply.strip = ps->full_header == 0 ! && (F_ON(F_ENABLE_STRIP_SIGDASHES, ps) ! || F_ON(F_ENABLE_SIGDASHES, ps)); ! ps_global->reply.attach = F_ON(F_ATTACHMENTS_IN_REPLY, ps); ! ps_global->reply.inchdr = F_ON(F_INCLUDE_HEADER, ps); if(F_ON(F_AUTO_INCLUDE_IN_REPLY, ps) ! && F_OFF(F_ENABLE_EDIT_REPLY_INDENT, ps) && F_OFF(F_ALT_REPLY_MENU,ps)) return(1); ! while(1){ ! /* TRANSLATORS: The final five %s's can probably be safely ignored */ ! snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Include %s original messages in Reply%s%s%s%s%s%s%s? "), ! (many > 1L) ? comatose(many) : "", ! (many > 1L) ? " " : "", ! (many > 1L) ? "s" : "", F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? " (using \"" : "", F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? *prefix : "", + ps_global->reply.role_chosen ? "\" and role \"" : "", + ps_global->reply.role_chosen ? ps_global->reply.role_chosen->nick : "", F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? "\")" : ""); ! ekey_num = 0; ! compose_style[ekey_num].ch = 'y'; ! compose_style[ekey_num].rval = 'y'; ! compose_style[ekey_num].name = "Y"; ! compose_style[ekey_num++].label = N_("Yes"); ! ! compose_style[ekey_num].ch = 'n'; ! compose_style[ekey_num].rval = 'n'; ! compose_style[ekey_num].name = "N"; ! compose_style[ekey_num++].label = N_("No"); ! ! if (F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps)){ ! compose_style[ekey_num].ch = ctrl('R'); ! compose_style[ekey_num].rval = 'r'; ! compose_style[ekey_num].name = "^R"; ! compose_style[ekey_num++].label = N_("Edit Indent String"); ! } ! ! /***** Alternate Reply Menu ********/ ! ! if (F_ON(F_ALT_REPLY_MENU, ps)){ ! unsigned which_help; ! ! if (F_ON(F_ENABLE_STRIP_SIGDASHES, ps) || ! F_ON(F_ENABLE_SIGDASHES, ps)){ ! compose_style[ekey_num].ch = 's'; ! compose_style[ekey_num].rval = 'S'; ! compose_style[ekey_num].name = "S"; ! compose_style[ekey_num++].label = ps_global->reply.strip ! ? N_("No Strip"): N_("Strip Sig"); ! } ! ! compose_style[ekey_num].ch = 'r'; ! compose_style[ekey_num].rval = 'R'; ! compose_style[ekey_num].name = "R"; ! compose_style[ekey_num++].label = N_("Set Role"); ! ! if(orig_sf){ ! compose_style[ekey_num].ch = 'f'; ! compose_style[ekey_num].rval = 'F'; ! compose_style[ekey_num].name = "F"; ! compose_style[ekey_num++].label = ps_global->reply.no_send_flowed ! ? N_("Quell Flow") : N_("Send Flowd"); ! } ! ! compose_style[ekey_num].ch = 'a'; ! compose_style[ekey_num].rval = 'A'; ! compose_style[ekey_num].name = "A"; ! compose_style[ekey_num++].label = ps_global->reply.attach ! ? N_("No Attach"): N_("Inc Attac"); ! ! compose_style[ekey_num].ch = 'h'; ! compose_style[ekey_num].rval = 'H'; ! compose_style[ekey_num].name = "H"; ! compose_style[ekey_num++].label = ps_global->reply.inchdr ! ? N_("No Header") : N_("Inc Head"); ! ! } ! compose_style[ekey_num].ch = -1; ! compose_style[ekey_num].name = NULL; ! compose_style[ekey_num].label = NULL; ! ! /***** End Alt Reply Menu *********/ switch(ret = radio_buttons(tmp_20k_buf, ps->ttyo->screen_rows > 4 ? -FOOTER_ROWS(ps_global) : -1, ! compose_style, (edited || F_ON(F_AUTO_INCLUDE_IN_REPLY, ps)) ? 'y' : 'n', 'x', NO_HELP, RB_SEQ_SENSITIVE)){ *************** *** 1018,1023 **** --- 1197,1242 ---- cmd_cancelled("Reply"); return(-1); + case 'F': + ps_global->reply.no_send_flowed = (ps_global->reply.no_send_flowed + 1) % 2; + break; + + case 'S': + ps_global->reply.strip = (ps_global->reply.strip + 1) % 2; + break; + + case 'A': + ps_global->reply.attach = (ps_global->reply.attach + 1) % 2; + break; + + case 'H': + ps_global->reply.inchdr = (ps_global->reply.inchdr + 1) % 2; + break; + + + case 'R': + { + void (*prev_screen)(struct pine *) = ps->prev_screen, + (*redraw)(void) = ps->redrawer; + ps->redrawer = NULL; + ps->next_screen = SCREEN_FUN_NULL; + if(role_select_screen(ps, &ps_global->reply.role_chosen, 1) < 0){ + cmd_cancelled("Reply"); + ps->next_screen = prev_screen; + ps->redrawer = redraw; + if (ps->redrawer) + (*ps->redrawer)(); + continue; + } + ps->next_screen = prev_screen; + ps->redrawer = redraw; + if(ps_global->reply.role_chosen) + ps_global->reply.role_chosen = combine_inherited_role(ps_global->reply.role_chosen); + } + if (ps->redrawer) + (*ps->redrawer)(); + break; + case 'r': if(prefix && *prefix){ int done = 0; *************** *** 1041,1046 **** --- 1260,1271 ---- if(flags & OE_USER_MODIFIED){ fs_give((void **)prefix); *prefix = removing_quotes(cpystr(buf)); + orig_sf = *prefix && **prefix ? + (F_OFF(F_QUELL_FLOWED_TEXT, ps) + && F_OFF(F_STRIP_WS_BEFORE_SEND, ps) + && (strcmp(*prefix, "> ") == 0 + || strcmp(*prefix, ">") == 0)) : 0; + ps_global->reply.no_send_flowed = !orig_sf; edited = 1; } *************** *** 1164,1172 **** } else if(!outgoing->newsgroups) outgoing->newsgroups = cpystr(env->newsgroups); - if(!IS_NEWS(ps_global->mail_stream)) - q_status_message(SM_ORDER, 2, 3, - _("Replying to message that MAY or MAY NOT have been posted to newsgroup")); } return(ret); --- 1389,1394 ---- *************** *** 1440,1448 **** } } ! if(role) q_status_message1(SM_ORDER, 3, 4, _("Forwarding using role \"%s\""), role->nick); if(role && role->template){ char *filtered; --- 1662,1675 ---- } } ! if (ps_global->role) ! fs_give((void **)&ps_global->role); ! ! if(role){ q_status_message1(SM_ORDER, 3, 4, _("Forwarding using role \"%s\""), role->nick); + ps_global->role = cpystr(role->nick); + } if(role && role->template){ char *filtered; *************** *** 1674,1679 **** --- 1901,1907 ---- #if defined(DOS) && !defined(_WINDOWS) free((void *)reserve); #endif + outgoing->sparep = env && env->from ? copyaddr(env->from) : NULL; pine_send(outgoing, &body, "FORWARD MESSAGE", role, NULL, &reply, redraft_pos, NULL, NULL, 0); *************** *** 2426,2431 **** --- 2654,2661 ---- { int rv; + if(ps_global->send_immediately) + return 0; clear_cursor_pos(); /* can't know where cursor is */ mark_status_dirty(); /* don't count on cached text */ fix_windsize(ps_global); *************** *** 2485,2490 **** --- 2715,2721 ---- resize_for_pico(void) { fix_windsize(ps_global); + ps_global->resize_for_pico = 1; } diff -rc alpine-2.00/alpine/reply.h alpine-2.00.I.USE/alpine/reply.h *** alpine-2.00/alpine/reply.h 2006-11-15 20:08:15.000000000 -0800 --- alpine-2.00.I.USE/alpine/reply.h 2011-02-07 20:33:42.000000000 -0800 *************** *** 28,34 **** int confirm_role(long, ACTION_S **); int reply_to_all_query(int *); int reply_using_replyto_query(void); ! int reply_text_query(struct pine *, long, char **); int reply_news_test(ENVELOPE *, ENVELOPE *); char *get_signature_file(char *, int, int, int); int forward(struct pine *, ACTION_S *); --- 28,34 ---- int confirm_role(long, ACTION_S **); int reply_to_all_query(int *); int reply_using_replyto_query(void); ! int reply_text_query(struct pine *, long, ENVELOPE *, char **); int reply_news_test(ENVELOPE *, ENVELOPE *); char *get_signature_file(char *, int, int, int); int forward(struct pine *, ACTION_S *); diff -rc alpine-2.00/alpine/roleconf.c alpine-2.00.I.USE/alpine/roleconf.c *** alpine-2.00/alpine/roleconf.c 2008-02-27 17:04:46.000000000 -0800 --- alpine-2.00.I.USE/alpine/roleconf.c 2011-02-07 20:33:42.000000000 -0800 *************** *** 139,146 **** --- 139,151 ---- if(!(nonempty_patterns(rflags, &pstate) && first_pattern(&pstate))){ + if (!ps->send_immediately) q_status_message(SM_ORDER, 0, 3, _("No roles available. Use Setup/Rules to add roles.")); + else{ + printf(_("No roles available. Use Setup/Rules to add roles.")); + exit(-1); + } return(ret); } *************** *** 4477,4487 **** ctmp->tool = role_sort_tool; ctmp->valoffset = rindent; ctmp->flags |= CF_NOSELECT; ! ctmp->value = cpystr(set_choose); \ pval = PVAL(&sort_act_var, ew); if(pval) ! decode_sort(pval, &def_sort, &def_sort_rev); /* allow user to set their default sort order */ new_confline(&ctmp)->var = &sort_act_var; --- 4482,4492 ---- ctmp->tool = role_sort_tool; ctmp->valoffset = rindent; ctmp->flags |= CF_NOSELECT; ! ctmp->value = cpystr(set_choose); pval = PVAL(&sort_act_var, ew); if(pval) ! decode_sort(pval, &def_sort, &def_sort_rev, 0); /* allow user to set their default sort order */ new_confline(&ctmp)->var = &sort_act_var; *************** *** 4491,4497 **** ctmp->tool = role_sort_tool; ctmp->valoffset = rindent; ctmp->varmem = -1; ! ctmp->value = generalized_sort_pretty_value(ps, ctmp, 0); for(j = 0; j < 2; j++){ for(i = 0; ps->sort_types[i] != EndofList; i++){ --- 4496,4502 ---- ctmp->tool = role_sort_tool; ctmp->valoffset = rindent; ctmp->varmem = -1; ! ctmp->value = generalized_sort_pretty_value(ps, ctmp, 0, 0); for(j = 0; j < 2; j++){ for(i = 0; ps->sort_types[i] != EndofList; i++){ *************** *** 4503,4509 **** ctmp->valoffset = rindent; ctmp->varmem = i + (j * EndofList); ctmp->value = generalized_sort_pretty_value(ps, ctmp, ! 0); } } --- 4508,4514 ---- ctmp->valoffset = rindent; ctmp->varmem = i + (j * EndofList); ctmp->value = generalized_sort_pretty_value(ps, ctmp, ! 0, 0); } } *************** *** 5436,5442 **** (*result)->patgrp->stat_boy = PAT_STAT_EITHER; if(sort_act){ ! decode_sort(sort_act, &def_sort, &def_sort_rev); (*result)->action->sort_is_set = 1; (*result)->action->sortorder = def_sort; (*result)->action->revsort = (def_sort_rev ? 1 : 0); --- 5441,5447 ---- (*result)->patgrp->stat_boy = PAT_STAT_EITHER; if(sort_act){ ! decode_sort(sort_act, &def_sort, &def_sort_rev, 0); (*result)->action->sort_is_set = 1; (*result)->action->sortorder = def_sort; (*result)->action->revsort = (def_sort_rev ? 1 : 0); *************** *** 7705,7710 **** --- 7710,7720 ---- if(apval) *apval = (role && role->nick) ? cpystr(role->nick) : NULL; + if (ps_global->role) + fs_give((void **)&ps_global->role); + if (role && role->nick) + ps_global->role = cpystr(role->nick); + if((*cl)->value) fs_give((void **)&((*cl)->value)); diff -rc alpine-2.00/alpine/send.c alpine-2.00.I.USE/alpine/send.c *** alpine-2.00/alpine/send.c 2008-06-30 15:03:35.000000000 -0700 --- alpine-2.00.I.USE/alpine/send.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 62,68 **** #include "../pith/mimetype.h" #include "../pith/send.h" #include "../pith/smime.h" ! typedef struct body_particulars { unsigned short type, encoding, had_csp; --- 62,68 ---- #include "../pith/mimetype.h" #include "../pith/send.h" #include "../pith/smime.h" ! #include "../pith/rules.h" typedef struct body_particulars { unsigned short type, encoding, had_csp; *************** *** 98,103 **** --- 98,104 ---- void free_body_particulars(BODY_PARTICULARS_S *); long message_format_for_pico(long, int (*)(int)); int send_exit_for_pico(struct headerentry *, void (*)(void), int, char **); + void new_thread_on_blank_subject(void); char *choose_a_priority(char *); int dont_flow_this_time(void); int mime_type_for_pico(char *); *************** *** 237,242 **** --- 238,248 ---- role->nick = cpystr("Default Role"); } + if (ps_global->role) + fs_give((void **)&ps_global->role); + + ps_global->role = cpystr(role->nick); + pine_state->redrawer = NULL; compose_mail(NULL, NULL, role, NULL, NULL); free_action(&role); *************** *** 446,453 **** ps_global->next_screen = prev_screen; ps_global->redrawer = redraw; ! if(role) role = combine_inherited_role(role); } break; --- 452,463 ---- ps_global->next_screen = prev_screen; ps_global->redrawer = redraw; ! if (ps_global->role) ! fs_give((void **)&ps_global->role); ! if(role){ role = combine_inherited_role(role); + ps_global->role = cpystr(role->nick); + } } break; *************** *** 612,617 **** --- 622,628 ---- if(given_to) rfc822_parse_adrlist(&outgoing->to, given_to, ps_global->maildomain); + outgoing->subject = cpystr(ps_global->subject); outgoing->message_id = generate_message_id(); /* *************** *** 642,650 **** } } ! if(role) q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""), role->nick); /* * The type of storage object allocated below is vitally --- 653,666 ---- } } ! if (ps_global->role) ! fs_give((void **)&ps_global->role); ! ! if(role){ q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""), role->nick); + ps_global->role = cpystr(role->nick); + } /* * The type of storage object allocated below is vitally *************** *** 910,916 **** 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"From : ", "From", h_composer_from, 10, 0, NULL, build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete, ! 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK}, {"Reply-To: ", "Reply To", h_composer_reply_to, 10, 0, NULL, build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK}, --- 926,932 ---- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"From : ", "From", h_composer_from, 10, 0, NULL, build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete, ! 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK}, {"Reply-To: ", "Reply To", h_composer_reply_to, 10, 0, NULL, build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK}, *************** *** 1780,1785 **** --- 1796,1804 ---- pbf = &pbuf1; standard_picobuf_setup(pbf); + pbf->auto_cmds = ps_global->initial_cmds_backup + + ps_global->initial_cmds_offset; + /* * Cancel any pending initial commands since pico uses a different * input routine. If we didn't cancel them, they would happen after *************** *** 1816,1821 **** --- 1835,1842 ---- pbf->mimetype = mime_type_for_pico; pbf->exittest = send_exit_for_pico; pbf->user_says_noflow = dont_flow_this_time; + pbf->newthread = new_thread_on_blank_subject; + ps_global->newthread = 0; /* reset this value */ if(F_OFF(F_CANCEL_CONFIRM, ps_global)) pbf->canceltest = cancel_for_pico; *************** *** 2301,2306 **** --- 2322,2332 ---- he->rich_header = 0; } } + if (F_ON(F_ALLOW_CHANGING_FROM, ps_global) && + !ps_global->never_allow_changing_from){ + he->display_it = 1; /* show it */ + he->rich_header = 0; + } he_from = he; break; *************** *** 2410,2415 **** --- 2436,2461 ---- removing_trailing_white_space(pf->textbuf); (void)removing_double_quotes(pf->textbuf); build_address(pf->textbuf, &addr, NULL, NULL, NULL); + if (!strncmp(pf->name,"Lcc",3) && addr && *addr){ + RULE_RESULT *rule; + + outgoing->date = (unsigned char *) cpystr(addr); + ps_global->procid = cpystr("fwd-lcc"); + rule = get_result_rule(V_FORWARD_RULES, + FOR_COMPOSE|FOR_TRIM, outgoing); + if (rule){ + addr = cpystr(rule->result); + removing_trailing_white_space(addr); + (void)removing_extra_stuff(addr); + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + fs_give((void **)&ps_global->procid); + if (outgoing->date) + fs_give((void **)&outgoing->date); + } + rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain); fs_give((void **)&addr); *************** *** 2979,2985 **** #ifdef _WINDOWS mswin_setwindowmenu (MENU_DEFAULT); #endif ! fix_windsize(ps_global); /* * Only reinitialize signals if we didn't receive an interesting --- 3025,3036 ---- #ifdef _WINDOWS mswin_setwindowmenu (MENU_DEFAULT); #endif ! if (ps_global->send_immediately){ ! if(ps_global->free_initial_cmds_backup) ! fs_give((void **)&ps_global->free_initial_cmds_backup); ! } ! else ! fix_windsize(ps_global); /* * Only reinitialize signals if we didn't receive an interesting *************** *** 3038,3044 **** if(outgoing->return_path) mail_free_address(&outgoing->return_path); ! outgoing->return_path = rfc822_cpy_adr(outgoing->from); /* * Don't ever believe the sender that is there. --- 3089,3097 ---- if(outgoing->return_path) mail_free_address(&outgoing->return_path); ! outgoing->return_path = F_ON(F_USE_DOMAIN_NAME,ps_global) ! ? rfc822_cpy_adr(generate_from()) ! : rfc822_cpy_adr(outgoing->from); /* * Don't ever believe the sender that is there. *************** *** 3068,3073 **** --- 3121,3131 ---- outgoing->sender->host = cpystr(ps_global->hostname); } + if(ps_global->newthread){ + if(outgoing->in_reply_to) fs_give((void **)&outgoing->in_reply_to); + if(outgoing->references) fs_give((void **)&outgoing->references); + } + /*----- Message is edited, now decide what to do with it ----*/ if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){ /*=========== Postpone or Interrupted message ============*/ *************** *** 3710,3719 **** if(sending_filter_requested && !filter_message_text(sending_filter_requested, outgoing, *body, &orig_so, &header)){ ! q_status_message1(SM_ORDER, 3, 3, _("Problem filtering! Nothing sent%s."), fcc ? " or saved to fcc" : ""); ! continue; } /*------ Actually post -------*/ --- 3768,3783 ---- if(sending_filter_requested && !filter_message_text(sending_filter_requested, outgoing, *body, &orig_so, &header)){ ! if (!ps_global->send_immediately){ ! q_status_message1(SM_ORDER, 3, 3, _("Problem filtering! Nothing sent%s."), fcc ? " or saved to fcc" : ""); ! continue; ! } ! else{ ! fprintf(stderr, _("Problem filtering! Nothing sent or saved to Fcc\n")); ! exit(-1); ! } } /*------ Actually post -------*/ *************** *** 3957,3962 **** --- 4021,4028 ---- /*----- Mail Post FAILED, back to composer -----*/ if(result & (P_MAIL_LOSE | P_FCC_LOSE)){ dprint((1, "Send failed, continuing\n")); + if (ps_global->send_immediately) + exit(1); if(result & P_FCC_LOSE){ /* *************** *** 3991,3996 **** --- 4057,4063 ---- update_answered_flags(reply); /*----- Signed, sealed, delivered! ------*/ + if (!ps_global->send_immediately) q_status_message(SM_ORDER, 0, 3, pine_send_status(result, fcc, tmp_20k_buf, SIZEOF_20KBUF, NULL)); *************** *** 4360,4365 **** --- 4427,4442 ---- return(buf); } + /* Callback from Pico to set the conditions for Alpine to start a new thread + */ + + void + new_thread_on_blank_subject(void) + { + ps_global->newthread = F_ON(F_NEW_THREAD_ON_BLANK_SUBJECT, ps_global); + } + + /*---------------------------------------------------------------------- Call back for pico to insert the specified message's text *************** *** 4447,4453 **** return(1); } ! if(F_ON(F_SEND_WO_CONFIRM, ps_global)){ if(result) *result = NULL; --- 4524,4530 ---- return(1); } ! if(!ps_global->send_immediately && F_ON(F_SEND_WO_CONFIRM, ps_global)){ if(result) *result = NULL; *************** *** 4627,4633 **** opts[i].ch = -1; ! fix_windsize(ps_global); while(1){ if(filters && filters->filter && (p = strindex(filters->filter, ' '))) --- 4704,4711 ---- opts[i].ch = -1; ! if (!ps_global->send_immediately) ! fix_windsize(ps_global); while(1){ if(filters && filters->filter && (p = strindex(filters->filter, ' '))) *************** *** 4809,4815 **** if(double_rad + ((call_mailer_flags & CM_DSN_SHOW) ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) > 11) ! rv = double_radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts, 'y', 'z', (F_ON(F_DSN, ps_global) && allow_flowed) ? h_send_prompt_dsn_flowed : --- 4887,4894 ---- if(double_rad + ((call_mailer_flags & CM_DSN_SHOW) ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) > 11) ! rv = ps_global->send_immediately ? 'y' : ! double_radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts, 'y', 'z', (F_ON(F_DSN, ps_global) && allow_flowed) ? h_send_prompt_dsn_flowed : *************** *** 4818,4824 **** h_send_prompt, RB_NORM); else ! rv = radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts, 'y', 'z', (double_rad + ((call_mailer_flags & CM_DSN_SHOW) --- 4897,4904 ---- h_send_prompt, RB_NORM); else ! rv = ps_global->send_immediately ? 'y' : ! radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts, 'y', 'z', (double_rad + ((call_mailer_flags & CM_DSN_SHOW) *************** *** 5155,5165 **** --- 5235,5247 ---- {'c', 'c', "C", N_("Confirm")}, {'n', 'n', "N", N_("No")}, {'y', 'y', "", ""}, + {'t', 't', "T", N_("CounT")}, {-1, 0, NULL, NULL} }; ps_global->redrawer = redraw_pico; fix_windsize(ps_global); + pbf->curpos[0] = '\0'; while(1){ rv = radio_buttons(prompt, -FOOTER_ROWS(ps_global), opts, *************** *** 5172,5183 **** q_status_message(SM_INFO, 1, 3, _(" Type \"C\" to cancel message ")); display_message('x'); } else break; } ps_global->redrawer = redraw; ! return(rstr); } --- 5254,5269 ---- q_status_message(SM_INFO, 1, 3, _(" Type \"C\" to cancel message ")); display_message('x'); } + else if(rv == 't'){ + showcpos(1,0); + break; + } else break; } ps_global->redrawer = redraw; ! return(pbf->curpos[0] ? pbf->curpos : rstr); } *************** *** 5204,5210 **** if(fcmd && (cmd=expand_filter_tokens(fcmd, outgoing, &tmpf, &resultf, &mtf, ! &key, &include_hdrs))){ if(tmpf){ /* * We need WRITE_TO_LOCALE here because the user is going to --- 5290,5296 ---- if(fcmd && (cmd=expand_filter_tokens(fcmd, outgoing, &tmpf, &resultf, &mtf, ! &key, &include_hdrs, NULL))){ if(tmpf){ /* * We need WRITE_TO_LOCALE here because the user is going to *************** *** 5281,5289 **** if((tmp_so = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){ gf_set_so_writec(&pc, tmp_so); ps_global->mangled_screen = 1; ! suspend_busy_cue(); ! ClearScreen(); ! fflush(stdout); if(tmpf){ PIPE_S *fpipe; --- 5367,5377 ---- if((tmp_so = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){ gf_set_so_writec(&pc, tmp_so); ps_global->mangled_screen = 1; ! if (!ps_global->send_immediately){ ! suspend_busy_cue(); ! ClearScreen(); ! fflush(stdout); ! } if(tmpf){ PIPE_S *fpipe; *************** *** 5311,5317 **** } else errstr = gf_filter(cmd, key ? filter_session_key() : NULL, ! readthis_so, pc, NULL, 0, pipe_callback); if(our_tmpf_so) --- 5399,5405 ---- } else errstr = gf_filter(cmd, key ? filter_session_key() : NULL, ! readthis_so, pc, NULL, 0, 0, pipe_callback); if(our_tmpf_so) *************** *** 5395,5402 **** set_mime_type_by_grope(b); } ! ClearScreen(); ! resume_busy_cue(0); } else errstr = "Can't create space for filtered text."; --- 5483,5492 ---- set_mime_type_by_grope(b); } ! if (!ps_global->send_immediately){ ! ClearScreen(); ! resume_busy_cue(0); ! } } else errstr = "Can't create space for filtered text."; *************** *** 5427,5436 **** if(tmp_so) so_give(&tmp_so); ! q_status_message1(SM_ORDER | SM_DING, 3, 6, _("Problem filtering: %s"), errstr); ! dprint((1, "Filter FAILED: %s\n", errstr ? errstr : "?")); } return(errstr == NULL); --- 5517,5532 ---- if(tmp_so) so_give(&tmp_so); ! if (!ps_global->send_immediately){ ! q_status_message1(SM_ORDER | SM_DING, 3, 6, _("Problem filtering: %s"), errstr); ! dprint((1, "Filter FAILED: %s\n", errstr ? errstr : "?")); + } + else{ + fprintf(stderr, _("Filter FAILED: %s\n"), errstr ? errstr : "?"); + exit(-1); + } } return(errstr == NULL); diff -rc alpine-2.00/alpine/setup.c alpine-2.00.I.USE/alpine/setup.c *** alpine-2.00/alpine/setup.c 2008-01-23 11:15:36.000000000 -0800 --- alpine-2.00.I.USE/alpine/setup.c 2011-02-07 20:33:41.000000000 -0800 *************** *** 257,263 **** ctmpa->flags |= CF_NOSELECT; ctmpa->value = cpystr("--- ----------------------"); ! decode_sort(pval, &def_sort, &def_sort_rev); for(j = 0; j < 2; j++){ for(i = 0; ps->sort_types[i] != EndofList; i++){ --- 257,263 ---- ctmpa->flags |= CF_NOSELECT; ctmpa->value = cpystr("--- ----------------------"); ! decode_sort(pval, &def_sort, &def_sort_rev, 0); for(j = 0; j < 2; j++){ for(i = 0; ps->sort_types[i] != EndofList; i++){ *************** *** 272,277 **** --- 272,326 ---- } } } + else if(vtmp == &ps->vars[V_THREAD_SORT_KEY]){ /* radio case */ + SortOrder thread_def_sort; + int thread_def_sort_rev, lv; + + ctmpa->flags |= CF_NOSELECT; + ctmpa->keymenu = &config_radiobutton_keymenu; + ctmpa->tool = NULL; + + /* put a nice delimiter before list */ + new_confline(&ctmpa)->var = NULL; + ctmpa->varnamep = ctmpb; + ctmpa->keymenu = &config_radiobutton_keymenu; + ctmpa->help = NO_HELP; + ctmpa->tool = radiobutton_tool; + ctmpa->valoffset = 12; + ctmpa->flags |= CF_NOSELECT; + ctmpa->value = cpystr("Set Thread Sort Options"); + + new_confline(&ctmpa)->var = NULL; + ctmpa->varnamep = ctmpb; + ctmpa->keymenu = &config_radiobutton_keymenu; + ctmpa->help = NO_HELP; + ctmpa->tool = radiobutton_tool; + ctmpa->valoffset = 12; + ctmpa->flags |= CF_NOSELECT; + ctmpa->value = cpystr("--- ----------------------"); + + /* find longest value's name */ + for(lv = 0, i = 0; ps->sort_types[i] != EndofList; i++) + if(lv < (j = strlen(sort_name(ps->sort_types[i])))) + lv = j; + + decode_sort(pval, &thread_def_sort, &thread_def_sort_rev, 1); + + for(j = 0; j < 2; j++){ + for(i = 0; ps->sort_types[i] != EndofList; i++){ + if (allowed_thread_key(ps->sort_types[i])){ + new_confline(&ctmpa)->var = vtmp; + ctmpa->varnamep = ctmpb; + ctmpa->keymenu = &config_radiobutton_keymenu; + ctmpa->help = config_help(vtmp - ps->vars, 0); + ctmpa->tool = radiobutton_tool; + ctmpa->valoffset = 12; + ctmpa->varmem = i + (j * EndofList); + ctmpa->value = pretty_value(ps, ctmpa); + } + } + } + } else if(vtmp == &ps->vars[V_USE_ONLY_DOMAIN_NAME]){ /* yesno case */ ctmpa->keymenu = &config_yesno_keymenu; ctmpa->tool = yesno_tool; *************** *** 463,468 **** --- 512,526 ---- } } + pval = PVAL(&ps->vars[V_THREAD_SORT_KEY], ew); + if(vsave[V_THREAD_SORT_KEY].saved_user_val.p && pval + && strcmp(vsave[V_THREAD_SORT_KEY].saved_user_val.p, pval)){ + if(!mn_get_mansort(ps_global->msgmap)){ + clear_index_cache(ps_global->mail_stream, 0); + reset_sort_order(SRT_VRB); + } + } + treat_color_vars_as_text = 0; free_saved_config(ps, &vsave, expose_hidden_config); #ifdef _WINDOWS diff -rc alpine-2.00/alpine/signal.c alpine-2.00.I.USE/alpine/signal.c *** alpine-2.00/alpine/signal.c 2008-04-07 15:58:40.000000000 -0700 --- alpine-2.00.I.USE/alpine/signal.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 819,826 **** #else if(F_ON(F_SUSPEND_SPAWNS, ps_global)){ PIPE_S *syspipe; ! if((syspipe = open_system_pipe(NULL, NULL, NULL, PIPE_USER|PIPE_RESET, 0, pipe_callback, pipe_report_error)) != NULL){ suspend_notice("exit"); #ifndef SIGCHLD --- 819,828 ---- #else if(F_ON(F_SUSPEND_SPAWNS, ps_global)){ PIPE_S *syspipe; + int flag = some_stream_is_locked() ? PIPE_NONEWMAIL : 0; ! flag |= PIPE_USER|PIPE_RESET; ! if((syspipe = open_system_pipe(NULL, NULL, NULL, flag, 0, pipe_callback, pipe_report_error)) != NULL){ suspend_notice("exit"); #ifndef SIGCHLD *************** *** 867,873 **** _("Error loading \"%s\""), shell); #endif ! if(isremote && !pine_mail_ping(ps_global->mail_stream)) q_status_message(SM_ORDER | SM_DING, 4, 9, _("Suspended for too long, IMAP connection broken")); --- 869,876 ---- _("Error loading \"%s\""), shell); #endif ! if(isremote && !ps_global->mail_stream->lock ! && !pine_mail_ping(ps_global->mail_stream)) q_status_message(SM_ORDER | SM_DING, 4, 9, _("Suspended for too long, IMAP connection broken")); diff -rc alpine-2.00/alpine/status.c alpine-2.00.I.USE/alpine/status.c *** alpine-2.00/alpine/status.c 2007-11-28 09:57:15.000000000 -0800 --- alpine-2.00.I.USE/alpine/status.c 2011-02-07 20:33:42.000000000 -0800 *************** *** 110,115 **** --- 110,118 ---- char *clean_msg; size_t mlen; + if (ps_global->send_immediately) + return; + status_message_lock(); /* *************** *** 604,609 **** --- 607,615 ---- SMQ_T *q, *copy_of_q; int ding; + if(ps_global->send_immediately) + return; + start_over: status_message_lock(); diff -rc alpine-2.00/doc/alpine.1 alpine-2.00.I.USE/doc/alpine.1 *** alpine-2.00/doc/alpine.1 2008-08-22 13:40:16.000000000 -0700 --- alpine-2.00.I.USE/doc/alpine.1 2011-02-07 20:33:45.000000000 -0800 *************** *** 164,169 **** --- 164,171 ---- .IP \fB-n\ \fInumber\fR 20 Start up with current message-number set to .I number. + .IP \fB-noutf8\fR 20 + Warns Alpine that piped input is not encoded in UTF-8. .IP \fB-o\fR 20 Open first folder read-only. .IP \fB-p\ \fIconfig-file\fR 20 diff -rc alpine-2.00/imap/src/c-client/imap4r1.c alpine-2.00.I.USE/imap/src/c-client/imap4r1.c *** alpine-2.00/imap/src/c-client/imap4r1.c 2008-06-04 11:39:54.000000000 -0700 --- alpine-2.00.I.USE/imap/src/c-client/imap4r1.c 2011-02-07 20:33:43.000000000 -0800 *************** *** 1121,1126 **** --- 1121,1127 ---- sprintf (tmp,"Retrying using %s authentication after %.80s", at->name,lsterr); mm_log (tmp,NIL); + delete_password(mb, usr); fs_give ((void **) &lsterr); } trial = 0; /* initial trial count */ *************** *** 1129,1134 **** --- 1130,1136 ---- if (lsterr) { /* previous attempt with this one failed? */ sprintf (tmp,"Retrying %s authentication after %.80s",at->name,lsterr); mm_log (tmp,WARN); + delete_password(mb, usr); fs_give ((void **) &lsterr); } LOCAL->saslcancel = NIL; *************** *** 1166,1171 **** --- 1168,1174 ---- sprintf (tmp,"Can not authenticate to IMAP server: %.80s",lsterr); mm_log (tmp,ERROR); } + delete_password(mb, usr); fs_give ((void **) &lsterr); } return NIL; /* ran out of authenticators */ *************** *** 1207,1212 **** --- 1210,1216 ---- if (imap_OK (stream,reply = imap_send (stream,"LOGIN",args))) ret = LONGT; /* success */ else { + delete_password(mb, usr); mm_log (reply->text,WARN); if (!LOCAL->referral && (trial == imap_maxlogintrials)) mm_log ("Too many login failures",ERROR); *************** *** 4527,4532 **** --- 4531,4537 ---- if (*env) { /* need to merge this header into envelope? */ if (!(*env)->newsgroups) { /* need Newsgroups? */ (*env)->newsgroups = nenv->newsgroups; + (*env)->ngpathexists = nenv->ngpathexists; nenv->newsgroups = NIL; } if (!(*env)->followup_to) { /* need Followup-To? */ *************** *** 4581,4586 **** --- 4586,4592 ---- if (oenv) { /* need to merge old envelope? */ (*env)->newsgroups = oenv->newsgroups; oenv->newsgroups = NIL; + (*env)->ngpathexists = oenv->ngpathexists; (*env)->followup_to = oenv->followup_to; oenv->followup_to = NIL; (*env)->references = oenv->references; diff -rc alpine-2.00/imap/src/c-client/mail.c alpine-2.00.I.USE/imap/src/c-client/mail.c *** alpine-2.00/imap/src/c-client/mail.c 2008-06-04 11:39:54.000000000 -0700 --- alpine-2.00.I.USE/imap/src/c-client/mail.c 2011-02-07 20:33:47.000000000 -0800 *************** *** 34,39 **** --- 34,41 ---- /* c-client global data */ + static char *pwdfile = NIL; /*password file */ + static int xlate_key; /* for password file support */ /* version of this library */ static char *mailcclientversion = CCLIENTVERSION; /* list of mail drivers */ *************** *** 52,57 **** --- 54,61 ---- static rfc822out_t mail822out = NIL; /* RFC-822 output generator (new style) */ static rfc822outfull_t mail822outfull = NIL; + /* Erase password (client side) */ + static deletepwd_t erase_password = NIL; /* SMTP verbose callback */ static smtpverbose_t mailsmtpverbose = mm_dlog; /* proxy copy routine */ *************** *** 544,549 **** --- 548,558 ---- case GET_SENDCOMMAND: ret = (void *) mailsendcommand; break; + case SET_ERASEPASSWORD: + erase_password = (deletepwd_t) value; + case GET_ERASEPASSWORD: + ret = (void *) erase_password; + break; case SET_SERVICENAME: servicename = (char *) value; *************** *** 640,645 **** --- 649,661 ---- case GET_SNARFMAILBOXNAME: if (stream) ret = (void *) stream->snarf.name; break; + case SET_PASSWORDFILE: + if (pwdfile) fs_give ((void **) &pwdfile); + pwdfile = cpystr ((char *) value); + break; + case GET_PASSWORDFILE: + ret = (void *) pwdfile; + break; default: if (r = smtp_parameters (function,value)) ret = r; if (r = env_parameters (function,value)) ret = r; *************** *** 991,997 **** MAILSTREAM *ts; char *s,*t,tmp[MAILTMPLEN]; size_t i; ! DRIVER *d; /* never allow names with newlines */ if (s = strpbrk (mailbox,"\015\012")) { MM_LOG ("Can't create mailbox with such a name",ERROR); --- 1007,1013 ---- MAILSTREAM *ts; char *s,*t,tmp[MAILTMPLEN]; size_t i; ! DRIVER *d, *md; /* never allow names with newlines */ if (s = strpbrk (mailbox,"\015\012")) { MM_LOG ("Can't create mailbox with such a name",ERROR); *************** *** 1015,1020 **** --- 1031,1038 ---- return NIL; } + /* Hack, we should do this better, but it works */ + for (md = maildrivers; md && strcmp (md->name, "md"); md = md->next); /* see if special driver hack */ if ((mailbox[0] == '#') && ((mailbox[1] == 'd') || (mailbox[1] == 'D')) && ((mailbox[2] == 'r') || (mailbox[2] == 'R')) && *************** *** 1045,1050 **** --- 1063,1075 ---- (((*mailbox == '{') || (*mailbox == '#')) && (stream = mail_open (NIL,mailbox,OP_PROTOTYPE | OP_SILENT)))) d = stream->dtb; + else if(mailbox[0] == '#' + && (mailbox[1] == 'm' || mailbox[1] == 'M') + && (mailbox[2] == 'd' || mailbox[2] == 'D' + || mailbox[2] == 'c' || mailbox[2] == 'C') + && mailbox[3] == '/' + && mailbox[4] != '\0') + return (*md->create)(stream, mailbox); else if ((*mailbox != '{') && (ts = default_proto (NIL))) d = ts->dtb; else { /* failed utterly */ sprintf (tmp,"Can't create mailbox %.80s: indeterminate format",mailbox); *************** *** 3352,3364 **** long flags) { STRINGLIST *hdrs; ! int notfound; unsigned long i; char c,*s,*e,*t,tmp[MAILTMPLEN]; char *src = text; char *dst = src; char *end = text + len; ! text[len] = '\012'; /* guard against running off buffer */ while (src < end) { /* process header */ /* slurp header line name */ for (s = src,e = s + MAILTMPLEN - 1,e = (e < end ? e : end),t = tmp; --- 3377,3389 ---- long flags) { STRINGLIST *hdrs; ! int notfound, fix = text[len - 1] == '\0'; unsigned long i; char c,*s,*e,*t,tmp[MAILTMPLEN]; char *src = text; char *dst = src; char *end = text + len; ! text[fix ? len - 1 : len] = '\012'; /* guard against running off buffer */ while (src < end) { /* process header */ /* slurp header line name */ for (s = src,e = s + MAILTMPLEN - 1,e = (e < end ? e : end),t = tmp; *************** *** 3397,3402 **** --- 3422,3431 ---- } } *dst = '\0'; /* tie off destination */ + if(fix){ + text[len] = '\012'; + text[len-1] = '\0'; + } return dst - text; } *************** *** 5108,5114 **** unsigned long msgno) { if (msgno && ov) { /* just in case */ ! MESSAGECACHE telt; SORTCACHE *s = (SORTCACHE *) (*mailcache) (stream,msgno,CH_SORTCACHE); if (!s->subject && ov->subject) { s->refwd = mail_strip_subject (ov->subject,&s->subject); --- 5137,5144 ---- unsigned long msgno) { if (msgno && ov) { /* just in case */ ! MESSAGECACHE telt, *elt; ! ENVELOPE *env; SORTCACHE *s = (SORTCACHE *) (*mailcache) (stream,msgno,CH_SORTCACHE); if (!s->subject && ov->subject) { s->refwd = mail_strip_subject (ov->subject,&s->subject); *************** *** 5127,5133 **** s->dirty = T; } if (!s->references && ! !(s->references = mail_thread_parse_references (ov->references,T))) { /* don't do In-Reply-To with NNTP mailboxes */ s->references = mail_newstringlist (); s->dirty = T; --- 5157,5168 ---- s->dirty = T; } if (!s->references && ! !(s->references = mail_thread_parse_references (ov->references,T)) ! && stream->dtb && !strcmp(stream->dtb->name, "imap") ! && (elt = mail_elt (stream, msgno)) != NULL ! && (env = elt->private.msg.env) != NULL ! && env->in_reply_to ! && !(s->references = mail_thread_parse_references(env->in_reply_to, NIL))) { /* don't do In-Reply-To with NNTP mailboxes */ s->references = mail_newstringlist (); s->dirty = T; *************** *** 6115,6120 **** --- 6150,6164 ---- return i; return 0; } + /* Client side callback warning to delete wrong password + * + */ + void delete_password(NETMBX *mb, char *user) + { + deletepwd_t ep = mail_parameters(NULL, GET_ERASEPASSWORD, NULL); + if (ep) (ep)(mb, user); + } + /* Standard TCP/IP network driver */ *************** *** 6329,6331 **** --- 6373,6450 ---- { return (*stream->dtb->localhost) (stream->stream); } + + /* + * + * Module to add support for password file to a c-client application + * + * Written by Eduardo Chappa, based on password file support for Pine + * + */ + #ifndef PWDFILE + #define PWDFILE 1 + #endif + + #define FIRSTCH 0x20 + #define LASTCH 0x7e + #define TABSZ (LASTCH - FIRSTCH + 1) + + char mm_xlate_out (char c); + void mm_userpwd (NETMBX *mb, char **username, char **password); + + /* function that decodes passwords */ + + char mm_xlate_out (char c) + { + register int dti; + register int xch; + + if((c >= FIRSTCH) && (c <= LASTCH)){ + xch = c - (dti = xlate_key); + xch += (xch < FIRSTCH-TABSZ) ? 2*TABSZ : (xch < FIRSTCH) ? TABSZ : 0; + dti = (xch - FIRSTCH) + dti; + dti -= (dti >= 2*TABSZ) ? 2*TABSZ : (dti >= TABSZ) ? TABSZ : 0; + xlate_key = dti; + return(xch); + } + else + return(c); + } + + void mm_userpwd (NETMBX *mb, char **username, char **password) + { + char *s; + char tmp[MAILTMPLEN], *ui[5]; + FILE *fp; + int i, j, n; + + if (!(pwdfile = env_parameters(GET_PASSWORDFILE, NULL))) + return; + + if (fp = fopen(pwdfile, "r")){ + for(n = 0; fgets(tmp, sizeof(tmp), fp); n++){ + xlate_key = n; + for(i = 0; tmp[i]; i++) + tmp[i] = mm_xlate_out(tmp[i]); + + if(i && tmp[i-1] == '\n') + tmp[i-1] = '\0'; + + ui[0] = ui[1] = ui[2] = ui[3] = ui[4] = NULL; + for(i = 0, j = 0; tmp[i] && j < 5; j++){ + for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++); + + if(tmp[i]) + tmp[i++] = '\0'; + } + if (*username && ui[1] && !strcmp(*username, ui[1]) && mb->host + && ((ui[2] && !strcmp(mb->host, ui[2])) + || (ui[4] && !strcmp(mb->host,ui[4])) + || (ui[2] && !strcmp(mb->orighost, ui[2])) + || (ui[4] && !strcmp(mb->orighost,ui[4])))) + strcpy (*password,ui[0]); + } + fclose(fp); + } + } + diff -rc alpine-2.00/imap/src/c-client/mail.h alpine-2.00.I.USE/imap/src/c-client/mail.h *** alpine-2.00/imap/src/c-client/mail.h 2008-08-08 10:34:22.000000000 -0700 --- alpine-2.00.I.USE/imap/src/c-client/mail.h 2011-02-07 20:33:47.000000000 -0800 *************** *** 177,182 **** --- 177,186 ---- #define SET_EXTERNALAUTHID (long) 230 #define GET_SSLCAPATH (long) 231 #define SET_SSLCAPATH (long) 232 + #define GET_ERASEPASSWORD (long) 233 + #define SET_ERASEPASSWORD (long) 234 + #define SET_PASSWORDFILE (long) 235 + #define GET_PASSWORDFILE (long) 236 /* 3xx: TCP/IP */ #define GET_OPENTIMEOUT (long) 300 *************** *** 353,358 **** --- 357,366 ---- #define SET_SCANCONTENTS (long) 573 #define GET_MHALLOWINBOX (long) 574 #define SET_MHALLOWINBOX (long) 575 + #define GET_COURIERSTYLE (long) 576 + #define SET_COURIERSTYLE (long) 577 + #define SET_MDINBOXPATH (long) 578 + #define GET_MDINBOXPATH (long) 579 /* Driver flags */ *************** *** 685,690 **** --- 693,699 ---- /* Message envelope */ typedef struct mail_envelope { + unsigned int ngpathexists : 1; /* newsgroups may be bogus */ unsigned int incomplete : 1; /* envelope may be incomplete */ unsigned int imapenvonly : 1; /* envelope only has IMAP envelope */ char *remail; /* remail header if any */ *************** *** 1302,1308 **** typedef void *(*mailcache_t) (MAILSTREAM *stream,unsigned long msgno,long op); typedef long (*mailproxycopy_t) (MAILSTREAM *stream,char *sequence, char *mailbox,long options); ! typedef long (*tcptimeout_t) (long overall,long last); typedef void *(*authchallenge_t) (void *stream,unsigned long *len); typedef long (*authrespond_t) (void *stream,char *s,unsigned long size); typedef long (*authcheck_t) (void); --- 1311,1317 ---- typedef void *(*mailcache_t) (MAILSTREAM *stream,unsigned long msgno,long op); typedef long (*mailproxycopy_t) (MAILSTREAM *stream,char *sequence, char *mailbox,long options); ! typedef long (*tcptimeout_t) (long overall,long last, char *host); typedef void *(*authchallenge_t) (void *stream,unsigned long *len); typedef long (*authrespond_t) (void *stream,char *s,unsigned long size); typedef long (*authcheck_t) (void); *************** *** 1325,1330 **** --- 1334,1340 ---- typedef void *(*blocknotify_t) (int reason,void *data); typedef long (*kinit_t) (char *host,char *reason); typedef void (*sendcommand_t) (MAILSTREAM *stream,char *cmd,long flags); + typedef void (*deletepwd_t) (NETMBX *mb,char *user); typedef char *(*newsrcquery_t) (MAILSTREAM *stream,char *mulname,char *name); typedef void (*getacl_t) (MAILSTREAM *stream,char *mailbox,ACLLIST *acl); typedef void (*listrights_t) (MAILSTREAM *stream,char *mailbox,char *id, *************** *** 1604,1609 **** --- 1614,1621 ---- void mm_fatal (char *string); void *mm_cache (MAILSTREAM *stream,unsigned long msgno,long op); + void delete_password (NETMBX *mb, char *user); + extern STRINGDRIVER mail_string; void mail_versioncheck (char *version); void mail_link (DRIVER *driver); diff -rc alpine-2.00/imap/src/c-client/nntp.c alpine-2.00.I.USE/imap/src/c-client/nntp.c *** alpine-2.00/imap/src/c-client/nntp.c 2008-06-04 11:18:34.000000000 -0700 --- alpine-2.00.I.USE/imap/src/c-client/nntp.c 2011-02-07 20:33:43.000000000 -0800 *************** *** 2031,2036 **** --- 2031,2037 ---- sprintf (tmp,"Retrying using %s authentication after %.80s", at->name,lsterr); mm_log (tmp,NIL); + delete_password(mb, mb ? mb->user : NULL); fs_give ((void **) &lsterr); } trial = 0; /* initial trial count */ *************** *** 2039,2044 **** --- 2040,2046 ---- if (lsterr) { sprintf (tmp,"Retrying %s authentication after %.80s",at->name,lsterr); mm_log (tmp,WARN); + delete_password(mb, mb ? mb->user : NULL); fs_give ((void **) &lsterr); } stream->saslcancel = NIL; *************** *** 2064,2069 **** --- 2066,2072 ---- sprintf (tmp,"Can not authenticate to NNTP server: %.80s",lsterr); mm_log (tmp,ERROR); } + delete_password(mb, mb ? mb->user : NULL); fs_give ((void **) &lsterr); } else if (mb->secflag) /* no SASL, can't do /secure */ *************** *** 2092,2097 **** --- 2095,2102 ---- stream->sensitive = T; /* hide this command */ if (nntp_send_work (stream,"AUTHINFO PASS",pwd) == NNTPAUTHED) ret = LONGT; /* password OK */ + else + delete_password(mb, mb ? mb->user : NULL); stream->sensitive = NIL; /* unhide */ if (ret) break; /* OK if successful */ default: /* authentication failed */ diff -rc alpine-2.00/imap/src/c-client/pop3.c alpine-2.00.I.USE/imap/src/c-client/pop3.c *** alpine-2.00/imap/src/c-client/pop3.c 2008-06-04 11:18:34.000000000 -0700 --- alpine-2.00.I.USE/imap/src/c-client/pop3.c 2011-02-07 20:33:43.000000000 -0800 *************** *** 615,620 **** --- 615,621 ---- sprintf (pwd,"Retrying using %.80s authentication after %.80s", at->name,t); mm_log (pwd,NIL); + delete_password(mb, usr); fs_give ((void **) &t); } trial = 0; /* initial trial count */ *************** *** 622,627 **** --- 623,629 ---- if (t) { sprintf (pwd,"Retrying %s authentication after %.80s",at->name,t); mm_log (pwd,WARN); + delete_password(mb, usr); fs_give ((void **) &t); } LOCAL->saslcancel = NIL; *************** *** 667,672 **** --- 669,675 ---- LOCAL->sensitive=NIL; /* unhide */ } if (!ret) { /* failure */ + delete_password(mb, usr); mm_log (LOCAL->reply,WARN); if (trial == pop3_maxlogintrials) mm_log ("Too many login failures",ERROR); diff -rc alpine-2.00/imap/src/c-client/rfc822.c alpine-2.00.I.USE/imap/src/c-client/rfc822.c *** alpine-2.00/imap/src/c-client/rfc822.c 2008-06-04 11:46:10.000000000 -0700 --- alpine-2.00.I.USE/imap/src/c-client/rfc822.c 2011-02-07 20:33:43.000000000 -0800 *************** *** 128,133 **** --- 128,134 ---- ENVELOPE *env = (*en = mail_newenvelope ()); BODY *body = bdy ? (*bdy = mail_newbody ()) : NIL; long MIMEp = -1; /* flag that MIME semantics are in effect */ + long PathP = NIL; /* flag that a Path: was seen */ parseline_t pl = (parseline_t) mail_parameters (NIL,GET_PARSELINE,NIL); if (!host) host = BADHOST; /* make sure that host is non-null */ while (i && *s != '\n') { /* until end of header */ *************** *** 230,235 **** --- 231,239 ---- *t++ = '\0'; } break; + case 'P': /* possible Path: */ + if (!strcmp (tmp+1,"ATH")) env->ngpathexists = T; + break; case 'R': /* possible Reply-To: */ if (!strcmp (tmp+1,"EPLY-TO")) rfc822_parse_adrlist (&env->reply_to,d,host); diff -rc alpine-2.00/imap/src/osdep/mac/tcp_mac.c alpine-2.00.I.USE/imap/src/osdep/mac/tcp_mac.c *** alpine-2.00/imap/src/osdep/mac/tcp_mac.c 2008-06-04 11:18:34.000000000 -0700 --- alpine-2.00.I.USE/imap/src/osdep/mac/tcp_mac.c 2011-02-07 20:33:45.000000000 -0800 *************** *** 383,389 **** if (stream->pb.ioResult) { /* punt if got an error */ time_t tc = time (0); if ((stream->pb.ioResult == commandTimeout) && tmoh && ! ((*tmoh) (tc - t,tc - tl))) continue; /* nuke connection */ stream->pb.csCode = TCPAbort; abortpb->userDataPtr = NIL; --- 383,389 ---- if (stream->pb.ioResult) { /* punt if got an error */ time_t tc = time (0); if ((stream->pb.ioResult == commandTimeout) && tmoh && ! ((*tmoh) (tc - t,tc - tl, stream->host))) continue; /* nuke connection */ stream->pb.csCode = TCPAbort; abortpb->userDataPtr = NIL; diff -rc alpine-2.00/imap/src/osdep/nt/tcp_nt.c alpine-2.00.I.USE/imap/src/osdep/nt/tcp_nt.c *** alpine-2.00/imap/src/osdep/nt/tcp_nt.c 2008-06-04 11:18:34.000000000 -0700 --- alpine-2.00.I.USE/imap/src/osdep/nt/tcp_nt.c 2011-02-07 20:33:45.000000000 -0800 *************** *** 430,436 **** SOCKET_ERROR) && ((errno = WSAGetLastError ()) == WSAEINTR)); else if (!i) { /* timeout, ignore if told to resume */ ! if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl))) continue; /* otherwise punt */ if (tcpdebug) mm_log ("TCP buffer read timeout",TCPDEBUG); return tcp_abort (stream); --- 430,438 ---- SOCKET_ERROR) && ((errno = WSAGetLastError ()) == WSAEINTR)); else if (!i) { /* timeout, ignore if told to resume */ ! if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl), ! stream->host)) ! continue; /* otherwise punt */ if (tcpdebug) mm_log ("TCP buffer read timeout",TCPDEBUG); return tcp_abort (stream); *************** *** 498,504 **** SOCKET_ERROR) && ((errno = WSAGetLastError ()) == WSAEINTR)); else if (!i) { /* timeout, ignore if told to resume */ ! if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl))) continue; /* otherwise punt */ if (tcpdebug) mm_log ("TCP data read timeout",TCPDEBUG); return tcp_abort (stream); --- 500,507 ---- SOCKET_ERROR) && ((errno = WSAGetLastError ()) == WSAEINTR)); else if (!i) { /* timeout, ignore if told to resume */ ! if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl), stream->host)) ! continue; /* otherwise punt */ if (tcpdebug) mm_log ("TCP data read timeout",TCPDEBUG); return tcp_abort (stream); *************** *** 582,588 **** SOCKET_ERROR) && ((errno = WSAGetLastError ()) == WSAEINTR)); else if (!i) { /* timeout, ignore if told to resume */ ! if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl))) continue; /* otherwise punt */ if (tcpdebug) mm_log ("TCP write timeout",TCPDEBUG); return tcp_abort (stream); --- 585,592 ---- SOCKET_ERROR) && ((errno = WSAGetLastError ()) == WSAEINTR)); else if (!i) { /* timeout, ignore if told to resume */ ! if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl), stream->host)) ! continue; /* otherwise punt */ if (tcpdebug) mm_log ("TCP write timeout",TCPDEBUG); return tcp_abort (stream); diff -rc alpine-2.00/imap/src/osdep/unix/dummy.c alpine-2.00.I.USE/imap/src/osdep/unix/dummy.c *** alpine-2.00/imap/src/osdep/unix/dummy.c 2008-06-04 11:18:34.000000000 -0700 --- alpine-2.00.I.USE/imap/src/osdep/unix/dummy.c 2011-02-07 20:33:41.000000000 -0800 *************** *** 106,118 **** * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ ! DRIVER *dummy_valid (char *name) { ! char *s,tmp[MAILTMPLEN]; struct stat sbuf; /* must be valid local mailbox */ ! if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) { /* indeterminate clearbox INBOX */ if (!*s) return &dummydriver; else if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) { --- 106,124 ---- * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */ ! char * maildir_remove_root(char *); DRIVER *dummy_valid (char *name) { ! char *s,tmp[MAILTMPLEN], *rname; struct stat sbuf; + + if(strlen(name) > MAILTMPLEN) + name[MAILTMPLEN] = '\0'; + + strcpy(tmp, name); + rname = maildir_remove_root(tmp); /* must be valid local mailbox */ ! if (rname && *rname && (*rname != '{') && (s = mailboxfile (tmp,rname))) { /* indeterminate clearbox INBOX */ if (!*s) return &dummydriver; else if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) { *************** *** 121,128 **** return &dummydriver; } /* blackbox INBOX does not exist yet */ ! else if (!compare_cstring (name,"INBOX")) return &dummydriver; } return NIL; } --- 127,135 ---- return &dummydriver; } /* blackbox INBOX does not exist yet */ ! else if (!compare_cstring (rname,"INBOX")) return &dummydriver; } + if(rname) fs_give((void **)&rname); return NIL; } *************** *** 454,459 **** --- 461,468 ---- { char *s,tmp[MAILTMPLEN]; long ret = NIL; + if(!strncmp(mailbox,"#md/",4) || !strncmp(mailbox,"#mc/", 4)) + return maildir_create(stream, mailbox); /* validate name */ if (!(compare_cstring (mailbox,"INBOX") && (s = dummy_file (tmp,mailbox)))) { sprintf (tmp,"Can't create %.80s: invalid name",mailbox); *************** *** 519,524 **** --- 528,541 ---- { struct stat sbuf; char *s,tmp[MAILTMPLEN]; + if (!strncmp(mailbox,"#md/",4) || !strncmp(mailbox,"#mc/", 4) + || is_valid_maildir(&mailbox)){ + char tmp[MAILTMPLEN] = {'\0'}; + strcpy(tmp, mailbox); + if(tmp[strlen(tmp) - 1] != '/') + tmp[strlen(tmp)] = '/'; + return maildir_delete(stream, tmp); + } if (!(s = dummy_file (tmp,mailbox))) { sprintf (tmp,"Can't delete - invalid name: %.80s",s); MM_LOG (tmp,ERROR); *************** *** 544,555 **** long dummy_rename (MAILSTREAM *stream,char *old,char *newname) { struct stat sbuf; ! char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN]; /* no trailing / allowed */ ! if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) || stat (oldname,&sbuf) || ((s = strrchr (s,'/')) && !s[1] && ((sbuf.st_mode & S_IFMT) != S_IFDIR))) { ! sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname); MM_LOG (mbx,ERROR); return NIL; } --- 561,583 ---- long dummy_rename (MAILSTREAM *stream,char *old,char *newname) { struct stat sbuf; ! char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN], *rold, *rnewname; ! ! if(strlen(old) > MAILTMPLEN) ! old[MAILTMPLEN] = '\0'; ! ! if(strlen(newname) > MAILTMPLEN) ! newname[MAILTMPLEN] = '\0'; ! ! strcpy(tmp, old); ! rold = maildir_remove_root(tmp); ! strcpy(tmp, newname); ! rnewname = maildir_remove_root(tmp); /* no trailing / allowed */ ! if (!dummy_file (oldname,rold) || !(s = dummy_file (mbx,rnewname)) || stat (oldname,&sbuf) || ((s = strrchr (s,'/')) && !s[1] && ((sbuf.st_mode & S_IFMT) != S_IFDIR))) { ! sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",rold,rnewname); MM_LOG (mbx,ERROR); return NIL; } *************** *** 565,578 **** } } /* rename of non-ex INBOX creates dest */ ! if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf)) return dummy_create (NIL,mbx); if (rename (oldname,mbx)) { ! sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname, strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } return T; /* return success */ } --- 593,608 ---- } } /* rename of non-ex INBOX creates dest */ ! if (!compare_cstring (rold,"INBOX") && stat (oldname,&sbuf)) return dummy_create (NIL,mbx); if (rename (oldname,mbx)) { ! sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",rold,rnewname, strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } + if(rold) fs_give((void **)&rold); + if(rnewname) fs_give((void **)&rnewname); return T; /* return success */ } diff -rc alpine-2.00/imap/src/osdep/unix/maildir.c alpine-2.00.I.USE/imap/src/osdep/unix/maildir.c *** alpine-2.00/imap/src/osdep/unix/maildir.c 2011-02-07 20:34:02.000000000 -0800 --- alpine-2.00.I.USE/imap/src/osdep/unix/maildir.c 2011-02-07 20:33:41.000000000 -0800 *************** *** 0 **** --- 1,2584 ---- + /* + * Maildir driver for Alpine 2.00 + * + * Written by Eduardo Chappa + * Last Update: November 6, 2010. + * + */ + + #include + #include + #include + extern int errno; /* just in case */ + #include "mail.h" + #include + #include + #include + #include "osdep.h" + #include "rfc822.h" + #include "fdstring.h" + #include "misc.h" + #include "dummy.h" + #include "maildir.h" + + /* Driver dispatch used by MAIL */ + DRIVER maildirdriver = { + "md", /* driver name, yes it's md, not maildir */ + /* driver flags */ + DR_MAIL|DR_LOCAL|DR_NAMESPACE|DR_DIRFMT, + (DRIVER *) NIL, /* next driver */ + maildir_valid, /* mailbox is valid for us */ + maildir_parameters, /* manipulate parameters */ + NIL, /* scan mailboxes */ + maildir_list, /* find mailboxes */ + maildir_lsub, /* find subscribed mailboxes */ + maildir_sub, /* subscribe to mailbox */ + maildir_unsub, /* unsubscribe from mailbox */ + maildir_create, /* create mailbox */ + maildir_delete, /* delete mailbox */ + maildir_rename, /* rename mailbox */ + mail_status_default, /* status of mailbox */ + maildir_open, /* open mailbox */ + maildir_close, /* close mailbox */ + maildir_fast, /* fetch message "fast" attributes */ + NIL, /* fetch message flags */ + NIL, /* fetch overview */ + NIL, /* fetch message structure */ + maildir_header, /* fetch message header */ + maildir_text, /* fetch message body */ + NIL, /* fetch partial message text */ + NIL, /* unique identifier */ + NIL, /* message number */ + NIL, /* modify flags */ + maildir_flagmsg, /* per-message modify flags */ + NIL, /* search for message based on criteria */ + NIL, /* sort messages */ + NIL, /* thread messages */ + maildir_ping, /* ping mailbox to see if still alive */ + maildir_check, /* check for new messages */ + maildir_expunge, /* expunge deleted messages */ + maildir_copy, /* copy messages to another mailbox */ + maildir_append, /* append string message to mailbox */ + NIL /* garbage collect stream */ + }; + + + DRIVER courierdriver = { + "mc", /* Why a separate driver? So that + createproto will work */ + /* driver flags */ + DR_MAIL|DR_LOCAL|DR_NAMESPACE|DR_DIRFMT, + (DRIVER *) NIL, /* next driver */ + maildir_valid, /* mailbox is valid for us */ + maildir_parameters, /* manipulate parameters */ + NIL, /* scan mailboxes */ + courier_list, /* find mailboxes */ + maildir_lsub, /* find subscribed mailboxes */ + maildir_sub, /* subscribe to mailbox */ + maildir_unsub, /* unsubscribe from mailbox */ + maildir_create, /* create mailbox */ + maildir_delete, /* delete mailbox */ + maildir_rename, /* rename mailbox */ + mail_status_default, /* status of mailbox */ + maildir_open, /* open mailbox */ + maildir_close, /* close mailbox */ + maildir_fast, /* fetch message "fast" attributes */ + NIL, /* fetch message flags */ + NIL, /* fetch overview */ + NIL, /* fetch message structure */ + maildir_header, /* fetch message header */ + maildir_text, /* fetch message body */ + NIL, /* fetch partial message text */ + NIL, /* unique identifier */ + NIL, /* message number */ + NIL, /* modify flags */ + maildir_flagmsg, /* per-message modify flags */ + NIL, /* search for message based on criteria */ + NIL, /* sort messages */ + NIL, /* thread messages */ + maildir_ping, /* ping mailbox to see if still alive */ + maildir_check, /* check for new messages */ + maildir_expunge, /* expunge deleted messages */ + maildir_copy, /* copy messages to another mailbox */ + maildir_append, /* append string message to mailbox */ + NIL /* garbage collect stream */ + }; + + MAILSTREAM maildirproto = {&maildirdriver}; /* prototype stream */ + MAILSTREAM courierproto = {&courierdriver}; /* prototype stream */ + + long maildir_dirfmttest (char *name) + { + int i; + for (i = 0; mdstruct[i] && strcmp(name, mdstruct[i]); i++); + return (i < EndDir) || !strcmp(name, MDDIR) + || !strncmp(name, MDUIDLAST, strlen(MDUIDLAST)) + || !strncmp(name, MDUIDTEMP, strlen(MDUIDTEMP)) ? LONGT : NIL; + } + + void + md_domain_name(void) + { + int i; + + strcpy(mdlocaldomain,mylocalhost ()); + for (i = 0; mdlocaldomain[i] ; i++) + if(mdlocaldomain[i] == '/') + mdlocaldomain[i] = '\057'; + else if (mdlocaldomain[i] == ':') + mdlocaldomain[i] = '\072'; + } + + char * + myrootdir(char *name) + { + return myhomedir(); + } + + char * + mdirpath(void) + { + char *path = maildir_parameters(GET_MDINBOXPATH,NIL); + return path ? (path[0] ? path : ".") : "Maildir"; + } + + /* remove the "#md/" or "#mc/" part from a folder name */ + char * + maildir_remove_root (char *name) + { + int courier = IS_COURIER(name), offset; + char realname[MAILTMPLEN]; + + offset = maildir_valid_name(name) ? (name[3] == '/' ? 4 : 3) : 0; + if(courier) + courier_realname(name+offset, realname); + else + strcpy(realname, name+offset); + return cpystr(realname); + } + + + /* Check validity of the name, we accept: + * a) #md/directory/folder + * b) #md/inbox + * A few considerations: We can only accept as valid + * a) names that start with #md/ and the directory exists or + * b) names that do not start with #md/ but are maildir directories (have + * the /cur, /tmp and /new structure) + */ + int maildir_valid_name (char *name) + { + char tmpname[MAILTMPLEN] = {'\0'}; + + if (mdfpath) + fs_give((void **)&mdfpath); + if (name && (name[0] != '#')) + sprintf(tmpname,"%s%s",MDPREFIX(CCLIENT), name); + mdfpath = cpystr(tmpname[0] ? tmpname : name); + + return IS_CCLIENT(name) || IS_COURIER(name); + } + + /* Check if the directory whose path is given by name is a valid maildir + * directory (contains /cur, /tmp and /new) + */ + int maildir_valid_dir (char *name) + { + int len; + DirNamesType i; + struct stat sbuf; + char tmp[MAILTMPLEN]; + + if(name[strlen(name) - 1] == '/') + name[strlen(name) - 1] = '\0'; + len = strlen(name); + for (i = Cur; i != EndDir; i++){ + MDFLD(tmp, name, i); + if (stat(tmp, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode)) + break; + } + name[len] = '\0'; + return (i == EndDir) ? T : NIL; + } + + void courier_realname(char *name, char *realname) + { + int i,j; + + if(!name) + return; + + for (i = 0, j = 0; i < MAILTMPLEN && j < strlen(name); j++, i++){ + realname[i] = name[j]; + if(name[j] == '/' && name[j+1] != '.' && name[j+1] != '%' + && name[j+1] != '*') + realname[++i] = '.'; + } + if(realname[i-1] == '.') + i--; + realname[i] = '\0'; + } + + + /* given a maildir folder, return its path. Memory freed by caller. Directory + * does not contain the trailing slash "/". On error NULL is returned. + */ + int maildir_file_path (char *name, char *tmp) + { + char *maildirpath = mdirpath(), *rname; + int courier = IS_COURIER(name); + + /* There are several ways in which the path can come, so we will handle + them here. First we deal with #mc/ or #md/ prefix by removing the + prefix, if any */ + + if(strlen(name) >= MAILTMPLEN) + name[MAILTMPLEN] = '\0'; + strcpy(tmp, name); + rname = maildir_remove_root(tmp); + tmp[0] = '\0'; /* just in case something fails */ + + if (strlen(myrootdir(rname)) + + max(strlen(rname), strlen(maildirpath)) > MAILTMPLEN){ + errno = ENAMETOOLONG; + sprintf(tmp,"Error opening \"%s\": %s", rname, strerror (errno)); + mm_log(tmp,ERROR); + return NIL; + } + + /* There are two ways in which the name can come here, either as a + full path or not. If it is not a full path it can come in two ways, + either as a file system path (Maildir/.Drafts) or as a maildir path + (INBOX.Drafts) + */ + + if(*rname == '/') /* full path */ + strcpy(tmp, rname); /* do nothing */ + else{ + sprintf (tmp,"%s/%s%s%s", myrootdir (rname), + strncmp (ucase (strcpy (tmp, rname)), "INBOX", 5) + ? rname : maildirpath, + strncmp (ucase (strcpy (tmp, rname)), "INBOX", 5) + ? "" : (courier ? "/" : ""), + strncmp (ucase (strcpy (tmp, rname)), "INBOX", 5) + ? "" : (*(rname+5) == MDSEPARATOR(courier) ? rname+5 : "")); + } + if(rname) fs_give((void **)&rname); + return tmp[0] ? T : NIL; + } + + /* This function is given a full path for a mailbox and returns + * if it is a valid maildir transformed to canonical notation + */ + int + is_valid_maildir (char **name) + { + if (!strncmp(*name, myrootdir (*name), strlen(myrootdir(*name)))){ + (*name) += strlen(myrootdir(*name)); + if (**name == '/') (*name)++; + } + return maildir_valid(*name) ? T : NIL; + } + + /* Check validity of mailbox. This routine does not send errors to log, other + * routines calling this one may do so, though + */ + + DRIVER *maildir_valid (char *name) + { + char tmpname[MAILTMPLEN]; + + maildir_file_path(name, tmpname); + + return maildir_valid_dir(tmpname) + ? (IS_COURIER(name) ? &courierdriver : &maildirdriver) : NIL; + } + + void maildir_fast (MAILSTREAM *stream,char *sequence,long flags) + { + unsigned long i; + MESSAGECACHE *elt; + /* get sequence */ + if (stream && LOCAL && ((flags & FT_UID) ? + mail_uid_sequence (stream,sequence) : + mail_sequence (stream,sequence))) + for (i = 1L; i <= stream->nmsgs; i++) { + if ((elt = mail_elt (stream,i))->sequence && (elt->valid = T) && + !(elt->day && elt->rfc822_size)) { + ENVELOPE **env = NIL; + ENVELOPE *e = NIL; + if (!stream->scache) env = &elt->private.msg.env; + else if (stream->msgno == i) env = &stream->env; + else env = &e; + if (!*env || !elt->rfc822_size) { + STRING bs; + unsigned long hs; + char *ht = (*stream->dtb->header) (stream,i,&hs,NIL); + + if (!*env) rfc822_parse_msg (env,NIL,ht,hs,NIL,BADHOST, + stream->dtb->flags); + if (!elt->rfc822_size) { + (*stream->dtb->text) (stream,i,&bs,FT_PEEK); + elt->rfc822_size = hs + SIZE (&bs) - GETPOS (&bs); + } + } + + if (!elt->day && *env && (*env)->date) + mail_parse_date (elt,(*env)->date); + + if (!elt->day) elt->day = elt->month = 1; + mail_free_envelope (&e); + } + } + } + + int + maildir_eliminate_duplicate (char *name, struct direct ***flist, unsigned long *nfiles) + { + int i, j, k, error = 0, scanr; + char new[MAILTMPLEN], old[MAILTMPLEN], tmp[MAILTMPLEN], *str; + struct direct **names = NIL; + + if((scanr = maildir_doscandir(name, &names, CCLIENT)) < 0) + return -1; + + if(nfiles) *nfiles = scanr; + for(i = 0, j = 1, k = 0; j < scanr; i++, j++){ + if(k) + names[i] = names[i+k]; + if(same_maildir_file(names[i]->d_name, names[j]->d_name)){ + int d, f, r, s; + maildir_getflag(names[i]->d_name, &d, &f, &r, &s, NIL); + sprintf(old,"%s/%s", name, names[i]->d_name); + sprintf(new,"%s/.%s", name, names[i]->d_name); + if(rename(old, new) < 0 && errno != EEXIST) + error++; + if(!error){ + for(; j < scanr + && same_maildir_file(names[i]->d_name, names[j]->d_name) + ; j++, k++){ + maildir_getflag(names[j]->d_name, (d ? NIL : &d), + (f ? NIL : &f), (r ? NIL : &r), (s ? NIL : &s), NIL); + sprintf(tmp,"%s/%s", name, names[j]->d_name); + if(unlink(tmp) < 0){ /* Hmmm... a problem, let's see */ + struct stat sbuf; + if (stat(tmp, &sbuf) == 0 && (sbuf.st_mode & S_IFMT) == S_IFREG) + error++; + } + } + if((str = strrchr(names[i]->d_name,FLAGSEP)) != NULL) *str = '\0'; + sprintf (old,"%s/%s%s%s%s%s%s", name, names[i]->d_name, MDSEP(2), + MDFLAG(Draft, d), MDFLAG(Flagged, f), MDFLAG(Replied, r), + MDFLAG(Seen, s)); + if(rename(new, old) < 0) + error++; + } + } + + } + if(k > 0) + fs_give((void **)&names); + else + *flist = names; + return error ? -1 : k; + } + + int + maildir_doscandir(char *name, struct direct ***flist, int flag) + { + return scandir(name, flist, + flag == CCLIENT ? maildir_select : courier_dir_select, + flag == CCLIENT ? maildir_namesort : courier_dir_sort); + } + + /* + * return all files in a given directory. This is a separate call + * so that if there are warnings during compilation this only appears once. + */ + unsigned long + maildir_scandir (char *name, struct direct ***flist, + unsigned long *nfiles, int *scand, int flag) + { + struct stat sbuf; + int rv = -2; /* impossible value */ + + if (scand) + *scand = -1; /* assume error for safety */ + *nfiles = 0; + if((stat(name,&sbuf) < 0) + || (flag == CCLIENT + && ((rv = maildir_eliminate_duplicate(name, flist, nfiles)) < 0))) + return 0L; + + if (scand && (rv > 0 || rv == -2)) + *nfiles = maildir_doscandir(name, flist, flag); + + if(scand) *scand = *nfiles; + + return (unsigned long) sbuf.st_ctime; + } + + /* Does a message with given name exists (or was it removed)? + * Returns: 1 - yes, such message exist, + * 0 - No, that message does not exist anymore + * + * Parameters: stream, name of mailbox, new name if his message does not + * exist. + */ + + int maildir_message_exists(MAILSTREAM *stream, char *name, char *newfile) + { + char tmp[MAILTMPLEN]; + int gotit = NIL; + DIR *dir; + struct direct *d; + struct stat sbuf; + + /* First check directly if it exists, if not there, look for it */ + sprintf(tmp,"%s/%s", LOCAL->curdir, name); + if ((stat(tmp, &sbuf) == 0) && ((sbuf.st_mode & S_IFMT) == S_IFREG)) + return T; + + if (!(dir = opendir (LOCAL->curdir))) + return NIL; + + while ((d = readdir(dir)) && gotit == NIL){ + if (d->d_name[0] == '.') + continue; + if (same_maildir_file(d->d_name, name)){ + gotit = T; + strcpy(newfile, d->d_name); + } + } + closedir(dir); + return gotit; + } + + /* Maildir open */ + + MAILSTREAM *maildir_open (MAILSTREAM *stream) + { + char tmp[MAILTMPLEN]; + struct stat sbuf; + + if (!stream) return &maildirproto; + if (stream->local) fatal ("maildir recycle stream"); + md_domain_name(); /* get domain name for maildir files in mdlocaldomain */ + if(mypid == (pid_t) 0) + mypid = getpid(); + if (!stream->rdonly){ + stream->perm_seen = stream->perm_deleted = stream->perm_flagged = + stream->perm_answered = stream->perm_draft = T; + } + stream->local = (MAILDIRLOCAL *) fs_get (sizeof (MAILDIRLOCAL)); + memset(LOCAL, 0, sizeof(MAILDIRLOCAL)); + LOCAL->fd = -1; + + LOCAL->courier = IS_COURIER(stream->mailbox); + strcpy(tmp, stream->mailbox); + if (maildir_file_path (stream->mailbox, tmp)) + LOCAL->dir = cpystr (tmp); + LOCAL->candouid = maildir_can_assign_uid(stream); + maildir_read_uid(stream, &stream->uid_last, &stream->uid_validity); + if (LOCAL->dir){ + MDFLD(tmp, LOCAL->dir, Cur); + LOCAL->curdir = cpystr (tmp); + if (stat (LOCAL->curdir,&sbuf) < 0) { + sprintf (tmp,"Can't open folder %s: %s", + stream->mailbox,strerror (errno)); + mm_log (tmp,ERROR); + maildir_close(stream, 0); + return NIL; + } + } + + if(maildir_file_path (stream->mailbox, tmp)){ + fs_give ((void **) &stream->mailbox); + stream->mailbox = cpystr(tmp); + } + + LOCAL->buf = (char *) fs_get ((LOCAL->buflen = SENDBUFLEN) + 1); + stream->sequence++; + stream->nmsgs = stream->recent = 0L; + + maildir_parse_folder(stream, 1); + + return stream; + } + + /* Maildir initial parsing of the folder */ + void + maildir_parse_folder (MAILSTREAM *stream, int full) + { + char tmp[MAILTMPLEN], tmp2[MAILTMPLEN]; + struct direct **namescur = NIL, **namesnew = NIL; + unsigned long i, nfilescur = 0L, nfilesnew = 0L, oldpos, newpos, total; + int scan_err, rescan, loop = 0; + + if (!stream) /* what??? */ + return; + + MM_CRITICAL(stream); + + MDFLD(tmp, LOCAL->dir, New); + maildir_scandir (tmp, &namesnew, &nfilesnew, &scan_err, CCLIENT); + if (scan_err < 0) + maildir_abort(stream); + + /* Scan old messages first, escoba! */ + if(stream->rdonly || + (LOCAL && ((maildir_initial_check(stream, Cur) == 0) + || nfilesnew > 0L))){ + MDFLD(tmp, LOCAL->dir, Cur); + LOCAL->scantime = maildir_scandir (tmp, &namescur, &nfilescur, + &scan_err, CCLIENT); + if (scan_err < 0){ + if(namesnew){ + for(i = 0L; i < nfilesnew; i++) + fs_give((void **)&namesnew[i]); + fs_give((void **) &namesnew); + } + maildir_abort(stream); + } + } + if(LOCAL && (maildir_initial_check(stream, New) == 0) + && (nfilescur > 0L)){ + MDFLD(tmp, LOCAL->dir, New); + while(LOCAL && loop < 10){ + if(nfilesnew == 0L) + maildir_scandir (tmp, &namesnew, &nfilesnew, &scan_err, CCLIENT); + if (scan_err < 0){ + if(namesnew){ + for(i = 0L; i < nfilesnew; i++) + fs_give((void **)&namesnew[i]); + fs_give((void **) &namesnew); + } + maildir_abort(stream); + break; + } + for(i = 0L, rescan = 0, newpos = oldpos = 0L; + newpos < nfilescur && i < nfilesnew; i++){ + if(maildir_message_in_list(namesnew[i]->d_name, namescur, oldpos, + nfilescur - 1L, &newpos)){ + oldpos = newpos; + sprintf(tmp2,"%s/%s",tmp,namesnew[i]->d_name); + if(unlink(tmp2) < 0) + scan_err = -1; + rescan++; + } + else + newpos = oldpos; + } + if(scan_err < 0) + maildir_abort(stream); + if(rescan == 0) + break; + else{ /* restart */ + if(namesnew){ + for(i = 0L; i < nfilesnew; i++) + fs_give((void **)&namesnew[i]); + fs_give((void **) &namesnew); + } + nfilesnew = 0L; + loop++; + } + } + } + if(loop == 10) + maildir_abort(stream); + if(LOCAL){ + if(stream->rdonly) + stream->recent = 0L; + total = namescur || stream->rdonly + ? maildir_parse_dir(stream, 0L, Cur, namescur, + nfilescur, full) : stream->nmsgs; + stream->nmsgs = maildir_parse_dir(stream, total, New, namesnew, + nfilesnew, full); + } + if(namesnew){ + for(i = 0L; i < nfilesnew; i++) + fs_give((void **)&namesnew[i]); + fs_give((void **) &namesnew); + } + if(namescur){ + for(i = 0L; i < nfilescur; i++) + fs_give((void **)&namescur[i]); + fs_give((void **) &namescur); + } + MM_NOCRITICAL(stream); + } + + int + maildir_initial_check (MAILSTREAM *stream, DirNamesType dirtype) + { + char tmp[MAILTMPLEN]; + struct stat sbuf; + + MDFLD(tmp, LOCAL->dir, dirtype); + if (access (tmp, R_OK|W_OK|X_OK) != 0){ + maildir_abort(stream); + return -1; + } + + MDFLD(tmp, LOCAL->dir, Cur); + if (dirtype != New && + (stat(tmp, &sbuf) < 0 || sbuf.st_ctime == LOCAL->scantime)) + return -1; + return 0; + } + + + /* Return the number of messages in the directory, while filling the + * elt structure. + */ + + unsigned long + maildir_parse_dir(MAILSTREAM *stream, unsigned long nmsgs, + DirNamesType dirtype, struct direct **names, + unsigned long nfiles, int full) + { + char tmp[MAILTMPLEN], tmp2[MAILTMPLEN], file[MAILTMPLEN], + newfile[MAILTMPLEN], *mdstr; + struct stat sbuf; + unsigned long i, new = 0L, l, uid_last; + unsigned long recent = stream ? stream->recent : 0L; + int d = 0, f = 0, r = 0, s = 0, t = 0; + int we_compute, in_list; + int silent = stream ? stream->silent : NIL; + MESSAGECACHE *elt; + + MDFLD(tmp, LOCAL->dir, dirtype); + if (dirtype == Cur && !stream->rdonly) + for (i = 1L; i <= stream->nmsgs;){ + elt = mail_elt(stream, i); + in_list = elt && elt->private.spare.ptr && nfiles > 0L + ? (MDPOS(elt) < nfiles + ? same_maildir_file(MDFILE(elt), names[MDPOS(elt)]->d_name) + : NIL) + || maildir_message_in_list(MDFILE(elt), names, 0L, + nfiles - 1L, &MDPOS(elt)) + : NIL; + if (!in_list){ + if (elt->private.spare.ptr) + maildir_free_file ((void **) &elt->private.spare.ptr); + + if (elt->recent) --recent; + mail_expunged(stream,i); + } + else i++; + } + + stream->silent = T; + uid_last = 0L; + for (we_compute = 0, i = l = 1L; l <= nfiles; l++){ + unsigned long pos, uid; + if (dirtype == New && !stream->rdonly){ /* move new messages to cur */ + pos = l - 1L; + sprintf (file,"%s/%s", tmp, names[pos]->d_name); + if(lstat(file,&sbuf) == 0) + switch(sbuf.st_mode & S_IFMT){ + case S_IFREG: + strcpy(tmp2, names[pos]->d_name); + if((mdstr = strstr(tmp2,MDSEP(3))) + || (mdstr = strstr(tmp2,MDSEP(2)))) + *(mdstr+1) = '2'; + else + strcat(tmp2, MDSEP(2)); + sprintf(newfile, "%s/%s",LOCAL->curdir, tmp2); + if(rename (file, newfile) != 0){ + mm_log("Unable to read new mail!", WARN); + continue; + } + unlink (file); + new++; + break; + case S_IFLNK: /* clean up, clean up, everybody, everywhere */ + if(unlink(file) < 0){ + if(LOCAL->link == NIL){ + mm_log("Unable to remove symbolic link", WARN); + LOCAL->link = T; + } + } + continue; + break; + default: + if(LOCAL && LOCAL->link == NIL){ + mm_log("Unrecognized file or link in folder", WARN); + LOCAL->link = T; + } + continue; + break; + } + } + mail_exists(stream, i + nmsgs); + elt = mail_elt(stream, i + nmsgs); + pos = (elt && elt->private.spare.ptr) ? MDPOS(elt) : l - 1L; + if (dirtype == New) elt->recent = T; + maildir_getflag(names[pos]->d_name, &d, &f, &r ,&s, &t); + if (elt->private.spare.ptr) + maildir_free_file_only ((void **)&elt->private.spare.ptr); + else{ + maildir_get_file((MAILDIRFILE **)&elt->private.spare.ptr); + we_compute++; + } + MDFILE(elt) = cpystr(names[pos]->d_name); + MDPOS(elt) = pos; + MDLOC(elt) = dirtype; + if (dirtype == Cur){ /* deal with UIDs */ + if(elt->private.uid == 0L) + elt->private.uid = maildir_get_uid(MDFILE(elt)); + if(elt->private.uid <= uid_last){ + uid = (we_compute ? uid_last : stream->uid_last) + 1L; + if(LOCAL->candouid) + maildir_assign_uid(stream, i + nmsgs, uid); + else + elt->private.uid = uid; + } + else + uid = elt->private.uid; + uid_last = uid; + if(uid_last > stream->uid_last) + stream->uid_last = uid_last; + } + if(dirtype == New && !stream->rdonly){ + maildir_free_file_only((void **)&elt->private.spare.ptr); + MDFILE(elt) = cpystr(tmp2); + MDSIZE(elt) = sbuf.st_size; + MDMTIME(elt) = sbuf.st_mtime; + MDLOC(elt) = Cur; + } + if (elt->draft != d || elt->flagged != f || + elt->answered != r || elt->seen != s || elt->deleted != t){ + elt->draft = d; elt->flagged = f; elt->answered = r; + elt->seen = s; elt->deleted = t; + if (!we_compute && !stream->rdonly) + MM_FLAGS(stream, i+nmsgs); + } + maildir_get_date(stream, i+nmsgs); + elt->valid = T; + i++; + } + stream->silent = silent; + if(LOCAL->candouid && dirtype == Cur) + maildir_read_uid(stream, NULL, &stream->uid_validity); + if (dirtype == New && stream->rdonly) + new = nfiles; + mail_exists(stream, nmsgs + ((dirtype == New) ? new : nfiles)); + mail_recent(stream, recent + ((dirtype == New) ? new : 0L)); + + return (nmsgs + (dirtype == New ? new : nfiles)); + } + + long maildir_ping (MAILSTREAM *stream) + { + maildir_parse_folder(stream, 0); + if(stream && LOCAL){ + if(LOCAL->candouid) + maildir_uid_renew_tempfile(stream); + else /* try again to get uids */ + LOCAL->candouid = maildir_can_assign_uid(stream); + } + return stream && LOCAL ? LONGT : NIL; + } + + int maildir_select (const struct direct *name) + { + return (name->d_name[0] != '.'); + } + + /* + * Unfortunately, there is no way to sort by arrival in this driver, this + * means that opening a folder in this driver using the scandir function + * will always make this driver slower than any driver that has a natural + * way of sorting by arrival (like a flat file format, "mbox", "mbx", etc). + */ + int maildir_namesort (const void *d1, const void *d2) + { + const struct direct *e1 = *(const struct direct **) d1; + const struct direct *e2 = *(const struct direct **) d2; + + return comp_maildir_file((char *) e1->d_name, (char *) e2->d_name); + } + + /* Maildir close */ + + void maildir_close (MAILSTREAM *stream, long options) + { + MESSAGECACHE *elt; + unsigned long i; + int silent = stream ? stream->silent : 0; + mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL); + + if (!stream) return; + + for (i = 1L; i <= stream->nmsgs; i++) + if((elt = (MESSAGECACHE *) (*mc)(stream,i,CH_ELT)) && elt->private.spare.ptr) + maildir_free_file ((void **) &elt->private.spare.ptr); + stream->silent = T; + if (options & CL_EXPUNGE) maildir_expunge (stream, NIL, NIL); + maildir_abort(stream); + if (mdfpath) fs_give((void **)&mdfpath); + if (mypid) mypid = (pid_t) 0; + stream->silent = silent; + } + + void maildir_check (MAILSTREAM *stream) + { + if (maildir_ping (stream)) mm_log ("Check completed",(long) NIL); + } + + long maildir_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs, long flags) + { + char tmp[MAILTMPLEN]; + unsigned long i; + MESSAGECACHE *elt; + char *s; + /* UID call "impossible" */ + if (flags & FT_UID || !LOCAL) return NIL; + elt = mail_elt (stream, msgno); + + if (!(flags & FT_PEEK) && !elt->seen){ + elt->seen = T; + maildir_flagmsg (stream, elt); + MM_FLAGS(stream, elt->msgno); + } + + MSGPATH(tmp, LOCAL->dir, MDFILE(elt), MDLOC(elt)); + if (LOCAL->fd < 0) /* if file closed ? */ + LOCAL->fd = open(tmp,O_RDONLY,NIL); + + if (LOCAL->fd < 0 && (errno == EACCES || errno == ENOENT)){ + INIT (bs, mail_string, "", 0); + elt->rfc822_size = 0L; + return NIL; + } + + s = maildir_text_work(stream, elt, &i, flags); + INIT (bs, mail_string, s, i); + return LONGT; + } + + char *maildir_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, + unsigned long *length,long flags) + { + FDDATA d; + STRING bs; + char *s,tmp[CHUNK]; + unsigned long msgno = elt->msgno; + static int try = 0; + + if (length) + *length = 0L; + LOCAL->buf[0] = '\0'; + + MSGPATH(tmp, LOCAL->dir, MDFILE(elt), MDLOC(elt)); + if (LOCAL->fd < 0) /* if file closed ? */ + LOCAL->fd = open(tmp,O_RDONLY,NIL); + + if (LOCAL->fd < 0){ /* flag change? */ + if (try < 5){ + try++; + if (maildir_update_elt_maildirp(stream, msgno) > 0) + try = 0; + return maildir_text_work(stream, mail_elt(stream, msgno),length, flags); + } + try = 0; + return NULL; + } + + lseek (LOCAL->fd, elt->private.msg.text.offset,L_SET); + + if (flags & FT_INTERNAL) { /* initial data OK? */ + if (elt->private.msg.text.text.size > LOCAL->buflen) { + fs_give ((void **) &LOCAL->buf); + LOCAL->buf = (char *) fs_get ((LOCAL->buflen = + elt->private.msg.text.text.size) + 1); + } + read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size); + LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0'; + } + else { + if (elt->rfc822_size > LOCAL->buflen) { + fs_give ((void **) &LOCAL->buf); + LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->rfc822_size) + 1); + } + d.fd = LOCAL->fd; /* yes, set up file descriptor */ + d.pos = elt->private.msg.text.offset; + d.chunk = tmp; /* initial buffer chunk */ + d.chunksize = CHUNK; + INIT (&bs,fd_string,&d,elt->private.msg.text.text.size); + for (s = LOCAL->buf; SIZE (&bs);) switch (CHR (&bs)) { + case '\r': /* carriage return seen */ + *s++ = SNX (&bs); /* copy it and any succeeding LF */ + if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs); + break; + case '\n': + *s++ = '\r'; /* insert a CR */ + default: + *s++ = SNX (&bs); /* copy characters */ + } + *s = '\0'; /* tie off buffer */ + *length = s - (char *) LOCAL->buf; /* calculate length */ + } + close(LOCAL->fd); LOCAL->fd = -1; + return LOCAL->buf; + } + + /* maildir parse, fill the elt structure... well not all of it... */ + unsigned long maildir_parse_message(MAILSTREAM *stream, unsigned long msgno, + DirNamesType dirtype) + { + char *b, *s, *t, c; + char tmp[MAILTMPLEN]; + struct stat sbuf; + unsigned long i, len; + int d, f, r, se, dt; + MESSAGECACHE *elt; + + elt = mail_elt (stream,msgno); + MSGPATH(tmp, LOCAL->dir, MDFILE(elt), dirtype); + if(stat(tmp, &sbuf) == 0) + MDSIZE(elt) = sbuf.st_size; + + maildir_get_date(stream, msgno); + maildir_getflag(MDFILE(elt), &d, &f, &r ,&se, &dt); + elt->draft = d; elt->flagged = f; elt->answered = r; elt->seen = se; + elt->deleted = dt; elt->valid = T; + if (LOCAL->fd < 0) /* if file closed ? */ + LOCAL->fd = open(tmp,O_RDONLY,NIL); + + if (LOCAL->fd >= 0){ + s = (char *) fs_get (MDSIZE(elt) + 1); + read (LOCAL->fd,s,MDSIZE(elt)); + s[MDSIZE(elt)] = '\0'; + t = s + strlen(s); /* make t point to the end of s */ + for (i = 0L, b = s; b < t && !(i && (*b == '\n')); i = (*b++ == '\n')); + len = (*b ? ++b : b) - s; + elt->private.msg.header.text.size = + elt->private.msg.text.offset = len; + elt->private.msg.text.text.size = MDSIZE(elt) - len; + for (i = 0L, b = s, c = *b; b && + ((c < '\016' && ((c == '\012' && ++i) + ||(c == '\015' && *(b+1) == '\012' && ++b && (i +=2)))) + || b < t); i++, c= *++b); + elt->rfc822_size = i; + fs_give ((void **) &s); + close(LOCAL->fd); LOCAL->fd = -1; + } + return elt->rfc822_size; + } + + int + maildir_update_elt_maildirp(MAILSTREAM *stream, unsigned long msgno) + { + char tmp[MAILTMPLEN]; + struct direct **names = NIL; + unsigned long i, nfiles, pos; + int d = 0, f = 0 , r = 0, s = 0, t = 0, in_list, scan_err; + MESSAGECACHE *elt; + + MDFLD(tmp, LOCAL->dir, Cur); + + maildir_scandir (tmp, &names, &nfiles, &scan_err, CCLIENT); + + elt = mail_elt (stream,msgno); + + in_list = nfiles > 0L + ? maildir_message_in_list(MDFILE(elt), names, 0L, nfiles - 1L, &pos) + : NIL; + + if (in_list && pos >= 0L && pos < nfiles + && !strcmp(MDFILE(elt), names[pos]->d_name)){ + in_list = NIL; + maildir_abort(stream); + } + + if (in_list && pos >= 0L && pos < nfiles){ + maildir_free_file_only((void **)&elt->private.spare.ptr); + MDFILE(elt) = cpystr(names[pos]->d_name); + maildir_getflag(MDFILE(elt), &d, &f, &r ,&s, &t); + if (elt->draft != d || elt->flagged != f || + elt->answered != r || elt->seen != s || elt->deleted != t){ + elt->draft = d; elt->flagged = f; elt->answered = r; + elt->seen = s; elt->deleted = t; + MM_FLAGS(stream, msgno); + } + } + for (i = 0L; i < nfiles; i++) + fs_give((void **) &names[i]); + if (names) + fs_give((void **) &names); + return in_list ? 1 : -1; + } + + /* Maildir fetch message header */ + + char *maildir_header (MAILSTREAM *stream,unsigned long msgno, + unsigned long *length, long flags) + { + char tmp[MAILTMPLEN], *s; + MESSAGECACHE *elt; + static int try = 0; + + if (length) *length = 0; + if (flags & FT_UID || !LOCAL) return ""; /* UID call "impossible" */ + elt = mail_elt (stream,msgno); + if(elt->private.msg.header.text.size == 0) + maildir_parse_message(stream, msgno, MDLOC(elt)); + + MSGPATH(tmp, LOCAL->dir, MDFILE(elt), MDLOC(elt)); + if (LOCAL->fd < 0) + LOCAL->fd = open (tmp,O_RDONLY,NIL); + + if (LOCAL->fd < 0 && errno == EACCES){ + mm_log ("Message exists but can not be read. Envelope and body lost!",ERROR); + return NULL; + } + + if (LOCAL->fd < 0){ /* flag change? */ + if (try < 5){ + try++; + if (maildir_update_elt_maildirp(stream, msgno) > 0) + try = 0; + return maildir_header(stream, msgno, length, flags); + } + try = 0; + return NULL; + } + + if (flags & FT_INTERNAL){ + if(elt->private.msg.header.text.size > LOCAL->buflen){ + fs_give ((void **) &LOCAL->buf); + LOCAL->buf = (char *) fs_get ((LOCAL->buflen = + elt->private.msg.header.text.size) + 1); + } + read (LOCAL->fd, (void *)LOCAL->buf, elt->private.msg.header.text.size); + LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0'; + } + else{ + s = (char *) fs_get(elt->private.msg.header.text.size+1); + read (LOCAL->fd, (void *)s, elt->private.msg.header.text.size); + s[elt->private.msg.header.text.size] = '\0'; + *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s, + elt->private.msg.header.text.size); + fs_give ((void **) &s); + } + elt->private.msg.text.offset = elt->private.msg.header.text.size; + elt->private.msg.text.text.size = MDSIZE(elt) - elt->private.msg.text.offset; + close(LOCAL->fd); LOCAL->fd = -1; + return LOCAL->buf; + } + + /* Maildir find list of subscribed mailboxes + * Accepts: mail stream + * pattern to search + */ + + void maildir_list (MAILSTREAM *stream,char *ref, char *pat) + { + char *s,test[MAILTMPLEN],file[MAILTMPLEN]; + long i = 0L; + + if((!pat || !*pat) && maildir_canonicalize (test,ref,"*") + && maildir_valid_name(test)){ /* there is a #md/ leading here */ + for (i = 3L; test[i] && test[i] != '/'; i++); + if ((s = strchr (test+i+1,'/')) != NULL) *++s = '\0'; + else test[0] = '\0'; + mm_list (stream,'/',test, LATT_NOSELECT); + } + else if (maildir_canonicalize (test,ref,pat)) { + if (test[3] == '/') { /* looking down levels? */ + /* yes, found any wildcards? */ + if ((s = strpbrk (test,"%*")) != NULL){ + /* yes, copy name up to that point */ + strncpy (file,test+4,i = s - (test+4)); + file[i] = '\0'; /* tie off */ + } + else strcpy (file,test+4);/* use just that name then */ + /* find directory name */ + if ((s = strrchr (file, '/')) != NULL){ + *s = '\0'; /* found, tie off at that point */ + s = file; + } + /* do the work */ + if(IS_COURIER(test)) + courier_list_work (stream,s,test,0); + else + maildir_list_work (stream,s,test,0); + } + /* always an INBOX */ + if (!compare_cstring (test,"#MD/INBOX")) + mm_list (stream,NIL,"#MD/INBOX",LATT_NOINFERIORS); + if (!compare_cstring (test,"#MC/INBOX")) + mm_list (stream,NIL,"#MC/INBOX",LATT_NOINFERIORS); + } + } + + void courier_list (MAILSTREAM *stream,char *ref, char *pat) + { + /* I am too lazy to do anything. Do you care to ask maildir list, please? + The real reason why this is a dummy function is because we do not want to + see the same folder listed twice. + */ + } + + /* For those that want to hide things, we give them a chance to do so */ + void *maildir_parameters (long function, void *value) + { + void *ret = NIL; + switch ((int) function) { + case SET_MDINBOXPATH: + if(strlen((char *) value ) > 49) + strcpy(myMdInboxDir, "Maildir"); + else + strcpy(myMdInboxDir, (char *) value); + case GET_MDINBOXPATH: + if (myMdInboxDir[0] == '\0') strcpy(myMdInboxDir,"Maildir"); + ret = (void *) myMdInboxDir; + break; + case SET_COURIERSTYLE: + CourierStyle = (long) value; + case GET_COURIERSTYLE: + ret = (void *) CourierStyle; + break; + case GET_DIRFMTTEST: + ret = (void *) maildir_dirfmttest; + break; + default: + break; + } + return ret; + } + + int maildir_create_folder(char *mailbox) + { + char tmp[MAILTMPLEN], err[MAILTMPLEN]; + int i; + + for (i = Cur; i != EndDir; i++){ + MDFLD(tmp, mailbox, i); + if (mkdir(tmp, 0700) && errno != EEXIST){ /* try to make new dir */ + sprintf (err, "Can't create %s: %s", tmp, strerror(errno)); + mm_log (err,ERROR); + return NIL; + } + } + return T; + } + + int maildir_create_work(char *mailbox, int loop) + { + char *s, c, err[MAILTMPLEN], tmp[MAILTMPLEN], tmp2[MAILTMPLEN], mbx[MAILTMPLEN]; + int fnlen, create_dir = 0, courier, mv; + struct stat sbuf; + long style = (long) maildir_parameters(GET_COURIERSTYLE, NIL); + + courier = IS_COURIER(mailbox); + strcpy(mbx, mailbox); + mv = maildir_valid(mbx) ? 1 : 0; + maildir_file_path(mailbox, tmp); + if (mailbox[strlen(mailbox) - 1] == MDSEPARATOR(courier)){ + create_dir++; + mailbox[strlen(mailbox) - 1] = '\0'; + } + + if(!loop && courier){ + if(mv){ + if(create_dir){ + if(style == CCLIENT) + strcpy (err,"Can not create directory: folder exists. Create subfolder"); + else + strcpy(err,"Folder and Directory already exist"); + } + else + strcpy (err, "Can't create mailbox: mailbox already exists"); + } + else{ + if(create_dir) + strcpy(err, "Can not create directory. Cread folder instead"); + else + err[0] = '\0'; + } + if(err[0]){ + mm_log (err,ERROR); + return NIL; + } + } + + fnlen = strlen(tmp); + if ((s = strrchr(mailbox,MDSEPARATOR(courier))) != NULL){ + c = *++s; + *s = '\0'; + if ((stat(tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && + !maildir_create_work (mailbox, ++loop)) + return NIL; + *s = c; + } + tmp[fnlen] = '\0'; + + if (mkdir(tmp,0700) && errno != EEXIST) + return NIL; + + if (create_dir) + mailbox[fnlen] = '/'; + + if (create_dir){ + if(style == CCLIENT){ + if(!courier){ + FILE *fp = NULL; + sprintf(tmp2,"%s%s", tmp, MDDIR); + if ((fp = fopen(tmp2,"w")) == NULL){ + sprintf (err,"Problem creating %s: %s", tmp2, strerror(errno)); + mm_log (err,ERROR); + return NIL; + } + fclose(fp); + } + } + return T; + } + else + return maildir_create_folder(tmp); + } + + long maildir_create (MAILSTREAM *stream,char *mailbox) + { + char tmp[MAILTMPLEN], err[MAILTMPLEN]; + int rv, create_dir; + + create_dir = mailbox ? + (mailbox[strlen(mailbox) - 1] == + MDSEPARATOR(IS_COURIER(mailbox))) : 0; + maildir_file_path(mailbox, tmp); + strcpy(tmp, mailbox); + rv = maildir_create_work(mailbox, 0); + strcpy(mailbox, tmp); + if (rv == 0){ + sprintf (err,"Can't create %s %s", + (create_dir ? "directory" : "mailbox"), mailbox); + mm_log (err,ERROR); + } + return rv ? LONGT : NIL; + } + + #define MAXTRY 10000 + void maildir_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) + { + char oldfile[MAILTMPLEN],newfile[MAILTMPLEN],fn[MAILTMPLEN]; + char *s; + int ren, try = 0; + + if (elt->valid){ + for (try = 1; try > 0 && try < MAXTRY; try++){ + /* build the new filename */ + sprintf (oldfile,"%s/%s",LOCAL->curdir, MDFILE(elt)); + fn[0] = '\0'; + if ((ren = maildir_message_exists(stream, MDFILE(elt), fn)) == 0){ + errno = ENOENT; + try = MAXTRY; + } + if (*fn) /* new oldfile! */ + sprintf (oldfile,"%s/%s",LOCAL->curdir,fn); + if ((s = strrchr (MDFILE(elt), FLAGSEP))) *s = '\0'; + sprintf (fn,"%s%s%s%s%s%s%s", MDFILE(elt), MDSEP(2), + MDFLAG(Draft, elt->draft), MDFLAG(Flagged, elt->flagged), + MDFLAG(Replied, elt->answered), MDFLAG(Seen, elt->seen), + MDFLAG(Trashed, elt->deleted)); + sprintf (newfile,"%s/%s",LOCAL->curdir,fn); + if (ren != 0 && rename (oldfile,newfile) >= 0) + try = -1; + } + + if (try > 0){ + sprintf(oldfile,"Unable to write flags to disk: %s", + (errno == ENOENT) ? "message is gone!" : strerror (errno)); + mm_log(oldfile,ERROR); + return; + } + #ifdef __CYGWIN__ + utime(LOCAL->curdir, NIL); /* make sure next scan will catch the change */ + #endif + maildir_free_file_only ((void **) &elt->private.spare.ptr); + MDFILE(elt) = cpystr (fn); + } + } + + long maildir_expunge (MAILSTREAM *stream, char *sequence, long options) + { + long ret; + MESSAGECACHE *elt; + unsigned long i, n = 0L; + unsigned long recent = stream->recent; + char tmp[MAILTMPLEN]; + + mm_critical (stream); /* go critical */ + ret = sequence ? ((options & EX_UID) ? + mail_uid_sequence (stream,sequence) : + mail_sequence (stream,sequence)) : LONGT; + if(ret == 0L) + return 0L; + for (i = 1L; i <= stream->nmsgs;){ + elt = mail_elt (stream,i); + if (elt->deleted && (sequence ? elt->sequence : T)){ + sprintf (tmp,"%s/%s",LOCAL->curdir, MDFILE(elt)); + if (unlink (tmp) < 0) {/* try to delete the message */ + sprintf (tmp,"Expunge of message %ld failed, aborted: %s",i, + strerror (errno)); + if (!stream->silent) + mm_log (tmp,WARN); + break; + } + if (elt->private.spare.ptr) + maildir_free_file ((void **) &elt->private.spare.ptr); + if (elt->recent) --recent;/* if recent, note one less recent message */ + mail_expunged (stream,i); /* notify upper levels */ + n++; /* count up one more expunged message */ + } + else i++; + } + if(n){ /* output the news if any expunged */ + sprintf (tmp,"Expunged %ld messages",n); + if (!stream->silent) + mm_log (tmp,(long) NIL); + } + else + if (!stream->silent) + mm_log ("No messages deleted, so no update needed",(long) NIL); + mm_nocritical (stream); /* release critical */ + /* notify upper level of new mailbox size */ + mail_exists (stream,stream->nmsgs); + mail_recent (stream,recent); + return ret; + } + + long maildir_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) + { + STRING st; + MESSAGECACHE *elt; + unsigned long len; + int fd; + unsigned long i; + struct stat sbuf; + char tmp[MAILTMPLEN], flags[MAILTMPLEN], path[MAILTMPLEN], *s; + /* copy the messages */ + if ((options & CP_UID) ? mail_uid_sequence (stream, sequence) : + mail_sequence (stream,sequence)) + for (i = 1L; i <= stream->nmsgs; i++) + if ((elt = mail_elt (stream,i))->sequence){ + MSGPATH(path, LOCAL->dir, MDFILE(elt), MDLOC(elt)); + if (((fd = open (path,O_RDONLY,NIL)) < 0) + ||((!elt->rfc822_size && + ((stat(path, &sbuf) < 0) || !S_ISREG (sbuf.st_mode))))) + return NIL; + if(!elt->rfc822_size) + MDSIZE(elt) = sbuf.st_size; + s = (char *) fs_get(MDSIZE(elt) + 1); + read (fd,s,MDSIZE(elt)); + s[MDSIZE(elt)] = '\0'; + close (fd); + len = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen, s, MDSIZE(elt)); + INIT (&st,mail_string, LOCAL->buf, len); + elt->rfc822_size = len; + fs_give ((void **)&s); + + flags[0] = flags[1] = '\0'; + if (elt->seen) strcat (flags," \\Seen"); + if (elt->draft) strcat (flags," \\Draft"); + if (elt->deleted) strcat (flags," \\Deleted"); + if (elt->flagged) strcat (flags," \\Flagged"); + if (elt->answered) strcat (flags," \\Answered"); + flags[0] = '('; /* open list */ + strcat (flags,")"); /* close list */ + mail_date (tmp,elt); /* generate internal date */ + if (!mail_append_full (NIL,mailbox,flags,tmp,&st)) + return NIL; + if (options & CP_MOVE) elt->deleted = T; + } + return LONGT; /* return success */ + } + + long maildir_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) + { + int fd, k; + STRING *message; + char c,*s, *flags, *date; + char tmp[MAILTMPLEN],file[MAILTMPLEN],path1[MAILTMPLEN],path2[MAILTMPLEN]; + MESSAGECACHE elt; + long i, size = 0L, ret = LONGT, f; + unsigned long uf, ti; + static unsigned int transact = 0; + + if (!maildir_valid(mailbox)) { + sprintf (tmp,"Not a valid Maildir mailbox: %s",mailbox); + mm_log (tmp,ERROR); + return NIL; + } + + if (!*mdlocaldomain) + md_domain_name(); /* get domain name for maildir files in mdlocaldomain now! */ + + if (mypid == (pid_t) 0) + mypid = getpid(); + + if (!stream){ + stream = &maildirproto; + + for (k = 0; k < NUSERFLAGS && stream->user_flags[k]; ++k) + fs_give ((void **) &stream->user_flags[k]); + } + + if (!(*af) (stream,data,&flags,&date,&message)) return NIL; + + mm_critical (stream); /* go critical */ + /* call time(0) only once, use transact to distinguish instead */ + ti = time(0); + do { + if (!SIZE (message)) { /* guard against zero-length */ + mm_log ("Append of zero-length message",ERROR); + ret = NIL; + break; + } + if (date && !mail_parse_date(&elt,date)){ + sprintf (tmp,"Bad date in append: %.80s",date); + mm_log (tmp,ERROR); + ret = NIL; + break; + } + f = mail_parse_flags (stream,flags,&uf); + /* build file name we will use */ + sprintf (file,"%lu.%d_%09u.%s%s%s%s%s%s", + ti, mypid, transact++, mdlocaldomain, (f ? MDSEP(2) : ""), + MDFLAG(Draft, f&fDRAFT), MDFLAG(Flagged, f&fFLAGGED), + MDFLAG(Replied, f&fANSWERED), MDFLAG(Seen, f&fSEEN)); + /* build tmp file name */ + if (maildir_file_path(mailbox, tmp)) + MSGPATH(path1, tmp, file, Tmp); + + if ((fd = open (path1,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0) { + sprintf (tmp, "Can't open append mailbox: %s", strerror (errno)); + mm_log (tmp, ERROR); + return NIL; + } + for (size = 0,i = SIZE (message),s = (char *) fs_get (i + 1); i; --i) + if ((c = SNX (message)) != '\015') s[size++] = c; + if ((write (fd, s, size) < 0) || fsync (fd)) { + unlink (path1); /* delete message */ + sprintf (tmp, "Message append failed: %s", strerror (errno)); + mm_log (tmp, ERROR); + ret = NIL; + } + fs_give ((void **) &s); /* flush the buffer */ + close (fd); /* close the file */ + /* build final filename to use */ + if (maildir_file_path(mailbox, tmp)) + MSGPATH(path2, tmp, file, New); + if (rename (path1,path2) < 0) { + sprintf (tmp, "Message append failed: %s", strerror (errno)); + mm_log (tmp, ERROR); + ret = NIL; + } + unlink (path1); + + if (ret) + if (!(*af) (stream,data,&flags,&date,&message)) ret = NIL; + + } while (ret && message); /* write the data */ + mm_nocritical (stream); /* release critical */ + return ret; + } + + long maildir_delete (MAILSTREAM *stream,char *mailbox) + { + DIR *dirp; + struct direct *d; + int i, remove_dir = 0, mddir = 0, rv, error = 0; + char tmp[MAILTMPLEN],tmp2[MAILTMPLEN], realname[MAILTMPLEN]; + struct stat sbuf; + int courier = IS_COURIER(mailbox); + + if (mailbox[strlen(mailbox) - 1] == MDSEPARATOR(courier)){ + remove_dir++; + mailbox[strlen(mailbox) -1] = '\0'; + } + + if (!maildir_valid(mailbox)){ + maildir_file_path(mailbox, tmp); + if (stat(tmp, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode)){ + sprintf(tmp,"Can not remove %s", mailbox); + error++; + } + } + + if (!error && remove_dir && !maildir_dir_is_empty(mailbox)){ + sprintf(tmp,"Can not remove directory %s/: directory not empty", mailbox); + error++; + } + + if(error){ + mm_log (tmp,ERROR); + return NIL; + } + + maildir_close(stream,0); /* even if stream was NULL */ + + maildir_file_path(mailbox, realname); + + if (remove_dir){ + sprintf(tmp,"%s/%s", realname, MDDIR); + if ((rv = stat (tmp,&sbuf)) == 0 && S_ISREG(sbuf.st_mode)) + rv = unlink(tmp); + else if (errno == ENOENT) + rv = 0; + if (rv != 0){ + sprintf(tmp,"Can not remove %s/%s: %s", tmp2, MDDIR, strerror(errno)); + mm_log (tmp,ERROR); + return NIL; + } + if (!maildir_valid(realname) && rmdir(realname) != 0){ + sprintf(tmp,"Can not remove %s/: %s", mailbox, strerror(errno)); + mm_log (tmp,ERROR); + return NIL; + } + return LONGT; + } + /* else remove just the folder. Remove all hidden files, except MDDIR */ + for (i = Cur; i != EndDir; i++){ + MDFLD(tmp, realname, i); + + if (!(dirp = opendir (tmp))){ + sprintf(tmp,"Can not read %s/: %s", mailbox, strerror(errno)); + mm_log (tmp,ERROR); + return NIL; + } + + while ((d = readdir(dirp)) != NULL){ + if (strcmp(d->d_name, ".") && strcmp(d->d_name,"..")){ + sprintf(tmp2,"%s/%s", tmp, d->d_name); + if (unlink(tmp2) != 0){ + sprintf(tmp2,"Can not remove %s: %s", mailbox, strerror(errno)); + mm_log (tmp2,ERROR); + return NIL; + } + } + } + closedir(dirp); + if (rmdir(tmp) != 0){ + sprintf(tmp,"Can not remove %s: %s", mailbox, strerror(errno)); + mm_log (tmp,ERROR); + return NIL; + } + } + /* + * ok we have removed all subdirectories of the folder mailbox, Remove the + * hidden files. + */ + + if(!(dirp = opendir (realname))){ + sprintf(tmp,"Can not read %s/: %s", realname, strerror(errno)); + mm_log (tmp,ERROR); + return NIL; + } + + while ((d = readdir(dirp)) != NULL){ + if (strcmp(d->d_name, ".") && strcmp(d->d_name,"..") + && (!strcmp(d->d_name, MDDIR) + || !strncmp(d->d_name, MDUIDLAST, strlen(MDUIDLAST)) + || !strncmp(d->d_name, MDUIDTEMP, strlen(MDUIDTEMP)))){ + if(strcmp(d->d_name, MDDIR) == 0) + mddir++; + sprintf(tmp,"%s/%s", realname, d->d_name); + if (unlink(tmp) != 0) + error++; + } + } + closedir(dirp); + if (error || + (maildir_dir_is_empty(mailbox) && mddir == 0 && rmdir(realname) < 0)){ + sprintf(tmp,"Can not remove folder %s: %s", mailbox, strerror(errno)); + mm_log (tmp,ERROR); + return NIL; + } + return LONGT; + } + + long maildir_rename (MAILSTREAM *stream, char *old, char *new) + { + char tmp[MAILTMPLEN],tmpnew[MAILTMPLEN], realold[MAILTMPLEN]; + char realnew[MAILTMPLEN]; + int courier = IS_COURIER(old) && IS_COURIER(new); + int i; + long rv = LONGT; + COURIER_S *cdir; + + if((IS_COURIER(old) || IS_COURIER(new)) && !courier){ + sprintf (tmp,"Can't rename mailbox %s to %s",old, new); + mm_log (tmp,ERROR); + return NIL; + } + + if (!maildir_valid(old)){ + sprintf (tmp,"Can't rename mailbox %s: folder not in maildir format",old); + mm_log (tmp,ERROR); + return NIL; + } + maildir_file_path(old, realold); + if (!maildir_valid_name(new) && new[0] == '#'){ + sprintf (tmp,"Can't rename mailbox %s: folder not in maildir format",new); + mm_log (tmp,ERROR); + return NIL; + } + maildir_file_path(new, realnew); + if (access(tmpnew,F_OK) == 0){ /* new mailbox name must not exist */ + sprintf (tmp,"Can't rename to mailbox %s: destination already exists",new); + mm_log (tmp,ERROR); + return NIL; + } + + if(!courier){ + if (rename (realold,realnew)){ /* try to rename the directory */ + sprintf (tmp,"Can't rename mailbox %s to %s: %s",old, new, + strerror(errno)); + mm_log (tmp,ERROR); + return NIL; + } + return LONGT; /* return success */ + } + + cdir = courier_list_dir(old); + for (i = 0; cdir && i < cdir->total; i++){ + if(strstr(cdir->data[i]->name, old)){ + sprintf(tmp,"%s%s", new, cdir->data[i]->name+strlen(old)); + maildir_file_path(cdir->data[i]->name, realold); + maildir_file_path(tmp, realnew); + if (rename (realold,realnew)){ + sprintf (tmp,"Can't rename mailbox %s to %s: %s",old, new, + strerror(errno)); + mm_log (tmp,ERROR); + rv = NIL; + } + } + } + courier_free_cdir(&cdir); + return rv; + } + + long maildir_sub (MAILSTREAM *stream,char *mailbox) + { + return sm_subscribe (mailbox); + } + + long maildir_unsub (MAILSTREAM *stream,char *mailbox) + { + return sm_unsubscribe (mailbox); + } + + void maildir_lsub (MAILSTREAM *stream,char *ref,char *pat) + { + void *sdb = NIL; + char *s, test[MAILTMPLEN]; + /* get canonical form of name */ + if (maildir_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) { + do if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,NIL); + while ((s = sm_read (&sdb)) != NULL); /* until no more subscriptions */ + } + } + + long maildir_canonicalize (char *pattern,char *ref,char *pat) + { + if (ref && *ref) { /* have a reference */ + strcpy (pattern,ref); /* copy reference to pattern */ + /* # overrides mailbox field in reference */ + if (*pat == '#') strcpy (pattern,pat); + /* pattern starts, reference ends, with / */ + else if ((*pat == '/') && (pattern[strlen (pattern) - 1] == '/')) + strcat (pattern,pat + 1); /* append, omitting one of the period */ + + else strcat (pattern,pat); /* anything else is just appended */ + } + else strcpy (pattern,pat); /* just have basic name */ + return maildir_valid_name(pattern) ? LONGT : NIL; + } + + void maildir_list_work (MAILSTREAM *stream,char *dir,char *pat,long level) + { + DIR *dp; + struct direct *d; + struct stat sbuf; + char curdir[MAILTMPLEN],name[MAILTMPLEN], tmp[MAILTMPLEN]; + char realpat[MAILTMPLEN]; + long i; + char *maildirpath = mdirpath(); + + sprintf(curdir,"%s/%s/", myrootdir(pat), dir ? dir : maildirpath); + if ((dp = opendir (curdir)) != NULL){ + if (dir) sprintf (name,"%s%s/",MDPREFIX(CCLIENT),dir); + else strcpy (name, pat); + + if (level == 0 && !strpbrk(pat,"%*")){ + if(maildir_valid(pat)){ + i = maildir_contains_folder(pat, NULL) + ? LATT_HASCHILDREN + : (maildir_is_dir(pat, NULL) + ? LATT_HASNOCHILDREN : LATT_NOINFERIORS); + maildir_file_path(pat, realpat); + i += maildir_any_new_msgs(realpat) + ? LATT_MARKED : LATT_UNMARKED; + mm_list (stream,'/', pat, i); + } + else + if(pat[strlen(pat) - 1] == '/') + mm_list (stream,'/', pat, LATT_NOSELECT); + } + + while ((d = readdir (dp)) != NULL) + if(strcmp(d->d_name, ".") && strcmp(d->d_name,"..") + && strcmp(d->d_name, MDNAME(Cur)) + && strcmp(d->d_name, MDNAME(Tmp)) + && strcmp(d->d_name, MDNAME(New))){ + + if (dir) sprintf (tmp,"%s%s", name,d->d_name); + else strcpy(tmp, d->d_name); + + if(pmatch_full (tmp, pat,'/')){ + sprintf(tmp,"%s/%s/%s", myrootdir(d->d_name), + (dir ? dir : maildirpath), d->d_name); + if(stat (tmp,&sbuf) == 0 + && ((sbuf.st_mode & S_IFMT) == S_IFDIR)){ + if (dir) sprintf (tmp,"%s%s", name,d->d_name); + else strcpy(tmp, d->d_name); + i = maildir_valid(tmp) + ? (maildir_contains_folder(dir, d->d_name) + ? LATT_HASCHILDREN + : (maildir_is_dir(dir, d->d_name) + ? LATT_HASNOCHILDREN : LATT_NOINFERIORS)) + : LATT_NOSELECT; + i += maildir_any_new_msgs(tmp) + ? LATT_MARKED : LATT_UNMARKED; + mm_list (stream,'/',tmp, i); + strcat (tmp, "/"); + if(dmatch (tmp, pat,'/') && + (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL))){ + sprintf(tmp,"%s/%s",dir,d->d_name); + maildir_list_work (stream,tmp,pat,level+1); + } + } + } + } + closedir (dp); + } + } + + void courier_list_work (MAILSTREAM *stream, char *dir, char *pat, long level) + { + char c, curdir[MAILTMPLEN], tmp[MAILTMPLEN]; + char realname[MAILTMPLEN], realpat[MAILTMPLEN] = {'\0'}; + int i, found; + long style = (long) maildir_parameters(GET_COURIERSTYLE, NIL), j; + char *maildirpath = mdirpath(); + COURIER_S *cdir; + + if(!strpbrk(pat,"%*")){ /* a mailbox */ + maildir_file_path(pat, curdir); + i = strlen(curdir) - 1; + if(curdir[i] == '/') + curdir[i] = '\0'; + cdir = courier_list_dir(curdir); + if(cdir){ + found = 0; j = 0L; + if(maildir_valid_name(pat)){ + for(i = 0; !found && i < cdir->total; i++) + if(strstr(curdir, cdir->data[i]->name)){ + if(strlen(curdir) < strlen(cdir->data[i]->name)) + found += 2; + else if(strlen(curdir) == strlen(cdir->data[i]->name)) + found -= 1; + } + if(found > 0) + j = LATT_HASCHILDREN; + else if(found == 0) + j = (style == COURIER) ? LATT_HASNOCHILDREN : LATT_NOINFERIORS; + } + else + j = LATT_NOSELECT; + j += maildir_any_new_msgs(curdir) ? LATT_MARKED : LATT_UNMARKED; + if (found) + mm_list (stream, '.', pat, j); + courier_free_cdir(&cdir); + } + return; + } + + strcpy(tmp,pat + 4); /* a directory */ + j = strlen(pat) - 1; + maildir_file_path(pat, realpat); + c = pat[j]; + pat[j] = '\0'; + realname[0] = '\0'; + if(dir) + maildir_file_path(dir, realname); + sprintf(curdir,"%s%s%s/%s", (dir ? "" : myrootdir(pat)), (dir ? "" : "/"), + (dir ? realname : maildirpath), (dir ? "" : ".")); + sprintf(tmp, "%s%s/.", MDPREFIX(COURIER), dir ? dir : maildirpath); + if (level == 0 && tmp && pmatch_full (tmp, realpat, '.')) + mm_list (stream,'.', tmp, LATT_NOSELECT); + + cdir = courier_list_dir(pat); + pat[j] = c; + for (i = 0; cdir && i < cdir->total; i++) + if(pmatch_full (cdir->data[i]->name, pat, '.')){ + sprintf(tmp, "%s.", cdir->data[i]->name); + courier_list_info(&cdir, tmp, i); + mm_list (stream,'.',cdir->data[i]->name, cdir->data[i]->attribute); + } + courier_free_cdir(&cdir); + } + + int + same_maildir_file(char *name1, char *name2) + { + char tmp1[MAILTMPLEN], tmp2[MAILTMPLEN]; + char *s; + + strcpy(tmp1, name1 ? name1 : ""); + strcpy(tmp2, name2 ? name2 : ""); + if ((s = strrchr(tmp1, FLAGSEP)) != NULL) + *s = '\0'; + if (((s = strrchr(tmp1, SIZESEP)) != NULL) && (strchr(s,'.') == NULL)) + *s = '\0'; + if ((s = strrchr(tmp2, FLAGSEP)) != NULL) + *s = '\0'; + if (((s = strrchr(tmp2, SIZESEP)) != NULL) && (strchr(s,'.') == NULL)) + *s = '\0'; + + return !strcmp(tmp1, tmp2); + } + + unsigned long antoul(char *seed) + { + int i, error = 0; + unsigned long val = 0L, rv1 = 0L, t; + char c, *p; + if(!seed) + return 0L; + t = strtoul(seed, &p, 10); + if(p && (*p == '.' || *p == '_')) + return t; + /* else */ + if((p = strchr(seed,'.')) != NULL) + *p = '\0'; + error = (strlen(seed) > 6); /* too long */ + for(i= strlen(seed)-1; error == 0 && i >= 0; i--){ + c = seed[i]; + if (c >= 'A' && c <= 'Z') val = c - 'A'; + else if (c >= 'a' && c <= 'z') val = c - 'a' + 26; + else if (c >= '0' && c <= '9') val = c - '0' + 26 + 26; + else if (c == '-') val = c - '-' + 26 + 26 + 10; + else if (c == '_') val = c - '_' + 26 + 26 + 10 + 1; + else error++; + rv1 = val + (rv1 << 6); + } + if(p) + *p = '.'; + return error ? 0L : rv1; + } + + unsigned long mdfntoul (char *name) + { + unsigned long t; + char *r, last; + + if((*name == '_') && ((r = strpbrk(name,".,%+")) != NULL)){ /* Grrr!!! */ + last = *r; + *r = '\0'; + t = antoul(r+1); + *r = last; + } + else + t = antoul(name); + return t; + } + + int comp_maildir_file(char *name1, char *name2) + { + int uset1 = 1, uset2 = 1, i, j, cmp; + unsigned long t1, t2; + char *s1, *s2; + + if (!(name1 && *name1)) + return (name2 && *name2) ? (*name2 == FLAGSEP ? 0 : -1) : 0; + + if (!(name2 && *name2)) + return (name1 && *name1) ? (*name1 == FLAGSEP ? 0 : 1) : 0; + + if((cmp = strcmp(name1,name2)) == 0) + return 0; + + t1 = strtoul(name1, &s1, 10); + t2 = strtoul(name2, &s2, 10); + + if(!s1 || *s1 != '.') + uset1 = 0; + + if(!s2 || *s2 != '.') + uset2 = 0; + + if(uset1 && uset2) /* normal sort order */ + return (t1 < t2) ? -1 : (t1 > t2 ? 1 : (cmp < 0 ? -1 : 1)); + + /* If we make it here we say Grrrr.... first, then we try to figure out + * how to sort this mess. + * These are the rules. + * If there is a number at the beginning it is bigger than anything else. + * If there are digits, then the number of digits decides which one is bigger. + */ + + for(i = 0; isdigit(name1[i]); i++); + for(j = 0; isdigit(name2[j]); j++); + + return(uset1 ? 1 + : (uset2 ? -1 + : (i < j ? -1 : (i > j ? 1 : (cmp < 0 ? -1 : 1))))); + } + + void + maildir_getflag(char *name, int *d, int *f, int *r ,int *s, int *t) + { + char tmp[MAILTMPLEN], *b; + int offset = 0; + int tmpd, tmpf, tmpr, tmps, tmpt; + + if(d) *d = 0; + if(f) *f = 0; + if(r) *r = 0; + if(s) *s = 0; + if(t) *t = 0; + + tmpd = tmpf = tmpr = tmps = tmpt = NIL; /* no flags set by default */ + strcpy(tmp,name); + while ((b = strrchr(tmp+offset, FLAGSEP)) != NULL){ + char flag,last; + int k; + if (!++b) break; + switch (*b){ + case '1': + case '2': + case '3': flag = *b; b += 2; + for (k = 0; b[k] && b[k] != FLAGSEP && b[k] != ','; k++); + last = b[k]; + b[k] = '\0'; + if (flag == '2' || flag == '3'){ + tmpd = strchr (b, MDFLAGC(Draft)) ? T : NIL; + tmpf = strchr (b, MDFLAGC(Flagged)) ? T : NIL; + tmpr = strchr (b, MDFLAGC(Replied)) ? T : NIL; + tmps = strchr (b, MDFLAGC(Seen)) ? T : NIL; + tmpt = strchr (b, MDFLAGC(Trashed)) ? T : NIL; + } + b[k] = last; + b += k; + for (; tmp[offset] && tmp[offset] != FLAGSEP; offset++); + offset++; + break; + default: break; /* Should we crash?... Nahhh */ + } + } + if(d) *d = tmpd; + if(f) *f = tmpf; + if(r) *r = tmpr; + if(s) *s = tmps; + if(t) *t = tmpt; + } + + int + maildir_message_in_list(char *msgname, struct direct **names, + unsigned long bottom, unsigned long top, unsigned long *pos) + { + unsigned long middle = (bottom + top)/2; + int test; + + if (!msgname) + return NIL; + + if (pos) *pos = middle; + + if (same_maildir_file(msgname, names[middle]->d_name)) + return T; + + if (middle == bottom){ /* 0 <= 0 < 1 */ + int rv = NIL; + if (same_maildir_file(msgname, names[middle]->d_name)){ + rv = T; + if (pos) *pos = middle; + } + else + if (same_maildir_file(msgname, names[top]->d_name)){ + rv = T; + if (pos) *pos = top; + } + return rv; + } + + test = comp_maildir_file(msgname, names[middle]->d_name); + + if (top <= bottom) + return test ? NIL : T; + + if (test < 0 ) /* bottom < msgname < middle */ + return maildir_message_in_list(msgname, names, bottom, middle, pos); + else if (test > 0) /* middle < msgname < top */ + return maildir_message_in_list(msgname, names, middle, top, pos); + else return T; + } + + void + maildir_abort(MAILSTREAM *stream) + { + if (LOCAL){ + if(LOCAL->candouid) + maildir_read_uid(stream, NULL, &stream->uid_validity); + if (LOCAL->dir) fs_give ((void **) &LOCAL->dir); + if (LOCAL->curdir) fs_give ((void **) &LOCAL->curdir); + if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); + if(LOCAL->uidtempfile){ + unlink(LOCAL->uidtempfile); + fs_give ((void **) &LOCAL->uidtempfile); + } + fs_give ((void **) &stream->local); + } + if (mdfpath) fs_give((void **)&mdfpath); + stream->dtb = NIL; + } + + int + maildir_contains_folder(char *dirname, char *name) + { + char tmp[MAILTMPLEN], tmp2[MAILTMPLEN]; + int rv = 0; + DIR *dir; + struct direct *d; + + maildir_file_path(dirname, tmp2); + if(name){ + strcat(tmp2,"/"); + strcat(tmp2, name); + } + + if (!(dir = opendir (tmp2))) + return NIL; + + while ((d = readdir(dir)) != NULL){ + if (strcmp(d->d_name, ".") && strcmp(d->d_name,"..") + && strcmp(d->d_name, MDNAME(Cur)) + && strcmp(d->d_name, MDNAME(Tmp)) + && strcmp(d->d_name, MDNAME(New))){ + + sprintf(tmp,"%s/%s", tmp2, d->d_name); + if(maildir_valid(tmp)){ + rv++; + break; + } + } + } + closedir(dir); + return rv; + } + + int + maildir_is_dir(char *dirname, char *name) + { + char tmp[MAILTMPLEN]; + struct stat sbuf; + + maildir_file_path(dirname, tmp); + if(name){ + strcat(tmp,"/"); + strcat(tmp,name); + } + strcat(tmp,"/"); + strcat(tmp,MDDIR); + + return ((stat(tmp, &sbuf) == 0) && S_ISREG (sbuf.st_mode)) ? 1 : 0; + } + + int + maildir_dir_is_empty(char *mailbox) + { + char tmp[MAILTMPLEN], tmp2[MAILTMPLEN], tmp3[MAILTMPLEN],*s; + int rv = 1, courier = IS_COURIER(mailbox); + DIR *dir; + struct direct *d; + struct stat sbuf; + + maildir_file_path(mailbox, tmp2); + + if(courier){ + strcpy(tmp3, tmp2); + if(s = strrchr(tmp2, '/')) + *s = '\0'; + } + + if (!(dir = opendir (tmp2))) + return rv; + + if(courier){ + while((d = readdir(dir)) != NULL){ + sprintf(tmp,"%s/%s", tmp2, d->d_name); + if(!strncmp(tmp, tmp3, strlen(tmp3)) + && tmp[strlen(tmp3)] == '.'){ + rv = 0; + break; + } + } + } + else + while ((d = readdir(dir)) != NULL){ + sprintf(tmp,"%s/%s", tmp2, d->d_name); + if (strcmp(d->d_name, ".") + && strcmp(d->d_name,"..") + && strcmp(d->d_name, MDNAME(Cur)) + && strcmp(d->d_name, MDNAME(Tmp)) + && strcmp(d->d_name, MDNAME(New)) + && strcmp(d->d_name, MDDIR) + && strcmp(d->d_name, MDUIDVALIDITY) + && !(d->d_name[0] == '.' + && stat (tmp,&sbuf) == 0 + && S_ISREG(sbuf.st_mode))){ + rv = 0; + break; + } + } + closedir(dir); + return rv; + } + + void + maildir_get_file (MAILDIRFILE **mdfile) + { + MAILDIRFILE *md; + + md = (MAILDIRFILE *) fs_get(sizeof(MAILDIRFILE)); + memset(md, 0, sizeof(MAILDIRFILE)); + *mdfile = md; + } + + void + maildir_free_file (void **mdfile) + { + MAILDIRFILE *md = (mdfile && *mdfile) ? (MAILDIRFILE *) *mdfile : NULL; + + if (md){ + if (md->name) fs_give((void **)&md->name); + fs_give((void **)&md); + } + } + + void + maildir_free_file_only (void **mdfile) + { + MAILDIRFILE *md = (mdfile && *mdfile) ? (MAILDIRFILE *) *mdfile : NULL; + + if (md && md->name) + fs_give((void **)&md->name); + } + + int + maildir_any_new_msgs(char *mailbox) + { + char tmp[MAILTMPLEN]; + int rv = NIL; + DIR *dir; + struct direct *d; + + MDFLD(tmp, mailbox, New); + + if (!(dir = opendir (tmp))) + return rv; + + while ((d = readdir(dir)) != NULL){ + if (d->d_name[0] == '.') + continue; + rv = T; + break; + } + closedir(dir); + return rv; + } + + + void + maildir_get_date(MAILSTREAM *stream, unsigned long msgno) + { + MESSAGECACHE *elt; + struct tm *t; + time_t ti; + int i,k; + + elt = mail_elt (stream,msgno); + if(elt && elt->year != 0) + return; + if ((ti = mdfntoul(MDFILE(elt))) > 0L && (t = gmtime(&ti))){ + i = t->tm_hour * 60 + t->tm_min; + k = t->tm_yday; + t = localtime(&ti); + i = t->tm_hour * 60 + t->tm_min - i; + if((k = t->tm_yday - k) != 0) + i += ((k < 0) == (abs (k) == 1)) ? -24*60 : 24*60; + k = abs (i); + elt->hours = t->tm_hour; + elt->minutes = t->tm_min; + elt->seconds = t->tm_sec; + elt->day = t->tm_mday; elt->month = t->tm_mon + 1; + elt->year = t->tm_year - (BASEYEAR - 1900); + elt->zoccident = (k == i) ? 0 : 1; + elt->zhours = k/60; + elt->zminutes = k % 60; + } + } + + /* Support for Courier Style directories + When this code is complete there will be two types of support, which + will be configurable. The problem is the following: In Courier style + folder structure, a "folder" may have a subfolder called + "folder.subfolder", which is not natural in the file system in the + sense that I can not stat for "folder.subfolder" wihtout knowing what + "subfolder" is. It needs to be guessed. Because of this I need to look + in the list of folders if there is a folder with a name + "folder.subfolder", before I can say if the folder is dual or not. One + can avoid this annoyance if one ignores the problem by declaring that + every folder is dual. I will however code as the default the more + complicated idea of scaning the containing directory each time it is + modified and search for subfolders, and list the entries it found. + */ + + int courier_dir_select (const struct direct *name) + { + return name->d_name[0] == '.' && (strlen(name->d_name) > 2 + || (strlen(name->d_name) == 2 && name->d_name[1] != '.')); + } + + int courier_dir_sort (const void *d1, const void *d2) + { + const struct direct *e1 = *(const struct direct **) d1; + const struct direct *e2 = *(const struct direct **) d2; + + return strcmp((char *) e1->d_name, (char *) e2->d_name); + } + + void courier_free_cdir (COURIER_S **cdir) + { + int i; + + if (!*cdir) + return; + + if ((*cdir)->path) fs_give((void **)&((*cdir)->path)); + for (i = 0; i < (*cdir)->total; i++) + if((*cdir)->data[i]->name) fs_give((void **)&((*cdir)->data[i]->name)); + fs_give((void **)&((*cdir)->data)); + fs_give((void **)&(*cdir)); + } + + COURIER_S *courier_get_cdir (int total) + { + COURIER_S *cdir; + + cdir = (COURIER_S *)fs_get(sizeof(COURIER_S)); + memset(cdir, 0, sizeof(COURIER_S)); + cdir->data = (COURIERLOCAL **) fs_get(total*sizeof(COURIERLOCAL *)); + memset(cdir->data, 0, sizeof(COURIERLOCAL *)); + cdir->total = total; + return cdir; + } + + int courier_search_list(COURIERLOCAL **data, char *name, int first, int last) + { + int try = (first + last)/2; + + if(!strstr(data[try]->name, name)){ + if(first == try) /* first == last || first + 1 == last */ + return strstr(data[last]->name, name) ? 1 : 0; + if(strcmp(data[try]->name, name) < 0) /*data[try] < name < data[end] */ + return courier_search_list(data, name, try, last); + else /* data[begin] < name < data[try] */ + return courier_search_list(data, name, first, try); + } + return 1; + } + + /* Lists all directories that are subdirectories of a given directory */ + + COURIER_S *courier_list_dir(char *curdir) + { + struct direct **names = NIL; + struct stat sbuf; + unsigned long ndir; + COURIER_S *cdir = NULL; + char tmp[MAILTMPLEN], tmp2[MAILTMPLEN], pathname[MAILTMPLEN], + realname[MAILTMPLEN]; + int i, j, scand, td; + + /* There are two cases, either curdir is + #mc/INBOX. #mc/INBOX.foo + or + #mc/Maildir/. #mc/Maildir/.foo + */ + strcpy(tmp,curdir + 4); + if(!strncmp(ucase(tmp), "INBOX", 5)) + strcpy(tmp, "#mc/INBOX."); + else{ + strcpy(tmp, curdir); + for (i = strlen(tmp) - 1; tmp[i] && tmp[i] != '/'; i--); + tmp[i+2] = '\0'; /* keep the last "." intact */ + } + maildir_file_path(tmp, realname); + maildir_scandir (realname, &names, &ndir, &scand, COURIER); + + if (scand > 0){ + cdir = courier_get_cdir(ndir); + cdir->path = cpystr(realname); + for(i = 0, j = 0; i < ndir; i++){ + td = realname[strlen(realname) - 1] == '.' + && *names[i]->d_name == '.'; + sprintf(tmp2,"%s%s", tmp, names[i]->d_name+1); + sprintf(pathname,"%s%s", realname, names[i]->d_name + td); + if(stat(pathname, &sbuf) == 0 && S_ISDIR(sbuf.st_mode)){ + cdir->data[j] = (COURIERLOCAL *) fs_get(sizeof(COURIERLOCAL)); + cdir->data[j++]->name = cpystr(tmp2); + } + fs_give((void **)&names[i]); + } + cdir->total = j; + if(cdir->total == 0) + courier_free_cdir(&cdir); + } + if(names) + fs_give((void **) &names); + return cdir; + } + + void + courier_list_info(COURIER_S **cdirp, char *data, int i) + { + long style = (long) maildir_parameters(GET_COURIERSTYLE, NIL); + COURIER_S *cdir = *cdirp; + + if(maildir_valid(cdir->data[i]->name)){ + if(courier_search_list(cdir->data, data, 0, cdir->total - 1)) + cdir->data[i]->attribute = LATT_HASCHILDREN; + else + cdir->data[i]->attribute = (style == COURIER) + ? LATT_HASNOCHILDREN : LATT_NOINFERIORS; + } + else + cdir->data[i]->attribute = LATT_NOSELECT; + cdir->data[i]->attribute += maildir_any_new_msgs(cdir->data[i]->name) + ? LATT_MARKED : LATT_UNMARKED; + } + + /* UID Support */ + /* Yes, I know I procastinated a lot about this, but here it is finally */ + + unsigned int + maildir_can_assign_uid (MAILSTREAM *stream) + { + unsigned int rv = 0; + int createtemp; + unsigned long t; + char tmp[MAILTMPLEN], *s; + DIR *dir; + struct direct *d; + + if(!stream || stream->rdonly + || !LOCAL || !LOCAL->dir || !(dir = opendir(LOCAL->dir))) + return rv; + + sprintf(tmp, "%s.%d", MDUIDTEMP, mypid); + while ((d = readdir(dir)) != NULL){ + if(!strncmp(d->d_name, tmp, strlen(tmp)) + || !strncmp(d->d_name, MDUIDTEMP, strlen(MDUIDTEMP))) + break; + } + rv = d ? !strncmp(d->d_name, tmp, strlen(tmp)) : 1; + createtemp = d ? 0 : 1; + if (d && rv == 0){ /* is there a temp file that is not ours? */ + s = strrchr(d->d_name, '.'); + t = strtoul(s+1, &s, 10); + if(s != NULL && *s != '\0') + createtemp++; + if(time(0) > t + MAXTEMPUID){ + createtemp++; + sprintf(tmp,"%s/%s", LOCAL->dir, d->d_name); + unlink(tmp); + } + } + closedir(dir); + if(createtemp){ + FILE *fp; + sprintf(tmp,"%s/%s.%d.%lu", LOCAL->dir, MDUIDTEMP, mypid, time(0)); + if(fp = fopen(tmp, "w")){ + fclose(fp); + if(LOCAL->uidtempfile) + fs_give((void **)&LOCAL->uidtempfile); + LOCAL->uidtempfile = cpystr(tmp); + rv++; + } + } + return rv; + } + + void + maildir_read_uid(MAILSTREAM *stream, unsigned long *uid_last, + unsigned long *uid_validity) + { + int createuid, deleteuid = 0; + char tmp[MAILTMPLEN], *s = NULL; + DIR *dir; + struct direct *d; + + if(uid_last) *uid_last = 0L; + if(uid_last && uid_validity) *uid_validity = time(0); + if(!stream || !LOCAL || !LOCAL->dir || !(dir = opendir(LOCAL->dir))) + return; + + while ((d = readdir(dir)) != NULL){ + if(!strncmp(d->d_name, MDUIDLAST, strlen(MDUIDLAST))) + break; + } + createuid = d == NULL ? 1 : 0; + if(uid_last == NULL) + deleteuid++; + if(d){ + if(uid_last){ + s = d->d_name + strlen(MDUIDLAST) + 1; + *uid_last = strtoul(s, &s, 10); + if(!s || *s != '.'){ + deleteuid++; + createuid++; + *uid_last = 0L; + } + } + if(s && *s == '.'){ + if(uid_validity){ + s++; + *uid_validity = strtoul(s, &s, 10); + if(s && *s != '\0'){ + *uid_validity = time(0); + deleteuid++; + createuid++; + } + } + } + else{ + deleteuid++; + createuid++; + } + } + if(deleteuid){ + sprintf(tmp,"%s/%s", LOCAL->dir, d->d_name); + unlink(tmp); + } + if(createuid) + maildir_write_uid(stream, (uid_last ? *uid_last : stream->uid_last), + uid_validity ? *uid_validity : time(0)); + closedir(dir); + } + + void + maildir_write_uid(MAILSTREAM *stream, unsigned long uid_last, + unsigned long uid_validity) + { + char tmp[MAILTMPLEN]; + FILE *fp; + + if(!stream || stream->rdonly || !LOCAL || !LOCAL->dir) + return; + + sprintf(tmp,"%s/%s.%010lu.%010lu", LOCAL->dir, MDUIDLAST, + uid_last, uid_validity); + if(fp = fopen(tmp, "w")) + fclose(fp); + } + + unsigned long + maildir_get_uid(char *name) + { + char *s; + unsigned long rv = 0L; + + if(!name || (s = strstr(name,MDUIDSEP)) == NULL) + return rv; + + s += strlen(MDUIDSEP); + rv = strtoul(s, NULL, 10); + return rv; + } + + + void + maildir_delete_uid(MAILSTREAM *stream, unsigned long msgno) + { + char old[MAILTMPLEN], new[MAILTMPLEN], *s, *t; + MESSAGECACHE *elt; + + elt = mail_elt(stream, msgno); + if(!stream || !elt || !elt->private.spare.ptr || !LOCAL || !LOCAL->dir) + return; + + sprintf(old, "%s/%s/%s", LOCAL->dir, MDNAME(Cur), MDFILE(elt)); + t = MDFILE(elt); + if(s = strstr(MDFILE(elt), MDUIDSEP)){ + *s = '\0'; + s += strlen(MDUIDSEP); + strtoul(s, &s, 10); + sprintf(new, "%s/%s/%s%s", LOCAL->dir, MDNAME(Cur), t, s); + if(rename(old, new) == 0){ + maildir_free_file_only ((void **)&elt->private.spare.ptr); + s = strrchr(new, '/'); + MDFILE(elt) = cpystr(s+1); + } + elt->private.uid = 0L; + } + } + + void + maildir_assign_uid(MAILSTREAM *stream, unsigned long msgno, unsigned long uid) + { + int createuid, deleteuid = 0; + char old[MAILTMPLEN], new[MAILTMPLEN], *s, *t; + MESSAGECACHE *elt; + + elt = mail_elt(stream, msgno); + if(!stream || !elt || !elt->private.spare.ptr || !LOCAL || !LOCAL->dir) + return; + + maildir_delete_uid(stream, msgno); + sprintf(old, "%s/%s/%s", LOCAL->dir, MDNAME(Cur), MDFILE(elt)); + t = MDFILE(elt); + if((s = strrchr(MDFILE(elt),FLAGSEP)) != NULL){ + *s++ = '\0'; + sprintf(new, "%s/%s/%s%s%lu%c%s", + LOCAL->dir, MDNAME(Cur), t, MDUIDSEP, uid, FLAGSEP, s); + if(rename(old, new) == 0){ + maildir_free_file_only ((void **)&elt->private.spare.ptr); + s = strrchr(new, '/'); + MDFILE(elt) = cpystr(s+1); + stream->uid_validity = time(0); + } + elt->private.uid = uid; + } + } + + void + maildir_uid_renew_tempfile(MAILSTREAM *stream) + { + char tmp[MAILTMPLEN]; + + if(!stream || stream->rdonly + || !LOCAL || !LOCAL->candouid || !LOCAL->dir || !LOCAL->uidtempfile) + return; + + sprintf(tmp,"%s/%s.%d.%lu", LOCAL->dir, MDUIDTEMP, mypid, time(0)); + if(rename(LOCAL->uidtempfile, tmp) == 0){ + fs_give((void **)&LOCAL->uidtempfile); + LOCAL->uidtempfile = cpystr(tmp); + } + } diff -rc alpine-2.00/imap/src/osdep/unix/maildir.h alpine-2.00.I.USE/imap/src/osdep/unix/maildir.h *** alpine-2.00/imap/src/osdep/unix/maildir.h 2011-02-07 20:34:02.000000000 -0800 --- alpine-2.00.I.USE/imap/src/osdep/unix/maildir.h 2011-02-07 20:33:41.000000000 -0800 *************** *** 0 **** --- 1,226 ---- + /* + * A few definitions that try to make this module portable to other + * platforms (e.g. Cygwin). This module is based on the information from + * http://cr.yp.to/proto/maildir.html + */ + + /* First we deal with the separator character */ + #ifndef FLAGSEP + #define FLAGSEP ':' + #endif + #define SIZESEP ',' + + const char sep1[] = {FLAGSEP, '1', ',', '\0'}; /* experimental semantics*/ + const char sep2[] = {FLAGSEP, '2', ',', '\0'}; /* Flags Information */ + const char sep3[] = {FLAGSEP, '3', ',', '\0'}; /* Grrrr.... */ + + const char *sep[] = { sep1, sep2, sep3, NULL}; + + #define MDSEP(i) sep[((i) - 1)] + + /* Now we deal with flags. Woohoo! */ + typedef enum {Draft, Flagged, Passed, Replied, Seen, Trashed, + EmptyFlag, EndFlags} MdFlagNamesType; + const int mdimapflags[] = {Draft, Flagged, Replied, Seen, Trashed, EmptyFlag, EndFlags}; + const int mdkwdflags[] = {Passed, EmptyFlag, EndFlags}; + + /* this array lists the codes for mdflgnms (maildir flag names) above */ + const char *mdflags[] = { "D", "F", "P", "R", "S", "T", "", NULL}; + /* and as characters too */ + const char cmdflags[] = { 'D', 'F', 'P', 'R', 'S', 'T', '0', '\0'}; + + /* MDFLAG(Seen, elt->seen) */ + #define MDFLAG(i,j) mdflags[j ? (i) : EmptyFlag] + /* MDFLAGC(Seen) */ + #define MDFLAGC(i) cmdflags[(i)] + + /* Now we deal with the directory structure */ + typedef enum {Cur, Tmp, New, EndDir} DirNamesType; + char *mdstruct[] = {"cur", "tmp", "new", NULL}; + #define MDNAME(i) mdstruct[(i)] + #define MDFLD(tmp, dir, i) sprintf((tmp),"%s/%s", (dir), mdstruct[(i)]) + #define MSGPATH(tmp, dir, msg,i) sprintf((tmp),"%s/%s/%s", (dir), mdstruct[(i)],(msg)) + + /* Files associated to a maildir directory */ + + #define MDUIDVALIDITY ".uidvalidity" /* support for old maildirs */ + #define MDDIR ".mdir" /* this folder is a directory */ + #define MDUIDLAST ".uidlast" /* last assigned uid */ + #define MDUIDTEMP ".uidtemp" /* We assign uid's no one else */ + + + + /* Support of Courier Structure */ + #define CCLIENT 0 + #define COURIER 1 + #define IS_CCLIENT(t) \ + (((t) && (t)[0] == '#' && ((t)[1] == 'm' || (t)[1] == 'M')\ + && ((t)[2] == 'd' || (t)[2] == 'D')\ + && (t)[3] == '/' && (t)[4] != '\0') ? 1 : 0) + + #define IS_COURIER(t) \ + (((t) && (t)[0] == '#' && ((t)[1] == 'm' || (t)[1] == 'M')\ + && ((t)[2] == 'c' || (t)[2] == 'C')\ + && (t)[3] == '/' && (t)[4] != '\0') ? 1 : 0) + #define MDPREFIX(s) ((s) ? "#mc/" : "#md/") + #define MDSEPARATOR(s) ((s) ? '.' : '/') + + /* UID Support */ + + #define MAXTEMPUID (unsigned long) 180L + const char mduid[] = {',','u','=','\0'}; + #define MDUIDSEP mduid + + + /* Now we deal with messages filenames */ + char mdlocaldomain[MAILTMPLEN+1] = {'\0'}; + pid_t mypid = (pid_t) 0; + static char *mdfpath = NULL; + static char myMdInboxDir[50] = { '\0' };/* Location of the Maildir INBOX */ + static long CourierStyle = CCLIENT; + + #define CHUNK 16384 /* from unix.h */ + + typedef struct courier_local { + char *name; /* name of directory/folder */ + int attribute; /* attributes (children/marked/etc) */ + } COURIERLOCAL; + + typedef struct courier { + char *path; /* Path to collection */ + time_t scantime; /* time at which information was generated */ + int total; /* total number of elements in data */ + COURIERLOCAL **data; + } COURIER_S; + + /* In gdb this is the *(struct maildir_local *)stream->local structure */ + typedef struct maildir_local { + unsigned int dirty : 1; /* diskcopy needs updating */ + unsigned int courier : 1; /* It is Courier style file system */ + unsigned int link : 1; /* There is a symbolic link */ + unsigned int candouid; /* we can assign uids and no one else */ + char *uidtempfile; /* path to uid temp file */ + int fd; /* fd of open message */ + char *dir; /* mail directory name */ + char *curdir; /* mail directory name/cur */ + unsigned char *buf; /* temporary buffer */ + unsigned long buflen; /* current size of temporary buffer */ + time_t scantime; /* last time directory scanned */ + } MAILDIRLOCAL; + + /* Convenient access to local data */ + #define LOCAL ((MAILDIRLOCAL *) stream->local) + + typedef struct maildir_file_info { + char *name; /* name of the file */ + DirNamesType loc; /* location of this file */ + unsigned long pos; /* place in list where this file is listed */ + off_t size; /* size in bytes, on disk */ + time_t atime; /* last access time */ + time_t mtime; /* last modified time */ + time_t ctime; /* last changed time */ + } MAILDIRFILE; + + #define MDFILE(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->name) + #define MDLOC(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->loc) + #define MDPOS(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->pos) + #define MDSIZE(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->size) + #define MDATIME(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->atime) + #define MDMTIME(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->mtime) + #define MDCTIME(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->ctime) + + /* Function prototypes */ + + DRIVER *maildir_valid (char *name); + MAILSTREAM *maildir_open (MAILSTREAM *stream); + void maildir_close (MAILSTREAM *stream, long options); + long maildir_ping (MAILSTREAM *stream); + void maildir_check (MAILSTREAM *stream); + long maildir_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); + char *maildir_header (MAILSTREAM *stream,unsigned long msgno, + unsigned long *length, long flags); + void maildir_list (MAILSTREAM *stream,char *ref,char *pat); + void *maildir_parameters (long function,void *value); + int maildir_create_folder (char *mailbox); + long maildir_create (MAILSTREAM *stream,char *mailbox); + void maildir_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); /*check */ + long maildir_expunge (MAILSTREAM *stream, char *sequence, long options); + long maildir_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); + long maildir_append (MAILSTREAM *stream,char *mailbox, append_t af, void *data); + long maildir_delete (MAILSTREAM *stream,char *mailbox); + long maildir_rename (MAILSTREAM *stream,char *old,char *new); + long maildir_sub (MAILSTREAM *stream,char *mailbox); + long maildir_unsub (MAILSTREAM *stream,char *mailbox); + void maildir_lsub (MAILSTREAM *stream,char *ref,char *pat); + void courier_list (MAILSTREAM *stream,char *ref, char *pat); + + /* utility functions */ + void courier_realname (char *name, char *realname); + long maildir_dirfmttest (char *name); + char *maildir_file (char *dst,char *name); + int maildir_select (const struct direct *name); + int maildir_namesort (const void *d1, const void *d2); + unsigned long antoul (char *seed); + unsigned long mdfntoul (char *name); + int courier_dir_select (const struct direct *name); + int courier_dir_sort (const void *d1, const void *d2); + long maildir_canonicalize (char *pattern,char *ref,char *pat); + void maildir_list_work (MAILSTREAM *stream,char *subdir,char *pat,long level); + void courier_list_work (MAILSTREAM *stream,char *subdir,char *pat,long level); + int maildir_file_path(char *name, char *tmp); + int maildir_valid_name (char *name); + int maildir_valid_dir (char *name); + int is_valid_maildir (char **name); + int maildir_message_exists(MAILSTREAM *stream,char *name, char *tmp); + char *maildir_remove_root(char *name); + char *maildir_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, unsigned long *length,long flags); + unsigned long maildir_parse_message(MAILSTREAM *stream, unsigned long msgno, + DirNamesType dirtype); + int maildir_eliminate_duplicate (char *name, struct direct ***flist, + unsigned long *nfiles); + int maildir_doscandir (char *name, struct direct ***flist, int flag); + unsigned long maildir_scandir (char *name, struct direct ***flist, + unsigned long *nfiles, int *scand, int flag); + void maildir_parse_folder (MAILSTREAM *stream, int full); + void md_domain_name (void); + char *myrootdir (char *name); + char *mdirpath (void); + int maildir_initial_check (MAILSTREAM *stream, DirNamesType dirtype); + unsigned long maildir_parse_dir(MAILSTREAM *stream, unsigned long nmsgs, + DirNamesType dirtype, struct direct **names, unsigned long nfiles, int full); + int same_maildir_file(char *name1, char *name2); + int comp_maildir_file(char *name1, char *name2); + int maildir_message_in_list(char *msgname, struct direct **names, + unsigned long bottom, unsigned long top, unsigned long *pos); + void maildir_getflag(char *name, int *d, int *f, int *r ,int *s, int *t); + int maildir_update_elt_maildirp(MAILSTREAM *stream, unsigned long msgno); + void maildir_abort (MAILSTREAM *stream); + int maildir_contains_folder(char *dirname, char *name); + int maildir_is_dir(char *dirname, char *name); + int maildir_dir_is_empty(char *mailbox); + int maildir_create_work (char *mailbox, int loop); + void maildir_get_file (MAILDIRFILE **mdfile); + void maildir_free_file (void **mdfile); + void maildir_free_file_only (void **mdfile); + int maildir_any_new_msgs(char *mailbox); + void maildir_get_date(MAILSTREAM *stream, unsigned long msgno); + void maildir_fast (MAILSTREAM *stream,char *sequence,long flags); + + /* Courier server support */ + void courier_free_cdir (COURIER_S **cdir); + COURIER_S *courier_get_cdir (int total); + int courier_search_list(COURIERLOCAL **data, char *name, int first, int last); + COURIER_S *courier_list_dir(char *curdir); + void courier_list_info(COURIER_S **cdirp, char *data, int i); + + /* UID Support */ + unsigned int maildir_can_assign_uid (MAILSTREAM *stream); + void maildir_read_uid(MAILSTREAM *stream, unsigned long *uid_last, + unsigned long *uid_validity); + void maildir_write_uid(MAILSTREAM *stream, unsigned long uid_last, + unsigned long uid_validity); + unsigned long maildir_get_uid(char *name); + void maildir_delete_uid(MAILSTREAM *stream, unsigned long msgno); + void maildir_assign_uid(MAILSTREAM *stream, unsigned long msgno, unsigned long uid); + void maildir_uid_renew_tempfile(MAILSTREAM *stream); + diff -rc alpine-2.00/imap/src/osdep/unix/Makefile alpine-2.00.I.USE/imap/src/osdep/unix/Makefile *** alpine-2.00/imap/src/osdep/unix/Makefile 2008-06-04 11:18:34.000000000 -0700 --- alpine-2.00.I.USE/imap/src/osdep/unix/Makefile 2011-02-07 20:33:41.000000000 -0800 *************** *** 144,150 **** # However, mh needs to be before any sysinbox formats (such as mmdf or unix) # since otherwise INBOX won't work correctly when mh_allow_inbox is set. # ! DEFAULTDRIVERS=imap nntp pop3 mix mx mbx tenex mtx mh mmdf unix news phile CHUNKSIZE=65536 # Normally no need to change any of these --- 144,150 ---- # However, mh needs to be before any sysinbox formats (such as mmdf or unix) # since otherwise INBOX won't work correctly when mh_allow_inbox is set. # ! DEFAULTDRIVERS=maildir courier imap nntp pop3 mix mx mbx tenex mtx mh mmdf unix news phile CHUNKSIZE=65536 # Normally no need to change any of these *************** *** 153,159 **** BINARIES=osdep.o mail.o misc.o newsrc.o smanager.o utf8.o utf8aux.o siglocal.o \ dummy.o pseudo.o netmsg.o flstring.o fdstring.o \ rfc822.o nntp.o smtp.o imap4r1.o pop3.o \ ! unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o mix.o CFLAGS=-g CAT=cat --- 153,159 ---- BINARIES=osdep.o mail.o misc.o newsrc.o smanager.o utf8.o utf8aux.o siglocal.o \ dummy.o pseudo.o netmsg.o flstring.o fdstring.o \ rfc822.o nntp.o smtp.o imap4r1.o pop3.o \ ! unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o mix.o maildir.o CFLAGS=-g CAT=cat *************** *** 282,288 **** cyg: # Cygwin - note that most local file drivers don't work!! $(BUILD) `$(CAT) SPECIALS` OS=$@ \ ! DEFAULTDRIVERS="imap nntp pop3 mbx unix phile" \ SIGTYPE=psx CHECKPW=cyg LOGINPW=cyg CRXTYPE=std \ SPOOLDIR=/var \ ACTIVEFILE=/usr/local/news/lib/active \ --- 282,288 ---- cyg: # Cygwin - note that most local file drivers don't work!! $(BUILD) `$(CAT) SPECIALS` OS=$@ \ ! DEFAULTDRIVERS="imap nntp pop3 mbx unix maildir phile" \ SIGTYPE=psx CHECKPW=cyg LOGINPW=cyg CRXTYPE=std \ SPOOLDIR=/var \ ACTIVEFILE=/usr/local/news/lib/active \ *************** *** 892,898 **** unix.o: mail.h misc.h osdep.h unix.h pseudo.h dummy.h utf8.o: mail.h misc.h osdep.h utf8.h tmap.c widths.c utf8aux.o: mail.h misc.h osdep.h utf8.h ! # OS-dependent --- 892,898 ---- unix.o: mail.h misc.h osdep.h unix.h pseudo.h dummy.h utf8.o: mail.h misc.h osdep.h utf8.h tmap.c widths.c utf8aux.o: mail.h misc.h osdep.h utf8.h ! maildir.o: mail.h misc.h osdep.h maildir.h dummy.h # OS-dependent diff -rc alpine-2.00/imap/src/osdep/unix/os_cyg.h alpine-2.00.I.USE/imap/src/osdep/unix/os_cyg.h *** alpine-2.00/imap/src/osdep/unix/os_cyg.h 2008-06-04 11:18:34.000000000 -0700 --- alpine-2.00.I.USE/imap/src/osdep/unix/os_cyg.h 2011-02-07 20:33:41.000000000 -0800 *************** *** 47,52 **** --- 47,53 ---- #define setpgrp setpgid #define SYSTEMUID 18 /* Cygwin returns this for SYSTEM */ + #define FLAGSEP ';' #define geteuid Geteuid uid_t Geteuid (void); diff -rc alpine-2.00/imap/src/osdep/unix/ssl_unix.c alpine-2.00.I.USE/imap/src/osdep/unix/ssl_unix.c *** alpine-2.00/imap/src/osdep/unix/ssl_unix.c 2008-06-04 11:18:34.000000000 -0700 --- alpine-2.00.I.USE/imap/src/osdep/unix/ssl_unix.c 2011-02-07 20:33:45.000000000 -0800 *************** *** 524,530 **** if (tcpdebug) mm_log ("Successfully read SSL data",TCPDEBUG); } /* timeout, punt unless told not to */ ! else if (!tmoh || !(*tmoh) (now - t,now - tl)) { if (tcpdebug) mm_log ("SSL data read timeout",TCPDEBUG); return ssl_abort (stream); } --- 524,530 ---- if (tcpdebug) mm_log ("Successfully read SSL data",TCPDEBUG); } /* timeout, punt unless told not to */ ! else if (!tmoh || !(*tmoh) (now - t,now - tl, stream->tcpstream->host)) { if (tcpdebug) mm_log ("SSL data read timeout",TCPDEBUG); return ssl_abort (stream); } diff -rc alpine-2.00/imap/src/osdep/unix/tcp_unix.c alpine-2.00.I.USE/imap/src/osdep/unix/tcp_unix.c *** alpine-2.00/imap/src/osdep/unix/tcp_unix.c 2008-06-04 11:18:34.000000000 -0700 --- alpine-2.00.I.USE/imap/src/osdep/unix/tcp_unix.c 2011-02-07 20:33:45.000000000 -0800 *************** *** 585,591 **** if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG); } /* timeout, punt unless told not to */ ! else if (!tmoh || !(*tmoh) (now - t,now - tl)) { if (tcpdebug) mm_log ("TCP buffer read timeout",TCPDEBUG); return tcp_abort (stream); } --- 585,591 ---- if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG); } /* timeout, punt unless told not to */ ! else if (!tmoh || !(*tmoh) (now - t,now - tl, stream->host)) { if (tcpdebug) mm_log ("TCP buffer read timeout",TCPDEBUG); return tcp_abort (stream); } *************** *** 645,651 **** if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG); } /* timeout, punt unless told not to */ ! else if (!tmoh || !(*tmoh) (now - t,now - tl)) { if (tcpdebug) mm_log ("TCP data read timeout",TCPDEBUG); return tcp_abort (stream);/* error or timeout no-continue */ } --- 645,651 ---- if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG); } /* timeout, punt unless told not to */ ! else if (!tmoh || !(*tmoh) (now - t,now - tl, stream->host)) { if (tcpdebug) mm_log ("TCP data read timeout",TCPDEBUG); return tcp_abort (stream);/* error or timeout no-continue */ } *************** *** 716,722 **** if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG); } /* timeout, punt unless told not to */ ! else if (!tmoh || !(*tmoh) (now - t,now - tl)) { if (tcpdebug) mm_log ("TCP write timeout",TCPDEBUG); return tcp_abort (stream); } --- 716,722 ---- if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG); } /* timeout, punt unless told not to */ ! else if (!tmoh || !(*tmoh) (now - t,now - tl, stream->host)) { if (tcpdebug) mm_log ("TCP write timeout",TCPDEBUG); return tcp_abort (stream); } diff -rc alpine-2.00/include/general.h alpine-2.00.I.USE/include/general.h *** alpine-2.00/include/general.h 2007-01-25 12:29:45.000000000 -0800 --- alpine-2.00.I.USE/include/general.h 2011-02-07 20:33:44.000000000 -0800 *************** *** 32,37 **** --- 32,38 ---- #undef TRUE #define TRUE 1 /* True, yes, good, etc. */ #define ABORT 2 /* Death, ^G, abort, etc. */ + #define COUNT 3 /* For control-c command */ #undef MIN diff -rc alpine-2.00/patchlevel alpine-2.00.I.USE/patchlevel *** alpine-2.00/patchlevel 2011-02-07 20:34:02.000000000 -0800 --- alpine-2.00.I.USE/patchlevel 2011-02-07 20:34:02.000000000 -0800 *************** *** 0 **** --- 1 ---- + char plevstamp[]="VERSION=84 created on Mon Feb 07 2011 20:34:02 PST"; diff -rc alpine-2.00/pico/attach.c alpine-2.00.I.USE/pico/attach.c *** alpine-2.00/pico/attach.c 2008-06-11 10:21:24.000000000 -0700 --- alpine-2.00.I.USE/pico/attach.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 108,115 **** switch(status){ case HELPCH: if(Pmaster){ - VARS_TO_SAVE *saved_state; - saved_state = save_pico_state(); (*Pmaster->helper)(Pmaster->attach_help, _("Attach Help"), 1); if(saved_state){ --- 108,113 ---- diff -rc alpine-2.00/pico/basic.c alpine-2.00.I.USE/pico/basic.c *** alpine-2.00/pico/basic.c 2007-11-26 15:45:22.000000000 -0800 --- alpine-2.00.I.USE/pico/basic.c 2011-02-07 20:33:44.000000000 -0800 *************** *** 25,33 **** * framing, are hard. */ #include "headers.h" ! #include "osdep/terminal.h" /* * Move the cursor to the --- 25,34 ---- * framing, are hard. */ #include "headers.h" ! #include "../pith/osdep/color.h" #include "osdep/terminal.h" + int indent_match(char *, LINE *, char *, int, int); /* * Move the cursor to the *************** *** 284,290 **** gotobop(int f, int n) { int quoted, qlen; ! UCS qstr[NLINE], qstr2[NLINE]; if (n < 0) /* the other way...*/ return(gotoeop(f, -n)); --- 285,291 ---- gotobop(int f, int n) { int quoted, qlen; ! char qstr[NLINE], qstr2[NLINE], ind_str[NLINE], pqstr[NLINE];; if (n < 0) /* the other way...*/ return(gotoeop(f, -n)); *************** *** 296,301 **** --- 297,310 ---- curwp->w_dotp = lback(curwp->w_dotp); curwp->w_doto = 0; } + + if (indent_match(default_qstr(), curwp->w_dotp,ind_str, NLINE, 0)){ + if (n){ /* look for another paragraph ? */ + curwp->w_dotp = lback(curwp->w_dotp); + continue; + } + break; + } /* scan line by line until we come to a line ending with * a or or *************** *** 303,322 **** * PLUS: if there's a quote string, a quoted-to-non-quoted * line transition. */ ! quoted = glo_quote_str ? quote_match(glo_quote_str, curwp->w_dotp, qstr, NLINE) : 0; ! qlen = quoted ? ucs4_strlen(qstr) : 0; while(lback(curwp->w_dotp) != curbp->b_linep && llength(lback(curwp->w_dotp)) > qlen ! && (glo_quote_str ! ? (quoted == quote_match(glo_quote_str, ! lback(curwp->w_dotp), ! qstr2, NLINE) ! && !ucs4_strcmp(qstr, qstr2)) ! : 1) ! && lgetc(curwp->w_dotp, qlen).c != TAB ! && lgetc(curwp->w_dotp, qlen).c != ' ') curwp->w_dotp = lback(curwp->w_dotp); if(n){ /* keep looking */ if(lback(curwp->w_dotp) == curbp->b_linep) --- 312,369 ---- * PLUS: if there's a quote string, a quoted-to-non-quoted * line transition. */ ! quoted = quote_match(default_qstr(), curwp->w_dotp, qstr, NLINE, 0); ! qlen = quoted ? strlen(qstr) : 0; while(lback(curwp->w_dotp) != curbp->b_linep && llength(lback(curwp->w_dotp)) > qlen ! && (quoted == quote_match(default_qstr(), ! lback(curwp->w_dotp), qstr2, NLINE, 0)) ! && !strcmp(qstr, qstr2) /* processed string */ ! && (quoted == quote_match(default_qstr(), ! lback(curwp->w_dotp), qstr2, NLINE, 1)) ! && !strcmp(qstr, qstr2) /* raw string */ ! && !indent_match(default_qstr(), ! lback(curwp->w_dotp),ind_str, NLINE, 0) ! && !ISspace(lgetc(curwp->w_dotp, qlen).c)) curwp->w_dotp = lback(curwp->w_dotp); + /* + * Ok, we made it here and we assume that we are at the begining + * of the paragraph. Let's double check this now. In order to do + * so we shell check if the first line was indented in a special + * way. + */ + if(lback(curwp->w_dotp) == curbp->b_linep) + break; + else{ + int i, j; + + /* + * First we test if the preceding line is indented. + * for the following test we need to have the raw values, + * not the processed values + */ + quote_match(default_qstr(), curwp->w_dotp, qstr, NLINE, 1); + quote_match(default_qstr(), lback(curwp->w_dotp), qstr2, NLINE, 1); + for (i = 0, j = 0; + qstr[i] && qstr2[i] && (qstr[i] == qstr2[i]); i++, j++); + for (; ISspace(qstr2[i]); i++); + for (; ISspace(qstr[j]); j++); + if ((indent_match(default_qstr(), lback(curwp->w_dotp), + ind_str, NLINE, 1) + && (strlenis(qstr2) + + strlenis(ind_str) >= strlenis(qstr))) + || (lback(curwp->w_dotp) != curbp->b_linep + && llength(lback(curwp->w_dotp)) > qlen + && (quoted == quote_match(default_qstr(), + lback(curwp->w_dotp), pqstr, NLINE, 0)) + && !strcmp(qstr, pqstr) + && !ISspace(lgetc(curwp->w_dotp, qlen).c) + && (strlenis(qstr2) > strlenis(qstr))) + && !qstr2[i] && !qstr[j]) + curwp->w_dotp = lback(curwp->w_dotp); + } + if(n){ /* keep looking */ if(lback(curwp->w_dotp) == curbp->b_linep) *************** *** 329,335 **** else{ /* leave cursor on first word in para */ curwp->w_doto = 0; ! while(ucs4_isspace(lgetc(curwp->w_dotp, curwp->w_doto).c)) if(++curwp->w_doto >= llength(curwp->w_dotp)){ curwp->w_doto = 0; curwp->w_dotp = lforw(curwp->w_dotp); --- 376,382 ---- else{ /* leave cursor on first word in para */ curwp->w_doto = 0; ! while(ISspace(lgetc(curwp->w_dotp, curwp->w_doto).c)) if(++curwp->w_doto >= llength(curwp->w_dotp)){ curwp->w_doto = 0; curwp->w_dotp = lforw(curwp->w_dotp); *************** *** 343,348 **** --- 390,578 ---- return(TRUE); } + unsigned char GetAccent() + { + UCS c,d; + c = GetKey(); + if ((c == '?') || (c == '!')) { + d = c; + c = '\\'; + } + else + if ((c == 's') || (c == 'S')){ + c = d = 's'; + } + else + if ((c == 'l') || (c == 'L')){ + c = d = 'l'; + } + else + d = GetKey(); + return accent(c,d); + } + + int pineaccent(f,n) + int f,n; + { unsigned char e; + + if (e = GetAccent()) + execute(e, 0, 1); + return 1; + } + + unsigned char accent(f,n) + UCS f,n; + { UCS c,d; + + c = f; + d = n; + switch(c){ + case '~' : + switch(d){ + case 'a' : return '\343'; + case 'n' : return '\361'; + case 'o' : return '\365'; + case 'A' : return '\303'; + case 'N' : return '\321'; + case 'O' : return '\325'; + } + break; + case '\047' : + switch(d){ + case 'a' : return '\341'; + case 'e' : return '\351'; + case 'i' : return '\355'; + case 'o' : return '\363'; + case 'u' : return '\372'; + case 'y' : return '\375'; + case 'A' : return '\301'; + case 'E' : return '\311'; + case 'I' : return '\315'; + case 'O' : return '\323'; + case 'U' : return '\332'; + case 'Y' : return '\335'; + } + break; + case '"' : + switch(d){ + case 'a' : return '\344'; + case 'e' : return '\353'; + case 'i' : return '\357'; + case 'o' : return '\366'; + case 'u' : return '\374'; + case 'y' : return '\377'; + case 'A' : return '\304'; + case 'E' : return '\313'; + case 'I' : return '\317'; + case 'O' : return '\326'; + case 'U' : return '\334'; + } + break; + case '^' : + switch(d){ + case 'a' : return '\342'; + case 'e' : return '\352'; + case 'i' : return '\356'; + case 'o' : return '\364'; + case 'u' : return '\373'; + case 'A' : return '\302'; + case 'E' : return '\312'; + case 'I' : return '\316'; + case 'O' : return '\324'; + case 'U' : return '\333'; + case '0' : return '\260'; + case '1' : return '\271'; + case '2' : return '\262'; + case '3' : return '\263'; + } + break; + case '`' : + switch(d){ + case 'a' : return '\340'; + case 'e' : return '\350'; + case 'i' : return '\354'; + case 'o' : return '\362'; + case 'u' : return '\371'; + case 'A' : return '\300'; + case 'E' : return '\310'; + case 'I' : return '\314'; + case 'O' : return '\322'; + case 'U' : return '\331'; + } + break; + case 'o' : + switch(d){ + case 'a' : return '\345'; + case 'A' : return '\305'; + case '/' : return '\370'; + case 'r' : return '\256'; + case 'R' : return '\256'; + case 'c' : return '\251'; + case 'C' : return '\251'; + } + break; + case '-' : + switch(d){ + case 'o' : return '\272'; + case 'O' : return '\272'; + case '0' : return '\272'; + case 'a' : return '\252'; + case 'A' : return '\252'; + case 'l' : return '\243'; + case 'L' : return '\243'; + } + break; + case 'O' : + switch(d){ + case '/' : return '\330'; + case 'r' : return '\256'; + case 'R' : return '\256'; + case 'c' : return '\251'; + case 'C' : return '\251'; + } + case '/' : + switch(d){ + case 'o' : return '\370'; + case 'O' : return '\330'; + } + break; + case 'a' : + switch(d){ + case 'e' : return '\346'; + case 'E' : return '\346'; + } + break; + case 'A' : + switch(d){ + case 'E' : return '\306'; + case 'e' : return '\306'; + } + break; + case ',' : + switch(d){ + case 'c' : return '\347'; + case 'C' : return '\307'; + } + break; + case '\\' : + switch(d){ + case '?' : return '\277'; + case '!' : return '\241'; + } + break; + case 's' : + switch(d){ + case 's' : return '\337'; + } + break; + case 'l' : + switch(d){ + case 'l' : return '\243'; + } + break; + } + return '\0'; + } /* * go forword to the end of the current paragraph *************** *** 352,359 **** int gotoeop(int f, int n) { ! int quoted, qlen; ! UCS qstr[NLINE], qstr2[NLINE]; if (n < 0) /* the other way...*/ return(gotobop(f, -n)); --- 582,590 ---- int gotoeop(int f, int n) { ! int quoted, qlen, indented, changeqstr = 0; ! int i,j, fli = 0; /* fli = first line indented a boolean variable */ ! char qstr[NLINE], qstr2[NLINE], ind_str[NLINE]; if (n < 0) /* the other way...*/ return(gotobop(f, -n)); *************** *** 366,392 **** break; } /* scan line by line until we come to a line ending with * a or or * * PLUS: if there's a quote string, a quoted-to-non-quoted * line transition. */ ! quoted = glo_quote_str ! ? quote_match(glo_quote_str, ! curwp->w_dotp, qstr, NLINE) : 0; ! qlen = quoted ? ucs4_strlen(qstr) : 0; while(curwp->w_dotp != curbp->b_linep && llength(lforw(curwp->w_dotp)) > qlen ! && (glo_quote_str ! ? (quoted == quote_match(glo_quote_str, ! lforw(curwp->w_dotp), ! qstr2, NLINE) ! && !ucs4_strcmp(qstr, qstr2)) ! : 1) ! && lgetc(lforw(curwp->w_dotp), qlen).c != TAB ! && lgetc(lforw(curwp->w_dotp), qlen).c != ' ') curwp->w_dotp = lforw(curwp->w_dotp); curwp->w_doto = llength(curwp->w_dotp); --- 597,666 ---- break; } + /* + * We need to figure out if this line is the first line of + * a paragraph that has been indented in a special way. If this + * is the case, we advance one more line before we use the + * algorithm below + */ + + if(curwp->w_dotp != curbp->b_linep){ + quote_match(default_qstr(), curwp->w_dotp, qstr, NLINE, 1); + quote_match(default_qstr(), lforw(curwp->w_dotp), qstr2, NLINE, 1); + indented = indent_match(default_qstr(), curwp->w_dotp, ind_str, + NLINE, 1); + if (strlenis(qstr) + + strlenis(ind_str) < strlenis(qstr2)){ + curwp->w_doto = llength(curwp->w_dotp); + if(n){ /* this line is a paragraph by itself */ + curwp->w_dotp = lforw(curwp->w_dotp); + continue; + } + break; + } + for (i=0,j=0; qstr[i] && qstr2[i] && (qstr[i] == qstr2[i]);i++,j++); + for (; ISspace(qstr[i]); i++); + for (; ISspace(qstr2[j]); j++); + if (!qstr[i] && !qstr2[j] && indented){ + fli++; + if (indent_match(default_qstr(), lforw(curwp->w_dotp), + ind_str, NLINE, 0)){ + if (n){ /* look for another paragraph ? */ + curwp->w_dotp = lforw(curwp->w_dotp); + continue; + } + } + else{ + if (!lisblank(lforw(curwp->w_dotp))) + curwp->w_dotp = lforw(curwp->w_dotp); + } + } + } + /* scan line by line until we come to a line ending with * a or or * * PLUS: if there's a quote string, a quoted-to-non-quoted * line transition. */ ! /* if the first line is indented (fli == 1), then the test below ! is on the second line, and in that case we will need the raw ! string, not the processed string ! */ ! quoted = quote_match(default_qstr(), curwp->w_dotp, qstr, NLINE, fli); ! qlen = quoted ? strlen(qstr) : 0; while(curwp->w_dotp != curbp->b_linep && llength(lforw(curwp->w_dotp)) > qlen ! && (quoted == quote_match(default_qstr(), ! lforw(curwp->w_dotp), qstr2, NLINE, fli)) ! && !strcmp(qstr, qstr2) ! && (quoted == quote_match(default_qstr(), ! lforw(curwp->w_dotp), qstr2, NLINE, 1)) ! && !strcmp(qstr, qstr2) ! && !indent_match(default_qstr(), ! lforw(curwp->w_dotp), ind_str, NLINE, 0) ! && !ISspace(lgetc(lforw(curwp->w_dotp), qlen).c)) curwp->w_dotp = lforw(curwp->w_dotp); curwp->w_doto = llength(curwp->w_dotp); *************** *** 683,689 **** --- 957,1013 ---- return (scrollforw (1, FALSE)); } + /* deltext deletes from the specified position until the end of the file + * or until the signature (when called from Pine), whichever comes first. + */ + int + deltext (f,n) + int f,n; + { + LINE *currline = curwp->w_dotp; + static int firsttime = 0; + + if ((lastflag&CFKILL) == 0) + kdelete(); + + curwp->w_markp = curwp->w_dotp; + curwp->w_marko = curwp->w_doto; + + while (curwp->w_dotp != curbp->b_linep){ + if ((Pmaster) + && (llength(curwp->w_dotp) == 3) + && (lgetc(curwp->w_dotp, 0).c == '-') + && (lgetc(curwp->w_dotp, 1).c == '-') + && (lgetc(curwp->w_dotp, 2).c == ' ')){ + if (curwp->w_dotp == currline){ + if (curwp->w_doto) + curwp->w_dotp = lforw(curwp->w_dotp); + else + break; + } + else{ + curwp->w_dotp = lback(curwp->w_dotp); + curwp->w_doto = llength(curwp->w_dotp); + break; + } + } + else{ + if(lforw(curwp->w_dotp) != curbp->b_linep) + curwp->w_dotp = lforw(curwp->w_dotp); + else{ + curwp->w_doto = llength(curwp->w_dotp); + break; + } + } + } + killregion(FALSE,1); + lastflag |= CFKILL; + if(firsttime == 0) + emlwrite("Deleted text can be recovered with the ^U command", NULL); + firsttime = 1; + return TRUE; + } /* * Scroll to a position. diff -rc alpine-2.00/pico/bind.c alpine-2.00.I.USE/pico/bind.c *** alpine-2.00/pico/bind.c 2007-12-07 15:45:22.000000000 -0800 --- alpine-2.00.I.USE/pico/bind.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 132,139 **** } if(Pmaster){ - VARS_TO_SAVE *saved_state; - saved_state = save_pico_state(); (*Pmaster->helper)(Pmaster->composer_help, Pmaster->headents --- 132,137 ---- diff -rc alpine-2.00/pico/blddate.c alpine-2.00.I.USE/pico/blddate.c *** alpine-2.00/pico/blddate.c 2006-11-22 14:50:13.000000000 -0800 --- alpine-2.00.I.USE/pico/blddate.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 19,25 **** char **argv; { struct tm *t; ! FILE *outfile=stdout; time_t ltime; if(argc > 1 && (outfile = fopen(argv[1], "w")) == NULL){ --- 19,25 ---- char **argv; { struct tm *t; ! FILE *outfile=stdout, *infile; time_t ltime; if(argc > 1 && (outfile = fopen(argv[1], "w")) == NULL){ *************** *** 46,51 **** --- 46,57 ---- 1900 + t->tm_year); fprintf(outfile, "char hoststamp[]=\"random-pc\";\n"); + if((infile = fopen("../patchlevel", "r")) != NULL){ + int c; + while ((c = getc(infile)) != EOF) putc(c, outfile); + fclose(infile); + } + else fprintf(outfile, "char plevstamp[]=\"No information available\";\n"); fclose(outfile); diff -rc alpine-2.00/pico/browse.c alpine-2.00.I.USE/pico/browse.c *** alpine-2.00/pico/browse.c 2008-02-26 15:07:15.000000000 -0800 --- alpine-2.00.I.USE/pico/browse.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 931,938 **** } if(Pmaster){ - VARS_TO_SAVE *saved_state; - saved_state = save_pico_state(); (*Pmaster->helper)(Pmaster->browse_help, _("Help for Browsing"), 1); --- 931,936 ---- diff -rc alpine-2.00/pico/composer.c alpine-2.00.I.USE/pico/composer.c *** alpine-2.00/pico/composer.c 2008-03-21 14:31:58.000000000 -0700 --- alpine-2.00.I.USE/pico/composer.c 2011-02-07 20:33:47.000000000 -0800 *************** *** 93,98 **** --- 93,103 ---- */ struct on_display ods; /* global on_display struct */ + /* a pointer to the subject line. This is so that we do not have to compute + * the header line in every call. It saves a few CPU cycles + */ + + struct hdr_line *subject_line = NULL; /* * useful macros *************** *** 495,500 **** --- 500,510 ---- mswin_setscrollrange (0, 0); #endif /* _WINDOWS */ + if(Pmaster) + for (subject_line = NULL, i=0; headents[i].name; i++) + if(strcmp(headents[i].name, "Subject") == 0) + subject_line = headents[i].hd_text; + /* * Decide where to begin editing. if f == TRUE begin editing * at the bottom. this case results from the cursor backing *************** *** 794,802 **** len += lmp->fname ? strlen(lmp->fname) : 0; if(len+3 > sizeof(buf)){ - bfp = malloc(len+3); space = len+3; ! if((bfp=malloc(len+3)) == NULL){ emlwrite("\007Can't malloc space for filename", NULL); continue; --- 804,812 ---- len += lmp->fname ? strlen(lmp->fname) : 0; if(len+3 > sizeof(buf)){ space = len+3; ! bfp = malloc(space*sizeof(char)); ! if(bfp == NULL){ emlwrite("\007Can't malloc space for filename", NULL); continue; *************** *** 1270,1278 **** len += strlen(lmp->size); if(len+3 > sizeof(buf)){ - bfp = malloc(len+3); space = len+3; ! if((bfp=malloc(len+3)) == NULL){ emlwrite("\007Can't malloc space for filename", NULL); continue; --- 1280,1288 ---- len += strlen(lmp->size); if(len+3 > sizeof(buf)){ space = len+3; ! bfp = malloc(space*sizeof(char)); ! if(bfp == NULL){ emlwrite("\007Can't malloc space for filename", NULL); continue; *************** *** 1309,1316 **** zotlmlist(lm); } /* else, nothing of interest */ } else if (headents[ods.cur_e].selector != NULL) { - VARS_TO_SAVE *saved_state; - /*---- General selector for non-attachments -----*/ /* --- 1319,1324 ---- *************** *** 1485,1491 **** struct hdr_line *line; int sz = 0; char *filename = NULL; - VARS_TO_SAVE *saved_state; /* * Since the fileedit will make a new call back to pico() --- 1493,1498 ---- *************** *** 1852,1857 **** --- 1859,1866 ---- } UpdateHeader(0); + if(sendnow) + return(status !=0); PaintHeader(COMPOSER_TOP_LINE, status != 0); PaintBody(1); return(status != 0); *************** *** 1899,1904 **** --- 1908,1918 ---- while(1){ /* edit the line... */ + if(Pmaster && subject_line != NULL + && ods.cur_l == subject_line + && ods.cur_l->text[0] == 0) + (*Pmaster->newthread)(); + if(skipmove) skipmove = 0; else *************** *** 1990,1996 **** tbufp = &strng[ods.p_len]; if(VALID_KEY(ch)){ /* char input */ ! /* * if we are allowing editing, insert the new char * end up leaving tbufp pointing to newly * inserted character in string, and offset to the --- 2004,2010 ---- tbufp = &strng[ods.p_len]; if(VALID_KEY(ch)){ /* char input */ ! insert_char:/* * if we are allowing editing, insert the new char * end up leaving tbufp pointing to newly * inserted character in string, and offset to the *************** *** 2070,2075 **** --- 2084,2096 ---- } else { /* interpret ch as a command */ switch (ch = normalize_cmd(ch, ckm, 2)) { + case (CTRL|'\\') : + if (ch = GetAccent()) + goto insert_char; + else + clearcursor(); + break; + case (CTRL|KEY_LEFT): /* word skip left */ if(ods.p_ind > 0) /* Scoot one char left if possible */ ods.p_ind--; *************** *** 2937,2943 **** ComposerHelp(int level) { char buf[80]; - VARS_TO_SAVE *saved_state; curwp->w_flag |= WFMODE; sgarbf = TRUE; --- 2958,2963 ---- *************** *** 3337,3342 **** --- 3357,3365 ---- { UCS *bufp, *buf; + if (sendnow) + return; + if(ComposerTopLine - 1 >= BOTTOM()) /* silently forget it */ return; *************** *** 3393,3398 **** --- 3416,3424 ---- UCS *end; int i; + if (sendnow) + return(TRUE); + buf = utf8_to_ucs4_cpystr(headents[entry].prompt); /* fresh prompt paint */ if(!buf) return(-1); *************** *** 3606,3612 **** char *tmp; struct headerentry *e; BUILDER_ARG *nextarg, *arg = NULL, *headarg = NULL; - VARS_TO_SAVE *saved_state; if(!entry->builder) return(0); --- 3632,3637 ---- *************** *** 3826,3832 **** call_expander(void) { char **s = NULL; - VARS_TO_SAVE *saved_state; int expret; if(!Pmaster->expander) --- 3851,3856 ---- *************** *** 4348,4353 **** --- 4372,4380 ---- void ShowPrompt(void) { + if (sendnow) + return; + if(headents[ods.cur_e].key_label){ menu_header[TO_KEY].name = "^T"; menu_header[TO_KEY].label = headents[ods.cur_e].key_label; *************** *** 4716,4721 **** --- 4743,4750 ---- free(state->opertree); free(state); + + state = NULL; } diff -rc alpine-2.00/pico/display.c alpine-2.00.I.USE/pico/display.c *** alpine-2.00/pico/display.c 2008-03-26 10:27:45.000000000 -0700 --- alpine-2.00.I.USE/pico/display.c 2011-02-07 20:33:44.000000000 -0800 *************** *** 386,391 **** --- 386,394 ---- int scroll = 0; CELL c; + if (sendnow) + return; + #if TYPEAH if (typahead()) return; *************** *** 915,921 **** int nbflag; /* non-blanks to the right flag? */ int cleartoeol = 0; ! if(row < 0 || row > term.t_nrow) return; /* set up pointers to virtual and physical lines */ --- 918,924 ---- int nbflag; /* non-blanks to the right flag? */ int cleartoeol = 0; ! if(row < 0 || row > term.t_nrow || sendnow) return; /* set up pointers to virtual and physical lines */ *************** *** 1284,1290 **** void mlerase(void) { ! if (term.t_nrow < term.t_mrow) return; movecursor(term.t_nrow - term.t_mrow, 0); --- 1287,1293 ---- void mlerase(void) { ! if (term.t_nrow < term.t_mrow || sendnow) return; movecursor(term.t_nrow - term.t_mrow, 0); *************** *** 1359,1364 **** --- 1362,1371 ---- menu_yesno[6].label = N_("Cancel"); menu_yesno[7].name = "N"; menu_yesno[7].label = (dflt == FALSE) ? "[" N_("No") "]" : N_("No"); + if(Pmaster && Pmaster->onctrlc){ + menu_yesno[8].name = "T"; + menu_yesno[8].label = N_("counT"); + } wkeyhelp(menu_yesno); /* paint generic menu */ sgarbk = TRUE; /* mark menu dirty */ if(Pmaster && curwp) *************** *** 1437,1442 **** --- 1444,1457 ---- km_popped++; break; } + + case 'T': + case 't': + if(Pmaster && Pmaster->onctrlc){ + pputs_utf8(_("counT"), 1); + rv = COUNT; + break; + } /* else fall through */ default: *************** *** 1750,1755 **** --- 1765,1775 ---- b = &buf[ucs4_strlen(buf)]; continue; + case (CTRL|'\\'): + if (c = GetAccent()) + goto text; + continue; + case (CTRL|'F') : /* CTRL-F forward a char*/ case KEY_RIGHT : if(*b == '\0') *************** *** 1759,1764 **** --- 1779,1796 ---- continue; + case (CTRL|'N'): /* Insert pattern */ + if (pat[0] != '\0'){ + ucs4_strncpy(buf+ucs4_strlen(buf), pat, NPAT); + pputs(pat,1); + b = &buf[ucs4_strlen(buf)]; + dline.vused += ucs4_strlen(pat); + changed = TRUE; + } + else + (*term.t_beep)(); + continue; + case (CTRL|'G') : /* CTRL-G help */ if(term.t_mrow == 0 && km_popped == 0){ movecursor(term.t_nrow-2, 0); *************** *** 1868,1874 **** #endif default : ! /* look for match in extra_v */ for(i = 0; i < 12; i++) if(c && c == extra_v[i]){ --- 1900,1906 ---- #endif default : ! text: /* look for match in extra_v */ for(i = 0; i < 12; i++) if(c && c == extra_v[i]){ *************** *** 1962,1968 **** mlerase(); ! if(!(message && *message) || term.t_nrow < 2) return; /* nothing to write or no space to write, bag it */ bufp = message; --- 1994,2000 ---- mlerase(); ! if(!(message && *message) || term.t_nrow < 2 || sendnow) return; /* nothing to write or no space to write, bag it */ bufp = message; *************** *** 2151,2158 **** } ret = ttcol; ! while(ttcol < term.t_ncol) ! pputc(' ', 0); movecursor(term.t_nrow - term.t_mrow, ret); --- 2183,2191 ---- } ret = ttcol; ! if(sendnow == 0) ! while(ttcol < term.t_ncol) ! pputc(' ', 0); movecursor(term.t_nrow - term.t_mrow, ret); *************** *** 2631,2636 **** --- 2664,2671 ---- { int ind, width, printable_ascii = 0; + if(sendnow) + return; /* * This is necessary but not sufficient to allow us to draw. Note that * ttrow runs from 0 to t_nrow (so total number of rows is t_nrow+1) *************** *** 2685,2690 **** --- 2720,2727 ---- pputs(UCS *s, /* string to write */ int a) /* and its attribute */ { + if(sendnow) + return; while (*s != '\0') pputc(*s++, a); } *************** *** 2695,2700 **** --- 2732,2739 ---- { UCS *ucsstr = NULL; + if(sendnow) + return; if(s && *s){ ucsstr = utf8_to_ucs4_cpystr(s); if(ucsstr){ *************** *** 2995,3000 **** --- 3034,3042 ---- char nbuf[NLINE]; #endif + if(sendnow) + return; + #ifdef _WINDOWS pico_config_menu_items (keymenu); #endif diff -rc alpine-2.00/pico/ebind.h alpine-2.00.I.USE/pico/ebind.h *** alpine-2.00/pico/ebind.h 2007-11-06 15:51:13.000000000 -0800 --- alpine-2.00.I.USE/pico/ebind.h 2011-02-07 20:33:43.000000000 -0800 *************** *** 61,67 **** #ifdef MOUSE {KEY_MOUSE, mousepress}, #ifndef _WINDOWS ! {CTRL|'\\', toggle_xterm_mouse}, #endif #endif {CTRL|'A', gotobol}, --- 61,67 ---- #ifdef MOUSE {KEY_MOUSE, mousepress}, #ifndef _WINDOWS ! {CTRL|'|', toggle_xterm_mouse}, #endif #endif {CTRL|'A', gotobol}, *************** *** 100,106 **** {CTRL|KEY_HOME, gotobob}, {CTRL|KEY_END, gotoeob}, {0x7F, backdel}, ! {0, NULL} }; --- 100,108 ---- {CTRL|KEY_HOME, gotobob}, {CTRL|KEY_END, gotoeob}, {0x7F, backdel}, ! {CTRL|'\\', pineaccent}, ! {0, ! NULL} }; *************** *** 123,129 **** #ifdef MOUSE {KEY_MOUSE, mousepress}, #ifndef _WINDOWS ! {CTRL|'\\', toggle_xterm_mouse}, #endif #endif {CTRL|'A', gotobol}, --- 125,131 ---- #ifdef MOUSE {KEY_MOUSE, mousepress}, #ifndef _WINDOWS ! {CTRL|'|', toggle_xterm_mouse}, #endif #endif {CTRL|'A', gotobol}, diff -rc alpine-2.00/pico/edef.h alpine-2.00.I.USE/pico/edef.h *** alpine-2.00/pico/edef.h 2008-01-04 16:41:49.000000000 -0800 --- alpine-2.00.I.USE/pico/edef.h 2011-02-07 20:33:47.000000000 -0800 *************** *** 32,37 **** --- 32,38 ---- /* initialized global definitions */ + int sendnow = 0; /* should we send now */ int fillcol = 72; /* Current fill column */ int userfillcol = -1; /* Fillcol set from cmd line */ UCS pat[NPAT]; /* Search pattern */ *************** *** 55,60 **** --- 56,62 ---- char *keyboard_character_set = NULL; UCS *glo_wordseps = NULL; /* points to word separators if set */ char *glo_wordseps_orig = NULL; + VARS_TO_SAVE *saved_state = NULL; /* uninitialized global definitions */ int currow; /* Cursor row */ *************** *** 84,89 **** --- 86,92 ---- /* initialized global external declarations */ + extern int sendnow; /* should we send now */ extern int fillcol; /* Fill column */ extern int userfillcol; /* Fillcol set from cmd line */ extern UCS pat[]; /* Search pattern */ *************** *** 149,154 **** --- 152,158 ---- extern KBESC_T *kbesc; /* keyboard esc sequence trie */ #endif /* HAS_TERMCAP/HAS_TERMINFO/VMS */ extern void *input_cs; /* passed to mbtow() via kbseq() */ + extern VARS_TO_SAVE *saved_state; #endif /* maindef */ diff -rc alpine-2.00/pico/efunc.h alpine-2.00.I.USE/pico/efunc.h *** alpine-2.00/pico/efunc.h 2007-11-06 15:51:13.000000000 -0800 --- alpine-2.00.I.USE/pico/efunc.h 2011-02-07 20:33:44.000000000 -0800 *************** *** 53,60 **** --- 53,64 ---- extern int backline(int, int); extern int gotobop(int, int); extern int gotoeop(int, int); + extern int pineaccent(int, int); + extern unsigned char accent(UCS, UCS); + extern unsigned char GetAccent(void); extern int forwpage(int, int); extern int backpage(int, int); + extern int deltext (int, int); extern int scrollupline(int, int); extern int scrolldownline(int, int); extern int scrollto(int, int); *************** *** 248,257 **** extern int fillpara(int, int); extern int fillbuf(int, int); extern int inword(void); ! extern int quote_match(UCS *, LINE *, UCS *, size_t); extern int ucs4_isalnum(UCS); extern int ucs4_isalpha(UCS); extern int ucs4_isspace(UCS); extern int ucs4_ispunct(UCS); #endif /* EFUNC_H */ --- 252,268 ---- extern int fillpara(int, int); extern int fillbuf(int, int); extern int inword(void); ! extern int quote_match(char *, LINE *, char *, size_t, int); ! extern char *default_qstr(void); ! extern void flatten_qstring(QSTRING_S *, char *, int); ! extern void free_qs(QSTRING_S **); ! extern QSTRING_S *do_quote_match (char *, char *, char *, char *, char *, int, int); ! extern QSTRING_S *do_raw_quote_match(char *, char *, char *, char *, QSTRING_S **, QSTRING_S **); ! extern int indent_match(char *, LINE *, char *, int, int); extern int ucs4_isalnum(UCS); extern int ucs4_isalpha(UCS); extern int ucs4_isspace(UCS); extern int ucs4_ispunct(UCS); #endif /* EFUNC_H */ + diff -rc alpine-2.00/pico/file.c alpine-2.00.I.USE/pico/file.c *** alpine-2.00/pico/file.c 2008-06-11 10:21:24.000000000 -0700 --- alpine-2.00.I.USE/pico/file.c 2011-02-07 20:33:47.000000000 -0800 *************** *** 331,338 **** #endif /* !(DOS || MAC) */ case HELPCH: if(Pmaster){ - VARS_TO_SAVE *saved_state; - saved_state = save_pico_state(); (*Pmaster->helper)(msg ? Pmaster->ins_m_help : Pmaster->ins_help, --- 331,336 ---- diff -rc alpine-2.00/pico/line.c alpine-2.00.I.USE/pico/line.c *** alpine-2.00/pico/line.c 2007-08-15 16:07:18.000000000 -0700 --- alpine-2.00.I.USE/pico/line.c 2011-02-07 20:33:42.000000000 -0800 *************** *** 607,620 **** lisblank(LINE *line) { int n = 0; ! UCS qstr[NLINE]; ! n = (glo_quote_str ! && quote_match(glo_quote_str, line, qstr, NLINE)) ! ? ucs4_strlen(qstr) : 0; for(; n < llength(line); n++) ! if(!ucs4_isspace(lgetc(line, n).c)) return(FALSE); return(TRUE); --- 607,618 ---- lisblank(LINE *line) { int n = 0; ! char qstr[NLINE]; ! n = quote_match(default_qstr(), line, qstr, NLINE, 1); for(; n < llength(line); n++) ! if(!ISspace(lgetc(line, n).c)) return(FALSE); return(TRUE); diff -rc alpine-2.00/pico/main.c alpine-2.00.I.USE/pico/main.c *** alpine-2.00/pico/main.c 2008-04-02 15:09:20.000000000 -0700 --- alpine-2.00.I.USE/pico/main.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 162,167 **** --- 162,168 ---- char *file_to_edit = NULL; char *display_charmap = NULL; char *keyboard_charmap = NULL; + int line_information_on = FALSE; int use_system = 0; char *err = NULL; *************** *** 187,193 **** if(display_character_set) display_charmap = cpstr(display_character_set); #if HAVE_LANGINFO_H && defined(CODESET) ! else display_charmap = cpstr(nl_langinfo_codeset_wrapper()); #endif --- 188,194 ---- if(display_character_set) display_charmap = cpstr(display_character_set); #if HAVE_LANGINFO_H && defined(CODESET) ! else if (nl_langinfo_codeset_wrapper() != NULL) display_charmap = cpstr(nl_langinfo_codeset_wrapper()); #endif *************** *** 415,420 **** --- 416,427 ---- emlwrite(_("You may possibly have new mail."), NULL); } + if (c == (CTRL|'\\')){ + c = GetAccent(); + if (!c) + c = NODATA; + } + if(km_popped) switch(c){ case NODATA: *************** *** 436,449 **** mlerase(); } ! f = FALSE; n = 1; #ifdef MOUSE clear_mfunc(mouse_in_content); #endif /* Do it. */ execute(normalize_cmd(c, fkm, 1), f, n); } } --- 443,471 ---- mlerase(); } ! f = (c == (CTRL|'J')); n = 1; + if (!line_information_on) + line_information_on = (c == (CTRL|'C')); + else + line_information_on = ((c == KEY_DOWN) || (c == KEY_UP) || + (c == KEY_RIGHT) || (c == KEY_LEFT) || + (c == (CTRL|'V')) || (c == (CTRL|'Y')) || + (c == (CTRL|'D')) || (c == (CTRL|'F')) || + (c == (CTRL|'B')) || (c == (CTRL|'N')) || + (c == (CTRL|'P')) || (c == (CTRL|'A')) || + (c == (CTRL|'E')) || (c == (CTRL|'U'))) + && (c != (CTRL|'C')); #ifdef MOUSE clear_mfunc(mouse_in_content); #endif /* Do it. */ execute(normalize_cmd(c, fkm, 1), f, n); + if (line_information_on){ + c = (CTRL|'C'); + execute(normalize_cmd(c, fkm, 1), f, n); + } } } diff -rc alpine-2.00/pico/osdep/getkey.c alpine-2.00.I.USE/pico/osdep/getkey.c *** alpine-2.00/pico/osdep/getkey.c 2007-08-20 12:46:37.000000000 -0700 --- alpine-2.00.I.USE/pico/osdep/getkey.c 2011-02-07 20:33:42.000000000 -0800 *************** *** 130,135 **** --- 130,145 ---- { UCS ch, status, cc; + if(sendnow){ + ch = Pmaster && Pmaster->auto_cmds && *Pmaster->auto_cmds + ? *Pmaster->auto_cmds++ : NODATA; + + if (ch >= 0x00 && ch <= 0x1F) + ch = CTRL | (ch+'@'); + + return(ch); + } + if(!ReadyForKey(FUDGE-5)) return(NODATA); diff -rc alpine-2.00/pico/osdep/terminal.c alpine-2.00.I.USE/pico/osdep/terminal.c *** alpine-2.00/pico/osdep/terminal.c 2008-01-29 15:21:10.000000000 -0800 --- alpine-2.00.I.USE/pico/osdep/terminal.c 2011-02-07 20:33:42.000000000 -0800 *************** *** 25,30 **** --- 25,31 ---- #include "../keydefs.h" #include "../pico.h" #include "../mode.h" + #include "../edef.h" #include "raw.h" #include "color.h" *************** *** 477,482 **** --- 478,489 ---- { int row, col; + if (sendnow){ + term.t_nrow = 23; + term.t_ncol = 80; + return 0; + } + /* * determine the terminal's communication speed and decide * if we need to do optimization ... *************** *** 1252,1257 **** --- 1259,1270 ---- { int row, col; + if (sendnow){ + term.t_nrow = 23; + term.t_ncol = 80; + return 0; + } + /* * determine the terminal's communication speed and decide * if we need to do optimization ... diff -rc alpine-2.00/pico/osdep/tty.c alpine-2.00.I.USE/pico/osdep/tty.c *** alpine-2.00/pico/osdep/tty.c 2007-08-15 16:07:18.000000000 -0700 --- alpine-2.00.I.USE/pico/osdep/tty.c 2011-02-07 20:33:47.000000000 -0800 *************** *** 223,229 **** int row = -1, col = -1; ttgetwinsz(&row, &col); ! resize_pico(row, col); } --- 223,236 ---- int row = -1, col = -1; ttgetwinsz(&row, &col); ! if(saved_state == NULL || wheadp != NULL) ! resize_pico(row, col); ! else{ /* this is awkward */ ! restore_pico_state(saved_state); ! free_pico_state(saved_state); ! resize_pico(row,col); ! saved_state = save_pico_state(); ! } } diff -rc alpine-2.00/pico/pico.c alpine-2.00.I.USE/pico/pico.c *** alpine-2.00/pico/pico.c 2008-01-30 16:44:12.000000000 -0800 --- alpine-2.00.I.USE/pico/pico.c 2011-02-07 20:33:44.000000000 -0800 *************** *** 137,142 **** --- 137,151 ---- pico_all_done = 0; km_popped = 0; + if (pm->auto_cmds){ + int i; + #define CTRL_X 24 + for (i = 0; pm->auto_cmds[i]; i++); + if ((i > 1) && (pm->auto_cmds[i - 2] == CTRL_X) && + ((pm->auto_cmds[i - 1] == 'y') || (pm->auto_cmds[i-1] == 'Y'))) + sendnow++; + } + if(!vtinit()) /* Init Displays. */ return(COMP_CANCEL); *************** *** 637,648 **** result = ""; Pmaster->arm_winch_cleanup++; if(Pmaster->canceltest){ if(((Pmaster->pine_flags & MDHDRONLY) && !any_header_changes()) || (result = (*Pmaster->canceltest)(redraw_pico_for_callback))){ - pico_all_done = COMP_CANCEL; emlwrite(result, NULL); Pmaster->arm_winch_cleanup--; return(TRUE); } else{ --- 646,664 ---- result = ""; Pmaster->arm_winch_cleanup++; + Pmaster->onctrlc++; if(Pmaster->canceltest){ if(((Pmaster->pine_flags & MDHDRONLY) && !any_header_changes()) || (result = (*Pmaster->canceltest)(redraw_pico_for_callback))){ emlwrite(result, NULL); Pmaster->arm_winch_cleanup--; + if(Pmaster->curpos[0]){ + curwp->w_flag |= WFMODE; /* and modeline so we */ + sgarbk = TRUE; /* redraw the keymenu */ + pclear(term.t_nrow - 1, term.t_nrow + 1); + return(FALSE); + } + pico_all_done = COMP_CANCEL; return(TRUE); } else{ *************** *** 671,676 **** --- 687,698 ---- emlwrite(_("\007Cancel Cancelled"), NULL); break; + case COUNT: + showcpos(1,0); + emlwrite(Pmaster->curpos, NULL); + Pmaster->onctrlc--; + break; + default: mlerase(); } *************** *** 713,718 **** --- 735,753 ---- return(FALSE); } + /* When we send a message using the command line we are going to + ignore if the user wants to spell check, we assume he already + did */ + if (sendnow){ + ret = (*Pmaster->exittest)(Pmaster->headents, + redraw_pico_for_callback, + Pmaster->allow_flowed_text, + &result); + if (!ret) + pico_all_done = COMP_EXIT; + return(result ? FALSE : TRUE); + } + #ifdef SPELLER if(Pmaster->always_spell_check) if(spell(0, 0) == -1) diff -rc alpine-2.00/pico/pico.h alpine-2.00.I.USE/pico/pico.h *** alpine-2.00/pico/pico.h 2008-08-22 13:40:16.000000000 -0700 --- alpine-2.00.I.USE/pico/pico.h 2011-02-07 20:33:46.000000000 -0800 *************** *** 198,208 **** --- 198,210 ---- PCOLORS *colors; /* colors for titlebar and keymenu */ void *input_cs; /* passed to mbtow() via kbseq() */ long pine_flags; /* entry mode flags */ + char curpos[80]; /* where are we now? */ /* The next few bits are features that don't fit in pine_flags */ /* If we had this to do over, it would probably be one giant bitmap */ unsigned always_spell_check:1; /* always spell-checking upon quit */ unsigned strip_ws_before_send:1; /* don't default strip bc of flowed */ unsigned allow_flowed_text:1; /* clean text when done to keep flowed */ + unsigned onctrlc; /* are we on ctrl-c command? */ int (*helper)(); /* Pine's help function */ int (*showmsg)(); /* Pine's display_message */ UCS (*suspend)(); /* Pine's suspend */ *************** *** 219,225 **** --- 221,229 ---- int (*user_says_noflow)(); /* callback to tell us we're not flowing */ void (*resize)(); /* callback handling screen resize */ void (*winch_cleanup)(); /* callback handling screen resize */ + void (*newthread)(); /* callback to create new thread */ int arm_winch_cleanup; /* do the winch_cleanup if resized */ + int *auto_cmds; /* Initial keystroke commands */ HELP_T search_help; HELP_T ins_help; HELP_T ins_m_help; diff -rc alpine-2.00/pico/pilot.c alpine-2.00.I.USE/pico/pilot.c *** alpine-2.00/pico/pilot.c 2008-04-02 15:09:20.000000000 -0700 --- alpine-2.00.I.USE/pico/pilot.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 142,148 **** if(display_character_set) display_charmap = cpstr(display_character_set); #if HAVE_LANGINFO_H && defined(CODESET) ! else display_charmap = cpstr(nl_langinfo_codeset_wrapper()); #endif --- 142,148 ---- if(display_character_set) display_charmap = cpstr(display_character_set); #if HAVE_LANGINFO_H && defined(CODESET) ! else if (nl_langinfo_codeset_wrapper() != NULL) display_charmap = cpstr(nl_langinfo_codeset_wrapper()); #endif diff -rc alpine-2.00/pico/random.c alpine-2.00.I.USE/pico/random.c *** alpine-2.00/pico/random.c 2007-08-15 16:07:18.000000000 -0700 --- alpine-2.00.I.USE/pico/random.c 2011-02-07 20:33:44.000000000 -0800 *************** *** 73,79 **** thisline+1, lines+1, (int)((100L*(thisline+1))/(lines+1)), nbc, nch, (nch) ? (int)((100L*nbc)/nch) : 0); ! emlwrite(buffer, NULL); return (TRUE); } --- 73,82 ---- thisline+1, lines+1, (int)((100L*(thisline+1))/(lines+1)), nbc, nch, (nch) ? (int)((100L*nbc)/nch) : 0); ! if(Pmaster) ! strcpy(Pmaster->curpos, buffer); ! else ! emlwrite(buffer, NULL); return (TRUE); } diff -rc alpine-2.00/pico/search.c alpine-2.00.I.USE/pico/search.c *** alpine-2.00/pico/search.c 2008-01-04 14:49:15.000000000 -0800 --- alpine-2.00.I.USE/pico/search.c 2011-02-07 20:33:47.000000000 -0800 *************** *** 35,41 **** int readpattern(char *, int); int replace_pat(UCS *, int *); int replace_all(UCS *, UCS *); ! #define FWS_RETURN(RV) { \ thisflag |= CFSRCH; \ --- 35,41 ---- int readpattern(char *, int); int replace_pat(UCS *, int *); int replace_all(UCS *, UCS *); ! int deletepara(int, int); #define FWS_RETURN(RV) { \ thisflag |= CFSRCH; \ *************** *** 75,80 **** --- 75,84 ---- N_("~ Hitting only ~R~e~t~u~r~n or at the prompt will cause the"), N_(" search to be made with the default value."), " ", + N_("~ Hitting ~^~N will reinsert the last string you searched for"), + N_(" so that you can edit it (in case you made a mistake entering the"), + N_(" search pattern the first time)."), + " ", N_(" The text search is not case sensitive, and will examine the"), N_(" entire message."), " ", *************** *** 139,146 **** case HELPCH: /* help requested */ if(Pmaster){ - VARS_TO_SAVE *saved_state; - saved_state = save_pico_state(); (*Pmaster->helper)(Pmaster->search_help, _("Help for Searching"), 1); --- 143,148 ---- *************** *** 231,240 **** --- 233,251 ---- mlerase(); FWS_RETURN(TRUE); + case (CTRL|'P'): + deletepara(0, 1); + mlerase(); + FWS_RETURN(TRUE); + case (CTRL|'R'): /* toggle replacement option */ repl_mode = !repl_mode; break; + case (CTRL|'X'): + deltext(f,n); + FWS_RETURN(TRUE); + default: if(status == ABORT) emlwrite(_("Search Cancelled"), NULL); *************** *** 273,279 **** } if(status + curwp->w_doto >= llength(curwp->w_dotp) || ! !eq(defpat[status],lgetc(curwp->w_dotp, curwp->w_doto + status).c)) break; /* do nothing! */ status++; } --- 284,290 ---- } if(status + curwp->w_doto >= llength(curwp->w_dotp) || ! !eq((unsigned char)defpat[status],lgetc(curwp->w_dotp, curwp->w_doto + status).c)) break; /* do nothing! */ status++; } *************** *** 430,437 **** case HELPCH: /* help requested */ if(Pmaster){ - VARS_TO_SAVE *saved_state; - saved_state = save_pico_state(); (*Pmaster->helper)(Pmaster->search_help, _("Help for Searching"), 1); --- 441,446 ---- *************** *** 463,470 **** break; default: ! if(status == ABORT) emlwrite(_("Replacement Cancelled"), NULL); else{ mlerase(); chword(defpat, origpat); --- 472,481 ---- break; default: ! if(status == ABORT){ emlwrite(_("Replacement Cancelled"), NULL); + pico_refresh(FALSE, 1); + } else{ mlerase(); chword(defpat, origpat); *************** *** 597,603 **** UCS *b; UCS prompt[NPMT]; UCS *promptp; ! EXTRAKEYS menu_pat[8]; menu_pat[i = 0].name = "^Y"; menu_pat[i].label = N_("FirstLine"); --- 608,614 ---- UCS *b; UCS prompt[NPMT]; UCS *promptp; ! EXTRAKEYS menu_pat[10]; menu_pat[i = 0].name = "^Y"; menu_pat[i].label = N_("FirstLine"); *************** *** 615,620 **** --- 626,636 ---- KS_OSDATASET(&menu_pat[i], KS_NONE); if(!repl_mode){ + menu_pat[++i].name = "^X"; + menu_pat[i].label = N_("DelEnd"); + menu_pat[i].key = (CTRL|'X'); + KS_OSDATASET(&menu_pat[i], KS_NONE); + menu_pat[++i].name = "^T"; menu_pat[i].label = N_("LineNumber"); menu_pat[i].key = (CTRL|'T'); *************** *** 631,636 **** --- 647,657 ---- menu_pat[i].key = (CTRL|'O'); KS_OSDATASET(&menu_pat[i], KS_NONE); + menu_pat[++i].name = "^P"; + menu_pat[i].label = N_("Delete Para"); + menu_pat[i].key = (CTRL|'P'); + KS_OSDATASET(&menu_pat[i], KS_NONE); + menu_pat[++i].name = "^U"; /* TRANSLATORS: Instead of justifying (formatting) just a single paragraph, Full Justify justifies the entire *************** *** 766,772 **** UCS *b; UCS tpat[NPAT+20]; UCS *tpatp; ! EXTRAKEYS menu_pat[7]; menu_pat[i = 0].name = "^Y"; menu_pat[i].label = N_("FirstLine"); --- 787,793 ---- UCS *b; UCS tpat[NPAT+20]; UCS *tpatp; ! EXTRAKEYS menu_pat[9]; menu_pat[i = 0].name = "^Y"; menu_pat[i].label = N_("FirstLine"); *************** *** 779,784 **** --- 800,810 ---- KS_OSDATASET(&menu_pat[i], KS_NONE); if(text_mode){ + menu_pat[++i].name = "^X"; + menu_pat[i].label = N_("DelEnd"); + menu_pat[i].key = (CTRL|'X'); + KS_OSDATASET(&menu_pat[i], KS_NONE); + menu_pat[++i].name = "^T"; menu_pat[i].label = N_("LineNumber"); menu_pat[i].key = (CTRL|'T'); *************** *** 794,799 **** --- 820,830 ---- menu_pat[i].key = (CTRL|'O'); KS_OSDATASET(&menu_pat[i], KS_NONE); + menu_pat[++i].name = "^P"; + menu_pat[i].label = N_("Delete Para"); + menu_pat[i].key = (CTRL|'P'); + KS_OSDATASET(&menu_pat[i], KS_NONE); + menu_pat[++i].name = "^U"; menu_pat[i].label = N_("FullJustify"); menu_pat[i].key = (CTRL|'U'); *************** *** 924,930 **** c = lgetc(curline, curoff++).c; /* get the char */ /* test it against first char in pattern */ ! if (eq(c, patrn[0]) != FALSE) { /* if we find it..*/ /* setup match pointers */ matchline = curline; matchoff = curoff; --- 955,961 ---- c = lgetc(curline, curoff++).c; /* get the char */ /* test it against first char in pattern */ ! if (eq(c, (unsigned char)patrn[0]) != FALSE) { /* if we find it..*/ /* setup match pointers */ matchline = curline; matchoff = curoff; *************** *** 945,951 **** return(FALSE); /* and test it against the pattern */ ! if (eq(*patptr, c) == FALSE) goto fail; } --- 976,982 ---- return(FALSE); /* and test it against the pattern */ ! if (eq((unsigned char) *patptr, c) == FALSE) goto fail; } *************** *** 1032,1034 **** --- 1063,1087 ---- curwp->w_flag |= WFEDIT; } + + int + deletepara(int f, int n) /* Delete the current paragraph */ + { + if(curbp->b_mode&MDVIEW) /* don't allow this command if */ + return(rdonly()); /* we are in read only mode */ + + if(!lisblank(curwp->w_dotp)) + gotobop(FALSE, 1); + + curwp->w_markp = curwp->w_dotp; + curwp->w_marko = 0; + + gotoeop(FALSE, 1); + if (curwp->w_dotp != curbp->b_linep){ /* if we are not at the end of buffer */ + curwp->w_dotp = lforw(curwp->w_dotp); /* get one more line */ + curwp->w_doto = 0; /* but only the beginning */ + } + killregion(f,n); + return(TRUE); + } + diff -rc alpine-2.00/pico/word.c alpine-2.00.I.USE/pico/word.c *** alpine-2.00/pico/word.c 2007-08-20 12:46:37.000000000 -0700 --- alpine-2.00.I.USE/pico/word.c 2011-02-07 20:33:42.000000000 -0800 *************** *** 24,33 **** */ #include "headers.h" ! int fpnewline(UCS *quote); ! int fillregion(UCS *qstr, REGION *addedregion); int setquotelevelinregion(int quotelevel, REGION *addedregion); int is_user_separator(UCS c); --- 24,33 ---- */ #include "headers.h" ! #include "../pith/osdep/color.h" int fpnewline(UCS *quote); ! int fillregion(UCS *qstr, UCS *istr, REGION *addedregion); int setquotelevelinregion(int quotelevel, REGION *addedregion); int is_user_separator(UCS c); *************** *** 430,471 **** return 0; } /* * Return number of quotes if whatever starts the line matches the quote string */ int ! quote_match(UCS *q, LINE *l, UCS *buf, size_t buflen) { ! register int i, n, j, qb; ! *buf = '\0'; ! if(*q == '\0') ! return(1); ! ! qb = (ucs4_strlen(q) > 1 && q[ucs4_strlen(q)-1] == ' ') ? 1 : 0; ! for(n = 0, j = 0; ;){ ! for(i = 0; j <= llength(l) && qb ? q[i+1] : q[i]; i++, j++) ! if(q[i] != lgetc(l, j).c) ! return(n); ! ! n++; ! if((!qb && q[i] == '\0') || (qb && q[i+1] == '\0')){ ! if(ucs4_strlen(buf) + ucs4_strlen(q) + 1 < buflen){ ! ucs4_strncat(buf, q, buflen-ucs4_strlen(q)-1); ! buf[buflen-1] = '\0'; ! if(qb && (j > llength(l) || lgetc(l, j).c != ' ')) ! buf[ucs4_strlen(buf)-1] = '\0'; ! } ! } ! if(j > llength(l)) ! return(n); ! else if(qb && lgetc(l, j).c == ' ') ! j++; } ! return(n); /* never reached */ } /* Justify the entire buffer instead of just a paragraph */ int --- 430,600 ---- return 0; } + /* Support of indentation of paragraphs */ + #define is_indent_char(c) (((c) == '.' || (c) == '}' || (c) == RPAREN || \ + (c) == '*' || (c) == '+' || is_a_digit(c) || \ + ISspace(c) || (c) == '-' || \ + (c) == ']') ? 1 : 0) + #define allowed_after_digit(c,word,k) ((((c) == '.' && \ + allowed_after_period(next((word),(k)))) ||\ + (c) == RPAREN || (c) == '}' || (c) == ']' ||\ + ISspace(c) || is_a_digit(c) || \ + ((c) == '-' ) && \ + allowed_after_dash(next((word),(k)))) \ + ? 1 : 0) + #define allowed_after_period(c) (((c) == RPAREN || (c) == '}' || (c) == ']' ||\ + ISspace(c) || (c) == '-' || \ + is_a_digit(c)) ? 1 : 0) + #define allowed_after_parenth(c) (ISspace(c) ? 1 : 0) + #define allowed_after_space(c) (ISspace(c) ? 1 : 0) + #define allowed_after_braces(c) (ISspace(c) ? 1 : 0) + #define allowed_after_star(c) ((ISspace(c) || (c) == RPAREN ||\ + (c) == ']' || (c) == '}') ? 1 : 0) + #define allowed_after_dash(c) ((ISspace(c) || is_a_digit(c)) ? 1 : 0) + #define EOLchar(c) (((c) == '.' || (c) == ':' || (c) == '?' ||\ + (c) == '!') ? 1 : 0) + + int indent_match(char *, LINE *, char *, int, int); + + /* Extended justification support */ + #define is_cquote(c) ((c) == '>' || (c) == '|' || (c) == ']' || (c) == ':') + #define is_cword(c) ((((c) >= 'a') && ((c) <= 'z')) || \ + (((c) >= 'A') && ((c) <= 'Z')) || \ + (((c) >= '0') && ((c) <= '9')) || \ + ((c) == ' ') || ((c) == '?') || \ + ((c) == '@') || ((c) == '.') || \ + ((c) == '!') || ((c) == '\'') || \ + ((c) == ',') || ((c) == '\"') ? 1 : 0) + #define isaquote(c) ((c) == '\"' || (c) == '\'') + #define is8bit(c) ((((int) (c)) & 0x80) ? 1 : 0) + #define iscontrol(c) (iscntrl(((int) (c)) & 0x7f) ? 1 : 0) + #define forbidden(c) (((c) == '\"') || ((c) == '\'') || ((c) == '$') ||\ + ((c) == ',') || ((c) == '.') || ((c) == '-') ||\ + ((c) == LPAREN) || ((c) == '/')|| ((c) == '`') ||\ + ((c) == '{') || ((c) == '\\') || (iscontrol((c))) ||\ + (((c) >= '0') && ((c) <= '9')) || ((c) == '?')) + #define is_cletter(c) ((((c) >= 'a') && ((c) <= 'z'))) ||\ + ((((c) >= 'A') && ((c) <= 'Z'))||\ + is8bit(c)) + #define is_cnumber(c) ((c) >= '0' && (c) <= '9') + #define allwd_after_word(c) (((c) == ' ') || ((c) == '>') || is_cletter(c)) + #define allwd_after_qsword(c) (((c) != '\\') && ((c) != RPAREN)) + #define before(word,i) (((i) > 0) ? (word)[(i) - 1] : 0) + #define next(w,i) ((((w)[(i)]) != 0) ? ((w)[(i) + 1]) : 0) + #define now(w,i) ((w)[(i)]) + #define is_qsword(c) (((c) == ':') || ((c) == RPAREN) ? 1 : 0) + #define is_colon(c) (((c) == ':') ? 1 : 0) + #define is_rarrow(c) (((c) == '>') ? 1 : 0) + #define is_tilde(c) (((c) == '~') ? 1 : 0) + #define is_dash(c) (((c) == '-') ? 1 : 0) + #define is_pound(c) (((c) == '#') ? 1 : 0) + #define is_a_digit(c) ((((c) >= '0') && ((c) <= '9')) ? 1 : 0) + #define is_allowed(c) (is_cquote(c) || is_cword(c) || is_dash(c) || \ + is_pound(c)) + #define qs_allowed(a) (((a)->qstype != qsGdb) && ((a)->qstype != qsProg)) + + /* Internal justification functions */ + QSTRING_S *qs_quote_match(char *, LINE *, char *, int); + int ucs4_strlenis(UCS *); + void linencpy(char *, LINE *, int); + + + char * + default_qstr(void) + { + char *default_qs; + static char ucs_def[NSTRING] = {'\0'}; + + if(ucs_def[0] == '\0'){ + default_qs = ucs4_to_utf8_cpystr(glo_quote_str); + strncpy(ucs_def, (default_qs ? default_qs : ""), NSTRING); + fs_give((void **)&default_qs); + } + + return glo_quote_str ? ucs_def : ""; + } + + void + linencpy(word, l, buflen) + char word[NSTRING]; + LINE *l; + int buflen; + { + int i; + UCS ucs_word[NSTRING]; + char *utf_word; + + word[0] = '\0'; + if(l){ + for (i = 0; i < buflen && i < llength(l) + && (ucs_word[i] = lgetc(l,i).c); i++); + ucs_word[i == buflen ? i-1 : i] = '\0'; + utf_word = ucs4_to_utf8_cpystr(ucs_word); + strncpy(word, utf_word, (NSTRING < buflen ? NSTRING : buflen)); + word[NSTRING-1] = '\0'; + if(utf_word) fs_give((void **)&utf_word); + } + } + + /* + * This function returns the quote string as a structure. In this way we + * have two ways to get the quote string: as a char * or as a QSTRING_S * + * directly. + */ + QSTRING_S * + qs_quote_match(char *q, LINE *l, char *rqstr, int rqstrlen) + { + char GLine[NSTRING], NLine[NSTRING], PLine[NSTRING]; + LINE *nl = l != curbp->b_linep ? lforw(l) : NULL; + LINE *pl = lback(l) != curbp->b_linep ? lback(l) : NULL; + int plb = 1; + + linencpy(GLine, l, NSTRING); + linencpy(NLine, nl, NSTRING); + + if (pl){ + linencpy(PLine, pl, NSTRING); + if(lback(pl) != curbp->b_linep){ + char PPLine[NSTRING]; + + linencpy(PPLine, lback(pl), NSTRING); + plb = line_isblank(q, PLine, GLine, PPLine, NSTRING); + } + } + return do_quote_match(q, GLine, NLine, PLine, rqstr, rqstrlen, plb); + } /* * Return number of quotes if whatever starts the line matches the quote string + * rqstring is pointer to raw qstring, buf points to processed qstring */ int ! quote_match(char *q, LINE *l, char *buf, size_t buflen, int raw) { ! QSTRING_S *qs; ! char rqstr[NSTRING]; ! qs = qs_quote_match(q, l, rqstr, NSTRING); ! flatten_qstring(qs, buf, buflen); ! if (qs) ! free_qs(&qs); ! ! if(raw){ ! strncpy(buf, rqstr, buflen < NSTRING ? buflen : NSTRING); ! buf[buflen-1] = '\0'; } ! ! return buf && buf[0] ? strlen(buf) : 0; } + int ucs4_strlenis(UCS *ucs_qstr) + { + char *str = ucs4_to_utf8_cpystr(ucs_qstr); + int i = (int) strlenis(str); + + if(str) fs_give((void **)&str); + return i; + } /* Justify the entire buffer instead of just a paragraph */ int *************** *** 720,725 **** --- 849,855 ---- } if(action == 'R' && curwp->w_markp){ + char qstrfl[NSTRING]; /* let yank() know that it may be restoring a paragraph */ thisflag |= CFFILL; *************** *** 732,752 **** /* determine if we're justifying quoted text or not */ qstr = (glo_quote_str ! && quote_match(glo_quote_str, ! curwp->w_doto > 0 ? curwp->w_dotp->l_fp : curwp->w_dotp, ! qstr2, NSTRING) ! && *qstr2) ? qstr2 : NULL; ! /* * Fillregion moves dot to the end of the filled region. */ ! if(!fillregion(qstr, &addedregion)) return(FALSE); set_last_region_added(&addedregion); } else if(action == 'P'){ /* * Justfiy the current paragraph. --- 862,886 ---- /* determine if we're justifying quoted text or not */ qstr = (glo_quote_str ! && quote_match(default_qstr(), ! (curwp->w_doto > 0 ? curwp->w_dotp->l_fp : curwp->w_dotp), ! qstrfl, NSTRING, 0) ! && *qstrfl) ? utf8_to_ucs4_cpystr(qstrfl) : NULL; /* * Fillregion moves dot to the end of the filled region. */ ! if(!fillregion(qstr, NULL, &addedregion)) return(FALSE); set_last_region_added(&addedregion); + + if(qstr) + fs_give((void **)&qstr); } else if(action == 'P'){ + char ind_str[NSTRING], qstrfl[NSTRING]; + UCS *istr; /* * Justfiy the current paragraph. *************** *** 758,774 **** if(gotoeop(FALSE, 1) == FALSE) return(FALSE); - /* determine if we're justifying quoted text or not */ - qstr = (glo_quote_str - && quote_match(glo_quote_str, - curwp->w_dotp, qstr2, NSTRING) - && *qstr2) ? qstr2 : NULL; - setmark(0,0); /* mark last line of para */ /* jump back to the beginning of the paragraph */ gotobop(FALSE, 1); /* let yank() know that it may be restoring a paragraph */ thisflag |= (CFFILL | CFFLPA); --- 892,907 ---- if(gotoeop(FALSE, 1) == FALSE) return(FALSE); setmark(0,0); /* mark last line of para */ /* jump back to the beginning of the paragraph */ gotobop(FALSE, 1); + istr = indent_match(default_qstr(), curwp->w_dotp, ind_str, NSTRING, 0) + && *ind_str ? utf8_to_ucs4_cpystr(ind_str) : NULL; + qstr = (quote_match(default_qstr(), curwp->w_dotp, qstrfl, NSTRING, 0) + && *qstrfl) ? utf8_to_ucs4_cpystr(qstrfl) : NULL; + /* let yank() know that it may be restoring a paragraph */ thisflag |= (CFFILL | CFFLPA); *************** *** 782,790 **** /* * Fillregion moves dot to the end of the filled region. */ ! if(!fillregion(qstr, &addedregion)) return(FALSE); set_last_region_added(&addedregion); /* Leave cursor on first char of first line after justified region */ --- 915,929 ---- /* * Fillregion moves dot to the end of the filled region. */ ! if(!fillregion(qstr, istr, &addedregion)) return(FALSE); + if(qstr) + fs_give((void **)&qstr); + + if(istr) + fs_give((void **)&istr); + set_last_region_added(&addedregion); /* Leave cursor on first char of first line after justified region */ *************** *** 826,841 **** * can delete it and restore the saved part. */ int ! fillregion(UCS *qstr, REGION *addedregion) { long c, sz, last_char = 0; ! int i, j, qlen, same_word, spaces, word_len, word_ind, line_len, ww; int starts_midline = 0; int ends_midline = 0; int offset_into_start; LINE *line_before_start, *lp; ! UCS line_last, word[NSTRING]; REGION region; /* if region starts midline insert a newline */ --- 965,980 ---- * can delete it and restore the saved part. */ int ! fillregion(UCS *qstr, UCS *istr, REGION *addedregion) { long c, sz, last_char = 0; ! int i, j, qlen, same_word, qi, pqi, qlenis, spaces, word_len, word_ind, line_len, ww; int starts_midline = 0; int ends_midline = 0; int offset_into_start; LINE *line_before_start, *lp; ! UCS line_last, word[NSTRING], quoid[NSTRING], qstr2[NSTRING]; REGION region; /* if region starts midline insert a newline */ *************** *** 846,851 **** --- 985,1019 ---- if(curwp->w_marko > 0 && curwp->w_marko < llength(curwp->w_markp)) ends_midline++; + for (i = 0; (i < NSTRING) && qstr && (quoid[i] = qstr[i]); i++); + for (j = 0; ((i + j) < NSTRING) && istr && (quoid[i] = istr[j]); i++,j++); + quoid[i] = '\0'; + qi = ucs4_strlen(quoid); + if (istr) /* strip trailing spaces */ + for (;ISspace(quoid[qi - 1]); qi--); + quoid[qi] = '\0'; /* we have closed quoid at "X" in the first line */ + + if (ucs4_strlenis(quoid) > fillcol) + return FALSE; /* Too wide, we can't justify this! */ + + if (qstr && istr){ + for (i = ucs4_strlen(qstr) - 1; ISspace(qstr[i]); i--); + qstr[i + 1] = '\0'; /* qstrfl */ + } + qlen = ucs4_strlen(qstr); /* qstrfl*/ + qlenis = ucs4_strlenis(qstr); + + for(i = 0, qstr2[0] = '\0'; qstr && qstr[i] && (qstr2[i] = qstr[i]); i++); + + if (istr && ((j = ucs4_strlenis(quoid) - ucs4_strlenis(qstr)) > 0)){ + pqi = ucs4_strlen(qstr); + for (i = 0; (i < j) && (qstr2[pqi + i] = ' '); i++); + if (ISspace(istr[ucs4_strlen(istr) - 1])) + qstr2[pqi + i++] = ' '; + qstr2[pqi + i] = '\0'; + qstr = qstr2; + } + /* cut the paragraph into our fill buffer */ fdelete(); if(!getregion(®ion, curwp->w_markp, curwp->w_marko)) *************** *** 862,889 **** /* Now insert it back wrapped */ spaces = word_len = word_ind = line_len = same_word = 0; - qlen = qstr ? ucs4_strlen(qstr) : 0; /* Beginning with leading quoting... */ ! if(qstr){ ! i = 0; ! while(qstr[i]){ ! ww = wcellwidth(qstr[i]); ! line_len += (ww >= 0 ? ww : 1); ! linsert(1, qstr[i++]); ! } line_last = ' '; /* no word-flush space! */ } /* remove first leading quotes if any */ if(starts_midline) i = 0; ! else ! for(i = qlen; (c = fremove(i)) == ' ' || c == TAB; i++){ linsert(1, line_last = (UCS) c); line_len += ((c == TAB) ? (~line_len & 0x07) + 1 : 1); } /* then digest the rest... */ while((c = fremove(i++)) >= 0){ --- 1030,1065 ---- /* Now insert it back wrapped */ spaces = word_len = word_ind = line_len = same_word = 0; /* Beginning with leading quoting... */ ! if(qstr || istr){ ! for(i = 0; quoid[i] != '\0' ; i++) ! linsert(1, quoid[i]); line_last = ' '; /* no word-flush space! */ + line_len = ucs4_strlenis(quoid); /* we demand a recount! */ } /* remove first leading quotes if any */ if(starts_midline) i = 0; ! else{ ! if(qstr || istr){ ! for (i = 0; (c = fremove(i)) != '\0'; i++){ ! word[i] = c; ! word[i+1] = '\0'; ! if(ucs4_strlenis(word) >= ucs4_strlenis(quoid)) ! break; ! } ! i++; ! } ! else ! i = 0; ! for(; ISspace(c = fremove(i)); i++){ linsert(1, line_last = (UCS) c); line_len += ((c == TAB) ? (~line_len & 0x07) + 1 : 1); } + } /* then digest the rest... */ while((c = fremove(i++)) >= 0){ *************** *** 905,925 **** case TAB : case ' ' : spaces++; same_word = 0; break; default : if(spaces){ /* flush word? */ ! if((line_len - qlen > 0) && line_len + word_len + 1 > fillcol ! && ((ucs4_isspace(line_last)) || (linsert(1, ' '))) && (line_len = fpnewline(qstr))) line_last = ' '; /* no word-flush space! */ if(word_len){ /* word to write? */ ! if(line_len && !ucs4_isspace(line_last)){ linsert(1, ' '); /* need padding? */ line_len++; } --- 1081,1102 ---- case TAB : case ' ' : + case NBSP: spaces++; same_word = 0; break; default : if(spaces){ /* flush word? */ ! if((line_len - qlenis > 0) && line_len + word_len + 1 > fillcol ! && ((ISspace(line_last)) || (linsert(1, ' '))) && (line_len = fpnewline(qstr))) line_last = ' '; /* no word-flush space! */ if(word_len){ /* word to write? */ ! if(line_len && !ISspace(line_last)){ linsert(1, ' '); /* need padding? */ line_len++; } *************** *** 941,948 **** if(word_ind + 1 >= NSTRING){ /* Magic! Fake that we output a wrapped word */ ! if((line_len - qlen > 0) && !same_word++){ ! if(!ucs4_isspace(line_last)) linsert(1, ' '); line_len = fpnewline(qstr); } --- 1118,1125 ---- if(word_ind + 1 >= NSTRING){ /* Magic! Fake that we output a wrapped word */ ! if((line_len - qlenis > 0) && !same_word++){ ! if(!ISspace(line_last)) linsert(1, ' '); line_len = fpnewline(qstr); } *************** *** 964,975 **** } if(word_len){ ! if((line_len - qlen > 0) && (line_len + word_len + 1 > fillcol)){ ! if(!ucs4_isspace(line_last)) linsert(1, ' '); (void) fpnewline(qstr); } ! else if(line_len && !ucs4_isspace(line_last)) linsert(1, ' '); for(j = 0; j < word_ind; j++) --- 1141,1152 ---- } if(word_len){ ! if((line_len - qlenis > 0) && (line_len + word_len + 1 > fillcol)){ ! if(!ISspace(line_last)) linsert(1, ' '); (void) fpnewline(qstr); } ! else if(line_len && !ISspace(line_last)) linsert(1, ' '); for(j = 0; j < word_ind; j++) *************** *** 1027,1037 **** int len; lnewline(); ! for(len = 0; quote && *quote; quote++){ int ww; ! ww = wcellwidth(*quote); ! len += (ww >= 0 ? ww : 1); linsert(1, *quote); } --- 1204,1214 ---- int len; lnewline(); ! for(len = ucs4_strlenis(quote); quote && *quote; quote++){ int ww; ! /* ww = wcellwidth(*quote); ! len += (ww >= 0 ? ww : 1);*/ linsert(1, *quote); } *************** *** 1175,1179 **** --- 1352,1396 ---- markregion(1); } + /* + * This puts us at the end of the quoted region instead + * of on the following line. This makes it convenient + * for the user to follow a quotelevel adjustment with + * a Justify if desired. + */ + if(backuptoprevline){ + curwp->w_doto = 0; + backchar(0, 1); + } + + if(ends_midline){ /* doesn't need fixing otherwise */ + unmarkbuffer(); + markregion(1); + } + return (TRUE); } + + /* + * If there is an indent string this function returns + * its length + */ + int + indent_match(char *q, LINE *l, char *buf, int buflen, int raw) + { + char GLine[NSTRING]; + int i, k, plb; + + k = quote_match(q,l, buf, buflen, raw); + linencpy(GLine, l, NSTRING); + plb = (lback(l) != curbp->b_linep) ? lisblank(lback(l)) : 1; + if (!plb){ + i = llength(lback(l)) - 1; + for (; i >= 0 && ISspace(lgetc(lback(l), i).c); i--); + if (EOLchar(lgetc(lback(l), i).c)) + plb++; + } + + return get_indent_raw_line(q, GLine, buf, buflen, k, plb); + } + diff -rc alpine-2.00/pith/adrbklib.c alpine-2.00.I.USE/pith/adrbklib.c *** alpine-2.00/pith/adrbklib.c 2008-06-03 08:54:15.000000000 -0700 --- alpine-2.00.I.USE/pith/adrbklib.c 2011-02-07 20:33:41.000000000 -0800 *************** *** 5119,5126 **** if(as.cur >= as.how_many_personals) pab->type |= GLOBAL; ! pab->access = adrbk_access(pab); ! /* global address books are forced readonly */ if(pab->type & GLOBAL && pab->access != NoAccess) pab->access = ReadOnly; --- 5119,5132 ---- if(as.cur >= as.how_many_personals) pab->type |= GLOBAL; ! if(ps_global->mail_stream && ! ps_global->mail_stream->lock && (pab->type & REMOTE_VIA_IMAP)){ ! as.initialized = 0; ! pab->access = NoAccess; ! } ! else{ ! pab->access = adrbk_access(pab); ! } /* global address books are forced readonly */ if(pab->type & GLOBAL && pab->access != NoAccess) pab->access = ReadOnly; diff -rc alpine-2.00/pith/charconv/utf8.c alpine-2.00.I.USE/pith/charconv/utf8.c *** alpine-2.00/pith/charconv/utf8.c 2008-04-02 15:09:20.000000000 -0700 --- alpine-2.00.I.USE/pith/charconv/utf8.c 2011-02-07 20:33:42.000000000 -0800 *************** *** 1048,1053 **** --- 1048,1103 ---- /* + * Returns the screen cells width of the UTF-8 string argument, treating tabs + * in a special way. + */ + unsigned + utf8_widthis(char *str) + { + unsigned width = 0; + int this_width; + UCS ucs; + unsigned long remaining_octets; + char *readptr; + + if(!(str && *str)) + return(width); + + readptr = str; + remaining_octets = readptr ? strlen(readptr) : 0; + + while(remaining_octets > 0 && *readptr){ + + ucs = (UCS) utf8_get((unsigned char **) &readptr, &remaining_octets); + + if(ucs & U8G_ERROR){ + /* + * This should not happen, but do something to handle it anyway. + * Treat each character as a single width character, which is what should + * probably happen when we actually go to write it out. + */ + remaining_octets--; + readptr++; + this_width = 1; + } + else{ + this_width = (ucs == TAB) ? ((~width & 0x07) + 1) : wcellwidth(ucs); + + /* + * If this_width is -1 that means we can't print this character + * with our current locale. Writechar will print a '?'. + */ + if(this_width < 0) + this_width = 1; + } + + width += (unsigned) this_width; + } + + return(width); + } + + /* * Copy UTF-8 characters from src into dst. * This is intended to be used if you want to truncate a string at * the start instead of the end. For example, you have a long string diff -rc alpine-2.00/pith/charconv/utf8.h alpine-2.00.I.USE/pith/charconv/utf8.h *** alpine-2.00/pith/charconv/utf8.h 2008-04-02 15:09:20.000000000 -0700 --- alpine-2.00.I.USE/pith/charconv/utf8.h 2011-02-07 20:33:42.000000000 -0800 *************** *** 80,85 **** --- 80,86 ---- UCS *ucs4_strchr(UCS *s, UCS c); UCS *ucs4_strrchr(UCS *s, UCS c); unsigned utf8_width(char *); + unsigned utf8_widthis(char *); size_t utf8_to_width_rhs(char *, char *, size_t, unsigned); int utf8_snprintf(char *, size_t, char *, ...); size_t utf8_to_width(char *, char *, size_t, unsigned, unsigned *); diff -rc alpine-2.00/pith/color.c alpine-2.00.I.USE/pith/color.c *** alpine-2.00/pith/color.c 2007-08-15 13:28:09.000000000 -0700 --- alpine-2.00.I.USE/pith/color.c 2011-02-07 20:33:42.000000000 -0800 *************** *** 20,26 **** #include "../pith/state.h" #include "../pith/conf.h" #include "../pith/filter.h" ! char * color_embed(char *fg, char *bg) --- 20,27 ---- #include "../pith/state.h" #include "../pith/conf.h" #include "../pith/filter.h" ! #include "../pith/mailview.h" ! #include "../pico/estruct.h" char * color_embed(char *fg, char *bg) *************** *** 69,91 **** struct quote_colors *next; }; int color_a_quote(long int linenum, char *line, LT_INS_S **ins, void *is_flowed_msg) { ! int countem = 0; struct variable *vars = ps_global->vars; ! char *p; struct quote_colors *colors = NULL, *cp, *next; COLOR_PAIR *col = NULL; int is_flowed = is_flowed_msg ? *((int *)is_flowed_msg) : 0; p = line; ! if(!is_flowed) ! while(isspace((unsigned char)*p)) ! p++; ! if(p[0] == '>'){ struct quote_colors *c; /* --- 70,179 ---- struct quote_colors *next; }; + int + is_word (buf, i, j) + char buf[NSTRING]; + int i, j; + { + return i <= j && is_letter(buf[i]) ? + (i < j ? is_word(buf,i+1,j) : 1) : 0; + } + + int + is_mailbox(buf,i,j) + char buf[NSTRING]; + int i, j; + { + return i <= j && (is_letter(buf[i]) || is_digit(buf[i]) || buf[i] == '.') + ? (i < j ? is_mailbox(buf,i+1,j) : 1) : 0; + } + + int + next_level_quote(buf, line, i, is_flowed) + char *buf; + char **line; + int i; + int is_flowed; + { + int j; + + if (!single_level(buf[i])){ + if(is_mailbox(buf,i,i)){ + for (j = i; buf[j] && !isspace(buf[j]); j++); + if (is_word(buf,i,j-1) || is_mailbox(buf,i,j-1)) + j += isspace(buf[j]) ? 2 : 1; + } + else{ + switch(buf[i]){ + case ':' : + if (next(buf,i) != RPAREN) + j = i + 1; + else + j = i + 2; + break; + + case '-' : + if (next(buf,i) != '-') + j = i + 2; + else + j = i + 3; + break; + + case '+' : + case '*' : + if (next(buf,i) != ' ') + j = i + 2; + else + j = i + 3; + break; + + default : + for (j = i; buf[j] && !isspace(buf[j]) + && (!single_level(buf[i]) && !is_letter(buf[j])); j++); + + j += isspace(buf[j]) ? 1 : 0; + break; + } + } + if (line && *line) + (*line) += j - i; + } + else{ + j = i+1; + if (line && *line) + (*line)++; + } + if(!is_flowed){ + if(line && *line) + for(; isspace((unsigned char)*(*line)); (*line)++); + for (i = j; isspace((unsigned char) buf[i]); i++); + } + else i = j; + if (is_flowed && i != j) + buf[i] = '\0'; + return i; + } int color_a_quote(long int linenum, char *line, LT_INS_S **ins, void *is_flowed_msg) { ! int countem = 0, i, j = 0; struct variable *vars = ps_global->vars; ! char *p, buf[NSTRING] = {'\0'}; struct quote_colors *colors = NULL, *cp, *next; COLOR_PAIR *col = NULL; int is_flowed = is_flowed_msg ? *((int *)is_flowed_msg) : 0; + int code; + + code = (is_flowed ? IS_FLOWED : NO_FLOWED) | COLORAQUO; + select_quote(linenum, line, ins, (void *) &code); + strncpy(buf, tmp_20k_buf, NSTRING < SIZEOF_20KBUF ? NSTRING : SIZEOF_20KBUF); + buf[sizeof(buf)-1] = '\0'; p = line; ! for(i = 0; isspace((unsigned char)buf[i]); i++, p++); ! if(buf[i]){ struct quote_colors *c; /* *************** *** 134,140 **** free_color_pair(&col); cp = NULL; ! while(*p == '>'){ cp = (cp && cp->next) ? cp->next : colors; if(countem > 0) --- 222,228 ---- free_color_pair(&col); cp = NULL; ! while(buf[i]){ cp = (cp && cp->next) ? cp->next : colors; if(countem > 0) *************** *** 144,153 **** countem = (countem == 1) ? 0 : countem; ! p++; ! if(!is_flowed) ! for(; isspace((unsigned char)*p); p++) ! ; } if(colors){ --- 232,240 ---- countem = (countem == 1) ? 0 : countem; ! i = next_level_quote(buf, &p, i, is_flowed); ! for (; isspace((unsigned char)*p); p++); ! for (; isspace((unsigned char)buf[i]); i++); } if(colors){ *************** *** 210,216 **** } } ! return(0); } --- 297,303 ---- } } ! return(1); } diff -rc alpine-2.00/pith/color.h alpine-2.00.I.USE/pith/color.h *** alpine-2.00/pith/color.h 2007-05-08 16:38:08.000000000 -0700 --- alpine-2.00.I.USE/pith/color.h 2011-02-07 20:33:42.000000000 -0800 *************** *** 21,26 **** --- 21,44 ---- #include "../pith/pattern.h" #include "../pith/osdep/color.h" + #define NO_FLOWED 0x0000 + #define IS_FLOWED 0x0001 + #define DELETEQUO 0x0010 + #define COLORAQUO 0x0100 + #define RAWSTRING 0x1000 + + /* This is needed for justification, I will move it to a better place later + * or maybe not + */ + #define is_digit(c) ((((c) >= '0') && ((c) <= '9')) ? 1 : 0) + + #define is_letter(c) (((c) >= 'a' && (c) <= 'z') || \ + ((c) >= 'A' && (c) <= 'Z')) + + #define next(w,i) ((((w)[(i)]) != 0) ? ((w)[(i) + 1]) : 0) + + #define single_level(c) (((c) == '>') || ((c) == '|') || ((c) == '~') || \ + ((c) == ']')) typedef struct spec_color_s { int inherit; /* this isn't a color, it is INHERIT */ *************** *** 80,85 **** --- 98,104 ---- /* exported protoypes */ char *color_embed(char *, char *); int colorcmp(char *, char *); + int next_level_quote(char *, char **, int, int); int color_a_quote(long, char *, LT_INS_S **, void *); void free_spec_colors(SPEC_COLOR_S **); diff -rc alpine-2.00/pith/conf.c alpine-2.00.I.USE/pith/conf.c *** alpine-2.00/pith/conf.c 2008-08-22 17:07:05.000000000 -0700 --- alpine-2.00.I.USE/pith/conf.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 28,33 **** --- 28,34 ---- #include "../pith/remote.h" #include "../pith/keyword.h" #include "../pith/mailview.h" + #include "../pith/rules.h" #include "../pith/list.h" #include "../pith/status.h" #include "../pith/ldap.h" *************** *** 197,208 **** --- 198,213 ---- CONF_TXT_T cf_text_view_hdr_color[] = "When viewing messages, these are the header colors"; + CONF_TXT_T cf_text_index_token_color[] = "Colors in which tokens will be displayed in the index screen"; + CONF_TXT_T cf_text_save_msg_name_rule[] = "Determines default folder name for Saves...\n# Choices: default-folder, by-sender, by-from, by-recipient, last-folder-used.\n# Default: \"default-folder\", i.e. \"saved-messages\" (Unix) or \"SAVEMAIL\" (PC)."; CONF_TXT_T cf_text_fcc_name_rule[] = "Determines default name for Fcc...\n# Choices: default-fcc, by-recipient, last-fcc-used.\n# Default: \"default-fcc\" (see also \"default-fcc=\" variable.)"; CONF_TXT_T cf_text_sort_key[] = "Sets presentation order of messages in Index. Choices:\n# Subject, From, Arrival, Date, Size, To, Cc, OrderedSubj, Score, and Thread.\n# Order may be reversed by appending /Reverse. Default: \"Arrival\"."; + CONF_TXT_T cf_text_thread_sort_key[] = "#Sets presentation order of threads in thread index. Choices:\n#arrival, and thread."; + CONF_TXT_T cf_text_addrbook_sort_rule[] = "Sets presentation order of address book entries. Choices: dont-sort,\n# fullname-with-lists-last, fullname, nickname-with-lists-last, nickname\n# Default: \"fullname-with-lists-last\"."; CONF_TXT_T cf_text_folder_sort_rule[] = "Sets presentation order of folder list entries. Choices: alphabetical,\n# alpha-with-dirs-last, alpha-with-dirs-first.\n# Default: \"alpha-with-directories-last\"."; *************** *** 219,230 **** --- 224,267 ---- CONF_TXT_T cf_text_editor[] = "Specifies the program invoked by ^_ in the Composer,\n# or the \"enable-alternate-editor-implicitly\" feature."; + CONF_TXT_T cf_text_compose_rules[] = "Allows a user to set rules when composing messages."; + + CONF_TXT_T cf_text_forward_rules[] = "Allows a user to set rules when forwarding messages."; + + CONF_TXT_T cf_text_reply_rules[] = "Allows a user to set rules when replying messages."; + + CONF_TXT_T cf_text_index_rules[] = "Allows a user to supersede global index format variable in designated folders."; + + CONF_TXT_T cf_text_key_def_rules[] = "Allows a user to override keystrokes in certain screens."; + + CONF_TXT_T cf_text_replace_rules[] = "Allows a user to change the form a specify field in the index-format is \n# displayed."; + + CONF_TXT_T cf_text_reply_indent_rules[] = "Allows a user to change the form a specify a reply-indent-string\n# based of rules."; + + CONF_TXT_T cf_text_reply_leadin_rules[] = "Allows a user to replace the reply-leadin message based on different parameters."; + + CONF_TXT_T cf_text_reply_subject_rules[] = "Allows a user to replace the subject of a message in a customs based way"; + + CONF_TXT_T cf_text_thread_displaystyle_rule[] = "Allows a user to specify the threading style of specific folders"; + + CONF_TXT_T cf_text_thread_indexstyle_rule[] = "Allows a user to specify the threading index style of specific folders"; + + CONF_TXT_T cf_text_save_rules[] = "Allows a user to specify a save folder message for specific senders or folders."; + + CONF_TXT_T cf_text_smtp_rules[] = "Allows a user to specify a smtp server to be used when sending e-mail,\n# according to the rules specified here."; + + CONF_TXT_T cf_text_sort_rules[] = "Allows a user to specify the sort default order of a specific folder."; + + CONF_TXT_T cf_text_startup_rules[] = "Allows a user to specify the position of a highlighted message when opening a \n# folder."; + CONF_TXT_T cf_text_speller[] = "Specifies the program invoked by ^T in the Composer."; CONF_TXT_T cf_text_deadlets[] = "Specifies the number of dead letter files to keep when canceling."; CONF_TXT_T cf_text_fillcol[] = "Specifies the column of the screen where the composer should wrap."; + CONF_TXT_T cf_special_text_color[] = "Specifies a comma separated list of text and regular expresions that Pine\n# will highlight"; + CONF_TXT_T cf_text_replystr[] = "Specifies the string to insert when replying to a message."; CONF_TXT_T cf_text_quotereplstr[] = "Specifies the string to replace quotes with when viewing a message."; *************** *** 427,432 **** --- 464,472 ---- CONF_TXT_T cf_text_newsrc_path[] = "Full path and name of NEWSRC file"; + #ifndef _WINDOWS + CONF_TXT_T cf_text_maildir_location[] = "Location relative to your HOME directory of the directory where your INBOX\n# for the maildir format is located. Default value is \"Maildir\". If your\n# inbox is located at \"~/Maildir\" you do not need to change this value.\n# A common value is also \".maildir\""; + #endif /*---------------------------------------------------------------------- These are the variables that control a number of pine functions. They *************** *** 517,522 **** --- 557,564 ---- NULL, cf_text_fcc_name_rule}, {"sort-key", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL, cf_text_sort_key}, + {"thread-sort-key", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, + NULL, cf_text_thread_sort_key}, {"addrbook-sort-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, "Address Book Sort Rule", cf_text_addrbook_sort_rule}, {"folder-sort-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, *************** *** 539,544 **** --- 581,614 ---- NULL, cf_text_thread_exp_char}, {"threading-lastreply-character", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, "Threading Last Reply Character", cf_text_thread_lastreply_char}, + {"threading-display-style-rule", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + "Threading Display Style Rule", cf_text_thread_displaystyle_rule}, + {"threading-index-style-rule", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + "Threading Index Style Rule", cf_text_thread_indexstyle_rule}, + {"compose-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + "Compose Rules", cf_text_compose_rules}, + {"forward-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + "Forward Rules", cf_text_forward_rules}, + {"index-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, + "Index Rules", cf_text_index_rules}, + {"key-definition-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, + "Key Definition Rules", cf_text_key_def_rules}, + {"replace-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, + "Replace Rules", cf_text_replace_rules}, + {"reply-indent-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + "Reply Indent Rules", cf_text_reply_indent_rules}, + {"reply-leadin-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, + "Reply Leadin Rules", cf_text_reply_leadin_rules}, + {"reply-subject-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, + "Reply Subject Rules", cf_text_reply_subject_rules}, + {"save-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + "Save Rules", cf_text_save_rules}, + {"smtp-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + "Smtp Rules", cf_text_smtp_rules}, + {"sort-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + "Sort Rules", cf_text_sort_rules}, + {"startup-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + "Startup Rules", cf_text_startup_rules}, #ifndef _WINDOWS {"display-character-set", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL, cf_text_disp_char_set}, *************** *** 557,562 **** --- 627,634 ---- NULL, cf_text_speller}, {"composer-wrap-column", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL, cf_text_fillcol}, + {"special-text-color", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + NULL, cf_special_text_color}, {"reply-indent-string", 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, NULL, cf_text_replystr}, {"reply-leadin", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, *************** *** 627,632 **** --- 699,708 ---- NULL, cf_text_news_active}, {"news-spool-directory", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL, cf_text_news_spooldir}, + #ifndef _WINDOWS + {"maildir-location", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, + "Maildir Location", cf_text_maildir_location}, + #endif {"upload-command", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL, cf_text_upload_cmd}, {"upload-command-prefix", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, *************** *** 788,793 **** --- 864,875 ---- {"title-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, {"title-closed-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, {"title-closed-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, + {"folder-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, + {"folder-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, + {"directory-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, + {"directory-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, + {"folder-list-text-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, + {"folder-list-text-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, {"status-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, {"status-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, {"keylabel-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, *************** *** 808,813 **** --- 890,897 ---- {"incoming-unseen-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, {"signature-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, {"signature-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, + {"special-text-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, + {"special-text-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, {"prompt-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, {"prompt-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, {"header-general-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, *************** *** 840,845 **** --- 924,931 ---- {"index-from-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, {"index-opening-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, {"index-opening-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, + {"index-token-colors", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + NULL, cf_text_index_token_color}, {"viewer-hdr-colors", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, "Viewer Header Colors", cf_text_view_hdr_color}, {"keyword-colors", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, *************** *** 1555,1561 **** register struct variable *vars = ps->vars; int obs_header_in_reply = 0, /* the obs_ variables are to */ obs_old_style_reply = 0, /* support backwards compatibility */ ! obs_save_by_sender, i, def_sort_rev; long rvl; PINERC_S *fixedprc = NULL; FeatureLevel obs_feature_level; --- 1641,1647 ---- register struct variable *vars = ps->vars; int obs_header_in_reply = 0, /* the obs_ variables are to */ obs_old_style_reply = 0, /* support backwards compatibility */ ! obs_save_by_sender, i, def_sort_rev, thread_def_sort_rev; long rvl; PINERC_S *fixedprc = NULL; FeatureLevel obs_feature_level; *************** *** 1580,1585 **** --- 1666,1672 ---- GLO_FEATURE_LEVEL = cpystr("sappling"); GLO_OLD_STYLE_REPLY = cpystr(DF_OLD_STYLE_REPLY); GLO_SORT_KEY = cpystr(DF_SORT_KEY); + GLO_THREAD_SORT_KEY = cpystr(DF_THREAD_SORT_KEY); GLO_SAVED_MSG_NAME_RULE = cpystr(DF_SAVED_MSG_NAME_RULE); GLO_FCC_RULE = cpystr(DF_FCC_RULE); GLO_AB_SORT_RULE = cpystr(DF_AB_SORT_RULE); *************** *** 1642,1647 **** --- 1729,1737 ---- GLO_TITLE_BACK_COLOR = cpystr(DEFAULT_TITLE_BACK_RGB); GLO_TITLECLOSED_FORE_COLOR = cpystr(DEFAULT_TITLECLOSED_FORE_RGB); GLO_TITLECLOSED_BACK_COLOR = cpystr(DEFAULT_TITLECLOSED_BACK_RGB); + GLO_FOLDER_FORE_COLOR = cpystr(DEFAULT_NORM_FORE_RGB); + GLO_DIRECTORY_FORE_COLOR = cpystr(DEFAULT_NORM_FORE_RGB); + GLO_FOLDER_LIST_FORE_COLOR = cpystr(DEFAULT_NORM_FORE_RGB); GLO_METAMSG_FORE_COLOR = cpystr(DEFAULT_METAMSG_FORE_RGB); GLO_METAMSG_BACK_COLOR = cpystr(DEFAULT_METAMSG_BACK_RGB); GLO_QUOTE1_FORE_COLOR = cpystr(DEFAULT_QUOTE1_FORE_RGB); *************** *** 1943,1948 **** --- 2033,2040 ---- set_current_val(&vars[V_FORM_FOLDER], TRUE, TRUE); set_current_val(&vars[V_EDITOR], TRUE, TRUE); set_current_val(&vars[V_SPELLER], TRUE, TRUE); + set_current_val(&vars[V_SPECIAL_TEXT], TRUE, TRUE); + regex_pattern(VAR_SPECIAL_TEXT); set_current_val(&vars[V_IMAGE_VIEWER], TRUE, TRUE); set_current_val(&vars[V_BROWSER], TRUE, TRUE); set_current_val(&vars[V_SMTP_SERVER], TRUE, TRUE); *************** *** 2216,2221 **** --- 2308,2319 ---- mail_parameters(NULL, SET_NEWSSPOOL, (void *)VAR_NEWS_SPOOL_DIR); + #ifndef _WINDOWS + set_current_val(&vars[V_MAILDIR_LOCATION], TRUE, TRUE); + if(VAR_MAILDIR_LOCATION && VAR_MAILDIR_LOCATION[0]) + mail_parameters(NULL, SET_MDINBOXPATH, (void *)VAR_MAILDIR_LOCATION); + #endif + /* guarantee a save default */ set_current_val(&vars[V_DEFAULT_SAVE_FOLDER], TRUE, TRUE); if(!VAR_DEFAULT_SAVE_FOLDER || !VAR_DEFAULT_SAVE_FOLDER[0]) *************** *** 2446,2452 **** set_current_val(&vars[V_ARCHIVED_FOLDERS], TRUE, TRUE); set_current_val(&vars[V_INCOMING_FOLDERS], TRUE, TRUE); set_current_val(&vars[V_SORT_KEY], TRUE, TRUE); ! if(decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev) == -1){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sort type \"%.200s\" is invalid", VAR_SORT_KEY); init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf); ps->def_sort = SortArrival; --- 2544,2550 ---- set_current_val(&vars[V_ARCHIVED_FOLDERS], TRUE, TRUE); set_current_val(&vars[V_INCOMING_FOLDERS], TRUE, TRUE); set_current_val(&vars[V_SORT_KEY], TRUE, TRUE); ! if(decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev,0) == -1){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sort type \"%.200s\" is invalid", VAR_SORT_KEY); init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf); ps->def_sort = SortArrival; *************** *** 2455,2460 **** --- 2553,2569 ---- else ps->def_sort_rev = def_sort_rev; + set_current_val(&vars[V_THREAD_SORT_KEY], TRUE, TRUE); + if(decode_sort(VAR_THREAD_SORT_KEY, &ps->thread_def_sort, + &thread_def_sort_rev, 1) == -1){ + sprintf(tmp_20k_buf, "Sort type \"%s\" is invalid", VAR_THREAD_SORT_KEY); + init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf); + ps->thread_def_sort = SortThread; + ps->thread_def_sort_rev = 0; + } + else + ps->thread_def_sort_rev = thread_def_sort_rev; + cur_rule_value(&vars[V_SAVED_MSG_NAME_RULE], TRUE, TRUE); {NAMEVAL_S *v; int i; for(i = 0; (v = save_msg_rules(i)); i++) *************** *** 2541,2546 **** --- 2650,2656 ---- if(cmds_f) (*cmds_f)(ps, VAR_INIT_CMD_LIST); + (void)create_rule_list(ps_global->vars); #ifdef _WINDOWS mswin_set_quit_confirm (F_OFF(F_QUIT_WO_CONFIRM, ps_global)); #endif /* _WINDOWS */ *************** *** 2744,2749 **** --- 2854,2861 ---- F_ALWAYS_SPELL_CHECK, h_config_always_spell_check, PREF_COMP, 0}, /* Reply Prefs */ + {"alternate-reply-menu", NULL, + F_ALT_REPLY_MENU, h_config_alt_reply_menu, PREF_RPLY, 0}, {"copy-to-address-to-from-if-it-is-us", "Copy To Address to From if it is Us", F_COPY_TO_TO_FROM, h_config_copy_to_to_from, PREF_RPLY, 0}, {"enable-reply-indent-string-editing", NULL, *************** *** 2762,2767 **** --- 2874,2881 ---- F_ENABLE_STRIP_SIGDASHES, h_config_strip_sigdashes, PREF_RPLY, 0}, {"forward-as-attachment", "Forward messages as attachments", F_FORWARD_AS_ATTACHMENT, h_config_forward_as_attachment, PREF_RPLY, 0}, + {"preserve-original-fields", NULL, + F_PRESERVE_ORIGINAL_FIELD, h_config_preserve_field, PREF_RPLY, 0}, /* Sending Prefs */ {"disable-sender", "Do Not Generate Sender Header", *************** *** 2786,2791 **** --- 2900,2907 ---- F_NO_FCC_ATTACH, h_config_no_fcc_attach, PREF_SEND, 0}, {"fcc-on-bounce", "Include Fcc When Bouncing Messages", F_FCC_ON_BOUNCE, h_config_fcc_on_bounce, PREF_SEND, 0}, + {"return-path-uses-domain-name", NULL, + F_USE_DOMAIN_NAME, h_config_use_domain, PREF_SEND, 0}, {"mark-fcc-seen", NULL, F_MARK_FCC_SEEN, h_config_mark_fcc_seen, PREF_SEND, 0}, {"fcc-only-without-confirm", "Send to Fcc Only Without Confirming", *************** *** 2832,2837 **** --- 2948,2957 ---- F_SORT_DEFAULT_SAVE_ALPHA, h_config_sort_save_alpha, PREF_FLDR, 0}, {"vertical-folder-list", "Use Vertical Folder List", F_VERTICAL_FOLDER_LIST, h_config_vertical_list, PREF_FLDR, 0}, + #ifndef _WINDOWS + {"use-courier-folder-list", "Courier Style Folder List", + F_COURIER_FOLDER_LIST, h_config_courier_list, PREF_FLDR, 0}, + #endif /* Addr book */ {"combined-addrbook-display", "Combined Address Book Display", *************** *** 2848,2853 **** --- 2968,2975 ---- /* Index prefs */ {"auto-open-next-unread", NULL, F_AUTO_OPEN_NEXT_UNREAD, h_config_auto_open_unread, PREF_INDX, 0}, + {"enable-circular-tab", NULL, + F_AUTO_CIRCULAR_TAB, h_config_circular_tab, PREF_INDX, 0}, {"continue-tab-without-confirm", "Continue NextNew Without Confirming", F_TAB_NO_CONFIRM, h_config_tab_no_prompt, PREF_INDX, 0}, {"convert-dates-to-localtime", NULL, *************** *** 2876,2881 **** --- 2998,3005 ---- F_COLOR_LINE_IMPORTANT, h_config_color_thrd_import, PREF_INDX, 0}, {"thread-sorts-by-arrival", "Thread Sorts by Arrival", F_THREAD_SORTS_BY_ARRIVAL, h_config_thread_sorts_by_arrival, PREF_INDX, 0}, + {"enhanced-fancy-thread-support", "Enhanced Fancy Thread Support", + F_ENHANCED_THREAD, h_config_enhanced_thread, PREF_INDX, 0}, /* Viewer prefs */ {"enable-msg-view-addresses", "Enable Message View Address Links", *************** *** 2886,2891 **** --- 3010,3017 ---- F_VIEW_SEL_URL, h_config_enable_view_url, PREF_VIEW, 1}, {"enable-msg-view-web-hostnames", "Enable Message View Web Hostname Links", F_VIEW_SEL_URL_HOST, h_config_enable_view_web_host, PREF_VIEW, 1}, + {"enable-msg-view-long-url", "Enable Recognition of Long URLS without Delimiter", + F_VIEW_LONG_URL, h_config_enable_long_url, PREF_VIEW, 0}, {"enable-msg-view-forced-arrows", "Enable Message View Forced Arrows", F_FORCE_ARROWS, h_config_enable_view_arrows, PREF_VIEW, 0}, /* set to TRUE for windows */ *************** *** 2983,2988 **** --- 3109,3116 ---- F_FORCE_LOW_SPEED, h_config_force_low_speed, PREF_OS_LWSD, 0}, {"auto-move-read-msgs", "Auto Move Read Messages", F_AUTO_READ_MSGS, h_config_auto_read_msgs, PREF_MISC, 0}, + {"auto-move-read-msgs-using-rules", "Auto Move Read Messages Using Rules", + F_AUTO_READ_MSGS_RULES, h_config_auto_read_msgs_rules, PREF_MISC, 0}, {"auto-unselect-after-apply", NULL, F_AUTO_UNSELECT, h_config_auto_unselect, PREF_MISC, 0}, {"auto-unzoom-after-apply", NULL, *************** *** 3044,3049 **** --- 3172,3179 ---- F_FULL_AUTO_EXPUNGE, h_config_full_auto_expunge, PREF_MISC, 0}, {"force-arrow-cursor", NULL, F_FORCE_ARROW, h_config_force_arrow, PREF_MISC, 0}, + {"ignore-size-changes", NULL, + F_IGNORE_SIZE, h_config_ignore_size, PREF_MISC, 0}, {"maildrops-preserve-state", NULL, F_MAILDROPS_PRESERVE_STATE, h_config_maildrops_preserve_state, PREF_MISC, 0}, *************** *** 3179,3184 **** --- 3309,3316 ---- F_DISABLE_SHARED_NAMESPACES, h_config_disable_shared, PREF_HIDDEN, 0}, {"disable-signature-edit-cmd", NULL, F_DISABLE_SIGEDIT_CMD, h_config_disable_signature_edit, PREF_HIDDEN, 0}, + {"new-thread-on-blank-subject", "New Thread on Blank Subject", + F_NEW_THREAD_ON_BLANK_SUBJECT, h_config_new_thread_blank_subject, PREF_HIDDEN, 1}, {"quell-personal-name-prompt", NULL, F_QUELL_PERSONAL_NAME_PROMPT, h_config_quell_personal_name_prompt, PREF_HIDDEN, 0}, {"quell-user-id-prompt", "Quell User ID Prompt", *************** *** 4734,4750 **** but also after each : in the path. ----*/ char * expand_variables(char *lineout, size_t lineoutlen, char *linein, int colon_path) { char *src = linein, *dest = lineout, *p; char *limit = lineout + lineoutlen; ! int envexpand = 0; if(!linein) return(NULL); while(*src ){ /* something in input string */ if(*src == '$' && *(src+1) == '$'){ /* --- 4866,4887 ---- but also after each : in the path. ----*/ + #define is_allowed_envchar(C, S) ((S) == 0 ? !isspace((C)) \ + : (((C) >= 'a' && (C) <= 'z') \ + || ((C) >= 'A' && (C) <= 'Z') \ + || ((C) >= '0' && (C) <= '9'))) char * expand_variables(char *lineout, size_t lineoutlen, char *linein, int colon_path) { char *src = linein, *dest = lineout, *p; char *limit = lineout + lineoutlen; ! int envexpand = 0, sp; if(!linein) return(NULL); + sp = strncmp(src,"LIT:pattern=\"/NICK=", strlen("LIT:pattern=\"/NICK=")) == 0; while(*src ){ /* something in input string */ if(*src == '$' && *(src+1) == '$'){ /* *************** *** 4825,4831 **** src = rbrace + 1; } else{ ! while(*src && !isspace((unsigned char) *src) && (p-word < sizeof(word)-1)) *p++ = *src++; } --- 4962,4968 ---- src = rbrace + 1; } else{ ! while(*src && is_allowed_envchar((unsigned char) *src, sp) && (p-word < sizeof(word)-1)) *p++ = *src++; } *************** *** 6356,6361 **** --- 6493,6501 ---- set_color_val(&vars[V_TITLE_FORE_COLOR], 1); set_color_val(&vars[V_TITLECLOSED_FORE_COLOR], 0); + set_color_val(&vars[V_FOLDER_FORE_COLOR], 0); + set_color_val(&vars[V_DIRECTORY_FORE_COLOR], 0); + set_color_val(&vars[V_FOLDER_LIST_FORE_COLOR], 0); set_color_val(&vars[V_STATUS_FORE_COLOR], 1); set_color_val(&vars[V_KEYLABEL_FORE_COLOR], 1); set_color_val(&vars[V_KEYNAME_FORE_COLOR], 1); *************** *** 6379,6385 **** --- 6519,6527 ---- set_color_val(&vars[V_IND_OP_FORE_COLOR], 0); set_color_val(&vars[V_INCUNSEEN_FORE_COLOR], 0); set_color_val(&vars[V_SIGNATURE_FORE_COLOR], 0); + set_color_val(&vars[V_SPECIAL_TEXT_FORE_COLOR], 0); + set_current_val(&ps->vars[V_INDEX_TOKEN_COLORS], TRUE, TRUE); set_current_val(&ps->vars[V_VIEW_HDR_COLORS], TRUE, TRUE); set_current_val(&ps->vars[V_KW_COLORS], TRUE, TRUE); set_custom_spec_colors(ps); *************** *** 6532,6537 **** --- 6674,6684 ---- void set_custom_spec_colors(struct pine *ps) { + if(ps->index_token_colors) + free_spec_colors(&ps->index_token_colors); + + ps->index_token_colors = spec_colors_from_varlist(ps->VAR_INDEX_TOKEN_COLORS, 1); + if(ps->hdr_colors) free_spec_colors(&ps->hdr_colors); *************** *** 6895,6900 **** --- 7042,7053 ---- break; + #ifndef _WINDOWS + case F_COURIER_FOLDER_LIST: + mail_parameters(NULL,SET_COURIERSTYLE,(void *)(F_ON(f->id ,ps)? 1 : 0)); + break; /* COURIER == 1, CCLIENT == 0, see maildir.h */ + #endif + case F_COLOR_LINE_IMPORTANT : case F_DATES_TO_LOCAL : clear_index_cache(ps->mail_stream, 0); *************** *** 7500,7509 **** --- 7653,7692 ---- return(h_config_fcc_rule); case V_SORT_KEY : return(h_config_sort_key); + case V_THREAD_SORT_KEY : + return(h_config_thread_sort_key); case V_AB_SORT_RULE : return(h_config_ab_sort_rule); case V_FLD_SORT_RULE : return(h_config_fld_sort_rule); + case V_THREAD_DISP_STYLE_RULES: + return(h_config_thread_display_style_rule); + case V_THREAD_INDEX_STYLE_RULES: + return(h_config_thread_index_style_rule); + case V_COMPOSE_RULES: + return(h_config_compose_rules); + case V_FORWARD_RULES: + return(h_config_forward_rules); + case V_INDEX_RULES: + return(h_config_index_rules); + case V_KEY_RULES: + return(h_config_key_macro_rules); + case V_REPLACE_RULES: + return(h_config_replace_rules); + case V_REPLY_INDENT_RULES: + return(h_config_reply_indent_rules); + case V_REPLY_LEADIN_RULES: + return(h_config_reply_leadin_rules); + case V_RESUB_RULES: + return(h_config_resub_rules); + case V_SAVE_RULES: + return(h_config_save_rules); + case V_SMTP_RULES: + return(h_config_smtp_rules); + case V_SORT_RULES: + return(h_config_sort_rules); + case V_STARTUP_RULES: + return(h_config_startup_rules); case V_POST_CHAR_SET : return(h_config_post_char_set); case V_UNK_CHAR_SET : *************** *** 7554,7559 **** --- 7737,7744 ---- return(h_config_scroll_margin); case V_DEADLETS : return(h_config_deadlets); + case V_SPECIAL_TEXT : + return(h_config_special_text_to_color); case V_FILLCOL : return(h_config_composer_wrap_column); case V_TCPOPENTIMEO : *************** *** 7676,7681 **** --- 7861,7870 ---- return(h_config_newmailwidth); case V_NEWSRC_PATH : return(h_config_newsrc_path); + #ifndef _WINDOWS + case V_MAILDIR_LOCATION : + return(h_config_maildir_location); + #endif case V_BROWSER : return(h_config_browser); #if defined(DOS) || defined(OS2) *************** *** 7694,7699 **** --- 7883,7894 ---- case V_TITLECLOSED_FORE_COLOR : case V_TITLECLOSED_BACK_COLOR : return(h_config_titleclosed_color); + case V_FOLDER_FORE_COLOR: + return(h_config_folder_color); + case V_DIRECTORY_FORE_COLOR: + return(h_config_directory_color); + case V_FOLDER_LIST_FORE_COLOR: + return(h_config_folder_list_color); case V_STATUS_FORE_COLOR : case V_STATUS_BACK_COLOR : return(h_config_status_color); *************** *** 7713,7718 **** --- 7908,7916 ---- case V_SIGNATURE_FORE_COLOR : case V_SIGNATURE_BACK_COLOR : return(h_config_signature_color); + case V_SPECIAL_TEXT_FORE_COLOR : + case V_SPECIAL_TEXT_BACK_COLOR : + return(h_config_special_text_color); case V_PROMPT_FORE_COLOR : case V_PROMPT_BACK_COLOR : return(h_config_prompt_color); *************** *** 7764,7769 **** --- 7962,7969 ---- return(h_config_metamsg_color); case V_VIEW_HDR_COLORS : return(h_config_customhdr_color); + case V_INDEX_TOKEN_COLORS : + return(h_config_indextoken_color); case V_PRINTER : return(h_config_printer); case V_PERSONAL_PRINT_CATEGORY : *************** *** 8194,8196 **** --- 8394,8397 ---- } #endif /* _WINDOWS */ + diff -rc alpine-2.00/pith/conf.h alpine-2.00.I.USE/pith/conf.h *** alpine-2.00/pith/conf.h 2008-08-19 17:27:11.000000000 -0700 --- alpine-2.00.I.USE/pith/conf.h 2011-02-07 20:33:45.000000000 -0800 *************** *** 143,152 **** --- 143,195 ---- #define VAR_SORT_KEY vars[V_SORT_KEY].current_val.p #define GLO_SORT_KEY vars[V_SORT_KEY].global_val.p #define COM_SORT_KEY vars[V_SORT_KEY].cmdline_val.p + #define VAR_THREAD_SORT_KEY vars[V_THREAD_SORT_KEY].current_val.p + #define GLO_THREAD_SORT_KEY vars[V_THREAD_SORT_KEY].global_val.p + #define COM_THREAD_SORT_KEY vars[V_THREAD_SORT_KEY].cmdline_val.p #define VAR_AB_SORT_RULE vars[V_AB_SORT_RULE].current_val.p #define GLO_AB_SORT_RULE vars[V_AB_SORT_RULE].global_val.p #define VAR_FLD_SORT_RULE vars[V_FLD_SORT_RULE].current_val.p #define GLO_FLD_SORT_RULE vars[V_FLD_SORT_RULE].global_val.p + #define VAR_COMPOSE_RULES vars[V_COMPOSE_RULES].current_val.l + #define GLO_COMPOSE_RULES vars[V_COMPOSE_RULES].global_val.l + #define USR_COMPOSE_RULES vars[V_COMPOSE_RULES].user_val.l + #define VAR_FORWARD_RULES vars[V_FORWARD_RULES].current_val.l + #define GLO_FORWARD_RULES vars[V_FORWARD_RULES].global_val.l + #define USR_FORWARD_RULES vars[V_FORWARD_RULES].user_val.l + #define VAR_INDEX_RULES vars[V_INDEX_RULES].current_val.l + #define GLO_INDEX_RULES vars[V_INDEX_RULES].global_val.l + #define USR_INDEX_RULES vars[V_INDEX_RULES].user_val.l + #define VAR_KEY_RULES vars[V_KEY_RULES].current_val.l + #define GLO_KEY_RULES vars[V_KEY_RULES].global_val.l + #define USR_KEY_RULES vars[V_KEY_RULES].user_val.l + #define VAR_REPLACE_RULES vars[V_REPLACE_RULES].current_val.l + #define GLO_REPLACE_RULES vars[V_REPLACE_RULES].global_val.l + #define USR_REPLACE_RULES vars[V_REPLACE_RULES].user_val.l + #define VAR_REPLY_INDENT_RULES vars[V_REPLY_INDENT_RULES].current_val.l + #define GLO_REPLY_INDENT_RULES vars[V_REPLY_INDENT_RULES].global_val.l + #define USR_REPLY_INDENT_RULES vars[V_REPLY_INDENT_RULES].user_val.l + #define VAR_REPLY_LEADIN_RULES vars[V_REPLY_LEADIN_RULES].current_val.l + #define GLO_REPLY_LEADIN_RULES vars[V_REPLY_LEADIN_RULES].global_val.l + #define USR_REPLY_LEADIN_RULES vars[V_REPLY_LEADIN_RULES].user_val.l + #define VAR_RESUB_RULES vars[V_RESUB_RULES].current_val.l + #define GLO_RESUB_RULES vars[V_RESUB_RULES].global_val.l + #define USR_RESUB_RULES vars[V_RESUB_RULES].user_val.l + #define VAR_THREAD_DISP_STYLE_RULES vars[V_THREAD_DISP_STYLE_RULES].current_val.l + #define GLO_THREAD_DISP_STYLE_RULES vars[V_THREAD_DISP_STYLE_RULES].global_val.l + #define VAR_THREAD_INDEX_STYLE_RULES vars[V_THREAD_INDEX_STYLE_RULES].current_val.l + #define GLO_THREAD_INDEX_STYLE_RULES vars[V_THREAD_INDEX_STYLE_RULES].global_val.l + #define VAR_SAVE_RULES vars[V_SAVE_RULES].current_val.l + #define GLO_SAVE_RULES vars[V_SAVE_RULES].global_val.l + #define USR_SAVE_RULES vars[V_SAVE_RULES].user_val.l + #define VAR_SMTP_RULES vars[V_SMTP_RULES].current_val.l + #define GLO_SMTP_RULES vars[V_SMTP_RULES].global_val.l + #define USR_SMTP_RULES vars[V_SMTP_RULES].user_val.l + #define VAR_SORT_RULES vars[V_SORT_RULES].current_val.l + #define GLO_SORT_RULES vars[V_SORT_RULES].global_val.l + #define USR_SORT_RULES vars[V_SORT_RULES].user_val.l + #define VAR_STARTUP_RULES vars[V_STARTUP_RULES].current_val.l + #define GLO_STARTUP_RULES vars[V_STARTUP_RULES].global_val.l + #define USR_STARTUP_RULES vars[V_STARTUP_RULES].user_val.l #ifndef _WINDOWS #define VAR_CHAR_SET vars[V_CHAR_SET].current_val.p #define GLO_CHAR_SET vars[V_CHAR_SET].global_val.p *************** *** 160,165 **** --- 203,210 ---- #define GLO_EDITOR vars[V_EDITOR].global_val.l #define VAR_SPELLER vars[V_SPELLER].current_val.p #define GLO_SPELLER vars[V_SPELLER].global_val.p + #define VAR_SPECIAL_TEXT vars[V_SPECIAL_TEXT].current_val.l + #define GLO_SPECIAL_TEXT vars[V_SPECIAL_TEXT].global_val.l #define VAR_FILLCOL vars[V_FILLCOL].current_val.p #define GLO_FILLCOL vars[V_FILLCOL].global_val.p #define VAR_DEADLETS vars[V_DEADLETS].current_val.p *************** *** 249,254 **** --- 294,303 ---- #define GLO_NEWS_ACTIVE_PATH vars[V_NEWS_ACTIVE_PATH].global_val.p #define VAR_NEWS_SPOOL_DIR vars[V_NEWS_SPOOL_DIR].current_val.p #define GLO_NEWS_SPOOL_DIR vars[V_NEWS_SPOOL_DIR].global_val.p + #ifndef _WINDOWS + #define VAR_MAILDIR_LOCATION vars[V_MAILDIR_LOCATION].current_val.p + #define GLO_MAILDIR_LOCATION vars[V_MAILDIR_LOCATION].global_val.p + #endif #define VAR_DISABLE_DRIVERS vars[V_DISABLE_DRIVERS].current_val.l #define VAR_DISABLE_AUTHS vars[V_DISABLE_AUTHS].current_val.l #define VAR_REMOTE_ABOOK_METADATA vars[V_REMOTE_ABOOK_METADATA].current_val.p *************** *** 373,378 **** --- 422,439 ---- #define GLO_TITLECLOSED_FORE_COLOR vars[V_TITLECLOSED_FORE_COLOR].global_val.p #define VAR_TITLECLOSED_BACK_COLOR vars[V_TITLECLOSED_BACK_COLOR].current_val.p #define GLO_TITLECLOSED_BACK_COLOR vars[V_TITLECLOSED_BACK_COLOR].global_val.p + #define VAR_FOLDER_FORE_COLOR vars[V_FOLDER_FORE_COLOR].current_val.p + #define GLO_FOLDER_FORE_COLOR vars[V_FOLDER_FORE_COLOR].global_val.p + #define VAR_FOLDER_BACK_COLOR vars[V_FOLDER_BACK_COLOR].current_val.p + #define GLO_FOLDER_BACK_COLOR vars[V_FOLDER_BACK_COLOR].global_val.p + #define VAR_DIRECTORY_FORE_COLOR vars[V_DIRECTORY_FORE_COLOR].current_val.p + #define GLO_DIRECTORY_FORE_COLOR vars[V_DIRECTORY_FORE_COLOR].global_val.p + #define VAR_DIRECTORY_BACK_COLOR vars[V_DIRECTORY_BACK_COLOR].current_val.p + #define GLO_DIRECTORY_BACK_COLOR vars[V_DIRECTORY_BACK_COLOR].global_val.p + #define VAR_FOLDER_LIST_FORE_COLOR vars[V_FOLDER_LIST_FORE_COLOR].current_val.p + #define GLO_FOLDER_LIST_FORE_COLOR vars[V_FOLDER_LIST_FORE_COLOR].global_val.p + #define VAR_FOLDER_LIST_BACK_COLOR vars[V_FOLDER_LIST_BACK_COLOR].current_val.p + #define GLO_FOLDER_LIST_BACK_COLOR vars[V_FOLDER_LIST_BACK_COLOR].global_val.p #define VAR_STATUS_FORE_COLOR vars[V_STATUS_FORE_COLOR].current_val.p #define VAR_STATUS_BACK_COLOR vars[V_STATUS_BACK_COLOR].current_val.p #define VAR_HEADER_GENERAL_FORE_COLOR vars[V_HEADER_GENERAL_FORE_COLOR].current_val.p *************** *** 443,451 **** --- 504,515 ---- #define GLO_SIGNATURE_FORE_COLOR vars[V_SIGNATURE_FORE_COLOR].global_val.p #define VAR_SIGNATURE_BACK_COLOR vars[V_SIGNATURE_BACK_COLOR].current_val.p #define GLO_SIGNATURE_BACK_COLOR vars[V_SIGNATURE_BACK_COLOR].global_val.p + #define VAR_SPECIAL_TEXT_FORE_COLOR vars[V_SPECIAL_TEXT_FORE_COLOR].current_val.p + #define VAR_SPECIAL_TEXT_BACK_COLOR vars[V_SPECIAL_TEXT_BACK_COLOR].current_val.p #define VAR_PROMPT_FORE_COLOR vars[V_PROMPT_FORE_COLOR].current_val.p #define VAR_PROMPT_BACK_COLOR vars[V_PROMPT_BACK_COLOR].current_val.p #define VAR_VIEW_HDR_COLORS vars[V_VIEW_HDR_COLORS].current_val.l + #define VAR_INDEX_TOKEN_COLORS vars[V_INDEX_TOKEN_COLORS].current_val.l #ifdef SMIME #define VAR_PUBLICCERT_DIR vars[V_PUBLICCERT_DIR].current_val.p #define GLO_PUBLICCERT_DIR vars[V_PUBLICCERT_DIR].global_val.p diff -rc alpine-2.00/pith/conftype.h alpine-2.00.I.USE/pith/conftype.h *** alpine-2.00/pith/conftype.h 2008-08-19 17:27:11.000000000 -0700 --- alpine-2.00.I.USE/pith/conftype.h 2011-02-07 20:33:46.000000000 -0800 *************** *** 58,63 **** --- 58,64 ---- , V_SAVED_MSG_NAME_RULE , V_FCC_RULE , V_SORT_KEY + , V_THREAD_SORT_KEY , V_AB_SORT_RULE , V_FLD_SORT_RULE , V_GOTO_DEFAULT_RULE *************** *** 69,74 **** --- 70,89 ---- , V_THREAD_MORE_CHAR , V_THREAD_EXP_CHAR , V_THREAD_LASTREPLY_CHAR + , V_THREAD_DISP_STYLE_RULES + , V_THREAD_INDEX_STYLE_RULES + , V_COMPOSE_RULES + , V_FORWARD_RULES + , V_INDEX_RULES + , V_KEY_RULES + , V_REPLACE_RULES + , V_REPLY_INDENT_RULES + , V_REPLY_LEADIN_RULES + , V_RESUB_RULES + , V_SAVE_RULES + , V_SMTP_RULES + , V_SORT_RULES + , V_STARTUP_RULES #ifndef _WINDOWS , V_CHAR_SET , V_OLD_CHAR_SET *************** *** 79,84 **** --- 94,100 ---- , V_EDITOR , V_SPELLER , V_FILLCOL + , V_SPECIAL_TEXT , V_REPLY_STRING , V_REPLY_INTRO , V_QUOTE_REPLACE_STRING *************** *** 114,119 **** --- 130,138 ---- , V_NEWSRC_PATH , V_NEWS_ACTIVE_PATH , V_NEWS_SPOOL_DIR + #ifndef _WINDOWS + , V_MAILDIR_LOCATION + #endif , V_UPLOAD_CMD , V_UPLOAD_CMD_PREFIX , V_DOWNLOAD_CMD *************** *** 203,208 **** --- 222,233 ---- , V_TITLE_BACK_COLOR , V_TITLECLOSED_FORE_COLOR , V_TITLECLOSED_BACK_COLOR + , V_FOLDER_FORE_COLOR + , V_FOLDER_BACK_COLOR + , V_DIRECTORY_FORE_COLOR + , V_DIRECTORY_BACK_COLOR + , V_FOLDER_LIST_FORE_COLOR + , V_FOLDER_LIST_BACK_COLOR , V_STATUS_FORE_COLOR , V_STATUS_BACK_COLOR , V_KEYLABEL_FORE_COLOR *************** *** 223,228 **** --- 248,255 ---- , V_INCUNSEEN_BACK_COLOR , V_SIGNATURE_FORE_COLOR , V_SIGNATURE_BACK_COLOR + , V_SPECIAL_TEXT_FORE_COLOR + , V_SPECIAL_TEXT_BACK_COLOR , V_PROMPT_FORE_COLOR , V_PROMPT_BACK_COLOR , V_HEADER_GENERAL_FORE_COLOR *************** *** 255,260 **** --- 282,288 ---- , V_IND_FROM_BACK_COLOR , V_IND_OP_FORE_COLOR , V_IND_OP_BACK_COLOR + , V_INDEX_TOKEN_COLORS , V_VIEW_HDR_COLORS , V_KW_COLORS #if defined(DOS) || defined(OS2) *************** *** 319,324 **** --- 347,353 ---- F_FULL_AUTO_EXPUNGE, F_EXPUNGE_MANUALLY, F_AUTO_READ_MSGS, + F_AUTO_READ_MSGS_RULES, F_AUTO_FCC_ONLY, F_READ_IN_NEWSRC_ORDER, F_SELECT_WO_CONFIRM, *************** *** 334,342 **** --- 363,373 ---- F_FORCE_ARROW, F_PRUNE_USES_ISO, F_ALT_ED_NOW, + F_IGNORE_SIZE, F_SHOW_DELAY_CUE, F_CANCEL_CONFIRM, F_AUTO_OPEN_NEXT_UNREAD, + F_AUTO_CIRCULAR_TAB, F_DISABLE_INDEX_LOCALE_DATES, F_SELECTED_SHOWN_BOLD, F_QUOTE_ALL_FROMS, *************** *** 380,389 **** --- 411,424 ---- F_PASS_C1_CONTROL_CHARS, F_SINGLE_FOLDER_LIST, F_VERTICAL_FOLDER_LIST, + #ifndef _WINDOWS + F_COURIER_FOLDER_LIST, + #endif F_TAB_CHK_RECENT, F_AUTO_REPLY_TO, F_VERBOSE_POST, F_FCC_ON_BOUNCE, + F_USE_DOMAIN_NAME, F_SEND_WO_CONFIRM, F_USE_SENDER_NOT_X, F_BLANK_KEYMENU, *************** *** 428,439 **** --- 463,476 ---- F_TCAP_WINS, F_ENABLE_SIGDASHES, F_ENABLE_STRIP_SIGDASHES, + F_NEW_THREAD_ON_BLANK_SUBJECT, F_QUELL_PARTIAL_FETCH, F_QUELL_PERSONAL_NAME_PROMPT, F_QUELL_USER_ID_PROMPT, F_VIEW_SEL_ATTACH, F_VIEW_SEL_URL, F_VIEW_SEL_URL_HOST, + F_VIEW_LONG_URL, F_SCAN_ADDR, F_FORCE_ARROWS, F_PREFER_PLAIN_TEXT, *************** *** 492,502 **** --- 529,541 ---- F_MAILDROPS_PRESERVE_STATE, F_EXPOSE_HIDDEN_CONFIG, F_ALT_COMPOSE_MENU, + F_ALT_REPLY_MENU, F_ALT_ROLE_MENU, F_ALWAYS_SPELL_CHECK, F_QUELL_TIMEZONE, F_QUELL_USERAGENT, F_COLOR_LINE_IMPORTANT, + F_ENHANCED_THREAD, F_SLASH_COLL_ENTIRE, F_ENABLE_FULL_HDR_AND_TEXT, F_QUELL_FULL_HDR_RESET, *************** *** 514,519 **** --- 553,559 ---- F_RENDER_HTML_INTERNALLY, F_ENABLE_JUMP_CMD, F_FORWARD_AS_ATTACHMENT, + F_PRESERVE_ORIGINAL_FIELD, #ifndef _WINDOWS F_USE_SYSTEM_TRANS, #endif /* ! _WINDOWS */ *************** *** 703,707 **** --- 743,748 ---- /* exported protoypes */ + #define DF_THREAD_SORT_KEY "thread" #endif /* PITH_CONFTYPE_INCLUDED */ diff -rc alpine-2.00/pith/detoken.c alpine-2.00.I.USE/pith/detoken.c *** alpine-2.00/pith/detoken.c 2007-08-14 12:02:07.000000000 -0700 --- alpine-2.00.I.USE/pith/detoken.c 2011-02-07 20:33:42.000000000 -0800 *************** *** 24,30 **** #include "../pith/reply.h" #include "../pith/mailindx.h" #include "../pith/options.h" ! /* * Hook to read signature from local file --- 24,30 ---- #include "../pith/reply.h" #include "../pith/mailindx.h" #include "../pith/options.h" ! #include "../pith/rules.h" /* * Hook to read signature from local file *************** *** 90,95 **** --- 90,97 ---- if(is_sig){ /* + * First we check if there is a rule about signatures, if there is + * use it, otherwise keep going and do the following: * If role->litsig is set, we use it; * Else, if VAR_LITERAL_SIG is set, we use that; * Else, if role->sig is set, we use that; *************** *** 103,116 **** * there is no reason to mix them, so we don't provide support to * do so. */ ! if(role && role->litsig) ! literal_sig = role->litsig; ! else if(ps_global->VAR_LITERAL_SIG) ! literal_sig = ps_global->VAR_LITERAL_SIG; ! else if(role && role->sig) ! sigfile = role->sig; ! else ! sigfile = ps_global->VAR_SIGNATURE_FILE; } else if(role && role->template) sigfile = role->template; --- 105,129 ---- * there is no reason to mix them, so we don't provide support to * do so. */ ! { RULE_RESULT *rule; ! rule = get_result_rule(V_COMPOSE_RULES, FOR_COMPOSE, env); ! if (rule){ ! sigfile = cpystr(rule->result); ! if (rule->result) ! fs_give((void **)&rule->result); ! fs_give((void **)&rule); ! } ! } ! if (!sigfile){ ! if(role && role->litsig) ! literal_sig = role->litsig; ! else if(ps_global->VAR_LITERAL_SIG) ! literal_sig = ps_global->VAR_LITERAL_SIG; ! else if(role && role->sig) ! sigfile = role->sig; ! else ! sigfile = ps_global->VAR_SIGNATURE_FILE; ! } } else if(role && role->template) sigfile = role->template; *************** *** 301,307 **** } } } ! else if(pt->what_for & FOR_REPLY_INTRO) repl = get_reply_data(env, role, pt->ctype, subbuf, sizeof(subbuf)-1); --- 314,320 ---- } } } ! else if(pt->what_for & (FOR_REPLY_INTRO | FOR_RULE)) repl = get_reply_data(env, role, pt->ctype, subbuf, sizeof(subbuf)-1); diff -rc alpine-2.00/pith/filter.c alpine-2.00.I.USE/pith/filter.c *** alpine-2.00/pith/filter.c 2008-08-21 16:50:47.000000000 -0700 --- alpine-2.00.I.USE/pith/filter.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 45,50 **** --- 45,51 ---- #include "../pith/conf.h" #include "../pith/store.h" #include "../pith/color.h" + #include "../pith/osdep/color.h" #include "../pith/escapes.h" #include "../pith/pipe.h" #include "../pith/status.h" *************** *** 1020,1026 **** */ char * gf_filter(char *cmd, char *prepend, STORE_S *source_so, gf_io_t pc, ! FILTLIST_S *aux_filters, int disable_reset, void (*pipecb_f)(PIPE_S *, int, void *)) { unsigned char c, obuf[MAX(MB_LEN_MAX,32)]; --- 1021,1027 ---- */ char * gf_filter(char *cmd, char *prepend, STORE_S *source_so, gf_io_t pc, ! FILTLIST_S *aux_filters, int silent, int disable_reset, void (*pipecb_f)(PIPE_S *, int, void *)) { unsigned char c, obuf[MAX(MB_LEN_MAX,32)]; *************** *** 1057,1064 **** * Spawn filter feeding it data, and reading what it writes. */ so_seek(source_so, 0L, 0); ! flags = PIPE_WRITE | PIPE_READ | PIPE_NOSHELL | ! (!disable_reset ? PIPE_RESET : 0); if((fpipe = open_system_pipe(cmd, NULL, NULL, flags, 0, pipecb_f, pipe_report_error)) != NULL){ --- 1058,1066 ---- * Spawn filter feeding it data, and reading what it writes. */ so_seek(source_so, 0L, 0); ! flags = PIPE_WRITE | PIPE_READ | PIPE_NOSHELL ! | (silent ? PIPE_SILENT : 0) ! | (!disable_reset ? PIPE_RESET : 0); if((fpipe = open_system_pipe(cmd, NULL, NULL, flags, 0, pipecb_f, pipe_report_error)) != NULL){ *************** *** 1373,1386 **** register unsigned char t = f->t; register int n = (int) f->n; register int state = f->f1; while(GF_GETC(f, c)){ if(state){ state = 0; if (c != '=') { ! gf_error("Illegal '=' in base64 text"); ! /* NO RETURN */ } } --- 1375,1398 ---- register unsigned char t = f->t; register int n = (int) f->n; register int state = f->f1; + register unsigned char lastc; while(GF_GETC(f, c)){ + lastc = c; + if(f->f2){ + GF_PUTC(f->next, c); + continue; + } + if(state){ state = 0; if (c != '=') { ! f->f2++; ! GF_PUTC(f->next, c); ! q_status_message(SM_ORDER,3,3, ! _("Warning: Illegal '=' in base64 text")); ! continue; } } *************** *** 1397,1404 **** break; default: /* impossible quantum position */ ! gf_error("Internal base64 decoder error"); ! /* NO RETURN */ } } } --- 1409,1419 ---- break; default: /* impossible quantum position */ ! f->f2++; ! GF_PUTC(f->next, lastc); ! q_status_message(SM_ORDER,3,3, ! _("Warning: Internal base64 decode error")); ! break; } } } *************** *** 1439,1444 **** --- 1454,1460 ---- dprint((9, "-- gf_reset b64_binary\n")); f->n = 0L; /* quantum position */ f->f1 = 0; /* state holder: equal seen? */ + f->f2 = 0; /* No errors when we start */ } } *************** *** 4979,4984 **** --- 4995,5001 ---- && p->value && (HANDLES_LOC(hd->html_data) || struncmp(p->value, "x-alpine-", 9) + || struncmp(p->value, "x-pine-help", 11) || p->value[0] == '#')) href = p; else if(!strucmp(p->attribute, "NAME")) *************** *** 7587,7592 **** --- 7604,7610 ---- char *p, buf[MAILTMPLEN]; ADDRESS *adr; extern char datestamp[]; + extern char plevstamp[]; if(!strcmp(s = removing_quotes(s + 4), "ALPINE_VERSION")){ p = ALPINE_VERSION; *************** *** 7600,7605 **** --- 7618,7626 ---- else if(!strcmp(s, "ALPINE_COMPILE_DATE")){ p = datestamp; } + else if(!strcmp(s, "ALPINE_PATCHLEVEL")){ + p = plevstamp; + } else if(!strcmp(s, "ALPINE_TODAYS_DATE")){ rfc822_date(p = buf); } *************** *** 9155,9160 **** --- 9176,9186 ---- margin_r, indent; char special[256]; + long curlinenum; /* current line number */ + int curqstrpos; /* current position in quote string */ + long linenum; /* line number */ + long qstrlen; /* multiples of 100 */ + char **qstrln; /* qstrln[i] = quote string line i - 1 */ } WRAP_S; #define WRAP_MARG_L(F) (((WRAP_S *)(F)->opt)->margin_l) *************** *** 9196,9201 **** --- 9222,9233 ---- #define WRAP_COLOR(F) (((WRAP_S *)(F)->opt)->color) #define WRAP_COLOR_SET(F) ((WRAP_COLOR(F)) && (WRAP_COLOR(F)->fg[0])) #define WRAP_SPACES(F) (((WRAP_S *)(F)->opt)->spaces) + #define WRAP_CURLINE(F) (((WRAP_S *)(F)->opt)->curlinenum) + #define WRAP_CURPOS(F) (((WRAP_S *)(F)->opt)->curqstrpos) + #define WRAP_LINENUM(F) (((WRAP_S *)(F)->opt)->linenum) + #define WRAP_QSTRLEN(F) (((WRAP_S *)(F)->opt)->qstrlen) + #define WRAP_QSTRN(F) (((WRAP_S *)(F)->opt)->qstrln) + #define WRAP_QSTR(F, N) (((WRAP_S *)(F)->opt)->qstrln[(N)]) #define WRAP_PUTC(F,C,W) { \ if((F)->linep == WRAP_LASTC(F)){ \ size_t offset = (F)->linep - (F)->line; \ *************** *** 9273,9278 **** --- 9305,9312 ---- case CCR : /* CRLF or CR in text ? */ state = BOL; /* either way, handle start */ + WRAP_CURLINE(f)++; + WRAP_CURPOS(f) = 0; if(WRAP_FLOW(f)){ /* wrapped line? */ if(f->f2 == 0 && WRAP_SPC_LEN(f) && WRAP_TRL_SPC(f)){ *************** *** 9366,9372 **** case BOL : if(WRAP_FLOW(f)){ ! if(c == '>'){ WRAP_FL_QC(f) = 1; /* init it */ state = FL_QLEV; /* go collect it */ } --- 9400,9410 ---- case BOL : if(WRAP_FLOW(f)){ ! if(WRAP_CURLINE(f) < WRAP_QSTRLEN(f) ! && WRAP_QSTR(f, WRAP_CURLINE(f)) ! && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)] ! && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)] == c){ ! WRAP_CURPOS(f)++; WRAP_FL_QC(f) = 1; /* init it */ state = FL_QLEV; /* go collect it */ } *************** *** 9380,9386 **** } /* quote level change implies new paragraph */ ! if(WRAP_FL_QD(f)){ WRAP_FL_QD(f) = 0; if(WRAP_HARD(f) == 0){ WRAP_HARD(f) = 1; --- 9418,9433 ---- } /* quote level change implies new paragraph */ ! if (WRAP_CURLINE(f) > 0 ! && WRAP_CURLINE(f) < WRAP_QSTRLEN(f) ! && (WRAP_QSTR(f, WRAP_CURLINE(f)) != NULL ! || WRAP_QSTR(f, WRAP_CURLINE(f) - 1) != NULL) ! && ((WRAP_QSTR(f, WRAP_CURLINE(f)) != NULL && ! WRAP_QSTR(f, WRAP_CURLINE(f) - 1) == NULL) ! || (WRAP_QSTR(f, WRAP_CURLINE(f)) == NULL && ! WRAP_QSTR(f, WRAP_CURLINE(f) - 1) != NULL) ! || strcmp(WRAP_QSTR(f, WRAP_CURLINE(f)), ! WRAP_QSTR(f, WRAP_CURLINE(f) - 1)))){ WRAP_FL_QD(f) = 0; if(WRAP_HARD(f) == 0){ WRAP_HARD(f) = 1; *************** *** 9432,9439 **** break; case FL_QLEV : ! if(c == '>'){ /* another level */ ! WRAP_FL_QC(f)++; } else { /* if EMBEDed, process it and return here */ --- 9479,9490 ---- break; case FL_QLEV : ! if(WRAP_CURLINE(f) < WRAP_QSTRLEN(f) ! && WRAP_QSTR(f, WRAP_CURLINE(f)) ! && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)] ! && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)] == c){ ! WRAP_CURPOS(f)++; ! WRAP_FL_QC(f)++; /* another level */ } else { /* if EMBEDed, process it and return here */ *************** *** 9445,9451 **** } /* quote level change signals new paragraph */ ! if(WRAP_FL_QC(f) != WRAP_FL_QD(f)){ WRAP_FL_QD(f) = WRAP_FL_QC(f); if(WRAP_HARD(f) == 0){ /* add hard newline */ WRAP_HARD(f) = 1; /* hard newline */ --- 9496,9511 ---- } /* quote level change signals new paragraph */ ! if (WRAP_CURLINE(f) > 0 ! && WRAP_CURLINE(f) < WRAP_QSTRLEN(f) ! && (WRAP_QSTR(f, WRAP_CURLINE(f)) ! || WRAP_QSTR(f, WRAP_CURLINE(f) - 1)) ! && ((WRAP_QSTR(f, WRAP_CURLINE(f)) && ! !WRAP_QSTR(f, WRAP_CURLINE(f) - 1)) ! || (!WRAP_QSTR(f, WRAP_CURLINE(f)) && ! WRAP_QSTR(f, WRAP_CURLINE(f) - 1)) ! || strcmp(WRAP_QSTR(f, WRAP_CURLINE(f)), ! WRAP_QSTR(f, WRAP_CURLINE(f) - 1)))){ WRAP_FL_QD(f) = WRAP_FL_QC(f); if(WRAP_HARD(f) == 0){ /* add hard newline */ WRAP_HARD(f) = 1; /* hard newline */ *************** *** 9502,9507 **** --- 9562,9574 ---- state = FL_SIG; break; + case ' ' : /* what? */ + if (WRAP_CURLINE(f) < WRAP_QSTRLEN(f) + && WRAP_QSTR(f, WRAP_CURLINE(f))){ + WRAP_SPC_LEN(f)++; + so_writec(' ', WRAP_SPACES(f)); + } + default : /* something else */ state = DFL; goto case_dfl; /* handle c like DFL */ *************** *** 9518,9524 **** &eob); /* note any embedded*/ wrap_eol(f, 1, &ip, &eib, &op, &eob); /* plunk down newline */ ! wrap_bol(f, 1, 1, &ip, &eib, &op, &eob); /* write any prefix */ } --- 9585,9591 ---- &eob); /* note any embedded*/ wrap_eol(f, 1, &ip, &eib, &op, &eob); /* plunk down newline */ ! wrap_bol(f, 1, WRAP_FLOW(f), &ip, &eib, &op, &eob); /* write any prefix */ } *************** *** 10015,10021 **** wrap_flush_embed(f, &ip, &eib, &op, &eob); wrap_eol(f, 1, &ip, &eib, &op, &eob); /* plunk down newline */ ! wrap_bol(f,1,1, &ip, &eib, &op, &eob); /* write any prefix */ } --- 10082,10088 ---- wrap_flush_embed(f, &ip, &eib, &op, &eob); wrap_eol(f, 1, &ip, &eib, &op, &eob); /* plunk down newline */ ! wrap_bol(f,1,WRAP_FLOW(f), &ip, &eib, &op, &eob); /* write any prefix */ } *************** *** 10088,10093 **** --- 10155,10167 ---- if(WRAP_COLOR(f)) free_color_pair(&WRAP_COLOR(f)); + { long i; + for (i = 0L; i < WRAP_QSTRLEN(f); i++) + if (WRAP_QSTR(f,i)) + fs_give((void **) &(WRAP_QSTR(f,i))); + fs_give((void **)&WRAP_QSTRN(f)); + } + fs_give((void **) &f->line); /* free temp line buffer */ so_give(&WRAP_SPACES(f)); fs_give((void **) &f->opt); /* free wrap widths struct */ *************** *** 10438,10444 **** { int j, i; COLOR_PAIR *col = NULL; ! char *prefix = NULL, *last_prefix = NULL; if(ps_global->VAR_QUOTE_REPLACE_STRING){ get_pair(ps_global->VAR_QUOTE_REPLACE_STRING, &prefix, &last_prefix, 0, 0); --- 10512,10519 ---- { int j, i; COLOR_PAIR *col = NULL; ! char *prefix = NULL, *last_prefix = NULL, *wrap_qstr = NULL; ! int level = 0, oldj, len; if(ps_global->VAR_QUOTE_REPLACE_STRING){ get_pair(ps_global->VAR_QUOTE_REPLACE_STRING, &prefix, &last_prefix, 0, 0); *************** *** 10447,10456 **** last_prefix = NULL; } } ! ! for(j = 0; j < WRAP_FL_QD(f); j++){ if(WRAP_USE_CLR(f)){ ! if((j % 3) == 0 && ps_global->VAR_QUOTE1_FORE_COLOR && ps_global->VAR_QUOTE1_BACK_COLOR && (col = new_color_pair(ps_global->VAR_QUOTE1_FORE_COLOR, --- 10522,10543 ---- last_prefix = NULL; } } ! ! if(WRAP_CURLINE(f) < WRAP_QSTRLEN(f) && WRAP_QSTR(f, WRAP_CURLINE(f))) ! wrap_qstr = cpystr(WRAP_QSTR(f, WRAP_CURLINE(f))); ! len = wrap_qstr ? strlen(wrap_qstr) : 0; ! ! for (j = wrap_qstr && *wrap_qstr == ' ' ? 1 : 0; ! j < len && isspace((unsigned char)wrap_qstr[j]); j++){ ! GF_PUTC_GLO(f->next, wrap_qstr[j]); ! f->n += ((wrap_qstr[j] == TAB) ? (~f->n & 0x07) + 1 : 1); ! } ! ! for(; j < len && level < len; level++){ ! oldj = j; ! j = next_level_quote(wrap_qstr, (char **)NULL, j, WRAP_FLOW(f)); if(WRAP_USE_CLR(f)){ ! if((level % 3) == 0 && ps_global->VAR_QUOTE1_FORE_COLOR && ps_global->VAR_QUOTE1_BACK_COLOR && (col = new_color_pair(ps_global->VAR_QUOTE1_FORE_COLOR, *************** *** 10458,10464 **** && pico_is_good_colorpair(col)){ GF_COLOR_PUTC(f, col); } ! else if((j % 3) == 1 && ps_global->VAR_QUOTE2_FORE_COLOR && ps_global->VAR_QUOTE2_BACK_COLOR && (col = new_color_pair(ps_global->VAR_QUOTE2_FORE_COLOR, --- 10545,10551 ---- && pico_is_good_colorpair(col)){ GF_COLOR_PUTC(f, col); } ! else if((level % 3) == 1 && ps_global->VAR_QUOTE2_FORE_COLOR && ps_global->VAR_QUOTE2_BACK_COLOR && (col = new_color_pair(ps_global->VAR_QUOTE2_FORE_COLOR, *************** *** 10466,10472 **** && pico_is_good_colorpair(col)){ GF_COLOR_PUTC(f, col); } ! else if((j % 3) == 2 && ps_global->VAR_QUOTE3_FORE_COLOR && ps_global->VAR_QUOTE3_BACK_COLOR && (col = new_color_pair(ps_global->VAR_QUOTE3_FORE_COLOR, --- 10553,10559 ---- && pico_is_good_colorpair(col)){ GF_COLOR_PUTC(f, col); } ! else if((level % 3) == 2 && ps_global->VAR_QUOTE3_FORE_COLOR && ps_global->VAR_QUOTE3_BACK_COLOR && (col = new_color_pair(ps_global->VAR_QUOTE3_FORE_COLOR, *************** *** 10480,10522 **** } } if(!WRAP_LV_FLD(f)){ if(!WRAP_FOR_CMPS(f) && ps_global->VAR_QUOTE_REPLACE_STRING && prefix){ for(i = 0; prefix[i]; i++) GF_PUTC_GLO(f->next, prefix[i]); ! f->n += utf8_width(prefix); ! } ! else if(ps_global->VAR_REPLY_STRING ! && (!strcmp(ps_global->VAR_REPLY_STRING, ">") ! || !strcmp(ps_global->VAR_REPLY_STRING, "\">\""))){ ! GF_PUTC_GLO(f->next, '>'); ! f->n += 1; } else{ ! GF_PUTC_GLO(f->next, '>'); ! GF_PUTC_GLO(f->next, ' '); ! f->n += 2; } } else{ ! GF_PUTC_GLO(f->next, '>'); ! f->n += 1; } } if(j && WRAP_LV_FLD(f)){ GF_PUTC_GLO(f->next, ' '); f->n++; } ! else if(j && last_prefix){ for(i = 0; last_prefix[i]; i++) GF_PUTC_GLO(f->next, last_prefix[i]); ! f->n += utf8_width(last_prefix); } if(prefix) fs_give((void **)&prefix); if(last_prefix) fs_give((void **)&last_prefix); return 0; } --- 10567,10626 ---- } } + if (j > 1 && wrap_qstr[j-1] == ' ') + j -= 1; + if(!WRAP_LV_FLD(f)){ if(!WRAP_FOR_CMPS(f) && ps_global->VAR_QUOTE_REPLACE_STRING && prefix){ for(i = 0; prefix[i]; i++) GF_PUTC_GLO(f->next, prefix[i]); ! f->n += utf8_widthis(prefix); } else{ ! for (i = oldj; i < j; i++) ! GF_PUTC_GLO(f->next, wrap_qstr[i]); ! f->n += j - oldj; } } else{ ! for (i = oldj; i < j; i++) ! GF_PUTC_GLO(f->next, wrap_qstr[i]); ! f->n += j - oldj; ! } ! for (i = j; isspace((unsigned char)wrap_qstr[i]); i++); ! if(!wrap_qstr[i]){ ! f->n += i - j; ! for (; j < i; j++) ! GF_PUTC_GLO(f->next, ' '); ! } ! else{ ! if((WRAP_LV_FLD(f) ! || !ps_global->VAR_QUOTE_REPLACE_STRING || !prefix) ! || !ps_global->VAR_REPLY_STRING ! || (strcmp(ps_global->VAR_REPLY_STRING, ">") ! && strcmp(ps_global->VAR_REPLY_STRING, "\">\""))){ ! GF_PUTC_GLO(f->next, ' '); ! f->n += 1; ! } } + for (; isspace((unsigned char)wrap_qstr[j]); j++); } if(j && WRAP_LV_FLD(f)){ GF_PUTC_GLO(f->next, ' '); f->n++; } ! else if(j && !value_is_space(wrap_qstr) && last_prefix){ for(i = 0; last_prefix[i]; i++) GF_PUTC_GLO(f->next, last_prefix[i]); ! f->n += utf8_widthis(last_prefix); } if(prefix) fs_give((void **)&prefix); if(last_prefix) fs_give((void **)&last_prefix); + if (wrap_qstr) + fs_give((void **)&wrap_qstr); return 0; } *************** *** 10548,10553 **** --- 10652,10663 ---- wrap->hdr_color = (GFW_HDRCOLOR & flags) == GFW_HDRCOLOR; wrap->for_compose = (GFW_FORCOMPOSE & flags) == GFW_FORCOMPOSE; wrap->handle_soft_hyphen = (GFW_SOFTHYPHEN & flags) == GFW_SOFTHYPHEN; + wrap->curlinenum = 0L; + wrap->curqstrpos = 0; + wrap->linenum = 0L; + wrap->qstrlen = 100L; + wrap->qstrln = (char **) fs_get(100*sizeof(char *)); + memset(wrap->qstrln, 0, 100*sizeof(char *)); return((void *) wrap); } *************** *** 10991,10997 **** --- 11101,11315 ---- } \ } + #define ADD_QUOTE_STRING(F) { \ + int len = tmp_20k_buf[0] ? strlen(tmp_20k_buf) + 1 : 0; \ + FILTER_S *fltr; \ + \ + for(fltr = (F); fltr && fltr->f != gf_wrap; fltr = fltr->next); \ + if (fltr){ \ + if (WRAP_LINENUM(fltr) >= WRAP_QSTRLEN(fltr)){ \ + fs_resize((void **)&WRAP_QSTRN(fltr), \ + (WRAP_QSTRLEN(fltr) + 100) * sizeof(char *)); \ + memset(WRAP_QSTRN(fltr)+WRAP_QSTRLEN(fltr), 0, \ + 100*sizeof(char*)); \ + WRAP_QSTRLEN(fltr) += 100L; \ + } \ + if (len){ \ + WRAP_QSTR(fltr, WRAP_LINENUM(fltr)) = \ + (char *) fs_get(len*sizeof(char)); \ + WRAP_QSTR(fltr, WRAP_LINENUM(fltr)) = cpystr(tmp_20k_buf);\ + } \ + WRAP_LINENUM(fltr)++; \ + } \ + } + + int end_of_line(char *line) + { + int i; + + for(i= 0; line && line[i]; i++){ + if((line[i] == '\015' && line[i+1] == '\012') || line[i] == '\012') + break; + } + return i; + } + + /* This macro is used in gf_quote_test. It receives a return code + from a filter. All filters that will print something must send + return code 0, except color_a_quote which must send return code + 1 + */ + + #define GF_ADD_QUOTED_LINE(F, line) \ + { \ + LT_INS_S *ins = NULL, *insp; \ + int done; \ + char *gline, *cline;\ + unsigned char ch;\ + register char *cp;\ + register int l;\ + \ + for (gline = cline = line; gline && cline; ){\ + if(cline = strchr(gline,'\012'))\ + *cline = '\0';\ + done = (*((LINETEST_S *) (F)->opt)->f)((F)->n++, gline, &ins,\ + ((LINETEST_S *) (F)->opt)->local);\ + if (done < 2){ \ + if(done == 1)\ + ADD_QUOTE_STRING((F));\ + for(insp = ins, cp = gline; *cp ; ){\ + if(insp && cp == insp->where){\ + if(insp->len > 0){ \ + for(l = 0; l < insp->len; l++){\ + ch = (unsigned char) insp->text[l];\ + GF_PUTC((F)->next, ch);\ + }\ + insp = insp->next;\ + continue; \ + } else if(insp->len < 0){ \ + cp -= insp->len; \ + insp = insp->next; \ + continue; \ + } \ + }\ + GF_PUTC((F)->next, *cp);\ + cp++;\ + }\ + while(insp){\ + for(l = 0; l < insp->len; l++){\ + ch = (unsigned char) insp->text[l];\ + GF_PUTC((F)->next, ch);\ + }\ + insp = insp->next;\ + }\ + gf_line_test_free_ins(&ins);\ + if(cline){ \ + *cline = '\012';\ + gline += cline - gline + 1;\ + }\ + GF_PUTC((F)->next, '\015');\ + GF_PUTC((F)->next, '\012');\ + }\ + }\ + } + /* test second line of old line first */ + #define SECOND_LINE_QUOTE_TEST(line, F) \ + {\ + *p = '\0';\ + i = end_of_line((F)->oldline); \ + if (((F)->oldline)[i]){\ + i += (((F)->oldline)[i] == '\015') ? 2 : 1;\ + line = (F)->oldline + i;\ + i = end_of_line(line); \ + if(line[i])\ + line[i] = '\0'; \ + }\ + for (i = 0; ((F)->line) \ + && (i < LINE_TEST_BLOCK) \ + && (i < SIZEOF_20KBUF)\ + && ((F)->line)[i] \ + && (((F)->line)[i] != '\015')\ + && (((F)->line)[i] != '\012')\ + && (tmp_20k_buf[i] = ((F)->line)[i]); i++);\ + tmp_20k_buf[i] = '\0';\ + GF_ADD_QUOTED_LINE((F), line);\ + } + + #define FIRST_LINE_QUOTE_TEST(line, F)\ + {\ + *p = '\0';\ + line = (F)->line;\ + if ((F)->oldline)\ + fs_give((void **)&(F)->oldline);\ + (F)->oldline = cpystr(line);\ + i = end_of_line(line); \ + if (line[i]){ \ + j = (line[i] == '\015') ? 2 : 1;\ + line[i] = '\0'; \ + i += j; \ + }\ + for (j = 0; ((F)->line) \ + && ((i + j) < LINE_TEST_BLOCK) \ + && (j < SIZEOF_20KBUF) \ + && ((F)->line)[i + j] \ + && (((F)->line)[i + j] != '\015')\ + && (((F)->line)[i + j] != '\012')\ + && (tmp_20k_buf[j] = ((F)->line)[i + j]); j++);\ + tmp_20k_buf[j] = '\0';\ + GF_ADD_QUOTED_LINE((F), line);\ + } + + + void + gf_quote_test(f, flg) + FILTER_S *f; + int flg; + { + register char *p = f->linep; + register char *eobuf = GF_LINE_TEST_EOB(f); + char *line = NULL; + int i, j; + GF_INIT(f, f->next); + + if(flg == GF_DATA){ + register unsigned char c; + register int state = f->f1; + + while(GF_GETC(f, c)){ + + GF_LINE_TEST_ADD(f, c); + if(c == '\012') + state++; + if(state == 2){ /* two full lines read */ + state = 0; + + /* first process the second line of an old line */ + if (f->oldline && f->oldline[0]) + SECOND_LINE_QUOTE_TEST(line, f); + + /* now we process the first line */ + FIRST_LINE_QUOTE_TEST(line, f); + p = f->line; + } + } + + f->f1 = state; + GF_END(f, f->next); + } + else if(flg == GF_EOD){ + /* first process the second line of an old line */ + if (f->oldline && f->oldline[0]) + SECOND_LINE_QUOTE_TEST(line, f); + + /* now we process the first line */ + FIRST_LINE_QUOTE_TEST(line, f); + + /* We are out of data. In this case we have processed the second + * line of an oldline, then the first line of a line, but we need + * to process the second line of the given line. We do this by + * processing it now!. + */ + if (line[i]){ + tmp_20k_buf[0] = '\0'; /* No next line */ + GF_ADD_QUOTED_LINE(f, line+i); + } + + fs_give((void **) &f->oldline); /* free old line buffer */ + fs_give((void **) &f->line); /* free line buffer */ + fs_give((void **) &f->opt); /* free test struct */ + GF_FLUSH(f->next); + (*f->next->f)(f->next, GF_EOD); + } + else if(flg == GF_RESET){ + f->f1 = 0; /* state */ + f->n = 0L; /* line number */ + f->f2 = LINE_TEST_BLOCK; /* size of alloc'd line */ + f->line = p = (char *) fs_get(f->f2 * sizeof(char)); + } + + f->linep = p; + } /* * this simple filter accumulates characters until a newline, offers it diff -rc alpine-2.00/pith/filter.h alpine-2.00.I.USE/pith/filter.h *** alpine-2.00/pith/filter.h 2008-08-22 12:46:13.000000000 -0700 --- alpine-2.00.I.USE/pith/filter.h 2011-02-07 20:33:45.000000000 -0800 *************** *** 175,181 **** char *gf_pipe(gf_io_t, gf_io_t); long gf_bytes_piped(void); char *gf_filter(char *, char *, STORE_S *, gf_io_t, FILTLIST_S *, int, ! void (*)(PIPE_S *, int, void *)); void gf_binary_b64(FILTER_S *, int); void gf_b64_binary(FILTER_S *, int); void gf_qp_8bit(FILTER_S *, int); --- 175,181 ---- char *gf_pipe(gf_io_t, gf_io_t); long gf_bytes_piped(void); char *gf_filter(char *, char *, STORE_S *, gf_io_t, FILTLIST_S *, int, ! int, void (*)(PIPE_S *, int, void *)); void gf_binary_b64(FILTER_S *, int); void gf_b64_binary(FILTER_S *, int); void gf_qp_8bit(FILTER_S *, int); *************** *** 215,220 **** --- 215,221 ---- void *gf_prepend_editorial_opt(prepedtest_t, char *); void gf_nvtnl_local(FILTER_S *, int); void gf_local_nvtnl(FILTER_S *, int); + void gf_quote_test(FILTER_S *, int); void *gf_url_hilite_opt(URL_HILITE_S *, HANDLE_S **, int); diff -rc alpine-2.00/pith/filttype.h alpine-2.00.I.USE/pith/filttype.h *** alpine-2.00/pith/filttype.h 2007-04-25 21:06:02.000000000 -0700 --- alpine-2.00.I.USE/pith/filttype.h 2011-02-07 20:33:42.000000000 -0800 *************** *** 35,40 **** --- 35,42 ---- unsigned char t; /* temporary char */ char *line; /* place for temporary storage */ char *linep; /* pointer into storage space */ + char *oldline; /* the previous line to "line" */ + char *oldlinep; /* the previous line to "line" */ void *opt; /* optional per instance data */ void *data; /* misc internal data pointer */ unsigned char queue[1 + GF_MAXBUF]; diff -rc alpine-2.00/pith/flag.c alpine-2.00.I.USE/pith/flag.c *** alpine-2.00/pith/flag.c 2008-07-14 11:01:54.000000000 -0700 --- alpine-2.00.I.USE/pith/flag.c 2011-02-07 20:33:41.000000000 -0800 *************** *** 593,606 **** was_invisible = (pelt->hidden || pelt->colhid) ? 1 : 0; if((chk_thrd_cnt = ((msgs->visible_threads >= 0L) && THRD_INDX_ENABLED() && (f & MN_HIDE) && (pelt->hidden != v))) != 0){ thrd = fetch_thread(stream, rawno); if(thrd && thrd->top){ ! if(thrd->top == thrd->rawno) topthrd = thrd; else ! topthrd = fetch_thread(stream, thrd->top); } if(topthrd){ --- 593,608 ---- was_invisible = (pelt->hidden || pelt->colhid) ? 1 : 0; + thrd = fetch_thread(stream, rawno); + if((chk_thrd_cnt = ((msgs->visible_threads >= 0L) && THRD_INDX_ENABLED() && (f & MN_HIDE) && (pelt->hidden != v))) != 0){ thrd = fetch_thread(stream, rawno); if(thrd && thrd->top){ ! if(top_thread(stream, thrd->top) == thrd->rawno) topthrd = thrd; else ! topthrd = fetch_thread(stream, top_thread(stream, thrd->top)); } if(topthrd){ diff -rc alpine-2.00/pith/handle.h alpine-2.00.I.USE/pith/handle.h *** alpine-2.00/pith/handle.h 2007-11-13 16:47:15.000000000 -0800 --- alpine-2.00.I.USE/pith/handle.h 2011-02-07 20:33:45.000000000 -0800 *************** *** 39,44 **** --- 39,45 ---- unsigned using_is_used:1; /* bit below is being used */ unsigned is_used:1; /* if not, remove it from list */ unsigned color_unseen:1; /* we're coloring folders with unseen */ + unsigned color_folder:1; /* and just folders for that matter... */ unsigned is_dual_do_open:1; /* choosing this handle means open */ union { struct { /* URL corresponding to this handle */ diff -rc alpine-2.00/pith/imap.c alpine-2.00.I.USE/pith/imap.c *** alpine-2.00/pith/imap.c 2008-07-14 11:01:54.000000000 -0700 --- alpine-2.00.I.USE/pith/imap.c 2011-02-07 20:33:43.000000000 -0800 *************** *** 1108,1110 **** --- 1108,1172 ---- return(block); } + + + void + pine_delete_pwd(NETMBX *mb, char *user) + { + char hostlist0[MAILTMPLEN], hostlist1[MAILTMPLEN]; + char port[20], non_def_port[20], insecure[20]; + STRLIST_S hostlist[2]; + MMLOGIN_S *l; + struct servent *sv; + + + dprint((9, "pine_delete_pwd\n")); + + /* setup hostlist */ + non_def_port[0] = '\0'; + if(mb->port && mb->service && + (sv = getservbyname(mb->service, "tcp")) && + (mb->port != ntohs(sv->s_port))){ + snprintf(non_def_port, sizeof(non_def_port), ":%lu", mb->port); + non_def_port[sizeof(non_def_port)-1] = '\0'; + dprint((9, "mm_login: using non-default port=%s\n", + non_def_port ? non_def_port : "?")); + } + + if(*non_def_port){ + strncpy(hostlist0, mb->host, sizeof(hostlist0)-1); + hostlist0[sizeof(hostlist0)-1] = '\0'; + strncat(hostlist0, non_def_port, sizeof(hostlist0)-strlen(hostlist0)-1); + hostlist0[sizeof(hostlist0)-1] = '\0'; + hostlist[0].name = hostlist0; + if(mb->orighost && mb->orighost[0] && strucmp(mb->host, mb->orighost)){ + strncpy(hostlist1, mb->orighost, sizeof(hostlist1)-1); + hostlist1[sizeof(hostlist1)-1] = '\0'; + strncat(hostlist1, non_def_port, sizeof(hostlist1)-strlen(hostlist1)-1); + hostlist1[sizeof(hostlist1)-1] = '\0'; + hostlist[0].next = &hostlist[1]; + hostlist[1].name = hostlist1; + hostlist[1].next = NULL; + } + else + hostlist[0].next = NULL; + } + else{ + hostlist[0].name = mb->host; + if(mb->orighost && mb->orighost[0] && strucmp(mb->host, mb->orighost)){ + hostlist[0].next = &hostlist[1]; + hostlist[1].name = mb->orighost; + hostlist[1].next = NULL; + } + else + hostlist[0].next = NULL; + } + + for(l = mm_login_list; l; l = l->next) + if(imap_same_host(l->hosts, hostlist) + && !strcmp(l->user, user ? user : "") + && l->passwd){ + l->passwd[0] = '\0'; + break; + } + } diff -rc alpine-2.00/pith/imap.h alpine-2.00.I.USE/pith/imap.h *** alpine-2.00/pith/imap.h 2008-04-23 19:00:26.000000000 -0700 --- alpine-2.00.I.USE/pith/imap.h 2011-02-07 20:33:43.000000000 -0800 *************** *** 123,129 **** int imap_get_passwd(MMLOGIN_S *, char *, char *, STRLIST_S *, int); void imap_set_passwd(MMLOGIN_S **, char *, char *, STRLIST_S *, int, int, int); void imap_flush_passwd_cache(int); ! /* currently mandatory to implement stubs */ --- 123,129 ---- int imap_get_passwd(MMLOGIN_S *, char *, char *, STRLIST_S *, int); void imap_set_passwd(MMLOGIN_S **, char *, char *, STRLIST_S *, int, int, int); void imap_flush_passwd_cache(int); ! void pine_delete_pwd(NETMBX *mb, char *user); /* currently mandatory to implement stubs */ diff -rc alpine-2.00/pith/indxtype.h alpine-2.00.I.USE/pith/indxtype.h *** alpine-2.00/pith/indxtype.h 2008-05-28 13:19:56.000000000 -0700 --- alpine-2.00.I.USE/pith/indxtype.h 2011-02-07 20:33:42.000000000 -0800 *************** *** 75,86 **** iKey, iKeyInit, iPrefDate, iPrefTime, iPrefDateTime, iCurPrefDate, iCurPrefTime, iCurPrefDateTime, ! iSize, iSizeComma, iSizeNarrow, iDescripSize, iNewsAndTo, iToAndNews, iNewsAndRecips, iRecipsAndNews, iFromTo, iFromToNotNews, iFrom, iTo, iSender, iCc, iNews, iRecips, iCurNews, iArrow, iMailbox, iAddress, iInit, iCursorPos, iDay2Digit, iMon2Digit, iYear2Digit, iSTime, iKSize, iRoleNick, iNewLine, iHeader, iText, --- 75,89 ---- iKey, iKeyInit, iPrefDate, iPrefTime, iPrefDateTime, iCurPrefDate, iCurPrefTime, iCurPrefDateTime, ! iSize, iSizeComma, iSizeNarrow, iDescripSize, iSizeThread, iNewsAndTo, iToAndNews, iNewsAndRecips, iRecipsAndNews, iFromTo, iFromToNotNews, iFrom, iTo, iSender, iCc, iNews, iRecips, iCurNews, iArrow, iMailbox, iAddress, iInit, iCursorPos, iDay2Digit, iMon2Digit, iYear2Digit, + iFolder, iFlag, iCollection, iRole, iProcid, iScreen, iPkey, + iNick, iAddressTo, iAddressCc, iAddressRecip, iBcc, iLcc, + iFfrom, iFadd, iSTime, iKSize, iRoleNick, iNewLine, iHeader, iText, *************** *** 102,116 **** /* these are flags for the what_for field in INDEX_PARSE_T */ ! #define FOR_NOTHING 0x00 ! #define FOR_INDEX 0x01 ! #define FOR_REPLY_INTRO 0x02 ! #define FOR_TEMPLATE 0x04 /* or for signature */ ! #define FOR_FILT 0x08 ! #define DELIM_USCORE 0x10 ! #define DELIM_PAREN 0x20 ! #define DELIM_COLON 0x40 ! #define DEFAULT_REPLY_INTRO "default" --- 105,130 ---- /* these are flags for the what_for field in INDEX_PARSE_T */ ! #define FOR_NOTHING 0x00000 ! #define FOR_INDEX 0x00001 ! #define FOR_REPLY_INTRO 0x00002 ! #define FOR_TEMPLATE 0x00004 /* or for signature */ ! #define FOR_FILT 0x00008 ! #define DELIM_USCORE 0x00010 ! #define DELIM_PAREN 0x00020 ! #define DELIM_COLON 0x00040 ! #define FOR_FOLDER 0x00080 /* for rules */ ! #define FOR_RULE 0x00100 /* for rules */ ! #define FOR_TRIM 0x00200 /* for rules */ ! #define FOR_RESUB 0x00400 /* for rules */ ! #define FOR_REPLACE 0x00800 /* for rules */ ! #define FOR_SORT 0x01000 /* for rules */ ! #define FOR_FLAG 0x02000 /* for rules */ ! #define FOR_COMPOSE 0x04000 /* for rules */ ! #define FOR_THREAD 0x08000 /* for rules */ ! #define FOR_STARTUP 0x10000 /* for rules */ ! #define FOR_KEY 0x20000 /* for rules */ ! #define FOR_SAVE 0x40000 /* for rules */ #define DEFAULT_REPLY_INTRO "default" diff -rc alpine-2.00/pith/init.c alpine-2.00.I.USE/pith/init.c *** alpine-2.00/pith/init.c 2007-08-16 15:25:10.000000000 -0700 --- alpine-2.00.I.USE/pith/init.c 2011-02-07 20:33:41.000000000 -0800 *************** *** 407,412 **** --- 407,415 ---- && stricmp(filename, folder_base)){ #else if(strncmp(filename, folder_base, folder_base_len) == 0 + #ifndef _WINDOWS + && filename[folder_base_len] != list_cntxt->dir->delim + #endif && strcmp(filename, folder_base)){ #endif #endif diff -rc alpine-2.00/pith/mailcap.c alpine-2.00.I.USE/pith/mailcap.c *** alpine-2.00/pith/mailcap.c 2008-03-18 10:24:31.000000000 -0700 --- alpine-2.00.I.USE/pith/mailcap.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 52,57 **** --- 52,58 ---- int needsterminal; char *contenttype; char *command; + char *nametemplate; char *testcommand; char *label; /* unused */ char *printcommand; /* unused */ *************** *** 212,217 **** --- 213,221 ---- if(mc->printcommand) dprint((11, " printcommand: %s", mc->printcommand ? mc->printcommand : "?")); + if(mc->nametemplate) + dprint((11, " nametemplate: %s", + mc->nametemplate ? mc->nametemplate : "?")); dprint((11, " needsterminal %d\n", mc->needsterminal)); } } *************** *** 487,492 **** --- 491,501 ---- dprint((9, "mailcap: printcommand=%s\n", mc->printcommand ? mc->printcommand : "?")); } + else if(arg && !strucmp(*tokens, "nametemplate")){ + mc->nametemplate = arg; + dprint((9, "mailcap: nametemplate=%s\n", + arg ? arg : "?")); + } else if(arg && !strucmp(*tokens, "compose")){ /* not used */ dprint((9, "mailcap: not using compose=%s\n", *************** *** 973,975 **** --- 982,1041 ---- mail_free_stringlist(&MailcapData.raw); mc_free_entry(&MailcapData.head); } + + char * + mc_template(char *tmp_file, BODY *body, int chk_extension) + { + MailcapEntry *mc; + int quoted = 0; + char *s, *to, *namefile = NULL; + + mc = mc_get_command(body->type, body->subtype, body, chk_extension, NULL); + if(!mc || !mc->nametemplate || !tmp_file) + return tmp_file; + + /* remove extension if requested for a specific extension */ + if(mc->nametemplate && tmp_file && (s = strrchr(tmp_file, '.')) != NULL + && strchr(s, C_FILESEP) == NULL) + *s = '\0'; + + to = tmp_20k_buf; + if((s = strrchr(tmp_file, C_FILESEP)) != NULL){ + *s++ = '\0'; + sstrncpy(&to, tmp_file, SIZEOF_20KBUF-(to-tmp_20k_buf)); + if(to-tmp_20k_buf < SIZEOF_20KBUF) + *to++ = C_FILESEP; + namefile = s; + } + + for(s = mc->nametemplate; *s; s++) + if(quoted){ + quoted = 0; + switch(*s){ + case '%': + if(to-tmp_20k_buf < SIZEOF_20KBUF) + *to++ = '%'; + break; + + case 's': + sstrncpy(&to, namefile ? namefile : tmp_file, SIZEOF_20KBUF-(to-tmp_20k_buf)); + break; + + default: + dprint((9, + "Ignoring unercognized format code in nametemplate: %%%c\n", *s )); + break; + } + } + else if(*s == '%') + quoted = 1; + else if(to-tmp_20k_buf < SIZEOF_20KBUF) + *to++ = *s; + + *to++ = '\0'; + + fs_give((void **)&tmp_file); + tmp_file = cpystr(tmp_20k_buf); + return tmp_file; + } + diff -rc alpine-2.00/pith/mailcap.h alpine-2.00.I.USE/pith/mailcap.h *** alpine-2.00/pith/mailcap.h 2008-03-18 10:24:31.000000000 -0700 --- alpine-2.00.I.USE/pith/mailcap.h 2011-02-07 20:33:46.000000000 -0800 *************** *** 28,33 **** --- 28,34 ---- int mailcap_can_display(int, char *, BODY *, int); MCAP_CMD_S *mailcap_build_command(int, char *, BODY *, char *, int *, int); void mailcap_free(void); + char *mc_template(char *, BODY *, int); /* currently mandatory to implement stubs */ diff -rc alpine-2.00/pith/mailcmd.c alpine-2.00.I.USE/pith/mailcmd.c *** alpine-2.00/pith/mailcmd.c 2008-07-09 22:01:13.000000000 -0700 --- alpine-2.00.I.USE/pith/mailcmd.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 38,43 **** --- 38,44 ---- #include "../pith/ablookup.h" #include "../pith/search.h" #include "../pith/charconv/utf8.h" + #include "../pith/rules.h" #ifdef _WINDOWS #include "../pico/osdep/mswin.h" *************** *** 659,664 **** --- 660,666 ---- strncpy(ps_global->cur_folder, p, sizeof(ps_global->cur_folder)-1); ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0'; ps_global->context_current = ps_global->context_list; + setup_threading_index_style(); reset_index_format(); clear_index_cache(ps_global->mail_stream, 0); /* MUST sort before restoring msgno! */ *************** *** 771,776 **** --- 773,783 ---- if(stream) sp_set_first_unseen(stream, 0L); + /* in case we closed the old stream by cancelling the connection, do + * not let that interfere with opening the new stream. + */ + ps_global->user_says_cancel = 0; + m = context_open((new_context && !open_inbox) ? new_context : NULL, stream, open_inbox ? ps_global->VAR_INBOX_PATH : expanded_file, *************** *** 974,979 **** --- 981,987 ---- clear_index_cache(ps_global->mail_stream, 0); reset_index_format(); + setup_threading_index_style(); /* * Start news reading with messages the user's marked deleted *************** *** 1092,1098 **** if(!cur_already_set && mn_get_total(ps_global->msgmap) > 0L){ ! perfolder_startup_rule = reset_startup_rule(ps_global->mail_stream); if(ps_global->start_entry > 0){ mn_set_cur(ps_global->msgmap, mn_get_revsort(ps_global->msgmap) --- 1100,1109 ---- if(!cur_already_set && mn_get_total(ps_global->msgmap) > 0L){ ! perfolder_startup_rule = get_perfolder_startup_rule(ps_global->mail_stream, ! V_STARTUP_RULES, newfolder); ! ! reset_startup_rule(ps_global->mail_stream); if(ps_global->start_entry > 0){ mn_set_cur(ps_global->msgmap, mn_get_revsort(ps_global->msgmap) *************** *** 1114,1237 **** else use_this_startup_rule = ps_global->inc_startup_rule; ! switch(use_this_startup_rule){ ! /* ! * For news in incoming collection we're doing the same thing ! * for first-unseen and first-recent. In both those cases you ! * get first-unseen if FAKE_NEW is off and first-recent if ! * FAKE_NEW is on. If FAKE_NEW is on, first unseen is the ! * same as first recent because all recent msgs are unseen ! * and all unrecent msgs are seen (see pine_mail_open). ! */ ! case IS_FIRST_UNSEEN: ! first_unseen: ! mn_set_cur(ps_global->msgmap, ! (sp_first_unseen(m) ! && mn_get_sort(ps_global->msgmap) == SortArrival ! && !mn_get_revsort(ps_global->msgmap) ! && !get_lflag(ps_global->mail_stream, NULL, ! sp_first_unseen(m), MN_EXLD) ! && (n = mn_raw2m(ps_global->msgmap, ! sp_first_unseen(m)))) ! ? n ! : first_sorted_flagged(F_UNSEEN | F_UNDEL, m, pc, ! THREADING() ? 0 : FSF_SKIP_CHID)); ! break; ! ! case IS_FIRST_RECENT: ! first_recent: ! /* ! * We could really use recent for news but this is the way ! * it has always worked, so we'll leave it. That is, if ! * the FAKE_NEW feature is on, recent and unseen are ! * equivalent, so it doesn't matter. If the feature isn't ! * on, all the undeleted messages are unseen and we start ! * at the first one. User controls with the FAKE_NEW feature. ! */ ! if(IS_NEWS(ps_global->mail_stream)){ ! mn_set_cur(ps_global->msgmap, ! first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc, ! THREADING() ? 0 : FSF_SKIP_CHID)); ! } ! else{ ! mn_set_cur(ps_global->msgmap, ! first_sorted_flagged(F_RECENT | F_UNSEEN ! | F_UNDEL, ! m, pc, ! THREADING() ? 0 : FSF_SKIP_CHID)); ! } ! break; ! ! case IS_FIRST_IMPORTANT: ! mn_set_cur(ps_global->msgmap, ! first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, ! THREADING() ? 0 : FSF_SKIP_CHID)); ! break; ! ! case IS_FIRST_IMPORTANT_OR_UNSEEN: ! ! if(IS_NEWS(ps_global->mail_stream)) ! goto first_unseen; ! ! { ! MsgNo flagged, first_unseen; ! ! flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, ! THREADING() ? 0 : FSF_SKIP_CHID); ! first_unseen = (sp_first_unseen(m) ! && mn_get_sort(ps_global->msgmap) == SortArrival ! && !mn_get_revsort(ps_global->msgmap) ! && !get_lflag(ps_global->mail_stream, NULL, ! sp_first_unseen(m), MN_EXLD) ! && (n = mn_raw2m(ps_global->msgmap, ! sp_first_unseen(m)))) ! ? n ! : first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc, ! THREADING() ? 0 : FSF_SKIP_CHID); ! mn_set_cur(ps_global->msgmap, ! (MsgNo) MIN((int) flagged, (int) first_unseen)); ! ! } ! ! break; ! ! case IS_FIRST_IMPORTANT_OR_RECENT: ! ! if(IS_NEWS(ps_global->mail_stream)) ! goto first_recent; ! ! { ! MsgNo flagged, first_recent; ! ! flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, ! THREADING() ? 0 : FSF_SKIP_CHID); ! first_recent = first_sorted_flagged(F_RECENT | F_UNSEEN ! | F_UNDEL, ! m, pc, ! THREADING() ? 0 : FSF_SKIP_CHID); ! mn_set_cur(ps_global->msgmap, ! (MsgNo) MIN((int) flagged, (int) first_recent)); ! } ! ! break; ! ! case IS_FIRST: ! mn_set_cur(ps_global->msgmap, ! first_sorted_flagged(F_UNDEL, m, pc, ! THREADING() ? 0 : FSF_SKIP_CHID)); ! break; ! ! case IS_LAST: ! mn_set_cur(ps_global->msgmap, ! first_sorted_flagged(F_UNDEL, m, pc, ! FSF_LAST | (THREADING() ? 0 : FSF_SKIP_CHID))); ! break; ! ! default: ! panic("Unexpected incoming startup case"); ! break; ! ! } } else if(IS_NEWS(ps_global->mail_stream)){ /* --- 1125,1131 ---- else use_this_startup_rule = ps_global->inc_startup_rule; ! find_startup_position(use_this_startup_rule, m, pc); } else if(IS_NEWS(ps_global->mail_stream)){ /* *************** *** 1409,1417 **** /* Save read messages? */ if(VAR_READ_MESSAGE_FOLDER && VAR_READ_MESSAGE_FOLDER[0] && sp_flagged(stream, SP_INBOX) ! && (seen_not_del = count_flagged(stream, F_SEEN | F_UNDEL))){ if(F_ON(F_AUTO_READ_MSGS,ps_global) || (pith_opt_read_msg_prompt && (*pith_opt_read_msg_prompt)(seen_not_del, VAR_READ_MESSAGE_FOLDER))) /* move inbox's read messages */ --- 1303,1313 ---- /* Save read messages? */ if(VAR_READ_MESSAGE_FOLDER && VAR_READ_MESSAGE_FOLDER[0] && sp_flagged(stream, SP_INBOX) ! && (F_ON(F_AUTO_READ_MSGS_RULES, ps_global) || ! (seen_not_del = count_flagged(stream, F_SEEN | F_UNDEL)))){ if(F_ON(F_AUTO_READ_MSGS,ps_global) + || F_ON(F_AUTO_READ_MSGS_RULES, ps_global) || (pith_opt_read_msg_prompt && (*pith_opt_read_msg_prompt)(seen_not_del, VAR_READ_MESSAGE_FOLDER))) /* move inbox's read messages */ *************** *** 1682,1687 **** --- 1578,1586 ---- char *bufp = NULL; MESSAGECACHE *mc; + if (F_ON(F_AUTO_READ_MSGS_RULES, ps_global)) + return move_read_msgs_using_rules(stream, dstfldr, buf); + if(!is_absolute_path(dstfldr) && !(save_context = default_save_context(ps_global->context_list))) save_context = ps_global->context_list; *************** *** 1721,1728 **** snprintf(buf, buflen, "Moving %s read message%s to \"%s\"", comatose(searched), plural(searched), dstfldr); we_cancel = busy_cue(buf, NULL, 0); ! if(save(ps_global, stream, save_context, dstfldr, msgmap, ! SV_DELETE | SV_FIX_DELS | SV_INBOXWOCNTXT) == searched) strncpy(bufp = buf + 1, "Moved", MIN(5,buflen)); /* change Moving to Moved */ buf[buflen-1] = '\0'; --- 1620,1628 ---- snprintf(buf, buflen, "Moving %s read message%s to \"%s\"", comatose(searched), plural(searched), dstfldr); we_cancel = busy_cue(buf, NULL, 0); ! ps_global->exiting = 1; ! if((save(ps_global, stream, save_context, dstfldr, msgmap, ! SV_DELETE | SV_FIX_DELS | SV_INBOXWOCNTXT) == searched)) strncpy(bufp = buf + 1, "Moved", MIN(5,buflen)); /* change Moving to Moved */ buf[buflen-1] = '\0'; *************** *** 1760,1766 **** && ((context_isambig(folder) && folder_is_nick(folder, FOLDERS(context), 0)) || folder_index(folder, context, FI_FOLDER) > 0) ! && (seen_undel = count_flagged(stream, F_SEEN | F_UNDEL))){ for(; f && *archive; archive++){ char *p; --- 1660,1668 ---- && ((context_isambig(folder) && folder_is_nick(folder, FOLDERS(context), 0)) || folder_index(folder, context, FI_FOLDER) > 0) ! && ((seen_undel = count_flagged(stream, F_SEEN | F_UNDEL)) ! || (F_ON(F_AUTO_READ_MSGS,ps_global) && ! F_ON(F_AUTO_READ_MSGS_RULES, ps_global)))){ for(; f && *archive; archive++){ char *p; *************** *** 2158,2165 **** int ! agg_text_select(MAILSTREAM *stream, MSGNO_S *msgmap, char type, int not, ! int check_for_my_addresses, char *sstring, char *charset, SEARCHSET **limitsrch) { int old_imap, we_cancel; --- 2060,2067 ---- int ! agg_text_select(MAILSTREAM *stream, MSGNO_S *msgmap, char type, char *namehdr, ! int not, int check_for_my_addresses, char *sstring, char *charset, SEARCHSET **limitsrch) { int old_imap, we_cancel; *************** *** 2308,2313 **** --- 2210,2219 ---- if(!mepgm) switch(type){ + case 'h' : /* Any header */ + pgm->header = mail_newsearchheader (namehdr, sstring); + break; + case 'r' : /* TO or CC */ if(old_imap){ /* No OR on old servers */ *************** *** 2714,2716 **** --- 2620,2921 ---- return(*target ? target : NULL); } + + char * + move_read_msgs_using_rules(MAILSTREAM *stream, char *dstfldr, char *buf) + { + CONTEXT_S *save_context = NULL; + char **folder_to_save = NULL; + int num, we_cancel; + long i, j, success; + MSGNO_S *msgmap = NULL; + unsigned long nmsgs = 0L, stream_nmsgs; + + saved_stream = stream; /* horrible hack! */ + if(!is_absolute_path(dstfldr) + && !(save_context = default_save_context(ps_global->context_list))) + save_context = ps_global->context_list; + + folder_to_save = (char **)fs_get((stream->nmsgs + 1)*sizeof(char *)); + folder_to_save[0] = NULL; + mn_init(&msgmap, stream->nmsgs); + stream_nmsgs = stream->nmsgs; + for (i = 1L; i <= stream_nmsgs ; i++){ + set_lflag(stream, msgmap, i, MN_SLCT, 0); + folder_to_save[i] = get_lflag(stream, NULL, i, MN_EXLD) + ? NULL : get_folder_to_save(stream, i, dstfldr); + } + for (i = 1L; i <= stream_nmsgs; i++){ + num = 0; + if (folder_to_save[i]){ + mn_init(&msgmap, stream_nmsgs); + for (j = i; j <= stream_nmsgs ; j++){ + if (folder_to_save[j]){ + if (!strcmp(folder_to_save[i], folder_to_save[j])){ + set_lflag(stream, msgmap, j, MN_SLCT, 1); + num++; + if (j != i) + fs_give((void **)&folder_to_save[j]); + } + } + } + pseudo_selected(stream, msgmap); + sprintf(buf, "Moving %s read message%s to \"%.45s\"", + comatose(num), plural(num), folder_to_save[i]); + we_cancel = busy_cue(buf, NULL, 1); + ps_global->exiting = 1; + if(success = save(ps_global, stream,save_context, folder_to_save[i], + msgmap, SV_DELETE | SV_FIX_DELS)) + nmsgs += success; + if(we_cancel) + cancel_busy_cue(success ? 0 : -1); + for (j = i; j <= stream_nmsgs ; j++) + set_lflag(stream, msgmap, j, MN_SLCT, 0); + fs_give((void **)&folder_to_save[i]); + mn_give(&msgmap); + } + } + ps_global->exiting = 0; /* useful if we call from aggregate operations */ + sprintf(buf, "Moved automatically %s message%s", + comatose(nmsgs), plural(nmsgs)); + if (folder_to_save) + fs_give((void **)folder_to_save); + rule_curpos = 0L; + return buf; + } + + char * + get_folder_to_save(MAILSTREAM *stream, long i, char *dstfldr) + { + MESSAGECACHE *mc = NULL; + RULE_RESULT *rule; + MSGNO_S *msgmap = NULL; + char *folder_to_save = NULL, *save_folder = NULL; + int n; + long msgno; + + /* The plan is as follows: Select each message of the folder. We + * need to set the cursor correctly so that iFlag gets the value + * correctly too, otherwise iFlag will get the value of the position + * of the cursor. After that we need to look for a rule that applies + * to the message and get the saving folder. If we get a saving folder, + * and we used the _FLAG_ token, use that folder, if no + * _FLAG_ token was used, move only if seen and not deleted, to the + * folder specified in the saving rule. If we did not get a saving + * folder from the rule, just save in the default folder. + */ + mn_init(&msgmap, stream->nmsgs); + rule_curpos = i; + msgno = mn_m2raw(msgmap, i); + if (msgno > 0L){ + mc = mail_elt(stream, msgno); + rule = (RULE_RESULT *) + get_result_rule(V_SAVE_RULES, FOR_SAVE, mc->private.msg.env); + if (rule){ + folder_to_save = cpystr(rule->result); + n = rule->number; + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + } + + if (folder_to_save && *folder_to_save){ + RULELIST *list = get_rulelist_from_code(V_SAVE_RULES, + ps_global->rule_list); + RULE_S *prule = get_rule(list, n); + if (condition_contains_token(prule->condition, "_FLAG_") + || (mc->valid && mc->seen && !mc->deleted) + || (!mc->valid && mc->searched)) + save_folder = cpystr(folder_to_save); + else + save_folder = NULL; + } + else + if (!mc || (mc->seen && !mc->deleted)) + save_folder = cpystr(dstfldr); + mn_give(&msgmap); + rule_curpos = 0L; + return save_folder; + } + + MAILSTREAM * + find_open_stream(void) + { + return saved_stream; + } + + unsigned long + rules_cursor_pos(MAILSTREAM *stream) + { + MSGNO_S *msgmap = sp_msgmap(stream); + return rule_curpos != 0L ? rule_curpos : mn_m2raw(msgmap,mn_get_cur(msgmap)); + } + + void + setup_threading_index_style(void) + { + RULE_RESULT *rule; + NAMEVAL_S *v; + int i; + + rule = get_result_rule(V_THREAD_INDEX_STYLE_RULES, FOR_THREAD, NULL); + if (rule || ps_global->VAR_THREAD_INDEX_STYLE){ + for(i = 0; v = thread_index_styles(i); i++) + if(!strucmp(rule ? rule->result : ps_global->VAR_THREAD_INDEX_STYLE, + rule ? (v ? v->name : "" ) : S_OR_L(v))){ + ps_global->thread_index_style = v->value; + break; + } + if (rule){ + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + } + } + + unsigned + get_perfolder_startup_rule(MAILSTREAM *stream, int rule_type, char *folder) + { + unsigned startup_rule; + char *rule_result; + + startup_rule = reset_startup_rule(stream); + rule_result = get_rule_result(FOR_STARTUP, folder, rule_type); + if (rule_result && *rule_result){ + int i; + NAMEVAL_S *v; + + for(i = 0; v = incoming_startup_rules(i); i++) + if(!strucmp(rule_result, v->name)){ + startup_rule = v->value; + break; + } + fs_give((void **)&rule_result); + } + return startup_rule; + } + + void + find_startup_position(int rule, MAILSTREAM *m, long pc) + { + long n; + switch(rule){ + /* + * For news in incoming collection we're doing the same thing + * for first-unseen and first-recent. In both those cases you + * get first-unseen if FAKE_NEW is off and first-recent if + * FAKE_NEW is on. If FAKE_NEW is on, first unseen is the + * same as first recent because all recent msgs are unseen + * and all unrecent msgs are seen (see pine_mail_open). + */ + case IS_FIRST_UNSEEN: + first_unseen: + mn_set_cur(ps_global->msgmap, + (sp_first_unseen(m) + && mn_get_sort(ps_global->msgmap) == SortArrival + && !mn_get_revsort(ps_global->msgmap) + && !get_lflag(ps_global->mail_stream, NULL, + sp_first_unseen(m), MN_EXLD) + && (n = mn_raw2m(ps_global->msgmap, + sp_first_unseen(m)))) + ? n + : first_sorted_flagged(F_UNSEEN | F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID)); + break; + + case IS_FIRST_RECENT: + first_recent: + /* + * We could really use recent for news but this is the way + * it has always worked, so we'll leave it. That is, if + * the FAKE_NEW feature is on, recent and unseen are + * equivalent, so it doesn't matter. If the feature isn't + * on, all the undeleted messages are unseen and we start + * at the first one. User controls with the FAKE_NEW feature. + */ + if(IS_NEWS(ps_global->mail_stream)){ + mn_set_cur(ps_global->msgmap, + first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID)); + } + else{ + mn_set_cur(ps_global->msgmap, + first_sorted_flagged(F_RECENT | F_UNSEEN + | F_UNDEL, + m, pc, + THREADING() ? 0 : FSF_SKIP_CHID)); + } + break; + + case IS_FIRST_IMPORTANT: + mn_set_cur(ps_global->msgmap, + first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID)); + break; + + case IS_FIRST_IMPORTANT_OR_UNSEEN: + + if(IS_NEWS(ps_global->mail_stream)) + goto first_unseen; + + { + MsgNo flagged, first_unseen; + + flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID); + first_unseen = (sp_first_unseen(m) + && mn_get_sort(ps_global->msgmap) == SortArrival + && !mn_get_revsort(ps_global->msgmap) + && !get_lflag(ps_global->mail_stream, NULL, + sp_first_unseen(m), MN_EXLD) + && (n = mn_raw2m(ps_global->msgmap, + sp_first_unseen(m)))) + ? n + : first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID); + mn_set_cur(ps_global->msgmap, + (MsgNo) MIN((int) flagged, (int) first_unseen)); + + } + + break; + + case IS_FIRST_IMPORTANT_OR_RECENT: + + if(IS_NEWS(ps_global->mail_stream)) + goto first_recent; + + { + MsgNo flagged, first_recent; + + flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID); + first_recent = first_sorted_flagged(F_RECENT | F_UNSEEN + | F_UNDEL, + m, pc, + THREADING() ? 0 : FSF_SKIP_CHID); + mn_set_cur(ps_global->msgmap, + (MsgNo) MIN((int) flagged, (int) first_recent)); + } + + break; + + case IS_FIRST: + mn_set_cur(ps_global->msgmap, + first_sorted_flagged(F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID)); + break; + + case IS_LAST: + mn_set_cur(ps_global->msgmap, + first_sorted_flagged(F_UNDEL, m, pc, + FSF_LAST | (THREADING() ? 0 : FSF_SKIP_CHID))); + break; + + default: + panic("Unexpected incoming startup case"); + break; + + } + } diff -rc alpine-2.00/pith/mailcmd.h alpine-2.00.I.USE/pith/mailcmd.h *** alpine-2.00/pith/mailcmd.h 2008-07-09 22:01:13.000000000 -0700 --- alpine-2.00.I.USE/pith/mailcmd.h 2011-02-07 20:33:44.000000000 -0800 *************** *** 41,46 **** --- 41,48 ---- #define DB_FROMTAB 0x02 /* opening because of TAB command */ #define DB_INBOXWOCNTXT 0x04 /* interpret inbox as one true inbox */ + static MAILSTREAM *saved_stream; + static unsigned long rule_curpos = 0L; /* * generic "is aggregate message command?" test *************** *** 53,58 **** --- 55,61 ---- void bogus_utf8_command(char *, char *); int can_set_flag(struct pine *, char *, int); void cmd_cancelled(char *); + void cmd_quota(struct pine *); int cmd_delete(struct pine *, MSGNO_S *, int, char *(*)(struct pine *, MSGNO_S *)); int cmd_undelete(struct pine *, MSGNO_S *, int); int cmd_expunge_work(MAILSTREAM *, MSGNO_S *); *************** *** 61,71 **** void expunge_and_close(MAILSTREAM *, char **, unsigned long); void agg_select_all(MAILSTREAM *, MSGNO_S *, long *, int); char *move_read_msgs(MAILSTREAM *, char *, char *, size_t, long); char *move_read_incoming(MAILSTREAM *, CONTEXT_S *, char *, char **, char *, size_t); void cross_delete_crossposts(MAILSTREAM *); long zoom_index(struct pine *, MAILSTREAM *, MSGNO_S *, int); int unzoom_index(struct pine *, MAILSTREAM *, MSGNO_S *); ! int agg_text_select(MAILSTREAM *, MSGNO_S *, char, int, int, char *, char *, SEARCHSET **); int agg_flag_select(MAILSTREAM *, int, int, SEARCHSET **); char *get_uname(char *, char *, int); --- 64,81 ---- void expunge_and_close(MAILSTREAM *, char **, unsigned long); void agg_select_all(MAILSTREAM *, MSGNO_S *, long *, int); char *move_read_msgs(MAILSTREAM *, char *, char *, size_t, long); + char *move_read_msgs_using_rules (MAILSTREAM *, char *, char *); + unsigned get_perfolder_startup_rule (MAILSTREAM *, int, char *); + void setup_threading_index_style (void); + void find_startup_position (int, MAILSTREAM *, long); + char *get_folder_to_save (MAILSTREAM *, long, char *); char *move_read_incoming(MAILSTREAM *, CONTEXT_S *, char *, char **, char *, size_t); + MAILSTREAM *find_open_stream (void); + unsigned long rules_cursor_pos (MAILSTREAM *); void cross_delete_crossposts(MAILSTREAM *); long zoom_index(struct pine *, MAILSTREAM *, MSGNO_S *, int); int unzoom_index(struct pine *, MAILSTREAM *, MSGNO_S *); ! int agg_text_select(MAILSTREAM *, MSGNO_S *, char, char *, int, int, char *, char *, SEARCHSET **); int agg_flag_select(MAILSTREAM *, int, int, SEARCHSET **); char *get_uname(char *, char *, int); diff -rc alpine-2.00/pith/mailindx.c alpine-2.00.I.USE/pith/mailindx.c *** alpine-2.00/pith/mailindx.c 2008-03-03 09:52:11.000000000 -0800 --- alpine-2.00.I.USE/pith/mailindx.c 2011-02-07 20:33:47.000000000 -0800 *************** *** 16,21 **** --- 16,22 ---- #include "../pith/headers.h" #include "../pith/mailindx.h" + #include "../pith/pineelt.h" #include "../pith/mailview.h" #include "../pith/flag.h" #include "../pith/icache.h" *************** *** 39,44 **** --- 40,46 ---- #include "../pith/send.h" #include "../pith/options.h" #include "../pith/ablookup.h" + #include "../pith/rules.h" #ifdef _WINDOWS #include "../pico/osdep/mswin.h" #endif *************** *** 103,109 **** void set_print_format(IELEM_S *, int, int); void set_ielem_widths_in_field(IFIELD_S *); - #define BIGWIDTH 2047 --- 105,110 ---- *************** *** 227,232 **** --- 228,234 ---- case iSTime: case iKSize: case iSize: + case iSizeThread: case iPrioAlpha: (*answer)[column].req_width = 7; break; *************** *** 291,296 **** --- 293,301 ---- case iSDate: case iSDateTime: case iSDateTime24: + case iPrefDate: + case iPrefTime: + case iPrefDateTime: { /* * Format a date to see how long it is. *************** *** 299,312 **** * of the translated yesterdays and friends but... */ struct tm tm; char ss[100]; memset(&tm, 0, sizeof(tm)); tm.tm_year = 106; tm.tm_mon = 11; tm.tm_mday = 31; ! our_strftime(ss, sizeof(ss), "%x", &tm); ! (*answer)[column].req_width = MIN(MAX(9, utf8_width(ss)), 20); } (*answer)[column].monabb_width = monabb_width; --- 304,332 ---- * of the translated yesterdays and friends but... */ struct tm tm; + int len = 20; char ss[100]; memset(&tm, 0, sizeof(tm)); tm.tm_year = 106; tm.tm_mon = 11; tm.tm_mday = 31; ! tm.tm_hour = 3; ! tm.tm_min = 23; ! tm.tm_wday = 3; ! switch((*answer)[column].ctype){ ! case iPrefTime: ! our_strftime(ss, sizeof(ss), "%X", &tm); ! break; ! case iPrefDateTime: ! our_strftime(ss, sizeof(ss), "%c", &tm); ! len = 32; ! break; ! default: ! our_strftime(ss, sizeof(ss), "%x", &tm); ! break; ! } ! (*answer)[column].req_width = MIN(MAX(9, utf8_width(ss)), len); } (*answer)[column].monabb_width = monabb_width; *************** *** 355,360 **** --- 375,387 ---- PAT_STATE pstate; PAT_S *pat; int we_set_it = 0; + char *rule; + + if(rule = get_rule_result(FOR_INDEX, ps_global->cur_folder, V_INDEX_RULES)){ + init_index_format(rule, &ps_global->index_disp_format); + fs_give((void **)&rule); + return; + } if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){ for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){ *************** *** 428,441 **** static INDEX_PARSE_T itokens[] = { {"STATUS", iStatus, FOR_INDEX}, {"MSGNO", iMessNo, FOR_INDEX}, ! {"DATE", iDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"FROMORTO", iFromTo, FOR_INDEX}, {"FROMORTONOTNEWS", iFromToNotNews, FOR_INDEX}, {"SIZE", iSize, FOR_INDEX}, {"SIZECOMMA", iSizeComma, FOR_INDEX}, {"SIZENARROW", iSizeNarrow, FOR_INDEX}, {"KSIZE", iKSize, FOR_INDEX}, ! {"SUBJECT", iSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"FULLSTATUS", iFStatus, FOR_INDEX}, {"IMAPSTATUS", iIStatus, FOR_INDEX}, {"SHORTIMAPSTATUS", iSIStatus, FOR_INDEX}, --- 455,469 ---- static INDEX_PARSE_T itokens[] = { {"STATUS", iStatus, FOR_INDEX}, {"MSGNO", iMessNo, FOR_INDEX}, ! {"DATE", iDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, {"FROMORTO", iFromTo, FOR_INDEX}, {"FROMORTONOTNEWS", iFromToNotNews, FOR_INDEX}, {"SIZE", iSize, FOR_INDEX}, {"SIZECOMMA", iSizeComma, FOR_INDEX}, + {"SIZETHREAD", iSizeThread, FOR_INDEX}, {"SIZENARROW", iSizeNarrow, FOR_INDEX}, {"KSIZE", iKSize, FOR_INDEX}, ! {"SUBJECT", iSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_TRIM}, {"FULLSTATUS", iFStatus, FOR_INDEX}, {"IMAPSTATUS", iIStatus, FOR_INDEX}, {"SHORTIMAPSTATUS", iSIStatus, FOR_INDEX}, *************** *** 444,498 **** {"SUBJECTTEXT", iSubjectText, FOR_INDEX}, {"SUBJKEYTEXT", iSubjKeyText, FOR_INDEX}, {"SUBJKEYINITTEXT", iSubjKeyInitText, FOR_INDEX}, ! {"OPENINGTEXT", iOpeningText, FOR_INDEX}, ! {"OPENINGTEXTNQ", iOpeningTextNQ, FOR_INDEX}, ! {"KEY", iKey, FOR_INDEX}, ! {"KEYINIT", iKeyInit, FOR_INDEX}, {"DESCRIPSIZE", iDescripSize, FOR_INDEX}, {"ATT", iAtt, FOR_INDEX}, {"SCORE", iScore, FOR_INDEX}, {"PRIORITY", iPrio, FOR_INDEX}, {"PRIORITYALPHA", iPrioAlpha, FOR_INDEX}, ! {"PRIORITY!", iPrioBang, FOR_INDEX}, ! {"LONGDATE", iLDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SHORTDATE1", iS1Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SHORTDATE2", iS2Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SHORTDATE3", iS3Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SHORTDATE4", iS4Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"DATEISO", iDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SHORTDATEISO", iDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATE", iSDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTTIME", iSTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATEISO", iSDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATESHORTISO",iSDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATES1", iSDateS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATES2", iSDateS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATES3", iSDateS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATES4", iSDateS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIME", iSDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMEISO",iSDateTimeIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMESHORTISO",iSDateTimeIsoS,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMES1", iSDateTimeS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMES2", iSDateTimeS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMES3", iSDateTimeS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMES4", iSDateTimeS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIME24", iSDateTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMEISO24", iSDateTimeIso24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMESHORTISO24",iSDateTimeIsoS24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMES124", iSDateTimeS124, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMES224", iSDateTimeS224, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMES324", iSDateTimeS324, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMES424", iSDateTimeS424, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"TIME24", iTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"TIME12", iTime12, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"TIMEZONE", iTimezone, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"MONTHABBREV", iMonAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"DAYOFWEEKABBREV", iDayOfWeekAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"DAYOFWEEK", iDayOfWeek, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"FROM", iFrom, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"TO", iTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SENDER", iSender, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"CC", iCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"RECIPS", iRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"NEWS", iNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"TOANDNEWS", iToAndNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, --- 472,529 ---- {"SUBJECTTEXT", iSubjectText, FOR_INDEX}, {"SUBJKEYTEXT", iSubjKeyText, FOR_INDEX}, {"SUBJKEYINITTEXT", iSubjKeyInitText, FOR_INDEX}, ! {"OPENINGTEXT", iOpeningText, FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_TRIM}, ! {"OPENINGTEXTNQ", iOpeningTextNQ, FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_TRIM}, ! {"KEY", iKey, FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_COMPOSE}, ! {"KEYINIT", iKeyInit, FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_COMPOSE}, {"DESCRIPSIZE", iDescripSize, FOR_INDEX}, {"ATT", iAtt, FOR_INDEX}, {"SCORE", iScore, FOR_INDEX}, {"PRIORITY", iPrio, FOR_INDEX}, {"PRIORITYALPHA", iPrioAlpha, FOR_INDEX}, ! {"PRIORITY!", iPrioBang, FOR_INDEX}, ! {"LONGDATE", iLDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SHORTDATE1", iS1Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SHORTDATE2", iS2Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SHORTDATE3", iS3Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SHORTDATE4", iS4Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"DATEISO", iDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SHORTDATEISO", iDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATE", iSDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTTIME", iSTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATEISO", iSDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATESHORTISO",iSDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATES1", iSDateS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATES2", iSDateS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATES3", iSDateS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATES4", iSDateS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIME", iSDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMEISO",iSDateTimeIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMESHORTISO",iSDateTimeIsoS,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMES1", iSDateTimeS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMES2", iSDateTimeS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMES3", iSDateTimeS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMES4", iSDateTimeS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIME24", iSDateTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMEISO24", iSDateTimeIso24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMESHORTISO24",iSDateTimeIsoS24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMES124", iSDateTimeS124, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMES224", iSDateTimeS224, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMES324", iSDateTimeS324, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMES424", iSDateTimeS424, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"TIME24", iTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"TIME12", iTime12, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"TIMEZONE", iTimezone, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"MONTHABBREV", iMonAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"DAYOFWEEKABBREV", iDayOfWeekAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"DAYOFWEEK", iDayOfWeek, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"FROM", iFrom, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_COMPOSE}, ! {"TO", iTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_COMPOSE}, ! {"SENDER", iSender, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"CC", iCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_SAVE|FOR_SAVE}, ! {"ADDRESSTO", iAddressTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"ADDRESSCC", iAddressCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"ADDRESSRECIPS", iAddressRecip, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, {"RECIPS", iRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"NEWS", iNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"TOANDNEWS", iToAndNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, *************** *** 501,559 **** {"NEWSANDRECIPS", iNewsAndRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"MSGID", iMsgID, FOR_REPLY_INTRO|FOR_TEMPLATE}, {"CURNEWS", iCurNews, FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"DAYDATE", iRDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"PREFDATE", iPrefDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"PREFTIME", iPrefTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"PREFDATETIME", iPrefDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"DAY", iDay, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"DAYORDINAL", iDayOrdinal, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"DAY2DIGIT", iDay2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"MONTHLONG", iMonLong, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"MONTH", iMon, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"MONTH2DIGIT", iMon2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"YEAR", iYear, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"YEAR2DIGIT", iYear2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"ADDRESS", iAddress, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"MAILBOX", iMailbox, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"ROLENICK", iRoleNick, FOR_REPLY_INTRO|FOR_TEMPLATE}, {"INIT", iInit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"CURDATE", iCurDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURDATEISO", iCurDateIso, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURDATEISOS", iCurDateIsoS, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURTIME24", iCurTime24, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURTIME12", iCurTime12, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURDAY", iCurDay, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURDAY2DIGIT", iCurDay2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURDAYOFWEEK", iCurDayOfWeek, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, {"CURDAYOFWEEKABBREV", iCurDayOfWeekAbb, ! FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURMONTH", iCurMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURMONTH2DIGIT", iCurMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURMONTHLONG", iCurMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURMONTHABBREV", iCurMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURYEAR", iCurYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURYEAR2DIGIT", iCurYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURPREFDATE", iCurPrefDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURPREFTIME", iCurPrefTime, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, {"CURPREFDATETIME", iCurPrefDateTime, ! FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"LASTMONTH", iLstMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"LASTMONTH2DIGIT", iLstMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"LASTMONTHLONG", iLstMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"LASTMONTHABBREV", iLstMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"LASTMONTHYEAR", iLstMonYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, {"LASTMONTHYEAR2DIGIT", iLstMonYear2Digit, ! FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"LASTYEAR", iLstYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"LASTYEAR2DIGIT", iLstYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, {"HEADER", iHeader, FOR_INDEX}, {"TEXT", iText, FOR_INDEX}, {"ARROW", iArrow, FOR_INDEX}, {"NEWLINE", iNewLine, FOR_REPLY_INTRO}, {"CURSORPOS", iCursorPos, FOR_TEMPLATE}, {NULL, iNothing, FOR_NOTHING} }; INDEX_PARSE_T * itoken(int i) { --- 532,613 ---- {"NEWSANDRECIPS", iNewsAndRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"MSGID", iMsgID, FOR_REPLY_INTRO|FOR_TEMPLATE}, {"CURNEWS", iCurNews, FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"DAYDATE", iRDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"PREFDATE", iPrefDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"PREFTIME", iPrefTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"PREFDATETIME", iPrefDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"DAY", iDay, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"DAYORDINAL", iDayOrdinal, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"DAY2DIGIT", iDay2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"MONTHLONG", iMonLong, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"MONTH", iMon, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"MONTH2DIGIT", iMon2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"YEAR", iYear, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"YEAR2DIGIT", iYear2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"ADDRESS", iAddress, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE}, {"MAILBOX", iMailbox, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"ROLENICK", iRoleNick, FOR_REPLY_INTRO|FOR_TEMPLATE}, {"INIT", iInit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"CURDATE", iCurDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURDATEISO", iCurDateIso, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURDATEISOS", iCurDateIsoS, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURTIME24", iCurTime24, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURTIME12", iCurTime12, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURDAY", iCurDay, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURDAY2DIGIT", iCurDay2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURDAYOFWEEK", iCurDayOfWeek, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, {"CURDAYOFWEEKABBREV", iCurDayOfWeekAbb, ! FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURMONTH", iCurMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURMONTH2DIGIT", iCurMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURMONTHLONG", iCurMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURMONTHABBREV", iCurMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURYEAR", iCurYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURYEAR2DIGIT", iCurYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURPREFDATE", iCurPrefDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURPREFTIME", iCurPrefTime, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, {"CURPREFDATETIME", iCurPrefDateTime, ! FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"LASTMONTH", iLstMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"LASTMONTH2DIGIT", iLstMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"LASTMONTHLONG", iLstMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"LASTMONTHABBREV", iLstMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"LASTMONTHYEAR", iLstMonYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, {"LASTMONTHYEAR2DIGIT", iLstMonYear2Digit, ! FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"LASTYEAR", iLstYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"LASTYEAR2DIGIT", iLstYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, {"HEADER", iHeader, FOR_INDEX}, {"TEXT", iText, FOR_INDEX}, {"ARROW", iArrow, FOR_INDEX}, {"NEWLINE", iNewLine, FOR_REPLY_INTRO}, {"CURSORPOS", iCursorPos, FOR_TEMPLATE}, + {"NICK", iNick, FOR_RULE|FOR_SAVE}, + {"FOLDER", iFolder, FOR_RULE|FOR_SAVE|FOR_FOLDER}, + {"ROLE", iRole, FOR_RULE|FOR_RESUB|FOR_TRIM|FOR_TEMPLATE}, + {"PROCID", iProcid, FOR_RULE|FOR_RESUB|FOR_FLAG|FOR_COMPOSE|FOR_TRIM|FOR_TEMPLATE}, + {"PKEY", iPkey, FOR_RULE|FOR_KEY}, + {"SCREEN", iScreen, FOR_RULE|FOR_KEY}, + {"FLAG", iFlag, FOR_RULE|FOR_SAVE|FOR_FLAG}, + {"COLLECTION", iCollection, FOR_RULE|FOR_SAVE|FOR_COMPOSE|FOR_FOLDER}, + {"BCC", iBcc, FOR_COMPOSE|FOR_RULE}, + {"LCC", iLcc, FOR_COMPOSE|FOR_RULE}, + {"FORWARDFROM", iFfrom, FOR_COMPOSE|FOR_RULE}, + {"FORWARDADDRESS", iFadd, FOR_COMPOSE|FOR_RULE}, {NULL, iNothing, FOR_NOTHING} }; + INDEX_PARSE_T itokensinv[sizeof(itokens)/sizeof(itokens[0])]; + + void + inverse_itokens(void) + { + INDEX_PARSE_T *pt; + for (pt = itokens; pt->name; pt++) + itokensinv[pt->ctype].ctype = pt - itokens; + + } + INDEX_PARSE_T * itoken(int i) { *************** *** 913,919 **** iSDateTimeS1, iSDateTimeS2, iSDateTimeS3, iSDateTimeS4, iSDateTimeIso24, iSDateTimeIsoS24, iSDateTimeS124, iSDateTimeS224, iSDateTimeS324, iSDateTimeS424, ! iSize, iSizeComma, iSizeNarrow, iKSize, iDescripSize, iPrio, iPrioBang, iPrioAlpha, iAtt, iTime24, iTime12, iTimezone, iMonAbb, iYear, iYear2Digit, iDay2Digit, iMon2Digit, iDayOfWeekAbb, iScore, iMonLong, iDayOfWeek --- 967,973 ---- iSDateTimeS1, iSDateTimeS2, iSDateTimeS3, iSDateTimeS4, iSDateTimeIso24, iSDateTimeIsoS24, iSDateTimeS124, iSDateTimeS224, iSDateTimeS324, iSDateTimeS424, ! iSize, iSizeComma, iSizeNarrow, iKSize, iDescripSize, iSizeThread, iPrio, iPrioBang, iPrioAlpha, iAtt, iTime24, iTime12, iTimezone, iMonAbb, iYear, iYear2Digit, iDay2Digit, iMon2Digit, iDayOfWeekAbb, iScore, iMonLong, iDayOfWeek *************** *** 1105,1110 **** --- 1159,1165 ---- case iTime12: case iSize: case iKSize: + case iSizeThread: cdesc->actual_length = 7; cdesc->adjustment = Right; break; *************** *** 1198,1204 **** cdesc->ctype != iNothing; cdesc++) if(cdesc->ctype == iSize || cdesc->ctype == iKSize || ! cdesc->ctype == iSizeNarrow || cdesc->ctype == iSizeComma || cdesc->ctype == iDescripSize){ if(cdesc->actual_length == 0){ if((fix=cdesc->width) > 0){ /* had this reserved */ --- 1253,1259 ---- cdesc->ctype != iNothing; cdesc++) if(cdesc->ctype == iSize || cdesc->ctype == iKSize || ! cdesc->ctype == iSizeNarrow || cdesc->ctype == iSizeThread || cdesc->ctype == iSizeComma || cdesc->ctype == iDescripSize){ if(cdesc->actual_length == 0){ if((fix=cdesc->width) > 0){ /* had this reserved */ *************** *** 1581,1590 **** /* find next thread which is visible */ do{ if(mn_get_revsort(msgmap) && thrd->prevthd) thrd = fetch_thread(stream, thrd->prevthd); ! else if(!mn_get_revsort(msgmap) && thrd->nextthd) ! thrd = fetch_thread(stream, thrd->nextthd); else thrd = NULL; } while(thrd --- 1636,1647 ---- /* find next thread which is visible */ do{ + unsigned long branch; if(mn_get_revsort(msgmap) && thrd->prevthd) thrd = fetch_thread(stream, thrd->prevthd); ! /*branch = get_branch(stream,thrd)*/ ! else if(!mn_get_revsort(msgmap) && thrd->branch) ! thrd = fetch_thread(stream, thrd->branch); else thrd = NULL; } while(thrd *************** *** 1970,1975 **** --- 2027,2033 ---- ICE_S *ice, **icep; IFIELD_S *ifield; IELEM_S *ielem; + COLOR_PAIR *color = NULL; struct variable *vars = ps_global->vars; dprint((8, "=== format_index_line(msgno=%ld,rawno=%ld) ===\n", *************** *** 1995,2007 **** */ ice = copy_ice(ice); /* is this a collapsed thread index line? */ ! if(!idata->bogus && THREADING()){ ! thrd = fetch_thread(idata->stream, idata->rawno); ! collapsed = thrd && thrd->next ! && get_lflag(idata->stream, NULL, ! idata->rawno, MN_COLL); ! } /* calculate contents of the required fields */ for(cdesc = ps_global->index_disp_format; cdesc->ctype != iNothing; cdesc++) --- 2053,2062 ---- */ ice = copy_ice(ice); + thrd = fetch_thread(idata->stream, idata->rawno); /* is this a collapsed thread index line? */ ! if(!idata->bogus && THREADING()) ! collapsed = thrd && thread_is_kolapsed(ps_global, idata->stream, ps_global->msgmap, idata->rawno); /* calculate contents of the required fields */ for(cdesc = ps_global->index_disp_format; cdesc->ctype != iNothing; cdesc++) *************** *** 2433,2438 **** --- 2488,2511 ---- from_str(cdesc->ctype, idata, str, sizeof(str), ice); break; + case iAddressTo: + case iAddressCc: + case iAddressRecip: + {ENVELOPE *env; + int we_clear; + env = rules_fetchenvelope(idata, &we_clear); + sprintf(str, "%-*.*s", ifield->width, ifield->width, + detoken_src((cdesc->ctype == iAddressTo + ? "_ADDRESSTO_" + : (cdesc->ctype == iAddressCc + ? "_ADRESSCC_" + : "_ADRESSRECIPS_")), FOR_INDEX, + env, NULL, NULL, NULL)); + if(we_clear) + mail_free_envelope(&env); + } + break; + case iTo: if(((field = ((addr = fetch_to(idata)) ? "To" *************** *** 2499,2505 **** --- 2572,2601 ---- break; + case iSizeThread: + if (!THREADING()){ + goto getsize; + } else if (collapsed){ + l = count_flags_in_thread(idata->stream, thrd, F_NONE); + snprintf(str, sizeof(str), "(%lu)", l); + } + else{ + thrd = fetch_thread(idata->stream, idata->rawno); + if(!thrd) + snprintf(str, sizeof(str), "%s", "Error"); + else{ + long lengthb; + lengthb = get_length_branch(idata->stream, idata->rawno); + if (lengthb > 0L) + snprintf(str, sizeof(str), "(%lu)", lengthb); + else + snprintf(str,sizeof(str), "%s", " "); + } + } + break; + case iSize: + getsize: /* 0 ... 9999 */ if((l = fetch_size(idata)) < 10*1000L) snprintf(str, sizeof(str), "(%lu)", l); *************** *** 2753,2759 **** if(first_text){ strncpy(str, first_text, BIGWIDTH); str[BIGWIDTH] = '\0'; - fs_give((void **) &first_text); } } --- 2849,2854 ---- *************** *** 2913,2918 **** --- 3008,3021 ---- if(!ifield->ielem){ ielem = new_ielem(&ifield->ielem); + if(color = hdr_color(itokens[itokensinv[cdesc->ctype].ctype].name, NULL, ps_global->index_token_colors)){ + if(pico_usingcolor()){ + ielem->color = new_color_pair(color->fg, color->bg); + ielem->type = eTypeCol; + } + free_color_pair(&color); + } + ielem->freedata = 1; ielem->data = cpystr(str); ielem->datalen = strlen(str); *************** *** 3670,3675 **** --- 3773,3829 ---- gf_io_t pc; long partial_fetch_len = 0L; SEARCHSET *ss, **sset; + MESSAGECACHE *mc; + PINELT_S *pelt; + + /* we cache the result we get from this function, so that we do not have to + * refetch the text in case there is a change. We could cache in the envelope + * but c-client does not have a special field for that, nor we want to use the + * sparep pointer, since there could be other uses for sparep later, and even + * if we add a pointer to the ENVELOPE structure, we would be caching the same + * text twice (one in a private pointer, and the new pointer) and that would + * not make sense. Instead we will use an elt for this + */ + + if((mc = mail_elt(idata->stream, idata->rawno)) + && ((pelt = (PINELT_S *) mc->sparep) == NULL)){ + pelt = (PINELT_S *) fs_get(sizeof(PINELT_S)); + memset(pelt, 0, sizeof(PINELT_S)); + } + + /* If there were no rules, we would just return the firsttext + * as cached in the pelt pointer, but if we are here and there + * is a rule, and the firsttext that is cached changes as a + * result of the rule, we need to refetch the text and process it + * again + */ + + if(pelt && pelt->firsttextraw != NULL){ + char buf[4*1024+1], ftext[6*1024+1], *rule_result; + strncpy(buf, pelt->firsttextraw, sizeof(buf)); + buf[sizeof(buf)-1] = '\0'; + if(rule_result = find_value((delete_quotes + ? "_OPENINGTEXTNQ_" : "_OPENINGTEXT_"), + buf, PROCESS_SP, idata, 4)){ + collspaces(rule_result); + strncpy(buf, rule_result, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + fs_give((void **) &rule_result); + } + ftext[0] = '\0'; + iutf8ncpy(ftext, buf, sizeof(ftext)); + ftext[sizeof(ftext)-1] = '\0'; + removing_trailing_white_space(ftext); + if(!strcmp(ftext, pelt->firsttext)) /* same as last pass */ + return(pelt->firsttext); + if(strlen(pelt->firsttextraw) > 256 /* not the same, but long enough */ + && utf8_width(ftext) >= 50){ + fs_give((void **)&pelt->firsttext); + pelt->firsttext = cpystr(ftext); + return pelt->firsttext; + } + fs_give((void **)&pelt->firsttext); /* do it again */ + } try_again: *************** *** 3706,3712 **** && ALLOWED_SUBTYPE(subtype))){ if((so = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){ ! char buf[1025], *p; unsigned char c; int success; int one_space_done = 0; --- 3860,3866 ---- && ALLOWED_SUBTYPE(subtype))){ if((so = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){ ! char buf[4*1024+1], rawbuf[6*1024+1], *p; unsigned char c; int success; int one_space_done = 0; *************** *** 3763,3769 **** --- 3917,3939 ---- if(p > buf){ size_t l; + ENVELOPE *env; + char *rule_result; + if(pelt && pelt->firsttextraw) + fs_give((void **)&pelt->firsttextraw); + iutf8ncpy(rawbuf, buf, sizeof(rawbuf)); + rawbuf[sizeof(rawbuf)-1] = '\0'; + removing_trailing_white_space(rawbuf); + pelt->firsttextraw = cpystr(rawbuf); + if(rule_result = find_value((delete_quotes + ? "_OPENINGTEXTNQ_" : "_OPENINGTEXT_"), + buf, PROCESS_SP, idata, 4)){ + collspaces(rule_result); + strncpy(buf, rule_result, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + fs_give((void **) &rule_result); + } l = strlen(buf); l += 100; firsttext = fs_get((l+1) * sizeof(char)); *************** *** 3787,3792 **** --- 3957,3964 ---- goto try_again; } } + if(mc && pelt) + pelt->firsttext = firsttext; } } } *************** *** 4271,4276 **** --- 4443,4449 ---- tm.tm_mday = MIN(MAX(d.day, 1), 31); tm.tm_hour = MIN(MAX(d.hour, 0), 23); tm.tm_min = MIN(MAX(d.minute, 0), 59); + tm.tm_wday = MIN(MAX(d.wkday, 0), 6); tmptr = &tm; } *************** *** 5226,5235 **** { char *subject, *origsubj, *origstr, *rawsubj, *sptr = NULL; char *p, *border, *q = NULL, *free_subj = NULL; ! char *sp; size_t len; int width = -1; ! int depth = 0, mult = 2; int save; int do_subj = 0, truncated_tree = 0; PINETHRD_S *thd, *thdorig; --- 5399,5408 ---- { char *subject, *origsubj, *origstr, *rawsubj, *sptr = NULL; char *p, *border, *q = NULL, *free_subj = NULL; ! char *sp, *rule_result; size_t len; int width = -1; ! int depth = 0, mult = 2, collapsed, i, we_clear = 0; int save; int do_subj = 0, truncated_tree = 0; PINETHRD_S *thd, *thdorig; *************** *** 5283,5289 **** * origsubj is the original subject but it has been decoded. We need * to free it at the end of this routine. */ ! /* * prepend_keyword will put the keyword stuff before the subject --- 5456,5468 ---- * origsubj is the original subject but it has been decoded. We need * to free it at the end of this routine. */ ! if (rule_result = find_value("_SUBJECT_", origsubj, PROCESS_SP, idata, 4)){ ! if(origsubj) ! fs_give((void **)&origsubj); ! we_clear++; ! origsubj = cpystr(rule_result); ! fs_give((void **)&rule_result); ! } /* * prepend_keyword will put the keyword stuff before the subject *************** *** 5371,5380 **** if(pith_opt_condense_thread_cue) width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width, ! thd && thd->next ! && get_lflag(idata->stream, ! NULL,idata->rawno, ! MN_COLL)); /* * width is < available strsize and --- 5550,5557 ---- if(pith_opt_condense_thread_cue) width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width, ! this_thread_is_kolapsed(ps_global, idata->stream, ps_global->msgmap, idata->rawno) && ! (count_thread(ps_global,idata->stream, ps_global->msgmap, idata->rawno) != 1)); /* * width is < available strsize and *************** *** 5724,5729 **** --- 5901,5909 ---- if(free_subj) fs_give((void **) &free_subj); + + if (we_clear && origsubj) + fs_give((void **)&origsubj); } *************** *** 6002,6012 **** border = str + width; if(pith_opt_condense_thread_cue) width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width, ! thd && thd->next ! && get_lflag(idata->stream, ! NULL,idata->rawno, ! MN_COLL)); ! fptr = str; if(thd) --- 6182,6189 ---- border = str + width; if(pith_opt_condense_thread_cue) width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width, ! this_thread_is_kolapsed(ps_global, idata->stream, ps_global->msgmap, idata->rawno) && ! (count_thread(ps_global,idata->stream, ps_global->msgmap, idata->rawno) != 1)); fptr = str; if(thd) *************** *** 6092,6107 **** ? "To" : (addr = fetch_cc(idata)) ? "Cc" ! : NULL)) ! && set_index_addr(idata, field, addr, "To: ", ! strsize-1, fptr)) ! break; if(ctype == iFromTo && (newsgroups = fetch_newsgroups(idata)) && *newsgroups){ ! snprintf(fptr, strsize, "To: %-*.*s", strsize-1-4, strsize-1-4, ! newsgroups); break; } --- 6269,6301 ---- ? "To" : (addr = fetch_cc(idata)) ? "Cc" ! : NULL))){ ! char *rule_result; ! rule_result = find_value("_FROM_", NULL, 0, idata, 1); ! if (!rule_result) ! set_index_addr(idata, field, addr, "To: ", ! strsize-1, fptr); ! else{ ! sprintf(str, "%-*.*s", strsize-1, strsize-1, ! rule_result); ! fs_give((void **)&rule_result); ! } + break; + } if(ctype == iFromTo && (newsgroups = fetch_newsgroups(idata)) && *newsgroups){ ! char *rule_result; ! rule_result = find_value("_FROM_", NULL, 0, idata, 1); ! if (!rule_result) ! sprintf(str, "To: %-*.*s", strsize-1-4, ! strsize-1-4, newsgroups); ! else{ ! sprintf(str, "%-*.*s", strsize-1, strsize-1, ! rule_result); ! fs_give((void **)&rule_result); ! } break; } *************** *** 6114,6120 **** break; case iFrom: ! set_index_addr(idata, "From", fetch_from(idata), NULL, strsize-1, fptr); break; case iAddress: --- 6308,6322 ---- break; case iFrom: ! { char *rule_result; ! rule_result = find_value("_FROM_", NULL, 0, idata, 4); ! if (!rule_result) ! set_index_addr(idata, "From", fetch_from(idata), NULL, strsize-1, fptr); ! else{ ! sprintf(str, "%-*.*s", strsize-1, strsize-1, rule_result); ! fs_give((void **)&rule_result); ! } ! } break; case iAddress: *************** *** 6411,6413 **** --- 6613,6676 ---- } } } + + void + setup_threading_display_style(void) + { + RULE_RESULT *rule; + NAMEVAL_S *v; + int i; + + rule = get_result_rule(V_THREAD_DISP_STYLE_RULES, FOR_THREAD, NULL); + if (rule || ps_global->VAR_THREAD_DISP_STYLE){ + for(i = 0; v = thread_disp_styles(i); i++) + if(!strucmp(rule ? rule->result : ps_global->VAR_THREAD_DISP_STYLE, + rule ? (v ? v->name : "" ) : S_OR_L(v))){ + ps_global->thread_disp_style = v->value; + break; + } + if (rule){ + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + } + } + + char * + find_value(char *token, char *use_this, int flag, INDEXDATA_S *idata, int nfcn) + { + int n = 0, i, rule_context, we_clear; + char *rule_result = NULL, **list; + ENVELOPE *env; + RULELIST *rule; + RULE_S *prule; + + env = rules_fetchenvelope(idata, &we_clear); + if(env && env->sparep) + fs_give((void **)&env->sparep); + if(we_clear) + mail_free_envelope(&env); + if(rule = get_rulelist_from_code(V_REPLACE_RULES, ps_global->rule_list)){ + list = functions_for_token(token); + while(rule_result == NULL && (prule = get_rule(rule,n++))){ + rule_context = 0; + if (prule->action->token && !strcmp(prule->action->token, token)){ + for (i = 0; i < nfcn; i++) + if(list[i+1] && !strcmp(prule->action->function, list[i+1])) + rule_context |= context_for_function(list[i+1]); + if (rule_context){ + env = rules_fetchenvelope(idata, &we_clear); + if(use_this) + env->sparep = get_sparep_for_rule(use_this, flag); + rule_result = process_rule(prule, rule_context, env); + if(env->sparep) + free_sparep_for_rule(&env->sparep); + if(we_clear) + mail_free_envelope(&env); + } + } + } + } + return rule_result; + } diff -rc alpine-2.00/pith/mailindx.h alpine-2.00.I.USE/pith/mailindx.h *** alpine-2.00/pith/mailindx.h 2008-02-01 10:42:29.000000000 -0800 --- alpine-2.00.I.USE/pith/mailindx.h 2011-02-07 20:33:45.000000000 -0800 *************** *** 29,34 **** --- 29,37 ---- /* exported prototypes */ + SortOrder translate (char *, int); + char *find_value (char *, char *, int, INDEXDATA_S *, int); + void setup_threading_display_style (void); int msgline_hidden(MAILSTREAM *, MSGNO_S *, long, int); void adjust_cur_to_visible(MAILSTREAM *, MSGNO_S *); unsigned long line_hash(char *); *************** *** 36,41 **** --- 39,45 ---- void free_index_format(INDEX_COL_S **); void reset_index_format(void); INDEX_PARSE_T *itoktype(char *, int); + void inverse_itokens(void); char *prepend_keyword_subject(MAILSTREAM *, long, char *, SubjKW, IELEM_S **, char *); int get_index_line_color(MAILSTREAM *, SEARCHSET *, PAT_STATE **, COLOR_PAIR **); void setup_for_index_index_screen(void); diff -rc alpine-2.00/pith/mailview.c alpine-2.00.I.USE/pith/mailview.c *** alpine-2.00/pith/mailview.c 2008-07-11 16:20:32.000000000 -0700 --- alpine-2.00.I.USE/pith/mailview.c 2011-02-07 20:33:45.000000000 -0800 *************** *** 51,57 **** #include "../pith/escapes.h" #include "../pith/keyword.h" #include "../pith/smime.h" ! #define FBUF_LEN (50) --- 51,60 ---- #include "../pith/escapes.h" #include "../pith/keyword.h" #include "../pith/smime.h" ! #include "../pith/osdep/color.h" ! #include "../pico/estruct.h" ! #include "../pico/pico.h" ! #include "../pico/efunc.h" #define FBUF_LEN (50) *************** *** 281,289 **** if((flgs & FM_DISPLAY) && !(flgs & FM_NOCOLOR) && pico_usingcolor() && ps_global->VAR_SIGNATURE_FORE_COLOR && ps_global->VAR_SIGNATURE_BACK_COLOR){ ! gf_link_filter(gf_line_test, gf_line_test_opt(color_signature, &is_in_sig)); } if((flgs & FM_DISPLAY) --- 284,300 ---- if((flgs & FM_DISPLAY) && !(flgs & FM_NOCOLOR) && pico_usingcolor() + && ps_global->VAR_SPECIAL_TEXT_FORE_COLOR + && ps_global->VAR_SPECIAL_TEXT_BACK_COLOR){ + gf_link_filter(gf_line_test, gf_line_test_opt(color_this_text, NULL)); + } + + if((flgs & FM_DISPLAY) + && !(flgs & FM_NOCOLOR) + && pico_usingcolor() && ps_global->VAR_SIGNATURE_FORE_COLOR && ps_global->VAR_SIGNATURE_BACK_COLOR){ ! gf_link_filter(gf_quote_test, gf_line_test_opt(color_signature, &is_in_sig)); } if((flgs & FM_DISPLAY) *************** *** 291,298 **** && pico_usingcolor() && ps_global->VAR_QUOTE1_FORE_COLOR && ps_global->VAR_QUOTE1_BACK_COLOR){ ! gf_link_filter(gf_line_test, gf_line_test_opt(color_a_quote, NULL)); } if(!(flgs & FM_NOWRAP)){ wrapflags = (flgs & FM_DISPLAY) ? (GFW_HANDLES|GFW_SOFTHYPHEN) : GFW_NONE; --- 302,311 ---- && pico_usingcolor() && ps_global->VAR_QUOTE1_FORE_COLOR && ps_global->VAR_QUOTE1_BACK_COLOR){ ! gf_link_filter(gf_quote_test, gf_line_test_opt(color_a_quote, NULL)); } + else + gf_link_filter(gf_quote_test,gf_line_test_opt(select_quote, NULL)); if(!(flgs & FM_NOWRAP)){ wrapflags = (flgs & FM_DISPLAY) ? (GFW_HANDLES|GFW_SOFTHYPHEN) : GFW_NONE; *************** *** 1096,1122 **** color_signature(long int linenum, char *line, LT_INS_S **ins, void *is_in_sig) { struct variable *vars = ps_global->vars; ! int *in_sig_block; COLOR_PAIR *col = NULL; if(is_in_sig == NULL) return 0; in_sig_block = (int *) is_in_sig; ! if(!strcmp(line, SIGDASHES)) ! *in_sig_block = START_SIG_BLOCK; ! else if(*line == '\0') /* * Suggested by Eduardo: allow for a blank line right after * the sigdashes. */ *in_sig_block = (*in_sig_block == START_SIG_BLOCK) ? IN_SIG_BLOCK : OUT_SIG_BLOCK; else *in_sig_block = (*in_sig_block != OUT_SIG_BLOCK) ? IN_SIG_BLOCK : OUT_SIG_BLOCK; if(*in_sig_block != OUT_SIG_BLOCK && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR && (col = new_color_pair(VAR_SIGNATURE_FORE_COLOR, --- 1109,1197 ---- color_signature(long int linenum, char *line, LT_INS_S **ins, void *is_in_sig) { struct variable *vars = ps_global->vars; ! int *in_sig_block, i, j,same_qstr = 0, plb; COLOR_PAIR *col = NULL; + static char GLine[NSTRING] = {'\0'}; + static char PLine[NSTRING] = {'\0'}; + static char PPLine[NSTRING] = {'\0'}; + char NLine[NSTRING] = {'\0'}; + char rqstr[NSTRING] = {'\0'}; + char *p; + static char *buf, buf2[NSTRING] = {'\0'}; + QSTRING_S *qs; + static int qstrlen = 0; if(is_in_sig == NULL) return 0; + if (linenum > 0){ + strncpy(PLine, GLine, sizeof(PLine)); + PLine[sizeof(PLine)-1] = '\0'; + } + + if(p = strchr(tmp_20k_buf, '\015')) *p = '\0'; + strncpy(NLine, tmp_20k_buf, sizeof(NLine)); + NLine[sizeof(NLine) - 1] = '\0'; + if (p) *p = '\015'; + + strncpy(GLine, line, sizeof(GLine)); + GLine[sizeof(GLine) - 1] = '\0'; + + plb = line_isblank((ps_global->prefix && *ps_global->prefix + ? ps_global->prefix : ">"), PLine, GLine, PPLine, NSTRING); + qs = do_quote_match((ps_global->prefix && *ps_global->prefix + ? ps_global->prefix : ">"), + GLine, NLine, PLine, rqstr, NSTRING, plb); + if(linenum > 0) + strncpy(PPLine, PLine, NSTRING); + strncpy(buf2, rqstr, NSTRING); + i = buf2 && buf2[0] ? strlen(buf2) : 0; + free_qs(&qs); + + /* determine if buf and buf2 are the same quote string */ + if (!struncmp(buf, buf2, qstrlen)){ + for (j = qstrlen; buf2[j] && isspace((unsigned char)buf2[j]); j++); + if (!buf2[j] || buf2[j] == '|' || (buf2[j] == '*' && buf2[j+1] != '>')) + same_qstr++; + } + in_sig_block = (int *) is_in_sig; ! if (*in_sig_block != OUT_SIG_BLOCK){ ! if (line && *line && (strlen(line) >= qstrlen) && same_qstr) ! line += qstrlen; ! else if (strlen(line) < qstrlen) ! line += i; ! else if (!same_qstr) ! *in_sig_block = OUT_SIG_BLOCK; ! } ! else ! line += i; ! ! if(!strcmp(line, SIGDASHES) || !strcmp(line, "--")){ ! *in_sig_block = START_SIG_BLOCK; ! buf = (char *) fs_get((i + 1)*sizeof(char)); ! buf = cpystr(buf2); ! qstrlen = i; ! } ! else if(*line == '\0'){ /* * Suggested by Eduardo: allow for a blank line right after * the sigdashes. */ *in_sig_block = (*in_sig_block == START_SIG_BLOCK) ? IN_SIG_BLOCK : OUT_SIG_BLOCK; + } else *in_sig_block = (*in_sig_block != OUT_SIG_BLOCK) ? IN_SIG_BLOCK : OUT_SIG_BLOCK; + if (*in_sig_block == OUT_SIG_BLOCK){ + qstrlen = 0; /* reset back in case there's another paragraph */ + if (buf) + fs_give((void **)&buf); + } + if(*in_sig_block != OUT_SIG_BLOCK && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR && (col = new_color_pair(VAR_SIGNATURE_FORE_COLOR, *************** *** 1480,1497 **** return(0); } int url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local) { register char *lp, *up = NULL, *urlp = NULL, *weburlp = NULL, *mailurlp = NULL; ! int n, n1, n2, n3, l; char buf[256], color[256]; HANDLE_S *h; URL_HILITE_S *uh; ! for(lp = line; ; lp = up + n){ /* scan for all of them so we can choose the first */ if(F_ON(F_VIEW_SEL_URL,ps_global)) urlp = rfc1738_scan(lp, &n1); --- 1555,1632 ---- return(0); } + int + incomplete_url(char *up, int n, int delim) + { + char *line, *line2; + int rv = 0, len; + + if(*(up + n) != '\0') + return 0; + + if(delim > 0) + return 1; + + if(F_ON(F_VIEW_LONG_URL, ps_global)){ + line = up; + if(!strncmp(line, "http://", 7)) + line += 7; + else if(!strncmp(line, "https://", 8)) + line += 8; + if(strchr(line, '/') != NULL && (line = strrchr(line, '/')) != NULL){ + line++; + line2 = strrchr(line, '.'); + rv = (strpbrk(line,"+#?=&") != NULL) + || (!line2 || line-line2 > 4); + } + } + return rv; + } + int url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local) { register char *lp, *up = NULL, *urlp = NULL, *weburlp = NULL, *mailurlp = NULL; ! char *use_this_line, c, *begin_line, *end_line; ! static int scannextline, delim = -1; ! int n, n1, n2, n3, l, len; ! int we_clear = 0, newhandle = 1, tie_off = 0; char buf[256], color[256]; HANDLE_S *h; URL_HILITE_S *uh; ! uh = (URL_HILITE_S *) local; ! if((uh && uh->handlesp && ((h = *(uh->handlesp)) == NULL) || h->key == 0) || ! (!line || !*line) || linenum == 0) ! scannextline = 0; /* initialize scannextline */ ! ! if(scannextline != 0){ ! up = rfc1738_scan(line, &n1); ! ! /* if we found a url in the current line, but it is not at the beginning of ! * the next line, or if there is no url in this line, we check if the url ! * in the previous line continues in this line. ! */ ! ! if(line != up){ ! if(*uh->handlesp == NULL) ! h = new_handle(uh->handlesp); ! for(h = *uh->handlesp; h->next; h = h->next); /* get last handle */ ! len = h->h.url.path ? strlen(h->h.url.path) : 0; ! use_this_line = (char *) fs_get((len + strlen(line) + 1)*sizeof(char)); ! sprintf(use_this_line,"%s%s", (h->h.url.path ? h->h.url.path : ""), line); ! we_clear++; ! newhandle = 0; ! } ! else ! use_this_line = line; ! } ! else ! use_this_line = line; ! ! for(lp = use_this_line; ; lp = up + n){ /* scan for all of them so we can choose the first */ if(F_ON(F_VIEW_SEL_URL,ps_global)) urlp = rfc1738_scan(lp, &n1); *************** *** 1501,1506 **** --- 1636,1645 ---- mailurlp = mail_addr_scan(lp, &n3); if(urlp || weburlp || mailurlp){ + if(scannextline == 0){ + newhandle++; + delim = -1; + } up = urlp ? urlp : weburlp ? weburlp : mailurlp; if(up == urlp && weburlp && weburlp < up) *************** *** 1509,1515 **** --- 1648,1663 ---- up = mailurlp; if(up == urlp){ + if(delim < 0) + delim = up > use_this_line && *(up - 1) == '<'; n = n1; + if(incomplete_url(up,n, delim)) + scannextline++; + else{ + if(scannextline) + tie_off++; + scannextline = 0; + } weburlp = mailurlp = NULL; } else if(up == weburlp){ *************** *** 1526,1561 **** uh = (URL_HILITE_S *) local; ! h = new_handle(uh->handlesp); ! h->type = URL; ! h->h.url.path = (char *) fs_get((n + 10) * sizeof(char)); ! snprintf(h->h.url.path, n+10, "%s%.*s", weburlp ? "http://" : (mailurlp ? "mailto:" : ""), n, up); ! h->h.url.path[n+10-1] = '\0'; if(handle_start_color(color, sizeof(color), &l, uh->hdr_color)) ! ins = gf_line_test_new_ins(ins, up, color, l); else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)) ! ins = gf_line_test_new_ins(ins, up, url_embed(TAG_BOLDON), 2); buf[0] = TAG_EMBED; buf[1] = TAG_HANDLE; snprintf(&buf[3], sizeof(buf)-3, "%d", h->key); buf[sizeof(buf)-1] = '\0'; buf[2] = strlen(&buf[3]); ! ins = gf_line_test_new_ins(ins, up, buf, (int) buf[2] + 3); /* in case it was the current selection */ ! ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_INVOFF), 2); if(scroll_handle_end_color(color, sizeof(color), &l, uh->hdr_color)) ! ins = gf_line_test_new_ins(ins, up + n, color, l); else ! ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_BOLDOFF), 2); urlp = weburlp = mailurlp = NULL; } return(0); } --- 1674,1731 ---- uh = (URL_HILITE_S *) local; ! if(tie_off){ ! tie_off = 0; /* do only once */ ! begin_line = line; ! end_line = line + n - strlen(h->h.url.path); ! fs_give((void **)&h->h.url.path); ! c = *(use_this_line + n); ! *(use_this_line+n) = '\0'; ! h->h.url.path = cpystr(use_this_line); ! *(use_this_line+n) = c; ! } ! else{ ! if(newhandle){ ! h = new_handle(uh->handlesp); ! h->type = URL; ! } ! begin_line = newhandle ? (we_clear ? line + strlen(line) - strlen(up) ! : up) : line; ! end_line = newhandle ? begin_line + n : line + strlen(line); ! if(scannextline && h->h.url.path) ! fs_give((void **)&h->h.url.path); ! h->h.url.path = (char *) fs_get((n + 10) * sizeof(char)); ! snprintf(h->h.url.path, n+10, "%s%.*s", weburlp ? "http://" : (mailurlp ? "mailto:" : ""), n, up); ! h->h.url.path[n+10-1] = '\0'; ! } if(handle_start_color(color, sizeof(color), &l, uh->hdr_color)) ! ins = gf_line_test_new_ins(ins, begin_line, color, l); else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)) ! ins = gf_line_test_new_ins(ins, begin_line, url_embed(TAG_BOLDON), 2); buf[0] = TAG_EMBED; buf[1] = TAG_HANDLE; snprintf(&buf[3], sizeof(buf)-3, "%d", h->key); buf[sizeof(buf)-1] = '\0'; buf[2] = strlen(&buf[3]); ! ins = gf_line_test_new_ins(ins, begin_line, buf, (int) buf[2] + 3); /* in case it was the current selection */ ! ins = gf_line_test_new_ins(ins, end_line, url_embed(TAG_INVOFF), 2); if(scroll_handle_end_color(color, sizeof(color), &l, uh->hdr_color)) ! ins = gf_line_test_new_ins(ins, end_line, color, l); else ! ins = gf_line_test_new_ins(ins, end_line, url_embed(TAG_BOLDOFF), 2); urlp = weburlp = mailurlp = NULL; } + if(we_clear) + fs_give((void **)&use_this_line); + return(0); } *************** *** 1676,1681 **** --- 1846,1922 ---- } + /* This filter gives a quote string of a line. It sends its reply back to the + calling filter in the tmp_20k_buf variable. This filter replies with + the full quote string including tailing spaces if any. It is the + responsibility of the calling filter to figure out if thos spaces are + useful for that filter or if they should be removed before doing any + useful work. For example, color_a_quote does not require the trailing + spaces, but gf_wrap does. + */ + int + select_quote(long linenum, char *line, LT_INS_S **ins, void *local) + { + int i, plb, *code; + char rqstr[NSTRING] = {'\0'}, buf[NSTRING] = {'\0'}; + char GLine[NSTRING] = {'\0'}, PLine[NSTRING] = {'\0'}; + char PPLine[NSTRING] = {'\0'}, NLine[NSTRING] = {'\0'}; + static char GLine1[NSTRING] = {'\0'}; + static char PLine1[NSTRING] = {'\0'}; + static char PPLine1[NSTRING] = {'\0'}; + static char GLine2[NSTRING] = {'\0'}; + static char PLine2[NSTRING] = {'\0'}; + static char PPLine2[NSTRING] = {'\0'}; + QSTRING_S *qs; + int buflen = NSTRING < SIZEOF_20KBUF ? NSTRING - 1: SIZEOF_20KBUF - 1; + int who, raw; + + code = (int *)local; + who = code ? (*code & COLORAQUO) : 0; /* may I ask who is calling? */ + raw = code ? (*code & RAWSTRING) : 0; /* return raw string */ + strncpy(GLine, (who ? GLine1 : GLine2), buflen); + strncpy(PLine, (who ? PLine1 : PLine2), buflen); + strncpy(PPLine, (who ? PPLine1 : PPLine2), buflen); + + if (linenum > 0) + strncpy(PLine, GLine, buflen); + + strncpy(NLine, tmp_20k_buf, buflen); + + if (line) + strncpy(GLine, line, buflen); + else + GLine[0] = '\0'; + + + plb = line_isblank((ps_global->prefix && *ps_global->prefix + ? ps_global->prefix : ">"), PLine, GLine, PPLine, NSTRING); + + qs = do_quote_match((ps_global->prefix && *ps_global->prefix + ? ps_global->prefix : ">"), GLine, NLine, PLine, + rqstr, NSTRING, plb); + if (raw) + strncpy(buf, rqstr, NSTRING); + else + flatten_qstring(qs, buf, NSTRING); + free_qs(&qs); + + /* do not paint an extra level for a line with a >From string at the + * begining of it + */ + if (buf[0]){ + i = strlen(buf); + if (strlen(line) >= i + 6 && !strncmp(line+i-1,">From ", 6)) + buf[i - 1] = '\0'; + } + strncpy(tmp_20k_buf, buf, buflen); + if (linenum > 0) + strncpy((who ? PPLine1 : PPLine2), PLine, buflen); + strncpy((who ? GLine1 : GLine2), GLine, buflen); + strncpy((who ? PLine1 : PLine2), PLine, buflen); + return 1; + } + #define UES_LEN 12 #define UES_MAX 32 *************** *** 2377,2384 **** format_addr_string(s, n, sect, "Return-Path: ", e->return_path, flags, oacs, pc); ! if((which & FE_NEWSGROUPS) && e->newsgroups) format_newsgroup_string("Newsgroups: ", e->newsgroups, flags, pc); if((which & FE_FOLLOWUPTO) && e->followup_to) format_newsgroup_string("Followup-To: ", e->followup_to, flags, pc); --- 2618,2638 ---- format_addr_string(s, n, sect, "Return-Path: ", e->return_path, flags, oacs, pc); ! if((which & FE_NEWSGROUPS) && e->newsgroups){ ! int bogus = NIL; format_newsgroup_string("Newsgroups: ", e->newsgroups, flags, pc); + if (!e->ngpathexists && e->message_id && + strncmp (e->message_id,"message_id,"message_id,"message_id,"message_id,"message_id,"followup_to) format_newsgroup_string("Followup-To: ", e->followup_to, flags, pc); *************** *** 2488,2493 **** --- 2742,2962 ---- return(color_pair); } + void + interval_free(ival) + IVAL_S **ival; + { + if (!(*ival)) + return; + + if ((*ival)->next) + interval_free(&((*ival)->next)); + + fs_give((void **)(ival)); + } + + IVAL_S * + compute_interval (string, endm) + char *string; + int endm; + { + IVAL_S *ival = NULL, *nextival= NULL, *d; + regmatch_t pmatch; + + if(ps_global->paterror == 0) + if (regexec(&ps_global->colorpat, string, 1, &pmatch, 0) == 0){ + ival = (IVAL_S *) fs_get(sizeof(IVAL_S)); + memset (ival, 0, sizeof(IVAL_S)); + ival->start = endm + pmatch.rm_so; + ival->end = endm + pmatch.rm_eo; + nextival = compute_interval(string+pmatch.rm_so+1, ival->start+1); + if (nextival){ + if (nextival->start <= ival->end){ + ival->end = nextival->end; + d = nextival->next; + nextival->next = NULL; + ival->next = d; + interval_free(&nextival); + } + else + ival->next = nextival; + } + } + return ival; + } + + void + regex_pattern(plist) + char **plist; + { + int i = 0, j = 0, len = 0; + char *pattern = NULL; + regex_t preg; + + if(ps_global->paterror == 0) + regfree(&ps_global->colorpat); + + if(plist && *plist && *plist){ + for (i = 0; plist[i] && plist[i][0]; i++) + len += strlen(plist[i]) + 1; + pattern = (char *) fs_get(len * sizeof(char)); + *pattern = '\0'; + for (j = 0; j < i; j++){ + strcat(pattern, plist[j]); + strcat(pattern, (j < i - 1) ? "|" : ""); + } + if ((ps_global->paterror = regcomp(&preg, pattern, REG_EXTENDED)) != 0) + regfree(&preg); + else + ps_global->colorpat = preg; + } + if(pattern) + fs_give((void **)&pattern); + } + + LT_INS_S ** + insert_color_special_text(ins, p, ival, last_end, col) + LT_INS_S **ins; + char **p; + IVAL_S *ival; + int last_end; + COLOR_PAIR *col; + { + struct variable *vars = ps_global->vars; + + if (ival){ + *p += ival->start - last_end; + ins = gf_line_test_new_ins(ins, *p, color_embed(col->fg, col->bg), + (2 * RGBLEN) + 4); + *p += ival->end - ival->start; + ins = gf_line_test_new_ins(ins, *p, color_embed(VAR_NORM_FORE_COLOR, + VAR_NORM_BACK_COLOR), (2 * RGBLEN) + 4); + ins = insert_color_special_text(ins, p, ival->next, ival->end, col); + } + return ins; + } + + int + length_color(p, begin_color) + char *p; + int begin_color; + { + int len = 0, done = begin_color ? 0 : -1; + char *orig = p; + + while (*p && done <= 0){ + switch(*p++){ + case TAG_HANDLE : + p += *p + 1; + done++; + break; + + case TAG_FGCOLOR : + case TAG_BGCOLOR : + p += RGBLEN; + if (!begin_color) + done++; + break; + + default : + break; + } + } + len = p - orig; + return len; + } + + int + any_color_in_string(p) + char *p; + { + int rv = 0; + char *orig = p; + while (*p && !rv) + if (*p++ == TAG_EMBED) + rv = p - orig; + return rv; + } + + void + remove_spaces_ival(ivalp, p) + IVAL_S **ivalp; + char *p; + { + IVAL_S *ival; + int i; + if (!ivalp || !*ivalp) + return; + ival = *ivalp; + for (i = 0; isspace((unsigned char) p[ival->start + i]); i++); + if (ival->start + i < ival->end) /* do not do this if match only spaces */ + ival->start += i; + else + return; + for (i = 0; isspace((unsigned char) p[ival->end - i - 1]); i++); + ival->end -= i; + if (ival->next) + remove_spaces_ival(&(ival->next), p); + } + + int + color_this_text(linenum, line, ins, local) + long linenum; + char *line; + LT_INS_S **ins; + void *local; + { + struct variable *vars = ps_global->vars; + COLOR_PAIR *col = NULL; + char *p; + int i = 0; + static char *pattern = NULL; + /* char *buf; + + select_quote(linenum, line, ins, (void *)code); + for (i = 0; tmp_20k_buf[i] && (buf[i] = tmp_20k_buf[i]); i++); + buf[i] = '\0'; */ + p = line + i; + + if(VAR_SPECIAL_TEXT_FORE_COLOR && VAR_SPECIAL_TEXT_BACK_COLOR + && (col = new_color_pair(VAR_SPECIAL_TEXT_FORE_COLOR, + VAR_SPECIAL_TEXT_BACK_COLOR)) + && !pico_is_good_colorpair(col)) + free_color_pair(&col); + + if(ps_global->VAR_SPECIAL_TEXT && *ps_global->VAR_SPECIAL_TEXT + && **ps_global->VAR_SPECIAL_TEXT && col){ + IVAL_S *ival; + int done = 0, begin_color = 0; + + while (!done){ + if (i = any_color_in_string(p)){ + begin_color = (begin_color + 1) % 2; + if (begin_color){ + p[i - 1] = '\0'; + ival = compute_interval(p, 0); + remove_spaces_ival(&ival, p); + p[i - 1] = TAG_EMBED; + ins = insert_color_special_text(ins, &p, ival, 0, col); + } + for (;*p++ != TAG_EMBED; ); + p += length_color(p, begin_color); + } + else{ + ival = compute_interval(p, 0); + remove_spaces_ival(&ival, p); + ins = insert_color_special_text(ins, &p, ival, 0, col); + done++; + } + interval_free(&ival); + if (!*p) + done++; + } + free_color_pair(&col); + } + + return 0; + } /* * The argument fieldname is something like "Subject:..." or "Subject". diff -rc alpine-2.00/pith/mailview.h alpine-2.00.I.USE/pith/mailview.h *** alpine-2.00/pith/mailview.h 2008-06-03 08:54:15.000000000 -0700 --- alpine-2.00.I.USE/pith/mailview.h 2011-02-07 20:33:43.000000000 -0800 *************** *** 29,34 **** --- 29,40 ---- #include "../pith/color.h" + typedef struct IVAL { + int start; + int end; + struct IVAL *next; + } IVAL_S; + /* format_message flags */ #define FM_DISPLAY 0x0001 /* result is headed for display */ #define FM_NEW_MESS 0x0002 /* a new message so zero out attachment descrip */ *************** *** 125,130 **** --- 131,145 ---- int url_hilite(long, char *, LT_INS_S **, void *); int handle_start_color(char *, size_t, int *, int); int handle_end_color(char *, size_t, int *); + IVAL_S *compute_interval(char *, int); + void remove_spaces_ival(IVAL_S **, char *); + void interval_free(IVAL_S **); + void regex_pattern(char **); + LT_INS_S **insert_color_special_text(LT_INS_S **, char **, IVAL_S *, + int, COLOR_PAIR *); + int any_color_in_string(char *); + int length_color(char *, int); + int color_this_text(long, char *, LT_INS_S **, void *); /* * BUG: BELOW IS UNIX/PC ONLY since config'd browser means nothing to webpine *************** *** 141,146 **** --- 156,162 ---- char *display_parameters(PARAMETER *); char *pine_fetch_header(MAILSTREAM *, long, char *, char **, long); int color_signature(long, char *, LT_INS_S **, void *); + int select_quote(long, char *, LT_INS_S **, void *); int scroll_handle_start_color(char *, size_t, int *); int scroll_handle_end_color(char *, size_t, int *, int); int width_at_this_position(unsigned char *, unsigned long); diff -rc alpine-2.00/pith/Makefile.am alpine-2.00.I.USE/pith/Makefile.am *** alpine-2.00/pith/Makefile.am 2008-06-03 08:54:15.000000000 -0700 --- alpine-2.00.I.USE/pith/Makefile.am 2011-02-07 20:33:42.000000000 -0800 *************** *** 25,31 **** filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \ keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \ margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \ ! readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \ state.c status.c store.c stream.c string.c strlst.c takeaddr.c tempfile.c text.c \ thread.c adjtime.c url.c util.c helptext.c smkeys.c smime.c --- 25,31 ---- filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \ keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \ margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \ ! readfile.c remote.c reply.c rfc2231.c rules.c save.c search.c sequence.c send.c sort.c \ state.c status.c store.c stream.c string.c strlst.c takeaddr.c tempfile.c text.c \ thread.c adjtime.c url.c util.c helptext.c smkeys.c smime.c diff -rc alpine-2.00/pith/Makefile.in alpine-2.00.I.USE/pith/Makefile.in *** alpine-2.00/pith/Makefile.in 2008-06-03 08:54:15.000000000 -0700 --- alpine-2.00.I.USE/pith/Makefile.in 2011-02-07 20:33:42.000000000 -0800 *************** *** 77,83 **** margin.$(OBJEXT) mimedesc.$(OBJEXT) mimetype.$(OBJEXT) \ msgno.$(OBJEXT) newmail.$(OBJEXT) news.$(OBJEXT) \ pattern.$(OBJEXT) pipe.$(OBJEXT) readfile.$(OBJEXT) \ ! remote.$(OBJEXT) reply.$(OBJEXT) rfc2231.$(OBJEXT) \ save.$(OBJEXT) search.$(OBJEXT) sequence.$(OBJEXT) \ send.$(OBJEXT) sort.$(OBJEXT) state.$(OBJEXT) status.$(OBJEXT) \ store.$(OBJEXT) stream.$(OBJEXT) string.$(OBJEXT) \ --- 77,83 ---- margin.$(OBJEXT) mimedesc.$(OBJEXT) mimetype.$(OBJEXT) \ msgno.$(OBJEXT) newmail.$(OBJEXT) news.$(OBJEXT) \ pattern.$(OBJEXT) pipe.$(OBJEXT) readfile.$(OBJEXT) \ ! remote.$(OBJEXT) reply.$(OBJEXT) rfc2231.$(OBJEXT) rules.$(OBJEXT) \ save.$(OBJEXT) search.$(OBJEXT) sequence.$(OBJEXT) \ send.$(OBJEXT) sort.$(OBJEXT) state.$(OBJEXT) status.$(OBJEXT) \ store.$(OBJEXT) stream.$(OBJEXT) string.$(OBJEXT) \ *************** *** 275,281 **** filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \ keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \ margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \ ! readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \ state.c status.c store.c stream.c string.c strlst.c takeaddr.c tempfile.c text.c \ thread.c adjtime.c url.c util.c helptext.c smkeys.c smime.c --- 275,281 ---- filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \ keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \ margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \ ! readfile.c remote.c reply.c rfc2231.c rules.c save.c search.c sequence.c send.c sort.c \ state.c status.c store.c stream.c string.c strlst.c takeaddr.c tempfile.c text.c \ thread.c adjtime.c url.c util.c helptext.c smkeys.c smime.c *************** *** 404,409 **** --- 404,410 ---- @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/url.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rules.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< diff -rc alpine-2.00/pith/makefile.wnt alpine-2.00.I.USE/pith/makefile.wnt *** alpine-2.00/pith/makefile.wnt 2007-10-24 14:58:00.000000000 -0700 --- alpine-2.00.I.USE/pith/makefile.wnt 2011-02-07 20:33:42.000000000 -0800 *************** *** 44,50 **** init.h keyword.h ldap.h list.h mailcap.h mailcmd.h mailindx.h maillist.h \ mailpart.h mailview.h margin.h mimedesc.h mimetype.h msgno.h newmail.h news.h \ options.h pattern.h pineelt.h pipe.h readfile.h remote.h remtype.h repltype.h reply.h \ ! rfc2231.h save.h savetype.h search.h send.h sequence.h signal.h sort.h sorttype.h \ state.h status.h store.h stream.h string.h strlst.h takeaddr.h tempfile.h text.h \ thread.h url.h user.h util.h --- 44,50 ---- init.h keyword.h ldap.h list.h mailcap.h mailcmd.h mailindx.h maillist.h \ mailpart.h mailview.h margin.h mimedesc.h mimetype.h msgno.h newmail.h news.h \ options.h pattern.h pineelt.h pipe.h readfile.h remote.h remtype.h repltype.h reply.h \ ! rfc2231.h rules.h save.h savetype.h search.h send.h sequence.h signal.h sort.h sorttype.h \ state.h status.h store.h stream.h string.h strlst.h takeaddr.h tempfile.h text.h \ thread.h url.h user.h util.h *************** *** 53,59 **** filter.obj flag.obj folder.obj handle.obj help.obj helptext.obj hist.obj icache.obj imap.obj init.obj \ keyword.obj ldap.obj list.obj mailcap.obj mailcmd.obj mailindx.obj maillist.obj mailview.obj \ margin.obj mimedesc.obj mimetype.obj msgno.obj newmail.obj news.obj pattern.obj pipe.obj \ ! readfile.obj remote.obj reply.obj rfc2231.obj save.obj search.obj sequence.obj send.obj sort.obj state.obj \ status.obj store.obj stream.obj string.obj strlst.obj takeaddr.obj tempfile.obj text.obj \ thread.obj adjtime.obj url.obj util.obj --- 53,59 ---- filter.obj flag.obj folder.obj handle.obj help.obj helptext.obj hist.obj icache.obj imap.obj init.obj \ keyword.obj ldap.obj list.obj mailcap.obj mailcmd.obj mailindx.obj maillist.obj mailview.obj \ margin.obj mimedesc.obj mimetype.obj msgno.obj newmail.obj news.obj pattern.obj pipe.obj \ ! readfile.obj remote.obj reply.obj rfc2231.obj rules.obj save.obj search.obj sequence.obj send.obj sort.obj state.obj \ status.obj store.obj stream.obj string.obj strlst.obj takeaddr.obj tempfile.obj text.obj \ thread.obj adjtime.obj url.obj util.obj diff -rc alpine-2.00/pith/mimetype.c alpine-2.00.I.USE/pith/mimetype.c *** alpine-2.00/pith/mimetype.c 2008-03-06 12:54:01.000000000 -0800 --- alpine-2.00.I.USE/pith/mimetype.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 178,183 **** --- 178,212 ---- panic("Unhandled mime type search"); } + /* if we still can not find the type, but it is a .docx (or alike) extension + set the type here. Do not use the grope function. + */ + if(rv == 0){ + rv = 1; /* assume success */ + mt_map->to.mime.type = TYPEAPPLICATION; + if(!strucmp(mt_map->from.ext, "docx")) + mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.WORDPROCESSINGML.DOCUMENT"); + else if(!strucmp(mt_map->from.ext, "xslx")) + mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.SPREADSHEETML.SHEET"); + else if(!strucmp(mt_map->from.ext, "xltx")) + mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.SPREADSHEETML.TEMPLATE"); + else if(!strucmp(mt_map->from.ext, "potx")) + mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.PRESENTATIONML.TEMPLATE"); + else if(!strucmp(mt_map->from.ext, "ppsx")) + mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.PRESENTATIONML.SLIDESHOW"); + else if(!strucmp(mt_map->from.ext, "pptx")) + mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.PRESENTATIONML.PRESENTATION"); + else if(!strucmp(mt_map->from.ext, "sldx")) + mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.PRESENTATIONML.SLIDE"); + else if(!strucmp(mt_map->from.ext, "dotx")) + mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.WORDPROCESSINGML.TEMPLATE"); + else if(!strucmp(mt_map->from.ext, "xlam")) + mt_map->to.mime.subtype = cpystr("VND.MS-EXCEL.ADDIN.MACROENABLED.12"); + else if(!strucmp(mt_map->from.ext, "xslb")) + mt_map->to.mime.subtype = cpystr("VND.MS-EXCEL.SHEET.BINARY.MACROENABLED.12"); + else rv = 0; /* else, failure */ + } + return(rv); } diff -rc alpine-2.00/pith/msgno.c alpine-2.00.I.USE/pith/msgno.c *** alpine-2.00/pith/msgno.c 2007-12-03 16:13:55.000000000 -0800 --- alpine-2.00.I.USE/pith/msgno.c 2011-02-07 20:33:47.000000000 -0800 *************** *** 932,937 **** --- 932,943 ---- if((*peltp)->pthrd) fs_give((void **) &(*peltp)->pthrd); + if((*peltp)->firsttext) + fs_give((void **) &(*peltp)->firsttext); + + if((*peltp)->firsttextraw) + fs_give((void **) &(*peltp)->firsttextraw); + if((*peltp)->ice) free_ice(&(*peltp)->ice); diff -rc alpine-2.00/pith/newmail.c alpine-2.00.I.USE/pith/newmail.c *** alpine-2.00/pith/newmail.c 2008-07-14 11:01:54.000000000 -0700 --- alpine-2.00.I.USE/pith/newmail.c 2011-02-07 20:33:47.000000000 -0800 *************** *** 684,692 **** if(!for_new_mail_win) q_status_message5(SM_ASYNC | SM_DING, 0, 60, "%s%s%s%.80s%.80s", intro, ! from ? ((number > 1L) ? " Most recent f" : " F") : "", ! from ? "rom " : "", ! from ? from : "", subjtext); #if (!defined(DOS) && !defined(OS2) && !defined(LEAVEOUTFIFO)) || defined(_WINDOWS) else { --- 684,692 ---- if(!for_new_mail_win) q_status_message5(SM_ASYNC | SM_DING, 0, 60, "%s%s%s%.80s%.80s", intro, ! from && from[0] ? ((number > 1L) ? " Most recent f" : " F") : "", ! from && from[0] ? "rom " : "", ! from && from[0] ? from : "", subjtext); #if (!defined(DOS) && !defined(OS2) && !defined(LEAVEOUTFIFO)) || defined(_WINDOWS) else { *************** *** 724,732 **** } else snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%.80s", intro, ! from ? ((number > 1L) ? " Most recent f" : " F") : "", ! from ? "rom " : "", ! from ? from : ""); (*pith_opt_icon_text)(tmp_20k_buf, IT_NEWMAIL); } --- 724,732 ---- } else snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%.80s", intro, ! from && from[0] ? ((number > 1L) ? " Most recent f" : " F") : "", ! from && from[0] ? "rom " : "", ! from && from[0] ? from : ""); (*pith_opt_icon_text)(tmp_20k_buf, IT_NEWMAIL); } diff -rc alpine-2.00/pith/osdep/color.c alpine-2.00.I.USE/pith/osdep/color.c *** alpine-2.00/pith/osdep/color.c 2006-09-26 12:30:49.000000000 -0700 --- alpine-2.00.I.USE/pith/osdep/color.c 2011-02-07 20:33:42.000000000 -0800 *************** *** 31,37 **** #include #include "./color.h" ! /* --- 31,37 ---- #include #include "./color.h" ! #include "./collate.h" /* *************** *** 91,93 **** --- 91,1278 ---- { return(pico_set_colors(col ? col->fg : NULL, col ? col->bg : NULL, flags)); } + + + /* + * Extended Justification support also does not belong here + * but otherwise webpine will not build, so we move everything + * here. Hopefully this will be the permanent place for these + * routines. This routines used to be in pico/word.c + */ + #define NSTRING 256 + #include "../../include/general.h" + + /* Support of indentation of paragraphs */ + #define is_indent_char(c) (((c) == '.' || (c) == '}' || (c) == RPAREN || \ + (c) == '*' || (c) == '+' || is_a_digit(c) || \ + ISspace(c) || (c) == '-' || \ + (c) == ']') ? 1 : 0) + #define allowed_after_digit(c,word,k) ((((c) == '.' && \ + allowed_after_period(next((word),(k)))) ||\ + (c) == RPAREN || (c) == '}' || (c) == ']' ||\ + ISspace(c) || is_a_digit(c) || \ + ((c) == '-' ) && \ + allowed_after_dash(next((word),(k)))) \ + ? 1 : 0) + #define allowed_after_period(c) (((c) == RPAREN || (c) == '}' || (c) == ']' ||\ + ISspace(c) || (c) == '-' || \ + is_a_digit(c)) ? 1 : 0) + #define allowed_after_parenth(c) (ISspace(c) ? 1 : 0) + #define allowed_after_space(c) (ISspace(c) ? 1 : 0) + #define allowed_after_braces(c) (ISspace(c) ? 1 : 0) + #define allowed_after_star(c) ((ISspace(c) || (c) == RPAREN ||\ + (c) == ']' || (c) == '}') ? 1 : 0) + #define allowed_after_dash(c) ((ISspace(c) || is_a_digit(c)) ? 1 : 0) + #define EOLchar(c) (((c) == '.' || (c) == ':' || (c) == '?' ||\ + (c) == '!') ? 1 : 0) + + + /* Extended justification support */ + #define is_cquote(c) ((c) == '>' || (c) == '|' || (c) == ']' || (c) == ':') + #define is_cword(c) ((((c) >= 'a') && ((c) <= 'z')) || \ + (((c) >= 'A') && ((c) <= 'Z')) || \ + (((c) >= '0') && ((c) <= '9')) || \ + ((c) == ' ') || ((c) == '?') || \ + ((c) == '@') || ((c) == '.') || \ + ((c) == '!') || ((c) == '\'') || \ + ((c) == ',') || ((c) == '\"') ? 1 : 0) + #define isaquote(c) ((c) == '\"' || (c) == '\'') + #define is8bit(c) ((((int) (c)) & 0x80) ? 1 : 0) + #define iscontrol(c) (iscntrl(((int) (c)) & 0x7f) ? 1 : 0) + #define forbidden(c) (((c) == '\"') || ((c) == '\'') || ((c) == '$') ||\ + ((c) == ',') || ((c) == '.') || ((c) == '-') ||\ + ((c) == LPAREN) || ((c) == '/')|| ((c) == '`') ||\ + ((c) == '{') || ((c) == '\\') || (iscontrol((c))) ||\ + (((c) >= '0') && ((c) <= '9')) || ((c) == '?')) + #define is_cletter(c) ((((c) >= 'a') && ((c) <= 'z'))) ||\ + ((((c) >= 'A') && ((c) <= 'Z'))||\ + is8bit(c)) + #define is_cnumber(c) ((c) >= '0' && (c) <= '9') + #define allwd_after_word(c) (((c) == ' ') || ((c) == '>') || is_cletter(c)) + #define allwd_after_qsword(c) (((c) != '\\') && ((c) != RPAREN)) + #define before(word,i) (((i) > 0) ? (word)[(i) - 1] : 0) + #define next(w,i) ((((w)[(i)]) != 0) ? ((w)[(i) + 1]) : 0) + #define now(w,i) ((w)[(i)]) + #define is_qsword(c) (((c) == ':') || ((c) == RPAREN) ? 1 : 0) + #define is_colon(c) (((c) == ':') ? 1 : 0) + #define is_rarrow(c) (((c) == '>') ? 1 : 0) + #define is_tilde(c) (((c) == '~') ? 1 : 0) + #define is_dash(c) (((c) == '-') ? 1 : 0) + #define is_pound(c) (((c) == '#') ? 1 : 0) + #define is_a_digit(c) ((((c) >= '0') && ((c) <= '9')) ? 1 : 0) + #define is_allowed(c) (is_cquote(c) || is_cword(c) || is_dash(c) || \ + is_pound(c)) + #define qs_allowed(a) (((a)->qstype != qsGdb) && ((a)->qstype != qsProg)) + + /* Internal justification functions */ + QSTRING_S *is_quote(char *, char *, int); + QSTRING_S *qs_normal_part(QSTRING_S *); + QSTRING_S *qs_remove_trailing_spaces(QSTRING_S *); + QSTRING_S *trim_qs_from_cl(QSTRING_S *, QSTRING_S *, QSTRING_S *); + QSTRING_S *fix_qstring(QSTRING_S *, QSTRING_S *, QSTRING_S *); + QSTRING_S *fix_qstring_allowed(QSTRING_S *, QSTRING_S *, QSTRING_S *); + QSTRING_S *qs_add(char *, char *, QStrType, int, int, int, int); + QSTRING_S *remove_qsword(QSTRING_S *); + QSTRING_S *do_raw_quote_match(char *, char *, char *, char *, QSTRING_S **, QSTRING_S **); + void free_qs(QSTRING_S **); + int word_is_prog(char *); + int qstring_is_normal(QSTRING_S *); + int exists_good_part(QSTRING_S *); + int strcmp_qs(char *, char *); + int count_levels_qstring(QSTRING_S *); + int same_qstring(QSTRING_S *, QSTRING_S *); + int advance_quote_string(char *, char *, int); + int isaword(char *,int ,int); + int isamailbox(char *,int ,int); + + + int + word_is_prog(char *word) + { + static char *list1[] = {"#include", + "#define", + "#ifdef", + "#ifndef", + "#elif", + "#if", + NULL}; + static char *list2[] = {"#else", + "#endif", + NULL}; + int i, j = strlen(word), k, rv = 0; + + for(i = 0; rv == 0 && list1[i] && (k = strlen(list1[i])) && k < j; i++) + if(!strncmp(list1[i], word, k) && ISspace(word[k])) + rv++; + + if(rv) + return rv; + + for(i = 0; rv == 0 && list2[i] && (k = strlen(list2[i])) && k <= j; i++) + if(!strncmp(list2[i], word, k) && (!word[k] || ISspace(word[k]))) + rv++; + + return rv; + } + + /* + * This function creates a qstring pointer with the information that + * is_quote handles to it. + * Parameters: + * qs - User supplied quote string + * word - The line of text that the user is trying to read/justify + * beginw - Where we need to start copying from + * endw - Where we end copying + * offset - Any offset in endw that we need to account for + * typeqs - type of the string to be created + * neednext - boolean, indicating if we need to compute the next field + * of leave it NULL + * + * It is a mistake to call this function if beginw >= endw + offset. + * Please note the equality sign in the above inequality (this is because + * we always assume that qstring->value != ""). + */ + QSTRING_S * + qs_add(qs, word, typeqs, beginw, endw, offset, neednext) + char *qs; + char word[NSTRING]; + QStrType typeqs; + int beginw, endw, offset, neednext; + { + QSTRING_S *qstring, *nextqs; + int i; + + qstring = (QSTRING_S *) malloc (sizeof(QSTRING_S)); + memset (qstring, 0, sizeof(QSTRING_S)); + qstring->qstype = qsNormal; + + if (beginw == 0){ + beginw = endw + offset; + qstring->qstype = typeqs; + } + + nextqs = neednext ? is_quote(qs, word+beginw, 1) : NULL; + + qstring->value = (char *) malloc((beginw+1)*sizeof(char)); + strncpy(qstring->value, word, beginw); + qstring->value[beginw] = '\0'; + + qstring->next = nextqs; + + return qstring; + } + + int + qstring_is_normal(cl) + QSTRING_S *cl; + { + for (;cl && (cl->qstype == qsNormal); cl = cl->next); + return cl ? 0 : 1; + } + + /* + * Given a quote string, this function returns the part that is the leading + * normal part of it. (the normal part is the part that is tagged qsNormal, + * that is to say, the one that is not controversial at all (like qsString + * for example). + */ + QSTRING_S * + qs_normal_part(cl) + QSTRING_S *cl; + { + + if (!cl) /* nothing in, nothing out */ + return cl; + + if (cl->qstype != qsNormal) + free_qs(&cl); + + if (cl) + cl->next = qs_normal_part(cl->next); + + return cl; + } + + /* + * this function removes trailing spaces from a quote string, but leaves the + * last one if there are trailing spaces + */ + QSTRING_S * + qs_remove_trailing_spaces(cl) + QSTRING_S *cl; + { + QSTRING_S *rl = cl; + if (!cl) /* nothing in, nothing out */ + return cl; + + if (cl->next) + cl->next = qs_remove_trailing_spaces(cl->next); + else{ + if (value_is_space(cl->value)) + free_qs(&cl); + else{ + int i, l; + i = l = strlen(cl->value) - 1; + while (cl->value && cl->value[i] + && ISspace(cl->value[i])) + i--; + i += (i < l) ? 2 : 1; + cl->value[i] = '\0'; + } + } + return cl; + } + + /* + * This function returns if two strings are the same quote string. + * The call is not symmetric. cl must preceed the line nl. This function + * should be called for comparing the last part of cl and nl. + */ + int + strcmp_qs(char *valuecl, char *valuenl) + { + int j; + + for (j = 0; valuecl[j] && (valuecl[j] == valuenl[j]); j++); + return !strcmp(valuecl, valuenl) + || (valuenl[j] && value_is_space(valuenl+j) + && value_is_space(valuecl+j) + && strlenis(valuecl+j) >= strlenis(valuenl+j)) + || (!valuenl[j] && value_is_space(valuecl+j)); + } + + int + count_levels_qstring(cl) + QSTRING_S *cl; + { + int count; + for (count = 0; cl ; count++, cl = cl->next); + + return count; + } + + int + value_is_space(char *value) + { + for (; value && *value && ISspace(*value); value++); + + return value && *value ? 0 : 1; + } + + void + free_qs(QSTRING_S **cl) + { + if (!(*cl)) + return; + + if ((*cl)->next) + free_qs(&((*cl)->next)); + + (*cl)->next = (QSTRING_S *) NULL; + + if ((*cl)->value) + free((void *)(*cl)->value); + (*cl)->value = (char *) NULL; + free((void *)(*cl)); + *cl = (QSTRING_S *) NULL; + } + + /* + * This function returns the number of agreements between + * cl and nl. The call is not symmetric. cl must be the line + * preceding nl. + */ + int + same_qstring(QSTRING_S *cl, QSTRING_S *nl) + { + int same = 0, done = 0; + + for (;cl && nl && !done; cl = cl->next, nl = nl->next) + if (cl->qstype == nl->qstype + && (!strcmp(cl->value, nl->value) + || (!cl->next && strcmp_qs(cl->value, nl->value)))) + same++; + else + done++; + return same; + } + + QSTRING_S * + trim_qs_from_cl(QSTRING_S *cl, QSTRING_S *nl, QSTRING_S *pl) + { + QSTRING_S *cqstring = pl ? pl : nl; + QSTRING_S *tl = pl ? pl : nl; + int p, c; + + if (qstring_is_normal(tl)) + return tl; + + p = same_qstring(pl ? pl : cl, pl ? cl : nl); + + for (c = 1; c < p; c++, cl = cl->next, tl = tl->next); + + /* + * cl->next and tl->next differ, it may be because cl->next does not + * exist or tl->next does not exist or simply both exist but are + * different. In this last case, it may be that cl->next->value is made + * of spaces. If this is the case, tl advances once more. + */ + + if (tl->next){ + if (cl && cl->next && value_is_space(cl->next->value)) + tl = tl->next; + if (tl->next) + free_qs(&(tl->next)); + } + + if (!p) + free_qs(&cqstring); + + return cqstring; + } + + /* This function trims cl so that it returns a real quote string based + * on information gathered from the previous and next lines. pl and cl are + * also trimmed, but that is done in another function, not here. + */ + QSTRING_S * + fix_qstring(QSTRING_S *cl, QSTRING_S *nl, QSTRING_S *pl) + { + QSTRING_S *cqstring = cl, *nqstring = nl, *pqstring = pl; + int c, n; + + if (qstring_is_normal(cl)) + return cl; + + c = count_levels_qstring(cl); + n = same_qstring(cl,nl); + + if (!n){ /* no next line or no agreement with next line */ + int p = same_qstring(pl, cl); /* number of agreements between pl and cl */ + QSTRING_S *tl; /* test line */ + + /* + * Here p <= c, so either p < c or p == c. If p == c, we are done, + * and return cl. If not, there are two cases, either p == 0 or + * 0 < p < c. In the first case, we do not have enough evidence + * to return anything other than the normal part of cl, in the second + * case we can only return p levels of cl. + */ + + if (p == c) + tl = cqstring; + else{ + if (p){ + for (c = 1; c < p; c++) + cl = cl->next; + free_qs(&(cl->next)); + tl = cqstring; + } + else{ + int done = 0; + QSTRING_S *al = cl; /* another line */ + /* + * Ok, we reaelly don't have enough evidence to return anything, + * different from the normal part of cl, but it could be possible + * that we may want to accept the not-normal part, so we better + * make an extra test to determine what needs to be freed + */ + while (pl && cl && cl->qstype == pl->qstype + && !strucmp(cl->value, pl->value)){ + cl = cl->next; + pl = pl->next; + } + if (pl && cl && cl->qstype == pl->qstype + && strcmp_qs(pl->value, cl->value)) + cl = cl->next; /* next level differs only in spaces */ + while (!done){ + while (cl && cl->qstype == qsNormal) + cl = cl->next; + if (cl){ + if ((cl->qstype == qsString) + && (cl->value[strlen(cl->value) - 1] == '>')) + cl = cl->next; + else done++; + } + else done++; + } + if (al == cl){ + free_qs(&(cl)); + tl = cl; + } + else { + while (al && (al->next != cl)) + al = al->next; + cl = al; + if (cl && cl->next) + free_qs(&(cl->next)); + tl = cqstring; + } + } + } + return tl; + } + if (n + 1 < c){ /* if there are not enough agreements */ + int p = same_qstring(pl, cl); /* number of agreement between pl and cl */ + QSTRING_S *tl; /* test line */ + /* + * There's no way we can use cl in this case, but we can use + * part of cl, this is if pl does not have more agreements + * with cl. + */ + if (p == c) + tl = cqstring; + else{ + int m = p < n ? n : p; + for (c = 1; c < m; c++){ + pl = pl ? pl->next : (QSTRING_S *) NULL; + nl = nl ? nl->next : (QSTRING_S *) NULL; + cl = cl->next; + } + if (p == n && pl && pl->next && nl && nl->next + && ((cl->next->qstype == pl->next->qstype) + || (cl->next->qstype == nl->next->qstype)) + && (strcmp_qs(cl->next->value, pl->next->value) + || strcmp_qs(pl->next->value, cl->next->value) + || strcmp_qs(cl->next->value, nl->next->value) + || strcmp_qs(nl->next->value, cl->next->value))) + cl = cl->next; /* next level differs only in spaces */ + if (cl->next) + free_qs(&(cl->next)); + tl = cqstring; + } + return tl; + } + if (n + 1 == c){ + int p = same_qstring(pl, cl); + QSTRING_S *tl; /* test line */ + + /* + * p <= c, so p <= n+1, which means p < n + 1 or p == n + 1. + * If p < n + 1, then p <= n. + * so we have three possibilities: + * p == n + 1 or p == n or p < n. + * In the first case we copy p == n + 1 == c levels, in the second + * and third case we copy n levels, and check if we can copy the + * n + 1 == c level. + */ + if (p == n + 1) /* p == c, in the above sense of c */ + tl = cl; /* use cl, this is enough evidence */ + else{ + for (c = 1; c < n; c++) + cl = cl->next; + /* + * Here c == n, we only have one more level of cl, and at least one + * more level of nl + */ + if (cl->next->qstype == qsNormal) + cl = cl->next; + if (cl->next) + free_qs(&(cl->next)); + tl = cqstring; + } + return tl; + } + if (n == c) /* Yeah!!! */ + return cqstring; + } + + QSTRING_S * + fix_qstring_allowed(QSTRING_S *cl, QSTRING_S *nl, QSTRING_S *pl) + { + if(!cl) + return (QSTRING_S *) NULL; + + if (qs_allowed(cl)) + cl->next = fix_qstring_allowed(cl->next, (nl ? nl->next : NULL), + (pl ? pl->next : NULL)); + else + if((nl && cl->qstype == nl->qstype) || (pl && cl->qstype == pl->qstype) + || (!nl && !pl)) + free_qs(&cl); + return cl; + } + + /* + * This function flattens the quote string returned to us by is_quote. A + * crash in this function implies a bug elsewhere. + */ + void + flatten_qstring(QSTRING_S *qs, char *buff, int bufflen) + { + int i, j; + if(!buff || bufflen <= 0) + return; + + for (i = 0; qs; qs = qs->next) + for (j = 0; i < bufflen - 1 + && (qs->value[j]) && (buff[i++] = qs->value[j]); j++); + buff[i] = '\0'; + } + + /* + * Given a string, we return the position where the function thinks that + * the quote string is over, if you are ever thinking of fixing something, + * you got to the right place. Memory freed by caller. Experience shows + * that it only makes sense to initialize memory when we need it, not at + * the start of this function. + */ + QSTRING_S * + is_quote (char *qs,char *word, int been_here) + { + int i = 0, j, nxt, prev, finished = 0, offset; + unsigned char c; + QSTRING_S *qstring = (QSTRING_S *) NULL; + + if (!word || !word[0]) + return (QSTRING_S *) NULL; + + while (!finished){ + /* + * Before we apply our rules, let's advance past the quote string + * given by the user, this will avoid not recognition of the + * user's indent string and application of the arbitrary rules + * below. Notice that this step may bring bugs into this + * procedure, but these bugs will only appear if the indent string + * is really really strange and the text to be justified + * cooperates a lot too, so in general this will not be a problem. + * If you are concerned about this bug, simply remove the + * following lines after this comment and before the "switch" + * command below and use a more normal quote string!. + */ + i += advance_quote_string(qs, word, i); + if (!word[i]) /* went too far? */ + return qs_add(qs, word, qsNormal, 0, i, 0, 0); + + switch (c = (unsigned char) now(word,i)){ + case NBSP: + case TAB : + case ' ' : { QSTRING_S *nextqs, *d; + + for (; ISspace(word[i]); i++); /* FIX ME */ + nextqs = is_quote(qs,word+i, 1); + /* + * Merge qstring and nextqs, since this is an artificial + * separation, unless nextqs is of different type. + * What this means in practice is that if + * qs->qstype == qsNormal and qs->next != NULL, then + * qs->next->qstype != qsNormal. + * + * Can't use qs_add to merge because it could lead + * to an infinite loop (e.g a line "^ ^"). + */ + i += nextqs && nextqs->qstype == qsNormal + ? strlen(nextqs->value) : 0; + qstring = (QSTRING_S *) malloc (sizeof(QSTRING_S)); + memset (qstring, 0, sizeof(QSTRING_S)); + qstring->value = (char *) malloc((i+1)*sizeof(char)); + strncpy(qstring->value, word, i); + qstring->value[i] = '\0'; + qstring->qstype = qsNormal; + if(nextqs && nextqs->qstype == qsNormal){ + d = nextqs->next; + nextqs->next = NULL; + qstring->next = d; + free_qs(&nextqs); + } + else + qstring->next = nextqs; + + return qstring; + } + break; + case RPAREN: /* parenthesis ')' */ + if ((i != 0) || ((i == 0) && been_here)) + i++; + else + if (i == 0) + return qs_add(qs, word, qsChar, i, i, 1, 1); + else + finished++; + break; + + case ':': /* colon */ + case '~': nxt = next(word,i); + if ((is_tilde(c) && (nxt == '/')) + || (is_colon(c) && !is_cquote(nxt) + && !is_cword(nxt) && nxt != RPAREN)) + finished++; + else if (is_cquote(c) + || is_cquote(nxt) + || (c != '~' && nxt == RPAREN) + || (i != 0 && ISspace(nxt)) + || is_cquote(prev = before(word,i)) + || (ISspace(prev) && !is_tilde(c)) + || (is_tilde(c) && nxt != '/')) + i++; + else if (i == 0 && been_here) + return qs_add(qs, word, qsChar, i, i, 1, 1); + else + finished++; + break; + + case '<' : + case '=' : + case '-' : offset = is_cquote(nxt = next(word,i)) ? 2 + : (nxt == c && is_cquote(next(word,i+1))) ? 3 : -1; + + if (offset > 0) + return qs_add(qs, word, qsString, i, i, offset, 1); + else + finished++; + break; + + case '[' : + case '+' : /* accept +>, *> */ + case '*' : if (is_rarrow(nxt = next(word, i)) || /* stars */ + (ISspace(nxt) && is_rarrow(next(word,i+1)))) + i++; + else + finished++; + break; + + case '^' : + case '!' : + case '%' : if (next(word,i) != c) + return qs_add(qs, word, qsChar, i, i+1, 0, 1); + else + finished++; + break; + + case '_' : if(ISspace(next(word, i))) + return qs_add(qs, word, qsChar, i, i+1, 0, 1); + else + finished++; + break; + + case '#' : { QStrType qstype = qsChar; + if((nxt = next(word, i)) != c){ + if(isdigit((int) nxt)) + qstype = qsGdb; + else + if(word_is_prog(word)) + qstype = qsProg; + return qs_add(qs, word, qstype, i, i+1, 0, 1); + } + else + finished++; + break; + } + + default: + if (is_cquote(c)) + i++; + else if (is_cletter(c)){ + for (j = i; (is_cletter(nxt = next(word,j)) || is_cnumber(nxt)) + && !(ISspace(nxt));j++); + /* + * The whole reason why we are splitting the quote + * string is so that we will be able to accept quote + * strings that are strange in some way. Here we got to + * a point in which a quote string might exist, but it + * could be strange, so we need to create a "next" field + * for the quote string to warn us that something + * strange is coming. We need to confirm if this is a + * good choice later. For now we will let it pass. + */ + if (isaword(word,i,j) || isamailbox(word,i,j)){ + int offset; + QStrType qstype; + + offset = (is_cquote(c = next(word,j)) + || (c == RPAREN)) ? 2 + : ((ISspace(c) + && is_cquote(next(word,j+1))) ? 3 : -1); + + qstype = (is_cquote(c) || (c == RPAREN)) + ? (is_qsword(c) ? qsWord : qsString) + : ((ISspace(c) && is_cquote(next(word,j+1))) + ? (is_qsword(next(word,j+1)) + ? qsWord : qsString) + : qsString); + + /* + * qsWords are valid quote strings only when + * they are followed by text. + */ + if (offset > 0 && qstype == qsWord && + !allwd_after_qsword(now(word,j + offset))) + offset = -1; + + if (offset > 0) + return qs_add(qs, word, qstype, i, j, offset, 1); + } + finished++; + } + else{ + if(i > 0) + return qs_add(qs, word, qsNormal, 0, i, 0, 1); + else if(!forbidden(c)) + return qs_add(qs, word, qsChar, 0, 1, 0, 1); + else /* chao pescao */ + finished++; + } + break; + } /* End Switch */ + } /* End while */ + if (i > 0) + qstring = qs_add(qs, word, qsNormal, 0, i, 0, 0); + return qstring; + } + + int + isaword(word,i,j) + char word[NSTRING]; + int i; + int j; + { + return i <= j && is_cletter(word[i]) ? + (i < j ? isaword(word,i+1,j) : 1) : 0; + } + + int + isamailbox(word,i,j) + char word[NSTRING]; + int i, j; + { + return i <= j && (is_cletter(word[i]) || is_a_digit(word[i]) + || word[i] == '.') + ? (i < j ? isamailbox(word,i+1,j) : 1) : 0; + } + + /* + This routine removes the last part that is qsword or qschar that is not + followed by a normal part. This means that if a qsword or qschar is + followed by a qsnormal (or qsstring), we accept the qsword (or qschar) + as part of a quote string. + */ + QSTRING_S * + remove_qsword(cl) + QSTRING_S *cl; + { + QSTRING_S *np = cl; + QSTRING_S *cp = np; /* this variable trails cl */ + + while(1){ + while (cl && cl->qstype == qsNormal) + cl = cl->next; + + if (cl){ + if (((cl->qstype == qsWord) || (cl->qstype == qsChar)) + && !exists_good_part(cl)){ + if (np == cl) /* qsword or qschar at the beginning */ + free_qs(&cp); + else{ + while (np->next != cl) + np = np->next; + free_qs(&(np->next)); + } + break; + } + else + cl = cl->next; + } + else + break; + } + return cp; + } + + int + exists_good_part (cl) + QSTRING_S *cl; + { + return (cl ? (((cl->qstype != qsWord) && (cl->qstype != qsChar) + && qs_allowed(cl) && !value_is_space(cl->value)) + ? 1 : exists_good_part(cl->next)) + : 0); + } + + int + line_isblank(char *q, char *GLine, char *NLine, char *PLine, int buflen) + { + int n = 0; + QSTRING_S *cl; + char qstr[NSTRING]; + + cl = do_raw_quote_match(q, GLine, NLine, PLine, NULL, NULL); + + flatten_qstring(cl, qstr, NSTRING); + + free_qs(&cl); + + for(n = strlen(qstr); n < buflen && GLine[n]; n++) + if(!ISspace((unsigned char) GLine[n])) + return(FALSE); + + return(TRUE); + } + + QSTRING_S * + do_raw_quote_match(char *q, char *GLine, char *NLine, char *PLine, QSTRING_S **nlp, QSTRING_S **plp) + { + QSTRING_S *cl, *nl = NULL, *pl = NULL; + char nbuf[NSTRING], pbuf[NSTRING], buf[NSTRING]; + int emptypl = 0, emptynl = 0; + + if (!(cl = is_quote(q, GLine, 0))) /* if nothing in, nothing out */ + return cl; + + nl = is_quote(q, NLine, 0); /* Next Line */ + if (nlp) *nlp = nl; + pl = is_quote(q, PLine, 0); /* Previous Line */ + if (plp) *plp = pl; + /* + * If there's nothing in the preceeding or following line + * there is not enough information to accept it or discard it. In this + * case it's likely to be an isolated line, so we better accept it + * if it does not look like a word. + */ + flatten_qstring(pl, pbuf, NSTRING); + emptypl = (!PLine || !PLine[0] || + (pl && value_is_space(pbuf)) && !PLine[strlen(pbuf)]) ? 1 : 0; + if (emptypl){ + flatten_qstring(nl, nbuf, NSTRING); + emptynl = (!NLine || !NLine[0] || + (nl && value_is_space(nbuf) && !NLine[strlen(nbuf)])) ? 1 : 0; + if (emptynl){ + cl = remove_qsword(cl); + if((cl = fix_qstring_allowed(cl, NULL, NULL)) != NULL) + cl = qs_remove_trailing_spaces(cl); + free_qs(&nl); + free_qs(&pl); + if(nlp) *nlp = NULL; + if(plp) *plp = NULL; + + return cl; + } + } + + /* + * If either cl, nl or pl contain suspicious characters that may make + * them (or not) be quote strings, we need to fix them, so that the + * next pass will be done correctly. + */ + + cl = fix_qstring(cl, nl, pl); + nl = trim_qs_from_cl(cl, nl, NULL); + pl = trim_qs_from_cl(cl, NULL, pl); + if((cl = fix_qstring_allowed(cl, nl, pl)) != NULL){ + nl = trim_qs_from_cl(cl, nl, NULL); + pl = trim_qs_from_cl(cl, NULL, pl); + } + else{ + free_qs(&nl); + free_qs(&pl); + } + if(nlp) + *nlp = nl; + else + free_qs(&nl); + if(plp) + *plp = pl; + else + free_qs(&pl); + return cl; + } + + QSTRING_S * + do_quote_match(q, GLine, NLine, PLine, rqstr, rqstrlen, plb) + char *q, *GLine, *NLine, *PLine, *rqstr; + int rqstrlen, plb; + { + QSTRING_S *cl, *nl = NULL, *pl = NULL; + int c, n, p,i, j, NewP, NewC, NewN, clength, same = 0; + char nbuf[NSTRING], pbuf[NSTRING], buf[NSTRING]; + + if(rqstr) + *rqstr = '\0'; + + /* if nothing in, nothing out */ + cl = do_raw_quote_match(q, GLine, NLine, PLine, &nl, &pl); + if(cl == NULL){ + free_qs(&nl); + free_qs(&pl); + return cl; + } + + flatten_qstring(cl, rqstr, rqstrlen); + flatten_qstring(cl, buf, NSTRING); + flatten_qstring(nl, nbuf, NSTRING); + flatten_qstring(pl, pbuf, NSTRING); + + /* + * Once upon a time, is_quote used to return the length of the quote + * string that it had found. One day, not long ago, black hand came + * and changed all that, and made is_quote return a quote string + * divided in several fields, making the algorithm much more + * complicated. Fortunately black hand left a few comments in the + * source code to make it more understandable. Because of this change + * we need to compute the lengths of the quote strings separately + */ + c = buf && buf[0] ? strlen(buf) : 0; + n = nbuf && nbuf[0] ? strlen(nbuf) : 0; + p = pbuf && pbuf[0] ? strlen(pbuf) : 0; + /* + * When quote strings contain only blank spaces (ascii code 32) the + * above count is equal to the length of the quote string, but if + * there are TABS, the length of the quote string as seen by the user + * is different than the number that was just computed. Because of + * this we demand a recount (hmm.. unless you are in Florida, where + * recounts are forbidden) + */ + NewP = strlenis(pbuf); + NewC = strlenis(buf); + NewN = strlenis(nbuf); + + /* + * For paragraphs with spaces in the first line, but no space in the + * quote string of the second line, we make sure we choose the quote + * string without a space at the end of it. + */ + if ((NLine && !NLine[0]) + && ((PLine && !PLine[0]) + || (((same = same_qstring(pl, cl)) != 0) + && (same != count_levels_qstring(cl))))) + cl = qs_remove_trailing_spaces(cl); + else + if (NewC > NewN){ + int agree = 0; + for (j = 0; (j < n) && (GLine[j] == NLine[j]); j++); + clength = j; + /* clength is the common length in which Gline and Nline agree */ + /* j < n means that they do not agree fully */ + /* GLine = " \tText" + NLine = " Text" */ + if(j == n) + agree++; + if (clength < n){ /* see if buf and nbuf are padded with spaces and tabs */ + for (i = clength; i < n && ISspace(NLine[i]); i++); + if (i == n){/* padded NLine until the end of spaces? */ + for (i = clength; i < c && ISspace(GLine[i]); i++); + if (i == c) /* Padded CLine until the end of spaces? */ + agree++; + } + } + if (agree){ + for (j = clength; j < c && ISspace(GLine[j]); j++); + if (j == c){ + /* + * If we get here, it means that the current line has the same + * quote string (visually) than the next line, but both of them + * are padded with different amount of TABS or spaces at the end. + * The current line (GLine) has more spaces/TABs than the next + * line. This is the typical situation that is found at the + * begining of a paragraph. We need to check this, however, by + * checking the previous line. This avoids that we confuse + * ourselves with being in the last line of a paragraph. + * Example when it should not free_qs(cl) + * " Text in Paragraph 1" (PLine) + * " Text in Paragraph 1" (GLine) + * " Other Paragraph Number 2" (NLine) + * + * Example when it should free_qs(cl): + * ":) " (PLine) p = 3, j = 3 + * ":) Text" (GLine) c = 5 + * ":) More text" (NLine) n = 3 + * + * Example when it should free_qs(cl): + * ":) " (PLine) p = 3, j = 3 + * ":) > > > Text" (GLine) c = 11 + * ":) > > > More text" (NLine) n = 9 + * + * Example when it should free_qs(cl): + * ":) :) " (PLine) p = 6, j = 3 + * ":) > > > Text" (GLine) c = 11 + * ":) > > > More text" (NLine) n = 9 + * + * Example when it should free_qs(cl): + * ":) > > > " (PLine) p = 13, j = 11 + * ":) > > > Text" (GLine) c = 11 + * ":) > > > More text" (NLine) n = 9 + * + * The following example is very interesting. The "Other Text" + * line below should free the quote string an make it equal to the + * quote string of the line below it, but any algorithm trying + * to advance past that line should make it stop there, so + * we need one more check, to check the raw quote string and the + * processed quote string at the same time. + * FREE qs in this example. + * " Some Text" (PLine) p = 3, j = 0 + * "\tOther Text" (GLine) c = 1 + * " More Text" (NLine) n = 3 + * + */ + for (j = 0; (j < p) && (GLine[j] == PLine[j]); j++); + if ((p != c || j != p) && NLine[n]) + if(!get_indent_raw_line(q, PLine, nbuf, NSTRING, p, plb) + || NewP + strlenis(nbuf) != NewC){ + free_qs(&cl); + free_qs(&pl); + return nl; + } + } + } + } + + free_qs(&nl); + free_qs(&pl); + + return cl; + } + + /* + * Given a line, an initial position, and a quote string, we advance the + * current line past the quote string, including arbitraty spaces + * contained in the line, except that it removes trailing spaces. We do + * not handle TABs, if any, contained in the quote string. At least not + * yet. + * + * Arguments: q - quote string + * l - a line to process + * i - position in the line to start processing. i = 0 is the + * begining of that line. + */ + int + advance_quote_string(q, l, i) + char *q; + char l[NSTRING]; + int i; + { + int n = 0, j = 0, is = 0, es = 0; + int k, m, p, adv; + char qs[NSTRING] = {'\0'}; + if(!q || !*q) + return(0); + for (p = strlen(q); (p > 0) && (q[p - 1] == ' '); p--, es++); + if (!p){ /* string contains only spaces */ + for (k = 0; ISspace(l[i + k]); k++); + k -= k % es; + return k; + } + for (is = 0; ISspace(q[is]); is++); /* count initial spaces */ + for (m = 0 ; is + m < p ; m++) + qs[m] = q[is + m]; /* qs = quote string without any space at the end */ + /* advance as many spaces as there are at the begining */ + for (k = 0; ISspace(l[i + j]); k++, j++); + /* now find the visible string in the line */ + for (m = 0; qs[m] && l[i + j] == qs[m]; m++, j++); + if (!qs[m]){ /* no match */ + /* + * So far we have advanced at least "is" spaces, plus the visible + * string "qs". Now we need to advance the trailing number of + * spaces "es". If we can do that, we have found the quote string. + */ + for (p = 0; ISspace(l[i + j + p]); p++); + adv = advance_quote_string(q, l, i + j + ((p < es) ? p : es)); + n = ((p < es) ? 0 : es) + k + m + adv; + } + return n; + } + + /* + * This function returns the effective length in screen of the quote + * string. If the string contains a TAB character, it is added here, if + * not, the length returned is the length of the string + */ + int strlenis(char *qstr) + { + int i, rv = 0; + for (i = 0; qstr && qstr[i]; i++) + rv += ((qstr[i] == TAB) ? (~rv & 0x07) + 1 : 1); + return rv; + } + + int + is_indent (word, plb) + char word[NSTRING]; + int plb; + { + int i = 0, finished = 0, c, nxt, j, k, digit = 0, bdigits = -1, alpha = 0; + + if (!word || !word[0]) + return i; + + for (i = 0, j = 0; ISspace(word[i]); i++, j++); + while ((i < NSTRING - 2) && !finished){ + switch (c = now(word,i)){ + case NBSP: + case TAB : + case ' ' : for (; ISspace(word[i]); i++); + if (!is_indent_char(now(word,i))) + finished++; + break; + + case '+' : + case '.' : + case ']' : + case '*' : + case '}' : + case '-' : + case RPAREN: + nxt = next(word,i); + if ((c == '.' && allowed_after_period(nxt) && alpha) + || (c == '*' && allowed_after_star(nxt)) + || (c == '}' && allowed_after_braces(nxt)) + || (c == '-' && allowed_after_dash(nxt)) + || (c == '+' && allowed_after_dash(nxt)) + || (c == RPAREN && allowed_after_parenth(nxt)) + || (c == ']' && allowed_after_parenth(nxt))) + i++; + else + finished++; + break; + + default : if (is_a_digit(c) && plb){ + if (bdigits < 0) + bdigits = i; /* first digit */ + for (k = i; is_a_digit(now(word,k)); k++); + if (k - bdigits > 2){ /* more than 2 digits? */ + i = bdigits; /* too many! */ + finished++; + } + else{ + if(allowed_after_digit(now(word,k),word,k)){ + alpha++; + i = k; + } + else{ + i = bdigits; + finished++; + } + } + } + else + finished++; + break; + + } + } + if (i == j) + i = 0; /* there must be something more than spaces in an indent string */ + return i; + } + + int + get_indent_raw_line(char *q, char *GLine, char *buf, int buflen, int k, int plb) + { + int i, j; + char testline[1024]; + + if(k > 0){ + for(j = 0; GLine[j] != '\0'; j++){ + testline[j] = GLine[j]; + testline[j+1] = '\0'; + if(strlenis(testline) >= strlenis(buf)) + break; + } + k = ++j; /* reset k */ + } + i = is_indent(GLine+k, plb); + + for (j = 0; j < i && j < buflen && (buf[j] = GLine[j + k]); j++); + buf[j] = '\0'; + + return i; + } + diff -rc alpine-2.00/pith/osdep/color.h alpine-2.00.I.USE/pith/osdep/color.h *** alpine-2.00/pith/osdep/color.h 2008-03-24 12:35:31.000000000 -0700 --- alpine-2.00.I.USE/pith/osdep/color.h 2011-02-07 20:33:42.000000000 -0800 *************** *** 43,48 **** --- 43,69 ---- #define PSC_REV 0x2 #define PSC_RET 0x4 /* return an allocated copy of previous color */ + /* + * struct that will help us determine what the quote string of a line + * is. The "next" field indicates the presence of a possible continuation. + * The idea is that if a continuation fails, we free it and check for the + * remaining structure left + */ + + typedef enum {qsNormal, qsString, qsWord, qsChar, + qsGdb, qsProg, qsText} QStrType; + + typedef struct QSTRING { + char *value; /* possible quote string */ + QStrType qstype; /* type of quote string */ + struct QSTRING *next; /* possible continuation */ + } QSTRING_S; + + #define UCH(c) ((unsigned char) (c)) + #define NBSP UCH('\240') + #define ISspace(c) (UCH(c) == ' ' || UCH(c) == TAB || UCH(c) == NBSP) + + /* * MATCH_NORM_COLOR means that the color that is set to this value *************** *** 92,97 **** --- 113,123 ---- char *pico_get_last_bg_color(void); char *color_to_canonical_name(char *); int pico_count_in_color_table(void); + int is_indent(char *, int); + int get_indent_raw_line (char *, char *, char *, int, int, int); + int line_isblank(char *, char *, char *, char *, int); + int strlenis(char *); + int value_is_space(char *); #endif /* PITH_OSDEP_COLOR_INCLUDED */ diff -rc alpine-2.00/pith/osdep/domnames.c alpine-2.00.I.USE/pith/osdep/domnames.c *** alpine-2.00/pith/osdep/domnames.c 2006-11-17 18:46:41.000000000 -0800 --- alpine-2.00.I.USE/pith/osdep/domnames.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 51,57 **** char **alias; char *maybe = NULL; ! gethostname(hname, MAX_ADDRESS); he = gethostbyname(hname); hostname[0] = '\0'; --- 51,70 ---- char **alias; char *maybe = NULL; ! if (gethostname(hname, MAX_ADDRESS)) hname[0] = 0xff; ! /* sanity check of hostname string */ ! for (dn = hname; (*dn > 0x20) && (*dn < 0x7f); ++dn); ! if (*dn) { /* only if invalid string returned */ ! #if 0 ! hostname[0] = domainname[0] = '\0'; ! #else ! /* Contrary to the comments above, the UNIX code does not expect ! these strings to be blank. ! */ ! strcpy (hostname, (strcpy (domainname,"unknown"))); ! #endif ! return; ! } he = gethostbyname(hname); hostname[0] = '\0'; diff -rc alpine-2.00/pith/osdep/hostname.c alpine-2.00.I.USE/pith/osdep/hostname.c *** alpine-2.00/pith/osdep/hostname.c 2006-12-11 10:06:32.000000000 -0800 --- alpine-2.00.I.USE/pith/osdep/hostname.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 47,53 **** { #if HAVE_GETHOSTNAME ! return(gethostname(hostname, size)); #elif HAVE_UNAME --- 47,57 ---- { #if HAVE_GETHOSTNAME ! if(gethostname(hostname, size)) return -1; ! /* sanity check of hostname string */ ! for (*dn = hname; (*dn > 0x20) && (*dn < 0x7f); ++dn); ! if (*dn) strcpy (hostname,"unknown"); ! return 0; #elif HAVE_UNAME diff -rc alpine-2.00/pith/pattern.c alpine-2.00.I.USE/pith/pattern.c *** alpine-2.00/pith/pattern.c 2008-07-14 11:01:54.000000000 -0700 --- alpine-2.00.I.USE/pith/pattern.c 2011-02-07 20:33:41.000000000 -0800 *************** *** 1755,1761 **** SortOrder def_sort; int def_sort_rev; ! if(decode_sort(p, &def_sort, &def_sort_rev) != -1){ action->sort_is_set = 1; action->sortorder = def_sort; action->revsort = (def_sort_rev ? 1 : 0); --- 1755,1761 ---- SortOrder def_sort; int def_sort_rev; ! if(decode_sort(p, &def_sort, &def_sort_rev, 0) != -1){ action->sort_is_set = 1; action->sortorder = def_sort; action->revsort = (def_sort_rev ? 1 : 0); *************** *** 5482,5487 **** --- 5482,5496 ---- break; case '#': + #ifndef _WINDOWS + if(!struncmp(patfolder, "#md/", 4) + || !struncmp(patfolder, "#mc/", 4)){ + maildir_file_path(patfolder, tmp1); + if(!strcmp(patfolder, stream->mailbox)) + match++; + break; + } + #endif if(!strcmp(patfolder, stream->mailbox)) match++; *************** *** 7894,7900 **** int we_cancel = 0, width; CONTEXT_S *save_context = NULL; char buf[MAX_SCREEN_COLS+1], sbuf[MAX_SCREEN_COLS+1]; ! char *save_ref = NULL; #define FILTMSG_MAX 30 if(!stream) --- 7903,7909 ---- int we_cancel = 0, width; CONTEXT_S *save_context = NULL; char buf[MAX_SCREEN_COLS+1], sbuf[MAX_SCREEN_COLS+1]; ! char *save_ref = NULL, *save_dstfldr = NULL, *save_dstfldr2 = NULL; #define FILTMSG_MAX 30 if(!stream) *************** *** 7928,7933 **** --- 7937,7952 ---- if(F_OFF(F_QUELL_FILTER_MSGS, ps_global)) we_cancel = busy_cue(buf, NULL, 0); + #ifndef _WINDOWS + if(!struncmp(dstfldr, "#md/", 4) || !struncmp(dstfldr, "#mc/", 4)){ + char tmp1[MAILTMPLEN]; + maildir_file_path(dstfldr, tmp1); + save_dstfldr2 = dstfldr; + save_dstfldr = cpystr(tmp1); + dstfldr = save_dstfldr; + } + #endif + if(!is_absolute_path(dstfldr) && !(save_context = default_save_context(ps_global->context_list))) save_context = ps_global->context_list; *************** *** 7991,7996 **** --- 8010,8020 ---- if(we_cancel) cancel_busy_cue(buf[0] ? 0 : -1); + if(save_dstfldr){ + fs_give((void **)&save_dstfldr); + dstfldr = save_dstfldr2; + } + return(buf[0] != '\0'); } diff -rc alpine-2.00/pith/pineelt.h alpine-2.00.I.USE/pith/pineelt.h *** alpine-2.00/pith/pineelt.h 2008-07-09 22:01:13.000000000 -0700 --- alpine-2.00.I.USE/pith/pineelt.h 2011-02-07 20:33:47.000000000 -0800 *************** *** 40,45 **** --- 40,47 ---- PINETHRD_S *pthrd; PARTEX_S *exceptions; ICE_S *ice; + char *firsttext; + char *firsttextraw; /* per-message pine state bits */ unsigned int hidden:1; unsigned int excluded:1; diff -rc alpine-2.00/pith/pine.hlp alpine-2.00.I.USE/pith/pine.hlp *** alpine-2.00/pith/pine.hlp 2008-08-22 17:07:05.000000000 -0700 --- alpine-2.00.I.USE/pith/pine.hlp 2011-02-07 20:33:48.000000000 -0800 *************** *** 88,93 **** --- 88,94 ---- ALPINE_VERSION ALPINE_REVISION ALPINE_COMPILE_DATE + ALPINE_PATCHLEVEL ALPINE_TODAYS_DATE C_CLIENT_VERSION _LOCAL_FULLNAME_ *************** *** 158,163 **** --- 159,172 ----

+ This version was modified from its original source code. More information + about some of the patches applied to this version can be found here. + + The patch level of this version, including creation date of the patch is: + . + + +

Alpine is an "Alternatively Licensed Program for Internet News and Email" produced by the University of Washington. It is intended to be an easy-to-use program for *************** *** 919,924 **** --- 928,1016 ---- <End of Configuration Notes> + ====== h_patches ====== + + + Information on patches added to this release + + +

Information on patches added to this release

+

+ This version of Alpine has been modified by including patches from + + http://staff.washington.edu/chappa/alpine/. These patches include + new features and bug fixes. More complete information on each patch + included in this version can be found in the web. + +

If you have any problems with this release of Alpine, please contact + Eduardo Chappa <chappa@washington.edu>. + +

The list of patches included in this release are: +

New Features: + +

    +
  • Enhanced Fancy thread interface. (more) + + + +
  • Patch that gives support for the maildir driver in Alpine (more) + +
  • Rules patch, to make Alpine flexible. (more) +
  • Send mail from the command Line. (more) +
  • Add a few more options when replying to a message (more) +
  • Choose a role when composing a message from a mailto: link (more) +
  • Alpine justifies paragraphs with more than one level of indentation. (more) +
  • Patch to write accents and foreign characters (more) +
  • Change your From Header without any effort! (more) +
  • Tab checks folders on cycles (more) +
  • Patch for Pico to update its status line according to cursor movements (more) +
  • Reuse your old search pattern to create a new one (more) +
  • Paint special text in the body of a message in any color. (more) +
  • Select messages by the content of an arbitrary header. (more) +
  • Remove incorrect passwords from memory (more) +
  • Get the quota report in an IMAP server (more) +
  • Remove text until the end of the file or a message (more) +
  • Get the internal name of a help topic to use it with a x-alpine-help or x-pine-help URL scheme. (more) +
  • Get the number of characters in a composed message (more) +
  • Configure ignoring change in size of a message (more) +
  • Add one more token to a sending filter (more) +
  • Make Alpine preserve To: and Cc: fields (more) +
  • Allow non utf-8 piped input in Alpine (more) +
  • Allow errors in base64 encoding (more) +
  • Get the name of the slow server (more) +
  • Color the text in the folder list screen (more) +
  • Color text in the index screen (more) +
  • Avoid clearing the screen when executing a display filter (more) +
  • Cache OPENINGTEXT information (more) +
  • Recognize multiline URLs (more) +
  • Recognize the nametemplate token in a mailcap file (more) +
  • Create new threads even when replying to a message (more) +
+ +

Bug Fixes: + +

    +
  • Fix a bug that makes Alpine not to give a warning if the Newsgroup header is present (more) +
  • Fix a bug in Pico which makes it not update the screen (more) +
  • Fix a bug in Pico and Pilot that makes them crash for bad locale information (more) +
  • Fix a bug in Alpine that makes it not to set a flag in a filtered message (more) +
  • Fix a bug that makes Alpine crash when suspending it (more) +
  • Fix a bug that makes Alpine crash when opening a unix type folder (more) +
  • Force update of the index screen after adding an address to the addressbook (more) +
  • Fix a bug that makes Alpine ignore the week day of a preftime token (more) +
  • Fix the type of a .docx document (more) +
  • Parse environment variables correctly (more) +
  • Do not reopen a folder that is meant to be closed (more) +
  • Fix a bug that makes Alpine crash when the screen is resized (more) +
  • Fix a bug in the threading algorithm (more) +
  • Fix a memory leak in Alpine (more) +
  • Fix a bug that sends Alpine in an infinite loop (more) +
  • Fix a bug that makes Alpine not give a correct new mail message (more) +
  • Fix a bug that makes Alpine not show a login prompt (more) +
  • Patch by Mark Crispin, which fixes a bug which makes Alpine not generate correct headers for servers that return UTF-8 names. (more) +
+ + ====== h_news_legal ====== *************** *** 3035,3043 **** --- 3127,3137 ----
  • FEATURE:
  • FEATURE:
  • FEATURE: +
  • FEATURE:
  • FEATURE:
  • FEATURE:
  • FEATURE: +
  • FEATURE:
  • FEATURE:
  • FEATURE:
  • FEATURE: *************** *** 3120,3125 **** --- 3214,3220 ----
  • FEATURE:
  • FEATURE:
  • FEATURE: +
  • FEATURE:
  • FEATURE:
  • FEATURE:
  • FEATURE: *************** *** 3136,3142 **** --- 3231,3239 ----
  • FEATURE:
  • FEATURE:
  • FEATURE: +
  • FEATURE:
  • FEATURE: +
  • FEATURE:
  • FEATURE:
  • FEATURE:
  • FEATURE: *************** *** 3402,3407 **** --- 3499,3505 ----
  • OPTION:
  • OPTION:
  • OPTION: +
  • OPTION:
  • OPTION:
  • OPTION: Print-Font-Char-Set
  • OPTION: Print-Font-Name *************** *** 3430,3438 **** --- 3528,3538 ----
  • OPTION:
  • OPTION:
  • OPTION: Signature Color +
  • OPTION: Special Text Color
  • OPTION:
  • OPTION:
  • OPTION: +
  • OPTION:
  • OPTION:
  • OPTION:
  • OPTION: *************** *** 4058,4063 **** --- 4158,4172 ---- key will Exit the Help system altogether.

    + The "N" command will tell you the internal name of the help text you are + reading each time, so that you can send this name in the text of a message + and create a direct link to that internal help using the x-pine-help URL + scheme. For example, the direct link to this item is + x-pine-help:h_special_help_nav. If you add this text to a message, then + a person using Pine to read such message would have a direct link to this + help text. + +

    When you are finished reading this help text, you can press the F3 function *************** *** 5316,5321 **** --- 5425,5583 ---- <End of help on this topic> + ======= h_thread_index_sort_arrival ======= + + + SORT OPTION: Arrival + + +

    SORT OPTION: Arrival

    + + The Arrival sort option arranges threads according to the last + time that a message was added to it. In this order the last thread + contains the most recent message in the folder. + +

    + <End of help on this topic> + + + ======= h_thread_index_sort_date ======= + + + SORT OPTION: Date + + +

    SORT OPTION: Date

    + + The Date sort option in the THREAD INDEX screen sorts + threads by the date in which messages were sent. The thread containing the + last message in this order is displayed last. +

    + <End of help on this topic> + + + ======= h_thread_index_sort_subj ======= + + + SORT OPTION: Subject + + +

    SORT OPTION: Subject

    + + The Subject sort option has not been defined yet. + +

    + <End of help on this topic> + + + ======= h_thread_index_sort_ordsubj ======= + + + SORT OPTION: OrderedSubject + + +

    SORT OPTION: OrderedSubject

    + + The OrderedSubject sort option in the THREAD INDEX screen is + the same as sorting by Subject. + +

    + <End of help on this topic> + + + ======= h_thread_index_sort_thread ======= + + + SORT OPTION: Thread + + +

    SORT OPTION: Thread

    + + The Thread sort option in the THREAD INDEX screen sorts all + messages by the proposed algorithm by Crispin and Murchison. In this + method of sorting once threads have been isolated they are sorted by the + date of their parents, or if that is missing, the first message in that + thread. + +

    + <End of help on this topic> + + + ======= h_thread_index_sort_from ======= + + + SORT OPTION: From + + +

    SORT OPTION: From

    + + The From sort option has not been defined yet. + +

    + <End of help on this topic> + + + ======= h_thread_index_sort_size ======= + + + SORT OPTION: Size + + +

    SORT OPTION: Size

    + + The Size sort option has not been defined yet. + +

    + <End of help on this topic> + + + ======= h_thread_index_sort_score ======= + + + SORT OPTION: Score + + +

    SORT OPTION: Score

    + + The Score sort option means that threads are sorted according to + the maximum score of a message in that thread. A thread all of whose + messages contain a smaller score than a message in some other thread is + placed in an earlier place in the list of messages for that folder; that + is, threads with the highest scores appear at the bottom of the index + list. + +

    + <End of help on this topic> + + + ======= h_thread_index_sort_to ======= + + + SORT OPTION: To + + +

    SORT OPTION: To

    + + The To sort option has not been defined yet. + +

    + <End of help on this topic> + + + ======= h_thread_index_sort_cc ======= + + + SORT OPTION: Cc + + +

    SORT OPTION: Cc

    + + The Cc sort option has not been defined yet. + +

    + <End of help on this topic> + + ======= h_index_cmd_whereis ======= *************** *** 6503,6508 **** --- 6765,6810 ---- "type the character ^".

    + This version of Alpine contains an enhanced algorithm for justification, + which allows you to justify text that contains more complicated quote + strings. This algorithm is based on pragmatics, rather than on a theory, + and seems to work well with most messages. Below you will find technical + information on how this algorithm works. + +

    + When justifying, Alpine goes through each line of the text and tries to + determine for each line what the quote string of that line is. The quote + string you provided is always recognized. Among other characters + recognized is ">". + +

    + Some other constructions of quote strings are recognized only if they + appear enough in the text. For example "Peter :" is only + recognized if it appears in two consecutive lines. + +

    + Additionaly, Alpine recognizes indent-strings and justifies text in a + paragraph to the right of indent-string, padding with spaces if necessary. + An indent string is one which you use to delimit elements of a list. For + example, if you were to write a list of groceries, one may write: + +

      +
    • Fruit +
    • Bread +
    • Eggs +
    + +

    + In this case the character "*" is the indent-string. Aline + recognizes numbers (0, 1, 2.5, etc) also as indent-strings, and certain + combinations of spaces, periods, and parenthesis. In any case, numbers are + recognized ONLY if the line preceeding the given line is empty or + ends in one of the characters "." or ":". + In addition to the explanation of what constitutes a paragraph above, a + new paragraph is recognized when an indent-string is found in it (and + validated according to the above stated rules). + +

    <End of help on this topic> *************** *** 18069,18074 **** --- 18371,18377 ---- "" option, in the "" option, in signature files, + in the "new-rules" option, in template files used in "roles", and in the folder name that is the target of a Filter Rule. *************** *** 18081,18087 ****

    !

    Tokens Available for all Cases (except Filter Rules)

    SUBJECT
    --- 18384,18390 ----

    !

    Tokens Available for all Cases (except Filter Rules or in some cases for new-rules)

    SUBJECT
    *************** *** 18114,18119 **** --- 18417,18431 ---- For example, "mailbox@domain". +
    ADDRESSTO
    +
    + This is similar to the "TO" token, only it is always the + email address of all people listed in the TO: field of the messages. Addresses + are separated by a blank space. Example, "mailbox@domain" when + the e-mail message contains only one person in the To: field, or + "peter@flintstones.com president@world.com". +
    +
    MAILBOX
    This is the same as the "ADDRESS" except that the *************** *** 18161,18166 **** --- 18473,18487 ---- message's "Cc:" header field.
    +
    ADDRESSCC
    +
    + This is similar to the "CC" token, only it is always the + email address of all people listed in the Cc: field of the messages. Addresses + are separated by a blank space. Example: "mailbox@domain" when + the e-mail message contains only one person in the Cc: field, or + "peter@flintstones.com president@world.com". +
    +
    RECIPS
    This token represents the personal names (or email addresses if the names *************** *** 18169,18174 **** --- 18490,18503 ---- the message's "Cc:" header field.
    +
    ADDRESSRECIPS
    +
    + This token represent the e-mail addresses of the people in the To: and + Cc: fields, exactly in that order separated by a space. It is almost obtained + by concatenating the ADDRESSTO and ADDRESSCC tokens. +
    + +
    NEWSANDRECIPS
    This token represents the newsgroups from the *************** *** 18650,18655 **** --- 18979,18992 ----

    +
    SIZETHREAD
    +
    + This token represents the total size of the thread for a collapsed thread + or the size of the branch for an expanded thread. The field is omitted for + messages that are not top of threads nor branches and it defaults to + the SIZE token when your folders is not sorted by thread. +
    +
    SIZENARROW
    This token represents the total size, in bytes, of the message. *************** *** 19265,19270 **** --- 19602,19679 ----

    +

    Tokens Available Only for New-Rules

    + +
    +
    FOLDER
    +
    + Name of the folder where the rule will be applied +
    +
    + +
    +
    COLLECTION
    +
    + Name of the collection list where the rule will be applied. +
    +
    + +
    +
    ROLE
    +
    + Name of the Role used to reply a message. +
    +
    + +
    +
    BCC
    +
    + Not implemented yet, but it will be implemented in future versions. It will + be used for compose + reply + forward + rules. +
    +
    + +
    +
    LCC
    +
    + This is the value of the Lcc: field at the moment that you start the composition. +
    +
    + +
    +
    FORWARDFROM
    +
    + This corresponds to the personal name (or address if there's no personal + name) of the person who sent the message that you are forwarding. +
    +
    + +
    +
    FORWARDADDRESS
    +
    + This is the address of the person that sent the message that you + are forwarding. +
    +
    + + + + +
    +
    FLAG
    +
    + A string containing the value of all the flags associated to a specific + message. The possible values of allowed flags are "*" for Important, "N" + for recent or new, "U" for unseen or unread, "R" for seen or read, "A" for + answered and "D" for deleted. See an example of its use in the + new rules explanation and example help. +
    +
    + +

    Token Available Only for Templates and Signatures

    *************** *** 21253,21258 **** --- 21662,21763 ---- <End of help on this topic> + ====== h_config_maildir_location ====== + + + OPTION: <!--#echo var="VAR_maildir-location"--> + + +

    OPTION:

    + +

    + This option should be used only if you have a Maildir folder which you + want to use as your INBOX. If this is not your case (or don't know what + this is), you can safely ignore this option. + +

    + This option overrides the default directory Pine uses to find the location of + your INBOX, in case this is in Maildir format. The default value of this + option is "Maildir", but in some systems, this directory could have been + renamed (e.g. to ".maildir"). If this is your case use this option to change + the default. + +

    + The value of this option is prefixed with the "~/" string to determine the + full path to your INBOX. + +

    + You should probably read a few tips that + teach you how to configure your maildir for optimal performance. This + version also has support for the + Courier style file system when a maildir collection is accessed locally. + +

    +

    + <End of help on this topic> + + + ====== h_config_maildir ===== + + + Maildir Support + + +

    Maildir Support

    + + This version of Alpine has been enhanced with Maildir support. This text is + intended to be a reference on its support. +

    + + A Maildir folder is a directory that contains three directories called + cur, tmp and new. A program that delivers mail (e.g. postfix) will put new + mail in the new directory. A program that reads mail will look for for old + messages in the cur directory, while it will look for new mail in the new + directory. +

    + + In order to use maildir support it is better to set your inbox-path to the + value "#md/inbox" (without quotes). This assumes that your mail + delivery agent is delivering new mail to ~/Maildir/new. If the directory + where new mail is being delivered is not called "Maildir", you can set the + name of the subdirectory of home where it is being delivered in the configuration + variable. Most of the time you will not have to worry about the + variable, because it will probably be set by your + administrator in the pine.conf configuration file. +

    + + One of the advantages of the Maildir support of this version of Alpine is + that you do not have to stop using folders in another styles (mbox, mbx, + etc.). This is desirable since the usage of a specific mail storage system + is a personal decision. Folders in the maildir format that are part of the + Mail collection will be recognized without any extra configuration of your + part. If your mail/ collection is located under the mail/ directory, then + creating a new maildir folder in this collection is done by pressing "A" + and entering the string "#driver.md/mail/newfolder". Observe that adding a + new folder as "newfolder" may not create such folder in maildir format. + +

    + If you would like to have all folders created in the maildir format by + default, you do so by adding a Maildir Collection. In order to convert + your current mail/ collection into a maildir collection, edit the + collection and change the path variable from "mail/" to + "#md/mail". In a maildir collection folders of any other format + are ignored. + +

    Finally, This version also has + support for the Courier style file system + when a maildir collection is accessed locally. + +

    +

    + <End of help on this topic> + + ====== h_config_literal_sig ===== *************** *** 22015,22020 **** --- 22520,22564 ---- <End of help on this topic> + ====== h_config_thread_sort_key ===== + + + OPTION: <!--#echo var="VAR_thread-sort-key--> + + +

    OPTION:

    + + This option determines the order in which threads will be displayed. You + can choose from the options listed below. Each folder is sorted in one of + the sort orders displayed below first, then the thread containing the last + message of that sorted list is put at the end of the index. All messages + of that thread are "removed" from the sorted list and the + process is repeated with the remaining messages in that list. + +

    +

    + +

    Each type of sort may also be reversed. Normal default is by + "Thread". + +

    +

    + <End of help on this topic> + + ====== h_config_other_startup ===== *************** *** 22285,22290 **** --- 22829,23726 ---- <End of help on this topic> + ====== h_config_procid ===== + + + Token: PROCID + + +

    TOKEN: PROCID explained

    + +

    + The PROCID token is a way in which the user and the program can differentiate + between different parts of a program. It allows the user to tell the + program when to use a specific rule, and only use it at that specific + moment. + +

    The normal way in which this is done is by adding a new configuration + variable. The idea behind the PROCID token is that instead of adding a new + configuration variable (which means the user has to go through more + configuration variables just to tune the program to his liking), we reuse + an old variable and let the user look inside that variable for the desired + behavior, which is actually set by setting the PROCID token. + +

    + Consider the following examples for forward-rules: + +

    + _ROLE_ == {work} => _SUBJECT_ := _COPY_{[tag] _SUBJECT_} + +

    + and + +

    + _ROLE_ == {work} => _LCC_ := _TRIM_{_FORWARDFROM_ <_FORWARDADDRESS_>} + +

    + both are triggered by the same condition. Since both are configured in the + same variable, only one of them will be executed all the time (whichever + is first). Therefore in order to differentiate, we add a _PROCID_ token. + So, for example, the first example above will be executed only when we are + determining the subject. In this case, the following rule will accomplish + this task + +

    + _PROCID_ == {fwd-subject} && _ROLE_ == {work} => _SUBJECT_ := _COPY_{[tag] _SUBJECT_} + +

    + In this case, this rule will be tested fully only when we are determining + the subject line of a forwarded message, not otherwise. + +

    + It is wise to add the _PROCID_ token as the first condition in a rule, so + that other conditions will not be tested in a long list of rules. + +

    <End of help on this topic> + + + ====== h_config_compose_rules ===== + + + OPTION: <!--#echo var="VAR_compose-rules"--> + + +

    OPTION:

    + +

    At this time, this option is used to generate values for signature + files that is not possible to do with the use of + roles. + +

    For example, you can have a rule like:
    + _TO_ >> {Peter Flinstones} => _SIGNATURE_{~/.petersignature} + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + ====== h_config_forward_rules ===== + + + OPTION: <!--#echo var="VAR_forward-rules"--> + + +

    OPTION:

    + +

    This option has several uses. This feature uses the PROCID function + to identify different features of forwarding. You can read more about PROCID + by following this link. + +

    If you want to edit the subject of a forwarded message, use the + PROCID fwd-subject. For example you could have a rule like + +

    + _ROLE_ == {admin} && _SUBJECT_ !> {[tag] } => _COPY_{[tag] _SUBJECT_} + +

    Another way in which this option can be used, is to trim the values of + some fields. For this application the PROCID is fwd-lcc. For + example it can be used in the following way: + +

    + _ROLE_ == {work} => _LCC_ := _TRIM_{_FORWARDFROM_ <_FORWARDADDRESS_>} + +

    Other functions that can be used in this option are _EXEC_ and _REXTRIM_. + +

    You can also use the _EXEC_ function. The documentation for this function + is in the + + help text. + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + ====== h_config_index_rules ===== + + + OPTION: <!--#echo var="VAR_index-rules"--> + + +

    OPTION:

    + +

    This option is used to supersede the value of the option for specific folders. In + this form you can have different index-formats for different folders. For + example an entry here may be: + +

    + _FOLDER_ == {INBOX} => _INDEX_{IMAPSTATUS DATE FROM(33%) SIZE SUBJECT(67%)} + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + ====== h_config_pretty_command ===== + + + Pretty-Command Explained + + +

    Pretty Command Explained

    + +

    This text explains how to encode keys so that they will be recognized + by Alpine in the _PKEY_ token. Most direct keystrokes are recognized in the + same way. For example, the key ~ is recognized by the same character. The + issue is how control, or functions keys are recognized. The internal code + is most times easy to find out. If the key you want to use is not already + recognized by Alpine simply press it. Alpine will print its code. For example, + the return key is not recognized in this screen, so if you press it, you + will see the following message. + +

    [Command "RETURN" not defined for this screen. Use ? for help] + +

    from here you can guess that the code for the return command is + RETURN. You can try other commands, like Control-C, the TAB key, F4, etc. + to see their codes. + +

    <End of help on this topic> + + + ====== h_config_key_macro_rules ===== + + + OPTION: <!--#echo var="VAR_key-definition-rules"--> + + +

    OPTION:

    + +

    This option can be used to define macros, that is, to define a key that + when pressed executes a group of predetermined keystrokes. Since Alpine is + a menu driven program, sometimes the same key may have different meanings + in different screens, so a global redefinition of a key although possible + is not advisable. + +

    Always use the _SCREEN_ token as defined below.. You have been + warned! + +

    In each screen, every time you press a recognized key a command is + activated. In order to understand this feature, think of commands instead + of keystrokes. For example, you can think of the sort by thread command. + This command is associated to the keystrokes $ and h. You may want to + associate this command to a specific keystroke, like ~, so every time you + press the ~ key, Alpine understand the $ and h keystrokes, which activates + the sort by thread command. + +

    Therefore, in order to use this option you must think of three + components. The screen where you will use the macro, the keystroke you + want to use and the set of keystrokes used by Alpine to accomplish the task + you want to accomplish. We will talk about these three components in what + follows. + +

    First you must decide in which screen the macro will be used. This + feature is currently only available for the screen where your messages + are listed in index form (MESSAGE INDEX), + the screen where your message is displayed + (MESSAGE TEXT) and the screen where the list of + folders is displayed (FOLDER LIST). The + internal names of these screens for this patch are "index", + "text" and + "folder" respectively. Please note that the internal names are + all in lowercase are are case sensitive. + +

    In order to define the screen, you use the _SCREEN_ token, so for + example, you can write _SCREEN_ == {index}. + +

    Second you must think of which key you will use to activate the macro. + Here you can use any key of your choice. The token you use to designate a + key is the _PKEY_ token (PKEY stands for "pressed key"). For + example you could use _PKEY_ == {~}, to designate the "~" + character to activate the command. Some keystrokes (like control, or + function keys) are encoded in special ways. You should read the + full explanation on how to find + out the encoding for each keystroke. + +

    Last, you must think of the list of keys you will use to accomplish + the task you want Alpine to perform. Say for example you want to have the + folder sorted by thread. That means you want Aline to execute the keys + "$" and "h". You use the _COMMAND_ function to specify + this. The syntax in this case is _COMMAND_{$,h}. + +

    Observe that in the above example the different inputs are separated + by commas. This is the standard way in which the + command works from + the command line. Due to restrictions in the way Alpine works, a comma is a + special character, which when added to a configuration option like this + will cause the configuration to split into several lines in the + configuration screen. This has the effect of producing several + configuration options, all of which are incorrect. This is undesirable + because what you want is to have it all in one line. In order to force the + configuration into one line you must quote the comma. The best way to + accomplish this is by quoting the full definition of the rule. For + example. + +

    + "_SCREEN_ == {index} && _PKEY_ == {~} => _COMMAND_{$,h}" + +

    Another way to accomplish the same effect is by quoting the command and + not using quotes for the full command, nor commas to separate the + keystrokes in the command, for example + +

    + _SCREEN_ == {index} && _PKEY_ == {~} => _COMMAND_{"$h"} + +

    For more information on how to define the argument of the _COMMAND_ + token see the help of + . + +

    Because the $ command can also be used as the first character in the + definition of an environemnt variable, no expansion of environment variables + is done when parsing this variable. The $ character does not need quoting + and quoting it will make Alpine fail to produce the correct result. + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + ====== h_config_replace_rules ===== + + + OPTION: <!--#echo var="VAR_replace-rules"--> + + +

    OPTION:

    + +

    This option is used to have Alpine print different values for specific + tokens in the . For example you + can replace strings like "To: newsgroup" by your name. + +

    Here are examples of possible rules:
    + _FOLDER_ != {sent-mail} && _NICK_ != {} => _FROM_ := _REPLACE_{_FROM_ (_NICK_)} + +

    or if you receive messages with tags that contain arbitrary numbers, and + you want them removed from the index (but not from the subject), use a rule + like the following
    + _FOLDER_ == {INBOX} => _SUBJECT_ := _REXTRIM_{\[some-tag-here #[0-9].*\]} + +

    You can also use this configuration option to remove specific strings of + the index display screen, so that you can trim unnecessary information in + your index, like the reply leadin string in the OPENINGTEXTNQ token of the index. +
    + _FOLDER_ == {mailing-list} => _OPENINGTEXTNQ_ := _REXTRIM_{On.*wrote: } + +

    or if you receive messages with tags that contain arbitrary numbers, and + you want them removed from the index (but not from the subject), use a rule + like the following
    + +

    You can also use the _EXEC_ function. The documentation for this function + is in the + + help text. + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + ====== h_config_reply_leadin_rules ===== + + + OPTION: <!--#echo var="VAR_reply-leadin-rules"--> + + +

    OPTION:

    + +

    This option is used to have Alpine generate a different + string dependent either on + the person you are replying to, or the folder where the message is being + replied is in, or both. + +

    Here there are examples of how this can be used. One can use the definition + below to post to newsgroups and the pine-info mailing list, say: +

    + _FOLDER_ << {pine-info;_NEWS_} => _REPLY_{*** _FROM_ _ADDRESS_("_FROM_" "" "(_ADDRESS_) ")wrote in_NEWS_("" " the" "") _FOLDER_ _NEWS_("" "list " "")_SMARTDATE_("Today" "today" "on _LONGDATE_"):} + +

    Here there is an example that one can use to change the reply indent string + to reply people that speak spanish. +

    + _FROM_{Condorito;Quico} => _REPLY_{*** _FROM_ (_ADDRESS_) escribió _SMARTDATE_("Today" "hoy" "en _LONGDATE_"):} + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + ====== h_config_resub_rules ===== + + + OPTION: <!--#echo var="VAR_reply-subject-rules"--> + + +

    OPTION:

    + +

    This option is used to have Alpine generate a different subject when + replying rather than the one Alpine would generate automatically. + +

    Here there are a couple of examples about how to use this + configuration option: + +

    In order to have messages with empty subject to be replied with the message + "your message" use the rule
    +

    _SUBJECT_ == {} => _RESUB_{Re: your message}
    + +

    If you want to trim some parts of the subject when you reply use the + rule
    +

    _SUBJECT_ >> {[one];two} => _SUBJECT_ := _TRIM_{[;];two}
    + +

    this rule removes the brackets "[" and "]" whenever the string "[one]" + appears in it, it also removes the word "two" from it. + +

    Another example where you may want to use this rule is when you + correspond with people that change the reply string from "Re:" + to "AW:" or "Sv:". In this case a rule like
    +

    _SUBJECT_ >> {Sv: ;AW: } => _SUBJECT_ := _TRIM_{Sv: ;AW: }
    +

    + would eliminate undesired strings in replies. + +

    Another interesting use of this option is the use of the _EXEC_ function. + This function takes as an argument a program or a script. This program + must take as the input a file, and write its output to that file. For example, + below is a sample of a script that removes the letter "a" of a file. + +

    + #!/bin/sh
    + sed 's/a//g' $1 > /tmp/mytest
    + mv /tmp/mytest $1
    + 
    + +

    + As you can see this script took "$1" as input file, the sed program + wrote its output to /tmp/mytest, and then the move program moved the file + /tmp/mytest to the input file "$1". This is the kind of behavior + that your program is expected to have. + +

    + The content of the input file ("$1" above) is the value of a token + like _SUBJECT_. In order to indicate this, we use the notation + +

    + _SUBJECT_ := _EXEC_{/path/to/script} + +

    for the action. So for example + +

    + _FOLDER_ := {sent-mail} => _SUBJECT_ := _EXEC_{/path/to/script} + +

    is a valid rule. + +

    You can also use this configuration option to customize reply subjects + according to the sender of the message. + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + ====== h_config_sort_rules ===== + + + OPTION: <!--#echo var="VAR_sort-rules"--> + + +

    OPTION:

    + +

    This option is used to have Alpine sort different folders in different orders + and thus override the value already set in the + configuration option. + +

    Here's an example of the way it can be used. In this case all incoming + folders are mailing lists, except for INBOX, so we sort INBOX by arrival + (which is the default type of sort), but we want all the rest of mailing + lists and newsgroups to be sorted by thread. + +

    + _COLLECTION_ >> {Incoming-Folders;News} && _FOLDER_ != {INBOX} => _SORT_{tHread} + +

    Another example could be
    + _FOLDER_ == {Mailing List} => _SORT_{Reverse tHread} + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + + ====== h_config_save_rules ===== + + + OPTION: <!--#echo var="VAR_save-rules"--> + + +

    OPTION:

    + +

    This option is used to specify which folder should be used to save a + message depending either on the folder the message is in, who the message + is from, or text that the message contains in specific headers (Cc:, + Subject:, etc). + +

    If this option is set and the + configuration + option is also enabled then these definitions will be used to move messages + from your INBOX when exiting Alpine. + +

    Here there are some examples
    + _FLAG_ >> {D} -> Trash
    + _FROM_ == {U2} -> Bono
    + _FOLDER_ == {comp.mail.pine} -> pine-stuff
    + _NICK_ != {} -> _NICK_/_NICK_
    + _DATEISO_ >> {02-10;02-11} -> archive-oct-nov-2002 + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + + ====== h_config_reply_indent_rules ===== + + + OPTION: <!--#echo var="VAR_reply-indent-rules"--> + + +

    OPTION:

    + +

    This option is used to specify which reply-indent-string is to be used + when replying to an e-mail. If none of the rules are successful, the result in + the variable + is used. + +

    The associated function to this configuration option is called "RESTR" (for + REply STRing). Some examples of its use are:
    + _FROM_ == {Your Boss} => _RESTR_{"> "}
    + _FROM_ == {My Wife} => _RESTR_{":* "}
    + _FROM_ == {Perter Flinstone;Wilma Flinstone} => _RESTR_{"_INIT_ > "}
    + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + + ====== h_config_smtp_rules ===== + + + OPTION: <!--#echo var="VAR_smtp-rules"--> + + +

    OPTION:

    + +

    This option is used to specify which SMTP server should be used when + sending a message, if this rule is not defined, or the execution of the rule + results in no server selected, then Alpine will look for + the value from the role that is being used to compose the message. If no smtp + server is defined in that role or you are not using a role, then Alpine will get + the name of the server from the + "" configuration + option according to the rules used in that variable. + +

    The function associated to this configuration option is _SMTP_, an example + of the use of this function is
    + _ADDRESSTO_ == {peter@bedrock.com} => _SMTP_{smtp.bedrock.com} + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + + ====== h_config_startup_rules ===== + + + OPTION: <!--#echo var="VAR_startup-rules"--> + + +

    OPTION:

    + +

    This option is used when a folder is being opened. You can use it to specify its and override + Alpine's global value set for all folders. + +

    An example of the usage of this option is:
    + _FOLDER_ == {Lynx;pine-info;_NEWS_} => _STARTUP_{first-unseen} + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + + ====== h_config_new_rules ===== + + + OPTION: New Rules Explained + + +

    OPTION: New Rules Explained

    + + This is a quite powerful option. Here you can define rules that override + the values of any other option you have set in Alpine. + +

    + For example, you can set your folders to be sorted in a certain way when + you open them (say by Arrival). You may want, however, your newsgroups to + be sorted by thread. The set of "rules" options allows you to + configure this and many other options, including the index-format for + specific folders, the way the subject is displayed in the index screen or + the reply-leadin-string, to name a few. + +

    + Every rule has three parts: a condition, a separator and an action. The + action is what will happen if the condition of the rule is satisified. + +

    + Here is an example: + +

    + _FROM_ == {Fred Flinstone} => _SAVE_{Fred} + +

    + Here the separator is "=>". Whatever is to the left of the separator + is the condition (that is _FROM_ == {Fred Flinstone}) and to the right is + the action (_SAVE_{Fred}). The condition means that the rule will be + applied only if the message that you are reading is from "Fred + Flinstone", and the action will be that you will be offered to save + it in the folder "Fred", whenever you press the letter + "S" to save a message. + +

    + The separator is always "=>", with one exception to be seen + later. But for the most part this will be the only one you will ever need. + +

    + Now let us see how to do it. There are 13 functions already defined for + you. These are: _EXEC_, _INDEX_, _REPLACE_, _REPLY_, _RESUB_, _SAVE_, + _SIGNATURE_, _SORT_, _STARTUP_, _TRIM_, _REXTRIM_, _THREADSTYLE and + _THREADINDEX_. The parameter of a function has to be enclosed between + "{" and "}", so for example you can specify + _SAVE_{saved-messages} as a valid sentence. + +

    + Later in the document you will find examples. Here is a short + description of what each function does: + +

    +

      +
    • _EXEC_ : This function takes as an argument a program. This program + gets as the input a file and must rewrite its output to that file, which + is then taken as the value to replace from the contents of that file. You + can use this function with + , + and + . + See the help of those options for examples of how to use this function + and configure these rules. +
       
      +
    • _INDEX_ : This function takes as an argument an index-format, and + makes that the index-format for the specified folder. +
       
      +
    • _REPLACE_ : This function replaces the subject/from of the given e-mail by + another subject/from only when displaying the index. +
       
      +
    • _REPLY_ : This function takes as an argument a definition of a + reply-leadin-string and makes this the reply-leading-string of the + specified folder or person. +
       
      +
    • _RESTR_ : This function takes as an argument the value of the + reply-indent-string to be used to answer the message being replied to. +
       
      +
    • _RESUB_ : This function replaces the subject of the given e-mail by + another subject only when replying to a message. +
       
      +
    • _SAVE_ : The save function takes as an argument the name of a + possibly non existing folder, whenever you want to save a message, that + folder will be offered for you to save. +
       
      +
    • _SIGNATURE_ : This function takes as an argument a signature file and + uses that file as the signature for the message you are about to + compose/reply/forward. +
       
      +
    • _SMTP_ : This function takes as an argument the definition of a + SMTP server. +
       
      +
    • _SORT_ : This function takes as an argument a Sort Style, and sorts a + specified folder in that sort order. +
       
      +
    • _TRIM_ : This function takes as an argument a list of strings that + you want removed from another string. At this time this only works for + _FROM_ and _SUBJECT_. +
       
      +
    • _REXTRIM_ : Same as _TRIM_ but its argument is one and + only one extended regular expression. +
       
      +
    • _STARTUP_ : This function takes as an argument an + incoming-startup-rule, and open an specified folder using that rule. +
       
      +
    • _THREADSTYLE_ : This function takes as an argument a + threading-display-style and uses it to display threads in a folder. +
       
      +
    • _THREADINDEX_ : This function takes as an argument a + threading-index-style and uses it to display threads in a folder. +
    + +

    + You must me wondering how to define the person/folder over who to apply + the action. This is done in the condition. When you specify a rule, the + rule is only executed if the condition is satisfied. In another words for + the rule: + +

    + _FROM_ == {Fred Flinstone} => _SAVE_{Fred} + +

    it will only be applied if the from is "Fred Flinstone". If + the From is "Wilma Flinstone" the rule will be skipped. + +

    In order to test a condition you can use the following tokens (in + alphabetical order): _ADDRESS_, _CC_, _FOLDER_, _FROM_,_NICK_, _ROLE, + _SENDER_, _SUBJECT_ and _TO_. The token will always be tested against what + it is between "{" and "}" in the condition, this part + of the condition is called the "condition set". The definition + of each token can be found here. + +

    A special testing token called _PROCID_ can be used to differentiate + inside a rule, between two rules that are triggered by the same condition. + A full explanation of the _PROCID_ token can be found in + this link. + +

    There are two more tokens related to the option + key-definition-rules. Those tokens + are only specific to that option, and hence are not explained here. + +

    You can also test in different ways, you can use the following + "test operands": <<, !<, >>, !>, == and !=. + All of them are two strings long. Here is the meaning of them: + +

    +

      +
    • << : It tests if the value of the token is contained in + the condition set. Here for example if the condition set were equal to + "Freddy", then the condition: _NICK_ << {Freddy}, would be true if + the value of _NICK_ were "Fred", "red" or "Freddy". You are just looking + for substrings here. +
    • >> : It tests if the value of the token contains the value of + the condition set. Here for example if the condittion set were equal to + "Fred", then the condition: _FROM_ >> {Fred}, would be true if + the value of _FROM_ were "Fred Flinstone" or "Fred P. Flinstone" or "Freddy". +
    • == : It tests if the value of the token is exactly equal to the value + of the set condition. For example _NICK_ == {Fred} will be false if the value + of _NICK_ is "Freddy" or "red". +
    • !< : This is true only when << is false and viceversa. +
    • !> : This is true only when >> is false and viceversa. +
    • != : This is true only when == is false and viceversa. +
    + +

    + Now let us say that you want the same action to be applied to more than + one person or folder, say you want "folder1" and "folder2" to be sorted by + Ordered Subject upon entering. Then you can list them all of them in the + condition part separting them by a ";". Here is the way to do it. + +

    + _FOLDER_ << {folder1; folder2} => _SORT_{OrderedSubj} + +

    + Here is the first subtelty about these definitions. Notice that the + following rule: + +

    + _FOLDER_ == {folder1; folder2} => _SORT_{Reverse OrderedSubj} + +

    works only for "folder1" but not for "folder2". This is because the + comparison of the name of the folder is done with whatever is in between + "{", ";" or "}", so in the above rule you would be testing
    + "folder2" == " folder2". The extra space makes the difference. + The reason why the first rule does not fail is because + "folder2" << " folder2" is actually + true. If something ever fails this may be something to look into. + +

    + Here are a few examples of what we have talked about before. + +

    + _NICK_ == {lisa;kika} => _SAVE_{_NICK_/_NICK_}
    + This means that if the nick is lisa, it will + save the message in the folder "lisa/lisa", and if the nick + is "kika", it will save the message in the folder "kika/kika" + +

    + _FOLDER_ == {Lynx} -> lynx
    + This, is an abreviation of the following rule:
    + _FOLDER_ == {Lynx} => _SAVE_{lynx}
    + (note the change in separator from "=>" to "->"). In the future + I will use that abreviation. + +

    _FOLDER_ << {comp.mail.pine; pine-info; pine-alpha} -> pine
    + Any message in the folders "comp.mail.pine", "pine-info" or "pine-alpha" + will be saved to the folder "pine". + +

    _FROM_ << {Pine Master} -> pine
    + Any message whose From field contains + "Pine Master" will be saved in the folder pine. + +

    _FOLDER_ << {Lynx; pine-info; comp.mail.pine} => + _INDEX_{IMAPSTATUS MSGNO DATE FROMORTO(33%) SUBJECT(66%)}
    Use a + different index-format for the folders "Lynx", "pine-info" and + "comp.mail.pine", where the size is not present. + +

    _FOLDER_ == {Lynx;pine-info} => _REPLY_{*** _FROM_ (_ADDRESS_) + wrote in the _FOLDER_ list _SMARTDATE_("Today" "today" "on + _LONGDATE_"):}
    If a message is in one of the incoming folders "Lynx" + or "pine-info", create a reply-leadin-string that acknowledges that. Note + the absence of "," in the function _SMARTDATE_. For example answering to a + message in the pine-info list would look like: + +

    + *** Steve Hubert (hubert@cac.washington.edu) wrote in the pine-info list today: + +

    + However replying for a message in the Lynx list would look: + +

    + *** mattack@area.com (mattack@area.com) wrote in the Lynx list today: + +

    + If you write in more than one language you can use this feature to create + Reply-leadin-strings in different languages. + +

    Note that at least for people you can create particular + reply-leadin-string using the role features, but it does not work as this + one does. This seems to be the right way to do it. + +

    _FOLDER_ << {Lynx; comp.mail.pine; pine_info; pine-alpha} => + _SORT_{OrderedSubj}
    This means upon opening, sort the folders "Lynx", + "comp.mail.pine", etc in ordered subject. All the others use the default + sort order. You can not sort in reverse in this form. The possible + arguments of this function are listed in the definition of the + default-sort-rule (Arrival, scorE, siZe, etc). + +

    The last examples use the function _TRIM_ which has a special form. + This function can only be used in the index list. + +

    _FOLDER_ << {Lynx} => _SUBJECT_ := _TRIM_{lynx-dev }
    In + the folder "Lynx" eliminate from the subject the string "lynx-dev " (with + the space at the end). For example a message whose subject is "Re: + lynx-dev unvisited Visited Links", would be shown in the index with + subject: "Re: unvisited Visited Links", making the subject shorter and + giving the same information. + +

    _FROM_ >> {Name (Comment)} => _FROM_ := + _TRIM_{ (Comment)}
    Remove the part " (Comment)" + from the _FROM_, so when displaying in the index the real From "Name" + will appear. + +

    _SUBJECT_ == {} => _RESUB_{Re: your mail without subject} + If there is no subject in the message, use the subject "Re: your mail + wiyhout subject" as a subject for the reply message. + +

    You can add more complexity to your rules by checking more than one + conditions before a rule is executed. For example: Assume that you want to + answer every email that contains the string "bug report", with the subject + "Re: About your bug report", you could make + +

    + _SUBJECT_ == {bug report} => _RESUB_{Re: About your _SUBJECT_} + +

    The problem with this construction is that if the person emails you + back, then the next time you answer the message the subject will be: "Re: + About your Re: About your bug report", so it grew. You may want to avoid + this growth by using the following rule: + +

    + _SUBJECT_ >> {bug report} && _SUBJECT_ !> {Re: } => _RESUB_{Re: About your _SUBJECT_}
    + +

    + which will only add the string "Re: About your" only the first time the + message is replied. + +

    + Say your personal name is "Fred Flinstones", and assume that you don't + like to see "To: comp.mail.pine" in every post you make to this newsgroup, + but instead would like to see it as everyone else sees it.
    + _FOLDER_ == {comp.mail.pine} && _FROM_ == {Fred Flinstones} => _FROM_ := _REPLACE_{_FROM_} + +

    + You can also list your index by nick, in the following way:
    + _NICK_ != {} => _FROM_ := _REPLACE_{_NICK_} + +

    + If you want to open the folder "pine-info" in the first non-read message + use the rule:
    + _FOLDER_ == {pine-info} => _STARTUP_{first-unseen} + +

    + If you want to move your deleted messages to a folder, called "Trash", use + the following rule:
    + _FLAG_ >> {D} -> Trash + +

    + The reason why the above test is not "_FLAG_ == {D}" is because that would mean + that this is the only flag set in the message. It's better to test by containment in this case. + +

    If you want to use a specific signature when you are in a specific collection + use the following rule:
    + _COLLECTION_ == {Mail} => _SIGNATURE_{/full/path/to/.signature} + +

    Finally about the question of which rule will be executed. Only the + first rule that matches will be executed. It is important to notice though + that "saving" rules do not compete with "sorting" rules. So the first + "saving" rule that matches will be executed in the case of saving and so + on. + +

    +

    + <End of help on this topic> + + ====== h_config_char_set ===== *************** *** 22592,22597 **** --- 24028,24070 ---- <End of help on this topic> + ====== h_config_special_text_to_color ===== + + + OPTION: <!--#echo var="VAR_special-text-color"--> + + +

    OPTION:

    + + Use this option to enter patterns (text or regular expressions) that + Alpine will highlight in the body of the text that is not part of a handle + (an internal or external link that Alpine paints in a different color). + +

    + Enter each pattern in a different line. Pine will internally merge these + patterns (by adding a "|" character), or you can add them all in one line + by separating them by a "|" character. There is only a set of regular expressions that are matched. + +

    + Pine will use the colors defined in the + Special Text Color variable. + to paint any match. + +

    + If the Special Text Color is not set, setting this variable will not + cause that special text to be indicated in any special way. It will look + like any normal text. You must set those colors in order to make Pine + paint the screen differently when it finds the patterns specified in this + variable. + +

    +

    + <End of help on this topic> + + ====== h_config_display_filters ===== *************** *** 22713,22718 **** --- 24186,24198 ---- improve security. The number is unique to the current Alpine session and is only generated once per session. + +

    _SILENT_
    +
    When the filter is executed, this token tells Alpine not to repaint + the screen while the command is being executed. This can be used with + filters that do not interact with the user, and therefore repainting + the screen is not necessary. +

    *************** *** 22749,22754 **** --- 24229,24240 ---- Command Modifying Tokens:

    +
    _ADDRESS_
    +
    When the command is executed, this token is replaced + with the address of the person sending the message in the format + mailbox@host. +
    +
    _RECIPIENTS_
    When the command is executed, this token is replaced with the space delimited list of recipients of the *************** *** 25861,25866 **** --- 27347,27422 ---- <End of help on this topic> + ====== h_config_thread_display_style_rule ===== + + + OPTION: Threading-Display-Style-Rule + + +

    OPTION: Threading-Display-Style-Rule

    + + This option is very similar to + , but it is a rule which specifies the + display styles for a thread that you want displayed in a specific + folder or collection. +

    + The token to be used in this function is _THREADSTYLE_. Here there is + an example of its use +

    + _FOLDER_ == {pine-info} => _THREADSTYLE_{mutt-like} +

    + The values that can be given for the _THREADSTYLE_ function are the + values of the threading-display-style function, which can be found + listed in the threading-display-style + configuration option. + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    +

    + <End of help on this topic> + + + ====== h_config_thread_index_style_rule ===== + + + OPTION: Threading-Index-Style-Rule + + +

    OPTION: Threading-Index-Style-Rule

    + + This option is very similar to + , but it is a rule which specifies the + index styles for a thread that you want displayed in a specific + folder or collection. +

    + The token to be used in this function is _THREADINDEX_. Here there is + an example of its use +

    + _FOLDER_ == {pine-info} => _THREADINDEX_{regular-index-with-expanded-threads} +

    + The values that can be given for the _THREADINDEX_ function are the + values of the threading-index-display function, which can be found + listed in the + configuration option. + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    +

    + <End of help on this topic> + + ====== h_config_pruning_rule ===== *************** *** 27467,27472 **** --- 29023,29058 ---- <End of help on this topic> + ====== h_config_new_thread_blank_subject ===== + + + FEATURE: <!--#echo var="FEAT_new-thread-on-blank-subject"--> + + +

    FEATURE:

    + + When this feature is enabled (the default) Alpine will create a new thread + every time that the subject line becomes empty at any time during composition. + +

    + This behavior is particularly useful in case you are replying to a message. + Replying to a message causes the message to be in the same thread than the + original message that is being replied to. However, many authors want to create + a new message (in a different thread) while replying to a message, and they do + this by changing the full subject, by first deleting the original subject and + typing the new subject of the current message. + +

    + Enabling this feature causes that any time that the subject is deleted, the + message being composed will be considered the first message of a new thread. + +

    +

    + <End of help on this topic> + + ====== h_config_strip_sigdashes ===== *************** *** 27517,27522 **** --- 29103,29144 ---- <End of help on this topic> + ====== h_config_preserve_field ===== + + + FEATURE: <!--#echo var="FEAT_preserve-original-fields"--> + + +

    FEATURE:

    + + When you reply to a message that has been sent to several recipients, some of + them may have been added in different parts of the headers. For example, + some of the recipients will be listed in the To: header, while others will + be listed in the Cc: header. +

    + If this feature is disabled, the default behavior of Alpine will be used, + and that is, that almost all recipients of the message will be + listed in the Cc: field. However, if you enable this feature, then, + excepting you, recipients originally listed in the Cc: field will be + listed again in the Cc: field, and those listed in the To: field in the + original message will be listed in the To: field again. The person in the + From: field will be added to the To: field. +

    + Note that this will cause some messages that you send in Alpine to look + different. In particular, the To: field of a message will not be put in the + Cc: field, as is normally done. In fact, most people expect this to happen. + If you find that this is a problem you should disable this feature. You can + still make Alpine have this behavior on a per message basis. In order to do + this, you will see a new option in the menu for the "Reply to all + recipients?" question. In this case, pressing "p" will make + Alpine toggle its question so you can preserve the To: and Cc: fields for that + message. +

    + <End of help on this topic> + + ====== h_config_sub_lists ===== *************** *** 27701,27706 **** --- 29323,29344 ---- <End of help on this topic> + ====== h_config_use_domain ===== + + + FEATURE: <!--#echo var="FEAT_return-path-uses-domain-name"--> + + +

    FEATURE:

    + + If you enable this configuration option Pine will use your domain name and your + username in that domain name to construct your Return-Path header, if not Pine + will use the address that you have set in the From: field to construct it. + +

    + <End of help on this topic> + + ====== h_config_use_sender_not_x ===== *************** *** 28297,28302 **** --- 29935,30005 ---- <End of help on this topic> + ====== h_config_alt_reply_menu ===== + + + FEATURE: <!--#echo var="FEAT_alternate-reply-menu"--> + + +

    FEATURE:

    + + This feature controls the menu that is displayed when Reply is selected. + If set, a list of options will be presented, with each option representing + the type of composition that could be used. This feature is most useful + for users who want to avoid being prompted with each option separately, or + would like to override some defaults set in your configuration for the + message that you are replying (e.g. you may have set the option to strip + signatures, but for the message you are answering you would like not to do + that) + +

    + The way this feature works is as follows. Initially you get the question + if you want to include the message, and in the menu you will see several + options, each option is accompanied by some text explaining what will + happen if you press the associated command. For example, if you read the + text "S Strip Sig", it means that if you press the letter + "S" the signature will be stripped off the message you are + replying. Observer that the menu will change to + "S No Strip", which means that if you press "S", the + signature will not be stripped off from the message. Your choices are + activated when you press RETURN. + +

    + Another way to remember what Pine will do, is that what will be done is + exactly the opposite of what you read in the menu. + +

    + The possible options are: + +

      +
    1. A: This determines if Pine will include or not the attachments sent to + you in the message that you are replying. By default Pine will use the value + of the configuration option + , but + you can use this option to override such behavior in a per message basis. + +
    2. F: To decide if you want to send flowed text or not. This option appears + unless you have quelled sending flowed text. + +
    3. H: This option determines if the headers of a message are to be + included in the body of the message that is being replied. By default Pine + will use the value of the configuration option + , but + you can use this option to override such behavior in a per message basis. + +
    4. R: To set a role, if you do not want Pine to set one automatically for you + or would like to set one when you can not select any. + +
    5. S: To strip the signature from a message, only available is the feature + or the + option are + enabled. + +
    +

    + <End of help on this topic> + + ====== h_config_del_from_dot ===== *************** *** 28820,28825 **** --- 30523,30560 ---- <End of help on this topic> + ====== h_config_enable_long_url ===== + + + FEATURE: <!--#echo var="FEAT_enable-msg-view-long-url"--> + + +

    FEATURE:

    + + This feature modifies the behavior of Alpine's MESSAGE TEXT screen. When this feature + is set alpine will attempt to recognize long urls (those that spread over several + lines in the text) for the HTTP protocol, even when they have not been enclosed between + delimiters "<" and ">". + +

    The normal behavior in Alpine is that if a URL is preceeded by the "<" + character and this URL was not finished before the end of the line, then a + continuation of the URL is searched in the following line(s). Normally, this type of + URLs will be ended by the ">" character, and if it is not, there is a + possibility of including erroneous text into the URL. + +

    Enabling this feature will make Alpine search for a continuation of certain URLs in + lines following its location. This will be of great help most times, but in some cases + the algorithm will catch some text into the URL that is not part of the URL. + +

    If you find that Alpine failed to recognize correctly a URL simply edit the URL before + passing it to your browser. + +

    + <End of help on this topic> + + ====== h_config_enable_view_addresses ===== *************** *** 29127,29132 **** --- 30862,30910 ---- <End of help on this topic> + ====== h_config_courier_list ===== + + + FEATURE: <!--#echo var="FEAT_courier-folder-list"--> + + +

    FEATURE:

    + + In a maildir collection, a folder could be used as a directory to store + folders. In the Courier server if you create a folder, then a directory + with the same name is created. If you use this patch to access a + collection created by the Courier server, then the display of such + collection will look confusing. The best way to access a maildir + collection created by the Courier server is by using the "#mc/" + prefix instead of the "#md/" prefix. If you use this alternate + prefix, then this feature applies to you, otherwise you can safely ignore + the text that follows. +

    + Depending on if you have enabled the option + + a folder may be listed as "folder[.]", or as two entries in the + list by "folder" and "folder.". +

    + If this option is disabled, Pine will list local folders that are in Courier + style format, as "folder", and those that are also directories as + "folder[.]". This makes the default display cleaner. +

    + If this feature is enabled then creating folders in a maildir collection + will create a directory with the same name. If this feature is disabled, then + a folder is considered a directory only if it contains subfolders, so you can + not create a directory with the same name as an exisiting folder unless + you create a subfolder of that folder first (e.g. if you have a folder + called "foo" simply add "foo.bar" directly. This will + create the directory "foo" and the subfolder "bar" of it). +

    + Observe that this feature works only for maildir collections that are accessed + locally. If a collection is accessed remotely then this feature has no value, + as the report is created in a server, and Pine only reports what received + from the server in this case. +

    + <End of help on this topic> + + ====== h_config_verbose_post ===== *************** *** 29281,29286 **** --- 31059,31087 ---- <End of help on this topic> + ====== h_config_auto_read_msgs_rules ===== + + + FEATURE: auto-move-read-msgs-using-rules + + +

    FEATURE: auto-move-read-msgs-using-rules

    + This feature controls an aspect of Alpine's behavior upon quitting. If set, + and the + "" + option is also set, then Alpine will automatically transfer all read + messages to the designated folder using the rules that you have defined in + your + "" and mark + them as deleted in the INBOX. Messages in the INBOX marked with an + "N" (meaning New, or unseen) are not affected. +

    +

    + <End of help on this topic> + + ====== h_config_auto_fcc_only ===== *************** *** 29731,29736 **** --- 31532,31554 ---- <End of help on this topic> + ====== h_config_enhanced_thread ===== + + + FEATURE: <!--#echo var="FEAT_enhanced-fancy-thread-support"--> + + +

    FEATURE:

    + + If this option is set certain commands in Pine will operate in loose + threads too. For example, the command ^D marks a thread deleted, but if + this feature is set, it will remove all threads that share the same missing + parent with this thread. + +

    + <End of help on this topic> + + ====== h_config_news_cross_deletes ===== *************** *** 30209,30214 **** --- 32027,32066 ---- <End of help on this topic> + ====== h_config_ignore_size ===== + + + FEATURE: <!--#echo var="FEAT_ignore-size-changes"--> + + +

    FEATURE:

    + + When you have an account residing in an IMAP server, Alpine gets the size of + each message from the server. However, when Alpine saves a message residing + in an IMAP server, Alpine computes the size of the message independently. If + these two numbers do not match for a message, Alpine asks you if you still + want to take the risk of saving the message, since data corruption or loss + of data could result of this save. + +

    + Sometimes the root of this problem is that the server is defective, and + there will not be loss of information when saving such message. Enabling + this feature will make Aline ignore such error and continue saving the + message. If you can determine that this is the case, enable this feature + so that the saving operation will succeed. An example of a defective server + is the Gmail IMAP server. Another example is some versions of the Exchange + server. + +

    + It is recommended that this feature be disabled most of the time and only + enabled when you find a server which you can determine that has the above + mentioned defect, but be disabled again after making this operation + succeed. + +

    + <End of help on this topic> + + ====== h_config_force_low_speed ===== *************** *** 30479,30484 **** --- 32331,32357 ---- <End of help on this topic> + ====== h_config_circular_tab ===== + + + FEATURE: <!--#echo var="FEAT_enable-circular-tab"--> + + +

    FEATURE:

    + +

    + This Feature is like + "", + in the sense that you can use TAB to browse through all of your Incoming + Folders checking for new mail. Once it gets to the last folder of the + collection it goes back to check again until it returns to the original + folder where it started. +

    + <End of help on this topic> + + ====== h_config_auto_include_reply ===== *************** *** 31142,31147 **** --- 33015,33113 ---- <End of help on this topic> + ====== h_config_folder_color ===== + + + OPTION: Folder Color + + +

    OPTION: Folder Color

    + + Sets the colors Alpine uses for coloring a folder in the FOLDER LIST + screen. By default, the Folder Color is the normal text color. + +

    + If you set a color for this feature, other than the normal color + (the default), or a color for + Directory Color, then directories + will be colored according to the color specified in the + Directory Color option. In this + case, the color will be the only indication that the colored name + refers to a directory. The normal behavior is that Alpine + indicates that a name refers to a directory by appending a + separator (like "/" or ".") to the name of + the folder. + +

    + If a folder is a directory, then the folder name will be painted + according to the color defined by this variable, and a separator + indicator (like "/" or ".") will be added + to the name. That + indicator will be painted according to the color defined in the + Directory Color option. + +

    + <End of help on this topic> + + + ====== h_config_directory_color ===== + + + OPTION: Directory Color + + +

    OPTION: Directory Color

    + + Sets the colors Alpine uses for coloring a directory in the FOLDER LIST + screen. By default, the Folder Color is the normal text color. +

    + If you set a color for this feature, other than the normal color + (the default), or a color for + Folder Color, then folders + will be colored according to the color specified in the + Folder Color option. In this + case, the color will be the only indication that the colored name + refers to a directory. The normal behavior is that Alpine + indicates that a name refers to a directory by appending a + separator (like "/" or ".") to the name of + the folder. +

    + If a folder is a directory, then the folder name will be painted + according to the color defined by the option + Folder Color, and the separator + indicator (like "/" or ".") will be added + after the name. That + indicator will be painted according to the color defined in this + option. +

    + <End of help on this topic> + + + ====== h_config_folder_list_color ===== + + + OPTION: Folder-List Color + + +

    OPTION: Folder-List Color

    + + Sets the colors Alpine uses for coloring normal text in the FOLDER LIST + screen. By default, the Folder-List Color is the normal text color. +

    + This text refers to the informative text that Alpine displays so you + can recognize each collection. The color of the content of each collection + is determined by the options Folder Color + and Directory Color. +

    + Unlike the options + Folder Color + and Directory Color, configuring + this option does not affect the way that it tradinionally reports folders, + directories and folders that are directories. +

    + <End of help on this topic> + + ====== h_config_incunseen_color ===== *************** *** 31197,31202 **** --- 33163,33192 ---- <End of help on this topic> + ====== h_config_special_text_color ===== + + + OPTION: Special Text Color + + +

    OPTION: Special Text Color

    + + Sets the color Pine uses for coloring any text in the body of the message + that is not part of a handle (and internal or external link that Pine + paints in a different color). By default, this variable is not defined, + which means that text that matches the pattern is not painted in any + particular way. This variable must be set in a special form if you + want text to be painted. + +

    + Descriptions of the available commands +

    + Look here + to see the available Editing and Navigation commands. +

    + <End of help on this topic> + + ====== h_config_prompt_color ===== *************** *** 31464,31469 **** --- 33454,33488 ---- <End of help on this topic> + ====== h_config_indextoken_color ===== + + + OPTION: <!--#echo var="VAR_index-token-colors"--> + + +

    OPTION:

    + + This option allows you to set up the color in which any token, not specified by the + previous options, will be colored in the MESSAGE INDEX screen. +

    + In order to use this option, you must press the "I" IndxHdr command, and add + a token that can be used in the index format. + The list of available tokens is here. +

    + If you fail to enter a valid token your entry will be ignored, and you will be asked to + enter a new one. Once you have entered a valid token, a line will be added to the + configuration screen that you can use to set up the colors in which that token will + be painted. This is done in the same way that you configure colors for other + variables. + + Descriptions of the available commands +

    + Look here + to see the available Editing and Navigation commands. +

    + <End of help on this topic> + + ====== h_config_customhdr_pattern ===== *************** *** 35075,35077 **** --- 37094,37099 ---- ========== h_select_by_smaller_size ========== Enter a number or ^C to cancel. All messages less than this many characters in size will be selected. Examples: 2176, 1.53K (1530), or 3M (3000000). + ========== h_preserve_field ========== + Use 'p' to toggle between preserving or not preserving the original To: + and Cc: fields of the message. Enter ^C to cancel message. diff -rc alpine-2.00/pith/reply.c alpine-2.00.I.USE/pith/reply.c *** alpine-2.00/pith/reply.c 2008-06-03 12:27:23.000000000 -0700 --- alpine-2.00.I.USE/pith/reply.c 2011-02-07 20:33:44.000000000 -0800 *************** *** 45,50 **** --- 45,52 ---- #include "../pith/ablookup.h" #include "../pith/mailcmd.h" #include "../pith/margin.h" + #include "../pith/copyaddr.h" + #include "../pith/rules.h" /* *************** *** 372,391 **** /* Put Reply-To or From in To. */ *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to, (ADDRESS *) NULL, saved_from, RCA_ALL); - /* and the rest in cc */ if(replytoall){ ! *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc, outgoing->to, saved_to, RCA_ALL); ! while(*cc_tail) /* stay on last address */ ! cc_tail = &(*cc_tail)->next; ! *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc, outgoing->to, saved_cc, RCA_ALL); ! while(*cc_tail) ! cc_tail = &(*cc_tail)->next; ! *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc, outgoing->to, saved_resent, RCA_ALL); } } else if(saved_to){ --- 374,410 ---- /* Put Reply-To or From in To. */ *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to, (ADDRESS *) NULL, saved_from, RCA_ALL); if(replytoall){ ! if(ps->preserve){ ! while(*to_tail) ! to_tail = &(*to_tail)->next; ! ! *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to, ! (ADDRESS *) NULL, saved_to, RCA_ALL); ! ! while(*to_tail) ! to_tail = &(*to_tail)->next; ! ! *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc, ! outgoing->to, saved_resent, RCA_ALL); ! ! *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc, ! outgoing->to, saved_cc, RCA_ALL); ! } ! else{ /* and the rest in cc */ ! *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc, outgoing->to, saved_to, RCA_ALL); ! while(*cc_tail) /* stay on last address */ ! cc_tail = &(*cc_tail)->next; ! *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc, outgoing->to, saved_cc, RCA_ALL); ! while(*cc_tail) ! cc_tail = &(*cc_tail)->next; ! *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc, outgoing->to, saved_resent, RCA_ALL); + } } } else if(saved_to){ *************** *** 796,803 **** reply_quote_str(ENVELOPE *env) { char *prefix, *repl, *p, buf[MAX_PREFIX+1], pbf[MAX_SUBSTITUTION+1]; ! strncpy(buf, ps_global->VAR_REPLY_STRING, sizeof(buf)-1); buf[sizeof(buf)-1] = '\0'; /* set up the prefix to quote included text */ --- 815,841 ---- reply_quote_str(ENVELOPE *env) { char *prefix, *repl, *p, buf[MAX_PREFIX+1], pbf[MAX_SUBSTITUTION+1]; + char reply_string[MAX_PREFIX+1]; + + { RULE_RESULT *rule; + rule = get_result_rule(V_REPLY_INDENT_RULES, FOR_COMPOSE , env); + if (rule){ + strncpy(reply_string,rule->result,sizeof(reply_string)); + reply_string[sizeof(reply_string)-1] = '\0'; + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + else + if ((ps_global->VAR_REPLY_STRING) && (ps_global->VAR_REPLY_STRING[0])){ + strncpy(reply_string,ps_global->VAR_REPLY_STRING, sizeof(reply_string)-1); + reply_string[sizeof(reply_string)-1] = '\0'; + } + else + strncpy(reply_string,"> ",sizeof("> ")); + } ! strncpy(buf, reply_string, sizeof(buf)-1); buf[sizeof(buf)-1] = '\0'; /* set up the prefix to quote included text */ *************** *** 849,858 **** int reply_quote_str_contains_tokens(void) { ! return(ps_global->VAR_REPLY_STRING && ps_global->VAR_REPLY_STRING[0] && ! (strstr(ps_global->VAR_REPLY_STRING, from_token) || ! strstr(ps_global->VAR_REPLY_STRING, nick_token) || ! strstr(ps_global->VAR_REPLY_STRING, init_token))); } --- 887,915 ---- int reply_quote_str_contains_tokens(void) { ! char *reply_string; ! ! reply_string = (char *) malloc( 80*sizeof(char)); ! { RULE_RESULT *rule; ! rule = get_result_rule(V_REPLY_INDENT_RULES, FOR_COMPOSE, NULL); ! if (rule){ ! reply_string = cpystr(rule->result); ! if (rule->result) ! fs_give((void **)&rule->result); ! fs_give((void **)&rule); ! } ! else ! if ((ps_global->VAR_REPLY_STRING) && (ps_global->VAR_REPLY_STRING[0])){ ! strncpy(reply_string,ps_global->VAR_REPLY_STRING, sizeof(reply_string)-1); ! reply_string[sizeof(reply_string)-1] = '\0'; ! } ! else ! reply_string = cpystr("> "); ! } ! return(reply_string && reply_string[0] && ! (strstr(reply_string, from_token) || ! strstr(reply_string, nick_token) || ! strstr(reply_string, init_token))); } *************** *** 954,960 **** if(!orig_body || orig_body->type == TYPETEXT || reply_raw_body ! || F_OFF(F_ATTACHMENTS_IN_REPLY, ps_global)){ char *charset = NULL; /*------ Simple text-only message ----*/ --- 1011,1017 ---- if(!orig_body || orig_body->type == TYPETEXT || reply_raw_body ! || !ps_global->reply.attach){ char *charset = NULL; /*------ Simple text-only message ----*/ *************** *** 962,968 **** body->type = TYPETEXT; body->contents.text.data = msgtext; reply_delimiter(env, role, pc); ! if(F_ON(F_INCLUDE_HEADER, ps_global)) reply_forward_header(stream, msgno, sect_prefix, env, pc, prefix); --- 1019,1025 ---- body->type = TYPETEXT; body->contents.text.data = msgtext; reply_delimiter(env, role, pc); ! if(ps_global->reply.inchdr) reply_forward_header(stream, msgno, sect_prefix, env, pc, prefix); *************** *** 1020,1026 **** if(reply_body_text(orig_body, &tmp_body)){ reply_delimiter(env, role, pc); ! if(F_ON(F_INCLUDE_HEADER, ps_global)) reply_forward_header(stream, msgno, sect_prefix, env, pc, prefix); --- 1077,1083 ---- if(reply_body_text(orig_body, &tmp_body)){ reply_delimiter(env, role, pc); ! if(ps_global->reply.inchdr) reply_forward_header(stream, msgno, sect_prefix, env, pc, prefix); *************** *** 1058,1064 **** body->nested.part->body.subtype = cpystr("Plain"); } reply_delimiter(env, role, pc); ! if(F_ON(F_INCLUDE_HEADER, ps_global)) reply_forward_header(stream, msgno, sect_prefix, env, pc, prefix); --- 1115,1121 ---- body->nested.part->body.subtype = cpystr("Plain"); } reply_delimiter(env, role, pc); ! if(ps_global->reply.inchdr) reply_forward_header(stream, msgno, sect_prefix, env, pc, prefix); *************** *** 1081,1087 **** int partnum; reply_delimiter(env, role, pc); ! if(F_ON(F_INCLUDE_HEADER, ps_global)) reply_forward_header(stream, msgno, sect_prefix, env, pc, prefix); --- 1138,1144 ---- int partnum; reply_delimiter(env, role, pc); ! if(ps_global->reply.inchdr) reply_forward_header(stream, msgno, sect_prefix, env, pc, prefix); *************** *** 1316,1321 **** --- 1373,1382 ---- buf[0] = '\0'; switch(type){ + case iFfrom: + addr = env && env->sparep ? env->sparep : NULL; + break; + case iFrom: addr = env ? env->from : NULL; break; *************** *** 1701,1721 **** break; case iFrom: case iTo: case iCc: case iSender: case iRecips: case iInit: get_addr_data(env, type, buf, maxlen); break; ! case iRoleNick: ! if(role && role->nick){ ! strncpy(buf, role->nick, maxlen); ! buf[maxlen] = '\0'; ! } ! break; case iNewLine: if(maxlen >= strlen(NEWLINE)){ --- 1762,1912 ---- break; + case iProcid: + if(ps_global->procid){ + strncpy(buf, ps_global->procid, maxlen); + buf[maxlen] = '\0'; + } + break; + + case iRole: + if (ps_global->role){ + strncpy(buf, ps_global->role, maxlen); + buf[maxlen] = '\0'; + } + break; + + case iRoleNick: + if(role && role->nick){ + strncpy(buf, role->nick, maxlen); + buf[maxlen] = '\0'; + } + break; + + case iPkey: + if(ps_global->pressed_key){ + strcpy(buf, ps_global->pressed_key); + buf[maxlen] = '\0'; + } + break; + + case iScreen: + if(ps_global->screen_name){ + strncpy(buf, ps_global->screen_name, maxlen); + buf[maxlen] = '\0'; + } + break; + + case iFfrom: case iFrom: case iTo: case iCc: case iSender: case iRecips: case iInit: + if (env) get_addr_data(env, type, buf, maxlen); break; ! case iFolder: ! if(ps_global->cur_folder){ ! strncpy(buf,ps_global->cur_folder, maxlen); ! buf[maxlen] = '\0'; ! } ! break; ! ! case iCollection: ! if(ps_global->context_current->nickname){ ! strncpy(buf,ps_global->context_current->nickname, maxlen); ! buf[maxlen] = '\0'; ! } ! break; ! ! case iFlag: ! {MAILSTREAM *stream = find_open_stream(); ! MSGNO_S *msgmap = NULL; ! long msgno; ! MESSAGECACHE *mc; ! strncpy(buf, "_FLAG_", maxlen); /* default value */ ! if (stream){ ! mn_init(&msgmap, stream->nmsgs); ! msgno = mn_m2raw(msgmap, rules_cursor_pos(stream)); ! if (msgno > 0L) mc = stream ? mail_elt(stream, msgno) : NULL; ! if (mc) ! sprintf(buf,"%s%s%s%s",mc->flagged ? "*" : "", ! mc->recent ? (mc->seen ? "R" : "N") : (mc->seen) ? "R" : "U", ! mc->answered ? "A" : "", ! mc->deleted ? "D" : "" ); ! mn_give(&msgmap); ! } ! buf[maxlen] = '\0'; ! } ! break; ! ! case iNick: ! { ! ADDRESS *tmp_adr = NULL; ! if (env){ ! tmp_adr = env->from ? copyaddr(env->from) ! : env->sender ? copyaddr(env->sender) : NULL; ! get_nickname_from_addr(tmp_adr,buf,maxlen); ! mail_free_address(&tmp_adr); ! } ! } ! break; ! ! case iAddressCc: ! case iAddressRecip: ! case iAddressTo: ! case iFadd: ! { ! int plen = 0; /* partial length */ ! ADDRESS *sparep2 = (type == iAddressTo || type == iAddressRecip) ! ? ((env && env->to) ! ? copyaddrlist(env->to) ! : NULL) ! : (type == iAddressCc) ! ? ((env && env->cc) ! ? copyaddrlist(env->cc) ! : NULL) ! : ((env && env->sparep) ! ? copyaddr((ADDRESS *)env->sparep) ! : NULL); ! ADDRESS *sparep; ! ! if (type == iAddressRecip){ ! ADDRESS *last_to = NULL; ! ! for(last_to = sparep2;last_to && last_to->next; last_to= last_to->next); ! ! /* Make the end of To list point to cc list */ ! if(last_to) ! last_to->next = (env && env->cc ? copyaddrlist(env->cc) : NULL); ! ! } ! sparep = sparep2; ! for(; sparep ; sparep = sparep->next) ! if(sparep && sparep->mailbox && sparep->mailbox[0] && ! (plen ? plen + 1 : plen) + strlen(sparep->mailbox) <= maxlen){ ! if (plen == 0) ! strcpy(buf, sparep->mailbox); ! else{ ! strcat(buf, " "); ! strcat(buf, sparep->mailbox); ! } ! if(sparep->host && ! sparep->host[0] && ! sparep->host[0] != '.' && ! strlen(buf) + strlen(sparep->host) + 1 <= maxlen){ ! strcat(buf, "@"); ! strcat(buf, sparep->host); ! } ! plen = strlen(buf); ! } ! mail_free_address(&sparep2); ! } ! ! break; case iNewLine: if(maxlen >= strlen(NEWLINE)){ *************** *** 1744,1749 **** --- 1935,1945 ---- break; + case iLcc: /* fake it, there are not enough spare pointers */ + if (env && env->date) + sprintf(buf,"%s",env->date); + break; + case iNews: case iCurNews: get_news_data(env, type, buf, maxlen); *************** *** 1793,1798 **** --- 1989,2002 ---- break; + case iOpeningText: + case iOpeningTextNQ: + if(env && env->sparep){ + strncpy(buf, ((SPAREP_S *)env->sparep)->value, maxlen); + buf[maxlen] = '\0'; + } + break; + case iSubject: if(env && env->subject){ size_t n, len; *************** *** 1851,1857 **** if(!env) return; ! strncpy(buf, ps_global->VAR_REPLY_INTRO, MAX_DELIM); buf[MAX_DELIM] = '\0'; /* preserve exact default behavior from before */ if(!strcmp(buf, DEFAULT_REPLY_INTRO)){ --- 2055,2072 ---- if(!env) return; ! { RULE_RESULT *rule; ! rule = get_result_rule(V_REPLY_LEADIN_RULES, FOR_REPLY_INTRO, env); ! if(rule){ ! strncpy(buf, rule->result, MAX_DELIM); ! if (rule->result) ! fs_give((void **)&rule->result); ! fs_give((void **)&rule); ! } ! else ! strncpy(buf, ps_global->VAR_REPLY_INTRO, MAX_DELIM); ! } ! buf[MAX_DELIM] = '\0'; /* preserve exact default behavior from before */ if(!strcmp(buf, DEFAULT_REPLY_INTRO)){ *************** *** 2110,2115 **** --- 2325,2331 ---- { size_t l; char *p, buftmp[MAILTMPLEN]; + RULE_RESULT *rule; if(!env) return(NULL); *************** *** 2117,2125 **** dprint((9, "checking subject: \"%s\"\n", env->subject ? env->subject : "NULL")); ! if(env->subject && env->subject[0]){ /* add (fwd)? */ ! snprintf(buftmp, sizeof(buftmp), "%s", env->subject); ! buftmp[sizeof(buftmp)-1] = '\0'; /* decode any 8bit (copy to the temp buffer if decoding doesn't) */ if(rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, buftmp) == (unsigned char *) buftmp) --- 2333,2352 ---- dprint((9, "checking subject: \"%s\"\n", env->subject ? env->subject : "NULL")); ! buftmp[0] = '\0'; ! ps_global->procid = cpystr("fwd-subject"); ! if (rule = get_result_rule(V_FORWARD_RULES,FOR_COMPOSE, env)){ ! sprintf(buftmp, "%.200s", rule->result); ! if(rule->result) ! fs_give((void **)&rule->result); ! fs_give((void **)&rule); ! } ! else if(env->subject) ! sprintf(buftmp, "%.200s", env->subject); ! buftmp[sizeof(buftmp)-1] = '\0'; ! fs_give((void **)&ps_global->procid); ! ! if(buftmp[0]){ /* add (fwd)? */ /* decode any 8bit (copy to the temp buffer if decoding doesn't) */ if(rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, buftmp) == (unsigned char *) buftmp) *************** *** 2620,2628 **** * tied our hands, alter the prefix to continue flowed * formatting... */ ! if(flow_res) wrapflags |= GFW_FLOW_RESULT; filters[filtcnt].filter = gf_wrap; /* * The 80 will cause longer lines than what is likely --- 2847,2858 ---- * tied our hands, alter the prefix to continue flowed * formatting... */ ! if(flow_res && !ps_global->reply.no_send_flowed) wrapflags |= GFW_FLOW_RESULT; + filters[filtcnt].filter = gf_quote_test; + filters[filtcnt++].data = gf_line_test_opt(select_quote, NULL); + filters[filtcnt].filter = gf_wrap; /* * The 80 will cause longer lines than what is likely *************** *** 2656,2664 **** * We also want to fold "> " quotes so we get the * attributions correct. */ ! if(flow_res && prefix && !strucmp(prefix, "> ")) *(prefix_p = prefix + 1) = '\0'; ! if(!(wrapflags & GFW_FLOWED) && flow_res){ filters[filtcnt].filter = gf_line_test; --- 2886,2894 ---- * We also want to fold "> " quotes so we get the * attributions correct. */ ! if(flow_res && !ps_global->reply.no_send_flowed && prefix && !strucmp(prefix, "> ")) *(prefix_p = prefix + 1) = '\0'; ! ps_global->reply.no_send_flowed = 0; /* reset for next call */ if(!(wrapflags & GFW_FLOWED) && flow_res){ filters[filtcnt].filter = gf_line_test; *************** *** 2691,2699 **** } if(prefix){ ! if(ps_global->full_header != 2 ! && (F_ON(F_ENABLE_SIGDASHES, ps_global) ! || F_ON(F_ENABLE_STRIP_SIGDASHES, ps_global))){ dashdata = 0; filters[filtcnt].filter = gf_line_test; filters[filtcnt++].data = gf_line_test_opt(sigdash_strip, &dashdata); --- 2921,2927 ---- } if(prefix){ ! if(ps_global->reply.strip){ dashdata = 0; filters[filtcnt].filter = gf_line_test; filters[filtcnt++].data = gf_line_test_opt(sigdash_strip, &dashdata); *************** *** 2718,2724 **** dq.do_color = 0; dq.delete_all = 1; ! filters[filtcnt].filter = gf_line_test; filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); } --- 2946,2952 ---- dq.do_color = 0; dq.delete_all = 1; ! filters[filtcnt].filter = gf_quote_test; filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); } diff -rc alpine-2.00/pith/rules.c alpine-2.00.I.USE/pith/rules.c *** alpine-2.00/pith/rules.c 2011-02-07 20:34:02.000000000 -0800 --- alpine-2.00.I.USE/pith/rules.c 2011-02-07 20:33:42.000000000 -0800 *************** *** 0 **** --- 1,1149 ---- + /* This module was written by + * + * Eduardo Chappa (chappa@washington.edu) + * http://staff.washington.edu/chappa/pine/ + * + * Original Version: November 1999 + * Last Modified : January 24, 2008 + * + * Send bug reports about this module to the address above. + */ + + #include "../pith/headers.h" + #include "../pith/state.h" + #include "../pith/conf.h" + #include "../pith/copyaddr.h" + #include "../pith/rules.h" + + /* Internal Prototypes */ + + int test_condition (CONDITION_S *, int, ENVELOPE *); + int test_in (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int); + int test_ni (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int); + int test_not_in (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int); + int test_not_ni (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int); + int test_eq (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int); + int test_not_eq (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int); + int isolate_condition (char *, char **, int *); + char *test_rule (RULELIST *, int, ENVELOPE *, int *); + char *trim (RULEACTION_S *, int, ENVELOPE *); + char *rextrim (RULEACTION_S *, int, ENVELOPE *); + char *raw_value (RULEACTION_S *, int, ENVELOPE *); + char *extended_value (RULEACTION_S *, int, ENVELOPE *); + char *exec_fcn (RULEACTION_S *, int, ENVELOPE *); + char *expand (char *, void *); + char *get_name_token (char *); + char *advance_to_char (char *, char, int, int *); + char **functions_for_token (char *); + void free_token_value (TOKEN_VALUE **); + void free_condition (CONDITION_S **); + void free_ruleaction (RULEACTION_S **); + void free_rule (RULE_S **); + void free_rule_list (RULELIST **); + void free_alloc_rule (void **, int); + void *alloc_mem (size_t); + void add_rule (int, int); + void set_rule_list (struct variable *); + void parse_patterns_into_action(TOKEN_VALUE **); + void free_parsed_value(TOKEN_VALUE **value); + RULE_S *parse_rule (char *, int); + RULELIST *get_rule_list (char **, int, int); + TOKEN_VALUE *parse_group_data (char *,int *); + TOKEN_VALUE *copy_parsed_value (TOKEN_VALUE *, int, ENVELOPE *); + CONDITION_S *fill_condition (char *); + CONDITION_S *parse_condition (char *, int *); + PRULELIST_S *add_prule (PRULELIST_S *, PRULELIST_S *); + RULEACTION_S *parse_action (char *, int); + + REL_TOKEN rel_rules_test[] = { + {EQ_REL, Equal, test_eq}, + {IN_REL, Subset, test_in}, + {NI_REL, Includes, test_ni}, + {NOT_EQ_REL, NotEqual, test_not_eq}, + {NOT_IN_REL, NotSubset, test_not_in}, + {NOT_NI_REL, NotIncludes, test_not_ni}, + {NULL, EndTypes, NULL} + }; + + #define NREL (sizeof(rel_rules_test)/sizeof(rel_rules_test[0]) - 1) + + RULE_FCN rule_fcns[] = { + {COPY_FCN, extended_value, FOR_SAVE|FOR_COMPOSE}, + {SAVE_FCN, extended_value, FOR_SAVE}, + {EXEC_FCN, exec_fcn, FOR_REPLACE|FOR_TRIM|FOR_RESUB|FOR_COMPOSE}, + {REPLY_FCN, extended_value, FOR_REPLY_INTRO}, + {TRIM_FCN, trim, FOR_TRIM|FOR_RESUB|FOR_COMPOSE}, + {REPLACE_FCN, extended_value, FOR_REPLACE}, + {SORT_FCN, raw_value, FOR_SORT}, + {INDEX_FCN, raw_value, FOR_INDEX}, + {COMMAND_FCN, raw_value, FOR_KEY}, + {REPLYSTR_FCN, raw_value, FOR_COMPOSE}, + {SIGNATURE_FCN, raw_value, FOR_COMPOSE}, + {RESUB_FCN, extended_value, FOR_RESUB}, + {STARTUP_FCN, raw_value, FOR_STARTUP}, + {REXTRIM_FCN, rextrim, FOR_TRIM|FOR_RESUB|FOR_COMPOSE}, + {THRDSTYLE_FCN, raw_value, FOR_THREAD}, + {THRDINDEX_FCN, raw_value, FOR_THREAD}, + {SMTP_FCN, raw_value, FOR_COMPOSE}, + {NULL, 0, FOR_NOTHING} + }; + + char* token_rules[] = { + FROM_TOKEN, + NICK_TOKEN, + OTEXT_TOKEN, + OTEXTNQ_TOKEN, + ROLE_TOKEN, + FOLDER_TOKEN, + SUBJ_TOKEN, + PROCID_TOKEN, + THDDSPSTY_TOKEN, + THDNDXSTY_TOKEN, + FLAG_TOKEN, + COLLECT_TOKEN, + THDDSPSTY_TOKEN, + ADDR_TOKEN, + TO_TOKEN, + ADDTO_TOKEN, + ADDCC_TOKEN, + ADDRECIP_TOKEN, + SCREEN_TOKEN, + KEY_TOKEN, + SEND_TOKEN, + CC_TOKEN, + LCC_TOKEN, + BCC_TOKEN, + FFROM_TOKEN, + FADDRESS_TOKEN, + NULL + }; + + #define NTOKENS (sizeof(token_rules)/sizeof(token_rules[0]) - 1) + #define NFCN (sizeof(rule_fcns)/sizeof(rule_fcns[0]) - 1) + + char *subj_fcn[] = {SUBJ_TOKEN, REPLACE_FCN, TRIM_FCN, REXTRIM_FCN, EXEC_FCN}; + char *from_fcn[] = {FROM_TOKEN, REPLACE_FCN, TRIM_FCN, REXTRIM_FCN, EXEC_FCN}; + char *otext_fcn[] = {OTEXT_TOKEN, REPLACE_FCN, TRIM_FCN, REXTRIM_FCN, EXEC_FCN}; + char *otextnq_fcn[] = {OTEXTNQ_TOKEN, REPLACE_FCN, TRIM_FCN, REXTRIM_FCN, EXEC_FCN}; + + char *adto_fcn[] = {ADDTO_TOKEN, EXEC_FCN, NULL, NULL, NULL}; + + char **fcns_for_index[] = {subj_fcn, from_fcn, otext_fcn, otextnq_fcn}; + + #define NFCNFI (sizeof(fcns_for_index)/sizeof(fcns_for_index[0])) /*for idx*/ + #define NFPT (sizeof(fcns_for_index[0])) /* functions pert token */ + + SPAREP_S * + get_sparep_for_rule(char *value, int flag) + { + SPAREP_S *rv; + rv = (SPAREP_S *) alloc_mem(sizeof(SPAREP_S)); + rv->flag = flag; + rv->value = value ? cpystr(value) : NULL; + return rv; + } + + void free_sparep_for_rule(void **sparep) + { + SPAREP_S *spare = (SPAREP_S *) *sparep; + if(!spare) return; + if(spare->value) + fs_give((void **)&spare->value); + fs_give((void **)sparep); + } + + + int context_for_function(char *name) + { + int i, j; + for (i = 0; i < NFCN && strcmp(rule_fcns[i].name, name); i++); + return i == NFCN ? 0 : rule_fcns[i].what_for; + + } + + char **functions_for_token(char *name) + { + int i; + for (i = 0; i < NFCNFI && strcmp(fcns_for_index[i][0], name); i++); + return i == NFCNFI ? NULL : fcns_for_index[i]; + } + + void + free_alloc_rule (void **voidtext, int code) + { + switch(code){ + case FREEREGEX : regfree((regex_t *)*voidtext); + break; + default: break; + } + } + + + + void free_token_value(TOKEN_VALUE **token) + { + if(token && *token){ + if ((*token)->testxt) + fs_give((void **)&(*token)->testxt); + free_alloc_rule (&(*token)->voidtxt, (*token)->codefcn); + if((*token)->next) + free_token_value(&(*token)->next); + fs_give((void **)token); + } + } + + void free_condition(CONDITION_S **condition) + { + if(condition && *condition){ + if ((*condition)->tname) + fs_give((void **)&((*condition)->tname)); + if ((*condition)->value) + free_token_value(&((*condition)->value)); + if((*condition)->next) + free_condition(&((*condition)->next)); + fs_give((void **)condition); + } + } + + void free_ruleaction(RULEACTION_S **raction) + { + if(raction && *raction){ + if ((*raction)->token) + fs_give((void **)&((*raction)->token)); + if ((*raction)->function) + fs_give((void **)&((*raction)->function)); + if ((*raction)->value) + free_token_value(&(*raction)->value); + fs_give((void **)raction); + } + } + + void free_rule(RULE_S **rule) + { + if(rule && *rule){ + free_condition(&((*rule)->condition)); + free_ruleaction(&((*rule)->action)); + fs_give((void **)rule); + } + } + + void free_rule_list(RULELIST **rule) + { + if(!*rule) + return; + + if((*rule)->next) + free_rule_list(&((*rule)->next)); + + if((*rule)->prule) + free_rule(&((*rule)->prule)); + + fs_give((void **)rule); + } + + void + free_parsed_rule_list(PRULELIST_S **rule) + { + if(!*rule) + return; + + if((*rule)->next) + free_parsed_rule_list(&((*rule)->next)); + + if((*rule)->rlist) + free_rule_list(&((*rule)->rlist)); + + fs_give((void **)rule); + } + + void * + alloc_mem (size_t amount) + { + void *genmem; + memset(genmem = fs_get(amount), 0, amount); + return genmem; + } + + + void + parse_patterns_into_action(TOKEN_VALUE **tokenp) + { + if(!*tokenp) + return; + + if((*tokenp)->testxt){ + regex_t preg; + + (*tokenp)->voidtxt = NULL; + (*tokenp)->voidtxt = fs_get(sizeof(regex_t)); + if (regcomp((regex_t *)(*tokenp)->voidtxt, + (*tokenp)->testxt, REG_EXTENDED) != 0){ + regfree((regex_t *)(*tokenp)->voidtxt); + (*tokenp)->voidtxt = NULL; + } + } + if((*tokenp)->voidtxt) + (*tokenp)->codefcn = FREEREGEX; + if((*tokenp)->next) + parse_patterns_into_action(&(*tokenp)->next); + } + + + int + isolate_condition (char *data, char **cvalue, int *len) + { + char *p = data; + int done = 0, error = 0, next_condition = 0, l; + + if(*p == '"' && p[strlen(p) - 1] == '"'){ + p[strlen(p) - 1] = '\0'; + p++; + } + *cvalue = NULL; + while (*p && !done){ + switch (*p){ + case '_': *cvalue = advance_to_char(p,'}', STRICTLY, NULL); + if(*cvalue){ + strcat(*cvalue,"}"); + p += strlen(*cvalue); + } + else + error++; + done++; + case ' ': p++; + break; + case '&': if (*(p+1) == '&'){ /* looking for && */ + p += 2; + next_condition++; + } + else{ + error++; + done++; + } + break; + case '=': /* looking for => or -> */ + case '-': if ((*(p+1) == '>') && (!next_condition)){ + is_save = (*p == '-'); + p += 2; + } + else + error++; + done++; + break; + default : done++; + error++; + break; + } + } + *len = p - data; + return error ? -1 : (*cvalue ? 1 : 0); + } + + TOKEN_VALUE * + parse_group_data (char *data, int *error) + { + TOKEN_VALUE *rvalue; + char *p; + int offset, err = 0; + + if(error) + *error = 0; + + if (!data) + return (TOKEN_VALUE *) NULL; + + rvalue = (TOKEN_VALUE *) alloc_mem(sizeof(TOKEN_VALUE)); + if (p = advance_to_char(data,';', STRICTLY, &offset)){ + rvalue->testxt = cpystr(p); + data += strlen(p) + 1 + offset; + rvalue->next = parse_group_data(data, error); + } + else if (p = advance_to_char(data,'}', STRICTLY, NULL)) + rvalue->testxt = cpystr(p); + else if (data && *data == '}') + rvalue->testxt = cpystr(""); + else{ + err++; + free_token_value(&rvalue); + } + if (error) + *error += err; + return(rvalue); + } + + CONDITION_S * + fill_condition(char *data) + { + CONDITION_S *condition; + int i, done, error = 0; + char *group; + + for (i = 0, done = 0; !done && (i < NTOKENS); i++) + done = strncmp(data,token_rules[i], strlen(token_rules[i])) ? 0 : 1; + if (done){ + condition = (CONDITION_S *) alloc_mem(sizeof(CONDITION_S)); + condition->tname = cpystr(token_rules[--i]); + } + else + return (CONDITION_S *)NULL; + + data += strlen(token_rules[i]); + for (; *data && *data == ' '; data++); + if (*data){ + for (i = 0, done = 0; !done && (i < NREL); i++) + done = strncmp(data, rel_rules_test[i].value, 2) ? 0 : 1; + if (done) + condition->ttype = rel_rules_test[--i].ttype; + else{ + free_condition(&condition); + return (CONDITION_S *) NULL; + } + } + data += 2; + for (; *data && *data == ' '; data++); + if (*data++ != '{'){ + free_condition(&condition); + return (CONDITION_S *) NULL; + } + group = advance_to_char(data,'}', STRICTLY, &error); + if (group || (!group && error < 0)){ + condition->value = parse_group_data(data, &error); + if(group && error) + free_condition(&condition); + if(group) + fs_give((void **) &group); + } + else + free_condition(&condition); + return condition; + } + + /* eoc = end of condition, equal to -1 on error */ + CONDITION_S * + parse_condition (char *data, int *eoc) + { + CONDITION_S *condition = NULL; + char *p = data, *cvalue; + int len, error = 0, rv; + + if((rv = isolate_condition(data, &cvalue, &len)) > 0){ + if(condition = fill_condition(cvalue)) + condition->next = parse_condition(data+len, eoc); + else + error++; + } + *eoc += len; + if (error) + *eoc = -1; + return condition; + } + + RULEACTION_S * + parse_action (char *data, int context) + { + int i, done; + RULEACTION_S *raction = NULL; + char *function, *p = data; + + if (!p) + return (RULEACTION_S *) NULL; + + for (; *p && *p == ' '; p++); + if (!*p) + return (RULEACTION_S *) NULL; + + if (is_save){ + raction = (RULEACTION_S *) alloc_mem(sizeof(RULEACTION_S)); + raction->function = cpystr("_SAVE_"); + raction->value = (TOKEN_VALUE *) alloc_mem(sizeof(TOKEN_VALUE)); + raction->context |= FOR_SAVE; + raction->exec = extended_value; + raction->value->testxt = cpystr(p); + return raction; + } + for (i = 0, done = 0; !done && (i < NFCN); i++) + done = (strstr(p,rule_fcns[i].name) == p); + p += done ? strlen(rule_fcns[--i].name) + 1 : 0; + if(!*p || (rule_fcns[i].what_for && !(rule_fcns[i].what_for & context))) + return (RULEACTION_S *) NULL; + if (done){ + raction = (RULEACTION_S *) alloc_mem(sizeof(RULEACTION_S)); + /* We assign raction->token to be subject. This is not necessary for + most rules. It is done only for rules that need it and will not + make any difference in rules that do not need it. It will hopefully + reduce complexity in the language + */ + raction->token = cpystr(SUBJ_TOKEN); + raction->function = cpystr(rule_fcns[i].name); + raction->context = rule_fcns[i].what_for; + raction->exec = rule_fcns[i].execute; + raction->value = (TOKEN_VALUE *) alloc_mem(sizeof(TOKEN_VALUE)); + raction->value->testxt = advance_to_char(p,'}', STRICTLY, NULL); + if(!raction->value->testxt) + free_ruleaction(&raction); + return raction; + } + + done = (((function = strstr(p, "_TRIM_")) != NULL) + ? 1 : ((function = strstr(p, "_COPY_")) != NULL) + ? 2 : ((function = strstr(p, "_EXEC_")) != NULL) + ? 3 : ((function = strstr(p, "_REXTRIM_")) != NULL) + ? 4 : ((function = strstr(p, "_REPLACE_")) != NULL) + ? 5 : 0); + + if(!function) + return (RULEACTION_S *) NULL; + + *function = '\0'; + raction = (RULEACTION_S *) alloc_mem(sizeof(RULEACTION_S)); + raction->token = get_name_token(p); + *function = '_'; + p += strlen(raction->token) + 1; + for (; *p && *p == ' '; p++); + if (!strncmp(p,":=",2)) + p += 2; + else{ + free_ruleaction(&raction); + return (RULEACTION_S *) NULL; + } + for (; *p && *p == ' '; p++); + if (p != function){ + free_ruleaction(&raction); + return (RULEACTION_S *) NULL; + } + p += done <= 3 ? 6 : 9; /* 6 = strlen("_EXEC_"), 9 = strlen("_REPLACE_") */ + if (*p != '{'){ + free_ruleaction(&raction); + return (RULEACTION_S *) NULL; + } + *p = '\0'; + for(i = 0; i < NFCN && strcmp(function, rule_fcns[i].name);i++); + raction->function = cpystr(function); + raction->is_trim = strcmp(function,"_TRIM_") ? 0 : 1; + raction->is_rextrim = strcmp(function,"_REXTRIM_") ? 0 : 1; + raction->is_replace = strcmp(function,"_REPLACE_") ? 0 : 1; + raction->context = rule_fcns[i].what_for; + raction->exec = rule_fcns[i].execute; + *p++ = '{'; + if((raction->value = parse_group_data(p, NULL)) == NULL + || raction->value->testxt == NULL) + free_ruleaction(&raction); + if(raction && raction->is_rextrim) + parse_patterns_into_action(&raction->value); + return raction; + } + + RULE_S * + parse_rule (char *data, int context) + { + RULE_S *prule; /*parsed rule */ + int len = 0; + + if (!(prule = (RULE_S *) alloc_mem(sizeof(RULE_S))) || + !(prule->condition = parse_condition(data, &len)) || + !(prule->action = parse_action(data+len, context))) + free_rule(&prule); + + return prule; + } + + RULELIST * + get_rule_list(char **list, int context, int i) + { + RULE_S *rule; + RULELIST *trulelist = NULL; + + if (list[i] && *list[i]){ + if(rule = parse_rule(list[i], context)){ + trulelist = (RULELIST *)alloc_mem(sizeof(RULELIST)); + trulelist->prule = rule; + trulelist->next = get_rule_list(list, context, i+1); + } + else + trulelist = get_rule_list(list, context, i+1); + } + return trulelist; + } + + PRULELIST_S * + add_prule(PRULELIST_S *rule_list, PRULELIST_S *rule) + { + if (!rule_list) + rule_list = (PRULELIST_S *) alloc_mem(sizeof(PRULELIST_S)); + + if(rule_list->next) + rule_list->next = add_prule(rule_list->next, rule); + else{ + if (rule_list->rlist) + rule_list->next = rule; + else + rule_list = rule; + } + return rule_list; + } + + void + add_rule(int code, int context) + { + char **list = ps_global->vars[code].current_val.l; + PRULELIST_S *prulelist, *trulelist, *orulelist; + + if (list && *list && **list){ + trulelist = (PRULELIST_S *)alloc_mem(sizeof(PRULELIST_S)); + trulelist->varnum = code; + if (trulelist->rlist = get_rule_list(list, context, 0)) + ps_global->rule_list = add_prule(ps_global->rule_list, trulelist); + else + free_parsed_rule_list(&trulelist); + } + } + + /* see create_rule_list below */ + void + set_rule_list(struct variable *vars) + { + set_current_val(&vars[V_THREAD_DISP_STYLE_RULES], TRUE, TRUE); + set_current_val(&vars[V_THREAD_INDEX_STYLE_RULES], TRUE, TRUE); + set_current_val(&vars[V_COMPOSE_RULES], TRUE, TRUE); + set_current_val(&vars[V_FORWARD_RULES], TRUE, TRUE); + set_current_val(&vars[V_INDEX_RULES], TRUE, TRUE); + set_current_val(&vars[V_KEY_RULES], FALSE, TRUE); + set_current_val(&vars[V_REPLACE_RULES], TRUE, TRUE); + set_current_val(&vars[V_REPLY_INDENT_RULES], TRUE, TRUE); + set_current_val(&vars[V_REPLY_LEADIN_RULES], TRUE, TRUE); + set_current_val(&vars[V_RESUB_RULES], TRUE, TRUE); + set_current_val(&vars[V_SAVE_RULES], TRUE, TRUE); + set_current_val(&vars[V_SMTP_RULES], TRUE, TRUE); + set_current_val(&vars[V_SORT_RULES], TRUE, TRUE); + set_current_val(&vars[V_STARTUP_RULES], TRUE, TRUE); + } + + /* see set_rule_list above */ + void + create_rule_list(struct variable *vars) + { + set_rule_list(vars); + add_rule(V_THREAD_DISP_STYLE_RULES, FOR_THREAD); + add_rule(V_THREAD_INDEX_STYLE_RULES, FOR_THREAD); + add_rule(V_COMPOSE_RULES, FOR_COMPOSE); + add_rule(V_FORWARD_RULES, FOR_COMPOSE); + add_rule(V_INDEX_RULES, FOR_INDEX); + add_rule(V_KEY_RULES, FOR_KEY); + add_rule(V_REPLACE_RULES, FOR_REPLACE); + add_rule(V_REPLY_INDENT_RULES, FOR_COMPOSE); + add_rule(V_REPLY_LEADIN_RULES, FOR_REPLY_INTRO); + add_rule(V_RESUB_RULES, FOR_RESUB|FOR_TRIM); + add_rule(V_SAVE_RULES, FOR_SAVE); + add_rule(V_SMTP_RULES, FOR_COMPOSE); + add_rule(V_SORT_RULES, FOR_SORT); + add_rule(V_STARTUP_RULES, FOR_STARTUP); + } + + int + condition_contains_token(CONDITION_S *condition, char *token) + { + return condition ? (!strcmp(condition->tname, token) + ? 1 + : condition_contains_token(condition->next, token)) + : 0; + } + + RULELIST * + get_rulelist_from_code(int code, PRULELIST_S *list) + { + return list ? (list->varnum == code ? list->rlist + : get_rulelist_from_code(code, list->next)) + : (RULELIST *) NULL; + } + + char * + test_rule(RULELIST *rlist, int ctxt, ENVELOPE *env, int *n) + { + char *result; + + if(!rlist) + return NULL; + + if (result = process_rule(rlist->prule, ctxt, env)) + return result; + else{ + (*n)++; + return test_rule(rlist->next, ctxt, env, n); + } + } + + RULE_S * + get_rule (RULELIST *rule, int n) + { + return rule ? (n ? get_rule(rule->next, n-1) : rule->prule) + : (RULE_S *) NULL; + } + + /* get_result_rule: + * Parameters: list: the list of rules to be passed to the function to check + * rule_context: context of the rule + * env : envelope used to check the rule, if needed. + * + * Returns: The value of the first rule that is satisfied in the list, or + * NULL if not. This function should be called in the following + * way (notice that memory is freed by caller). + * + * You should use this function to obtain the result of a rule. You can + * also call directly "process_rule", but I advice to use this function if + * there's no difference on which function to call. + + RULE_RESULT *rule; + + rule = (RULE_RESULT *) + get_result_rule(V_SOME_RULE, context, envelope); + + if (rule){ + assign the value of rule->result; + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + */ + + RULE_RESULT * + get_result_rule(int code, int rule_context, ENVELOPE *env) + { + char *rule_result; + RULE_RESULT *rule = NULL; + RULELIST *rlist; + int n = 0; + + if(!(rule_context & FOR_RULE)) + rule_context |= FOR_RULE; + rlist = get_rulelist_from_code(code, ps_global->rule_list); + if (rlist){ + rule_result = test_rule(rlist, rule_context, env, &n); + if (rule_result && *rule_result){ + rule = (RULE_RESULT *) fs_get (sizeof(RULE_RESULT)); + rule->result = rule_result; + rule->number = n; + } + } + return rule; + } + + char *get_rule_result(int rule_context, char *newfolder, int code) + { + char *rule_result = NULL; + ENVELOPE *news_envelope; + RULE_RESULT *rule; + + if (IS_NEWS(ps_global->mail_stream)){ + news_envelope = mail_newenvelope(); + news_envelope->newsgroups = cpystr(newfolder); + } + else + news_envelope = NULL; + + rule = get_result_rule(code, rule_context, news_envelope); + + if (news_envelope) + mail_free_envelope (&news_envelope); + + if (rule){ + rule_result = cpystr(rule->result); + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + return rule_result; + } + + /* process_rule: + Parameters: rule_data, is a rule. It's obtained as + rule_data = ps_global->VAR_SOME_RULE[n], for + some integer n + rule_context: context of the rule, and + env: An envelope if needed. + + Returns : The value of the processed rule_data if the processing was + successful and matches context and possibly the envelope, or + NULL if there's no match + */ + + char * + process_rule (RULE_S *prule, int rule_context, ENVELOPE *env) + { + char *result = NULL; + int rv; + CONDITION_S *condition; + + if(!prule) + return NULL; + + if(!(rule_context & FOR_RULE)) + rule_context |= FOR_RULE; + + for(condition = prule->condition; + condition && + (rv = test_condition(condition, rule_context, env)); + condition = condition->next); + + if(rv && !condition) + result = (prule->action->exec)(prule->action, rule_context, env); + + return result; + } + + TOKEN_VALUE * + copy_parsed_value(TOKEN_VALUE *value, int ctxt, ENVELOPE *env) + { + TOKEN_VALUE *tval = NULL; + + if(!value) + return NULL; + + if(value->testxt){ + tval = (TOKEN_VALUE *) alloc_mem(sizeof(TOKEN_VALUE)); + tval->testxt = detoken_src(value->testxt, ctxt, env, NULL, NULL, NULL); + tval->voidtxt = value->voidtxt; + tval->codefcn = value->codefcn; + } + if(value->next) + tval->next = copy_parsed_value(value->next, ctxt, env); + + return tval; + } + + void + free_parsed_value(TOKEN_VALUE **value) + { + TOKEN_VALUE *tval = NULL; + + if(!*value) + return; + + if((*value)->testxt) + fs_give((void **)&(*value)->testxt); + + if((*value)->next) + free_parsed_value(&(*value)->next); + + fs_give((void **)value); + } + + + int + test_condition(CONDITION_S *condition, int rule_context, ENVELOPE *env) + { + int next_step; + TOKEN_VALUE *group; + + group = copy_parsed_value(condition->value, rule_context, env); + next_step = (*rel_rules_test[condition->ttype].execute)(condition, group, env, rule_context); + free_parsed_value(&group); + return next_step; + } + + /* returns the name of the token it found or NULL if there is no token, the + * real value of the token is obtained by calling the detoken_src function. + */ + + char * + get_name_token (char *condition) + { + char *p = NULL, *q, *s; + + if ((q = strchr(condition,'_')) && (s = strchr(q+1,'_'))){ + char c = *++s; + *s = '\0'; + p = cpystr(q); + *s = c; + } + return p; + } + + /* This function tests if a string contained in the variable "group" is + * in the "condition" + */ + int test_in (CONDITION_S *condition, TOKEN_VALUE *group, ENVELOPE *env, + int context) + { + int rv = 0; + char *test; + TOKEN_VALUE *test_group = group; + + test = env && env->sparep && ((SPAREP_S *)env->sparep)->flag & USE_RAW_SP + ? cpystr(((SPAREP_S *)env->sparep)->value) + : detoken_src(condition->tname, context, env, NULL, NULL, NULL); + if (test){ + while (rv == 0 && test_group){ + if(!*test || strstr(test_group->testxt, test)) + rv++; + else + test_group = test_group->next; + } + fs_give((void **)&test); + } + return rv; + } + + int test_ni (CONDITION_S *condition, TOKEN_VALUE *group, + ENVELOPE *env, int context) + { + int rv = 0; + char *test; + TOKEN_VALUE *test_group = group; + + test = env && env->sparep && ((SPAREP_S *)env->sparep)->flag & USE_RAW_SP + ? cpystr(((SPAREP_S *)env->sparep)->value) + : detoken_src(condition->tname, context, env, NULL, NULL, NULL); + if (test){ + if(!test_group) + rv++; + while (rv == 0 && test_group){ + if(!*test_group->testxt || strstr(test, test_group->testxt)) + rv++; + else + test_group = test_group->next; + } + fs_give((void **)&test); + } + return rv; + } + + int test_not_in (CONDITION_S *condition, TOKEN_VALUE *group, + ENVELOPE *env, int context) + { + return !test_in(condition, group, env, context); + } + + int test_not_ni (CONDITION_S *condition, TOKEN_VALUE *group, + ENVELOPE *env, int context) + { + return !test_ni(condition, group, env, context); + } + + int test_eq (CONDITION_S *condition, TOKEN_VALUE *group, + ENVELOPE *env, int context) + { + int rv = 0; + char *test; + TOKEN_VALUE *test_group = group; + + test = env && env->sparep && ((SPAREP_S *)env->sparep)->flag & USE_RAW_SP + ? cpystr(((SPAREP_S *)env->sparep)->value) + :detoken_src(condition->tname, context, env, NULL, NULL, NULL); + if (test){ + while (rv == 0 && test_group){ + if((!*test && !*test_group->testxt) || !strcmp(test_group->testxt, test)) + rv++; + else + test_group = test_group->next; + } + fs_give((void **)&test); + } + return rv; + } + + int test_not_eq (CONDITION_S *condition, TOKEN_VALUE *group, + ENVELOPE *env, int context) + { + return !test_eq(condition, group, env, context); + } + + char * + do_trim (char *test, TOKEN_VALUE *tval) + { + char *begin_text; + int offset = 0; + + if (!tval) + return test; + + while(begin_text = strstr(test+offset,tval->testxt)){ + strcpy(begin_text, begin_text+strlen(tval->testxt)); + offset = begin_text - test; + } + + return do_trim(test, tval->next); + } + + char * + trim (RULEACTION_S *action, int context, ENVELOPE *env) + { + char *begin_text, *test; + RULEACTION_S *taction = action; + int offset; + + if (taction->context & context){ + if (test = detoken_src(taction->token, context, env, NULL, NULL, NULL)) + test = do_trim(test, taction->value); + return test; + } + return NULL; + } + + + char * + do_rextrim (char *test, TOKEN_VALUE *tval) + { + char *begin_text, *trim_text; + int offset = 0; + + if (!tval) + return test; + + trim_text = expand(test, tval->voidtxt); + while(trim_text && (begin_text = strstr(test+offset,trim_text))){ + strcpy(begin_text, begin_text+strlen(trim_text)); + offset = begin_text - test; + } + + return do_rextrim(test, tval->next); + } + + char * + rextrim (RULEACTION_S *action, int context, ENVELOPE *env) + { + char *test = NULL; + RULEACTION_S *taction = action; + + if (taction->context & context && + (test = detoken_src(taction->token, context, env, NULL, NULL, NULL))) + test = do_rextrim(test, taction->value); + return test; + } + + char * + raw_value (RULEACTION_S *action, int context, ENVELOPE *env) + { + return (action->context & context) ? cpystr(action->value->testxt) : NULL; + } + + char * + extended_value (RULEACTION_S *action, int ctxt, ENVELOPE *env) + { + return (action->context & ctxt) + ? detoken_src(action->value->testxt, ctxt, env, NULL, NULL, NULL) + : NULL; + } + + /* advances given_string until it finds given_char */ + char * + advance_to_char(char *given_string, char given_char, int flag, int *error) + { + char *b, *s, c; + int i, err = 0, quoted ; + + if (error) + *error = 0; + + if (!given_string || !*given_string) + return NULL; + + b = s = cpystr(given_string); + for(i = 0, quoted = 0, c = *s; c ; c = *++s){ + if(c == '\\'){ + quoted++; + continue; + } + if(quoted){ + quoted = 0; + if (c == given_char){ + err += flag & STRICTLY ? 0 : 1; + err++; + break; + } + b[i++] = '\\'; + } + if(c == given_char){ + err += flag & STRICTLY ? 0 : 1; + break; + } + b[i++] = c; + } + b[i] = '\0'; + if (b && (strlen(b) == strlen(given_string)) && (flag & STRICTLY)) + return NULL; /* character not found */ + + if(b && !*b){ + fs_give((void **)&b); + err = -1; + } + + if (error) + *error = err; + + return b; + } + + /* Regular Expressions Support */ + char * + expand (char *string, void *pattern) + { + char c, *ret_string = NULL; + regmatch_t pmatch; + + if((regex_t *)pattern == NULL) + return NULL; + + if(regexec((regex_t *)pattern, string , 1, &pmatch, 0) == 0 + && pmatch.rm_so < pmatch.rm_eo){ + c = string[pmatch.rm_eo]; + string[pmatch.rm_eo] = '\0'; + ret_string = cpystr(string+pmatch.rm_so); + string[pmatch.rm_eo] = c; + } + return ret_string; + } + + + char * + exec_fcn (RULEACTION_S *action, int ctxt, ENVELOPE *env) + { + STORE_S *output_so; + gf_io_t gc, pc; + char *status, *rv, *cmd, *test; + + if(!(action->context & ctxt)) + return NULL; + + if((test = detoken_src(action->token, ctxt, env, NULL, NULL, NULL)) != NULL) + gf_set_readc(&gc, test, (unsigned long)strlen(test), CharStar, 0); + + if((output_so = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL) + gf_set_so_writec(&pc, output_so); + + cmd = (char *)fs_get((strlen(action->value->testxt) + strlen("_TMPFILE_") + 2)*sizeof(char)); + sprintf(cmd,"%s _TMPFILE_", action->value->testxt); + status = (*ps_global->tools.exec_rule)(cmd, gc, pc); + + so_seek(output_so, 0L, 0); + rv = cpystr(output_so->dp); + gf_clear_so_writec(output_so); + so_give(&output_so); + if(test) + fs_give((void **)&test); + + return status ? NULL : rv; + } + + ENVELOPE * + rules_fetchenvelope(INDEXDATA_S *idata, int *we_clear) + { + ENVELOPE *env; + + if (idata->no_fetch){ + if(we_clear) + *we_clear = 1; + env = mail_newenvelope(); + env->from = copyaddrlist(idata->from); + env->to = copyaddrlist(idata->to); + env->cc = copyaddrlist(idata->cc); + env->sender = copyaddrlist(idata->sender); + env->subject = cpystr(idata->subject); + env->date = cpystr((unsigned char *) idata->date); + env->newsgroups = cpystr(idata->newsgroups); + return env; + } + if(we_clear) + *we_clear = 0; + env = pine_mail_fetchenvelope(idata->stream, idata->rawno); + return env; + } diff -rc alpine-2.00/pith/rules.h alpine-2.00.I.USE/pith/rules.h *** alpine-2.00/pith/rules.h 2011-02-07 20:34:02.000000000 -0800 --- alpine-2.00.I.USE/pith/rules.h 2011-02-07 20:33:42.000000000 -0800 *************** *** 0 **** --- 1,151 ---- + /* Included file rules.h */ + + #ifndef PITH_RULES_INCLUDED + #define PITH_RULES_INCLUDED + + #include "../pith/conftype.h" + #include "../pith/detoken.h" + #include "../pith/indxtype.h" + #include "../pith/rulestype.h" + + int is_save; /* this rule has the form condition -> folder */ + + /* Exported prototypes */ + + void create_rule_list (struct variable *); + SPAREP_S *get_sparep_for_rule(char *, int); + void free_sparep_for_rule(void **); + void free_parsed_rule_list (PRULELIST_S **); + RULE_RESULT *get_result_rule (int, int, ENVELOPE *); + char *get_rule_result (int , char *, int); + char *process_rule (RULE_S *, int, ENVELOPE *); + char **functions_for_token (char *); + RULELIST *get_rulelist_from_code (int, PRULELIST_S *); + RULE_S *get_rule (RULELIST *, int); + int condition_contains_token (CONDITION_S *, char *); + int context_for_function (char *); + ENVELOPE *rules_fetchenvelope(INDEXDATA_S *idata, int *we_clear); + + /* Separators: + * + * A separator is a string that separates the rule condition with the rule + * action. Below is the list of separators + * + */ + + #define SAVE_TO_SEP "->" + #define APPLY_SEP "=>" + + /*------- Definitions of tokens -------*/ + /*------ Keep the list alphabetically sorted, thanks -------*/ + + #define ADDR_TOKEN "_ADDRESS_" + #define ADDCC_TOKEN "_ADDRESSCC_" + #define ADDRECIP_TOKEN "_ADDRESSRECIPS_" + #define ADDTO_TOKEN "_ADDRESSTO_" + #define BCC_TOKEN "_BCC_" + #define CC_TOKEN "_CC_" + #define COLLECT_TOKEN "_COLLECTION_" + #define FLAG_TOKEN "_FLAG_" + #define FOLDER_TOKEN "_FOLDER_" + #define FADDRESS_TOKEN "_FORWARDADDRESS_" + #define FFROM_TOKEN "_FORWARDFROM_" + #define FROM_TOKEN "_FROM_" + #define KEY_TOKEN "_PKEY_" + #define LCC_TOKEN "_LCC_" + #define NICK_TOKEN "_NICK_" + #define OTEXT_TOKEN "_OPENINGTEXT_" + #define OTEXTNQ_TOKEN "_OPENINGTEXTNQ_" + #define PROCID_TOKEN "_PROCID_" + #define ROLE_TOKEN "_ROLE_" + #define SCREEN_TOKEN "_SCREEN_" + #define SEND_TOKEN "_SENDER_" + #define SUBJ_TOKEN "_SUBJECT_" + #define THDDSPSTY_TOKEN "_THREADSTYLE_" + #define THDNDXSTY_TOKEN "_THREADINDEX_" + #define TO_TOKEN "_TO_" + + /*------ Definitions of relational operands -------------*/ + + typedef struct { + char *value; + TestType ttype; + int (*execute)(); + } REL_TOKEN; + + /* Relational Operands */ + #define AND_REL "&&" /* For putting more than one condition */ + #define IN_REL "<<" /* For belonging relation */ + #define NI_REL ">>" /* For contain relation */ + #define NOT_IN_REL "!<" /* Negation of IN_REL */ + #define NOT_NI_REL "!>" /* Negation of NI_REL */ + #define EQ_REL "==" /* Test of equality */ + #define NOT_EQ_REL "!=" /* Test of inequality */ + #define OPEN_SET "{" /* Braces to open a set */ + #define CLOSE_SET "}" /* Braces to close a set*/ + + /*--- Context in which these variables can be used ---*/ + + typedef struct use_context { + char *name; + int what_for; + } USE_IN_CONTEXT; + + + static USE_IN_CONTEXT tokens_use[] = { + {NICK_TOKEN, FOR_SAVE}, + {FROM_TOKEN, FOR_SAVE}, + {OTEXT_TOKEN, FOR_SAVE|FOR_FOLDER}, + {OTEXTNQ_TOKEN, FOR_SAVE|FOR_FOLDER}, + {ROLE_TOKEN, FOR_COMPOSE}, + {FOLDER_TOKEN, FOR_SAVE|FOR_FOLDER|FOR_THREAD|FOR_COMPOSE}, + {SUBJ_TOKEN, FOR_SAVE|FOR_FOLDER|FOR_COMPOSE}, + {FLAG_TOKEN, FOR_SAVE|FOR_FLAG}, + {COLLECT_TOKEN, FOR_SAVE|FOR_COMPOSE|FOR_FOLDER|FOR_THREAD}, + {THDDSPSTY_TOKEN, FOR_THREAD}, + {THDNDXSTY_TOKEN, FOR_THREAD}, + {ADDR_TOKEN, FOR_SAVE|FOR_FOLDER}, + {TO_TOKEN, FOR_SAVE}, + {ADDTO_TOKEN, FOR_SAVE|FOR_COMPOSE}, + {ADDCC_TOKEN, FOR_SAVE|FOR_COMPOSE}, + {ADDRECIP_TOKEN, FOR_SAVE|FOR_COMPOSE}, + {SCREEN_TOKEN, FOR_KEY}, + {KEY_TOKEN, FOR_KEY}, + {SEND_TOKEN, FOR_SAVE}, + {CC_TOKEN, FOR_SAVE}, + {BCC_TOKEN, FOR_COMPOSE}, + {LCC_TOKEN, FOR_COMPOSE}, + {FFROM_TOKEN, FOR_COMPOSE}, + {FADDRESS_TOKEN, FOR_COMPOSE}, + {NULL, FOR_NOTHING} + }; + + + typedef struct { + char *name; + char* (*execute)(); + int what_for; + } RULE_FCN; + + #define COMMAND_FCN "_COMMAND_" + #define COPY_FCN "_COPY_" + #define EXEC_FCN "_EXEC_" + #define INDEX_FCN "_INDEX_" + #define REPLACE_FCN "_REPLACE_" + #define REPLYSTR_FCN "_RESTR_" + #define REPLY_FCN "_REPLY_" + #define RESUB_FCN "_RESUB_" + #define REXTRIM_FCN "_REXTRIM_" + #define SAVE_FCN "_SAVE_" + #define SIGNATURE_FCN "_SIGNATURE_" + #define SMTP_FCN "_SMTP_" + #define SORT_FCN "_SORT_" + #define STARTUP_FCN "_STARTUP_" + #define THRDSTYLE_FCN "_THREADSTYLE_" + #define THRDINDEX_FCN "_THREADINDEX_" + #define TRIM_FCN "_TRIM_" + + #define STRICTLY 0x1 + #define RELAXED 0x2 + + #endif /* PITH_RULES_INCLUDED */ diff -rc alpine-2.00/pith/rulestype.h alpine-2.00.I.USE/pith/rulestype.h *** alpine-2.00/pith/rulestype.h 2011-02-07 20:34:02.000000000 -0800 --- alpine-2.00.I.USE/pith/rulestype.h 2011-02-07 20:33:42.000000000 -0800 *************** *** 0 **** --- 1,78 ---- + #ifndef PITH_RULESTYPE_INCLUDED + #define PITH_RULESTYPE_INCLUDED + + typedef struct rule { + char *result; /* The result of the rule */ + int number; /* The number of the rule that succeded, -1 if not */ + } RULE_RESULT; + + typedef struct { + char *value; + int type; + } RULE_ACTION; + + + #define TOKEN_VALUE struct tokenvalue_s + #define CONDITION_S struct condition_s + #define RULEACTION_S struct ruleaction_s + #define RULE_S struct rule_s + #define RULELIST struct rulelist_s + #define PRULELIST_S struct parsedrulelist_s + + #define FREEREGEX 1 + + TOKEN_VALUE { + char *testxt; + void *voidtxt; + int codefcn; + TOKEN_VALUE *next; + }; + + typedef enum {Equal, Subset, Includes, + NotEqual, NotSubset, NotIncludes, + EndTypes} TestType; + + CONDITION_S { + char *tname; /* tname ttype {value} */ + TestType ttype; /* tname ttype {value} */ + TOKEN_VALUE *value; /* value to check against */ + CONDITION_S *next; /* next condition to test */ + }; + + RULEACTION_S { + char *token; /* token := function{value} or token = null */ + char *function; /* token := function{value} or simply function{value}*/ + TOKEN_VALUE *value; /* token := function{value} or simply function{value}*/ + int context; /* context in which this rule can be used */ + char* (*exec)(); + unsigned int is_trim:1; + unsigned int is_rextrim:1; + unsigned int is_replace:1; + }; + + RULE_S { + CONDITION_S *condition; + RULEACTION_S *action; + }; + + RULELIST { + RULE_S *prule; + RULELIST *next; + }; + + PRULELIST_S { + int varnum; /* number associated to the variable */ + RULELIST *rlist; + PRULELIST_S *next; + }; + + #define USE_RAW_SP 0x001 + #define PROCESS_SP 0x010 + + typedef struct sparep { + int flag; + char *value; + } SPAREP_S; + + + #endif /* PITH_RULESTYPE_INCLUDED */ diff -rc alpine-2.00/pith/save.c alpine-2.00.I.USE/pith/save.c *** alpine-2.00/pith/save.c 2008-05-02 14:58:34.000000000 -0700 --- alpine-2.00.I.USE/pith/save.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 421,426 **** --- 421,427 ---- { int rv, rc, j, our_stream = 0, cancelled = 0; int delete, filter, k, worry_about_keywords = 0; + int flags_for_save = F_ANS|F_FWD|F_FLAG|F_SEEN|F_KEYWORD; char *save_folder, *seq, *flags = NULL, date[64], tmp[MAILTMPLEN]; long i, nmsgs, rawno; size_t len; *************** *** 864,870 **** STRING msg; pkg.stream = stream; ! pkg.flags = NULL; pkg.date = date; pkg.msg = &msg; pkg.msgmap = msgmap; --- 865,871 ---- STRING msg; pkg.stream = stream; ! pkg.flags = flgs & SV_FIX_DELS ? NULL : cpystr("\\DELETED"); pkg.date = date; pkg.msg = &msg; pkg.msgmap = msgmap; *************** *** 938,944 **** mc = (rawno > 0L && stream && rawno <= stream->nmsgs) ? mail_elt(stream, rawno) : NULL; ! flags = flag_string(stream, rawno, F_ANS|F_FWD|F_FLAG|F_SEEN|F_KEYWORD); if(mc && mc->day) mail_date(date, mc); --- 939,946 ---- mc = (rawno > 0L && stream && rawno <= stream->nmsgs) ? mail_elt(stream, rawno) : NULL; ! flags_for_save |= flgs & SV_FIX_DELS ? 0 : F_DEL; ! flags = flag_string(stream, rawno, flags_for_save); if(mc && mc->day) mail_date(date, mc); *************** *** 946,952 **** *date = '\0'; rv = save_fetch_append(stream, mn_m2raw(msgmap, i), ! NULL, save_stream, save_folder, context, mc ? mc->rfc822_size : 0L, flags, date, so); if(flags) --- 948,954 ---- *date = '\0'; rv = save_fetch_append(stream, mn_m2raw(msgmap, i), ! NULL, save_stream, folder, context, mc ? mc->rfc822_size : 0L, flags, date, so); if(flags) *************** *** 1043,1048 **** --- 1045,1051 ---- MESSAGECACHE *mc; char *fetch; int rc; + int flags_for_save = F_ANS|F_FWD|F_FLAG|F_SEEN|F_KEYWORD; unsigned long raw, hlen, tlen, mlen; if(pkg->so && (pkg->msgno > 0L)) { *************** *** 1051,1060 **** ? mail_elt(pkg->stream, raw) : NULL; if(mc){ size = mc->rfc822_size; ! if(pkg->flags) fs_give((void **) &pkg->flags); ! ! pkg->flags = flag_string(pkg->stream, raw, F_ANS|F_FWD|F_FLAG|F_SEEN|F_KEYWORD); } if(mc && mc->day) --- 1054,1065 ---- ? mail_elt(pkg->stream, raw) : NULL; if(mc){ size = mc->rfc822_size; ! if(pkg->flags){ ! if(strstr(pkg->flags,"\\DELETED")) ! flags_for_save |= F_DEL; fs_give((void **) &pkg->flags); ! } ! pkg->flags = flag_string(pkg->stream, raw, flags_for_save); } if(mc && mc->day) *************** *** 1136,1141 **** --- 1141,1147 ---- snprintf(buf, sizeof(buf), "Message to save shrank: source msg # %ld may be saved incorrectly", mn_raw2m(pkg->msgmap, raw)); + if(F_OFF(F_IGNORE_SIZE, ps_global)) q_status_message(SM_ORDER, 0, 3, buf); } else{ diff -rc alpine-2.00/pith/send.c alpine-2.00.I.USE/pith/send.c *** alpine-2.00/pith/send.c 2008-08-06 11:25:58.000000000 -0700 --- alpine-2.00.I.USE/pith/send.c 2011-02-07 20:33:43.000000000 -0800 *************** *** 43,48 **** --- 43,49 ---- #include "../pith/ablookup.h" #include "../pith/sort.h" #include "../pith/smime.h" + #include "../pith/rules.h" #include "../c-client/smtp.h" #include "../c-client/nntp.h" *************** *** 52,58 **** /* name::type::canedit::writehdr::localcopy::rcptto */ PINEFIELD pf_template[] = { {"X-Auth-Received", FreeText, 0, 1, 1, 0}, /* N_AUTHRCVD */ ! {"From", Address, 0, 1, 1, 0}, {"Reply-To", Address, 0, 1, 1, 0}, {TONAME, Address, 1, 1, 1, 1}, {CCNAME, Address, 1, 1, 1, 1}, --- 53,59 ---- /* name::type::canedit::writehdr::localcopy::rcptto */ PINEFIELD pf_template[] = { {"X-Auth-Received", FreeText, 0, 1, 1, 0}, /* N_AUTHRCVD */ ! {"From", Address, 1, 1, 1, 0}, {"Reply-To", Address, 0, 1, 1, 0}, {TONAME, Address, 1, 1, 1, 1}, {CCNAME, Address, 1, 1, 1, 1}, *************** *** 256,261 **** --- 257,269 ---- if(exists & FEX_ISFILE){ context_apply(tmp, p_cntxt, mbox, sizeof(tmp)); + #ifndef _WINDOWS + if (!struncmp(tmp, "#md/",4) || !struncmp(tmp, "#mc/", 4)){ + char tmp2[MAILTMPLEN]; + maildir_file_path(tmp, tmp2); + strcpy(tmp, tmp2); + } + #endif if(!(IS_REMOTE(tmp) || is_absolute_path(tmp))){ /* * The mbox is relative to the home directory. *************** *** 1228,1234 **** *p = *(p+4); pf->type = pf_template[i].type; ! pf->canedit = pf_template[i].canedit; pf->rcptto = pf_template[i].rcptto; pf->writehdr = pf_template[i].writehdr; pf->localcopy = pf_template[i].localcopy; --- 1236,1242 ---- *p = *(p+4); pf->type = pf_template[i].type; ! pf->canedit = (i == N_FROM) ? CAN_EDIT(ps_global) : pf_template[i].canedit; pf->rcptto = pf_template[i].rcptto; pf->writehdr = pf_template[i].writehdr; pf->localcopy = pf_template[i].localcopy; *************** *** 1737,1745 **** char error_buf[200], *error_mess = NULL, *postcmd; ADDRESS *a; ENVELOPE *fake_env = NULL; ! int addr_error_count, we_cancel = 0; long smtp_opts = 0L; ! char *verbose_file = NULL; BODY *bp = NULL; PINEFIELD *pf; BODY *origBody = body; --- 1745,1753 ---- char error_buf[200], *error_mess = NULL, *postcmd; ADDRESS *a; ENVELOPE *fake_env = NULL; ! int addr_error_count, we_cancel = 0, choice, num_rules = 0, added_rules = -1; long smtp_opts = 0L; ! char *verbose_file = NULL, **smtp_list; BODY *bp = NULL; PINEFIELD *pf; BODY *origBody = body; *************** *** 1892,1911 **** * OK, who posts what? We tried an mta_handoff above, but there * was either none specified or we decided not to use it. So, * if there's an smtp-server defined anywhere, */ ! if(alt_smtp_servers && alt_smtp_servers[0] && alt_smtp_servers[0][0]){ ! /*---------- SMTP ----------*/ ! dprint((4, "call_mailer: via TCP (%s)\n", ! alt_smtp_servers[0])); ! TIME_STAMP("smtp-open start (tcp)", 1); ! sending_stream = smtp_open(alt_smtp_servers, smtp_opts); ! } ! else if(ps_global->VAR_SMTP_SERVER && ps_global->VAR_SMTP_SERVER[0] ! && ps_global->VAR_SMTP_SERVER[0][0]){ ! /*---------- SMTP ----------*/ ! dprint((4, "call_mailer: via TCP\n")); ! TIME_STAMP("smtp-open start (tcp)", 1); ! sending_stream = smtp_open(ps_global->VAR_SMTP_SERVER, smtp_opts); } else if((postcmd = smtp_command(ps_global->c_client_error, sizeof(ps_global->c_client_error))) != NULL){ char *cmdlist[2]; --- 1900,1948 ---- * OK, who posts what? We tried an mta_handoff above, but there * was either none specified or we decided not to use it. So, * if there's an smtp-server defined anywhere, + * First we check for rules and make a list using the rules. */ ! if(ps_global->VAR_SMTP_RULES && ps_global->VAR_SMTP_RULES[0] ! && ps_global->VAR_SMTP_RULES[0][0]) ! while (ps_global->VAR_SMTP_RULES[num_rules]) num_rules++; ! ! if(num_rules){ ! int i, j; ! ! added_rules = 0; ! smtp_list = (char **) fs_get ((num_rules + 1)*sizeof(char*)); ! for (i = 0, j = 0; i < num_rules; i++){ ! RULELIST *rule = get_rulelist_from_code(V_SMTP_RULES, ! ps_global->rule_list); ! RULE_S *prule = get_rule(rule, i); ! if(prule){ ! char *rule_result = process_rule(prule, FOR_COMPOSE, header->env); ! if(rule_result && *rule_result){ ! smtp_list[j++] = cpystr(rule_result); ! added_rules++; ! } ! } ! } ! } ! ! if (added_rules < 0){ ! smtp_list = (char **) fs_get (sizeof(char*)); ! added_rules = 0; ! } ! smtp_list[added_rules] = NULL; ! ! choice = smtp_list && smtp_list[0] && smtp_list[0][0] ? 3 : ! (alt_smtp_servers && alt_smtp_servers[0] && alt_smtp_servers[0][0] ? 2 : ! (ps_global->VAR_SMTP_SERVER && ps_global->VAR_SMTP_SERVER[0] ! && ps_global->VAR_SMTP_SERVER[0][0] ? 1 : -1)); ! ! if(choice > 0){ ! /*---------- SMTP ----------*/ ! dprint((4, "call_mailer: via TCP (%s)\n",smtp_list[0])); ! TIME_STAMP("smtp-open start (tcp)", 1); ! sending_stream = smtp_open(choice == 3 ? smtp_list ! : (choice == 2 ? alt_smtp_servers ! : ps_global->VAR_SMTP_SERVER), smtp_opts); } else if((postcmd = smtp_command(ps_global->c_client_error, sizeof(ps_global->c_client_error))) != NULL){ char *cmdlist[2]; *************** *** 2141,2146 **** --- 2178,2185 ---- if(error_mess){ q_status_message(SM_ORDER | SM_DING, 4, 7, error_mess); dprint((1, "call_mailer ERROR: %s\n", error_mess)); + if (ps_global->send_immediately) + printf("%s\n",error_mess); } return(-1); diff -rc alpine-2.00/pith/send.h alpine-2.00.I.USE/pith/send.h *** alpine-2.00/pith/send.h 2008-06-30 15:03:35.000000000 -0700 --- alpine-2.00.I.USE/pith/send.h 2011-02-07 20:33:43.000000000 -0800 *************** *** 158,163 **** --- 158,165 ---- unsigned text_written:1; }; + #define CAN_EDIT(x) (!((x)->never_allow_changing_from) && \ + F_ON(F_ALLOW_CHANGING_FROM, (x))) #define TONAME "To" #define CCNAME "cc" diff -rc alpine-2.00/pith/sort.c alpine-2.00.I.USE/pith/sort.c *** alpine-2.00/pith/sort.c 2008-07-09 22:01:13.000000000 -0700 --- alpine-2.00.I.USE/pith/sort.c 2011-02-07 20:33:47.000000000 -0800 *************** *** 29,35 **** #include "../pith/signal.h" #include "../pith/busy.h" #include "../pith/icache.h" ! /* * global place to store mail_sort and mail_thread results --- 29,35 ---- #include "../pith/signal.h" #include "../pith/busy.h" #include "../pith/icache.h" ! #include "../pith/rules.h" /* * global place to store mail_sort and mail_thread results *************** *** 90,96 **** ----*/ void sort_folder(MAILSTREAM *stream, MSGNO_S *msgmap, SortOrder new_sort, ! int new_rev, unsigned int flags) { long raw_current, i, j; unsigned long *sort = NULL; --- 90,96 ---- ----*/ void sort_folder(MAILSTREAM *stream, MSGNO_S *msgmap, SortOrder new_sort, ! int new_rev, unsigned int flags, int first) { long raw_current, i, j; unsigned long *sort = NULL; *************** *** 100,105 **** --- 100,114 ---- int current_rev; MESSAGECACHE *mc; + if (first){ + if (new_sort == SortThread) + find_msgmap(stream, msgmap, flags, + ps_global->thread_cur_sort, new_rev); + else + sort_folder(stream, msgmap, new_sort, new_rev, flags, 0); + return; + } + dprint((2, "Sorting by %s%s\n", sort_name(new_sort), new_rev ? "/reverse" : "")); *************** *** 529,548 **** * argument also means arrival/reverse. */ int ! decode_sort(char *sort_spec, SortOrder *def_sort, int *def_sort_rev) { char *sep; char *fix_this = NULL; ! int x, reverse; if(!sort_spec || !*sort_spec){ ! *def_sort = SortArrival; *def_sort_rev = 0; return(0); } if(struncmp(sort_spec, "reverse", strlen(sort_spec)) == 0){ ! *def_sort = SortArrival; *def_sort_rev = 1; return(0); } --- 538,557 ---- * argument also means arrival/reverse. */ int ! decode_sort(char *sort_spec, SortOrder *def_sort, int *def_sort_rev, int thread) { char *sep; char *fix_this = NULL; ! int x = 0, reverse; if(!sort_spec || !*sort_spec){ ! *def_sort = thread ? SortThread : SortArrival; *def_sort_rev = 0; return(0); } if(struncmp(sort_spec, "reverse", strlen(sort_spec)) == 0){ ! *def_sort = thread ? SortThread : SortArrival; *def_sort_rev = 1; return(0); } *************** *** 571,577 **** if(ps_global->sort_types[x] == EndofList) return(-1); ! *def_sort = ps_global->sort_types[x]; *def_sort_rev = reverse; return(0); } --- 580,586 ---- if(ps_global->sort_types[x] == EndofList) return(-1); ! *def_sort = ps_global->sort_types[x]; *def_sort_rev = reverse; return(0); } *************** *** 685,694 **** PAT_S *pat; SortOrder the_sort_order; int sort_is_rev; ! /* set default order */ the_sort_order = ps_global->def_sort; ! sort_is_rev = ps_global->def_sort_rev; if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){ for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){ --- 694,719 ---- PAT_S *pat; SortOrder the_sort_order; int sort_is_rev; ! char *rule_result; ! SortOrder new_sort = EndofList; ! int is_rev; ! ! rule_result = get_rule_result(FOR_SORT, ps_global->cur_folder, V_SORT_RULES); ! if (rule_result && *rule_result){ ! new_sort = (SortOrder) translate(rule_result, 1); ! is_rev = (SortOrder) translate(rule_result, 0) == EndofList ? 0 : 1; ! fs_give((void **)&rule_result); ! } ! if (new_sort != EndofList){ ! the_sort_order = new_sort; ! sort_is_rev = is_rev; ! } ! else{ /* set default order */ the_sort_order = ps_global->def_sort; ! sort_is_rev = the_sort_order == SortThread ! ? (ps_global->thread_def_sort_rev + ps_global->def_sort_rev) % 2 ! : ps_global->def_sort_rev; if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){ for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){ *************** *** 701,709 **** && pat->action->sort_is_set){ the_sort_order = pat->action->sortorder; sort_is_rev = pat->action->revsort; } } sort_folder(ps_global->mail_stream, ps_global->msgmap, ! the_sort_order, sort_is_rev, flags); } --- 726,776 ---- && pat->action->sort_is_set){ the_sort_order = pat->action->sortorder; sort_is_rev = pat->action->revsort; + sort_is_rev = the_sort_order == SortThread + ? (ps_global->thread_def_sort_rev + pat->action->revsort) % 2 + : pat->action->revsort; } } + } + if(the_sort_order == SortThread && !(flags & SRT_MAN)) + ps_global->thread_cur_sort = ps_global->thread_def_sort; sort_folder(ps_global->mail_stream, ps_global->msgmap, ! the_sort_order, sort_is_rev, flags, 1); ! } ! ! SortOrder translate(char *order, int is_rev) ! { ! int rev = 0; ! if (!strncmp(order,"tHread", 6) ! || (rev = !strncmp(order,"Reverse tHread", 14))) ! return is_rev || rev ? SortThread : EndofList; ! if (!strncmp(order,"OrderedSubj", 11) ! || (rev = !strncmp(order,"Reverse OrderedSubj", 19))) ! return is_rev || rev ? SortSubject2 : EndofList; ! if (!strncmp(order,"Subject", 7) ! || (rev = !strncmp(order,"Reverse SortSubject", 15))) ! return is_rev || rev ? SortSubject : EndofList; ! if (!strncmp(order,"Arrival", 7) ! || (rev = !strncmp(order,"Reverse Arrival", 15))) ! return is_rev || rev ? SortArrival : EndofList; ! if (!strncmp(order,"From", 4) ! || (rev = !strncmp(order,"Reverse From", 12))) ! return is_rev || rev ? SortFrom : EndofList; ! if (!strncmp(order,"To", 2) ! || (rev = !strncmp(order,"Reverse To", 10))) ! return is_rev || rev ? SortTo : EndofList; ! if (!strncmp(order,"Cc", 2) ! || (rev = !strncmp(order,"Reverse Cc", 10))) ! return is_rev || rev ? SortCc : EndofList; ! if (!strncmp(order,"Date", 4) ! || (rev = !strncmp(order,"Reverse Date", 12))) ! return is_rev || rev ? SortDate : EndofList; ! if (!strncmp(order,"siZe", 4) ! || (rev = !strncmp(order,"Reverse siZe", 12))) ! return is_rev || rev ? SortSize : EndofList; ! if (!strncmp(order,"scorE", 5) ! || (rev = !strncmp(order,"Reverse scorE", 13))) ! return is_rev || rev ? SortScore : EndofList; ! return EndofList; } diff -rc alpine-2.00/pith/sort.h alpine-2.00.I.USE/pith/sort.h *** alpine-2.00/pith/sort.h 2006-09-22 13:06:05.000000000 -0700 --- alpine-2.00.I.USE/pith/sort.h 2011-02-07 20:33:42.000000000 -0800 *************** *** 22,28 **** #define refresh_sort(S,M,F) sort_folder((S), (M), mn_get_sort(M), \ ! mn_get_revsort(M), (F)) struct global_sort_data { MSGNO_S *msgmap; --- 22,28 ---- #define refresh_sort(S,M,F) sort_folder((S), (M), mn_get_sort(M), \ ! mn_get_revsort(M), (F), 1) struct global_sort_data { MSGNO_S *msgmap; *************** *** 41,49 **** /* exported protoypes */ char *sort_name(SortOrder); ! void sort_folder(MAILSTREAM *, MSGNO_S *, SortOrder, int, unsigned); ! int decode_sort(char *, SortOrder *, int *); void reset_sort_order(unsigned); ! #endif /* PITH_SORT_INCLUDED */ --- 41,49 ---- /* exported protoypes */ char *sort_name(SortOrder); ! void sort_folder(MAILSTREAM *, MSGNO_S *, SortOrder, int, unsigned, int); ! int decode_sort(char *, SortOrder *, int *, int); void reset_sort_order(unsigned); ! SortOrder translate(char *, int); #endif /* PITH_SORT_INCLUDED */ diff -rc alpine-2.00/pith/state.c alpine-2.00.I.USE/pith/state.c *** alpine-2.00/pith/state.c 2008-06-03 15:31:05.000000000 -0700 --- alpine-2.00.I.USE/pith/state.c 2011-02-07 20:33:45.000000000 -0800 *************** *** 32,38 **** #include "../pith/remote.h" #include "../pith/list.h" #include "../pith/smime.h" ! /* * Globals referenced throughout pine... --- 32,38 ---- #include "../pith/remote.h" #include "../pith/list.h" #include "../pith/smime.h" ! #include "../pith/rules.h" /* * Globals referenced throughout pine... *************** *** 73,78 **** --- 73,79 ---- p = (struct pine *)fs_get(sizeof (struct pine)); memset((void *) p, 0, sizeof(struct pine)); + p->thread_def_sort = SortDate; p->def_sort = SortArrival; p->sort_types[0] = SortSubject; p->sort_types[1] = SortArrival; *************** *** 115,120 **** --- 116,124 ---- if(!(pps && (*pps))) return; + if((*pps)->subject != NULL) + fs_give((void **)&(*pps)->subject); + if((*pps)->hostname != NULL) fs_give((void **)&(*pps)->hostname); *************** *** 130,135 **** --- 134,142 ---- if((*pps)->folders_dir != NULL) fs_give((void **)&(*pps)->folders_dir); + if((*pps)->paterror == 0) + regfree(&(*pps)->colorpat); + if((*pps)->ui.homedir) fs_give((void **)&(*pps)->ui.homedir); *************** *** 182,187 **** --- 189,197 ---- if((*pps)->hdr_colors) free_spec_colors(&(*pps)->hdr_colors); + if((*pps)->index_token_colors) + free_spec_colors(&(*pps)->index_token_colors); + if((*pps)->keywords) free_keyword_list(&(*pps)->keywords); *************** *** 202,207 **** --- 212,220 ---- if((*pps)->msgmap) msgno_give(&(*pps)->msgmap); + if((*pps)->rule_list) + free_parsed_rule_list(&(*pps)->rule_list); + free_vars(*pps); fs_give((void **) pps); diff -rc alpine-2.00/pith/state.h alpine-2.00.I.USE/pith/state.h *** alpine-2.00/pith/state.h 2008-06-03 08:54:15.000000000 -0700 --- alpine-2.00.I.USE/pith/state.h 2011-02-07 20:33:47.000000000 -0800 *************** *** 32,38 **** #include "../pith/stream.h" #include "../pith/color.h" #include "../pith/user.h" ! /* * Printing control structure --- 32,38 ---- #include "../pith/stream.h" #include "../pith/color.h" #include "../pith/user.h" ! #include "../pith/rulestype.h" /* * Printing control structure *************** *** 104,112 **** --- 104,118 ---- MAILSTREAM *mail_stream; /* ptr to current folder stream */ MSGNO_S *msgmap; /* ptr to current message map */ + char screen_name[10]; /* name of current screen */ + char *role; /* role used when composing */ + char *procid; /* procedure id when needed */ + int exiting; + unsigned read_predicted:1; char cur_folder[MAXPATH+1]; + QUOTALIST *quota; char last_unambig_folder[MAXPATH+1]; char last_save_folder[MAXPATH+1]; CONTEXT_S *last_save_context; *************** *** 122,127 **** --- 128,134 ---- unsigned mangled_header:1; /* header needs repainting */ unsigned mangled_body:1; /* body of screen needs repainting */ unsigned mangled_screen:1; /* whole screen needs repainting */ + unsigned resize_for_pico:1; /* screen needs repainting due to resizing */ unsigned in_init_seq:1; /* executing initial cmd list */ unsigned save_in_init_seq:1; *************** *** 135,140 **** --- 142,149 ---- unsigned unseen_in_view:1; unsigned start_in_context:1; /* start fldr_scrn in current cntxt */ unsigned def_sort_rev:1; /* true if reverse sort is default */ + unsigned thread_def_sort_rev:1; /* true if reverse sort is default in thread screen */ + unsigned msgmap_thread_def_sort_rev:1; /* true if reverse sort is being used in thread screen */ unsigned restricted:1; unsigned save_msg_rule:5; *************** *** 168,173 **** --- 177,183 ---- unsigned intr_pending:1; /* received SIGINT and haven't acted */ unsigned expunge_in_progress:1; /* don't want to re-enter c-client */ unsigned never_allow_changing_from:1; /* not even for roles */ + unsigned newthread:1; /* start a new thread on composing */ unsigned readonly_pinerc:1; unsigned view_all_except:1; *************** *** 239,249 **** --- 249,272 ---- char **feat_list_back_compat; SPEC_COLOR_S *hdr_colors; /* list of configed colors for view */ + SPEC_COLOR_S *index_token_colors; /* list of configed colors for index */ + char *prefix; /* prefix for fillpara */ short init_context; + struct { + ACTION_S *role_chosen; + int attach; + int strip; + int no_send_flowed; + int inchdr; + } reply; + int *initial_cmds; /* cmds to execute on startup */ int *free_initial_cmds; /* used to free when done */ + int *initial_cmds_backup; /* keep a copy in case they are freed */ + int *free_initial_cmds_backup; /* free the copy */ + int initial_cmds_offset; /* how many commands we have executed */ char c_client_error[300]; /* when nowhow_error is set and PARSE */ *************** *** 281,288 **** --- 304,316 ---- EditWhich ew_for_srch_take; SortOrder def_sort, /* Default sort type */ + thread_def_sort, /* Default Sort Type in Thread Screen */ + thread_cur_sort, /* current sort style for threads */ + msgmap_thread_sort, sort_types[22]; + int preserve; + int last_expire_year, last_expire_month; int printer_category; *************** *** 295,300 **** --- 323,332 ---- int nmw_width; + char *subject; + int send_immediately; + int failed_read; + int hours_to_timeout; int tcp_query_timeout; *************** *** 317,322 **** --- 349,356 ---- char *display_charmap; /* needs to be freed */ char *keyboard_charmap; /* needs to be freed */ void *input_cs; + regex_t colorpat; + int paterror; char *posting_charmap; /* needs to be freed */ *************** *** 328,333 **** --- 362,368 ---- struct { char *(*display_filter)(char *, STORE_S *, gf_io_t, FILTLIST_S *); char *(*display_filter_trigger)(BODY *, char *, size_t); + char *(*exec_rule)(char *, gf_io_t, gf_io_t); } tools; KEYWORD_S *keywords; *************** *** 338,343 **** --- 373,381 ---- char last_error[500]; INIT_ERR_S *init_errs; + PRULELIST_S *rule_list; + char *pressed_key; + PRINT_S *print; #ifdef SMIME diff -rc alpine-2.00/pith/store.c alpine-2.00.I.USE/pith/store.c *** alpine-2.00/pith/store.c 2008-06-03 12:27:23.000000000 -0700 --- alpine-2.00.I.USE/pith/store.c 2011-02-07 20:33:42.000000000 -0800 *************** *** 170,175 **** --- 170,183 ---- if(source == TmpFileStar) our_unlink(so->name); + if (ps_global->send_immediately){ + ps_global->failed_read++; + if(ps_global->failed_read == 5){ + printf("No configurationf file found. Where is your .pinerc file?\n"); + exit(1); + } + } + fs_give((void **)&so->name); fs_give((void **)&so); /* so freed & set to NULL */ } diff -rc alpine-2.00/pith/stream.c alpine-2.00.I.USE/pith/stream.c *** alpine-2.00/pith/stream.c 2008-03-25 11:57:53.000000000 -0700 --- alpine-2.00.I.USE/pith/stream.c 2011-02-07 20:33:46.000000000 -0800 *************** *** 3318,3323 **** --- 3318,3342 ---- } + /* Some stream is locked checks to see if there is any stream for which we + * are in a callback from c-client + */ + + int + some_stream_is_locked(void) + { + int rv = 0, i; + MAILSTREAM *m; + + for(i = 0; rv == 0 && i < ps_global->s_pool.nstream; i++){ + m = ps_global->s_pool.streams[i]; + if(m && m->lock) + rv++; + } + + return(rv); + } + /* * Very simple version of appenduid_cb until we need something * more complex. diff -rc alpine-2.00/pith/stream.h alpine-2.00.I.USE/pith/stream.h *** alpine-2.00/pith/stream.h 2007-06-15 16:23:02.000000000 -0700 --- alpine-2.00.I.USE/pith/stream.h 2011-02-07 20:33:46.000000000 -0800 *************** *** 460,465 **** --- 460,466 ---- int is_imap_stream(MAILSTREAM *); int modern_imap_stream(MAILSTREAM *); int streams_died(void); + int some_stream_is_locked(void); void appenduid_cb(char *mailbox,unsigned long uidvalidity, SEARCHSET *set); imapuid_t get_last_append_uid(void); MAILSTREAM *mail_cmd_stream(CONTEXT_S *, int *); diff -rc alpine-2.00/pith/string.c alpine-2.00.I.USE/pith/string.c *** alpine-2.00/pith/string.c 2008-01-09 12:45:31.000000000 -0800 --- alpine-2.00.I.USE/pith/string.c 2011-02-07 20:33:42.000000000 -0800 *************** *** 19,24 **** --- 19,25 ---- string.c Misc extra and useful string functions - rplstr replace a substring with another string + - collspaces consecutive spaces are reduced to one space. - sqzspaces Squeeze out the extra blanks in a string - sqznewlines Squeeze out \n and \r. - removing_trailing_white_space *************** *** 131,136 **** --- 132,162 ---- return(x3); } + /*---------------------------------------------------------------------- + collapse blank space + ----------------------------------------------------------------------*/ + void + collspaces(char *string) + { + char *p = string; + int only_one_space = 0; + + if(!string) + return; + + for(;isspace(*p); p++); + + while(*string = *p++) + if(!isspace((unsigned char)*string)){ + only_one_space = 0; + string++; + } + else if(!only_one_space){ + string++; + only_one_space++; + } + *string = '\0'; + } /*---------------------------------------------------------------------- *************** *** 2859,2861 **** --- 2885,2919 ---- fs_give((void **) strp); } } + + + void + removing_extra_stuff(string) + char *string; + { + char *p = NULL; + int change = 0, length = 0; + + + if(!string) + return; + + for(; *string; string++, length++) + p = ((unsigned char)*string != ',') ? NULL : (!p) ? string : p; + + if(p) + *p = '\0'; + + string -= length; + for (; *string; string++){ + if (change){ + *string = ' '; + change = 0; + } + if ((((unsigned char)*string == ' ') || + ((unsigned char)*string == ',')) && + ((unsigned char)*(string + 1) == ',')) + change++; + } + } + diff -rc alpine-2.00/pith/string.h alpine-2.00.I.USE/pith/string.h *** alpine-2.00/pith/string.h 2007-08-15 13:28:09.000000000 -0700 --- alpine-2.00.I.USE/pith/string.h 2011-02-07 20:33:42.000000000 -0800 *************** *** 85,96 **** --- 85,98 ---- /* exported protoypes */ char *rplstr(char *, size_t, int, char *); + void collspaces(char *); void sqzspaces(char *); void sqznewlines(char *); void removing_leading_white_space(char *); void removing_trailing_white_space(char *); void removing_leading_and_trailing_white_space(char *); int removing_double_quotes(char *); + void removing_extra_stuff (char *); char *skip_white_space(char *); char *skip_to_white_space(char *); char *removing_quotes(char *); diff -rc alpine-2.00/pith/text.c alpine-2.00.I.USE/pith/text.c *** alpine-2.00/pith/text.c 2008-03-18 10:24:31.000000000 -0700 --- alpine-2.00.I.USE/pith/text.c 2011-02-07 20:33:43.000000000 -0800 *************** *** 91,97 **** char *err, *charset; int filtcnt = 0, error_found = 0, column, wrapit; int is_in_sig = OUT_SIG_BLOCK; ! int is_flowed_msg = 0; int is_delsp_yes = 0; int filt_only_c0 = 0; char *parmval; --- 91,97 ---- char *err, *charset; int filtcnt = 0, error_found = 0, column, wrapit; int is_in_sig = OUT_SIG_BLOCK; ! int is_flowed_msg = 0, add_me = 1, doraw = RAWSTRING; int is_delsp_yes = 0; int filt_only_c0 = 0; char *parmval; *************** *** 170,175 **** --- 170,184 ---- gf_url_hilite_opt(&uh,handlesp,0)); } + if((flags & FM_DISPLAY) + && !(flags & FM_NOCOLOR) + && pico_usingcolor() + && VAR_SPECIAL_TEXT_FORE_COLOR + && VAR_SPECIAL_TEXT_BACK_COLOR){ + filters[filtcnt].filter = gf_line_test; + filters[filtcnt++].data = gf_line_test_opt(color_this_text, NULL); + } + /* * First, paint the signature. * Disclaimers noted below for coloring quotes apply here as well. *************** *** 179,185 **** && pico_usingcolor() && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR){ ! filters[filtcnt].filter = gf_line_test; filters[filtcnt++].data = gf_line_test_opt(color_signature, &is_in_sig); } --- 188,194 ---- && pico_usingcolor() && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR){ ! filters[filtcnt].filter = gf_quote_test; filters[filtcnt++].data = gf_line_test_opt(color_signature, &is_in_sig); } *************** *** 197,205 **** && pico_usingcolor() && VAR_QUOTE1_FORE_COLOR && VAR_QUOTE1_BACK_COLOR){ ! filters[filtcnt].filter = gf_line_test; ! filters[filtcnt++].data = gf_line_test_opt(color_a_quote, ! &is_flowed_msg); } } else if(!strucmp(att->body->subtype, "richtext")){ --- 206,214 ---- && pico_usingcolor() && VAR_QUOTE1_FORE_COLOR && VAR_QUOTE1_BACK_COLOR){ ! add_me = 0; ! filters[filtcnt].filter = gf_quote_test; ! filters[filtcnt++].data = gf_line_test_opt(color_a_quote, &is_flowed_msg); } } else if(!strucmp(att->body->subtype, "richtext")){ *************** *** 280,285 **** --- 289,299 ---- } } + if (add_me){ + filters[filtcnt].filter = gf_quote_test; + filters[filtcnt++].data = gf_line_test_opt(select_quote, &doraw); + } + /* * If the message is not flowed, we do the quote suppression before * the wrapping, because the wrapping does not preserve the quote *************** *** 304,310 **** dq.handlesp = handlesp; dq.do_color = (!(flags & FM_NOCOLOR) && pico_usingcolor()); ! filters[filtcnt].filter = gf_line_test; filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); } if(ps_global->VAR_QUOTE_REPLACE_STRING --- 318,324 ---- dq.handlesp = handlesp; dq.do_color = (!(flags & FM_NOCOLOR) && pico_usingcolor()); ! filters[filtcnt].filter = gf_quote_test; filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); } if(ps_global->VAR_QUOTE_REPLACE_STRING *************** *** 363,369 **** dq.handlesp = handlesp; dq.do_color = (!(flags & FM_NOCOLOR) && pico_usingcolor()); ! filters[filtcnt].filter = gf_line_test; filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); } --- 377,383 ---- dq.handlesp = handlesp; dq.do_color = (!(flags & FM_NOCOLOR) && pico_usingcolor()); ! filters[filtcnt].filter = gf_quote_test; filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); } *************** *** 568,574 **** { DELQ_S *dq; char *lp; ! int i, lines, not_a_quote = 0; size_t len; dq = (DELQ_S *) local; --- 582,588 ---- { DELQ_S *dq; char *lp; ! int i, lines, not_a_quote = 0, code; size_t len; dq = (DELQ_S *) local; *************** *** 588,593 **** --- 602,609 ---- for(i = dq->indent_length; i > 0 && !not_a_quote && *lp; i--) if(*lp++ != SPACE) not_a_quote++; + while(isspace((unsigned char) *lp)) + lp++; /* skip over leading tags */ while(!not_a_quote *************** *** 627,639 **** } } ! /* skip over whitespace */ ! if(!dq->is_flowed) ! while(isspace((unsigned char) *lp)) ! lp++; ! ! /* check first character to see if it is a quote */ ! if(!not_a_quote && *lp != '>') not_a_quote++; if(not_a_quote){ --- 643,654 ---- } } ! len = lp - line; ! if(strlen(tmp_20k_buf) > len) ! strcpy(tmp_20k_buf, tmp_20k_buf+len); ! code = (dq->is_flowed ? IS_FLOWED : NO_FLOWED) | DELETEQUO; ! select_quote(linenum, lp, ins, &code); ! if (!not_a_quote && !tmp_20k_buf[0]) not_a_quote++; if(not_a_quote){ diff -rc alpine-2.00/pith/thread.c alpine-2.00.I.USE/pith/thread.c *** alpine-2.00/pith/thread.c 2008-03-03 09:52:11.000000000 -0800 --- alpine-2.00.I.USE/pith/thread.c 2011-02-07 20:33:41.000000000 -0800 *************** *** 29,40 **** #include "../pith/mailcmd.h" #include "../pith/ablookup.h" /* * Internal prototypes */ long *sort_thread_flatten(THREADNODE *, MAILSTREAM *, long *, ! char *, long, PINETHRD_S *, unsigned); void make_thrdflags_consistent(MAILSTREAM *, MSGNO_S *, PINETHRD_S *, int); THREADNODE *collapse_threadnode_tree(THREADNODE *); THREADNODE *collapse_threadnode_tree_sorted(THREADNODE *); --- 29,41 ---- #include "../pith/mailcmd.h" #include "../pith/ablookup.h" + static int erase_thread_info = 1; /* * Internal prototypes */ long *sort_thread_flatten(THREADNODE *, MAILSTREAM *, long *, ! char *, long, PINETHRD_S *, unsigned, int, long, long); void make_thrdflags_consistent(MAILSTREAM *, MSGNO_S *, PINETHRD_S *, int); THREADNODE *collapse_threadnode_tree(THREADNODE *); THREADNODE *collapse_threadnode_tree_sorted(THREADNODE *); *************** *** 94,113 **** set_flags_for_thread(MAILSTREAM *stream, MSGNO_S *msgmap, int f, PINETHRD_S *thrd, int v) { PINETHRD_S *nthrd, *bthrd; if(!(stream && thrd && msgmap)) return; set_lflag(stream, msgmap, mn_raw2m(msgmap, thrd->rawno), f, v); ! if(thrd->next){ ! nthrd = fetch_thread(stream, thrd->next); if(nthrd) set_flags_for_thread(stream, msgmap, f, nthrd, v); } ! if(thrd->branch){ ! bthrd = fetch_thread(stream, thrd->branch); if(bthrd) set_flags_for_thread(stream, msgmap, f, bthrd, v); } --- 95,116 ---- set_flags_for_thread(MAILSTREAM *stream, MSGNO_S *msgmap, int f, PINETHRD_S *thrd, int v) { PINETHRD_S *nthrd, *bthrd; + unsigned long next = 0L, branch = 0L; if(!(stream && thrd && msgmap)) return; set_lflag(stream, msgmap, mn_raw2m(msgmap, thrd->rawno), f, v); ! if(next = get_next(stream,thrd)){ ! nthrd = fetch_thread(stream, next); if(nthrd) set_flags_for_thread(stream, msgmap, f, nthrd, v); } ! ! if(branch = get_branch(stream, thrd)){ ! bthrd = fetch_thread(stream, branch); if(bthrd) set_flags_for_thread(stream, msgmap, f, bthrd, v); } *************** *** 121,127 **** MESSAGECACHE *mc; PINELT_S *peltp; ! if(!(stream && stream->spare)) return; ps_global->view_skipped_index = 0; --- 124,130 ---- MESSAGECACHE *mc; PINELT_S *peltp; ! if(!(stream && stream->spare) || !erase_thread_info) return; ps_global->view_skipped_index = 0; *************** *** 154,160 **** PINETHRD_S *thrd = NULL; unsigned long msgno, rawno; int un_view_thread = 0; ! long raw_current; char *dup_chk = NULL; --- 157,163 ---- PINETHRD_S *thrd = NULL; unsigned long msgno, rawno; int un_view_thread = 0; ! long raw_current, branch; char *dup_chk = NULL; *************** *** 167,176 **** * way. If the dummy node is at the top-level, then its children are * promoted to the top-level as separate threads. */ ! if(F_ON(F_THREAD_SORTS_BY_ARRIVAL, ps_global)) ! collapsed_tree = collapse_threadnode_tree_sorted(tree); ! else ! collapsed_tree = collapse_threadnode_tree(tree); /* dup_chk is like sort with an origin of 1 */ dup_chk = (char *) fs_get((mn_get_nmsgs(g_sort.msgmap)+1) * sizeof(char)); --- 170,180 ---- * way. If the dummy node is at the top-level, then its children are * promoted to the top-level as separate threads. */ ! collapsed_tree = F_ON(F_ENHANCED_THREAD, ps_global) ! ? copy_tree(tree) ! : (F_ON(F_THREAD_SORTS_BY_ARRIVAL, ps_global) ! ? collapse_threadnode_tree_sorted(tree) ! : collapse_threadnode_tree(tree)); /* dup_chk is like sort with an origin of 1 */ dup_chk = (char *) fs_get((mn_get_nmsgs(g_sort.msgmap)+1) * sizeof(char)); *************** *** 181,187 **** (void) sort_thread_flatten(collapsed_tree, stream, &g_sort.msgmap->sort[1], dup_chk, mn_get_nmsgs(g_sort.msgmap), ! NULL, THD_TOP); /* reset the inverse array */ msgno_reset_isort(g_sort.msgmap); --- 185,191 ---- (void) sort_thread_flatten(collapsed_tree, stream, &g_sort.msgmap->sort[1], dup_chk, mn_get_nmsgs(g_sort.msgmap), ! NULL, THD_TOP, 0, 1L, 0L); /* reset the inverse array */ msgno_reset_isort(g_sort.msgmap); *************** *** 339,350 **** else{ thrd = fetch_head_thread(stream); while(thrd){ /* * The top-level threads aren't hidden by collapse. */ msgno = mn_raw2m(g_sort.msgmap, thrd->rawno); ! if(msgno) ! set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0); if(thrd->next){ PINETHRD_S *nthrd; --- 343,356 ---- else{ thrd = fetch_head_thread(stream); while(thrd){ + unsigned long raw = thrd->rawno; + unsigned long top = top_thread(stream, raw); /* * The top-level threads aren't hidden by collapse. */ msgno = mn_raw2m(g_sort.msgmap, thrd->rawno); ! if(msgno && !get_lflag(stream, NULL,thrd->rawno, MN_COLL)) ! set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0); if(thrd->next){ PINETHRD_S *nthrd; *************** *** 358,366 **** MN_COLL)); } ! if(thrd->nextthd) ! thrd = fetch_thread(stream, thrd->nextthd); ! else thrd = NULL; } } --- 364,373 ---- MN_COLL)); } ! while (thrd && top_thread(stream, thrd->rawno) == top ! && thrd->nextthd) ! thrd = fetch_thread(stream, thrd->nextthd); ! if (!(thrd && thrd->nextthd)) thrd = NULL; } } *************** *** 411,417 **** int a_parent_is_collapsed) { PINETHRD_S *nthrd, *bthrd; ! unsigned long msgno; if(!thrd) return; --- 418,424 ---- int a_parent_is_collapsed) { PINETHRD_S *nthrd, *bthrd; ! unsigned long msgno, next, branch; if(!thrd) return; *************** *** 429,436 **** set_lflag(stream, msgmap, msgno, MN_CHID, 0); } ! if(thrd->next){ ! nthrd = fetch_thread(stream, thrd->next); if(nthrd) make_thrdflags_consistent(stream, msgmap, nthrd, a_parent_is_collapsed --- 436,443 ---- set_lflag(stream, msgmap, msgno, MN_CHID, 0); } ! if(next = get_next(stream, thrd)){ ! nthrd = fetch_thread(stream, next); if(nthrd) make_thrdflags_consistent(stream, msgmap, nthrd, a_parent_is_collapsed *************** *** 439,446 **** MN_COLL)); } ! if(thrd->branch){ ! bthrd = fetch_thread(stream, thrd->branch); if(bthrd) make_thrdflags_consistent(stream, msgmap, bthrd, a_parent_is_collapsed); --- 446,453 ---- MN_COLL)); } ! if(branch = get_branch(stream, thrd)){ ! bthrd = fetch_thread(stream, branch); if(bthrd) make_thrdflags_consistent(stream, msgmap, bthrd, a_parent_is_collapsed); *************** *** 487,495 **** long * sort_thread_flatten(THREADNODE *node, MAILSTREAM *stream, long *entry, char *dup_chk, long maxno, ! PINETHRD_S *thrd, unsigned int flags) { ! PINETHRD_S *newthrd = NULL; if(node){ if(node->num > 0L && node->num <= maxno){ /* holes happen */ --- 494,503 ---- long * sort_thread_flatten(THREADNODE *node, MAILSTREAM *stream, long *entry, char *dup_chk, long maxno, ! PINETHRD_S *thrd, unsigned int flags, ! int adopted, long top, long threadno) { ! PINETHRD_S *newthrd = NULL, *save_thread = NULL; if(node){ if(node->num > 0L && node->num <= maxno){ /* holes happen */ *************** *** 497,502 **** --- 505,513 ---- *entry = node->num; dup_chk[node->num] = 1; + if(adopted == 2) + top = node->num; + /* * Build a richer threading structure that will help us paint * and operate on threads and subthreads. *************** *** 505,524 **** if(newthrd){ entry++; if(node->next) entry = sort_thread_flatten(node->next, stream, entry, dup_chk, maxno, ! newthrd, THD_NEXT); if(node->branch) entry = sort_thread_flatten(node->branch, stream, entry, dup_chk, maxno, newthrd, ! (flags == THD_TOP) ? THD_TOP ! : THD_BRANCH); } } } } return(entry); --- 516,566 ---- if(newthrd){ entry++; + if(adopted == 2) + threadno = newthrd->thrdno; + if(adopted){ + newthrd->toploose = top; + newthrd->thrdno = threadno; + } + adopted = adopted ? 1 : 0; if(node->next) entry = sort_thread_flatten(node->next, stream, entry, dup_chk, maxno, ! newthrd, THD_NEXT, adopted, top, threadno); if(node->branch) entry = sort_thread_flatten(node->branch, stream, entry, dup_chk, maxno, newthrd, ! ((flags == THD_TOP) ? THD_TOP ! : THD_BRANCH), ! adopted, top, threadno); } } } + else{ + adopted = 2; + if(node->next) + entry = sort_thread_flatten(node->next, stream, entry, dup_chk, + maxno, thrd, THD_TOP, adopted, top, threadno); + adopted = 0; + if(node->branch){ + if(entry){ + long *last_entry = entry; + + do{ + last_entry--; + save_thread = ((PINELT_S *)mail_elt(stream, *last_entry)->sparep)->pthrd; + } while (save_thread->parent != 0L); + entry = sort_thread_flatten(node->branch, stream, entry, dup_chk, + maxno, save_thread, (flags == THD_TOP ? THD_TOP : THD_BRANCH), + adopted, top, threadno); + } + else + entry = sort_thread_flatten(node->branch, stream, entry, dup_chk, + maxno, NULL, THD_TOP, adopted, top, threadno); + } + } } return(entry); *************** *** 787,793 **** */ void collapse_or_expand(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, ! long unsigned int msgno) { int collapsed, adjust_current = 0; PINETHRD_S *thrd = NULL, *nthrd; --- 829,835 ---- */ void collapse_or_expand(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, ! long unsigned int msgno, int display) { int collapsed, adjust_current = 0; PINETHRD_S *thrd = NULL, *nthrd; *************** *** 840,846 **** if(!thrd) return; ! collapsed = get_lflag(stream, NULL, thrd->rawno, MN_COLL) && thrd->next; if(collapsed){ msgno = mn_raw2m(msgmap, thrd->rawno); --- 882,888 ---- if(!thrd) return; ! collapsed = this_thread_is_kolapsed(ps_global, stream, msgmap, thrd->rawno); if(collapsed){ msgno = mn_raw2m(msgmap, thrd->rawno); *************** *** 858,870 **** msgno = mn_raw2m(msgmap, thrd->rawno); if(msgno > 0L && msgno <= mn_get_total(msgmap)){ set_lflag(stream, msgmap, msgno, MN_COLL, 1); ! if((nthrd = fetch_thread(stream, thrd->next)) != NULL) set_thread_subtree(stream, nthrd, msgmap, 1, MN_CHID); clear_index_cache_ent(stream, msgno, 0); } } ! else q_status_message(SM_ORDER, 0, 1, _("No thread to collapse or expand on this line")); --- 900,912 ---- msgno = mn_raw2m(msgmap, thrd->rawno); if(msgno > 0L && msgno <= mn_get_total(msgmap)){ set_lflag(stream, msgmap, msgno, MN_COLL, 1); ! if((thrd->next) && ((nthrd = fetch_thread(stream, thrd->next)) != NULL)) set_thread_subtree(stream, nthrd, msgmap, 1, MN_CHID); clear_index_cache_ent(stream, msgno, 0); } } ! else if(display) q_status_message(SM_ORDER, 0, 1, _("No thread to collapse or expand on this line")); *************** *** 951,968 **** unsigned long count = 0; PINETHRD_S *nthrd, *bthrd; MESSAGECACHE *mc; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) return count; ! if(thrd->next){ ! nthrd = fetch_thread(stream, thrd->next); if(nthrd) count += count_flags_in_thread(stream, nthrd, flags); } ! if(thrd->branch){ ! bthrd = fetch_thread(stream, thrd->branch); if(bthrd) count += count_flags_in_thread(stream, bthrd, flags); } --- 993,1011 ---- unsigned long count = 0; PINETHRD_S *nthrd, *bthrd; MESSAGECACHE *mc; + unsigned long next = 0L, branch = 0L; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) return count; ! if(next = get_next(stream, thrd)){ ! nthrd = fetch_thread(stream, next); if(nthrd) count += count_flags_in_thread(stream, nthrd, flags); } ! if(branch = get_branch(stream, thrd)){ ! bthrd = fetch_thread(stream, branch); if(bthrd) count += count_flags_in_thread(stream, bthrd, flags); } *************** *** 1050,1069 **** mark_msgs_in_thread(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap) { int count = 0; PINETHRD_S *nthrd, *bthrd; MESSAGECACHE *mc; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) return count; ! if(thrd->next){ ! nthrd = fetch_thread(stream, thrd->next); if(nthrd) count += mark_msgs_in_thread(stream, nthrd, msgmap); } ! if(thrd->branch){ ! bthrd = fetch_thread(stream, thrd->branch); if(bthrd) count += mark_msgs_in_thread(stream, bthrd, msgmap); } --- 1093,1113 ---- mark_msgs_in_thread(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap) { int count = 0; + long next, branch; PINETHRD_S *nthrd, *bthrd; MESSAGECACHE *mc; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) return count; ! if(next = get_next(stream, thrd)){ ! nthrd = fetch_thread(stream, next); if(nthrd) count += mark_msgs_in_thread(stream, nthrd, msgmap); } ! if(branch = get_branch(stream, thrd)){ ! bthrd = fetch_thread(stream, branch); if(bthrd) count += mark_msgs_in_thread(stream, bthrd, msgmap); } *************** *** 1097,1103 **** /* flags to set or clear */ /* set or clear? */ { ! unsigned long msgno; PINETHRD_S *nthrd, *bthrd; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) --- 1141,1147 ---- /* flags to set or clear */ /* set or clear? */ { ! unsigned long msgno, next, branch; PINETHRD_S *nthrd, *bthrd; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) *************** *** 1121,1134 **** if(msgno > 0L && flags == MN_CHID2 && v == 1) clear_index_cache_ent(stream, msgno, 0); ! if(thrd->next){ ! nthrd = fetch_thread(stream, thrd->next); if(nthrd) set_thread_lflags(stream, nthrd, msgmap, flags, v); } ! if(thrd->branch){ ! bthrd = fetch_thread(stream, thrd->branch); if(bthrd) set_thread_lflags(stream, bthrd, msgmap, flags, v); } --- 1165,1178 ---- if(msgno > 0L && flags == MN_CHID2 && v == 1) clear_index_cache_ent(stream, msgno, 0); ! if(next = get_next(stream, thrd)){ ! nthrd = fetch_thread(stream, next); if(nthrd) set_thread_lflags(stream, nthrd, msgmap, flags, v); } ! if(branch = get_branch(stream,thrd)){ ! bthrd = fetch_thread(stream, branch); if(bthrd) set_thread_lflags(stream, bthrd, msgmap, flags, v); } *************** *** 1217,1235 **** char to_us = ' '; char branch_to_us = ' '; PINETHRD_S *nthrd, *bthrd; MESSAGECACHE *mc; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) return to_us; ! if(thrd->next){ ! nthrd = fetch_thread(stream, thrd->next); if(nthrd) to_us = to_us_symbol_for_thread(stream, nthrd, consider_flagged); } if(((consider_flagged && to_us != '*') || (!consider_flagged && to_us != '+')) ! && thrd->branch){ bthrd = fetch_thread(stream, thrd->branch); if(bthrd) branch_to_us = to_us_symbol_for_thread(stream, bthrd, consider_flagged); --- 1261,1280 ---- char to_us = ' '; char branch_to_us = ' '; PINETHRD_S *nthrd, *bthrd; + unsigned long next = 0L, branch = 0L; MESSAGECACHE *mc; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) return to_us; ! if(next = get_next(stream,thrd)){ ! nthrd = fetch_thread(stream, next); if(nthrd) to_us = to_us_symbol_for_thread(stream, nthrd, consider_flagged); } if(((consider_flagged && to_us != '*') || (!consider_flagged && to_us != '+')) ! && (branch = get_branch(stream, thrd))){ bthrd = fetch_thread(stream, thrd->branch); if(bthrd) branch_to_us = to_us_symbol_for_thread(stream, bthrd, consider_flagged); *************** *** 1279,1285 **** break; } ! if(to_us != '+' && resent_to_us(&idata)) to_us = '+'; if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global)) --- 1324,1330 ---- break; } ! if(to_us != '+' && !idata.bogus && resent_to_us(&idata)) to_us = '+'; if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global)) *************** *** 1327,1333 **** set_lflag(stream, msgmap, msgno, flags, v); ! if(thrd->next && (hiding || !get_lflag(stream,NULL,thrd->rawno,MN_COLL))){ nthrd = fetch_thread(stream, thrd->next); if(nthrd) set_thread_subtree(stream, nthrd, msgmap, v, flags); --- 1372,1379 ---- set_lflag(stream, msgmap, msgno, flags, v); ! if(thrd->next ! && (hiding || !get_lflag(stream,NULL,thrd->rawno,MN_COLL))){ nthrd = fetch_thread(stream, thrd->next); if(nthrd) set_thread_subtree(stream, nthrd, msgmap, v, flags); *************** *** 1367,1374 **** if(rawno) thrd = fetch_thread(stream, rawno); ! if(thrd && thrd->top && thrd->top != thrd->rawno) ! thrd = fetch_thread(stream, thrd->top); if(!thrd) return 0; --- 1413,1420 ---- if(rawno) thrd = fetch_thread(stream, rawno); ! if(thrd && thrd->top && top_thread(stream,thrd->top) != thrd->rawno) ! thrd = fetch_thread(stream, top_thread(stream,thrd->top)); if(!thrd) return 0; *************** *** 1432,1438 **** thrd = fetch_thread(stream, rawno); if(thrd && thrd->top) ! topthrd = fetch_thread(stream, thrd->top); if(!topthrd) return 0; --- 1478,1484 ---- thrd = fetch_thread(stream, rawno); if(thrd && thrd->top) ! topthrd = fetch_thread(stream, top_thread(stream,thrd->top)); if(!topthrd) return 0; *************** *** 1538,1543 **** --- 1584,1590 ---- set_search_bit_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, SEARCHSET **msgset) { PINETHRD_S *nthrd, *bthrd; + unsigned long next, branch; if(!(stream && thrd)) return; *************** *** 1546,1560 **** && (!(msgset && *msgset) || in_searchset(*msgset, thrd->rawno))) mm_searched(stream, thrd->rawno); ! if(thrd->next){ ! nthrd = fetch_thread(stream, thrd->next); if(nthrd) set_search_bit_for_thread(stream, nthrd, msgset); } ! if(thrd->branch){ ! bthrd = fetch_thread(stream, thrd->branch); if(bthrd) set_search_bit_for_thread(stream, bthrd, msgset); } } --- 1593,2185 ---- && (!(msgset && *msgset) || in_searchset(*msgset, thrd->rawno))) mm_searched(stream, thrd->rawno); ! if(next= get_next(stream, thrd)){ ! nthrd = fetch_thread(stream, next); if(nthrd) set_search_bit_for_thread(stream, nthrd, msgset); } ! if(branch = get_branch(stream, thrd)){ ! bthrd = fetch_thread(stream, branch); if(bthrd) set_search_bit_for_thread(stream, bthrd, msgset); } } + + /* + * Make a copy of c-client's THREAD tree + */ + THREADNODE * + copy_tree(THREADNODE *tree) + { + THREADNODE *newtree = NULL; + + if(tree){ + newtree = mail_newthreadnode(NULL); + newtree->num = tree->num; + if(tree->next) + newtree->next = copy_tree(tree->next); + + if(tree->branch) + newtree->branch = copy_tree(tree->branch); + } + return(newtree); + } + + long + top_thread(MAILSTREAM *stream, long rawmsgno) + { + PINETHRD_S *thrd = NULL; + unsigned long rawno; + + if(!stream) + return -1L; + + if(rawmsgno) + thrd = fetch_thread(stream, rawmsgno); + + if(!thrd) + return -1L; + + return F_ON(F_ENHANCED_THREAD, ps_global) + ? (thrd->toploose ? thrd->toploose : thrd->top) + : thrd->top; + } + + void + move_top_thread(MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno) + { + mn_set_cur(msgmap,mn_raw2m(msgmap, top_thread(stream, rawmsgno))); + } + + long + top_this_thread(MAILSTREAM *stream, long rawmsgno) + { + PINETHRD_S *thrd = NULL; + unsigned long rawno; + + if(!stream) + return -1L; + + if(rawmsgno) + thrd = fetch_thread(stream, rawmsgno); + + if(!thrd) + return -1L; + + return thrd->top; + } + + void + move_top_this_thread(MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno) + { + mn_set_cur(msgmap,mn_raw2m(msgmap, top_this_thread(stream, rawmsgno))); + } + + int + thread_is_kolapsed(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno) + { + int collapsed; + PINETHRD_S *thrd = NULL; + unsigned long rawno, orig, orig_rawno; + + if(!stream) + return -1; + + orig = mn_get_cur(msgmap); + move_top_thread(stream, msgmap, rawmsgno); + rawno = orig_rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return -1; + + while(collapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno)) + if (F_OFF(F_ENHANCED_THREAD, state) + || (move_next_this_thread(state, stream, msgmap, 0) <= 0) + || !(rawno = mn_m2raw(msgmap, mn_get_cur(msgmap))) + || (orig_rawno != top_thread(stream, rawno))) + break; + + mn_set_cur(msgmap,orig); /* return home */ + + return collapsed; + } + + /* this function tells us if the thread (or branch in the case of loose threads) + * is collapsed + */ + + int + this_thread_is_kolapsed(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno) + { + int collapsed; + PINETHRD_S *thrd = NULL; + unsigned long rawno, orig; + + if(!stream) + return -1; + + rawno = rawmsgno; + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return -1; + + collapsed = get_lflag(stream, NULL, rawno, MN_COLL | MN_CHID); + + if (!thrd->next){ + if (thrd->rawno != top_thread(stream, thrd->rawno)) + collapsed = get_lflag(stream, NULL, rawno, MN_CHID); + else + collapsed = get_lflag(stream, NULL, rawno, MN_COLL); + } + + return collapsed; + } + + /* + * This function assumes that it is called at a top of a thread in its + * first call + */ + + int + count_this_thread(MAILSTREAM *stream, unsigned long rawno) + { + unsigned long top, orig_top, topnxt; + PINETHRD_S *thrd = NULL; + int count = 1; + + if(!stream) + return 0; + + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return 0; + + if (thrd->next) + count += count_this_thread(stream, thrd->next); + + if (thrd->branch) + count += count_this_thread(stream, thrd->branch); + + return count; + } + + int + count_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long rawno) + { + unsigned long top, orig, orig_top; + PINETHRD_S *thrd = NULL; + int done = 0, count = 0; + + if(!stream) + return 0; + + orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); + move_top_thread(stream, msgmap,rawno); + top = orig_top = top_thread(stream, rawno); + if(top) + thrd = fetch_thread(stream, top); + + if(!thrd) + return 0; + + while (!done){ + count += count_this_thread(stream, top); + if (F_OFF(F_ENHANCED_THREAD, state) + || (move_next_this_thread(state, stream, msgmap, 0) <= 0) + || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) + || (orig_top != top_thread(stream, top))) + done++; + } + mn_set_cur(msgmap,mn_raw2m(msgmap, orig)); + return count; + } + + unsigned long + get_branch(MAILSTREAM *stream, PINETHRD_S *thrd) + { + PINETHRD_S *nthrd = NULL; + unsigned long top; + + if (thrd->toploose && thrd->nextthd) + nthrd = fetch_thread(stream, thrd->nextthd); + if (!nthrd) + return thrd->branch; + top = top_thread(stream, thrd->rawno); + return thrd->branch + ? thrd->branch + : (F_ON(F_ENHANCED_THREAD, ps_global) + ? (top == top_thread(stream, nthrd->rawno) ? thrd->nextthd : 0L) + : 0L); + } + + unsigned long + get_next(MAILSTREAM *stream, PINETHRD_S *thrd) + { + return thrd->next; + } + + long + get_length_branch(MAILSTREAM *stream, long rawno) + { + int branchp = 0, done = 0; + long top, count = 1L, raw; + PINETHRD_S *thrd, *pthrd = NULL, *nthrd; + + thrd = fetch_thread(stream, rawno); + + if (!thrd) + return -1L; + + top = thrd->top; + + if (thrd->parent) + pthrd = fetch_thread(stream, thrd->parent); + + if (thrd->rawno == top) + branchp++; + + if (!branchp && !pthrd){ /* what!!?? */ + raw = top; + while (!done){ + pthrd = fetch_thread(stream, raw); + if ((pthrd->next == rawno) || (pthrd->branch == rawno)) + done++; + else{ + if (pthrd->next) + raw = pthrd->next; + else if (pthrd->branch) + raw = pthrd->branch; + } + } + } + + if (pthrd && pthrd->next == thrd->rawno && thrd->branch) + branchp++; + + if (pthrd && pthrd->next && pthrd->next != thrd->rawno){ + nthrd = fetch_thread(stream, pthrd->next); + while (nthrd && nthrd->branch && nthrd->branch != thrd->rawno) + nthrd = fetch_thread(stream, nthrd->branch); + if(nthrd && nthrd->branch && nthrd->branch == thrd->rawno) + branchp++; + } + + if(branchp){ + int entry = 0; + while(thrd && thrd->next){ + entry = 1; + count++; + thrd = fetch_thread(stream, thrd->next); + if (thrd->branch) + break; + } + if (entry && thrd->branch) + count--; + } + return branchp ? (count ? count : 1L) : 0L; + } + + void + find_msgmap(MAILSTREAM *stream, MSGNO_S *msgmap, int flags, SortOrder ordersort, unsigned is_rev) + { + int we_cancel; + long *old_arrival,*new_arrival; + long init_thread, end_thread, current; + long k = 1L, j, last_thread = 0L; + long i, tmsg, ntmsg, nthreads; + char sort_msg[MAX_SCREEN_COLS+1] = {'\0'}; + PINETHRD_S *thrd, *tthrd, *nthrd; + + erase_thread_info = 0; + current = mn_m2raw(msgmap, mn_get_cur(msgmap)); + + sort_folder(stream, msgmap, ordersort, 0, SRT_VRB, 0); + + tmsg = mn_get_total(msgmap) + 1; + + if (tmsg <= 1) + return; + + old_arrival = (long *) fs_get(tmsg * sizeof(long)); + memset(old_arrival, 0, tmsg*sizeof(long)); + for (i= 1L;(i <= mn_get_total(msgmap)) && (old_arrival[i] = msgmap->sort[i]); i++); + + /* now sort by thread */ + sort_folder(stream, msgmap, SortThread, 0, SRT_VRB, 0); + ntmsg = mn_get_total(msgmap) + 1; + + if (tmsg != ntmsg){ /* oh oh, something happened,we better try again */ + fs_give((void **)&old_arrival); + find_msgmap(stream, msgmap, flags, ordersort, is_rev); + return; + } + + /* reconstruct the msgmap */ + + new_arrival = (long *) fs_get(tmsg * sizeof(long)); + memset(new_arrival, 0, tmsg*sizeof(long)); + i = mn_get_total(msgmap); + while (new_arrival[1] == 0){ /* think of this as (tmsg > 0) */ + int done = 0; + long n = mn_get_total(msgmap); + + init_thread = top_thread(stream, old_arrival[i]); + thrd = fetch_thread(stream, init_thread); + while ((new_arrival[n] != 0) && !done){ /* compare raw numbers */ + done = (new_arrival[n] == init_thread); + n--; + } + if (!done){ + k = 1L; + mn_set_cur(msgmap, mn_raw2m(msgmap, init_thread)); + if (move_next_thread(ps_global, stream, msgmap, 0) <= 0) + j = mn_get_total(msgmap) - mn_raw2m(msgmap, init_thread) + 1; + else + j = mn_get_cur(msgmap) - mn_raw2m(msgmap, init_thread); + end_thread = mn_raw2m(msgmap, init_thread) + j; + while (k <= j){ + new_arrival[tmsg - k] = msgmap->sort[end_thread - k]; + k++; + } + tmsg -= j; + } + i--; + } + relink_threads(stream, msgmap, new_arrival); + for (i = 1; (i <= mn_get_total(msgmap)) + && (msgmap->sort[i] = new_arrival[i]); i++); + msgno_reset_isort(msgmap); + + fs_give((void **)&new_arrival); + fs_give((void **)&old_arrival); + + + if(is_rev && (mn_get_total(msgmap) > 1L)){ + long *rev_sort; + long i = 1L, l = mn_get_total(msgmap); + + rev_sort = (long *) fs_get((mn_get_total(msgmap)+1L) * sizeof(long)); + memset(rev_sort, 0, (mn_get_total(msgmap)+1L)*sizeof(long)); + while (l > 0L){ + if (top_thread(stream, msgmap->sort[l]) == msgmap->sort[l]){ + long init_thread = msgmap->sort[l]; + long j, k; + + mn_set_cur(msgmap, mn_raw2m(msgmap, init_thread)); + if (move_next_thread(ps_global, stream, msgmap, 0) <= 0) + j = mn_get_total(msgmap) - mn_raw2m(msgmap, init_thread) + 1; + else + j = mn_get_cur(msgmap) - mn_raw2m(msgmap, init_thread); + for (k = 0L; (k < j) && (rev_sort[i+k] = msgmap->sort[l+k]); k++); + i += j; + } + l--; + } + relink_threads(stream, msgmap, rev_sort); + for (i = 1L; i <= mn_get_total(msgmap); i++) + msgmap->sort[i] = rev_sort[i]; + msgno_reset_isort(msgmap); + fs_give((void **)&rev_sort); + } + mn_reset_cur(msgmap, first_sorted_flagged(is_rev ? F_NONE : F_SRCHBACK, + stream, mn_raw2m(msgmap, current), FSF_SKIP_CHID)); + msgmap->top = -1L; + + sp_set_unsorted_newmail(ps_global->mail_stream, 0); + + for(i = 1L; i <= ps_global->mail_stream->nmsgs; i++) + mail_elt(ps_global->mail_stream, i)->spare7 = 0; + + mn_set_sort(msgmap, SortThread); + mn_set_revsort(msgmap, is_rev); + erase_thread_info = 1; + clear_index_cache(stream, 0); + } + + void + move_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int direction) + { + long new_cursor, old_cursor = mn_get_cur(msgmap); + int rv; + PINETHRD_S *thrd; + + rv = direction > 0 ? move_next_thread(state, stream, msgmap, 1): + move_prev_thread(state, stream, msgmap, 1); + if (rv > 0 && THRD_INDX_ENABLED()){ + new_cursor = mn_get_cur(msgmap); + mn_set_cur(msgmap, old_cursor); + unview_thread(state, stream, msgmap); + thrd = fetch_thread(stream,mn_m2raw(msgmap, new_cursor)); + mn_set_cur(msgmap, new_cursor); + view_thread(state, stream, msgmap, 1); + state->next_screen = SCREEN_FUN_NULL; + } + } + + void + relink_threads(MAILSTREAM *stream, MSGNO_S *msgmap, long *new_arrival) + { + long last_thread = 0L; + long i = 0L, j = 1L, k; + PINETHRD_S *thrd, *nthrd; + + while (j <= mn_get_total(msgmap)){ + i++; + thrd = fetch_thread(stream, new_arrival[j]); + if (!thrd) /* sort failed!, better leave from here now!!! */ + break; + thrd->prevthd = last_thread; + thrd->thrdno = i; + thrd->head = new_arrival[1]; + last_thread = thrd->rawno; + mn_set_cur(msgmap, mn_raw2m(msgmap,thrd->top)); + k = mn_get_cur(msgmap); + if (move_next_thread(ps_global, stream, msgmap, 0) <= 0) + j += mn_get_total(msgmap) + 1 - k; + else + j += mn_get_cur(msgmap) - k; + if (!thrd->toploose) + thrd->nextthd = (j <= mn_get_total(msgmap)) ? new_arrival[j] : 0L; + else{ + int done = 0; + while(thrd->nextthd && !done){ + thrd->thrdno = i; + thrd->head = new_arrival[1]; + if (thrd->nextthd) + nthrd = fetch_thread(stream, thrd->nextthd); + else + done++; + if(top_thread(stream, thrd->rawno) == top_thread(stream, nthrd->rawno)) + thrd = nthrd; + else + done++; + } + thrd->nextthd = (j <= mn_get_total(msgmap)) ? new_arrival[j] : 0L; + last_thread = thrd->rawno; + } + } + } + + int + move_next_this_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int display) + { + PINETHRD_S *thrd = NULL, *thrdnxt; + unsigned long rawno, top; + int rv = 1; + + if(!stream) + return -1; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return -1; + + top = top_thread(stream, rawno); + + thrdnxt = (top == rawno) ? fetch_thread(stream, top) : thrd; + if (thrdnxt->nextthd) + mn_set_cur(msgmap,mn_raw2m(msgmap, thrdnxt->nextthd)); + else{ + rv = 0; + if (display) + q_status_message(SM_ORDER, 0, 1, "No more Threads to advance"); + } + return rv; + } + + int + move_next_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int display) + { + int collapsed, rv = 1, done = 0; + PINETHRD_S *thrd = NULL; + unsigned long orig, orig_top, top; + + if(!stream) + return 0; + + orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); + move_top_thread(stream, msgmap,orig); + top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); + + if(top) + thrd = fetch_thread(stream, top); + + if(!thrd) + return 0; + + while (rv > 0 && !done){ + rv = move_next_this_thread(state, stream, msgmap, display); + if (F_OFF(F_ENHANCED_THREAD, state) + || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) + || (orig_top != top_thread(stream, top))) + done++; + } + if (display){ + if (rv > 0 && SEP_THRDINDX()) + q_status_message(SM_ORDER, 0, 2, "Viewing next thread"); + if (!rv) + q_status_message(SM_ORDER, 0, 2, "No more threads to advance"); + } + if(rv <= 0){ + rv = 0; + mn_set_cur(msgmap, mn_raw2m(msgmap, orig)); + } + + return rv; + } + + int + move_prev_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int display) + { + PINETHRD_S *thrd = NULL; + unsigned long rawno, top; + int rv = 1; + + if(!stream) + return -1; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return -1; + + top = top_thread(stream, rawno); + + if (top != rawno) + mn_set_cur(msgmap,mn_raw2m(msgmap, top)); + else if (thrd->prevthd) + mn_set_cur(msgmap,mn_raw2m(msgmap, top_thread(stream,thrd->prevthd))); + else + rv = 0; + if (display){ + if (rv && SEP_THRDINDX()) + q_status_message(SM_ORDER, 0, 2, "Viewing previous thread"); + if (!rv) + q_status_message(SM_ORDER, 0, 2, "No more threads to go back"); + } + + return rv; + } + + /* add more keys to this list */ + int + allowed_thread_key(SortOrder sort) + { + return sort == SortArrival + || sort == SortDate + || sort == SortScore + || sort == SortThread; + } + diff -rc alpine-2.00/pith/thread.h alpine-2.00.I.USE/pith/thread.h *** alpine-2.00/pith/thread.h 2006-09-22 13:06:05.000000000 -0700 --- alpine-2.00.I.USE/pith/thread.h 2011-02-07 20:33:41.000000000 -0800 *************** *** 37,42 **** --- 37,43 ---- unsigned long nextthd; /* next thread, only tops have this */ unsigned long prevthd; /* previous thread, only tops have this */ unsigned long top; /* top of this thread */ + unsigned long toploose; /* top of this thread, if is loose */ unsigned long head; /* head of the whole thread list */ } PINETHRD_S; *************** *** 92,98 **** void sort_thread_callback(MAILSTREAM *, THREADNODE *); void collapse_threads(MAILSTREAM *, MSGNO_S *, PINETHRD_S *); PINETHRD_S *msgno_thread_info(MAILSTREAM *, unsigned long, PINETHRD_S *, unsigned); ! void collapse_or_expand(struct pine *, MAILSTREAM *, MSGNO_S *, unsigned long); void select_thread_stmp(struct pine *, MAILSTREAM *, MSGNO_S *); unsigned long count_flags_in_thread(MAILSTREAM *, PINETHRD_S *, long); unsigned long count_lflags_in_thread(MAILSTREAM *, PINETHRD_S *, MSGNO_S *, int); --- 93,99 ---- void sort_thread_callback(MAILSTREAM *, THREADNODE *); void collapse_threads(MAILSTREAM *, MSGNO_S *, PINETHRD_S *); PINETHRD_S *msgno_thread_info(MAILSTREAM *, unsigned long, PINETHRD_S *, unsigned); ! void collapse_or_expand(struct pine *, MAILSTREAM *, MSGNO_S *, unsigned long, int); void select_thread_stmp(struct pine *, MAILSTREAM *, MSGNO_S *); unsigned long count_flags_in_thread(MAILSTREAM *, PINETHRD_S *, long); unsigned long count_lflags_in_thread(MAILSTREAM *, PINETHRD_S *, MSGNO_S *, int); *************** *** 106,111 **** int unview_thread(struct pine *, MAILSTREAM *, MSGNO_S *); PINETHRD_S *find_thread_by_number(MAILSTREAM *, MSGNO_S *, long, PINETHRD_S *); void set_search_bit_for_thread(MAILSTREAM *, PINETHRD_S *, SEARCHSET **); ! #endif /* PITH_THREAD_INCLUDED */ --- 107,130 ---- int unview_thread(struct pine *, MAILSTREAM *, MSGNO_S *); PINETHRD_S *find_thread_by_number(MAILSTREAM *, MSGNO_S *, long, PINETHRD_S *); void set_search_bit_for_thread(MAILSTREAM *, PINETHRD_S *, SEARCHSET **); ! void find_msgmap(MAILSTREAM *, MSGNO_S *, int, SortOrder, unsigned); ! void move_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); ! void relink_threads(MAILSTREAM *, MSGNO_S *, long *); ! long top_thread(MAILSTREAM *, long); ! long top_this_thread(MAILSTREAM *, long); ! long get_length_branch(MAILSTREAM *, long); ! unsigned long get_next(MAILSTREAM *,PINETHRD_S *); ! unsigned long get_branch(MAILSTREAM *,PINETHRD_S *); ! int count_thread(struct pine *, MAILSTREAM *, MSGNO_S *, long); ! int count_this_thread(MAILSTREAM *, unsigned long); ! int this_thread_is_kolapsed(struct pine *, MAILSTREAM *, MSGNO_S *, long); ! int thread_is_kolapsed(struct pine *, MAILSTREAM *, MSGNO_S *, long); ! int move_prev_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); ! int move_next_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); ! int move_next_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); ! void move_top_thread(MAILSTREAM *, MSGNO_S *, long); ! void move_top_this_thread(MAILSTREAM *, MSGNO_S *, long); ! THREADNODE *copy_tree(THREADNODE *); ! int allowed_thread_key(SortOrder sort); #endif /* PITH_THREAD_INCLUDED */ diff -rc alpine-2.00/pith/url.c alpine-2.00.I.USE/pith/url.c *** alpine-2.00/pith/url.c 2007-08-29 15:31:51.000000000 -0700 --- alpine-2.00.I.USE/pith/url.c 2011-02-07 20:33:45.000000000 -0800 *************** *** 52,58 **** rfc1738_scan(char *line, int *len) { char *colon, *start, *end; ! int n; /* process each : in the line */ for(; (colon = strindex(line, ':')) != NULL; line = end){ --- 52,58 ---- rfc1738_scan(char *line, int *len) { char *colon, *start, *end; ! int n, delim; /* process each : in the line */ for(; (colon = strindex(line, ':')) != NULL; line = end){ *************** *** 113,119 **** && !struncmp(start + 1, "elnet", 5)))) || (n == 8 && (*start == 'P' || *start == 'p') ! && !struncmp(start + 1, "rospero", 7))) || url_external_specific_handler(start, n))){ /* * Second, make sure that everything to the right of the --- 113,125 ---- && !struncmp(start + 1, "elnet", 5)))) || (n == 8 && (*start == 'P' || *start == 'p') ! && !struncmp(start + 1, "rospero", 7)) ! || (n == 11 ! && (*start == 'x' || *start == 'X') ! && !struncmp(start + 1, "-pine-help", 10)) ! || (n == 13 ! && (*start == 'x' || *start == 'X') ! && !struncmp(start + 1, "-alpine-help", 12))) || url_external_specific_handler(start, n))){ /* * Second, make sure that everything to the right of the *************** *** 130,135 **** --- 136,142 ---- if(i != j){ *len = end - start; + delim = start > line && *(start - 1) == '<'; /* * Special case handling for comma. *************** *** 139,146 **** * In most cases any way, that's why we have the * exception. */ ! if(*(end - 1) == ',' ! || (*(end - 1) == '.' && (!*end || *end == ' '))) (*len)--; if(*len - (colon - start) > 0) --- 146,153 ---- * In most cases any way, that's why we have the * exception. */ ! if(delim == 0 && (*(end - 1) == ',' ! || (*(end - 1) == '.' && (!*end || *end == ' ')))) (*len)--; if(*len - (colon - start) > 0) diff -rc alpine-2.00/README.maildir alpine-2.00.I.USE/README.maildir *** alpine-2.00/README.maildir 2011-02-07 20:34:02.000000000 -0800 --- alpine-2.00.I.USE/README.maildir 2011-02-07 20:33:41.000000000 -0800 *************** *** 0 **** --- 1,153 ---- + --------------------------------------- + + Maildir Driver for Alpine 1.0 + By Eduardo Chappa + http://staff.washington.edu/chappa/alpine/ + + --------------------------------------- + 1. General Information About This Patch + --------------------------------------- + + This patch adds support for the maildir format to Alpine. We take the + approach that this patch is one more driver among the number of formats + supported by Alpine (more generally c-client). This approach differs from + older versions of similar patches, in that once a maildir patch was + applied, it was assumed that all your folders would be created in the + maildir format. + + This patch does not assume that maildir is a preferred format, instead + puts maildir in equal footing with other formats (mbox, mbx, mix, etc), + and so a maildir folder in the mail/ collection is treated in the same way + as any other folder in any other format. In other words, just by reading + the name of a folder, or opening it, or doing any operation with it, you + can not know in which format the folder is. + + This implies that if you want to add a folder in the maildir format to the + mail/ collection, then you must add by pressing "A" in the folder list + collection and enter "#driver.md/mail/name_maildir_folder". + + If you only want to use maildir, however, you can do so too. In this case, + you must create a maildir collection. In that collection, only maildir + folders will be listed. If there is any folder in any other format, that + folder will be ignored. In another words, any folder listed there is in + maildir format and can be accessed through that collection, conversely, + any folder not listed there is not in maildir format and there is no way + to access it using this collection. + + In order to create a maildir collection, you could press M S L, and "A" to + add a collection. Fill in the required fields as follows: + + Nickname : Anything + Server : + Path : #md/relative/path/to/maildir/collection/ + View : + + For example, if "path" is set to "#md/mail/", then Alpine will look for your + maildir folders that are in ~/mail/. + + The code in this patch is mostly based in code for the unix driver plus + some combinations of the mh, mbx and nntp drivers for the c-client + library. Those drivers were designed by Mark Crispin, and bugs in this + code are not his bugs, but my own. + + I got all the specification for this patch from + http://cr.yp.to/proto/maildir.html. If you know of a place with a better + specification for maildir format please let me know. The method this patch + uses to create a unique filename for a message is one of the "old + fashioned" methods. I realize that this is old fashioned, but it is + portable, and portability is the main reason why I decided to use an old + fashioned method (most methods are not portable. See the word + "Unfortunately" in that document). + + -------------- + 2. Other Goals + -------------- + + It is intended that this code will work well with any application + written using the c-client library. Of paramount importance is to make the + associated imap server work well when the server accesses a folder in + Maildir format. The program mailutil should also work flawlessly with this + implemetation of the driver. + + It is intended that this driver be fast and stable. We intend not to + patch Alpine to make this driver do its work, unless such patching is for + fixing bugs in Alpine or to pass parameters to the driver. + + ------------------------------------------------------------------------ + 3. What are the known bugs of this implementation of the Maildir driver? + ------------------------------------------------------------------------ + + I don't know any at this time. There have been bugs before, though, but + I try to fix bugs as soon as they are reported. A complete list of updates + for this patch, which includes bug fixes, improvements and addition of new + features can be found at + + http://staff.washington.edu/chappa/alpine/updates/maildir.html + + ---------- + 4. On UIDs + ---------- + + This patch keeps uids in the name of the file that contains the message, + by adding a ",u=" string to the file name to save the uid of a message. A + file is kept between sessions to save information on the last uid assigned + and its time of validity. Only one session with writing access can write + uids, all others must wait for the other session to assign them. The + session assigning uids creates a ".uidtemp" file which other sessions must + not disturb. + + Uid support appeared in Alpine 1.00 (snapshot 925), and is experimental, + please report any problems. + + -------------------------------------------- + 5. Configuring Alpine and Setting up a Maildir + -------------------------------------------- + + Once this approach was chosen, it implied the following: + + * This patch assumes that your INBOX is located at "$HOME/Maildir". + This is a directory which should have three subdirectories "cur", + "tmp" and "new". Mail is delivered to 'new' and read from 'cur'. I + have added a configuration option "maildir-location" which can be + used to tell Alpine where your Maildir inbox is, in case your system + do not use the above directory (e.g. your system may use + "~/.maildir"). In this case define that variable to be the name of + the directory where your e-mail is being delivered (e.g. + ".maildir"). + + * If you want to use the above configuration as your inbox, you must + define your inbox-path as "#md/inbox" (no quotes). You can define + the inbox-path like above even if you have changed the + maildir-location variable. That's the whole point of that variable. + + ----------------------------------- + 6. What about Courier file systems? + ----------------------------------- + + In a courier file system all folders are subfolders of a root folder + called INBOX. Normally INBOX is located at ~/Maildir and subfolders are + "dot" directories in ~/Maildir. For example ~/Maildir/.Trash is a + subfolder of INBOX and is accessed with the nickname "INBOX.Trash". + + You can not access folders in this way unless you preceed them with the + string "#mc/". The purpose of the string "#mc/" is to warn Alpine that a + collection in the Courier format is going to be accessed, so you can + SELECT a folder like "#mc/INBOX.Trash", but not "INBOX.Trash" + + You can access a collection through a server, but if you want to access a + collection of folders created using the Courier server, you MUST edit your + ".pinerc" file and enter the definition of the collection as follows: + + folder-collections="Anything you want" #mc/INBOX.[] + + You can replace the string "#mc/INBOX." by something different, for example + "#mc/Courier/." will make Alpine search for your collection in ~/Courier. + + You can not add this directly into Alpine because Alpine fails to accept this + value from its input, but it takes it correctly when it is added through + the ".pinerc" file. + + You can access your inbox as "#mc/INBOX" or "#md/INBOX". Both definitions + point to the same place. + + Last Updated February 9, 2008 diff -rc alpine-2.00/web/src/alpined.d/alpined.c alpine-2.00.I.USE/web/src/alpined.d/alpined.c *** alpine-2.00/web/src/alpined.d/alpined.c 2008-08-22 13:41:37.000000000 -0700 --- alpine-2.00.I.USE/web/src/alpined.d/alpined.c 2011-02-07 20:33:43.000000000 -0800 *************** *** 2795,2801 **** init_save_defaults(); break; case V_SORT_KEY: ! decode_sort(ps_global->VAR_SORT_KEY, &ps_global->def_sort, &def_sort_rev); break; case V_VIEW_HDR_COLORS : set_custom_spec_colors(ps_global); --- 2795,2801 ---- init_save_defaults(); break; case V_SORT_KEY: ! decode_sort(ps_global->VAR_SORT_KEY, &ps_global->def_sort, &def_sort_rev, 0); break; case V_VIEW_HDR_COLORS : set_custom_spec_colors(ps_global); *************** *** 6271,6277 **** && mn_get_revsort(sp_msgmap(ps_global->mail_stream)) == reversed)) sort_folder(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), ps_global->sort_types[i], ! reversed, 0); break; } --- 6271,6277 ---- && mn_get_revsort(sp_msgmap(ps_global->mail_stream)) == reversed)) sort_folder(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), ps_global->sort_types[i], ! reversed, 0, 1); break; } *************** *** 6848,6854 **** /* BUG: fix charset not to be NULL below */ if(agg_text_select(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), ! field, not, 0, text, NULL, NULL)) /* BUG: plug in "charset" above? */ return(peSelectError(interp, "programmer botch")); } --- 6848,6854 ---- /* BUG: fix charset not to be NULL below */ if(agg_text_select(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), ! field, NULL, not, 0, text, NULL, NULL)) /* BUG: plug in "charset" above? */ return(peSelectError(interp, "programmer botch")); } diff -rc alpine-2.00/web/src/alpined.d/Makefile.am alpine-2.00.I.USE/web/src/alpined.d/Makefile.am *** alpine-2.00/web/src/alpined.d/Makefile.am 2008-08-12 13:47:04.000000000 -0700 --- alpine-2.00.I.USE/web/src/alpined.d/Makefile.am 2011-02-07 20:33:46.000000000 -0800 *************** *** 50,52 **** --- 50,53 ---- debug.c status.c stubs.c alpined.h color.h ldap.h echo "char datestamp[]="\"`date`\"";" > local.c echo "char hoststamp[]="\"`hostname`\"";" >> local.c + cat ../../../patchlevel >> local.c diff -rc alpine-2.00/web/src/alpined.d/Makefile.in alpine-2.00.I.USE/web/src/alpined.d/Makefile.in *** alpine-2.00/web/src/alpined.d/Makefile.in 2008-08-13 08:44:42.000000000 -0700 --- alpine-2.00.I.USE/web/src/alpined.d/Makefile.in 2011-02-07 20:33:46.000000000 -0800 *************** *** 619,624 **** --- 619,625 ---- debug.c status.c stubs.c alpined.h color.h ldap.h echo "char datestamp[]="\"`date`\"";" > local.c echo "char hoststamp[]="\"`hostname`\"";" >> local.c + cat ../../../patchlevel >> local.c # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: