* Fix for a better bound check in imap/src/c-client/nntp.c submitted by
[alpine.git] / pith / abdlc.c
blobd884211367fcfc2a6c85bb4fe081d308e4935b90
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2007 University of Washington
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 "../pith/headers.h" /* for os-dep and pith defs/includes */
16 #include "../pith/abdlc.h"
17 #include "../pith/state.h"
18 #include "../pith/conf.h"
19 #include "../pith/status.h"
20 #include "../pith/ldap.h"
21 #include "../pith/tempfile.h"
25 * Internal prototypes
27 void initialize_dlc_cache(void);
28 int dlc_siblings(DL_CACHE_S *, DL_CACHE_S *);
29 DL_CACHE_S *dlc_mgr(long, DlMgrOps, DL_CACHE_S *);
30 void free_cache_array(DL_CACHE_S **, int);
31 DL_CACHE_S *dlc_prev(DL_CACHE_S *, DL_CACHE_S *);
32 DL_CACHE_S *dlc_next(DL_CACHE_S *, DL_CACHE_S *);
33 DL_CACHE_S *get_global_top_dlc(DL_CACHE_S *);
34 DL_CACHE_S *get_global_bottom_dlc(DL_CACHE_S *);
35 DL_CACHE_S *get_top_dl_of_adrbk(int, DL_CACHE_S *);
36 DL_CACHE_S *get_bottom_dl_of_adrbk(int, DL_CACHE_S *);
39 #define NO_PERMISSION _("[ Permission Denied ]")
40 /* TRANSLATORS: This is a heading referring to something that is readable
41 but not writeable. */
42 #define READONLY _(" (ReadOnly)")
43 /* TRANSLATORS: Not readable */
44 #define NOACCESS _(" (Un-readable)")
45 /* TRANSLATORS: Directories, as in LDAP Directories. A heading. */
46 #define OLDSTYLE_DIR_TITLE _("Directories")
49 /* data for the display list cache */
50 static DL_CACHE_S *cache_array = (DL_CACHE_S *)NULL;
51 static long valid_low,
52 valid_high;
53 static int index_of_low,
54 size_of_cache,
55 n_cached;
58 void
59 initialize_dlc_cache(void)
61 dprint((11, "- initialize_dlc_cache -\n"));
63 (void)dlc_mgr(NO_LINE, Initialize, (DL_CACHE_S *)NULL);
67 void
68 done_with_dlc_cache(void)
70 dprint((11, "- done_with_dlc_cache -\n"));
72 (void)dlc_mgr(NO_LINE, DoneWithCache, (DL_CACHE_S *)NULL);
77 * Returns 1 if the dlc's are related to each other, 0 otherwise.
79 * The idea is that if you are going to flush one of these dlcs from the
80 * cache you should also flush its partners. For example, if you flush one
81 * Listent from a list you should flush the entire entry including all the
82 * Listents and the ListHead. If you flush a DlcTitle, you should also
83 * flush the SubTitle and the TitleBlankTop.
85 int
86 dlc_siblings(DL_CACHE_S *dlc1, DL_CACHE_S *dlc2)
88 if(!dlc1 || !dlc2 || dlc1->adrbk_num != dlc2->adrbk_num)
89 return 0;
91 switch(dlc1->type){
93 case DlcSimple:
94 case DlcListHead:
95 case DlcListBlankTop:
96 case DlcListBlankBottom:
97 case DlcListEnt:
98 case DlcListClickHere:
99 case DlcListEmpty:
100 switch(dlc2->type){
101 case DlcSimple:
102 case DlcListHead:
103 case DlcListBlankTop:
104 case DlcListBlankBottom:
105 case DlcListEnt:
106 case DlcListClickHere:
107 case DlcListEmpty:
108 return(dlc1->dlcelnum == dlc2->dlcelnum);
110 default:
111 return 0;
114 break;
116 case DlcDirDelim1:
117 case DlcDirDelim2:
118 switch(dlc2->type){
119 case DlcDirDelim1:
120 case DlcDirDelim2:
121 return 1;
123 default:
124 return 0;
126 break;
128 case DlcGlobDelim1:
129 case DlcGlobDelim2:
130 switch(dlc2->type){
131 case DlcGlobDelim1:
132 case DlcGlobDelim2:
133 return 1;
135 default:
136 return 0;
138 break;
140 case DlcTitle:
141 case DlcTitleNoPerm:
142 case DlcSubTitle:
143 case DlcTitleBlankTop:
144 switch(dlc2->type){
145 case DlcTitle:
146 case DlcTitleNoPerm:
147 case DlcSubTitle:
148 case DlcTitleBlankTop:
149 return 1;
151 default:
152 return 0;
154 break;
156 case DlcDirAccess:
157 case DlcDirSubTitle:
158 case DlcDirBlankTop:
159 switch(dlc2->type){
160 case DlcDirAccess:
161 case DlcDirSubTitle:
162 case DlcDirBlankTop:
163 return 1;
165 default:
166 return 0;
168 break;
170 case DlcTitleDashTopCmb:
171 case DlcTitleCmb:
172 case DlcTitleDashBottomCmb:
173 case DlcTitleBlankBottomCmb:
174 case DlcClickHereCmb:
175 case DlcTitleBlankTopCmb:
176 switch(dlc2->type){
177 case DlcTitleDashTopCmb:
178 case DlcTitleCmb:
179 case DlcTitleDashBottomCmb:
180 case DlcTitleBlankBottomCmb:
181 case DlcClickHereCmb:
182 case DlcTitleBlankTopCmb:
183 return 1;
185 default:
186 return 0;
188 break;
190 default:
191 return 0;
193 /*NOTREACHED*/
198 * Manage the display list cache.
200 * The cache is a circular array of DL_CACHE_S elements. It always
201 * contains a contiguous set of display lines.
202 * The lowest numbered line in the cache is
203 * valid_low, and the highest is valid_high. Everything in between is
204 * also valid. Index_of_low is where to look
205 * for the valid_low element in the circular array.
207 * We make calls to dlc_prev and dlc_next to get new entries for the cache.
208 * We need a starting value before we can do that.
210 * This returns a pointer to a dlc for the desired row. If you want the
211 * actual display list line you call dlist(row) instead of dlc_mgr.
213 DL_CACHE_S *
214 dlc_mgr(long int row, DlMgrOps op, DL_CACHE_S *dlc_start)
216 int new_index, known_index, next_index = 0;
217 DL_CACHE_S *dlc = (DL_CACHE_S *)NULL;
218 long next_row = 0L;
219 long prev_row = 0L;
222 if(op == Lookup){
224 if(row >= valid_low && row <= valid_high){ /* already cached */
226 new_index = ((row - valid_low) + index_of_low) % size_of_cache;
227 dlc = &cache_array[new_index];
230 else if(row > valid_high){ /* row is past where we've looked */
232 known_index =
233 ((valid_high - valid_low) + index_of_low) % size_of_cache;
234 next_row = valid_high + 1L;
236 /* we'll usually be looking for row = valid_high + 1 */
237 while(next_row <= row){
239 new_index = (known_index + 1) % size_of_cache;
241 dlc =
242 dlc_next(&cache_array[known_index], &cache_array[new_index]);
245 * This means somebody changed the file out from underneath
246 * us. This would happen if dlc_next needed to ask for an
247 * abe to figure out what the type of the next row is, but
248 * adrbk_get_ae returned a NULL. I don't think that can
249 * happen, but if it does...
251 if(dlc->type == DlcNotSet){
252 dprint((1, "dlc_next returned DlcNotSet\n"));
253 goto panic_abook_corrupt;
256 if(n_cached == size_of_cache){ /* replaced low cache entry */
257 valid_low++;
258 index_of_low = (index_of_low + 1) % size_of_cache;
260 else
261 n_cached++;
263 valid_high++;
265 next_row++;
266 known_index = new_index; /* for next time through loop */
269 else if(row < valid_low){ /* row is back up the screen */
271 known_index = index_of_low;
272 prev_row = valid_low - 1L;
274 while(prev_row >= row){
276 new_index = (known_index - 1 + size_of_cache) % size_of_cache;
277 dlc =
278 dlc_prev(&cache_array[known_index], &cache_array[new_index]);
280 if(dlc->type == DlcNotSet){
281 dprint((1, "dlc_prev returned DlcNotSet (1)\n"));
282 goto panic_abook_corrupt;
285 if(n_cached == size_of_cache) /* replaced high cache entry */
286 valid_high--;
287 else
288 n_cached++;
290 valid_low--;
291 index_of_low =
292 (index_of_low - 1 + size_of_cache) % size_of_cache;
294 prev_row--;
295 known_index = new_index;
299 else if(op == Initialize){
301 n_cached = 0;
303 if(!cache_array || size_of_cache != 3 * MAX(as.l_p_page,1)){
304 if(cache_array)
305 free_cache_array(&cache_array, size_of_cache);
307 size_of_cache = 3 * MAX(as.l_p_page,1);
308 cache_array =
309 (DL_CACHE_S *)fs_get(size_of_cache * sizeof(DL_CACHE_S));
310 memset((void *)cache_array, 0, size_of_cache * sizeof(DL_CACHE_S));
313 /* this will return NULL below and the caller should ignore that */
316 * Flush all rows for a particular addrbook entry from the cache, but
317 * keep the cache alive and anchored in the same place. The particular
318 * entry is the one that dlc_start is one of the rows of.
320 else if(op == FlushDlcFromCache){
321 long low_entry;
323 next_row = dlc_start->global_row - 1;
324 for(; next_row >= valid_low; next_row--){
325 next_index = ((next_row - valid_low) + index_of_low) %
326 size_of_cache;
327 if(!dlc_siblings(dlc_start, &cache_array[next_index]))
328 break;
331 low_entry = next_row + 1L;
334 * If low_entry now points one past a ListBlankBottom, delete that,
335 * too, since it may not make sense anymore.
337 if(low_entry > valid_low){
338 next_index = ((low_entry -1L - valid_low) + index_of_low) %
339 size_of_cache;
340 if(cache_array[next_index].type == DlcListBlankBottom)
341 low_entry--;
344 if(low_entry > valid_low){ /* invalidate everything >= this */
345 n_cached -= (valid_high - (low_entry - 1L));
346 valid_high = low_entry - 1L;
348 else{
350 * This is the tough case. That entry was the first thing cached,
351 * so we need to invalidate the whole cache. However, we also
352 * need to keep at least one thing cached for an anchor, so
353 * we need to get the dlc before this one and it should be a
354 * dlc not related to this same addrbook entry.
356 known_index = index_of_low;
357 prev_row = valid_low - 1L;
359 for(;;){
361 new_index = (known_index - 1 + size_of_cache) % size_of_cache;
362 dlc =
363 dlc_prev(&cache_array[known_index], &cache_array[new_index]);
365 if(dlc->type == DlcNotSet){
366 dprint((1, "dlc_prev returned DlcNotSet (2)\n"));
367 goto panic_abook_corrupt;
370 valid_low--;
371 index_of_low =
372 (index_of_low - 1 + size_of_cache) % size_of_cache;
374 if(!dlc_siblings(dlc_start, dlc))
375 break;
377 known_index = new_index;
380 n_cached = 1;
381 valid_high = valid_low;
385 * We have to anchor ourselves at a first element.
386 * Here's how we start at the top.
388 else if(op == FirstEntry){
389 initialize_dlc_cache();
390 n_cached++;
391 dlc = &cache_array[0];
392 dlc = get_global_top_dlc(dlc);
393 dlc->global_row = row;
394 index_of_low = 0;
395 valid_low = row;
396 valid_high = row;
398 /* And here's how we start from the bottom. */
399 else if(op == LastEntry){
400 initialize_dlc_cache();
401 n_cached++;
402 dlc = &cache_array[0];
403 dlc = get_global_bottom_dlc(dlc);
404 dlc->global_row = row;
405 index_of_low = 0;
406 valid_low = row;
407 valid_high = row;
410 * And here's how we start from an arbitrary position in the middle.
411 * We root the cache at display line row, so it helps if row is close
412 * to where we're going to be starting so that things are easy to find.
413 * The dl that goes with line row is dl_start from addrbook number
414 * adrbk_num_start.
416 else if(op == ArbitraryStartingPoint){
417 AddrScrn_Disp dl;
419 initialize_dlc_cache();
420 n_cached++;
421 dlc = &cache_array[0];
423 * Save this in case fill_in_dl_field needs to free the text
424 * it points to.
426 dl = dlc->dl;
427 *dlc = *dlc_start;
428 dlc->dl = dl;
429 dlc->global_row = row;
431 index_of_low = 0;
432 valid_low = row;
433 valid_high = row;
435 else if(op == DoneWithCache){
437 n_cached = 0;
438 if(cache_array)
439 free_cache_array(&cache_array, size_of_cache);
442 return(dlc);
444 panic_abook_corrupt:
445 q_status_message(SM_ORDER | SM_DING, 5, 10,
446 _("Addrbook changed unexpectedly, re-syncing..."));
447 dprint((1,
448 _("addrbook changed while we had it open?, re-sync\n")));
449 dprint((2,
450 "valid_low=%ld valid_high=%ld index_of_low=%d size_of_cache=%d\n",
451 valid_low, valid_high, index_of_low, size_of_cache));
452 dprint((2,
453 "n_cached=%d new_index=%d known_index=%d next_index=%d\n",
454 n_cached, new_index, known_index, next_index));
455 dprint((2,
456 "next_row=%ld prev_row=%ld row=%ld\n", next_row, prev_row, row));
457 /* jump back to a safe starting point */
458 longjmp(addrbook_changed_unexpectedly, 1);
459 /*NOTREACHED*/
463 void
464 free_cache_array(DL_CACHE_S **c_array, int size)
466 DL_CACHE_S *dlc;
467 int i;
469 for(i = 0; i < size; i++){
470 dlc = &(*c_array)[i];
471 /* free any allocated space */
472 switch(dlc->dl.type){
473 case Text:
474 case Title:
475 case TitleCmb:
476 case AskServer:
477 if(dlc->dl.usst)
478 fs_give((void **)&dlc->dl.usst);
480 break;
481 default:
482 break;
486 fs_give((void **)c_array);
491 * Get the dlc element that comes before "old". The function that calls this
492 * function is the one that keeps a cache and checks in the cache before
493 * calling here. New is a passed in pointer to a buffer where we fill in
494 * the answer.
496 DL_CACHE_S *
497 dlc_prev(DL_CACHE_S *old, DL_CACHE_S *new)
499 PerAddrBook *pab;
500 AdrBk_Entry *abe;
501 adrbk_cntr_t list_count;
503 new->adrbk_num = -2;
504 new->dlcelnum = NO_NEXT;
505 new->dlcoffset = NO_NEXT;
506 new->type = DlcNotSet;
507 pab = &as.adrbks[old->adrbk_num];
509 switch(old->type){
510 case DlcTitle:
511 case DlcTitleNoPerm:
512 if(old->adrbk_num == 0 && as.config && as.how_many_personals == 0)
513 new->type = DlcGlobDelim2;
514 else if(old->adrbk_num == 0)
515 new->type = DlcOneBeforeBeginning;
516 else if(old->adrbk_num == as.how_many_personals)
517 new->type = DlcGlobDelim2;
518 else
519 new->type = DlcTitleBlankTop;
521 break;
523 case DlcSubTitle:
524 if(pab->access == NoAccess)
525 new->type = DlcTitleNoPerm;
526 else
527 new->type = DlcTitle;
529 break;
531 case DlcTitleBlankTop:
532 new->adrbk_num = old->adrbk_num - 1;
533 new->type = DlcSubTitle;
534 break;
536 case DlcEmpty:
537 case DlcZoomEmpty:
538 case DlcNoPermission:
539 case DlcNoAbooks:
540 if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
541 if(as.n_addrbk == 1 && as.n_serv == 0)
542 new->type = DlcOneBeforeBeginning;
543 else
544 new->type = DlcTitleBlankBottomCmb;
546 else
547 new->type = DlcOneBeforeBeginning;
549 break;
551 case DlcSimple:
553 adrbk_cntr_t el;
554 long i;
556 i = old->dlcelnum;
557 i--;
558 el = old->dlcelnum - 1;
559 while(i >= 0L){
560 if(!as.zoomed || entry_is_selected(pab->address_book->selects,
561 (a_c_arg_t)el))
562 break;
564 el--;
565 i--;
568 if(i >= 0){
569 new->dlcelnum = el;
570 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
571 if(abe && abe->tag == Single)
572 new->type = DlcSimple;
573 else if(abe && abe->tag == List)
574 new->type = DlcListBlankBottom;
576 else if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
577 if(as.n_addrbk == 1 && as.n_serv == 0)
578 new->type = DlcOneBeforeBeginning;
579 else
580 new->type = DlcTitleBlankBottomCmb;
582 else
583 new->type = DlcOneBeforeBeginning;
586 break;
588 case DlcListHead:
590 adrbk_cntr_t el;
591 long i;
593 i = old->dlcelnum;
594 i--;
595 el = old->dlcelnum - 1;
596 while(i >= 0L){
597 if(!as.zoomed || entry_is_selected(pab->address_book->selects,
598 (a_c_arg_t)el))
599 break;
601 el--;
602 i--;
605 if(i >= 0){
606 new->dlcelnum = el;
607 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
608 if(abe && abe->tag == Single){
609 new->type = DlcListBlankTop;
610 new->dlcelnum = old->dlcelnum;
612 else if(abe && abe->tag == List)
613 new->type = DlcListBlankBottom;
615 else if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
616 if(as.n_addrbk == 1 && as.n_serv == 0)
617 new->type = DlcOneBeforeBeginning;
618 else
619 new->type = DlcTitleBlankBottomCmb;
621 else
622 new->type = DlcOneBeforeBeginning;
625 break;
627 case DlcListEnt:
628 if(old->dlcoffset > 0){
629 new->type = DlcListEnt;
630 new->dlcelnum = old->dlcelnum;
631 new->dlcoffset = old->dlcoffset - 1;
633 else{
634 new->type = DlcListHead;
635 new->dlcelnum = old->dlcelnum;
638 break;
640 case DlcListClickHere:
641 case DlcListEmpty:
642 new->type = DlcListHead;
643 new->dlcelnum = old->dlcelnum;
644 break;
646 case DlcListBlankTop: /* can only occur between a Simple and a List */
647 new->type = DlcSimple;
649 adrbk_cntr_t el;
650 long i;
652 i = old->dlcelnum;
653 i--;
654 el = old->dlcelnum - 1;
655 while(i >= 0L){
656 if(!as.zoomed || entry_is_selected(pab->address_book->selects,
657 (a_c_arg_t)el))
658 break;
660 el--;
661 i--;
664 if(i >= 0)
665 new->dlcelnum = el;
666 else{
667 dprint((1, "Bug in addrbook: case ListBlankTop with no selected entry\n"));
668 goto oops;
672 break;
674 case DlcListBlankBottom:
675 new->dlcelnum = old->dlcelnum;
676 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
677 if(F_ON(F_EXPANDED_DISTLISTS,ps_global)
678 || exp_is_expanded(pab->address_book->exp, (a_c_arg_t)new->dlcelnum)){
679 list_count = listmem_count_from_abe(abe);
680 if(list_count == 0)
681 new->type = DlcListEmpty;
682 else{
683 new->type = DlcListEnt;
684 new->dlcoffset = list_count - 1;
687 else
688 new->type = DlcListClickHere;
690 break;
692 case DlcGlobDelim1:
693 if(as.how_many_personals == 0)
694 new->type = DlcPersAdd;
695 else{
696 new->adrbk_num = as.how_many_personals - 1;
697 new->type = DlcSubTitle;
699 break;
701 case DlcGlobDelim2:
702 new->type = DlcGlobDelim1;
703 break;
705 case DlcPersAdd:
706 new->type = DlcOneBeforeBeginning;
707 break;
709 case DlcGlobAdd:
710 new->type = DlcGlobDelim2;
711 break;
713 case DlcDirAccess:
714 if(old->adrbk_num == 0 && as.n_addrbk == 0)
715 new->type = DlcOneBeforeBeginning;
716 else if(old->adrbk_num == 0)
717 new->type = DlcDirDelim2;
718 else
719 new->type = DlcDirBlankTop;
721 break;
723 case DlcDirSubTitle:
724 new->type = DlcDirAccess;
725 break;
727 case DlcDirBlankTop:
728 new->adrbk_num = old->adrbk_num -1;
729 new->type = DlcDirSubTitle;
730 break;
732 case DlcDirDelim1:
733 new->adrbk_num = as.n_addrbk - 1;
734 if(as.n_addrbk == 0)
735 new->type = DlcNoAbooks;
736 else
737 new = get_bottom_dl_of_adrbk(new->adrbk_num, new);
739 break;
741 case DlcDirDelim1a:
742 new->type = DlcDirDelim1;
743 break;
745 case DlcDirDelim1b:
746 new->type = DlcDirDelim1a;
747 break;
749 case DlcDirDelim1c:
750 new->type = DlcDirDelim1b;
751 break;
753 case DlcDirDelim2:
754 if(F_ON(F_CMBND_ABOOK_DISP,ps_global))
755 new->type = DlcDirDelim1c;
756 else
757 new->type = DlcDirDelim1;
759 break;
761 case DlcTitleDashTopCmb:
762 if(old->adrbk_num == 0)
763 new->type = DlcOneBeforeBeginning;
764 else
765 new->type = DlcTitleBlankTopCmb;
767 break;
769 case DlcTitleCmb:
770 new->type = DlcTitleDashTopCmb;
771 break;
773 case DlcTitleDashBottomCmb:
774 new->type = DlcTitleCmb;
775 break;
777 case DlcTitleBlankBottomCmb:
778 new->type = DlcTitleDashBottomCmb;
779 break;
781 case DlcClickHereCmb:
782 if(as.n_addrbk == 1 && as.n_serv == 0)
783 new->type = DlcOneBeforeBeginning;
784 else
785 new->type = DlcTitleBlankBottomCmb;
787 break;
789 case DlcTitleBlankTopCmb:
790 new->adrbk_num = old->adrbk_num - 1;
791 new = get_bottom_dl_of_adrbk(new->adrbk_num, new);
792 break;
794 case DlcBeginning:
795 case DlcTwoBeforeBeginning:
796 new->type = DlcBeginning;
797 break;
799 case DlcOneBeforeBeginning:
800 new->type = DlcTwoBeforeBeginning;
801 break;
803 oops:
804 default:
805 q_status_message(SM_ORDER | SM_DING, 5, 10,
806 _("Bug in addrbook, not supposed to happen, re-syncing..."));
807 dprint((1, "Bug in addrbook, impossible case (%d) in dlc_prev, re-sync\n",
808 old->type));
809 /* jump back to a safe starting point */
810 longjmp(addrbook_changed_unexpectedly, 1);
811 /*NOTREACHED*/
814 new->global_row = old->global_row - 1L;
815 if(new->adrbk_num == -2)
816 new->adrbk_num = old->adrbk_num;
818 return(new);
823 * Get the dlc element that comes after "old". The function that calls this
824 * function is the one that keeps a cache and checks in the cache before
825 * calling here.
827 DL_CACHE_S *
828 dlc_next(DL_CACHE_S *old, DL_CACHE_S *new)
830 PerAddrBook *pab;
831 AdrBk_Entry *abe;
832 adrbk_cntr_t ab_count;
833 adrbk_cntr_t list_count;
835 new->adrbk_num = -2;
836 new->dlcelnum = NO_NEXT;
837 new->dlcoffset = NO_NEXT;
838 new->type = DlcNotSet;
839 pab = &as.adrbks[old->adrbk_num];
841 switch(old->type){
842 case DlcTitle:
843 case DlcTitleNoPerm:
844 new->type = DlcSubTitle;
845 break;
847 case DlcSubTitle:
848 if((old->adrbk_num == as.how_many_personals - 1) &&
849 (as.config || as.n_addrbk > as.how_many_personals))
850 new->type = DlcGlobDelim1;
851 else if(as.n_serv && !as.config &&
852 (old->adrbk_num == as.n_addrbk - 1))
853 new->type = DlcDirDelim1;
854 else if(old->adrbk_num == as.n_addrbk - 1)
855 new->type = DlcEnd;
856 else{
857 new->adrbk_num = old->adrbk_num + 1;
858 new->type = DlcTitleBlankTop;
861 break;
863 case DlcTitleBlankTop:
864 if(pab->access == NoAccess)
865 new->type = DlcTitleNoPerm;
866 else
867 new->type = DlcTitle;
869 break;
871 case DlcEmpty:
872 case DlcZoomEmpty:
873 case DlcNoPermission:
874 case DlcClickHereCmb:
875 if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
876 if(old->adrbk_num < as.n_addrbk - 1){
877 new->adrbk_num = old->adrbk_num + 1;
878 new->type = DlcTitleBlankTopCmb;
880 else if(as.n_serv)
881 new->type = DlcDirDelim1;
882 else
883 new->type = DlcEnd;
885 else
886 new->type = DlcEnd;
888 break;
890 case DlcNoAbooks:
891 case DlcGlobAdd:
892 case DlcEnd:
893 new->type = DlcEnd;
894 break;
896 case DlcSimple:
898 adrbk_cntr_t el;
899 long i;
901 ab_count = adrbk_count(pab->address_book);
902 i = old->dlcelnum;
903 i++;
904 el = old->dlcelnum + 1;
905 while(i < ab_count){
906 if(!as.zoomed || entry_is_selected(pab->address_book->selects,
907 (a_c_arg_t)el))
908 break;
910 el++;
911 i++;
914 if(i < ab_count){
915 new->dlcelnum = el;
916 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
917 if(abe && abe->tag == Single)
918 new->type = DlcSimple;
919 else if(abe && abe->tag == List)
920 new->type = DlcListBlankTop;
922 else if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
923 if(old->adrbk_num < as.n_addrbk - 1){
924 new->adrbk_num = old->adrbk_num + 1;
925 new->type = DlcTitleBlankTopCmb;
927 else if(as.n_serv)
928 new->type = DlcDirDelim1;
929 else
930 new->type = DlcEnd;
932 else
933 new->type = DlcEnd;
936 break;
938 case DlcListHead:
939 new->dlcelnum = old->dlcelnum;
940 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
941 if(F_ON(F_EXPANDED_DISTLISTS,ps_global)
942 || exp_is_expanded(pab->address_book->exp, (a_c_arg_t)new->dlcelnum)){
943 list_count = listmem_count_from_abe(abe);
944 if(list_count == 0)
945 new->type = DlcListEmpty;
946 else{
947 new->type = DlcListEnt;
948 new->dlcoffset = 0;
951 else
952 new->type = DlcListClickHere;
954 break;
956 case DlcListEnt:
957 new->dlcelnum = old->dlcelnum;
958 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
959 list_count = listmem_count_from_abe(abe);
960 if(old->dlcoffset == list_count - 1){ /* last member of list */
961 adrbk_cntr_t el;
962 long i;
964 ab_count = adrbk_count(pab->address_book);
965 i = old->dlcelnum;
966 i++;
967 el = old->dlcelnum + 1;
968 while(i < ab_count){
969 if(!as.zoomed || entry_is_selected(pab->address_book->selects,
970 (a_c_arg_t)el))
971 break;
973 el++;
974 i++;
977 if(i < ab_count)
978 new->type = DlcListBlankBottom;
979 else if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
980 if(old->adrbk_num < as.n_addrbk - 1){
981 new->adrbk_num = old->adrbk_num + 1;
982 new->type = DlcTitleBlankTopCmb;
984 else if(as.n_serv)
985 new->type = DlcDirDelim1;
986 else
987 new->type = DlcEnd;
989 else
990 new->type = DlcEnd;
992 else{
993 new->type = DlcListEnt;
994 new->dlcoffset = old->dlcoffset + 1;
997 break;
999 case DlcListClickHere:
1000 case DlcListEmpty:
1002 adrbk_cntr_t el;
1003 long i;
1005 new->dlcelnum = old->dlcelnum;
1006 ab_count = adrbk_count(pab->address_book);
1007 i = old->dlcelnum;
1008 i++;
1009 el = old->dlcelnum + 1;
1010 while(i < ab_count){
1011 if(!as.zoomed || entry_is_selected(pab->address_book->selects,
1012 (a_c_arg_t)el))
1013 break;
1015 el++;
1016 i++;
1019 if(i < ab_count)
1020 new->type = DlcListBlankBottom;
1021 else if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
1022 if(old->adrbk_num < as.n_addrbk - 1){
1023 new->adrbk_num = old->adrbk_num + 1;
1024 new->type = DlcTitleBlankTopCmb;
1026 else if(as.n_serv)
1027 new->type = DlcDirDelim1;
1028 else
1029 new->type = DlcEnd;
1031 else
1032 new->type = DlcEnd;
1035 break;
1037 case DlcListBlankTop:
1038 new->type = DlcListHead;
1039 new->dlcelnum = old->dlcelnum;
1040 break;
1042 case DlcListBlankBottom:
1044 adrbk_cntr_t el;
1045 long i;
1047 ab_count = adrbk_count(pab->address_book);
1048 i = old->dlcelnum;
1049 i++;
1050 el = old->dlcelnum + 1;
1051 while(i < ab_count){
1052 if(!as.zoomed || entry_is_selected(pab->address_book->selects,
1053 (a_c_arg_t)el))
1054 break;
1056 el++;
1057 i++;
1060 if(i < ab_count){
1061 new->dlcelnum = el;
1062 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
1063 if(abe && abe->tag == Single)
1064 new->type = DlcSimple;
1065 else if(abe && abe->tag == List)
1066 new->type = DlcListHead;
1068 else
1069 /* can't happen */
1070 new->type = DlcEnd;
1073 break;
1075 case DlcGlobDelim1:
1076 new->type = DlcGlobDelim2;
1077 break;
1079 case DlcGlobDelim2:
1080 if(as.config && as.how_many_personals == as.n_addrbk)
1081 new->type = DlcGlobAdd;
1082 else{
1083 new->adrbk_num = as.how_many_personals;
1084 pab = &as.adrbks[new->adrbk_num];
1085 if(pab->access == NoAccess)
1086 new->type = DlcTitleNoPerm;
1087 else
1088 new->type = DlcTitle;
1091 break;
1093 case DlcDirDelim1:
1094 if(F_ON(F_CMBND_ABOOK_DISP,ps_global))
1095 new->type = DlcDirDelim1a;
1096 else
1097 new->type = DlcDirDelim2;
1099 new->adrbk_num = 0;
1100 break;
1102 case DlcDirDelim1a:
1103 new->type = DlcDirDelim1b;
1104 break;
1106 case DlcDirDelim1b:
1107 new->type = DlcDirDelim1c;
1108 break;
1110 case DlcDirDelim1c:
1111 new->type = DlcDirDelim2;
1112 new->adrbk_num = 0;
1113 break;
1115 case DlcDirDelim2:
1116 new->type = DlcDirAccess;
1117 new->adrbk_num = 0;
1118 break;
1120 case DlcPersAdd:
1121 new->type = DlcGlobDelim1;
1122 break;
1124 case DlcDirAccess:
1125 new->type = DlcDirSubTitle;
1126 break;
1128 case DlcDirSubTitle:
1129 if(old->adrbk_num == as.n_serv - 1)
1130 new->type = DlcEnd;
1131 else{
1132 new->type = DlcDirBlankTop;
1133 new->adrbk_num = old->adrbk_num + 1;
1136 break;
1138 case DlcDirBlankTop:
1139 new->type = DlcDirAccess;
1140 break;
1142 case DlcTitleDashTopCmb:
1143 new->type = DlcTitleCmb;
1144 break;
1146 case DlcTitleCmb:
1147 new->type = DlcTitleDashBottomCmb;
1148 break;
1150 case DlcTitleDashBottomCmb:
1151 new->type = DlcTitleBlankBottomCmb;
1152 break;
1154 case DlcTitleBlankBottomCmb:
1155 pab = &as.adrbks[old->adrbk_num];
1156 if(pab->ostatus != Open && pab->access != NoAccess)
1157 new->type = DlcClickHereCmb;
1158 else
1159 new = get_top_dl_of_adrbk(old->adrbk_num, new);
1161 break;
1163 case DlcTitleBlankTopCmb:
1164 new->type = DlcTitleDashTopCmb;
1165 break;
1167 case DlcOneBeforeBeginning:
1168 new = get_global_top_dlc(new);
1169 break;
1171 case DlcTwoBeforeBeginning:
1172 new->type = DlcOneBeforeBeginning;
1173 break;
1175 default:
1176 q_status_message(SM_ORDER | SM_DING, 5, 10,
1177 _("Bug in addrbook, not supposed to happen, re-syncing..."));
1178 dprint((1, "Bug in addrbook, impossible case (%d) in dlc_next, re-sync\n",
1179 old->type));
1180 /* jump back to a safe starting point */
1181 longjmp(addrbook_changed_unexpectedly, 1);
1182 /*NOTREACHED*/
1185 new->global_row = old->global_row + 1L;
1186 if(new->adrbk_num == -2)
1187 new->adrbk_num = old->adrbk_num;
1189 return(new);
1194 * Get the display line at the very top of whole addrbook screen display.
1196 DL_CACHE_S *
1197 get_global_top_dlc(DL_CACHE_S *new)
1198 /* fill in answer here */
1200 PerAddrBook *pab;
1202 new->dlcelnum = NO_NEXT;
1203 new->dlcoffset = NO_NEXT;
1204 new->type = DlcNotSet;
1206 if(F_ON(F_CMBND_ABOOK_DISP,ps_global) && !as.config){
1207 new->adrbk_num = 0;
1208 if(as.n_addrbk > 1 || (as.n_serv && as.n_addrbk == 1))
1209 new->type = DlcTitleDashTopCmb;
1210 else if(as.n_addrbk == 1)
1211 new = get_top_dl_of_adrbk(new->adrbk_num, new);
1212 else if(as.n_serv > 0){ /* 1st directory */
1213 new->type = DlcDirAccess;
1214 new->adrbk_num = 0;
1216 else
1217 new->type = DlcNoAbooks;
1219 else{
1220 new->adrbk_num = 0;
1222 if(as.config){
1223 if(as.how_many_personals == 0) /* no personals */
1224 new->type = DlcPersAdd;
1225 else{
1226 pab = &as.adrbks[new->adrbk_num]; /* 1st personal */
1227 if(pab->access == NoAccess)
1228 new->type = DlcTitleNoPerm;
1229 else
1230 new->type = DlcTitle;
1233 else if(any_ab_open()){
1234 new->adrbk_num = as.cur;
1235 new = get_top_dl_of_adrbk(new->adrbk_num, new);
1237 else if(as.n_addrbk > 0){ /* 1st addrbook */
1238 pab = &as.adrbks[new->adrbk_num];
1239 if(pab->access == NoAccess)
1240 new->type = DlcTitleNoPerm;
1241 else
1242 new->type = DlcTitle;
1244 else if(as.n_serv > 0){ /* 1st directory */
1245 new->type = DlcDirAccess;
1246 new->adrbk_num = 0;
1248 else
1249 new->type = DlcNoAbooks;
1252 return(new);
1257 * Get the last display line for the whole address book screen.
1258 * This gives us a way to start at the end and move back up.
1260 DL_CACHE_S *
1261 get_global_bottom_dlc(DL_CACHE_S *new)
1262 /* fill in answer here */
1264 new->dlcelnum = NO_NEXT;
1265 new->dlcoffset = NO_NEXT;
1266 new->type = DlcNotSet;
1268 if(F_ON(F_CMBND_ABOOK_DISP,ps_global) && !as.config){
1269 if(as.n_serv){
1270 new->type = DlcDirSubTitle;
1271 new->adrbk_num = as.n_serv - 1;
1273 else if(as.n_addrbk > 0){
1274 new->adrbk_num = as.n_addrbk - 1;
1275 new = get_bottom_dl_of_adrbk(new->adrbk_num, new);
1277 else
1278 new->type = DlcNoAbooks;
1280 else{
1281 new->adrbk_num = MAX(as.n_addrbk - 1, 0);
1283 if(as.config){
1284 if(as.how_many_personals == as.n_addrbk) /* no globals */
1285 new->type = DlcGlobAdd;
1286 else
1287 new->type = DlcSubTitle;
1289 else if(any_ab_open()){
1290 new->adrbk_num = as.cur;
1291 new = get_bottom_dl_of_adrbk(new->adrbk_num, new);
1293 else if(as.n_serv){
1294 new->type = DlcDirSubTitle;
1295 new->adrbk_num = as.n_serv - 1;
1297 else{ /* !config && !opened && !n_serv */
1298 if(as.n_addrbk)
1299 new->type = DlcSubTitle;
1300 else
1301 new->type = DlcNoAbooks;
1305 return(new);
1310 * First dl in a particular addrbook, not counting title lines.
1311 * Assumes as.opened.
1313 DL_CACHE_S *
1314 get_top_dl_of_adrbk(int adrbk_num, DL_CACHE_S *new)
1316 /* fill in answer here */
1318 PerAddrBook *pab;
1319 AdrBk_Entry *abe;
1320 adrbk_cntr_t ab_count;
1322 pab = &as.adrbks[adrbk_num];
1323 new->adrbk_num = adrbk_num;
1325 if(pab->access == NoAccess)
1326 new->type = DlcNoPermission;
1327 else{
1328 adrbk_cntr_t el;
1329 long i;
1331 ab_count = adrbk_count(pab->address_book);
1333 i = 0L;
1334 el = 0;
1335 /* find first displayed entry */
1336 while(i < ab_count){
1337 if(!as.zoomed || entry_is_selected(pab->address_book->selects,
1338 (a_c_arg_t)el))
1339 break;
1341 el++;
1342 i++;
1345 if(i < ab_count){
1346 new->dlcelnum = el;
1347 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
1348 if(abe && abe->tag == Single)
1349 new->type = DlcSimple;
1350 else if(abe && abe->tag == List)
1351 new->type = DlcListHead;
1353 else if(as.zoomed)
1354 new->type = DlcZoomEmpty;
1355 else
1356 new->type = DlcEmpty;
1359 return(new);
1364 * Find the last display line for addrbook number adrbk_num.
1365 * Assumes as.opened (unless OLD_ABOOK_DISP).
1367 DL_CACHE_S *
1368 get_bottom_dl_of_adrbk(int adrbk_num, DL_CACHE_S *new)
1370 /* fill in answer here */
1372 PerAddrBook *pab;
1373 AdrBk_Entry *abe;
1374 adrbk_cntr_t ab_count;
1375 adrbk_cntr_t list_count;
1377 pab = &as.adrbks[adrbk_num];
1378 new->adrbk_num = adrbk_num;
1380 if(F_ON(F_CMBND_ABOOK_DISP,ps_global) && pab->ostatus != Open){
1381 if(pab->access == NoAccess)
1382 new->type = DlcNoPermission;
1383 else
1384 new->type = DlcClickHereCmb;
1386 else if(F_OFF(F_CMBND_ABOOK_DISP,ps_global) && pab->ostatus != Open){
1387 new->type = DlcSubTitle;
1389 else{
1390 if(pab->access == NoAccess)
1391 new->type = DlcNoPermission;
1392 else{
1393 adrbk_cntr_t el;
1394 long i;
1396 ab_count = adrbk_count(pab->address_book);
1397 i = ab_count - 1;
1398 el = ab_count - 1;
1399 /* find last displayed entry */
1400 while(i >= 0L){
1401 if(!as.zoomed || entry_is_selected(pab->address_book->selects,
1402 (a_c_arg_t)el))
1403 break;
1405 el--;
1406 i--;
1409 if(i >= 0){
1410 new->dlcelnum = el;
1411 abe = adrbk_get_ae(pab->address_book, (a_c_arg_t)new->dlcelnum);
1412 if(abe && abe->tag == Single)
1413 new->type = DlcSimple;
1414 else if(abe && abe->tag == List){
1415 if(F_ON(F_EXPANDED_DISTLISTS,ps_global)
1416 || exp_is_expanded(pab->address_book->exp,
1417 (a_c_arg_t)new->dlcelnum)){
1418 list_count = listmem_count_from_abe(abe);
1419 if(list_count == 0)
1420 new->type = DlcListEmpty;
1421 else{
1422 new->type = DlcListEnt;
1423 new->dlcoffset = list_count - 1;
1426 else
1427 new->type = DlcListClickHere;
1430 else if(as.zoomed)
1431 new->type = DlcZoomEmpty;
1432 else
1433 new->type = DlcEmpty;
1438 return(new);
1443 * This returns the actual dlc instead of the dl within the dlc.
1445 DL_CACHE_S *
1446 get_dlc(long int row)
1448 dprint((11, "- get_dlc(%ld) -\n", row));
1450 return(dlc_mgr(row, Lookup, (DL_CACHE_S *)NULL));
1455 * Move to new_dlc and give it row number row_number_to_assign_it.
1456 * We copy the passed in dlc in case the caller passed us a pointer into
1457 * the cache.
1459 void
1460 warp_to_dlc(DL_CACHE_S *new_dlc, long int row_number_to_assign_it)
1462 DL_CACHE_S dlc;
1464 dprint((9, "- warp_to_dlc(%ld) -\n", row_number_to_assign_it));
1466 dlc = *new_dlc;
1469 * Make sure we can move forward and backward from these
1470 * types that we may wish to warp to. The caller may not have known
1471 * to set adrbk_num for these types.
1473 switch(dlc.type){
1474 case DlcPersAdd:
1475 dlc.adrbk_num = 0;
1476 break;
1477 case DlcGlobAdd:
1478 dlc.adrbk_num = as.n_addrbk;
1479 break;
1480 default:
1481 break;
1484 (void)dlc_mgr(row_number_to_assign_it, ArbitraryStartingPoint, &dlc);
1489 * Move to first dlc and give it row number 0.
1491 void
1492 warp_to_beginning(void)
1494 dprint((9, "- warp_to_beginning -\n"));
1496 (void)dlc_mgr(0L, FirstEntry, (DL_CACHE_S *)NULL);
1501 * Move to first dlc in abook_num and give it row number 0.
1503 void
1504 warp_to_top_of_abook(int abook_num)
1506 DL_CACHE_S dlc;
1508 dprint((9, "- warp_to_top_of_abook(%d) -\n", abook_num));
1510 (void)get_top_dl_of_adrbk(abook_num, &dlc);
1511 warp_to_dlc(&dlc, 0L);
1516 * Move to last dlc and give it row number 0.
1518 void
1519 warp_to_end(void)
1521 dprint((9, "- warp_to_end -\n"));
1523 (void)dlc_mgr(0L, LastEntry, (DL_CACHE_S *)NULL);
1528 * This flushes all of the cache that is related to this dlc.
1530 void
1531 flush_dlc_from_cache(DL_CACHE_S *dlc_to_flush)
1533 dprint((11, "- flush_dlc_from_cache -\n"));
1535 (void)dlc_mgr(NO_LINE, FlushDlcFromCache, dlc_to_flush);
1540 * Returns 1 if the dlc's match, 0 otherwise.
1543 matching_dlcs(DL_CACHE_S *dlc1, DL_CACHE_S *dlc2)
1545 if(!dlc1 || !dlc2 ||
1546 dlc1->type != dlc2->type ||
1547 dlc1->adrbk_num != dlc2->adrbk_num)
1548 return 0;
1550 switch(dlc1->type){
1552 case DlcSimple:
1553 case DlcListHead:
1554 case DlcListBlankTop:
1555 case DlcListBlankBottom:
1556 case DlcListClickHere:
1557 case DlcListEmpty:
1558 return(dlc1->dlcelnum == dlc2->dlcelnum);
1560 case DlcListEnt:
1561 return(dlc1->dlcelnum == dlc2->dlcelnum &&
1562 dlc1->dlcoffset == dlc2->dlcoffset);
1564 case DlcTitle:
1565 case DlcSubTitle:
1566 case DlcTitleNoPerm:
1567 case DlcTitleBlankTop:
1568 case DlcEmpty:
1569 case DlcZoomEmpty:
1570 case DlcNoPermission:
1571 case DlcPersAdd:
1572 case DlcGlobAdd:
1573 case DlcGlobDelim1:
1574 case DlcGlobDelim2:
1575 case DlcDirAccess:
1576 case DlcDirDelim1:
1577 case DlcDirDelim2:
1578 case DlcTitleDashTopCmb:
1579 case DlcTitleCmb:
1580 case DlcTitleDashBottomCmb:
1581 case DlcTitleBlankBottomCmb:
1582 case DlcClickHereCmb:
1583 case DlcTitleBlankTopCmb:
1584 return 1;
1586 case DlcNotSet:
1587 case DlcBeginning:
1588 case DlcOneBeforeBeginning:
1589 case DlcTwoBeforeBeginning:
1590 case DlcEnd:
1591 case DlcNoAbooks:
1592 return 0;
1594 default:
1595 break;
1597 /*NOTREACHED*/
1599 return 0;
1604 * Uses information in new to fill in new->dl.
1606 void
1607 fill_in_dl_field(DL_CACHE_S *new)
1609 AddrScrn_Disp *dl;
1610 PerAddrBook *pab;
1611 char buf[6*MAX_SCREEN_COLS + 1];
1612 char buf2[6*1024];
1613 char hostbuf[128];
1614 char *folder;
1615 char *q;
1616 unsigned screen_width = ps_global->ttyo->screen_cols;
1617 unsigned got_width, need_width, cellwidth;
1619 screen_width = MIN(MAX_SCREEN_COLS, screen_width);
1621 dl = &(new->dl);
1623 /* free any previously allocated space */
1624 switch(dl->type){
1625 case Text:
1626 case Title:
1627 case TitleCmb:
1628 case AskServer:
1629 if(dl->usst)
1630 fs_give((void **)&dl->usst);
1631 default:
1632 break;
1635 /* set up new dl */
1636 switch(new->type){
1637 case DlcListBlankTop:
1638 case DlcListBlankBottom:
1639 case DlcGlobDelim1:
1640 case DlcGlobDelim2:
1641 case DlcDirDelim1:
1642 case DlcDirDelim2:
1643 case DlcTitleBlankTop:
1644 case DlcDirBlankTop:
1645 case DlcTitleBlankBottomCmb:
1646 case DlcTitleBlankTopCmb:
1647 dl->type = Text;
1648 dl->usst = cpystr("");
1649 break;
1651 case DlcTitleDashTopCmb:
1652 case DlcTitleDashBottomCmb:
1653 case DlcDirDelim1a:
1654 case DlcDirDelim1c:
1655 /* line of dashes in txt field */
1656 dl->type = Text;
1657 memset((void *)buf, '-', screen_width * sizeof(char));
1658 buf[screen_width] = '\0';
1659 dl->usst = cpystr(buf);
1660 break;
1662 case DlcNoPermission:
1663 dl->type = Text;
1664 dl->usst = cpystr(NO_PERMISSION);
1665 break;
1667 case DlcTitle:
1668 case DlcTitleNoPerm:
1669 dl->type = Title;
1670 pab = &as.adrbks[new->adrbk_num];
1671 /* title for this addrbook */
1672 snprintf(buf, sizeof(buf), " %s", pab->abnick ? pab->abnick : pab->filename);
1673 cellwidth = utf8_width(buf);
1674 if(cellwidth > screen_width)
1675 cellwidth = utf8_truncate(buf, screen_width);
1677 /* add space padding */
1678 if(cellwidth < screen_width)
1679 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%*.*s",
1680 screen_width - cellwidth, screen_width - cellwidth, "");
1682 buf[sizeof(buf)-1] = '\0';
1684 if(as.ro_warning && (pab->access == NoAccess || pab->access == ReadOnly)){
1685 char *str;
1687 if(pab->access == NoAccess)
1688 str = NOACCESS;
1689 else
1690 str = READONLY;
1692 need_width = utf8_width(str);
1694 if(screen_width > need_width && (q = utf8_count_back_width(buf, buf+strlen(buf), need_width, &got_width)) != NULL)
1696 (void) utf8_pad_to_width(q, str, sizeof(buf)-(q-buf), need_width, 0);
1699 buf[sizeof(buf)-1] = '\0';
1700 dl->usst = cpystr(buf);
1701 break;
1703 case DlcSubTitle:
1704 dl->type = Text;
1705 pab = &as.adrbks[new->adrbk_num];
1706 /* Find a hostname to put in title */
1707 hostbuf[0] = '\0';
1708 folder = NULL;
1709 if(pab->type & REMOTE_VIA_IMAP && pab->filename){
1710 char *start, *end = NULL;
1712 start = strindex(pab->filename, '{');
1713 if(start)
1714 end = strindex(start+1, '}');
1716 if(start && end){
1717 strncpy(hostbuf, start + 1,
1718 MIN(end - start - 1, sizeof(hostbuf)-1));
1719 hostbuf[MIN(end - start - 1, sizeof(hostbuf)-1)] = '\0';
1720 if(*(end+1))
1721 folder = end+1;
1725 if(!folder)
1726 folder = pab->filename;
1729 * Just trying to find the name relative to the home directory
1730 * to display in an OS-independent way.
1732 if(folder && in_dir(ps_global->home_dir, folder)){
1733 char *p, *new_folder = NULL, *savep = NULL;
1734 int l, saveval = 0;
1736 l = strlen(ps_global->home_dir);
1738 while(!new_folder && (p = last_cmpnt(folder)) != NULL){
1739 if(savep){
1740 *savep = saveval;
1741 savep = NULL;
1744 if(folder + l == p || folder + l + 1 == p)
1745 new_folder = p;
1746 else{
1747 savep = --p;
1748 saveval = *savep;
1749 *savep = '\0';
1753 if(savep)
1754 *savep = saveval;
1756 if(new_folder)
1757 folder = new_folder;
1760 snprintf(buf, sizeof(buf), " %s AddressBook%s%s in %s",
1761 (pab->type & GLOBAL) ? "Global" : "Personal",
1762 (pab->type & REMOTE_VIA_IMAP && *hostbuf) ? " on " : "",
1763 (pab->type & REMOTE_VIA_IMAP && *hostbuf) ? hostbuf : "",
1764 (folder && *folder) ? folder : "<?>");
1765 cellwidth = utf8_width(buf);
1766 if(cellwidth > screen_width)
1767 cellwidth = utf8_truncate(buf, screen_width);
1769 /* add space padding */
1770 if(cellwidth < screen_width)
1771 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%*.*s",
1772 screen_width - cellwidth, screen_width - cellwidth, "");
1774 buf[sizeof(buf)-1] = '\0';
1775 dl->usst = cpystr(buf);
1776 break;
1778 case DlcTitleCmb:
1779 dl->type = TitleCmb;
1780 pab = &as.adrbks[new->adrbk_num];
1781 /* title for this addrbook */
1782 snprintf(buf, sizeof(buf), "%s AddressBook <%s>",
1783 (new->adrbk_num < as.how_many_personals) ?
1784 "Personal" :
1785 "Global",
1786 pab->abnick ? pab->abnick : pab->filename);
1787 cellwidth = utf8_width(buf);
1788 if(cellwidth > screen_width)
1789 cellwidth = utf8_truncate(buf, screen_width);
1791 /* add space padding */
1792 if(cellwidth < screen_width)
1793 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%*.*s",
1794 screen_width - cellwidth, screen_width - cellwidth, "");
1796 buf[sizeof(buf)-1] = '\0';
1798 if(as.ro_warning && (pab->access == NoAccess || pab->access == ReadOnly)){
1799 char *str;
1801 if(pab->access == NoAccess)
1802 str = NOACCESS;
1803 else
1804 str = READONLY;
1806 need_width = utf8_width(str);
1808 if(screen_width > need_width && (q = utf8_count_back_width(buf, buf+strlen(buf), need_width, &got_width)) != NULL)
1810 (void) utf8_pad_to_width(q, str, sizeof(buf)-(q-buf), need_width, 0);
1813 buf[sizeof(buf)-1] = '\0';
1814 dl->usst = cpystr(buf);
1815 break;
1817 case DlcDirDelim1b:
1818 dl->type = Text;
1819 utf8_snprintf(buf, sizeof(buf), "%-*.*w", screen_width, screen_width, OLDSTYLE_DIR_TITLE);
1820 dl->usst = cpystr(buf);
1821 break;
1823 case DlcClickHereCmb:
1824 dl->type = ClickHereCmb;
1825 break;
1827 case DlcListClickHere:
1828 dl->type = ListClickHere;
1829 dl->elnum = new->dlcelnum;
1830 break;
1832 case DlcListEmpty:
1833 dl->type = ListEmpty;
1834 dl->elnum = new->dlcelnum;
1835 break;
1837 case DlcEmpty:
1838 dl->type = Empty;
1839 break;
1841 case DlcZoomEmpty:
1842 dl->type = ZoomEmpty;
1843 break;
1845 case DlcNoAbooks:
1846 dl->type = NoAbooks;
1847 break;
1849 case DlcPersAdd:
1850 dl->type = AddFirstPers;
1851 break;
1853 case DlcGlobAdd:
1854 dl->type = AddFirstGlob;
1855 break;
1857 case DlcDirAccess:
1858 dl->type = AskServer;
1860 #ifdef ENABLE_LDAP
1862 LDAP_SERV_S *info;
1864 info = break_up_ldap_server(ps_global->VAR_LDAP_SERVERS[new->adrbk_num]);
1865 snprintf(buf2, sizeof(buf2), " %s",
1866 (info && info->nick && *info->nick) ? info->nick :
1867 (info && info->serv && *info->serv) ? info->serv :
1868 comatose(new->adrbk_num + 1));
1869 if(info)
1870 free_ldap_server_info(&info);
1872 #else
1873 snprintf(buf2, sizeof(buf2), " %s", comatose(new->adrbk_num + 1));
1874 #endif
1876 utf8_snprintf(buf, sizeof(buf), "%-*.*w", screen_width, screen_width, buf2);
1877 dl->usst = cpystr(buf);
1878 break;
1880 case DlcDirSubTitle:
1881 dl->type = Text;
1882 #ifdef ENABLE_LDAP
1884 LDAP_SERV_S *info;
1886 info = break_up_ldap_server(ps_global->VAR_LDAP_SERVERS[new->adrbk_num]);
1887 if(info && info->port >= 0)
1888 snprintf(buf2, sizeof(buf2), " Directory Server on %s:%d",
1889 (info && info->serv && *info->serv) ? info->serv : "<?>",
1890 info->port);
1891 else
1892 snprintf(buf2, sizeof(buf2), " Directory Server on %s",
1893 (info && info->serv && *info->serv) ? info->serv : "<?>");
1895 if(info)
1896 free_ldap_server_info(&info);
1898 #else
1899 snprintf(buf2, sizeof(buf2), " Directory Server %s",
1900 comatose(new->adrbk_num + 1));
1901 #endif
1903 utf8_snprintf(buf, sizeof(buf), "%-*.*w", screen_width, screen_width, buf2);
1904 dl->usst = cpystr(buf);
1905 break;
1907 case DlcSimple:
1908 dl->type = Simple;
1909 dl->elnum = new->dlcelnum;
1910 break;
1912 case DlcListHead:
1913 dl->type = ListHead;
1914 dl->elnum = new->dlcelnum;
1915 break;
1917 case DlcListEnt:
1918 dl->type = ListEnt;
1919 dl->elnum = new->dlcelnum;
1920 dl->l_offset = new->dlcoffset;
1921 break;
1923 case DlcBeginning:
1924 case DlcOneBeforeBeginning:
1925 case DlcTwoBeforeBeginning:
1926 dl->type = Beginning;
1927 break;
1929 case DlcEnd:
1930 dl->type = End;
1931 break;
1933 default:
1934 q_status_message(SM_ORDER | SM_DING, 5, 10,
1935 _("Bug in addrbook, not supposed to happen, re-syncing..."));
1936 dprint((1, "Bug in addrbook, impossible dflt in fill_in_dl (%d)\n",
1937 new->type));
1938 /* jump back to a safe starting point */
1939 longjmp(addrbook_changed_unexpectedly, 1);
1940 /*NOTREACHED*/
1946 * Returns a pointer to the member_number'th list member of the list
1947 * associated with this display line.
1949 char *
1950 listmem(long int row)
1952 PerAddrBook *pab;
1953 AddrScrn_Disp *dl;
1955 dl = dlist(row);
1956 if(dl->type != ListEnt)
1957 return((char *)NULL);
1959 pab = &as.adrbks[adrbk_num_from_lineno(row)];
1961 return(listmem_from_dl(pab->address_book, dl));
1966 * Returns a pointer to the list member
1967 * associated with this display line.
1969 char *
1970 listmem_from_dl(AdrBk *address_book, AddrScrn_Disp *dl)
1972 AdrBk_Entry *abe;
1973 char **p = (char **)NULL;
1975 /* This shouldn't happen */
1976 if(dl->type != ListEnt)
1977 return((char *)NULL);
1979 abe = adrbk_get_ae(address_book, (a_c_arg_t) dl->elnum);
1982 * If we wanted to be more careful, We'd go through the list making sure
1983 * we don't pass the end. We'll count on the caller being careful
1984 * instead.
1986 if(abe && abe->tag == List){
1987 p = abe->addr.list;
1988 p += dl->l_offset;
1991 return((p && *p) ? *p : (char *)NULL);
1996 * How many members in list?
1998 adrbk_cntr_t
1999 listmem_count_from_abe(AdrBk_Entry *abe)
2001 char **p;
2003 if(abe->tag != List)
2004 return 0;
2006 for(p = abe->addr.list; p != NULL && *p != NULL; p++)
2007 ;/* do nothing */
2009 return((adrbk_cntr_t)(p - abe->addr.list));
2014 * Return a ptr to the row'th line of the global disp_list.
2015 * Line numbers count up but you can't count on knowing which line number
2016 * goes with the first or the last row. That is, row 0 is not necessarily
2017 * special. It could be before the rows that make up the display list, after
2018 * them, or anywhere in between. You can't tell what the last row is
2019 * numbered, but a dl with type End is returned when you get past the end.
2020 * You can't tell what the number of the first row is, but if you go past
2021 * the first row a dl of type Beginning will be returned. Row numbers can
2022 * be positive or negative. Their values have no meaning other than how
2023 * they line up relative to other row numbers.
2025 AddrScrn_Disp *
2026 dlist(long int row)
2028 DL_CACHE_S *dlc = (DL_CACHE_S *)NULL;
2030 dlc = get_dlc(row);
2032 if(dlc){
2033 fill_in_dl_field(dlc);
2034 return(&dlc->dl);
2036 else{
2037 q_status_message(SM_ORDER | SM_DING, 5, 10,
2038 _("Bug in addrbook, not supposed to happen, re-syncing..."));
2039 dprint((1, "Bug in addrbook (null dlc in dlist(%ld), not supposed to happen\n", row));
2040 /* jump back to a safe starting point */
2042 dprint((1, "dlist: panic: initialized %d, n_addrbk %d, cur_row %d, top_ent %ld, ro_warning %d, no_op_possbl %d\n",
2043 as.initialized, as.n_addrbk, as.cur_row, as.top_ent, as.ro_warning, as.no_op_possbl));
2045 longjmp(addrbook_changed_unexpectedly, 1);
2046 /*NOTREACHED*/
2052 * Returns the index of the current address book.
2055 adrbk_num_from_lineno(long int lineno)
2057 DL_CACHE_S *dlc;
2059 dlc = get_dlc(lineno);
2061 return(dlc->adrbk_num);