/* Program Name            : DX.C                                       */
/*   Original Author       : Chau-Kuang Hung				*/
/*   Date                  : 7-12-89					*/
/* 	Chau-Kuang Hung (chung@us.oracle.com)                           */
/* 	Oracle Corporation                                              */
/* 	300 Oracle Parkway                                              */
/* 	BOX 659304                                                      */
/* 	Redwood Shores, CA 94065                                        */
/*                                                                      */
/*   Program Description   :                                            */
/* 	DX is a VAX/VMS utility for displaying files on a VT100 or      */
/* 	upper type terminal.  It can be used to delete, edit, or        */
/* 	rename files in the current directory.  DX can also create a    */
/* 	hardcopy listing of all the files displayed.                    */
/*                         :                                            */
/* References                                                           */
/*   Files open for Input  :                                            */
/*   Files open for Output :                                            */
/*   Modules Referenced    :                                            */
/*									*/
/* Revision History follows                                             */
/*  04-Jun-1999 JL Modified for CLI and MSG interface			*/
/*		   Modified for (weak) error handler			*/
/*
** ..........................................................................
**
** DISTRIBUTION AND COPYRIGHT:
**
** Copyright (C) 1989-1992 Chau-Kuang Hung
** Copyright 1994-1999 (c) Jérôme Lauret <jlauret@mail.chem.sunysb.edu>
**
** This program is free software; permission is granted to any
** individual or institution to use, copy, or redistribute this
** software so long as it is not sold for profit, provided this
** copyright notice is retained.
**
** DISCLAIMER:
**
** This software and its documentation are provided "AS IS" and
** without any expressed or implied warranties whatsoever.
** No warranties as to performance, merchantability, or fitness
** for a particular purpose exist.
**
** In no event shall any person or organization of people be
** held responsible for any direct, indirect, consequential
** or inconsequential damages or lost profits.
**
** ..........................................................................
**/
 
#include "global.h"
#include "directque.h"
#include "dx.h"
#include "filer.h"
#include "filerque.h"
#include "findfile.h"
#include "inquire.h"
#include <climsgdef.h>
#include <iodef.h>
#include <jpidef.h>
#include <libvmdef.h>
#include <lnmdef.h>
#include <rmsdef.h>
#include <stsdef.h>
#include <ttdef.h>
#include <fibdef.h>
#include <trmdef.h>
 
 
/* ****  INTERNAL FUNCTION PROTOTYPING *** */
 
static int	    DX_initialized(void);
static int	    parse_cli_parameters(void);
static int	    parse_cli_qualifiers(void);
static int	    trnlnm_1(const char *, int *, int *);
 
 
/*
**  FUNCTIONAL DESCRIPTION:
**
**      MAIN() is the main driver routine
**
**/
 
 
 
 
main()
{
 
    /*
    **	Initialize DX for execution
    **/
 
    DX_initialized();
 
    /*
    **	Find all the files in the directory.  The status returned from
    **	the function calls are not checked since the only reason to
    **	fail is run out of memory, which is very unlikely for the first
    **	call.
    **/
    find_direct_file();
    find_exclude_file(
	&DX_CURRENT_DIRECTORY.exclude_filelist,
	DX_CURRENT_DIRECTORY.cur_filter,
	DX_CURRENT_DIRECTORY.sortby,
	DX_CURRENT_DIRECTORY.sortorder);
    find_non_direct_file();
 
    /*
    **	Process commands until user exits
    **/
 
    DX_process_loop();
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      Initialization routine that fills the global data
**	CNTRL_INFO_BLOCK. Every fields in CNTRL_INFO_BLOCK will be
**	assigned a value except DIR_DX, which will be assigned in
**	PARSE_CLI_QUALIFIERS().
**
**--
**/
static int	DX_initialized()
{
    static unsigned long type_of_terminal;
 
    struct SMG$R_ATTRIBUTE_INFO_BLOCK pasteboard_info_table;
    long int rows,
	     status_rightmost;
    int i;
 
    /*
    **	Initially, there is only one winodw.
    **	The window number is 0.
    **/
 
    cntrl_info_block.windows = 1;
    cntrl_info_block.cur_win = 0;
 
    /*
    **	Parse command-line parameters.  Abort DX if invalid directory
    **  or file specification.
    **/
 
    cntrl_info_block.dir_dx[0].cur_dir[0] = EOS;
    cntrl_info_block.dir_dx[0].par_dir[0] = EOS;
    if (parse_cli_parameters() == DX__ERROR)
    {
	exit(DXM__BADPARSE);
    }
 
    /*
    **	Set up smg$ environment and verify that the display
    **	device is valid (must be vt-100 or up).
    **/
 
    if (smg$create_pasteboard (
		&cntrl_info_block.pasteboard_id,
		$DESCR ("SYS$OUTPUT"),
		0,
		0,
		0,
		&type_of_terminal) != SS$_NORMAL)
    {
	exit(DXM__NANSITERM);
    }
 
    if (type_of_terminal != SMG$K_VTTERMTABLE)
    {
	exit(DXM__NANSITERM);
    }
 
    check_OK(smg$get_pasteboard_attributes (
		&cntrl_info_block.pasteboard_id,
		&pasteboard_info_table,
		&SMG$C_PASTEBOARD_INFO_BLOCK))
 
    if (pasteboard_info_table.SMG$B_DEV_TYPE < TT$_VT100)
    {
	exit(DXM__NONVT100CRT);
    }
 
    if (pasteboard_info_table.SMG$R_ROW_OVERLAY.SMG$B_NUM_ROWS <
	    CMDS_RSV_LNS+DIRINFO_RSV_LNS+4)
    {
	exit(DXM__PGLEN);
    }
 
    cntrl_info_block.pasteboard_rows =
	(unsigned short) pasteboard_info_table.SMG$R_ROW_OVERLAY.SMG$B_NUM_ROWS;
    cntrl_info_block.pasteboard_width =	    /**  Set maximum columns to 80   **/
	min (80, pasteboard_info_table.SMG$W_NUM_COLUMNS);
    if (cntrl_info_block.pasteboard_width < 76)
    {
	exit(DXM__PGWID);
    }
 
    /*
    **	Create a virtual keyboard to accept user input
    **/
 
    check_OK(smg$create_virtual_keyboard (
	    &cntrl_info_block.keyboard_id,
	    $DESCR ("SYS$INPUT"),
	    0,
	    0,
	    0))
 
    /*
    **	Create COMMANDS_DISPLAY to display menus and function keys.
    **	The virtual display is as large as the pasteboard.
    **/
 
    check_OK(smg$create_virtual_display (
		&cntrl_info_block.pasteboard_rows,
		&cntrl_info_block.pasteboard_width,
		&cntrl_info_block.commands_display.id,
		&SMG$M_TRUNC_ICON,
		0,
		0))
    check_OK(smg$paste_virtual_display (
                 &cntrl_info_block.commands_display.id,
                 &cntrl_info_block.pasteboard_id,
                 &COMMANDS_PBD_ROW,
                 &COMMANDS_PBD_COLUMN,
                 0))
 
    cntrl_info_block.commands_display.rows = cntrl_info_block.pasteboard_rows;
    cntrl_info_block.commands_display.width = cntrl_info_block.pasteboard_width;
    cntrl_info_block.commands_display.beg_y = COMMANDS_PBD_ROW;
    cntrl_info_block.commands_display.beg_x = COMMANDS_PBD_COLUMN;
 
    /*
    **	Create STATUS_DISPLAY to show error messages.
    **/
 
    status_rightmost = cntrl_info_block.pasteboard_width-10;
			    /**  Reserve 10 columns for clock  **/
    check_OK(smg$create_virtual_display (
		&STATUS_BOTTOM,
		&status_rightmost,
		&cntrl_info_block.status_display.id,
		&SMG$M_TRUNC_ICON,
		0,
		0))
 
    cntrl_info_block.status_display.rows = STATUS_BOTTOM;
    cntrl_info_block.status_display.width = status_rightmost;
    cntrl_info_block.status_display.beg_y = STATUS_PBD_ROW;
    cntrl_info_block.status_display.beg_x = STATUS_PBD_COLUMN;
 
    /*
    **	Use default settings for the user preference.  They will be
    **	overwritten by the command-line options or the initial file.
    **/
 
    use_default("", "", "");
 
    /*
    **	Define all other settings except for the directory equivalent
    **	names and directory file id.  Use one window initially.
    **/
 
    /**  Create a virtual display to show directory-related information  **/
 
    rows = cntrl_info_block.pasteboard_rows-3;
    check_OK(smg$create_virtual_display (
		&rows,
		&cntrl_info_block.pasteboard_width,
		&cntrl_info_block.dir_dx[0].dir_info_display.id,
		&SMG$M_TRUNC_ICON,
		0,
		0))
    check_OK(smg$paste_virtual_display (
                 &DX_CURRENT_DIRECTORY.dir_info_display.id,
                 &cntrl_info_block.pasteboard_id,
                 &SINGLE_WINDOW_DIR_INFO_PBD_ROW,
                 &SINGLE_WINDOW_DIR_INFO_PBD_COLUMN,
                 0))
 
    cntrl_info_block.dir_dx[0].dir_info_display.rows =
	cntrl_info_block.pasteboard_rows-3;
    cntrl_info_block.dir_dx[0].dir_info_display.width =
	cntrl_info_block.pasteboard_width;
    cntrl_info_block.dir_dx[0].dir_info_display.beg_y =
	SINGLE_WINDOW_DIR_INFO_PBD_ROW;
    cntrl_info_block.dir_dx[0].dir_info_display.beg_x =
	SINGLE_WINDOW_DIR_INFO_PBD_COLUMN;
 
    cntrl_info_block.dir_dx[0].form = long_form;
    cntrl_info_block.dir_dx[0].sortby = sortname;
    cntrl_info_block.dir_dx[0].sortorder = ascending;
    cntrl_info_block.dir_dx[0].filemode = single;
    cntrl_info_block.dir_dx[0].direction = advance;
    cntrl_info_block.dir_dx[0].exitn = 0;
 
    cntrl_info_block.dir_dx[0].dir_filelist = NULL;
    cntrl_info_block.dir_dx[0].non_dir_filelist = NULL;
    cntrl_info_block.dir_dx[0].exclude_filelist = NULL;
    cntrl_info_block.dir_dx[0].cur_file = NULL;
 
    strcpy(cntrl_info_block.dir_dx[0].search_pattern, "*");
 
    cntrl_info_block.dir_dx[0].tot_files = 0;
    cntrl_info_block.dir_dx[0].tot_blocks = 0;
 
    cntrl_info_block.dir_dx[0].sel_files = 0;
    cntrl_info_block.dir_dx[0].sel_blocks = 0;
 
    cntrl_info_block.dir_dx[0].dpro = 0Xffff;
 
    cntrl_info_block.dir_dx[0].subdir = 0;
 
    cntrl_info_block.dir_dx[0].cur_filter.date = DX_CREDATE;
    convert_date_string (
	"31-DEC-2857 00:00:00.00",
	&cntrl_info_block.dir_dx[0].cur_filter.before);
    convert_date_string (
	"17-NOV-1858 00:00:00.00",
	&cntrl_info_block.dir_dx[0].cur_filter.since);
    cntrl_info_block.dir_dx[0].cur_filter.maximum = 1073741823;
    cntrl_info_block.dir_dx[0].cur_filter.minimum = 0;
 
    /*
    **	Parse command-line qualifiers
    **/
 
    parse_cli_qualifiers();
 
    /*
    **	Trap broadcast messages
    **/
 
    check_OK(smg$set_broadcast_trapping (
                 &cntrl_info_block.pasteboard_id,
                 broadcast_routine,
                 0))
 
    /*
    **	Initilaize filer and directory info buffer
    **/
 
    initialize_filer_cache();
    initialize_direct_cache();
 
    /*
    **	Return normal status
    **/
 
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      PARSE_CLI_PARAMETERS() gets command-line parameters
**	from the foreign command.
**
**--
**/
static int	parse_cli_parameters()
{
unsigned short int len;
char original_cwd[MAXFILESPEC+1];
struct {
	short buflen;
	short itemcode;
	char *buf;
	short *reslen;
} itmlist [] = {
	{ MAXFILESPEC, LNM$_STRING, original_cwd, &len },
	{ 0, 0, 0, 0 }
	};
 
char curdir[MAXFILESPEC+1];
$DESCRIPTOR (curdir_descrip, curdir);
 
char resultant_string[MAXFILESPEC+1];
$DESCRIPTOR(resultant_string_descrip, resultant_string);
 
char lognm[MAXFILESPEC+1];		    /**  LOG NAME(BEFORE TRANSLATED) **/
$DESCRIPTOR (lognm_descrip, lognm);
unsigned long int status;
 
char default_filespec[MAXFILESPEC+1];
char errmsg[MAXFILESPEC+1];
 
#include "dxcli.h"
 
    /*
    **  Get current working directory (use full vms filespec.)
    **/
 
    strcpy(lognm, "SYS$DISK");
    LENGTH(lognm_descrip) = strlen(lognm);
    check_OK(sys$trnlnm (
		 0,
		 $DESCR ("LNM$FILE_DEV"),
		 &lognm_descrip,
		 0,
		 itmlist))
    strncpy (cntrl_info_block.original_cwd, original_cwd, len);
    cntrl_info_block.original_cwd[len] = EOS;
    check_OK(sys$setddir (
		0,
		&len,
		&resultant_string_descrip))
    resultant_string[len] = EOS;
    strcat(cntrl_info_block.original_cwd, resultant_string);
 
    /**  Get command-line string  **/
 
    check_OK(lib$get_foreign (
                 &resultant_string_descrip,
                 0,
                 &len,
                 0))
 
    strcpy(default_filespec, "*.*;*");
 
    if (len == 0)
    {  	/**  DX was invoked by 'RUN' command or a command without parameters **/
 
	sprintf(resultant_string, "%s*.*;*", cntrl_info_block.original_cwd);
	if (insert_filespec_list(
		&cntrl_info_block.dir_dx[0].cur_filter.name_filespec_list,
		default_filespec,
		resultant_string,
		errmsg) == DX__ERROR)
	{
	    lib$signal(DXM__OPENIN,1,default_filespec);
	    lib$signal(DXM__ERRMSG,1,errmsg);
	    exit(SS$_NORMAL);
	}
	set_cwd_and_par(default_filespec);
    } else { /**  DX was invoked by a command with at least one qualifiers	     **/
 
	int count = 0;
 
	/*
	**  Process parameter "P1 (FILE_SPEC)"
	**/
 
	str$concat(&resultant_string_descrip,&command,
		   &resultant_string_descrip);
	cli$dcl_parse(&resultant_string_descrip,&DX_CLI);
 
	while (cli$get_value(
		    $DESCR("FILE_SPEC"),
		    &resultant_string_descrip,
		    &len) & 1)
	{
	    resultant_string[len] = EOS;
	    if (insert_filespec_list(
		    &cntrl_info_block.dir_dx[0].cur_filter.name_filespec_list,
		    default_filespec,
		    resultant_string,
		    errmsg) != DX__NORMAL)
	    {
		lib$signal(DXM__OPENIN,1,default_filespec);
		lib$signal(DXM__ERRMSG,1,errmsg);
		exit(SS$_NORMAL);
	    }
	    if (count++ == 0)
	    {
		set_cwd_and_par(default_filespec);
	    }
	}
    }
 
    /*
    **	Set default directory to target directory
    **/
 
    if (setddir(cntrl_info_block.dir_dx[0].cur_dir, errmsg) == DX__ERROR)
    {
	lib$signal(DXM__CLIPAR,1,errmsg);
	return DX__ERROR;
    }
 
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      PARSE_CLI_QUALIFIERS() gets command-line qualifiers
**	from the foreign command.
**
**--
**/
static int	parse_cli_qualifiers()
{
char resultant_string[MAXFILESPEC+1];
unsigned short int len;
$DESCRIPTOR(resultant_string_descrip, resultant_string);
char dat_tim_string[256];
$DESCRIPTOR(dat_tim_string_descrip, dat_tim_string);
char initial_file[MAXFILESPEC+1];
char errmsg[256];
unsigned long int status;
char default_filespec[MAXFILESPEC+1];
 
#include "dxcli.h"
 
    check_OK(lib$get_foreign (
                 &resultant_string_descrip,
                 0,
                 &len,
                 0))
 
    if (len == 0)  	/**  DX was invoked by 'RUN' command or a foreign    **/
    {			/**  without any command-line options		     **/
	if (restore_setting(DEFAULT_STARTUP, "init", errmsg) != DX__NORMAL)
	{
	    sprintf (initial_file, "SYS$LOGIN:%s", DEFAULT_STARTUP);
	    restore_setting(initial_file, "init", errmsg);
	}
    } else {		    /**  Invoked from the foreign command   **/
	/*
	**  Process qualifier "INITIAL_FILE"
	**/
 
	str$concat(&resultant_string_descrip,&command,
		   &resultant_string_descrip);
	cli$dcl_parse(&resultant_string_descrip,&DX_CLI);
 
 
	status = cli$present($DESCR("INITIAL_FILE"));
	if (cli$present($DESCR("INITIAL_FILE")) == CLI$_PRESENT)
	{
	    cli$get_value(
		    $DESCR("INITIAL_FILE"),
		    &resultant_string_descrip,
		    &len);
	    resultant_string[len] = EOS;
	    restore_setting(resultant_string, "init", errmsg);
	}
        else if (restore_setting(DEFAULT_STARTUP, "init", errmsg) != DX__NORMAL)
        {
	    sprintf (initial_file, "SYS$LOGIN:%s", DEFAULT_STARTUP);
	    restore_setting(initial_file, "init", errmsg);
        }
 
	/*
	**  Process qualifier "EXIT"
	**/
 
	status = cli$present($DESCR("EXIT"));
	if (status == CLI$_PRESENT || status == CLI$_LOCPRES)
	{
            cntrl_info_block.dir_dx[0].exitn = 1;
	}
 
	/*
	**  Process qualifier "ASCENDING"
	**/
 
	status = cli$present($DESCR("ASCENDING"));
	if (status == CLI$_PRESENT || status == CLI$_LOCPRES ||
	    status == CLI$_DEFAULTED)
	{
            cntrl_info_block.dir_dx[0].sortorder = ascending;
	}
 
	/*
	**  Process qualifier "DESCENDING"
	**/
 
	status = cli$present($DESCR("DESCENDING"));
	if (status == CLI$_LOCPRES || status == CLI$_PRESENT)
	{
            cntrl_info_block.dir_dx[0].sortorder = descending;
	}
 
	/*
	**  Process qualifier "DETAIL"
	**/
 
	status = cli$present($DESCR("DETAIL"));
	if (status == CLI$_LOCPRES || status == CLI$_PRESENT ||
	    (status == CLI$_DEFAULTED &&
		!cntrl_info_block.user_pref.use_short_form))
	{
            cntrl_info_block.dir_dx[0].form = long_form;
	}
 
	/*
	**  Process qualifier "BRIEF"
	**/
 
	status = cli$present($DESCR("BRIEF"));
	if (status == CLI$_LOCPRES || status == CLI$_PRESENT)
	{
            cntrl_info_block.dir_dx[0].form = short_form;
	}
 
	/*
	**  Process qualifier "BEFORE"
	**/
 
	status = cli$present($DESCR("BEFORE"));
	if (status == CLI$_PRESENT)
	{
	    cli$get_value(
		    $DESCR("BEFORE"),
		    &resultant_string_descrip,
		    &len);
	    resultant_string[len] = EOS;
	    strcpy(dat_tim_string, resultant_string);
 
	    convert_date_string (
		dat_tim_string,
		&cntrl_info_block.dir_dx[0].cur_filter.before);
	}
 
	/*
	**  Process qualifier "SINCE"
	**/
 
	status = cli$present($DESCR("SINCE"));
	if (status == CLI$_PRESENT)
	{
	    cli$get_value(
		    $DESCR("SINCE"),
		    &resultant_string_descrip,
		    &len);
	    resultant_string[len] = EOS;
	    strcpy(dat_tim_string, resultant_string);
 
	    convert_date_string (
		 dat_tim_string,
		 &cntrl_info_block.dir_dx[0].cur_filter.since);
	}
 
	/*
	**  Process qualifier "CREATED"
	**/
 
	status = cli$present($DESCR("CREATED"));
	if (status == CLI$_LOCPRES ||
	    status == CLI$_PRESENT ||
	    (status == CLI$_DEFAULTED &&
		cntrl_info_block.user_pref.display_date != DX_REVDATE &&
		cntrl_info_block.user_pref.display_date != DX_EXPDATE &&
		cntrl_info_block.user_pref.display_date != DX_BAKDATE))
	{
            cntrl_info_block.dir_dx[0].cur_filter.date = DX_CREDATE;
	}
 
	/*
	**  Process qualifier "MODIFIED"
	**/
 
	status = cli$present($DESCR("MODIFIED"));
	if (status == CLI$_LOCPRES || status == CLI$_PRESENT)
	{
            cntrl_info_block.dir_dx[0].cur_filter.date = DX_REVDATE;
	}
 
	/*
	**  Process qualifier "EXPIRED"
	**/
 
	status = cli$present($DESCR("EXPIRED"));
	if (status == CLI$_LOCPRES || status == CLI$_PRESENT)
	{
            cntrl_info_block.dir_dx[0].cur_filter.date = DX_EXPDATE;
	}
 
	/*
	**  Process qualifier "BACKUP"
	**/
 
	status = cli$present($DESCR("BACKUP"));
	if (status == CLI$_LOCPRES || status == CLI$_PRESENT)
	{
            cntrl_info_block.dir_dx[0].cur_filter.date = DX_BAKDATE;
	}
 
	/*
	**  Process qualifier "SORT_BY"
	**/
 
	cli$get_value($DESCR("SORT_BY"), &resultant_string_descrip, &len);
	resultant_string[len] = EOS;
        if (!strncmp (resultant_string, "NAME", len))
        {
            cntrl_info_block.dir_dx[0].sortby = sortname;
        }
        else if (!strncmp (resultant_string, "TYPE", len))
        {
            cntrl_info_block.dir_dx[0].sortby = sorttype;
        }
        else if (!strncmp (resultant_string, "SIZE", len))
        {
            cntrl_info_block.dir_dx[0].sortby = sortsize;
        }
        else if (!strncmp (resultant_string, "DATE", len))
        {
            switch (cntrl_info_block.dir_dx[0].cur_filter.date)
            {
                case DX_CREDATE:
                    cntrl_info_block.dir_dx[0].sortby = sortcredate;
		    break;
                case DX_REVDATE:
                    cntrl_info_block.dir_dx[0].sortby = sortrevdate;
		    break;
                case DX_EXPDATE:
                    cntrl_info_block.dir_dx[0].sortby = sortexpdate;
		    break;
                case DX_BAKDATE:
                    cntrl_info_block.dir_dx[0].sortby = sortbakdate;
		    break;
                default:
                    break;
            }
        }
 
	/*
	**  Process qualifier "MAXIMUM"
	**/
 
	if (cli$present($DESCR("MAXIMUM")) == CLI$_PRESENT)
	{
	    cli$get_value(
		    $DESCR("MAXIMUM"),
		    &resultant_string_descrip,
		    &len);
	    resultant_string[len] = EOS;
	    cntrl_info_block.dir_dx[0].cur_filter.maximum =
		atoi(resultant_string);
	}
 
	/*
	**  Process qualifier "MINIMUM"
	**/
 
	if (cli$present($DESCR("MINIMUM")) == CLI$_PRESENT)
	{
	    cli$get_value(
		    $DESCR("MINIMUM"),
		    &resultant_string_descrip,
		    &len);
	    resultant_string[len] = EOS;
	    cntrl_info_block.dir_dx[0].cur_filter.minimum =
		atoi(resultant_string);
	}
 
	/*
	**  Process qualifier "EXCLUDE"
	**/
 
	if (cli$present($DESCR("EXCLUDE")) == CLI$_PRESENT)
	{
	    strcpy(default_filespec, "*.*;*");
	    while (cli$get_value(
			$DESCR("EXCLUDE"),
			&resultant_string_descrip,
			&len) & 1)
	    {
		resultant_string[len] = EOS;
		insert_filespec_list(
		    &cntrl_info_block.dir_dx[0].cur_filter.exclude_filespec_list,
		    default_filespec,
		    resultant_string,
		    errmsg);
	    }
	}
    }
 
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	trnlnm(original, logicalQ)
const char *original;
int *logicalQ;
{
    struct logical_queue_entry_tag *E;
    int status;
    static _align (QUADWORD) int workingQ[2];
    int allocate_size;
 
    workingQ[0] = workingQ[1] = 0;
 
    /**  Allocate dynamic memory for a queue entry  **/
 
    allocate_size = sizeof (struct logical_queue_entry_tag);
    check_OK(lib$get_vm (
                 &allocate_size,
                 &E,
                 0))
 
    /*
    **  Allocate dynamic memory for the content of a queue entry.
    **	The size is the string length plus EOS.
    **/
 
    allocate_size = strlen(original)+1;
    check_OK(lib$get_vm (
                 &allocate_size,
                 &E->equivalent_name,
                 0))
    strcpy(E->equivalent_name, original);
 
    _INSQTI (E, workingQ);	    /**  Insert the first entry to queue  **/
    while (_REMQHI (workingQ, &E) != 3)
	trnlnm_1(E->equivalent_name, workingQ, logicalQ);
 
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
static int	trnlnm_1(original, workingQ, logicalQ)
const char *original;
int *workingQ;
int *logicalQ;
{
    char before[256],
	 after[256],
	 final[256];
    char postfix[256];
    $DESCRIPTOR (original_dst, original);
    $DESCRIPTOR (before_dst, before);
    unsigned long status;
    int maxindex;
    int index;
    short retlen;
    short dummy;
    struct {
	short buflen;
	short itemcode;
	char *buf;
	short *reslen;
    } itmlist_1 [] =
	{
	    {sizeof(int), LNM$_MAX_INDEX, &maxindex, &retlen},
	    {0, 0, 0, 0}
	},
      itmlist_2 [] =
	{
	    {sizeof (int), LNM$_INDEX, &index, &dummy},
	    {255, LNM$_STRING, after, &retlen},
	    {0, 0, 0, 0}
	};
 
    char *cp, *cp1;
    int allocate_size;
    struct logical_queue_entry_tag *E;
 
    /*  Find a logical name to be translate */
 
    strcpy(before, original);
    if ((cp = strchr(before, ':')) != 0)
	*cp = EOS;
 
    /* Get max index */
 
    LENGTH(before_dst) = strlen(before);
    if ((status = sys$trnlnm (
			 &LNM$M_CASE_BLIND,
			 $DESCR ("LNM$FILE_DEV"),
			 &before_dst,
			 0,
			 itmlist_1)) != SS$_NORMAL)
    {	/**  No more translation  **/
	if ((cp = strchr(original, '[')) != 0)
	{   /**  Remove all '[' except the first one  **/
	    cp++;
	    while ((cp1 = strchr(cp, '[')) != 0)
		memmove (cp1-1, cp1+1, strlen(cp1+1)+1);
	}
 
	allocate_size = sizeof (struct logical_queue_entry_tag);
	check_OK(lib$get_vm (
		     &allocate_size,
		     &E,
		     0))
	E->equivalent_name = original;
	_INSQTI (E, logicalQ);
	return DX__NORMAL;
    }
 
    for (index = 0; index <= maxindex; index++)
    {
	/*  Find a logical name to be translate */
	strcpy(before, original);
	if ((cp = strchr(before, ':')) == 0)
	{
	    postfix[0] = EOS;
	}
	else
	{
	    strcpy(postfix, cp);
	    *cp = EOS;
	}
	LENGTH(before_dst) = strlen(before);
 
	/* Get i-th equivalent string */
	sys$trnlnm (
		 &LNM$M_CASE_BLIND,
		 $DESCR ("LNM$FILE_DEV"),
		 &before_dst,
		 0,
		 itmlist_2);
	after[retlen] = EOS;
 
	/*  Post to result */
	if ((cp = strchr(after, ':')) == 0)
	    sprintf(final, "%s%s", after, postfix);
	else if (postfix[0] == ':')
	    sprintf(final, "%s%s", after, postfix+1);
	else
	    sprintf(final, "%s%s", after, postfix);
 
	allocate_size = sizeof (struct logical_queue_entry_tag);
	check_OK(lib$get_vm (
		     &allocate_size,
		     &E,
		     0))
	allocate_size = strlen(final)+1;
	check_OK(lib$get_vm (
		     &allocate_size,
		     &E->equivalent_name,
		     0))
	strcpy(E->equivalent_name, final);
 
	_INSQTI (E, workingQ);
    }
 
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	insert_dids(struct w_dids_tag **l,char *s, unsigned short int d[3],
		unsigned short int c)
{
    struct w_dids_tag *m;
    int allocate_size;
 
    if (*l != NULL)
    {
        insert_dids(&((*l)->next), s, d, c);
    }
    else
    {
	allocate_size = sizeof (struct w_dids_tag);
	check_OK(lib$get_vm (
		     &allocate_size,
		     &m,
		     0))
	strcpy(m->equivalent_name, s);
	m->did[0] = d[0];
	m->did[1] = d[1];
	m->did[2] = d[2];
	m->chan = c;
	m->next = (struct w_dids_tag *) NULL;
	*l = m;
    }
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	set_cwd_and_par(s)
char *s;
{
    static unsigned long context;
 
    size_t len;
    char wild[MAXFILESPEC+1];
    $DESCRIPTOR (wild_descrip, wild);
    char filespec[MAXFILESPEC+1];
    $DESCRIPTOR (filespec_descrip, filespec);
    char *cp;
 
    /*
    **	Set current woking directory
    **/
 
    len = strcspn (s, "]")+1;
    strncpy(DX_CURRENT_DIRECTORY.cur_dir, s, len);
    DX_CURRENT_DIRECTORY.cur_dir[len] = EOS;
 
    /*
    **  Set parent directory
    **/
 
    DX_CURRENT_DIRECTORY.par_dir[0] = EOS;
    strcpy(wild, DX_CURRENT_DIRECTORY.cur_dir);
    wild[strlen(wild)-1] = EOS;
    strcat(wild, ".-]*.*;*");
    LENGTH(wild_descrip) = strlen(wild);
    if (lib$find_file (
	    &wild_descrip,
	    &filespec_descrip,
	    &context,
	    0,
	    0,
	    0,
	    0) & 1)
    {
	if ((cp = strchr(filespec, ']')) != NULL)
	{
	    *(cp+1) = EOS;
	    strcpy(DX_CURRENT_DIRECTORY.par_dir, filespec);
	}
    }
    lib$find_file_end (
	&context);
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
unsigned short get_chan(nam)
char *nam;
{
    struct dsc$descriptor_s rslbuf =
	{strlen(nam), DSC$K_DTYPE_T, DSC$K_CLASS_S, nam};
    unsigned short chan;
 
    check_OK (sys$assign(
		    &rslbuf, &chan, 0, 0))
    return chan;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	insert_filespec_list(l, dna, s, errmsg)
struct filespec_list_tag **l;
char *dna;
char *s;
char *errmsg;
{
    static _align (QUADWORD) int logicalQ[2];
    static unsigned long context;
 
    struct filespec_list_tag temp_entry;
    char dev_dir[MAXFILESPEC+1];
    char expand_name[NAM$C_MAXRSS];
    struct logical_queue_entry_tag *E;
    unsigned long int status;
    struct FAB fab;
    struct NAM nam;
    char fab_dna[MAXFILESPEC+1];
    int number_of_bytes;
 
    char wild[MAXFILESPEC+1];
    $DESCRIPTOR (wild_descrip, wild);
    char devname[MAXFILESPEC+1];
    char *cp;
    char filespec[MAXFILESPEC+1];
    $DESCRIPTOR (filespec_descrip, filespec);
    unsigned short int len;
    char errmsg1[MAXFILESPEC+1];
    $DESCRIPTOR (errmsg1_descrip, errmsg1);
 
    logicalQ[0] = logicalQ[1] = 0;
 
    /**  Initialize FAB and NAM blocks  **/
 
    fab = cc$rms_fab;
    nam = cc$rms_nam;
 
    nam.nam$l_esa = &expand_name;
    nam.nam$b_ess = NAM$C_MAXRSS;
 
    /**  Get directory fid, name, and equivalent name  **/
    fab.fab$l_fna = s;
    fab.fab$b_fns = strlen(s);
    strcpy(fab_dna, dna);
    fab.fab$l_dna = fab_dna;
    fab.fab$b_dns = strlen(fab_dna);
    fab.fab$l_nam = &nam;
 
    status = sys$parse(&fab, 0, 0);
    if (!(status & 1))
    {
        lib$sys_getmsg (
            &status,
            &len,
            &errmsg1_descrip,
            0,
            0);
	errmsg1[len] = EOS;
	strcpy(errmsg, errmsg1);
	strcpy(dna, s);
	return DX__ERROR;
    }
 
    /*
    **	Is this directory accessible?
    **/
 
    strncpy(wild, expand_name, nam.nam$b_esl);
    wild[nam.nam$b_esl] = EOS;
    LENGTH(wild_descrip) = strlen(wild);
    status = lib$find_file (
		&wild_descrip,
		&filespec_descrip,
		&context,
		0,
		0,
		0,
		0);
    lib$find_file_end (
	&context);
    if (status != RMS$_FNF && !(status & 1))
    {
        lib$sys_getmsg (
            &status,
            &len,
            &errmsg1_descrip,
            0,
            0);
	errmsg1[len] = EOS;
	strcpy(errmsg, errmsg1);
	strcpy (dna, wild);
	return DX__ERROR;
    }
 
    strncpy(
	dna,
	nam.nam$l_dev,
	nam.nam$b_dev+nam.nam$b_dir+nam.nam$b_name+nam.nam$b_type+nam.nam$b_ver);
    dna[nam.nam$b_dev+nam.nam$b_dir+nam.nam$b_name+nam.nam$b_type+nam.nam$b_ver] = EOS;
    temp_entry.w_dids = NULL;
 
    strncpy (
	temp_entry.name,
	nam.nam$l_name,
	nam.nam$b_name+nam.nam$b_type+nam.nam$b_ver);
    temp_entry.name[nam.nam$b_name+nam.nam$b_type+nam.nam$b_ver] = EOS;
 
    strncpy (dev_dir, nam.nam$l_dev, nam.nam$b_dev+nam.nam$b_dir);
    dev_dir[nam.nam$b_dev+nam.nam$b_dir] = EOS;
 
    /*
    **	Translate current directory logical name to equivalent name(s).
    **	The directory may contains more than one physical directories.
    **/
 
    trnlnm(dev_dir, logicalQ);
    while (_REMQHI (logicalQ, &E) != 3)
    {
	number_of_bytes = strlen(E->equivalent_name)+1;
        if ((cp = strstr (E->equivalent_name, ".000000]")) != NULL)
	{
	    *cp++ = ']';
	    *cp = EOS;
	}
	fab.fab$l_fna = E->equivalent_name;
	fab.fab$b_fns = strlen(E->equivalent_name);
	fab.fab$l_nam = &nam;
 
	status = sys$parse(&fab, 0, 0);
        if (!(status & 1))
        {
	    lib$sys_getmsg (
		&status,
		&len,
		&errmsg1_descrip,
		0,
		0);
	    errmsg1[len] = EOS;
	    strcpy(errmsg, errmsg1);
	    strcpy(dna, E->equivalent_name);
	    return DX__ERROR;
        }
 
	/*
	**  Get an i/o channel for each physical device
	**/
 
	strcpy(devname, E->equivalent_name);
	cp = strchr(devname, ':');
	*++cp = EOS;
 
	insert_dids (
	    &temp_entry.w_dids,
	    E->equivalent_name,
	    nam.nam$w_did,
	    get_chan(devname));
        check_OK(lib$free_vm (
                     &number_of_bytes,
                     &E->equivalent_name,
                     0))
    }
    insert_filespec_list$1(l, temp_entry);
 
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	insert_filespec_list$1(l, e)
struct filespec_list_tag **l;
struct filespec_list_tag e;
{
    struct filespec_list_tag *p;
    int allocate_size;
 
    if (*l != (struct filespec_list_tag *)NULL)
    {
	insert_filespec_list$1(&((*l)->next), e);
    }
    else
    {
	allocate_size = sizeof (struct filespec_list_tag);
        check_OK(lib$get_vm (
                     &allocate_size,
                     &p,
                     0))
	strcpy(p->name, e.name);
	p->w_dids = e.w_dids;
	p->next = (struct filespec_list_tag *)NULL;
	*l = p;
    }
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	    broadcast_routine()
{
    static unsigned short word_terminator_code;
 
    unsigned long status;
    char message[BROADMSGLENG+1];
    $DESCRIPTOR (message_descrip, message);
    unsigned short message_length;
    unsigned short message_type;
    unsigned long prev_display_id, display_id;
    long int start_row, start_column, col;
    char *prompt = "Press RETURN to continue ...";
    $DESCRIPTOR (prompt_descrip, prompt);
    unsigned long int temp_keyboard_id;
    char resultant[512];
    $DESCRIPTOR (resultant_descrip, resultant);
    int i;
 
    /*
    **	Do not use 'check' to verify the return status
    **	(in case of user disables broadcast message)
    **/
 
    status = smg$get_broadcast_message (
		  &cntrl_info_block.pasteboard_id,
		  &message_descrip,
		  &message_length,
		  &message_type);
 
    if (status == SS$_NORMAL)
    {
	message[message_length] = EOS;
	for (i = 0;  i < message_length && !isprint ((int) message[i]);  i++)
	{
;
	}
	POINTER (message_descrip) += i;
	LENGTH(message_descrip) = message_length - i;
 
	check_OK(smg$find_cursor_display (
		  &cntrl_info_block.pasteboard_id,
		  &prev_display_id,
		  0,
		  0))
	check_OK(smg$return_cursor_pos (
		  &prev_display_id,
		  &start_row,
		  &start_column))
	check_OK(smg$create_virtual_display (
		     &cntrl_info_block.pasteboard_rows,
		     &cntrl_info_block.pasteboard_width,
		     &display_id,
		     &SMG$M_DISPLAY_CONTROLS,
		     0,
		     0))
	check_OK(smg$paste_virtual_display (
		     &display_id,
		     &cntrl_info_block.pasteboard_id,
		     &1,
		     &1,
		     0))
	smg$put_chars (
		 &display_id,
		 &message_descrip,
		 0,
		 0,
		 0,
		 &SMG$M_BOLD,
		 0,
		 0);
	col = cntrl_info_block.pasteboard_width - strlen(prompt);
	LENGTH(prompt_descrip) = strlen(prompt);
	check_OK(smg$put_chars (
		     &display_id,
		     &prompt_descrip,
		     &cntrl_info_block.pasteboard_rows,
		     &col,
		     0,
		     &SMG$M_BOLD,
		     0,
		     0))
	check_OK(smg$cancel_input (
		     &cntrl_info_block.keyboard_id))
	gets (resultant);
 
	check_OK(smg$erase_display (
		     &display_id,
		     0,
		     0,
		     0,
		     0))
	check_OK(smg$delete_virtual_display (
		     &display_id))
	check_OK(smg$set_cursor_abs (
		  &prev_display_id,
		  &start_row,
		  &start_column))
    }
    return DX__NORMAL;
}
