mirror of
https://github.com/open-simh/simtools.git
synced 2026-01-24 11:22:09 +00:00
Dismount command lost drive parameter in previous commit. Under windows, limit confusion by assigning pseudo-drive letters for images more carefully. Avoid letters used by Windows. Dir /detail wasn't reporting rab$b_fsz correctly. qualifier list keyword syntax error now reported correctly. Include Windows release .exes in git. Improve conditional compilation in compat.
2288 lines
77 KiB
C
2288 lines
77 KiB
C
#define MODULE_NAME ODS2
|
|
|
|
/* Feb-2016, v1.4 add readline support, dir/full etc
|
|
* See git commit messages for details.
|
|
*/
|
|
|
|
/* Jul-2003, v1.3hb, some extensions by Hartmut Becker */
|
|
|
|
/* Ods2.c v1.3 Mainline ODS2 program */
|
|
|
|
/*
|
|
This is part of ODS2 written by Paul Nankervis,
|
|
email address: Paulnank@au1.ibm.com
|
|
|
|
ODS2 is distributed freely for all members of the
|
|
VMS community to use. However all derived works
|
|
must maintain comments in their source to acknowledge
|
|
the contibution of the original author.
|
|
|
|
The modules in ODS2 are:-
|
|
|
|
ACCESS.C Routines for accessing ODS2 disks
|
|
CACHE.C Routines for managing memory cache
|
|
COMPAT.C Routines for OS compatibility
|
|
DEVICE.C Routines to maintain device information
|
|
DIRECT.C Routines for handling directories
|
|
ODS2.C The mainline program
|
|
PHYVMS.C Routine to perform physical I/O
|
|
RMS.C Routines to handle RMS structures
|
|
SYSMSG.C Routines to convert status codes to text
|
|
UPDATE.C Routines for updating ODS2 structures
|
|
VMSTIME.C Routines to handle VMS times
|
|
|
|
On non-VMS platforms PHYVMS.C should be replaced as follows:-
|
|
|
|
Unix PHYUNIX.C
|
|
OS/2 PHYOS2.C
|
|
Windows 95/NT PHYNT.C
|
|
|
|
For example under OS/2 the program is compiled using the GCC
|
|
compiler with the single command:-
|
|
|
|
gcc -fdollars-in-identifiers ods2.c,rms.c,direct.c,
|
|
access.c,device.c,cache.c,phyos2.c,vmstime.c
|
|
|
|
For accessing disk images (e.g. .ISO or simulator files),
|
|
replace PHYVMS.C with DISKIO.c and define DISKIMAGE
|
|
|
|
The included make/mms/com files do all this for you.
|
|
*/
|
|
|
|
/* Modified by:
|
|
* Feb 2016 Timothe Litt <litt at acm _ddot_ org>
|
|
* Bug fixes, readline support, build on NT without wnaspi32,
|
|
* Decode error messages, patch from vms2linux.de. VS project files.
|
|
* Rework command parsing and help. Bugs, bugs & bugs. See git
|
|
* commit history for details.
|
|
*
|
|
* 31-AUG-2001 01:04 Hunter Goatley <goathunter@goatley.com>
|
|
*
|
|
* For VMS, added routine getcmd() to read commands with full
|
|
* command recall capabilities.
|
|
*
|
|
*/
|
|
|
|
/* This version will compile and run using normal VMS I/O by
|
|
defining VMSIO
|
|
*/
|
|
|
|
/* This is the top level set of routines. It is fairly
|
|
simple minded asking the user for a command, doing some
|
|
primitive command parsing, and then calling a set of routines
|
|
to perform whatever function is required (for example COPY).
|
|
Some routines are implemented in different ways to test the
|
|
underlying routines - for example TYPE is implemented without
|
|
a NAM block meaning that it cannot support wildcards...
|
|
(sorry! - could be easily fixed though!)
|
|
*/
|
|
|
|
#ifdef VMS
|
|
#ifdef __DECC
|
|
#pragma module MODULE_NAME MODULE_IDENT
|
|
#else
|
|
#ifdef vaxc
|
|
#module MODULE_NAME MODULE_IDENT
|
|
#endif /* vaxc */
|
|
#endif /* __DECC */
|
|
#endif /* VMS */
|
|
|
|
#define DEBUGx on
|
|
#define VMSIOx on
|
|
|
|
#define _BSD_SOURCE
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include "version.h"
|
|
#include "compat.h"
|
|
#ifdef DISKIMAGE
|
|
#include "diskio.h"
|
|
#endif
|
|
#include "sysmsg.h"
|
|
#include "phyio.h"
|
|
|
|
#ifdef _WIN32
|
|
#include <Windows.h>
|
|
#endif
|
|
|
|
#ifdef VMSIO
|
|
#include <ssdef.h>
|
|
#include <descrip.h>
|
|
#include <starlet.h>
|
|
#include <rms.h>
|
|
#include <fiddef.h>
|
|
|
|
#define sys_parse sys$parse
|
|
#define sys_search sys$search
|
|
#define sys_open sys$open
|
|
#define sys_close sys$close
|
|
#define sys_connect sys$connect
|
|
#define sys_disconnect sys$disconnect
|
|
#define sys_get sys$get
|
|
#define sys_put sys$put
|
|
#define sys_create sys$create
|
|
#define sys_erase sys$erase
|
|
#define sys_extend sys$extend
|
|
#define sys_asctim sys$asctim
|
|
#define sys_setddir sys$setddir
|
|
#define dsc_descriptor dsc$descriptor
|
|
#define dsc_w_length dsc$w_length
|
|
#define dsc_a_pointer dsc$a_pointer
|
|
|
|
#else
|
|
#include "ssdef.h"
|
|
#include "descrip.h"
|
|
#include "access.h"
|
|
#include "rms.h"
|
|
#endif
|
|
|
|
#define PRINT_ATTR (FAB$M_CR | FAB$M_PRN | FAB$M_FTN)
|
|
|
|
#ifdef USE_READLINE
|
|
#define _XOPEN_SOURCE
|
|
#include <wordexp.h>
|
|
#include <readline/readline.h>
|
|
#include <readline/history.h>
|
|
#endif
|
|
|
|
/* keycomp: routine to compare parameter to a keyword - case insensitive! */
|
|
|
|
int keycomp(const char *param, const char *keywrd)
|
|
{
|
|
while (*param != '\0') {
|
|
if (tolower(*param++) != *keywrd++) return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* set, clear: bits specified are cleared, then set in value
|
|
* helpstr: if null, switch not listed in help. If starts with -,
|
|
* listed as negatable in help
|
|
*/
|
|
#define NV NOVAL, NULL
|
|
#define KV(list) KEYVAL, list
|
|
struct qual {
|
|
const char *name;
|
|
int set;
|
|
int clear;
|
|
enum qualtype { NOVAL, KEYVAL } qtype;
|
|
void *arg;
|
|
const char *helpstr;
|
|
};
|
|
|
|
struct param;
|
|
struct CMDSET;
|
|
|
|
typedef const char *(hlpfunc_t)( struct CMDSET *cmd, struct param *p, int argc, char **argv );
|
|
|
|
struct param {
|
|
const char *name;
|
|
enum parflags { REQ, OPT, CND } flags;
|
|
enum partype { VMSFS, LCLFS, LIST, KEYWD, STRING, CMDNAM, NONE } ptype;
|
|
#define PA(arg) NULL, (arg)
|
|
#define NOPA PA(NULL)
|
|
hlpfunc_t *hlpfnc;
|
|
void *arg;
|
|
const char *helpstr;
|
|
};
|
|
|
|
/* Command table entry */
|
|
|
|
struct CMDSET {
|
|
char *name;
|
|
unsigned (*proc) (int argc,char *argv[],int qualc,char *qualv[]);
|
|
unsigned uniq;
|
|
struct qual *validquals;
|
|
struct param *params;
|
|
char *helpstr;
|
|
};
|
|
|
|
void qualhelp( int par, struct qual *qtable );
|
|
|
|
/* Qualifier style = vms = /qualifier; else -option
|
|
*/
|
|
|
|
static int vms_qual = 1;
|
|
static int verify_cmd = 1;
|
|
|
|
/* checkquals: Given valid qualifier definitions, process qualifer
|
|
* list from a command left to right. Also handles parameters and
|
|
* qualifier values (/Qual=value).
|
|
*/
|
|
|
|
int checkquals(int result, struct qual qualset[],int qualc,char *qualv[])
|
|
{
|
|
int i;
|
|
const char *type;
|
|
|
|
type = (qualc < 0)? "parameter": "qualifier";
|
|
qualc = abs( qualc );
|
|
|
|
for( i = 0; i < qualc; i++ ) {
|
|
char *qv;
|
|
struct qual *qs, *qp = NULL;
|
|
|
|
qv = strchr( qualv[i], '=' );
|
|
if( qv == NULL )
|
|
qv = strchr( qualv[i], ':' );
|
|
if( qv != NULL )
|
|
*qv++ = '\0';
|
|
for( qs = qualset; qs->name != NULL; qs++) {
|
|
if (keycomp(qualv[i],qs->name)) {
|
|
if( qp != NULL ) {
|
|
printf ( "%%ODS2-W-AMBQUAL, %c%s '%s' is ambiguous\n",
|
|
type[0], type+1, qualv[i] );
|
|
return -1;
|
|
}
|
|
qp = qs;
|
|
}
|
|
}
|
|
if (qp == NULL) {
|
|
printf("%%ODS2-W-ILLQUAL, Unknown %s '%s'\n", type, qualv[i]);
|
|
return -1;
|
|
}
|
|
result = (result & ~qp->clear) | qp->set;
|
|
if( qv != NULL ) {
|
|
char *nvp;
|
|
|
|
if( qp->qtype == NOVAL ) {
|
|
printf( "%%ODS2-W-NOQVAL, %c%s '%s' does not accept a value\n",
|
|
toupper( *type ), type+1, qualv[i] );
|
|
return -1;
|
|
}
|
|
if( *qv == '(' ) {
|
|
qv++;
|
|
nvp = strchr( qv, ')' );
|
|
if( nvp == NULL ) {
|
|
printf( "%%ODS2-W-NQP, %c%s %s is missing ')'\n",
|
|
toupper( *type ), type+1, qualv[i] );
|
|
return -1;
|
|
}
|
|
*nvp = '\0';
|
|
}
|
|
do {
|
|
while( *qv == ' ' ) qv++;
|
|
nvp = strchr( qv, ',' );
|
|
if( nvp != NULL )
|
|
*nvp++ = '\0';
|
|
switch( qp->qtype ) {
|
|
case KEYVAL:
|
|
result = checkquals( result, (struct qual *)qp->arg, -1, &qv );
|
|
if( result == -1 )
|
|
return result;
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
qv = nvp;
|
|
} while( qv != NULL );
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
int prvmstime(VMSTIME vtime, const char *sfx) {
|
|
int sts = 0;
|
|
char tim[24];
|
|
static const VMSTIME nil;
|
|
struct dsc_descriptor timdsc;
|
|
|
|
if( memcmp( vtime, nil, sizeof(nil) ) ) {
|
|
timdsc.dsc_w_length = 23;
|
|
timdsc.dsc_a_pointer = tim;
|
|
sts = sys_asctim(0,&timdsc,vtime,0);
|
|
if ((sts & 1) == 0) printf("Asctim error: %s\n",getmsg(sts));
|
|
tim[23] = '\0';
|
|
printf(" %s",tim);
|
|
} else {
|
|
printf( " %-23s", " <Not recorded>" );
|
|
sts = 1;
|
|
}
|
|
if (sfx != NULL)
|
|
printf( "%s", sfx );
|
|
return sts;
|
|
}
|
|
|
|
void pwrap( int *pos, const char *fmt, ... ) {
|
|
char pbuf[200], *p, *q;
|
|
va_list ap;
|
|
va_start(ap, fmt );
|
|
vsnprintf( pbuf, sizeof(pbuf), fmt, ap );
|
|
va_end(ap);
|
|
p = pbuf;
|
|
while( *p ) {
|
|
int len;
|
|
int eol = 0;
|
|
|
|
q = strchr( p, '\n' );
|
|
if( q != NULL ) {
|
|
*q++ = '\0';
|
|
eol = 1;
|
|
len = strlen(p);
|
|
} else {
|
|
len = strlen(p);
|
|
q = p + len;
|
|
}
|
|
if( *pos + len > 80 ) {
|
|
static const char wrap[] = " ";
|
|
printf( "\n%s", wrap );
|
|
*pos = sizeof(wrap) -1;
|
|
if( p[0] == ',' && p[1] == ' ' )
|
|
p += 2;
|
|
}
|
|
*pos += strlen(p);
|
|
printf( "%s%s", p, eol? "\n":"" );
|
|
if( eol ) *pos = 0;
|
|
p = q;
|
|
}
|
|
}
|
|
|
|
/* dir: a directory routine */
|
|
|
|
#define dir_extra (dir_date | dir_fileid | dir_owner | dir_prot | dir_size)
|
|
#define dir_date (1 << 0)
|
|
#define dir_fileid (1 << 1)
|
|
#define dir_owner (1 << 2)
|
|
#define dir_prot (1 << 3)
|
|
#define dir_size (1 << 4)
|
|
|
|
#define dir_grand (1 << 5)
|
|
#define dir_heading (1 << 6)
|
|
#define dir_names (1 << 7)
|
|
#define dir_trailing (1 << 8)
|
|
#define dir_full (1 << 9)
|
|
#define dir_total (1 << 10)
|
|
|
|
#define dir_backup (1 << 11)
|
|
#define dir_created (1 << 12)
|
|
#define dir_expired (1 << 13)
|
|
#define dir_modified (1 << 14)
|
|
#define dir_dates (dir_backup | dir_created | dir_expired | dir_modified)
|
|
|
|
#define dir_allocated (1 << 15)
|
|
#define dir_used (1 << 16)
|
|
#define dir_sizes (dir_allocated | dir_used)
|
|
|
|
#define dir_default (dir_heading|dir_names|dir_trailing)
|
|
|
|
struct qual datekwds[] = { {"created", dir_created, 0, NV, "Date file created (default)"},
|
|
{"modified", dir_modified, 0, NV, "Date file modified"},
|
|
{"expired", dir_expired, 0, NV, "Date file expired"},
|
|
{"backup", dir_backup, 0, NV, "Date of last backup"},
|
|
{NULL, 0, 0, NV, NULL}
|
|
};
|
|
struct qual sizekwds[] = { {"both", dir_used|dir_allocated, 0, NV, "Both used and allocated" },
|
|
{"allocation", dir_allocated, 0, NV, "Blocks allocated to file" },
|
|
{"used", dir_used, 0, NV, "Blocks used in file" },
|
|
{NULL, 0, 0, NV, NULL}
|
|
};
|
|
struct qual dirquals[] = { {"brief", dir_default, ~dir_default, NV, "Brief display - names with header/trailer (default)"},
|
|
{"date", dir_date, dir_dates, KV(datekwds), "-Include file date(s)", },
|
|
{"nodate", 0, dir_date, NV, NULL, },
|
|
{"file_id", dir_fileid, 0, NV, "-Include file ID", },
|
|
{"nofile_id", 0, dir_fileid, NV, NULL },
|
|
{"full", dir_full|dir_heading|dir_trailing,
|
|
~dir_full, NV, "Include full details", },
|
|
{"grand_total", dir_grand, ~dir_grand & ~(dir_size|dir_sizes),
|
|
NV, "-Include only grand total",},
|
|
{"nogrand_total", 0, dir_grand, NV, NULL},
|
|
{"heading", dir_heading, 0, NV, "-Include heading", },
|
|
{"noheading", 0, dir_heading, NV, NULL},
|
|
{"owner", dir_owner, 0, NV, "-Include file owner", },
|
|
{"noowner", 0, dir_owner, NV, NULL, },
|
|
{"protection", dir_prot, 0, NV, "-Include file protection", },
|
|
{"noprotection", 0, dir_prot, NV, NULL, },
|
|
{"size", dir_size, dir_sizes, KV(sizekwds), "-Include file size (blocks)", },
|
|
{"nosize", 0, dir_size|dir_sizes,
|
|
NV, NULL, },
|
|
{"total", dir_total|dir_heading,
|
|
~dir_total & ~(dir_size|dir_sizes),
|
|
NV, "Include only directory name and summary",},
|
|
{"trailing", dir_trailing, 0, NV, "-Include trailing summary line",},
|
|
{"notrailing", 0, dir_trailing, NV, NULL},
|
|
{NULL, 0, 0, NV, NULL} };
|
|
int dir_defopt = dir_default;
|
|
|
|
struct param dirpars[] = { {"filespec", OPT, VMSFS, NOPA, "for files to select. Wildcards are allowed."},
|
|
{ NULL, 0, 0, NOPA, NULL }
|
|
};
|
|
|
|
void dirtotal( int options, int size, int alloc ) {
|
|
if ( !(options & dir_size) )
|
|
return;
|
|
fputs( ", ", stdout );
|
|
|
|
if ( options & dir_used )
|
|
printf( "%d", size );
|
|
if ( options & dir_allocated ) {
|
|
if (options & dir_used) printf( "/" );
|
|
printf( "%d", alloc );
|
|
}
|
|
if ((options & dir_dates) == dir_dates)
|
|
printf( " block%s",(size ==1 && alloc == 1 ? "" : "s"));
|
|
else
|
|
printf( " block%s",(((options & dir_used) && size == 1) ||
|
|
((options & dir_allocated) && alloc == 1))? "" : "s");
|
|
return;
|
|
}
|
|
|
|
unsigned dir(int argc,char *argv[],int qualc,char *qualv[])
|
|
{
|
|
char res[NAM$C_MAXRSS + 1],rsa[NAM$C_MAXRSS + 1];
|
|
int sts,options;
|
|
int filecount = 0;
|
|
struct NAM nam = cc$rms_nam;
|
|
struct FAB fab = cc$rms_fab;
|
|
struct XABDAT dat = cc$rms_xabdat;
|
|
struct XABFHC fhc = cc$rms_xabfhc;
|
|
struct XABPRO pro = cc$rms_xabpro;
|
|
struct XABITM itm = cc$rms_xabitm;
|
|
int nobackup = 0, contigb = 0, contig = 0, directory = 0;
|
|
struct item_list xitems[] = {
|
|
{ XAB$_UCHAR_NOBACKUP, sizeof(int), NULL, 0 },
|
|
{ XAB$_UCHAR_CONTIG, sizeof(int), NULL, 0 },
|
|
{ XAB$_UCHAR_CONTIGB, sizeof(int), NULL, 0 },
|
|
{ XAB$_UCHAR_DIRECTORY, sizeof(int), NULL, 0 },
|
|
{ 0, 0, NULL, 0 }
|
|
};
|
|
UNUSED(argc);
|
|
|
|
nam.nam$l_esa = res;
|
|
nam.nam$b_ess = NAM$C_MAXRSS;
|
|
|
|
fab.fab$l_nam = &nam;
|
|
fab.fab$l_xab = &dat;
|
|
|
|
dat.xab$l_nxt = &fhc;
|
|
|
|
fhc.xab$l_nxt = &pro;
|
|
|
|
pro.xab$l_nxt = &itm;
|
|
xitems[0].buffer = &nobackup;
|
|
xitems[1].buffer = &contig;
|
|
xitems[2].buffer = &contigb;
|
|
xitems[3].buffer = &directory;
|
|
|
|
itm.xab$b_mode = XAB$K_SENSEMODE;
|
|
itm.xab$l_itemlist = xitems;
|
|
|
|
fab.fab$l_fna = argv[1];
|
|
fab.fab$b_fns = strlen(fab.fab$l_fna);
|
|
fab.fab$l_dna = "*.*;*";
|
|
fab.fab$b_dns = strlen(fab.fab$l_dna);
|
|
|
|
options = checkquals(dir_defopt,dirquals,qualc,qualv);
|
|
if( options == -1 )
|
|
return SS$_BADPARAM;
|
|
|
|
if (options & dir_full)
|
|
options |= dir_extra;
|
|
if (options & (dir_total | dir_grand)) {
|
|
options |= dir_trailing;
|
|
if (options & (dir_extra & ~dir_size))
|
|
options |= dir_names;
|
|
} else {
|
|
if (options & dir_extra)
|
|
options |= dir_names;
|
|
}
|
|
if( (options & dir_size) && !(options & dir_sizes) )
|
|
options |= dir_used;
|
|
if( (options & dir_date) && !(options & dir_dates) )
|
|
options |= dir_created;
|
|
|
|
sts = sys_parse(&fab);
|
|
if (sts & 1) {
|
|
char dir[NAM$C_MAXRSS + 1];
|
|
int namelen;
|
|
int dirlen = 0;
|
|
int dirfiles = 0, dircount = 0;
|
|
int dirblocks = 0, diralloc = 0, totblocks = 0, totalloc = 0;
|
|
int printcol = 0;
|
|
#ifdef DEBUG
|
|
res[nam.nam$b_esl] = '\0';
|
|
printf("Parse: %s\n",res);
|
|
#endif
|
|
nam.nam$l_rsa = rsa;
|
|
nam.nam$b_rss = NAM$C_MAXRSS;
|
|
fab.fab$l_fop = FAB$M_NAM;
|
|
while ((sts = sys_search(&fab)) & 1) {
|
|
|
|
if (dirlen != nam.nam$b_dev + nam.nam$b_dir ||
|
|
memcmp(rsa, dir, nam.nam$b_dev + nam.nam$b_dir) != 0) {
|
|
if (dirfiles > 0 && (options & dir_trailing)) {
|
|
if (printcol > 0) printf("\n");
|
|
printf("\nTotal of %d file%s",dirfiles,(dirfiles == 1 ? "" : "s"));
|
|
dirtotal( options, dirblocks, diralloc );
|
|
fputs(".\n",stdout);
|
|
}
|
|
dirlen = nam.nam$b_dev + nam.nam$b_dir;
|
|
memcpy(dir,rsa,dirlen);
|
|
dir[dirlen] = '\0';
|
|
if( options & dir_heading) printf("\nDirectory %s\n\n",dir);
|
|
filecount += dirfiles;
|
|
totblocks += dirblocks;
|
|
totalloc += diralloc;
|
|
dircount++;
|
|
dirfiles = 0;
|
|
dirblocks = 0;
|
|
diralloc = 0;
|
|
printcol = 0;
|
|
}
|
|
rsa[nam.nam$b_rsl] = '\0';
|
|
namelen = nam.nam$b_name + nam.nam$b_type + nam.nam$b_ver;
|
|
if ((options & (dir_heading|dir_extra|dir_full)) == dir_heading) {
|
|
if( options & dir_names ) {
|
|
if (printcol > 0) {
|
|
int newcol = (printcol + 20) / 20 * 20;
|
|
if (newcol + namelen >= 80) {
|
|
fputs("\n",stdout);
|
|
printcol = 0;
|
|
} else {
|
|
printf("%*s",newcol - printcol," ");
|
|
printcol = newcol;
|
|
}
|
|
}
|
|
fputs(rsa + dirlen,stdout);
|
|
printcol += namelen;
|
|
}
|
|
} else {
|
|
if (options & dir_names) {
|
|
if ( (options & dir_heading) == 0 ) printf( "%s",dir );
|
|
if (namelen > 18) {
|
|
printf("%s",rsa + dirlen);
|
|
if( options & dir_extra )
|
|
printf( "\n " );
|
|
} else {
|
|
printf("%-19s",rsa + dirlen);
|
|
}
|
|
}
|
|
sts = sys_open(&fab);
|
|
if ((sts & 1) == 0) {
|
|
printf("Open error: %s\n", getmsg(sts));
|
|
} else {
|
|
sts = sys_close(&fab);
|
|
if (options & dir_fileid) {
|
|
char fileid[30]; /* 24 bits, 16 bits. 8 bits = 8 + 5 + 3 digits = 16 + (,,)\0 => 21 */
|
|
if( options & dir_full) printf( " File ID:" );
|
|
snprintf(fileid,sizeof(fileid),"(%d,%d,%d)",
|
|
(nam.nam$b_fid_nmx << 16) | nam.nam$w_fid_num,
|
|
nam.nam$w_fid_seq,nam.nam$b_fid_rvn);
|
|
printf(" %-22s",fileid);
|
|
}
|
|
diralloc += fab.fab$l_alq;
|
|
if (options & dir_used) {
|
|
unsigned filesize = fhc.xab$l_ebk;
|
|
if (fhc.xab$w_ffb == 0) filesize--;
|
|
dirblocks += filesize;
|
|
|
|
if (options & dir_names) { /* Don't print w/o names (e.g. totals) */
|
|
if( options & dir_full) printf( "\nSize: " );
|
|
printf("%9d",filesize);
|
|
if( options & (dir_allocated|dir_full)) printf( "/%-9d ",fab.fab$l_alq );
|
|
}
|
|
} else {
|
|
if ( (options & (dir_allocated|dir_names)) == (dir_allocated|dir_names)) {
|
|
printf( "%9d", fab.fab$l_alq );
|
|
}
|
|
}
|
|
#define pprot(val,pos,del) {\
|
|
unsigned int v = ~(((val) >> (pos)) & xab$m_prot); \
|
|
if( v & xab$m_noread ) printf( "R" ); \
|
|
if( v & xab$m_nowrite ) printf( "W" ); \
|
|
if( v & xab$m_noexe ) printf( "E" ); \
|
|
if( v & xab$m_nodel ) printf( "D" ); \
|
|
printf( del ); \
|
|
}
|
|
if (options & dir_full) {
|
|
int pos = 0;
|
|
|
|
printf( "Owner: [%o,%o]\n", ((pro.xab$l_uic>>16)&0xFFFF), pro.xab$l_uic&0xFFFF);
|
|
printf( "Created: " ); prvmstime( dat.xab$q_cdt, "\n" );
|
|
printf( "Revised: " ); prvmstime( dat.xab$q_rdt, " (" ); printf( "%u)\n", dat.xab$w_rvn );
|
|
printf( "Expires: " ); prvmstime( dat.xab$q_edt, "\n" );
|
|
printf( "Backup: " ); prvmstime( dat.xab$q_bdt, "\n" );
|
|
pwrap( &pos, "File organization: " );
|
|
switch( fab.fab$b_org ) {
|
|
case FAB$C_SEQ:
|
|
pwrap( &pos, "Sequential" ); break;
|
|
case FAB$C_REL:
|
|
pwrap( &pos, "Relative" /*, Maximum record number %u", fab.fab$l_mrn*/ ); break;
|
|
case FAB$C_IDX:
|
|
pwrap( &pos, "Indexed" ); break; /*, Prolog: 3, Using 4 keys\nIn 3 areas */
|
|
default:
|
|
pwrap( &pos, "Unknown (%u)", fab.fab$b_org ); break;
|
|
}
|
|
/* File attributes: Allocation: 372, Extend: 3, Maximum bucket size: 3, Global buffer count: 0, No version limit
|
|
Contiguous best try */
|
|
pwrap( &pos, "\nFile attributes: " );
|
|
pwrap( &pos, "Allocation: %u", fab.fab$l_alq );
|
|
pwrap( &pos, ", Extend: %u", fab.fab$w_deq );
|
|
/* Missing: , Maximum bucket size: n*/
|
|
pwrap( &pos, ", Global buffer count: %u", fab.fab$w_gbc );
|
|
if( fhc.xab$w_verlimit == 0 || fhc.xab$w_verlimit == 32767 )
|
|
pwrap( &pos, ", No %sversion limit", directory? "default ": "" );
|
|
else
|
|
pwrap( &pos, ", %sersion limit: %u", (directory? "Default v": "V"), fhc.xab$w_verlimit );
|
|
if( contig )
|
|
pwrap( &pos, ", Contiguous" );
|
|
if( contigb )
|
|
pwrap( &pos, ", Contiguous best try" );
|
|
if( nobackup )
|
|
pwrap( &pos, ", Backups disabled" );
|
|
if( directory )
|
|
pwrap( &pos, ", Directory file" );
|
|
pwrap( &pos, "\n" );
|
|
|
|
pwrap( &pos, "Record format: " );
|
|
switch( fab.fab$b_rfm ) {
|
|
default:
|
|
case FAB$C_UDF:
|
|
pwrap( &pos, "Undefined" ); break;
|
|
case FAB$C_FIX:
|
|
pwrap( &pos, "Fixed length %u byte records", fab.fab$w_mrs ); break;
|
|
case FAB$C_VAR:
|
|
pwrap( &pos, "Variable length, maximum %u bytes", fab.fab$w_mrs ); break;
|
|
case FAB$C_VFC:
|
|
pwrap( &pos, "Variable length, fixed carriage control %u, maximum %u bytes", (fab.fab$b_fsz? fab.fab$b_fsz: 2), fab.fab$w_mrs ); break;
|
|
case FAB$C_STM:
|
|
pwrap( &pos, "Stream" ); break;
|
|
case FAB$C_STMLF:
|
|
pwrap( &pos, "Stream-LF" ); break;
|
|
case FAB$C_STMCR:
|
|
pwrap( &pos, "Stream-CR" ); break;
|
|
}
|
|
pwrap( &pos, "\n" );
|
|
|
|
pwrap( &pos, "Record attributes: " );
|
|
if( fab.fab$b_rat == 0 )
|
|
pwrap( &pos, "None" );
|
|
else {
|
|
const char *more = "";
|
|
if( fab.fab$b_rat & FAB$M_FTN ) {
|
|
pwrap( &pos, "%sFortran carriage control", more );
|
|
more = ", ";
|
|
}
|
|
if( fab.fab$b_rat & FAB$M_CR ) {
|
|
pwrap( &pos, "%sCarriage return carriage control", more );
|
|
more = ", ";
|
|
}
|
|
if( fab.fab$b_rat & FAB$M_PRN ) {
|
|
pwrap( &pos, "%sPrinter control", more );
|
|
more = ", ";
|
|
}
|
|
if( fab.fab$b_rat & FAB$M_BLK ) {
|
|
pwrap( &pos, "%sNon-spanned", more );
|
|
}
|
|
}
|
|
printf( "\n" );
|
|
/*
|
|
RMS attributes: None
|
|
Journaling enabled: None
|
|
*/
|
|
printf( "File protection: System:" );
|
|
pprot(pro.xab$w_pro,xab$v_system,", Owner:")
|
|
pprot(pro.xab$w_pro,xab$v_owner,", Group:")
|
|
pprot(pro.xab$w_pro,xab$v_group,", World:")
|
|
pprot(pro.xab$w_pro,xab$v_world,"\n");
|
|
} else { /* !full */
|
|
if (options & dir_date) {
|
|
if( options & dir_created )
|
|
sts = prvmstime( dat.xab$q_cdt, NULL );
|
|
if( options & dir_modified )
|
|
sts = prvmstime( dat.xab$q_rdt, NULL );
|
|
if( options & dir_expired )
|
|
sts = prvmstime( dat.xab$q_edt, NULL );
|
|
if( options & dir_backup )
|
|
sts = prvmstime( dat.xab$q_bdt, NULL );
|
|
}
|
|
if (options & dir_owner) {
|
|
printf(" [%o,%o]", ((pro.xab$l_uic>>16)&0xFFFF), pro.xab$l_uic&0xFFFF);
|
|
}
|
|
if (options & dir_prot) {
|
|
printf( " (" );
|
|
pprot(pro.xab$w_pro,xab$v_system,",")
|
|
pprot(pro.xab$w_pro,xab$v_owner,",")
|
|
pprot(pro.xab$w_pro,xab$v_group,",")
|
|
pprot(pro.xab$w_pro,xab$v_world,")")
|
|
}
|
|
}
|
|
#undef pprot
|
|
if (options & dir_names)
|
|
printf("\n");
|
|
}
|
|
}
|
|
dirfiles++;
|
|
}
|
|
if (sts == RMS$_NMF) sts = 1;
|
|
if (printcol > 0) printf("\n");
|
|
if (options & dir_trailing) {
|
|
printf("\nTotal of %d file%s",dirfiles,(dirfiles == 1 ? "" : "s"));
|
|
dirtotal( options, dirblocks, diralloc );
|
|
fputs(".\n",stdout);
|
|
}
|
|
filecount += dirfiles;
|
|
totblocks += dirblocks;
|
|
totalloc += diralloc;
|
|
if (options & dir_grand) {
|
|
printf("\nGrand total of %d director%s, %d file%s",
|
|
dircount,(dircount == 1 ? "y" : "ies"),
|
|
filecount,(filecount == 1 ? "" : "s"));
|
|
dirtotal( options, totblocks, totalloc );
|
|
fputs(".\n",stdout);
|
|
}
|
|
}
|
|
if (sts & 1) {
|
|
if (filecount < 1) printf("%%DIRECT-W-NOFILES, no files found\n");
|
|
} else {
|
|
printf("%%DIR-E-ERROR Status: %s\n",getmsg(sts));
|
|
}
|
|
return sts;
|
|
}
|
|
|
|
|
|
/* copy: a file copy routine */
|
|
|
|
#define MAXREC 32767
|
|
struct qual copyquals[] = { {"ascii", 0, 1, NV, "Copy file in ascii mode (default)"},
|
|
{"binary", 1, 0, NV, "Copy file in binary mode", },
|
|
{NULL, 0, 0, NV, NULL}
|
|
};
|
|
|
|
struct param copypars[] = { {"from_filespec", REQ, VMSFS, NOPA, "for source file. Wildcards are allowed."},
|
|
{"to_filespec", REQ, LCLFS, NOPA, "for destination file. Wildcards are replaced from source file name."},
|
|
{ NULL, 0, 0, NOPA, NULL }
|
|
};
|
|
|
|
unsigned copy(int argc,char *argv[],int qualc,char *qualv[])
|
|
{
|
|
int sts,options;
|
|
struct NAM nam = cc$rms_nam;
|
|
struct FAB fab = cc$rms_fab;
|
|
char res[NAM$C_MAXRSS + 1],rsa[NAM$C_MAXRSS + 1];
|
|
int filecount = 0;
|
|
|
|
UNUSED(argc);
|
|
|
|
nam.nam$l_esa = res;
|
|
nam.nam$b_ess = NAM$C_MAXRSS;
|
|
fab.fab$l_nam = &nam;
|
|
fab.fab$l_fna = argv[1];
|
|
fab.fab$b_fns = strlen(fab.fab$l_fna);
|
|
options = checkquals(0,copyquals,qualc,qualv);
|
|
if( options == -1 )
|
|
return SS$_BADPARAM;
|
|
sts = sys_parse(&fab);
|
|
if (sts & 1) {
|
|
nam.nam$l_rsa = rsa;
|
|
nam.nam$b_rss = NAM$C_MAXRSS;
|
|
fab.fab$l_fop = FAB$M_NAM;
|
|
while ((sts = sys_search(&fab)) & 1) {
|
|
sts = sys_open(&fab);
|
|
if ((sts & 1) == 0) {
|
|
printf("%%COPY-F-OPENFAIL, Open error: %s\n",getmsg(sts));
|
|
perror("-COPY-F-ERR ");
|
|
} else {
|
|
struct RAB rab = cc$rms_rab;
|
|
rab.rab$l_fab = &fab;
|
|
if ((sts = sys_connect(&rab)) & 1) {
|
|
FILE *tof;
|
|
char name[NAM$C_MAXRSS + 1];
|
|
unsigned records = 0;
|
|
{
|
|
char *out = name,*inp = argv[2];
|
|
int dot = 0;
|
|
while (*inp != '\0') {
|
|
if (*inp == '*') {
|
|
inp++;
|
|
if (dot) {
|
|
memcpy(out,nam.nam$l_type + 1,nam.nam$b_type - 1);
|
|
out += nam.nam$b_type - 1;
|
|
} else {
|
|
unsigned length = nam.nam$b_name;
|
|
if (*inp == '\0') length += nam.nam$b_type;
|
|
memcpy(out,nam.nam$l_name,length);
|
|
out += length;
|
|
}
|
|
} else {
|
|
if (*inp == '.') {
|
|
dot = 1;
|
|
} else {
|
|
if (strchr(":]\\/",*inp)) dot = 0;
|
|
}
|
|
*out++ = *inp++;
|
|
}
|
|
}
|
|
*out++ = '\0';
|
|
}
|
|
#ifndef _WIN32
|
|
tof = openf(name,"w");
|
|
#else
|
|
if ((options & 1) == 0 && fab.fab$b_rat & PRINT_ATTR) {
|
|
tof = openf(name,"w");
|
|
} else {
|
|
tof = openf(name,"wb");
|
|
}
|
|
#endif
|
|
if (tof == NULL) {
|
|
printf("%%COPY-F-OPENOUT, Could not open %s\n",name);
|
|
perror("-COPY-F-ERR ");
|
|
} else {
|
|
char rec[MAXREC + 2];
|
|
filecount++;
|
|
rab.rab$l_ubf = rec;
|
|
rab.rab$w_usz = MAXREC;
|
|
while ((sts = sys_get(&rab)) & 1) {
|
|
unsigned rsz = rab.rab$w_rsz;
|
|
if ((options & 1) == 0 &&
|
|
fab.fab$b_rat & PRINT_ATTR) rec[rsz++] = '\n';
|
|
if (fwrite(rec,rsz,1,tof) == 1) {
|
|
records++;
|
|
} else {
|
|
printf("%%COPY-F- fwrite error!!\n");
|
|
perror("-COPY-F-ERR ");
|
|
break;
|
|
}
|
|
}
|
|
if (fclose(tof)) {
|
|
printf("%%COPY-F- fclose error!!\n");
|
|
perror("-COPY-F-ERR ");
|
|
}
|
|
}
|
|
sys_disconnect(&rab);
|
|
rsa[nam.nam$b_rsl] = '\0';
|
|
if (sts == RMS$_EOF) {
|
|
printf("%%COPY-S-COPIED, %s copied to %s (%d record%s)\n",
|
|
rsa,name,records,(records == 1 ? "" : "s"));
|
|
} else {
|
|
printf("%%COPY-F-ERROR Status: %s for %s\n",getmsg(sts),rsa);
|
|
sts = 1;
|
|
}
|
|
}
|
|
sys_close(&fab);
|
|
}
|
|
}
|
|
if (sts == RMS$_NMF) sts = 1;
|
|
}
|
|
if (sts & 1) {
|
|
if (filecount > 0) printf("%%COPY-S-NEWFILES, %d file%s created\n",
|
|
filecount,(filecount == 1 ? "" : "s"));
|
|
} else {
|
|
printf("%%COPY-F-ERROR Status: %s\n",getmsg(sts));
|
|
}
|
|
return sts;
|
|
}
|
|
|
|
/* import: a file copy routine */
|
|
|
|
struct param importpars[] = { {"from_filespec", REQ, LCLFS, NOPA, "for source file."},
|
|
{"to_filespec", REQ, VMSFS, NOPA, "for destination file."},
|
|
{ NULL, 0, 0, NOPA, NULL }
|
|
};
|
|
|
|
unsigned import(int argc,char *argv[],int qualc,char *qualv[])
|
|
{
|
|
int sts = 0;
|
|
FILE *fromf;
|
|
|
|
UNUSED(argc);
|
|
UNUSED(qualc);
|
|
UNUSED(qualv);
|
|
|
|
fromf = openf(argv[1],"r");
|
|
if (fromf != NULL) {
|
|
struct FAB fab = cc$rms_fab;
|
|
fab.fab$l_fna = argv[2];
|
|
fab.fab$b_fns = strlen(fab.fab$l_fna);
|
|
if ((sts = sys_create(&fab)) & 1) {
|
|
struct RAB rab = cc$rms_rab;
|
|
rab.rab$l_fab = &fab;
|
|
if ((sts = sys_connect(&rab)) & 1) {
|
|
char rec[MAXREC + 2];
|
|
rab.rab$l_rbf = rec;
|
|
rab.rab$w_usz = MAXREC;
|
|
while (fgets(rec,sizeof(rec),fromf) != NULL) {
|
|
rab.rab$w_rsz = strlen(rec);
|
|
sts = sys_put(&rab);
|
|
if ((sts & 1) == 0) break;
|
|
}
|
|
sys_disconnect(&rab);
|
|
}
|
|
sys_close(&fab);
|
|
}
|
|
fclose(fromf);
|
|
if (!(sts & 1)) {
|
|
printf("%%IMPORT-F-ERROR Status: %s\n",getmsg(sts));
|
|
}
|
|
} else {
|
|
printf("Can't open %s\n",argv[1]);
|
|
}
|
|
return sts;
|
|
}
|
|
|
|
|
|
/* diff: a simple file difference routine */
|
|
|
|
struct param diffpars[] = { {"ods-2_filespec", REQ, VMSFS, NOPA, "for file on ODS-2 volume."},
|
|
{"local__filespec", REQ, LCLFS, NOPA, "for file on local filesystem."},
|
|
{ NULL, 0, 0, NOPA, NULL }
|
|
};
|
|
unsigned diff(int argc,char *argv[],int qualc,char *qualv[])
|
|
{
|
|
int sts;
|
|
struct FAB fab = cc$rms_fab;
|
|
FILE *tof;
|
|
int records = 0;
|
|
|
|
UNUSED(argc);
|
|
UNUSED(qualc);
|
|
UNUSED(qualv);
|
|
|
|
fab.fab$l_fna = argv[1];
|
|
fab.fab$b_fns = strlen(fab.fab$l_fna);
|
|
tof = openf(argv[2],"r");
|
|
if (tof == NULL) {
|
|
printf("Could not open file %s\n",argv[1]);
|
|
sts = 0;
|
|
} else {
|
|
if ((sts = sys_open(&fab)) & 1) {
|
|
struct RAB rab = cc$rms_rab;
|
|
rab.rab$l_fab = &fab;
|
|
if ((sts = sys_connect(&rab)) & 1) {
|
|
char rec[MAXREC + 2],cpy[MAXREC + 1];
|
|
rab.rab$l_ubf = rec;
|
|
rab.rab$w_usz = MAXREC;
|
|
while ((sts = sys_get(&rab)) & 1) {
|
|
rec[rab.rab$w_rsz++] = '\n';
|
|
rec[rab.rab$w_rsz] = '\0';
|
|
fgets(cpy,MAXREC,tof);
|
|
if (rab.rab$w_rsz != strlen( cpy ) || memcmp(rec,cpy,rab.rab$w_rsz) != 0) {
|
|
printf("%%DIFF-F-DIFFERENT Files are different!\n");
|
|
sts = 4;
|
|
break;
|
|
} else {
|
|
records++;
|
|
}
|
|
}
|
|
sys_disconnect(&rab);
|
|
}
|
|
sys_close(&fab);
|
|
}
|
|
fclose(tof);
|
|
if (sts == RMS$_EOF) sts = 1;
|
|
}
|
|
if (sts & 1) {
|
|
printf("%%DIFF-I-Compared %d records\n",records);
|
|
} else {
|
|
printf("%%DIFF-F-Error %s in difference\n",getmsg(sts));
|
|
}
|
|
return sts;
|
|
}
|
|
|
|
|
|
/* typ: a file TYPE routine */
|
|
|
|
struct param typepars[] = { {"filespec", REQ, VMSFS, NOPA, "for file on ODS-2 volume."},
|
|
{ NULL, 0, 0, NOPA, NULL }
|
|
};
|
|
unsigned typ(int argc,char *argv[],int qualc,char *qualv[])
|
|
{
|
|
int sts;
|
|
int records = 0;
|
|
struct FAB fab = cc$rms_fab;
|
|
|
|
UNUSED(argc);
|
|
UNUSED(qualc);
|
|
UNUSED(qualv);
|
|
|
|
fab.fab$l_fna = argv[1];
|
|
fab.fab$b_fns = strlen(fab.fab$l_fna);
|
|
if ((sts = sys_open(&fab)) & 1) {
|
|
struct RAB rab = cc$rms_rab;
|
|
rab.rab$l_fab = &fab;
|
|
if ((sts = sys_connect(&rab)) & 1) {
|
|
char rec[MAXREC + 2];
|
|
rab.rab$l_ubf = rec;
|
|
rab.rab$w_usz = MAXREC;
|
|
while ((sts = sys_get(&rab)) & 1) {
|
|
unsigned rsz = rab.rab$w_rsz;
|
|
if (fab.fab$b_rat & PRINT_ATTR) rec[rsz++] = '\n';
|
|
rec[rsz++] = '\0';
|
|
fputs(rec,stdout);
|
|
records++;
|
|
}
|
|
sys_disconnect(&rab);
|
|
}
|
|
sys_close(&fab);
|
|
if (sts == RMS$_EOF) sts = 1;
|
|
}
|
|
if ((sts & 1) == 0) {
|
|
printf("%%TYPE-F-ERROR Status: %s\n",getmsg(sts));
|
|
}
|
|
return sts;
|
|
}
|
|
|
|
|
|
|
|
/* search: a simple file search routine */
|
|
|
|
struct param searchpars[] = { {"filespec", REQ, VMSFS, NOPA, "for file to search. Wildcards are allowed."},
|
|
{"string", REQ, STRING, NOPA, "string to search for."},
|
|
{ NULL, 0, 0, NOPA, NULL }
|
|
};
|
|
|
|
unsigned search(int argc,char *argv[],int qualc,char *qualv[])
|
|
{
|
|
int sts = 0;
|
|
int filecount = 0;
|
|
int findcount = 0;
|
|
char res[NAM$C_MAXRSS + 1],rsa[NAM$C_MAXRSS + 1];
|
|
struct NAM nam = cc$rms_nam;
|
|
struct FAB fab = cc$rms_fab;
|
|
register char *searstr = argv[2];
|
|
register char firstch = tolower(*searstr++);
|
|
register char *searend = searstr + strlen(searstr);
|
|
|
|
UNUSED(argc);
|
|
UNUSED(qualc);
|
|
UNUSED(qualv);
|
|
|
|
{
|
|
char *str = searstr;
|
|
while (str < searend) {
|
|
*str = tolower(*str);
|
|
str++;
|
|
}
|
|
}
|
|
nam = cc$rms_nam;
|
|
fab = cc$rms_fab;
|
|
nam.nam$l_esa = res;
|
|
nam.nam$b_ess = NAM$C_MAXRSS;
|
|
fab.fab$l_nam = &nam;
|
|
fab.fab$l_fna = argv[1];
|
|
fab.fab$b_fns = strlen(fab.fab$l_fna);
|
|
fab.fab$l_dna = "";
|
|
fab.fab$b_dns = strlen(fab.fab$l_dna);
|
|
sts = sys_parse(&fab);
|
|
if (sts & 1) {
|
|
nam.nam$l_rsa = rsa;
|
|
nam.nam$b_rss = NAM$C_MAXRSS;
|
|
fab.fab$l_fop = FAB$M_NAM;
|
|
while ((sts = sys_search(&fab)) & 1) {
|
|
sts = sys_open(&fab);
|
|
if ((sts & 1) == 0) {
|
|
printf("%%SEARCH-F-OPENFAIL, Open error: %s\n",getmsg(sts));
|
|
} else {
|
|
struct RAB rab = cc$rms_rab;
|
|
rab.rab$l_fab = &fab;
|
|
if ((sts = sys_connect(&rab)) & 1) {
|
|
int printname = 1;
|
|
char rec[MAXREC + 2];
|
|
filecount++;
|
|
rab.rab$l_ubf = rec;
|
|
rab.rab$w_usz = MAXREC;
|
|
while ((sts = sys_get(&rab)) & 1) {
|
|
register char *strng = rec;
|
|
register char *strngend = strng + (rab.rab$w_rsz - (searend - searstr));
|
|
while (strng < strngend) {
|
|
register char ch = *strng++;
|
|
if (ch == firstch || (ch >= 'A' && ch <= 'Z' && ch + 32 == firstch)) {
|
|
register char *str = strng;
|
|
register char *cmp = searstr;
|
|
while (cmp < searend) {
|
|
register char ch2 = *str++;
|
|
ch = *cmp;
|
|
if (ch2 != ch && (ch2 < 'A' || ch2 > 'Z' || ch2 + 32 != ch)) break;
|
|
cmp++;
|
|
}
|
|
if (cmp >= searend) {
|
|
findcount++;
|
|
rec[rab.rab$w_rsz] = '\0';
|
|
if (printname) {
|
|
rsa[nam.nam$b_rsl] = '\0';
|
|
printf("\n******************************\n%s\n\n",rsa);
|
|
printname = 0;
|
|
}
|
|
fputs(rec,stdout);
|
|
if (fab.fab$b_rat & PRINT_ATTR) fputc('\n',stdout);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
sys_disconnect(&rab);
|
|
}
|
|
if (sts == SS$_NOTINSTALL) {
|
|
printf("%%SEARCH-W-NOIMPLEM, file operation not implemented\n");
|
|
sts = 1;
|
|
}
|
|
sys_close(&fab);
|
|
}
|
|
}
|
|
if (sts == RMS$_NMF || sts == RMS$_FNF) sts = 1;
|
|
}
|
|
if (sts & 1) {
|
|
if (filecount < 1) {
|
|
printf("%%SEARCH-W-NOFILES, no files found\n");
|
|
} else {
|
|
if (findcount < 1) printf("%%SEARCH-I-NOMATCHES, no strings matched\n");
|
|
}
|
|
} else {
|
|
printf("%%SEARCH-F-ERROR Status: %s\n",getmsg(sts));
|
|
}
|
|
return sts;
|
|
}
|
|
|
|
|
|
/* del: you don't want to know! */
|
|
|
|
struct qual delquals[] = { {"log", 1, 0, NV, "-List name of each file deleted. (Default)"},
|
|
{"nolog", 0, 1, NV, NULL },
|
|
{ NULL, 0, 0, NV, NULL }
|
|
};
|
|
struct param delpars[] = { {"filespec", REQ, VMSFS, NOPA, "for files to be deleted from ODS-2 volume. Wildcards are permitted.."},
|
|
{ NULL, 0, 0, NOPA, NULL }
|
|
};
|
|
|
|
unsigned del(int argc,char *argv[],int qualc,char *qualv[])
|
|
{
|
|
int sts = 0;
|
|
struct NAM nam = cc$rms_nam;
|
|
struct FAB fab = cc$rms_fab;
|
|
char res[NAM$C_MAXRSS + 1],rsa[NAM$C_MAXRSS + 1];
|
|
int filecount = 0;
|
|
int options;
|
|
|
|
UNUSED(argc);
|
|
|
|
options = checkquals(1,delquals,qualc,qualv);
|
|
if( options == -1 )
|
|
return SS$_BADPARAM;
|
|
|
|
nam.nam$l_esa = res;
|
|
nam.nam$b_ess = NAM$C_MAXRSS;
|
|
fab.fab$l_nam = &nam;
|
|
fab.fab$l_fna = argv[1];
|
|
fab.fab$b_fns = strlen(fab.fab$l_fna);
|
|
sts = sys_parse(&fab);
|
|
if (sts & 1) {
|
|
if (nam.nam$b_ver < 2) {
|
|
printf("%%DELETE-F-NOVER, you must specify a version!!\n");
|
|
return SS$_BADPARAM;
|
|
}
|
|
|
|
nam.nam$l_rsa = rsa;
|
|
nam.nam$b_rss = NAM$C_MAXRSS;
|
|
fab.fab$l_fop = FAB$M_NAM;
|
|
while ((sts = sys_search(&fab)) & 1) {
|
|
sts = sys_erase(&fab);
|
|
if ((sts & 1) == 0) {
|
|
printf("%%DELETE-F-DELERR, Delete error: %s\n",getmsg(sts));
|
|
break;
|
|
} else {
|
|
filecount++;
|
|
if( options & 1 ) {
|
|
rsa[nam.nam$b_rsl] = '\0';
|
|
printf("%%DELETE-I-DELETED, Deleted %s\n",rsa);
|
|
}
|
|
}
|
|
}
|
|
if (sts == RMS$_NMF) sts = 1;
|
|
}
|
|
if (sts & 1) {
|
|
if (filecount < 1) {
|
|
printf("%%DELETE-W-NOFILES, no files deleted\n");
|
|
}
|
|
} else {
|
|
printf("%%DELETE-F-ERROR Status: %s\n",getmsg(sts));
|
|
}
|
|
|
|
return sts;
|
|
}
|
|
|
|
/* test: you don't want to know! */
|
|
|
|
struct param testpars[] = { {"parameter", REQ, STRING, NOPA, "for test."},
|
|
{ NULL, 0, 0, NOPA, NULL }
|
|
};
|
|
|
|
struct VCB *test_vcb;
|
|
|
|
unsigned test(int argc,char *argv[],int qualc,char *qualv[])
|
|
{
|
|
int sts = 0;
|
|
struct fiddef fid;
|
|
|
|
UNUSED(argc);
|
|
UNUSED(qualc);
|
|
UNUSED(qualv);
|
|
|
|
sts = update_create(test_vcb,NULL,"Test.File",&fid,NULL);
|
|
printf("Test status of %d (%s)\n",sts,argv[1]);
|
|
return sts;
|
|
}
|
|
|
|
/* more test code... */
|
|
|
|
struct param extendpars[] = { {"ods-2_filespec", REQ, VMSFS, NOPA, "for file on ODS-2 volume."},
|
|
{ NULL, 0, 0, NOPA, NULL }
|
|
};
|
|
unsigned extend(int argc,char *argv[],int qualc,char *qualv[])
|
|
{
|
|
int sts;
|
|
struct FAB fab = cc$rms_fab;
|
|
|
|
UNUSED(argc);
|
|
UNUSED(qualc);
|
|
UNUSED(qualv);
|
|
|
|
fab.fab$l_fna = argv[1];
|
|
fab.fab$b_fns = strlen(fab.fab$l_fna);
|
|
fab.fab$b_fac = FAB$M_UPD;
|
|
if ((sts = sys_open(&fab)) & 1) {
|
|
fab.fab$l_alq = 32;
|
|
sts = sys_extend(&fab);
|
|
sys_close(&fab);
|
|
}
|
|
if ((sts & 1) == 0) {
|
|
printf("%%EXTEND-F-ERROR Status: %s\n",getmsg(sts));
|
|
}
|
|
return sts;
|
|
}
|
|
|
|
|
|
/* show: version */
|
|
|
|
#define MNAMEX(n) #n
|
|
#define MNAME(n) MNAMEX(n)
|
|
|
|
void show_version( void ) {
|
|
printf(" %s %s", MNAME(MODULE_NAME), MODULE_IDENT );
|
|
#ifdef DISKIMAGE
|
|
printf( " for image files" );
|
|
#endif
|
|
printf( " built %s %s", __DATE__, __TIME__ );
|
|
#ifdef USE_READLINE
|
|
printf(" with readline version %s", rl_library_version );
|
|
#endif
|
|
#ifdef USE_WNASPI32
|
|
# ifdef USE_READLINE
|
|
printf( " and" );
|
|
# else
|
|
printf( " with" );
|
|
# endif
|
|
printf( " direct SCSI access support");
|
|
#endif
|
|
printf( "\n\n" );
|
|
return;
|
|
}
|
|
|
|
/* show: the show command */
|
|
|
|
struct qual showkwds[] = { {"default", 0, 0, NV, "Default directory"},
|
|
#if defined(DISKIMAGE) || defined(_WIN32)
|
|
{"devices", 1, 0, NV, "Devices"},
|
|
#endif
|
|
{"qualifier_style", 2, 0, NV, "Qualifier style (Unix, VMS)" },
|
|
{"time", 3, 0, NV, "Time"},
|
|
{"verify", 4, 0, NV, "Command file echo" },
|
|
{"version", 5, 0, NV, "Version"},
|
|
{NULL, 0, 0, NV, NULL }
|
|
};
|
|
struct param showpars[] = { {"item_name", REQ, KEYWD, PA(showkwds), NULL },
|
|
{NULL, 0, 0, NOPA, NULL }
|
|
};
|
|
unsigned show(int argc,char *argv[],int qualc,char *qualv[]) {
|
|
int parnum;
|
|
|
|
UNUSED(argc);
|
|
UNUSED(qualc);
|
|
UNUSED(qualv);
|
|
|
|
parnum = checkquals( 0, showkwds, -1, argv+1 );
|
|
switch( parnum ) {
|
|
default:
|
|
return SS$_BADPARAM;
|
|
case 0: {
|
|
int sts;
|
|
unsigned short curlen;
|
|
char curdir[NAM$C_MAXRSS + 1];
|
|
struct dsc_descriptor curdsc;
|
|
curdsc.dsc_w_length = NAM$C_MAXRSS;
|
|
curdsc.dsc_a_pointer = curdir;
|
|
if ((sts = sys_setddir(NULL,&curlen,&curdsc)) & 1) {
|
|
curdir[curlen] = '\0';
|
|
printf(" %s\n",curdir);
|
|
} else {
|
|
printf("Error %s getting default\n",getmsg(sts));
|
|
}
|
|
return sts;
|
|
}
|
|
case 2:
|
|
printf ( " Qualifier style: %s\n", vms_qual? "/VMS": "-unix" );
|
|
return SS$_NORMAL;
|
|
case 3: {
|
|
unsigned sts;
|
|
char timstr[24];
|
|
unsigned short timlen;
|
|
struct dsc_descriptor timdsc;
|
|
|
|
timdsc.dsc_w_length = 20;
|
|
timdsc.dsc_a_pointer = timstr;
|
|
sts = sys_asctim(&timlen,&timdsc,NULL,0);
|
|
if (sts & 1) {
|
|
timstr[timlen] = '\0';
|
|
printf(" %s\n",timstr);
|
|
} else {
|
|
printf("%%SHOW-W-TIMERR error %s\n",getmsg(sts));
|
|
}
|
|
}
|
|
return SS$_NORMAL;
|
|
case 4:
|
|
printf( "Command file verification is %s\n", (verify_cmd? "on": "off") );
|
|
return SS$_NORMAL;
|
|
case 5:
|
|
show_version();
|
|
return SS$_NORMAL;
|
|
|
|
#if defined( _WIN32 ) && !defined( DISKIMAGE )
|
|
case 1: {
|
|
TCHAR *namep = NULL, *dname = NULL;
|
|
TCHAR devname[sizeof("Z:")];
|
|
int n = 0;
|
|
TCHAR l;
|
|
|
|
for( l = 'A'; l <= 'Z'; l++ ) {
|
|
snprintf( devname, sizeof( devname ), "%c:", l );
|
|
dname = namep = driveFromLetter( devname );
|
|
if( namep != NULL ) {
|
|
const char *type = NULL;
|
|
#define WINDEVPFX "\\Device\\"
|
|
if( strncmp( dname, WINDEVPFX, sizeof(WINDEVPFX)-1 ) == 0 )
|
|
dname += sizeof( WINDEVPFX ) -1;
|
|
#undef WINDEVPFX
|
|
if( !n++ )
|
|
printf( "Drv Type PhysicalName\n"
|
|
"--- --------- ------------\n" );
|
|
printf( " %s ", devname );
|
|
switch( GetDriveType( devname ) ) {
|
|
case DRIVE_REMOVABLE:
|
|
type = "Removable"; break;
|
|
case DRIVE_FIXED:
|
|
type = "Fixed"; break;
|
|
case DRIVE_REMOTE:
|
|
type = "Network";
|
|
dname = NULL; break;
|
|
case DRIVE_CDROM:
|
|
type = "CDROM"; break;
|
|
case DRIVE_RAMDISK:
|
|
type = "RAMdisk"; break;
|
|
default:
|
|
type = "Other";
|
|
dname = NULL; break;
|
|
}
|
|
printf( "%-9s %s\n", type, (dname? dname: "") );
|
|
free(namep);
|
|
}
|
|
}
|
|
}
|
|
return SS$_NORMAL;
|
|
#endif
|
|
#ifdef DISKIMAGE
|
|
case 1:
|
|
return diskio_showdrives();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
unsigned setdef_count = 0;
|
|
|
|
void setdef(char *newdef)
|
|
{
|
|
register unsigned sts;
|
|
struct dsc_descriptor defdsc;
|
|
defdsc.dsc_a_pointer = newdef;
|
|
defdsc.dsc_w_length = strlen(defdsc.dsc_a_pointer);
|
|
if ((sts = sys_setddir(&defdsc,NULL,NULL)) & 1) {
|
|
setdef_count++;
|
|
} else {
|
|
printf("Error %s setting default to %s\n",getmsg(sts),newdef);
|
|
}
|
|
}
|
|
|
|
/* set: the set command */
|
|
|
|
hlpfunc_t sethelp;
|
|
|
|
struct qual setkwds[] = { {"default", 0, 0, NV, "Default directory"},
|
|
{"directory_qualifiers", 1, 0, NV, "Default qualifiers for DIRECTORY command" },
|
|
{"qualifier_style", 2, 0, NV, "Qualifier style (Unix, VMS)" },
|
|
{"verify", 3, 0, NV, "-Display commands in indirect files" },
|
|
{"noverify", 4, 0, NV, NULL },
|
|
{NULL, 0, 0, NV, NULL }
|
|
};
|
|
struct qual setqskwds[] = {{"unix", 1, 0, NV, "Unix style options, '-option'"},
|
|
{"vms", 2, 0, NV, "VMS style qualifiers, '/qualifier'"},
|
|
{NULL, 0, 0, NV, NULL }
|
|
};
|
|
struct param setpars[] = { {"item_name", REQ, KEYWD, PA(setkwds), NULL },
|
|
{"value" , CND, KEYWD, sethelp, setqskwds, NULL },
|
|
{NULL, 0, 0, NOPA, NULL },
|
|
};
|
|
|
|
const char * sethelp( struct CMDSET *cmd, struct param *p, int argc, char **argv ) {
|
|
int par;
|
|
|
|
UNUSED( cmd );
|
|
UNUSED( argc );
|
|
|
|
if( argc == 1 ) {
|
|
p->helpstr = "";
|
|
p->ptype = KEYWD;
|
|
p->arg = setkwds;
|
|
return "Type 'help set value ITEM' for item details\n";
|
|
}
|
|
|
|
par = checkquals( 0, setkwds, -1, argv+1 );
|
|
if( par == -1 )
|
|
return NULL;
|
|
switch( par ) {
|
|
case 0:
|
|
p->helpstr = "default directory on volume - ";
|
|
p->ptype = VMSFS;
|
|
break;
|
|
case 1:
|
|
p->helpstr = "directory qualifier name ";
|
|
p->ptype = KEYWD;
|
|
p->arg = dirquals;
|
|
break;
|
|
case 2:
|
|
p->helpstr = "style ";
|
|
p->ptype = KEYWD;
|
|
p->arg = setqskwds;
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
p->ptype = NONE;
|
|
break;
|
|
|
|
default:
|
|
abort();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
unsigned set(int argc,char *argv[],int qualc,char *qualv[])
|
|
{
|
|
int parnum;
|
|
|
|
UNUSED(argc);
|
|
UNUSED(qualc);
|
|
UNUSED(qualv);
|
|
|
|
parnum = checkquals( 0, setkwds, -1, argv+1 );
|
|
switch( parnum ) {
|
|
default:
|
|
return SS$_BADPARAM;
|
|
case 0: /* default */
|
|
if( qualc ) {
|
|
printf( "No qualifiers are permitted\n" );
|
|
return 0;
|
|
}
|
|
setdef(argv[2]);
|
|
return 1;
|
|
case 1:{ /* directory_qualifiers */
|
|
int options = checkquals(dir_default,dirquals,qualc,qualv);
|
|
if( options == -1 )
|
|
return SS$_BADPARAM;
|
|
dir_defopt = options;
|
|
return 1;
|
|
}
|
|
case 2: { /* qualifier_style */
|
|
int par = checkquals (0,setqskwds,1,argv+2);
|
|
if( par == -1 )
|
|
return SS$_BADPARAM;
|
|
switch(par) {
|
|
default:
|
|
abort();
|
|
case 2:
|
|
vms_qual = 1;
|
|
break;
|
|
case 1:
|
|
vms_qual = 0;
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
case 3:
|
|
verify_cmd = 1;
|
|
return 1;
|
|
case 4:
|
|
verify_cmd = 0;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
#ifndef VMSIO
|
|
|
|
/* The bits we need when we don't have real VMS routines underneath... */
|
|
|
|
struct param dmopars[] = { {"drive_letter", REQ, STRING, NOPA, "Drive containing volume to dismount", },
|
|
{NULL, 0, 0, NOPA, NULL }
|
|
};
|
|
|
|
unsigned dodismount(int argc,char *argv[],int qualc,char *qualv[])
|
|
{
|
|
struct DEV *dev;
|
|
int sts;
|
|
|
|
UNUSED(argc);
|
|
UNUSED(qualc);
|
|
UNUSED(qualv);
|
|
|
|
sts = device_lookup(strlen(argv[1]),argv[1],0,&dev);
|
|
if (sts & 1) {
|
|
if (dev->vcb != NULL) {
|
|
sts = dismount(dev->vcb);
|
|
#ifdef DISKIMAGE
|
|
sts = diskio_unmapdrive( dev->devnam );
|
|
#endif
|
|
} else {
|
|
sts = SS$_DEVNOTMOUNT;
|
|
}
|
|
}
|
|
if ((sts & 1) == 0) printf("%%DISMOUNT-E-STATUS Error: %s\n",getmsg(sts));
|
|
return sts;
|
|
}
|
|
|
|
#define mnt_write 1
|
|
|
|
struct qual mouquals[] = { {"readonly", 0, mnt_write, NV, "Only allow reading from volume"},
|
|
{"write", mnt_write, 0, NV, "Allow writing to volume", },
|
|
{NULL, 0, 0, NV, NULL } };
|
|
struct param moupars[] = { {"volumes", REQ, LIST, NOPA,
|
|
#ifdef DISKIMAGE
|
|
"disk images in volume set separated by comma"
|
|
#else
|
|
"devices in volume set separated by comma"
|
|
#endif
|
|
},
|
|
{"labels", OPT, LIST, NOPA, "volume labels separated by comma" },
|
|
{ NULL, 0, 0, NOPA, NULL }
|
|
};
|
|
|
|
unsigned domount(int argc,char *argv[],int qualc,char *qualv[])
|
|
{
|
|
char *dev = argv[1];
|
|
char *lab = argv[2];
|
|
int sts = 1,devices = 0;
|
|
char *devs[100],*labs[100];
|
|
int options;
|
|
|
|
options = checkquals(0,mouquals,qualc,qualv);
|
|
if( options == -1 )
|
|
return SS$_BADPARAM;
|
|
|
|
UNUSED(argc);
|
|
|
|
while (*lab != '\0') {
|
|
labs[devices++] = lab;
|
|
while (*lab != ',' && *lab != '\0') lab++;
|
|
if (*lab != '\0') {
|
|
*lab++ = '\0';
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
devices = 0;
|
|
while (*dev != '\0') {
|
|
devs[devices++] = dev;
|
|
while (*dev != ',' && *dev != '\0') dev++;
|
|
if (*dev != '\0') {
|
|
*dev++ = '\0';
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (devices > 0) {
|
|
unsigned i;
|
|
struct VCB *vcb;
|
|
#ifdef DISKIMAGE
|
|
for( i = 0; i < (unsigned)devices; i++ ) {
|
|
char *drive;
|
|
drive = diskio_mapfile( devs[i], (options & mnt_write) != 0 );
|
|
if( drive == NULL )
|
|
return 0;
|
|
devs[i] = drive;
|
|
}
|
|
#endif
|
|
sts = mount(options&mnt_write,devices,devs,labs,&vcb);
|
|
if (sts & 1) {
|
|
for (i = 0; i < vcb->devices; i++)
|
|
if (vcb->vcbdev[i].dev != NULL)
|
|
printf("%%MOUNT-I-MOUNTED, Volume %12.12s mounted on %s\n",
|
|
vcb->vcbdev[i].home.hm2$t_volname,vcb->vcbdev[i].dev->devnam);
|
|
if (setdef_count == 0) {
|
|
char *colon,tmp[256],defdir[256];
|
|
snprintf( tmp, sizeof(tmp), "%s", vcb->vcbdev[0].dev->devnam);
|
|
colon = strchr(tmp,':');
|
|
if (colon != NULL) *colon = '\0';
|
|
snprintf( defdir, sizeof(defdir), "%s:[000000]", tmp );
|
|
setdef(defdir);
|
|
test_vcb = vcb;
|
|
}
|
|
} else {
|
|
printf("Mount failed with %s\n",getmsg(sts));
|
|
#ifdef DISKIMAGE
|
|
for( i = 0; i < (unsigned)devices; i++ ) {
|
|
sts = diskio_unmapdrive( devs[i] );
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
return sts;
|
|
}
|
|
|
|
|
|
void direct_show(void);
|
|
void phyio_show(void);
|
|
|
|
/* statis: print some simple statistics */
|
|
|
|
unsigned statis(int argc,char *argv[],int qualc,char *qualv[])
|
|
{
|
|
UNUSED(argc);
|
|
UNUSED(argv);
|
|
UNUSED(qualc);
|
|
UNUSED(qualv);
|
|
|
|
printf("Statistics:-\n");
|
|
direct_show();
|
|
cache_show();
|
|
phyio_show();
|
|
return 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
void cmdhelp( struct CMDSET *cmdset ) {
|
|
struct CMDSET *cmd;
|
|
int n = 0;
|
|
size_t max = 0;
|
|
|
|
for( cmd = cmdset; cmd->name; cmd++ ) {
|
|
if( strlen(cmd->name) > max )
|
|
max = strlen(cmd->name);
|
|
}
|
|
for( cmd = cmdset; cmd->name; cmd++ ) {
|
|
if( cmd->helpstr == NULL )
|
|
continue;
|
|
if( n++ % 4 == 0 )
|
|
printf( "\n" );
|
|
printf(" %-*s", (int)max,cmd->name );
|
|
}
|
|
printf( "\n" );
|
|
}
|
|
|
|
void qualhelp( int par, struct qual *qtable ) {
|
|
struct qual *q;
|
|
int n = 0;
|
|
size_t max = 0;
|
|
|
|
if( par < 0 )
|
|
max = -par;
|
|
|
|
#define NOSTR "[no]"
|
|
#define NOSTR_LEN (sizeof(NOSTR)-1)
|
|
for( q = qtable; q->name; q++ ) {
|
|
if( q->helpstr ) {
|
|
size_t len = strlen(q->name);
|
|
if( *q->helpstr == '-' )
|
|
len += NOSTR_LEN;
|
|
if( q->qtype != NOVAL )
|
|
len++;
|
|
if( len > max )
|
|
max = len;
|
|
}
|
|
}
|
|
for( q = qtable; q->name; q++ ) {
|
|
if( q->helpstr ) {
|
|
if( !n++ && !par )
|
|
printf( " %s:\n", "Qualifiers" );
|
|
printf(" %s", par? par<0? " ": "": vms_qual? "/" : "-" );
|
|
if( *q->helpstr == '-' )
|
|
switch( q->qtype ) {
|
|
case NOVAL:
|
|
printf( NOSTR "%-*s - %s\n",
|
|
(int) (max-NOSTR_LEN), q->name, q->helpstr+1 );
|
|
break;
|
|
case KEYVAL:
|
|
printf( NOSTR "%s=%-*s - %s\n",
|
|
q->name, (int) (max-(NOSTR_LEN+strlen(q->name)+1)), "",
|
|
q->helpstr+1 );
|
|
qualhelp( -(int)max, (struct qual *)q->arg );
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
else
|
|
switch( q->qtype ) {
|
|
case NOVAL:
|
|
printf("%-*s - %s\n", (int) max, q->name, q->helpstr );
|
|
break;
|
|
case KEYVAL:
|
|
printf( "%s=%-*s - %s\n", q->name, (int)(max-strlen(q->name)+1), "",
|
|
q->helpstr );
|
|
qualhelp( -(int)max, (struct qual *)q->arg );
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
}
|
|
if( par >= 0 )
|
|
printf( "\n" );
|
|
}
|
|
#undef NOSTR
|
|
#undef NOSTR_LEN
|
|
|
|
void parhelp( struct CMDSET *cmd, int argc, char **argv) {
|
|
struct param *p;
|
|
struct param *ptable;
|
|
const char *footer = NULL;
|
|
|
|
ptable = cmd->params;
|
|
|
|
if( ptable == NULL ) {
|
|
printf( "%s has no parameters\n", cmd->name );
|
|
return;
|
|
}
|
|
|
|
if( argc == 0 ) {
|
|
int col = 0;
|
|
size_t max = 0;
|
|
|
|
for( p = ptable; p->name != NULL; p++ ) {
|
|
if( 1||p->helpstr ) {
|
|
size_t len = strlen(p->name);
|
|
if( len > max )
|
|
max = len;
|
|
}
|
|
}
|
|
for( p = ptable; p->name != NULL; p++ ) {
|
|
if(1|| p->helpstr ) {
|
|
size_t len = strlen(p->name);
|
|
if( !col ) {
|
|
printf( " Parameters:\n " );
|
|
col = 4;
|
|
} else {
|
|
if( 1+col + len > 80 ) {
|
|
printf( "\n " );
|
|
col = 4;
|
|
} else {
|
|
printf ( " " );
|
|
col++;
|
|
}
|
|
}
|
|
printf( "%-*s", (int)max, p->name );
|
|
col += len;
|
|
}
|
|
}
|
|
printf( "\n\n Type help PARAMETER for more about each parameter.\n" );
|
|
return;
|
|
}
|
|
|
|
for( p = ptable; p->name != NULL; p++ ) {
|
|
if( keycomp( argv[0], p->name ) )
|
|
break;
|
|
}
|
|
if( p->name == NULL ) {
|
|
printf( "No parameter '%s' found\n", argv[0] );
|
|
return;
|
|
}
|
|
if( p->hlpfnc != NULL )
|
|
footer = (*p->hlpfnc)(cmd, p, argc, argv);
|
|
if( p->ptype == NONE ) {
|
|
printf( "Parameter is not used for this command\n" );
|
|
return;
|
|
}
|
|
|
|
printf( " %s: ", p->name );
|
|
|
|
if( p->flags == OPT )
|
|
printf( "optional " );
|
|
|
|
switch( p->ptype ) {
|
|
case VMSFS:
|
|
printf( "VMS file specification %s\n", p->helpstr );
|
|
break;
|
|
case LCLFS:
|
|
printf( "local file specification %s\n", p->helpstr );
|
|
break;
|
|
case KEYWD:
|
|
printf( "%skeyword, one of the following:\n",
|
|
(p->helpstr == NULL? "": p->helpstr) );
|
|
qualhelp( 1, (struct qual *)p->arg );
|
|
break;
|
|
case LIST:
|
|
printf( "list, %s\n", p->helpstr );
|
|
break;
|
|
case STRING:
|
|
printf( "%s\n", p->helpstr );
|
|
break;
|
|
case CMDNAM:
|
|
printf( "command name, one of the following:\n");
|
|
cmdhelp( (struct CMDSET *)p->arg );
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
if( footer )
|
|
printf( "%s", footer );
|
|
|
|
return;
|
|
}
|
|
|
|
/* help: Display help guided by command table. */
|
|
|
|
struct CMDSET cmdset[];
|
|
|
|
struct param helppars[] = { {"command", OPT, CMDNAM, PA(cmdset), NULL },
|
|
{"parameter", OPT, STRING, NOPA, NULL },
|
|
{"value", OPT, STRING, NOPA, NULL },
|
|
{NULL, 0, 0, NOPA, NULL },
|
|
};
|
|
|
|
/* information about the commands we know... */
|
|
unsigned help(int argc,char *argv[],int qualc,char *qualv[]);
|
|
|
|
struct CMDSET cmdset[] = {
|
|
{ "copy", copy, 0,copyquals,copypars, "Copy a file from VMS to host file", },
|
|
{ "import", import, 0,NULL, importpars, "Copy a file from host to VMS", },
|
|
{ "delete", del, 0,delquals, delpars, "Delete a VMS file", },
|
|
{ "difference",diff, 0,NULL, diffpars, "Compare VMS file to host file", },
|
|
{ "directory", dir, 0,dirquals,dirpars, "List directory of VMS files", },
|
|
{ "exit", NULL, 2,NULL, NULL, "Exit ODS2", },
|
|
{ "extend", extend, 0,NULL, extendpars, NULL },
|
|
{ "help", help, 0,NULL, helppars, "Obtain help on a command", },
|
|
{ "quit", NULL, 2,NULL, NULL, "Exit ODS-2", },
|
|
{ "show", show, 0,NULL, showpars, "Display state", },
|
|
{ "search", search, 0,NULL, searchpars, "Search VMS file for a string", },
|
|
{ "set", set, 0,NULL, setpars, "Set PARAMETER - set HELP for list", },
|
|
#ifndef VMSIO
|
|
{ "dismount", dodismount,0,NULL, dmopars, "Dismount a VMS volume", },
|
|
{ "mount", domount, 0,mouquals, moupars, "Mount a VMS volume", },
|
|
{ "statistics",statis, 0,NULL, NULL, "Display debugging statistics", },
|
|
#endif
|
|
{ "test", test, 0,NULL, testpars, NULL },
|
|
{ "type", typ, 0,NULL, typepars, "Display a VMS file on the terminal", },
|
|
{ NULL, NULL, 0,NULL, NULL, NULL } /* ** END MARKER ** */
|
|
};
|
|
|
|
unsigned help(int argc,char *argv[],int qualc,char *qualv[])
|
|
{
|
|
struct CMDSET *cmd;
|
|
|
|
UNUSED(qualc);
|
|
UNUSED(qualv);
|
|
|
|
if( argc <= 1 ) {
|
|
show_version();
|
|
|
|
printf("\n Please send problems/comments to Paulnank@au1.ibm.com\n");
|
|
|
|
printf(" Commands are:\n");
|
|
cmdhelp( cmdset );
|
|
printf( "\n Type HELP COMMAND for information on any command.\n" );
|
|
return 1;
|
|
}
|
|
|
|
for( cmd = cmdset; cmd->name; cmd++ ) {
|
|
if( keycomp(argv[1],cmd->name) ) {
|
|
if( argc >= 3 ) {
|
|
parhelp( cmd, argc-2, argv+2 );
|
|
return 1;
|
|
}
|
|
if( cmd->helpstr == NULL ) {
|
|
printf( "%s: No help available\n",cmd->name );
|
|
} else {
|
|
printf( "%s: %s\n",cmd->name,cmd->helpstr );
|
|
}
|
|
if( cmd->validquals != NULL )
|
|
qualhelp( 0, cmd->validquals );
|
|
if( cmd->params != NULL )
|
|
parhelp( cmd, 0, NULL );
|
|
if( strcmp(cmd->name, "mount") == 0 )
|
|
phyio_help(stdout);
|
|
return 1;
|
|
}
|
|
}
|
|
printf( "%s: command not found\n", argv[1] );
|
|
return 0;
|
|
|
|
printf(" set_default type\n");
|
|
printf(" Example:-\n $ mount e:\n");
|
|
printf(" $ search e:[vms_common.decc*...]*.h rms$_wld\n");
|
|
printf(" $ set default e:[sys0.sysmgr]\n");
|
|
printf(" $ copy *.com;-1 c:\\*.*\n");
|
|
printf(" $ directory/file/size/date [-.sys*...].%%\n");
|
|
printf(" $ exit\n");
|
|
return 1;
|
|
}
|
|
|
|
/* cmdexecute: identify and execute a command */
|
|
|
|
int cmdexecute(int argc,char *argv[],int qualc,char *qualv[])
|
|
{
|
|
char *ptr;
|
|
struct CMDSET *cmd, *cp = NULL;
|
|
unsigned cmdsiz;
|
|
int minpars, maxpars;
|
|
struct param *p;
|
|
int sts;
|
|
|
|
ptr = argv[0];
|
|
while (*ptr != '\0') {
|
|
*ptr = tolower(*ptr);
|
|
ptr++;
|
|
}
|
|
ptr = argv[0];
|
|
cmdsiz = strlen(ptr);
|
|
|
|
for( cmd = cmdset; cmd->name != NULL; cmd++ ) {
|
|
if( cmdsiz <= strlen( cmd->name ) &&
|
|
keycomp( ptr, cmd->name ) ) {
|
|
if( (cmd->uniq && cmdsiz >= cmd->uniq) ) {
|
|
cp = cmd;
|
|
break;
|
|
}
|
|
if( cp != NULL ) {
|
|
printf("%%ODS2-E-AMBCMD, '%s' is ambiguous\n", ptr );
|
|
return 0;
|
|
}
|
|
cp = cmd;
|
|
}
|
|
}
|
|
if( cp == NULL ) {
|
|
printf("%%ODS2-E-ILLCMD, Unknown command '%s'\n", ptr );
|
|
return 0;
|
|
}
|
|
cmd = cp;
|
|
|
|
if (cmd->proc == NULL)
|
|
return -1;
|
|
|
|
minpars =
|
|
maxpars = 1;
|
|
for( p = cmd->params; p && p->name != NULL; p++ ) {
|
|
maxpars++;
|
|
if( p->flags == REQ )
|
|
minpars++;
|
|
}
|
|
|
|
if (argc < minpars|| argc > maxpars) {
|
|
printf( "%%ODS2-E-PARAMS, Too %s parameters specified\n", (argc < minpars? "few": "many") );
|
|
return 0;
|
|
}
|
|
sts = (*cmd->proc) (argc,argv,qualc,qualv);
|
|
#ifndef VMSIO
|
|
/* cache_flush(); */
|
|
#endif
|
|
|
|
return sts;
|
|
}
|
|
|
|
/* cmdsplit: break a command line into its components */
|
|
|
|
/*
|
|
* New feature for Unix: '//' or '--' stops qualifier parsing.
|
|
* This enables us to copy to Unix directories with VMS style /qualifiers.
|
|
* copy /bin // *.com /tmp/ *
|
|
* is split into argv[0] -> "*.com" argv[1] -> "/tmp/ *" qualv[0]-> "/bin"
|
|
*
|
|
* Of course, one can just "" the string (VMS double quote rules)...
|
|
*/
|
|
#define MAXITEMS 32
|
|
int cmdsplit(char *str)
|
|
{
|
|
int argc = 0,qualc = 0;
|
|
char *argv[MAXITEMS],*qualv[MAXITEMS];
|
|
char *sp = str;
|
|
int i;
|
|
char q = vms_qual? '/': '-';
|
|
for (i = 0; i < MAXITEMS; i++) argv[i] = qualv[i] = "";
|
|
while (*sp != '\0') {
|
|
while (*sp == ' ') sp++;
|
|
if (*sp != '\0') {
|
|
if (*sp == q) {
|
|
*sp++ = '\0';
|
|
if (*sp == q) {
|
|
sp++;
|
|
q = '\0';
|
|
continue;
|
|
}
|
|
if( qualc >= MAXITEMS ) {
|
|
printf( "Too many qualifiers specified\n" );
|
|
return 0;
|
|
}
|
|
qualv[qualc++] = sp;
|
|
} else {
|
|
if( argc >= MAXITEMS ) {
|
|
printf( "Too many arguments specified\n" );
|
|
return 0;
|
|
}
|
|
argv[argc++] = sp;
|
|
if( *sp == '"' ) {
|
|
++argv[argc-1];
|
|
for( ++sp; *sp && (*sp != '"' || sp[1] == '"'); sp++ ) {
|
|
if( *sp == '"' )
|
|
memmove( sp, sp+1, strlen(sp+1)+1 );
|
|
}
|
|
if( *sp == '"' ) {
|
|
*sp++ = '\0';
|
|
if( *sp && *sp != ' ' ) {
|
|
printf( "Unterminated string\n" );
|
|
return 0;
|
|
}
|
|
} else {
|
|
printf( "Unterminated string\n" );
|
|
return 0;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
while (*sp != ' ' && *sp != q && *sp != '\0') sp++;
|
|
if (*sp == '\0') {
|
|
break;
|
|
} else {
|
|
if (*sp != q) *sp++ = '\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (argc > 0) return cmdexecute(argc,argv,qualc,qualv);
|
|
return 1;
|
|
}
|
|
|
|
#ifdef VMS
|
|
#include <smgdef.h>
|
|
#include <smg$routines.h>
|
|
|
|
char *getcmd(char *inp, char *prompt)
|
|
{
|
|
struct dsc_descriptor prompt_d = {strlen(prompt),DSC$K_DTYPE_T,
|
|
DSC$K_CLASS_S, prompt};
|
|
struct dsc_descriptor input_d = {1024,DSC$K_DTYPE_T,
|
|
DSC$K_CLASS_S, inp};
|
|
int status;
|
|
char *retstat;
|
|
static unsigned long key_table_id = 0;
|
|
static unsigned long keyboard_id = 0;
|
|
|
|
if (key_table_id == 0) {
|
|
status = smg$create_key_table (&key_table_id);
|
|
if (status & 1)
|
|
status = smg$create_virtual_keyboard (&keyboard_id);
|
|
if (!(status & 1)) return (NULL);
|
|
}
|
|
|
|
status = smg$read_composed_line (&keyboard_id, &key_table_id,
|
|
&input_d, &prompt_d, &input_d, 0,0,0,0,0,0,0);
|
|
|
|
if (status == SMG$_EOF)
|
|
retstat = NULL;
|
|
else {
|
|
inp[input_d.dsc_w_length] = '\0';
|
|
retstat = inp;
|
|
}
|
|
|
|
return(retstat);
|
|
}
|
|
#endif /* VMS */
|
|
|
|
|
|
/* main: the simple mainline of this puppy... */
|
|
|
|
/*
|
|
* Parse the command line to read and execute commands:
|
|
* ./ods2 mount scd1 $ set def [hartmut] $ copy *.com $ exit
|
|
* '$' is used as command delimiter because it is a familiar character
|
|
* in the VMS world. However, it should be used surounded by white spaces;
|
|
* otherwise, a token '$copy' is interpreted by the Unix shell as a shell
|
|
* variable. Quoting the whole command string might help:
|
|
* ./ods2 'mount scd1 $set def [hartmut] $copy *.com $exit'
|
|
* If the ods2 reader should use any switches '-c' should be used for
|
|
* the command strings, then quoting will be required:
|
|
* ./ods2 -c 'mount scd1 $ set def [hartmut] $ copy *.com $ exit'
|
|
*
|
|
* The same command concatenation can be implemented for the prompted input.
|
|
* As for indirect command files (@), it isn't checked if one of the chained
|
|
* commands fails. The user has to be careful, all the subsequent commands
|
|
* are executed!
|
|
*/
|
|
|
|
int main(int argc,char *argv[])
|
|
{
|
|
char str[2048];
|
|
char *command_line = NULL;
|
|
FILE *atfile = NULL;
|
|
#ifdef USE_READLINE
|
|
char *p;
|
|
wordexp_t wex;
|
|
char *rl = NULL;
|
|
char *hfname = NULL;
|
|
char mname[3+sizeof( MNAME(MODULE_NAME) )];
|
|
|
|
snprintf( mname, sizeof(mname ), "~/.%s", MNAME(MODULE_NAME) );
|
|
rl_readline_name =
|
|
p = mname+3;
|
|
do {
|
|
*p = tolower( *p );
|
|
} while( *p++ );
|
|
memset( &wex, 0, sizeof( wordexp_t ) );
|
|
if( wordexp( mname, &wex, WRDE_NOCMD ) == 0 && wex.we_wordc == 1 ) {
|
|
hfname = wex.we_wordv[0];
|
|
history_truncate_file( hfname, 200 );
|
|
stifle_history( 200 );
|
|
read_history( hfname );
|
|
} else {
|
|
hfname = NULL;
|
|
}
|
|
#endif
|
|
|
|
if (argc>1) {
|
|
int i, l = 0;
|
|
for (i=1; i<argc; i++) {
|
|
int al;
|
|
char *newp;
|
|
|
|
if (command_line == NULL) {
|
|
command_line = (char *)malloc(1);
|
|
*command_line = '\0';
|
|
l = 0;
|
|
}
|
|
al = strlen(argv[i]);
|
|
newp = (char *)realloc(command_line,l+1+al+1);
|
|
if( newp == NULL ) {
|
|
perror( "realloc" );
|
|
exit (1);
|
|
}
|
|
command_line = newp;
|
|
snprintf(command_line+l,1+al+1," %s",argv[i]);
|
|
l += 1+al;
|
|
}
|
|
} else command_line = NULL;
|
|
while (1) {
|
|
char *ptr;
|
|
if (command_line) {
|
|
static int i=0;
|
|
unsigned j=0;
|
|
for (; j < sizeof str && command_line[i]; i++)
|
|
if (command_line[i] == '$') {
|
|
++i;
|
|
break;
|
|
} else str[j++] = command_line[i];
|
|
if (j<sizeof str)
|
|
str[j]= '\0';
|
|
else str[j-1]= '\0';
|
|
if (command_line[i] =='\0') {
|
|
free(command_line);
|
|
command_line = NULL;
|
|
}
|
|
ptr = str;
|
|
} else {
|
|
if (atfile != NULL) {
|
|
if (fgets(str,sizeof(str),atfile) == NULL) {
|
|
fclose(atfile);
|
|
atfile = NULL;
|
|
*str = '\0';
|
|
} else {
|
|
#ifndef _WIN32
|
|
ptr = strchr(str, '\r' );
|
|
if( ptr == NULL ) /* Do not separate from the next line */
|
|
#endif
|
|
ptr = strchr(str,'\n');
|
|
if (ptr != NULL) *ptr = '\0';
|
|
if( verify_cmd )
|
|
printf("$> %s\n",str);
|
|
}
|
|
ptr = str;
|
|
} else {
|
|
#ifdef VMS
|
|
if (getcmd (str, "$> ") == NULL) break;
|
|
ptr = str;
|
|
#elif( defined USE_READLINE )
|
|
if (rl != NULL) {
|
|
free( rl );
|
|
}
|
|
rl =
|
|
ptr = readline( MNAME(MODULE_NAME) "$> " );
|
|
if (rl == NULL) {
|
|
break;
|
|
} else {
|
|
if (*rl != '\0') {
|
|
add_history( rl );
|
|
}
|
|
}
|
|
#else
|
|
printf("$> ");
|
|
if (fgets(str, sizeof(str), stdin) == NULL) break;
|
|
ptr = strchr(str, '\n');
|
|
if(ptr != NULL) *ptr = '\0';
|
|
ptr = str;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
while (*ptr == ' ' || *ptr == '\t') ptr++;
|
|
if (strlen(ptr) && *ptr != '!') {
|
|
if (*ptr == '@') {
|
|
if (atfile != NULL) {
|
|
printf("%%ODS2-W-INDIRECT, indirect indirection not permitted\n");
|
|
} else {
|
|
if ((atfile = openf(ptr + 1,"r")) == NULL) {
|
|
perror("%%Indirection failed");
|
|
printf("\n");
|
|
free(command_line);
|
|
command_line = NULL;
|
|
*str = '\0';
|
|
}
|
|
}
|
|
} else {
|
|
int sts;
|
|
|
|
sts = cmdsplit(ptr);
|
|
if( sts == -1 )
|
|
break;
|
|
if ((sts & 1) == 0) {
|
|
if( atfile != NULL ) {
|
|
fclose(atfile);
|
|
atfile = NULL;
|
|
}
|
|
free(command_line);
|
|
command_line = NULL;
|
|
*str = '\0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#ifdef USE_READLINE
|
|
if (rl != NULL) {
|
|
free( rl );
|
|
}
|
|
if (hfname != NULL) {
|
|
write_history( hfname );
|
|
}
|
|
#endif
|
|
if (atfile != NULL) fclose(atfile);
|
|
#ifdef VMS
|
|
return 1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|