/* Program Name            : DIRECT.C                                   */
/*   Original Author       : C. K. Hung                                 */
/*   Date                  : 7-12-89                                    */
/*   Program Description   :                                            */
/*                         :                                            */
/* References                                                           */
/*   Files open for Input  :                                            */
/*   Files open for Output :                                            */
/*   Modules Referenced    :                                            */
/* Revision History follows                                             */
 
 
#include "global.h"
#include "direct.h"
#include "directque.h"
#include "directkpd.h"
#include "dx.h"
#include "filer.h"
#include "inquire.h"
#include <signal.h>
 
 
/* **  GLOBAL DECLARATION ** */
struct node_tag		*root,	     /**  Root directory		     **/
			*cwdnode;    /**  Current working directory	     **/
 
enum directions dirtree_search_direction;
char dirtree_search_pattern[MAXFILESPEC+1];
 
 
/* ****  VIRTUAL DISPLAY IDs *** */
struct display_tag 	dirtree_cmds_display;
struct display_tag 	dirtree_display;
 
 
/* ****  INTERNAL FUNCTION PROTOTYPING *** */
 
static int		traverse_tree$1(struct node_tag *, struct node_tag *,
				int, int, enum directions);
 
static int		direct_alarm_working$1();
 
 
 
/* **  FUNCTIONAL DESCRIPTION: * */
 
int	    filer_direct()
{
    char initial_root[MAXFILESPEC+1];		/**  Initial root directory  **/
    int n;
    unsigned long status_flags;
    int status;
    char errmsg[MAXFILESPEC+1];
    int (*cstat)();
 
    /*
    **	Output 'working...' message if total processing time
    **	is longer than 3 seconds.  Needs to disable broadcast message
    **	first to avoid interference.
    **/
#ifndef DEBUG
    check_OK(smg$disable_broadcast_trapping (
                 &cntrl_info_block.pasteboard_id))
 
    cstat = signal(SIGALRM, filter_alarm_working);
    alarm(1);
#endif
    /*
    **	Start building the directory tree.
    **	Find the root directory.
    **/
 
    find_root(DX_CURRENT_DIRECTORY.cur_dir, initial_root);
 
    /*
    **	Assign new value related to the directory tree.
    **	Do not free up the memory used by the tree.  it might
    **	be used the cache queue.
    **/
 
    if ((n = search_direct_cache(initial_root)) == -1)
    {	    /**  This is a new directory tree **/
	dirtree_search_direction = advance;
	strcpy(dirtree_search_pattern, "[]");
	root = (struct node *)NULL;
	status = build_dirtree(&root, initial_root);
 
	if (status == DX__ERROR)
	{
	    signal_err("Insufficient virtual memory", bell);
	    free_dirtree(&root, (struct node_tag *)NULL);
	    return DX__ERROR;
	}
    }
    else
    {	    /**  This directory tree was saved in the cache queue  **/
	restore_from_direct_cache(
	    n, &dirtree_search_direction, dirtree_search_pattern, &root);
    }
 
    traverse_tree(root, (struct node_tag *)NULL, 1, 1, dummy);
 
    /*
    **	Set the signal back to system default
    **/
#ifndef DEBUG
    alarm(0);
    signal(SIGALRM, cstat);
    check_OK(smg$erase_chars (
    		 &cntrl_info_block.commands_display.id,
    		 &10,
    		 &2,
    		 &1))
    check_OK(smg$set_broadcast_trapping (
                 &cntrl_info_block.pasteboard_id,
                 broadcast_routine,
                 0))
#endif
    /*
    **	Create a directory commands display
    **/
 
    dirtree_cmds_display.rows = cntrl_info_block.pasteboard_rows;
    dirtree_cmds_display.width = cntrl_info_block.pasteboard_width;
    dirtree_cmds_display.beg_y = DIRTREE_CMDS_PBD_ROW;
    dirtree_cmds_display.beg_x = DIRTREE_CMDS_PBD_COLUMN;
 
    check_OK(smg$create_virtual_display (
		&cntrl_info_block.pasteboard_rows,
		&cntrl_info_block.pasteboard_width,
		&dirtree_cmds_display.id,
		&SMG$M_TRUNC_ICON,
		0,
		0))
    check_OK(smg$paste_virtual_display (
                 &dirtree_cmds_display.id,
                 &cntrl_info_block.pasteboard_id,
                 &DIRTREE_CMDS_PBD_ROW,
                 &DIRTREE_CMDS_PBD_COLUMN,
                 0))
 
    /*
    **	Locate current directory in the tree.  If not found,
    **	change initial directory to the root directory.
    **/
 
    if ((cwdnode = search_node(
	root, strchr(DX_CURRENT_DIRECTORY.cur_dir, '[')+1)) == NULL)
    {
	cwdnode = root;
    }
 
    /**  Change default directory  **/
    setddir(cwdnode->full_path_name, errmsg);
 
    dirtree_display.view_rows =
	cntrl_info_block.pasteboard_rows - DIRTREE_CMDS_RSV_LNS;
    dirtree_display.view_width = cntrl_info_block.pasteboard_width;
 
    /**  Resize dirtree virtual display's size  **/
 
    dirtree_display.rows += dirtree_display.view_rows-1;
    dirtree_display.width += dirtree_display.view_width-MAXDIRSPEC;
    dirtree_display.beg_y = DIRTREE_PBD_ROW;
    dirtree_display.beg_x = DIRTREE_PBD_COLUMN;
 
    signal_err("Press RETURN to change directory, or keypad-0 to return", silence);
    put_dirtree_title(cwdnode);
    if (cntrl_info_block.user_pref.display_clock)
        write_time(dirtree_cmds_display.id);
    put_filer_pfs(DIRTREE_PFS, dirtree_cmds_display.id);
 
    check_OK(smg$create_virtual_display (
		&dirtree_display.rows,
		&dirtree_display.width,
		&dirtree_display.id,
		&SMG$M_TRUNC_ICON,
		0,
		0))
 
    paint_dirtree_display(root);
 
    dirtree_display.view_beg_y = dirtree_display.view_beg_x = 1;
    check_OK(smg$create_viewport (
		&dirtree_display.id,
		&dirtree_display.view_beg_y,
		&dirtree_display.view_beg_x,
		&dirtree_display.view_rows,
		&dirtree_display.view_width))
 
    /**  Position viewport using current working directory  **/
    change_dirtree_viewport(
	    cwdnode->row,
	    cwdnode->column,
	    &dirtree_display.view_beg_y,
	    &dirtree_display.view_beg_x);
 
    check_OK(smg$paste_virtual_display (
	    &dirtree_display.id,
	    &cntrl_info_block.pasteboard_id,
	    &dirtree_display.beg_y,
	    &dirtree_display.beg_x,
	    0))
 
    dirtree_search_direction = advance;
    strcpy(dirtree_search_pattern, "[]");
 
    direct_process_loop();
 
    /**  Clear error line if any  **/
    check_OK(smg$get_pasting_info (
	      &cntrl_info_block.status_display.id,
	      &cntrl_info_block.pasteboard_id,
	      &status_flags,
	      0,
	      0))
    if (status_flags == SMG$M_DISPLAY_PASTED)
    {
	check_OK(smg$unpaste_virtual_display (
		  &cntrl_info_block.status_display.id,
		  &cntrl_info_block.pasteboard_id))
    }
 
    check_OK(smg$begin_pasteboard_update (
	     	  &cntrl_info_block.pasteboard_id))
    check_OK(smg$erase_display (
	     	  &dirtree_cmds_display.id,
	     	  0,
	     	  0,
	     	  0,
	     	  0))
    check_OK(smg$erase_display (
	     	  &dirtree_display.id,
	     	  0,
	     	  0,
	     	  0,
	     	  0))
    check_OK(smg$end_pasteboard_update (
	     	  &cntrl_info_block.pasteboard_id))
 
    check_OK(smg$delete_virtual_display (
	      &dirtree_cmds_display.id))
    check_OK(smg$delete_virtual_display (
	      &dirtree_display.id))
 
    /*
    **  Reset display ids.
    **	Will be checked by read_string_and_clear_errmsg().
    **/
 
    dirtree_cmds_display.id = dirtree_display.id = 0;
 
    /*
    **	Save to the directory cache queue.  If it was previously
    **	saved in the queue, just replace the slot with
    **	root.  Otherwise, find an empty slot to place root.
    **/
 
    if ((n = search_direct_cache(initial_root)) == -1)
    {
	/*
	**  Current directory info not found in the
	**  directory cache.  Find the LRU slot in the cache.
	**/
 
	n = find_LRU_in_direct_cache();
    }
 
    /*
    **  Save this directory information to the direct cache queue.
    **/
 
    save_to_direct_cache(
	n,
	initial_root,
	dirtree_search_direction,
	dirtree_search_pattern,
	root);
 
    return DX__NORMAL;
}
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	    direct_process_loop()
{
    char errmsg[256];				/**  Error msg buffer	     **/
    char *cp;
    unsigned long int status;
    char keyname[16];
    $DESCRIPTOR (keyname_descrip, keyname);
    char short_path_name[MAXFILESPEC+1];
    $DESCRIPTOR (short_path_name_descrip, short_path_name);
    unsigned long status_flags;		/**  SMG$READ_KEYSTROKE status	     **/
    unsigned short keystroke;
    int find_down_flag;
    struct node_tag *lastnode,
		    *leftnode;
    int timeout = cntrl_info_block.user_pref.update_in_second? 1 : 60;
    int (*cstat)();
 
    /*
    **	Keep processing until exits by user.
    **	Exit is done by pressing kp0 or exit command.
    **/
 
    do
    {
	/**  Update changes made from last command  **/
	setddir(cwdnode->full_path_name, errmsg);
	change_dirtree_viewport(
	    cwdnode->row,
	    cwdnode->column,
	    &dirtree_display.view_beg_y,
	    &dirtree_display.view_beg_x);
	put_dirtree_title(cwdnode);
 
	sprintf(short_path_name, "%.8s", cwdnode->short_path_name);
	LENGTH(short_path_name_descrip) = strlen(short_path_name);
	check_OK(smg$put_chars (
		    &dirtree_display.id,
		    &short_path_name_descrip,
		    &cwdnode->row,
		    &cwdnode->column,
		    0,
		    &SMG$M_REVERSE,
		    0,
		    0))
 
	/*
	**  Read next command.
	**  Clear error message line if any
	**/
 
	do {
	    status_flags = smg$read_keystroke (
				&cntrl_info_block.keyboard_id,
				&keystroke,
				0,
				&timeout,
				0,
				0,
				0);
	    if (cntrl_info_block.user_pref.display_clock)
		write_time(dirtree_cmds_display.id);
	} while (status_flags == SS$_TIMEOUT ||
		 keystroke == SMG$K_TRM_CANCELLED);
 
	check_OK(smg$get_pasting_info (
		      &cntrl_info_block.status_display.id,
		      &cntrl_info_block.pasteboard_id,
		      &status_flags,
		      0,
		      0))
	if (status_flags == SMG$M_DISPLAY_PASTED)
	{
	    check_OK(smg$unpaste_virtual_display (
		      &cntrl_info_block.status_display.id,
		      &cntrl_info_block.pasteboard_id))
        }
	
	/*
	**  Do job according to key pressed
	**/
 
	switch (keystroke)
	{
 
	/**  Exit without changing directory  **/
	case SMG$K_TRM_KP0:
	    setddir(DX_CURRENT_DIRECTORY.cur_dir, errmsg);
	    break;
 
	case SMG$K_TRM_KP1:	    /**  Help command  **/
	    direct_helpdir();
	    break;
 
	case SMG$K_TRM_KP2:	    /**  Forward/backward switch  **/
 
	    if (dirtree_search_direction == advance)
	    {
		dirtree_search_direction = backup;
	    }
	    else
	    {
		dirtree_search_direction = advance;
	    }
 
	    put_dirtree_title(cwdnode);
	    break;
	case SMG$K_TRM_KP3:	    /**  Find first command  **/
 
	    dirtree_find_first();
	    break;
	case SMG$K_TRM_KP4:	    /**  Find next command  **/
 
	    if (dirtree_find_next() == DX__ERROR)
		signal_err("Directory not found", bell);
	    break;
	case SMG$K_TRM_KP5:	    /**  Goto top  **/
 
	    check_OK(smg$put_chars (
		    &dirtree_display.id,
		    &short_path_name_descrip,
		    &cwdnode->row,
		    &cwdnode->column,
		    0,
		    &SMG$M_NORMAL,
		    0,
		    0))
	    cwdnode = root;
	    break;
	case SMG$K_TRM_KP6:	    /**  Goto dirtree_display.rows  **/
 
	    check_OK(smg$put_chars (
		    &dirtree_display.id,
		    &short_path_name_descrip,
		    &cwdnode->row,
		    &cwdnode->column,
		    0,
		    &SMG$M_NORMAL,
		    0,
		    0))
	    find_down_flag = 0;
	    find_down(root, (struct node_tag *)NULL, &cwdnode, &find_down_flag);
	    break;
	case SMG$K_TRM_KP7:	    /**  Goto initial  **/
 
	    direct_initial();
	    break;
	case SMG$K_TRM_KP8:	    /**  Page up/down  **/
 
	    if (dirtree_search_direction == advance)
	    {
		find_down_flag = 0;
		find_down(root, (struct node_tag *)NULL, &lastnode, &find_down_flag);
		if (cwdnode == lastnode)
		    signal_err("Already at bottom of directory tree", bell);
		else
		    cwdnode = dirtree_page_down(cwdnode, lastnode);
	    }
	    else if (cwdnode == root)
	    {
		signal_err("Already at top of directory tree", bell);
	    }
	    else
	    {
		cwdnode = dirtree_page_up(cwdnode);
	    }
	    break;
	case SMG$K_TRM_UP:	    /**  Go up one directory  **/
 
	    if (cwdnode == root)
	    {
		signal_err("Already at top of directory tree", bell);
	    }
	    else
	    {
		check_OK(smg$put_chars (
			&dirtree_display.id,
			&short_path_name_descrip,
			&cwdnode->row,
			&cwdnode->column,
			0,
			&SMG$M_NORMAL,
			0,
			0))
		cwdnode = (cwdnode->up != NULL)? cwdnode->up : cwdnode->left;
	    }
	    break;
	case SMG$K_TRM_DOWN:	/**  Go down one directory  **/
 
	    if (cwdnode->down != NULL || cwdnode->right != NULL)
	    {
		check_OK(smg$put_chars (
			&dirtree_display.id,
			&short_path_name_descrip,
			&cwdnode->row,
			&cwdnode->column,
			0,
			&SMG$M_NORMAL,
			0,
			0))
		cwdnode = (cwdnode->down != NULL)? cwdnode->down : cwdnode->right;
	    }
	    else
	    {
		for (leftnode = cwdnode->left; leftnode != (struct node_tag *)NULL; leftnode = leftnode->left)
		    if (leftnode->down != (struct node_tag *)NULL)
			if (leftnode->left == leftnode->down->left)
			    break;
		if (leftnode == (struct node_tag *)NULL)
		    signal_err("Already at bottom of directory tree", bell);
		else {
		    check_OK(smg$put_chars (
			    &dirtree_display.id,
			    &short_path_name_descrip,
			    &cwdnode->row,
			    &cwdnode->column,
			    0,
			    &SMG$M_NORMAL,
			    0,
			    0))
		    cwdnode = leftnode->down;
		}
	    }
	    break;
	case SMG$K_TRM_RIGHT:	/**  Go right one directory  **/
 
	    if (cwdnode->right != NULL)
	    {
		check_OK(smg$put_chars (
			&dirtree_display.id,
			&short_path_name_descrip,
			&cwdnode->row,
			&cwdnode->column,
			0,
			&SMG$M_NORMAL,
			0,
			0))
		cwdnode = cwdnode->right;
	    }
	    else
	    {
		for (leftnode = cwdnode; leftnode != (struct node_tag *)NULL; leftnode = leftnode->left)
		    if (leftnode->down != (struct node_tag *)NULL)
			if (leftnode->left == leftnode->down->left)
			    break;
		if (leftnode == (struct node_tag *)NULL)
		{
		    signal_err("Already at bottom of directory tree", bell);
		}
		else
		{
		    check_OK(smg$put_chars (
			    &dirtree_display.id,
			    &short_path_name_descrip,
			    &cwdnode->row,
			    &cwdnode->column,
			    0,
			    &SMG$M_NORMAL,
			    0,
			    0))
		    cwdnode = leftnode->down;
		}
	    }
	    break;
	case SMG$K_TRM_LEFT:	/**  Go left one directory  **/
 
	    if (cwdnode == root)
	    {
		signal_err("Already at top of directory tree", bell);
	    }
	    else
	    {
		check_OK(smg$put_chars (
			&dirtree_display.id,
			&short_path_name_descrip,
			&cwdnode->row,
			&cwdnode->column,
			0,
			&SMG$M_NORMAL,
			0,
			0))
		cwdnode = (cwdnode->left != NULL)? cwdnode->left : cwdnode->up;
	    }
	    break;
	case SMG$K_TRM_CR:	/**  Change directory  **/
	
	    /*
	    **	Output 'working...' message if total processing time
	    **	is longer than 1 second.  Needs to disable broadcast message
	    **	first because of interference.
	    **/
 
	    check_OK(smg$disable_broadcast_trapping (
			 &cntrl_info_block.pasteboard_id))
 
	    cstat = signal(SIGALRM, direct_alarm_working);
	    alarm(1);
 
	    status = filer_filter$1(
		    cwdnode->full_path_name, "Name", errmsg);
 
	    /*
	    **	Set the signal back to system default
	    **/
 
	    alarm(0);
	    signal(SIGALRM, cstat);
	    check_OK(smg$erase_chars (
			 &dirtree_cmds_display.id,
			 &10,
			 &2,
			 &1))
	    check_OK(smg$set_broadcast_trapping (
			 &cntrl_info_block.pasteboard_id,
			 broadcast_routine,
			 0))
 
	    if (status == DX__ERROR)
	    {
		signal_err(errmsg, bell);
	    }
	    else
	    {
		return status;
	    }
	    break;
 
	case SMG$K_TRM_CTRLW:    /**  Refresh screen  **/
 
	    check_OK(smg$repaint_screen (
			 &cntrl_info_block.pasteboard_id))
	    break;
	default:		/**  NO DEFINITION  **/
 
	    memset (keyname, ' ', 15);
	    smg$keycode_to_name (
	       &keystroke,
	       &keyname_descrip);
 
	    keyname[strcspn (keyname, " ")] = EOS;
	    sprintf (errmsg, "Key '%s' currently has no definition", keyname);
	    signal_err(errmsg, bell);
	}	/**  switch (keystroke)  **/
    } while ( keystroke != SMG$K_TRM_KP0);
 
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	find_root(initdir, rootdir)
char *initdir;
char *rootdir;
{
    int len;
    char *cp;
 
    len = strcspn(initdir, ".]");
    strncpy(rootdir, initdir, len);
    rootdir[len] = ']';
    rootdir[len+1] = EOS;
 
    cp = strchr(rootdir, '[');
    if (!strcmp(cp, "[0,0]") || !strcmp(cp, "[000,000]"))
    {	    /**  Master system directory  **/
	*cp = EOS;
	strcat(rootdir, "[000000]");
    }
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      CREATE_NODE() allocates virtual memory to a node.
**
**--
**/
struct node_tag *create_node(filespec, row, col)
char *filespec;
int row;
int col;
{
    struct node_tag *newnode;
    char *start, *end;
    int i;
    int len;
    int allocate_size = sizeof (struct node_tag);
    unsigned long int status;
 
    /*
    **	    ALLOCATE STOREAGE FOR A NEW NODE
    **/
 
    check_OK(lib$get_vm (
		 &allocate_size,
		 &newnode,
		 0))
 
    /*
    **	Initialize the newly-created node
    **/
 
    strcpy(newnode->full_path_name, filespec);
    if ((start = strrchr(filespec, '.')) == NULL)
	start = strchr(filespec, '[');
    start++;
    strcpy(newnode->short_path_name, start);
    end = strchr(newnode->short_path_name, ']');
    *end = EOS;
    newnode->row = row;			    /**  DIR COORDINATES **/
    newnode->column = col;
    newnode->left = newnode->up = newnode->right =
	newnode->down = (struct node_tag *)NULL;
 
    /*
    **	Adjust the size of the dirtree display
    **/
 
    dirtree_display.width = max(dirtree_display.width, col+MAXDIRSPEC-1);
    dirtree_display.rows = max(dirtree_display.rows, row);
 
    return newnode;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      BUILD_DIRTREE() expands "DEV:[USERNAME]*.DIR;1" wildcard and
**	build a tree to be used for painting the DIRTREE virtual display.
**
**--
**/
int	build_dirtree(parentnode, pathname)
struct node_tag **parentnode;
char *pathname;
{
    static _align (QUADWORD) int logicalQ[2];
    static unsigned long context;
 
    struct logical_queue_entry_tag *E;
    unsigned long condcode;
    char *indx;
    int number_of_bytes;
 
    char filespec[MAXFILESPEC+1];
    $DESCRIPTOR (filespec_descrip, filespec);
 
    char resultant_filespec[MAXFILESPEC+1];
    $DESCRIPTOR (resultant_filespec_descrip, resultant_filespec);
 
    logicalQ[0] = logicalQ[1] = 0;
    trnlnm(pathname, logicalQ);
 
    while (_REMQHI (logicalQ, &E) != 3) {
	if (add_node(parentnode, E->equivalent_name, (struct node_tag *)NULL)
	    == DX__ERROR)
	{
	    return DX__ERROR;
	}
	/**  Get wildcard file spec  **/
	strncpy(filespec, E->equivalent_name, strlen(E->equivalent_name)-1);
	filespec[ strlen(E->equivalent_name)-1 ] = EOS;
	number_of_bytes = strlen(E->equivalent_name)+1;
        check_OK(lib$free_vm (
                     &number_of_bytes,
                     &E->equivalent_name,
                     0))
	strcat(filespec, "...]*.DIR;1");
	LENGTH(filespec_descrip) = strlen(filespec);
 
	/**  Call system services to expand the wildcard  */
	while ((condcode = lib$find_file (
				&filespec_descrip,
				&resultant_filespec_descrip,
				&context,
				0,
				0,
				0,
				0)) != RMS$_NMF)
	{
	    if (condcode == RMS$_NORMAL &&
		!strchr(resultant_filespec, '*') &&
		strchr(resultant_filespec, ' '))
	    {	/** Convert "DEV:[*.*...]*.DIR;1" into "DEV:[*.*...]"  **/
		for (indx = resultant_filespec; indx; indx++)
		    if (*indx == ']')
		    {
			*indx = '.';
			break;
		    }
		for (indx++; indx; indx++)
		    if (*indx == '.')
		    {
			*indx = ']';
			break;
		    }
		indx++;
		*indx = EOS;
 
		if (strstr (filespec, "[000000."))
		{
		    if (!strstr (resultant_filespec, "[000000."))
		    {
			char s[MAXFILESPEC+1];
 
			indx = strchr(resultant_filespec, '[')+1;
			strcpy(s, indx);
			*indx = EOS;
			strcat(resultant_filespec, "000000.");
			strcat(resultant_filespec, s);
		    }
		}
 
		if (add_node(
			parentnode,
			resultant_filespec,
			(struct node_tag *)NULL) == DX__ERROR)
		{
		    lib$find_file_end(&context);
		    return DX__ERROR;
		}
	    }
	}
	lib$find_file_end(&context);
    }
 
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      add_node() inserts a node into the tree.
**
**--
**/
struct node_tag *add_node(p, filespec, q)
struct node_tag **p;
char *filespec;
struct node_tag *q;	/*
			**  Parent node if moved right or
			**  sibling node if moved down
			**/
{
    struct node_tag *newnode;
    struct node_tag *upnode, *leftnode, *downnode, *rightnode;
    char *indx;
    int cmp;
    char *cp;
    int len;
 
    if (*p == (struct node_tag *)NULL)
    {
	newnode = create_node(filespec, 0, 0);
 
	/**  Do double linking  **/
	if (q != (struct node *)NULL)
	{
	    if (filespec[ strlen(q->full_path_name)-1 ] == '.')
	    {	/**  New node is the oldest son  **/
		if ((newnode->up = find_prev_node(q)) != (struct node_tag *)NULL)
		    newnode->up->down = newnode;
		newnode->left = q;
	    }
	    else
	    {	/**  New node is a sibling  **/
		newnode->up = q;
		newnode->left = q->left;
	    }
	}
	*p = newnode;
	return *p;
    }
    else if (filespec[ strlen((*p)->full_path_name)-1 ] == '.' &&
	     !strncmp(
		filespec,
		(*p)->full_path_name,
		strlen((*p)->full_path_name)-1))
    {	    /**  Search right  **/
	return add_node(&((*p)->right), filespec, *p);
    }
    else if (strncmp(
		filespec,
		(*p)->full_path_name,
		strlen((*p)->full_path_name)-1) > 0)
    {	    /**  Search down  **/
	return add_node(&((*p)->down), filespec, *p);
    }
    else
    {
	if ((cp = strrchr((*p)->full_path_name, '.')) == NULL)
	{
	    cp = strrchr((*p)->full_path_name, ']');
	}
	len = cp-(*p)->full_path_name;
	if (strchr(filespec+len+1, '.') != NULL)
	{	/*
		**  The parent directory did not exist prior to the
		**  creation of this directory
		**/
	    return (struct node_tag *)NULL;
	}
	else
	{
	    newnode = create_node(filespec, 0, 0);
 
	    /**  Do double linking  **/
	    if (q != (struct node *)NULL)
	    {
		if (filespec[ strlen(q->full_path_name)-1 ] == '.')
		{	/**  New node is the oldest son  **/
		    if ((newnode->up = (*p)->up) != (struct node_tag *)NULL)
			newnode->up->down = newnode;
		    newnode->left = q;
		    newnode->down = *p;
		    (*p)->up = newnode;
		}
		else
		{	/**  New node is not the oldest son  **/
		    newnode->up = q;
		    newnode->left = (*p)->left;
		    newnode->down = *p;
		    (*p)->up = newnode;
		}
	    }
	    *p = newnode;
	    return *p;
	}
    }
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      FIND_PREV_NODE() finds previous node with different parent directory.
**
**--
**/
struct node_tag *find_prev_node(q)
struct node_tag *q;
{
    struct node_tag *s;
 
    for (q = q->up; q != (struct node_tag *)NULL; q = q->up)
    {
	if (q->right != (struct node_tag *)NULL)
	{
	    for (s = q->right; s->down != (struct node_tag *)NULL; s = s->down)
		if (s->down->left != q)
		    return s;
	    return s;
	}
 
    }
    return (struct node_tag *)NULL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      FIND_NEXT_NODE() finds next node with different parent directory.
**
**--
**/
struct node_tag *find_next_node(q)
struct node_tag *q;
{
    struct node_tag *s;
 
    for (q = q->down; q != (struct node_tag *)NULL; q = q->down)
    {
	if (q->right != (struct node_tag *)NULL)
	{
	    for (s = q->right; s->up != (struct node_tag *)NULL; s = s->up)
		if (s->up->left != q)
		    return s;
	    return s;
	}
    }
    return (struct node_tag *)NULL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      TRAVERSE_TREE() calculates the row and column value when
**	visiting each node.  The dimensions for DIRTREE are derived
**	when the function finished.
**
**--
**/
int	traverse_tree(childnode, parentnode, row, col, direction)
struct node_tag *childnode, *parentnode;
int row, col;
enum directions direction;
{
    /**  Reset DIRTREE virtual display's dimension  **/
    dirtree_display.rows = dirtree_display.width = 0;
 
    traverse_tree$1(childnode, parentnode, row, col, direction);
 
    /**  Resize DIRTREE virtual display's size  **/
    dirtree_display.rows += dirtree_display.view_rows-1;
    dirtree_display.width += dirtree_display.view_width-MAXDIRSPEC;
    return DX__NORMAL;
}
 
static int	traverse_tree$1(childnode, parentnode, row, col, direction)
struct node_tag *childnode, *parentnode;
int row, col;
enum directions direction;
{
    int lineno = 0;
 
    if (childnode == (struct node_tag *)NULL)
    {
	return (direction == right)? LINESPACE : 0;
    }
    else if (childnode->left == parentnode)
    {
	if (dirtree_display.width < col+MAXDIRSPEC-1)
	    dirtree_display.width = col+MAXDIRSPEC-1;
	if (dirtree_display.rows < row)
	    dirtree_display.rows = row;
	childnode->row = row;
	childnode->column = col;
	lineno += traverse_tree$1(childnode->right, childnode, row, col+DISTANCE_COUNT, right);
	lineno += traverse_tree$1(childnode->down, parentnode, row+lineno, col, down);
    }
    return lineno;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      SEARCH_NODE() searchs a node starting from the root directory.
**      Return error if not found.
**
**--
**/
struct node_tag *search_node(p, filespec)
struct node_tag *p;
char *filespec;
{
    if (p == (struct node_tag *)NULL)
    {
	return (struct node_tag *)NULL;
    }
    else if (!strncmp(p->short_path_name,
		      filespec,
		      strlen(p->short_path_name)) &&
	     !strncmp(p->short_path_name,
		      filespec,
		      strcspn(filespec, ".]")))
	/**  A MATCHED DIRECTORY IS FOUND  **/
    {
	if (*(filespec+strlen(p->short_path_name)) == ']')	
	    return p;
	else
	    return search_node(p->right, strchr(filespec, '.')+1);
    }
    else if (p->down != (struct node_tag *)NULL)
    {
        if (p->down->left == p->left)
	    return search_node(p->down, filespec);
    }
 
    return (struct node_tag *)NULL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      PAINT_DIRTREE_DISPLAY() takes the tree head as input and
**	writes the directory tree structure to the display.
**
**--
**/
void	paint_dirtree_display(p)
struct node_tag *p;
{
    char short_path_name[MAXDIRSPEC+1];
    $DESCRIPTOR(short_path_name_descrip, short_path_name);
 
    unsigned int start_row, end_row, start_column, end_column;
    unsigned long char_up = SMG$M_LEFT + SMG$M_RIGHT + SMG$M_DOWN;
    unsigned long char_left = SMG$M_UP + SMG$M_DOWN + SMG$M_RIGHT;
    unsigned long left_corner = SMG$M_UP + SMG$M_RIGHT;
    unsigned long draw_char;
 
    if (p != (struct node_tag *)NULL)
    {
	sprintf(short_path_name, "%.8s", p->short_path_name);
	LENGTH(short_path_name_descrip) = strlen(short_path_name);
	check_OK(smg$put_chars (
		    &dirtree_display.id,
		    &short_path_name_descrip,
		    &(p->row),
		    &(p->column),
		    0,
		    0,
		    0,
		    0))
 
	if (p->left != (struct node_tag *)NULL)
	{
	    if (p->up == NULL)
	    {
		start_column =
		    p->left->column+min(
			strlen(p->left->short_path_name), MAXDIRSPEC);
		end_column = p->column-1;
		check_OK(smg$draw_line (
		    &dirtree_display.id,
		    &(p->row),
		    &start_column,
		    &(p->row),
		    &end_column,
		    0,
		    0))
	    }
	    else if (p->left != p->up->left)
	    {
		start_column =
		    p->left->column+min(
			strlen(p->left->short_path_name), MAXDIRSPEC);
		end_column = p->column-1;
		check_OK(smg$draw_line (
		    &dirtree_display.id,
		    &(p->row),
		    &start_column,
		    &(p->row),
		    &end_column,
		    0,
		    0))
	    }
	    else
	    {
		if (p->up->up == (struct node_tag *)NULL)
		    draw_char = char_up;
		else if (p->up->up->left != p->up->left)
		    draw_char = char_up;
		else
		    draw_char = char_left;
		start_column = p->up->column-LINE_NODE_DISTANCE;
		check_OK(smg$draw_char (
			&dirtree_display.id,
			&draw_char,
			&(p->up->row),
			&start_column,
			0,
			0))
		start_row = p->up->row+1;
		end_row = p->row;
		start_column = p->column-LINE_NODE_DISTANCE;
		check_OK(smg$draw_line (
		    &dirtree_display.id,
		    &start_row,
		    &start_column,
		    &end_row,
		    &start_column,
		    0,
		    0))
		check_OK(smg$draw_char (
			&dirtree_display.id,
			&left_corner,
			&(p->row),
			&start_column,
			0,
			0))
		start_column = p->column-LINE_NODE_DISTANCE;
		end_column = p->column-1;
		check_OK(smg$draw_line (
		    &dirtree_display.id,
		    &(p->row),
		    &start_column,
		    &(p->row),
		    &end_column,
		    0,
		    0))
	    }
	}
	paint_dirtree_display(p->right);
	if (p->down != (struct node_tag *)NULL)
	    if (p->left == p->down->left)
		paint_dirtree_display(p->down);
    }
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      FREE_DIRTREE() makes the virtual memory previously allocated to
**	DIRTREE available again.
**
**--
**/
int	free_dirtree(p, q)
struct node_tag **p;
struct node_tag *q;
{
    int node_entry_size = sizeof (struct node_tag);
 
    if (*p != (struct node_tag *)NULL)
    {
        if ((*p)->left == q)
        {
            free_dirtree(&((*p)->down), q);
            free_dirtree(&((*p)->down), *p);
 
            check_OK(lib$free_vm (
                         &node_entry_size,
                         p,
                         0))
	    *p = (struct node_tag *)NULL;
        }
    }
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	put_dirtree_title(n)
struct node_tag *n;
{
    char dir[10];
    int rows_in_page,
	current_page;
    char str[81];
    $DESCRIPTOR(str_descrip, str);
    char cwd_line[81];
    $DESCRIPTOR(cwd_line_descrip, cwd_line);
    char page_str[81];
    $DESCRIPTOR(page_str_descrip, page_str);
    struct fil_dx_tag *p;
    int line_row;
    int len;
 
    if (dirtree_search_direction == advance)
    {
        strcpy(dir, "Forward");
    }
    else
    {
        strcpy(dir, "Backward");
    }
    if (strlen(n->full_path_name) > 67)
    {
        sprintf(cwd_line, " %-66.66s~ | %-8.8s ", n->full_path_name, dir);
    }
    else
    {
        sprintf(cwd_line, " %-67.67s | %-8.8s ", n->full_path_name, dir);
    }
    LENGTH(cwd_line_descrip) = strlen(cwd_line);
    check_OK(smg$put_chars (
	      &dirtree_cmds_display.id,
	      &cwd_line_descrip,
	      &DIRTREE_DIRECT_ROW,
	      &1,
	      &SMG$M_ERASE_LINE,
	      &SMG$M_REVERSE,
	      0,
	      0))
 
    current_page = 1 + (n->row-1)/dirtree_display.view_rows;
    sprintf(page_str, "%d", current_page);
    LENGTH(page_str_descrip) = strlen(page_str);
    len = strlen(page_str)+2;
    line_row = cntrl_info_block.pasteboard_rows-1;
    check_OK(smg$draw_line (
		    &dirtree_cmds_display.id,
		    &line_row,
		    &1,
		    &line_row,
		    &1,
		    0,
		    0))
    check_OK(smg$put_chars (
		    &dirtree_cmds_display.id,
		    &page_str_descrip,
		    &line_row,
		    &2,
		    0,
		    &SMG$M_BOLD,
		    0,
		    0))
    check_OK(smg$draw_line (
		    &dirtree_cmds_display.id,
		    &line_row,
		    &len,
		    &line_row,
		    &dirtree_cmds_display.width,
		    0,
		    0))
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      DIRECT_HELPDIR() calls HELPDIR() to output help message
**	by using VMS HELP utilities.
**
**--
**/
int	direct_helpdir()
{
    filer_help("DX OPTION_MENU MOVING_AROUND_DIRTREE");
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      to be specified
**
**--
**/
int	    direct_alarm_working()
{
    static int remain_alarm = 2;
 
    if (cntrl_info_block.user_pref.display_clock &&
	cntrl_info_block.user_pref.update_in_second)
    {
	write_time(dirtree_cmds_display.id);
    }
 
    remain_alarm--;
    if (remain_alarm > 0)
    {
	signal(SIGALRM, direct_alarm_working);
    }
    else
    {
	remain_alarm = 2;
	signal(SIGALRM, direct_alarm_working$1);
    }
 
    alarm(1);
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      to be specified
**
**--
**/
static int	    direct_alarm_working$1()
{
    static int switch_on = 0;
    static int remain_timeout = 60;
 
    if (!switch_on)
    {
        switch_on++;
        check_OK(smg$put_chars (
                     &dirtree_cmds_display.id,
                     $DESCR ("Working..."),
                     &2,
                     &1,
                     0,
                     0,
                     0,
                     0))
    }
    else
    {
        switch_on = 0;
        check_OK(smg$erase_chars (
                     &dirtree_cmds_display.id,
                     &10,
                     &2,
                     &1))
    }
 
    if (cntrl_info_block.user_pref.display_clock)
    {
	if (cntrl_info_block.user_pref.update_in_second)
	{
	    write_time(dirtree_cmds_display.id);
	}
	else
	{
	    remain_timeout--;
	    if (remain_timeout == 0)
	    {
	    	 write_time(dirtree_cmds_display.id);
		 remain_timeout = 60;
	    }
	}
    }
 
    signal(SIGALRM, direct_alarm_working$1);
    alarm(1);
    return DX__NORMAL;
}
