* New version 2.26
[alpine.git] / alpine / context.c
blobda3838d30dda64a70f9aae8c0efe4722a206c6e4
1 /*
2 * ========================================================================
3 * Copyright 2006-2007 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include "headers.h"
16 #include "context.h"
17 #include "confscroll.h"
18 #include "status.h"
19 #include "folder.h"
20 #include "radio.h"
21 #include "alpine.h"
22 #include "mailindx.h"
23 #include "mailcmd.h"
24 #include "send.h"
25 #include "../pith/list.h"
26 #include "../pith/state.h"
27 #include "../pith/conf.h"
28 #include "../pith/thread.h"
32 * Internal prototypes
34 int context_config_tool(struct pine *, int, CONF_S **, unsigned);
35 int context_config_add(struct pine *, CONF_S **);
36 int context_config_shuffle(struct pine *, CONF_S **);
37 int context_config_edit(struct pine *, CONF_S **);
38 int context_config_delete(struct pine *, CONF_S **);
39 int ccs_var_delete(struct pine *, CONTEXT_S *);
40 int ccs_var_insert(struct pine *, char *, struct variable *, char **, int);
41 int context_select_tool(struct pine *, int, CONF_S **, unsigned);
45 * Setup CollectionLists. Build a context list on the fly from the config
46 * variable and edit it. This won't include Incoming-Folders because that
47 * is a pseudo collection, but that's ok since we can't do the operations
48 * on it, anyway. Reset real config list at the end.
50 void
51 context_config_screen(struct pine *ps, CONT_SCR_S *cs, int edit_exceptions)
53 CONTEXT_S *top, **clist, *cp;
54 CONF_S *ctmpa, *first_line, *heading;
55 OPT_SCREEN_S screen;
56 int i, readonly_warning, some_defined, ret;
57 int reinit_contexts = 0, prime;
58 char **lval, **lval1, **lval2, ***alval;
59 struct variable fake_fspec_var, fake_nspec_var;
60 struct variable *fake_fspec, *fake_nspec;
62 ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
64 if(ps->restricted)
65 readonly_warning = 1;
66 else{
67 PINERC_S *prc = NULL;
69 switch(ew){
70 case Main:
71 prc = ps->prc;
72 break;
73 case Post:
74 prc = ps->post_prc;
75 break;
76 default:
77 break;
80 readonly_warning = prc ? prc->readonly : 1;
81 if(prc && prc->quit_to_edit){
82 quit_to_edit_msg(prc);
83 return;
87 go_again:
88 top = NULL; ctmpa = NULL; first_line = NULL;
89 some_defined = 0, prime = 0;
90 fake_fspec = &fake_fspec_var;
91 fake_nspec = &fake_nspec_var;
92 memset((void *)fake_fspec, 0, sizeof(*fake_fspec));
93 memset((void *)fake_nspec, 0, sizeof(*fake_nspec));
95 /* so fixed_var() will work right */
96 fake_fspec->is_list = 1;
97 fake_nspec->is_list = 1;
98 if((ps->vars[V_FOLDER_SPEC]).is_fixed){
99 fake_fspec->is_fixed = 1;
100 if((ps->vars[V_FOLDER_SPEC]).fixed_val.l
101 && (ps->vars[V_FOLDER_SPEC]).fixed_val.l[0]){
102 fake_fspec->fixed_val.l = (char **) fs_get(2 * sizeof(char *));
103 fake_fspec->fixed_val.l[0]
104 = cpystr((ps->vars[V_FOLDER_SPEC]).fixed_val.l[0]);
105 fake_fspec->fixed_val.l[1] = NULL;
109 if((ps->vars[V_NEWS_SPEC]).is_fixed){
110 fake_nspec->is_fixed = 1;
111 if((ps->vars[V_NEWS_SPEC]).fixed_val.l
112 && (ps->vars[V_NEWS_SPEC]).fixed_val.l[0]){
113 fake_nspec->fixed_val.l = (char **) fs_get(2 * sizeof(char *));
114 fake_nspec->fixed_val.l[0] = cpystr(INHERIT);
115 fake_nspec->fixed_val.l[0]
116 = cpystr((ps->vars[V_NEWS_SPEC]).fixed_val.l[0]);
117 fake_nspec->fixed_val.l[1] = NULL;
121 clist = ⊤
122 lval1 = LVAL(&ps->vars[V_FOLDER_SPEC], ew);
123 lval2 = LVAL(&ps->vars[V_NEWS_SPEC], ew);
125 alval = ALVAL(fake_fspec, ew);
126 if(lval1)
127 *alval = copy_list_array(lval1);
128 else if(!edit_exceptions && ps->VAR_FOLDER_SPEC && ps->VAR_FOLDER_SPEC[0] &&
129 ps->VAR_FOLDER_SPEC[0][0])
130 *alval = copy_list_array(ps->VAR_FOLDER_SPEC);
131 else
132 fake_fspec = NULL;
134 if(fake_fspec){
135 lval = LVAL(fake_fspec, ew);
136 for(i = 0; lval && lval[i]; i++){
137 cp = NULL;
138 if(i == 0 && !strcmp(lval[i], INHERIT)){
139 cp = (CONTEXT_S *)fs_get(sizeof(*cp));
140 memset((void *)cp, 0, sizeof(*cp));
141 cp->use = CNTXT_INHERIT;
142 cp->label = cpystr("Default collections are inherited");
144 else if((cp = new_context(lval[i], &prime)) != NULL){
145 cp->var.v = fake_fspec;
146 cp->var.i = i;
149 if(cp){
150 *clist = cp; /* add it to list */
151 clist = &cp->next; /* prepare for next */
155 set_current_val(fake_fspec, FALSE, FALSE);
158 alval = ALVAL(fake_nspec, ew);
159 if(lval2)
160 *alval = copy_list_array(lval2);
161 else if(!edit_exceptions && ps->VAR_NEWS_SPEC && ps->VAR_NEWS_SPEC[0] &&
162 ps->VAR_NEWS_SPEC[0][0])
163 *alval = copy_list_array(ps->VAR_NEWS_SPEC);
164 else
165 fake_nspec = NULL;
167 if(fake_nspec){
168 lval = LVAL(fake_nspec, ew);
169 for(i = 0; lval && lval[i]; i++){
170 cp = NULL;
171 if(i == 0 && !strcmp(lval[i], INHERIT)){
172 cp = (CONTEXT_S *)fs_get(sizeof(*cp));
173 memset((void *)cp, 0, sizeof(*cp));
174 cp->use = CNTXT_INHERIT;
175 cp->label = cpystr("Default collections are inherited");
177 else if((cp = new_context(lval[i], &prime)) != NULL){
178 cp->var.v = fake_nspec;
179 cp->var.i = i;
182 if(cp){
183 *clist = cp; /* add it to list */
184 clist = &cp->next; /* prepare for next */
188 set_current_val(fake_nspec, FALSE, FALSE);
191 for(cp = top; cp; cp = cp->next)
192 if(!(cp->use & CNTXT_INHERIT)){
193 some_defined++;
194 break;
197 if(edit_exceptions && !some_defined){
198 q_status_message(SM_ORDER, 3, 7,
199 _("No exceptions to edit. First collection exception must be set by editing file"));
200 free_contexts(&top);
201 if(reinit_contexts){
202 free_contexts(&ps_global->context_list);
203 init_folders(ps_global);
206 return;
210 /* fix up prev pointers */
211 for(cp = top; cp; cp = cp->next)
212 if(cp->next)
213 cp->next->prev = cp;
215 new_confline(&ctmpa); /* blank line */
216 ctmpa->keymenu = cs->keymenu;
217 ctmpa->help = cs->help.text;
218 ctmpa->help_title = cs->help.title;
219 ctmpa->tool = context_config_tool;
220 ctmpa->flags |= (CF_NOSELECT | CF_B_LINE);
222 for(cp = top; cp; cp = cp->next){
223 new_confline(&ctmpa);
224 heading = ctmpa;
225 if(!(cp->use & CNTXT_INHERIT))
226 ctmpa->value = cpystr(cp->nickname ? cp->nickname : cp->context);
228 ctmpa->var = cp->var.v;
229 ctmpa->keymenu = cs->keymenu;
230 ctmpa->help = cs->help.text;
231 ctmpa->help_title = cs->help.title;
232 ctmpa->tool = context_config_tool;
233 ctmpa->flags |= CF_STARTITEM;
234 ctmpa->valoffset = 4;
235 ctmpa->d.c.ct = cp;
236 ctmpa->d.c.cs = cs;
237 if(cp->use & CNTXT_INHERIT)
238 ctmpa->flags |= CF_INHERIT | CF_NOSELECT;
240 if((!first_line && !(cp->use & CNTXT_INHERIT)) ||
241 (!(cp->use & CNTXT_INHERIT) &&
242 cp->var.v &&
243 (cs->starting_var == cp->var.v) &&
244 (cs->starting_varmem == cp->var.i)))
245 first_line = ctmpa;
247 /* Add explanatory text */
248 new_confline(&ctmpa);
249 ctmpa->value = cpystr(cp->label ? cp->label : "* * *");
250 ctmpa->keymenu = cs->keymenu;
251 ctmpa->help = cs->help.text;
252 ctmpa->help_title = cs->help.title;
253 ctmpa->tool = context_config_tool;
254 ctmpa->flags |= CF_NOSELECT;
255 ctmpa->valoffset = 8;
257 /* Always add blank line, make's shuffling a little easier */
258 new_confline(&ctmpa);
259 heading->headingp = ctmpa; /* use headingp to mark end */
260 ctmpa->keymenu = cs->keymenu;
261 ctmpa->help = cs->help.text;
262 ctmpa->help_title = cs->help.title;
263 ctmpa->tool = context_config_tool;
264 ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
265 ctmpa->valoffset = 0;
268 cs->starting_var = NULL;
271 memset(&screen, 0, sizeof(screen));
272 screen.ro_warning = readonly_warning;
273 ret = conf_scroll_screen(ps, &screen, first_line, cs->title,
274 cs->print_string, 0, NULL);
276 free_contexts(&top);
278 if(ret)
279 reinit_contexts++;
282 * 15 means the tool wants us to reset and go again. The config var
283 * has been changed. The contexts will be built again from the
284 * config variable and the real contexts will be rebuilt below.
285 * This is easier and safer than having the tools rebuild the context
286 * list and the display correct. It's difficult to do because of all
287 * the inheriting and defaulting going on.
289 if(ret == 15){
290 if(edit_exceptions && !LVAL(fake_fspec, ew) && !LVAL(fake_nspec, ew)){
291 if(want_to(_("Really delete last exceptional collection"),
292 'n', 'n', h_config_context_del_except,
293 WT_FLUSH_IN) != 'y'){
294 free_variable_values(fake_fspec);
295 free_variable_values(fake_nspec);
296 goto go_again;
300 /* resolve variable changes */
301 if((lval1 && !equal_list_arrays(lval1, LVAL(fake_fspec, ew))) ||
302 (fake_fspec && !equal_list_arrays(ps->VAR_FOLDER_SPEC,
303 LVAL(fake_fspec, ew)))){
304 i = set_variable_list(V_FOLDER_SPEC,
305 LVAL(fake_fspec, ew), TRUE, ew);
306 set_current_val(&ps->vars[V_FOLDER_SPEC], TRUE, FALSE);
308 if(i)
309 q_status_message(SM_ORDER, 3, 3,
310 _("Trouble saving change, cancelled"));
311 else if(!edit_exceptions && lval1 && !LVAL(fake_fspec, ew)){
312 cs->starting_var = fake_fspec;
313 cs->starting_varmem = 0;
314 q_status_message(SM_ORDER, 3, 3,
315 _("Deleted last Folder-Collection, reverting to default"));
317 else if(!edit_exceptions && !lval1 && !LVAL(fake_fspec, ew)){
318 cs->starting_var = fake_fspec;
319 cs->starting_varmem = 0;
320 q_status_message(SM_ORDER, 3, 3,
321 _("Deleted default Folder-Collection, reverting back to default"));
325 if((lval2 && !equal_list_arrays(lval2, LVAL(fake_nspec, ew))) ||
326 (fake_nspec && !equal_list_arrays(ps->VAR_NEWS_SPEC,
327 LVAL(fake_nspec, ew)))){
328 i = set_variable_list(V_NEWS_SPEC,
329 LVAL(fake_nspec, ew), TRUE, ew);
330 set_news_spec_current_val(TRUE, FALSE);
332 if(i)
333 q_status_message(SM_ORDER, 3, 3,
334 _("Trouble saving change, cancelled"));
335 else if(!edit_exceptions && lval2 && !LVAL(fake_nspec, ew) &&
336 ps->VAR_NEWS_SPEC && ps->VAR_NEWS_SPEC[0] &&
337 ps->VAR_NEWS_SPEC[0][0]){
338 cs->starting_var = fake_nspec;
339 cs->starting_varmem = 0;
340 q_status_message(SM_ORDER, 3, 3,
341 _("Deleted last News-Collection, reverting to default"));
343 else if(!edit_exceptions && !lval2 && !LVAL(fake_nspec, ew) &&
344 ps->VAR_NEWS_SPEC && ps->VAR_NEWS_SPEC[0] &&
345 ps->VAR_NEWS_SPEC[0][0]){
346 cs->starting_var = fake_nspec;
347 cs->starting_varmem = 0;
348 q_status_message(SM_ORDER, 3, 3,
349 _("Deleted default News-Collection, reverting back to default"));
353 free_variable_values(fake_fspec);
354 free_variable_values(fake_nspec);
355 goto go_again;
358 ps->mangled_screen = 1;
360 /* make the real context list match the changed config variables */
361 if(reinit_contexts){
362 free_contexts(&ps_global->context_list);
363 init_folders(ps_global);
368 /*----------------------------------------------------------------------
369 Function to display/manage collections
371 ----*/
372 CONTEXT_S *
373 context_select_screen(struct pine *ps, CONT_SCR_S *cs, int ro_warn)
375 CONTEXT_S *cp;
376 CONF_S *ctmpa = NULL, *first_line = NULL, *heading;
377 OPT_SCREEN_S screen, *saved_screen;
378 int readonly_warning = 0;
380 /* restrict to normal config */
381 ew = Main;
383 if(!cs->edit)
384 readonly_warning = 0;
385 else if(ps->restricted)
386 readonly_warning = 1;
387 else{
388 PINERC_S *prc = NULL;
390 switch(ew){
391 case Main:
392 prc = ps->prc;
393 break;
394 case Post:
395 prc = ps->post_prc;
396 break;
397 default:
398 break;
401 readonly_warning = prc ? prc->readonly : 1;
402 if(ro_warn && prc && prc->quit_to_edit){
403 quit_to_edit_msg(prc);
404 return(NULL);
408 readonly_warning *= ro_warn;
411 * Loop thru available contexts, setting up for display
412 * (note: if no "cp" we're hosed. should never happen ;)
414 for(cp = *cs->contexts; cp->prev; cp = cp->prev)
417 /* delimiter for Mail Collections */
418 new_confline(&ctmpa); /* blank line */
419 ctmpa->keymenu = cs->keymenu;
420 ctmpa->help = cs->help.text;
421 ctmpa->help_title = cs->help.title;
422 ctmpa->tool = context_select_tool;
423 ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
426 new_confline(&ctmpa);
427 heading = ctmpa;
428 ctmpa->value = cpystr(cp->nickname ? cp->nickname : cp->context);
429 ctmpa->var = cp->var.v;
430 ctmpa->keymenu = cs->keymenu;
431 ctmpa->help = cs->help.text;
432 ctmpa->help_title = cs->help.title;
433 ctmpa->tool = context_select_tool;
434 ctmpa->flags |= CF_STARTITEM;
435 ctmpa->valoffset = 4;
436 ctmpa->d.c.ct = cp;
437 ctmpa->d.c.cs = cs;
439 if(!first_line || cp == cs->start)
440 first_line = ctmpa;
442 /* Add explanatory text */
443 new_confline(&ctmpa);
444 ctmpa->value = cpystr(cp->label ? cp->label : "* * *");
445 ctmpa->keymenu = cs->keymenu;
446 ctmpa->help = cs->help.text;
447 ctmpa->help_title = cs->help.title;
448 ctmpa->tool = context_select_tool;
449 ctmpa->flags |= CF_NOSELECT;
450 ctmpa->valoffset = 8;
452 /* Always add blank line, make's shuffling a little easier */
453 new_confline(&ctmpa);
454 heading->headingp = ctmpa;
455 ctmpa->keymenu = cs->keymenu;
456 ctmpa->help = cs->help.text;
457 ctmpa->help_title = cs->help.title;
458 ctmpa->tool = context_select_tool;
459 ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
460 ctmpa->valoffset = 0;
462 while((cp = cp->next) != NULL);
465 saved_screen = opt_screen;
466 memset(&screen, 0, sizeof(screen));
467 screen.ro_warning = readonly_warning;
468 (void) conf_scroll_screen(ps, &screen, first_line, cs->title,
469 cs->print_string, 0, NULL);
470 opt_screen = saved_screen;
471 ps->mangled_screen = 1;
472 return(cs->selected);
477 context_config_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
479 int retval = 0;
481 switch(cmd){
482 case MC_DELETE :
483 if(!fixed_var((*cl)->var, "delete", "collection"))
484 retval = context_config_delete(ps, cl);
486 break;
488 case MC_EDIT :
489 if(!fixed_var((*cl)->var, "change", "collection"))
490 retval = context_config_edit(ps, cl);
492 break;
494 case MC_ADD :
495 if(!fixed_var((*cl)->var, "add to", "collection"))
496 retval = context_config_add(ps, cl);
498 break;
500 case MC_SHUFFLE :
501 if(!fixed_var((*cl)->var, "shuffle", "collection"))
502 retval = context_config_shuffle(ps, cl);
504 break;
506 case MC_EXIT :
507 retval = simple_exit_cmd(flags);
508 break;
510 default:
511 retval = -1;
512 break;
515 if(retval > 0)
516 ps->mangled_body = 1;
518 return(retval);
523 context_config_add(struct pine *ps, CONF_S **cl)
525 char *raw_ctxt;
526 struct variable *var;
527 char **lval;
528 int count;
530 if((raw_ctxt = context_edit_screen(ps, "ADD", NULL, NULL, NULL, NULL)) != NULL){
533 * If var is non-NULL we add to the end of that var.
534 * If it is NULL, that means we're adding to the current_val, so
535 * we'll have to soak up the default values from there into our
536 * own variable.
538 if((*cl)->var){
539 var = (*cl)->var;
540 lval = LVAL((*cl)->var, ew);
542 else{
543 q_status_message(SM_ORDER|SM_DING, 3, 3,
544 "Programmer botch in context_config_add");
545 return(0);
548 for(count = 0; lval && lval[count]; count++)
551 if(!ccs_var_insert(ps, raw_ctxt, var, lval, count)){
552 fs_give((void **)&raw_ctxt);
553 q_status_message(SM_ORDER|SM_DING, 3, 3,
554 _("Error adding new collection"));
555 return(0);
558 fs_give((void **)&raw_ctxt);
560 (*cl)->d.c.cs->starting_var = var;
561 (*cl)->d.c.cs->starting_varmem = count;
562 q_status_message(SM_ORDER, 0, 3,
563 _("New collection added. Use \"$\" to adjust order."));
564 return(15);
567 ps->mangled_screen = 1;
568 return(0);
573 context_config_shuffle(struct pine *ps, CONF_S **cl)
575 char prompt[256];
576 int n = 0, cmd, i1, i2, count = 0, insert_num, starting_varmem;
577 int news_problem = 0, deefault = 0;
578 ESCKEY_S ekey[3];
579 CONTEXT_S *cur_ctxt, *other_ctxt = NULL;
580 char *tmp, **lval, **lval1, **lval2;
581 struct variable *cur_var, *other_var;
583 if(!((*cl)->d.c.ct && (*cl)->var))
584 return(0);
586 /* enable UP? */
587 if((*cl)->d.c.ct->prev && !((*cl)->d.c.ct->prev->use & CNTXT_INHERIT)){
589 * Don't allow shuffling news collection up to become
590 * the primary collection. That would happen if prev is primary
591 * and this one is news.
593 if((*cl)->d.c.ct->prev->use & CNTXT_SAVEDFLT &&
594 (*cl)->d.c.ct->use & CNTXT_NEWS)
595 news_problem++;
596 else{
597 ekey[n].ch = 'u';
598 ekey[n].rval = 'u';
599 ekey[n].name = "U";
600 ekey[n++].label = N_("Up");
601 deefault = 'u';
605 /* enable DOWN? */
606 if((*cl)->d.c.ct->next && !((*cl)->d.c.ct->next->use & CNTXT_INHERIT)){
608 * Don't allow shuffling down past news collection if this
609 * is primary collection.
611 if((*cl)->d.c.ct->use & CNTXT_SAVEDFLT &&
612 (*cl)->d.c.ct->next->use & CNTXT_NEWS)
613 news_problem++;
614 else{
615 ekey[n].ch = 'd';
616 ekey[n].rval = 'd';
617 ekey[n].name = "D";
618 ekey[n++].label = N_("Down");
619 if(!deefault)
620 deefault = 'd';
624 if(n){
625 ekey[n].ch = -1;
626 snprintf(prompt, sizeof(prompt), _("Shuffle selected context %s%s%s? "),
627 (ekey[0].ch == 'u') ? _("UP") : "",
628 (n > 1) ? " or " : "",
629 (ekey[0].ch == 'd' || n > 1) ? _("DOWN") : "");
630 prompt[sizeof(prompt)-1] = '\0';
632 cmd = radio_buttons(prompt, -FOOTER_ROWS(ps), ekey,
633 deefault, 'x', NO_HELP, RB_NORM);
634 switch(cmd){
635 case 'x':
636 default:
637 cmd_cancelled("Shuffle");
638 return(0);
640 case 'u':
641 case 'd':
642 break;
646 * This is complicated by the fact that there are two
647 * vars involved, the folder-collections and the news-collections.
648 * We may have to shuffle across collections.
650 cur_ctxt = (*cl)->d.c.ct;
651 if(cmd == 'd')
652 other_ctxt = (*cl)->d.c.ct->next;
653 else if(cmd == 'u')
654 other_ctxt = (*cl)->d.c.ct->prev;
656 cur_var = cur_ctxt->var.v;
657 other_var = other_ctxt->var.v;
659 /* swap elements of config var */
660 if(cur_var == other_var){
661 i1 = cur_ctxt->var.i;
662 i2 = other_ctxt->var.i;
663 lval = LVAL(cur_var, ew);
664 if(lval){
665 tmp = lval[i1];
666 lval[i1] = lval[i2];
667 lval[i2] = tmp;
670 starting_varmem = i2;
672 else{
673 /* swap into the other_var */
674 i1 = cur_ctxt->var.i;
675 i2 = other_ctxt->var.i;
676 lval1 = LVAL(cur_var, ew);
677 lval2 = LVAL(other_var, ew);
678 /* count */
679 for(count = 0; lval2 && lval2[count]; count++)
681 if(cmd == 'd')
682 insert_num = count ? 1 : 0;
683 else{
684 insert_num = count ? count - 1 : count;
687 starting_varmem = insert_num;
688 if(ccs_var_insert(ps,lval1[i1],other_var,lval2,insert_num)){
689 if(!ccs_var_delete(ps, cur_ctxt)){
690 q_status_message(SM_ORDER|SM_DING, 3, 3,
691 _("Error deleting shuffled context"));
692 return(0);
695 else{
696 q_status_message(SM_ORDER, 3, 3,
697 _("Trouble shuffling, cancelled"));
698 return(0);
702 (*cl)->d.c.cs->starting_var = other_var;
703 (*cl)->d.c.cs->starting_varmem = starting_varmem;
705 q_status_message(SM_ORDER, 0, 3, _("Collections shuffled"));
706 return(15);
709 if(news_problem)
710 /* TRANSLATORS: Sorry, can't move news group collections above email collections */
711 q_status_message(SM_ORDER, 0, 3, _("Sorry, cannot Shuffle news to top"));
712 else
713 q_status_message(SM_ORDER, 0, 3, _("Sorry, nothing to Shuffle"));
715 return(0);
720 context_config_edit(struct pine *ps, CONF_S **cl)
722 char *raw_ctxt, tpath[MAILTMPLEN], *p, **lval;
723 struct variable *var;
724 int i;
726 if(!(*cl)->d.c.ct)
727 return(0);
729 /* Undigest the context */
730 strncpy(tpath, ((*cl)->d.c.ct->context[0] == '{'
731 && (p = strchr((*cl)->d.c.ct->context, '}')))
732 ? ++p
733 : (*cl)->d.c.ct->context, sizeof(tpath)-1);
734 tpath[sizeof(tpath)-1] = '\0';
736 if((p = strstr(tpath, "%s")) != NULL)
737 *p = '\0';
739 if((raw_ctxt = context_edit_screen(ps, "EDIT", (*cl)->d.c.ct->nickname,
740 (*cl)->d.c.ct->server, tpath,
741 (*cl)->d.c.ct->dir ?
742 (*cl)->d.c.ct->dir->view.user
743 : NULL)) != NULL){
745 if((*cl)->var){
746 var = (*cl)->var;
747 lval = LVAL(var, ew);
748 i = (*cl)->d.c.ct->var.i;
750 if(lval && lval[i] && !strcmp(lval[i], raw_ctxt))
751 q_status_message(SM_ORDER, 0, 3, _("No change"));
752 else if(lval){
753 if(lval[i])
754 fs_give((void **) &lval[i]);
756 lval[i] = raw_ctxt;
757 raw_ctxt = NULL;
759 q_status_message(SM_ORDER, 0, 3, _("Collection list entry updated"));
762 (*cl)->d.c.cs->starting_var = var;
763 (*cl)->d.c.cs->starting_varmem = i;
765 if(raw_ctxt)
766 fs_give((void **) &raw_ctxt);
768 else{
769 q_status_message(SM_ORDER|SM_DING, 3, 3,
770 "Programmer botch in context_config_edit");
771 return(0);
774 return(15);
777 ps->mangled_screen = 1;
778 return(0);
783 context_config_delete(struct pine *ps, CONF_S **cl)
785 char tmp[MAILTMPLEN];
787 if(!(*cl)->var){
788 q_status_message(SM_ORDER|SM_DING, 3, 3,
789 "Programmer botch in context_config_delete");
790 return(0);
793 if((*cl)->d.c.ct->use & CNTXT_SAVEDFLT &&
794 (*cl)->d.c.ct->next &&
795 (*cl)->d.c.ct->next->use & CNTXT_NEWS &&
796 (*cl)->d.c.ct->var.v == (*cl)->d.c.ct->next->var.v){
797 q_status_message(SM_ORDER|SM_DING, 3, 3,
798 _("Sorry, cannot Delete causing news to move to top"));
799 return(0);
802 snprintf(tmp, sizeof(tmp), _("Delete the collection definition for \"%s\""),
803 (*cl)->value);
804 tmp[sizeof(tmp)-1] = '\0';
805 if(want_to(tmp, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
807 (*cl)->d.c.cs->starting_var = (*cl)->var;
808 (*cl)->d.c.cs->starting_varmem = (*cl)->d.c.ct->var.i;
809 if((*cl)->d.c.ct->next){
810 if((*cl)->d.c.ct->next->var.v != (*cl)->var){
811 (*cl)->d.c.cs->starting_var = (*cl)->d.c.ct->next->var.v;
812 (*cl)->d.c.cs->starting_varmem = 0;
814 else{
815 (*cl)->d.c.cs->starting_var = (*cl)->var;
816 (*cl)->d.c.cs->starting_varmem = (*cl)->d.c.ct->var.i;
819 else{
820 if((*cl)->d.c.ct->var.i > 0){
821 (*cl)->d.c.cs->starting_var = (*cl)->var;
822 (*cl)->d.c.cs->starting_varmem = (*cl)->d.c.ct->var.i - 1;
824 else{
825 if((*cl)->d.c.ct->prev){
826 (*cl)->d.c.cs->starting_var = (*cl)->d.c.ct->prev->var.v;
827 (*cl)->d.c.cs->starting_varmem = (*cl)->d.c.ct->prev->var.i;
832 /* Remove from var list */
833 if(!ccs_var_delete(ps, (*cl)->d.c.ct)){
834 q_status_message(SM_ORDER|SM_DING, 3, 3,
835 _("Error deleting renamed context"));
836 return(0);
839 q_status_message(SM_ORDER, 0, 3, _("Collection deleted"));
841 return(15);
844 q_status_message(SM_ORDER, 0, 3, _("No collections deleted"));
845 return(0);
850 ccs_var_delete(struct pine *ps, CONTEXT_S *ctxt)
852 int count, i;
853 char **newl = NULL, **lval, **lp, ***alval;
855 if(ctxt)
856 lval = LVAL(ctxt->var.v, ew);
857 else
858 lval = NULL;
860 for(count = 0; lval && lval[count]; count++)
861 ; /* sum the list */
863 if(count > 1){
864 newl = (char **) fs_get(count * sizeof(char *));
865 for(i = 0, lp = newl; lval[i]; i++)
866 if(i != ctxt->var.i)
867 *lp++ = cpystr(lval[i]);
869 *lp = NULL;
872 alval = ALVAL(ctxt->var.v, ew);
873 if(alval){
874 free_list_array(alval);
875 if(newl){
876 for(i = 0; newl[i] ; i++) /* count elements */
879 *alval = (char **) fs_get((i+1) * sizeof(char *));
881 for(i = 0; newl[i] ; i++)
882 (*alval)[i] = cpystr(newl[i]);
884 (*alval)[i] = NULL;
888 free_list_array(&newl);
889 return(1);
894 * Insert into "var", which currently has values "oldvarval", the "newline"
895 * at position "insert".
898 ccs_var_insert(struct pine *ps, char *newline, struct variable *var, char **oldvarval, int insert)
900 int count, i, offset;
901 char **newl, ***alval;
903 for(count = 0; oldvarval && oldvarval[count]; count++)
906 if(insert < 0 || insert > count){
907 q_status_message(SM_ORDER,3,5, "unexpected problem inserting folder");
908 return(0);
911 newl = (char **)fs_get((count + 2) * sizeof(char *));
912 newl[insert] = cpystr(newline);
913 newl[count + 1] = NULL;
914 for(i = offset = 0; oldvarval && oldvarval[i]; i++){
915 if(i == insert)
916 offset = 1;
918 newl[i + offset] = cpystr(oldvarval[i]);
921 alval = ALVAL(var, ew);
922 if(alval){
923 free_list_array(alval);
924 if(newl){
925 for(i = 0; newl[i] ; i++) /* count elements */
928 *alval = (char **) fs_get((i+1) * sizeof(char *));
930 for(i = 0; newl[i] ; i++)
931 (*alval)[i] = cpystr(newl[i]);
933 (*alval)[i] = NULL;
937 free_list_array(&newl);
938 return(1);
943 context_select_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
945 int retval = 0;
947 switch(cmd){
948 case MC_CHOICE :
949 (*cl)->d.c.cs->selected = (*cl)->d.c.ct;
950 retval = simple_exit_cmd(flags);
951 break;
953 case MC_EXIT :
954 retval = simple_exit_cmd(flags);
955 break;
957 case MC_MAIN :
958 retval = simple_exit_cmd(flags);
959 ps_global->next_screen = main_menu_screen;
960 break;
962 case MC_INDEX :
963 if(THREADING()
964 && sp_viewing_a_thread(ps_global->mail_stream)
965 && unview_thread(ps_global, ps_global->mail_stream, ps_global->msgmap)){
966 ps_global->next_screen = mail_index_screen;
967 ps_global->view_skipped_index = 0;
968 ps_global->mangled_screen = 1;
971 retval = simple_exit_cmd(flags);
972 ps_global->next_screen = mail_index_screen;
973 break;
975 case MC_COMPOSE :
976 retval = simple_exit_cmd(flags);
977 ps_global->next_screen = compose_screen;
978 break;
980 case MC_ROLE :
981 retval = simple_exit_cmd(flags);
982 ps_global->next_screen = alt_compose_screen;
983 break;
985 case MC_GOTO :
987 int notrealinbox;
988 CONTEXT_S *c = (*cl)->d.c.ct;
989 char *new_fold = broach_folder(-FOOTER_ROWS(ps), 0, &notrealinbox, &c);
991 if(new_fold && do_broach_folder(new_fold, c, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT) > 0){
992 ps_global->next_screen = mail_index_screen;
993 retval = simple_exit_cmd(flags);
995 else
996 ps->mangled_footer = 1;
999 break;
1001 case MC_QUIT :
1002 retval = simple_exit_cmd(flags);
1003 ps_global->next_screen = quit_screen;
1004 break;
1006 default:
1007 retval = -1;
1008 break;
1011 if(retval > 0)
1012 ps->mangled_body = 1;
1014 return(retval);