summaryrefslogtreecommitdiff
path: root/pith/abdlc.c
diff options
context:
space:
mode:
Diffstat (limited to 'pith/abdlc.c')
-rw-r--r--pith/abdlc.c2066
1 files changed, 2066 insertions, 0 deletions
diff --git a/pith/abdlc.c b/pith/abdlc.c
new file mode 100644
index 00000000..d332a1b7
--- /dev/null
+++ b/pith/abdlc.c
@@ -0,0 +1,2066 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: abdlc.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h" /* for os-dep and pith defs/includes */
+#include "../pith/abdlc.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/status.h"
+#include "../pith/ldap.h"
+#include "../pith/tempfile.h"
+
+
+/*
+ * Internal prototypes
+ */
+void initialize_dlc_cache(void);
+int dlc_siblings(DL_CACHE_S *, DL_CACHE_S *);
+DL_CACHE_S *dlc_mgr(long, DlMgrOps, DL_CACHE_S *);
+void free_cache_array(DL_CACHE_S **, int);
+DL_CACHE_S *dlc_prev(DL_CACHE_S *, DL_CACHE_S *);
+DL_CACHE_S *dlc_next(DL_CACHE_S *, DL_CACHE_S *);
+DL_CACHE_S *get_global_top_dlc(DL_CACHE_S *);
+DL_CACHE_S *get_global_bottom_dlc(DL_CACHE_S *);
+DL_CACHE_S *get_top_dl_of_adrbk(int, DL_CACHE_S *);
+DL_CACHE_S *get_bottom_dl_of_adrbk(int, DL_CACHE_S *);
+
+
+#define NO_PERMISSION _("[ Permission Denied ]")
+/* TRANSLATORS: This is a heading referring to something that is readable
+ but not writeable. */
+#define READONLY _(" (ReadOnly)")
+/* TRANSLATORS: Not readable */
+#define NOACCESS _(" (Un-readable)")
+/* TRANSLATORS: Directories, as in LDAP Directories. A heading. */
+#define OLDSTYLE_DIR_TITLE _("Directories")
+
+
+/* data for the display list cache */
+static DL_CACHE_S *cache_array = (DL_CACHE_S *)NULL;
+static long valid_low,
+ valid_high;
+static int index_of_low,
+ size_of_cache,
+ n_cached;
+
+
+void
+initialize_dlc_cache(void)
+{
+ dprint((11, "- initialize_dlc_cache -\n"));
+
+ (void)dlc_mgr(NO_LINE, Initialize, (DL_CACHE_S *)NULL);
+}
+
+
+void
+done_with_dlc_cache(void)
+{
+ dprint((11, "- done_with_dlc_cache -\n"));
+
+ (void)dlc_mgr(NO_LINE, DoneWithCache, (DL_CACHE_S *)NULL);
+}
+
+
+/*
+ * Returns 1 if the dlc's are related to each other, 0 otherwise.
+ *
+ * The idea is that if you are going to flush one of these dlcs from the
+ * cache you should also flush its partners. For example, if you flush one
+ * Listent from a list you should flush the entire entry including all the
+ * Listents and the ListHead. If you flush a DlcTitle, you should also
+ * flush the SubTitle and the TitleBlankTop.
+ */
+int
+dlc_siblings(DL_CACHE_S *dlc1, DL_CACHE_S *dlc2)
+{
+ if(!dlc1 || !dlc2 || dlc1->adrbk_num != dlc2->adrbk_num)
+ return 0;
+
+ switch(dlc1->type){
+
+ case DlcSimple:
+ case DlcListHead:
+ case DlcListBlankTop:
+ case DlcListBlankBottom:
+ case DlcListEnt:
+ case DlcListClickHere:
+ case DlcListEmpty:
+ switch(dlc2->type){
+ case DlcSimple:
+ case DlcListHead:
+ case DlcListBlankTop:
+ case DlcListBlankBottom:
+ case DlcListEnt:
+ case DlcListClickHere:
+ case DlcListEmpty:
+ return(dlc1->dlcelnum == dlc2->dlcelnum);
+
+ default:
+ return 0;
+ }
+
+ break;
+
+ case DlcDirDelim1:
+ case DlcDirDelim2:
+ switch(dlc2->type){
+ case DlcDirDelim1:
+ case DlcDirDelim2:
+ return 1;
+
+ default:
+ return 0;
+ }
+ break;
+
+ case DlcGlobDelim1:
+ case DlcGlobDelim2:
+ switch(dlc2->type){
+ case DlcGlobDelim1:
+ case DlcGlobDelim2:
+ return 1;
+
+ default:
+ return 0;
+ }
+ break;
+
+ case DlcTitle:
+ case DlcTitleNoPerm:
+ case DlcSubTitle:
+ case DlcTitleBlankTop:
+ switch(dlc2->type){
+ case DlcTitle:
+ case DlcTitleNoPerm:
+ case DlcSubTitle:
+ case DlcTitleBlankTop:
+ return 1;
+
+ default:
+ return 0;
+ }
+ break;
+
+ case DlcDirAccess:
+ case DlcDirSubTitle:
+ case DlcDirBlankTop:
+ switch(dlc2->type){
+ case DlcDirAccess:
+ case DlcDirSubTitle:
+ case DlcDirBlankTop:
+ return 1;
+
+ default:
+ return 0;
+ }
+ break;
+
+ case DlcTitleDashTopCmb:
+ case DlcTitleCmb:
+ case DlcTitleDashBottomCmb:
+ case DlcTitleBlankBottomCmb:
+ case DlcClickHereCmb:
+ case DlcTitleBlankTopCmb:
+ switch(dlc2->type){
+ case DlcTitleDashTopCmb:
+ case DlcTitleCmb:
+ case DlcTitleDashBottomCmb:
+ case DlcTitleBlankBottomCmb:
+ case DlcClickHereCmb:
+ case DlcTitleBlankTopCmb:
+ return 1;
+
+ default:
+ return 0;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+ /*NOTREACHED*/
+}
+
+
+/*
+ * Manage the display list cache.
+ *
+ * The cache is a circular array of DL_CACHE_S elements. It always
+ * contains a contiguous set of display lines.
+ * The lowest numbered line in the cache is
+ * valid_low, and the highest is valid_high. Everything in between is
+ * also valid. Index_of_low is where to look
+ * for the valid_low element in the circular array.
+ *
+ * We make calls to dlc_prev and dlc_next to get new entries for the cache.
+ * We need a starting value before we can do that.
+ *
+ * This returns a pointer to a dlc for the desired row. If you want the
+ * actual display list line you call dlist(row) instead of dlc_mgr.
+ */
+DL_CACHE_S *
+dlc_mgr(long int row, DlMgrOps op, DL_CACHE_S *dlc_start)
+{
+ int new_index, known_index, next_index;
+ DL_CACHE_S *dlc = (DL_CACHE_S *)NULL;
+ long next_row;
+ long prev_row;
+
+
+ if(op == Lookup){
+
+ if(row >= valid_low && row <= valid_high){ /* already cached */
+
+ new_index = ((row - valid_low) + index_of_low) % size_of_cache;
+ dlc = &cache_array[new_index];
+
+ }
+ else if(row > valid_high){ /* row is past where we've looked */
+
+ known_index =
+ ((valid_high - valid_low) + index_of_low) % size_of_cache;
+ next_row = valid_high + 1L;
+
+ /* we'll usually be looking for row = valid_high + 1 */
+ while(next_row <= row){
+
+ new_index = (known_index + 1) % size_of_cache;
+
+ dlc =
+ dlc_next(&cache_array[known_index], &cache_array[new_index]);
+
+ /*
+ * This means somebody changed the file out from underneath
+ * us. This would happen if dlc_next needed to ask for an
+ * abe to figure out what the type of the next row is, but
+ * adrbk_get_ae returned a NULL. I don't think that can
+ * happen, but if it does...
+ */
+ if(dlc->type == DlcNotSet){
+ dprint((1, "dlc_next returned DlcNotSet\n"));
+ goto panic_abook_corrupt;
+ }
+
+ if(n_cached == size_of_cache){ /* replaced low cache entry */
+ valid_low++;
+ index_of_low = (index_of_low + 1) % size_of_cache;
+ }
+ else
+ n_cached++;
+
+ valid_high++;
+
+ next_row++;
+ known_index = new_index; /* for next time through loop */
+ }
+ }
+ else if(row < valid_low){ /* row is back up the screen */
+
+ known_index = index_of_low;
+ prev_row = valid_low - 1L;
+
+ while(prev_row >= row){
+
+ new_index = (known_index - 1 + size_of_cache) % size_of_cache;
+ dlc =
+ dlc_prev(&cache_array[known_index], &cache_array[new_index]);
+
+ if(dlc->type == DlcNotSet){
+ dprint((1, "dlc_prev returned DlcNotSet (1)\n"));
+ goto panic_abook_corrupt;
+ }
+
+ if(n_cached == size_of_cache) /* replaced high cache entry */
+ valid_high--;
+ else
+ n_cached++;
+
+ valid_low--;
+ index_of_low =
+ (index_of_low - 1 + size_of_cache) % size_of_cache;
+
+ prev_row--;
+ known_index = new_index;
+ }
+ }
+ }
+ else if(op == Initialize){
+
+ n_cached = 0;
+
+ if(!cache_array || size_of_cache != 3 * MAX(as.l_p_page,1)){
+ if(cache_array)
+ free_cache_array(&cache_array, size_of_cache);
+
+ size_of_cache = 3 * MAX(as.l_p_page,1);
+ cache_array =
+ (DL_CACHE_S *)fs_get(size_of_cache * sizeof(DL_CACHE_S));
+ memset((void *)cache_array, 0, size_of_cache * sizeof(DL_CACHE_S));
+ }
+
+ /* this will return NULL below and the caller should ignore that */
+ }
+ /*
+ * Flush all rows for a particular addrbook entry from the cache, but
+ * keep the cache alive and anchored in the same place. The particular
+ * entry is the one that dlc_start is one of the rows of.
+ */
+ else if(op == FlushDlcFromCache){
+ long low_entry;
+
+ next_row = dlc_start->global_row - 1;
+ for(; next_row >= valid_low; next_row--){
+ next_index = ((next_row - valid_low) + index_of_low) %
+ size_of_cache;
+ if(!dlc_siblings(dlc_start, &cache_array[next_index]))
+ break;
+ }
+
+ low_entry = next_row + 1L;
+
+ /*
+ * If low_entry now points one past a ListBlankBottom, delete that,
+ * too, since it may not make sense anymore.
+ */
+ if(low_entry > valid_low){
+ next_index = ((low_entry -1L - valid_low) + index_of_low) %
+ size_of_cache;
+ if(cache_array[next_index].type == DlcListBlankBottom)
+ low_entry--;
+ }
+
+ if(low_entry > valid_low){ /* invalidate everything >= this */
+ n_cached -= (valid_high - (low_entry - 1L));
+ valid_high = low_entry - 1L;
+ }
+ else{
+ /*
+ * This is the tough case. That entry was the first thing cached,
+ * so we need to invalidate the whole cache. However, we also
+ * need to keep at least one thing cached for an anchor, so
+ * we need to get the dlc before this one and it should be a
+ * dlc not related to this same addrbook entry.
+ */
+ known_index = index_of_low;
+ prev_row = valid_low - 1L;
+
+ for(;;){
+
+ new_index = (known_index - 1 + size_of_cache) % size_of_cache;
+ dlc =
+ dlc_prev(&cache_array[known_index], &cache_array[new_index]);
+
+ if(dlc->type == DlcNotSet){
+ dprint((1, "dlc_prev returned DlcNotSet (2)\n"));
+ goto panic_abook_corrupt;
+ }
+
+ valid_low--;
+ index_of_low =
+ (index_of_low - 1 + size_of_cache) % size_of_cache;
+
+ if(!dlc_siblings(dlc_start, dlc))
+ break;
+
+ known_index = new_index;
+ }
+
+ n_cached = 1;
+ valid_high = valid_low;
+ }
+ }
+ /*
+ * We have to anchor ourselves at a first element.
+ * Here's how we start at the top.
+ */
+ else if(op == FirstEntry){
+ initialize_dlc_cache();
+ n_cached++;
+ dlc = &cache_array[0];
+ dlc = get_global_top_dlc(dlc);
+ dlc->global_row = row;
+ index_of_low = 0;
+ valid_low = row;
+ valid_high = row;
+ }
+ /* And here's how we start from the bottom. */
+ else if(op == LastEntry){
+ initialize_dlc_cache();
+ n_cached++;
+ dlc = &cache_array[0];
+ dlc = get_global_bottom_dlc(dlc);
+ dlc->global_row = row;
+ index_of_low = 0;
+ valid_low = row;
+ valid_high = row;
+ }
+ /*
+ * And here's how we start from an arbitrary position in the middle.
+ * We root the cache at display line row, so it helps if row is close
+ * to where we're going to be starting so that things are easy to find.
+ * The dl that goes with line row is dl_start from addrbook number
+ * adrbk_num_start.
+ */
+ else if(op == ArbitraryStartingPoint){
+ AddrScrn_Disp dl;
+
+ initialize_dlc_cache();
+ n_cached++;
+ dlc = &cache_array[0];
+ /*
+ * Save this in case fill_in_dl_field needs to free the text
+ * it points to.
+ */
+ dl = dlc->dl;
+ *dlc = *dlc_start;
+ dlc->dl = dl;
+ dlc->global_row = row;
+
+ index_of_low = 0;
+ valid_low = row;
+ valid_high = row;
+ }
+ else if(op == DoneWithCache){
+
+ n_cached = 0;
+ if(cache_array)
+ free_cache_array(&cache_array, size_of_cache);
+ }
+
+ return(dlc);
+
+panic_abook_corrupt:
+ q_status_message(SM_ORDER | SM_DING, 5, 10,
+ _("Addrbook changed unexpectedly, re-syncing..."));
+ dprint((1,
+ _("addrbook changed while we had it open?, re-sync\n")));
+ dprint((2,
+ "valid_low=%ld valid_high=%ld index_of_low=%d size_of_cache=%d\n",
+ valid_low, valid_high, index_of_low, size_of_cache));
+ dprint((2,
+ "n_cached=%d new_index=%d known_index=%d next_index=%d\n",
+ n_cached, new_index, known_index, next_index));
+ dprint((2,
+ "next_row=%ld prev_row=%ld row=%ld\n", next_row, prev_row, row));
+ /* jump back to a safe starting point */
+ longjmp(addrbook_changed_unexpectedly, 1);
+ /*NOTREACHED*/
+}
+
+
+void
+free_cache_array(DL_CACHE_S **c_array, int size)
+{
+ DL_CACHE_S *dlc;
+ int i;
+
+ for(i = 0; i < size; i++){
+ dlc = &(*c_array)[i];
+ /* free any allocated space */
+ switch(dlc->dl.type){
+ case Text:
+ case Title:
+ case TitleCmb:
+ case AskServer:
+ if(dlc->dl.usst)
+ fs_give((void **)&dlc->dl.usst);
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ fs_give((void **)c_array);
+}
+
+
+/*
+ * Get the dlc element that comes before "old". The function that calls this
+ * function is the one that keeps a cache and checks in the cache before
+ * calling here. New is a passed in pointer to a buffer where we fill in
+ * the answer.
+ */
+DL_CACHE_S *
+dlc_prev(DL_CACHE_S *old, DL_CACHE_S *new)
+{
+ PerAddrBook *pab;
+ AdrBk_Entry *abe;
+ adrbk_cntr_t list_count;
+
+ new->adrbk_num = -2;
+ new->dlcelnum = NO_NEXT;
+ new->dlcoffset = NO_NEXT;
+ new->type = DlcNotSet;
+ pab = &as.adrbks[old->adrbk_num];
+
+ switch(old->type){
+ case DlcTitle:
+ case DlcTitleNoPerm:
+ if(old->adrbk_num == 0 && as.config && as.how_many_personals == 0)
+ new->type = DlcGlobDelim2;
+ else if(old->adrbk_num == 0)
+ new->type = DlcOneBeforeBeginning;
+ else if(old->adrbk_num == as.how_many_personals)
+ new->type = DlcGlobDelim2;
+ else
+ new->type = DlcTitleBlankTop;
+
+ break;
+
+ case DlcSubTitle:
+ if(pab->access == NoAccess)
+ new->type = DlcTitleNoPerm;
+ else
+ new->type = DlcTitle;
+
+ break;
+
+ case DlcTitleBlankTop:
+ new->adrbk_num = old->adrbk_num - 1;
+ new->type = DlcSubTitle;
+ break;
+
+ case DlcEmpty:
+ case DlcZoomEmpty:
+ case DlcNoPermission:
+ case DlcNoAbooks:
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ if(as.n_addrbk == 1 && as.n_serv == 0)
+ new->type = DlcOneBeforeBeginning;
+ else
+ new->type = DlcTitleBlankBottomCmb;
+ }
+ else
+ new->type = DlcOneBeforeBeginning;
+
+ break;
+
+ case DlcSimple:
+ {
+ adrbk_cntr_t el;
+ long i;
+
+ i = old->dlcelnum;
+ i--;
+ el = old->dlcelnum - 1;
+ while(i >= 0L){
+ if(!as.zoomed || entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)el))
+ break;
+
+ el--;
+ i--;
+ }
+
+ if(i >= 0){
+ new->dlcelnum = el;
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
+ if(abe && abe->tag == Single)
+ new->type = DlcSimple;
+ else if(abe && abe->tag == List)
+ new->type = DlcListBlankBottom;
+ }
+ else if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ if(as.n_addrbk == 1 && as.n_serv == 0)
+ new->type = DlcOneBeforeBeginning;
+ else
+ new->type = DlcTitleBlankBottomCmb;
+ }
+ else
+ new->type = DlcOneBeforeBeginning;
+ }
+
+ break;
+
+ case DlcListHead:
+ {
+ adrbk_cntr_t el;
+ long i;
+
+ i = old->dlcelnum;
+ i--;
+ el = old->dlcelnum - 1;
+ while(i >= 0L){
+ if(!as.zoomed || entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)el))
+ break;
+
+ el--;
+ i--;
+ }
+
+ if(i >= 0){
+ new->dlcelnum = el;
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
+ if(abe && abe->tag == Single){
+ new->type = DlcListBlankTop;
+ new->dlcelnum = old->dlcelnum;
+ }
+ else if(abe && abe->tag == List)
+ new->type = DlcListBlankBottom;
+ }
+ else if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ if(as.n_addrbk == 1 && as.n_serv == 0)
+ new->type = DlcOneBeforeBeginning;
+ else
+ new->type = DlcTitleBlankBottomCmb;
+ }
+ else
+ new->type = DlcOneBeforeBeginning;
+ }
+
+ break;
+
+ case DlcListEnt:
+ if(old->dlcoffset > 0){
+ new->type = DlcListEnt;
+ new->dlcelnum = old->dlcelnum;
+ new->dlcoffset = old->dlcoffset - 1;
+ }
+ else{
+ new->type = DlcListHead;
+ new->dlcelnum = old->dlcelnum;
+ }
+
+ break;
+
+ case DlcListClickHere:
+ case DlcListEmpty:
+ new->type = DlcListHead;
+ new->dlcelnum = old->dlcelnum;
+ break;
+
+ case DlcListBlankTop: /* can only occur between a Simple and a List */
+ new->type = DlcSimple;
+ {
+ adrbk_cntr_t el;
+ long i;
+
+ i = old->dlcelnum;
+ i--;
+ el = old->dlcelnum - 1;
+ while(i >= 0L){
+ if(!as.zoomed || entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)el))
+ break;
+
+ el--;
+ i--;
+ }
+
+ if(i >= 0)
+ new->dlcelnum = el;
+ else{
+ dprint((1, "Bug in addrbook: case ListBlankTop with no selected entry\n"));
+ goto oops;
+ }
+ }
+
+ break;
+
+ case DlcListBlankBottom:
+ new->dlcelnum = old->dlcelnum;
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
+ if(F_ON(F_EXPANDED_DISTLISTS,ps_global)
+ || exp_is_expanded(pab->address_book->exp, (a_c_arg_t)new->dlcelnum)){
+ list_count = listmem_count_from_abe(abe);
+ if(list_count == 0)
+ new->type = DlcListEmpty;
+ else{
+ new->type = DlcListEnt;
+ new->dlcoffset = list_count - 1;
+ }
+ }
+ else
+ new->type = DlcListClickHere;
+
+ break;
+
+ case DlcGlobDelim1:
+ if(as.how_many_personals == 0)
+ new->type = DlcPersAdd;
+ else{
+ new->adrbk_num = as.how_many_personals - 1;
+ new->type = DlcSubTitle;
+ }
+ break;
+
+ case DlcGlobDelim2:
+ new->type = DlcGlobDelim1;
+ break;
+
+ case DlcPersAdd:
+ new->type = DlcOneBeforeBeginning;
+ break;
+
+ case DlcGlobAdd:
+ new->type = DlcGlobDelim2;
+ break;
+
+ case DlcDirAccess:
+ if(old->adrbk_num == 0 && as.n_addrbk == 0)
+ new->type = DlcOneBeforeBeginning;
+ else if(old->adrbk_num == 0)
+ new->type = DlcDirDelim2;
+ else
+ new->type = DlcDirBlankTop;
+
+ break;
+
+ case DlcDirSubTitle:
+ new->type = DlcDirAccess;
+ break;
+
+ case DlcDirBlankTop:
+ new->adrbk_num = old->adrbk_num -1;
+ new->type = DlcDirSubTitle;
+ break;
+
+ case DlcDirDelim1:
+ new->adrbk_num = as.n_addrbk - 1;
+ if(as.n_addrbk == 0)
+ new->type = DlcNoAbooks;
+ else
+ new = get_bottom_dl_of_adrbk(new->adrbk_num, new);
+
+ break;
+
+ case DlcDirDelim1a:
+ new->type = DlcDirDelim1;
+ break;
+
+ case DlcDirDelim1b:
+ new->type = DlcDirDelim1a;
+ break;
+
+ case DlcDirDelim1c:
+ new->type = DlcDirDelim1b;
+ break;
+
+ case DlcDirDelim2:
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global))
+ new->type = DlcDirDelim1c;
+ else
+ new->type = DlcDirDelim1;
+
+ break;
+
+ case DlcTitleDashTopCmb:
+ if(old->adrbk_num == 0)
+ new->type = DlcOneBeforeBeginning;
+ else
+ new->type = DlcTitleBlankTopCmb;
+
+ break;
+
+ case DlcTitleCmb:
+ new->type = DlcTitleDashTopCmb;
+ break;
+
+ case DlcTitleDashBottomCmb:
+ new->type = DlcTitleCmb;
+ break;
+
+ case DlcTitleBlankBottomCmb:
+ new->type = DlcTitleDashBottomCmb;
+ break;
+
+ case DlcClickHereCmb:
+ if(as.n_addrbk == 1 && as.n_serv == 0)
+ new->type = DlcOneBeforeBeginning;
+ else
+ new->type = DlcTitleBlankBottomCmb;
+
+ break;
+
+ case DlcTitleBlankTopCmb:
+ new->adrbk_num = old->adrbk_num - 1;
+ new = get_bottom_dl_of_adrbk(new->adrbk_num, new);
+ break;
+
+ case DlcBeginning:
+ case DlcTwoBeforeBeginning:
+ new->type = DlcBeginning;
+ break;
+
+ case DlcOneBeforeBeginning:
+ new->type = DlcTwoBeforeBeginning;
+ break;
+
+oops:
+ default:
+ q_status_message(SM_ORDER | SM_DING, 5, 10,
+ _("Bug in addrbook, not supposed to happen, re-syncing..."));
+ dprint((1, "Bug in addrbook, impossible case (%d) in dlc_prev, re-sync\n",
+ old->type));
+ /* jump back to a safe starting point */
+ longjmp(addrbook_changed_unexpectedly, 1);
+ /*NOTREACHED*/
+ }
+
+ new->global_row = old->global_row - 1L;
+ if(new->adrbk_num == -2)
+ new->adrbk_num = old->adrbk_num;
+
+ return(new);
+}
+
+
+/*
+ * Get the dlc element that comes after "old". The function that calls this
+ * function is the one that keeps a cache and checks in the cache before
+ * calling here.
+ */
+DL_CACHE_S *
+dlc_next(DL_CACHE_S *old, DL_CACHE_S *new)
+{
+ PerAddrBook *pab;
+ AdrBk_Entry *abe;
+ adrbk_cntr_t ab_count;
+ adrbk_cntr_t list_count;
+
+ new->adrbk_num = -2;
+ new->dlcelnum = NO_NEXT;
+ new->dlcoffset = NO_NEXT;
+ new->type = DlcNotSet;
+ pab = &as.adrbks[old->adrbk_num];
+
+ switch(old->type){
+ case DlcTitle:
+ case DlcTitleNoPerm:
+ new->type = DlcSubTitle;
+ break;
+
+ case DlcSubTitle:
+ if((old->adrbk_num == as.how_many_personals - 1) &&
+ (as.config || as.n_addrbk > as.how_many_personals))
+ new->type = DlcGlobDelim1;
+ else if(as.n_serv && !as.config &&
+ (old->adrbk_num == as.n_addrbk - 1))
+ new->type = DlcDirDelim1;
+ else if(old->adrbk_num == as.n_addrbk - 1)
+ new->type = DlcEnd;
+ else{
+ new->adrbk_num = old->adrbk_num + 1;
+ new->type = DlcTitleBlankTop;
+ }
+
+ break;
+
+ case DlcTitleBlankTop:
+ if(pab->access == NoAccess)
+ new->type = DlcTitleNoPerm;
+ else
+ new->type = DlcTitle;
+
+ break;
+
+ case DlcEmpty:
+ case DlcZoomEmpty:
+ case DlcNoPermission:
+ case DlcClickHereCmb:
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ if(old->adrbk_num < as.n_addrbk - 1){
+ new->adrbk_num = old->adrbk_num + 1;
+ new->type = DlcTitleBlankTopCmb;
+ }
+ else if(as.n_serv)
+ new->type = DlcDirDelim1;
+ else
+ new->type = DlcEnd;
+ }
+ else
+ new->type = DlcEnd;
+
+ break;
+
+ case DlcNoAbooks:
+ case DlcGlobAdd:
+ case DlcEnd:
+ new->type = DlcEnd;
+ break;
+
+ case DlcSimple:
+ {
+ adrbk_cntr_t el;
+ long i;
+
+ ab_count = adrbk_count(pab->address_book);
+ i = old->dlcelnum;
+ i++;
+ el = old->dlcelnum + 1;
+ while(i < ab_count){
+ if(!as.zoomed || entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)el))
+ break;
+
+ el++;
+ i++;
+ }
+
+ if(i < ab_count){
+ new->dlcelnum = el;
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
+ if(abe && abe->tag == Single)
+ new->type = DlcSimple;
+ else if(abe && abe->tag == List)
+ new->type = DlcListBlankTop;
+ }
+ else if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ if(old->adrbk_num < as.n_addrbk - 1){
+ new->adrbk_num = old->adrbk_num + 1;
+ new->type = DlcTitleBlankTopCmb;
+ }
+ else if(as.n_serv)
+ new->type = DlcDirDelim1;
+ else
+ new->type = DlcEnd;
+ }
+ else
+ new->type = DlcEnd;
+ }
+
+ break;
+
+ case DlcListHead:
+ new->dlcelnum = old->dlcelnum;
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
+ if(F_ON(F_EXPANDED_DISTLISTS,ps_global)
+ || exp_is_expanded(pab->address_book->exp, (a_c_arg_t)new->dlcelnum)){
+ list_count = listmem_count_from_abe(abe);
+ if(list_count == 0)
+ new->type = DlcListEmpty;
+ else{
+ new->type = DlcListEnt;
+ new->dlcoffset = 0;
+ }
+ }
+ else
+ new->type = DlcListClickHere;
+
+ break;
+
+ case DlcListEnt:
+ new->dlcelnum = old->dlcelnum;
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
+ list_count = listmem_count_from_abe(abe);
+ if(old->dlcoffset == list_count - 1){ /* last member of list */
+ adrbk_cntr_t el;
+ long i;
+
+ ab_count = adrbk_count(pab->address_book);
+ i = old->dlcelnum;
+ i++;
+ el = old->dlcelnum + 1;
+ while(i < ab_count){
+ if(!as.zoomed || entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)el))
+ break;
+
+ el++;
+ i++;
+ }
+
+ if(i < ab_count)
+ new->type = DlcListBlankBottom;
+ else if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ if(old->adrbk_num < as.n_addrbk - 1){
+ new->adrbk_num = old->adrbk_num + 1;
+ new->type = DlcTitleBlankTopCmb;
+ }
+ else if(as.n_serv)
+ new->type = DlcDirDelim1;
+ else
+ new->type = DlcEnd;
+ }
+ else
+ new->type = DlcEnd;
+ }
+ else{
+ new->type = DlcListEnt;
+ new->dlcoffset = old->dlcoffset + 1;
+ }
+
+ break;
+
+ case DlcListClickHere:
+ case DlcListEmpty:
+ {
+ adrbk_cntr_t el;
+ long i;
+
+ new->dlcelnum = old->dlcelnum;
+ ab_count = adrbk_count(pab->address_book);
+ i = old->dlcelnum;
+ i++;
+ el = old->dlcelnum + 1;
+ while(i < ab_count){
+ if(!as.zoomed || entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)el))
+ break;
+
+ el++;
+ i++;
+ }
+
+ if(i < ab_count)
+ new->type = DlcListBlankBottom;
+ else if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ if(old->adrbk_num < as.n_addrbk - 1){
+ new->adrbk_num = old->adrbk_num + 1;
+ new->type = DlcTitleBlankTopCmb;
+ }
+ else if(as.n_serv)
+ new->type = DlcDirDelim1;
+ else
+ new->type = DlcEnd;
+ }
+ else
+ new->type = DlcEnd;
+ }
+
+ break;
+
+ case DlcListBlankTop:
+ new->type = DlcListHead;
+ new->dlcelnum = old->dlcelnum;
+ break;
+
+ case DlcListBlankBottom:
+ {
+ adrbk_cntr_t el;
+ long i;
+
+ ab_count = adrbk_count(pab->address_book);
+ i = old->dlcelnum;
+ i++;
+ el = old->dlcelnum + 1;
+ while(i < ab_count){
+ if(!as.zoomed || entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)el))
+ break;
+
+ el++;
+ i++;
+ }
+
+ if(i < ab_count){
+ new->dlcelnum = el;
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
+ if(abe && abe->tag == Single)
+ new->type = DlcSimple;
+ else if(abe && abe->tag == List)
+ new->type = DlcListHead;
+ }
+ else
+ /* can't happen */
+ new->type = DlcEnd;
+ }
+
+ break;
+
+ case DlcGlobDelim1:
+ new->type = DlcGlobDelim2;
+ break;
+
+ case DlcGlobDelim2:
+ if(as.config && as.how_many_personals == as.n_addrbk)
+ new->type = DlcGlobAdd;
+ else{
+ new->adrbk_num = as.how_many_personals;
+ pab = &as.adrbks[new->adrbk_num];
+ if(pab->access == NoAccess)
+ new->type = DlcTitleNoPerm;
+ else
+ new->type = DlcTitle;
+ }
+
+ break;
+
+ case DlcDirDelim1:
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global))
+ new->type = DlcDirDelim1a;
+ else
+ new->type = DlcDirDelim2;
+
+ new->adrbk_num = 0;
+ break;
+
+ case DlcDirDelim1a:
+ new->type = DlcDirDelim1b;
+ break;
+
+ case DlcDirDelim1b:
+ new->type = DlcDirDelim1c;
+ break;
+
+ case DlcDirDelim1c:
+ new->type = DlcDirDelim2;
+ new->adrbk_num = 0;
+ break;
+
+ case DlcDirDelim2:
+ new->type = DlcDirAccess;
+ new->adrbk_num = 0;
+ break;
+
+ case DlcPersAdd:
+ new->type = DlcGlobDelim1;
+ break;
+
+ case DlcDirAccess:
+ new->type = DlcDirSubTitle;
+ break;
+
+ case DlcDirSubTitle:
+ if(old->adrbk_num == as.n_serv - 1)
+ new->type = DlcEnd;
+ else{
+ new->type = DlcDirBlankTop;
+ new->adrbk_num = old->adrbk_num + 1;
+ }
+
+ break;
+
+ case DlcDirBlankTop:
+ new->type = DlcDirAccess;
+ break;
+
+ case DlcTitleDashTopCmb:
+ new->type = DlcTitleCmb;
+ break;
+
+ case DlcTitleCmb:
+ new->type = DlcTitleDashBottomCmb;
+ break;
+
+ case DlcTitleDashBottomCmb:
+ new->type = DlcTitleBlankBottomCmb;
+ break;
+
+ case DlcTitleBlankBottomCmb:
+ pab = &as.adrbks[old->adrbk_num];
+ if(pab->ostatus != Open && pab->access != NoAccess)
+ new->type = DlcClickHereCmb;
+ else
+ new = get_top_dl_of_adrbk(old->adrbk_num, new);
+
+ break;
+
+ case DlcTitleBlankTopCmb:
+ new->type = DlcTitleDashTopCmb;
+ break;
+
+ case DlcOneBeforeBeginning:
+ new = get_global_top_dlc(new);
+ break;
+
+ case DlcTwoBeforeBeginning:
+ new->type = DlcOneBeforeBeginning;
+ break;
+
+ default:
+ q_status_message(SM_ORDER | SM_DING, 5, 10,
+ _("Bug in addrbook, not supposed to happen, re-syncing..."));
+ dprint((1, "Bug in addrbook, impossible case (%d) in dlc_next, re-sync\n",
+ old->type));
+ /* jump back to a safe starting point */
+ longjmp(addrbook_changed_unexpectedly, 1);
+ /*NOTREACHED*/
+ }
+
+ new->global_row = old->global_row + 1L;
+ if(new->adrbk_num == -2)
+ new->adrbk_num = old->adrbk_num;
+
+ return(new);
+}
+
+
+/*
+ * Get the display line at the very top of whole addrbook screen display.
+ */
+DL_CACHE_S *
+get_global_top_dlc(DL_CACHE_S *new)
+ /* fill in answer here */
+{
+ PerAddrBook *pab;
+
+ new->dlcelnum = NO_NEXT;
+ new->dlcoffset = NO_NEXT;
+ new->type = DlcNotSet;
+
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global) && !as.config){
+ new->adrbk_num = 0;
+ if(as.n_addrbk > 1 || (as.n_serv && as.n_addrbk == 1))
+ new->type = DlcTitleDashTopCmb;
+ else if(as.n_addrbk == 1)
+ new = get_top_dl_of_adrbk(new->adrbk_num, new);
+ else if(as.n_serv > 0){ /* 1st directory */
+ new->type = DlcDirAccess;
+ new->adrbk_num = 0;
+ }
+ else
+ new->type = DlcNoAbooks;
+ }
+ else{
+ new->adrbk_num = 0;
+
+ if(as.config){
+ if(as.how_many_personals == 0) /* no personals */
+ new->type = DlcPersAdd;
+ else{
+ pab = &as.adrbks[new->adrbk_num]; /* 1st personal */
+ if(pab->access == NoAccess)
+ new->type = DlcTitleNoPerm;
+ else
+ new->type = DlcTitle;
+ }
+ }
+ else if(any_ab_open()){
+ new->adrbk_num = as.cur;
+ new = get_top_dl_of_adrbk(new->adrbk_num, new);
+ }
+ else if(as.n_addrbk > 0){ /* 1st addrbook */
+ pab = &as.adrbks[new->adrbk_num];
+ if(pab->access == NoAccess)
+ new->type = DlcTitleNoPerm;
+ else
+ new->type = DlcTitle;
+ }
+ else if(as.n_serv > 0){ /* 1st directory */
+ new->type = DlcDirAccess;
+ new->adrbk_num = 0;
+ }
+ else
+ new->type = DlcNoAbooks;
+ }
+
+ return(new);
+}
+
+
+/*
+ * Get the last display line for the whole address book screen.
+ * This gives us a way to start at the end and move back up.
+ */
+DL_CACHE_S *
+get_global_bottom_dlc(DL_CACHE_S *new)
+ /* fill in answer here */
+{
+ new->dlcelnum = NO_NEXT;
+ new->dlcoffset = NO_NEXT;
+ new->type = DlcNotSet;
+
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global) && !as.config){
+ if(as.n_serv){
+ new->type = DlcDirSubTitle;
+ new->adrbk_num = as.n_serv - 1;
+ }
+ else if(as.n_addrbk > 0){
+ new->adrbk_num = as.n_addrbk - 1;
+ new = get_bottom_dl_of_adrbk(new->adrbk_num, new);
+ }
+ else
+ new->type = DlcNoAbooks;
+ }
+ else{
+ new->adrbk_num = MAX(as.n_addrbk - 1, 0);
+
+ if(as.config){
+ if(as.how_many_personals == as.n_addrbk) /* no globals */
+ new->type = DlcGlobAdd;
+ else
+ new->type = DlcSubTitle;
+ }
+ else if(any_ab_open()){
+ new->adrbk_num = as.cur;
+ new = get_bottom_dl_of_adrbk(new->adrbk_num, new);
+ }
+ else if(as.n_serv){
+ new->type = DlcDirSubTitle;
+ new->adrbk_num = as.n_serv - 1;
+ }
+ else{ /* !config && !opened && !n_serv */
+ if(as.n_addrbk)
+ new->type = DlcSubTitle;
+ else
+ new->type = DlcNoAbooks;
+ }
+ }
+
+ return(new);
+}
+
+
+/*
+ * First dl in a particular addrbook, not counting title lines.
+ * Assumes as.opened.
+ */
+DL_CACHE_S *
+get_top_dl_of_adrbk(int adrbk_num, DL_CACHE_S *new)
+
+ /* fill in answer here */
+{
+ PerAddrBook *pab;
+ AdrBk_Entry *abe;
+ adrbk_cntr_t ab_count;
+
+ pab = &as.adrbks[adrbk_num];
+ new->adrbk_num = adrbk_num;
+
+ if(pab->access == NoAccess)
+ new->type = DlcNoPermission;
+ else{
+ adrbk_cntr_t el;
+ long i;
+
+ ab_count = adrbk_count(pab->address_book);
+
+ i = 0L;
+ el = 0;
+ /* find first displayed entry */
+ while(i < ab_count){
+ if(!as.zoomed || entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)el))
+ break;
+
+ el++;
+ i++;
+ }
+
+ if(i < ab_count){
+ new->dlcelnum = el;
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
+ if(abe && abe->tag == Single)
+ new->type = DlcSimple;
+ else if(abe && abe->tag == List)
+ new->type = DlcListHead;
+ }
+ else if(as.zoomed)
+ new->type = DlcZoomEmpty;
+ else
+ new->type = DlcEmpty;
+ }
+
+ return(new);
+}
+
+
+/*
+ * Find the last display line for addrbook number adrbk_num.
+ * Assumes as.opened (unless OLD_ABOOK_DISP).
+ */
+DL_CACHE_S *
+get_bottom_dl_of_adrbk(int adrbk_num, DL_CACHE_S *new)
+
+ /* fill in answer here */
+{
+ PerAddrBook *pab;
+ AdrBk_Entry *abe;
+ adrbk_cntr_t ab_count;
+ adrbk_cntr_t list_count;
+
+ pab = &as.adrbks[adrbk_num];
+ new->adrbk_num = adrbk_num;
+
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global) && pab->ostatus != Open){
+ if(pab->access == NoAccess)
+ new->type = DlcNoPermission;
+ else
+ new->type = DlcClickHereCmb;
+ }
+ else if(F_OFF(F_CMBND_ABOOK_DISP,ps_global) && pab->ostatus != Open){
+ new->type = DlcSubTitle;
+ }
+ else{
+ if(pab->access == NoAccess)
+ new->type = DlcNoPermission;
+ else{
+ adrbk_cntr_t el;
+ long i;
+
+ ab_count = adrbk_count(pab->address_book);
+ i = ab_count - 1;
+ el = ab_count - 1;
+ /* find last displayed entry */
+ while(i >= 0L){
+ if(!as.zoomed || entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)el))
+ break;
+
+ el--;
+ i--;
+ }
+
+ if(i >= 0){
+ new->dlcelnum = el;
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t)new->dlcelnum);
+ if(abe && abe->tag == Single)
+ new->type = DlcSimple;
+ else if(abe && abe->tag == List){
+ if(F_ON(F_EXPANDED_DISTLISTS,ps_global)
+ || exp_is_expanded(pab->address_book->exp,
+ (a_c_arg_t)new->dlcelnum)){
+ list_count = listmem_count_from_abe(abe);
+ if(list_count == 0)
+ new->type = DlcListEmpty;
+ else{
+ new->type = DlcListEnt;
+ new->dlcoffset = list_count - 1;
+ }
+ }
+ else
+ new->type = DlcListClickHere;
+ }
+ }
+ else if(as.zoomed)
+ new->type = DlcZoomEmpty;
+ else
+ new->type = DlcEmpty;
+ }
+
+ }
+
+ return(new);
+}
+
+
+/*
+ * This returns the actual dlc instead of the dl within the dlc.
+ */
+DL_CACHE_S *
+get_dlc(long int row)
+{
+ dprint((11, "- get_dlc(%ld) -\n", row));
+
+ return(dlc_mgr(row, Lookup, (DL_CACHE_S *)NULL));
+}
+
+
+/*
+ * Move to new_dlc and give it row number row_number_to_assign_it.
+ * We copy the passed in dlc in case the caller passed us a pointer into
+ * the cache.
+ */
+void
+warp_to_dlc(DL_CACHE_S *new_dlc, long int row_number_to_assign_it)
+{
+ DL_CACHE_S dlc;
+
+ dprint((9, "- warp_to_dlc(%ld) -\n", row_number_to_assign_it));
+
+ dlc = *new_dlc;
+
+ /*
+ * Make sure we can move forward and backward from these
+ * types that we may wish to warp to. The caller may not have known
+ * to set adrbk_num for these types.
+ */
+ switch(dlc.type){
+ case DlcPersAdd:
+ dlc.adrbk_num = 0;
+ break;
+ case DlcGlobAdd:
+ dlc.adrbk_num = as.n_addrbk;
+ break;
+ default:
+ break;
+ }
+
+ (void)dlc_mgr(row_number_to_assign_it, ArbitraryStartingPoint, &dlc);
+}
+
+
+/*
+ * Move to first dlc and give it row number 0.
+ */
+void
+warp_to_beginning(void)
+{
+ dprint((9, "- warp_to_beginning -\n"));
+
+ (void)dlc_mgr(0L, FirstEntry, (DL_CACHE_S *)NULL);
+}
+
+
+/*
+ * Move to first dlc in abook_num and give it row number 0.
+ */
+void
+warp_to_top_of_abook(int abook_num)
+{
+ DL_CACHE_S dlc;
+
+ dprint((9, "- warp_to_top_of_abook(%d) -\n", abook_num));
+
+ (void)get_top_dl_of_adrbk(abook_num, &dlc);
+ warp_to_dlc(&dlc, 0L);
+}
+
+
+/*
+ * Move to last dlc and give it row number 0.
+ */
+void
+warp_to_end(void)
+{
+ dprint((9, "- warp_to_end -\n"));
+
+ (void)dlc_mgr(0L, LastEntry, (DL_CACHE_S *)NULL);
+}
+
+
+/*
+ * This flushes all of the cache that is related to this dlc.
+ */
+void
+flush_dlc_from_cache(DL_CACHE_S *dlc_to_flush)
+{
+ dprint((11, "- flush_dlc_from_cache -\n"));
+
+ (void)dlc_mgr(NO_LINE, FlushDlcFromCache, dlc_to_flush);
+}
+
+
+/*
+ * Returns 1 if the dlc's match, 0 otherwise.
+ */
+int
+matching_dlcs(DL_CACHE_S *dlc1, DL_CACHE_S *dlc2)
+{
+ if(!dlc1 || !dlc2 ||
+ dlc1->type != dlc2->type ||
+ dlc1->adrbk_num != dlc2->adrbk_num)
+ return 0;
+
+ switch(dlc1->type){
+
+ case DlcSimple:
+ case DlcListHead:
+ case DlcListBlankTop:
+ case DlcListBlankBottom:
+ case DlcListClickHere:
+ case DlcListEmpty:
+ return(dlc1->dlcelnum == dlc2->dlcelnum);
+
+ case DlcListEnt:
+ return(dlc1->dlcelnum == dlc2->dlcelnum &&
+ dlc1->dlcoffset == dlc2->dlcoffset);
+
+ case DlcTitle:
+ case DlcSubTitle:
+ case DlcTitleNoPerm:
+ case DlcTitleBlankTop:
+ case DlcEmpty:
+ case DlcZoomEmpty:
+ case DlcNoPermission:
+ case DlcPersAdd:
+ case DlcGlobAdd:
+ case DlcGlobDelim1:
+ case DlcGlobDelim2:
+ case DlcDirAccess:
+ case DlcDirDelim1:
+ case DlcDirDelim2:
+ case DlcTitleDashTopCmb:
+ case DlcTitleCmb:
+ case DlcTitleDashBottomCmb:
+ case DlcTitleBlankBottomCmb:
+ case DlcClickHereCmb:
+ case DlcTitleBlankTopCmb:
+ return 1;
+
+ case DlcNotSet:
+ case DlcBeginning:
+ case DlcOneBeforeBeginning:
+ case DlcTwoBeforeBeginning:
+ case DlcEnd:
+ case DlcNoAbooks:
+ return 0;
+
+ default:
+ break;
+ }
+ /*NOTREACHED*/
+
+ return 0;
+}
+
+
+/*
+ * Uses information in new to fill in new->dl.
+ */
+void
+fill_in_dl_field(DL_CACHE_S *new)
+{
+ AddrScrn_Disp *dl;
+ PerAddrBook *pab;
+ char buf[6*MAX_SCREEN_COLS + 1];
+ char buf2[6*1024];
+ char hostbuf[128];
+ char *folder;
+ char *q;
+ unsigned screen_width = ps_global->ttyo->screen_cols;
+ unsigned got_width, need_width, cellwidth;
+
+ screen_width = MIN(MAX_SCREEN_COLS, screen_width);
+
+ dl = &(new->dl);
+
+ /* free any previously allocated space */
+ switch(dl->type){
+ case Text:
+ case Title:
+ case TitleCmb:
+ case AskServer:
+ if(dl->usst)
+ fs_give((void **)&dl->usst);
+ default:
+ break;
+ }
+
+ /* set up new dl */
+ switch(new->type){
+ case DlcListBlankTop:
+ case DlcListBlankBottom:
+ case DlcGlobDelim1:
+ case DlcGlobDelim2:
+ case DlcDirDelim1:
+ case DlcDirDelim2:
+ case DlcTitleBlankTop:
+ case DlcDirBlankTop:
+ case DlcTitleBlankBottomCmb:
+ case DlcTitleBlankTopCmb:
+ dl->type = Text;
+ dl->usst = cpystr("");
+ break;
+
+ case DlcTitleDashTopCmb:
+ case DlcTitleDashBottomCmb:
+ case DlcDirDelim1a:
+ case DlcDirDelim1c:
+ /* line of dashes in txt field */
+ dl->type = Text;
+ memset((void *)buf, '-', screen_width * sizeof(char));
+ buf[screen_width] = '\0';
+ dl->usst = cpystr(buf);
+ break;
+
+ case DlcNoPermission:
+ dl->type = Text;
+ dl->usst = cpystr(NO_PERMISSION);
+ break;
+
+ case DlcTitle:
+ case DlcTitleNoPerm:
+ dl->type = Title;
+ pab = &as.adrbks[new->adrbk_num];
+ /* title for this addrbook */
+ snprintf(buf, sizeof(buf), " %s", pab->abnick ? pab->abnick : pab->filename);
+ cellwidth = utf8_width(buf);
+ if(cellwidth > screen_width)
+ cellwidth = utf8_truncate(buf, screen_width);
+
+ /* add space padding */
+ if(cellwidth < screen_width)
+ snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%*.*s",
+ screen_width - cellwidth, screen_width - cellwidth, "");
+
+ buf[sizeof(buf)-1] = '\0';
+
+ if(as.ro_warning && (pab->access == NoAccess || pab->access == ReadOnly)){
+ char *str;
+
+ if(pab->access == NoAccess)
+ str = NOACCESS;
+ else
+ str = READONLY;
+
+ need_width = utf8_width(str);
+
+ if(screen_width > need_width && (q = utf8_count_back_width(buf, buf+strlen(buf), need_width, &got_width)) != NULL)
+
+ (void) utf8_pad_to_width(q, str, sizeof(buf)-(q-buf), need_width, 0);
+ }
+
+ buf[sizeof(buf)-1] = '\0';
+ dl->usst = cpystr(buf);
+ break;
+
+ case DlcSubTitle:
+ dl->type = Text;
+ pab = &as.adrbks[new->adrbk_num];
+ /* Find a hostname to put in title */
+ hostbuf[0] = '\0';
+ folder = NULL;
+ if(pab->type & REMOTE_VIA_IMAP && pab->filename){
+ char *start, *end;
+
+ start = strindex(pab->filename, '{');
+ if(start)
+ end = strindex(start+1, '}');
+
+ if(start && end){
+ strncpy(hostbuf, start + 1,
+ MIN(end - start - 1, sizeof(hostbuf)-1));
+ hostbuf[MIN(end - start - 1, sizeof(hostbuf)-1)] = '\0';
+ if(*(end+1))
+ folder = end+1;
+ }
+ }
+
+ if(!folder)
+ folder = pab->filename;
+
+ /*
+ * Just trying to find the name relative to the home directory
+ * to display in an OS-independent way.
+ */
+ if(folder && in_dir(ps_global->home_dir, folder)){
+ char *p, *new_folder = NULL, *savep = NULL;
+ int l, saveval = 0;
+
+ l = strlen(ps_global->home_dir);
+
+ while(!new_folder && (p = last_cmpnt(folder)) != NULL){
+ if(savep){
+ *savep = saveval;
+ savep = NULL;
+ }
+
+ if(folder + l == p || folder + l + 1 == p)
+ new_folder = p;
+ else{
+ savep = --p;
+ saveval = *savep;
+ *savep = '\0';
+ }
+ }
+
+ if(savep)
+ *savep = saveval;
+
+ if(new_folder)
+ folder = new_folder;
+ }
+
+ snprintf(buf, sizeof(buf), " %s AddressBook%s%s in %s",
+ (pab->type & GLOBAL) ? "Global" : "Personal",
+ (pab->type & REMOTE_VIA_IMAP && *hostbuf) ? " on " : "",
+ (pab->type & REMOTE_VIA_IMAP && *hostbuf) ? hostbuf : "",
+ (folder && *folder) ? folder : "<?>");
+ cellwidth = utf8_width(buf);
+ if(cellwidth > screen_width)
+ cellwidth = utf8_truncate(buf, screen_width);
+
+ /* add space padding */
+ if(cellwidth < screen_width)
+ snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%*.*s",
+ screen_width - cellwidth, screen_width - cellwidth, "");
+
+ buf[sizeof(buf)-1] = '\0';
+ dl->usst = cpystr(buf);
+ break;
+
+ case DlcTitleCmb:
+ dl->type = TitleCmb;
+ pab = &as.adrbks[new->adrbk_num];
+ /* title for this addrbook */
+ snprintf(buf, sizeof(buf), "%s AddressBook <%s>",
+ (new->adrbk_num < as.how_many_personals) ?
+ "Personal" :
+ "Global",
+ pab->abnick ? pab->abnick : pab->filename);
+ cellwidth = utf8_width(buf);
+ if(cellwidth > screen_width)
+ cellwidth = utf8_truncate(buf, screen_width);
+
+ /* add space padding */
+ if(cellwidth < screen_width)
+ snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%*.*s",
+ screen_width - cellwidth, screen_width - cellwidth, "");
+
+ buf[sizeof(buf)-1] = '\0';
+
+ if(as.ro_warning && (pab->access == NoAccess || pab->access == ReadOnly)){
+ char *str;
+
+ if(pab->access == NoAccess)
+ str = NOACCESS;
+ else
+ str = READONLY;
+
+ need_width = utf8_width(str);
+
+ if(screen_width > need_width && (q = utf8_count_back_width(buf, buf+strlen(buf), need_width, &got_width)) != NULL)
+
+ (void) utf8_pad_to_width(q, str, sizeof(buf)-(q-buf), need_width, 0);
+ }
+
+ buf[sizeof(buf)-1] = '\0';
+ dl->usst = cpystr(buf);
+ break;
+
+ case DlcDirDelim1b:
+ dl->type = Text;
+ utf8_snprintf(buf, sizeof(buf), "%-*.*w", screen_width, screen_width, OLDSTYLE_DIR_TITLE);
+ dl->usst = cpystr(buf);
+ break;
+
+ case DlcClickHereCmb:
+ dl->type = ClickHereCmb;
+ break;
+
+ case DlcListClickHere:
+ dl->type = ListClickHere;
+ dl->elnum = new->dlcelnum;
+ break;
+
+ case DlcListEmpty:
+ dl->type = ListEmpty;
+ dl->elnum = new->dlcelnum;
+ break;
+
+ case DlcEmpty:
+ dl->type = Empty;
+ break;
+
+ case DlcZoomEmpty:
+ dl->type = ZoomEmpty;
+ break;
+
+ case DlcNoAbooks:
+ dl->type = NoAbooks;
+ break;
+
+ case DlcPersAdd:
+ dl->type = AddFirstPers;
+ break;
+
+ case DlcGlobAdd:
+ dl->type = AddFirstGlob;
+ break;
+
+ case DlcDirAccess:
+ dl->type = AskServer;
+
+#ifdef ENABLE_LDAP
+ {
+ LDAP_SERV_S *info;
+
+ info = break_up_ldap_server(ps_global->VAR_LDAP_SERVERS[new->adrbk_num]);
+ snprintf(buf2, sizeof(buf2), " %s",
+ (info && info->nick && *info->nick) ? info->nick :
+ (info && info->serv && *info->serv) ? info->serv :
+ comatose(new->adrbk_num + 1));
+ if(info)
+ free_ldap_server_info(&info);
+ }
+#else
+ snprintf(buf2, sizeof(buf2), " %s", comatose(new->adrbk_num + 1));
+#endif
+
+ utf8_snprintf(buf, sizeof(buf), "%-*.*w", screen_width, screen_width, buf2);
+ dl->usst = cpystr(buf);
+ break;
+
+ case DlcDirSubTitle:
+ dl->type = Text;
+#ifdef ENABLE_LDAP
+ {
+ LDAP_SERV_S *info;
+
+ info = break_up_ldap_server(ps_global->VAR_LDAP_SERVERS[new->adrbk_num]);
+ if(info && info->port >= 0)
+ snprintf(buf2, sizeof(buf2), " Directory Server on %s:%d",
+ (info && info->serv && *info->serv) ? info->serv : "<?>",
+ info->port);
+ else
+ snprintf(buf2, sizeof(buf2), " Directory Server on %s",
+ (info && info->serv && *info->serv) ? info->serv : "<?>");
+
+ if(info)
+ free_ldap_server_info(&info);
+ }
+#else
+ snprintf(buf2, sizeof(buf2), " Directory Server %s",
+ comatose(new->adrbk_num + 1));
+#endif
+
+ utf8_snprintf(buf, sizeof(buf), "%-*.*w", screen_width, screen_width, buf2);
+ dl->usst = cpystr(buf);
+ break;
+
+ case DlcSimple:
+ dl->type = Simple;
+ dl->elnum = new->dlcelnum;
+ break;
+
+ case DlcListHead:
+ dl->type = ListHead;
+ dl->elnum = new->dlcelnum;
+ break;
+
+ case DlcListEnt:
+ dl->type = ListEnt;
+ dl->elnum = new->dlcelnum;
+ dl->l_offset = new->dlcoffset;
+ break;
+
+ case DlcBeginning:
+ case DlcOneBeforeBeginning:
+ case DlcTwoBeforeBeginning:
+ dl->type = Beginning;
+ break;
+
+ case DlcEnd:
+ dl->type = End;
+ break;
+
+ default:
+ q_status_message(SM_ORDER | SM_DING, 5, 10,
+ _("Bug in addrbook, not supposed to happen, re-syncing..."));
+ dprint((1, "Bug in addrbook, impossible dflt in fill_in_dl (%d)\n",
+ new->type));
+ /* jump back to a safe starting point */
+ longjmp(addrbook_changed_unexpectedly, 1);
+ /*NOTREACHED*/
+ }
+}
+
+
+/*
+ * Returns a pointer to the member_number'th list member of the list
+ * associated with this display line.
+ */
+char *
+listmem(long int row)
+{
+ PerAddrBook *pab;
+ AddrScrn_Disp *dl;
+
+ dl = dlist(row);
+ if(dl->type != ListEnt)
+ return((char *)NULL);
+
+ pab = &as.adrbks[adrbk_num_from_lineno(row)];
+
+ return(listmem_from_dl(pab->address_book, dl));
+}
+
+
+/*
+ * Returns a pointer to the list member
+ * associated with this display line.
+ */
+char *
+listmem_from_dl(AdrBk *address_book, AddrScrn_Disp *dl)
+{
+ AdrBk_Entry *abe;
+ char **p = (char **)NULL;
+
+ /* This shouldn't happen */
+ if(dl->type != ListEnt)
+ return((char *)NULL);
+
+ abe = adrbk_get_ae(address_book, (a_c_arg_t) dl->elnum);
+
+ /*
+ * If we wanted to be more careful, We'd go through the list making sure
+ * we don't pass the end. We'll count on the caller being careful
+ * instead.
+ */
+ if(abe && abe->tag == List){
+ p = abe->addr.list;
+ p += dl->l_offset;
+ }
+
+ return((p && *p) ? *p : (char *)NULL);
+}
+
+
+/*
+ * How many members in list?
+ */
+adrbk_cntr_t
+listmem_count_from_abe(AdrBk_Entry *abe)
+{
+ char **p;
+
+ if(abe->tag != List)
+ return 0;
+
+ for(p = abe->addr.list; p != NULL && *p != NULL; p++)
+ ;/* do nothing */
+
+ return((adrbk_cntr_t)(p - abe->addr.list));
+}
+
+
+/*
+ * Return a ptr to the row'th line of the global disp_list.
+ * Line numbers count up but you can't count on knowing which line number
+ * goes with the first or the last row. That is, row 0 is not necessarily
+ * special. It could be before the rows that make up the display list, after
+ * them, or anywhere in between. You can't tell what the last row is
+ * numbered, but a dl with type End is returned when you get past the end.
+ * You can't tell what the number of the first row is, but if you go past
+ * the first row a dl of type Beginning will be returned. Row numbers can
+ * be positive or negative. Their values have no meaning other than how
+ * they line up relative to other row numbers.
+ */
+AddrScrn_Disp *
+dlist(long int row)
+{
+ DL_CACHE_S *dlc = (DL_CACHE_S *)NULL;
+
+ dlc = get_dlc(row);
+
+ if(dlc){
+ fill_in_dl_field(dlc);
+ return(&dlc->dl);
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 5, 10,
+ _("Bug in addrbook, not supposed to happen, re-syncing..."));
+ dprint((1, "Bug in addrbook (null dlc in dlist(%ld), not supposed to happen\n", row));
+ /* jump back to a safe starting point */
+
+ dprint((1, "dlist: panic: initialized %d, n_addrbk %d, cur_row %d, top_ent %ld, ro_warning %d, no_op_possbl %d\n",
+ as.initialized, as.n_addrbk, as.cur_row, as.top_ent, as.ro_warning, as.no_op_possbl));
+
+ longjmp(addrbook_changed_unexpectedly, 1);
+ /*NOTREACHED*/
+ }
+}
+
+
+/*
+ * Returns the index of the current address book.
+ */
+int
+adrbk_num_from_lineno(long int lineno)
+{
+ DL_CACHE_S *dlc;
+
+ dlc = get_dlc(lineno);
+
+ return(dlc->adrbk_num);
+}