  File PURGE (DELETE) API? 
 The Question is:
 
I was wondering if you could tell me how I might program a "purge" program
that will give the option to purge all files or give the options to purge a
certain file and keep certain version numbers(for a VAX system.)
 
 
 The Answer is:
 
  Use of the existing PURGE command is the easiest approach, either
  directly, via lib$spawn, via C system, via DECnet task-to-task, via
  sys$sndjbc, or otherwise.
 
  API-level program implementations are possible using lib$find_file,
  lib$delete_file, and lib$find_file_end, or via RMS service calls
  such as sys$search and sys$erase/sys$remove, or via XQP (F11XQP,
  IO$_ACPCONTROL) calls (as described in the I/O User's Reference Manual).
 
  The following C example shows a directory tree-deletion operation using
  calls such as lib$find_file -- this example assumes that the getopt()
  call is available.
 
	--
 
/*
** COPYRIGHT (c) 1994-1999 BY
** DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS.
** ALL RIGHTS RESERVED.
**
** THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED
** ONLY  IN  ACCORDANCE  OF  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
** INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR  ANY  OTHER
** COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
** OTHER PERSON.  NO TITLE TO AND  OWNERSHIP OF THE  SOFTWARE IS  HEREBY
** TRANSFERRED.
**
** THE INFORMATION IN THIS SOFTWARE IS  SUBJECT TO CHANGE WITHOUT NOTICE
** AND  SHOULD  NOT  BE  CONSTRUED  AS A COMMITMENT BY DIGITAL EQUIPMENT
** CORPORATION.
**
** DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE  OR  RELIABILITY OF ITS
** SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.
**
*/
/*
**++
**  Facility:
**
**      DELETE_TREE
**
**  Version: V1.0
**
**  Abstract:
**
**      Deletes the specified directory tree.
**
**  Author:
**      Steve Hoffman
**
**  Creation Date:  14-Mar-1994
**
**  Modification History:
**--
*/
 
#include &lt;stdarg.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;ssdef.h&gt;
#include &lt;stsdef.h&gt;
#include &lt;str$routines.h&gt;
#include &lt;lib$routines.h&gt;
#include &lt;descrip.h&gt;
#include &lt;strdef.h&gt;
#include &lt;starlet.h&gt;
#include &lt;rms.h&gt;
#include &lt;fiddef.h&gt;
#include &lt;string.h&gt;
#include &lt;unistd.h&gt;
 
typedef unsigned long int u_long;
typedef unsigned short int u_short;
typedef char u_char;
typedef struct dsc$descriptor desc_t;
 
 
 
 
/*
** ************************************************************************
** common definitions
** ************************************************************************
*/
 
 
typedef u_short fid_t[(FID$K_LENGTH/sizeof(u_short))];
 
typedef struct itemlist_s itemlist_t;
struct itemlist_s
    {
    /*
    **	A standard VMS itemlist `il3' data structure...
    */
    u_short itmlen;
    u_short itmcod;
    u_char *itmadr;
    u_short *itmrla;
    };
 
typedef struct hdr_s hdr_t;
struct hdr_s
    {
    /*
    **	The common header; prefixes all heap-allocated internal
    **	data structures...  Used by a couple of routines -- mostly
    **	in the area of memory management and corruption detection.
    */
    u_short size;
    u_short type;
    void *core;
    };
 
typedef struct dir_s dir_t;
struct dir_s
    {
    /*
    **	A standard VMS itemlist `il3' data structure...
    */
    hdr_t hdr;
    dir_t *nextdir;
    u_short depth;
    char fna[NAM$C_MAXRSS+1];
    u_long fnl;
    };
 
typedef struct core_s core_t;
struct core_s
    {
    /*
    **	This is the core data structure for this application...
    */
    hdr_t hdr;
    char fns;
    char fna[NAM$C_MAXRSS+1];
    char dns;
    char dna[NAM$C_MAXRSS+1];
    char esa[NAM$C_MAXRSS+1];
    char rsa[NAM$C_MAXRSS+1];
 
    desc_t userinput_d;
 
    struct
	{
	char *target_dirspec;
	u_long verbose;
	u_long quit;
	u_long confirm;
	u_long noconfirm;
	u_long errors;
	} options;
 
    u_short iosb[4];
 
    u_long context;
    fid_t didx_old;
    dir_t *nextdir;
 
    struct FAB fab;
    struct NAM nam;
    struct RAB rab;
    };
 
 
/*
** ************************************************************************
** The externals; used by and required for getopt();
** ************************************************************************
*/
extern char *optarg;
extern int optind;
extern int opterr;
 
/*
** ************************************************************************
** The function prototypes...
** ************************************************************************
*/
 
u_long dt_parse_opt( core_t *, u_long, u_char ** );
u_long getopt( u_long, u_char **, u_char * );
u_long dt_file_scan( core_t * );
u_long dt_file_scan_ok( struct FAB * );
u_long dt_file_scan_ng( struct FAB * );
u_long dt_delete_confirm( desc_t *, struct FAB *, core_t * );
u_long dt_load_name( core_t *, char *, char * );
 
 
/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      parses the user-specified options via a getopt().
**
**  FORMAL PARAMETERS:
**
**      {@subtags@}
**
**  RETURN VALUE:
**
**      1:  happiness
**
**  SIDE EFFECTS:
**
**      none
**
**  DESIGN:
**
**      This routine is highly UNIX-flavoured.  This routine
**	handles all argument parsing and processing.  The key
**	(and only) interface used by this routine to communicate
**	with the rest of the program is the `options' substructure
**	found within the core data structure.
**
**
**--
*/
u_long dt_parse_opt( core_t *core, u_long argc, u_char **argv )
    {
    u_long retstat;
    u_long c;
    u_long asciw;
    u_char *validargs = (u_char *) "vcdf:";
    u_long got_an_option = 0;
 
    while ((c = getopt( argc, argv, validargs )) != EOF)
	{
	got_an_option++;
        switch ( c )
	    {
            case 'f':
		/*
		**  Start processing at the specified file...
		*/
	        asciw = strlen( (char *) optarg);
	        if ( asciw &lt;= NAM$C_MAXRSS )
		    {
		    retstat = dt_load_name( core, optarg, 0 );
		    if ( !$VMS_STATUS_SUCCESS( retstat ) ) return retstat;
		    }
                break;
            case 'd':
		/*
		**  Start processing at the specified default file...
		*/
	        asciw = strlen( (char *) optarg);
	        if ( asciw &lt;= NAM$C_MAXRSS )
		    {
		    retstat = dt_load_name( core, 0, optarg );
		    if ( !$VMS_STATUS_SUCCESS( retstat ) ) return retstat;
		    }
                break;
            case 'v':
		/*
		**  The typical verbose (chatty) option...
		*/
		core-&gt;options.verbose++;
                break;
            case 'c':
		/*
		**  Confirm the deletion.
		*/
		core-&gt;options.confirm++;
                break;
            case '?':
            default:
		core-&gt;options.errors++;
	    }
	}
 
    if ( !got_an_option )
	core-&gt;options.errors++;
 
    if ( core-&gt;options.errors )
	{
        fprintf (stderr,
	    "RMS directory tree deletion utility.\n" );
        fprintf (stderr,
	    "Set up foreign command: dt :== $%s\n", argv[0]);
        fprintf (stderr,
	    "usage: dt [-v] [-c] -f filespec [-d defspec]\n" );
        fprintf (stderr, " -v\tverbose\n" );
        fprintf (stderr, " -c\tconfirm\n" );
        fprintf (stderr, " -f\tfile or directory tree to delete\n" );
        fprintf (stderr, " -d\tdefault file specification\n" );
        exit( SS$_NORMAL );
	}
 
    return 1;
    }
 
 
/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      This initializes some fields in the core structure.
**
**  FORMAL PARAMETERS:
**
**      a pointer to the core structure.
**
**  RETURN VALUE:
**
**      SS$_NORMAL:	happiness
**
**  SIDE EFFECTS:
**
**      none.
**
**  DESIGN:
**
**--
*/
dt_init( core_t *core )
    {
    unsigned long int retstat;
    struct FAB *fab;
    struct NAM *nam;
 
    memset( core, 0, sizeof( core_t ) );
 
    core-&gt;userinput_d.dsc$a_pointer = 0;
    core-&gt;userinput_d.dsc$w_length = 0;
    core-&gt;userinput_d.dsc$b_class = DSC$K_CLASS_D;
    core-&gt;userinput_d.dsc$b_dtype = DSC$K_DTYPE_T;
    core-&gt;dns = 0;
    core-&gt;dna[0] = 0;
 
    core-&gt;context = 0;
    core-&gt;nextdir = 0;
    memset( core-&gt;didx_old, 0, FID$K_LENGTH );
 
    core-&gt;options.confirm = 0;
    core-&gt;options.errors = 0;
 
    fab = &amp;core-&gt;fab;
    nam = &amp;core-&gt;nam;
    core-&gt;fab = cc$rms_fab;
    core-&gt;nam = cc$rms_nam;
 
    fab-&gt;fab$l_fop = FAB$M_NAM;
    fab-&gt;fab$b_fac = 0;
    fab-&gt;fab$b_shr = 0;
    fab-&gt;fab$l_nam = nam;
    fab-&gt;fab$w_ifi = 0;
    fab-&gt;fab$l_fna = 0;
    fab-&gt;fab$b_fns = 0;
    fab-&gt;fab$l_dna = 0;
    fab-&gt;fab$b_dns = 0;
 
    fab-&gt;fab$l_ctx = (u_long) core;
 
    nam-&gt;nam$l_esa = core-&gt;esa;
    nam-&gt;nam$b_ess = NAM$C_MAXRSS;
    nam-&gt;nam$b_esl = 0;
    nam-&gt;nam$l_rsa = core-&gt;rsa;
    nam-&gt;nam$b_rss = NAM$C_MAXRSS;
    nam-&gt;nam$b_rsl = 0;
 
    return SS$_NORMAL;
    }
/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      This stuffs the directory specification or the default
**	directory specification into the storage buffer.
**
**  FORMAL PARAMETERS:
**
**      the core, the file name, and the default file name.
**
**  RETURN VALUE:
**
**      SS$_NORMAL:	happiness
**
**  SIDE EFFECTS:
**
**      none.
**
**  DESIGN:
**
**--
*/
u_long dt_load_name( core_t *core, char *filename, char *defname )
    {
    unsigned long int retstat;
    struct FAB *fab;
    struct NAM *nam;
 
    fab = &amp;core-&gt;fab;
    nam = &amp;core-&gt;nam;
 
    if ( (int) filename )
	{
	core-&gt;fns = strlen( filename );
	strncpy( core-&gt;fna, filename, NAM$C_MAXRSS );
	fab-&gt;fab$l_fna = core-&gt;fna;
	fab-&gt;fab$b_fns = core-&gt;fns;
	}
 
    if ( (int) defname )
	{
	core-&gt;dns = strlen( defname );
	strncpy( core-&gt;dna, defname, NAM$C_MAXRSS );
	fab-&gt;fab$l_dna = core-&gt;dna;
	fab-&gt;fab$b_dns = strlen( defname );
	}
 
 
    return SS$_NORMAL;
    }
 
/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      This scans for files, looking for the directories to be deleted.
**
**  FORMAL PARAMETERS:
**
**      the standard C main function arguments...
**
**  RETURN VALUE:
**
**      1: happiness
**
**  SIDE EFFECTS:
**
**      none.
**
**  DESIGN:
**
**--
*/
u_long dt_file_scan( core_t *core )
    {
    u_long retstat;
 
    retstat = lib$file_scan( &amp;core-&gt;fab,
	dt_file_scan_ok, dt_file_scan_ng,
	&amp;core-&gt;context );
    if ( retstat == RMS$_NMF ) retstat = SS$_NORMAL;
    if ( retstat == RMS$_DNF ) retstat = SS$_NORMAL;
    if ( retstat == RMS$_FNF ) retstat = SS$_NORMAL;
    if ( !$VMS_STATUS_SUCCESS( retstat ) ) return retstat;
 
    return SS$_NORMAL;
    }
/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      This processes the specified file, and marks it for delete.
**
**  FORMAL PARAMETERS:
**
**      the standard C main function arguments...
**
**  RETURN VALUE:
**
**      1: happiness
**
**  SIDE EFFECTS:
**
**      none.
**
**  DESIGN:
**
**--
*/
u_long dt_file_scan_ok( struct FAB *fab )
    {
    u_long retstat;
    char *wildcard = "*.*;*";
    struct NAM *nam;
    core_t *core;
    fid_t fidx;
    dir_t *dir;
    u_long devdirsize;
 
    nam = fab-&gt;fab$l_nam;
    core = (core_t *) fab-&gt;fab$l_ctx;
 
    retstat = memcmp( &amp;core-&gt;didx_old, nam-&gt;nam$w_did, FID$K_LENGTH );
    if ( retstat )
	{
	/*
	**  A new file.  And the file is in a new directory file...
	*/
	dir = malloc( sizeof( dir_t ));
	dir-&gt;nextdir = core-&gt;nextdir;
	core-&gt;nextdir = dir;
 
	devdirsize = 0;
	memcpy( dir-&gt;fna + devdirsize, nam-&gt;nam$l_dev, nam-&gt;nam$b_dev );
	devdirsize += nam-&gt;nam$b_dev;
	memcpy( dir-&gt;fna + devdirsize, nam-&gt;nam$l_dir, nam-&gt;nam$b_dir );
	devdirsize += nam-&gt;nam$b_dir;
	memcpy( dir-&gt;fna + devdirsize, wildcard, strlen( wildcard ));
	devdirsize += strlen( wildcard );
	dir-&gt;fnl = devdirsize;
	memcpy( &amp;core-&gt;didx_old, nam-&gt;nam$w_did, FID$K_LENGTH );
	}
 
    return SS$_NORMAL;
    }
 
/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      This processes the specified file.
**
**  FORMAL PARAMETERS:
**
**      the standard C main function arguments...
**
**  RETURN VALUE:
**
**      1: happiness
**
**  SIDE EFFECTS:
**
**      none.
**
**  DESIGN:
**
**--
*/
u_long dt_file_scan_ng( struct FAB *fab )
    {
    u_long retstat;
    struct NAM *nam;
 
    nam = fab-&gt;fab$l_nam;
 
    printf("Unable to process %*.*s\n",
	nam-&gt;nam$b_rsl, nam-&gt;nam$b_rsl, nam-&gt;nam$l_rsa );
 
    return SS$_NORMAL;
    }
 
/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      This scans for files, looking for the directories to be deleted.
**
**  FORMAL PARAMETERS:
**
**      the standard C main function arguments...
**
**  RETURN VALUE:
**
**      1: happiness
**
**  SIDE EFFECTS:
**
**      none.
**
**  DESIGN:
**
**--
*/
u_long dt_delete_confirm( desc_t *file_d, struct FAB *fab, core_t *core )
    {
    u_long retstat;
    u_long delstat;
    u_short nyaq_len;
    char fao_b[NAM$C_MAXRSS];
    desc_t fao_d;
    $DESCRIPTOR( file_prompt, "Delete !AS?" );
    $DESCRIPTOR( vnodelete, "Not deleting !AS" );
    $DESCRIPTOR( faclete, "Deleting !AS" );
    $DESCRIPTOR( nyaq_prompt, "Delete? (Y/N/Q/A) " );
 
    delstat = SS$_NORMAL;
 
    if ( core-&gt;options.quit )
	{
	/*
	**  Skip any sort of delete.
	*/
	delstat = SS$_NODELETE;
	goto dt_delete_confirm_done;
	}
 
    if ( core-&gt;options.noconfirm )
	{
	/*
	**  We were asked to confirm each deletion, then countermanded.
	*/
	delstat = SS$_NORMAL;
	goto dt_delete_confirm_done;
	}
 
    if ( core-&gt;options.confirm )
	{
	/*
	**  Confirm each deletion.
	*/
	delstat = SS$_NODELETE;
 
	fao_d.dsc$a_pointer = fao_b;
	fao_d.dsc$w_length = NAM$C_MAXRSS;
	fao_d.dsc$b_class = DSC$K_CLASS_S;
	fao_d.dsc$b_dtype = DSC$K_DTYPE_T;
 
	retstat = sys$fao( &amp;file_prompt,
	    &amp;fao_d.dsc$w_length, &amp;fao_d, file_d );
	if ( !$VMS_STATUS_SUCCESS( retstat ) )
	    {
	    core-&gt;options.quit++;
	    delstat = SS$_NODELETE;
	    goto dt_delete_confirm_done;
	    }
 
	lib$put_output( &amp;fao_d );
 
	retstat = lib$get_input( &amp;core-&gt;userinput_d, &amp;nyaq_prompt, &amp;nyaq_len );
	if ( !$VMS_STATUS_SUCCESS( retstat ) )
	    {
	    core-&gt;options.quit++;
	    delstat = SS$_NODELETE;
	    goto dt_delete_confirm_done;
	    }
 
	str$upcase( &amp;core-&gt;userinput_d, &amp;core-&gt;userinput_d );
 
	/*
	**  What the user answer means...
	**
	**  YES	    causes the file to be deleted.
	**  TRUE    causes the file to be deleted.
	**  ALL	    causes all files to be deleted.
	**  QUIT    quits; no more files are deleted.
	**  FALSE   the current deletion is not performed.
	**  NO	    the current deletion is not performed.
	**
	**  Any other answer from the user is treated as a FALSE or a NO.
	*/
 
	retstat = strncmp( core-&gt;userinput_d.dsc$a_pointer, "YES", nyaq_len );
	if ( !retstat )
	    delstat = SS$_NORMAL;
	retstat = strncmp( core-&gt;userinput_d.dsc$a_pointer, "TRUE", nyaq_len );
	if ( !retstat )
	    delstat = SS$_NORMAL;
	retstat = strncmp( core-&gt;userinput_d.dsc$a_pointer, "QUIT", nyaq_len );
	if ( !retstat )
	    core-&gt;options.quit++;
	retstat = strncmp( core-&gt;userinput_d.dsc$a_pointer, "ALL", nyaq_len );
	if ( !retstat )
	    delstat = core-&gt;options.noconfirm = SS$_NORMAL;
 
	goto dt_delete_confirm_done;
	}
 
dt_delete_confirm_done:
 
    if ( core-&gt;options.verbose )
	{
	fao_d.dsc$a_pointer = fao_b;
	fao_d.dsc$w_length = NAM$C_MAXRSS;
	fao_d.dsc$b_class = DSC$K_CLASS_S;
	fao_d.dsc$b_dtype = DSC$K_DTYPE_T;
 
	if ( $VMS_STATUS_SUCCESS( delstat ) )
	    retstat = sys$fao( &amp;faclete,
		&amp;fao_d.dsc$w_length, &amp;fao_d, file_d );
	else
	    retstat = sys$fao( &amp;vnodelete,
		&amp;fao_d.dsc$w_length, &amp;fao_d, file_d );
 
	lib$put_output( &amp;fao_d );
	}
 
    return delstat;
    }
/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      This scans for files, looking for the directories to be deleted.
**
**  FORMAL PARAMETERS:
**
**      the standard C main function arguments...
**
**  RETURN VALUE:
**
**      1: happiness
**
**  SIDE EFFECTS:
**
**      none.
**
**  DESIGN:
**
**--
*/
u_long dt_delete_all( core_t *core )
    {
    u_long retstat;
    dir_t *dir;
    dir_t *dir_old;
    desc_t dsc;
 
    dsc.dsc$b_dtype = DSC$K_DTYPE_T;
    dsc.dsc$b_class = DSC$K_CLASS_S;
 
    dir = core-&gt;nextdir;
    while ((int) dir )
	{
	dsc.dsc$w_length = dir-&gt;fnl;
	dsc.dsc$a_pointer = dir-&gt;fna;
 
	retstat = lib$delete_file( &amp;dsc, 0, 0,
	    0, 0, dt_delete_confirm,
	    core, 0, 0 );
	if ( !$VMS_STATUS_SUCCESS( retstat ) )
	    break;
 
	dir_old = dir;
	dir = dir-&gt;nextdir;
	free( dir_old );
 
	}
 
    return SS$_NORMAL;
    }
 
/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      This processes the specified file, and marks it for delete.
**
**  FORMAL PARAMETERS:
**
**      the standard C main function arguments...
**
**  RETURN VALUE:
**
**      1: happiness
**
**  SIDE EFFECTS:
**
**      none.
**
**  DESIGN:
**
**--
*/
dt_cleanup( core_t *core )
    {
    u_long retstat;
 
    retstat = lib$file_scan_end( &amp;core-&gt;fab, &amp;core-&gt;context );
    if ( !$VMS_STATUS_SUCCESS( retstat )) return retstat;
 
    return SS$_NORMAL;
    }
 
/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      This is the mainline of the program.
**
**  FORMAL PARAMETERS:
**
**      the standard C main function arguments...
**
**  RETURN VALUE:
**
**      1: happiness
**
**  SIDE EFFECTS:
**
**      none.
**
**  DESIGN:
**
**--
*/
main( u_long argc, u_char **argv )
    {
    u_long retstat;
    core_t core_buffer, *core;
    fid_t fid;
 
    core = &amp;core_buffer;
 
    retstat = dt_init( core );
    if ( !$VMS_STATUS_SUCCESS( retstat ) ) return retstat;
 
    retstat = dt_parse_opt( core, argc, argv );
    if ( !$VMS_STATUS_SUCCESS( retstat ) ) return retstat;
 
    retstat = dt_file_scan( core );
    if ( !$VMS_STATUS_SUCCESS( retstat ) ) return retstat;
 
    retstat = dt_delete_all( core );
    if ( !$VMS_STATUS_SUCCESS( retstat ) ) return retstat;
 
    retstat = dt_cleanup( core );
    if ( !$VMS_STATUS_SUCCESS( retstat ) ) return retstat;
 
    return 1;
    }
 
 
