From 3f193e6f99b6eeca1f6b9de5b43b4a5f5ef9bd90 Mon Sep 17 00:00:00 2001 From: Timothe Litt Date: Wed, 23 Mar 2016 13:11:05 -0400 Subject: [PATCH] Baselevel commit for V2.99 Reorganize commands into separate modules - ods2.c was way too big. This reduces it from around 3000 LOC to less than 1K LOC. Reorder I/O so PHYVIRT mediates all physical access. Fix memory leaks of device structures. Reorganize Files-11 vs. internal structures. Allow more granular control of DEBUG from the makefile. Show VOLUME handles free space and tries to match geometry from SCB to known disk types. Fix many compilation warnings. Add code to allow dumping disk data for debug. Automatically generate descrip.mms from dependencies. Correctly handle directory default version limit; previously confused with file version limit. Teach help to sort tables for presentation; manually keeping them sorted is problematic, and code maintenance prefers functional groupings. Add the ability to initialize a Files-11 volume. (Not quite complete.) Add dependency generation to makefiles. Excludes system headers so makedepends isn't required to build. Simplify makefiles to use more recipes. Teach makefiles to list all sources and headers so it's easier to keep git and MSVC up-to-date. Add support for accessing images of disks with interleave/skew/offsets (e.g. RX02). Add VHD support, including ability to create a snapshot with mount/write. Teach RMS to handle NOSPAN variable length records correctly. Fix RMS GET handling of variable length records with VFC that span block boundaries. Fix delete file (a day-one incomplete). Still some cases to validate. Purge cache of modified blocks at rundown and at the end of each command. Do not allow deletion of reserved files. Move revision history to version.h Correct various permissions in git. --- .gitignore | 1 + extracters/ods2/ODS2.exe | Bin 89600 -> 140288 bytes extracters/ods2/ODS2.vcxproj | 44 +- extracters/ods2/ODS2.vcxproj.filters | 96 + extracters/ods2/access.c | 678 ++-- extracters/ods2/access.h | 188 +- extracters/ods2/build.com | 0 extracters/ods2/cache.c | 85 +- extracters/ods2/cache.h | 6 +- extracters/ods2/cmddef.h | 108 + extracters/ods2/compat.c | 135 +- extracters/ods2/compat.h | 29 + extracters/ods2/copycmd.c | 239 ++ extracters/ods2/createcmd.c | 52 + extracters/ods2/debug.c | 122 + extracters/ods2/debug.h | 15 + extracters/ods2/deletecmd.c | 85 + extracters/ods2/descrip.h | 2 +- extracters/ods2/descrip.mms | 224 +- extracters/ods2/device.c | 31 +- extracters/ods2/device.h | 19 +- extracters/ods2/diffcmd.c | 96 + extracters/ods2/dircmd.c | 436 +++ extracters/ods2/direct.c | 276 +- extracters/ods2/direct.h | 24 +- extracters/ods2/dismountcmd.c | 49 + extracters/ods2/extendcmd.c | 50 + extracters/ods2/f11def.h | 217 ++ extracters/ods2/fibdef.h | 3 +- extracters/ods2/helpcmd.c | 344 ++ extracters/ods2/importcmd.c | 119 + extracters/ods2/initialcmd.c | 161 + extracters/ods2/initvol.c | 1264 +++++++ extracters/ods2/initvol.h | 84 + extracters/ods2/makefile-vhd.nt | 11 + extracters/ods2/makefile-vhd.unix | 19 + extracters/ods2/makefile.depends | 123 + extracters/ods2/makefile.generic | 132 +- extracters/ods2/makefile.nt | 24 +- extracters/ods2/makefile.solaris | 14 +- extracters/ods2/makefile.tru64 | 14 +- extracters/ods2/makefile.unix | 25 +- extracters/ods2/makefile.vms | 32 + extracters/ods2/mountcmd.c | 153 + extracters/ods2/ods2.c | 3140 ++++------------- extracters/ods2/ods2.h | 37 +- extracters/ods2/ods2_alpha.exe | Bin extracters/ods2/ods2_solaris_intel.exe | Bin extracters/ods2/ods2_solaris_sparc.exe | Bin extracters/ods2/ods2_tru64.exe | Bin extracters/ods2/ods2_vax_decc.exe | Bin extracters/ods2/ods2_vax_vaxc.exe | Bin extracters/ods2/ods2_win32.exe | Bin extracters/ods2/phyio.h | 16 +- extracters/ods2/phynt.c | 219 +- extracters/ods2/phyos2.c | 20 +- extracters/ods2/phyunix.c | 174 +- extracters/ods2/phyvhd.c | 447 +++ extracters/ods2/phyvhd.h | 13 + extracters/ods2/phyvirt.c | 963 ++++- extracters/ods2/phyvirt.h | 61 +- extracters/ods2/phyvms.c | 113 +- extracters/ods2/rms.c | 379 +- extracters/ods2/rms.h | 5 +- extracters/ods2/searchcmd.c | 153 + extracters/ods2/setcmd.c | 202 ++ extracters/ods2/showcmd.c | 206 ++ extracters/ods2/spawncmd.c | 89 + extracters/ods2/ssdef.h | 3 + extracters/ods2/sysmsg.c | 44 +- extracters/ods2/sysmsg.h | 26 + extracters/ods2/typecmd.c | 75 + extracters/ods2/update.c | 201 +- .../ods2/valgrind_suppressions_readline | 9 + extracters/ods2/version.h | 58 +- extracters/ods2/vmstime.c | 20 +- extracters/ods2/vmstime.h | 2 +- putr/putr.com | Bin 78 files changed, 8619 insertions(+), 3885 deletions(-) mode change 100644 => 100755 extracters/ods2/ODS2.exe mode change 100644 => 100755 extracters/ods2/build.com create mode 100644 extracters/ods2/cmddef.h create mode 100644 extracters/ods2/copycmd.c create mode 100644 extracters/ods2/createcmd.c create mode 100644 extracters/ods2/debug.c create mode 100644 extracters/ods2/debug.h create mode 100644 extracters/ods2/deletecmd.c create mode 100644 extracters/ods2/diffcmd.c create mode 100644 extracters/ods2/dircmd.c create mode 100644 extracters/ods2/dismountcmd.c create mode 100644 extracters/ods2/extendcmd.c create mode 100644 extracters/ods2/f11def.h create mode 100644 extracters/ods2/helpcmd.c create mode 100644 extracters/ods2/importcmd.c create mode 100644 extracters/ods2/initialcmd.c create mode 100644 extracters/ods2/initvol.c create mode 100644 extracters/ods2/initvol.h create mode 100644 extracters/ods2/makefile-vhd.nt create mode 100644 extracters/ods2/makefile-vhd.unix create mode 100644 extracters/ods2/makefile.depends create mode 100644 extracters/ods2/makefile.vms create mode 100644 extracters/ods2/mountcmd.c mode change 100644 => 100755 extracters/ods2/ods2_alpha.exe mode change 100644 => 100755 extracters/ods2/ods2_solaris_intel.exe mode change 100644 => 100755 extracters/ods2/ods2_solaris_sparc.exe mode change 100644 => 100755 extracters/ods2/ods2_tru64.exe mode change 100644 => 100755 extracters/ods2/ods2_vax_decc.exe mode change 100644 => 100755 extracters/ods2/ods2_vax_vaxc.exe mode change 100644 => 100755 extracters/ods2/ods2_win32.exe create mode 100644 extracters/ods2/phyvhd.c create mode 100644 extracters/ods2/phyvhd.h create mode 100644 extracters/ods2/searchcmd.c create mode 100644 extracters/ods2/setcmd.c create mode 100644 extracters/ods2/showcmd.c create mode 100644 extracters/ods2/spawncmd.c create mode 100644 extracters/ods2/typecmd.c mode change 100644 => 100755 putr/putr.com diff --git a/.gitignore b/.gitignore index 5b7dd43..510595b 100644 --- a/.gitignore +++ b/.gitignore @@ -248,3 +248,4 @@ _Pvt_Extensions .idea/ *.sln.iml +.gdb_history diff --git a/extracters/ods2/ODS2.exe b/extracters/ods2/ODS2.exe old mode 100644 new mode 100755 index c697cfc252a92be1058f9d2faa548849e875850b..2a1d8423914c30cc881459d1b4e8b9391fd84345 GIT binary patch literal 140288 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 z%0A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 literal 89600 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 - HOME_SKIP=1 ;HOME_LOG;USE_VLD;DEBUG_BUILD;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + USE_VLD;DEBUG_BUILD;USE_VHD;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreadedDebugDLL Level3 EditAndContinue Disabled + vhd\;%(AdditionalIncludeDirectories) MachineX86 true Console - Shlwapi.lib;%(AdditionalDependencies) + Rpcrt4.lib;Shlwapi.lib;%(AdditionalDependencies) @@ -80,10 +81,11 @@ - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + USE_VHD;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreadedDLL Level3 ProgramDatabase + vhd\;%(AdditionalIncludeDirectories) MachineX86 @@ -91,7 +93,7 @@ Console true true - Shlwapi.lib;%(AdditionalDependencies) + Rpcrt4.lib;Shlwapi.lib;%(AdditionalDependencies) @@ -108,31 +110,65 @@ + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extracters/ods2/ODS2.vcxproj.filters b/extracters/ods2/ODS2.vcxproj.filters index 66da791..00f22da 100644 --- a/extracters/ods2/ODS2.vcxproj.filters +++ b/extracters/ods2/ODS2.vcxproj.filters @@ -51,6 +51,69 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -101,6 +164,39 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + diff --git a/extracters/ods2/access.c b/extracters/ods2/access.c index 7491681..01b0dc5 100644 --- a/extracters/ods2/access.c +++ b/extracters/ods2/access.c @@ -1,4 +1,4 @@ -/* Access.c V2.1 */ +/* Access.c */ /* This is part of ODS2 written by Paul Nankervis, @@ -17,6 +17,14 @@ 'RMS' routines. */ +#if !defined( DEBUG ) && defined( DEBUG_ACCESS ) +#define DEBUG DEBUG_ACCCESS +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + #include #include #include @@ -26,20 +34,15 @@ #include "ssdef.h" #include "access.h" #include "device.h" +#include "initvol.h" #include "ods2.h" -#include "phyio.h" #include "phyvirt.h" #include "stsdef.h" #include "compat.h" #include "sysmsg.h" #include "vmstime.h" -#ifndef TRUE -#define TRUE ( 0 == 0 ) -#endif -#ifndef FALSE -#define FALSE ( 0 != 0 ) -#endif +extern int mountdef( const char *devnam ); struct VCB *vcb_list = NULL; @@ -51,15 +54,6 @@ struct WCBKEY { struct WCB *prevwcb; }; -#if 0 -static unsigned int delta_from_name( const char *diskname ); -#endif -static unsigned int delta_from_index( size_t index ); -static unsigned int compute_delta( unsigned long sectorsize, - unsigned long sectors, - unsigned long tracks, - unsigned long cylinders ); - unsigned deallocfile(struct FCB *fcb); /* Update.c */ #define DEBUGx @@ -67,7 +61,7 @@ unsigned deallocfile(struct FCB *fcb); /* Update.c */ /* checksum() to produce header checksum values... */ -vmsword checksumn( vmsword *block, int count ) { +f11word checksumn( f11word *block, int count ) { register unsigned result; register unsigned short *ptr; register unsigned data; @@ -76,12 +70,12 @@ vmsword checksumn( vmsword *block, int count ) { result = 0; for ( ; count > 0; --count ) { data = *ptr++; - result += VMSWORD(data); + result += F11WORD(data); } - return (vmsword)(result & 0xFFFF); + return (f11word)(result & 0xFFFF); } -vmsword checksum( vmsword *block ) { +f11word checksum( f11word *block ) { return checksumn( block, 255 ); } @@ -91,8 +85,8 @@ vmsword checksum( vmsword *block ) { void fid_copy(struct fiddef *dst,struct fiddef *src,unsigned rvn) { - dst->fid$w_num = VMSWORD(src->fid$w_num); - dst->fid$w_seq = VMSWORD(src->fid$w_seq); + dst->fid$w_num = F11WORD(src->fid$w_num); + dst->fid$w_seq = F11WORD(src->fid$w_seq); dst->fid$b_rvn = src->fid$b_rvn == 0 ? rvn : src->fid$b_rvn; dst->fid$b_nmx = src->fid$b_nmx; } @@ -103,11 +97,11 @@ void fid_copy(struct fiddef *dst,struct fiddef *src,unsigned rvn) unsigned deaccesshead( struct VIOC *vioc, struct HEAD *head, unsigned idxblk ) { - vmsword check; + f11word check; if ( head && idxblk ) { - check = checksum( (vmsword *) head ); - head->fh2$w_checksum = VMSWORD(check); + check = checksum( (f11word *) head ); + head->fh2$w_checksum = F11WORD(check); } return deaccesschunk( vioc, idxblk, 1, TRUE ); } @@ -125,15 +119,19 @@ unsigned accesshead(struct VCB *vcb,struct fiddef *fid,unsigned seg_num, register unsigned idxblk; vcbdev = RVN_TO_DEV(vcb,fid->fid$b_rvn); if (vcbdev == NULL) return SS$_DEVNOTMOUNT; - if (wrtflg && ((vcb->status & VCB_WRITE) == 0)) return SS$_WRITLCK; + if( wrtflg && !(vcb->status & VCB_WRITE) ) + return SS$_WRITLCK; idxblk = fid->fid$w_num + (fid->fid$b_nmx << 16) - 1 + - VMSWORD(vcbdev->home.hm2$w_ibmapvbn) + - VMSWORD(vcbdev->home.hm2$w_ibmapsize); + F11WORD(vcbdev->home.hm2$w_ibmapvbn) + + F11WORD(vcbdev->home.hm2$w_ibmapsize); if (vcbdev->idxfcb->head != NULL) { if (idxblk >= - VMSSWAP(vcbdev->idxfcb->head->fh2$w_recattr.fat$l_efblk)) { - printf("Not in index file\n"); + F11SWAP(vcbdev->idxfcb->head->fh2$w_recattr.fat$l_efblk)) { +#if DEBUG + printf("%u,%u,%u, %u Not in index file\n", + fid->fid$w_num,fid->fid$w_seq,fid->fid$b_rvn,fid->fid$b_nmx); +#endif return SS$_NOSUCHFILE; } } @@ -144,9 +142,9 @@ unsigned accesshead(struct VCB *vcb,struct fiddef *fid,unsigned seg_num, if (retidxblk) { *retidxblk = wrtflg ? idxblk : 0; } - if (VMSWORD(head->fh2$w_fid.fid$w_num) != fid->fid$w_num || + if (F11WORD(head->fh2$w_fid.fid$w_num) != fid->fid$w_num || head->fh2$w_fid.fid$b_nmx != fid->fid$b_nmx || - VMSWORD(head->fh2$w_fid.fid$w_seq) != fid->fid$w_seq || + F11WORD(head->fh2$w_fid.fid$w_seq) != fid->fid$w_seq || (head->fh2$w_fid.fid$b_rvn != fid->fid$b_rvn && head->fh2$w_fid.fid$b_rvn != 0)) { /* lib$signal(SS$_NOSUCHFILE); */ @@ -158,18 +156,18 @@ unsigned accesshead(struct VCB *vcb,struct fiddef *fid,unsigned seg_num, head->fh2$b_acoffset > head->fh2$b_rsoffset || head->fh2$b_map_inuse > head->fh2$b_acoffset - head->fh2$b_mpoffset || - checksum( (vmsword *) head ) != - VMSWORD( head->fh2$w_checksum ) ) { -#ifdef DEBUG + checksum( (f11word *) head ) != + F11WORD( head->fh2$w_checksum ) ) { +#if DEBUG printf( "--->accesshead(): File header checksum failure:" ); printf( " FH2$W_CHECKSUM=%u, checksum()=%u\n", - VMSWORD( head->fh2$w_checksum ), - checksum( (vmsword *) head ) + F11WORD( head->fh2$w_checksum ), + checksum( (f11word *) head ) ); #endif sts = SS$_DATACHECK; } else { - if (VMSWORD(head->fh2$w_seg_num) != seg_num) { + if (F11WORD(head->fh2$w_seg_num) != seg_num) { sts = SS$_FILESEQCHK; } } @@ -230,24 +228,24 @@ struct HEAD *premap_indexf(struct FCB *fcb,unsigned *retsts) if (head == NULL) { *retsts = SS$_INSFMEM; } else { - *retsts = phyio_read( vcbdev->dev, - VMSLONG( vcbdev->home.hm2$l_ibmaplbn ) + - VMSWORD( vcbdev->home.hm2$w_ibmapsize ), + *retsts = virt_read( vcbdev->dev, + F11LONG( vcbdev->home.hm2$l_ibmaplbn ) + + F11WORD( vcbdev->home.hm2$w_ibmapsize ), sizeof( struct HEAD ), (char *) head ); if (!(*retsts & STS$M_SUCCESS)) { free(head); head = NULL; } else { - if (VMSWORD(head->fh2$w_fid.fid$w_num) != 1 || + if (F11WORD(head->fh2$w_fid.fid$w_num) != 1 || head->fh2$w_fid.fid$b_nmx != 0 || - VMSWORD(head->fh2$w_fid.fid$w_seq) != 1 || - VMSWORD(head->fh2$w_checksum) != - checksum( (vmsword *) head ) ) { -#ifdef DEBUG + F11WORD(head->fh2$w_fid.fid$w_seq) != 1 || + F11WORD(head->fh2$w_checksum) != + checksum( (f11word *) head ) ) { +#if DEBUG printf( "--->premap_indexf(): Index file header checksum" ); printf( " failure: FH2$W_CHECKSUM=%u, checksum()=%u\n", - VMSWORD( head->fh2$w_checksum ), - checksum( (vmsword *) head ) + F11WORD( head->fh2$w_checksum ), + checksum( (f11word *) head ) ); #endif *retsts = SS$_DATACHECK; @@ -315,28 +313,28 @@ static void *wcb_create( unsigned hashval, void *keyval, unsigned *retsts ) { while (mp < me) { register unsigned phylen,phyblk; register unsigned short mp0; - switch ((mp0 = VMSWORD(*mp)) >> 14) { + switch ((mp0 = F11WORD(*mp)) >> 14) { case 0: phylen = 0; mp++; break; case 1: phylen = (mp0 & 0x00ff) + 1; - phyblk = ((mp0 & 0x3f00) << 8) | VMSWORD(mp[1]); + phyblk = ((mp0 & 0x3f00) << 8) | F11WORD(mp[1]); mp += 2; break; case 2: phylen = (mp0 & 0x3fff) + 1; - phyblk = (VMSWORD(mp[2]) << 16) | VMSWORD(mp[1]); + phyblk = (F11WORD(mp[2]) << 16) | F11WORD(mp[1]); mp += 3; break; case 3: - phylen = ((mp0 & 0x3fff) << 16) + VMSWORD(mp[1]) + 1; - phyblk = (VMSWORD(mp[3]) << 16) | VMSWORD(mp[2]); + phylen = ((mp0 & 0x3fff) << 16) + F11WORD(mp[1]) + 1; + phyblk = (F11WORD(mp[3]) << 16) | F11WORD(mp[2]); mp += 4; break; default: - printf( "Unknown type %x\n", (VMSWORD(*mp)>>14) ); + printf( "Unknown type %x\n", (F11WORD(*mp)>>14) ); abort(); } curvbn += phylen; @@ -355,7 +353,7 @@ static void *wcb_create( unsigned hashval, void *keyval, unsigned *retsts ) { } } if (extents >= EXTMAX || - (VMSWORD(head->fh2$w_ext_fid.fid$w_num) == 0 && + (F11WORD(head->fh2$w_ext_fid.fid$w_num) == 0 && head->fh2$w_ext_fid.fid$b_nmx == 0)) { break; } else { @@ -376,7 +374,7 @@ static void *wcb_create( unsigned hashval, void *keyval, unsigned *retsts ) { *retsts = SS$_NORMAL; if (curvbn <= wcbkey->vbn) { free(wcb); -#ifdef DEBUG +#if DEBUG printf( "--->wcb_create(): curvbn (=%u) <= wcbkey->vbn (=%u)\n", curvbn, wcbkey->vbn ); #endif @@ -398,7 +396,7 @@ unsigned getwindow(struct FCB * fcb,unsigned vbn,struct VCBDEV **devptr, unsigned sts; struct WCB *wcb; struct WCBKEY wcbkey; -#ifdef DEBUG +#if DEBUG printf("Accessing window for vbn %d, file (%x)\n",vbn,fcb->cache.hashval); #endif wcbkey.vbn = vbn; @@ -418,7 +416,7 @@ unsigned getwindow(struct FCB * fcb,unsigned vbn,struct VCBDEV **devptr, *phylen = wcb->phylen[extent] - togo; if (hdrfid != NULL) memcpy(hdrfid,&wcb->hd_fid,sizeof(struct fiddef)); if (hdrseq != NULL) *hdrseq = wcb->hd_seg_num; -#ifdef DEBUG +#if DEBUG printf("Mapping vbn %d to %d (%d -> %d)[%d] file (%x)\n", vbn,*phyblk,wcb->loblk,wcb->hiblk,wcb->hd_basevbn, fcb->cache.hashval); @@ -447,19 +445,21 @@ void *vioc_manager(struct CACHE * cacheobj,int flushonly) register unsigned curvbn = vioc->cache.hashval + 1; register char *address = (char *) vioc->data; register unsigned modmask = vioc->modmask; +#if DEBUG printf("\nvioc_manager writing vbn %d\n",curvbn); +#endif do { register unsigned sts; unsigned int wrtlen = 0; unsigned phyblk,phylen; struct VCBDEV *vcbdev; - while (length > 0 && (1 & modmask) == 0) { + while (length > 0 && !(1 & modmask) ) { length--; curvbn++; address += 512; modmask = modmask >> 1; } - while (wrtlen < length && (1 & modmask) != 0) { + while (wrtlen < length && (1 & modmask) ) { wrtlen++; modmask = modmask >> 1; } @@ -476,7 +476,7 @@ void *vioc_manager(struct CACHE * cacheobj,int flushonly) curvbn + phylen > fcb->highwater) { phylen = fcb->highwater - curvbn; } - sts = phyio_write( vcbdev->dev, phyblk, phylen * 512, address ); + sts = virt_write( vcbdev->dev, phyblk, phylen * 512, address ); if (!(sts & STS$M_SUCCESS)) return NULL; wrtlen -= phylen; curvbn += phylen; @@ -496,7 +496,7 @@ void *vioc_manager(struct CACHE * cacheobj,int flushonly) unsigned deaccesschunk(struct VIOC *vioc,unsigned wrtvbn, int wrtblks,int reuse) { -#ifdef DEBUG +#if DEBUG printf("Deaccess chunk %8x\n",vioc->cache.hashval); #endif if (wrtvbn) { @@ -550,7 +550,7 @@ static void *vioc_create( unsigned hashval, void *keyval, unsigned *retsts ) { if (fcb->highwater != 0 && curvbn + phylen > fcb->highwater) { phylen = fcb->highwater - curvbn; } - sts = phyio_read( vcbdev->dev, phyblk, phylen * 512, + sts = virt_read( vcbdev->dev, phyblk, phylen * 512, address); } if (!(sts & STS$M_SUCCESS)) { @@ -578,13 +578,14 @@ unsigned accesschunk(struct FCB *fcb,unsigned vbn,struct VIOC **retvioc, unsigned sts; register unsigned int blocks; register struct VIOC *vioc; -#ifdef DEBUG +#if DEBUG printf("Access chunk %d (%x)\n",vbn,fcb->cache.hashval); #endif if (vbn < 1 || vbn > fcb->hiblock) return SS$_ENDOFFILE; blocks = (vbn - 1) / VIOC_CHUNKSIZE * VIOC_CHUNKSIZE; if (wrtblks) { - if (!(fcb->status & FCB_WRITE)) return SS$_WRITLCK; + if( !(fcb->status & FCB_WRITE) ) + return SS$_WRITLCK; if (vbn + wrtblks > blocks + VIOC_CHUNKSIZE + 1) { return SS$_BADPARAM; } @@ -618,7 +619,7 @@ unsigned accesschunk(struct FCB *fcb,unsigned vbn,struct VIOC **retvioc, unsigned deaccessfile(struct FCB *fcb) { -#ifdef DEBUG +#if DEBUG printf("Deaccessing file (%x) reference %d\n",fcb->cache.hashval, fcb->cache.refcount); #endif @@ -627,17 +628,19 @@ unsigned deaccessfile(struct FCB *fcb) refcount = cache_refcount((struct CACHE *) fcb->wcb) + cache_refcount((struct CACHE *) fcb->vioc); if (refcount != 0) { +#if DEBUG printf("File reference counts non-zero %d (%d)\n",refcount, fcb->cache.hashval); -#ifdef DEBUG +#endif +#if DEBUG printf("File reference counts non-zero %d %d\n", cache_refcount((struct CACHE *) fcb->wcb), cache_refcount((struct CACHE *) fcb->vioc)); #endif return SS$_BUGCHECK; } - if (fcb->status & FCB_WRITE) { - if (VMSLONG(fcb->head->fh2$l_filechar) & FH2$M_MARKDEL) { + if( fcb->status & FCB_WRITE ) { + if (F11LONG(fcb->head->fh2$l_filechar) & FH2$M_MARKDEL) { return deallocfile(fcb); } } @@ -704,12 +707,13 @@ unsigned accessfile(struct VCB * vcb,struct fiddef * fid,struct FCB **fcbadd, unsigned sts; register struct FCB *fcb; register unsigned filenum = (fid->fid$b_nmx << 16) + fid->fid$w_num; -#ifdef DEBUG +#if DEBUG printf("Accessing file (%d,%d,%d)\n",(fid->fid$b_nmx << 16) + fid->fid$w_num,fid->fid$w_seq,fid->fid$b_rvn); #endif if (filenum < 1) return SS$_BADPARAM; - if (wrtflg && ((vcb->status & VCB_WRITE) == 0)) return SS$_WRITLCK; + if( wrtflg && !(vcb->status & VCB_WRITE) ) + return SS$_WRITLCK; if (fid->fid$b_rvn > 1) filenum |= fid->fid$b_rvn << 24; fcb = cache_find((void *) &vcb->fcb,filenum,NULL,&sts,NULL,fcb_create); if (fcb == NULL) return sts; @@ -721,7 +725,7 @@ unsigned accessfile(struct VCB * vcb,struct fiddef * fid,struct FCB **fcbadd, fcb->vcb = vcb; } if (wrtflg) { - if (fcb->headvioc != NULL && (fcb->status & FCB_WRITE) == 0) { + if( fcb->headvioc != NULL && !(fcb->status & FCB_WRITE) ) { deaccesshead(fcb->headvioc,NULL,0); fcb->headvioc = NULL; } @@ -732,14 +736,16 @@ unsigned accessfile(struct VCB * vcb,struct fiddef * fid,struct FCB **fcbadd, sts = accesshead(vcb,fid,0,&fcb->headvioc,&fcb->head,&fcb->headvbn, wrtflg); if (sts & STS$M_SUCCESS) { - fcb->hiblock = VMSSWAP(fcb->head->fh2$w_recattr.fat$l_hiblk); + fcb->hiblock = F11SWAP(fcb->head->fh2$w_recattr.fat$l_hiblk); if (fcb->head->fh2$b_idoffset > 39) { - fcb->highwater = VMSLONG(fcb->head->fh2$l_highwater); + fcb->highwater = F11LONG(fcb->head->fh2$l_highwater); } else { fcb->highwater = 0; } } else { +#if DEBUG printf("Accessfile status %d\n",sts); +#endif fcb->cache.objmanager = NULL; cache_untouch(&fcb->cache,FALSE); cache_delete(&fcb->cache); @@ -759,8 +765,9 @@ unsigned dismount(struct VCB * vcb) struct VCBDEV *vcbdev; int expectfiles = vcb->devices; int openfiles = cache_refcount(&vcb->fcb->cache); - if (vcb->status & VCB_WRITE) expectfiles *= 2; -#ifdef DEBUG + if( vcb->status & VCB_WRITE ) + expectfiles *= 2; +#if DEBUG printf("Dismounting disk %d\n",openfiles); #endif sts = SS$_NORMAL; @@ -769,14 +776,14 @@ unsigned dismount(struct VCB * vcb) } else { vcbdev = vcb->vcbdev; for (device = 0; device < vcb->devices; device++) { -#ifdef DEBUG +#if DEBUG printf( "--->dismount(): vcbdev[%d] = \"%s\"\n", device, vcbdev->dev != NULL ? vcbdev->dev->devnam : "NULL" ); #endif if (vcbdev->dev != NULL) { if (vcb->status & VCB_WRITE && vcbdev->mapfcb != NULL) { sts = deaccessfile(vcbdev->mapfcb); -#ifdef DEBUG +#if DEBUG printf( "--->dismount(): %d = deaccessfile()\n", sts ); #endif if (!(sts & STS$M_SUCCESS)) break; @@ -784,25 +791,25 @@ unsigned dismount(struct VCB * vcb) vcbdev->mapfcb = NULL; } cache_remove(&vcb->fcb->cache); -#ifdef DEBUG +#if DEBUG printf( "--->dismount(): cache_remove()\n" ); #endif sts = deaccesshead(vcbdev->idxfcb->headvioc, vcbdev->idxfcb->head, vcbdev->idxfcb->headvbn); -#ifdef DEBUG +#if DEBUG printf( "--->dismount(): %d = deaccesshead()\n", sts ); #endif if (!(sts & STS$M_SUCCESS)) break; vcbdev->idxfcb->headvioc = NULL; cache_untouch(&vcbdev->idxfcb->cache,FALSE); -#ifdef DEBUG +#if DEBUG printf( "--->dismount(): cache_untouch()\n" ); #endif vcbdev->dev->vcb = NULL; - phyio_done( vcbdev->dev ); -#ifdef DEBUG - printf( "--->dismount(): phyio_done()\n" ); + virt_close( vcbdev->dev ); +#if DEBUG + printf( "--->dismount(): virt_close()\n" ); #endif } vcbdev++; @@ -811,12 +818,12 @@ unsigned dismount(struct VCB * vcb) struct VCB *lp; cache_remove(&vcb->fcb->cache); -#ifdef DEBUG +#if DEBUG printf( "--->dismount(): cache_remove()\n" ); #endif while (vcb->dircache) { cache_delete((struct CACHE *) vcb->dircache); -#ifdef DEBUG +#if DEBUG printf( "--->dismount(): cache_delete()\n" ); #endif } @@ -834,7 +841,7 @@ unsigned dismount(struct VCB * vcb) /******************************************************************** mount() */ -#ifdef DEBUG +#if DEBUG #ifndef HOME_SKIP #define HOME_SKIP 1 #endif @@ -855,18 +862,15 @@ unsigned dismount(struct VCB * vcb) unsigned mount( unsigned flags, unsigned devices, char *devnam[], - char *label[], - struct VCB **retvcb ) { + char *label[] ) { register unsigned device,sts = 0; struct VCB *vcb; struct VCBDEV *vcbdev; struct VOLSETREC *volsetSYS = NULL; -#ifdef DEBUG +#if DEBUG if (sizeof(struct HOME) != 512 || sizeof(struct HEAD) != 512) return SS$_NOTINSTALL; #endif - if( retvcb ) - *retvcb = NULL; vcb = (struct VCB *) calloc( 1, sizeof(struct VCB) + ((devices - 1) * sizeof(struct VCBDEV)) ); @@ -897,29 +901,10 @@ unsigned mount( unsigned flags, sts = SS$_BADPARAM; break; } - if( flags & MOU_VIRTUAL ) { - sts = virt_device( dname, &dname ); - if( !(sts & STS$M_SUCCESS) ) break; - } else { - if( virt_lookup( dname ) != NULL ) { - char *p; - p = strchr( dname, ':' ); - if( p != NULL ) *p = '\0'; - printf( "%%ODS2-E-VIRTDEV, %s is a virtual device\n", dname ); - sts = SS$_DEVMOUNT; - break; - } - } + sts = virt_open( &dname, flags, &vcbdev->dev ); + if( !(sts & STS$M_SUCCESS) ) + break; vcb->devices++; - sts = device_lookup( strlen( dname ), dname, TRUE, - &vcbdev->dev ); - if( !(sts & STS$M_SUCCESS) ) - break; - vcbdev->dev->access = flags; /* Requested mount options */ - /* (e.g., /Write) */ - sts = phyio_init( vcbdev->dev ); - if( !(sts & STS$M_SUCCESS) ) - break; if (vcbdev->dev->vcb != NULL) { sts = SS$_DEVMOUNT; break; @@ -933,7 +918,7 @@ unsigned mount( unsigned flags, if( homtry < HOME_SKIP ) continue; #endif - sts = phyio_read( vcbdev->dev, hba, sizeof( struct HOME ), + sts = virt_read( vcbdev->dev, hba, sizeof( struct HOME ), (char *) &vcbdev->home ); if (!(sts & STS$M_SUCCESS)) break; hom = &vcbdev->home; @@ -941,70 +926,73 @@ unsigned mount( unsigned flags, printf( "--->mount(%u): LBA=%u, HM2$L_HOMELBN=%u, " "HM2$L_ALHOMELBN=%u, " "HM2$T_FORMAT=\"%12.12s\", memcmp()=%u\n", - homtry+1,hba, VMSLONG( hom->hm2$l_homelbn ), - VMSLONG( hom->hm2$l_alhomelbn ), + homtry+1,hba, F11LONG( hom->hm2$l_homelbn ), + F11LONG( hom->hm2$l_alhomelbn ), hom->hm2$t_format, memcmp( hom->hm2$t_format, "DECFILE11B ", 12 ) ); #endif - if( (hba == VMSLONG(hom->hm2$l_homelbn)) && - (VMSLONG(hom->hm2$l_alhomelbn) != 0) && - (VMSLONG(hom->hm2$l_altidxlbn) != 0) && - (VMSWORD(hom->hm2$w_homevbn) != 0) && - (VMSLONG(hom->hm2$l_ibmaplbn) != 0) && - (VMSWORD(hom->hm2$w_ibmapsize) != 0) && - (VMSWORD(hom->hm2$w_resfiles) >= 5) && - (VMSWORD(hom->hm2$w_checksum1) == - checksumn( (vmsword *)hom, + if( (hba == F11LONG(hom->hm2$l_homelbn)) && + (F11LONG(hom->hm2$l_alhomelbn) != 0) && + (F11LONG(hom->hm2$l_altidxlbn) != 0) && + (F11WORD(hom->hm2$w_homevbn) != 0) && + (F11LONG(hom->hm2$l_ibmaplbn) != 0) && + (F11WORD(hom->hm2$w_ibmapsize) != 0) && + (F11WORD(hom->hm2$w_resfiles) >= 5) && + (F11WORD(hom->hm2$w_checksum1) == + checksumn( (f11word *)hom, (offsetof( struct HOME, hm2$w_checksum1 )/2) )) && - (VMSWORD(hom->hm2$w_checksum2) == - checksum( (vmsword *)hom )) && + (F11WORD(hom->hm2$w_checksum2) == + checksum( (f11word *)hom )) && (memcmp(hom->hm2$t_format,"DECFILE11B ",12) == 0) ) { break; } #if defined( DEBUG ) || defined( HOME_LOG ) printf( "--->mount(): Home block validation failure\n" ); - printf( "(VMSLONG(hom->hm2$l_alhomelbn) != 0) %u\n", (VMSLONG(hom->hm2$l_alhomelbn) != 0) ); - printf( "(VMSLONG(hom->hm2$l_altidxlbn) != 0) %u\n", (VMSLONG(hom->hm2$l_altidxlbn) != 0) ); - printf( "(VMSWORD(hom->hm2$w_homevbn) != 0) %u\n", (VMSWORD(hom->hm2$w_homevbn) != 0)); - printf( "(VMSLONG(hom->hm2$l_ibmaplbn) != 0) %u\n", (VMSLONG(hom->hm2$l_ibmaplbn) != 0) ); - printf( "(VMSWORD(hom->hm2$w_ibmapsize) != 0) %u\n", (VMSWORD(hom->hm2$w_ibmapsize) != 0)); - printf( "(VMSWORD(hom->hm2$w_resfiles) >= 5) %u\n", (VMSWORD(hom->hm2$w_resfiles) >= 5)); - printf( "(VMSWORD(hom->hm2$w_checksum1) = %u %u\n", VMSWORD(hom->hm2$w_checksum1), - checksumn( (vmsword *)hom, + printf( "(F11LONG(hom->hm2$l_alhomelbn) != 0) %u\n", (F11LONG(hom->hm2$l_alhomelbn) != 0) ); + printf( "(F11LONG(hom->hm2$l_altidxlbn) != 0) %u\n", (F11LONG(hom->hm2$l_altidxlbn) != 0) ); + printf( "(F11WORD(hom->hm2$w_homevbn) != 0) %u\n", (F11WORD(hom->hm2$w_homevbn) != 0)); + printf( "(F11LONG(hom->hm2$l_ibmaplbn) != 0) %u\n", (F11LONG(hom->hm2$l_ibmaplbn) != 0) ); + printf( "(F11WORD(hom->hm2$w_ibmapsize) != 0) %u\n", (F11WORD(hom->hm2$w_ibmapsize) != 0)); + printf( "(F11WORD(hom->hm2$w_resfiles) >= 5) %u\n", (F11WORD(hom->hm2$w_resfiles) >= 5)); + printf( "(F11WORD(hom->hm2$w_checksum1) = %u %u\n", F11WORD(hom->hm2$w_checksum1), + checksumn( (f11word *)hom, (offsetof( struct HOME, hm2$w_checksum1 )/2) ) ); - printf( "(VMSWORD(hom->hm2$w_checksum2) = %u %u\n", VMSWORD(hom->hm2$w_checksum2), - checksum( (vmsword *)hom )); + printf( "(F11WORD(hom->hm2$w_checksum2) = %u %u\n", F11WORD(hom->hm2$w_checksum2), + checksum( (f11word *)hom )); #endif sts = SS$_ENDOFFILE; } - if( !(sts & 1) ) break; + if( !(sts & STS$M_SUCCESS) ) + break; if( label[device] != NULL ) { int i; char lbl[12+1]; /* Pad CLI-supplied label to match ODS */ - snprintf( lbl, sizeof(lbl), "%-12s", label[device] ); + (void) snprintf( lbl, sizeof(lbl), "%-12s", label[device] ); for( i = 0; i < 12; i++ ) { - if( toupper(lbl[i]) != vcbdev->home.hm2$t_volname[i] ) { + if( toupper( lbl[i] ) != toupper( vcbdev->home.hm2$t_volname[i] ) ) { printf( "%%ODS2-W-WRONGVOL, Device %s contains volume '%12.12s', '%s' expected\n", dname, vcbdev->home.hm2$t_volname, lbl ); sts = SS$_ITEMNOTFOUND; break; } } - if (!(sts & STS$M_SUCCESS)) break; - if (flags & MOU_WRITE && !(vcbdev->dev->access & MOU_WRITE)) { - printf("%%MOUNT-W-WRITELOCK, %s is write locked\n", - dname); - vcb->status &= ~VCB_WRITE; + if( !(sts & STS$M_SUCCESS) ) + break; + if (flags & MOU_WRITE && !(vcbdev->dev->access & MOU_WRITE)) { + printf("%%MOUNT-W-WRITELOCK, %s is write locked\n", + dname); + vcb->status &= ~VCB_WRITE; + } } - } - if( (VMSWORD(vcbdev->home.hm2$w_rvn) != device + 1) && - !(VMSWORD(vcbdev->home.hm2$w_rvn) == 0 && device == 0) ) { + if( (F11WORD(vcbdev->home.hm2$w_rvn) != device + 1) && + !(F11WORD(vcbdev->home.hm2$w_rvn) == 0 && device == 0) ) { printf( "%%ODS2-E-WRONGVOL, Device %s contains RVN %u, RVN %u expected\n", - dname, VMSWORD(vcbdev->home.hm2$w_rvn), device+1 ); + dname, F11WORD(vcbdev->home.hm2$w_rvn), device+1 ); sts = SS$_UNSUPVOLSET; } - if (!(sts & 1)) break; + if( !(sts & STS$M_SUCCESS) ) + break; } /* for(each device) */ } if (sts & STS$M_SUCCESS) { @@ -1024,11 +1012,12 @@ unsigned mount( unsigned flags, idxfid.fid$b_rvn = device + 1; sts = accessfile( vcb, &idxfid, &vcbdev->idxfcb, (vcb->status & VCB_WRITE) != 0 ); if (!(sts & STS$M_SUCCESS)) { - vcbdev->dev = NULL; /*** DECREF ***/ + virt_close( vcbdev->dev ); + vcbdev->dev = NULL; continue; } vcbdev->dev->vcb = vcb; - if( VMSWORD(vcbdev->home.hm2$w_rvn) != 0 ) { + if( F11WORD(vcbdev->home.hm2$w_rvn) != 0 ) { if( device == 0 ) { struct fiddef vsfid = {6,6,1,0}; struct FCB *vsfcb = NULL; @@ -1037,7 +1026,7 @@ unsigned mount( unsigned flags, int rec; unsigned int vbn = 1; struct VOLSETREC *bufp; - int setcount = VMSWORD(vcbdev->home.hm2$w_setcount); + int setcount = F11WORD(vcbdev->home.hm2$w_setcount); if( setcount != (int)devices ) { printf( "%%ODS2-E-VOLCOUNT, Volume set %12.12s has %u members, but %u specified\n", @@ -1048,7 +1037,7 @@ unsigned mount( unsigned flags, /* Read VOLSET.SYS */ volsetSYS = (struct VOLSETREC *)malloc( (1+setcount) * sizeof( struct VOLSETREC ) ); sts = accessfile( vcb, &vsfid, &vsfcb, 0 ); - if( (sts & 1) == 0 ) { + if( !(sts & STS$M_SUCCESS) ) { printf( "%%ODS2-E-NOVOLSET, Unable to access VOLSET.SYS: %s\n", getmsg(sts, MSG_TEXT) ); break; } @@ -1056,7 +1045,8 @@ unsigned mount( unsigned flags, if( recs == 0 ) { if( vbn != 1 ) deaccesschunk(vioc,0,0,0); sts = accesschunk(vsfcb,vbn, &vioc,(char **)&bufp, &recs, 0); - if( (sts & 1) == 0 ) break; + if( !(sts & STS$M_SUCCESS) ) + break; vbn += recs; recs *= 512 / sizeof( struct VOLSETREC ); } @@ -1065,9 +1055,9 @@ unsigned mount( unsigned flags, deaccesschunk(vioc,0,0,0); { int st2; st2 = deaccessfile(vsfcb); - if( sts & 1 ) sts = st2; + if( sts & STS$M_SUCCESS ) sts = st2; } - if( (sts & 1) == 0 ) { + if( !(sts & STS$M_SUCCESS) ) { printf( "%%ODS2-E-VOLIOERR, Error reading VOLSET.SYS: %s\n", getmsg(sts, MSG_TEXT) ); break; } @@ -1102,97 +1092,103 @@ unsigned mount( unsigned flags, } /* rvn != 0 */ if( vcb->status & VCB_WRITE ) { - struct fiddef mapfid = {2,2,0,0}; + struct fiddef mapfid = { 2, 2, 0, 0 }; mapfid.fid$b_rvn = device + 1; - sts = accessfile(vcb,&mapfid,&vcbdev->mapfcb,TRUE); - if (sts & STS$M_SUCCESS) { - struct VIOC *vioc; - struct SCB *scb; - sts = accesschunk(vcbdev->mapfcb,1,&vioc, - (char **) &scb,NULL,0); - if (sts & STS$M_SUCCESS) { - if (scb->scb$w_cluster == - vcbdev->home.hm2$w_cluster) { + sts = accessfile( vcb,&mapfid, &vcbdev->mapfcb, TRUE ); + if( sts & STS$M_SUCCESS ) { + struct VIOC *vioc; + struct SCB *scb; + +/* TODO *** scb->scb$w_checksum == checksum( (f11word *)scb */ + sts = accesschunk( vcbdev->mapfcb, 1, &vioc, + (char **) &scb, NULL ,0 ); + if( sts & STS$M_SUCCESS ) { + if( scb->scb$w_cluster == + vcbdev->home.hm2$w_cluster ) { vcbdev->clustersize = - vcbdev->home.hm2$w_cluster; + F11WORD( vcbdev->home.hm2$w_cluster ); vcbdev->max_cluster = - (scb->scb$l_volsize + - scb->scb$w_cluster - 1) / - scb->scb$w_cluster; + (F11LONG(scb->scb$l_volsize) + + F11WORD(scb->scb$w_cluster) - 1) / + F11WORD(scb->scb$w_cluster); deaccesschunk(vioc,0,0,FALSE); sts = update_freecount( - vcbdev,&vcbdev->free_clusters - ); -#ifdef DEBUG - printf( "%d of %d blocks are free on %12.12s\n", - vcbdev->free_clusters * vcbdev->clustersize, scb->scb$l_volsize, + vcbdev,&vcbdev->free_clusters + ); +#if DEBUG + printf( "%d of %d blocks are free on %12.12s\n", + vcbdev->free_clusters * vcbdev->clustersize, scb->scb$l_volsize, vcbdev->home.hm2$t_volname ); #endif - } + } else { + deaccesschunk(vioc,0,0,FALSE); + } } - } else { - printf( "%%ODS2-E-NOBITMAP, Unable to access BITMAP.SYS: %s\n", getmsg(sts, MSG_TEXT) ); + } else { + printf( "%%ODS2-E-NOBITMAP, Unable to access BITMAP.SYS: %s\n", getmsg(sts, MSG_TEXT) ); vcbdev->mapfcb = NULL; break; } } - if( (sts & 1) && (flags & MOU_LOG) ) { - printf("%%MOUNT-I-MOUNTED, Volume %12.12s mounted on %s\n", - vcbdev->home.hm2$t_volname, vcbdev->dev->devnam); + if( (sts & STS$M_SUCCESS) && (flags & MOU_LOG) ) { + printf( "%%MOUNT-I-MOUNTED, Volume %12.12s mounted on %s\n", + vcbdev->home.hm2$t_volname, vcbdev->dev->devnam ); } } /* device len */ } /* for( each device ) */ - if( !(sts & 1) ) { + if( !(sts & STS$M_SUCCESS) ) { vcbdev = vcb->vcbdev; for( device = 0; device < devices; device++, vcbdev++ ) { if (vcbdev->dev == NULL) { - if( flags & MOU_VIRTUAL ) - virt_device( devnam[device], NULL ); continue; } if( vcb->status & VCB_WRITE && vcbdev->mapfcb != NULL ) { /* sts = */ deaccessfile(vcbdev->mapfcb); - /* if( !(sts & 1) ) ??; */ + /* if( !(sts & STS$M_SUCCESS) ) ??; */ vcbdev->idxfcb->status &= ~FCB_WRITE; vcbdev->mapfcb = NULL; } cache_remove( &vcb->fcb->cache ); /* sts = */ deaccesshead(vcbdev->idxfcb->headvioc,vcbdev->idxfcb->head,vcbdev->idxfcb->headvbn); - /* if (!(sts & 1)) ??; */ + /* if (!(sts & STS$M_SUCCESS)) ??; */ vcbdev->idxfcb->headvioc = NULL; cache_untouch(&vcbdev->idxfcb->cache,0); vcbdev->dev->vcb = NULL; - /* ?? vcbdev->dev = NULL *//* **DECREF **/ - sts = phyio_done( vcbdev->dev ); + sts = virt_close( vcbdev->dev ); } cache_remove( &vcb->fcb->cache ); while( vcb->dircache ) cache_delete( (struct CACHE *) vcb->dircache ); } - } else { /* *** DECREF *** */ + } else { vcbdev = vcb->vcbdev; for( device = 0; device < vcb->devices; device++, vcbdev++ ) { if (vcbdev->dev == NULL) { - if( flags & MOU_VIRTUAL ) - virt_device( devnam[device], NULL ); continue; } - phyio_done( vcbdev->dev ); + virt_close( vcbdev->dev ); } free(vcb); vcb = NULL; } - if( (sts & 1) && (flags & MOU_LOG) && VMSWORD(vcb->vcbdev[0].home.hm2$w_rvn) != 0 ) { + if( (sts & STS$M_SUCCESS) && (flags & MOU_LOG) && F11WORD(vcb->vcbdev[0].home.hm2$w_rvn) != 0 ) { printf ( "%%MOUNT-I-MOUNTEDVS, Volume set %12.12s mounted\n", vcb->vcbdev[0].home.hm2$t_strucname ); } if( vcb != NULL ) { - vcb->next = vcb_list; - vcb_list = vcb; + struct VCB *lp; + + for( lp = (struct VCB *)&vcb_list; lp->next != NULL; lp = lp->next ) + ; + lp->next = vcb; + vcb->next = NULL; } if( volsetSYS != NULL ) free( volsetSYS ); - if (retvcb != NULL) *retvcb = vcb; + + if( sts & STS$M_SUCCESS ) + (void) mountdef( vcb->vcbdev[0].dev->devnam ); + return sts; } @@ -1208,7 +1204,7 @@ static void print_volhdr( int volset, size_t devwid ) { else printf( " " ); printf( -"%-*s Volume Lvl Clust Owner/CreateDate VolProt/Default FileProt\n", +"%-*s Volume/User Lvl Clust Owner/CreateDate VolProt/Default FileProt\n", (int)devwid, "Dev" ); if( volset ) printf( " --- " ); @@ -1224,7 +1220,7 @@ static void print_volhdr( int volset, size_t devwid ) { } /********************************************************** print_prot() */ -static void print_prot( vmsword code, int vol ) { +static void print_prot( f11word code, int vol ) { static const char grp[4] = { "SOGW" }; static const char acc[2][4] = {{"RWED"}, {"RWCD"}}; int g, b, w; @@ -1253,11 +1249,42 @@ static void print_volinf( struct VCB *vcb, unsigned device, size_t wrap ) { struct VCBDEV *vcbdev; + struct SCB *scb = NULL; + struct VIOC *vioc = NULL; + unsigned sts; size_t n; - vmsword timbuf[7]; + f11word timbuf[7]; vcbdev = vcb->vcbdev+device; + if( !(vcb->status & VCB_WRITE) ) { + struct fiddef mapfid = { 2, 2, 0, 0 }; + + if( !(( sts = accessfile( vcb, &mapfid, &vcbdev->mapfcb, 0 )) & STS$M_SUCCESS) ) { + printf( " *Can't access BITMAP.SYS %s\n", getmsg( sts, MSG_TEXT ) ); + return; + } + } + if( (sts = accesschunk( vcbdev->mapfcb, 1, &vioc, (char **)&scb, NULL, 0 )) & STS$M_SUCCESS ) { + if( scb->scb$w_cluster == vcbdev->home.hm2$w_cluster ) { + vcbdev->clustersize = F11WORD(vcbdev->home.hm2$w_cluster); + vcbdev->max_cluster = (F11LONG(scb->scb$l_volsize) + + F11WORD(scb->scb$w_cluster) - 1) / + F11WORD(scb->scb$w_cluster); + deaccesschunk(vioc,0,0,FALSE); + if( !(vcb->status & VCB_WRITE) ) + sts = update_freecount( vcbdev, &vcbdev->free_clusters ); + } else { + printf(" SCB data doesn't match HOM %u / %u ", + F11WORD(scb->scb$w_cluster), F11WORD(vcbdev->home.hm2$w_cluster) ); + deaccesschunk(vioc,0,0,FALSE); + } + } else { + printf( " *Can't read SCB %s", getmsg( sts, MSG_TEXT ) ); + } + if( !(vcb->status & VCB_WRITE) ) + deaccessfile( vcbdev->mapfcb ); + for( n = 0; n < strlen( vcbdev->dev->devnam ); n++ ) { if( vcbdev->dev->devnam[n] == ':' ) break; @@ -1268,18 +1295,53 @@ static void print_volinf( struct VCB *vcb, printf( "%12.12s", vcbdev->home.hm2$t_volname ); printf( " %u.%u %5u [%6o,%6o]", - VMSWORD( vcbdev->home.hm2$w_struclev ) >> 8, - VMSWORD( vcbdev->home.hm2$w_struclev ) & 0xFF, - VMSWORD( vcbdev->home.hm2$w_cluster ), - VMSWORD( vcbdev->home.hm2$w_volowner.uic$w_grp ), - VMSWORD( vcbdev->home.hm2$w_volowner.uic$w_mem ) + F11WORD( vcbdev->home.hm2$w_struclev ) >> 8, + F11WORD( vcbdev->home.hm2$w_struclev ) & 0xFF, + F11WORD( vcbdev->home.hm2$w_cluster ), + F11WORD( vcbdev->home.hm2$w_volowner.uic$w_grp ), + F11WORD( vcbdev->home.hm2$w_volowner.uic$w_mem ) ); printf( " " ); - print_prot( VMSWORD( vcbdev->home.hm2$w_protect ), 1 ); + print_prot( F11WORD( vcbdev->home.hm2$w_protect ), 1 ); if( !(vcb->status & VCB_WRITE) ) printf( " write-locked" ); - printf( "\n%*s ", (int)(wrap+devwid+23), "" ); + if( sts & STS$M_SUCCESS ) { + disktypep_t dp; + int first = 1; + + for( dp = disktype; dp->name != NULL; dp++ ) { + unsigned size; + unsigned blksize = 1; + + size = dp->sectors * dp->tracks * dp->cylinders - dp->reserved; + if( dp->sectorsize > 512 ) + size = size * dp->sectorsize / 512; + else { + if( dp->sectorsize < 512 ) { + blksize = (512 / dp->sectorsize); + size = size / blksize; + } + } + if( scb->scb$l_volsize == size && + scb->scb$l_blksize == blksize && + scb->scb$l_sectors == dp->sectors && + scb->scb$l_tracks == dp->tracks && + scb->scb$l_cylinders == dp->cylinders ) { + if( first ) { + printf( " type:" ); + first = 0; + } else + printf( "|" ); + printf( " %s", dp->name ); + } + } + if( first ) + printf( " Unrecognized type" ); + } + + printf( "\n%*s %12.12s ", (int)(wrap+devwid), "", + vcbdev->home.hm2$t_ownername ); if( sys$numtim( timbuf, vcbdev->home.hm2$q_credate ) & STS$M_SUCCESS ) { static const char *months = @@ -1291,7 +1353,16 @@ static void print_volinf( struct VCB *vcb, } else printf( "%*s", 41-23, "" ); - print_prot( VMSWORD( vcbdev->home.hm2$w_fileprot ), 0 ); + print_prot( F11WORD( vcbdev->home.hm2$w_fileprot ), 0 ); + + if( sts & STS$M_SUCCESS ) { + printf( " Free: %u/%u", vcbdev->free_clusters * vcbdev->clustersize, + scb->scb$l_volsize ); + printf( " Geo: %u/%u/%u", F11LONG(scb->scb$l_sectors), + F11LONG(scb->scb$l_tracks), F11LONG(scb->scb$l_cylinders) ); + if( F11LONG(scb->scb$l_blksize) != 1 ) + printf( " %u phys blks/LBN", F11LONG(scb->scb$l_blksize) ); + } putchar( '\n' ); } @@ -1330,7 +1401,7 @@ void show_volumes( void ) { unsigned device; vcbdev = vcb->vcbdev; - if( VMSWORD(vcbdev->home.hm2$w_rvn) == 0 ) + if( F11WORD(vcbdev->home.hm2$w_rvn) == 0 ) continue; nvol--; @@ -1339,7 +1410,7 @@ void show_volumes( void ) { print_volhdr( TRUE, maxd ); for( device = 0; device < vcb->devices; device++, vcbdev++ ) { - printf( " %3d ", VMSWORD(vcbdev->home.hm2$w_rvn) ); + printf( " %3d ", F11WORD(vcbdev->home.hm2$w_rvn) ); print_volinf( vcb, maxd, device, 8 ); } } @@ -1354,7 +1425,7 @@ void show_volumes( void ) { unsigned device; vcbdev = vcb->vcbdev; - if( VMSWORD(vcbdev->home.hm2$w_rvn) != 0 ) + if( F11WORD(vcbdev->home.hm2$w_rvn) != 0 ) continue; vcbdev = vcb->vcbdev; @@ -1377,187 +1448,8 @@ void access_rundown( void ) { sts = dismount( vcb ); if( !(sts & STS$M_SUCCESS) ) { - printf( "Dismount failed in rundown: %s", getmsg(sts, MSG_TEXT) ); + printf( "Dismount failed in rundown: %s\n", getmsg(sts, MSG_TEXT) ); } } } -/*************************************************************** compute_delta() */ -/* - * The search delta is computed from the - * volume geometry, expressed in sectors, tracks (surfaces), and - * cylinders, according to the following rules, to handle the cases where - * one or two dimensions of the volume have a size of 1. - * - * Geometry: Delta - * - * s x 1 x 1: 1 Rule 1 - * 1 x t x 1: 1 Rule 2 - * 1 x 1 x c: 1 Rule 3 - * - * s x t x 1: s+1 Rule 4 - * s x 1 x c: s+1 Rule 5 - * 1 x t x c: t+1 Rule 6 - * - * s x t x c: (t+1)*s+1 Rule 7 - */ - - -#define DISK( name, sectors, tracks, cylinders ) \ - {#name, 512 ## ul, sectors ## ul, tracks ## ul, cylinders ## ul}, -#define DISKS( name, sectorsize, sectors, tracks, cylinders ) \ - {#name, sectorsize ## ul, sectors ## ul, tracks ## ul, cylinders ## ul}, -struct disktype disktype[] = { - DISK(UNKNOWN, 1, 1, 1) /* First = short sequence delta = 1 */ - DISK(RK05, 12, 2, 203) - DISK(RK06, 22, 3, 411) - DISK(RK07, 22, 3, 815) - DISK(RK11, 12, 2, 203) - - DISK(RL01, 40, 2, 256) - DISK(RL02, 40, 2, 512) - - DISK(RM02, 32, 5, 823) - DISK(RM03, 32, 5, 823) - DISK(RP04, 22, 19, 411) - DISK(RP05, 22, 19, 411) - DISK(RM80, 31, 14, 559) - DISK(RP06, 22, 19, 815) - DISK(RM05, 32, 19, 823) - DISK(RP07, 50, 32, 630) - -#if 0 /* Not useful now as RSX20-F used ODS-1 */ - DISKS(RM02-T, 576, 30, 5, 823) - DISKS(RM03-T, 576, 30, 5, 823) - DISKS(RP04-T, 576, 20, 19, 411) - DISKS(RP05-T, 576, 20, 19, 411) - DISKS(RM80-T, 576, 30, 14, 559) - DISKS(RP06-T, 576, 20, 19, 815) - DISKS(RM05-T, 576, 30, 19, 823) - DISKS(RP07-T, 576, 43, 32, 630) -#endif - - DISK(RX50, 10, 1, 80) - DISK(RX33, 15, 2, 80) - -#if 0 - DISK(RD50, 99, 99, 9999) -#endif - DISK(RD51, 18, 4, 306) - DISK(RD31, 17, 4, 615) - DISK(RD52, 17, 8, 512) - DISK(RD53, 17, 7, 1024) - DISK(RD54, 17, 15, 1225) - - DISK(RA72, 51, 20, 1921) - -#if 0 - DISK(RA80, 99, 99, 9999) - -#endif - DISK(RA81, 51, 14, 1258) - DISK(RA82, 57, 15, 1435) - - DISK(RA90, 69, 13, 2649) - DISK(RA92, 73, 13, 3099) - - DISK(RRD40,128, 1, 10400) - DISK(RRD50,128, 1, 10400) - DISKS(RX01, 128, 26, 1, 77) - DISKS(RX02, 256, 26, 1, 77) - -#if 0 - DISK(RX23-SD, 99, 99, 9999) - DISK(RX23-DD, 99, 99, 9999) - - DISK(RX33-SD, 10, 1, 80) - DISK(RX33-DD, 99, 99, 9999) -#endif - - DISK(RX50, 10, 1, 80) -#if 0 - DISK(RC25, 99, 99, 9999 ) - - DISK(RF30, 99, 99, 9999 ) - DISK(RF31, 99, 99, 9999 ) - DISK(RF35, 99, 99, 9999 ) - DISK(RF36, 99, 99, 9999 ) - DISK(RF71, 99, 99, 9999 ) - DISK(RF72, 99, 99, 9999 ) - DISK(RF73, 99, 99, 9999 ) - DISK(RF74, 99, 99, 9999 ) - - DISK(RZ22, 99, 99, 9999 ) - DISK(RZ23, 99, 99, 9999 ) - DISK(RZ24, 99, 99, 9999 ) - DISK(RZ25, 99, 99, 9999 ) - DISK(RZ26, 99, 99, 9999 ) - DISK(RZ27, 99, 99, 9999 ) - DISK(RZ28, 99, 99, 9999 ) - DISK(RZ29, 99, 99, 9999 ) - DISK(RZ31, 99, 99, 9999 ) - DISK(RZ33, 99, 99, 9999 ) - DISK(RZ35, 99, 99, 9999 ) - DISK(RZ55, 99, 99, 9999 ) - DISK(RZ56, 99, 99, 9999 ) - DISK(RZ57, 99, 99, 9999 ) - DISK(RZ58, 99, 99, 9999 ) - DISK(RZ59, 99, 99, 9999 ) - - DISK(RZ72, 99, 99, 9999 ) - DISK(RZ73, 99, 99, 9999 ) - DISK(RZ74, 99, 99, 9999 ) -#endif - - { NULL, 0, 0, 0, 0 } -}; - - -static unsigned int compute_delta( unsigned long sectorsize, - unsigned long sectors, - unsigned long tracks, - unsigned long cylinders ) { - - if( sectorsize < 512 ) - sectors = (sectorsize * sectors) / 512; - - if( sectors > 1 && tracks > 1 && cylinders > 1 ) /* Rule 7 */ - return (tracks + 1) * sectors +1; - - if( (sectors > 1 && tracks > 1 && cylinders == 1 ) || /* Rule 4 */ - (sectors > 1 && tracks == 1 && cylinders > 1 ) ) /* Rule 5 */ - return sectors + 1; - - if( sectors == 1 && tracks > 1 && cylinders > 1 ) /* Rule 6 */ - return tracks + 1; - - return 1; /* Rules 1-3 */ -} - -#if 0 -static unsigned int delta_from_name( const char *diskname ) { - struct disktype *dp; - - for( dp = disktype; dp->name != NULL; dp++ ) { - if( !strcmp( dp->name, diskname ) ) - return compute_delta( dp->sectorsize, dp->sectors, dp->tracks, dp->cylinders ); - } - - return ~0u; -} -#endif -static unsigned int delta_from_index( size_t index ) { - struct disktype *dp; - unsigned int delta; - - if( index >= sizeof(disktype)/sizeof(disktype[0]) ) - abort(); - dp = disktype + index; - delta = compute_delta( dp->sectorsize, dp->sectors, dp->tracks, dp->cylinders ); - -#if defined( DEBUG ) || HOME_SKIP > 0 - printf( "HOM search index for %s is %u\n", dp->name, delta ); -#endif - - return delta; -} diff --git a/extracters/ods2/access.h b/extracters/ods2/access.h index 2ef9f46..b8cc266 100644 --- a/extracters/ods2/access.h +++ b/extracters/ods2/access.h @@ -1,4 +1,4 @@ -/* Access.h V2.1 Definitions for file access routines */ +/* Access.h Definitions for file access routines */ /* This is part of ODS2 written by Paul Nankervis, @@ -14,180 +14,9 @@ #define _ACCESS_H #include "cache.h" +#include "f11def.h" #include "vmstime.h" -#ifdef ODS2_BIG_ENDIAN -#define VMSLONG( l ) ( ( (l) & 0x000000ff ) << 24 | \ - ( (l) & 0x0000ff00 ) << 8 | \ - ( (l) & 0x00ff0000 ) >> 8 | \ - (l) >> 24 ) -#define VMSWORD( w ) ( ( (w) & 0x00ff ) << 8 | \ - (w) >> 8 ) -#define VMSSWAP( l ) ( ( (l) & 0x00ff0000 ) << 8 | \ - ( (l) & 0xff000000 ) >> 8 | \ - ( (l) & 0x000000ff ) << 8 | \ - ( (l) & 0x0000ff00 ) >> 8 ) -#else -#define VMSLONG( l ) (l) -#define VMSWORD( w ) (w) -#define VMSSWAP( l ) ( ( (l) & 0x0000ffff ) << 16 | \ - (l) >> 16 ) -#endif - -typedef unsigned char vmsbyte; -typedef unsigned short vmsword; -typedef unsigned int vmsswap; -typedef unsigned int vmslong; - -#define FH2$M_NOBACKUP 0x00002 -#define FH2$M_CONTIGB 0x00020 -#define FH2$M_CONTIG 0x00080 -#define FH2$M_DIRECTORY 0x02000 -#define FH2$M_MARKDEL 0x08000 -#define FH2$M_ERASE 0x20000 - -#ifdef __ALPHA -#pragma member_alignment save -#pragma nomember_alignment -#endif - - -struct UIC { - vmsword uic$w_mem; - vmsword uic$w_grp; -}; - -struct fiddef { - vmsword fid$w_num; - vmsword fid$w_seq; - vmsbyte fid$b_rvn; - vmsbyte fid$b_nmx; -}; - -struct RECATTR { - vmsbyte fat$b_rtype; - vmsbyte fat$b_rattrib; - vmsword fat$w_rsize; - vmsswap fat$l_hiblk; - vmsswap fat$l_efblk; - vmsword fat$w_ffbyte; - vmsbyte fat$b_bktsize; - vmsbyte fat$b_vfcsize; - vmsword fat$w_maxrec; - vmsword fat$w_defext; - vmsword fat$w_gbc; - vmsbyte fat$_UU0[8]; - vmsword fat$w_versions; -}; - -struct HOME { - vmslong hm2$l_homelbn; - vmslong hm2$l_alhomelbn; - vmslong hm2$l_altidxlbn; - vmsword hm2$w_struclev; - vmsword hm2$w_cluster; - vmsword hm2$w_homevbn; - vmsword hm2$w_alhomevbn; - vmsword hm2$w_altidxvbn; - vmsword hm2$w_ibmapvbn; - vmslong hm2$l_ibmaplbn; - vmslong hm2$l_maxfiles; - vmsword hm2$w_ibmapsize; - vmsword hm2$w_resfiles; - vmsword hm2$w_devtype; - vmsword hm2$w_rvn; - vmsword hm2$w_setcount; - vmsword hm2$w_volchar; - struct UIC hm2$w_volowner; - vmslong hm2$l_reserved1; - vmsword hm2$w_protect; - vmsword hm2$w_fileprot; - vmsword hm2$w_reserved2; - vmsword hm2$w_checksum1; - VMSTIME hm2$q_credate; - vmsbyte hm2$b_window; - vmsbyte hm2$b_lru_lim; - vmsword hm2$w_extend; - VMSTIME hm2$q_retainmin; - VMSTIME hm2$q_retainmax; - VMSTIME hm2$q_revdate; - vmsbyte hm2$r_min_class[20]; - vmsbyte hm2$r_max_class[20]; - vmsbyte hm2$t_reserved3[320]; - vmslong hm2$l_serialnum; - char hm2$t_strucname[12]; - char hm2$t_volname[12]; - char hm2$t_ownername[12]; - char hm2$t_format[12]; - vmsword hm2$w_reserved4; - vmsword hm2$w_checksum2; -}; - -struct IDENT { - char fi2$t_filename[20]; - vmsword fi2$w_revision; - VMSTIME fi2$q_credate; - VMSTIME fi2$q_revdate; - VMSTIME fi2$q_expdate; - VMSTIME fi2$q_bakdate; - char fi2$t_filenamext[66]; -}; - -struct HEAD { - vmsbyte fh2$b_idoffset; - vmsbyte fh2$b_mpoffset; - vmsbyte fh2$b_acoffset; - vmsbyte fh2$b_rsoffset; - vmsword fh2$w_seg_num; - vmsword fh2$w_struclev; - struct fiddef fh2$w_fid; - struct fiddef fh2$w_ext_fid; - struct RECATTR fh2$w_recattr; - vmslong fh2$l_filechar; - vmsword fh2$w_reserved1; - vmsbyte fh2$b_map_inuse; - vmsbyte fh2$b_acc_mode; - struct UIC fh2$l_fileowner; - vmsword fh2$w_fileprot; - struct fiddef fh2$w_backlink; - vmsbyte fh2$b_journal; - vmsbyte fh2$b_ru_active; - vmsword fh2$w_reserved2; - vmslong fh2$l_highwater; - vmsbyte fh2$b_reserved3[8]; - vmsbyte fh2$r_class_prot[20]; - vmsbyte fh2$r_restofit[402]; - vmsword fh2$w_checksum; -}; - -struct SCB { - vmsword scb$w_struclev; - vmsword scb$w_cluster; - vmslong scb$l_volsize; - vmslong scb$l_blksize; - vmslong scb$l_sectors; - vmslong scb$l_tracks; - vmslong scb$l_cylinders; - vmslong scb$l_status; - vmslong scb$l_status2; - vmsword scb$w_writecnt; - char scb$t_volockname[12]; - VMSTIME scb$q_mounttime; - vmsword scb$w_backrev; - vmslong scb$q_genernum[2]; - char scb$b_reserved[446]; - vmsword scb$w_checksum; -}; - -struct VOLSETREC { - char vsr$t_label[12]; - char vsr$b_reserved[52]; -}; - -#ifdef __ALPHA -#pragma member_alignment restore -#endif - #define EXTMAX 20 struct WCB { @@ -256,13 +85,6 @@ struct VCB { extern struct VCB *vcb_list; void show_volumes( void ); -struct disktype { - const char *name; - unsigned long sectorsize, sectors, tracks, cylinders; -}; - -extern struct disktype disktype[]; - /* RVN_TO_DEV( vcb, rvn ) - returns device from relative volume number */ /* returns NULL if RVN illegal or device not mounted */ @@ -278,8 +100,7 @@ extern struct disktype disktype[]; void fid_copy(struct fiddef *dst,struct fiddef *src,unsigned rvn); unsigned dismount(struct VCB *vcb); -unsigned mount(unsigned flags,unsigned devices,char *devnam[],char *label[], - struct VCB **vcb); +unsigned mount(unsigned flags,unsigned devices,char *devnam[],char *label[] ); unsigned accesserase(struct VCB *vcb,struct fiddef *fid); unsigned deaccessfile(struct FCB *fcb); @@ -294,7 +115,8 @@ unsigned update_freecount(struct VCBDEV *vcbdev,unsigned *retcount); unsigned update_create(struct VCB *vcb,struct fiddef *did,char *filename, struct fiddef *fid,struct FCB **fcb); unsigned update_extend(struct FCB *fcb,unsigned blocks,unsigned contig); -vmsword checksum( vmsword *block ); +f11word checksumn( f11word *block, int count ); +f11word checksum( f11word *block ); void access_rundown( void ); diff --git a/extracters/ods2/build.com b/extracters/ods2/build.com old mode 100644 new mode 100755 diff --git a/extracters/ods2/cache.c b/extracters/ods2/cache.c index 4983dcd..111e78f 100644 --- a/extracters/ods2/cache.c +++ b/extracters/ods2/cache.c @@ -1,4 +1,4 @@ -/* Cache.c V2.1 Caching control routines */ +/* Cache.c Caching control routines */ /* This is part of ODS2 written by Paul Nankervis, @@ -46,24 +46,26 @@ about ODS2 objects or structures.... */ +#if !defined( DEBUG ) && defined( DEBUG_CACHE ) +#define DEBUG DEBUG_CACHE +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +/* *** TEMP TODO ***/ +#undef DEBUG +#define DEBUG 1 + #include #include #include "cache.h" +#include "ods2.h" #include "ssdef.h" -#ifndef TRUE -#define TRUE ( 0 == 0 ) -#endif -#ifndef FALSE -#define FALSE ( 0 != 0 ) -#endif - -#ifndef DEBUG -#define DEBUG -#endif - #define IMBALANCE 5 /* Tree imbalance limit */ #define CACHELIM 256 /* Free object limit */ #define CACHEGOAL 128 /* Free object goal */ @@ -80,6 +82,8 @@ static int cachedeleteing = FALSE; /* Cache deletion in progress... */ struct CACHE lrulist = {&lrulist,&lrulist,NULL,NULL,NULL,NULL,0,0,1,0}; +static void cache_deleter( struct CACHE *cacheobj, struct CACHE **actualobj ); + /*************************************************************** cache_show() */ /* cache_show() - to print cache statistics */ @@ -118,26 +122,40 @@ int cache_refcount(struct CACHE *cacheobj) /* cache_delete() - blow away item from cache - allow item to select a proxy (!) and adjust 'the tree' containing the item. */ -struct CACHE *cache_delete(struct CACHE *cacheobj) +void cache_delete( struct CACHE *cacheobj ) { + cache_deleter( cacheobj, NULL ); +} + +/* Internal routine that does the work. Optionally returns the address of the + * object actually deleted, which allows pointers to be adjusted correctly. + */ +static void cache_deleter( struct CACHE *cacheobj, struct CACHE **actualobj ) { while (cacheobj->objmanager != NULL) { register struct CACHE *proxyobj; cachedeleteing = TRUE; proxyobj = (*cacheobj->objmanager) (cacheobj,FALSE); cachedeleteing = FALSE; - if (proxyobj == NULL) return NULL; + if( proxyobj == NULL ) { + if( actualobj != NULL ) + *actualobj = NULL; + return; + } if (proxyobj == cacheobj) break; cacheobj = proxyobj; } -#ifdef DEBUG +#if DEBUG if (cachedeleteing) printf("CACHE deletion while delete in progress\n"); #endif if (cacheobj->refcount != 0) { -#ifdef DEBUG +#if DEBUG printf("CACHE attempt to delete referenced object %d:%d\n", cacheobj->objtype,cacheobj->hashval); #endif - return NULL; + abort(); + if( actualobj != NULL ) + *actualobj = NULL; + return; } cacheobj->lastlru->nextlru = cacheobj->nextlru; cacheobj->nextlru->lastlru = cacheobj->lastlru; @@ -173,7 +191,7 @@ struct CACHE *cache_delete(struct CACHE *cacheobj) cachecount--; cachefreecount--; cachedeletes++; -#ifdef DEBUG +#if DEBUG cacheobj->nextlru = NULL; cacheobj->lastlru = NULL; cacheobj->left = NULL; @@ -184,22 +202,25 @@ struct CACHE *cache_delete(struct CACHE *cacheobj) cacheobj->balance = 0; cacheobj->refcount = 0; #endif - free(cacheobj); - return cacheobj; + if( actualobj != NULL ) /* The returned value is used as a flag. It will not be dereferenced. */ + *actualobj = cacheobj; + + free(cacheobj); /* This may be the supplied object, or it may have been replaced by a proxy. */ + return; } /************************************************************** cache_purge() */ /* cache_purge() - trim size of free list */ -void cache_purge(void) +void cache_purge(int all) { if (!cachedeleteing) { register struct CACHE *cacheobj = lrulist.lastlru; cachepurges++; - while (cachefreecount > CACHEGOAL && cacheobj != &lrulist) { + while ( (all || cachefreecount > CACHEGOAL) && cacheobj != &lrulist) { register struct CACHE *lastobj = cacheobj->lastlru; -#ifdef DEBUG +#if DEBUG if (cacheobj->lastlru->nextlru != cacheobj || cacheobj->nextlru->lastlru != cacheobj || *(cacheobj->parent) != cacheobj) { @@ -207,7 +228,9 @@ void cache_purge(void) } #endif if (cacheobj->refcount == 0) { - if (cache_delete(cacheobj) != lastobj) cacheobj = lastobj; + struct CACHE *actualobj; + cache_deleter( cacheobj, &actualobj ); + if (actualobj != lastobj) cacheobj = lastobj; } else { cacheobj = lastobj; } @@ -242,7 +265,7 @@ void cache_remove(struct CACHE *cacheobj) if (cacheobj->refcount == 0) { struct CACHE *delobj; do { - delobj = cache_delete(cacheobj); + cache_deleter(cacheobj, &delobj); } while (delobj != NULL && delobj != cacheobj); } } @@ -255,9 +278,10 @@ void cache_remove(struct CACHE *cacheobj) void cache_touch(struct CACHE *cacheobj) { if (cacheobj->refcount++ == 0) { -#ifdef DEBUG +#if DEBUG if (cacheobj->nextlru == NULL || cacheobj->lastlru == NULL) { printf("CACHE LRU pointers corrupt!\n"); + abort(); } #endif cacheobj->nextlru->lastlru = cacheobj->lastlru; @@ -276,8 +300,8 @@ void cache_untouch(struct CACHE *cacheobj,int recycle) { if (cacheobj->refcount > 0) { if (--cacheobj->refcount == 0) { - if (++cachefreecount >= CACHELIM) cache_purge(); -#ifdef DEBUG + if (++cachefreecount >= CACHELIM) cache_purge(FALSE); +#if DEBUG if (cacheobj->nextlru != NULL || cacheobj->lastlru != NULL) { printf("CACHE LRU pointers corrupt\n"); } @@ -294,10 +318,11 @@ void cache_untouch(struct CACHE *cacheobj,int recycle) lrulist.lastlru = cacheobj; } } -#ifdef DEBUG } else { +#if DEBUG printf("CACHE untouch limit exceeded\n"); #endif + abort(); } } @@ -325,7 +350,7 @@ void *cache_find( void **root, unsigned hashval, void *keyval, unsigned *retsts, cachefinds++; while ((cacheobj = *parent) != NULL) { register int cmp = hashval - cacheobj->hashval; -#ifdef DEBUG +#if DEBUG if (cacheobj->parent != parent) { printf("CACHE Parent pointer is corrupt\n"); } diff --git a/extracters/ods2/cache.h b/extracters/ods2/cache.h index a463917..59f0671 100644 --- a/extracters/ods2/cache.h +++ b/extracters/ods2/cache.h @@ -1,4 +1,4 @@ -/* Cache.h V2.1 Definitions for cache routines */ +/* Cache.h Definitions for cache routines */ /* This is part of ODS2 written by Paul Nankervis, @@ -38,8 +38,8 @@ struct CACHE { void cache_show(void); int cache_refcount(struct CACHE *cacheobj); -struct CACHE *cache_delete(struct CACHE *cacheobj); -void cache_purge(void); +void cache_delete(struct CACHE *cacheobj); +void cache_purge(int all); void cache_flush(void); void cache_remove(struct CACHE *cacheobj); void cache_touch(struct CACHE * cacheobj); diff --git a/extracters/ods2/cmddef.h b/extracters/ods2/cmddef.h new file mode 100644 index 0000000..f5887ae --- /dev/null +++ b/extracters/ods2/cmddef.h @@ -0,0 +1,108 @@ +/* Definitions for command handlers */ + +/* 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. + */ + + +#ifndef _CMDDEF_H +#define _CMDDEF_H + +#define _BSD_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +#include "compat.h" +#include "ods2.h" +#include "rms.h" +#include "ssdef.h" +#include "stsdef.h" +#include "sysmsg.h" + +#define MAXREC 32767 +#define PRINT_ATTR ( FAB$M_CR | FAB$M_PRN | FAB$M_FTN ) + +#define DT_NAME "drive_type" + +#define DECL_CMD(x) unsigned do ## x(int argc,char **argv,int qualc,char **qualv) + +typedef struct CMDSET CMDSET_t; +typedef struct param param_t; +typedef struct qual qual_t; + +typedef CMDSET_t *CMDSETp_t; +typedef param_t *paramp_t; +typedef qual_t *qualp_t; + +/* Command table entry */ + +struct CMDSET { + char *name; + unsigned (*proc) (int argc,char *argv[],int qualc,char *qualv[]); + int uniq; + qualp_t validquals; + paramp_t params; + char *helpstr; +}; + +/* 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 +#define CV(list) KEYCOL, list +#define DV(val) DECVAL, val +#define SV(val) STRVAL, ((void *)val) +struct qual { + const char *name; + int set; + int clear; + enum qualtype { NOVAL, KEYVAL, KEYCOL, DECVAL, STRVAL } qtype; + void *arg; + const char *helpstr; +}; + +typedef const char *(hlpfunc_t)( CMDSET_t *cmd, param_t *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; +}; + +extern int vms_qual; +extern int verify_cmd; + +int parselist( char ***items, size_t min, char *arg, const char *label ); +int checkquals( int result, qual_t qualset[],int qualc,char *qualv[] ); +int keycomp(const char *param, const char *keywrd); +char *fgetline( FILE *stream, int keepnl ); + +int prvmstime(VMSTIME vtime, const char *sfx); +void pwrap( int *pos, const char *fmt, ... ); + +#endif diff --git a/extracters/ods2/compat.c b/extracters/ods2/compat.c index e4bd3ed..2fefec5 100644 --- a/extracters/ods2/compat.c +++ b/extracters/ods2/compat.c @@ -1,24 +1,60 @@ -/* Timothe Litt February 2016 */ +/* Timothe Litt March 2016 + * Copyright (C) 2016 Timothe litt + * litt at acm dot org + * + * Free for use with the ODS2 package. All other rights reserved. + */ + +/* + * This is distributed as part of ODS2, originally 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. + */ /* This module contains compatibility code, currently just * to support Microsoft Windows. * * Microsoft deprecates sprintf, but doesn't supply standard * replacement until very recent IDEs. - * Microsoft doesn't like fopen, or strerror, or getcwd. + * Microsoft doesn't like fopen, or strerror, or getcwd, or getenv. * One needs to use a M$ call to translate system errors. * * Finding out about drive letter assignments is unique to windows. */ +#if !defined( DEBUG ) && defined( DEBUG_COMPAT ) +#define DEBUG DEBUG_COMPAT +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + #include #include #include +#include +#include #include "compat.h" +#include "descrip.h" +#include "stsdef.h" +#ifdef VMS +#include +#else #ifdef _WIN32 #include -#include +#else +#include +#include +#ifndef USER_FROM_ENV +#include +#endif +#endif #endif #if defined(_MSC_VER) && _MSC_VER < 1900 @@ -74,7 +110,7 @@ const char *ods2_strerror( int errn ) { static char buf[256]; if( strerror_s( buf, sizeof( buf ), errn ) != 0 ) - snprintf( buf, sizeof( buf ), "Untranslatable error %u", errn ); + (void) snprintf( buf, sizeof( buf ), "Untranslatable error %u", errn ); return buf; } @@ -93,7 +129,11 @@ TCHAR *w32_errstr( DWORD eno, ... ) { FORMAT_MESSAGE_FROM_SYSTEM, NULL, eno, 0, (LPSTR)&msg, 1, &ap ) == 0 ) { msg = (TCHAR *)malloc( 32 ); - snprintf( msg, 32, "(%u)", eno ); + if( msg == NULL ) { + perror( "malloc" ); + printf( "Original error number (%u)\n", eno ); + } else + (void) snprintf( msg, 32, "(%u)", eno ); } va_end(ap); return msg; @@ -128,5 +168,86 @@ char *driveFromLetter( const char *letter ) { } #endif -/* For ISO C compliance, ensure that there's something in this module */ -void dummy_compat ( void ) { return; } +/******************************************************************* get_env() */ + +char *get_env( const char *name ) { + size_t i; + char *r; + +#ifdef _WIN32 + (void) getenv_s( &i, NULL, 0, name ); + if( i == 0 ) + return NULL; + if ((r = malloc( i )) == NULL ) + return NULL; + (void)getenv_s( &i, r, i, "USERNAME" ); + return r; +#else + char *t; + + t = getenv( name ); + if( t == NULL ) + return NULL; + i = strlen( t ); + r = malloc( i + 1 ); + if( r == NULL ) + return NULL; + + memcpy( r, t, i+1 ); + + return r; +#endif +} + +/******************************************************************* get_username() */ + +char *get_username( void ) { + char *username; + char *r; + size_t i; +#ifdef VMS + char uame[12 + 1] = "UNKNOWN "; + struct dsc$descriptor_s userdsc = { sizeof( uname ) - 1, DSC$K_DTYPE_T, DSC$K_CLASS_S, uname }; + + r = "UNKNOWN"; + if( lib$getjpi( &JPI$_USERNAME, 0, 0, 0, &userdsc, 0 ) & STS$M_SUCCESS ) { + for( i = sizeof( uname ) - 1; i >= 0; i-- ) { + if( uname[i] == ' ' ) + uname[i] = '\0'; + else + break; + } + if( uname[0] ) + r = uname; + } +#else +#ifdef _WIN32 + (void) getenv_s( &i, NULL, 0, "USERNAME" ); + if( i == 0 ) + r = "UNKNOWN"; + else { + if ((r = malloc( i )) == NULL ) + return NULL; + (void)getenv_s( &i, r, i, "USERNAME" ); + return r; + } +#else +#ifdef USER_FROM_ENV + if( (r = getenv( "USER" )) == NULL ) + r = getenv( "LOGNAME" ); + if( r == NULL ) + r = "UNKNOWN"; +#else + struct passwd *pw; + + pw = getpwuid(geteuid()); + r = pw->pw_name; +#endif +#endif +#endif + i = strlen( r ); + if( (username = malloc( i + 1 )) == NULL ) + return NULL; + memcpy( username, r, i + 1 ); + return username; +} diff --git a/extracters/ods2/compat.h b/extracters/ods2/compat.h index ae39b2b..9023bb3 100644 --- a/extracters/ods2/compat.h +++ b/extracters/ods2/compat.h @@ -1,3 +1,20 @@ +/* Timothe Litt March 2016 + * Copyright (C) 2016 Timothe litt + * litt at acm dot org + * + * Free for use with the ODS2 package. All other rights reserved. + */ + +/* + * This is distributed as part of ODS2, originally 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. + */ + #ifndef COMPAT_H #define COMPAT_H @@ -14,6 +31,7 @@ int c99_snprintf(char *outBuf, size_t size, const char *format, ...); #endif #ifdef _MSC_VER +#include FILE *openf( const char *filename, const char *mode ); #else #define openf fopen @@ -28,6 +46,14 @@ FILE *openf( const char *filename, const char *mode ); #define getcwd _getcwd #undef chdir #define chdir _chdir +#undef tzset +#define tzset _tzset +#undef setenv +#define setenv(name, value, overwrite) _putenv_s(name, value) +#undef unsetenv +#define unsetenv(name) _putenv_s(name,"") +#undef unlink +#define unlink _unlink #undef strerror #define strerror(n) ods2_strerror(n) @@ -40,6 +66,9 @@ char *driveFromLetter( const char *letter ); #include #endif +char *get_username( void ); +char *get_env( const char *name ); + #define UNUSED(x) (void)(x) #endif diff --git a/extracters/ods2/copycmd.c b/extracters/ods2/copycmd.c new file mode 100644 index 0000000..974956a --- /dev/null +++ b/extracters/ods2/copycmd.c @@ -0,0 +1,239 @@ + +/* 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. + */ + +#if !defined( DEBUG ) && defined( DEBUG_COPYCMD ) +#define DEBUG DEBUG_COPYCMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + + +/******************************************************************* docopy() */ + +/* copy: a file copy routine */ + +#define copy_binary OPT_GENERIC_1 +#define copy_log OPT_GENERIC_2 + +qual_t copyquals[] = { {"ascii", 0, copy_binary, NV, "Copy file in ascii mode (default)"}, + {"binary", copy_binary, 0, NV, "Copy file in binary mode", }, + {"log", copy_log, 0, NV, "-Log files copied"}, + {"nolog", 0, copy_log, NV, NULL}, + {NULL, 0, 0, NV, NULL} +}; + +param_t 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 } +}; + +DECL_CMD(copy) { + 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 & STS$M_SUCCESS ) { + nam.nam$l_rsa = rsa; + nam.nam$b_rss = NAM$C_MAXRSS; + fab.fab$l_fop = FAB$M_NAM; + + while( (sts = sys_search( &fab )) & STS$M_SUCCESS ) { + int directory = 0; + struct RAB rab = cc$rms_rab; + struct XABDAT dat = cc$rms_xabdat; + struct XABITM itm = cc$rms_xabitm; + struct item_list xitems[] = { + { XAB$_UCHAR_DIRECTORY, sizeof(int), NULL, 0 }, + { 0, 0, NULL, 0 } + }; + + itm.xab$l_nxt = &dat; + xitems[0].buffer = &directory; + itm.xab$b_mode = XAB$K_SENSEMODE; + itm.xab$l_itemlist = xitems; + fab.fab$l_xab = &itm; + + sts = sys_open(&fab); + fab.fab$l_xab = NULL; + + if ( !(sts & STS$M_SUCCESS) ) { + printf("%%COPY-F-OPENFAIL, Open error: %s\n",getmsg(sts, MSG_TEXT)); + perror("-COPY-F-ERR "); + continue; + } + if( directory ) { + rsa[nam.nam$b_rsl] = '\0'; + printf( "%%COPY-I-NOTDIR, %s is directory, not copied\n", rsa ); + sys_close( &fab ); + continue; + } + + rab.rab$l_fab = &fab; + if( (sts = sys_connect(&rab)) & STS_M_SUCCESS ) { + FILE *tof; + char name[NAM$C_MAXRSS + 1]; + unsigned records = 0; + char *out = name,*inp = argv[2]; /* unquote(argv[2]) */ + int dot = FALSE; + 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 = TRUE; + } else { + if (strchr(":]\\/",*inp)) dot = FALSE; + } + *out++ = *inp++; + } + } + *out++ = '\0'; +#ifndef _WIN32 + tof = openf( name,"w" ); +#else + if( !(options & copy_binary) && (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; + + if( (rec = malloc( MAXREC + 2 )) == NULL ) + sts = SS$_INSFMEM; + else { + filecount++; + rab.rab$l_ubf = rec; + rab.rab$w_usz = MAXREC; + while( (sts = sys_get( &rab )) & STS$M_SUCCESS ) { + unsigned rsz = rab.rab$w_rsz; + if( !(options & copy_binary) && + (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; + } + } + free( rec ); + rec = NULL; + } + if( fclose( tof ) ) { + printf( "%%COPY-F- fclose error!!\n" ); + perror( "-COPY-F-ERR " ); + } else { + time_t posixtime; + unsigned short timvec[7]; + char *tz; + struct tm tm; +#ifdef _WIN32 + struct _utimbuf tv; +#else + struct timeval tv[2]; +#endif + if( sys$numtim( timvec, dat.xab$q_rdt ) & STS$M_SUCCESS ) { + tm.tm_sec = timvec[5]; + tm.tm_min = timvec[4]; + tm.tm_hour = timvec[3]; + tm.tm_mday = timvec[2]; + tm.tm_mon = timvec[1] -1; + tm.tm_year = timvec[0] - 1900; + + tz = get_env( "TZ" ); + setenv( "TZ", "", 1 ); + tzset(); + posixtime = mktime( &tm ); + if( posixtime != (time_t)-1 ) { +#ifdef _WIN32 + tv.actime = + tv.modtime = posixtime; + (void)_utime( name, &tv ); +#else + tv[0].tv_sec = posixtime; + tv[0].tv_usec = timvec[6] * 10000; + tv[1] = tv[0]; /* Set mtime to atime */ + (void) utimes( name, tv ); +#endif + } + if( tz != NULL ) + setenv( "TZ", tz, 1 ); + else + unsetenv( "TZ" ); + tzset(); + free( tz ); + } + } + } + + sys_disconnect( &rab ); + rsa[nam.nam$b_rsl] = '\0'; + if (sts == RMS$_EOF) { + if( options & copy_log ) + 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, MSG_TEXT),rsa); + sts = SS$_NORMAL; + } + } + sys_close(&fab); + } + if (sts == RMS$_NMF) sts = SS$_NORMAL; + } + if ( sts & STS$M_SUCCESS ) { + if (filecount > 0) { + if( options & copy_log ) + printf("%%COPY-S-NEWFILES, %d file%s copied\n", + filecount,(filecount == 1 ? "" : "s")); + } else { + printf( "%%COPY-I-NOFILES, no files found\n" ); + } + } else { + printf("%%COPY-F-ERROR Status: %s\n",getmsg(sts, MSG_TEXT)); + } + return sts; +} diff --git a/extracters/ods2/createcmd.c b/extracters/ods2/createcmd.c new file mode 100644 index 0000000..9052183 --- /dev/null +++ b/extracters/ods2/createcmd.c @@ -0,0 +1,52 @@ +/* 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. + */ + +#if !defined( DEBUG ) && defined( DEBUG_CREATECMD ) +#define DEBUG DEBUG_CREATECMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + +#include "f11def.h" + +/******************************************************************* dotest() */ + +param_t createpars[] = { {"parameter", REQ, STRING, NOPA, "for create."}, + { NULL, 0, 0, NOPA, NULL } +}; + +#if 0 +extern struct VCB *test_vcb; +#endif + +/* docreate: you don't want to know! */ + +DECL_CMD(create) { + unsigned sts = 0; + struct fiddef fid; + + UNUSED(argc); + UNUSED(qualc); + UNUSED(qualv); + +#if 0 /* Needs to call RMS, etc */ + sts = update_create( test_vcb, NULL, "Test.File", &fid, NULL ); +#else + memset( &fid, 0, sizeof( struct fiddef ) ); +#endif + printf( "Create status of %d (%s)\n", sts, argv[1] ); + return sts; +} + + + diff --git a/extracters/ods2/debug.c b/extracters/ods2/debug.c new file mode 100644 index 0000000..5aa2f1f --- /dev/null +++ b/extracters/ods2/debug.c @@ -0,0 +1,122 @@ +/* Debug routines */ + +/* Timothe Litt March 2016 + * Copyright (C) 2016 Timothe litt + * litt at acm dot org + * + * Free for use with the ODS2 package. All other rights reserved. + */ + +/* + * This is distributed as part of ODS2, originally 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. + */ + + +#include +#include + +#include "debug.h" + +/*********************************************************** dumpblock() */ +void dumpblock( unsigned block, unsigned length, const char *buffer, + const char *more, ... ) { + va_list ap; + + va_start( ap, more ); + vdumpblockto( stdout, block, length, buffer, more, ap ); + va_end( ap ); +} + +/*********************************************************** vdumpblock() */ + +void vdumpblock( unsigned block, unsigned length, const char *buffer, + const char *more, va_list ap ) { + + vdumpblockto( stdout, block, length, buffer, more, ap ); +} + +/*********************************************************** dumpblockto() */ + +void dumpblockto( FILE *fp, unsigned block, unsigned length, const char *buffer, + const char *more, ... ) { + va_list ap; + + va_start( ap, more ); + vdumpblockto( fp, block, length, buffer, more, ap ); + va_end( ap ); +} + +/*********************************************************** vdumpblockto() */ + +void vdumpblockto( FILE *fp, unsigned block, unsigned length, const char *buffer, + const char *more, va_list ap ) { +#ifndef DEBUG_DISKIO + (void) fp; + (void) block; + (void) length; + (void) buffer; + (void) more; + (void) ap; +#else + const char *p; + + size_t n, wid = 32, off = 0; + + fprintf( fp, "\n****************************************\n" + "Block %u Length %u", block, length ); + if( more != NULL ) { + fputc( ' ', fp ); + vfprintf( fp, more, ap ); + } + fputc( '\n', fp ); + if( length == 0 ) + return; + + fprintf( fp, " " ); + for( n = 0; n < wid; n++ ) { + if( n % 2 == 0 ) + fprintf( fp, " %02x", (int)n ); + else + fprintf( fp, " " ); + } + fprintf( fp, " " ); + for( n = 0; n < wid; n++ ) { + if( n % 4 == 0 ) + fprintf( fp, " %02x ", (int)n ); + } + fputc( '\n', fp ); + + while( length ) { + fprintf( fp, "%04x:", (unsigned) off ); + p = buffer; + for( n = 0; n < wid; n++ ) { + if( n % 2 == 0 ) + fputc( ' ', fp ); + if( n < length ) { + fprintf( fp, "%02x", 0xff & (unsigned)*p++ ); + } else + fprintf( fp, " " ); + } + + fprintf( fp, " " ); + p = buffer; + for( n = 0; n < wid; n++, p++ ) { + if( n % 4 == 0 ) + fputc( ' ', fp ); + fprintf( fp, "%c", (n >= length)? ' ': (*p < 040 || *p > 0176)? '.': *p ); + } + length -= wid; + buffer += wid; + off += wid; + fputc( '\n', fp ); + } +#endif + + return; +} diff --git a/extracters/ods2/debug.h b/extracters/ods2/debug.h new file mode 100644 index 0000000..e14179c --- /dev/null +++ b/extracters/ods2/debug.h @@ -0,0 +1,15 @@ +#ifndef DEBUG_H +#define DEBUG_H + +#include +#include + +void dumpblock( unsigned block, unsigned length, const char *buffer, + const char *more, ... ); +void vdumpblock( unsigned block, unsigned length, const char *buffer, + const char *more, va_list ap ); +void dumpblockto( FILE *fp, unsigned block, unsigned length, const char *buffer, + const char *more, ... ); +void vdumpblockto( FILE *fp, unsigned block, unsigned length, const char *buffer, + const char *more, va_list ap ); +#endif diff --git a/extracters/ods2/deletecmd.c b/extracters/ods2/deletecmd.c new file mode 100644 index 0000000..9a2c61d --- /dev/null +++ b/extracters/ods2/deletecmd.c @@ -0,0 +1,85 @@ +/* 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. + */ + +#if !defined( DEBUG ) && defined( DEBUG_DELETECMD ) +#define DEBUG DEBUG_DELETECMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + +/***************************************************************** dodelete() */ + +#define delete_log OPT_GENERIC_1 + +qual_t delquals[] = { {"log", delete_log, 0, NV, "-List name of each file deleted. (Default)"}, + {"nolog", 0, delete_log, NV, NULL }, + { NULL, 0, 0, NV, NULL } +}; +param_t delpars[] = { {"filespec", REQ, VMSFS, NOPA, + "for files to be deleted from ODS-2 volume. Wildcards are permitted.."}, + { NULL, 0, 0, NOPA, NULL } +}; + +DECL_CMD(delete) { + int sts = 0; + char res[NAM$C_MAXRSS + 1], rsa[NAM$C_MAXRSS + 1]; + struct NAM nam = cc$rms_nam; + struct FAB fab = cc$rms_fab; + int options, filecount = 0; + + UNUSED(argc); + + options = checkquals( delete_log, 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 & STS$M_SUCCESS ) { + if (nam.nam$b_ver < 2) { + printf( "%s\n", getmsg( DELETE$_DELVER, MSG_FULL) ); + 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 ) ) & STS$M_SUCCESS ) { + sts = sys_erase(&fab); + if ( !( sts & STS$M_SUCCESS ) ) { + printf("%%DELETE-F-DELERR, Delete error: %s\n",getmsg(sts, MSG_TEXT)); + break; + } else { + filecount++; + if( options & delete_log ) { + rsa[nam.nam$b_rsl] = '\0'; + printf("%%DELETE-I-DELETED, Deleted %s\n",rsa); + } + } + } + if( sts == RMS$_NMF ) sts = SS$_NORMAL; + } + if( sts & STS$M_SUCCESS ) { + if( filecount < 1 ) { + printf( "%%DELETE-W-NOFILES, no files deleted\n" ); + } + } else { + printf( "%%DELETE-F-ERROR Status: %s\n", getmsg(sts, MSG_TEXT) ); + } + + return sts; +} diff --git a/extracters/ods2/descrip.h b/extracters/ods2/descrip.h index fca0e1a..9fa6518 100644 --- a/extracters/ods2/descrip.h +++ b/extracters/ods2/descrip.h @@ -1,4 +1,4 @@ -/* Descrip.h V2.1 Definitions for descriptors */ +/* Descrip.h Definitions for descriptors */ /* This is part of ODS2 written by Paul Nankervis, diff --git a/extracters/ods2/descrip.mms b/extracters/ods2/descrip.mms index 930c505..0a48161 100644 --- a/extracters/ods2/descrip.mms +++ b/extracters/ods2/descrip.mms @@ -1,62 +1,162 @@ -# -# MMS (MMK) description file to build ODS2 for VMS -# -# To compile on VAX using VAX C, use: -# -# $ mmk/macro=vaxc=1 -# -# -.IFDEF EXE -.ELSE -EXE = .EXE -OBJ = .OBJ -OLB = .OLB -.ENDIF - -.IFDEF __DEBUG__ -CFLAGS = $(CFLAGS)/DEBUG/NOOPTIMIZE -LINKFLAGS = $(LINKFLAGS)/DEBUG -.ELSE -LINKFLAGS = $(LINKFLAGS)/NOTRACE -.ENDIF -CFLAGS = $(CFLAGS)/DEFINE=(NO_DOLLAR) - -.IFDEF __VAXC__ -OPTFILE = ,VAXCRTL.OPT -OPTIONS = $(OPTFILE)/OPTIONS -.ELSE -OPTFILE = -OPTIONS = -.ENDIF - -OBJS = ODS2,RMS,DIRECT,ACCESS,DEVICE,CACHE,PHYVMS,SYSMSG,UPDATE,PHYVIRT - -ODS2$(EXE) : ODS2$(OLB)($(OBJS))$(OPTFILE) - $(LINK)$(LINKFLAGS) ODS2$(OLB)/INCLUDE=($(OBJS))$(OPTIONS) - -phyvirt$(obj) : phyvirt.c phyvirt.h compat.h ssdef.h - -sysmsg$(obj) : sysmsg.c sysmsg.h rms.h ssdef.h - -compat$(obj) : compat.c compat.h - -cache$(obj) : cache.c cache.h ssdef.h - -phyvms$(obj) : phyvms.c phyio.h phyvirt.h ssdef.h descrip.h access.h rms.h - -device$(obj) : device.c ssdef.h access.h phyio.h - -access$(obj) : access.c ssdef.h access.h phyio.h compat.h sysmsg.h phyvirt.h - -update$(obj) : update.c ssdef.h access.h - -direct$(obj) : direct.c direct.h access.h fibdef.h descrip.h ssdef.h - -rms$(obj) : rms.c rms.h compat.h direct.h access.h fibdef.h descrip.h ssdef.h - -ods2$(obj) : ods2.c compat.h sysmsg.h phyio.h ssdef.h descrip.h access.h rms.h version.h - -VAXCRTL.OPT : - @ open/write tmp $(MMS$TARGET) - @ write tmp "SYS$SHARE:VAXCRTL.EXE/SHARE" - @ close tmp +# +# MMS (MMK) description file to build ODS2 for VMS +# +# To compile on VAX using VAX C, use: +# +# $ mmk/macro=vaxc=1 +# +# +.IFDEF EXE +.ELSE +EXE = .EXE +OBJ = .OBJ +OLB = .OLB +.ENDIF + +.IFDEF __DEBUG__ +CFLAGS = $(CFLAGS)/DEBUG/NOOPTIMIZE +LINKFLAGS = $(LINKFLAGS)/DEBUG +.ELSE +LINKFLAGS = $(LINKFLAGS)/NOTRACE +.ENDIF +CFLAGS = $(CFLAGS)/DEFINE=(NO_DOLLAR) + +.IFDEF __VAXC__ +OPTFILE = ,VAXCRTL.OPT +OPTIONS = $(OPTFILE)/OPTIONS +.ELSE +OPTFILE = +OPTIONS = +.ENDIF + +# The next line is automatically generated. Do not change the format or split into multiple lines +OBJS = ODS2 ACCESS CACHE COMPAT COPYCMD CREATECMD DEBUG DELETECMD DEVICE DIFFCMD DIRCMD DIRECT DISMOUNTCMD EXTENDCMD HELPCMD IMPORTCMD INITIALCMD INITVOL MOUNTCMD PHYVMS PHYVIRT RMS SEARCHCMD SETCMD SHOWCMD SPAWNCMD SYSMSG TYPECMD UPDATE VMSTIME + +ODS2$(EXE) : ODS2$(OLB)($(OBJS))$(OPTFILE) + $(LINK)$(LINKFLAGS) ODS2$(OLB)/INCLUDE=($(OBJS))$(OPTIONS) + +VAXCRTL.OPT : + @ open/write tmp $(MMS$TARGET) + @ write tmp "SYS$SHARE:VAXCRTL.EXE/SHARE" + @ close tmp + +# Everything from the next line through EOF is automatically generated by make -f makefile.unix depend +# (or make -f makefile.vms depend) on Unix. + +# ### BEGIN RULES ### + +ODS2$(OBJ): VERSION.H +ODS2$(OBJ): CMDDEF.H +ODS2$(OBJ): COMPAT.H +ODS2$(OBJ): ODS2.H RMS.H +ODS2$(OBJ): VMSTIME.H DESCRIP.H SSDEF.H STSDEF.H SYSMSG.H CACHE.H PHYVIRT.H +ACCESS$(OBJ): SSDEF.H ACCESS.H CACHE.H F11DEF.H +ACCESS$(OBJ): VMSTIME.H +ACCESS$(OBJ): DESCRIP.H STSDEF.H DEVICE.H PHYIO.H INITVOL.H ODS2.H PHYVIRT.H +ACCESS$(OBJ): COMPAT.H +ACCESS$(OBJ): SYSMSG.H +CACHE$(OBJ): CACHE.H ODS2.H SSDEF.H +COMPAT$(OBJ): COMPAT.H +COMPAT$(OBJ): DESCRIP.H STSDEF.H +COPYCMD$(OBJ): CMDDEF.H +COPYCMD$(OBJ): COMPAT.H +COPYCMD$(OBJ): ODS2.H RMS.H VMSTIME.H DESCRIP.H SSDEF.H +COPYCMD$(OBJ): STSDEF.H SYSMSG.H +CREATECMD$(OBJ): CMDDEF.H +CREATECMD$(OBJ): COMPAT.H +CREATECMD$(OBJ): ODS2.H RMS.H VMSTIME.H DESCRIP.H SSDEF.H +CREATECMD$(OBJ): STSDEF.H SYSMSG.H F11DEF.H +DEBUG$(OBJ): DEBUG.H +DELETECMD$(OBJ): CMDDEF.H +DELETECMD$(OBJ): COMPAT.H +DELETECMD$(OBJ): ODS2.H RMS.H VMSTIME.H DESCRIP.H SSDEF.H +DELETECMD$(OBJ): STSDEF.H SYSMSG.H +DEVICE$(OBJ): ODS2.H ACCESS.H CACHE.H F11DEF.H +DEVICE$(OBJ): VMSTIME.H +DEVICE$(OBJ): DESCRIP.H SSDEF.H STSDEF.H DEVICE.H PHYIO.H +DIFFCMD$(OBJ): CMDDEF.H +DIFFCMD$(OBJ): COMPAT.H +DIFFCMD$(OBJ): ODS2.H RMS.H VMSTIME.H DESCRIP.H SSDEF.H +DIFFCMD$(OBJ): STSDEF.H SYSMSG.H +DIRCMD$(OBJ): CMDDEF.H +DIRCMD$(OBJ): COMPAT.H +DIRCMD$(OBJ): ODS2.H RMS.H VMSTIME.H DESCRIP.H SSDEF.H +DIRCMD$(OBJ): STSDEF.H SYSMSG.H +DIRECT$(OBJ): ACCESS.H CACHE.H F11DEF.H +DIRECT$(OBJ): VMSTIME.H +DIRECT$(OBJ): DESCRIP.H SSDEF.H STSDEF.H DIRECT.H FIBDEF.H ODS2.H +DISMOUNTCMD$(OBJ): CMDDEF.H +DISMOUNTCMD$(OBJ): COMPAT.H +DISMOUNTCMD$(OBJ): ODS2.H +DISMOUNTCMD$(OBJ): RMS.H VMSTIME.H DESCRIP.H SSDEF.H STSDEF.H SYSMSG.H ACCESS.H +DISMOUNTCMD$(OBJ): CACHE.H F11DEF.H +DISMOUNTCMD$(OBJ): DEVICE.H PHYIO.H +EXTENDCMD$(OBJ): CMDDEF.H +EXTENDCMD$(OBJ): COMPAT.H +EXTENDCMD$(OBJ): ODS2.H RMS.H VMSTIME.H DESCRIP.H SSDEF.H +EXTENDCMD$(OBJ): STSDEF.H SYSMSG.H +HELPCMD$(OBJ): CMDDEF.H +HELPCMD$(OBJ): COMPAT.H +HELPCMD$(OBJ): ODS2.H RMS.H VMSTIME.H DESCRIP.H SSDEF.H +HELPCMD$(OBJ): STSDEF.H SYSMSG.H +IMPORTCMD$(OBJ): CMDDEF.H +IMPORTCMD$(OBJ): COMPAT.H +IMPORTCMD$(OBJ): ODS2.H RMS.H VMSTIME.H DESCRIP.H SSDEF.H +IMPORTCMD$(OBJ): STSDEF.H SYSMSG.H +INITIALCMD$(OBJ): CMDDEF.H +INITIALCMD$(OBJ): COMPAT.H +INITIALCMD$(OBJ): ODS2.H RMS.H VMSTIME.H DESCRIP.H +INITIALCMD$(OBJ): SSDEF.H STSDEF.H SYSMSG.H ACCESS.H CACHE.H F11DEF.H +INITIALCMD$(OBJ): DEVICE.H +INITIALCMD$(OBJ): PHYIO.H INITVOL.H PHYVIRT.H +INITVOL$(OBJ): COMPAT.H +INITVOL$(OBJ): F11DEF.H +INITVOL$(OBJ): VMSTIME.H +INITVOL$(OBJ): DESCRIP.H SSDEF.H STSDEF.H INITVOL.H ODS2.H PHYVIRT.H RMS.H +MOUNTCMD$(OBJ): CMDDEF.H +MOUNTCMD$(OBJ): COMPAT.H +MOUNTCMD$(OBJ): ODS2.H RMS.H VMSTIME.H DESCRIP.H SSDEF.H +MOUNTCMD$(OBJ): STSDEF.H SYSMSG.H ACCESS.H CACHE.H F11DEF.H +MOUNTCMD$(OBJ): DEVICE.H +MOUNTCMD$(OBJ): PHYIO.H PHYVIRT.H +PHYVMS$(OBJ): DESCRIP.H RMS.H VMSTIME.H SSDEF.H STSDEF.H +PHYVMS$(OBJ): ODS2.H PHYIO.H PHYVIRT.H +PHYVIRT$(OBJ): COMPAT.H +PHYVIRT$(OBJ): DEVICE.H +PHYVIRT$(OBJ): ACCESS.H CACHE.H F11DEF.H +PHYVIRT$(OBJ): VMSTIME.H DESCRIP.H SSDEF.H STSDEF.H +PHYVIRT$(OBJ): PHYIO.H ODS2.H PHYVIRT.H +RMS$(OBJ): ACCESS.H CACHE.H +RMS$(OBJ): F11DEF.H VMSTIME.H +RMS$(OBJ): DESCRIP.H SSDEF.H STSDEF.H DEVICE.H PHYIO.H DIRECT.H FIBDEF.H ODS2.H +RMS$(OBJ): RMS.H COMPAT.H +RMS$(OBJ): SYSMSG.H +SEARCHCMD$(OBJ): CMDDEF.H +SEARCHCMD$(OBJ): COMPAT.H +SEARCHCMD$(OBJ): ODS2.H RMS.H VMSTIME.H DESCRIP.H SSDEF.H +SEARCHCMD$(OBJ): STSDEF.H SYSMSG.H +SETCMD$(OBJ): CMDDEF.H +SETCMD$(OBJ): COMPAT.H +SETCMD$(OBJ): ODS2.H RMS.H VMSTIME.H DESCRIP.H SSDEF.H +SETCMD$(OBJ): STSDEF.H SYSMSG.H +SHOWCMD$(OBJ): CMDDEF.H +SHOWCMD$(OBJ): COMPAT.H +SHOWCMD$(OBJ): ODS2.H RMS.H VMSTIME.H DESCRIP.H SSDEF.H +SHOWCMD$(OBJ): STSDEF.H SYSMSG.H ACCESS.H CACHE.H F11DEF.H +SHOWCMD$(OBJ): DIRECT.H PHYIO.H +SHOWCMD$(OBJ): PHYVIRT.H VERSION.H +SPAWNCMD$(OBJ): CMDDEF.H +SPAWNCMD$(OBJ): COMPAT.H +SPAWNCMD$(OBJ): ODS2.H RMS.H VMSTIME.H DESCRIP.H SSDEF.H +SPAWNCMD$(OBJ): STSDEF.H SYSMSG.H +SYSMSG$(OBJ): SSDEF.H RMS.H +SYSMSG$(OBJ): VMSTIME.H DESCRIP.H STSDEF.H COMPAT.H +SYSMSG$(OBJ): SYSMSG.H +TYPECMD$(OBJ): CMDDEF.H +TYPECMD$(OBJ): COMPAT.H +TYPECMD$(OBJ): ODS2.H RMS.H VMSTIME.H DESCRIP.H SSDEF.H +TYPECMD$(OBJ): STSDEF.H SYSMSG.H +UPDATE$(OBJ): ACCESS.H CACHE.H +UPDATE$(OBJ): F11DEF.H +UPDATE$(OBJ): VMSTIME.H DESCRIP.H SSDEF.H STSDEF.H DEVICE.H PHYIO.H ODS2.H +VMSTIME$(OBJ): VMSTIME.H DESCRIP.H SSDEF.H STSDEF.H diff --git a/extracters/ods2/device.c b/extracters/ods2/device.c index d38cdbb..0631283 100644 --- a/extracters/ods2/device.c +++ b/extracters/ods2/device.c @@ -1,4 +1,4 @@ -/* Device.c V2.1 Module to remember and find devices...*/ +/* Device.c Module to remember and find devices...*/ /* This is part of ODS2 written by Paul Nankervis, @@ -20,11 +20,20 @@ #include #include +#include "ods2.h" #include "access.h" #include "cache.h" #include "device.h" #include "ssdef.h" +#if !defined( DEBUG ) && defined( DEBUG_DEVICE ) +#define DEBUG DEBUG_DEVICE +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + static void *device_create( unsigned devsiz, void *keyval, unsigned *retsts ); static int device_compare( unsigned keylen, void *keyval, void *node ); @@ -48,12 +57,32 @@ static void *device_create( unsigned devsiz, void *keyval, unsigned *retsts ) { dev->cache.objtype = OBJTYPE_DEV; dev->vcb = NULL; dev->access = 0; + dev->context = NULL; memcpy( dev->devnam, keyval, devsiz ); memcpy( dev->devnam + devsiz, ":", 2 ); *retsts = SS$_NORMAL; return dev; } +/************************************************************ device_done() */ + +/* device_done() releases a device reference and + * eventually the device structure. + */ + +void device_done( struct DEV *dev ) { + if( dev->cache.refcount < 1 ) { +#if DEBUG + printf( "Device done with no reference\n" ); +#endif + abort(); + } + cache_untouch( &dev->cache, FALSE ); + if( dev->cache.refcount == 0 ) /* Safe because on LRU list */ + cache_delete( &dev->cache ); + return; +} + /*********************************************************** device_compare() */ /* device_compare() compares a device name to that of a device object... */ diff --git a/extracters/ods2/device.h b/extracters/ods2/device.h index 50fb10c..66aedee 100644 --- a/extracters/ods2/device.h +++ b/extracters/ods2/device.h @@ -1,4 +1,4 @@ -/* Device.h V2.1 Definitions for device routines */ +/* Device.h Definitions for device routines */ /* This is part of ODS2 written by Paul Nankervis, @@ -17,14 +17,18 @@ #include /* HANDLE */ #endif +#include +#include #include "access.h" #include "cache.h" +#include "phyio.h" struct DEV { /* Device information */ struct CACHE cache; struct VCB *vcb; /* Pointer to volume (if mounted) */ - int access; /* Device mount options (e.g., /Write) */ - unsigned sectors; /* Device physical sectors */ + int access; /* Device mount options (e.g., /Write) */ + void *context; /* Context for implementation */ + #ifdef _WIN32 short drive; /* Drive no. (0=A, 1=B, 2=C, ...) */ unsigned bytespersector; /* Device physical sectorsize (bytes) */ @@ -39,15 +43,22 @@ struct DEV { /* Device information */ short id; /* ASPI device id */ } ASPI; } API; - char *IoBuffer; /* Pointer to a buffer for the device */ unsigned last_sector; /* Last sector no read (still in buffer) */ #else int handle; /* Device physical I/O handle */ #endif +#if defined(_WIN32) || defined(USE_VHD) + char *IoBuffer; /* Pointer to a buffer for the device */ +#endif + struct disktype *disktype; /* Structure defining virtual disk geometry */ + phy_iord_t devread; /* Device read function */ + phy_iowr_t devwrite; /* Device write function */ + off_t eofptr; /* End of file on virtual files */ char devnam[1]; /* Device name */ }; unsigned device_lookup( unsigned devlen, char *devnam, int create, struct DEV **retdev ); +void device_done( struct DEV *dev ); #endif /* # ifndef _DEVICE_H */ diff --git a/extracters/ods2/diffcmd.c b/extracters/ods2/diffcmd.c new file mode 100644 index 0000000..c7d7f83 --- /dev/null +++ b/extracters/ods2/diffcmd.c @@ -0,0 +1,96 @@ +/* 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. + */ + +#if !defined( DEBUG ) && defined( DEBUG_DIFFCMD ) +#define DEBUG DEBUG_DIFFCMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + +/******************************************************************* dodiff() */ + +/* dodiff: a simple file difference routine */ + +param_t 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 } +}; + +DECL_CMD(diff) { + int sts, records = 0; + char *rec, *cpy = NULL; + char *name; + FILE *tof; + struct FAB fab = cc$rms_fab; + struct RAB rab = cc$rms_rab; + + UNUSED(argc); + UNUSED(qualc); + UNUSED(qualv); + + name = argv[1]; + sts = SS$_BADFILENAME; + if ( *name == '\0' ) { + return sts; + } + records = 0; + fab.fab$l_fna = name; + fab.fab$b_fns = strlen( fab.fab$l_fna ); + tof = openf( argv[2], "r" ); + if ( tof == NULL ) { + printf("%%ODS2-E-OPENERR, Could not open file %s\n",name); + perror( " - " ); + return SS$_NOSUCHFILE; + } + sts = sys_open( &fab ); + if ( sts & STS$M_SUCCESS ) { + rab.rab$l_fab = &fab; + sts = sys_connect( &rab ); + if( sts & STS$M_SUCCESS ) { + if( (rec = malloc( MAXREC + 2 )) == NULL ) { + perror( "malloc" ); + } else { + rab.rab$l_ubf = rec; + rab.rab$w_usz = MAXREC; + while( (sts = sys_get( &rab )) & STS$M_SUCCESS ) { + rec[rab.rab$w_rsz] = '\0'; + cpy = fgetline( tof, FALSE ); + if( cpy == NULL || + 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; + } + free( cpy ); + cpy = NULL; + records++; + } + if( cpy != NULL ) free( cpy ); + free( rec ); + rec = cpy = NULL; + } + sys_disconnect(&rab); + } + sys_close(&fab); + } + fclose(tof); + if (sts == RMS$_EOF) sts = SS$_NORMAL; + if ( sts & STS$M_SUCCESS ) { + printf( "%%DIFF-I-Compared %d records\n", records ); + } else if ( sts != 4 ) { + printf("%%DIFF-F-Error %s in difference\n",getmsg(sts, MSG_TEXT)); + } + return sts; +} diff --git a/extracters/ods2/dircmd.c b/extracters/ods2/dircmd.c new file mode 100644 index 0000000..55bbe2f --- /dev/null +++ b/extracters/ods2/dircmd.c @@ -0,0 +1,436 @@ +/* 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. + */ + +#if !defined( DEBUG ) && defined( DEBUG_DIRCMD ) +#define DEBUG DEBUG_DIRCMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + +static void dirtotal( int options, int size, int alloc ); + + +/******************************************************************* dodir() */ + +#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) + +const int dir_defaults = dir_default; + +static qual_t 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} +}; +static qual_t 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} +}; +qual_t 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; + +param_t dirpars[] = { {"filespec", OPT, VMSFS, NOPA, "for files to select. Wildcards are allowed."}, + { NULL, 0, 0, NOPA, NULL } +}; + + +/************************************************************ dodir() */ + +DECL_CMD(dir) { + char res[NAM$C_MAXRSS + 1],rsa[NAM$C_MAXRSS + 1]; + int options; + unsigned sts; + int filecount = 0, nobackup = 0, contigb = 0, contig = 0, directory = 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; + 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 & STS$M_SUCCESS ) { + 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; +#if 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 ) ) & STS$M_SUCCESS ) { + + 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 & STS$M_SUCCESS ) ) { + printf("%%ODS2-E-OPENERR, Open error: %s\n", getmsg(sts, MSG_TEXT)); + } 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:" ); + (void) 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 = SS$_NORMAL; + 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 & STS$M_SUCCESS ) { + if (filecount < 1) printf("%%DIRECT-W-NOFILES, no files found\n"); + } else { + printf("%%DIR-E-ERROR Status: %s\n",getmsg(sts, MSG_TEXT)); + } + return sts; +} + +/*********************************************************** dirtotal() */ + +static 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; +} diff --git a/extracters/ods2/direct.c b/extracters/ods2/direct.c index 435fb8c..edb5301 100644 --- a/extracters/ods2/direct.c +++ b/extracters/ods2/direct.c @@ -1,4 +1,4 @@ -/* Direct.c V2.1 */ +/* Direct.c */ /* This is part of ODS2 written by Paul Nankervis, @@ -13,6 +13,14 @@ /* This module does all directory file handling - mostly lookups of filenames in directory files... */ +#if !defined( DEBUG ) && defined( DEBUG_DIRECT ) +#define DEBUG DEBUG_DIRECT +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + #define DEBUGx on #include @@ -24,20 +32,13 @@ #include "descrip.h" #include "direct.h" #include "fibdef.h" +#include "ods2.h" #include "ssdef.h" #include "stsdef.h" -#ifndef TRUE -#define TRUE ( 0 == 0 ) -#endif -#ifndef FALSE -#define FALSE ( 0 != 0 ) -#endif - #define BLOCKSIZE 512 #define MAXREC (BLOCKSIZE - 2) - /* Some statistical counters... */ static int direct_lookups = 0; @@ -66,8 +67,7 @@ void direct_show( void ) { /* name_check() - take a name specification and return name length without the version number, an integer version number, and a wildcard flag */ -unsigned name_check(char *str,int len,int *retlen,int *retver,int *wildflag) -{ +static unsigned name_check( char *str, int len, int *retlen, int *retver, int *wildflag ) { int wildcard = FALSE; char *name_start = str; register int dots = 0; @@ -256,7 +256,7 @@ int name_match(char *spec,int spec_len,char *dirent,int dirent_len) unsigned insert_ent(struct FCB * fcb,unsigned eofblk,unsigned curblk, struct VIOC * vioc,char *buffer, - struct dir$rec * dr,struct dir$ent * de, + struct dir$r_rec * dr,struct dir$r_ent * de, char *filename,unsigned filelen, unsigned version,struct fiddef * fid) { @@ -265,10 +265,10 @@ unsigned insert_ent(struct FCB * fcb,unsigned eofblk,unsigned curblk, /* Compute space required... */ - register int addlen = sizeof(struct dir$ent); + register int addlen = sizeof(struct dir$r_ent); direct_inserts++; if (de == NULL) - addlen += (filelen + sizeof(struct dir$rec)) & ~1; + addlen += (filelen + sizeof(struct dir$r_rec)) & ~1; /* Compute block space in use ... */ @@ -276,29 +276,29 @@ unsigned insert_ent(struct FCB * fcb,unsigned eofblk,unsigned curblk, int invalid_dir = TRUE; while (TRUE) { register int sizecheck; - register struct dir$rec *nr = (struct dir$rec *) (buffer + inuse); + register struct dir$r_rec *nr = (struct dir$r_rec *) (buffer + inuse); if (dr == nr) invalid_dir = FALSE; - sizecheck = VMSWORD(nr->dir$size); - if (sizecheck == 0xffff) + sizecheck = F11WORD(nr->dir$w_size); + if (sizecheck == 0xffff) /* End of data marker */ break; sizecheck += 2; inuse += sizecheck; - sizecheck -= (nr->dir$namecount + sizeof(struct dir$rec)) & ~1; + sizecheck -= (nr->dir$b_namecount + sizeof(struct dir$r_rec)) & ~1; if (inuse > MAXREC || (inuse & 1) || sizecheck <= 0 || - sizecheck % sizeof(struct dir$ent) != 0) { + sizecheck % sizeof(struct dir$r_ent) != 0) { deaccesschunk(vioc,0,0,FALSE); return SS$_BADIRECTORY; } - }; + } if (invalid_dir) { printf("BUGCHECK invalid dir\n"); exit(EXIT_FAILURE); } if (de != NULL) { - if (VMSWORD(dr->dir$size) > MAXREC || - (char *) de < dr->dir$name + dr->dir$namecount || - (char *) de > (char *) dr + VMSWORD(dr->dir$size) + 2) { + if (F11WORD(dr->dir$w_size) > MAXREC || + (char *) de < dr->dir$t_name + dr->dir$b_namecount || + (char *) de > (char *) dr + F11WORD(dr->dir$w_size) + 2) { printf("BUGCHECK invalid de\n"); exit(EXIT_FAILURE); } @@ -308,14 +308,18 @@ unsigned insert_ent(struct FCB * fcb,unsigned eofblk,unsigned curblk, /* If not enough space free extend the directory... */ if (addlen > MAXREC - inuse) { - register struct dir$rec *nr; + register struct dir$r_rec *nr; unsigned keep_new = 0; char *newbuf; struct VIOC *newvioc; unsigned newblk = eofblk + 1; direct_splits++; - printf("Splitting record... %s %u,%u,%u,%u\n", dr->dir$name,de->dir$fid.fid$w_num, - de->dir$fid.fid$w_seq,de->dir$fid.fid$b_rvn,de->dir$fid.fid$b_nmx); + printf( "Splitting record... %s", dr->dir$t_name ); + if( de != NULL ) + printf( " %u,%u,%u,%u\n", de->dir$w_fid.fid$w_num, + de->dir$w_fid.fid$w_seq, de->dir$w_fid.fid$b_rvn, de->dir$w_fid.fid$b_nmx ); + else + putchar( '\n' ); if (newblk > fcb->hiblock) { printf("I can't extend a directory yet!!\n"); exit(EXIT_FAILURE); @@ -345,49 +349,49 @@ unsigned insert_ent(struct FCB * fcb,unsigned eofblk,unsigned curblk, } memset(newbuf,0,BLOCKSIZE); eofblk++; - fcb->head->fh2$w_recattr.fat$l_efblk = VMSWORD(eofblk + 1); + fcb->head->fh2$w_recattr.fat$l_efblk = F11WORD(eofblk + 1); /* First find where the next record is... */ nr = dr; - if (VMSWORD(nr->dir$size) <= MAXREC) - nr = (struct dir$rec *) ((char *) nr + VMSWORD(nr->dir$size) + 2); + if (F11WORD(nr->dir$w_size) <= MAXREC) + nr = (struct dir$r_rec *) ((char *) nr + F11WORD(nr->dir$w_size) + 2); /* Can we split between records? */ if (de == NULL || (char *) dr != buffer || - VMSWORD(nr->dir$size) <= MAXREC) { - register struct dir$rec *sp = dr; + F11WORD(nr->dir$w_size) <= MAXREC) { + register struct dir$r_rec *sp = dr; if ((char *) dr == buffer && de != NULL) sp = nr; memcpy(newbuf,sp,((buffer + BLOCKSIZE) - (char *) sp)); memset(sp,0,((buffer + BLOCKSIZE) - (char *) sp)); - sp->dir$size = VMSWORD(0xffff); + sp->dir$w_size = F11WORD(0xffff); if (sp == dr && (de != NULL || (char *) sp >= buffer + MAXREC - addlen)) { if (de != NULL) - de = (struct dir$ent *) + de = (struct dir$r_ent *) (newbuf + ((char *) de - (char *) sp)); - dr = (struct dir$rec *) (newbuf + ((char *) dr - (char *) sp)); + dr = (struct dir$r_rec *) (newbuf + ((char *) dr - (char *) sp)); keep_new = 1; } /* OK, we have to split the record then.. */ } else { - register unsigned reclen = (dr->dir$namecount + - sizeof(struct dir$rec)) & ~1; - register struct dir$rec *nbr = (struct dir$rec *) newbuf; - printf("Super split %s %u,%u,%u,%u\n", dr->dir$name,de->dir$fid.fid$w_num, - de->dir$fid.fid$w_seq,de->dir$fid.fid$b_rvn,de->dir$fid.fid$b_nmx); + register unsigned reclen = (dr->dir$b_namecount + + sizeof(struct dir$r_rec)) & ~1; + register struct dir$r_rec *nbr = (struct dir$r_rec *) newbuf; + printf("Super split %s %u,%u,%u,%u\n", dr->dir$t_name,de->dir$w_fid.fid$w_num, + de->dir$w_fid.fid$w_seq,de->dir$w_fid.fid$b_rvn,de->dir$w_fid.fid$b_nmx); memcpy(newbuf,buffer,reclen); memcpy(newbuf + reclen,de,((char *) nr - (char *) de) + 2); - nbr->dir$size = VMSWORD(reclen + ((char *) nr - (char *) de) - 2); + nbr->dir$w_size = F11WORD(reclen + ((char *) nr - (char *) de) - 2); memset((char *) de + 2,0,((char *) nr - (char *) de)); - ((struct dir$rec *) de)->dir$size = VMSWORD(0xffff); - dr->dir$size = VMSWORD(((char *) de - (char *) dr) - 2); + ((struct dir$r_rec *) de)->dir$w_size = F11WORD(0xffff); + dr->dir$w_size = F11WORD(((char *) de - (char *) dr) - 2); if ((char *) de >= (char *) nr) { - dr = (struct dir$rec *) newbuf; - de = (struct dir$ent *) (newbuf + reclen); + dr = (struct dir$r_rec *) newbuf; + de = (struct dir$r_ent *) (newbuf + reclen); keep_new = 1; } } @@ -409,23 +413,23 @@ unsigned insert_ent(struct FCB * fcb,unsigned eofblk,unsigned curblk, if (de == NULL) { memmove((char *) dr + addlen,dr, BLOCKSIZE - (((char *) dr + addlen) - buffer)); - dr->dir$size = VMSWORD(addlen - 2); - dr->dir$verlimit = 0; - dr->dir$flags = 0; - dr->dir$namecount = filelen; - memcpy(dr->dir$name,filename,filelen); - de = (struct dir$ent *) - ((char *) dr + (addlen - sizeof(struct dir$ent))); + dr->dir$w_size = F11WORD(addlen - 2); + dr->dir$w_verlimit = 0; + dr->dir$b_flags = 0; + dr->dir$b_namecount = filelen; + memcpy(dr->dir$t_name,filename,filelen); + de = (struct dir$r_ent *) + ((char *) dr + (addlen - sizeof(struct dir$r_ent))); } else { - dr->dir$size = VMSWORD(VMSWORD(dr->dir$size) + addlen); + dr->dir$w_size = F11WORD(F11WORD(dr->dir$w_size) + addlen); memmove((char *) de + addlen,de, BLOCKSIZE - (((char *) de + addlen) - buffer)); } /* Write the entry values are we are done! */ - de->dir$version = VMSWORD(version); - fid_copy(&de->dir$fid,fid,0); + de->dir$w_version = F11WORD(version); + fid_copy(&de->dir$w_fid,fid,0); return deaccesschunk(vioc,curblk,1,TRUE); } @@ -434,20 +438,20 @@ unsigned insert_ent(struct FCB * fcb,unsigned eofblk,unsigned curblk, /* delete_ent() - delete a directory entry */ unsigned delete_ent(struct FCB * fcb,struct VIOC * vioc,unsigned curblk, - struct dir$rec * dr,struct dir$ent * de, + struct dir$r_rec * dr,struct dir$r_ent * de, char *buffer,unsigned eofblk) { unsigned sts = SS$_NORMAL; unsigned ent; direct_deletes++; - ent = (VMSWORD(dr->dir$size) - sizeof(struct dir$rec) - - dr->dir$namecount + 3) / sizeof(struct dir$ent); + ent = (F11WORD(dr->dir$w_size) - sizeof(struct dir$r_rec) + - dr->dir$b_namecount + 3) / sizeof(struct dir$r_ent); if (ent > 1) { - char *ne = (char *) de + sizeof(struct dir$ent); + char *ne = (char *) de + sizeof(struct dir$r_ent); memcpy(de,ne,BLOCKSIZE - (ne - buffer)); - dr->dir$size = VMSWORD(VMSWORD(dr->dir$size) - sizeof(struct dir$ent)); + dr->dir$w_size = F11WORD(F11WORD(dr->dir$w_size) - sizeof(struct dir$r_ent)); } else { - char *nr = (char *) dr + VMSWORD(dr->dir$size) + 2; + char *nr = (char *) dr + F11WORD(dr->dir$w_size) + 2; if (eofblk == 1 || (char *) dr > buffer || (nr <= buffer + MAXREC && (unsigned short) *nr < BLOCKSIZE)) { memcpy(dr,nr,BLOCKSIZE - (nr - buffer)); @@ -464,7 +468,7 @@ unsigned delete_ent(struct FCB * fcb,struct VIOC * vioc,unsigned curblk, vioc = nxtvioc; } if (sts & STS$M_SUCCESS) { - fcb->head->fh2$w_recattr.fat$l_efblk = VMSSWAP(eofblk); + fcb->head->fh2$w_recattr.fat$l_efblk = F11SWAP(eofblk); eofblk--; } } @@ -481,17 +485,17 @@ unsigned delete_ent(struct FCB * fcb,struct VIOC * vioc,unsigned curblk, /* return_ent() - return information about a directory entry */ unsigned return_ent(struct FCB * fcb,struct VIOC * vioc,unsigned curblk, - struct dir$rec * dr,struct dir$ent * de,struct fibdef * fib, + struct dir$r_rec * dr,struct dir$r_ent * de,struct fibdef * fib, unsigned short *reslen,struct dsc_descriptor * resdsc, int wildcard) { register int scale = 10; - register int version = VMSWORD(de->dir$version); - register int length = dr->dir$namecount; + register int version = F11WORD(de->dir$w_version); + register int length = dr->dir$b_namecount; register char *ptr = resdsc->dsc_a_pointer; register int outlen = resdsc->dsc_w_length; if (length > outlen) length = outlen; - memcpy(ptr,dr->dir$name,length); + memcpy(ptr,dr->dir$t_name,length); while (version >= scale) scale *= 10; ptr += length; if (length < outlen) { @@ -506,8 +510,9 @@ unsigned return_ent(struct FCB * fcb,struct VIOC * vioc,unsigned curblk, } while (scale > 1); } *reslen = length; - fid_copy((struct fiddef *)&fib->fib$w_fid_num,&de->dir$fid,0); + fid_copy((struct fiddef *)&fib->fib$w_fid_num,&de->dir$w_fid,0); if (fib->fib$b_fid_rvn == 0) fib->fib$b_fid_rvn = fcb->rvn; + fib->fib$w_verlimit = dr->dir$w_verlimit; if (wildcard || (fib->fib$w_nmctl & FIB$M_WILD)) { fib->fib$l_wcc = curblk; } else { @@ -540,14 +545,15 @@ unsigned search_ent(struct FCB *fcb, curblk = fib->fib$l_wcc; if (curblk != 0) { searchspec = resdsc->dsc_a_pointer; - sts = name_check(searchspec,*reslen,&searchlen,&version,&wildcard); - if (action || wildcard) sts = SS$_BADFILENAME; + sts = name_check( searchspec, *reslen, &searchlen, &version, &wildcard ); + if( MODIFIES(action) || wildcard ) + sts = SS$_BADFILENAME; wcc_flag = TRUE; } else { searchspec = filedsc->dsc_a_pointer; - sts = name_check(searchspec,filedsc->dsc_w_length,&searchlen,&version, - &wildcard); - if ((action && wildcard) || (action > 1 && version < 0)) { + sts = name_check( searchspec, filedsc->dsc_w_length, &searchlen, &version, + &wildcard); + if( (MODIFIES(action) && wildcard) || (action == DIRECT_CREATE && version < 0) ) { sts = SS$_BADFILENAME; } wcc_flag = FALSE; @@ -564,27 +570,27 @@ unsigned search_ent(struct FCB *fcb, while (loblk < hiblk) { register int cmp; register unsigned newblk; - register struct dir$rec *dr; + register struct dir$r_rec *dr; direct_searches++; - sts = accesschunk(fcb,curblk,&vioc,&buffer,NULL,action ? 1 : 0); + sts = accesschunk(fcb,curblk,&vioc,&buffer,NULL,MODIFIES(action)); if (!(sts & STS$M_SUCCESS)) return sts; - dr = (struct dir$rec *) buffer; - if (VMSWORD(dr->dir$size) > MAXREC) { + dr = (struct dir$r_rec *) buffer; + if (F11WORD(dr->dir$w_size) > MAXREC) { cmp = MAT_GT; } else { - cmp = name_match(searchspec,searchlen,dr->dir$name, - dr->dir$namecount); + cmp = name_match(searchspec,searchlen,dr->dir$t_name, + dr->dir$b_namecount); if (cmp == MAT_EQ) { if (wildcard || version < 1 || version > 32767) { cmp = MAT_NE; /* no match - want to find start */ } else { - register struct dir$ent *de = - (struct dir$ent *) - (dr->dir$name + ((dr->dir$namecount + 1) & ~1)); - if (VMSWORD(de->dir$version) < version) { + register struct dir$r_ent *de = + (struct dir$r_ent *) + (dr->dir$t_name + ((dr->dir$b_namecount + 1) & ~1)); + if (F11WORD(de->dir$w_version) < version) { cmp = MAT_GT; /* too far... */ } else { - if (VMSWORD(de->dir$version) > version) { + if (F11WORD(de->dir$w_version) > version) { cmp = MAT_LT; /* further ahead... */ } } @@ -624,21 +630,21 @@ unsigned search_ent(struct FCB *fcb, unsigned last_len = 0; register int relver = 0; while ((sts & STS$M_SUCCESS) && curblk <= eofblk) { - register struct dir$rec *dr; + register struct dir$r_rec *dr; register int cmp = MAT_LT; /* Access a directory block. Reset relative version if it starts with a record we haven't seen before... */ - if (vioc == NULL) { - sts = accesschunk(fcb,curblk,&vioc,&buffer,NULL,action ? 1 : 0); + if( vioc == NULL ) { + sts = accesschunk( fcb,curblk,&vioc,&buffer,NULL,MODIFIES(action) ); if (!(sts & STS$M_SUCCESS)) return sts; } - dr = (struct dir$rec *) buffer; - if (last_len != dr->dir$namecount) { + dr = (struct dir$r_rec *) buffer; + if (last_len != dr->dir$b_namecount) { relver = 0; } else { - if (name_match(last_name,last_len,dr->dir$name,last_len) != + if (name_match(last_name,last_len,dr->dir$t_name,last_len) != MAT_EQ) { relver = 0; } @@ -647,11 +653,11 @@ unsigned search_ent(struct FCB *fcb, /* Now loop through the records seeing which match our spec... */ while (TRUE) { /* dr records within block */ - register char *nr = (char *) dr + VMSWORD(dr->dir$size) + 2; + register char *nr = (char *) dr + F11WORD(dr->dir$w_size) + 2; if (nr >= buffer + BLOCKSIZE) break; - if (dr->dir$name + dr->dir$namecount >= nr) break; - cmp = name_match(searchspec,searchlen,dr->dir$name, - dr->dir$namecount); + if (dr->dir$t_name + dr->dir$b_namecount >= nr) break; + cmp = name_match(searchspec,searchlen,dr->dir$t_name, + dr->dir$b_namecount); if (cmp == MAT_GT && wcc_flag) { wcc_flag = FALSE; searchspec = filedsc->dsc_a_pointer; @@ -660,12 +666,12 @@ unsigned search_ent(struct FCB *fcb, if (!(sts & STS$M_SUCCESS)) break; } else { if (cmp == MAT_EQ) { - register struct dir$ent *de; - de = (struct dir$ent *) - (dr->dir$name + - ((dr->dir$namecount + 1) & ~1)); - if (version == 0 && action == 2) { - version = VMSWORD(de->dir$version) + 1; + register struct dir$r_ent *de; + de = (struct dir$r_ent *) + (dr->dir$t_name + + ((dr->dir$b_namecount + 1) & ~1)); + if (version == 0 && action == DIRECT_CREATE) { + version = F11WORD(de->dir$w_version) + 1; if (version > 32767) { sts = SS$_BADFILENAME; break; @@ -679,12 +685,12 @@ unsigned search_ent(struct FCB *fcb, while ((char *) de < nr) { if ((version < 1) ? (relver > version) : - (version < VMSWORD(de->dir$version))) { + (version < F11WORD(de->dir$w_version))) { relver--; de++; } else { if (version > 32767 || version == relver || - version == VMSWORD(de->dir$version)) { + version == F11WORD(de->dir$w_version)) { cmp = MAT_EQ; } else { cmp = MAT_GT; @@ -694,14 +700,15 @@ unsigned search_ent(struct FCB *fcb, } else { wcc_flag = FALSE; searchspec = filedsc->dsc_a_pointer; - sts = name_check(searchspec, - filedsc->dsc_w_length, - &searchlen,&version, - &wildcard); - if (!(sts & STS$M_SUCCESS)) break; + sts = name_check( searchspec, + filedsc->dsc_w_length, + &searchlen,&version, + &wildcard ); + if( !(sts & STS$M_SUCCESS) ) + break; if (name_match(searchspec,searchlen, - dr->dir$name, - dr->dir$namecount) != + dr->dir$t_name, + dr->dir$b_namecount) != MAT_EQ) { cmp = MAT_NE; break; @@ -725,23 +732,25 @@ unsigned search_ent(struct FCB *fcb, if (cmp == MAT_EQ) { switch (action) { - case 0: - return return_ent(fcb,vioc,curblk,dr,de,fib, - reslen,resdsc,wildcard); - case 1: - return delete_ent(fcb,vioc,curblk,dr,de, - buffer,eofblk); - default: - sts = SS$_DUPFILENAME; - break; + case DIRECT_FIND: + return return_ent( fcb, vioc, curblk, dr, de, fib, + reslen, resdsc, wildcard ); + case DIRECT_DELETE: + return delete_ent( fcb, vioc, curblk, dr, de, + buffer, eofblk ); + case DIRECT_CREATE: + sts = SS$_DUPFILENAME; + break; + default: + abort(); } } else { - if (cmp != MAT_NE && action == 2) { - return insert_ent(fcb,eofblk,curblk,vioc,buffer, - dr,de,searchspec,searchlen, - version, - (struct fiddef *) - &fib->fib$w_fid_num); + if( cmp != MAT_NE && action == DIRECT_CREATE ) { + return insert_ent( fcb, eofblk, curblk, vioc, buffer, + dr, de, searchspec, searchlen, + version, + (struct fiddef *) + &fib->fib$w_fid_num); } } } @@ -753,23 +762,23 @@ unsigned search_ent(struct FCB *fcb, so that if it continues into the next block we can get the relative version right! Sigh! */ - if (VMSWORD(((struct dir$rec *) nr)->dir$size) > MAXREC) { - last_len = dr->dir$namecount; + if (F11WORD(((struct dir$r_rec *) nr)->dir$w_size) > MAXREC) { + last_len = dr->dir$b_namecount; if (last_len > sizeof(last_name)) { last_len = sizeof(last_name); } - memcpy(last_name,dr->dir$name,last_len); - dr = (struct dir$rec *) nr; + memcpy(last_name,dr->dir$t_name,last_len); + dr = (struct dir$r_rec *) nr; break; } - dr = (struct dir$rec *) nr; + dr = (struct dir$r_rec *) nr; } } /* Release the buffer ready to get the next one - unless it is the last one in which case we can't defer an insert any longer!! */ - if (!(sts & STS$M_SUCCESS) || action != 2 || + if (!(sts & STS$M_SUCCESS) || action != DIRECT_CREATE || (cmp != MAT_GT && curblk < eofblk)) { register unsigned dests = deaccesschunk(vioc,0,0,TRUE); if (!(dests & STS$M_SUCCESS)) { @@ -805,7 +814,8 @@ unsigned search_ent(struct FCB *fcb, /* direct() - this routine handles all directory manipulations:- action 0 - find directory entry 1 - delete entry - 2 - create an entry */ + 2 - create an entry + */ unsigned direct(struct VCB * vcb,struct dsc_descriptor * fibdsc, struct dsc_descriptor * filedsc,unsigned short *reslen, @@ -814,11 +824,11 @@ unsigned direct(struct VCB * vcb,struct dsc_descriptor * fibdsc, struct FCB *fcb; register unsigned sts,eofblk; register struct fibdef *fib = (struct fibdef *) fibdsc->dsc_a_pointer; - sts = accessfile(vcb,(struct fiddef *) & fib->fib$w_did_num,&fcb,action); + sts = accessfile(vcb,(struct fiddef *) &fib->fib$w_did_num, &fcb, (action != DIRECT_FIND) ); if (sts & STS$M_SUCCESS) { - if (VMSLONG(fcb->head->fh2$l_filechar) & FH2$M_DIRECTORY) { - eofblk = VMSSWAP(fcb->head->fh2$w_recattr.fat$l_efblk); - if (VMSWORD(fcb->head->fh2$w_recattr.fat$w_ffbyte) == 0) --eofblk; + if (F11LONG(fcb->head->fh2$l_filechar) & FH2$M_DIRECTORY) { + eofblk = F11SWAP(fcb->head->fh2$w_recattr.fat$l_efblk); + if (F11WORD(fcb->head->fh2$w_recattr.fat$w_ffbyte) == 0) --eofblk; sts = search_ent(fcb,fibdsc,filedsc,reslen,resdsc,eofblk,action); } else { sts = SS$_BADIRECTORY; diff --git a/extracters/ods2/direct.h b/extracters/ods2/direct.h index 2706388..b62e324 100644 --- a/extracters/ods2/direct.h +++ b/extracters/ods2/direct.h @@ -1,4 +1,4 @@ -/* Direct.h V2.1 Definitions for directory access routines */ +/* Direct.h Definitions for directory access routines */ /* This is part of ODS2 written by Paul Nankervis, @@ -13,23 +13,19 @@ #ifndef _DIRECT_H #define _DIRECT_H -struct dir$rec { - vmsword dir$size; - vmsword dir$verlimit; - vmsbyte dir$flags; - vmsbyte dir$namecount; - char dir$name[1]; -}; - -struct dir$ent { - vmsword dir$version; - struct fiddef dir$fid; -}; - +#include "access.h" +#include "descrip.h" +#include "f11def.h" void direct_show( void ); unsigned direct(struct VCB *vcb,struct dsc_descriptor *fibdsc, struct dsc_descriptor *filedsc,unsigned short *reslen, struct dsc_descriptor *resdsc,unsigned action); +#define DIRECT_FIND 0 +#define DIRECT_DELETE 1 +#define DIRECT_CREATE 2 + +#define MODIFIES(action) (action != DIRECT_FIND) + #endif /* #ifndef _DIRECT_H */ diff --git a/extracters/ods2/dismountcmd.c b/extracters/ods2/dismountcmd.c new file mode 100644 index 0000000..76e24f7 --- /dev/null +++ b/extracters/ods2/dismountcmd.c @@ -0,0 +1,49 @@ +/* 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. + */ + +#if !defined( DEBUG ) && defined( DEBUG_DISMOUNTCMD ) +#define DEBUG DEBUG_DISMOUNTCMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + +#include "access.h" +#include "device.h" + +/******************************************************************* dodismount() */ + +param_t dmopars[] = { {"drive_letter", REQ, STRING, NOPA, "Drive containing volume to dismount", }, + {NULL, 0, 0, NOPA, NULL } +}; + +DECL_CMD(dismount) { + struct DEV *dev; + int sts; + + UNUSED(argc); + UNUSED(qualc); + UNUSED(qualv); + + sts = device_lookup( strlen(argv[1]), argv[1], FALSE, &dev ); + if( sts & STS$M_SUCCESS ) { + device_done( dev ); + if( dev->vcb != NULL ) { + sts = dismount( dev->vcb ); + } else { + sts = SS$_DEVNOTMOUNT; + } + } + if( !(sts & STS$M_SUCCESS) ) + printf("%%DISMOUNT-E-STATUS Error: %s\n",getmsg(sts, MSG_TEXT)); + return sts; +} diff --git a/extracters/ods2/extendcmd.c b/extracters/ods2/extendcmd.c new file mode 100644 index 0000000..9b9c433 --- /dev/null +++ b/extracters/ods2/extendcmd.c @@ -0,0 +1,50 @@ +/* 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. + */ + +#if !defined( DEBUG ) && defined( DEBUG_EXTENDCMD ) +#define DEBUG DEBUG_EXTENDCMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + + +/***************************************************************** doextend() */ + +/* more test code... */ + +param_t extendpars[] = { {"ods-2_filespec", REQ, VMSFS, NOPA, "for file on ODS-2 volume."}, + { NULL, 0, 0, NOPA, NULL } +}; + +DECL_CMD(extend) { + 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; + sts = sys_open( &fab ); + if ( sts & STS$M_SUCCESS ) { + fab.fab$l_alq = 32; + sts = sys_extend(&fab); + sys_close(&fab); + } + if ( !( sts & STS$M_SUCCESS ) ) { + printf("%%EXTEND-F-ERROR Status: %s\n",getmsg(sts, MSG_TEXT)); + } + return sts; +} diff --git a/extracters/ods2/f11def.h b/extracters/ods2/f11def.h new file mode 100644 index 0000000..dcbd630 --- /dev/null +++ b/extracters/ods2/f11def.h @@ -0,0 +1,217 @@ +/* Access.h Definitions for file access routines */ + +/* + 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. +*/ + +#ifndef _F11DEF_H +#define _F11DEF_H + +#include + +#include "vmstime.h" + +#ifndef ODS2_BIG_ENDIAN +#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define ODS2_BIG_ENDIAN 1 +#endif +#else +#if defined(__BIG_ENDIAN__) || defined(__BIG_ENDIAN) || defined(_BIG_ENDIAN) +#define ODS2_BIG_ENDIAN 1 +#endif +#endif +#endif + +#ifdef ODS2_BIG_ENDIAN +#define F11LONG( l ) ( ( (l) & 0x000000ff ) << 24 | \ + ( (l) & 0x0000ff00 ) << 8 | \ + ( (l) & 0x00ff0000 ) >> 8 | \ + (l) >> 24 ) +#define F11WORD( w ) ( ( (w) & 0x00ff ) << 8 | \ + (w) >> 8 ) +#define F11SWAP( l ) ( ( (l) & 0x00ff0000 ) << 8 | \ + ( (l) & 0xff000000 ) >> 8 | \ + ( (l) & 0x000000ff ) << 8 | \ + ( (l) & 0x0000ff00 ) >> 8 ) +#else +#define F11LONG( l ) (l) +#define F11WORD( w ) (w) +#define F11SWAP( l ) ( ( (l) & 0x0000ffff ) << 16 | \ + (l) >> 16 ) +#endif + +typedef uint8_t f11byte; +typedef uint16_t f11word; +typedef uint32_t f11swap; +typedef uint32_t f11long; + +#define FH2$M_NOBACKUP 0x00002 +#define FH2$M_CONTIGB 0x00020 +#define FH2$M_CONTIG 0x00080 +#define FH2$M_DIRECTORY 0x02000 +#define FH2$M_MARKDEL 0x08000 +#define FH2$M_ERASE 0x20000 + +#ifdef __ALPHA +#pragma member_alignment save +#pragma nomember_alignment +#endif + +struct UIC { + f11word uic$w_mem; + f11word uic$w_grp; +}; + +struct fiddef { + f11word fid$w_num; + f11word fid$w_seq; + f11byte fid$b_rvn; + f11byte fid$b_nmx; +}; + +struct RECATTR { + f11byte fat$b_rtype; + f11byte fat$b_rattrib; + f11word fat$w_rsize; + f11swap fat$l_hiblk; + f11swap fat$l_efblk; + f11word fat$w_ffbyte; + f11byte fat$b_bktsize; + f11byte fat$b_vfcsize; + f11word fat$w_maxrec; + f11word fat$w_defext; + f11word fat$w_gbc; + f11byte fat$_UU0[8]; + f11word fat$w_versions; +}; + +struct HOME { + f11long hm2$l_homelbn; + f11long hm2$l_alhomelbn; + f11long hm2$l_altidxlbn; + f11word hm2$w_struclev; + f11word hm2$w_cluster; + f11word hm2$w_homevbn; + f11word hm2$w_alhomevbn; + f11word hm2$w_altidxvbn; + f11word hm2$w_ibmapvbn; + f11long hm2$l_ibmaplbn; + f11long hm2$l_maxfiles; + f11word hm2$w_ibmapsize; + f11word hm2$w_resfiles; + f11word hm2$w_devtype; + f11word hm2$w_rvn; + f11word hm2$w_setcount; + f11word hm2$w_volchar; + struct UIC hm2$w_volowner; + f11long hm2$l_reserved1; + f11word hm2$w_protect; + f11word hm2$w_fileprot; + f11word hm2$w_reserved2; + f11word hm2$w_checksum1; + VMSTIME hm2$q_credate; + f11byte hm2$b_window; + f11byte hm2$b_lru_lim; + f11word hm2$w_extend; + VMSTIME hm2$q_retainmin; + VMSTIME hm2$q_retainmax; + VMSTIME hm2$q_revdate; + f11byte hm2$r_min_class[20]; + f11byte hm2$r_max_class[20]; + f11byte hm2$t_reserved3[320]; + f11long hm2$l_serialnum; + char hm2$t_strucname[12]; + char hm2$t_volname[12]; + char hm2$t_ownername[12]; + char hm2$t_format[12]; + f11word hm2$w_reserved4; + f11word hm2$w_checksum2; +}; + +struct IDENT { + char fi2$t_filename[20]; + f11word fi2$w_revision; + VMSTIME fi2$q_credate; + VMSTIME fi2$q_revdate; + VMSTIME fi2$q_expdate; + VMSTIME fi2$q_bakdate; + char fi2$t_filenamext[66]; +}; + +struct HEAD { + f11byte fh2$b_idoffset; + f11byte fh2$b_mpoffset; + f11byte fh2$b_acoffset; + f11byte fh2$b_rsoffset; + f11word fh2$w_seg_num; + f11word fh2$w_struclev; + struct fiddef fh2$w_fid; + struct fiddef fh2$w_ext_fid; + struct RECATTR fh2$w_recattr; + f11long fh2$l_filechar; + f11word fh2$w_reserved1; + f11byte fh2$b_map_inuse; + f11byte fh2$b_acc_mode; + struct UIC fh2$l_fileowner; + f11word fh2$w_fileprot; + struct fiddef fh2$w_backlink; + f11byte fh2$b_journal; + f11byte fh2$b_ru_active; + f11word fh2$w_reserved2; + f11long fh2$l_highwater; + f11byte fh2$b_reserved3[8]; + f11byte fh2$r_class_prot[20]; + f11byte fh2$r_restofit[402]; + f11word fh2$w_checksum; +}; + +struct SCB { + f11word scb$w_struclev; + f11word scb$w_cluster; + f11long scb$l_volsize; + f11long scb$l_blksize; + f11long scb$l_sectors; + f11long scb$l_tracks; + f11long scb$l_cylinders; + f11long scb$l_status; + f11long scb$l_status2; + f11word scb$w_writecnt; + char scb$t_volockname[12]; + VMSTIME scb$q_mounttime; + f11word scb$w_backrev; + f11long scb$q_genernum[2]; + char scb$b_reserved[446]; + f11word scb$w_checksum; +}; + +struct VOLSETREC { + char vsr$t_label[12]; + char vsr$b_reserved[52]; +}; + +struct dir$r_rec { + f11word dir$w_size; + f11word dir$w_verlimit; + f11byte dir$b_flags; + f11byte dir$b_namecount; + char dir$t_name[1]; +}; + +struct dir$r_ent { + f11word dir$w_version; + struct fiddef dir$w_fid; +}; + + +#ifdef __ALPHA +#pragma member_alignment restore +#endif + +#endif diff --git a/extracters/ods2/fibdef.h b/extracters/ods2/fibdef.h index 99402fe..d3b7bd7 100644 --- a/extracters/ods2/fibdef.h +++ b/extracters/ods2/fibdef.h @@ -1,4 +1,4 @@ -/* Fibdef.h V2.1 Definition of 'struct fibdef' */ +/* Fibdef.h Definition of 'struct fibdef' */ /* This is part of ODS2 written by Paul Nankervis, @@ -29,6 +29,7 @@ struct fibdef { unsigned fib$w_nmctl; unsigned fib$l_exsz; unsigned fib$w_exctl; + unsigned short fib$w_verlimit; }; #endif /* #ifndef _FIBDEF_H */ diff --git a/extracters/ods2/helpcmd.c b/extracters/ods2/helpcmd.c new file mode 100644 index 0000000..646d9b7 --- /dev/null +++ b/extracters/ods2/helpcmd.c @@ -0,0 +1,344 @@ +/* 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. + */ + +#if !defined( DEBUG ) && defined( DEBUG_HELPCMD ) +#define DEBUG DEBUG_HELPCMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + +extern void mounthelp(void); + +extern CMDSET_t maincmds[]; +extern void show_version( void ); + +static void cmdhelp( CMDSETp_t cmdset ); +static void parhelp( CMDSETp_t cmd, int argc, char **argv ); +static void qualhelp( int par, qualp_t qtable ); + +static int cmdcmp( const void *qa, const void *qb ); +static int qualcmp( const void *qa, const void *qb ); + +/******************************************************************* dohelp() */ + +/* help: Display help guided by command table. */ + +param_t helppars[] = { {"command", OPT, CMDNAM, PA(maincmds), "" }, + {"parameter", OPT, STRING, NOPA, "" }, + {"value", OPT, STRING, NOPA, "" }, + {NULL, 0, 0, NOPA, NULL }, +}; + +DECL_CMD(help) { + CMDSETp_t cmd; + + UNUSED(qualc); + UNUSED(qualv); + + if( argc <= 1 ) { + show_version(); + + printf( " The orginal version of ods2 was developed by Paul Nankervis\n" ); + printf( " \n" ); + printf( " This modified version was developed by Timothe Litt, and\n" ); + printf( " incorporates significant previous modifications by Larry \n" ); + printf( " Baker of the USGS and by Hunter Goatley\n" ); + printf("\n Please send problems/comments to litt@ieee.org\n\n"); + + printf(" Commands are:\n"); + cmdhelp( maincmds ); + printf( "\n Type HELP COMMAND for information on any command.\n" ); + return 1; + } + + for( cmd = maincmds; 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 ) + mounthelp(); + return 1; + } + } + printf( "%s: command not found\n", argv[1] ); + return 0; +} + +/*********************************************************** cmdhelp() */ + +static void cmdhelp( CMDSETp_t cmdset ) { + CMDSETp_t cmd; + int n = 0; + size_t max = 0; + + for( cmd = cmdset; cmd->name != NULL; cmd++ ) { + if( strlen(cmd->name) > max ) + max = strlen(cmd->name); + } + qsort( cmdset, (size_t)(cmd - cmdset), sizeof( CMDSET_t ), cmdcmp ); + + 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" ); +} + +/*********************************************************** parhelp() */ + +static void parhelp( CMDSETp_t cmd, int argc, char **argv ) { + paramp_t p; + paramp_t 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( p->helpstr ) { + size_t len = strlen(p->name); + if( len > max ) + max = len; + } + } + for( p = ptable; p->name != NULL; p++ ) { + if( 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 %s PARAMETER for more about each parameter.\n", + cmd->name ); + 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, (qualp_t )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( (CMDSETp_t )p->arg ); + break; + default: + abort(); + } + if( footer ) + printf( "%s", footer ); + + return; +} + +/*********************************************************** qualhelp() */ + +static void qualhelp( int par, qualp_t qtable ) { + qualp_t q; + int n = 0; + size_t max = 0, col = 4; + + 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( q->qtype == DECVAL ) + ++len; + if( len > max ) + max = len; + } + } + + if( !(q->set & OPT_NOSORT) ) { + qsort( qtable, (size_t)(q - qtable), sizeof( qual_t ), qualcmp ); + } + + 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: + if( *q->helpstr ) { + printf( NOSTR "%-*s - %s\n", + (int) (max-NOSTR_LEN), q->name, q->helpstr+1 ); + break; + } + if( col + max > 50 ) { + printf( "\n " ); + col = 8; + } else { + while( col < 8 ) { + putchar( ' ' ); col++; + } + } + printf( "%-*s", (int) max, q->name ); + col += max; + break; + case KEYVAL: + case KEYCOL: + printf( NOSTR "%s=%-*s - %s\n", + q->name, (int) (max-(NOSTR_LEN+strlen(q->name)+1)), "", + q->helpstr+1 ); + qualhelp( q->qtype == KEYCOL? 1 : -(int)max, (qualp_t )q->arg ); + break; + case DECVAL: + printf( NOSTR "%s=n%-*s - %s\n", + q->name, (int) (max-(NOSTR_LEN+strlen(q->name)+2)), "", + q->helpstr+1 ); + break; + case STRVAL: + printf( NOSTR "%s=%-*s - %s\n", + q->name, (int) (max-(NOSTR_LEN+strlen(q->name)+2)), "", + q->helpstr+1 ); + break; + default: + abort(); + } + else + switch( q->qtype ) { + case NOVAL: + if( *q->helpstr ) { + printf("%-*s - %s\n", (int) max, q->name, q->helpstr ); + break; + } + if( col + max > 50 ) { + printf( "\n " ); + col = 8; + } else { + while( col < 8 ) { + putchar( ' ' ); col++; + } + } + printf( "%-*s", (int) max, q->name ); + col += max; + break; + case KEYVAL: + case KEYCOL: + printf( "%s=%-*s - %s\n", q->name, (int)(max-(strlen(q->name)+1)), "", + q->helpstr ); + qualhelp( q->qtype == KEYCOL? 1 : -(int)max, (qualp_t )q->arg ); + break; + case DECVAL: + printf( "%s=n%-*s - %s\n", + q->name, (int) (max-(strlen(q->name)+2)), "", + q->helpstr ); + break; + case STRVAL: + printf( "%s=%-*s - %s\n", + q->name, (int) (max-(strlen(q->name)+1)), "", + q->helpstr ); + break; + default: + abort(); + } + } + } + if( par >= 0 ) + printf( "\n" ); +} +#undef NOSTR +#undef NOSTR_LEN + +/***************************************************************** cmdcmp() */ + +static int cmdcmp( const void *qa, const void *qb ){ + + return strcmp( ((CMDSETp_t )qa)[0].name, + ((CMDSETp_t )qb)[0].name ); +} + +/***************************************************************** qualcmp() */ + +static int qualcmp( const void *qa, const void *qb ) { + + return strcmp( ((qualp_t )qa)[0].name, + ((qualp_t )qb)[0].name ); +} diff --git a/extracters/ods2/importcmd.c b/extracters/ods2/importcmd.c new file mode 100644 index 0000000..303be14 --- /dev/null +++ b/extracters/ods2/importcmd.c @@ -0,0 +1,119 @@ +/* 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. + */ + +#if !defined( DEBUG ) && defined( DEBUG_IMPORTCMD ) +#define DEBUG DEBUG_IMPORTCMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + + +/***************************************************************** doimport() */ + +#define import_binary OPT_GENERIC_1 +#define import_log OPT_GENERIC_2 + +qual_t importquals[] = { {"ascii", 0, import_binary, NV, + "Transfer file in ascii mode (default)"}, + {"binary", import_binary, 0, NV, "Transfer file in binary mode", }, + {NULL, 0, 0, NV, NULL} +}; +param_t importpars[] = { {"from_filespec", REQ, LCLFS, NOPA, "for source file."}, + {"to_filespec", REQ, VMSFS, NOPA, "for destination file."}, + { NULL, 0, 0, NOPA, NULL } +}; + +DECL_CMD(import) { + int options; + unsigned sts; + char *name; + FILE *fromf; + struct FAB fab = cc$rms_fab; + struct RAB rab = cc$rms_rab; + + UNUSED(argc); + + options = checkquals( 0, importquals, qualc, qualv ); + if( options == -1 ) + return SS$_BADPARAM; + + sts = SS$_BADFILENAME; + name = argv[1]; /*unquote( argv[1] );*/ + if ( *name == '\0' ) { + return sts; + } + fromf = openf( name, (options & import_binary)? "rb": "r" ); + if( fromf == NULL ) { + printf( "%%ODS2-E-OPENERR, Can't open %s\n", name ); + perror( " - " ); + return sts; + } + fab.fab$b_fac = FAB$M_PUT; + fab.fab$l_fop = FAB$M_OFP | FAB$M_TEF; + fab.fab$b_org = FAB$C_SEQ; + if( options & import_binary ) { + fab.fab$w_mrs = 512; + fab.fab$b_rfm = FAB$C_FIX; + } else { + fab.fab$b_rat = 0; + fab.fab$b_rfm = FAB$C_STM; + } + + fab.fab$l_fna = argv[2]; + fab.fab$b_fns = strlen( fab.fab$l_fna ); + sts = sys_create( &fab ); + if ( sts & STS$M_SUCCESS ) { + rab.rab$l_fab = &fab; + sts = sys_connect( &rab ); + if ( sts & STS$M_SUCCESS ) { + if( options & import_binary ) { + char *buf; + size_t len; + + if( (buf = malloc( 512 )) == NULL ) + sts = SS$_INSFMEM; + else { + rab.rab$l_rbf = buf; + while( (len = fread( buf, 1, 512, fromf )) > 0 ) { + if( len != 512 ) + memset( buf + len, 0, 512 - len ); + rab.rab$w_rsz = len; + sts = sys_put( &rab ); + if( !(sts & STS$M_SUCCESS) ) { + break; + } + } + free( buf ); + buf = NULL; + } + } else { + while ( (rab.rab$l_rbf = fgetline( fromf, TRUE )) != NULL ) { + rab.rab$w_rsz = strlen( rab.rab$l_rbf ); + sts = sys_put( &rab ); + free( rab.rab$l_rbf ); + if ( !( sts & STS$M_SUCCESS ) ) { + break; + } + } + } + sys_disconnect( &rab ); + } + sys_close( &fab ); + } + fclose( fromf ); + if ( !(sts & STS$M_SUCCESS) ) { + printf("%%IMPORT-F-ERROR Status: %s\n",getmsg(sts, MSG_TEXT)); + } + + return sts; +} diff --git a/extracters/ods2/initialcmd.c b/extracters/ods2/initialcmd.c new file mode 100644 index 0000000..f680417 --- /dev/null +++ b/extracters/ods2/initialcmd.c @@ -0,0 +1,161 @@ +/* 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. + */ + +#if !defined( DEBUG ) && defined( DEBUG_INITIALCMD ) +#define DEBUG DEBUG_INITIALCMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + +#include "access.h" +#include "device.h" +#include "initvol.h" +#include "phyvirt.h" + +/******************************************************************* doinitial() */ + +static struct NEWVOL initpars; + +#define init_beg OPT_GENERIC_1 +#define init_mid OPT_GENERIC_2 +#define init_blk OPT_GENERIC_3 +#define init_mount OPT_GENERIC_4 + +qual_t idxkwds[] = { {"beginning", init_beg, init_mid|init_blk, NV, + "Place index file in lowest available LBNs"}, + {"block", init_blk, init_beg|init_mid, DV(&initpars.indexlbn), + "Place index file near specific LBN"}, + {"middle", init_mid, init_beg|init_blk, NV, + "Place index file near middle of volume (default)"}, + {NULL, 0, 0, NV, NULL} +}; + +qual_t iniquals[] = { {DT_NAME, 0, MOU_DEVTYPE, CV(NULL), "Drive type (DEC model name) "}, + {"cluster_size", 0, 0, DV(&initpars.clustersize), + "Cluster size (blocks)" }, + {"create", PHY_CREATE|MOU_VIRTUAL, 0, NV, "Create a new image file" }, + {"directories", 0, 0, DV(&initpars.directories), + "Directory entries to pre-allocate" }, + {"headers", 0, 0, DV(&initpars.headers), + "File headers to pre-allocate" }, + {"index", 0, init_beg|init_mid|init_blk, KV(idxkwds), + "Index file placement hint"}, + {"image", PHY_CREATE|MOU_VIRTUAL, 0, NV, + "Initialize a disk image file", }, + {"log", MOU_LOG, 0, NV, "-Show progress"}, + {"nolog", 0, MOU_LOG, NV, NULL}, + {"replace", MOU_VIRTUAL, PHY_CREATE, NV, + "Replace filesystem in existing image file" }, + {"mount", init_mount, 0, NV, "-Mount volume after initializing it"}, + {"nomount", 0, init_mount, NV, NULL }, + {"virtual", PHY_CREATE|MOU_VIRTUAL, 0, NV, NULL, }, + {"maximum_files", 0, 0, DV(&initpars.maxfiles), + "Maximum number of files volume can (ever) contain" }, + {"owner_uic", 0, 0, SV(&initpars.useruic), + "Volume owner's UIC, default [1,1]"}, + {"user_name", 0, 0, SV(&initpars.username), + "Volume owner's username, default yours"}, + {NULL, 0, 0, NV, NULL } }; + +param_t inipars[] = { {"device", REQ, LCLFS, NOPA, + "for device or disk image to be initialized.\n " + "ALL EXISTING DATA WILL BE LOST." + }, + {"labels", REQ, LCLFS, NOPA, "volume label for new volume" }, + { NULL, 0, 0, NOPA, NULL } +}; + +DECL_CMD(initial) { + int sts = SS$_NORMAL; + char *devname = NULL; + size_t len; + struct DEV *dev; + int options; + + UNUSED(argc); + + devname = argv[1]; + + options = checkquals( PHY_CREATE | init_mid | init_mount, iniquals, qualc, qualv ); + do { + + if( options == -1 ) { + sts = SS$_BADPARAM; + break; + } + options |= MOU_WRITE; + if( !(options & MOU_VIRTUAL) ) + options &= ~PHY_CREATE; + + if( options & init_mid ) + initpars.indexlbn = INIT_INDEX_MIDDLE; + if( options & init_beg ) + initpars.indexlbn = 0; + if( (options & init_blk) && initpars.indexlbn == INIT_INDEX_MIDDLE ) + initpars.indexlbn--; + + initpars.options = 0; + if( options & MOU_LOG ) + initpars.options |= INIT_LOG; + if( options & MOU_VIRTUAL ) + initpars.options |= INIT_VIRTUAL; + + initpars.devtype = disktype[(options & MOU_DEVTYPE) >> MOU_V_DEVTYPE].name; + + len = strlen( argv[2] ); + if( len > 12 || !*argv[2] || + strspn( argv[2], VOL_ALPHABET ) != len ) { + printf( "Volume label must be between 1 and 12 alphanumeric characters long\n" ); + sts = SS$_BADPARAM; + break; + } + initpars.label = argv[2]; + + if( !((sts = virt_open( &devname, options, &dev )) & STS$M_SUCCESS) ) + break; + + if( !(dev->access & MOU_WRITE) ) { + sts = SS$_WRITLCK; + virt_close( dev ); + break; + } + initpars.devnam = devname; + if( !((sts = initvol( dev, &initpars )) & STS$M_SUCCESS) ) { + virt_close( dev ); + break; + } + if( !((sts = virt_close( dev )) & STS$M_SUCCESS) ) + break; + + if( options & init_mount ) { + sts = mount( (options & (MOU_VIRTUAL|MOU_DEVTYPE|MOU_WRITE)) | MOU_LOG, + 1, argv+1, argv+2 ); + } + } while( 0 ); + + initpars.username = + initpars.useruic = NULL; + + if( sts & STS$M_SUCCESS ) + return sts; + + printf( "%%ODS2-E-FAILED, Initialize failed: %s\n", getmsg( sts, MSG_TEXT ) ); + + if( (options & (MOU_VIRTUAL|PHY_CREATE)) == (MOU_VIRTUAL|PHY_CREATE) && + !( (sts & STS$M_COND_ID) == (SS$_DUPFILENAME & STS$M_COND_ID) || + (sts & STS$M_COND_ID) == (SS$_IVDEVNAM & STS$M_COND_ID) ) ) { + (void) unlink( argv[1] ); + } + + return sts; +} diff --git a/extracters/ods2/initvol.c b/extracters/ods2/initvol.c new file mode 100644 index 0000000..01caa13 --- /dev/null +++ b/extracters/ods2/initvol.c @@ -0,0 +1,1264 @@ +/* Initialize a FILES-11 volume */ + +/* Timothe Litt March 2016 + * Copyright (C) 2016 Timothe litt + * litt at acm dot org + * + * Free for use with the ODS2 package. All other rights reserved. + */ + +/* + * This is distributed as part of ODS2, originally 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. + */ + +#if !defined( DEBUG ) && defined( DEBUG_INITVOL ) +#define DEBUG DEBUG_INITVOL +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "compat.h" + +#include "f11def.h" +#include "initvol.h" +#include "ods2.h" +#include "phyvirt.h" +#include "rms.h" +#include "stsdef.h" +#include "vmstime.h" + +#define bootlbn 0 + +#define STRUCLEV (0x0201) +#define FM2$M_FORMAT1 (1 << 14) +#define FM2$M_FORMAT2 (2 << 14) +#define FM2$M_FORMAT3 (3 << 14) +#define FM2$C_FMT1_MAXLBN ((1 << 22) - 1) +#define FM2$C_FMT1_MAXCNT (256) +#define FM2$C_FMT2_MAXLBN (0xFFFFFFFF) +#define FM2$C_FMT2_MAXCNT (1 << 14) +#define FM2$C_FMT3_MAXCNT (1 << 30) + +#define CLUSTERS(lbns) ( ((lbns) + clustersize - 1) / clustersize ) +#define CLU2LBN(cluster) ( (cluster) * clustersize ) + +#if DEBUG +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +static unsigned add2dir( void *mfdp, unsigned *mfdlen, unsigned mfdblocks, + struct HEAD *head ); + +static unsigned add2map( struct NEWVOL *params, struct HEAD *fhd, + unsigned start, unsigned n ); + +static unsigned writeHEAD( void *dev, struct HEAD *head, unsigned hdrbase ) ; +static unsigned writeHDR( void *dev, struct HEAD *head, unsigned lbn ); +static unsigned writeHOM( void *dev, + struct HOME *hom, + unsigned homlbn, unsigned homvbn, + unsigned ncopies ); +static unsigned alloclbns( unsigned char *bitmap, + unsigned clustersize, + unsigned n_clusters, + unsigned startlbn, unsigned n, + unsigned *found ); +static int tstlbnbit( struct NEWVOL *params, unsigned char *bitmap, unsigned lbn ); +static void modlbnbits( struct NEWVOL *params, unsigned char *bitmap, int set, unsigned start, unsigned n ); +static void modbits( unsigned char *bitmap, int set, unsigned start, unsigned n ); +static f11word checksum( void *data, size_t length ); +#if 0 +static unsigned int delta_from_name( const char *diskname ); +#endif +static unsigned int compute_delta( unsigned long sectorsize, + unsigned long sectors, + unsigned long tracks, + unsigned long cylinders ); + +static void *Malloc( void ***list, size_t size ); +static void *Calloc( void ***list, size_t nmemb, size_t n ); +#define MALLOC(name, size) do { \ + if( (name = Malloc( size )) == NULL ) \ + return SS$_INSFMEM; \ + } while( 0 ) +#define CALLOC(name, nmemb, n) do { \ + if( (name = Calloc( nmemb, n )) == NULL ) \ + return SS$_INSFMEM; \ + } while( 0 ) + +unsigned eofmark = 0; + +#define WRITE(dev, lbn, n, buf) do { \ + unsigned status; \ + \ + if( !((status = virt_write( dev, (lbn), ((n)*512), (char *)(buf) )) & STS$M_SUCCESS) ) \ + return status; \ + if( ( (lbn) + (n) ) > eofmark ) \ + eofmark = (lbn) + (n) -1; \ + } while( 0 ); + +#define WRITEP(dev, pbn, n, buf) do { \ + unsigned status; \ + \ + if( !((status = virt_writep( dev, (pbn), ((n)*dp->sectorsize), (char *)(buf) )) & STS$M_SUCCESS) ) \ + return status; \ + } while( 0 ); + +#define READ(dev, lbn, n, buf) do { \ + unsigned status; \ + \ + if( !((status = virt_read( dev, (lbn), ((n)*512), (char *)(buf) )) & STS$M_SUCCESS) ) \ + return status; \ + } while( 0 ); + +#define READP(dev, pbn, n, buf) do { \ + unsigned status; \ + \ + if( !((status = virt_readp( dev, (pbn), ((n)*dp->sectorsize), (char *)(buf) )) & STS$M_SUCCESS) ) \ + return status; \ + } while( 0 ); + +static unsigned doinit( void ***memlist, void *dev, struct NEWVOL *params ); + +static uint16_t bootcode[] = { /* EHPLI M'T ARPPDEI SNDI E ADP-P11 */ + 000240, 012706, 001000, 010700, 062700, 000036, 0112001, 001403, + 004767, 000006, 000773, 000005, 000000, 0110137, 0177566, 0105737, + 0177564, 0100375, 000207, 020040, 020040, 020040, 020040, 020040, + 020040, 020040, 020040, 071551, 067040, 072157, 060440, 071440, + 071571, 062564, 020155, 064544, 065563, 005015, 000012, 000000, +#define BOOT_LABEL_OFS 046 +#define BOOT_LABEL_SIZE 15 +}; + +unsigned initvol( void *dev, struct NEWVOL *params ) { + void **memlist, **mp; + unsigned status; + memlist = (void **)calloc( 1, sizeof( void * ) ); + + if( params->options & INIT_LOG ) { + printf( "%%INIT-I-STARTING, Initializing %s on %s as %s %s\n", + params->label, params->devnam, params->devtype, + (params->options & INIT_VIRTUAL)? "disk image": "physical device" ); + } + + status = doinit( &memlist, dev, params ); + + for( mp = memlist; *mp != NULL; mp++ ) + free( *mp ); + free( memlist ); + + if((status & STS$M_SUCCESS) && (params->options & INIT_LOG) ) { + printf( "%%INIT-I-VOLINIT, %s initialized successfully\n", + params->label ); + } + + return status; +} + +static void *Calloc( void ***list, size_t nmemb, size_t n ) { + void *mp; + + n *= nmemb; + mp = Malloc( list, n ); + if( mp == NULL ) + return NULL; + memset( mp, 0, n ); + return mp; +} + +static void *Malloc( void ***memlist, size_t size ) { + void **mp; + size_t n; + for( n = 0, mp = *memlist; *mp != NULL; n++, mp++ ) + ; + mp = realloc( *memlist, (n + 2) * sizeof( void * ) ); + if( mp == NULL ) + return NULL; + *memlist = mp; + mp[n] = malloc( size ); + if( mp[n] == NULL ) + return NULL; + mp[n+1] = NULL; + return mp[n]; +} +#define Malloc( size ) Malloc( memlist, size ) +#define Calloc( nmemb, size ) Calloc( memlist, nmemb, size ) + +static unsigned doinit( void ***memlist, void *dev, struct NEWVOL *params ) { + struct disktype *dp = NULL; + struct HOME *hom = NULL; + struct SCB *scb = NULL; + struct HEAD + *idxhead = NULL, + *badhead = NULL, + *corhead = NULL, + *volhead = NULL, + *conthead = NULL, + *bakhead = NULL, + *loghead = NULL, + *bmphead = NULL, + *mfdhead = NULL; + struct IDENT *ident = NULL; + struct BAD144 *mbad = NULL; + struct SWBAD *sbad = NULL; + unsigned status, tmp, volstart, clustersize; + unsigned pbs, pb_per_lb, lb_per_pb, npb, nlb, n_clusters; + unsigned homlbn, homvbn, hm2lbn, hm2cluster, fhlbn; + unsigned mfdlbn, bmplbn, idxlbn, aidxlbn; + const char *p = NULL; + unsigned char *bitmap = NULL, *indexmap = NULL; + unsigned bitmap_size, indexmap_size; + uint16_t *bootblock = NULL; + time_t serial; + char tbuf[12+1]; + char *mfdp = NULL; + unsigned mfdlen = 0, mfdblocks = 3, mfdalloc; + struct UIC owneruic = { 1, 1 }; + + if( params->username && (!params->username[0] || + (tmp = strlen( params->username )) > 12 || + strspn( params->username, VOL_ALPHABET ) != tmp) ) + return SS$_BADPARAM; + + if( params->useruic && +#ifdef _WIN32 + sscanf_s +#else + sscanf +#endif + ( params->useruic, " [%ho,%ho]", &owneruic.uic$w_grp, &owneruic.uic$w_mem ) != 2 ) + return SS$_BADPARAM; + + for( dp = disktype; dp->name != NULL; dp++ ) { + if( !strcmp( params->devtype, dp->name ) ) + break; + + } + if( dp->name == NULL ) + return SS$_BADPARAM; + + pbs = dp->sectorsize; + pb_per_lb = (pbs < 512)? 512/pbs: 1; + lb_per_pb = (pbs >512)? pbs/512: 1; + + npb = (dp->sectors * dp->tracks * dp->cylinders ) - dp->reserved; + nlb = (unsigned) ((((unsigned long long)npb) * lb_per_pb) + / pb_per_lb); + + if( params->clustersize == 0 ) { + if( nlb < 50000 ) + params->clustersize = 1; + else { + unsigned long long tmp; + tmp = (nlb + (255 * 4096) - 1) / (255 * 4096); + + params->clustersize = (unsigned) (tmp > 3? tmp: 3); + } + } + tmp = nlb / 50; + if( tmp > 16382 ) + tmp = 16382; + if( params->clustersize > tmp ) + params->clustersize = tmp; + + clustersize = params->clustersize; + + if( npb < 100 || nlb < 100 || clustersize < 1 ) { + printf( "%%INIT-E-TOOSMALL, Device is too small to hold a useful Files-11 volume\n" ); + return SS$_DEVICEFULL; + } + + n_clusters = CLUSTERS( nlb ); + + if( params->maxfiles < 16 ) { + params->maxfiles = nlb / ((clustersize +1) * 2); + if( params->maxfiles < 16 ) + params->maxfiles = 16; + } + tmp = nlb / (clustersize + 1 ); + if( params->maxfiles > tmp ) + params->maxfiles = tmp; + + if( params->maxfiles > ((1 << 24) - 1) ) + params->maxfiles = ((1 << 24) - 1); + + if( params->headers < 16 ) + params->headers = 16; + if( params->headers > 65500 ) + params->headers = 65500; + + if( params->headers > params->maxfiles ) + params->headers = params->maxfiles; + + if( params->directories > 16000 ) + params->directories = 16000; + if( params->directories > params->maxfiles ) + params->directories = params->maxfiles; + if( params->directories < 16 ) + params->directories = 16; + + mfdalloc = params->directories / 16; + mfdalloc = CLU2LBN( CLUSTERS( mfdalloc ) ); + if( mfdalloc < mfdblocks ) + mfdalloc = mfdblocks; + + (void) time( &serial ); + + /* Create the bootblock. (This is PDP-11 code. ) + */ + + CALLOC( bootblock, 256, sizeof( uint16_t ) ); + memcpy( bootblock, bootcode, sizeof( bootcode ) ); + + for( p = params->label, tmp = 0; *p && tmp < BOOT_LABEL_SIZE; tmp++, p++ ) + ((char *)bootblock)[BOOT_LABEL_OFS+tmp] = toupper(*p); +#ifdef ODS2_BIG_ENDIAN + for( tmp = 0; tmp < 256; tmp++ ) + bootblock[tmp] = F11WORD(bootblock[tmp]); +#endif + +#define ALLHEAD(name) \ + CALLOC( name, 1, sizeof(struct HEAD) ) + + ALLHEAD( idxhead ); + ALLHEAD( badhead ); + ALLHEAD( corhead ); + ALLHEAD( volhead ); + ALLHEAD( conthead ); + ALLHEAD( bakhead ); + ALLHEAD( loghead ); + ALLHEAD( bmphead ); + ALLHEAD( mfdhead ); + + CALLOC(hom, 1, sizeof( struct HOME ) ); + + hom->hm2$w_struclev = F11WORD( STRUCLEV ); + hom->hm2$w_cluster = F11WORD( clustersize ); + hom->hm2$w_resfiles = F11WORD( 9 ); + hom->hm2$l_maxfiles = F11LONG( params->maxfiles ); + hom->hm2$w_volowner.uic$w_grp = F11WORD( owneruic.uic$w_grp ); + hom->hm2$w_volowner.uic$w_mem = F11WORD( owneruic.uic$w_mem ); + hom->hm2$w_protect = F11WORD( 0 ); + hom->hm2$w_fileprot = F11WORD( 0xFA00 ); + hom->hm2$b_window = 7; + hom->hm2$b_lru_lim = 16; + hom->hm2$w_extend = F11WORD( 5 ); + + CALLOC( scb, 1, 512 ); + scb->scb$w_cluster = F11WORD(clustersize); + scb->scb$w_struclev = F11WORD(STRUCLEV); + scb->scb$l_volsize = F11LONG(nlb); + scb->scb$l_blksize = F11LONG(pb_per_lb); + scb->scb$l_sectors = F11LONG(dp->sectors); + scb->scb$l_tracks = F11LONG(dp->tracks); + scb->scb$l_cylinders = F11LONG(dp->cylinders); + scb->scb$l_status = F11LONG( 0 ); + scb->scb$l_status = F11LONG( 0 ); + scb->scb$w_writecnt = 0; + + /* If necessary, scb$t_volockname will be initialized by mount under + * an OS. + */ + + if( !((status = sys$gettim( scb->scb$q_mounttime )) & STS$M_SUCCESS) ) { + return status; + } + scb->scb$w_checksum = F11WORD(checksum((f11word *)&scb, offsetof( struct SCB, scb$w_checksum ) / 2 ) ); + + bitmap_size = (n_clusters + 4095) / 4096; + + indexmap_size = (params->maxfiles + 4095) / 4096; + hom->hm2$l_maxfiles = F11LONG( params->maxfiles ); + memcpy( hom->hm2$q_credate, scb->scb$q_mounttime, sizeof(hom->hm2$q_credate) ); + memcpy( hom->hm2$q_revdate, scb->scb$q_mounttime, sizeof(hom->hm2$q_revdate) ); + + DPRINTF(("Total LBNs: %u cluster size: %u total clusters: %u, " + "bitmap size: %u blocks maxfiles %u index map size %u blocks\n", + nlb, clustersize, n_clusters, bitmap_size, params->maxfiles, + indexmap_size)); + + hom->hm2$l_serialnum = (f11long) F11LONG( serial ); + memset( hom->hm2$t_strucname, ' ', sizeof( hom->hm2$t_strucname ) ); + (void) snprintf( tbuf, sizeof( tbuf), "%-12.12s", params->label ); + for( tmp = 0; tmp < sizeof( tbuf ); tmp++ ) + tbuf[tmp] = toupper( tbuf[tmp] ); + memcpy( hom->hm2$t_volname, tbuf, sizeof( hom->hm2$t_volname ) ); + p = get_username(); + (void) snprintf( tbuf, sizeof( tbuf), "%-12.12s", + (params->username? params->username: (p == NULL)? "": p) ); + free( (void *)p ); + for( tmp = 0; tmp < sizeof( tbuf ); tmp++ ) + tbuf[tmp] = toupper( tbuf[tmp] ); + memcpy( hom->hm2$t_ownername, tbuf, sizeof( hom->hm2$t_ownername ) ); + (void) snprintf( tbuf, sizeof( tbuf ), "%-12.12s", "DECFILE11B" ); + memcpy( hom->hm2$t_format, tbuf, sizeof( hom->hm2$t_format ) ); + + CALLOC( indexmap, 1, indexmap_size * 512 ); + + hom->hm2$w_ibmapsize = F11WORD( (f11word)indexmap_size ); + + /* Index header will be template for all other files. + * Index-specific settings must come after copy. + */ + + idxhead->fh2$b_idoffset = offsetof( struct HEAD, fh2$r_restofit ) / 2; + idxhead->fh2$b_acoffset = offsetof( struct HEAD, fh2$w_checksum ) / 2; + idxhead->fh2$b_rsoffset = offsetof( struct HEAD, fh2$w_checksum ) / 2; + + idxhead->fh2$w_seg_num = F11WORD( 0 ); + idxhead->fh2$w_struclev = F11WORD( STRUCLEV ); + + idxhead->fh2$w_fid.fid$b_rvn = 0; + idxhead->fh2$w_fid.fid$b_nmx = 0; + idxhead->fh2$w_ext_fid.fid$w_num = F11WORD( 0 ); + idxhead->fh2$w_ext_fid.fid$w_seq = F11WORD( 0 ); + idxhead->fh2$w_ext_fid.fid$b_rvn = 0; + idxhead->fh2$w_ext_fid.fid$b_nmx = 0; + + idxhead->fh2$w_recattr.fat$l_hiblk = F11LONG( 0 ); + idxhead->fh2$w_recattr.fat$l_efblk = F11LONG( 0 ); + idxhead->fh2$w_recattr.fat$w_ffbyte = F11WORD( 0 ); + idxhead->fh2$w_recattr.fat$b_bktsize = 0; + idxhead->fh2$w_recattr.fat$b_vfcsize = 0; + idxhead->fh2$w_recattr.fat$w_defext = F11WORD( 0 ); + idxhead->fh2$w_recattr.fat$w_gbc = F11WORD( 0 ); + idxhead->fh2$w_recattr.fat$w_versions = F11WORD( 0 ); + idxhead->fh2$l_filechar = F11LONG( 0 ); + idxhead->fh2$b_map_inuse = 0; + idxhead->fh2$b_acc_mode = 0; + idxhead->fh2$l_fileowner.uic$w_mem = F11WORD( 1 ); + idxhead->fh2$l_fileowner.uic$w_grp = F11WORD( 1 ); + idxhead->fh2$w_fileprot = F11WORD( 0xFA00 ); + idxhead->fh2$w_backlink.fid$w_num = F11WORD( 4 ); + idxhead->fh2$w_backlink.fid$w_seq = F11WORD( 4 ); + idxhead->fh2$w_backlink.fid$b_rvn = 0; + idxhead->fh2$w_backlink.fid$b_nmx = 0; + idxhead->fh2$l_highwater = F11LONG( 1 ); + +#define IDENTp(head) ((struct IDENT *) (((f11word *)head) + F11WORD(head->fh2$b_idoffset))) + ident = IDENTp(idxhead); + + ident->fi2$w_revision = F11WORD( 1 ); + memcpy( ident->fi2$q_credate, scb->scb$q_mounttime, sizeof(ident->fi2$q_credate) ); + memcpy( ident->fi2$q_revdate, scb->scb$q_mounttime, sizeof(ident->fi2$q_credate) ); + idxhead->fh2$b_mpoffset =F11WORD( F11WORD(idxhead->fh2$b_idoffset) + + (offsetof( struct IDENT, fi2$t_filenamext ) / 2) ); + + memcpy( badhead, idxhead, sizeof( *badhead ) ); + memcpy( corhead, idxhead, sizeof( *corhead ) ); + memcpy( volhead, idxhead, sizeof( *volhead ) ); + memcpy( conthead, idxhead, sizeof( *conthead ) ); + memcpy( bakhead, idxhead, sizeof( *bakhead ) ); + memcpy( loghead, idxhead, sizeof( *loghead ) ); + memcpy( bmphead, idxhead, sizeof( *bmphead ) ); + memcpy( mfdhead, idxhead, sizeof( *mfdhead ) ); + + bmphead->fh2$l_filechar = F11LONG( FH2$M_CONTIG ); + mfdhead->fh2$l_filechar = F11LONG( FH2$M_DIRECTORY | FH2$M_CONTIG ); + mfdhead->fh2$w_fileprot &= ~F11WORD( 0x4000 ); /* W:E allowed for MFD */ + + mfdblocks = CLU2LBN( CLUSTERS( mfdblocks ) ); + CALLOC( mfdp, 1, mfdblocks * 512 ); + +#define SETFILE(head, num, seq, name, rtype, rattr, rsize ) do { \ + unsigned status; \ + struct IDENT *id; \ + char fname[sizeof( id->fi2$t_filename )+1]; \ + id = IDENTp(head); \ + (void) snprintf( fname, sizeof( fname ), "%-20.20s", #name ); \ + memcpy( id->fi2$t_filename, fname, sizeof( id->fi2$t_filename ) ); \ + head->fh2$w_fid.fid$w_num = F11WORD( num ); \ + head->fh2$w_fid.fid$w_seq = F11WORD( seq ); \ + head->fh2$w_recattr.fat$b_rtype = (FAB$C_SEQ << 4) | (rtype); \ + head->fh2$w_recattr.fat$b_rattrib = rattr; \ + head->fh2$w_recattr.fat$w_rsize = F11WORD( rsize ); \ + head->fh2$w_recattr.fat$w_maxrec = F11WORD( rsize ); \ + if( !((status = add2dir( mfdp, &mfdlen, mfdblocks, head )) & STS$M_SUCCESS) ) \ + return status; \ + } while( 0 ) + + /* Must be in alphabetical order for MFD */ + SETFILE( mfdhead, 4,4, 000000.DIR;1, FAB$C_VAR, FAB$M_BLK, 512 ); + SETFILE( bakhead, 8,8, BACKUP.SYS;1, FAB$C_FIX, 0, 64 ); + SETFILE( badhead, 3,3, BADBLK.SYS;1, FAB$C_FIX, 0, 512 ); + SETFILE( loghead, 9,9, BADLOG.SYS;1, FAB$C_FIX, 0, 16 ); + SETFILE( bmphead, 2,2, BITMAP.SYS;1, FAB$C_FIX, 0, 512 ); + SETFILE( conthead, 7,7, CONTIN.SYS;1, FAB$C_FIX, 0, 512 ); + SETFILE( corhead, 5,5, CORIMG.SYS;1, FAB$C_FIX, 0, 512 ); + SETFILE( idxhead, 1,1, INDEXF.SYS;1, FAB$C_FIX, 0, 512 ); + SETFILE( volhead, 6,6, VOLSET.SYS;1, FAB$C_FIX, 0, 64 ); + + modbits( indexmap, 1, 0, 9 ); + + MALLOC( bitmap, 512 * bitmap_size ); + memset( bitmap, 0xFF, 512 * bitmap_size ); + + DPRINTF(("Non-existent bitmap bits %u / %u LBNs %u - %u\n", + (bitmap_size *4096) - n_clusters, bitmap_size *4096, + n_clusters * clustersize, bitmap_size * 4096 * clustersize)); + modbits( bitmap, 0, n_clusters, (bitmap_size * 4096) - n_clusters ); + +#define ADDMAPENTRY(head, start, n ) do { \ + unsigned status; \ + \ + if( !((status = add2map( params, (head), start, n)) & STS$M_SUCCESS ) ) \ + return status; \ + } while( 0 ) + + if( dp->flags & DISK_BAD144 ) { + unsigned start, count; + + tmp = dp->sectors * dp->sectorsize; + + MALLOC( mbad, tmp * 10 ); + if( params->options & INIT_VIRTUAL ) { + memset( mbad->bbd$l_badblock, ~0, tmp ); + mbad->bbd$l_serial = (uint32_t) serial; + mbad->bbd$w_reserved = 0; + mbad->bbd$w_flags = 0; + for( count = 0; count < 10; count++ ) + memcpy( ((char *)mbad)+(count * dp->sectorsize), + mbad, dp->sectorsize ); + } + + /* Might have to deal with finding clusters more accurately + * for disks with interleave/skew. + */ + start = npb - 10; + start = (unsigned) ( ( ((off_t)tmp) * pb_per_lb ) / lb_per_pb ); + count = (unsigned) ( ( ((off_t)10) * lb_per_pb ) / pb_per_lb ); + + modlbnbits( params, bitmap, 0, start, count ); + ADDMAPENTRY( badhead, start, count ); + if( !(params->options & INIT_VIRTUAL) ) { + READP( dev, start, 10, mbad ); + /* *** TODO : Decode and add to bitmap/badblk.sys */ + } + } + if( dp->flags & DISK_BADSW ) { + CALLOC( sbad, 1, sizeof( struct SWBAD ) ); + if( params->options & INIT_VIRTUAL ) { + sbad->bbm$b_countsize = 1; + sbad->bbm$b_lbnsize = 3; + sbad->bbm$b_inuse = 0; + sbad->bbm$b_avail = (uint8_t)( ( offsetof( struct SWBAD, bbm$w_checksum ) - + offsetof( struct SWBAD, bbm$r_map ) ) / sizeof( uint8_t ) ); + } + modlbnbits( params, bitmap, 0, nlb - 1, 1 ); + ADDMAPENTRY( badhead, nlb-1, 1 ); + if( !(params->options & INIT_VIRTUAL) ) { + READ( dev, nlb-1, 1, sbad ); + /* *** TODO : Decode and add to bitmap/badblk.sys */ + } + } + + /* Make index file map entries (in increasing VBN order) + */ + + /* BOOT and HOM blocks. + * + * HOM1 and boot merge if clustersize >1 & primary HOM is good. + */ + + homlbn = 1; + tmp = 0; + + if( !tstlbnbit( params, bitmap, homlbn ) ) { + tmp = delta_from_index( dp - disktype ); + do { + int freelbn; + + freelbn = tstlbnbit( params, bitmap, homlbn ); + modlbnbits( params, bitmap, 0, homlbn, 1 ); + modlbnbits( params, bitmap, 0, bootlbn, 1 ); + + if( freelbn ) + break; + + /* add to bad blocks */ + + homlbn += tmp; + } while( homlbn < nlb ); + } + + if( homlbn >= nlb ) { + printf( "No good block found for HOM block\n" ); + return SS$_DEVICEFULL; + } + + modlbnbits( params, bitmap, 0, bootlbn, 1 ); + + + DPRINTF(("Boot block = %u, primary HOM block = %u\n", bootlbn, homlbn)); + + if( bootlbn == 0 && homlbn < clustersize * 2 ) { + modlbnbits( params, bitmap, 0, bootlbn, clustersize * 2); + ADDMAPENTRY( idxhead, bootlbn, clustersize * 2 ); + } else { + modlbnbits( params, bitmap, 0, bootlbn, clustersize ); + modlbnbits( params, bitmap, 0, homlbn, clustersize *2 ); + + ADDMAPENTRY( idxhead, bootlbn, clustersize ); + ADDMAPENTRY( idxhead, homlbn, clustersize * 2 ); + } + + /* Alternate HOM block + */ + + if( tmp == 0 ) + tmp = delta_from_index( dp - disktype ); + + hm2lbn = homlbn; + do { + int freelbn; + + hm2lbn += tmp; + freelbn = tstlbnbit( params, bitmap, hm2lbn ); + + modlbnbits( params, bitmap, 0, hm2lbn, 1 ); + if( freelbn && hm2lbn >= params->clustersize * 2 ) + break; + /* Add to bad blocks */ + } while( hm2lbn < nlb ); + + if( hm2lbn >= nlb ) { + printf( "No good block found for HOM block\n" ); + return SS$_DEVICEFULL; + } + + hm2cluster = hm2lbn / clustersize; + + DPRINTF(("Alternate HOM block is %u\n", hm2lbn)); + + ADDMAPENTRY( idxhead, CLU2LBN( hm2cluster ), 1 ); + + hom->hm2$l_alhomelbn = F11LONG( hm2lbn ); + + if( params->options & INIT_LOG ) { + printf( "%%INIT-I-SIZING, Volume size %u block, cluster size %us, maximum files %u\n", + nlb, clustersize, params->maxfiles ); + printf( " -I-SIZING, Preallocated %u file headers, %u directory entries\n", + params->headers, params->directories ); + printf( "%%INIT-I-PLACED, Boot block at LBN %u, HOM blocks at LBNs %u and %u\n", + bootlbn, homlbn, hm2lbn ); + } + + /* Variable placement. */ + + if( params->indexlbn == INIT_INDEX_MIDDLE ) + volstart = nlb / 2; + else + volstart = params->indexlbn; + +#define ALLOCLBNS( start, number, result ) do { \ + unsigned status; \ + if( !((status = alloclbns( bitmap, clustersize, n_clusters, \ + (start), (number), (result))) & STS$M_SUCCESS) ) \ + return status; \ + } while( 0 ) + + /* This order is recommended for best placement */ + + /* MFD */ + ALLOCLBNS( volstart, mfdalloc, &mfdlbn ); + + /* Storage bitmap */ + ALLOCLBNS( volstart, 1 + bitmap_size, &bmplbn ); + + /* Index bitmap and initial file headers. */ + ALLOCLBNS( volstart, indexmap_size + params->headers, &idxlbn ); + + /* Backup index header -- Could be up around +nlb /4... */ + ALLOCLBNS( volstart, 1, &aidxlbn ); + + hom->hm2$l_altidxlbn = F11LONG( aidxlbn ); + hom->hm2$w_altidxvbn = F11WORD( (clustersize * 3) +1 ); + + DPRINTF(("Backup index header %u\n", aidxlbn)); + ADDMAPENTRY( idxhead, aidxlbn, 1 ); + + fhlbn = idxlbn + indexmap_size; + hom->hm2$l_ibmaplbn = F11LONG( idxlbn ); + + DPRINTF(("Index bitmap LBN is %u, first extent %u\n", + idxlbn, indexmap_size+params->headers)); + + /* EOF is 1 block past the 9 used headers */ + + tmp = (clustersize * 4) + indexmap_size + 9 +1; + idxhead->fh2$w_recattr.fat$l_efblk = F11SWAP( tmp ); + idxhead->fh2$w_recattr.fat$w_ffbyte = F11WORD( 0 ); + idxhead->fh2$l_highwater = F11LONG( tmp ); + + ADDMAPENTRY( idxhead, idxlbn, indexmap_size + params->headers ); + + hom->hm2$w_ibmapvbn = F11WORD( (clustersize * 4) + 1 ); + + /* Accurate EOF is not kept for directories, reclen = 32767 is + * end marker in data. + */ +#if 0 + mfdhead->fh2$w_recattr.fat$l_efblk = F11SWAP( 1 + (mfdlen / 512) ); + mfdhead->fh2$w_recattr.fat$w_ffbyte = F11WORD( mfdlen % 512 ); +#else + mfdhead->fh2$w_recattr.fat$l_efblk = F11SWAP( 1 + ((mfdlen + 511) / 512) ); + mfdhead->fh2$w_recattr.fat$w_ffbyte = F11WORD( 0 ); +#endif + mfdhead->fh2$l_highwater = F11LONG( 1 + ((mfdlen + 511) / 512) ); + + ADDMAPENTRY( mfdhead, mfdlbn, mfdalloc ); + + /* Storage bitmap */ + + bmphead->fh2$w_recattr.fat$l_efblk = F11SWAP( 1 + bitmap_size + 1); + bmphead->fh2$w_recattr.fat$w_ffbyte = F11WORD( 0 ); + bmphead->fh2$l_highwater = F11LONG( 1 + bitmap_size + 1 ); + + ADDMAPENTRY( bmphead, bmplbn, 1 + bitmap_size ); + + if( params->options & INIT_LOG ) { + printf( "%%INIT-I-PLACED, MFD at LBN %u, index file at LBNs %u thru %u\n", + mfdlbn, idxlbn, idxlbn + indexmap_size + params->headers -1 ); + } + + /* Let's try for a spiral write... */ + + WRITE( dev, 0, 1, bootblock ); + + /* Write HOM blocks */ + + homvbn = 2; + hom->hm2$w_homevbn = F11WORD( homvbn ); + + DPRINTF(("Primary HOM block lbn %u vbn %u\n", homlbn, homvbn )); + + homvbn = (clustersize * 2) + 1; + hom->hm2$w_alhomevbn = F11WORD( homvbn ); + + tmp = (clustersize > 2)? /* Copies needed */ + (clustersize * 2) - 1: 1; + + if( !((status = writeHOM( dev, hom, homlbn, 2, tmp )) & STS$M_SUCCESS) ) + return status; + + DPRINTF(("Secondary HOM block lbn %u vbn %u\n", hm2lbn, homvbn)); + + if( !((status = writeHOM( dev, hom, hm2cluster * clustersize, homvbn, clustersize )) & STS$M_SUCCESS) ) + return status; + + /* MFD */ + + DPRINTF(("MFD at %u, %u blocks, len %u\n", mfdlbn, mfdalloc, mfdlen)); + + WRITE( dev, mfdlbn, mfdblocks, mfdp ); + + DPRINTF(("Writing BITMAP.SYS at %u, %u blocks\n", bmplbn, + 1 + bitmap_size)); + + WRITE( dev, bmplbn, 1, scb ); + WRITE( dev, bmplbn + 1, bitmap_size, bitmap ); + + DPRINTF(("Index file bitmap\n")); + WRITE( dev, idxlbn, indexmap_size, indexmap ); + +#define WRITEHEAD(head,name) do { \ + unsigned status; \ + head->fh2$w_checksum = checksum( (f11word *)(head), \ + offsetof( struct HEAD, fh2$w_checksum ) / 2 ); \ + DPRINTF(("Writing " #name " header\n")); \ + if( !((status = writeHEAD( dev, (head), fhlbn )) & STS$M_SUCCESS) ) \ + return status; \ + } while( 0 ) + + WRITEHEAD( idxhead, INDEXF.SYS;1 ); + WRITEHEAD( bmphead, BITMAP.SYS;1 ); + WRITEHEAD( badhead, BADBLK.SYS;1 ); + WRITEHEAD( mfdhead, 000000.DIR;1 ); + WRITEHEAD( corhead, CORIMG.SYS;1 ); + WRITEHEAD( volhead, VOLSET.SYS;1 ); + WRITEHEAD( conthead, CONTIN.SYS;1 ); + WRITEHEAD( bakhead, BACKUP.SYS;1 ); + WRITEHEAD( loghead, BADLOG.SYS;1 ); + + if( !((status = writeHDR( dev, idxhead, aidxlbn )) & STS$M_SUCCESS) ) + return status; + + if( params->options & INIT_VIRTUAL ) { + if( dp->flags & DISK_BAD144 ) { + if( params->options & INIT_LOG ) { + printf( "%%INIT-I-MFGBAD, writing manufacturer's bad block index\n" ); + } + WRITEP( dev, npb - 10, 10, mbad ); + } + if( dp->flags & DISK_BADSW ) { + if( params->options & INIT_LOG ) { + printf( "%%INIT-I-SWBAD, writing software bad block descriptor\n" ); + } + sbad->bbm$w_checksum = checksum( sbad, offsetof( struct SWBAD, bbm$w_checksum ) / 2 ); + WRITE( dev, nlb - 1, 1, sbad ); + } + + /* Access last block so full size of file is allocated. (Hopefully + * sparse.) To account for possible interleaving, do the last track. + * Helps humans in various ways. + */ + + if( eofmark < nlb ) { + for( tmp = nlb - ((dp->sectors * lb_per_pb) / pb_per_lb); + tmp < nlb; tmp++ ) { + READ( dev, tmp, 1, idxhead ); + WRITE( dev, tmp, 1, idxhead ); + } + } + } + + return status; +} +#undef Malloc + +static unsigned add2dir( void *dirp, unsigned *dirlen, unsigned dirblocks, + struct HEAD *head ) { + struct dir$r_ent *de; + struct dir$r_rec *dr; + struct IDENT *ident; + unsigned block, offset; + f11word reclen, paddednamelen, version, verlimit; + size_t namelen, typlen; + char fname[ sizeof( ident->fi2$t_filename ) + + sizeof( ident->fi2$t_filenamext ) + 1], *p; + int extlen; + + ident = IDENTp(head); + + memcpy( fname, ident->fi2$t_filename, sizeof( ident->fi2$t_filename ) ); + + extlen = ((head->fh2$b_mpoffset - head->fh2$b_idoffset) * sizeof( f11word )) - + offsetof( struct IDENT, fi2$t_filenamext ); + if( extlen > 0 ) { + if( extlen > (int) sizeof( ident->fi2$t_filenamext ) ) + extlen = sizeof( ident->fi2$t_filenamext ); + memcpy( fname + sizeof( ident->fi2$t_filename ), + ident->fi2$t_filenamext, extlen ); + } else extlen = 0; + + namelen = sizeof( ident->fi2$t_filename ) + extlen; + fname[namelen] = '\0'; + while( namelen > 1 && fname[namelen - 1] == ' ' ) + fname[--namelen] = '\0'; + + if( (p = strrchr( fname, ';' )) != NULL ) { + typlen = (size_t) ( fname + namelen - p ); + namelen = (size_t)(p -fname ); + *p++ = '\0'; + version = (f11word)strtoul( p, &p, 10 ); + if( p != fname + namelen + typlen || typlen < 1 ) + return SS$_BADFILENAME; + } else + version = 1; + + block = *dirlen / 512; + offset = *dirlen % 512; + + verlimit = (head->fh2$l_filechar & FH2$M_DIRECTORY)? 1: 0; + + paddednamelen = (f11word) ( ((namelen + 1) / 2) * 2 ); + + reclen = offsetof( struct dir$r_rec, dir$t_name ) + paddednamelen + sizeof( struct dir$r_ent ); + if( reclen + offset > 512 ) { + block++; + if( block >= dirblocks ) + return SS$_DEVICEFULL; + if( offset <= 512 - sizeof(f11word) ) { + ((f11word *)dirp)[offset / sizeof(f11word)] = F11WORD( -1 ); + offset += sizeof( f11word ); + } + *dirlen += 512 - offset; + offset = 0; + } + dr = (struct dir$r_rec *)( ((char *)dirp) + *dirlen ); + dr->dir$w_size = (f11word) F11WORD( reclen - sizeof( dr->dir$w_size ) ); + dr->dir$w_verlimit = F11WORD( verlimit ); + dr->dir$b_flags = 0; /* dir$c_fid */ + dr->dir$b_namecount = (uint8_t) namelen; + memcpy( dr->dir$t_name, fname, paddednamelen ); + de = (struct dir$r_ent *) ( ((char *)dirp) + ( block * 512 ) + offset + reclen - sizeof( struct dir$r_ent )); + de->dir$w_version = version; + de->dir$w_fid = head->fh2$w_fid; + offset += reclen; + if( offset <= 512 - sizeof(f11word) ) + ((f11word *)dirp)[offset/sizeof(f11word)] = F11WORD( -1 ); + *dirlen += reclen; + + return SS$_NORMAL; +} + +static unsigned add2map( struct NEWVOL *params, struct HEAD *fhd, + unsigned start, unsigned n ) { + f11word *mpe; + uint8_t mapsize, inuse; + f11long hiblk; + unsigned clustersize; + + clustersize = params->clustersize; + + hiblk = F11SWAP( fhd->fh2$w_recattr.fat$l_hiblk ); + +#if DEBUG + printf( "Adding %u blocks at lbn %u, clu %u to %-20.20s", + n, start, start / clustersize, IDENTp(fhd)->fi2$t_filename ); +#endif + + n = CLU2LBN( CLUSTERS( n ) ); + start = CLU2LBN( start / clustersize ); + + mapsize = fhd->fh2$b_acoffset - fhd->fh2$b_mpoffset; + inuse = fhd->fh2$b_map_inuse; + + mpe = ((f11word *)fhd) + F11WORD( fhd->fh2$b_mpoffset ) + inuse; + +#if DEBUG + printf( "Alloc: %u\n", n ); +#endif + + while( n > 0 ) { + f11word freew = mapsize - inuse; + unsigned count; + + count = n; + if( start <= FM2$C_FMT1_MAXLBN ) { + if( freew < 2 ) + return SS$_DEVICEFULL; + if( count <= FM2$C_FMT1_MAXCNT ) { + hiblk += count; + n -= count--; + *mpe++ = F11WORD( FM2$M_FORMAT1 | ((start >> 16) << 8) | count ); + *mpe++ = F11WORD( (f11word)start ); + inuse += 2; + start += count + 1; + continue; + } + } + if( count <= FM2$C_FMT2_MAXCNT ) { + if( freew < 3 ) + return SS$_DEVICEFULL; + hiblk += count--; + *mpe++ = F11WORD( FM2$M_FORMAT2 | count ); + *mpe++ = F11WORD( (f11word)start ); + *mpe++ = F11WORD( (f11word)(start >> 16) ); + inuse += 3; + break; + } + if( freew < 4 ) + return SS$_DEVICEFULL; + if( count > FM2$C_FMT3_MAXCNT ) + count = FM2$C_FMT3_MAXCNT; + hiblk += count; + n -= count--; + *mpe++ = F11WORD( FM2$M_FORMAT3 | (f11word)(count >> 16) ); + *mpe++ = F11WORD( (f11word)count ); + *mpe++ = F11WORD( (f11word)start ); + *mpe++ = F11WORD( (f11word)(start >> 16) ); + inuse += 4; + start += count + 1; + } + + fhd->fh2$w_recattr.fat$l_hiblk = F11SWAP( hiblk ); + +#if DEBUG + { + unsigned vbn = 1; + unsigned i; + f11word *mp; + + mp = ((f11word *)fhd) + F11WORD( fhd->fh2$b_mpoffset ); + + for( i = 0; i < inuse; i++ ) { + f11word e0; + unsigned count, pbn; + + e0 = *mp++; + e0 = F11WORD(e0); + switch( e0 & FM2$M_FORMAT3 ) { + case FM2$M_FORMAT1: + count = e0 & 0xff; + pbn = F11WORD(*mp) | (((e0 & ~FM2$M_FORMAT3) >> 8) << 16); + mp++; + break; + case FM2$M_FORMAT2: + count = e0 & ~FM2$M_FORMAT3; + pbn = *mp++; + pbn = F11WORD( pbn ); + pbn |= F11WORD (*mp ) << 16; + mp++; + break; + case FM2$M_FORMAT3: + count = ((e0 & ~FM2$M_FORMAT3) << 16); + count |= F11WORD( *mp ); + mp++; + pbn = *mp++; + pbn = F11WORD( pbn ); + pbn |= F11WORD (*mp ) << 16; + mp++; + break; + default: + continue; + } + ++count; + printf(" VBN %u, FMT %u PBN %u, Count %u\n", vbn, (e0 >> 14), pbn, count ); + vbn += count; + } + printf( " File EOF VBN %u FFB %u, Alloc: %u\n", + F11SWAP( fhd->fh2$w_recattr.fat$l_efblk ), + fhd->fh2$w_recattr.fat$w_ffbyte, + F11SWAP( fhd->fh2$w_recattr.fat$l_hiblk ) ); + } +#endif + + fhd->fh2$b_map_inuse = inuse; + return SS$_NORMAL; +} + +static unsigned writeHEAD( void *dev, struct HEAD *head, unsigned hdrbase ) { + hdrbase += head->fh2$w_fid.fid$w_num - 1; + + return writeHDR( dev, head, hdrbase ); +} + +static unsigned writeHDR( void *dev, struct HEAD *head, unsigned lbn ) { + head->fh2$w_checksum = checksum( (f11word *)head, offsetof( struct HEAD, fh2$w_checksum ) / 2 ); + head->fh2$w_checksum = F11WORD( head->fh2$w_checksum ); + + WRITE( dev, lbn, 1, head ); + return SS$_NORMAL; +} +static unsigned writeHOM( void *dev, struct HOME *hom, + unsigned homlbn, unsigned homvbn, + unsigned ncopies ) { + unsigned copy = 0; + + do { + hom->hm2$l_homelbn = F11LONG( homlbn ); + hom->hm2$w_homevbn = F11WORD( homvbn ); + hom->hm2$w_checksum1 = checksum( (f11word *)hom, + offsetof( struct HOME, hm2$w_checksum1 ) /2 ); + hom->hm2$w_checksum1 = F11WORD( hom->hm2$w_checksum1 ); + hom->hm2$w_checksum2 = checksum( (f11word *)hom, + offsetof( struct HOME, hm2$w_checksum2 ) /2 ); + hom->hm2$w_checksum2 = F11WORD( hom->hm2$w_checksum2 ); + + WRITE( dev, homlbn, 1, hom ); + homlbn++; + homvbn++; + } while( ++copy < ncopies ); + + return SS$_NORMAL; +} + +static unsigned alloclbns( unsigned char *bitmap, + unsigned clustersize, + unsigned n_clusters, + unsigned startlbn, unsigned n, + unsigned *found ) { + unsigned nfound; + int pass; + + *found = ~0U; + + startlbn /= clustersize; + n = CLUSTERS( n ); + + for( pass = 0, nfound = 0; pass < 2 && nfound < n; pass++ ) { + unsigned lbn; + + if( pass > 0 ) startlbn = 0; + + for( lbn = startlbn, nfound = 0; nfound < n && lbn + n < n_clusters; lbn++ ) { + if( bitmap[ lbn >> 3 ] & (1 << (lbn & 0x7)) ) { + if( nfound++ == 0 ) + startlbn = lbn; + } else + nfound = 0; + } + } + + if( nfound < n ) + return SS$_DEVICEFULL; + + modbits( bitmap, 0, startlbn, n ); + + *found = startlbn * clustersize; + return SS$_NORMAL; +} + +static void modlbnbits( struct NEWVOL *params, unsigned char *bitmap, int set, unsigned start, unsigned n ) { + ldiv_t d; + unsigned first, clusters, clustersize; + + if( n == 0 ) + return; + + clustersize = params->clustersize; + + d = ldiv( start, clustersize ); + + clusters = 1; + + if( (first = clustersize - d.rem) < n ) + clusters += CLUSTERS( n - first ); + + modbits( bitmap, set, d.quot, clusters ); + return; +} + +static int tstlbnbit( struct NEWVOL *params, unsigned char *bitmap, unsigned lbn ) { + + lbn = lbn / params->clustersize; + + return (bitmap[lbn >> 3] & (1 << (lbn & 0x7))) != 0; +} + +static void modbits( unsigned char *bitmap, int set, unsigned start, unsigned n ) { + unsigned q, bit, byte; + + if( set ) { + while( n > 0 ) { + bit = start & 0x7; + byte = start >>3; + + if( bit == 0 && n > 8 ) { + q = n >> 3; + n &= 0x7; + + if( q == 1 ) + bitmap[byte] = 0xff; + else + memset( bitmap+byte, 0xff, q ); + start += q << 3; + } else { + bitmap[byte] |= 1 << bit; + start++; + n--; + } + } + return; + } + while( n > 0 ) { + bit = start & 0x7; + byte = start >>3; + + if( bit == 0 && n > 8 ) { + q = n >> 3; + n &= 0x7; + + if( q == 1 ) + bitmap[byte] = 0; + else + memset( bitmap+byte, 0x00, q ); + start += q << 3; + } else { + bitmap[byte] &= ~(1 << bit); + start++; + n--; + } + } + return; +} + +static f11word checksum( void *data, size_t length ) { + f11word sum, *dp; + size_t count; + + for( sum = 0, dp = (f11word *)data, + count = 0; count < length; count++ ) + sum += *dp++; + return sum; +} + +/*************************************************************** compute_delta() */ +/* + * The search delta is computed from the + * volume geometry, expressed in sectors, tracks (surfaces), and + * cylinders, according to the following rules, to handle the cases where + * one or two dimensions of the volume have a size of 1. + * + * Geometry: Delta + * + * s x 1 x 1: 1 Rule 1 + * 1 x t x 1: 1 Rule 2 + * 1 x 1 x c: 1 Rule 3 + * + * s x t x 1: s+1 Rule 4 + * s x 1 x c: s+1 Rule 5 + * 1 x t x c: t+1 Rule 6 + * + * s x t x c: (t+1)*s+1 Rule 7 + */ + + +static unsigned int compute_delta( unsigned long sectorsize, + unsigned long sectors, + unsigned long tracks, + unsigned long cylinders ) { + + if( sectorsize < 512 ) + sectors = (sectorsize * sectors) / 512; + + if( sectors > 1 && tracks > 1 && cylinders > 1 ) /* Rule 7 */ + return (tracks + 1) * sectors +1; + + if( (sectors > 1 && tracks > 1 && cylinders == 1 ) || /* Rule 4 */ + (sectors > 1 && tracks == 1 && cylinders > 1 ) ) /* Rule 5 */ + return sectors + 1; + + if( sectors == 1 && tracks > 1 && cylinders > 1 ) /* Rule 6 */ + return tracks + 1; + + return 1; /* Rules 1-3 */ +} + +#if 0 +static unsigned int delta_from_name( const char *diskname ) { + struct disktype *dp; + + for( dp = disktype; dp->name != NULL; dp++ ) { + if( !strcmp( dp->name, diskname ) ) + return compute_delta( dp->sectorsize, dp->sectors, dp->tracks, dp->cylinders ); + } + + return ~0u; +} +#endif +unsigned int delta_from_index( size_t index ) { + struct disktype *dp; + unsigned int delta; + + if( index > max_disktype ) + abort(); + + dp = disktype + index; + delta = compute_delta( dp->sectorsize, dp->sectors, dp->tracks, dp->cylinders ); + +#if defined( DEBUG ) || HOME_SKIP > 0 + printf( "HOM search index for %s is %u\n", dp->name, delta ); +#endif + + return delta; +} diff --git a/extracters/ods2/initvol.h b/extracters/ods2/initvol.h new file mode 100644 index 0000000..e30aeab --- /dev/null +++ b/extracters/ods2/initvol.h @@ -0,0 +1,84 @@ +/* Timothe Litt March 2016 + * Copyright (C) 2016 Timothe litt + * litt at acm dot org + * + * Free for use with the ODS2 package. All other rights reserved. + */ + +/* + * This is distributed as part of ODS2, originally 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. + */ + +#ifndef _INITVOL_H +#define _INITVOL_H + +#include + +struct NEWVOL { + const char *label; + const char *devtype; + const char *devnam; + const char *username; + const char *useruic; + unsigned options; + unsigned clustersize; + unsigned maxfiles; + unsigned headers; + unsigned indexlbn; + unsigned directories; +}; +#define INIT_INDEX_MIDDLE (~0U) +#define INIT_LOG (1 << 0) +#define INIT_VIRTUAL (1 << 1) + +#define VOL_ALPHABET "abcdefghijklmnopqrstuvwxyz$-_" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +#ifdef __ALPHA +#pragma member_alignment save +#pragma nomember_alignment +#endif + +struct BAD144 { + uint32_t bbd$l_serial; + uint16_t bbd$w_reserved; + uint16_t bbd$w_flags; + uint32_t bbd$l_badblock[1]; +#define BBD$M_TRACK (0x7F << BBD$V_TRACK) +#define BBD$V_TRACK (24) +#define BBD$M_SECTOR (0xFF << BBD$V_SECTOR) +#define BBD$V_SECTOR (16) +#define BBD$M_CYLINDER (0x7FFF << BBD$V_CYLINDER) +#define BBD$V_CYLINDER (0) +#define BBD$C_FILL ((uint32_t)~0u) +}; + +struct SWBADENT { + uint8_t bbm$b_highlbn; + uint8_t bbm$b_count; + uint16_t bbm$w_lowlbn; +}; +struct SWBAD { + uint8_t bbm$b_countsize; + uint8_t bbm$b_lbnsize; + uint8_t bbm$b_inuse; + uint8_t bbm$b_avail; + struct SWBADENT bbm$r_map[126]; + uint16_t bbm$w_reserved; + uint16_t bbm$w_checksum; +}; + +#ifdef __ALPHA +#pragma member_alignment restore +#endif + +unsigned initvol( void *dev, struct NEWVOL *params ); +unsigned int delta_from_index( size_t index ); + +#endif diff --git a/extracters/ods2/makefile-vhd.nt b/extracters/ods2/makefile-vhd.nt new file mode 100644 index 0000000..c0586e5 --- /dev/null +++ b/extracters/ods2/makefile-vhd.nt @@ -0,0 +1,11 @@ +# -*- Makefile -*- + +# Include file for VHD format image file support on +# Unix OSs that support it + +# Enable VHD static library, disable syslog so errors go +# to stderr. +DEFS += -DUSE_VHD -Ivhd -DLIBVHD_HAS_SYSLOG=0 +LIBS += Rpcrt4.lib +VHDLIB = phyvhd.c vhd/libvhd.c vhd/relative-path.c + diff --git a/extracters/ods2/makefile-vhd.unix b/extracters/ods2/makefile-vhd.unix new file mode 100644 index 0000000..8426b75 --- /dev/null +++ b/extracters/ods2/makefile-vhd.unix @@ -0,0 +1,19 @@ +# -*- Makefile -*- + +# Include file for VHD format image file support on +# Unix OSs that support it + +# Enable VHD static library, disable syslog so errors go +# to stderr. +DEFS += -DUSE_VHD -Ivhd -D_GNU_SOURCE -DLIBVHD_HAS_SYSLOG=0 +LDLIBS += -luuid +VHDLIB = phyvhd.c vhd/libvhd.c vhd/relative-path.c + +# Enable VHD sharable library +# Not recommended, as it logs to syslog. Used for testing +# against unmodified Xen libraries. A modified sharable image +# can also be built - see the vhdtools makefile. +# +#DEFS += -DUSE_VHD -DUSE_LIBVHD="libvhd.so" -Ivhd -D_GNU_SOURCE +#LDLIBS += -ldl +#VHDLIB = phyvhd.c diff --git a/extracters/ods2/makefile.depends b/extracters/ods2/makefile.depends new file mode 100644 index 0000000..9cfd6c9 --- /dev/null +++ b/extracters/ods2/makefile.depends @@ -0,0 +1,123 @@ +# DO NOT DELETE + +ods2.o: version.h cmddef.h +ods2.o: compat.h +ods2.o: ods2.h rms.h vmstime.h descrip.h ssdef.h +ods2.o: stsdef.h sysmsg.h cache.h phyvirt.h +access.o: ssdef.h access.h +access.o: cache.h f11def.h +access.o: vmstime.h descrip.h stsdef.h device.h phyio.h initvol.h ods2.h +access.o: phyvirt.h compat.h +access.o: sysmsg.h +cache.o: cache.h ods2.h ssdef.h +compat.o: compat.h +compat.o: descrip.h stsdef.h +copycmd.o: cmddef.h +copycmd.o: compat.h +copycmd.o: ods2.h rms.h vmstime.h descrip.h ssdef.h +copycmd.o: stsdef.h sysmsg.h +createcmd.o: cmddef.h +createcmd.o: compat.h +createcmd.o: ods2.h rms.h vmstime.h descrip.h ssdef.h +createcmd.o: stsdef.h sysmsg.h f11def.h +debug.o: debug.h +deletecmd.o: cmddef.h +deletecmd.o: compat.h +deletecmd.o: ods2.h rms.h vmstime.h descrip.h ssdef.h +deletecmd.o: stsdef.h sysmsg.h +device.o: ods2.h access.h cache.h +device.o: f11def.h vmstime.h +device.o: descrip.h ssdef.h stsdef.h device.h phyio.h +diffcmd.o: cmddef.h +diffcmd.o: compat.h +diffcmd.o: ods2.h rms.h vmstime.h descrip.h ssdef.h +diffcmd.o: stsdef.h sysmsg.h +dircmd.o: cmddef.h +dircmd.o: compat.h +dircmd.o: ods2.h rms.h vmstime.h descrip.h ssdef.h +dircmd.o: stsdef.h sysmsg.h +direct.o: access.h cache.h +direct.o: f11def.h vmstime.h +direct.o: descrip.h ssdef.h stsdef.h direct.h fibdef.h ods2.h +dismountcmd.o: cmddef.h +dismountcmd.o: compat.h +dismountcmd.o: ods2.h rms.h vmstime.h descrip.h ssdef.h +dismountcmd.o: stsdef.h sysmsg.h access.h cache.h f11def.h +dismountcmd.o: device.h +dismountcmd.o: phyio.h +extendcmd.o: cmddef.h +extendcmd.o: compat.h +extendcmd.o: ods2.h rms.h vmstime.h descrip.h ssdef.h +extendcmd.o: stsdef.h sysmsg.h +helpcmd.o: cmddef.h +helpcmd.o: compat.h +helpcmd.o: ods2.h rms.h vmstime.h descrip.h ssdef.h +helpcmd.o: stsdef.h sysmsg.h +importcmd.o: cmddef.h +importcmd.o: compat.h +importcmd.o: ods2.h rms.h vmstime.h descrip.h ssdef.h +importcmd.o: stsdef.h sysmsg.h +initialcmd.o: cmddef.h +initialcmd.o: compat.h +initialcmd.o: ods2.h rms.h vmstime.h descrip.h ssdef.h +initialcmd.o: stsdef.h sysmsg.h access.h cache.h f11def.h +initialcmd.o: device.h +initialcmd.o: phyio.h initvol.h phyvirt.h +initvol.o: compat.h +initvol.o: f11def.h +initvol.o: vmstime.h descrip.h ssdef.h stsdef.h +initvol.o: initvol.h ods2.h phyvirt.h rms.h +mountcmd.o: cmddef.h +mountcmd.o: compat.h +mountcmd.o: ods2.h rms.h vmstime.h descrip.h ssdef.h +mountcmd.o: stsdef.h sysmsg.h access.h cache.h f11def.h +mountcmd.o: device.h phyio.h phyvirt.h phyvhd.h +phyunix.o: device.h +phyunix.o: access.h cache.h f11def.h vmstime.h descrip.h ssdef.h stsdef.h +phyunix.o: phyio.h ods2.h phyvirt.h compat.h +phyvirt.o: compat.h +phyvirt.o: device.h access.h cache.h f11def.h +phyvirt.o: vmstime.h +phyvirt.o: descrip.h ssdef.h stsdef.h phyio.h ods2.h phyvirt.h phyvhd.h +rms.o: access.h cache.h f11def.h +rms.o: vmstime.h descrip.h ssdef.h stsdef.h +rms.o: device.h phyio.h direct.h fibdef.h ods2.h rms.h compat.h +rms.o: sysmsg.h +searchcmd.o: cmddef.h +searchcmd.o: compat.h +searchcmd.o: ods2.h rms.h vmstime.h descrip.h ssdef.h +searchcmd.o: stsdef.h sysmsg.h +setcmd.o: cmddef.h +setcmd.o: compat.h +setcmd.o: ods2.h rms.h vmstime.h descrip.h ssdef.h +setcmd.o: stsdef.h sysmsg.h +showcmd.o: cmddef.h +showcmd.o: compat.h +showcmd.o: ods2.h rms.h vmstime.h descrip.h ssdef.h +showcmd.o: stsdef.h sysmsg.h +showcmd.o: access.h cache.h f11def.h +showcmd.o: direct.h phyio.h +showcmd.o: phyvhd.h phyvirt.h version.h +spawncmd.o: cmddef.h +spawncmd.o: compat.h +spawncmd.o: ods2.h rms.h vmstime.h descrip.h ssdef.h +spawncmd.o: stsdef.h sysmsg.h +sysmsg.o: ssdef.h rms.h vmstime.h +sysmsg.o: descrip.h stsdef.h compat.h +sysmsg.o: sysmsg.h +typecmd.o: cmddef.h +typecmd.o: compat.h +typecmd.o: ods2.h rms.h vmstime.h descrip.h ssdef.h +typecmd.o: stsdef.h sysmsg.h +update.o: access.h cache.h +update.o: f11def.h vmstime.h +update.o: descrip.h ssdef.h stsdef.h device.h phyio.h ods2.h +vmstime.o: vmstime.h descrip.h ssdef.h stsdef.h +phyvhd.o: device.h +phyvhd.o: access.h cache.h f11def.h +phyvhd.o: vmstime.h descrip.h ssdef.h stsdef.h +phyvhd.o: phyio.h ods2.h phyvirt.h vhd/libvhd.h +phyvhd.o: vhd/blk_uuid.h vhd/vhd.h +vhd/libvhd.o: vhd/libvhd.h +vhd/libvhd.o: vhd/blk_uuid.h vhd/vhd.h vhd/relative-path.h +vhd/relative-path.o: vhd/relative-path.h diff --git a/extracters/ods2/makefile.generic b/extracters/ods2/makefile.generic index 46c20d1..5ebfc0e 100644 --- a/extracters/ods2/makefile.generic +++ b/extracters/ods2/makefile.generic @@ -1,5 +1,11 @@ +# -*- Makefile -*- + # Generic makefile for ods2 # +# This tries really hard to be OS-agnostic, because +# otherwise, every little change means updating all +# the os-specific makefiles. +# # See makefile., which normally includes it # # If your make doesn't support include, these can be @@ -18,72 +24,112 @@ #DEFS = # Libraries, e.g. -lreadline +#LDLIBS = + +# Linker flags #LDFLAGS = # Object file suffix #OBJ = .o +# Executable file suffix + +#EXE = + # Physical IO module #PHYSIO = phyunix # Extra physical IO headers #PHYSIOH = +# VHD format options + +include $(VHDOPTS) + # ##################################################### -all : ods2 +DEPENDS = makefile.depends +MAKEDEPEND = makedepend +SED = sed -OBJS = $(ODS2_OBJS) +all : ods2$(EXE) -COMMON_OBJS = \ -access$(OBJ) \ -cache$(OBJ) \ -compat$(OBJ)\ -device$(OBJ) \ -direct$(OBJ) \ -phyvirt$(OBJ) \ -rms$(OBJ) \ -sysmsg$(OBJ) \ -update$(OBJ) \ -vmstime$(OBJ) +SRCS = ods2.c \ +access.c \ +cache.c \ +compat.c \ +copycmd.c \ +createcmd.c \ +debug.c \ +deletecmd.c \ +device.c \ +diffcmd.c \ +dircmd.c \ +direct.c \ +dismountcmd.c \ +extendcmd.c \ +helpcmd.c \ +importcmd.c \ +initialcmd.c \ +initvol.c \ +mountcmd.c \ +$(PHYSIO).c \ +phyvirt.c \ +rms.c \ +searchcmd.c \ +setcmd.c \ +showcmd.c \ +spawncmd.c \ +sysmsg.c \ +typecmd.c \ +update.c \ +vmstime.c \ +$(VHDLIB) -ODS2_OBJS = ods2$(OBJ) $(COMMON_OBJS) $(PHYSIO)$(OBJ) +OBJS = $(SRCS:.c=$(OBJ)) -ods2 : $(ODS2_OBJS) $(LIBS) - $(CC) $(CCFLAGS) -o ods2 $(ODS2_OBJS) $(LDFLAGS) +depend: $(MAKEFILE_LIST) + $(DELETE) $(DEPENDS) + $(MAKE) -f $(TOPMAKE) $(DEPENDS) + $(MAKE) -f makefile.vms descrip.mms -access$(OBJ) : access.c ssdef.h access.h phyio.h compat.h sysmsg.h phyvirt.h - $(CC) -c $(CCFLAGS) $(DEFS) access.c +# Used to list sourcefiles for updating VC project & git repository. +# Not used for Makefile builds +# Run after make depends +# To get the Windows file list on Unix, use +# make -f makefile.nt "DELETE=rm -f" depend vclist -cache$(OBJ) : cache.c cache.h ssdef.h - $(CC) -c $(CCFLAGS) $(DEFS) cache.c +vclist: + @echo "** Header files **" + @$(SED) $(DEPENDS) -e'/ *#/d' -e's/^.*.$(OBJ): //g' -e's/ /\n/g' | sort | uniq + @echo "" + @echo "** Source files **" + @echo $(SRCS) | sed -e's/ */\n/g' | sort | uniq -compat$(OBJ) : compat.c compat.h - $(CC) -c $(CCFLAGS) $(DEFS) compat.c +# This processing is so the kit can contain a generic, up-to-date depends file +# I don't want to require makedepend for people to build from source. +# I do want the file to be accurate when shipped, so auto-maintained. +# makedepend will include all the system library includes, which is not +# helpful for this case. So the sed processing removes dependencies that +# start with "/" - e.g. /usr/include and friends. -device$(OBJ) : device.c ssdef.h access.h phyio.h - $(CC) -c $(CCFLAGS) $(DEFS) device.c +$(DEPENDS): $(MAKEFILE_LIST) + touch $(DEPENDS) + 2>/dev/null $(MAKEDEPEND) -o$(OBJ) -I. -f- -- $(CFLAGS) $(CPPFLAGS) $(DEFS) -- $(SRCS) | $(SED) -e's/ \/[^ ]*\.h/ /g' -e'/^[^:]*: *$$/d' -e's/ */ /g' >$(DEPENDS) -direct$(OBJ) : direct.c direct.h access.h fibdef.h descrip.h ssdef.h - $(CC) -c $(CCFLAGS) $(DEFS) direct.c +-include $(DEPENDS) -ods2$(OBJ) : ods2.c compat.h sysmsg.h phyio.h ssdef.h descrip.h access.h rms.h version.h - $(CC) -c $(CCFLAGS) $(VERSION) $(DEFS) ods2.c +# Currently, the descrip.mms file is updated on Unix. A makefile.vms +# exists solely to enable this. It might be possible to build ODS2 +# on VMS with it using GNU tools. But that's not the point. -$(PHYSIO)$(OBJ) : $(PHYSIO).c phyio.h phyvirt.h ssdef.h compat.h $(PHYSIOH) - $(CC) -c $(CCFLAGS) $(DEFS) $(PHYSIO).c +descrip.mms : $(SRCS) $(DEPENDS) + $(SED) -i.bak -e's!^OBJS = .*$$!OBJS = $(SRCS:.c=)!' -e'/^# ### BEGIN RULES ###/,$$d' -e'/^OBJS =/y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' descrip.mms + echo "# ### BEGIN RULES ###" >>descrip.mms + 2>/dev/null $(MAKEDEPEND) -o$(OBJ) -I. -f- -- $(CFLAGS) $(CPPFLAGS) $(DEFS) -- $(SRCS) | $(SED) -e's/ \/[^ ]*\.h/ /g' -e'/^[^:]*: *$$/d' -e's/ */ /g' | $(SED) -e'/^# DO NOT DELETE/d' -e's/^\([^:]*\)$(OBJ):/\1$$(OBJ):/g' -e'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' >>descrip.mms -phyvirt$(OBJ) : phyvirt.c phyvirt.h compat.h ssdef.h - $(CC) -c $(CCFLAGS) $(DEFS) phyvirt.c +%$(OBJ): %.c + $(CC) -c $(CCFLAGS) $(CPPFLAGS) $(DEFS) $< -o $@ -rms$(OBJ) : rms.c rms.h compat.h direct.h access.h fibdef.h descrip.h ssdef.h - $(CC) -c $(CCFLAGS) $(DEFS) rms.c - -sysmsg$(OBJ) : sysmsg.c sysmsg.h rms.h ssdef.h - $(CC) -c $(CCFLAGS) $(DEFS) sysmsg.c - -update$(OBJ) : update.c ssdef.h access.h - $(CC) -c $(CCFLAGS) $(DEFS) update.c - -vmstime$(OBJ) : vmstime.c vmstime.h - $(CC) -c $(CCFLAGS) $(DEFS) vmstime.c +ods2$(EXE) : $(OBJS) $(LIBS) + $(CC) $(LDFLAGS) -o ods2$(EXE) $(OBJS) $(LDLIBS) diff --git a/extracters/ods2/makefile.nt b/extracters/ods2/makefile.nt index 822c6e7..a4f94ee 100644 --- a/extracters/ods2/makefile.nt +++ b/extracters/ods2/makefile.nt @@ -1,23 +1,37 @@ +# -*- Makefile -*- # Makefile for Windows NT # CCFLAGS = "-Oxs" +LIBS = Shlwapi.lib + # For the direct SCSI module #DEFS = -DUSE_ASPI -#LDFLAGS = wnaspi32.lib +#LDLIBS = wnaspi32.lib #PHYSIOH = wnaspi32.h scsidefs.h -#LIBS = wnaspi32.lib +#LIBS += wnaspi32.lib OBJ = .OBJ PHYSIO = phynt +EXE = .EXE +DELETE = DEL -include makefile.generic +# Include VHD format image file support + +VHDOPTS = makefile-vhd.nt wnaspi32.lib : wnaspi32.def LIB /DEF:WNASPI32.DEF /MACHINE:IX86 +TOPMAKE = makefile.nt + +all: + clean: - del $(OBJS) - del ods2.exe + $(DELETE) $(OBJS) + $(DELETE) ods2.exe + +include makefile.generic + diff --git a/extracters/ods2/makefile.solaris b/extracters/ods2/makefile.solaris index 186be7d..bf7b5cb 100644 --- a/extracters/ods2/makefile.solaris +++ b/extracters/ods2/makefile.solaris @@ -1,3 +1,5 @@ +# -*- Makefile -*- + # Makefile for solaris OS # Use gcc @@ -9,14 +11,24 @@ CCFLAGS = -O4 -g DEFS = -DUSE_READLINE LDFLAGS = -lreadline -ltermcap +# Include VHD format image file support + +VHDOPTS = makefile-vhd.unix + # Object file extension OBJ = .o +DELETE = "rm -f" + # Physical I/O module PHYSIO = phyunix PHYSIOH = -include makefile.generic +TOPMAKE = makefile.solaris + +all: clean: rm -f ods2 $(OBJS) + +include makefile.generic diff --git a/extracters/ods2/makefile.tru64 b/extracters/ods2/makefile.tru64 index d4b10a3..0b7f72a 100644 --- a/extracters/ods2/makefile.tru64 +++ b/extracters/ods2/makefile.tru64 @@ -1,3 +1,5 @@ +# -*- Makefile -*- + # Makefile for Tru64 (DEC/OSF1) # @@ -6,14 +8,24 @@ CCFLAGS = -O4 -g # DEFS = -DUSE_READLINE # LDFLAGS = -lreadline -ltermcap +# Include VHD format image file support + +VHDOPTS = makefile-vhd.unix + # Object file extension OBJ = .o +DELETE = rm -f + # Physical I/O module PHYSIO = phyunix PHYSIOH = -include makefile.generic +TOPMAKE = makefile.tru64 + +all: clean: rm -f ods2 $(OBJS) + +include makefile.generic diff --git a/extracters/ods2/makefile.unix b/extracters/ods2/makefile.unix index fb334d4..3bb6cc3 100644 --- a/extracters/ods2/makefile.unix +++ b/extracters/ods2/makefile.unix @@ -1,6 +1,11 @@ +# -*- Makefile -*- + # Makefile for most Unix distributions # +# Special defines, e.g. +# DEFS="-DDEBUG_RMS -DDEBUG_BUILD -D_FILE_OFFSET_BITS=32" + CCFLAGS = -O4 -g #-O0 -g -DUSE_READLINE -Wall -pedantic # Extra warns about $ in identifiers... @@ -9,16 +14,28 @@ CCFLAGS = -O4 -g # Include readline support DEFS = -DUSE_READLINE -LDFLAGS = -lreadline -ltermcap +LDLIBS = -lreadline -ltermcap + +# Include VHD format image file support + +VHDOPTS = makefile-vhd.unix # Object file extension OBJ = .o +EXE = + +DELETE = rm -f + # Physical I/O module PHYSIO = phyunix -PHYSIOH = + +TOPMAKE = makefile.unix + +all: + +clean: + rm -f ods2$(EXE) $(OBJS) include makefile.generic -clean: - rm -f ods2 ods2i $(OBJS) diff --git a/extracters/ods2/makefile.vms b/extracters/ods2/makefile.vms new file mode 100644 index 0000000..8eeb967 --- /dev/null +++ b/extracters/ods2/makefile.vms @@ -0,0 +1,32 @@ +# -*- Makefile -*- + +# Used to generate descrip.mms +# In theory should build under VMS with GNU tools. +# But that's just a theory... + +# Special defines, e.g. +# DEFS="-DDEBUG_RMS -DDEBUG_BUILD -D_FILE_OFFSET_BITS=32" + +CCFLAGS = -O4 -g + +#LDLIBS = -lreadline -ltermcap + +# Object file extension +OBJ = .OBJ + +EXE = .EXE + +DELETE = DELETE +GEN = ";*" + +# Physical I/O module +PHYSIO = phyvms + +TOPMAKE = makefile.vms + +all: + +clean: + $(DELETE) *.$(OBJ)$(GEN),*.$(EXE)$(GEN) + +include makefile.generic diff --git a/extracters/ods2/mountcmd.c b/extracters/ods2/mountcmd.c new file mode 100644 index 0000000..e1a5b4d --- /dev/null +++ b/extracters/ods2/mountcmd.c @@ -0,0 +1,153 @@ +/* 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. + */ + +#if !defined( DEBUG ) && defined( DEBUG_MOUNTCMD ) +#define DEBUG DEBUG_MOUNTCMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + +#include "access.h" +#include "device.h" +#include "phyio.h" +#include "phyvirt.h" + +#ifdef USE_VHD +#include "phyvhd.h" + +static char *parents = NULL; +#endif + +void mounthelp(void); + +#define mou_snapshot OPT_GENERIC_1 + + +qual_t mouquals[] = { {DT_NAME, 0, MOU_DEVTYPE, CV(NULL), "Drive type (DEC model name) "}, + {"image", MOU_VIRTUAL, 0, NV, "Mount a disk image file", }, + {"log", MOU_LOG, 0, NV, "-Show progress"}, + {"nolog", 0, MOU_LOG, NV, NULL}, + {"readonly", 0, MOU_WRITE, NV, "Only allow reading from volume"}, +#ifdef USE_VHD + {"snapshots_of", MOU_VIRTUAL | MOU_WRITE | mou_snapshot, PHY_CREATE, SV(&parents), + "Create snapshot(s) of the (existing) VHD disk(s) specified by this qualifier." }, +#endif + {"virtual", MOU_VIRTUAL, 0, NV, NULL, }, + {"write", MOU_WRITE, 0, NV, "Allow writing to volume", }, + {NULL, 0, 0, NV, NULL } }; + +param_t moupars[] = { {"volumes", REQ, LIST, NOPA, + "devices or disk image(s) of volume set in RVN order separated by comma" + }, + {"labels", OPT, LIST, NOPA, "volume labels in RVN order separated by comma" }, + { NULL, 0, 0, NOPA, NULL } +}; + +/******************************************************************* domount() */ + +DECL_CMD(mount) { + int sts = 1, devices = 0; + char **devs = NULL, **labs = NULL; + int options; + +#ifdef USE_VHD + int nparents = 0; + size_t i; + char **parfiles = NULL; +#endif + + options = checkquals( 0, mouquals, qualc, qualv ); + if( options == -1 ) + return SS$_BADPARAM; + + UNUSED(argc); + + if( (devices = parselist( &devs, 0, argv[1], "devices")) < 0 ) + return SS$_BADPARAM; + if( parselist( &labs, devices, argv[2], "labels") < 0 ) { + free( devs ); + return SS$_BADPARAM; + } +#ifdef USE_VHD + if( options & mou_snapshot ) { + nparents = parselist( &parfiles, devices, parents, "parent VHD files " ); + if( nparents != devices ) { + printf( "%%ODS2-E-NOTSAME, You must specify the same number of existing files (%u) with %cSNAPSHOT as filenames to create (%u)\n", + nparents, vms_qual? '/': '-', devices ); + free( parfiles ); + return SS$_BADPARAM; + } + for( i = 0; i < (size_t)nparents; i++ ) { + sts = phyvhd_snapshot( devs[i], parfiles[i] ); + if( !(sts & STS$M_SUCCESS) ) { + if( (sts & STS$M_COND_ID) != (SS$_DUPFILENAME & STS$M_COND_ID) ) { + (void) unlink( devs[i] ); + } + while( i > 0 ) { + --i; + (void) unlink( devs[i] ); + } + free( parfiles ); + return SS$_BADPARAM; + } + } + if( options & MOU_LOG ) { + for( i = 0; i < (size_t)nparents; i++ ) + printf( "%%ODS2-I-SNAPOK, %s created from %s\n", + devs[i], parfiles[i] ); + } + options |= MOU_WRITE; + free( parfiles ); + parfiles = NULL; + } +#endif + + if (devices > 0) { + sts = mount( options | MOU_LOG, devices, devs, labs ); + if( !(sts & STS$M_SUCCESS) ) + printf("%%ODS2-E-MOUNTERR, Mount failed with %s\n", getmsg(sts, MSG_TEXT)); + } + + free( devs ); + free( labs ); + return sts; +} + +void mounthelp(void) { + printf( "\n" ); + printf( "You can mount a volume(-set) from either physical devices\n" ); + printf( "such a CDROM or hard disk drive, or files containing an\n" ); + printf( "image of a volume, such as a .ISO file or simulator disk\n\n" ); + printf( "To mount a disk image, use the %cimage qualifier and\n", + (vms_qual? '/': '-') ); + printf( "specify the filename as the parameter.\n\n" ); + printf( "If the filename contains %c, specify it in double quotes\n\n", + (vms_qual? '/': '-') ); + printf( "Mount will assign a virtual device name to each volume.\n" ); + printf( "You can select a virtual device name using the format\n" ); + printf( " dka100=my_files.iso\n\n" ); + printf( "To mount a physical device, use the format:\n" ); + phyio_help(stdout); + printf( "To mount a volume set, specify all the members in RVN order\n" ); + printf( "as a comma-separated list.\n\n" ); + printf( "If you specify a list of volume labels, they must be in\n" ); + printf( "the same order as the volumes, and each must match the label\n" ); + printf( "stored in the data\n" ); +#ifdef USE_VHD + printf( "\nTo create and mount a snapshot of a VHD-based volume set, use the %csnapshot_of qualifier.\n", vms_qual? '/': '-' ); + printf( "Specify the existing volumes' filenames as an argument to %csnapshot, and\n", vms_qual? '/': '-' ); + printf( "list the corresponding filenames to be created as the mount parameter.\n" ); +#endif + + return; +} diff --git a/extracters/ods2/ods2.c b/extracters/ods2/ods2.c index 0878537..755e4b4 100644 --- a/extracters/ods2/ods2.c +++ b/extracters/ods2/ods2.c @@ -1,90 +1,64 @@ -#define MODULE_NAME ODS2 - -/* Feb-2016, v1.4 add readline support, dir/full etc - * See git commit messages for details. - * V2.1 - Merge and adapte code from Larry Baker's V2.0 - */ - -/* Jul-2003, v1.3hb, some extensions by Hartmut Becker */ - -/* ODS2.c V2.1 Mainline ODS2 program */ +/* main() - command parsing and dispatch */ /* - 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 - PHYVIRT.C Routines for managing virtual disks. - 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), - simply mount /image (or /virtual) filename. - - The included make/mms/com files do all this for you. -*/ - -/* Modified by: - * Feb 2016 Timothe Litt - * 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. Merge - * code from Larry Baker, USGS. See git - * commit history for details. + * This is part of ODS2 written by Paul Nankervis, + * email address: Paulnank@au1.ibm.com * - * 31-AUG-2001 01:04 Hunter Goatley + * 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. * - * For VMS, added routine getcmd() to read commands with full - * command recall capabilities. + * The modules in ODS2 are:- * - * 8-JUN-2005 Larry Baker + * 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 + * *CMD.C Routines to execute CLI commands + * PHYVMS.C Routine to perform physical I/O + * PHYVHD.C Interface to libvhd for VHD format virtual disks. + * PHYVIRT.C Routines for managing virtual disks. + * 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 * - * Add #include guards in .h files. - * Use named constants in place of literals. - * Replace BIG_ENDIAN with ODS2_BIG_ENDIAN (Linux always #defines - * BIG_ENDIAN). - * Add SUBST DRIVE: FILE to "mount" a file (vs. a disk). - * Implement quoted arguments (paired " or '; no escape characters) - * in cmdsplit(), e.g., to specify a Unix path or a null argument. - * Remove VMSIO conditional code (need to "mount" a file now). + * On non-VMS platforms PHYVMS.C should be replaced as follows:- + * + * Unix PHYUNIX.C + * OS/2 PHYOS2.C + * Windows 95/NT PHYNT.C + * + * The included make/mms/com files do all this for you. + * + * For accessing disk images (e.g. .ISO or simulator files), + * simply mount /image (or /virtual) filename. + * + * See version.h for revision history. */ /* 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!) -*/ + * 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!) + */ + +#include "version.h" + +#if !defined( DEBUG ) && defined( DEBUG_ODS2 ) +#define DEBUG DEBUG_ODS2 +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif #ifdef VMS #ifdef __DECC @@ -101,45 +75,19 @@ #define DEBUGx on -#define _BSD_SOURCE -#include /* isalpha(), isspace(), tolower() */ - -#include -#include -#include -#include -#include - #include "version.h" -#include "access.h" +#include "cmddef.h" + +#include "cache.h" #include "compat.h" #include "descrip.h" -#include "device.h" -#include "direct.h" #include "ods2.h" -#include "phyio.h" #include "phyvirt.h" -#include "rms.h" #include "ssdef.h" #include "stsdef.h" #include "sysmsg.h" -#if !defined( _WIN32 ) && !defined( VMS ) -#include -#include -#else -# ifdef _WIN32 -# include -# endif -#endif - -#ifdef VMS -#include -#else -#include "vmstime.h" -#endif - #ifdef VAXC #ifdef EXIT_SUCCESS #undef EXIT_SUCCESS @@ -147,13 +95,6 @@ #define EXIT_SUCCESS SS$_NORMAL #endif -#ifndef TRUE -#define TRUE ( 0 == 0 ) -#endif -#ifndef FALSE -#define FALSE ( 0 != 0 ) -#endif - #if defined( _WIN32 ) && defined(_MSC_VER) && defined( DEBUG_BUILD ) && defined( USE_VLD ) /* Normally done in the project file, but VLD is optional and I'd rather not provide * instructions as they vary by IDE version. See http://vld.codeplex.com/ if interested. @@ -162,1613 +103,489 @@ #pragma comment (lib,"C:\\Program Files (x86)\\Visual Leak Detector\\lib\\Win32\\vld.lib") #endif -#define MAXREC 32767 -#define PRINT_ATTR ( FAB$M_CR | FAB$M_PRN | FAB$M_FTN ) - #ifdef USE_READLINE +#ifndef _GNU_SOURCE #define _XOPEN_SOURCE +#endif #include #include #include #endif -/******************************************************************* fgetline() */ -/* Read a line of input - unlimited length - * Removes \n, returns NULL at EOF - * Caller responsible for free() - */ -static char *fgetline( FILE *stream, int keepnl ) { - size_t bufsize = 0, - xpnsize = 80, - idx = 0; - char *buf = NULL; - int c; - while( (c = fgetc(stream)) != EOF && c != '\n' ) { - if( idx + (keepnl != 0) +2 > bufsize ) { /* In buf + char + (optional \n) + \0 */ - char *nbuf; - bufsize += xpnsize; - nbuf = (char *) realloc( buf, bufsize ); - if( nbuf == NULL ) { - perror( "realloc" ); - abort(); - } - buf = nbuf; - } - buf[idx++] = c; - } - if( c == '\n' ) { - if( keepnl ) - buf[idx++] = '\n'; - } else { - if( c == EOF && idx == 0 ) { - free( buf ); - return NULL; - } - } - if( bufsize == 0 ) { - buf = (char *) malloc( 1 ); - if( buf == NULL ) { - perror( "malloc" ); - abort(); - } - } - buf[idx] = '\0'; - return buf; -} +static int disktypecmp( const void *da, const void *db ); +static int cmdsplit( char *str ); +static int cmdexecute( int argc, char **argv, int qualc, char **qualv ); -/******************************************************************* keycomp() */ -/* keycomp: routine to compare parameter to a keyword - case insensitive! */ +DECL_CMD(copy); +DECL_CMD(create); +DECL_CMD(delete); +DECL_CMD(diff); +DECL_CMD(dir); +DECL_CMD(dismount); +DECL_CMD(extend); +DECL_CMD(help); +DECL_CMD(import); +DECL_CMD(initial); +DECL_CMD(mount); +DECL_CMD(search); +DECL_CMD(set); +DECL_CMD(show); +DECL_CMD(spawn); +DECL_CMD(type); -static int keycomp(const char *param, const char *keywrd) { - while (*param != '\0') { - if( tolower(*param++) != tolower(*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 -#define CV(list) KEYCOL, list -struct qual { - const char *name; - int set; - int clear; - enum qualtype { NOVAL, KEYVAL, KEYCOL } qtype; - void *arg; - const char *helpstr; -}; +#ifdef VMS +static char *getcmd( char *inp, size_t max, char *prompt ); +#endif -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; -}; - -static void qualhelp( int par, struct qual *qtable ); +static void add_diskkwds( qualp_t qualset, const char *qname, qualp_t *disks ); /* Qualifier style = vms = /qualifier; else -option */ -static int vms_qual = 1; -static int verify_cmd = 1; +int vms_qual = 1; +int verify_cmd = 1; -/******************************************************************* checkquals() */ -/* checkquals: Given valid qualifier definitions, process qualifer - * list from a command left to right. Also handles parameters and - * qualifier values (/Qual=value). +/******************************************************************* Command table */ + +/* information about the commands we know... */ +extern qual_t copyquals[], delquals[], dirquals[], importquals[], + iniquals[], mouquals[]; + +extern param_t copypars[], createpars[], delpars[], diffpars[], dirpars[], + dmopars[], extendpars[], helppars[], importpars[], inipars[], moupars[], + searchpars[], setpars[], showpars[], typepars[]; + +CMDSET_t maincmds[] = { + { "copy", docopy, 0,copyquals, copypars, "Copy a file from VMS to host file" }, + { "create", docreate, 0,NULL, createpars, NULL }, + { "delete", dodelete, 0,delquals, delpars, "Delete a VMS file" }, + { "difference", dodiff, 0,NULL, diffpars, "Compare VMS file to host file" }, + { "directory", dodir, 0,dirquals, dirpars, "List directory of VMS files" }, + { "dismount", dodismount,0,NULL, dmopars, "Dismount a VMS volume" }, + { "exit", NULL, 2,NULL, NULL, "Exit ODS2" }, + { "extend", doextend, 0,NULL, extendpars, NULL }, + { "help", dohelp, 0,NULL, helppars, "Obtain help on a command" }, + { "import", doimport, 0,importquals,importpars, "Copy a file from host to VMS" }, + { "initialize", doinitial,-4, iniquals, inipars, "Create a new filesystem" }, + { "mount", domount, 0,mouquals, moupars, "Mount a VMS volume" }, + { "quit", NULL, 2,NULL, NULL, "Exit ODS2" }, + { "search", dosearch, 0,NULL, searchpars, "Search VMS file for a string" }, + { "set", doset, 0,NULL, setpars, "Set PARAMETER - set HELP for list" }, + { "show", doshow, 0,NULL, showpars, "Display state" }, + { "spawn", dospawn, 0,NULL, NULL, "Open a command subprocess" }, + { "type", dotype, 0,NULL, typepars, "Display a VMS file on the terminal" }, + + { NULL, NULL, 0,NULL, NULL, NULL } /* ** END MARKER ** */ +}; + + +/*********************************************************** main() */ + +/* + * 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. */ -static 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: - case KEYCOL: - result = checkquals( result, (struct qual *)qp->arg, -1, &qv ); - if( result == -1 ) - return result; - break; - default: - abort(); - } - qv = nvp; - } while( qv != NULL ); - } - } - return result; -} - -/******************************************************************* dodir() */ -/*********************************************************** prvmstime() */ - -static 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("%%ODS2-W-TIMERR, SYS$ASCTIM error: %s\n",getmsg(sts, MSG_TEXT)); - tim[23] = '\0'; - printf(" %s",tim); - } else { - printf( " %-23s", " " ); - sts = 1; - } - if (sfx != NULL) - printf( "%s", sfx ); - return sts; -} - -/*********************************************************** pwrap() */ - -static 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; - } -} - -/*********************************************************** dirtotal() */ - -#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) - -static 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} -}; -static 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} -}; -static 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} }; -static int dir_defopt = dir_default; - -static struct param dirpars[] = { {"filespec", OPT, VMSFS, NOPA, "for files to select. Wildcards are allowed."}, - { NULL, 0, 0, NOPA, NULL } -}; - -static 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; -} - -/************************************************************ dodir() */ - -static unsigned dodir(int argc,char *argv[],int qualc,char *qualv[]) { - char res[NAM$C_MAXRSS + 1],rsa[NAM$C_MAXRSS + 1]; - int options; - unsigned sts; - int filecount = 0, nobackup = 0, contigb = 0, contig = 0, directory = 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; - 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 & STS$M_SUCCESS ) { - 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 ) ) & STS$M_SUCCESS ) { - - 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 & STS$M_SUCCESS ) ) { - printf("%%ODS2-E-OPENERR, Open error: %s\n", getmsg(sts, MSG_TEXT)); - } 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 = SS$_NORMAL; - 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 & STS$M_SUCCESS ) { - if (filecount < 1) printf("%%DIRECT-W-NOFILES, no files found\n"); - } else { - printf("%%DIR-E-ERROR Status: %s\n",getmsg(sts, MSG_TEXT)); - } - return sts; -} - -/******************************************************************* docopy() */ - -/* copy: a file copy routine */ - -#define COP_BINARY 1 - -static struct qual copyquals[] = { {"ascii", 0, COP_BINARY, NV, "Copy file in ascii mode (default)"}, - {"binary", COP_BINARY, 0, NV, "Copy file in binary mode", }, - {NULL, 0, 0, NV, NULL} -}; - -static 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 } -}; - -static unsigned docopy(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 & STS$M_SUCCESS ) { - nam.nam$l_rsa = rsa; - nam.nam$b_rss = NAM$C_MAXRSS; - fab.fab$l_fop = FAB$M_NAM; - while ( ( sts = sys_search( &fab ) ) & STS$M_SUCCESS ) { - sts = sys_open(&fab); - if ( !( sts & STS$M_SUCCESS ) ) { - printf("%%COPY-F-OPENFAIL, Open error: %s\n",getmsg(sts, MSG_TEXT)); - perror("-COPY-F-ERR "); - } else { - struct RAB rab = cc$rms_rab; - rab.rab$l_fab = &fab; - if ((sts = sys_connect(&rab)) & STS_M_SUCCESS) { - FILE *tof; - char name[NAM$C_MAXRSS + 1]; - unsigned records = 0; - { - char *out = name,*inp = argv[2]; /* unquote(argv[2]) */ - int dot = FALSE; - 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 = TRUE; - } else { - if (strchr(":]\\/",*inp)) dot = FALSE; - } - *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 ) ) & STS$M_SUCCESS ) { - 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, MSG_TEXT),rsa); - sts = SS$_NORMAL; - } - } - sys_close(&fab); - } - } - if (sts == RMS$_NMF) sts = SS$_NORMAL; - } - if ( sts & STS$M_SUCCESS ) { - 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, MSG_TEXT)); - } - return sts; -} - -/***************************************************************** doimport() */ - -static struct qual importquals[] = { {"ascii", 0, COP_BINARY, NV, - "Transfer file in ascii mode (default)"}, - {"binary", COP_BINARY, 0, NV, "Transfer file in binary mode", }, - {NULL, 0, 0, NV, NULL} -}; -static struct param importpars[] = { {"from_filespec", REQ, LCLFS, NOPA, "for source file."}, - {"to_filespec", REQ, VMSFS, NOPA, "for destination file."}, - { NULL, 0, 0, NOPA, NULL } -}; - -static unsigned doimport( int argc, char *argv[], int qualc, char *qualv[] ) { - int options; - unsigned sts; - char *name; - FILE *fromf; - struct FAB fab = cc$rms_fab; - struct RAB rab = cc$rms_rab; - - UNUSED(argc); - - options = checkquals( 0, importquals, qualc, qualv ); - if( options == -1 ) - return SS$_BADPARAM; - - sts = SS$_BADFILENAME; - name = argv[1]; /*unquote( argv[1] );*/ - if ( *name == '\0' ) { - return sts; - } - fromf = openf( name, (options & COP_BINARY)? "rb": "r" ); - if( fromf == NULL ) { - printf( "%%ODS2-E-OPENERR, Can't open %s\n", name ); - perror( " - " ); - return sts; - } - fab.fab$b_fac = FAB$M_PUT; - fab.fab$l_fop = FAB$M_OFP | FAB$M_TEF; - fab.fab$b_org = FAB$C_SEQ; - if( options & COP_BINARY ) { - fab.fab$w_mrs = 512; - fab.fab$b_rfm = FAB$C_FIX; - } else { - fab.fab$b_rat = 0; - fab.fab$b_rfm = FAB$C_STM; - } - - fab.fab$l_fna = argv[2]; - fab.fab$b_fns = strlen( fab.fab$l_fna ); - sts = sys_create( &fab ); - if ( sts & STS$M_SUCCESS ) { - rab.rab$l_fab = &fab; - sts = sys_connect( &rab ); - if ( sts & STS$M_SUCCESS ) { - if( options & COP_BINARY ) { - char buf[512]; - size_t len; - - rab.rab$l_rbf = buf; - while( (len = fread( buf, 1, 512, fromf )) > 0 ) { - if( len != 512 ) - memset( buf+len, 0, 512-len ); - rab.rab$w_rsz = len; - sts = sys_put( &rab ); - if ( !( sts & STS$M_SUCCESS ) ) { - break; - } - } - } else { - while ( (rab.rab$l_rbf = fgetline( fromf, TRUE )) != NULL ) { - rab.rab$w_rsz = strlen( rab.rab$l_rbf ); - sts = sys_put( &rab ); - free( rab.rab$l_rbf ); - if ( !( sts & STS$M_SUCCESS ) ) { - break; - } - } - } - sys_disconnect( &rab ); - } - sys_close( &fab ); - } - fclose( fromf ); - if ( !(sts & STS$M_SUCCESS) ) { - printf("%%IMPORT-F-ERROR Status: %s\n",getmsg(sts, MSG_TEXT)); - } - - return sts; -} - -/******************************************************************* dodiff() */ - -/* dodiff: a simple file difference routine */ - -static 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 dodiff(int argc,char *argv[],int qualc,char *qualv[]) { - int sts, records = 0; - char rec[MAXREC + 2], *cpy = NULL; - char *name; - FILE *tof; - struct FAB fab = cc$rms_fab; - struct RAB rab = cc$rms_rab; - - UNUSED(argc); - UNUSED(qualc); - UNUSED(qualv); - - name = argv[1]; - sts = SS$_BADFILENAME; - if ( *name == '\0' ) { - return sts; - } - records = 0; - fab.fab$l_fna = name; - fab.fab$b_fns = strlen( fab.fab$l_fna ); - tof = openf( argv[2], "r" ); - if ( tof == NULL ) { - printf("%%ODS2-E-OPENERR, Could not open file %s\n",name); - perror( " - " ); - return SS$_NOSUCHFILE; - } - sts = sys_open( &fab ); - if ( sts & STS$M_SUCCESS ) { - rab.rab$l_fab = &fab; - sts = sys_connect( &rab ); - if( sts & STS$M_SUCCESS ) { - rab.rab$l_ubf = rec; - rab.rab$w_usz = MAXREC; - while( (sts = sys_get( &rab )) & STS$M_SUCCESS ) { - rec[rab.rab$w_rsz] = '\0'; - cpy = fgetline( tof, FALSE ); - if( cpy == NULL || - 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; - } - free(cpy); - cpy = NULL; - records++; - } - if( cpy != NULL ) free(cpy); - sys_disconnect(&rab); - } - sys_close(&fab); - } - fclose(tof); - if (sts == RMS$_EOF) sts = SS$_NORMAL; - if ( sts & STS$M_SUCCESS ) { - printf( "%%DIFF-I-Compared %d records\n", records ); - } else if ( sts != 4 ) { - printf("%%DIFF-F-Error %s in difference\n",getmsg(sts, MSG_TEXT)); - } - return sts; -} - -/******************************************************************* dotype() */ - -/* dotype: a file TYPE routine */ - -static struct param typepars[] = { {"filespec", REQ, VMSFS, NOPA, "for file on ODS-2 volume."}, - { NULL, 0, 0, NOPA, NULL } -}; - -static unsigned dotype(int argc,char *argv[],int qualc,char *qualv[]) { +int main( int argc,char **argv ) { int sts; - int records; - struct FAB fab = cc$rms_fab; - struct RAB rab = cc$rms_rab; + FILE *atfile = NULL; + char *rl = NULL; + qualp_t disks = NULL; - UNUSED(argc); - UNUSED(qualc); - UNUSED(qualv); - - records = 0; - - fab.fab$l_fna = argv[1]; - fab.fab$b_fns = strlen(fab.fab$l_fna); - sts = sys_open( &fab ); - if ( sts & STS$M_SUCCESS ) { - rab.rab$l_fab = &fab; - sts = sys_connect( &rab ); - if ( sts & STS$M_SUCCESS ) { - char rec[MAXREC + 2]; - rab.rab$l_ubf = rec; - rab.rab$w_usz = MAXREC; - while ( ( sts = sys_get( &rab ) ) & STS$M_SUCCESS ) { - 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 = SS$_NORMAL; - } - if ( !( sts & STS$M_SUCCESS ) ) { - printf("%%TYPE-F-ERROR Status: %s\n",getmsg(sts, MSG_TEXT)); - } - return sts; -} - -/***************************************************************** dosearch() */ - -/* dosearch: a simple file search routine */ - -static 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 } -}; - -static unsigned dosearch(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]; - register char firstch, *searstr, *searend, *s; - struct NAM nam = cc$rms_nam; - struct FAB fab = cc$rms_fab; - struct RAB rab = cc$rms_rab; - - UNUSED(argc); - UNUSED(qualc); - UNUSED(qualv); - - searstr = argv[2]; /* unquote( argv[2] ); */ - - searend = searstr + strlen( searstr ); - for ( s = searstr; s < searend; s++ ) { - *s = tolower( *s ); - } - firstch = *searstr++; - filecount = 0; - findcount = 0; - 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 & STS$M_SUCCESS ) { - nam.nam$l_rsa = rsa; - nam.nam$b_rss = NAM$C_MAXRSS; - fab.fab$l_fop = FAB$M_NAM; - while ( ( sts = sys_search( &fab ) ) & STS$M_SUCCESS ) { - sts = sys_open(&fab); - if ( !( sts & STS$M_SUCCESS ) ) { - printf("%%SEARCH-F-OPENFAIL, Open error: %s\n",getmsg(sts, MSG_TEXT)); - } else { - rab.rab$l_fab = &fab; - sts = sys_connect( &rab ); - if ( sts & STS$M_SUCCESS ) { - int printname = 1; - char rec[MAXREC + 2]; - filecount++; - rab.rab$l_ubf = rec; - rab.rab$w_usz = MAXREC; - while ( ( sts = sys_get( &rab ) ) & STS$M_SUCCESS ) { - 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" - ); - printf("%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 = SS$_NORMAL; - } - sys_close(&fab); - } - } - if (sts == RMS$_NMF || sts == RMS$_FNF) sts = SS$_NORMAL; - } - if ( sts & STS$M_SUCCESS ) { - 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, MSG_TEXT)); - } - return sts; -} - -/***************************************************************** dodelete() */ - -/* dodelete: you don't want to know! */ - -static struct qual delquals[] = { {"log", 1, 0, NV, "-List name of each file deleted. (Default)"}, - {"nolog", 0, 1, NV, NULL }, - { NULL, 0, 0, NV, NULL } -}; -static struct param delpars[] = { {"filespec", REQ, VMSFS, NOPA, - "for files to be deleted from ODS-2 volume. Wildcards are permitted.."}, - { NULL, 0, 0, NOPA, NULL } -}; - -static unsigned dodelete(int argc,char *argv[],int qualc,char *qualv[]) { - int sts = 0; - char res[NAM$C_MAXRSS + 1], rsa[NAM$C_MAXRSS + 1]; - struct NAM nam = cc$rms_nam; - struct FAB fab = cc$rms_fab; - int options, filecount = 0; - - 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 & STS$M_SUCCESS ) { - 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 ) ) & STS$M_SUCCESS ) { - sts = sys_erase(&fab); - if ( !( sts & STS$M_SUCCESS ) ) { - printf("%%DELETE-F-DELERR, Delete error: %s\n",getmsg(sts, MSG_TEXT)); - break; - } else { - filecount++; - if( options & 1 ) { - rsa[nam.nam$b_rsl] = '\0'; - if( options & 1 ) - printf("%%DELETE-I-DELETED, Deleted %s\n",rsa); - } - } - } - if (sts == RMS$_NMF) sts = SS$_NORMAL; - } - if ( sts & STS$M_SUCCESS ) { - if (filecount < 1) { - printf("%%DELETE-W-NOFILES, no files deleted\n"); - } - } else { - printf("%%DELETE-F-ERROR Status: %s\n",getmsg(sts, MSG_TEXT)); - } - - return sts; -} - -/******************************************************************* dotest() */ - -static struct param testpars[] = { {"parameter", REQ, STRING, NOPA, "for test."}, - { NULL, 0, 0, NOPA, NULL } -}; - -struct VCB *test_vcb; - -/* dotest: you don't want to know! */ - -static unsigned dotest( int argc, char *argv[], int qualc, char *qualv[] ) { - unsigned 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; -} - -/***************************************************************** doextend() */ - -/* more test code... */ - -static struct param extendpars[] = { {"ods-2_filespec", REQ, VMSFS, NOPA, "for file on ODS-2 volume."}, - { NULL, 0, 0, NOPA, NULL } -}; - -static unsigned doextend(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; - sts = sys_open( &fab ); - if ( sts & STS$M_SUCCESS ) { - fab.fab$l_alq = 32; - sts = sys_extend(&fab); - sys_close(&fab); - } - if ( !( sts & STS$M_SUCCESS ) ) { - printf("%%EXTEND-F-ERROR Status: %s\n",getmsg(sts, MSG_TEXT)); - } - return sts; -} - -/****************************************************************** dostats() */ - -/* dostats: print some simple statistics */ - -static unsigned dostats( void ) { - - printf("Statistics:-\n"); - direct_show(); - cache_show(); - phyio_show(SHOW_STATS); - - return SS$_NORMAL; -} - -/******************************************************************* doshow() */ - -/*********************************************************** show_version() */ - -#define MNAMEX(n) #n -#define MNAME(n) MNAMEX(n) - -static void show_version( void ) { - printf(" %s %s", MNAME(MODULE_NAME), MODULE_IDENT ); - printf( " built %s %s", __DATE__, __TIME__ ); +#ifdef VMS + char str[2048]; +#endif #ifdef USE_READLINE - printf(" with readline version %s", rl_library_version ); + char *p; + wordexp_t wex; + 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 ); + } #endif -#ifdef USE_WNASPI32 -# ifdef USE_READLINE - printf( " and" ); -# else - printf( " with" ); -# endif - printf( " direct SCSI access support"); + sts = sys_initialize(); + if( !(sts & STS$M_SUCCESS) ) { + printf( "Unable to initialize library: %s\n", getmsg( sts, MSG_TEXT ) ); + exit(EXIT_FAILURE); + } + + add_diskkwds( mouquals, DT_NAME, &disks ); + add_diskkwds( iniquals, DT_NAME, &disks ); + + argc--; + argv++; + + while( 1 ) { + char *ptr = NULL; + + if( argc > 0 ) { + size_t n, al = 0; + + free( rl ); + rl = NULL; + + for( n = 0; n < (unsigned)argc; n++ ) + if( !strcmp( "$", argv[n] ) ) + break; + else + al += 1 + strlen( argv[n] ); + if( al > 0 ) { + size_t i; + char *cp; + + cp = + rl = (char *) malloc( al ); + if( rl == NULL ) { + perror( "malloc" ); + exit( EXIT_FAILURE ); + } + for( i = 0; i < n; i++ ) { + size_t len; + + len = strlen( *argv ); + if( i != 0 ) + *cp++ = ' '; + memcpy( cp, *argv++, len ); + argc--; + cp += len; + } + *cp = '\0'; + + ptr = rl; +#ifdef USE_READLINE + if( *ptr ) + add_history( ptr ); #endif - printf( "\n " ); - phyio_show( SHOW_FILE64 ); - putchar( '\n' ); + } + if( argc > 0 ) { + argc--; + argv++; + } + } + if( ptr == NULL ) { + if (atfile != NULL) { + free( rl ); + if( (rl = fgetline( atfile, FALSE )) == NULL) { + fclose(atfile); + atfile = NULL; + } else { +#ifndef _WIN32 + ptr = strchr( rl, '\r' ); + if( ptr != NULL ) + *ptr = '\0'; +#endif + if( verify_cmd ) + printf("%c> %s\n", (vms_qual? '$': '-'), rl ); + } + ptr = rl; + } + if( ptr == NULL ) { +#ifdef VMS + if( getcmd( str, sizeof(str), + (vms_qual? "$> ": "-> ") ) == NULL ) + break; + ptr = str; +#else + free( rl ); +#ifdef USE_READLINE + rl = + ptr = readline( vms_qual? + MNAME(MODULE_NAME) "$> ": MNAME(MODULE_NAME) "-> " ); + if (rl == NULL) { + break; + } else { + if( *rl != '\0' ) { + add_history( rl ); + } + } +#else + printf( "%s", vms_qual? "$> ": "-> " ); + if( (rl = fgetline(stdin, FALSE)) == NULL ) break; + ptr = rl; +#endif +#endif + } + } + + while( *ptr == ' ' || *ptr == '\t' ) + ptr++; + if( !strlen(ptr) || *ptr == '!' || *ptr == ';' ) + continue; + + if (*ptr == '@') { + if (atfile != NULL) { + printf("%%ODS2-W-INDIRECT, nested indirect command files not supported\n"); + } else { + if( (atfile = openf( ptr + 1, "r" )) == NULL ) { + perror("%%ODS2-E-INDERR, Failed to open indirect command file"); + putchar( '\n' ); + argc = 0; + } + } + continue; + } + + sts = cmdsplit(ptr); + if( sts == -1 ) + break; + if( !(sts & STS$M_SUCCESS) ) { + if( atfile != NULL ) { + fclose(atfile); + atfile = NULL; + } + argc = 0; + } + } /* while 1 */ + + free( rl ); + +#ifdef USE_READLINE + if( hfname != NULL ) { + write_history( hfname ); + clear_history(); + } + wordfree( &wex ); /* hfname points into wex and should not be free()d */ +#endif + if (atfile != NULL) + fclose(atfile); + + free( disks ); + + exit( EXIT_SUCCESS ); +} + +/***************************************************************** add_diskkwds() */ +/* Build parser keyword list from disk table entries */ +static void add_diskkwds( qualp_t qualset, const char *qname, qualp_t *disks ) { + qualp_t qp; + + if( *disks == NULL ) { + disktypep_t dp; + size_t n = 0; + + for( dp = disktype; dp->name != NULL; dp++ ) + ; + n = (size_t) (dp - disktype); + if( (n << MOU_V_DEVTYPE) & ~MOU_DEVTYPE ) + abort(); /* MOU_DEVTYPE isn't wide enough for the max index */ + + *disks = (qualp_t)calloc( n+1, sizeof( qual_t ) ); + if( *disks == NULL ) { + perror( "malloc" ); + exit( EXIT_FAILURE ); + } + /* Do NOT sort 1st element (default, must have search seq 1) + * or last - list terminator. + */ + qsort( disktype+1, n - 1, sizeof( disktype_t ), + disktypecmp ); + + for( qp = *disks, dp = disktype; dp->name != NULL; dp++, qp++ ) { + qp->name = dp->name; + qp->set = OPT_NOSORT | (int)((dp-disktype) << MOU_V_DEVTYPE); + qp->clear = MOU_DEVTYPE; + qp->qtype = NOVAL; + if( dp != disktype ) + qp->helpstr = ""; + } + + } + for( qp = qualset; qp->name != NULL; qp++ ) + if( !strcmp( qp->name, qname ) ) + break; + if( qp->name == NULL ) + abort(); + + qp->arg = *disks; + return; } -/*********************************************************** doshow() */ +/***************************************************************** disktypecmp() */ -static struct qual showkwds[] = { {"cwd", 0, 0, NV, "Working directory on local system"}, - {"default", 1, 0, NV, "Default directory on VMS volume"}, - {"devices", 2, 0, NV, "Devices"}, - {"qualifier_style", 3, 0, NV, "Qualifier style (Unix, VMS)" }, - {"statistics", 4, 0, NV, "Debugging statistics"}, - {"time", 5, 0, NV, "Time"}, - {"verify", 6, 0, NV, "Command file echo" }, - {"version", 7, 0, NV, "Version"}, - {"volumes", 8, 0, NV, "Mounted volume information" }, - {NULL, 0, 0, NV, NULL } -}; -static struct param showpars[] = { {"item_name", REQ, KEYWD, PA(showkwds), "" }, - {NULL, 0, 0, NOPA, NULL } -}; +static int disktypecmp( const void *da, const void *db ) { -unsigned doshow(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: { - size_t size = 32; - char *buf = NULL; - while( 1 ) { - char *nbuf; - nbuf = (char *)realloc( buf, size ); - if( nbuf == NULL ) { - free( buf ); - return SS$_INSFMEM; - } - buf = nbuf; - if( getcwd( buf, size ) != NULL ) - break; - if( errno != ERANGE ) { - perror( "getcwd" ); - return SS$_BADPARAM; - } - size *= 2; - } - printf( " Current working directory is %s\n", buf ); - free( buf ); - return SS$_NORMAL; - } - case 1: { - 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; - sts = sys_setddir( NULL, &curlen, &curdsc ); - if ( sts & STS$M_SUCCESS ) { - curdir[curlen] = '\0'; - puts( curdir ); - } else { - printf("%%ODS2-E-GETDEF, Error %s getting default\n",getmsg(sts, MSG_TEXT)); - } - return sts; - } - case 2: - phyio_show( SHOW_DEVICES ); - virt_show( NULL ); - return SS$_NORMAL; - case 3: - printf ( " Qualifier style: %s\n", vms_qual? "/VMS": "-unix" ); - return SS$_NORMAL; - case 4: - return dostats(); - case 5: { - 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 & STS$M_SUCCESS ) { - timstr[timlen] = '\0'; - printf(" %s\n",timstr); - } else { - printf("%%SHOW-W-TIMERR error %s\n",getmsg(sts, MSG_TEXT)); - } - } - return SS$_NORMAL; - case 6: - printf( "Command file verification is %s\n", (verify_cmd? "on": "off") ); - return SS$_NORMAL; - case 7: - show_version(); - return SS$_NORMAL; - case 8: - show_volumes(); - return SS$_NORMAL; - } - return SS$_NORMAL; + return strcmp( ((disktypep_t )da)[0].name, + ((disktypep_t )db)[0].name ); } -/******************************************************************* doset() */ -/*********************************************************** setdef() */ +/***************************************************************** cmdsplit() */ -static int default_set = FALSE; +/* cmdsplit: break a command line into its components */ -static unsigned setdef( char *newdef ) { - register unsigned sts; - struct dsc_descriptor defdsc; +/* + * 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 +static int cmdsplit( char *str ) { + int argc = 0,qualc = 0; + char *argv[MAXITEMS],*qualv[MAXITEMS]; + char *sp = str; + int i; + char q = vms_qual? '/': '-'; - defdsc.dsc_a_pointer = (char *) newdef; - defdsc.dsc_w_length = (unsigned short)strlen( defdsc.dsc_a_pointer ); - sts = sys_setddir( &defdsc, NULL, NULL ); - if ( sts & STS$M_SUCCESS ) { - default_set = TRUE; - } else { - printf( "%%ODS2-E-SETDEF, Error %s setting default to %s\n", getmsg(sts, MSG_TEXT), newdef ); - } - return sts; -} + for( i = 0; i < MAXITEMS; i++ ) + argv[i] = qualv[i] = ""; -static unsigned setcwd( char *newdef ) { - if( chdir( newdef ) != 0 ) { - printf( "%%ODS2-W-SETDEF, Error %s setting cwd to %s\n", - strerror( errno ), newdef ); - return SS$_BADPARAM; - } - return SS$_NORMAL; -} - -/************************************************************ sethelp() */ - -static hlpfunc_t sethelp; - -static struct qual setkwds[] = { {"cwd", 0, 0, NV, "Working directory on local system"}, - {"directory_qualifiers", 1, 0, NV, "Default qualifiers for DIRECTORY command" }, - {"default", 2, 0, NV, "Default directory on VMS volume"}, - {"qualifier_style", 3, 0, NV, "Qualifier style (Unix, VMS)"}, - {"verify", 4, 0, NV, "-Display commands in indirect files"}, - {"noverify", 5, 0, NV, NULL }, - {NULL, 0, 0, NV, NULL } -}; -static struct qual setqskwds[] = {{"unix", 1, 0, NV, "Unix style options, '-option'"}, - {"vms", 2, 0, NV, "VMS style qualifiers, '/qualifier'"}, - {NULL, 0, 0, NV, NULL } -}; -static struct param setpars[] = { {"item_name", REQ, KEYWD, PA(setkwds), "" }, - {"value" , CND, KEYWD, sethelp, setqskwds, "" }, - {NULL, 0, 0, NOPA, NULL }, -}; - -static 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 = "working directory for local files"; - p->ptype = STRING; - break; - case 1: - p->helpstr = "default directory on volume"; - p->ptype = VMSFS; - break; - case 2: - p->helpstr = "directory qualifier name "; - p->ptype = KEYWD; - p->arg = dirquals; - break; - case 3: - p->helpstr = "style "; - p->ptype = KEYWD; - p->arg = setqskwds; - break; - case 4: - case 5: - p->ptype = NONE; - break; - - default: - abort(); - } - return NULL; -} - -/*********************************************************** doset() */ - -static unsigned doset(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 */ - case 2: /* current working directory */ - if( qualc ) { - printf( "%%ODS2-E-NOQUAL, No qualifiers are permitted\n" ); - return 0; - } - return (parnum == 0)? setdef( argv[2] ) : setcwd( argv[2] ); - case 1:{ /* directory_qualifiers */ - int options = checkquals(dir_default,dirquals,qualc,qualv); - if( options == -1 ) - return SS$_BADPARAM; - dir_defopt = options; - return 1; - } - case 3: { /* 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; + while( *sp != '\0' ) { + while( *sp == ' ' ) sp++; + if( *sp == '\0' ) break; + + if( *sp == q ) { /* Start of qualifier */ + *sp++ = '\0'; /* Terminate previous word */ + if (*sp == q) { /* qq = end of qualifiers */ + sp++; + q = '\0'; + continue; + } + if( qualc >= MAXITEMS ) { + printf( "%%ODS2-E-CMDERR, Too many qualifiers specified\n" ); + return 0; + } + qualv[qualc++] = sp; + } else { /* New argument */ + int qt; + + if( argc >= MAXITEMS ) { + printf( "%%ODS2-E-CMDERR, Too many arguments specified\n" ); + return 0; + } + argv[argc++] = sp; + if( (qt = *sp) == '"' || (qt = *sp) == '\'' ) { + ++argv[argc-1]; + for( ++sp; *sp && (*sp != qt || sp[1] == qt); sp++ ) { + if( *sp == qt ) /* Interior "" => " */ + memmove( sp, sp+1, strlen(sp+1)+1 ); + } + if( *sp == qt ) { /* Ending " of string */ + *sp++ = '\0'; + if( *sp && *sp != ' ' ) { /* Something following */ + printf( "%%ODS2-E-CMDERR, Unterminated string\n" ); + return 0; + } + } else { + printf( "%%ODS2-E-CMDERR, Unterminated string\n" ); + return 0; + } + continue; + } /* Quoted string */ } - return 1; - } - case 4: - verify_cmd = 1; - return 1; - case 5: - verify_cmd = 0; - return 1; + + /* Find end of atom */ + + while( !(*sp == '\0' || *sp == ' ' || *sp == q) ) sp++; + if (*sp == '\0') + break; + if( *sp == ' ' ) + *sp++ = '\0'; } + + if( argc > 0 ) + return cmdexecute( argc, argv, qualc, qualv ); + + return 1; } -/******************************************************************* dodismount() */ -static struct param dmopars[] = { {"drive_letter", REQ, STRING, NOPA, "Drive containing volume to dismount", }, - {NULL, 0, 0, NOPA, NULL } -}; +/*************************************************************** cmdexecute() */ +/* cmdexecute: identify and execute a command */ -unsigned dodismount(int argc,char *argv[],int qualc,char *qualv[]) { - struct DEV *dev; +static int cmdexecute( int argc, char **argv, int qualc, char **qualv ) { + char *ptr; + CMDSETp_t cmd, cp = NULL; + unsigned cmdsiz; + int minpars, maxpars; + paramp_t p; int sts; - UNUSED(argc); - UNUSED(qualc); - UNUSED(qualv); + ptr = argv[0]; + while (*ptr != '\0') { + *ptr = tolower(*ptr); + ptr++; + } + ptr = argv[0]; + cmdsiz = strlen(ptr); - sts = device_lookup(strlen(argv[1]),argv[1],FALSE,&dev); - if (sts & 1) { - if (dev->vcb != NULL) { - sts = dismount(dev->vcb); - } else { - sts = SS$_DEVNOTMOUNT; + for( cmd = maincmds; cmd->name != NULL; cmd++ ) { + if( cmdsiz <= strlen( cmd->name ) && + keycomp( ptr, cmd->name ) ) { + if( cmd->uniq ) { + if( cmd->uniq > 0 && (int) cmdsiz >= cmd->uniq ) { + cp = cmd; /* Unique in n */ + break; + } + if( cmd->uniq < 0 && cmdsiz < (size_t) abs( cmd->uniq ) ) + cp = cmd; /* Requires n even if fewer unique */ + } + if( cp != NULL ) { + printf("%%ODS2-E-AMBCMD, '%s' is ambiguous\n", ptr ); + return 0; + } + cp = cmd; } } - if (!(sts & STS$M_SUCCESS)) printf("%%DISMOUNT-E-STATUS Error: %s\n",getmsg(sts, MSG_TEXT)); + 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); + + cache_flush(); + return sts; } -#if MOU_WRITE != 1 -#error MOU_WRITE != 1 -#endif - -#define DT_NAME "drive_type" - -static struct qual mouquals[] = { {DT_NAME, 0, MOU_DEVTYPE, CV(NULL), "Drive type (DEC model name) "}, - {"image", MOU_VIRTUAL, 0, NV, "Mount a disk image file", }, - {"readonly", 0, MOU_WRITE, NV, "Only allow reading from volume"}, - {"virtual", MOU_VIRTUAL, 0, NV, NULL, }, - {"write", MOU_WRITE, 0, NV, "Allow writing to volume", }, - {NULL, 0, 0, NV, NULL } }; - -static struct param moupars[] = { {"volumes", REQ, LIST, NOPA, - "devices or disk image(s) of volume set in RVN order separated by comma" - }, - {"labels", OPT, LIST, NOPA, "volume labels in RVN order separated by comma" }, - { NULL, 0, 0, NOPA, NULL } -}; - -/******************************************************************* domount() */ - /*********************************************************** parselist() */ -static int parselist( char ***items, size_t min, char *arg, const char *label ) { +int parselist( char ***items, size_t min, char *arg, const char *label ) { size_t n = 0, i; char **list = NULL; @@ -1799,6 +616,7 @@ static int parselist( char ***items, size_t min, char *arg, const char *label ) } if( list == NULL ) { + n = 0; list = (char **) malloc( (min + 1) * sizeof( char * ) ); if( list == NULL ) { printf( "%%ODS2-E-NOMEM, Not enough memory for %s\n", label ); @@ -1814,577 +632,158 @@ static int parselist( char ***items, size_t min, char *arg, const char *label ) return (int)n; } -/*********************************************************** domount() */ - -static unsigned domount( int argc,char *argv[],int qualc,char *qualv[] ) { - int sts = 1,devices = 0; - char **devs = NULL, **labs = NULL; - - int options; - - options = checkquals( 0, mouquals, qualc, qualv ); - if( options == -1 ) - return SS$_BADPARAM; - - UNUSED(argc); - - if( (devices = parselist( &devs, 0, argv[1], "devices")) < 0 ) - return SS$_BADPARAM; - if( parselist( &labs, devices, argv[2], "labels") < 0 ) { - free( devs ); - return SS$_BADPARAM; - } - - if (devices > 0) { - struct VCB *vcb; - sts = mount( options | MOU_LOG, devices, devs, labs, &vcb ); - if (sts & STS$M_SUCCESS) { - if( !default_set ) { - char *colon, *buf; - size_t len; - - len = strlen( vcb->vcbdev[0].dev->devnam ); - buf = (char *) malloc( len + sizeof( ":[000000]" )); - if( buf == NULL ) { - perror( "malloc" ); - } else { - colon = strchr( vcb->vcbdev[0].dev->devnam, ':' ); - if( colon != NULL ) - len = (size_t)(colon - vcb->vcbdev[0].dev->devnam); - memcpy( buf, vcb->vcbdev[0].dev->devnam, len ); - memcpy( buf+len, ":[000000]", sizeof( ":[000000]" ) ); - setdef(buf); - free( buf ); - } - } - test_vcb = vcb; - } else { - printf("%%ODS2-E-MOUNTERR, Mount failed with %s\n", getmsg(sts, MSG_TEXT)); - } - } - - free( devs ); - free( labs ); - return sts; -} - -/******************************************************************* dospawn() */ - -static unsigned dospawn( int argc,char *argv[],int qualc,char *qualv[] ) { -#ifdef VMS - unsigned sts; - - UNUSED( argc ); - UNUSED( argv ); - UNUSED( qualc ); - UNUSED( qualv ); - - sts = lib$spawn( 0,0,0,0,0,0,0,0,0,0,0,0,0 ); - return sts; -#else -# ifdef _WIN32 - UNUSED( argc ); - UNUSED( argv ); - UNUSED( qualc ); - UNUSED( qualv ); - - if( system( "cmd" ) == -1 ) { - perror( "cmd" ); - return SS$_NOSUCHFILE; - } - return SS$_NORMAL; -# else - char *shell, *p; - pid_t pid; - - UNUSED( argc ); - UNUSED( argv ); - UNUSED( qualc ); - UNUSED( qualv ); - - if( (shell = getenv( "SHELL" )) == NULL ) - shell = "/bin/sh"; - if( (p = strrchr( shell, '/')) == NULL ) - p = shell; - else - p++; - - if( (pid = fork()) == 0 ) { - execlp( shell, p, (char *)NULL ); - perror( "%s" ); - exit(EXIT_FAILURE); - } - if( pid == -1 ) { - perror( shell ); - return SS$_NOSUCHFILE; - } - waitpid( pid, NULL, 0 ); - - return SS$_NORMAL; -# endif -#endif - -} - -/******************************************************************* dohelp() */ - -/*********************************************************** cmdhelp() */ - -static 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" ); -} - -/*********************************************************** cmdhelp() */ - -static void qualhelp( int par, struct qual *qtable ) { - struct qual *q; - int n = 0; - size_t max = 0, col = 4; - - 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: - if( *q->helpstr ) { - printf( NOSTR "%-*s - %s\n", - (int) (max-NOSTR_LEN), q->name, q->helpstr+1 ); - break; - } - if( col + max > 50 ) { - printf( "\n " ); - col = 8; - } else { - while( col < 8 ) { - putchar( ' ' ); col++; - } - } - printf( "%-*s", (int) max, q->name ); - col += max; - break; - case KEYVAL: - case KEYCOL: - printf( NOSTR "%s=%-*s - %s\n", - q->name, (int) (max-(NOSTR_LEN+strlen(q->name)+1)), "", - q->helpstr+1 ); - qualhelp( q->qtype == KEYCOL? 1 : -(int)max, (struct qual *)q->arg ); - break; - default: - abort(); - } - else - switch( q->qtype ) { - case NOVAL: - if( *q->helpstr ) { - printf("%-*s - %s\n", (int) max, q->name, q->helpstr ); - break; - } - if( col + max > 50 ) { - printf( "\n " ); - col = 8; - } else { - while( col < 8 ) { - putchar( ' ' ); col++; - } - } - printf( "%-*s", (int) max, q->name ); - col += max; - break; - case KEYVAL: - case KEYCOL: - printf( "%s=%-*s - %s\n", q->name, (int)(max-strlen(q->name)+1), "", - q->helpstr ); - qualhelp( q->qtype == KEYCOL? 1 : -(int)max, (struct qual *)q->arg ); - break; - default: - abort(); - } - } - } - if( par >= 0 ) - printf( "\n" ); -} -#undef NOSTR -#undef NOSTR_LEN - -/*********************************************************** parhelp() */ - -static 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( p->helpstr ) { - size_t len = strlen(p->name); - if( len > max ) - max = len; - } - } - for( p = ptable; p->name != NULL; p++ ) { - if( 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 %s PARAMETER for more about each parameter.\n", - cmd->name ); - 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; -} - -/*********************************************************** dohelp() */ -/* help: Display help guided by command table. */ - -#define NCMD 17 -static struct CMDSET cmdset[NCMD+1]; - -static struct param helppars[] = { {"command", OPT, CMDNAM, PA(cmdset), "" }, - {"parameter", OPT, STRING, NOPA, "" }, - {"value", OPT, STRING, NOPA, "" }, - {NULL, 0, 0, NOPA, NULL }, -}; - -/* information about the commands we know... */ -static unsigned dohelp(int argc,char *argv[],int qualc,char *qualv[]); - -static struct CMDSET cmdset[NCMD+1] = { - { "copy", docopy, 0,copyquals,copypars, "Copy a file from VMS to host file", }, - { "delete", dodelete, 0,delquals, delpars, "Delete a VMS file", }, - { "difference",dodiff, 0,NULL, diffpars, "Compare VMS file to host file", }, - { "directory", dodir, 0,dirquals,dirpars, "List directory of VMS files", }, - { "dismount", dodismount,0,NULL, dmopars, "Dismount a VMS volume", }, - { "exit", NULL, 2,NULL, NULL, "Exit ODS2", }, - { "extend", doextend, 0,NULL, extendpars, NULL }, - { "help", dohelp, 0,NULL, helppars, "Obtain help on a command", }, - { "import", doimport, 0,NULL, importpars, "Copy a file from host to VMS", }, - { "mount", domount, 0,mouquals, moupars, "Mount a VMS volume", }, - { "quit", NULL, 2,NULL, NULL, "Exit ODS-2", }, - { "search", dosearch, 0,NULL, searchpars, "Search VMS file for a string", }, - { "set", doset, 0,NULL, setpars, "Set PARAMETER - set HELP for list", }, - { "show", doshow, 0,NULL, showpars, "Display state", }, - { "spawn", dospawn, 0,NULL, NULL, "Open a command subprocess", }, - { "test", dotest, 0,NULL, testpars, NULL }, - { "type", dotype, 0,NULL, typepars, "Display a VMS file on the terminal", }, - { NULL, NULL, 0,NULL, NULL, NULL } /* ** END MARKER ** */ -}; - -static unsigned dohelp(int argc,char *argv[],int qualc,char *qualv[]) { - struct CMDSET *cmd; - - UNUSED(qualc); - UNUSED(qualv); - - if( argc <= 1 ) { - show_version(); - - printf( " The orginal version of ods2 was developed by Paul Nankervis\n" ); - printf( " \n" ); - printf( " This modified version was developed by Timothe Litt, and\n" ); - printf( " incorporates significant previous modifications by Larry \n" ); - printf( " Baker of the USGS and by Hunter Goatley\n" ); - printf("\n Please send problems/comments to litt@ieee.org\n\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 ) { - printf( "\n" ); - printf( "You can mount a volume(-set) from either physical devices\n" ); - printf( "such a CDROM or hard disk drive, or files containing an\n" ); - printf( "image of a volume, such as a .ISO file or simulator disk\n\n" ); - printf( "To mount a disk image, use the %cimage qualifier and\n", - (vms_qual? '/': '-') ); - printf( "specify the filename as the parameter.\n\n" ); - printf( "If the filename contains %c, specify it in double quotes\n\n", - (vms_qual? '/': '-') ); - printf( "Mount will assign a virtual device name to each volume.\n" ); - printf( "You can select a virtual device name using the format\n" ); - printf( " dka100=my_files.iso\n\n" ); - printf( "To mount a physical device, use the format:\n" ); - phyio_help(stdout); - printf( "To mount a volume set, specify all the members in RVN order\n" ); - printf( "as a comma-separated list.\n\n" ); - printf( "If you specify a list of volume labels, they must be in\n" ); - printf( "the same order as the volumes, and each must match the label\n" ); - printf( "stored in the data\n" ); - } - return 1; - } - } - printf( "%s: command not found\n", argv[1] ); - return 0; -} - -/*************************************************************** cmdexecute() */ -/* cmdexecute: identify and execute a command */ - -static 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() */ - -/* cmdsplit: break a command line into its components */ - -/* - * 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)... +/******************************************************************* checkquals() */ +/* checkquals: Given valid qualifier definitions, process qualifer + * list from a command left to right. Also handles parameters and + * qualifier values (/Qual=value). */ -#define MAXITEMS 32 -static int cmdsplit(char *str) { - int argc = 0,qualc = 0; - char *argv[MAXITEMS],*qualv[MAXITEMS]; - char *sp = str; + +int checkquals(int result, qualp_t qualset,int qualc,char **qualv) { int i; - char q = vms_qual? '/': '-'; + const char *type; - for( i = 0; i < MAXITEMS; i++ ) argv[i] = qualv[i] = ""; +#ifdef _WIN32 /* Code analysis thinks qualset can be NULL, resulting in a false positive. */ + if( qualset == NULL ) + abort(); +#endif - while( *sp != '\0' ) { - while( *sp == ' ' ) sp++; - if( *sp == '\0' ) - break; + type = (qualc < 0)? "parameter": "qualifier"; + qualc = abs( qualc ); - if( *sp == q ) { /* Start of qualifier */ - *sp++ = '\0'; /* Terminate previous word */ - if (*sp == q) { /* qq = end of qualifiers */ - sp++; - q = '\0'; - continue; - } - if( qualc >= MAXITEMS ) { - printf( "%%ODS2-E-CMDERR, Too many qualifiers specified\n" ); - return 0; - } - qualv[qualc++] = sp; - } else { /* New argument */ - if( argc >= MAXITEMS ) { - printf( "%%ODS2-E-CMDERR, Too many arguments specified\n" ); - return 0; - } - argv[argc++] = sp; - if( *sp == '"' ) { - ++argv[argc-1]; - for( ++sp; *sp && (*sp != '"' || sp[1] == '"'); sp++ ) { - if( *sp == '"' ) /* Interior "" => " */ - memmove( sp, sp+1, strlen(sp+1)+1 ); + for( i = 0; i < qualc; i++ ) { + char *qv; + qualp_t 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; } - if( *sp == '"' ) { /* Ending " of string */ - *sp++ = '\0'; - if( *sp && *sp != ' ' ) { /* Something following */ - printf( "%%ODS2-E-CMDERR, Unterminated string\n" ); - return 0; - } - } else { - printf( "%%ODS2-E-CMDERR, Unterminated string\n" ); - return 0; - } - continue; - } /* Quoted string */ + 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; - /* Find end of atom */ + 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( qp->qtype == STRVAL ) { + *((char **)qp->arg) = qv; + continue; + } + 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 DECVAL: { + unsigned long int val; + char *ep; - while( !(*sp == '\0' || *sp == ' ' || *sp == q) ) sp++; - if (*sp == '\0') - break; - if( *sp == ' ' ) - *sp++ = '\0'; + errno = 0; + val = strtoul( qv, &ep, 10 ); + if( !*qv || *ep || errno != 0 ) { + printf( "%%ODS2-BADVAL, %s is not a valid number\n", qv ); + return -1; + } + *((unsigned *)qp->arg) = (unsigned)val; + break; + } + case KEYVAL: + case KEYCOL: + result = checkquals( result, (qualp_t )qp->arg, -1, &qv ); + if( result == -1 ) + return result; + break; + default: + abort(); + } + qv = nvp; + } while( qv != NULL ); + } } + return result; +} - if( argc > 0 ) - return cmdexecute( argc, argv, qualc, qualv ); +/******************************************************************* keycomp() */ +/* keycomp: routine to compare parameter to a keyword - case insensitive! */ +int keycomp(const char *param, const char *keywrd) { + while (*param != '\0') { + if( tolower(*param++) != tolower(*keywrd++) ) + return 0; + } return 1; } -/******************************************************************* main() */ +/******************************************************************* fgetline() */ +/* Read a line of input - unlimited length + * Removes \n, returns NULL at EOF + * Caller responsible for free() + */ +char *fgetline( FILE *stream, int keepnl ) { + size_t bufsize = 0, + xpnsize = 80, + idx = 0; + char *buf = NULL; + int c; + + if( (buf = malloc( (bufsize = xpnsize) )) == NULL ) { + perror( "malloc" ); + abort(); + } + + while( (c = fgetc( stream )) != EOF && c != '\n' ) { + if( idx + (keepnl != 0) +2 > bufsize ) { /* Now + char + (? \n) + \0 */ + char *nbuf; + bufsize += xpnsize; + nbuf = (char *) realloc( buf, bufsize ); + if( nbuf == NULL ) { + perror( "realloc" ); + abort(); + } + buf = nbuf; + } + buf[idx++] = c; + } + if( c == '\n' ) { + if( keepnl ) + buf[idx++] = '\n'; + } else { + if( c == EOF && idx == 0 ) { + free( buf ); + return NULL; + } + } + buf[idx] = '\0'; + return buf; +} /*********************************************************** getcmd() */ @@ -2401,15 +800,16 @@ static char *getcmd( char *inp, size_t max, char *prompt ) { if (key_table_id == 0) { status = smg$create_key_table( &key_table_id ); - if (status & 1) + if( status & STS$M_SUCCESS ) status = smg$create_virtual_keyboard( &keyboard_id ); - if (!(status & 1)) return (NULL); + if( !(status & STS$M_SUCCESS) ) + 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); + 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) + if( status == SMG$_EOF ) retstat = NULL; else { inp[input_d.dsc_w_length] = '\0'; @@ -2420,230 +820,64 @@ static char *getcmd( char *inp, size_t max, char *prompt ) { } #endif /* VMS */ -/*********************************************************** main() */ -/* main: the simple mainline of this puppy... */ +/*********************************************************** prvmstime() */ -/* - * 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. - */ +int prvmstime(VMSTIME vtime, const char *sfx) { + int sts = 0; + char tim[24]; + static const VMSTIME nil; + struct dsc_descriptor timdsc; -int main( int argc,char *argv[] ) { - int sts; - char *command_line = NULL; - FILE *atfile = NULL; - char *rl = NULL; -#ifdef VMS - char str[2048]; -#endif -#ifdef USE_READLINE - char *p; - wordexp_t wex; - 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 ); + 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 & STS$M_SUCCESS) ) + printf("%%ODS2-W-TIMERR, SYS$ASCTIM error: %s\n",getmsg(sts, MSG_TEXT)); + tim[23] = '\0'; + printf(" %s",tim); + } else { + printf( " %-23s", " " ); + sts = 1; + } + if (sfx != NULL) + printf( "%s", sfx ); + return sts; +} + +/*********************************************************** pwrap() */ + +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); + + for( p = pbuf; *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; } -#endif - sts = sys_initialize(); - if( !(sts & STS$M_SUCCESS) ) { - printf( "Unable to initialize library: %s\n", getmsg( sts, MSG_TEXT ) ); - exit(EXIT_FAILURE); - } - { /* Build parser keyword list from disk table entries */ - struct qual *qp, *kp = NULL; - struct disktype * dp; - size_t n = 0; - - for( dp = disktype; dp->name != NULL; dp++ ) - ; - n = (size_t) (dp - disktype); - kp = (struct qual *)calloc( n+1, sizeof( struct qual ) ); - if( kp == NULL ) { - perror( "malloc" ); - exit( EXIT_FAILURE ); - } - for( qp = mouquals; qp->name != NULL; qp++ ) - if( !strcmp( qp->name, DT_NAME ) ) - break; - qp->arg = kp; - - for( dp = disktype; dp->name != NULL; dp++ ) { - kp->name = dp->name; - kp->set = (int)((dp-disktype) << MOU_V_DEVTYPE); - kp->clear = MOU_DEVTYPE; - kp->qtype = NOVAL; - if( dp != disktype ) - kp->helpstr = ""; - kp++; - } - if( (((unsigned)(dp-disktype)) << MOU_V_DEVTYPE) & ~MOU_DEVTYPE ) - abort(); /* MOU_DEVTYPE isn't wide enough for the max index */ - } - - if( argc > 1 ) { - int i, l = 0; - for( i = 1; i < argc; i++ ) { - int al; - char *newp; - - al = strlen(argv[i]); - newp = (char *)realloc( command_line, l+(l != 0)+al+1 ); - if( newp == NULL ) { - perror( "realloc" ); - exit (1); - } - command_line = newp; - if( l != 0 ) command_line[l++] = ' '; - memcpy( command_line+l, argv[i], al+1 ); - l += al; - } - } - while( 1 ) { - char *ptr = NULL; - if( command_line ) { - static int i = 0; - char *p; - - ptr = command_line + i; - if( (p = strchr( ptr, '$' )) == NULL ) { - if( *ptr == '\0' ) { - free( command_line ); - command_line = NULL; - ptr = NULL; - } else { - i += strlen( ptr ); - } - } else { - *p = '\0'; - i += strlen( ptr ) +1; - } -#ifdef USE_READLINE - if( ptr != NULL && *ptr ) - add_history( ptr ); -#endif - } - if( ptr == NULL ) { - if (atfile != NULL) { - if( rl != NULL ) free( rl ); - if( (rl = fgetline(atfile, FALSE)) == NULL) { - fclose(atfile); - atfile = NULL; - } else { -#ifndef _WIN32 - ptr = strchr( rl, '\r' ); - if( ptr != NULL ) - *ptr = '\0'; -#endif - if( verify_cmd ) - printf("$> %s\n", rl); - } - ptr = rl; - } - if( ptr == NULL ) { -#ifdef VMS - if( getcmd( str, sizeof(str), "$> " ) == NULL ) break; - ptr = str; -#else - if( rl != NULL ) { - free( rl ); - } -#ifdef USE_READLINE - rl = - ptr = readline( MNAME(MODULE_NAME) "$> " ); - if (rl == NULL) { - break; - } else { - if( *rl != '\0' ) { - add_history( rl ); - } - } -#else - printf("$> "); - if( (rl = fgetline(stdin, FALSE)) == NULL) break; - ptr = rl; -#endif -#endif - } - } - - while( *ptr == ' ' || *ptr == '\t' ) - ptr++; - if( !strlen(ptr) || *ptr == '!' || *ptr == ';' ) - continue; - - if (*ptr == '@') { - if (atfile != NULL) { - printf("%%ODS2-W-INDIRECT, nested indirect command files not supported\n"); - } else { - if ((atfile = openf(ptr + 1,"r")) == NULL) { - perror("%%ODS2-E-INDERR, Failed to open indirect command file"); - printf("\n"); - free(command_line); - command_line = NULL; - } - } - continue; - } - - sts = cmdsplit(ptr); - if( sts == -1 ) - break; - if ((sts & 1) == 0) { - if( atfile != NULL ) { - fclose(atfile); - atfile = NULL; - } - free(command_line); - command_line = NULL; - } - } /* while 1 */ - - free( command_line ); - - if( rl != NULL ) { - free( rl ); - } -#ifdef USE_READLINE - if( hfname != NULL ) { - write_history( hfname ); - clear_history(); - } - wordfree( &wex ); /* hfname points into wex and should not be free()d */ -#endif - if (atfile != NULL) - fclose(atfile); - - { - struct qual *qp; - - for( qp = mouquals; qp->name != NULL; qp++ ) - if( !strcmp( qp->name, DT_NAME ) ) { - free( qp-> arg ); - qp->arg = NULL; - break; - } - } - exit( EXIT_SUCCESS ); } diff --git a/extracters/ods2/ods2.h b/extracters/ods2/ods2.h index 7b7bcb2..c81652e 100644 --- a/extracters/ods2/ods2.h +++ b/extracters/ods2/ods2.h @@ -5,11 +5,40 @@ #ifndef _ODS2_H #define _ODS2_H -#define MOU_WRITE 1 -#define MOU_VIRTUAL 2 -#define MOU_LOG 4 +#ifndef TRUE +#define TRUE ( 0 == 0 ) +#endif +#ifndef FALSE +#define FALSE ( 0 != 0 ) +#endif -#define MOU_V_DEVTYPE 8 +#if DEBUG +#include "debug.h" +#endif + +/* Option bits common to I/O, access, visible outside the ODS-2 parser */ + +#define MOU_WRITE (1 << 0) +#define MOU_VIRTUAL (1 << 1) +#define MOU_LOG (1 << 2) +#define PHY_CREATE (1 << 3) +#define PHY_VHDDSK (1 << 4) + +#define MOU_V_DEVTYPE (8) #define MOU_DEVTYPE (0xffff << MOU_V_DEVTYPE) +#define MOU_S_DEVTYPE (16) + +/* Option bits that will not conflict with I/O */ + +#define OPT_GENERIC_1 (1 << (MOU_V_DEVTYPE + MOU_S_DEVTYPE + 0) ) +#define OPT_GENERIC_2 (1 << (MOU_V_DEVTYPE + MOU_S_DEVTYPE + 1) ) +#define OPT_GENERIC_3 (1 << (MOU_V_DEVTYPE + MOU_S_DEVTYPE + 2) ) +#define OPT_GENERIC_4 (1 << (MOU_V_DEVTYPE + MOU_S_DEVTYPE + 3) ) +#define OPT_GENERIC_5 (1 << (MOU_V_DEVTYPE + MOU_S_DEVTYPE + 4) ) +#define OPT_GENERIC_6 (1 << (MOU_V_DEVTYPE + MOU_S_DEVTYPE + 5) ) +#define OPT_GENERIC_7 (1 << (MOU_V_DEVTYPE + MOU_S_DEVTYPE + 6) ) +#define OPT_GENERIC_8 (1 << (MOU_V_DEVTYPE + MOU_S_DEVTYPE + 7) ) + +#define OPT_NOSORT (1 << 31) #endif /* #ifndef _ODS2_H */ diff --git a/extracters/ods2/ods2_alpha.exe b/extracters/ods2/ods2_alpha.exe old mode 100644 new mode 100755 diff --git a/extracters/ods2/ods2_solaris_intel.exe b/extracters/ods2/ods2_solaris_intel.exe old mode 100644 new mode 100755 diff --git a/extracters/ods2/ods2_solaris_sparc.exe b/extracters/ods2/ods2_solaris_sparc.exe old mode 100644 new mode 100755 diff --git a/extracters/ods2/ods2_tru64.exe b/extracters/ods2/ods2_tru64.exe old mode 100644 new mode 100755 diff --git a/extracters/ods2/ods2_vax_decc.exe b/extracters/ods2/ods2_vax_decc.exe old mode 100644 new mode 100755 diff --git a/extracters/ods2/ods2_vax_vaxc.exe b/extracters/ods2/ods2_vax_vaxc.exe old mode 100644 new mode 100755 diff --git a/extracters/ods2/ods2_win32.exe b/extracters/ods2/ods2_win32.exe old mode 100644 new mode 100755 diff --git a/extracters/ods2/phyio.h b/extracters/ods2/phyio.h index 8b11032..1586dde 100644 --- a/extracters/ods2/phyio.h +++ b/extracters/ods2/phyio.h @@ -1,4 +1,4 @@ -/* Phyio.h V2.1 Definition of Physical I/O routines */ +/* Phyio.h Definition of Physical I/O routines */ /* This is part of ODS2 written by Paul Nankervis, @@ -37,7 +37,7 @@ #ifndef _PHYIO_H #define _PHYIO_H -#include "device.h" +#include #define PHYIO_READONLY 1 @@ -47,14 +47,18 @@ typedef enum showtype { SHOW_DEVICES } showtype_t; +struct DEV; + void phyio_show( showtype_t type ); char *phyio_path( const char *filnam ); unsigned phyio_init( struct DEV *dev ); unsigned phyio_done( struct DEV *dev ); -unsigned phyio_read( struct DEV *dev, unsigned block, unsigned length, - char *buffer ); -unsigned phyio_write( struct DEV *dev, unsigned block, unsigned length, - const char *buffer ); + +typedef unsigned (*phy_iord_t)( struct DEV *dev, unsigned block, unsigned length, + char *buffer ); +typedef unsigned (*phy_iowr_t)( struct DEV *dev, unsigned block, unsigned length, + const char *buffer ); + void phyio_help(FILE *fp ); #endif /* #ifndef _PHYIO_H */ diff --git a/extracters/ods2/phynt.c b/extracters/ods2/phynt.c index 1f2543a..53b763d 100644 --- a/extracters/ods2/phynt.c +++ b/extracters/ods2/phynt.c @@ -1,4 +1,4 @@ -/* PHYNT.C V2.1 Physical I/O module for Windows NT */ +/* PHYNT.C Physical I/O module for Windows NT */ /* Win9X code now included with code to automatically determine if we are running under WinNT... @@ -21,6 +21,13 @@ use the NT drive letter, which will do physical I/O to the device. */ +#if !defined( DEBUG ) && defined( DEBUG_PHYNT ) +#define DEBUG DEBUG_PHYNT +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif #include #include @@ -139,6 +146,10 @@ static void getsysversion( void ); static unsigned phy_getsect( struct DEV *dev, unsigned sector ); static unsigned phy_putsect( struct DEV *dev, unsigned sector ); +static unsigned phyio_read( struct DEV *dev, unsigned block, unsigned length, + char *buffer ); +static unsigned phyio_write( struct DEV *dev, unsigned block, unsigned length, + const char *buffer ); #ifdef USE_ASPI /************************************************************* aspi_execute() */ @@ -408,10 +419,10 @@ static BOOL GetDiskGeometry( struct DEV *dev, unsigned *sectors, ); } if ( result ) { - *sectors = Geometry.Cylinders.QuadPart * - Geometry.TracksPerCylinder * - Geometry.SectorsPerTrack; - *bytespersector = Geometry.BytesPerSector; + *sectors = (unsigned int)(Geometry.Cylinders.QuadPart * /* In theory, this can overfolow */ + Geometry.TracksPerCylinder * + Geometry.SectorsPerTrack); + *bytespersector = (unsigned int) Geometry.BytesPerSector; } } else { @@ -445,7 +456,7 @@ static BOOL GetDiskGeometry( struct DEV *dev, unsigned *sectors, &TotalNumberOfBytes, &TotalNumberOfFreeBytes ) ) { - *sectors = TotalNumberOfBytes.QuadPart / BytesPerSector; + *sectors = (unsigned int)(TotalNumberOfBytes.QuadPart / BytesPerSector); } } } @@ -596,9 +607,11 @@ static unsigned phy_getsect( struct DEV *dev, unsigned sector ) { FILE_BEGIN /* dwMoveMethod */ ) == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR ) { TCHAR *msg = w32_errstr(NO_ERROR); - printf( "PHYIO_READ: SetFilePointer() failed at sector %lu: %s\n", + if( msg != NULL ) { + printf( "PHYIO_READ: SetFilePointer() failed at sector %lu: %s\n", sector, msg ); - LocalFree(msg); + LocalFree( msg ); + } sts = SS$_PARITY; } if ( sts & STS$M_SUCCESS ) { @@ -616,9 +629,11 @@ static unsigned phy_getsect( struct DEV *dev, unsigned sector ) { return SS$_ENDOFFILE; msg = w32_errstr(NO_ERROR); - printf( "PHYIO_READ: ReadFile() failed at sector %lu: %s\n", + if( msg != NULL ) { + printf( "PHYIO_READ: ReadFile() failed at sector %lu: %s\n", sector, msg ); - LocalFree(msg); + LocalFree( msg ); + } sts = SS$_PARITY; } } @@ -650,9 +665,11 @@ static unsigned phy_getsect( struct DEV *dev, unsigned sector ) { ) && !( reg.reg_Flags & 0x0001 ); if ( !result ) { TCHAR *msg = w32_errstr(NO_ERROR); - printf( "PHYIO_READ: Read sector %d failed: %s\n", + if( msg != NULL ) { + printf( "PHYIO_READ: Read sector %d failed: %s\n", sector, msg ); - LocalFree(msg); + LocalFree( msg ); + } sts = SS$_PARITY; } } @@ -691,9 +708,11 @@ static unsigned phy_putsect( struct DEV *dev, unsigned sector ) { FILE_BEGIN /* dwMoveMethod */ ) == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR ) { TCHAR *msg = w32_errstr(NO_ERROR); - printf( "PHYIO_WRITE: SetFilePointer() failed at sector %lu: %s\n", + if( msg != NULL ) { + printf( "PHYIO_WRITE: SetFilePointer() failed at sector %lu: %s\n", sector, msg ); - LocalFree(msg); + LocalFree( msg ); + } sts = SS$_PARITY; } if ( sts & STS$M_SUCCESS ) { @@ -705,9 +724,11 @@ static unsigned phy_putsect( struct DEV *dev, unsigned sector ) { NULL /* lpOverlapped */ ) || BytesWritten != dev->bytespersector ) { TCHAR *msg = w32_errstr(NO_ERROR); - printf( "PHYIO_WRITE: WriteFile() failed at sector %lu: %s\n", + if( msg != NULL ) { + printf( "PHYIO_WRITE: WriteFile() failed at sector %lu: %s\n", sector, msg ); - LocalFree(msg); + LocalFree( msg ); + } sts = SS$_PARITY; } } @@ -738,9 +759,11 @@ static unsigned phy_putsect( struct DEV *dev, unsigned sector ) { ) && !( reg.reg_Flags & 0x0001 ); if ( !result ) { TCHAR *msg = w32_errstr(NO_ERROR); - printf( "PHYIO_WRITE: Write sector %d failed: %s\n", + if( msg != NULL ) { + printf( "PHYIO_WRITE: Write sector %d failed: %s\n", sector, msg ); - LocalFree(msg); + LocalFree( msg ); + } sts = SS$_PARITY; } } @@ -767,7 +790,7 @@ void phyio_show( showtype_t type ) { TCHAR l; for( l = 'A'; l <= 'Z'; l++ ) { - snprintf( devname, sizeof( devname ), "%c:", l ); + (void) snprintf( devname, sizeof( devname ), "%c:", l ); dname = namep = driveFromLetter( devname ); if( namep != NULL ) { const char *type = NULL; @@ -858,17 +881,76 @@ unsigned phyio_init( struct DEV *dev ) { init_count++; - dev->access &= ~MOU_VIRTUAL; + dev->API.Win32.handle = INVALID_HANDLE_VALUE; + dev->devread = phyio_read; + dev->devwrite = phyio_write; dev->drive = -1; - dev->sectors = 0; dev->bytespersector = 0; dev->blockspersector = 0; dev->last_sector = 0; dev->IoBuffer = NULL; - virtual = virt_lookup( dev->devnam ); - if ( virtual == NULL ) { + if( dev->access & MOU_VIRTUAL ) { + DWORD FileSizeLow, FileSizeHigh; + + virtual = virt_lookup( dev->devnam ); + if ( virtual == NULL ) { + return SS$_NOSUCHDEV; + } + dev->API.Win32.handle = + CreateFile( virtual, /* lpFileName */ + GENERIC_READ | /* dwDesiredAccess */ + ((dev->access & MOU_WRITE)? GENERIC_WRITE : 0 ), + 0, /* dwShareMode */ + NULL, /* lpSecurityAttributes */ + ((dev->access & PHY_CREATE)? CREATE_NEW: OPEN_EXISTING), + /* dwCreationDisposition */ + /* dwFlagsAndAttributes */ + FILE_FLAG_RANDOM_ACCESS | + ( dev->access & MOU_WRITE ? + FILE_FLAG_WRITE_THROUGH : 0 ), + NULL /* hTemplateFile */ + ); + if ( dev->API.Win32.handle == INVALID_HANDLE_VALUE && + (dev->access & (MOU_WRITE|PHY_CREATE)) == MOU_WRITE ) { + dev->access &= ~MOU_WRITE; + dev->API.Win32.handle = + CreateFile( virtual, /* lpFileName */ + GENERIC_READ, /* dwDesiredAccess */ + 0, /* dwShareMode */ + NULL, /* lpSecurityAttributes */ + OPEN_EXISTING, /* dwCreationDisposition */ + /* dwFlagsAndAttributes */ + FILE_FLAG_RANDOM_ACCESS, + NULL /* hTemplateFile */ + ); + } + if ( dev->API.Win32.handle == INVALID_HANDLE_VALUE ) { + TCHAR *msg = w32_errstr(NO_ERROR); + if( msg != NULL ) { + printf( "PHYIO_INIT: CreateFile() failed for %s: %s\n", + virtual, msg ); + LocalFree( msg ); + } + return SS$_NOSUCHFILE; + } + SetLastError( 0 ); + FileSizeLow = GetFileSize( dev->API.Win32.handle, /* hFile */ + &FileSizeHigh /* lpFileSizeHigh */ + ); + if ( GetLastError() == NO_ERROR ) { +#if 0 + dev->sectors = ( FileSizeHigh << 23 ) | + ( ( ( FileSizeLow + 511 ) >> 9 ) & 0x007FFFFF ) ; +#endif + dev->eofptr = (off_t)(( ((__int64)FileSizeHigh) << 32 ) | FileSizeLow); + dev->bytespersector = 512; + dev->blockspersector = 1; + } + } else { /* Physical */ + unsigned sectors; + if ( strlen( dev->devnam ) != 2 || !isalpha( dev->devnam[0] ) || dev->devnam[1] != ':' ) { return SS$_IVDEVNAM; @@ -895,6 +977,7 @@ unsigned phyio_init( struct DEV *dev ) { if ( is_NT ) { char ntname[7] = "\\\\.\\"; /* 7 = sizeof \\.\a:\0 */ + memcpy( ntname+4, dev->devnam, 3 ); /* Copy a:\0 */ ntname[4] = toupper( ntname[4] ); dev->API.Win32.handle = @@ -942,77 +1025,41 @@ unsigned phyio_init( struct DEV *dev ) { } if ( dev->API.Win32.handle == INVALID_HANDLE_VALUE ) { TCHAR *msg = w32_errstr(NO_ERROR); - printf( "PHYIO_INIT: Open( \"%s\" ) failed: %s\n", + if( msg != NULL ) { + printf( "PHYIO_INIT: Open( \"%s\" ) failed: %s\n", dev->devnam, msg ); - LocalFree(msg); + LocalFree( msg ); + } return SS$_NOSUCHDEV; } if ( !LockVolume( dev ) ) { TCHAR *msg = w32_errstr(NO_ERROR); - printf( "PHYIO_INIT: LockVolume( \"%s\" ) failed: %s\n", + if( msg != NULL ) { + printf( "PHYIO_INIT: LockVolume( \"%s\" ) failed: %s\n", dev->devnam, msg ); - LocalFree(msg); + LocalFree( msg ); + } phyio_done( dev ); return SS$_DEVNOTALLOC; } } - if ( !GetDiskGeometry( dev, &dev->sectors, &dev->bytespersector ) ) { + + if ( !GetDiskGeometry( dev, §ors, &dev->bytespersector ) ) { TCHAR *msg = w32_errstr(NO_ERROR); - printf( "PHYIO_INIT: GetDiskGeometry( \"%s\" ) failed: %s\n", + if( msg != NULL ) { + printf( "PHYIO_INIT: GetDiskGeometry( \"%s\" ) failed: %s\n", dev->devnam, msg ); - LocalFree(msg); + LocalFree( msg ); + } phyio_done( dev ); return 80; } dev->blockspersector = dev->bytespersector / 512; - } else { /* Virtual device */ - DWORD FileSizeLow, FileSizeHigh; - dev->access |= MOU_VIRTUAL; - dev->API.Win32.handle = - CreateFile( virtual, /* lpFileName */ - GENERIC_READ | /* dwDesiredAccess */ - ( dev->access & MOU_WRITE ? GENERIC_WRITE : 0 ), - 0, /* dwShareMode */ - NULL, /* lpSecurityAttributes */ - OPEN_EXISTING, /* dwCreationDisposition */ - /* dwFlagsAndAttributes */ - FILE_FLAG_RANDOM_ACCESS | - ( dev->access & MOU_WRITE ? - FILE_FLAG_WRITE_THROUGH : 0 ), - NULL /* hTemplateFile */ - ); - if ( dev->API.Win32.handle == INVALID_HANDLE_VALUE && - dev->access & MOU_WRITE ) { - dev->access &= ~MOU_WRITE; - dev->API.Win32.handle = - CreateFile( virtual, /* lpFileName */ - GENERIC_READ, /* dwDesiredAccess */ - 0, /* dwShareMode */ - NULL, /* lpSecurityAttributes */ - OPEN_EXISTING, /* dwCreationDisposition */ - /* dwFlagsAndAttributes */ - FILE_FLAG_RANDOM_ACCESS, - NULL /* hTemplateFile */ - ); - } - if ( dev->API.Win32.handle == INVALID_HANDLE_VALUE ) { - TCHAR *msg = w32_errstr(NO_ERROR); - printf( "PHYIO_INIT: CreateFile() failed for %s: %s\n", - virtual, msg ); - LocalFree(msg); - return SS$_NOSUCHFILE; - } - SetLastError( 0 ); - FileSizeLow = GetFileSize( dev->API.Win32.handle, /* hFile */ - &FileSizeHigh /* lpFileSizeHigh */ - ); - if ( GetLastError() == NO_ERROR ) { - dev->sectors = ( FileSizeHigh << 23 ) | - ( ( ( FileSizeLow + 511 ) >> 9 ) & 0x007FFFFF ) ; - dev->bytespersector = 512; - dev->blockspersector = 1; - } + dev->eofptr = ((off_t)sectors) * dev->bytespersector; } + + /* Common exit for virtual and physical */ + dev->IoBuffer = VirtualAlloc( NULL, /* lpAddress */ dev->bytespersector, /* dwSize */ MEM_COMMIT, /* flAllocationType */ @@ -1022,7 +1069,7 @@ unsigned phyio_init( struct DEV *dev ) { phyio_done( dev ); return SS$_INSFMEM; } -#ifdef DEBUG +#if DEBUG printf( "--->phyio_init(): sectors = %u, bytespersector = %u\n", dev->sectors, dev->bytespersector ); #endif @@ -1040,11 +1087,9 @@ unsigned phyio_done( struct DEV *dev ) { ); dev->API.Win32.handle = INVALID_HANDLE_VALUE; } - if( dev->access & MOU_VIRTUAL ) - virt_device( dev->devnam, NULL ); if ( dev->IoBuffer != NULL ) { VirtualFree( dev->IoBuffer, /* lpAddress */ - dev->bytespersector, /* dwSize */ + 0, /* dwSize */ MEM_RELEASE /* dwFreeType */ ); dev->IoBuffer = NULL; @@ -1057,13 +1102,13 @@ unsigned phyio_done( struct DEV *dev ) { /* Handle a read request ... need to read the approriate sectors to complete the request... */ -unsigned phyio_read( struct DEV *dev, unsigned block, unsigned length, - char *buffer ) { +static unsigned phyio_read( struct DEV *dev, unsigned block, unsigned length, + char *buffer ) { register unsigned sts, transfer, sectno, offset; char *sectbuff; -#ifdef DEBUG +#if DEBUG printf( "Phyio read block: %d into %x (%d bytes)\n", block, buffer, length ); #endif @@ -1105,13 +1150,13 @@ unsigned phyio_read( struct DEV *dev, unsigned block, unsigned length, /* Handle a write request ... need to read the approriate sectors to complete the request... */ -unsigned phyio_write( struct DEV *dev, unsigned block, unsigned length, - const char *buffer) { +static unsigned phyio_write( struct DEV *dev, unsigned block, unsigned length, + const char *buffer) { register unsigned sts, transfer, sectno, offset; char *sectbuff; -#ifdef DEBUG +#if DEBUG printf( "Phyio write block: %d from %x (%d bytes)\n", block, buffer, length ); #endif diff --git a/extracters/ods2/phyos2.c b/extracters/ods2/phyos2.c index 5d068f2..b937067 100644 --- a/extracters/ods2/phyos2.c +++ b/extracters/ods2/phyos2.c @@ -15,7 +15,10 @@ #include "phyio.h" #include "ssdef.h" - +static unsigned phyio_read( struct DEV *dev, unsigned block, unsigned length, + char *buffer ); +static unsigned phyio_write( struct DEV *dev, unsigned block, unsigned length, + const char *buffer ); unsigned init_count = 0; unsigned read_count = 0; @@ -70,8 +73,17 @@ void phyio_help(FILE *fp ) { return; } +NOTE: This will not compile. The API for phyio_init has changed to +taking a DEV struct. I'm leaving it this way becauses I don't have the +ability to test under OS2. This was left undone when the API was changed... +before my time. + + unsigned phyio_init(int devlen,char *devnam,unsigned *hand,struct phyio_info *info) { + dev->devread = phyio_read; + dev->devwrite = phyio_write; + if (hand_count < HANDLE_MAX - 1) { ULONG usAction; ULONG open_mode; @@ -163,10 +175,10 @@ unsigned phy_getsect(HFILE hfile,unsigned sector,char *buffer) -unsigned phyio_read(unsigned handno,unsigned block,unsigned length,char *buffer) +static unsigned phyio_read(unsigned handno,unsigned block,unsigned length,char *buffer) { register unsigned sts = 1; -#ifdef DEBUG +#if DEBUG printf("PHYIO_READ block %d length %d\n",block,length); #endif if (handno >= hand_count) { @@ -212,7 +224,7 @@ unsigned phyio_read(unsigned handno,unsigned block,unsigned length,char *buffer) } -unsigned phyio_write(unsigned handle,unsigned block,unsigned length,char *buffer) +static unsigned phyio_write(unsigned handle,unsigned block,unsigned length,char *buffer) { write_count++; return SS$_WRITLCK; diff --git a/extracters/ods2/phyunix.c b/extracters/ods2/phyunix.c index 51a911f..bd8265f 100644 --- a/extracters/ods2/phyunix.c +++ b/extracters/ods2/phyunix.c @@ -1,4 +1,4 @@ -/* PHYUNIX.c V2.1 Physical I/O module for Unix */ +/* PHYUNIX.c Physical I/O module for Unix */ /* This is part of ODS2 written by Paul Nankervis, @@ -26,6 +26,14 @@ /* */ /******************************************************************************/ +#if !defined( DEBUG ) && defined( DEBUG_PHYUNIX ) +#define DEBUG DEBUG_PHYUNIX +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + #include #include #include @@ -46,13 +54,6 @@ #include "phyvirt.h" #include "compat.h" -#ifndef TRUE -#define TRUE ( 0 == 0 ) -#endif -#ifndef FALSE -#define FALSE ( 0 != 0 ) -#endif - #if defined(__digital__) && defined(__unix__) #define DEV_PREFIX "/devices/rdisk/" #else @@ -73,6 +74,11 @@ struct devdat { unsigned high; }; +static unsigned phyio_read( struct DEV *dev, unsigned block, unsigned length, + char *buffer ); +static unsigned phyio_write( struct DEV *dev, unsigned block, unsigned length, + const char *buffer ); + /*************************************************************** showdevs() */ static int devcmp( const void *d1, const void *d2 ) { @@ -253,85 +259,131 @@ char *phyio_path( const char *filnam ) { /*************************************************************** phyio_init() */ unsigned phyio_init( struct DEV *dev ) { - + int sts = SS$_NORMAL; size_t n; - int fd; + int fd, saverr = 0; char *device; const char *virtual; struct stat statbuf; init_count++; - dev->access &= ~MOU_VIRTUAL; - dev->handle = 0; - dev->sectors = 0; + dev->handle = -1; + dev->devread = phyio_read; + dev->devwrite = phyio_write; - virtual = virt_lookup( dev->devnam ); - if ( virtual == NULL ) { - n = sizeof( DEV_PREFIX ) + strlen( dev->devnam ); - device = (char *) malloc( n ); - if ( device != NULL ) { - memcpy( device, DEV_PREFIX, sizeof( DEV_PREFIX ) -1 ); - memcpy( device+sizeof( DEV_PREFIX ) -1, dev->devnam, n +1 - sizeof( DEV_PREFIX ) ); - device[n - 2] = '\0'; /* Remove : from device name */ + if( dev->access & MOU_VIRTUAL ) { + virtual = virt_lookup( dev->devnam ); + if ( virtual == NULL ) { + return SS$_NOSUCHDEV; } - } else { n = strlen( virtual ); device = (char *) malloc( n + 1 ); - if ( device != NULL ) { - memcpy( device, virtual, n + 1 ); - dev->access |= MOU_VIRTUAL; - } + if( device == NULL ) + return SS$_INSFMEM; + + memcpy( device, virtual, n + 1 ); + } else { + n = sizeof( DEV_PREFIX ) + strlen( dev->devnam ); + device = (char *) malloc( n ); + if ( device == NULL ) + return SS$_INSFMEM; + + memcpy( device, DEV_PREFIX, sizeof( DEV_PREFIX ) -1 ); + memcpy( device+sizeof( DEV_PREFIX ) -1, dev->devnam, n +1 - sizeof( DEV_PREFIX ) ); + device[n - 2] = '\0'; /* Remove : from device name */ } - if ( device == NULL ) { - return SS$_INSFMEM; - } - stat( device, &statbuf ); - dev->sectors = ( statbuf.st_size + (off_t) 511 ) / (off_t) 512; - fd = open( device, ( dev->access & MOU_WRITE ) ? O_RDWR : O_RDONLY ); -#ifdef DEBUG - printf( "%d = open( \"%s\", %s )\n", fd, device, - ( dev->access & MOU_WRITE ) ? "O_RDWR" : "O_RDONLY" ); + + fd = open( device, + ((dev->access & MOU_WRITE )? O_RDWR : O_RDONLY) | + ((dev->access & (MOU_VIRTUAL|PHY_CREATE)) == (MOU_VIRTUAL|PHY_CREATE)? + O_CREAT | O_EXCL : 0), + 0644 ); + if( fd < 0 ) + saverr = errno; +#if DEBUG + printf( "%d = open( \"%s\", %s%s )\n", fd, device, + (dev->access & MOU_WRITE )? "O_RDWR" : "O_RDONLY", + (dev->access & PHY_CREATE)? "|O_CREAT|O_EXCL, 0666" : "" ); #endif - if ( fd < 0 && dev->access & MOU_WRITE ) { + if ( fd < 0 && (dev->access & (MOU_WRITE|PHY_CREATE)) == MOU_WRITE ) { dev->access &= ~MOU_WRITE; fd = open( device, O_RDONLY ); -#ifdef DEBUG +#if DEBUG printf( "%d = open( \"%s\", O_RDONLY )\n", fd, device ); #endif } - free( device ); + dev->handle = fd; + + if( fd >= 0 ) { + if( fstat( fd, &statbuf ) == 0 ) { + if( dev->access & MOU_VIRTUAL ) { + if( !S_ISREG(statbuf.st_mode) ) { + printf( "%%ODS2-E-NOTFILE, %s is not a regular file\n", device ); + close( fd ); + dev->handle = -1; + sts = SS$_IVDEVNAM; + } else + dev->eofptr = statbuf.st_size; + } else { + if( !S_ISBLK(statbuf.st_mode ) ) { + printf( "%%ODS2-E-NOTFILE, %s is not a block device\n", device ); + close( fd ); + dev->handle = -1; + sts = SS$_IVDEVNAM; + } + } + } else { + if( !saverr ) + saverr = errno; + close( fd ); + dev->handle = -1; + fd = -1; + } + } + if ( fd < 0 ) { -#ifdef DEBUG +#if DEBUG + errno = saverr; perror( "open" ); #endif - return ( ( dev->access & MOU_VIRTUAL ) ? - SS$_NOSUCHFILE : SS$_NOSUCHDEV ); + + errno = saverr; + switch( errno ) { + case EEXIST: + printf( "%%ODS2-E-EXISTS, File %s already exists, not superseded\n", device ); + sts = SS$_DUPFILENAME; + break; + default: + printf( "%%ODS2-E-OPENERR, Open %s: %s\n", device, strerror( errno ) ); + sts = (dev->access & MOU_VIRTUAL) ? SS$_NOSUCHFILE : SS$_NOSUCHDEV; + } } - dev->handle = fd; - return SS$_NORMAL; + free( device ); + + return sts; } /*************************************************************** phyio_done() */ unsigned phyio_done( struct DEV *dev ) { - close( dev->handle ); - dev->handle = 0; - if( dev->access & MOU_VIRTUAL ) - virt_device( dev->devnam, NULL ); + if( dev->handle != -1 ) + close( dev->handle ); + dev->handle = -1; + return SS$_NORMAL; } /*************************************************************** phyio_read() */ -unsigned phyio_read( struct DEV *dev, unsigned block, unsigned length, +static unsigned phyio_read( struct DEV *dev, unsigned block, unsigned length, char *buffer ) { ssize_t res; off_t pos; -#ifdef DEBUG +#if DEBUG printf("Phyio read block: %d into %p (%d bytes)\n", block, buffer, length ); #endif @@ -340,15 +392,19 @@ unsigned phyio_read( struct DEV *dev, unsigned block, unsigned length, pos = (off_t) block * (off_t) 512; if ( ( pos = lseek( dev->handle, pos, SEEK_SET ) ) < 0 ) { perror( "lseek " ); - printf("lseek failed %" PRIuMAX "u\n",(uintmax_t)pos); + printf("lseek failed %" PRIuMAX "\n",(uintmax_t)pos); return SS$_PARITY; } if ( ( res = read( dev->handle, buffer, length ) ) != length ) { if( res == 0 ) { return SS$_ENDOFFILE; } - perror( "read " ); - printf("read failed %" PRIuMAX "u\n", (uintmax_t)res); + if( res == (off_t)-1 ) { + perror( "%%ODS2-F-READERR, read failed" ); + } else { + printf( "%%ODS2-F-READERR, read failed with bc = %" PRIuMAX " for length %u, lbn %u\n", + (uintmax_t)res, length, block ); + } return SS$_PARITY; } return SS$_NORMAL; @@ -356,13 +412,13 @@ unsigned phyio_read( struct DEV *dev, unsigned block, unsigned length, /************************************************************** phyio_write() */ -unsigned phyio_write( struct DEV *dev, unsigned block, unsigned length, +static unsigned phyio_write( struct DEV *dev, unsigned block, unsigned length, const char *buffer ) { off_t pos; ssize_t res; -#ifdef DEBUG +#if DEBUG printf("Phyio write block: %d from %p (%d bytes)\n", block, buffer, length ); #endif @@ -371,12 +427,16 @@ unsigned phyio_write( struct DEV *dev, unsigned block, unsigned length, pos = (off_t) block * (off_t) 512; if ( ( pos = lseek( dev->handle, pos, SEEK_SET ) ) < 0 ) { perror( "lseek " ); - printf( "lseek failed %" PRIuMAX "u\n", (uintmax_t)pos ); + printf( "lseek failed %" PRIuMAX "\n", (uintmax_t)pos ); return SS$_PARITY; } if ( ( res = write( dev->handle, buffer, length ) ) != length ) { - perror( "write " ); - printf( "write failed %" PRIuMAX "u\n", (uintmax_t)res ); + if( res == (off_t)-1 ) { + perror( "%%ODS2-F-WRITEERR, write failed" ); + } else { + printf( "%%ODS2-F-WRITEERR, write failed with bc = %" PRIuMAX " for length %u, lbn %u\n", + (uintmax_t)res, length, block ); + } return SS$_PARITY; } return SS$_NORMAL; diff --git a/extracters/ods2/phyvhd.c b/extracters/ods2/phyvhd.c new file mode 100644 index 0000000..3cbd59a --- /dev/null +++ b/extracters/ods2/phyvhd.c @@ -0,0 +1,447 @@ +/* Phyvhd.c Physical I/O for VHD format disks */ + +/* Timothe Litt March 2016 + * Copyright (C) 2016 Timothe litt + * litt at acm dot org + * + * Free for use with the ODS2 package. All other rights reserved. + */ + +/* + * This is distributed as part of ODS2, originally 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. + */ + +#if !defined( DEBUG ) && defined( DEBUG_PHYVHD ) +#define DEBUG DEBUG_PHYVHD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#ifndef phyvhd_BUFSIZE +#define phyvhd_BUFSIZE (10 * 512) +#endif + +#ifndef phyvhd_MINSIZE +#define phyvhd_MINSIZE (20 * 1000 * 1000) +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#define close _close +#else +#include +#define _aligned_free free +#endif + +#include "device.h" +#include "ods2.h" +#include "phyvirt.h" +#include "ssdef.h" +#include "stsdef.h" + +#include "libvhd.h" + +typedef vhd_context_t *vhd_contextp; +typedef vhd_context_t vhd_context; + +#ifdef USE_LIBVHD +#include +#include +#include +#include +#include + +typedef int (*vhd_open_p)( vhd_contextp ctx, const char *filename, int flags ); +typedef void (*vhd_close_p)( vhd_contextp ctx ); +typedef int (*vhd_create_p)(const char *file, uint64_t size, + int type, uint32_t flags ); +typedef int vhd_header_decode_parent(vhd_contextp ctx, + vhd_header_t *header, char **buf); +typedef int (*vhd_snapshot_p)( const char *filename, const char *parent, uint64_t size, + const char *parentfile, uint32_t flags ); +typedef int (*vhd_io_read_p)(vhd_contextp ctx, void *buf, uint64_t pbn, uint32_t count ); +typedef int (*vhd_io_write_p)(vhd_contextp ctx, void *buf, uint64_t pbn, uint32_t count); +typedef void (*libvhd_set_log_level_p)(int); +typedef uint32_t (*vhd_chs_p)(uint64_t size); +/* *** */ + +static void *libvhd = NULL; + +#define vhddeclare(s) s ## _p s ## _fcn = NULL + vhddeclare(vhd_open); + vhddeclare(vhd_close); + vhddeclare(vhd_create); + vhddeclare(libvhd_set_log_level); + vhddeclare(vhd_header_decode_parent); + vhddeclare(vhd_snapshot); +#if 0 + vhddeclare(vhd_chs); +#endif + vhddeclare(vhd_io_read); + vhddeclare(vhd_io_write); + +#define vhdresolve(s) do { \ + if( libvhd != NULL && \ + (s ## _fcn = (s ## _p)dlsym( libvhd, #s )) == NULL ) { \ + printf( "VHD format image files can not be used\n" ); \ + printf( "%%ODS2-F-NOSYM, Missing symbol %s in " LIBVHDSO(USE_LIBVHD) "\n", #s ); \ + dlclose( libvhd ); \ + libvhd = NULL; \ + } \ + } while( 0 ) +#else + +#define vhd_open_fcn vhd_open +#define vhd_close_fcn vhd_close +#define vhd_create_fcn vhd_create +#define libvhd_set_log_level_fcn libvhd_set_log_level +#define vhd_header_decode_parent_fcn vhd_header_decode_parent +#define vhd_snapshot_fcn vhd_snapshot +#if 0 +#define vhd_chs_fcn vhd_chs +#endif +#define vhd_io_read_fcn vhd_io_read +#define vhd_io_write_fcn vhd_io_write + +#endif + +static unsigned phyvhd_read( struct DEV *dev, unsigned lbn, unsigned length, + char *buffer ); +static unsigned phyvhd_write( struct DEV *dev, unsigned lbn, unsigned length, + const char *buffer ); + +/*********************************************************** phyvhd_available() */ +int phyvhd_available( int query ) { +#ifdef USE_LIBVHD +#define LIBVHDSOx(x) #x +#define LIBVHDSO(n) LIBVHDSOx(n) + + if( libvhd == NULL ) { + if( (libvhd = dlopen( LIBVHDSO(USE_LIBVHD), RTLD_LAZY )) == NULL ) { + const char *err; + + if( query ) + printf( "VHD format image file support is configud, but not available\n" ); + + printf( "%%ODS2-F-NOLIB, " LIBVHDSO(USE_LIBVHD) " not could not be loaded (see vhdtools or XEN libraries)" ); + if( (err = dlerror()) != NULL ) + printf( ": %s\n", err ); + else + putchar( '\n' ); + return FALSE; + } + + vhdresolve( vhd_open ); + vhdresolve( vhd_close ); + vhdresolve( vhd_create ); + vhdresolve( vhd_snapshot ); + vhdresolve( libvhd_set_log_level ); + vhdresolve( vhd_header_decode_parent ); + + vhdresolve( vhd_io_read ); + vhdresolve( vhd_io_write ); + + if( libvhd == NULL ) { + printf( "%%ODS2-F-BADLIB, the " LIBVHDSO(USE_LIBVHD) " library is not valid\n" ); + return FALSE; + } + } +#endif + + if( query ) + printf( "VHD format image files can be used\n" ); + + return TRUE; +} + +#ifdef _WIN32 +#pragma warning(push) +#pragma warning(disable: 4996) /* MS complains about _open, which is open() */ +#endif +/*********************************************************** phyvhd_snapshot() */ +unsigned phyvhd_snapshot( const char *filename, const char *parent ) { + int fd, err; + char *p; + + if( !( (p = strrchr( filename, '.')) != NULL && ( !strcmp( p, ".vhd" ) || + !strcmp( p, ".VHD" ) ) ) ) { + printf( "%%ODS2-E-NOTVHD, %s: Snapshots can only be taken of VHD format image files\n", filename ); + return SS$_DUPFILENAME; + } + +#ifdef USE_LIBVHD + if( libvhd == NULL && !phyvhd_available( FALSE ) ) + return SS$_NOTINSTALL; +#endif + +#ifdef _WIN32 + fd = _open( filename, _O_RDWR | _O_CREAT | _O_EXCL, 0644 ); +#else + fd = open( filename, O_RDWR | O_CREAT | O_EXCL, 0644 ); +#endif + if( fd == -1 ) { + printf( "%%ODS2-E-EXISTS, %s already exists\n", filename ); + return SS$_DUPFILENAME; + } + err = vhd_snapshot_fcn( filename, 0, parent, 0 ); + close( fd ); + + if( err == 0 ) + return SS$_NORMAL; + + printf( "%%ODS2-E-NOSNAP, snapshot failed\n" ); + errno = -err; + perror( " - " ); + return SS$_DEVOFFLINE; +} + +/*********************************************************** phyvhd_init() */ +unsigned phyvhd_init( struct DEV *dev ) { + int err; + char *fname; + unsigned status; + long align; +#ifdef _WIN32 + SYSTEM_INFO inf; + + GetSystemInfo( &inf ); + align = inf.dwPageSize; +#elif defined( _SC_PAGESIZE) + align = sysconf( _SC_PAGESIZE ); +#else + align = getpagesize(); +#endif + +#ifdef USE_LIBVHD + if( libvhd == NULL && !phyvhd_available( FALSE ) ) + return SS$_NOTINSTALL; +#endif + libvhd_set_log_level_fcn( -1 ); + + fname = virt_lookup( dev->devnam ); + if( fname == NULL ) + return SS$_NOSUCHDEV; + + dev->devread = phyvhd_read; + dev->devwrite = phyvhd_write; + +#ifdef _WIN32 + if( (dev->IoBuffer = _aligned_malloc( phyvhd_BUFSIZE, align )) == NULL ) { + printf( "Unable to allocate memory for bufer\n" ); + return SS$_INSFMEM; + } + + if( (dev->context = _aligned_malloc( sizeof( vhd_context ), align )) == NULL ) { + printf( "Unable to allocate memory for context\n" ); + _aligned_free(dev->IoBuffer); + dev->IoBuffer = NULL; + return SS$_INSFMEM; + } +#else + if( posix_memalign( (void**)&dev->IoBuffer, align, phyvhd_BUFSIZE ) != 0 ) { + printf( "Unable to allocate memory for bufer\n" ); + return SS$_INSFMEM; + } + if( posix_memalign( (void**)&dev->context, align, + sizeof( vhd_context ) ) != 0 ) { + printf( "Unable to allocate memory for context\n" ); + free(dev->IoBuffer); + dev->IoBuffer = NULL; + return SS$_INSFMEM; + } +#endif + + status = SS$_NORMAL; + + do { + if( dev->access & PHY_CREATE ) { + disktypep_t dp; + uint64_t size; + int fd; + +#ifdef _WIN32 + fd = _open( fname, _O_RDWR | _O_CREAT | _O_EXCL, 0644 ); +#else + fd = open( fname, O_RDWR | O_CREAT | O_EXCL, 0644 ); +#endif + if( fd == -1 ) { + close( fd ); + status = SS$_DUPFILENAME; + break; + } + if( dev->access & MOU_LOG ) + printf( "%%ODS2-I-VHDINIT, Formatting %s\n", fname ); + + dp = dev->disktype; + size = ((uint64_t)dp->sectors) * dp->tracks * dp->cylinders * dp->sectorsize; + size = (size + 511) / 512; + size *= 512; + + err = vhd_create_fcn( fname, size, (size >= phyvhd_MINSIZE? + HD_TYPE_DYNAMIC: HD_TYPE_FIXED), + 0 ); + close( fd ); + if( err != 0 ) { + errno = -err; + perror( "vhd create" ); + status = SS$_NOSUCHDEV; + break; + } + if( dev->access & MOU_LOG ) + printf( "%%ODS2-I-VHDDONE, VHD volume formatting completed\n" ); + } + err = vhd_open_fcn( dev->context, fname, + (dev->access & MOU_WRITE)? + VHD_OPEN_RDWR | VHD_OPEN_STRICT : + VHD_OPEN_RDONLY | VHD_OPEN_STRICT ); + if( err != 0 && (dev->access & (MOU_WRITE|PHY_CREATE)) == MOU_WRITE ) { + dev->access &= ~MOU_WRITE; + err = vhd_open_fcn( dev->context, fname, VHD_OPEN_RDONLY | VHD_OPEN_STRICT ); + } + if( err != 0 ) { + errno = -err; + perror( "vhd open" ); + status = SS$_NOSUCHDEV; + break; + } + if( (dev->access & MOU_WRITE) && ((vhd_contextp)dev->context)->footer.saved ) { + printf( "%%ODS2-W-VHDSAVED, %s contains a suspended system and should not be modified\n", fname ); + } + dev->eofptr = (off_t)(((vhd_contextp)dev->context)->footer.curr_size); + } while( 0 ); + + if( !(status & STS$M_SUCCESS) ) { + _aligned_free( dev->context ); + _aligned_free( dev->IoBuffer ); + dev->context = + dev->IoBuffer = NULL; + } + return status; +} +#ifdef _WIN32 +#pragma warning(pop) +#endif +/*********************************************************** phyvhd_show() */ +void phyvhd_show( struct DEV *dev, size_t column ) { + vhd_contextp ctx; + const char *type, *p; + size_t i, n; + + if( (ctx = dev->context) == NULL ) + return; + i = ctx->footer.type; + if( i > HD_TYPE_MAX ) + type = "Unknown"; + else + type = HD_TYPE_STR[i]; + + n = strlen( type ); + if( (p = strchr( type, ' ' )) == NULL ) + p = type + n; + n = (size_t) (p -type); + + printf( " VHD %*.*s disk", (int)n, (int)n, type ); + if( i == HD_TYPE_DIFF ) { + char *name; +#define PLABEL "parent: " + if( vhd_header_decode_parent_fcn( ctx, &ctx->header, &name ) == 0 ) { + printf( "\n%*sparent: %s", (int)(column < sizeof( PLABEL ) -1? + (sizeof( PLABEL )-1)-column : + column - (sizeof( PLABEL ) -1)), + "", name ); + free( name ); + } + } + + return; +} +/*********************************************************** phyvhd_read() */ +static unsigned phyvhd_read( struct DEV *dev, unsigned lbn, unsigned length, + char *buffer ) { + unsigned status; + int err; + + status = SS$_NORMAL; + while( length > 0 ) { + unsigned rdsize; + + rdsize = (length + 511) / 512; + rdsize = ( (rdsize > phyvhd_BUFSIZE/512)? + phyvhd_BUFSIZE/512 : rdsize ); + + if( (err = vhd_io_read_fcn(dev->context, dev->IoBuffer, lbn, rdsize)) != 0 ) { + if( err == -ERANGE ) + return SS$_ILLBLKNUM; + return SS$_PARITY; + } + lbn += rdsize; + rdsize *= 512; + if( rdsize > length ) + rdsize = length; + memcpy( buffer, dev->IoBuffer, rdsize ); + buffer += rdsize; + length -= rdsize; + } + return status; +} + + +/*********************************************************** phyvhd_write() */ + +static unsigned phyvhd_write( struct DEV *dev, unsigned lbn, unsigned length, + const char *buffer ) { + unsigned status; + int err; + + status = SS$_NORMAL; + while( length > 0 ) { + unsigned wrsize, iosize; + + iosize = (length + 511) / 512; + iosize = ( (iosize > phyvhd_BUFSIZE/512)? + phyvhd_BUFSIZE/512 : iosize ); + + wrsize = iosize * 512; + if( wrsize > length ) + wrsize = length; + memcpy( dev->IoBuffer, buffer, wrsize ); + buffer += wrsize; + length -= wrsize; + + if( (err = vhd_io_write_fcn(dev->context, dev->IoBuffer, lbn, iosize)) != 0 ) { + if( err == -ERANGE ) + return SS$_ILLBLKNUM; + return SS$_PARITY; + } + lbn += iosize; + } + return status; +} + +/*********************************************************** phyvhd_close() */ +unsigned phyvhd_close( struct DEV *dev ) { + if( dev->context != NULL ) { + vhd_close_fcn( dev->context ); + _aligned_free( dev->context ); + dev->context = NULL; + } + _aligned_free( dev->IoBuffer ); + dev->IoBuffer = NULL; + return SS$_NORMAL; +} + diff --git a/extracters/ods2/phyvhd.h b/extracters/ods2/phyvhd.h new file mode 100644 index 0000000..6c30bb2 --- /dev/null +++ b/extracters/ods2/phyvhd.h @@ -0,0 +1,13 @@ +#ifndef _PHYVHD_H +#define _PHYVHD_H 1 + +struct DEV; + +unsigned phyvhd_snapshot( const char *filename, const char *parent ); +unsigned phyvhd_init( struct DEV *dev ); +unsigned phyvhd_close( struct DEV *dev ); +void phyvhd_show( struct DEV *dev, size_t column ); + +int phyvhd_available( int query ); + +#endif diff --git a/extracters/ods2/phyvirt.c b/extracters/ods2/phyvirt.c index ccc1f8a..cb148c3 100644 --- a/extracters/ods2/phyvirt.c +++ b/extracters/ods2/phyvirt.c @@ -1,18 +1,25 @@ -/* Phyvirt.c V2.1 Module to map, remember and find virtual devices */ +/* Phyvirt.c Module to map, remember and find virtual devices */ /* Timothe Litt Feb 2016 * Incorporates some code from Larry Baker of the USGS. */ -/* - This is part of ODS2 written by Paul Nankervis, - email address: Paulnank@au1.ibm.com +/* Timothe Litt March 2016 + * Copyright (C) 2016 Timothe litt + * litt at acm dot org + * + * Free for use with the ODS2 package. All other rights reserved. + */ - 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. -*/ +/* + * This is distributed as part of ODS2, originally 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. + */ /* A virtual device is used when normal file I/O is desired, e.g. to * a disk image (simulator, .iso). @@ -21,35 +28,78 @@ * /virtual to the mount command. * If the device name is of the form dev:file, dev: becomes the * virtual device name. Otherwise, a suitable name is created. + * + * This module also virtualizes all I/O, living between the filesystem + * and the platform-specific read/write LBN layer. This enables + * device specific transformations, such as interleave and skew. + * + * It also provides an access layer for VHD format image files, on + * platforms where libvhd is available. */ +#if !defined( DEBUG ) && defined( DEBUG_PHYVIRT ) +#define DEBUG DEBUG_PHYVIRT +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + #include +#include #include #include #include -#include "phyio.h" -#include "ssdef.h" + #include "compat.h" +#include "device.h" +#include "ods2.h" +#include "phyio.h" +#include "phyvirt.h" +#include "ssdef.h" static struct VDEV { struct VDEV *next; char *devnam; char *path; + struct DEV *dev; } *virt_device_list = NULL; +#ifdef USE_VHD +#include "phyvhd.h" +#endif + static int virt_compare( unsigned keylen, const char *keynam, - const char *devnam ); + const char *devnam ); static struct VDEV *virt_insert( const char *devnam, unsigned devsiz, const char *path ); static void virt_remove( const char *devnam, unsigned devsiz ); + +unsigned virt_open( char **devname, unsigned flags, struct DEV **dev ); +static unsigned virt_makedev( char *devnam, char **vname ); static char *autodev( void ); +static unsigned virt_assign( char *devnam, struct DEV *dev ); +unsigned virt_close( struct DEV *dev ); + +static unsigned maplbn( unsigned lbn, disktypep_t dp, unsigned sect ); + +unsigned virt_read( struct DEV *dev, unsigned lbn, unsigned length, + char *buffer ) ; +unsigned virt_write( struct DEV *dev, unsigned lbn, unsigned length, + const char *buffer ); +unsigned virt_readp( struct DEV *dev, unsigned pbn, unsigned length, + char *buffer ); +unsigned virt_writep( struct DEV *dev, unsigned pbn, unsigned length, + char *buffer ); +static size_t get_devsiz( const char *devnam ); + /************************************************************* virt_show() */ void virt_show( const char *devnam ) { - unsigned devlen, devsiz; + unsigned devsiz; struct VDEV *vp; if( devnam == NULL ) { @@ -81,16 +131,17 @@ void virt_show( const char *devnam ) { for( vp = virt_device_list; vp != NULL; vp = vp->next ) { n = strlen( vp->path ); - printf( " %-*s %.*s\n", (int)maxd, vp->devnam, (int)maxp, - (n > maxp)? vp->path+(n-maxp): vp->path ); + printf( " %-*s %-*.*s", (int)maxd, vp->devnam, (int)maxp, + (int)maxp, (n > maxp)? vp->path+(n-maxp): vp->path ); +#ifdef USE_VHD + if( vp->dev->access & PHY_VHDDSK ) + phyvhd_show( vp->dev, 2 + maxd + 1 + maxp + 1 ); +#endif + putchar( '\n' ); } } else { - devlen = strlen( devnam ); - for( devsiz = 0; devsiz < devlen; devsiz++ ) { - if( devnam[devsiz] == ':' ) { - break; - } - } + devsiz = get_devsiz( devnam ); + if( devsiz == 0 ) { return; } @@ -129,15 +180,10 @@ static int virt_compare( unsigned keylen, const char *keynam, char *virt_lookup( const char *devnam ) { - unsigned devlen, devsiz; + unsigned devsiz; struct VDEV *vp; - devlen = strlen( devnam ); - for( devsiz = 0; devsiz < devlen; devsiz++ ) { - if( devnam[devsiz] == ':' ) { - break; - } - } + devsiz = get_devsiz( devnam ); if( devsiz == 0 ) { return NULL; } @@ -149,66 +195,6 @@ char *virt_lookup( const char *devnam ) { return NULL; } -/*********************************************************** virt_device() */ - -/* virt_device() creates/deletes virtual devices... */ - -unsigned virt_device( char *devnam, char **vname ) { - - unsigned devlen, devsiz; - char *path = NULL; - - if( vname != NULL ) { - char *p; - struct VDEV **vpp; - - if( (p = strchr( devnam, '=' )) != NULL ) { - *p = '\0'; - path = ++p; - } else { - path = devnam; - devnam = autodev(); - } - if( (p = phyio_path( path )) == NULL || strlen( p ) == 0 ) { - printf( "%%ODS2-E-BADPATH, Invalid path: %s\n", path ); - free( p ); - return SS$_BADPARAM; - } - path = p; - - for( vpp = &virt_device_list; *vpp != NULL; vpp = &(*vpp)->next ) { - if( strcmp( (*vpp)->path, path ) == 0 ) { - printf( "%%ODS2-E-MAPPED, %s is in use on virtual drive %s\n", path, (*vpp)->devnam ); - free( path ); - return SS$_DEVMOUNT; - } - } - } - devlen = strlen( devnam ); - - for( devsiz = 0; devsiz < devlen; devsiz++ ) { - if( devnam[devsiz] == ':' ) { - break; - } - } - if( devsiz == 0 ) { - free( path ); - return SS$_BADPARAM; - } - virt_remove( devnam, devsiz ); - if( vname != NULL ) { - struct VDEV *vp; - - vp = virt_insert( devnam, devsiz, path ); - free( path ); - if( vp == NULL ) - return SS$_INSFMEM; - - *vname = vp->devnam; - } - return SS$_NORMAL; -} - /*********************************************************** virt_insert() */ static struct VDEV *virt_insert( const char *devnam, unsigned devsiz, @@ -231,6 +217,7 @@ static struct VDEV *virt_insert( const char *devnam, unsigned devsiz, } vp->devnam = (char *) malloc( devsiz + 1 ); vp->path = (char *) malloc( (pathsiz = strlen( path )) + 1 ); + if( vp->devnam == NULL || vp->path == NULL ) { free( vp->devnam ); free( vp->path ); @@ -240,6 +227,7 @@ static struct VDEV *virt_insert( const char *devnam, unsigned devsiz, memcpy( vp->devnam, devnam, devsiz ); vp->devnam[devsiz] = '\0'; memcpy( vp->path, path, pathsiz+1 ); + vp->dev = NULL; vp->next = *here; *here = vp; @@ -264,6 +252,118 @@ static void virt_remove( const char *devnam, unsigned devsiz ) { } } +/*********************************************************** virt_open() */ +/* Wrapper for phyio that handles the mechanics of virtual + * devices. Calls phyio_init with everything straightend out. + */ + +unsigned virt_open( char **devname, unsigned flags, struct DEV **dev ) { + unsigned sts; + + flags &= ~PHY_VHDDSK; + + if( flags & MOU_VIRTUAL ) { + char *p; + if( (p = strrchr( *devname, '.')) != NULL && ( !strcmp( p, ".vhd" ) || + !strcmp( p, ".VHD" ) ) ) { +#ifdef USE_VHD + flags |= PHY_VHDDSK; +#else + printf( "%%ODS2-W-NOVHD, VHD disk support is not available in this version\n" ); + return SS$_IVDEVNAM; +#endif + } + + sts = virt_makedev( *devname, devname ); + if( !(sts & STS$M_SUCCESS) ) + return sts; + } else { + if( virt_lookup( *devname ) != NULL ) { + int devsiz; + + devsiz = get_devsiz( *devname ); + printf( "%%ODS2-E-VIRTDEV, %.*s is a virtual device\n", + (int)devsiz, *devname ); + return SS$_DEVMOUNT; + } + } + sts = device_lookup( strlen( *devname ), *devname, TRUE, dev ); + if( !(sts & STS$M_SUCCESS) ) { + if( flags & MOU_VIRTUAL ) + virt_remove( *devname, get_devsiz( *devname ) ); + return sts; + } + (*dev)->access = flags; /* Requested mount options */ + + (*dev)->disktype = disktype+((flags & MOU_DEVTYPE) >> MOU_V_DEVTYPE); + + if( flags & PHY_VHDDSK ) { +#ifdef USE_VHD + sts = phyvhd_init( *dev ); +#else + return SS$_IVDEVNAM; +#endif + } else { + sts = phyio_init( *dev ); + } + + if( (flags & MOU_VIRTUAL) ) { + if( sts & STS$M_SUCCESS ) { + if( !((sts = virt_assign( *devname, *dev )) & STS$M_SUCCESS) ) { +#ifdef USE_VHD + if( flags & PHY_VHDDSK ) + phyvhd_close( *dev ); + else +#endif + phyio_done( *dev ); + device_done( *dev ); + } + } else + virt_remove( *devname, get_devsiz( *devname ) ); + } + return sts; +} + +/************************************************************* virt_virt_makedev() */ + +/* First part of device creation: + * assign a virtual device to whatever path we have. + * We can't resolve the path or see if it's a duplicate because the + * file might not exist yet. (initialize) + */ + +static unsigned virt_makedev( char *devnam, char **vname ) { + struct VDEV *vp; + unsigned devsiz; + char *path = NULL; + char *p; + + if( (p = strchr( devnam, '=' )) != NULL ) { + *p = '\0'; + path = ++p; + if( (p = virt_lookup( devnam)) != NULL ) { + printf( "%%ODS2-E-INUSE, %s is in use by %s\n", devnam, p ); + return SS$_DEVALLOC; + } + } else { + path = devnam; + devnam = autodev(); + } + + devsiz = get_devsiz( devnam ); + if( devsiz == 0 ) { + return SS$_BADPARAM; + } + + vp = virt_insert( devnam, devsiz, path ); + if( vp == NULL ) + return SS$_INSFMEM; + + *vname = vp->devnam; + + return SS$_NORMAL; +} + /************************************************************* autodev() */ char *autodev( void ) { @@ -330,3 +430,690 @@ char *autodev( void ) { d++; } } + +/************************************************************* virt_assign() */ +/* Part two: + * Resolve the pathname and check for duplicate use. + * Delete the device if we have issues. Otherwise, resolve + * and record the full path. + * + * virt_open will close the physical device if we have problems. + */ + +static unsigned virt_assign( char *devnam, struct DEV *dev ) { + unsigned devsiz; + char *p; + struct VDEV *vp, **vpp; + + devsiz = get_devsiz( devnam ); + if( devsiz == 0 ) { + return SS$_BADPARAM; + } + + for( vp = virt_device_list; vp != NULL; vp = vp->next ) { + if( virt_compare( devsiz, (char *) devnam, vp->devnam ) == 0 ) + break; + } + if( vp == NULL ) + return SS$_BADPARAM; + + if( (p = phyio_path( vp->path )) == NULL || strlen( p ) == 0 ) { + printf( "%%ODS2-E-BADPATH, Invalid path: %s\n", vp->path ); + free( p ); + virt_remove( devnam, devsiz ); + return SS$_BADPARAM; + } + + for( vpp = &virt_device_list; *vpp != NULL; vpp = &(*vpp)->next ) { + if( (*vpp != vp) && strcmp( (*vpp)->path, p ) == 0 ) { + printf( "%%ODS2-E-MAPPED, %s is in use on virtual drive %s\n", p, (*vpp)->devnam ); + free( p ); + virt_remove( devnam, devsiz ); + return SS$_DEVALLOC; + } + } + + free( vp->path ); + vp->path = p; + vp->dev = dev; + + return SS$_NORMAL; +} + +/*********************************************************** virt_close() */ + +/* Destroy a (virtual) device. + * Wraps phyio_done; + */ + +unsigned virt_close( struct DEV *dev ) { + unsigned devsiz; + unsigned sts; + + cache_flush(); + + if( dev->access & MOU_VIRTUAL ) { + devsiz = get_devsiz( dev->devnam ); + virt_remove( dev->devnam, devsiz ); + } +#ifdef USE_VHD + if( dev->access & PHY_VHDDSK ) + sts = phyvhd_close( dev ); + else +#endif + sts = phyio_done( dev ); + device_done( dev ); + return sts; +} + +/*********************************************************** maplbn() */ +/* Map an LBN as viewed by the OS to each of its physical sectors. + */ + +static unsigned maplbn( unsigned lbn, disktypep_t dp, unsigned sect ) { + unsigned c, t, s; + ldiv_t r; + + lbn = (lbn * (512 / dp->sectorsize)) + sect; + + r = ldiv( lbn, dp->sectors * dp->tracks ); + c = r.quot; + r = ldiv( r.rem, dp->sectors ); + t = r.quot; + s = r.rem; + + if( s >= dp->sectors / dp->interleave ) + s = (s * dp->interleave) - (dp->sectors -1); + else + s = s * dp->interleave; + + s = (s + (dp->skew * c)) % dp->sectors; + + return (c * dp->sectors * dp->tracks) + (t * dp->sectors) + dp->reserved + s; +} + +/*********************************************************** virt_read() */ + +unsigned virt_read( struct DEV *dev, unsigned lbn, unsigned length, + char *buffer ) { + disktypep_t dp; + char *lbnbuf = NULL; + unsigned status, secblk; + off_t devlimit, end, eofptr; +#ifdef DEBUG_DISKIO + unsigned inlbn = lbn, inlen = length; + char *inbuf = buffer; +#endif + + dp = dev->disktype; + + /* First pass - inefficient but should function. + * First bit of ugliness is that simulator files may be smaller than the + * device size (not sparse, EOF isn't set). The filesystem will happily + * read never-written data (not a security issue), so we have to emulate + * sparse file semantics up to the device size. (For write, the file will + * extend.) + * + * Second is that interleaved devices cause lots of seeks. This could be + * a lot smarter - but then the filesystem has a cache and those devices + * tend to be small. So there's no rush to do better. + * + * Third, disk ID by the user isn't reliable; especially for images of + * physical disks. This can cause file system accesses to valid data + * to exceed the limit established by (alleged) geometry. + * + * Fourth, the (virtual) size of VHD files is not necessarily what was + * requested; it may have been rounded up to a page boundary. + */ + + devlimit = ((off_t)dp->sectors) * dp->tracks * dp->cylinders * dp->sectorsize; + eofptr = dev->eofptr; + + if( eofptr > devlimit ) + devlimit = eofptr; + + status = SS$_NORMAL; + + if( dp->reserved == 0 && dp->interleave == 0 && dp->skew == 0 ) { + if( (dev->access & MOU_VIRTUAL) && + (end = ((off_t)lbn * 512 + length)) > eofptr ) { + unsigned avail; + off_t start; + + if( end > devlimit ) { + printf( "%%ODS2-E-BLK2BIG, Read from non-existent block %lu\n", + ((end + dp->sectorsize -1)/dp->sectorsize) ); + return SS$_ILLBLKNUM; + } + start = (off_t)lbn * 512; + + if( start < eofptr ) { + avail = (unsigned)( eofptr - start ); + + status = dev->devread( dev, lbn, avail, buffer ); + if( !(status & STS$M_SUCCESS) ) + return status; + + length -= avail; + buffer += avail; + } + memset( buffer, 0, length ); +#ifdef DEBUG_DISKIO + dumpblock( inlbn, inlen, inbuf, "Virt Read with %u bytes of fill status %08X", + length, status ); +#endif + return status; + } + status = dev->devread( dev, lbn, length, buffer ); +#ifdef DEBUG_DISKIO + dumpblock( inlbn, inlen, inbuf, "Virt Read status %08X", status ); +#endif + return status; + } + + status = SS$_NORMAL; + + if( dp->sectorsize > 512 ) + abort(); /* TODO *** Handle large sectors, e.g. CDROM 2K where multiple LBNs fit into one. */ + + secblk = 512 / dp->sectorsize; + if( (lbnbuf = malloc( 512 * 2 )) == NULL ) + return SS$_INSFMEM; + + while( length > 0 && (status & STS$M_SUCCESS) ) { + unsigned pbn; + unsigned s; + + for( s = 0; s < secblk && length > 0; s++ ) { + unsigned n; + ldiv_t r; + + pbn = maplbn( lbn, dp, s ); + + r = ldiv( (pbn * dp->sectorsize), 512 ); + + n = r.rem + (length > dp->sectorsize? dp->sectorsize: length); + + if( (dev->access & MOU_VIRTUAL) && + (end = ((off_t)r.quot * 512 + n)) > eofptr ) { + unsigned avail; + off_t start; + + if( ((off_t)pbn * (long)dp->sectorsize) + (long)n - r.rem > devlimit ) { + printf( "%%ODS2-E-BLK2BIG, Read from non-existent block %lu\n", + (((pbn * dp->sectorsize + n) + dp->sectorsize -1) / + dp->sectorsize) -1 ); + status = SS$_ILLBLKNUM; + break; + } + + start = (off_t)r.quot * 512; + + if( start < eofptr ) { + avail = (unsigned)( eofptr - start ); + + status = dev->devread( dev, r.quot, avail, lbnbuf ); + if( !(status & STS$M_SUCCESS) ) + return status; + + memset( lbnbuf+avail, 0, n - avail ); + } else + memset( lbnbuf, 0, n ); + } else { + status = dev->devread( dev, r.quot, n, lbnbuf ); + if( !(status & STS$M_SUCCESS) ) + break; + } + n = (dp->sectorsize > length)? length: dp->sectorsize; + memcpy( buffer, lbnbuf+r.rem, n ); + buffer += n; + length -= n; + } + ++lbn; + } + free( lbnbuf ); +#ifdef DEBUG_DISKIO + dumpblock( inlbn, inlen, inbuf, "Virt Read interleaved status %08X", status ); +#endif + return status; +} + +/*********************************************************** virt_write() */ + +unsigned virt_write( struct DEV *dev, unsigned lbn, unsigned length, + const char *buffer ) { + disktypep_t dp; + char *lbnbuf = NULL; + unsigned status, secblk; + off_t devlimit, start, end; +#ifdef DEBUG_DISKIO + unsigned inlbn = lbn, inlen = length; + const char *inbuf = buffer; +#endif + + dp = dev->disktype; + + devlimit = ((off_t)dp->sectors) * dp->tracks * dp->cylinders * dp->sectorsize; + if( dev->eofptr > devlimit ) + devlimit = dev->eofptr; + + end = ((off_t)lbn * 512) + length; + + if( dp->reserved == 0 && dp->interleave == 0 && dp->skew == 0 ) { + if( (end + 511)/512 > devlimit ) { + printf( "%%ODS2-E-BLK2BIG, Write to non-existent block %lu\n", + ((end + dp->sectorsize -1)/dp->sectorsize) ); + return SS$_ILLBLKNUM; + } + status = dev->devwrite( dev, lbn, length, buffer ); +#ifdef DEBUG_DISKIO + dumpblock( inlbn, inlen, inbuf, "Virt Write, status %08X", status ); +#endif + if( (status & STS$M_SUCCESS) && (dev->access & MOU_VIRTUAL) ) { + if( end > dev->eofptr ) { + dev->eofptr = end; + } + } + return status; + } + + status = SS$_NORMAL; + + if( dp->sectorsize > 512 ) + abort(); /* TODO *** Handle large sectors, e.g. CDROM 2K where multiple LBNs fit into one. */ + + secblk = 512 / dp->sectorsize; + if( (lbnbuf = malloc( 512 * 2 )) == NULL ) + return SS$_INSFMEM; + + while( length > 0 && (status & STS$M_SUCCESS) ) { + unsigned pbn; + unsigned s; + + for( s = 0; s < secblk && length > 0; s++ ) { + unsigned n, m; + ldiv_t r; + + pbn = maplbn( lbn, dp, s ); + + r = ldiv( (pbn * dp->sectorsize), 512 ); + + n = r.rem + (length > dp->sectorsize? dp->sectorsize: length); + + start = (off_t)r.quot * 512; + m = ((n + 511) / 512) * 512; + if( start < dev->eofptr && (off_t)(start + m) > dev->eofptr ) + m = (unsigned)(dev->eofptr - start); + + if( dev->access & MOU_VIRTUAL ) { + if( ((off_t)pbn * (long)dp->sectorsize) + ((long)n - r.rem) > devlimit ) { + printf( "%%ODS2-E-BLK2BIG, Write to non-existent block %lu\n", + (((pbn * dp->sectorsize) + (n - r.rem) + + dp->sectorsize -1) / dp->sectorsize ) -1 ); + status = SS$_ILLBLKNUM; + break; + } + } + memset( lbnbuf, 0, 512 * 2 ); + if( start < dev->eofptr ) { + status = dev->devread( dev, r.quot, m, lbnbuf ); + if( !(status & STS$M_SUCCESS) ) + break; + if( m < n ) + memset( lbnbuf+m, 0, n - m ); + } + memcpy( lbnbuf+r.rem, buffer, n - r.rem ); + end = start + n; + if( end > dev->eofptr ) { + status = dev->devwrite( dev, r.quot, n, lbnbuf ); + dev->eofptr = end; + } else + status = dev->devwrite( dev, r.quot, m, lbnbuf ); + + if( !(status & STS$M_SUCCESS) ) + break; + n -= r.rem; + buffer += n; + length -= n; + } + ++lbn; + } +#ifdef DEBUG_DISKIO + dumpblock( inlbn, inlen, inbuf, "Virt Write (interleaved) status %08X", status ); +#endif + + free( lbnbuf ); + return status; +} + +/*********************************************************** virt_readp() */ + +unsigned virt_readp( struct DEV *dev, unsigned pbn, unsigned length, + char *buffer ) { + disktypep_t dp; + unsigned status; + off_t devlimit, eofptr; + off_t fp, offset; + char *buf; + unsigned avail; + + dp = dev->disktype; + + devlimit = ((off_t)dp->sectors) * dp->tracks * dp->cylinders * dp->sectorsize; + if( dev->eofptr > devlimit ) + devlimit = dev->eofptr; + + status = SS$_NORMAL; + fp = ((off_t)pbn) * dp->sectorsize; + + if( fp + (off_t)length > devlimit ) { + printf( "%%ODS2-E-BLK2BIG, Read from non-existent block %lu\n", + ((fp + length + dp->sectorsize -1)/dp->sectorsize) ); + return SS$_ILLBLKNUM; + } + + if( dev->access & MOU_VIRTUAL ) { + eofptr = dev->eofptr; + + if( fp >= eofptr ) { + memset( buffer, 0, length ); +#ifdef DEBUG_DISKIO + dumpblock( pbn, length, buffer, "Phys Read of unallocated block status %08X", + status ); +#endif + return status; + } + } else { + eofptr = devlimit; + } + + offset = fp % 512; + + avail = (unsigned)(eofptr - fp); + if( avail > length ) + avail = length; + + if( offset == 0 ) { + status = dev->devread( dev, (unsigned) (fp / 512), avail, buffer ); + if( !(status & STS$M_SUCCESS) ) + return status; + + length -= avail; + buffer += avail; + + if( length > 0 ) + memset( buffer, 0, length ); +#ifdef DEBUG_DISKIO + dumpblock( pbn, avail+length, buffer-avail, "Phys Read with %u bytes of fill status %08X", + length, status ); +#endif + return status; + } + if( (buf = malloc( (size_t) (offset + avail) )) == NULL ) + return SS$_INSFMEM; + status = dev->devread( dev, (unsigned) (fp / 512), (unsigned) (offset + avail), buf ); + + memcpy( buffer, buf+offset, avail ); + free( buf ); + length -= avail; + buffer += avail; + if( length > 0 ) + memset( buffer, 0, length ); +#ifdef DEBUG_DISKIO + dumpblock( pbn, avail+length, buffer-avail, "Phys Read with %u bytes of fill status %08X", + length, status ); +#endif + return status; +} + +/*********************************************************** virt_writep() */ + +unsigned virt_writep( struct DEV *dev, unsigned pbn, unsigned length, + char *buffer ) { + disktypep_t dp; + unsigned status; + off_t devlimit; + off_t fp, offset; + char *buf = NULL; + unsigned avail, n, m; +#ifdef DEBUG_DISKIO + unsigned inpbn = pbn, inlen = length; + const char *inbuf = buffer; +#endif + + dp = dev->disktype; + + devlimit = ((off_t)dp->sectors) * dp->tracks * dp->cylinders * dp->sectorsize; + if( dev->eofptr > devlimit ) + devlimit = dev->eofptr; + + status = SS$_NORMAL; + fp = ((off_t)pbn) * dp->sectorsize; + + if( fp + (off_t)length > devlimit ) { + printf( "%%ODS2-E-BLK2BIG, Write to non-existent block %lu\n", + ((fp + length + dp->sectorsize -1)/dp->sectorsize) ); + return SS$_ILLBLKNUM; + } + + offset = fp % 512; + + if( offset == 0 && length % 512 == 0 ) { + status = dev->devwrite( dev, (unsigned)(fp / 512), length, buffer ); +#ifdef DEBUG_DISKIO + dumpblock( inpbn, inlen, inbuf, "Phys Write, status %08X", status ); +#endif + if( dev->access & MOU_VIRTUAL ) { + fp += length; + if( fp > dev->eofptr ) + dev->eofptr = fp; + } + return status; + } + + if( (buf = malloc( 512 )) == NULL ) + return SS$_INSFMEM; + + do { + if( offset != 0 ) { + avail = (unsigned) ( devlimit - (fp -offset) ); + + n = (avail > 512)? 512: avail; + + status = dev->devread( dev, (unsigned) (fp / 512), n, buf ); + if( !(status & STS$M_SUCCESS) ) + break; + + m = (n > length)? length: n; + + memcpy( buf+offset, buffer, m ); + buffer += m; + length -= m; + + status = dev->devwrite( dev, (unsigned) (fp / 512), n, buf ); + if( !(status & STS$M_SUCCESS) ) + break; + fp += n; + } + + if( (n = length / 512) > 0 ) { + n *= 512; + status = dev->devwrite( dev, (unsigned) (fp / 512), n, buffer ); + if( !(status & STS$M_SUCCESS) ) + break; + + buffer += n; + length -= n; + fp += n; + } + + if( length > 0 ) { + avail = (unsigned) ( devlimit - fp ); + + n = (avail > 512)? 512: avail; + + status = dev->devread( dev, (unsigned) (fp / 512), n, buf ); + if( !(status & STS$M_SUCCESS) ) + break; + + m = (n > length)? length: n; + + memcpy( buf, buffer, m ); + buffer += m; + length -= m; + + status = dev->devwrite( dev, (unsigned) (fp / 512), n, buf ); + if( !(status & STS$M_SUCCESS) ) + break; + + fp += n; + } + if( length != 0 ) + status = SS$_BUGCHECK; + } while( 0 ); + + if( (dev->access & MOU_VIRTUAL) && fp > dev->eofptr ) + dev->eofptr = fp; + +#ifdef DEBUG_DISKIO + dumpblock( inpbn, inlen, inbuf, "Phys Write, status %08X", status ); +#endif + free( buf ); + return status; +} + +/*********************************************************** vget_devsiz() */ + +static size_t get_devsiz( const char *devnam ) { + const char *p; + + if( (p = strchr( devnam, ':' )) == NULL ) + return strlen( devnam ); + + return (size_t)( p - devnam ); +} + +/*********************************************************** disktype */ + +#define DISK( name, sectors, tracks, cylinders, flags ) \ + {#name, 512 ## ul, sectors ## ul, tracks ## ul, cylinders ## ul, \ + 0 ## ul, 0 ## ul, 0 ## ul, (flags)}, +#define DISKS( name, sectorsize, sectors, tracks, cylinders, reserved, flags ) \ + {#name, sectorsize ## ul, sectors ## ul, tracks ## ul, cylinders ## ul, \ + reserved ## ul, 0 ## ul, 0 ## ul, (flags)}, +#define DISKI( name, sectorsize, sectors, tracks, cylinders, reserved, \ + interleave, skew, flags ) \ + {#name, sectorsize ## ul, sectors ## ul, tracks ## ul, cylinders ## ul, \ + reserved ## ul, interleave ## ul, skew ## ul, (flags)}, + +disktype_t disktype[] = { + DISK(UNKNOWN, 574, 1, 1,DISK_BADSW) /* First = short sequence delta = 1 */ + +#if 0 + DISK(RB02, 20, 2, 512, DISK_BAD144) + DISK(RB80, 31, 14, 559, DISK_BAD144) +#endif + + DISK(RK05, 12, 2, 203, DISK_BAD144) + DISK(RK06, 22, 3, 411, DISK_BAD144) + DISK(RK07, 22, 3, 815, DISK_BAD144) + DISK(RK11, 12, 2, 203, DISK_BAD144) + + DISKS(RL01, 256, 40, 2, 256, 0, DISK_BAD144) + DISKS(RL02, 256, 40, 2, 512, 0, DISK_BAD144) + + DISK(RM02, 32, 5, 823, DISK_BAD144) + DISK(RM03, 32, 5, 823, DISK_BAD144) + DISK(RP04, 22, 19, 411, DISK_BAD144) + DISK(RP05, 22, 19, 411, DISK_BAD144) + DISK(RM80, 31, 14, 559, DISK_BAD144) + DISK(RP06, 22, 19, 815, DISK_BAD144) + DISK(RM05, 32, 19, 823, DISK_BAD144) + DISK(RP07, 50, 32, 630, DISK_BAD144) + +#if 0 /* Not useful now as RSX20-F used ODS-1 */ + DISKS(RM02-T, 576, 30, 5, 823, 0, DISK_BAD144) + DISKS(RM03-T, 576, 30, 5, 823, 0, DISK_BAD144) + DISKS(RP04-T, 576, 20, 19, 411, 0, DISK_BAD144) + DISKS(RP05-T, 576, 20, 19, 411, 0, DISK_BAD144) + DISKS(RM80-T, 576, 30, 14, 559, 0, DISK_BAD144) + DISKS(RP06-T, 576, 20, 19, 815, 0, DISK_BAD144) + DISKS(RM05-T, 576, 30, 19, 823, 0, DISK_BAD144) + DISKS(RP07-T, 576, 43, 32, 630, 0, DISK_BAD144) +#endif + + DISK(RX50, 10, 1, 80, DISK_BADSW) + DISK(RX33, 15, 2, 80, DISK_BADSW) + +#if 0 + DISK(RD50, 99, 99, 9999,0) +#endif + DISK(RD51, 18, 4, 306,0) + DISK(RD31, 17, 4, 615,0) + DISK(RD52, 17, 8, 512,0) + DISK(RD53, 17, 7, 1024,0) + DISK(RD54, 17, 15, 1225,0) + + DISK(RA72, 51, 20, 1921,0) + +#if 0 + DISK(RA80, 99, 99, 9999,0) + +#endif + DISK(RA81, 51, 14, 1258,0) + DISK(RA82, 57, 15, 1435,0) + + DISK(RA90, 69, 13, 2649,0) + DISK(RA92, 73, 13, 3099,0) + + DISK(RRD40,128, 1, 10400,0) + DISK(RRD50,128, 1, 10400,0) + DISKI(RX01, 128, 26, 1, 77, 26, 2, 6, DISK_BADSW) + DISKI(RX02, 256, 26, 1, 77, 26, 2, 6, DISK_BADSW) + +#if 0 + DISK(RX23-SD, 99, 99, 9999, DISK_BADSW) + DISK(RX23-DD, 99, 99, 9999, DISK_BADSW) + + DISK(RX33-SD, 10, 1, 80, DISK_BADSW) + DISK(RX33-DD, 99, 99, 9999, DISK_BADSW) +#endif + + DISK(RX50, 10, 1, 80, DISK_BADSW) +#if 0 + DISK(RC25, 99, 99, 9999, 0) + + DISK(RF30, 99, 99, 9999, 0) + DISK(RF31, 99, 99, 9999, 0) + DISK(RF35, 99, 99, 9999, 0) + DISK(RF36, 99, 99, 9999, 0) + DISK(RF71, 99, 99, 9999, 0) + DISK(RF72, 99, 99, 9999, 0) + DISK(RF73, 99, 99, 9999, 0) + DISK(RF74, 99, 99, 9999, 0) + + DISK(RZ22, 99, 99, 9999, 0) + DISK(RZ23, 99, 99, 9999, 0) + DISK(RZ24, 99, 99, 9999, 0) + DISK(RZ25, 99, 99, 9999, 0) + DISK(RZ26, 99, 99, 9999, 0) + DISK(RZ27, 99, 99, 9999, 0) + DISK(RZ28, 99, 99, 9999, 0) + DISK(RZ29, 99, 99, 9999, 0) + DISK(RZ31, 99, 99, 9999, 0) + DISK(RZ33, 99, 99, 9999, 0) + DISK(RZ35, 99, 99, 9999, 0) + DISK(RZ55, 99, 99, 9999, 0) + DISK(RZ56, 99, 99, 9999, 0) + DISK(RZ57, 99, 99, 9999, 0) + DISK(RZ58, 99, 99, 9999, 0) + DISK(RZ59, 99, 99, 9999, 0) + + DISK(RZ72, 99, 99, 9999, 0) + DISK(RZ73, 99, 99, 9999, 0) + DISK(RZ74, 99, 99, 9999, 0) +#endif + + { NULL, 0, 0, 0, 0, 0, 0, 0, 0 } +}; +size_t max_disktype = (sizeof( disktype ) / sizeof( disktype[0] )) - 2; diff --git a/extracters/ods2/phyvirt.h b/extracters/ods2/phyvirt.h index 400fabb..6631956 100644 --- a/extracters/ods2/phyvirt.h +++ b/extracters/ods2/phyvirt.h @@ -1,20 +1,61 @@ -/* Phyvirt.h V2.1 Definitions for virtual device routines */ +/* Phyvirt.h Definitions for virtual device routines */ + +/* Timothe Litt March 2016 + * Copyright (C) 2016 Timothe litt + * litt at acm dot org + * + * Free for use with the ODS2 package. All other rights reserved. + */ /* - 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. -*/ + * This is distributed as part of ODS2, originally 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. + */ #ifndef _PHYVIRT_H #define _PHYVIRT_H +#include + +typedef struct disktype disktype_t; +typedef disktype_t *disktypep_t; + +struct disktype { + const char *name; + unsigned long sectorsize, sectors, tracks, cylinders; + unsigned long reserved, interleave, skew; + unsigned int flags; +#define DISK_BAD144 1 +#define DISK_BADSW 2 +}; + +extern struct disktype disktype[]; +extern size_t max_disktype; + +struct DEV; + void virt_show( const char *devnam ); +unsigned virt_open( char **devname, unsigned flags, struct DEV **dev ); char *virt_lookup( const char *devnam ); -unsigned virt_device( char *devnam, char **vname ); +unsigned virt_close( struct DEV *dev ); + +unsigned virt_read( struct DEV *dev, unsigned lbn, unsigned length, + char *buffer ); +unsigned virt_write( struct DEV *dev, unsigned lbn, unsigned length, + const char *buffer ); + +unsigned virt_readp( struct DEV *dev, unsigned pbn, unsigned length, + char *buffer ); +unsigned virt_writep( struct DEV *dev, unsigned pbn, unsigned length, + char *buffer ); + +#ifdef USE_VHD +int virt_vhdavailable( int needed ); +#endif #endif /* # ifndef _PHYVIRT_H */ diff --git a/extracters/ods2/phyvms.c b/extracters/ods2/phyvms.c index 2d7d963..2ca1fb2 100644 --- a/extracters/ods2/phyvms.c +++ b/extracters/ods2/phyvms.c @@ -1,4 +1,4 @@ -/* PHYVMS.c V2.1 Physical I/O module for VMS */ +/* PHYVMS.c Physical I/O module for VMS */ /* This is part of ODS2 written by Paul Nankervis, @@ -30,6 +30,14 @@ * Negative handle -> Unix I/O; positive handle -> SYS$QIOW(). */ +#if !defined( DEBUG ) && defined( DEBUG_PHYVMS ) +#define DEBUG DEBUG_PHYVMS +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + #include #include #include @@ -78,7 +86,12 @@ unsigned sys$dassgn(); #include "ods2.h" #include "phyio.h" -#include "virtual.h" +#include "phyvirt.h" + +static unsigned phyio_read( struct DEV *dev, unsigned block, unsigned length, + char *buffer ); +static unsigned phyio_write( struct DEV *dev, unsigned block, unsigned length, + const char *buffer ); struct ITMLST { unsigned short length; @@ -252,12 +265,60 @@ unsigned phyio_init( struct DEV *dev ) { init_count++; - dev->handle = 0; + dev->handle = -1; + dev->devread = phyio_read; + dev->devwrite = phyio_write; dev->sectors = 0; - dev->access &= ~MOU_VIRTUAL; - virtual = virt_lookup( dev->devnam ); - if ( virtual == NULL ) { + if( dev->access & MOU_VIRTUAL ) { + int vmsfd; + struct FAB fab; + + virtual = virt_lookup( dev->devnam ); + if ( virtual == NULL ) { + return SS$_NOSUCHDEV; + } +#define opnopts "ctx=stm", "rfm=udf", "shr=get,put" + vmsfd = open( virtual, + ((dev->access & MOU_WRITE )? O_RDWR : O_RDONLY) | + ((dev->access & (MOU_VIRTUAL|PHY_CREATE)) == (MOU_VIRTUAL|PHY_CREATE)? + O_CREAT | O_EXCL : 0), + 0666, opnopts ); + + if ( vmsfd < 0 && (dev->access & (MOU_WRITE|PHY_CREATE)) == MOU_WRITE ) { + dev->access &= ~MOU_WRITE; + vmsfd = open( virtual, O_RDONLY, 0444, opnopts ); + } + if ( vmsfd < 0 ) { + switch( errno ) { + case EEXIST: + printf( "%%ODS2-E-EXISTS, File %s already exists, not superseded\n", + virtual ); + sts = SS$_DUPFILENAME; + break; + default: + printf( "%%ODS2-E-OPENERR, Open %s: %s\n", + virtual, strerror( errno ) ); + } + return SS$_NOSUCHFILE; + } + + dev->handle = vmsfd; + + fab = cc$rms_fab; /* Make this a real FAB (bid and bln) */ + fab.fab$l_fna = (char *) virtual; + fab.fab$b_fns = strlen( virtual ); + sts = sys$open( &fab ); /* Lookup file, get file size */ + if ( sts & STS$M_SUCCESS ) { + dev->sectors = fab.fab$l_alq; + sys$close( &fab ); + if ( sizeof( off_t ) < 8 && dev->sectors > 0x3FFFFF ) { + close( vmsfd ); + dev->handle = -1; + return SS$_OFFSET_TOO_BIG; /* Sorry, need 64-bit off_t */ + } + } + } else { /* Physical */ struct dsc$descriptor devdsc; unsigned long devclass, cylinders, tracks, sectors; char devname[65]; @@ -318,34 +379,8 @@ unsigned phyio_init( struct DEV *dev ) { if ( sts & STS$M_SUCCESS ) { sts = sys$assign( &devdsc, &dev->handle, 0, 0, 0, 0 ); } - } else { - int vmsfd; - struct FAB fab; - fab = cc$rms_fab; /* Make this a real FAB (bid and bln) */ - fab.fab$l_fna = (char *) virtual; - fab.fab$b_fns = strlen( virtual ); - sts = sys$open( &fab ); /* Lookup file, get file size */ - if ( sts & STS$M_SUCCESS ) { - dev->sectors = fab.fab$l_alq; - sys$close( &fab ); - if ( sizeof( off_t ) < 8 && dev->sectors > 0x3FFFFF ) { - return SS$_OFFSET_TOO_BIG; /* Sorry, need 64-bit off_t */ - } - dev->access |= MOU_VIRTUAL; - vmsfd = ( dev->access & MOU_WRITE ) ? - open( virtual, O_RDWR, 0666 ) : - open( virtual, O_RDONLY, 0444 ); - if ( vmsfd < 0 && dev->access & MOU_WRITE ) { - dev->access &= ~MOU_WRITE; - vmsfd = open( virtual, O_RDONLY, 0444 ); - } - if ( vmsfd < 0 ) { - return SS$_NOSUCHFILE; - } - } - dev->handle = vmsfd; - sts = SS$_NORMAL; } + return sts; } @@ -365,10 +400,10 @@ unsigned phyio_done( struct DEV *dev ) { /*************************************************************** phyio_read() */ -unsigned phyio_read( struct DEV *dev, unsigned block, unsigned length, - char *buffer ) { +static unsigned phyio_read( struct DEV *dev, unsigned block, unsigned length, + char *buffer ) { -#ifdef DEBUG +#if DEBUG printf("Phyio read block: %d into %x (%d bytes)\n",block,buffer,length); #endif read_count++; @@ -396,10 +431,10 @@ unsigned phyio_read( struct DEV *dev, unsigned block, unsigned length, /************************************************************** phyio_write() */ -unsigned phyio_write( struct DEV *dev, unsigned block, unsigned length, - const char *buffer ) { +static unsigned phyio_write( struct DEV *dev, unsigned block, unsigned length, + const char *buffer ) { -#ifdef DEBUG +#if DEBUG printf("Phyio write block: %d from %x (%d bytes)\n",block,buffer,length); #endif write_count++; diff --git a/extracters/ods2/rms.c b/extracters/ods2/rms.c index a9f494f..ef96d55 100644 --- a/extracters/ods2/rms.c +++ b/extracters/ods2/rms.c @@ -1,5 +1,5 @@ /* check for cr - return terminator - update file length */ -/* RMS.c V2.1 RMS components */ +/* RMS.c RMS components */ /* This is part of ODS2 written by Paul Nankervis, @@ -19,6 +19,14 @@ file opens, gets, etc... */ +#if !defined( DEBUG ) && defined( DEBUG_RMS ) +#define DEBUG DEBUG_RMS +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + #define DEBUGx x #include @@ -33,19 +41,13 @@ #include "device.h" #include "direct.h" #include "fibdef.h" +#include "ods2.h" #include "rms.h" #include "ssdef.h" #include "stsdef.h" #include "compat.h" #include "sysmsg.h" -#ifndef TRUE -#define TRUE ( 0 == 0 ) -#endif -#ifndef FALSE -#define FALSE ( 0 != 0 ) -#endif - /* For file context info we use WCCDIR and WCCFILE structures... Each WCCFILE structure contains one WCCDIR structure for file @@ -73,7 +75,7 @@ struct WCCDIR { struct WCCDIR *wcd_next; struct WCCDIR *wcd_prev; -#ifdef DEBUG +#if DEBUG size_t size; #endif int wcd_status; @@ -186,7 +188,7 @@ unsigned name_delim(char *str,int len,int size[5]) } else { size[4] = 0; } -#ifdef DEBUG +#if DEBUG printf("Name delim %d %d %d %d %d\n", size[0],size[1],size[2],size[3],size[4]); #endif @@ -268,13 +270,13 @@ void cleanup_wcf(struct WCCFILE **wccfilep) struct WCCDIR *next = wcc->wcd_next; wcc->wcd_next = NULL; wcc->wcd_prev = NULL; -#ifdef DEBUG +#if DEBUG memset(wcc,0,wcc->size); #endif free(wcc); wcc = next; } -#ifdef DEBUG +#if DEBUG memset(wccfile,0,WCFALLOC_SIZE); #endif free(wccfile); @@ -297,13 +299,14 @@ unsigned do_search(struct FAB *fab,struct WCCFILE *wccfile) /* if first time through position at top directory... WCCDIR */ - while ((wcc->wcd_status & STATUS_INIT) == 0 && wcc->wcd_next != NULL) { + while( !(wcc->wcd_status & STATUS_INIT) && wcc->wcd_next != NULL) { wcc = wcc->wcd_next; } fibdsc.dsc_w_length = sizeof(struct fibdef); fibdsc.dsc_a_pointer = (char *) &fibblk; + fab->fab__w_verlimit = 0; while (TRUE) { - if ((wcc->wcd_status & STATUS_INIT) == 0 || wcc->wcd_wcc != 0) { + if( !(wcc->wcd_status & STATUS_INIT) || wcc->wcd_wcc != 0) { wcc->wcd_status |= STATUS_INIT; resdsc.dsc_w_length = 256 - wcc->wcd_prelen; resdsc.dsc_a_pointer = wccfile->wcf_result + wcc->wcd_prelen; @@ -315,7 +318,7 @@ unsigned do_search(struct FAB *fab,struct WCCFILE *wccfile) fibblk.fib$b_fid_rvn = 0; fibblk.fib$b_fid_nmx = 0; fibblk.fib$l_wcc = wcc->wcd_wcc; -#ifdef DEBUG +#if DEBUG wcc->wcd_sernam[wcc->wcd_serdsc.dsc_w_length] = '\0'; wccfile->wcf_result[wcc->wcd_prelen + wcc->wcd_reslen] = '\0'; printf("Ser: '%s' (%d,%d,%d) WCC: %d Prelen: %d '%s'\n", @@ -331,7 +334,8 @@ unsigned do_search(struct FAB *fab,struct WCCFILE *wccfile) sts = SS$_NOMOREFILES; } if (sts & STS$M_SUCCESS) { -#ifdef DEBUG + fab->fab__w_verlimit = fibblk.fib$w_verlimit; /* Nees a better mechanism** */ +#if DEBUG wccfile->wcf_result[wcc->wcd_prelen + wcc->wcd_reslen] = '\0'; printf("Fnd: '%s' (%d,%d,%d) WCC: %d\n", wccfile->wcf_result + wcc->wcd_prelen, @@ -388,12 +392,12 @@ unsigned do_search(struct FAB *fab,struct WCCFILE *wccfile) return SS$_NORMAL; } } else { -#ifdef DEBUG - printf("Err: %d\n",sts); +#if DEBUG + printf("Err: %s\n", getmsg( sts, MSG_TEXT )); #endif if (sts == SS$_BADIRECTORY) { if (wcc->wcd_next != NULL) { - if (wcc->wcd_next->wcd_status & STATUS_INIT) { + if( wcc->wcd_next->wcd_status & STATUS_INIT ) { sts = SS$_NOMOREFILES; } } @@ -410,10 +414,10 @@ unsigned do_search(struct FAB *fab,struct WCCFILE *wccfile) if (wcc->wcd_prev != NULL) { wcc->wcd_prev->wcd_next = wcc->wcd_next; } - wcc = wcc->wcd_next; + wcc = wcc->wcd_next; /* 6 lines back, this can be null, so how can we reference ->prelen ->reslen?? */ memcpy(wccfile->wcf_result + wcc->wcd_prelen + wcc->wcd_reslen - 6,".DIR;1",6); -#ifdef DEBUG +#if DEBUG memset(savwcc, 0, savwcc->size); #endif free(savwcc); @@ -425,7 +429,7 @@ unsigned do_search(struct FAB *fab,struct WCCFILE *wccfile) if ( newwcc == NULL ) { return SS$_INSFMEM; } -#ifdef DEBUG +#if DEBUG newwcc->size = WCDALLOC_SIZE(8); #endif newwcc->wcd_next = wcc->wcd_next; @@ -446,7 +450,7 @@ unsigned do_search(struct FAB *fab,struct WCCFILE *wccfile) wcc = newwcc; } else { if (wcc->wcd_next != NULL) { -#ifdef DEBUG +#if DEBUG if (wcc->wcd_next->wcd_prev != wcc) { printf("wcd_NEXT corruption\n"); } @@ -664,10 +668,11 @@ unsigned do_parse(struct FAB *fab,struct WCCFILE **wccret) struct WCCDIR *wcc; struct DEV *dev; sts = device_lookup(fna_size[0],wccfile->wcf_result,FALSE,&dev); - if ((sts & STS$M_SUCCESS) == 0) { + if( !(sts & STS$M_SUCCESS) ) { /** cleanup_wcf(&wccfile); **/ return sts; } + device_done(dev); if ((wccfile->wcf_vcb = dev->vcb) == NULL) { /** cleanup_wcf(&wccfile); **/ return SS$_DEVNOTMOUNT; @@ -713,7 +718,7 @@ unsigned do_parse(struct FAB *fab,struct WCCFILE **wccret) if ( wcd == NULL ) { return SS$_INSFMEM; } -#ifdef DEBUG +#if DEBUG wcd->size = WCDALLOC_SIZE(seglen + 8); #endif /* calloc() @@ -746,10 +751,11 @@ unsigned do_parse(struct FAB *fab,struct WCCFILE **wccret) } memcpy(wcc->wcd_sernam,wccfile->wcf_result + fna_size[0] + fna_size[1], wcc->wcd_serdsc.dsc_w_length); -#ifdef DEBUG +#if DEBUG wcc->wcd_sernam[wcc->wcd_serdsc.dsc_w_length] = '\0'; printf("Parse spec is %s\n",wccfile->wcf_wcd.wcd_sernam); - for (dirsiz = 0; dirsiz < 5; dirsiz++) printf(" %d",fna_size[dirsiz]); + for (dirsiz = 0; dirsiz < 5; dirsiz++) + printf( " %d",fna_size[dirsiz] ); printf("\n"); #endif } @@ -874,134 +880,162 @@ unsigned sys_get(struct RAB *rab) unsigned delim,rfm,sts; struct VIOC *vioc; struct FCB *fcb = ifi_table[rab->rab$l_fab->fab$w_ifi]->wcf_fcb; + int span; reclen = rab->rab$w_usz; recbuff = rab->rab$l_ubf; - delim = 0; - switch (rfm = rab->rab$l_fab->fab$b_rfm) { - case FAB$C_STMLF: - delim = 1; - break; - case FAB$C_STMCR: - delim = 2; - break; - case FAB$C_STM: - delim = 3; - break; - case FAB$C_VFC: - reclen += rab->rab$l_fab->fab$b_fsz; - break; - case FAB$C_FIX: - if (reclen < rab->rab$l_fab->fab$w_mrs) - return RMS$_RTB; - reclen = rab->rab$l_fab->fab$w_mrs; - break; - } offset = rab->rab$w_rfa[2] % 512; block = (rab->rab$w_rfa[1] << 16) + rab->rab$w_rfa[0]; if (block == 0) block = 1; - { - unsigned eofblk = VMSSWAP(fcb->head->fh2$w_recattr.fat$l_efblk); - if (block > eofblk || (block == eofblk && - offset >= VMSWORD(fcb->head->fh2$w_recattr.fat$w_ffbyte))) { - return RMS$_EOF; - } - } - sts = accesschunk(fcb,block,&vioc,&buffer,&blocks,0); - if (!(sts & STS$M_SUCCESS)) { - if (sts == SS$_ENDOFFILE) sts = RMS$_EOF; - return sts; - } + span = (rab->rab$l_fab->fab$b_rat & FAB$M_BLK) == 0; - if (rfm == FAB$C_VAR || rfm == FAB$C_VFC) { - vmsword *lenptr = (vmsword *) (buffer + offset); - reclen = VMSWORD(*lenptr); - offset += 2; - if (reclen > rab->rab$w_usz) { - sts = deaccesschunk(vioc,0,0,FALSE); + delim = 0; + switch( rfm = rab->rab$l_fab->fab$b_rfm ) { + case FAB$C_STMLF: + delim = 1; + span = 1; + break; + case FAB$C_STMCR: + delim = 2; + span = 1; + break; + case FAB$C_STM: + delim = 3; + span = 1; + break; + case FAB$C_VFC: + reclen += rab->rab$l_fab->fab$b_fsz; + break; + case FAB$C_FIX: + if (reclen < rab->rab$l_fab->fab$w_mrs) return RMS$_RTB; - } + reclen = rab->rab$l_fab->fab$w_mrs; + break; } + do { + unsigned eofblk = F11SWAP(fcb->head->fh2$w_recattr.fat$l_efblk); + if (block > eofblk || (block == eofblk && + offset >= F11WORD(fcb->head->fh2$w_recattr.fat$w_ffbyte))) { + return RMS$_EOF; + } + + sts = accesschunk( fcb, block, &vioc, &buffer, &blocks, 0 ); + if( !(sts & STS$M_SUCCESS) ) { + if (sts == SS$_ENDOFFILE) sts = RMS$_EOF; + return sts; + } + + if( rfm == FAB$C_VAR || rfm == FAB$C_VFC ) { + do { + f11word *lenptr = (f11word *) (buffer + offset); + + reclen = F11WORD(*lenptr); + offset += 2; + if( !span && offset + reclen > 510 ) { /* N.B. Length words 0xFFFF are used for fill */ + block++; + offset = 0; + blocks--; + buffer += 512; + } else + span = 1; + } while( !span && blocks > 0 && + (block < eofblk || + (block == eofblk && fcb->head->fh2$w_recattr.fat$w_ffbyte > 0)) ); + if( !span ) { + sts = deaccesschunk( vioc, 0, 0, FALSE ); + continue; + } + if( reclen > rab->rab$w_usz ) { + sts = deaccesschunk(vioc,0,0,FALSE); + return RMS$_RTB; + } + } + } while( !span ); + cpylen = 0; while (TRUE) { int dellen = 0; unsigned int seglen = blocks * 512 - offset; - if (delim) { - if (delim >= 3) { - char *ptr = buffer + offset; - if (dellen == 1 && *ptr != '\n') { - if (cpylen >= reclen) { - seglen = 0; - sts = RMS$_RTB; - } else { - *recbuff++ = '\r'; - cpylen++; - } - } - while (seglen-- > 0) { - char ch = *ptr++; - if (ch == '\n' || ch == '\f' || ch == '\v') { - if (ch == '\n') { - dellen++; + + if( seglen > 0 ) { + if (delim) { + if (delim >= 3) { + char *ptr = buffer + offset; + if (dellen == 1 && *ptr != '\n') { + if (cpylen >= reclen) { + seglen = 0; + sts = RMS$_RTB; } else { - dellen = 0; + *recbuff++ = '\r'; + cpylen++; } - delim = 99; - break; } - dellen = 0; - if (ch == '\r') dellen = 1; + while (seglen-- > 0) { + char ch = *ptr++; + if (ch == '\n' || ch == '\f' || ch == '\v') { + if (ch == '\n') { + dellen++; + } else { + dellen = 0; + } + delim = 99; + break; + } + dellen = 0; + if (ch == '\r') dellen = 1; + } + seglen = ptr - (buffer + offset) - dellen; + } else { + char *ptr = buffer + offset; + char term = '\r'; + if (delim == 1) + term = '\n'; + while (seglen-- > 0) { + if (*ptr++ == term) { + dellen = 1; + delim = 99; + break; + } + } + seglen = ptr - (buffer + offset) - dellen; } - seglen = ptr - (buffer + offset) - dellen;; } else { - char *ptr = buffer + offset; - char term = '\r'; - if (delim == 1) - term = '\n'; - while (seglen-- > 0) { - if (*ptr++ == term) { - dellen = 1; - delim = 99; - break; - } + if (seglen > reclen - cpylen) + seglen = reclen - cpylen; + if (rfm == FAB$C_VFC && cpylen < rab->rab$l_fab->fab$b_fsz) { + unsigned fsz = rab->rab$l_fab->fab$b_fsz - cpylen; + if (fsz > seglen) + if (rab->rab$l_rhb) { + memcpy(rab->rab$l_rhb + cpylen,buffer + offset,fsz); + } + cpylen += fsz; + offset += fsz; + seglen -= fsz; } - seglen = ptr - (buffer + offset) - dellen;; } - } else { - if (seglen > reclen - cpylen) - seglen = reclen - cpylen; - if (rfm == FAB$C_VFC && cpylen < rab->rab$l_fab->fab$b_fsz) { - unsigned fsz = rab->rab$l_fab->fab$b_fsz - cpylen; - if (fsz > seglen) - if (rab->rab$l_rhb) { - memcpy(rab->rab$l_rhb + cpylen,buffer + offset,fsz); + if (seglen) { + if (cpylen + seglen > reclen) { + seglen = reclen - cpylen; + sts = RMS$_RTB; } - cpylen += fsz; - offset += fsz; - seglen -= fsz; + memcpy(recbuff,buffer + offset,seglen); + recbuff += seglen; + cpylen += seglen; } + offset += seglen + dellen; + if ((offset & 1) && (rfm == FAB$C_VAR || rfm == FAB$C_VFC)) offset++; } - if (seglen) { - if (cpylen + seglen > reclen) { - seglen = reclen - cpylen; - sts = RMS$_RTB; - } - memcpy(recbuff,buffer + offset,seglen); - recbuff += seglen; - cpylen += seglen; - } - offset += seglen + dellen; - if ((offset & 1) && (rfm == FAB$C_VAR || rfm == FAB$C_VFC)) offset++; -/* ??? This was missing the "sts = ". Assumed to be an error. (LMB) ??? */ - { unsigned sts2; + + { unsigned sts2; sts2 = deaccesschunk(vioc,0,0,TRUE); if( (sts & STS$M_SUCCESS) ) sts = sts2; } - if (!(sts & STS$M_SUCCESS)) return sts; + if( !(sts & STS$M_SUCCESS) ) + return sts; block += offset / 512; offset %= 512; if ((delim == 0 && cpylen >= reclen) || delim == 99) { @@ -1073,15 +1107,15 @@ unsigned sys_put(struct RAB *rab) break; } - block = VMSSWAP(fcb->head->fh2$w_recattr.fat$l_efblk); - offset = VMSWORD(fcb->head->fh2$w_recattr.fat$w_ffbyte); + block = F11SWAP(fcb->head->fh2$w_recattr.fat$l_efblk); + offset = F11WORD(fcb->head->fh2$w_recattr.fat$w_ffbyte); sts = accesschunk(fcb,block,&vioc,&buffer,&blocks,1); if (!(sts & STS$M_SUCCESS)) return sts; if (rfm == FAB$C_VAR || rfm == FAB$C_VFC) { - vmsword *lenptr = (vmsword *) (buffer + offset); - *lenptr = VMSWORD(reclen); + f11word *lenptr = (f11word *) (buffer + offset); + *lenptr = F11WORD(reclen); offset += 2; } @@ -1115,11 +1149,11 @@ unsigned sys_put(struct RAB *rab) case 1: *buffer = '\n'; delim = 0; - break; + break; case 2: *buffer = '\r'; delim = 0; - break; + break; case 3: *buffer = '\r'; if (offset < blocks * 512) { @@ -1147,8 +1181,8 @@ unsigned sys_put(struct RAB *rab) if ((offset & 1) && (rfm == FAB$C_VAR || rfm == FAB$C_VFC)) offset++; block += offset / 512; offset %= 512; - fcb->head->fh2$w_recattr.fat$l_efblk = VMSSWAP(block); - fcb->head->fh2$w_recattr.fat$w_ffbyte = VMSWORD(offset); + fcb->head->fh2$w_recattr.fat$l_efblk = F11SWAP(block); + fcb->head->fh2$w_recattr.fat$w_ffbyte = F11WORD(offset); rab->rab$w_rfa[0] = block & 0xffff; rab->rab$w_rfa[1] = block >> 16; rab->rab$w_rfa[2] = offset; @@ -1170,15 +1204,16 @@ unsigned sys_display(struct FAB *fab) int ifi_no = fab->fab$w_ifi; if (ifi_no == 0 || ifi_no >= IFI_MAX) return RMS$_IFI; - fab->fab$l_alq = VMSSWAP(head->fh2$w_recattr.fat$l_hiblk); + fab->fab$l_alq = F11SWAP(head->fh2$w_recattr.fat$l_hiblk); fab->fab$b_bks = head->fh2$w_recattr.fat$b_bktsize; - fab->fab$w_deq = VMSWORD(head->fh2$w_recattr.fat$w_defext); + fab->fab$w_deq = F11WORD(head->fh2$w_recattr.fat$w_defext); fab->fab$b_fsz = head->fh2$w_recattr.fat$b_vfcsize; - fab->fab$w_gbc = VMSWORD(head->fh2$w_recattr.fat$w_gbc); - fab->fab$w_mrs = VMSWORD(head->fh2$w_recattr.fat$w_maxrec); + fab->fab$w_gbc = F11WORD(head->fh2$w_recattr.fat$w_gbc); + fab->fab$w_mrs = F11WORD(head->fh2$w_recattr.fat$w_maxrec); fab->fab$b_org = head->fh2$w_recattr.fat$b_rtype & 0xf0; fab->fab$b_rfm = head->fh2$w_recattr.fat$b_rtype & 0x0f; fab->fab$b_rat = head->fh2$w_recattr.fat$b_rattrib; + while (xab != NULL) { switch (xab->xab$b_cod) { case XAB$C_DAT: @@ -1196,31 +1231,31 @@ unsigned sys_display(struct FAB *fab) struct XABFHC *fhc = (struct XABFHC *) xab; fhc->xab$b_atr = head->fh2$w_recattr.fat$b_rattrib; fhc->xab$b_bkz = head->fh2$w_recattr.fat$b_bktsize; - fhc->xab$w_dxq = VMSWORD(head->fh2$w_recattr.fat$w_defext); - fhc->xab$l_ebk = VMSSWAP(head->fh2$w_recattr.fat$l_efblk); - fhc->xab$w_ffb = VMSWORD(head->fh2$w_recattr.fat$w_ffbyte); + fhc->xab$w_dxq = F11WORD(head->fh2$w_recattr.fat$w_defext); + fhc->xab$l_ebk = F11SWAP(head->fh2$w_recattr.fat$l_efblk); + fhc->xab$w_ffb = F11WORD(head->fh2$w_recattr.fat$w_ffbyte); if (fhc->xab$l_ebk == 0) { fhc->xab$l_ebk = fab->fab$l_alq; if (fhc->xab$w_ffb == 0) fhc->xab$l_ebk++; } - fhc->xab$w_gbc = VMSWORD(head->fh2$w_recattr.fat$w_gbc); - fhc->xab$l_hbk = VMSSWAP(head->fh2$w_recattr.fat$l_hiblk); + fhc->xab$w_gbc = F11WORD(head->fh2$w_recattr.fat$w_gbc); + fhc->xab$l_hbk = F11SWAP(head->fh2$w_recattr.fat$l_hiblk); fhc->xab$b_hsz = head->fh2$w_recattr.fat$b_vfcsize; - fhc->xab$w_lrl = VMSWORD(head->fh2$w_recattr.fat$w_maxrec); - fhc->xab$w_verlimit = - VMSWORD(head->fh2$w_recattr.fat$w_versions); - } - break; + fhc->xab$w_lrl = F11WORD(head->fh2$w_recattr.fat$w_maxrec); + fhc->xab$w_verlimit = (F11LONG( head->fh2$l_filechar ) & FH2$M_DIRECTORY)? + F11WORD(head->fh2$w_recattr.fat$w_versions): fab->fab__w_verlimit; + } + break; case XAB$C_PRO:{ struct XABPRO *pro = (struct XABPRO *) xab; - pro->xab$w_pro = VMSWORD(head->fh2$w_fileprot); + pro->xab$w_pro = F11WORD(head->fh2$w_fileprot); memcpy(&pro->xab$l_uic,&head->fh2$l_fileowner,4); - } - break; + } + break; case XAB$C_ITM:{ struct XABITM *itm = (struct XABITM *) xab; struct item_list *list; - vmslong fch = VMSLONG( head->fh2$l_filechar ); + f11long fch = F11LONG( head->fh2$l_filechar ); if( itm->xab$b_mode != XAB$K_SENSEMODE ) break; @@ -1275,6 +1310,7 @@ unsigned sys_close(struct FAB *fab) fab->fab$w_ifi = 0; ifi_table[ifi_no] = NULL; } + cache_flush(); /* Excessive, but seems safe... */ return sts; } @@ -1309,7 +1345,7 @@ unsigned sys_open(struct FAB *fab) sts = RMS$_WLD; } else { sts = do_search(fab,wccfile); - if ((sts & 1) == 0) + if( !(sts & STS$M_SUCCESS) ) wcc_flag = 0; } wccfile->wcf_status |= STATUS_TMPWCC; @@ -1350,21 +1386,24 @@ unsigned sys_erase(struct FAB *fab) int wcc_flag = FALSE; struct WCCFILE *wccfile = NULL; struct NAM *nam = fab->fab$l_nam; - if (fab->fab$w_ifi != 0) return RMS$_IFI; - while (ifi_table[ifi_no] != NULL && ifi_no < IFI_MAX) ifi_no++; - if (ifi_no >= IFI_MAX) return RMS$_IFI; - if (nam != NULL) { + + if( fab->fab$w_ifi != 0 ) + return RMS$_IFI; + while( ifi_table[ifi_no] != NULL && ifi_no < IFI_MAX ) ifi_no++; + if( ifi_no >= IFI_MAX ) return RMS$_IFI; + + if( nam != NULL ) { wccfile = (struct WCCFILE *) fab->fab$l_nam->nam$l_wcc; } if (wccfile == NULL) { sts = do_parse(fab,&wccfile); - if (sts & STS$M_SUCCESS) { + if( sts & STS$M_SUCCESS ) { wcc_flag = TRUE; - if (wccfile->wcf_status & STATUS_WILDCARD) { + if( wccfile->wcf_status & STATUS_WILDCARD ) { sts = RMS$_WLD; } else { - sts = do_search(fab,wccfile); - if ((sts & 1) == 0) + sts = do_search( fab,wccfile ); + if( !(sts & STS$M_SUCCESS) ) wcc_flag = 0; } } @@ -1374,13 +1413,15 @@ unsigned sys_erase(struct FAB *fab) if (sts & STS$M_SUCCESS) { struct fibdef fibblk; struct dsc_descriptor fibdsc,serdsc; + fibdsc.dsc_w_length = sizeof(struct fibdef); fibdsc.dsc_a_pointer = (char *) &fibblk; serdsc.dsc_w_length = wccfile->wcf_wcd.wcd_reslen; serdsc.dsc_a_pointer = wccfile->wcf_result + wccfile->wcf_wcd.wcd_prelen; - memcpy(&fibblk.fib$w_did_num,&wccfile->wcf_wcd.wcd_dirid, - sizeof(struct fiddef)); + + memcpy( &fibblk.fib$w_did_num, &wccfile->wcf_wcd.wcd_dirid, + sizeof(struct fiddef)); fibblk.fib$w_nmctl = 0; fibblk.fib$l_acctl = 0; fibblk.fib$w_fid_num = 0; @@ -1388,15 +1429,17 @@ unsigned sys_erase(struct FAB *fab) fibblk.fib$b_fid_rvn = 0; fibblk.fib$b_fid_nmx = 0; fibblk.fib$l_wcc = 0; - sts = direct(wccfile->wcf_vcb,&fibdsc,&serdsc,NULL,NULL,TRUE); + + sts = direct( wccfile->wcf_vcb, &fibdsc, &serdsc, + NULL, NULL, DIRECT_DELETE ) ; if (sts & STS$M_SUCCESS) { - sts = accesserase(wccfile->wcf_vcb,&wccfile->wcf_fid); + sts = accesserase( wccfile->wcf_vcb, &wccfile->wcf_fid ); } else { - printf("Direct status is %d\n",sts); + printf( "%%ODS2-E-DIRENT, failed to remove directory entry: %sn", getmsg( sts, MSG_TEXT ) ); } } - if (wcc_flag) { - cleanup_wcf(&wccfile); + if( wcc_flag ) { + cleanup_wcf( &wccfile ); if (nam != NULL) { nam->nam$l_wcc = NULL; } @@ -1494,6 +1537,8 @@ static void sys_rundown( void ) { access_rundown(); sysmsg_rundown(); + + cache_purge(TRUE); } /*************************************************************** sys_initialize() */ diff --git a/extracters/ods2/rms.h b/extracters/ods2/rms.h index 5775a8e..dfa82c5 100644 --- a/extracters/ods2/rms.h +++ b/extracters/ods2/rms.h @@ -1,4 +1,4 @@ -/* RMS.h V2.1 RMS routine definitions */ +/* RMS.h RMS routine definitions */ /* This is part of ODS2 written by Paul Nankervis, @@ -282,10 +282,11 @@ struct FAB { int fab$b_rfm; int fab$b_fac; void *fab$l_xab; + unsigned short fab__w_verlimit; }; #ifdef RMS$INITIALIZE -struct FAB cc$rms_fab = {NULL,0,NULL,NULL,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}; +struct FAB cc$rms_fab = {NULL,0,NULL,NULL,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL,0}; #else extern struct FAB cc$rms_fab; #endif diff --git a/extracters/ods2/searchcmd.c b/extracters/ods2/searchcmd.c new file mode 100644 index 0000000..a26bc45 --- /dev/null +++ b/extracters/ods2/searchcmd.c @@ -0,0 +1,153 @@ +/* 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. + */ + +#if !defined( DEBUG ) && defined( DEBUG_SEARCHCMD ) +#define DEBUG DEBUG_SEARCHCMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + + +/***************************************************************** dosearch() */ + +/* dosearch: a simple file search routine */ + +param_t 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 } +}; + +DECL_CMD(search) { + int sts = 0; + int filecount = 0; + int findcount = 0; + char res[NAM$C_MAXRSS + 1],rsa[NAM$C_MAXRSS + 1]; + register char firstch, *searstr, *searend, *s; + struct NAM nam = cc$rms_nam; + struct FAB fab = cc$rms_fab; + struct RAB rab = cc$rms_rab; + + UNUSED(argc); + UNUSED(qualc); + UNUSED(qualv); + + searstr = argv[2]; /* unquote( argv[2] ); */ + + searend = searstr + strlen( searstr ); + for ( s = searstr; s < searend; s++ ) { + *s = tolower( *s ); + } + firstch = *searstr++; + filecount = 0; + findcount = 0; + 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 & STS$M_SUCCESS ) { + nam.nam$l_rsa = rsa; + nam.nam$b_rss = NAM$C_MAXRSS; + fab.fab$l_fop = FAB$M_NAM; + while ( ( sts = sys_search( &fab ) ) & STS$M_SUCCESS ) { + sts = sys_open(&fab); + if ( !( sts & STS$M_SUCCESS ) ) { + printf("%%SEARCH-F-OPENFAIL, Open error: %s\n",getmsg(sts, MSG_TEXT)); + } else { + rab.rab$l_fab = &fab; + sts = sys_connect( &rab ); + if ( sts & STS$M_SUCCESS ) { + int printname = 1; + char *rec; + + if( (rec = malloc( MAXREC + 2 )) == NULL ) + sts = SS$_INSFMEM; + else { + filecount++; + rab.rab$l_ubf = rec; + rab.rab$w_usz = MAXREC; + while( (sts = sys_get( &rab )) & STS$M_SUCCESS ) { + 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" + ); + printf( "%s\n\n", rsa ); + printname = 0; + } + fputs( rec, stdout ); + if( fab.fab$b_rat & PRINT_ATTR ) { + fputc( '\n', stdout ); + } + break; + } + } + } + } + free( rec ); + rec = NULL; + } + sys_disconnect(&rab); + } + if (sts == SS$_NOTINSTALL) { + printf( + "%%SEARCH-W-NOIMPLEM, file operation not implemented\n" + ); + sts = SS$_NORMAL; + } + sys_close(&fab); + } + } + if (sts == RMS$_NMF || sts == RMS$_FNF) sts = SS$_NORMAL; + } + if ( sts & STS$M_SUCCESS ) { + 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, MSG_TEXT)); + } + return sts; +} + + diff --git a/extracters/ods2/setcmd.c b/extracters/ods2/setcmd.c new file mode 100644 index 0000000..456e722 --- /dev/null +++ b/extracters/ods2/setcmd.c @@ -0,0 +1,202 @@ +/* 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. + */ + +#if !defined( DEBUG ) && defined( DEBUG_SETCMD ) +#define DEBUG DEBUG_SETCMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + +static hlpfunc_t sethelp; + +static unsigned setcwd( char *newdef ); +static unsigned setdef( char *newdef ); + + +/******************************************************************* doset() */ + +static qual_t setkwds[] = { {"cwd", 0, 0, NV, "Working directory on local system"}, + {"directory_qualifiers", 1, 0, NV, "Default qualifiers for DIRECTORY command" }, + {"default", 2, 0, NV, "Default directory on VMS volume"}, + {"qualifier_style", 3, 0, NV, "Qualifier style (Unix, VMS)"}, + {"verify", 4, 0, NV, "-Display commands in indirect files"}, + {"noverify", 5, 0, NV, NULL }, + {NULL, 0, 0, NV, NULL } +}; +static qual_t setqskwds[] = {{"unix", 1, 0, NV, "Unix style options, '-option'"}, + {"vms", 2, 0, NV, "VMS style qualifiers, '/qualifier'"}, + {NULL, 0, 0, NV, NULL } +}; +param_t setpars[] = { {"item_name", REQ, KEYWD, PA(setkwds), "" }, + {"value" , CND, KEYWD, sethelp, setqskwds, "" }, + {NULL, 0, 0, NOPA, NULL }, +}; + +/*********************************************************** doset() */ + +extern qual_t dirquals[]; +extern const int dir_defaults; +extern int dir_defopt; + +DECL_CMD(set) { + int parnum; + + UNUSED(argc); + + parnum = checkquals( 0, setkwds, -1, argv+1 ); + switch( parnum ) { + default: + return SS$_BADPARAM; + case 0: /* default */ + case 2: /* current working directory */ + if( qualc ) { + printf( "%%ODS2-E-NOQUAL, No qualifiers are permitted\n" ); + return 0; + } + return (parnum == 0)? setcwd( argv[2] ) : setdef( argv[2] ); + case 1:{ /* directory_qualifiers */ + int options = checkquals(dir_defaults,dirquals,qualc,qualv); + if( options == -1 ) + return SS$_BADPARAM; + dir_defopt = options; + return 1; + } + case 3: { /* 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 4: + verify_cmd = 1; + return 1; + case 5: + verify_cmd = 0; + return 1; + } +} + +/************************************************************ sethelp() */ + +static const char * sethelp( CMDSETp_t cmd, paramp_t 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 = "working directory for local files"; + p->ptype = STRING; + break; + case 1: + p->helpstr = "default directory on volume"; + p->ptype = VMSFS; + break; + case 2: + p->helpstr = "directory qualifier name "; + p->ptype = KEYWD; + p->arg = dirquals; + break; + case 3: + p->helpstr = "style "; + p->ptype = KEYWD; + p->arg = setqskwds; + break; + case 4: + case 5: + p->ptype = NONE; + break; + + default: + abort(); + } + return NULL; +} + +/*********************************************************** setcwd() */ + +static unsigned setcwd( char *newdef ) { + if( chdir( newdef ) != 0 ) { + printf( "%%ODS2-W-SETDEF, Error %s setting cwd to %s\n", + strerror( errno ), newdef ); + return SS$_BADPARAM; + } + return SS$_NORMAL; +} + +/*********************************************************** setdef() */ + +static int default_set = FALSE; + +static unsigned setdef( char *newdef ) { + register unsigned sts; + struct dsc_descriptor defdsc; + + defdsc.dsc_a_pointer = (char *) newdef; + defdsc.dsc_w_length = (unsigned short)strlen( defdsc.dsc_a_pointer ); + sts = sys_setddir( &defdsc, NULL, NULL ); + if ( sts & STS$M_SUCCESS ) { + default_set = TRUE; + } else { + printf( "%%ODS2-E-SETDEF, Error %s setting default to %s\n", getmsg(sts, MSG_TEXT), newdef ); + } + return sts; +} + +/*********************************************************** mountdef() */ + +int mountdef( const char *devnam ) { + char *colon, *buf; + size_t len; + + if( default_set ) + return SS$_NORMAL; + + len = strlen( devnam ); + buf = (char *) malloc( len + sizeof( ":[000000]" )); + if( buf == NULL ) { + perror( "malloc" ); + return SS$_INSFMEM; + } + + colon = strchr( devnam, ':' ); + if( colon != NULL ) + len = (size_t)(colon - devnam); + memcpy( buf, devnam, len ); + memcpy( buf+len, ":[000000]", sizeof( ":[000000]" ) ); + setdef(buf); + free( buf ); + + return SS$_NORMAL; +} diff --git a/extracters/ods2/showcmd.c b/extracters/ods2/showcmd.c new file mode 100644 index 0000000..caa734b --- /dev/null +++ b/extracters/ods2/showcmd.c @@ -0,0 +1,206 @@ +/* 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. + */ + +#if !defined( DEBUG ) && defined( DEBUG_SHOWCMD ) +#define DEBUG DEBUG_SHOWCMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + +#ifdef USE_READLINE +#ifndef _GNU_SOURCE +#define _XOPEN_SOURCE +#endif +#include +#endif + +#include "access.h" +#include "cache.h" +#include "direct.h" +#include "phyio.h" +#ifdef USE_VHD +#include "phyvhd.h" +#endif +#include "phyvirt.h" +#include "version.h" + +static unsigned show_stats( void ); + +void show_version( void ); + +static char *get_cwd( void ); + + +/******************************************************************* doshow() */ + +static qual_t showkwds[] = { {"cwd", 0, 0, NV, "Working directory on local system"}, + {"default", 1, 0, NV, "Default directory on VMS volume"}, + {"devices", 2, 0, NV, "Devices"}, + {"qualifier_style", 3, 0, NV, "Qualifier style (Unix, VMS)" }, + {"statistics", 4, 0, NV, "Debugging statistics"}, + {"time", 5, 0, NV, "Time"}, + {"verify", 6, 0, NV, "Command file echo" }, + {"version", 7, 0, NV, "Version"}, + {"volumes", 8, 0, NV, "Mounted volume information" }, + {NULL, 0, 0, NV, NULL } +}; +param_t showpars[] = { {"item_name", REQ, KEYWD, PA(showkwds), "" }, + {NULL, 0, 0, NOPA, NULL } +}; + +/*********************************************************** doshow() */ + +DECL_CMD(show) { + int parnum; + + UNUSED(argc); + UNUSED(qualc); + UNUSED(qualv); + + parnum = checkquals( 0, showkwds, -1, argv+1 ); + switch( parnum ) { + default: + return SS$_BADPARAM; + case 0: { + char *cwd; + + cwd = get_cwd(); + if( cwd == NULL ) { + return SS$_BADPARAM; + } + printf( " Current working directory is %s\n", cwd ); + free( cwd ); + return SS$_NORMAL; + } + case 1: { + 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; + sts = sys_setddir( NULL, &curlen, &curdsc ); + if ( sts & STS$M_SUCCESS ) { + curdir[curlen] = '\0'; + puts( curdir ); + } else { + printf("%%ODS2-E-GETDEF, Error %s getting default\n",getmsg(sts, MSG_TEXT)); + } + return sts; + } + case 2: + phyio_show( SHOW_DEVICES ); + virt_show( NULL ); + return SS$_NORMAL; + case 3: + printf ( " Qualifier style: %s\n", vms_qual? "/VMS": "-unix" ); + return SS$_NORMAL; + case 4: + return show_stats(); + case 5: { + 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 & STS$M_SUCCESS ) { + timstr[timlen] = '\0'; + printf(" %s\n",timstr); + } else { + printf("%%SHOW-W-TIMERR error %s\n",getmsg(sts, MSG_TEXT)); + } + } + return SS$_NORMAL; + case 6: + printf( "Command file verification is %s\n", (verify_cmd? "on": "off") ); + return SS$_NORMAL; + case 7: + show_version(); + return SS$_NORMAL; + case 8: + show_volumes(); + return SS$_NORMAL; + } + return SS$_NORMAL; +} + + +/****************************************************************** show_stats() */ + +/* dostats: print some simple statistics */ + +static unsigned show_stats( void ) { + printf("Statistics:-\n"); + direct_show(); + cache_show(); + phyio_show(SHOW_STATS); + + return SS$_NORMAL; +} + +/*********************************************************** show_version() */ + + +void show_version( void ) { + printf(" %s %s", MNAME(MODULE_NAME), MODULE_IDENT ); + 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 " ); + phyio_show( SHOW_FILE64 ); +#ifdef USE_VHD + putchar( ' '); + (void) phyvhd_available( TRUE ); +#else + printf( " VHD format image files are not supported\n" ); +#endif + + putchar( '\n' ); + return; +} + +/*********************************************************** get_cwd() */ + +static char *get_cwd( void ) { + size_t size = 32; + char *buf = NULL; + while( 1 ) { + char *nbuf; + nbuf = (char *)realloc( buf, size ); + if( nbuf == NULL ) { + free( buf ); + return NULL; + } + buf = nbuf; + if( getcwd( buf, size ) != NULL ) + break; + if( errno != ERANGE ) { + perror( "getcwd" ); + return NULL; + } + size *= 2; + } + return buf; +} diff --git a/extracters/ods2/spawncmd.c b/extracters/ods2/spawncmd.c new file mode 100644 index 0000000..d53a3fb --- /dev/null +++ b/extracters/ods2/spawncmd.c @@ -0,0 +1,89 @@ +/* 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. + */ + +#if !defined( DEBUG ) && defined( DEBUG_SPAWNCMD ) +#define DEBUG DEBUG_SPAWNCMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + +#if !defined( _WIN32 ) && !defined( VMS ) +#include +#else +# ifdef _WIN32 +# include +# endif +# ifdef VMS +# include +# endif +#endif + + +/******************************************************************* dospawn() */ + +DECL_CMD(spawn) { +#ifdef VMS + unsigned sts; + + UNUSED( argc ); + UNUSED( argv ); + UNUSED( qualc ); + UNUSED( qualv ); + + sts = lib$spawn( 0,0,0,0,0,0,0,0,0,0,0,0,0 ); + return sts; +#else +# ifdef _WIN32 + UNUSED( argc ); + UNUSED( argv ); + UNUSED( qualc ); + UNUSED( qualv ); + + if( system( "cmd" ) == -1 ) { + perror( "cmd" ); + return SS$_NOSUCHFILE; + } + return SS$_NORMAL; +# else + char *shell, *p; + pid_t pid; + + UNUSED( argc ); + UNUSED( argv ); + UNUSED( qualc ); + UNUSED( qualv ); + + if( (shell = getenv( "SHELL" )) == NULL ) + shell = "/bin/sh"; + if( (p = strrchr( shell, '/')) == NULL ) + p = shell; + else + p++; + + if( (pid = fork()) == 0 ) { + execlp( shell, p, (char *)NULL ); + perror( "%s" ); + exit(EXIT_FAILURE); + } + if( pid == -1 ) { + perror( shell ); + return SS$_NOSUCHFILE; + } + waitpid( pid, NULL, 0 ); + + return SS$_NORMAL; +# endif +#endif + +} + diff --git a/extracters/ods2/ssdef.h b/extracters/ods2/ssdef.h index 602f7f2..85a906f 100644 --- a/extracters/ods2/ssdef.h +++ b/extracters/ods2/ssdef.h @@ -26,6 +26,7 @@ #define SS$_DEVNOTMOUNT 124 #define SS$_DEVOFFLINE 132 #define SS$_DUPLICATE 148 +#define SS$_ILLBLKNUM 220 #define SS$_ILLEFC 236 #define SS$_INSFMEM 292 #define SS$_IVCHAN 316 @@ -37,6 +38,8 @@ #define SS$_DEVCMDERR 812 #define SS$_BADFILENAME 2072 #define SS$_BADIRECTORY 2088 +#define SS$_DEVALLOC 2112 +#define SS$_DEVASSIGN 2120 #define SS$_DEVICEFULL 2128 #define SS$_DEVNOTALLOC 2136 #define SS$_DUPFILENAME 2152 diff --git a/extracters/ods2/sysmsg.c b/extracters/ods2/sysmsg.c index a4ad9f6..62d3e82 100644 --- a/extracters/ods2/sysmsg.c +++ b/extracters/ods2/sysmsg.c @@ -1,16 +1,30 @@ -/* Timothe Litt litt _at_ acm _ddot_ org */ -/* - This is part of ODS2 written by Paul Nankervis, - email address: Paulnank@au1.ibm.com +/* Timothe Litt March 2016 + * Copyright (C) 2016 Timothe litt + * litt at acm dot org + * + * Free for use with the ODS2 package. All other rights reserved. + */ - 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. -*/ +/* + * This is distributed as part of ODS2, originally 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. + */ /* Message code translations for non-VMS systems */ +#if !defined( DEBUG ) && defined( DEBUG_SYSMSG ) +#define DEBUG DEBUG_SYSMSG +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + #include #include #include @@ -36,6 +50,8 @@ const struct VMSFAC { } fac2text[] = { { SYSTEM$_FACILITY, "SYSTEM" }, { RMS$_FACILITY, "RMS" }, + { COPY$_FACILITY, "COPY" }, + { DELETE$_FACILITY, "DELETE" }, { 0, NULL }, { 0, "NONAME" } }; @@ -58,6 +74,10 @@ const struct VMSMSG { const char *const txtcode; char *text; } vms2text[] = { + MSG(DELETE$_DELVER, "explicit version number or wild card required") + + MSG(COPY$_OPENIN, " error opening %s as input") + MSG(RMS$_BUG, "fatal RMS condition detected, process deleted") MSG(RMS$_DIR, "error in directory name") MSG(RMS$_DNF, "directory not found") @@ -79,6 +99,8 @@ const struct VMSMSG { MSG(SS$_BADPARAM, "bad parameter value") MSG(SS$_BUGCHECK, "internal consistency failure") MSG(SS$_DATACHECK, "write check error") + MSG(SS$_DEVALLOC, "device already allocated to another user") + MSG(SS$_DEVASSIGN, "device has channels assigned" ) MSG(SS$_DEVICEFULL, "device full - allocation failure") MSG(SS$_DEVMOUNT, "device is already mounted") MSG(SS$_DEVNOTALLOC, "device not allocated") @@ -87,6 +109,7 @@ const struct VMSMSG { MSG(SS$_DUPFILENAME, "duplicate file name") MSG(SS$_DUPLICATE, "duplicate name") MSG(SS$_ENDOFFILE, "end of file") + MSG(SS$_ILLBLKNUM, "illegal logical block number") MSG(SS$_FILELOCKED, "file is deaccess locked") MSG(SS$_FILESEQCHK, "file identification sequence number check") MSG(SS$_ILLEFC, "illegal event flag cluster") @@ -94,6 +117,7 @@ const struct VMSMSG { MSG(SS$_ITEMNOTFOUND, "requested item cannot be returned") MSG(SS$_NOMOREDEV, "no more devices") MSG(SS$_IVCHAN, "invalid I/O channel") + MSG(SS$_DEVOFFLINE, "device is not in configuration or not available") MSG(SS$_IVDEVNAM, "invalid device name") MSG(SS$_NOIOCHAN, "no I/O channel available") MSG(SS$_NOMOREFILES, "no more files") @@ -238,7 +262,7 @@ const char *getmsg( unsigned int vmscode, unsigned int flags, ... ) { fp = fac2text + (sizeof(fac2text)/sizeof(fac2text[0])) -1; mp = vms2text + (sizeof(vms2text)/sizeof(vms2text[0])) -1; - snprintf( notext, sizeof(notext), nofmt, vmscode ); + (void) snprintf( notext, sizeof(notext), nofmt, vmscode ); } } diff --git a/extracters/ods2/sysmsg.h b/extracters/ods2/sysmsg.h index b8f4a04..876a141 100644 --- a/extracters/ods2/sysmsg.h +++ b/extracters/ods2/sysmsg.h @@ -1,3 +1,20 @@ +/* Timothe Litt March 2016 + * Copyright (C) 2016 Timothe litt + * litt at acm dot org + * + * Free for use with the ODS2 package. All other rights reserved. + */ + +/* + * This is distributed as part of ODS2, originally 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. + */ + #ifndef SYSMSG_H #define SYSMSG_H @@ -11,6 +28,15 @@ #define MSG_FULL (MSG_FACILITY|MSG_SEVERITY|MSG_NAME|MSG_TEXT) +/* Some random non-system codes from VMS */ + +#define COPY$_FACILITY 103 +#define DELETE$_FACILITY 147 + +#define COPY$_OPENIN 0x109a +#define COPY$_ +#define DELETE$_DELVER 0x120a + const char *getmsg( unsigned int vmscode, unsigned int flags, ... ); void sysmsg_rundown( void ); diff --git a/extracters/ods2/typecmd.c b/extracters/ods2/typecmd.c new file mode 100644 index 0000000..f0898d6 --- /dev/null +++ b/extracters/ods2/typecmd.c @@ -0,0 +1,75 @@ +/* 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. + */ + +#if !defined( DEBUG ) && defined( DEBUG_TYPECMD ) +#define DEBUG DEBUG_TYPECMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + +/******************************************************************* dotype() */ + +/* dotype: a file TYPE routine */ + +param_t typepars[] = { {"filespec", REQ, VMSFS, NOPA, "for file on ODS-2 volume."}, + { NULL, 0, 0, NOPA, NULL } +}; + +DECL_CMD(type) { + int sts; + int records; + struct FAB fab = cc$rms_fab; + struct RAB rab = cc$rms_rab; + + UNUSED(argc); + UNUSED(qualc); + UNUSED(qualv); + + records = 0; + + fab.fab$l_fna = argv[1]; + fab.fab$b_fns = strlen(fab.fab$l_fna); + sts = sys_open( &fab ); + if ( sts & STS$M_SUCCESS ) { + rab.rab$l_fab = &fab; + sts = sys_connect( &rab ); + if ( sts & STS$M_SUCCESS ) { + char *rec; + + if( (rec = malloc( MAXREC + 2 )) == NULL ) + sts = SS$_INSFMEM; + else { + rab.rab$l_ubf = rec; + rab.rab$w_usz = MAXREC; + while( (sts = sys_get( &rab )) & STS$M_SUCCESS ) { + unsigned rsz = rab.rab$w_rsz; + if( fab.fab$b_rat & PRINT_ATTR ) rec[rsz++] = '\n'; + rec[rsz++] = '\0'; + fputs( rec, stdout ); + records++; + } + free( rec ); + rec = NULL; + } + sys_disconnect(&rab); + } + sys_close(&fab); + if (sts == RMS$_EOF) sts = SS$_NORMAL; + } + if ( !( sts & STS$M_SUCCESS ) ) { + printf("%%TYPE-F-ERROR Status: %s\n",getmsg(sts, MSG_TEXT)); + } + return sts; +} + + diff --git a/extracters/ods2/update.c b/extracters/ods2/update.c index 87c3b47..4c140e8 100644 --- a/extracters/ods2/update.c +++ b/extracters/ods2/update.c @@ -1,4 +1,12 @@ -/* Update.c V2.1 */ +/* Update.c */ + +#if !defined( DEBUG ) && defined( DEBUG_UPDATE ) +#define DEBUG DEBUG_UPDATE +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif #include #include @@ -6,6 +14,7 @@ #include "access.h" #include "device.h" +#include "ods2.h" #include "phyio.h" #include "ssdef.h" #include "stsdef.h" @@ -16,13 +25,6 @@ #include "vmstime.h" #endif -#ifndef TRUE -#define TRUE ( 0 == 0 ) -#endif -#ifndef FALSE -#define FALSE ( 0 != 0 ) -#endif - unsigned deaccesshead(struct VIOC *vioc,struct HEAD *head,unsigned idxblk); unsigned accesshead(struct VCB *vcb,struct fiddef *fid,unsigned seg_num, struct VIOC **vioc,struct HEAD **headbuff, @@ -39,7 +41,7 @@ unsigned getwindow(struct FCB * fcb,unsigned vbn,struct VCBDEV **devptr, #define WORK_MASK 0xff #else #define WORK_UNIT unsigned int -#define WORK_MASK 0xffffffff +#define WORK_MASK ((WORK_UNIT)~0) #endif #define WORK_BITS (sizeof(WORK_UNIT) * 8) @@ -89,29 +91,48 @@ unsigned update_freecount(struct VCBDEV *vcbdev,unsigned *retcount) /* bitmap_modify() will either set or release a block of bits in the device cluster bitmap */ -unsigned bitmap_modify(struct VCBDEV *vcbdev,unsigned cluster,unsigned count, - unsigned release_flag) -{ +unsigned bitmap_modify(struct VCBDEV *vcbdev, + unsigned cluster, unsigned count, + unsigned release_flag) { register unsigned sts; - register unsigned clust_count = count; - register unsigned map_block = cluster / 4096 + 2; - register unsigned block_offset = cluster % 4096; - if (clust_count < 1) return SS$_BADPARAM; - if (cluster + clust_count > vcbdev->max_cluster + 1) return SS$_BADPARAM; + register unsigned clust_count; + register unsigned map_block; + register unsigned block_offset; + + clust_count = count; + map_block = cluster / 4096 + 2; + block_offset = cluster % 4096; + + if( clust_count < 1 ) + return SS$_BADPARAM; + + if (cluster + clust_count > vcbdev->max_cluster + 1) + return SS$_BADPARAM; + + if( release_flag ) + vcbdev->free_clusters += clust_count; + else + vcbdev->free_clusters -= clust_count; + do { struct VIOC *vioc; unsigned blkcount; WORK_UNIT *bitmap; register WORK_UNIT *work_ptr; register unsigned work_count; - sts = accesschunk(vcbdev->mapfcb,map_block,&vioc,(char **) &bitmap, - &blkcount,1); - if (!(sts & STS$M_SUCCESS)) return sts; + register unsigned bit_no; + + sts = accesschunk( vcbdev->mapfcb, map_block, &vioc, (char **) &bitmap, + &blkcount, 1 ); + if( !(sts & STS$M_SUCCESS) ) + return sts; + work_ptr = bitmap + block_offset / WORK_BITS; - if (block_offset % WORK_BITS) { - register unsigned bit_no = block_offset % WORK_BITS; + + if( (bit_no = block_offset % WORK_BITS) != 0 ) { register WORK_UNIT bit_mask = WORK_MASK; - if (bit_no + clust_count < WORK_BITS) { + + if( bit_no + clust_count < WORK_BITS ) { bit_mask = bit_mask >> (WORK_BITS - clust_count); clust_count = 0; } else { @@ -119,14 +140,30 @@ unsigned bitmap_modify(struct VCBDEV *vcbdev,unsigned cluster,unsigned count, } bit_mask = bit_mask << bit_no; if (release_flag) { - *work_ptr++ |= bit_mask; +#if DEBUG + if( !(*work_ptr & bit_mask) ) { + *work_ptr |= bit_mask; + } /* else BUGCHECK( freeing unallocated cluster(s) ) */ + else abort(); +#else + *work_ptr |= bit_mask; +#endif } else { - *work_ptr++ &= ~bit_mask; +#if DEBUG + if( *work_ptr & bit_mask ) { + *work_ptr &= ~bit_mask; + } /* else BUGCHECK( allocating allocated cluster(s) ) */ + else abort(); +#else + *work_ptr &= ~bit_mask; +#endif } + ++work_ptr; block_offset += WORK_BITS - bit_no; } + work_count = (blkcount * 4096 - block_offset) / WORK_BITS; - if (work_count > clust_count / WORK_BITS) { + if( work_count > clust_count / WORK_BITS ) { work_count = clust_count / WORK_BITS; block_offset = 1; } else { @@ -134,29 +171,56 @@ unsigned bitmap_modify(struct VCBDEV *vcbdev,unsigned cluster,unsigned count, } clust_count -= work_count * WORK_BITS; if (release_flag) { - while (clust_count-- > 0) { + while( work_count-- > 0 ) { +#if DEBUG + if( *work_ptr != 0 ) + abort(); /* else BUGCHECK( freeing unallocated cluster(s) ) */ +#endif *work_ptr++ = WORK_MASK; } } else { - while (work_count-- > 0) { + while( work_count-- > 0 ) { +#if DEBUG + if( *work_ptr != WORK_MASK ) + abort(); /* else BUGCHECK( allocating allocated cluster(s) ) */ +#endif *work_ptr++ = 0; } } - if (clust_count != 0 && block_offset) { + if( clust_count != 0 && block_offset ) { register WORK_UNIT bit_mask; + bit_mask = WORK_MASK >> (WORK_BITS - clust_count); if (release_flag) { - *work_ptr++ |= bit_mask; +#if DEBUG + if( !(*work_ptr & bit_mask) ) { + *work_ptr |= bit_mask; + } /* else BUGCHECK( freeing unallocated cluster(s) ) */ + else abort(); +#else + *work_ptr |= bit_mask; +#endif } else { - *work_ptr++ &= ~bit_mask; +#if DEBUG + if( *work_ptr & bit_mask ) { + *work_ptr &= ~bit_mask; + } /* else BUGCHECK( allocating allocated cluster(s) ) */ + else abort(); +#else + *work_ptr &= ~bit_mask; +#endif } + ++work_ptr; + clust_count = 0; } - sts = deaccesschunk(vioc,map_block,blkcount,TRUE); - if (!(sts & STS$M_SUCCESS)) return sts; + sts = deaccesschunk( vioc, map_block, blkcount, TRUE ); + if( !(sts & STS$M_SUCCESS) ) + return sts; map_block += blkcount; block_offset = 0; - } while (clust_count != 0); + } while( clust_count != 0 ); + return sts; } @@ -193,7 +257,7 @@ unsigned bitmap_search(struct VCBDEV *vcbdev,unsigned *position,unsigned *count) work_val = *work_ptr++; if (block_offset % WORK_BITS) { work_val = work_val && (WORK_MASK << block_offset % WORK_BITS); - } + } work_count = (blkcount * 4096 - block_offset) / WORK_BITS; if (work_count > search_words) work_count = search_words; search_words -= work_count; @@ -253,7 +317,7 @@ unsigned headmap_clear(struct VCBDEV *vcbdev,unsigned head_no) register unsigned sts; register unsigned map_block; map_block = head_no / 4096 + vcbdev->home.hm2$w_cluster * 4 + 1; - if (head_no <= VMSWORD(vcbdev->home.hm2$w_resfiles)) return SS$_NOPRIV; /* Protect reserved files */ + if (head_no <= F11WORD(vcbdev->home.hm2$w_resfiles)) return SS$_NOPRIV; /* Protect reserved files */ sts = accesschunk(vcbdev->idxfcb,map_block,&vioc,(char **) &bitmap,NULL,1); if (sts & STS$M_SUCCESS) { bitmap[(head_no % 4096) / WORK_BITS] &= ~(1 << (head_no % WORK_BITS)); @@ -294,8 +358,8 @@ unsigned update_findhead(struct VCBDEV *vcbdev,unsigned *rethead_no, for (bit_no = 0; bit_no < WORK_BITS; bit_no++) { if ((work_val & (1 << bit_no)) == 0) { register unsigned idxblk = head_no + - VMSWORD(vcbdev->home.hm2$w_ibmapvbn) + - VMSWORD(vcbdev->home.hm2$w_ibmapsize); + F11WORD(vcbdev->home.hm2$w_ibmapvbn) + + F11WORD(vcbdev->home.hm2$w_ibmapsize); sts = accesschunk(vcbdev->idxfcb,idxblk,retvioc, (char **) headbuff,NULL,1); if (sts & STS$M_SUCCESS) { @@ -303,7 +367,7 @@ unsigned update_findhead(struct VCBDEV *vcbdev,unsigned *rethead_no, modify_flag = TRUE; if( (*headbuff)->fh2$w_checksum != 0 || (*headbuff)->fh2$w_fid.fid$w_num != 0 || - !(VMSLONG((*headbuff)->fh2$l_filechar) & + !(F11LONG((*headbuff)->fh2$l_filechar) & FH2$M_MARKDEL) ) { sts = deaccesschunk(*retvioc,0,0,FALSE); } else { @@ -322,7 +386,7 @@ unsigned update_findhead(struct VCBDEV *vcbdev,unsigned *rethead_no, } while (--work_count != 0); deaccesschunk(vioc,map_block,blkcount,modify_flag); if (!(sts & STS$M_SUCCESS)) break; - } while (head_no < VMSLONG(vcbdev->home.hm2$l_maxfiles)); + } while (head_no < F11LONG(vcbdev->home.hm2$l_maxfiles)); return sts; } @@ -389,10 +453,10 @@ unsigned update_addhead(struct VCB *vcb,char *filename,struct fiddef *back, sys$gettim(id->fi2$q_credate); memcpy(id->fi2$q_revdate,id->fi2$q_credate,sizeof(id->fi2$q_credate)); memcpy(id->fi2$q_expdate,id->fi2$q_credate,sizeof(id->fi2$q_credate)); - head->fh2$w_recattr.fat$l_efblk = VMSSWAP(1); + head->fh2$w_recattr.fat$l_efblk = F11SWAP(1); { - unsigned short check = checksum((vmsword *) head); - head->fh2$w_checksum = VMSWORD(check); + unsigned short check = checksum((f11word *) head); + head->fh2$w_checksum = F11WORD(check); } if( rethead != NULL ) *rethead = head; return SS$_NORMAL; @@ -433,7 +497,7 @@ unsigned update_extend(struct FCB *fcb,unsigned blocks,unsigned contig) unsigned start_pos = 0; unsigned block_count = blocks; if (block_count < 1) return 0; - if ((fcb->status & FCB_WRITE) == 0) return SS$_WRITLCK; + if( !(fcb->status & FCB_WRITE) ) return SS$_WRITLCK; if (fcb->hiblock > 0) { unsigned mapblk,maplen; sts = getwindow(fcb,fcb->hiblock,&vcbdev,&mapblk,&maplen,&hdrfid, @@ -488,7 +552,7 @@ unsigned update_extend(struct FCB *fcb,unsigned blocks,unsigned contig) head->fh2$b_map_inuse += 4; fcb->hiblock += block_count * vcbdev->clustersize; fcb->head->fh2$w_recattr.fat$l_hiblk = - VMSSWAP(fcb->hiblock * vcbdev->clustersize); + F11SWAP(fcb->hiblock * vcbdev->clustersize); sts = bitmap_modify(vcbdev,start_pos,block_count,0); } } @@ -498,25 +562,27 @@ unsigned update_extend(struct FCB *fcb,unsigned blocks,unsigned contig) /************************************************************** deallocfile() */ -/* This routine has bugs and does NOT work properly yet!!!! -It may be something simple but I haven't had time to look... -So DON'T use mount/write!!! */ - unsigned deallocfile(struct FCB *fcb) { register unsigned sts = SS$_NORMAL; /* First mark all file clusters as free in BITMAP.SYS */ - register unsigned vbn = 1; - while (vbn <= fcb->hiblock) { + register unsigned vbn; + for( vbn = 1; vbn <= fcb->hiblock; ) { register unsigned sts; unsigned phyblk,phylen; struct VCBDEV *vcbdev; - sts = getwindow(fcb,vbn,&vcbdev,&phyblk,&phylen,NULL,NULL); - if (!(sts & STS$M_SUCCESS)) break; - sts = bitmap_modify(vcbdev,phyblk,phylen,1); - if (!(sts & STS$M_SUCCESS)) break; + + sts = getwindow( fcb, vbn, &vcbdev, &phyblk, &phylen, NULL, NULL ); + if( !(sts & STS$M_SUCCESS) ) + break; + vbn += phylen; + phyblk /= vcbdev->clustersize; + phylen = (phylen + vcbdev->clustersize -1)/vcbdev->clustersize; + sts = bitmap_modify( vcbdev, phyblk, phylen, 1 ); + if( !(sts & STS$M_SUCCESS) ) + break; } /* Now reset file header bit map in INDEXF.SYS and @@ -527,6 +593,7 @@ unsigned deallocfile(struct FCB *fcb) unsigned headvbn = fcb->headvbn; struct HEAD *head = fcb->head; struct VIOC *headvioc = fcb->headvioc; + while (TRUE) { unsigned ext_seg_num = 0; struct fiddef extfid; @@ -543,7 +610,8 @@ unsigned deallocfile(struct FCB *fcb) idxblk = filenum / 4096 + vcbdev->home.hm2$w_cluster * 4 + 1; sts = accesschunk(vcbdev->idxfcb,idxblk,&vioc,(char **) &bitmap, NULL,1); - if (!(sts & STS$M_SUCCESS)) break; + if( !(sts & STS$M_SUCCESS) ) + break; bitmap[(filenum % 4096) / WORK_BITS] &= ~(1 << (filenum % WORK_BITS)); sts = deaccesschunk(vioc,idxblk,1,TRUE); @@ -554,7 +622,8 @@ unsigned deallocfile(struct FCB *fcb) ext_seg_num++; memcpy(&extfid,&fcb->head->fh2$w_ext_fid,sizeof(struct fiddef)); sts = deaccesshead(headvioc,NULL,headvbn); - if (!(sts & STS$M_SUCCESS)) break; + if( !(sts & STS$M_SUCCESS) ) + break; if (extfid.fid$b_rvn == 0) { extfid.fid$b_rvn = rvn; } else { @@ -585,10 +654,22 @@ unsigned accesserase(struct VCB * vcb,struct fiddef * fid) { struct FCB *fcb; register int sts; + struct VCBDEV *vcbdev; + + vcbdev = RVN_TO_DEV(vcb,fid->fid$b_rvn); + if (vcbdev == NULL) + return SS$_DEVNOTMOUNT; + if( (fid->fid$w_num | (fid->fid$b_nmx << 16)) <= + F11WORD( vcbdev->home.hm2$w_resfiles ) ) { + return SS$_NOPRIV; + } + sts = accessfile(vcb,fid,&fcb,1); if (sts & STS$M_SUCCESS) { fcb->head->fh2$l_filechar |= FH2$M_MARKDEL; +#if DEBUG printf("Accesserase ... \n"); +#endif sts = deaccessfile(fcb); } return sts; @@ -604,7 +685,7 @@ unsigned extend(struct FCB *fcb,unsigned blocks) struct VCBDEV *vcbdev; unsigned clusterno; unsigned extended = 0; - if ((fcb->status & FCB_WRITE) == 0) return SS$_WRITLCK; + if( !(fcb->status & FCB_WRITE) ) return SS$_WRITLCK; if (fcb->hiblock > 0) { unsigned phyblk,phylen; sts = getwindow(fcb,fcb->hiblock,&vcbdev,&phyblk,&phylen,NULL,NULL); @@ -673,7 +754,7 @@ unsigned access_create(struct VCB * vcb,struct FCB ** fcbadd,unsigned blocks) register struct FCB *fcb; struct fiddef fid; unsigned create = sizeof(struct FCB); - if (wrtflg && ((vcb->status & VCB_WRITE) == 0)) return SS$_WRITLCK; + if( wrtflg && !(vcb->status & VCB_WRITE) ) return SS$_WRITLCK; sts = headmap_search(struct VCBDEV * vcbdev,struct fiddef * fid, struct VIOC ** vioc,struct HEAD ** headbuff,unsigned *retidxblk,) { @@ -710,7 +791,7 @@ unsigned access_create(struct VCB * vcb,struct FCB ** fcbadd,unsigned blocks) sts = accesshead(vcb,fid,0,&fcb->headvioc,&fcb->head,&fcb->headvbn, wrtflg); if (sts & STS$M_SUCCESS) { - fcb->hiblock = VMSSWAP(fcb->head->fh2$w_recattr.fat$l_hiblk); + fcb->hiblock = F11SWAP(fcb->head->fh2$w_recattr.fat$l_hiblk); if (fcb->head->fh2$b_idoffset > 39) { fcb->highwater = fcb->head->fh2$l_highwater; } else { diff --git a/extracters/ods2/valgrind_suppressions_readline b/extracters/ods2/valgrind_suppressions_readline index 470c88e..a6f805a 100644 --- a/extracters/ods2/valgrind_suppressions_readline +++ b/extracters/ods2/valgrind_suppressions_readline @@ -44,3 +44,12 @@ fun:rl_set_prompt fun:readline } +{ + readline + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:readline_internal_charloop + fun:readline_internal + fun:readline +} diff --git a/extracters/ods2/version.h b/extracters/ods2/version.h index e984b03..c179bcd 100644 --- a/extracters/ods2/version.h +++ b/extracters/ods2/version.h @@ -1,10 +1,66 @@ +/* Timothe Litt March 2016 + * Copyright (C) 2016 Timothe litt + * litt at acm dot org + * Distribution per Paul's license below. + * + * Revision history at bottom of this file. + * + */ + +/* + * This is distributed as part of ODS2, originally 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. + */ + + #ifndef VERSION_H #define VERSION_H #ifdef DEBUG_BUILD #define MODULE_IDENT "Debug build" #else -#define MODULE_IDENT "v2.1" +#define MODULE_IDENT "v3.0" #endif +#define MODULE_NAME ODS2 + +#define MNAMEX(n) #n +#define MNAME(n) MNAMEX(n) + + +/* Modified by: + * Feb 2016 Timothe Litt + * 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. Merge + * code from Larry Baker, USGS. Add initialize volume, spawn, + * and more. See git commit history for details. + * + * V3.0 - Merge and adapt code from Larry Baker's V2.0 + * +* 8-JUN-2005 Larry Baker + * + * Add #include guards in .h files. + * Use named constants in place of literals. + * Replace BIG_ENDIAN with ODS2_BIG_ENDIAN (Linux always #defines + * BIG_ENDIAN). + * Add SUBST DRIVE: FILE to "mount" a file (vs. a disk). + * Implement quoted arguments (paired " or '; no escape characters) + * in cmdsplit(), e.g., to specify a Unix path or a null argument. + * Remove VMSIO conditional code (need to "mount" a file now). + * + * Jul-2003, v1.3hb, some extensions by Hartmut Becker + * + * 31-AUG-2001 01:04 Hunter Goatley + * + * For VMS, added routine getcmd() to read commands with full + * command recall capabilities. + * + */ + #endif diff --git a/extracters/ods2/vmstime.c b/extracters/ods2/vmstime.c index a30643d..d82e068 100644 --- a/extracters/ods2/vmstime.c +++ b/extracters/ods2/vmstime.c @@ -1,6 +1,6 @@ /* - Vmstime.c V2.1 + Vmstime.c Author: Paul Nankervis @@ -56,6 +56,13 @@ */ +#if !defined( DEBUG ) && defined( DEBUG_VMSTIME ) +#define DEBUG DEBUG_VMSTIME +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif #include #include @@ -750,15 +757,16 @@ unsigned sys_asctim(unsigned short *timlen,struct dsc_descriptor *timbuf, /* Generate two digit day... */ - if (chrptr < endptr) { - if ((timval = timvec[2]) / 10 == 0) { + if( chrptr < endptr ) { + if( (timval = timvec[2]) / 10 == 0 ) { *chrptr++ = ' '; } else { *chrptr++ = vmstime_digits[timval / 10]; } - } - if (chrptr < endptr) { - *chrptr++ = vmstime_digits[timval % 10]; + + if( chrptr < endptr ) { + *chrptr++ = vmstime_digits[timval % 10]; + } } /* Add month name with hyphen separators... */ diff --git a/extracters/ods2/vmstime.h b/extracters/ods2/vmstime.h index c7f6d6a..a963374 100644 --- a/extracters/ods2/vmstime.h +++ b/extracters/ods2/vmstime.h @@ -1,6 +1,6 @@ /* - Vmstime.h V2.1 + Vmstime.h Author: Paul Nankervis diff --git a/putr/putr.com b/putr/putr.com old mode 100644 new mode 100755