text: #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-2018 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);
  }
  