/* Program Name            : FILERQUE.C                                 */
/*   Original Author       : C. K. Hung					*/
/*   Date                  : AUG-5-1991					*/
/*   Program Description   :                                            */
/*                         :                                            */
/* Revision History follows                                             */
 
#include "global.h"
#include "dx.h"
#include "filerque.h"
#include "filer.h"
#include "filerflt.h"
#include "filersta.h"
#include "findfile.h"
#include <libdef.h>
 
/*
**
**  INTERNAL FUNCTION PROTOTYPING
**
**/
 
static int	initialize_filer_cache$1(struct filer_cache_tag *);
static int	search_filer_cache$1(struct cur_filter_tag, struct cur_filter_tag);
static int	cmp_filespec(struct filespec_list_tag *, struct filespec_list_tag *);
static int      display_filer_cache(int, struct display_tag);
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	    initialize_filer_cache()
{
    int i;
 
    for (i = 0;  i < MAXBUFFER;  i++)
    {
	initialize_filer_cache$1(&filer_cache[i]);
    }
     return DX__NORMAL;
}
 
 
/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
static int	    initialize_filer_cache$1(e)
struct filer_cache_tag *e;
{
    convert_date_string (
	"17-NOV-1858 00:00:00.00", &e->time_stamp);
    e->sortby = sortname;
    e->sortorder = ascending;
    e->cur_dir[0] = EOS;
    e->par_dir[0] = EOS;
    e->tot_files = 0;
    e->tot_blocks = 0;
    e->dpro = 0xFFFF;
    e->subdir = 0;
	
    e->cur_filter.name_filespec_list = NULL;
    e->cur_filter.exclude_filespec_list = NULL;
    e->cur_filter.date = DX_CREDATE;
    convert_date_string (
	"31-DEC-2857 00:00:00.00",
	&e->cur_filter.before);
    convert_date_string (
	"17-NOV-1858 00:00:00.00",
	&e->cur_filter.since);
    e->cur_filter.maximum = 1073741823;
    e->cur_filter.minimum = 0;
 
    e->dir_filelist = NULL;
    e->non_dir_filelist = NULL;
    e->exclude_filelist = NULL;
     return DX__NORMAL;
}
 
 
/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      find_LRU_in_filer_cache() find the Least-Recently-Used slot
**	in the directory cache.
**
**--
**/
int	find_LRU_in_filer_cache()
{
    DATE_TIME earliest_time,
	      cmp;
    int m,
        n;
 
    m = 0;
    memcpy (&earliest_time, &filer_cache[0].time_stamp, sizeof (DATE_TIME));
    for (n = 1;  n < MAXBUFFER;  n++)
    {
        if (lib$sub_times(
                &filer_cache[n].time_stamp,
                &earliest_time,
                &cmp) == LIB$_NEGTIM)
	{
	    memcpy(
		&earliest_time, &filer_cache[n].time_stamp, sizeof (DATE_TIME));
	    m = n;
	}
    }
 
    /*
    **  Free up memory used by this entry
    **/
 
    free_filespec_list(&filer_cache[m].cur_filter.name_filespec_list);
    free_filespec_list(&filer_cache[m].cur_filter.exclude_filespec_list);
    free_filelist(&filer_cache[m].dir_filelist);
    free_filelist(&filer_cache[m].non_dir_filelist);
    free_filelist(&filer_cache[m].exclude_filelist);
 
    return m;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	search_filer_cache(e)
struct cur_filter_tag e;
{
    int i;
 
    for (i = 0;  i < MAXBUFFER;  i++)
    {
        if (search_filer_cache$1(e, filer_cache[i].cur_filter))
        {
            return i;
        }
    }
 
    return -1;	    /**  Not found  **/
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
static int	search_filer_cache$1(e, f)
struct cur_filter_tag e,
		      f;
{
    if (cmp_filespec(e.name_filespec_list, f.name_filespec_list) ||
	cmp_filespec(e.exclude_filespec_list, f.exclude_filespec_list) ||
	memcmp (&e.before, &f.before, sizeof (DATE_TIME)) ||
	memcmp (&e.since, &f.since, sizeof (DATE_TIME)) ||
	e.maximum != f.maximum ||
	e.minimum != f.minimum )
    {
	return 0;
    }
    return 1;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
static int	cmp_filespec(l, m)
struct filespec_list_tag *l,
			 *m;
{
    struct filespec_list_tag *p,
			     *q;
    struct w_dids_tag *a,
		      *b;
    int found, found1;
 
    for (p = l;  p != (struct filespec_list_tag *)NULL;  p = p->next)
    {
	found = 0;
	for (q = m;  !found && q != (struct filespec_list_tag *)NULL;  q = q->next)
        {
            if (!strcmp (p->name, q->name))
            {
		for (a = p->w_dids;  a != (struct w_dids_tag *)NULL;  a = a->next)
                {
                    found1 = 0;
		    for (b = q->w_dids;  !found1 && b != (struct w_dids_tag *)NULL;  b = b->next)
		    {
			if (!strcmp (a->equivalent_name, b->equivalent_name))
			{
			    found1++;
			}
		    }
		    if (!found1)
		    {
			goto loop1;
		    }
		}
		found++;
            }
loop1:;
        }
        if (!found)
        {
            return -1;
        }
    }
 
    for (p = m;  p != (struct filespec_list_tag *)NULL;  p = p->next)
    {
	found = 0;
	for (q = l;  !found && q != (struct filespec_list_tag *)NULL;  q = q->next)
        {
            if (!strcmp (p->name, q->name))
            {
		for (a = p->w_dids;  a != (struct w_dids_tag *)NULL;  a = a->next)
                {
                    found1 = 0;
		    for (b = q->w_dids;  !found1 && b != (struct w_dids_tag *)NULL;  b = b->next)
		    {
			if (!strcmp (a->equivalent_name, b->equivalent_name))
			{
			    found1++;
			}
		    }
		    if (!found1)
		    {
			goto loop2;
		    }
		}
		found++;
            }
loop2:;
        }
        if (!found)
        {
            return -1;
        }
    }
    return 0;	    /**  Two identical namespec list  **/
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	    save_to_filer_cache(n, e)
int n;
struct dir_dx_tag e;
{
    check_OK(sys$gettim (       /**  Time stamp  **/
		&filer_cache[n].time_stamp))
    filer_cache[n].sortby = e.sortby;
    filer_cache[n].sortorder = e.sortorder;
    strcpy(filer_cache[n].cur_dir, e.cur_dir);
    strcpy(filer_cache[n].par_dir, e.par_dir);
    filer_cache[n].tot_files = e.tot_files;
    filer_cache[n].tot_blocks = e.tot_blocks;
    filer_cache[n].dpro = e.dpro;
    filer_cache[n].subdir = e.subdir;
    filer_cache[n].cur_filter.date = e.cur_filter.date;
    memcpy(
	&filer_cache[n].cur_filter.before,
	&e.cur_filter.before,
	sizeof (DATE_TIME));
    memcpy(
	&filer_cache[n].cur_filter.since,
	&e.cur_filter.since,
	sizeof (DATE_TIME));
    filer_cache[n].cur_filter.maximum = e.cur_filter.maximum;
    filer_cache[n].cur_filter.minimum = e.cur_filter.minimum;
 
    copy_filespec_list(
	e.cur_filter.name_filespec_list,
	&filer_cache[n].cur_filter.name_filespec_list);
    copy_filespec_list(
	e.cur_filter.exclude_filespec_list,
	&filer_cache[n].cur_filter.exclude_filespec_list);
 
    copy_filelist(
	e.dir_filelist,
	&filer_cache[n].dir_filelist,
	e.sortby,
	e.sortorder);
    copy_filelist(
	e.non_dir_filelist,
	&filer_cache[n].non_dir_filelist,
	e.sortby,
	e.sortorder);
    copy_filelist(
	e.exclude_filelist,
	&filer_cache[n].exclude_filelist,
	e.sortby,
	e.sortorder);
     return DX__NORMAL;
}
 
 
/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	    restore_from_filer_cache(n, d)
int n;
struct dir_dx_tag *d;
{
    d->sortby = filer_cache[n].sortby;
    d->sortorder = filer_cache[n].sortorder;
    strcpy(d->cur_dir, filer_cache[n].cur_dir);
    strcpy(d->par_dir, filer_cache[n].par_dir);
    d->tot_files = filer_cache[n].tot_files;
    d->tot_blocks = filer_cache[n].tot_blocks;
    d->dpro = filer_cache[n].dpro;
    d->subdir = filer_cache[n].subdir;
    d->cur_filter.date = filer_cache[n].cur_filter.date;
    memcpy(
	&d->cur_filter.before,
	&filer_cache[n].cur_filter.before,
	sizeof (DATE_TIME));
    memcpy(
	&d->cur_filter.since,
	&filer_cache[n].cur_filter.since,
	sizeof (DATE_TIME));
    d->cur_filter.maximum = filer_cache[n].cur_filter.maximum;
    d->cur_filter.minimum = filer_cache[n].cur_filter.minimum;
 
    free_filespec_list(&d->cur_filter.name_filespec_list);
    copy_filespec_list(
	filer_cache[n].cur_filter.name_filespec_list,
	&d->cur_filter.name_filespec_list);
    free_filespec_list(&d->cur_filter.exclude_filespec_list);
    copy_filespec_list(
	filer_cache[n].cur_filter.exclude_filespec_list,
	&d->cur_filter.exclude_filespec_list);
 
    free_filelist(&d->dir_filelist);
    copy_filelist(
	filer_cache[n].dir_filelist,
	&d->dir_filelist,
	filer_cache[n].sortby,
	filer_cache[n].sortorder);
    free_filelist(&d->non_dir_filelist);
    copy_filelist(
	filer_cache[n].non_dir_filelist,
	&d->non_dir_filelist,
	filer_cache[n].sortby,
	filer_cache[n].sortorder);
    free_filelist(&d->exclude_filelist);
    copy_filelist(
	filer_cache[n].exclude_filelist,
	&d->exclude_filelist,
	filer_cache[n].sortby,
	filer_cache[n].sortorder);
     return DX__NORMAL;
}
 
 
/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	    update_filer_cache(e, func, errmsg)
struct fil_dx_tag e;
int (*func)();
char *errmsg;
{
    int i;
    struct filespec_list_tag *s, *u;
    struct w_dids_tag *t, *v;
    int allocate_size;
 
    /*
    **	Search queue for update
    **/
 
    for (i = 0;  i < MAXBUFFER;  i++)
    {
        for (s = filer_cache[i].cur_filter.name_filespec_list;
	    s;  s = s->next, u = s)
        {
            for (t = s->w_dids;  t;  t = t->next, v = t)
            {
                if (t->did[0] == e.did[0] &&
		    t->did[1] == e.did[1] &&
		    t->did[2] == e.did[2])
                {
                    if ((*func)(e, &filer_cache[i], errmsg) == DX__ERROR)
                    {
                        return DX__ERROR;
                    }
                }
		else if (t->did[0] == e.fid[0] &&
			 t->did[1] == e.fid[1] &&
			 t->did[2] == e.fid[2] &&
			 *func == delete_from_filer_cache)
		{
		    if (t == s->w_dids)
		    {
			s->w_dids = t->next;
		    }
		    else
		    {
		    	v->next = t->next;
		    }
		    allocate_size = sizeof (struct w_dids_tag);
		    check_OK(lib$free_vm (
			         &allocate_size,
			         &t,
			         0))
		}
            }
	    if (s->w_dids == NULL)
	    {
		if (s == filer_cache[i].cur_filter.name_filespec_list)
		{
		    filer_cache[i].cur_filter.name_filespec_list = s->next;
		}
		else
		{
		    u->next = s->next;
		}
		allocate_size = sizeof (struct filespec_list_tag);
		check_OK(lib$free_vm (
			 	&allocate_size,
			 	&s,
			 	0))
	    }
        }
	if (filer_cache[i].cur_filter.name_filespec_list == NULL)
	{
	    initialize_filer_cache$1(&filer_cache[i]);
	}
    }
 
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	    add_to_filer_cache(e, cachep, errmsg)
struct fil_dx_tag e;
struct filer_cache_tag *cachep;
char *errmsg;
{
    int total_dids;
    char equ_name[MAXFILESPEC+1];
    int total_devices;
    char dev_name[MAXFILESPEC+1];
    unsigned short len;
 
    char fn[NAM$C_MAXRSS];
    struct filespec_list_tag *s;
    struct w_dids_tag *t;
 
    /*
    **	Find out the number of equivalent names of current directory
    **/
 
    total_dids = 0;
    equ_name[0] = EOS;
    total_devices = 0;
    dev_name[0] = EOS;
    for (s = cachep->cur_filter.name_filespec_list;  s;  s = s->next)
    {
        for (t = s->w_dids;  t;  t = t->next)
        {
            if (strcmp (t->equivalent_name, equ_name))
            {
                total_dids++;
                strcpy (equ_name, t->equivalent_name);
            }
	    len = strcspn (t->equivalent_name, "[");
            if (strncmp (t->equivalent_name, dev_name, len))
            {
                total_devices++;
                strncpy (dev_name, t->equivalent_name, len);
            }
            if (t->did[0] == e.did[0] &&
		t->did[1] == e.did[1] &&
		t->did[2] == e.did[2])
            {
                strcpy(fn, t->equivalent_name);
            }
        }
    }
 
    if (!strcmp(strrchr(e.fn, '.'), ".DIR;1"))
    {	    /**  Insert a directory file  **/
	char *cp;
	char s[MAXFILESPEC+1];
 
	strcpy(s, e.fn);
	cp = strrchr(s, '.');
	*cp = ']';
	*++cp = EOS;
        if (total_dids <= 1)
        {
	    sprintf(e.fn, "[.%s", s);
        }
        else
        {
            if (total_devices <= 1)
            {
                strcpy (e.fn, strchr (fn, '['));
            }
	    else
	    {
		strcpy (e.fn, fn);
	    }
	    e.fn[strlen(e.fn)-1] = '.';
	    strcat (e.fn, s);
        }
 
        if (insert_filelist_entry(
		cachep->dir_filelist,
		e,
		cachep->sortby,
		cachep->sortorder) == DX__NORMAL)
        {
            cachep->tot_files++;
	    cachep->tot_blocks += e.filesize;
	    cachep->subdir++;
        }
    }
    else
    {
	if (find_exclude_file(	    /**  Update exclude list  **/
		&(cachep->exclude_filelist),
		cachep->cur_filter,
		cachep->sortby,
		cachep->sortorder) == DX__ERROR)
	{
	    strcpy(
		errmsg, "Insufficient memory to update the exclude file list");
	    return DX__ERROR;
	}
 
	strcat(fn, e.fn);	    /**  Get full path name  **/
 
	if (find_queue_file(e.fid, cachep))
	{
	    if (total_dids > 1)
	    {
		if (total_devices <= 1)
		{
		    strcpy(e.fn, strchr(fn, '['));
		}
		else
		{
		    strcpy(e.fn, fn);
		}
	    }
 
	    if (filter_OK(e, cachep->exclude_filelist, cachep->cur_filter))
	    {
		switch (insert_filelist_entry(
			    cachep->non_dir_filelist,
			    e,
			    cachep->sortby,
			    cachep->sortorder))
		{
		    case DX__ERROR:
			strcpy(
			    errmsg, "Insufficient memory to insert a new file");
			return DX__ERROR;
		    case DX__NORMAL:
			cachep->tot_files++;
			cachep->tot_blocks += e.filesize;
			break;
		    default:    /**  Don't care  **/
			;
		}
	    }
	}
    }
 
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	    delete_from_filer_cache(e, cachep, errmsg)
struct fil_dx_tag e;
struct filer_cache_tag *cachep;
char *errmsg;
{
    struct fil_dx_tag *p, *q;
 
    /*
    **	Find the file entry.
    **	Search directory file first, then search non-directory file.
    **/
 
    for (p = cachep->dir_filelist->forward;
	 p != cachep->non_dir_filelist; p = p->forward)
    {
        if (p == cachep->dir_filelist)
        {
            p = cachep->non_dir_filelist;
        }
	else
	{
	    if (e.fid[0] == p->fid[0] &&
		e.fid[1] == p->fid[1] &&
		e.fid[2] == p->fid[2])
	    {
		cachep->tot_files--;
		cachep->tot_blocks -= p->filesize;
		remove_filelist_entry(&p);
		break;
	    }
	}
    }
 
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	    change_to_filer_cache(e, cachep, errmsg)
struct fil_dx_tag e;
struct filer_cache_tag *cachep;
char *errmsg;
{
    struct fil_dx_tag *p, *q;
 
    /*
    **	Find the file entry.
    **	Search directory file first, then search non-directory file.
    **/
 
    for (p = cachep->dir_filelist->forward;
	 p != cachep->non_dir_filelist; p = p->forward)
    {
        if (p == cachep->dir_filelist)
        {
            p = cachep->non_dir_filelist;
        }
	else
	{
	    if (e.fid[0] == p->fid[0] &&
		e.fid[1] == p->fid[1] &&
		e.fid[2] == p->fid[2])
	    {
		p->fpro = e.fpro;
		cachep->tot_blocks += e.filesize - p->filesize;
		p->filesize = e.filesize;
		strcpy(p->fn, e.fn);
		break;
	    }
	}
    }
 
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
void	    copy_filespec_list(from, to)
struct filespec_list_tag *from,
			 **to;
 
{
    struct filespec_list_tag *r;
 
    *to = (struct filesec_list_tag *)NULL;
    for (r = from; r != (struct filespec_list_tag *)NULL;  r = r->next)
    {
	struct filespec_list_tag t;
	struct w_dids_tag *u;
 
	strcpy(t.name, r->name);
	t.w_dids = (struct w_dids_tag *)NULL;
	t.next = (struct filespec_list_tag *)NULL;
        for (u = r->w_dids;  u != (struct w_dids_tag *)NULL;  u = u->next)
        {
            insert_dids(&t.w_dids, u->equivalent_name, u->did, u->chan);
        }
        insert_filespec_list$1(to, t);
    }
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
void	    copy_filelist(from, to, sortby, sortorder)
struct fil_dx_tag *from,
		  **to;
enum sortbys sortby;
enum sortorders sortorder;
{
    struct fil_dx_tag *p,
		      *q;	
 
    q = create_MARKER();
    *q = *from;
    q->backward = q->forward = q;
    for (p = from->forward; p != from; p = p->forward)
    {
        insert_filelist_entry(q, *p, sortby, sortorder);
    }
    *to = q;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	    select_from_filer_cache()
{
    struct display_tag list_cache_display =
	{
	    0,					    /**  id		     **/
	    cntrl_info_block.pasteboard_rows,	    /**  rows		     **/
	    cntrl_info_block.pasteboard_width,	    /**  width		     **/
	    1,					    /**  paste row	     **/
	    1,					    /**  paste column	     **/
	    0,					    /**  unused		     **/
	    0,					    /**  unused		     **/
	    0,					    /**  unused		     **/
	    0					    /**  unused		     **/
	};
    int i;
    unsigned short int word_terminator_code;
 
    check_OK(smg$create_virtual_display (
		  &list_cache_display.rows,
		  &list_cache_display.width,
		  &list_cache_display.id,
		  &SMG$M_TRUNC_ICON,
		  0,
		  0))
 
    check_OK(smg$paste_virtual_display (
		  &list_cache_display.id,
		  &cntrl_info_block.pasteboard_id,
		  &list_cache_display.beg_y,
		  &list_cache_display.beg_x,
		  0))
 
    i = 0;
    display_filer_cache(i, list_cache_display);
    check_OK(smg$put_chars (
	     	  &list_cache_display.id,
	     	  $DESCR (
  "Press ARROW KEYS for prev or next, RETURN to select, or keypad 0 to cancel"),
	     	  &list_cache_display.rows,
	     	  &1,
	     	  &SMG$M_ERASE_LINE,
	     	  &SMG$M_REVERSE,
	     	  0,
	     	  0))
    smg$read_keystroke (
    	&cntrl_info_block.keyboard_id,
    	&word_terminator_code,
    	0,
    	0,
	&list_cache_display.id,
    	0,
    	0);
 
    /*
    **  Keep typing until exit by the user
    **/
 
    while (1)
    {
        switch (word_terminator_code)
        {
            case SMG$K_TRM_UP:
		if (i == 0)
		{
		    check_OK(smg$ring_bell (
			         &list_cache_display.id,
			         0))
		    i = MAXBUFFER-1;
		}
		else
		{
		    i--;
		}
		display_filer_cache (i, list_cache_display);
		break;
	    case SMG$K_TRM_DOWN:
		if (i == MAXBUFFER-1)
		{
		    check_OK(smg$ring_bell (
			         &list_cache_display.id,
			         0))
		    i = 0;
		}
		else
		{
		    i++;
		}
		display_filer_cache (i, list_cache_display);
		break;
	    case SMG$K_TRM_CR:
	    case SMG$K_TRM_ENTER:
		if (filer_cache[i].cur_filter.name_filespec_list == NULL)
		{	/**  Buffer is empty  **/
                    check_OK(smg$ring_bell (
                                 &list_cache_display.id,
                                 0))
		}
		else
		{
		    check_OK(smg$erase_display (
			      &list_cache_display.id,
			      0,
			      0,
			      0,
			      0))
 
		    check_OK(smg$delete_virtual_display (
			      &list_cache_display.id))
		    return i;
		}
		break;
	    case SMG$K_TRM_KP0:
	    case SMG$K_TRM_CTRLZ:
		check_OK(smg$erase_display (
			  &list_cache_display.id,
			  0,
			  0,
			  0,
			  0))
 
		check_OK(smg$delete_virtual_display (
			  &list_cache_display.id))
		return -1;
		break;
	    case SMG$K_TRM_CTRLW:
		check_OK(smg$repaint_screen (
			      &list_cache_display.id))
		check_OK(smg$repaint_screen (
			      &list_cache_display.id))
		break;
	    default :
		check_OK(smg$ring_bell (
			     &list_cache_display.id,
			     0))
		break;
	}
	check_OK(smg$put_chars (
		      &list_cache_display.id,
		      $DESCR (
  "Press ARROW KEYS for prev or next, RETURN to select, or keypad 0 to cancel"),
		      &list_cache_display.rows,
		      &1,
		      &SMG$M_ERASE_LINE,
		      &SMG$M_REVERSE,
		      0,
		      0))
	smg$read_keystroke (
	    &cntrl_info_block.keyboard_id,
	    &word_terminator_code,
	    0,
	    0,
	    &list_cache_display.id,
	    0,
	    0);
    }
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
static int	    display_filer_cache(i, list_display)
int i;
struct display_tag list_display;
{
    char numstr[3];
    $DESCRIPTOR (numstr_descrip, numstr);
    int numstr_column;
    unsigned short int timbuf[7];
    char line[256];
    $DESCRIPTOR (line_descrip, line);
    struct filespec_list_tag *p;
    struct w_dids_tag *q;
 
    check_OK(smg$home_cursor (
	     	  &list_display.id,
	     	  0))
    /**  Filter by  **/
    sprintf(line, "Buffer: ");
    if (filer_cache[i].cur_filter.name_filespec_list == NULL)
    {
	strcat(line, "?????");
    }
    else
    {
	p = filer_cache[i].cur_filter.name_filespec_list;
	strcat(line, p->w_dids->equivalent_name);
	strcat(line, p->name);
	for (q = p->w_dids->next; q;  q = q->next)
	{
	    strcat(line, ", ");
	    strcat(line, q->equivalent_name);
	    strcat(line, p->name);
	}
	for (p = p->next;  p;  p = p->next)
	{
	    for (q = p->w_dids; q; q = q->next)
	    {
		strcat(line, ", ");
		strcat(line, q->equivalent_name);
		strcat(line, p->name);
	    }
	}
    }
    numstr_column = list_display.width-1;
    if (strlen(line) >= numstr_column-1)
    {
	line[numstr_column-3] = '~';
	line[numstr_column-2] = EOS;
    }
    LENGTH(line_descrip) = strlen(line);
    check_OK(smg$put_chars (
	     	  &list_display.id,
	     	  &line_descrip,
	     	  &1,
	     	  &1,
	     	  &SMG$M_ERASE_TO_EOL,
	     	  0,
	     	  0,
	     	  0))
    sprintf(numstr, "%2.2d", i+1);
    check_OK(smg$put_chars (
		  &list_display.id,
		  &numstr_descrip,
		  &1,
		  &numstr_column,
		  0,
		  &SMG$M_BOLD,
		  0,
		  0))
    check_OK(smg$set_cursor_abs (
	     	  &list_display.id,
	     	  &3,
	     	  &1))
 
    /**  Excluded  **/
    sprintf(line, "%23s : ", "Excluded");
    if (filer_cache[i].cur_filter.exclude_filespec_list != NULL)
    {
	p = filer_cache[i].cur_filter.exclude_filespec_list;
	strcat(line, p->name);
	for (p = p->next;  p;  p = p->next)
	{
	    strcat(line, ", ");
	    strcat(line, p->name);
	}
    }
    LENGTH(line_descrip) = strlen(line);
    check_OK(smg$put_line (
	     	  &list_display.id,
	     	  &line_descrip,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0))
 
    /**  Since  **/
    sprintf(line, "%23s : ", "Since");
    check_OK(sys$numtim (
		  timbuf,
		  &filer_cache[i].cur_filter.since))
    if (!(timbuf[0] == 1858 && timbuf[1] == 11 && timbuf[2] == 17))
    {
	sprintf(line, "%s%2.2d/%02.2d/%02.2d %2.2d:%02.2d:%02.2d",
		    line, timbuf[0]-1900, timbuf[1], timbuf[2],
		    timbuf[3], timbuf[4], timbuf[5]);
    }
    LENGTH(line_descrip) = strlen(line);
    check_OK(smg$put_line (
	     	  &list_display.id,
	     	  &line_descrip,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0))
 
    /**  Before  **/
    sprintf(line, "%23s : ", "Before");
    check_OK(sys$numtim (
		  timbuf,
		  &filer_cache[i].cur_filter.before))
    if (!(timbuf[0] == 2857 && timbuf[1] == 12 && timbuf[2] == 31))
    {
	sprintf(line, "%s%2.2d/%02.2d/%02.2d %2.2d:%02.2d:%02.2d",
		    line, timbuf[0]-1900, timbuf[1], timbuf[2],
		    timbuf[3], timbuf[4], timbuf[5]);
    }
    LENGTH(line_descrip) = strlen(line);
    check_OK(smg$put_line (
	     	  &list_display.id,
	     	  &line_descrip,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0))
 
    /**  Maximum  **/
    sprintf(line, "%23s : ", "Less than");
    if (filer_cache[i].cur_filter.name_filespec_list != NULL &&
	filer_cache[i].cur_filter.maximum != 1073741823)
    {
	sprintf(line, "%s%-d", line, filer_cache[i].cur_filter.maximum);
    }
    LENGTH(line_descrip) = strlen(line);
    check_OK(smg$put_line (
	     	  &list_display.id,
	     	  &line_descrip,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0))
 
    /**  Minimum  **/
    sprintf(line, "%23s : ", "Larger than");
    if (filer_cache[i].cur_filter.name_filespec_list != NULL &&
	filer_cache[i].cur_filter.minimum != 0)
    {
    	sprintf(line, "%s%-d", line, filer_cache[i].cur_filter.minimum);
    }
    LENGTH(line_descrip) = strlen(line);
    check_OK(smg$put_line (
	     	  &list_display.id,
	     	  &line_descrip,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0))
    check_OK(smg$put_line (
	     	  &list_display.id,
	     	  $DESCR (""),
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0))
 
    /**  Sort by  **/
    sprintf(line, "%23s : ", "Sort by");
    if (filer_cache[i].cur_filter.name_filespec_list != NULL)
    {
	switch (filer_cache[i].sortby)
	{
	    case sortname:
		strcat(line, "Name");
		break;
	    case sorttype:
		strcat(line, "Type");
		break;
	    case sortsize:
		strcat(line, "Size");
		break;
	    case sortmark:
		strcat(line, "Marked");
		break;
	    case sortcredate:
	    case sortrevdate:
	    case sortexpdate:
	    case sortbakdate:
		strcat(line, "Date");
		break;
	    default:
		;
	}
    }
    LENGTH(line_descrip) = strlen(line);
    check_OK(smg$put_line (
	     	  &list_display.id,
	     	  &line_descrip,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0))
 
    /**  Order by  **/
    sprintf(line, "%23s : ", "Order by");
    if (filer_cache[i].cur_filter.name_filespec_list != NULL)
    {
	if (filer_cache[i].sortorder ==  ascending)
	{
	    strcat(line, "Ascending");
	}
	else
	{
	    strcat(line, "Descending");
	}
    }
    LENGTH(line_descrip) = strlen(line);
    check_OK(smg$put_line (
	     	  &list_display.id,
	     	  &line_descrip,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0))
 
    /**  Last saved  **/
    sprintf(line, "%23s : ", "Last saved");
    check_OK(sys$numtim (
		  timbuf,
		  &filer_cache[i].time_stamp))
    if (!(timbuf[0] == 1858 && timbuf[1] == 11 && timbuf[2] == 17))
    {
	sprintf(line, "%s%2.2d/%02.2d/%02.2d %2.2d:%02.2d:%02.2d",
		    line, timbuf[0]-1900, timbuf[1], timbuf[2],
		    timbuf[3], timbuf[4], timbuf[5]);
    }
    LENGTH(line_descrip) = strlen(line);
    check_OK(smg$put_line (
	     	  &list_display.id,
	     	  &line_descrip,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0))
 
    /**  Total files  **/
    sprintf(line, "%23s : ", "Total of");
    if (filer_cache[i].cur_filter.name_filespec_list != NULL)
    {
	sprintf(line, "%s%-d %s, ",
		line,
		filer_cache[i].subdir,
		(filer_cache[i].subdir <= 1? "directory" : "directories"));
	sprintf(
	    line,
	    "%s%-d files, %-d blocks",
	    line,
	    filer_cache[i].tot_files,
	    filer_cache[i].tot_blocks);
    }
    LENGTH(line_descrip) = strlen(line);
    check_OK(smg$put_line (
	     	  &list_display.id,
	     	  &line_descrip,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0,
	     	  0))
     return DX__NORMAL;
}
